package services import ( "testing" "time" "git.mchus.pro/mchus/quoteforge/internal/models" "git.mchus.pro/mchus/quoteforge/internal/repository" "github.com/glebarez/sqlite" "gorm.io/gorm" ) func TestCalculatePriceLevels_WithMissingLevel(t *testing.T) { db := newPriceLevelsTestDB(t) repo := repository.NewPricelistRepository(db) service := NewQuoteService(nil, nil, repo, nil, nil) estimate := seedPricelistWithItem(t, repo, "estimate", "CPU_X", 100) _ = estimate seedPricelistWithItem(t, repo, "warehouse", "CPU_X", 120) result, err := service.CalculatePriceLevels(&PriceLevelsRequest{ Items: []struct { LotName string `json:"lot_name"` Quantity int `json:"quantity"` }{ {LotName: "CPU_X", Quantity: 2}, }, }) if err != nil { t.Fatalf("CalculatePriceLevels returned error: %v", err) } if len(result.Items) != 1 { t.Fatalf("expected 1 item, got %d", len(result.Items)) } item := result.Items[0] if item.EstimatePrice == nil || *item.EstimatePrice != 100 { t.Fatalf("expected estimate 100, got %#v", item.EstimatePrice) } if item.WarehousePrice == nil || *item.WarehousePrice != 120 { t.Fatalf("expected warehouse 120, got %#v", item.WarehousePrice) } if item.CompetitorPrice != nil { t.Fatalf("expected competitor nil, got %#v", item.CompetitorPrice) } if len(item.PriceMissing) != 1 || item.PriceMissing[0] != "competitor" { t.Fatalf("expected price_missing [competitor], got %#v", item.PriceMissing) } if item.DeltaWhEstimateAbs == nil || *item.DeltaWhEstimateAbs != 20 { t.Fatalf("expected delta abs 20, got %#v", item.DeltaWhEstimateAbs) } if item.DeltaWhEstimatePct == nil || *item.DeltaWhEstimatePct != 20 { t.Fatalf("expected delta pct 20, got %#v", item.DeltaWhEstimatePct) } } func TestCalculatePriceLevels_UsesExplicitPricelistIDs(t *testing.T) { db := newPriceLevelsTestDB(t) repo := repository.NewPricelistRepository(db) service := NewQuoteService(nil, nil, repo, nil, nil) olderEstimate := seedPricelistWithItem(t, repo, "estimate", "CPU_Y", 80) seedPricelistWithItem(t, repo, "estimate", "CPU_Y", 90) result, err := service.CalculatePriceLevels(&PriceLevelsRequest{ Items: []struct { LotName string `json:"lot_name"` Quantity int `json:"quantity"` }{ {LotName: "CPU_Y", Quantity: 1}, }, PricelistIDs: map[string]uint{ "estimate": olderEstimate.ID, }, }) if err != nil { t.Fatalf("CalculatePriceLevels returned error: %v", err) } item := result.Items[0] if item.EstimatePrice == nil || *item.EstimatePrice != 80 { t.Fatalf("expected explicit estimate 80, got %#v", item.EstimatePrice) } } func newPriceLevelsTestDB(t *testing.T) *gorm.DB { t.Helper() db, err := gorm.Open(sqlite.Open("file::memory:?cache=shared"), &gorm.Config{}) if err != nil { t.Fatalf("open sqlite: %v", err) } if err := db.AutoMigrate(&models.Pricelist{}, &models.PricelistItem{}); err != nil { t.Fatalf("migrate: %v", err) } return db } func seedPricelistWithItem(t *testing.T, repo *repository.PricelistRepository, source, lot string, price float64) *models.Pricelist { t.Helper() version, err := repo.GenerateVersionBySource(source) if err != nil { t.Fatalf("GenerateVersionBySource: %v", err) } expiresAt := time.Now().Add(24 * time.Hour) pl := &models.Pricelist{ Source: source, Version: version, CreatedBy: "test", IsActive: true, ExpiresAt: &expiresAt, } if err := repo.Create(pl); err != nil { t.Fatalf("create pricelist: %v", err) } if err := repo.CreateItems([]models.PricelistItem{ { PricelistID: pl.ID, LotName: lot, Price: price, }, }); err != nil { t.Fatalf("create items: %v", err) } return pl }