diff --git a/ui/src/pages/ProductionProductCostingHasCostDetail.vue b/ui/src/pages/ProductionProductCostingHasCostDetail.vue index 5f44998..1b3593a 100644 --- a/ui/src/pages/ProductionProductCostingHasCostDetail.vue +++ b/ui/src/pages/ProductionProductCostingHasCostDetail.vue @@ -2836,10 +2836,16 @@ function recalculateDetailRow (row, options = {}) { } function recalculateAllDetailRows () { - detailGroups.value = detailGroups.value.map(grp => ({ - ...grp, - items: (Array.isArray(grp?.items) ? grp.items : []).map(row => recalculateDetailRow({ ...row }, { preserveInputs: true })) - })) + // Preserve row identity. QTable editors and the computed group totals must + // observe the exact same reactive row objects. Replacing rows with + // `{ ...row }` after the async exchange-rate request left QTable editing an + // older object while totals watched the replacement object. + detailGroups.value.forEach(grp => { + const items = Array.isArray(grp?.items) ? grp.items : [] + items.forEach(row => { + recalculateDetailRow(row, { preserveInputs: true }) + }) + }) } function resolveRowTRYTutar (row) { @@ -3420,25 +3426,23 @@ async function fetchBulkItemPrices () { } let appliedCount = 0 - detailGroups.value = detailGroups.value.map(grp => ({ - ...grp, - items: (Array.isArray(grp?.items) ? grp.items : []).map(row => { + detailGroups.value.forEach(grp => { + const items = Array.isArray(grp?.items) ? grp.items : [] + items.forEach(row => { const match = updates.find(update => rowMatchesBulkUpdate(row, update)) - if (!match) return row + if (!match) return appliedCount += 1 - return recalculateDetailRow({ - ...row, - inputPrice: match.inputPrice, - fiyat_girilen: match.fiyat_girilen, - inputPricePrBr: match.inputPricePrBr, - fiyat_doviz: match.fiyat_doviz - }, { + row.inputPrice = match.inputPrice + row.fiyat_girilen = match.fiyat_girilen + row.inputPricePrBr = match.inputPricePrBr + row.fiyat_doviz = match.fiyat_doviz + recalculateDetailRow(row, { priceType: match.priceType || 'SAF', updateState: 'bulk', markChanged: true }) }) - })) + }) // Update sub-headers immediately after the bulk response is applied. Do // not wait for the optional previous-cost autofill request below. triggerUIUpdate() @@ -3481,14 +3485,14 @@ async function fetchBulkItemPrices () { }) let filled = 0 - detailGroups.value = detailGroups.value.map(grp => ({ - ...grp, - items: (Array.isArray(grp?.items) ? grp.items : []).map(row => { - if (!row?.requiredPlaceholder) return row - if (isCMGroupName(row?.sAciklama3) || normalizeGroupName(row?.sAciklama3) === 'FABRIC') return row + detailGroups.value.forEach(grp => { + const items = Array.isArray(grp?.items) ? grp.items : [] + items.forEach(row => { + if (!row?.requiredPlaceholder) return + if (isCMGroupName(row?.sAciklama3) || normalizeGroupName(row?.sAciklama3) === 'FABRIC') return const no = parseInt(String(row?.nHammaddeTuruNo || '').trim() || '0', 10) || 0 const hit = byNo[no] - if (!hit) return row + if (!hit) return const next = { ...row } const hasCode = String(next.sKodu || '').trim() !== '' @@ -3508,20 +3512,19 @@ async function fetchBulkItemPrices () { // Only mark if something changed const changed = (next.sKodu !== row.sKodu) || (next.sAciklama !== row.sAciklama) || (next.inputPrice !== row.inputPrice) || (next.inputPricePrBr !== row.inputPricePrBr) - if (!changed) return row + if (!changed) return filled += 1 - return recalculateDetailRow({ - ...next, - __autoFilledFromPrev: true - , + Object.assign(row, next, { + __autoFilledFromPrev: true, __autoFilledFromPrevSameFirma: Boolean(hit?.is_same_firma || hit?.isSameFirma) - }, { + }) + recalculateDetailRow(row, { priceType: 'PREV', updateState: 'autofill', markChanged: true }) }) - })) + }) triggerUIUpdate() if (filled > 0) {