-- Disable foreign key checks SET FOREIGN_KEY_CHECKS = 0; -- Temporary tables for batch metrics processing CREATE TABLE IF NOT EXISTS temp_sales_metrics ( pid BIGINT NOT NULL, daily_sales_avg DECIMAL(10,3), weekly_sales_avg DECIMAL(10,3), monthly_sales_avg DECIMAL(10,3), total_revenue DECIMAL(10,3), avg_margin_percent DECIMAL(10,3), first_sale_date DATE, last_sale_date DATE, PRIMARY KEY (pid) ); CREATE TABLE IF NOT EXISTS temp_purchase_metrics ( pid BIGINT NOT NULL, avg_lead_time_days INT, last_purchase_date DATE, first_received_date DATE, last_received_date DATE, PRIMARY KEY (pid) ); -- New table for product metrics CREATE TABLE IF NOT EXISTS product_metrics ( pid BIGINT NOT NULL, last_calculated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, -- Sales velocity metrics daily_sales_avg DECIMAL(10,3), weekly_sales_avg DECIMAL(10,3), monthly_sales_avg DECIMAL(10,3), avg_quantity_per_order DECIMAL(10,3), number_of_orders INT, first_sale_date DATE, last_sale_date DATE, -- Stock metrics days_of_inventory INT, weeks_of_inventory INT, reorder_point INT, safety_stock INT, reorder_qty INT DEFAULT 0, overstocked_amt INT DEFAULT 0, -- Financial metrics avg_margin_percent DECIMAL(10,3), total_revenue DECIMAL(10,3), inventory_value DECIMAL(10,3), cost_of_goods_sold DECIMAL(10,3), gross_profit DECIMAL(10,3), gmroi DECIMAL(10,3), -- Purchase metrics avg_lead_time_days INT, last_purchase_date DATE, first_received_date DATE, last_received_date DATE, -- Classification metrics abc_class CHAR(1), stock_status VARCHAR(20), -- Turnover metrics turnover_rate DECIMAL(12,3), -- Lead time metrics current_lead_time INT, target_lead_time INT, lead_time_status VARCHAR(20), -- Forecast metrics forecast_accuracy DECIMAL(5,2) DEFAULT NULL, forecast_bias DECIMAL(5,2) DEFAULT NULL, last_forecast_date DATE DEFAULT NULL, PRIMARY KEY (pid), FOREIGN KEY (pid) REFERENCES products(pid) ON DELETE CASCADE, INDEX idx_metrics_revenue (total_revenue), INDEX idx_metrics_stock_status (stock_status), INDEX idx_metrics_lead_time (lead_time_status), INDEX idx_metrics_turnover (turnover_rate), INDEX idx_metrics_last_calculated (last_calculated_at), INDEX idx_metrics_abc (abc_class), INDEX idx_metrics_sales (daily_sales_avg, weekly_sales_avg, monthly_sales_avg), INDEX idx_metrics_forecast (forecast_accuracy, forecast_bias) ); -- New table for time-based aggregates CREATE TABLE IF NOT EXISTS product_time_aggregates ( pid BIGINT NOT NULL, year INT NOT NULL, month INT NOT NULL, -- Sales metrics total_quantity_sold INT DEFAULT 0, total_revenue DECIMAL(10,3) DEFAULT 0, total_cost DECIMAL(10,3) DEFAULT 0, order_count INT DEFAULT 0, -- Stock changes stock_received INT DEFAULT 0, stock_ordered INT DEFAULT 0, -- Calculated fields avg_price DECIMAL(10,3), profit_margin DECIMAL(10,3), inventory_value DECIMAL(10,3), gmroi DECIMAL(10,3), PRIMARY KEY (pid, year, month), FOREIGN KEY (pid) REFERENCES products(pid) ON DELETE CASCADE, INDEX idx_date (year, month) ); -- Create vendor details table CREATE TABLE IF NOT EXISTS vendor_details ( vendor VARCHAR(100) NOT NULL, contact_name VARCHAR(100), email VARCHAR(100), phone VARCHAR(20), status VARCHAR(20) DEFAULT 'active', notes TEXT, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (vendor), INDEX idx_vendor_status (status) ); -- New table for vendor metrics CREATE TABLE IF NOT EXISTS vendor_metrics ( vendor VARCHAR(100) NOT NULL, last_calculated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, -- Performance metrics avg_lead_time_days DECIMAL(10,3), on_time_delivery_rate DECIMAL(5,2), order_fill_rate DECIMAL(5,2), total_orders INT DEFAULT 0, total_late_orders INT DEFAULT 0, total_purchase_value DECIMAL(10,3) DEFAULT 0, avg_order_value DECIMAL(10,3), -- Product metrics active_products INT DEFAULT 0, total_products INT DEFAULT 0, -- Financial metrics total_revenue DECIMAL(10,3) DEFAULT 0, avg_margin_percent DECIMAL(5,2), -- Status status VARCHAR(20) DEFAULT 'active', PRIMARY KEY (vendor), FOREIGN KEY (vendor) REFERENCES vendor_details(vendor) ON DELETE CASCADE, INDEX idx_vendor_performance (on_time_delivery_rate), INDEX idx_vendor_status (status), INDEX idx_metrics_last_calculated (last_calculated_at), INDEX idx_vendor_metrics_orders (total_orders, total_late_orders) ); -- New table for category metrics CREATE TABLE IF NOT EXISTS category_metrics ( category_id BIGINT NOT NULL, last_calculated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, -- Product metrics product_count INT DEFAULT 0, active_products INT DEFAULT 0, -- Financial metrics total_value DECIMAL(10,3) DEFAULT 0, avg_margin DECIMAL(5,2), turnover_rate DECIMAL(12,3), growth_rate DECIMAL(5,2), -- Status status VARCHAR(20) DEFAULT 'active', PRIMARY KEY (category_id), FOREIGN KEY (category_id) REFERENCES categories(cat_id) ON DELETE CASCADE, INDEX idx_category_status (status), INDEX idx_category_growth (growth_rate), INDEX idx_metrics_last_calculated (last_calculated_at), INDEX idx_category_metrics_products (product_count, active_products) ); -- New table for vendor time-based metrics CREATE TABLE IF NOT EXISTS vendor_time_metrics ( vendor VARCHAR(100) NOT NULL, year INT NOT NULL, month INT NOT NULL, -- Order metrics total_orders INT DEFAULT 0, late_orders INT DEFAULT 0, avg_lead_time_days DECIMAL(10,3), -- Financial metrics total_purchase_value DECIMAL(10,3) DEFAULT 0, total_revenue DECIMAL(10,3) DEFAULT 0, avg_margin_percent DECIMAL(5,2), PRIMARY KEY (vendor, year, month), FOREIGN KEY (vendor) REFERENCES vendor_details(vendor) ON DELETE CASCADE, INDEX idx_vendor_date (year, month) ); -- New table for category time-based metrics CREATE TABLE IF NOT EXISTS category_time_metrics ( category_id BIGINT NOT NULL, year INT NOT NULL, month INT NOT NULL, -- Product metrics product_count INT DEFAULT 0, active_products INT DEFAULT 0, -- Financial metrics total_value DECIMAL(10,3) DEFAULT 0, total_revenue DECIMAL(10,3) DEFAULT 0, avg_margin DECIMAL(5,2), turnover_rate DECIMAL(12,3), PRIMARY KEY (category_id, year, month), FOREIGN KEY (category_id) REFERENCES categories(cat_id) ON DELETE CASCADE, INDEX idx_category_date (year, month) ); -- New table for category-based sales metrics CREATE TABLE IF NOT EXISTS category_sales_metrics ( category_id BIGINT NOT NULL, brand VARCHAR(100) NOT NULL, period_start DATE NOT NULL, period_end DATE NOT NULL, avg_daily_sales DECIMAL(10,3) DEFAULT 0, total_sold INT DEFAULT 0, num_products INT DEFAULT 0, avg_price DECIMAL(10,3) DEFAULT 0, last_calculated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (category_id, brand, period_start, period_end), FOREIGN KEY (category_id) REFERENCES categories(cat_id) ON DELETE CASCADE, INDEX idx_category_brand (category_id, brand), INDEX idx_period (period_start, period_end) ); -- New table for brand metrics CREATE TABLE IF NOT EXISTS brand_metrics ( brand VARCHAR(100) NOT NULL, last_calculated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, -- Product metrics product_count INT DEFAULT 0, active_products INT DEFAULT 0, -- Stock metrics total_stock_units INT DEFAULT 0, total_stock_cost DECIMAL(10,2) DEFAULT 0, total_stock_retail DECIMAL(10,2) DEFAULT 0, -- Sales metrics total_revenue DECIMAL(10,2) DEFAULT 0, avg_margin DECIMAL(5,2) DEFAULT 0, growth_rate DECIMAL(5,2) DEFAULT 0, PRIMARY KEY (brand), INDEX idx_brand_metrics_last_calculated (last_calculated_at), INDEX idx_brand_metrics_revenue (total_revenue), INDEX idx_brand_metrics_growth (growth_rate) ); -- New table for brand time-based metrics CREATE TABLE IF NOT EXISTS brand_time_metrics ( brand VARCHAR(100) NOT NULL, year INT NOT NULL, month INT NOT NULL, -- Product metrics product_count INT DEFAULT 0, active_products INT DEFAULT 0, -- Stock metrics total_stock_units INT DEFAULT 0, total_stock_cost DECIMAL(10,2) DEFAULT 0, total_stock_retail DECIMAL(10,2) DEFAULT 0, -- Sales metrics total_revenue DECIMAL(10,2) DEFAULT 0, avg_margin DECIMAL(5,2) DEFAULT 0, PRIMARY KEY (brand, year, month), INDEX idx_brand_date (year, month) ); -- New table for sales forecasts CREATE TABLE IF NOT EXISTS sales_forecasts ( pid BIGINT NOT NULL, forecast_date DATE NOT NULL, forecast_units DECIMAL(10,2) DEFAULT 0, forecast_revenue DECIMAL(10,2) DEFAULT 0, confidence_level DECIMAL(5,2) DEFAULT 0, last_calculated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (pid, forecast_date), FOREIGN KEY (pid) REFERENCES products(pid) ON DELETE CASCADE, INDEX idx_forecast_date (forecast_date), INDEX idx_forecast_last_calculated (last_calculated_at) ); -- New table for category forecasts CREATE TABLE IF NOT EXISTS category_forecasts ( category_id BIGINT NOT NULL, forecast_date DATE NOT NULL, forecast_units DECIMAL(10,2) DEFAULT 0, forecast_revenue DECIMAL(10,2) DEFAULT 0, confidence_level DECIMAL(5,2) DEFAULT 0, last_calculated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (category_id, forecast_date), FOREIGN KEY (category_id) REFERENCES categories(cat_id) ON DELETE CASCADE, INDEX idx_category_forecast_date (forecast_date), INDEX idx_category_forecast_last_calculated (last_calculated_at) ); -- Create table for sales seasonality factors CREATE TABLE IF NOT EXISTS sales_seasonality ( month INT NOT NULL, seasonality_factor DECIMAL(5,3) DEFAULT 0, last_updated TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (month), CHECK (month BETWEEN 1 AND 12), CHECK (seasonality_factor BETWEEN -1.0 AND 1.0) ); -- Insert default seasonality factors (neutral) INSERT INTO sales_seasonality (month, seasonality_factor) VALUES (1, 0), (2, 0), (3, 0), (4, 0), (5, 0), (6, 0), (7, 0), (8, 0), (9, 0), (10, 0), (11, 0), (12, 0) ON DUPLICATE KEY UPDATE last_updated = CURRENT_TIMESTAMP; -- Re-enable foreign key checks SET FOREIGN_KEY_CHECKS = 1; -- Create view for inventory health CREATE OR REPLACE VIEW inventory_health AS WITH product_thresholds AS ( SELECT p.pid, COALESCE( -- Try category+vendor specific (SELECT critical_days FROM stock_thresholds st JOIN product_categories pc ON st.category_id = pc.cat_id WHERE pc.pid = p.pid AND st.vendor = p.vendor LIMIT 1), -- Try category specific (SELECT critical_days FROM stock_thresholds st JOIN product_categories pc ON st.category_id = pc.cat_id WHERE pc.pid = p.pid AND st.vendor IS NULL LIMIT 1), -- Try vendor specific (SELECT critical_days FROM stock_thresholds st WHERE st.category_id IS NULL AND st.vendor = p.vendor LIMIT 1), -- Fall back to default (SELECT critical_days FROM stock_thresholds st WHERE st.category_id IS NULL AND st.vendor IS NULL LIMIT 1), 7 ) as critical_days, COALESCE( -- Try category+vendor specific (SELECT reorder_days FROM stock_thresholds st JOIN product_categories pc ON st.category_id = pc.cat_id WHERE pc.pid = p.pid AND st.vendor = p.vendor LIMIT 1), -- Try category specific (SELECT reorder_days FROM stock_thresholds st JOIN product_categories pc ON st.category_id = pc.cat_id WHERE pc.pid = p.pid AND st.vendor IS NULL LIMIT 1), -- Try vendor specific (SELECT reorder_days FROM stock_thresholds st WHERE st.category_id IS NULL AND st.vendor = p.vendor LIMIT 1), -- Fall back to default (SELECT reorder_days FROM stock_thresholds st WHERE st.category_id IS NULL AND st.vendor IS NULL LIMIT 1), 14 ) as reorder_days, COALESCE( -- Try category+vendor specific (SELECT overstock_days FROM stock_thresholds st JOIN product_categories pc ON st.category_id = pc.cat_id WHERE pc.pid = p.pid AND st.vendor = p.vendor LIMIT 1), -- Try category specific (SELECT overstock_days FROM stock_thresholds st JOIN product_categories pc ON st.category_id = pc.cat_id WHERE pc.pid = p.pid AND st.vendor IS NULL LIMIT 1), -- Try vendor specific (SELECT overstock_days FROM stock_thresholds st WHERE st.category_id IS NULL AND st.vendor = p.vendor LIMIT 1), -- Fall back to default (SELECT overstock_days FROM stock_thresholds st WHERE st.category_id IS NULL AND st.vendor IS NULL LIMIT 1), 90 ) as overstock_days FROM products p ) SELECT p.pid, p.SKU, p.title, p.stock_quantity, COALESCE(pm.daily_sales_avg, 0) as daily_sales_avg, COALESCE(pm.days_of_inventory, 0) as days_of_inventory, COALESCE(pm.reorder_point, 0) as reorder_point, COALESCE(pm.safety_stock, 0) as safety_stock, CASE WHEN pm.daily_sales_avg = 0 THEN 'New' WHEN p.stock_quantity <= CEIL(pm.daily_sales_avg * pt.critical_days) THEN 'Critical' WHEN p.stock_quantity <= CEIL(pm.daily_sales_avg * pt.reorder_days) THEN 'Reorder' WHEN p.stock_quantity > (pm.daily_sales_avg * pt.overstock_days) THEN 'Overstocked' ELSE 'Healthy' END as stock_status FROM products p LEFT JOIN product_metrics pm ON p.pid = pm.pid LEFT JOIN product_thresholds pt ON p.pid = pt.pid WHERE p.managing_stock = true; -- Create view for category performance trends CREATE OR REPLACE VIEW category_performance_trends AS SELECT c.cat_id as category_id, c.name, c.description, p.name as parent_name, c.status, cm.product_count, cm.active_products, cm.total_value, cm.avg_margin, cm.turnover_rate, cm.growth_rate, CASE WHEN cm.growth_rate >= 20 THEN 'High Growth' WHEN cm.growth_rate >= 5 THEN 'Growing' WHEN cm.growth_rate >= -5 THEN 'Stable' ELSE 'Declining' END as performance_rating FROM categories c LEFT JOIN categories p ON c.parent_id = p.cat_id LEFT JOIN category_metrics cm ON c.cat_id = cm.category_id;