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 @@
+
+
@@ -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)