package service import ( "context" "log" "math" "time" commonRepo "gitlab.com/mbugroup/lti-api.git/internal/common/repository" ) type HppService interface { CalculateHppCost(projectFlockKandangId uint, date *time.Time) (*HppCostResponse, error) GetTotalDepresiasiFlockGrowing(sourceProjectFlockID uint, date *time.Time) (float64, error) GetTotalProductionCost(projectFlockKandangId uint, endDate *time.Time, depresiasiTransfer float64) (float64, error) GetBudgetKandangLaying(projectFlockKandangId uint, endDate *time.Time) (float64, error) GetDepresiasiTransfer(projectFlockKandangId uint, date *time.Time) (float64, error) GetHppEstimationDanRealisasi(totalProductionCost float64, projectFlockKandangId uint, startDate *time.Time, endDate *time.Time) (*HppCostResponse, error) } type HppCostResponse struct { Estimation HppCostDetail `json:"estimation"` Real HppCostDetail `json:"real"` } type HppCostDetail struct { HargaKg float64 `json:"harga_kg"` HargaButir float64 `json:"harga_butir"` Total float64 `json:"total"` Kg float64 `json:"kg"` Butir float64 `json:"butir"` } type hppService struct { hppRepo commonRepo.HppCostRepository } func NewHppService(hppRepo commonRepo.HppCostRepository) HppService { return &hppService{hppRepo: hppRepo} } func (s *hppService) CalculateHppCost(projectFlockKandangId uint, date *time.Time) (*HppCostResponse, error) { logHpp("CalculateHppCost", "start project_flock_kandang_id=%d input_date=%s", projectFlockKandangId, formatTimePtr(date)) if date == nil { now := time.Now() date = &now } logHpp("CalculateHppCost", "normalized_date=%s", formatTimePtr(date)) location, err := time.LoadLocation("Asia/Jakarta") if err != nil { logHpp("CalculateHppCost", "load_location_error=%v", err) return nil, err } logHpp("CalculateHppCost", "location=%s", location.String()) startOfDay := time.Date(date.Year(), date.Month(), date.Day(), 0, 0, 0, 0, location) endOfDay := startOfDay.Add(24 * time.Hour) logHpp("CalculateHppCost", "start_of_day=%s end_of_day=%s", startOfDay.Format(time.RFC3339), endOfDay.Format(time.RFC3339)) depresiasiTransfer, err := s.GetDepresiasiTransfer(projectFlockKandangId, &endOfDay) if err != nil { logHpp("CalculateHppCost", "get_depresiasi_transfer_error=%v", err) return nil, err } logHpp("CalculateHppCost", "depresiasi_transfer=%f", depresiasiTransfer) totalProductionCost, err := s.GetTotalProductionCost(projectFlockKandangId, &endOfDay, depresiasiTransfer) if err != nil { logHpp("CalculateHppCost", "get_total_production_cost_error=%v", err) return nil, err } logHpp("CalculateHppCost", "total_production_cost=%f", totalProductionCost) result, err := s.GetHppEstimationDanRealisasi(totalProductionCost, projectFlockKandangId, &startOfDay, &endOfDay) if err != nil { logHpp("CalculateHppCost", "get_hpp_estimation_dan_realisasi_error=%v", err) return nil, err } logHpp("CalculateHppCost", "done estimation=%+v real=%+v", result.Estimation, result.Real) return result, nil } func (s *hppService) GetTotalDepresiasiFlockGrowing(sourceProjectFlockID uint, date *time.Time) (float64, error) { logHpp("GetTotalDepresiasiFlockGrowing", "start source_project_flock_id=%d input_date=%s", sourceProjectFlockID, formatTimePtr(date)) if date == nil { now := time.Now() date = &now } logHpp("GetTotalDepresiasiFlockGrowing", "normalized_date=%s", formatTimePtr(date)) if s.hppRepo == nil { logHpp("GetTotalDepresiasiFlockGrowing", "repo_nil return=0") return 0, nil } kandangIDs, err := s.hppRepo.GetProjectFlockKandangIDs(context.Background(), sourceProjectFlockID) if err != nil { logHpp("GetTotalDepresiasiFlockGrowing", "get_project_flock_kandang_ids_error=%v", err) return 0, err } logHpp("GetTotalDepresiasiFlockGrowing", "kandang_ids=%v", kandangIDs) docCost, err := s.hppRepo.GetDocCost(context.Background(), kandangIDs) if err != nil { logHpp("GetTotalDepresiasiFlockGrowing", "get_doc_cost_error=%v", err) return 0, err } logHpp("GetTotalDepresiasiFlockGrowing", "doc_cost=%f", docCost) budgetCost, err := s.hppRepo.GetBudgetCostByProjectFlockId(context.Background(), sourceProjectFlockID) if err != nil { logHpp("GetTotalDepresiasiFlockGrowing", "get_budget_cost_error=%v", err) return 0, err } logHpp("GetTotalDepresiasiFlockGrowing", "budget_cost=%f", budgetCost) expedisionCost, err := s.hppRepo.GetExpedisionCost(context.Background(), kandangIDs) if err != nil { logHpp("GetTotalDepresiasiFlockGrowing", "get_expedision_cost_error=%v", err) return 0, err } logHpp("GetTotalDepresiasiFlockGrowing", "expedision_cost=%f", expedisionCost) feedCost, err := s.hppRepo.GetFeedUsageCost(context.Background(), kandangIDs, date) if err != nil { logHpp("GetTotalDepresiasiFlockGrowing", "get_feed_usage_cost_error=%v", err) return 0, err } logHpp("GetTotalDepresiasiFlockGrowing", "feed_cost=%f", feedCost) ovkCost, err := s.hppRepo.GetOvkUsageCost(context.Background(), kandangIDs, date) if err != nil { logHpp("GetTotalDepresiasiFlockGrowing", "get_ovk_usage_cost_error=%v", err) return 0, err } logHpp("GetTotalDepresiasiFlockGrowing", "ovk_cost=%f", ovkCost) total := docCost + budgetCost + expedisionCost + feedCost + ovkCost logHpp("GetTotalDepresiasiFlockGrowing", "done total=%f", total) return total, nil } func (s *hppService) GetTotalProductionCost(projectFlockKandangId uint, endDate *time.Time, depresiasiTransfer float64) (float64, error) { logHpp("GetTotalProductionCost", "start project_flock_kandang_id=%d end_date=%s depresiasi_transfer=%f", projectFlockKandangId, formatTimePtr(endDate), depresiasiTransfer) // if date == nil { // now := time.Now() // date = &now // } costPullet, err := s.hppRepo.GetPulletCost(context.Background(), projectFlockKandangId) if err != nil { logHpp("GetTotalProductionCost", "get_pullet_cost_error=%v", err) return 0, err } logHpp("GetTotalProductionCost", "cost_pullet=%f", costPullet) costFeed, err := s.hppRepo.GetFeedUsageCost(context.Background(), []uint{projectFlockKandangId}, endDate) if err != nil { logHpp("GetTotalProductionCost", "get_feed_usage_cost_error=%v", err) return 0, err } logHpp("GetTotalProductionCost", "cost_feed=%f", costFeed) costOvk, err := s.hppRepo.GetOvkUsageCost(context.Background(), []uint{projectFlockKandangId}, endDate) if err != nil { logHpp("GetTotalProductionCost", "get_ovk_usage_cost_error=%v", err) return 0, err } logHpp("GetTotalProductionCost", "cost_ovk=%f", costOvk) costExpedision, err := s.hppRepo.GetExpedisionCost(context.Background(), []uint{projectFlockKandangId}) if err != nil { logHpp("GetTotalProductionCost", "get_expedision_cost_error=%v", err) return 0, err } logHpp("GetTotalProductionCost", "cost_expedision=%f", costExpedision) costBudget, err := s.GetBudgetKandangLaying(projectFlockKandangId, endDate) if err != nil { logHpp("GetTotalProductionCost", "get_budget_kandang_laying_error=%v", err) return 0, err } logHpp("GetTotalProductionCost", "cost_budget=%f", costBudget) // fmt.Println(costBudget, costExpedision, costOvk, costFeed, costPullet, depresiasiTransfer) // depresiasiTransfer = 0 total := depresiasiTransfer + costPullet + costFeed + costOvk + costExpedision + costBudget logHpp("GetTotalProductionCost", "done total=%f", total) return total, nil } func (s *hppService) GetBudgetKandangLaying(projectFlockKandangId uint, endDate *time.Time) (float64, error) { logHpp("GetBudgetKandangLaying", "start project_flock_kandang_id=%d end_date=%s", projectFlockKandangId, formatTimePtr(endDate)) // if date == nil { // now := time.Now() // date = &now // } if s.hppRepo == nil { logHpp("GetBudgetKandangLaying", "repo_nil return=0") return 0, nil } projectFlockId, err := s.hppRepo.GetProjectFlockIDByProjectFlockKandangID(context.Background(), projectFlockKandangId) if err != nil { logHpp("GetBudgetKandangLaying", "get_project_flock_id_error=%v", err) return 0, err } logHpp("GetBudgetKandangLaying", "project_flock_id=%d", projectFlockId) projectFlockKandangIds, err := s.hppRepo.GetProjectFlockKandangIDs(context.Background(), projectFlockId) if err != nil { logHpp("GetBudgetKandangLaying", "get_project_flock_kandang_ids_error=%v", err) return 0, err } logHpp("GetBudgetKandangLaying", "project_flock_kandang_ids=%v", projectFlockKandangIds) eggProduksiPiecesFlock, _, err := s.hppRepo.GetEggProduksiPiecesAndWeightKgByProjectFlockKandangIds(context.Background(), projectFlockKandangIds, endDate) if err != nil { logHpp("GetBudgetKandangLaying", "get_egg_produksi_pieces_flock_error=%v", err) return 0, err } logHpp("GetBudgetKandangLaying", "egg_produksi_pieces_flock=%f", eggProduksiPiecesFlock) eggProduksiPiecesKandang, _, err := s.hppRepo.GetEggProduksiPiecesAndWeightKgByProjectFlockKandangIds(context.Background(), []uint{projectFlockKandangId}, endDate) if err != nil { logHpp("GetBudgetKandangLaying", "get_egg_produksi_pieces_kandang_error=%v", err) return 0, err } logHpp("GetBudgetKandangLaying", "egg_produksi_pieces_kandang=%f", eggProduksiPiecesKandang) totalBudgetCost, err := s.hppRepo.GetBudgetCostByProjectFlockId(context.Background(), projectFlockId) if err != nil { logHpp("GetBudgetKandangLaying", "get_budget_cost_error=%v", err) return 0, err } logHpp("GetBudgetKandangLaying", "total_budget_cost=%f", totalBudgetCost) if eggProduksiPiecesFlock == 0 { logHpp("GetBudgetKandangLaying", "egg_produksi_pieces_flock_zero return=0") return 0, nil } result := (totalBudgetCost * eggProduksiPiecesKandang) / eggProduksiPiecesFlock logHpp("GetBudgetKandangLaying", "done result=%f", result) return result, nil } func (s *hppService) GetDepresiasiTransfer(projectFlockKandangId uint, endDate *time.Time) (float64, error) { logHpp("GetDepresiasiTransfer", "start project_flock_kandang_id=%d end_date=%s", projectFlockKandangId, formatTimePtr(endDate)) if endDate == nil { now := time.Now() endDate = &now } logHpp("GetDepresiasiTransfer", "normalized_end_date=%s", formatTimePtr(endDate)) if s.hppRepo == nil { logHpp("GetDepresiasiTransfer", "repo_nil return=0") return 0, nil } sourceProjectFlockID, transferTotalQty, err := s.hppRepo.GetTransferSourceSummary(context.Background(), projectFlockKandangId) if err != nil { logHpp("GetDepresiasiTransfer", "get_transfer_source_summary_error=%v", err) return 0, err } logHpp("GetDepresiasiTransfer", "source_project_flock_id=%d transfer_total_qty=%f", sourceProjectFlockID, transferTotalQty) if sourceProjectFlockID == 0 || transferTotalQty <= 0 { logHpp("GetDepresiasiTransfer", "use_manual_fallback=true") result, fallbackErr := s.getManualDepresiasiTransferFallback(projectFlockKandangId) if fallbackErr != nil { logHpp("GetDepresiasiTransfer", "manual_fallback_error=%v", fallbackErr) return 0, fallbackErr } logHpp("GetDepresiasiTransfer", "done_fallback result=%f", result) return result, nil } kandangIDsGrowing, err := s.hppRepo.GetProjectFlockKandangIDs(context.Background(), sourceProjectFlockID) if err != nil { logHpp("GetDepresiasiTransfer", "get_project_flock_kandang_ids_error=%v", err) return 0, err } logHpp("GetDepresiasiTransfer", "kandang_ids_growing=%v", kandangIDsGrowing) totalPopulationFlockGrowing, err := s.hppRepo.GetTotalPopulation(context.Background(), kandangIDsGrowing) if err != nil { logHpp("GetDepresiasiTransfer", "get_total_population_error=%v", err) return 0, err } logHpp("GetDepresiasiTransfer", "total_population_flock_growing=%f", totalPopulationFlockGrowing) if totalPopulationFlockGrowing == 0 { logHpp("GetDepresiasiTransfer", "total_population_flock_growing_zero return=0") return 0, nil } totalDepresiasiFlockGrowing, err := s.GetTotalDepresiasiFlockGrowing(sourceProjectFlockID, endDate) if err != nil { logHpp("GetDepresiasiTransfer", "get_total_depresiasi_flock_growing_error=%v", err) return 0, err } logHpp("GetDepresiasiTransfer", "total_depresiasi_flock_growing=%f", totalDepresiasiFlockGrowing) result := (totalDepresiasiFlockGrowing * transferTotalQty) / totalPopulationFlockGrowing logHpp("GetDepresiasiTransfer", "done result=%f", result) return result, nil } func (s *hppService) getManualDepresiasiTransferFallback(projectFlockKandangId uint) (float64, error) { logHpp("getManualDepresiasiTransferFallback", "start project_flock_kandang_id=%d", projectFlockKandangId) projectFlockID, err := s.hppRepo.GetProjectFlockIDByProjectFlockKandangID(context.Background(), projectFlockKandangId) if err != nil { logHpp("getManualDepresiasiTransferFallback", "get_project_flock_id_error=%v", err) return 0, err } logHpp("getManualDepresiasiTransferFallback", "project_flock_id=%d", projectFlockID) if projectFlockID == 0 { logHpp("getManualDepresiasiTransferFallback", "project_flock_id_zero return=0") return 0, nil } manualCost, err := s.hppRepo.GetManualDepreciationCostByProjectFlockID(context.Background(), projectFlockID) if err != nil { logHpp("getManualDepresiasiTransferFallback", "get_manual_depreciation_cost_error=%v", err) return 0, err } logHpp("getManualDepresiasiTransferFallback", "manual_cost=%f", manualCost) if manualCost <= 0 { logHpp("getManualDepresiasiTransferFallback", "manual_cost_non_positive return=0") return 0, nil } kandangIDs, err := s.hppRepo.GetProjectFlockKandangIDs(context.Background(), projectFlockID) if err != nil { logHpp("getManualDepresiasiTransferFallback", "get_project_flock_kandang_ids_error=%v", err) return 0, err } logHpp("getManualDepresiasiTransferFallback", "kandang_ids=%v", kandangIDs) if len(kandangIDs) == 0 { logHpp("getManualDepresiasiTransferFallback", "kandang_ids_empty return=0") return 0, nil } totalUsageQty, err := s.hppRepo.GetTotalPopulation(context.Background(), kandangIDs) if err != nil { logHpp("getManualDepresiasiTransferFallback", "get_total_usage_qty_error=%v", err) return 0, err } logHpp("getManualDepresiasiTransferFallback", "total_usage_qty=%f", totalUsageQty) if totalUsageQty <= 0 { logHpp("getManualDepresiasiTransferFallback", "total_usage_qty_non_positive return=0") return 0, nil } kandangUsageQty, err := s.hppRepo.GetTotalPopulation(context.Background(), []uint{projectFlockKandangId}) if err != nil { logHpp("getManualDepresiasiTransferFallback", "get_kandang_usage_qty_error=%v", err) return 0, err } logHpp("getManualDepresiasiTransferFallback", "kandang_usage_qty=%f", kandangUsageQty) if kandangUsageQty <= 0 { logHpp("getManualDepresiasiTransferFallback", "kandang_usage_qty_non_positive return=0") return 0, nil } result := manualCost * (kandangUsageQty / totalUsageQty) logHpp("getManualDepresiasiTransferFallback", "done result=%f", result) return result, nil } func (s *hppService) GetHppEstimationDanRealisasi(totalProductionCost float64, projectFlockKandangId uint, startDate *time.Time, endDate *time.Time) (*HppCostResponse, error) { logHpp("GetHppEstimationDanRealisasi", "start total_production_cost=%f project_flock_kandang_id=%d start_date=%s end_date=%s", totalProductionCost, projectFlockKandangId, formatTimePtr(startDate), formatTimePtr(endDate)) if s.hppRepo == nil { logHpp("GetHppEstimationDanRealisasi", "repo_nil return_empty_response") return &HppCostResponse{}, nil } estimPieces, estimWeightKg, err := s.hppRepo.GetEggProduksiPiecesAndWeightKgByProjectFlockKandangIds(context.Background(), []uint{projectFlockKandangId}, endDate) if err != nil { logHpp("GetHppEstimationDanRealisasi", "get_egg_produksi_error=%v", err) return nil, err } logHpp("GetHppEstimationDanRealisasi", "estim_pieces=%f estim_weight_kg=%f", estimPieces, estimWeightKg) realPieces, realWeightKg, err := s.hppRepo.GetEggTerjualPiecesAndWeightKgByProjectFlockKandangIds(context.Background(), []uint{projectFlockKandangId}, startDate, endDate) if err != nil { logHpp("GetHppEstimationDanRealisasi", "get_egg_terjual_error=%v", err) return nil, err } logHpp("GetHppEstimationDanRealisasi", "real_pieces=%f real_weight_kg=%f", realPieces, realWeightKg) estimation := HppCostDetail{ Total: totalProductionCost, Kg: estimWeightKg, Butir: estimPieces, } if estimWeightKg > 0 { estimation.HargaKg = roundToTwoDecimals(totalProductionCost / estimWeightKg) } if estimPieces > 0 { estimation.HargaButir = roundToTwoDecimals(totalProductionCost / estimPieces) } logHpp("GetHppEstimationDanRealisasi", "estimation=%+v", estimation) real := HppCostDetail{ Total: totalProductionCost, Kg: realWeightKg, Butir: realPieces, } if realWeightKg > 0 { real.HargaKg = roundToTwoDecimals(totalProductionCost / realWeightKg) } if realPieces > 0 { real.HargaButir = roundToTwoDecimals(totalProductionCost / realPieces) } logHpp("GetHppEstimationDanRealisasi", "real=%+v", real) result := &HppCostResponse{ Estimation: estimation, Real: real, } logHpp("GetHppEstimationDanRealisasi", "done response=%+v", *result) return result, nil } func roundToTwoDecimals(value float64) float64 { result := math.Round(value*100) / 100 logHpp("roundToTwoDecimals", "input=%f output=%f", value, result) return result } func formatTimePtr(value *time.Time) string { if value == nil { return "" } return value.Format(time.RFC3339) } func logHpp(method, format string, args ...any) { log.Printf("[HPP][%s] "+format, append([]any{method}, args...)...) }