From 8bc6bea4b2c46916bc4703e798b6d89ac3329a9b Mon Sep 17 00:00:00 2001 From: Dave Richer Date: Mon, 7 Jul 2025 12:14:23 -0400 Subject: [PATCH] feature/IO-3255-simplified-parts-management - Bump deps, add Change Request --- client/package-lock.json | 104 ++++++++--------- client/package.json | 10 +- .../partsManagement/partsManagementUtils.js | 54 +++++++++ ...sampleBody.vehicleDamageEstimateAddRq.xml} | 0 ...AddRq.js => vehicleDamageEstimateAddRq.js} | 55 +-------- .../vehicleDamageEstimateChgRq.js | 106 ++++++++++++++++++ server/routes/intergrationRoutes.js | 26 ++++- 7 files changed, 242 insertions(+), 113 deletions(-) create mode 100644 server/integrations/partsManagement/partsManagementUtils.js rename server/integrations/partsManagement/{sampleBody.xml => sampleBody.vehicleDamageEstimateAddRq.xml} (100%) rename server/integrations/partsManagement/{partsManagementVehicleDamageEstimateAddRq.js => vehicleDamageEstimateAddRq.js} (92%) create mode 100644 server/integrations/partsManagement/vehicleDamageEstimateChgRq.js diff --git a/client/package-lock.json b/client/package-lock.json index b672a0c37..9e3a2cfd9 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -21,11 +21,11 @@ "@jsreport/browser-client": "^3.1.0", "@reduxjs/toolkit": "^2.8.2", "@sentry/cli": "^2.46.0", - "@sentry/react": "^9.34.0", + "@sentry/react": "^9.35.0", "@sentry/vite-plugin": "^3.5.0", "@splitsoftware/splitio-react": "^2.3.1", "@tanem/react-nprogress": "^5.0.53", - "antd": "^5.26.3", + "antd": "^5.26.4", "apollo-link-logger": "^2.0.1", "apollo-link-sentry": "^4.3.0", "autosize": "^6.0.1", @@ -50,7 +50,7 @@ "object-hash": "^3.0.0", "phone": "^3.1.62", "prop-types": "^15.8.1", - "query-string": "^9.2.1", + "query-string": "^9.2.2", "raf-schd": "^4.0.3", "react": "^18.3.1", "react-big-calendar": "^1.19.4", @@ -60,7 +60,7 @@ "react-drag-listview": "^2.0.0", "react-grid-gallery": "^1.0.1", "react-grid-layout": "1.3.4", - "react-i18next": "^15.5.3", + "react-i18next": "^15.6.0", "react-icons": "^5.5.0", "react-image-lightbox": "^5.1.4", "react-markdown": "^10.1.0", @@ -118,7 +118,7 @@ "vite": "^6.3.5", "vite-plugin-babel": "^1.3.2", "vite-plugin-eslint": "^1.8.1", - "vite-plugin-node-polyfills": "^0.23.0", + "vite-plugin-node-polyfills": "^0.24.0", "vite-plugin-pwa": "^1.0.1", "vite-plugin-style-import": "^2.0.0", "vitest": "^3.2.4", @@ -4466,50 +4466,50 @@ "license": "MIT" }, "node_modules/@sentry-internal/browser-utils": { - "version": "9.34.0", - "resolved": "https://registry.npmjs.org/@sentry-internal/browser-utils/-/browser-utils-9.34.0.tgz", - "integrity": "sha512-pXVznvP4CROejYtk6y7UQvPTieWz2vXjukGlO45fsnQa9nNo30lkQh3Ws2HZw2YbTxYZQYx75FBDezwKl2q0hQ==", + "version": "9.35.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/browser-utils/-/browser-utils-9.35.0.tgz", + "integrity": "sha512-75/zOArDQ4ASgndKGQo0m0v8P921eq/Q/sJvR14NopzwuwAchBhjziixWCwxKgvoA20eg3OGwMIkzztxmdp2Tw==", "license": "MIT", "dependencies": { - "@sentry/core": "9.34.0" + "@sentry/core": "9.35.0" }, "engines": { "node": ">=18" } }, "node_modules/@sentry-internal/feedback": { - "version": "9.34.0", - "resolved": "https://registry.npmjs.org/@sentry-internal/feedback/-/feedback-9.34.0.tgz", - "integrity": "sha512-HT/EBRl1DR8XqlJk2wFNPJFcnIzNcEDjmW7C/o7K0GeP5jcSH0dKpcH7ykz2bi46gMRPrkO5EK2eXGK81KYI3g==", + "version": "9.35.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/feedback/-/feedback-9.35.0.tgz", + "integrity": "sha512-IKaZWUmqqqLucuJ5EGgwdrBdvP3l3STXvgKsLmW2l+s9WYbvfPPHukZhUULYRsXleQKXnOuz44WQmwNeZYQutw==", "license": "MIT", "dependencies": { - "@sentry/core": "9.34.0" + "@sentry/core": "9.35.0" }, "engines": { "node": ">=18" } }, "node_modules/@sentry-internal/replay": { - "version": "9.34.0", - "resolved": "https://registry.npmjs.org/@sentry-internal/replay/-/replay-9.34.0.tgz", - "integrity": "sha512-joYSqWltmpkcqI8Gg8jwFtPv0F01whmuQfNGoGaL7Z6B/xO1vvkqEudrg1tmswUHhqtYpZYaEaCvrmv0sPGCfA==", + "version": "9.35.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/replay/-/replay-9.35.0.tgz", + "integrity": "sha512-veGNAXeHXULzkGPudMg5iFqkW4wFD/qVbQSr+s0q3+IZ7vJ+Eql+eBDZEKrfKYIBdNOf5POr+KaEBMpMGCbEkQ==", "license": "MIT", "dependencies": { - "@sentry-internal/browser-utils": "9.34.0", - "@sentry/core": "9.34.0" + "@sentry-internal/browser-utils": "9.35.0", + "@sentry/core": "9.35.0" }, "engines": { "node": ">=18" } }, "node_modules/@sentry-internal/replay-canvas": { - "version": "9.34.0", - "resolved": "https://registry.npmjs.org/@sentry-internal/replay-canvas/-/replay-canvas-9.34.0.tgz", - "integrity": "sha512-GCtqMFk9WwrU3JNz1tlCFAhzmNfgZhLRaS0cLzoTuxPbG3CC2VUIWYEOw7+AdCJZGm8ElTMxu+BkChgGb8qthQ==", + "version": "9.35.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/replay-canvas/-/replay-canvas-9.35.0.tgz", + "integrity": "sha512-nXxrEIkpn+FBxYsD4JPQStEGQWF0j0Rs0LoCyuB1e2QeEg6Pipqg4DIjWDjZyeUAsdoaUsIRhWbMK5OBWUuudw==", "license": "MIT", "dependencies": { - "@sentry-internal/replay": "9.34.0", - "@sentry/core": "9.34.0" + "@sentry-internal/replay": "9.35.0", + "@sentry/core": "9.35.0" }, "engines": { "node": ">=18" @@ -4525,16 +4525,16 @@ } }, "node_modules/@sentry/browser": { - "version": "9.34.0", - "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-9.34.0.tgz", - "integrity": "sha512-6oJxU7JEA/RCgMTVlHXT54U9d0DWg61GgzyLTM+FUa8OUrAoK/t+CZGSMc/13nYN8xs7vcpiORdRx0ogch9zGw==", + "version": "9.35.0", + "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-9.35.0.tgz", + "integrity": "sha512-m1fRwMa1vik6VFAAz6RlJUUU+0+Uo+QIKJWWOx9calb11Zt4wIg9wvox7TOgMd8KPt3sefPXIPM38A+uixyXYw==", "license": "MIT", "dependencies": { - "@sentry-internal/browser-utils": "9.34.0", - "@sentry-internal/feedback": "9.34.0", - "@sentry-internal/replay": "9.34.0", - "@sentry-internal/replay-canvas": "9.34.0", - "@sentry/core": "9.34.0" + "@sentry-internal/browser-utils": "9.35.0", + "@sentry-internal/feedback": "9.35.0", + "@sentry-internal/replay": "9.35.0", + "@sentry-internal/replay-canvas": "9.35.0", + "@sentry/core": "9.35.0" }, "engines": { "node": ">=18" @@ -4911,22 +4911,22 @@ } }, "node_modules/@sentry/core": { - "version": "9.34.0", - "resolved": "https://registry.npmjs.org/@sentry/core/-/core-9.34.0.tgz", - "integrity": "sha512-M/zikVaE3KLkhCFDyrHB35sF7pVkB2RPy07BcRsdFsSsdpjoG+Zq2Sxth2tMTbjd0x9Vtb/X6LVjyCj9GSEvVg==", + "version": "9.35.0", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-9.35.0.tgz", + "integrity": "sha512-bdAtzVQZ/wn4L/m8r2OUCCG/NWr0Q8dyZDwdwvINJaMbyhDRUdQh/MWjrz+id/3JoOL1LigAyTV1h4FJDGuwUQ==", "license": "MIT", "engines": { "node": ">=18" } }, "node_modules/@sentry/react": { - "version": "9.34.0", - "resolved": "https://registry.npmjs.org/@sentry/react/-/react-9.34.0.tgz", - "integrity": "sha512-xrai0g8qBS0AyiAytHlrBiTPu1zG7DNNh4GodAkHcd1j2iVti2c+AR7KgUY7UrsrjXd8Fpy7c+qo+5DnHLpulw==", + "version": "9.35.0", + "resolved": "https://registry.npmjs.org/@sentry/react/-/react-9.35.0.tgz", + "integrity": "sha512-zoLcucRYhSLKGYJ0b06MBVF+s3DvLK3YY651sf9boV071tWZs6Q8FDDD3E+pgw8t+ngL+6kB989Ns2HhyLyYIQ==", "license": "MIT", "dependencies": { - "@sentry/browser": "9.34.0", - "@sentry/core": "9.34.0", + "@sentry/browser": "9.35.0", + "@sentry/core": "9.35.0", "hoist-non-react-statics": "^3.3.2" }, "engines": { @@ -6120,9 +6120,9 @@ } }, "node_modules/antd": { - "version": "5.26.3", - "resolved": "https://registry.npmjs.org/antd/-/antd-5.26.3.tgz", - "integrity": "sha512-M/s9Q39h/+G7AWnS6fbNxmAI9waTH4ti022GVEXBLq2j810V1wJ3UOQps13nEilzDNcyxnFN/EIbqIgS7wSYaA==", + "version": "5.26.4", + "resolved": "https://registry.npmjs.org/antd/-/antd-5.26.4.tgz", + "integrity": "sha512-e1EnOvEkvvqcQ18dxfzChBJyJACyih13WpNf2OtnP9z2POh/SF0fXL+ynUemT1zfr+p+P1po/tmHXaMc5PMghg==", "license": "MIT", "dependencies": { "@ant-design/colors": "^7.2.1", @@ -13610,9 +13610,9 @@ } }, "node_modules/query-string": { - "version": "9.2.1", - "resolved": "https://registry.npmjs.org/query-string/-/query-string-9.2.1.tgz", - "integrity": "sha512-3jTGGLRzlhu/1ws2zlr4Q+GVMLCQTLFOj8CMX5x44cdZG9FQE07x2mQhaNxaKVPNmIDu0mvJ/cEwtY7Pim7hqA==", + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-9.2.2.tgz", + "integrity": "sha512-pDSIZJ9sFuOp6VnD+5IkakSVf+rICAuuU88Hcsr6AKL0QtxSIfVuKiVP2oahFI7tk3CRSexwV+Ya6MOoTxzg9g==", "license": "MIT", "dependencies": { "decode-uri-component": "^0.4.1", @@ -14441,9 +14441,9 @@ } }, "node_modules/react-i18next": { - "version": "15.5.3", - "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-15.5.3.tgz", - "integrity": "sha512-ypYmOKOnjqPEJZO4m1BI0kS8kWqkBNsKYyhVUfij0gvjy9xJNoG/VcGkxq5dRlVwzmrmY1BQMAmpbbUBLwC4Kw==", + "version": "15.6.0", + "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-15.6.0.tgz", + "integrity": "sha512-W135dB0rDfiFmbMipC17nOhGdttO5mzH8BivY+2ybsQBbXvxWIwl3cmeH3T9d+YPBSJu/ouyJKFJTtkK7rJofw==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.27.6", @@ -17680,9 +17680,9 @@ } }, "node_modules/vite-plugin-node-polyfills": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/vite-plugin-node-polyfills/-/vite-plugin-node-polyfills-0.23.0.tgz", - "integrity": "sha512-4n+Ys+2bKHQohPBKigFlndwWQ5fFKwaGY6muNDMTb0fSQLyBzS+jjUNRZG9sKF0S/Go4ApG6LFnUGopjkILg3w==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/vite-plugin-node-polyfills/-/vite-plugin-node-polyfills-0.24.0.tgz", + "integrity": "sha512-GA9QKLH+vIM8NPaGA+o2t8PDfFUl32J8rUp1zQfMKVJQiNkOX4unE51tR6ppl6iKw5yOrDAdSH7r/UIFLCVhLw==", "dev": true, "license": "MIT", "dependencies": { @@ -17693,7 +17693,7 @@ "url": "https://github.com/sponsors/davidmyersdev" }, "peerDependencies": { - "vite": "^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0" + "vite": "^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "node_modules/vite-plugin-pwa": { diff --git a/client/package.json b/client/package.json index 206dd44f4..732de4277 100644 --- a/client/package.json +++ b/client/package.json @@ -20,11 +20,11 @@ "@jsreport/browser-client": "^3.1.0", "@reduxjs/toolkit": "^2.8.2", "@sentry/cli": "^2.46.0", - "@sentry/react": "^9.34.0", + "@sentry/react": "^9.35.0", "@sentry/vite-plugin": "^3.5.0", "@splitsoftware/splitio-react": "^2.3.1", "@tanem/react-nprogress": "^5.0.53", - "antd": "^5.26.3", + "antd": "^5.26.4", "apollo-link-logger": "^2.0.1", "apollo-link-sentry": "^4.3.0", "autosize": "^6.0.1", @@ -49,7 +49,7 @@ "object-hash": "^3.0.0", "phone": "^3.1.62", "prop-types": "^15.8.1", - "query-string": "^9.2.1", + "query-string": "^9.2.2", "raf-schd": "^4.0.3", "react": "^18.3.1", "react-big-calendar": "^1.19.4", @@ -59,7 +59,7 @@ "react-drag-listview": "^2.0.0", "react-grid-gallery": "^1.0.1", "react-grid-layout": "1.3.4", - "react-i18next": "^15.5.3", + "react-i18next": "^15.6.0", "react-icons": "^5.5.0", "react-image-lightbox": "^5.1.4", "react-markdown": "^10.1.0", @@ -158,7 +158,7 @@ "vite": "^6.3.5", "vite-plugin-babel": "^1.3.2", "vite-plugin-eslint": "^1.8.1", - "vite-plugin-node-polyfills": "^0.23.0", + "vite-plugin-node-polyfills": "^0.24.0", "vite-plugin-pwa": "^1.0.1", "vite-plugin-style-import": "^2.0.0", "vitest": "^3.2.4", diff --git a/server/integrations/partsManagement/partsManagementUtils.js b/server/integrations/partsManagement/partsManagementUtils.js new file mode 100644 index 000000000..b1b68b557 --- /dev/null +++ b/server/integrations/partsManagement/partsManagementUtils.js @@ -0,0 +1,54 @@ +const xml2js = require("xml2js"); + +/** + * Parses XML string into a JavaScript object. + * @param {string} xml - The XML string to parse. + * @param {object} logger - The logger instance. + * @returns {Promise} The parsed XML object. + * @throws {Error} If XML parsing fails. + */ +const parseXml = async (xml, logger) => { + try { + return await xml2js.parseStringPromise(xml, { + explicitArray: false, + tagNameProcessors: [xml2js.processors.stripPrefix], + attrNameProcessors: [xml2js.processors.stripPrefix] + }); + } catch (err) { + logger.log("parts-xml-parse-error", "error", null, null, { error: err }); + throw new Error("Invalid XML"); + } +}; + +/** + * Recursively strip `xml2js`-style { _: 'value', $: { ... } } nodes into plain strings. + * @param {*} obj - Parsed XML object + * @returns {*} Normalized object + */ +const normalizeXmlObject = (obj) => { + if (Array.isArray(obj)) { + return obj.map(normalizeXmlObject); + } + + if (typeof obj === "object" && obj !== null) { + if (Object.keys(obj).length === 2 && "_" in obj && "$" in obj) { + return normalizeXmlObject(obj._); // unwrap {_:"value",$:{...}} to just "value" + } + if (Object.keys(obj).length === 1 && "_" in obj) { + return normalizeXmlObject(obj._); // unwrap {_:"value"} + } + + const normalized = {}; + for (const key in obj) { + normalized[key] = normalizeXmlObject(obj[key]); + } + return normalized; + } + + return obj; +}; + +module.exports = { + parseXml, + normalizeXmlObject +}; diff --git a/server/integrations/partsManagement/sampleBody.xml b/server/integrations/partsManagement/sampleBody.vehicleDamageEstimateAddRq.xml similarity index 100% rename from server/integrations/partsManagement/sampleBody.xml rename to server/integrations/partsManagement/sampleBody.vehicleDamageEstimateAddRq.xml diff --git a/server/integrations/partsManagement/partsManagementVehicleDamageEstimateAddRq.js b/server/integrations/partsManagement/vehicleDamageEstimateAddRq.js similarity index 92% rename from server/integrations/partsManagement/partsManagementVehicleDamageEstimateAddRq.js rename to server/integrations/partsManagement/vehicleDamageEstimateAddRq.js index 547b4739f..82bf09cc5 100644 --- a/server/integrations/partsManagement/partsManagementVehicleDamageEstimateAddRq.js +++ b/server/integrations/partsManagement/vehicleDamageEstimateAddRq.js @@ -1,8 +1,8 @@ // no-dd-sa:javascript-code-style/assignment-name // CamelCase is used for GraphQL and database fields. -const xml2js = require("xml2js"); const client = require("../../graphql-client/graphql-client").client; +const { parseXml, normalizeXmlObject } = require("./partsManagementUtils"); // GraphQL Queries and Mutations const { @@ -34,55 +34,6 @@ const KNOWN_PART_RATE_TYPES = [ "CCM", "CCDR" ]; - -/** - * Parses XML string into a JavaScript object. - * @param {string} xml - The XML string to parse. - * @param {object} logger - The logger instance. - * @returns {Promise} The parsed XML object. - * @throws {Error} If XML parsing fails. - */ -const parseXml = async (xml, logger) => { - try { - return await xml2js.parseStringPromise(xml, { - explicitArray: false, - tagNameProcessors: [xml2js.processors.stripPrefix], - attrNameProcessors: [xml2js.processors.stripPrefix] - }); - } catch (err) { - logger.log("parts-xml-parse-error", "error", null, null, { error: err }); - throw new Error("Invalid XML"); - } -}; - -/** - * Recursively strip `xml2js`-style { _: 'value', $: { ... } } nodes into plain strings. - * @param {*} obj - Parsed XML object - * @returns {*} Normalized object - */ -const normalizeXmlObject = (obj) => { - if (Array.isArray(obj)) { - return obj.map(normalizeXmlObject); - } - - if (typeof obj === "object" && obj !== null) { - if (Object.keys(obj).length === 2 && "_" in obj && "$" in obj) { - return normalizeXmlObject(obj._); // unwrap {_:"value",$:{...}} to just "value" - } - if (Object.keys(obj).length === 1 && "_" in obj) { - return normalizeXmlObject(obj._); // unwrap {_:"value"} - } - - const normalized = {}; - for (const key in obj) { - normalized[key] = normalizeXmlObject(obj[key]); - } - return normalized; - } - - return obj; -}; - /** * Fetches the default order status for a bodyshop. * @param {string} shopId - The bodyshop UUID. @@ -508,7 +459,7 @@ const insertOwner = async (ownerInput, logger) => { * @param {object} res - The HTTP response object. * @returns {Promise} */ -const partsManagementVehicleDamageEstimateAddRq = async (req, res) => { +const vehicleDamageEstimateAddRq = async (req, res) => { const { logger } = req; try { @@ -617,4 +568,4 @@ const partsManagementVehicleDamageEstimateAddRq = async (req, res) => { } }; -module.exports = partsManagementVehicleDamageEstimateAddRq; +module.exports = vehicleDamageEstimateAddRq; diff --git a/server/integrations/partsManagement/vehicleDamageEstimateChgRq.js b/server/integrations/partsManagement/vehicleDamageEstimateChgRq.js new file mode 100644 index 000000000..a486bd24a --- /dev/null +++ b/server/integrations/partsManagement/vehicleDamageEstimateChgRq.js @@ -0,0 +1,106 @@ +// no-dd-sa:javascript-code-style/assignment-name +// Handler for VehicleDamageEstimateChgRq + +const client = require("../../graphql-client/graphql-client").client; +const { parseXml, normalizeXmlObject } = require("./partsManagementUtils"); + +const { + GET_JOB_BY_CLAIM_OR_DOCID, + UPDATE_JOB_BY_PK, + UPSERT_JOBLINES, + DELETE_JOBLINES_BY_IDS +} = require("./partsManagement.queries"); + +const findJob = async (claimNum, documentId, logger) => { + try { + const { jobs } = await client.request(GET_JOB_BY_CLAIM_OR_DOCID, { claimNum, documentId }); + return jobs?.[0] || null; + } catch (err) { + logger.log("parts-job-lookup-failed", "error", null, null, { error: err }); + return null; + } +}; + +const extractUpdatedJobData = (rq) => { + const doc = rq.DocumentInfo || {}; + const claim = rq.ClaimInfo || {}; + + return { + comment: doc.Comment || null, + clm_no: claim.ClaimNum || null, + status: claim.ClaimStatus || null, + policy_no: claim.PolicyInfo?.PolicyNum || null + }; +}; + +const extractUpdatedJobLines = (addsChgs = {}) => { + const lines = Array.isArray(addsChgs.DamageLineInfo) ? addsChgs.DamageLineInfo : [addsChgs.DamageLineInfo || []]; + + return lines.map((line) => ({ + line_no: parseInt(line.LineNum, 10), + unq_seq: parseInt(line.UniqueSequenceNum, 10), + status: line.LineStatusCode || null, + line_desc: line.LineDesc || null, + part_type: line.PartInfo?.PartType || null, + part_qty: parseFloat(line.PartInfo?.Quantity || 0), + oem_partno: line.PartInfo?.OEMPartNum || null, + db_price: parseFloat(line.PartInfo?.PartPrice || 0), + act_price: parseFloat(line.PartInfo?.PartPrice || 0), + mod_lbr_ty: line.LaborInfo?.LaborType || null, + mod_lb_hrs: parseFloat(line.LaborInfo?.LaborHours || 0), + lbr_op: line.LaborInfo?.LaborOperation || null, + lbr_amt: parseFloat(line.LaborInfo?.LaborAmt || 0), + notes: line.LineMemo || null, + manual_line: line.ManualLineInd || null + })); +}; + +const extractDeletions = (deletions = {}) => { + const lines = Array.isArray(deletions.DamageLineInfo) ? deletions.DamageLineInfo : [deletions.DamageLineInfo || []]; + + return lines.map((line) => parseInt(line.UniqueSequenceNum, 10)).filter((id) => !isNaN(id)); +}; + +const partsManagementVehicleDamageEstimateChgRq = async (req, res) => { + const { logger } = req; + + try { + const payload = await parseXml(req.body, logger); + const rq = normalizeXmlObject(payload.VehicleDamageEstimateChgRq); + if (!rq) return res.status(400).send("Missing "); + + const shopId = rq.ShopID; + const claimNum = rq.ClaimInfo?.ClaimNum; + const documentId = rq.DocumentInfo?.DocumentID; + + if (!shopId || !claimNum) return res.status(400).send("Missing ShopID or ClaimNum"); + + const job = await findJob(claimNum, documentId, logger); + if (!job) return res.status(404).send("Job not found"); + + const updatedJobData = extractUpdatedJobData(rq); + const updatedLines = extractUpdatedJobLines(rq.AddsChgs); + const deletedLineIds = extractDeletions(rq.Deletions); + + await client.request(UPDATE_JOB_BY_PK, { id: job.id, changes: updatedJobData }); + + if (updatedLines.length > 0) { + await client.request(UPSERT_JOBLINES, { + jobid: job.id, + joblines: updatedLines + }); + } + + if (deletedLineIds.length > 0) { + await client.request(DELETE_JOBLINES_BY_IDS, { jobid: job.id, unqSeqs: deletedLineIds }); + } + + logger.log("parts-job-changed", "info", job.id, null); + return res.status(200).json({ success: true, jobId: job.id }); + } catch (err) { + logger.log("parts-chgrq-error", "error", null, null, { error: err }); + return res.status(err.status || 500).json({ error: err.message || "Internal error" }); + } +}; + +module.exports = partsManagementVehicleDamageEstimateChgRq; diff --git a/server/routes/intergrationRoutes.js b/server/routes/intergrationRoutes.js index 4af112a97..5914a2bde 100644 --- a/server/routes/intergrationRoutes.js +++ b/server/routes/intergrationRoutes.js @@ -1,6 +1,6 @@ const express = require("express"); const router = express.Router(); -const bodyParser = require("body-parser"); // Add body-parser dependency +const bodyParser = require("body-parser"); // Pull secrets from env const { VSSTA_INTEGRATION_SECRET, PARTS_MANAGEMENT_INTEGRATION_SECRET } = process.env; @@ -17,18 +17,36 @@ if (typeof VSSTA_INTEGRATION_SECRET === "string" && VSSTA_INTEGRATION_SECRET.len // Only load Parts Management routes if that secret is set if (typeof PARTS_MANAGEMENT_INTEGRATION_SECRET === "string" && PARTS_MANAGEMENT_INTEGRATION_SECRET.length > 0) { + const XML_BODY_LIMIT = "10mb"; // Set a limit for XML body size + const partsManagementProvisioning = require("../integrations/partsManagement/partsManagementProvisioning"); const partsManagementIntegrationMiddleware = require("../middleware/partsManagementIntegrationMiddleware"); - const partsManagementVehicleDamageEstimateAddRq = require("../integrations/partsManagement/partsManagementVehicleDamageEstimateAddRq"); + const partsManagementVehicleDamageEstimateAddRq = require("../integrations/partsManagement/vehicleDamageEstimateAddRq"); + const partsManagementVehicleDamageEstimateChqRq = require("../integrations/partsManagement/vehicleDamageEstimateChgRq"); - // Add XML parsing middleware for the VehicleDamageEstimateAddRq route + /** + * Route to handle Vehicle Damage Estimate Add Request + */ router.post( "/parts-management/VehicleDamageEstimateAddRq", - bodyParser.raw({ type: "application/xml", limit: "10mb" }), // Parse XML body + bodyParser.raw({ type: "application/xml", limit: XML_BODY_LIMIT }), // Parse XML body partsManagementIntegrationMiddleware, partsManagementVehicleDamageEstimateAddRq ); + /** + * Route to handle Vehicle Damage Estimate Change Request + */ + router.post( + "/parts-management/VehicleDamageEstimateChgRq", + bodyParser.raw({ type: "application/xml", limit: XML_BODY_LIMIT }), // Parse XML body + partsManagementIntegrationMiddleware, + partsManagementVehicleDamageEstimateChqRq + ); + + /** + * Route to handle Parts Management Provisioning + */ router.post("/parts-management/provision", partsManagementIntegrationMiddleware, partsManagementProvisioning); } else { console.warn("PARTS_MANAGEMENT_INTEGRATION_SECRET is not set — skipping /parts-management/provision route");