Merge remote-tracking branch 'origin/master'
This commit is contained in:
200
ui/src/pages/AccountAgingComparison.vue
Normal file
200
ui/src/pages/AccountAgingComparison.vue
Normal file
@@ -0,0 +1,200 @@
|
||||
<template>
|
||||
<q-page v-if="canReadFinance" class="q-pa-md">
|
||||
<div class="row q-col-gutter-sm items-end q-mb-md">
|
||||
<div class="col-12 col-sm-4 col-md-3">
|
||||
<q-input v-model="accountCode" filled dense label="Cari Kod" clearable />
|
||||
</div>
|
||||
<div class="col-12 col-sm-4 col-md-2">
|
||||
<q-input v-model="endDate" filled dense label="Son Tarih" readonly>
|
||||
<template #append>
|
||||
<q-icon name="event" class="cursor-pointer">
|
||||
<q-popup-proxy cover transition-show="scale" transition-hide="scale">
|
||||
<q-date v-model="endDate" mask="YYYY-MM-DD" locale="tr-TR" />
|
||||
</q-popup-proxy>
|
||||
</q-icon>
|
||||
</template>
|
||||
</q-input>
|
||||
</div>
|
||||
<div class="col-12 col-sm-4 col-md-3">
|
||||
<q-select
|
||||
v-model="parislemler"
|
||||
:options="parislemOptions"
|
||||
emit-value
|
||||
map-options
|
||||
multiple
|
||||
filled
|
||||
dense
|
||||
label="Parasal İşlem Tipi"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<q-btn color="primary" icon="filter_alt" label="Filtrele" :loading="store.loading" @click="load" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="text-caption text-grey-7 q-mb-sm">
|
||||
Son Tarih bazlı fatura/ödeme karşılaştırma ekranı
|
||||
</div>
|
||||
|
||||
<q-table
|
||||
:rows="store.masterRows"
|
||||
:columns="masterColumns"
|
||||
row-key="group_key"
|
||||
flat
|
||||
bordered
|
||||
dense
|
||||
:loading="store.loading"
|
||||
:rows-per-page-options="[25, 50, 100, 0]"
|
||||
>
|
||||
<template #body="props">
|
||||
<q-tr :props="props">
|
||||
<q-td auto-width>
|
||||
<q-btn
|
||||
size="sm"
|
||||
dense
|
||||
flat
|
||||
round
|
||||
:icon="expandedMaster[props.row.group_key] ? 'remove' : 'add'"
|
||||
@click="toggleMaster(props.row.group_key)"
|
||||
/>
|
||||
</q-td>
|
||||
<q-td key="cari8" :props="props">{{ props.row.cari8 }}</q-td>
|
||||
<q-td key="cari_detay" :props="props">{{ props.row.cari_detay }}</q-td>
|
||||
<q-td key="acik_kalem_tutari_usd" :props="props" class="text-right">{{ fmt(props.row.acik_kalem_tutari_usd) }}</q-td>
|
||||
<q-td key="acik_kalem_tutari_try" :props="props" class="text-right">{{ fmt(props.row.acik_kalem_tutari_try) }}</q-td>
|
||||
<q-td key="acik_kalem_ort_vade_gun" :props="props" class="text-right">{{ fmt(props.row.acik_kalem_ort_vade_gun) }}</q-td>
|
||||
<q-td key="acik_kalem_ort_belge_gun" :props="props" class="text-right">{{ fmt(props.row.acik_kalem_ort_belge_gun) }}</q-td>
|
||||
</q-tr>
|
||||
|
||||
<q-tr v-if="expandedMaster[props.row.group_key]">
|
||||
<q-td colspan="100%" class="bg-grey-1">
|
||||
<q-table
|
||||
:rows="store.getCurrenciesByMaster(props.row.group_key)"
|
||||
:columns="currencyColumns"
|
||||
row-key="group_key"
|
||||
flat
|
||||
dense
|
||||
hide-bottom
|
||||
>
|
||||
<template #body="cprops">
|
||||
<q-tr :props="cprops">
|
||||
<q-td auto-width>
|
||||
<q-btn
|
||||
size="sm"
|
||||
dense
|
||||
flat
|
||||
round
|
||||
:icon="expandedCurrency[cprops.row.group_key] ? 'remove' : 'add'"
|
||||
@click="toggleCurrency(cprops.row.group_key)"
|
||||
/>
|
||||
</q-td>
|
||||
<q-td key="doviz_cinsi" :props="cprops">{{ cprops.row.doviz_cinsi }}</q-td>
|
||||
<q-td key="acik_kalem_tutari" :props="cprops" class="text-right">{{ fmt(cprops.row.acik_kalem_tutari) }}</q-td>
|
||||
<q-td key="acik_kalem_usd" :props="cprops" class="text-right">{{ fmt(cprops.row.acik_kalem_usd) }}</q-td>
|
||||
<q-td key="acik_kalem_try" :props="cprops" class="text-right">{{ fmt(cprops.row.acik_kalem_try) }}</q-td>
|
||||
<q-td key="ort_gun" :props="cprops" class="text-right">{{ fmt(cprops.row.ort_gun) }}</q-td>
|
||||
<q-td key="ort_belge_gun" :props="cprops" class="text-right">{{ fmt(cprops.row.ort_belge_gun) }}</q-td>
|
||||
</q-tr>
|
||||
|
||||
<q-tr v-if="expandedCurrency[cprops.row.group_key]">
|
||||
<q-td colspan="100%" class="bg-white">
|
||||
<q-table
|
||||
:rows="store.getDetailsByCurrency(cprops.row.group_key)"
|
||||
:columns="detailColumns"
|
||||
row-key="detail_key"
|
||||
flat
|
||||
dense
|
||||
hide-bottom
|
||||
/>
|
||||
</q-td>
|
||||
</q-tr>
|
||||
</template>
|
||||
</q-table>
|
||||
</q-td>
|
||||
</q-tr>
|
||||
</template>
|
||||
</q-table>
|
||||
</q-page>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, ref } from 'vue'
|
||||
import { useStatementAgingStore } from 'src/stores/statementAgingStore'
|
||||
import { usePermission } from 'src/composables/usePermission'
|
||||
|
||||
const { canRead } = usePermission()
|
||||
const canReadFinance = canRead('finance')
|
||||
|
||||
const store = useStatementAgingStore()
|
||||
const accountCode = ref('')
|
||||
const endDate = ref(new Date().toISOString().slice(0, 10))
|
||||
const parislemler = ref(['2'])
|
||||
|
||||
const parislemOptions = [
|
||||
{ label: '1-2 hesap', value: '2' },
|
||||
{ label: '1-3 hesap', value: '3' }
|
||||
]
|
||||
|
||||
const expandedMaster = reactive({})
|
||||
const expandedCurrency = reactive({})
|
||||
|
||||
const masterColumns = [
|
||||
{ name: 'expand', label: '', field: 'expand' },
|
||||
{ name: 'cari8', label: 'Cari', field: 'cari8', align: 'left' },
|
||||
{ name: 'cari_detay', label: 'Cari Detay', field: 'cari_detay', align: 'left' },
|
||||
{ name: 'acik_kalem_tutari_usd', label: 'Açık Kalem USD', field: 'acik_kalem_tutari_usd', align: 'right' },
|
||||
{ name: 'acik_kalem_tutari_try', label: 'Açık Kalem TRY', field: 'acik_kalem_tutari_try', align: 'right' },
|
||||
{ name: 'acik_kalem_ort_vade_gun', label: 'Ort Vade Gün', field: 'acik_kalem_ort_vade_gun', align: 'right' },
|
||||
{ name: 'acik_kalem_ort_belge_gun', label: 'Ort Belge Gün', field: 'acik_kalem_ort_belge_gun', align: 'right' }
|
||||
]
|
||||
|
||||
const currencyColumns = [
|
||||
{ name: 'expand', label: '', field: 'expand' },
|
||||
{ name: 'doviz_cinsi', label: 'Döviz', field: 'doviz_cinsi', align: 'left' },
|
||||
{ name: 'acik_kalem_tutari', label: 'Açık Kalem Tutar', field: 'acik_kalem_tutari', align: 'right' },
|
||||
{ name: 'acik_kalem_usd', label: 'Açık Kalem USD', field: 'acik_kalem_usd', align: 'right' },
|
||||
{ name: 'acik_kalem_try', label: 'Açık Kalem TRY', field: 'acik_kalem_try', align: 'right' },
|
||||
{ name: 'ort_gun', label: 'Ort Gün', field: 'ort_gun', align: 'right' },
|
||||
{ name: 'ort_belge_gun', label: 'Ort Belge Gün', field: 'ort_belge_gun', align: 'right' }
|
||||
]
|
||||
|
||||
const detailColumns = [
|
||||
{ name: 'fatura_cari', label: 'Fatura Cari', field: 'fatura_cari', align: 'left' },
|
||||
{ name: 'odeme_cari', label: 'Ödeme Cari', field: 'odeme_cari', align: 'left' },
|
||||
{ name: 'fatura_ref', label: 'Fatura Ref', field: 'fatura_ref', align: 'left' },
|
||||
{ name: 'odeme_ref', label: 'Ödeme Ref', field: 'odeme_ref', align: 'left' },
|
||||
{ name: 'fatura_tarihi', label: 'Fatura Tarihi', field: 'fatura_tarihi', align: 'left' },
|
||||
{ name: 'odeme_tarihi', label: 'Ödeme Tarihi', field: 'odeme_tarihi', align: 'left' },
|
||||
{ name: 'eslesen_tutar', label: 'Eşleşen Tutar', field: 'eslesen_tutar', align: 'right' },
|
||||
{ name: 'usd_tutar', label: 'USD Tutar', field: 'usd_tutar', align: 'right' },
|
||||
{ name: 'try_tutar', label: 'TRY Tutar', field: 'try_tutar', align: 'right' },
|
||||
{ name: 'gun_sayisi', label: 'Gün Sayısı', field: 'gun_sayisi', align: 'right' },
|
||||
{ name: 'gun_sayisi_docdate', label: 'Belge Gün', field: 'gun_sayisi_docdate', align: 'right' },
|
||||
{ name: 'gun_kur', label: 'Gün Kur', field: 'gun_kur', align: 'right' },
|
||||
{ name: 'aciklama', label: 'Açıklama', field: 'aciklama', align: 'left' }
|
||||
]
|
||||
|
||||
function fmt(v) {
|
||||
const n = Number(v)
|
||||
if (!Number.isFinite(n)) return v ?? ''
|
||||
return n.toLocaleString('tr-TR', { minimumFractionDigits: 2, maximumFractionDigits: 2 })
|
||||
}
|
||||
|
||||
function toggleMaster(key) {
|
||||
expandedMaster[key] = !expandedMaster[key]
|
||||
}
|
||||
|
||||
function toggleCurrency(key) {
|
||||
expandedCurrency[key] = !expandedCurrency[key]
|
||||
}
|
||||
|
||||
async function load() {
|
||||
await store.load({
|
||||
accountcode: accountCode.value || '',
|
||||
enddate: endDate.value || '',
|
||||
parislemler: parislemler.value
|
||||
})
|
||||
}
|
||||
|
||||
load()
|
||||
</script>
|
||||
@@ -1,454 +1,129 @@
|
||||
<template>
|
||||
<q-page v-if="canReadFinance" class="q-pa-md page-layout">
|
||||
<div class="filter-sticky" :class="{ collapsed: filtersCollapsed }">
|
||||
<div class="top-actions row q-col-gutter-sm items-end q-mb-sm" :class="{ 'single-line': filtersCollapsed }">
|
||||
<div class="col-12 col-sm-6 col-md-2">
|
||||
<q-input
|
||||
v-model="store.filters.selectedDate"
|
||||
label="Tarih (Bugün)"
|
||||
<template>
|
||||
<q-page v-if="canReadFinance" class="q-px-md q-pb-md q-pt-xs page-col statement-page">
|
||||
<q-slide-transition>
|
||||
<div v-show="!filtersCollapsed" class="local-filter-bar compact-filter q-pa-sm q-mb-xs">
|
||||
<div class="row q-col-gutter-sm items-end">
|
||||
<div class="col-12 col-md-5">
|
||||
<q-select
|
||||
v-model="selectedCari"
|
||||
:options="filteredOptions"
|
||||
label="Cari kod / isim"
|
||||
filled
|
||||
dense
|
||||
readonly
|
||||
disable
|
||||
clearable
|
||||
use-input
|
||||
input-debounce="300"
|
||||
@filter="filterCari"
|
||||
emit-value
|
||||
map-options
|
||||
:loading="accountStore.loading"
|
||||
option-value="value"
|
||||
option-label="label"
|
||||
behavior="menu"
|
||||
:keep-selected="true"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-sm-6 col-md-2">
|
||||
<q-input v-model="dateTo" label="Son tarih" filled dense clearable readonly>
|
||||
<template #append>
|
||||
<q-icon name="event" class="cursor-pointer">
|
||||
<q-popup-proxy cover transition-show="scale" transition-hide="scale">
|
||||
<q-date v-model="dateTo" mask="YYYY-MM-DD" locale="tr-TR" />
|
||||
</q-popup-proxy>
|
||||
</q-icon>
|
||||
</template>
|
||||
</q-input>
|
||||
</div>
|
||||
<div class="col-12 col-sm-6 col-md-3">
|
||||
<q-toggle
|
||||
v-model="store.filters.excludeZeroBalance12"
|
||||
<q-select
|
||||
v-model="selectedMonType"
|
||||
:options="monetaryTypeOptions"
|
||||
label="Parasal İşlem Tipi"
|
||||
emit-value
|
||||
map-options
|
||||
filled
|
||||
dense
|
||||
label="1_2 Bakiyesi Sıfır Olanları Alma"
|
||||
@update:model-value="onToggle12Changed"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-sm-6 col-md-3">
|
||||
<q-toggle
|
||||
v-model="store.filters.excludeZeroBalance13"
|
||||
dense
|
||||
label="1_3 Bakiyesi Sıfır Olanları Alma"
|
||||
@update:model-value="onToggle13Changed"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="col-auto">
|
||||
<q-btn
|
||||
color="primary"
|
||||
icon="download"
|
||||
label="Verileri Yenile"
|
||||
:loading="store.loading"
|
||||
@click="store.fetchBalances()"
|
||||
/>
|
||||
<q-btn color="primary" icon="filter_alt" label="Filtrele" @click="onFilterClick" />
|
||||
</div>
|
||||
|
||||
<div class="col-auto">
|
||||
<q-btn
|
||||
flat
|
||||
color="grey-8"
|
||||
icon="restart_alt"
|
||||
label="Sıfırla"
|
||||
@click="onReset"
|
||||
/>
|
||||
<q-btn flat color="grey-8" icon="restart_alt" label="Sıfırla" @click="resetFilters" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</q-slide-transition>
|
||||
|
||||
<q-slide-transition>
|
||||
<div v-show="!filtersCollapsed" class="filters-panel q-pa-sm q-mb-md">
|
||||
<div class="row q-col-gutter-sm">
|
||||
<div class="col-12 col-sm-6 col-md-4">
|
||||
<q-input
|
||||
v-model="store.filters.cariSearch"
|
||||
filled
|
||||
dense
|
||||
label="Cari Kodu / Cari Adı"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-sm-6 col-md-2">
|
||||
<q-select
|
||||
v-model="store.filters.cariIlkGrup"
|
||||
:options="store.cariIlkGrupOptions"
|
||||
multiple
|
||||
emit-value
|
||||
map-options
|
||||
filled
|
||||
dense
|
||||
options-dense
|
||||
class="compact-select"
|
||||
label="Cari İlk Grup"
|
||||
:display-value="selectionLabel(store.filters.cariIlkGrup, 'Cari İlk Grup')"
|
||||
>
|
||||
<template #before-options>
|
||||
<q-item clickable dense @click.stop="store.selectAll('cariIlkGrup', store.cariIlkGrupOptions)">
|
||||
<q-item-section>Tümünü Seç</q-item-section>
|
||||
</q-item>
|
||||
<q-item clickable dense @click.stop="store.clearAll('cariIlkGrup')">
|
||||
<q-item-section>Tümünü Temizle</q-item-section>
|
||||
</q-item>
|
||||
<q-separator />
|
||||
</template>
|
||||
<template #option="scope">
|
||||
<q-item v-bind="scope.itemProps">
|
||||
<q-item-section avatar>
|
||||
<q-checkbox :model-value="scope.selected" />
|
||||
</q-item-section>
|
||||
<q-item-section>
|
||||
<q-item-label>{{ scope.opt.label }}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</template>
|
||||
</q-select>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-sm-6 col-md-2">
|
||||
<q-select
|
||||
v-model="store.filters.piyasa"
|
||||
:options="store.piyasaOptions"
|
||||
multiple
|
||||
emit-value
|
||||
map-options
|
||||
filled
|
||||
dense
|
||||
options-dense
|
||||
class="compact-select"
|
||||
label="Piyasa"
|
||||
:display-value="selectionLabel(store.filters.piyasa, 'Piyasa')"
|
||||
>
|
||||
<template #before-options>
|
||||
<q-item clickable dense @click.stop="store.selectAll('piyasa', store.piyasaOptions)">
|
||||
<q-item-section>Tümünü Seç</q-item-section>
|
||||
</q-item>
|
||||
<q-item clickable dense @click.stop="store.clearAll('piyasa')">
|
||||
<q-item-section>Tümünü Temizle</q-item-section>
|
||||
</q-item>
|
||||
<q-separator />
|
||||
</template>
|
||||
<template #option="scope">
|
||||
<q-item v-bind="scope.itemProps">
|
||||
<q-item-section avatar>
|
||||
<q-checkbox :model-value="scope.selected" />
|
||||
</q-item-section>
|
||||
<q-item-section>
|
||||
<q-item-label>{{ scope.opt.label }}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</template>
|
||||
</q-select>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-sm-6 col-md-2">
|
||||
<q-select
|
||||
v-model="store.filters.temsilci"
|
||||
:options="store.temsilciOptions"
|
||||
multiple
|
||||
emit-value
|
||||
map-options
|
||||
filled
|
||||
dense
|
||||
options-dense
|
||||
class="compact-select"
|
||||
label="Temsilci"
|
||||
:display-value="selectionLabel(store.filters.temsilci, 'Temsilci')"
|
||||
>
|
||||
<template #before-options>
|
||||
<q-item clickable dense @click.stop="store.selectAll('temsilci', store.temsilciOptions)">
|
||||
<q-item-section>Tümünü Seç</q-item-section>
|
||||
</q-item>
|
||||
<q-item clickable dense @click.stop="store.clearAll('temsilci')">
|
||||
<q-item-section>Tümünü Temizle</q-item-section>
|
||||
</q-item>
|
||||
<q-separator />
|
||||
</template>
|
||||
<template #option="scope">
|
||||
<q-item v-bind="scope.itemProps">
|
||||
<q-item-section avatar>
|
||||
<q-checkbox :model-value="scope.selected" />
|
||||
</q-item-section>
|
||||
<q-item-section>
|
||||
<q-item-label>{{ scope.opt.label }}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</template>
|
||||
</q-select>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-sm-6 col-md-2">
|
||||
<q-select
|
||||
v-model="store.filters.riskDurumu"
|
||||
:options="store.riskDurumuOptions"
|
||||
multiple
|
||||
emit-value
|
||||
map-options
|
||||
filled
|
||||
dense
|
||||
options-dense
|
||||
class="compact-select"
|
||||
label="Risk Durumu"
|
||||
:display-value="selectionLabel(store.filters.riskDurumu, 'Risk Durumu')"
|
||||
>
|
||||
<template #before-options>
|
||||
<q-item clickable dense @click.stop="store.selectAll('riskDurumu', store.riskDurumuOptions)">
|
||||
<q-item-section>Tümünü Seç</q-item-section>
|
||||
</q-item>
|
||||
<q-item clickable dense @click.stop="store.clearAll('riskDurumu')">
|
||||
<q-item-section>Tümünü Temizle</q-item-section>
|
||||
</q-item>
|
||||
<q-separator />
|
||||
</template>
|
||||
<template #option="scope">
|
||||
<q-item v-bind="scope.itemProps">
|
||||
<q-item-section avatar>
|
||||
<q-checkbox :model-value="scope.selected" />
|
||||
</q-item-section>
|
||||
<q-item-section>
|
||||
<q-item-label>{{ scope.opt.label }}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</template>
|
||||
</q-select>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-sm-6 col-md-2">
|
||||
<q-select
|
||||
v-model="store.filters.islemTipi"
|
||||
:options="islemTipiOptions"
|
||||
multiple
|
||||
emit-value
|
||||
map-options
|
||||
filled
|
||||
dense
|
||||
options-dense
|
||||
class="compact-select"
|
||||
label="İşlem Tipi"
|
||||
:display-value="selectionLabel(store.filters.islemTipi, 'İşlem Tipi')"
|
||||
>
|
||||
<template #before-options>
|
||||
<q-item clickable dense @click.stop="store.selectAll('islemTipi', islemTipiOptions)">
|
||||
<q-item-section>Tümünü Seç</q-item-section>
|
||||
</q-item>
|
||||
<q-item clickable dense @click.stop="store.clearAll('islemTipi')">
|
||||
<q-item-section>Tümünü Temizle</q-item-section>
|
||||
</q-item>
|
||||
<q-separator />
|
||||
</template>
|
||||
<template #option="scope">
|
||||
<q-item v-bind="scope.itemProps">
|
||||
<q-item-section avatar>
|
||||
<q-checkbox :model-value="scope.selected" />
|
||||
</q-item-section>
|
||||
<q-item-section>
|
||||
<q-item-label>{{ scope.opt.label }}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</template>
|
||||
</q-select>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-sm-6 col-md-2">
|
||||
<q-select
|
||||
v-model="store.filters.ulke"
|
||||
:options="store.ulkeOptions"
|
||||
multiple
|
||||
emit-value
|
||||
map-options
|
||||
filled
|
||||
dense
|
||||
options-dense
|
||||
class="compact-select"
|
||||
label="Ülke"
|
||||
:display-value="selectionLabel(store.filters.ulke, 'Ülke')"
|
||||
>
|
||||
<template #before-options>
|
||||
<q-item clickable dense @click.stop="store.selectAll('ulke', store.ulkeOptions)">
|
||||
<q-item-section>Tümünü Seç</q-item-section>
|
||||
</q-item>
|
||||
<q-item clickable dense @click.stop="store.clearAll('ulke')">
|
||||
<q-item-section>Tümünü Temizle</q-item-section>
|
||||
</q-item>
|
||||
<q-separator />
|
||||
</template>
|
||||
<template #option="scope">
|
||||
<q-item v-bind="scope.itemProps">
|
||||
<q-item-section avatar>
|
||||
<q-checkbox :model-value="scope.selected" />
|
||||
</q-item-section>
|
||||
<q-item-section>
|
||||
<q-item-label>{{ scope.opt.label }}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</template>
|
||||
</q-select>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-sm-6 col-md-2">
|
||||
<q-select
|
||||
v-model="store.filters.il"
|
||||
:options="store.ilOptions"
|
||||
multiple
|
||||
emit-value
|
||||
map-options
|
||||
filled
|
||||
dense
|
||||
options-dense
|
||||
class="compact-select"
|
||||
label="İl"
|
||||
:display-value="selectionLabel(store.filters.il, 'İl')"
|
||||
>
|
||||
<template #before-options>
|
||||
<q-item clickable dense @click.stop="store.selectAll('il', store.ilOptions)">
|
||||
<q-item-section>Tümünü Seç</q-item-section>
|
||||
</q-item>
|
||||
<q-item clickable dense @click.stop="store.clearAll('il')">
|
||||
<q-item-section>Tümünü Temizle</q-item-section>
|
||||
</q-item>
|
||||
<q-separator />
|
||||
</template>
|
||||
<template #option="scope">
|
||||
<q-item v-bind="scope.itemProps">
|
||||
<q-item-section avatar>
|
||||
<q-checkbox :model-value="scope.selected" />
|
||||
</q-item-section>
|
||||
<q-item-section>
|
||||
<q-item-label>{{ scope.opt.label }}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</template>
|
||||
</q-select>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-sm-6 col-md-2">
|
||||
<q-select
|
||||
v-model="store.filters.ilce"
|
||||
:options="store.ilceOptions"
|
||||
multiple
|
||||
emit-value
|
||||
map-options
|
||||
filled
|
||||
dense
|
||||
options-dense
|
||||
class="compact-select"
|
||||
label="İlçe"
|
||||
:display-value="selectionLabel(store.filters.ilce, 'İlçe')"
|
||||
>
|
||||
<template #before-options>
|
||||
<q-item clickable dense @click.stop="store.selectAll('ilce', store.ilceOptions)">
|
||||
<q-item-section>Tümünü Seç</q-item-section>
|
||||
</q-item>
|
||||
<q-item clickable dense @click.stop="store.clearAll('ilce')">
|
||||
<q-item-section>Tümünü Temizle</q-item-section>
|
||||
</q-item>
|
||||
<q-separator />
|
||||
</template>
|
||||
<template #option="scope">
|
||||
<q-item v-bind="scope.itemProps">
|
||||
<q-item-section avatar>
|
||||
<q-checkbox :model-value="scope.selected" />
|
||||
</q-item-section>
|
||||
<q-item-section>
|
||||
<q-item-label>{{ scope.opt.label }}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</template>
|
||||
</q-select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</q-slide-transition>
|
||||
|
||||
<q-banner v-if="store.error" class="bg-red-1 text-negative q-mb-md rounded-borders">
|
||||
{{ store.error }}
|
||||
</q-banner>
|
||||
|
||||
<q-banner v-if="!store.hasFetched && !store.loading" class="bg-blue-1 text-primary q-mb-md rounded-borders">
|
||||
Veriler bugünün cache tablosundan okunur. Verileri Yenile ile tekrar yükleyebilirsiniz.
|
||||
</q-banner>
|
||||
</div>
|
||||
|
||||
<div class="table-area">
|
||||
<div class="sticky-bar row justify-between items-center q-pa-sm bg-grey-1">
|
||||
<div />
|
||||
<div class="row items-center q-gutter-sm">
|
||||
<q-btn
|
||||
flat
|
||||
color="secondary"
|
||||
icon="list"
|
||||
:label="allDetailsOpen ? 'Tüm Detayları Kapat' : 'Tüm Detayları Aç'"
|
||||
@click="toggleAllDetails"
|
||||
/>
|
||||
<q-btn-dropdown
|
||||
v-if="canExportFinance"
|
||||
flat
|
||||
color="red"
|
||||
icon="picture_as_pdf"
|
||||
label="Yazdır"
|
||||
>
|
||||
<q-list style="min-width: 240px">
|
||||
<q-item clickable v-close-popup @click="downloadAgingBalancePDF(true)">
|
||||
<q-item-section class="text-primary">
|
||||
Detaylı Cari Yaşlandırmalı Bakiye Listesi Yazdır
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item clickable v-close-popup @click="downloadAgingBalancePDF(false)">
|
||||
<q-item-section class="text-secondary">
|
||||
Detaysız Cari Yaşlandırmalı Bakiye Listesi Yazdır
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
</q-btn-dropdown>
|
||||
<q-btn
|
||||
v-if="canExportFinance"
|
||||
flat
|
||||
color="green-8"
|
||||
icon="table_view"
|
||||
label="Excel"
|
||||
@click="downloadAgingBalanceExcel"
|
||||
/>
|
||||
<q-btn
|
||||
flat
|
||||
color="primary"
|
||||
:icon="filtersCollapsed ? 'unfold_more' : 'unfold_less'"
|
||||
:label="filtersCollapsed ? 'Filtreleri Genişlet' : 'Filtreleri Daralt'"
|
||||
@click="toggleFiltersCollapsed"
|
||||
/>
|
||||
</div>
|
||||
<div class="table-scroll">
|
||||
<div class="sticky-bar row justify-end items-center q-pa-sm bg-grey-1 q-gutter-sm">
|
||||
<q-btn
|
||||
flat
|
||||
color="primary"
|
||||
:icon="filtersCollapsed ? 'unfold_more' : 'unfold_less'"
|
||||
:label="filtersCollapsed ? 'Filtreleri Genişlet' : 'Filtreleri Daralt'"
|
||||
@click="toggleFiltersCollapsed"
|
||||
/>
|
||||
<q-btn-dropdown
|
||||
v-if="canExportFinance"
|
||||
flat
|
||||
color="red"
|
||||
icon="picture_as_pdf"
|
||||
label="Yazdır"
|
||||
>
|
||||
<q-list style="min-width: 240px">
|
||||
<q-item clickable v-close-popup @click="downloadAgingBalancePDF(true)">
|
||||
<q-item-section class="text-primary">
|
||||
Detaylı Cari Yaşlandırmalı Bakiye Listesi Yazdır
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item clickable v-close-popup @click="downloadAgingBalancePDF(false)">
|
||||
<q-item-section class="text-secondary">
|
||||
Detaysız Cari Yaşlandırmalı Bakiye Listesi Yazdır
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
</q-btn-dropdown>
|
||||
<q-btn
|
||||
v-if="canExportFinance"
|
||||
flat
|
||||
color="green-8"
|
||||
icon="table_view"
|
||||
label="Excel"
|
||||
@click="downloadAgingBalanceExcel"
|
||||
/>
|
||||
<q-btn
|
||||
flat
|
||||
color="secondary"
|
||||
icon="list"
|
||||
:label="allDetailsOpen ? 'Tüm Detayları Kapat' : 'Tüm Detayları Aç'"
|
||||
@click="toggleAllDetails"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<q-table
|
||||
title="Cari Yaşlandırmalı Cari Bakiye Listesi"
|
||||
:rows="store.summaryRows"
|
||||
:columns="summaryColumns"
|
||||
class="sticky-table statement-table"
|
||||
title="Cari Yaşlandırmalı Ekstre"
|
||||
:rows="agingStore.masterRows"
|
||||
:columns="masterColumns"
|
||||
row-key="group_key"
|
||||
:loading="store.loading"
|
||||
flat
|
||||
bordered
|
||||
dense
|
||||
wrap-cells
|
||||
separator="cell"
|
||||
hide-bottom
|
||||
wrap-cells
|
||||
:rows-per-page-options="[0]"
|
||||
:pagination="{ rowsPerPage: 0 }"
|
||||
:loading="agingStore.loading"
|
||||
:table-style="{ tableLayout: 'fixed', width: '100%' }"
|
||||
class="balance-table"
|
||||
>
|
||||
<template #header="props">
|
||||
<q-tr :props="props" class="header-row">
|
||||
<q-th v-for="col in props.cols" :key="col.name" :props="props">
|
||||
{{ col.label }}
|
||||
</q-th>
|
||||
</q-tr>
|
||||
<q-tr class="totals-row">
|
||||
<q-th
|
||||
v-for="col in props.cols"
|
||||
:key="`tot-${col.name}`"
|
||||
:class="col.align === 'right' ? 'text-right' : ''"
|
||||
>
|
||||
{{ totalCellValue(col.name) }}
|
||||
</q-th>
|
||||
<q-th v-for="col in props.cols" :key="col.name" :props="props">{{ col.label }}</q-th>
|
||||
</q-tr>
|
||||
</template>
|
||||
|
||||
<template #body="props">
|
||||
<q-tr :props="props" class="sub-header-row">
|
||||
<q-tr :props="props" class="master-row">
|
||||
<q-td v-for="col in props.cols" :key="col.name" :props="props">
|
||||
<q-btn
|
||||
v-if="col.name === 'expand'"
|
||||
@@ -456,287 +131,301 @@
|
||||
flat
|
||||
round
|
||||
size="sm"
|
||||
:icon="expanded[props.row.group_key] ? 'expand_less' : 'expand_more'"
|
||||
@click="toggleGroup(props.row.group_key)"
|
||||
:icon="masterExpanded[props.row.group_key] ? 'expand_less' : 'expand_more'"
|
||||
@click="toggleMaster(props.row.group_key)"
|
||||
/>
|
||||
<span v-else-if="col.name === 'prbr_1_2'" class="text-right block prbr-cell">
|
||||
{{ formatCurrencyMap(props.row.bakiye_1_2_map) }}
|
||||
</span>
|
||||
<span v-else-if="col.name === 'prbr_1_3'" class="text-right block prbr-cell">
|
||||
{{ formatCurrencyMap(props.row.bakiye_1_3_map) }}
|
||||
</span>
|
||||
<span v-else-if="staticMoneyFields.includes(col.name)" class="text-center block">
|
||||
<span
|
||||
v-else-if="masterNumericCols.includes(col.name)"
|
||||
:class="['block', masterCenteredCols.includes(col.name) ? 'text-center' : 'text-right']"
|
||||
>
|
||||
{{ formatAmount(props.row[col.field]) }}
|
||||
</span>
|
||||
<span v-else-if="dayFields.includes(col.name)" class="text-center block">
|
||||
{{ formatDay(props.row[col.field]) }}
|
||||
</span>
|
||||
<span v-else>{{ props.row[col.field] || '-' }}</span>
|
||||
<span v-else>{{ props.row[col.field] ?? '-' }}</span>
|
||||
</q-td>
|
||||
</q-tr>
|
||||
|
||||
<q-tr v-if="expanded[props.row.group_key]" class="detail-host-row">
|
||||
<q-td colspan="100%">
|
||||
<div class="detail-wrap">
|
||||
<q-table
|
||||
:rows="store.getDetailsByGroup(props.row.group_key)"
|
||||
:columns="detailColumns"
|
||||
row-key="cari_kodu"
|
||||
dense
|
||||
flat
|
||||
bordered
|
||||
hide-bottom
|
||||
:table-style="{ tableLayout: 'fixed', width: '100%' }"
|
||||
class="detail-table"
|
||||
<q-tr v-if="masterExpanded[props.row.group_key]" class="master-sub-row">
|
||||
<q-td colspan="100%" class="q-pa-none">
|
||||
<div class="currency-groups">
|
||||
<div class="currency-level-head">
|
||||
<div class="cgh-cell cgh-expand"></div>
|
||||
<div class="cgh-cell cgh-code">Döviz</div>
|
||||
<div class="cgh-cell cgh-num">Satır</div>
|
||||
<div class="cgh-cell cgh-num">Toplam Tutar</div>
|
||||
<div class="cgh-cell cgh-num">Toplam USD</div>
|
||||
<div class="cgh-cell cgh-num">Normal</div>
|
||||
<div class="cgh-cell cgh-num">Açık Kalem</div>
|
||||
<div class="cgh-cell cgh-center">Ort. Gün</div>
|
||||
<div class="cgh-cell cgh-center">Ort. Gün (DocDate)</div>
|
||||
</div>
|
||||
<div
|
||||
v-for="currRow in agingStore.getCurrenciesByMaster(props.row.group_key)"
|
||||
:key="currRow.group_key"
|
||||
class="currency-group"
|
||||
>
|
||||
<template #body-cell-prbr_1_2="scope">
|
||||
<q-td :props="scope" class="text-right prbr-cell">
|
||||
{{ formatRowPrBr(scope.row, '1_2') }}
|
||||
</q-td>
|
||||
</template>
|
||||
<template #body-cell-prbr_1_3="scope">
|
||||
<q-td :props="scope" class="text-right prbr-cell">
|
||||
{{ formatRowPrBr(scope.row, '1_3') }}
|
||||
</q-td>
|
||||
</template>
|
||||
<template #body-cell-vade_gun="scope">
|
||||
<q-td :props="scope" class="text-center">
|
||||
{{ formatDay(scope.row.vade_gun) }}
|
||||
</q-td>
|
||||
</template>
|
||||
<template #body-cell-vade_belge_tarihi_gun="scope">
|
||||
<q-td :props="scope" class="text-center">
|
||||
{{ formatDay(scope.row.vade_belge_tarihi_gun) }}
|
||||
</q-td>
|
||||
</template>
|
||||
</q-table>
|
||||
<div class="currency-group-header">
|
||||
<div class="cgh-cell cgh-expand">
|
||||
<q-btn
|
||||
dense
|
||||
flat
|
||||
round
|
||||
size="sm"
|
||||
:icon="currencyExpanded[currRow.group_key] ? 'expand_less' : 'expand_more'"
|
||||
@click="toggleCurrency(currRow.group_key)"
|
||||
/>
|
||||
</div>
|
||||
<div class="cgh-cell cgh-code">{{ currRow.doc_currency_code }}</div>
|
||||
<div class="cgh-cell cgh-num">{{ formatAmount(currRow.satir_sayisi, 0) }}</div>
|
||||
<div class="cgh-cell cgh-num">{{ formatAmount(currRow.toplam_tutar) }}</div>
|
||||
<div class="cgh-cell cgh-num">{{ formatAmount(currRow.toplam_usd) }}</div>
|
||||
<div class="cgh-cell cgh-num">{{ formatAmount(currRow.normal_tutar) }}</div>
|
||||
<div class="cgh-cell cgh-num">{{ formatAmount(currRow.acik_kalem_tutar) }}</div>
|
||||
<div class="cgh-cell cgh-center">{{ formatAmount(currRow.ortalama_gun, 0) }}</div>
|
||||
<div class="cgh-cell cgh-center">{{ formatAmount(currRow.ortalama_gun_docdate, 0) }}</div>
|
||||
</div>
|
||||
|
||||
<div v-if="currencyExpanded[currRow.group_key]" class="detail-host-row">
|
||||
<q-table
|
||||
:rows="agingStore.getDetailsByCurrency(currRow.group_key)"
|
||||
:columns="detailColumns"
|
||||
row-key="detail_key"
|
||||
flat
|
||||
dense
|
||||
bordered
|
||||
hide-bottom
|
||||
:rows-per-page-options="[0]"
|
||||
:pagination="{ rowsPerPage: 0 }"
|
||||
class="detail-subtable"
|
||||
:table-style="{ minWidth: '1500px' }"
|
||||
>
|
||||
<template #body-cell-eslesen_tutar="d">
|
||||
<q-td :props="d" class="text-right">{{ formatAmount(d.row.eslesen_tutar) }}</q-td>
|
||||
</template>
|
||||
<template #body-cell-usd_tutar="d">
|
||||
<q-td :props="d" class="text-right">{{ formatAmount(d.row.usd_tutar) }}</q-td>
|
||||
</template>
|
||||
<template #body-cell-gun_sayisi="d">
|
||||
<q-td :props="d" class="text-center">{{ formatAmount(d.row.gun_sayisi, 0) }}</q-td>
|
||||
</template>
|
||||
<template #body-cell-gun_sayisi_docdate="d">
|
||||
<q-td :props="d" class="text-center">{{ formatAmount(d.row.gun_sayisi_docdate, 0) }}</q-td>
|
||||
</template>
|
||||
</q-table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</q-td>
|
||||
</q-tr>
|
||||
</template>
|
||||
</q-table>
|
||||
</div>
|
||||
|
||||
</q-page>
|
||||
|
||||
<q-page v-else class="q-pa-md flex flex-center">
|
||||
<div class="text-negative text-subtitle1">
|
||||
Bu modüle erişim yetkiniz yok.
|
||||
</div>
|
||||
<div class="text-negative text-subtitle1">Bu modüle erişim yetkiniz yok.</div>
|
||||
</q-page>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, ref } from 'vue'
|
||||
import { onMounted, ref } from 'vue'
|
||||
import { useQuasar } from 'quasar'
|
||||
import { onMounted } from 'vue'
|
||||
import { useAccountAgingBalanceStore } from 'src/stores/accountAgingBalanceStore'
|
||||
import dayjs from 'dayjs'
|
||||
import { usePermission } from 'src/composables/usePermission'
|
||||
import { useAccountStore } from 'src/stores/accountStore'
|
||||
import { useStatementAgingStore } from 'src/stores/statementAgingStore'
|
||||
import { download, extractApiErrorDetail } from 'src/services/api'
|
||||
|
||||
const store = useAccountAgingBalanceStore()
|
||||
const expanded = ref({})
|
||||
const allDetailsOpen = ref(false)
|
||||
const filtersCollapsed = ref(false)
|
||||
const $q = useQuasar()
|
||||
|
||||
const { canRead, canExport } = usePermission()
|
||||
const canReadFinance = canRead('finance')
|
||||
const canExportFinance = canExport('finance')
|
||||
|
||||
const islemTipiOptions = [
|
||||
{ label: '1_2 Bakiye Pr.Br', value: 'prbr_1_2' },
|
||||
{ label: '1_3 Bakiye Pr.Br', value: 'prbr_1_3' },
|
||||
{ label: '1_2 USD Bakiye', value: 'usd_1_2' },
|
||||
{ label: '1_2 TRY Bakiye', value: 'try_1_2' },
|
||||
{ label: '1_3 USD Bakiye', value: 'usd_1_3' },
|
||||
{ label: '1_3 TRY Bakiye', value: 'try_1_3' }
|
||||
const $q = useQuasar()
|
||||
const accountStore = useAccountStore()
|
||||
const agingStore = useStatementAgingStore()
|
||||
|
||||
const selectedCari = ref(null)
|
||||
const filteredOptions = ref([])
|
||||
const dateTo = ref(dayjs().format('YYYY-MM-DD'))
|
||||
|
||||
const masterExpanded = ref({})
|
||||
const currencyExpanded = ref({})
|
||||
const allDetailsOpen = ref(false)
|
||||
const filtersCollapsed = ref(false)
|
||||
|
||||
const monetaryTypeOptions = [
|
||||
{ label: '1-2 hesap', value: ['1', '2'] },
|
||||
{ label: '1-3 r hesap', value: ['1', '3'] }
|
||||
]
|
||||
const selectedMonType = ref(monetaryTypeOptions[0].value)
|
||||
|
||||
const masterColumns = [
|
||||
{ name: 'expand', label: '', field: 'expand', align: 'center' },
|
||||
{ name: 'cari8', label: 'Ana Cari Kod', field: 'cari8', align: 'left', sortable: true },
|
||||
{ name: 'cari_detay', label: 'Ana Cari Detay', field: 'cari_detay', align: 'left', sortable: true },
|
||||
{ name: 'satir_sayisi', label: 'Satır', field: 'satir_sayisi', align: 'right', sortable: true },
|
||||
{ name: 'toplam_usd', label: 'Toplam USD', field: 'toplam_usd', align: 'right', sortable: true },
|
||||
{ name: 'normal_usd', label: 'Normal USD', field: 'normal_usd', align: 'right', sortable: true },
|
||||
{ name: 'acik_kalem_usd', label: 'Açık Kalem USD', field: 'acik_kalem_usd', align: 'right', sortable: true },
|
||||
{ name: 'ortalama_gun', label: 'Ort. Gün', field: 'ortalama_gun', align: 'center', sortable: true },
|
||||
{ name: 'ortalama_gun_docdate', label: 'Ort. Gün (DocDate)', field: 'ortalama_gun_docdate', align: 'center', sortable: true }
|
||||
]
|
||||
|
||||
const staticMoneyFields = ['usd_bakiye_1_2', 'tl_bakiye_1_2', 'usd_bakiye_1_3', 'tl_bakiye_1_3']
|
||||
const dayFields = ['vade_gun', 'vade_belge_tarihi_gun']
|
||||
const detailColumns = [
|
||||
{ name: 'fatura_cari', label: 'Fatura Cari', field: 'fatura_cari', align: 'left' },
|
||||
{ name: 'odeme_cari', label: 'Ödeme Cari', field: 'odeme_cari', align: 'left' },
|
||||
{ name: 'fatura_ref', label: 'Fatura Ref', field: 'fatura_ref', align: 'left' },
|
||||
{ name: 'odeme_ref', label: 'Ödeme Ref', field: 'odeme_ref', align: 'left' },
|
||||
{ name: 'fatura_tarihi', label: 'Fatura Tarihi', field: 'fatura_tarihi', align: 'left' },
|
||||
{ name: 'odeme_tarihi', label: 'Ödeme Vade', field: 'odeme_tarihi', align: 'left' },
|
||||
{ name: 'odeme_doc_date', label: 'Ödeme DocDate', field: 'odeme_doc_date', align: 'left' },
|
||||
{ name: 'eslesen_tutar', label: 'Eşleşen Tutar', field: 'eslesen_tutar', align: 'right' },
|
||||
{ name: 'usd_tutar', label: 'USD Tutar', field: 'usd_tutar', align: 'right' },
|
||||
{ name: 'gun_sayisi', label: 'Gün', field: 'gun_sayisi', align: 'center' },
|
||||
{ name: 'gun_sayisi_docdate', label: 'Gün (DocDate)', field: 'gun_sayisi_docdate', align: 'center' },
|
||||
{ name: 'aciklama', label: 'Açıklama', field: 'aciklama', align: 'left' },
|
||||
{ name: 'doc_currency_code', label: 'Döviz', field: 'doc_currency_code', align: 'left' }
|
||||
]
|
||||
|
||||
function toNumericSortValue (value) {
|
||||
if (typeof value === 'number') {
|
||||
return Number.isFinite(value) ? value : 0
|
||||
}
|
||||
const masterNumericCols = ['satir_sayisi', 'toplam_usd', 'normal_usd', 'acik_kalem_usd', 'ortalama_gun', 'ortalama_gun_docdate']
|
||||
const masterCenteredCols = ['ortalama_gun', 'ortalama_gun_docdate']
|
||||
|
||||
const s = String(value ?? '').trim()
|
||||
if (!s) return 0
|
||||
function normalizeText(str) {
|
||||
return (str || '')
|
||||
.toString()
|
||||
.toLocaleLowerCase('tr-TR')
|
||||
.normalize('NFD')
|
||||
.replace(/[\u0300-\u036f]/g, '')
|
||||
.trim()
|
||||
}
|
||||
|
||||
const hasComma = s.includes(',')
|
||||
const hasDot = s.includes('.')
|
||||
function filterCari(val, update) {
|
||||
const needle = normalizeText(val)
|
||||
|
||||
let normalized = s.replace(/\s+/g, '')
|
||||
|
||||
if (hasComma && hasDot) {
|
||||
const lastComma = normalized.lastIndexOf(',')
|
||||
const lastDot = normalized.lastIndexOf('.')
|
||||
if (lastComma > lastDot) {
|
||||
normalized = normalized.replace(/\./g, '').replace(',', '.')
|
||||
} else {
|
||||
normalized = normalized.replace(/,/g, '')
|
||||
update(() => {
|
||||
if (!needle) {
|
||||
filteredOptions.value = accountStore.accountOptions
|
||||
return
|
||||
}
|
||||
} else if (hasComma) {
|
||||
normalized = normalized.replace(/\./g, '').replace(',', '.')
|
||||
}
|
||||
|
||||
const n = Number.parseFloat(normalized)
|
||||
return Number.isFinite(n) ? n : 0
|
||||
}
|
||||
|
||||
function sortTextTr (a, b) {
|
||||
return String(a ?? '').localeCompare(String(b ?? ''), 'tr', { sensitivity: 'base' })
|
||||
}
|
||||
|
||||
const metricDefs = {
|
||||
prbr_1_2: { name: 'prbr_1_2', label: '1_2 Bakiye\nPr.Br', field: 'prbr_1_2', align: 'right', sortable: false },
|
||||
prbr_1_3: { name: 'prbr_1_3', label: '1_3 Bakiye\nPr.Br', field: 'prbr_1_3', align: 'right', sortable: false },
|
||||
usd_1_2: { name: 'usd_bakiye_1_2', label: '1_2 USD_BAKIYE', field: 'usd_bakiye_1_2', align: 'center', sortable: true, sort: (a, b) => toNumericSortValue(a) - toNumericSortValue(b) },
|
||||
try_1_2: { name: 'tl_bakiye_1_2', label: '1_2 TRY_BAKIYE', field: 'tl_bakiye_1_2', align: 'center', sortable: true, sort: (a, b) => toNumericSortValue(a) - toNumericSortValue(b) },
|
||||
usd_1_3: { name: 'usd_bakiye_1_3', label: '1_3 USD_BAKIYE', field: 'usd_bakiye_1_3', align: 'center', sortable: true, sort: (a, b) => toNumericSortValue(a) - toNumericSortValue(b) },
|
||||
try_1_3: { name: 'tl_bakiye_1_3', label: '1_3 TRY_BAKIYE', field: 'tl_bakiye_1_3', align: 'center', sortable: true, sort: (a, b) => toNumericSortValue(a) - toNumericSortValue(b) }
|
||||
}
|
||||
|
||||
const selectedMetricKeys = computed(() => {
|
||||
const selected = store.filters.islemTipi || []
|
||||
if (!selected.length) return [...Object.keys(metricDefs)]
|
||||
return selected.filter((k) => k in metricDefs)
|
||||
})
|
||||
|
||||
const summaryColumns = computed(() => ([
|
||||
{ name: 'expand', label: '', field: 'expand', align: 'center', sortable: false },
|
||||
{ name: 'ana_cari_kodu', label: 'Ana Cari Kodu', field: 'ana_cari_kodu', align: 'left', sortable: true, sort: sortTextTr },
|
||||
{ name: 'ana_cari_adi', label: 'Ana Cari Detay', field: 'ana_cari_adi', align: 'left', sortable: true, sort: sortTextTr },
|
||||
{ name: 'piyasa', label: 'Piyasa', field: 'piyasa', align: 'left', sortable: true, sort: sortTextTr },
|
||||
{ name: 'temsilci', label: 'Temsilci', field: 'temsilci', align: 'left', sortable: true, sort: sortTextTr },
|
||||
{ name: 'risk_durumu', label: 'Risk Durumu', field: 'risk_durumu', align: 'left', sortable: true, sort: sortTextTr },
|
||||
...selectedMetricKeys.value.map((k) => metricDefs[k]),
|
||||
{ name: 'vade_gun', label: 'Vade Gun', field: 'vade_gun', align: 'center', sortable: true, sort: (a, b) => toNumericSortValue(a) - toNumericSortValue(b) },
|
||||
{ name: 'vade_belge_tarihi_gun', label: 'Belge Tarihi Gun', field: 'vade_belge_tarihi_gun', align: 'center', sortable: true, sort: (a, b) => toNumericSortValue(a) - toNumericSortValue(b) }
|
||||
]))
|
||||
|
||||
const liveTotals = computed(() => {
|
||||
return store.filteredRows.reduce((acc, row) => {
|
||||
acc.usd_bakiye_1_2 += Number(row.usd_bakiye_1_2) || 0
|
||||
acc.tl_bakiye_1_2 += Number(row.tl_bakiye_1_2) || 0
|
||||
acc.usd_bakiye_1_3 += Number(row.usd_bakiye_1_3) || 0
|
||||
acc.tl_bakiye_1_3 += Number(row.tl_bakiye_1_3) || 0
|
||||
const vadeGun = Number(row.vade_gun) || 0
|
||||
const vadeBelge = Number(row.vade_belge_tarihi_gun) || 0
|
||||
if (vadeGun !== 0 || vadeBelge !== 0) {
|
||||
acc.vade_count += 1
|
||||
acc.vade_gun_sum += vadeGun
|
||||
acc.vade_belge_sum += vadeBelge
|
||||
}
|
||||
return acc
|
||||
}, {
|
||||
usd_bakiye_1_2: 0,
|
||||
tl_bakiye_1_2: 0,
|
||||
usd_bakiye_1_3: 0,
|
||||
tl_bakiye_1_3: 0,
|
||||
vade_gun_sum: 0,
|
||||
vade_belge_sum: 0,
|
||||
vade_count: 0
|
||||
filteredOptions.value = accountStore.accountOptions.filter(o => {
|
||||
const label = normalizeText(o.label)
|
||||
const value = normalizeText(o.value)
|
||||
return label.includes(needle) || value.includes(needle)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await accountStore.fetchAccounts()
|
||||
filteredOptions.value = accountStore.accountOptions
|
||||
})
|
||||
|
||||
const detailColumns = computed(() => [
|
||||
{ name: 'cari_kodu', label: 'Cari Kodu', field: 'cari_kodu', align: 'left' },
|
||||
{ name: 'cari_detay', label: 'Cari Detay', field: 'cari_detay', align: 'left' },
|
||||
{ name: 'sirket', label: 'Şirket', field: 'sirket', align: 'left' },
|
||||
{ name: 'sirket_detay', label: 'Şirket Detayı', field: 'sirket_detay', align: 'left' },
|
||||
{ name: 'muhasebe_kodu', label: 'Muhasebe Kodu', field: 'muhasebe_kodu', align: 'left' },
|
||||
{ name: 'piyasa', label: 'Piyasa', field: 'piyasa', align: 'left' },
|
||||
{ name: 'temsilci', label: 'Temsilci', field: 'temsilci', align: 'left' },
|
||||
{ name: 'risk_durumu', label: 'Risk Durumu', field: 'risk_durumu', align: 'left' },
|
||||
{ name: 'ozellik05', label: 'Ülke', field: 'ozellik05', align: 'left' },
|
||||
{ name: 'il', label: 'İl', field: 'il', align: 'left' },
|
||||
{ name: 'ilce', label: 'İlçe', field: 'ilce', align: 'left' },
|
||||
{ name: 'cari_doviz', label: 'Döviz', field: 'cari_doviz', align: 'left' },
|
||||
...selectedMetricKeys.value.map((k) => metricDefs[k]),
|
||||
{ name: 'vade_gun', label: 'Vade Gun', field: 'vade_gun', align: 'center' },
|
||||
{ name: 'vade_belge_tarihi_gun', label: 'Belge Tarihi Gun', field: 'vade_belge_tarihi_gun', align: 'center' }
|
||||
])
|
||||
|
||||
|
||||
function onReset () {
|
||||
store.resetFilters()
|
||||
store.fetchBalances()
|
||||
}
|
||||
|
||||
function onToggle12Changed (val) {
|
||||
if (val) {
|
||||
store.filters.excludeZeroBalance13 = false
|
||||
}
|
||||
}
|
||||
|
||||
function onToggle13Changed (val) {
|
||||
if (val) {
|
||||
store.filters.excludeZeroBalance12 = false
|
||||
}
|
||||
}
|
||||
|
||||
function toggleFiltersCollapsed () {
|
||||
filtersCollapsed.value = !filtersCollapsed.value
|
||||
}
|
||||
|
||||
|
||||
function toggleGroup (key) {
|
||||
expanded.value[key] = !expanded.value[key]
|
||||
if (!expanded.value[key]) {
|
||||
allDetailsOpen.value = false
|
||||
return
|
||||
}
|
||||
|
||||
allDetailsOpen.value =
|
||||
store.summaryRows.length > 0 &&
|
||||
store.summaryRows.every(r => expanded.value[r.group_key])
|
||||
}
|
||||
|
||||
function toggleAllDetails () {
|
||||
allDetailsOpen.value = !allDetailsOpen.value
|
||||
|
||||
if (allDetailsOpen.value) {
|
||||
const next = {}
|
||||
for (const row of store.summaryRows) {
|
||||
next[row.group_key] = true
|
||||
}
|
||||
expanded.value = next
|
||||
return
|
||||
}
|
||||
|
||||
expanded.value = {}
|
||||
}
|
||||
|
||||
async function downloadAgingBalancePDF (detailed) {
|
||||
if (!canExportFinance.value) {
|
||||
$q.notify({ type: 'negative', message: 'PDF export yetkiniz yok', position: 'top-right' })
|
||||
return
|
||||
}
|
||||
|
||||
if (!store.hasFetched) {
|
||||
$q.notify({ type: 'warning', message: 'Önce Bakiyeleri Getir ile veri yükleyin.', position: 'top-right' })
|
||||
async function onFilterClick() {
|
||||
if (!selectedCari.value || !dateTo.value) {
|
||||
$q.notify({
|
||||
type: 'warning',
|
||||
message: 'Lütfen cari ve son tarih seçiniz.',
|
||||
position: 'top-right'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const params = {
|
||||
cari_search: String(store.filters.cariSearch || '').trim(),
|
||||
cari_ilk_grup: (store.filters.cariIlkGrup || []).join(','),
|
||||
piyasa: (store.filters.piyasa || []).join(','),
|
||||
temsilci: (store.filters.temsilci || []).join(','),
|
||||
risk_durumu: (store.filters.riskDurumu || []).join(','),
|
||||
islem_tipi: (store.filters.islemTipi || []).join(','),
|
||||
ulke: (store.filters.ulke || []).join(','),
|
||||
il: (store.filters.il || []).join(','),
|
||||
ilce: (store.filters.ilce || []).join(','),
|
||||
exclude_zero_12: store.filters.excludeZeroBalance12 ? '1' : '0',
|
||||
exclude_zero_13: store.filters.excludeZeroBalance13 ? '1' : '0',
|
||||
detailed: detailed ? '1' : '0'
|
||||
}
|
||||
await agingStore.load({
|
||||
accountcode: selectedCari.value,
|
||||
enddate: dateTo.value,
|
||||
parislemler: selectedMonType.value
|
||||
})
|
||||
|
||||
const blob = await download('/finance/account-aging-statement/export-pdf', params)
|
||||
const m = {}
|
||||
const c = {}
|
||||
for (const row of agingStore.masterRows) {
|
||||
m[row.group_key] = true
|
||||
for (const cr of agingStore.getCurrenciesByMaster(row.group_key)) {
|
||||
c[cr.group_key] = true
|
||||
}
|
||||
}
|
||||
masterExpanded.value = m
|
||||
currencyExpanded.value = c
|
||||
allDetailsOpen.value = agingStore.masterRows.length > 0
|
||||
} catch (err) {
|
||||
const msg = await extractApiErrorDetail(err)
|
||||
$q.notify({
|
||||
type: 'negative',
|
||||
message: msg || 'Veriler yüklenemedi',
|
||||
position: 'top-right'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function resetFilters() {
|
||||
selectedCari.value = null
|
||||
dateTo.value = dayjs().format('YYYY-MM-DD')
|
||||
selectedMonType.value = monetaryTypeOptions[0].value
|
||||
masterExpanded.value = {}
|
||||
currencyExpanded.value = {}
|
||||
allDetailsOpen.value = false
|
||||
agingStore.reset()
|
||||
}
|
||||
|
||||
function toggleMaster(key) {
|
||||
masterExpanded.value[key] = !masterExpanded.value[key]
|
||||
}
|
||||
|
||||
function toggleCurrency(key) {
|
||||
currencyExpanded.value[key] = !currencyExpanded.value[key]
|
||||
}
|
||||
|
||||
function toggleAllDetails() {
|
||||
allDetailsOpen.value = !allDetailsOpen.value
|
||||
|
||||
if (!allDetailsOpen.value) {
|
||||
masterExpanded.value = {}
|
||||
currencyExpanded.value = {}
|
||||
return
|
||||
}
|
||||
|
||||
const m = {}
|
||||
const c = {}
|
||||
for (const row of agingStore.masterRows) {
|
||||
m[row.group_key] = true
|
||||
for (const cr of agingStore.getCurrenciesByMaster(row.group_key)) {
|
||||
c[cr.group_key] = true
|
||||
}
|
||||
}
|
||||
masterExpanded.value = m
|
||||
currencyExpanded.value = c
|
||||
}
|
||||
|
||||
function toggleFiltersCollapsed() {
|
||||
filtersCollapsed.value = !filtersCollapsed.value
|
||||
}
|
||||
|
||||
function buildExportParams(detailed = false) {
|
||||
return {
|
||||
accountcode: String(selectedCari.value || '').trim(),
|
||||
cari_search: String(selectedCari.value || '').trim(),
|
||||
enddate: dateTo.value,
|
||||
selected_date: dateTo.value,
|
||||
parislemler: selectedMonType.value,
|
||||
detailed: detailed ? '1' : '0',
|
||||
exclude_zero_12: '0',
|
||||
exclude_zero_13: '0'
|
||||
}
|
||||
}
|
||||
|
||||
async function downloadAgingBalancePDF(detailed) {
|
||||
if (!canExportFinance.value) {
|
||||
$q.notify({ type: 'negative', message: 'PDF export yetkiniz yok', position: 'top-right' })
|
||||
return
|
||||
}
|
||||
if (!selectedCari.value || !dateTo.value) {
|
||||
$q.notify({ type: 'warning', message: 'Önce cari ve son tarih seçiniz.', position: 'top-right' })
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const blob = await download('/finance/account-aging-statement/export-pdf', buildExportParams(detailed))
|
||||
const pdfUrl = window.URL.createObjectURL(new Blob([blob], { type: 'application/pdf' }))
|
||||
window.open(pdfUrl, '_blank')
|
||||
} catch (err) {
|
||||
@@ -749,37 +438,19 @@ async function downloadAgingBalancePDF (detailed) {
|
||||
}
|
||||
}
|
||||
|
||||
async function downloadAgingBalanceExcel () {
|
||||
async function downloadAgingBalanceExcel() {
|
||||
if (!canExportFinance.value) {
|
||||
$q.notify({ type: 'negative', message: 'Excel export yetkiniz yok', position: 'top-right' })
|
||||
return
|
||||
}
|
||||
|
||||
if (!store.hasFetched) {
|
||||
$q.notify({ type: 'warning', message: 'Önce Bakiyeleri Getir ile veri yükleyin.', position: 'top-right' })
|
||||
if (!selectedCari.value || !dateTo.value) {
|
||||
$q.notify({ type: 'warning', message: 'Önce cari ve son tarih seçiniz.', position: 'top-right' })
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const params = {
|
||||
cari_search: String(store.filters.cariSearch || '').trim(),
|
||||
cari_ilk_grup: (store.filters.cariIlkGrup || []).join(','),
|
||||
piyasa: (store.filters.piyasa || []).join(','),
|
||||
temsilci: (store.filters.temsilci || []).join(','),
|
||||
risk_durumu: (store.filters.riskDurumu || []).join(','),
|
||||
islem_tipi: (store.filters.islemTipi || []).join(','),
|
||||
ulke: (store.filters.ulke || []).join(','),
|
||||
il: (store.filters.il || []).join(','),
|
||||
ilce: (store.filters.ilce || []).join(','),
|
||||
exclude_zero_12: store.filters.excludeZeroBalance12 ? '1' : '0',
|
||||
exclude_zero_13: store.filters.excludeZeroBalance13 ? '1' : '0'
|
||||
}
|
||||
|
||||
const file = await download('/finance/account-aging-statement/export-excel', params)
|
||||
const blob = new Blob(
|
||||
[file],
|
||||
{ type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }
|
||||
)
|
||||
const file = await download('/finance/account-aging-statement/export-excel', buildExportParams(false))
|
||||
const blob = new Blob([file], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' })
|
||||
const url = window.URL.createObjectURL(blob)
|
||||
const a = document.createElement('a')
|
||||
a.href = url
|
||||
@@ -798,261 +469,224 @@ async function downloadAgingBalanceExcel () {
|
||||
}
|
||||
}
|
||||
|
||||
function formatAmount (value) {
|
||||
function formatAmount(value, fraction = 2) {
|
||||
const n = Number(value || 0)
|
||||
return new Intl.NumberFormat('tr-TR', {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2
|
||||
minimumFractionDigits: fraction,
|
||||
maximumFractionDigits: fraction
|
||||
}).format(n)
|
||||
}
|
||||
|
||||
function formatDay (value) {
|
||||
const n = Number(value || 0)
|
||||
return new Intl.NumberFormat('tr-TR', {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2
|
||||
}).format(n)
|
||||
}
|
||||
|
||||
function selectionLabel (arr, label) {
|
||||
const count = Array.isArray(arr) ? arr.length : 0
|
||||
if (count === 0) return `Tümü (${label})`
|
||||
if (count === 1) return '1 seçim'
|
||||
return `${count} seçim`
|
||||
}
|
||||
|
||||
function totalCellValue (colName) {
|
||||
if (colName === 'expand') return 'Toplam'
|
||||
if (colName === 'piyasa') return '-'
|
||||
if (colName === 'temsilci') return '-'
|
||||
if (colName === 'risk_durumu') return '-'
|
||||
if (colName === 'prbr_1_2') return formatCurrencyMap(totalByCurrency('1_2'))
|
||||
if (colName === 'prbr_1_3') return formatCurrencyMap(totalByCurrency('1_3'))
|
||||
if (colName === 'usd_bakiye_1_2') return formatAmount(liveTotals.value.usd_bakiye_1_2)
|
||||
if (colName === 'tl_bakiye_1_2') return formatAmount(liveTotals.value.tl_bakiye_1_2)
|
||||
if (colName === 'usd_bakiye_1_3') return formatAmount(liveTotals.value.usd_bakiye_1_3)
|
||||
if (colName === 'tl_bakiye_1_3') return formatAmount(liveTotals.value.tl_bakiye_1_3)
|
||||
if (colName === 'vade_gun') return formatDay(liveTotals.value.vade_count > 0 ? liveTotals.value.vade_gun_sum / liveTotals.value.vade_count : 0)
|
||||
if (colName === 'vade_belge_tarihi_gun') return formatDay(liveTotals.value.vade_count > 0 ? liveTotals.value.vade_belge_sum / liveTotals.value.vade_count : 0)
|
||||
return '-'
|
||||
}
|
||||
|
||||
function totalByCurrency (tip) {
|
||||
const key = tip === '1_2' ? 'bakiye_1_2_map' : 'bakiye_1_3_map'
|
||||
const out = {}
|
||||
|
||||
for (const r of store.summaryRows) {
|
||||
const m = r[key] || {}
|
||||
for (const [curr, val] of Object.entries(m)) {
|
||||
out[curr] = (Number(out[curr]) || 0) + (Number(val) || 0)
|
||||
}
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
function formatCurrencyMap (mapObj) {
|
||||
const entries = Object.entries(mapObj || {})
|
||||
.filter(([, amount]) => Number(amount) !== 0)
|
||||
.sort((a, b) => a[0].localeCompare(b[0], 'en'))
|
||||
|
||||
if (!entries.length) return '-'
|
||||
return entries
|
||||
.map(([curr, amount]) => `${curr}: ${formatAmount(amount)}`)
|
||||
.join('\n')
|
||||
}
|
||||
|
||||
function formatRowPrBr (row, tip) {
|
||||
const curr = String(row.cari_doviz || '').trim().toUpperCase() || 'N/A'
|
||||
|
||||
const amount = tip === '1_2'
|
||||
? (Number(row.bakiye_1_2) || 0)
|
||||
: (Number(row.bakiye_1_3) || 0)
|
||||
|
||||
if (amount === 0) return '-'
|
||||
return `${curr} ${formatAmount(amount)}`
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
store.filters.selectedDate = new Date().toISOString().slice(0, 10)
|
||||
await store.fetchBalances()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.page-layout {
|
||||
height: calc(100vh - 110px);
|
||||
.statement-page {
|
||||
height: calc(100vh - 56px);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.filter-sticky {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 20;
|
||||
background: #fff;
|
||||
padding-bottom: 6px;
|
||||
.table-scroll {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.filter-sticky.collapsed {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.top-actions.single-line {
|
||||
flex-wrap: nowrap;
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
scrollbar-width: thin;
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
|
||||
.top-actions.single-line > [class*='col-'],
|
||||
.top-actions.single-line > .col-auto {
|
||||
flex: 0 0 auto;
|
||||
min-width: 220px;
|
||||
}
|
||||
|
||||
.top-actions.single-line > .col-auto {
|
||||
min-width: auto;
|
||||
}
|
||||
|
||||
.filters-panel {
|
||||
.compact-filter {
|
||||
border: 1px solid rgba(0, 0, 0, 0.12);
|
||||
border-radius: 8px;
|
||||
background: #fafafa;
|
||||
}
|
||||
|
||||
.compact-select :deep(.q-field__control) {
|
||||
min-height: 40px;
|
||||
}
|
||||
|
||||
.compact-select :deep(.q-field__native),
|
||||
.compact-select :deep(.q-field__input) {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.table-area {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.statement-page .local-filter-bar {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 40;
|
||||
}
|
||||
|
||||
.sticky-bar {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 9;
|
||||
z-index: 30;
|
||||
flex: 0 0 auto;
|
||||
background: var(--q-secondary);
|
||||
color: #fff;
|
||||
border: 1px solid rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
.balance-table {
|
||||
.statement-table {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.balance-table :deep(.q-table__container) {
|
||||
.statement-table :deep(.q-table__container) {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.balance-table :deep(.q-table__top) {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 6;
|
||||
background: #fff;
|
||||
.statement-table :deep(.q-table__top) {
|
||||
flex: 0 0 auto;
|
||||
position: static;
|
||||
}
|
||||
|
||||
.balance-table :deep(.q-table__middle) {
|
||||
.statement-table :deep(.q-table__middle) {
|
||||
flex: 1 1 auto;
|
||||
min-height: 0;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
overflow: auto !important;
|
||||
max-height: none !important;
|
||||
}
|
||||
|
||||
.balance-table :deep(.header-row th) {
|
||||
.statement-table :deep(.header-row th) {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 8;
|
||||
z-index: 30;
|
||||
background: var(--q-primary);
|
||||
color: #fff;
|
||||
font-weight: 600;
|
||||
font-family: 'Roboto', sans-serif;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.22);
|
||||
box-shadow: inset 0 -1px 0 rgba(255, 255, 255, 0.2), 0 1px 0 rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.balance-table :deep(.totals-row th) {
|
||||
position: sticky;
|
||||
top: 38px;
|
||||
z-index: 7;
|
||||
background: var(--q-secondary);
|
||||
color: var(--q-dark);
|
||||
font-weight: 700;
|
||||
font-family: 'Roboto', sans-serif;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
.detail-table :deep(.q-table__middle) {
|
||||
max-height: 320px;
|
||||
}
|
||||
|
||||
.balance-table :deep(.sub-header-row td) {
|
||||
background: #fff;
|
||||
.statement-table :deep(.master-row td) {
|
||||
background: color-mix(in srgb, var(--q-secondary) 12%, white);
|
||||
border-bottom: 2px solid rgba(0, 0, 0, 0.18);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.balance-table :deep(.sub-header-row td:first-child) {
|
||||
.statement-table :deep(.master-row td:first-child) {
|
||||
border-left: 3px solid var(--q-primary);
|
||||
}
|
||||
|
||||
.balance-table :deep(.detail-host-row td) {
|
||||
background: #f7f7f7;
|
||||
border-bottom: 10px solid #fff;
|
||||
padding-top: 10px;
|
||||
padding-bottom: 12px;
|
||||
.statement-table :deep(.master-sub-row td) {
|
||||
background: #f4f6fb;
|
||||
border-bottom: 8px solid #fff;
|
||||
vertical-align: top;
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
.detail-wrap {
|
||||
border: 1px solid rgba(0, 0, 0, 0.14);
|
||||
border-left: 4px solid var(--q-secondary);
|
||||
border-radius: 6px;
|
||||
background: #fff;
|
||||
.currency-groups {
|
||||
padding: 6px;
|
||||
background: #f8faff;
|
||||
}
|
||||
|
||||
.balance-table :deep(.header-row th) {
|
||||
white-space: pre-line;
|
||||
line-height: 1.15;
|
||||
.currency-group {
|
||||
border-left: 4px solid var(--q-secondary);
|
||||
border-top: 1px solid rgba(0, 0, 0, 0.08);
|
||||
border-right: 1px solid rgba(0, 0, 0, 0.08);
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.08);
|
||||
margin-bottom: 8px;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.prbr-cell {
|
||||
white-space: pre-line;
|
||||
word-break: break-word;
|
||||
line-height: 1.25;
|
||||
.currency-level-head {
|
||||
position: sticky;
|
||||
top: 36px;
|
||||
z-index: 26;
|
||||
display: grid;
|
||||
grid-template-columns: 48px 100px 80px 1.2fr 1.2fr 1.1fr 1.1fr 110px 140px;
|
||||
align-items: center;
|
||||
gap: 0;
|
||||
background: var(--q-secondary);
|
||||
color: #fff;
|
||||
border: 1px solid rgba(0, 0, 0, 0.15);
|
||||
margin-bottom: 6px;
|
||||
box-shadow: 0 1px 0 rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.balance-table :deep(th),
|
||||
.balance-table :deep(td),
|
||||
.detail-table :deep(th),
|
||||
.detail-table :deep(td) {
|
||||
white-space: normal !important;
|
||||
word-break: break-word;
|
||||
overflow-wrap: anywhere;
|
||||
.currency-group-header {
|
||||
position: sticky;
|
||||
top: 72px;
|
||||
z-index: 24;
|
||||
display: grid;
|
||||
grid-template-columns: 48px 100px 80px 1.2fr 1.2fr 1.1fr 1.1fr 110px 140px;
|
||||
align-items: center;
|
||||
gap: 0;
|
||||
background: #4c5f7a;
|
||||
color: #fff;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.18);
|
||||
box-shadow: 0 1px 0 rgba(0, 0, 0, 0.18);
|
||||
}
|
||||
|
||||
.cgh-cell {
|
||||
padding: 6px 8px;
|
||||
border-right: 1px solid rgba(255, 255, 255, 0.2);
|
||||
font-weight: 600;
|
||||
font-size: 11px;
|
||||
line-height: 1.2;
|
||||
padding: 4px 6px !important;
|
||||
}
|
||||
|
||||
.balance-table :deep(.q-table__table),
|
||||
.detail-table :deep(.q-table__table) {
|
||||
width: 100% !important;
|
||||
.cgh-cell:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.detail-table :deep(.q-table__middle) {
|
||||
overflow-x: hidden;
|
||||
.cgh-num {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.cgh-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.cgh-expand {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.detail-host-row :deep(td) {
|
||||
background: #fdfdfd;
|
||||
padding: 6px !important;
|
||||
}
|
||||
|
||||
.detail-subtable {
|
||||
border-left: 4px solid var(--q-primary);
|
||||
border-top: 1px solid rgba(0, 0, 0, 0.08);
|
||||
border-right: 1px solid rgba(0, 0, 0, 0.08);
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.08);
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.statement-table :deep(th),
|
||||
.statement-table :deep(td),
|
||||
.detail-subtable :deep(th),
|
||||
.detail-subtable :deep(td) {
|
||||
padding: 3px 6px !important;
|
||||
font-size: 11px !important;
|
||||
line-height: 1.2 !important;
|
||||
}
|
||||
|
||||
.detail-subtable :deep(.q-table__top) {
|
||||
position: static;
|
||||
}
|
||||
|
||||
.detail-subtable :deep(thead th) {
|
||||
position: sticky;
|
||||
top: 108px;
|
||||
z-index: 22;
|
||||
background: #1f3b5b;
|
||||
color: #fff;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.2);
|
||||
box-shadow: 0 1px 0 rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.detail-subtable :deep(td[data-col="gun_sayisi"]),
|
||||
.detail-subtable :deep(td[data-col="gun_sayisi_docdate"]),
|
||||
.statement-table :deep(td[data-col="ortalama_gun"]),
|
||||
.statement-table :deep(td[data-col="ortalama_gun_docdate"]) {
|
||||
text-align: center !important;
|
||||
}
|
||||
|
||||
@media (max-width: 1366px) {
|
||||
.statement-table :deep(th),
|
||||
.statement-table :deep(td),
|
||||
.detail-subtable :deep(th),
|
||||
.detail-subtable :deep(td) {
|
||||
font-size: 10px !important;
|
||||
padding: 2px 4px !important;
|
||||
}
|
||||
|
||||
.currency-group-header {
|
||||
grid-template-columns: 44px 90px 70px 1.1fr 1.1fr 1fr 1fr 90px 120px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
1148
ui/src/pages/AgingCustomerBalancelist.go.vue
Normal file
1148
ui/src/pages/AgingCustomerBalancelist.go.vue
Normal file
@@ -0,0 +1,1148 @@
|
||||
Logo
|
||||
Konular
|
||||
Değişiklik İstekleri
|
||||
Dönüm noktaları
|
||||
Keşfet
|
||||
M_Kececi
|
||||
/
|
||||
bssapp
|
||||
Kod
|
||||
Konular
|
||||
Değişiklik İstekleri
|
||||
İşlemler
|
||||
Paketler
|
||||
Projeler
|
||||
Sürüm
|
||||
Wiki
|
||||
Aktivite
|
||||
Ayarlar
|
||||
Dosyalar
|
||||
AccountAgingStatement.vue
|
||||
ActivityLogs.vue
|
||||
ChangePassword.vue
|
||||
CustomerBalanceList.vue
|
||||
Dashboard.vue
|
||||
ErrorNotFound.vue
|
||||
FirstPasswordChange.vue
|
||||
MainPage.vue
|
||||
MainPanel.vue
|
||||
MePassword.vue
|
||||
OrderBulkClose.vue
|
||||
OrderEntry.vue
|
||||
OrderGateway.vue
|
||||
OrderList.vue
|
||||
OrderPdf.vue
|
||||
OrderProductionUpdate.vue
|
||||
OrderProductionUpdateList.vue
|
||||
PermissionMatrix.vue
|
||||
ProductStockByAttributes.vue
|
||||
ProductStockQuery.vue
|
||||
ProductionWorker.vue
|
||||
ProductionWorkerGateway.vue
|
||||
ResetPassword.vue
|
||||
RoleDepartmentPermissionGateway.vue
|
||||
RoleDepartmentPermissionList.vue
|
||||
RoleDepartmentPermissionPage.vue
|
||||
StatementHeaderReport.vue
|
||||
StatementReport.vue
|
||||
TestMail.vue
|
||||
UserDetail.vue
|
||||
UserGateway.vue
|
||||
UserList.vue
|
||||
UserPermissionPage.vue
|
||||
UserSync.vue
|
||||
dummydata.vue
|
||||
statementofaccount.vue
|
||||
App.vue
|
||||
-.editorconfig
|
||||
.editorconfig
|
||||
.env.development
|
||||
.env.production
|
||||
.gitignore
|
||||
.npmrc
|
||||
README.md
|
||||
babel.config.js
|
||||
index.html
|
||||
jsconfig.json
|
||||
package-lock.json
|
||||
package.json
|
||||
postcss.config.js
|
||||
quasar.config.js
|
||||
quasar.config.js.temporary.compiled.1772696562168.mjs
|
||||
.gitignore
|
||||
readme.md
|
||||
bssapp
|
||||
/
|
||||
ui
|
||||
/
|
||||
src
|
||||
/
|
||||
pages
|
||||
/
|
||||
AccountAgingStatement.vue
|
||||
|
||||
M_Kececi
|
||||
ce31aff645
|
||||
Merge remote-tracking branch 'origin/master'
|
||||
3 gün önce
|
||||
1059 satır
|
||||
35 KiB
|
||||
Vue
|
||||
<template>
|
||||
<q-page v-if="canReadFinance" class="q-pa-md page-layout">
|
||||
<div class="filter-sticky" :class="{ collapsed: filtersCollapsed }">
|
||||
<div class="top-actions row q-col-gutter-sm items-end q-mb-sm" :class="{ 'single-line': filtersCollapsed }">
|
||||
<div class="col-12 col-sm-6 col-md-2">
|
||||
<q-input
|
||||
v-model="store.filters.selectedDate"
|
||||
label="Tarih (Bugün)"
|
||||
filled
|
||||
dense
|
||||
readonly
|
||||
disable
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-sm-6 col-md-3">
|
||||
<q-toggle
|
||||
v-model="store.filters.excludeZeroBalance12"
|
||||
dense
|
||||
label="1_2 Bakiyesi Sıfır Olanları Alma"
|
||||
@update:model-value="onToggle12Changed"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-sm-6 col-md-3">
|
||||
<q-toggle
|
||||
v-model="store.filters.excludeZeroBalance13"
|
||||
dense
|
||||
label="1_3 Bakiyesi Sıfır Olanları Alma"
|
||||
@update:model-value="onToggle13Changed"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="col-auto">
|
||||
<q-btn
|
||||
color="primary"
|
||||
icon="download"
|
||||
label="Verileri Yenile"
|
||||
:loading="store.loading"
|
||||
@click="store.fetchBalances()"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="col-auto">
|
||||
<q-btn
|
||||
flat
|
||||
color="grey-8"
|
||||
icon="restart_alt"
|
||||
label="Sıfırla"
|
||||
@click="onReset"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<q-slide-transition>
|
||||
<div v-show="!filtersCollapsed" class="filters-panel q-pa-sm q-mb-md">
|
||||
<div class="row q-col-gutter-sm">
|
||||
<div class="col-12 col-sm-6 col-md-4">
|
||||
<q-input
|
||||
v-model="store.filters.cariSearch"
|
||||
filled
|
||||
dense
|
||||
label="Cari Kodu / Cari Adı"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-sm-6 col-md-2">
|
||||
<q-select
|
||||
v-model="store.filters.cariIlkGrup"
|
||||
:options="store.cariIlkGrupOptions"
|
||||
multiple
|
||||
emit-value
|
||||
map-options
|
||||
filled
|
||||
dense
|
||||
options-dense
|
||||
class="compact-select"
|
||||
label="Cari İlk Grup"
|
||||
:display-value="selectionLabel(store.filters.cariIlkGrup, 'Cari İlk Grup')"
|
||||
>
|
||||
<template #before-options>
|
||||
<q-item clickable dense @click.stop="store.selectAll('cariIlkGrup', store.cariIlkGrupOptions)">
|
||||
<q-item-section>Tümünü Seç</q-item-section>
|
||||
</q-item>
|
||||
<q-item clickable dense @click.stop="store.clearAll('cariIlkGrup')">
|
||||
<q-item-section>Tümünü Temizle</q-item-section>
|
||||
</q-item>
|
||||
<q-separator />
|
||||
</template>
|
||||
<template #option="scope">
|
||||
<q-item v-bind="scope.itemProps">
|
||||
<q-item-section avatar>
|
||||
<q-checkbox :model-value="scope.selected" />
|
||||
</q-item-section>
|
||||
<q-item-section>
|
||||
<q-item-label>{{ scope.opt.label }}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</template>
|
||||
</q-select>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-sm-6 col-md-2">
|
||||
<q-select
|
||||
v-model="store.filters.piyasa"
|
||||
:options="store.piyasaOptions"
|
||||
multiple
|
||||
emit-value
|
||||
map-options
|
||||
filled
|
||||
dense
|
||||
options-dense
|
||||
class="compact-select"
|
||||
label="Piyasa"
|
||||
:display-value="selectionLabel(store.filters.piyasa, 'Piyasa')"
|
||||
>
|
||||
<template #before-options>
|
||||
<q-item clickable dense @click.stop="store.selectAll('piyasa', store.piyasaOptions)">
|
||||
<q-item-section>Tümünü Seç</q-item-section>
|
||||
</q-item>
|
||||
<q-item clickable dense @click.stop="store.clearAll('piyasa')">
|
||||
<q-item-section>Tümünü Temizle</q-item-section>
|
||||
</q-item>
|
||||
<q-separator />
|
||||
</template>
|
||||
<template #option="scope">
|
||||
<q-item v-bind="scope.itemProps">
|
||||
<q-item-section avatar>
|
||||
<q-checkbox :model-value="scope.selected" />
|
||||
</q-item-section>
|
||||
<q-item-section>
|
||||
<q-item-label>{{ scope.opt.label }}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</template>
|
||||
</q-select>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-sm-6 col-md-2">
|
||||
<q-select
|
||||
v-model="store.filters.temsilci"
|
||||
:options="store.temsilciOptions"
|
||||
multiple
|
||||
emit-value
|
||||
map-options
|
||||
filled
|
||||
dense
|
||||
options-dense
|
||||
class="compact-select"
|
||||
label="Temsilci"
|
||||
:display-value="selectionLabel(store.filters.temsilci, 'Temsilci')"
|
||||
>
|
||||
<template #before-options>
|
||||
<q-item clickable dense @click.stop="store.selectAll('temsilci', store.temsilciOptions)">
|
||||
<q-item-section>Tümünü Seç</q-item-section>
|
||||
</q-item>
|
||||
<q-item clickable dense @click.stop="store.clearAll('temsilci')">
|
||||
<q-item-section>Tümünü Temizle</q-item-section>
|
||||
</q-item>
|
||||
<q-separator />
|
||||
</template>
|
||||
<template #option="scope">
|
||||
<q-item v-bind="scope.itemProps">
|
||||
<q-item-section avatar>
|
||||
<q-checkbox :model-value="scope.selected" />
|
||||
</q-item-section>
|
||||
<q-item-section>
|
||||
<q-item-label>{{ scope.opt.label }}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</template>
|
||||
</q-select>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-sm-6 col-md-2">
|
||||
<q-select
|
||||
v-model="store.filters.riskDurumu"
|
||||
:options="store.riskDurumuOptions"
|
||||
multiple
|
||||
emit-value
|
||||
map-options
|
||||
filled
|
||||
dense
|
||||
options-dense
|
||||
class="compact-select"
|
||||
label="Risk Durumu"
|
||||
:display-value="selectionLabel(store.filters.riskDurumu, 'Risk Durumu')"
|
||||
>
|
||||
<template #before-options>
|
||||
<q-item clickable dense @click.stop="store.selectAll('riskDurumu', store.riskDurumuOptions)">
|
||||
<q-item-section>Tümünü Seç</q-item-section>
|
||||
</q-item>
|
||||
<q-item clickable dense @click.stop="store.clearAll('riskDurumu')">
|
||||
<q-item-section>Tümünü Temizle</q-item-section>
|
||||
</q-item>
|
||||
<q-separator />
|
||||
</template>
|
||||
<template #option="scope">
|
||||
<q-item v-bind="scope.itemProps">
|
||||
<q-item-section avatar>
|
||||
<q-checkbox :model-value="scope.selected" />
|
||||
</q-item-section>
|
||||
<q-item-section>
|
||||
<q-item-label>{{ scope.opt.label }}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</template>
|
||||
</q-select>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-sm-6 col-md-2">
|
||||
<q-select
|
||||
v-model="store.filters.islemTipi"
|
||||
:options="islemTipiOptions"
|
||||
multiple
|
||||
emit-value
|
||||
map-options
|
||||
filled
|
||||
dense
|
||||
options-dense
|
||||
class="compact-select"
|
||||
label="İşlem Tipi"
|
||||
:display-value="selectionLabel(store.filters.islemTipi, 'İşlem Tipi')"
|
||||
>
|
||||
<template #before-options>
|
||||
<q-item clickable dense @click.stop="store.selectAll('islemTipi', islemTipiOptions)">
|
||||
<q-item-section>Tümünü Seç</q-item-section>
|
||||
</q-item>
|
||||
<q-item clickable dense @click.stop="store.clearAll('islemTipi')">
|
||||
<q-item-section>Tümünü Temizle</q-item-section>
|
||||
</q-item>
|
||||
<q-separator />
|
||||
</template>
|
||||
<template #option="scope">
|
||||
<q-item v-bind="scope.itemProps">
|
||||
<q-item-section avatar>
|
||||
<q-checkbox :model-value="scope.selected" />
|
||||
</q-item-section>
|
||||
<q-item-section>
|
||||
<q-item-label>{{ scope.opt.label }}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</template>
|
||||
</q-select>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-sm-6 col-md-2">
|
||||
<q-select
|
||||
v-model="store.filters.ulke"
|
||||
:options="store.ulkeOptions"
|
||||
multiple
|
||||
emit-value
|
||||
map-options
|
||||
filled
|
||||
dense
|
||||
options-dense
|
||||
class="compact-select"
|
||||
label="Ülke"
|
||||
:display-value="selectionLabel(store.filters.ulke, 'Ülke')"
|
||||
>
|
||||
<template #before-options>
|
||||
<q-item clickable dense @click.stop="store.selectAll('ulke', store.ulkeOptions)">
|
||||
<q-item-section>Tümünü Seç</q-item-section>
|
||||
</q-item>
|
||||
<q-item clickable dense @click.stop="store.clearAll('ulke')">
|
||||
<q-item-section>Tümünü Temizle</q-item-section>
|
||||
</q-item>
|
||||
<q-separator />
|
||||
</template>
|
||||
<template #option="scope">
|
||||
<q-item v-bind="scope.itemProps">
|
||||
<q-item-section avatar>
|
||||
<q-checkbox :model-value="scope.selected" />
|
||||
</q-item-section>
|
||||
<q-item-section>
|
||||
<q-item-label>{{ scope.opt.label }}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</template>
|
||||
</q-select>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-sm-6 col-md-2">
|
||||
<q-select
|
||||
v-model="store.filters.il"
|
||||
:options="store.ilOptions"
|
||||
multiple
|
||||
emit-value
|
||||
map-options
|
||||
filled
|
||||
dense
|
||||
options-dense
|
||||
class="compact-select"
|
||||
label="İl"
|
||||
:display-value="selectionLabel(store.filters.il, 'İl')"
|
||||
>
|
||||
<template #before-options>
|
||||
<q-item clickable dense @click.stop="store.selectAll('il', store.ilOptions)">
|
||||
<q-item-section>Tümünü Seç</q-item-section>
|
||||
</q-item>
|
||||
<q-item clickable dense @click.stop="store.clearAll('il')">
|
||||
<q-item-section>Tümünü Temizle</q-item-section>
|
||||
</q-item>
|
||||
<q-separator />
|
||||
</template>
|
||||
<template #option="scope">
|
||||
<q-item v-bind="scope.itemProps">
|
||||
<q-item-section avatar>
|
||||
<q-checkbox :model-value="scope.selected" />
|
||||
</q-item-section>
|
||||
<q-item-section>
|
||||
<q-item-label>{{ scope.opt.label }}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</template>
|
||||
</q-select>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-sm-6 col-md-2">
|
||||
<q-select
|
||||
v-model="store.filters.ilce"
|
||||
:options="store.ilceOptions"
|
||||
multiple
|
||||
emit-value
|
||||
map-options
|
||||
filled
|
||||
dense
|
||||
options-dense
|
||||
class="compact-select"
|
||||
label="İlçe"
|
||||
:display-value="selectionLabel(store.filters.ilce, 'İlçe')"
|
||||
>
|
||||
<template #before-options>
|
||||
<q-item clickable dense @click.stop="store.selectAll('ilce', store.ilceOptions)">
|
||||
<q-item-section>Tümünü Seç</q-item-section>
|
||||
</q-item>
|
||||
<q-item clickable dense @click.stop="store.clearAll('ilce')">
|
||||
<q-item-section>Tümünü Temizle</q-item-section>
|
||||
</q-item>
|
||||
<q-separator />
|
||||
</template>
|
||||
<template #option="scope">
|
||||
<q-item v-bind="scope.itemProps">
|
||||
<q-item-section avatar>
|
||||
<q-checkbox :model-value="scope.selected" />
|
||||
</q-item-section>
|
||||
<q-item-section>
|
||||
<q-item-label>{{ scope.opt.label }}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</template>
|
||||
</q-select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</q-slide-transition>
|
||||
|
||||
<q-banner v-if="store.error" class="bg-red-1 text-negative q-mb-md rounded-borders">
|
||||
{{ store.error }}
|
||||
</q-banner>
|
||||
|
||||
<q-banner v-if="!store.hasFetched && !store.loading" class="bg-blue-1 text-primary q-mb-md rounded-borders">
|
||||
Veriler bugünün cache tablosundan okunur. Verileri Yenile ile tekrar yükleyebilirsiniz.
|
||||
</q-banner>
|
||||
</div>
|
||||
|
||||
<div class="table-area">
|
||||
<div class="sticky-bar row justify-between items-center q-pa-sm bg-grey-1">
|
||||
<div />
|
||||
<div class="row items-center q-gutter-sm">
|
||||
<q-btn
|
||||
flat
|
||||
color="secondary"
|
||||
icon="list"
|
||||
:label="allDetailsOpen ? 'Tüm Detayları Kapat' : 'Tüm Detayları Aç'"
|
||||
@click="toggleAllDetails"
|
||||
/>
|
||||
<q-btn-dropdown
|
||||
v-if="canExportFinance"
|
||||
flat
|
||||
color="red"
|
||||
icon="picture_as_pdf"
|
||||
label="Yazdır"
|
||||
>
|
||||
<q-list style="min-width: 240px">
|
||||
<q-item clickable v-close-popup @click="downloadAgingBalancePDF(true)">
|
||||
<q-item-section class="text-primary">
|
||||
Detaylı Cari Yaşlandırmalı Bakiye Listesi Yazdır
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item clickable v-close-popup @click="downloadAgingBalancePDF(false)">
|
||||
<q-item-section class="text-secondary">
|
||||
Detaysız Cari Yaşlandırmalı Bakiye Listesi Yazdır
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
</q-btn-dropdown>
|
||||
<q-btn
|
||||
v-if="canExportFinance"
|
||||
flat
|
||||
color="green-8"
|
||||
icon="table_view"
|
||||
label="Excel"
|
||||
@click="downloadAgingBalanceExcel"
|
||||
/>
|
||||
<q-btn
|
||||
flat
|
||||
color="primary"
|
||||
:icon="filtersCollapsed ? 'unfold_more' : 'unfold_less'"
|
||||
:label="filtersCollapsed ? 'Filtreleri Genişlet' : 'Filtreleri Daralt'"
|
||||
@click="toggleFiltersCollapsed"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<q-table
|
||||
title="Cari Yaşlandırmalı Cari Bakiye Listesi"
|
||||
:rows="store.summaryRows"
|
||||
:columns="summaryColumns"
|
||||
row-key="group_key"
|
||||
:loading="store.loading"
|
||||
flat
|
||||
bordered
|
||||
dense
|
||||
wrap-cells
|
||||
separator="cell"
|
||||
hide-bottom
|
||||
:rows-per-page-options="[0]"
|
||||
:pagination="{ rowsPerPage: 0 }"
|
||||
:table-style="{ tableLayout: 'fixed', width: '100%' }"
|
||||
class="balance-table"
|
||||
>
|
||||
<template #header="props">
|
||||
<q-tr :props="props" class="header-row">
|
||||
<q-th v-for="col in props.cols" :key="col.name" :props="props">
|
||||
{{ col.label }}
|
||||
</q-th>
|
||||
</q-tr>
|
||||
<q-tr class="totals-row">
|
||||
<q-th
|
||||
v-for="col in props.cols"
|
||||
:key="`tot-${col.name}`"
|
||||
:class="col.align === 'right' ? 'text-right' : ''"
|
||||
>
|
||||
{{ totalCellValue(col.name) }}
|
||||
</q-th>
|
||||
</q-tr>
|
||||
</template>
|
||||
|
||||
<template #body="props">
|
||||
<q-tr :props="props" class="sub-header-row">
|
||||
<q-td v-for="col in props.cols" :key="col.name" :props="props">
|
||||
<q-btn
|
||||
v-if="col.name === 'expand'"
|
||||
dense
|
||||
flat
|
||||
round
|
||||
size="sm"
|
||||
:icon="expanded[props.row.group_key] ? 'expand_less' : 'expand_more'"
|
||||
@click="toggleGroup(props.row.group_key)"
|
||||
/>
|
||||
<span v-else-if="col.name === 'prbr_1_2'" class="text-right block prbr-cell">
|
||||
{{ formatCurrencyMap(props.row.bakiye_1_2_map) }}
|
||||
</span>
|
||||
<span v-else-if="col.name === 'prbr_1_3'" class="text-right block prbr-cell">
|
||||
{{ formatCurrencyMap(props.row.bakiye_1_3_map) }}
|
||||
</span>
|
||||
<span v-else-if="staticMoneyFields.includes(col.name)" class="text-center block">
|
||||
{{ formatAmount(props.row[col.field]) }}
|
||||
</span>
|
||||
<span v-else-if="dayFields.includes(col.name)" class="text-center block">
|
||||
{{ formatDay(props.row[col.field]) }}
|
||||
</span>
|
||||
<span v-else>{{ props.row[col.field] || '-' }}</span>
|
||||
</q-td>
|
||||
</q-tr>
|
||||
|
||||
<q-tr v-if="expanded[props.row.group_key]" class="detail-host-row">
|
||||
<q-td colspan="100%">
|
||||
<div class="detail-wrap">
|
||||
<q-table
|
||||
:rows="store.getDetailsByGroup(props.row.group_key)"
|
||||
:columns="detailColumns"
|
||||
row-key="cari_kodu"
|
||||
dense
|
||||
flat
|
||||
bordered
|
||||
hide-bottom
|
||||
:table-style="{ tableLayout: 'fixed', width: '100%' }"
|
||||
class="detail-table"
|
||||
>
|
||||
<template #body-cell-prbr_1_2="scope">
|
||||
<q-td :props="scope" class="text-right prbr-cell">
|
||||
{{ formatRowPrBr(scope.row, '1_2') }}
|
||||
</q-td>
|
||||
</template>
|
||||
<template #body-cell-prbr_1_3="scope">
|
||||
<q-td :props="scope" class="text-right prbr-cell">
|
||||
{{ formatRowPrBr(scope.row, '1_3') }}
|
||||
</q-td>
|
||||
</template>
|
||||
<template #body-cell-vade_gun="scope">
|
||||
<q-td :props="scope" class="text-center">
|
||||
{{ formatDay(scope.row.vade_gun) }}
|
||||
</q-td>
|
||||
</template>
|
||||
<template #body-cell-vade_belge_tarihi_gun="scope">
|
||||
<q-td :props="scope" class="text-center">
|
||||
{{ formatDay(scope.row.vade_belge_tarihi_gun) }}
|
||||
</q-td>
|
||||
</template>
|
||||
</q-table>
|
||||
</div>
|
||||
</q-td>
|
||||
</q-tr>
|
||||
</template>
|
||||
</q-table>
|
||||
</div>
|
||||
|
||||
</q-page>
|
||||
|
||||
<q-page v-else class="q-pa-md flex flex-center">
|
||||
<div class="text-negative text-subtitle1">
|
||||
Bu modüle erişim yetkiniz yok.
|
||||
</div>
|
||||
</q-page>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, ref } from 'vue'
|
||||
import { useQuasar } from 'quasar'
|
||||
import { onMounted } from 'vue'
|
||||
import { useAccountAgingBalanceStore } from 'src/stores/accountAgingBalanceStore'
|
||||
import { usePermission } from 'src/composables/usePermission'
|
||||
import { download, extractApiErrorDetail } from 'src/services/api'
|
||||
|
||||
const store = useAccountAgingBalanceStore()
|
||||
const expanded = ref({})
|
||||
const allDetailsOpen = ref(false)
|
||||
const filtersCollapsed = ref(false)
|
||||
const $q = useQuasar()
|
||||
|
||||
const { canRead, canExport } = usePermission()
|
||||
const canReadFinance = canRead('finance')
|
||||
const canExportFinance = canExport('finance')
|
||||
|
||||
const islemTipiOptions = [
|
||||
{ label: '1_2 Bakiye Pr.Br', value: 'prbr_1_2' },
|
||||
{ label: '1_3 Bakiye Pr.Br', value: 'prbr_1_3' },
|
||||
{ label: '1_2 USD Bakiye', value: 'usd_1_2' },
|
||||
{ label: '1_2 TRY Bakiye', value: 'try_1_2' },
|
||||
{ label: '1_3 USD Bakiye', value: 'usd_1_3' },
|
||||
{ label: '1_3 TRY Bakiye', value: 'try_1_3' }
|
||||
]
|
||||
|
||||
const staticMoneyFields = ['usd_bakiye_1_2', 'tl_bakiye_1_2', 'usd_bakiye_1_3', 'tl_bakiye_1_3']
|
||||
const dayFields = ['vade_gun', 'vade_belge_tarihi_gun']
|
||||
|
||||
function toNumericSortValue (value) {
|
||||
if (typeof value === 'number') {
|
||||
return Number.isFinite(value) ? value : 0
|
||||
}
|
||||
|
||||
const s = String(value ?? '').trim()
|
||||
if (!s) return 0
|
||||
|
||||
const hasComma = s.includes(',')
|
||||
const hasDot = s.includes('.')
|
||||
|
||||
let normalized = s.replace(/\s+/g, '')
|
||||
|
||||
if (hasComma && hasDot) {
|
||||
const lastComma = normalized.lastIndexOf(',')
|
||||
const lastDot = normalized.lastIndexOf('.')
|
||||
if (lastComma > lastDot) {
|
||||
normalized = normalized.replace(/\./g, '').replace(',', '.')
|
||||
} else {
|
||||
normalized = normalized.replace(/,/g, '')
|
||||
}
|
||||
} else if (hasComma) {
|
||||
normalized = normalized.replace(/\./g, '').replace(',', '.')
|
||||
}
|
||||
|
||||
const n = Number.parseFloat(normalized)
|
||||
return Number.isFinite(n) ? n : 0
|
||||
}
|
||||
|
||||
function sortTextTr (a, b) {
|
||||
return String(a ?? '').localeCompare(String(b ?? ''), 'tr', { sensitivity: 'base' })
|
||||
}
|
||||
|
||||
const metricDefs = {
|
||||
prbr_1_2: { name: 'prbr_1_2', label: '1_2 Bakiye\nPr.Br', field: 'prbr_1_2', align: 'right', sortable: false },
|
||||
prbr_1_3: { name: 'prbr_1_3', label: '1_3 Bakiye\nPr.Br', field: 'prbr_1_3', align: 'right', sortable: false },
|
||||
usd_1_2: { name: 'usd_bakiye_1_2', label: '1_2 USD_BAKIYE', field: 'usd_bakiye_1_2', align: 'center', sortable: true, sort: (a, b) => toNumericSortValue(a) - toNumericSortValue(b) },
|
||||
try_1_2: { name: 'tl_bakiye_1_2', label: '1_2 TRY_BAKIYE', field: 'tl_bakiye_1_2', align: 'center', sortable: true, sort: (a, b) => toNumericSortValue(a) - toNumericSortValue(b) },
|
||||
usd_1_3: { name: 'usd_bakiye_1_3', label: '1_3 USD_BAKIYE', field: 'usd_bakiye_1_3', align: 'center', sortable: true, sort: (a, b) => toNumericSortValue(a) - toNumericSortValue(b) },
|
||||
try_1_3: { name: 'tl_bakiye_1_3', label: '1_3 TRY_BAKIYE', field: 'tl_bakiye_1_3', align: 'center', sortable: true, sort: (a, b) => toNumericSortValue(a) - toNumericSortValue(b) }
|
||||
}
|
||||
|
||||
const selectedMetricKeys = computed(() => {
|
||||
const selected = store.filters.islemTipi || []
|
||||
if (!selected.length) return [...Object.keys(metricDefs)]
|
||||
return selected.filter((k) => k in metricDefs)
|
||||
})
|
||||
|
||||
const summaryColumns = computed(() => ([
|
||||
{ name: 'expand', label: '', field: 'expand', align: 'center', sortable: false },
|
||||
{ name: 'ana_cari_kodu', label: 'Ana Cari Kodu', field: 'ana_cari_kodu', align: 'left', sortable: true, sort: sortTextTr },
|
||||
{ name: 'ana_cari_adi', label: 'Ana Cari Detay', field: 'ana_cari_adi', align: 'left', sortable: true, sort: sortTextTr },
|
||||
{ name: 'piyasa', label: 'Piyasa', field: 'piyasa', align: 'left', sortable: true, sort: sortTextTr },
|
||||
{ name: 'temsilci', label: 'Temsilci', field: 'temsilci', align: 'left', sortable: true, sort: sortTextTr },
|
||||
{ name: 'risk_durumu', label: 'Risk Durumu', field: 'risk_durumu', align: 'left', sortable: true, sort: sortTextTr },
|
||||
...selectedMetricKeys.value.map((k) => metricDefs[k]),
|
||||
{ name: 'vade_gun', label: 'Vade Gun', field: 'vade_gun', align: 'center', sortable: true, sort: (a, b) => toNumericSortValue(a) - toNumericSortValue(b) },
|
||||
{ name: 'vade_belge_tarihi_gun', label: 'Belge Tarihi Gun', field: 'vade_belge_tarihi_gun', align: 'center', sortable: true, sort: (a, b) => toNumericSortValue(a) - toNumericSortValue(b) }
|
||||
]))
|
||||
|
||||
const liveTotals = computed(() => {
|
||||
return store.filteredRows.reduce((acc, row) => {
|
||||
acc.usd_bakiye_1_2 += Number(row.usd_bakiye_1_2) || 0
|
||||
acc.tl_bakiye_1_2 += Number(row.tl_bakiye_1_2) || 0
|
||||
acc.usd_bakiye_1_3 += Number(row.usd_bakiye_1_3) || 0
|
||||
acc.tl_bakiye_1_3 += Number(row.tl_bakiye_1_3) || 0
|
||||
const vadeGun = Number(row.vade_gun) || 0
|
||||
const vadeBelge = Number(row.vade_belge_tarihi_gun) || 0
|
||||
if (vadeGun !== 0 || vadeBelge !== 0) {
|
||||
acc.vade_count += 1
|
||||
acc.vade_gun_sum += vadeGun
|
||||
acc.vade_belge_sum += vadeBelge
|
||||
}
|
||||
return acc
|
||||
}, {
|
||||
usd_bakiye_1_2: 0,
|
||||
tl_bakiye_1_2: 0,
|
||||
usd_bakiye_1_3: 0,
|
||||
tl_bakiye_1_3: 0,
|
||||
vade_gun_sum: 0,
|
||||
vade_belge_sum: 0,
|
||||
vade_count: 0
|
||||
})
|
||||
})
|
||||
|
||||
const detailColumns = computed(() => [
|
||||
{ name: 'cari_kodu', label: 'Cari Kodu', field: 'cari_kodu', align: 'left' },
|
||||
{ name: 'cari_detay', label: 'Cari Detay', field: 'cari_detay', align: 'left' },
|
||||
{ name: 'sirket', label: 'Şirket', field: 'sirket', align: 'left' },
|
||||
{ name: 'sirket_detay', label: 'Şirket Detayı', field: 'sirket_detay', align: 'left' },
|
||||
{ name: 'muhasebe_kodu', label: 'Muhasebe Kodu', field: 'muhasebe_kodu', align: 'left' },
|
||||
{ name: 'piyasa', label: 'Piyasa', field: 'piyasa', align: 'left' },
|
||||
{ name: 'temsilci', label: 'Temsilci', field: 'temsilci', align: 'left' },
|
||||
{ name: 'risk_durumu', label: 'Risk Durumu', field: 'risk_durumu', align: 'left' },
|
||||
{ name: 'ozellik05', label: 'Ülke', field: 'ozellik05', align: 'left' },
|
||||
{ name: 'il', label: 'İl', field: 'il', align: 'left' },
|
||||
{ name: 'ilce', label: 'İlçe', field: 'ilce', align: 'left' },
|
||||
{ name: 'cari_doviz', label: 'Döviz', field: 'cari_doviz', align: 'left' },
|
||||
...selectedMetricKeys.value.map((k) => metricDefs[k]),
|
||||
{ name: 'vade_gun', label: 'Vade Gun', field: 'vade_gun', align: 'center' },
|
||||
{ name: 'vade_belge_tarihi_gun', label: 'Belge Tarihi Gun', field: 'vade_belge_tarihi_gun', align: 'center' }
|
||||
])
|
||||
|
||||
|
||||
function onReset () {
|
||||
store.resetFilters()
|
||||
store.fetchBalances()
|
||||
}
|
||||
|
||||
function onToggle12Changed (val) {
|
||||
if (val) {
|
||||
store.filters.excludeZeroBalance13 = false
|
||||
}
|
||||
}
|
||||
|
||||
function onToggle13Changed (val) {
|
||||
if (val) {
|
||||
store.filters.excludeZeroBalance12 = false
|
||||
}
|
||||
}
|
||||
|
||||
function toggleFiltersCollapsed () {
|
||||
filtersCollapsed.value = !filtersCollapsed.value
|
||||
}
|
||||
|
||||
|
||||
function toggleGroup (key) {
|
||||
expanded.value[key] = !expanded.value[key]
|
||||
if (!expanded.value[key]) {
|
||||
allDetailsOpen.value = false
|
||||
return
|
||||
}
|
||||
|
||||
allDetailsOpen.value =
|
||||
store.summaryRows.length > 0 &&
|
||||
store.summaryRows.every(r => expanded.value[r.group_key])
|
||||
}
|
||||
|
||||
function toggleAllDetails () {
|
||||
allDetailsOpen.value = !allDetailsOpen.value
|
||||
|
||||
if (allDetailsOpen.value) {
|
||||
const next = {}
|
||||
for (const row of store.summaryRows) {
|
||||
next[row.group_key] = true
|
||||
}
|
||||
expanded.value = next
|
||||
return
|
||||
}
|
||||
|
||||
expanded.value = {}
|
||||
}
|
||||
|
||||
async function downloadAgingBalancePDF (detailed) {
|
||||
if (!canExportFinance.value) {
|
||||
$q.notify({ type: 'negative', message: 'PDF export yetkiniz yok', position: 'top-right' })
|
||||
return
|
||||
}
|
||||
|
||||
if (!store.hasFetched) {
|
||||
$q.notify({ type: 'warning', message: 'Önce Bakiyeleri Getir ile veri yükleyin.', position: 'top-right' })
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const params = {
|
||||
cari_search: String(store.filters.cariSearch || '').trim(),
|
||||
cari_ilk_grup: (store.filters.cariIlkGrup || []).join(','),
|
||||
piyasa: (store.filters.piyasa || []).join(','),
|
||||
temsilci: (store.filters.temsilci || []).join(','),
|
||||
risk_durumu: (store.filters.riskDurumu || []).join(','),
|
||||
islem_tipi: (store.filters.islemTipi || []).join(','),
|
||||
ulke: (store.filters.ulke || []).join(','),
|
||||
il: (store.filters.il || []).join(','),
|
||||
ilce: (store.filters.ilce || []).join(','),
|
||||
exclude_zero_12: store.filters.excludeZeroBalance12 ? '1' : '0',
|
||||
exclude_zero_13: store.filters.excludeZeroBalance13 ? '1' : '0',
|
||||
detailed: detailed ? '1' : '0'
|
||||
}
|
||||
|
||||
const blob = await download('/finance/account-aging-statement/export-pdf', params)
|
||||
const pdfUrl = window.URL.createObjectURL(new Blob([blob], { type: 'application/pdf' }))
|
||||
window.open(pdfUrl, '_blank')
|
||||
} catch (err) {
|
||||
const detail = await extractApiErrorDetail(err?.original || err)
|
||||
$q.notify({
|
||||
type: 'negative',
|
||||
message: detail || 'PDF oluşturulamadı',
|
||||
position: 'top-right'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async function downloadAgingBalanceExcel () {
|
||||
if (!canExportFinance.value) {
|
||||
$q.notify({ type: 'negative', message: 'Excel export yetkiniz yok', position: 'top-right' })
|
||||
return
|
||||
}
|
||||
|
||||
if (!store.hasFetched) {
|
||||
$q.notify({ type: 'warning', message: 'Önce Bakiyeleri Getir ile veri yükleyin.', position: 'top-right' })
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const params = {
|
||||
cari_search: String(store.filters.cariSearch || '').trim(),
|
||||
cari_ilk_grup: (store.filters.cariIlkGrup || []).join(','),
|
||||
piyasa: (store.filters.piyasa || []).join(','),
|
||||
temsilci: (store.filters.temsilci || []).join(','),
|
||||
risk_durumu: (store.filters.riskDurumu || []).join(','),
|
||||
islem_tipi: (store.filters.islemTipi || []).join(','),
|
||||
ulke: (store.filters.ulke || []).join(','),
|
||||
il: (store.filters.il || []).join(','),
|
||||
ilce: (store.filters.ilce || []).join(','),
|
||||
exclude_zero_12: store.filters.excludeZeroBalance12 ? '1' : '0',
|
||||
exclude_zero_13: store.filters.excludeZeroBalance13 ? '1' : '0'
|
||||
}
|
||||
|
||||
const file = await download('/finance/account-aging-statement/export-excel', params)
|
||||
const blob = new Blob(
|
||||
[file],
|
||||
{ type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }
|
||||
)
|
||||
const url = window.URL.createObjectURL(blob)
|
||||
const a = document.createElement('a')
|
||||
a.href = url
|
||||
a.download = 'cari_yaslandirmali_bakiye_listesi.xlsx'
|
||||
document.body.appendChild(a)
|
||||
a.click()
|
||||
a.remove()
|
||||
window.URL.revokeObjectURL(url)
|
||||
} catch (err) {
|
||||
const detail = await extractApiErrorDetail(err?.original || err)
|
||||
$q.notify({
|
||||
type: 'negative',
|
||||
message: detail || 'Excel oluşturulamadı',
|
||||
position: 'top-right'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function formatAmount (value) {
|
||||
const n = Number(value || 0)
|
||||
return new Intl.NumberFormat('tr-TR', {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2
|
||||
}).format(n)
|
||||
}
|
||||
|
||||
function formatDay (value) {
|
||||
const n = Number(value || 0)
|
||||
return new Intl.NumberFormat('tr-TR', {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2
|
||||
}).format(n)
|
||||
}
|
||||
|
||||
function selectionLabel (arr, label) {
|
||||
const count = Array.isArray(arr) ? arr.length : 0
|
||||
if (count === 0) return `Tümü (${label})`
|
||||
if (count === 1) return '1 seçim'
|
||||
return `${count} seçim`
|
||||
}
|
||||
|
||||
function totalCellValue (colName) {
|
||||
if (colName === 'expand') return 'Toplam'
|
||||
if (colName === 'piyasa') return '-'
|
||||
if (colName === 'temsilci') return '-'
|
||||
if (colName === 'risk_durumu') return '-'
|
||||
if (colName === 'prbr_1_2') return formatCurrencyMap(totalByCurrency('1_2'))
|
||||
if (colName === 'prbr_1_3') return formatCurrencyMap(totalByCurrency('1_3'))
|
||||
if (colName === 'usd_bakiye_1_2') return formatAmount(liveTotals.value.usd_bakiye_1_2)
|
||||
if (colName === 'tl_bakiye_1_2') return formatAmount(liveTotals.value.tl_bakiye_1_2)
|
||||
if (colName === 'usd_bakiye_1_3') return formatAmount(liveTotals.value.usd_bakiye_1_3)
|
||||
if (colName === 'tl_bakiye_1_3') return formatAmount(liveTotals.value.tl_bakiye_1_3)
|
||||
if (colName === 'vade_gun') return formatDay(liveTotals.value.vade_count > 0 ? liveTotals.value.vade_gun_sum / liveTotals.value.vade_count : 0)
|
||||
if (colName === 'vade_belge_tarihi_gun') return formatDay(liveTotals.value.vade_count > 0 ? liveTotals.value.vade_belge_sum / liveTotals.value.vade_count : 0)
|
||||
return '-'
|
||||
}
|
||||
|
||||
function totalByCurrency (tip) {
|
||||
const key = tip === '1_2' ? 'bakiye_1_2_map' : 'bakiye_1_3_map'
|
||||
const out = {}
|
||||
|
||||
for (const r of store.summaryRows) {
|
||||
const m = r[key] || {}
|
||||
for (const [curr, val] of Object.entries(m)) {
|
||||
out[curr] = (Number(out[curr]) || 0) + (Number(val) || 0)
|
||||
}
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
function formatCurrencyMap (mapObj) {
|
||||
const entries = Object.entries(mapObj || {})
|
||||
.filter(([, amount]) => Number(amount) !== 0)
|
||||
.sort((a, b) => a[0].localeCompare(b[0], 'en'))
|
||||
|
||||
if (!entries.length) return '-'
|
||||
return entries
|
||||
.map(([curr, amount]) => `${curr}: ${formatAmount(amount)}`)
|
||||
.join('\n')
|
||||
}
|
||||
|
||||
function formatRowPrBr (row, tip) {
|
||||
const curr = String(row.cari_doviz || '').trim().toUpperCase() || 'N/A'
|
||||
|
||||
const amount = tip === '1_2'
|
||||
? (Number(row.bakiye_1_2) || 0)
|
||||
: (Number(row.bakiye_1_3) || 0)
|
||||
|
||||
if (amount === 0) return '-'
|
||||
return `${curr} ${formatAmount(amount)}`
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
store.filters.selectedDate = new Date().toISOString().slice(0, 10)
|
||||
await store.fetchBalances()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.page-layout {
|
||||
height: calc(100vh - 110px);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.filter-sticky {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 20;
|
||||
background: #fff;
|
||||
padding-bottom: 6px;
|
||||
}
|
||||
|
||||
.filter-sticky.collapsed {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.top-actions.single-line {
|
||||
flex-wrap: nowrap;
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
scrollbar-width: thin;
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
|
||||
.top-actions.single-line > [class*='col-'],
|
||||
.top-actions.single-line > .col-auto {
|
||||
flex: 0 0 auto;
|
||||
min-width: 220px;
|
||||
}
|
||||
|
||||
.top-actions.single-line > .col-auto {
|
||||
min-width: auto;
|
||||
}
|
||||
|
||||
.filters-panel {
|
||||
border: 1px solid rgba(0, 0, 0, 0.12);
|
||||
border-radius: 8px;
|
||||
background: #fafafa;
|
||||
}
|
||||
|
||||
.compact-select :deep(.q-field__control) {
|
||||
min-height: 40px;
|
||||
}
|
||||
|
||||
.compact-select :deep(.q-field__native),
|
||||
.compact-select :deep(.q-field__input) {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.table-area {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.sticky-bar {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 9;
|
||||
}
|
||||
|
||||
.balance-table {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.balance-table :deep(.q-table__container) {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.balance-table :deep(.q-table__top) {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 6;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.balance-table :deep(.q-table__middle) {
|
||||
flex: 1 1 auto;
|
||||
min-height: 0;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.balance-table :deep(.header-row th) {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 8;
|
||||
background: var(--q-primary);
|
||||
color: #fff;
|
||||
font-weight: 600;
|
||||
font-family: 'Roboto', sans-serif;
|
||||
}
|
||||
|
||||
.balance-table :deep(.totals-row th) {
|
||||
position: sticky;
|
||||
top: 38px;
|
||||
z-index: 7;
|
||||
background: var(--q-secondary);
|
||||
color: var(--q-dark);
|
||||
font-weight: 700;
|
||||
font-family: 'Roboto', sans-serif;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
.detail-table :deep(.q-table__middle) {
|
||||
max-height: 320px;
|
||||
}
|
||||
|
||||
.balance-table :deep(.sub-header-row td) {
|
||||
background: #fff;
|
||||
border-bottom: 2px solid rgba(0, 0, 0, 0.18);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.balance-table :deep(.sub-header-row td:first-child) {
|
||||
border-left: 3px solid var(--q-primary);
|
||||
}
|
||||
|
||||
.balance-table :deep(.detail-host-row td) {
|
||||
background: #f7f7f7;
|
||||
border-bottom: 10px solid #fff;
|
||||
padding-top: 10px;
|
||||
padding-bottom: 12px;
|
||||
}
|
||||
|
||||
.detail-wrap {
|
||||
border: 1px solid rgba(0, 0, 0, 0.14);
|
||||
border-left: 4px solid var(--q-secondary);
|
||||
border-radius: 6px;
|
||||
background: #fff;
|
||||
padding: 6px;
|
||||
}
|
||||
|
||||
.balance-table :deep(.header-row th) {
|
||||
white-space: pre-line;
|
||||
line-height: 1.15;
|
||||
}
|
||||
|
||||
.prbr-cell {
|
||||
white-space: pre-line;
|
||||
word-break: break-word;
|
||||
line-height: 1.25;
|
||||
}
|
||||
|
||||
.balance-table :deep(th),
|
||||
.balance-table :deep(td),
|
||||
.detail-table :deep(th),
|
||||
.detail-table :deep(td) {
|
||||
white-space: normal !important;
|
||||
word-break: break-word;
|
||||
overflow-wrap: anywhere;
|
||||
font-size: 11px;
|
||||
line-height: 1.2;
|
||||
padding: 4px 6px !important;
|
||||
}
|
||||
|
||||
.balance-table :deep(.q-table__table),
|
||||
.detail-table :deep(.q-table__table) {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
.detail-table :deep(.q-table__middle) {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
@@ -2,96 +2,104 @@
|
||||
<q-page v-if="canReadFinance" class="q-px-md q-pb-md page-col statement-page">
|
||||
|
||||
<q-slide-transition>
|
||||
<div
|
||||
v-show="!filtersCollapsed"
|
||||
class="local-filter-bar compact-filter q-pa-sm q-mb-xs"
|
||||
>
|
||||
<div class="row q-col-gutter-sm items-end">
|
||||
<div class="col-12 col-md-4">
|
||||
<q-select
|
||||
v-model="selectedCari"
|
||||
:options="filteredOptions"
|
||||
label="Cari kod / isim"
|
||||
filled
|
||||
dense
|
||||
clearable
|
||||
use-input
|
||||
input-debounce="300"
|
||||
@filter="filterCari"
|
||||
emit-value
|
||||
map-options
|
||||
:loading="accountStore.loading"
|
||||
option-value="value"
|
||||
option-label="label"
|
||||
behavior="menu"
|
||||
:keep-selected="true"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-12 col-sm-6 col-md-2">
|
||||
<q-input
|
||||
v-model="dateFrom"
|
||||
label="Tarih aralığı - başlangıç"
|
||||
filled
|
||||
dense
|
||||
clearable
|
||||
readonly
|
||||
>
|
||||
<template #append>
|
||||
<q-icon name="event" class="cursor-pointer">
|
||||
<q-popup-proxy cover transition-show="scale" transition-hide="scale">
|
||||
<q-date
|
||||
v-model="dateFrom"
|
||||
mask="YYYY-MM-DD"
|
||||
locale="tr-TR"
|
||||
:options="isValidFromDate"
|
||||
/>
|
||||
</q-popup-proxy>
|
||||
</q-icon>
|
||||
</template>
|
||||
</q-input>
|
||||
</div>
|
||||
<div class="col-12 col-sm-6 col-md-2">
|
||||
<q-input
|
||||
v-model="dateTo"
|
||||
label="Tarih aralığı - bitiş"
|
||||
filled
|
||||
dense
|
||||
clearable
|
||||
readonly
|
||||
>
|
||||
<template #append>
|
||||
<q-icon name="event" class="cursor-pointer">
|
||||
<q-popup-proxy cover transition-show="scale" transition-hide="scale">
|
||||
<q-date
|
||||
v-model="dateTo"
|
||||
mask="YYYY-MM-DD"
|
||||
locale="tr-TR"
|
||||
:options="isValidToDate"
|
||||
/>
|
||||
</q-popup-proxy>
|
||||
</q-icon>
|
||||
</template>
|
||||
</q-input>
|
||||
</div>
|
||||
<div class="col-12 col-sm-6 col-md-2">
|
||||
<q-select
|
||||
v-model="selectedMonType"
|
||||
:options="monetaryTypeOptions"
|
||||
label="Parasal İşlem Tipi"
|
||||
emit-value
|
||||
map-options
|
||||
filled
|
||||
dense
|
||||
/>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<q-btn color="primary" icon="filter_alt" label="Filtrele" @click="onFilterClick" />
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<q-btn flat color="grey-8" icon="restart_alt" label="Sıfırla" @click="resetFilters" />
|
||||
<div
|
||||
v-show="!filtersCollapsed"
|
||||
class="local-filter-bar compact-filter q-pa-sm q-mb-xs"
|
||||
>
|
||||
<div class="row q-col-gutter-sm items-end">
|
||||
<div class="col-12 col-md-4">
|
||||
<q-select
|
||||
v-model="selectedCari"
|
||||
:options="filteredOptions"
|
||||
label="Cari kod / isim"
|
||||
filled
|
||||
dense
|
||||
clearable
|
||||
use-input
|
||||
input-debounce="300"
|
||||
@filter="filterCari"
|
||||
emit-value
|
||||
map-options
|
||||
:loading="accountStore.loading"
|
||||
option-value="value"
|
||||
option-label="label"
|
||||
behavior="menu"
|
||||
:keep-selected="true"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-12 col-sm-6 col-md-2">
|
||||
<q-input
|
||||
v-model="dateFrom"
|
||||
label="Tarih aralığı - başlangıç"
|
||||
filled
|
||||
dense
|
||||
clearable
|
||||
readonly
|
||||
>
|
||||
<template #append>
|
||||
<q-icon name="event" class="cursor-pointer">
|
||||
<q-popup-proxy cover transition-show="scale" transition-hide="scale">
|
||||
<q-date
|
||||
v-model="dateFrom"
|
||||
mask="YYYY-MM-DD"
|
||||
locale="tr-TR"
|
||||
:options="isValidFromDate"
|
||||
/>
|
||||
</q-popup-proxy>
|
||||
</q-icon>
|
||||
</template>
|
||||
</q-input>
|
||||
</div>
|
||||
<div class="col-12 col-sm-6 col-md-2">
|
||||
<q-input
|
||||
v-model="dateTo"
|
||||
label="Tarih aralığı - bitiş"
|
||||
filled
|
||||
dense
|
||||
clearable
|
||||
readonly
|
||||
>
|
||||
<template #append>
|
||||
<q-icon name="event" class="cursor-pointer">
|
||||
<q-popup-proxy cover transition-show="scale" transition-hide="scale">
|
||||
<q-date
|
||||
v-model="dateTo"
|
||||
mask="YYYY-MM-DD"
|
||||
locale="tr-TR"
|
||||
:options="isValidToDate"
|
||||
/>
|
||||
</q-popup-proxy>
|
||||
</q-icon>
|
||||
</template>
|
||||
</q-input>
|
||||
</div>
|
||||
<div class="col-12 col-sm-6 col-md-2">
|
||||
<q-select
|
||||
v-model="selectedMonType"
|
||||
:options="monetaryTypeOptions"
|
||||
label="Parasal İşlem Tipi"
|
||||
emit-value
|
||||
map-options
|
||||
filled
|
||||
dense
|
||||
/>
|
||||
</div>
|
||||
<div class="col-12 col-sm-6 col-md-2">
|
||||
<q-toggle
|
||||
v-model="excludeOpening"
|
||||
label="Devir bakiyesiz listele"
|
||||
color="primary"
|
||||
dense
|
||||
/>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<q-btn color="primary" icon="filter_alt" label="Filtrele" @click="onFilterClick" />
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<q-btn flat color="grey-8" icon="restart_alt" label="Sıfırla" @click="resetFilters" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</q-slide-transition>
|
||||
|
||||
<!-- Tablo Alanı -->
|
||||
@@ -358,6 +366,7 @@ const monetaryTypeOptions = [
|
||||
{ label: '1-3 r hesap', value: ['1', '3'] }
|
||||
]
|
||||
const selectedMonType = ref(monetaryTypeOptions[0].value)
|
||||
const excludeOpening = ref(false)
|
||||
|
||||
/* Expand kontrolü */
|
||||
const expandedRows = ref({})
|
||||
@@ -404,7 +413,8 @@ async function onFilterClick() {
|
||||
enddate: dateTo.value,
|
||||
accountcode: selectedCari.value,
|
||||
langcode: 'TR',
|
||||
parislemler: selectedMonType.value
|
||||
parislemler: selectedMonType.value,
|
||||
excludeopening: excludeOpening.value
|
||||
})
|
||||
|
||||
await detailStore.loadDetails({
|
||||
@@ -454,6 +464,7 @@ function resetFilters() {
|
||||
dateFrom.value = ''
|
||||
dateTo.value = ''
|
||||
selectedMonType.value = monetaryTypeOptions[0].value
|
||||
excludeOpening.value = false
|
||||
statementheaderStore.headers = []
|
||||
detailStore.reset()
|
||||
}
|
||||
@@ -715,4 +726,3 @@ async function CurrheadDownload() {
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
@@ -149,7 +149,7 @@ const routes = [
|
||||
{
|
||||
path: 'aged-customer-balance-list',
|
||||
name: 'aged-customer-balance-list',
|
||||
redirect: { name: 'account-aging-statement' },
|
||||
component: () => import('pages/AgingCustomerBalancelist.go.vue'),
|
||||
meta: { permission: 'finance:view' }
|
||||
},
|
||||
|
||||
|
||||
@@ -180,7 +180,7 @@ export const useAccountAgingBalanceStore = defineStore('accountAgingBalance', {
|
||||
|
||||
try {
|
||||
this.filters.selectedDate = new Date().toISOString().slice(0, 10)
|
||||
const { data } = await api.get('/finance/account-aging-statement', {
|
||||
const { data } = await api.get('/finance/aged-customer-balance-list', {
|
||||
params: {
|
||||
cari_search: String(this.filters.cariSearch || '').trim(),
|
||||
cari_ilk_grup: (this.filters.cariIlkGrup || []).join(','),
|
||||
|
||||
@@ -50,32 +50,25 @@ export const useStatementAgingStore = defineStore('statementAging', {
|
||||
const currencyKey = `${cari8}|${curr}`
|
||||
const tutar = Number(row?.eslesen_tutar) || 0
|
||||
const usd = Number(row?.usd_tutar) || 0
|
||||
const trY = Number(row?.try_tutar) || 0
|
||||
const absTry = Math.abs(trY)
|
||||
const absUsd = Math.abs(usd)
|
||||
const gun = Number(row?.gun_sayisi) || 0
|
||||
const gunDoc = Number(row?.gun_sayisi_docdate) || 0
|
||||
const aciklama = String(row?.aciklama || '').toUpperCase()
|
||||
const isAcik = aciklama === 'ACIKKALEM'
|
||||
|
||||
if (!masterMap[masterKey]) {
|
||||
masterMap[masterKey] = {
|
||||
group_key: masterKey,
|
||||
cari8: masterKey,
|
||||
cari_detay: String(row?.cari_detay || '').trim(),
|
||||
acik_kalem_tutari_usd: 0,
|
||||
acik_kalem_tutari_try: 0,
|
||||
acik_kalem_ort_vade_gun: 0,
|
||||
acik_kalem_ort_belge_gun: 0,
|
||||
normal_usd_tutar: 0,
|
||||
normal_try_tutar: 0,
|
||||
ortalama_vade_gun: 0,
|
||||
ortalama_belge_gun: 0,
|
||||
weighted_all_base: 0,
|
||||
weighted_all_gun_sum: 0,
|
||||
weighted_all_doc_sum: 0,
|
||||
weighted_open_base: 0,
|
||||
weighted_open_gun_sum: 0,
|
||||
weighted_open_doc_sum: 0,
|
||||
satir_sayisi: 0,
|
||||
toplam_usd: 0,
|
||||
normal_usd: 0,
|
||||
acik_kalem_usd: 0,
|
||||
weighted_gun_sum: 0,
|
||||
weighted_gun_doc_sum: 0,
|
||||
weighted_base: 0,
|
||||
ortalama_gun: 0,
|
||||
ortalama_gun_docdate: 0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,46 +78,48 @@ export const useStatementAgingStore = defineStore('statementAging', {
|
||||
master_key: masterKey,
|
||||
cari8,
|
||||
cari_detay: String(row?.cari_detay || '').trim(),
|
||||
doviz_cinsi: curr,
|
||||
acik_kalem_tutari: 0,
|
||||
acik_kalem_usd: 0,
|
||||
acik_kalem_try: 0,
|
||||
ort_gun: 0,
|
||||
ort_belge_gun: 0,
|
||||
weighted_open_base: 0,
|
||||
weighted_open_gun_sum: 0,
|
||||
weighted_open_doc_sum: 0
|
||||
doc_currency_code: curr,
|
||||
satir_sayisi: 0,
|
||||
toplam_tutar: 0,
|
||||
toplam_usd: 0,
|
||||
normal_tutar: 0,
|
||||
acik_kalem_tutar: 0,
|
||||
weighted_gun_sum: 0,
|
||||
weighted_gun_doc_sum: 0,
|
||||
weighted_base: 0,
|
||||
ortalama_gun: 0,
|
||||
ortalama_gun_docdate: 0
|
||||
}
|
||||
}
|
||||
|
||||
const m = masterMap[masterKey]
|
||||
const c = currencyMap[currencyKey]
|
||||
|
||||
if (isAcik) {
|
||||
m.acik_kalem_tutari_usd += usd
|
||||
m.acik_kalem_tutari_try += trY
|
||||
c.acik_kalem_tutari += tutar
|
||||
c.acik_kalem_usd += usd
|
||||
c.acik_kalem_try += trY
|
||||
m.satir_sayisi += 1
|
||||
m.toplam_usd += usd
|
||||
if (aciklama === 'ACIKKALEM') {
|
||||
m.acik_kalem_usd += usd
|
||||
} else {
|
||||
m.normal_usd_tutar += usd
|
||||
m.normal_try_tutar += trY
|
||||
m.normal_usd += usd
|
||||
}
|
||||
|
||||
if (absTry > 0) {
|
||||
m.weighted_all_base += absTry
|
||||
m.weighted_all_gun_sum += absTry * gun
|
||||
m.weighted_all_doc_sum += absTry * gunDoc
|
||||
c.satir_sayisi += 1
|
||||
c.toplam_tutar += tutar
|
||||
c.toplam_usd += usd
|
||||
if (aciklama === 'ACIKKALEM') {
|
||||
c.acik_kalem_tutar += tutar
|
||||
} else {
|
||||
c.normal_tutar += tutar
|
||||
}
|
||||
|
||||
if (isAcik) {
|
||||
m.weighted_open_base += absTry
|
||||
m.weighted_open_gun_sum += absTry * gun
|
||||
m.weighted_open_doc_sum += absTry * gunDoc
|
||||
if (absUsd > 0) {
|
||||
m.weighted_base += absUsd
|
||||
m.weighted_gun_sum += absUsd * gun
|
||||
m.weighted_gun_doc_sum += absUsd * gunDoc
|
||||
|
||||
c.weighted_open_base += absTry
|
||||
c.weighted_open_gun_sum += absTry * gun
|
||||
c.weighted_open_doc_sum += absTry * gunDoc
|
||||
}
|
||||
c.weighted_base += absUsd
|
||||
c.weighted_gun_sum += absUsd * gun
|
||||
c.weighted_gun_doc_sum += absUsd * gunDoc
|
||||
}
|
||||
|
||||
if (!detailMap[currencyKey]) detailMap[currencyKey] = []
|
||||
@@ -134,10 +129,8 @@ export const useStatementAgingStore = defineStore('statementAging', {
|
||||
this.masterRows = Object.values(masterMap)
|
||||
.map((m) => ({
|
||||
...m,
|
||||
acik_kalem_ort_vade_gun: m.weighted_open_base > 0 ? ceilDay(m.weighted_open_gun_sum / m.weighted_open_base) : 0,
|
||||
acik_kalem_ort_belge_gun: m.weighted_open_base > 0 ? ceilDay(m.weighted_open_doc_sum / m.weighted_open_base) : 0,
|
||||
ortalama_vade_gun: m.weighted_all_base > 0 ? ceilDay(m.weighted_all_gun_sum / m.weighted_all_base) : 0,
|
||||
ortalama_belge_gun: m.weighted_all_base > 0 ? ceilDay(m.weighted_all_doc_sum / m.weighted_all_base) : 0
|
||||
ortalama_gun: m.weighted_base > 0 ? (m.weighted_gun_sum / m.weighted_base) : 0,
|
||||
ortalama_gun_docdate: m.weighted_base > 0 ? (m.weighted_gun_doc_sum / m.weighted_base) : 0
|
||||
}))
|
||||
.sort((a, b) => String(a.cari8).localeCompare(String(b.cari8), 'tr', { sensitivity: 'base' }))
|
||||
|
||||
@@ -145,32 +138,18 @@ export const useStatementAgingStore = defineStore('statementAging', {
|
||||
for (const c of Object.values(currencyMap)) {
|
||||
const row = {
|
||||
...c,
|
||||
ort_gun: c.weighted_open_base > 0 ? ceilDay(c.weighted_open_gun_sum / c.weighted_open_base) : 0,
|
||||
ort_belge_gun: c.weighted_open_base > 0 ? ceilDay(c.weighted_open_doc_sum / c.weighted_open_base) : 0
|
||||
ortalama_gun: c.weighted_base > 0 ? (c.weighted_gun_sum / c.weighted_base) : 0,
|
||||
ortalama_gun_docdate: c.weighted_base > 0 ? (c.weighted_gun_doc_sum / c.weighted_base) : 0
|
||||
}
|
||||
if (!currencyByMaster[row.master_key]) currencyByMaster[row.master_key] = []
|
||||
currencyByMaster[row.master_key].push(row)
|
||||
}
|
||||
|
||||
for (const key of Object.keys(currencyByMaster)) {
|
||||
currencyByMaster[key].sort((a, b) => String(a.doviz_cinsi).localeCompare(String(b.doviz_cinsi), 'en', { sensitivity: 'base' }))
|
||||
currencyByMaster[key].sort((a, b) => String(a.doc_currency_code).localeCompare(String(b.doc_currency_code), 'en', { sensitivity: 'base' }))
|
||||
}
|
||||
|
||||
this.currencyRowsByMaster = currencyByMaster
|
||||
for (const key of Object.keys(detailMap)) {
|
||||
detailMap[key].sort((a, b) => {
|
||||
const aEmpty = !a?.odeme_tarihi
|
||||
const bEmpty = !b?.odeme_tarihi
|
||||
if (aEmpty && !bEmpty) return -1
|
||||
if (!aEmpty && bEmpty) return 1
|
||||
if (aEmpty && bEmpty) return 0
|
||||
const aTs = Date.parse(a.odeme_tarihi)
|
||||
const bTs = Date.parse(b.odeme_tarihi)
|
||||
const aNum = Number.isFinite(aTs) ? aTs : -Infinity
|
||||
const bNum = Number.isFinite(bTs) ? bTs : -Infinity
|
||||
return bNum - aNum
|
||||
})
|
||||
}
|
||||
this.detailByCurrency = detailMap
|
||||
},
|
||||
|
||||
@@ -206,17 +185,9 @@ function normalizeRowKeys(row) {
|
||||
odeme_doc_date: row.OdemeDocDate ?? row.odeme_doc_date ?? null,
|
||||
eslesen_tutar: Number(row.EslesenTutar ?? row.eslesen_tutar ?? 0),
|
||||
usd_tutar: Number(row.UsdTutar ?? row.usd_tutar ?? 0),
|
||||
try_tutar: Number(row.TryTutar ?? row.try_tutar ?? 0),
|
||||
gun_sayisi: Number(row.GunSayisi ?? row.gun_sayisi ?? 0),
|
||||
gun_sayisi_docdate: Number(row.GunSayisi_DocDate ?? row.gun_sayisi_docdate ?? 0),
|
||||
gun_kur: Number(row.GunKur ?? row.gun_kur ?? 0),
|
||||
aciklama: row.Aciklama ?? row.aciklama ?? null,
|
||||
doc_currency_code: row.DocCurrencyCode ?? row.doc_currency_code ?? null
|
||||
}
|
||||
}
|
||||
|
||||
function ceilDay(value) {
|
||||
const n = Number(value)
|
||||
if (!Number.isFinite(n)) return 0
|
||||
return Math.ceil(n)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user