Compare commits

...

23 Commits

Author SHA1 Message Date
Dave
043471fdbc feature/IO-3385-Remove-CASL-From-Rome - Remove CASL Report from Rome Customers. 2025-10-15 11:46:51 -04:00
Dave Richer
13a44b9a59 Merged in hotfix/2025-10-10 (pull request #2628)
Hotfix/2025 10 10 - Change BullMQ Key Delimiters based on system logs.

Approved-by: Patrick Fic
2025-10-11 01:35:51 +00:00
Dave
8e6c809fc6 Update 2025-10-10 16:02:56 -04:00
Dave
41afedd02c Change Delemiters in BullMQ, was using an invalid : 2025-10-10 15:58:32 -04:00
Allan Carr
11e0c3e507 Merged in hotfix/2025-10-09 (pull request #2624)
Hotfix/2025 10 09
2025-10-09 17:57:20 +00:00
Allan Carr
6c1c7c9c2c Merged in feature/IO-3386-carfax-rps (pull request #2623)
Feature/IO-3386 carfax rps
2025-10-09 17:55:21 +00:00
Allan Carr
36dfed80fb IO-3386 CARFAX RPS adjustment
Signed-off-by: Allan Carr <allan@imexsystems.ca>
2025-10-08 19:31:46 -07:00
Dave Richer
e3df22160b Merged in release/2025-10-17 (pull request #2621)
Release/2025 10 17 into master-AIO - IO-3330, IO-3356, IO-3365, IO-3368, IO-3373, IO-3386, IO-3388, IO-3389, IO-3390, IO-3395
2025-10-08 01:22:16 +00:00
Allan Carr
0ccfe6f3aa IO-3368 CARFAX Adjustments for OP_CODE
Signed-off-by: Allan Carr <allan@imexsystems.ca>
2025-10-07 13:02:44 -07:00
Allan Carr
34d70f6a18 Merged in feature/IO-3373-Dashboard-Component-Redux-Fix (pull request #2618)
IO-3373 Remove Cache Eviction

Approved-by: Dave Richer
2025-10-07 18:40:51 +00:00
Allan Carr
fc1bf213c7 Merged in feature/IO-3395-Postback-Payment-Date (pull request #2614)
IO-3395 Postback Payment Date

Approved-by: Dave Richer
2025-10-07 18:40:28 +00:00
Allan Carr
f19b9cb8e1 IO-3395 Postback Payment Date
Signed-off-by: Allan Carr <allan@imexsystems.ca>
2025-10-07 10:59:10 -07:00
Allan Carr
c9ade68dc9 Merged in feature/IO-3386-carfax-rps (pull request #2610)
IO-3386 Cleanup To extension

Approved-by: Dave Richer
2025-10-07 16:16:15 +00:00
Allan Carr
0d247a38d2 Merged in feature/IO-3373-Dashboard-Component-Redux-Fix (pull request #2609)
IO-3373 Dashboard Component Redux Fix

Approved-by: Dave Richer
2025-10-07 16:15:49 +00:00
Dave Richer
595ffa02ba Merged in feature/IO-3390-parts-management-2 (pull request #2615)
feature/IO-3390-Parts-Management-2 - Add Job status patch route, Bodyshop Patch route, remove PAO from simplified parts filter.
2025-10-07 16:13:24 +00:00
Dave
d573335eb0 feature/IO-3390-Parts-Management-2 - Add Job status patch route, Bodyshop Patch route, remove PAO from simplified parts filter. 2025-10-07 12:11:53 -04:00
Allan Carr
bdd5056c9a IO-3395 Postback Payment Date
Signed-off-by: Allan Carr <allan@imexsystems.ca>
2025-10-06 21:35:31 -07:00
Allan Carr
4f6db827e7 IO-3395 Postback Payment Date
Signed-off-by: Allan Carr <allan@imexsystems.ca>
2025-10-06 17:50:19 -07:00
Patrick Fic
e50bbc3bcc Merged in hotfix/2025-10-06-remove-amplitude (pull request #2613)
Hotfix/2025 10 06 remove amplitude
2025-10-06 15:35:46 +00:00
Patrick Fic
15792cb0ef Merged in hotfix/2025-10-06-remove-amplitude (pull request #2612)
Hotfix/2025 10 06 remove amplitude
2025-10-06 15:33:23 +00:00
Patrick Fic
856a24d496 Remove console log. 2025-10-06 08:33:01 -07:00
Patrick Fic
74d6bcc004 Remove amplitude. 2025-10-06 08:19:43 -07:00
Allan Carr
2af95de353 IO-3386 Cleanup To extension
Signed-off-by: Allan Carr <allan@imexsystems.ca>
2025-10-03 12:13:42 -07:00
25 changed files with 1817 additions and 1593 deletions

View File

@@ -0,0 +1,61 @@
# PATCH /integrations/parts-management/job/:id/status
Update (patch) the status of a job created under parts management. This endpoint is only available
for jobs whose parent bodyshop has an `external_shop_id` (i.e., is provisioned for parts
management).
## Endpoint
```
PATCH /integrations/parts-management/job/:id/status
```
- `:id` is the UUID of the job to update.
## Request Headers
- `Authorization`: (if required by your integration middleware)
- `Content-Type: application/json`
## Request Body
Send a JSON object with the following field:
- `status` (string, required): The new status for the job.
Example:
```
PATCH /integrations/parts-management/job/123e4567-e89b-12d3-a456-426614174000/status
Content-Type: application/json
{
"status": "IN_PROGRESS"
}
```
## Success Response
- **200 OK**
- Returns the updated job object with the new status.
```
{
"id": "123e4567-e89b-12d3-a456-426614174000",
"status": "IN_PROGRESS",
...
}
```
## Error Responses
- **400 Bad Request**: Missing status field, or parent bodyshop does not have an `external_shop_id`.
- **404 Not Found**: No job found with the given ID.
- **500 Internal Server Error**: Unexpected error.
## Notes
- Only jobs whose parent bodyshop has an `external_shop_id` can be patched via this route.
- Fields other than `status` will be ignored if included in the request body.
- The route is protected by the same middleware as other parts management endpoints.

View File

@@ -0,0 +1,86 @@
# PATCH /integrations/parts-management/provision/:id
Update (patch) select fields for a parts management bodyshop. Only available for shops that have an
`external_shop_id` (i.e., are provisioned for parts management).
## Endpoint
```
PATCH /integrations/parts-management/provision/:id
```
- `:id` is the UUID of the bodyshop to update.
## Request Headers
- `Authorization`: (if required by your integration middleware)
- `Content-Type: application/json`
## Request Body
Send a JSON object with one or more of the following fields to update:
- `shopname` (string)
- `address1` (string)
- `address2` (string, optional)
- `city` (string)
- `state` (string)
- `zip_post` (string)
- `country` (string)
- `email` (string, shop's email, not user email)
- `timezone` (string)
- `phone` (string)
- `logo_img_path` (object, e.g. `{ src, width, height, headerMargin }`)
Any fields not included in the request body will remain unchanged.
## Example Request
```
PATCH /integrations/parts-management/provision/123e4567-e89b-12d3-a456-426614174000
Content-Type: application/json
{
"shopname": "New Shop Name",
"address1": "123 Main St",
"city": "Springfield",
"state": "IL",
"zip_post": "62704",
"country": "USA",
"email": "shop@example.com",
"timezone": "America/Chicago",
"phone": "555-123-4567",
"logo_img_path": {
"src": "https://example.com/logo.png",
"width": "200",
"height": "100",
"headerMargin": 10
}
}
```
## Success Response
- **200 OK**
- Returns the updated shop object with the patched fields.
```
{
"id": "123e4567-e89b-12d3-a456-426614174000",
"shopname": "New Shop Name",
...
}
```
## Error Responses
- **400 Bad Request**: No valid fields provided, or shop does not have an `external_shop_id`.
- **404 Not Found**: No shop found with the given ID.
- **500 Internal Server Error**: Unexpected error.
## Notes
- Only shops with an `external_shop_id` can be patched via this route.
- Fields not listed above will be ignored if included in the request body.
- The route is protected by the same middleware as other parts management endpoints.

728
client/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -8,56 +8,56 @@
"private": true,
"proxy": "http://localhost:4000",
"dependencies": {
"@amplitude/analytics-browser": "^2.23.5",
"@amplitude/analytics-browser": "^2.25.2",
"@ant-design/pro-layout": "^7.22.6",
"@apollo/client": "^3.13.9",
"@emotion/is-prop-valid": "^1.4.0",
"@fingerprintjs/fingerprintjs": "^4.6.1",
"@firebase/analytics": "^0.10.17",
"@firebase/app": "^0.14.2",
"@firebase/app": "^0.14.3",
"@firebase/auth": "^1.10.8",
"@firebase/firestore": "^4.9.1",
"@firebase/firestore": "^4.9.2",
"@firebase/messaging": "^0.12.22",
"@jsreport/browser-client": "^3.1.0",
"@reduxjs/toolkit": "^2.9.0",
"@sentry/cli": "^2.53.0",
"@sentry/cli": "^2.56.0",
"@sentry/react": "^9.43.0",
"@sentry/vite-plugin": "^4.3.0",
"@splitsoftware/splitio-react": "^2.3.1",
"@splitsoftware/splitio-react": "^2.5.0",
"@tanem/react-nprogress": "^5.0.53",
"antd": "^5.27.3",
"antd": "^5.27.4",
"apollo-link-logger": "^2.0.1",
"apollo-link-sentry": "^4.4.0",
"autosize": "^6.0.1",
"axios": "^1.11.0",
"axios": "^1.12.2",
"classnames": "^2.5.1",
"css-box-model": "^1.2.1",
"dayjs": "^1.11.18",
"dayjs-business-days2": "^1.3.0",
"dinero.js": "^1.9.1",
"dotenv": "^17.2.2",
"dotenv": "^17.2.3",
"env-cmd": "^10.1.0",
"exifr": "^7.1.3",
"graphql": "^16.11.0",
"i18next": "^25.5.2",
"i18next": "^25.5.3",
"i18next-browser-languagedetector": "^8.2.0",
"immutability-helper": "^3.1.1",
"libphonenumber-js": "^1.12.15",
"libphonenumber-js": "^1.12.23",
"lightningcss": "^1.30.2",
"logrocket": "^9.0.2",
"markerjs2": "^2.32.6",
"markerjs2": "^2.32.7",
"memoize-one": "^6.0.0",
"normalize-url": "^8.0.2",
"normalize-url": "^8.1.0",
"object-hash": "^3.0.0",
"phone": "^3.1.67",
"posthog-js": "^1.261.7",
"posthog-js": "^1.271.0",
"prop-types": "^15.8.1",
"query-string": "^9.2.2",
"query-string": "^9.3.1",
"raf-schd": "^4.0.3",
"react": "^18.3.1",
"react-big-calendar": "^1.19.4",
"react-color": "^2.19.3",
"react-cookie": "^8.0.1",
"lightningcss": "^1.30.1",
"react-dom": "^18.3.1",
"react-drag-listview": "^2.0.0",
"react-grid-gallery": "^1.0.1",
@@ -73,7 +73,7 @@
"react-resizable": "^3.0.5",
"react-router-dom": "^6.30.0",
"react-sticky": "^6.0.3",
"react-virtuoso": "^4.14.0",
"react-virtuoso": "^4.14.1",
"recharts": "^2.15.2",
"redux": "^5.0.1",
"redux-actions": "^3.0.3",
@@ -81,7 +81,7 @@
"redux-saga": "^1.3.0",
"redux-state-sync": "^3.1.4",
"reselect": "^5.1.1",
"sass": "^1.92.0",
"sass": "^1.93.2",
"socket.io-client": "^4.8.1",
"styled-components": "^6.1.19",
"subscriptions-transport-ws": "^0.11.0",
@@ -133,33 +133,33 @@
"@rollup/rollup-linux-x64-gnu": "4.6.1"
},
"devDependencies": {
"@ant-design/icons": "^6.0.0",
"@ant-design/icons": "^6.1.0",
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
"@babel/preset-react": "^7.27.1",
"@dotenvx/dotenvx": "^1.49.0",
"@dotenvx/dotenvx": "^1.51.0",
"@emotion/babel-plugin": "^11.13.5",
"@emotion/react": "^11.14.0",
"@eslint/js": "^9.33.0",
"@playwright/test": "^1.55.0",
"@sentry/webpack-plugin": "^4.1.1",
"@eslint/js": "^9.37.0",
"@playwright/test": "^1.56.0",
"@sentry/webpack-plugin": "^4.3.0",
"@testing-library/dom": "^10.4.1",
"@testing-library/jest-dom": "^6.8.0",
"@testing-library/jest-dom": "^6.9.1",
"@testing-library/react": "^16.3.0",
"@vitejs/plugin-react": "^4.6.0",
"browserslist": "^4.25.3",
"browserslist": "^4.26.3",
"browserslist-to-esbuild": "^2.1.1",
"chalk": "^5.6.0",
"eslint": "^9.33.0",
"chalk": "^5.6.2",
"eslint": "^9.37.0",
"eslint-plugin-react": "^7.37.5",
"globals": "^15.15.0",
"jsdom": "^26.0.0",
"memfs": "^4.36.3",
"memfs": "^4.48.1",
"os-browserify": "^0.3.0",
"playwright": "^1.55.0",
"playwright": "^1.56.0",
"react-error-overlay": "^6.1.0",
"redux-logger": "^3.0.6",
"source-map-explorer": "^2.5.3",
"vite": "^7.1.3",
"vite": "^7.1.9",
"vite-plugin-babel": "^1.3.2",
"vite-plugin-eslint": "^1.8.1",
"vite-plugin-node-polyfills": "^0.24.0",

View File

@@ -97,7 +97,7 @@ export function JobLinesComponent({
filteredInfo: {
...(isPartsEntry
? {
part_type: ["PAN", "PAC", "PAR", "PAL", "PAA", "PAM", "PAP", "PAS", "PASL", "PAG", "PAO"]
part_type: ["PAN", "PAC", "PAR", "PAL", "PAA", "PAM", "PAP", "PAS", "PASL", "PAG"] //"PAO" Removed by request
}
: {})
}

View File

@@ -4,7 +4,7 @@ import { getAuth, updatePassword, updateProfile } from "@firebase/auth";
import { getFirestore } from "@firebase/firestore";
import { getMessaging, getToken, onMessage } from "@firebase/messaging";
import { store } from "../redux/store";
import * as amplitude from '@amplitude/analytics-browser';
//import * as amplitude from '@amplitude/analytics-browser';
import posthog from 'posthog-js'
const config = JSON.parse(import.meta.env.VITE_APP_FIREBASE_CONFIG);
@@ -91,14 +91,14 @@ export const logImEXEvent = (eventName, additionalParams, stateProp = null) => {
// dbevent: false,
// env: `master-AIO|${import.meta.env.VITE_APP_GIT_SHA_DATE}`
// });
console.log(
"%c[Analytics]",
"background-color: green ;font-weight:bold;",
eventName,
eventParams
);
// console.log(
// "%c[Analytics]",
// "background-color: green ;font-weight:bold;",
// eventName,
// eventParams
// );
logEvent(analytics, eventName, eventParams);
amplitude.track(eventName, eventParams);
//amplitude.track(eventName, eventParams);
posthog.capture(eventName, eventParams);
} finally {

View File

@@ -14,7 +14,7 @@ import { persistor, store } from "./redux/store";
import reportWebVitals from "./reportWebVitals";
import "./translations/i18n";
import "./utils/CleanAxios";
import * as amplitude from "@amplitude/analytics-browser";
//import * as amplitude from "@amplitude/analytics-browser";
import { PostHogProvider } from "posthog-js/react";
import posthog from "posthog-js";
@@ -26,23 +26,23 @@ registerSW({ immediate: true });
// Dinero.globalLocale = "en-CA";
Dinero.globalRoundingMode = "HALF_EVEN";
amplitude.init(import.meta.env.VITE_APP_AMP_KEY, {
defaultTracking: true,
serverUrl: import.meta.env.VITE_APP_AMP_URL
// {
// attribution: {
// excludeReferrers: true,
// initialEmptyValue: true,
// resetSessionOnNewCampaign: true,
// },
// fileDownloads: true,
// formInteractions: true,
// pageViews: {
// trackHistoryChanges: 'all'
// },
// sessions: true
// }
});
// amplitude.init(import.meta.env.VITE_APP_AMP_KEY, {
// defaultTracking: true,
// serverUrl: import.meta.env.VITE_APP_AMP_URL
// // {
// // attribution: {
// // excludeReferrers: true,
// // initialEmptyValue: true,
// // resetSessionOnNewCampaign: true,
// // },
// // fileDownloads: true,
// // formInteractions: true,
// // pageViews: {
// // trackHistoryChanges: 'all'
// // },
// // sessions: true
// // }
// });
posthog.init(import.meta.env.VITE_PUBLIC_POSTHOG_KEY, {
autocapture: false,

View File

@@ -49,7 +49,7 @@ import {
validatePasswordResetSuccess
} from "./user.actions";
import UserActionTypes from "./user.types";
import * as amplitude from '@amplitude/analytics-browser';
//import * as amplitude from '@amplitude/analytics-browser';
import posthog from 'posthog-js';
const fpPromise = FingerprintJS.load();
@@ -92,7 +92,7 @@ export function* isUserAuthenticated() {
}
LogRocket.identify(user.email);
amplitude.setUserId(user.email);
//amplitude.setUserId(user.email);
posthog.identify(user.email);
const eulaQuery = yield client.query({
@@ -139,7 +139,7 @@ export function* signOutStart() {
imexshopid: state.user.bodyshop.imexshopid,
type: "messaging"
});
amplitude.reset();
//amplitude.reset();
} catch {
console.log("No FCM token. Skipping unsubscribe.");
}
@@ -365,7 +365,7 @@ export function* SetAuthLevelFromShopDetails({ payload }) {
}
try {
amplitude.setGroup('Shop', payload.shopname);
//amplitude.setGroup('Shop', payload.shopname);
window.$crisp.push(["set", "user:company", [payload.shopname]]);
if (authRecord[0] && authRecord[0].user.validemail) {
window.$crisp.push(["set", "user:email", [authRecord[0].user.email]]);

View File

@@ -15,21 +15,29 @@ export const EmailSettings = {
export const TemplateList = (type, context) => {
//const { bodyshop } = store.getState().user;
const casl = InstanceRenderManager({
imex: {
casl_authorization: {
title: i18n.t("printcenter.jobs.casl_authorization"),
description: "",
subject: i18n.t("printcenter.jobs.casl_authorization"),
key: "casl_authorization",
disabled: false,
group: "authorization",
regions: {
CA: true
}
}
},
rome: {}
});
return {
//If there's no type or the type is job, send it back.
...(!type || type === "job"
? {
casl_authorization: {
title: i18n.t("printcenter.jobs.casl_authorization"),
description: "",
subject: i18n.t("printcenter.jobs.casl_authorization"),
key: "casl_authorization",
disabled: false,
group: "authorization",
regions: {
CA: true
}
},
...casl,
fippa_authorization: {
title: i18n.t("printcenter.jobs.fippa_authorization"),
description: "",

2155
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -18,40 +18,40 @@
"job-totals-fixtures:local": "docker exec node-app /usr/bin/node /app/download-job-totals-fixtures.js"
},
"dependencies": {
"@aws-sdk/client-cloudwatch-logs": "^3.882.0",
"@aws-sdk/client-elasticache": "^3.882.0",
"@aws-sdk/client-s3": "^3.882.0",
"@aws-sdk/client-secrets-manager": "^3.882.0",
"@aws-sdk/client-ses": "^3.882.0",
"@aws-sdk/credential-provider-node": "^3.882.0",
"@aws-sdk/lib-storage": "^3.882.0",
"@aws-sdk/s3-request-presigner": "^3.882.0",
"@aws-sdk/client-cloudwatch-logs": "^3.901.0",
"@aws-sdk/client-elasticache": "^3.901.0",
"@aws-sdk/client-s3": "^3.901.0",
"@aws-sdk/client-secrets-manager": "^3.901.0",
"@aws-sdk/client-ses": "^3.901.0",
"@aws-sdk/credential-provider-node": "^3.901.0",
"@aws-sdk/lib-storage": "^3.903.0",
"@aws-sdk/s3-request-presigner": "^3.901.0",
"@opensearch-project/opensearch": "^2.13.0",
"@socket.io/admin-ui": "^0.5.1",
"@socket.io/redis-adapter": "^8.3.0",
"archiver": "^7.0.1",
"aws4": "^1.13.2",
"axios": "^1.11.0",
"axios": "^1.12.2",
"better-queue": "^3.8.12",
"bullmq": "^5.58.5",
"bullmq": "^5.61.0",
"chart.js": "^4.5.0",
"cloudinary": "^2.7.0",
"compression": "^1.8.1",
"cookie-parser": "^1.4.7",
"cors": "^2.8.5",
"crisp-status-reporter": "^1.2.2",
"dd-trace": "^5.65.0",
"dd-trace": "^5.70.0",
"dinero.js": "^1.9.1",
"dotenv": "^17.2.2",
"dotenv": "^17.2.3",
"express": "^4.21.1",
"firebase-admin": "^13.5.0",
"graphql": "^16.11.0",
"graphql-request": "^6.1.0",
"intuit-oauth": "^4.2.0",
"ioredis": "^5.7.0",
"ioredis": "^5.8.1",
"json-2-csv": "^5.5.9",
"jsonwebtoken": "^9.0.2",
"juice": "^11.0.1",
"juice": "^11.0.3",
"lodash": "^4.17.21",
"moment": "^2.30.1",
"moment-timezone": "^0.6.0",
@@ -62,22 +62,22 @@
"query-string": "7.1.3",
"recursive-diff": "^1.0.9",
"rimraf": "^6.0.1",
"skia-canvas": "^3.0.6",
"soap": "^1.3.0",
"skia-canvas": "^3.0.8",
"soap": "^1.5.0",
"socket.io": "^4.8.1",
"socket.io-adapter": "^2.5.5",
"ssh2-sftp-client": "^11.0.0",
"twilio": "^5.9.0",
"twilio": "^5.10.2",
"uuid": "^11.1.0",
"winston": "^3.17.0",
"winston": "^3.18.3",
"winston-cloudwatch": "^6.3.0",
"xml2js": "^0.6.2",
"xmlbuilder2": "^3.1.1",
"yazl": "^3.3.1"
},
"devDependencies": {
"@eslint/js": "^9.35.0",
"eslint": "^9.35.0",
"@eslint/js": "^9.37.0",
"eslint": "^9.37.0",
"eslint-plugin-react": "^7.37.5",
"globals": "^15.15.0",
"mock-require": "^3.0.3",

View File

@@ -277,6 +277,7 @@ const GenerateDetailLines = (line) => {
line_desc: line.line_desc ? line.line_desc.replace(NON_ASCII_REGEX, "") : null,
oem_partno: line.oem_partno ? line.oem_partno.replace(NON_ASCII_REGEX, "") : null,
alt_partno: line.alt_partno ? line.alt_partno.replace(NON_ASCII_REGEX, "") : null,
op_code: line.lbr_op || null,
op_code_desc: generateOpCodeDescription(line.lbr_op),
lbr_ty: generateLaborType(line.mod_lbr_ty),
lbr_hrs: line.mod_lb_hrs || 0,

View File

@@ -336,6 +336,7 @@ const GenerateDetailLines = (line) => {
line_desc: line.line_desc ? line.line_desc.replace(NON_ASCII_REGEX, "") : null,
oem_partno: line.oem_partno ? line.oem_partno.replace(NON_ASCII_REGEX, "") : null,
alt_partno: line.alt_partno ? line.alt_partno.replace(NON_ASCII_REGEX, "") : null,
op_code: line.lbr_op || null,
op_code_desc: line.op_code_desc ? line.op_code_desc.replace(NON_ASCII_REGEX, "") : null,
lbr_ty: generateLaborType(line.mod_lbr_ty),
lbr_hrs: line.mod_lb_hrs || 0,

View File

@@ -47,11 +47,6 @@ const logEmail = async (req, email) => {
const sendServerEmail = async ({ subject, text, to = [] }) => {
if (process.env.NODE_ENV === undefined) return;
let sentTo = ["support@imexsystems.ca"];
if (to?.length) {
sentTo = [...sentTo, ...to];
}
try {
mailer.sendMail(
{
@@ -59,7 +54,7 @@ const sendServerEmail = async ({ subject, text, to = [] }) => {
imex: `ImEX Online API - ${process.env.NODE_ENV} <noreply@imex.online>`,
rome: `Rome Online API - ${process.env.NODE_ENV} <noreply@romeonline.io>`
}),
to: sentTo,
to: ["support@imexsystems.ca", ...to],
subject: subject,
text: text,
ses: {

View File

@@ -1,5 +1,3 @@
const logger = require("../utils/logger");
const GraphQLClient = require("graphql-request").GraphQLClient;
//New bug introduced with Graphql Request.
@@ -14,17 +12,18 @@ const client = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, {
});
const rpsClient =
process.env.RPS_GRAPHQL_ENDPOINT && process.env.RPS_HASURA_ADMIN_SECRET ?
new GraphQLClient(process.env.RPS_GRAPHQL_ENDPOINT, {
headers: {
"x-hasura-admin-secret": process.env.RPS_HASURA_ADMIN_SECRET
}
}) : null;
process.env.RPS_GRAPHQL_ENDPOINT && process.env.RPS_HASURA_ADMIN_SECRET
? new GraphQLClient(process.env.RPS_GRAPHQL_ENDPOINT, {
headers: {
"x-hasura-admin-secret": process.env.RPS_HASURA_ADMIN_SECRET
}
})
: null;
if (!rpsClient) {
//System log to disable RPS functions
logger.log(`RPS secrets are not set. Client is not configured.`, "WARN", "redis", "api", {
});
console.log(`RPS secrets are not set. Client is not configured.`, "WARN", "redis", "api", {});
}
const unauthorizedClient = new GraphQLClient(process.env.GRAPHQL_ENDPOINT);

View File

@@ -904,6 +904,7 @@ exports.CARFAX_QUERY = `query CARFAX_EXPORT($start: timestamptz, $bodyshopid: uu
line_desc
mod_lb_hrs
mod_lbr_ty
lbr_op
oem_partno
op_code_desc
part_type
@@ -2901,6 +2902,7 @@ exports.GET_BODYSHOP_BY_ID = `
intellipay_config
state
notification_followers
timezone
}
}
`;
@@ -2992,6 +2994,7 @@ query GET_JOBID_BY_MERCHANTID_RONUMBER($merchantID: String!, $roNumber: String!)
id
intellipay_config
email
timezone
}
}
}`;
@@ -3001,6 +3004,7 @@ query GET_BODYSHOP_BY_MERCHANTID($merchantID: String!) {
bodyshops(where: {intellipay_merchant_id: {_eq: $merchantID}}) {
id
email
timezone
}
}`;

View File

@@ -7,7 +7,8 @@ const {
CREATE_SHOP,
DELETE_VENDORS_BY_SHOP,
DELETE_SHOP,
CREATE_USER
CREATE_USER,
UPDATE_BODYSHOP_BY_ID
} = require("../partsManagement.queries");
/**
@@ -131,6 +132,61 @@ const insertUserAssociation = async (uid, email, shopId) => {
return resp.insert_users_one;
};
/**
* PATCH handler for updating bodyshop fields.
* Allows patching: shopname, address1, address2, city, state, zip_post, country, email, timezone, phone, logo_img_path
* @param req
* @param res
* @returns {Promise<void>}
*/
const patchPartsManagementProvisioning = async (req, res) => {
const { id } = req.params;
const allowedFields = [
"shopname",
"address1",
"address2",
"city",
"state",
"zip_post",
"country",
"email",
"timezone",
"phone",
"logo_img_path"
];
const updateFields = {};
for (const field of allowedFields) {
if (req.body[field] !== undefined) {
updateFields[field] = req.body[field];
}
}
if (Object.keys(updateFields).length === 0) {
return res.status(400).json({ error: "No valid fields provided for update." });
}
// Check that the bodyshop has an external_shop_id before allowing patch
try {
// Fetch the bodyshop by id
const shopResp = await client.request(
`query GetBodyshop($id: uuid!) { bodyshops_by_pk(id: $id) { id external_shop_id } }`,
{ id }
);
if (!shopResp.bodyshops_by_pk?.external_shop_id) {
return res.status(400).json({ error: "Cannot patch: bodyshop does not have an external_shop_id." });
}
} catch (err) {
return res.status(500).json({ error: "Failed to validate bodyshop external_shop_id.", detail: err });
}
try {
const resp = await client.request(UPDATE_BODYSHOP_BY_ID, { id, fields: updateFields });
if (!resp.update_bodyshops_by_pk) {
return res.status(404).json({ error: "Bodyshop not found." });
}
return res.json(resp.update_bodyshops_by_pk);
} catch (err) {
return res.status(500).json({ error: "Failed to update bodyshop.", detail: err });
}
};
/**
* Handles provisioning a new shop for parts management.
* @param req
@@ -259,4 +315,4 @@ const partsManagementProvisioning = async (req, res) => {
}
};
module.exports = partsManagementProvisioning;
module.exports = { partsManagementProvisioning, patchPartsManagementProvisioning };

View File

@@ -298,6 +298,25 @@ const UPDATE_JOBLINE_BY_PK = `
}
`;
const UPDATE_BODYSHOP_BY_ID = `
mutation UpdateBodyshopById($id: uuid!, $fields: bodyshops_set_input!) {
update_bodyshops_by_pk(pk_columns: { id: $id }, _set: $fields) {
id
shopname
address1
address2
city
state
zip_post
country
email
timezone
phone
logo_img_path
}
}
`;
module.exports = {
GET_BODYSHOP_STATUS,
GET_VEHICLE_BY_SHOP_VIN,
@@ -329,5 +348,6 @@ module.exports = {
DELETE_PARTS_ORDERS_BY_JOB_IDS,
UPSERT_JOBLINES,
GET_JOBLINE_IDS_BY_JOBID_UNQSEQ,
UPDATE_JOBLINE_BY_PK
UPDATE_JOBLINE_BY_PK,
UPDATE_BODYSHOP_BY_ID
};

View File

@@ -48,7 +48,9 @@ const handleCommentBasedPayment = async (values, decodedComment, logger, logMeta
payer: "Customer",
type: getPaymentType(ipMapping, values.cardtype),
jobid: p.jobid,
date: moment(Date.now()),
date: moment()
.tz(bodyshop?.bodyshops_by_pk?.timezone ?? "UTC")
.format("YYYY-MM-DD"),
payment_responses: {
data: {
amount: values.total,

View File

@@ -97,7 +97,9 @@ const handleInvoiceBasedPayment = async (values, logger, logMeta, res) => {
payer: "Customer",
type: getPaymentType(ipMapping, values.cardtype),
jobid: job.id,
date: moment(Date.now())
date: moment()
.tz(bodyshop?.timezone ?? "UTC")
.format("YYYY-MM-DD")
}
});

View File

@@ -0,0 +1,40 @@
const client = require("../graphql-client/graphql-client").client;
const { UPDATE_JOB_BY_ID } = require("../integrations/partsManagement/partsManagement.queries");
/**
* PATCH handler to update job status (parts management only)
* @param req
* @param res
* @returns {Promise<void>}
*/
module.exports = async (req, res) => {
const { id } = req.params;
const { status } = req.body;
if (!status) {
return res.status(400).json({ error: "Missing required field: status" });
}
try {
// Fetch job to get shopid
const jobResp = await client.request(`query GetJob($id: uuid!) { jobs_by_pk(id: $id) { id shopid } }`, { id });
const job = jobResp.jobs_by_pk;
if (!job) {
return res.status(404).json({ error: "Job not found" });
}
// Fetch bodyshop to check external_shop_id
const shopResp = await client.request(
`query GetBodyshop($id: uuid!) { bodyshops_by_pk(id: $id) { id external_shop_id } }`,
{ id: job.shopid }
);
if (!shopResp.bodyshops_by_pk || !shopResp.bodyshops_by_pk.external_shop_id) {
return res.status(400).json({ error: "Cannot patch: parent bodyshop does not have an external_shop_id." });
}
// Update job status
const updateResp = await client.request(UPDATE_JOB_BY_ID, { id, job: { status } });
if (!updateResp.update_jobs_by_pk) {
return res.status(404).json({ error: "Job not found after update" });
}
return res.json(updateResp.update_jobs_by_pk);
} catch (err) {
return res.status(500).json({ error: "Failed to update job status.", detail: err });
}
};

View File

@@ -92,7 +92,7 @@ const loadAppQueue = async ({ pubClient, logger, redisHelpers, ioRedis }) => {
"consolidate-notifications",
{ jobId, recipients },
{
jobId: `consolidate:${jobId}`,
jobId: `consolidate-${jobId}`,
delay: APP_CONSOLIDATION_DELAY,
attempts: 3,
backoff: LOCK_EXPIRATION
@@ -288,7 +288,7 @@ const dispatchAppsToQueue = async ({ appsToDispatch }) => {
await appQueue.add(
"add-notification",
{ jobId, bodyShopId, key, variables, recipients, body, jobRoNumber },
{ jobId: `${jobId}:${Date.now()}` }
{ jobId: `${jobId}-${Date.now()}` }
);
devDebugLogger(`Added notification to queue for jobId ${jobId} with ${recipients.length} recipients`);
}

View File

@@ -86,7 +86,7 @@ const loadEmailQueue = async ({ pubClient, logger }) => {
"consolidate-emails",
{ jobId, jobRoNumber, bodyShopName, bodyShopTimezone },
{
jobId: `consolidate:${jobId}`,
jobId: `consolidate-${jobId}`,
delay: EMAIL_CONSOLIDATION_DELAY,
attempts: 3,
backoff: LOCK_EXPIRATION
@@ -252,7 +252,7 @@ const dispatchEmailsToQueue = async ({ emailsToDispatch, logger }) => {
await emailAddQueue.add(
"add-email-notification",
{ jobId, jobRoNumber, bodyShopName, bodyShopTimezone, body, recipients },
{ jobId: `${jobId}:${Date.now()}` }
{ jobId: `${jobId}-${Date.now()}` }
);
devDebugLogger(`Added email notification to queue for jobId ${jobId} with ${recipients.length} recipients`);
}

View File

@@ -19,11 +19,15 @@ if (typeof VSSTA_INTEGRATION_SECRET === "string" && VSSTA_INTEGRATION_SECRET.len
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/endpoints/partsManagementProvisioning");
const {
partsManagementProvisioning,
patchPartsManagementProvisioning
} = require("../integrations/partsManagement/endpoints/partsManagementProvisioning");
const partsManagementDeprovisioning = require("../integrations/partsManagement/endpoints/partsManagementDeprovisioning");
const partsManagementIntegrationMiddleware = require("../middleware/partsManagementIntegrationMiddleware");
const partsManagementVehicleDamageEstimateAddRq = require("../integrations/partsManagement/endpoints/vehicleDamageEstimateAddRq");
const partsManagementVehicleDamageEstimateChqRq = require("../integrations/partsManagement/endpoints/vehicleDamageEstimateChgRq");
const patchJobStatus = require("../job/patchJobStatus");
/**
* Route to handle Vehicle Damage Estimate Add Request
@@ -55,6 +59,20 @@ if (typeof PARTS_MANAGEMENT_INTEGRATION_SECRET === "string" && PARTS_MANAGEMENT_
* Route to handle Parts Management Provisioning
*/
router.post("/parts-management/provision", partsManagementIntegrationMiddleware, partsManagementProvisioning);
/**
* PATCH route to update Parts Management Provisioning info
*/
router.patch(
"/parts-management/provision/:id",
partsManagementIntegrationMiddleware,
patchPartsManagementProvisioning
);
/**
* PATCH route to update job status (parts management only)
*/
router.patch("/parts-management/job/:id/status", partsManagementIntegrationMiddleware, patchJobStatus);
} else {
logger.logger.warn("PARTS_MANAGEMENT_INTEGRATION_SECRET is not set — skipping /parts-management/provision route");
}