Merge remote-tracking branch 'origin/master'
This commit is contained in:
@@ -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(() => {
|
||||
|
||||
@@ -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' })
|
||||
|
||||
@@ -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 = []
|
||||
|
||||
Reference in New Issue
Block a user