feat: doc direct purchase cost

This commit is contained in:
Adnan Zahir
2026-04-19 14:52:01 +07:00
parent 58fbceea24
commit a2ae139fae
7 changed files with 718 additions and 108 deletions
@@ -59,6 +59,19 @@ type HppV2ExpenseCostRow struct {
RealizationDate time.Time
}
type HppV2ChickinCostRow struct {
ProjectChickinID uint
ProjectFlockKandangID uint
ChickInDate time.Time
StockableType string
StockableID uint
SourceProductID uint
SourceProductName string
Qty float64
UnitPrice float64
TotalCost float64
}
type HppV2CostRepository interface {
GetProjectFlockKandangContext(ctx context.Context, projectFlockKandangId uint) (*HppV2ProjectFlockKandangContext, error)
GetProjectFlockKandangIDs(ctx context.Context, projectFlockId uint) ([]uint, error)
@@ -66,6 +79,7 @@ type HppV2CostRepository interface {
ListAdjustmentCostRowsByProductFlags(ctx context.Context, projectFlockKandangIDs []uint, flagNames []string, date *time.Time) ([]HppV2AdjustmentCostRow, error)
ListExpenseRealizationRowsByProjectFlockKandangIDs(ctx context.Context, projectFlockKandangIDs []uint, date *time.Time, ekspedisi bool) ([]HppV2ExpenseCostRow, error)
ListExpenseRealizationRowsByProjectFlockID(ctx context.Context, projectFlockID uint, date *time.Time, ekspedisi bool) ([]HppV2ExpenseCostRow, error)
ListChickinCostRowsByProductFlags(ctx context.Context, projectFlockKandangIDs []uint, flagNames []string, date *time.Time, excludeTransferToLaying bool) ([]HppV2ChickinCostRow, error)
GetFeedUsageCost(ctx context.Context, projectFlockKandangIDs []uint, date *time.Time) (float64, error)
GetTotalPopulation(ctx context.Context, projectFlockKandangIDs []uint) (float64, error)
GetEggProduksiPiecesAndWeightKgByProjectFlockKandangIds(ctx context.Context, projectFlockKandangIDs []uint, date *time.Time) (float64, float64, error)
@@ -251,6 +265,181 @@ func (r *HppV2RepositoryImpl) ListAdjustmentCostRowsByProductFlags(
return rows, nil
}
func (r *HppV2RepositoryImpl) ListChickinCostRowsByProductFlags(
ctx context.Context,
projectFlockKandangIDs []uint,
flagNames []string,
date *time.Time,
excludeTransferToLaying bool,
) ([]HppV2ChickinCostRow, error) {
if len(projectFlockKandangIDs) == 0 || len(flagNames) == 0 {
return []HppV2ChickinCostRow{}, nil
}
if date == nil {
now := time.Now()
date = &now
}
stockablePurchase := fifo.StockableKeyPurchaseItems.String()
stockableAdjustment := fifo.StockableKeyAdjustmentIn.String()
stockableTransferIn := fifo.StockableKeyStockTransferIn.String()
stockableTransferToLaying := fifo.StockableKeyTransferToLayingIn.String()
usableProjectChickin := fifo.UsableKeyProjectChickin.String()
usableStockTransferOut := fifo.UsableKeyStockTransferOut.String()
rows := make([]HppV2ChickinCostRow, 0)
query := r.db.WithContext(ctx).
Table("project_chickins AS pc").
Select(`
pc.id AS project_chickin_id,
pc.project_flock_kandang_id AS project_flock_kandang_id,
pc.chick_in_date AS chick_in_date,
sa.stockable_type AS stockable_type,
sa.stockable_id AS stockable_id,
COALESCE(
pi.product_id,
ast_pw.product_id,
tpi.product_id,
tast_pw.product_id,
spi.product_id,
sast_pw.product_id,
0
) AS source_product_id,
COALESCE(
pi_prod.name,
ast_prod.name,
tpi_prod.name,
tast_prod.name,
spi_prod.name,
sast_prod.name,
''
) AS source_product_name,
COALESCE(SUM(sa.qty), 0) AS qty,
CASE
WHEN sa.stockable_type = ? THEN COALESCE(pi.price, 0)
WHEN sa.stockable_type = ? THEN COALESCE(ast.price, 0)
WHEN sa.stockable_type = ? THEN COALESCE(spi.price, sast.price, 0)
WHEN sa.stockable_type = ? THEN COALESCE(tpi.price, tast.price, 0)
ELSE 0
END AS unit_price,
COALESCE(SUM(sa.qty * CASE
WHEN sa.stockable_type = ? THEN COALESCE(pi.price, 0)
WHEN sa.stockable_type = ? THEN COALESCE(ast.price, 0)
WHEN sa.stockable_type = ? THEN COALESCE(spi.price, sast.price, 0)
WHEN sa.stockable_type = ? THEN COALESCE(tpi.price, tast.price, 0)
ELSE 0
END), 0) AS total_cost
`,
stockablePurchase,
stockableAdjustment,
stockableTransferIn,
stockableTransferToLaying,
stockablePurchase,
stockableAdjustment,
stockableTransferIn,
stockableTransferToLaying,
).
Joins(
"JOIN stock_allocations AS sa ON sa.usable_type = ? AND sa.usable_id = pc.id AND sa.status = ? AND sa.allocation_purpose = ?",
usableProjectChickin,
entity.StockAllocationStatusActive,
entity.StockAllocationPurposeTraceChickin,
).
Joins("LEFT JOIN purchase_items AS pi ON pi.id = sa.stockable_id AND sa.stockable_type = ?", stockablePurchase).
Joins("LEFT JOIN products AS pi_prod ON pi_prod.id = pi.product_id").
Joins("LEFT JOIN adjustment_stocks AS ast ON ast.id = sa.stockable_id AND sa.stockable_type = ?", stockableAdjustment).
Joins("LEFT JOIN product_warehouses AS ast_pw ON ast_pw.id = ast.product_warehouse_id").
Joins("LEFT JOIN products AS ast_prod ON ast_prod.id = ast_pw.product_id").
Joins(
"LEFT JOIN stock_allocations AS tsa_transfer ON tsa_transfer.usable_type = ? AND tsa_transfer.usable_id = sa.stockable_id AND sa.stockable_type = ? AND tsa_transfer.status = ? AND tsa_transfer.allocation_purpose = ?",
stockableTransferToLaying,
stockableTransferToLaying,
entity.StockAllocationStatusActive,
entity.StockAllocationPurposeConsume,
).
Joins("LEFT JOIN purchase_items AS tpi ON tpi.id = tsa_transfer.stockable_id AND tsa_transfer.stockable_type = ?", stockablePurchase).
Joins("LEFT JOIN products AS tpi_prod ON tpi_prod.id = tpi.product_id").
Joins("LEFT JOIN adjustment_stocks AS tast ON tast.id = tsa_transfer.stockable_id AND tsa_transfer.stockable_type = ?", stockableAdjustment).
Joins("LEFT JOIN product_warehouses AS tast_pw ON tast_pw.id = tast.product_warehouse_id").
Joins("LEFT JOIN products AS tast_prod ON tast_prod.id = tast_pw.product_id").
Joins(
"LEFT JOIN stock_allocations AS tsa_stock ON tsa_stock.usable_type = ? AND tsa_stock.usable_id = sa.stockable_id AND sa.stockable_type = ? AND tsa_stock.status = ? AND tsa_stock.allocation_purpose = ?",
usableStockTransferOut,
stockableTransferIn,
entity.StockAllocationStatusActive,
entity.StockAllocationPurposeConsume,
).
Joins("LEFT JOIN purchase_items AS spi ON spi.id = tsa_stock.stockable_id AND tsa_stock.stockable_type = ?", stockablePurchase).
Joins("LEFT JOIN products AS spi_prod ON spi_prod.id = spi.product_id").
Joins("LEFT JOIN adjustment_stocks AS sast ON sast.id = tsa_stock.stockable_id AND tsa_stock.stockable_type = ?", stockableAdjustment).
Joins("LEFT JOIN product_warehouses AS sast_pw ON sast_pw.id = sast.product_warehouse_id").
Joins("LEFT JOIN products AS sast_prod ON sast_prod.id = sast_pw.product_id").
Where("pc.project_flock_kandang_id IN ?", projectFlockKandangIDs).
Where("pc.chick_in_date <= ?", *date).
Where(`
EXISTS (
SELECT 1
FROM flags f
WHERE f.flagable_type = ?
AND f.flagable_id = COALESCE(
pi.product_id,
ast_pw.product_id,
tpi.product_id,
tast_pw.product_id,
spi.product_id,
sast_pw.product_id,
0
)
AND f.name IN ?
)
`, entity.FlagableTypeProduct, flagNames)
if excludeTransferToLaying {
query = query.Where("sa.stockable_type <> ?", stockableTransferToLaying)
}
err := query.
Group(`
pc.id,
pc.project_flock_kandang_id,
pc.chick_in_date,
sa.stockable_type,
sa.stockable_id,
COALESCE(
pi.product_id,
ast_pw.product_id,
tpi.product_id,
tast_pw.product_id,
spi.product_id,
sast_pw.product_id,
0
),
COALESCE(
pi_prod.name,
ast_prod.name,
tpi_prod.name,
tast_prod.name,
spi_prod.name,
sast_prod.name,
''
),
CASE
WHEN sa.stockable_type = '` + stockablePurchase + `' THEN COALESCE(pi.price, 0)
WHEN sa.stockable_type = '` + stockableAdjustment + `' THEN COALESCE(ast.price, 0)
WHEN sa.stockable_type = '` + stockableTransferIn + `' THEN COALESCE(spi.price, sast.price, 0)
WHEN sa.stockable_type = '` + stockableTransferToLaying + `' THEN COALESCE(tpi.price, tast.price, 0)
ELSE 0
END
`).
Order("pc.chick_in_date ASC, pc.id ASC, sa.stockable_type ASC, sa.stockable_id ASC").
Scan(&rows).Error
if err != nil {
return nil, err
}
return rows, nil
}
func (r *HppV2RepositoryImpl) ListExpenseRealizationRowsByProjectFlockKandangIDs(
ctx context.Context,
projectFlockKandangIDs []uint,