139 lines
4.5 KiB
Go
139 lines
4.5 KiB
Go
package repository
|
|
|
|
import (
|
|
"regexp"
|
|
"testing"
|
|
|
|
"github.com/DATA-DOG/go-sqlmock"
|
|
"github.com/shopspring/decimal"
|
|
"github.com/wmms/wmms/pkg/database"
|
|
)
|
|
|
|
const stockByMaterialSQL = `SELECT m.id, m.material_code, m.name, m.spec, m.unit,
|
|
COALESCE(SUM(ib.on_hand), 0), COALESCE(MAX(swr.minimum_available), 0),
|
|
IFNULL(pc.code, ''), IFNULL(c.code, '')
|
|
FROM materials m
|
|
LEFT JOIN inventory_balances ib ON ib.material_id = m.id
|
|
LEFT JOIN stock_warning_rules swr ON swr.organization_id = ib.organization_id
|
|
AND swr.warehouse_id = ib.warehouse_id AND swr.material_id = m.id AND swr.enabled = TRUE
|
|
LEFT JOIN material_categories c ON c.id = m.category_id
|
|
LEFT JOIN material_categories pc ON pc.id = c.parent_id
|
|
WHERE m.id = ?
|
|
GROUP BY m.id, m.material_code, m.name, m.spec, m.unit, pc.code, c.code`
|
|
|
|
func TestStockRepoGetByMaterialIDReadsInventoryTables(t *testing.T) {
|
|
db, mock, err := sqlmock.New()
|
|
if err != nil {
|
|
t.Fatalf("open sql mock: %v", err)
|
|
}
|
|
t.Cleanup(func() {
|
|
database.DB = nil
|
|
db.Close()
|
|
})
|
|
database.DB = db
|
|
|
|
mock.ExpectQuery(regexp.QuoteMeta(stockByMaterialSQL)).WithArgs(int64(9)).WillReturnRows(
|
|
sqlmock.NewRows([]string{"id", "material_code", "name", "spec", "unit", "on_hand", "minimum_available", "major_category", "minor_category"}).
|
|
AddRow(9, "MAT-009", "电机", "80W", "台", "12.500000", "3.000000", "B01", "CAT-SUB-027"),
|
|
)
|
|
|
|
stock, err := NewStockRepo().GetByMaterialID(9)
|
|
if err != nil {
|
|
t.Fatalf("get stock: %v", err)
|
|
}
|
|
if !stock.TotalOnHand.Equal(decimal.RequireFromString("12.500000")) || !stock.WarningStock.Equal(decimal.NewFromInt(3)) {
|
|
t.Fatalf("unexpected stock quantities: %+v", stock)
|
|
}
|
|
if stock.MajorCategory != "B01" || stock.MinorCategory != "CAT-SUB-027" {
|
|
t.Fatalf("unexpected stock categories: %+v", stock)
|
|
}
|
|
if err := mock.ExpectationsWereMet(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestStockRepoGetStockPreservesDecimalQuantity(t *testing.T) {
|
|
db, mock, err := sqlmock.New()
|
|
if err != nil {
|
|
t.Fatalf("open sql mock: %v", err)
|
|
}
|
|
t.Cleanup(func() {
|
|
database.DB = nil
|
|
db.Close()
|
|
})
|
|
database.DB = db
|
|
|
|
query := `SELECT COALESCE(SUM(on_hand), 0) FROM inventory_balances WHERE material_id = ?`
|
|
mock.ExpectQuery(regexp.QuoteMeta(query)).WithArgs(int64(9)).WillReturnRows(
|
|
sqlmock.NewRows([]string{"on_hand"}).AddRow("1500.250000"),
|
|
)
|
|
|
|
stock, err := NewStockRepo().GetStock(9)
|
|
if err != nil {
|
|
t.Fatalf("get stock: %v", err)
|
|
}
|
|
if !stock.Equal(decimal.RequireFromString("1500.250000")) {
|
|
t.Fatalf("stock = %s, want 1500.250000", stock)
|
|
}
|
|
}
|
|
|
|
func TestStockRepoUpdateStockTxWritesDefaultInventoryBalance(t *testing.T) {
|
|
db, mock, err := sqlmock.New()
|
|
if err != nil {
|
|
t.Fatalf("open sql mock: %v", err)
|
|
}
|
|
t.Cleanup(func() {
|
|
database.DB = nil
|
|
db.Close()
|
|
})
|
|
database.DB = db
|
|
|
|
mock.ExpectBegin()
|
|
tx, err := db.Begin()
|
|
if err != nil {
|
|
t.Fatalf("begin: %v", err)
|
|
}
|
|
query := `INSERT INTO inventory_balances (organization_id, warehouse_id, location_id, material_id, on_hand)
|
|
SELECT o.id, w.id, l.id, ?, ?
|
|
FROM organizations o
|
|
JOIN warehouses w ON w.organization_id = o.id AND w.code = 'DEFAULT'
|
|
JOIN warehouse_locations l ON l.warehouse_id = w.id AND l.code = 'MAIN'
|
|
WHERE o.code = 'DEFAULT'
|
|
ON DUPLICATE KEY UPDATE on_hand = on_hand + VALUES(on_hand), version = version + 1`
|
|
mock.ExpectExec(regexp.QuoteMeta(query)).WithArgs(int64(11), "5.5").WillReturnResult(sqlmock.NewResult(1, 1))
|
|
|
|
if err := NewStockRepo().UpdateStockTx(tx, 11, decimal.RequireFromString("5.500000")); err != nil {
|
|
t.Fatalf("update inventory balance: %v", err)
|
|
}
|
|
if err := mock.ExpectationsWereMet(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestStockRepoUpdateWarningStockWritesInventoryRule(t *testing.T) {
|
|
db, mock, err := sqlmock.New()
|
|
if err != nil {
|
|
t.Fatalf("open sql mock: %v", err)
|
|
}
|
|
t.Cleanup(func() {
|
|
database.DB = nil
|
|
db.Close()
|
|
})
|
|
database.DB = db
|
|
|
|
query := `INSERT INTO stock_warning_rules (organization_id, warehouse_id, material_id, minimum_available, enabled)
|
|
SELECT o.id, w.id, ?, ?, TRUE
|
|
FROM organizations o
|
|
JOIN warehouses w ON w.organization_id = o.id AND w.code = 'DEFAULT'
|
|
WHERE o.code = 'DEFAULT'
|
|
ON DUPLICATE KEY UPDATE minimum_available = VALUES(minimum_available), enabled = TRUE`
|
|
mock.ExpectExec(regexp.QuoteMeta(query)).WithArgs(int64(13), "8.75").WillReturnResult(sqlmock.NewResult(1, 1))
|
|
|
|
if err := NewStockRepo().UpdateWarningStock(13, decimal.RequireFromString("8.750000")); err != nil {
|
|
t.Fatalf("update stock warning: %v", err)
|
|
}
|
|
if err := mock.ExpectationsWereMet(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|