565 lines
15 KiB
Vue
565 lines
15 KiB
Vue
<template>
|
||
<q-page v-if="canReadOrder" class="ol-page">
|
||
<div class="ol-filter-bar">
|
||
<div class="ol-filter-row">
|
||
<q-input
|
||
v-model="store.filters.search"
|
||
class="ol-filter-input ol-search"
|
||
dense
|
||
filled
|
||
debounce="300"
|
||
clearable
|
||
label="Arama (Sipariş No / Cari / Açıklama)"
|
||
>
|
||
<template #append>
|
||
<q-icon name="search" />
|
||
</template>
|
||
</q-input>
|
||
|
||
<q-input
|
||
v-model="store.filters.CurrAccCode"
|
||
class="ol-filter-input"
|
||
dense
|
||
filled
|
||
clearable
|
||
label="Cari Kodu"
|
||
/>
|
||
|
||
<q-input
|
||
v-model="store.filters.OrderDate"
|
||
class="ol-filter-input"
|
||
dense
|
||
filled
|
||
type="date"
|
||
label="Sipariş Tarihi"
|
||
/>
|
||
|
||
<div class="ol-filter-actions">
|
||
<q-btn
|
||
label="Temizle"
|
||
icon="clear"
|
||
color="grey-7"
|
||
flat
|
||
:disable="store.loading"
|
||
@click="clearFilters"
|
||
>
|
||
<q-tooltip>Tüm filtreleri temizle</q-tooltip>
|
||
</q-btn>
|
||
|
||
<q-btn
|
||
label="Yenile"
|
||
color="primary"
|
||
icon="refresh"
|
||
:loading="store.loading"
|
||
@click="store.fetchOrders"
|
||
/>
|
||
|
||
<q-btn
|
||
label="Excel'e Aktar"
|
||
icon="download"
|
||
color="primary"
|
||
outline
|
||
:disable="store.loading || store.filteredOrders.length === 0"
|
||
@click="exportExcel"
|
||
/>
|
||
</div>
|
||
|
||
<div class="ol-filter-total">
|
||
<div class="ol-total-line">
|
||
<span class="ol-total-label">Toplam USD:</span>
|
||
<strong class="ol-total-value">
|
||
{{ store.totalVisibleUSD.toLocaleString('tr-TR', { minimumFractionDigits: 2, maximumFractionDigits: 2 }) }}
|
||
</strong>
|
||
</div>
|
||
<div class="ol-total-line">
|
||
<span class="ol-total-label">Paketlenen USD:</span>
|
||
<strong class="ol-total-value">
|
||
{{ store.totalPackedVisibleUSD.toLocaleString('tr-TR', { minimumFractionDigits: 2, maximumFractionDigits: 2 }) }}
|
||
</strong>
|
||
</div>
|
||
<div class="ol-total-line">
|
||
<span class="ol-total-label">Paketlenme %:</span>
|
||
<strong class="ol-total-value" :class="packRateClass(store.packedVisibleRatePct)">
|
||
{{ store.packedVisibleRatePct.toLocaleString('tr-TR', { minimumFractionDigits: 2, maximumFractionDigits: 2 }) }}
|
||
</strong>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<q-table
|
||
title="Mevcut Siparişler"
|
||
class="ol-table"
|
||
flat
|
||
bordered
|
||
dense
|
||
separator="cell"
|
||
row-key="OrderHeaderID"
|
||
:rows="store.filteredOrders"
|
||
:columns="columns"
|
||
:loading="store.loading"
|
||
no-data-label="Sipariş bulunamadı"
|
||
:rows-per-page-options="[0]"
|
||
hide-bottom
|
||
>
|
||
<template #body-cell-IsCreditableConfirmed="props">
|
||
<q-td :props="props" class="text-center q-gutter-sm">
|
||
<q-btn
|
||
icon="picture_as_pdf"
|
||
color="red"
|
||
flat
|
||
round
|
||
dense
|
||
@click="printPDF(props.row)"
|
||
>
|
||
<q-tooltip>Siparişi PDF olarak aç</q-tooltip>
|
||
</q-btn>
|
||
|
||
<q-icon
|
||
:name="props.row.IsCreditableConfirmed ? 'check_circle' : 'cancel'"
|
||
:color="props.row.IsCreditableConfirmed ? 'green' : 'red'"
|
||
size="20px"
|
||
>
|
||
<q-tooltip>
|
||
{{ props.row.IsCreditableConfirmed ? 'Onaylı' : 'Onaysız' }}
|
||
</q-tooltip>
|
||
</q-icon>
|
||
</q-td>
|
||
</template>
|
||
|
||
<template #body-cell-OrderDate="props">
|
||
<q-td :props="props" class="text-center">
|
||
{{ formatDate(props.row.OrderDate) }}
|
||
</q-td>
|
||
</template>
|
||
|
||
<template #body-cell-CreditableConfirmedDate="props">
|
||
<q-td :props="props" class="text-center">
|
||
{{ formatDate(props.row.CreditableConfirmedDate) }}
|
||
</q-td>
|
||
</template>
|
||
|
||
<template #body-cell-PackedAmount="props">
|
||
<q-td :props="props" class="text-right text-weight-medium">
|
||
{{ Number(props.row.PackedAmount || 0).toLocaleString('tr-TR', { minimumFractionDigits: 2 }) }}
|
||
{{ props.row.DocCurrencyCode }}
|
||
</q-td>
|
||
</template>
|
||
|
||
<template #body-cell-PackedUSD="props">
|
||
<q-td :props="props" class="text-right text-weight-medium">
|
||
{{ Number(props.row.PackedUSD || 0).toLocaleString('tr-TR', { minimumFractionDigits: 2 }) }} USD
|
||
</q-td>
|
||
</template>
|
||
|
||
<template #body-cell-PackedRatePct="props">
|
||
<q-td
|
||
:props="props"
|
||
class="text-right text-weight-bold ol-pack-rate-cell"
|
||
:class="packRateClass(props.row.PackedRatePct)"
|
||
>
|
||
{{ Number(props.row.PackedRatePct || 0).toLocaleString('tr-TR', { minimumFractionDigits: 2 }) }} %
|
||
</q-td>
|
||
</template>
|
||
|
||
<template #body-cell-CurrAccDescription="props">
|
||
<q-td :props="props" class="ol-col-cari">
|
||
<div class="ol-col-multiline">{{ props.value }}</div>
|
||
<q-tooltip v-if="props.value">
|
||
{{ props.value }}
|
||
</q-tooltip>
|
||
</q-td>
|
||
</template>
|
||
|
||
<template #body-cell-MusteriTemsilcisi="props">
|
||
<q-td :props="props" class="ol-col-short">
|
||
<div class="ol-col-multiline">{{ props.value }}</div>
|
||
<q-tooltip v-if="props.value">
|
||
{{ props.value }}
|
||
</q-tooltip>
|
||
</q-td>
|
||
</template>
|
||
|
||
<template #body-cell-Piyasa="props">
|
||
<q-td :props="props" class="ol-col-short">
|
||
<div class="ol-col-multiline">{{ props.value }}</div>
|
||
<q-tooltip v-if="props.value">
|
||
{{ props.value }}
|
||
</q-tooltip>
|
||
</q-td>
|
||
</template>
|
||
|
||
<template #body-cell-Description="props">
|
||
<q-td :props="props" class="ol-col-desc">
|
||
<div class="ol-col-multiline">{{ props.value }}</div>
|
||
<q-tooltip v-if="props.value">
|
||
{{ props.value }}
|
||
</q-tooltip>
|
||
</q-td>
|
||
</template>
|
||
|
||
<template #body-cell-select="props">
|
||
<q-td :props="props" class="text-center">
|
||
<q-btn
|
||
icon="open_in_new"
|
||
color="primary"
|
||
flat
|
||
round
|
||
dense
|
||
@click="selectOrder(props.row)"
|
||
>
|
||
<q-tooltip>Siparişi Aç</q-tooltip>
|
||
</q-btn>
|
||
</q-td>
|
||
</template>
|
||
</q-table>
|
||
|
||
<q-banner v-if="store.error" class="bg-red text-white q-mt-sm">
|
||
Hata: {{ store.error }}
|
||
</q-banner>
|
||
</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 { onMounted, watch } from 'vue'
|
||
import { useRouter } from 'vue-router'
|
||
import { useQuasar } from 'quasar'
|
||
import { useOrderListStore } from 'src/stores/OrdernewListStore'
|
||
import { useAuthStore } from 'src/stores/authStore'
|
||
import { usePermission } from 'src/composables/usePermission'
|
||
|
||
const { canRead } = usePermission()
|
||
const canReadOrder = canRead('order')
|
||
|
||
const router = useRouter()
|
||
const $q = useQuasar()
|
||
const store = useOrderListStore()
|
||
|
||
let searchTimer = null
|
||
watch(
|
||
() => store.filters.search,
|
||
() => {
|
||
clearTimeout(searchTimer)
|
||
searchTimer = setTimeout(() => {
|
||
store.fetchOrders()
|
||
}, 400)
|
||
}
|
||
)
|
||
|
||
function exportExcel () {
|
||
const auth = useAuthStore()
|
||
|
||
if (!auth?.token) {
|
||
$q.notify({
|
||
type: 'negative',
|
||
message: 'Oturum bulunamadı',
|
||
position: 'top-right'
|
||
})
|
||
return
|
||
}
|
||
|
||
const params = new URLSearchParams({
|
||
search: store.filters.search || '',
|
||
CurrAccCode: store.filters.CurrAccCode || '',
|
||
OrderDate: store.filters.OrderDate || ''
|
||
})
|
||
|
||
const url = `http://localhost:8080/api/orders/export?${params.toString()}`
|
||
|
||
fetch(url, {
|
||
headers: {
|
||
Authorization: `Bearer ${auth.token}`
|
||
}
|
||
})
|
||
.then(res => res.blob())
|
||
.then(blob => {
|
||
const link = document.createElement('a')
|
||
link.href = URL.createObjectURL(blob)
|
||
link.download = 'siparis_listesi.xlsx'
|
||
link.click()
|
||
})
|
||
}
|
||
|
||
function formatDate (s) {
|
||
if (!s) return ''
|
||
const [y, m, d] = String(s).split('-')
|
||
if (!y || !m || !d) return s
|
||
return `${d}.${m}.${y}`
|
||
}
|
||
|
||
function packRateClass (value) {
|
||
const pct = Number(value || 0)
|
||
if (pct <= 50) return 'pack-rate-danger'
|
||
if (pct < 100) return 'pack-rate-warn'
|
||
return 'pack-rate-ok'
|
||
}
|
||
|
||
const columns = [
|
||
{ name: 'select', label: '', field: 'select', align: 'center', sortable: false },
|
||
{ name: 'OrderNumber', label: 'Sipariş No', field: 'OrderNumber', align: 'left', sortable: true, style: 'min-width:108px;white-space:nowrap', headerStyle: 'min-width:108px;white-space:nowrap' },
|
||
{ name: 'OrderDate', label: 'Tarih', field: 'OrderDate', align: 'center', sortable: true, style: 'min-width:82px;white-space:nowrap', headerStyle: 'min-width:82px;white-space:nowrap' },
|
||
{ name: 'CurrAccCode', label: 'Cari Kod', field: 'CurrAccCode', align: 'left', sortable: true, style: 'min-width:82px;white-space:nowrap', headerStyle: 'min-width:82px;white-space:nowrap' },
|
||
{ name: 'CurrAccDescription', label: 'Cari Adı', field: 'CurrAccDescription', align: 'left', sortable: true, classes: 'ol-col-cari', headerClasses: 'ol-col-cari', style: 'width:160px;max-width:160px', headerStyle: 'width:160px;max-width:160px' },
|
||
{ name: 'MusteriTemsilcisi', label: 'Temsilci', field: 'MusteriTemsilcisi', align: 'left', sortable: true, classes: 'ol-col-short', headerClasses: 'ol-col-short', style: 'width:88px;max-width:88px', headerStyle: 'width:88px;max-width:88px' },
|
||
{ name: 'Piyasa', label: 'Piyasa', field: 'Piyasa', align: 'left', sortable: true, classes: 'ol-col-short', headerClasses: 'ol-col-short', style: 'width:72px;max-width:72px', headerStyle: 'width:72px;max-width:72px' },
|
||
{ name: 'CreditableConfirmedDate', label: 'Onay', field: 'CreditableConfirmedDate', align: 'center', sortable: true, style: 'min-width:86px;white-space:nowrap', headerStyle: 'min-width:86px;white-space:nowrap' },
|
||
{ name: 'DocCurrencyCode', label: 'PB', field: 'DocCurrencyCode', align: 'center', sortable: true, style: 'min-width:46px;white-space:nowrap', headerStyle: 'min-width:46px;white-space:nowrap' },
|
||
{
|
||
name: 'TotalAmount',
|
||
label: 'Tutar',
|
||
field: 'TotalAmount',
|
||
align: 'right',
|
||
sortable: true,
|
||
style: 'min-width:120px;white-space:nowrap',
|
||
headerStyle: 'min-width:120px;white-space:nowrap',
|
||
format: (val, row) => Number(val || 0).toLocaleString('tr-TR', { minimumFractionDigits: 2 }) + ' ' + row.DocCurrencyCode
|
||
},
|
||
{
|
||
name: 'TotalAmountUSD',
|
||
label: 'Tutar (USD)',
|
||
field: 'TotalAmountUSD',
|
||
align: 'right',
|
||
sortable: true,
|
||
style: 'min-width:120px;white-space:nowrap',
|
||
headerStyle: 'min-width:120px;white-space:nowrap',
|
||
format: val => Number(val || 0).toLocaleString('tr-TR', { minimumFractionDigits: 2 }) + ' USD'
|
||
},
|
||
{
|
||
name: 'PackedAmount',
|
||
label: 'Paketlenen',
|
||
field: 'PackedAmount',
|
||
align: 'right',
|
||
sortable: true,
|
||
style: 'min-width:120px;white-space:nowrap',
|
||
headerStyle: 'min-width:120px;white-space:nowrap',
|
||
format: (val, row) => Number(val || 0).toLocaleString('tr-TR', { minimumFractionDigits: 2 }) + ' ' + row.DocCurrencyCode
|
||
},
|
||
{
|
||
name: 'PackedUSD',
|
||
label: 'Paketlenen (USD)',
|
||
field: 'PackedUSD',
|
||
align: 'right',
|
||
sortable: true,
|
||
style: 'min-width:120px;white-space:nowrap',
|
||
headerStyle: 'min-width:120px;white-space:nowrap',
|
||
format: val => Number(val || 0).toLocaleString('tr-TR', { minimumFractionDigits: 2 }) + ' USD'
|
||
},
|
||
{
|
||
name: 'PackedRatePct',
|
||
label: 'Paketlenme %',
|
||
field: 'PackedRatePct',
|
||
align: 'right',
|
||
sortable: true,
|
||
classes: 'ol-pack-rate-cell',
|
||
headerClasses: 'ol-pack-rate-cell',
|
||
style: 'min-width:96px;white-space:nowrap',
|
||
headerStyle: 'min-width:96px;white-space:nowrap',
|
||
format: val => Number(val || 0).toLocaleString('tr-TR', { minimumFractionDigits: 2 }) + ' %'
|
||
},
|
||
{ name: 'IsCreditableConfirmed', label: 'Durum', field: 'IsCreditableConfirmed', align: 'center', sortable: true },
|
||
{ name: 'Description', label: 'Açıklama', field: 'Description', align: 'left', sortable: false, classes: 'ol-col-desc', headerClasses: 'ol-col-desc', style: 'width:160px;max-width:160px', headerStyle: 'width:160px;max-width:160px' },
|
||
{ name: 'pdf', label: 'PDF', field: 'pdf', align: 'center', sortable: false }
|
||
]
|
||
|
||
function selectOrder (row) {
|
||
if (!row?.OrderHeaderID) {
|
||
$q.notify({ type: 'warning', message: 'OrderHeaderID bulunamadı' })
|
||
return
|
||
}
|
||
|
||
router.push({
|
||
name: 'order-edit',
|
||
params: { orderHeaderID: row.OrderHeaderID },
|
||
query: { mode: 'edit' }
|
||
})
|
||
}
|
||
|
||
async function printPDF (row) {
|
||
if (!row?.OrderHeaderID) return
|
||
|
||
const token = useAuthStore().token
|
||
const url = `http://localhost:8080/api/order/pdf/${row.OrderHeaderID}`
|
||
|
||
try {
|
||
const res = await fetch(url, {
|
||
headers: { Authorization: `Bearer ${token}` }
|
||
})
|
||
|
||
if (!res.ok) throw new Error()
|
||
|
||
const blob = await res.blob()
|
||
window.open(URL.createObjectURL(blob), '_blank')
|
||
} catch {
|
||
$q.notify({ type: 'negative', message: 'PDF yüklenemedi' })
|
||
}
|
||
}
|
||
|
||
function clearFilters () {
|
||
store.filters.search = ''
|
||
store.filters.CurrAccCode = ''
|
||
store.filters.OrderDate = ''
|
||
|
||
store.fetchOrders()
|
||
|
||
$q.notify({
|
||
type: 'info',
|
||
message: 'Filtreler temizlendi',
|
||
position: 'top-right'
|
||
})
|
||
}
|
||
|
||
onMounted(() => {
|
||
store.fetchOrders()
|
||
})
|
||
</script>
|
||
|
||
<style scoped>
|
||
.ol-page {
|
||
padding: 10px;
|
||
}
|
||
|
||
.ol-filter-bar {
|
||
margin-bottom: 8px;
|
||
}
|
||
|
||
.ol-filter-row {
|
||
display: flex;
|
||
flex-wrap: nowrap;
|
||
gap: 10px;
|
||
align-items: center;
|
||
}
|
||
|
||
.ol-filter-input {
|
||
min-width: 118px;
|
||
width: 136px;
|
||
flex: 0 0 136px;
|
||
}
|
||
|
||
.ol-search {
|
||
min-width: 240px;
|
||
max-width: 420px;
|
||
flex: 1 1 360px;
|
||
}
|
||
|
||
.ol-filter-actions {
|
||
display: flex;
|
||
gap: 8px;
|
||
flex-wrap: nowrap;
|
||
flex: 0 0 auto;
|
||
}
|
||
|
||
.ol-filter-total {
|
||
margin-left: auto;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: flex-end;
|
||
justify-content: center;
|
||
gap: 2px;
|
||
min-width: 250px;
|
||
line-height: 1.2;
|
||
flex: 0 0 auto;
|
||
align-self: center;
|
||
}
|
||
|
||
.ol-total-line {
|
||
display: flex;
|
||
align-items: baseline;
|
||
gap: 8px;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.ol-total-label {
|
||
font-size: 12px;
|
||
color: #4b5563;
|
||
}
|
||
|
||
.ol-total-value {
|
||
font-size: 13px;
|
||
}
|
||
|
||
.ol-table :deep(.q-table thead th) {
|
||
font-size: 11px;
|
||
padding: 4px 6px;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.ol-table :deep(.q-table tbody td) {
|
||
font-size: 11px;
|
||
padding: 3px 6px;
|
||
}
|
||
|
||
.ol-col-multiline {
|
||
display: block;
|
||
white-space: normal !important;
|
||
word-break: break-word;
|
||
line-height: 1.15;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
display: -webkit-box;
|
||
-webkit-box-orient: vertical;
|
||
-webkit-line-clamp: 2;
|
||
max-height: 2.35em;
|
||
}
|
||
|
||
.ol-col-cari {
|
||
max-width: 160px;
|
||
}
|
||
|
||
.ol-col-short {
|
||
max-width: 88px;
|
||
}
|
||
|
||
.ol-col-desc {
|
||
max-width: 160px;
|
||
}
|
||
|
||
.ol-pack-rate-cell {
|
||
font-weight: 700;
|
||
}
|
||
|
||
.pack-rate-danger {
|
||
color: #c62828;
|
||
}
|
||
|
||
.pack-rate-warn {
|
||
color: #8a6d00;
|
||
}
|
||
|
||
.pack-rate-ok {
|
||
color: #1f7a4f;
|
||
}
|
||
|
||
@media (max-width: 1440px) {
|
||
.ol-filter-row {
|
||
flex-wrap: wrap;
|
||
align-items: flex-start;
|
||
}
|
||
|
||
.ol-filter-actions {
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.ol-filter-input {
|
||
flex: 1 1 140px;
|
||
}
|
||
|
||
.ol-filter-total {
|
||
min-width: 100%;
|
||
margin-left: 0;
|
||
align-items: flex-start;
|
||
margin-top: 6px;
|
||
}
|
||
|
||
.ol-total-line {
|
||
width: 100%;
|
||
justify-content: space-between;
|
||
}
|
||
}
|
||
</style>
|