diff --git a/svc/queries/product_pricing_dims_mssql.go b/svc/queries/product_pricing_dims_mssql.go index f292dfe..d5baf70 100644 --- a/svc/queries/product_pricing_dims_mssql.go +++ b/svc/queries/product_pricing_dims_mssql.go @@ -1,7 +1,7 @@ package queries // GetProductVariantDimsForPricing: -// Pull variant dimension combos from Nebim stock tables (same source as product-stock-query UI). +// Pull variant dimension combos from Nebim's variant master table. // We intentionally keep it small: only the keys we need to write dim-aware prices into PG sdprc. // // Note: Column semantics depend on your Nebim setup. We treat ItemDim1Code/ItemDim3Code as the @@ -10,16 +10,16 @@ const GetProductVariantDimsForPricing = ` DECLARE @ProductCode NVARCHAR(50) = @p1; SELECT DISTINCT - LTRIM(RTRIM(ISNULL(S.ColorCode,''))) AS ColorCode, - LTRIM(RTRIM(ISNULL(S.ItemDim1Code,''))) AS ItemDim1Code, - LTRIM(RTRIM(ISNULL(S.ItemDim3Code,''))) AS ItemDim3Code -FROM trStock S WITH(NOLOCK) -WHERE S.ItemTypeCode = 1 - AND S.ItemCode = @ProductCode - AND LEN(S.ItemCode) = 13 + LTRIM(RTRIM(ISNULL(V.ColorCode,''))) AS ColorCode, + LTRIM(RTRIM(ISNULL(V.ItemDim1Code,''))) AS ItemDim1Code, + LTRIM(RTRIM(ISNULL(V.ItemDim3Code,''))) AS ItemDim3Code +FROM prItemVariant V WITH(NOLOCK) +WHERE V.ItemTypeCode = 1 + AND V.ItemCode = @ProductCode + AND LEN(V.ItemCode) = 13 AND LEN(@ProductCode) = 13 ORDER BY - LTRIM(RTRIM(ISNULL(S.ColorCode,''))), - LTRIM(RTRIM(ISNULL(S.ItemDim1Code,''))), - LTRIM(RTRIM(ISNULL(S.ItemDim3Code,''))); + LTRIM(RTRIM(ISNULL(V.ColorCode,''))), + LTRIM(RTRIM(ISNULL(V.ItemDim1Code,''))), + LTRIM(RTRIM(ISNULL(V.ItemDim3Code,''))); ` diff --git a/svc/queries/wholesale_campaign_variants_mssql.go b/svc/queries/wholesale_campaign_variants_mssql.go index a40258d..a31400d 100644 --- a/svc/queries/wholesale_campaign_variants_mssql.go +++ b/svc/queries/wholesale_campaign_variants_mssql.go @@ -16,6 +16,45 @@ DECLARE @Codes NVARCHAR(MAX) = @p1; CROSS APPLY D.XmlData.nodes('/i') AS X(C) WHERE LTRIM(RTRIM(X.C.value('.', 'NVARCHAR(50)'))) <> '' ), +VARIANT_MASTER AS ( + SELECT + V.ItemCode, + LTRIM(RTRIM(ISNULL(V.ColorCode,''))) AS ColorCode, + LTRIM(RTRIM(ISNULL(V.ItemDim3Code,''))) AS ItemDim3Code, + MAX(LTRIM(RTRIM(ISNULL(V.ItemDim1Code,'')))) AS ItemDim1Code + FROM prItemVariant V WITH(NOLOCK) + JOIN INP ON INP.ItemCode = V.ItemCode + WHERE V.ItemTypeCode = 1 + AND LEN(V.ItemCode) = 13 + GROUP BY + V.ItemCode, V.ColorCode, V.ItemDim3Code +), +VARIANT_STOCK AS ( + SELECT + S.ItemCode, + LTRIM(RTRIM(ISNULL(S.ColorCode,''))) AS ColorCode, + LTRIM(RTRIM(ISNULL(S.ItemDim3Code,''))) AS ItemDim3Code, + MAX(LTRIM(RTRIM(ISNULL(S.ItemDim1Code,'')))) AS ItemDim1Code + FROM trStock S WITH(NOLOCK) + JOIN INP ON INP.ItemCode = S.ItemCode + WHERE S.ItemTypeCode = 1 + AND LEN(S.ItemCode) = 13 + GROUP BY + S.ItemCode, S.ColorCode, S.ItemDim3Code +), +VARIANT AS ( + SELECT + X.ItemCode, + X.ColorCode, + X.ItemDim3Code, + MAX(X.ItemDim1Code) AS ItemDim1Code + FROM ( + SELECT ItemCode, ColorCode, ItemDim3Code, ItemDim1Code FROM VARIANT_MASTER + UNION ALL + SELECT ItemCode, ColorCode, ItemDim3Code, ItemDim1Code FROM VARIANT_STOCK + ) X + GROUP BY X.ItemCode, X.ColorCode, X.ItemDim3Code +), STOCK AS ( SELECT S.ItemCode, @@ -73,27 +112,25 @@ DISP AS ( D.ItemCode, D.ColorCode, D.ItemDim3Code ) SELECT - S.ItemCode AS ItemCode, - S.ColorCode AS ColorCode, - S.ItemDim1Code AS ItemDim1Code, - S.ItemDim3Code AS ItemDim3Code, + V.ItemCode AS ItemCode, + V.ColorCode AS ColorCode, + V.ItemDim1Code AS ItemDim1Code, + V.ItemDim3Code AS ItemDim3Code, CAST(ROUND( - S.InventoryQty1 + ISNULL(S.InventoryQty1,0) - ISNULL(PK.PickingQty1,0) - ISNULL(RS.ReserveQty1,0) - ISNULL(DP.DispOrderQty1,0), 2 ) AS FLOAT) AS StockQty -FROM STOCK S +FROM VARIANT V +LEFT JOIN STOCK S + ON S.ItemCode=V.ItemCode AND S.ColorCode=V.ColorCode AND S.ItemDim3Code=V.ItemDim3Code LEFT JOIN PICK PK - ON PK.ItemCode=S.ItemCode AND PK.ColorCode=S.ColorCode AND PK.ItemDim3Code=S.ItemDim3Code + ON PK.ItemCode=V.ItemCode AND PK.ColorCode=V.ColorCode AND PK.ItemDim3Code=V.ItemDim3Code LEFT JOIN RESERVE RS - ON RS.ItemCode=S.ItemCode AND RS.ColorCode=S.ColorCode AND RS.ItemDim3Code=S.ItemDim3Code + ON RS.ItemCode=V.ItemCode AND RS.ColorCode=V.ColorCode AND RS.ItemDim3Code=V.ItemDim3Code LEFT JOIN DISP DP - ON DP.ItemCode=S.ItemCode AND DP.ColorCode=S.ColorCode AND DP.ItemDim3Code=S.ItemDim3Code -WHERE (S.InventoryQty1 - - ISNULL(PK.PickingQty1,0) - - ISNULL(RS.ReserveQty1,0) - - ISNULL(DP.DispOrderQty1,0)) <> 0 -ORDER BY S.ItemCode, S.ColorCode, S.ItemDim3Code; + ON DP.ItemCode=V.ItemCode AND DP.ColorCode=V.ColorCode AND DP.ItemDim3Code=V.ItemDim3Code +ORDER BY V.ItemCode, V.ColorCode, V.ItemDim3Code; ` diff --git a/svc/routes/first_group_mail_mapping.go b/svc/routes/first_group_mail_mapping.go index c5fa724..2af8468 100644 --- a/svc/routes/first_group_mail_mapping.go +++ b/svc/routes/first_group_mail_mapping.go @@ -53,9 +53,7 @@ CREATE TABLE IF NOT EXISTS mk_order_price_list_first_group_mail ( urun_ilk_grubu TEXT NOT NULL, mail_id UUID NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), - PRIMARY KEY (urun_ilk_grubu, mail_id), - CONSTRAINT fk_order_price_list_first_group_mail_mail - FOREIGN KEY (mail_id) REFERENCES mk_mail(id) ON DELETE CASCADE + PRIMARY KEY (urun_ilk_grubu, mail_id) ) `, `CREATE INDEX IF NOT EXISTS ix_order_price_list_first_group_mail_group ON mk_order_price_list_first_group_mail (urun_ilk_grubu)`, @@ -79,7 +77,7 @@ func GetCostingFirstGroupMailMappingLookupsHandler(pg *sql.DB) http.HandlerFunc return } if err := ensureFirstGroupMailMappingTables(pg); err != nil { - http.Error(w, "mapping table bootstrap error", http.StatusInternalServerError) + http.Error(w, "mapping table bootstrap error: "+err.Error(), http.StatusInternalServerError) return } @@ -156,7 +154,7 @@ func GetCostingFirstGroupMailMappingsHandler(pg *sql.DB) http.HandlerFunc { return } if err := ensureFirstGroupMailMappingTables(pg); err != nil { - http.Error(w, "mapping table bootstrap error", http.StatusInternalServerError) + http.Error(w, "mapping table bootstrap error: "+err.Error(), http.StatusInternalServerError) return } @@ -279,7 +277,7 @@ func GetPricingFirstGroupMailMappingsHandler(pg *sql.DB) http.HandlerFunc { return } if err := ensureFirstGroupMailMappingTables(pg); err != nil { - http.Error(w, "mapping table bootstrap error", http.StatusInternalServerError) + http.Error(w, "mapping table bootstrap error: "+err.Error(), http.StatusInternalServerError) return } @@ -531,7 +529,7 @@ func getFirstGroupMailMappingsByQuery(pg *sql.DB, mappingQuery string) http.Hand return } if err := ensureFirstGroupMailMappingTables(pg); err != nil { - http.Error(w, "mapping table bootstrap error", http.StatusInternalServerError) + http.Error(w, "mapping table bootstrap error: "+err.Error(), http.StatusInternalServerError) return } diff --git a/svc/routes/product_pricing_save.go b/svc/routes/product_pricing_save.go index 35483c9..5cbd383 100644 --- a/svc/routes/product_pricing_save.go +++ b/svc/routes/product_pricing_save.go @@ -619,16 +619,13 @@ DO UPDATE SET dim_id = EXCLUDED.dim_id, updated_at = EXCLUDED.updated_at if c.Dim1 <= 0 { continue } - v2 := int64(0) var v2any any = nil - if c.Dim3.Valid && c.Dim3.Int64 > 0 { - v2 = c.Dim3.Int64 - v2any = v2 - } - // If we managed to resolve an "ItemDim3Code" id too, store it in val3 and mark mmdim_id=3. + // Active key: val1=color, val3=itemdim3. val2 is size and is not part of price/campaign key. v3 := int64(0) - if extraVal3 != nil { - if vv, ok := extraVal3[fmt.Sprintf("%d|%d", c.Dim1, v2)]; ok && vv > 0 { + if c.Dim3.Valid && c.Dim3.Int64 > 0 { + v3 = c.Dim3.Int64 + } else if extraVal3 != nil { + if vv, ok := extraVal3[fmt.Sprintf("%d|0", c.Dim1)]; ok && vv > 0 { v3 = vv } } @@ -1071,18 +1068,38 @@ VALUES ( _ = upsertDimCombosCache(code, dims) // best-effort cache fill } - // 2) Last resort: MSSQL stock tokens, then seed mmitem_dim. Do not use - // mk_mmitem_dim_combo as a write source; stale cache rows can create wrong keys. - if len(dims) == 0 { - d, err := loadDimsFromMssqlStock(code) - if err != nil { - logger.Error("save:pg:dims:mssql:error", "product_code", code, "err", err) - } else { - dims = d - _ = upsertDimCombosCache(code, dims) - // If PG doesn't have mmitem_dim rows for this product yet, try to seed them. - ensureMMItemDimRows(mmItemID, dims, nil) + // 2) Merge MSSQL variant master combos. PG may be partially populated; missing + // colors/dim3 combos still need to be seeded before sdprc/zbggcampaign writes. + if d, err := loadDimsFromMssqlStock(code); err != nil { + logger.Error("save:pg:dims:mssql:error", "product_code", code, "err", err) + } else if len(d) > 0 { + seenDims := make(map[string]struct{}, len(dims)+len(d)) + merged := make([]dimCombo, 0, len(dims)+len(d)) + dim3Value := func(v sql.NullInt64) int64 { + if v.Valid { + return v.Int64 + } + return 0 } + for _, c := range dims { + k := fmt.Sprintf("%d|%d", c.Dim1, dim3Value(c.Dim3)) + if _, ok := seenDims[k]; ok { + continue + } + seenDims[k] = struct{}{} + merged = append(merged, c) + } + for _, c := range d { + k := fmt.Sprintf("%d|%d", c.Dim1, dim3Value(c.Dim3)) + if _, ok := seenDims[k]; ok { + continue + } + seenDims[k] = struct{}{} + merged = append(merged, c) + } + dims = merged + _ = upsertDimCombosCache(code, dims) + ensureMMItemDimRows(mmItemID, d, nil) } } diff --git a/svc/routes/wholesale_campaigns.go b/svc/routes/wholesale_campaigns.go index 8e8dc0f..0b3f0d8 100644 --- a/svc/routes/wholesale_campaigns.go +++ b/svc/routes/wholesale_campaigns.go @@ -9,6 +9,7 @@ import ( "fmt" "log" "net/http" + "sort" "strconv" "strings" "time" @@ -420,6 +421,18 @@ type wholesaleVariantRow struct { CampaignLast string `json:"campaign_last_dttm"` } +func buildNebimVariantDisplayCode(colorCode string, dim3Code string) string { + colorCode = strings.TrimSpace(colorCode) + dim3Code = strings.TrimSpace(dim3Code) + if colorCode != "" && dim3Code != "" { + return colorCode + "-" + dim3Code + } + if colorCode != "" { + return colorCode + } + return dim3Code +} + type wholesaleCampaignHistoryRow struct { ID int64 `json:"id"` CampaignID *int64 `json:"campaign_id"` @@ -736,6 +749,7 @@ DO UPDATE SET dim_id = EXCLUDED.dim_id, updated_at = EXCLUDED.updated_at ItemID int64 Dim1 int64 Dim3Key int64 + HasMSSQL bool } // Build base variant keys from PG's authoritative table (mmitem_dim). @@ -805,7 +819,8 @@ WHERE mmitem_id = ANY($1::bigint[]) rows.Close() } - // Resolve dim ids -> tokens for a readable VariantCode. + // Resolve dim ids -> tokens for a fallback readable VariantCode. + // MSSQL/Nebim tokens override this below; PG ids are only storage keys. idToToken := map[int64]string{} if len(dimIDs) > 0 { // uniq @@ -900,23 +915,23 @@ ORDER BY dim_id, updated_at DESC; key := fmt.Sprintf("%d|%d|%d", itemID, d1, d3k) prev, ok := tmpMap[key] if !ok { - // If PG does not have mmitem_dim rows for this item yet, seed it from MSSQL and include it. - if !hasMMItemDim[itemID] { - var v2 any = nil - if d3k > 0 { - v2 = d3k - } - v3 := int64(0) - if id, ok := resolveDimID("dimval1", dim3Code); ok { - v3 = id - } - mmdimID := int64(2) - var v3any any = nil - if v3 > 0 { - mmdimID = 3 - v3any = v3 - } - _, _ = pg.ExecContext(ctx, ` + // Seed missing MSSQL combos even when the product already has some mmitem_dim rows. + // PG remains the storage key source, but MSSQL may reveal new/missing color or dim3 combos. + var v2 any = nil + if sizeID, ok := resolveDimID("dimval1", dim1Code); ok && sizeID > 0 { + v2 = sizeID + } + v3 := int64(0) + if id, ok := resolveDimID("dimval1", dim3Code); ok { + v3 = id + } + mmdimID := int64(2) + var v3any any = nil + if v3 > 0 { + mmdimID = 3 + v3any = v3 + } + _, _ = pg.ExecContext(ctx, ` INSERT INTO mmitem_dim (mmitem_id, mmdim_id, val1, val2, val3, is_active, qty) SELECT $1, $2, $3, $4, $5, TRUE, 0 WHERE NOT EXISTS ( @@ -930,26 +945,25 @@ WHERE NOT EXISTS ( LIMIT 1 ); `, itemID, mmdimID, d1, v2, v3any) - hasMMItemDim[itemID] = true + hasMMItemDim[itemID] = true - code := strings.TrimSpace(itemToCode[itemID]) - if code != "" { - tmpMap[key] = tmpRow{ - ProductCode: code, - VariantCode: "", - StockQty: 0, - ItemID: itemID, - Dim1: d1, - Dim3Key: d3k, - } - // Keep dim token cache for VariantCode formatting. - dimIDs = append(dimIDs, d1) - if d3k > 0 { - dimIDs = append(dimIDs, d3k) - } - prev = tmpMap[key] - ok = true + code := strings.TrimSpace(itemToCode[itemID]) + if code != "" { + tmpMap[key] = tmpRow{ + ProductCode: code, + VariantCode: "", + StockQty: 0, + ItemID: itemID, + Dim1: d1, + Dim3Key: d3k, } + // Keep dim token cache for VariantCode formatting. + dimIDs = append(dimIDs, d1) + if d3k > 0 { + dimIDs = append(dimIDs, d3k) + } + prev = tmpMap[key] + ok = true } if !ok { continue @@ -960,6 +974,8 @@ WHERE NOT EXISTS ( q = qty.Float64 } prev.StockQty += q + prev.VariantCode = buildNebimVariantDisplayCode(colorCode, dim3Code) + prev.HasMSSQL = true tmpMap[key] = prev _ = colorCode // display-only } @@ -968,6 +984,15 @@ WHERE NOT EXISTS ( for _, v := range tmpMap { tmp = append(tmp, v) } + sort.SliceStable(tmp, func(i, j int) bool { + if tmp[i].ProductCode != tmp[j].ProductCode { + return tmp[i].ProductCode < tmp[j].ProductCode + } + if tmp[i].HasMSSQL != tmp[j].HasMSSQL { + return tmp[i].HasMSSQL + } + return strings.TrimSpace(tmp[i].VariantCode) < strings.TrimSpace(tmp[j].VariantCode) + }) // Bulk load campaign assignment for each (mmitem_id, dim1, dim3_key) type keyRec struct { diff --git a/ui/src/pages/OrderPriceList.vue b/ui/src/pages/OrderPriceList.vue index 89eb014..bc001f7 100644 --- a/ui/src/pages/OrderPriceList.vue +++ b/ui/src/pages/OrderPriceList.vue @@ -321,6 +321,137 @@ + + + {{ selectedVariantCodes.length }} + + +
+ +
+ + +
+ + + +
Sonuc yok
+
+
+
+ + + {{ getColumnFilterValues(col.name).length }} + + +
+ +
+ + +
+ + + +
Sonuc yok
+
+
+
@@ -503,7 +634,11 @@ const topUrunIlkGrubu = ref(null) const topUrunAnaGrubu = ref(null) const selectedProductCodes = ref([]) const selectedCampaignLabels = ref([]) +const selectedVariantCodes = ref([]) const campaignFilterSearch = ref('') +const variantFilterSearch = ref('') +const columnFilters = ref({}) +const columnFilterSearch = ref({}) const selectedPriceOptions = ref(['usd5', 'try5']) const leftDetailsExpanded = ref(true) @@ -537,6 +672,7 @@ const fullscreenImages = computed(() => productCardImages.value || []) const selectedPriceSet = computed(() => new Set(selectedPriceOptions.value || [])) const selectedProductCodeSet = computed(() => new Set(selectedProductCodes.value || [])) const selectedCampaignLabelSet = computed(() => new Set(selectedCampaignLabels.value || [])) +const selectedVariantCodeSet = computed(() => new Set(selectedVariantCodes.value || [])) const pageBusy = computed(() => loading.value || renderPending.value) const canFetch = computed(() => Boolean(topUrunIlkGrubu.value || topUrunAnaGrubu.value || selectedProductCodes.value.length > 0)) const showGuidanceOverlay = computed(() => !loading.value && rows.value.length === 0 && error.value === GUIDANCE_MSG) @@ -559,6 +695,21 @@ const filteredCampaignOptions = computed(() => { const list = campaignOptions.value return q ? list.filter((x) => x.label.toLocaleLowerCase('tr').includes(q)) : list }) +const variantOptions = computed(() => { + const uniq = new Set() + for (const row of rows.value || []) { + const val = toText(row?.variantCodes) + if (val) uniq.add(val) + } + return Array.from(uniq) + .sort((a, b) => variantCodeCollator.compare(a, b)) + .map((value) => ({ label: value, value })) +}) +const filteredVariantOptions = computed(() => { + const q = toText(variantFilterSearch.value).toLocaleLowerCase('tr') + const list = variantOptions.value + return q ? list.filter((x) => x.label.toLocaleLowerCase('tr').includes(q)) : list +}) function toText (value) { return String(value ?? '').trim() @@ -763,6 +914,79 @@ function clearCampaignOptions () { selectedCampaignLabels.value = [] } +function toggleVariantValue (value) { + const v = toText(value) + if (!v) return + const set = new Set(selectedVariantCodes.value || []) + if (set.has(v)) set.delete(v) + else set.add(v) + selectedVariantCodes.value = Array.from(set).sort((a, b) => variantCodeCollator.compare(a, b)) +} + +function selectAllVariantOptions () { + const set = new Set(selectedVariantCodes.value || []) + for (const option of filteredVariantOptions.value) { + const v = toText(option.value) + if (v) set.add(v) + } + selectedVariantCodes.value = Array.from(set).sort((a, b) => variantCodeCollator.compare(a, b)) +} + +function clearVariantOptions () { + selectedVariantCodes.value = [] +} + +function isLocalFilterableColumn (name) { + if (!name || ['image', 'productCode', 'variantCodes', 'campaignLabel'].includes(name)) return false + return true +} + +function getColumnFilterValues (name) { + const list = columnFilters.value?.[name] + return Array.isArray(list) ? list : [] +} + +function getColumnFilterSet (name) { + return new Set(getColumnFilterValues(name)) +} + +function setColumnFilterSearch (name, value) { + columnFilterSearch.value = { ...columnFilterSearch.value, [name]: toText(value) } +} + +function getColumnOptions (col) { + const uniq = new Set() + for (const row of rows.value || []) { + uniq.add(exportCell(row, col)) + } + return Array.from(uniq) + .sort((a, b) => String(a).localeCompare(String(b), 'tr', { numeric: true, sensitivity: 'base' })) + .map((value) => ({ label: value || '-', value })) +} + +function getFilteredColumnOptions (col) { + const q = toText(columnFilterSearch.value?.[col.name]).toLocaleLowerCase('tr') + const list = getColumnOptions(col) + return q ? list.filter((x) => String(x.label || '').toLocaleLowerCase('tr').includes(q)) : list +} + +function toggleColumnFilterValue (name, value) { + const set = new Set(getColumnFilterValues(name)) + if (set.has(value)) set.delete(value) + else set.add(value) + columnFilters.value = { ...columnFilters.value, [name]: Array.from(set) } +} + +function selectAllColumnOptions (col) { + const set = new Set(getColumnFilterValues(col.name)) + for (const option of getFilteredColumnOptions(col)) set.add(option.value) + columnFilters.value = { ...columnFilters.value, [col.name]: Array.from(set) } +} + +function clearColumnOptions (name) { + columnFilters.value = { ...columnFilters.value, [name]: [] } +} + function onTopUrunIlkGrubuChange () { topUrunAnaGrubu.value = null void fetchServerFilterOptions('urunAnaGrubu', '') @@ -947,6 +1171,10 @@ function resetSelections () { topUrunIlkGrubu.value = null topUrunAnaGrubu.value = null selectedProductCodes.value = [] + selectedCampaignLabels.value = [] + selectedVariantCodes.value = [] + columnFilters.value = {} + columnFilterSearch.value = {} rows.value = [] error.value = GUIDANCE_MSG currentPage.value = 1 @@ -1031,8 +1259,25 @@ const visibleColumns = computed(() => allColumns.filter((c) => { const filteredRows = computed(() => { const campaignSet = selectedCampaignLabelSet.value - if (campaignSet.size === 0) return rows.value || [] - return (rows.value || []).filter((row) => campaignSet.has(toText(row?.campaignLabel))) + const variantSet = selectedVariantCodeSet.value + const localFilters = columnFilters.value || {} + let list = rows.value || [] + if (campaignSet.size > 0) { + list = list.filter((row) => campaignSet.has(toText(row?.campaignLabel))) + } + if (variantSet.size > 0) { + list = list.filter((row) => variantSet.has(toText(row?.variantCodes))) + } + const active = Object.entries(localFilters).filter(([, values]) => Array.isArray(values) && values.length > 0) + if (active.length > 0) { + const byName = new Map(allColumns.map((c) => [c.name, c])) + list = list.filter((row) => active.every(([name, values]) => { + const col = byName.get(name) + if (!col) return true + return new Set(values).has(exportCell(row, col)) + })) + } + return list }) const tableMinWidth = computed(() => visibleColumns.value.reduce((sum, c) => sum + extractWidth(c.style), 0)) const tableStyle = computed(() => ({ diff --git a/ui/src/pages/WholesaleCampaigns.vue b/ui/src/pages/WholesaleCampaigns.vue index 3537a30..568457a 100644 --- a/ui/src/pages/WholesaleCampaigns.vue +++ b/ui/src/pages/WholesaleCampaigns.vue @@ -884,6 +884,7 @@ const currentPage = ref(1) let reloadTimer = null const variantRows = ref([]) const variantRowsCache = new Map() +const variantCodeCollator = new Intl.Collator('tr', { numeric: true, sensitivity: 'base' }) const VARIANT_ROWS_CACHE_LIMIT = 16 const GUIDANCE_MSG = "Calismak icin once Urun Ilk Grubu veya Urun Ana Grubu Secin ve GRUPLARI GETIR'e Basin." @@ -2678,7 +2679,7 @@ async function buildVariantRowsForProductPage (baseProductRows = []) { }) continue } - vs.sort((a, b) => String(a?.variant_code || '').localeCompare(String(b?.variant_code || ''), 'tr')) + vs.sort((a, b) => variantCodeCollator.compare(String(a?.variant_code || ''), String(b?.variant_code || ''))) for (const v of vs) { const d1 = Number(v?.dim1 || 0) const d3 = v?.dim3 == null ? null : Number(v?.dim3 || 0)