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:
@@ -55,6 +55,10 @@ type HppV2Service interface {
|
||||
GetDirectPulletPurchaseBreakdown(projectFlockKandangId uint, endDate *time.Time) (*HppV2Component, error)
|
||||
GetBopRegularBreakdown(projectFlockKandangId uint, endDate *time.Time) (*HppV2Component, error)
|
||||
GetBopEkspedisiBreakdown(projectFlockKandangId uint, endDate *time.Time) (*HppV2Component, error)
|
||||
// GetBopRegularProductionScopeRange / GetBopEkspedisiProductionScopeRange mengembalikan BOP
|
||||
// production_cost untuk rentang [startDate, endDate] secara range-correct (tidak pernah negatif).
|
||||
GetBopRegularProductionScopeRange(projectFlockKandangId uint, startDate, endDate *time.Time) (float64, error)
|
||||
GetBopEkspedisiProductionScopeRange(projectFlockKandangId uint, startDate, endDate *time.Time) (float64, error)
|
||||
GetHppEstimationDanRealisasi(totalProductionCost float64, projectFlockKandangId uint, startDate *time.Time, endDate *time.Time) (*HppCostResponse, error)
|
||||
}
|
||||
|
||||
@@ -453,7 +457,7 @@ func (s *hppV2Service) getStockUsageComponent(projectFlockKandangId uint, endDat
|
||||
total += growingCutoverPart.Total
|
||||
}
|
||||
|
||||
layingNormalPart, err := s.buildLayingUsagePart(projectFlockKandangId, endDate, config, false)
|
||||
layingNormalPart, err := s.buildLayingUsagePart(projectFlockKandangId, contextRow, endDate, config, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -462,7 +466,7 @@ func (s *hppV2Service) getStockUsageComponent(projectFlockKandangId uint, endDat
|
||||
total += layingNormalPart.Total
|
||||
}
|
||||
|
||||
layingCutoverPart, err := s.buildLayingUsagePart(projectFlockKandangId, endDate, config, true)
|
||||
layingCutoverPart, err := s.buildLayingUsagePart(projectFlockKandangId, contextRow, endDate, config, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -737,6 +741,7 @@ func (s *hppV2Service) buildGrowingUsagePart(
|
||||
|
||||
func (s *hppV2Service) buildLayingUsagePart(
|
||||
projectFlockKandangId uint,
|
||||
contextRow *commonRepo.HppV2ProjectFlockKandangContext,
|
||||
endDate *time.Time,
|
||||
config hppV2StockComponentConfig,
|
||||
cutover bool,
|
||||
@@ -778,7 +783,16 @@ func (s *hppV2Service) buildLayingUsagePart(
|
||||
}, nil
|
||||
}
|
||||
|
||||
rows, err := s.hppRepo.ListUsageCostRowsByProductFlags(context.Background(), []uint{projectFlockKandangId}, config.NormalFlags, endDate)
|
||||
// Untuk kandang LAYING, atribusi pakan/OVK berbasis kandang recording (termasuk konsumsi
|
||||
// dari gudang LOKASI yang punya recording_stocks.project_flock_kandang_id = NULL). Untuk
|
||||
// kandang non-laying, pertahankan semantik lama (strict rs.project_flock_kandang_id IN [pfk]).
|
||||
var rows []commonRepo.HppV2UsageCostRow
|
||||
var err error
|
||||
if contextRow != nil && contextRow.ProjectFlockCategory == string(utils.ProjectFlockCategoryLaying) {
|
||||
rows, err = s.hppRepo.ListLayingUsageCostRowsByProductFlags(context.Background(), projectFlockKandangId, config.NormalFlags, endDate)
|
||||
} else {
|
||||
rows, err = s.hppRepo.ListUsageCostRowsByProductFlags(context.Background(), []uint{projectFlockKandangId}, config.NormalFlags, endDate)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -931,17 +945,48 @@ func (s *hppV2Service) buildLayingExpenseFarmPart(
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
farmPFKIDs, err := s.hppRepo.GetProjectFlockKandangIDs(context.Background(), contextRow.ProjectFlockID)
|
||||
ratio, proration, err := s.layingFarmExpenseRatio(projectFlockKandangId, contextRow, endDate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ratio <= 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return buildExpensePartFromRows(
|
||||
rows,
|
||||
hppV2PartLayingFarm,
|
||||
"Laying Farm",
|
||||
[]string{hppV2ScopeProductionCost},
|
||||
proration,
|
||||
ratio,
|
||||
), nil
|
||||
}
|
||||
|
||||
// layingFarmExpenseRatio menghitung porsi (share) kandang laying terhadap seluruh farm pada
|
||||
// endDate berdasarkan bobot telur KUMULATIF (fallback ke jumlah butir bila bobot 0). Return
|
||||
// ratio 0 bila tak terhitung. Diekstrak agar dipakai bersama oleh buildLayingExpenseFarmPart
|
||||
// dan GetExpenseProductionScopeRange (perhitungan BOP range-correct).
|
||||
func (s *hppV2Service) layingFarmExpenseRatio(
|
||||
projectFlockKandangId uint,
|
||||
contextRow *commonRepo.HppV2ProjectFlockKandangContext,
|
||||
endDate *time.Time,
|
||||
) (float64, *HppV2Proration, error) {
|
||||
if contextRow == nil {
|
||||
return 0, nil, nil
|
||||
}
|
||||
|
||||
farmPFKIDs, err := s.hppRepo.GetProjectFlockKandangIDs(context.Background(), contextRow.ProjectFlockID)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
targetPieces, targetWeight, err := s.hppRepo.GetEggProduksiPiecesAndWeightKgByProjectFlockKandangIds(context.Background(), []uint{projectFlockKandangId}, endDate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return 0, nil, err
|
||||
}
|
||||
farmPieces, farmWeight, err := s.hppRepo.GetEggProduksiPiecesAndWeightKgByProjectFlockKandangIds(context.Background(), farmPFKIDs, endDate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
basis := hppV2ProrationEggWeight
|
||||
@@ -953,27 +998,120 @@ func (s *hppV2Service) buildLayingExpenseFarmPart(
|
||||
denominator = farmPieces
|
||||
}
|
||||
if denominator <= 0 {
|
||||
return nil, nil
|
||||
return 0, nil, nil
|
||||
}
|
||||
|
||||
ratio := numerator / denominator
|
||||
if ratio <= 0 {
|
||||
return nil, nil
|
||||
return 0, nil, nil
|
||||
}
|
||||
|
||||
return buildExpensePartFromRows(
|
||||
rows,
|
||||
hppV2PartLayingFarm,
|
||||
"Laying Farm",
|
||||
[]string{hppV2ScopeProductionCost},
|
||||
&HppV2Proration{
|
||||
Basis: basis,
|
||||
Numerator: numerator,
|
||||
Denominator: denominator,
|
||||
Ratio: ratio,
|
||||
},
|
||||
ratio,
|
||||
), nil
|
||||
return ratio, &HppV2Proration{
|
||||
Basis: basis,
|
||||
Numerator: numerator,
|
||||
Denominator: denominator,
|
||||
Ratio: ratio,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetExpenseProductionScopeRange menghitung BOP production_cost satu komponen expense untuk rentang
|
||||
// [startDate, endDate] secara range-correct (tidak pernah negatif untuk expense non-negatif).
|
||||
// - laying-direct (ratio 1, monoton): selisih kumulatif end - start.
|
||||
// - laying-farm (prorated): (expenseCum(end) - expenseCum(start)) × ratio(end).
|
||||
//
|
||||
// Ini mengganti pola lama di report yang men-differensiasi dua angka yang sudah diprorata dengan
|
||||
// ratio berbeda (ratio(end) vs ratio(start)) — sumber bug BOP negatif saat share antar kandang bergeser.
|
||||
func (s *hppV2Service) GetExpenseProductionScopeRange(projectFlockKandangId uint, startDate, endDate *time.Time, config hppV2ExpenseComponentConfig) (float64, error) {
|
||||
if s.hppRepo == nil {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
contextRow, err := s.hppRepo.GetProjectFlockKandangContext(context.Background(), projectFlockKandangId)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// Samakan semantik tanggal dengan CalculateHppBreakdown: kumulatif dihitung sampai AKHIR hari
|
||||
// (endOfDay). Penting karena ratio egg-weight memakai r.record_datetime (granular jam).
|
||||
_, endOfEndDay, err := hppV2DayWindow(endDate)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
_, endOfStartDay, err := hppV2DayWindow(startDate)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// laying-direct: delta kumulatif (monoton, >= 0).
|
||||
directEnd, err := s.buildLayingExpenseDirectPart(projectFlockKandangId, &endOfEndDay, config)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
directStart, err := s.buildLayingExpenseDirectPart(projectFlockKandangId, &endOfStartDay, config)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
directDelta := hppV2PartTotal(directEnd) - hppV2PartTotal(directStart)
|
||||
if directDelta < 0 {
|
||||
directDelta = 0
|
||||
}
|
||||
|
||||
// laying-farm: delta expense kumulatif × ratio(end).
|
||||
farmRowsEnd, err := s.hppRepo.ListExpenseRealizationRowsByProjectFlockID(context.Background(), contextRow.ProjectFlockID, &endOfEndDay, config.Ekspedisi)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
farmRowsStart, err := s.hppRepo.ListExpenseRealizationRowsByProjectFlockID(context.Background(), contextRow.ProjectFlockID, &endOfStartDay, config.Ekspedisi)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
farmExpenseDelta := hppV2SumExpenseRows(farmRowsEnd) - hppV2SumExpenseRows(farmRowsStart)
|
||||
if farmExpenseDelta < 0 {
|
||||
farmExpenseDelta = 0
|
||||
}
|
||||
farmDelta := 0.0
|
||||
if farmExpenseDelta > 0 {
|
||||
ratio, _, err := s.layingFarmExpenseRatio(projectFlockKandangId, contextRow, &endOfEndDay)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
farmDelta = farmExpenseDelta * ratio
|
||||
}
|
||||
|
||||
return directDelta + farmDelta, nil
|
||||
}
|
||||
|
||||
// GetBopRegularProductionScopeRange / GetBopEkspedisiProductionScopeRange — wrapper range-correct
|
||||
// untuk dua komponen BOP, memakai config yang sama dengan GetBopRegularBreakdown/GetBopEkspedisiBreakdown.
|
||||
func (s *hppV2Service) GetBopRegularProductionScopeRange(projectFlockKandangId uint, startDate, endDate *time.Time) (float64, error) {
|
||||
return s.GetExpenseProductionScopeRange(projectFlockKandangId, startDate, endDate, hppV2ExpenseComponentConfig{
|
||||
Code: hppV2ComponentBopRegular,
|
||||
Title: "BOP Regular",
|
||||
Ekspedisi: false,
|
||||
})
|
||||
}
|
||||
|
||||
func (s *hppV2Service) GetBopEkspedisiProductionScopeRange(projectFlockKandangId uint, startDate, endDate *time.Time) (float64, error) {
|
||||
return s.GetExpenseProductionScopeRange(projectFlockKandangId, startDate, endDate, hppV2ExpenseComponentConfig{
|
||||
Code: hppV2ComponentBopEksp,
|
||||
Title: "BOP Ekspedisi",
|
||||
Ekspedisi: true,
|
||||
})
|
||||
}
|
||||
|
||||
func hppV2PartTotal(part *HppV2ComponentPart) float64 {
|
||||
if part == nil {
|
||||
return 0
|
||||
}
|
||||
return part.Total
|
||||
}
|
||||
|
||||
func hppV2SumExpenseRows(rows []commonRepo.HppV2ExpenseCostRow) float64 {
|
||||
total := 0.0
|
||||
for _, row := range rows {
|
||||
total += row.TotalCost
|
||||
}
|
||||
return total
|
||||
}
|
||||
|
||||
func (s *hppV2Service) getManualPulletCostComponent(
|
||||
|
||||
Reference in New Issue
Block a user