const { outputProgress, formatElapsedTime, estimateRemaining, calculateRate, logError } = require('./utils/progress'); const { getConnection } = require('./utils/db'); async function calculateBrandMetrics(startTime, totalProducts, processedCount, isCancelled = false) { const connection = await getConnection(); try { if (isCancelled) { outputProgress({ status: 'cancelled', operation: 'Brand metrics calculation cancelled', current: processedCount, total: totalProducts, elapsed: formatElapsedTime(startTime), remaining: null, rate: calculateRate(startTime, processedCount), percentage: ((processedCount / totalProducts) * 100).toFixed(1), timing: { start_time: new Date(startTime).toISOString(), end_time: new Date().toISOString(), elapsed_seconds: Math.round((Date.now() - startTime) / 1000) } }); return processedCount; } outputProgress({ status: 'running', operation: 'Starting brand metrics calculation', current: processedCount, total: totalProducts, elapsed: formatElapsedTime(startTime), remaining: estimateRemaining(startTime, processedCount, totalProducts), rate: calculateRate(startTime, processedCount), percentage: ((processedCount / totalProducts) * 100).toFixed(1), timing: { start_time: new Date(startTime).toISOString(), end_time: new Date().toISOString(), elapsed_seconds: Math.round((Date.now() - startTime) / 1000) } }); // Calculate brand metrics with optimized queries await connection.query(` INSERT INTO brand_metrics ( brand, product_count, active_products, total_stock_units, total_stock_cost, total_stock_retail, total_revenue, avg_margin, growth_rate ) WITH filtered_products AS ( SELECT p.*, CASE WHEN p.stock_quantity <= 5000 THEN p.pid END as valid_pid, CASE WHEN p.visible = true AND p.stock_quantity <= 5000 THEN p.pid END as active_pid, CASE WHEN p.stock_quantity IS NULL OR p.stock_quantity < 0 OR p.stock_quantity > 5000 THEN 0 ELSE p.stock_quantity END as valid_stock FROM products p WHERE p.brand IS NOT NULL ), sales_periods AS ( SELECT p.brand, SUM(o.quantity * o.price) as period_revenue, CASE WHEN o.date >= DATE_SUB(CURRENT_DATE, INTERVAL 3 MONTH) THEN 'current' WHEN o.date BETWEEN DATE_SUB(CURRENT_DATE, INTERVAL 15 MONTH) AND DATE_SUB(CURRENT_DATE, INTERVAL 12 MONTH) THEN 'previous' END as period_type FROM filtered_products p JOIN orders o ON p.pid = o.pid WHERE o.canceled = false AND o.date >= DATE_SUB(CURRENT_DATE, INTERVAL 15 MONTH) GROUP BY p.brand, period_type ), brand_data AS ( SELECT p.brand, COUNT(DISTINCT p.valid_pid) as product_count, COUNT(DISTINCT p.active_pid) as active_products, SUM(p.valid_stock) as total_stock_units, SUM(p.valid_stock * p.cost_price) as total_stock_cost, SUM(p.valid_stock * p.price) as total_stock_retail, COALESCE(SUM(o.quantity * o.price), 0) as total_revenue, CASE WHEN SUM(o.quantity * o.price) > 0 THEN (SUM((o.price - p.cost_price) * o.quantity) * 100.0) / SUM(o.price * o.quantity) ELSE 0 END as avg_margin FROM filtered_products p LEFT JOIN orders o ON p.pid = o.pid AND o.canceled = false GROUP BY p.brand ) SELECT bd.brand, bd.product_count, bd.active_products, bd.total_stock_units, bd.total_stock_cost, bd.total_stock_retail, bd.total_revenue, bd.avg_margin, CASE WHEN MAX(CASE WHEN sp.period_type = 'previous' THEN sp.period_revenue END) = 0 AND MAX(CASE WHEN sp.period_type = 'current' THEN sp.period_revenue END) > 0 THEN 100.0 WHEN MAX(CASE WHEN sp.period_type = 'previous' THEN sp.period_revenue END) = 0 THEN 0.0 ELSE LEAST( GREATEST( ((MAX(CASE WHEN sp.period_type = 'current' THEN sp.period_revenue END) - MAX(CASE WHEN sp.period_type = 'previous' THEN sp.period_revenue END)) / NULLIF(MAX(CASE WHEN sp.period_type = 'previous' THEN sp.period_revenue END), 0)) * 100.0, -100.0 ), 999.99 ) END as growth_rate FROM brand_data bd LEFT JOIN sales_periods sp ON bd.brand = sp.brand GROUP BY bd.brand, bd.product_count, bd.active_products, bd.total_stock_units, bd.total_stock_cost, bd.total_stock_retail, bd.total_revenue, bd.avg_margin ON DUPLICATE KEY UPDATE product_count = VALUES(product_count), active_products = VALUES(active_products), total_stock_units = VALUES(total_stock_units), total_stock_cost = VALUES(total_stock_cost), total_stock_retail = VALUES(total_stock_retail), total_revenue = VALUES(total_revenue), avg_margin = VALUES(avg_margin), growth_rate = VALUES(growth_rate), last_calculated_at = CURRENT_TIMESTAMP `); processedCount = Math.floor(totalProducts * 0.97); outputProgress({ status: 'running', operation: 'Brand metrics calculated, starting time-based metrics', current: processedCount, total: totalProducts, elapsed: formatElapsedTime(startTime), remaining: estimateRemaining(startTime, processedCount, totalProducts), rate: calculateRate(startTime, processedCount), percentage: ((processedCount / totalProducts) * 100).toFixed(1), timing: { start_time: new Date(startTime).toISOString(), end_time: new Date().toISOString(), elapsed_seconds: Math.round((Date.now() - startTime) / 1000) } }); if (isCancelled) return processedCount; // Calculate brand time-based metrics with optimized query await connection.query(` INSERT INTO brand_time_metrics ( brand, year, month, product_count, active_products, total_stock_units, total_stock_cost, total_stock_retail, total_revenue, avg_margin ) WITH filtered_products AS ( SELECT p.*, CASE WHEN p.stock_quantity <= 5000 THEN p.pid END as valid_pid, CASE WHEN p.visible = true AND p.stock_quantity <= 5000 THEN p.pid END as active_pid, CASE WHEN p.stock_quantity IS NULL OR p.stock_quantity < 0 OR p.stock_quantity > 5000 THEN 0 ELSE p.stock_quantity END as valid_stock FROM products p WHERE p.brand IS NOT NULL ), monthly_metrics AS ( SELECT p.brand, YEAR(o.date) as year, MONTH(o.date) as month, COUNT(DISTINCT p.valid_pid) as product_count, COUNT(DISTINCT p.active_pid) as active_products, SUM(p.valid_stock) as total_stock_units, SUM(p.valid_stock * p.cost_price) as total_stock_cost, SUM(p.valid_stock * p.price) as total_stock_retail, SUM(o.quantity * o.price) as total_revenue, CASE WHEN SUM(o.quantity * o.price) > 0 THEN (SUM((o.price - p.cost_price) * o.quantity) * 100.0) / SUM(o.price * o.quantity) ELSE 0 END as avg_margin FROM filtered_products p LEFT JOIN orders o ON p.pid = o.pid AND o.canceled = false WHERE o.date >= DATE_SUB(CURRENT_DATE, INTERVAL 12 MONTH) GROUP BY p.brand, YEAR(o.date), MONTH(o.date) ) SELECT * FROM monthly_metrics ON DUPLICATE KEY UPDATE product_count = VALUES(product_count), active_products = VALUES(active_products), total_stock_units = VALUES(total_stock_units), total_stock_cost = VALUES(total_stock_cost), total_stock_retail = VALUES(total_stock_retail), total_revenue = VALUES(total_revenue), avg_margin = VALUES(avg_margin) `); processedCount = Math.floor(totalProducts * 0.99); outputProgress({ status: 'running', operation: 'Brand time-based metrics calculated', current: processedCount, total: totalProducts, elapsed: formatElapsedTime(startTime), remaining: estimateRemaining(startTime, processedCount, totalProducts), rate: calculateRate(startTime, processedCount), percentage: ((processedCount / totalProducts) * 100).toFixed(1), timing: { start_time: new Date(startTime).toISOString(), end_time: new Date().toISOString(), elapsed_seconds: Math.round((Date.now() - startTime) / 1000) } }); return processedCount; } catch (error) { logError(error, 'Error calculating brand metrics'); throw error; } finally { if (connection) { connection.release(); } } } module.exports = calculateBrandMetrics;