507 lines
17 KiB
JavaScript
507 lines
17 KiB
JavaScript
// src/stores/OrderProductionItemStore.js
|
|
import { defineStore } from 'pinia'
|
|
import api from 'src/services/api'
|
|
|
|
function extractApiErrorMessage (err, fallback) {
|
|
const data = err?.response?.data
|
|
if (typeof data === 'string' && data.trim()) return data
|
|
if (data && typeof data === 'object') {
|
|
const validationMessages = Array.isArray(data.barcodeValidations)
|
|
? data.barcodeValidations.map(v => String(v?.message || '').trim()).filter(Boolean)
|
|
: []
|
|
const msg = String(data.message || '').trim()
|
|
const step = String(data.step || '').trim()
|
|
const detail = String(data.detail || '').trim()
|
|
const parts = [msg]
|
|
if (step) parts.push(`step=${step}`)
|
|
if (detail) parts.push(detail)
|
|
if (validationMessages.length) parts.push(validationMessages.join(' | '))
|
|
const merged = parts.filter(Boolean).join(' | ')
|
|
if (merged) return merged
|
|
}
|
|
return err?.message || fallback
|
|
}
|
|
|
|
function logApiError (action, err, payload = null) {
|
|
const status = err?.response?.status
|
|
const data = err?.response?.data
|
|
console.error(`[OrderProductionItemStore] ${action} failed`, {
|
|
status,
|
|
payload,
|
|
data,
|
|
message: err?.message
|
|
})
|
|
}
|
|
|
|
function nowMs () {
|
|
if (typeof performance !== 'undefined' && typeof performance.now === 'function') {
|
|
return performance.now()
|
|
}
|
|
return Date.now()
|
|
}
|
|
|
|
const YAS_NUMERIC_SIZES = new Set(['2', '4', '6', '8', '10', '12', '14'])
|
|
|
|
function safeStr (value) {
|
|
return value == null ? '' : String(value).trim()
|
|
}
|
|
|
|
function normalizeProductionDim1Label (value) {
|
|
let text = safeStr(value)
|
|
if (!text) return ''
|
|
|
|
text = text.toUpperCase()
|
|
const yasMatch = text.match(/^(\d+)\s*(Y|YAS|YAŞ)$/)
|
|
if (yasMatch?.[1] && YAS_NUMERIC_SIZES.has(yasMatch[1])) {
|
|
return yasMatch[1]
|
|
}
|
|
|
|
return text
|
|
}
|
|
|
|
function pickPreferredProductionYasPayloadLabel (currentRaw, nextRaw) {
|
|
const current = safeStr(currentRaw).toUpperCase()
|
|
const next = safeStr(nextRaw).toUpperCase()
|
|
if (!next) return current
|
|
if (!current) return next
|
|
|
|
const currentHasYas = /YAS$|YAŞ$/.test(current)
|
|
const nextHasYas = /YAS$|YAŞ$/.test(next)
|
|
if (!currentHasYas && nextHasYas) return next
|
|
return current
|
|
}
|
|
|
|
function toProductionPayloadDim1 (row, value) {
|
|
const base = normalizeProductionDim1Label(value)
|
|
if (!base) return ''
|
|
if (!YAS_NUMERIC_SIZES.has(base)) return base
|
|
|
|
const map =
|
|
row?.yasPayloadMap && typeof row.yasPayloadMap === 'object'
|
|
? row.yasPayloadMap
|
|
: {}
|
|
const mapped = safeStr(map[base]).toUpperCase()
|
|
if (mapped) return mapped
|
|
return `${base}Y`
|
|
}
|
|
|
|
export const useOrderProductionItemStore = defineStore('orderproductionitems', {
|
|
state: () => ({
|
|
items: [],
|
|
header: null,
|
|
products: [],
|
|
colorOptionsByCode: {},
|
|
newColorOptionsByCode: {},
|
|
secondColorOptionsByKey: {},
|
|
newSecondColorOptionsByKey: {},
|
|
colorRequestsByCode: {},
|
|
newColorRequestsByCode: {},
|
|
secondColorRequestsByKey: {},
|
|
newSecondColorRequestsByKey: {},
|
|
productAttributesByItemType: {},
|
|
productItemAttributesByKey: {},
|
|
cdItemLookups: null,
|
|
cdItemDraftsByCode: {},
|
|
productAttributeDraftsByCode: {},
|
|
knownExistingItemCodes: {},
|
|
loading: false,
|
|
saving: false,
|
|
error: null
|
|
}),
|
|
|
|
getters: {
|
|
productCodeSet (state) {
|
|
const set = new Set()
|
|
for (const p of (state.products || [])) {
|
|
const code = String(p?.ProductCode || '').trim().toUpperCase()
|
|
if (code) set.add(code)
|
|
}
|
|
return set
|
|
}
|
|
},
|
|
|
|
actions: {
|
|
normalizeDim1ForUi (value) {
|
|
return normalizeProductionDim1Label(value)
|
|
},
|
|
pickPreferredYasPayloadLabel (currentRaw, nextRaw) {
|
|
return pickPreferredProductionYasPayloadLabel(currentRaw, nextRaw)
|
|
},
|
|
toPayloadDim1Code (row, value) {
|
|
return toProductionPayloadDim1(row, value)
|
|
},
|
|
classifyItemCode (value) {
|
|
const normalized = String(value || '').trim().toUpperCase()
|
|
if (!normalized) {
|
|
return { normalized: '', mode: 'empty', exists: false }
|
|
}
|
|
const exists = this.productCodeSet.has(normalized) || !!this.knownExistingItemCodes[normalized]
|
|
return {
|
|
normalized,
|
|
mode: exists ? 'existing' : 'new',
|
|
exists
|
|
}
|
|
},
|
|
markItemCodeKnownExisting (itemCode, exists = true) {
|
|
const code = String(itemCode || '').trim().toUpperCase()
|
|
if (!code) return
|
|
this.knownExistingItemCodes = {
|
|
...this.knownExistingItemCodes,
|
|
[code]: !!exists
|
|
}
|
|
},
|
|
|
|
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 = []
|
|
return
|
|
}
|
|
|
|
this.loading = true
|
|
this.error = null
|
|
|
|
try {
|
|
const res = await api.get(`/orders/production-items/${encodeURIComponent(orderHeaderID)}`)
|
|
const data = res?.data
|
|
this.items = Array.isArray(data) ? data : []
|
|
} catch (err) {
|
|
this.items = []
|
|
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 fetchCdItemByCode (code) {
|
|
if (!code) return null
|
|
try {
|
|
const res = await api.get('/product-cditem', { params: { code } })
|
|
const data = res?.data || null
|
|
if (data) {
|
|
this.markItemCodeKnownExisting(code, true)
|
|
}
|
|
return data
|
|
} catch (err) {
|
|
console.error('[OrderProductionItemStore] fetchCdItemByCode failed', err)
|
|
return null
|
|
}
|
|
},
|
|
async fetchColors (productCode) {
|
|
const code = String(productCode || '').trim()
|
|
if (!code) return []
|
|
|
|
if (this.colorOptionsByCode[code]) {
|
|
return this.colorOptionsByCode[code]
|
|
}
|
|
if (this.colorRequestsByCode[code]) {
|
|
return this.colorRequestsByCode[code]
|
|
}
|
|
|
|
try {
|
|
this.colorRequestsByCode[code] = (async () => {
|
|
const t0 = nowMs()
|
|
console.info('[OrderProductionItemStore] fetchColors start', { code })
|
|
const res = await api.get('/product-colors', { params: { code } })
|
|
const data = res?.data
|
|
const list = Array.isArray(data) ? data : []
|
|
if (list.length) this.markItemCodeKnownExisting(code, true)
|
|
this.colorOptionsByCode[code] = list
|
|
console.info('[OrderProductionItemStore] fetchColors done', { code, count: list.length, durationMs: Math.round(nowMs() - t0) })
|
|
return list
|
|
})()
|
|
return await this.colorRequestsByCode[code]
|
|
} catch (err) {
|
|
this.error = err?.response?.data || err?.message || 'Renk listesi alinamadi'
|
|
return []
|
|
} finally {
|
|
delete this.colorRequestsByCode[code]
|
|
}
|
|
},
|
|
async fetchNewColors (productCode) {
|
|
const code = String(productCode || '').trim()
|
|
if (!code) return []
|
|
|
|
if (this.newColorOptionsByCode[code]) {
|
|
return this.newColorOptionsByCode[code]
|
|
}
|
|
if (this.newColorRequestsByCode[code]) {
|
|
return this.newColorRequestsByCode[code]
|
|
}
|
|
|
|
try {
|
|
this.newColorRequestsByCode[code] = (async () => {
|
|
const t0 = nowMs()
|
|
console.info('[OrderProductionItemStore] fetchNewColors start', { code })
|
|
const res = await api.get('/product-newcolors', { params: { code } })
|
|
const data = res?.data
|
|
const list = Array.isArray(data) ? data : []
|
|
this.newColorOptionsByCode[code] = list
|
|
console.info('[OrderProductionItemStore] fetchNewColors done', { code, count: list.length, durationMs: Math.round(nowMs() - t0) })
|
|
return list
|
|
})()
|
|
return await this.newColorRequestsByCode[code]
|
|
} catch (err) {
|
|
this.error = err?.response?.data || err?.message || 'Yeni urun renk listesi alinamadi'
|
|
return []
|
|
} finally {
|
|
delete this.newColorRequestsByCode[code]
|
|
}
|
|
},
|
|
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]
|
|
}
|
|
if (this.secondColorRequestsByKey[key]) {
|
|
return this.secondColorRequestsByKey[key]
|
|
}
|
|
|
|
try {
|
|
this.secondColorRequestsByKey[key] = (async () => {
|
|
const t0 = nowMs()
|
|
console.info('[OrderProductionItemStore] fetchSecondColors start', { code, color })
|
|
const res = await api.get('/product-secondcolor', { params: { code, color } })
|
|
const data = res?.data
|
|
const list = Array.isArray(data) ? data : []
|
|
this.secondColorOptionsByKey[key] = list
|
|
console.info('[OrderProductionItemStore] fetchSecondColors done', { code, color, count: list.length, durationMs: Math.round(nowMs() - t0) })
|
|
return list
|
|
})()
|
|
return await this.secondColorRequestsByKey[key]
|
|
} catch (err) {
|
|
this.error = err?.response?.data || err?.message || '2. renk listesi alinamadi'
|
|
return []
|
|
} finally {
|
|
delete this.secondColorRequestsByKey[key]
|
|
}
|
|
},
|
|
async fetchNewSecondColors (productCode, colorCode) {
|
|
const code = String(productCode || '').trim()
|
|
const color = String(colorCode || '').trim()
|
|
if (!code || !color) return []
|
|
|
|
const key = `${code}::${color}`
|
|
if (this.newSecondColorOptionsByKey[key]) {
|
|
return this.newSecondColorOptionsByKey[key]
|
|
}
|
|
if (this.newSecondColorRequestsByKey[key]) {
|
|
return this.newSecondColorRequestsByKey[key]
|
|
}
|
|
|
|
try {
|
|
this.newSecondColorRequestsByKey[key] = (async () => {
|
|
const t0 = nowMs()
|
|
console.info('[OrderProductionItemStore] fetchNewSecondColors start', { code, color })
|
|
const res = await api.get('/product-newsecondcolor', { params: { code, color } })
|
|
const data = res?.data
|
|
const list = Array.isArray(data) ? data : []
|
|
this.newSecondColorOptionsByKey[key] = list
|
|
console.info('[OrderProductionItemStore] fetchNewSecondColors done', { code, color, count: list.length, durationMs: Math.round(nowMs() - t0) })
|
|
return list
|
|
})()
|
|
return await this.newSecondColorRequestsByKey[key]
|
|
} catch (err) {
|
|
this.error = err?.response?.data || err?.message || 'Yeni urun 2. renk listesi alinamadi'
|
|
return []
|
|
} finally {
|
|
delete this.newSecondColorRequestsByKey[key]
|
|
}
|
|
},
|
|
async fetchProductAttributes (itemTypeCode = 1) {
|
|
const key = String(itemTypeCode || 1)
|
|
if (this.productAttributesByItemType[key]) {
|
|
return this.productAttributesByItemType[key]
|
|
}
|
|
try {
|
|
const res = await api.get('/product-attributes', { params: { itemTypeCode } })
|
|
const list = Array.isArray(res?.data) ? res.data : []
|
|
this.productAttributesByItemType[key] = list
|
|
return list
|
|
} catch (err) {
|
|
this.error = err?.response?.data || err?.message || 'Urun ozellikleri alinamadi'
|
|
return []
|
|
}
|
|
},
|
|
async fetchProductItemAttributes (itemCode, itemTypeCode = 1, force = false) {
|
|
const code = String(itemCode || '').trim().toUpperCase()
|
|
const itc = Number(itemTypeCode || 1)
|
|
if (!code) return []
|
|
const key = `${itc}|${code}`
|
|
if (!force && this.productItemAttributesByKey[key]) {
|
|
return this.productItemAttributesByKey[key]
|
|
}
|
|
try {
|
|
const res = await api.get('/product-item-attributes', { params: { itemTypeCode: itc, itemCode: code } })
|
|
const list = Array.isArray(res?.data) ? res.data : []
|
|
if (list.length) this.markItemCodeKnownExisting(code, true)
|
|
this.productItemAttributesByKey[key] = list
|
|
return list
|
|
} catch (err) {
|
|
this.error = err?.response?.data || err?.message || 'Urunun mevcut ozellikleri alinamadi'
|
|
return []
|
|
}
|
|
},
|
|
async fetchCdItemLookups (force = false) {
|
|
if (this.cdItemLookups && !force) return this.cdItemLookups
|
|
try {
|
|
const res = await api.get('/orders/production-items/cditem-lookups')
|
|
this.cdItemLookups = res?.data || null
|
|
return this.cdItemLookups
|
|
} catch (err) {
|
|
const rid =
|
|
err?.response?.headers?.['x-debug-request-id'] ||
|
|
err?.response?.data?.requestId ||
|
|
''
|
|
logApiError('fetchCdItemLookups', err, { force, requestId: rid })
|
|
if (rid) {
|
|
console.error(`[OrderProductionItemStore] fetchCdItemLookups requestId=${rid}`)
|
|
}
|
|
this.error = extractApiErrorMessage(err, 'cdItem lookup listesi alinamadi')
|
|
return null
|
|
}
|
|
},
|
|
setCdItemDraft (itemCode, draft) {
|
|
const code = String(itemCode || '').trim().toUpperCase()
|
|
if (!code) return
|
|
this.cdItemDraftsByCode = {
|
|
...this.cdItemDraftsByCode,
|
|
[code]: {
|
|
...(draft || {}),
|
|
ItemCode: code,
|
|
ItemTypeCode: Number(draft?.ItemTypeCode || 1)
|
|
}
|
|
}
|
|
},
|
|
getCdItemDraft (itemCode) {
|
|
const code = String(itemCode || '').trim().toUpperCase()
|
|
if (!code) return null
|
|
return this.cdItemDraftsByCode[code] || null
|
|
},
|
|
setProductAttributeDraft (itemCode, rows) {
|
|
const code = String(itemCode || '').trim().toUpperCase()
|
|
if (!code) return
|
|
this.productAttributeDraftsByCode = {
|
|
...this.productAttributeDraftsByCode,
|
|
[code]: Array.isArray(rows) ? rows : []
|
|
}
|
|
},
|
|
getProductAttributeDraft (itemCode) {
|
|
const code = String(itemCode || '').trim().toUpperCase()
|
|
if (!code) return []
|
|
return this.productAttributeDraftsByCode[code] || []
|
|
},
|
|
async validateUpdates (orderHeaderID, lines) {
|
|
if (!orderHeaderID) return { missingCount: 0, missing: [] }
|
|
|
|
this.saving = true
|
|
this.error = null
|
|
|
|
try {
|
|
const t0 = nowMs()
|
|
console.info('[OrderProductionItemStore] validateUpdates start', { orderHeaderID, lineCount: lines?.length || 0 })
|
|
const res = await api.post(
|
|
`/orders/production-items/${encodeURIComponent(orderHeaderID)}/validate`,
|
|
{ lines }
|
|
)
|
|
const data = res?.data || { missingCount: 0, missing: [] }
|
|
const rid = res?.headers?.['x-debug-request-id'] || ''
|
|
console.info('[OrderProductionItemStore] validateUpdates done', {
|
|
orderHeaderID,
|
|
lineCount: lines?.length || 0,
|
|
missingCount: Number(data?.missingCount || 0),
|
|
barcodeValidationCount: Number(data?.barcodeValidationCount || 0),
|
|
requestId: rid,
|
|
durationMs: Math.round(nowMs() - t0)
|
|
})
|
|
return data
|
|
} catch (err) {
|
|
logApiError('validateUpdates', err, { orderHeaderID, lineCount: lines?.length || 0 })
|
|
this.error = extractApiErrorMessage(err, 'Kontrol basarisiz')
|
|
throw err
|
|
} finally {
|
|
this.saving = false
|
|
}
|
|
},
|
|
async applyUpdates (orderHeaderID, lines, insertMissing, cdItems = [], productAttributes = [], headerAverageDueDate = null) {
|
|
if (!orderHeaderID) return { updated: 0, inserted: 0 }
|
|
|
|
this.saving = true
|
|
this.error = null
|
|
|
|
try {
|
|
const t0 = nowMs()
|
|
console.info('[OrderProductionItemStore] applyUpdates start', {
|
|
orderHeaderID,
|
|
lineCount: lines?.length || 0,
|
|
insertMissing: !!insertMissing,
|
|
cdItemCount: cdItems?.length || 0,
|
|
attributeCount: productAttributes?.length || 0,
|
|
headerAverageDueDate
|
|
})
|
|
const res = await api.post(
|
|
`/orders/production-items/${encodeURIComponent(orderHeaderID)}/apply`,
|
|
{
|
|
lines,
|
|
insertMissing,
|
|
cdItems,
|
|
productAttributes,
|
|
HeaderAverageDueDate: headerAverageDueDate
|
|
}
|
|
)
|
|
const data = res?.data || { updated: 0, inserted: 0 }
|
|
const rid = res?.headers?.['x-debug-request-id'] || ''
|
|
console.info('[OrderProductionItemStore] applyUpdates done', {
|
|
orderHeaderID,
|
|
updated: Number(data?.updated || 0),
|
|
inserted: Number(data?.inserted || 0),
|
|
barcodeInserted: Number(data?.barcodeInserted || 0),
|
|
attributeUpserted: Number(data?.attributeUpserted || 0),
|
|
headerUpdated: !!data?.headerUpdated,
|
|
requestId: rid,
|
|
durationMs: Math.round(nowMs() - t0)
|
|
})
|
|
return data
|
|
} catch (err) {
|
|
logApiError('applyUpdates', err, { orderHeaderID, lineCount: lines?.length || 0, insertMissing })
|
|
this.error = extractApiErrorMessage(err, 'Guncelleme basarisiz')
|
|
throw err
|
|
} finally {
|
|
this.saving = false
|
|
}
|
|
}
|
|
}
|
|
})
|