package processor import ( "context" "fmt" "apskel-pos-be/internal/appcontext" "apskel-pos-be/internal/entities" "apskel-pos-be/internal/mappers" "apskel-pos-be/internal/models" "github.com/google/uuid" ) type AccountProcessor interface { CreateAccount(ctx context.Context, req *models.CreateAccountRequest) (*models.AccountResponse, error) GetAccountByID(ctx context.Context, id uuid.UUID) (*models.AccountResponse, error) UpdateAccount(ctx context.Context, id uuid.UUID, req *models.UpdateAccountRequest) (*models.AccountResponse, error) DeleteAccount(ctx context.Context, id uuid.UUID) error ListAccounts(ctx context.Context, req *models.ListAccountsRequest) ([]models.AccountResponse, int, error) GetAccountsByOrganization(ctx context.Context, organizationID uuid.UUID, outletID *uuid.UUID) ([]models.AccountResponse, error) GetAccountsByChartOfAccount(ctx context.Context, chartOfAccountID uuid.UUID) ([]models.AccountResponse, error) UpdateAccountBalance(ctx context.Context, id uuid.UUID, amount float64) error GetAccountBalance(ctx context.Context, id uuid.UUID) (float64, error) } type AccountProcessorImpl struct { accountRepo AccountRepository chartOfAccountRepo ChartOfAccountRepository } func NewAccountProcessorImpl(accountRepo AccountRepository, chartOfAccountRepo ChartOfAccountRepository) *AccountProcessorImpl { return &AccountProcessorImpl{ accountRepo: accountRepo, chartOfAccountRepo: chartOfAccountRepo, } } func (p *AccountProcessorImpl) CreateAccount(ctx context.Context, req *models.CreateAccountRequest) (*models.AccountResponse, error) { // Get organization and outlet from context appCtx := appcontext.FromGinContext(ctx) organizationID := appCtx.OrganizationID var outletID *uuid.UUID if appCtx.OutletID != uuid.Nil { outletID = &appCtx.OutletID } // Check if account number already exists for this organization/outlet existing, err := p.accountRepo.GetByNumber(ctx, organizationID, req.Number, outletID) if err == nil && existing != nil { return nil, fmt.Errorf("account with number %s already exists", req.Number) } // Validate chart of account exists _, err = p.chartOfAccountRepo.GetByID(ctx, req.ChartOfAccountID) if err != nil { return nil, fmt.Errorf("chart of account not found: %w", err) } entity := mappers.AccountCreateRequestToEntity(req, organizationID, outletID) err = p.accountRepo.Create(ctx, entity) if err != nil { return nil, fmt.Errorf("failed to create account: %w", err) } return mappers.AccountEntityToResponse(entity), nil } func (p *AccountProcessorImpl) GetAccountByID(ctx context.Context, id uuid.UUID) (*models.AccountResponse, error) { entity, err := p.accountRepo.GetByID(ctx, id) if err != nil { return nil, fmt.Errorf("account not found: %w", err) } return mappers.AccountEntityToResponse(entity), nil } func (p *AccountProcessorImpl) UpdateAccount(ctx context.Context, id uuid.UUID, req *models.UpdateAccountRequest) (*models.AccountResponse, error) { entity, err := p.accountRepo.GetByID(ctx, id) if err != nil { return nil, fmt.Errorf("account not found: %w", err) } // Check if new number already exists (if number is being updated) if req.Number != nil && *req.Number != entity.Number { existing, err := p.accountRepo.GetByNumber(ctx, entity.OrganizationID, *req.Number, entity.OutletID) if err == nil && existing != nil { return nil, fmt.Errorf("account with number %s already exists", *req.Number) } } // Validate chart of account exists if provided if req.ChartOfAccountID != nil { _, err = p.chartOfAccountRepo.GetByID(ctx, *req.ChartOfAccountID) if err != nil { return nil, fmt.Errorf("chart of account not found: %w", err) } } mappers.AccountUpdateRequestToEntity(entity, req) err = p.accountRepo.Update(ctx, entity) if err != nil { return nil, fmt.Errorf("failed to update account: %w", err) } return mappers.AccountEntityToResponse(entity), nil } func (p *AccountProcessorImpl) DeleteAccount(ctx context.Context, id uuid.UUID) error { entity, err := p.accountRepo.GetByID(ctx, id) if err != nil { return fmt.Errorf("account not found: %w", err) } // Prevent deletion of system accounts if entity.IsSystem { return fmt.Errorf("cannot delete system account") } err = p.accountRepo.Delete(ctx, id) if err != nil { return fmt.Errorf("failed to delete account: %w", err) } return nil } func (p *AccountProcessorImpl) ListAccounts(ctx context.Context, req *models.ListAccountsRequest) ([]models.AccountResponse, int, error) { // Get organization and outlet from context appCtx := appcontext.FromGinContext(ctx) organizationID := appCtx.OrganizationID var outletID *uuid.UUID if appCtx.OutletID != uuid.Nil { outletID = &appCtx.OutletID } filterEntity := &entities.Account{ OrganizationID: organizationID, OutletID: outletID, ChartOfAccountID: *req.ChartOfAccountID, AccountType: entities.AccountType(*req.AccountType), } entities, total, err := p.accountRepo.List(ctx, filterEntity) if err != nil { return nil, 0, fmt.Errorf("failed to list accounts: %w", err) } responses := make([]models.AccountResponse, len(entities)) for i, entity := range entities { responses[i] = *mappers.AccountEntityToResponse(entity) } return responses, total, nil } func (p *AccountProcessorImpl) GetAccountsByOrganization(ctx context.Context, organizationID uuid.UUID, outletID *uuid.UUID) ([]models.AccountResponse, error) { entities, err := p.accountRepo.GetByOrganization(ctx, organizationID, outletID) if err != nil { return nil, fmt.Errorf("failed to get accounts by organization: %w", err) } responses := make([]models.AccountResponse, len(entities)) for i, entity := range entities { responses[i] = *mappers.AccountEntityToResponse(entity) } return responses, nil } func (p *AccountProcessorImpl) GetAccountsByChartOfAccount(ctx context.Context, chartOfAccountID uuid.UUID) ([]models.AccountResponse, error) { entities, err := p.accountRepo.GetByChartOfAccount(ctx, chartOfAccountID) if err != nil { return nil, fmt.Errorf("failed to get accounts by chart of account: %w", err) } responses := make([]models.AccountResponse, len(entities)) for i, entity := range entities { responses[i] = *mappers.AccountEntityToResponse(entity) } return responses, nil } func (p *AccountProcessorImpl) UpdateAccountBalance(ctx context.Context, id uuid.UUID, amount float64) error { _, err := p.accountRepo.GetByID(ctx, id) if err != nil { return fmt.Errorf("account not found: %w", err) } err = p.accountRepo.UpdateBalance(ctx, id, amount) if err != nil { return fmt.Errorf("failed to update account balance: %w", err) } return nil } func (p *AccountProcessorImpl) GetAccountBalance(ctx context.Context, id uuid.UUID) (float64, error) { balance, err := p.accountRepo.GetBalance(ctx, id) if err != nil { return 0, fmt.Errorf("failed to get account balance: %w", err) } return balance, nil }