202 lines
7.5 KiB
Go
202 lines
7.5 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 ChartOfAccountProcessor interface {
|
|
CreateChartOfAccount(ctx context.Context, req *models.CreateChartOfAccountRequest) (*models.ChartOfAccountResponse, error)
|
|
GetChartOfAccountByID(ctx context.Context, id uuid.UUID) (*models.ChartOfAccountResponse, error)
|
|
UpdateChartOfAccount(ctx context.Context, id uuid.UUID, req *models.UpdateChartOfAccountRequest) (*models.ChartOfAccountResponse, error)
|
|
DeleteChartOfAccount(ctx context.Context, id uuid.UUID) error
|
|
ListChartOfAccounts(ctx context.Context, req *models.ListChartOfAccountsRequest) ([]models.ChartOfAccountResponse, int, error)
|
|
GetChartOfAccountsByOrganization(ctx context.Context, organizationID uuid.UUID, outletID *uuid.UUID) ([]models.ChartOfAccountResponse, error)
|
|
GetChartOfAccountsByType(ctx context.Context, organizationID uuid.UUID, chartOfAccountTypeID uuid.UUID, outletID *uuid.UUID) ([]models.ChartOfAccountResponse, error)
|
|
CreateDefaultChartOfAccounts(ctx context.Context, organizationID uuid.UUID, outletID *uuid.UUID) error
|
|
}
|
|
|
|
type ChartOfAccountProcessorImpl struct {
|
|
chartOfAccountRepo ChartOfAccountRepository
|
|
chartOfAccountTypeRepo ChartOfAccountTypeRepository
|
|
}
|
|
|
|
func NewChartOfAccountProcessorImpl(chartOfAccountRepo ChartOfAccountRepository, chartOfAccountTypeRepo ChartOfAccountTypeRepository) *ChartOfAccountProcessorImpl {
|
|
return &ChartOfAccountProcessorImpl{
|
|
chartOfAccountRepo: chartOfAccountRepo,
|
|
chartOfAccountTypeRepo: chartOfAccountTypeRepo,
|
|
}
|
|
}
|
|
|
|
func (p *ChartOfAccountProcessorImpl) CreateChartOfAccount(ctx context.Context, req *models.CreateChartOfAccountRequest) (*models.ChartOfAccountResponse, 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 code already exists for this organization/outlet
|
|
existing, err := p.chartOfAccountRepo.GetByCode(ctx, organizationID, req.Code, outletID)
|
|
if err == nil && existing != nil {
|
|
return nil, fmt.Errorf("chart of account with code %s already exists", req.Code)
|
|
}
|
|
|
|
// Validate parent exists if provided
|
|
if req.ParentID != nil {
|
|
_, err := p.chartOfAccountRepo.GetByID(ctx, *req.ParentID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("parent chart of account not found: %w", err)
|
|
}
|
|
}
|
|
|
|
// Validate chart of account type exists
|
|
_, err = p.chartOfAccountTypeRepo.GetByID(ctx, req.ChartOfAccountTypeID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("chart of account type not found: %w", err)
|
|
}
|
|
|
|
entity := mappers.ChartOfAccountCreateRequestToEntity(req, organizationID, outletID)
|
|
err = p.chartOfAccountRepo.Create(ctx, entity)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to create chart of account: %w", err)
|
|
}
|
|
|
|
return mappers.ChartOfAccountEntityToResponse(entity), nil
|
|
}
|
|
|
|
func (p *ChartOfAccountProcessorImpl) GetChartOfAccountByID(ctx context.Context, id uuid.UUID) (*models.ChartOfAccountResponse, error) {
|
|
entity, err := p.chartOfAccountRepo.GetByID(ctx, id)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("chart of account not found: %w", err)
|
|
}
|
|
|
|
return mappers.ChartOfAccountEntityToResponse(entity), nil
|
|
}
|
|
|
|
func (p *ChartOfAccountProcessorImpl) UpdateChartOfAccount(ctx context.Context, id uuid.UUID, req *models.UpdateChartOfAccountRequest) (*models.ChartOfAccountResponse, error) {
|
|
entity, err := p.chartOfAccountRepo.GetByID(ctx, id)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("chart of account not found: %w", err)
|
|
}
|
|
|
|
// Check if new code already exists (if code is being updated)
|
|
if req.Code != nil && *req.Code != entity.Code {
|
|
existing, err := p.chartOfAccountRepo.GetByCode(ctx, entity.OrganizationID, *req.Code, &entity.OutletID)
|
|
if err == nil && existing != nil {
|
|
return nil, fmt.Errorf("chart of account with code %s already exists", *req.Code)
|
|
}
|
|
}
|
|
|
|
// Validate parent exists if provided
|
|
if req.ParentID != nil {
|
|
_, err := p.chartOfAccountRepo.GetByID(ctx, *req.ParentID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("parent chart of account not found: %w", err)
|
|
}
|
|
}
|
|
|
|
// Validate chart of account type exists if provided
|
|
if req.ChartOfAccountTypeID != nil {
|
|
_, err = p.chartOfAccountTypeRepo.GetByID(ctx, *req.ChartOfAccountTypeID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("chart of account type not found: %w", err)
|
|
}
|
|
}
|
|
|
|
mappers.ChartOfAccountUpdateRequestToEntity(entity, req)
|
|
err = p.chartOfAccountRepo.Update(ctx, entity)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to update chart of account: %w", err)
|
|
}
|
|
|
|
return mappers.ChartOfAccountEntityToResponse(entity), nil
|
|
}
|
|
|
|
func (p *ChartOfAccountProcessorImpl) DeleteChartOfAccount(ctx context.Context, id uuid.UUID) error {
|
|
entity, err := p.chartOfAccountRepo.GetByID(ctx, id)
|
|
if err != nil {
|
|
return fmt.Errorf("chart of account not found: %w", err)
|
|
}
|
|
|
|
// Prevent deletion of system accounts
|
|
if entity.IsSystem {
|
|
return fmt.Errorf("cannot delete system chart of account")
|
|
}
|
|
|
|
err = p.chartOfAccountRepo.Delete(ctx, id)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to delete chart of account: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (p *ChartOfAccountProcessorImpl) ListChartOfAccounts(ctx context.Context, req *models.ListChartOfAccountsRequest) ([]models.ChartOfAccountResponse, int, error) {
|
|
filterEntity := &entities.ChartOfAccount{
|
|
OrganizationID: req.OrganizationID,
|
|
OutletID: req.OutletID,
|
|
ParentID: req.ParentID,
|
|
}
|
|
|
|
// Apply optional filter for ChartOfAccountTypeID
|
|
if req.ChartOfAccountTypeID != nil {
|
|
filterEntity.ChartOfAccountTypeID = *req.ChartOfAccountTypeID
|
|
}
|
|
|
|
entities, total, err := p.chartOfAccountRepo.List(ctx, filterEntity)
|
|
if err != nil {
|
|
return nil, 0, fmt.Errorf("failed to list chart of accounts: %w", err)
|
|
}
|
|
|
|
responses := make([]models.ChartOfAccountResponse, len(entities))
|
|
for i, entity := range entities {
|
|
responses[i] = *mappers.ChartOfAccountEntityToResponse(entity)
|
|
}
|
|
|
|
return responses, total, nil
|
|
}
|
|
|
|
func (p *ChartOfAccountProcessorImpl) GetChartOfAccountsByOrganization(ctx context.Context, organizationID uuid.UUID, outletID *uuid.UUID) ([]models.ChartOfAccountResponse, error) {
|
|
entities, err := p.chartOfAccountRepo.GetByOrganization(ctx, organizationID, outletID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get chart of accounts by organization: %w", err)
|
|
}
|
|
|
|
responses := make([]models.ChartOfAccountResponse, len(entities))
|
|
for i, entity := range entities {
|
|
responses[i] = *mappers.ChartOfAccountEntityToResponse(entity)
|
|
}
|
|
|
|
return responses, nil
|
|
}
|
|
|
|
func (p *ChartOfAccountProcessorImpl) GetChartOfAccountsByType(ctx context.Context, organizationID uuid.UUID, chartOfAccountTypeID uuid.UUID, outletID *uuid.UUID) ([]models.ChartOfAccountResponse, error) {
|
|
entities, err := p.chartOfAccountRepo.GetByType(ctx, organizationID, chartOfAccountTypeID, outletID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get chart of accounts by type: %w", err)
|
|
}
|
|
|
|
responses := make([]models.ChartOfAccountResponse, len(entities))
|
|
for i, entity := range entities {
|
|
responses[i] = *mappers.ChartOfAccountEntityToResponse(entity)
|
|
}
|
|
|
|
return responses, nil
|
|
}
|
|
|
|
func (p *ChartOfAccountProcessorImpl) CreateDefaultChartOfAccounts(ctx context.Context, organizationID uuid.UUID, outletID *uuid.UUID) error {
|
|
// This method will be implemented to create default chart of accounts
|
|
// based on the JSON template when a new organization/outlet is created
|
|
// For now, we'll return nil as this is a placeholder
|
|
return nil
|
|
}
|