feat[BE-384]: enhance reporting by adding chickin quantity and egg production weight calculations; refactor HPP calculations to consider product categories

This commit is contained in:
aguhh18
2025-12-18 17:56:18 +07:00
parent c95f90f0b9
commit e551995c66
6 changed files with 157 additions and 110 deletions
@@ -90,6 +90,7 @@ func (r *MarketingDeliveryProductRepositoryImpl) GetAllWithFilters(ctx context.C
Preload("Marketing.SalesPerson"). Preload("Marketing.SalesPerson").
Preload("ProductWarehouse"). Preload("ProductWarehouse").
Preload("ProductWarehouse.Product"). Preload("ProductWarehouse.Product").
Preload("ProductWarehouse.Product.Flags").
Preload("ProductWarehouse.Warehouse"). Preload("ProductWarehouse.Warehouse").
Preload("ProductWarehouse.ProjectFlockKandang"). Preload("ProductWarehouse.ProjectFlockKandang").
Preload("ProductWarehouse.ProjectFlockKandang.ProjectFlock") Preload("ProductWarehouse.ProjectFlockKandang.ProjectFlock")
@@ -15,6 +15,7 @@ type ProjectChickinRepository interface {
GetByProjectFlockKandangID(ctx context.Context, projectFlockKandangID uint) ([]entity.ProjectChickin, error) GetByProjectFlockKandangID(ctx context.Context, projectFlockKandangID uint) ([]entity.ProjectChickin, error)
GetPendingByProjectFlockKandangID(ctx context.Context, projectFlockKandangID uint) ([]entity.ProjectChickin, error) GetPendingByProjectFlockKandangID(ctx context.Context, projectFlockKandangID uint) ([]entity.ProjectChickin, error)
GetTotalPendingUsageQtyByProjectFlockKandangID(ctx context.Context, projectFlockKandangID uint) (float64, error) GetTotalPendingUsageQtyByProjectFlockKandangID(ctx context.Context, projectFlockKandangID uint) (float64, error)
GetTotalChickinQtyByProjectFlockID(ctx context.Context, projectFlockID uint) (float64, error)
} }
type ChickinRepositoryImpl struct { type ChickinRepositoryImpl struct {
@@ -90,3 +91,14 @@ func (r *ChickinRepositoryImpl) GetTotalPendingUsageQtyByProjectFlockKandangID(c
} }
return total, nil return total, nil
} }
func (r *ChickinRepositoryImpl) GetTotalChickinQtyByProjectFlockID(ctx context.Context, projectFlockID uint) (float64, error) {
var result float64
err := r.db.WithContext(ctx).
Table("project_chickins").
Select("COALESCE(SUM(project_chickins.usage_qty), 0)").
Joins("JOIN project_flock_kandangs ON project_flock_kandangs.id = project_chickins.project_flock_kandang_id").
Where("project_flock_kandangs.project_flock_id = ?", projectFlockID).
Scan(&result).Error
return result, err
}
@@ -48,6 +48,7 @@ type RecordingRepository interface {
GetProductionWeightAndQtyByProjectFlockID(ctx context.Context, projectFlockID uint) (totalWeight float64, totalQty float64, err error) GetProductionWeightAndQtyByProjectFlockID(ctx context.Context, projectFlockID uint) (totalWeight float64, totalQty float64, err error)
GetTotalDepletionByProjectFlockID(ctx context.Context, projectFlockID uint) (totalDepletion float64, err error) GetTotalDepletionByProjectFlockID(ctx context.Context, projectFlockID uint) (totalDepletion float64, err error)
GetLatestAvgWeightByProjectFlockID(ctx context.Context, projectFlockID uint) (avgWeight float64, err error) GetLatestAvgWeightByProjectFlockID(ctx context.Context, projectFlockID uint) (avgWeight float64, err error)
GetTotalEggProductionWeightByProjectFlockID(ctx context.Context, projectFlockID uint) (totalWeightKg float64, err error)
} }
type RecordingRepositoryImpl struct { type RecordingRepositoryImpl struct {
@@ -371,28 +372,23 @@ func (r *RecordingRepositoryImpl) GetProductionWeightAndQtyByProjectFlockID(ctx
return 0, 0, nil return 0, 0, nil
} }
// Get total chickin quantity for this ProjectFlock
totalChickinQty, err := r.getTotalChickinQtyByProjectFlockID(ctx, projectFlockID) totalChickinQty, err := r.getTotalChickinQtyByProjectFlockID(ctx, projectFlockID)
if err != nil { if err != nil {
return 0, 0, err return 0, 0, err
} }
// Get total depletion for this ProjectFlock
totalDepletion, err := r.GetTotalDepletionByProjectFlockID(ctx, projectFlockID) totalDepletion, err := r.GetTotalDepletionByProjectFlockID(ctx, projectFlockID)
if err != nil { if err != nil {
return 0, 0, err return 0, 0, err
} }
// Calculate actual quantity produced
actualQty := totalChickinQty - totalDepletion actualQty := totalChickinQty - totalDepletion
// Get latest average weight from RecordingBW
avgWeight, err := r.GetLatestAvgWeightByProjectFlockID(ctx, projectFlockID) avgWeight, err := r.GetLatestAvgWeightByProjectFlockID(ctx, projectFlockID)
if err != nil { if err != nil {
return 0, 0, err return 0, 0, err
} }
// Calculate total weight
totalWeight = actualQty * avgWeight totalWeight = actualQty * avgWeight
return totalWeight, actualQty, nil return totalWeight, actualQty, nil
@@ -434,6 +430,22 @@ func (r *RecordingRepositoryImpl) GetLatestAvgWeightByProjectFlockID(ctx context
return result, err return result, err
} }
func (r *RecordingRepositoryImpl) GetTotalEggProductionWeightByProjectFlockID(ctx context.Context, projectFlockID uint) (float64, error) {
if projectFlockID == 0 {
return 0, nil
}
var result float64
err := r.DB().WithContext(ctx).
Table("recording_eggs").
Select("COALESCE(SUM(recording_eggs.qty * recording_eggs.weight), 0) / 1000").
Joins("JOIN recordings ON recordings.id = recording_eggs.recording_id").
Joins("JOIN project_flock_kandangs ON project_flock_kandangs.id = recordings.project_flock_kandangs_id").
Where("project_flock_kandangs.project_flock_id = ?", projectFlockID).
Scan(&result).Error
return result, err
}
func nextRecordingDay(days []int) int { func nextRecordingDay(days []int) int {
if len(days) == 0 { if len(days) == 0 {
return 1 return 1
@@ -1,14 +1,15 @@
package dto package dto
import ( import (
"fmt"
"time" "time"
entity "gitlab.com/mbugroup/lti-api.git/internal/entities" entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
marketingDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/marketing/dto"
customerDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/customers/dto" customerDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/customers/dto"
productDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/products/dto" productDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/products/dto"
warehouseDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/warehouses/dto" warehouseDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/warehouses/dto"
userDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/users/dto" userDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/users/dto"
"gitlab.com/mbugroup/lti-api.git/internal/utils"
) )
type RepportMarketingItemDTO struct { type RepportMarketingItemDTO struct {
@@ -45,7 +46,7 @@ type RepportMarketingResponseDTO struct {
Total *Summary `json:"total,omitempty"` Total *Summary `json:"total,omitempty"`
} }
func ToRepportMarketingItemDTO(mdp entity.MarketingDeliveryProduct, hppPricePerKg float64) RepportMarketingItemDTO { func ToRepportMarketingItemDTO(mdp entity.MarketingDeliveryProduct, hppPricePerKg float64, category string) RepportMarketingItemDTO {
soDate := time.Time{} soDate := time.Time{}
agingDays := 0 agingDays := 0
if mdp.MarketingProduct.Marketing.SoDate.Year() > 1 { if mdp.MarketingProduct.Marketing.SoDate.Year() > 1 {
@@ -58,11 +59,17 @@ func ToRepportMarketingItemDTO(mdp entity.MarketingDeliveryProduct, hppPricePerK
realizationDate = *mdp.DeliveryDate realizationDate = *mdp.DeliveryDate
} }
doNumber := generateDoNumber(mdp.MarketingProduct.Marketing.SoNumber, mdp.DeliveryDate, mdp.MarketingProduct.ProductWarehouse.WarehouseId) doNumber := marketingDTO.GenerateDeliveryOrderNumber(mdp.MarketingProduct.Marketing.SoNumber, mdp.DeliveryDate, mdp.MarketingProduct.ProductWarehouse.WarehouseId)
totalWeightKg := mdp.Qty * mdp.AvgWeight totalWeightKg := mdp.Qty * mdp.AvgWeight
salesAmount := totalWeightKg * mdp.UnitPrice salesAmount := totalWeightKg * mdp.UnitPrice
hppAmount := totalWeightKg * hppPricePerKg
var hpp float64
var hppAmount float64
if isProductEligibleForHpp(mdp, category) {
hpp = hppPricePerKg
hppAmount = totalWeightKg * hppPricePerKg
}
item := RepportMarketingItemDTO{ item := RepportMarketingItemDTO{
ID: int(mdp.Id), ID: int(mdp.Id),
@@ -70,12 +77,12 @@ func ToRepportMarketingItemDTO(mdp entity.MarketingDeliveryProduct, hppPricePerK
RealizationDate: realizationDate, RealizationDate: realizationDate,
AgingDays: agingDays, AgingDays: agingDays,
DoNumber: doNumber, DoNumber: doNumber,
MarketingType: "ayam", MarketingType: getMarketingType(mdp),
Qty: mdp.Qty, Qty: mdp.Qty,
AverageWeightKg: mdp.AvgWeight, AverageWeightKg: mdp.AvgWeight,
TotalWeightKg: totalWeightKg, TotalWeightKg: totalWeightKg,
SalesPricePerKg: mdp.UnitPrice, SalesPricePerKg: mdp.UnitPrice,
HppPricePerKg: hppPricePerKg, HppPricePerKg: hpp,
SalesAmount: salesAmount, SalesAmount: salesAmount,
HppAmount: hppAmount, HppAmount: hppAmount,
} }
@@ -105,10 +112,10 @@ func ToRepportMarketingItemDTO(mdp entity.MarketingDeliveryProduct, hppPricePerK
return item return item
} }
func ToRepportMarketingItemDTOs(mdps []entity.MarketingDeliveryProduct, hppPricePerKg float64) []RepportMarketingItemDTO { func ToRepportMarketingItemDTOs(mdps []entity.MarketingDeliveryProduct, hppPricePerKg float64, category string) []RepportMarketingItemDTO {
items := make([]RepportMarketingItemDTO, 0, len(mdps)) items := make([]RepportMarketingItemDTO, 0, len(mdps))
for _, mdp := range mdps { for _, mdp := range mdps {
items = append(items, ToRepportMarketingItemDTO(mdp, hppPricePerKg)) items = append(items, ToRepportMarketingItemDTO(mdp, hppPricePerKg, category))
} }
return items return items
} }
@@ -117,23 +124,72 @@ func ToRepportMarketingItemDTOsWithHppMap(mdps []entity.MarketingDeliveryProduct
items := make([]RepportMarketingItemDTO, 0, len(mdps)) items := make([]RepportMarketingItemDTO, 0, len(mdps))
for _, mdp := range mdps { for _, mdp := range mdps {
hppPerKg := float64(0) hppPerKg := float64(0)
category := ""
if projectFlockKandang := mdp.MarketingProduct.ProductWarehouse.ProjectFlockKandang; projectFlockKandang != nil { if projectFlockKandang := mdp.MarketingProduct.ProductWarehouse.ProjectFlockKandang; projectFlockKandang != nil {
if hpp, exists := hppMap[projectFlockKandang.ProjectFlockId]; exists { if hpp, exists := hppMap[projectFlockKandang.ProjectFlockId]; exists {
hppPerKg = hpp hppPerKg = hpp
} }
category = projectFlockKandang.ProjectFlock.Category
} }
items = append(items, ToRepportMarketingItemDTO(mdp, hppPerKg))
item := ToRepportMarketingItemDTO(mdp, hppPerKg, category)
items = append(items, item)
} }
return items return items
} }
func ToSummary(mdps []entity.MarketingDeliveryProduct, hppPricePerKg float64) *Summary { func getMarketingType(mdp entity.MarketingDeliveryProduct) string {
hasAyam, hasTelur := checkProductFlags(mdp.MarketingProduct.ProductWarehouse.Product.Flags)
if hasAyam {
return "ayam"
}
if hasTelur {
return "telur"
}
return "trading"
}
func checkProductFlags(flags []entity.Flag) (hasAyam, hasTelur bool) {
if len(flags) == 0 {
return false, false
}
for _, flag := range flags {
ft := utils.FlagType(flag.Name)
if ft == utils.FlagAyamAfkir || ft == utils.FlagAyamCulling || ft == utils.FlagAyamMati ||
ft == utils.FlagDOC || ft == utils.FlagPullet || ft == utils.FlagLayer {
hasAyam = true
}
if ft == utils.FlagTelur || ft == utils.FlagTelurUtuh || ft == utils.FlagTelurPecah ||
ft == utils.FlagTelurPutih || ft == utils.FlagTelurRetak {
hasTelur = true
}
}
return hasAyam, hasTelur
}
func isProductEligibleForHpp(mdp entity.MarketingDeliveryProduct, category string) bool {
hasAyam, hasTelur := checkProductFlags(mdp.MarketingProduct.ProductWarehouse.Product.Flags)
if utils.ProjectFlockCategory(category) == utils.ProjectFlockCategoryGrowing {
return hasAyam
}
return hasAyam || hasTelur
}
func ToSummary(mdps []entity.MarketingDeliveryProduct, hppPricePerKg float64, category string) *Summary {
if len(mdps) == 0 { if len(mdps) == 0 {
return nil return nil
} }
totalQty := 0 totalQty := 0
totalWeightKg := 0.0 totalWeightKg := 0.0
totalEligibleWeightKg := 0.0
totalSalesAmount := int64(0) totalSalesAmount := int64(0)
totalHppAmount := int64(0) totalHppAmount := int64(0)
@@ -142,12 +198,16 @@ func ToSummary(mdps []entity.MarketingDeliveryProduct, hppPricePerKg float64) *S
totalQty += int(mdp.Qty) totalQty += int(mdp.Qty)
totalWeightKg += calculatedTotalWeight totalWeightKg += calculatedTotalWeight
totalSalesAmount += int64(calculatedTotalWeight * mdp.UnitPrice) totalSalesAmount += int64(calculatedTotalWeight * mdp.UnitPrice)
totalHppAmount += int64(calculatedTotalWeight * hppPricePerKg)
if isProductEligibleForHpp(mdp, category) {
totalEligibleWeightKg += calculatedTotalWeight
totalHppAmount += int64(calculatedTotalWeight * hppPricePerKg)
}
} }
totalHppPricePerKg := float64(0) totalHppPricePerKg := float64(0)
if totalWeightKg > 0 { if totalEligibleWeightKg > 0 {
totalHppPricePerKg = float64(totalHppAmount) / totalWeightKg totalHppPricePerKg = float64(totalHppAmount) / totalEligibleWeightKg
} }
return &Summary{ return &Summary{
@@ -159,14 +219,6 @@ func ToSummary(mdps []entity.MarketingDeliveryProduct, hppPricePerKg float64) *S
} }
} }
func generateDoNumber(soNumber string, deliveryDate *time.Time, warehouseId uint) string {
dateStr := ""
if deliveryDate != nil {
dateStr = deliveryDate.Format("20060102")
}
return fmt.Sprintf("%s-%s-%d", soNumber, dateStr, warehouseId)
}
func ToSummaryFromDTOItems(items []RepportMarketingItemDTO) *Summary { func ToSummaryFromDTOItems(items []RepportMarketingItemDTO) *Summary {
if len(items) == 0 { if len(items) == 0 {
return nil return nil
@@ -198,9 +250,9 @@ func ToSummaryFromDTOItems(items []RepportMarketingItemDTO) *Summary {
} }
} }
func ToRepportMarketingResponseDTO(mdps []entity.MarketingDeliveryProduct, hppPricePerKg float64) RepportMarketingResponseDTO { func ToRepportMarketingResponseDTO(mdps []entity.MarketingDeliveryProduct, hppPricePerKg float64, category string) RepportMarketingResponseDTO {
items := ToRepportMarketingItemDTOs(mdps, hppPricePerKg) items := ToRepportMarketingItemDTOs(mdps, hppPricePerKg, category)
total := ToSummary(mdps, hppPricePerKg) total := ToSummary(mdps, hppPricePerKg, category)
return RepportMarketingResponseDTO{ return RepportMarketingResponseDTO{
Items: items, Items: items,
+3 -1
View File
@@ -11,6 +11,7 @@ import (
expenseRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/expenses/repositories" expenseRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/expenses/repositories"
marketingRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/marketing/repositories" marketingRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/marketing/repositories"
chickinRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/production/chickins/repositories"
recordingRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/production/recordings/repositories" recordingRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/production/recordings/repositories"
purchaseRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/purchases/repositories" purchaseRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/purchases/repositories"
) )
@@ -22,11 +23,12 @@ func (RepportModule) RegisterRoutes(router fiber.Router, db *gorm.DB, validate *
expenseRealizationRepository := expenseRepo.NewExpenseRealizationRepository(db) expenseRealizationRepository := expenseRepo.NewExpenseRealizationRepository(db)
marketingDeliveryProductRepository := marketingRepo.NewMarketingDeliveryProductRepository(db) marketingDeliveryProductRepository := marketingRepo.NewMarketingDeliveryProductRepository(db)
purchaseRepository := purchaseRepo.NewPurchaseRepository(db) purchaseRepository := purchaseRepo.NewPurchaseRepository(db)
chickinRepository := chickinRepo.NewChickinRepository(db)
recordingRepository := recordingRepo.NewRecordingRepository(db) recordingRepository := recordingRepo.NewRecordingRepository(db)
approvalRepository := commonRepo.NewApprovalRepository(db) approvalRepository := commonRepo.NewApprovalRepository(db)
approvalSvc := approvalService.NewApprovalService(approvalRepository) approvalSvc := approvalService.NewApprovalService(approvalRepository)
repportService := sRepport.NewRepportService(validate, expenseRealizationRepository, marketingDeliveryProductRepository, purchaseRepository, recordingRepository, approvalSvc) repportService := sRepport.NewRepportService(validate, expenseRealizationRepository, marketingDeliveryProductRepository, purchaseRepository, chickinRepository, recordingRepository, approvalSvc)
RepportRoutes(router, repportService) RepportRoutes(router, repportService)
} }
@@ -3,7 +3,6 @@ package service
import ( import (
"context" "context"
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
"gitlab.com/mbugroup/lti-api.git/internal/modules/repports/dto" "gitlab.com/mbugroup/lti-api.git/internal/modules/repports/dto"
validation "gitlab.com/mbugroup/lti-api.git/internal/modules/repports/validations" validation "gitlab.com/mbugroup/lti-api.git/internal/modules/repports/validations"
"gitlab.com/mbugroup/lti-api.git/internal/utils" "gitlab.com/mbugroup/lti-api.git/internal/utils"
@@ -12,6 +11,7 @@ import (
approvalDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/approvals/dto" approvalDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/approvals/dto"
expenseRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/expenses/repositories" expenseRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/expenses/repositories"
marketingRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/marketing/repositories" marketingRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/marketing/repositories"
chickinRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/production/chickins/repositories"
recordingRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/production/recordings/repositories" recordingRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/production/recordings/repositories"
purchaseRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/purchases/repositories" purchaseRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/purchases/repositories"
@@ -32,17 +32,19 @@ type repportService struct {
ExpenseRealizationRepo expenseRepo.ExpenseRealizationRepository ExpenseRealizationRepo expenseRepo.ExpenseRealizationRepository
MarketingDeliveryRepo marketingRepo.MarketingDeliveryProductRepository MarketingDeliveryRepo marketingRepo.MarketingDeliveryProductRepository
PurchaseRepo purchaseRepo.PurchaseRepository PurchaseRepo purchaseRepo.PurchaseRepository
ChickinRepo chickinRepo.ProjectChickinRepository
RecordingRepo recordingRepo.RecordingRepository RecordingRepo recordingRepo.RecordingRepository
ApprovalSvc approvalService.ApprovalService ApprovalSvc approvalService.ApprovalService
} }
func NewRepportService(validate *validator.Validate, expenseRealizationRepo expenseRepo.ExpenseRealizationRepository, marketingDeliveryRepo marketingRepo.MarketingDeliveryProductRepository, purchaseRepo purchaseRepo.PurchaseRepository, recordingRepo recordingRepo.RecordingRepository, approvalSvc approvalService.ApprovalService) RepportService { func NewRepportService(validate *validator.Validate, expenseRealizationRepo expenseRepo.ExpenseRealizationRepository, marketingDeliveryRepo marketingRepo.MarketingDeliveryProductRepository, purchaseRepo purchaseRepo.PurchaseRepository, chickinRepo chickinRepo.ProjectChickinRepository, recordingRepo recordingRepo.RecordingRepository, approvalSvc approvalService.ApprovalService) RepportService {
return &repportService{ return &repportService{
Log: utils.Log, Log: utils.Log,
Validate: validate, Validate: validate,
ExpenseRealizationRepo: expenseRealizationRepo, ExpenseRealizationRepo: expenseRealizationRepo,
MarketingDeliveryRepo: marketingDeliveryRepo, MarketingDeliveryRepo: marketingDeliveryRepo,
PurchaseRepo: purchaseRepo, PurchaseRepo: purchaseRepo,
ChickinRepo: chickinRepo,
RecordingRepo: recordingRepo, RecordingRepo: recordingRepo,
ApprovalSvc: approvalSvc, ApprovalSvc: approvalSvc,
} }
@@ -98,74 +100,63 @@ func (s *repportService) GetMarketing(c *fiber.Ctx, params *validation.Marketing
return nil, 0, err return nil, 0, err
} }
projectFlockIDs := s.collectProjectFlockIDs(deliveryProducts) projectFlockIDMap := make(map[uint]bool)
hppMap := s.buildHppMap(c.Context(), projectFlockIDs, deliveryProducts) hppMap := make(map[uint]float64)
items := dto.ToRepportMarketingItemDTOsWithHppMap(deliveryProducts, hppMap)
for _, dp := range deliveryProducts {
if projectFlockKandang := dp.MarketingProduct.ProductWarehouse.ProjectFlockKandang; projectFlockKandang != nil {
projectFlockID := projectFlockKandang.ProjectFlockId
if projectFlockID > 0 && !projectFlockIDMap[projectFlockID] {
projectFlockIDMap[projectFlockID] = true
category := projectFlockKandang.ProjectFlock.Category
hppPerKg := s.calculateHppPricePerKg(c.Context(), projectFlockID, category)
hppMap[projectFlockID] = hppPerKg
}
}
}
items := dto.ToRepportMarketingItemDTOsWithHppMap(deliveryProducts, hppMap)
return items, total, nil return items, total, nil
} }
func (s *repportService) collectProjectFlockIDs(deliveryProducts []entity.MarketingDeliveryProduct) []uint { func (s *repportService) calculateHppPricePerKg(ctx context.Context, projectFlockID uint, category string) float64 {
projectFlockIDMap := make(map[uint]bool) totalCost := s.getTotalProjectCost(ctx, projectFlockID)
projectFlockIDs := make([]uint, 0) if totalCost == 0 {
for _, dp := range deliveryProducts {
if projectFlockKandang := dp.MarketingProduct.ProductWarehouse.ProjectFlockKandang; projectFlockKandang != nil {
if projectFlockKandang.ProjectFlockId > 0 && !projectFlockIDMap[projectFlockKandang.ProjectFlockId] {
projectFlockIDs = append(projectFlockIDs, projectFlockKandang.ProjectFlockId)
projectFlockIDMap[projectFlockKandang.ProjectFlockId] = true
}
}
}
return projectFlockIDs
}
func (s *repportService) buildHppMap(ctx context.Context, projectFlockIDs []uint, deliveryProducts []entity.MarketingDeliveryProduct) map[uint]float64 {
hppMap := make(map[uint]float64)
for _, projectFlockID := range projectFlockIDs {
category := s.getProjectFlockCategory(deliveryProducts, projectFlockID)
hppPerKg := s.calculateHppByCategory(ctx, category, projectFlockID, deliveryProducts)
hppMap[projectFlockID] = hppPerKg
}
return hppMap
}
func (s *repportService) calculateHppByCategory(ctx context.Context, category string, projectFlockID uint, deliveryProducts []entity.MarketingDeliveryProduct) float64 {
switch utils.ProjectFlockCategory(category) {
case utils.ProjectFlockCategoryGrowing:
return s.calculateHppPricePerKg(ctx, projectFlockID, deliveryProducts)
case utils.ProjectFlockCategoryLaying:
return 0
default:
return 0 return 0
} }
}
func (s *repportService) getProjectFlockCategory(deliveryProducts []entity.MarketingDeliveryProduct, projectFlockID uint) string { chickinQty, _ := s.ChickinRepo.GetTotalChickinQtyByProjectFlockID(ctx, projectFlockID)
for _, dp := range deliveryProducts { depletion, _ := s.RecordingRepo.GetTotalDepletionByProjectFlockID(ctx, projectFlockID)
if projectFlockKandang := dp.MarketingProduct.ProductWarehouse.ProjectFlockKandang; projectFlockKandang != nil { avgWeight, _ := s.RecordingRepo.GetLatestAvgWeightByProjectFlockID(ctx, projectFlockID)
if projectFlockKandang.ProjectFlockId == projectFlockID {
return projectFlockKandang.ProjectFlock.Category var totalWeight float64
} if utils.ProjectFlockCategory(category) == utils.ProjectFlockCategoryGrowing {
} totalWeight = (chickinQty - depletion) * avgWeight
} else {
eggWeight, _ := s.RecordingRepo.GetTotalEggProductionWeightByProjectFlockID(ctx, projectFlockID)
totalWeight = (chickinQty-depletion)*avgWeight + eggWeight
} }
return ""
if totalWeight == 0 {
return 0
}
return totalCost / totalWeight
} }
func (s *repportService) calculateHppPricePerKg(ctx context.Context, projectFlockID uint, deliveryProducts []entity.MarketingDeliveryProduct) float64 { func (s *repportService) getTotalProjectCost(ctx context.Context, projectFlockID uint) float64 {
if projectFlockID == 0 { if projectFlockID == 0 {
return 0 return 0
} }
purchaseItems, err := s.PurchaseRepo.GetItemsByProjectFlockID(ctx, projectFlockID) purchases, err := s.PurchaseRepo.GetItemsByProjectFlockID(ctx, projectFlockID)
if err != nil { if err != nil {
s.Log.Warnf("GetItemsByProjectFlockID error: %v", err) s.Log.Warnf("GetItemsByProjectFlockID error: %v", err)
} }
costPurchase := float64(0) cost := float64(0)
for _, item := range purchaseItems { for _, p := range purchases {
costPurchase += item.TotalPrice cost += p.TotalPrice
} }
realizations, err := s.ExpenseRealizationRepo.GetByProjectFlockID(ctx, projectFlockID) realizations, err := s.ExpenseRealizationRepo.GetByProjectFlockID(ctx, projectFlockID)
@@ -173,34 +164,11 @@ func (s *repportService) calculateHppPricePerKg(ctx context.Context, projectFloc
s.Log.Warnf("GetByProjectFlockID error: %v", err) s.Log.Warnf("GetByProjectFlockID error: %v", err)
} }
costBop := float64(0) for _, r := range realizations {
for _, realization := range realizations { if r.ExpenseNonstock != nil && r.ExpenseNonstock.Expense != nil &&
cost := realization.Price * realization.Qty r.ExpenseNonstock.Expense.Category == string(utils.ExpenseCategoryBOP) {
category := "" cost += r.Price * r.Qty
if realization.ExpenseNonstock != nil && realization.ExpenseNonstock.Expense != nil {
category = realization.ExpenseNonstock.Expense.Category
}
if category == "BOP" {
costBop += cost
} }
} }
return cost
totalActualCost := costPurchase + costBop
if totalActualCost == 0 {
return 0
}
totalWeightProduced, _, err := s.RecordingRepo.GetProductionWeightAndQtyByProjectFlockID(ctx, projectFlockID)
if err != nil {
s.Log.Warnf("GetProductionWeightAndQtyByProjectFlockID error: %v", err)
}
if totalWeightProduced == 0 {
return 0
}
hppPerKg := totalActualCost / totalWeightProduced
return hppPerKg
} }