fcsc_ipi_backend/app/controllers/calculation.controller.js

399 lines
12 KiB
JavaScript

const db = require("../models");
const logger = require("../services/logger");
const IPICalculationService = require("../services/ipi_calculation_service");
const SubmissionAutoFillService = require("../services/auto_fill_missing_quarterly_submissions_service");
const CalculationLog = db.CalculationLog;
// Database configuration
const dbConfig = {
host: process.env.DB_HOST,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME,
dialect: process.env.DB_DIALECT || 'mysql',
pool: {
max: 5,
min: 0,
acquire: 30000,
idle: 10000
}
};
// SELECT
// p.id AS product_id,
// p.hs_code AS product_hs_code,
// s.year,
// u.uom_short_name AS unit,
// -- Q1
// 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,
// -- Q2
// 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,
// -- Q3
// 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,
// -- Q4
// 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`,
// -- Average (A.M.) Base Year Production
// 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 -- if you have units table
// WHERE
// s.year = 2022 -- base year
// AND sp.is_active = 1
// AND s.status = 'Approved' -- optional, recommended
// GROUP BY
// p.id;
// 1. Calculate Base Year Production (Run once for base year 2022)
exports.calculate_base_year = async (req, res) => {
try {
const { baseYear = 2022, forceRecalculate = false } = req.body;
const service = new IPICalculationService(dbConfig);
const result = await service.calculateBaseYearProduction(baseYear , forceRecalculate);
await service.close();
res.json({
success: true,
message: `Base year ${baseYear} production calculated successfully (quarter-based)`,
data: result
});
} catch (error) {
console.error('Error:', error);
res.status(500).json({
success: false,
message: 'Error calculating base year production',
error: error.message
});
}
};
// 2. Calculate IPI for Specific Quarter
exports.calculate_month = async (req, res) => {
try {
const { year, quarter } = req.body;
if (!year || !quarter) {
return res.status(400).json({
success: false,
message: 'Year and quarter are required'
});
}
if (!['Q1', 'Q2', 'Q3', 'Q4'].includes(quarter)) {
return res.status(400).json({
success: false,
message: 'Quarter must be one of Q1, Q2, Q3, Q4'
});
}
// check if the year and quarter window created or not and start the calculation
const window = await QuarterlyWindowsConfiguration.findOne({
where: {
year: year,
quarter: quarter
}
});
if (!window) {
return res.status(400).json({
success: false,
message: 'Year and quarter window not created'
});
}
const service = new IPICalculationService(dbConfig);
const result = await service.runCompleteCalculation(year, quarter);
await service.close();
res.json({
success: true,
message: `IPI calculated for ${year}-${quarter}`,
data: result
});
} catch (error) {
console.error('Error:', error);
res.status(500).json({
success: false,
message: 'Error calculating IPI',
error: error.message
});
}
};
// 3. Calculate IPI for quarter
exports.calculate_quarter = async (req, res) => {
try {
const { year, quarter } = req.body;
if (!year || !quarter) {
return res.status(400).json({
success: false,
message: 'Year and quarter are required'
});
}
if (!['Q1', 'Q2', 'Q3', 'Q4'].includes(quarter)) {
return res.status(400).json({
success: false,
message: 'Invalid quarter. Accepted values: Q1, Q2, Q3, Q4'
});
}
const service = new IPICalculationService(dbConfig);
const result = await service.runCompleteCalculation(year, quarter);
await service.close();
res.json({ success: true, message: `IPI calculated for ${year} ${quarter}`, data: result });
} catch (error) {
console.error('Error:', error);
res.status(500).json({
success: false,
message: 'Error calculating IPI range',
error: error.message
});
}
};
// 4. Get Calculation Status/Log
exports.calculation_log = async (req, res) => {
try {
const { year, month, type } = req.query;
const service = new IPICalculationService(dbConfig);
const connection = await service.pool.getConnection();
let query = `
SELECT *
FROM calculation_log
WHERE 1=1
`;
const params = [];
if (year) {
query += ` AND reference_year = ?`;
params.push(year);
}
if (month) {
query += ` AND reference_month = ?`;
params.push(month);
}
if (type) {
query += ` AND calculation_type = ?`;
params.push(type);
}
query += ` ORDER BY started_at DESC LIMIT 100`;
const [results] = await connection.query(query, params);
connection.release();
await service.close();
res.json({ success: true, data: results});
} catch (error) {
console.error('Error:', error);
res.status(500).json({ success: false, message: 'Error fetching calculation log', error: error.message });
}
};
const validateYearQuarter = (year, quarter) => {
if (!year || !quarter) {
return { ok: false, message: "Year and quarter are required" };
}
if (!["Q1", "Q2", "Q3", "Q4"].includes(quarter)) {
return {
ok: false,
message: "Invalid quarter. Accepted values: Q1, Q2, Q3, Q4",
};
}
return { ok: true };
};
async function runSurveyAutoSubmit(year, quarter) {
const autoSubmissionService = new SubmissionAutoFillService(dbConfig);
const autoFillMissingSubmissions =
await autoSubmissionService.autoFillMissingSubmissions(year, quarter);
const verifyCompleteness =
await autoSubmissionService.verifyCompleteness(year, quarter);
return { autoFillMissingSubmissions, verifyCompleteness };
}
async function runQuarterCalculation(year, quarter) {
const service = new IPICalculationService(dbConfig);
try {
const data = await service.runCompleteCalculation(year, quarter);
return { message: `IPI calculated for ${year} ${quarter}`, data };
} finally {
await service.close();
}
}
// 5. Survey Auto Submit
exports.survey_auto_submit = async (req, res) => {
try {
const { year, quarter } = req.body;
const validation = validateYearQuarter(year, quarter);
if (!validation.ok) {
return res.status(400).json({ success: false, message: validation.message });
}
const result = await runSurveyAutoSubmit(year, quarter);
res.json({ success: true, ...result });
} catch (error) {
console.error("Error:", error);
res.status(500).json({
success: false,
message: "Error during survey auto submit",
error: error.message,
});
}
};
// 6. Auto submit missing surveys, then run quarterly IPI calculation
exports.survey_auto_submit_and_calculate = async (req, res) => {
try {
const { year, quarter } = req.body;
const validation = validateYearQuarter(year, quarter);
if (!validation.ok) {
return res.status(400).json({ success: false, message: validation.message });
}
// check if the year and quarter window created or not and start the calculation
const window = await QuarterlyWindowsConfiguration.findOne({
where: {
year: year,
quarter: quarter
}
});
if (!window) {
return res.status(400).json({
success: false,
message: 'Year and quarter window not created'
});
}
const autoSubmitResult = await runSurveyAutoSubmit(year, quarter);
const calculationResult = await runQuarterCalculation(year, quarter);
res.json({
success: true,
message: `Auto submit and IPI calculation completed for ${year} ${quarter}`,
year,
quarter,
steps: {
survey_auto_submit: autoSubmitResult,
calculate_quarter: calculationResult,
},
});
} catch (error) {
console.error("Error:", error);
res.status(500).json({
success: false,
message: "Error during auto submit and quarterly calculation",
error: error.message,
});
}
};
exports.getCalculationLogsByYearMonth = async (req, res) => {
try {
const { year, quarter, month } = req.query;
if (!year) {
return res.status(400).json({
message: "Year is required",
});
}
const resolvedQuarter = quarter || (month ? (month <= 3 ? "Q1" : month <= 6 ? "Q2" : month <= 9 ? "Q3" : "Q4") : null);
if (!resolvedQuarter) {
return res.status(400).json({
message: "Quarter (or month to derive quarter) is required",
});
}
const logs = await CalculationLog.findAll({
where: {
reference_year: year,
reference_month: resolvedQuarter,
},
order: [["started_at", "DESC"]],
});
if (!logs.length) {
return res.status(404).json({
message: "No calculation logs found",
});
}
return res.status(200).json({
message: "Calculation logs fetched successfully",
data: logs,
});
} catch (err) {
logger.error(`Error getting calculation logs: ${err.message }`);
logger.error(`Stack trace: ${err.stack}`);
return res.status(500).json({
message: "Server error",
});
}
};