From 2fc9699ff1d7ebe63cd98c3a5e704f596dc1af31 Mon Sep 17 00:00:00 2001 From: M_Kececi Date: Thu, 2 Jul 2026 20:02:07 +0300 Subject: [PATCH] ui: update ProductPerformanceProfitability page to enhance table views and tabs management --- svc/queries/product_performance.go | 112 ++++-- svc/queries/product_performance_sql.go | 9 +- .../pages/ProductPerformanceProfitability.vue | 341 ++++++++++++++---- 3 files changed, 359 insertions(+), 103 deletions(-) diff --git a/svc/queries/product_performance.go b/svc/queries/product_performance.go index a929a63..04acaaa 100644 --- a/svc/queries/product_performance.go +++ b/svc/queries/product_performance.go @@ -670,8 +670,10 @@ func ListProductPerformance(ctx context.Context, pg *sql.DB, f ProductPerformanc return nil, 0, err } limit := f.Limit - if limit <= 0 || limit > 500 { + if limit <= 0 { limit = 100 + } else if limit > 50000 { + limit = 50000 } page := f.Page if page <= 0 { @@ -741,6 +743,25 @@ KPI AS ( FROM mk_product_performance_kpi_daily WHERE kpi_date = (SELECT kpi_date FROM Latest) ), +VariantStock AS ( + SELECT + product_code, + color_code, + yaka_kodu, + MAX(stock_qty) AS stock_qty, + MAX(cost_price_usd) AS cost_price_usd, + BOOL_OR(performance_bucket IN ('STOK_RISKI','TAKIP')) AS has_risk_bucket, + SUM(COALESCE(sales_qty_90d,0)) AS sales_qty_90d + FROM KPI + GROUP BY product_code, color_code, yaka_kodu +), +StockTotals AS ( + SELECT + COALESCE(SUM(stock_qty),0) AS total_stock, + COALESCE(SUM(stock_qty * cost_price_usd),0) AS stock_cost_usd, + COALESCE(SUM(CASE WHEN has_risk_bucket AND COALESCE(sales_qty_90d,0)=0 THEN stock_qty * cost_price_usd ELSE 0 END),0) AS risk_cost_usd + FROM VariantStock +), Sales90 AS ( SELECT COUNT(DISTINCT market_key) AS market_count_90d, @@ -754,9 +775,9 @@ SELECT COUNT(*) FILTER (WHERE performance_bucket='YILDIZ_URUN'), COUNT(*) FILTER (WHERE performance_bucket='STOK_RISKI'), COUNT(*) FILTER (WHERE performance_bucket='STOKSUZ_TALEP'), - COALESCE(SUM(stock_qty),0), - COALESCE(SUM(stock_qty * cost_price_usd),0), - COALESCE(SUM(CASE WHEN performance_bucket IN ('STOK_RISKI','TAKIP') AND COALESCE(sales_qty_90d,0)=0 THEN stock_qty * cost_price_usd ELSE 0 END),0), + COALESCE(MAX(StockTotals.total_stock),0), + COALESCE(MAX(StockTotals.stock_cost_usd),0), + COALESCE(MAX(StockTotals.risk_cost_usd),0), COALESCE(SUM(sales_qty_90d),0), COALESCE(SUM(sales_usd_90d),0), COALESCE(SUM(gross_profit_usd_90d),0), @@ -765,6 +786,7 @@ SELECT COALESCE(to_char(MAX(updated_at),'YYYY-MM-DD HH24:MI:SS'),'') FROM KPI CROSS JOIN Sales90 +CROSS JOIN StockTotals `).Scan(&s.KpiDate, &s.TotalRows, &s.StarCount, &s.StockRisk, &s.NoStockDemand, &s.TotalStock, &s.StockCostUSD, &s.RiskCostUSD, &s.SalesQty90, &s.SalesUSD90, &s.GrossProfit90, &s.MarketCount90, &s.CustomerCount90, &s.UpdatedAt) return s, err } @@ -2007,10 +2029,22 @@ func ListProductPerformanceStockSizes(ctx context.Context, productCode, colorCod return nil, fmt.Errorf("mssql db nil") } rows, err := db.MssqlDB.QueryContext(ctx, ` +;WITH ActiveWarehouses AS ( + SELECT WarehouseCode + FROM (VALUES + ('1-0-12'),('1.01.2014'),('1.02.2005'),('1.02.2004'),('1-0-43'), + ('4.02.2001'),('1-0-55'),('1.01.2003'),('1-0-21'),('1-0-2'), + ('1.01.2004'),('1-0-49'),('1-0-37'),('1-0-29'),('1-0-28'), + ('1-0-10'),('100'),('1.02.2006'),('1-0-42'),('1.01.2002'), + ('1-0-52') + ) W(WarehouseCode) +) SELECT SizeCode = LTRIM(RTRIM(ISNULL(S.ItemDim1Code, ''))), StockQty = SUM(S.InventoryQty1) FROM StockWithCost S WITH(NOLOCK) +INNER JOIN ActiveWarehouses W + ON W.WarehouseCode = LTRIM(RTRIM(S.WarehouseCode)) WHERE S.ItemTypeCode = 1 AND LTRIM(RTRIM(S.ItemCode)) = @p1 AND LTRIM(RTRIM(ISNULL(S.ColorCode, ''))) = @p2 @@ -2068,39 +2102,43 @@ func productPerformanceWhere(f ProductPerformanceFilters) (string, []any) { func productPerformanceOrderBy(sortBy string, desc bool) string { allowed := map[string]string{ - "product_code": "product_code", - "color_code": "color_code", - "yaka_kodu": "yaka_kodu", - "item_description": "item_description", - "kategori": "kategori", - "urun_ilk_grubu": "urun_ilk_grubu", - "askili_yan": "askili_yan", - "urun_ana_grubu": "urun_ana_grubu", - "urun_alt_grubu": "urun_alt_grubu", - "market_key": "market_key", - "stock_qty": "stock_qty", - "sales_qty_90d": "sales_qty_90d", - "sales_qty_180d": "sales_qty_180d", - "sales_usd_90d": "sales_usd_90d", - "sales_usd_180d": "sales_usd_180d", - "stock_days_90d": "stock_days_90d", - "stock_days_180d": "stock_days_180d", - "avg_price_usd_90d": "avg_price_usd_90d", - "avg_price_usd_180d": "avg_price_usd_180d", - "base_price_usd": "base_price_usd", - "cost_price_usd": "cost_price_usd", - "unit_profit_base_90d": "unit_profit_base_90d", - "unit_profit_cost_90d": "unit_profit_cost_90d", - "unit_profit_base_180d": "unit_profit_base_180d", - "unit_profit_cost_180d": "unit_profit_cost_180d", - "gross_margin_90d": "gross_margin_90d", - "gross_margin_180d": "gross_margin_180d", - "market_count_90d": "market_count_90d", - "customer_count_90d": "customer_count_90d", - "sales_index_90d": "sales_index_90d", - "performance_score": "performance_score", - "performance_bucket": "performance_bucket", - "last_sale_date": "last_sale_date", + "product_code": "product_code", + "color_code": "color_code", + "yaka_kodu": "yaka_kodu", + "item_description": "item_description", + "kategori": "kategori", + "urun_ilk_grubu": "urun_ilk_grubu", + "askili_yan": "askili_yan", + "urun_ana_grubu": "urun_ana_grubu", + "urun_alt_grubu": "urun_alt_grubu", + "market_key": "market_key", + "stock_qty": "stock_qty", + "sales_qty_90d": "sales_qty_90d", + "sales_qty_180d": "sales_qty_180d", + "sales_usd_90d": "sales_usd_90d", + "sales_usd_180d": "sales_usd_180d", + "stock_days_90d": "stock_days_90d", + "stock_days_180d": "stock_days_180d", + "avg_price_usd_90d": "avg_price_usd_90d", + "avg_price_usd_180d": "avg_price_usd_180d", + "base_price_usd": "base_price_usd", + "cost_price_usd": "cost_price_usd", + "unit_profit_base_90d": "unit_profit_base_90d", + "unit_profit_cost_90d": "unit_profit_cost_90d", + "unit_profit_base_180d": "unit_profit_base_180d", + "unit_profit_cost_180d": "unit_profit_cost_180d", + "gross_margin_base_90d": "(CASE WHEN COALESCE(sales_usd_90d,0) <= 0 THEN 0 ELSE (sales_usd_90d - (sales_qty_90d * COALESCE(base_price_usd,0))) / NULLIF(sales_usd_90d,0) END)", + "gross_margin_cost_90d": "gross_margin_90d", + "gross_margin_base_180d": "(CASE WHEN COALESCE(sales_usd_180d,0) <= 0 THEN 0 ELSE (sales_usd_180d - (sales_qty_180d * COALESCE(base_price_usd,0))) / NULLIF(sales_usd_180d,0) END)", + "gross_margin_cost_180d": "gross_margin_180d", + "gross_margin_90d": "gross_margin_90d", + "gross_margin_180d": "gross_margin_180d", + "market_count_90d": "market_count_90d", + "customer_count_90d": "customer_count_90d", + "sales_index_90d": "sales_index_90d", + "performance_score": "performance_score", + "performance_bucket": "performance_bucket", + "last_sale_date": "last_sale_date", } col := allowed[strings.TrimSpace(sortBy)] if col == "" { diff --git a/svc/queries/product_performance_sql.go b/svc/queries/product_performance_sql.go index e5466d4..c161eeb 100644 --- a/svc/queries/product_performance_sql.go +++ b/svc/queries/product_performance_sql.go @@ -216,10 +216,11 @@ func productPerformanceStockSQL() string { ;WITH ActiveWarehouses AS ( SELECT WarehouseCode FROM (VALUES - ('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-12'),('1.01.2014'),('1.02.2005'),('1.02.2004'),('1-0-43'), + ('4.02.2001'),('1-0-55'),('1.01.2003'),('1-0-21'),('1-0-2'), + ('1.01.2004'),('1-0-49'),('1-0-37'),('1-0-29'),('1-0-28'), + ('1-0-10'),('100'),('1.02.2006'),('1-0-42'),('1.01.2002'), + ('1-0-52') ) W(WarehouseCode) ), Raw AS ( diff --git a/ui/src/pages/ProductPerformanceProfitability.vue b/ui/src/pages/ProductPerformanceProfitability.vue index 075da2d..2b08a72 100644 --- a/ui/src/pages/ProductPerformanceProfitability.vue +++ b/ui/src/pages/ProductPerformanceProfitability.vue @@ -449,32 +449,28 @@