GWM : iip calculation

This commit is contained in:
Gowtham M 2026-04-29 10:39:44 +05:30
parent 5ffb872c3a
commit 7be44b562d
11 changed files with 3049 additions and 316 deletions

View File

@ -94,7 +94,7 @@ exports.calculate_base_year = async (req, res) => {
res.json({
success: true,
message: `Base year ${baseYear} production calculated successfully`,
message: `Base year ${baseYear} production calculated successfully (quarter-based)`,
data: result
});
@ -109,34 +109,32 @@ exports.calculate_base_year = async (req, res) => {
};
// 2. Calculate IPI for Specific Month
// 2. Calculate IPI for Specific Quarter
exports.calculate_month = async (req, res) => {
try {
// const { year, month } = req.body;
const year = 2026;
const month = 3;
const { year, quarter } = req.body;
if (!year || !month) {
if (!year || !quarter) {
return res.status(400).json({
success: false,
message: 'Year and month are required'
message: 'Year and quarter are required'
});
}
if (month < 1 || month > 12) {
if (!['Q1', 'Q2', 'Q3', 'Q4'].includes(quarter)) {
return res.status(400).json({
success: false,
message: 'Month must be between 1 and 12'
message: 'Quarter must be one of Q1, Q2, Q3, Q4'
});
}
const service = new IPICalculationService(dbConfig);
const result = await service.runCompleteCalculation(year, month);
const result = await service.runCompleteCalculation(year, quarter);
await service.close();
res.json({
success: true,
message: `IPI calculated for ${year}-${String(month).padStart(2, '0')}`,
message: `IPI calculated for ${year}-${quarter}`,
data: result
});
@ -151,7 +149,7 @@ exports.calculate_month = async (req, res) => {
};
// 3. Calculate IPI for Multiple Months
// 3. Calculate IPI for quarter
exports.calculate_quarter = async (req, res) => {
try {
@ -164,39 +162,19 @@ exports.calculate_quarter = async (req, res) => {
});
}
// Quarter to month mapping
const quarterMap = {
Q1: { startMonth: 1, endMonth: 3 },
Q2: { startMonth: 4, endMonth: 6 },
Q3: { startMonth: 7, endMonth: 9 },
Q4: { startMonth: 10, endMonth: 12 },
};
const selectedQuarter = quarterMap[quarter];
if (!selectedQuarter) {
if (!['Q1', 'Q2', 'Q3', 'Q4'].includes(quarter)) {
return res.status(400).json({
success: false,
message: 'Invalid quarter. Accepted values: Q1, Q2, Q3, Q4'
});
}
const { startMonth, endMonth } = selectedQuarter;
const service = new IPICalculationService(dbConfig);
const results = [];
for (let month = startMonth; month <= endMonth; month++) {
const result = await service.runCompleteCalculation(year, month);
results.push({
month,
...result
});
}
const result = await service.runCompleteCalculation(year, quarter);
await service.close();
res.json({ success: true, message: `IPI calculated for ${year} months ${startMonth}-${endMonth}`, data: results });
res.json({ success: true, message: `IPI calculated for ${year} ${quarter}`, data: result });
} catch (error) {
console.error('Error:', error);
@ -289,7 +267,7 @@ exports.survey_auto_submit = async (req, res) => {
exports.getCalculationLogsByYearMonth = async (req, res) => {
try {
const { year, month } = req.query;
const { year, quarter, month } = req.query;
if (!year) {
return res.status(400).json({
@ -297,16 +275,18 @@ exports.getCalculationLogsByYearMonth = async (req, res) => {
});
}
if (!month) {
const resolvedQuarter = quarter || (month ? (month <= 3 ? "Q1" : month <= 6 ? "Q2" : month <= 9 ? "Q3" : "Q4") : null);
if (!resolvedQuarter) {
return res.status(400).json({
message: "Month is required",
message: "Quarter (or month to derive quarter) is required",
});
}
const logs = await CalculationLog.findAll({
where: {
reference_year: year,
reference_month: month,
reference_month: resolvedQuarter,
},
order: [["started_at", "DESC"]],
});

View File

@ -37,6 +37,14 @@ const formatNumber = (value) => {
return parsed.toFixed(2);
};
const monthToQuarter = (month) => {
if (month >= 1 && month <= 3) return "Q1";
if (month >= 4 && month <= 6) return "Q2";
if (month >= 7 && month <= 9) return "Q3";
if (month >= 10 && month <= 12) return "Q4";
return null;
};
exports.renderPage = async (req, res) => {
const now = new Date();
const year = Number(req.query.year) || now.getFullYear();
@ -61,7 +69,8 @@ exports.getData = async (req, res) => {
});
}
const whereClause = { year, month };
const quarter = monthToQuarter(month);
const whereClause = { year, quarter };
const [manufacturing, isic2, isic3, isic4, itemCount, logs] = await Promise.all([
ManufacturingIpi.findOne({ where: whereClause, raw: true }),
@ -69,36 +78,21 @@ exports.getData = async (req, res) => {
Isic3DigitIndices.findAll({ where: whereClause, order: [["isic_3digit_code", "ASC"]], raw: true }),
Isic4DigitIndices.findAll({ where: whereClause, order: [["isic_4digit_code", "ASC"]], raw: true }),
db.sequelize.query(
`SELECT COUNT(*) AS total FROM item_level_indices WHERE year = ? AND month = ?`,
{ replacements: [year, month], type: db.Sequelize.QueryTypes.SELECT }
`SELECT COUNT(*) AS total FROM item_level_indices WHERE year = ? AND quarter = ?`,
{ replacements: [year, quarter], type: db.Sequelize.QueryTypes.SELECT }
),
CalculationLog.findAll({
where: { reference_year: year, reference_month: month },
where: { reference_year: year, reference_month: quarter },
order: [["started_at", "DESC"]],
raw: true,
}),
db.sequelize.query(
`SELECT product_id, product_hs_code, base_year, avg_by_production, jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, \`dec\`
FROM base_year_production
WHERE base_year = 2022
ORDER BY product_id
LIMIT 25`,
{ type: db.Sequelize.QueryTypes.SELECT }
),
db.sequelize.query(
`SELECT product_id, product_hs_code, year, month, month_name, production_quantity, unit
FROM monthly_production
WHERE year = ? AND month = ?
ORDER BY product_id
LIMIT 50`,
{ replacements: [year, month], type: db.Sequelize.QueryTypes.SELECT }
),
})
]);
return res.json({
success: true,
year,
month,
quarter,
summary: {
item_count: Number(itemCount[0]?.total || 0),
isic4_count: isic4.length,
@ -149,6 +143,7 @@ exports.getTableData = async (req, res) => {
const table = String(req.query.table || "");
const year = Number(req.query.year);
const month = Number(req.query.month);
const quarter = monthToQuarter(month);
const baseYear = Number(req.query.baseYear) || 2022;
const page = Math.max(1, Number(req.query.page) || 1);
const pageSize = Math.min(200, Math.max(1, Number(req.query.pageSize) || 50));
@ -157,14 +152,14 @@ exports.getTableData = async (req, res) => {
const queryConfig = {
monthly: {
whereSql: `year = ? AND month = ?`,
whereParams: [year, month],
searchSql: search ? ` AND (CAST(product_id AS CHAR) LIKE ? OR CAST(product_hs_code AS CHAR) LIKE ? OR month_name LIKE ?)` : "",
whereSql: `year = ? AND quarter = ?`,
whereParams: [year, quarter],
searchSql: search ? ` AND (CAST(product_id AS CHAR) LIKE ? OR CAST(product_hs_code AS CHAR) LIKE ? OR quarter_name LIKE ?)` : "",
searchParams: search ? [`%${search}%`, `%${search}%`, `%${search}%`] : [],
countSql: `SELECT COUNT(*) AS total FROM monthly_production WHERE year = ? AND month = ?`,
dataSql: `SELECT product_id, product_hs_code, year, month, month_name, production_quantity, unit
countSql: `SELECT COUNT(*) AS total FROM monthly_production WHERE year = ? AND quarter = ?`,
dataSql: `SELECT product_id, product_hs_code, year, quarter, quarter_name, production_quantity, unit
FROM monthly_production
WHERE year = ? AND month = ?`,
WHERE year = ? AND quarter = ?`,
orderSql: ` ORDER BY product_id`,
},
baseyear: {
@ -173,20 +168,20 @@ exports.getTableData = async (req, res) => {
searchSql: search ? ` AND (CAST(product_id AS CHAR) LIKE ? OR CAST(product_hs_code AS CHAR) LIKE ?)` : "",
searchParams: search ? [`%${search}%`, `%${search}%`] : [],
countSql: `SELECT COUNT(*) AS total FROM base_year_production WHERE base_year = ?`,
dataSql: `SELECT product_id, product_hs_code, base_year, avg_by_production, jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, \`dec\`
dataSql: `SELECT product_id, product_hs_code, base_year, avg_by_production, q1, q2, q3, q4
FROM base_year_production
WHERE base_year = ?`,
orderSql: ` ORDER BY product_id`,
},
items: {
whereSql: `year = ? AND month = ?`,
whereParams: [year, month],
whereSql: `year = ? AND quarter = ?`,
whereParams: [year, quarter],
searchSql: search ? ` AND (CAST(product_id AS CHAR) LIKE ? OR CAST(product_hs_code AS CHAR) LIKE ?)` : "",
searchParams: search ? [`%${search}%`, `%${search}%`] : [],
countSql: `SELECT COUNT(*) AS total FROM item_level_indices WHERE year = ? AND month = ?`,
countSql: `SELECT COUNT(*) AS total FROM item_level_indices WHERE year = ? AND quarter = ?`,
dataSql: `SELECT product_id, product_hs_code, current_production, base_year_avg_production, production_relative, item_index
FROM item_level_indices
WHERE year = ? AND month = ?`,
WHERE year = ? AND quarter = ?`,
orderSql: ` ORDER BY product_id`,
},
};
@ -195,7 +190,7 @@ exports.getTableData = async (req, res) => {
return res.status(400).json({ success: false, message: "Invalid table parameter" });
}
if ((table === "monthly" || table === "items") && (!year || !month || month < 1 || month > 12)) {
if ((table === "monthly" || table === "items") && (!year || !month || month < 1 || month > 12 || !quarter)) {
return res.status(400).json({ success: false, message: "Valid year and month are required" });
}
@ -216,7 +211,7 @@ exports.getTableData = async (req, res) => {
const copy = { ...row };
Object.keys(copy).forEach((k) => {
if (
["production_quantity", "avg_by_production", "current_production", "base_year_avg_production", "production_relative", "item_index", "jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec"].includes(k)
["production_quantity", "avg_by_production", "current_production", "base_year_avg_production", "production_relative", "item_index", "q1", "q2", "q3", "q4"].includes(k)
) {
copy[k] = formatNumber(copy[k]);
}
@ -245,8 +240,9 @@ exports.runMonthCalculation = async (req, res) => {
try {
const year = Number(req.body.year);
const month = Number(req.body.month);
const quarter = monthToQuarter(month);
if (!year || !month || month < 1 || month > 12) {
if (!year || !month || month < 1 || month > 12 || !quarter) {
return res.status(400).json({
success: false,
message: "Valid year and month are required",
@ -254,12 +250,12 @@ exports.runMonthCalculation = async (req, res) => {
}
const service = new IPICalculationService(dbConfig);
const result = await service.runCompleteCalculation(year, month);
const result = await service.runCompleteCalculation(year, quarter);
await service.close();
return res.json({
success: true,
message: `Calculation completed for ${year}-${String(month).padStart(2, "0")}`,
message: `Calculation completed for ${year}-${quarter}`,
data: result,
});
} catch (error) {
@ -275,8 +271,9 @@ exports.getRunStatus = async (req, res) => {
try {
const year = Number(req.query.year);
const month = Number(req.query.month);
const quarter = monthToQuarter(month);
if (!year || !month || month < 1 || month > 12) {
if (!year || !month || month < 1 || month > 12 || !quarter) {
return res.status(400).json({
success: false,
message: "Valid year and month are required",
@ -286,7 +283,7 @@ exports.getRunStatus = async (req, res) => {
const logs = await CalculationLog.findAll({
where: {
reference_year: year,
reference_month: month,
reference_month: quarter,
},
order: [["started_at", "ASC"]],
raw: true,
@ -314,6 +311,7 @@ exports.getRunStatus = async (req, res) => {
success: true,
year,
month,
quarter,
started: startedAny,
isFinished,
completedCount,

View File

@ -61,27 +61,27 @@ exports.getAllManufacturingIndexDetails = async (req, res) => {
exports.getManufacturingMonthlyOverviewByYearMonth = async (req, res) => {
try {
const { year, month } = req.query;
const { year, month, quarter } = req.query;
// Validate parameters exist
if (!year || !month) {
if (!year || (!month && !quarter)) {
logger.warn('getManufacturingMonthlyOverview API: Missing required parameters');
return res.status(400).send({
status: "failed",
message: "Year and month are required"
message: "Year and quarter (or month to derive quarter) are required"
});
}
// Sanitize and validate input - prevent SQL injection
const yearInt = parseInt(year, 10);
const monthInt = parseInt(month, 10);
const monthInt = month ? parseInt(month, 10) : null;
// Validate that parsing was successful
if (isNaN(yearInt) || isNaN(monthInt)) {
logger.warn(`getManufacturingMonthlyOverview API: Invalid input - year: ${year}, month: ${month}`);
if (isNaN(yearInt) || (month && isNaN(monthInt))) {
logger.warn(`getManufacturingMonthlyOverview API: Invalid input - year: ${year}, month: ${month}, quarter: ${quarter}`);
return res.status(400).send({
status: "failed",
message: "Year and month must be valid numbers"
message: "Year/month must be valid numbers"
});
}
@ -93,18 +93,25 @@ exports.getManufacturingMonthlyOverviewByYearMonth = async (req, res) => {
});
}
// Validate month range
if (monthInt < 1 || monthInt > 12) {
if (month && (monthInt < 1 || monthInt > 12)) {
return res.status(400).send({
status: "failed",
message: "Month must be between 1 and 12"
});
}
const derivedQuarter = quarter || (monthInt <= 3 ? "Q1" : monthInt <= 6 ? "Q2" : monthInt <= 9 ? "Q3" : "Q4");
if (!["Q1", "Q2", "Q3", "Q4"].includes(derivedQuarter)) {
return res.status(400).send({
status: "failed",
message: "Quarter must be one of Q1, Q2, Q3, Q4"
});
}
// Common where clause
const whereClause = {
year: yearInt,
month: monthInt
quarter: derivedQuarter
};
// Fetch data from all tables in parallel
@ -132,7 +139,7 @@ exports.getManufacturingMonthlyOverviewByYearMonth = async (req, res) => {
// Check if manufacturing data exists (main data)
if (!manufacturingData) {
logger.info(`getManufacturingMonthlyOverview API: No data found for year ${yearInt}, month ${monthInt}`);
logger.info(`getManufacturingMonthlyOverview API: No data found for year ${yearInt}, quarter ${derivedQuarter}`);
return res.status(404).send({
status: "failed",
message: "No data found for the specified period"
@ -183,7 +190,7 @@ exports.getManufacturingMonthlyOverviewByYearMonth = async (req, res) => {
};
});
logger.info(`getManufacturingMonthlyOverview API: Successfully fetched data for year ${yearInt}, month ${monthInt}`);
logger.info(`getManufacturingMonthlyOverview API: Successfully fetched data for year ${yearInt}, quarter ${derivedQuarter}`);
res.status(200).send({
status: "success",

View File

@ -22,11 +22,11 @@ module.exports = (sequelize, DataTypes) => {
type: DataTypes.INTEGER,
allowNull: false,
},
month: {
type: DataTypes.INTEGER,
quarter: {
type: DataTypes.STRING,
allowNull: false,
},
month_name: {
quarter_name: {
type: DataTypes.STRING,
allowNull: false,
},

View File

@ -22,11 +22,11 @@ module.exports = (sequelize, DataTypes) => {
type: DataTypes.INTEGER,
allowNull: false,
},
month: {
type: DataTypes.INTEGER,
quarter: {
type: DataTypes.STRING,
allowNull: false,
},
month_name: {
quarter_name: {
type: DataTypes.STRING,
allowNull: false,
},

View File

@ -22,11 +22,11 @@ module.exports = (sequelize, DataTypes) => {
type: DataTypes.INTEGER,
allowNull: false,
},
month: {
type: DataTypes.INTEGER,
quarter: {
type: DataTypes.STRING,
allowNull: false,
},
month_name: {
quarter_name: {
type: DataTypes.STRING,
allowNull: false,
},

View File

@ -9,11 +9,11 @@ module.exports = (sequelize, DataTypes) => {
type: DataTypes.INTEGER,
allowNull: false,
},
month: {
type: DataTypes.INTEGER,
quarter: {
type: DataTypes.STRING,
allowNull: false,
},
month_name: {
quarter_name: {
type: DataTypes.STRING,
allowNull: false,
},

View File

@ -10,7 +10,7 @@ class IPICalculationService {
}
// STEP 1: Calculate and Store Base Year Average Production (2022)
// STEP 1: Calculate and Store Base Year Average Production (Quarter-based)
async calculateBaseYearProduction(baseYear = 2022, forceRecalculate = false) {
const connection = await this.pool.getConnection();
@ -33,38 +33,22 @@ class IPICalculationService {
// Log calculation start
logId = await this.logRecord( 'base_year', baseYear, null, 'Started', null, 0, null, connection);
// Your existing query to calculate base year production
// Quarter-based base year production using submission_products.current_quantity
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\`,
SUM(CASE WHEN s.quarter = 'Q1' THEN COALESCE(sp.current_quantity, 0) END) AS q1,
SUM(CASE WHEN s.quarter = 'Q2' THEN COALESCE(sp.current_quantity, 0) END) AS q2,
SUM(CASE WHEN s.quarter = 'Q3' THEN COALESCE(sp.current_quantity, 0) END) AS q3,
SUM(CASE WHEN s.quarter = 'Q4' THEN COALESCE(sp.current_quantity, 0) END) AS q4,
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
COALESCE(SUM(CASE WHEN s.quarter='Q1' THEN sp.current_quantity END),0) +
COALESCE(SUM(CASE WHEN s.quarter='Q2' THEN sp.current_quantity END),0) +
COALESCE(SUM(CASE WHEN s.quarter='Q3' THEN sp.current_quantity END),0) +
COALESCE(SUM(CASE WHEN s.quarter='Q4' THEN sp.current_quantity END),0)
) / 4, 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
@ -84,15 +68,11 @@ class IPICalculationService {
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_id, product_hs_code, base_year, unit, q1, q2, q3, q4, 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.q1 || 0, product.q2 || 0, product.q3 || 0, product.q4 || 0,
product.avg_by_production
]
);
@ -128,38 +108,23 @@ class IPICalculationService {
}
// STEP 2: Aggregate Monthly Production
async aggregateMonthlyProduction(year, month) {
// STEP 2: Aggregate Quarterly Production
async aggregateQuarterlyProduction(year, quarter) {
const connection = await this.pool.getConnection();
let logId = null;
try {
await connection.beginTransaction();
logger.info(`aggregateMonthlyProduction - Aggregating production for ${year}-${month}...`);
logger.info(`aggregateQuarterlyProduction - Aggregating production for ${year}-${quarter}...`);
// Log calculation start
logId = await this.logRecord( 'monthly_production', year, month, 'Started', null, 0, null, connection);
logId = await this.logRecord('monthly_production', year, quarter, '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'];
const validQuarters = new Set(['Q1', 'Q2', 'Q3', 'Q4']);
if (!validQuarters.has(quarter)) {
throw new Error('Invalid quarter. Accepted values: Q1, Q2, Q3, Q4');
}
// Aggregate production by product
const query = `
@ -167,7 +132,7 @@ class IPICalculationService {
p.id AS product_id,
p.hs_code AS product_hs_code,
u.uom_short_name AS unit,
SUM(sp.${period}) AS production_quantity
SUM(COALESCE(sp.current_quantity, 0)) 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
@ -184,34 +149,35 @@ class IPICalculationService {
for (const product of products) {
await connection.query(
`INSERT INTO monthly_production
(product_id, product_hs_code, year, month, month_name,
(product_id, product_hs_code, year, quarter, quarter_name,
production_quantity, unit)
VALUES (?, ?, ?, ?, ?, ?, ?)
ON DUPLICATE KEY UPDATE
production_quantity=VALUES(production_quantity),
quarter_name=VALUES(quarter_name),
unit=VALUES(unit)`,
[
product.product_id, product.product_hs_code, year, month,
monthNames[month], product.production_quantity || 0, product.unit
product.product_id, product.product_hs_code, year, quarter,
quarter, product.production_quantity || 0, product.unit
]
);
}
// Update log
await this.logRecord('monthly_production', year, month, 'Completed', logId, products.length, null,connection);
await this.logRecord('monthly_production', year, quarter, 'Completed', logId, products.length, null, connection);
await connection.commit();
logger.info(`aggregateMonthlyProduction - Monthly production aggregated: ${products.length} products`);
logger.info(`aggregateQuarterlyProduction - Quarterly 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}`);
logger.error(`aggregateQuarterlyProduction - Error aggregating quarterly production: ${error}`);
// Update log OUTSIDE the failed transaction
if (logId) {
await this.logRecord('monthly_production', year, month, 'Failed', logId, 0, error.message);
await this.logRecord('monthly_production', year, quarter, 'Failed', logId, 0, error.message);
}
throw error;
} finally {
@ -221,18 +187,16 @@ class IPICalculationService {
// STEP 3: Calculate Item Level Indices
async calculateItemLevelIndices(year, month) {
async calculateItemLevelIndices(year, quarter) {
const connection = await this.pool.getConnection();
let logId = null;
try {
await connection.beginTransaction();
logger.info(`calculateItemLevelIndices - Calculating item level indices for ${year}-${month}...`);
logger.info(`calculateItemLevelIndices - Calculating item level indices for ${year}-${quarter}...`);
// 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'];
logId = await this.logRecord('item_level_indices', year, quarter, 'Started', null, 0, null, connection);
// Calculate indices: Ri = Current Production / Base Year Avg Production
// Ii = Ri × 100
@ -246,17 +210,17 @@ class IPICalculationService {
((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 = ?
WHERE mp.year = ? AND mp.quarter = ?
AND byp.avg_by_production > 0
`;
const [items] = await connection.query(query, [year, month]);
const [items] = await connection.query(query, [year, quarter]);
// 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,
(product_id, product_hs_code, year, quarter, quarter_name,
current_production, base_year_avg_production,
production_relative, item_index)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
@ -266,7 +230,7 @@ class IPICalculationService {
production_relative=VALUES(production_relative),
item_index=VALUES(item_index)`,
[
item.product_id, item.product_hs_code, year, month, monthNames[month],
item.product_id, item.product_hs_code, year, quarter, quarter,
item.current_production, item.base_year_avg_production,
item.production_relative, item.item_index
]
@ -274,7 +238,7 @@ class IPICalculationService {
}
// Update log
await this.logRecord('item_level_indices', year, month, 'Completed', logId, items.length, null,connection);
await this.logRecord('item_level_indices', year, quarter, 'Completed', logId, items.length, null, connection);
await connection.commit();
logger.info(`calculateItemLevelIndices - Item level indices calculated: ${items.length} items`);
@ -286,7 +250,7 @@ class IPICalculationService {
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);
await this.logRecord('item_level_indices', year, quarter, 'Failed', logId, 0, error.message);
}
throw error;
} finally {
@ -296,52 +260,65 @@ class IPICalculationService {
// STEP 4: Calculate ISIC 4-Digit Level Indices
async calculateISIC4DigitIndices(year, month) {
async calculateISIC4DigitIndices(year, quarter) {
const connection = await this.pool.getConnection();
let logId = null;
try {
await connection.beginTransaction();
logger.info(`calculateISIC4DigitIndices - Calculating ISIC 4-digit indices for ${year}-${month}...`);
logger.info(`calculateISIC4DigitIndices - Calculating ISIC 4-digit indices for ${year}-${quarter}...`);
// Log calculation start
logId = await this.logRecord( 'isic_4digit_indices', year, month, 'Started', null, 0, null, connection);
logId = await this.logRecord('isic_4digit_indices', year, quarter, 'Started', null, 0, null, connection);
const monthNames = ['', 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
// ISIC mapping source is now products.isic_code.
// Aggregate weighted item indices to ISIC 4-digit using product master mapping.
// ISIC 4-digit grouping uses establishments.isic_code (via approved submissions).
// One establishment per product-period is chosen (highest current_quantity, then submission id)
// so item_index and weight_in_ib are not multiplied when several establishments report the same product.
const query = `
WITH product_isic_from_establishment AS (
SELECT
ili.product_id,
ili.item_index,
LEFT(TRIM(CAST(e.isic_code AS CHAR)), 4) AS isic_4digit_code,
ROW_NUMBER() OVER (
PARTITION BY ili.product_id
ORDER BY COALESCE(sp.current_quantity, 0) DESC, s.id ASC
) AS rn
FROM item_level_indices ili
JOIN submission_products sp ON sp.product_id = ili.product_id
JOIN submission s ON s.id = sp.submission_id
AND s.year = ?
AND s.quarter = ?
AND s.status = 'Approved'
JOIN establishments e ON e.id = s.establishment_id
WHERE ili.year = ?
AND ili.quarter = ?
AND sp.is_active = 1
AND e.isic_code IS NOT NULL
AND TRIM(CAST(e.isic_code AS CHAR)) <> ''
)
SELECT
LEFT(CAST(p.isic_code AS CHAR), 4) AS isic_4digit_code,
pie.isic_4digit_code,
SUM(COALESCE(p.weight_in_ib, 0)) AS total_weight,
SUM(
COALESCE(p.weight_in_ib, 0) *
ili.item_index
) AS weighted_index_sum,
SUM(COALESCE(p.weight_in_ib, 0) * pie.item_index) AS weighted_index_sum,
(
SUM(
COALESCE(p.weight_in_ib, 0) *
ili.item_index
) /
SUM(COALESCE(p.weight_in_ib, 0) * pie.item_index) /
NULLIF(SUM(COALESCE(p.weight_in_ib, 0)), 0)
) AS isic_4digit_index
FROM item_level_indices ili
JOIN products p ON p.id = ili.product_id
WHERE ili.year = ?
AND ili.month = ?
AND p.isic_code IS NOT NULL
FROM product_isic_from_establishment pie
JOIN products p ON p.id = pie.product_id
WHERE pie.rn = 1
AND p.weight_in_ib IS NOT NULL
AND p.weight_in_ib > 0
GROUP BY LEFT(CAST(p.isic_code AS CHAR), 4)
GROUP BY pie.isic_4digit_code
`;
const [isic4Results] = await connection.query(query, [year, month]);
const [isic4Results] = await connection.query(query, [year, quarter, year, quarter]);
for (const result of isic4Results) {
await connection.query(
`INSERT INTO isic_4digit_indices
(isic_4digit_code, year, month, month_name, total_weight,
(isic_4digit_code, year, quarter, quarter_name, total_weight,
weighted_index_sum, isic_4digit_index)
VALUES (?, ?, ?, ?, ?, ?, ?)
ON DUPLICATE KEY UPDATE
@ -349,7 +326,7 @@ class IPICalculationService {
weighted_index_sum=VALUES(weighted_index_sum),
isic_4digit_index=VALUES(isic_4digit_index)`,
[
result.isic_4digit_code, year, month, monthNames[month],
result.isic_4digit_code, year, quarter, quarter,
result.total_weight,
result.weighted_index_sum,
result.isic_4digit_index
@ -358,7 +335,7 @@ class IPICalculationService {
}
// Update log
await this.logRecord('isic_4digit_indices', year, month, 'Completed', logId, isic4Results.length, null,connection);
await this.logRecord('isic_4digit_indices', year, quarter, 'Completed', logId, isic4Results.length, null, connection);
await connection.commit();
logger.info(`calculateISIC4DigitIndices - ISIC 4-digit indices calculated: ${isic4Results.length} codes`);
@ -370,7 +347,7 @@ class IPICalculationService {
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);
await this.logRecord('isic_4digit_indices', year, quarter, 'Failed', logId, 0, error.message);
}
throw error;
} finally {
@ -380,18 +357,16 @@ class IPICalculationService {
// STEP 5: Calculate ISIC 3-Digit Level Indices
async calculateISIC3DigitIndices(year, month) {
async calculateISIC3DigitIndices(year, quarter) {
const connection = await this.pool.getConnection();
let logId = null;
try {
await connection.beginTransaction();
logger.info(`calculateISIC3DigitIndices - Calculating ISIC 3-digit indices for ${year}-${month}...`);
logger.info(`calculateISIC3DigitIndices - Calculating ISIC 3-digit indices for ${year}-${quarter}...`);
// 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'];
logId = await this.logRecord('isic_3digit_indices', year, quarter, 'Started', null, 0, null, connection);
const query = `
SELECT
@ -400,16 +375,16 @@ class IPICalculationService {
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 = ?
WHERE year = ? AND quarter = ?
GROUP BY LEFT(isic_4digit_code, 3)
`;
const [isic3Results] = await connection.query(query, [year, month]);
const [isic3Results] = await connection.query(query, [year, quarter]);
for (const result of isic3Results) {
await connection.query(
`INSERT INTO isic_3digit_indices
(isic_3digit_code, year, month, month_name, total_weight,
(isic_3digit_code, year, quarter, quarter_name, total_weight,
weighted_index_sum, isic_3digit_index)
VALUES (?, ?, ?, ?, ?, ?, ?)
ON DUPLICATE KEY UPDATE
@ -417,7 +392,7 @@ class IPICalculationService {
weighted_index_sum=VALUES(weighted_index_sum),
isic_3digit_index=VALUES(isic_3digit_index)`,
[
result.isic_3digit_code, year, month, monthNames[month],
result.isic_3digit_code, year, quarter, quarter,
result.total_weight, result.weighted_index_sum,
result.isic_3digit_index
]
@ -425,7 +400,7 @@ class IPICalculationService {
}
// Update log
await this.logRecord('isic_3digit_indices', year, month, 'Completed', logId, isic3Results.length, null,connection);
await this.logRecord('isic_3digit_indices', year, quarter, 'Completed', logId, isic3Results.length, null, connection);
await connection.commit();
logger.info(`calculateISIC3DigitIndices - ISIC 3-digit indices calculated: ${isic3Results.length} codes`);
@ -437,7 +412,7 @@ class IPICalculationService {
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);
await this.logRecord('isic_3digit_indices', year, quarter, 'Failed', logId, 0, error.message);
}
throw error;
} finally {
@ -447,18 +422,16 @@ class IPICalculationService {
// STEP 6: Calculate ISIC 2-Digit Level Indices
async calculateISIC2DigitIndices(year, month) {
async calculateISIC2DigitIndices(year, quarter) {
const connection = await this.pool.getConnection();
let logId = null;
try {
await connection.beginTransaction();
logger.info(`calculateISIC2DigitIndices - Calculating ISIC 2-digit indices for ${year}-${month}...`);
logger.info(`calculateISIC2DigitIndices - Calculating ISIC 2-digit indices for ${year}-${quarter}...`);
// 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'];
logId = await this.logRecord('isic_2digit_indices', year, quarter, 'Started', null, 0, null, connection);
const query = `
SELECT
@ -467,16 +440,16 @@ class IPICalculationService {
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 = ?
WHERE year = ? AND quarter = ?
GROUP BY LEFT(isic_3digit_code, 2)
`;
const [isic2Results] = await connection.query(query, [year, month]);
const [isic2Results] = await connection.query(query, [year, quarter]);
for (const result of isic2Results) {
await connection.query(
`INSERT INTO isic_2digit_indices
(isic_2digit_code, year, month, month_name, total_weight,
(isic_2digit_code, year, quarter, quarter_name, total_weight,
weighted_index_sum, isic_2digit_index)
VALUES (?, ?, ?, ?, ?, ?, ?)
ON DUPLICATE KEY UPDATE
@ -484,7 +457,7 @@ class IPICalculationService {
weighted_index_sum=VALUES(weighted_index_sum),
isic_2digit_index=VALUES(isic_2digit_index)`,
[
result.isic_2digit_code, year, month, monthNames[month],
result.isic_2digit_code, year, quarter, quarter,
result.total_weight, result.weighted_index_sum,
result.isic_2digit_index
]
@ -492,7 +465,7 @@ class IPICalculationService {
}
// Update log
await this.logRecord('isic_2digit_indices', year, month, 'Completed', logId, isic2Results.length, null,connection);
await this.logRecord('isic_2digit_indices', year, quarter, 'Completed', logId, isic2Results.length, null, connection);
await connection.commit();
logger.info(`calculateISIC2DigitIndices - ISIC 2-digit indices calculated: ${isic2Results.length} codes`);
@ -504,7 +477,7 @@ class IPICalculationService {
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);
await this.logRecord('isic_2digit_indices', year, quarter, 'Failed', logId, 0, error.message);
}
throw error;
} finally {
@ -514,18 +487,16 @@ class IPICalculationService {
// STEP 7: Calculate Manufacturing IPI (Headline Index)
async calculateManufacturingIPI(year, month) {
async calculateManufacturingIPI(year, quarter) {
const connection = await this.pool.getConnection();
let logId = null;
try {
await connection.beginTransaction();
logger.info(`calculateManufacturingIPI - Calculating Manufacturing indices for ${year}-${month}...`);
logger.info(`calculateManufacturingIPI - Calculating Manufacturing indices for ${year}-${quarter}...`);
// 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'];
logId = await this.logRecord('manufacturing_ipi', year, quarter, 'Started', null, 0, null, connection);
// Calculate Manufacturing IPI
const query = `
@ -534,10 +505,10 @@ class IPICalculationService {
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 = ?
WHERE year = ? AND quarter = ?
`;
const [result] = await connection.query(query, [year, month]);
const [result] = await connection.query(query, [year, quarter]);
// If no data found (or aggregate returns NULLs), skip insert to avoid NOT NULL DB errors
if (
@ -547,47 +518,50 @@ class IPICalculationService {
result[0].weighted_index_sum == null ||
result[0].manufacturing_index == null
) {
await this.logRecord('manufacturing_ipi', year, month, 'Completed', logId, 0, null, connection);
await this.logRecord('manufacturing_ipi', year, quarter, 'Completed', logId, 0, null, connection);
await connection.commit();
logger.info(`calculateManufacturingIPI - No valid ISIC 2-digit aggregates found for ${year}-${month}`);
logger.info(`calculateManufacturingIPI - No valid ISIC 2-digit aggregates found for ${year}-${quarter}`);
return {
success: true,
index: null,
momChange: null,
qoqChange: null,
momChange: null,
yoyChange: null,
message: "No IPI data found for the given year and month"
message: "No IPI data found for the given year and quarter"
};
}
const ipiData = result[0];
// Calculate MoM and YoY changes
let momChange = null;
// Calculate QoQ and YoY changes
let qoqChange = null;
let yoyChange = null;
// Get previous month index for MoM
const prevMonth = month === 1 ? 12 : month - 1;
const prevYear = month === 1 ? year - 1 : year;
// Previous quarter (QoQ)
const quarterOrder = ['Q1', 'Q2', 'Q3', 'Q4'];
const quarterIdx = quarterOrder.indexOf(quarter);
const prevQuarter = quarterIdx === 0 ? 'Q4' : quarterOrder[quarterIdx - 1];
const prevYear = quarterIdx === 0 ? year - 1 : year;
const [prevMonthData] = await connection.query(
const [prevQuarterData] = await connection.query(
`SELECT manufacturing_index FROM manufacturing_ipi
WHERE year = ? AND month = ?`,
[prevYear, prevMonth]
WHERE year = ? AND quarter = ?`,
[prevYear, prevQuarter]
);
if (prevMonthData.length > 0 && prevMonthData[0].manufacturing_index) {
const prevIndex = prevMonthData[0].manufacturing_index;
if (prevQuarterData.length > 0 && prevQuarterData[0].manufacturing_index) {
const prevIndex = prevQuarterData[0].manufacturing_index;
if (Number(prevIndex) !== 0) {
momChange = ((ipiData.manufacturing_index - prevIndex) / prevIndex) * 100;
qoqChange = ((ipiData.manufacturing_index - prevIndex) / prevIndex) * 100;
}
}
// Get same month last year for YoY
// Get same quarter last year for YoY
const [lastYearData] = await connection.query(
`SELECT manufacturing_index FROM manufacturing_ipi
WHERE year = ? AND month = ?`,
[year - 1, month]
WHERE year = ? AND quarter = ?`,
[year - 1, quarter]
);
if (lastYearData.length > 0 && lastYearData[0].manufacturing_index) {
@ -597,13 +571,13 @@ class IPICalculationService {
}
}
// Reference date is the first day of the month
const referenceDate = `${year}-${String(month).padStart(2, '0')}-01`;
const quarterStartMonth = { Q1: '01', Q2: '04', Q3: '07', Q4: '10' };
const referenceDate = `${year}-${quarterStartMonth[quarter]}-01`;
// Insert Manufacturing IPI
await connection.query(
`INSERT INTO manufacturing_ipi
(year, month, month_name, reference_date, total_weight,
(year, quarter, quarter_name, reference_date, total_weight,
weighted_index_sum, manufacturing_index, mom_change, yoy_change, status, generated_on)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 'Completed',?)
ON DUPLICATE KEY UPDATE
@ -615,27 +589,28 @@ class IPICalculationService {
status=VALUES(status),
generated_on = VALUES(generated_on)`,
[
year, month, monthNames[month], referenceDate,
year, quarter, quarter, referenceDate,
ipiData.total_weight, ipiData.weighted_index_sum,
ipiData.manufacturing_index, momChange, yoyChange , new Date()
ipiData.manufacturing_index, qoqChange, yoyChange , new Date()
]
);
// Update log
await this.logRecord('manufacturing_ipi', year, month, 'Completed', logId, result.length, null,connection);
await this.logRecord('manufacturing_ipi', year, quarter, '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 - QoQ: ${qoqChange ? qoqChange.toFixed(2) + '%' : 'N/A'}`);
logger.info(`calculateManufacturingIPI - YoY: ${yoyChange ? yoyChange.toFixed(2) + '%' : 'N/A'}`);
return {
success: true,
index: ipiData.manufacturing_index,
momChange,
qoqChange,
momChange: qoqChange,
yoyChange
};
@ -645,7 +620,7 @@ class IPICalculationService {
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);
await this.logRecord('manufacturing_ipi', year, quarter, 'Failed', logId, 0, error.message);
}
throw error;
} finally {
@ -655,28 +630,28 @@ class IPICalculationService {
// MASTER FUNCTION: Run Complete Calculation Pipeline
async runCompleteCalculation(year, month) {
async runCompleteCalculation(year, quarter) {
logger.info(`**************************** Starting IPI Calculation for ${year}-${month} ****************************`);
logger.info(`**************************** Starting IPI Calculation for ${year}-${quarter} ****************************`);
try {
// Step 1: Aggregate monthly production
await this.aggregateMonthlyProduction(year, month);
// Step 1: Aggregate quarterly production
await this.aggregateQuarterlyProduction(year, quarter);
// Step 2: Calculate item level indices
await this.calculateItemLevelIndices(year, month);
await this.calculateItemLevelIndices(year, quarter);
// Step 3: Calculate ISIC 4-digit indices
await this.calculateISIC4DigitIndices(year, month);
await this.calculateISIC4DigitIndices(year, quarter);
// Step 4: Calculate ISIC 3-digit indices
await this.calculateISIC3DigitIndices(year, month);
await this.calculateISIC3DigitIndices(year, quarter);
// Step 5: Calculate ISIC 2-digit indices
await this.calculateISIC2DigitIndices(year, month);
await this.calculateISIC2DigitIndices(year, quarter);
// Step 6: Calculate Manufacturing IPI
const result = await this.calculateManufacturingIPI(year, month);
const result = await this.calculateManufacturingIPI(year, quarter);
logger.info(`**************************** IPI Calculation Completed Successfully ****************************`);
@ -746,7 +721,7 @@ async logRecord(calculation_type, reference_year, reference_month = null, status
product_id,
product_hs_code,
avg_by_production,
jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, \`dec\`
q1, q2, q3, q4
FROM base_year_production
WHERE base_year = ?
ORDER BY product_id
@ -780,46 +755,46 @@ async logRecord(calculation_type, reference_year, reference_month = null, status
// HELPER: Clear All Calculated Data for a Period
async clearCalculatedData(year, month) {
async clearCalculatedData(year, quarter) {
const connection = await this.pool.getConnection();
try {
await connection.beginTransaction();
logger.info(` Clearing calculated data for ${year}-${month} `);
logger.info(` Clearing calculated data for ${year}-${quarter} `);
await connection.query(
`DELETE FROM monthly_production WHERE year = ? AND month = ?`,
[year, month]
`DELETE FROM monthly_production WHERE year = ? AND quarter = ?`,
[year, quarter]
);
await connection.query(
`DELETE FROM item_level_indices WHERE year = ? AND month = ?`,
[year, month]
`DELETE FROM item_level_indices WHERE year = ? AND quarter = ?`,
[year, quarter]
);
await connection.query(
`DELETE FROM isic_4digit_indices WHERE year = ? AND month = ?`,
[year, month]
`DELETE FROM isic_4digit_indices WHERE year = ? AND quarter = ?`,
[year, quarter]
);
await connection.query(
`DELETE FROM isic_3digit_indices WHERE year = ? AND month = ?`,
[year, month]
`DELETE FROM isic_3digit_indices WHERE year = ? AND quarter = ?`,
[year, quarter]
);
await connection.query(
`DELETE FROM isic_2digit_indices WHERE year = ? AND month = ?`,
[year, month]
`DELETE FROM isic_2digit_indices WHERE year = ? AND quarter = ?`,
[year, quarter]
);
await connection.query(
`DELETE FROM manufacturing_ipi WHERE year = ? AND month = ?`,
[year, month]
`DELETE FROM manufacturing_ipi WHERE year = ? AND quarter = ?`,
[year, quarter]
);
await connection.commit();
logger.info(` Data cleared for ${year}-${month} `);
logger.info(` Data cleared for ${year}-${quarter} `);
return { success: true };

View File

@ -107,31 +107,12 @@ class AutomatedSchedulerService {
console.log(`[${new Date().toISOString()}] Starting calculate_quarter for ${year} ${quarter}`);
try {
// Quarter to month mapping
const quarterMap = {
Q1: { startMonth: 1, endMonth: 3 },
Q2: { startMonth: 4, endMonth: 6 },
Q3: { startMonth: 7, endMonth: 9 },
Q4: { startMonth: 10, endMonth: 12 },
};
const selectedQuarter = quarterMap[quarter];
if (!selectedQuarter) {
if (!['Q1', 'Q2', 'Q3', 'Q4'].includes(quarter)) {
throw new Error('Invalid quarter. Accepted values: Q1, Q2, Q3, Q4');
}
const { startMonth, endMonth } = selectedQuarter;
const service = new IPICalculationService(dbConfig);
const results = [];
for (let month = startMonth; month <= endMonth; month++) {
const result = await service.runCompleteCalculation(year, month);
results.push({
month,
...result
});
}
const result = await service.runCompleteCalculation(year, quarter);
await service.close();
@ -139,8 +120,8 @@ class AutomatedSchedulerService {
return {
success: true,
message: `IPI calculated for ${year} months ${startMonth}-${endMonth}`,
data: results
message: `IPI calculated for ${year} ${quarter}`,
data: result
};
} catch (error) {
console.error(`[${new Date().toISOString()}] Error in calculate_quarter:`, error);

View File

@ -44,7 +44,7 @@
<div class="wrap">
<div class="card">
<h2 style="margin:0 0 8px 0;">IPI Calculation Demo Dashboard</h2>
<div class="muted">Use Swagger to trigger calculation first, then view data here by year/month.</div>
<div class="muted">Use Swagger to trigger calculation first, then view data by year + month (month is mapped to quarter).</div>
<div class="row" style="margin-top:12px;">
<label>Year <input id="year" type="number" value="<%= defaultYear %>" min="2000" max="2100" /></label>
<label>Month <input id="month" type="number" value="<%= defaultMonth %>" min="1" max="12" /></label>
@ -252,8 +252,8 @@
{ key: "product_id", label: "Product ID" },
{ key: "product_hs_code", label: "HS Code" },
{ key: "year", label: "Year" },
{ key: "month", label: "Month" },
{ key: "month_name", label: "Month Name" },
{ key: "quarter", label: "Quarter" },
{ key: "quarter_name", label: "Quarter Name" },
{ key: "production_quantity", label: "Production Qty" },
{ key: "unit", label: "Unit" },
];
@ -264,10 +264,10 @@
{ key: "product_hs_code", label: "HS Code" },
{ key: "base_year", label: "Base Year" },
{ key: "avg_by_production", label: "Avg Base Year Prod." },
{ key: "jan", label: "Jan" }, { key: "feb", label: "Feb" }, { key: "mar", label: "Mar" },
{ key: "apr", label: "Apr" }, { key: "may", label: "May" }, { key: "jun", label: "Jun" },
{ key: "jul", label: "Jul" }, { key: "aug", label: "Aug" }, { key: "sep", label: "Sep" },
{ key: "oct", label: "Oct" }, { key: "nov", label: "Nov" }, { key: "dec", label: "Dec" },
{ key: "q1", label: "Q1" },
{ key: "q2", label: "Q2" },
{ key: "q3", label: "Q3" },
{ key: "q4", label: "Q4" },
];
}
return [

File diff suppressed because one or more lines are too long