LOT из BOM-маппинга мог быть в смешанном регистре, а корзина — в каноничном UPPERCASE, из-за чего позиции дублировались (в таблице «Цена покупки» и в экспорте CSV). - localdb.NormalizeLotMappings: единая каноничная нормализация LOT-маппингов (UPPERCASE + схлопывание дублей с суммированием qty). Убраны две разошедшиеся копии normalizeLotMappings (handlers и services — последняя только тримила, что и было причиной бага в CSV). - export.go: BOM-ветка использует общую функцию + канонизирует LOT корзины для coverage/lookup. Удалена мёртвая computeMappingTotal. - index.html (renderPricingTab): сопоставление/дедуп LOT через каноничный ключ UPPERCASE; аксессоры _getRowBaseLot/_getRowAllocations возвращают канон. - Добавлен регресс-тест TestNormalizeLotMappings_*. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
65 lines
1.7 KiB
Go
65 lines
1.7 KiB
Go
package localdb
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"git.mchus.pro/mchus/quoteforge/internal/models"
|
|
)
|
|
|
|
func TestNormalizeLotMappings_CaseInsensitiveMerge(t *testing.T) {
|
|
in := []VendorSpecLotMapping{
|
|
{LotName: "cpu_intel_6960p", QuantityPerPN: 1},
|
|
{LotName: "CPU_INTEL_6960P", QuantityPerPN: 2},
|
|
{LotName: " ps_5200w_Titanium ", QuantityPerPN: 0},
|
|
{LotName: "", QuantityPerPN: 5},
|
|
}
|
|
|
|
out := NormalizeLotMappings(in)
|
|
|
|
if len(out) != 2 {
|
|
t.Fatalf("expected 2 merged mappings, got %d: %+v", len(out), out)
|
|
}
|
|
if out[0].LotName != "CPU_INTEL_6960P" || out[0].QuantityPerPN != 3 {
|
|
t.Fatalf("expected CPU_INTEL_6960P qty 3, got %+v", out[0])
|
|
}
|
|
if out[1].LotName != "PS_5200W_TITANIUM" || out[1].QuantityPerPN != 1 {
|
|
t.Fatalf("expected PS_5200W_TITANIUM qty 1 (clamped), got %+v", out[1])
|
|
}
|
|
}
|
|
|
|
func TestNormalizeLotMappings_Empty(t *testing.T) {
|
|
if NormalizeLotMappings(nil) != nil {
|
|
t.Fatal("expected nil for empty input")
|
|
}
|
|
if NormalizeLotMappings([]VendorSpecLotMapping{{LotName: " "}}) != nil {
|
|
t.Fatal("expected nil when all entries blank")
|
|
}
|
|
}
|
|
|
|
func TestPricelistItemToLocal_PreservesLotCategory(t *testing.T) {
|
|
item := &models.PricelistItem{
|
|
LotName: "CPU_A",
|
|
LotCategory: "CPU",
|
|
Price: 10,
|
|
}
|
|
|
|
local := PricelistItemToLocal(item, 123)
|
|
if local.LotCategory != "CPU" {
|
|
t.Fatalf("expected LotCategory=CPU, got %q", local.LotCategory)
|
|
}
|
|
}
|
|
|
|
func TestLocalToPricelistItem_PreservesLotCategory(t *testing.T) {
|
|
local := &LocalPricelistItem{
|
|
LotName: "CPU_A",
|
|
LotCategory: "CPU",
|
|
Price: 10,
|
|
}
|
|
|
|
item := LocalToPricelistItem(local, 456)
|
|
if item.LotCategory != "CPU" {
|
|
t.Fatalf("expected LotCategory=CPU, got %q", item.LotCategory)
|
|
}
|
|
}
|
|
|