From 9fee8c8ab29bb5115e4150191576b3b8392ce413 Mon Sep 17 00:00:00 2001 From: M_Kececi Date: Fri, 3 Jul 2026 17:12:10 +0300 Subject: [PATCH] product performance grouped kpi improvements --- svc/models/product_performance.go | 112 +++++----- svc/queries/product_performance.go | 194 ++++++++++++++++-- svc/queries/product_performance_sql.go | 72 ++++++- svc/queries/productstockquery.go | 46 ++--- .../productstockquery_by_attributes.go | 96 ++++----- .../pages/ProductPerformanceProfitability.vue | 18 +- 6 files changed, 379 insertions(+), 159 deletions(-) diff --git a/svc/models/product_performance.go b/svc/models/product_performance.go index d890150..0ced5c4 100644 --- a/svc/models/product_performance.go +++ b/svc/models/product_performance.go @@ -1,57 +1,67 @@ package models type ProductPerformanceRow struct { - KpiDate string `json:"kpi_date"` - ProductCode string `json:"product_code"` - ColorCode string `json:"color_code"` - YakaKodu string `json:"yaka_kodu"` - ItemDescription string `json:"item_description"` - Kategori string `json:"kategori"` - Seri string `json:"seri"` - YasGrubu string `json:"yas_grubu"` - AskiliYan string `json:"askili_yan"` - UrunIlkGrubu string `json:"urun_ilk_grubu"` - UrunAnaGrubu string `json:"urun_ana_grubu"` - UrunAltGrubu string `json:"urun_alt_grubu"` - MarketKey string `json:"market_key"` - StockQty float64 `json:"stock_qty"` - SalesQty30 float64 `json:"sales_qty_30d"` - SalesQty90 float64 `json:"sales_qty_90d"` - SalesQty180 float64 `json:"sales_qty_180d"` - SalesQty365 float64 `json:"sales_qty_365d"` - SalesQty730 float64 `json:"sales_qty_730d"` - SalesUSD30 float64 `json:"sales_usd_30d"` - SalesUSD90 float64 `json:"sales_usd_90d"` - SalesUSD180 float64 `json:"sales_usd_180d"` - SalesUSD365 float64 `json:"sales_usd_365d"` - AvgDailySales90 float64 `json:"avg_daily_sales_90d"` - AvgDailySales180 float64 `json:"avg_daily_sales_180d"` - StockDays90 float64 `json:"stock_days_90d"` - StockDays180 float64 `json:"stock_days_180d"` - AvgPriceUSD90 float64 `json:"avg_price_usd_90d"` - AvgPriceUSD180 float64 `json:"avg_price_usd_180d"` - CostPriceUSD float64 `json:"cost_price_usd"` - BasePriceUSD float64 `json:"base_price_usd"` - BasePriceTRY float64 `json:"base_price_try"` - GrossProfitUSD90 float64 `json:"gross_profit_usd_90d"` - GrossProfitUSD180 float64 `json:"gross_profit_usd_180d"` - GrossMargin90 float64 `json:"gross_margin_90d"` - GrossMargin180 float64 `json:"gross_margin_180d"` - UnitProfitCost90 float64 `json:"unit_profit_cost_90d"` - UnitProfitCost180 float64 `json:"unit_profit_cost_180d"` - UnitProfitBase90 float64 `json:"unit_profit_base_90d"` - UnitProfitBase180 float64 `json:"unit_profit_base_180d"` - MarketCount90 int `json:"market_count_90d"` - CustomerCount90 int `json:"customer_count_90d"` - SalesIndex90 float64 `json:"sales_index_90d"` - PriceIndex90 float64 `json:"price_index_90d"` - MarginIndex90 float64 `json:"margin_index_90d"` - PerformanceScore float64 `json:"performance_score"` - PerformanceBucket string `json:"performance_bucket"` - Recommendation string `json:"recommendation"` - LastSaleDate string `json:"last_sale_date"` - LastRefNumber string `json:"last_ref_number"` - UpdatedAt string `json:"updated_at"` + KpiDate string `json:"kpi_date"` + ProductCode string `json:"product_code"` + ColorCode string `json:"color_code"` + YakaKodu string `json:"yaka_kodu"` + ItemDescription string `json:"item_description"` + Kategori string `json:"kategori"` + Seri string `json:"seri"` + YasGrubu string `json:"yas_grubu"` + AskiliYan string `json:"askili_yan"` + UrunIlkGrubu string `json:"urun_ilk_grubu"` + UrunAnaGrubu string `json:"urun_ana_grubu"` + UrunAltGrubu string `json:"urun_alt_grubu"` + MarketKey string `json:"market_key"` + StockQty float64 `json:"stock_qty"` + SalesQty30 float64 `json:"sales_qty_30d"` + SalesQty90 float64 `json:"sales_qty_90d"` + SalesQty180 float64 `json:"sales_qty_180d"` + SalesQty365 float64 `json:"sales_qty_365d"` + SalesQty730 float64 `json:"sales_qty_730d"` + SalesQtyTotal float64 `json:"sales_qty_total"` + SalesUSD30 float64 `json:"sales_usd_30d"` + SalesUSD90 float64 `json:"sales_usd_90d"` + SalesUSD180 float64 `json:"sales_usd_180d"` + SalesUSD365 float64 `json:"sales_usd_365d"` + SalesUSDTotal float64 `json:"sales_usd_total"` + AvgDailySales90 float64 `json:"avg_daily_sales_90d"` + AvgDailySales180 float64 `json:"avg_daily_sales_180d"` + AvgDailySales365 float64 `json:"avg_daily_sales_365d"` + AvgDailySalesTotal float64 `json:"avg_daily_sales_total"` + StockDays90 float64 `json:"stock_days_90d"` + StockDays180 float64 `json:"stock_days_180d"` + StockDays365 float64 `json:"stock_days_365d"` + StockDaysTotal float64 `json:"stock_days_total"` + StockTurnover90 float64 `json:"stock_turnover_90d"` + StockTurnover180 float64 `json:"stock_turnover_180d"` + StockTurnover365 float64 `json:"stock_turnover_365d"` + StockTurnoverTotal float64 `json:"stock_turnover_total"` + AvgPriceUSD90 float64 `json:"avg_price_usd_90d"` + AvgPriceUSD180 float64 `json:"avg_price_usd_180d"` + CostPriceUSD float64 `json:"cost_price_usd"` + BasePriceUSD float64 `json:"base_price_usd"` + BasePriceTRY float64 `json:"base_price_try"` + GrossProfitUSD90 float64 `json:"gross_profit_usd_90d"` + GrossProfitUSD180 float64 `json:"gross_profit_usd_180d"` + GrossMargin90 float64 `json:"gross_margin_90d"` + GrossMargin180 float64 `json:"gross_margin_180d"` + UnitProfitCost90 float64 `json:"unit_profit_cost_90d"` + UnitProfitCost180 float64 `json:"unit_profit_cost_180d"` + UnitProfitBase90 float64 `json:"unit_profit_base_90d"` + UnitProfitBase180 float64 `json:"unit_profit_base_180d"` + MarketCount90 int `json:"market_count_90d"` + CustomerCount90 int `json:"customer_count_90d"` + SalesIndex90 float64 `json:"sales_index_90d"` + PriceIndex90 float64 `json:"price_index_90d"` + MarginIndex90 float64 `json:"margin_index_90d"` + PerformanceScore float64 `json:"performance_score"` + PerformanceBucket string `json:"performance_bucket"` + Recommendation string `json:"recommendation"` + LastSaleDate string `json:"last_sale_date"` + LastRefNumber string `json:"last_ref_number"` + UpdatedAt string `json:"updated_at"` } type ProductPerformanceSummary struct { diff --git a/svc/queries/product_performance.go b/svc/queries/product_performance.go index 60b227b..3c82b9e 100644 --- a/svc/queries/product_performance.go +++ b/svc/queries/product_performance.go @@ -505,9 +505,19 @@ UPDATE mk_product_performance_kpi_daily SET askili_yan = '', updated_at = now() WHERE btrim(askili_yan) = '-'`, `ALTER TABLE mk_product_performance_kpi_daily ADD COLUMN IF NOT EXISTS sales_qty_180d NUMERIC(18,4) NOT NULL DEFAULT 0`, + `ALTER TABLE mk_product_performance_kpi_daily ADD COLUMN IF NOT EXISTS sales_qty_total NUMERIC(18,4) NOT NULL DEFAULT 0`, `ALTER TABLE mk_product_performance_kpi_daily ADD COLUMN IF NOT EXISTS sales_usd_180d NUMERIC(18,4) NOT NULL DEFAULT 0`, + `ALTER TABLE mk_product_performance_kpi_daily ADD COLUMN IF NOT EXISTS sales_usd_total NUMERIC(18,4) NOT NULL DEFAULT 0`, `ALTER TABLE mk_product_performance_kpi_daily ADD COLUMN IF NOT EXISTS avg_daily_sales_180d NUMERIC(18,6) NOT NULL DEFAULT 0`, + `ALTER TABLE mk_product_performance_kpi_daily ADD COLUMN IF NOT EXISTS avg_daily_sales_365d NUMERIC(18,6) NOT NULL DEFAULT 0`, + `ALTER TABLE mk_product_performance_kpi_daily ADD COLUMN IF NOT EXISTS avg_daily_sales_total NUMERIC(18,6) NOT NULL DEFAULT 0`, `ALTER TABLE mk_product_performance_kpi_daily ADD COLUMN IF NOT EXISTS stock_days_180d NUMERIC(18,6) NOT NULL DEFAULT 0`, + `ALTER TABLE mk_product_performance_kpi_daily ADD COLUMN IF NOT EXISTS stock_days_365d NUMERIC(18,6) NOT NULL DEFAULT 0`, + `ALTER TABLE mk_product_performance_kpi_daily ADD COLUMN IF NOT EXISTS stock_days_total NUMERIC(18,6) NOT NULL DEFAULT 0`, + `ALTER TABLE mk_product_performance_kpi_daily ADD COLUMN IF NOT EXISTS stock_turnover_90d NUMERIC(18,6) NOT NULL DEFAULT 0`, + `ALTER TABLE mk_product_performance_kpi_daily ADD COLUMN IF NOT EXISTS stock_turnover_180d NUMERIC(18,6) NOT NULL DEFAULT 0`, + `ALTER TABLE mk_product_performance_kpi_daily ADD COLUMN IF NOT EXISTS stock_turnover_365d NUMERIC(18,6) NOT NULL DEFAULT 0`, + `ALTER TABLE mk_product_performance_kpi_daily ADD COLUMN IF NOT EXISTS stock_turnover_total NUMERIC(18,6) NOT NULL DEFAULT 0`, `ALTER TABLE mk_product_performance_kpi_daily ADD COLUMN IF NOT EXISTS avg_price_usd_180d NUMERIC(18,6) NOT NULL DEFAULT 0`, `ALTER TABLE mk_product_performance_kpi_daily ADD COLUMN IF NOT EXISTS gross_profit_usd_180d NUMERIC(18,4) NOT NULL DEFAULT 0`, `ALTER TABLE mk_product_performance_kpi_daily ADD COLUMN IF NOT EXISTS gross_margin_180d NUMERIC(18,6) NOT NULL DEFAULT 0`, @@ -1073,9 +1083,12 @@ func ListProductPerformance(ctx context.Context, pg *sql.DB, f ProductPerformanc SELECT to_char(kpi_date,'YYYY-MM-DD'), product_code, color_code, yaka_kodu, item_description, kategori, seri, yas_grubu, askili_yan, urun_ilk_grubu, urun_ana_grubu, urun_alt_grubu, market_key, stock_qty, - sales_qty_30d, sales_qty_90d, sales_qty_180d, sales_qty_365d, sales_qty_730d, - sales_usd_30d, sales_usd_90d, sales_usd_180d, sales_usd_365d, avg_daily_sales_90d, avg_daily_sales_180d, - stock_days_90d, stock_days_180d, avg_price_usd_90d, avg_price_usd_180d, cost_price_usd, base_price_usd, base_price_try, + sales_qty_30d, sales_qty_90d, sales_qty_180d, sales_qty_365d, sales_qty_730d, sales_qty_total, + sales_usd_30d, sales_usd_90d, sales_usd_180d, sales_usd_365d, sales_usd_total, + avg_daily_sales_90d, avg_daily_sales_180d, avg_daily_sales_365d, avg_daily_sales_total, + stock_days_90d, stock_days_180d, stock_days_365d, stock_days_total, + stock_turnover_90d, stock_turnover_180d, stock_turnover_365d, stock_turnover_total, + avg_price_usd_90d, avg_price_usd_180d, cost_price_usd, base_price_usd, base_price_try, gross_profit_usd_90d, gross_profit_usd_180d, gross_margin_90d, gross_margin_180d, unit_profit_cost_90d, unit_profit_cost_180d, unit_profit_base_90d, unit_profit_base_180d, market_count_90d, customer_count_90d, sales_index_90d, price_index_90d, margin_index_90d, performance_score, performance_bucket, @@ -1095,9 +1108,12 @@ LIMIT $`+fmt.Sprint(len(args)-1)+` OFFSET $`+fmt.Sprint(len(args)), args...) if err := rows.Scan( &r.KpiDate, &r.ProductCode, &r.ColorCode, &r.YakaKodu, &r.ItemDescription, &r.Kategori, &r.Seri, &r.YasGrubu, &r.AskiliYan, &r.UrunIlkGrubu, &r.UrunAnaGrubu, &r.UrunAltGrubu, &r.MarketKey, &r.StockQty, - &r.SalesQty30, &r.SalesQty90, &r.SalesQty180, &r.SalesQty365, &r.SalesQty730, - &r.SalesUSD30, &r.SalesUSD90, &r.SalesUSD180, &r.SalesUSD365, &r.AvgDailySales90, &r.AvgDailySales180, - &r.StockDays90, &r.StockDays180, &r.AvgPriceUSD90, &r.AvgPriceUSD180, &r.CostPriceUSD, &r.BasePriceUSD, &r.BasePriceTRY, + &r.SalesQty30, &r.SalesQty90, &r.SalesQty180, &r.SalesQty365, &r.SalesQty730, &r.SalesQtyTotal, + &r.SalesUSD30, &r.SalesUSD90, &r.SalesUSD180, &r.SalesUSD365, &r.SalesUSDTotal, + &r.AvgDailySales90, &r.AvgDailySales180, &r.AvgDailySales365, &r.AvgDailySalesTotal, + &r.StockDays90, &r.StockDays180, &r.StockDays365, &r.StockDaysTotal, + &r.StockTurnover90, &r.StockTurnover180, &r.StockTurnover365, &r.StockTurnoverTotal, + &r.AvgPriceUSD90, &r.AvgPriceUSD180, &r.CostPriceUSD, &r.BasePriceUSD, &r.BasePriceTRY, &r.GrossProfitUSD90, &r.GrossProfitUSD180, &r.GrossMargin90, &r.GrossMargin180, &r.UnitProfitCost90, &r.UnitProfitCost180, &r.UnitProfitBase90, &r.UnitProfitBase180, &r.MarketCount90, &r.CustomerCount90, &r.SalesIndex90, &r.PriceIndex90, &r.MarginIndex90, &r.PerformanceScore, &r.PerformanceBucket, @@ -1232,6 +1248,16 @@ StockAgg AS ( WHERE s.stock_date = (SELECT stock_date FROM LatestStockDate) GROUP BY s.product_code, s.color_code, s.yaka_kodu ), +StockTotalStart AS ( + SELECT DISTINCT ON (s.product_code, s.color_code, s.yaka_kodu) + s.product_code, + s.color_code, + s.yaka_kodu, + s.stock_qty + FROM mk_product_performance_stock_daily s + WHERE s.stock_date <= DATE '2022-01-01' + ORDER BY s.product_code, s.color_code, s.yaka_kodu, s.stock_date DESC +), Dim AS ( SELECT DISTINCT ON (product_code, color_code, yaka_kodu) product_code, @@ -1354,7 +1380,7 @@ Scored AS ( b.sales_qty_total / GREATEST(1, (Bounds.period_end - Bounds.period_start + 1)) AS avg_daily_sales_total, CASE WHEN b.sales_qty_total <= 0 THEN 9999 - ELSE COALESCE(st.stock_qty, 0) / NULLIF(b.sales_qty_total / GREATEST(1, (Bounds.period_end - Bounds.period_start + 1)), 0) + ELSE ((COALESCE(st_start.stock_qty, 0) + COALESCE(st.stock_qty, 0)) / 2.0) / NULLIF(b.sales_qty_total / GREATEST(1, (Bounds.period_end - Bounds.period_start + 1)), 0) END AS stock_days_total, CASE WHEN b.sales_qty_total <= 0 THEN 0 ELSE b.sales_usd_total / NULLIF(b.sales_qty_total,0) END AS avg_price_usd_total, COALESCE(pd.cost_price_usd,0) AS cost_price_usd, @@ -1379,6 +1405,10 @@ Scored AS ( ON st.product_code = b.product_code AND st.color_code = b.color_code AND st.yaka_kodu = b.yaka_kodu + LEFT JOIN StockTotalStart st_start + ON st_start.product_code = b.product_code + AND st_start.color_code = b.color_code + AND st_start.yaka_kodu = b.yaka_kodu LEFT JOIN mk_product_performance_price_dim pd ON pd.product_code = b.product_code LEFT JOIN Spread sp ON sp.product_code = b.product_code @@ -2548,6 +2578,46 @@ StockAgg AS ( WHERE stock_date = (SELECT MAX(stock_date) FROM mk_product_performance_stock_daily) GROUP BY product_code, color_code, yaka_kodu ), +Stock90Start AS ( + SELECT DISTINCT ON (product_code, color_code, yaka_kodu) + product_code, + color_code, + yaka_kodu, + stock_qty + FROM mk_product_performance_stock_daily + WHERE stock_date <= current_date - INTERVAL '89 days' + ORDER BY product_code, color_code, yaka_kodu, stock_date DESC +), +Stock180Start AS ( + SELECT DISTINCT ON (product_code, color_code, yaka_kodu) + product_code, + color_code, + yaka_kodu, + stock_qty + FROM mk_product_performance_stock_daily + WHERE stock_date <= current_date - INTERVAL '179 days' + ORDER BY product_code, color_code, yaka_kodu, stock_date DESC +), +Stock365Start AS ( + SELECT DISTINCT ON (product_code, color_code, yaka_kodu) + product_code, + color_code, + yaka_kodu, + stock_qty + FROM mk_product_performance_stock_daily + WHERE stock_date <= current_date - INTERVAL '359 days' + ORDER BY product_code, color_code, yaka_kodu, stock_date DESC +), +StockTotalStart AS ( + SELECT DISTINCT ON (product_code, color_code, yaka_kodu) + product_code, + color_code, + yaka_kodu, + stock_qty + FROM mk_product_performance_stock_daily + WHERE stock_date <= DATE '2022-01-01' + ORDER BY product_code, color_code, yaka_kodu, stock_date DESC +), Agg AS ( SELECT $2::text AS breakdown, @@ -2567,6 +2637,10 @@ Agg AS ( %s, COUNT(DISTINCT product_code) AS product_count, COALESCE(MAX(stock_qty),0) AS stock_qty, + COALESCE(MAX(avg_stock_90d),0) AS avg_stock_90d, + COALESCE(MAX(avg_stock_180d),0) AS avg_stock_180d, + COALESCE(MAX(avg_stock_365d),0) AS avg_stock_365d, + COALESCE(MAX(avg_stock_total),0) AS avg_stock_total, COUNT(DISTINCT customer_code) FILTER (WHERE sales_date >= current_date - INTERVAL '89 days' AND COALESCE(customer_code,'') <> '')::integer AS customer_count_90d, COALESCE(SUM(invoice_count) FILTER (WHERE sales_date >= current_date - INTERVAL '89 days'),0)::integer AS invoice_count_90d, COALESCE(SUM(sales_qty) FILTER (WHERE sales_date >= current_date - INTERVAL '89 days'),0) AS sales_qty_90d, @@ -2637,11 +2711,19 @@ Agg AS ( s.*, COALESCE(pd.base_price_usd,0) AS base_price_usd, COALESCE(pd.cost_price_usd,0) AS cost_price_usd, - COALESCE(st.stock_qty,0) AS stock_qty + COALESCE(st.stock_qty,0) AS stock_qty, + (COALESCE(s90.stock_qty,0) + COALESCE(st.stock_qty,0)) / 2.0 AS avg_stock_90d, + (COALESCE(s180.stock_qty,0) + COALESCE(st.stock_qty,0)) / 2.0 AS avg_stock_180d, + (COALESCE(s365.stock_qty,0) + COALESCE(st.stock_qty,0)) / 2.0 AS avg_stock_365d, + (COALESCE(stotal.stock_qty,0) + COALESCE(st.stock_qty,0)) / 2.0 AS avg_stock_total FROM mk_product_performance_sales_daily s LEFT JOIN mk_product_performance_price_dim pd ON pd.product_code = s.product_code LEFT JOIN LatestKPI k ON k.product_code = s.product_code AND k.color_code = s.color_code AND k.yaka_kodu = s.yaka_kodu LEFT JOIN StockAgg st ON st.product_code = s.product_code AND st.color_code = s.color_code AND st.yaka_kodu = s.yaka_kodu + LEFT JOIN Stock90Start s90 ON s90.product_code = s.product_code AND s90.color_code = s.color_code AND s90.yaka_kodu = s.yaka_kodu + LEFT JOIN Stock180Start s180 ON s180.product_code = s.product_code AND s180.color_code = s.color_code AND s180.yaka_kodu = s.yaka_kodu + LEFT JOIN Stock365Start s365 ON s365.product_code = s.product_code AND s365.color_code = s.color_code AND s365.yaka_kodu = s.yaka_kodu + LEFT JOIN StockTotalStart stotal ON stotal.product_code = s.product_code AND stotal.color_code = s.color_code AND stotal.yaka_kodu = s.yaka_kodu ) s WHERE sales_date >= DATE '2022-01-01' GROUP BY %s @@ -2738,10 +2820,10 @@ SELECT gross_profit_cost_usd_total, gross_margin_base_total, gross_margin_cost_total, - CASE WHEN stock_qty > 0 THEN sales_qty_90d / NULLIF(stock_qty,0) ELSE 0 END AS stock_turnover_90d, - CASE WHEN stock_qty > 0 THEN sales_qty_180d / NULLIF(stock_qty,0) ELSE 0 END AS stock_turnover_180d, - CASE WHEN stock_qty > 0 THEN sales_qty_365d / NULLIF(stock_qty,0) ELSE 0 END AS stock_turnover_365d, - CASE WHEN stock_qty > 0 THEN sales_qty_total / NULLIF(stock_qty,0) ELSE 0 END AS stock_turnover_total, + CASE WHEN avg_stock_90d > 0 THEN sales_qty_90d / NULLIF(avg_stock_90d,0) ELSE 0 END AS stock_turnover_90d, + CASE WHEN avg_stock_180d > 0 THEN sales_qty_180d / NULLIF(avg_stock_180d,0) ELSE 0 END AS stock_turnover_180d, + CASE WHEN avg_stock_365d > 0 THEN sales_qty_365d / NULLIF(avg_stock_365d,0) ELSE 0 END AS stock_turnover_365d, + CASE WHEN avg_stock_total > 0 THEN sales_qty_total / NULLIF(avg_stock_total,0) ELSE 0 END AS stock_turnover_total, has_cost, sales_index_90d, CASE WHEN NOT has_cost THEN 0 ELSE @@ -2786,7 +2868,7 @@ SELECT + LEAST(30, GREATEST(gross_margin_cost_90d,0) * 60) + LEAST(15, invoice_count_90d * 1.5) + LEAST(10, sales_qty_90d / 10) - + LEAST(10, (CASE WHEN stock_qty > 0 THEN sales_qty_90d / NULLIF(stock_qty,0) ELSE 0 END) * 5), + + LEAST(10, (CASE WHEN avg_stock_90d > 0 THEN sales_qty_90d / NULLIF(avg_stock_90d,0) ELSE 0 END) * 5), 4 ) END AS performance_score, @@ -2982,9 +3064,11 @@ SELECT jsonb_build_object( 'sales_qty_90d', sales_qty_90d, 'sales_qty_180d', sales_qty_180d, 'sales_qty_365d', sales_qty_365d, + 'sales_qty_total', sales_qty_total, 'sales_usd_90d', sales_usd_90d, 'sales_usd_180d', sales_usd_180d, 'sales_usd_365d', sales_usd_365d, + 'sales_usd_total', sales_usd_total, 'avg_price_usd_90d', avg_price_usd_90d, 'avg_price_usd_180d', avg_price_usd_180d, 'cost_price_usd', cost_price_usd, @@ -3006,9 +3090,14 @@ SELECT jsonb_build_object( 'performance_score', performance_score, 'performance_bucket', performance_bucket, 'idle_cost_usd', idle_cost_usd, - 'stock_turnover_90d', CASE WHEN stock_qty > 0 THEN sales_qty_90d / NULLIF(stock_qty,0) ELSE 0 END, - 'stock_turnover_180d', CASE WHEN stock_qty > 0 THEN sales_qty_180d / NULLIF(stock_qty,0) ELSE 0 END, - 'stock_turnover_365d', CASE WHEN stock_qty > 0 THEN sales_qty_365d / NULLIF(stock_qty,0) ELSE 0 END + 'stock_days_90d', stock_days_90d, + 'stock_days_180d', stock_days_180d, + 'stock_days_365d', stock_days_365d, + 'stock_days_total', stock_days_total, + 'stock_turnover_90d', CASE WHEN avg_stock_90d > 0 THEN sales_qty_90d / NULLIF(avg_stock_90d,0) ELSE 0 END, + 'stock_turnover_180d', CASE WHEN avg_stock_180d > 0 THEN sales_qty_180d / NULLIF(avg_stock_180d,0) ELSE 0 END, + 'stock_turnover_365d', CASE WHEN avg_stock_365d > 0 THEN sales_qty_365d / NULLIF(avg_stock_365d,0) ELSE 0 END, + 'stock_turnover_total', CASE WHEN avg_stock_total > 0 THEN sales_qty_total / NULLIF(avg_stock_total,0) ELSE 0 END ) AS row_json FROM ( SELECT @@ -3028,12 +3117,22 @@ FROM ( COALESCE((ARRAY_AGG(NULLIF(urun_alt_grubu,'') ORDER BY performance_score DESC NULLS LAST))[1], '') AS urun_alt_grubu, COALESCE((ARRAY_AGG(NULLIF(market_key,'') ORDER BY performance_score DESC NULLS LAST))[1], '') AS market_key, COALESCE(SUM(CASE WHEN stock_variant_rank = 1 THEN stock_qty ELSE 0 END),0) AS stock_qty, + COALESCE(SUM(CASE WHEN stock_variant_rank = 1 THEN avg_stock_90d ELSE 0 END),0) AS avg_stock_90d, + COALESCE(SUM(CASE WHEN stock_variant_rank = 1 THEN avg_stock_180d ELSE 0 END),0) AS avg_stock_180d, + COALESCE(SUM(CASE WHEN stock_variant_rank = 1 THEN avg_stock_365d ELSE 0 END),0) AS avg_stock_365d, + COALESCE(SUM(CASE WHEN stock_variant_rank = 1 THEN avg_stock_total ELSE 0 END),0) AS avg_stock_total, COALESCE(SUM(sales_qty_90d),0) AS sales_qty_90d, COALESCE(SUM(sales_qty_180d),0) AS sales_qty_180d, COALESCE(SUM(sales_qty_365d),0) AS sales_qty_365d, + COALESCE(SUM(sales_qty_total),0) AS sales_qty_total, COALESCE(SUM(sales_usd_90d),0) AS sales_usd_90d, COALESCE(SUM(sales_usd_180d),0) AS sales_usd_180d, COALESCE(SUM(sales_usd_365d),0) AS sales_usd_365d, + COALESCE(SUM(sales_usd_total),0) AS sales_usd_total, + CASE WHEN SUM(CASE WHEN stock_variant_rank = 1 THEN stock_qty ELSE 0 END) > 0 THEN SUM(CASE WHEN stock_variant_rank = 1 THEN stock_days_90d * stock_qty ELSE 0 END) / NULLIF(SUM(CASE WHEN stock_variant_rank = 1 THEN stock_qty ELSE 0 END),0) ELSE 0 END AS stock_days_90d, + CASE WHEN SUM(CASE WHEN stock_variant_rank = 1 THEN stock_qty ELSE 0 END) > 0 THEN SUM(CASE WHEN stock_variant_rank = 1 THEN stock_days_180d * stock_qty ELSE 0 END) / NULLIF(SUM(CASE WHEN stock_variant_rank = 1 THEN stock_qty ELSE 0 END),0) ELSE 0 END AS stock_days_180d, + CASE WHEN SUM(CASE WHEN stock_variant_rank = 1 THEN stock_qty ELSE 0 END) > 0 THEN SUM(CASE WHEN stock_variant_rank = 1 THEN stock_days_365d * stock_qty ELSE 0 END) / NULLIF(SUM(CASE WHEN stock_variant_rank = 1 THEN stock_qty ELSE 0 END),0) ELSE 0 END AS stock_days_365d, + CASE WHEN SUM(CASE WHEN stock_variant_rank = 1 THEN stock_qty ELSE 0 END) > 0 THEN SUM(CASE WHEN stock_variant_rank = 1 THEN stock_days_total * stock_qty ELSE 0 END) / NULLIF(SUM(CASE WHEN stock_variant_rank = 1 THEN stock_qty ELSE 0 END),0) ELSE 0 END AS stock_days_total, CASE WHEN SUM(sales_qty_90d) > 0 THEN SUM(sales_usd_90d) / NULLIF(SUM(sales_qty_90d),0) ELSE 0 END AS avg_price_usd_90d, CASE WHEN SUM(sales_qty_180d) > 0 THEN SUM(sales_usd_180d) / NULLIF(SUM(sales_qty_180d),0) ELSE 0 END AS avg_price_usd_180d, CASE WHEN SUM(CASE WHEN stock_variant_rank = 1 THEN stock_qty ELSE 0 END) > 0 THEN SUM(CASE WHEN stock_variant_rank = 1 THEN cost_price_usd * stock_qty ELSE 0 END) / NULLIF(SUM(CASE WHEN stock_variant_rank = 1 THEN stock_qty ELSE 0 END),0) ELSE 0 END AS cost_price_usd, @@ -3082,9 +3181,10 @@ func queryProductPerformanceSQLLeafRows(ctx context.Context, pg *sql.DB, mode st query := productPerformanceSQLSourceCTE(mode) + fmt.Sprintf(` SELECT to_jsonb(t) || jsonb_build_object( 'row_key', 'leaf|' || product_code || '|' || color_code || '|' || yaka_kodu || '|' || market_key, - 'stock_turnover_90d', CASE WHEN stock_qty > 0 THEN sales_qty_90d / NULLIF(stock_qty,0) ELSE 0 END, - 'stock_turnover_180d', CASE WHEN stock_qty > 0 THEN sales_qty_180d / NULLIF(stock_qty,0) ELSE 0 END, - 'stock_turnover_365d', CASE WHEN stock_qty > 0 THEN sales_qty_365d / NULLIF(stock_qty,0) ELSE 0 END + 'stock_turnover_90d', CASE WHEN avg_stock_90d > 0 THEN sales_qty_90d / NULLIF(avg_stock_90d,0) ELSE 0 END, + 'stock_turnover_180d', CASE WHEN avg_stock_180d > 0 THEN sales_qty_180d / NULLIF(avg_stock_180d,0) ELSE 0 END, + 'stock_turnover_365d', CASE WHEN avg_stock_365d > 0 THEN sales_qty_365d / NULLIF(avg_stock_365d,0) ELSE 0 END, + 'stock_turnover_total', CASE WHEN avg_stock_total > 0 THEN sales_qty_total / NULLIF(avg_stock_total,0) ELSE 0 END ) AS row_json FROM Source t %s @@ -3123,7 +3223,39 @@ func productPerformanceSQLSourceCTE(mode string) string { AND (performance_bucket = 'STOK_RISKI' OR COALESCE(sales_qty_90d,0) = 0 OR COALESCE(stock_days_90d,0) > 180)` } return ` -WITH Source AS ( +WITH LatestKPIDate AS ( + SELECT MAX(kpi_date) AS kpi_date + FROM mk_product_performance_kpi_daily +), +Stock90Start AS ( + SELECT DISTINCT ON (product_code, color_code, yaka_kodu) + product_code, color_code, yaka_kodu, stock_qty + FROM mk_product_performance_stock_daily, LatestKPIDate + WHERE stock_date <= LatestKPIDate.kpi_date - INTERVAL '89 days' + ORDER BY product_code, color_code, yaka_kodu, stock_date DESC +), +Stock180Start AS ( + SELECT DISTINCT ON (product_code, color_code, yaka_kodu) + product_code, color_code, yaka_kodu, stock_qty + FROM mk_product_performance_stock_daily, LatestKPIDate + WHERE stock_date <= LatestKPIDate.kpi_date - INTERVAL '179 days' + ORDER BY product_code, color_code, yaka_kodu, stock_date DESC +), +Stock365Start AS ( + SELECT DISTINCT ON (product_code, color_code, yaka_kodu) + product_code, color_code, yaka_kodu, stock_qty + FROM mk_product_performance_stock_daily, LatestKPIDate + WHERE stock_date <= LatestKPIDate.kpi_date - INTERVAL '359 days' + ORDER BY product_code, color_code, yaka_kodu, stock_date DESC +), +StockTotalStart AS ( + SELECT DISTINCT ON (product_code, color_code, yaka_kodu) + product_code, color_code, yaka_kodu, stock_qty + FROM mk_product_performance_stock_daily + WHERE stock_date <= DATE '2022-01-01' + ORDER BY product_code, color_code, yaka_kodu, stock_date DESC +), +Source AS ( SELECT product_code, color_code, @@ -3142,12 +3274,22 @@ WITH Source AS ( urun_alt_grubu, market_key, COALESCE(stock_qty,0) AS stock_qty, + (COALESCE((SELECT s.stock_qty FROM Stock90Start s WHERE s.product_code = mk_product_performance_kpi_daily.product_code AND s.color_code = mk_product_performance_kpi_daily.color_code AND s.yaka_kodu = mk_product_performance_kpi_daily.yaka_kodu),0) + COALESCE(stock_qty,0)) / 2.0 AS avg_stock_90d, + (COALESCE((SELECT s.stock_qty FROM Stock180Start s WHERE s.product_code = mk_product_performance_kpi_daily.product_code AND s.color_code = mk_product_performance_kpi_daily.color_code AND s.yaka_kodu = mk_product_performance_kpi_daily.yaka_kodu),0) + COALESCE(stock_qty,0)) / 2.0 AS avg_stock_180d, + (COALESCE((SELECT s.stock_qty FROM Stock365Start s WHERE s.product_code = mk_product_performance_kpi_daily.product_code AND s.color_code = mk_product_performance_kpi_daily.color_code AND s.yaka_kodu = mk_product_performance_kpi_daily.yaka_kodu),0) + COALESCE(stock_qty,0)) / 2.0 AS avg_stock_365d, + (COALESCE((SELECT s.stock_qty FROM StockTotalStart s WHERE s.product_code = mk_product_performance_kpi_daily.product_code AND s.color_code = mk_product_performance_kpi_daily.color_code AND s.yaka_kodu = mk_product_performance_kpi_daily.yaka_kodu),0) + COALESCE(stock_qty,0)) / 2.0 AS avg_stock_total, COALESCE(sales_qty_90d,0) AS sales_qty_90d, COALESCE(sales_qty_180d,0) AS sales_qty_180d, COALESCE(sales_qty_365d,0) AS sales_qty_365d, + COALESCE(sales_qty_total,0) AS sales_qty_total, COALESCE(sales_usd_90d,0) AS sales_usd_90d, COALESCE(sales_usd_180d,0) AS sales_usd_180d, COALESCE(sales_usd_365d,0) AS sales_usd_365d, + COALESCE(sales_usd_total,0) AS sales_usd_total, + COALESCE(stock_days_90d,0) AS stock_days_90d, + COALESCE(stock_days_180d,0) AS stock_days_180d, + COALESCE(stock_days_365d,0) AS stock_days_365d, + COALESCE(stock_days_total,0) AS stock_days_total, COALESCE(avg_price_usd_90d,0) AS avg_price_usd_90d, COALESCE(avg_price_usd_180d,0) AS avg_price_usd_180d, COALESCE(cost_price_usd,0) AS cost_price_usd, @@ -3430,7 +3572,7 @@ func deriveProductPerformanceGroupMetrics(out map[string]any, groupField string) sales := floatFromMap(out, "sales_usd_"+suffix) qty := floatFromMap(out, "sales_qty_"+suffix) stockQty := floatFromMap(out, "stock_qty") - if stockQty > 0 { + if _, exists := out["stock_turnover_"+suffix]; !exists && stockQty > 0 { out["stock_turnover_"+suffix] = qty / stockQty } if qty > 0 { @@ -4012,10 +4154,20 @@ func productPerformanceOrderBy(sortBy string, desc bool) string { "stock_qty": "stock_qty", "sales_qty_90d": "sales_qty_90d", "sales_qty_180d": "sales_qty_180d", + "sales_qty_365d": "sales_qty_365d", + "sales_qty_total": "sales_qty_total", "sales_usd_90d": "sales_usd_90d", "sales_usd_180d": "sales_usd_180d", + "sales_usd_365d": "sales_usd_365d", + "sales_usd_total": "sales_usd_total", "stock_days_90d": "stock_days_90d", "stock_days_180d": "stock_days_180d", + "stock_days_365d": "stock_days_365d", + "stock_days_total": "stock_days_total", + "stock_turnover_90d": "stock_turnover_90d", + "stock_turnover_180d": "stock_turnover_180d", + "stock_turnover_365d": "stock_turnover_365d", + "stock_turnover_total": "stock_turnover_total", "avg_price_usd_90d": "avg_price_usd_90d", "avg_price_usd_180d": "avg_price_usd_180d", "base_price_usd": "base_price_usd", diff --git a/svc/queries/product_performance_sql.go b/svc/queries/product_performance_sql.go index 258cfd9..4bc897b 100644 --- a/svc/queries/product_performance_sql.go +++ b/svc/queries/product_performance_sql.go @@ -396,6 +396,34 @@ WITH LatestStock AS ( WHERE stock_date <= $1 ORDER BY product_code, color_code, yaka_kodu, stock_date DESC ), +Stock90Start AS ( + SELECT DISTINCT ON (product_code, color_code, yaka_kodu) + product_code, color_code, yaka_kodu, stock_qty + FROM mk_product_performance_stock_daily + WHERE stock_date <= $1::date - INTERVAL '89 days' + ORDER BY product_code, color_code, yaka_kodu, stock_date DESC +), +Stock180Start AS ( + SELECT DISTINCT ON (product_code, color_code, yaka_kodu) + product_code, color_code, yaka_kodu, stock_qty + FROM mk_product_performance_stock_daily + WHERE stock_date <= $1::date - INTERVAL '179 days' + ORDER BY product_code, color_code, yaka_kodu, stock_date DESC +), +Stock365Start AS ( + SELECT DISTINCT ON (product_code, color_code, yaka_kodu) + product_code, color_code, yaka_kodu, stock_qty + FROM mk_product_performance_stock_daily + WHERE stock_date <= $1::date - INTERVAL '359 days' + ORDER BY product_code, color_code, yaka_kodu, stock_date DESC +), +StockTotalStart AS ( + SELECT DISTINCT ON (product_code, color_code, yaka_kodu) + product_code, color_code, yaka_kodu, stock_qty + FROM mk_product_performance_stock_daily + WHERE stock_date <= DATE '2022-01-01' + ORDER BY product_code, color_code, yaka_kodu, stock_date DESC +), SalesAgg AS ( SELECT product_code, @@ -415,15 +443,17 @@ SalesAgg AS ( SUM(sales_qty) FILTER (WHERE sales_date >= $1::date - INTERVAL '179 days') AS sales_qty_180d, SUM(sales_qty) FILTER (WHERE sales_date >= $1::date - INTERVAL '359 days') AS sales_qty_365d, SUM(sales_qty) FILTER (WHERE sales_date >= $1::date - INTERVAL '729 days') AS sales_qty_730d, + SUM(sales_qty) FILTER (WHERE sales_date >= DATE '2022-01-01') AS sales_qty_total, SUM(sales_usd) FILTER (WHERE sales_date >= $1::date - INTERVAL '29 days') AS sales_usd_30d, SUM(sales_usd) FILTER (WHERE sales_date >= $1::date - INTERVAL '89 days') AS sales_usd_90d, SUM(sales_usd) FILTER (WHERE sales_date >= $1::date - INTERVAL '179 days') AS sales_usd_180d, SUM(sales_usd) FILTER (WHERE sales_date >= $1::date - INTERVAL '359 days') AS sales_usd_365d, + SUM(sales_usd) FILTER (WHERE sales_date >= DATE '2022-01-01') AS sales_usd_total, COUNT(DISTINCT NULLIF(customer_code, '-')) FILTER (WHERE sales_date >= $1::date - INTERVAL '89 days') AS customer_count_90d, MAX(sales_date) AS last_sale_date, MAX(last_ref_number) AS last_ref_number FROM mk_product_performance_sales_daily - WHERE sales_date >= $1::date - INTERVAL '729 days' + WHERE sales_date >= DATE '2022-01-01' AND sales_date <= $1::date GROUP BY product_code, color_code, yaka_kodu, market_key ), @@ -431,8 +461,8 @@ Scope AS ( SELECT product_code, color_code, yaka_kodu, market_key, item_description, kategori, seri, yas_grubu, askili_yan, urun_ilk_grubu, urun_ana_grubu, urun_alt_grubu, - sales_qty_30d, sales_qty_90d, sales_qty_180d, sales_qty_365d, sales_qty_730d, - sales_usd_30d, sales_usd_90d, sales_usd_180d, sales_usd_365d, + sales_qty_30d, sales_qty_90d, sales_qty_180d, sales_qty_365d, sales_qty_730d, sales_qty_total, + sales_usd_30d, sales_usd_90d, sales_usd_180d, sales_usd_365d, sales_usd_total, customer_count_90d, last_sale_date, last_ref_number FROM SalesAgg UNION ALL @@ -454,10 +484,12 @@ Scope AS ( 0 AS sales_qty_180d, 0 AS sales_qty_365d, 0 AS sales_qty_730d, + 0 AS sales_qty_total, 0 AS sales_usd_30d, 0 AS sales_usd_90d, 0 AS sales_usd_180d, 0 AS sales_usd_365d, + 0 AS sales_usd_total, 0 AS customer_count_90d, NULL::date AS last_sale_date, '' AS last_ref_number @@ -480,6 +512,12 @@ Base AS ( COALESCE(pd.base_price_try, 0) AS base_price_try, COALESCE(s.sales_qty_90d, 0) / 90.0 AS avg_daily_sales_90d, COALESCE(s.sales_qty_180d, 0) / 180.0 AS avg_daily_sales_180d, + COALESCE(s.sales_qty_365d, 0) / 360.0 AS avg_daily_sales_365d, + COALESCE(s.sales_qty_total, 0) / GREATEST(1, ($1::date - DATE '2022-01-01') + 1) AS avg_daily_sales_total, + (COALESCE(s90.stock_qty, 0) + COALESCE(ls.stock_qty, 0)) / 2.0 AS avg_stock_90d, + (COALESCE(s180.stock_qty, 0) + COALESCE(ls.stock_qty, 0)) / 2.0 AS avg_stock_180d, + (COALESCE(s365.stock_qty, 0) + COALESCE(ls.stock_qty, 0)) / 2.0 AS avg_stock_365d, + (COALESCE(stotal.stock_qty, 0) + COALESCE(ls.stock_qty, 0)) / 2.0 AS avg_stock_total, CASE WHEN COALESCE(s.sales_qty_90d, 0) = 0 THEN 0 ELSE COALESCE(s.sales_usd_90d, 0) / NULLIF(s.sales_qty_90d, 0) END AS avg_price_usd_90d, CASE WHEN COALESCE(s.sales_qty_180d, 0) = 0 THEN 0 ELSE COALESCE(s.sales_usd_180d, 0) / NULLIF(s.sales_qty_180d, 0) END AS avg_price_usd_180d, COALESCE(s.sales_usd_90d, 0) - (COALESCE(s.sales_qty_90d, 0) * COALESCE(pd.cost_price_usd, 0)) AS gross_profit_usd_90d, @@ -496,6 +534,10 @@ Base AS ( CASE WHEN COALESCE(s.sales_qty_180d, 0) = 0 THEN 0 ELSE (COALESCE(s.sales_usd_180d, 0) / NULLIF(s.sales_qty_180d, 0)) - COALESCE(pd.base_price_usd, 0) END AS unit_profit_base_180d FROM Scope s LEFT JOIN LatestStock ls ON ls.product_code=s.product_code AND ls.color_code=s.color_code AND ls.yaka_kodu=s.yaka_kodu + LEFT JOIN Stock90Start s90 ON s90.product_code=s.product_code AND s90.color_code=s.color_code AND s90.yaka_kodu=s.yaka_kodu + LEFT JOIN Stock180Start s180 ON s180.product_code=s.product_code AND s180.color_code=s.color_code AND s180.yaka_kodu=s.yaka_kodu + LEFT JOIN Stock365Start s365 ON s365.product_code=s.product_code AND s365.color_code=s.color_code AND s365.yaka_kodu=s.yaka_kodu + LEFT JOIN StockTotalStart stotal ON stotal.product_code=s.product_code AND stotal.color_code=s.color_code AND stotal.yaka_kodu=s.yaka_kodu LEFT JOIN mk_product_performance_price_dim pd ON pd.product_code=s.product_code ), Spread AS ( @@ -521,8 +563,14 @@ Scored AS ( SELECT b.*, COALESCE(sp.market_count_90d, 0) AS market_count_90d, - CASE WHEN b.avg_daily_sales_90d = 0 THEN 0 ELSE b.stock_qty / NULLIF(b.avg_daily_sales_90d, 0) END AS stock_days_90d, - CASE WHEN b.avg_daily_sales_180d = 0 THEN 0 ELSE b.stock_qty / NULLIF(b.avg_daily_sales_180d, 0) END AS stock_days_180d, + CASE WHEN b.avg_daily_sales_90d = 0 THEN 0 ELSE b.avg_stock_90d / NULLIF(b.avg_daily_sales_90d, 0) END AS stock_days_90d, + CASE WHEN b.avg_daily_sales_180d = 0 THEN 0 ELSE b.avg_stock_180d / NULLIF(b.avg_daily_sales_180d, 0) END AS stock_days_180d, + CASE WHEN b.avg_daily_sales_365d = 0 THEN 0 ELSE b.avg_stock_365d / NULLIF(b.avg_daily_sales_365d, 0) END AS stock_days_365d, + CASE WHEN b.avg_daily_sales_total = 0 THEN 0 ELSE b.avg_stock_total / NULLIF(b.avg_daily_sales_total, 0) END AS stock_days_total, + CASE WHEN b.avg_stock_90d = 0 THEN 0 ELSE b.sales_qty_90d / NULLIF(b.avg_stock_90d, 0) END AS stock_turnover_90d, + CASE WHEN b.avg_stock_180d = 0 THEN 0 ELSE b.sales_qty_180d / NULLIF(b.avg_stock_180d, 0) END AS stock_turnover_180d, + CASE WHEN b.avg_stock_365d = 0 THEN 0 ELSE b.sales_qty_365d / NULLIF(b.avg_stock_365d, 0) END AS stock_turnover_365d, + CASE WHEN b.avg_stock_total = 0 THEN 0 ELSE b.sales_qty_total / NULLIF(b.avg_stock_total, 0) END AS stock_turnover_total, CASE WHEN COALESCE(m.market_avg_sales_qty_90d, 0) = 0 THEN 0 ELSE b.sales_qty_90d / NULLIF(m.market_avg_sales_qty_90d, 0) END AS sales_index_90d, CASE WHEN COALESCE(m.market_avg_price_usd_90d, 0) = 0 THEN 0 ELSE b.avg_price_usd_90d / NULLIF(m.market_avg_price_usd_90d, 0) END AS price_index_90d, CASE WHEN COALESCE(m.market_avg_margin_90d, 0) = 0 THEN 0 ELSE b.gross_margin_90d / NULLIF(m.market_avg_margin_90d, 0) END AS margin_index_90d @@ -533,9 +581,10 @@ Scored AS ( INSERT INTO mk_product_performance_kpi_daily ( kpi_date, product_code, color_code, yaka_kodu, item_description, kategori, seri, yas_grubu, askili_yan, urun_ilk_grubu, urun_ana_grubu, urun_alt_grubu, market_key, stock_qty, - sales_qty_30d, sales_qty_90d, sales_qty_180d, sales_qty_365d, sales_qty_730d, - sales_usd_30d, sales_usd_90d, sales_usd_180d, sales_usd_365d, avg_daily_sales_90d, avg_daily_sales_180d, - stock_days_90d, stock_days_180d, avg_price_usd_90d, avg_price_usd_180d, cost_price_usd, base_price_usd, base_price_try, + sales_qty_30d, sales_qty_90d, sales_qty_180d, sales_qty_365d, sales_qty_730d, sales_qty_total, + sales_usd_30d, sales_usd_90d, sales_usd_180d, sales_usd_365d, sales_usd_total, avg_daily_sales_90d, avg_daily_sales_180d, avg_daily_sales_365d, avg_daily_sales_total, + stock_days_90d, stock_days_180d, stock_days_365d, stock_days_total, stock_turnover_90d, stock_turnover_180d, stock_turnover_365d, stock_turnover_total, + avg_price_usd_90d, avg_price_usd_180d, cost_price_usd, base_price_usd, base_price_try, gross_profit_usd_90d, gross_profit_usd_180d, gross_margin_90d, gross_margin_180d, unit_profit_cost_90d, unit_profit_cost_180d, unit_profit_base_90d, unit_profit_base_180d, market_count_90d, customer_count_90d, sales_index_90d, price_index_90d, margin_index_90d, performance_score, @@ -545,9 +594,10 @@ SELECT $1::date, product_code, color_code, yaka_kodu, item_description, kategori, seri, yas_grubu, askili_yan, urun_ilk_grubu, urun_ana_grubu, urun_alt_grubu, market_key, stock_qty, - COALESCE(sales_qty_30d, 0), COALESCE(sales_qty_90d, 0), COALESCE(sales_qty_180d, 0), COALESCE(sales_qty_365d, 0), COALESCE(sales_qty_730d, 0), - COALESCE(sales_usd_30d, 0), COALESCE(sales_usd_90d, 0), COALESCE(sales_usd_180d, 0), COALESCE(sales_usd_365d, 0), COALESCE(avg_daily_sales_90d, 0), COALESCE(avg_daily_sales_180d, 0), - COALESCE(stock_days_90d, 0), COALESCE(stock_days_180d, 0), COALESCE(avg_price_usd_90d, 0), COALESCE(avg_price_usd_180d, 0), COALESCE(cost_price_usd, 0), COALESCE(base_price_usd, 0), COALESCE(base_price_try, 0), + COALESCE(sales_qty_30d, 0), COALESCE(sales_qty_90d, 0), COALESCE(sales_qty_180d, 0), COALESCE(sales_qty_365d, 0), COALESCE(sales_qty_730d, 0), COALESCE(sales_qty_total, 0), + COALESCE(sales_usd_30d, 0), COALESCE(sales_usd_90d, 0), COALESCE(sales_usd_180d, 0), COALESCE(sales_usd_365d, 0), COALESCE(sales_usd_total, 0), COALESCE(avg_daily_sales_90d, 0), COALESCE(avg_daily_sales_180d, 0), COALESCE(avg_daily_sales_365d, 0), COALESCE(avg_daily_sales_total, 0), + COALESCE(stock_days_90d, 0), COALESCE(stock_days_180d, 0), COALESCE(stock_days_365d, 0), COALESCE(stock_days_total, 0), COALESCE(stock_turnover_90d, 0), COALESCE(stock_turnover_180d, 0), COALESCE(stock_turnover_365d, 0), COALESCE(stock_turnover_total, 0), + COALESCE(avg_price_usd_90d, 0), COALESCE(avg_price_usd_180d, 0), COALESCE(cost_price_usd, 0), COALESCE(base_price_usd, 0), COALESCE(base_price_try, 0), COALESCE(gross_profit_usd_90d, 0), COALESCE(gross_profit_usd_180d, 0), COALESCE(gross_margin_90d, 0), COALESCE(gross_margin_180d, 0), COALESCE(unit_profit_cost_90d, 0), COALESCE(unit_profit_cost_180d, 0), COALESCE(unit_profit_base_90d, 0), COALESCE(unit_profit_base_180d, 0), COALESCE(market_count_90d, 0), COALESCE(customer_count_90d, 0), COALESCE(sales_index_90d, 0), COALESCE(price_index_90d, 0), COALESCE(margin_index_90d, 0), diff --git a/svc/queries/productstockquery.go b/svc/queries/productstockquery.go index 365a556..4b7ea20 100644 --- a/svc/queries/productstockquery.go +++ b/svc/queries/productstockquery.go @@ -3,7 +3,7 @@ package queries // GetProductStockQuery: // Urun kodu bazli, STOCK/PICK/RESERVE/DISP ayrik CTE ile optimize sorgu. const GetProductStockQuery = ` -DECLARE @ProductCode NVARCHAR(50) = @p1; +DECLARE @ProductCode NVARCHAR(50) = UPPER(LTRIM(RTRIM(@p1))); ;WITH STOCK AS ( @@ -14,7 +14,7 @@ DECLARE @ProductCode NVARCHAR(50) = @p1; StoreCode, WarehouseCode, ItemTypeCode, - ItemCode, + UPPER(LTRIM(RTRIM(ItemCode))) AS ItemCode, ColorCode, ItemDim1Code, ItemDim2Code, @@ -22,12 +22,12 @@ DECLARE @ProductCode NVARCHAR(50) = @p1; SUM(In_Qty1 - Out_Qty1) AS InventoryQty1 FROM trStock WHERE ItemTypeCode = 1 - AND ItemCode = @ProductCode - AND LEN(ItemCode) = 13 + AND UPPER(LTRIM(RTRIM(ItemCode))) = @ProductCode + AND LEN(LTRIM(RTRIM(ItemCode))) = 13 AND LEN(@ProductCode) = 13 GROUP BY CompanyCode, OfficeCode, StoreTypeCode, StoreCode, WarehouseCode, - ItemTypeCode, ItemCode, ColorCode, + ItemTypeCode, UPPER(LTRIM(RTRIM(ItemCode))), ColorCode, ItemDim1Code, ItemDim2Code, ItemDim3Code ), @@ -35,17 +35,17 @@ PICK AS ( SELECT CompanyCode, OfficeCode, StoreTypeCode, StoreCode, WarehouseCode, - ItemTypeCode, ItemCode, ColorCode, + ItemTypeCode, UPPER(LTRIM(RTRIM(ItemCode))) AS ItemCode, ColorCode, ItemDim1Code, ItemDim2Code, ItemDim3Code, SUM(Qty1) AS PickingQty1 FROM PickingStates WHERE ItemTypeCode = 1 - AND ItemCode = @ProductCode - AND LEN(ItemCode) = 13 + AND UPPER(LTRIM(RTRIM(ItemCode))) = @ProductCode + AND LEN(LTRIM(RTRIM(ItemCode))) = 13 AND LEN(@ProductCode) = 13 GROUP BY CompanyCode, OfficeCode, StoreTypeCode, StoreCode, WarehouseCode, - ItemTypeCode, ItemCode, ColorCode, + ItemTypeCode, UPPER(LTRIM(RTRIM(ItemCode))), ColorCode, ItemDim1Code, ItemDim2Code, ItemDim3Code ), @@ -53,17 +53,17 @@ RESERVE AS ( SELECT CompanyCode, OfficeCode, StoreTypeCode, StoreCode, WarehouseCode, - ItemTypeCode, ItemCode, ColorCode, + ItemTypeCode, UPPER(LTRIM(RTRIM(ItemCode))) AS ItemCode, ColorCode, ItemDim1Code, ItemDim2Code, ItemDim3Code, SUM(Qty1) AS ReserveQty1 FROM ReserveStates WHERE ItemTypeCode = 1 - AND ItemCode = @ProductCode - AND LEN(ItemCode) = 13 + AND UPPER(LTRIM(RTRIM(ItemCode))) = @ProductCode + AND LEN(LTRIM(RTRIM(ItemCode))) = 13 AND LEN(@ProductCode) = 13 GROUP BY CompanyCode, OfficeCode, StoreTypeCode, StoreCode, WarehouseCode, - ItemTypeCode, ItemCode, ColorCode, + ItemTypeCode, UPPER(LTRIM(RTRIM(ItemCode))), ColorCode, ItemDim1Code, ItemDim2Code, ItemDim3Code ), @@ -71,17 +71,17 @@ DISP AS ( SELECT CompanyCode, OfficeCode, StoreTypeCode, StoreCode, WarehouseCode, - ItemTypeCode, ItemCode, ColorCode, + ItemTypeCode, UPPER(LTRIM(RTRIM(ItemCode))) AS ItemCode, ColorCode, ItemDim1Code, ItemDim2Code, ItemDim3Code, SUM(Qty1) AS DispOrderQty1 FROM DispOrderStates WHERE ItemTypeCode = 1 - AND ItemCode = @ProductCode - AND LEN(ItemCode) = 13 + AND UPPER(LTRIM(RTRIM(ItemCode))) = @ProductCode + AND LEN(LTRIM(RTRIM(ItemCode))) = 13 AND LEN(@ProductCode) = 13 GROUP BY CompanyCode, OfficeCode, StoreTypeCode, StoreCode, WarehouseCode, - ItemTypeCode, ItemCode, ColorCode, + ItemTypeCode, UPPER(LTRIM(RTRIM(ItemCode))), ColorCode, ItemDim1Code, ItemDim2Code, ItemDim3Code ) @@ -185,14 +185,14 @@ AND DP.ItemDim2Code=S.ItemDim2Code AND DP.ItemDim3Code=S.ItemDim3Code JOIN cdItem -ON S.ItemCode = cdItem.ItemCode +ON UPPER(LTRIM(RTRIM(S.ItemCode))) = UPPER(LTRIM(RTRIM(cdItem.ItemCode))) AND S.ItemTypeCode = cdItem.ItemTypeCode LEFT JOIN cdUnitOfMeasure ON cdItem.UnitOfMeasureCode1 = cdUnitOfMeasure.UnitOfMeasureCode LEFT JOIN ProductFilterWithDescription('TR') P -ON P.ProductCode = S.ItemCode +ON UPPER(LTRIM(RTRIM(P.ProductCode))) = UPPER(LTRIM(RTRIM(S.ItemCode))) LEFT JOIN bsItemTypeDesc ON bsItemTypeDesc.ItemTypeCode = S.ItemTypeCode @@ -214,14 +214,14 @@ LEFT JOIN ( ROW_NUMBER() OVER (PARTITION BY ItemCode, ItemTypeCode ORDER BY PriceDate DESC) AS RowNum FROM prItemBasePrice ) prFilteredBasePrice -ON prFilteredBasePrice.ItemCode = S.ItemCode +ON UPPER(LTRIM(RTRIM(prFilteredBasePrice.ItemCode))) = UPPER(LTRIM(RTRIM(S.ItemCode))) AND prFilteredBasePrice.ItemTypeCode = S.ItemTypeCode AND prFilteredBasePrice.RowNum = 1 WHERE S.ItemTypeCode IN (1) - AND S.ItemCode = @ProductCode - AND LEN(S.ItemCode) = 13 + AND UPPER(LTRIM(RTRIM(S.ItemCode))) = @ProductCode + AND LEN(LTRIM(RTRIM(S.ItemCode))) = 13 AND LEN(@ProductCode) = 13 AND ( S.InventoryQty1 @@ -234,6 +234,6 @@ WHERE ( '1-0-14','1-0-10','1-0-8','1-2-5','1-2-4','1-0-12','100','1-0-28', '1-0-24','1-2-6','1-1-14','1-0-2','1-0-52','1-1-2','1-0-21','1-1-3', - '1-0-33','101','1-014','1-0-49','1-0-36' + '1-0-33','101','1-014','1-0-49','1-0-36','1-0-4','1-0-29' ); ` diff --git a/svc/queries/productstockquery_by_attributes.go b/svc/queries/productstockquery_by_attributes.go index c1909c6..60c4eea 100644 --- a/svc/queries/productstockquery_by_attributes.go +++ b/svc/queries/productstockquery_by_attributes.go @@ -28,7 +28,7 @@ IF OBJECT_ID('dbo.ProductFilterTRCache','U') IS NOT NULL BEGIN INSERT INTO #AttrBase (ProductCode, Kategori, UrunAnaGrubu, UrunAltGrubu, UrunIcerigi, Fit, DropVal) SELECT - ProductCode, + UPPER(LTRIM(RTRIM(ProductCode))) AS ProductCode, Kategori = LTRIM(RTRIM(ProductAtt44Desc)), UrunAnaGrubu = LTRIM(RTRIM(ProductAtt01Desc)), UrunAltGrubu = LTRIM(RTRIM(ProductAtt02Desc)), @@ -48,7 +48,7 @@ ELSE BEGIN INSERT INTO #AttrBase (ProductCode, Kategori, UrunAnaGrubu, UrunAltGrubu, UrunIcerigi, Fit, DropVal) SELECT - ProductCode, + UPPER(LTRIM(RTRIM(ProductCode))) AS ProductCode, Kategori = LTRIM(RTRIM(ProductAtt44Desc)), UrunAnaGrubu = LTRIM(RTRIM(ProductAtt01Desc)), UrunAltGrubu = LTRIM(RTRIM(ProductAtt02Desc)), @@ -101,40 +101,40 @@ CREATE NONCLUSTERED INDEX IX_AttrBase_Filter ON #AttrBase(Kategori, UrunAnaGrubu FROM ( SELECT - P.ItemCode, P.ColorCode, P.ItemDim1Code, P.ItemDim2Code, + UPPER(LTRIM(RTRIM(P.ItemCode))) AS ItemCode, P.ColorCode, P.ItemDim1Code, P.ItemDim2Code, P.Qty1 AS PickingQty1, 0 AS ReserveQty1, 0 AS DispOrderQty1, 0 AS InventoryQty1 FROM PickingStates P - INNER JOIN #AttrBase AB ON AB.ProductCode = P.ItemCode + INNER JOIN #AttrBase AB ON AB.ProductCode = UPPER(LTRIM(RTRIM(P.ItemCode))) WHERE P.ItemTypeCode = 1 - AND LEN(P.ItemCode) = 13 + AND LEN(LTRIM(RTRIM(P.ItemCode))) = 13 UNION ALL SELECT - R.ItemCode, R.ColorCode, R.ItemDim1Code, R.ItemDim2Code, + UPPER(LTRIM(RTRIM(R.ItemCode))) AS ItemCode, R.ColorCode, R.ItemDim1Code, R.ItemDim2Code, 0, R.Qty1, 0, 0 FROM ReserveStates R - INNER JOIN #AttrBase AB ON AB.ProductCode = R.ItemCode + INNER JOIN #AttrBase AB ON AB.ProductCode = UPPER(LTRIM(RTRIM(R.ItemCode))) WHERE R.ItemTypeCode = 1 - AND LEN(R.ItemCode) = 13 + AND LEN(LTRIM(RTRIM(R.ItemCode))) = 13 UNION ALL SELECT - D.ItemCode, D.ColorCode, D.ItemDim1Code, D.ItemDim2Code, + UPPER(LTRIM(RTRIM(D.ItemCode))) AS ItemCode, D.ColorCode, D.ItemDim1Code, D.ItemDim2Code, 0, 0, D.Qty1, 0 FROM DispOrderStates D - INNER JOIN #AttrBase AB ON AB.ProductCode = D.ItemCode + INNER JOIN #AttrBase AB ON AB.ProductCode = UPPER(LTRIM(RTRIM(D.ItemCode))) WHERE D.ItemTypeCode = 1 - AND LEN(D.ItemCode) = 13 + AND LEN(LTRIM(RTRIM(D.ItemCode))) = 13 UNION ALL SELECT - T.ItemCode, T.ColorCode, T.ItemDim1Code, T.ItemDim2Code, + UPPER(LTRIM(RTRIM(T.ItemCode))) AS ItemCode, T.ColorCode, T.ItemDim1Code, T.ItemDim2Code, 0, 0, 0, SUM(T.In_Qty1 - T.Out_Qty1) FROM trStock T - INNER JOIN #AttrBase AB ON AB.ProductCode = T.ItemCode + INNER JOIN #AttrBase AB ON AB.ProductCode = UPPER(LTRIM(RTRIM(T.ItemCode))) WHERE T.ItemTypeCode = 1 - AND LEN(T.ItemCode) = 13 - GROUP BY T.ItemCode, T.ColorCode, T.ItemDim1Code, T.ItemDim2Code + AND LEN(LTRIM(RTRIM(T.ItemCode))) = 13 + GROUP BY UPPER(LTRIM(RTRIM(T.ItemCode))), T.ColorCode, T.ItemDim1Code, T.ItemDim2Code ) X GROUP BY X.ItemCode, X.ColorCode, X.ItemDim1Code, X.ItemDim2Code ), @@ -180,7 +180,7 @@ SELECT 'renk', X.FieldValue FROM ( SELECT DISTINCT FieldValue = CASE WHEN A.RenkAciklama <> '' THEN A.RenkAciklama ELSE A.Renk END FROM Avail A - INNER JOIN #AttrBase AB ON AB.ProductCode = A.ItemCode + INNER JOIN #AttrBase AB ON AB.ProductCode = UPPER(LTRIM(RTRIM(A.ItemCode))) WHERE (CASE WHEN A.RenkAciklama <> '' THEN A.RenkAciklama ELSE A.Renk END) <> '' AND (@Kategori IS NULL OR AB.Kategori = @Kategori) AND (@UrunAnaGrubu IS NULL OR AB.UrunAnaGrubu = @UrunAnaGrubu) @@ -196,7 +196,7 @@ SELECT 'renk2', X.FieldValue FROM ( SELECT DISTINCT FieldValue = A.Renk2 FROM Avail A - INNER JOIN #AttrBase AB ON AB.ProductCode = A.ItemCode + INNER JOIN #AttrBase AB ON AB.ProductCode = UPPER(LTRIM(RTRIM(A.ItemCode))) WHERE A.Renk2 <> '' AND (@Kategori IS NULL OR AB.Kategori = @Kategori) AND (@UrunAnaGrubu IS NULL OR AB.UrunAnaGrubu = @UrunAnaGrubu) @@ -245,7 +245,7 @@ SELECT 'beden', X.FieldValue FROM ( SELECT DISTINCT FieldValue = A.Beden FROM Avail A - INNER JOIN #AttrBase AB ON AB.ProductCode = A.ItemCode + INNER JOIN #AttrBase AB ON AB.ProductCode = UPPER(LTRIM(RTRIM(A.ItemCode))) WHERE A.Beden <> '' AND (@Kategori IS NULL OR AB.Kategori = @Kategori) AND (@UrunAnaGrubu IS NULL OR AB.UrunAnaGrubu = @UrunAnaGrubu) @@ -346,7 +346,7 @@ BEGIN ProductAtt46Desc ) SELECT - C.ProductCode, + UPPER(LTRIM(RTRIM(C.ProductCode))) AS ProductCode, C.ProductDescription, C.ProductAtt01Desc, C.ProductAtt02Desc, @@ -379,7 +379,7 @@ BEGIN ISNULL(PF.ProductAtt45Desc, '') AS ProductAtt45Desc, ISNULL(PF.ProductAtt46Desc, '') AS ProductAtt46Desc FROM dbo.ProductFilterTRCache C - LEFT JOIN ProductFilterWithDescription('TR') PF ON PF.ProductCode = C.ProductCode + LEFT JOIN ProductFilterWithDescription('TR') PF ON UPPER(LTRIM(RTRIM(PF.ProductCode))) = UPPER(LTRIM(RTRIM(C.ProductCode))) WHERE LEN(C.ProductCode) = 13 AND (@Kategori IS NULL OR C.ProductAtt44Desc = @Kategori) AND (@UrunAnaGrubu IS NULL OR C.ProductAtt01Desc = @UrunAnaGrubu) @@ -390,9 +390,9 @@ BEGIN END ELSE BEGIN - INSERT INTO #AttrFiltered - ( - ProductCode, + INSERT INTO #AttrFiltered + ( + ProductCode, ProductDescription, ProductAtt01Desc, ProductAtt02Desc, @@ -426,7 +426,7 @@ BEGIN ProductAtt46Desc ) SELECT - ProductCode, + UPPER(LTRIM(RTRIM(ProductCode))) AS ProductCode, ProductDescription, ProductAtt01Desc, ProductAtt02Desc, @@ -493,69 +493,69 @@ INV AS ( SELECT P.CompanyCode, P.OfficeCode, P.StoreTypeCode, P.StoreCode, P.WarehouseCode, - P.ItemTypeCode, P.ItemCode, P.ColorCode, P.ItemDim1Code, P.ItemDim2Code, P.ItemDim3Code, + P.ItemTypeCode, UPPER(LTRIM(RTRIM(P.ItemCode))) AS ItemCode, P.ColorCode, P.ItemDim1Code, P.ItemDim2Code, P.ItemDim3Code, P.Qty1 AS PickingQty1, 0 AS ReserveQty1, 0 AS DispOrderQty1, 0 AS InventoryQty1 FROM PickingStates P - INNER JOIN #AttrFiltered AF ON AF.ProductCode = P.ItemCode + INNER JOIN #AttrFiltered AF ON AF.ProductCode = UPPER(LTRIM(RTRIM(P.ItemCode))) WHERE P.ItemTypeCode = 1 - AND LEN(P.ItemCode) = 13 + AND LEN(LTRIM(RTRIM(P.ItemCode))) = 13 AND P.WarehouseCode IN ( '1-0-14','1-0-10','1-0-8','1-2-5','1-2-4','1-0-12','100','1-0-28', '1-0-24','1-2-6','1-1-14','1-0-2','1-0-52','1-1-2','1-0-21','1-1-3', - '1-0-33','101','1-014','1-0-49','1-0-36' + '1-0-33','101','1-014','1-0-49','1-0-36','1-0-4','1-0-29' ) UNION ALL SELECT R.CompanyCode, R.OfficeCode, R.StoreTypeCode, R.StoreCode, R.WarehouseCode, - R.ItemTypeCode, R.ItemCode, R.ColorCode, R.ItemDim1Code, R.ItemDim2Code, R.ItemDim3Code, + R.ItemTypeCode, UPPER(LTRIM(RTRIM(R.ItemCode))) AS ItemCode, R.ColorCode, R.ItemDim1Code, R.ItemDim2Code, R.ItemDim3Code, 0, R.Qty1, 0, 0 FROM ReserveStates R - INNER JOIN #AttrFiltered AF ON AF.ProductCode = R.ItemCode + INNER JOIN #AttrFiltered AF ON AF.ProductCode = UPPER(LTRIM(RTRIM(R.ItemCode))) WHERE R.ItemTypeCode = 1 - AND LEN(R.ItemCode) = 13 + AND LEN(LTRIM(RTRIM(R.ItemCode))) = 13 AND R.WarehouseCode IN ( '1-0-14','1-0-10','1-0-8','1-2-5','1-2-4','1-0-12','100','1-0-28', '1-0-24','1-2-6','1-1-14','1-0-2','1-0-52','1-1-2','1-0-21','1-1-3', - '1-0-33','101','1-014','1-0-49','1-0-36' + '1-0-33','101','1-014','1-0-49','1-0-36','1-0-4','1-0-29' ) UNION ALL SELECT D.CompanyCode, D.OfficeCode, D.StoreTypeCode, D.StoreCode, D.WarehouseCode, - D.ItemTypeCode, D.ItemCode, D.ColorCode, D.ItemDim1Code, D.ItemDim2Code, D.ItemDim3Code, + D.ItemTypeCode, UPPER(LTRIM(RTRIM(D.ItemCode))) AS ItemCode, D.ColorCode, D.ItemDim1Code, D.ItemDim2Code, D.ItemDim3Code, 0, 0, D.Qty1, 0 FROM DispOrderStates D - INNER JOIN #AttrFiltered AF ON AF.ProductCode = D.ItemCode + INNER JOIN #AttrFiltered AF ON AF.ProductCode = UPPER(LTRIM(RTRIM(D.ItemCode))) WHERE D.ItemTypeCode = 1 - AND LEN(D.ItemCode) = 13 + AND LEN(LTRIM(RTRIM(D.ItemCode))) = 13 AND D.WarehouseCode IN ( '1-0-14','1-0-10','1-0-8','1-2-5','1-2-4','1-0-12','100','1-0-28', '1-0-24','1-2-6','1-1-14','1-0-2','1-0-52','1-1-2','1-0-21','1-1-3', - '1-0-33','101','1-014','1-0-49','1-0-36' + '1-0-33','101','1-014','1-0-49','1-0-36','1-0-4','1-0-29' ) UNION ALL SELECT T.CompanyCode, T.OfficeCode, T.StoreTypeCode, T.StoreCode, T.WarehouseCode, - T.ItemTypeCode, T.ItemCode, T.ColorCode, T.ItemDim1Code, T.ItemDim2Code, T.ItemDim3Code, + T.ItemTypeCode, UPPER(LTRIM(RTRIM(T.ItemCode))) AS ItemCode, T.ColorCode, T.ItemDim1Code, T.ItemDim2Code, T.ItemDim3Code, 0, 0, 0, SUM(T.In_Qty1 - T.Out_Qty1) FROM trStock T - INNER JOIN #AttrFiltered AF ON AF.ProductCode = T.ItemCode + INNER JOIN #AttrFiltered AF ON AF.ProductCode = UPPER(LTRIM(RTRIM(T.ItemCode))) WHERE T.ItemTypeCode = 1 - AND LEN(T.ItemCode) = 13 + AND LEN(LTRIM(RTRIM(T.ItemCode))) = 13 AND T.WarehouseCode IN ( '1-0-14','1-0-10','1-0-8','1-2-5','1-2-4','1-0-12','100','1-0-28', '1-0-24','1-2-6','1-1-14','1-0-2','1-0-52','1-1-2','1-0-21','1-1-3', - '1-0-33','101','1-014','1-0-49','1-0-36' + '1-0-33','101','1-014','1-0-49','1-0-36','1-0-4','1-0-29' ) GROUP BY T.CompanyCode, T.OfficeCode, T.StoreTypeCode, T.StoreCode, T.WarehouseCode, - T.ItemTypeCode, T.ItemCode, T.ColorCode, T.ItemDim1Code, T.ItemDim2Code, T.ItemDim3Code + T.ItemTypeCode, UPPER(LTRIM(RTRIM(T.ItemCode))), T.ColorCode, T.ItemDim1Code, T.ItemDim2Code, T.ItemDim3Code ) X GROUP BY X.CompanyCode, X.OfficeCode, X.StoreTypeCode, X.StoreCode, X.WarehouseCode, @@ -580,21 +580,21 @@ Avail AS FROM INV I JOIN cdItem CI ON CI.ItemTypeCode = I.ItemTypeCode - AND CI.ItemCode = I.ItemCode + AND UPPER(LTRIM(RTRIM(CI.ItemCode))) = I.ItemCode LEFT JOIN cdUnitOfMeasure U ON U.UnitOfMeasureCode = CI.UnitOfMeasureCode1 LEFT JOIN cdColorDesc C ON C.ColorCode = I.ColorCode AND C.LangCode = 'TR' WHERE I.ItemTypeCode = 1 - AND LEN(I.ItemCode) = 13 + AND LEN(LTRIM(RTRIM(I.ItemCode))) = 13 AND (I.InventoryQty1 - I.PickingQty1 - I.ReserveQty1 - I.DispOrderQty1) > 0 AND CI.IsBlocked = 0 AND I.WarehouseCode IN ( '1-0-14','1-0-10','1-0-8','1-2-5','1-2-4','1-0-12','100','1-0-28', '1-0-24','1-2-6','1-1-14','1-0-2','1-0-52','1-1-2','1-0-21','1-1-3', - '1-0-33','101','1-014','1-0-49','1-0-36' + '1-0-33','101','1-014','1-0-49','1-0-36','1-0-4','1-0-29' ) ), Grouped AS @@ -604,7 +604,7 @@ Grouped AS A.ColorCode, A.ItemDim2Code FROM Avail A - INNER JOIN #AttrFiltered AF ON AF.ProductCode = A.ItemCode + INNER JOIN #AttrFiltered AF ON AF.ProductCode = UPPER(LTRIM(RTRIM(A.ItemCode))) WHERE (@RenkList IS NULL OR CHARINDEX(NCHAR(31) + ISNULL((CASE WHEN A.RenkAciklama <> '' THEN A.RenkAciklama ELSE A.ColorCode END),'') + NCHAR(31), NCHAR(31) + @RenkList + NCHAR(31)) > 0) AND (@Renk2List IS NULL OR CHARINDEX(NCHAR(31) + ISNULL(A.ItemDim2Code,'') + NCHAR(31), NCHAR(31) + @Renk2List + NCHAR(31)) > 0) AND ( @@ -669,7 +669,7 @@ INNER JOIN Grouped G AND G.ColorCode = A.ColorCode AND ISNULL(G.ItemDim2Code, '') = ISNULL(A.ItemDim2Code, '') INNER JOIN #AttrFiltered AF - ON AF.ProductCode = A.ItemCode + ON AF.ProductCode = UPPER(LTRIM(RTRIM(A.ItemCode))) LEFT JOIN cdWarehouseDesc W ON W.WarehouseCode = A.WarehouseCode AND W.LangCode = 'TR' @@ -680,8 +680,8 @@ OUTER APPLY ( SELECT TOP 1 Price FROM prItemBasePrice PB WHERE PB.ItemTypeCode = 1 - AND PB.ItemCode = A.ItemCode - AND LEN(PB.ItemCode) = 13 + AND UPPER(LTRIM(RTRIM(PB.ItemCode))) = UPPER(LTRIM(RTRIM(A.ItemCode))) + AND LEN(LTRIM(RTRIM(PB.ItemCode))) = 13 ORDER BY PB.PriceDate DESC ) P OPTION (RECOMPILE); diff --git a/ui/src/pages/ProductPerformanceProfitability.vue b/ui/src/pages/ProductPerformanceProfitability.vue index 11c452b..6bea295 100644 --- a/ui/src/pages/ProductPerformanceProfitability.vue +++ b/ui/src/pages/ProductPerformanceProfitability.vue @@ -1631,6 +1631,7 @@ const columns = [ { name: 'sales_usd_total', label: 'Genel USD', field: 'sales_usd_total', 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_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 Stok Devir', field: row => formatNumber(row.stock_turnover_90d, 2), align: 'right', sortable: true }, { name: 'stock_turnover_180d', label: '180G Stok Devir', field: row => formatNumber(row.stock_turnover_180d, 2), align: 'right', sortable: true }, @@ -2821,13 +2822,15 @@ function productPeriodMetricSource (row, suffix) { const salesIndex = Number(row?.[`sales_index_${suffix}`] || 0) const qty = Number(row?.[`sales_qty_${suffix}`] || 0) const stockQty = Number(row?.stock_qty || 0) + const turnoverKey = `stock_turnover_${suffix}` + const hasTurnover = Object.prototype.hasOwnProperty.call(row || {}, turnoverKey) return { sales_index_90d: salesIndex, gross_margin_cost_90d: Number(row?.[`gross_margin_cost_${suffix}`] ?? 0), gross_margin_90d: Number(row?.[`gross_margin_${suffix}`] ?? 0), invoice_count_90d: Number(row?.[`invoice_count_${suffix}`] || 0), sales_qty_90d: qty, - stock_turnover_90d: Number(row?.[`stock_turnover_${suffix}`] || 0) || (stockQty > 0 ? qty / stockQty : 0), + stock_turnover_90d: hasTurnover ? Number(row?.[turnoverKey] || 0) : (stockQty > 0 ? qty / stockQty : 0), stock_qty: stockQty } } @@ -3115,9 +3118,9 @@ function withProductMargins (row) { 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, - stock_turnover_90d: stockTurnover(row?.sales_qty_90d, row?.stock_qty), - stock_turnover_180d: stockTurnover(row?.sales_qty_180d, row?.stock_qty), - stock_turnover_365d: stockTurnover(row?.sales_qty_365d, row?.stock_qty), + 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), 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), @@ -3128,7 +3131,7 @@ function withProductMargins (row) { function withGeneralMargins (row) { return { ...row, - stock_turnover_total: stockTurnover(row?.sales_qty_total, row?.stock_qty), + 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) } @@ -3140,6 +3143,11 @@ function stockTurnover (salesQty, stockQty) { return Number(salesQty || 0) / stock } +function existingOrStockTurnover (value, salesQty, stockQty) { + const current = Number(value) + return value !== undefined && value !== null && Number.isFinite(current) ? current : stockTurnover(salesQty, stockQty) +} + function filterKey (tableKey, name) { return `${tableKey}:${name}` }