feat: reimplement with plan hppv2 flow and logics

This commit is contained in:
Adnan Zahir
2026-04-19 14:06:42 +07:00
parent 187e497f97
commit 58fbceea24
9 changed files with 1864 additions and 52 deletions
+707 -43
View File
@@ -5,13 +5,40 @@ import (
"time"
commonRepo "gitlab.com/mbugroup/lti-api.git/internal/common/repository"
"gitlab.com/mbugroup/lti-api.git/internal/utils"
)
const (
hppV2ComponentPakan = "PAKAN"
hppV2ComponentOvk = "OVK"
hppV2ComponentBopRegular = "BOP_REGULAR"
hppV2ComponentBopEksp = "BOP_EKSPEDISI"
hppV2PartGrowingNormal = "growing_normal"
hppV2PartGrowingCutover = "growing_cutover"
hppV2PartLayingNormal = "laying_normal"
hppV2PartLayingCutover = "laying_cutover"
hppV2PartGrowingDirect = "growing_direct"
hppV2PartGrowingFarm = "growing_farm"
hppV2PartLayingDirect = "laying_direct"
hppV2PartLayingFarm = "laying_farm"
hppV2ProrationPopulation = "growing_population_share"
hppV2ProrationEggWeight = "laying_egg_weight_share"
hppV2ProrationEggPiece = "laying_egg_piece_share"
hppV2CutoverFlagPakan = "PAKAN-CUTOVER"
hppV2CutoverFlagOvk = "OVK-CUTOVER"
)
type HppV2Service interface {
CalculateHppCost(projectFlockKandangId uint, date *time.Time) (*HppCostResponse, error)
CalculateHppBreakdown(projectFlockKandangId uint, date *time.Time) (*HppV2Breakdown, error)
GetCostPakan(projectFlockKandangId uint, endDate *time.Time) (float64, error)
GetFeedGrowing(projectFlockKandangId uint, endDate *time.Time) (float64, error)
GetFeedLaying(projectFlockKandangId uint, endDate *time.Time) (float64, error)
GetCostOvk(projectFlockKandangId uint, endDate *time.Time) (float64, error)
GetCostBopRegular(projectFlockKandangId uint, endDate *time.Time) (float64, error)
GetCostBopEkspedisi(projectFlockKandangId uint, endDate *time.Time) (float64, error)
GetPakanBreakdown(projectFlockKandangId uint, endDate *time.Time) (*HppV2Component, error)
GetOvkBreakdown(projectFlockKandangId uint, endDate *time.Time) (*HppV2Component, error)
GetBopRegularBreakdown(projectFlockKandangId uint, endDate *time.Time) (*HppV2Component, error)
GetBopEkspedisiBreakdown(projectFlockKandangId uint, endDate *time.Time) (*HppV2Component, error)
GetHppEstimationDanRealisasi(totalProductionCost float64, projectFlockKandangId uint, startDate *time.Time, endDate *time.Time) (*HppCostResponse, error)
}
@@ -19,101 +46,676 @@ type hppV2Service struct {
hppRepo commonRepo.HppV2CostRepository
}
type hppV2StockComponentConfig struct {
Code string
Title string
NormalFlags []string
CutoverFlags []string
}
type hppV2ExpenseComponentConfig struct {
Code string
Title string
Ekspedisi bool
}
func NewHppV2Service(hppRepo commonRepo.HppV2CostRepository) HppV2Service {
return &hppV2Service{hppRepo: hppRepo}
}
func (s *hppV2Service) CalculateHppCost(projectFlockKandangId uint, date *time.Time) (*HppCostResponse, error) {
if date == nil {
now := time.Now()
date = &now
breakdown, err := s.CalculateHppBreakdown(projectFlockKandangId, date)
if err != nil {
return nil, err
}
if breakdown == nil {
return &HppCostResponse{}, nil
}
location, err := time.LoadLocation("Asia/Jakarta")
result := breakdown.Hpp
return &result, nil
}
func (s *hppV2Service) CalculateHppBreakdown(projectFlockKandangId uint, date *time.Time) (*HppV2Breakdown, error) {
if s.hppRepo == nil {
return &HppV2Breakdown{
ProjectFlockKandangID: projectFlockKandangId,
Hpp: HppCostResponse{},
}, nil
}
startOfDay, endOfDay, err := hppV2DayWindow(date)
if err != nil {
return nil, err
}
startOfDay := time.Date(date.Year(), date.Month(), date.Day(), 0, 0, 0, 0, location)
endOfDay := startOfDay.Add(24 * time.Hour)
pakan, err := s.GetCostPakan(projectFlockKandangId, &endOfDay)
contextRow, err := s.hppRepo.GetProjectFlockKandangContext(context.Background(), projectFlockKandangId)
if err != nil {
return nil, err
}
result, err := s.GetHppEstimationDanRealisasi(pakan, projectFlockKandangId, &startOfDay, &endOfDay)
pakanComponent, err := s.GetPakanBreakdown(projectFlockKandangId, &endOfDay)
if err != nil {
return nil, err
}
return result, nil
totalProductionCost := 0.0
components := make([]HppV2Component, 0, 4)
if pakanComponent != nil && (pakanComponent.Total > 0 || len(pakanComponent.Parts) > 0) {
totalProductionCost += pakanComponent.Total
components = append(components, *pakanComponent)
}
ovkComponent, err := s.GetOvkBreakdown(projectFlockKandangId, &endOfDay)
if err != nil {
return nil, err
}
if ovkComponent != nil && (ovkComponent.Total > 0 || len(ovkComponent.Parts) > 0) {
totalProductionCost += ovkComponent.Total
components = append(components, *ovkComponent)
}
bopRegularComponent, err := s.GetBopRegularBreakdown(projectFlockKandangId, &endOfDay)
if err != nil {
return nil, err
}
if bopRegularComponent != nil && (bopRegularComponent.Total > 0 || len(bopRegularComponent.Parts) > 0) {
totalProductionCost += bopRegularComponent.Total
components = append(components, *bopRegularComponent)
}
bopEkspedisiComponent, err := s.GetBopEkspedisiBreakdown(projectFlockKandangId, &endOfDay)
if err != nil {
return nil, err
}
if bopEkspedisiComponent != nil && (bopEkspedisiComponent.Total > 0 || len(bopEkspedisiComponent.Parts) > 0) {
totalProductionCost += bopEkspedisiComponent.Total
components = append(components, *bopEkspedisiComponent)
}
hppCost, err := s.GetHppEstimationDanRealisasi(totalProductionCost, projectFlockKandangId, &startOfDay, &endOfDay)
if err != nil {
return nil, err
}
if hppCost == nil {
hppCost = &HppCostResponse{}
}
return &HppV2Breakdown{
ProjectFlockKandangID: projectFlockKandangId,
ProjectFlockID: contextRow.ProjectFlockID,
ProjectFlockCategory: contextRow.ProjectFlockCategory,
KandangID: contextRow.KandangID,
KandangName: contextRow.KandangName,
LocationID: contextRow.LocationID,
PeriodDate: startOfDay.Format("2006-01-02"),
Window: HppV2DateWindow{
Start: startOfDay.Format(time.RFC3339),
End: endOfDay.Format(time.RFC3339),
},
TotalProductionCost: totalProductionCost,
Components: components,
Hpp: *hppCost,
}, nil
}
func (s *hppV2Service) GetCostPakan(projectFlockKandangId uint, endDate *time.Time) (float64, error) {
feedGrowing, err := s.GetFeedGrowing(projectFlockKandangId, endDate)
component, err := s.GetPakanBreakdown(projectFlockKandangId, endDate)
if err != nil {
return 0, err
}
feedLaying, err := s.GetFeedLaying(projectFlockKandangId, endDate)
if err != nil {
return 0, err
if component == nil {
return 0, nil
}
pakan := feedGrowing + feedLaying
return pakan, nil
return component.Total, nil
}
func (s *hppV2Service) GetFeedGrowing(projectFlockKandangId uint, endDate *time.Time) (float64, error) {
if s.hppRepo == nil {
func (s *hppV2Service) GetPakanBreakdown(projectFlockKandangId uint, endDate *time.Time) (*HppV2Component, error) {
return s.getStockUsageComponent(projectFlockKandangId, endDate, hppV2StockComponentConfig{
Code: hppV2ComponentPakan,
Title: "Pakan",
NormalFlags: []string{string(utils.FlagPakan)},
CutoverFlags: []string{hppV2CutoverFlagPakan},
})
}
func (s *hppV2Service) GetCostOvk(projectFlockKandangId uint, endDate *time.Time) (float64, error) {
component, err := s.GetOvkBreakdown(projectFlockKandangId, endDate)
if err != nil {
return 0, err
}
if component == nil {
return 0, nil
}
return component.Total, nil
}
func (s *hppV2Service) GetOvkBreakdown(projectFlockKandangId uint, endDate *time.Time) (*HppV2Component, error) {
return s.getStockUsageComponent(projectFlockKandangId, endDate, hppV2StockComponentConfig{
Code: hppV2ComponentOvk,
Title: "OVK",
NormalFlags: []string{
string(utils.FlagOVK),
string(utils.FlagObat),
string(utils.FlagVitamin),
string(utils.FlagKimia),
},
CutoverFlags: []string{hppV2CutoverFlagOvk},
})
}
func (s *hppV2Service) GetCostBopRegular(projectFlockKandangId uint, endDate *time.Time) (float64, error) {
component, err := s.GetBopRegularBreakdown(projectFlockKandangId, endDate)
if err != nil {
return 0, err
}
if component == nil {
return 0, nil
}
return component.Total, nil
}
func (s *hppV2Service) GetCostBopEkspedisi(projectFlockKandangId uint, endDate *time.Time) (float64, error) {
component, err := s.GetBopEkspedisiBreakdown(projectFlockKandangId, endDate)
if err != nil {
return 0, err
}
if component == nil {
return 0, nil
}
return component.Total, nil
}
func (s *hppV2Service) GetBopRegularBreakdown(projectFlockKandangId uint, endDate *time.Time) (*HppV2Component, error) {
return s.getExpenseComponent(projectFlockKandangId, endDate, hppV2ExpenseComponentConfig{
Code: hppV2ComponentBopRegular,
Title: "BOP Regular",
Ekspedisi: false,
})
}
func (s *hppV2Service) GetBopEkspedisiBreakdown(projectFlockKandangId uint, endDate *time.Time) (*HppV2Component, error) {
return s.getExpenseComponent(projectFlockKandangId, endDate, hppV2ExpenseComponentConfig{
Code: hppV2ComponentBopEksp,
Title: "BOP Ekspedisi",
Ekspedisi: true,
})
}
func (s *hppV2Service) getStockUsageComponent(projectFlockKandangId uint, endDate *time.Time, config hppV2StockComponentConfig) (*HppV2Component, error) {
if s.hppRepo == nil {
return &HppV2Component{
Code: config.Code,
Title: config.Title,
Parts: []HppV2ComponentPart{},
}, nil
}
contextRow, err := s.hppRepo.GetProjectFlockKandangContext(context.Background(), projectFlockKandangId)
if err != nil {
return nil, err
}
parts := make([]HppV2ComponentPart, 0, 4)
total := 0.0
growingPart, err := s.buildGrowingUsagePart(projectFlockKandangId, contextRow, endDate, config, false)
if err != nil {
return nil, err
}
if growingPart != nil {
parts = append(parts, *growingPart)
total += growingPart.Total
}
growingCutoverPart, err := s.buildGrowingUsagePart(projectFlockKandangId, contextRow, endDate, config, true)
if err != nil {
return nil, err
}
if growingCutoverPart != nil {
parts = append(parts, *growingCutoverPart)
total += growingCutoverPart.Total
}
layingNormalPart, err := s.buildLayingUsagePart(projectFlockKandangId, endDate, config, false)
if err != nil {
return nil, err
}
if layingNormalPart != nil {
parts = append(parts, *layingNormalPart)
total += layingNormalPart.Total
}
layingCutoverPart, err := s.buildLayingUsagePart(projectFlockKandangId, endDate, config, true)
if err != nil {
return nil, err
}
if layingCutoverPart != nil {
parts = append(parts, *layingCutoverPart)
total += layingCutoverPart.Total
}
return &HppV2Component{
Code: config.Code,
Title: config.Title,
Total: total,
Parts: parts,
}, nil
}
func (s *hppV2Service) getExpenseComponent(projectFlockKandangId uint, endDate *time.Time, config hppV2ExpenseComponentConfig) (*HppV2Component, error) {
if s.hppRepo == nil {
return &HppV2Component{
Code: config.Code,
Title: config.Title,
Parts: []HppV2ComponentPart{},
}, nil
}
contextRow, err := s.hppRepo.GetProjectFlockKandangContext(context.Background(), projectFlockKandangId)
if err != nil {
return nil, err
}
parts := make([]HppV2ComponentPart, 0, 4)
total := 0.0
growingDirect, err := s.buildGrowingExpenseDirectPart(projectFlockKandangId, contextRow, endDate, config)
if err != nil {
return nil, err
}
if growingDirect != nil {
parts = append(parts, *growingDirect)
total += growingDirect.Total
}
growingFarm, err := s.buildGrowingExpenseFarmPart(projectFlockKandangId, contextRow, endDate, config)
if err != nil {
return nil, err
}
if growingFarm != nil {
parts = append(parts, *growingFarm)
total += growingFarm.Total
}
layingDirect, err := s.buildLayingExpenseDirectPart(projectFlockKandangId, endDate, config)
if err != nil {
return nil, err
}
if layingDirect != nil {
parts = append(parts, *layingDirect)
total += layingDirect.Total
}
layingFarm, err := s.buildLayingExpenseFarmPart(projectFlockKandangId, contextRow, endDate, config)
if err != nil {
return nil, err
}
if layingFarm != nil {
parts = append(parts, *layingFarm)
total += layingFarm.Total
}
return &HppV2Component{
Code: config.Code,
Title: config.Title,
Total: total,
Parts: parts,
}, nil
}
func (s *hppV2Service) buildGrowingUsagePart(
projectFlockKandangId uint,
contextRow *commonRepo.HppV2ProjectFlockKandangContext,
endDate *time.Time,
config hppV2StockComponentConfig,
cutover bool,
) (*HppV2ComponentPart, error) {
if contextRow == nil {
return nil, nil
}
sourceProjectFlockID, transferTotalQty, err := s.hppRepo.GetTransferSourceSummary(context.Background(), projectFlockKandangId)
if err != nil {
return 0, err
return nil, err
}
if sourceProjectFlockID == 0 || transferTotalQty <= 0 {
return 0, nil
return nil, nil
}
kandangIDsGrowing, err := s.hppRepo.GetProjectFlockKandangIDs(context.Background(), sourceProjectFlockID)
if err != nil {
return 0, err
return nil, err
}
if len(kandangIDsGrowing) == 0 {
return 0, nil
}
feedUsageCostGrowing, err := s.hppRepo.GetFeedUsageCost(context.Background(), kandangIDsGrowing, endDate)
if err != nil {
return 0, err
return nil, nil
}
totalPopulationFlockGrowing, err := s.hppRepo.GetTotalPopulation(context.Background(), kandangIDsGrowing)
if err != nil {
return 0, err
return nil, err
}
if totalPopulationFlockGrowing == 0 {
return 0, nil
return nil, nil
}
result := feedUsageCostGrowing * (transferTotalQty / totalPopulationFlockGrowing)
return result, nil
ratio := transferTotalQty / totalPopulationFlockGrowing
if ratio <= 0 {
return nil, nil
}
partCode := hppV2PartGrowingNormal
partTitle := "Growing"
baseRows := make([]HppV2Reference, 0)
baseTotal := 0.0
if cutover {
partCode = hppV2PartGrowingCutover
partTitle = "Growing Cut-over"
rows, err := s.hppRepo.ListAdjustmentCostRowsByProductFlags(context.Background(), kandangIDsGrowing, config.CutoverFlags, endDate)
if err != nil {
return nil, err
}
for _, row := range rows {
rowTotal := adjustmentRowTotalCost(row)
baseTotal += rowTotal
baseRows = append(baseRows, HppV2Reference{
Type: "adjustment_stock",
ID: row.AdjustmentID,
ProjectFlockKandangID: row.ProjectFlockKandangID,
ProductID: row.ProductID,
ProductName: row.ProductName,
Date: row.CreatedAt.Format("2006-01-02"),
Qty: row.Qty,
UnitPrice: row.Price,
Total: rowTotal,
AppliedTotal: rowTotal * ratio,
})
}
} else {
rows, err := s.hppRepo.ListUsageCostRowsByProductFlags(context.Background(), kandangIDsGrowing, config.NormalFlags, endDate)
if err != nil {
return nil, err
}
for _, row := range rows {
baseTotal += row.TotalCost
refDate := row.LastUsedAt
if refDate.IsZero() {
refDate = row.FirstUsedAt
}
baseRows = append(baseRows, HppV2Reference{
Type: "stock_allocation",
ID: row.StockableID,
StockableType: row.StockableType,
ProductID: row.SourceProductID,
ProductName: row.SourceProductName,
Date: refDate.Format("2006-01-02"),
Qty: row.Qty,
UnitPrice: row.UnitPrice,
Total: row.TotalCost,
AppliedTotal: row.TotalCost * ratio,
})
}
}
if baseTotal == 0 {
return nil, nil
}
return &HppV2ComponentPart{
Code: partCode,
Title: partTitle,
Total: baseTotal * ratio,
Proration: &HppV2Proration{
Basis: hppV2ProrationPopulation,
Numerator: transferTotalQty,
Denominator: totalPopulationFlockGrowing,
Ratio: ratio,
},
References: baseRows,
}, nil
}
func (s *hppV2Service) GetFeedLaying(projectFlockKandangId uint, endDate *time.Time) (float64, error) {
if s.hppRepo == nil {
return 0, nil
func (s *hppV2Service) buildLayingUsagePart(
projectFlockKandangId uint,
endDate *time.Time,
config hppV2StockComponentConfig,
cutover bool,
) (*HppV2ComponentPart, error) {
if cutover {
rows, err := s.hppRepo.ListAdjustmentCostRowsByProductFlags(context.Background(), []uint{projectFlockKandangId}, config.CutoverFlags, endDate)
if err != nil {
return nil, err
}
total := 0.0
references := make([]HppV2Reference, 0, len(rows))
for _, row := range rows {
rowTotal := adjustmentRowTotalCost(row)
total += rowTotal
references = append(references, HppV2Reference{
Type: "adjustment_stock",
ID: row.AdjustmentID,
ProjectFlockKandangID: row.ProjectFlockKandangID,
ProductID: row.ProductID,
ProductName: row.ProductName,
Date: row.CreatedAt.Format("2006-01-02"),
Qty: row.Qty,
UnitPrice: row.Price,
Total: rowTotal,
AppliedTotal: rowTotal,
})
}
if total == 0 {
return nil, nil
}
return &HppV2ComponentPart{
Code: hppV2PartLayingCutover,
Title: "Laying Cut-over",
Total: total,
References: references,
}, nil
}
result, err := s.hppRepo.GetFeedUsageCost(context.Background(), []uint{projectFlockKandangId}, endDate)
rows, err := s.hppRepo.ListUsageCostRowsByProductFlags(context.Background(), []uint{projectFlockKandangId}, config.NormalFlags, endDate)
if err != nil {
return 0, err
return nil, err
}
return result, nil
total := 0.0
references := make([]HppV2Reference, 0, len(rows))
for _, row := range rows {
total += row.TotalCost
refDate := row.LastUsedAt
if refDate.IsZero() {
refDate = row.FirstUsedAt
}
references = append(references, HppV2Reference{
Type: "stock_allocation",
ID: row.StockableID,
StockableType: row.StockableType,
ProductID: row.SourceProductID,
ProductName: row.SourceProductName,
Date: refDate.Format("2006-01-02"),
Qty: row.Qty,
UnitPrice: row.UnitPrice,
Total: row.TotalCost,
AppliedTotal: row.TotalCost,
})
}
if total == 0 {
return nil, nil
}
return &HppV2ComponentPart{
Code: hppV2PartLayingNormal,
Title: "Laying",
Total: total,
References: references,
}, nil
}
func (s *hppV2Service) buildGrowingExpenseDirectPart(
projectFlockKandangId uint,
contextRow *commonRepo.HppV2ProjectFlockKandangContext,
endDate *time.Time,
config hppV2ExpenseComponentConfig,
) (*HppV2ComponentPart, error) {
return s.buildGrowingExpensePart(projectFlockKandangId, contextRow, endDate, config, false)
}
func (s *hppV2Service) buildGrowingExpenseFarmPart(
projectFlockKandangId uint,
contextRow *commonRepo.HppV2ProjectFlockKandangContext,
endDate *time.Time,
config hppV2ExpenseComponentConfig,
) (*HppV2ComponentPart, error) {
return s.buildGrowingExpensePart(projectFlockKandangId, contextRow, endDate, config, true)
}
func (s *hppV2Service) buildGrowingExpensePart(
projectFlockKandangId uint,
contextRow *commonRepo.HppV2ProjectFlockKandangContext,
endDate *time.Time,
config hppV2ExpenseComponentConfig,
farmLevel bool,
) (*HppV2ComponentPart, error) {
if contextRow == nil {
return nil, nil
}
sourceProjectFlockID, transferTotalQty, err := s.hppRepo.GetTransferSourceSummary(context.Background(), projectFlockKandangId)
if err != nil {
return nil, err
}
if sourceProjectFlockID == 0 || transferTotalQty <= 0 {
return nil, nil
}
kandangIDsGrowing, err := s.hppRepo.GetProjectFlockKandangIDs(context.Background(), sourceProjectFlockID)
if err != nil {
return nil, err
}
if len(kandangIDsGrowing) == 0 {
return nil, nil
}
totalPopulationFlockGrowing, err := s.hppRepo.GetTotalPopulation(context.Background(), kandangIDsGrowing)
if err != nil {
return nil, err
}
if totalPopulationFlockGrowing <= 0 {
return nil, nil
}
ratio := transferTotalQty / totalPopulationFlockGrowing
if ratio <= 0 {
return nil, nil
}
var rows []commonRepo.HppV2ExpenseCostRow
if farmLevel {
rows, err = s.hppRepo.ListExpenseRealizationRowsByProjectFlockID(context.Background(), sourceProjectFlockID, endDate, config.Ekspedisi)
} else {
rows, err = s.hppRepo.ListExpenseRealizationRowsByProjectFlockKandangIDs(context.Background(), kandangIDsGrowing, endDate, config.Ekspedisi)
}
if err != nil {
return nil, err
}
return buildExpensePartFromRows(
rows,
map[bool]string{false: hppV2PartGrowingDirect, true: hppV2PartGrowingFarm}[farmLevel],
map[bool]string{false: "Growing Direct", true: "Growing Farm"}[farmLevel],
&HppV2Proration{
Basis: hppV2ProrationPopulation,
Numerator: transferTotalQty,
Denominator: totalPopulationFlockGrowing,
Ratio: ratio,
},
ratio,
), nil
}
func (s *hppV2Service) buildLayingExpenseDirectPart(
projectFlockKandangId uint,
endDate *time.Time,
config hppV2ExpenseComponentConfig,
) (*HppV2ComponentPart, error) {
rows, err := s.hppRepo.ListExpenseRealizationRowsByProjectFlockKandangIDs(context.Background(), []uint{projectFlockKandangId}, endDate, config.Ekspedisi)
if err != nil {
return nil, err
}
return buildExpensePartFromRows(rows, hppV2PartLayingDirect, "Laying Direct", nil, 1), nil
}
func (s *hppV2Service) buildLayingExpenseFarmPart(
projectFlockKandangId uint,
contextRow *commonRepo.HppV2ProjectFlockKandangContext,
endDate *time.Time,
config hppV2ExpenseComponentConfig,
) (*HppV2ComponentPart, error) {
if contextRow == nil {
return nil, nil
}
rows, err := s.hppRepo.ListExpenseRealizationRowsByProjectFlockID(context.Background(), contextRow.ProjectFlockID, endDate, config.Ekspedisi)
if err != nil {
return nil, err
}
if len(rows) == 0 {
return nil, nil
}
farmPFKIDs, err := s.hppRepo.GetProjectFlockKandangIDs(context.Background(), contextRow.ProjectFlockID)
if err != nil {
return nil, err
}
targetPieces, targetWeight, err := s.hppRepo.GetEggProduksiPiecesAndWeightKgByProjectFlockKandangIds(context.Background(), []uint{projectFlockKandangId}, endDate)
if err != nil {
return nil, err
}
farmPieces, farmWeight, err := s.hppRepo.GetEggProduksiPiecesAndWeightKgByProjectFlockKandangIds(context.Background(), farmPFKIDs, endDate)
if err != nil {
return nil, err
}
basis := hppV2ProrationEggWeight
numerator := targetWeight
denominator := farmWeight
if denominator <= 0 {
basis = hppV2ProrationEggPiece
numerator = targetPieces
denominator = farmPieces
}
if denominator <= 0 {
return nil, nil
}
ratio := numerator / denominator
if ratio <= 0 {
return nil, nil
}
return buildExpensePartFromRows(
rows,
hppV2PartLayingFarm,
"Laying Farm",
&HppV2Proration{
Basis: basis,
Numerator: numerator,
Denominator: denominator,
Ratio: ratio,
},
ratio,
), nil
}
func (s *hppV2Service) GetHppEstimationDanRealisasi(totalProductionCost float64, projectFlockKandangId uint, startDate *time.Time, endDate *time.Time) (*HppCostResponse, error) {
@@ -155,9 +757,71 @@ func (s *hppV2Service) GetHppEstimationDanRealisasi(totalProductionCost float64,
real.HargaButir = roundToTwoDecimals(totalProductionCost / realPieces)
}
result := &HppCostResponse{
return &HppCostResponse{
Estimation: estimation,
Real: real,
}
return result, nil
}, nil
}
func hppV2DayWindow(date *time.Time) (time.Time, time.Time, error) {
if date == nil {
now := time.Now()
date = &now
}
location, err := time.LoadLocation("Asia/Jakarta")
if err != nil {
return time.Time{}, time.Time{}, err
}
startOfDay := time.Date(date.Year(), date.Month(), date.Day(), 0, 0, 0, 0, location)
endOfDay := startOfDay.Add(24 * time.Hour)
return startOfDay, endOfDay, nil
}
func adjustmentRowTotalCost(row commonRepo.HppV2AdjustmentCostRow) float64 {
if row.GrandTotal > 0 {
return row.GrandTotal
}
return row.Qty * row.Price
}
func buildExpensePartFromRows(
rows []commonRepo.HppV2ExpenseCostRow,
code string,
title string,
proration *HppV2Proration,
ratio float64,
) *HppV2ComponentPart {
if len(rows) == 0 {
return nil
}
total := 0.0
references := make([]HppV2Reference, 0, len(rows))
for _, row := range rows {
total += row.TotalCost * ratio
references = append(references, HppV2Reference{
Type: "expense_realization",
ID: row.ExpenseRealizationID,
ProductID: row.NonstockID,
ProductName: row.NonstockName,
Date: row.RealizationDate.Format("2006-01-02"),
Qty: row.Qty,
UnitPrice: row.Price,
Total: row.TotalCost,
AppliedTotal: row.TotalCost * ratio,
})
}
if total == 0 {
return nil
}
return &HppV2ComponentPart{
Code: code,
Title: title,
Total: total,
Proration: proration,
References: references,
}
}