208 lines
6.8 KiB
Go
208 lines
6.8 KiB
Go
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
|
|
}
|