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, }); } };