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