Merge remote-tracking branch 'origin/master'

This commit is contained in:
M_Kececi
2026-03-29 22:41:02 +03:00
parent 96ede55936
commit 05c6103a3a
10 changed files with 629 additions and 92 deletions

View File

@@ -57,6 +57,60 @@ export const schemaByKey = BEDEN_SCHEMA.reduce((m, g) => {
return m
}, {})
const productSizeMatchCache = {
loaded: false,
rules: [],
schemas: {}
}
function resetProductSizeMatchCache() {
productSizeMatchCache.loaded = false
productSizeMatchCache.rules = []
productSizeMatchCache.schemas = {}
}
function setProductSizeMatchCache(payload) {
const rules = Array.isArray(payload?.rules) ? payload.rules : []
const schemasRaw = payload?.schemas && typeof payload.schemas === 'object'
? payload.schemas
: {}
const normalizedRules = rules
.map(r => ({
productGroupID: Number(r?.product_group_id || r?.productGroupID || 0),
kategori: normalizeTextForMatch(r?.kategori || ''),
urunAnaGrubu: normalizeTextForMatch(r?.urun_ana_grubu || r?.urunAnaGrubu || ''),
urunAltGrubu: normalizeTextForMatch(r?.urun_alt_grubu || r?.urunAltGrubu || ''),
groupKeys: Array.isArray(r?.group_keys || r?.groupKeys)
? (r.group_keys || r.groupKeys).map(g => String(g || '').trim()).filter(Boolean)
: []
}))
.filter(r => r.groupKeys.length > 0)
.sort((a, b) => {
if (a.productGroupID && b.productGroupID) return a.productGroupID - b.productGroupID
return 0
})
const normalizedSchemas = {}
for (const [k, vals] of Object.entries(schemasRaw)) {
const key = String(k || '').trim()
if (!key) continue
const arr = Array.isArray(vals)
? vals
: String(vals || '').split(',')
normalizedSchemas[key] = arr
.map(v => {
const s = String(v == null ? '' : v).trim()
return s === '' ? ' ' : s
})
.filter((v, idx, all) => all.indexOf(v) === idx)
}
productSizeMatchCache.loaded = true
productSizeMatchCache.rules = normalizedRules
productSizeMatchCache.schemas = normalizedSchemas
}
export const stockMap = ref({})
export const bedenStock = ref([])
@@ -228,6 +282,28 @@ export const useOrderEntryStore = defineStore('orderentry', {
)
},
async ensureProductSizeMatchRules($q = null, force = false) {
if (!force && productSizeMatchCache.loaded && productSizeMatchCache.rules.length > 0) {
return true
}
try {
const res = await api.get('/product-size-match/rules')
setProductSizeMatchCache(res?.data || {})
return true
} catch (err) {
if (force) {
resetProductSizeMatchCache()
}
console.warn('⚠ product-size-match rules alınamadı:', err)
$q?.notify?.({
type: 'warning',
message: 'Beden eşleme kuralları alınamadı.'
})
return false
}
},
getRowKey(row) {
if (!row) return null
@@ -923,6 +999,14 @@ export const useOrderEntryStore = defineStore('orderentry', {
try {
// geçici varsayım (sonra isClosed durumuna göre set edilecek)
this.setMode?.('edit')
const rulesReady = await this.ensureProductSizeMatchRules?.($q)
if (!rulesReady) {
$q?.notify?.({
type: 'negative',
message: 'Beden eşleme kuralları yüklenemedi.'
})
return false
}
/* =======================================================
🔹 BACKEND — authoritative load
@@ -1774,6 +1858,13 @@ export const useOrderEntryStore = defineStore('orderentry', {
form.tutar = Number((adet * Number(form.fiyat || 0)).toFixed(2))
const newRow = toSummaryRowFromForm(form)
if (!newRow) {
$q?.notify?.({
type: 'negative',
message: 'Beden grubu eşleşmesi bulunamadı.'
})
return false
}
/* =======================================================
5⃣ EDIT MODE (editingKey ZORUNLU)
@@ -2327,14 +2418,15 @@ export const useOrderEntryStore = defineStore('orderentry', {
detectBedenGroup(
Object.keys(srcMap || {}),
raw.urunAnaGrubu || raw.UrunAnaGrubu || '',
raw.kategori || raw.Kategori || raw.urunAltGrubu || raw.UrunAltGrubu || '',
raw.kategori || raw.Kategori || '',
raw.yetiskinGarson ||
raw.YETISKIN_GARSON ||
raw.YetiskinGarson ||
raw.AskiliYan ||
raw.ASKILIYAN ||
raw.askiliyan ||
''
'',
raw.urunAltGrubu || raw.UrunAltGrubu || ''
) ||
'tak'
@@ -2393,7 +2485,12 @@ export const useOrderEntryStore = defineStore('orderentry', {
urunAnaGrubu: raw.UrunAnaGrubu || 'GENEL',
urunAltGrubu: raw.UrunAltGrubu || '',
kategori: raw.Kategori || raw.UrunAltGrubu || '',
kategori:
raw.Kategori ||
raw.YETISKIN_GARSON ||
raw.YetiskinGarson ||
raw.yetiskinGarson ||
'',
yetiskinGarson:
raw.YETISKIN_GARSON ||
raw.YetiskinGarson ||
@@ -2458,8 +2555,9 @@ export const useOrderEntryStore = defineStore('orderentry', {
const grpKey = detectBedenGroup(
bedenList,
row.urunAnaGrubu,
row.kategori || row.urunAltGrubu,
row.yetiskinGarson
row.kategori || '',
row.yetiskinGarson,
row.urunAltGrubu || ''
)
const cleanedMap = { ...row.__tmpMap }
@@ -2655,11 +2753,36 @@ export const useOrderEntryStore = defineStore('orderentry', {
// 🔸 GRUP ANAHTARI TESPİTİ
// =======================================================
activeGroupKeyForRow(row) {
const bedenSet = new Set()
if (row?.bedenMap && typeof row.bedenMap === 'object') {
const grp = row?.grpKey && row.bedenMap[row.grpKey] && typeof row.bedenMap[row.grpKey] === 'object'
? row.bedenMap[row.grpKey]
: null
if (grp) {
Object.keys(grp).forEach(k => bedenSet.add(String(k || '')))
} else {
Object.values(row.bedenMap).forEach(m => {
if (m && typeof m === 'object') {
Object.keys(m).forEach(k => bedenSet.add(String(k || '')))
}
})
}
}
if (bedenSet.size === 0 && Array.isArray(row?.bedenLabels)) {
row.bedenLabels.forEach(lbl => {
bedenSet.add(String(lbl == null ? '' : lbl))
})
}
return detectBedenGroup(
null,
Array.from(bedenSet),
row?.urunAnaGrubu || '',
row?.kategori || row?.urunAltGrubu || '',
row?.YETISKIN_GARSON || row?.yetiskinGarson || ''
row?.kategori || '',
row?.YETISKIN_GARSON || row?.yetiskinGarson || '',
row?.urunAltGrubu || ''
)
},
/* =======================================================
@@ -3621,16 +3744,113 @@ export function normalizeBeden(v) {
return normalizeBedenLabel(v)
}
function deriveKategoriToken(urunKategori = '', yetiskinGarson = '') {
const kat = normalizeTextForMatch(urunKategori || '')
if (kat.includes('GARSON')) return 'GARSON'
if (kat.includes('YETISKIN')) return 'YETISKIN'
return ''
}
function normalizeRuleAltGroup(urunAltGrubu = '') {
return normalizeTextForMatch(urunAltGrubu || '')
}
function pickBestGroupFromCandidates(groupKeys = [], bedenList = []) {
if (!Array.isArray(groupKeys) || groupKeys.length === 0) return ''
if (groupKeys.length === 1) return groupKeys[0]
const normalizedBeden = (Array.isArray(bedenList) ? bedenList : [])
.map(v => normalizeBedenLabel(v))
.filter(Boolean)
if (!normalizedBeden.length) return groupKeys[0]
let bestKey = groupKeys[0]
let bestScore = -1
for (const key of groupKeys) {
const schema = Array.isArray(productSizeMatchCache.schemas?.[key])
? productSizeMatchCache.schemas[key]
: []
const normalizedSchema = new Set(schema.map(v => normalizeBedenLabel(v)))
let score = 0
for (const b of normalizedBeden) {
if (normalizedSchema.has(b)) score += 1
}
if (score > bestScore) {
bestScore = score
bestKey = key
}
}
return bestKey || groupKeys[0]
}
function resolveGroupFromProductSizeMatchRules(
bedenList,
urunAnaGrubu = '',
urunKategori = '',
yetiskinGarson = '',
urunAltGrubu = ''
) {
if (!productSizeMatchCache.loaded || !Array.isArray(productSizeMatchCache.rules) || !productSizeMatchCache.rules.length) {
return ''
}
const kategoriToken = deriveKategoriToken(urunKategori, yetiskinGarson)
const ana = normalizeTextForMatch(urunAnaGrubu || '')
const alt = normalizeRuleAltGroup(urunAltGrubu)
if (!kategoriToken || !ana) return ''
const candidateGroupKeys = []
for (const rule of productSizeMatchCache.rules) {
if (!rule?.urunAnaGrubu || rule.urunAnaGrubu !== ana) continue
if (rule.kategori !== kategoriToken) continue
const ruleAlt = normalizeTextForMatch(rule.urunAltGrubu || '')
if (ruleAlt !== alt) continue
for (const g of (rule.groupKeys || [])) {
const key = String(g || '').trim()
if (key && !candidateGroupKeys.includes(key)) {
candidateGroupKeys.push(key)
}
}
}
if (!candidateGroupKeys.length) return ''
return pickBestGroupFromCandidates(candidateGroupKeys, bedenList)
}
/* ===========================================================
Size Group Detection
- Core logic aligned with backend detectBedenGroupGo
- Keeps frontend aksbir bucket for accessory lines
=========================================================== */
export function detectBedenGroup(bedenList, urunAnaGrubu = '', urunKategori = '', yetiskinGarson = '') {
export function detectBedenGroup(bedenList, urunAnaGrubu = '', urunKategori = '', yetiskinGarson = '', urunAltGrubu = '') {
const list = Array.isArray(bedenList) && bedenList.length > 0
? bedenList.map(v => (v || '').toString().trim().toUpperCase())
: [' ']
const ruleBased = resolveGroupFromProductSizeMatchRules(
list,
urunAnaGrubu,
urunKategori,
yetiskinGarson,
urunAltGrubu
)
if (productSizeMatchCache.loaded) {
if (!ruleBased) {
console.warn('⚠ product-size-match eşleşme bulunamadı', {
kategori: deriveKategoriToken(urunKategori, yetiskinGarson),
urunAnaGrubu: normalizeTextForMatch(urunAnaGrubu || ''),
urunAltGrubu: normalizeRuleAltGroup(urunAltGrubu),
bedenList: list
})
}
return ruleBased || ''
}
return ''
const rawAna = normalizeTextForMatch(urunAnaGrubu || '')
const rawKat = normalizeTextForMatch(urunKategori || '')
const rawYetiskinGarson = normalizeTextForMatch(yetiskinGarson || '')
@@ -3672,6 +3892,12 @@ export function detectBedenGroup(bedenList, urunAnaGrubu = '', urunKategori = ''
return 'yas'
}
// Ayakkabi kurali garsondan once uygulanmali:
// GARSON + AYAKKABI => ayk_garson, digerleri => ayk
if (mappedRawAna.includes('AYAKKABI') || rawKat.includes('AYAKKABI')) {
return hasGarsonSignal ? 'ayk_garson' : 'ayk'
}
const hasGarson = hasGarsonSignal
if (hasGarson) return 'yas'
@@ -3721,7 +3947,8 @@ export function detectBedenGroup(bedenList, urunAnaGrubu = '', urunKategori = ''
export function toSummaryRowFromForm(form) {
if (!form) return null
const grpKey = form.grpKey || 'tak'
const grpKey = form.grpKey
if (!grpKey) return null
const bedenMap = {}
const labels = Array.isArray(form.bedenLabels) ? form.bedenLabels : []