// IPI Index Calculation Service const mysql = require('mysql2/promise'); const logger = require("./logger"); class IPICalculationService { constructor(dbConfig) { this.pool = mysql.createPool(dbConfig); } // STEP 1: Calculate and Store Base Year Average Production (2022) async calculateBaseYearProduction(baseYear = 2022, forceRecalculate = false) { const connection = await this.pool.getConnection(); let logId = null; try { await connection.beginTransaction(); logger.info(`calculateBaseYearProduction - Calculating Base Year Production for ${baseYear}...`); // If force recalculate, delete existing data if (forceRecalculate) { logger.info('calculateBaseYearProduction - Force recalculate enabled , clearing existing data...'); await connection.query( `DELETE FROM base_year_production WHERE base_year = ?`, [baseYear] ); } // Log calculation start logId = await this.logRecord( 'base_year', baseYear, null, 'Started', null, 0, null, connection); // Your existing query to calculate base year production const query = ` SELECT p.id AS product_id, p.hs_code AS product_hs_code, u.uom_short_name AS unit, SUM(CASE WHEN s.quarter = 'Q1' THEN sp.current_quantity_period_one END) AS Jan, SUM(CASE WHEN s.quarter = 'Q1' THEN sp.current_quantity_period_two END) AS Feb, SUM(CASE WHEN s.quarter = 'Q1' THEN sp.current_quantity_period_three END) AS Mar, SUM(CASE WHEN s.quarter = 'Q2' THEN sp.current_quantity_period_one END) AS Apr, SUM(CASE WHEN s.quarter = 'Q2' THEN sp.current_quantity_period_two END) AS May, SUM(CASE WHEN s.quarter = 'Q2' THEN sp.current_quantity_period_three END) AS Jun, SUM(CASE WHEN s.quarter = 'Q3' THEN sp.current_quantity_period_one END) AS Jul, SUM(CASE WHEN s.quarter = 'Q3' THEN sp.current_quantity_period_two END) AS Aug, SUM(CASE WHEN s.quarter = 'Q3' THEN sp.current_quantity_period_three END) AS Sep, SUM(CASE WHEN s.quarter = 'Q4' THEN sp.current_quantity_period_one END) AS Oct, SUM(CASE WHEN s.quarter = 'Q4' THEN sp.current_quantity_period_two END) AS Nov, SUM(CASE WHEN s.quarter = 'Q4' THEN sp.current_quantity_period_three END) AS \`Dec\`, ROUND(( COALESCE(SUM(CASE WHEN s.quarter='Q1' THEN sp.current_quantity_period_one END),0) + COALESCE(SUM(CASE WHEN s.quarter='Q1' THEN sp.current_quantity_period_two END),0) + COALESCE(SUM(CASE WHEN s.quarter='Q1' THEN sp.current_quantity_period_three END),0) + COALESCE(SUM(CASE WHEN s.quarter='Q2' THEN sp.current_quantity_period_one END),0) + COALESCE(SUM(CASE WHEN s.quarter='Q2' THEN sp.current_quantity_period_two END),0) + COALESCE(SUM(CASE WHEN s.quarter='Q2' THEN sp.current_quantity_period_three END),0) + COALESCE(SUM(CASE WHEN s.quarter='Q3' THEN sp.current_quantity_period_one END),0) + COALESCE(SUM(CASE WHEN s.quarter='Q3' THEN sp.current_quantity_period_two END),0) + COALESCE(SUM(CASE WHEN s.quarter='Q3' THEN sp.current_quantity_period_three END),0) + COALESCE(SUM(CASE WHEN s.quarter='Q4' THEN sp.current_quantity_period_one END),0) + COALESCE(SUM(CASE WHEN s.quarter='Q4' THEN sp.current_quantity_period_two END),0) + COALESCE(SUM(CASE WHEN s.quarter='Q4' THEN sp.current_quantity_period_three END),0) ) / 12, 2) AS avg_by_production FROM submission_products sp JOIN submission s ON s.id = sp.submission_id JOIN products p ON p.id = sp.product_id LEFT JOIN unit_master u ON u.id = p.unit_id WHERE s.year = ? AND sp.is_active = 1 AND s.status = 'Approved' GROUP BY p.id `; const [products] = await connection.query(query, [baseYear]); logger.info(`calculateBaseYearProduction - Found ${products.length} products to process...`); // Insert into base_year_production table let processedCount = 0; for (const product of products) { await connection.query( `INSERT INTO base_year_production (product_id, product_hs_code, base_year, unit, jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, \`dec\`, avg_by_production) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [ product.product_id, product.product_hs_code, baseYear, product.unit, product.Jan || 0, product.Feb || 0, product.Mar || 0, product.Apr || 0, product.May || 0, product.Jun || 0, product.Jul || 0, product.Aug || 0, product.Sep || 0, product.Oct || 0, product.Nov || 0, product.Dec || 0, product.avg_by_production ] ); processedCount++; // Log progress every 50 products if (processedCount % 50 === 0) { logger.info(`calculateBaseYearProduction - Processed ${processedCount}/${products.length} products...`); } } // Update log await this.logRecord('base_year', baseYear, null, 'Completed', logId, products.length, null,connection); await connection.commit(); logger.info(`calculateBaseYearProduction - Base Year Production calculated: ${products.length} products`); return { success: true, productsProcessed: products.length }; } catch (error) { await connection.rollback(); console.error('Error calculating base year production:', error); logger.error(`calculateBaseYearProduction - Error calculating base year production: ${error} `); // Update log OUTSIDE the failed transaction if (logId) { await this.logRecord('base_year', baseYear, null, 'Failed', logId, 0, error.message); } throw error; } finally { connection.release(); } } // STEP 2: Aggregate Monthly Production async aggregateMonthlyProduction(year, month) { const connection = await this.pool.getConnection(); let logId = null; try { await connection.beginTransaction(); logger.info(`aggregateMonthlyProduction - Aggregating production for ${year}-${month}...`); // Log calculation start logId = await this.logRecord( 'monthly_production', year, month, 'Started', null, 0, null, connection); // Determine quarter and period based on month const quarterMap = { 1: { quarter: 'Q1', period: 'current_quantity_period_one' }, 2: { quarter: 'Q1', period: 'current_quantity_period_two' }, 3: { quarter: 'Q1', period: 'current_quantity_period_three' }, 4: { quarter: 'Q2', period: 'current_quantity_period_one' }, 5: { quarter: 'Q2', period: 'current_quantity_period_two' }, 6: { quarter: 'Q2', period: 'current_quantity_period_three' }, 7: { quarter: 'Q3', period: 'current_quantity_period_one' }, 8: { quarter: 'Q3', period: 'current_quantity_period_two' }, 9: { quarter: 'Q3', period: 'current_quantity_period_three' }, 10: { quarter: 'Q4', period: 'current_quantity_period_one' }, 11: { quarter: 'Q4', period: 'current_quantity_period_two' }, 12: { quarter: 'Q4', period: 'current_quantity_period_three' } }; const { quarter, period } = quarterMap[month]; const monthNames = ['', 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; // Aggregate production by product const query = ` SELECT p.id AS product_id, p.hs_code AS product_hs_code, u.uom_short_name AS unit, SUM(sp.${period}) AS production_quantity FROM submission_products sp JOIN submission s ON s.id = sp.submission_id JOIN products p ON p.id = sp.product_id LEFT JOIN unit_master u ON u.id = p.unit_id WHERE s.year = ? AND s.quarter = ? AND sp.is_active = 1 AND s.status = 'Approved' GROUP BY p.id `; const [products] = await connection.query(query, [year, quarter]); // Insert monthly production for (const product of products) { await connection.query( `INSERT INTO monthly_production (product_id, product_hs_code, year, month, month_name, production_quantity, unit) VALUES (?, ?, ?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE production_quantity=VALUES(production_quantity), unit=VALUES(unit)`, [ product.product_id, product.product_hs_code, year, month, monthNames[month], product.production_quantity || 0, product.unit ] ); } // Update log await this.logRecord('monthly_production', year, month, 'Completed', logId, products.length, null,connection); await connection.commit(); logger.info(`aggregateMonthlyProduction - Monthly production aggregated: ${products.length} products`); return { success: true, productsProcessed: products.length }; } catch (error) { await connection.rollback(); console.error('Error aggregating monthly production:', error); logger.error(`aggregateMonthlyProduction - Error aggregating monthly production: ${error}`); // Update log OUTSIDE the failed transaction if (logId) { await this.logRecord('monthly_production', year, month, 'Failed', logId, 0, error.message); } throw error; } finally { connection.release(); } } // STEP 3: Calculate Item Level Indices async calculateItemLevelIndices(year, month) { const connection = await this.pool.getConnection(); let logId = null; try { await connection.beginTransaction(); logger.info(`calculateItemLevelIndices - Calculating item level indices for ${year}-${month}...`); // Log calculation start logId = await this.logRecord( 'item_level_indices', year, month, 'Started', null, 0, null, connection); const monthNames = ['', 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; // Calculate indices: Ri = Current Production / Base Year Avg Production // Ii = Ri × 100 const query = ` SELECT mp.product_id, mp.product_hs_code, mp.production_quantity AS current_production, byp.avg_by_production AS base_year_avg_production, (mp.production_quantity / byp.avg_by_production) AS production_relative, ((mp.production_quantity / byp.avg_by_production) * 100) AS item_index FROM monthly_production mp JOIN base_year_production byp ON byp.product_id = mp.product_id WHERE mp.year = ? AND mp.month = ? AND byp.avg_by_production > 0 `; const [items] = await connection.query(query, [year, month]); // Insert item level indices for (const item of items) { await connection.query( `INSERT INTO item_level_indices (product_id, product_hs_code, year, month, month_name, current_production, base_year_avg_production, production_relative, item_index) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE current_production=VALUES(current_production), base_year_avg_production=VALUES(base_year_avg_production), production_relative=VALUES(production_relative), item_index=VALUES(item_index)`, [ item.product_id, item.product_hs_code, year, month, monthNames[month], item.current_production, item.base_year_avg_production, item.production_relative, item.item_index ] ); } // Update log await this.logRecord('item_level_indices', year, month, 'Completed', logId, items.length, null,connection); await connection.commit(); logger.info(`calculateItemLevelIndices - Item level indices calculated: ${items.length} items`); return { success: true, itemsProcessed: items.length }; } catch (error) { await connection.rollback(); console.error('Error calculating item indices:', error); logger.error(`calculateItemLevelIndices - Error calculating item indices: ${error}`); // Update log OUTSIDE the failed transaction if (logId) { await this.logRecord('item_level_indices', year, month, 'Failed', logId, 0, error.message); } throw error; } finally { connection.release(); } } // STEP 4: Calculate ISIC 4-Digit Level Indices async calculateISIC4DigitIndices(year, month) { const connection = await this.pool.getConnection(); let logId = null; try { await connection.beginTransaction(); logger.info(`calculateISIC4DigitIndices - Calculating ISIC 4-digit indices for ${year}-${month}...`); // Log calculation start logId = await this.logRecord( 'isic_4digit_indices', year, month, 'Started', null, 0, null, connection); const monthNames = ['', 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; // Get ISIC code from establishments table directly // Using simple average of item indices for each ISIC 4-digit code const query = ` SELECT LEFT(e.isic_code , 4) as isic_4digit_code, COUNT(DISTINCT ili.product_id) AS total_weight, SUM(ili.item_index) AS weighted_index_sum, AVG(ili.item_index) AS isic_4digit_index FROM item_level_indices ili JOIN products p ON p.id = ili.product_id JOIN submission_products sp ON sp.product_id = p.id JOIN submission s ON s.id = sp.submission_id AND s.year = ili.year JOIN establishments e ON e.id = s.establishment_id WHERE ili.year = ? AND ili.month = ? AND e.isic_code IS NOT NULL AND e.isic_code != '' GROUP BY LEFT(e.isic_code , 4) `; const [isic4Results] = await connection.query(query, [year, month]); for (const result of isic4Results) { await connection.query( `INSERT INTO isic_4digit_indices (isic_4digit_code, year, month, month_name, total_weight, weighted_index_sum, isic_4digit_index) VALUES (?, ?, ?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE total_weight=VALUES(total_weight), weighted_index_sum=VALUES(weighted_index_sum), isic_4digit_index=VALUES(isic_4digit_index)`, [ result.isic_4digit_code, year, month, monthNames[month], result.total_weight, result.weighted_index_sum, result.isic_4digit_index ] ); } // Update log await this.logRecord('isic_4digit_indices', year, month, 'Completed', logId, isic4Results.length, null,connection); await connection.commit(); logger.info(`calculateISIC4DigitIndices - ISIC 4-digit indices calculated: ${isic4Results.length} codes`); return { success: true, codesProcessed: isic4Results.length }; } catch (error) { await connection.rollback(); console.error('Error calculating ISIC 4-digit indices:', error); logger.error(`calculateISIC4DigitIndices - Error calculating ISIC 4-digit indices: ${error} `); // Update log OUTSIDE the failed transaction if (logId) { await this.logRecord('isic_4digit_indices', year, month, 'Failed', logId, 0, error.message); } throw error; } finally { connection.release(); } } // STEP 5: Calculate ISIC 3-Digit Level Indices async calculateISIC3DigitIndices(year, month) { const connection = await this.pool.getConnection(); let logId = null; try { await connection.beginTransaction(); logger.info(`calculateISIC3DigitIndices - Calculating ISIC 3-digit indices for ${year}-${month}...`); // Log calculation start logId = await this.logRecord( 'isic_3digit_indices', year, month, 'Started', null, 0, null, connection); const monthNames = ['', 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; const query = ` SELECT LEFT(isic_4digit_code, 3) AS isic_3digit_code, SUM(total_weight) AS total_weight, SUM(total_weight * isic_4digit_index) AS weighted_index_sum, (SUM(total_weight * isic_4digit_index) / SUM(total_weight)) AS isic_3digit_index FROM isic_4digit_indices WHERE year = ? AND month = ? GROUP BY LEFT(isic_4digit_code, 3) `; const [isic3Results] = await connection.query(query, [year, month]); for (const result of isic3Results) { await connection.query( `INSERT INTO isic_3digit_indices (isic_3digit_code, year, month, month_name, total_weight, weighted_index_sum, isic_3digit_index) VALUES (?, ?, ?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE total_weight=VALUES(total_weight), weighted_index_sum=VALUES(weighted_index_sum), isic_3digit_index=VALUES(isic_3digit_index)`, [ result.isic_3digit_code, year, month, monthNames[month], result.total_weight, result.weighted_index_sum, result.isic_3digit_index ] ); } // Update log await this.logRecord('isic_3digit_indices', year, month, 'Completed', logId, isic3Results.length, null,connection); await connection.commit(); logger.info(`calculateISIC3DigitIndices - ISIC 3-digit indices calculated: ${isic3Results.length} codes`); return { success: true, codesProcessed: isic3Results.length }; } catch (error) { await connection.rollback(); console.error('Error calculating ISIC 3-digit indices:', error); logger.error(`calculateISIC3DigitIndices - Error calculating ISIC 3-digit indices: ${error} `); // Update log OUTSIDE the failed transaction if (logId) { await this.logRecord('isic_3digit_indices', year, month, 'Failed', logId, 0, error.message); } throw error; } finally { connection.release(); } } // STEP 6: Calculate ISIC 2-Digit Level Indices async calculateISIC2DigitIndices(year, month) { const connection = await this.pool.getConnection(); let logId = null; try { await connection.beginTransaction(); logger.info(`calculateISIC2DigitIndices - Calculating ISIC 2-digit indices for ${year}-${month}...`); // Log calculation start logId = await this.logRecord( 'isic_2digit_indices', year, month, 'Started', null, 0, null, connection); const monthNames = ['', 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; const query = ` SELECT LEFT(isic_3digit_code, 2) AS isic_2digit_code, SUM(total_weight) AS total_weight, SUM(total_weight * isic_3digit_index) AS weighted_index_sum, (SUM(total_weight * isic_3digit_index) / SUM(total_weight)) AS isic_2digit_index FROM isic_3digit_indices WHERE year = ? AND month = ? GROUP BY LEFT(isic_3digit_code, 2) `; const [isic2Results] = await connection.query(query, [year, month]); for (const result of isic2Results) { await connection.query( `INSERT INTO isic_2digit_indices (isic_2digit_code, year, month, month_name, total_weight, weighted_index_sum, isic_2digit_index) VALUES (?, ?, ?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE total_weight=VALUES(total_weight), weighted_index_sum=VALUES(weighted_index_sum), isic_2digit_index=VALUES(isic_2digit_index)`, [ result.isic_2digit_code, year, month, monthNames[month], result.total_weight, result.weighted_index_sum, result.isic_2digit_index ] ); } // Update log await this.logRecord('isic_2digit_indices', year, month, 'Completed', logId, isic2Results.length, null,connection); await connection.commit(); logger.info(`calculateISIC2DigitIndices - ISIC 2-digit indices calculated: ${isic2Results.length} codes`); return { success: true, codesProcessed: isic2Results.length }; } catch (error) { await connection.rollback(); console.error('Error calculating ISIC 2-digit indices:', error); logger.error(`calculateISIC2DigitIndices - Error calculating ISIC 2-digit indices: ${error}`); // Update log OUTSIDE the failed transaction if (logId) { await this.logRecord('isic_2digit_indices', year, month, 'Failed', logId, 0, error.message); } throw error; } finally { connection.release(); } } // STEP 7: Calculate Manufacturing IPI (Headline Index) async calculateManufacturingIPI(year, month) { const connection = await this.pool.getConnection(); let logId = null; try { await connection.beginTransaction(); logger.info(`calculateManufacturingIPI - Calculating Manufacturing indices for ${year}-${month}...`); // Log calculation start logId = await this.logRecord( 'manufacturing_ipi', year, month, 'Started', null, 0, null, connection); const monthNames = ['', 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; // Calculate Manufacturing IPI const query = ` SELECT SUM(total_weight) AS total_weight, SUM(total_weight * isic_2digit_index) AS weighted_index_sum, (SUM(total_weight * isic_2digit_index) / SUM(total_weight)) AS manufacturing_index FROM isic_2digit_indices WHERE year = ? AND month = ? `; const [result] = await connection.query(query, [year, month]); // If no data found, return empty response if (!result || result.length === 0) { return { success: true, index: null, momChange: null, yoyChange: null, message: "No IPI data found for the given year and month" }; } const ipiData = result[0]; // Calculate MoM and YoY changes let momChange = null; let yoyChange = null; // Get previous month index for MoM const prevMonth = month === 1 ? 12 : month - 1; const prevYear = month === 1 ? year - 1 : year; const [prevMonthData] = await connection.query( `SELECT manufacturing_index FROM manufacturing_ipi WHERE year = ? AND month = ?`, [prevYear, prevMonth] ); if (prevMonthData.length > 0) { const prevIndex = prevMonthData[0].manufacturing_index; momChange = ((ipiData.manufacturing_index - prevIndex) / prevIndex) * 100; } // Get same month last year for YoY const [lastYearData] = await connection.query( `SELECT manufacturing_index FROM manufacturing_ipi WHERE year = ? AND month = ?`, [year - 1, month] ); if (lastYearData.length > 0) { const lastYearIndex = lastYearData[0].manufacturing_index; yoyChange = ((ipiData.manufacturing_index - lastYearIndex) / lastYearIndex) * 100; } // Reference date is the first day of the month const referenceDate = `${year}-${String(month).padStart(2, '0')}-01`; // Insert Manufacturing IPI await connection.query( `INSERT INTO manufacturing_ipi (year, month, month_name, reference_date, total_weight, weighted_index_sum, manufacturing_index, mom_change, yoy_change, status) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 'Completed') ON DUPLICATE KEY UPDATE total_weight=VALUES(total_weight), weighted_index_sum=VALUES(weighted_index_sum), manufacturing_index=VALUES(manufacturing_index), mom_change=VALUES(mom_change), yoy_change=VALUES(yoy_change), status=VALUES(status)`, [ year, month, monthNames[month], referenceDate, ipiData.total_weight, ipiData.weighted_index_sum, ipiData.manufacturing_index, momChange, yoyChange ] ); // Update log await this.logRecord('manufacturing_ipi', year, month, 'Completed', logId, result.length, null,connection); await connection.commit(); logger.info(`calculateManufacturingIPI - Manufacturing IPI calculated: ${ipiData.manufacturing_index}`); logger.info(`calculateManufacturingIPI - MoM: ${momChange ? momChange.toFixed(2) + '%' : 'N/A'}`); logger.info(`calculateManufacturingIPI - YoY: ${yoyChange ? yoyChange.toFixed(2) + '%' : 'N/A'}`); return { success: true, index: ipiData.manufacturing_index, momChange, yoyChange }; } catch (error) { await connection.rollback(); console.error('Error calculating Manufacturing IPI:', error); logger.error(`calculateManufacturingIPI - Error calculating Manufacturing IPI: ${error}`); // Update log OUTSIDE the failed transaction if (logId) { await this.logRecord('manufacturing_ipi', year, month, 'Failed', logId, 0, error.message); } throw error; } finally { connection.release(); } } // MASTER FUNCTION: Run Complete Calculation Pipeline async runCompleteCalculation(year, month) { logger.info(`**************************** Starting IPI Calculation for ${year}-${month} ****************************`); try { // Step 1: Aggregate monthly production await this.aggregateMonthlyProduction(year, month); // Step 2: Calculate item level indices await this.calculateItemLevelIndices(year, month); // Step 3: Calculate ISIC 4-digit indices await this.calculateISIC4DigitIndices(year, month); // Step 4: Calculate ISIC 3-digit indices await this.calculateISIC3DigitIndices(year, month); // Step 5: Calculate ISIC 2-digit indices await this.calculateISIC2DigitIndices(year, month); // Step 6: Calculate Manufacturing IPI const result = await this.calculateManufacturingIPI(year, month); logger.info(`**************************** IPI Calculation Completed Successfully ****************************`); return result; } catch (error) { console.error('Error in complete calculation:', error); logger.error(` Error in complete calculation: ${error}`); throw error; } } // HELPER : calculation log async logRecord(calculation_type, reference_year, reference_month = null, status = 'Started', logId = null, records_processed = 0, error_message = null, useConnection = null) { const connection = useConnection || await this.pool.getConnection(); try { if (!logId) { const [result] = await connection.query( `INSERT INTO calculation_log (calculation_type, reference_year, reference_month, status) VALUES (?, ?, ?, ?)`, [calculation_type, reference_year, reference_month, status] ); return result.insertId; } if(records_processed == 0) { status = 'Failed'; } await connection.query( `UPDATE calculation_log SET status=?, records_processed=?, error_message=?, completed_at=NOW() WHERE id=?`, [status, records_processed, error_message?.substring(0,65535) || null, logId] ); } catch (err) { console.error('[LOG ERROR]', err.message); logger.error(` Log record error: ${err.message} `); } finally { if (!useConnection) connection.release(); } } // HELPER: Verify Base Year Data async verifyBaseYearData(baseYear = 2022) { const connection = await this.pool.getConnection(); try { logger.info(` Verifying Base Year ${baseYear} Data...`); // Count products const [countResult] = await connection.query( `SELECT COUNT(*) as count FROM base_year_production WHERE base_year = ?`, [baseYear] ); // Get sample data const [sampleData] = await connection.query( `SELECT product_id, product_hs_code, avg_by_production, jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, \`dec\` FROM base_year_production WHERE base_year = ? ORDER BY product_id LIMIT 5`, [baseYear] ); // Check for products with zero average const [zeroAvgResult] = await connection.query( `SELECT COUNT(*) as count FROM base_year_production WHERE base_year = ? AND (avg_by_production = 0 OR avg_by_production IS NULL)`, [baseYear] ); logger.info(` Total products in base year: ${countResult[0].count}`); logger.info(` Products with zero/null average: ${zeroAvgResult[0].count}`); logger.info(` Sample data (first 5 products): ${sampleData} `); return { totalProducts: countResult[0].count, zeroAverageProducts: zeroAvgResult[0].count, sampleData: sampleData }; } finally { connection.release(); } } // HELPER: Clear All Calculated Data for a Period async clearCalculatedData(year, month) { const connection = await this.pool.getConnection(); try { await connection.beginTransaction(); logger.info(` Clearing calculated data for ${year}-${month} `); await connection.query( `DELETE FROM monthly_production WHERE year = ? AND month = ?`, [year, month] ); await connection.query( `DELETE FROM item_level_indices WHERE year = ? AND month = ?`, [year, month] ); await connection.query( `DELETE FROM isic_4digit_indices WHERE year = ? AND month = ?`, [year, month] ); await connection.query( `DELETE FROM isic_3digit_indices WHERE year = ? AND month = ?`, [year, month] ); await connection.query( `DELETE FROM isic_2digit_indices WHERE year = ? AND month = ?`, [year, month] ); await connection.query( `DELETE FROM manufacturing_ipi WHERE year = ? AND month = ?`, [year, month] ); await connection.commit(); logger.info(` Data cleared for ${year}-${month} `); return { success: true }; } catch (error) { await connection.rollback(); console.error('Error clearing data:', error); logger.error(` Error clearing data: ${error} `); throw error; } finally { connection.release(); } } // Close pool async close() { await this.pool.end(); } } module.exports = IPICalculationService;