Merge remote-tracking branch 'origin/master'
# Conflicts: # svc/queries/product_performance_sql.go # ui/src/pages/ProductPerformanceProfitability.vue
This commit is contained in:
@@ -3234,7 +3234,7 @@ SELECT jsonb_build_object(
|
|||||||
'group_value', group_value,
|
'group_value', group_value,
|
||||||
'label', group_value,
|
'label', group_value,
|
||||||
'count', row_count,
|
'count', row_count,
|
||||||
'recommendation', row_count::text || ' satir',
|
'recommendation', CASE WHEN recommendation <> '' THEN recommendation ELSE row_count::text || ' satir' END,
|
||||||
'image_product_code', image_product_code,
|
'image_product_code', image_product_code,
|
||||||
'image_color_code', image_color_code,
|
'image_color_code', image_color_code,
|
||||||
'image_yaka_kodu', image_yaka_kodu,
|
'image_yaka_kodu', image_yaka_kodu,
|
||||||
@@ -3356,6 +3356,7 @@ FROM (
|
|||||||
CASE WHEN SUM(sales_qty_90d) > 0 THEN SUM(margin_index_90d * sales_qty_90d) / NULLIF(SUM(sales_qty_90d),0) ELSE 0 END AS margin_index_90d,
|
CASE WHEN SUM(sales_qty_90d) > 0 THEN SUM(margin_index_90d * sales_qty_90d) / NULLIF(SUM(sales_qty_90d),0) ELSE 0 END AS margin_index_90d,
|
||||||
CASE WHEN SUM(sales_qty_90d) > 0 THEN SUM(performance_score * sales_qty_90d) / NULLIF(SUM(sales_qty_90d),0) ELSE 0 END AS performance_score,
|
CASE WHEN SUM(sales_qty_90d) > 0 THEN SUM(performance_score * sales_qty_90d) / NULLIF(SUM(sales_qty_90d),0) ELSE 0 END AS performance_score,
|
||||||
MODE() WITHIN GROUP (ORDER BY performance_bucket) AS performance_bucket,
|
MODE() WITHIN GROUP (ORDER BY performance_bucket) AS performance_bucket,
|
||||||
|
COALESCE(MODE() WITHIN GROUP (ORDER BY NULLIF(recommendation,'')), '') AS recommendation,
|
||||||
COALESCE(SUM(CASE WHEN stock_variant_rank = 1 THEN idle_cost_usd ELSE 0 END),0) AS idle_cost_usd
|
COALESCE(SUM(CASE WHEN stock_variant_rank = 1 THEN idle_cost_usd ELSE 0 END),0) AS idle_cost_usd
|
||||||
FROM (
|
FROM (
|
||||||
SELECT
|
SELECT
|
||||||
@@ -3537,6 +3538,8 @@ func productPerformanceSQLGroupExpr(field string) (string, bool) {
|
|||||||
return "CASE WHEN btrim(COALESCE(askili_yan,'')) = '-' THEN '' ELSE COALESCE(askili_yan,'') END", true
|
return "CASE WHEN btrim(COALESCE(askili_yan,'')) = '-' THEN '' ELSE COALESCE(askili_yan,'') END", true
|
||||||
case "kategori", "urun_ana_grubu", "urun_alt_grubu", "product_code", "item_description", "color_code", "yaka_kodu":
|
case "kategori", "urun_ana_grubu", "urun_alt_grubu", "product_code", "item_description", "color_code", "yaka_kodu":
|
||||||
return field, true
|
return field, true
|
||||||
|
case "color_yaka":
|
||||||
|
return "concat_ws('/', NULLIF(btrim(COALESCE(color_code,'')), ''), NULLIF(btrim(COALESCE(yaka_kodu,'')), ''))", true
|
||||||
case "market_key":
|
case "market_key":
|
||||||
return "btrim(regexp_replace(COALESCE(market_key,''), '^.*\\|', ''))", true
|
return "btrim(regexp_replace(COALESCE(market_key,''), '^.*\\|', ''))", true
|
||||||
default:
|
default:
|
||||||
@@ -3939,7 +3942,7 @@ func maxFloat(a, b float64) float64 {
|
|||||||
func sanitizeProductPerformanceGroupLevels(levels []string) []string {
|
func sanitizeProductPerformanceGroupLevels(levels []string) []string {
|
||||||
allowed := map[string]bool{
|
allowed := map[string]bool{
|
||||||
"kategori": true, "askili_yan": true, "urun_ilk_grubu": true, "urun_ana_grubu": true, "urun_alt_grubu": true,
|
"kategori": true, "askili_yan": true, "urun_ilk_grubu": true, "urun_ana_grubu": true, "urun_alt_grubu": true,
|
||||||
"product_code": true, "item_description": true, "color_code": true, "yaka_kodu": true, "market_key": true, "customer_code": true,
|
"product_code": true, "item_description": true, "color_code": true, "yaka_kodu": true, "color_yaka": true, "market_key": true, "customer_code": true,
|
||||||
"customer_name": true, "country": true, "customer_segment": true,
|
"customer_name": true, "country": true, "customer_segment": true,
|
||||||
}
|
}
|
||||||
out := make([]string, 0, len(levels))
|
out := make([]string, 0, len(levels))
|
||||||
@@ -3957,9 +3960,9 @@ func sanitizeProductPerformanceGroupLevels(levels []string) []string {
|
|||||||
func defaultProductPerformanceGroupLevels(mode string) []string {
|
func defaultProductPerformanceGroupLevels(mode string) []string {
|
||||||
switch mode {
|
switch mode {
|
||||||
case "sales_color_yaka_market_customer":
|
case "sales_color_yaka_market_customer":
|
||||||
return []string{"color_code", "yaka_kodu", "urun_ilk_grubu", "askili_yan", "kategori", "urun_ana_grubu", "urun_alt_grubu", "product_code", "item_description", "country", "market_key", "customer_segment", "customer_code", "customer_name"}
|
return []string{"color_yaka", "urun_ilk_grubu", "askili_yan", "kategori", "urun_ana_grubu", "urun_alt_grubu", "product_code", "item_description", "country", "market_key", "customer_segment", "customer_code", "customer_name"}
|
||||||
case "product_detail":
|
case "product_detail":
|
||||||
return []string{"urun_ana_grubu", "urun_alt_grubu", "product_code", "item_description", "color_code", "yaka_kodu", "market_key"}
|
return []string{"urun_ana_grubu", "urun_alt_grubu", "product_code", "item_description", "color_yaka", "market_key"}
|
||||||
case "idle":
|
case "idle":
|
||||||
return []string{"urun_ilk_grubu", "askili_yan", "kategori", "urun_ana_grubu", "urun_alt_grubu", "product_code", "item_description", "color_code", "yaka_kodu"}
|
return []string{"urun_ilk_grubu", "askili_yan", "kategori", "urun_ana_grubu", "urun_alt_grubu", "product_code", "item_description", "color_code", "yaka_kodu"}
|
||||||
case "sales_product_country_segment_market_customer":
|
case "sales_product_country_segment_market_customer":
|
||||||
@@ -3983,6 +3986,15 @@ func mapGroupValue(row map[string]any, field string) string {
|
|||||||
return cleanProductPerformanceFirstGroup(stringFromMap(row, "urun_ilk_grubu"))
|
return cleanProductPerformanceFirstGroup(stringFromMap(row, "urun_ilk_grubu"))
|
||||||
case "market_key":
|
case "market_key":
|
||||||
return displayProductPerformanceMarketName(stringFromMap(row, "market_key"))
|
return displayProductPerformanceMarketName(stringFromMap(row, "market_key"))
|
||||||
|
case "color_yaka":
|
||||||
|
parts := make([]string, 0, 2)
|
||||||
|
if color := strings.TrimSpace(stringFromMap(row, "color_code")); color != "" {
|
||||||
|
parts = append(parts, color)
|
||||||
|
}
|
||||||
|
if yaka := strings.TrimSpace(stringFromMap(row, "yaka_kodu")); yaka != "" {
|
||||||
|
parts = append(parts, yaka)
|
||||||
|
}
|
||||||
|
return strings.Join(parts, "/")
|
||||||
default:
|
default:
|
||||||
return stringFromMap(row, field)
|
return stringFromMap(row, field)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -543,7 +543,12 @@
|
|||||||
flat
|
flat
|
||||||
bordered
|
bordered
|
||||||
row-key="row_key"
|
row-key="row_key"
|
||||||
class="performance-table product-breakdown-table bg-white"
|
:class="[
|
||||||
|
'performance-table',
|
||||||
|
'product-breakdown-table',
|
||||||
|
'bg-white',
|
||||||
|
activeTab === 'product_detail' ? 'product-detail-table' : 'product-summary-table'
|
||||||
|
]"
|
||||||
:rows="displayProductKpiTableRows"
|
:rows="displayProductKpiTableRows"
|
||||||
:columns="activeProductColumns"
|
:columns="activeProductColumns"
|
||||||
:loading="loading || backendGroupedLoading"
|
:loading="loading || backendGroupedLoading"
|
||||||
@@ -984,7 +989,13 @@
|
|||||||
flat
|
flat
|
||||||
bordered
|
bordered
|
||||||
row-key="row_key"
|
row-key="row_key"
|
||||||
class="performance-table sticky-dim-table sticky-dim-9 bg-white"
|
:class="[
|
||||||
|
'performance-table',
|
||||||
|
'sticky-dim-table',
|
||||||
|
salesBreakdownStickyClass,
|
||||||
|
activeTab === 'sales_color_yaka_market_customer' ? 'sales-color-yaka-table' : '',
|
||||||
|
'bg-white'
|
||||||
|
]"
|
||||||
:rows="displayActiveSalesBreakdownTableRows"
|
:rows="displayActiveSalesBreakdownTableRows"
|
||||||
:columns="visibleSalesBreakdownColumns"
|
:columns="visibleSalesBreakdownColumns"
|
||||||
:loading="loading || backendGroupedLoading"
|
:loading="loading || backendGroupedLoading"
|
||||||
@@ -1564,8 +1575,8 @@ const expandedGroups = ref({})
|
|||||||
const lastManualExpandedGroupKey = ref('')
|
const lastManualExpandedGroupKey = ref('')
|
||||||
const selectedExpandLevelKeysByTab = reactive({
|
const selectedExpandLevelKeysByTab = reactive({
|
||||||
products: ['urun_ilk_grubu', 'askili_yan', 'kategori', 'urun_ana_grubu', 'urun_alt_grubu'],
|
products: ['urun_ilk_grubu', 'askili_yan', 'kategori', 'urun_ana_grubu', 'urun_alt_grubu'],
|
||||||
product_detail: ['urun_ana_grubu', 'urun_alt_grubu', 'product_code', 'color_code', 'yaka_kodu'],
|
product_detail: ['urun_ana_grubu', 'urun_alt_grubu', 'product_code', 'color_yaka'],
|
||||||
sales_color_yaka_market_customer: ['color_code', 'yaka_kodu', 'urun_ilk_grubu', 'askili_yan'],
|
sales_color_yaka_market_customer: ['color_yaka', 'urun_ilk_grubu', 'askili_yan'],
|
||||||
idle: ['urun_ilk_grubu', 'askili_yan', 'kategori', 'urun_ana_grubu', 'urun_alt_grubu'],
|
idle: ['urun_ilk_grubu', 'askili_yan', 'kategori', 'urun_ana_grubu', 'urun_alt_grubu'],
|
||||||
sales_product_country_segment_market_customer: ['market_key', 'customer_code', 'customer_name', 'urun_ilk_grubu', 'askili_yan'],
|
sales_product_country_segment_market_customer: ['market_key', 'customer_code', 'customer_name', 'urun_ilk_grubu', 'askili_yan'],
|
||||||
sales_market_customer_product: ['market_key', 'customer_code', 'customer_name', 'urun_ilk_grubu', 'askili_yan'],
|
sales_market_customer_product: ['market_key', 'customer_code', 'customer_name', 'urun_ilk_grubu', 'askili_yan'],
|
||||||
@@ -1656,13 +1667,13 @@ const columns = [
|
|||||||
{ name: 'yaka_kodu', label: 'Yaka', field: 'yaka_kodu', align: 'left' },
|
{ name: 'yaka_kodu', label: 'Yaka', field: 'yaka_kodu', align: 'left' },
|
||||||
{ name: 'market_key', label: 'Piyasa', field: row => displayMarketName(row.market_key), align: 'left', sortable: true },
|
{ name: 'market_key', label: 'Piyasa', field: row => displayMarketName(row.market_key), align: 'left', sortable: true },
|
||||||
{ name: 'stock_qty', label: 'Stok', field: row => formatNumber(row.stock_qty, 0), align: 'right', sortable: true },
|
{ name: 'stock_qty', label: 'Stok', field: row => formatNumber(row.stock_qty, 0), align: 'right', sortable: true },
|
||||||
{ name: 'performance_score_90d', label: '90G Skor', field: row => formatNumber(row.performance_score_90d, 1), align: 'right', sortable: true },
|
{ name: 'performance_score_90d', label: '90G Skor', field: row => formatNumber(row.performance_score_90d, 2), align: 'right', sortable: true },
|
||||||
{ name: 'sales_qty_90d', label: '90G Satış', field: row => formatNumber(row.sales_qty_90d, 0), align: 'right', sortable: true },
|
{ name: 'sales_qty_90d', label: '90G Satış', field: row => formatNumber(row.sales_qty_90d, 0), align: 'right', sortable: true },
|
||||||
{ name: 'performance_score_180d', label: '180G Skor', field: row => formatNumber(row.performance_score_180d, 1), align: 'right', sortable: true },
|
{ name: 'performance_score_180d', label: '180G Skor', field: row => formatNumber(row.performance_score_180d, 2), align: 'right', sortable: true },
|
||||||
{ name: 'sales_qty_180d', label: '180G Satış', field: row => formatNumber(row.sales_qty_180d, 0), align: 'right', sortable: true },
|
{ name: 'sales_qty_180d', label: '180G Satış', field: row => formatNumber(row.sales_qty_180d, 0), align: 'right', sortable: true },
|
||||||
{ name: 'performance_score_365d', label: '360G Skor', field: row => formatNumber(row.performance_score_365d, 1), align: 'right', sortable: true },
|
{ name: 'performance_score_365d', label: '360G Skor', field: row => formatNumber(row.performance_score_365d, 2), align: 'right', sortable: true },
|
||||||
{ name: 'sales_qty_365d', label: '360G Satış', field: row => formatNumber(row.sales_qty_365d, 0), align: 'right', sortable: true },
|
{ name: 'sales_qty_365d', label: '360G Satış', field: row => formatNumber(row.sales_qty_365d, 0), align: 'right', sortable: true },
|
||||||
{ name: 'performance_score_total', label: 'Genel Skor', field: row => formatNumber(row.performance_score_total, 1), align: 'right', sortable: true },
|
{ name: 'performance_score_total', label: 'Genel Skor', field: row => formatNumber(row.performance_score_total, 2), align: 'right', sortable: true },
|
||||||
{ name: 'sales_qty_total', label: 'Genel Satış', field: row => formatNumber(row.sales_qty_total, 0), align: 'right', sortable: true },
|
{ name: 'sales_qty_total', label: 'Genel Satış', field: row => formatNumber(row.sales_qty_total, 0), align: 'right', sortable: true },
|
||||||
{ name: 'sales_usd_90d', label: '90G USD', field: 'sales_usd_90d', align: 'right', sortable: true },
|
{ name: 'sales_usd_90d', label: '90G USD', field: 'sales_usd_90d', align: 'right', sortable: true },
|
||||||
{ name: 'sales_usd_180d', label: '180G USD', field: 'sales_usd_180d', align: 'right', sortable: true },
|
{ name: 'sales_usd_180d', label: '180G USD', field: 'sales_usd_180d', align: 'right', sortable: true },
|
||||||
@@ -1703,7 +1714,7 @@ const columns = [
|
|||||||
{ name: 'customer_count_total', label: 'Genel Müşteri', field: row => formatNumber(row.customer_count_total, 0), align: 'right', sortable: true },
|
{ name: 'customer_count_total', label: 'Genel Müşteri', field: row => formatNumber(row.customer_count_total, 0), align: 'right', sortable: true },
|
||||||
{ name: 'sales_index_90d', label: 'Piyasa End.', field: row => formatNumber(row.sales_index_90d, 2), align: 'right', sortable: true },
|
{ name: 'sales_index_90d', label: 'Piyasa End.', field: row => formatNumber(row.sales_index_90d, 2), align: 'right', sortable: true },
|
||||||
{ name: 'sales_index_total', label: 'Genel Endeks', field: row => formatNumber(row.sales_index_total, 2), align: 'right', sortable: true },
|
{ name: 'sales_index_total', label: 'Genel Endeks', field: row => formatNumber(row.sales_index_total, 2), align: 'right', sortable: true },
|
||||||
{ name: 'performance_score', label: 'Skor', field: row => formatNumber(row.performance_score, 1), align: 'right', sortable: true },
|
{ name: 'performance_score', label: 'Skor', field: row => formatNumber(row.performance_score, 2), align: 'right', sortable: true },
|
||||||
{ name: 'performance_bucket', label: 'Durum', field: 'performance_bucket', align: 'left' },
|
{ name: 'performance_bucket', label: 'Durum', field: 'performance_bucket', align: 'left' },
|
||||||
{ name: 'recommendation', label: 'Öneri', field: 'recommendation', align: 'left' }
|
{ name: 'recommendation', label: 'Öneri', field: 'recommendation', align: 'left' }
|
||||||
]
|
]
|
||||||
@@ -1895,7 +1906,7 @@ const generalColumns = [
|
|||||||
{ name: 'customer_count_total', label: 'Toplam Müşteri', field: row => formatNumber(row.customer_count_total, 0), align: 'right', sortable: true },
|
{ name: 'customer_count_total', label: 'Toplam Müşteri', field: row => formatNumber(row.customer_count_total, 0), align: 'right', sortable: true },
|
||||||
{ name: 'invoice_count_total', label: 'Fatura', field: row => formatNumber(row.invoice_count_total, 0), align: 'right', sortable: true },
|
{ name: 'invoice_count_total', label: 'Fatura', field: row => formatNumber(row.invoice_count_total, 0), align: 'right', sortable: true },
|
||||||
{ name: 'sales_index_total', label: 'Genel Endeks', field: row => formatNumber(row.sales_index_total, 2), align: 'right', sortable: true },
|
{ name: 'sales_index_total', label: 'Genel Endeks', field: row => formatNumber(row.sales_index_total, 2), align: 'right', sortable: true },
|
||||||
{ name: 'performance_score', label: 'Genel Skor', field: row => formatNumber(row.performance_score, 1), align: 'right', sortable: true },
|
{ name: 'performance_score', label: 'Genel Skor', field: row => formatNumber(row.performance_score, 2), align: 'right', sortable: true },
|
||||||
{ name: 'performance_bucket', label: 'Durum', field: 'performance_bucket', align: 'left', sortable: true },
|
{ name: 'performance_bucket', label: 'Durum', field: 'performance_bucket', align: 'left', sortable: true },
|
||||||
{ name: 'first_sale_date', label: 'İlk Satış', field: 'first_sale_date', align: 'left', sortable: true },
|
{ name: 'first_sale_date', label: 'İlk Satış', field: 'first_sale_date', align: 'left', sortable: true },
|
||||||
{ name: 'last_sale_date', label: 'Son Satış', field: 'last_sale_date', align: 'left', sortable: true },
|
{ name: 'last_sale_date', label: 'Son Satış', field: 'last_sale_date', align: 'left', sortable: true },
|
||||||
@@ -2089,7 +2100,7 @@ const customerColumns = [
|
|||||||
{ name: 'sales_qty_90d', label: '90G Adet', field: row => formatNumber(row.sales_qty_90d, 0), align: 'right', sortable: true },
|
{ name: 'sales_qty_90d', label: '90G Adet', field: row => formatNumber(row.sales_qty_90d, 0), align: 'right', sortable: true },
|
||||||
{ name: 'sales_usd_90d', label: '90G USD', field: 'sales_usd_90d', align: 'right', sortable: true },
|
{ name: 'sales_usd_90d', label: '90G USD', field: 'sales_usd_90d', align: 'right', sortable: true },
|
||||||
{ name: 'avg_price_usd_90d', label: 'Ort. USD', field: 'avg_price_usd_90d', align: 'right', sortable: true },
|
{ name: 'avg_price_usd_90d', label: 'Ort. USD', field: 'avg_price_usd_90d', align: 'right', sortable: true },
|
||||||
{ name: 'customer_score_90d', label: '90G Müşteri Skor', field: row => formatNumber(row.customer_score_90d, 1), align: 'right', sortable: true },
|
{ name: 'customer_score_90d', label: '90G Müşteri Skor', field: row => formatNumber(row.customer_score_90d, 2), align: 'right', sortable: true },
|
||||||
{ name: 'base_price_usd_90d', label: '90G Taban', field: row => formatMoney(row.base_price_usd_90d, 'USD'), align: 'right', sortable: true },
|
{ name: 'base_price_usd_90d', label: '90G Taban', field: row => formatMoney(row.base_price_usd_90d, 'USD'), align: 'right', sortable: true },
|
||||||
{ name: 'cost_price_usd_90d', label: '90G Çıplak', field: row => formatMoney(row.cost_price_usd_90d, 'USD'), align: 'right', sortable: true },
|
{ name: 'cost_price_usd_90d', label: '90G Çıplak', field: row => formatMoney(row.cost_price_usd_90d, 'USD'), align: 'right', sortable: true },
|
||||||
{ name: 'gross_profit_base_usd_90d', label: '90G Taban K/Z', field: row => formatMoney(row.gross_profit_base_usd_90d, 'USD'), align: 'right', sortable: true },
|
{ name: 'gross_profit_base_usd_90d', label: '90G Taban K/Z', field: row => formatMoney(row.gross_profit_base_usd_90d, 'USD'), align: 'right', sortable: true },
|
||||||
@@ -2137,7 +2148,7 @@ const salesBreakdownColumns = [
|
|||||||
{ name: 'gross_profit_cost_usd_180d', label: '180G Çıplak K/Z', field: row => formatMoney(row.gross_profit_cost_usd_180d, 'USD'), align: 'right', sortable: true },
|
{ name: 'gross_profit_cost_usd_180d', label: '180G Çıplak K/Z', field: row => formatMoney(row.gross_profit_cost_usd_180d, 'USD'), align: 'right', sortable: true },
|
||||||
{ name: 'gross_margin_base_180d', label: '180G Taban Marj', field: row => formatPercent(row.gross_margin_base_180d), align: 'right', sortable: true },
|
{ name: 'gross_margin_base_180d', label: '180G Taban Marj', field: row => formatPercent(row.gross_margin_base_180d), align: 'right', sortable: true },
|
||||||
{ name: 'gross_margin_cost_180d', label: '180G Çıplak Marj', field: row => formatPercent(row.gross_margin_cost_180d), align: 'right', sortable: true },
|
{ name: 'gross_margin_cost_180d', label: '180G Çıplak Marj', field: row => formatPercent(row.gross_margin_cost_180d), align: 'right', sortable: true },
|
||||||
{ name: 'customer_score_180d', label: '180G Müşteri Skor', field: row => formatNumber(row.customer_score_180d, 1), align: 'right', sortable: true },
|
{ name: 'customer_score_180d', label: '180G Müşteri Skor', field: row => formatNumber(row.customer_score_180d, 2), align: 'right', sortable: true },
|
||||||
{ name: 'sales_qty_365d', label: '360G Adet', field: row => formatNumber(row.sales_qty_365d, 0), align: 'right', sortable: true },
|
{ name: 'sales_qty_365d', label: '360G Adet', field: row => formatNumber(row.sales_qty_365d, 0), align: 'right', sortable: true },
|
||||||
{ name: 'sales_usd_365d', label: '360G USD', field: row => formatMoney(row.sales_usd_365d, 'USD'), align: 'right', sortable: true },
|
{ name: 'sales_usd_365d', label: '360G USD', field: row => formatMoney(row.sales_usd_365d, 'USD'), align: 'right', sortable: true },
|
||||||
{ name: 'avg_price_usd_365d', label: '360G Ort. USD', field: row => formatMoney(row.avg_price_usd_365d, 'USD'), align: 'right', sortable: true },
|
{ name: 'avg_price_usd_365d', label: '360G Ort. USD', field: row => formatMoney(row.avg_price_usd_365d, 'USD'), align: 'right', sortable: true },
|
||||||
@@ -2148,7 +2159,7 @@ const salesBreakdownColumns = [
|
|||||||
{ name: 'gross_profit_cost_usd_365d', label: '360G Çıplak K/Z', field: row => formatMoney(row.gross_profit_cost_usd_365d, 'USD'), align: 'right', sortable: true },
|
{ name: 'gross_profit_cost_usd_365d', label: '360G Çıplak K/Z', field: row => formatMoney(row.gross_profit_cost_usd_365d, 'USD'), align: 'right', sortable: true },
|
||||||
{ name: 'gross_margin_base_365d', label: '360G Taban Marj', field: row => formatPercent(row.gross_margin_base_365d), align: 'right', sortable: true },
|
{ name: 'gross_margin_base_365d', label: '360G Taban Marj', field: row => formatPercent(row.gross_margin_base_365d), align: 'right', sortable: true },
|
||||||
{ name: 'gross_margin_cost_365d', label: '360G Çıplak Marj', field: row => formatPercent(row.gross_margin_cost_365d), align: 'right', sortable: true },
|
{ name: 'gross_margin_cost_365d', label: '360G Çıplak Marj', field: row => formatPercent(row.gross_margin_cost_365d), align: 'right', sortable: true },
|
||||||
{ name: 'customer_score_365d', label: '360G Müşteri Skor', field: row => formatNumber(row.customer_score_365d, 1), align: 'right', sortable: true },
|
{ name: 'customer_score_365d', label: '360G Müşteri Skor', field: row => formatNumber(row.customer_score_365d, 2), align: 'right', sortable: true },
|
||||||
{ name: 'customer_count_total', label: 'Genel Müşteri', field: row => formatNumber(row.customer_count_total, 0), align: 'right', sortable: true },
|
{ name: 'customer_count_total', label: 'Genel Müşteri', field: row => formatNumber(row.customer_count_total, 0), align: 'right', sortable: true },
|
||||||
{ name: 'invoice_count_total', label: 'Genel Fatura', field: row => formatNumber(row.invoice_count_total, 0), align: 'right', sortable: true },
|
{ name: 'invoice_count_total', label: 'Genel Fatura', field: row => formatNumber(row.invoice_count_total, 0), align: 'right', sortable: true },
|
||||||
{ name: 'sales_qty_total', label: 'Genel Adet', field: row => formatNumber(row.sales_qty_total, 0), align: 'right', sortable: true },
|
{ name: 'sales_qty_total', label: 'Genel Adet', field: row => formatNumber(row.sales_qty_total, 0), align: 'right', sortable: true },
|
||||||
@@ -2161,26 +2172,26 @@ const salesBreakdownColumns = [
|
|||||||
{ name: 'gross_profit_cost_usd_total', label: 'Genel Çıplak K/Z', field: row => formatMoney(row.gross_profit_cost_usd_total, 'USD'), align: 'right', sortable: true },
|
{ name: 'gross_profit_cost_usd_total', label: 'Genel Çıplak K/Z', field: row => formatMoney(row.gross_profit_cost_usd_total, 'USD'), align: 'right', sortable: true },
|
||||||
{ name: 'gross_margin_base_total', label: 'Genel Taban Marj', field: row => formatPercent(row.gross_margin_base_total), align: 'right', sortable: true },
|
{ name: 'gross_margin_base_total', label: 'Genel Taban Marj', field: row => formatPercent(row.gross_margin_base_total), align: 'right', sortable: true },
|
||||||
{ name: 'gross_margin_cost_total', label: 'Genel Çıplak Marj', field: row => formatPercent(row.gross_margin_cost_total), align: 'right', sortable: true },
|
{ name: 'gross_margin_cost_total', label: 'Genel Çıplak Marj', field: row => formatPercent(row.gross_margin_cost_total), align: 'right', sortable: true },
|
||||||
{ name: 'customer_score_total', label: 'Genel Müşteri Skor', field: row => formatNumber(row.customer_score_total, 1), align: 'right', sortable: true },
|
{ name: 'customer_score_total', label: 'Genel Müşteri Skor', field: row => formatNumber(row.customer_score_total, 2), align: 'right', sortable: true },
|
||||||
{ name: 'sales_index_90d', label: 'Endeks', field: row => formatNumber(row.sales_index_90d, 2), align: 'right', sortable: true },
|
{ name: 'sales_index_90d', label: 'Endeks', field: row => formatNumber(row.sales_index_90d, 2), align: 'right', sortable: true },
|
||||||
{ name: 'performance_score', label: 'Skor', field: row => formatNumber(row.performance_score, 1), align: 'right', sortable: true },
|
{ name: 'performance_score', label: 'Skor', field: row => formatNumber(row.performance_score, 2), align: 'right', sortable: true },
|
||||||
{ name: 'performance_bucket', label: 'Durum', field: 'performance_bucket', align: 'left', sortable: true },
|
{ name: 'performance_bucket', label: 'Durum', field: 'performance_bucket', align: 'left', sortable: true },
|
||||||
{ name: 'last_sale_date', label: 'Son Satış', field: 'last_sale_date', align: 'left', sortable: true },
|
{ name: 'last_sale_date', label: 'Son Satış', field: 'last_sale_date', align: 'left', sortable: true },
|
||||||
{ name: 'recommendation', label: 'Öneri', field: 'recommendation', align: 'left' }
|
{ name: 'recommendation', label: 'Öneri', field: 'recommendation', align: 'left' }
|
||||||
]
|
]
|
||||||
|
|
||||||
const productPeriodScoreColumns = [
|
const productPeriodScoreColumns = [
|
||||||
{ name: 'performance_score_90d', label: '90G Ürün Skor', field: row => formatNumber(row.performance_score_90d, 1), align: 'right', sortable: true },
|
{ name: 'performance_score_90d', label: '90G Ürün Skor', field: row => formatNumber(row.performance_score_90d, 2), align: 'right', sortable: true },
|
||||||
{ name: 'performance_score_180d', label: '180G Ürün Skor', field: row => formatNumber(row.performance_score_180d, 1), align: 'right', sortable: true },
|
{ name: 'performance_score_180d', label: '180G Ürün Skor', field: row => formatNumber(row.performance_score_180d, 2), align: 'right', sortable: true },
|
||||||
{ name: 'performance_score_365d', label: '360G Ürün Skor', field: row => formatNumber(row.performance_score_365d, 1), align: 'right', sortable: true },
|
{ name: 'performance_score_365d', label: '360G Ürün Skor', field: row => formatNumber(row.performance_score_365d, 2), align: 'right', sortable: true },
|
||||||
{ name: 'performance_score_total', label: 'Genel Ürün Skor', field: row => formatNumber(row.performance_score_total, 1), align: 'right', sortable: true }
|
{ name: 'performance_score_total', label: 'Genel Ürün Skor', field: row => formatNumber(row.performance_score_total, 2), align: 'right', sortable: true }
|
||||||
]
|
]
|
||||||
|
|
||||||
const customerPeriodScoreColumns = [
|
const customerPeriodScoreColumns = [
|
||||||
{ name: 'customer_score_90d', label: '90G Müşteri Skor', field: row => formatNumber(row.customer_score_90d, 1), align: 'right', sortable: true },
|
{ name: 'customer_score_90d', label: '90G Müşteri Skor', field: row => formatNumber(row.customer_score_90d, 2), align: 'right', sortable: true },
|
||||||
{ name: 'customer_score_180d', label: '180G Müşteri Skor', field: row => formatNumber(row.customer_score_180d, 1), align: 'right', sortable: true },
|
{ name: 'customer_score_180d', label: '180G Müşteri Skor', field: row => formatNumber(row.customer_score_180d, 2), align: 'right', sortable: true },
|
||||||
{ name: 'customer_score_365d', label: '360G Müşteri Skor', field: row => formatNumber(row.customer_score_365d, 1), align: 'right', sortable: true },
|
{ name: 'customer_score_365d', label: '360G Müşteri Skor', field: row => formatNumber(row.customer_score_365d, 2), align: 'right', sortable: true },
|
||||||
{ name: 'customer_score_total', label: 'Genel Müşteri Skor', field: row => formatNumber(row.customer_score_total, 1), align: 'right', sortable: true }
|
{ name: 'customer_score_total', label: 'Genel Müşteri Skor', field: row => formatNumber(row.customer_score_total, 2), align: 'right', sortable: true }
|
||||||
]
|
]
|
||||||
|
|
||||||
function ensureColumns (targetColumns, columnsToEnsure) {
|
function ensureColumns (targetColumns, columnsToEnsure) {
|
||||||
@@ -2278,7 +2289,27 @@ function ensureColorYakaColumn (targetColumns) {
|
|||||||
attachColumnSorts(detailStockColumns)
|
attachColumnSorts(detailStockColumns)
|
||||||
attachColumnSorts(detailSalesColumns)
|
attachColumnSorts(detailSalesColumns)
|
||||||
|
|
||||||
const visibleSalesBreakdownColumns = computed(() => orderedMetricColumns(salesBreakdownColumns.filter(visiblePeriodColumn)))
|
const visibleSalesBreakdownColumns = computed(() => {
|
||||||
|
const base = orderedMetricColumns(salesBreakdownColumns.filter(visiblePeriodColumn))
|
||||||
|
if (activeTab.value !== 'sales_color_yaka_market_customer') return base
|
||||||
|
|
||||||
|
const priority = ['performance_bucket', 'recommendation']
|
||||||
|
const pulled = priority
|
||||||
|
.map(name => base.find(col => col.name === name))
|
||||||
|
.filter(Boolean)
|
||||||
|
const rest = base.filter(col => !priority.includes(col.name))
|
||||||
|
const insertAfter = Math.max(
|
||||||
|
rest.findIndex(col => col.name === 'item_description'),
|
||||||
|
rest.findIndex(col => col.name === 'product_code')
|
||||||
|
)
|
||||||
|
if (insertAfter < 0) return [...pulled, ...rest]
|
||||||
|
return [
|
||||||
|
...rest.slice(0, insertAfter + 1),
|
||||||
|
...pulled,
|
||||||
|
...rest.slice(insertAfter + 1)
|
||||||
|
]
|
||||||
|
})
|
||||||
|
const salesBreakdownStickyClass = computed(() => activeTab.value === 'sales_color_yaka_market_customer' ? 'sticky-dim-9' : 'sticky-dim-9')
|
||||||
|
|
||||||
const summaryCards = computed(() => [
|
const summaryCards = computed(() => [
|
||||||
{ key: 'date', label: 'KPI Tarihi', value: summary.value.kpi_date || '-' },
|
{ key: 'date', label: 'KPI Tarihi', value: summary.value.kpi_date || '-' },
|
||||||
@@ -2352,7 +2383,7 @@ const visibleOrderAnalysisColumns = computed(() => {
|
|||||||
function performanceCardsForRow (row) {
|
function performanceCardsForRow (row) {
|
||||||
const source = row || {}
|
const source = row || {}
|
||||||
return [
|
return [
|
||||||
{ key: 'score', label: 'Skor', value: formatNumber(source.performance_score, 1) },
|
{ key: 'score', label: 'Skor', value: formatNumber(source.performance_score, 2) },
|
||||||
{ key: 'stock', label: 'Stok', value: formatNumber(source.stock_qty, 0) },
|
{ key: 'stock', label: 'Stok', value: formatNumber(source.stock_qty, 0) },
|
||||||
{ key: 'sales90', label: '90G Satış', value: formatNumber(source.sales_qty_90d, 0) },
|
{ key: 'sales90', label: '90G Satış', value: formatNumber(source.sales_qty_90d, 0) },
|
||||||
{ key: 'sales180', label: '180G Satış', value: formatNumber(source.sales_qty_180d, 0) },
|
{ key: 'sales180', label: '180G Satış', value: formatNumber(source.sales_qty_180d, 0) },
|
||||||
@@ -2411,13 +2442,11 @@ const tabGroupLevels = {
|
|||||||
{ key: 'urun_ana_grubu', label: 'Ürün Ana Grubu' },
|
{ key: 'urun_ana_grubu', label: 'Ürün Ana Grubu' },
|
||||||
{ key: 'urun_alt_grubu', label: 'Ürün Alt Grubu' },
|
{ key: 'urun_alt_grubu', label: 'Ürün Alt Grubu' },
|
||||||
{ key: 'product_code', label: 'Ürün' },
|
{ key: 'product_code', label: 'Ürün' },
|
||||||
{ key: 'color_code', label: 'Renk' },
|
{ key: 'color_yaka', label: 'Renk/Yaka' },
|
||||||
{ key: 'yaka_kodu', label: 'Yaka' },
|
|
||||||
{ key: 'market_key', label: 'Piyasa' }
|
{ key: 'market_key', label: 'Piyasa' }
|
||||||
],
|
],
|
||||||
sales_color_yaka_market_customer: [
|
sales_color_yaka_market_customer: [
|
||||||
{ key: 'color_code', label: 'Renk' },
|
{ key: 'color_yaka', label: 'Renk/Yaka' },
|
||||||
{ key: 'yaka_kodu', label: 'Yaka' },
|
|
||||||
{ key: 'urun_ilk_grubu', label: 'Ürün İlk Grubu' },
|
{ key: 'urun_ilk_grubu', label: 'Ürün İlk Grubu' },
|
||||||
{ key: 'askili_yan', label: 'Askılı/Yan' },
|
{ key: 'askili_yan', label: 'Askılı/Yan' },
|
||||||
{ key: 'kategori', label: 'Kategori' },
|
{ key: 'kategori', label: 'Kategori' },
|
||||||
@@ -2531,7 +2560,8 @@ const selectedExpandThroughLevel = computed(() => {
|
|||||||
|
|
||||||
const autoExpandThroughLevel = computed(() => {
|
const autoExpandThroughLevel = computed(() => {
|
||||||
const selectedLevel = selectedExpandThroughLevel.value
|
const selectedLevel = selectedExpandThroughLevel.value
|
||||||
return selectedLevel >= 0 ? selectedLevel : activeGroupLevels.value.length - 1
|
const lastGroupLevel = Math.max(0, activeGroupLevels.value.length - 2)
|
||||||
|
return selectedLevel >= 0 ? Math.min(selectedLevel, lastGroupLevel) : Math.min(2, lastGroupLevel)
|
||||||
})
|
})
|
||||||
|
|
||||||
const tableFilterSourceMap = computed(() => ({
|
const tableFilterSourceMap = computed(() => ({
|
||||||
@@ -2880,8 +2910,8 @@ function makeGroupRow (key, level, groupDef, value, groupRows) {
|
|||||||
avg_price_usd_180d: weightedAverage(groupRows, 'sales_usd_180d', 'sales_qty_180d'),
|
avg_price_usd_180d: weightedAverage(groupRows, 'sales_usd_180d', 'sales_qty_180d'),
|
||||||
avg_price_usd_365d: weightedAverage(groupRows, 'sales_usd_365d', 'sales_qty_365d'),
|
avg_price_usd_365d: weightedAverage(groupRows, 'sales_usd_365d', 'sales_qty_365d'),
|
||||||
avg_price_usd_total: weightedAverage(groupRows, 'sales_usd_total', 'sales_qty_total'),
|
avg_price_usd_total: weightedAverage(groupRows, 'sales_usd_total', 'sales_qty_total'),
|
||||||
base_price_usd: weightedAverageOrAverage(groupRows, 'base_price_usd', 'sales_qty_90d'),
|
base_price_usd: weightedAverageProductCost(groupRows, 'base_price_usd'),
|
||||||
cost_price_usd: weightedAverageOrAverage(groupRows, 'cost_price_usd', 'sales_qty_90d'),
|
cost_price_usd: weightedAverageProductCost(groupRows, 'cost_price_usd'),
|
||||||
unit_profit_base_90d: weightedAverageOrAverage(groupRows, 'unit_profit_base_90d', 'sales_qty_90d'),
|
unit_profit_base_90d: weightedAverageOrAverage(groupRows, 'unit_profit_base_90d', 'sales_qty_90d'),
|
||||||
unit_profit_cost_90d: weightedAverageOrAverage(groupRows, 'unit_profit_cost_90d', 'sales_qty_90d'),
|
unit_profit_cost_90d: weightedAverageOrAverage(groupRows, 'unit_profit_cost_90d', 'sales_qty_90d'),
|
||||||
unit_profit_base_180d: weightedAverageOrAverage(groupRows, 'unit_profit_base_180d', 'sales_qty_180d'),
|
unit_profit_base_180d: weightedAverageOrAverage(groupRows, 'unit_profit_base_180d', 'sales_qty_180d'),
|
||||||
@@ -2904,7 +2934,7 @@ function makeGroupRow (key, level, groupDef, value, groupRows) {
|
|||||||
performance_bucket: dominantValue(groupRows, 'performance_bucket'),
|
performance_bucket: dominantValue(groupRows, 'performance_bucket'),
|
||||||
...aggregate,
|
...aggregate,
|
||||||
performance_score: groupPerformanceScore(aggregate, groupDef.key),
|
performance_score: groupPerformanceScore(aggregate, groupDef.key),
|
||||||
recommendation: `${formatNumber(groupRows.length, 0)} satır`
|
recommendation: dominantValue(groupRows, 'recommendation') || `${formatNumber(groupRows.length, 0)} satır`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2925,6 +2955,10 @@ function aggregateGroupFields (sourceRows, groupField = '') {
|
|||||||
out[field] = distinctVariantStockCost(sourceRows)
|
out[field] = distinctVariantStockCost(sourceRows)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if (field === 'base_price_usd' || field === 'cost_price_usd') {
|
||||||
|
out[field] = weightedAverageProductCost(sourceRows, field)
|
||||||
|
continue
|
||||||
|
}
|
||||||
if (shouldSumField(field)) {
|
if (shouldSumField(field)) {
|
||||||
out[field] = sumRows(sourceRows, field)
|
out[field] = sumRows(sourceRows, field)
|
||||||
} else if (shouldAverageField(field)) {
|
} else if (shouldAverageField(field)) {
|
||||||
@@ -3205,6 +3239,38 @@ function weightedAverageOrAverage (sourceRows, valueField, qtyField) {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function weightedAverageProductCost (sourceRows, valueField) {
|
||||||
|
const salesRows = sourceRows
|
||||||
|
.map(row => ({
|
||||||
|
value: Number(row[valueField] || 0),
|
||||||
|
qty: Number(row.sales_qty_total || 0)
|
||||||
|
}))
|
||||||
|
.filter(row => Number.isFinite(row.value) && Number.isFinite(row.qty) && row.value > 0 && row.qty > 0)
|
||||||
|
const salesQty = salesRows.reduce((sum, row) => sum + row.qty, 0)
|
||||||
|
if (salesQty > 0) {
|
||||||
|
return salesRows.reduce((sum, row) => sum + row.value * row.qty, 0) / salesQty
|
||||||
|
}
|
||||||
|
|
||||||
|
const variants = new Map()
|
||||||
|
for (const row of sourceRows) {
|
||||||
|
const key = productVariantStockKey(row)
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
function marginFromSalesCost (salesUSD, qty, unitCost) {
|
function marginFromSalesCost (salesUSD, qty, unitCost) {
|
||||||
const sales = Number(salesUSD || 0)
|
const sales = Number(salesUSD || 0)
|
||||||
if (!sales) return 0
|
if (!sales) return 0
|
||||||
@@ -3280,13 +3346,13 @@ function expandSelectedProductGroups () {
|
|||||||
|
|
||||||
function groupLabelColumnName (row) {
|
function groupLabelColumnName (row) {
|
||||||
const field = String(row?.group_field || '')
|
const field = String(row?.group_field || '')
|
||||||
if (field === 'color_code' || field === 'yaka_kodu') return 'color_yaka'
|
if (field === 'color_code' || field === 'yaka_kodu' || field === 'color_yaka') return 'color_yaka'
|
||||||
return field || 'product_code'
|
return field || 'product_code'
|
||||||
}
|
}
|
||||||
|
|
||||||
function groupDisplayLabel (row) {
|
function groupDisplayLabel (row) {
|
||||||
const field = String(row?.group_field || '')
|
const field = String(row?.group_field || '')
|
||||||
if (field === 'color_code' || field === 'yaka_kodu') {
|
if (field === 'color_code' || field === 'yaka_kodu' || field === 'color_yaka') {
|
||||||
return colorYakaValue(row) || row?.label || row?.group_value || ''
|
return colorYakaValue(row) || row?.label || row?.group_value || ''
|
||||||
}
|
}
|
||||||
return row?.label || row?.group_value || ''
|
return row?.label || row?.group_value || ''
|
||||||
@@ -3303,13 +3369,13 @@ function hasProductVariantImageKey (row) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function canShowProductImage (row) {
|
function canShowProductImage (row) {
|
||||||
return activeTab.value === 'products' && hasProductVariantImageKey(row)
|
return isProductKpiTab.value && hasProductVariantImageKey(row)
|
||||||
}
|
}
|
||||||
|
|
||||||
function groupShowsImage (row) {
|
function groupShowsImage (row) {
|
||||||
return activeTab.value === 'products' &&
|
return isProductKpiTab.value &&
|
||||||
row?.__group &&
|
row?.__group &&
|
||||||
['product_code', 'color_code', 'yaka_kodu'].includes(String(row.group_field || '')) &&
|
['product_code', 'color_code', 'yaka_kodu', 'color_yaka'].includes(String(row.group_field || '')) &&
|
||||||
hasProductVariantImageKey(row)
|
hasProductVariantImageKey(row)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3328,7 +3394,7 @@ function formatGroupCell (row, colOrName) {
|
|||||||
const name = colOrName?.name || colOrName
|
const name = colOrName?.name || colOrName
|
||||||
if (name === 'image') return ''
|
if (name === 'image') return ''
|
||||||
if (name === 'recommendation') return row.recommendation || ''
|
if (name === 'recommendation') return row.recommendation || ''
|
||||||
if (name === 'color_yaka' && ['color_code', 'yaka_kodu'].includes(String(row?.group_field || ''))) return colorYakaValue(row)
|
if (name === 'color_yaka' && ['color_code', 'yaka_kodu', 'color_yaka'].includes(String(row?.group_field || ''))) return colorYakaValue(row)
|
||||||
if (groupDimensionFields.has(name) && name !== row?.group_field) return ''
|
if (groupDimensionFields.has(name) && name !== row?.group_field) return ''
|
||||||
return formatTableCell(row, colOrName)
|
return formatTableCell(row, colOrName)
|
||||||
}
|
}
|
||||||
@@ -3635,7 +3701,7 @@ function formatTableCell (row, colOrName) {
|
|||||||
if (isMoneyField(name)) return formatMoney(row[name], 'USD')
|
if (isMoneyField(name)) return formatMoney(row[name], 'USD')
|
||||||
if (isPercentField(name)) return formatPercent(row[name])
|
if (isPercentField(name)) return formatPercent(row[name])
|
||||||
if (isIntegerField(name)) return formatNumber(row[name], 0)
|
if (isIntegerField(name)) return formatNumber(row[name], 0)
|
||||||
if (isDecimalField(name)) return formatNumber(row[name], name === 'performance_score' ? 1 : 2)
|
if (isDecimalField(name)) return formatNumber(row[name], 2)
|
||||||
if (typeof col?.field === 'function') return col.field(row)
|
if (typeof col?.field === 'function') return col.field(row)
|
||||||
return formatProductCell(row, name)
|
return formatProductCell(row, name)
|
||||||
}
|
}
|
||||||
@@ -3679,7 +3745,7 @@ function formatProductCell (row, name) {
|
|||||||
case 'sales_index_90d':
|
case 'sales_index_90d':
|
||||||
case 'sales_index_total':
|
case 'sales_index_total':
|
||||||
case 'performance_score':
|
case 'performance_score':
|
||||||
return formatNumber(row[name], name === 'performance_score' ? 1 : 2)
|
return formatNumber(row[name], 2)
|
||||||
case 'sales_usd_90d':
|
case 'sales_usd_90d':
|
||||||
case 'sales_usd_180d':
|
case 'sales_usd_180d':
|
||||||
case 'sales_usd_365d':
|
case 'sales_usd_365d':
|
||||||
@@ -3955,7 +4021,7 @@ function markProductImageFailed (row) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function primeProductImages (sourceRows) {
|
async function primeProductImages (sourceRows) {
|
||||||
if (activeTab.value !== 'products') return
|
if (!isProductKpiTab.value) return
|
||||||
imagePrimeActiveRequests += 1
|
imagePrimeActiveRequests += 1
|
||||||
imagePrimeLoading.value = true
|
imagePrimeLoading.value = true
|
||||||
const items = []
|
const items = []
|
||||||
@@ -4570,6 +4636,15 @@ onMounted(reload)
|
|||||||
|
|
||||||
.performance-table :deep(.q-table__middle) {
|
.performance-table :deep(.q-table__middle) {
|
||||||
max-height: calc(100vh - 220px);
|
max-height: calc(100vh - 220px);
|
||||||
|
transform: rotateX(180deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.performance-table :deep(.q-table__middle > .q-table) {
|
||||||
|
transform: rotateX(180deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.performance-table :deep(.q-table__bottom) {
|
||||||
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.performance-table :deep(.q-table) {
|
.performance-table :deep(.q-table) {
|
||||||
@@ -4647,7 +4722,7 @@ onMounted(reload)
|
|||||||
.performance-table :deep(.q-table tbody td .q-field),
|
.performance-table :deep(.q-table tbody td .q-field),
|
||||||
.performance-table :deep(.q-table tbody td input),
|
.performance-table :deep(.q-table tbody td input),
|
||||||
.performance-table :deep(.q-table tbody td textarea) {
|
.performance-table :deep(.q-table tbody td textarea) {
|
||||||
min-width: 54px;
|
min-width: 72px;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4661,10 +4736,10 @@ onMounted(reload)
|
|||||||
|
|
||||||
.performance-table :deep(.q-table th.color-yaka-cell),
|
.performance-table :deep(.q-table th.color-yaka-cell),
|
||||||
.performance-table :deep(.q-table td.color-yaka-cell) {
|
.performance-table :deep(.q-table td.color-yaka-cell) {
|
||||||
width: 132px;
|
width: 170px;
|
||||||
min-width: 132px;
|
min-width: 170px;
|
||||||
max-width: 150px;
|
max-width: 190px;
|
||||||
font-size: 10px;
|
font-size: 10.5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.performance-table :deep(.q-table th:first-child),
|
.performance-table :deep(.q-table th:first-child),
|
||||||
@@ -4781,6 +4856,95 @@ onMounted(reload)
|
|||||||
box-shadow: 8px 0 10px -10px rgba(17, 24, 39, 0.45);
|
box-shadow: 8px 0 10px -10px rgba(17, 24, 39, 0.45);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.product-breakdown-table.product-detail-table :deep(.q-table) {
|
||||||
|
min-width: 2500px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-breakdown-table.product-detail-table :deep(.q-table td:nth-child(n+8)) {
|
||||||
|
position: static;
|
||||||
|
left: auto;
|
||||||
|
z-index: auto;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-breakdown-table.product-detail-table :deep(.q-table th:nth-child(n+8)) {
|
||||||
|
left: auto;
|
||||||
|
z-index: 1;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-breakdown-table.product-detail-table :deep(.q-table th:nth-child(-n+7)),
|
||||||
|
.product-breakdown-table.product-detail-table :deep(.q-table td:nth-child(-n+7)) {
|
||||||
|
position: sticky;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-breakdown-table.product-detail-table :deep(.q-table th:nth-child(-n+7)) {
|
||||||
|
z-index: 4;
|
||||||
|
background: #f8fbff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-breakdown-table.product-detail-table :deep(.q-table tbody tr:not(.group-row) td:nth-child(-n+7)) {
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-breakdown-table.product-detail-table :deep(.q-table th:nth-child(1)),
|
||||||
|
.product-breakdown-table.product-detail-table :deep(.q-table td:nth-child(1)) {
|
||||||
|
left: 0;
|
||||||
|
width: 72px;
|
||||||
|
min-width: 72px;
|
||||||
|
max-width: 72px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-breakdown-table.product-detail-table :deep(.q-table th:nth-child(2)),
|
||||||
|
.product-breakdown-table.product-detail-table :deep(.q-table td:nth-child(2)) {
|
||||||
|
left: 72px;
|
||||||
|
width: 150px;
|
||||||
|
min-width: 150px;
|
||||||
|
max-width: 150px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-breakdown-table.product-detail-table :deep(.q-table th:nth-child(3)),
|
||||||
|
.product-breakdown-table.product-detail-table :deep(.q-table td:nth-child(3)) {
|
||||||
|
left: 222px;
|
||||||
|
width: 150px;
|
||||||
|
min-width: 150px;
|
||||||
|
max-width: 150px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-breakdown-table.product-detail-table :deep(.q-table th:nth-child(4)),
|
||||||
|
.product-breakdown-table.product-detail-table :deep(.q-table td:nth-child(4)) {
|
||||||
|
left: 372px;
|
||||||
|
width: 130px;
|
||||||
|
min-width: 130px;
|
||||||
|
max-width: 130px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-breakdown-table.product-detail-table :deep(.q-table th:nth-child(5)),
|
||||||
|
.product-breakdown-table.product-detail-table :deep(.q-table td:nth-child(5)) {
|
||||||
|
left: 502px;
|
||||||
|
width: 170px;
|
||||||
|
min-width: 170px;
|
||||||
|
max-width: 170px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-breakdown-table.product-detail-table :deep(.q-table th:nth-child(6)),
|
||||||
|
.product-breakdown-table.product-detail-table :deep(.q-table td:nth-child(6)) {
|
||||||
|
left: 672px;
|
||||||
|
width: 170px;
|
||||||
|
min-width: 170px;
|
||||||
|
max-width: 190px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-breakdown-table.product-detail-table :deep(.q-table th:nth-child(7)),
|
||||||
|
.product-breakdown-table.product-detail-table :deep(.q-table td:nth-child(7)) {
|
||||||
|
left: 842px;
|
||||||
|
width: 120px;
|
||||||
|
min-width: 120px;
|
||||||
|
max-width: 120px;
|
||||||
|
box-shadow: 8px 0 10px -10px rgba(17, 24, 39, 0.45);
|
||||||
|
}
|
||||||
|
|
||||||
.sticky-dim-table :deep(.q-table) {
|
.sticky-dim-table :deep(.q-table) {
|
||||||
min-width: 1700px;
|
min-width: 1700px;
|
||||||
table-layout: fixed;
|
table-layout: fixed;
|
||||||
@@ -4797,12 +4961,17 @@ onMounted(reload)
|
|||||||
.sticky-dim-table :deep(.q-table td.text-right),
|
.sticky-dim-table :deep(.q-table td.text-right),
|
||||||
.product-breakdown-table :deep(.q-table th.text-right),
|
.product-breakdown-table :deep(.q-table th.text-right),
|
||||||
.product-breakdown-table :deep(.q-table td.text-right) {
|
.product-breakdown-table :deep(.q-table td.text-right) {
|
||||||
min-width: 98px;
|
min-width: 124px;
|
||||||
width: 98px;
|
width: 124px;
|
||||||
max-width: none;
|
max-width: none;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.product-breakdown-table :deep(.q-table td.text-right) {
|
||||||
|
font-size: 10.5px;
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
|
}
|
||||||
|
|
||||||
.product-breakdown-table :deep(.q-table th:nth-child(n+11):not(.text-right)),
|
.product-breakdown-table :deep(.q-table th:nth-child(n+11):not(.text-right)),
|
||||||
.product-breakdown-table :deep(.q-table td:nth-child(n+11):not(.text-right)),
|
.product-breakdown-table :deep(.q-table td:nth-child(n+11):not(.text-right)),
|
||||||
.sticky-dim-table.sticky-dim-3 :deep(.q-table th:nth-child(n+4):not(.text-right)),
|
.sticky-dim-table.sticky-dim-3 :deep(.q-table th:nth-child(n+4):not(.text-right)),
|
||||||
@@ -4939,6 +5108,63 @@ onMounted(reload)
|
|||||||
box-shadow: 8px 0 10px -10px rgba(17, 24, 39, 0.45);
|
box-shadow: 8px 0 10px -10px rgba(17, 24, 39, 0.45);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sales-color-yaka-table :deep(.q-table th:nth-child(1)),
|
||||||
|
.sales-color-yaka-table :deep(.q-table td:nth-child(1)) {
|
||||||
|
left: 0;
|
||||||
|
width: 170px;
|
||||||
|
min-width: 170px;
|
||||||
|
max-width: 190px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sales-color-yaka-table :deep(.q-table th:nth-child(2)),
|
||||||
|
.sales-color-yaka-table :deep(.q-table td:nth-child(2)) {
|
||||||
|
left: 170px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sales-color-yaka-table :deep(.q-table th:nth-child(3)),
|
||||||
|
.sales-color-yaka-table :deep(.q-table td:nth-child(3)) {
|
||||||
|
left: 288px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sales-color-yaka-table :deep(.q-table th:nth-child(4)),
|
||||||
|
.sales-color-yaka-table :deep(.q-table td:nth-child(4)) {
|
||||||
|
left: 406px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sales-color-yaka-table :deep(.q-table th:nth-child(5)),
|
||||||
|
.sales-color-yaka-table :deep(.q-table td:nth-child(5)) {
|
||||||
|
left: 524px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sales-color-yaka-table :deep(.q-table th:nth-child(6)),
|
||||||
|
.sales-color-yaka-table :deep(.q-table td:nth-child(6)) {
|
||||||
|
left: 642px;
|
||||||
|
width: 130px;
|
||||||
|
min-width: 130px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sales-color-yaka-table :deep(.q-table th:nth-child(7)),
|
||||||
|
.sales-color-yaka-table :deep(.q-table td:nth-child(7)) {
|
||||||
|
left: 772px;
|
||||||
|
width: 170px;
|
||||||
|
min-width: 170px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sales-color-yaka-table :deep(.q-table th:nth-child(8)),
|
||||||
|
.sales-color-yaka-table :deep(.q-table td:nth-child(8)) {
|
||||||
|
left: 942px;
|
||||||
|
width: 116px;
|
||||||
|
min-width: 116px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sales-color-yaka-table :deep(.q-table th:nth-child(9)),
|
||||||
|
.sales-color-yaka-table :deep(.q-table td:nth-child(9)) {
|
||||||
|
left: 1058px;
|
||||||
|
width: 190px;
|
||||||
|
min-width: 190px;
|
||||||
|
box-shadow: 8px 0 10px -10px rgba(17, 24, 39, 0.45);
|
||||||
|
}
|
||||||
|
|
||||||
.filterable-header-cell {
|
.filterable-header-cell {
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user