package customer import ( "context" "errors" "strings" ) // ErrCompanyNotAssigned is returned when the requested company is not assigned to the customer. var ErrCompanyNotAssigned = errors.New("company is not assigned to customer") var errCompanyNotAssigned = ErrCompanyNotAssigned func normalizeAssignedCompanyIDs(assigned []string) []string { normalized := make([]string, 0, len(assigned)) seen := make(map[string]struct{}, len(assigned)) for _, id := range assigned { clean := strings.TrimSpace(id) if clean == "" { continue } if _, ok := seen[clean]; ok { continue } seen[clean] = struct{}{} normalized = append(normalized, clean) } return normalized } // pickActiveCompanyID chooses the company context for a customer request. // When requestedCompanyID is set it must be in assigned; otherwise a still-valid // token company is kept; if the token company was removed, the first assignment is used. func pickActiveCompanyID(assigned []string, tokenCompanyID, requestedCompanyID string) (string, error) { assigned = normalizeAssignedCompanyIDs(assigned) if len(assigned) == 0 { return "", nil } requested := strings.TrimSpace(requestedCompanyID) if requested != "" { for _, id := range assigned { if id == requested { return requested, nil } } return "", errCompanyNotAssigned } token := strings.TrimSpace(tokenCompanyID) if token != "" { for _, id := range assigned { if id == token { return token, nil } } } return assigned[0], nil } // PickAssignedCompanyID chooses the company for a company-scoped write (e.g. tender approval). // When requestedCompanyID is set it must be in assigned; otherwise activeCompanyID is used. func PickAssignedCompanyID(activeCompanyID string, assigned []string, requestedCompanyID string) (string, error) { activeCompanyID = strings.TrimSpace(activeCompanyID) requestedCompanyID = strings.TrimSpace(requestedCompanyID) if requestedCompanyID != "" { for _, id := range normalizeAssignedCompanyIDs(assigned) { if id == requestedCompanyID { return requestedCompanyID, nil } } return "", ErrCompanyNotAssigned } if activeCompanyID == "" { return "", errors.New("company ID is required") } return activeCompanyID, nil } // ResolveCompanyContext resolves the active company and assigned companies for an authenticated customer. func (s *customerService) ResolveCompanyContext(ctx context.Context, customerID, tokenCompanyID, requestedCompanyID string) (string, []string, error) { customer, err := s.repository.GetByID(ctx, customerID) if err != nil { return "", nil, errors.New("customer not found") } assigned := normalizeAssignedCompanyIDs(customer.Companies) companyID, err := pickActiveCompanyID(assigned, tokenCompanyID, requestedCompanyID) if err != nil { return "", nil, err } return companyID, assigned, nil } // ResolveActiveCompanyID resolves the active company for an authenticated customer. func (s *customerService) ResolveActiveCompanyID(ctx context.Context, customerID, tokenCompanyID, requestedCompanyID string) (string, error) { companyID, _, err := s.ResolveCompanyContext(ctx, customerID, tokenCompanyID, requestedCompanyID) return companyID, err }