From 35d128cd52f42e1ab2274d0a40f190c1a1de0213 Mon Sep 17 00:00:00 2001 From: aguhh18 Date: Thu, 15 Jan 2026 18:04:37 +0700 Subject: [PATCH] FEAT[BE]: add GetAllProductUsageByProjectFlockKandangID method and ProductUsageRow struct --- .../closingKeuangan.repository.go | 162 ++++++++++++++++++ 1 file changed, 162 insertions(+) diff --git a/internal/modules/closings/repositories/closingKeuangan.repository.go b/internal/modules/closings/repositories/closingKeuangan.repository.go index 5dedfa0e..7aa68989 100644 --- a/internal/modules/closings/repositories/closingKeuangan.repository.go +++ b/internal/modules/closings/repositories/closingKeuangan.repository.go @@ -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 +}