mirror of
https://gitlab.com/mbugroup/lti-api.git
synced 2026-05-20 13:31:56 +00:00
feat(BE):Rekapitulasi hutang supplier
This commit is contained in:
@@ -18,6 +18,9 @@ import (
|
||||
approvalDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/approvals/dto"
|
||||
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"
|
||||
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"
|
||||
recordingRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/production/recordings/repositories"
|
||||
purchaseRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/purchases/repositories"
|
||||
@@ -34,6 +37,7 @@ type RepportService interface {
|
||||
GetExpense(ctx *fiber.Ctx, params *validation.ExpenseQuery) ([]dto.RepportExpenseListDTO, int64, error)
|
||||
GetMarketing(ctx *fiber.Ctx, params *validation.MarketingQuery) ([]dto.RepportMarketingItemDTO, int64, error)
|
||||
GetPurchaseSupplier(ctx *fiber.Ctx, params *validation.PurchaseSupplierQuery) ([]dto.PurchaseSupplierDTO, int64, error)
|
||||
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)
|
||||
}
|
||||
@@ -48,6 +52,7 @@ type repportService struct {
|
||||
RecordingRepo recordingRepo.RecordingRepository
|
||||
ApprovalSvc approvalService.ApprovalService
|
||||
PurchaseSupplierRepo repportRepo.PurchaseSupplierRepository
|
||||
DebtSupplierRepo repportRepo.DebtSupplierRepository
|
||||
HppPerKandangRepo repportRepo.HppPerKandangRepository
|
||||
ProductionResultRepo repportRepo.ProductionResultRepository
|
||||
}
|
||||
@@ -70,6 +75,7 @@ func NewRepportService(
|
||||
recordingRepo recordingRepo.RecordingRepository,
|
||||
approvalSvc approvalService.ApprovalService,
|
||||
purchaseSupplierRepo repportRepo.PurchaseSupplierRepository,
|
||||
debtSupplierRepo repportRepo.DebtSupplierRepository,
|
||||
hppPerKandangRepo repportRepo.HppPerKandangRepository,
|
||||
productionResultRepo repportRepo.ProductionResultRepository,
|
||||
) RepportService {
|
||||
@@ -83,6 +89,7 @@ func NewRepportService(
|
||||
RecordingRepo: recordingRepo,
|
||||
ApprovalSvc: approvalSvc,
|
||||
PurchaseSupplierRepo: purchaseSupplierRepo,
|
||||
DebtSupplierRepo: debtSupplierRepo,
|
||||
HppPerKandangRepo: hppPerKandangRepo,
|
||||
ProductionResultRepo: productionResultRepo,
|
||||
}
|
||||
@@ -634,6 +641,200 @@ func (s *repportService) GetPurchaseSupplier(c *fiber.Ctx, params *validation.Pu
|
||||
return result, totalSuppliers, nil
|
||||
}
|
||||
|
||||
func (s *repportService) GetDebtSupplier(c *fiber.Ctx, params *validation.DebtSupplierQuery) ([]dto.DebtSupplierDTO, int64, error) {
|
||||
if params.FilterBy == "" {
|
||||
params.FilterBy = "do_date"
|
||||
}
|
||||
|
||||
if err := s.Validate.Struct(params); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
offset := (params.Page - 1) * params.Limit
|
||||
if offset < 0 {
|
||||
offset = 0
|
||||
}
|
||||
|
||||
suppliers, totalSuppliers, err := s.DebtSupplierRepo.GetSuppliersWithPurchases(c.Context(), offset, params.Limit, params)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
if totalSuppliers == 0 || len(suppliers) == 0 {
|
||||
return []dto.DebtSupplierDTO{}, totalSuppliers, nil
|
||||
}
|
||||
|
||||
supplierMap := make(map[uint]entity.Supplier, len(suppliers))
|
||||
supplierIDs := make([]uint, 0, len(suppliers))
|
||||
for _, supplier := range suppliers {
|
||||
supplierMap[supplier.Id] = supplier
|
||||
supplierIDs = append(supplierIDs, supplier.Id)
|
||||
}
|
||||
|
||||
purchases, err := s.DebtSupplierRepo.GetPurchasesBySuppliers(c.Context(), supplierIDs, params)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
purchasesBySupplier := make(map[uint][]entity.Purchase, len(supplierIDs))
|
||||
references := make([]string, 0)
|
||||
seenRefs := make(map[string]struct{})
|
||||
for _, purchase := range purchases {
|
||||
supplierID := purchase.SupplierId
|
||||
purchasesBySupplier[supplierID] = append(purchasesBySupplier[supplierID], purchase)
|
||||
|
||||
reference := purchase.PrNumber
|
||||
if purchase.PoNumber != nil && strings.TrimSpace(*purchase.PoNumber) != "" {
|
||||
reference = *purchase.PoNumber
|
||||
}
|
||||
if _, exists := seenRefs[reference]; !exists {
|
||||
seenRefs[reference] = struct{}{}
|
||||
references = append(references, reference)
|
||||
}
|
||||
}
|
||||
|
||||
paymentTotals, err := s.DebtSupplierRepo.GetPaymentTotalsByReferences(c.Context(), supplierIDs, references)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
location, err := time.LoadLocation("Asia/Jakarta")
|
||||
if err != nil {
|
||||
return nil, 0, fiber.NewError(fiber.StatusInternalServerError, "failed to load timezone configuration")
|
||||
}
|
||||
now := time.Now().In(location)
|
||||
|
||||
result := make([]dto.DebtSupplierDTO, 0, len(supplierIDs))
|
||||
for _, supplierID := range supplierIDs {
|
||||
supplier, exists := supplierMap[supplierID]
|
||||
if !exists {
|
||||
continue
|
||||
}
|
||||
|
||||
items := purchasesBySupplier[supplierID]
|
||||
rows := make([]dto.DebtSupplierRowDTO, 0, len(items))
|
||||
total := dto.DebtSupplierTotalDTO{}
|
||||
|
||||
for _, purchase := range items {
|
||||
row := buildDebtSupplierRow(purchase, paymentTotals, now, location)
|
||||
rows = append(rows, row)
|
||||
|
||||
if row.Aging > total.Aging {
|
||||
total.Aging = row.Aging
|
||||
}
|
||||
total.TotalPrice += row.TotalPrice
|
||||
total.PaymentPrice += row.PaymentPrice
|
||||
total.DebtPrice += row.DebtPrice
|
||||
}
|
||||
|
||||
sortDesc := strings.EqualFold(params.SortOrder, "desc")
|
||||
sort.SliceStable(rows, func(i, j int) bool {
|
||||
if sortDesc {
|
||||
return rows[i].PrDate > rows[j].PrDate
|
||||
}
|
||||
return rows[i].PrDate < rows[j].PrDate
|
||||
})
|
||||
|
||||
var supplierDTORef *supplierDTO.SupplierRelationDTO
|
||||
if supplier.Id != 0 {
|
||||
mapped := supplierDTO.ToSupplierRelationDTO(supplier)
|
||||
supplierDTORef = &mapped
|
||||
}
|
||||
|
||||
result = append(result, dto.DebtSupplierDTO{
|
||||
Supplier: supplierDTORef,
|
||||
Rows: rows,
|
||||
Total: total,
|
||||
})
|
||||
}
|
||||
|
||||
return result, totalSuppliers, nil
|
||||
}
|
||||
|
||||
func buildDebtSupplierRow(purchase entity.Purchase, paymentTotals map[string]float64, now time.Time, loc *time.Location) dto.DebtSupplierRowDTO {
|
||||
prNumber := purchase.PrNumber
|
||||
poNumber := ""
|
||||
if purchase.PoNumber != nil {
|
||||
poNumber = *purchase.PoNumber
|
||||
}
|
||||
|
||||
reference := prNumber
|
||||
if strings.TrimSpace(poNumber) != "" {
|
||||
reference = poNumber
|
||||
}
|
||||
|
||||
prDate := purchase.CreatedAt.In(loc)
|
||||
startDate := time.Date(prDate.Year(), prDate.Month(), prDate.Day(), 0, 0, 0, 0, loc)
|
||||
endDate := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, loc)
|
||||
aging := int(endDate.Sub(startDate).Hours() / 24)
|
||||
|
||||
totalPrice := 0.0
|
||||
travelNumber := "-"
|
||||
var area *areaDTO.AreaRelationDTO
|
||||
var warehouse *warehouseDTO.WarehouseRelationDTO
|
||||
|
||||
if len(purchase.Items) > 0 {
|
||||
firstItem := purchase.Items[0]
|
||||
if firstItem.TravelNumber != nil && strings.TrimSpace(*firstItem.TravelNumber) != "" {
|
||||
travelNumber = *firstItem.TravelNumber
|
||||
}
|
||||
|
||||
if firstItem.Warehouse != nil && firstItem.Warehouse.Id != 0 {
|
||||
mappedWarehouse := warehouseDTO.ToWarehouseRelationDTO(*firstItem.Warehouse)
|
||||
warehouse = &mappedWarehouse
|
||||
if firstItem.Warehouse.Area.Id != 0 {
|
||||
mappedArea := areaDTO.ToAreaRelationDTO(firstItem.Warehouse.Area)
|
||||
area = &mappedArea
|
||||
}
|
||||
}
|
||||
|
||||
for _, item := range purchase.Items {
|
||||
totalPrice += item.TotalPrice
|
||||
}
|
||||
}
|
||||
|
||||
paymentPrice := paymentTotals[reference]
|
||||
debtPrice := paymentPrice - totalPrice
|
||||
|
||||
dueDate := ""
|
||||
dueStatus := "-"
|
||||
if purchase.DueDate != nil && !purchase.DueDate.IsZero() {
|
||||
due := purchase.DueDate.In(loc)
|
||||
dueDate = due.Format("2006-01-02")
|
||||
if now.After(due) {
|
||||
dueStatus = "Sudah Jatuh Tempo"
|
||||
} else {
|
||||
dueStatus = "Mendekati Jatuh Tempo"
|
||||
}
|
||||
}
|
||||
|
||||
status := "Belum Lunas"
|
||||
if debtPrice >= 0 {
|
||||
status = "Lunas"
|
||||
}
|
||||
|
||||
poDate := ""
|
||||
if purchase.PoDate != nil && !purchase.PoDate.IsZero() {
|
||||
poDate = purchase.PoDate.In(loc).Format("2006-01-02")
|
||||
}
|
||||
|
||||
return dto.DebtSupplierRowDTO{
|
||||
PrNumber: prNumber,
|
||||
PoNumber: poNumber,
|
||||
PrDate: prDate.Format("2006-01-02"),
|
||||
PoDate: poDate,
|
||||
Aging: aging,
|
||||
Area: area,
|
||||
Warehouse: warehouse,
|
||||
DueDate: dueDate,
|
||||
DueStatus: dueStatus,
|
||||
TotalPrice: totalPrice,
|
||||
PaymentPrice: paymentPrice,
|
||||
DebtPrice: debtPrice,
|
||||
Status: status,
|
||||
TravelNumber: travelNumber,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *repportService) GetHppPerKandang(ctx *fiber.Ctx) (*dto.HppPerKandangResponseData, *dto.HppPerKandangMetaDTO, error) {
|
||||
params, filters, err := s.parseHppPerKandangQuery(ctx)
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user