mirror of
https://gitlab.com/mbugroup/lti-api.git
synced 2026-05-20 21:41:55 +00:00
1184 lines
39 KiB
Go
1184 lines
39 KiB
Go
package repository
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"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 HppV2ProjectFlockKandangContext struct {
|
|
ProjectFlockKandangID uint
|
|
ProjectFlockID uint
|
|
ProjectFlockCategory string
|
|
KandangID uint
|
|
KandangName string
|
|
LocationID uint
|
|
HouseType string
|
|
}
|
|
|
|
type HppV2UsageCostRow struct {
|
|
StockableType string
|
|
StockableID uint
|
|
SourceProductID uint
|
|
SourceProductName string
|
|
Qty float64
|
|
UnitPrice float64
|
|
TotalCost float64
|
|
FirstUsedAt time.Time
|
|
LastUsedAt time.Time
|
|
}
|
|
|
|
type HppV2AdjustmentCostRow struct {
|
|
AdjustmentID uint
|
|
ProjectFlockKandangID *uint
|
|
ProductWarehouseID uint
|
|
ProductID uint
|
|
ProductName string
|
|
WarehouseID uint
|
|
WarehouseType string
|
|
Qty float64
|
|
Price float64
|
|
GrandTotal float64
|
|
CreatedAt time.Time
|
|
}
|
|
|
|
type HppV2ExpenseCostRow struct {
|
|
ExpenseRealizationID uint
|
|
ExpenseNonstockID uint
|
|
ExpenseID uint
|
|
NonstockID uint
|
|
NonstockName string
|
|
Qty float64
|
|
Price float64
|
|
TotalCost float64
|
|
RealizationDate time.Time
|
|
}
|
|
|
|
type HppV2ChickinCostRow struct {
|
|
ProjectChickinID uint
|
|
ProjectFlockKandangID uint
|
|
ChickInDate time.Time
|
|
StockableType string
|
|
StockableID uint
|
|
SourceProductID uint
|
|
SourceProductName string
|
|
Qty float64
|
|
UnitPrice float64
|
|
TotalCost float64
|
|
}
|
|
|
|
type HppV2LatestTransferInputRow struct {
|
|
ProjectFlockKandangID uint
|
|
SourceProjectFlockID uint
|
|
TransferDate time.Time
|
|
TransferQty float64
|
|
TransferID uint
|
|
}
|
|
|
|
type HppV2ManualDepreciationInputRow struct {
|
|
ID uint
|
|
ProjectFlockID uint
|
|
TotalCost float64
|
|
CutoverDate time.Time
|
|
Note *string
|
|
}
|
|
|
|
type HppV2FarmDepreciationSnapshotRow struct {
|
|
ID uint
|
|
ProjectFlockID uint
|
|
PeriodDate time.Time
|
|
DepreciationPercentEffective float64
|
|
DepreciationValue float64
|
|
PulletCostDayNTotal float64
|
|
}
|
|
|
|
type HppV2CostRepository interface {
|
|
GetProjectFlockKandangContext(ctx context.Context, projectFlockKandangId uint) (*HppV2ProjectFlockKandangContext, error)
|
|
GetProjectFlockKandangIDs(ctx context.Context, projectFlockId uint) ([]uint, error)
|
|
GetLatestTransferInputByProjectFlockKandangID(ctx context.Context, projectFlockKandangId uint, period time.Time) (*HppV2LatestTransferInputRow, error)
|
|
GetManualDepreciationInputByProjectFlockID(ctx context.Context, projectFlockID uint) (*HppV2ManualDepreciationInputRow, error)
|
|
GetRecordingStockRoutingAdjustmentCostByProjectFlockID(ctx context.Context, projectFlockID uint, periodDate time.Time) (float64, error)
|
|
GetFarmDepreciationSnapshotByProjectFlockIDAndPeriod(ctx context.Context, projectFlockID uint, periodDate time.Time) (*HppV2FarmDepreciationSnapshotRow, error)
|
|
GetEarliestChickInDateByProjectFlockID(ctx context.Context, projectFlockID uint) (*time.Time, error)
|
|
GetDepreciationPercents(ctx context.Context, houseTypes []string, maxDay int) (map[string]map[int]float64, error)
|
|
ListUsageCostRowsByProductFlags(ctx context.Context, projectFlockKandangIDs []uint, flagNames []string, date *time.Time) ([]HppV2UsageCostRow, error)
|
|
ListAdjustmentCostRowsByProductFlags(ctx context.Context, projectFlockKandangIDs []uint, flagNames []string, date *time.Time) ([]HppV2AdjustmentCostRow, error)
|
|
ListExpenseRealizationRowsByProjectFlockKandangIDs(ctx context.Context, projectFlockKandangIDs []uint, date *time.Time, ekspedisi bool) ([]HppV2ExpenseCostRow, error)
|
|
ListExpenseRealizationRowsByProjectFlockID(ctx context.Context, projectFlockID uint, date *time.Time, ekspedisi bool) ([]HppV2ExpenseCostRow, error)
|
|
ListChickinCostRowsByProductFlags(ctx context.Context, projectFlockKandangIDs []uint, flagNames []string, date *time.Time, excludeTransferToLaying bool) ([]HppV2ChickinCostRow, 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)
|
|
GetEggProduksiBreakdownByProjectFlockKandangIds(ctx context.Context, projectFlockKandangIDs []uint, date *time.Time) (recordingQty, recordingWeight, adjustmentQty, adjustmentWeight float64, err 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) GetProjectFlockKandangContext(ctx context.Context, projectFlockKandangId uint) (*HppV2ProjectFlockKandangContext, error) {
|
|
var row HppV2ProjectFlockKandangContext
|
|
err := r.db.WithContext(ctx).
|
|
Table("project_flock_kandangs AS pfk").
|
|
Select(`
|
|
pfk.id AS project_flock_kandang_id,
|
|
pf.id AS project_flock_id,
|
|
pf.category AS project_flock_category,
|
|
k.id AS kandang_id,
|
|
k.name AS kandang_name,
|
|
k.location_id AS location_id,
|
|
k.house_type::text AS house_type
|
|
`).
|
|
Joins("JOIN project_flocks AS pf ON pf.id = pfk.project_flock_id").
|
|
Joins("JOIN kandangs AS k ON k.id = pfk.kandang_id").
|
|
Where("pfk.id = ?", projectFlockKandangId).
|
|
Scan(&row).Error
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if row.ProjectFlockKandangID == 0 {
|
|
return nil, gorm.ErrRecordNotFound
|
|
}
|
|
|
|
return &row, nil
|
|
}
|
|
|
|
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) GetLatestTransferInputByProjectFlockKandangID(
|
|
ctx context.Context,
|
|
projectFlockKandangId uint,
|
|
period time.Time,
|
|
) (*HppV2LatestTransferInputRow, error) {
|
|
var row HppV2LatestTransferInputRow
|
|
query := `
|
|
WITH latest_transfer_approval AS (
|
|
SELECT a.approvable_id, a.action
|
|
FROM approvals a
|
|
JOIN (
|
|
SELECT approvable_id, MAX(action_at) AS latest_action_at
|
|
FROM approvals
|
|
WHERE approvable_type = @approval_type
|
|
GROUP BY approvable_id
|
|
) la
|
|
ON la.approvable_id = a.approvable_id
|
|
AND la.latest_action_at = a.action_at
|
|
WHERE a.approvable_type = @approval_type
|
|
),
|
|
approved_transfers AS (
|
|
SELECT
|
|
lt.id,
|
|
lt.from_project_flock_id,
|
|
COALESCE(DATE(lt.effective_move_date), DATE(lt.economic_cutoff_date), DATE(lt.transfer_date)) AS effective_date
|
|
FROM laying_transfers lt
|
|
JOIN latest_transfer_approval lta ON lta.approvable_id = lt.id
|
|
WHERE lt.deleted_at IS NULL
|
|
AND lt.executed_at IS NOT NULL
|
|
AND lta.action = 'APPROVED'
|
|
)
|
|
SELECT
|
|
ltt.target_project_flock_kandang_id AS project_flock_kandang_id,
|
|
at.from_project_flock_id AS source_project_flock_id,
|
|
at.effective_date AS transfer_date,
|
|
ltt.total_qty AS transfer_qty,
|
|
at.id AS transfer_id
|
|
FROM laying_transfer_targets ltt
|
|
JOIN approved_transfers at ON at.id = ltt.laying_transfer_id
|
|
WHERE ltt.deleted_at IS NULL
|
|
AND ltt.target_project_flock_kandang_id = @project_flock_kandang_id
|
|
AND at.effective_date <= DATE(@period_date)
|
|
ORDER BY at.effective_date DESC, at.id DESC
|
|
LIMIT 1
|
|
`
|
|
|
|
err := r.db.WithContext(ctx).Raw(query, map[string]any{
|
|
"approval_type": utils.ApprovalWorkflowTransferToLaying.String(),
|
|
"project_flock_kandang_id": projectFlockKandangId,
|
|
"period_date": period,
|
|
}).Scan(&row).Error
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if row.TransferID == 0 {
|
|
return nil, nil
|
|
}
|
|
|
|
return &row, nil
|
|
}
|
|
|
|
func (r *HppV2RepositoryImpl) GetManualDepreciationInputByProjectFlockID(
|
|
ctx context.Context,
|
|
projectFlockID uint,
|
|
) (*HppV2ManualDepreciationInputRow, error) {
|
|
var row HppV2ManualDepreciationInputRow
|
|
err := r.db.WithContext(ctx).
|
|
Table("farm_depreciation_manual_inputs").
|
|
Select("id, project_flock_id, total_cost, cutover_date, note").
|
|
Where("project_flock_id = ?", projectFlockID).
|
|
Limit(1).
|
|
Take(&row).Error
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return nil, nil
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &row, nil
|
|
}
|
|
|
|
func (r *HppV2RepositoryImpl) GetRecordingStockRoutingAdjustmentCostByProjectFlockID(
|
|
ctx context.Context,
|
|
projectFlockID uint,
|
|
periodDate time.Time,
|
|
) (float64, error) {
|
|
if projectFlockID == 0 || periodDate.IsZero() {
|
|
return 0, nil
|
|
}
|
|
|
|
flags := []utils.FlagType{
|
|
utils.FlagPakan,
|
|
utils.FlagOVK,
|
|
utils.FlagObat,
|
|
utils.FlagVitamin,
|
|
utils.FlagKimia,
|
|
}
|
|
transferExistsCondition := `
|
|
EXISTS (
|
|
SELECT 1
|
|
FROM laying_transfer_targets AS ltt
|
|
JOIN laying_transfers AS lt ON lt.id = ltt.laying_transfer_id
|
|
WHERE ltt.deleted_at IS NULL
|
|
AND lt.deleted_at IS NULL
|
|
AND lt.executed_at IS NOT NULL
|
|
AND ltt.target_project_flock_kandang_id = r.project_flock_kandangs_id
|
|
AND COALESCE(DATE(lt.effective_move_date), DATE(lt.economic_cutoff_date), DATE(lt.transfer_date)) <= DATE(?)
|
|
AND (
|
|
SELECT a.action
|
|
FROM approvals a
|
|
WHERE a.approvable_type = ?
|
|
AND a.approvable_id = lt.id
|
|
ORDER BY a.id DESC
|
|
LIMIT 1
|
|
) = ?
|
|
)
|
|
`
|
|
|
|
var total float64
|
|
err := r.db.WithContext(ctx).
|
|
Table("recording_stocks AS rs").
|
|
Select("COALESCE(SUM(sa.qty * COALESCE(pi.price, 0)), 0)").
|
|
Joins("JOIN recordings AS r ON r.id = rs.recording_id AND r.deleted_at IS NULL").
|
|
Joins("JOIN project_flock_kandangs AS pfk_rec ON pfk_rec.id = r.project_flock_kandangs_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("pfk_rec.project_flock_id = ?", projectFlockID).
|
|
Where("DATE(r.record_datetime) <= DATE(?)", periodDate).
|
|
Where(
|
|
fmt.Sprintf(
|
|
"((%s) AND rs.project_flock_kandang_id IS NOT NULL AND rs.project_flock_kandang_id <> r.project_flock_kandangs_id) OR (NOT (%s) AND rs.project_flock_kandang_id IS NULL)",
|
|
transferExistsCondition,
|
|
transferExistsCondition,
|
|
),
|
|
periodDate,
|
|
string(utils.ApprovalWorkflowTransferToLaying),
|
|
entity.ApprovalActionApproved,
|
|
periodDate,
|
|
string(utils.ApprovalWorkflowTransferToLaying),
|
|
entity.ApprovalActionApproved,
|
|
).
|
|
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 *HppV2RepositoryImpl) GetFarmDepreciationSnapshotByProjectFlockIDAndPeriod(
|
|
ctx context.Context,
|
|
projectFlockID uint,
|
|
periodDate time.Time,
|
|
) (*HppV2FarmDepreciationSnapshotRow, error) {
|
|
var row HppV2FarmDepreciationSnapshotRow
|
|
err := r.db.WithContext(ctx).
|
|
Table("farm_depreciation_snapshots").
|
|
Select("id, project_flock_id, period_date, depreciation_percent_effective, depreciation_value, pullet_cost_day_n_total").
|
|
Where("project_flock_id = ?", projectFlockID).
|
|
Where("period_date = DATE(?)", periodDate).
|
|
Limit(1).
|
|
Take(&row).Error
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return nil, nil
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &row, nil
|
|
}
|
|
|
|
func (r *HppV2RepositoryImpl) GetEarliestChickInDateByProjectFlockID(ctx context.Context, projectFlockID uint) (*time.Time, error) {
|
|
type row struct {
|
|
ChickInDate *time.Time
|
|
}
|
|
|
|
var selected row
|
|
err := r.db.WithContext(ctx).
|
|
Table("project_chickins AS pc").
|
|
Select("MIN(pc.chick_in_date) AS chick_in_date").
|
|
Joins("JOIN project_flock_kandangs AS pfk ON pfk.id = pc.project_flock_kandang_id").
|
|
Where("pc.deleted_at IS NULL").
|
|
Where("pfk.project_flock_id = ?", projectFlockID).
|
|
Scan(&selected).Error
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if selected.ChickInDate == nil || selected.ChickInDate.IsZero() {
|
|
return nil, nil
|
|
}
|
|
|
|
return selected.ChickInDate, nil
|
|
}
|
|
|
|
func (r *HppV2RepositoryImpl) GetDepreciationPercents(
|
|
ctx context.Context,
|
|
houseTypes []string,
|
|
maxDay int,
|
|
) (map[string]map[int]float64, error) {
|
|
result := make(map[string]map[int]float64)
|
|
if len(houseTypes) == 0 || maxDay <= 0 {
|
|
return result, nil
|
|
}
|
|
|
|
type row struct {
|
|
HouseType string
|
|
Day int
|
|
DepreciationPercent float64
|
|
}
|
|
|
|
rows := make([]row, 0)
|
|
err := r.db.WithContext(ctx).
|
|
Table("house_depreciation_standards").
|
|
Select("house_type::text AS house_type, day, depreciation_percent").
|
|
Where("house_type::text IN ?", houseTypes).
|
|
Where("day <= ?", maxDay).
|
|
Order("house_type ASC, day ASC").
|
|
Scan(&rows).Error
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for _, item := range rows {
|
|
if _, exists := result[item.HouseType]; !exists {
|
|
result[item.HouseType] = make(map[int]float64)
|
|
}
|
|
result[item.HouseType][item.Day] = item.DepreciationPercent
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
func (r *HppV2RepositoryImpl) ListUsageCostRowsByProductFlags(
|
|
ctx context.Context,
|
|
projectFlockKandangIDs []uint,
|
|
flagNames []string,
|
|
date *time.Time,
|
|
) ([]HppV2UsageCostRow, error) {
|
|
if len(projectFlockKandangIDs) == 0 || len(flagNames) == 0 {
|
|
return []HppV2UsageCostRow{}, nil
|
|
}
|
|
if date == nil {
|
|
now := time.Now()
|
|
date = &now
|
|
}
|
|
|
|
stockablePurchase := fifo.StockableKeyPurchaseItems.String()
|
|
stockableAdjustment := fifo.StockableKeyAdjustmentIn.String()
|
|
usableRecordingStock := fifo.UsableKeyRecordingStock.String()
|
|
|
|
rows := make([]HppV2UsageCostRow, 0)
|
|
err := r.db.WithContext(ctx).
|
|
Table("recordings AS r").
|
|
Select(`
|
|
sa.stockable_type AS stockable_type,
|
|
sa.stockable_id AS stockable_id,
|
|
COALESCE(pi.product_id, ast_pw.product_id, 0) AS source_product_id,
|
|
COALESCE(pi_prod.name, ast_prod.name, '') AS source_product_name,
|
|
COALESCE(SUM(sa.qty), 0) AS qty,
|
|
COALESCE(MAX(CASE
|
|
WHEN sa.stockable_type = ? THEN COALESCE(pi.price, 0)
|
|
WHEN sa.stockable_type = ? THEN COALESCE(ast.price, 0)
|
|
ELSE 0
|
|
END), 0) AS unit_price,
|
|
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) AS total_cost,
|
|
MIN(r.record_datetime) AS first_used_at,
|
|
MAX(r.record_datetime) AS last_used_at
|
|
`,
|
|
stockablePurchase,
|
|
stockableAdjustment,
|
|
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 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 products AS pi_prod ON pi_prod.id = pi.product_id").
|
|
Joins("LEFT JOIN adjustment_stocks AS ast ON ast.id = sa.stockable_id AND sa.stockable_type = ?", stockableAdjustment).
|
|
Joins("LEFT JOIN product_warehouses AS ast_pw ON ast_pw.id = ast.product_warehouse_id").
|
|
Joins("LEFT JOIN products AS ast_prod ON ast_prod.id = ast_pw.product_id").
|
|
Where("rs.project_flock_kandang_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, flagNames).
|
|
Group(`
|
|
sa.stockable_type,
|
|
sa.stockable_id,
|
|
COALESCE(pi.product_id, ast_pw.product_id, 0),
|
|
COALESCE(pi_prod.name, ast_prod.name, '')
|
|
`).
|
|
Order("MIN(r.record_datetime) ASC, sa.stockable_type ASC, sa.stockable_id ASC").
|
|
Scan(&rows).Error
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return rows, nil
|
|
}
|
|
|
|
func (r *HppV2RepositoryImpl) ListAdjustmentCostRowsByProductFlags(
|
|
ctx context.Context,
|
|
projectFlockKandangIDs []uint,
|
|
flagNames []string,
|
|
date *time.Time,
|
|
) ([]HppV2AdjustmentCostRow, error) {
|
|
if len(projectFlockKandangIDs) == 0 || len(flagNames) == 0 {
|
|
return []HppV2AdjustmentCostRow{}, nil
|
|
}
|
|
if date == nil {
|
|
now := time.Now()
|
|
date = &now
|
|
}
|
|
|
|
rows := make([]HppV2AdjustmentCostRow, 0)
|
|
err := r.db.WithContext(ctx).
|
|
Table("adjustment_stocks AS ast").
|
|
Select(`
|
|
ast.id AS adjustment_id,
|
|
pw.project_flock_kandang_id AS project_flock_kandang_id,
|
|
ast.product_warehouse_id AS product_warehouse_id,
|
|
pw.product_id AS product_id,
|
|
p.name AS product_name,
|
|
w.id AS warehouse_id,
|
|
w.type AS warehouse_type,
|
|
COALESCE(ast.total_qty, 0) AS qty,
|
|
COALESCE(ast.price, 0) AS price,
|
|
COALESCE(ast.grand_total, 0) AS grand_total,
|
|
ast.created_at AS created_at
|
|
`).
|
|
Joins("JOIN product_warehouses AS pw ON pw.id = ast.product_warehouse_id").
|
|
Joins("JOIN products AS p ON p.id = pw.product_id").
|
|
Joins("JOIN warehouses AS w ON w.id = pw.warehouse_id").
|
|
Where("pw.project_flock_kandang_id IN ?", projectFlockKandangIDs).
|
|
// Where("ast.created_at <= ?", *date).
|
|
Where("COALESCE(ast.total_qty, 0) > 0").
|
|
Where("EXISTS (SELECT 1 FROM flags f WHERE f.flagable_id = pw.product_id AND f.flagable_type = ? AND f.name IN ?)", entity.FlagableTypeProduct, flagNames).
|
|
Order("ast.created_at ASC, ast.id ASC").
|
|
Scan(&rows).Error
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return rows, nil
|
|
}
|
|
|
|
func (r *HppV2RepositoryImpl) ListChickinCostRowsByProductFlags(
|
|
ctx context.Context,
|
|
projectFlockKandangIDs []uint,
|
|
flagNames []string,
|
|
date *time.Time,
|
|
excludeTransferToLaying bool,
|
|
) ([]HppV2ChickinCostRow, error) {
|
|
if len(projectFlockKandangIDs) == 0 || len(flagNames) == 0 {
|
|
return []HppV2ChickinCostRow{}, nil
|
|
}
|
|
if date == nil {
|
|
now := time.Now()
|
|
date = &now
|
|
}
|
|
|
|
stockablePurchase := fifo.StockableKeyPurchaseItems.String()
|
|
stockableAdjustment := fifo.StockableKeyAdjustmentIn.String()
|
|
stockableTransferIn := fifo.StockableKeyStockTransferIn.String()
|
|
stockableTransferToLaying := fifo.StockableKeyTransferToLayingIn.String()
|
|
usableProjectChickin := fifo.UsableKeyProjectChickin.String()
|
|
usableStockTransferOut := fifo.UsableKeyStockTransferOut.String()
|
|
unitPriceExpr := fmt.Sprintf(`
|
|
CASE
|
|
WHEN sa.stockable_type = '%s' THEN COALESCE(pi.price, 0)
|
|
WHEN sa.stockable_type = '%s' THEN COALESCE(ast.price, 0)
|
|
WHEN sa.stockable_type = '%s' THEN COALESCE(spi.price, sast.price, 0)
|
|
WHEN sa.stockable_type = '%s' THEN COALESCE(tpi.price, tast.price, 0)
|
|
ELSE 0
|
|
END
|
|
`, stockablePurchase, stockableAdjustment, stockableTransferIn, stockableTransferToLaying)
|
|
|
|
rows := make([]HppV2ChickinCostRow, 0)
|
|
query := r.db.WithContext(ctx).
|
|
Table("project_chickins AS pc").
|
|
Select(`
|
|
pc.id AS project_chickin_id,
|
|
pc.project_flock_kandang_id AS project_flock_kandang_id,
|
|
pc.chick_in_date AS chick_in_date,
|
|
sa.stockable_type AS stockable_type,
|
|
sa.stockable_id AS stockable_id,
|
|
COALESCE(
|
|
pi.product_id,
|
|
ast_pw.product_id,
|
|
tpi.product_id,
|
|
tast_pw.product_id,
|
|
spi.product_id,
|
|
sast_pw.product_id,
|
|
0
|
|
) AS source_product_id,
|
|
COALESCE(
|
|
pi_prod.name,
|
|
ast_prod.name,
|
|
tpi_prod.name,
|
|
tast_prod.name,
|
|
spi_prod.name,
|
|
sast_prod.name,
|
|
''
|
|
) AS source_product_name,
|
|
COALESCE(SUM(sa.qty), 0) AS qty,
|
|
`+unitPriceExpr+` AS unit_price,
|
|
COALESCE(SUM(sa.qty * (`+unitPriceExpr+`)), 0) AS total_cost
|
|
`).
|
|
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 products AS pi_prod ON pi_prod.id = pi.product_id").
|
|
Joins("LEFT JOIN adjustment_stocks AS ast ON ast.id = sa.stockable_id AND sa.stockable_type = ?", stockableAdjustment).
|
|
Joins("LEFT JOIN product_warehouses AS ast_pw ON ast_pw.id = ast.product_warehouse_id").
|
|
Joins("LEFT JOIN products AS ast_prod ON ast_prod.id = ast_pw.product_id").
|
|
Joins(
|
|
"LEFT JOIN stock_allocations AS tsa_transfer ON tsa_transfer.usable_type = ? AND tsa_transfer.usable_id = sa.stockable_id AND sa.stockable_type = ? AND tsa_transfer.status = ? AND tsa_transfer.allocation_purpose = ?",
|
|
stockableTransferToLaying,
|
|
stockableTransferToLaying,
|
|
entity.StockAllocationStatusActive,
|
|
entity.StockAllocationPurposeConsume,
|
|
).
|
|
Joins("LEFT JOIN purchase_items AS tpi ON tpi.id = tsa_transfer.stockable_id AND tsa_transfer.stockable_type = ?", stockablePurchase).
|
|
Joins("LEFT JOIN products AS tpi_prod ON tpi_prod.id = tpi.product_id").
|
|
Joins("LEFT JOIN adjustment_stocks AS tast ON tast.id = tsa_transfer.stockable_id AND tsa_transfer.stockable_type = ?", stockableAdjustment).
|
|
Joins("LEFT JOIN product_warehouses AS tast_pw ON tast_pw.id = tast.product_warehouse_id").
|
|
Joins("LEFT JOIN products AS tast_prod ON tast_prod.id = tast_pw.product_id").
|
|
Joins(
|
|
"LEFT JOIN stock_allocations AS tsa_stock ON tsa_stock.usable_type = ? AND tsa_stock.usable_id = sa.stockable_id AND sa.stockable_type = ? AND tsa_stock.status = ? AND tsa_stock.allocation_purpose = ?",
|
|
usableStockTransferOut,
|
|
stockableTransferIn,
|
|
entity.StockAllocationStatusActive,
|
|
entity.StockAllocationPurposeConsume,
|
|
).
|
|
Joins("LEFT JOIN purchase_items AS spi ON spi.id = tsa_stock.stockable_id AND tsa_stock.stockable_type = ?", stockablePurchase).
|
|
Joins("LEFT JOIN products AS spi_prod ON spi_prod.id = spi.product_id").
|
|
Joins("LEFT JOIN adjustment_stocks AS sast ON sast.id = tsa_stock.stockable_id AND tsa_stock.stockable_type = ?", stockableAdjustment).
|
|
Joins("LEFT JOIN product_warehouses AS sast_pw ON sast_pw.id = sast.product_warehouse_id").
|
|
Joins("LEFT JOIN products AS sast_prod ON sast_prod.id = sast_pw.product_id").
|
|
Where("pc.project_flock_kandang_id IN ?", projectFlockKandangIDs).
|
|
Where("pc.chick_in_date <= ?", *date).
|
|
Where(`
|
|
EXISTS (
|
|
SELECT 1
|
|
FROM flags f
|
|
WHERE f.flagable_type = ?
|
|
AND f.flagable_id = COALESCE(
|
|
pi.product_id,
|
|
ast_pw.product_id,
|
|
tpi.product_id,
|
|
tast_pw.product_id,
|
|
spi.product_id,
|
|
sast_pw.product_id,
|
|
0
|
|
)
|
|
AND f.name IN ?
|
|
)
|
|
`, entity.FlagableTypeProduct, flagNames)
|
|
|
|
if excludeTransferToLaying {
|
|
query = query.Where("sa.stockable_type <> ?", stockableTransferToLaying)
|
|
}
|
|
|
|
err := query.
|
|
Group(fmt.Sprintf(`
|
|
pc.id,
|
|
pc.project_flock_kandang_id,
|
|
pc.chick_in_date,
|
|
sa.stockable_type,
|
|
sa.stockable_id,
|
|
COALESCE(
|
|
pi.product_id,
|
|
ast_pw.product_id,
|
|
tpi.product_id,
|
|
tast_pw.product_id,
|
|
spi.product_id,
|
|
sast_pw.product_id,
|
|
0
|
|
),
|
|
COALESCE(
|
|
pi_prod.name,
|
|
ast_prod.name,
|
|
tpi_prod.name,
|
|
tast_prod.name,
|
|
spi_prod.name,
|
|
sast_prod.name,
|
|
''
|
|
),
|
|
%s
|
|
`, unitPriceExpr)).
|
|
Order("pc.chick_in_date ASC, pc.id ASC, sa.stockable_type ASC, sa.stockable_id ASC").
|
|
Scan(&rows).Error
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return rows, nil
|
|
}
|
|
|
|
func (r *HppV2RepositoryImpl) ListExpenseRealizationRowsByProjectFlockKandangIDs(
|
|
ctx context.Context,
|
|
projectFlockKandangIDs []uint,
|
|
date *time.Time,
|
|
ekspedisi bool,
|
|
) ([]HppV2ExpenseCostRow, error) {
|
|
if len(projectFlockKandangIDs) == 0 {
|
|
return []HppV2ExpenseCostRow{}, nil
|
|
}
|
|
if date == nil {
|
|
now := time.Now()
|
|
date = &now
|
|
}
|
|
|
|
rows := make([]HppV2ExpenseCostRow, 0)
|
|
query := r.db.WithContext(ctx).
|
|
Table("expense_realizations AS er").
|
|
Select(`
|
|
er.id AS expense_realization_id,
|
|
en.id AS expense_nonstock_id,
|
|
e.id AS expense_id,
|
|
COALESCE(n.id, 0) AS nonstock_id,
|
|
COALESCE(n.name, '') AS nonstock_name,
|
|
COALESCE(er.qty, 0) AS qty,
|
|
COALESCE(er.price, 0) AS price,
|
|
COALESCE(er.qty, 0) * COALESCE(er.price, 0) AS total_cost,
|
|
COALESCE(e.realization_date, DATE(er.created_at)) AS realization_date
|
|
`).
|
|
Joins("JOIN expense_nonstocks AS en ON en.id = er.expense_nonstock_id").
|
|
Joins("JOIN expenses AS e ON e.id = en.expense_id").
|
|
Joins("LEFT JOIN nonstocks AS n ON n.id = en.nonstock_id").
|
|
Joins("LEFT JOIN flags AS f ON f.flagable_id = n.id AND f.flagable_type = ? AND f.name = ?", entity.FlagableTypeNonstock, utils.FlagEkspedisi).
|
|
Where("e.deleted_at IS NULL").
|
|
Where("e.category = ?", utils.ExpenseCategoryBOP).
|
|
Where("en.project_flock_kandang_id IN ?", projectFlockKandangIDs).
|
|
Where("COALESCE(e.realization_date, DATE(er.created_at)) <= ?", *date)
|
|
|
|
if ekspedisi {
|
|
query = query.Where("f.id IS NOT NULL")
|
|
} else {
|
|
query = query.Where("f.id IS NULL")
|
|
}
|
|
|
|
if err := query.
|
|
Order("COALESCE(e.realization_date, DATE(er.created_at)) ASC, er.id ASC").
|
|
Scan(&rows).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return rows, nil
|
|
}
|
|
|
|
func (r *HppV2RepositoryImpl) ListExpenseRealizationRowsByProjectFlockID(
|
|
ctx context.Context,
|
|
projectFlockID uint,
|
|
date *time.Time,
|
|
ekspedisi bool,
|
|
) ([]HppV2ExpenseCostRow, error) {
|
|
if projectFlockID == 0 {
|
|
return []HppV2ExpenseCostRow{}, nil
|
|
}
|
|
if date == nil {
|
|
now := time.Now()
|
|
date = &now
|
|
}
|
|
|
|
rows := make([]HppV2ExpenseCostRow, 0)
|
|
query := r.db.WithContext(ctx).
|
|
Table("expense_realizations AS er").
|
|
Select(`
|
|
er.id AS expense_realization_id,
|
|
en.id AS expense_nonstock_id,
|
|
e.id AS expense_id,
|
|
COALESCE(n.id, 0) AS nonstock_id,
|
|
COALESCE(n.name, '') AS nonstock_name,
|
|
COALESCE(er.qty, 0) AS qty,
|
|
COALESCE(er.price, 0) AS price,
|
|
COALESCE(er.qty, 0) * COALESCE(er.price, 0) AS total_cost,
|
|
COALESCE(e.realization_date, DATE(er.created_at)) AS realization_date
|
|
`).
|
|
Joins("JOIN expense_nonstocks AS en ON en.id = er.expense_nonstock_id").
|
|
Joins("JOIN expenses AS e ON e.id = en.expense_id").
|
|
Joins("LEFT JOIN nonstocks AS n ON n.id = en.nonstock_id").
|
|
Joins("LEFT JOIN flags AS f ON f.flagable_id = n.id AND f.flagable_type = ? AND f.name = ?", entity.FlagableTypeNonstock, utils.FlagEkspedisi).
|
|
Where("e.deleted_at IS NULL").
|
|
Where("e.category = ?", utils.ExpenseCategoryBOP).
|
|
Where("en.project_flock_kandang_id IS NULL").
|
|
Where("e.project_flock_id IS NOT NULL").
|
|
Where("e.project_flock_id::jsonb @> ?::jsonb", fmt.Sprintf("[%d]", projectFlockID)).
|
|
Where("COALESCE(e.realization_date, DATE(er.created_at)) <= ?", *date)
|
|
|
|
if ekspedisi {
|
|
query = query.Where("f.id IS NOT NULL")
|
|
} else {
|
|
query = query.Where("f.id IS NULL")
|
|
}
|
|
|
|
if err := query.
|
|
Order("COALESCE(e.realization_date, DATE(er.created_at)) ASC, er.id ASC").
|
|
Scan(&rows).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return rows, 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("rs.project_flock_kandang_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) {
|
|
rQty, rWeight, aQty, aWeight, err := r.GetEggProduksiBreakdownByProjectFlockKandangIds(ctx, projectFlockKandangIDs, date)
|
|
if err != nil {
|
|
return 0, 0, err
|
|
}
|
|
return rQty + aQty, rWeight + aWeight, nil
|
|
}
|
|
|
|
func (r *HppV2RepositoryImpl) GetEggProduksiBreakdownByProjectFlockKandangIds(ctx context.Context, projectFlockKandangIDs []uint, date *time.Time) (recordingQty, recordingWeight, adjustmentQty, adjustmentWeight float64, err error) {
|
|
if date == nil {
|
|
now := time.Now()
|
|
date = &now
|
|
}
|
|
|
|
var recordingTotals 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(&recordingTotals).Error
|
|
if err != nil {
|
|
return 0, 0, 0, 0, err
|
|
}
|
|
|
|
var adjustmentTotals struct {
|
|
TotalQty float64
|
|
TotalWeight float64
|
|
}
|
|
err = r.db.WithContext(ctx).
|
|
Table("adjustment_stocks AS ast").
|
|
Select("COALESCE(SUM(ast.total_qty), 0) AS total_qty, COALESCE(SUM(ast.price), 0) AS total_weight").
|
|
Joins("JOIN product_warehouses AS pw ON pw.id = ast.product_warehouse_id").
|
|
Where("pw.project_flock_kandang_id IN (?)", projectFlockKandangIDs).
|
|
Where("ast.function_code = ?", string(utils.AdjustmentTransactionSubtypeRecordingEggIn)).
|
|
Scan(&adjustmentTotals).Error
|
|
if err != nil {
|
|
return 0, 0, 0, 0, err
|
|
}
|
|
|
|
return recordingTotals.TotalPieces, recordingTotals.TotalWeightKg, adjustmentTotals.TotalQty, adjustmentTotals.TotalWeight, 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,
|
|
*endDate,
|
|
entity.FlagableTypeProduct,
|
|
eggFlags,
|
|
*endDate,
|
|
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
|
|
}
|