Merge remote-tracking branch 'origin/master'

This commit is contained in:
M_Kececi
2026-04-02 16:30:19 +03:00
parent 7a98652a8e
commit 028c11e042
10 changed files with 783 additions and 175 deletions

View File

@@ -216,6 +216,7 @@
filled
label="Yeni 2. Renk"
:disable="isColorSelectionLocked(props.row)"
@update:model-value="() => onNewDim2Change(props.row)"
/>
</q-td>
</template>
@@ -331,6 +332,13 @@ const store = useOrderProductionItemStore()
const BAGGI_CODE_PATTERN = /^[A-Z][0-9]{3}-[A-Z]{3}[0-9]{5}$/
const BAGGI_CODE_ERROR = 'Girdiginiz kod BAGGI kod sistemine uyumlu degil. Format: X999-XXX99999'
function nowMs () {
if (typeof performance !== 'undefined' && typeof performance.now === 'function') {
return performance.now()
}
return Date.now()
}
const orderHeaderID = computed(() => String(route.params.orderHeaderID || '').trim())
const header = computed(() => store.header || {})
const cariLabel = computed(() => {
@@ -517,8 +525,13 @@ function onNewItemChange (row, val, source = 'typed') {
applyNewItemVisualState(row, source)
row.NewColor = ''
row.NewDim2 = ''
row.NewDesc = mergeDescWithAutoNote(row, row.NewDesc || row.OldDesc)
if (row.NewItemCode) {
store.fetchColors(row.NewItemCode)
if (row.NewItemMode === 'new') {
store.fetchNewColors(row.NewItemCode)
} else {
store.fetchColors(row.NewItemCode)
}
}
if (row.NewItemMode === 'new' && isValidBaggiModelCode(row.NewItemCode) && row.NewItemCode !== prevCode) {
openNewCodeSetupFlow(row.NewItemCode)
@@ -560,14 +573,27 @@ function openNewCodeSetupFlow (itemCode) {
function onNewColorChange (row) {
row.NewColor = normalizeShortCode(row.NewColor, 3)
row.NewDim2 = ''
row.NewDesc = mergeDescWithAutoNote(row, row.NewDesc || row.OldDesc)
if (row.NewItemCode && row.NewColor) {
store.fetchSecondColors(row.NewItemCode, row.NewColor)
if (String(row?.NewItemMode || '').trim() === 'new') {
store.fetchNewSecondColors(row.NewItemCode, row.NewColor)
} else {
store.fetchSecondColors(row.NewItemCode, row.NewColor)
}
}
}
function onNewDim2Change (row) {
row.NewDim2 = normalizeShortCode(row.NewDim2, 3)
row.NewDesc = mergeDescWithAutoNote(row, row.NewDesc || row.OldDesc)
}
function getColorOptions (row) {
const code = row?.NewItemCode || ''
const list = store.colorOptionsByCode[code] || []
const isNewMode = String(row?.NewItemMode || '').trim() === 'new'
const list = isNewMode
? (store.newColorOptionsByCode[code] || [])
: (store.colorOptionsByCode[code] || [])
return list.map(c => ({
...c,
colorLabel: `${c.color_code} - ${c.color_description || ''}`.trim()
@@ -578,7 +604,10 @@ function getSecondColorOptions (row) {
const code = row?.NewItemCode || ''
const color = row?.NewColor || ''
const key = `${code}::${color}`
const list = store.secondColorOptionsByKey[key] || []
const isNewMode = String(row?.NewItemMode || '').trim() === 'new'
const list = isNewMode
? (store.newSecondColorOptionsByKey[key] || [])
: (store.secondColorOptionsByKey[key] || [])
return list.map(c => ({
...c,
item_dim2_label: `${c.item_dim2_code} - ${c.color_description || ''}`.trim()
@@ -609,6 +638,55 @@ function isValidBaggiModelCode (code) {
return BAGGI_CODE_PATTERN.test(code)
}
function formatCodeColorDim2 (itemCode, color, dim2) {
const item = String(itemCode || '').trim().toUpperCase()
const c1 = normalizeShortCode(color, 3)
const c2 = normalizeShortCode(dim2, 3)
const c1Safe = c1 || '-'
const c2Safe = c2 || '-'
return `${item}/${c1Safe}/${c2Safe}`
}
function buildAutoUpdateNote (row) {
const oldInfo = formatCodeColorDim2(row?.OldItemCode, row?.OldColor, row?.OldDim2)
const nextInfo = formatCodeColorDim2(row?.NewItemCode, row?.NewColor, row?.NewDim2)
return `Bu siparis satirinda kod ${oldInfo} bilgisinden ${nextInfo} bilgisine guncellenmistir.`
}
function isSelectionCompleteByOldShape (row) {
const hasModel = String(row?.NewItemCode || '').trim().length > 0
if (!hasModel) return false
const oldHasColor = String(row?.OldColor || '').trim().length > 0
const oldHasDim2 = String(row?.OldDim2 || '').trim().length > 0
const hasNewColor = normalizeShortCode(row?.NewColor, 3).length === 3
const hasNewDim2 = normalizeShortCode(row?.NewDim2, 3).length === 3
if (oldHasDim2) return hasNewColor && hasNewDim2
if (oldHasColor) return hasNewColor
return true
}
function stripAutoUpdateNote (text) {
const desc = String(text || '').trim()
if (!desc) return ''
const marker = ' Bu siparis satirinda kod '
const idx = desc.indexOf(marker)
if (idx > -1) return desc.slice(0, idx).trim()
if (desc.startsWith('Bu siparis satirinda kod ')) return ''
return desc
}
function mergeDescWithAutoNote (row, baseDesc) {
const desc = stripAutoUpdateNote(baseDesc)
if (!isSelectionCompleteByOldShape(row)) return desc
const note = buildAutoUpdateNote(row)
if (!note) return desc
if (desc.includes(note)) return desc
if (!desc) return note
return `${desc} ${note}`
}
function validateRowInput (row) {
const entryMode = String(row?.NewItemEntryMode || '').trim()
const newItemCode = String(row.NewItemCode || '').trim().toUpperCase()
@@ -631,6 +709,7 @@ function validateRowInput (row) {
row.NewItemCode = newItemCode
row.NewColor = newColor
row.NewDim2 = newDim2
row.NewDesc = mergeDescWithAutoNote(row, row.NewDesc || row.OldDesc)
return ''
}
@@ -646,7 +725,7 @@ function collectLinesFromRows (selectedRows) {
NewItemCode: String(row.NewItemCode || '').trim().toUpperCase(),
NewColor: normalizeShortCode(row.NewColor, 3),
NewDim2: normalizeShortCode(row.NewDim2, 3),
NewDesc: String((row.NewDesc || row.OldDesc) || '').trim()
NewDesc: mergeDescWithAutoNote(row, row.NewDesc || row.OldDesc)
}
for (const id of (row.OrderLineIDs || [])) {
@@ -668,7 +747,7 @@ function createEmptyCdItemDraft (itemCode) {
ProductHierarchyID: '',
UnitOfMeasureCode1: 'AD',
ItemAccountGrCode: '',
ItemTaxGrCode: '10%',
ItemTaxGrCode: '%10',
ItemPaymentPlanGrCode: '',
ItemDiscountGrCode: '',
ItemVendorGrCode: '',
@@ -715,11 +794,6 @@ function isDummyLookupOption (key, codeRaw, descRaw) {
if (code === '0' || code === '00' || code === '000' || code === '0000') return true
if (desc.includes('DUMMY')) return true
// Is plani dokumanindaki sari/default alanlar
if (key === 'unitOfMeasureCode1List' && code === 'AD') return true
if (key === 'itemTaxGrCodes' && code === '10%') return true
if (key === 'companyCodes' && code === '1') return true
return false
}
@@ -755,19 +829,19 @@ function normalizeCdItemDraftForPayload (draftRaw) {
ItemTypeCode: toIntOrNil(d.ItemTypeCode) || 1,
ItemCode: String(d.ItemCode || '').trim().toUpperCase(),
ItemDimTypeCode: toIntOrNil(d.ItemDimTypeCode) || 1,
ProductTypeCode: 1,
ProductTypeCode: null,
ProductHierarchyID: toIntOrNil(d.ProductHierarchyID),
UnitOfMeasureCode1: 'AD',
ItemAccountGrCode: null,
ItemTaxGrCode: '10%',
ItemTaxGrCode: '%10',
ItemPaymentPlanGrCode: null,
ItemDiscountGrCode: null,
ItemVendorGrCode: null,
PromotionGroupCode: null,
ProductCollectionGrCode: '0',
StorePriceLevelCode: '0',
PerceptionOfFashionCode: '0',
CommercialRoleCode: '0',
ProductCollectionGrCode: null,
StorePriceLevelCode: null,
PerceptionOfFashionCode: null,
CommercialRoleCode: null,
StoreCapacityLevelCode: null,
CustomsTariffNumberCode: null,
CompanyCode: '1'
@@ -956,6 +1030,11 @@ function buildProductionUpdateMailPayload (selectedRows) {
async function sendUpdateMailAfterApply (selectedRows) {
const orderId = String(orderHeaderID.value || '').trim()
if (!orderId) return
const host = String(window?.location?.hostname || '').trim().toLowerCase()
const isLocalHost = host === 'localhost' || host === '127.0.0.1'
if (isLocalHost) {
return
}
try {
const payload = buildProductionUpdateMailPayload(selectedRows)
@@ -1082,12 +1161,14 @@ async function refreshAll () {
}
async function onBulkSubmit () {
const flowStart = nowMs()
const selectedRows = rows.value.filter(r => !!selectedMap.value[r.RowKey])
if (!selectedRows.length) {
$q.notify({ type: 'warning', message: 'Lutfen en az bir satir seciniz.' })
return
}
const prepStart = nowMs()
const { errMsg, lines } = collectLinesFromRows(selectedRows)
if (errMsg) {
$q.notify({ type: 'negative', message: errMsg })
@@ -1112,8 +1193,24 @@ async function onBulkSubmit () {
return
}
console.info('[OrderProductionUpdate] onBulkSubmit prepared', {
orderHeaderID: orderHeaderID.value,
selectedRowCount: selectedRows.length,
lineCount: lines.length,
cdItemCount: cdItems.length,
attributeCount: productAttributes.length,
prepDurationMs: Math.round(nowMs() - prepStart)
})
try {
const validateStart = nowMs()
const validate = await store.validateUpdates(orderHeaderID.value, lines)
console.info('[OrderProductionUpdate] validate finished', {
orderHeaderID: orderHeaderID.value,
lineCount: lines.length,
missingCount: Number(validate?.missingCount || 0),
durationMs: Math.round(nowMs() - validateStart)
})
const missingCount = validate?.missingCount || 0
if (missingCount > 0) {
const missingList = (validate?.missing || []).map(v => (
@@ -1126,7 +1223,13 @@ async function onBulkSubmit () {
ok: { label: 'Ekle ve Guncelle', color: 'primary' },
cancel: { label: 'Vazgec', flat: true }
}).onOk(async () => {
const applyStart = nowMs()
await store.applyUpdates(orderHeaderID.value, lines, true, cdItems, productAttributes)
console.info('[OrderProductionUpdate] apply finished', {
orderHeaderID: orderHeaderID.value,
insertMissing: true,
durationMs: Math.round(nowMs() - applyStart)
})
await store.fetchItems(orderHeaderID.value)
selectedMap.value = {}
await sendUpdateMailAfterApply(selectedRows)
@@ -1134,7 +1237,13 @@ async function onBulkSubmit () {
return
}
const applyStart = nowMs()
await store.applyUpdates(orderHeaderID.value, lines, false, cdItems, productAttributes)
console.info('[OrderProductionUpdate] apply finished', {
orderHeaderID: orderHeaderID.value,
insertMissing: false,
durationMs: Math.round(nowMs() - applyStart)
})
await store.fetchItems(orderHeaderID.value)
selectedMap.value = {}
await sendUpdateMailAfterApply(selectedRows)
@@ -1148,6 +1257,10 @@ async function onBulkSubmit () {
})
$q.notify({ type: 'negative', message: store.error || 'Toplu kayit islemi basarisiz.' })
}
console.info('[OrderProductionUpdate] onBulkSubmit total', {
orderHeaderID: orderHeaderID.value,
durationMs: Math.round(nowMs() - flowStart)
})
}
</script>

View File

@@ -29,13 +29,26 @@ function logApiError (action, err, payload = null) {
})
}
function nowMs () {
if (typeof performance !== 'undefined' && typeof performance.now === 'function') {
return performance.now()
}
return Date.now()
}
export const useOrderProductionItemStore = defineStore('orderproductionitems', {
state: () => ({
items: [],
header: null,
products: [],
colorOptionsByCode: {},
newColorOptionsByCode: {},
secondColorOptionsByKey: {},
newSecondColorOptionsByKey: {},
colorRequestsByCode: {},
newColorRequestsByCode: {},
secondColorRequestsByKey: {},
newSecondColorRequestsByKey: {},
productAttributesByItemType: {},
cdItemLookups: null,
cdItemDraftsByCode: {},
@@ -127,16 +140,57 @@ export const useOrderProductionItemStore = defineStore('orderproductionitems', {
if (this.colorOptionsByCode[code]) {
return this.colorOptionsByCode[code]
}
if (this.colorRequestsByCode[code]) {
return this.colorRequestsByCode[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
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 : []
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) {
@@ -148,16 +202,59 @@ export const useOrderProductionItemStore = defineStore('orderproductionitems', {
if (this.secondColorOptionsByKey[key]) {
return this.secondColorOptionsByKey[key]
}
if (this.secondColorRequestsByKey[key]) {
return this.secondColorRequestsByKey[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
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) {
@@ -231,11 +328,22 @@ export const useOrderProductionItemStore = defineStore('orderproductionitems', {
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 }
)
return res?.data || { missingCount: 0, missing: [] }
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),
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')
@@ -251,11 +359,29 @@ export const useOrderProductionItemStore = defineStore('orderproductionitems', {
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
})
const res = await api.post(
`/orders/production-items/${encodeURIComponent(orderHeaderID)}/apply`,
{ lines, insertMissing, cdItems, productAttributes }
)
return res?.data || { updated: 0, inserted: 0 }
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),
attributeUpserted: Number(data?.attributeUpserted || 0),
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')