Merge remote-tracking branch 'origin/master'

This commit is contained in:
M_Kececi
2026-03-10 10:25:03 +03:00
parent 0d303f0c0f
commit 6f2a6df3d4
7 changed files with 648 additions and 170 deletions

View 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
};

View File

@@ -1,4 +1,4 @@
<template>
<template>
<q-page
v-if="canReadOrder"
class="order-page q-pa-md"
@@ -7,21 +7,23 @@
<div class="sticky-stack">
<div class="filter-bar row q-col-gutter-md q-mb-sm">
<div
v-for="def in attrDefs"
v-for="def in filterDefs"
:key="def.key"
class="col-12 col-md-3"
>
<q-select
v-model="filters[def.key]"
:options="filteredAttrOptions[def.key] || []"
:options="filteredOptionLists[def.key] || []"
:label="def.label"
filled
dense
clearable
use-input
input-debounce="250"
:loading="loadingAttributeOptions"
@filter="(val, update) => filterAttributeOptions(def.key, val, update)"
:disable="isFilterDisabled(def.key)"
:loading="loadingFilterOptions"
@update:model-value="onFilterValueChange(def.key)"
@filter="(val, update) => filterOptions(def.key, val, update)"
@keyup.enter="fetchStockByAttributes"
/>
</div>
@@ -32,7 +34,7 @@
icon="search"
label="Sorgula"
:loading="loadingStock"
:disable="!hasAnyFilter"
:disable="!canQuery"
@click="fetchStockByAttributes"
/>
</div>
@@ -253,32 +255,34 @@ const { canRead } = usePermission()
const canReadOrder = canRead('order')
const orderStore = useOrderEntryStore()
const attrDefs = [
{ key: 'att01', label: 'Urun Ana Grubu' },
{ key: 'att02', label: 'Urun Alt Grubu' },
{ key: 'att10', label: 'Marka' },
{ key: 'att11', label: 'DR' },
{ key: 'att21', label: 'Kalip' },
{ key: 'att35', label: 'Sezon Yili' },
{ key: 'att36', label: 'Mevsim' },
{ key: 'att44', label: 'Yetiskin/Garson' }
const filterDefs = [
{ key: 'kategori', label: 'Kategori' },
{ key: 'urun_ana_grubu', label: 'Urun Ana Grubu' },
{ key: 'urun_alt_grubu', label: 'Urun Alt Grubu' },
{ key: 'renk', label: 'Renk' },
{ key: 'renk2', label: '2.Renk' },
{ key: 'urun_icerigi', label: 'Urun Icerigi' },
{ key: 'fit', label: 'Fit' },
{ key: 'drop', label: 'Drop' },
{ key: 'beden', label: 'Beden' }
]
const loadingAttributeOptions = ref(false)
const loadingFilterOptions = ref(false)
const loadingStock = ref(false)
const errorMessage = ref('')
const filters = ref({
att01: '',
att02: '',
att10: '',
att11: '',
att21: '',
att35: '',
att36: '',
att44: ''
kategori: '',
urun_ana_grubu: '',
urun_alt_grubu: '',
renk: '',
renk2: '',
urun_icerigi: '',
fit: '',
drop: '',
beden: ''
})
const attributeOptions = ref({})
const filteredAttrOptions = ref({})
const optionLists = ref({})
const filteredOptionLists = ref({})
const rawRows = ref([])
const productImageCache = ref({})
const productImageLoading = ref({})
@@ -294,8 +298,9 @@ const imageListWaitQueue = []
const activeSchema = ref(storeSchemaByKey.tak)
const activeGrpKey = ref('tak')
const openState = ref({})
const hasAnyFilter = computed(() =>
attrDefs.some((def) => String(filters.value?.[def.key] || '').trim() !== '')
const canQuery = computed(() =>
String(filters.value?.kategori || '').trim() !== '' &&
String(filters.value?.urun_ana_grubu || '').trim() !== ''
)
const sizeLabels = computed(() => activeSchema.value?.values || [])
@@ -401,9 +406,8 @@ function getProductImageUrl(code, color) {
async function onProductImageError(code, color) {
const key = buildImageKey(code, color)
const current = String(productImageCache.value[key] || '')
const fallback = String(productImageFallbackByKey.value[key] || '')
if (fallback && current !== fallback && !productImageContentLoading.value[key]) {
if (fallback && !productImageContentLoading.value[key]) {
productImageContentLoading.value[key] = true
try {
const blobRes = await api.get(fallback, {
@@ -685,33 +689,84 @@ const level1Groups = computed(() => {
}))
})
function filterAttributeOptions(field, val, update) {
const source = Array.isArray(attributeOptions.value?.[field])
? attributeOptions.value[field]
function normalizeText(v) {
return String(v || '').trim()
}
function buildFilterParams() {
const out = {}
for (const def of filterDefs) {
const val = normalizeText(filters.value?.[def.key])
if (val) out[def.key] = val
}
return out
}
function isFilterDisabled(key) {
if (key === 'kategori') return false
if (key === 'urun_ana_grubu') {
return normalizeText(filters.value.kategori) === ''
}
return !canQuery.value
}
function onFilterValueChange(changedKey) {
if (changedKey === 'kategori') {
filters.value.urun_ana_grubu = ''
filters.value.urun_alt_grubu = ''
filters.value.renk = ''
filters.value.renk2 = ''
filters.value.urun_icerigi = ''
filters.value.fit = ''
filters.value.drop = ''
filters.value.beden = ''
} else if (changedKey === 'urun_ana_grubu') {
filters.value.urun_alt_grubu = ''
filters.value.renk = ''
filters.value.renk2 = ''
filters.value.urun_icerigi = ''
filters.value.fit = ''
filters.value.drop = ''
filters.value.beden = ''
} else if (changedKey === 'renk') {
filters.value.renk2 = ''
filters.value.beden = ''
} else if (changedKey === 'renk2') {
filters.value.beden = ''
}
void loadFilterOptions()
}
function filterOptions(field, val, update) {
const source = Array.isArray(optionLists.value?.[field])
? optionLists.value[field]
: []
if (!val) {
update(() => {
filteredAttrOptions.value[field] = [...source]
filteredOptionLists.value[field] = [...source]
})
return
}
const needle = String(val || '').toLocaleLowerCase('tr-TR')
update(() => {
filteredAttrOptions.value[field] = source.filter((opt) =>
filteredOptionLists.value[field] = source.filter((opt) =>
String(opt || '').toLocaleLowerCase('tr-TR').includes(needle)
)
})
}
async function loadAttributeOptions() {
loadingAttributeOptions.value = true
async function loadFilterOptions() {
loadingFilterOptions.value = true
try {
const res = await api.get('/product-stock-attribute-options')
const res = await api.get('/product-stock-attribute-options', {
params: buildFilterParams()
})
const payload = res?.data && typeof res.data === 'object' ? res.data : {}
const next = {}
const nextFiltered = {}
for (const def of attrDefs) {
for (const def of filterDefs) {
const arr = Array.isArray(payload?.[def.key]) ? payload[def.key] : []
const list = arr
.map((x) => String(x || '').trim())
@@ -719,26 +774,33 @@ async function loadAttributeOptions() {
.sort((a, b) => a.localeCompare(b, 'tr'))
next[def.key] = list
nextFiltered[def.key] = [...list]
const selected = normalizeText(filters.value?.[def.key])
if (selected && !list.includes(selected)) {
filters.value[def.key] = ''
}
}
attributeOptions.value = next
filteredAttrOptions.value = nextFiltered
optionLists.value = next
filteredOptionLists.value = nextFiltered
} catch (err) {
errorMessage.value = 'Urun ozellik secenekleri alinamadi.'
console.error('loadAttributeOptions error:', err)
console.error('loadFilterOptions error:', err)
} finally {
loadingAttributeOptions.value = false
loadingFilterOptions.value = false
}
}
async function fetchStockByAttributes() {
if (!hasAnyFilter.value) return
const params = {}
for (const def of attrDefs) {
const val = String(filters.value?.[def.key] || '').trim()
if (val) params[def.key] = val
if (!canQuery.value) {
$q.notify({
type: 'warning',
position: 'top-right',
message: 'Kategori ve Urun Ana Grubu secimi zorunludur.'
})
return
}
const params = buildFilterParams()
loadingStock.value = true
errorMessage.value = ''
@@ -803,14 +865,15 @@ function onLevel2Click(productCode, grp2) {
function resetForm() {
filters.value = {
att01: '',
att02: '',
att10: '',
att11: '',
att21: '',
att35: '',
att36: '',
att44: ''
kategori: '',
urun_ana_grubu: '',
urun_alt_grubu: '',
renk: '',
renk2: '',
urun_icerigi: '',
fit: '',
drop: '',
beden: ''
}
rawRows.value = []
errorMessage.value = ''
@@ -823,10 +886,11 @@ function resetForm() {
productImageFallbackByKey.value = {}
productImageContentLoading.value = {}
productImageListBlockedUntil.value = 0
void loadFilterOptions()
}
onMounted(() => {
loadAttributeOptions()
loadFilterOptions()
})
onUnmounted(() => {

View File

@@ -1,4 +1,4 @@
<template>
<template>
<q-page
v-if="canReadOrder"
class="order-page q-pa-md"
@@ -367,9 +367,8 @@ function getProductImageUrl(code, color) {
async function onProductImageError(code, color) {
const key = buildImageKey(code, color)
const current = String(productImageCache.value[key] || '')
const fallback = String(productImageFallbackByKey.value[key] || '')
if (fallback && current !== fallback && !productImageContentLoading.value[key]) {
if (fallback && !productImageContentLoading.value[key]) {
productImageContentLoading.value[key] = true
try {
const blobRes = await api.get(fallback, { baseURL: '', responseType: 'blob' })

View File

@@ -334,8 +334,16 @@ onMounted(async () => {
})
/* Tarih aralığı */
const dateFrom = ref(dayjs().startOf('year').format('YYYY-MM-DD'))
const dateTo = ref(dayjs().format('YYYY-MM-DD'))
function getDefaultDateRange () {
return {
from: dayjs().startOf('year').format('YYYY-MM-DD'),
to: dayjs().format('YYYY-MM-DD')
}
}
const defaultDateRange = getDefaultDateRange()
const dateFrom = ref(defaultDateRange.from)
const dateTo = ref(defaultDateRange.to)
function isValidFromDate (date) {
if (!dateTo.value) return true
@@ -486,8 +494,9 @@ function normalizeText (str) {
/* Reset */
function resetFilters() {
selectedCari.value = null
dateFrom.value = ''
dateTo.value = ''
const range = getDefaultDateRange()
dateFrom.value = range.from
dateTo.value = range.to
selectedMonType.value = monetaryTypeOptions[0].value
excludeOpening.value = false
statementheaderStore.headers = []