const mysql = require('mysql2/promise'); const path = require('path'); const fs = require('fs'); require('dotenv').config({ path: path.resolve(__dirname, '../.env') }); const dbConfig = { host: process.env.DB_HOST, user: process.env.DB_USER, password: process.env.DB_PASSWORD, database: process.env.DB_NAME, multipleStatements: true }; function outputProgress(data) { console.log(JSON.stringify(data)); } // Explicitly define all metrics-related tables const METRICS_TABLES = [ 'temp_sales_metrics', 'temp_purchase_metrics', 'product_metrics', 'product_time_aggregates', 'vendor_metrics', 'category_sales_metrics' ]; // Config tables that must exist const CONFIG_TABLES = [ 'stock_thresholds', 'lead_time_thresholds', 'sales_velocity_config', 'abc_classification_config', 'safety_stock_config', 'turnover_config' ]; // Core tables that must exist const REQUIRED_CORE_TABLES = [ 'products', 'orders', 'purchase_orders' ]; async function resetMetrics() { outputProgress({ status: 'running', operation: 'Starting metrics reset', percentage: '0' }); const connection = await mysql.createConnection(dbConfig); try { // First verify that core tables exist outputProgress({ status: 'running', operation: 'Verifying core tables exist', percentage: '10' }); // Use SHOW TABLES to verify core tables exist const [showTables] = await connection.query('SHOW TABLES'); const existingTables = showTables.map(t => Object.values(t)[0]); outputProgress({ operation: 'Core tables verification', message: { found: existingTables, required: REQUIRED_CORE_TABLES } }); // Check if any core tables are missing const missingCoreTables = REQUIRED_CORE_TABLES.filter( t => !existingTables.includes(t) ); if (missingCoreTables.length > 0) { throw new Error( `Core tables missing: ${missingCoreTables.join(', ')}. Please run reset-db.js first.` ); } // Verify all core tables use InnoDB const [engineStatus] = await connection.query('SHOW TABLE STATUS WHERE Name IN (?)', [REQUIRED_CORE_TABLES]); const nonInnoDBTables = engineStatus.filter(t => t.Engine !== 'InnoDB'); if (nonInnoDBTables.length > 0) { throw new Error( `Tables using non-InnoDB engine: ${nonInnoDBTables.map(t => t.Name).join(', ')}` ); } // Disable foreign key checks first await connection.query('SET FOREIGN_KEY_CHECKS = 0'); // Drop the metrics views first outputProgress({ status: 'running', operation: 'Dropping metrics views', percentage: '15' }); await connection.query('DROP VIEW IF EXISTS inventory_health, product_sales_trends'); // Drop only the metrics tables if they exist const [existing] = await connection.query(` SELECT GROUP_CONCAT(table_name) as tables FROM information_schema.tables WHERE table_schema = DATABASE() AND table_name IN (${METRICS_TABLES.map(table => `'${table}'`).join(',')}) `); if (existing[0].tables) { outputProgress({ status: 'running', operation: 'Dropping existing metrics tables', percentage: '20' }); const dropQuery = ` DROP TABLE IF EXISTS ${existing[0].tables .split(',') .map(table => '`' + table + '`') .join(', ')} `; await connection.query(dropQuery); } // Read metrics schema in its entirety outputProgress({ status: 'running', operation: 'Creating metrics tables', percentage: '40' }); const schemaPath = path.join(__dirname, '../db/metrics-schema.sql'); const schemaSQL = fs.readFileSync(schemaPath, 'utf8'); // Run the entire metrics-schema so it creates // the metrics tables and indexes in one shot await connection.query(schemaSQL); // Read and execute config schema outputProgress({ status: 'running', operation: 'Creating configuration tables', percentage: '60' }); const configSchemaPath = path.join(__dirname, '../db/config-schema.sql'); const configSchemaSQL = fs.readFileSync(configSchemaPath, 'utf8'); // Run the config schema await connection.query(configSchemaSQL); // Verify all tables were actually created using SHOW TABLES const [verifyTables] = await connection.query('SHOW TABLES'); const tablesAfterCreation = verifyTables.map(t => Object.values(t)[0]); // First verify metrics tables outputProgress({ status: 'running', operation: 'Verifying metrics tables', message: { found: tablesAfterCreation, required: METRICS_TABLES } }); const missingMetricsTables = METRICS_TABLES.filter( t => !tablesAfterCreation.includes(t) ); if (missingMetricsTables.length > 0) { throw new Error( `Failed to create metrics tables: ${missingMetricsTables.join(', ')}` ); } // Then verify config tables outputProgress({ status: 'running', operation: 'Verifying config tables', message: { found: tablesAfterCreation, required: CONFIG_TABLES } }); const missingConfigTables = CONFIG_TABLES.filter( t => !tablesAfterCreation.includes(t) ); if (missingConfigTables.length > 0) { throw new Error( `Failed to create config tables: ${missingConfigTables.join(', ')}` ); } // Re-enable foreign key checks await connection.query('SET FOREIGN_KEY_CHECKS = 1'); outputProgress({ status: 'complete', operation: 'Metrics and config tables have been reset', percentage: '100' }); return { success: true }; } catch (error) { console.error('Error resetting metrics:', error); outputProgress({ status: 'error', operation: 'Failed to reset metrics', error: error.message }); throw error; } finally { await connection.end(); } } // Export if required as a module if (typeof module !== 'undefined' && module.exports) { module.exports = resetMetrics; } // Run if called from command line if (require.main === module) { resetMetrics().catch(error => { console.error('Error:', error); process.exit(1); }); }