From b088eebac5f9aa8a3256c3c865b47062fe663cac Mon Sep 17 00:00:00 2001 From: aguhh18 Date: Tue, 13 Jan 2026 13:36:08 +0700 Subject: [PATCH] feat[BE]: enhance GetOverhead functionality with project flock kandang count mapping and update related DTOs --- .../controllers/closing.controller.go | 2 +- .../closings/dto/closingOverhead.dto.go | 57 +++++++++++++------ .../closings/services/closing.service.go | 32 +++++++++-- .../expense_realization.repository.go | 10 +--- 4 files changed, 71 insertions(+), 30 deletions(-) diff --git a/internal/modules/closings/controllers/closing.controller.go b/internal/modules/closings/controllers/closing.controller.go index c4ef4585..a10d6a94 100644 --- a/internal/modules/closings/controllers/closing.controller.go +++ b/internal/modules/closings/controllers/closing.controller.go @@ -184,7 +184,7 @@ func (u *ClosingController) GetPenjualanByProjectFlockKandang(c *fiber.Ctx) erro func (u *ClosingController) GetOverhead(c *fiber.Ctx) error { projectParam := c.Params("project_flock_id") - kandangParam := c.Params("project_flock_kandang_id") // bisa kosong + kandangParam := c.Params("project_flock_kandang_id") projectFlockID, err := strconv.Atoi(projectParam) if err != nil || projectFlockID <= 0 { diff --git a/internal/modules/closings/dto/closingOverhead.dto.go b/internal/modules/closings/dto/closingOverhead.dto.go index 42903794..4730474a 100644 --- a/internal/modules/closings/dto/closingOverhead.dto.go +++ b/internal/modules/closings/dto/closingOverhead.dto.go @@ -71,7 +71,7 @@ func ToOverheadDTO(budget *entity.ProjectBudget, realization *entity.ExpenseReal return dto } -func ToOverheadListDTOs(budgets []entity.ProjectBudget, realizations []entity.ExpenseRealization, totalChickinQty, totalActualPopulation float64, isPerKandang bool, totalKandangCount int) OverheadListDTO { +func ToOverheadListDTOs(budgets []entity.ProjectBudget, realizations []entity.ExpenseRealization, totalChickinQty, totalActualPopulation float64, isPerKandang bool, totalKandangCount int, projectFlockKandangCountMap map[uint]int) OverheadListDTO { overheadsByNonstockID := make(map[uint]*OverheadDTO) latestDateByNonstockID := make(map[uint]string) @@ -89,6 +89,7 @@ func ToOverheadListDTOs(budgets []entity.ProjectBudget, realizations []entity.Ex budgetPrice := budgets[i].Price budgetTotal := calculateTotal(budgets[i].Qty, budgets[i].Price) + // Budget division: per kandang view only if isPerKandang && totalKandangCount > 0 { budgetQty = budgetQty / float64(totalKandangCount) budgetTotal = budgetTotal / float64(totalKandangCount) @@ -109,17 +110,35 @@ func ToOverheadListDTOs(budgets []entity.ProjectBudget, realizations []entity.Ex overheadsByNonstockID[nonstockID] = &OverheadDTO{} } - // Check if this is farm-level expense (multiple project flocks) qty := realizations[i].Qty totalAmount := calculateTotal(realizations[i].Qty, realizations[i].Price) + // Farm-level expense division if realizations[i].ExpenseNonstock.Expense != nil && realizations[i].ExpenseNonstock.Expense.ProjectFlockId != nil { - projectFlockCount := countProjectFlocksInJSON(*realizations[i].ExpenseNonstock.Expense.ProjectFlockId) - if projectFlockCount > 1 { - // Bagi biaya sesuai jumlah project flock - qty = qty / float64(projectFlockCount) - totalAmount = totalAmount / float64(projectFlockCount) + projectFlockIDs := parseProjectFlockIDsFromJSON(*realizations[i].ExpenseNonstock.Expense.ProjectFlockId) + + if len(projectFlockIDs) > 0 { + totalKandangInAllProjects := 0 + for _, pfID := range projectFlockIDs { + if count, exists := projectFlockKandangCountMap[pfID]; exists { + totalKandangInAllProjects += count + } + } + + if totalKandangInAllProjects > 0 { + if isPerKandang { + qty = qty / float64(totalKandangInAllProjects) + totalAmount = totalAmount / float64(totalKandangInAllProjects) + } else { + // Overhead ALL: divide by total kandang then multiply by this project's kandang count + perKandangAmount := totalAmount / float64(totalKandangInAllProjects) + perKandangQty := qty / float64(totalKandangInAllProjects) + + qty = perKandangQty * float64(totalKandangCount) + totalAmount = perKandangAmount * float64(totalKandangCount) + } + } } } @@ -172,22 +191,24 @@ func ToOverheadListDTOs(budgets []entity.ProjectBudget, realizations []entity.Ex } } -// === Helper Functions === +func parseProjectFlockIDsFromJSON(projectFlockJSON string) []uint { + if projectFlockJSON == "" { + return []uint{} + } + + var projectFlocks []uint + if err := json.Unmarshal([]byte(projectFlockJSON), &projectFlocks); err != nil { + return []uint{} + } + + return projectFlocks +} func countProjectFlocksInJSON(projectFlockJSON string) int { - if projectFlockJSON == "" { - return 0 - } - - var projectFlocks []int - if err := json.Unmarshal([]byte(projectFlockJSON), &projectFlocks); err != nil { - return 1 // default to 1 if parsing fails - } - + projectFlocks := parseProjectFlockIDsFromJSON(projectFlockJSON) if len(projectFlocks) == 0 { return 1 } - return len(projectFlocks) } diff --git a/internal/modules/closings/services/closing.service.go b/internal/modules/closings/services/closing.service.go index 5870aa12..f137901d 100644 --- a/internal/modules/closings/services/closing.service.go +++ b/internal/modules/closings/services/closing.service.go @@ -2,6 +2,7 @@ package service import ( "context" + "encoding/json" "errors" "math" "strconv" @@ -368,13 +369,38 @@ func (s closingService) GetOverhead(c *fiber.Ctx, projectFlockID uint, projectFl return nil, err } - // Count total kandang in project flock (for budget division if per kandang) projectFlockKandangs, err := s.ProjectFlockKandangRepo.GetByProjectFlockID(c.Context(), projectFlockID) if err != nil { return nil, err } totalKandangCount := len(projectFlockKandangs) + // Build kandang count map for farm expense division + projectFlockKandangCountMap := make(map[uint]int) + projectFlockKandangCountMap[projectFlockID] = totalKandangCount + + involvedProjectFlocks := make(map[uint]bool) + for _, realization := range realizations { + if realization.ExpenseNonstock != nil && + realization.ExpenseNonstock.Expense != nil && + realization.ExpenseNonstock.Expense.ProjectFlockId != nil { + var projectFlockIDs []uint + if err := json.Unmarshal([]byte(*realization.ExpenseNonstock.Expense.ProjectFlockId), &projectFlockIDs); err == nil { + for _, pfID := range projectFlockIDs { + if pfID != projectFlockID { + involvedProjectFlocks[pfID] = true + } + } + } + } + } + + for pfID := range involvedProjectFlocks { + if pfKandangs, err := s.ProjectFlockKandangRepo.GetByProjectFlockID(c.Context(), pfID); err == nil { + projectFlockKandangCountMap[pfID] = len(pfKandangs) + } + } + chickins, err := s.ChickinRepo.GetByProjectFlockID(c.Context(), projectFlockID) if err != nil { return nil, err @@ -384,7 +410,6 @@ func (s closingService) GetOverhead(c *fiber.Ctx, projectFlockID uint, projectFl var totalDepletion float64 if projectFlockKandangID != nil { - for _, chickin := range chickins { if chickin.ProjectFlockKandangId == *projectFlockKandangID { totalChickinQty += chickin.UsageQty @@ -404,7 +429,6 @@ func (s closingService) GetOverhead(c *fiber.Ctx, projectFlockID uint, projectFl totalDepletion = depletionResult } } else { - for _, chickin := range chickins { totalChickinQty += chickin.UsageQty } @@ -417,7 +441,7 @@ func (s closingService) GetOverhead(c *fiber.Ctx, projectFlockID uint, projectFl totalActualPopulation := totalChickinQty - totalDepletion - result := dto.ToOverheadListDTOs(budgets, realizations, totalChickinQty, totalActualPopulation, projectFlockKandangID != nil, totalKandangCount) + result := dto.ToOverheadListDTOs(budgets, realizations, totalChickinQty, totalActualPopulation, projectFlockKandangID != nil, totalKandangCount, projectFlockKandangCountMap) return &result, nil } diff --git a/internal/modules/expenses/repositories/expense_realization.repository.go b/internal/modules/expenses/repositories/expense_realization.repository.go index 2c7db649..60ec97a7 100644 --- a/internal/modules/expenses/repositories/expense_realization.repository.go +++ b/internal/modules/expenses/repositories/expense_realization.repository.go @@ -72,18 +72,14 @@ func (r *ExpenseRealizationRepositoryImpl) GetClosingOverhead(ctx context.Contex Joins("LEFT JOIN kandangs ON kandangs.id = expense_nonstocks.kandang_id"). Where("expenses.realization_date IS NOT NULL") - // Build WHERE clause for project flock filtering if projectFlockKandangID != nil { - // Per kandang: hanya ambil expense yang specific ke kandang tersebut - // SKIP expense level farm (yang punya multiple project flocks di JSON array) - // IMPORTANT: Untuk kandang_id, pastikan kandang tersebut belong to project_flock_kandang ini db = db.Where(`( expense_nonstocks.project_flock_kandang_id = ? OR (expense_nonstocks.kandang_id = (SELECT kandang_id FROM project_flock_kandangs WHERE id = ?) AND - expense_nonstocks.project_flock_kandang_id IS NULL) - )`, *projectFlockKandangID, *projectFlockKandangID) + expense_nonstocks.project_flock_kandang_id IS NULL) OR + (expenses.project_flock_id IS NOT NULL AND expenses.project_flock_id::jsonb @> ?::jsonb) + )`, *projectFlockKandangID, *projectFlockKandangID, fmt.Sprintf("[%d]", projectFlockID)) } else { - // All kandang: include expense kandang-specific DAN expense level farm db = db.Where(`( project_flock_kandangs.project_flock_id = ? OR kandangs.id IN (SELECT kandang_id FROM project_flock_kandangs WHERE project_flock_id = ?) OR