feat: manual pullet cost

This commit is contained in:
Adnan Zahir
2026-04-19 15:10:53 +07:00
parent a2ae139fae
commit 69d6fc165a
13 changed files with 857 additions and 33 deletions
@@ -2,6 +2,7 @@ package repository
import (
"context"
"errors"
"fmt"
"time"
@@ -72,9 +73,29 @@ type HppV2ChickinCostRow struct {
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 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)
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)
@@ -136,6 +157,149 @@ func (r *HppV2RepositoryImpl) GetProjectFlockKandangIDs(ctx context.Context, pro
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) 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,