GWM : 500 error in product creation

This commit is contained in:
Gowtham M 2026-05-05 10:44:32 +05:30
parent 0a2b20213c
commit 050cecbee2
3 changed files with 78 additions and 26 deletions

View File

@ -14,19 +14,69 @@ const cleanString = (value) =>
? sanitize(value, { allowedTags: [], allowedAttributes: {} })
: value;
/** Parses optional products.weight_in_ib (nullable integer, non-negative). */
/** Matches products.weight_in_ib DECIMAL(18,10): max 8 digits before the decimal. */
const WEIGHT_DECIMAL_PLACES = 10;
const WEIGHT_MAX_BEFORE_DECIMAL = 8;
const WEIGHT_MAX =
Number("9".repeat(WEIGHT_MAX_BEFORE_DECIMAL) + "." + "9".repeat(WEIGHT_DECIMAL_PLACES));
/** Parses optional products.weight_in_ib (nullable non-negative decimal). */
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." };
const str = String(value).trim().replace(",", ".");
const n = Number(str);
if (!Number.isFinite(n)) {
return { ok: false, message: "weight_in_ib must be a valid number." };
}
if (n < 0) {
return { ok: false, message: "weight_in_ib must be non-negative." };
}
return { ok: true, value: n };
const rounded = Number(n.toFixed(WEIGHT_DECIMAL_PLACES));
if (rounded > WEIGHT_MAX) {
return {
ok: false,
message: `weight_in_ib is too large. Maximum is ${WEIGHT_MAX} (${WEIGHT_MAX_BEFORE_DECIMAL} digits before the decimal).`,
};
}
return { ok: true, value: rounded };
}
/** Returns true if a response was sent (4xx). */
function tryRespondProductPersistenceError(error, res) {
if (!error || res.headersSent) {
return false;
}
if (error.name === "SequelizeUniqueConstraintError") {
const field = error.errors?.[0]?.path ?? "field";
res.status(400).json({
status: "failed",
message: `${field} already exists.`,
});
return true;
}
const sqlMessage = error.parent?.sqlMessage || error.original?.sqlMessage || "";
const errno = error.parent?.errno ?? error.original?.errno;
const combined = `${error.message} ${sqlMessage}`;
if (error.name === "SequelizeDatabaseError") {
if (
combined.includes("weight_in_ib") ||
(errno === 1264 && /weight/i.test(combined))
) {
res.status(400).json({
status: "failed",
message:
"weight_in_ib is outside the range allowed by your database column (MySQL DECIMAL precision/scale). For example, DECIMAL(12,10) only allows values below 100. Either use a smaller weight, or widen the column (e.g. ALTER TABLE products MODIFY COLUMN weight_in_ib DECIMAL(18,10) NULL).",
});
return true;
}
}
return false;
}
exports.createProduct = async (req, res) => {
@ -95,6 +145,9 @@ exports.createProduct = async (req, res) => {
} catch (error) {
logger.error(error.message);
logger.error(`Stack trace: ${error.stack}`);
if (tryRespondProductPersistenceError(error, res)) {
return;
}
res.status(500).send({'status':"failed",'message': "Internal server error" });
}
};
@ -188,6 +241,9 @@ exports.updateProduct = async (req, res) => {
} catch (error) {
logger.error(error.message);
logger.error(`Stack trace: ${error.stack}`);
if (tryRespondProductPersistenceError(error, res)) {
return;
}
res.status(500).send({'status':"failed",'message': "Internal server error" });
}
};
@ -441,22 +497,15 @@ exports.uploadProductsFromCSV = async (req, res) => {
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)) {
const wp = parseWeightInIb(row.weight_in_ib);
if (!wp.ok) {
validationErrors.push({
row: rowNumber,
error: "weight_in_ib must be numeric",
error: wp.message,
});
continue;
}
if (w < 0) {
validationErrors.push({
row: rowNumber,
error: "weight_in_ib must be non-negative",
});
continue;
}
weightInIb = w;
weightInIb = wp.value;
}
// Validate required fields
@ -664,9 +713,12 @@ exports.uploadProductsFromCSV = async (req, res) => {
logger.error("CSV processing error: " + processingError.message);
logger.error(`Stack trace: ${processingError.stack}`);
}
return res.status(500).send({
status: "failed",
message: processingError.message
if (tryRespondProductPersistenceError(processingError, res)) {
return;
}
return res.status(500).send({
status: "failed",
message: "Internal server error",
});
}
})

View File

@ -39,7 +39,7 @@ module.exports = (sequelize, DataTypes) => {
allowNull: true,
},
weight_in_ib: {
type: DataTypes.INTEGER,
type: DataTypes.DECIMAL(18, 10),
allowNull: true,
},
is_active: {

View File

@ -1281,9 +1281,9 @@ router.post("/trigger-establishment-users-welcome-email",[verifySignature, verif
* type: integer
* example: "select data from unit master"
* weight_in_ib:
* type: integer
* description: Optional weight used in IPI weighting (integer).
* example: 100
* type: number
* description: Optional weight used in IPI weighting (decimal, DECIMAL(18,10) in DB).
* example: 100.655214
* is_active:
* type: boolean
* example: true
@ -1368,9 +1368,9 @@ router.get("/products/:id", [verifySignature, verifyToken],productController.get
* type: integer
* 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
* type: number
* description: Optional weight used in IPI weighting (decimal). Omit to leave unchanged; send null to clear.
* example: 100.655214
* is_active:
* type: boolean
* example: true