GWM : index calculation demo changes.

This commit is contained in:
Gowtham M 2026-04-17 17:40:58 +05:30
parent 466fcecda8
commit 5ffb872c3a
4 changed files with 355 additions and 63 deletions

View File

@ -0,0 +1,243 @@
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,
});
}
};

View File

@ -42,6 +42,10 @@ module.exports = (sequelize, DataTypes) => {
type: DataTypes.INTEGER,
allowNull: true,
},
isic_code: {
type: DataTypes.INTEGER,
allowNull: true,
},
is_active: {
type: DataTypes.BOOLEAN,
defaultValue: true,

View File

@ -19,6 +19,7 @@ const notificationTemplateController = require("../controllers/notificationTempl
const quarterlyWindowsController = require("../controllers/quarterlyWindowsConfiguration.controller");
const calculationController = require("../controllers/calculation.controller");
const ManufacturingIpiController = require("../controllers/manufacturingIPI.controller")
const testController = require("../controllers/test.controller");
const fs = require("fs");
const path = require("path");
const multer = require("multer");
@ -530,6 +531,101 @@ router.put("/admin_users/:id/change-password",[verifySignature, verifyToken], ad
router.get("/testEmail", establishmentController.testEmail);
/**
* @swagger
* /api/test/submissions-with-products:
* post:
* summary: Create test submission and submission-products for multiple quarters
* tags: [Submissions]
* security:
* - appSignature: []
* - CSRF: []
* cookieAuth: []
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: array
* items:
* type: object
* required:
* - year
* - establishment_id
* - product_id
* - data
* properties:
* year:
* type: integer
* example: 2022
* establishment_id:
* type: integer
* example: 2
* product_id:
* type: integer
* example: 3
* data:
* type: array
* items:
* type: object
* required:
* - quarter
* - qty
* - cost
* properties:
* quarter:
* type: string
* enum: [Q1, Q2, Q3, Q4]
* example: Q1
* qty:
* type: number
* example: 456573559
* cost:
* type: number
* example: 40566560717
* example:
* - year: 2022
* establishment_id: 2
* product_id: 3
* data:
* - quarter: Q1
* qty: 456573559
* cost: 40566560717
* - quarter: Q2
* qty: 8755776
* cost: 7654656
* - quarter: Q3
* qty: 7465465
* cost: 865454
* - quarter: Q4
* qty: 656465465
* cost: 98776
* - year: 2022
* establishment_id: 2
* product_id: 3
* data:
* - quarter: Q1
* qty: 456573559
* cost: 40566560717
* - quarter: Q2
* qty: 8755776
* cost: 7654656
* - quarter: Q3
* qty: 7465465
* cost: 865454
* - quarter: Q4
* qty: 656465465
* cost: 98776
* responses:
* 201:
* description: Submission and products processed successfully
* 400:
* description: Validation error
* 500:
* description: Server error
*/
router.post("/test/submissions-with-products",[verifySignature], testController.createSubmissionWithProducts);
/**

View File

@ -309,85 +309,34 @@ class IPICalculationService {
const monthNames = ['', 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
// Aggregate by establishment ISIC4 for the target month-period.
// This keeps grouping driven by establishment isic_code as requested.
// ISIC mapping source is now products.isic_code.
// Aggregate weighted item indices to ISIC 4-digit using product master mapping.
const query = `
WITH submission_period AS (
SELECT
s.establishment_id,
sp.product_id,
LEFT(e.isic_code, 4) AS isic_4digit_code,
SUM(
CASE
WHEN ? BETWEEN 1 AND 3 THEN
CASE
WHEN ? = 1 THEN COALESCE(sp.current_quantity_period_one, 0)
WHEN ? = 2 THEN COALESCE(sp.current_quantity_period_two, 0)
ELSE COALESCE(sp.current_quantity_period_three, 0)
END
WHEN ? BETWEEN 4 AND 6 THEN
CASE
WHEN ? = 4 THEN COALESCE(sp.current_quantity_period_one, 0)
WHEN ? = 5 THEN COALESCE(sp.current_quantity_period_two, 0)
ELSE COALESCE(sp.current_quantity_period_three, 0)
END
WHEN ? BETWEEN 7 AND 9 THEN
CASE
WHEN ? = 7 THEN COALESCE(sp.current_quantity_period_one, 0)
WHEN ? = 8 THEN COALESCE(sp.current_quantity_period_two, 0)
ELSE COALESCE(sp.current_quantity_period_three, 0)
END
ELSE
CASE
WHEN ? = 10 THEN COALESCE(sp.current_quantity_period_one, 0)
WHEN ? = 11 THEN COALESCE(sp.current_quantity_period_two, 0)
ELSE COALESCE(sp.current_quantity_period_three, 0)
END
END
) AS current_production
FROM submission_products sp
JOIN submission s ON s.id = sp.submission_id
JOIN establishments e ON e.id = s.establishment_id
WHERE s.year = ?
AND s.quarter = CASE
WHEN ? BETWEEN 1 AND 3 THEN 'Q1'
WHEN ? BETWEEN 4 AND 6 THEN 'Q2'
WHEN ? BETWEEN 7 AND 9 THEN 'Q3'
ELSE 'Q4'
END
AND s.status = 'Approved'
AND sp.is_active = 1
AND e.isic_code IS NOT NULL
AND e.isic_code != ''
GROUP BY s.establishment_id, sp.product_id, LEFT(e.isic_code, 4)
)
SELECT
spd.isic_4digit_code,
LEFT(CAST(p.isic_code AS CHAR), 4) AS isic_4digit_code,
SUM(COALESCE(p.weight_in_ib, 0)) AS total_weight,
SUM(
COALESCE(p.weight_in_ib, 0) *
((spd.current_production / byp.avg_by_production) * 100)
ili.item_index
) AS weighted_index_sum,
(
SUM(
COALESCE(p.weight_in_ib, 0) *
((spd.current_production / byp.avg_by_production) * 100)
ili.item_index
) /
NULLIF(SUM(COALESCE(p.weight_in_ib, 0)), 0)
) AS isic_4digit_index
FROM submission_period spd
JOIN products p ON p.id = spd.product_id
JOIN base_year_production byp ON byp.product_id = spd.product_id
WHERE byp.avg_by_production > 0
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
AND p.weight_in_ib IS NOT NULL
AND p.weight_in_ib > 0
GROUP BY spd.isic_4digit_code
GROUP BY LEFT(CAST(p.isic_code AS CHAR), 4)
`;
const [isic4Results] = await connection.query(query, [
month, month, month, month, month, month, month, month, month, month, month,
year, month, month, month
]);
const [isic4Results] = await connection.query(query, [year, month]);
for (const result of isic4Results) {
await connection.query(