Fix product performance grouped JSON build
This commit is contained in:
@@ -1784,7 +1784,7 @@ const detailSalesColumns = [
|
||||
{ name: 'customer_name', label: 'Müşteri', field: 'customer_name', align: 'left', sortable: true },
|
||||
{ name: 'sales_qty', label: 'Adet', field: row => formatNumber(row.sales_qty, 2), align: 'right', sortable: true },
|
||||
{ name: 'sales_usd', label: 'USD', field: row => formatMoney(row.sales_usd, 'USD'), align: 'right', sortable: true },
|
||||
{ name: 'avg_price_usd', label: 'Ort. USD', field: row => formatMoney(row.avg_price_usd, 'USD'), align: 'right', sortable: true }
|
||||
{ name: 'avg_price_usd', label: 'Ort. Satış Fiyatı USD', field: row => formatMoney(row.avg_price_usd, 'USD'), align: 'right', sortable: true }
|
||||
]
|
||||
|
||||
const columns = [
|
||||
@@ -1816,14 +1816,14 @@ const columns = [
|
||||
{ name: 'stock_days_180d', label: '180G Stok Gün', field: row => formatNumber(row.stock_days_180d, 1), align: 'right', sortable: true },
|
||||
{ name: 'stock_days_365d', label: '360G Stok Gün', field: row => formatNumber(row.stock_days_365d, 1), align: 'right', sortable: true },
|
||||
{ name: 'stock_days_total', label: 'Genel Stok Gün', field: row => formatNumber(row.stock_days_total, 1), align: 'right', sortable: true },
|
||||
{ name: 'stock_turnover_90d', label: '90G Satış/Stok', field: row => formatNumber(row.stock_turnover_90d, 2), align: 'right', sortable: true },
|
||||
{ name: 'stock_turnover_180d', label: '180G Satış/Stok', field: row => formatNumber(row.stock_turnover_180d, 2), align: 'right', sortable: true },
|
||||
{ name: 'stock_turnover_365d', label: '360G Satış/Stok', field: row => formatNumber(row.stock_turnover_365d, 2), align: 'right', sortable: true },
|
||||
{ name: 'stock_turnover_total', label: 'Genel Satış/Stok', field: row => formatNumber(row.stock_turnover_total, 2), align: 'right', sortable: true },
|
||||
{ name: 'avg_price_usd_90d', label: '90G Ciro/Adet USD', field: row => formatMoney(row.avg_price_usd_90d, 'USD'), align: 'right' },
|
||||
{ name: 'avg_price_usd_180d', label: '180G Ciro/Adet USD', field: row => formatMoney(row.avg_price_usd_180d, 'USD'), align: 'right' },
|
||||
{ name: 'avg_price_usd_365d', label: '360G Ciro/Adet USD', field: row => formatMoney(row.avg_price_usd_365d, 'USD'), align: 'right' },
|
||||
{ name: 'avg_price_usd_total', label: 'Genel Ciro/Adet USD', field: row => formatMoney(row.avg_price_usd_total, 'USD'), align: 'right' },
|
||||
{ name: 'stock_turnover_90d', label: '90G Stok Devir Hızı', field: row => formatNumber(row.stock_turnover_90d, 2), align: 'right', sortable: true },
|
||||
{ name: 'stock_turnover_180d', label: '180G Stok Devir Hızı', field: row => formatNumber(row.stock_turnover_180d, 2), align: 'right', sortable: true },
|
||||
{ name: 'stock_turnover_365d', label: '360G Stok Devir Hızı', field: row => formatNumber(row.stock_turnover_365d, 2), align: 'right', sortable: true },
|
||||
{ name: 'stock_turnover_total', label: 'Genel Stok Devir Hızı', field: row => formatNumber(row.stock_turnover_total, 2), align: 'right', sortable: true },
|
||||
{ name: 'avg_price_usd_90d', label: '90G Ort. Satış Fiyatı USD', field: row => formatMoney(row.avg_price_usd_90d, 'USD'), align: 'right' },
|
||||
{ name: 'avg_price_usd_180d', label: '180G Ort. Satış Fiyatı USD', field: row => formatMoney(row.avg_price_usd_180d, 'USD'), align: 'right' },
|
||||
{ name: 'avg_price_usd_365d', label: '360G Ort. Satış Fiyatı USD', field: row => formatMoney(row.avg_price_usd_365d, 'USD'), align: 'right' },
|
||||
{ name: 'avg_price_usd_total', label: 'Genel Ort. Satış Fiyatı USD', field: row => formatMoney(row.avg_price_usd_total, 'USD'), align: 'right' },
|
||||
{ name: 'base_price_usd', label: 'USD Taban Maliyeti', field: row => formatMoney(row.base_price_usd, 'USD'), align: 'right', sortable: true },
|
||||
{ name: 'cost_price_usd', label: 'USD Çıplak Maliyeti', field: row => formatMoney(row.cost_price_usd, 'USD'), align: 'right', sortable: true },
|
||||
{ name: 'unit_profit_base_90d', label: '90G P.Başı Taban K/Z', field: row => formatMoney(row.unit_profit_base_90d, 'USD'), align: 'right', sortable: true },
|
||||
@@ -2045,7 +2045,7 @@ const generalColumns = [
|
||||
{ name: 'sales_usd_total', label: 'Toplam Ciro USD', field: row => formatMoney(row.sales_usd_total, 'USD'), align: 'right', sortable: true },
|
||||
{ name: 'avg_daily_sales_total', label: 'Günlük Ort.', field: row => formatNumber(row.avg_daily_sales_total, 3), align: 'right', sortable: true },
|
||||
{ name: 'stock_days_total', label: 'Genel Stok Gün', field: row => formatNumber(row.stock_days_total, 1), align: 'right', sortable: true },
|
||||
{ name: 'avg_price_usd_total', label: 'Genel Ciro/Adet USD', field: row => formatMoney(row.avg_price_usd_total, 'USD'), align: 'right', sortable: true },
|
||||
{ name: 'avg_price_usd_total', label: 'Genel Ort. Satış Fiyatı USD', field: row => formatMoney(row.avg_price_usd_total, 'USD'), align: 'right', sortable: true },
|
||||
{ name: 'base_price_usd', label: 'USD Taban Maliyeti', field: row => formatMoney(row.base_price_usd, 'USD'), align: 'right', sortable: true },
|
||||
{ name: 'cost_price_usd', label: 'USD Çıplak Maliyeti', field: row => formatMoney(row.cost_price_usd, 'USD'), align: 'right', sortable: true },
|
||||
{ name: 'unit_profit_base_total', label: 'P.Başı Taban K/Z', field: row => formatMoney(row.unit_profit_base_total, 'USD'), align: 'right', sortable: true },
|
||||
@@ -2108,7 +2108,7 @@ const orderGroupColumns = [
|
||||
{ name: 'product_count', label: 'Ürün', field: row => formatNumber(row.product_count, 0), align: 'right', sortable: true },
|
||||
{ name: 'order_qty', label: 'Açık Sipariş', field: row => formatNumber(row.order_qty, 0), align: 'right', sortable: true },
|
||||
{ name: 'order_usd', label: 'Sipariş USD', field: row => formatMoney(row.order_usd, 'USD'), align: 'right', sortable: true },
|
||||
{ name: 'avg_order_price_usd', label: 'Ort. USD', field: row => formatMoney(row.avg_order_price_usd, 'USD'), align: 'right', sortable: true },
|
||||
{ name: 'avg_order_price_usd', label: 'Ort. Sipariş Fiyatı USD', field: row => formatMoney(row.avg_order_price_usd, 'USD'), align: 'right', sortable: true },
|
||||
{ name: 'base_cost_usd', label: 'Taban Maliyet USD', field: row => formatMoney(row.base_cost_usd, 'USD'), align: 'right', sortable: true },
|
||||
{ name: 'cost_amount_usd', label: 'Çıplak Maliyet USD', field: row => formatMoney(row.cost_amount_usd, 'USD'), align: 'right', sortable: true },
|
||||
{ name: 'expected_profit_base_usd', label: 'Brüt K/Z', field: row => formatMoney(row.expected_profit_base_usd, 'USD'), align: 'right', sortable: true },
|
||||
@@ -2142,7 +2142,7 @@ const orderProductCustomerColumns = [
|
||||
{ name: 'yaka_kodu', label: 'Yaka', field: 'yaka_kodu', align: 'left', sortable: true },
|
||||
{ name: 'order_qty', label: 'Açık Sipariş', field: row => formatNumber(row.order_qty, 0), align: 'right', sortable: true },
|
||||
{ name: 'order_usd', label: 'Sipariş USD', field: row => formatMoney(row.order_usd, 'USD'), align: 'right', sortable: true },
|
||||
{ name: 'avg_order_price_usd', label: 'Ort. USD', field: row => formatMoney(row.avg_order_price_usd, 'USD'), align: 'right', sortable: true },
|
||||
{ name: 'avg_order_price_usd', label: 'Ort. Sipariş Fiyatı USD', field: row => formatMoney(row.avg_order_price_usd, 'USD'), align: 'right', sortable: true },
|
||||
{ name: 'stock_qty', label: 'Stok', field: row => formatNumber(row.stock_qty, 0), align: 'right', sortable: true },
|
||||
{ name: 'net_stock_after_order', label: 'Stok - Sipariş', field: 'net_stock_after_order', align: 'right', sortable: true },
|
||||
{ name: 'base_price_usd', label: 'Taban USD', field: row => formatMoney(row.base_price_usd, 'USD'), align: 'right', sortable: true },
|
||||
@@ -2176,7 +2176,7 @@ const orderMarketDetailColumns = [
|
||||
{ name: 'due_date', label: 'Termin', field: 'due_date', align: 'left', sortable: true },
|
||||
{ name: 'order_qty', label: 'Adet', field: row => formatNumber(row.order_qty, 0), align: 'right', sortable: true },
|
||||
{ name: 'order_usd', label: 'USD', field: row => formatMoney(row.order_usd, 'USD'), align: 'right', sortable: true },
|
||||
{ name: 'avg_order_price_usd', label: 'Ort. USD', field: row => formatMoney(row.avg_order_price_usd, 'USD'), align: 'right', sortable: true },
|
||||
{ name: 'avg_order_price_usd', label: 'Ort. Sipariş Fiyatı USD', field: row => formatMoney(row.avg_order_price_usd, 'USD'), align: 'right', sortable: true },
|
||||
{ name: 'stock_qty', label: 'Stok', field: row => formatNumber(row.stock_qty, 0), align: 'right', sortable: true },
|
||||
{ name: 'net_stock_after_order', label: 'Stok - Sipariş', field: 'net_stock_after_order', align: 'right', sortable: true },
|
||||
{ name: 'expected_profit_base_usd', label: 'Brüt K/Z', field: row => formatMoney(row.expected_profit_base_usd, 'USD'), align: 'right', sortable: true },
|
||||
@@ -2205,7 +2205,7 @@ const idleColumns = [
|
||||
{ name: 'idle_cost_usd', label: 'Stok Maliyeti USD', field: 'idle_cost_usd', align: 'right', sortable: true },
|
||||
{ name: 'sales_qty_90d', label: '90G Satış', field: row => formatNumber(row.sales_qty_90d, 0), align: 'right', sortable: true },
|
||||
{ name: 'stock_days_90d', label: 'Stok Gün', field: row => formatNumber(row.stock_days_90d, 1), align: 'right', sortable: true },
|
||||
{ name: 'stock_turnover_90d', label: '90G Stok Devir', field: row => formatNumber(row.stock_turnover_90d, 2), align: 'right', sortable: true },
|
||||
{ name: 'stock_turnover_90d', label: '90G Stok Devir Hızı', field: row => formatNumber(row.stock_turnover_90d, 2), align: 'right', sortable: true },
|
||||
{ name: 'performance_bucket', label: 'Durum', field: 'performance_bucket', align: 'left' },
|
||||
{ name: 'recommendation', label: 'Öneri', field: 'recommendation', align: 'left' }
|
||||
]
|
||||
@@ -2236,7 +2236,7 @@ const countryColumns = [
|
||||
{ name: 'product_count', label: 'Ürün', field: row => formatNumber(row.product_count, 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: row => formatMoney(row.sales_usd_90d, 'USD'), align: 'right', sortable: true },
|
||||
{ name: 'avg_price_usd_90d', label: 'Ort. USD', field: row => formatMoney(row.avg_price_usd_90d, 'USD'), align: 'right', sortable: true },
|
||||
{ name: 'avg_price_usd_90d', label: 'Ort. Satış Fiyatı USD', field: row => formatMoney(row.avg_price_usd_90d, 'USD'), align: 'right', sortable: true },
|
||||
{ name: 'customer_count_90d', label: 'Müşteri', field: row => formatNumber(row.customer_count_90d, 0), align: 'right', sortable: true },
|
||||
{ name: 'invoice_count_90d', label: 'Fatura', field: row => formatNumber(row.invoice_count_90d, 0), align: 'right', sortable: true },
|
||||
{ name: 'sales_qty_365d', label: '360G Adet', field: row => formatNumber(row.sales_qty_365d, 0), align: 'right', sortable: true },
|
||||
@@ -2252,7 +2252,7 @@ const customerColumns = [
|
||||
{ name: 'product_count', label: 'Ürün', field: row => formatNumber(row.product_count, 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: 'avg_price_usd_90d', label: 'Ort. USD', field: 'avg_price_usd_90d', align: 'right', sortable: true },
|
||||
{ name: 'avg_price_usd_90d', label: 'Ort. Satış Fiyatı 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, 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: 'cost_price_usd_90d', label: '90G Çıplak', field: row => formatMoney(row.cost_price_usd_90d, 'USD'), align: 'right', sortable: true },
|
||||
@@ -2287,14 +2287,14 @@ const salesBreakdownColumns = [
|
||||
{ name: 'invoice_count_90d', label: '90G Fatura', field: row => formatNumber(row.invoice_count_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: 'avg_price_usd_90d', label: 'Ort. USD', field: 'avg_price_usd_90d', align: 'right', sortable: true },
|
||||
{ name: 'stock_turnover_90d', label: '90G Stok Devir', field: row => formatNumber(row.stock_turnover_90d, 2), align: 'right', sortable: true },
|
||||
{ name: 'avg_price_usd_90d', label: '90G Ort. Satış Fiyatı USD', field: 'avg_price_usd_90d', align: 'right', sortable: true },
|
||||
{ name: 'stock_turnover_90d', label: '90G Stok Devir Hızı', field: row => formatNumber(row.stock_turnover_90d, 2), align: 'right', sortable: true },
|
||||
{ name: 'customer_count_180d', label: '180G Müşteri', field: row => formatNumber(row.customer_count_180d, 0), align: 'right', sortable: true },
|
||||
{ name: 'invoice_count_180d', label: '180G Fatura', field: row => formatNumber(row.invoice_count_180d, 0), align: 'right', sortable: true },
|
||||
{ name: 'sales_qty_180d', label: '180G Adet', field: row => formatNumber(row.sales_qty_180d, 0), align: 'right', sortable: true },
|
||||
{ name: 'sales_usd_180d', label: '180G USD', field: row => formatMoney(row.sales_usd_180d, 'USD'), align: 'right', sortable: true },
|
||||
{ name: 'avg_price_usd_180d', label: '180G Ort. USD', field: row => formatMoney(row.avg_price_usd_180d, 'USD'), align: 'right', sortable: true },
|
||||
{ name: 'stock_turnover_180d', label: '180G Stok Devir', field: row => formatNumber(row.stock_turnover_180d, 2), align: 'right', sortable: true },
|
||||
{ name: 'avg_price_usd_180d', label: '180G Ort. Satış Fiyatı USD', field: row => formatMoney(row.avg_price_usd_180d, 'USD'), align: 'right', sortable: true },
|
||||
{ name: 'stock_turnover_180d', label: '180G Stok Devir Hızı', field: row => formatNumber(row.stock_turnover_180d, 2), align: 'right', sortable: true },
|
||||
{ name: 'base_price_usd_180d', label: '180G Taban', field: row => formatMoney(row.base_price_usd_180d, 'USD'), align: 'right', sortable: true },
|
||||
{ name: 'cost_price_usd_180d', label: '180G Çıplak', field: row => formatMoney(row.cost_price_usd_180d, 'USD'), align: 'right', sortable: true },
|
||||
{ name: 'gross_profit_base_usd_180d', label: '180G Taban K/Z', field: row => formatMoney(row.gross_profit_base_usd_180d, 'USD'), align: 'right', sortable: true },
|
||||
@@ -2304,8 +2304,8 @@ const salesBreakdownColumns = [
|
||||
{ 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_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: 'stock_turnover_365d', label: '360G Stok Devir', field: row => formatNumber(row.stock_turnover_365d, 2), align: 'right', sortable: true },
|
||||
{ name: 'avg_price_usd_365d', label: '360G Ort. Satış Fiyatı USD', field: row => formatMoney(row.avg_price_usd_365d, 'USD'), align: 'right', sortable: true },
|
||||
{ name: 'stock_turnover_365d', label: '360G Stok Devir Hızı', field: row => formatNumber(row.stock_turnover_365d, 2), align: 'right', sortable: true },
|
||||
{ name: 'base_price_usd_365d', label: '360G Taban', field: row => formatMoney(row.base_price_usd_365d, 'USD'), align: 'right', sortable: true },
|
||||
{ name: 'cost_price_usd_365d', label: '360G Çıplak', field: row => formatMoney(row.cost_price_usd_365d, 'USD'), align: 'right', sortable: true },
|
||||
{ name: 'gross_profit_base_usd_365d', label: '360G Taban K/Z', field: row => formatMoney(row.gross_profit_base_usd_365d, 'USD'), align: 'right', sortable: true },
|
||||
@@ -2317,8 +2317,8 @@ const salesBreakdownColumns = [
|
||||
{ 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_usd_total', label: 'Genel USD', field: row => formatMoney(row.sales_usd_total, 'USD'), align: 'right', sortable: true },
|
||||
{ name: 'avg_price_usd_total', label: 'Genel Ort. USD', field: row => formatMoney(row.avg_price_usd_total, 'USD'), align: 'right', sortable: true },
|
||||
{ name: 'stock_turnover_total', label: 'Genel Stok Devir', field: row => formatNumber(row.stock_turnover_total, 2), align: 'right', sortable: true },
|
||||
{ name: 'avg_price_usd_total', label: 'Genel Ort. Satış Fiyatı USD', field: row => formatMoney(row.avg_price_usd_total, 'USD'), align: 'right', sortable: true },
|
||||
{ name: 'stock_turnover_total', label: 'Genel Stok Devir Hızı', field: row => formatNumber(row.stock_turnover_total, 2), align: 'right', sortable: true },
|
||||
{ name: 'base_price_usd_total', label: 'Genel Taban', field: row => formatMoney(row.base_price_usd_total, 'USD'), align: 'right', sortable: true },
|
||||
{ name: 'cost_price_usd_total', label: 'Genel Çıplak', field: row => formatMoney(row.cost_price_usd_total, 'USD'), align: 'right', sortable: true },
|
||||
{ name: 'gross_profit_base_usd_total', label: 'Genel Taban K/Z', field: row => formatMoney(row.gross_profit_base_usd_total, 'USD'), align: 'right', sortable: true },
|
||||
@@ -2359,11 +2359,11 @@ const metricLabelOverrides = {
|
||||
sales_usd_180d: '180G Toplam Ciro USD',
|
||||
sales_usd_365d: '360G Toplam Ciro USD',
|
||||
sales_usd_total: 'Genel Toplam Ciro USD',
|
||||
avg_price_usd: 'Ciro/Adet USD',
|
||||
avg_price_usd_90d: '90G Ciro/Adet USD',
|
||||
avg_price_usd_180d: '180G Ciro/Adet USD',
|
||||
avg_price_usd_365d: '360G Ciro/Adet USD',
|
||||
avg_price_usd_total: 'Genel Ciro/Adet USD',
|
||||
avg_price_usd: 'Ort. Satış Fiyatı USD',
|
||||
avg_price_usd_90d: '90G Ort. Satış Fiyatı USD',
|
||||
avg_price_usd_180d: '180G Ort. Satış Fiyatı USD',
|
||||
avg_price_usd_365d: '360G Ort. Satış Fiyatı USD',
|
||||
avg_price_usd_total: 'Genel Ort. Satış Fiyatı USD',
|
||||
base_price_usd: 'Taban Maliyet USD',
|
||||
cost_price_usd: 'Çıplak Maliyet USD',
|
||||
base_price_usd_90d: '90G Taban Maliyet USD',
|
||||
@@ -2412,10 +2412,10 @@ const metricLabelOverrides = {
|
||||
customer_count_90d: '90G Tekil Müşteri',
|
||||
market_count_total: 'Genel Tekil Piyasa',
|
||||
customer_count_total: 'Genel Tekil Müşteri',
|
||||
stock_turnover_90d: '90G Satış/Stok',
|
||||
stock_turnover_180d: '180G Satış/Stok',
|
||||
stock_turnover_365d: '360G Satış/Stok',
|
||||
stock_turnover_total: 'Genel Satış/Stok',
|
||||
stock_turnover_90d: '90G Stok Devir Hızı',
|
||||
stock_turnover_180d: '180G Stok Devir Hızı',
|
||||
stock_turnover_365d: '360G Stok Devir Hızı',
|
||||
stock_turnover_total: 'Genel Stok Devir Hızı',
|
||||
sales_index_90d: '90G Endeks',
|
||||
sales_index_total: 'Genel Endeks',
|
||||
stock_days_90d: '90G Stok Gün',
|
||||
@@ -2645,7 +2645,7 @@ function performanceCardsForRow (row) {
|
||||
{ key: 'sales180', label: '180G Satış', value: formatNumber(source.sales_qty_180d, 0) },
|
||||
{ key: 'markets', label: '90G Piyasa', value: formatNumber(source.market_count_90d, 0) },
|
||||
{ key: 'customers', label: '90G Müşteri', value: formatNumber(source.customer_count_90d, 0) },
|
||||
{ key: 'avg', label: 'Ort. USD Satış', value: formatMoney(source.avg_price_usd_90d, 'USD') },
|
||||
{ key: 'avg', label: 'Ort. Satış Fiyatı', value: formatMoney(source.avg_price_usd_90d, 'USD') },
|
||||
{ key: 'base', label: 'Taban Maliyet', value: formatMoney(source.base_price_usd, 'USD') },
|
||||
{ key: 'cost', label: 'Çıplak Maliyet', value: formatMoney(source.cost_price_usd, 'USD') },
|
||||
{ key: 'profitBase', label: 'P.Başı Taban K/Z', value: formatMoney(source.unit_profit_base_90d, 'USD') },
|
||||
@@ -3470,7 +3470,7 @@ function shouldSumField (field) {
|
||||
}
|
||||
|
||||
function shouldAverageField (field) {
|
||||
return /^(avg_|unit_|base_price|cost_price|gross_margin|expected_margin|sales_index|performance_score|stock_days|stock_turnover)/i.test(field) ||
|
||||
return /^(avg_|unit_|base_price|cost_price|gross_margin|expected_margin|sales_index|performance_score|customer_score|stock_days|stock_turnover)/i.test(field) ||
|
||||
/(_price_usd|_margin|_index|_days)$/i.test(field)
|
||||
}
|
||||
|
||||
@@ -3484,21 +3484,35 @@ function weightFieldForMetric (field) {
|
||||
}
|
||||
|
||||
function applyDerivedGroupMetrics (out, sourceRows, groupField = '') {
|
||||
Object.assign(out, normalizeProductCostFields(out))
|
||||
for (const suffix of ['90d', '180d', '365d', 'total']) {
|
||||
const sales = Number(out[`sales_usd_${suffix}`] || 0)
|
||||
const qty = Number(out[`sales_qty_${suffix}`] || 0)
|
||||
const stockQty = Number(out.stock_qty || 0)
|
||||
const avgStock = Number(out[`avg_stock_${suffix}`] || 0)
|
||||
const turnoverBase = avgStock > 0 ? avgStock : stockQty
|
||||
out[`stock_turnover_${suffix}`] = turnoverBase > 0 ? qty / turnoverBase : 0
|
||||
if (qty > 0) out[`avg_price_usd_${suffix}`] = sales / qty
|
||||
if (sales > 0) {
|
||||
if (out[`gross_profit_base_usd_${suffix}`] !== undefined) {
|
||||
out[`gross_margin_base_${suffix}`] = Number(out[`gross_profit_base_usd_${suffix}`] || 0) / sales
|
||||
}
|
||||
if (out[`gross_profit_cost_usd_${suffix}`] !== undefined) {
|
||||
out[`gross_margin_cost_${suffix}`] = Number(out[`gross_profit_cost_usd_${suffix}`] || 0) / sales
|
||||
}
|
||||
if (out[`gross_profit_usd_${suffix}`] !== undefined) {
|
||||
out[`gross_margin_${suffix}`] = Number(out[`gross_profit_usd_${suffix}`] || 0) / sales
|
||||
}
|
||||
const { costPrice, basePrice } = periodCostPair(out, suffix)
|
||||
out[`base_price_usd_${suffix}`] = basePrice
|
||||
out[`cost_price_usd_${suffix}`] = costPrice
|
||||
if (qty > 0) {
|
||||
const avgPrice = sales / qty
|
||||
out[`unit_profit_base_${suffix}`] = avgPrice - basePrice
|
||||
out[`unit_profit_cost_${suffix}`] = avgPrice - costPrice
|
||||
out[`gross_profit_base_usd_${suffix}`] = sales - qty * basePrice
|
||||
out[`gross_profit_cost_usd_${suffix}`] = sales - qty * costPrice
|
||||
out[`gross_profit_usd_${suffix}`] = out[`gross_profit_cost_usd_${suffix}`]
|
||||
} else {
|
||||
out[`unit_profit_base_${suffix}`] = 0
|
||||
out[`unit_profit_cost_${suffix}`] = 0
|
||||
out[`gross_profit_base_usd_${suffix}`] = 0
|
||||
out[`gross_profit_cost_usd_${suffix}`] = 0
|
||||
out[`gross_profit_usd_${suffix}`] = 0
|
||||
}
|
||||
out[`gross_margin_base_${suffix}`] = sales > 0 ? Number(out[`gross_profit_base_usd_${suffix}`] || 0) / sales : 0
|
||||
out[`gross_margin_cost_${suffix}`] = sales > 0 ? Number(out[`gross_profit_cost_usd_${suffix}`] || 0) / sales : 0
|
||||
out[`gross_margin_${suffix}`] = sales > 0 ? Number(out[`gross_profit_usd_${suffix}`] || 0) / sales : 0
|
||||
}
|
||||
|
||||
const orderUsd = Number(out.order_usd || 0)
|
||||
@@ -3524,7 +3538,11 @@ function applyDerivedGroupMetrics (out, sourceRows, groupField = '') {
|
||||
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)
|
||||
if (!Object.prototype.hasOwnProperty.call(out, 'performance_score')) {
|
||||
out.performance_score = Object.prototype.hasOwnProperty.call(out, 'performance_score_90d')
|
||||
? Number(out.performance_score_90d || 0)
|
||||
: groupPerformanceScore(out, groupField)
|
||||
}
|
||||
}
|
||||
|
||||
function groupPerformanceScore (row, groupField = '') {
|
||||
@@ -3837,6 +3855,9 @@ function weightedAverage (sourceRows, amountField, qtyField) {
|
||||
}
|
||||
|
||||
function weightedAverageOrAverage (sourceRows, valueField, qtyField) {
|
||||
if (/^(performance_score|customer_score)/i.test(valueField)) {
|
||||
return weightedAverageScore(sourceRows, valueField, scoreSuffixForMetric(valueField))
|
||||
}
|
||||
const weightedRows = sourceRows
|
||||
.map(row => ({
|
||||
value: Number(row[valueField] || 0),
|
||||
@@ -3850,6 +3871,39 @@ function weightedAverageOrAverage (sourceRows, valueField, qtyField) {
|
||||
return 0
|
||||
}
|
||||
|
||||
function scoreSuffixForMetric (field) {
|
||||
if (field.includes('_180d')) return '180d'
|
||||
if (field.includes('_365d')) return '365d'
|
||||
if (field.includes('_total')) return 'total'
|
||||
return '90d'
|
||||
}
|
||||
|
||||
function scoreWeight (row, suffix) {
|
||||
const salesUsd = Number(row?.[`sales_usd_${suffix}`] || 0)
|
||||
if (salesUsd > 0) return salesUsd
|
||||
const salesQty = Number(row?.[`sales_qty_${suffix}`] || 0)
|
||||
if (salesQty > 0) return salesQty
|
||||
const stockQty = Number(row?.stock_qty || 0)
|
||||
if (stockQty <= 0) return 1
|
||||
const costPrice = Number(row?.[`cost_price_usd_${suffix}`] || row?.cost_price_usd || 0)
|
||||
return costPrice > 0 ? stockQty * costPrice : stockQty
|
||||
}
|
||||
|
||||
function weightedAverageScore (sourceRows, valueField, suffix) {
|
||||
const rows = sourceRows
|
||||
.filter(row => Object.prototype.hasOwnProperty.call(row || {}, valueField))
|
||||
.map(row => ({
|
||||
value: Number(row[valueField] || 0),
|
||||
weight: scoreWeight(row, suffix)
|
||||
}))
|
||||
.filter(row => Number.isFinite(row.value) && Number.isFinite(row.weight))
|
||||
const weight = rows.reduce((sum, row) => sum + Math.max(0, row.weight), 0)
|
||||
if (weight > 0) {
|
||||
return rows.reduce((sum, row) => sum + row.value * Math.max(0, row.weight), 0) / weight
|
||||
}
|
||||
return rows.length ? rows.reduce((sum, row) => sum + row.value, 0) / rows.length : 0
|
||||
}
|
||||
|
||||
function weightedAverageProductCost (sourceRows, valueField) {
|
||||
const salesRows = sourceRows
|
||||
.map(row => ({
|
||||
@@ -4068,53 +4122,93 @@ 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_base_total: marginFromSalesCost(salesUSDTotal, salesQtyTotal, basePrice),
|
||||
gross_margin_cost_total: marginFromSalesCost(salesUSDTotal, salesQtyTotal, costPrice)
|
||||
const next = normalizeProductCostFields(row)
|
||||
const out = {
|
||||
...next,
|
||||
avg_price_usd_365d: Number(next?.sales_qty_365d || 0) > 0 ? Number(next?.sales_usd_365d || 0) / Number(next?.sales_qty_365d || 0) : 0,
|
||||
stock_turnover_90d: existingOrStockTurnover(next?.stock_turnover_90d, next?.sales_qty_90d, next?.stock_qty),
|
||||
stock_turnover_180d: existingOrStockTurnover(next?.stock_turnover_180d, next?.sales_qty_180d, next?.stock_qty),
|
||||
stock_turnover_365d: existingOrStockTurnover(next?.stock_turnover_365d, next?.sales_qty_365d, next?.stock_qty),
|
||||
stock_turnover_total: existingOrStockTurnover(next?.stock_turnover_total, next?.sales_qty_total, next?.stock_qty)
|
||||
}
|
||||
for (const suffix of ['90d', '180d', '365d', 'total']) {
|
||||
applyPeriodProfitFields(out, suffix)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
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
|
||||
const out = normalizeProductCostFields(row)
|
||||
applyPeriodProfitFields(out, 'total')
|
||||
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_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)
|
||||
...out,
|
||||
stock_turnover_total: existingOrStockTurnover(out?.stock_turnover_total, out?.sales_qty_total, out?.stock_qty)
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeProductCostFields (row) {
|
||||
const next = { ...row }
|
||||
normalizeProductCostPairFields(next, 'cost_price_usd', 'base_price_usd')
|
||||
for (const suffix of ['90d', '180d', '365d', 'total']) {
|
||||
normalizeProductCostPairFields(next, `cost_price_usd_${suffix}`, `base_price_usd_${suffix}`)
|
||||
}
|
||||
return next
|
||||
}
|
||||
|
||||
function normalizeProductCostPairFields (row, costField, baseField) {
|
||||
const hasCost = Object.prototype.hasOwnProperty.call(row || {}, costField)
|
||||
const hasBase = Object.prototype.hasOwnProperty.call(row || {}, baseField)
|
||||
if (!hasCost && !hasBase) return
|
||||
let costPrice = Number(row?.[costField] || 0)
|
||||
let basePrice = Number(row?.[baseField] || 0)
|
||||
if (costPrice > 0 && basePrice > 0 && costPrice > basePrice) {
|
||||
const tmp = costPrice
|
||||
costPrice = basePrice
|
||||
basePrice = tmp
|
||||
}
|
||||
if (hasCost || costPrice > 0) row[costField] = costPrice
|
||||
if (hasBase || basePrice > 0) row[baseField] = basePrice
|
||||
}
|
||||
|
||||
function periodCostPair (row, suffix) {
|
||||
const costField = `cost_price_usd_${suffix}`
|
||||
const baseField = `base_price_usd_${suffix}`
|
||||
let costPrice = Number(row?.[costField] || 0)
|
||||
let basePrice = Number(row?.[baseField] || 0)
|
||||
if (costPrice <= 0) costPrice = Number(row?.cost_price_usd || 0)
|
||||
if (basePrice <= 0) basePrice = Number(row?.base_price_usd || 0)
|
||||
if (costPrice > 0 && basePrice > 0 && costPrice > basePrice) {
|
||||
const tmp = costPrice
|
||||
costPrice = basePrice
|
||||
basePrice = tmp
|
||||
}
|
||||
return { costPrice, basePrice }
|
||||
}
|
||||
|
||||
function applyPeriodProfitFields (row, suffix) {
|
||||
const qty = Number(row?.[`sales_qty_${suffix}`] || 0)
|
||||
const salesUSD = Number(row?.[`sales_usd_${suffix}`] || 0)
|
||||
const { costPrice, basePrice } = periodCostPair(row, suffix)
|
||||
row[`base_price_usd_${suffix}`] = basePrice
|
||||
row[`cost_price_usd_${suffix}`] = costPrice
|
||||
if (qty > 0) {
|
||||
row[`avg_price_usd_${suffix}`] = salesUSD / qty
|
||||
row[`unit_profit_base_${suffix}`] = salesUSD / qty - basePrice
|
||||
row[`unit_profit_cost_${suffix}`] = salesUSD / qty - costPrice
|
||||
row[`gross_profit_base_usd_${suffix}`] = salesUSD - qty * basePrice
|
||||
row[`gross_profit_cost_usd_${suffix}`] = salesUSD - qty * costPrice
|
||||
row[`gross_profit_usd_${suffix}`] = row[`gross_profit_cost_usd_${suffix}`]
|
||||
} else {
|
||||
row[`unit_profit_base_${suffix}`] = 0
|
||||
row[`unit_profit_cost_${suffix}`] = 0
|
||||
row[`gross_profit_base_usd_${suffix}`] = 0
|
||||
row[`gross_profit_cost_usd_${suffix}`] = 0
|
||||
row[`gross_profit_usd_${suffix}`] = 0
|
||||
}
|
||||
row[`gross_margin_base_${suffix}`] = marginFromSalesCost(salesUSD, qty, basePrice)
|
||||
row[`gross_margin_cost_${suffix}`] = marginFromSalesCost(salesUSD, qty, costPrice)
|
||||
}
|
||||
|
||||
function stockTurnover (salesQty, stockQty) {
|
||||
const stock = Number(stockQty || 0)
|
||||
if (stock <= 0) return 0
|
||||
@@ -4461,11 +4555,21 @@ function formatProductCell (row, name) {
|
||||
case 'gross_profit_usd_90d':
|
||||
case 'gross_profit_usd_180d':
|
||||
case 'gross_profit_usd_total':
|
||||
case 'gross_profit_base_usd_90d':
|
||||
case 'gross_profit_cost_usd_90d':
|
||||
case 'gross_profit_base_usd_180d':
|
||||
case 'gross_profit_cost_usd_180d':
|
||||
case 'gross_profit_base_usd_365d':
|
||||
case 'gross_profit_cost_usd_365d':
|
||||
case 'gross_profit_base_usd_total':
|
||||
case 'gross_profit_cost_usd_total':
|
||||
return formatMoney(row[name], 'USD')
|
||||
case 'gross_margin_base_90d':
|
||||
case 'gross_margin_cost_90d':
|
||||
case 'gross_margin_base_180d':
|
||||
case 'gross_margin_cost_180d':
|
||||
case 'gross_margin_base_365d':
|
||||
case 'gross_margin_cost_365d':
|
||||
case 'gross_margin_base_total':
|
||||
case 'gross_margin_cost_total':
|
||||
case 'gross_margin_90d':
|
||||
@@ -5045,7 +5149,7 @@ async function loadBackendGroupedRows (options = {}) {
|
||||
function normalizeGroupedDisplayRow (tabKey, row) {
|
||||
const nextRow = { ...row }
|
||||
normalizeGroupedProductHierarchyValues(nextRow)
|
||||
return nextRow
|
||||
return withPeriodScores(withGeneralMargins(withProductMargins(nextRow)))
|
||||
}
|
||||
|
||||
function normalizeGroupedProductHierarchyValues (row) {
|
||||
|
||||
Reference in New Issue
Block a user