399 lines
12 KiB
JavaScript
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",
|
|
});
|
|
}
|
|
};
|
|
|
|
|
|
|
|
|