Fix product performance grouped JSON build
This commit is contained in:
@@ -3804,10 +3804,10 @@ SELECT
|
||||
avg_stock_180d,
|
||||
avg_stock_365d,
|
||||
avg_stock_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_90d > 0 THEN sales_qty_90d / NULLIF(avg_stock_90d,0) * 4.0 ELSE 0 END AS stock_turnover_90d,
|
||||
CASE WHEN avg_stock_180d > 0 THEN sales_qty_180d / NULLIF(avg_stock_180d,0) * 2.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,
|
||||
CASE WHEN avg_stock_total > 0 THEN sales_qty_total / NULLIF(avg_stock_total,0) * 360.0 / GREATEST(1, (current_date - DATE '2022-01-01') + 1) ELSE 0 END AS stock_turnover_total,
|
||||
has_cost,
|
||||
sales_index_90d,
|
||||
CASE WHEN NOT has_cost THEN 0 WHEN sales_usd_90d <= 0 THEN 1 ELSE
|
||||
@@ -3845,7 +3845,7 @@ SELECT
|
||||
CASE WHEN NOT has_cost THEN 0 WHEN sales_usd_90d <= 0 THEN 1 ELSE
|
||||
ROUND(
|
||||
LEAST(30, GREATEST(gross_margin_cost_90d,0) / 0.40 * 30)
|
||||
+ LEAST(20, (CASE WHEN avg_stock_90d > 0 THEN sales_qty_90d / NULLIF(avg_stock_90d,0) ELSE 0 END) / 1.5 * 20)
|
||||
+ LEAST(20, (CASE WHEN avg_stock_90d > 0 THEN sales_qty_90d / NULLIF(avg_stock_90d,0) * 4.0 ELSE 0 END) / 4.0 * 20)
|
||||
+ LEAST(20, sales_usd_90d / 10000 * 20)
|
||||
+ LEAST(15, market_count_90d / 8.0 * 15)
|
||||
+ LEAST(15, customer_count_90d / 25.0 * 15),
|
||||
@@ -4517,10 +4517,10 @@ SELECT (
|
||||
'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_90d', CASE WHEN avg_stock_90d > 0 THEN sales_qty_90d / NULLIF(avg_stock_90d,0) * 4.0 ELSE 0 END,
|
||||
'stock_turnover_180d', CASE WHEN avg_stock_180d > 0 THEN sales_qty_180d / NULLIF(avg_stock_180d,0) * 2.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
|
||||
'stock_turnover_total', CASE WHEN avg_stock_total > 0 THEN sales_qty_total / NULLIF(avg_stock_total,0) * 360.0 / GREATEST(1, ((SELECT kpi_date FROM LatestKPIDate) - DATE '2022-01-01') + 1) ELSE 0 END
|
||||
)
|
||||
) AS row_json
|
||||
FROM (
|
||||
@@ -4643,10 +4643,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 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_90d', CASE WHEN avg_stock_90d > 0 THEN sales_qty_90d / NULLIF(avg_stock_90d,0) * 4.0 ELSE 0 END,
|
||||
'stock_turnover_180d', CASE WHEN avg_stock_180d > 0 THEN sales_qty_180d / NULLIF(avg_stock_180d,0) * 2.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
|
||||
'stock_turnover_total', CASE WHEN avg_stock_total > 0 THEN sales_qty_total / NULLIF(avg_stock_total,0) * 360.0 / GREATEST(1, ((SELECT kpi_date FROM LatestKPIDate) - DATE '2022-01-01') + 1) ELSE 0 END
|
||||
) AS row_json
|
||||
FROM Source t
|
||||
%s
|
||||
@@ -6000,11 +6000,7 @@ func deriveProductPerformanceGroupMetrics(out map[string]any, groupField string)
|
||||
} else if _, ok := out["stock_days_"+suffix]; !ok {
|
||||
out["stock_days_"+suffix] = 0
|
||||
}
|
||||
if turnoverBase > 0 {
|
||||
out["stock_turnover_"+suffix] = qty / turnoverBase
|
||||
} else {
|
||||
out["stock_turnover_"+suffix] = 0
|
||||
}
|
||||
out["stock_turnover_"+suffix] = productPerformanceAnnualizedStockTurnover(qty, turnoverBase, days)
|
||||
if qty > 0 {
|
||||
out["avg_price_usd_"+suffix] = sales / qty
|
||||
}
|
||||
@@ -6111,7 +6107,7 @@ func productPerformancePeriodDays(row map[string]any, suffix string) float64 {
|
||||
case "180d":
|
||||
return 180
|
||||
case "365d":
|
||||
return 365
|
||||
return 360
|
||||
case "total":
|
||||
start := parseProductPerformanceDate(stringFromMap(row, "period_start"))
|
||||
if start.IsZero() {
|
||||
@@ -6130,6 +6126,19 @@ func productPerformancePeriodDays(row map[string]any, suffix string) float64 {
|
||||
}
|
||||
}
|
||||
|
||||
const productPerformanceStockTurnoverYearDays = 360.0
|
||||
|
||||
func productPerformanceAnnualizedStockTurnover(salesQty, avgStock, periodDays float64) float64 {
|
||||
if salesQty <= 0 || avgStock <= 0 {
|
||||
return 0
|
||||
}
|
||||
raw := salesQty / avgStock
|
||||
if periodDays <= 0 {
|
||||
return raw
|
||||
}
|
||||
return raw * productPerformanceStockTurnoverYearDays / periodDays
|
||||
}
|
||||
|
||||
func parseProductPerformanceDate(value string) time.Time {
|
||||
value = strings.TrimSpace(value)
|
||||
if value == "" {
|
||||
@@ -6206,8 +6215,12 @@ func productPerformanceSalesPeriodScore(row map[string]any, suffix string) float
|
||||
salesQty := floatFromMap(row, "sales_qty_"+suffix)
|
||||
stockTurnover := floatFromMap(row, "stock_turnover_"+suffix)
|
||||
if stockTurnover == 0 {
|
||||
if stockQty := floatFromMap(row, "stock_qty"); stockQty > 0 {
|
||||
stockTurnover = salesQty / stockQty
|
||||
avgStock := floatFromMap(row, "avg_stock_"+suffix)
|
||||
if avgStock <= 0 {
|
||||
avgStock = floatFromMap(row, "stock_qty")
|
||||
}
|
||||
if avgStock > 0 {
|
||||
stockTurnover = productPerformanceAnnualizedStockTurnover(salesQty, avgStock, productPerformancePeriodDays(row, suffix))
|
||||
}
|
||||
}
|
||||
return productPerformanceProductScore(
|
||||
@@ -6267,13 +6280,17 @@ func productPerformanceProductScore(suffix string, salesUSD, salesIndex, margin,
|
||||
}
|
||||
revenueScore := productPerformanceRevenueScore(suffix, salesUSD, salesIndex, productPerformanceProductRevenueTarget(suffix))
|
||||
score := 0.30*productPerformanceMarginComponentScore(margin) +
|
||||
0.20*productPerformanceRatioScore(stockTurnover, 1.50) +
|
||||
0.20*productPerformanceRatioScore(stockTurnover, productPerformanceStockTurnoverTarget(suffix)) +
|
||||
0.20*revenueScore +
|
||||
0.15*productPerformanceRatioScore(marketCount, 8) +
|
||||
0.15*productPerformanceRatioScore(customerCount, 25)
|
||||
return productPerformanceRoundScore(score)
|
||||
}
|
||||
|
||||
func productPerformanceStockTurnoverTarget(suffix string) float64 {
|
||||
return 4
|
||||
}
|
||||
|
||||
func productPerformanceCustomerScore(suffix string, salesUSD, margin, productGroupCount, salesQty float64) float64 {
|
||||
if salesUSD <= 0 {
|
||||
return 1
|
||||
|
||||
Reference in New Issue
Block a user