adjust grouping by project flock kandang

This commit is contained in:
giovanni
2026-01-19 17:05:43 +07:00
parent 71c62c5e02
commit 3052497fc0
3 changed files with 74 additions and 68 deletions
@@ -27,6 +27,7 @@ type HppPerKandangResponseData struct {
type HppPerKandangRowDTO struct { type HppPerKandangRowDTO struct {
ID int `json:"id"` ID int `json:"id"`
Kandang HppPerKandangRowKandangDTO `json:"kandang"` Kandang HppPerKandangRowKandangDTO `json:"kandang"`
NameWithPeriode string `json:"name_with_periode"`
WeightRange HppPerKandangWeightRangeDTO `json:"weight_range"` WeightRange HppPerKandangWeightRangeDTO `json:"weight_range"`
AvgWeightKg float64 `json:"avg_weight_kg"` AvgWeightKg float64 `json:"avg_weight_kg"`
EggProductionPieces int64 `json:"egg_production_pieces"` EggProductionPieces int64 `json:"egg_production_pieces"`
@@ -12,6 +12,7 @@ import (
type HppPerKandangRow struct { type HppPerKandangRow struct {
ProjectFlockKandangID uint ProjectFlockKandangID uint
ProjectFlockPeriod int
KandangID uint KandangID uint
KandangName string KandangName string
KandangStatus string KandangStatus string
@@ -29,21 +30,21 @@ type HppPerKandangRow struct {
} }
type HppPerKandangCostRow struct { type HppPerKandangCostRow struct {
KandangID uint ProjectFlockKandangID uint
FeedCost float64 FeedCost float64
OvkCost float64 OvkCost float64
DocCost float64 DocCost float64
DocQty float64 DocQty float64
BudgetCost float64 BudgetCost float64
ExpenseCost float64 ExpenseCost float64
} }
type HppPerKandangSupplierRow struct { type HppPerKandangSupplierRow struct {
KandangID uint ProjectFlockKandangID uint
SupplierID uint SupplierID uint
SupplierName string SupplierName string
SupplierAlias string SupplierAlias string
Category string Category string
} }
type HppPerKandangRepository interface { type HppPerKandangRepository interface {
@@ -88,6 +89,7 @@ func (r *hppPerKandangRepository) GetRowsByPeriod(ctx context.Context, start, en
Table("project_flocks AS pf"). Table("project_flocks AS pf").
Select(` Select(`
pfk.id AS project_flock_kandang_id, pfk.id AS project_flock_kandang_id,
pfk.period AS project_flock_period,
k.id AS kandang_id, k.id AS kandang_id,
k.name AS kandang_name, k.name AS kandang_name,
k.status AS kandang_status, k.status AS kandang_status,
@@ -118,8 +120,8 @@ func (r *hppPerKandangRepository) GetRowsByPeriod(ctx context.Context, start, en
query = applyLocationFilters(query, areaIDs, locationIDs, kandangIDs) query = applyLocationFilters(query, areaIDs, locationIDs, kandangIDs)
query = query.Group("pfk.id, k.id, k.name, k.status, loc.id, loc.name, pic.id, pic.name"). query = query.Group("pfk.id, pfk.period, k.id, k.name, k.status, loc.id, loc.name, pic.id, pic.name").
Order("k.id ASC") Order("pfk.id ASC")
if err := query.Scan(&rows).Error; err != nil { if err := query.Scan(&rows).Error; err != nil {
return nil, err return nil, err
@@ -150,7 +152,7 @@ func (r *hppPerKandangRepository) GetFeedOvkDocCostByPeriod(ctx context.Context,
query := r.db.WithContext(ctx). query := r.db.WithContext(ctx).
Table("recordings AS r"). Table("recordings AS r").
Select(` Select(`
k.id AS kandang_id, pfk.id AS project_flock_kandang_id,
COALESCE(SUM(CASE COALESCE(SUM(CASE
WHEN f.name = ? THEN COALESCE(sa.qty, 0) * COALESCE(pi.price, 0) WHEN f.name = ? THEN COALESCE(sa.qty, 0) * COALESCE(pi.price, 0)
WHEN sa.stockable_type = ? AND tf.name = ? THEN COALESCE(std.total_qty, 0) * COALESCE(tpi.price, 0) WHEN sa.stockable_type = ? AND tf.name = ? THEN COALESCE(std.total_qty, 0) * COALESCE(tpi.price, 0)
@@ -179,25 +181,25 @@ func (r *hppPerKandangRepository) GetFeedOvkDocCostByPeriod(ctx context.Context,
Where("r.deleted_at IS NULL"). Where("r.deleted_at IS NULL").
Where("(la.action IS NULL OR la.action != ?)", string(entity.ApprovalActionRejected)) Where("(la.action IS NULL OR la.action != ?)", string(entity.ApprovalActionRejected))
query = query.Group("k.id").Order("k.id ASC") query = query.Group("pfk.id").Order("pfk.id ASC")
if err := query.Scan(&rows).Error; err != nil { if err := query.Scan(&rows).Error; err != nil {
return nil, nil, err return nil, nil, err
} }
docRows := make([]struct { docRows := make([]struct {
KandangID uint ProjectFlockKandangID uint
DocCost float64 DocCost float64
DocQty float64 DocQty float64
SupplierID *uint SupplierID *uint
SupplierName *string SupplierName *string
SupplierAlias *string SupplierAlias *string
}, 0) }, 0)
docQuery := r.db.WithContext(ctx). docQuery := r.db.WithContext(ctx).
Table("project_chickins AS pc"). Table("project_chickins AS pc").
Select(` Select(`
pfk.kandang_id AS kandang_id, pfk.id AS project_flock_kandang_id,
COALESCE(SUM(pc.usage_qty * COALESCE(pi.price, 0)), 0) AS doc_cost, COALESCE(SUM(pc.usage_qty * COALESCE(pi.price, 0)), 0) AS doc_cost,
COALESCE(SUM(pc.usage_qty), 0) AS doc_qty, COALESCE(SUM(pc.usage_qty), 0) AS doc_qty,
s.id AS supplier_id, s.id AS supplier_id,
@@ -210,7 +212,7 @@ func (r *hppPerKandangRepository) GetFeedOvkDocCostByPeriod(ctx context.Context,
Joins("LEFT JOIN purchases AS pur ON pur.id = pi.purchase_id"). Joins("LEFT JOIN purchases AS pur ON pur.id = pi.purchase_id").
Joins("LEFT JOIN suppliers AS s ON s.id = pur.supplier_id"). Joins("LEFT JOIN suppliers AS s ON s.id = pur.supplier_id").
Where("pc.project_flock_kandang_id IN ?", projectFlockKandangIDs). Where("pc.project_flock_kandang_id IN ?", projectFlockKandangIDs).
Group("pfk.kandang_id, s.id, s.name, s.alias") Group("pfk.id, s.id, s.name, s.alias")
if err := docQuery.Scan(&docRows).Error; err != nil { if err := docQuery.Scan(&docRows).Error; err != nil {
return nil, nil, err return nil, nil, err
@@ -219,28 +221,28 @@ func (r *hppPerKandangRepository) GetFeedOvkDocCostByPeriod(ctx context.Context,
costMap := make(map[uint]*HppPerKandangCostRow, len(rows)) costMap := make(map[uint]*HppPerKandangCostRow, len(rows))
for i := range rows { for i := range rows {
row := rows[i] row := rows[i]
costMap[row.KandangID] = &rows[i] costMap[row.ProjectFlockKandangID] = &rows[i]
} }
docSuppliers := make([]HppPerKandangSupplierRow, 0) docSuppliers := make([]HppPerKandangSupplierRow, 0)
docSeen := make(map[uint]map[uint]bool) docSeen := make(map[uint]map[uint]bool)
for _, doc := range docRows { for _, doc := range docRows {
entry, ok := costMap[doc.KandangID] entry, ok := costMap[doc.ProjectFlockKandangID]
if !ok { if !ok {
rows = append(rows, HppPerKandangCostRow{ rows = append(rows, HppPerKandangCostRow{
KandangID: doc.KandangID, ProjectFlockKandangID: doc.ProjectFlockKandangID,
}) })
entry = &rows[len(rows)-1] entry = &rows[len(rows)-1]
costMap[doc.KandangID] = entry costMap[doc.ProjectFlockKandangID] = entry
} }
entry.DocCost += doc.DocCost entry.DocCost += doc.DocCost
entry.DocQty += doc.DocQty entry.DocQty += doc.DocQty
if doc.SupplierID != nil { if doc.SupplierID != nil {
if docSeen[doc.KandangID] == nil { if docSeen[doc.ProjectFlockKandangID] == nil {
docSeen[doc.KandangID] = make(map[uint]bool) docSeen[doc.ProjectFlockKandangID] = make(map[uint]bool)
} }
if !docSeen[doc.KandangID][*doc.SupplierID] { if !docSeen[doc.ProjectFlockKandangID][*doc.SupplierID] {
docSeen[doc.KandangID][*doc.SupplierID] = true docSeen[doc.ProjectFlockKandangID][*doc.SupplierID] = true
supplierName := "" supplierName := ""
if doc.SupplierName != nil { if doc.SupplierName != nil {
supplierName = *doc.SupplierName supplierName = *doc.SupplierName
@@ -250,19 +252,19 @@ func (r *hppPerKandangRepository) GetFeedOvkDocCostByPeriod(ctx context.Context,
supplierAlias = *doc.SupplierAlias supplierAlias = *doc.SupplierAlias
} }
docSuppliers = append(docSuppliers, HppPerKandangSupplierRow{ docSuppliers = append(docSuppliers, HppPerKandangSupplierRow{
KandangID: doc.KandangID, ProjectFlockKandangID: doc.ProjectFlockKandangID,
SupplierID: *doc.SupplierID, SupplierID: *doc.SupplierID,
SupplierName: supplierName, SupplierName: supplierName,
SupplierAlias: supplierAlias, SupplierAlias: supplierAlias,
Category: "DOC", Category: "DOC",
}) })
} }
} }
} }
budgetRows := make([]struct { budgetRows := make([]struct {
KandangID uint ProjectFlockKandangID uint
BudgetCost float64 BudgetCost float64
}, 0) }, 0)
pfkUsageSub := r.db. pfkUsageSub := r.db.
@@ -283,7 +285,7 @@ func (r *hppPerKandangRepository) GetFeedOvkDocCostByPeriod(ctx context.Context,
budgetQuery := r.db.WithContext(ctx). budgetQuery := r.db.WithContext(ctx).
Table("project_flock_kandangs AS pfk"). Table("project_flock_kandangs AS pfk").
Select(` Select(`
k.id AS kandang_id, pfk.id AS project_flock_kandang_id,
COALESCE(SUM((pb.qty * pb.price) * COALESCE(k_usage.kandang_usage_qty, 0) / NULLIF(p_usage.project_usage_qty, 0)), 0) AS budget_cost`). COALESCE(SUM((pb.qty * pb.price) * COALESCE(k_usage.kandang_usage_qty, 0) / NULLIF(p_usage.project_usage_qty, 0)), 0) AS budget_cost`).
Joins("JOIN kandangs AS k ON k.id = pfk.kandang_id"). Joins("JOIN kandangs AS k ON k.id = pfk.kandang_id").
Joins("JOIN locations AS loc ON loc.id = k.location_id"). Joins("JOIN locations AS loc ON loc.id = k.location_id").
@@ -291,7 +293,7 @@ func (r *hppPerKandangRepository) GetFeedOvkDocCostByPeriod(ctx context.Context,
Joins("LEFT JOIN (?) AS k_usage ON k_usage.project_flock_kandang_id = pfk.id", pfkUsageSub). Joins("LEFT JOIN (?) AS k_usage ON k_usage.project_flock_kandang_id = pfk.id", pfkUsageSub).
Joins("LEFT JOIN (?) AS p_usage ON p_usage.project_flock_id = pfk.project_flock_id", projectUsageSub). Joins("LEFT JOIN (?) AS p_usage ON p_usage.project_flock_id = pfk.project_flock_id", projectUsageSub).
Where("pfk.id IN (?)", projectFlockKandangIDs). Where("pfk.id IN (?)", projectFlockKandangIDs).
Group("k.id") Group("pfk.id")
// budgetQuery = applyLocationFilters(budgetQuery, areaIDs, locationIDs, kandangIDs) // budgetQuery = applyLocationFilters(budgetQuery, areaIDs, locationIDs, kandangIDs)
if err := budgetQuery.Scan(&budgetRows).Error; err != nil { if err := budgetQuery.Scan(&budgetRows).Error; err != nil {
@@ -299,33 +301,33 @@ func (r *hppPerKandangRepository) GetFeedOvkDocCostByPeriod(ctx context.Context,
} }
for _, budget := range budgetRows { for _, budget := range budgetRows {
entry, ok := costMap[budget.KandangID] entry, ok := costMap[budget.ProjectFlockKandangID]
if !ok { if !ok {
rows = append(rows, HppPerKandangCostRow{ rows = append(rows, HppPerKandangCostRow{
KandangID: budget.KandangID, ProjectFlockKandangID: budget.ProjectFlockKandangID,
}) })
entry = &rows[len(rows)-1] entry = &rows[len(rows)-1]
costMap[budget.KandangID] = entry costMap[budget.ProjectFlockKandangID] = entry
} }
entry.BudgetCost += budget.BudgetCost entry.BudgetCost += budget.BudgetCost
} }
expenseRows := make([]struct { expenseRows := make([]struct {
KandangID uint ProjectFlockKandangID uint
ExpenseCost float64 ExpenseCost float64
}, 0) }, 0)
expenseQuery := r.db.WithContext(ctx). expenseQuery := r.db.WithContext(ctx).
Table("project_flock_kandangs AS pfk"). Table("project_flock_kandangs AS pfk").
Select(` Select(`
k.id AS kandang_id, pfk.id AS project_flock_kandang_id,
COALESCE(SUM(er.qty * er.price), 0) AS expense_cost`). COALESCE(SUM(er.qty * er.price), 0) AS expense_cost`).
Joins("JOIN kandangs AS k ON k.id = pfk.kandang_id"). Joins("JOIN kandangs AS k ON k.id = pfk.kandang_id").
Joins("JOIN locations AS loc ON loc.id = k.location_id"). Joins("JOIN locations AS loc ON loc.id = k.location_id").
Joins("JOIN expense_nonstocks AS en ON en.project_flock_kandang_id = pfk.id"). Joins("JOIN expense_nonstocks AS en ON en.project_flock_kandang_id = pfk.id").
Joins("JOIN expense_realizations AS er ON er.expense_nonstock_id = en.id"). Joins("JOIN expense_realizations AS er ON er.expense_nonstock_id = en.id").
Where("pfk.id IN (?)", projectFlockKandangIDs). Where("pfk.id IN (?)", projectFlockKandangIDs).
Group("k.id") Group("pfk.id")
// expenseQuery = applyLocationFilters(expenseQuery, areaIDs, locationIDs, kandangIDs) // expenseQuery = applyLocationFilters(expenseQuery, areaIDs, locationIDs, kandangIDs)
if err := expenseQuery.Scan(&expenseRows).Error; err != nil { if err := expenseQuery.Scan(&expenseRows).Error; err != nil {
@@ -333,13 +335,13 @@ func (r *hppPerKandangRepository) GetFeedOvkDocCostByPeriod(ctx context.Context,
} }
for _, exp := range expenseRows { for _, exp := range expenseRows {
entry, ok := costMap[exp.KandangID] entry, ok := costMap[exp.ProjectFlockKandangID]
if !ok { if !ok {
rows = append(rows, HppPerKandangCostRow{ rows = append(rows, HppPerKandangCostRow{
KandangID: exp.KandangID, ProjectFlockKandangID: exp.ProjectFlockKandangID,
}) })
entry = &rows[len(rows)-1] entry = &rows[len(rows)-1]
costMap[exp.KandangID] = entry costMap[exp.ProjectFlockKandangID] = entry
} }
entry.ExpenseCost += exp.ExpenseCost entry.ExpenseCost += exp.ExpenseCost
} }
@@ -348,7 +350,7 @@ func (r *hppPerKandangRepository) GetFeedOvkDocCostByPeriod(ctx context.Context,
feedQuery := r.db.WithContext(ctx). feedQuery := r.db.WithContext(ctx).
Table("recordings AS r"). Table("recordings AS r").
Select("DISTINCT k.id AS kandang_id, s.id AS supplier_id, s.name AS supplier_name, s.alias AS supplier_alias"). Select("DISTINCT pfk.id AS project_flock_kandang_id, s.id AS supplier_id, s.name AS supplier_name, s.alias AS supplier_alias").
Joins("JOIN project_flock_kandangs AS pfk ON pfk.id = r.project_flock_kandangs_id"). Joins("JOIN project_flock_kandangs AS pfk ON pfk.id = r.project_flock_kandangs_id").
Joins("JOIN kandangs AS k ON k.id = pfk.kandang_id"). Joins("JOIN kandangs AS k ON k.id = pfk.kandang_id").
Joins("JOIN locations AS loc ON loc.id = k.location_id"). Joins("JOIN locations AS loc ON loc.id = k.location_id").
@@ -369,11 +371,11 @@ func (r *hppPerKandangRepository) GetFeedOvkDocCostByPeriod(ctx context.Context,
} }
for i := range feedSuppliers { for i := range feedSuppliers {
if _, exists := costMap[feedSuppliers[i].KandangID]; !exists { if _, exists := costMap[feedSuppliers[i].ProjectFlockKandangID]; !exists {
rows = append(rows, HppPerKandangCostRow{ rows = append(rows, HppPerKandangCostRow{
KandangID: feedSuppliers[i].KandangID, ProjectFlockKandangID: feedSuppliers[i].ProjectFlockKandangID,
}) })
costMap[feedSuppliers[i].KandangID] = &rows[len(rows)-1] costMap[feedSuppliers[i].ProjectFlockKandangID] = &rows[len(rows)-1]
} }
feedSuppliers[i].Category = "FEED" feedSuppliers[i].Category = "FEED"
} }
@@ -1380,7 +1380,7 @@ func (s *repportService) GetHppPerKandang(ctx *fiber.Ctx) (*dto.HppPerKandangRes
costMap := make(map[uint]HppCostAggregate, len(costRows)) costMap := make(map[uint]HppCostAggregate, len(costRows))
for _, row := range costRows { for _, row := range costRows {
costMap[row.KandangID] = HppCostAggregate{ costMap[row.ProjectFlockKandangID] = HppCostAggregate{
FeedCost: row.FeedCost, FeedCost: row.FeedCost,
OvkCost: row.OvkCost, OvkCost: row.OvkCost,
DocCost: row.DocCost, DocCost: row.DocCost,
@@ -1409,15 +1409,15 @@ func (s *repportService) GetHppPerKandang(ctx *fiber.Ctx) (*dto.HppPerKandangRes
category = "DOC" category = "DOC"
} }
if seen[sup.KandangID] == nil { if seen[sup.ProjectFlockKandangID] == nil {
seen[sup.KandangID] = make(map[uint]bool) seen[sup.ProjectFlockKandangID] = make(map[uint]bool)
} }
if seen[sup.KandangID][sup.SupplierID] { if seen[sup.ProjectFlockKandangID][sup.SupplierID] {
continue continue
} }
seen[sup.KandangID][sup.SupplierID] = true seen[sup.ProjectFlockKandangID][sup.SupplierID] = true
targetMap[sup.KandangID] = append(targetMap[sup.KandangID], dto.HppPerKandangSupplierDTO{ targetMap[sup.ProjectFlockKandangID] = append(targetMap[sup.ProjectFlockKandangID], dto.HppPerKandangSupplierDTO{
ID: int64(sup.SupplierID), ID: int64(sup.SupplierID),
Name: sup.SupplierName, Name: sup.SupplierName,
Alias: sup.SupplierAlias, Alias: sup.SupplierAlias,
@@ -1507,7 +1507,7 @@ func (s *repportService) GetHppPerKandang(ctx *fiber.Ctx) (*dto.HppPerKandangRes
rangeKey := weightRangeKey{Min: weightMin, Max: weightMax} rangeKey := weightRangeKey{Min: weightMin, Max: weightMax}
// rowBirds := int64(math.Round(birdsFloat)) // rowBirds := int64(math.Round(birdsFloat))
costEntry := costMap[row.KandangID] costEntry := costMap[row.ProjectFlockKandangID]
totalCost := costEntry.FeedCost + costEntry.OvkCost + costEntry.DocCost + costEntry.BudgetCost + costEntry.ExpenseCost totalCost := costEntry.FeedCost + costEntry.OvkCost + costEntry.DocCost + costEntry.BudgetCost + costEntry.ExpenseCost
// hppRp := 0.0 // hppRp := 0.0
// if weightFloat > 0 { // if weightFloat > 0 {
@@ -1526,8 +1526,10 @@ func (s *repportService) GetHppPerKandang(ctx *fiber.Ctx) (*dto.HppPerKandangRes
avgDocPrice = int64(math.Round(costEntry.DocCost / costEntry.DocQty)) avgDocPrice = int64(math.Round(costEntry.DocCost / costEntry.DocQty))
} }
nameWithPeriod := fmt.Sprintf("%s Period %d", row.KandangName, row.ProjectFlockPeriod)
dataRows = append(dataRows, dto.HppPerKandangRowDTO{ dataRows = append(dataRows, dto.HppPerKandangRowDTO{
ID: int(row.KandangID), ID: int(row.ProjectFlockKandangID),
Kandang: dto.HppPerKandangRowKandangDTO{ Kandang: dto.HppPerKandangRowKandangDTO{
ID: int64(row.KandangID), ID: int64(row.KandangID),
Name: row.KandangName, Name: row.KandangName,
@@ -1545,11 +1547,12 @@ func (s *repportService) GetHppPerKandang(ctx *fiber.Ctx) (*dto.HppPerKandangRes
WeightMin: weightMin, WeightMin: weightMin,
WeightMax: weightMax, WeightMax: weightMax,
}, },
AvgWeightKg: avgWeight, AvgWeightKg: avgWeight,
NameWithPeriode: nameWithPeriod,
// FeedCostRp: costEntry.FeedCost, // FeedCostRp: costEntry.FeedCost,
// OvkCostRp: costEntry.OvkCost, // OvkCostRp: costEntry.OvkCost,
DocSuppliers: docSupplierMap[row.KandangID], DocSuppliers: docSupplierMap[row.ProjectFlockKandangID],
FeedSuppliers: feedSupplierMap[row.KandangID], FeedSuppliers: feedSupplierMap[row.ProjectFlockKandangID],
EggProductionPieces: int64(math.Round(eggPiecesFloatRemaining)), EggProductionPieces: int64(math.Round(eggPiecesFloatRemaining)),
EggProductionKg: eggRemainingWeightFloatRemaining, EggProductionKg: eggRemainingWeightFloatRemaining,
// EggProductionTotalWeightKg: eggWeightFloat, // EggProductionTotalWeightKg: eggWeightFloat,
@@ -1603,12 +1606,12 @@ func (s *repportService) GetHppPerKandang(ctx *fiber.Ctx) (*dto.HppPerKandangRes
// rangeAgg.RemainingWeightKg += row.RemainingChickenWeight // rangeAgg.RemainingWeightKg += row.RemainingChickenWeight
rangeAgg.AvgWeightSum += avgWeight rangeAgg.AvgWeightSum += avgWeight
rangeAgg.AvgWeightCount++ rangeAgg.AvgWeightCount++
for _, supplier := range feedSupplierMap[row.KandangID] { for _, supplier := range feedSupplierMap[row.ProjectFlockKandangID] {
if _, ok := rangeAgg.FeedSuppliers[supplier.ID]; !ok { if _, ok := rangeAgg.FeedSuppliers[supplier.ID]; !ok {
rangeAgg.FeedSuppliers[supplier.ID] = supplier rangeAgg.FeedSuppliers[supplier.ID] = supplier
} }
} }
for _, supplier := range docSupplierMap[row.KandangID] { for _, supplier := range docSupplierMap[row.ProjectFlockKandangID] {
if _, ok := rangeAgg.DocSuppliers[supplier.ID]; !ok { if _, ok := rangeAgg.DocSuppliers[supplier.ID]; !ok {
rangeAgg.DocSuppliers[supplier.ID] = supplier rangeAgg.DocSuppliers[supplier.ID] = supplier
} }