mirror of
https://gitlab.com/mbugroup/lti-api.git
synced 2026-05-20 13:31:56 +00:00
feat[BE-378]:Create API Get All HPP Harian Kandang
This commit is contained in:
@@ -0,0 +1,361 @@
|
||||
package repositories
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
||||
"gitlab.com/mbugroup/lti-api.git/internal/utils"
|
||||
"gitlab.com/mbugroup/lti-api.git/internal/utils/fifo"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type HppPerKandangRow struct {
|
||||
KandangID uint
|
||||
KandangName string
|
||||
KandangStatus string
|
||||
LocationID uint
|
||||
LocationName string
|
||||
PicID uint
|
||||
PicName string
|
||||
RemainingChickenBirds float64
|
||||
RemainingChickenWeight float64
|
||||
EggProductionWeightKg float64
|
||||
EggProductionPieces float64
|
||||
}
|
||||
|
||||
type HppPerKandangCostRow struct {
|
||||
KandangID uint
|
||||
FeedCost float64
|
||||
OvkCost float64
|
||||
DocCost float64
|
||||
DocQty float64
|
||||
BudgetCost float64
|
||||
ExpenseCost float64
|
||||
}
|
||||
|
||||
type HppPerKandangSupplierRow struct {
|
||||
KandangID uint
|
||||
SupplierID uint
|
||||
SupplierName string
|
||||
SupplierAlias string
|
||||
Category string
|
||||
}
|
||||
|
||||
type HppPerKandangRepository interface {
|
||||
GetRowsByPeriod(ctx context.Context, start, end time.Time, areaIDs, locationIDs, kandangIDs []int64) ([]HppPerKandangRow, error)
|
||||
GetFeedOvkDocCostByPeriod(ctx context.Context, start, end time.Time, areaIDs, locationIDs, kandangIDs []int64) ([]HppPerKandangCostRow, []HppPerKandangSupplierRow, error)
|
||||
}
|
||||
|
||||
type hppPerKandangRepository struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
func NewHppPerKandangRepository(db *gorm.DB) HppPerKandangRepository {
|
||||
return &hppPerKandangRepository{db: db}
|
||||
}
|
||||
|
||||
func (r *hppPerKandangRepository) GetRowsByPeriod(ctx context.Context, start, end time.Time, areaIDs, locationIDs, kandangIDs []int64) ([]HppPerKandangRow, error) {
|
||||
var rows []HppPerKandangRow
|
||||
|
||||
query := r.db.WithContext(ctx).
|
||||
Table("recordings AS r").
|
||||
Select(`
|
||||
k.id AS kandang_id,
|
||||
k.name AS kandang_name,
|
||||
k.status AS kandang_status,
|
||||
loc.id AS location_id,
|
||||
loc.name AS location_name,
|
||||
pic.id AS pic_id,
|
||||
pic.name AS pic_name,
|
||||
COALESCE(MAX(r.total_chick_qty), 0) AS remaining_chicken_birds,
|
||||
COALESCE(SUM(rbw.total_weight), 0) AS remaining_chicken_weight,
|
||||
COALESCE(SUM(re.weight), 0) AS egg_production_weight_kg,
|
||||
COALESCE(SUM(re.qty), 0) AS egg_production_pieces`).
|
||||
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 locations AS loc ON loc.id = k.location_id").
|
||||
Joins("JOIN users AS pic ON pic.id = k.pic_id").
|
||||
Joins("LEFT JOIN recording_bws AS rbw ON rbw.recording_id = r.id").
|
||||
Joins("LEFT JOIN recording_eggs AS re ON re.recording_id = r.id").
|
||||
Where("r.record_datetime >= ? AND r.record_datetime < ?", start, end).
|
||||
Where("r.deleted_at IS NULL")
|
||||
|
||||
query = applyLocationFilters(query, areaIDs, locationIDs, kandangIDs)
|
||||
|
||||
query = query.Group("k.id, k.name, k.status, loc.id, loc.name, pic.id, pic.name").
|
||||
Order("k.id ASC")
|
||||
|
||||
if err := query.Scan(&rows).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return rows, nil
|
||||
}
|
||||
|
||||
func (r *hppPerKandangRepository) GetFeedOvkDocCostByPeriod(ctx context.Context, start, end time.Time, areaIDs, locationIDs, kandangIDs []int64) ([]HppPerKandangCostRow, []HppPerKandangSupplierRow, error) {
|
||||
var rows []HppPerKandangCostRow
|
||||
|
||||
recordingPfk := r.db.WithContext(ctx).
|
||||
Table("recordings AS r").
|
||||
Select("DISTINCT pfk.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 locations AS loc ON loc.id = k.location_id").
|
||||
Where("r.record_datetime >= ? AND r.record_datetime < ?", start, end).
|
||||
Where("r.deleted_at IS NULL")
|
||||
recordingPfk = applyLocationFilters(recordingPfk, areaIDs, locationIDs, kandangIDs)
|
||||
|
||||
purchaseStockableKey := fifo.StockableKey("PURCHASE_ITEMS").String()
|
||||
transferStockableKey := fifo.StockableKey("STOCK_TRANSFER_DETAILS").String()
|
||||
|
||||
query := r.db.WithContext(ctx).
|
||||
Table("recordings AS r").
|
||||
Select(`
|
||||
k.id AS kandang_id,
|
||||
COALESCE(SUM(CASE
|
||||
WHEN f.name = ? THEN COALESCE(sa.qty, 0) * COALESCE(pi.price, 0)
|
||||
WHEN sa.stockable_type = ? AND tf.name = ? THEN COALESCE(std.quantity, 0) * COALESCE(tpi.price, 0)
|
||||
ELSE 0
|
||||
END), 0) AS feed_cost,
|
||||
COALESCE(SUM(CASE
|
||||
WHEN f.name = ? THEN COALESCE(sa.qty, 0) * COALESCE(pi.price, 0)
|
||||
WHEN sa.stockable_type = ? AND tf.name = ? THEN COALESCE(std.quantity, 0) * COALESCE(tpi.price, 0)
|
||||
ELSE 0
|
||||
END), 0) AS ovk_cost`,
|
||||
utils.FlagPakan, transferStockableKey, utils.FlagPakan,
|
||||
utils.FlagOVK, transferStockableKey, utils.FlagOVK).
|
||||
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 locations AS loc ON loc.id = k.location_id").
|
||||
Joins("LEFT JOIN recording_stocks AS rs ON rs.recording_id = r.id").
|
||||
Joins("LEFT JOIN stock_allocations AS sa ON sa.usable_type = ? AND sa.usable_id = rs.id AND sa.status = ?", fifo.UsableKeyRecordingStock.String(), entity.StockAllocationStatusActive).
|
||||
Joins("LEFT JOIN purchase_items AS pi ON pi.id = sa.stockable_id AND sa.stockable_type = ?", purchaseStockableKey).
|
||||
Joins("LEFT JOIN stock_transfer_details AS std ON std.id = sa.stockable_id AND sa.stockable_type = ?", transferStockableKey).
|
||||
Joins("LEFT JOIN stock_transfers AS st ON st.id = std.stock_transfer_id").
|
||||
Joins("LEFT JOIN purchase_items AS tpi ON tpi.product_id = std.product_id AND tpi.warehouse_id = st.from_warehouse_id").
|
||||
Joins("LEFT JOIN flags AS f ON f.flagable_id = pi.product_id AND f.flagable_type = ?", entity.FlagableTypeProduct).
|
||||
Joins("LEFT JOIN flags AS tf ON tf.flagable_id = std.product_id AND tf.flagable_type = ?", entity.FlagableTypeProduct).
|
||||
Where("r.project_flock_kandangs_id IN (?)", recordingPfk.Session(&gorm.Session{NewDB: true})).
|
||||
Where("r.record_datetime >= ? AND r.record_datetime < ?", start, end).
|
||||
Where("r.deleted_at IS NULL")
|
||||
|
||||
query = applyLocationFilters(query, areaIDs, locationIDs, kandangIDs)
|
||||
|
||||
query = query.Group("k.id").Order("k.id ASC")
|
||||
|
||||
if err := query.Scan(&rows).Error; err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
docRows := make([]struct {
|
||||
KandangID uint
|
||||
DocCost float64
|
||||
DocQty float64
|
||||
SupplierID *uint
|
||||
SupplierName *string
|
||||
SupplierAlias *string
|
||||
}, 0)
|
||||
|
||||
docQuery := r.db.WithContext(ctx).
|
||||
Table("project_chickins AS pc").
|
||||
Select(`
|
||||
pfk.kandang_id AS kandang_id,
|
||||
COALESCE(SUM(pc.usage_qty * COALESCE(pi.price, 0)), 0) AS doc_cost,
|
||||
COALESCE(SUM(pc.usage_qty), 0) AS doc_qty,
|
||||
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 = pc.project_flock_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("LEFT JOIN purchase_items AS pi ON pi.product_warehouse_id = pc.product_warehouse_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").
|
||||
Where("pc.project_flock_kandang_id IN (?)", recordingPfk.Session(&gorm.Session{NewDB: true})).
|
||||
Group("pfk.kandang_id, s.id, s.name, s.alias")
|
||||
docQuery = applyLocationFilters(docQuery, areaIDs, locationIDs, kandangIDs)
|
||||
|
||||
if err := docQuery.Scan(&docRows).Error; err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
costMap := make(map[uint]*HppPerKandangCostRow, len(rows))
|
||||
for i := range rows {
|
||||
row := rows[i]
|
||||
costMap[row.KandangID] = &rows[i]
|
||||
}
|
||||
|
||||
docSuppliers := make([]HppPerKandangSupplierRow, 0)
|
||||
docSeen := make(map[uint]map[uint]bool)
|
||||
for _, doc := range docRows {
|
||||
entry, ok := costMap[doc.KandangID]
|
||||
if !ok {
|
||||
rows = append(rows, HppPerKandangCostRow{
|
||||
KandangID: doc.KandangID,
|
||||
})
|
||||
entry = &rows[len(rows)-1]
|
||||
costMap[doc.KandangID] = entry
|
||||
}
|
||||
entry.DocCost += doc.DocCost
|
||||
entry.DocQty += doc.DocQty
|
||||
if doc.SupplierID != nil {
|
||||
if docSeen[doc.KandangID] == nil {
|
||||
docSeen[doc.KandangID] = make(map[uint]bool)
|
||||
}
|
||||
if !docSeen[doc.KandangID][*doc.SupplierID] {
|
||||
docSeen[doc.KandangID][*doc.SupplierID] = true
|
||||
supplierName := ""
|
||||
if doc.SupplierName != nil {
|
||||
supplierName = *doc.SupplierName
|
||||
}
|
||||
supplierAlias := ""
|
||||
if doc.SupplierAlias != nil {
|
||||
supplierAlias = *doc.SupplierAlias
|
||||
}
|
||||
docSuppliers = append(docSuppliers, HppPerKandangSupplierRow{
|
||||
KandangID: doc.KandangID,
|
||||
SupplierID: *doc.SupplierID,
|
||||
SupplierName: supplierName,
|
||||
SupplierAlias: supplierAlias,
|
||||
Category: "DOC",
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
budgetRows := make([]struct {
|
||||
KandangID uint
|
||||
BudgetCost float64
|
||||
}, 0)
|
||||
|
||||
pfkUsageSub := r.db.
|
||||
Table("project_chickins AS pc").
|
||||
Select(`
|
||||
pc.project_flock_kandang_id,
|
||||
SUM(pc.usage_qty) AS kandang_usage_qty`).
|
||||
Group("pc.project_flock_kandang_id")
|
||||
|
||||
projectUsageSub := r.db.
|
||||
Table("project_chickins AS pc").
|
||||
Select(`
|
||||
pfk.project_flock_id,
|
||||
SUM(pc.usage_qty) AS project_usage_qty`).
|
||||
Joins("JOIN project_flock_kandangs AS pfk ON pfk.id = pc.project_flock_kandang_id").
|
||||
Group("pfk.project_flock_id")
|
||||
|
||||
budgetQuery := r.db.WithContext(ctx).
|
||||
Table("project_flock_kandangs AS pfk").
|
||||
Select(`
|
||||
k.id AS 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`).
|
||||
Joins("JOIN kandangs AS k ON k.id = pfk.kandang_id").
|
||||
Joins("JOIN locations AS loc ON loc.id = k.location_id").
|
||||
Joins("JOIN project_budgets AS pb ON pb.project_flock_id = pfk.project_flock_id").
|
||||
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).
|
||||
Where("pfk.id IN (?)", recordingPfk.Session(&gorm.Session{NewDB: true})).
|
||||
Group("k.id")
|
||||
budgetQuery = applyLocationFilters(budgetQuery, areaIDs, locationIDs, kandangIDs)
|
||||
|
||||
if err := budgetQuery.Scan(&budgetRows).Error; err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
for _, budget := range budgetRows {
|
||||
entry, ok := costMap[budget.KandangID]
|
||||
if !ok {
|
||||
rows = append(rows, HppPerKandangCostRow{
|
||||
KandangID: budget.KandangID,
|
||||
})
|
||||
entry = &rows[len(rows)-1]
|
||||
costMap[budget.KandangID] = entry
|
||||
}
|
||||
entry.BudgetCost += budget.BudgetCost
|
||||
}
|
||||
|
||||
expenseRows := make([]struct {
|
||||
KandangID uint
|
||||
ExpenseCost float64
|
||||
}, 0)
|
||||
|
||||
expenseQuery := r.db.WithContext(ctx).
|
||||
Table("project_flock_kandangs AS pfk").
|
||||
Select(`
|
||||
k.id AS kandang_id,
|
||||
COALESCE(SUM(er.qty * er.price), 0) AS expense_cost`).
|
||||
Joins("JOIN kandangs AS k ON k.id = pfk.kandang_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_realizations AS er ON er.expense_nonstock_id = en.id").
|
||||
Where("pfk.id IN (?)", recordingPfk.Session(&gorm.Session{NewDB: true})).
|
||||
Group("k.id")
|
||||
expenseQuery = applyLocationFilters(expenseQuery, areaIDs, locationIDs, kandangIDs)
|
||||
|
||||
if err := expenseQuery.Scan(&expenseRows).Error; err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
for _, exp := range expenseRows {
|
||||
entry, ok := costMap[exp.KandangID]
|
||||
if !ok {
|
||||
rows = append(rows, HppPerKandangCostRow{
|
||||
KandangID: exp.KandangID,
|
||||
})
|
||||
entry = &rows[len(rows)-1]
|
||||
costMap[exp.KandangID] = entry
|
||||
}
|
||||
entry.ExpenseCost += exp.ExpenseCost
|
||||
}
|
||||
|
||||
feedSuppliers := make([]HppPerKandangSupplierRow, 0)
|
||||
|
||||
feedQuery := r.db.WithContext(ctx).
|
||||
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").
|
||||
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 locations AS loc ON loc.id = k.location_id").
|
||||
Joins("LEFT JOIN recording_stocks AS rs ON rs.recording_id = r.id").
|
||||
Joins("LEFT JOIN stock_allocations AS sa ON sa.usable_type = ? AND sa.usable_id = rs.id AND sa.status = ?", fifo.UsableKeyRecordingStock.String(), entity.StockAllocationStatusActive).
|
||||
Joins("LEFT JOIN purchase_items AS pi ON pi.id = sa.stockable_id AND sa.stockable_type = ?", purchaseStockableKey).
|
||||
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 flags AS f ON f.flagable_id = pi.product_id AND f.flagable_type = ?", entity.FlagableTypeProduct).
|
||||
Where("f.name IN ?", []utils.FlagType{utils.FlagPakan, utils.FlagOVK}).
|
||||
Where("r.project_flock_kandangs_id IN (?)", recordingPfk.Session(&gorm.Session{NewDB: true})).
|
||||
Where("r.record_datetime >= ? AND r.record_datetime < ?", start, end).
|
||||
Where("r.deleted_at IS NULL")
|
||||
feedQuery = applyLocationFilters(feedQuery, areaIDs, locationIDs, kandangIDs)
|
||||
|
||||
if err := feedQuery.Scan(&feedSuppliers).Error; err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
for i := range feedSuppliers {
|
||||
if _, exists := costMap[feedSuppliers[i].KandangID]; !exists {
|
||||
rows = append(rows, HppPerKandangCostRow{
|
||||
KandangID: feedSuppliers[i].KandangID,
|
||||
})
|
||||
costMap[feedSuppliers[i].KandangID] = &rows[len(rows)-1]
|
||||
}
|
||||
feedSuppliers[i].Category = "FEED"
|
||||
}
|
||||
|
||||
supplierRows := append(docSuppliers, feedSuppliers...)
|
||||
|
||||
return rows, supplierRows, nil
|
||||
}
|
||||
|
||||
func applyLocationFilters(query *gorm.DB, areaIDs, locationIDs, kandangIDs []int64) *gorm.DB {
|
||||
if len(areaIDs) > 0 {
|
||||
query = query.Where("loc.area_id IN ?", areaIDs)
|
||||
}
|
||||
if len(locationIDs) > 0 {
|
||||
query = query.Where("k.location_id IN ?", locationIDs)
|
||||
}
|
||||
if len(kandangIDs) > 0 {
|
||||
query = query.Where("k.id IN ?", kandangIDs)
|
||||
}
|
||||
return query
|
||||
}
|
||||
Reference in New Issue
Block a user