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] fix closing count sapronak,expense notes purchase See merge request mbugroup/lti-api!255
This commit is contained in:
@@ -196,7 +196,11 @@ func ToSapronakProjectAggregatedFromReport(report *SapronakReportDTO, flag strin
|
||||
}
|
||||
|
||||
for idx, item := range group.Items {
|
||||
productKey := strings.ToUpper(flagKey + "|" + item.ProductName + "|" + item.NoReferensi + "|" + formatDate(item.Tanggal))
|
||||
refKey := strings.TrimSpace(item.NoReferensi)
|
||||
productKey := strings.ToUpper(flagKey + "|" + item.ProductName + "|" + refKey)
|
||||
if refKey == "" {
|
||||
productKey = strings.ToUpper(flagKey + "|" + item.ProductName + "|" + formatDate(item.Tanggal))
|
||||
}
|
||||
baseRow := SapronakCategoryRowDTO{
|
||||
ID: idx + 1,
|
||||
Date: formatDate(item.Tanggal),
|
||||
@@ -212,6 +216,9 @@ func ToSapronakProjectAggregatedFromReport(report *SapronakReportDTO, flag strin
|
||||
switch strings.ToLower(item.JenisTransaksi) {
|
||||
case "pembelian", "adjustment masuk", "mutasi masuk":
|
||||
row.QtyIn += item.QtyMasuk
|
||||
if item.Tanggal != nil {
|
||||
row.Date = formatDate(item.Tanggal)
|
||||
}
|
||||
if row.UnitPrice == 0 {
|
||||
if item.QtyMasuk > 0 && item.Nilai > 0 {
|
||||
row.UnitPrice = item.Nilai / item.QtyMasuk
|
||||
|
||||
@@ -709,6 +709,23 @@ var (
|
||||
sapronakFlagsChickin = sapronakFlags(utils.FlagDOC, utils.FlagPullet)
|
||||
)
|
||||
|
||||
func (r *ClosingRepositoryImpl) joinSapronakProductFlag(db *gorm.DB, productAlias string) *gorm.DB {
|
||||
subquery := r.DB().
|
||||
Table("flags").
|
||||
Select("DISTINCT ON (flagable_id) flagable_id, name").
|
||||
Where("flagable_type = ?", entity.FlagableTypeProduct).
|
||||
Where("name IN ?", sapronakFlagsAll).
|
||||
Order(fmt.Sprintf(
|
||||
"flagable_id, CASE WHEN name = '%s' THEN 1 WHEN name = '%s' THEN 2 WHEN name = '%s' THEN 3 WHEN name = '%s' THEN 4 ELSE 5 END",
|
||||
utils.FlagDOC,
|
||||
utils.FlagPullet,
|
||||
utils.FlagPakan,
|
||||
utils.FlagOVK,
|
||||
))
|
||||
|
||||
return db.Joins("JOIN (?) f ON f.flagable_id = "+productAlias+".id", subquery)
|
||||
}
|
||||
|
||||
func groupSapronakDetails(rows []SapronakDetailRow) map[uint][]SapronakDetailRow {
|
||||
m := make(map[uint][]SapronakDetailRow)
|
||||
for _, row := range rows {
|
||||
@@ -745,11 +762,12 @@ func (r *ClosingRepositoryImpl) usageQuery(
|
||||
COALESCE(p.product_price, 0) AS default_price
|
||||
`)
|
||||
db = applyJoins(db, joins...)
|
||||
return db.
|
||||
db = db.
|
||||
Joins("JOIN product_warehouses pw ON "+pwJoinCond).
|
||||
Joins("JOIN products p ON p.id = pw.product_id").
|
||||
Joins("JOIN flags f ON f.flagable_id = p.id AND f.flagable_type = ?", entity.FlagableTypeProduct).
|
||||
Where(where, args...)
|
||||
db = r.joinSapronakProductFlag(db, "p")
|
||||
return db
|
||||
}
|
||||
|
||||
func (r *ClosingRepositoryImpl) fetchSapronakUsage(
|
||||
@@ -780,10 +798,10 @@ func (r *ClosingRepositoryImpl) detailQuery(
|
||||
db := r.withCtx(ctx).
|
||||
Table(table).
|
||||
Joins("JOIN product_warehouses pw ON "+pwJoinCond).
|
||||
Joins("JOIN products p ON p.id = pw.product_id").
|
||||
Joins("JOIN flags f ON f.flagable_id = p.id AND f.flagable_type = ?", entity.FlagableTypeProduct)
|
||||
Joins("JOIN products p ON p.id = pw.product_id")
|
||||
|
||||
db = applyJoins(db, joins...)
|
||||
db = r.joinSapronakProductFlag(db, "p")
|
||||
return db.Select(selectSQL).Where(where, args...)
|
||||
}
|
||||
|
||||
@@ -907,7 +925,6 @@ func (r *ClosingRepositoryImpl) FetchSapronakUsageAllocatedDetails(ctx context.C
|
||||
`).
|
||||
Joins("JOIN product_warehouses pw ON pw.id = sa.product_warehouse_id").
|
||||
Joins("JOIN products p ON p.id = pw.product_id").
|
||||
Joins("JOIN flags f ON f.flagable_id = p.id AND f.flagable_type = ?", entity.FlagableTypeProduct).
|
||||
Joins("LEFT JOIN recording_stocks rs ON rs.id = sa.usable_id AND sa.usable_type = ?", fifo.UsableKeyRecordingStock.String()).
|
||||
Joins("LEFT JOIN recordings r ON r.id = rs.recording_id").
|
||||
Joins("LEFT JOIN project_chickins pc_used ON pc_used.id = sa.usable_id AND sa.usable_type = ?", fifo.UsableKeyProjectChickin.String()).
|
||||
@@ -930,7 +947,8 @@ func (r *ClosingRepositoryImpl) FetchSapronakUsageAllocatedDetails(ctx context.C
|
||||
`,
|
||||
fifo.UsableKeyRecordingStock.String(), projectFlockKandangID,
|
||||
fifo.UsableKeyProjectChickin.String(), projectFlockKandangID,
|
||||
).
|
||||
)
|
||||
query = r.joinSapronakProductFlag(query, "p").
|
||||
Group(`
|
||||
pw.product_id, p.name, f.name,
|
||||
pi.received_date, st.transfer_date, lt.transfer_date, sl.created_at, pc.chick_in_date, r.record_datetime,
|
||||
@@ -942,15 +960,15 @@ func (r *ClosingRepositoryImpl) FetchSapronakUsageAllocatedDetails(ctx context.C
|
||||
}
|
||||
|
||||
func (r *ClosingRepositoryImpl) incomingPurchaseBase(ctx context.Context, kandangID uint) *gorm.DB {
|
||||
return r.withCtx(ctx).
|
||||
db := r.withCtx(ctx).
|
||||
Table("purchase_items AS pi").
|
||||
Joins("JOIN purchases po ON po.id = pi.purchase_id AND po.deleted_at IS NULL").
|
||||
Joins("JOIN products p ON p.id = pi.product_id").
|
||||
Joins("JOIN flags f ON f.flagable_id = p.id AND f.flagable_type = ?", entity.FlagableTypeProduct).
|
||||
Joins("JOIN warehouses w ON w.id = pi.warehouse_id").
|
||||
Where("w.kandang_id = ?", kandangID).
|
||||
Where("f.name IN ?", sapronakFlagsAll).
|
||||
Where("pi.received_date IS NOT NULL")
|
||||
return r.joinSapronakProductFlag(db, "p")
|
||||
}
|
||||
|
||||
func (r *ClosingRepositoryImpl) FetchSapronakIncoming(ctx context.Context, kandangID uint) ([]SapronakIncomingRow, error) {
|
||||
@@ -1021,10 +1039,10 @@ func (r *ClosingRepositoryImpl) fetchStockLogs(ctx context.Context, kandangID ui
|
||||
`).
|
||||
Joins("JOIN product_warehouses pw ON pw.id = sl.product_warehouse_id").
|
||||
Joins("JOIN products p ON p.id = pw.product_id").
|
||||
Joins("JOIN flags f ON f.flagable_id = p.id AND f.flagable_type = ?", entity.FlagableTypeProduct).
|
||||
Joins("JOIN warehouses w ON w.id = pw.warehouse_id")
|
||||
|
||||
db = applyJoins(db, joins...)
|
||||
db = r.joinSapronakProductFlag(db, "p")
|
||||
|
||||
if err := db.
|
||||
Where("sl.loggable_type = ?", logType).
|
||||
@@ -1093,10 +1111,10 @@ func (r *ClosingRepositoryImpl) FetchSapronakTransfers(ctx context.Context, kand
|
||||
Joins("JOIN product_warehouses pw ON pw.id = std.dest_product_warehouse_id").
|
||||
Joins("JOIN warehouses w ON w.id = pw.warehouse_id").
|
||||
Joins("JOIN products p ON p.id = std.product_id").
|
||||
Joins("JOIN flags f ON f.flagable_id = p.id AND f.flagable_type = ?", entity.FlagableTypeProduct).
|
||||
Where("w.kandang_id = ?", kandangID).
|
||||
Where("(fw.kandang_id IS NULL OR fw.kandang_id <> w.kandang_id)").
|
||||
Where("f.name IN ?", sapronakFlagsAll)
|
||||
incomingQuery = r.joinSapronakProductFlag(incomingQuery, "p")
|
||||
incoming, err := scanAndGroupDetails(incomingQuery)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
@@ -1121,10 +1139,10 @@ func (r *ClosingRepositoryImpl) FetchSapronakTransfers(ctx context.Context, kand
|
||||
Joins("JOIN product_warehouses pw ON pw.id = ltt.product_warehouse_id").
|
||||
Joins("JOIN warehouses w ON w.id = pw.warehouse_id").
|
||||
Joins("JOIN products p ON p.id = pw.product_id").
|
||||
Joins("JOIN flags f ON f.flagable_id = p.id AND f.flagable_type = ?", entity.FlagableTypeProduct).
|
||||
Where("w.kandang_id = ?", kandangID).
|
||||
Where("(w_source.kandang_id IS NULL OR w_source.kandang_id <> w.kandang_id)").
|
||||
Where("f.name IN ?", sapronakFlagsAll)
|
||||
incomingLayingQuery = r.joinSapronakProductFlag(incomingLayingQuery, "p")
|
||||
incomingLaying, err := scanAndGroupDetails(incomingLayingQuery)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
@@ -1152,12 +1170,12 @@ func (r *ClosingRepositoryImpl) FetchSapronakTransfers(ctx context.Context, kand
|
||||
Joins("LEFT JOIN product_warehouses pw_dest ON pw_dest.id = std.dest_product_warehouse_id").
|
||||
Joins("LEFT JOIN warehouses w_dest ON w_dest.id = pw_dest.warehouse_id").
|
||||
Joins("JOIN products p ON p.id = std.product_id").
|
||||
Joins("JOIN flags f ON f.flagable_id = p.id AND f.flagable_type = ?", entity.FlagableTypeProduct).
|
||||
Where("sa.status = ?", entity.StockAllocationStatusActive).
|
||||
Where("w.kandang_id = ?", kandangID).
|
||||
Where("(w_dest.kandang_id IS NULL OR w_dest.kandang_id <> w.kandang_id)").
|
||||
Where("f.name IN ?", sapronakFlagsAll).
|
||||
Group("std.id, std.product_id, p.name, f.name, st.transfer_date, st.movement_number, p.product_price")
|
||||
outgoingQuery = r.joinSapronakProductFlag(outgoingQuery, "p")
|
||||
outgoing, err := scanAndGroupDetails(outgoingQuery)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
@@ -1183,12 +1201,12 @@ func (r *ClosingRepositoryImpl) FetchSapronakTransfers(ctx context.Context, kand
|
||||
Joins("JOIN product_warehouses pw ON pw.id = sa.product_warehouse_id").
|
||||
Joins("JOIN warehouses w ON w.id = pw.warehouse_id").
|
||||
Joins("JOIN products p ON p.id = pw.product_id").
|
||||
Joins("JOIN flags f ON f.flagable_id = p.id AND f.flagable_type = ?", entity.FlagableTypeProduct).
|
||||
Where("sa.status = ?", entity.StockAllocationStatusActive).
|
||||
Where("w.kandang_id = ?", kandangID).
|
||||
Where("(w_dest.kandang_id IS NULL OR w_dest.kandang_id <> w.kandang_id)").
|
||||
Where("f.name IN ?", sapronakFlagsAll).
|
||||
Group("lts.id, pw.product_id, p.name, f.name, lt.transfer_date, lt.transfer_number, p.product_price")
|
||||
outgoingLayingQuery = r.joinSapronakProductFlag(outgoingLayingQuery, "p")
|
||||
outgoingLaying, err := scanAndGroupDetails(outgoingLayingQuery)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
@@ -1218,12 +1236,12 @@ func (r *ClosingRepositoryImpl) FetchSapronakSales(ctx context.Context, projectF
|
||||
Joins("JOIN marketings m ON m.id = mp.marketing_id").
|
||||
Joins("JOIN product_warehouses pw ON pw.id = sa.product_warehouse_id").
|
||||
Joins("JOIN products p ON p.id = pw.product_id").
|
||||
Joins("JOIN flags f ON f.flagable_id = p.id AND f.flagable_type = ?", entity.FlagableTypeProduct).
|
||||
Where("sa.status = ?", entity.StockAllocationStatusActive).
|
||||
Where("pw.project_flock_kandang_id = ?", projectFlockKandangID).
|
||||
Where("f.name IN ?", sapronakFlagsAll).
|
||||
Group("mdp.id, pw.product_id, p.name, f.name, mdp.delivery_date, mdp.created_at, m.so_number, mdp.unit_price, mp.unit_price")
|
||||
|
||||
query = r.joinSapronakProductFlag(query, "p")
|
||||
sales, err := scanAndGroupDetails(query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -1245,7 +1263,6 @@ func (r *ClosingRepositoryImpl) FetchSapronakSales(ctx context.Context, projectF
|
||||
Joins("JOIN marketings m ON m.id = mp.marketing_id").
|
||||
Joins("JOIN product_warehouses pw ON pw.id = mp.product_warehouse_id").
|
||||
Joins("JOIN products p ON p.id = pw.product_id").
|
||||
Joins("JOIN flags f ON f.flagable_id = p.id AND f.flagable_type = ?", entity.FlagableTypeProduct).
|
||||
Joins("LEFT JOIN stock_allocations sa ON sa.usable_id = mdp.id AND sa.usable_type = ? AND sa.status = ?",
|
||||
fifo.UsableKeyMarketingDelivery.String(),
|
||||
entity.StockAllocationStatusActive,
|
||||
@@ -1256,6 +1273,7 @@ func (r *ClosingRepositoryImpl) FetchSapronakSales(ctx context.Context, projectF
|
||||
Where("f.name IN ?", sapronakFlagsAll).
|
||||
Group("mdp.id, pw.product_id, p.name, f.name, mdp.delivery_date, mdp.created_at, m.so_number, mdp.unit_price, mp.unit_price")
|
||||
|
||||
nonFifoQuery = r.joinSapronakProductFlag(nonFifoQuery, "p")
|
||||
nonFifoSales, err := scanAndGroupDetails(nonFifoQuery)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -45,10 +45,7 @@ type groupedItem struct {
|
||||
projectFK *uint
|
||||
kandangID *uint
|
||||
totalPrice float64
|
||||
}
|
||||
|
||||
func groupingKey(supplierID uint, date time.Time, warehouseID uint) string {
|
||||
return fmt.Sprintf("%d:%s:%d", supplierID, utils.FormatDate(date), warehouseID)
|
||||
poNumber string
|
||||
}
|
||||
|
||||
type expenseBridge struct {
|
||||
@@ -222,6 +219,7 @@ func (b *expenseBridge) OnItemsReceived(c *fiber.Ctx, purchaseID uint, updates [
|
||||
purchase, err := b.purchaseRepo.GetByID(ctx, purchaseID, func(db *gorm.DB) *gorm.DB {
|
||||
return db.
|
||||
Preload("Items").
|
||||
Preload("Items.Product").
|
||||
Preload("Items.Warehouse").
|
||||
Preload("Items.Warehouse.Kandang")
|
||||
})
|
||||
@@ -309,7 +307,7 @@ func (b *expenseBridge) OnItemsReceived(c *fiber.Ctx, purchaseID uint, updates [
|
||||
|
||||
// If supplier/date unchanged, update nonstock in place.
|
||||
if oldSupplier == supplierID && oldDate.Equal(newDate) {
|
||||
note := fmt.Sprintf("purchase_item:%d", payload.PurchaseItemID)
|
||||
note := purchaseItemDisplayNote(item, payload.PurchaseItemID, purchasePoNumber(purchase))
|
||||
if err := b.db.WithContext(ctx).
|
||||
Model(&entity.ExpenseNonstock{}).
|
||||
Where("id = ?", link.ExpenseNonstockID).
|
||||
@@ -340,7 +338,7 @@ func (b *expenseBridge) OnItemsReceived(c *fiber.Ctx, purchaseID uint, updates [
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
note := fmt.Sprintf("purchase_item:%d", payload.PurchaseItemID)
|
||||
note := purchaseItemDisplayNote(item, payload.PurchaseItemID, purchasePoNumber(purchase))
|
||||
if err := b.db.WithContext(ctx).
|
||||
Model(&entity.Expense{}).
|
||||
Where("id = ?", link.ExpenseID).
|
||||
@@ -392,6 +390,7 @@ func (b *expenseBridge) OnItemsReceived(c *fiber.Ctx, purchaseID uint, updates [
|
||||
projectFK: projectFK,
|
||||
kandangID: kandangID,
|
||||
totalPrice: totalPrice,
|
||||
poNumber: purchasePoNumber(purchase),
|
||||
}
|
||||
|
||||
newNonstockID, err := b.findExpeditionNonstockID(ctx, supplierID)
|
||||
@@ -410,7 +409,7 @@ func (b *expenseBridge) OnItemsReceived(c *fiber.Ctx, purchaseID uint, updates [
|
||||
createdNonstockID = noteMap[payload.PurchaseItemID]
|
||||
}
|
||||
|
||||
note := fmt.Sprintf("purchase_item:%d", payload.PurchaseItemID)
|
||||
note := purchaseItemDisplayNote(item, payload.PurchaseItemID, purchasePoNumber(purchase))
|
||||
updateBody := map[string]interface{}{
|
||||
"expense_id": expenseDetail.Id,
|
||||
"qty": payload.ReceivedQty,
|
||||
@@ -483,6 +482,7 @@ func (b *expenseBridge) OnItemsReceived(c *fiber.Ctx, purchaseID uint, updates [
|
||||
projectFK: projectFK,
|
||||
kandangID: kandangID,
|
||||
totalPrice: totalPrice,
|
||||
poNumber: purchasePoNumber(purchase),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -679,6 +679,14 @@ func (b *expenseBridge) linkExpenseNonstocksToItems(ctx context.Context, detail
|
||||
Update("expense_nonstock_id", expenseNonstockID).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
note := purchaseItemDisplayNote(gi.item, gi.payload.PurchaseItemID, gi.poNumber)
|
||||
if err := b.db.WithContext(ctx).
|
||||
Model(&entity.ExpenseNonstock{}).
|
||||
Where("id = ?", expenseNonstockID).
|
||||
Update("notes", note).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -709,3 +717,22 @@ func mapExpenseNotes(detail *expenseDto.ExpenseDetailDTO) map[uint]uint64 {
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func purchaseItemDisplayNote(item *entity.PurchaseItem, itemID uint, poNumber string) string {
|
||||
poLabel := "PO"
|
||||
if strings.TrimSpace(poNumber) != "" {
|
||||
poLabel = strings.TrimSpace(poNumber)
|
||||
}
|
||||
productName := fmt.Sprintf("Item %d", itemID)
|
||||
if item != nil && item.Product != nil && strings.TrimSpace(item.Product.Name) != "" {
|
||||
productName = item.Product.Name
|
||||
}
|
||||
return fmt.Sprintf("%s (%s)", poLabel, productName)
|
||||
}
|
||||
|
||||
func purchasePoNumber(purchase *entity.Purchase) string {
|
||||
if purchase == nil || purchase.PoNumber == nil {
|
||||
return ""
|
||||
}
|
||||
return *purchase.PoNumber
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user