mirror of
https://gitlab.com/mbugroup/lti-api.git
synced 2026-05-20 13:31:56 +00:00
feat: reimplement with plan hppv2 flow and logics
This commit is contained in:
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user