mirror of
https://gitlab.com/mbugroup/lti-api.git
synced 2026-05-20 13:31:56 +00:00
feat[BE-375]: get api closing data produksi
This commit is contained in:
@@ -3,6 +3,7 @@ package service
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"math"
|
||||
"strconv"
|
||||
|
||||
commonSvc "gitlab.com/mbugroup/lti-api.git/internal/common/service"
|
||||
@@ -30,6 +31,7 @@ type ClosingService interface {
|
||||
GetPenjualan(ctx *fiber.Ctx, projectFlockID uint) ([]entity.MarketingDeliveryProduct, error)
|
||||
GetClosingSummary(ctx *fiber.Ctx, projectFlockID uint) (*dto.ClosingSummaryDTO, error)
|
||||
GetOverhead(ctx *fiber.Ctx, projectFlockID uint) (*dto.OverheadListDTO, error)
|
||||
GetClosingDataProduksi(ctx *fiber.Ctx, projectFlockID uint) (*dto.ClosingProductionReportDTO, error)
|
||||
GetClosingSapronak(ctx *fiber.Ctx, projectFlockID uint, params *validation.SapronakQuery) ([]dto.ClosingSapronakItemDTO, int64, error)
|
||||
}
|
||||
|
||||
@@ -379,3 +381,219 @@ func (s closingService) GetOverhead(c *fiber.Ctx, projectFlockID uint) (*dto.Ove
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
func (s closingService) GetClosingDataProduksi(c *fiber.Ctx, projectFlockID uint) (*dto.ClosingProductionReportDTO, error) {
|
||||
if projectFlockID == 0 {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid project flock id")
|
||||
}
|
||||
|
||||
project, err := s.Repository.GetByID(c.Context(), projectFlockID, s.withClosingRelations)
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, fiber.NewError(fiber.StatusNotFound, "Project flock not found")
|
||||
}
|
||||
if err != nil {
|
||||
s.Log.Errorf("Failed get project flock %d for closing data produksi: %+v", projectFlockID, err)
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch project flock")
|
||||
}
|
||||
|
||||
var population float64
|
||||
for _, history := range project.KandangHistory {
|
||||
for _, chickin := range history.Chickins {
|
||||
population += chickin.UsageQty + chickin.PendingUsageQty
|
||||
}
|
||||
}
|
||||
|
||||
projectFlockKandangIDs, err := s.getProjectFlockKandangIDs(c.Context(), projectFlockID)
|
||||
if err != nil {
|
||||
s.Log.Errorf("Failed to fetch project flock kandangs for %d: %+v", projectFlockID, err)
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch project flock kandangs")
|
||||
}
|
||||
|
||||
feedIn, feedUsed, err := s.Repository.SumFeedPurchaseAndUsedByProjectFlockKandangIDs(c.Context(), projectFlockKandangIDs)
|
||||
if err != nil {
|
||||
s.Log.Errorf("Failed to sum feed purchase/used qty for project flock %d: %+v", projectFlockID, err)
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch feed purchase data")
|
||||
}
|
||||
|
||||
claimCulling, err := s.Repository.SumClaimCullingByProjectFlockKandangIDs(c.Context(), projectFlockKandangIDs)
|
||||
if err != nil {
|
||||
s.Log.Errorf("Failed to sum claim culling for project flock %d: %+v", projectFlockID, err)
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch claim culling data")
|
||||
}
|
||||
|
||||
finalPopulation := population - claimCulling
|
||||
|
||||
var standards []entity.FcrStandard
|
||||
if project.FcrId > 0 {
|
||||
standards, err = s.Repository.GetFcrStandardsByFcrID(c.Context(), project.FcrId)
|
||||
if err != nil {
|
||||
s.Log.Errorf("Failed to fetch FCR standards for project flock %d: %+v", projectFlockID, err)
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch FCR standard data")
|
||||
}
|
||||
}
|
||||
// masih dummy, karena tab penjualan agenya masih dummy juga
|
||||
age := 1.0
|
||||
|
||||
feedUsedPerHead := 0.0
|
||||
if population > 0 {
|
||||
feedUsedPerHead = feedUsed / population
|
||||
}
|
||||
|
||||
purchase := dto.ClosingPurchaseDTO{
|
||||
InitialPopulation: int(population),
|
||||
ClaimCulling: int(claimCulling),
|
||||
FinalPopulation: int(finalPopulation),
|
||||
FeedIn: feedIn,
|
||||
FeedUsed: feedUsed,
|
||||
FeedUsedPerHead: feedUsedPerHead,
|
||||
}
|
||||
|
||||
chickenFlagNames := []string{string(utils.FlagPullet)}
|
||||
chickenSalesWeight, chickenSalesQty, chickenSalesPrice, err := s.Repository.SumMarketingWeightAndQtyByProjectFlockKandangIDsAndFlagNames(c.Context(), projectFlockKandangIDs, chickenFlagNames)
|
||||
if err != nil {
|
||||
s.Log.Errorf("Failed to fetch chicken sales data for project flock %d: %+v", projectFlockID, err)
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch chicken sales data")
|
||||
}
|
||||
|
||||
var chickenAverageWeight float64
|
||||
if chickenSalesQty > 0 {
|
||||
chickenAverageWeight = chickenSalesWeight / chickenSalesQty
|
||||
}
|
||||
|
||||
var chickenAverageSellingPrice float64
|
||||
if chickenSalesWeight > 0 {
|
||||
chickenAverageSellingPrice = chickenSalesPrice / chickenSalesWeight
|
||||
}
|
||||
|
||||
eggFlagNames := []string{
|
||||
string(utils.FlagTelur),
|
||||
string(utils.FlagTelurUtuh),
|
||||
string(utils.FlagTelurPecah),
|
||||
string(utils.FlagTelurPutih),
|
||||
string(utils.FlagTelurRetak),
|
||||
}
|
||||
eggSalesWeight, eggSalesQty, eggSalesPrice, err := s.Repository.SumMarketingWeightAndQtyByProjectFlockKandangIDsAndFlagNames(c.Context(), projectFlockKandangIDs, eggFlagNames)
|
||||
if err != nil {
|
||||
s.Log.Errorf("Failed to fetch egg sales data for project flock %d: %+v", projectFlockID, err)
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch egg sales data")
|
||||
}
|
||||
|
||||
var averageEggWeight float64
|
||||
if eggSalesQty > 0 {
|
||||
averageEggWeight = eggSalesWeight / eggSalesQty
|
||||
}
|
||||
|
||||
var averageEggSellingPrice float64
|
||||
if eggSalesWeight > 0 {
|
||||
averageEggSellingPrice = eggSalesPrice / eggSalesWeight
|
||||
}
|
||||
|
||||
chickenSales := dto.ClosingSalesDTO{
|
||||
SalesPopulation: int(chickenSalesQty),
|
||||
SalesWeight: chickenSalesWeight,
|
||||
AverageWeight: chickenAverageWeight,
|
||||
AverageSellingPrice: chickenAverageSellingPrice,
|
||||
}
|
||||
|
||||
eggSales := dto.ClosingEggSalesDTO{
|
||||
EggPieces: int(eggSalesQty),
|
||||
EggMassKg: eggSalesWeight,
|
||||
AverageEggWeightKg: averageEggWeight,
|
||||
AverageSellingPrice: averageEggSellingPrice,
|
||||
}
|
||||
|
||||
harvestEggQty, err := s.Repository.SumRecordingEggQtyByProjectFlockKandangIDsAndFlagNames(c.Context(), projectFlockKandangIDs, eggFlagNames)
|
||||
if err != nil {
|
||||
s.Log.Errorf("Failed to fetch recording egg qty for project flock %d: %+v", projectFlockID, err)
|
||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch egg harvest data")
|
||||
}
|
||||
|
||||
chickenDepletion := population - chickenSalesQty
|
||||
if chickenDepletion < 0 {
|
||||
chickenDepletion = 0
|
||||
}
|
||||
eggDepletion := harvestEggQty - eggSalesQty
|
||||
if eggDepletion < 0 {
|
||||
eggDepletion = 0
|
||||
}
|
||||
|
||||
chickenPerformance := calculatePerformanceMetrics(chickenAverageWeight, chickenSalesWeight, feedUsed, population, chickenDepletion, age, standards)
|
||||
eggPerformance := calculatePerformanceMetrics(averageEggWeight, eggSalesWeight, feedUsed, harvestEggQty, eggDepletion, age, standards)
|
||||
|
||||
sales := dto.ClosingSalesGroupDTO{
|
||||
ChickenProduction: chickenSales,
|
||||
EggProduction: eggSales,
|
||||
}
|
||||
|
||||
performance := dto.ClosingPerformanceDTO{
|
||||
Depletion: chickenPerformance.Depletion,
|
||||
Age: age,
|
||||
MortalityStd: chickenPerformance.MortalityStd,
|
||||
MortalityAct: chickenPerformance.MortalityAct,
|
||||
DeffMortality: chickenPerformance.DeffMortality,
|
||||
FcrStd: eggPerformance.FcrStd,
|
||||
FcrAct: eggPerformance.FcrAct,
|
||||
DeffFcr: eggPerformance.DeffFcr,
|
||||
Adg: eggPerformance.Adg,
|
||||
}
|
||||
|
||||
result := dto.ClosingProductionReportDTO{
|
||||
Purchase: purchase,
|
||||
Sales: sales,
|
||||
Performance: performance,
|
||||
}
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
func calculatePerformanceMetrics(averageWeight, totalWeight, feedUsed, basePopulation, depletion, age float64, standards []entity.FcrStandard) dto.ClosingPerformanceDTO {
|
||||
mortalityStd, fcrStd := closestFcrValues(standards, averageWeight)
|
||||
|
||||
fcrAct := 0.0
|
||||
if totalWeight > 0 {
|
||||
fcrAct = feedUsed / totalWeight
|
||||
}
|
||||
|
||||
mortalityAct := 0.0
|
||||
if basePopulation > 0 {
|
||||
mortalityAct = (depletion / basePopulation) * 100
|
||||
}
|
||||
|
||||
deffMortality := mortalityStd - mortalityAct
|
||||
deffFcr := fcrStd - fcrAct
|
||||
|
||||
adg := 0.0
|
||||
if age > 0 {
|
||||
adg = averageWeight / age
|
||||
}
|
||||
|
||||
return dto.ClosingPerformanceDTO{
|
||||
Depletion: depletion,
|
||||
Age: age,
|
||||
MortalityStd: mortalityStd,
|
||||
MortalityAct: mortalityAct,
|
||||
DeffMortality: deffMortality,
|
||||
FcrStd: fcrStd,
|
||||
FcrAct: fcrAct,
|
||||
DeffFcr: deffFcr,
|
||||
Adg: adg,
|
||||
}
|
||||
}
|
||||
|
||||
func closestFcrValues(standards []entity.FcrStandard, averageWeight float64) (float64, float64) {
|
||||
if len(standards) == 0 || averageWeight <= 0 {
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
closest := standards[0]
|
||||
minDiff := math.Abs(closest.Weight - averageWeight)
|
||||
for _, std := range standards[1:] {
|
||||
diff := math.Abs(std.Weight - averageWeight)
|
||||
if diff < minDiff {
|
||||
minDiff = diff
|
||||
closest = std
|
||||
}
|
||||
}
|
||||
|
||||
return closest.Mortality, closest.FcrNumber
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user