Files
lti-api/internal/common/repository/common.hppv2.repository.go
T

432 lines
14 KiB
Go

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 HppV2CostRepository interface {
GetProjectFlockKandangIDs(ctx context.Context, projectFlockId uint) ([]uint, error)
GetFeedUsageCost(ctx context.Context, projectFlockKandangIDs []uint, date *time.Time) (float64, error)
GetTotalPopulation(ctx context.Context, projectFlockKandangIDs []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)
GetTransferSourceSummary(ctx context.Context, projectFlockKandangId uint) (uint, float64, error)
}
type HppV2RepositoryImpl struct {
db *gorm.DB
}
func NewHppV2CostRepository(db *gorm.DB) HppV2CostRepository {
return &HppV2RepositoryImpl{db: db}
}
func (r *HppV2RepositoryImpl) 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 *HppV2RepositoryImpl) GetFeedUsageCost(ctx context.Context, projectFlockKandangIDs []uint, date *time.Time) (float64, error) {
if date == nil {
now := time.Now()
date = &now
}
stockablePurchase := fifo.StockableKeyPurchaseItems.String()
stockableAdjustment := fifo.StockableKeyAdjustmentIn.String()
usableRecordingStock := fifo.UsableKeyRecordingStock.String()
var total float64
err := r.db.WithContext(ctx).
Table("recordings AS r").
Select(`
COALESCE(SUM(sa.qty * CASE
WHEN sa.stockable_type = ? THEN COALESCE(pi.price, 0)
WHEN sa.stockable_type = ? THEN COALESCE(ast.price, 0)
ELSE 0
END), 0)`,
stockablePurchase,
stockableAdjustment,
).
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 = ? OR sa.stockable_type = ?) AND sa.status = ? AND sa.allocation_purpose = ?",
usableRecordingStock,
stockablePurchase,
stockableAdjustment,
entity.StockAllocationStatusActive,
entity.StockAllocationPurposeConsume,
).
Joins("LEFT JOIN purchase_items AS pi ON pi.id = sa.stockable_id AND sa.stockable_type = ?", stockablePurchase).
Joins("LEFT JOIN adjustment_stocks AS ast ON ast.id = sa.stockable_id AND sa.stockable_type = ?", stockableAdjustment).
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 *HppV2RepositoryImpl) 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 *HppV2RepositoryImpl) 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
}
var adjustmentTotalWeight float64
adjustmentSubQuery := r.db.WithContext(ctx).
Table("recordings AS r").
Select("DISTINCT ast.id AS adjustment_id, ast.price AS price").
Joins("JOIN recording_eggs AS re ON re.recording_id = r.id").
Joins("JOIN stock_transfer_details AS std ON std.dest_product_warehouse_id = re.product_warehouse_id").
Joins(
"JOIN stock_allocations AS sa ON sa.usable_type = ? AND sa.usable_id = std.id AND sa.stockable_type = ? AND sa.status = ? AND sa.allocation_purpose = ?",
fifo.UsableKeyStockTransferOut.String(),
fifo.StockableKeyAdjustmentIn.String(),
entity.StockAllocationStatusActive,
entity.StockAllocationPurposeConsume,
).
Joins("JOIN adjustment_stocks AS ast ON ast.id = sa.stockable_id AND ast.product_warehouse_id = std.source_product_warehouse_id").
Where("r.project_flock_kandangs_id IN (?)", projectFlockKandangIDs).
Where("r.record_datetime <= ?", *date)
err = r.db.WithContext(ctx).
Table("(?) AS adjustment_sources", adjustmentSubQuery).
Select("COALESCE(SUM(adjustment_sources.price), 0)").
Scan(&adjustmentTotalWeight).Error
if err != nil {
return 0, 0, err
}
totals.TotalWeightKg += adjustmentTotalWeight
return totals.TotalPieces, totals.TotalWeightKg, nil
}
func (r *HppV2RepositoryImpl) GetEggTerjualPiecesAndWeightKgByProjectFlockKandangIds(
ctx context.Context,
projectFlockKandangIDs []uint,
startDate *time.Time,
endDate *time.Time,
) (float64, float64, error) {
if len(projectFlockKandangIDs) == 0 {
return 0, 0, nil
}
if endDate == nil {
now := time.Now()
endDate = &now
}
if startDate == nil {
startDate = endDate
}
eggFlags := []string{
string(utils.FlagTelur),
string(utils.FlagTelurUtuh),
string(utils.FlagTelurPecah),
string(utils.FlagTelurPutih),
string(utils.FlagTelurRetak),
string(utils.FlagTelurPapacal),
string(utils.FlagTelurJumbo),
}
query := `
WITH selected_pfk AS (
SELECT pfk.id, k.location_id
FROM project_flock_kandangs pfk
JOIN kandangs k ON k.id = pfk.kandang_id
WHERE pfk.id IN ?
),
selected_locations AS (
SELECT DISTINCT location_id
FROM selected_pfk
),
sales_kandang AS (
SELECT DISTINCT
mdp.id AS mdp_id,
COALESCE(mdp.usage_qty, 0) AS usage_qty,
COALESCE(mdp.total_weight, 0) AS total_weight
FROM marketing_delivery_products mdp
JOIN marketing_products mp ON mp.id = mdp.marketing_product_id
JOIN product_warehouses pw ON pw.id = mp.product_warehouse_id
JOIN warehouses w ON w.id = pw.warehouse_id
WHERE mdp.delivery_date IS NOT NULL
AND mdp.delivery_date <= ?
AND UPPER(COALESCE(w.type, '')) = 'KANDANG'
AND pw.project_flock_kandang_id IN (SELECT id FROM selected_pfk)
AND EXISTS (
SELECT 1
FROM recording_eggs re
JOIN recordings rr ON rr.id = re.recording_id
WHERE re.product_warehouse_id = mp.product_warehouse_id
AND COALESCE(re.project_flock_kandang_id, rr.project_flock_kandangs_id) IN (SELECT id FROM selected_pfk)
AND rr.deleted_at IS NULL
AND DATE(rr.record_datetime) <= DATE(mdp.delivery_date)
)
AND EXISTS (
SELECT 1
FROM flags f
WHERE f.flagable_type = ?
AND f.flagable_id = pw.product_id
AND f.name IN ?
)
),
sales_lokasi AS (
SELECT DISTINCT
mdp.id AS mdp_id,
COALESCE(mdp.usage_qty, 0) AS usage_qty,
COALESCE(mdp.total_weight, 0) AS total_weight,
mdp.delivery_date AS delivery_date,
pw.id AS lokasi_pw_id,
pw.product_id AS product_id,
w.location_id AS location_id
FROM marketing_delivery_products mdp
JOIN marketing_products mp ON mp.id = mdp.marketing_product_id
JOIN product_warehouses pw ON pw.id = mp.product_warehouse_id
JOIN warehouses w ON w.id = pw.warehouse_id
WHERE mdp.delivery_date IS NOT NULL
AND mdp.delivery_date <= ?
AND UPPER(COALESCE(w.type, '')) = 'LOKASI'
AND w.location_id IN (SELECT location_id FROM selected_locations)
AND EXISTS (
SELECT 1
FROM flags f
WHERE f.flagable_type = ?
AND f.flagable_id = pw.product_id
AND f.name IN ?
)
),
transfer_pairs AS (
SELECT
std.source_product_warehouse_id AS source_pw_id,
std.dest_product_warehouse_id AS dest_pw_id,
MIN(st.transfer_date) AS first_transfer_date
FROM stock_transfer_details std
JOIN stock_transfers st ON st.id = std.stock_transfer_id
WHERE std.source_product_warehouse_id IS NOT NULL
AND std.dest_product_warehouse_id IS NOT NULL
GROUP BY std.source_product_warehouse_id, std.dest_product_warehouse_id
),
adj_pool AS (
SELECT
sl.mdp_id,
SUM(CASE
WHEN spw.project_flock_kandang_id IN (SELECT id FROM selected_pfk)
THEN COALESCE(ast.usage_qty, 0)
ELSE 0
END) AS sel_usage_qty,
SUM(COALESCE(ast.usage_qty, 0)) AS farm_usage_qty,
SUM(CASE
WHEN spw.project_flock_kandang_id IN (SELECT id FROM selected_pfk)
THEN COALESCE(ast.price, 0)
ELSE 0
END) AS sel_price_sum,
SUM(COALESCE(ast.price, 0)) AS farm_price_sum
FROM sales_lokasi sl
JOIN transfer_pairs tf
ON tf.dest_pw_id = sl.lokasi_pw_id
AND DATE(tf.first_transfer_date) <= DATE(sl.delivery_date)
JOIN product_warehouses spw
ON spw.id = tf.source_pw_id
AND spw.product_id = sl.product_id
JOIN warehouses sw ON sw.id = spw.warehouse_id
JOIN adjustment_stocks ast ON ast.product_warehouse_id = tf.source_pw_id
WHERE UPPER(COALESCE(sw.type, '')) = 'KANDANG'
AND sw.location_id = sl.location_id
AND UPPER(COALESCE(ast.function_code, '')) = UPPER(?)
AND UPPER(COALESCE(ast.transaction_type, '')) = UPPER(?)
AND DATE(ast.created_at) <= DATE(sl.delivery_date)
GROUP BY sl.mdp_id
),
sales_lokasi_adj AS (
SELECT sl.*
FROM sales_lokasi sl
JOIN adj_pool ap ON ap.mdp_id = sl.mdp_id
WHERE COALESCE(ap.farm_usage_qty, 0) > 0
OR COALESCE(ap.farm_price_sum, 0) > 0
),
sales_lokasi_rec AS (
SELECT sl.*
FROM sales_lokasi sl
WHERE NOT EXISTS (
SELECT 1 FROM sales_lokasi_adj sla WHERE sla.mdp_id = sl.mdp_id
)
),
rec_pool AS (
SELECT
sl.mdp_id,
SUM(CASE
WHEN COALESCE(re.project_flock_kandang_id, r.project_flock_kandangs_id) IN (SELECT id FROM selected_pfk)
THEN COALESCE(re.qty, 0)
ELSE 0
END) AS sel_qty,
SUM(COALESCE(re.qty, 0)) AS farm_qty,
SUM(CASE
WHEN COALESCE(re.project_flock_kandang_id, r.project_flock_kandangs_id) IN (SELECT id FROM selected_pfk)
THEN COALESCE(re.weight, 0)
ELSE 0
END) AS sel_weight,
SUM(COALESCE(re.weight, 0)) AS farm_weight
FROM sales_lokasi_rec sl
JOIN recordings r
ON r.deleted_at IS NULL
AND DATE(r.record_datetime) <= DATE(sl.delivery_date)
JOIN recording_eggs re
ON re.recording_id = r.id
AND re.product_warehouse_id = sl.lokasi_pw_id
JOIN project_flock_kandangs pfk
ON pfk.id = COALESCE(re.project_flock_kandang_id, r.project_flock_kandangs_id)
JOIN kandangs k ON k.id = pfk.kandang_id
WHERE k.location_id = sl.location_id
GROUP BY sl.mdp_id
),
kandang_totals AS (
SELECT
COALESCE(SUM(sk.usage_qty), 0) AS total_pieces,
COALESCE(SUM(sk.total_weight), 0) AS total_weight
FROM sales_kandang sk
),
lokasi_adj_totals AS (
SELECT
COALESCE(SUM(
sla.usage_qty *
CASE
WHEN COALESCE(ap.farm_usage_qty, 0) > 0 THEN (COALESCE(ap.sel_usage_qty, 0) * 1.0) / NULLIF(ap.farm_usage_qty, 0)
ELSE 0
END
), 0) AS total_pieces,
COALESCE(SUM(
sla.total_weight *
CASE
WHEN COALESCE(ap.farm_price_sum, 0) > 0 THEN (COALESCE(ap.sel_price_sum, 0) * 1.0) / NULLIF(ap.farm_price_sum, 0)
ELSE 0
END
), 0) AS total_weight
FROM sales_lokasi_adj sla
JOIN adj_pool ap ON ap.mdp_id = sla.mdp_id
),
lokasi_rec_totals AS (
SELECT
COALESCE(SUM(
slr.usage_qty *
CASE
WHEN COALESCE(rp.farm_qty, 0) > 0 THEN (COALESCE(rp.sel_qty, 0) * 1.0) / NULLIF(rp.farm_qty, 0)
ELSE 0
END
), 0) AS total_pieces,
COALESCE(SUM(
slr.total_weight *
CASE
WHEN COALESCE(rp.farm_weight, 0) > 0 THEN (COALESCE(rp.sel_weight, 0) * 1.0) / NULLIF(rp.farm_weight, 0)
ELSE 0
END
), 0) AS total_weight
FROM sales_lokasi_rec slr
LEFT JOIN rec_pool rp ON rp.mdp_id = slr.mdp_id
)
SELECT
COALESCE(kt.total_pieces, 0) + COALESCE(lat.total_pieces, 0) + COALESCE(lrt.total_pieces, 0) AS total_pieces,
COALESCE(kt.total_weight, 0) + COALESCE(lat.total_weight, 0) + COALESCE(lrt.total_weight, 0) AS total_weight
FROM kandang_totals kt
CROSS JOIN lokasi_adj_totals lat
CROSS JOIN lokasi_rec_totals lrt
`
var totals struct {
TotalPieces float64
TotalWeight float64
}
err := r.db.WithContext(ctx).
Raw(
query,
projectFlockKandangIDs,
*startDate,
entity.FlagableTypeProduct,
eggFlags,
*startDate,
entity.FlagableTypeProduct,
eggFlags,
string(utils.AdjustmentTransactionSubtypeRecordingEggIn),
string(utils.AdjustmentTransactionTypeRecording),
).
Scan(&totals).Error
if err != nil {
return 0, 0, err
}
return totals.TotalPieces, totals.TotalWeight, nil
}
func (r *HppV2RepositoryImpl) 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("lt.deleted_at IS NULL").
Where("ltt.deleted_at IS NULL").
Where("lt.executed_at IS NOT NULL").
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
}