Merge remote-tracking branch 'origin/master'
This commit is contained in:
@@ -87,6 +87,43 @@
|
||||
|
||||
<template #top-left>
|
||||
<div class="row items-center q-gutter-sm">
|
||||
<q-select
|
||||
v-model="commonHammaddeSelected"
|
||||
dense
|
||||
filled
|
||||
multiple
|
||||
use-chips
|
||||
clearable
|
||||
emit-value
|
||||
map-options
|
||||
option-label="label"
|
||||
option-value="value"
|
||||
:options="commonHammaddeOptions"
|
||||
:disable="loading || saving || commonHammaddeOptions.length === 0"
|
||||
class="pcmm-multi-select"
|
||||
style="min-width: 420px"
|
||||
>
|
||||
<template #selected-item="scope">
|
||||
<q-chip
|
||||
class="q-mr-xs"
|
||||
dense
|
||||
removable
|
||||
@remove="scope.removeAtIndex(scope.index)"
|
||||
>
|
||||
{{ scope.opt.label }}
|
||||
</q-chip>
|
||||
</template>
|
||||
<template #hint>
|
||||
Ekrandaki satirlarda bulunan hammadde turleri (distinct)
|
||||
</template>
|
||||
</q-select>
|
||||
<q-btn
|
||||
color="negative"
|
||||
icon="delete_sweep"
|
||||
label="Butun Parcalardan Kaldir"
|
||||
:disable="loading || saving || (commonHammaddeSelected || []).length === 0"
|
||||
@click="confirmRemoveCommonHammadde"
|
||||
/>
|
||||
<q-btn
|
||||
label="Kolon Filtreleri"
|
||||
icon="filter_alt_off"
|
||||
@@ -327,6 +364,8 @@ const hammaddeLoading = ref(false)
|
||||
|
||||
const bolumByKey = ref({})
|
||||
const hammaddeByKey = ref({})
|
||||
const commonHammaddeSelected = ref([])
|
||||
const hammaddeLabelCache = ref({}) // no -> "NO - ACIKLAMA"
|
||||
|
||||
const columns = [
|
||||
{ name: 'copy_select', label: '', field: 'copy_select', align: 'center' },
|
||||
@@ -345,6 +384,22 @@ const canCopySelected = computed(() => copySelectedCount.value >= 2)
|
||||
const saveSelectedCount = computed(() => Object.keys(saveSelectedKeyMap.value || {}).length)
|
||||
const canSaveSelected = computed(() => saveSelectedCount.value > 0)
|
||||
|
||||
function findHammaddeLabel (no) {
|
||||
const n = Number(no || 0)
|
||||
if (!(n > 0)) return ''
|
||||
const cached = String(hammaddeLabelCache.value?.[String(n)] || '').trim()
|
||||
if (cached) return cached
|
||||
const opt = (Array.isArray(hammaddeOptions.value) ? hammaddeOptions.value : []).find(o => Number(o?.value) === n)
|
||||
const raw = String(opt?.label || '').trim()
|
||||
if (raw) {
|
||||
// Ensure "NO - ACIKLAMA" format. Some option sources may only provide the description.
|
||||
if (/^\d+\s*-\s*/.test(raw)) return raw
|
||||
if (/^\d+\s*$/.test(raw)) return `${n}`
|
||||
return `${n} - ${raw}`
|
||||
}
|
||||
return `${n}`
|
||||
}
|
||||
|
||||
function normalizeSearch (value) {
|
||||
const s = String(value ?? '').trim()
|
||||
if (!s) return ''
|
||||
@@ -459,6 +514,33 @@ const rows = computed(() => {
|
||||
return result
|
||||
})
|
||||
|
||||
const commonHammaddeOptions = computed(() => {
|
||||
const listRows = Array.isArray(rows.value) ? rows.value : []
|
||||
if (listRows.length === 0) return []
|
||||
const keys = listRows.map(r => String(r?.__key || '').trim()).filter(Boolean)
|
||||
if (keys.length === 0) return []
|
||||
|
||||
// DISTINCT across visible rows (union).
|
||||
const set = new Set()
|
||||
for (const k of keys) {
|
||||
const ham = normalizeIntList(hammaddeByKey.value?.[k] || [])
|
||||
ham.forEach(n => { if (n > 0) set.add(n) })
|
||||
}
|
||||
return Array.from(set)
|
||||
.sort((a, b) => a - b)
|
||||
.map(n => ({ value: n, label: findHammaddeLabel(n) }))
|
||||
})
|
||||
|
||||
watch(commonHammaddeOptions, (opts) => {
|
||||
// Ensure labels for large/rare nos that won't be in the first 200 option fetch.
|
||||
try {
|
||||
const nos = (Array.isArray(opts) ? opts : []).map(o => Number(o?.value || 0)).filter(n => n > 0)
|
||||
ensureHammaddeLabelsForNos(nos)
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
}, { immediate: true })
|
||||
|
||||
function hardResetAndRefresh () {
|
||||
// reset view state (filters + selections + dirty)
|
||||
clearAllColumnFilters()
|
||||
@@ -626,9 +708,14 @@ function updateBolumSelection (key, newValue) {
|
||||
function updateHammaddeSelection (key, newValue) {
|
||||
const k = String(key || '').trim()
|
||||
if (!k) return
|
||||
hammaddeByKey.value = {
|
||||
...(hammaddeByKey.value || {}),
|
||||
[k]: normalizeIntList(newValue)
|
||||
const nextList = normalizeIntList(newValue)
|
||||
hammaddeByKey.value = { ...(hammaddeByKey.value || {}), [k]: nextList }
|
||||
// Keep table field in sync for column filtering/sorting/export behavior.
|
||||
const idx = (Array.isArray(mappings.value) ? mappings.value : []).findIndex(r => String(r?.__key || '') === k)
|
||||
if (idx >= 0) {
|
||||
const copy = [...mappings.value]
|
||||
copy[idx] = { ...(copy[idx] || {}), nHammaddeTurleri: [...nextList] }
|
||||
mappings.value = copy
|
||||
}
|
||||
}
|
||||
|
||||
@@ -653,6 +740,62 @@ function pruneHammaddeSelection (rowKey, list) {
|
||||
return allowed
|
||||
}
|
||||
|
||||
function confirmRemoveCommonHammadde () {
|
||||
const selected = normalizeIntList(commonHammaddeSelected.value || [])
|
||||
if (selected.length === 0) return
|
||||
|
||||
const listRows = Array.isArray(rows.value) ? rows.value : []
|
||||
const keys = listRows.map(r => String(r?.__key || '').trim()).filter(Boolean)
|
||||
if (keys.length === 0) return
|
||||
|
||||
// Affected "parcalar": only rows that currently contain at least one selected hammadde.
|
||||
const affectedRows = listRows.filter(r => {
|
||||
const k = String(r?.__key || '').trim()
|
||||
if (!k) return false
|
||||
const current = normalizeIntList(hammaddeByKey.value?.[k] || [])
|
||||
return current.some(v => selected.includes(v))
|
||||
})
|
||||
if (affectedRows.length === 0) {
|
||||
$q.notify({ type: 'info', message: 'Secilen hammadde turleri bu ekrandaki satirlarda bulunmuyor.', position: 'top-right' })
|
||||
return
|
||||
}
|
||||
const affected = affectedRows
|
||||
.map(r => `${String(r?.urunIlkGrubu || '').trim() || '-'} | ${String(r?.urunAnaGrubu || '').trim() || '-'} | ${String(r?.urunAltGrubu || '').trim() || '-'}`)
|
||||
.filter(Boolean)
|
||||
|
||||
const removedLabels = selected.map(n => findHammaddeLabel(n))
|
||||
const htmlList = affected.slice(0, 30).map(x => `<div>${x}</div>`).join('')
|
||||
const more = affected.length > 30 ? `<div class="text-grey-7 q-mt-sm">(+${affected.length - 30} satir daha)</div>` : ''
|
||||
|
||||
$q.dialog({
|
||||
title: 'Butun Parcalardan Kaldir',
|
||||
message: `
|
||||
<div class="q-mb-sm"><b>Kaldirilacak hammadde turleri:</b></div>
|
||||
<div class="q-mb-sm">${removedLabels.map(x => `<div>${x}</div>`).join('')}</div>
|
||||
<div class="q-mb-sm"><b>Etkilenecek parcalar:</b></div>
|
||||
<div style="max-height: 240px; overflow:auto; border: 1px solid #eee; padding: 8px;">${htmlList}${more}</div>
|
||||
<div class="q-mt-sm text-grey-7">Onaylarsaniz secilen hammadde turleri sadece bu satirlardan kaldirilacak ve Degisenleri Kaydet ile kaydedilebilecek.</div>
|
||||
`,
|
||||
html: true,
|
||||
cancel: true,
|
||||
persistent: true,
|
||||
ok: { label: 'Onayla', color: 'negative' },
|
||||
cancelLabel: 'Geri Don'
|
||||
}).onOk(() => {
|
||||
const affectedKeys = affectedRows.map(r => String(r?.__key || '').trim()).filter(Boolean)
|
||||
for (const k of affectedKeys) {
|
||||
const current = normalizeIntList(hammaddeByKey.value?.[k] || [])
|
||||
const next = current.filter(v => !selected.includes(v))
|
||||
if (String(next) === String(current)) continue
|
||||
updateHammaddeSelection(k, next)
|
||||
const row = listRows.find(r => String(r?.__key || '') === k) || (Array.isArray(mappings.value) ? mappings.value : []).find(r => String(r?.__key || '') === k)
|
||||
if (row) markDirty(row)
|
||||
}
|
||||
commonHammaddeSelected.value = []
|
||||
$q.notify({ type: 'positive', message: 'Secilen hammadde turleri etkilenen satirlardan kaldirildi (taslak).', position: 'top-right' })
|
||||
})
|
||||
}
|
||||
|
||||
// label resolution now handled by options' `label` field + selected-item slot (see UserDetail.vue "Piyasalar").
|
||||
async function fetchMappings () {
|
||||
loading.value = true
|
||||
@@ -816,11 +959,55 @@ async function fetchHammaddeOptions (search) {
|
||||
.filter(x => Number.isFinite(x.value) && x.value > 0)
|
||||
.sort((a, b) => a.value - b.value)
|
||||
: []
|
||||
|
||||
// Prime cache with currently loaded options.
|
||||
try {
|
||||
const next = { ...(hammaddeLabelCache.value || {}) }
|
||||
;(Array.isArray(hammaddeOptions.value) ? hammaddeOptions.value : []).forEach(opt => {
|
||||
const no = Number(opt?.value || 0)
|
||||
const label = String(opt?.label || '').trim()
|
||||
if (no > 0 && label) next[String(no)] = label
|
||||
})
|
||||
hammaddeLabelCache.value = next
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
} finally {
|
||||
hammaddeLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function ensureHammaddeLabelsForNos (nos) {
|
||||
const list = normalizeIntList(nos || [])
|
||||
if (list.length === 0) return
|
||||
const missing = list.filter(n => {
|
||||
const cached = String(hammaddeLabelCache.value?.[String(n)] || '').trim()
|
||||
if (cached) return false
|
||||
const opt = (Array.isArray(hammaddeOptions.value) ? hammaddeOptions.value : []).find(o => Number(o?.value) === n)
|
||||
return !String(opt?.label || '').trim()
|
||||
})
|
||||
if (missing.length === 0) return
|
||||
try {
|
||||
const data = await post('/pricing/production-product-costing/options/hammadde-by-nos', {
|
||||
nHammaddeTuruNos: missing
|
||||
}, { trace_id: traceId })
|
||||
const rows = Array.isArray(data) ? data : []
|
||||
const next = { ...(hammaddeLabelCache.value || {}) }
|
||||
rows.forEach(r => {
|
||||
const no = Number(r?.nHammaddeTuruNo || 0)
|
||||
const name = String(r?.sAciklama || '').trim()
|
||||
if (no > 0 && name) next[String(no)] = `${no} - ${name}`
|
||||
})
|
||||
hammaddeLabelCache.value = next
|
||||
} catch (e) {
|
||||
// Non-blocking: fallback will show only the number
|
||||
slog.error('production-product-costing.mtbolum-map', 'hammadde-by-nos:error', {
|
||||
trace_id: traceId,
|
||||
detail: await extractApiErrorDetail(e)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function onFilterMTBolum (val, update) {
|
||||
update(async () => {
|
||||
await fetchMTBolumOptions(val)
|
||||
|
||||
Reference in New Issue
Block a user