mirror of
https://gitlab.com/mbugroup/lti-api.git
synced 2026-05-20 13:31:56 +00:00
feat[BE]: update HPP calculations to use totalWeightProduced and totalActualPopulation
This commit is contained in:
@@ -96,7 +96,7 @@ func getFlagLabel(flagType utils.FlagType) string {
|
||||
return "Pembelian " + string(flagType)
|
||||
}
|
||||
|
||||
func buildHppItemsByPurchaseFlags(purchaseItems []entities.PurchaseItem, totalWeightSold, totalPopulation float64) []HppItem {
|
||||
func buildHppItemsByPurchaseFlags(purchaseItems []entities.PurchaseItem, totalWeightProduced, totalPopulation float64) []HppItem {
|
||||
flags := []utils.FlagType{
|
||||
utils.FlagDOC, utils.FlagPullet, utils.FlagLayer, utils.FlagPakan,
|
||||
utils.FlagPreStarter, utils.FlagStarter, utils.FlagFinisher,
|
||||
@@ -125,7 +125,7 @@ func buildHppItemsByPurchaseFlags(purchaseItems []entities.PurchaseItem, totalWe
|
||||
|
||||
if isValid && !seenFlags[flagType] {
|
||||
amount := sumPurchasesByFlag(purchaseItems, flagType)
|
||||
rpPerBird, rpPerKg := calculatePerUnitMetrics(amount, totalPopulation, totalWeightSold)
|
||||
rpPerBird, rpPerKg := calculatePerUnitMetrics(amount, totalPopulation, totalWeightProduced)
|
||||
|
||||
items = append(items, HppItem{
|
||||
Type: getFlagLabel(flagType),
|
||||
@@ -144,14 +144,14 @@ func buildHppItemsByPurchaseFlags(purchaseItems []entities.PurchaseItem, totalWe
|
||||
|
||||
// === HPP BAHAN BAKU (from ProjectBudget + ExpenseRealization) ===
|
||||
|
||||
func ToHppBahanBakuGroup(budgets []entities.ProjectBudget, realizations []entities.ExpenseRealization, totalWeightSold, totalPopulation float64) HppGroup {
|
||||
func ToHppBahanBakuGroup(budgets []entities.ProjectBudget, realizations []entities.ExpenseRealization, totalWeightProduced, totalPopulation float64) HppGroup {
|
||||
items := []HppItem{}
|
||||
|
||||
// Overhead: all budgets vs (all expenses EXCEPT ekspedisi)
|
||||
budgetAmount := sumBudgetsByFilter(budgets, func(*entities.ProjectBudget) bool { return true })
|
||||
realizationAmount := sumRealizationsByFilter(realizations, filterRealizationExceptFlag(utils.FlagEkspedisi))
|
||||
budgetRpPerBird, budgetRpPerKg := calculatePerUnitMetrics(budgetAmount, totalPopulation, totalWeightSold)
|
||||
realizationRpPerBird, realizationRpPerKg := calculatePerUnitMetrics(realizationAmount, totalPopulation, totalWeightSold)
|
||||
budgetRpPerBird, budgetRpPerKg := calculatePerUnitMetrics(budgetAmount, totalPopulation, totalWeightProduced)
|
||||
realizationRpPerBird, realizationRpPerKg := calculatePerUnitMetrics(realizationAmount, totalPopulation, totalWeightProduced)
|
||||
|
||||
if budgetAmount > 0 || realizationAmount > 0 {
|
||||
items = append(items, HppItem{
|
||||
@@ -165,7 +165,7 @@ func ToHppBahanBakuGroup(budgets []entities.ProjectBudget, realizations []entiti
|
||||
|
||||
// Ekspedisi: no budgeting, only expenses WITH flag EKSPEDISI
|
||||
ekspedisiAmount := sumRealizationsByFilter(realizations, filterRealizationByNonstockFlag(utils.FlagEkspedisi))
|
||||
ekspedisiRpPerBird, ekspedisiRpPerKg := calculatePerUnitMetrics(ekspedisiAmount, totalPopulation, totalWeightSold)
|
||||
ekspedisiRpPerBird, ekspedisiRpPerKg := calculatePerUnitMetrics(ekspedisiAmount, totalPopulation, totalWeightProduced)
|
||||
|
||||
if ekspedisiAmount > 0 {
|
||||
items = append(items, HppItem{
|
||||
@@ -185,7 +185,7 @@ func ToHppBahanBakuGroup(budgets []entities.ProjectBudget, realizations []entiti
|
||||
|
||||
// === HPP SUMMARY ===
|
||||
|
||||
func ToSummaryHpp(label string, purchaseItems []entities.PurchaseItem, budgets []entities.ProjectBudget, realizations []entities.ExpenseRealization, totalWeightSold, totalPopulation float64) SummaryHpp {
|
||||
func ToSummaryHpp(label string, purchaseItems []entities.PurchaseItem, budgets []entities.ProjectBudget, realizations []entities.ExpenseRealization, totalWeightProduced, totalPopulation float64) SummaryHpp {
|
||||
// Budget: purchases + budgets
|
||||
purchaseTotal := sumPurchaseTotal(purchaseItems)
|
||||
budgetTotal := sumBudgetsByFilter(budgets, func(*entities.ProjectBudget) bool { return true })
|
||||
@@ -194,8 +194,8 @@ func ToSummaryHpp(label string, purchaseItems []entities.PurchaseItem, budgets [
|
||||
// Realization: all expenses
|
||||
totalRealization := sumRealizationsByFilter(realizations, func(*entities.ExpenseRealization) bool { return true })
|
||||
|
||||
budgetRpPerBird, budgetRpPerKg := calculatePerUnitMetrics(totalBudget, totalPopulation, totalWeightSold)
|
||||
realizationRpPerBird, realizationRpPerKg := calculatePerUnitMetrics(totalRealization, totalPopulation, totalWeightSold)
|
||||
budgetRpPerBird, budgetRpPerKg := calculatePerUnitMetrics(totalBudget, totalPopulation, totalWeightProduced)
|
||||
realizationRpPerBird, realizationRpPerKg := calculatePerUnitMetrics(totalRealization, totalPopulation, totalWeightProduced)
|
||||
|
||||
return SummaryHpp{
|
||||
Label: label,
|
||||
@@ -206,16 +206,16 @@ func ToSummaryHpp(label string, purchaseItems []entities.PurchaseItem, budgets [
|
||||
}
|
||||
}
|
||||
|
||||
func ToHppPurchasesSection(purchaseItems []entities.PurchaseItem, budgets []entities.ProjectBudget, realizations []entities.ExpenseRealization, totalWeightSold, totalPopulation float64) HppPurchasesSection {
|
||||
func ToHppPurchasesSection(purchaseItems []entities.PurchaseItem, budgets []entities.ProjectBudget, realizations []entities.ExpenseRealization, totalWeightProduced, totalPopulation float64) HppPurchasesSection {
|
||||
hppGroups := []HppGroup{
|
||||
{
|
||||
GroupName: "HPP dan Pengeluaran",
|
||||
Data: buildHppItemsByPurchaseFlags(purchaseItems, totalWeightSold, totalPopulation),
|
||||
Data: buildHppItemsByPurchaseFlags(purchaseItems, totalWeightProduced, totalPopulation),
|
||||
},
|
||||
ToHppBahanBakuGroup(budgets, realizations, totalWeightSold, totalPopulation),
|
||||
ToHppBahanBakuGroup(budgets, realizations, totalWeightProduced, totalPopulation),
|
||||
}
|
||||
|
||||
summaryHpp := ToSummaryHpp("HPP", purchaseItems, budgets, realizations, totalWeightSold, totalPopulation)
|
||||
summaryHpp := ToSummaryHpp("HPP", purchaseItems, budgets, realizations, totalWeightProduced, totalPopulation)
|
||||
|
||||
return HppPurchasesSection{
|
||||
Hpp: hppGroups,
|
||||
@@ -266,37 +266,33 @@ func ToPenjualanItems(projectFlockCategory string, deliveryProducts []entities.M
|
||||
return items
|
||||
}
|
||||
|
||||
func ToPembelianItems(purchases []entities.PurchaseItem, totalPopulation, totalWeightSold float64) []PLItem {
|
||||
amount := sumPurchasesByFilter(purchases, func(item *entities.PurchaseItem) bool {
|
||||
if item.Product == nil || len(item.Product.Flags) == 0 {
|
||||
return false
|
||||
}
|
||||
for _, flag := range item.Product.Flags {
|
||||
flagType := strings.ToUpper(flag.Name)
|
||||
if flagType == string(utils.FlagDOC) || flagType == string(utils.FlagOVK) || flagType == string(utils.FlagPakan) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
})
|
||||
func ToPembelianItems(purchases []entities.PurchaseItem, budgets []entities.ProjectBudget, realizations []entities.ExpenseRealization, totalPopulation, totalWeightProduced float64) []PLItem {
|
||||
// Calculate total cost using same logic as report penjualan:
|
||||
// Total Cost = All Purchase Items + All BOP Expenses
|
||||
purchaseAmount := sumPurchaseTotal(purchases)
|
||||
|
||||
rpPerBird, rpPerKg := calculatePerUnitMetrics(amount, totalPopulation, totalWeightSold)
|
||||
// Get BOP expenses (all expenses except ekspedisi)
|
||||
bopAmount := sumRealizationsByFilter(realizations, filterRealizationExceptFlag(utils.FlagEkspedisi))
|
||||
|
||||
totalCost := purchaseAmount + bopAmount
|
||||
|
||||
rpPerBird, rpPerKg := calculatePerUnitMetrics(totalCost, totalPopulation, totalWeightProduced)
|
||||
return []PLItem{
|
||||
ToPLItem("Pembelian Sapronak Supplier", ToFinancialMetrics(rpPerBird, rpPerKg, amount)),
|
||||
ToPLItem("Harga Pokok Penjualan (HPP)", ToFinancialMetrics(rpPerBird, rpPerKg, totalCost)),
|
||||
}
|
||||
}
|
||||
|
||||
func ToOverheadItems(budgets []entities.ProjectBudget, realizations []entities.ExpenseRealization, totalPopulation, totalWeightSold float64) []PLItem {
|
||||
func ToOverheadItems(budgets []entities.ProjectBudget, realizations []entities.ExpenseRealization, totalPopulation, totalWeightProduced float64) []PLItem {
|
||||
realizationAmount := sumRealizationsByFilter(realizations, filterRealizationExceptFlag(utils.FlagEkspedisi))
|
||||
rpPerBird, rpPerKg := calculatePerUnitMetrics(realizationAmount, totalPopulation, totalWeightSold)
|
||||
rpPerBird, rpPerKg := calculatePerUnitMetrics(realizationAmount, totalPopulation, totalWeightProduced)
|
||||
return []PLItem{
|
||||
ToPLItem("Pengeluaran Overhead", ToFinancialMetrics(rpPerBird, rpPerKg, realizationAmount)),
|
||||
}
|
||||
}
|
||||
|
||||
func ToEkspedisiItems(realizations []entities.ExpenseRealization, totalPopulation, totalWeightSold float64) []PLItem {
|
||||
func ToEkspedisiItems(realizations []entities.ExpenseRealization, totalPopulation, totalWeightProduced float64) []PLItem {
|
||||
amount := sumRealizationsByFilter(realizations, filterRealizationByNonstockFlag(utils.FlagEkspedisi))
|
||||
rpPerBird, rpPerKg := calculatePerUnitMetrics(amount, totalPopulation, totalWeightSold)
|
||||
rpPerBird, rpPerKg := calculatePerUnitMetrics(amount, totalPopulation, totalWeightProduced)
|
||||
return []PLItem{
|
||||
ToPLItem("Beban Ekspedisi", ToFinancialMetrics(rpPerBird, rpPerKg, amount)),
|
||||
}
|
||||
@@ -348,7 +344,7 @@ func ToReportResponse(hppPurchases HppPurchasesSection, profitLoss ProfitLossSec
|
||||
|
||||
// === MAIN BUILDER ===
|
||||
|
||||
func ToClosingKeuanganReport(projectFlockCategory string, purchaseItems []entities.PurchaseItem, budgets []entities.ProjectBudget, realizations []entities.ExpenseRealization, deliveryProducts []entities.MarketingDeliveryProduct, chickins []entities.ProjectChickin) ReportResponse {
|
||||
func ToClosingKeuanganReport(projectFlockCategory string, purchaseItems []entities.PurchaseItem, budgets []entities.ProjectBudget, realizations []entities.ExpenseRealization, deliveryProducts []entities.MarketingDeliveryProduct, chickins []entities.ProjectChickin, totalWeightProduced float64) ReportResponse {
|
||||
var totalPopulation float64
|
||||
var totalWeightSold float64
|
||||
|
||||
@@ -360,12 +356,13 @@ func ToClosingKeuanganReport(projectFlockCategory string, purchaseItems []entiti
|
||||
totalWeightSold += delivery.TotalWeight
|
||||
}
|
||||
|
||||
hppSection := ToHppPurchasesSection(purchaseItems, budgets, realizations, totalWeightSold, totalPopulation)
|
||||
// Use totalWeightProduced for HPP calculation (not totalWeightSold)
|
||||
hppSection := ToHppPurchasesSection(purchaseItems, budgets, realizations, totalWeightProduced, totalPopulation)
|
||||
|
||||
penjualanItems := ToPenjualanItems(projectFlockCategory, deliveryProducts, totalPopulation, totalWeightSold)
|
||||
pembelianItems := ToPembelianItems(purchaseItems, totalPopulation, totalWeightSold)
|
||||
overheadItems := ToOverheadItems(budgets, realizations, totalPopulation, totalWeightSold)
|
||||
ekspedisiItems := ToEkspedisiItems(realizations, totalPopulation, totalWeightSold)
|
||||
pembelianItems := ToPembelianItems(purchaseItems, budgets, realizations, totalPopulation, totalWeightProduced)
|
||||
overheadItems := ToOverheadItems(budgets, realizations, totalPopulation, totalWeightProduced)
|
||||
ekspedisiItems := ToEkspedisiItems(realizations, totalPopulation, totalWeightProduced)
|
||||
plSection := ToProfitLossSection(penjualanItems, pembelianItems, overheadItems, ekspedisiItems)
|
||||
|
||||
return ToReportResponse(hppSection, plSection)
|
||||
|
||||
@@ -69,7 +69,7 @@ func ToOverheadDTO(budget *entity.ProjectBudget, realization *entity.ExpenseReal
|
||||
return dto
|
||||
}
|
||||
|
||||
func ToOverheadListDTOs(budgets []entity.ProjectBudget, realizations []entity.ExpenseRealization, totalChickinQty float64) OverheadListDTO {
|
||||
func ToOverheadListDTOs(budgets []entity.ProjectBudget, realizations []entity.ExpenseRealization, totalChickinQty, totalActualPopulation float64) OverheadListDTO {
|
||||
overheadsByNonstockID := make(map[uint]*OverheadDTO)
|
||||
latestDateByNonstockID := make(map[uint]string)
|
||||
|
||||
@@ -119,7 +119,8 @@ func ToOverheadListDTOs(budgets []entity.ProjectBudget, realizations []entity.Ex
|
||||
|
||||
for nonstockID, overhead := range overheadsByNonstockID {
|
||||
overhead.ActualDate = latestDateByNonstockID[nonstockID]
|
||||
overhead.CostPerBird = calculateCostPerBird(overhead.ActualTotalAmount, totalChickinQty)
|
||||
|
||||
overhead.CostPerBird = calculateCostPerBird(overhead.ActualTotalAmount, totalActualPopulation)
|
||||
|
||||
if overhead.ActualQuantity > 0 {
|
||||
overhead.ActualUnitPrice = overhead.ActualTotalAmount / overhead.ActualQuantity
|
||||
@@ -139,7 +140,7 @@ func ToOverheadListDTOs(budgets []entity.ProjectBudget, realizations []entity.Ex
|
||||
BudgetTotalAmount: totalBudgetAmount,
|
||||
ActualQuantity: totalActualQuantity,
|
||||
ActualTotalAmount: totalActualAmount,
|
||||
CostPerBird: calculateCostPerBird(totalActualAmount, totalChickinQty),
|
||||
CostPerBird: calculateCostPerBird(totalActualAmount, totalActualPopulation),
|
||||
},
|
||||
Overheads: overheadItems,
|
||||
}
|
||||
@@ -158,9 +159,9 @@ func calculateTotal(qty, price float64) float64 {
|
||||
return qty * price
|
||||
}
|
||||
|
||||
func calculateCostPerBird(totalPrice, totalChickinQty float64) float64 {
|
||||
if totalChickinQty > 0 {
|
||||
return totalPrice / totalChickinQty
|
||||
func calculateCostPerBird(totalPrice, totalActualPopulation float64) float64 {
|
||||
if totalActualPopulation > 0 {
|
||||
return totalPrice / totalActualPopulation
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
rMarketings "gitlab.com/mbugroup/lti-api.git/internal/modules/marketing/repositories"
|
||||
rChickin "gitlab.com/mbugroup/lti-api.git/internal/modules/production/chickins/repositories"
|
||||
rProjectFlock "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project_flocks/repositories"
|
||||
rRecording "gitlab.com/mbugroup/lti-api.git/internal/modules/production/recordings/repositories"
|
||||
rPurchase "gitlab.com/mbugroup/lti-api.git/internal/modules/purchases/repositories"
|
||||
|
||||
rUser "gitlab.com/mbugroup/lti-api.git/internal/modules/users/repositories"
|
||||
@@ -31,11 +32,12 @@ func (ClosingModule) RegisterRoutes(router fiber.Router, db *gorm.DB, validate *
|
||||
marketingDeliveryProductRepo := rMarketings.NewMarketingDeliveryProductRepository(db)
|
||||
expenseRealizationRepo := rExpenseRealization.NewExpenseRealizationRepository(db)
|
||||
chickinRepo := rChickin.NewChickinRepository(db)
|
||||
recordingRepo := rRecording.NewRecordingRepository(db)
|
||||
purchaseRepo := rPurchase.NewPurchaseRepository(db)
|
||||
approvalRepo := commonRepo.NewApprovalRepository(db)
|
||||
approvalService := commonSvc.NewApprovalService(approvalRepo)
|
||||
|
||||
closingService := sClosing.NewClosingService(closingRepo, projectFlockRepo, marketingRepo, marketingDeliveryProductRepo, approvalService, expenseRealizationRepo, projectBudgetRepo, chickinRepo, purchaseRepo, validate)
|
||||
closingService := sClosing.NewClosingService(closingRepo, projectFlockRepo, marketingRepo, marketingDeliveryProductRepo, approvalService, expenseRealizationRepo, projectBudgetRepo, chickinRepo, purchaseRepo, recordingRepo, validate)
|
||||
sapronakService := sClosing.NewSapronakService(closingRepo, projectFlockKandangRepo, validate)
|
||||
userService := sUser.NewUserService(userRepo, validate)
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
marketingRepository "gitlab.com/mbugroup/lti-api.git/internal/modules/marketing/repositories"
|
||||
chickinRepository "gitlab.com/mbugroup/lti-api.git/internal/modules/production/chickins/repositories"
|
||||
projectflockRepository "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project_flocks/repositories"
|
||||
recordingRepository "gitlab.com/mbugroup/lti-api.git/internal/modules/production/recordings/repositories"
|
||||
purchaseRepository "gitlab.com/mbugroup/lti-api.git/internal/modules/purchases/repositories"
|
||||
"gitlab.com/mbugroup/lti-api.git/internal/utils"
|
||||
approvalutils "gitlab.com/mbugroup/lti-api.git/internal/utils/approvals"
|
||||
@@ -47,9 +48,10 @@ type closingService struct {
|
||||
ProjectBudgetRepo projectflockRepository.ProjectBudgetRepository
|
||||
ChickinRepo chickinRepository.ProjectChickinRepository
|
||||
PurchaseRepo purchaseRepository.PurchaseRepository
|
||||
RecordingRepo recordingRepository.RecordingRepository
|
||||
}
|
||||
|
||||
func NewClosingService(repo repository.ClosingRepository, projectFlockRepo projectflockRepository.ProjectflockRepository, marketingRepo marketingRepository.MarketingRepository, marketingDeliveryProductRepo marketingDeliveryProductRepository.MarketingDeliveryProductRepository, approvalSvc commonSvc.ApprovalService, expenseRealizationRepo expenseRealizationRepository.ExpenseRealizationRepository, projectBudgetRepo projectflockRepository.ProjectBudgetRepository, chickinRepo chickinRepository.ProjectChickinRepository, purchaseRepo purchaseRepository.PurchaseRepository, validate *validator.Validate) ClosingService {
|
||||
func NewClosingService(repo repository.ClosingRepository, projectFlockRepo projectflockRepository.ProjectflockRepository, marketingRepo marketingRepository.MarketingRepository, marketingDeliveryProductRepo marketingDeliveryProductRepository.MarketingDeliveryProductRepository, approvalSvc commonSvc.ApprovalService, expenseRealizationRepo expenseRealizationRepository.ExpenseRealizationRepository, projectBudgetRepo projectflockRepository.ProjectBudgetRepository, chickinRepo chickinRepository.ProjectChickinRepository, purchaseRepo purchaseRepository.PurchaseRepository, recordingRepo recordingRepository.RecordingRepository, validate *validator.Validate) ClosingService {
|
||||
return &closingService{
|
||||
Log: utils.Log,
|
||||
Validate: validate,
|
||||
@@ -62,6 +64,7 @@ func NewClosingService(repo repository.ClosingRepository, projectFlockRepo proje
|
||||
ProjectBudgetRepo: projectBudgetRepo,
|
||||
ChickinRepo: chickinRepo,
|
||||
PurchaseRepo: purchaseRepo,
|
||||
RecordingRepo: recordingRepo,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -379,7 +382,14 @@ func (s closingService) GetOverhead(c *fiber.Ctx, projectFlockID uint) (*dto.Ove
|
||||
totalChickinQty += chickin.UsageQty
|
||||
}
|
||||
|
||||
result := dto.ToOverheadListDTOs(budgets, realizations, totalChickinQty)
|
||||
totalDepletion, err := s.RecordingRepo.GetTotalDepletionByProjectFlockID(c.Context(), projectFlockID)
|
||||
if err != nil {
|
||||
s.Log.Warnf("GetTotalDepletionByProjectFlockID error: %v", err)
|
||||
}
|
||||
|
||||
totalActualPopulation := totalChickinQty - totalDepletion
|
||||
|
||||
result := dto.ToOverheadListDTOs(budgets, realizations, totalChickinQty, totalActualPopulation)
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
@@ -435,7 +445,12 @@ func (s closingService) GetClosingKeuangan(c *fiber.Ctx, projectFlockID uint) (*
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch chickins")
|
||||
}
|
||||
|
||||
report := dto.ToClosingKeuanganReport(projectFlock.Category, purchaseItems, budgets, realizations, deliveryProducts, chickins)
|
||||
totalWeightProduced, _, err := s.RecordingRepo.GetProductionWeightAndQtyByProjectFlockID(c.Context(), projectFlockID)
|
||||
if err != nil {
|
||||
s.Log.Warnf("GetProductionWeightAndQtyByProjectFlockID error: %v", err)
|
||||
}
|
||||
|
||||
report := dto.ToClosingKeuanganReport(projectFlock.Category, purchaseItems, budgets, realizations, deliveryProducts, chickins, totalWeightProduced)
|
||||
|
||||
return &report, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user