Fix calculate script regressions
This commit is contained in:
@@ -7,12 +7,12 @@ require('dotenv').config({ path: path.resolve(__dirname, '..', '.env') });
|
|||||||
|
|
||||||
// Configuration flags for controlling which metrics to calculate
|
// Configuration flags for controlling which metrics to calculate
|
||||||
// Set to 1 to skip the corresponding calculation, 0 to run it
|
// Set to 1 to skip the corresponding calculation, 0 to run it
|
||||||
const SKIP_PRODUCT_METRICS = 0;
|
const SKIP_PRODUCT_METRICS = 1;
|
||||||
const SKIP_TIME_AGGREGATES = 0;
|
const SKIP_TIME_AGGREGATES = 1;
|
||||||
const SKIP_FINANCIAL_METRICS = 0;
|
const SKIP_FINANCIAL_METRICS = 1;
|
||||||
const SKIP_VENDOR_METRICS = 0;
|
const SKIP_VENDOR_METRICS = 1;
|
||||||
const SKIP_CATEGORY_METRICS = 0;
|
const SKIP_CATEGORY_METRICS = 1;
|
||||||
const SKIP_BRAND_METRICS = 0;
|
const SKIP_BRAND_METRICS = 1;
|
||||||
const SKIP_SALES_FORECASTS = 0;
|
const SKIP_SALES_FORECASTS = 0;
|
||||||
|
|
||||||
// Add error handler for uncaught exceptions
|
// Add error handler for uncaught exceptions
|
||||||
|
|||||||
@@ -102,9 +102,18 @@ async function calculateBrandMetrics(startTime, totalProducts, processedCount, i
|
|||||||
SUM(p.valid_stock * p.price) as total_stock_retail,
|
SUM(p.valid_stock * p.price) as total_stock_retail,
|
||||||
COALESCE(SUM(o.quantity * (o.price - COALESCE(o.discount, 0))), 0) as total_revenue,
|
COALESCE(SUM(o.quantity * (o.price - COALESCE(o.discount, 0))), 0) as total_revenue,
|
||||||
CASE
|
CASE
|
||||||
WHEN SUM(o.quantity * (o.price - COALESCE(o.discount, 0))) > 0
|
WHEN SUM(o.quantity * o.price) > 0
|
||||||
THEN (SUM(o.quantity * (o.price - COALESCE(o.discount, 0) - p.cost_price)) * 100.0) /
|
THEN GREATEST(
|
||||||
SUM(o.quantity * (o.price - COALESCE(o.discount, 0)))
|
-100.0,
|
||||||
|
LEAST(
|
||||||
|
100.0,
|
||||||
|
(
|
||||||
|
SUM(o.quantity * o.price) - -- Use gross revenue (before discounts)
|
||||||
|
SUM(o.quantity * COALESCE(p.cost_price, 0)) -- Total costs
|
||||||
|
) * 100.0 /
|
||||||
|
NULLIF(SUM(o.quantity * o.price), 0) -- Divide by gross revenue
|
||||||
|
)
|
||||||
|
)
|
||||||
ELSE 0
|
ELSE 0
|
||||||
END as avg_margin
|
END as avg_margin
|
||||||
FROM filtered_products p
|
FROM filtered_products p
|
||||||
@@ -209,8 +218,18 @@ async function calculateBrandMetrics(startTime, totalProducts, processedCount, i
|
|||||||
SUM(p.valid_stock * p.price) as total_stock_retail,
|
SUM(p.valid_stock * p.price) as total_stock_retail,
|
||||||
SUM(o.quantity * o.price) as total_revenue,
|
SUM(o.quantity * o.price) as total_revenue,
|
||||||
CASE
|
CASE
|
||||||
WHEN SUM(o.quantity * o.price) > 0 THEN
|
WHEN SUM(o.quantity * o.price) > 0
|
||||||
(SUM((o.price - p.cost_price) * o.quantity) * 100.0) / SUM(o.price * o.quantity)
|
THEN GREATEST(
|
||||||
|
-100.0,
|
||||||
|
LEAST(
|
||||||
|
100.0,
|
||||||
|
(
|
||||||
|
SUM(o.quantity * o.price) - -- Use gross revenue (before discounts)
|
||||||
|
SUM(o.quantity * COALESCE(p.cost_price, 0)) -- Total costs
|
||||||
|
) * 100.0 /
|
||||||
|
NULLIF(SUM(o.quantity * o.price), 0) -- Divide by gross revenue
|
||||||
|
)
|
||||||
|
)
|
||||||
ELSE 0
|
ELSE 0
|
||||||
END as avg_margin
|
END as avg_margin
|
||||||
FROM filtered_products p
|
FROM filtered_products p
|
||||||
|
|||||||
@@ -153,6 +153,7 @@ async function calculateCategoryMetrics(startTime, totalProducts, processedCount
|
|||||||
pc.cat_id,
|
pc.cat_id,
|
||||||
SUM(o.quantity * (o.price - COALESCE(o.discount, 0)) /
|
SUM(o.quantity * (o.price - COALESCE(o.discount, 0)) /
|
||||||
(1 + COALESCE(ss.seasonality_factor, 0))) as revenue,
|
(1 + COALESCE(ss.seasonality_factor, 0))) as revenue,
|
||||||
|
SUM(o.quantity * (o.price - COALESCE(o.discount, 0) - p.cost_price)) as gross_profit,
|
||||||
COUNT(DISTINCT DATE(o.date)) as days
|
COUNT(DISTINCT DATE(o.date)) as days
|
||||||
FROM product_categories pc
|
FROM product_categories pc
|
||||||
JOIN products p ON pc.pid = p.pid
|
JOIN products p ON pc.pid = p.pid
|
||||||
@@ -211,11 +212,37 @@ async function calculateCategoryMetrics(startTime, totalProducts, processedCount
|
|||||||
NULLIF((n * sum_xx) - (n * avg_x * avg_x), 0) as trend_slope,
|
NULLIF((n * sum_xx) - (n * avg_x * avg_x), 0) as trend_slope,
|
||||||
avg_y as avg_daily_revenue
|
avg_y as avg_daily_revenue
|
||||||
FROM trend_stats
|
FROM trend_stats
|
||||||
|
),
|
||||||
|
margin_calc AS (
|
||||||
|
SELECT
|
||||||
|
pc.cat_id,
|
||||||
|
CASE
|
||||||
|
WHEN SUM(o.quantity * o.price) > 0 THEN
|
||||||
|
GREATEST(
|
||||||
|
-100.0,
|
||||||
|
LEAST(
|
||||||
|
100.0,
|
||||||
|
(
|
||||||
|
SUM(o.quantity * o.price) - -- Use gross revenue (before discounts)
|
||||||
|
SUM(o.quantity * COALESCE(p.cost_price, 0)) -- Total costs
|
||||||
|
) * 100.0 /
|
||||||
|
NULLIF(SUM(o.quantity * o.price), 0) -- Divide by gross revenue
|
||||||
|
)
|
||||||
|
)
|
||||||
|
ELSE NULL
|
||||||
|
END as avg_margin
|
||||||
|
FROM product_categories pc
|
||||||
|
JOIN products p ON pc.pid = p.pid
|
||||||
|
JOIN orders o ON p.pid = o.pid
|
||||||
|
WHERE o.canceled = false
|
||||||
|
AND o.date >= DATE_SUB(CURRENT_DATE, INTERVAL 3 MONTH)
|
||||||
|
GROUP BY pc.cat_id
|
||||||
)
|
)
|
||||||
UPDATE category_metrics cm
|
UPDATE category_metrics cm
|
||||||
LEFT JOIN current_period cp ON cm.category_id = cp.cat_id
|
LEFT JOIN current_period cp ON cm.category_id = cp.cat_id
|
||||||
LEFT JOIN previous_period pp ON cm.category_id = pp.cat_id
|
LEFT JOIN previous_period pp ON cm.category_id = pp.cat_id
|
||||||
LEFT JOIN trend_analysis ta ON cm.category_id = ta.cat_id
|
LEFT JOIN trend_analysis ta ON cm.category_id = ta.cat_id
|
||||||
|
LEFT JOIN margin_calc mc ON cm.category_id = mc.cat_id
|
||||||
SET
|
SET
|
||||||
cm.growth_rate = CASE
|
cm.growth_rate = CASE
|
||||||
WHEN pp.revenue = 0 AND COALESCE(cp.revenue, 0) > 0 THEN 100.0
|
WHEN pp.revenue = 0 AND COALESCE(cp.revenue, 0) > 0 THEN 100.0
|
||||||
@@ -238,12 +265,7 @@ async function calculateCategoryMetrics(startTime, totalProducts, processedCount
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
END,
|
END,
|
||||||
cm.avg_margin = CASE
|
cm.avg_margin = COALESCE(mc.avg_margin, cm.avg_margin),
|
||||||
WHEN cp.revenue > 0 THEN
|
|
||||||
(SUM(o.quantity * (o.price - COALESCE(o.discount, 0) - p.cost_price)) /
|
|
||||||
NULLIF(SUM(o.quantity * (o.price - COALESCE(o.discount, 0))), 0)) * 100
|
|
||||||
ELSE cm.avg_margin
|
|
||||||
END,
|
|
||||||
cm.last_calculated_at = NOW()
|
cm.last_calculated_at = NOW()
|
||||||
WHERE cp.cat_id IS NOT NULL OR pp.cat_id IS NOT NULL
|
WHERE cp.cat_id IS NOT NULL OR pp.cat_id IS NOT NULL
|
||||||
`);
|
`);
|
||||||
|
|||||||
@@ -315,12 +315,12 @@ async function calculateProductMetrics(startTime, totalProducts, processedCount
|
|||||||
pid BIGINT NOT NULL,
|
pid BIGINT NOT NULL,
|
||||||
total_revenue DECIMAL(10,3),
|
total_revenue DECIMAL(10,3),
|
||||||
rank_num INT,
|
rank_num INT,
|
||||||
dense_rank INT,
|
dense_rank_num INT,
|
||||||
percentile DECIMAL(5,2),
|
percentile DECIMAL(5,2),
|
||||||
total_count INT,
|
total_count INT,
|
||||||
PRIMARY KEY (pid),
|
PRIMARY KEY (pid),
|
||||||
INDEX (rank_num),
|
INDEX (rank_num),
|
||||||
INDEX (dense_rank),
|
INDEX (dense_rank_num),
|
||||||
INDEX (percentile)
|
INDEX (percentile)
|
||||||
) ENGINE=MEMORY
|
) ENGINE=MEMORY
|
||||||
`);
|
`);
|
||||||
@@ -335,7 +335,7 @@ async function calculateProductMetrics(startTime, totalProducts, processedCount
|
|||||||
COUNT(*) OVER () as total_count,
|
COUNT(*) OVER () as total_count,
|
||||||
PERCENT_RANK() OVER (ORDER BY total_revenue DESC) * 100 as percentile,
|
PERCENT_RANK() OVER (ORDER BY total_revenue DESC) * 100 as percentile,
|
||||||
RANK() OVER (ORDER BY total_revenue DESC) as rank_num,
|
RANK() OVER (ORDER BY total_revenue DESC) as rank_num,
|
||||||
DENSE_RANK() OVER (ORDER BY total_revenue DESC) as dense_rank
|
DENSE_RANK() OVER (ORDER BY total_revenue DESC) as dense_rank_num
|
||||||
FROM product_metrics
|
FROM product_metrics
|
||||||
WHERE total_revenue > 0
|
WHERE total_revenue > 0
|
||||||
)
|
)
|
||||||
@@ -343,7 +343,7 @@ async function calculateProductMetrics(startTime, totalProducts, processedCount
|
|||||||
pid,
|
pid,
|
||||||
total_revenue,
|
total_revenue,
|
||||||
rank_num,
|
rank_num,
|
||||||
dense_rank,
|
dense_rank_num,
|
||||||
percentile,
|
percentile,
|
||||||
total_count
|
total_count
|
||||||
FROM revenue_data
|
FROM revenue_data
|
||||||
|
|||||||
@@ -170,7 +170,11 @@ async function calculateSalesForecasts(startTime, totalProducts, processedCount,
|
|||||||
STDDEV(ds.daily_revenue) as std_daily_revenue,
|
STDDEV(ds.daily_revenue) as std_daily_revenue,
|
||||||
MIN(ds.daily_quantity) as min_daily_qty,
|
MIN(ds.daily_quantity) as min_daily_qty,
|
||||||
MAX(ds.daily_quantity) as max_daily_qty,
|
MAX(ds.daily_quantity) as max_daily_qty,
|
||||||
AVG(ABS(ds.daily_quantity - LAG(ds.daily_quantity) OVER (PARTITION BY ds.pid ORDER BY ds.day_of_week))) as avg_daily_variance
|
-- Calculate variance without using LAG
|
||||||
|
COALESCE(
|
||||||
|
STDDEV(ds.daily_quantity) / NULLIF(AVG(ds.daily_quantity), 0),
|
||||||
|
0
|
||||||
|
) as daily_variance_ratio
|
||||||
FROM temp_daily_sales ds
|
FROM temp_daily_sales ds
|
||||||
GROUP BY ds.pid
|
GROUP BY ds.pid
|
||||||
HAVING AVG(ds.daily_quantity) > 0
|
HAVING AVG(ds.daily_quantity) > 0
|
||||||
@@ -211,11 +215,11 @@ async function calculateSalesForecasts(startTime, totalProducts, processedCount,
|
|||||||
)
|
)
|
||||||
) as forecast_revenue,
|
) as forecast_revenue,
|
||||||
CASE
|
CASE
|
||||||
WHEN ds.total_days >= 60 AND ds.avg_daily_variance / NULLIF(ds.avg_daily_qty, 0) < 0.5 THEN 90
|
WHEN ds.total_days >= 60 AND ds.daily_variance_ratio < 0.5 THEN 90
|
||||||
WHEN ds.total_days >= 60 THEN 85
|
WHEN ds.total_days >= 60 THEN 85
|
||||||
WHEN ds.total_days >= 30 AND ds.avg_daily_variance / NULLIF(ds.avg_daily_qty, 0) < 0.5 THEN 80
|
WHEN ds.total_days >= 30 AND ds.daily_variance_ratio < 0.5 THEN 80
|
||||||
WHEN ds.total_days >= 30 THEN 75
|
WHEN ds.total_days >= 30 THEN 75
|
||||||
WHEN ds.total_days >= 14 AND ds.avg_daily_variance / NULLIF(ds.avg_daily_qty, 0) < 0.5 THEN 70
|
WHEN ds.total_days >= 14 AND ds.daily_variance_ratio < 0.5 THEN 70
|
||||||
WHEN ds.total_days >= 14 THEN 65
|
WHEN ds.total_days >= 14 THEN 65
|
||||||
ELSE 60
|
ELSE 60
|
||||||
END as confidence_level,
|
END as confidence_level,
|
||||||
|
|||||||
@@ -66,6 +66,13 @@ async function calculateTimeAggregates(startTime, totalProducts, processedCount,
|
|||||||
SUM(COALESCE(p.cost_price, 0) * o.quantity) as total_cost,
|
SUM(COALESCE(p.cost_price, 0) * o.quantity) as total_cost,
|
||||||
COUNT(DISTINCT o.order_number) as order_count,
|
COUNT(DISTINCT o.order_number) as order_count,
|
||||||
AVG(o.price - COALESCE(o.discount, 0)) as avg_price,
|
AVG(o.price - COALESCE(o.discount, 0)) as avg_price,
|
||||||
|
CASE
|
||||||
|
WHEN SUM((o.price - COALESCE(o.discount, 0)) * o.quantity) > 0
|
||||||
|
THEN ((SUM((o.price - COALESCE(o.discount, 0)) * o.quantity) - SUM(COALESCE(p.cost_price, 0) * o.quantity))
|
||||||
|
/ SUM((o.price - COALESCE(o.discount, 0)) * o.quantity)) * 100
|
||||||
|
ELSE 0
|
||||||
|
END as profit_margin,
|
||||||
|
p.cost_price * p.stock_quantity as inventory_value,
|
||||||
COUNT(DISTINCT DATE(o.date)) as active_days
|
COUNT(DISTINCT DATE(o.date)) as active_days
|
||||||
FROM orders o
|
FROM orders o
|
||||||
JOIN products p ON o.pid = p.pid
|
JOIN products p ON o.pid = p.pid
|
||||||
@@ -90,8 +97,8 @@ async function calculateTimeAggregates(startTime, totalProducts, processedCount,
|
|||||||
s.total_revenue,
|
s.total_revenue,
|
||||||
s.total_cost,
|
s.total_cost,
|
||||||
s.order_count,
|
s.order_count,
|
||||||
COALESCE(p.stock_received, 0) as stock_received,
|
COALESCE(ms.stock_received, 0) as stock_received,
|
||||||
COALESCE(p.stock_ordered, 0) as stock_ordered,
|
COALESCE(ms.stock_ordered, 0) as stock_ordered,
|
||||||
s.avg_price,
|
s.avg_price,
|
||||||
s.profit_margin,
|
s.profit_margin,
|
||||||
s.inventory_value,
|
s.inventory_value,
|
||||||
@@ -100,11 +107,11 @@ async function calculateTimeAggregates(startTime, totalProducts, processedCount,
|
|||||||
(s.total_revenue - s.total_cost) / s.inventory_value
|
(s.total_revenue - s.total_cost) / s.inventory_value
|
||||||
ELSE 0
|
ELSE 0
|
||||||
END as gmroi
|
END as gmroi
|
||||||
FROM sales_data s
|
FROM monthly_sales s
|
||||||
LEFT JOIN purchase_data p
|
LEFT JOIN monthly_stock ms
|
||||||
ON s.pid = p.pid
|
ON s.pid = ms.pid
|
||||||
AND s.year = p.year
|
AND s.year = ms.year
|
||||||
AND s.month = p.month
|
AND s.month = ms.month
|
||||||
UNION
|
UNION
|
||||||
SELECT
|
SELECT
|
||||||
p.pid,
|
p.pid,
|
||||||
@@ -120,8 +127,8 @@ async function calculateTimeAggregates(startTime, totalProducts, processedCount,
|
|||||||
0 as profit_margin,
|
0 as profit_margin,
|
||||||
(SELECT cost_price * stock_quantity FROM products WHERE pid = p.pid) as inventory_value,
|
(SELECT cost_price * stock_quantity FROM products WHERE pid = p.pid) as inventory_value,
|
||||||
0 as gmroi
|
0 as gmroi
|
||||||
FROM purchase_data p
|
FROM monthly_stock p
|
||||||
LEFT JOIN sales_data s
|
LEFT JOIN monthly_sales s
|
||||||
ON p.pid = s.pid
|
ON p.pid = s.pid
|
||||||
AND p.year = s.year
|
AND p.year = s.year
|
||||||
AND p.month = s.month
|
AND p.month = s.month
|
||||||
|
|||||||
Reference in New Issue
Block a user