diff --git a/svc/queries/order_write.go b/svc/queries/order_write.go index 1657c39..fbd7b53 100644 --- a/svc/queries/order_write.go +++ b/svc/queries/order_write.go @@ -485,6 +485,54 @@ func normalizeDim1Token(s string) string { return s } +func variantCacheKey(item, color, dim2 string) string { + return item + "||" + color + "||" + dim2 +} + +func loadVariantDim1SetTx(tx *sql.Tx, item, color, dim2 string) (map[string]struct{}, error) { + rows, err := tx.Query(` + SELECT ISNULL(LTRIM(RTRIM(V.ItemDim1Code)),'') AS ItemDim1Code + FROM BAGGI_V3.dbo.prItemVariant V WITH (NOLOCK) + WHERE ISNULL(LTRIM(RTRIM(V.ItemCode)),'') = @p1 + AND ( + ( + ISNULL(LTRIM(RTRIM(V.ColorCode)),'') = @p2 + AND ( + ISNULL(LTRIM(RTRIM(@p3)),'') = '' + OR ISNULL(LTRIM(RTRIM(V.ItemDim2Code)),'') = @p3 + ) + ) + OR ( + ISNULL(LTRIM(RTRIM(@p3)),'') = '' + AND ISNULL(LTRIM(RTRIM(V.ItemDim2Code)),'') = @p2 + ) + ) + `, item, color, dim2) + if err != nil { + return nil, fmt.Errorf("variant set query hatası: %w", err) + } + defer rows.Close() + + set := make(map[string]struct{}) + for rows.Next() { + var raw string + if err := rows.Scan(&raw); err != nil { + return nil, fmt.Errorf("variant set scan hatası: %w", err) + } + norm := normalizeDim1Token(raw) + if norm != "" { + set[norm] = struct{}{} + } + if num := normalizeNumericToken(norm); num != "" { + set["#NUM:"+num] = struct{}{} + } + } + if err := rows.Err(); err != nil { + return nil, fmt.Errorf("variant set rows hatası: %w", err) + } + return set, nil +} + // ======================================================= // AKSBIR DETECTION // ======================================================= @@ -495,6 +543,10 @@ func normalizeDim1Token(s string) string { // Variant check: ItemCode + ColorCode + Dim1 + Dim2 func ValidateItemVariant(tx *sql.Tx, ln models.OrderDetail) error { + return ValidateItemVariantCached(tx, ln, nil) +} + +func ValidateItemVariantCached(tx *sql.Tx, ln models.OrderDetail, cache map[string]map[string]struct{}) error { fmt.Printf( "🧪 VARIANT GUARD INPUT | ClientKey=%s Item=%q Color=%q Dim1=%q Dim2=%q Dim3=%q Qty1=%v\n", safeNS(ln.ClientKey), @@ -527,56 +579,42 @@ func ValidateItemVariant(tx *sql.Tx, ln models.OrderDetail) error { dim1Norm := normalizeDim1Token(dim1) dim1Numeric := normalizeNumericToken(dim1Norm) + fmt.Printf( + "🧪 VARIANT NORMALIZED | Item=%q Color=%q Dim1=%q Dim2=%q\n", + item, color, dim1Norm, dim2, + ) + if item == "" { - return fmt.Errorf( - "ItemCode boş olamaz (ClientKey=%s)", - safeNS(ln.ClientKey), - ) - fmt.Printf( - "🧪 VARIANT NORMALIZED | Item=%q Color=%q Dim1=%q Dim2=%q\n", - item, color, dim1, dim2, - ) - + return &models.ValidationError{ + Code: "INVALID_ITEM_VARIANT", + Message: "Tanımsız ürün kombinasyonu", + ClientKey: safeNS(ln.ClientKey), + ItemCode: item, + ColorCode: color, + Dim1: dim1, + Dim2: dim2, + } } - var exists int - err := tx.QueryRow(` - SELECT CASE WHEN EXISTS ( - SELECT 1 - FROM BAGGI_V3.dbo.prItemVariant V WITH (NOLOCK) - CROSS APPLY ( - SELECT UPPER(REPLACE(REPLACE(REPLACE(ISNULL(LTRIM(RTRIM(V.ItemDim1Code)),'') ,' ', ''), 'YAS', ''), 'Y', '')) AS Dim1Norm - ) X - WHERE ISNULL(LTRIM(RTRIM(V.ItemCode)),'') = @p1 - AND ( - ( - ISNULL(LTRIM(RTRIM(V.ColorCode)),'') = @p2 - AND ( - ISNULL(LTRIM(RTRIM(@p4)),'') = '' - OR ISNULL(LTRIM(RTRIM(V.ItemDim2Code)),'') = @p4 - ) - ) - OR ( - ISNULL(LTRIM(RTRIM(@p4)),'') = '' - AND ISNULL(LTRIM(RTRIM(V.ItemDim2Code)),'') = @p2 - ) - ) - AND ( - X.Dim1Norm = @p3 - OR ( - ISNULL(LTRIM(RTRIM(@p5)),'') <> '' - AND X.Dim1Norm NOT LIKE '%[^0-9]%' - AND SUBSTRING(X.Dim1Norm, PATINDEX('%[^0]%', X.Dim1Norm + '0'), LEN(X.Dim1Norm)) = @p5 - ) - ) - ) THEN 1 ELSE 0 END - `, item, color, dim1Norm, dim2, dim1Numeric).Scan(&exists) - - if err != nil { - return fmt.Errorf("ItemVariant kontrol query hatası: %w", err) + key := variantCacheKey(item, color, dim2) + set := map[string]struct{}(nil) + if cache != nil { + set = cache[key] + } + if set == nil { + var err error + set, err = loadVariantDim1SetTx(tx, item, color, dim2) + if err != nil { + return fmt.Errorf("ItemVariant kontrol query hatası: %w", err) + } + if cache != nil { + cache[key] = set + } } - if exists != 1 { + _, okNorm := set[dim1Norm] + _, okNum := set["#NUM:"+dim1Numeric] + if !(okNorm || (dim1Numeric != "" && okNum)) { return &models.ValidationError{ Code: "INVALID_ITEM_VARIANT", Message: "Tanımsız ürün kombinasyonu", @@ -998,6 +1036,7 @@ VALUES ( defer insStmt.Close() lineResults := make([]OrderLineResult, 0, len(lines)) + variantCache := make(map[string]map[string]struct{}) // ✅ Duplicate Guard (payload içi) seenCombo := make(map[string]bool) @@ -1045,7 +1084,7 @@ VALUES ( // ✅ INSERT ÖNCESİ ItemVariant GUARD if qtyValue(ln.Qty1) > 0 { - if err := ValidateItemVariant(tx, ln); err != nil { + if err := ValidateItemVariantCached(tx, ln, variantCache); err != nil { fmt.Println("❌ VARIANT GUARD (INSERT):", err) return "", nil, err } @@ -1440,6 +1479,7 @@ WHERE OrderLineID=@p42 AND ISNULL(IsClosed,0)=0`) // LOOP // ====================================================== lineResults := make([]OrderLineResult, 0) + variantCache := make(map[string]map[string]struct{}) seenCombo := make(map[string]bool) for _, ln := range lines { @@ -1555,7 +1595,7 @@ WHERE OrderLineID=@p42 AND ISNULL(IsClosed,0)=0`) // Variant guard if qtyValue(ln.Qty1) > 0 { - if err := ValidateItemVariant(tx, ln); err != nil { + if err := ValidateItemVariantCached(tx, ln, variantCache); err != nil { return nil, err } }