GWM : 500 error in product creation
This commit is contained in:
parent
0a2b20213c
commit
050cecbee2
@ -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",
|
||||
});
|
||||
}
|
||||
})
|
||||
|
||||
@ -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: {
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
Reference in New Issue
Block a user