244 lines
6.1 KiB
JavaScript
244 lines
6.1 KiB
JavaScript
const db = require("../models");
|
|
|
|
const VALID_QUARTERS = ["Q1", "Q2", "Q3", "Q4"];
|
|
|
|
async function createSubmissionWithProducts(transaction, payload) {
|
|
const { year, establishment_id, product_id, data } = payload;
|
|
|
|
for (const row of data) {
|
|
const { quarter, qty, cost } = row;
|
|
|
|
await db.sequelize.query(
|
|
`
|
|
INSERT INTO submission (
|
|
establishment_id, quarter, year,
|
|
edit_request, edit_access, status,
|
|
created_by, created_at, updated_at,
|
|
approve_reject_status
|
|
)
|
|
SELECT ?, ?, ?, 0, 0, 'Approved', 1,
|
|
CASE ?
|
|
WHEN 'Q1' THEN CONCAT(?, '-03-15 10:00:00')
|
|
WHEN 'Q2' THEN CONCAT(?, '-06-15 10:00:00')
|
|
WHEN 'Q3' THEN CONCAT(?, '-09-15 10:00:00')
|
|
WHEN 'Q4' THEN CONCAT(?, '-12-15 10:00:00')
|
|
END,
|
|
CASE ?
|
|
WHEN 'Q1' THEN CONCAT(?, '-03-15 10:00:00')
|
|
WHEN 'Q2' THEN CONCAT(?, '-06-15 10:00:00')
|
|
WHEN 'Q3' THEN CONCAT(?, '-09-15 10:00:00')
|
|
WHEN 'Q4' THEN CONCAT(?, '-12-15 10:00:00')
|
|
END,
|
|
1
|
|
WHERE NOT EXISTS (
|
|
SELECT 1
|
|
FROM submission
|
|
WHERE establishment_id = ?
|
|
AND quarter = ?
|
|
AND year = ?
|
|
)
|
|
`,
|
|
{
|
|
replacements: [
|
|
establishment_id,
|
|
quarter,
|
|
year,
|
|
quarter,
|
|
year,
|
|
year,
|
|
year,
|
|
year,
|
|
quarter,
|
|
year,
|
|
year,
|
|
year,
|
|
year,
|
|
establishment_id,
|
|
quarter,
|
|
year,
|
|
],
|
|
transaction,
|
|
}
|
|
);
|
|
|
|
const submissionRows = await db.sequelize.query(
|
|
`
|
|
SELECT id
|
|
FROM submission
|
|
WHERE establishment_id = ?
|
|
AND quarter = ?
|
|
AND year = ?
|
|
LIMIT 1
|
|
`,
|
|
{
|
|
replacements: [establishment_id, quarter, year],
|
|
type: db.Sequelize.QueryTypes.SELECT,
|
|
transaction,
|
|
}
|
|
);
|
|
|
|
if (!submissionRows.length) {
|
|
throw new Error(`Submission not found for ${quarter}-${year}`);
|
|
}
|
|
|
|
const submission_id = submissionRows[0].id;
|
|
|
|
await db.sequelize.query(
|
|
`
|
|
INSERT INTO submission_products (
|
|
submission_id, product_id, unit_id,
|
|
annual_installed_capacity,
|
|
|
|
previous_quantity_period_one,
|
|
previous_quantity_period_two,
|
|
previous_quantity_period_three,
|
|
|
|
previous_cost_period_one,
|
|
previous_cost_period_two,
|
|
previous_cost_period_three,
|
|
|
|
current_quantity_period_one,
|
|
current_quantity_period_two,
|
|
current_quantity_period_three,
|
|
|
|
current_cost_period_one,
|
|
current_cost_period_two,
|
|
current_cost_period_three,
|
|
|
|
forecast_quantity_period_one,
|
|
forecast_quantity_period_two,
|
|
forecast_quantity_period_three,
|
|
|
|
forecast_cost_period_one,
|
|
forecast_cost_period_two,
|
|
forecast_cost_period_three,
|
|
|
|
previous_quantity, previous_cost,
|
|
current_quantity, current_cost,
|
|
forecast_quantity, forecast_cost,
|
|
|
|
remarks, created_by, created_at, updated_at, is_active
|
|
)
|
|
SELECT ?, ?, 1, 50000,
|
|
0, 0, 0,
|
|
0, 0, 0,
|
|
?, ?, ?,
|
|
?, ?, ?,
|
|
0, 0, 0,
|
|
0, 0, 0,
|
|
0, 0,
|
|
?, ?,
|
|
0, 0,
|
|
CONCAT(?, ' current only'),
|
|
1, NOW(), NOW(), 1
|
|
WHERE NOT EXISTS (
|
|
SELECT 1
|
|
FROM submission_products
|
|
WHERE submission_id = ?
|
|
AND product_id = ?
|
|
)
|
|
`,
|
|
{
|
|
replacements: [
|
|
submission_id,
|
|
product_id,
|
|
qty / 3,
|
|
qty / 3,
|
|
qty / 3,
|
|
cost / 3,
|
|
cost / 3,
|
|
cost / 3,
|
|
qty,
|
|
cost,
|
|
quarter,
|
|
submission_id,
|
|
product_id,
|
|
],
|
|
transaction,
|
|
}
|
|
);
|
|
}
|
|
}
|
|
|
|
exports.createSubmissionWithProducts = async (req, res) => {
|
|
const payloadList = Array.isArray(req.body) ? req.body : [req.body];
|
|
|
|
if (!payloadList.length) {
|
|
return res.status(400).json({
|
|
success: false,
|
|
message: "Request body must be a non-empty array",
|
|
});
|
|
}
|
|
|
|
const invalidItem = payloadList.find(
|
|
(item) =>
|
|
!item ||
|
|
!item.year ||
|
|
!item.establishment_id ||
|
|
!item.product_id ||
|
|
!Array.isArray(item.data) ||
|
|
item.data.length === 0
|
|
);
|
|
if (invalidItem) {
|
|
return res.status(400).json({
|
|
success: false,
|
|
message:
|
|
"Each payload item must contain year, establishment_id, product_id and non-empty data array",
|
|
});
|
|
}
|
|
|
|
const invalidQuarterItem = payloadList.find((item) =>
|
|
item.data.some((row) => !VALID_QUARTERS.includes(row?.quarter))
|
|
);
|
|
if (invalidQuarterItem) {
|
|
const invalidQuarterRow = invalidQuarterItem.data.find(
|
|
(row) => !VALID_QUARTERS.includes(row?.quarter)
|
|
);
|
|
return res.status(400).json({
|
|
success: false,
|
|
message: `Invalid quarter '${invalidQuarterRow.quarter}'. Allowed: Q1, Q2, Q3, Q4`,
|
|
});
|
|
}
|
|
|
|
const invalidNumberItem = payloadList.find((item) =>
|
|
item.data.some((row) => Number.isNaN(Number(row?.qty)) || Number.isNaN(Number(row?.cost)))
|
|
);
|
|
if (invalidNumberItem) {
|
|
return res.status(400).json({
|
|
success: false,
|
|
message: "Each data row in each payload item must contain numeric qty and cost values",
|
|
});
|
|
}
|
|
|
|
const transaction = await db.sequelize.transaction();
|
|
try {
|
|
for (const item of payloadList) {
|
|
await createSubmissionWithProducts(transaction, {
|
|
year: Number(item.year),
|
|
establishment_id: Number(item.establishment_id),
|
|
product_id: Number(item.product_id),
|
|
data: item.data.map((row) => ({
|
|
quarter: row.quarter,
|
|
qty: Number(row.qty),
|
|
cost: Number(row.cost),
|
|
})),
|
|
});
|
|
}
|
|
|
|
await transaction.commit();
|
|
|
|
return res.status(201).json({
|
|
success: true,
|
|
message: "Submission and submission products processed successfully",
|
|
total_payload_items: payloadList.length,
|
|
});
|
|
} catch (error) {
|
|
await transaction.rollback();
|
|
return res.status(500).json({
|
|
success: false,
|
|
message: "Failed to process submission data",
|
|
error: error.message,
|
|
});
|
|
}
|
|
};
|