diff --git a/internal/modules/finance/transactions/module.go b/internal/modules/finance/transactions/module.go index c98931a3..01b68621 100644 --- a/internal/modules/finance/transactions/module.go +++ b/internal/modules/finance/transactions/module.go @@ -35,7 +35,8 @@ func (TransactionModule) RegisterRoutes(router fiber.Router, db *gorm.DB, valida panic(fmt.Sprintf("failed to register injection approval workflow: %v", err)) } - transactionService := sTransaction.NewTransactionService(transactionRepo, approvalService, validate) + fifoPaymentService := commonSvc.NewFifoPaymentService(db, utils.Log) + transactionService := sTransaction.NewTransactionService(transactionRepo, approvalService, fifoPaymentService, validate) userService := sUser.NewUserService(userRepo, validate) TransactionRoutes(router, userService, transactionService) diff --git a/internal/modules/finance/transactions/services/transaction.service.go b/internal/modules/finance/transactions/services/transaction.service.go index d58c4aa3..4ace9ca8 100644 --- a/internal/modules/finance/transactions/services/transaction.service.go +++ b/internal/modules/finance/transactions/services/transaction.service.go @@ -30,19 +30,22 @@ type transactionService struct { Validate *validator.Validate Repository repository.TransactionRepository ApprovalSvc commonSvc.ApprovalService + FifoPaymentSvc commonSvc.FifoPaymentService approvalWorkflows map[string]approvalutils.ApprovalWorkflowKey } func NewTransactionService( repo repository.TransactionRepository, approvalSvc commonSvc.ApprovalService, + fifoPaymentSvc commonSvc.FifoPaymentService, validate *validator.Validate, ) TransactionService { return &transactionService{ - Log: utils.Log, - Validate: validate, - Repository: repo, - ApprovalSvc: approvalSvc, + Log: utils.Log, + Validate: validate, + Repository: repo, + ApprovalSvc: approvalSvc, + FifoPaymentSvc: fifoPaymentSvc, approvalWorkflows: map[string]approvalutils.ApprovalWorkflowKey{ string(utils.TransactionTypeSaldoAwal): utils.ApprovalWorkflowInitial, string(utils.TransactionTypeInjection): utils.ApprovalWorkflowInjection, @@ -182,6 +185,19 @@ func (s transactionService) GetOne(c *fiber.Ctx, id uint) (*entity.Payment, erro } func (s transactionService) DeleteOne(c *fiber.Ctx, id uint) error { + // Snapshot party SEBELUM delete supaya bisa re-FIFO setelah trigger DB + // (`trg_soft_delete_fk_payments`) CASCADE hard-DELETE allocations. + existing, err := s.Repository.GetByID(c.Context(), id, nil) + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return fiber.NewError(fiber.StatusNotFound, "Transaction not found") + } + s.Log.Errorf("Failed to load transaction before delete: %+v", err) + return err + } + partyType := existing.PartyType + partyID := existing.PartyId + if err := s.Repository.DeleteOne(c.Context(), id); err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return fiber.NewError(fiber.StatusNotFound, "Transaction not found") @@ -189,6 +205,14 @@ func (s transactionService) DeleteOne(c *fiber.Ctx, id uint) error { s.Log.Errorf("Failed to delete transaction: %+v", err) return err } + + // Re-FIFO setelah delete agar payment lain yang masih punya unallocated nominal + // otomatis reflow ke MDP/purchase_item/expense_realization yang kekurangan paid. + if s.FifoPaymentSvc != nil && partyID > 0 { + if err := s.FifoPaymentSvc.ReallocateForParty(c.Context(), nil, partyType, partyID); err != nil { + s.Log.Warnf("Failed to reallocate payments after delete (party=%s id=%d): %+v", partyType, partyID, err) + } + } return nil }