90 lines
3.3 KiB
Go
90 lines
3.3 KiB
Go
package processor
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"apskel-pos-be/internal/constants"
|
|
"apskel-pos-be/internal/models"
|
|
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
// Teams are the parent product categories, plus Pusat for spending that belongs to
|
|
// no single team. Both purchase orders and cash advances are charged to one, so the rules
|
|
// for picking and storing a team live here rather than in either processor.
|
|
|
|
// listTeams returns the teams money can be charged to: the parent categories of the
|
|
// outlet in scope, followed by Pusat. Pusat has no category row, so it is appended
|
|
// here rather than read from the database.
|
|
func listTeams(ctx context.Context, categoryRepo CategoryRepository, organizationID uuid.UUID, outletID *uuid.UUID) (*models.ListPurchaseTeamsResponse, error) {
|
|
categories, err := categoryRepo.ListParentCategories(ctx, organizationID, outletID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to list parent categories: %w", err)
|
|
}
|
|
|
|
teams := make([]models.PurchaseTeam, 0, len(categories)+1)
|
|
for _, category := range categories {
|
|
categoryID := category.ID
|
|
teams = append(teams, models.PurchaseTeam{
|
|
Scope: constants.PurchaseTeamScopeCategory,
|
|
CategoryID: &categoryID,
|
|
Name: category.Name,
|
|
})
|
|
}
|
|
|
|
teams = append(teams, models.PurchaseTeam{
|
|
Scope: constants.PurchaseTeamScopeCentral,
|
|
Name: constants.PurchaseTeamCentralName,
|
|
})
|
|
|
|
return &models.ListPurchaseTeamsResponse{Teams: teams}, nil
|
|
}
|
|
|
|
// resolveTeamSelection turns a requested team into the scope/category pair that gets
|
|
// stored, mirroring the database check constraint. A nil or empty scope means no
|
|
// team, which is deliberately different from Pusat — callers that require a team
|
|
// reject that case before getting here. Which outlet's Pusat it is comes from the
|
|
// record's own outlet, so 'central' needs nothing stored beyond the scope itself.
|
|
func resolveTeamSelection(ctx context.Context, categoryRepo CategoryRepository, organizationID uuid.UUID, outletID *uuid.UUID, scope *string, categoryID *uuid.UUID) (*string, *uuid.UUID, error) {
|
|
if scope == nil {
|
|
return nil, nil, nil
|
|
}
|
|
|
|
switch strings.TrimSpace(*scope) {
|
|
case "":
|
|
return nil, nil, nil
|
|
|
|
case constants.PurchaseTeamScopeCentral:
|
|
resolved := constants.PurchaseTeamScopeCentral
|
|
return &resolved, nil, nil
|
|
|
|
case constants.PurchaseTeamScopeCategory:
|
|
if categoryID == nil {
|
|
return nil, nil, fmt.Errorf("team_category_id is required when team_scope is category")
|
|
}
|
|
|
|
category, err := categoryRepo.GetByID(ctx, *categoryID)
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("team category not found: %w", err)
|
|
}
|
|
if category.OrganizationID != organizationID {
|
|
return nil, nil, fmt.Errorf("team category does not belong to this organization")
|
|
}
|
|
if category.ParentID != nil {
|
|
return nil, nil, fmt.Errorf("team must be a parent category")
|
|
}
|
|
// Categories without an outlet are shared, so only an outlet-specific
|
|
// category has to match the outlet the record is booked against.
|
|
if category.OutletID != nil && outletID != nil && *category.OutletID != *outletID {
|
|
return nil, nil, fmt.Errorf("team category belongs to a different outlet")
|
|
}
|
|
|
|
resolved := constants.PurchaseTeamScopeCategory
|
|
return &resolved, &category.ID, nil
|
|
}
|
|
|
|
return nil, nil, fmt.Errorf("team_scope must be one of: category, central")
|
|
}
|