Fix product performance grouped JSON build

This commit is contained in:
M_Kececi
2026-08-29 18:13:17 +03:00
parent b7131ac6ce
commit 68b024cd5d
5 changed files with 302 additions and 0 deletions
@@ -134,6 +134,17 @@
>
<q-tooltip v-if="!canDeleteCosting">Once maliyet olusturulunca aktif olur</q-tooltip>
</q-btn>
<q-btn
label="Malzemeleri Onceki Maliyetlerden Kopyala"
icon="content_copy"
dense
color="deep-purple-7"
outline
class="pcd-toolbar-btn"
:loading="firmCostHistoryLoading"
:disable="!detailHeader || detailLoading || saveLoading || bulkPriceLoading"
@click="openFirmCostHistoryDialog"
/>
</div>
</div>
</div>
@@ -690,6 +701,68 @@
</div>
</q-page>
<q-dialog v-model="firmCostHistoryDialogOpen">
<q-card style="width: min(1600px, 98vw); max-width: 1600px;">
<q-card-section class="row items-center justify-between q-pb-sm">
<div>
<div class="text-subtitle1 text-weight-bold">Malzemeleri Onceki Maliyetlerden Kopyala</div>
<div class="text-caption text-grey-7">
HasCost listesinden bir maliyet secin. Yalnizca bos olan TP/DT gruplari aktarilir.
</div>
</div>
<q-btn flat round dense icon="close" v-close-popup />
</q-card-section>
<q-separator />
<q-card-section class="q-pa-md">
<q-banner v-if="firmCostHistoryError" class="bg-red text-white q-mb-md">
Hata: {{ firmCostHistoryError }}
</q-banner>
<q-input
v-model="firmCostHistoryFilter"
dense
outlined
clearable
debounce="250"
label="Maliyet no, is emri, urun kodu veya urun adi ara"
class="q-mb-md"
>
<template #prepend><q-icon name="search" /></template>
</q-input>
<q-table
v-model:selected="firmCostHistorySelected"
:rows="firmCostHistoryRows"
:columns="firmCostHistoryColumns"
:loading="firmCostHistoryLoading"
:filter="firmCostHistoryFilter"
row-key="nOnMLNo"
selection="single"
dense
flat
bordered
no-data-label="Kopyalanabilecek maliyet kaydi bulunamadi"
:rows-per-page-options="[10, 25, 50, 0]"
>
</q-table>
<div class="text-caption text-grey-7 q-mt-sm">
Mevcut TP veya DT satirlari korunur. Sadece hic satiri olmayan TP/DT grubu, secilen maliyetten tum satir ozellikleriyle doldurulur.
</div>
</q-card-section>
<q-separator />
<q-card-actions align="right" class="q-pa-sm">
<q-btn flat label="Iptal" color="grey-7" v-close-popup />
<q-btn
unelevated
color="deep-purple-7"
icon="content_copy"
label="Bos TP/DT Gruplarini Doldur"
:loading="firmCostCopyLoading"
:disable="firmCostHistorySelected.length !== 1"
@click="copyTPDTFromSelectedCost"
/>
</q-card-actions>
</q-card>
</q-dialog>
<q-dialog v-model="rowEditorDialogOpen" persistent @show="resetRowEditorDialogPosition" @hide="stopRowEditorDialogDrag">
<q-card class="pcd-row-editor-dialog" :style="rowEditorDialogStyle">
<q-card-section
@@ -1142,6 +1215,13 @@ const priceDeviationRows = ref([])
const priceDeviationResolver = ref(null)
const fabricCopyDialogOpen = ref(false)
const fabricCopySelectedKey = ref('')
const firmCostHistoryDialogOpen = ref(false)
const firmCostHistoryLoading = ref(false)
const firmCostCopyLoading = ref(false)
const firmCostHistoryError = ref('')
const firmCostHistoryRows = ref([])
const firmCostHistorySelected = ref([])
const firmCostHistoryFilter = ref('')
// Draggable "Satir Duzenle" dialog (mouse drag by header).
const rowEditorDialogPos = ref({ x: 0, y: 0 })
@@ -1388,6 +1468,21 @@ const priceCurrencyOptions = [
{ label: 'EUR', value: 'EUR' },
{ label: 'GBP', value: 'GBP' }
]
const firmCostHistoryColumns = [
{ name: 'UretimSekli', label: 'Uretim Sekli', field: 'UretimSekli', align: 'left', sortable: true },
{ name: 'nUrtSiparisNo', label: 'Is Emri No', field: 'nUrtSiparisNo', align: 'left', sortable: true },
{ name: 'nOnMLNo', label: 'Maliyet No', field: 'nOnMLNo', align: 'right', sortable: true },
{ name: 'UrunKodu', label: 'Urun Kodu', field: 'UrunKodu', align: 'left', sortable: true },
{ name: 'UrunAdi', label: 'Urun Adi', field: 'UrunAdi', align: 'left', sortable: true },
{ name: 'Tarihi', label: 'Maliyet Tarihi', field: 'Tarihi', align: 'center', sortable: true, format: value => formatDateTR(value) },
{ name: 'lTutarTL', label: 'Tutar TL', field: 'lTutarTL', align: 'right', sortable: true, format: value => formatMoney(value) },
{ name: 'lTutarUSD', label: 'Tutar USD', field: 'lTutarUSD', align: 'right', sortable: true, format: value => formatMoney(value) },
{ name: 'IscilikTutari', label: 'Iscilik Tutari', field: 'IscilikTutari', align: 'right', sortable: true, format: value => formatMoney(value) },
{ name: 'IscilikPrBr', label: 'Iscilik Pr.Br', field: 'IscilikPrBr', align: 'center', sortable: true },
{ name: 'IscilikUSDTutari', label: 'Iscilik USD', field: 'IscilikUSDTutari', align: 'right', sortable: true, format: value => formatMoney(value) },
{ name: 'nUrtReceteID', label: 'Recete No', field: 'nUrtReceteID', align: 'right', sortable: true },
{ name: 'MaliyetDurumu', label: 'Maliyet Durumu', field: 'MaliyetDurumu', align: 'left', sortable: true }
]
const flatDetailRows = computed(() => detailGroups.value.flatMap(grp => Array.isArray(grp?.items) ? grp.items : []))
const priceDeviationAllSelected = computed(() => {
const list = Array.isArray(priceDeviationRows.value) ? priceDeviationRows.value : []
@@ -4935,6 +5030,116 @@ async function confirmBrPriceDeviationIfNeeded () {
return ok
}
function isTPDTGroupName (value) {
const group = normalizeGroupName(value)
return group === 'TP' || group === 'DT'
}
async function openFirmCostHistoryDialog () {
firmCostHistoryDialogOpen.value = true
firmCostHistoryLoading.value = true
firmCostHistoryError.value = ''
firmCostHistoryRows.value = []
firmCostHistorySelected.value = []
firmCostHistoryFilter.value = ''
try {
const data = await get('/pricing/production-product-costing/has-cost-products', {
search: '',
offset: 0,
limit: 1000,
trace_id: traceId.value
})
const currentNo = parseInt(String(onMLNo.value || detailHeader.value?.nOnMLNo || '0'), 10) || 0
firmCostHistoryRows.value = (Array.isArray(data) ? data : [])
.filter(row => (parseInt(String(row?.nOnMLNo || '0'), 10) || 0) !== currentNo)
} catch (err) {
firmCostHistoryError.value = await extractApiErrorDetail(err)
} finally {
firmCostHistoryLoading.value = false
}
}
function confirmFirmCostCopy (source) {
return new Promise(resolve => {
$q.dialog({
title: 'TP/DT Malzemelerini Kopyala',
message: `${source?.UrunKodu || '-'} / Maliyet No ${source?.nOnMLNo || '-'} kaydindan yalnizca bos olan TP/DT gruplari tum ozellikleriyle doldurulacak. Devam edilsin mi?`,
cancel: { label: 'Vazgec', flat: true },
ok: { label: 'Kopyala', color: 'deep-purple-7' },
persistent: true
}).onOk(() => resolve(true)).onCancel(() => resolve(false)).onDismiss(() => resolve(false))
})
}
async function copyTPDTFromSelectedCost () {
const source = firmCostHistorySelected.value[0]
const sourceOnMLNo = parseInt(String(source?.nOnMLNo || '0'), 10) || 0
if (!sourceOnMLNo) return
if (!(await confirmFirmCostCopy(source))) return
firmCostCopyLoading.value = true
firmCostHistoryError.value = ''
try {
const sourceData = await get('/pricing/production-product-costing/has-cost-detail-groups', {
n_onml_no: sourceOnMLNo,
trace_id: traceId.value
})
const sourceGroups = normalizeDetailGroups(sourceData).filter(group => isTPDTGroupName(group?.sAciklama3))
const populatedGroups = new Set(
flatDetailRows.value
.filter(row => isTPDTGroupName(row?.sAciklama3))
.map(row => normalizeGroupName(row?.sAciklama3))
)
const groupsToCopy = sourceGroups.filter(group => !populatedGroups.has(normalizeGroupName(group?.sAciklama3)))
const sourceRows = groupsToCopy.flatMap(group => (
(Array.isArray(group?.items) ? group.items : []).map(row => ({ group, row }))
))
if (sourceRows.length === 0) {
firmCostHistoryError.value = populatedGroups.has('TP') && populatedGroups.has('DT')
? 'Mevcut maliyette TP ve DT gruplari zaten dolu. Hicbir satir degistirilmedi.'
: 'Secilen maliyette bos gruplari dolduracak TP/DT satiri bulunamadi.'
return
}
sourceRows.forEach(({ group, row }) => {
newRowSequence.value += 1
const copiedRow = recalculateDetailRow({
...row,
__rowKey: `firm-history-copy-${sourceOnMLNo}-${newRowSequence.value}`,
isNew: true,
nOnMLNo: detailHeader.value?.nOnMLNo || onMLNo.value || '',
nOnMLDetNo: getNextDetailNo(),
sAciklama3: normalizeGroupName(group?.sAciklama3 || row?.sAciklama3),
requiredPlaceholder: false,
draftChanged: true,
__copiedFromOnMLNo: sourceOnMLNo
}, {
preserveInputs: true,
priceType: 'COPY',
updateState: 'firm-history-copy',
markChanged: true
})
applyEditorRowToGroups(copiedRow)
})
recalculateAllDetailRows()
syncAllGroupsOpen()
schedulePersistLocalDraft()
triggerUIUpdate()
firmCostHistoryDialogOpen.value = false
$q.notify({
type: 'positive',
message: `${groupsToCopy.map(group => normalizeGroupName(group?.sAciklama3)).join(' ve ')} gruplarindaki ${sourceRows.length} satir ${source?.UrunKodu || ''} maliyetinden tum ozellikleriyle eklendi. Kaydet butonu ile onaylayabilirsiniz.`,
position: 'top-right',
timeout: 5000
})
} catch (err) {
firmCostHistoryError.value = await extractApiErrorDetail(err)
} finally {
firmCostCopyLoading.value = false
}
}
async function deleteCosting () {
if (!detailHeader.value) return
const n = parseInt(String(detailHeader.value?.nOnMLNo || detailHeader.value?.NOnMLNo || onMLNo.value || '0'), 10) || 0