package warehouse import ( "math" "slices" "testing" "time" "git.mchus.pro/mchus/priceforge/internal/models" "github.com/glebarez/sqlite" "gorm.io/gorm" ) func TestComputePricelistItemsFromStockLog(t *testing.T) { db := openTestDB(t) if err := db.AutoMigrate(&models.Lot{}, &models.LotPartnumber{}, &models.StockLog{}); err != nil { t.Fatalf("automigrate: %v", err) } if err := db.Create(&models.Lot{LotName: "CPU_X"}).Error; err != nil { t.Fatalf("seed lot: %v", err) } if err := db.Create(&models.LotPartnumber{Partnumber: "PN-CPU-X", LotName: "CPU_X"}).Error; err != nil { t.Fatalf("seed mapping: %v", err) } qtySmall := 1.0 qtyBig := 9.0 now := time.Now() rows := []models.StockLog{ {Partnumber: "PN CPU X", Date: now, Price: 100, Qty: &qtySmall}, {Partnumber: "CPU_X-EXTRA", Date: now, Price: 200, Qty: &qtyBig}, } if err := db.Create(&rows).Error; err != nil { t.Fatalf("seed stock rows: %v", err) } items, err := ComputePricelistItemsFromStockLog(db) if err != nil { t.Fatalf("ComputePricelistItemsFromStockLog: %v", err) } if len(items) != 1 { t.Fatalf("expected 1 item, got %d", len(items)) } if items[0].LotName != "CPU_X" { t.Fatalf("expected lot CPU_X, got %s", items[0].LotName) } if math.Abs(items[0].Price-200) > 0.001 { t.Fatalf("expected weighted median 200, got %f", items[0].Price) } } func TestLoadLotMetricsLatestOnlyIncludesPartnumbers(t *testing.T) { db := openTestDB(t) if err := db.AutoMigrate(&models.Lot{}, &models.LotPartnumber{}, &models.StockLog{}); err != nil { t.Fatalf("automigrate: %v", err) } if err := db.Create(&models.Lot{LotName: "CPU_X"}).Error; err != nil { t.Fatalf("seed lot: %v", err) } if err := db.Create(&models.LotPartnumber{Partnumber: "PN-MAPPED", LotName: "CPU_X"}).Error; err != nil { t.Fatalf("seed mapping: %v", err) } oldDate := time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC) newDate := time.Date(2026, 1, 2, 0, 0, 0, 0, time.UTC) oldQty := 10.0 newQty := 3.0 rows := []models.StockLog{ {Partnumber: "CPU_X-001", Date: oldDate, Price: 100, Qty: &oldQty}, {Partnumber: "CPU_X-001", Date: newDate, Price: 100, Qty: &newQty}, } if err := db.Create(&rows).Error; err != nil { t.Fatalf("seed stock rows: %v", err) } qtyByLot, pnsByLot, err := LoadLotMetrics(db, []string{"CPU_X"}, true) if err != nil { t.Fatalf("LoadLotMetrics: %v", err) } if got := qtyByLot["CPU_X"]; math.Abs(got-3.0) > 0.001 { t.Fatalf("expected latest qty 3, got %f", got) } pns := pnsByLot["CPU_X"] if !slices.Contains(pns, "PN-MAPPED") { t.Fatalf("expected mapped PN-MAPPED in partnumbers, got %v", pns) } if !slices.Contains(pns, "CPU_X-001") { t.Fatalf("expected stock CPU_X-001 in partnumbers, got %v", pns) } } func openTestDB(t *testing.T) *gorm.DB { t.Helper() db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{}) if err != nil { t.Fatalf("open sqlite: %v", err) } return db }