Compare commits

...

2 Commits

Author SHA1 Message Date
MEHMETKECECI
c888ef9b3c Merge remote-tracking branch 'origin/master'
# Conflicts:
#	ui/src/pages/OrderList.vue
2026-02-13 07:48:58 +03:00
MEHMETKECECI
7f56bb40c5 Merge remote-tracking branch 'origin/master' 2026-02-13 07:48:17 +03:00
2 changed files with 62 additions and 188 deletions

View File

@@ -1,12 +1,12 @@
<template> <template>
<q-page v-if="canReadOrder" class="ol-page"> <q-page class="ol-page">
<!-- 🍠Sticky Filter --> <!-- 🔍 Sticky Filter -->
<div class="ol-filter-bar"> <div class="ol-filter-bar">
<!-- 🹠TEK SATIR FLEX --> <!-- 🔹 TEK SATIR FLEX -->
<div class="ol-filter-row"> <div class="ol-filter-row">
<!-- 🍠Arama --> <!-- 🔍 Arama -->
<q-input <q-input
class="ol-filter-input ol-search" class="ol-filter-input ol-search"
dense dense
@@ -21,7 +21,7 @@
</template> </template>
</q-input> </q-input>
<!-- 🧾 Cari Kodu --> <!-- 🧾 Cari Kodu -->
<q-input <q-input
class="ol-filter-input" class="ol-filter-input"
dense dense
@@ -31,7 +31,7 @@
clearable clearable
/> />
<!-- ğŸ Sipariş Tarihi --> <!-- 📅 Sipariş Tarihi -->
<q-input <q-input
class="ol-filter-input" class="ol-filter-input"
dense dense
@@ -41,11 +41,10 @@
type="date" type="date"
/> />
<!-- 😠Butonlar --> <!-- 🔘 Butonlar -->
<div class="ol-filter-actions"> <div class="ol-filter-actions">
<q-btn <q-btn
v-if="canReadOrder"
label="Temizle" label="Temizle"
icon="clear" icon="clear"
color="grey-7" color="grey-7"
@@ -59,7 +58,6 @@
</q-btn> </q-btn>
<q-btn <q-btn
v-if="canReadOrder"
label="Yenile" label="Yenile"
color="primary" color="primary"
icon="refresh" icon="refresh"
@@ -68,7 +66,6 @@
/> />
<q-btn <q-btn
v-if="canExportOrder"
label="Excel'e Aktar" label="Excel'e Aktar"
icon="download" icon="download"
color="primary" color="primary"
@@ -80,7 +77,7 @@
</div> </div>
<!-- 🰠Toplam --> <!-- 💰 Toplam -->
<div class="ol-filter-total"> <div class="ol-filter-total">
Toplam Görünen Sipariş Tutarı (USD): Toplam Görünen Sipariş Tutarı (USD):
<strong> <strong>
@@ -94,7 +91,7 @@
</div> </div>
<!-- ğŸ ORDER LIST TABLE --> <!-- 📋 ORDER LIST TABLE -->
<q-table <q-table
title="Mevcut Siparişler" title="Mevcut Siparişler"
class="ol-table" class="ol-table"
@@ -106,18 +103,16 @@
:rows="store.filteredOrders" :rows="store.filteredOrders"
:columns="columns" :columns="columns"
:loading="store.loading" :loading="store.loading"
:table-style="tableStyle"
no-data-label="Sipariş bulunamadı" no-data-label="Sipariş bulunamadı"
:rows-per-page-options="[0]" :rows-per-page-options="[0]"
hide-bottom hide-bottom
> >
<!-- ğŸ PDF + DURUM --> <!-- 📄 PDF + DURUM -->
<template #body-cell-IsCreditableConfirmed="props"> <template #body-cell-IsCreditableConfirmed="props">
<q-td :props="props" class="text-center q-gutter-sm"> <q-td :props="props" class="text-center q-gutter-sm">
<q-btn <q-btn
v-if="canExportOrder"
icon="picture_as_pdf" icon="picture_as_pdf"
color="red" color="red"
flat flat
@@ -141,7 +136,7 @@
</q-td> </q-td>
</template> </template>
<!-- ğŸ Tarih --> <!-- 📅 Tarih -->
<template #body-cell-OrderDate="props"> <template #body-cell-OrderDate="props">
<q-td :props="props" class="text-center"> <q-td :props="props" class="text-center">
{{ formatDate(props.row.OrderDate) }} {{ formatDate(props.row.OrderDate) }}
@@ -154,7 +149,7 @@
</q-td> </q-td>
</template> </template>
<!-- 🧾 Cari Adı â 2 Satır --> <!-- 🧾 Cari Adı 2 Satır -->
<template #body-cell-CurrAccDescription="props"> <template #body-cell-CurrAccDescription="props">
<q-td :props="props" class="ol-col-cari ol-col-multiline"> <q-td :props="props" class="ol-col-cari ol-col-multiline">
{{ props.value }} {{ props.value }}
@@ -164,7 +159,7 @@
</q-td> </q-td>
</template> </template>
<!-- 🝠ıklama â 5 Satır --> <!-- 📝 ıklama 5 Satır -->
<template #body-cell-Description="props"> <template #body-cell-Description="props">
<q-td :props="props" class="ol-col-desc ol-col-multiline"> <q-td :props="props" class="ol-col-desc ol-col-multiline">
{{ props.value }} {{ props.value }}
@@ -174,11 +169,10 @@
</q-td> </q-td>
</template> </template>
<!-- ğŸ aç --> <!-- 🔗 Aç -->
<template #body-cell-select="props"> <template #body-cell-select="props">
<q-td :props="props" class="text-center"> <q-td :props="props" class="text-center">
<q-btn <q-btn
v-if="canUpdateOrder"
icon="open_in_new" icon="open_in_new"
color="primary" color="primary"
flat flat
@@ -186,7 +180,7 @@
dense dense
@click="selectOrder(props.row)" @click="selectOrder(props.row)"
> >
<q-tooltip>Siparişi aç</q-tooltip> <q-tooltip>Siparişi Aç</q-tooltip>
</q-btn> </q-btn>
</q-td> </q-td>
</template> </template>
@@ -199,16 +193,10 @@
</q-banner> </q-banner>
</q-page> </q-page>
<q-page v-else class="q-pa-md flex flex-center">
<div class="text-negative text-subtitle1">
Bu module erisim yetkiniz yok.
</div>
</q-page>
</template> </template>
<script setup> <script setup>
import { computed, onMounted, watch } from 'vue' import { onMounted, watch } from 'vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { useQuasar } from 'quasar' import { useQuasar } from 'quasar'
@@ -216,12 +204,11 @@ import { useOrderListStore } from 'src/stores/OrdernewListStore'
import { useAuthStore } from 'src/stores/authStore' import { useAuthStore } from 'src/stores/authStore'
import { usePermission } from 'src/composables/usePermission' import { usePermission } from 'src/composables/usePermission'
const { canRead, canWrite, canUpdate, canExport } = usePermission() const { canRead, canWrite, canUpdate } = usePermission()
const canReadOrder = canRead('order') const canReadOrder = canRead('order')
const canWriteOrder = canWrite('order') const canWriteOrder = canWrite('order')
const canUpdateOrder = canUpdate('order') const canUpdateOrder = canUpdate('order')
const canExportOrder = canExport('order')
/* ========================= /* =========================
INIT INIT
@@ -230,13 +217,9 @@ const canExportOrder = canExport('order')
const router = useRouter() const router = useRouter()
const $q = useQuasar() const $q = useQuasar()
// ⚠️ ÖNCE store tanımlanır // ⚠️ ÖNCE store tanımlanır
const store = useOrderListStore() const store = useOrderListStore()
const tableStyle = computed(() => ({
minWidth: $q.screen.lt.lg ? '1280px' : '100%'
}))
/* ========================= /* =========================
SEARCH DEBOUNCE SEARCH DEBOUNCE
========================= */ ========================= */
@@ -258,15 +241,14 @@ watch(
HELPERS HELPERS
========================= */ ========================= */
function exportExcel () { function exportExcel () {
if (!canExportOrder.value) {
$q.notify({ type: 'negative', message: 'Excel export yetkiniz yok', position: 'top-right' })
return
}
const auth = useAuthStore() const auth = useAuthStore()
if (!auth?.token) { if (!auth?.token) {
$q.notify({ type: 'negative', message: 'Oturum bulunamadı', position: 'top-right' }) $q.notify({
type: 'negative',
message: 'Oturum bulunamadı',
position: 'top-right'
})
return return
} }
@@ -279,43 +261,19 @@ function exportExcel () {
const url = `http://localhost:8080/api/orders/export?${params.toString()}` const url = `http://localhost:8080/api/orders/export?${params.toString()}`
fetch(url, { fetch(url, {
method: 'GET', headers: {
headers: { Authorization: `Bearer ${auth.token}` } Authorization: `Bearer ${auth.token}`
}
}) })
.then(async res => { .then(res => res.blob())
// ✅ 200 değilse EXCEL İNDİRME
if (!res.ok) {
const text = await res.text()
throw new Error(`HTTP ${res.status} - ${text}`)
}
// ✅ Content-Type kontrol (debug için)
const ct = res.headers.get('content-type') || ''
if (!ct.includes('spreadsheetml.sheet')) {
const text = await res.text()
throw new Error(`Beklenmeyen Content-Type: ${ct} | Body: ${text}`)
}
return res.blob()
})
.then(blob => { .then(blob => {
const link = document.createElement('a') const link = document.createElement('a')
link.href = URL.createObjectURL(blob) link.href = URL.createObjectURL(blob)
link.download = 'siparis_listesi.xlsx' link.download = 'siparis_listesi.xlsx'
link.click() link.click()
}) })
.catch(err => {
$q.notify({
type: 'negative',
message: 'Excel export hatası: ' + (err?.message || err),
position: 'top-right',
timeout: 8000
})
console.error(err)
})
} }
function formatDate (s) { function formatDate (s) {
if (!s) return '' if (!s) return ''
const [y, m, d] = s.split('-') const [y, m, d] = s.split('-')
@@ -327,18 +285,12 @@ function formatDate (s) {
========================= */ ========================= */
const columns = [ const columns = [
{ { name: 'select', label: '', field: 'select', align: 'center', sortable: false },
name: 'select',
label: '', { name: 'OrderNumber', label: 'Sipariş No', field: 'OrderNumber', align: 'left', sortable: true },
field: 'select', { name: 'OrderDate', label: 'Tarih', field: 'OrderDate', align: 'center', sortable: true },
align: 'center',
sortable: false, { name: 'CurrAccCode', label: 'Cari Kod', field: 'CurrAccCode', align: 'left', sortable: true },
style: 'width:42px; min-width:42px; max-width:42px; padding:2px 4px;',
headerStyle: 'width:42px; min-width:42px; max-width:42px; padding:2px 4px;'
},
{ name: 'OrderNumber', label: 'Sipariş No', field: 'OrderNumber', align: 'left', sortable: true, style: 'width:108px; min-width:108px;', headerStyle: 'width:108px; min-width:108px;' },
{ name: 'OrderDate', label: 'Tarih', field: 'OrderDate', align: 'center', sortable: true, style: 'width:78px; min-width:78px;', headerStyle: 'width:78px; min-width:78px;' },
{ name: 'CurrAccCode', label: 'Cari Kod', field: 'CurrAccCode', align: 'left', sortable: true, style: 'width:78px; min-width:78px;', headerStyle: 'width:78px; min-width:78px;' },
{ {
name: 'CurrAccDescription', name: 'CurrAccDescription',
@@ -348,13 +300,14 @@ const columns = [
sortable: true, sortable: true,
classes: 'ol-col-cari', classes: 'ol-col-cari',
headerClasses: 'ol-col-cari', headerClasses: 'ol-col-cari',
style: 'width:130px; min-width:130px; max-width:130px;', style: 'max-width:200px'
headerStyle: 'width:130px; min-width:130px; max-width:130px;'
}, },
{ name: 'MusteriTemsilcisi', label: 'Temsilci', field: 'MusteriTemsilcisi', align: 'left', sortable: true, style: 'width:78px; min-width:78px;', headerStyle: 'width:78px; min-width:78px;' },
{ name: 'Piyasa', label: 'Piyasa', field: 'Piyasa', align: 'left', sortable: true, style: 'width:74px; min-width:74px;', headerStyle: 'width:74px; min-width:74px;' }, { name: 'MusteriTemsilcisi', label: 'Temsilci', field: 'MusteriTemsilcisi', align: 'left', sortable: true },
{ name: 'CreditableConfirmedDate', label: 'Onay', field: 'CreditableConfirmedDate', align: 'center', sortable: true, style: 'width:82px; min-width:82px;', headerStyle: 'width:82px; min-width:82px;' }, { name: 'Piyasa', label: 'Piyasa', field: 'Piyasa', align: 'left', sortable: true },
{ name: 'DocCurrencyCode', label: 'PB', field: 'DocCurrencyCode', align: 'center', sortable: true, style: 'width:46px; min-width:46px;', headerStyle: 'width:46px; min-width:46px;' },
{ name: 'CreditableConfirmedDate', label: 'Onay', field: 'CreditableConfirmedDate', align: 'center', sortable: true },
{ name: 'DocCurrencyCode', label: 'PB', field: 'DocCurrencyCode', align: 'center', sortable: true },
{ {
name: 'TotalAmount', name: 'TotalAmount',
@@ -362,8 +315,6 @@ const columns = [
field: 'TotalAmount', field: 'TotalAmount',
align: 'right', align: 'right',
sortable: true, sortable: true,
style: 'width:94px; min-width:94px;',
headerStyle: 'width:94px; min-width:94px;',
format: (val, row) => format: (val, row) =>
Number(val || 0).toLocaleString('tr-TR', { minimumFractionDigits: 2 }) + Number(val || 0).toLocaleString('tr-TR', { minimumFractionDigits: 2 }) +
' ' + row.DocCurrencyCode ' ' + row.DocCurrencyCode
@@ -375,49 +326,11 @@ const columns = [
field: 'TotalAmountUSD', field: 'TotalAmountUSD',
align: 'right', align: 'right',
sortable: true, sortable: true,
style: 'width:96px; min-width:96px;',
headerStyle: 'width:96px; min-width:96px;',
format: val => format: val =>
Number(val || 0).toLocaleString('tr-TR', { minimumFractionDigits: 2 }) + ' USD' Number(val || 0).toLocaleString('tr-TR', { minimumFractionDigits: 2 }) + ' USD'
}, },
{ { name: 'IsCreditableConfirmed', label: 'Durum', field: 'IsCreditableConfirmed', align: 'center', sortable: true },
name: 'PackedAmount',
label: 'Paket Tutar',
field: 'PackedAmount',
align: 'right',
sortable: true,
style: 'width:98px; min-width:98px;',
headerStyle: 'width:98px; min-width:98px;',
format: (val, row) =>
Number(val || 0).toLocaleString('tr-TR', { minimumFractionDigits: 2 }) +
' ' + (row.DocCurrencyCode || '')
},
{
name: 'PackedUSD',
label: 'Paket USD',
field: 'PackedUSD',
align: 'right',
sortable: true,
style: 'width:96px; min-width:96px;',
headerStyle: 'width:96px; min-width:96px;',
format: val =>
Number(val || 0).toLocaleString('tr-TR', { minimumFractionDigits: 2 }) + ' USD'
},
{
name: 'PackedRatePct',
label: 'Paket %',
field: 'PackedRatePct',
align: 'right',
sortable: true,
style: 'width:72px; min-width:72px;',
headerStyle: 'width:72px; min-width:72px;',
format: val =>
Number(val || 0).toLocaleString('tr-TR', { minimumFractionDigits: 2 }) + ' %'
},
{ name: 'IsCreditableConfirmed', label: 'Durum', field: 'IsCreditableConfirmed', align: 'center', sortable: true, style: 'width:66px; min-width:66px;', headerStyle: 'width:66px; min-width:66px;' },
{ {
name: 'Description', name: 'Description',
@@ -427,10 +340,10 @@ const columns = [
sortable: false, sortable: false,
classes: 'ol-col-desc', classes: 'ol-col-desc',
headerClasses: 'ol-col-desc', headerClasses: 'ol-col-desc',
style: 'width:130px; min-width:130px; max-width:130px;', style: 'max-width:220px'
headerStyle: 'width:130px; min-width:130px; max-width:130px;'
}, },
{ name: 'pdf', label: 'PDF', field: 'pdf', align: 'center', sortable: false, style: 'width:44px; min-width:44px;', headerStyle: 'width:44px; min-width:44px;' }
{ name: 'pdf', label: 'PDF', field: 'pdf', align: 'center', sortable: false }
] ]
/* ========================= /* =========================
@@ -438,11 +351,6 @@ const columns = [
========================= */ ========================= */
function selectOrder (row) { function selectOrder (row) {
if (!canUpdateOrder.value) {
$q.notify({ type: 'negative', message: 'Siparis guncelleme yetkiniz yok' })
return
}
if (!row?.OrderHeaderID) { if (!row?.OrderHeaderID) {
$q.notify({ type: 'warning', message: 'OrderHeaderID bulunamadı' }) $q.notify({ type: 'warning', message: 'OrderHeaderID bulunamadı' })
return return
@@ -456,11 +364,6 @@ function selectOrder (row) {
} }
async function printPDF (row) { async function printPDF (row) {
if (!canExportOrder.value) {
$q.notify({ type: 'negative', message: 'PDF export yetkiniz yok' })
return
}
if (!row?.OrderHeaderID) return if (!row?.OrderHeaderID) return
const token = useAuthStore().token const token = useAuthStore().token
@@ -499,48 +402,7 @@ function clearFilters () {
========================= */ ========================= */
onMounted(() => { onMounted(() => {
if (canReadOrder.value) { store.fetchOrders()
store.fetchOrders()
}
}) })
</script> </script>
<style scoped>
.ol-page {
padding: 8px;
}
.ol-table :deep(.q-table thead th) {
font-size: 11px;
line-height: 1.1;
font-weight: 700;
padding: 3px 6px;
white-space: nowrap;
}
.ol-table :deep(.q-table tbody td) {
font-size: 11px;
line-height: 1.15;
padding: 2px 6px;
white-space: nowrap;
}
.ol-col-multiline {
white-space: normal !important;
word-break: break-word;
line-height: 1.1;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
@media (max-width: 1600px) {
.ol-table :deep(.q-table thead th),
.ol-table :deep(.q-table tbody td) {
font-size: 10px;
padding: 2px 4px;
}
}
</style>

View File

@@ -34,11 +34,23 @@ export const useOrderListStore = defineStore('orderlist', {
return result return result
}, },
totalVisibleUSD (state) { totalVisibleUSD () {
return state.filteredOrders.reduce( return this.filteredOrders.reduce((sum, o) => {
(sum, o) => sum + Number(o.TotalAmountUSD || 0), const value = Number(o.TotalAmountUSD || 0)
0 return sum + (Number.isFinite(value) ? value : 0)
) }, 0)
},
totalPackedVisibleUSD () {
return this.filteredOrders.reduce((sum, o) => {
const value = Number(o.PackedUSD || 0)
return sum + (Number.isFinite(value) ? value : 0)
}, 0)
},
packedVisibleRatePct () {
if (!this.totalVisibleUSD) return 0
return (this.totalPackedVisibleUSD / this.totalVisibleUSD) * 100
} }
}, },