package dto import ( entity "gitlab.com/mbugroup/lti-api.git/internal/entities" ) // === DTO Structs === type OverheadDTO struct { ItemName string `json:"item_name"` UOMName string `json:"uom_name"` BudgetQuantity float64 `json:"budget_quantity"` BudgetUnitPrice float64 `json:"budget_unit_price"` BudgetTotalAmount float64 `json:"budget_total_amount"` ActualDate string `json:"actual_date"` ActualQuantity float64 `json:"actual_quantity"` ActualUnitPrice float64 `json:"actual_unit_price"` ActualTotalAmount float64 `json:"actual_total_amount"` CostPerBird float64 `json:"cost_per_bird"` } type TotalDTO struct { BudgetQuantity float64 `json:"budget_quantity"` BudgetTotalAmount float64 `json:"budget_total_amount"` ActualQuantity float64 `json:"actual_quantity"` ActualTotalAmount float64 `json:"actual_total_amount"` CostPerBird float64 `json:"cost_per_bird"` } type OverheadListDTO struct { Total TotalDTO `json:"total"` Overheads []OverheadDTO `json:"overheads"` } // === Mapper Functions === func ToOverheadDTO(budget *entity.ProjectBudget, realization *entity.ExpenseRealization) OverheadDTO { if budget == nil && realization == nil { return OverheadDTO{} } var itemName, itemUOM string if budget != nil { itemName, itemUOM = getItemInfo(budget.Nonstock) } if itemName == "" && realization != nil && realization.ExpenseNonstock != nil { itemName, itemUOM = getItemInfo(realization.ExpenseNonstock.Nonstock) } dto := OverheadDTO{ ItemName: itemName, UOMName: itemUOM, } if budget != nil { dto.BudgetQuantity = budget.Qty dto.BudgetUnitPrice = budget.Price dto.BudgetTotalAmount = calculateTotal(budget.Qty, budget.Price) } if realization != nil { dto.ActualQuantity = realization.Qty dto.ActualUnitPrice = realization.Price dto.ActualTotalAmount = calculateTotal(realization.Qty, realization.Price) dto.ActualDate = formatRealizationDate(realization) } return dto } func ToOverheadListDTOs(budgets []entity.ProjectBudget, realizations []entity.ExpenseRealization, totalChickinQty, totalActualPopulation float64) OverheadListDTO { overheadsByNonstockID := make(map[uint]*OverheadDTO) latestDateByNonstockID := make(map[uint]string) for i := range budgets { nonstockID := budgets[i].NonstockId if overheadsByNonstockID[nonstockID] == nil { overheadsByNonstockID[nonstockID] = &OverheadDTO{} } itemName, itemUOM := getItemInfo(budgets[i].Nonstock) overheadsByNonstockID[nonstockID].ItemName = itemName overheadsByNonstockID[nonstockID].UOMName = itemUOM overheadsByNonstockID[nonstockID].BudgetQuantity = budgets[i].Qty overheadsByNonstockID[nonstockID].BudgetUnitPrice = budgets[i].Price overheadsByNonstockID[nonstockID].BudgetTotalAmount = calculateTotal(budgets[i].Qty, budgets[i].Price) } for i := range realizations { if realizations[i].ExpenseNonstock == nil || realizations[i].ExpenseNonstock.NonstockId == nil { continue } nonstockID := uint(*realizations[i].ExpenseNonstock.NonstockId) if overheadsByNonstockID[nonstockID] == nil { overheadsByNonstockID[nonstockID] = &OverheadDTO{} } overheadsByNonstockID[nonstockID].ActualQuantity += realizations[i].Qty overheadsByNonstockID[nonstockID].ActualTotalAmount += calculateTotal(realizations[i].Qty, realizations[i].Price) if overheadsByNonstockID[nonstockID].ItemName == "" { itemName, itemUOM := getItemInfo(realizations[i].ExpenseNonstock.Nonstock) overheadsByNonstockID[nonstockID].ItemName = itemName overheadsByNonstockID[nonstockID].UOMName = itemUOM } realizationDateStr := formatRealizationDate(&realizations[i]) if realizationDateStr != "" { if latestDateByNonstockID[nonstockID] == "" || realizationDateStr > latestDateByNonstockID[nonstockID] { latestDateByNonstockID[nonstockID] = realizationDateStr } } } var totalBudgetQuantity, totalBudgetAmount, totalActualQuantity, totalActualAmount float64 overheadItems := make([]OverheadDTO, 0, len(overheadsByNonstockID)) for nonstockID, overhead := range overheadsByNonstockID { overhead.ActualDate = latestDateByNonstockID[nonstockID] overhead.CostPerBird = calculateCostPerBird(overhead.ActualTotalAmount, totalActualPopulation) if overhead.ActualQuantity > 0 { overhead.ActualUnitPrice = overhead.ActualTotalAmount / overhead.ActualQuantity } totalBudgetQuantity += overhead.BudgetQuantity totalBudgetAmount += overhead.BudgetTotalAmount totalActualQuantity += overhead.ActualQuantity totalActualAmount += overhead.ActualTotalAmount overheadItems = append(overheadItems, *overhead) } return OverheadListDTO{ Total: TotalDTO{ BudgetQuantity: totalBudgetQuantity, BudgetTotalAmount: totalBudgetAmount, ActualQuantity: totalActualQuantity, ActualTotalAmount: totalActualAmount, CostPerBird: calculateCostPerBird(totalActualAmount, totalActualPopulation), }, Overheads: overheadItems, } } // === Helper Functions === func getItemInfo(nonstock *entity.Nonstock) (string, string) { if nonstock != nil && nonstock.Id != 0 { return nonstock.Name, nonstock.Uom.Name } return "", "" } func calculateTotal(qty, price float64) float64 { return qty * price } func calculateCostPerBird(totalPrice, totalActualPopulation float64) float64 { if totalActualPopulation > 0 { return totalPrice / totalActualPopulation } return 0 } func formatRealizationDate(realization *entity.ExpenseRealization) string { if realization != nil && realization.ExpenseNonstock != nil && realization.ExpenseNonstock.Expense != nil { if !realization.ExpenseNonstock.Expense.RealizationDate.IsZero() { return realization.ExpenseNonstock.Expense.RealizationDate.Format("2006-01-02T15:04:05Z07:00") } } return "" }