mirror of
https://gitlab.com/mbugroup/lti-api.git
synced 2026-05-23 14:55:42 +00:00
feat(BE): enhance closing service and repository with actual usage cost calculations and egg weight tracking
This commit is contained in:
@@ -31,6 +31,8 @@ type ClosingRepository interface {
|
||||
FetchSapronakChickinUsageDetails(ctx context.Context, pfkID uint) (map[uint][]SapronakDetailRow, error)
|
||||
FetchSapronakAdjustments(ctx context.Context, kandangID uint) (map[uint][]SapronakDetailRow, map[uint][]SapronakDetailRow, error)
|
||||
FetchSapronakTransfers(ctx context.Context, kandangID uint) (map[uint][]SapronakDetailRow, map[uint][]SapronakDetailRow, error)
|
||||
GetActualUsageCostByProjectFlockID(ctx context.Context, projectFlockID uint) ([]ActualUsageCostRow, error)
|
||||
GetProductsWithFlagsByIDs(ctx context.Context, productIDs []uint) ([]entity.Product, error)
|
||||
}
|
||||
|
||||
type ClosingRepositoryImpl struct {
|
||||
@@ -804,3 +806,150 @@ func (r *ClosingRepositoryImpl) FetchSapronakTransfers(ctx context.Context, kand
|
||||
})
|
||||
return in, out, nil
|
||||
}
|
||||
|
||||
type ActualUsageCostRow struct {
|
||||
ProductID uint `gorm:"column:product_id"`
|
||||
ProductName string `gorm:"column:product_name"`
|
||||
FlagName string `gorm:"column:flag_name"`
|
||||
TotalQty float64 `gorm:"column:total_qty"`
|
||||
TotalPrice float64 `gorm:"column:total_price"`
|
||||
AveragePrice float64 `gorm:"column:average_price"`
|
||||
}
|
||||
|
||||
func (r *ClosingRepositoryImpl) GetActualUsageCostByProjectFlockID(ctx context.Context, projectFlockID uint) ([]ActualUsageCostRow, error) {
|
||||
if projectFlockID == 0 {
|
||||
return []ActualUsageCostRow{}, nil
|
||||
}
|
||||
|
||||
db := r.DB().WithContext(ctx)
|
||||
|
||||
// Get all project flock kandang IDs for this project flock
|
||||
var pfkIDs []uint
|
||||
err := db.Table("project_flock_kandangs").
|
||||
Where("project_flock_id = ?", projectFlockID).
|
||||
Pluck("id", &pfkIDs).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(pfkIDs) == 0 {
|
||||
return []ActualUsageCostRow{}, nil
|
||||
}
|
||||
|
||||
var rows []ActualUsageCostRow
|
||||
|
||||
// Part 1: Get usage from recording_stocks (PAKAN, OVK, Vitamin, Obat, Kimia, dll)
|
||||
purchaseStockableKey := "PURCHASE_ITEMS"
|
||||
transferStockableKey := "STOCK_TRANSFER_DETAILS"
|
||||
|
||||
recordingQuery := db.
|
||||
Table("recordings AS r").
|
||||
Select(`
|
||||
pw.product_id AS product_id,
|
||||
p.name AS product_name,
|
||||
COALESCE(f.name, tf.name) AS flag_name,
|
||||
COALESCE(SUM(
|
||||
CASE
|
||||
WHEN sa.stockable_type = ? THEN COALESCE(sa.qty, 0)
|
||||
WHEN sa.stockable_type = ? THEN COALESCE(std.quantity, 0)
|
||||
ELSE 0
|
||||
END
|
||||
), 0) AS total_qty,
|
||||
COALESCE(SUM(
|
||||
CASE
|
||||
WHEN sa.stockable_type = ? THEN COALESCE(sa.qty, 0) * COALESCE(pi.price, 0)
|
||||
WHEN sa.stockable_type = ? THEN COALESCE(std.quantity, 0) * COALESCE(tpi.price, 0)
|
||||
ELSE 0
|
||||
END
|
||||
), 0) AS total_price,
|
||||
COALESCE(SUM(
|
||||
CASE
|
||||
WHEN sa.stockable_type = ? THEN COALESCE(sa.qty, 0)
|
||||
WHEN sa.stockable_type = ? THEN COALESCE(std.quantity, 0)
|
||||
ELSE 0
|
||||
END
|
||||
), 0) AS qty_divisor,
|
||||
COALESCE(SUM(
|
||||
CASE
|
||||
WHEN sa.stockable_type = ? THEN COALESCE(sa.qty, 0) * COALESCE(pi.price, 0)
|
||||
WHEN sa.stockable_type = ? THEN COALESCE(std.quantity, 0) * COALESCE(tpi.price, 0)
|
||||
ELSE 0
|
||||
END
|
||||
), 0) / NULLIF(COALESCE(SUM(
|
||||
CASE
|
||||
WHEN sa.stockable_type = ? THEN COALESCE(sa.qty, 0)
|
||||
WHEN sa.stockable_type = ? THEN COALESCE(std.quantity, 0)
|
||||
ELSE 0
|
||||
END
|
||||
), 0), 0) AS average_price`,
|
||||
purchaseStockableKey, transferStockableKey,
|
||||
purchaseStockableKey, transferStockableKey,
|
||||
purchaseStockableKey, transferStockableKey,
|
||||
purchaseStockableKey, transferStockableKey,
|
||||
purchaseStockableKey, transferStockableKey).
|
||||
Joins("JOIN recording_stocks AS rs ON rs.recording_id = r.id").
|
||||
Joins("JOIN product_warehouses AS pw ON pw.id = rs.product_warehouse_id").
|
||||
Joins("JOIN products AS p ON p.id = pw.product_id").
|
||||
Joins("LEFT JOIN stock_allocations AS sa ON sa.usable_type = ? AND sa.usable_id = rs.id AND sa.status = ?",
|
||||
"recording_stocks", entity.StockAllocationStatusActive).
|
||||
Joins("LEFT JOIN purchase_items AS pi ON pi.id = sa.stockable_id AND sa.stockable_type = ?", purchaseStockableKey).
|
||||
Joins("LEFT JOIN stock_transfer_details AS std ON std.id = sa.stockable_id AND sa.stockable_type = ?", transferStockableKey).
|
||||
Joins("LEFT JOIN stock_transfers AS st ON st.id = std.stock_transfer_id").
|
||||
Joins("LEFT JOIN purchase_items AS tpi ON tpi.product_id = std.product_id AND tpi.warehouse_id = st.from_warehouse_id").
|
||||
Joins("LEFT JOIN flags AS f ON f.flagable_id = pi.product_id AND f.flagable_type = ?", entity.FlagableTypeProduct).
|
||||
Joins("LEFT JOIN flags AS tf ON tf.flagable_id = std.product_id AND tf.flagable_type = ?", entity.FlagableTypeProduct).
|
||||
Where("r.project_flock_kandangs_id IN ?", pfkIDs).
|
||||
Where("r.deleted_at IS NULL").
|
||||
Group("pw.product_id, p.name, COALESCE(f.name, tf.name)")
|
||||
|
||||
if err := recordingQuery.Scan(&rows).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Part 2: Get usage from project_chickins (DOC, Pullet)
|
||||
chickinQuery := db.
|
||||
Table("project_chickins AS pc").
|
||||
Select(`
|
||||
pw.product_id AS product_id,
|
||||
p.name AS product_name,
|
||||
f.name AS flag_name,
|
||||
COALESCE(SUM(pc.usage_qty), 0) AS total_qty,
|
||||
COALESCE(SUM(pc.usage_qty * COALESCE(pi.price, 0)), 0) AS total_price,
|
||||
COALESCE(AVG(COALESCE(pi.price, 0)), 0) AS average_price
|
||||
`).
|
||||
Joins("JOIN product_warehouses AS pw ON pw.id = pc.product_warehouse_id").
|
||||
Joins("JOIN products AS p ON p.id = pw.product_id").
|
||||
Joins("LEFT JOIN purchase_items AS pi ON pi.product_warehouse_id = pc.product_warehouse_id").
|
||||
Joins("LEFT JOIN flags AS f ON f.flagable_id = p.id AND f.flagable_type = ?", entity.FlagableTypeProduct).
|
||||
Where("pc.project_flock_kandang_id IN ?", pfkIDs).
|
||||
Where("pc.usage_qty > 0").
|
||||
Group("pw.product_id, p.name, f.name")
|
||||
|
||||
var chickinRows []ActualUsageCostRow
|
||||
if err := chickinQuery.Scan(&chickinRows).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Merge results
|
||||
rows = append(rows, chickinRows...)
|
||||
|
||||
return rows, nil
|
||||
}
|
||||
|
||||
func (r *ClosingRepositoryImpl) GetProductsWithFlagsByIDs(ctx context.Context, productIDs []uint) ([]entity.Product, error) {
|
||||
if len(productIDs) == 0 {
|
||||
return []entity.Product{}, nil
|
||||
}
|
||||
|
||||
var products []entity.Product
|
||||
err := r.DB().WithContext(ctx).
|
||||
Preload("Flags").
|
||||
Where("id IN ?", productIDs).
|
||||
Find(&products).Error
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return products, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user