Files
bssapp/ui/src/pages/ProductionProductCostingDefaultQuantities.vue
2026-05-20 13:17:14 +03:00

275 lines
7.2 KiB
Vue

<template>
<q-page class="pcdq-page q-pa-md">
<div class="pcdq-top sticky-top">
<div class="save-toolbar">
<div class="row items-center justify-between q-col-gutter-sm">
<div class="col-auto text-weight-bold">Maliyet Varsayilan Miktarlar</div>
<div class="col-auto row items-center q-gutter-sm">
<q-btn
dense
outline
color="grey-8"
icon="undo"
label="Taslagi Temizle"
:disable="!hasUnsavedChanges"
@click="onClearDraft"
/>
<q-btn
dense
color="primary"
icon="save"
label="Degisiklikleri Kaydet"
:loading="saving"
:disable="!hasUnsavedChanges"
@click="onSaveAll"
/>
<q-btn
dense
outline
color="grey-8"
icon="refresh"
label="Yenile"
:loading="loading || saving"
@click="fetchRows"
/>
</div>
</div>
</div>
<div class="filter-bar q-mb-md">
<div class="row q-col-gutter-sm items-center">
<div class="col-12 col-md-4">
<q-input v-model="search" dense filled clearable label="Ara (Hammadde No)" @update:model-value="debouncedFetch" />
</div>
<div class="col-12 col-md-auto text-grey-7">
Kayit: {{ Array.isArray(rows) ? rows.length : 0 }} | Degisen: {{ dirtyNos.length }}
</div>
</div>
</div>
</div>
<q-banner v-if="error" class="bg-red text-white q-mb-md">
{{ error }}
</q-banner>
<div class="pcdq-table-wrap">
<q-table
class="pcdq-table"
flat
bordered
dense
row-key="nHammaddeTuruNo"
:rows="rows"
:columns="columns"
:loading="loading"
:rows-per-page-options="[0]"
hide-bottom
sticky-header
>
<template #body-cell-actions="props">
<q-td :props="props">
<q-btn
dense
flat
icon="calculate"
color="primary"
@click="onCalcAvg(props.row)"
>
<q-tooltip>Son 10 Ortalama</q-tooltip>
</q-btn>
</q-td>
</template>
<template #body-cell-lDefaultMiktar="props">
<q-td :props="props">
<q-input
:model-value="props.row.lDefaultMiktar"
dense
filled
type="number"
step="0.0001"
@update:model-value="val => onEditQty(props.row, val)"
style="max-width: 140px"
/>
</q-td>
</template>
</q-table>
</div>
</q-page>
</template>
<script setup>
import { computed, onMounted, onUnmounted, ref, watch } from 'vue'
import { useQuasar } from 'quasar'
import { onBeforeRouteLeave } from 'vue-router'
import { storeToRefs } from 'pinia'
import { useProductionProductCostingDefaultQtyStore } from 'src/stores/productionProductCostingDefaultQtyStore'
const $q = useQuasar()
const store = useProductionProductCostingDefaultQtyStore()
const { rows, loading, saving, error, dirtyNos, hasUnsavedChanges } = storeToRefs(store)
const search = ref('')
const columns = [
{ name: 'nHammaddeTuruNo', label: 'HammaddeTuruNo', field: 'nHammaddeTuruNo', align: 'left', sortable: true },
{ name: 'sAciklama', label: 'Aciklama', field: 'sAciklama', align: 'left', sortable: true },
{ name: 'lDefaultMiktar', label: 'Varsayilan Miktar', field: 'lDefaultMiktar', align: 'right', sortable: true },
{ name: 'dteCalcTarihi', label: 'Hesap Tarihi', field: 'dteCalcTarihi', align: 'left', sortable: true },
{ name: 'actions', label: '', field: '__actions', align: 'right', sortable: false }
]
let debounceTimer = null
function debouncedFetch () {
if (debounceTimer) window.clearTimeout(debounceTimer)
debounceTimer = window.setTimeout(() => fetchRows(), 250)
}
async function fetchRows () {
await store.fetch({
search: String(search.value || '').trim()
})
}
function onEditQty (row, val) {
const no = Number(row?.nHammaddeTuruNo || 0)
const qty = Number(val || 0)
if (!(no > 0)) return
store.setDraft(no, { lDefaultMiktar: qty })
// keep table row in sync visually
row.lDefaultMiktar = qty
}
async function onCalcAvg (row) {
const no = Number(row?.nHammaddeTuruNo || 0)
if (!(no > 0)) return
try {
const resp = await store.calcAvgForRow({ nHammaddeTuruNo: no, topN: 10 })
const qty = Number(resp?.lDefaultMiktar || 0)
if (qty > 0) {
store.setDraft(no, { lDefaultMiktar: qty })
row.lDefaultMiktar = qty
$q.notify({ type: 'positive', message: `Ortalama yazildi (n=${Number(resp?.nSampleCount || 0)})`, position: 'top-right' })
} else {
$q.notify({ type: 'warning', message: 'Ortalama bulunamadi', position: 'top-right' })
}
} catch (e) {
$q.notify({ type: 'negative', message: String(e?.message || e || 'Hata'), position: 'top-right' })
}
}
async function onSaveAll () {
try {
const resp = await store.saveAll()
await fetchRows()
$q.notify({ type: 'positive', message: `Kaydedildi (${Number(resp?.updated || 0)})`, position: 'top-right' })
} catch (e) {
$q.notify({ type: 'negative', message: String(e?.message || e || 'Kaydedilemedi'), position: 'top-right' })
}
}
function onClearDraft () {
$q.dialog({
title: 'Taslak Temizlensin mi?',
message: 'Kaydedilmemis degisiklikler silinecek.',
cancel: true,
persistent: true
}).onOk(() => {
store.clearDraft()
fetchRows()
})
}
function ensureBeforeUnloadGuard (enabled) {
if (!enabled) {
window.onbeforeunload = null
return
}
window.onbeforeunload = (e) => {
e.preventDefault()
e.returnValue = ''
return ''
}
}
watch(() => Boolean(hasUnsavedChanges.value), (v) => ensureBeforeUnloadGuard(Boolean(v)))
onBeforeRouteLeave((to, from, next) => {
if (!hasUnsavedChanges.value) return next()
$q.dialog({
title: 'Kaydedilmemis Degisiklikler Var',
message: 'Sayfadan cikmak istiyor musunuz?',
cancel: true,
persistent: true
}).onOk(() => next()).onCancel(() => next(false))
})
onMounted(() => {
fetchRows()
})
onUnmounted(() => {
window.onbeforeunload = null
})
</script>
<style scoped>
.pcdq-page {
background: #fafafa;
display: flex;
flex-direction: column;
height: calc(100vh - 56px);
min-height: 0;
overflow: hidden;
padding-top: 10px;
}
.sticky-top {
flex: 0 0 auto;
z-index: 10;
background: #fafafa;
}
.pcdq-top {
flex: 0 0 auto;
}
.pcdq-table-wrap {
flex: 1 1 auto;
display: flex;
flex-direction: column;
min-height: 0;
overflow: hidden;
}
.pcdq-table {
width: 100%;
display: flex;
flex-direction: column;
flex: 1 1 auto;
min-height: 0;
}
.pcdq-table :deep(.q-table__container) {
flex: 1 1 auto;
display: flex;
flex-direction: column;
min-height: 0;
}
.pcdq-table :deep(.q-table__middle) {
flex: 1 1 auto;
min-height: 0;
overflow: auto !important;
}
.pcdq-table :deep(.q-table thead th) {
background: #f0f0f0;
}
.pcdq-table :deep(.q-table__middle thead tr th) {
position: sticky;
top: 0;
z-index: 10;
}
</style>