mirror of
https://gitlab.com/mbugroup/lti-api.git
synced 2026-05-20 13:31:56 +00:00
a15fd1b174
[FIX][BE]: fix perhitunga sapronak See merge request mbugroup/lti-api!439
285 lines
9.7 KiB
Go
285 lines
9.7 KiB
Go
package repository
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/glebarez/sqlite"
|
|
"gitlab.com/mbugroup/lti-api.git/internal/utils/fifo"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
func TestSapronakIncomingPurchaseQueryPartsUsesAttributedPurchasesWhenProjectFlockKandangIDsProvided(t *testing.T) {
|
|
sql, args := sapronakIncomingPurchaseQueryParts(SapronakQueryParams{
|
|
WarehouseIDs: []uint{46},
|
|
ProjectFlockKandangIDs: []uint{101},
|
|
})
|
|
|
|
if sql != sapronakIncomingPurchasesScopedSQL() {
|
|
t.Fatalf("expected scoped purchase SQL, got %q", sql)
|
|
}
|
|
if len(args) != 11 {
|
|
t.Fatalf("expected 11 argument groups, got %d", len(args))
|
|
}
|
|
}
|
|
|
|
func TestFetchSapronakIncomingIncludesAttributedFarmPurchasesAndHistoricalWarehouseFallback(t *testing.T) {
|
|
db := setupClosingRepositoryTestDB(t)
|
|
repo := NewClosingRepository(db)
|
|
ctx := context.Background()
|
|
|
|
receivedAt := time.Date(2026, 4, 1, 4, 0, 0, 0, time.UTC)
|
|
statements := []string{
|
|
`INSERT INTO warehouses (id, kandang_id) VALUES (1, NULL), (2, 59), (3, 88)`,
|
|
`INSERT INTO product_categories (id, code) VALUES (1, 'OBT'), (2, 'RAW')`,
|
|
`INSERT INTO products (id, name, product_category_id, product_price) VALUES
|
|
(10, 'MEFISTO @1 LITER', 1, 261700),
|
|
(20, 'PAKAN GROWING CRUMBLE MALINDO', 2, 15000)`,
|
|
`INSERT INTO flags (id, flagable_id, flagable_type, name) VALUES
|
|
(1, 10, 'products', 'OVK'),
|
|
(2, 10, 'products', 'OBAT')`,
|
|
`INSERT INTO purchases (id, po_number, deleted_at) VALUES (1, 'PO-LTI-0005', NULL)`,
|
|
`INSERT INTO recordings (id, project_flock_kandangs_id, deleted_at) VALUES (11, 101, NULL), (12, 999, NULL)`,
|
|
`INSERT INTO recording_stocks (id, recording_id, product_warehouse_id, usage_qty, project_flock_kandang_id) VALUES (21, 11, 501, 150, 101), (22, 12, 502, 10, 999)`,
|
|
`INSERT INTO purchase_items (id, purchase_id, product_id, warehouse_id, project_flock_kandang_id, total_qty, price, received_date) VALUES
|
|
(1, 1, 10, 1, NULL, 100, 261700, '` + receivedAt.Format(time.RFC3339) + `'),
|
|
(2, 1, 20, 1, NULL, 50, 15000, '` + receivedAt.Format(time.RFC3339) + `'),
|
|
(3, 1, 20, 2, NULL, 25, 12000, '` + receivedAt.Format(time.RFC3339) + `'),
|
|
(4, 1, 10, 3, 999, 10, 261700, '` + receivedAt.Format(time.RFC3339) + `'),
|
|
(5, 1, 20, 1, NULL, 40, 15000, '` + receivedAt.Format(time.RFC3339) + `')`,
|
|
fmt.Sprintf(`INSERT INTO stock_allocations (id, product_warehouse_id, stockable_type, stockable_id, usable_type, usable_id, qty, allocation_purpose, status) VALUES
|
|
(1, 701, '%s', 1, '%s', 21, 100, 'CONSUME', 'ACTIVE'),
|
|
(2, 702, '%s', 2, '%s', 21, 50, 'CONSUME', 'ACTIVE'),
|
|
(3, 703, '%s', 5, '%s', 22, 40, 'CONSUME', 'ACTIVE')`,
|
|
fifo.StockableKeyPurchaseItems.String(),
|
|
fifo.UsableKeyRecordingStock.String(),
|
|
fifo.StockableKeyPurchaseItems.String(),
|
|
fifo.UsableKeyRecordingStock.String(),
|
|
fifo.StockableKeyPurchaseItems.String(),
|
|
fifo.UsableKeyRecordingStock.String(),
|
|
),
|
|
}
|
|
for _, stmt := range statements {
|
|
if err := db.Exec(stmt).Error; err != nil {
|
|
t.Fatalf("failed seeding schema: %v", err)
|
|
}
|
|
}
|
|
|
|
rows, err := repo.FetchSapronakIncoming(ctx, 101, 59, nil, nil)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if len(rows) != 2 {
|
|
t.Fatalf("expected 2 sapronak rows, got %d", len(rows))
|
|
}
|
|
|
|
byProduct := make(map[uint]SapronakIncomingRow, len(rows))
|
|
for _, row := range rows {
|
|
byProduct[row.ProductID] = row
|
|
}
|
|
|
|
if got := byProduct[10]; got.ProductID == 0 || got.Flag != "OVK" || got.Qty != 100 {
|
|
t.Fatalf("expected OVK farm purchase qty 100 for product 10, got %+v", got)
|
|
}
|
|
|
|
if got := byProduct[20]; got.ProductID == 0 || got.Flag != "PAKAN" || got.Qty != 75 {
|
|
t.Fatalf("expected PAKAN total qty 75 including farm allocated qty 50 and kandang receipt qty 25, got %+v", got)
|
|
}
|
|
}
|
|
|
|
func TestFetchSapronakAdjustmentsUsesAdjustmentReferenceAndPrice(t *testing.T) {
|
|
db := setupClosingRepositoryTestDB(t)
|
|
repo := NewClosingRepository(db)
|
|
ctx := context.Background()
|
|
|
|
statements := []string{
|
|
`INSERT INTO warehouses (id, kandang_id) VALUES (5, 5)`,
|
|
`INSERT INTO product_categories (id, code) VALUES (1, 'OBT')`,
|
|
`INSERT INTO products (id, name, product_category_id, product_price) VALUES (17, 'OVK CUT-OVER', 1, 1)`,
|
|
`INSERT INTO flags (id, flagable_id, flagable_type, name) VALUES (1, 17, 'products', 'OVK')`,
|
|
`INSERT INTO product_warehouses (id, product_id, warehouse_id, project_flock_kandang_id) VALUES (1365, 17, 5, 66)`,
|
|
`INSERT INTO adjustment_stocks (id, product_warehouse_id, total_qty, usage_qty, price) VALUES
|
|
(1139, 1365, 1, 0, 298594487),
|
|
(1140, 1365, 0, 1, 298594487)`,
|
|
fmt.Sprintf(`INSERT INTO stock_allocations (id, product_warehouse_id, stockable_type, stockable_id, usable_type, usable_id, qty, allocation_purpose, status) VALUES
|
|
(25990, 1365, '%s', 1139, '%s', 1140, 1, 'CONSUME', 'ACTIVE')`,
|
|
fifo.StockableKeyAdjustmentIn.String(),
|
|
fifo.UsableKeyAdjustmentOut.String(),
|
|
),
|
|
}
|
|
for _, stmt := range statements {
|
|
if err := db.Exec(stmt).Error; err != nil {
|
|
t.Fatalf("failed seeding schema: %v", err)
|
|
}
|
|
}
|
|
|
|
incoming, outgoing, err := repo.FetchSapronakAdjustments(ctx, 5, nil, nil)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
|
|
incomingRows := incoming[17]
|
|
if len(incomingRows) != 1 {
|
|
t.Fatalf("expected 1 incoming row for product 17, got %d", len(incomingRows))
|
|
}
|
|
if incomingRows[0].Reference != "ADJ-1139" {
|
|
t.Fatalf("expected incoming reference ADJ-1139, got %q", incomingRows[0].Reference)
|
|
}
|
|
if incomingRows[0].Price != 298594487 {
|
|
t.Fatalf("expected incoming price 298594487 from adjustment_stocks.price, got %.3f", incomingRows[0].Price)
|
|
}
|
|
|
|
outgoingRows := outgoing[17]
|
|
if len(outgoingRows) != 1 {
|
|
t.Fatalf("expected 1 outgoing row for product 17, got %d", len(outgoingRows))
|
|
}
|
|
if outgoingRows[0].Reference != "ADJ-1139" {
|
|
t.Fatalf("expected outgoing reference ADJ-1139, got %q", outgoingRows[0].Reference)
|
|
}
|
|
if outgoingRows[0].Reference == "CHICKIN-" {
|
|
t.Fatalf("expected outgoing reference to avoid CHICKIN- placeholder")
|
|
}
|
|
if outgoingRows[0].Price != 298594487 {
|
|
t.Fatalf("expected outgoing price 298594487 from adjustment_stocks.price, got %.3f", outgoingRows[0].Price)
|
|
}
|
|
}
|
|
|
|
func setupClosingRepositoryTestDB(t *testing.T) *gorm.DB {
|
|
t.Helper()
|
|
|
|
db, err := gorm.Open(sqlite.Open("file:"+t.Name()+"?mode=memory&cache=private"), &gorm.Config{})
|
|
if err != nil {
|
|
t.Fatalf("failed opening sqlite db: %v", err)
|
|
}
|
|
|
|
statements := []string{
|
|
`CREATE TABLE warehouses (
|
|
id INTEGER PRIMARY KEY,
|
|
kandang_id INTEGER NULL
|
|
)`,
|
|
`CREATE TABLE product_categories (
|
|
id INTEGER PRIMARY KEY,
|
|
code TEXT NOT NULL
|
|
)`,
|
|
`CREATE TABLE uoms (
|
|
id INTEGER PRIMARY KEY,
|
|
name TEXT NOT NULL
|
|
)`,
|
|
`CREATE TABLE products (
|
|
id INTEGER PRIMARY KEY,
|
|
name TEXT NOT NULL,
|
|
product_category_id INTEGER NULL,
|
|
uom_id INTEGER NULL,
|
|
product_price NUMERIC(15,3) NOT NULL DEFAULT 0
|
|
)`,
|
|
`CREATE TABLE flags (
|
|
id INTEGER PRIMARY KEY,
|
|
flagable_id INTEGER NOT NULL,
|
|
flagable_type TEXT NOT NULL,
|
|
name TEXT NOT NULL
|
|
)`,
|
|
`CREATE TABLE purchases (
|
|
id INTEGER PRIMARY KEY,
|
|
po_number TEXT NULL,
|
|
notes TEXT NULL,
|
|
deleted_at TIMESTAMP NULL
|
|
)`,
|
|
`CREATE TABLE purchase_items (
|
|
id INTEGER PRIMARY KEY,
|
|
purchase_id INTEGER NOT NULL,
|
|
product_id INTEGER NOT NULL,
|
|
warehouse_id INTEGER NOT NULL,
|
|
product_warehouse_id INTEGER NULL,
|
|
project_flock_kandang_id INTEGER NULL,
|
|
total_qty NUMERIC(15,3) NOT NULL DEFAULT 0,
|
|
price NUMERIC(15,3) NOT NULL DEFAULT 0,
|
|
received_date TIMESTAMP NULL
|
|
)`,
|
|
`CREATE TABLE recordings (
|
|
id INTEGER PRIMARY KEY,
|
|
project_flock_kandangs_id INTEGER NOT NULL,
|
|
deleted_at TIMESTAMP NULL
|
|
)`,
|
|
`CREATE TABLE recording_stocks (
|
|
id INTEGER PRIMARY KEY,
|
|
recording_id INTEGER NOT NULL,
|
|
product_warehouse_id INTEGER NOT NULL,
|
|
usage_qty NUMERIC(15,3) NOT NULL DEFAULT 0,
|
|
project_flock_kandang_id INTEGER NULL
|
|
)`,
|
|
`CREATE TABLE project_chickins (
|
|
id INTEGER PRIMARY KEY,
|
|
project_flock_kandang_id INTEGER NOT NULL,
|
|
chick_in_date TIMESTAMP NULL
|
|
)`,
|
|
`CREATE TABLE project_flock_populations (
|
|
id INTEGER PRIMARY KEY,
|
|
project_chickin_id INTEGER NULL,
|
|
product_warehouse_id INTEGER NULL
|
|
)`,
|
|
`CREATE TABLE stock_allocations (
|
|
id INTEGER PRIMARY KEY,
|
|
product_warehouse_id INTEGER NOT NULL,
|
|
stockable_type TEXT NOT NULL,
|
|
stockable_id INTEGER NOT NULL,
|
|
usable_type TEXT NOT NULL,
|
|
usable_id INTEGER NOT NULL,
|
|
qty NUMERIC(15,3) NOT NULL DEFAULT 0,
|
|
allocation_purpose TEXT NOT NULL,
|
|
status TEXT NOT NULL
|
|
)`,
|
|
`CREATE TABLE product_warehouses (
|
|
id INTEGER PRIMARY KEY,
|
|
product_id INTEGER NOT NULL,
|
|
warehouse_id INTEGER NOT NULL,
|
|
project_flock_kandang_id INTEGER NULL
|
|
)`,
|
|
`CREATE TABLE stock_transfers (
|
|
id INTEGER PRIMARY KEY,
|
|
from_warehouse_id INTEGER NULL,
|
|
to_warehouse_id INTEGER NULL,
|
|
transfer_date TIMESTAMP NULL,
|
|
movement_number TEXT NULL,
|
|
reason TEXT NULL
|
|
)`,
|
|
`CREATE TABLE laying_transfers (
|
|
id INTEGER PRIMARY KEY,
|
|
transfer_date TIMESTAMP NULL,
|
|
transfer_number TEXT NULL
|
|
)`,
|
|
`CREATE TABLE laying_transfer_targets (
|
|
id INTEGER PRIMARY KEY,
|
|
laying_transfer_id INTEGER NOT NULL,
|
|
product_warehouse_id INTEGER NULL
|
|
)`,
|
|
`CREATE TABLE stock_transfer_details (
|
|
id INTEGER PRIMARY KEY,
|
|
stock_transfer_id INTEGER NOT NULL,
|
|
product_id INTEGER NOT NULL,
|
|
dest_product_warehouse_id INTEGER NULL,
|
|
source_product_warehouse_id INTEGER NULL,
|
|
total_qty NUMERIC(15,3) NOT NULL DEFAULT 0,
|
|
usage_qty NUMERIC(15,3) NOT NULL DEFAULT 0
|
|
)`,
|
|
`CREATE TABLE adjustment_stocks (
|
|
id INTEGER PRIMARY KEY,
|
|
product_warehouse_id INTEGER NOT NULL,
|
|
total_qty NUMERIC(15,3) NOT NULL DEFAULT 0,
|
|
usage_qty NUMERIC(15,3) NOT NULL DEFAULT 0,
|
|
price NUMERIC(15,3) NOT NULL DEFAULT 0,
|
|
adj_number TEXT NULL,
|
|
created_at TIMESTAMP NULL
|
|
)`,
|
|
}
|
|
|
|
for _, stmt := range statements {
|
|
if err := db.Exec(stmt).Error; err != nil {
|
|
t.Fatalf("failed preparing schema: %v", err)
|
|
}
|
|
}
|
|
|
|
return db
|
|
}
|