High-severity issues fixed

This commit is contained in:
unknown 2025-12-15 18:59:01 +05:30
parent e635c9787a
commit 3a6c3c6c8f
10 changed files with 433 additions and 197 deletions

View File

@ -2,6 +2,7 @@ const db = require("../models");
const bcrypt = require("bcryptjs"); const bcrypt = require("bcryptjs");
const jwt = require("jsonwebtoken"); const jwt = require("jsonwebtoken");
require("dotenv").config(); require("dotenv").config();
const sanitize = require("sanitize-html");
const User = db.user; const User = db.user;
const EstablishmentUser = db.EstablishmentUser; const EstablishmentUser = db.EstablishmentUser;
@ -115,10 +116,18 @@ exports.logout = (req, res) => {
}); });
}; };
const sanitizeStringValue = (value) =>
typeof value === "string"
? sanitize(value, { allowedTags: [], allowedAttributes: {} })
: value;
// Register new user // Register new user
exports.register = async (req, res) => { exports.register = async (req, res) => {
try { try {
const { name, email, password } = req.body; const name = sanitizeStringValue(req.body.name);
const email = sanitizeStringValue(req.body.email);
const password = req.body.password;
if (!name || !email || !password) if (!name || !email || !password)
return res.status(400).send({ return res.status(400).send({
status: "error", status: "error",
@ -141,8 +150,7 @@ exports.register = async (req, res) => {
return res.status(201).send({ return res.status(201).send({
status: "ok", status: "ok",
code: "REGISTERED", code: "REGISTERED",
message: "User registered successfully", message: "User registered successfully"
data: newUser
}); });
} catch (err) { } catch (err) {

View File

@ -19,6 +19,12 @@ const path = require("path");
const { version } = require("os"); const { version } = require("os");
const sequelize = db.sequelize; const sequelize = db.sequelize;
const { sanitizeForLog } = require("../utils/sanitize"); const { sanitizeForLog } = require("../utils/sanitize");
const sanitize = require("sanitize-html");
const sanitizeStringValue = (value) =>
typeof value === "string"
? sanitize(value, { allowedTags: [], allowedAttributes: {} })
: value;
exports.testEmail = async (req, res) => { exports.testEmail = async (req, res) => {
placeHolderData = { placeHolderData = {
@ -137,39 +143,39 @@ exports.createEstablishment = async (req, res) => {
// Create establishment record // Create establishment record
const establishment = await Establishment.create({ const establishment = await Establishment.create({
establishment_code, establishment_code: sanitizeStringValue(establishment_code),
factory_name, factory_name: sanitizeStringValue(factory_name),
permanent_factory_code: permanent_factory_code || null, permanent_factory_code: sanitizeStringValue(permanent_factory_code),
industry_code: industry_code || null, industry_code: sanitizeStringValue(industry_code),
industry_code_production: industry_code_production || null, industry_code_production: sanitizeStringValue(industry_code_production),
industry_code_mismatch_remarks, industry_code_mismatch_remarks: sanitizeStringValue(industry_code_mismatch_remarks),
license_number, license_number: sanitizeStringValue(license_number),
isic_code, isic_code: sanitizeStringValue(isic_code),
description, description: sanitizeStringValue(description),
establishment_address, establishment_address: sanitizeStringValue(establishment_address),
establishment_city_town_id, establishment_city_town_id,
establishment_emirate_id, establishment_emirate_id,
establishment_postal_code, establishment_postal_code: sanitizeStringValue(establishment_postal_code),
establishment_po_box, establishment_po_box: sanitizeStringValue(establishment_po_box),
establishment_makani_number, establishment_makani_number: sanitizeStringValue(establishment_makani_number),
establishment_contact_person_name, establishment_contact_person_name: sanitizeStringValue(establishment_contact_person_name),
establishment_contact_person_designation, establishment_contact_person_designation: sanitizeStringValue(establishment_contact_person_designation),
establishment_mobile_number, establishment_mobile_number: sanitizeStringValue(establishment_mobile_number),
establishment_contact_email, establishment_contact_email: sanitizeStringValue(establishment_contact_email),
establishment_website, establishment_website: sanitizeStringValue(establishment_website),
corporate_same_as_establishment, corporate_same_as_establishment,
corporate_name, corporate_name: sanitizeStringValue(corporate_name),
corporate_address, corporate_address: sanitizeStringValue(corporate_address),
corporate_city_town_id, corporate_city_town_id,
corporate_emirate_id, corporate_emirate_id,
corporate_postal_code, corporate_postal_code: sanitizeStringValue(corporate_postal_code),
corporate_po_box, corporate_po_box: sanitizeStringValue(corporate_po_box),
corporate_makani_number, corporate_makani_number: sanitizeStringValue(corporate_makani_number),
corporate_contact_person_name, corporate_contact_person_name: sanitizeStringValue(corporate_contact_person_name),
corporate_contact_person_designation, corporate_contact_person_designation: sanitizeStringValue(corporate_contact_person_designation),
corporate_mobile_number, corporate_mobile_number: sanitizeStringValue(corporate_mobile_number),
corporate_email, corporate_email: sanitizeStringValue(corporate_email),
corporate_website, corporate_website: sanitizeStringValue(corporate_website),
emirati_male, emirati_male,
emirati_female, emirati_female,
non_emirati_male, non_emirati_male,
@ -179,7 +185,6 @@ exports.createEstablishment = async (req, res) => {
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(),
}); });
// Hash password // Hash password
const hashedPassword = await bcrypt.hash(establishment_user.password, 10); const hashedPassword = await bcrypt.hash(establishment_user.password, 10);
@ -194,46 +199,15 @@ exports.createEstablishment = async (req, res) => {
//send email to user //send email to user
placeHolderData = { placeHolderData = {
contact_name : sanitizeForLog(establishment_user.name), contact_name : sanitizeStringValue(establishment_user.name),
portal_url : process.env.FE_BASE_URL, portal_url : process.env.FE_BASE_URL,
username : sanitizeForLog(establishment_user.email), username : sanitizeStringValue(establishment_user.email),
password : establishment_user.password, password : establishment_user.password,
support_email : process.env.SUPPORT_EMAIL, support_email : process.env.SUPPORT_EMAIL,
support_phone : process.env.SUPPORT_PHONE, support_phone : process.env.SUPPORT_PHONE,
} }
await sendEmailService(sanitizeForLog(establishment_user.email), 'establishment_user_creation_to_user', placeHolderData); await sendEmailService(sanitizeStringValue(establishment_user.email), 'establishment_user_creation_to_user', placeHolderData);
// insert establishment_products
// if (Array.isArray(establishment_products) && establishment_products.length > 0) {
// // remove duplicates from request itself
// let uniqueProducts = [...new Set(establishment_products.map(x => x.product_id))];
// // now check which already exist for this establishment
// const existing = await EstablishmentProduct.findAll({
// where: {
// establishment_id: establishment.id,
// product_id: uniqueProducts
// },
// attributes: ['product_id']
// });
// const existingIds = existing.map(x => x.product_id);
// // filter only new ones (not existing)
// const newProducts = uniqueProducts
// .filter(pid => !existingIds.includes(pid))
// .map(pid => ({
// establishment_id: establishment.id,
// product_id: pid,
// created_at: new Date(),
// }));
// // only insert if have new ones
// if (newProducts.length) {
// await EstablishmentProduct.bulkCreate(newProducts);
// }
// }
// insert establishment_products // insert establishment_products
if (Array.isArray(establishment_products) && establishment_products.length > 0) if (Array.isArray(establishment_products) && establishment_products.length > 0)
@ -273,14 +247,15 @@ exports.createEstablishment = async (req, res) => {
await EstablishmentProduct.bulkCreate(insertData); await EstablishmentProduct.bulkCreate(insertData);
} }
} }
//Return success response //Return success response
return res.status(201).send({ return res.status(201).send({
status: "success", status: "success",
message: "Establishment and linked user created successfully", message: "Establishment and linked user created successfully",
data: { data: {
establishment, establishment:{
id:establishment.id,
establishment_code: establishment.establishment_code,
},
user: { user: {
id: user.id, id: user.id,
name: user.name, name: user.name,
@ -290,18 +265,14 @@ exports.createEstablishment = async (req, res) => {
}); });
} catch (err) { } catch (err) {
if (err.name === "SequelizeUniqueConstraintError") { if (err.name === "SequelizeUniqueConstraintError") {
const field = err.errors[0].path;
// extract exact field
const field = err.errors[0].path; // <-- this gives the column name
return res.status(400).json({ return res.status(400).json({
status: "failed", status: "failed",
message: `${field} already exists` message: `${field} already exists`
}); });
} }
return res.status(500).send({status: "failed",message: err.message,});
return res.status(500).send({status: "failed",message: err.message || "Internal server error",});
} }
}; };
@ -807,7 +778,29 @@ exports.getAllEmirates = async (req, res) => {
exports.getAllCityTowns = async (req, res) => { exports.getAllCityTowns = async (req, res) => {
try { try {
const { emirate_id } = req.query; let { emirate_id } = req.query;
if (emirate_id) {
emirate_id = Number(emirate_id);
if (isNaN(emirate_id)) {
return res.status(400).send({
status: "failed",
message: "Invalid emirate_id"
});
}
const emirateExists = await Emirate.findOne({
where: { id: emirate_id }
});
if (!emirateExists) {
return res.status(404).send({
status: "failed",
message: "Emirate not found"
});
}
}
const whereClause = {}; const whereClause = {};
if (emirate_id) { if (emirate_id) {
@ -819,8 +812,13 @@ exports.getAllCityTowns = async (req, res) => {
attributes: ["id", "name", "emirate_id"], attributes: ["id", "name", "emirate_id"],
order: [["name", "ASC"]], order: [["name", "ASC"]],
}); });
const sanitizedCities = cities.map(city => ({
id: city.id,
name: sanitizeStringValue(city.name),
emirate_id: city.emirate_id,
}));
return res.status(200).send({status: "success", message: "City/Town fetched successfully", data: cities, }); return res.status(200).send({status: "success", message: "City/Town fetched successfully", data: sanitizedCities, });
} catch (err) { } catch (err) {
return res.status(500).send({ status: "failed", message: err.message || "Internal server error", }); return res.status(500).send({ status: "failed", message: err.message || "Internal server error", });

View File

@ -8,16 +8,69 @@ const UnitMaster = db.UnitMaster;
exports.createEstablishmentProduct = async (req, res) => { exports.createEstablishmentProduct = async (req, res) => {
try { try {
const { establishment_id , product_id, action_done_by , created_by } = req.body; let { establishment_id , product_id, action_done_by , created_by } = req.body;
if (establishment_id === undefined) {
return res.status(400).json({
status: "failed",
message: "establishment_id is required"
});
}
establishment_id = Number(establishment_id);
if (!Number.isInteger(establishment_id) || establishment_id <= 0) {
return res.status(400).json({
status: "failed",
message: "Invalid establishment_id"
});
}
if (product_id === undefined) {
return res.status(400).json({
status: "failed",
message: "product_id is required"
});
}
product_id = Number(product_id);
if (!Number.isInteger(product_id) || (product_id <= 0 && product_id > 10)) {
return res.status(400).json({
status: "failed",
message: "Invalid product_id"
});
}
if (action_done_by === undefined) {
return res.status(400).json({
status: "failed",
message: "action_done_by is required"
});
}
if (typeof action_done_by !== "string" || action_done_by.trim().length === 0) {
return res.status(400).json({
status: "failed",
message: "Invalid action_done_by"
});
}
action_done_by = action_done_by.trim();
const existingMapping = await EstablishmentProduct.findOne({
where: {
establishment_id,
product_id
}
});
if (existingMapping) {
return res.status(409).json({
status: "failed",
message: "This product is already mapped to the establishment"
});
}
const data = await EstablishmentProduct.create({ const data = await EstablishmentProduct.create({
establishment_id, establishment_id,
product_id, product_id,
action_done_by, action_done_by,
created_by: created_by || req.user?.id || null, created_by: created_by || req.user?.id || null,
}); });
res.status(201).send({'status':"success",'message':"Establishment Product Created successfully."});
res.status(201).send({'status':"success",'message':"Creation successful",'data': data });
} catch (err) { } catch (err) {
res.status(500).send({'status':"failed",'message':err.message }); res.status(500).send({'status':"failed",'message':err.message });
@ -28,8 +81,16 @@ exports.getAllEstablishmentProducts = async (req, res) => {
try { try {
const whereClause = {}; const whereClause = {};
if (req.query.establishment_id) { if (typeof req.query.establishment_id !== "undefined") {
whereClause.establishment_id = req.query.establishment_id; const establishmentId = parseInt(req.query.establishment_id, 10);
if (!Number.isInteger(establishmentId) || establishmentId <= 0) {
return res.status(400).json({
status: "failed",
message: "Invalid establishment_id"
});
}
whereClause.establishment_id = establishmentId;
} }
const data = await EstablishmentProduct.findAll({ const data = await EstablishmentProduct.findAll({

View File

@ -1,6 +1,7 @@
const db = require("../models"); const db = require("../models");
const bcrypt = require("bcryptjs"); const bcrypt = require("bcryptjs");
const EstablishmentUser = db.EstablishmentUser; const EstablishmentUser = db.EstablishmentUser;
const Establishment = db.Establishment;
const { sendEmailService } = require("../services/email.service"); const { sendEmailService } = require("../services/email.service");
const { sanitizeForLog } = require("../utils/sanitize"); const { sanitizeForLog } = require("../utils/sanitize");
@ -8,64 +9,110 @@ const { sanitizeForLog } = require("../utils/sanitize");
// Create User // Create User
exports.createUser = async (req, res) => { exports.createUser = async (req, res) => {
try { try {
let { establishment_id, name, email, password, gender } = req.body;
const { establishment_id, name, email, password , gender} = req.body; establishment_id = Number(establishment_id);
const hashed = await bcrypt.hash(password, 10); if (!Number.isInteger(establishment_id) || establishment_id <= 0) {
return res.status(400).json({
email = sanitizeForLog(email || "");
name = sanitizeForLog(name || "");
gender = sanitizeForLog(gender || "");
if (!establishment_id || !email) {
return res.status(400).send({
status: "error", status: "error",
code: "MISSING_FIELDS", message: "Invalid establishment_id"
message: "establishment_id and email are required"
}); });
} }
const establishment = await Establishment.findByPk(establishment_id);
const existing = await EstablishmentUser.findOne({ where: { email, is_active: true } }); if (!establishment) {
if (existing) { return res.status(404).json({
return res.status(400).send({ status: "error",
message: "Establishment not found"
});
}
if (typeof email !== "string" || email.trim().length === 0) {
return res.status(400).json({
status: "error",
message: "Email is required"
});
}
email = email.trim().toLowerCase();
if (typeof password !== "string" || password.length < 6) {
return res.status(400).json({
status: "error",
message: "Password must be at least 6 characters long"
});
}
name = typeof name === "string" ? name.trim() : null;
if (gender) {
gender = gender.toString().toLowerCase();
if (!["male", "female", "other"].includes(gender)) {
return res.status(400).json({
status: "error",
message: "Invalid gender"
});
}
gender = gender.charAt(0).toUpperCase() + gender.slice(1);
}
const existingUser = await EstablishmentUser.findOne({
where: { email, is_active: true }
});
if (existingUser) {
return res.status(409).json({
status: "error", status: "error",
code: "EMAIL_EXISTS",
message: "Email already exists" message: "Email already exists"
}); });
} }
const hashedPassword = await bcrypt.hash(password, 10);
const user = await EstablishmentUser.create({ const user = await EstablishmentUser.create({
establishment_id, establishment_id,
name, name,
email, email,
password: hashed, password: hashedPassword,
gender, gender,
created_by: req.user.id, created_by: req.user.id
}); });
//send email to user await sendEmailService(email, "establishment_user_creation_to_user", {
placeHolderData = { contact_name: name,
contact_name : name, portal_url: process.env.FE_BASE_URL,
portal_url : process.env.FE_BASE_URL, username: email,
username : email, password,
password : password, support_email: process.env.SUPPORT_EMAIL,
support_email : process.env.SUPPORT_EMAIL, support_phone: process.env.SUPPORT_PHONE
support_phone : process.env.SUPPORT_PHONE, });
return res.status(201).json({
status: "success",
message: "Establishment User created successfully",
data: {
name: user.name,
email: user.email,
} }
await sendEmailService(email, 'establishment_user_creation_to_user', placeHolderData); });
} catch (error) {
return res.status(201).send({'status':"ok", 'message':"Creation successful",'data': user }); return res.status(500).json({
status: "error",
} catch (err) { message: error.message
return res.status(500).send({'status':"error",'message':err.message }); });
} }
}; };
// Get all users // Get all users
exports.getAllUsers = async (req, res) => { exports.getAllUsers = async (req, res) => {
try { try {
let users;
if (typeof req.query.establishment_id !== 'undefined') {
const establishmentId = parseInt(req.query.establishment_id, 10);
if (Number.isNaN(establishmentId)) {
return res.status(400).json({
status: "failed",
message: "Invalid establishment_id"
});
}
users = await EstablishmentUser.findAll({ where:{ establishment_id : establishmentId} });
}else{
users = await EstablishmentUser.findAll();
}
const users = await EstablishmentUser.findAll({ where:{ establishment_id : req.query.establishment_id} });
return res.status(200).send({'status':"success",'message':"Fetched successfully",'data': users }); return res.status(200).send({'status':"success",'message':"Fetched successfully",'data': users });
} catch (err) { } catch (err) {

View File

@ -1,12 +1,19 @@
const bcrypt = require("bcryptjs"); const bcrypt = require("bcryptjs");
const jwt = require("jsonwebtoken"); const jwt = require("jsonwebtoken");
const db = require("../models"); const db = require("../models");
const sanitize = require("sanitize-html");
const EstablishmentUser = db.EstablishmentUser; const EstablishmentUser = db.EstablishmentUser;
const sanitizeStringValue = (value) =>
typeof value === "string"
? sanitize(value, { allowedTags: [], allowedAttributes: {} })
: value;
// User Login // User Login
exports.login = async (req, res) => { exports.login = async (req, res) => {
try { try {
const { email, password } = req.body; const email = sanitizeStringValue(req.body.email);
const password = req.body.password;
if (!email || !password) if (!email || !password)
return res.status(400).send({'status':"failed",'message':"Email and password required",'data': ""}); return res.status(400).send({'status':"failed",'message':"Email and password required",'data': ""});
@ -16,11 +23,17 @@ exports.login = async (req, res) => {
const validPassword = await bcrypt.compare(password, user.password); const validPassword = await bcrypt.compare(password, user.password);
if (!validPassword) if (!validPassword)
return res.status(401).json({ message: "Invalid password" }); return res.status(401).json({ message: "Invalid password" });
// Create JWT token // Create JWT token
const token = jwt.sign({ id: user.id, email: user.email , name: user.name, establishment_id: user.establishment_id }, process.env.JWT_SECRET, { const token = jwt.sign(
expiresIn: "6h", {
}); id: user.id,
email: sanitizeStringValue(user.email),
name: sanitizeStringValue(user.name),
establishment_id: user.establishment_id
},
process.env.JWT_SECRET,
{ expiresIn: "6h" }
);
res.status(200).send({'status':"success",'message':"Login successful",'data': token }); res.status(200).send({'status':"success",'message':"Login successful",'data': token });

View File

@ -5,12 +5,20 @@ const db = require("../models");
const { Sequelize } = require("sequelize"); const { Sequelize } = require("sequelize");
const Product = db.Product; const Product = db.Product;
const UnitMaster = db.UnitMaster; const UnitMaster = db.UnitMaster;
const sanitize = require("sanitize-html");
const cleanString = (value) =>
typeof value === "string"
? sanitize(value, { allowedTags: [], allowedAttributes: {} })
: value;
exports.createProduct = async (req, res) => { exports.createProduct = async (req, res) => {
try { try {
const { product_name, hs_code, hs_description } = req.body; const product_name = cleanString(req.body.product_name);
const hs_description = cleanString(req.body.hs_description);
const hs_code = req.body.hs_code;
const unit_id = req.body.unit_id;
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",
@ -51,9 +59,16 @@ exports.createProduct = async (req, res) => {
message: `HS Code '${hs_code}' already exists.` message: `HS Code '${hs_code}' already exists.`
}); });
} }
req.body.created_by = req.user.id;
const data = await Product.create(req.body); await Product.create({
res.status(201).send({'status':"success",'message':"created successfully",'data': data }); product_name,
hs_code,
hs_description,
unit_id,
created_by: req.user.id
});
res.status(201).send({'status':"success",'message':"created successfully." });
} catch (error) { } catch (error) {
res.status(500).send({'status':"failed",'message':error.message }); res.status(500).send({'status':"failed",'message':error.message });
} }

View File

@ -5,6 +5,7 @@ const QuarterlyWindowsConfiguration = db.QuarterlyWindowsConfiguration;
const Establishment = db.Establishment; const Establishment = db.Establishment;
const EstablishmentUser = db.EstablishmentUser; const EstablishmentUser = db.EstablishmentUser;
const { sendEmailService } = require("../services/email.service"); const { sendEmailService } = require("../services/email.service");
const sanitize = require("sanitize-html");
// Create new configuration // Create new configuration
@ -25,11 +26,29 @@ exports.createConfig = async (req, res) => {
where: { is_active: true } where: { is_active: true }
}); });
// set auto values before inserting function sanitizeValue(value) {
req.body.assigned = assignedCount; if (typeof value === "string") {
req.body.responded = 0; return sanitize(value, {
req.body.not_responded = assignedCount; allowedTags: [],
req.body.created_by = req.body.created_by || req.user.id; allowedAttributes: {},
});
}
if (Array.isArray(value)) {
return value.map(sanitizeValue);
}}
const payload = {
survey_name: sanitizeValue(req.body.survey_name),
quarter: sanitizeValue(req.body.quarter),
year: req.body.year,
start_date: req.body.start_date,
end_date: req.body.end_date,
grace_periods_days: req.body.grace_periods_days,
assigned: assignedCount,
responded: 0,
not_responded: assignedCount,
created_by: req.user.id,
};
const placeholder = { const placeholder = {
survey_name: survey_name, survey_name: survey_name,
@ -42,11 +61,10 @@ exports.createConfig = async (req, res) => {
support_phone: process.env.SUPPORT_PHONE support_phone: process.env.SUPPORT_PHONE
}; };
const data = await QuarterlyWindowsConfiguration.create(req.body); const data = await QuarterlyWindowsConfiguration.create(payload);
res.status(201).send({ res.status(201).send({
status: "success", status: "success",
message: "Quarterly window configuration created successfully", message: "Quarterly window configuration created successfully"
data,
}); });
sendEmailsToEstablishments(placeholder); sendEmailsToEstablishments(placeholder);
} catch (err) { } catch (err) {

View File

@ -5,11 +5,26 @@ const { UnitMaster } = require("../models");
const fs = require("fs"); const fs = require("fs");
const csv = require("csv-parser"); const csv = require("csv-parser");
const path = require("path"); const path = require("path");
const sanitize = require("sanitize-html");
const sanitizeStringValue = (value) =>
typeof value === "string"
? sanitize(value, { allowedTags: [], allowedAttributes: {} }).trim()
: value;
// Create new Unit
exports.createUnit = async (req, res) => { exports.createUnit = async (req, res) => {
try { try {
const { uom } = req.body; let {
uom,
description,
is_base_unit,
base_unit_id,
factor
} = req.body;
uom = sanitizeStringValue(uom);
description = sanitizeStringValue(description);
factor = sanitizeStringValue(factor);
if (!uom || typeof uom !== "string" || !uom.trim()) { if (!uom || typeof uom !== "string" || !uom.trim()) {
return res.status(400).json({ return res.status(400).json({
@ -17,27 +32,47 @@ exports.createUnit = async (req, res) => {
message: "UOM (Unit Name) is required and must be a non-empty string.", message: "UOM (Unit Name) is required and must be a non-empty string.",
}); });
} }
const cleaned = uom.replace(/[^a-zA-Z]/g, "").toUpperCase(); if (!description) {
return res.status(400).json({
status: "error",
message: "Description is required and must be a non-empty string.",
});
}
const cleaned = uom.replace(/[^A-Za-z]/g, "").toUpperCase();
if (!cleaned) { if (!cleaned) {
return res.status(400).json({ return res.status(400).json({
status: "error", status: "error",
message: "UOM must contain at least one letter (A-Z).", message: "UOM must contain at least one alphabetic character.",
}); });
} }
const existingUOM = await UnitMaster.findOne({ is_base_unit = Boolean(is_base_unit);
where: Sequelize.where(
Sequelize.fn("LOWER", Sequelize.col("uom")),
uom.toLowerCase()
),
});
if (existingUOM) if (base_unit_id !== null && base_unit_id !== undefined) {
base_unit_id = Number(base_unit_id);
if (Number.isNaN(base_unit_id)) {
return res.status(400).json({ return res.status(400).json({
status: "error", status: "error",
message: `Unit '${uom}' already exists with short key '${existingUOM.uom_short_name}'.`, message: "base_unit_id must be a valid number."
});
}
}
// SAFE: no Sequelize.where, no SQL functions
const existingUOM = await UnitMaster.findOne({
where: {
uom: cleaned
}
}); });
//Generate short name (same rule as CSV) if (existingUOM) {
return res.status(400).json({
status: "error",
message: `Unit '${cleaned}' already exists with short key '${existingUOM.uom_short_name}'.`,
});
}
// Abbreviation map
const abbreviationMap = { const abbreviationMap = {
METER: "MT", METER: "MT",
METRE: "MT", METRE: "MT",
@ -58,44 +93,51 @@ exports.createUnit = async (req, res) => {
UNIT: "UNT", UNIT: "UNT",
}; };
let uomShort; let uomShort =
cleaned.length <= 5
? cleaned
: (abbreviationMap[cleaned] || cleaned.substring(0, 3)) +
Math.random().toString(36).substring(2, 4).toUpperCase();
if (cleaned.length <= 5) { uomShort = uomShort.substring(0, 5);
uomShort = cleaned;
} else {
// If >5 letters, use abbreviation or auto-generate
const baseShort = abbreviationMap[cleaned] || cleaned.substring(0, 3);
const randomLetters = () =>
Array.from({ length: 2 }, () =>
String.fromCharCode(65 + Math.floor(Math.random() * 26))
).join("");
uomShort = `${baseShort}${randomLetters()}`.substring(0, 5);
}
let exists = await UnitMaster.findOne({ let exists = await UnitMaster.findOne({
where: Sequelize.where( where: {
Sequelize.fn("LOWER", Sequelize.col("uom_short_name")), uom_short_name: uomShort
uomShort.toLowerCase() }
),
}); });
while (exists) { while (exists) {
const randomSuffix = Math.random().toString(36).substring(2, 3).toUpperCase(); const suffix = Math.random().toString(36).substring(2, 3).toUpperCase();
uomShort = (uomShort.substring(0, 4) + randomSuffix).substring(0, 5); uomShort = (uomShort.substring(0, 4) + suffix).substring(0, 5);
exists = await UnitMaster.findOne({ exists = await UnitMaster.findOne({
where: Sequelize.where( where: {
Sequelize.fn("LOWER", Sequelize.col("uom_short_name")), uom_short_name: uomShort
uomShort.toLowerCase() }
),
}); });
} }
req.body.uom_short_name = uomShort;
req.body.created_by = req.body.created_by || req.user.id;
const unit = await UnitMaster.create(req.body); const unit = await UnitMaster.create({
res.status(201).json({ status: "success", data: unit }); uom: cleaned,
uom_short_name: uomShort,
description,
is_base_unit,
base_unit_id,
factor: factor || null,
created_by: req.body.created_by || req.user.id
});
return res.status(201).json({
status: "success",
message: "Unit created successfully."
});
} catch (err) { } catch (err) {
res.status(400).json({ status: "error", message: err.message }); return res.status(400).json({
status: "error",
message: err.message
});
} }
}; };
@ -385,19 +427,16 @@ exports.uploadUnitMasterFromCSV = async (req, res) => {
let uomShort = row._generatedShort; let uomShort = row._generatedShort;
const description = row._description; const description = row._description;
const normalizedUom = sanitizeStringValue(uom).toUpperCase();
const normalizedShort = uomShort.toUpperCase();
const existing = await UnitMaster.findOne({ const existing = await UnitMaster.findOne({
where: { where: {
[Op.or]: [ [Op.or]: [
Sequelize.where( { uom: normalizedUom },
Sequelize.fn("LOWER", Sequelize.col("uom")), { uom_short_name: normalizedShort }
uom.toLowerCase() ]
), }
Sequelize.where(
Sequelize.fn("LOWER", Sequelize.col("uom_short_name")),
uomShort.toLowerCase()
),
],
},
}); });
if (existing) { if (existing) {

View File

@ -5,15 +5,43 @@ const VariationReasonMaster = db.VariationReasonMaster;
// Create // Create
exports.createReason = async (req, res) => { exports.createReason = async (req, res) => {
try { try {
const { reason, high_or_low } = req.body; let { reason, high_or_low } = req.body;
if (typeof reason !== "string" || reason.trim().length === 0) {
return res.status(400).json({
status: "failed",
message: "Reason is required"
});
}
reason = reason.trim();
if (!reason) if (high_or_low) {
return res.status(400).send({ status: "failed", message: "Reason is required" }); high_or_low = high_or_low.toString().toLowerCase();
if (!["low", "high"].includes(high_or_low)) {
return res.status(400).json({
status: "failed",
message: "Invalid high_or_low value"
});
}
high_or_low = high_or_low === "low" ? "Low" : "High";
}
const newReason = await VariationReasonMaster.create({
reason,
high_or_low,
created_by: req.body.created_by || req.user.id
});
return res.status(201).json({
status: "success",
message: "Reason created successfully"
});
const newReason = await VariationReasonMaster.create({ reason, high_or_low, created_by: req.body.created_by || req.user.id });
res.status(201).send({ status: "success", message: "Reason created successfully", data: newReason });
} catch (err) { } catch (err) {
res.status(500).send({ status: "failed", message: err.message }); return res.status(500).json({
status: "failed",
message: "Internal server error"
});
} }
}; };

View File

@ -4,11 +4,20 @@ const ZeroTargetReasonMaster = db.ZeroTargetReasonMaster;
// Create // Create
exports.create = async (req, res) => { exports.create = async (req, res) => {
try { try {
const { reason, is_active } = req.body; let { reason, is_active } = req.body;
if (!reason) return res.status(400).send({ status: "failed", message: "Reason is required" }); if (typeof reason !== "string" || reason.trim().length === 0) {
return res.status(400).json({
status: "failed",
message: "Reason is required"
});
}
reason = reason.trim();
if (typeof is_active !== "boolean") {
is_active = true;
}
const data = await ZeroTargetReasonMaster.create({ reason, is_active, created_by: req.user.id }); const data = await ZeroTargetReasonMaster.create({ reason, is_active, created_by: req.user.id });
res.status(201).send({ status: "success", data }); res.status(201).send({ status: "success", message: "Record created successfully",});
} catch (err) { } catch (err) {
res.status(500).send({ status: "failed", message: err.message }); res.status(500).send({ status: "failed", message: err.message });
} }