mirror of
https://gitlab.com/mbugroup/lti-api.git
synced 2026-06-09 15:07:49 +00:00
adjust hpp per farm query to take feed and ovk
This commit is contained in:
@@ -25,9 +25,13 @@ type hppV2RepoStub struct {
|
||||
chickinRowsByKey map[string][]commonRepo.HppV2ChickinCostRow
|
||||
expenseRowsByPFKKey map[string][]commonRepo.HppV2ExpenseCostRow
|
||||
expenseRowsByFarmKey map[string][]commonRepo.HppV2ExpenseCostRow
|
||||
routeCostByProject map[uint]float64
|
||||
totalPopulationByKey map[string]float64
|
||||
transferSummaryByPFK map[uint]struct {
|
||||
// expenseRowsByFarmDateKey (opsional) membuat ListExpenseRealizationRowsByProjectFlockID
|
||||
// date-aware untuk menguji perhitungan range BOP. Bila non-nil, dipakai menggantikan
|
||||
// expenseRowsByFarmKey; key = "<flock>|<ekspedisi>|<YYYY-MM-DD>".
|
||||
expenseRowsByFarmDateKey map[string][]commonRepo.HppV2ExpenseCostRow
|
||||
routeCostByProject map[uint]float64
|
||||
totalPopulationByKey map[string]float64
|
||||
transferSummaryByPFK map[uint]struct {
|
||||
projectFlockID uint
|
||||
totalQty float64
|
||||
}
|
||||
@@ -118,6 +122,10 @@ func (s *hppV2RepoStub) ListUsageCostRowsByProductFlags(_ context.Context, proje
|
||||
return append([]commonRepo.HppV2UsageCostRow{}, s.usageRowsByKey[stubKey(projectFlockKandangIDs, flagNames)]...), nil
|
||||
}
|
||||
|
||||
func (s *hppV2RepoStub) ListLayingUsageCostRowsByProductFlags(_ context.Context, layingProjectFlockKandangID uint, flagNames []string, _ *time.Time) ([]commonRepo.HppV2UsageCostRow, error) {
|
||||
return append([]commonRepo.HppV2UsageCostRow{}, s.usageRowsByKey[stubKey([]uint{layingProjectFlockKandangID}, flagNames)]...), nil
|
||||
}
|
||||
|
||||
func (s *hppV2RepoStub) ListAdjustmentCostRowsByProductFlags(_ context.Context, projectFlockKandangIDs []uint, flagNames []string, _ *time.Time) ([]commonRepo.HppV2AdjustmentCostRow, error) {
|
||||
return append([]commonRepo.HppV2AdjustmentCostRow{}, s.adjustRowsByKey[stubKey(projectFlockKandangIDs, flagNames)]...), nil
|
||||
}
|
||||
@@ -126,7 +134,10 @@ func (s *hppV2RepoStub) ListExpenseRealizationRowsByProjectFlockKandangIDs(_ con
|
||||
return append([]commonRepo.HppV2ExpenseCostRow{}, s.expenseRowsByPFKKey[expenseStubKey(projectFlockKandangIDs, ekspedisi)]...), nil
|
||||
}
|
||||
|
||||
func (s *hppV2RepoStub) ListExpenseRealizationRowsByProjectFlockID(_ context.Context, projectFlockID uint, _ *time.Time, ekspedisi bool) ([]commonRepo.HppV2ExpenseCostRow, error) {
|
||||
func (s *hppV2RepoStub) ListExpenseRealizationRowsByProjectFlockID(_ context.Context, projectFlockID uint, date *time.Time, ekspedisi bool) ([]commonRepo.HppV2ExpenseCostRow, error) {
|
||||
if s.expenseRowsByFarmDateKey != nil && date != nil {
|
||||
return append([]commonRepo.HppV2ExpenseCostRow{}, s.expenseRowsByFarmDateKey[expenseFarmDateKey(projectFlockID, ekspedisi, *date)]...), nil
|
||||
}
|
||||
return append([]commonRepo.HppV2ExpenseCostRow{}, s.expenseRowsByFarmKey[expenseFarmKey(projectFlockID, ekspedisi)]...), nil
|
||||
}
|
||||
|
||||
@@ -904,6 +915,108 @@ func expenseFarmKey(projectFlockID uint, ekspedisi bool) string {
|
||||
return fmt.Sprintf("farm=%d|ekspedisi=%t", projectFlockID, ekspedisi)
|
||||
}
|
||||
|
||||
func expenseFarmDateKey(projectFlockID uint, ekspedisi bool, date time.Time) string {
|
||||
return fmt.Sprintf("%d|%t|%s", projectFlockID, ekspedisi, date.Format("2006-01-02"))
|
||||
}
|
||||
|
||||
func chickinStubKey(ids []uint, flags []string, excludeTransferToLaying bool) string {
|
||||
return stubKey(ids, append(append([]string{}, flags...), fmt.Sprintf("exclude_transfer_to_laying=%t", excludeTransferToLaying)))
|
||||
}
|
||||
|
||||
// TestHppV2PakanBreakdown_LayingAttributesLokasiFeedAsProductionCost membuktikan Fix 1:
|
||||
// untuk kandang LAYING, pemakaian pakan (termasuk dari gudang LOKASI dengan pfk NULL) diatribusikan
|
||||
// sebagai production_cost via ListLayingUsageCostRowsByProductFlags — BUKAN pullet_cost.
|
||||
// Stub memetakan ListLayingUsageCostRowsByProductFlags(50,...) ke usageRowsByKey[[50]+PAKAN].
|
||||
func TestHppV2PakanBreakdown_LayingAttributesLokasiFeedAsProductionCost(t *testing.T) {
|
||||
repo := &hppV2RepoStub{
|
||||
contextByPFK: map[uint]*commonRepo.HppV2ProjectFlockKandangContext{
|
||||
50: {
|
||||
ProjectFlockKandangID: 50,
|
||||
ProjectFlockID: 20,
|
||||
ProjectFlockCategory: string(utils.ProjectFlockCategoryLaying),
|
||||
KandangID: 1,
|
||||
LocationID: 14,
|
||||
HouseType: "close_house",
|
||||
},
|
||||
},
|
||||
usageRowsByKey: map[string][]commonRepo.HppV2UsageCostRow{
|
||||
stubKey([]uint{50}, []string{"PAKAN"}): {
|
||||
{StockableType: "purchase_items", StockableID: 9001, SourceProductID: 9, SourceProductName: "Pakan Laying", Qty: 310, UnitPrice: 1, TotalCost: 310},
|
||||
},
|
||||
},
|
||||
// Tanpa transferSummaryByPFK[50] -> growing part nil; tanpa adjustRowsByKey -> laying cutover nil.
|
||||
}
|
||||
|
||||
svc := NewHppV2Service(repo)
|
||||
component, err := svc.GetPakanBreakdown(50, mustDate(t, "2026-05-31"))
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, got %v", err)
|
||||
}
|
||||
if component == nil {
|
||||
t.Fatal("expected PAKAN component")
|
||||
}
|
||||
if component.Total != 310 {
|
||||
t.Fatalf("expected component total 310, got %v", component.Total)
|
||||
}
|
||||
if len(component.Parts) != 1 || component.Parts[0].Code != hppV2PartLayingNormal {
|
||||
t.Fatalf("expected single laying_normal part, got %+v", component.Parts)
|
||||
}
|
||||
if got := componentScopeTotal(component, hppV2ScopeProductionCost); got != 310 {
|
||||
t.Fatalf("expected production_cost 310, got %v", got)
|
||||
}
|
||||
if got := componentScopeTotal(component, hppV2ScopePulletCost); got != 0 {
|
||||
t.Fatalf("expected pullet_cost 0 (feed laying bukan pullet), got %v", got)
|
||||
}
|
||||
}
|
||||
|
||||
// TestHppV2BopProductionScopeRange_NonNegativeAndProrated membuktikan Fix 2: BOP farm-level dihitung
|
||||
// sebagai (expenseCum(end) - expenseCum(start)) × ratio(end) — range-correct & tidak pernah negatif.
|
||||
// Range [2026-04-30, 2026-05-31] -> engine memakai endOfDay: start=2026-05-01, end=2026-06-01.
|
||||
// Share kandang 50 = 30/(30+70) = 0.3.
|
||||
// - REGULAR: expense farm tumbuh 1000 -> 1300 (delta 300) => 300 × 0.3 = 90.
|
||||
// - EKSPEDISI: expense farm "turun" 500 -> 200 (delta -300, kasus uji clamp) => di-clamp ke 0.
|
||||
func TestHppV2BopProductionScopeRange_NonNegativeAndProrated(t *testing.T) {
|
||||
repo := &hppV2RepoStub{
|
||||
contextByPFK: map[uint]*commonRepo.HppV2ProjectFlockKandangContext{
|
||||
50: {ProjectFlockKandangID: 50, ProjectFlockID: 20, ProjectFlockCategory: string(utils.ProjectFlockCategoryLaying)},
|
||||
},
|
||||
pfkIDsByProject: map[uint][]uint{
|
||||
20: {50, 51},
|
||||
},
|
||||
eggProductionByPFK: map[uint]struct {
|
||||
pieces float64
|
||||
kg float64
|
||||
}{
|
||||
50: {pieces: 300, kg: 30},
|
||||
51: {pieces: 700, kg: 70},
|
||||
},
|
||||
expenseRowsByFarmDateKey: map[string][]commonRepo.HppV2ExpenseCostRow{
|
||||
// REGULAR (ekspedisi=false): kumulatif 1000 (start) -> 1300 (end)
|
||||
expenseFarmDateKey(20, false, mustTime(t, "2026-05-01")): {{TotalCost: 1000}},
|
||||
expenseFarmDateKey(20, false, mustTime(t, "2026-06-01")): {{TotalCost: 800}, {TotalCost: 500}},
|
||||
// EKSPEDISI (ekspedisi=true): kumulatif 500 (start) -> 200 (end) => delta negatif, harus di-clamp
|
||||
expenseFarmDateKey(20, true, mustTime(t, "2026-05-01")): {{TotalCost: 500}},
|
||||
expenseFarmDateKey(20, true, mustTime(t, "2026-06-01")): {{TotalCost: 200}},
|
||||
},
|
||||
}
|
||||
|
||||
svc := NewHppV2Service(repo)
|
||||
start := mustDate(t, "2026-04-30")
|
||||
end := mustDate(t, "2026-05-31")
|
||||
|
||||
reg, err := svc.GetBopRegularProductionScopeRange(50, start, end)
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, got %v", err)
|
||||
}
|
||||
if reg != 90 {
|
||||
t.Fatalf("expected BOP regular range 90 (300 × 0.3), got %v", reg)
|
||||
}
|
||||
|
||||
eksp, err := svc.GetBopEkspedisiProductionScopeRange(50, start, end)
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, got %v", err)
|
||||
}
|
||||
if eksp != 0 {
|
||||
t.Fatalf("expected BOP ekspedisi range clamped to 0 (delta negatif), got %v", eksp)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user