mirror of
https://gitlab.com/mbugroup/lti-api.git
synced 2026-05-20 13:31:56 +00:00
Merge branch 'fix/BE/Report-purchasing-Debt-supplier-and-Closing-counting-sapronak' into 'development'
[FIX/BE-US] debt-supplier only show receive purchase See merge request mbugroup/lti-api!232
This commit is contained in:
@@ -37,6 +37,21 @@ func NewDebtSupplierRepository(db *gorm.DB) DebtSupplierRepository {
|
|||||||
return &debtSupplierRepositoryImpl{db: db}
|
return &debtSupplierRepositoryImpl{db: db}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *debtSupplierRepositoryImpl) latestPurchaseApproval(ctx context.Context) *gorm.DB {
|
||||||
|
return r.db.WithContext(ctx).
|
||||||
|
Table("approvals AS a").
|
||||||
|
Select("a.approvable_id, a.step_number, a.action").
|
||||||
|
Joins(`
|
||||||
|
JOIN (
|
||||||
|
SELECT approvable_id, MAX(action_at) AS latest_action_at
|
||||||
|
FROM approvals
|
||||||
|
WHERE approvable_type = ?
|
||||||
|
GROUP BY approvable_id
|
||||||
|
) AS la ON la.approvable_id = a.approvable_id AND la.latest_action_at = a.action_at`,
|
||||||
|
string(utils.ApprovalWorkflowPurchase),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
func resolveDebtSupplierDateColumn(filterBy string) string {
|
func resolveDebtSupplierDateColumn(filterBy string) string {
|
||||||
switch strings.ToLower(strings.TrimSpace(filterBy)) {
|
switch strings.ToLower(strings.TrimSpace(filterBy)) {
|
||||||
case "po_date":
|
case "po_date":
|
||||||
@@ -54,7 +69,11 @@ func (r *debtSupplierRepositoryImpl) baseSupplierQuery(ctx context.Context, filt
|
|||||||
db := r.db.WithContext(ctx).
|
db := r.db.WithContext(ctx).
|
||||||
Model(&entity.Supplier{}).
|
Model(&entity.Supplier{}).
|
||||||
Joins("JOIN purchases ON purchases.supplier_id = suppliers.id").
|
Joins("JOIN purchases ON purchases.supplier_id = suppliers.id").
|
||||||
Joins("JOIN purchase_items ON purchase_items.purchase_id = purchases.id")
|
Joins("JOIN purchase_items ON purchase_items.purchase_id = purchases.id").
|
||||||
|
Joins("JOIN (?) AS la ON la.approvable_id = purchases.id", r.latestPurchaseApproval(ctx)).
|
||||||
|
Where("la.step_number >= ?", uint16(utils.PurchaseStepReceiving)).
|
||||||
|
Where("(la.action IS NULL OR la.action != ?)", string(entity.ApprovalActionRejected)).
|
||||||
|
Where("purchase_items.received_date IS NOT NULL")
|
||||||
|
|
||||||
if len(filters.SupplierIDs) > 0 {
|
if len(filters.SupplierIDs) > 0 {
|
||||||
db = db.Where("suppliers.id IN ?", filters.SupplierIDs)
|
db = db.Where("suppliers.id IN ?", filters.SupplierIDs)
|
||||||
@@ -207,7 +226,11 @@ func (r *debtSupplierRepositoryImpl) getPurchaseIDs(ctx context.Context, supplie
|
|||||||
Table("purchases").
|
Table("purchases").
|
||||||
Select("DISTINCT purchases.id").
|
Select("DISTINCT purchases.id").
|
||||||
Joins("JOIN purchase_items ON purchase_items.purchase_id = purchases.id").
|
Joins("JOIN purchase_items ON purchase_items.purchase_id = purchases.id").
|
||||||
Where("purchases.supplier_id IN ?", supplierIDs)
|
Joins("JOIN (?) AS la ON la.approvable_id = purchases.id", r.latestPurchaseApproval(ctx)).
|
||||||
|
Where("purchases.supplier_id IN ?", supplierIDs).
|
||||||
|
Where("la.step_number >= ?", uint16(utils.PurchaseStepReceiving)).
|
||||||
|
Where("(la.action IS NULL OR la.action != ?)", string(entity.ApprovalActionRejected)).
|
||||||
|
Where("purchase_items.received_date IS NOT NULL")
|
||||||
|
|
||||||
if filters.StartDate != "" {
|
if filters.StartDate != "" {
|
||||||
if dateFrom, err := utils.ParseDateString(filters.StartDate); err == nil {
|
if dateFrom, err := utils.ParseDateString(filters.StartDate); err == nil {
|
||||||
@@ -355,7 +378,11 @@ func (r *debtSupplierRepositoryImpl) GetPurchaseTotalsBeforeDate(ctx context.Con
|
|||||||
Table("purchases").
|
Table("purchases").
|
||||||
Select("purchases.supplier_id AS supplier_id, SUM(purchase_items.total_price) AS total").
|
Select("purchases.supplier_id AS supplier_id, SUM(purchase_items.total_price) AS total").
|
||||||
Joins("JOIN purchase_items ON purchase_items.purchase_id = purchases.id").
|
Joins("JOIN purchase_items ON purchase_items.purchase_id = purchases.id").
|
||||||
|
Joins("JOIN (?) AS la ON la.approvable_id = purchases.id", r.latestPurchaseApproval(ctx)).
|
||||||
Where("purchases.supplier_id IN ?", supplierIDs).
|
Where("purchases.supplier_id IN ?", supplierIDs).
|
||||||
|
Where("la.step_number >= ?", uint16(utils.PurchaseStepReceiving)).
|
||||||
|
Where("(la.action IS NULL OR la.action != ?)", string(entity.ApprovalActionRejected)).
|
||||||
|
Where("purchase_items.received_date IS NOT NULL").
|
||||||
Where(fmt.Sprintf("DATE(%s) < ?", dateColumn), dateFrom).
|
Where(fmt.Sprintf("DATE(%s) < ?", dateColumn), dateFrom).
|
||||||
Group("purchases.supplier_id").
|
Group("purchases.supplier_id").
|
||||||
Scan(&rows).Error; err != nil {
|
Scan(&rows).Error; err != nil {
|
||||||
|
|||||||
@@ -1156,12 +1156,6 @@ func (s *repportService) GetDebtSupplier(c *fiber.Ctx, params *validation.DebtSu
|
|||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
references := collectDebtSupplierReferences(purchases)
|
|
||||||
paymentSummaries, err := s.DebtSupplierRepo.GetPaymentSummariesByReferences(c.Context(), supplierIDs, references)
|
|
||||||
if err != nil {
|
|
||||||
return nil, 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
location, err := time.LoadLocation("Asia/Jakarta")
|
location, err := time.LoadLocation("Asia/Jakarta")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, fiber.NewError(fiber.StatusInternalServerError, "failed to load timezone configuration")
|
return nil, 0, fiber.NewError(fiber.StatusInternalServerError, "failed to load timezone configuration")
|
||||||
@@ -1176,6 +1170,16 @@ func (s *repportService) GetDebtSupplier(c *fiber.Ctx, params *validation.DebtSu
|
|||||||
DeltaBalance float64
|
DeltaBalance float64
|
||||||
CountTotals bool
|
CountTotals bool
|
||||||
}
|
}
|
||||||
|
type debtSupplierAllocation struct {
|
||||||
|
RowIndex int
|
||||||
|
SortTime time.Time
|
||||||
|
Amount float64
|
||||||
|
Purchase entity.Purchase
|
||||||
|
}
|
||||||
|
type paymentAllocation struct {
|
||||||
|
Date time.Time
|
||||||
|
Amount float64
|
||||||
|
}
|
||||||
|
|
||||||
for _, supplierID := range supplierIDs {
|
for _, supplierID := range supplierIDs {
|
||||||
supplier, exists := supplierMap[supplierID]
|
supplier, exists := supplierMap[supplierID]
|
||||||
@@ -1189,19 +1193,11 @@ func (s *repportService) GetDebtSupplier(c *fiber.Ctx, params *validation.DebtSu
|
|||||||
total := dto.DebtSupplierTotalDTO{}
|
total := dto.DebtSupplierTotalDTO{}
|
||||||
|
|
||||||
combinedRows := make([]debtSupplierRowItem, 0, len(items)+len(paymentItems))
|
combinedRows := make([]debtSupplierRowItem, 0, len(items)+len(paymentItems))
|
||||||
|
purchaseAllocations := make([]debtSupplierAllocation, 0, len(items))
|
||||||
for _, purchase := range items {
|
for _, purchase := range items {
|
||||||
row := buildDebtSupplierRow(purchase, now, location)
|
row := buildDebtSupplierRow(purchase, now, location)
|
||||||
if reference := resolveDebtSupplierReference(purchase); reference != "" {
|
|
||||||
if summary, ok := paymentSummaries[reference]; ok {
|
|
||||||
if isDebtSupplierPaid(row.TotalPrice, summary.Total) {
|
|
||||||
row.Status = "Lunas"
|
|
||||||
if !summary.LatestPaymentDate.IsZero() {
|
|
||||||
row.Aging = calculateDebtSupplierAging(purchase, summary.LatestPaymentDate, location)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sortTime := resolveDebtSupplierSortTime(purchase, params.FilterBy, location)
|
sortTime := resolveDebtSupplierSortTime(purchase, params.FilterBy, location)
|
||||||
|
rowIndex := len(combinedRows)
|
||||||
combinedRows = append(combinedRows, debtSupplierRowItem{
|
combinedRows = append(combinedRows, debtSupplierRowItem{
|
||||||
Row: row,
|
Row: row,
|
||||||
SortTime: sortTime,
|
SortTime: sortTime,
|
||||||
@@ -1209,6 +1205,24 @@ func (s *repportService) GetDebtSupplier(c *fiber.Ctx, params *validation.DebtSu
|
|||||||
DeltaBalance: -row.TotalPrice,
|
DeltaBalance: -row.TotalPrice,
|
||||||
CountTotals: true,
|
CountTotals: true,
|
||||||
})
|
})
|
||||||
|
purchaseAllocations = append(purchaseAllocations, debtSupplierAllocation{
|
||||||
|
RowIndex: rowIndex,
|
||||||
|
SortTime: sortTime,
|
||||||
|
Amount: row.TotalPrice,
|
||||||
|
Purchase: purchase,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
paymentAllocations := make([]paymentAllocation, 0, len(paymentItems)+1)
|
||||||
|
initialAllocation := initialBalanceTotals[supplierID] + initialPaymentTotals[supplierID] - initialPurchaseTotals[supplierID]
|
||||||
|
paymentCarry := 0.0
|
||||||
|
if initialAllocation > 0 && len(purchaseAllocations) > 0 {
|
||||||
|
paymentAllocations = append(paymentAllocations, paymentAllocation{
|
||||||
|
Date: purchaseAllocations[0].SortTime,
|
||||||
|
Amount: initialAllocation,
|
||||||
|
})
|
||||||
|
} else if initialAllocation < 0 {
|
||||||
|
paymentCarry = -initialAllocation
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, payment := range paymentItems {
|
for _, payment := range paymentItems {
|
||||||
@@ -1221,6 +1235,53 @@ func (s *repportService) GetDebtSupplier(c *fiber.Ctx, params *validation.DebtSu
|
|||||||
DeltaBalance: payment.Nominal,
|
DeltaBalance: payment.Nominal,
|
||||||
CountTotals: false,
|
CountTotals: false,
|
||||||
})
|
})
|
||||||
|
paymentAllocations = append(paymentAllocations, paymentAllocation{
|
||||||
|
Date: sortTime,
|
||||||
|
Amount: payment.Nominal,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(purchaseAllocations) > 0 && len(paymentAllocations) > 0 {
|
||||||
|
sort.SliceStable(purchaseAllocations, func(i, j int) bool {
|
||||||
|
return purchaseAllocations[i].SortTime.Before(purchaseAllocations[j].SortTime)
|
||||||
|
})
|
||||||
|
sort.SliceStable(paymentAllocations, func(i, j int) bool {
|
||||||
|
return paymentAllocations[i].Date.Before(paymentAllocations[j].Date)
|
||||||
|
})
|
||||||
|
remaining := make([]float64, len(purchaseAllocations))
|
||||||
|
for i := range purchaseAllocations {
|
||||||
|
remaining[i] = purchaseAllocations[i].Amount
|
||||||
|
}
|
||||||
|
purchaseIndex := 0
|
||||||
|
for _, pay := range paymentAllocations {
|
||||||
|
amount := pay.Amount
|
||||||
|
if amount <= 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if paymentCarry > 0 {
|
||||||
|
used := math.Min(amount, paymentCarry)
|
||||||
|
paymentCarry -= used
|
||||||
|
amount -= used
|
||||||
|
}
|
||||||
|
for amount > 0 && purchaseIndex < len(remaining) {
|
||||||
|
if remaining[purchaseIndex] <= 0 {
|
||||||
|
purchaseIndex++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
used := math.Min(amount, remaining[purchaseIndex])
|
||||||
|
remaining[purchaseIndex] -= used
|
||||||
|
amount -= used
|
||||||
|
if remaining[purchaseIndex] <= 0.000001 {
|
||||||
|
allocation := purchaseAllocations[purchaseIndex]
|
||||||
|
combinedRows[allocation.RowIndex].Row.Status = "Lunas"
|
||||||
|
combinedRows[allocation.RowIndex].Row.Aging = calculateDebtSupplierAging(allocation.Purchase, pay.Date, location)
|
||||||
|
purchaseIndex++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if purchaseIndex >= len(remaining) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sort.SliceStable(combinedRows, func(i, j int) bool {
|
sort.SliceStable(combinedRows, func(i, j int) bool {
|
||||||
|
|||||||
Reference in New Issue
Block a user