Final updates for Fortellis to pass certification and remove logging.

This commit is contained in:
Patrick Fic
2026-01-09 10:16:33 -08:00
parent 3cf1d9a59d
commit f4b45c693a
4 changed files with 114 additions and 54 deletions

View File

@@ -133,16 +133,23 @@ export function ExportLogsPageComponent() {
title: t("general.labels.message"), title: t("general.labels.message"),
dataIndex: "message", dataIndex: "message",
key: "message", key: "message",
render: (text, record) => render: (text, record) => {
record.message && ( if (!record.message) return null;
<div> const message = JSON.parse(record.message);
<ul> if (Array.isArray(message)) {
{JSON.parse(record.message).map((m, idx) => ( return (
<li key={idx}>{m}</li> <div>
))} <ul>
</ul> {message.map((m, idx) => (
</div> <li key={idx}>{m}</li>
) ))}
</ul>
</div>
);
} else {
return <div>{record.message}</div>;
}
}
} }
]; ];

View File

@@ -11,7 +11,7 @@ const vehicletype = async (req, res) => {
if (!model || model.trim() === "") { if (!model || model.trim() === "") {
res.status(400).json({ success: false, error: "Please provide a model" }); res.status(400).json({ success: false, error: "Please provide a model" });
} else { } else {
vehicle
const type = getVehicleType(model.trim()) const type = getVehicleType(model.trim())
res.status(200).json({ success: true, ...type }); res.status(200).json({ success: true, ...type });
} }

View File

@@ -38,8 +38,6 @@ const defaultFortellisTTL = 60 * 60;
async function GetAuthToken() { async function GetAuthToken() {
//Done with Authorization Code Flow //Done with Authorization Code Flow
//https://docs.fortellis.io/docs/tutorials/solution-integration/authorization-code-flow/ //https://docs.fortellis.io/docs/tutorials/solution-integration/authorization-code-flow/
//TODO: This should get stored in the redis cache and only be refreshed when it expires.
const { const {
data: { access_token, expires_in, token_type } data: { access_token, expires_in, token_type }
} = await axios.post( } = await axios.post(
@@ -221,6 +219,22 @@ async function MakeFortellisCall({
console.log(JSON.stringify(result.data, null, 4)); console.log(JSON.stringify(result.data, null, 4));
} }
// await writeFortellisLogToFile({
// timestamp: new Date().toISOString(),
// reqId: ReqId,
// url: fullUrl,
// request:
// {
// requestcurl: result.config.curlCommand,
// reqid: result.config.headers["Request-Id"] || null,
// subscriptionId: result.config.headers["Subscription-Id"] || null,
// resultdata: result.data,
// resultStatus: result.status
// },
// user: socket?.user?.email,
// jobid: socket?.recordid
// });
if (result.data.checkStatusAfterSeconds) { if (result.data.checkStatusAfterSeconds) {
return DelayedCallback({ return DelayedCallback({
delayMeta: result.data, delayMeta: result.data,
@@ -231,6 +245,8 @@ async function MakeFortellisCall({
}); });
} }
logger.log( logger.log(
"fortellis-log-event-json", "fortellis-log-event-json",
"DEBUG", "DEBUG",
@@ -256,6 +272,21 @@ async function MakeFortellisCall({
errorStatusText: error.response?.statusText, errorStatusText: error.response?.statusText,
originalError: error originalError: error
}; };
// await writeFortellisLogToFile({
// timestamp: new Date().toISOString(),
// reqId: ReqId,
// url: fullUrl,
// request:
// {
// requestcurl: error.config.curlCommand,
// reqid: error.config.headers["Request-Id"] || null,
// subscriptionId: error.config.headers["Subscription-Id"] || null,
// resultdata: error.message,
// resultStatus: error.status
// },
// user: socket?.user?.email,
// jobid: socket?.recordid
// });
logger.log( logger.log(
"fortellis-log-event-error", "fortellis-log-event-error",
@@ -263,10 +294,11 @@ async function MakeFortellisCall({
socket?.user?.email, socket?.user?.email,
socket?.recordid, socket?.recordid,
{ {
wsmessage: "",//message, requestcurl: error.config.curlCommand,
curl: error?.config.curlCommand, reqid: error.config.headers["Request-Id"] || null,
reqid: error.config?.headers["Request-Id"] || null, subscriptionId: error.config.headers["Subscription-Id"] || null,
subscriptionId: error.config?.headers["Subscription-Id"] || null, resultdata: error.message,
resultStatus: error.status
}, },
true true
); );
@@ -302,6 +334,19 @@ async function DelayedCallback({ delayMeta, access_token, SubscriptionID, ReqId,
//"Department-Id": departmentIds[0].id //"Department-Id": departmentIds[0].id
} }
}); });
// await writeFortellisLogToFile({
// timestamp: new Date().toISOString(),
// reqId: ReqId,
// url: statusResult.data._links.result.href,
// request:
// {
// requestcurl: batchResult.config.curlCommand,
// reqid: batchResult.config.headers["Request-Id"] || null,
// subscriptionId: batchResult.config.headers["Subscription-Id"] || null,
// resultdata: batchResult.data,
// resultStatus: batchResult.status
// },
// });
return batchResult; return batchResult;
} else { } else {
return "Error!!! Still need to implement batch waiting."; return "Error!!! Still need to implement batch waiting.";
@@ -313,6 +358,7 @@ function sleep(ms) {
} }
async function writeFortellisLogToFile(logObject) { async function writeFortellisLogToFile(logObject) {
//The was only used for the certification. Commented out in case of future need.
try { try {
const logsDir = path.join(process.cwd(), 'logs'); const logsDir = path.join(process.cwd(), 'logs');
const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
@@ -336,9 +382,6 @@ async function writeFortellisLogToFile(logObject) {
async function WriteToIOEventLog({ apiName, type, fullUrl, bodyshopid, useremail, logger, socket }) { async function WriteToIOEventLog({ apiName, type, fullUrl, bodyshopid, useremail, logger, socket }) {
try { try {
await client.request(INSERT_IOEVENT, { await client.request(INSERT_IOEVENT, {
event: { event: {
operationname: `fortellis-${apiName}-${type}`, operationname: `fortellis-${apiName}-${type}`,

View File

@@ -242,7 +242,7 @@ async function FortellisSelectedCustomer({ socket, redisHelpers, selectedCustome
JobData, JobData,
DMSVeh, DMSVeh,
DMSCust, DMSCust,
selectedCustomerId, selectedCustomerId: selectedCustomerId || DMSCust.customerId,
txEnvelope txEnvelope
}); });
await setSessionTransactionData( await setSessionTransactionData(
@@ -293,14 +293,14 @@ async function FortellisSelectedCustomer({ socket, redisHelpers, selectedCustome
try { try {
CreateFortellisLogEvent(socket, "DEBUG", `{7} Updating Service Vehicle History.`); CreateFortellisLogEvent(socket, "DEBUG", `{7} Updating Service Vehicle History.`);
const DMSVehHistory = await InsertServiceVehicleHistory({ socket, redisHelpers, JobData }); const DMSVehHistory = await InsertServiceVehicleHistory({ socket, redisHelpers, JobData });
await setSessionTransactionData( await setSessionTransactionData(
socket.id, socket.id,
getTransactionType(jobid), getTransactionType(jobid),
FortellisCacheEnums.DMSVehHistory, FortellisCacheEnums.DMSVehHistory,
DMSVehHistory, DMSVehHistory,
defaultFortellisTTL defaultFortellisTTL
); );
} catch (error) { } catch (error) {
CreateFortellisLogEvent(socket, "ERROR", `{7.1} Error posting vehicle service history. ${error.message}`); CreateFortellisLogEvent(socket, "ERROR", `{7.1} Error posting vehicle service history. ${error.message}`);
@@ -346,7 +346,7 @@ async function FortellisSelectedCustomer({ socket, redisHelpers, selectedCustome
stack: error.stack, stack: error.stack,
data: error.errorData data: error.errorData
}); });
await InsertFailedExportLog({ socket, JobData, error }); await InsertFailedExportLog({ socket, JobData, error: error.errorData?.issues || [JSON.stringify(error.errorData)] });
} finally { } finally {
//Ensure we always insert logEvents //Ensure we always insert logEvents
//GQL to insert logevents. //GQL to insert logevents.
@@ -430,10 +430,11 @@ async function QueryDmsCustomerById({ socket, redisHelpers, JobData, CustomerId
async function QueryDmsCustomerByName({ socket, redisHelpers, JobData }) { async function QueryDmsCustomerByName({ socket, redisHelpers, JobData }) {
const ownerName = const ownerName =
JobData.ownr_co_nm && JobData.ownr_co_nm.trim() !== "" JobData.ownr_co_nm && JobData.ownr_co_nm.trim() !== ""
? [["lastName", JobData.ownr_co_nm.replace(replaceSpecialRegex, "")]] //? [["firstName", JobData.ownr_co_nm.replace(replaceSpecialRegex, "").toUpperCase()]] // Commented out until we receive direction.
? [["email", JobData.ownr_ea.toUpperCase()]]
: [ : [
["firstName", JobData.ownr_fn.replace(replaceSpecialRegex, "")], ["firstName", JobData.ownr_fn.replace(replaceSpecialRegex, "").toUpperCase()],
["lastName", JobData.ownr_ln.replace(replaceSpecialRegex, "")] ["lastName", JobData.ownr_ln.replace(replaceSpecialRegex, "").toUpperCase()]
]; ];
try { try {
const result = await MakeFortellisCall({ const result = await MakeFortellisCall({
@@ -457,6 +458,7 @@ async function QueryDmsCustomerByName({ socket, redisHelpers, JobData }) {
async function InsertDmsCustomer({ socket, redisHelpers, JobData }) { async function InsertDmsCustomer({ socket, redisHelpers, JobData }) {
try { try {
const isBusiness = (JobData.ownr_co_nm && JobData.ownr_co_nm.replace(replaceSpecialRegex, "").trim() !== "")
const result = await MakeFortellisCall({ const result = await MakeFortellisCall({
...FortellisActions.CreateCustomer, ...FortellisActions.CreateCustomer,
headers: {}, headers: {},
@@ -464,28 +466,32 @@ async function InsertDmsCustomer({ socket, redisHelpers, JobData }) {
socket, socket,
jobid: JobData.id, jobid: JobData.id,
body: { body: {
customerType: "INDIVIDUAL", customerType: isBusiness ? "BUSINESS" : "INDIVIDUAL",
customerName: { ...isBusiness ? {
//"suffix": "Mr.", companyName: JobData.ownr_co_nm && JobData.ownr_co_nm.replace(replaceSpecialRegex, "").toUpperCase(),
firstName: JobData.ownr_fn && JobData.ownr_fn.replace(replaceSpecialRegex, "").toUpperCase(), secondaryCustomerName: {
//"middleName": "", //lastName: JobData.ownr_co_nm && JobData.ownr_co_nm.replace(replaceSpecialRegex, "").toUpperCase()
lastName: JobData.ownr_ln && JobData.ownr_ln.replace(replaceSpecialRegex, "").toUpperCase() }
//"title": "", } : {
//"nickName": "" customerName: {
//"suffix": "Mr.",
firstName: JobData.ownr_fn && JobData.ownr_fn.replace(replaceSpecialRegex, "").toUpperCase(),
//"middleName": "",
lastName: JobData.ownr_ln && JobData.ownr_ln.replace(replaceSpecialRegex, "").toUpperCase()
//"title": "",
//"nickName": ""
}
}, },
companyName: JobData.ownr_co_nm && JobData.ownr_co_nm.replace(replaceSpecialRegex, "").toUpperCase(),
postalAddress: { postalAddress: {
addressLine1: JobData.ownr_addr1?.replace(replaceSpecialRegex, "").trim(), addressLine1: JobData.ownr_addr1?.replace(replaceSpecialRegex, "").trim().toUpperCase(),
addressLine2: JobData.ownr_addr2?.replace(replaceSpecialRegex, "").trim(), addressLine2: JobData.ownr_addr2?.replace(replaceSpecialRegex, "").trim().toUpperCase(),
city: JobData.ownr_city?.replace(replaceSpecialRegex, "").trim(), city: JobData.ownr_city?.replace(replaceSpecialRegex, "").trim().toUpperCase(),
state: JobData.ownr_state?.replace(replaceSpecialRegex, "").trim(),
postalCode: InstanceMgr({ postalCode: InstanceMgr({
imex: JobData.ownr_zip && JobData.ownr_zip.toUpperCase().replace(/\W/g, "").replace(/(...)/, "$1 "), imex: JobData.ownr_zip && JobData.ownr_zip.toUpperCase().replace(/\W/g, "").replace(/(...)/, "$1 "),
rome: JobData.ownr_zip rome: JobData.ownr_zip
}), }),
//"county": JobData.ownr_county?.trim(), state: JobData.ownr_st?.replace(replaceSpecialRegex, "").trim().toUpperCase(),
country: JobData.ownr_ctry?.replace(replaceSpecialRegex, "").trim(), country: JobData.ownr_ctry?.replace(replaceSpecialRegex, "").trim().toUpperCase(),
province: JobData.ownr_st?.replace(replaceSpecialRegex, "").trim()
//"territory": "" //"territory": ""
}, },
// "birthDate": { // "birthDate": {
@@ -537,7 +543,7 @@ async function InsertDmsCustomer({ socket, redisHelpers, JobData }) {
? [ ? [
{ {
//"uuid": "", //"uuid": "",
address: JobData.ownr_ea, address: JobData.ownr_ea.toUpperCase(),
type: "PERSONAL" type: "PERSONAL"
// "doNotEmailSource": "", // "doNotEmailSource": "",
// "doNotEmail": false, // "doNotEmail": false,
@@ -836,7 +842,7 @@ async function InsertDmsVehicle({ socket, redisHelpers, JobData, txEnvelope, DMS
// "vehicleStatus": "G", // "vehicleStatus": "G",
// "vehicleStock": "82268", // "vehicleStock": "82268",
// "vehicleWeight": "6800", // "vehicleWeight": "6800",
vin: JobData.v_vin vin: JobData.v_vin.toUpperCase()
// "warrantyExpDate": "2015-01-12", // "warrantyExpDate": "2015-01-12",
// "wheelbase": "" // "wheelbase": ""
}, },
@@ -898,7 +904,7 @@ async function UpdateDmsVehicle({ socket, redisHelpers, JobData, DMSVeh, DMSCust
{ {
id: { id: {
assigningPartyId: "PREVIOUS", assigningPartyId: "PREVIOUS",
value: oldOwner.id value: oldOwner.id.value
} }
} }
] ]
@@ -994,7 +1000,7 @@ async function InsertServiceVehicleHistory({ socket, redisHelpers, JobData }) {
openTime: moment(JobData.actual_in).tz(JobData.bodyshop.timezone).format("HH:mm:ss"), openTime: moment(JobData.actual_in).tz(JobData.bodyshop.timezone).format("HH:mm:ss"),
closeDate: moment(JobData.invoice_date).tz(JobData.bodyshop.timezone).format("YYYY-MM-DD"), closeDate: moment(JobData.invoice_date).tz(JobData.bodyshop.timezone).format("YYYY-MM-DD"),
closeTime: moment(JobData.invoice_date).tz(JobData.bodyshop.timezone).format("HH:mm:ss"), closeTime: moment(JobData.invoice_date).tz(JobData.bodyshop.timezone).format("HH:mm:ss"),
comments: txEnvelope.story?.slice(0, 40), // has to be between 0 and 40. comments: txEnvelope.story?.slice(0, 40).toUpperCase(), // has to be between 0 and 40.
cashierId: JobData.bodyshop.cdk_configuration.cashierid, cashierId: JobData.bodyshop.cdk_configuration.cashierid,
referenceNumber: JobData.ro_number.match(/\d+/g)[0] referenceNumber: JobData.ro_number.match(/\d+/g)[0]
} }
@@ -1026,7 +1032,7 @@ async function InsertDmsStartWip({ socket, redisHelpers, JobData }) {
jobid: JobData.id, jobid: JobData.id,
body: { body: {
acctgDate: moment().tz(JobData.bodyshop.timezone).format("YYYY-MM-DD"), acctgDate: moment().tz(JobData.bodyshop.timezone).format("YYYY-MM-DD"),
desc: txEnvelope.story && txEnvelope.story.replace(replaceSpecialRegex, ""), desc: txEnvelope.story && txEnvelope.story.replace(replaceSpecialRegex, "").toUpperCase(),
docType: "10", docType: "10",
m13Flag: "0", m13Flag: "0",
refer: JobData.ro_number, refer: JobData.ro_number,
@@ -1039,6 +1045,10 @@ async function InsertDmsStartWip({ socket, redisHelpers, JobData }) {
userID: JobData.bodyshop.cdk_configuration.cashierid, userID: JobData.bodyshop.cdk_configuration.cashierid,
userName: "IMEX" userName: "IMEX"
//Cert Values Below
// userID: "partprgm",
// userName: "PROGRAM, PARTNER"
// acctgDate: "2025-07-07", // acctgDate: "2025-07-07",
// desc: "DOCUMENT DESC. OPTIONAL REQUIREMENT", // desc: "DOCUMENT DESC. OPTIONAL REQUIREMENT",
// docType: "3", // docType: "3",