Merge remote-tracking branch 'origin/master'
This commit is contained in:
125
ui/quasar.config.js.temporary.compiled.1771513237059.mjs
Normal file
125
ui/quasar.config.js.temporary.compiled.1771513237059.mjs
Normal file
@@ -0,0 +1,125 @@
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* THIS FILE IS GENERATED AUTOMATICALLY.
|
||||
* 1. DO NOT edit this file directly as it won't do anything.
|
||||
* 2. EDIT the original quasar.config file INSTEAD.
|
||||
* 3. DO NOT git commit this file. It should be ignored.
|
||||
*
|
||||
* This file is still here because there was an error in
|
||||
* the original quasar.config file and this allows you to
|
||||
* investigate the Node.js stack error.
|
||||
*
|
||||
* After you fix the original file, this file will be
|
||||
* deleted automatically.
|
||||
**/
|
||||
|
||||
|
||||
// quasar.config.js
|
||||
import { defineConfig } from "@quasar/app-webpack/wrappers";
|
||||
var quasar_config_default = defineConfig(() => {
|
||||
const apiBaseUrl = (process.env.VITE_API_BASE_URL || "/api").trim();
|
||||
return {
|
||||
/* =====================================================
|
||||
APP INFO
|
||||
===================================================== */
|
||||
productName: "Baggi BSS",
|
||||
productDescription: "Baggi Tekstil Business Support System",
|
||||
/* =====================================================
|
||||
BOOT FILES
|
||||
===================================================== */
|
||||
boot: ["dayjs"],
|
||||
/* =====================================================
|
||||
GLOBAL CSS
|
||||
===================================================== */
|
||||
css: ["app.css"],
|
||||
/* =====================================================
|
||||
ICONS / FONTS
|
||||
===================================================== */
|
||||
extras: [
|
||||
"roboto-font",
|
||||
"material-icons"
|
||||
],
|
||||
/* =====================================================
|
||||
BUILD (PRODUCTION)
|
||||
===================================================== */
|
||||
build: {
|
||||
vueRouterMode: "hash",
|
||||
env: {
|
||||
VITE_API_BASE_URL: apiBaseUrl
|
||||
},
|
||||
esbuildTarget: {
|
||||
browser: ["es2022", "firefox115", "chrome115", "safari14"],
|
||||
node: "node20"
|
||||
},
|
||||
// Cache & performance
|
||||
gzip: true,
|
||||
preloadChunks: true
|
||||
},
|
||||
/* =====================================================
|
||||
DEV SERVER (LOCAL)
|
||||
===================================================== */
|
||||
devServer: {
|
||||
server: { type: "http" },
|
||||
port: 9e3,
|
||||
open: true,
|
||||
// DEV proxy (CORS'suz)
|
||||
proxy: [
|
||||
{
|
||||
context: ["/api"],
|
||||
target: "http://localhost:8080",
|
||||
changeOrigin: true,
|
||||
secure: false
|
||||
}
|
||||
]
|
||||
},
|
||||
/* =====================================================
|
||||
QUASAR FRAMEWORK
|
||||
===================================================== */
|
||||
framework: {
|
||||
config: {
|
||||
notify: {
|
||||
position: "top",
|
||||
timeout: 2500
|
||||
}
|
||||
},
|
||||
lang: "tr",
|
||||
plugins: [
|
||||
"Loading",
|
||||
"Dialog",
|
||||
"Notify"
|
||||
]
|
||||
},
|
||||
animations: [],
|
||||
/* =====================================================
|
||||
SSR / PWA (DISABLED)
|
||||
===================================================== */
|
||||
ssr: {
|
||||
prodPort: 3e3,
|
||||
middlewares: ["render"],
|
||||
pwa: false
|
||||
},
|
||||
pwa: {
|
||||
workboxMode: "GenerateSW"
|
||||
},
|
||||
/* =====================================================
|
||||
MOBILE / DESKTOP
|
||||
===================================================== */
|
||||
capacitor: {
|
||||
hideSplashscreen: true
|
||||
},
|
||||
electron: {
|
||||
preloadScripts: ["electron-preload"],
|
||||
inspectPort: 5858,
|
||||
bundler: "packager",
|
||||
builder: {
|
||||
appId: "baggisowtfaresystem"
|
||||
}
|
||||
},
|
||||
bex: {
|
||||
extraScripts: []
|
||||
}
|
||||
};
|
||||
});
|
||||
export {
|
||||
quasar_config_default as default
|
||||
};
|
||||
57
ui/src/pages/OrderProductionUpdate.vue
Normal file
57
ui/src/pages/OrderProductionUpdate.vue
Normal file
@@ -0,0 +1,57 @@
|
||||
<template>
|
||||
<q-page class="q-pa-md">
|
||||
<div class="text-h6 text-weight-bold">Üretime Verilen Ürünler</div>
|
||||
<div class="text-caption text-grey-7 q-mt-xs">
|
||||
OrderHeaderID: {{ orderHeaderID || '-' }}
|
||||
</div>
|
||||
|
||||
<q-table
|
||||
class="q-mt-md"
|
||||
flat
|
||||
bordered
|
||||
dense
|
||||
separator="cell"
|
||||
row-key="OrderLineID"
|
||||
:rows="store.items"
|
||||
:columns="columns"
|
||||
:loading="store.loading"
|
||||
no-data-label="Üretime verilecek ürün bulunamadı"
|
||||
:rows-per-page-options="[0]"
|
||||
hide-bottom
|
||||
/>
|
||||
|
||||
<q-banner v-if="store.error" class="bg-red text-white q-mt-sm">
|
||||
Hata: {{ store.error }}
|
||||
</q-banner>
|
||||
</q-page>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, onMounted, watch } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { useOrderProductionItemStore } from 'src/stores/OrderProductionItemStore'
|
||||
|
||||
const route = useRoute()
|
||||
const store = useOrderProductionItemStore()
|
||||
|
||||
const orderHeaderID = computed(() => String(route.params.orderHeaderID || '').trim())
|
||||
|
||||
const columns = [
|
||||
{ name: 'OldItemCode', label: 'Eski Ürün Kodu', field: 'OldItemCode', align: 'left', sortable: true, style: 'min-width:140px;white-space:nowrap', headerStyle: 'min-width:140px;white-space:nowrap' },
|
||||
{ name: 'OldColor', label: 'Eski Ürün Rengi', field: 'OldColor', align: 'left', sortable: true, style: 'min-width:120px;white-space:nowrap', headerStyle: 'min-width:120px;white-space:nowrap' },
|
||||
{ name: 'OldDim2', label: 'Eski 2. Renk', field: 'OldDim2', align: 'left', sortable: true, style: 'min-width:110px;white-space:nowrap', headerStyle: 'min-width:110px;white-space:nowrap' },
|
||||
{ name: 'OldDesc', label: 'Eski Açıklama', field: 'OldDesc', align: 'left', sortable: false, style: 'min-width:180px;white-space:nowrap', headerStyle: 'min-width:180px;white-space:nowrap' },
|
||||
{ name: 'NewItemCode', label: 'Yeni Ürün Kodu', field: 'NewItemCode', align: 'left', sortable: true, style: 'min-width:140px;white-space:nowrap', headerStyle: 'min-width:140px;white-space:nowrap' },
|
||||
{ name: 'NewColor', label: 'Yeni Ürün Rengi', field: 'NewColor', align: 'left', sortable: true, style: 'min-width:120px;white-space:nowrap', headerStyle: 'min-width:120px;white-space:nowrap' },
|
||||
{ name: 'NewDim2', label: 'Yeni 2. Renk', field: 'NewDim2', align: 'left', sortable: true, style: 'min-width:110px;white-space:nowrap', headerStyle: 'min-width:110px;white-space:nowrap' },
|
||||
{ name: 'NewDesc', label: 'Yeni Açıklama', field: 'NewDesc', align: 'left', sortable: false, style: 'min-width:180px;white-space:nowrap', headerStyle: 'min-width:180px;white-space:nowrap' }
|
||||
]
|
||||
|
||||
onMounted(() => {
|
||||
store.fetchItems(orderHeaderID.value)
|
||||
})
|
||||
|
||||
watch(orderHeaderID, (id) => {
|
||||
store.fetchItems(id)
|
||||
})
|
||||
</script>
|
||||
357
ui/src/pages/OrderProductionUpdateList.vue
Normal file
357
ui/src/pages/OrderProductionUpdateList.vue
Normal file
@@ -0,0 +1,357 @@
|
||||
<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 || productionOrders.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">
|
||||
{{ totalVisibleUSD.toLocaleString('tr-TR', { minimumFractionDigits: 2, maximumFractionDigits: 2 }) }}
|
||||
</strong>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<q-table
|
||||
title="Üretime Verilecek Ürünleri Olan Siparişler"
|
||||
class="ol-table"
|
||||
flat
|
||||
bordered
|
||||
dense
|
||||
separator="cell"
|
||||
row-key="OrderHeaderID"
|
||||
:rows="productionOrders"
|
||||
:columns="columns"
|
||||
:loading="store.loading"
|
||||
no-data-label="Üretime verilecek sipariş bulunamadı"
|
||||
:rows-per-page-options="[0]"
|
||||
hide-bottom
|
||||
>
|
||||
<template #body-cell-open="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>
|
||||
|
||||
<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-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-HasUretimUrunu="props">
|
||||
<q-td :props="props" class="text-center">
|
||||
<q-icon
|
||||
:name="props.row.HasUretimUrunu ? 'check_circle' : 'cancel'"
|
||||
:color="props.row.HasUretimUrunu ? 'green' : 'grey-5'"
|
||||
size="18px"
|
||||
/>
|
||||
</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 { computed, onMounted, watch } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useQuasar } from 'quasar'
|
||||
import { useOrderProductionUpdateStore } from 'src/stores/OrderProductionUpdateStore'
|
||||
import { useAuthStore } from 'src/stores/authStore'
|
||||
import { usePermission } from 'src/composables/usePermission'
|
||||
import api, { extractApiErrorDetail } from 'src/services/api'
|
||||
|
||||
const { canRead } = usePermission()
|
||||
const canReadOrder = canRead('order')
|
||||
|
||||
const router = useRouter()
|
||||
const $q = useQuasar()
|
||||
const store = useOrderProductionUpdateStore()
|
||||
|
||||
let searchTimer = null
|
||||
watch(
|
||||
() => store.filters.search,
|
||||
() => {
|
||||
clearTimeout(searchTimer)
|
||||
searchTimer = setTimeout(() => {
|
||||
store.fetchOrders()
|
||||
}, 400)
|
||||
}
|
||||
)
|
||||
|
||||
const productionOrders = computed(() => store.filteredOrders)
|
||||
|
||||
const totalVisibleUSD = computed(() =>
|
||||
productionOrders.value.reduce((sum, o) => {
|
||||
const v = Number(o.TotalAmountUSD || 0)
|
||||
return sum + (Number.isFinite(v) ? v : 0)
|
||||
}, 0)
|
||||
)
|
||||
|
||||
function clearFilters () {
|
||||
store.filters.search = ''
|
||||
store.filters.CurrAccCode = ''
|
||||
store.filters.OrderDate = ''
|
||||
store.fetchOrders()
|
||||
}
|
||||
|
||||
function formatDate (v) {
|
||||
if (!v) return ''
|
||||
return String(v)
|
||||
}
|
||||
|
||||
function selectOrder (row) {
|
||||
if (!row?.OrderHeaderID) {
|
||||
$q.notify({ type: 'warning', message: 'OrderHeaderID bulunamadı' })
|
||||
return
|
||||
}
|
||||
|
||||
router.push({
|
||||
name: 'orderproductionupdate',
|
||||
params: { orderHeaderID: row.OrderHeaderID }
|
||||
})
|
||||
}
|
||||
|
||||
async function printPDF (row) {
|
||||
try {
|
||||
const auth = useAuthStore()
|
||||
if (!auth?.token) {
|
||||
$q.notify({ type: 'warning', message: 'Oturum bulunamadı' })
|
||||
return
|
||||
}
|
||||
|
||||
const res = await api.get(`/order/pdf/${row.OrderHeaderID}`, {
|
||||
responseType: 'blob'
|
||||
})
|
||||
|
||||
const blob = new Blob([res.data], { type: 'application/pdf' })
|
||||
const url = URL.createObjectURL(blob)
|
||||
window.open(url, '_blank')
|
||||
setTimeout(() => URL.revokeObjectURL(url), 2000)
|
||||
} catch (err) {
|
||||
const status = err?.response?.status
|
||||
const detail = extractApiErrorDetail(err)
|
||||
console.error(`PDF load error [${status}] /order/pdf/${row?.OrderHeaderID}: ${detail}`)
|
||||
$q.notify({ type: 'negative', message: `PDF alınamadı: ${detail}` })
|
||||
}
|
||||
}
|
||||
|
||||
function exportExcel () {
|
||||
const auth = useAuthStore()
|
||||
|
||||
if (!auth?.token) {
|
||||
$q.notify({
|
||||
type: 'negative',
|
||||
message: 'Oturum bulunamadı'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
const params = new URLSearchParams()
|
||||
if (store.filters.search) params.append('search', store.filters.search)
|
||||
|
||||
api.get(`/orders/production-list?${params.toString()}`, {
|
||||
responseType: 'blob'
|
||||
}).then((res) => {
|
||||
const blob = new Blob([res.data], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' })
|
||||
const url = URL.createObjectURL(blob)
|
||||
const a = document.createElement('a')
|
||||
a.href = url
|
||||
a.download = 'uretime_verilecek_siparisler.xlsx'
|
||||
a.click()
|
||||
URL.revokeObjectURL(url)
|
||||
}).catch((err) => {
|
||||
const detail = extractApiErrorDetail(err)
|
||||
$q.notify({ type: 'negative', message: `Excel alınamadı: ${detail}` })
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
store.fetchOrders()
|
||||
})
|
||||
|
||||
const columns = [
|
||||
{ name: 'open', label: '', field: 'open', 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: 'IsCreditableConfirmed', label: 'Durum', field: 'IsCreditableConfirmed', align: 'center', sortable: true },
|
||||
{ name: 'HasUretimUrunu', label: 'Üretim', field: 'HasUretimUrunu', align: 'left', sortable: true, style: 'min-width:190px;white-space:nowrap', headerStyle: 'min-width:190px;white-space:nowrap' },
|
||||
{ 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' }
|
||||
]
|
||||
</script>
|
||||
34
ui/src/stores/OrderProductionItemStore.js
Normal file
34
ui/src/stores/OrderProductionItemStore.js
Normal file
@@ -0,0 +1,34 @@
|
||||
// src/stores/OrderProductionItemStore.js
|
||||
import { defineStore } from 'pinia'
|
||||
import api from 'src/services/api'
|
||||
|
||||
export const useOrderProductionItemStore = defineStore('orderproductionitems', {
|
||||
state: () => ({
|
||||
items: [],
|
||||
loading: false,
|
||||
error: null
|
||||
}),
|
||||
|
||||
actions: {
|
||||
async fetchItems (orderHeaderID) {
|
||||
if (!orderHeaderID) {
|
||||
this.items = []
|
||||
return
|
||||
}
|
||||
|
||||
this.loading = true
|
||||
this.error = null
|
||||
|
||||
try {
|
||||
const res = await api.get(`/orders/production-items/${encodeURIComponent(orderHeaderID)}`)
|
||||
const data = res?.data
|
||||
this.items = Array.isArray(data) ? data : []
|
||||
} catch (err) {
|
||||
this.items = []
|
||||
this.error = err?.response?.data || err?.message || 'Liste alınamadı'
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
166
ui/src/stores/OrderProductionUpdateStore.js
Normal file
166
ui/src/stores/OrderProductionUpdateStore.js
Normal file
@@ -0,0 +1,166 @@
|
||||
// src/stores/OrderProductionUpdateStore.js
|
||||
import { defineStore } from 'pinia'
|
||||
import api from 'src/services/api'
|
||||
|
||||
let lastRequestId = 0
|
||||
|
||||
export const useOrderProductionUpdateStore = defineStore('orderproductionupdate', {
|
||||
state: () => ({
|
||||
orders: [],
|
||||
loading: false,
|
||||
error: null,
|
||||
|
||||
filters: {
|
||||
search: '',
|
||||
CurrAccCode: '',
|
||||
OrderDate: ''
|
||||
}
|
||||
}),
|
||||
|
||||
getters: {
|
||||
filteredOrders (state) {
|
||||
let result = state.orders
|
||||
|
||||
if (state.filters.CurrAccCode) {
|
||||
result = result.filter(o => o.CurrAccCode === state.filters.CurrAccCode)
|
||||
}
|
||||
|
||||
if (state.filters.OrderDate) {
|
||||
result = result.filter(o =>
|
||||
o.OrderDate?.startsWith(state.filters.OrderDate)
|
||||
)
|
||||
}
|
||||
|
||||
return result
|
||||
},
|
||||
|
||||
totalVisibleUSD () {
|
||||
return this.filteredOrders.reduce((sum, o) => {
|
||||
const value = Number(o.TotalAmountUSD || 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
|
||||
}
|
||||
},
|
||||
|
||||
actions: {
|
||||
async fetchOrders () {
|
||||
|
||||
// ==============================
|
||||
// 📌 REQUEST ID
|
||||
// ==============================
|
||||
const rid = ++lastRequestId
|
||||
|
||||
// ==============================
|
||||
// 📌 SEARCH SNAPSHOT
|
||||
// ==============================
|
||||
const raw = this.filters.search ?? ''
|
||||
const trimmed = String(raw).trim()
|
||||
|
||||
// ==============================
|
||||
// 📌 REQUEST LOG
|
||||
// ==============================
|
||||
console.groupCollapsed(
|
||||
`%c[orders-prod] FETCH rid=${rid}`,
|
||||
'color:#1976d2;font-weight:bold'
|
||||
)
|
||||
|
||||
console.log('raw =', JSON.stringify(raw), 'len=', String(raw).length)
|
||||
console.log('trimmed =', JSON.stringify(trimmed), 'len=', trimmed.length)
|
||||
console.log('filters =', JSON.parse(JSON.stringify(this.filters)))
|
||||
console.log('lastRID =', lastRequestId)
|
||||
|
||||
console.groupEnd()
|
||||
|
||||
this.loading = true
|
||||
this.error = null
|
||||
|
||||
try {
|
||||
|
||||
// ==============================
|
||||
// 📌 PARAMS
|
||||
// ==============================
|
||||
const params = {}
|
||||
if (trimmed) params.search = trimmed
|
||||
|
||||
// ==============================
|
||||
// 📌 API CALL
|
||||
// ==============================
|
||||
const res = await api.get('/orders/production-list', { params })
|
||||
|
||||
// ==============================
|
||||
// 📌 STALE CHECK
|
||||
// ==============================
|
||||
if (rid !== lastRequestId) {
|
||||
console.warn(
|
||||
`[orders-prod] IGNORE stale response rid=${rid} last=${lastRequestId}`
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
// ==============================
|
||||
// 📌 DATA
|
||||
// ==============================
|
||||
const data = res?.data
|
||||
this.orders = Array.isArray(data) ? data : []
|
||||
|
||||
// ==============================
|
||||
// 📌 RESPONSE LOG
|
||||
// ==============================
|
||||
console.groupCollapsed(
|
||||
`%c[orders-prod] RESPONSE rid=${rid} count=${this.orders.length}`,
|
||||
'color:#2e7d32;font-weight:bold'
|
||||
)
|
||||
|
||||
console.log('status =', res?.status)
|
||||
|
||||
console.log(
|
||||
'sample =',
|
||||
this.orders.slice(0, 5).map(o => ({
|
||||
id: o.OrderHeaderID,
|
||||
no: o.OrderNumber,
|
||||
code: o.CurrAccCode,
|
||||
name: o.CurrAccDescription
|
||||
}))
|
||||
)
|
||||
|
||||
console.groupEnd()
|
||||
|
||||
} catch (err) {
|
||||
|
||||
if (rid !== lastRequestId) return
|
||||
|
||||
console.error(
|
||||
'[orders-prod] FETCH FAILED',
|
||||
err?.response?.status,
|
||||
err?.response?.data || err
|
||||
)
|
||||
|
||||
this.orders = []
|
||||
|
||||
this.error =
|
||||
err?.response?.data ||
|
||||
err?.message ||
|
||||
'Sipariş listesi alınamadı'
|
||||
|
||||
} finally {
|
||||
|
||||
if (rid === lastRequestId) {
|
||||
this.loading = false
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user