Compare commits

...

17 Commits

Author SHA1 Message Date
Patrick Fic
4043bd3d33 Merged in feature/IO-3722-disable-contact-fortellis (pull request #3286)
IO-3722 Remove delivery date for bypass vehicles.

Approved-by: Dave Richer
2026-05-28 18:33:50 +00:00
Patrick Fic
c1c0b35c8f IO-3722 Remove delivery date for bypass vehicles. 2026-05-28 11:32:23 -07:00
Patrick Fic
4fd2f034a3 Merged in feature/IO-3722-disable-contact-fortellis (pull request #3283)
IO-3722 Remove customer lookup by Vehicle Owner.

Approved-by: Dave Richer
2026-05-28 16:55:21 +00:00
Patrick Fic
aa3b303fe9 IO-3722 Remove customer lookup by Vehicle Owner. 2026-05-28 09:53:40 -07:00
Patrick Fic
bd25245290 Merged in feature/IO-3722-disable-contact-fortellis (pull request #3281)
IO-3722 Fix undefined customer ref.
2026-05-27 21:19:02 +00:00
Patrick Fic
468ed23f73 IO-3722 Fix undefined customer ref. 2026-05-27 14:18:31 -07:00
Patrick Fic
6472b053ed Merged in feature/IO-3722-disable-contact-fortellis (pull request #3280)
Resolve inversed if statement.
2026-05-27 19:54:30 +00:00
Patrick Fic
322ebd3bc7 Resolve inversed if statement. 2026-05-27 12:46:09 -07:00
Patrick Fic
169070594c Merged in feature/IO-3722-disable-contact-fortellis (pull request #3279)
IO-3722 Add additional await.
2026-05-27 19:42:38 +00:00
Patrick Fic
0f800c5a4c IO-3722 Add additional await. 2026-05-27 12:40:41 -07:00
Dave Richer
0974e69a50 Merged in feature/IO-3722-disable-contact-fortellis (pull request #3277)
IO-3722 Disable contact API calls for Fortellis.
2026-05-27 18:36:51 +00:00
Patrick FIc
345a470731 IO-3722 Disable contact API calls for Fortellis. 2026-05-27 10:31:33 -07:00
Dave Richer
ebde2f1581 Merged in release/2026-05-22 (pull request #3257)
Release/2026 05 22
2026-05-25 12:45:19 +00:00
Allan Carr
a45808eb94 Merged in feature/IO-3710-Visual-Board-Vehicle-Color (pull request #3255)
IO-3710 Visual Board Vehicle Color

Approved-by: Dave Richer
2026-05-20 23:57:28 +00:00
Allan Carr
a2389b1f26 IO-3710 Visual Board Vehicle Color
Signed-off-by: Allan Carr <allan@imexsystems.ca>
2026-05-20 13:42:35 -07:00
Dave
ab606a4266 release/2026-05-22 - Remove uncessary require 2026-05-20 14:46:52 -04:00
Patrick Fic
da317704c4 Merged in feature/IO-3712-disable-analytics (pull request #3252)
IO-3712 Disable analytics in client side.

Approved-by: Dave Richer
2026-05-20 18:10:39 +00:00
7 changed files with 163 additions and 112 deletions

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

@@ -44,9 +44,7 @@ export default function VehiclesListComponent({ loading, vehicles, total, refetc
key: "description",
render: (text, record) => {
return (
<span>{`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${
record.v_model_desc || ""
} ${record.v_color || ""}`}</span>
<span>{`${record.v_model_yr || ""} ${record.v_color || ""} ${record.v_make_desc || ""} ${record.v_model_desc || ""} `}</span>
);
}
},
@@ -111,7 +109,13 @@ export default function VehiclesListComponent({ loading, vehicles, total, refetc
>
<ResponsiveTable
loading={loading}
pagination={{ placement: "top", pageSize: currentPageSize, current: currentPage, showSizeChanger: true, total: total }}
pagination={{
placement: "top",
pageSize: currentPageSize,
current: currentPage,
showSizeChanger: true,
total: total
}}
columns={columns}
mobileColumnKeys={["v_vin", "description", "plate_no"]}
rowKey="id"

View File

@@ -9,14 +9,13 @@ import {
} from "@firebase/auth";
import { arrayUnion, doc, getDoc, setDoc, updateDoc } from "@firebase/firestore";
import { getToken } from "@firebase/messaging";
import * as Sentry from "@sentry/react";
// import * as Sentry from "@sentry/react";
import { notification } from "antd";
import axios from "axios";
import i18next from "i18next";
//import LogRocket from "logrocket";
import { all, call, delay, put, select, takeLatest } from "redux-saga/effects";
import {
//analytics,
auth,
firestore,
getCurrentUser,

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",

View File

@@ -370,6 +370,7 @@
"cashierid": "",
"default_journal": "",
"disablebillwip": "",
"disablecontact": "",
"disablecontactvehiclecreation": "",
"dms_acctnumber": "",
"dms_control_override": "",

View File

@@ -370,6 +370,7 @@
"cashierid": "",
"default_journal": "",
"disablebillwip": "",
"disablecontact": "",
"disablecontactvehiclecreation": "",
"dms_acctnumber": "",
"dms_control_override": "",

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
}