Merge remote-tracking branch 'origin/master'

This commit is contained in:
M_Kececi
2026-02-20 11:20:53 +03:00
parent d9c527d13f
commit 32d0c38ab9
11 changed files with 1264 additions and 122 deletions

View File

@@ -5,11 +5,35 @@ import api from 'src/services/api'
export const useOrderProductionItemStore = defineStore('orderproductionitems', {
state: () => ({
items: [],
header: null,
products: [],
colorOptionsByCode: {},
secondColorOptionsByKey: {},
loading: false,
saving: false,
error: null
}),
actions: {
async fetchHeader (orderHeaderID) {
if (!orderHeaderID) {
this.header = null
return
}
this.loading = true
this.error = null
try {
const res = await api.get(`/order/get/${encodeURIComponent(orderHeaderID)}`)
this.header = res?.data?.header || null
} catch (err) {
this.header = null
this.error = err?.response?.data || err?.message || 'Siparis bilgisi alinamadi'
} finally {
this.loading = false
}
},
async fetchItems (orderHeaderID) {
if (!orderHeaderID) {
this.items = []
@@ -25,10 +49,99 @@ export const useOrderProductionItemStore = defineStore('orderproductionitems', {
this.items = Array.isArray(data) ? data : []
} catch (err) {
this.items = []
this.error = err?.response?.data || err?.message || 'Liste alınamadı'
this.error = err?.response?.data || err?.message || 'Liste alinamadi'
} finally {
this.loading = false
}
},
async fetchProducts () {
this.error = null
try {
const res = await api.get('/products')
const data = res?.data
this.products = Array.isArray(data) ? data : []
} catch (err) {
this.products = []
this.error = err?.response?.data || err?.message || 'Urun listesi alinamadi'
}
},
async fetchColors (productCode) {
const code = String(productCode || '').trim()
if (!code) return []
if (this.colorOptionsByCode[code]) {
return this.colorOptionsByCode[code]
}
try {
const res = await api.get('/product-colors', { params: { code } })
const data = res?.data
const list = Array.isArray(data) ? data : []
this.colorOptionsByCode[code] = list
return list
} catch (err) {
this.error = err?.response?.data || err?.message || 'Renk listesi alinamadi'
return []
}
},
async fetchSecondColors (productCode, colorCode) {
const code = String(productCode || '').trim()
const color = String(colorCode || '').trim()
if (!code || !color) return []
const key = `${code}::${color}`
if (this.secondColorOptionsByKey[key]) {
return this.secondColorOptionsByKey[key]
}
try {
const res = await api.get('/product-secondcolor', { params: { code, color } })
const data = res?.data
const list = Array.isArray(data) ? data : []
this.secondColorOptionsByKey[key] = list
return list
} catch (err) {
this.error = err?.response?.data || err?.message || '2. renk listesi alinamadi'
return []
}
},
async validateUpdates (orderHeaderID, lines) {
if (!orderHeaderID) return { missingCount: 0, missing: [] }
this.saving = true
this.error = null
try {
const res = await api.post(
`/orders/production-items/${encodeURIComponent(orderHeaderID)}/validate`,
{ lines }
)
return res?.data || { missingCount: 0, missing: [] }
} catch (err) {
this.error = err?.response?.data || err?.message || 'Kontrol basarisiz'
throw err
} finally {
this.saving = false
}
},
async applyUpdates (orderHeaderID, lines, insertMissing) {
if (!orderHeaderID) return { updated: 0, inserted: 0 }
this.saving = true
this.error = null
try {
const res = await api.post(
`/orders/production-items/${encodeURIComponent(orderHeaderID)}/apply`,
{ lines, insertMissing }
)
return res?.data || { updated: 0, inserted: 0 }
} catch (err) {
this.error = err?.response?.data || err?.message || 'Guncelleme basarisiz'
throw err
} finally {
this.saving = false
}
}
}
})

View File

@@ -1701,14 +1701,15 @@ export const useOrderEntryStore = defineStore('orderentry', {
: {}
/* ===== SAME COMBO → UPDATE ===== */
if (sameCombo) {
rows[idx] = {
...prev,
...newRow,
id: prev.id,
OrderLineID: prev.OrderLineID || null,
lineIdMap: preservedLineIdMap
}
if (sameCombo) {
rows[idx] = {
...prev,
...newRow,
_dirty: true,
id: prev.id,
OrderLineID: prev.OrderLineID || null,
lineIdMap: preservedLineIdMap
}
this.summaryRows = rows
this.orders = rows
@@ -1761,12 +1762,13 @@ export const useOrderEntryStore = defineStore('orderentry', {
comboLineIds: { ...(prev.comboLineIds || {}) }
}
const insertedRow = {
...newRow,
id: crypto.randomUUID(),
OrderLineID: null,
lineIdMap: {}
}
const insertedRow = {
...newRow,
_dirty: true,
id: crypto.randomUUID(),
OrderLineID: null,
lineIdMap: {}
}
rows.splice(idx, 1, insertedRow)
@@ -1835,13 +1837,14 @@ export const useOrderEntryStore = defineStore('orderentry', {
const price = Number(newRow?.fiyat ?? prev?.fiyat ?? 0)
const totalTutar = Number((totalAdet * price).toFixed(2))
rows[dupIdx] = {
...prev,
...newRow,
rows[dupIdx] = {
...prev,
...newRow,
_dirty: true,
// kritik korumalar
id: prev.id,
OrderLineID: prev.OrderLineID || null,
// kritik korumalar
id: prev.id,
OrderLineID: prev.OrderLineID || null,
lineIdMap: { ...(prev.lineIdMap || {}) },
// MERGED bedenMap
@@ -1869,12 +1872,13 @@ export const useOrderEntryStore = defineStore('orderentry', {
}
// dup yoksa (veya dup delete satırıydı) → yeni satır
rows.push({
...newRow,
id: newRow.id || crypto.randomUUID(),
OrderLineID: null,
lineIdMap: { ...(newRow.lineIdMap || {}) }
})
rows.push({
...newRow,
_dirty: true,
id: newRow.id || crypto.randomUUID(),
OrderLineID: null,
lineIdMap: { ...(newRow.lineIdMap || {}) }
})
this.summaryRows = rows
this.orders = rows
@@ -2633,7 +2637,12 @@ export const useOrderEntryStore = defineStore('orderentry', {
// 🧪 PRE-VALIDATE — prItemVariant ön kontrol
// - invalid varsa CREATE/UPDATE ÇALIŞMAZ
// =======================================================
const v = await api.post('/order/validate', { header, lines })
const linesToValidate =
isNew
? lines
: lines.filter(l => l._deleteSignal === true || l._dirty === true || !l.OrderLineID)
const v = await api.post('/order/validate', { header, lines: linesToValidate })
const invalid = v?.data?.invalid || []
if (invalid.length > 0) {
@@ -2969,10 +2978,12 @@ export const useOrderEntryStore = defineStore('orderentry', {
// ComboKey stabil kalsın diye bedenKey kullan
const comboKey = buildComboKey(row, bedenKey)
const makeLine = () => ({
OrderLineID: orderLineId || '',
ClientKey: makeLineClientKey(row, grpKey, bedenKey),
ComboKey: comboKey,
const makeLine = () => ({
OrderLineID: orderLineId || '',
ClientKey: makeLineClientKey(row, grpKey, bedenKey),
ComboKey: comboKey,
_dirty: row?._dirty === true,
_deleteSignal: isDeleteSignal === true,
SortOrder: 0,
ItemTypeCode: 1,
@@ -3040,26 +3051,30 @@ export const useOrderEntryStore = defineStore('orderentry', {
DOVCode: ''
})
const existing = lineByCombo.get(comboKey)
const existing = lineByCombo.get(comboKey)
if (!existing) {
const ln = makeLine()
lineByCombo.set(comboKey, ln)
lines.push(ln)
return
}
if (!existing) {
const ln = makeLine()
lineByCombo.set(comboKey, ln)
lines.push(ln)
return
}
/* DELETE */
if (isDeleteSignal) {
if (orderLineId && !existing.OrderLineID) {
existing.OrderLineID = orderLineId
if (isDeleteSignal) {
if (orderLineId && !existing.OrderLineID) {
existing.OrderLineID = orderLineId
}
existing._deleteSignal = true
existing.Qty1 = 0
return
}
existing.Qty1 = 0
return
}
/* MERGE */
existing.Qty1 += qty
/* MERGE */
existing.Qty1 += qty
if (row?._dirty === true) {
existing._dirty = true
}
if (this.mode === 'edit' && orderLineId && !existing.OrderLineID) {
existing.OrderLineID = orderLineId
@@ -3346,66 +3361,56 @@ export function normalizeBeden(v) {
- Keeps frontend aksbir bucket for accessory lines
=========================================================== */
export function detectBedenGroup(bedenList, urunAnaGrubu = '', urunKategori = '') {
const list = Array.isArray(bedenList) ? bedenList : []
const ana = normalizeTextForMatch(urunAnaGrubu)
const alt = normalizeTextForMatch(urunKategori)
const list = Array.isArray(bedenList) && bedenList.length > 0
? bedenList.map(v => (v || '').toString().trim().toUpperCase())
: [' ']
// Frontend compatibility: accessory-only products should stay in aksbir.
const accessoryGroups = [
'AKSESUAR', 'KRAVAT', 'PAPYON', 'KEMER', 'CORAP',
'FULAR', 'MENDIL', 'KASKOL', 'ASKI', 'YAKA', 'KOL DUGMESI'
const ana = (urunAnaGrubu || '')
.toUpperCase()
.trim()
.replace(/\(.*?\)/g, '')
.replace(/[^A-ZÇĞİÖŞÜ0-9\s]/g, '')
.replace(/\s+/g, ' ')
const kat = (urunKategori || '').toUpperCase().trim()
// 🔸 Aksesuar ise "aksbir"
const aksesuarGruplari = [
'AKSESUAR','KRAVAT','PAPYON','KEMER','CORAP','ÇORAP',
'FULAR','MENDIL','MENDİL','KASKOL','ASKI',
'YAKA','KOL DUGMESI','KOL DÜĞMESİ'
]
const clothingGroups = ['GOMLEK', 'CEKET', 'PANTOLON', 'MONT', 'YELEK', 'TAKIM', 'TSHIRT']
const giyimGruplari = ['GÖMLEK','CEKET','PANTOLON','MONT','YELEK','TAKIM','TSHIRT','TİŞÖRT']
// 🔸 Pantolon özel durumu
if (
accessoryGroups.some(g => ana.includes(g) || alt.includes(g)) &&
!clothingGroups.some(g => ana.includes(g))
) {
return 'aksbir'
aksesuarGruplari.some(g => ana.includes(g) || kat.includes(g)) &&
!giyimGruplari.some(g => ana.includes(g))
) return 'aksbir'
if (ana.includes('PANTOLON') && kat.includes('YETİŞKİN')) return 'pan'
// 🔸 Tamamen numerik (örneğin 39-44 arası) → ayakkabı
const allNumeric = list.every(v => /^\d+$/.test(v))
if (allNumeric) {
const nums = list.map(v => parseInt(v, 10)).filter(Boolean)
const diffs = nums.slice(1).map((v, i) => v - nums[i])
if (diffs.every(d => d === 1) && nums[0] >= 35 && nums[0] <= 46) return 'ayk'
}
if (ana.includes('AYAKKABI') || alt.includes('AYAKKABI')) {
return 'ayk'
}
// 🔸 Yaş grubu (çocuk/garson)
if (kat.includes('GARSON') || kat.includes('ÇOCUK')) return 'yas'
let hasYasNumeric = false
let hasAykNumeric = false
let hasPanNumeric = false
// 🔸 Harfli beden varsa doğrudan "gom" (gömlek, üst giyim)
const harfliBedenler = ['XS','S','M','L','XL','XXL','3XL','4XL']
if (list.some(b => harfliBedenler.includes(b))) return 'gom'
for (const raw of list) {
const b = safeTrimUpperJs(raw)
switch (b) {
case 'XS':
case 'S':
case 'M':
case 'L':
case 'XL':
case '2XL':
case '3XL':
case '4XL':
case '5XL':
case '6XL':
case '7XL':
return 'gom'
}
const n = parseNumericSizeJs(b)
if (n == null) continue
if (n >= 2 && n <= 14) hasYasNumeric = true
if (n >= 39 && n <= 45) hasAykNumeric = true
if (n >= 38 && n <= 68) hasPanNumeric = true
}
if (hasAykNumeric) return 'ayk'
if (ana.includes('PANTOLON')) return 'pan'
if (hasPanNumeric) return 'pan'
if (alt.includes('COCUK') || alt.includes('GARSON')) return 'yas'
if (hasYasNumeric) return 'yas'
// 🔸 Varsayılan: takım elbise
return 'tak'
}
export function toSummaryRowFromForm(form) {
if (!form) return null