mirror of
https://gitlab.com/mbugroup/lti-api.git
synced 2026-05-23 14:55:42 +00:00
feat[BE]: implement customer payment report retrieval with pagination and filtering
This commit is contained in:
@@ -19,6 +19,7 @@ import (
|
||||
expenseRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/expenses/repositories"
|
||||
marketingRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/marketing/repositories"
|
||||
areaDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/areas/dto"
|
||||
customerDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/customers/dto"
|
||||
supplierDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/suppliers/dto"
|
||||
warehouseDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/warehouses/dto"
|
||||
chickinRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/production/chickins/repositories"
|
||||
@@ -40,12 +41,13 @@ type RepportService interface {
|
||||
GetDebtSupplier(ctx *fiber.Ctx, params *validation.DebtSupplierQuery) ([]dto.DebtSupplierDTO, int64, error)
|
||||
GetHppPerKandang(ctx *fiber.Ctx) (*dto.HppPerKandangResponseData, *dto.HppPerKandangMetaDTO, error)
|
||||
GetProductionResult(ctx *fiber.Ctx, params *validation.ProductionResultQuery) ([]dto.ProductionResultDTO, int64, error)
|
||||
GetCustomerPayment(ctx *fiber.Ctx) (int, error)
|
||||
GetCustomerPayment(ctx *fiber.Ctx, params *validation.CustomerPaymentQuery) ([]dto.CustomerPaymentReportItem, int64, error)
|
||||
}
|
||||
|
||||
type repportService struct {
|
||||
Log *logrus.Logger
|
||||
Validate *validator.Validate
|
||||
DB *gorm.DB
|
||||
ExpenseRealizationRepo expenseRepo.ExpenseRealizationRepository
|
||||
MarketingDeliveryRepo marketingRepo.MarketingDeliveryProductRepository
|
||||
PurchaseRepo purchaseRepo.PurchaseRepository
|
||||
@@ -56,6 +58,7 @@ type repportService struct {
|
||||
DebtSupplierRepo repportRepo.DebtSupplierRepository
|
||||
HppPerKandangRepo repportRepo.HppPerKandangRepository
|
||||
ProductionResultRepo repportRepo.ProductionResultRepository
|
||||
CustomerPaymentRepo repportRepo.CustomerPaymentRepository
|
||||
}
|
||||
|
||||
type HppCostAggregate struct {
|
||||
@@ -68,6 +71,7 @@ type HppCostAggregate struct {
|
||||
}
|
||||
|
||||
func NewRepportService(
|
||||
db *gorm.DB,
|
||||
validate *validator.Validate,
|
||||
expenseRealizationRepo expenseRepo.ExpenseRealizationRepository,
|
||||
marketingDeliveryRepo marketingRepo.MarketingDeliveryProductRepository,
|
||||
@@ -79,10 +83,12 @@ func NewRepportService(
|
||||
debtSupplierRepo repportRepo.DebtSupplierRepository,
|
||||
hppPerKandangRepo repportRepo.HppPerKandangRepository,
|
||||
productionResultRepo repportRepo.ProductionResultRepository,
|
||||
customerPaymentRepo repportRepo.CustomerPaymentRepository,
|
||||
) RepportService {
|
||||
return &repportService{
|
||||
Log: utils.Log,
|
||||
Validate: validate,
|
||||
DB: db,
|
||||
ExpenseRealizationRepo: expenseRealizationRepo,
|
||||
MarketingDeliveryRepo: marketingDeliveryRepo,
|
||||
PurchaseRepo: purchaseRepo,
|
||||
@@ -93,6 +99,7 @@ func NewRepportService(
|
||||
DebtSupplierRepo: debtSupplierRepo,
|
||||
HppPerKandangRepo: hppPerKandangRepo,
|
||||
ProductionResultRepo: productionResultRepo,
|
||||
CustomerPaymentRepo: customerPaymentRepo,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -308,6 +315,163 @@ func (s *repportService) GetProductionResult(ctx *fiber.Ctx, params *validation.
|
||||
return weeklyResults, totalWeeks, nil
|
||||
}
|
||||
|
||||
func (s *repportService) GetCustomerPayment(ctx *fiber.Ctx, params *validation.CustomerPaymentQuery) ([]dto.CustomerPaymentReportItem, int64, error) {
|
||||
if err := s.Validate.Struct(params); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
// Determine customer IDs to process
|
||||
var customerIDs []uint
|
||||
var totalCustomers int64
|
||||
|
||||
if params.CustomerID != nil {
|
||||
// Single customer mode
|
||||
customerIDs = []uint{*params.CustomerID}
|
||||
totalCustomers = 1
|
||||
} else {
|
||||
// Multiple customers mode with pagination
|
||||
page := params.Page
|
||||
limit := params.Limit
|
||||
if page < 1 {
|
||||
page = 1
|
||||
}
|
||||
if limit < 1 {
|
||||
limit = 10
|
||||
}
|
||||
|
||||
offset := (page - 1) * limit
|
||||
|
||||
var err error
|
||||
customerIDs, totalCustomers, err = s.CustomerPaymentRepo.GetCustomerIDsWithTransactions(ctx.Context(), limit, offset)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
if len(customerIDs) == 0 {
|
||||
return []dto.CustomerPaymentReportItem{}, 0, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Process each customer
|
||||
var result []dto.CustomerPaymentReportItem
|
||||
for _, customerID := range customerIDs {
|
||||
item, err := s.processCustomerPayment(ctx.Context(), customerID)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
result = append(result, item)
|
||||
}
|
||||
|
||||
return result, totalCustomers, nil
|
||||
}
|
||||
|
||||
func (s *repportService) processCustomerPayment(ctx context.Context, customerID uint) (dto.CustomerPaymentReportItem, error) {
|
||||
// Get customer info
|
||||
customer := entity.Customer{}
|
||||
if err := s.DB.WithContext(ctx).
|
||||
Where("id = ?", customerID).
|
||||
First(&customer).Error; err != nil {
|
||||
return dto.CustomerPaymentReportItem{}, err
|
||||
}
|
||||
|
||||
// Get initial balance
|
||||
initialBalance, err := s.CustomerPaymentRepo.GetInitialBalanceByCustomer(ctx, customerID)
|
||||
if err != nil {
|
||||
return dto.CustomerPaymentReportItem{}, err
|
||||
}
|
||||
|
||||
// Get transactions for this customer
|
||||
cid := customerID
|
||||
transactions, err := s.CustomerPaymentRepo.GetCustomerPaymentTransactions(ctx, &cid)
|
||||
if err != nil {
|
||||
return dto.CustomerPaymentReportItem{}, err
|
||||
}
|
||||
|
||||
// Process transactions and calculate running balance
|
||||
rows := make([]dto.CustomerPaymentReportRow, 0, len(transactions))
|
||||
runningBalance := initialBalance
|
||||
|
||||
for _, tx := range transactions {
|
||||
row := dto.CustomerPaymentReportRow{
|
||||
TransactionType: tx.TransactionType,
|
||||
TransactionID: tx.TransactionID,
|
||||
TransDate: tx.TransDate,
|
||||
DeliveryDate: tx.DeliveryDate,
|
||||
Reference: tx.Reference,
|
||||
VehicleNumbers: tx.VehicleNumbers,
|
||||
Qty: tx.Qty,
|
||||
Weight: tx.Weight,
|
||||
AverageWeight: tx.AverageWeight,
|
||||
Price: tx.Price,
|
||||
CreditNote: tx.CreditNote,
|
||||
FinalPrice: tx.FinalPrice,
|
||||
PPN: tx.PPN,
|
||||
TotalPrice: tx.TotalPrice,
|
||||
PaymentAmount: tx.PaymentAmount,
|
||||
PickupInfo: tx.PickupInfo,
|
||||
SalesPerson: tx.SalesPerson,
|
||||
}
|
||||
|
||||
// Calculate running balance
|
||||
if tx.TransactionType == "SALES" {
|
||||
runningBalance -= tx.TotalPrice
|
||||
// Status will be calculated later (requires looking ahead)
|
||||
row.Status = ""
|
||||
row.AgingDay = 0 // Will be calculated later
|
||||
} else if tx.TransactionType == "PAYMENT" {
|
||||
runningBalance += tx.PaymentAmount
|
||||
row.Status = ""
|
||||
row.AgingDay = 0
|
||||
}
|
||||
|
||||
row.AccountsReceivable = runningBalance
|
||||
rows = append(rows, row)
|
||||
}
|
||||
|
||||
// Calculate summary
|
||||
summary := s.calculateSummary(rows, initialBalance)
|
||||
|
||||
// Build customer DTO
|
||||
customerDTO := customerDTO.CustomerRelationDTO{
|
||||
Id: customer.Id,
|
||||
Name: customer.Name,
|
||||
Type: customer.Type,
|
||||
AccountNumber: customer.AccountNumber,
|
||||
Balance: customer.Balance,
|
||||
}
|
||||
|
||||
return dto.CustomerPaymentReportItem{
|
||||
Customer: customerDTO,
|
||||
InitialBalance: initialBalance,
|
||||
Rows: rows,
|
||||
Summary: summary,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *repportService) calculateSummary(rows []dto.CustomerPaymentReportRow, initialBalance float64) dto.CustomerPaymentReportSummary {
|
||||
summary := dto.CustomerPaymentReportSummary{}
|
||||
|
||||
for _, row := range rows {
|
||||
summary.TotalQty += row.Qty
|
||||
summary.TotalWeight += row.Weight
|
||||
summary.TotalCreditNote += row.CreditNote
|
||||
summary.TotalPPN += row.PPN
|
||||
|
||||
if row.TransactionType == "SALES" {
|
||||
summary.TotalInitialAmount += row.TotalPrice
|
||||
summary.TotalFinalAmount += row.FinalPrice
|
||||
summary.TotalGrandAmount += row.TotalPrice
|
||||
} else if row.TransactionType == "PAYMENT" {
|
||||
summary.TotalPayment += row.PaymentAmount
|
||||
}
|
||||
}
|
||||
|
||||
// Final AR = initial balance + total sales - total payment
|
||||
summary.TotalAccountsReceivable = initialBalance + summary.TotalGrandAmount - summary.TotalPayment
|
||||
|
||||
return summary
|
||||
}
|
||||
|
||||
func mapRecordingToProductionResultDTO(record entity.Recording) dto.ProductionResultDTO {
|
||||
result := dto.ProductionResultDTO{
|
||||
CreatedAt: record.CreatedAt,
|
||||
@@ -1374,11 +1538,6 @@ func (s *repportService) parseHppPerKandangQuery(ctx *fiber.Ctx) (*validation.Hp
|
||||
return params, filters, nil
|
||||
}
|
||||
|
||||
func (s *repportService) GetCustomerPayment(c *fiber.Ctx) (int, error) {
|
||||
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func parseCommaSeparatedInt64s(raw string) ([]int64, error) {
|
||||
raw = strings.TrimSpace(raw)
|
||||
if raw == "" {
|
||||
|
||||
Reference in New Issue
Block a user