init adjustment recording

This commit is contained in:
giovanni
2026-04-21 22:43:18 +07:00
parent 5594c27108
commit 091f706276
15 changed files with 863 additions and 185 deletions
@@ -151,7 +151,7 @@ func (r *HppRepositoryImpl) GetFeedUsageCost(ctx context.Context, projectFlockKa
).
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("rs.project_flock_kandang_id IN (?)", projectFlockKandangIDs).
Where("r.record_datetime <= ?", *date).
Where("f.name = ?", utils.FlagPakan).
Scan(&total).Error
@@ -202,7 +202,7 @@ func (r *HppRepositoryImpl) GetOvkUsageCost(ctx context.Context, projectFlockKan
).
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("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, flags).
Scan(&total).Error
@@ -103,6 +103,7 @@ type HppV2CostRepository interface {
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)
@@ -249,6 +250,50 @@ func (r *HppV2RepositoryImpl) GetManualDepreciationInputByProjectFlockID(
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,
}
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("(rs.project_flock_kandang_id IS NULL OR rs.project_flock_kandang_id <> r.project_flock_kandangs_id)").
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,
@@ -393,7 +438,7 @@ func (r *HppV2RepositoryImpl) ListUsageCostRowsByProductFlags(
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("r.project_flock_kandangs_id IN ?", projectFlockKandangIDs).
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(`
@@ -755,7 +800,7 @@ func (r *HppV2RepositoryImpl) GetFeedUsageCost(ctx context.Context, projectFlock
).
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("rs.project_flock_kandang_id IN (?)", projectFlockKandangIDs).
Where("r.record_datetime <= ?", *date).
Where("f.name = ?", utils.FlagPakan).
Scan(&total).Error
@@ -96,6 +96,54 @@ func TestHppV2RepositoryGetEggTerjualProratesHistoricalFarmSalesFromAdjustments(
assertFloatEquals(t, totalWeightKg, 1.4)
}
func TestHppV2RepositoryGetRecordingStockRoutingAdjustmentCostByProjectFlockID(t *testing.T) {
db := setupHppV2RepositoryTestDB(t)
mustExecHppV2(t, db,
`INSERT INTO project_flock_kandangs (id, kandang_id, project_flock_id) VALUES (101, 1, 1), (201, 2, 2)`,
`INSERT INTO recordings (id, project_flock_kandangs_id, record_datetime, deleted_at) VALUES
(1, 101, '2026-04-10 08:00:00', NULL),
(2, 101, '2026-04-11 08:00:00', NULL),
(3, 101, '2026-04-12 08:00:00', NULL)`,
`INSERT INTO product_warehouses (id, warehouse_id, product_id, project_flock_kandang_id) VALUES
(501, 201, 10, NULL),
(502, 201, 11, NULL),
(503, 201, 12, NULL)`,
`INSERT INTO flags (id, flagable_type, flagable_id, name) VALUES
(10, 'products', 10, 'PAKAN'),
(11, 'products', 11, 'OVK'),
(12, 'products', 12, 'PAKAN')`,
`INSERT INTO recording_stocks (id, recording_id, product_warehouse_id, project_flock_kandang_id) VALUES
(101, 1, 501, NULL),
(102, 2, 502, 201),
(103, 3, 503, 101)`,
`INSERT INTO purchase_items (id, product_id, price) VALUES
(601, 10, 100),
(602, 11, 200),
(603, 12, 300)`,
`INSERT INTO stock_allocations (id, usable_type, usable_id, stockable_type, stockable_id, status, allocation_purpose, qty) VALUES
(9001, 'RECORDING_STOCK', 101, 'PURCHASE_ITEMS', 601, 'ACTIVE', 'CONSUME', 2),
(9002, 'RECORDING_STOCK', 102, 'PURCHASE_ITEMS', 602, 'ACTIVE', 'CONSUME', 1.5),
(9003, 'RECORDING_STOCK', 103, 'PURCHASE_ITEMS', 603, 'ACTIVE', 'CONSUME', 1)`,
)
repo := &HppV2RepositoryImpl{db: db}
periodDate := mustJakartaTime(t, "2026-04-30 00:00:00")
total, err := repo.GetRecordingStockRoutingAdjustmentCostByProjectFlockID(context.Background(), 1, periodDate)
if err != nil {
t.Fatalf("expected no error, got %v", err)
}
assertFloatEquals(t, total, 500)
earlyPeriod := mustJakartaTime(t, "2026-04-10 23:59:59")
earlyTotal, err := repo.GetRecordingStockRoutingAdjustmentCostByProjectFlockID(context.Background(), 1, earlyPeriod)
if err != nil {
t.Fatalf("expected no error, got %v", err)
}
assertFloatEquals(t, earlyTotal, 200)
}
func setupHppV2RepositoryTestDB(t *testing.T) *gorm.DB {
t.Helper()
@@ -111,6 +159,12 @@ func setupHppV2RepositoryTestDB(t *testing.T) *gorm.DB {
record_datetime DATETIME NULL,
deleted_at DATETIME NULL
)`,
`CREATE TABLE recording_stocks (
id INTEGER PRIMARY KEY,
recording_id INTEGER NULL,
product_warehouse_id INTEGER NULL,
project_flock_kandang_id INTEGER NULL
)`,
`CREATE TABLE recording_eggs (
id INTEGER PRIMARY KEY,
recording_id INTEGER NULL,
@@ -174,6 +228,11 @@ func setupHppV2RepositoryTestDB(t *testing.T) *gorm.DB {
id INTEGER PRIMARY KEY,
product_warehouse_id INTEGER NULL
)`,
`CREATE TABLE purchase_items (
id INTEGER PRIMARY KEY,
product_id INTEGER NULL,
price NUMERIC(15,3) NULL
)`,
`CREATE TABLE marketing_delivery_products (
id INTEGER PRIMARY KEY,
marketing_product_id INTEGER NULL,