Compare commits

...

71 Commits

Author SHA1 Message Date
Dave Richer
045f36e294 Merged in release/2026-06-05 (pull request #3288)
IO-3722 Remove delivery date for bypass vehicles.
2026-05-28 19:23:09 +00:00
Dave Richer
c7c6dfcd7d Merged in feature/IO-3722-disable-contact-fortellis (pull request #3287)
IO-3722 Remove delivery date for bypass vehicles.
2026-05-28 19:22:53 +00:00
Patrick Fic
c1c0b35c8f IO-3722 Remove delivery date for bypass vehicles. 2026-05-28 11:32:23 -07:00
Dave Richer
c024fdd57b Merged in release/2026-06-05 (pull request #3285)
Release/2026 06 05
2026-05-28 16:56:04 +00:00
Dave Richer
a4ccacf83a Merged in feature/IO-3722-disable-contact-fortellis (pull request #3284)
IO-3722 Remove customer lookup by Vehicle Owner.
2026-05-28 16:55:39 +00:00
Patrick Fic
aa3b303fe9 IO-3722 Remove customer lookup by Vehicle Owner. 2026-05-28 09:53:40 -07:00
Patrick Fic
fdaf50d778 Merged in feature/IO-3722-disable-contact-fortellis (pull request #3282)
Feature/IO-3722 disable contact fortellis
2026-05-27 21:48:17 +00:00
Patrick Fic
468ed23f73 IO-3722 Fix undefined customer ref. 2026-05-27 14:18:31 -07:00
Patrick Fic
322ebd3bc7 Resolve inversed if statement. 2026-05-27 12:46:09 -07:00
Patrick Fic
b887cfed01 Merged in feature/IO-3722-disable-contact-fortellis (pull request #3278)
IO-3722 Add additional await.
2026-05-27 19:41:41 +00:00
Patrick Fic
0f800c5a4c IO-3722 Add additional await. 2026-05-27 12:40:41 -07:00
Dave Richer
6cce92b0fd Merged in release/2026-06-05 (pull request #3276)
IO-3722 Disable contact API calls for Fortellis.
2026-05-27 18:29:33 +00:00
Dave Richer
60ab04cb38 Merged in feature/IO-3722-disable-contact-fortellis (pull request #3275)
IO-3722 Disable contact API calls for Fortellis.
2026-05-27 18:29:14 +00:00
Patrick FIc
345a470731 IO-3722 Disable contact API calls for Fortellis. 2026-05-27 10:31:33 -07:00
Dave Richer
0025e113c6 Merged in release/2026-06-05 (pull request #3273)
feature/IO-3541-Parts-Dispatch-Return-Data - Add Refetech queries to keep data in sync
2026-05-26 16:25:53 +00:00
Dave Richer
dc435b2bb0 Merged in feature/IO-3541-Parts-Dispatch-Return-Data (pull request #3272)
feature/IO-3541-Parts-Dispatch-Return-Data - Add Refetech queries to keep data in sync
2026-05-26 16:25:23 +00:00
Dave
fd72d244e7 feature/IO-3541-Parts-Dispatch-Return-Data - Add Refetech queries to keep data in sync 2026-05-26 12:24:56 -04:00
Dave Richer
87bb472271 Merged in release/2026-06-05 (pull request #3271)
feature/IO-2960-Employee-Email-Info - Fix
2026-05-26 16:18:15 +00:00
Dave Richer
825959880e Merged in feature/IO-2960-Employee-Email-Info (pull request #3270)
feature/IO-2960-Employee-Email-Info - Fix
2026-05-26 16:17:55 +00:00
Dave
c40fea0ec9 feature/IO-2960-Employee-Email-Info - Fix 2026-05-26 12:17:25 -04:00
Dave Richer
ebdf427b58 Merged in release/2026-06-05 (pull request #3269)
feature/IO-3567-New-Job-Line-Tab - Fix
2026-05-26 16:06:29 +00:00
Dave Richer
b3fdd68276 Merged in feature/IO-3567-New-Job-Line-Tab (pull request #3268)
feature/IO-3567-New-Job-Line-Tab - Fix
2026-05-26 16:06:02 +00:00
Dave Richer
3e63c58b9b Merged in release/2026-06-05 (pull request #3267)
release/2026-06-05 - Esignture Banner
2026-05-26 15:49:49 +00:00
Dave Richer
7e2df3e341 Merged in release/2026-06-05 (pull request #3266)
release/2026-06-05 - Fix Documenso
2026-05-25 20:43:05 +00:00
Dave Richer
709b6ef1d6 Merged in release/2026-06-05 (pull request #3265)
release/2026-06-05 - Package updates, Esig Fixes, Ant tweak for upgrade
2026-05-25 19:52:15 +00:00
Dave Richer
b920bb4437 Merged in release/2026-06-05 (pull request #3264)
Release/2026 06 05
2026-05-25 19:06:02 +00:00
Dave Richer
4d299bb226 Merged in release/2026-06-05 (pull request #3262)
feature/IO-3701-Harness-Replacement - Implement
2026-05-25 15:44:29 +00:00
Dave Richer
99b65e8186 Merged in release/2026-06-05 (pull request #3260)
feature/IO-3714-Esignature-Lock - Add Lock to Esignatures
2026-05-25 15:27:36 +00:00
Dave Richer
426283ffee Merged in release/2026-05-22 (pull request #3256)
IO-3710 Visual Board Vehicle Color
2026-05-20 23:57:48 +00:00
Dave Richer
4fc86ccaa3 Merged in release/2026-05-22 (pull request #3254)
release/2026-05-22 - Remove uncessary require
2026-05-20 20:12:07 +00:00
Dave Richer
a67946c5a3 Merged in release/2026-05-22 (pull request #3253)
IO-3712 Disable analytics in client side.
2026-05-20 18:11:11 +00:00
Dave Richer
e43923b7a0 Merged in release/2026-05-22 (pull request #3251)
Release/2026 05 22
2026-05-20 16:54:53 +00:00
Dave Richer
e9ef429729 Merged in release/2026-05-22 (pull request #3247)
Release/2026 05 22
2026-05-14 17:56:22 +00:00
Dave Richer
db01ad9155 Merged in release/2026-05-22 (pull request #3245)
Release/2026 05 22
2026-05-13 16:10:49 +00:00
Allan Carr
8bf7fbd1f1 Merged in release/2026-05-22 (pull request #3241)
IO-3691 Job Totals Issues
2026-05-12 15:42:36 +00:00
Dave Richer
c37037ef21 Merged in release/2026-05-22 (pull request #3238)
hotfix/2020hotfix/2026-05-11 - Fix so polling throws error on missing env var (logs error), and does not start poll, vs starting polling and logging error every 10 seconds
2026-05-11 21:26:40 +00:00
Dave Richer
6050aebcd5 Merged in release/2026-05-08 (pull request #3235)
IO-3689 Customer List Restriction
2026-05-08 18:45:38 +00:00
Dave Richer
77d0f5ab38 Merged in release/2026-05-08 (pull request #3233)
feature/IO-3688-Searchable-Referral-Source - Implement (convert button)
2026-05-08 15:43:23 +00:00
Dave Richer
a0692f8c69 Merged in release/2026-05-08 (pull request #3231)
feature/IO-3688-Searchable-Referral-Source - Implement
2026-05-08 14:42:36 +00:00
Dave Richer
4f76aeb06f Merged in release/2026-05-08 (pull request #3229)
feature/IO-3679-Tech-Console-Null-Error - fix
2026-05-07 14:43:08 +00:00
Dave Richer
302a42089f Merged in release/2026-05-08 (pull request #3227)
IO-3686 River city enhancements for AR customers and Contact Code
2026-05-07 14:05:14 +00:00
Dave Richer
906265c4b2 Merged in release/2026-05-08 (pull request #3226)
feature/IO-3687-Grey-Scale-Invisible-text - implement
2026-05-06 20:46:31 +00:00
Dave Richer
388b042037 Merged in release/2026-05-08 (pull request #3223)
Release/2026 05 08
2026-05-06 15:48:38 +00:00
Dave Richer
73eb76a230 Merged in release/2026-05-08 (pull request #3220)
feature/IO-3672-Reynolds-Adjustments-V3 - Expand Export logs for Reynolds
2026-05-05 17:31:56 +00:00
Dave Richer
d5e9b79f75 Merged in release/2026-05-08 (pull request #3218)
feature/IO-3672-Reynolds-Adjustments-V3 - Hide DMS Posting sheet report in reynolds mode.
2026-05-04 21:09:10 +00:00
Dave Richer
56d0c009e2 Merged in release/2026-05-08 (pull request #3216)
feature/IO-3672-Reynolds-Adjustments-V3 - Make sure there is never a scenario where a ROGOG does not have a ROLABOR
2026-05-04 20:33:08 +00:00
Dave Richer
79030f6b36 Merged in release/2026-05-08 (pull request #3214)
feature/IO-3674-Fix-Save-And-New - Fix Save and New so state gets reset on form when starting from a new employee
2026-05-04 20:13:21 +00:00
Dave Richer
5e78cdd8ae Merged in release/2026-05-08 (pull request #3210)
Release/2026 05 08
2026-04-29 16:46:47 +00:00
Dave Richer
8f4ac866f1 Merged in release/2026-05-08 (pull request #3205)
hotfix/2026-04-28 - Add Label, fix exported
2026-04-28 17:52:54 +00:00
Dave Richer
9ad2a53bec Merged in release/2026-05-08 (pull request #3202)
hotfix/2026-04-21 - fix Parts order comments
2026-04-22 16:44:10 +00:00
Dave Richer
6590f8961b Merged in release/2026-05-08 (pull request #3200)
feature/IO-3647-Reynolds-Integration-Phase-2-Optional - Add option to make 'Enhanced Early ROS' optional
2026-04-21 14:52:31 +00:00
Dave Richer
7df71b8f44 Merged in release/2026-05-08 (pull request #3198)
hotfix/2026-04-21 - Fix save dirty state on employees causing prompt, add 'save and new'
2026-04-21 14:29:55 +00:00
Dave Richer
4776b03a21 Merged in release/2026-05-08 (pull request #3194)
hotfix/2026-04-20 - Remove item from Cost centers
2026-04-20 15:41:47 +00:00
Dave Richer
20943f74e9 Merged in release/2026-04-17 (pull request #3190)
feature/IO-3647-Reynolds-Integration-Phase-2 - Enhance early RO with meaningful amounts.
2026-04-13 14:41:37 +00:00
Dave Richer
4af312854e Merged in release/2026-04-17 (pull request #3187)
hotfix/2026-04-10 - Fix Location Identifier in chatter-api
2026-04-10 15:41:08 +00:00
Dave Richer
ff084f6fb8 Merged in release/2026-04-17 (pull request #3185)
feature/IO-3638-Reynolds-OpenSearch - Add Search on DMS id in Reynolds shops
2026-04-09 15:16:48 +00:00
Dave Richer
5c9e4517a6 Merged in release/2026-04-17 (pull request #3183)
Release/2026 04 17
2026-04-08 18:03:10 +00:00
Dave Richer
190217ffce Merged in release/2026-04-17 (pull request #3181)
Release/2026 04 17
2026-04-03 01:56:19 +00:00
Dave Richer
28dc1d4533 Merged in release/2026-04-03 (pull request #3178)
Merged in feature/IO-3637-DMS-ID-Production-Board-Column (pull request #3175)
2026-04-03 01:37:45 +00:00
Allan Carr
a97e03e0b1 Merged in feature/IO-3637-DMS-ID-Production-Board-Column (pull request #3176)
Feature/IO-3637 DMS ID Production Board Column
2026-04-02 23:07:28 +00:00
Dave Richer
e30353cab6 Merged in release/2026-04-03 (pull request #3171)
Release/2026 04 03
2026-03-31 20:19:00 +00:00
Dave Richer
c9b9f67170 Merged in release/2026-04-03 (pull request #3168)
Release/2026 04 03
2026-03-30 19:08:50 +00:00
Dave Richer
4a47f543b2 Merged in release/2026-04-03 (pull request #3164)
IO-3629 PostBatchWip Rtn != 0 error
2026-03-30 15:07:33 +00:00
Dave Richer
3b60aa89f1 Merged in release/2026-04-03 (pull request #3160)
Release/2026 04 03
2026-03-27 18:48:51 +00:00
Dave Richer
20d2572087 Merged in release/2026-04-03 (pull request #3157)
Release/2026 04 03
2026-03-25 22:36:49 +00:00
Dave Richer
ac4c09af60 Merged in release/2026-04-03 (pull request #3154)
Release/2026 04 03
2026-03-24 17:50:29 +00:00
Dave Richer
6a60af9dfe Merged in release/2026-04-03 (pull request #3150)
Release/2026 04 03
2026-03-23 17:05:19 +00:00
Dave Richer
dfb6f02864 Merged in release/2026-04-03 (pull request #3148)
Fix RR
2026-03-20 18:56:28 +00:00
Dave Richer
48bb494e0f Merged in release/2026-04-03 (pull request #3146)
IO-3515 Add shopname to bill ai feedback.
2026-03-20 18:16:04 +00:00
Dave Richer
9b74cba56b Merged in release/2026-04-03 (pull request #3144)
Release/2026 04 03
2026-03-19 22:44:56 +00:00
Dave Richer
6fc8124268 Merged in release/2026-04-03 (pull request #3141)
Release/2026 04 03
2026-03-19 18:47:24 +00:00
9 changed files with 271 additions and 127 deletions

View File

@@ -63,7 +63,9 @@ export function JobLineDispatchButton({
}
}
//joblineids: selectedLines.map((l) => l.id),
}
},
refetchQueries: ["QUERY_PARTS_BILLS_BY_JOBID", "GET_JOB_BY_PK"],
awaitRefetchQueries: true
});
if (result.errors) {
console.log("🚀 ~ handleConvert ~ result.errors:", result.errors);

View File

@@ -12,11 +12,11 @@ import { createStructuredSelector } from "reselect";
import { useNotification } from "../../contexts/Notifications/notificationContext.jsx";
import { logImEXEvent } from "../../firebase/firebase.utils";
import {
CHECK_EMPLOYEE_EMAIL,
CHECK_EMPLOYEE_NUMBER,
DELETE_VACATION,
INSERT_EMPLOYEES,
QUERY_EMPLOYEE_BY_ID,
QUERY_USERS_BY_EMAIL,
UPDATE_EMPLOYEE
} from "../../graphql/employees.queries";
import { selectBodyshop } from "../../redux/user/user.selectors";
@@ -174,9 +174,10 @@ export function ShopEmployeesFormComponent({ bodyshop, form, onDirtyChange, isDi
const handleFinish = async (values) => {
const submitAction = saveAndResetSubmitAction();
const userEmail = typeof values.user_email === "string" ? values.user_email.trim() : values.user_email;
const normalizedValues = {
...values,
user_email: values.user_email === "" ? null : values.user_email
user_email: userEmail === "" ? null : userEmail
};
if (search.employeeId && search.employeeId !== "new") {
@@ -491,18 +492,29 @@ export function ShopEmployeesFormComponent({ bodyshop, form, onDirtyChange, isDi
rules={[
({ getFieldValue }) => ({
async validator(rule, value) {
const user_email = getFieldValue("user_email");
const user_email = typeof value === "string" ? value.trim() : getFieldValue("user_email");
if (user_email && value) {
const response = await client.query({
query: QUERY_USERS_BY_EMAIL,
query: CHECK_EMPLOYEE_EMAIL,
variables: {
email: user_email
email: user_email,
shopId: bodyshop.id
}
});
if (response.data.users.length === 1) {
return Promise.resolve();
const matchingEmployees = response.data.employees_aggregate.nodes;
const currentEmployeeId = form.getFieldValue("id") ?? search.employeeId;
if (
response.data.employees_aggregate.aggregate.count === 0 ||
matchingEmployees.every((employee) => employee.id === currentEmployeeId)
) {
return Promise.resolve();
}
return Promise.reject(t("employees.validation.unique_user_email"));
}
return Promise.reject(t("bodyshop.validation.useremailmustexist"));
} else {

View File

@@ -4,6 +4,7 @@ import { fireEvent, render, screen, waitFor } from "@testing-library/react";
import { useEffect } from "react";
import { beforeEach, describe, expect, it, vi } from "vitest";
import {
CHECK_EMPLOYEE_EMAIL,
DELETE_VACATION,
INSERT_EMPLOYEES,
QUERY_EMPLOYEE_BY_ID,
@@ -16,6 +17,7 @@ const updateEmployeeMock = vi.fn();
const deleteVacationMock = vi.fn();
const useQueryMock = vi.fn();
const useMutationMock = vi.fn();
const apolloClientQueryMock = vi.fn();
const navigateMock = vi.fn();
const notification = {
error: vi.fn(),
@@ -87,6 +89,10 @@ vi.mock("react-i18next", () => ({
return "Employee number must be unique";
}
if (key === "employees.validation.unique_user_email") {
return "User email already assigned";
}
if (key === "bodyshop.validation.useremailmustexist") {
return "User email must exist";
}
@@ -203,18 +209,20 @@ describe("ShopEmployeesFormComponent", () => {
return [vi.fn()];
});
useApolloClient.mockReturnValue({
query: vi.fn().mockResolvedValue({
data: {
employees_aggregate: {
aggregate: {
count: 0
},
nodes: []
apolloClientQueryMock.mockResolvedValue({
data: {
employees_aggregate: {
aggregate: {
count: 0
},
users: []
}
})
nodes: []
},
users: []
}
});
useApolloClient.mockReturnValue({
query: apolloClientQueryMock
});
insertEmployeesMock.mockResolvedValue({
@@ -356,4 +364,59 @@ describe("ShopEmployeesFormComponent", () => {
title: "Saved"
});
});
it("blocks saving when the user email belongs to another employee in the shop", async () => {
apolloClientQueryMock.mockImplementation(({ query }) => {
if (query === CHECK_EMPLOYEE_EMAIL) {
return Promise.resolve({
data: {
users: [{ email: "jamie@example.com" }],
employees_aggregate: {
aggregate: {
count: 1
},
nodes: [{ id: "other-employee" }]
}
}
});
}
return Promise.resolve({
data: {
employees_aggregate: {
aggregate: {
count: 0
},
nodes: []
},
users: []
}
});
});
fireEvent.change(screen.getByRole("textbox", { name: "First Name" }), {
target: { value: "Jamie" }
});
fireEvent.change(screen.getByRole("textbox", { name: "Last Name" }), {
target: { value: "Rivera" }
});
fireEvent.change(screen.getByRole("textbox", { name: "Employee Number" }), {
target: { value: "42" }
});
fireEvent.change(screen.getByRole("textbox", { name: "PIN" }), {
target: { value: "1234" }
});
fireEvent.change(screen.getByRole("textbox", { name: "Hire Date" }), {
target: { value: "2026-04-20" }
});
fireEvent.change(screen.getByRole("textbox", { name: "User Email" }), {
target: { value: "jamie@example.com" }
});
fireEvent.click(screen.getByRole("button", { name: "Save Employee" }));
expect(await screen.findByText("User email already assigned")).toBeInTheDocument();
expect(insertEmployeesMock).not.toHaveBeenCalled();
expect(notification.success).not.toHaveBeenCalled();
});
});

View File

@@ -157,36 +157,36 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
</Col>
{HasFeatureAccess({ featureName: "export", bodyshop }) &&
ClosingPeriod.treatment === "on" && (
<Col xs={24} sm={12} xl={8}>
<Form.Item
key="ClosingPeriod"
name={["accountingconfig", "ClosingPeriod"]}
label={t("bodyshop.fields.closingperiod")}
>
<DatePicker.RangePicker format="MM/DD/YYYY" presets={DatePickerRanges} />
</Form.Item>
</Col>
)}
<Col xs={24} sm={12} xl={8}>
<Form.Item
key="ClosingPeriod"
name={["accountingconfig", "ClosingPeriod"]}
label={t("bodyshop.fields.closingperiod")}
>
<DatePicker.RangePicker format="MM/DD/YYYY" presets={DatePickerRanges} />
</Form.Item>
</Col>
)}
{HasFeatureAccess({ featureName: "export", bodyshop }) &&
ADPPayroll.treatment === "on" && (
<Col xs={24} sm={12} xl={8}>
<Form.Item
key="companyCode"
name={["accountingconfig", "companyCode"]}
label={t("bodyshop.fields.companycode")}
>
<Input />
</Form.Item>
</Col>
)}
<Col xs={24} sm={12} xl={8}>
<Form.Item
key="companyCode"
name={["accountingconfig", "companyCode"]}
label={t("bodyshop.fields.companycode")}
>
<Input />
</Form.Item>
</Col>
)}
{HasFeatureAccess({ featureName: "export", bodyshop }) &&
ADPPayroll.treatment === "on" && (
<Col xs={24} sm={12} xl={8}>
<Col xs={24} sm={12} xl={8}>
<Form.Item key="batchID" name={["accountingconfig", "batchID"]} label={t("bodyshop.fields.batchid")}>
<Input />
</Form.Item>
</Col>
)}
<Input />
</Form.Item>
</Col>
)}
{HasFeatureAccess({ featureName: "export", bodyshop }) && !hasDMSKey && (
<>
<Col xs={24} sm={12} xl={8}>
@@ -512,6 +512,15 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
>
<InputNumber min={0} max={100} suffix="%" />
</Form.Item>
{bodyshop.cdk_dealerid && (
<Form.Item
label={t("bodyshop.fields.dms.disablecontact")}
valuePropName="checked"
name={["cdk_configuration", "disablecontact"]}
>
<Switch />
</Form.Item>
)}
{bodyshop.pbs_serialnumber && (
<Form.Item
label={t("bodyshop.fields.dms.disablecontactvehiclecreation")}

View File

@@ -49,6 +49,22 @@ export const CHECK_EMPLOYEE_NUMBER = gql`
}
`;
export const CHECK_EMPLOYEE_EMAIL = gql`
query CHECK_EMPLOYEE_EMAIL($email: String!, $shopId: uuid!) {
users(where: { email: { _ilike: $email } }) {
email
}
employees_aggregate(where: { user_email: { _ilike: $email }, shopid: { _eq: $shopId } }) {
aggregate {
count
}
nodes {
id
}
}
}
`;
export const QUERY_ACTIVE_EMPLOYEES = gql`
query QUERY_ACTIVE_EMPLOYEES {
employees(where: { active: { _eq: true } }) {

View File

@@ -370,6 +370,7 @@
"cashierid": "Cashier ID",
"default_journal": "Default Journal",
"disablebillwip": "Disable bill WIP for A/P Posting",
"disablecontact": "Disable Contact Updates/Creation",
"disablecontactvehiclecreation": "Disable Contact & Vehicle Updates/Creation",
"dms_acctnumber": "DMS Account #",
"dms_control_override": "Static Control # Override",
@@ -1350,7 +1351,8 @@
"vacationadded": "Employee vacation added."
},
"validation": {
"unique_employee_number": "You must enter a unique employee number."
"unique_employee_number": "You must enter a unique employee number.",
"unique_user_email": "This email is already assigned to another employee."
}
},
"esignature": {

View File

@@ -370,6 +370,7 @@
"cashierid": "",
"default_journal": "",
"disablebillwip": "",
"disablecontact": "",
"disablecontactvehiclecreation": "",
"dms_acctnumber": "",
"dms_control_override": "",
@@ -1350,7 +1351,8 @@
"vacationadded": ""
},
"validation": {
"unique_employee_number": ""
"unique_employee_number": "",
"unique_user_email": "Este correo electrónico ya está asignado a otro empleado."
}
},
"esignature": {

View File

@@ -370,6 +370,7 @@
"cashierid": "",
"default_journal": "",
"disablebillwip": "",
"disablecontact": "",
"disablecontactvehiclecreation": "",
"dms_acctnumber": "",
"dms_control_override": "",
@@ -1350,7 +1351,8 @@
"vacationadded": ""
},
"validation": {
"unique_employee_number": ""
"unique_employee_number": "",
"unique_user_email": "Cette adresse courriel est déjà assignée à un autre employé."
}
},
"esignature": {

View File

@@ -15,7 +15,7 @@ const _ = require("lodash");
const moment = require("moment-timezone");
const replaceSpecialRegex = /[^a-zA-Z0-9 ]+/g;
const bypassCustomerId = "bypass";
// Helper function to handle FortellisApiError logging
function handleFortellisApiError(socket, error, functionName, additionalDetails = {}) {
if (error instanceof FortellisApiError) {
@@ -95,7 +95,8 @@ async function FortellisJobExport({ socket, redisHelpers, txEnvelope, jobid }) {
defaultFortellisTTL
);
let DMSVehCustomer;
let DMSVehCustomerFromVehicle;
//let DMSVehCustomer;
if (!DMSVid.newId) {
CreateFortellisLogEvent(socket, "DEBUG", `{2.1} Querying the Vehicle using the DMSVid: ${DMSVid.vehiclesVehId}`);
const DMSVeh = await QueryDmsVehicleById({ socket, redisHelpers, JobData, DMSVid });
@@ -106,46 +107,66 @@ async function FortellisJobExport({ socket, redisHelpers, txEnvelope, jobid }) {
DMSVeh,
defaultFortellisTTL
);
DMSVehCustomerFromVehicle = DMSVeh?.owners && DMSVeh.owners.find((o) => o.id.assigningPartyId === "CURRENT");
const DMSVehCustomerFromVehicle =
DMSVeh?.owners && DMSVeh.owners.find((o) => o.id.assigningPartyId === "CURRENT");
// //Add in contact bypass for Fortellis.
// if (!JobData.bodyshop.cdk_configuration.disablecontact) {
// const DMSVehCustomerFromVehicle =
// DMSVeh?.owners && DMSVeh.owners.find((o) => o.id.assigningPartyId === "CURRENT");
if (DMSVehCustomerFromVehicle?.id && DMSVehCustomerFromVehicle.id.value) {
CreateFortellisLogEvent(
socket,
"DEBUG",
`{2.2} Querying the Customer using the ID from DMSVeh: ${DMSVehCustomerFromVehicle.id.value}`
);
DMSVehCustomer = await QueryDmsCustomerById({
socket,
redisHelpers,
JobData,
CustomerId: DMSVehCustomerFromVehicle.id.value
});
await setSessionTransactionData(
socket.id,
getTransactionType(jobid),
FortellisCacheEnums.DMSVehCustomer,
DMSVehCustomer,
defaultFortellisTTL
);
}
// if (DMSVehCustomerFromVehicle?.id && DMSVehCustomerFromVehicle.id.value) {
// CreateFortellisLogEvent(
// socket,
// "DEBUG",
// `{2.2} Querying the Customer using the ID from DMSVeh: ${DMSVehCustomerFromVehicle.id.value}`
// );
// DMSVehCustomer = await QueryDmsCustomerById({
// socket,
// redisHelpers,
// JobData,
// CustomerId: DMSVehCustomerFromVehicle.id.value
// });
// await setSessionTransactionData(
// socket.id,
// getTransactionType(jobid),
// FortellisCacheEnums.DMSVehCustomer,
// DMSVehCustomer,
// defaultFortellisTTL
// );
// }
// }
}
CreateFortellisLogEvent(socket, "DEBUG", `{2.3} Querying the Customer using the name.`);
if (JobData.bodyshop.cdk_configuration.disablecontact) {
//Just go straight to posting.
await FortellisSelectedCustomer({ socket, redisHelpers, selectedCustomerId: bypassCustomerId, jobid });
} else {
const DMSCustList = await QueryDmsCustomerByName({ socket, redisHelpers, JobData });
await setSessionTransactionData(
socket.id,
getTransactionType(jobid),
FortellisCacheEnums.DMSCustList,
DMSCustList,
defaultFortellisTTL
);
const DMSCustList = await QueryDmsCustomerByName({ socket, redisHelpers, JobData });
await setSessionTransactionData(
socket.id,
getTransactionType(jobid),
FortellisCacheEnums.DMSCustList,
DMSCustList,
defaultFortellisTTL
);
socket.emit("fortellis-select-customer", [
...(DMSVehCustomer ? [{ ...DMSVehCustomer, vinOwner: true }] : []),
...DMSCustList
]);
socket.emit("fortellis-select-customer",
//Removed to save one one API call while disputing with fortellis.
// [
// // ...(DMSVehCustomer ? [{ ...DMSVehCustomer, vinOwner: true }] : []),
// ...DMSCustList
// ]
DMSVehCustomerFromVehicle ?
DMSCustList.map(c => {
//if customer id is the same as the current assigned owner on the vehicle id, set it as vinowner true. )
if (DMSVehCustomerFromVehicle?.id?.value === c.customerId) {
return { ...c, vinOwner: true }
} else {
return c
}
}) : DMSCustList
);
}
} catch (error) {
CreateFortellisLogEvent(socket, "ERROR", `Error in FortellisJobExport - ${error} `, {
error: error.message,
@@ -218,36 +239,40 @@ async function FortellisSelectedCustomer({ socket, redisHelpers, selectedCustome
});
return;
}
//Bypass only the customer creation. We still need to create the vehicle and update it to post the service history later on.
let DMSCust;
if (selectedCustomerId) {
CreateFortellisLogEvent(socket, "DEBUG", `{3.1} Querying the Customer using Customer ID: ${selectedCustomerId}`);
if (!JobData.bodyshop.cdk_configuration.disablecontact) {
if (selectedCustomerId) {
CreateFortellisLogEvent(socket, "DEBUG", `{3.1} Querying the Customer using Customer ID: ${selectedCustomerId}`);
//Get cust list from Redis. Return the item
const DMSCustList =
(await getSessionTransactionData(socket.id, getTransactionType(jobid), FortellisCacheEnums.DMSCustList)) || [];
const existingCustomerInDMSCustList = DMSCustList.find((c) => c.customerId === selectedCustomerId);
DMSCust = existingCustomerInDMSCustList || {
customerId: selectedCustomerId //This is the fall back in case it is the generic customer.
};
await setSessionTransactionData(
socket.id,
getTransactionType(jobid),
FortellisCacheEnums.DMSCust,
DMSCust,
defaultFortellisTTL
);
} else {
CreateFortellisLogEvent(socket, "DEBUG", `{3.2} Creating new customer.`);
const DMSCustomerInsertResponse = await InsertDmsCustomer({ socket, redisHelpers, JobData });
DMSCust = { customerId: DMSCustomerInsertResponse.data };
await setSessionTransactionData(
socket.id,
getTransactionType(jobid),
FortellisCacheEnums.DMSCust,
DMSCust,
defaultFortellisTTL
);
//Get cust list from Redis. Return the item
const DMSCustList =
(await getSessionTransactionData(socket.id, getTransactionType(jobid), FortellisCacheEnums.DMSCustList)) || [];
const existingCustomerInDMSCustList = DMSCustList.find((c) => c.customerId === selectedCustomerId);
DMSCust = existingCustomerInDMSCustList || {
customerId: selectedCustomerId //This is the fall back in case it is the generic customer.
};
await setSessionTransactionData(
socket.id,
getTransactionType(jobid),
FortellisCacheEnums.DMSCust,
DMSCust,
defaultFortellisTTL
);
} else {
CreateFortellisLogEvent(socket, "DEBUG", `{3.2} Creating new customer.`);
const DMSCustomerInsertResponse = await InsertDmsCustomer({ socket, redisHelpers, JobData });
DMSCust = { customerId: DMSCustomerInsertResponse.data };
await setSessionTransactionData(
socket.id,
getTransactionType(jobid),
FortellisCacheEnums.DMSCust,
DMSCust,
defaultFortellisTTL
);
}
}else{
DMSCust = { customerId: bypassCustomerId };
}
let DMSVeh;
@@ -258,8 +283,12 @@ async function FortellisSelectedCustomer({ socket, redisHelpers, selectedCustome
DMSVeh = await getSessionTransactionData(socket.id, getTransactionType(jobid), FortellisCacheEnums.DMSVeh);
CreateFortellisLogEvent(socket, "DEBUG", `{4.3} Updating Existing Vehicle to associate to owner.`);
//If it's a bypass scenario, skip this all.
//Check to see if the vehicle needs to be updated - i.e. the owner is not the selected customer.
if (!DMSVeh?.owners.find((o) => o.id.value === DMSCust.customerId && o.id.assigningPartyId === "CURRENT")) {
if (
selectedCustomerId !== bypassCustomerId &&
!DMSVeh?.owners.find((o) => o.id.value === DMSCust.customerId && o.id.assigningPartyId === "CURRENT")
) {
DMSVeh = await UpdateDmsVehicle({
socket,
redisHelpers,
@@ -782,12 +811,14 @@ async function InsertDmsVehicle({ socket, redisHelpers, JobData, txEnvelope, DMS
// "chassis": "",
// "color": "",
// "dealerBodyStyle": "",
deliveryDate:
txEnvelope.dms_unsold === true
? ""
: moment()
...(DMSCust?.customerId !== bypassCustomerId && {
deliveryDate:
txEnvelope.dms_unsold === true
? ""
: moment()
// .tz(JobData.bodyshop.timezone)
.format("YYYY-MM-DD"),
}),
// "deliveryMileage": 4,
// "doorsQuantity": 4,
// "engineNumber": "",
@@ -902,14 +933,17 @@ async function InsertDmsVehicle({ socket, redisHelpers, JobData, txEnvelope, DMS
// "warrantyExpDate": "2015-01-12",
// "wheelbase": ""
},
owners: [
{
id: {
assigningPartyId: "CURRENT",
value: DMSCust.customerId
// Owners is not required. Exclude it if we are bypassing.
...(DMSCust?.customerId !== bypassCustomerId && {
owners: [
{
id: {
assigningPartyId: "CURRENT",
value: DMSCust.customerId
}
}
}
]
]
})
//"inventoryAccount": "237"
}
});
@@ -1009,12 +1043,14 @@ async function UpdateDmsVehicle({ socket, redisHelpers, JobData, DMSVeh, DMSCust
modelAbrev: txEnvelope.dms_model
}
: {}),
deliveryDate:
txEnvelope.dms_unsold === true
? ""
: moment(DMSVehToSend.vehicle.deliveryDate)
...(DMSCust?.customerId !== bypassCustomerId && {
deliveryDate:
txEnvelope.dms_unsold === true
? ""
: moment(DMSVehToSend.vehicle.deliveryDate)
//.tz(JobData.bodyshop.timezone)
.toISOString()
})
},
owners: ids
}