GWM : iip Calculation fix commit

This commit is contained in:
Gowtham M 2026-05-02 11:12:53 +05:30
parent 21d1d17bde
commit 0a2b20213c
7 changed files with 98 additions and 11 deletions

View File

@ -61,6 +61,7 @@ exports.createEstablishment = async (req, res) => {
"industry_code", "industry_code",
"license_number", "license_number",
"isic_code", "isic_code",
"zone",
].forEach(key => { ].forEach(key => {
if (req.body[key] === "") req.body[key] = null; if (req.body[key] === "") req.body[key] = null;
}); });
@ -109,7 +110,8 @@ exports.createEstablishment = async (req, res) => {
created_by, created_by,
establishment_user, establishment_user,
establishment_products, establishment_products,
ERN ERN,
zone,
} = req.body; } = req.body;
// Basic Validation // Basic Validation
@ -243,6 +245,10 @@ exports.createEstablishment = async (req, res) => {
total_emirati, total_emirati,
total_employees, total_employees,
ERN: sanitizeStringValue(ERN), ERN: sanitizeStringValue(ERN),
zone:
zone === undefined || zone === null || zone === ""
? null
: sanitizeStringValue(zone),
created_by: req.body.created_by || req.user.id, created_by: req.body.created_by || req.user.id,
created_at: new Date(), created_at: new Date(),
}); });
@ -421,6 +427,7 @@ exports.getAllEstablishments = async (req, res) => {
worksheet.columns = [ worksheet.columns = [
{ header: "ID", key: "id", width: 10 }, { header: "ID", key: "id", width: 10 },
{ header: "Establishment Name", key: "factory_name", width: 30 }, { header: "Establishment Name", key: "factory_name", width: 30 },
{ header: "Zone", key: "zone", width: 16 },
{ header: "Contact Name", key: "establishment_contact_person_name", width: 30 }, { header: "Contact Name", key: "establishment_contact_person_name", width: 30 },
{ header: "Emirate", key: "emirate_name", width: 20 }, { header: "Emirate", key: "emirate_name", width: 20 },
{ header: "ISIC Code", key: "isic_code", width: 20 }, { header: "ISIC Code", key: "isic_code", width: 20 },
@ -438,6 +445,7 @@ exports.getAllEstablishments = async (req, res) => {
worksheet.addRow({ worksheet.addRow({
id: item.id, id: item.id,
factory_name: item.factory_name, factory_name: item.factory_name,
zone: item.zone ?? "-",
establishment_contact_person_name: item.establishment_contact_person_name, establishment_contact_person_name: item.establishment_contact_person_name,
emirate_name: item.establishment_emirate?.name || "-", emirate_name: item.establishment_emirate?.name || "-",
isic_code: item.isic_code || "-", isic_code: item.isic_code || "-",
@ -600,7 +608,8 @@ exports.updateEstablishment = async (req, res) => {
"permanent_factory_code", "permanent_factory_code",
"industry_code", "industry_code",
"license_number", "license_number",
"isic_code" "isic_code",
"zone",
].forEach(key => { ].forEach(key => {
if (req.body[key] === "") req.body[key] = null; if (req.body[key] === "") req.body[key] = null;
}); });
@ -723,6 +732,10 @@ exports.updateEstablishment = async (req, res) => {
if (isic_code !== undefined) { if (isic_code !== undefined) {
estData.isic_code = isic_code; estData.isic_code = isic_code;
} }
if (estData.zone !== undefined && estData.zone !== null && estData.zone !== "") {
estData.zone = sanitizeStringValue(estData.zone);
}
estData.updated_by = estData.updated_by || req.user.id; estData.updated_by = estData.updated_by || req.user.id;
estData.updated_at = estData.updated_at || new Date(); estData.updated_at = estData.updated_at || new Date();
@ -1913,6 +1926,10 @@ exports.establishmentBulkUpload = async (req, res) => {
Number(r.number_of_non_emirati_male || 0) + Number(r.number_of_non_emirati_male || 0) +
Number(r.number_of_non_emirati_female || 0), Number(r.number_of_non_emirati_female || 0),
zone:
r.zone && String(r.zone).trim() !== ""
? sanitizeStringValue(String(r.zone).trim())
: null,
created_by: req.user.id created_by: req.user.id
}, { transaction: transaction }); }, { transaction: transaction });

View File

@ -14,6 +14,21 @@ const cleanString = (value) =>
? sanitize(value, { allowedTags: [], allowedAttributes: {} }) ? sanitize(value, { allowedTags: [], allowedAttributes: {} })
: value; : value;
/** Parses optional products.weight_in_ib (nullable integer, non-negative). */
function parseWeightInIb(value) {
if (value === undefined || value === null || value === "") {
return { ok: true, value: null };
}
const n = parseInt(String(value).trim(), 10);
if (Number.isNaN(n)) {
return { ok: false, message: "weight_in_ib must be a valid integer." };
}
if (n < 0) {
return { ok: false, message: "weight_in_ib must be non-negative." };
}
return { ok: true, value: n };
}
exports.createProduct = async (req, res) => { exports.createProduct = async (req, res) => {
try { try {
const product_name = cleanString(req.body.product_name); const product_name = cleanString(req.body.product_name);
@ -21,6 +36,11 @@ exports.createProduct = async (req, res) => {
const hs_code = req.body.hs_code; const hs_code = req.body.hs_code;
const unit_id = req.body.unit_id; const unit_id = req.body.unit_id;
const weightParsed = parseWeightInIb(req.body.weight_in_ib);
if (!weightParsed.ok) {
return res.status(400).json({ status: "failed", message: weightParsed.message });
}
if (hs_description && hs_description.length > 1000) { if (hs_description && hs_description.length > 1000) {
return res.status(400).json({ return res.status(400).json({
status: "failed", status: "failed",
@ -67,6 +87,7 @@ exports.createProduct = async (req, res) => {
hs_code, hs_code,
hs_description, hs_description,
unit_id, unit_id,
weight_in_ib: weightParsed.value,
created_by: req.user.id created_by: req.user.id
}); });
@ -150,6 +171,14 @@ exports.updateProduct = async (req, res) => {
updated_at: new Date() updated_at: new Date()
}; };
if (Object.prototype.hasOwnProperty.call(req.body, "weight_in_ib")) {
const weightParsed = parseWeightInIb(req.body.weight_in_ib);
if (!weightParsed.ok) {
return res.status(400).json({ status: "failed", message: weightParsed.message });
}
object.weight_in_ib = weightParsed.value;
}
const [updated] = await Product.update(object, { where: { id: req.params.id } }); const [updated] = await Product.update(object, { where: { id: req.params.id } });
if (!updated) res.status(404).send({'status':"failed",'message':"Record Not found",'data': "" }); if (!updated) res.status(404).send({'status':"failed",'message':"Record Not found",'data': "" });
@ -348,6 +377,12 @@ exports.uploadProductsFromCSV = async (req, res) => {
mappedKey = "unit"; mappedKey = "unit";
} else if (/desc(ription)?/i.test(normalizedKey)) { } else if (/desc(ription)?/i.test(normalizedKey)) {
mappedKey = "description"; mappedKey = "description";
} else if (
normalizedKey === "weight_in_ib" ||
/^weight.*(ib|lb)/i.test(normalizedKey) ||
/^weight$/i.test(normalizedKey)
) {
mappedKey = "weight_in_ib";
} }
cleanRow[mappedKey] = row[key] ? row[key].trim() : null; cleanRow[mappedKey] = row[key] ? row[key].trim() : null;
@ -368,10 +403,12 @@ exports.uploadProductsFromCSV = async (req, res) => {
// Validate required columns // Validate required columns
const requiredCols = ["hs_code", "product_name", "unit", "description"]; const requiredCols = ["hs_code", "product_name", "unit", "description"];
const optionalCols = ["weight_in_ib"];
const allowedCols = requiredCols.concat(optionalCols);
const headers = Object.keys(csvResults[0]); const headers = Object.keys(csvResults[0]);
const missingCols = requiredCols.filter(col => !headers.includes(col)); const missingCols = requiredCols.filter(col => !headers.includes(col));
const extraCols = headers.filter(col => !requiredCols.includes(col)); const extraCols = headers.filter(col => !allowedCols.includes(col));
if (missingCols.length > 0 || extraCols.length > 0) { if (missingCols.length > 0 || extraCols.length > 0) {
deleteFileSecure(sanitizedPath); deleteFileSecure(sanitizedPath);
@ -398,6 +435,30 @@ exports.uploadProductsFromCSV = async (req, res) => {
const unit = row.unit ? row.unit.trim().toLowerCase() : ""; const unit = row.unit ? row.unit.trim().toLowerCase() : "";
const description = row.description ? row.description.trim().toLowerCase() : ""; const description = row.description ? row.description.trim().toLowerCase() : "";
let weightInIb = null;
if (
row.weight_in_ib !== undefined &&
row.weight_in_ib !== null &&
String(row.weight_in_ib).trim() !== ""
) {
const w = parseInt(String(row.weight_in_ib).trim(), 10);
if (Number.isNaN(w)) {
validationErrors.push({
row: rowNumber,
error: "weight_in_ib must be numeric",
});
continue;
}
if (w < 0) {
validationErrors.push({
row: rowNumber,
error: "weight_in_ib must be non-negative",
});
continue;
}
weightInIb = w;
}
// Validate required fields // Validate required fields
if (!hsCode || !productName) { if (!hsCode || !productName) {
validationErrors.push({ validationErrors.push({
@ -455,7 +516,8 @@ exports.uploadProductsFromCSV = async (req, res) => {
hsCode: hsCode, hsCode: hsCode,
productName: productName, productName: productName,
unit: unit, unit: unit,
description: description description: description,
weightInIb,
}); });
} }
@ -560,7 +622,8 @@ exports.uploadProductsFromCSV = async (req, res) => {
hs_code: row.hsCode, hs_code: row.hsCode,
product_name: row.productName, product_name: row.productName,
unit_id: unitId, unit_id: unitId,
hs_description: row.description, hs_description: row.description,
weight_in_ib: row.weightInIb,
created_by: userId, created_by: userId,
created_at: new Date(), created_at: new Date(),
}); });

View File

@ -1 +1 @@
Establishment Id * (Mandatory),Factory Name * (Mandatory),User Name * (Mandatory),Email * (Mandatory),Emirate * (Mandatory),City/Town * (Mandatory),Principal activity Code (ISIC Rev 4 - 4 digits) * (Mandatory),HS Code 1 * (Mandatory),HS Code 2,HS Code 3,HS Code 4,HS Code 5,Permanent Factory Code,Industry Code Business Register,Description,Industry Code Mismatch Remarks,Establishment Address,Postal Code,PO Box,Makani Number,Contact Person Name,Contact Person Designation,Mobile Number,Website,Number of Emirati Male,Number of Emirati Female,Number of Non-Emirati Male,Number of Non-Emirati Female Establishment Id * (Mandatory),Factory Name * (Mandatory),User Name * (Mandatory),Email * (Mandatory),Emirate * (Mandatory),City/Town * (Mandatory),Principal activity Code (ISIC Rev 4 - 4 digits) * (Mandatory),HS Code 1 * (Mandatory),HS Code 2,HS Code 3,HS Code 4,HS Code 5,Permanent Factory Code,Industry Code Business Register,Description,Industry Code Mismatch Remarks,Establishment Address,Postal Code,PO Box,Makani Number,Contact Person Name,Contact Person Designation,Mobile Number,Website,Number of Emirati Male,Number of Emirati Female,Number of Non-Emirati Male,Number of Non-Emirati Female,Zone (Optional)

1 Establishment Id * (Mandatory) Factory Name * (Mandatory) User Name * (Mandatory) Email * (Mandatory) Emirate * (Mandatory) City/Town * (Mandatory) Principal activity Code (ISIC Rev 4 - 4 digits) * (Mandatory) HS Code 1 * (Mandatory) HS Code 2 HS Code 3 HS Code 4 HS Code 5 Permanent Factory Code Industry Code Business Register Description Industry Code Mismatch Remarks Establishment Address Postal Code PO Box Makani Number Contact Person Name Contact Person Designation Mobile Number Website Number of Emirati Male Number of Emirati Female Number of Non-Emirati Male Number of Non-Emirati Female Zone (Optional)

View File

@ -1 +1 @@
HS Code * (Mandatory),Product Name * (Mandatory),Unit * (Mandatory),Description HS Code * (Mandatory),Product Name * (Mandatory),Unit * (Mandatory),Description,Weight in lb (Optional)

1 HS Code * (Mandatory) Product Name * (Mandatory) Unit * (Mandatory) Description Weight in lb (Optional)

View File

@ -74,6 +74,7 @@ module.exports = (sequelize, DataTypes) => {
allowNull: true, allowNull: true,
}, },
ERN: { type: DataTypes.STRING }, ERN: { type: DataTypes.STRING },
zone: { type: DataTypes.STRING },
}, },
{ {
timestamps: false, timestamps: false,

View File

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

View File

@ -688,6 +688,7 @@ router.post("/test/submissions-with-products",[verifySignature], testController.
* total_emirati: { type: integer } * total_emirati: { type: integer }
* total_employees: { type: integer } * total_employees: { type: integer }
* ERN: { type: string } * ERN: { type: string }
* zone: { type: string, description: "Establishment zone (optional)" }
* created_by: { type: integer } * created_by: { type: integer }
* establishment_user: * establishment_user:
* type: object * type: object
@ -865,6 +866,7 @@ router.get("/establishments/:id",[verifySignature, verifyToken], establishmentCo
* total_emirati: { type: integer } * total_emirati: { type: integer }
* total_employees: { type: integer } * total_employees: { type: integer }
* ERN: { type: string } * ERN: { type: string }
* zone: { type: string, description: "Establishment zone (optional)" }
* updated_by: { type: integer } * updated_by: { type: integer }
* establishment_products: * establishment_products:
* type: array * type: array
@ -1278,6 +1280,10 @@ router.post("/trigger-establishment-users-welcome-email",[verifySignature, verif
* unit_id: * unit_id:
* type: integer * type: integer
* example: "select data from unit master" * example: "select data from unit master"
* weight_in_ib:
* type: integer
* description: Optional weight used in IPI weighting (integer).
* example: 100
* is_active: * is_active:
* type: boolean * type: boolean
* example: true * example: true
@ -1361,6 +1367,10 @@ router.get("/products/:id", [verifySignature, verifyToken],productController.get
* unit_id: * unit_id:
* type: integer * type: integer
* example: "select data from unit master" * example: "select data from unit master"
* weight_in_ib:
* type: integer
* description: Optional weight used in IPI weighting (integer). Omit to leave unchanged; send null to clear.
* example: 100
* is_active: * is_active:
* type: boolean * type: boolean
* example: true * example: true