mirror of
https://gitlab.com/mbugroup/lti-api.git
synced 2026-05-24 23:35:43 +00:00
feat: doc direct purchase cost
This commit is contained in:
@@ -9,23 +9,27 @@ import (
|
||||
)
|
||||
|
||||
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"
|
||||
hppV2ComponentPakan = "PAKAN"
|
||||
hppV2ComponentOvk = "OVK"
|
||||
hppV2ComponentDocChickin = "DOC_CHICKIN"
|
||||
hppV2ComponentDirectPulletPurchase = "DIRECT_PULLET_PURCHASE"
|
||||
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"
|
||||
hppV2ScopePulletCost = "pullet_cost"
|
||||
hppV2ScopeProductionCost = "production_cost"
|
||||
hppV2CutoverFlagPakan = "PAKAN-CUTOVER"
|
||||
hppV2CutoverFlagOvk = "OVK-CUTOVER"
|
||||
)
|
||||
|
||||
type HppV2Service interface {
|
||||
@@ -33,10 +37,14 @@ type HppV2Service interface {
|
||||
CalculateHppBreakdown(projectFlockKandangId uint, date *time.Time) (*HppV2Breakdown, error)
|
||||
GetCostPakan(projectFlockKandangId uint, endDate *time.Time) (float64, error)
|
||||
GetCostOvk(projectFlockKandangId uint, endDate *time.Time) (float64, error)
|
||||
GetCostDocChickin(projectFlockKandangId uint, endDate *time.Time) (float64, error)
|
||||
GetCostDirectPulletPurchase(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)
|
||||
GetDocChickinBreakdown(projectFlockKandangId uint, endDate *time.Time) (*HppV2Component, error)
|
||||
GetDirectPulletPurchaseBreakdown(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)
|
||||
@@ -99,39 +107,52 @@ func (s *hppV2Service) CalculateHppBreakdown(projectFlockKandangId uint, date *t
|
||||
return nil, err
|
||||
}
|
||||
|
||||
totalPulletCost := 0.0
|
||||
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)
|
||||
components := make([]HppV2Component, 0, 6)
|
||||
appendComponent := func(component *HppV2Component) {
|
||||
if component == nil || (component.Total == 0 && len(component.Parts) == 0) {
|
||||
return
|
||||
}
|
||||
components = append(components, *component)
|
||||
if componentHasScope(component, hppV2ScopePulletCost) {
|
||||
totalPulletCost += component.Total
|
||||
}
|
||||
if componentHasScope(component, hppV2ScopeProductionCost) {
|
||||
totalProductionCost += component.Total
|
||||
}
|
||||
}
|
||||
appendComponent(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)
|
||||
appendComponent(ovkComponent)
|
||||
|
||||
docComponent, err := s.GetDocChickinBreakdown(projectFlockKandangId, &endOfDay)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
appendComponent(docComponent)
|
||||
|
||||
directPulletComponent, err := s.GetDirectPulletPurchaseBreakdown(projectFlockKandangId, &endOfDay)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
appendComponent(directPulletComponent)
|
||||
|
||||
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)
|
||||
}
|
||||
appendComponent(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)
|
||||
}
|
||||
appendComponent(bopEkspedisiComponent)
|
||||
|
||||
hppCost, err := s.GetHppEstimationDanRealisasi(totalProductionCost, projectFlockKandangId, &startOfDay, &endOfDay)
|
||||
if err != nil {
|
||||
@@ -153,6 +174,7 @@ func (s *hppV2Service) CalculateHppBreakdown(projectFlockKandangId uint, date *t
|
||||
Start: startOfDay.Format(time.RFC3339),
|
||||
End: endOfDay.Format(time.RFC3339),
|
||||
},
|
||||
TotalPulletCost: totalPulletCost,
|
||||
TotalProductionCost: totalProductionCost,
|
||||
Components: components,
|
||||
Hpp: *hppCost,
|
||||
@@ -206,6 +228,88 @@ func (s *hppV2Service) GetOvkBreakdown(projectFlockKandangId uint, endDate *time
|
||||
})
|
||||
}
|
||||
|
||||
func (s *hppV2Service) GetCostDocChickin(projectFlockKandangId uint, endDate *time.Time) (float64, error) {
|
||||
component, err := s.GetDocChickinBreakdown(projectFlockKandangId, endDate)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if component == nil {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
return component.Total, nil
|
||||
}
|
||||
|
||||
func (s *hppV2Service) GetCostDirectPulletPurchase(projectFlockKandangId uint, endDate *time.Time) (float64, error) {
|
||||
component, err := s.GetDirectPulletPurchaseBreakdown(projectFlockKandangId, endDate)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if component == nil {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
return component.Total, nil
|
||||
}
|
||||
|
||||
func (s *hppV2Service) GetDocChickinBreakdown(projectFlockKandangId uint, endDate *time.Time) (*HppV2Component, error) {
|
||||
if s.hppRepo == nil {
|
||||
return &HppV2Component{
|
||||
Code: hppV2ComponentDocChickin,
|
||||
Title: "DOC Chick-in",
|
||||
Scopes: []string{hppV2ScopePulletCost},
|
||||
Parts: []HppV2ComponentPart{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
contextRow, err := s.hppRepo.GetProjectFlockKandangContext(context.Background(), projectFlockKandangId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
part, err := s.buildGrowingChickinPart(projectFlockKandangId, contextRow, endDate, []string{string(utils.FlagDOC)}, false, hppV2PartGrowingDirect, "Growing DOC")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
parts := make([]HppV2ComponentPart, 0, 1)
|
||||
total := 0.0
|
||||
if part != nil {
|
||||
parts = append(parts, *part)
|
||||
total += part.Total
|
||||
}
|
||||
|
||||
return &HppV2Component{
|
||||
Code: hppV2ComponentDocChickin,
|
||||
Title: "DOC Chick-in",
|
||||
Scopes: []string{hppV2ScopePulletCost},
|
||||
Total: total,
|
||||
Parts: parts,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *hppV2Service) GetDirectPulletPurchaseBreakdown(projectFlockKandangId uint, endDate *time.Time) (*HppV2Component, error) {
|
||||
part, err := s.buildLayingChickinPart(projectFlockKandangId, endDate, []string{string(utils.FlagPullet), string(utils.FlagLayer)}, true, hppV2PartLayingDirect, "Laying Direct Pullet")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
parts := make([]HppV2ComponentPart, 0, 1)
|
||||
total := 0.0
|
||||
if part != nil {
|
||||
parts = append(parts, *part)
|
||||
total += part.Total
|
||||
}
|
||||
|
||||
return &HppV2Component{
|
||||
Code: hppV2ComponentDirectPulletPurchase,
|
||||
Title: "Direct Pullet Purchase",
|
||||
Scopes: []string{hppV2ScopeProductionCost},
|
||||
Total: total,
|
||||
Parts: parts,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *hppV2Service) GetCostBopRegular(projectFlockKandangId uint, endDate *time.Time) (float64, error) {
|
||||
component, err := s.GetBopRegularBreakdown(projectFlockKandangId, endDate)
|
||||
if err != nil {
|
||||
@@ -249,9 +353,10 @@ func (s *hppV2Service) GetBopEkspedisiBreakdown(projectFlockKandangId uint, endD
|
||||
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{},
|
||||
Code: config.Code,
|
||||
Title: config.Title,
|
||||
Scopes: []string{hppV2ScopePulletCost, hppV2ScopeProductionCost},
|
||||
Parts: []HppV2ComponentPart{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -300,19 +405,21 @@ func (s *hppV2Service) getStockUsageComponent(projectFlockKandangId uint, endDat
|
||||
}
|
||||
|
||||
return &HppV2Component{
|
||||
Code: config.Code,
|
||||
Title: config.Title,
|
||||
Total: total,
|
||||
Parts: parts,
|
||||
Code: config.Code,
|
||||
Title: config.Title,
|
||||
Scopes: []string{hppV2ScopePulletCost, hppV2ScopeProductionCost},
|
||||
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{},
|
||||
Code: config.Code,
|
||||
Title: config.Title,
|
||||
Scopes: []string{hppV2ScopePulletCost, hppV2ScopeProductionCost},
|
||||
Parts: []HppV2ComponentPart{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -361,13 +468,91 @@ func (s *hppV2Service) getExpenseComponent(projectFlockKandangId uint, endDate *
|
||||
}
|
||||
|
||||
return &HppV2Component{
|
||||
Code: config.Code,
|
||||
Title: config.Title,
|
||||
Total: total,
|
||||
Parts: parts,
|
||||
Code: config.Code,
|
||||
Title: config.Title,
|
||||
Scopes: []string{hppV2ScopePulletCost, hppV2ScopeProductionCost},
|
||||
Total: total,
|
||||
Parts: parts,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *hppV2Service) buildGrowingChickinPart(
|
||||
projectFlockKandangId uint,
|
||||
contextRow *commonRepo.HppV2ProjectFlockKandangContext,
|
||||
endDate *time.Time,
|
||||
flagNames []string,
|
||||
excludeTransferToLaying bool,
|
||||
partCode string,
|
||||
partTitle string,
|
||||
) (*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
|
||||
}
|
||||
|
||||
rows, err := s.hppRepo.ListChickinCostRowsByProductFlags(context.Background(), kandangIDsGrowing, flagNames, endDate, excludeTransferToLaying)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return buildChickinPartFromRows(
|
||||
rows,
|
||||
partCode,
|
||||
partTitle,
|
||||
&HppV2Proration{
|
||||
Basis: hppV2ProrationPopulation,
|
||||
Numerator: transferTotalQty,
|
||||
Denominator: totalPopulationFlockGrowing,
|
||||
Ratio: ratio,
|
||||
},
|
||||
ratio,
|
||||
), nil
|
||||
}
|
||||
|
||||
func (s *hppV2Service) buildLayingChickinPart(
|
||||
projectFlockKandangId uint,
|
||||
endDate *time.Time,
|
||||
flagNames []string,
|
||||
excludeTransferToLaying bool,
|
||||
partCode string,
|
||||
partTitle string,
|
||||
) (*HppV2ComponentPart, error) {
|
||||
rows, err := s.hppRepo.ListChickinCostRowsByProductFlags(context.Background(), []uint{projectFlockKandangId}, flagNames, endDate, excludeTransferToLaying)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return buildChickinPartFromRows(rows, partCode, partTitle, nil, 1), nil
|
||||
}
|
||||
|
||||
func (s *hppV2Service) buildGrowingUsagePart(
|
||||
projectFlockKandangId uint,
|
||||
contextRow *commonRepo.HppV2ProjectFlockKandangContext,
|
||||
@@ -825,3 +1010,58 @@ func buildExpensePartFromRows(
|
||||
References: references,
|
||||
}
|
||||
}
|
||||
|
||||
func buildChickinPartFromRows(
|
||||
rows []commonRepo.HppV2ChickinCostRow,
|
||||
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
|
||||
projectFlockKandangID := row.ProjectFlockKandangID
|
||||
references = append(references, HppV2Reference{
|
||||
Type: "project_chickin",
|
||||
ID: row.ProjectChickinID,
|
||||
StockableType: row.StockableType,
|
||||
ProjectFlockKandangID: &projectFlockKandangID,
|
||||
ProductID: row.SourceProductID,
|
||||
ProductName: row.SourceProductName,
|
||||
Date: row.ChickInDate.Format("2006-01-02"),
|
||||
Qty: row.Qty,
|
||||
UnitPrice: row.UnitPrice,
|
||||
Total: row.TotalCost,
|
||||
AppliedTotal: row.TotalCost * ratio,
|
||||
})
|
||||
}
|
||||
if total == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &HppV2ComponentPart{
|
||||
Code: code,
|
||||
Title: title,
|
||||
Total: total,
|
||||
Proration: proration,
|
||||
References: references,
|
||||
}
|
||||
}
|
||||
|
||||
func componentHasScope(component *HppV2Component, scope string) bool {
|
||||
if component == nil || scope == "" {
|
||||
return false
|
||||
}
|
||||
for _, candidate := range component.Scopes {
|
||||
if candidate == scope {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user