package repository 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 HppCostRepository interface { GetProjectFlockKandangIDs(ctx context.Context, projectFlockId uint) ([]uint, error) GetDocCost(ctx context.Context, projectFlockKandangIDs []uint) (float64, error) GetBudgetCostByProjectFlockId(ctx context.Context, projectFlockId uint) (float64, error) GetExpedisionCost(ctx context.Context, projectFlockKandangIDs []uint) (float64, error) GetFeedUsageCost(ctx context.Context, projectFlockKandangIDs []uint, date *time.Time) (float64, error) GetOvkUsageCost(ctx context.Context, projectFlockKandangIDs []uint, date *time.Time) (float64, error) GetTotalPopulation(ctx context.Context, projectFlockKandangIDs []uint) (float64, error) GetPulletCost(ctx context.Context, projectFlockKandangId uint) (float64, error) GetEggProduksiPiecesAndWeightKgByProjectFlockKandangIds(ctx context.Context, projectFlockKandangIDs []uint, date *time.Time) (float64, float64, error) GetEggTerjualPiecesAndWeightKgByProjectFlockKandangIds(ctx context.Context, projectFlockKandangIDs []uint, startDate *time.Time, endDate *time.Time) (float64, float64, error) GetProjectFlockIDByProjectFlockKandangID(ctx context.Context, projectFlockKandangId uint) (uint, error) GetTransferSourceSummary(ctx context.Context, projectFlockKandangId uint) (uint, float64, error) } type HppRepositoryImpl struct { db *gorm.DB } func NewHppCostRepository(db *gorm.DB) HppCostRepository { return &HppRepositoryImpl{db: db} } func (r *HppRepositoryImpl) GetProjectFlockKandangIDs(ctx context.Context, projectFlockId uint) ([]uint, error) { var ids []uint err := r.db.WithContext(ctx). Table("project_flock_kandangs"). Select("id"). Where("project_flock_id = ?", projectFlockId). Scan(&ids).Error if err != nil { return nil, err } return ids, nil } func (r *HppRepositoryImpl) GetDocCost(ctx context.Context, projectFlockKandangIDs []uint) (float64, error) { var total float64 err := r.db.WithContext(ctx). Table("project_chickins AS pc"). Select("COALESCE(SUM(sa.qty * COALESCE(pi.price, 0)), 0)"). Joins("JOIN stock_allocations AS sa ON sa.usable_type = ? AND sa.usable_id = pc.id AND sa.stockable_type = ? AND sa.status = ? AND sa.allocation_purpose = ?", fifo.UsableKeyProjectChickin.String(), fifo.StockableKeyPurchaseItems.String(), entity.StockAllocationStatusActive, entity.StockAllocationPurposeTraceChickin). Joins("JOIN purchase_items AS pi ON pi.id = sa.stockable_id"). Where("pc.project_flock_kandang_id IN (?)", projectFlockKandangIDs). Scan(&total).Error if err != nil { return 0, err } return total, nil } func (r *HppRepositoryImpl) GetBudgetCostByProjectFlockId(ctx context.Context, projectFlockId uint) (float64, error) { var total float64 err := r.db.WithContext(ctx). Table("project_budgets AS pb"). Select("COALESCE(SUM(pb.qty * pb.price), 0)"). Where("pb.project_flock_id = ?", projectFlockId). Scan(&total).Error if err != nil { return 0, err } return total, nil } func (r *HppRepositoryImpl) GetExpedisionCost(ctx context.Context, projectFlockKandangIDs []uint) (float64, error) { var total float64 err := r.db.WithContext(ctx). Table("expense_nonstocks AS en"). Select("COALESCE(SUM(er.qty * er.price), 0)"). Joins("JOIN expense_realizations AS er ON er.expense_nonstock_id = en.id"). Joins("JOIN flags AS f ON f.flagable_id = en.nonstock_id AND f.flagable_type = ?", entity.FlagableTypeNonstock). Where("en.project_flock_kandang_id IN (?)", projectFlockKandangIDs). Where("f.name = ?", utils.FlagEkspedisi). Scan(&total).Error if err != nil { return 0, err } return total, nil } func (r *HppRepositoryImpl) GetFeedUsageCost(ctx context.Context, projectFlockKandangIDs []uint, date *time.Time) (float64, error) { if date == nil { now := time.Now() date = &now } var total float64 err := r.db.WithContext(ctx). Table("recordings AS r"). Select("COALESCE(SUM(sa.qty * COALESCE(pi.price, 0)), 0)"). Joins("JOIN recording_stocks AS rs ON rs.recording_id = r.id"). Joins("JOIN product_warehouses AS pw ON pw.id = rs.product_warehouse_id"). Joins("JOIN flags AS f ON f.flagable_id = pw.product_id AND f.flagable_type = ?", entity.FlagableTypeProduct). Joins("JOIN stock_allocations AS sa ON sa.usable_type = ? AND sa.usable_id = rs.id AND sa.stockable_type = ? AND sa.status = ? AND sa.allocation_purpose = ?", fifo.UsableKeyRecordingStock.String(), fifo.StockableKeyPurchaseItems.String(), entity.StockAllocationStatusActive, entity.StockAllocationPurposeConsume). Joins("JOIN purchase_items AS pi ON pi.id = sa.stockable_id"). Where("r.project_flock_kandangs_id IN (?)", projectFlockKandangIDs). Where("r.record_datetime <= ?", *date). Where("f.name = ?", utils.FlagPakan). Scan(&total).Error if err != nil { return 0, err } return total, nil } func (r *HppRepositoryImpl) GetOvkUsageCost(ctx context.Context, projectFlockKandangIDs []uint, date *time.Time) (float64, error) { if date == nil { now := time.Now() date = &now } flags := []utils.FlagType{ utils.FlagOVK, utils.FlagObat, utils.FlagVitamin, utils.FlagKimia, } var total float64 err := r.db.WithContext(ctx). Table("recordings AS r"). Select("COALESCE(SUM(sa.qty * COALESCE(pi.price, 0)), 0)"). Joins("JOIN recording_stocks AS rs ON rs.recording_id = r.id"). Joins("JOIN product_warehouses AS pw ON pw.id = rs.product_warehouse_id"). Joins("JOIN stock_allocations AS sa ON sa.usable_type = ? AND sa.usable_id = rs.id AND sa.stockable_type = ? AND sa.status = ? AND sa.allocation_purpose = ?", fifo.UsableKeyRecordingStock.String(), fifo.StockableKeyPurchaseItems.String(), entity.StockAllocationStatusActive, entity.StockAllocationPurposeConsume). Joins("JOIN purchase_items AS pi ON pi.id = sa.stockable_id"). Where("r.project_flock_kandangs_id IN (?)", projectFlockKandangIDs). Where("r.record_datetime <= ?", *date). Where("EXISTS (SELECT 1 FROM flags f WHERE f.flagable_id = pw.product_id AND f.flagable_type = ? AND f.name IN ?)", entity.FlagableTypeProduct, flags). Scan(&total).Error if err != nil { return 0, err } return total, nil } func (r *HppRepositoryImpl) GetTotalPopulation(ctx context.Context, projectFlockKandangIDs []uint) (float64, error) { var total float64 err := r.db.WithContext(ctx). Table("project_chickins AS pc"). Select("COALESCE(SUM(pc.usage_qty), 0)"). Where("pc.project_flock_kandang_id IN (?)", projectFlockKandangIDs). Scan(&total).Error if err != nil { return 0, err } return total, nil } func (r *HppRepositoryImpl) GetPulletCost(ctx context.Context, projectFlockKandangId uint) (float64, error) { stockablePurchase := fifo.StockableKeyPurchaseItems.String() stockableTransferIn := fifo.StockableKeyStockTransferIn.String() usableProjectChickin := fifo.UsableKeyProjectChickin.String() var total float64 err := r.db.WithContext(ctx). Table("project_chickins AS pc"). Select(` COALESCE(SUM(sa.qty * CASE WHEN sa.stockable_type = ? THEN COALESCE(pi.price, 0) WHEN sa.stockable_type = ? THEN COALESCE(tpi.price, 0) ELSE 0 END), 0)`, stockablePurchase, stockableTransferIn). Joins("JOIN stock_allocations AS sa ON sa.usable_type = ? AND sa.usable_id = pc.id AND sa.status = ? AND sa.allocation_purpose = ?", usableProjectChickin, entity.StockAllocationStatusActive, entity.StockAllocationPurposeTraceChickin). Joins("LEFT JOIN purchase_items AS pi ON pi.id = sa.stockable_id AND sa.stockable_type = ?", stockablePurchase). Joins("LEFT JOIN stock_allocations AS tsa ON tsa.usable_type = ? AND tsa.usable_id = sa.stockable_id AND sa.stockable_type = ? AND tsa.stockable_type = ? AND tsa.status = ? AND tsa.allocation_purpose = ?", stockableTransferIn, stockableTransferIn, stockablePurchase, entity.StockAllocationStatusActive, entity.StockAllocationPurposeConsume). Joins("LEFT JOIN purchase_items AS tpi ON tpi.id = tsa.stockable_id"). Where("pc.project_flock_kandang_id = ?", projectFlockKandangId). Scan(&total).Error if err != nil { return 0, err } return total, nil } func (r *HppRepositoryImpl) GetEggProduksiPiecesAndWeightKgByProjectFlockKandangIds(ctx context.Context, projectFlockKandangIDs []uint, date *time.Time) (float64, float64, error) { if date == nil { now := time.Now() date = &now } var totals struct { TotalPieces float64 TotalWeightKg float64 } err := r.db.WithContext(ctx). Table("recordings AS r"). Select("COALESCE(SUM(re.qty), 0) AS total_pieces, COALESCE(SUM(re.weight), 0)AS total_weight_kg"). Joins("JOIN recording_eggs AS re ON re.recording_id = r.id"). Where("r.project_flock_kandangs_id IN (?)", projectFlockKandangIDs). Where("r.record_datetime <= ?", *date). Scan(&totals).Error if err != nil { return 0, 0, err } return totals.TotalPieces, totals.TotalWeightKg, nil } func (r *HppRepositoryImpl) GetEggTerjualPiecesAndWeightKgByProjectFlockKandangIds( ctx context.Context, projectFlockKandangIDs []uint, startDate *time.Time, endDate *time.Time, ) (float64, float64, error) { if endDate == nil { now := time.Now() endDate = &now } type subResult struct { UsableID uint MdpUsageQty float64 MdpWeight float64 } subQuery := r.db.WithContext(ctx). Table("recordings AS r"). Select(` DISTINCT sa.usable_id, mdp.usage_qty AS mdp_usage_qty, mdp.total_weight AS mdp_weight `). Joins("JOIN recording_eggs re ON re.recording_id = r.id"). Joins( "JOIN stock_allocations sa ON sa.stockable_type = ? AND sa.stockable_id = re.id AND sa.usable_type = ? AND sa.status = ? AND sa.allocation_purpose = ?", fifo.StockableKeyRecordingEgg.String(), fifo.UsableKeyMarketingDelivery.String(), entity.StockAllocationStatusActive, entity.StockAllocationPurposeConsume, ). Joins("JOIN marketing_delivery_products mdp ON mdp.id = sa.usable_id"). Where("r.project_flock_kandangs_id IN (?)", projectFlockKandangIDs). Where("r.record_datetime <= ?", *endDate). Where("mdp.delivery_date <= ?", *startDate) var totals struct { TotalPieces float64 TotalWeight float64 } err := r.db.WithContext(ctx). Table("(?) AS x", subQuery). Select(` COALESCE(SUM(x.mdp_usage_qty), 0) AS total_pieces, COALESCE(SUM(x.mdp_weight), 0) AS total_weight `). Scan(&totals).Error if err != nil { return 0, 0, err } return totals.TotalPieces, totals.TotalWeight, nil } func (r *HppRepositoryImpl) GetProjectFlockIDByProjectFlockKandangID(ctx context.Context, projectFlockKandangId uint) (uint, error) { var projectFlockID uint err := r.db.WithContext(ctx). Table("project_flock_kandangs"). Select("project_flock_id"). Where("id = ?", projectFlockKandangId). Scan(&projectFlockID).Error if err != nil { return 0, err } return projectFlockID, nil } func (r *HppRepositoryImpl) GetTransferSourceSummary(ctx context.Context, projectFlockKandangId uint) (uint, float64, error) { var summary struct { ProjectFlockID uint TotalQty float64 } err := r.db.WithContext(ctx). Table("laying_transfer_targets AS ltt"). Select("lt.from_project_flock_id AS project_flock_id, COALESCE(SUM(ltt.total_qty), 0) AS total_qty"). Joins("JOIN laying_transfers AS lt ON lt.id = ltt.laying_transfer_id"). Where("ltt.target_project_flock_kandang_id = ?", projectFlockKandangId). Group("lt.from_project_flock_id"). Scan(&summary).Error if err != nil { return 0, 0, err } return summary.ProjectFlockID, summary.TotalQty, nil }