FEAT[BE]: add GetAllProductUsageByProjectFlockKandangID method and ProductUsageRow struct

This commit is contained in:
aguhh18
2026-01-15 18:04:37 +07:00
parent 824e96575a
commit 35d128cd52
@@ -39,6 +39,9 @@ type ClosingKeuanganRepository interface {
// Products
GetProductsWithFlagsByIDs(ctx context.Context, productIDs []uint) ([]entity.Product, error)
// All Product Usage
GetAllProductUsageByProjectFlockKandangID(ctx context.Context, projectFlockKandangID uint) ([]ProductUsageRow, error)
}
type ClosingKeuanganRepositoryImpl struct {
@@ -75,6 +78,15 @@ type ActualUsageCostRow struct {
AveragePrice float64 `gorm:"column:average_price"`
}
type ProductUsageRow struct {
ProductID uint `gorm:"column:product_id"`
ProductName string `gorm:"column:product_name"`
FlagNames string `gorm:"column:flag_names"`
TotalQty float64 `gorm:"column:total_qty"`
Price float64 `gorm:"column:price"`
TotalPengeluaran float64 `gorm:"column:total_pengeluaran"`
}
// === EGG PRODUCTION QUERIES ===
// GetTotalEggProductionByProjectFlockID gets total egg production for all kandangs in a project flock
@@ -609,3 +621,153 @@ func (r *ClosingKeuanganRepositoryImpl) GetProductsWithFlagsByIDs(ctx context.Co
return products, nil
}
// GetAllProductUsageByProjectFlockKandangID gets all product usage for a project flock kandang
// Combines data from all usable types: recordings, chickins, marketing, transfers, adjustments
func (r *ClosingKeuanganRepositoryImpl) GetAllProductUsageByProjectFlockKandangID(ctx context.Context, projectFlockKandangID uint) ([]ProductUsageRow, error) {
if projectFlockKandangID == 0 {
return []ProductUsageRow{}, nil
}
var results []ProductUsageRow
rawQuery := `
SELECT
q.product_id,
q.product_name,
f.flag_names,
q.total_qty,
q.price,
q.total_qty * q.price AS total_pengeluaran
FROM (
SELECT
product_id,
product_name,
SUM(total_qty) AS total_qty,
AVG(price) AS price
FROM (
SELECT
pw.product_id,
p.name AS product_name,
COALESCE(SUM(
CASE
WHEN sa.stockable_type = 'PURCHASE_ITEMS' THEN COALESCE(sa.qty, 0)
WHEN sa.stockable_type = 'STOCK_TRANSFER_IN' THEN COALESCE(std.usage_qty, 0)
WHEN sa.stockable_type = 'TRANSFERTOLAYING_IN' THEN COALESCE(ltt.total_used, 0)
WHEN sa.stockable_type = 'ADJUSTMENT_IN' THEN COALESCE(adjs.total_used, 0)
WHEN sa.stockable_type = 'PROJECT_FLOCK_POPULATION' THEN COALESCE(pfp.total_used_qty, 0)
ELSE 0
END
), 0) AS total_qty,
COALESCE(AVG(CASE WHEN sa.stockable_type = 'PURCHASE_ITEMS' THEN COALESCE(pi.price, 0) END), 0) AS price
FROM recordings r
JOIN recording_stocks rs ON rs.recording_id = r.id
JOIN product_warehouses pw ON pw.id = rs.product_warehouse_id
JOIN products p ON p.id = pw.product_id
LEFT JOIN stock_allocations sa ON sa.usable_type = 'RECORDING_STOCK' AND sa.usable_id = rs.id AND sa.status = 'ACTIVE'
LEFT JOIN purchase_items pi ON pi.id = sa.stockable_id AND sa.stockable_type = 'PURCHASE_ITEMS'
LEFT JOIN stock_transfer_details std ON std.id = sa.stockable_id AND sa.stockable_type = 'STOCK_TRANSFER_IN'
LEFT JOIN laying_transfer_targets ltt ON ltt.id = sa.stockable_id AND sa.stockable_type = 'TRANSFERTOLAYING_IN'
LEFT JOIN adjustment_stocks adjs ON adjs.id = sa.stockable_id AND sa.stockable_type = 'ADJUSTMENT_IN'
LEFT JOIN project_flock_populations pfp ON pfp.id = sa.stockable_id AND sa.stockable_type = 'PROJECT_FLOCK_POPULATION'
WHERE r.project_flock_kandangs_id = ?
AND r.deleted_at IS NULL
GROUP BY pw.product_id, p.name
UNION ALL
SELECT
pw.product_id,
p.name AS product_name,
COALESCE(SUM(pc.usage_qty), 0) AS total_qty,
COALESCE(AVG(COALESCE(pi.price, 0)), 0) AS price
FROM project_chickins pc
JOIN product_warehouses pw ON pw.id = pc.product_warehouse_id
JOIN products p ON p.id = pw.product_id
LEFT JOIN purchase_items pi ON pi.product_warehouse_id = pw.id
WHERE pc.project_flock_kandang_id = ?
AND pc.usage_qty > 0
GROUP BY pw.product_id, p.name
UNION ALL
SELECT
pw.product_id,
p.name AS product_name,
COALESCE(SUM(mdp.usage_qty), 0) AS total_qty,
COALESCE(AVG(COALESCE(pi.price, 0)), 0) AS price
FROM marketing_delivery_products mdp
JOIN marketing_products mp ON mp.id = mdp.marketing_product_id
JOIN product_warehouses pw ON pw.id = mp.product_warehouse_id
JOIN products p ON p.id = pw.product_id
LEFT JOIN purchase_items pi ON pi.product_warehouse_id = pw.id
WHERE pw.project_flock_kandang_id = ?
GROUP BY pw.product_id, p.name
UNION ALL
SELECT
pw.product_id,
p.name AS product_name,
COALESCE(SUM(lts.usage_qty), 0) AS total_qty,
COALESCE(AVG(COALESCE(pi.price, 0)), 0) AS price
FROM laying_transfer_sources lts
JOIN laying_transfers lt ON lt.id = lts.laying_transfer_id
JOIN product_warehouses pw ON pw.id = lts.product_warehouse_id
JOIN products p ON p.id = pw.product_id
LEFT JOIN purchase_items pi ON pi.product_warehouse_id = pw.id
WHERE pw.project_flock_kandang_id = ?
GROUP BY pw.product_id, p.name
UNION ALL
SELECT
pw.product_id,
p.name AS product_name,
COALESCE(SUM(std.usage_qty), 0) AS total_qty,
COALESCE(AVG(COALESCE(pi.price, 0)), 0) AS price
FROM stock_transfer_details std
JOIN product_warehouses pw ON pw.id = std.source_product_warehouse_id
JOIN products p ON p.id = std.product_id
LEFT JOIN purchase_items pi ON pi.product_warehouse_id = pw.id
WHERE pw.project_flock_kandang_id = ?
GROUP BY pw.product_id, p.name
UNION ALL
SELECT
pw.product_id,
p.name AS product_name,
COALESCE(SUM(ads.usage_qty), 0) AS total_qty,
COALESCE(AVG(COALESCE(pi.price, 0)), 0) AS price
FROM adjustment_stocks ads
JOIN product_warehouses pw ON pw.id = ads.product_warehouse_id
JOIN products p ON p.id = pw.product_id
LEFT JOIN purchase_items pi ON pi.product_warehouse_id = pw.id
WHERE pw.project_flock_kandang_id = ?
AND ads.usage_qty > 0
GROUP BY pw.product_id, p.name
) x
GROUP BY product_id, product_name
) q
LEFT JOIN (
SELECT
p.id AS product_id,
STRING_AGG(DISTINCT f.name, ', ') AS flag_names
FROM products p
LEFT JOIN flags f ON f.flagable_type = 'products' AND f.flagable_id = p.id
GROUP BY p.id
) f ON f.product_id = q.product_id
ORDER BY q.product_name
`
err := r.DB().WithContext(ctx).
Raw(rawQuery, projectFlockKandangID, projectFlockKandangID, projectFlockKandangID, projectFlockKandangID, projectFlockKandangID, projectFlockKandangID).
Scan(&results).Error
if err != nil {
return nil, fmt.Errorf("failed to get all product usage: %w", err)
}
return results, nil
}