Files
bssapp/ui/src/stores/OrderProductionItemStore.js
2026-04-14 16:17:59 +03:00

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
}
}
}
})