Fix and restyle forecast component
This commit is contained in:
@@ -3,7 +3,7 @@ const path = require('path');
|
||||
require('dotenv').config({ path: path.resolve(__dirname, '..', '.env') });
|
||||
const fs = require('fs');
|
||||
|
||||
// Configuration flags
|
||||
// Set to 1 to skip product metrics and only calculate the remaining metrics
|
||||
const SKIP_PRODUCT_METRICS = 0;
|
||||
|
||||
// Helper function to format elapsed time
|
||||
@@ -1149,8 +1149,12 @@ async function calculateSalesForecasts(connection, startTime, totalProducts) {
|
||||
DATE_ADD(CURRENT_DATE, INTERVAL n DAY) as forecast_date
|
||||
FROM (
|
||||
SELECT 0 as n UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION
|
||||
SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 14 UNION SELECT 30 UNION
|
||||
SELECT 60 UNION SELECT 90
|
||||
SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9 UNION
|
||||
SELECT 10 UNION SELECT 11 UNION SELECT 12 UNION SELECT 13 UNION SELECT 14 UNION
|
||||
SELECT 15 UNION SELECT 16 UNION SELECT 17 UNION SELECT 18 UNION SELECT 19 UNION
|
||||
SELECT 20 UNION SELECT 21 UNION SELECT 22 UNION SELECT 23 UNION SELECT 24 UNION
|
||||
SELECT 25 UNION SELECT 26 UNION SELECT 27 UNION SELECT 28 UNION SELECT 29 UNION
|
||||
SELECT 30
|
||||
) numbers
|
||||
),
|
||||
product_stats AS (
|
||||
@@ -1160,7 +1164,15 @@ async function calculateSalesForecasts(connection, startTime, totalProducts) {
|
||||
STDDEV_SAMP(ds.daily_quantity) as std_daily_quantity,
|
||||
AVG(ds.daily_revenue) as avg_daily_revenue,
|
||||
STDDEV_SAMP(ds.daily_revenue) as std_daily_revenue,
|
||||
COUNT(*) as data_points
|
||||
COUNT(*) as data_points,
|
||||
-- Calculate day-of-week averages
|
||||
AVG(CASE WHEN DAYOFWEEK(ds.sale_date) = 1 THEN ds.daily_revenue END) as sunday_avg,
|
||||
AVG(CASE WHEN DAYOFWEEK(ds.sale_date) = 2 THEN ds.daily_revenue END) as monday_avg,
|
||||
AVG(CASE WHEN DAYOFWEEK(ds.sale_date) = 3 THEN ds.daily_revenue END) as tuesday_avg,
|
||||
AVG(CASE WHEN DAYOFWEEK(ds.sale_date) = 4 THEN ds.daily_revenue END) as wednesday_avg,
|
||||
AVG(CASE WHEN DAYOFWEEK(ds.sale_date) = 5 THEN ds.daily_revenue END) as thursday_avg,
|
||||
AVG(CASE WHEN DAYOFWEEK(ds.sale_date) = 6 THEN ds.daily_revenue END) as friday_avg,
|
||||
AVG(CASE WHEN DAYOFWEEK(ds.sale_date) = 7 THEN ds.daily_revenue END) as saturday_avg
|
||||
FROM daily_sales ds
|
||||
GROUP BY ds.product_id
|
||||
)
|
||||
@@ -1178,14 +1190,24 @@ async function calculateSalesForecasts(connection, startTime, totalProducts) {
|
||||
))
|
||||
) as forecast_units,
|
||||
GREATEST(0,
|
||||
ps.avg_daily_revenue *
|
||||
CASE DAYOFWEEK(fd.forecast_date)
|
||||
WHEN 1 THEN COALESCE(ps.sunday_avg, ps.avg_daily_revenue)
|
||||
WHEN 2 THEN COALESCE(ps.monday_avg, ps.avg_daily_revenue)
|
||||
WHEN 3 THEN COALESCE(ps.tuesday_avg, ps.avg_daily_revenue)
|
||||
WHEN 4 THEN COALESCE(ps.wednesday_avg, ps.avg_daily_revenue)
|
||||
WHEN 5 THEN COALESCE(ps.thursday_avg, ps.avg_daily_revenue)
|
||||
WHEN 6 THEN COALESCE(ps.friday_avg, ps.avg_daily_revenue)
|
||||
WHEN 7 THEN COALESCE(ps.saturday_avg, ps.avg_daily_revenue)
|
||||
END *
|
||||
(1 + COALESCE(
|
||||
(SELECT seasonality_factor
|
||||
FROM sales_seasonality
|
||||
WHERE MONTH(fd.forecast_date) = month
|
||||
LIMIT 1),
|
||||
0
|
||||
))
|
||||
)) *
|
||||
-- Add some randomness within a small range (±5%)
|
||||
(0.95 + (RAND() * 0.1))
|
||||
) as forecast_revenue,
|
||||
CASE
|
||||
WHEN ps.data_points >= 60 THEN 90
|
||||
@@ -1231,8 +1253,12 @@ async function calculateSalesForecasts(connection, startTime, totalProducts) {
|
||||
DATE_ADD(CURRENT_DATE, INTERVAL n DAY) as forecast_date
|
||||
FROM (
|
||||
SELECT 0 as n UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION
|
||||
SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 14 UNION SELECT 30 UNION
|
||||
SELECT 60 UNION SELECT 90
|
||||
SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9 UNION
|
||||
SELECT 10 UNION SELECT 11 UNION SELECT 12 UNION SELECT 13 UNION SELECT 14 UNION
|
||||
SELECT 15 UNION SELECT 16 UNION SELECT 17 UNION SELECT 18 UNION SELECT 19 UNION
|
||||
SELECT 20 UNION SELECT 21 UNION SELECT 22 UNION SELECT 23 UNION SELECT 24 UNION
|
||||
SELECT 25 UNION SELECT 26 UNION SELECT 27 UNION SELECT 28 UNION SELECT 29 UNION
|
||||
SELECT 30
|
||||
) numbers
|
||||
),
|
||||
category_stats AS (
|
||||
@@ -1242,7 +1268,15 @@ async function calculateSalesForecasts(connection, startTime, totalProducts) {
|
||||
STDDEV_SAMP(cds.daily_quantity) as std_daily_quantity,
|
||||
AVG(cds.daily_revenue) as avg_daily_revenue,
|
||||
STDDEV_SAMP(cds.daily_revenue) as std_daily_revenue,
|
||||
COUNT(*) as data_points
|
||||
COUNT(*) as data_points,
|
||||
-- Calculate day-of-week averages
|
||||
AVG(CASE WHEN DAYOFWEEK(cds.sale_date) = 1 THEN cds.daily_revenue END) as sunday_avg,
|
||||
AVG(CASE WHEN DAYOFWEEK(cds.sale_date) = 2 THEN cds.daily_revenue END) as monday_avg,
|
||||
AVG(CASE WHEN DAYOFWEEK(cds.sale_date) = 3 THEN cds.daily_revenue END) as tuesday_avg,
|
||||
AVG(CASE WHEN DAYOFWEEK(cds.sale_date) = 4 THEN cds.daily_revenue END) as wednesday_avg,
|
||||
AVG(CASE WHEN DAYOFWEEK(cds.sale_date) = 5 THEN cds.daily_revenue END) as thursday_avg,
|
||||
AVG(CASE WHEN DAYOFWEEK(cds.sale_date) = 6 THEN cds.daily_revenue END) as friday_avg,
|
||||
AVG(CASE WHEN DAYOFWEEK(cds.sale_date) = 7 THEN cds.daily_revenue END) as saturday_avg
|
||||
FROM category_daily_sales cds
|
||||
GROUP BY cds.category_id
|
||||
)
|
||||
@@ -1260,14 +1294,24 @@ async function calculateSalesForecasts(connection, startTime, totalProducts) {
|
||||
))
|
||||
) as forecast_units,
|
||||
GREATEST(0,
|
||||
cs.avg_daily_revenue *
|
||||
CASE DAYOFWEEK(fd.forecast_date)
|
||||
WHEN 1 THEN COALESCE(cs.sunday_avg, cs.avg_daily_revenue)
|
||||
WHEN 2 THEN COALESCE(cs.monday_avg, cs.avg_daily_revenue)
|
||||
WHEN 3 THEN COALESCE(cs.tuesday_avg, cs.avg_daily_revenue)
|
||||
WHEN 4 THEN COALESCE(cs.wednesday_avg, cs.avg_daily_revenue)
|
||||
WHEN 5 THEN COALESCE(cs.thursday_avg, cs.avg_daily_revenue)
|
||||
WHEN 6 THEN COALESCE(cs.friday_avg, cs.avg_daily_revenue)
|
||||
WHEN 7 THEN COALESCE(cs.saturday_avg, cs.avg_daily_revenue)
|
||||
END *
|
||||
(1 + COALESCE(
|
||||
(SELECT seasonality_factor
|
||||
FROM sales_seasonality
|
||||
WHERE MONTH(fd.forecast_date) = month
|
||||
LIMIT 1),
|
||||
0
|
||||
))
|
||||
)) *
|
||||
-- Add some randomness within a small range (±5%)
|
||||
(0.95 + (RAND() * 0.1))
|
||||
) as forecast_revenue,
|
||||
CASE
|
||||
WHEN cs.data_points >= 60 THEN 90
|
||||
|
||||
@@ -270,14 +270,13 @@ router.get('/forecast/metrics', async (req, res) => {
|
||||
// Get daily forecasts
|
||||
const [dailyForecasts] = await executeQuery(`
|
||||
SELECT
|
||||
forecast_date as date,
|
||||
COALESCE(SUM(forecast_units), 0) as units,
|
||||
DATE(forecast_date) as date,
|
||||
COALESCE(SUM(forecast_revenue), 0) as revenue,
|
||||
COALESCE(AVG(confidence_level), 0) as confidence
|
||||
FROM sales_forecasts
|
||||
WHERE forecast_date BETWEEN ? AND ?
|
||||
GROUP BY forecast_date
|
||||
ORDER BY forecast_date
|
||||
GROUP BY DATE(forecast_date)
|
||||
ORDER BY date
|
||||
`, [startDate, endDate]);
|
||||
|
||||
// Get category forecasts
|
||||
@@ -296,12 +295,11 @@ router.get('/forecast/metrics', async (req, res) => {
|
||||
|
||||
// Format response
|
||||
const response = {
|
||||
forecastSales: parseInt(metrics.total_forecast_units) || 0,
|
||||
forecastRevenue: parseFloat(metrics.total_forecast_revenue) || 0,
|
||||
confidenceLevel: parseFloat(metrics.overall_confidence) || 0,
|
||||
forecastSales: parseInt(metrics[0].total_forecast_units) || 0,
|
||||
forecastRevenue: parseFloat(metrics[0].total_forecast_revenue) || 0,
|
||||
confidenceLevel: parseFloat(metrics[0].overall_confidence) || 0,
|
||||
dailyForecasts: dailyForecasts.map(d => ({
|
||||
date: d.date,
|
||||
units: parseInt(d.units) || 0,
|
||||
revenue: parseFloat(d.revenue) || 0,
|
||||
confidence: parseFloat(d.confidence) || 0
|
||||
})),
|
||||
|
||||
Reference in New Issue
Block a user