Fix product performance grouped JSON build
This commit is contained in:
@@ -60,6 +60,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</q-btn-dropdown>
|
||||
<q-btn dense outline color="positive" icon="grid_on" label="Excel" :loading="excelExportLoading" :disable="pageBusy || excelExportLoading" @click="exportProductPerformanceExcel" />
|
||||
<q-btn dense outline color="secondary" icon="refresh" label="Yenile" :loading="loading" :disable="pageBusy" @click="reload" />
|
||||
</div>
|
||||
</div>
|
||||
@@ -1520,7 +1521,7 @@
|
||||
<script setup>
|
||||
import { computed, nextTick, onBeforeUnmount, onMounted, reactive, ref, watch } from 'vue'
|
||||
import { Notify } from 'quasar'
|
||||
import api from 'src/services/api'
|
||||
import api, { extractApiErrorDetail } from 'src/services/api'
|
||||
import { useProductPerformanceStore } from 'src/stores/productPerformanceStore'
|
||||
|
||||
const performanceStore = useProductPerformanceStore()
|
||||
@@ -1544,6 +1545,7 @@ function formatPercent (value) {
|
||||
}
|
||||
|
||||
const loading = ref(false)
|
||||
const excelExportLoading = ref(false)
|
||||
const rows = ref([])
|
||||
const detailMainGroupOptionRows = ref([])
|
||||
const generalRows = ref([])
|
||||
@@ -1658,6 +1660,18 @@ const backendGroupedFilterFields = new Set([
|
||||
'customer_name',
|
||||
'performance_bucket'
|
||||
])
|
||||
const productPerformanceExcelExportFilterFields = new Set([
|
||||
'kategori',
|
||||
'askili_yan',
|
||||
'urun_ilk_grubu',
|
||||
'urun_ana_grubu',
|
||||
'urun_alt_grubu',
|
||||
'product_code',
|
||||
'color_yaka',
|
||||
'color_code',
|
||||
'yaka_kodu',
|
||||
'performance_bucket'
|
||||
])
|
||||
const performanceTabs = [
|
||||
{ name: 'products', icon: 'dashboard', label: 'Genel Özet KPI' },
|
||||
{ name: 'product_detail', icon: 'category', label: 'Detay KPI' },
|
||||
@@ -2312,6 +2326,105 @@ const customerPeriodScoreColumns = [
|
||||
{ name: 'customer_score_total', label: 'Genel Müşteri Skor', field: row => formatNumber(row.customer_score_total, 2), align: 'right', sortable: true }
|
||||
]
|
||||
|
||||
const metricLabelOverrides = {
|
||||
stock_qty: 'Toplam Stok',
|
||||
sales_qty: 'Toplam Adet',
|
||||
sales_qty_90d: '90G Toplam Adet',
|
||||
sales_qty_180d: '180G Toplam Adet',
|
||||
sales_qty_365d: '360G Toplam Adet',
|
||||
sales_qty_total: 'Genel Toplam Adet',
|
||||
sales_usd: 'Toplam Ciro USD',
|
||||
sales_usd_90d: '90G Toplam Ciro USD',
|
||||
sales_usd_180d: '180G Toplam Ciro USD',
|
||||
sales_usd_365d: '360G Toplam Ciro USD',
|
||||
sales_usd_total: 'Genel Toplam Ciro USD',
|
||||
avg_price_usd: 'Ağ. Ort. Fiyat USD',
|
||||
avg_price_usd_90d: '90G Ağ. Ort. Fiyat USD',
|
||||
avg_price_usd_180d: '180G Ağ. Ort. Fiyat USD',
|
||||
avg_price_usd_365d: '360G Ağ. Ort. Fiyat USD',
|
||||
avg_price_usd_total: 'Genel Ağ. Ort. Fiyat USD',
|
||||
base_price_usd: 'Ort. Taban Maliyet USD',
|
||||
cost_price_usd: 'Ort. Çıplak Maliyet USD',
|
||||
base_price_usd_90d: '90G Ort. Taban Maliyet USD',
|
||||
cost_price_usd_90d: '90G Ort. Çıplak Maliyet USD',
|
||||
base_price_usd_180d: '180G Ort. Taban Maliyet USD',
|
||||
cost_price_usd_180d: '180G Ort. Çıplak Maliyet USD',
|
||||
base_price_usd_365d: '360G Ort. Taban Maliyet USD',
|
||||
cost_price_usd_365d: '360G Ort. Çıplak Maliyet USD',
|
||||
base_price_usd_total: 'Genel Ort. Taban Maliyet USD',
|
||||
cost_price_usd_total: 'Genel Ort. Çıplak Maliyet USD',
|
||||
unit_profit_base_90d: '90G Ağ. Ort. Birim Taban K/Z USD',
|
||||
unit_profit_cost_90d: '90G Ağ. Ort. Birim Çıplak K/Z USD',
|
||||
unit_profit_base_180d: '180G Ağ. Ort. Birim Taban K/Z USD',
|
||||
unit_profit_cost_180d: '180G Ağ. Ort. Birim Çıplak K/Z USD',
|
||||
unit_profit_base_total: 'Genel Ağ. Ort. Birim Taban K/Z USD',
|
||||
unit_profit_cost_total: 'Genel Ağ. Ort. Birim Çıplak K/Z USD',
|
||||
gross_profit_usd_90d: '90G Toplam K/Z USD',
|
||||
gross_profit_usd_180d: '180G Toplam K/Z USD',
|
||||
gross_profit_usd_total: 'Genel Toplam K/Z USD',
|
||||
gross_profit_base_usd_90d: '90G Taban Toplam K/Z USD',
|
||||
gross_profit_cost_usd_90d: '90G Çıplak Toplam K/Z USD',
|
||||
gross_profit_base_usd_180d: '180G Taban Toplam K/Z USD',
|
||||
gross_profit_cost_usd_180d: '180G Çıplak Toplam K/Z USD',
|
||||
gross_profit_base_usd_365d: '360G Taban Toplam K/Z USD',
|
||||
gross_profit_cost_usd_365d: '360G Çıplak Toplam K/Z USD',
|
||||
gross_profit_base_usd_total: 'Genel Taban Toplam K/Z USD',
|
||||
gross_profit_cost_usd_total: 'Genel Çıplak Toplam K/Z USD',
|
||||
gross_margin_base_90d: '90G Ağ. Ort. Taban Marj',
|
||||
gross_margin_cost_90d: '90G Ağ. Ort. Çıplak Marj',
|
||||
gross_margin_base_180d: '180G Ağ. Ort. Taban Marj',
|
||||
gross_margin_cost_180d: '180G Ağ. Ort. Çıplak Marj',
|
||||
gross_margin_base_365d: '360G Ağ. Ort. Taban Marj',
|
||||
gross_margin_cost_365d: '360G Ağ. Ort. Çıplak Marj',
|
||||
gross_margin_base_total: 'Genel Ağ. Ort. Taban Marj',
|
||||
gross_margin_cost_total: 'Genel Ağ. Ort. Çıplak Marj',
|
||||
performance_score: 'Ağ. Ort. Skor',
|
||||
performance_score_90d: '90G Ağ. Ort. Ürün Skor',
|
||||
performance_score_180d: '180G Ağ. Ort. Ürün Skor',
|
||||
performance_score_365d: '360G Ağ. Ort. Ürün Skor',
|
||||
performance_score_total: 'Genel Ağ. Ort. Ürün Skor',
|
||||
customer_score_90d: '90G Ağ. Ort. Müşteri Skor',
|
||||
customer_score_180d: '180G Ağ. Ort. Müşteri Skor',
|
||||
customer_score_365d: '360G Ağ. Ort. Müşteri Skor',
|
||||
customer_score_total: 'Genel Ağ. Ort. Müşteri Skor',
|
||||
market_count_90d: '90G Tekil Piyasa',
|
||||
customer_count_90d: '90G Tekil Müşteri',
|
||||
market_count_total: 'Genel Tekil Piyasa',
|
||||
customer_count_total: 'Genel Tekil Müşteri',
|
||||
sales_index_90d: '90G Ağ. Ort. Endeks',
|
||||
sales_index_total: 'Genel Ağ. Ort. Endeks',
|
||||
stock_days_90d: '90G Ağ. Ort. Stok Gün',
|
||||
stock_days_180d: '180G Ağ. Ort. Stok Gün',
|
||||
stock_days_365d: '360G Ağ. Ort. Stok Gün',
|
||||
stock_days_total: 'Genel Ağ. Ort. Stok Gün',
|
||||
avg_daily_sales_total: 'Genel Günlük Ort. Adet'
|
||||
}
|
||||
|
||||
function applyMetricLabelOverrides (...columnGroups) {
|
||||
for (const group of columnGroups) {
|
||||
for (const col of group || []) {
|
||||
if (metricLabelOverrides[col?.name]) col.label = metricLabelOverrides[col.name]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
applyMetricLabelOverrides(
|
||||
detailSalesColumns,
|
||||
columns,
|
||||
generalColumns,
|
||||
orderAnalysisColumns,
|
||||
orderGroupColumns,
|
||||
orderProductCustomerColumns,
|
||||
orderMarketDetailColumns,
|
||||
idleColumns,
|
||||
marketColumns,
|
||||
countryColumns,
|
||||
customerColumns,
|
||||
salesBreakdownColumns,
|
||||
productPeriodScoreColumns,
|
||||
customerPeriodScoreColumns
|
||||
)
|
||||
|
||||
function ensureColumns (targetColumns, columnsToEnsure) {
|
||||
const existing = new Set(targetColumns.map(col => col.name))
|
||||
for (const col of columnsToEnsure) {
|
||||
@@ -2922,7 +3035,7 @@ function backendGroupedFilterOptionParams (tabKey) {
|
||||
mode: tabKey,
|
||||
groupLevels: (tabGroupLevels[tabKey] || groupLevels).map(level => level.key),
|
||||
fields,
|
||||
limit: 5000
|
||||
limit: 50000
|
||||
}
|
||||
if (tabKey === 'product_detail') params.urunAnaGrubu = selectedDetailMainGroup.value
|
||||
return params
|
||||
@@ -3283,7 +3396,9 @@ function makeGroupRow (key, level, groupDef, value, groupRows) {
|
||||
sales_index_total: averageRows(groupRows, 'sales_index_total'),
|
||||
performance_bucket: dominantValue(groupRows, 'performance_bucket'),
|
||||
...aggregate,
|
||||
performance_score: groupPerformanceScore(aggregate, groupDef.key),
|
||||
performance_score: Object.prototype.hasOwnProperty.call(aggregate, 'performance_score')
|
||||
? aggregate.performance_score
|
||||
: groupPerformanceScore(aggregate, groupDef.key),
|
||||
recommendation: dominantValue(groupRows, 'recommendation') || `${formatNumber(groupRows.length, 0)} satır`
|
||||
}
|
||||
}
|
||||
@@ -3372,15 +3487,15 @@ function applyDerivedGroupMetrics (out, sourceRows, groupField = '') {
|
||||
if (sourceRows.some(row => row.performance_bucket)) {
|
||||
out.performance_bucket = dominantValue(sourceRows, 'performance_bucket')
|
||||
}
|
||||
out.customer_score_90d = customerSalesPeriodScore(periodMetricSource(out, '90d'))
|
||||
out.customer_score_180d = customerSalesPeriodScore(periodMetricSource(out, '180d'))
|
||||
out.customer_score_365d = customerSalesPeriodScore(periodMetricSource(out, '365d'))
|
||||
out.customer_score_total = customerSalesPeriodScore(periodMetricSource(out, 'total'))
|
||||
out.performance_score_90d = productSalesPeriodScore(productPeriodMetricSource(out, '90d'))
|
||||
out.performance_score_180d = productSalesPeriodScore(productPeriodMetricSource(out, '180d'))
|
||||
out.performance_score_365d = productSalesPeriodScore(productPeriodMetricSource(out, '365d'))
|
||||
out.performance_score_total = productSalesPeriodScore(productPeriodMetricSource(out, 'total'))
|
||||
out.performance_score = groupPerformanceScore(out, groupField)
|
||||
if (!Object.prototype.hasOwnProperty.call(out, 'customer_score_90d')) out.customer_score_90d = customerSalesPeriodScore(periodMetricSource(out, '90d'))
|
||||
if (!Object.prototype.hasOwnProperty.call(out, 'customer_score_180d')) out.customer_score_180d = customerSalesPeriodScore(periodMetricSource(out, '180d'))
|
||||
if (!Object.prototype.hasOwnProperty.call(out, 'customer_score_365d')) out.customer_score_365d = customerSalesPeriodScore(periodMetricSource(out, '365d'))
|
||||
if (!Object.prototype.hasOwnProperty.call(out, 'customer_score_total')) out.customer_score_total = customerSalesPeriodScore(periodMetricSource(out, 'total'))
|
||||
if (!Object.prototype.hasOwnProperty.call(out, 'performance_score_90d')) out.performance_score_90d = productSalesPeriodScore(productPeriodMetricSource(out, '90d'))
|
||||
if (!Object.prototype.hasOwnProperty.call(out, 'performance_score_180d')) out.performance_score_180d = productSalesPeriodScore(productPeriodMetricSource(out, '180d'))
|
||||
if (!Object.prototype.hasOwnProperty.call(out, 'performance_score_365d')) out.performance_score_365d = productSalesPeriodScore(productPeriodMetricSource(out, '365d'))
|
||||
if (!Object.prototype.hasOwnProperty.call(out, 'performance_score_total')) out.performance_score_total = productSalesPeriodScore(productPeriodMetricSource(out, 'total'))
|
||||
if (!Object.prototype.hasOwnProperty.call(out, 'performance_score')) out.performance_score = groupPerformanceScore(out, groupField)
|
||||
}
|
||||
|
||||
function groupPerformanceScore (row, groupField = '') {
|
||||
@@ -3672,18 +3787,12 @@ function weightedAverageProductCost (sourceRows, valueField) {
|
||||
if (!key || variants.has(key)) continue
|
||||
variants.set(key, row)
|
||||
}
|
||||
const stockSource = variants.size > 0 ? Array.from(variants.values()) : sourceRows
|
||||
const stockRows = stockSource
|
||||
.map(row => ({
|
||||
value: Number(row[valueField] || 0),
|
||||
qty: Number(row.stock_qty || 0)
|
||||
}))
|
||||
.filter(row => Number.isFinite(row.value) && Number.isFinite(row.qty) && row.value > 0 && row.qty > 0)
|
||||
const stockQty = stockRows.reduce((sum, row) => sum + row.qty, 0)
|
||||
if (stockQty > 0) {
|
||||
return stockRows.reduce((sum, row) => sum + row.value * row.qty, 0) / stockQty
|
||||
}
|
||||
return 0
|
||||
const averageSource = variants.size > 0 ? Array.from(variants.values()) : sourceRows
|
||||
const values = averageSource
|
||||
.map(row => Number(row[valueField] || 0))
|
||||
.filter(value => Number.isFinite(value) && value > 0)
|
||||
if (!values.length) return 0
|
||||
return values.reduce((sum, value) => sum + value, 0) / values.length
|
||||
}
|
||||
|
||||
function marginFromSalesCost (salesUSD, qty, unitCost) {
|
||||
@@ -3878,27 +3987,50 @@ function formatGroupCell (row, colOrName) {
|
||||
}
|
||||
|
||||
function withProductMargins (row) {
|
||||
const salesQtyTotal = Number(row?.sales_qty_total || 0)
|
||||
const salesUSDTotal = Number(row?.sales_usd_total || 0)
|
||||
const basePrice = Number(row?.base_price_usd || 0)
|
||||
const costPrice = Number(row?.cost_price_usd || 0)
|
||||
const grossProfitBaseTotal = salesQtyTotal > 0 ? salesUSDTotal - (salesQtyTotal * basePrice) : 0
|
||||
const grossProfitCostTotal = salesQtyTotal > 0 ? salesUSDTotal - (salesQtyTotal * costPrice) : 0
|
||||
return {
|
||||
...row,
|
||||
avg_price_usd_365d: Number(row?.sales_qty_365d || 0) > 0 ? Number(row?.sales_usd_365d || 0) / Number(row?.sales_qty_365d || 0) : 0,
|
||||
avg_price_usd_total: salesQtyTotal > 0 ? salesUSDTotal / salesQtyTotal : Number(row?.avg_price_usd_total || 0),
|
||||
stock_turnover_90d: existingOrStockTurnover(row?.stock_turnover_90d, row?.sales_qty_90d, row?.stock_qty),
|
||||
stock_turnover_180d: existingOrStockTurnover(row?.stock_turnover_180d, row?.sales_qty_180d, row?.stock_qty),
|
||||
stock_turnover_365d: existingOrStockTurnover(row?.stock_turnover_365d, row?.sales_qty_365d, row?.stock_qty),
|
||||
stock_turnover_total: existingOrStockTurnover(row?.stock_turnover_total, row?.sales_qty_total, row?.stock_qty),
|
||||
gross_profit_base_usd_total: grossProfitBaseTotal,
|
||||
gross_profit_cost_usd_total: grossProfitCostTotal,
|
||||
gross_profit_usd_total: Number(row?.gross_profit_usd_total ?? grossProfitCostTotal),
|
||||
gross_margin_base_90d: marginFromSalesCost(row?.sales_usd_90d, row?.sales_qty_90d, row?.base_price_usd),
|
||||
gross_margin_cost_90d: marginFromSalesCost(row?.sales_usd_90d, row?.sales_qty_90d, row?.cost_price_usd),
|
||||
gross_margin_base_180d: marginFromSalesCost(row?.sales_usd_180d, row?.sales_qty_180d, row?.base_price_usd),
|
||||
gross_margin_cost_180d: marginFromSalesCost(row?.sales_usd_180d, row?.sales_qty_180d, row?.cost_price_usd),
|
||||
gross_margin_base_365d: marginFromSalesCost(row?.sales_usd_365d, row?.sales_qty_365d, row?.base_price_usd),
|
||||
gross_margin_cost_365d: marginFromSalesCost(row?.sales_usd_365d, row?.sales_qty_365d, row?.cost_price_usd)
|
||||
gross_margin_cost_365d: marginFromSalesCost(row?.sales_usd_365d, row?.sales_qty_365d, row?.cost_price_usd),
|
||||
gross_margin_base_total: marginFromSalesCost(salesUSDTotal, salesQtyTotal, basePrice),
|
||||
gross_margin_cost_total: marginFromSalesCost(salesUSDTotal, salesQtyTotal, costPrice)
|
||||
}
|
||||
}
|
||||
|
||||
function withGeneralMargins (row) {
|
||||
const salesQtyTotal = Number(row?.sales_qty_total || 0)
|
||||
const salesUSDTotal = Number(row?.sales_usd_total || 0)
|
||||
const basePrice = Number(row?.base_price_usd || 0)
|
||||
const costPrice = Number(row?.cost_price_usd || 0)
|
||||
const grossProfitBaseTotal = salesQtyTotal > 0 ? salesUSDTotal - (salesQtyTotal * basePrice) : 0
|
||||
const grossProfitCostTotal = salesQtyTotal > 0 ? salesUSDTotal - (salesQtyTotal * costPrice) : 0
|
||||
return {
|
||||
...row,
|
||||
avg_price_usd_total: salesQtyTotal > 0 ? salesUSDTotal / salesQtyTotal : Number(row?.avg_price_usd_total || 0),
|
||||
stock_turnover_total: existingOrStockTurnover(row?.stock_turnover_total, row?.sales_qty_total, row?.stock_qty),
|
||||
gross_margin_base_total: marginFromSalesCost(row?.sales_usd_total, row?.sales_qty_total, row?.base_price_usd),
|
||||
gross_margin_cost_total: marginFromSalesCost(row?.sales_usd_total, row?.sales_qty_total, row?.cost_price_usd)
|
||||
gross_profit_base_usd_total: grossProfitBaseTotal,
|
||||
gross_profit_cost_usd_total: grossProfitCostTotal,
|
||||
gross_profit_usd_total: Number(row?.gross_profit_usd_total ?? grossProfitCostTotal),
|
||||
gross_margin_base_total: marginFromSalesCost(salesUSDTotal, salesQtyTotal, basePrice),
|
||||
gross_margin_cost_total: marginFromSalesCost(salesUSDTotal, salesQtyTotal, costPrice)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4276,19 +4408,19 @@ function normalizeRow (row) {
|
||||
const color = String(row?.color_code || '').trim()
|
||||
const yaka = String(row?.yaka_kodu || '').trim()
|
||||
const market = String(row?.market_key || '').trim()
|
||||
return withPeriodScores(withProductMargins({
|
||||
return withPeriodScores(withGeneralMargins(withProductMargins({
|
||||
...row,
|
||||
row_key: `${product}|${color}|${yaka}|${market}`
|
||||
}))
|
||||
})))
|
||||
}
|
||||
|
||||
function withPeriodScores (row) {
|
||||
const next = { ...row }
|
||||
next.performance_score_90d = Number(next.performance_score_90d || productSalesPeriodScore(productPeriodMetricSource(next, '90d')))
|
||||
next.performance_score_180d = Number(next.performance_score_180d || productSalesPeriodScore(productPeriodMetricSource(next, '180d')))
|
||||
next.performance_score_365d = Number(next.performance_score_365d || productSalesPeriodScore(productPeriodMetricSource(next, '365d')))
|
||||
next.performance_score_total = Number(next.performance_score_total || productSalesPeriodScore(productPeriodMetricSource(next, 'total')))
|
||||
if (!Number(next.performance_score || 0)) next.performance_score = next.performance_score_90d
|
||||
if (!Object.prototype.hasOwnProperty.call(next, 'performance_score_90d')) next.performance_score_90d = productSalesPeriodScore(productPeriodMetricSource(next, '90d'))
|
||||
if (!Object.prototype.hasOwnProperty.call(next, 'performance_score_180d')) next.performance_score_180d = productSalesPeriodScore(productPeriodMetricSource(next, '180d'))
|
||||
if (!Object.prototype.hasOwnProperty.call(next, 'performance_score_365d')) next.performance_score_365d = productSalesPeriodScore(productPeriodMetricSource(next, '365d'))
|
||||
if (!Object.prototype.hasOwnProperty.call(next, 'performance_score_total')) next.performance_score_total = productSalesPeriodScore(productPeriodMetricSource(next, 'total'))
|
||||
if (!Object.prototype.hasOwnProperty.call(next, 'performance_score')) next.performance_score = next.performance_score_90d
|
||||
return next
|
||||
}
|
||||
|
||||
@@ -4422,16 +4554,6 @@ function displayMarketName (value) {
|
||||
return parts.length ? parts[parts.length - 1] : ''
|
||||
}
|
||||
|
||||
function normalizeUploadsPath (storagePath) {
|
||||
const raw = String(storagePath || '').trim()
|
||||
if (!raw) return ''
|
||||
const normalized = raw.replace(/\\/g, '/')
|
||||
const idx = normalized.toLowerCase().indexOf('/uploads/')
|
||||
if (idx >= 0) return normalized.slice(idx)
|
||||
if (normalized.toLowerCase().startsWith('uploads/')) return `/${normalized}`
|
||||
return ''
|
||||
}
|
||||
|
||||
function resolveProductImageUrl (item) {
|
||||
return resolveProductImageUrls(item)[0] || ''
|
||||
}
|
||||
@@ -4450,18 +4572,9 @@ function resolveProductImageUrls (item) {
|
||||
const fullURL = String(item.full_url || item.fullUrl || '').trim()
|
||||
addURL(fullURL)
|
||||
|
||||
const uploadsPath = normalizeUploadsPath(item.storage_path || item.storage || '')
|
||||
addURL(uploadsPath)
|
||||
|
||||
const fileName = String(item.file_name || item.FileName || '').trim()
|
||||
if (fileName) addURL(`/uploads/image/${fileName}`)
|
||||
|
||||
const contentURL = String(item.content_url || item.ContentURL || '').trim()
|
||||
if (contentURL.startsWith('/api/')) addURL(contentURL)
|
||||
else if (contentURL.startsWith('/')) addURL(`/api${contentURL}`)
|
||||
|
||||
const imageId = Number(item.id || item.ID || 0)
|
||||
if (Number.isFinite(imageId) && imageId > 0) addURL(`/api/product-images/${imageId}/content`)
|
||||
return urls
|
||||
}
|
||||
|
||||
@@ -4897,6 +5010,72 @@ async function timedProductPerformanceGet (label, url, config = {}) {
|
||||
}
|
||||
}
|
||||
|
||||
function productPerformanceExcelExportTableKey () {
|
||||
if (isProductKpiTab.value) return activeProductTableKey.value
|
||||
if (backendGroupedSupportedTab(activeTab.value)) return activeTab.value
|
||||
return 'products'
|
||||
}
|
||||
|
||||
function buildProductPerformanceExcelExportFilters () {
|
||||
const tableKey = productPerformanceExcelExportTableKey()
|
||||
const out = {}
|
||||
for (const col of columnsForTableKey(tableKey) || []) {
|
||||
const name = String(col?.name || '').trim()
|
||||
if (!productPerformanceExcelExportFilterFields.has(name)) continue
|
||||
const selected = selectedColumnFilters(tableKey, name)
|
||||
if (selected.length) out[name] = selected
|
||||
}
|
||||
if (activeTab.value === 'product_detail') {
|
||||
const mainGroup = String(selectedDetailMainGroup.value || '').trim()
|
||||
if (mainGroup && !out.urun_ana_grubu?.includes(mainGroup)) {
|
||||
out.urun_ana_grubu = [...(out.urun_ana_grubu || []), mainGroup]
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
function productPerformanceExcelFilename (headers = {}) {
|
||||
const disposition = String(headers?.['content-disposition'] || headers?.['Content-Disposition'] || '').trim()
|
||||
const utfMatch = disposition.match(/filename\*=UTF-8''([^;]+)/i)
|
||||
if (utfMatch?.[1]) return decodeURIComponent(utfMatch[1].replaceAll('"', '').trim())
|
||||
const match = disposition.match(/filename="?([^";]+)"?/i)
|
||||
return match?.[1]?.trim() || `product_performance_color_yaka_${new Date().toISOString().slice(0, 10)}.xlsx`
|
||||
}
|
||||
|
||||
function downloadProductPerformanceExcelBlob (blob, filename) {
|
||||
const url = URL.createObjectURL(blob)
|
||||
const a = document.createElement('a')
|
||||
a.href = url
|
||||
a.download = filename
|
||||
document.body.appendChild(a)
|
||||
a.click()
|
||||
a.remove()
|
||||
URL.revokeObjectURL(url)
|
||||
}
|
||||
|
||||
async function exportProductPerformanceExcel () {
|
||||
if (excelExportLoading.value) return
|
||||
excelExportLoading.value = true
|
||||
try {
|
||||
const tableKey = productPerformanceExcelExportTableKey()
|
||||
const pagination = tablePagination[tableKey] || tablePagination.products || {}
|
||||
const response = await api.post('/pricing/product-performance/export-excel', {
|
||||
filters: buildProductPerformanceExcelExportFilters(),
|
||||
sort_by: pagination.sortBy || '',
|
||||
descending: pagination.descending !== false
|
||||
}, {
|
||||
responseType: 'blob',
|
||||
timeout: 180000
|
||||
})
|
||||
downloadProductPerformanceExcelBlob(response.data, productPerformanceExcelFilename(response.headers || {}))
|
||||
} catch (err) {
|
||||
const detail = await extractApiErrorDetail(err)
|
||||
Notify.create({ type: 'negative', message: detail || 'Excel çıktısı alınamadı' })
|
||||
} finally {
|
||||
excelExportLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function reload () {
|
||||
loading.value = true
|
||||
startLoadingTimer('Rapor verileri hazırlanıyor...')
|
||||
|
||||
Reference in New Issue
Block a user