P0 fixes: - Fix OrderDetail product change overwriting product_id due to React state batching (single setItems call now updates both fields) - Validate all :id route params via parseId helper; return 400 for invalid IDs instead of passing raw strings to SQLite - Product/customer delete now checks for references first, returns 409 Conflict instead of letting FK constraint produce 500 P1 fixes: - Disallow quantity_on_hand in product PUT so all stock changes go through PATCH /stock (preserves audit trail) - Add global Express error handler and unhandledRejection listener P2 fixes: - Validate report date params (YYYY-MM-DD format) and stock-history limit (positive integer, capped at 1000) - Add jsonSafe() helper to api.js for safe 204 handling - OrderNew setSubmitting now runs in finally block - Login shows specific message for 429 rate limit, generic message for other auth failures P3 fixes: - Replace brittle try/catch ALTER TABLE with schema_version migration table and versioned migrations - Fix OrderDetail useEffect missing dependency (useCallback + [load]) Also: expanded README with full production deployment instructions (PM2, nginx, backups) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
22 lines
497 B
JavaScript
22 lines
497 B
JavaScript
function parseId(raw) {
|
|
const n = Number(raw);
|
|
if (!Number.isInteger(n) || n <= 0) return null;
|
|
return n;
|
|
}
|
|
|
|
const DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
|
|
|
|
function isValidDate(str) {
|
|
if (!DATE_RE.test(str)) return false;
|
|
const d = new Date(str + 'T00:00:00');
|
|
return !isNaN(d.getTime());
|
|
}
|
|
|
|
function parseLimit(raw, max = 1000) {
|
|
const n = Number(raw);
|
|
if (!Number.isInteger(n) || n <= 0) return null;
|
|
return Math.min(n, max);
|
|
}
|
|
|
|
module.exports = { parseId, isValidDate, parseLimit };
|