Merge branch 'feature/IO-3357-Reynolds-and-Reynolds-DMS-API-Integration' of bitbucket.org:snaptsoft/bodyshop into feature/IO-3357-Reynolds-and-Reynolds-DMS-API-Integration

This commit is contained in:
Dave
2025-12-18 11:38:18 -05:00
6 changed files with 186 additions and 31 deletions

2
.gitignore vendored
View File

@@ -114,7 +114,7 @@ firebase/.env
!.elasticbeanstalk/*.cfg.yml
!.elasticbeanstalk/*.global.yml
logs/oAuthClient-log.log
logs/*
.node-persist/**

View File

@@ -1,4 +1,4 @@
import { Button } from "antd";
import { Button, Space } from "antd";
import axios from "axios";
import { useState } from "react";
import { useTranslation } from "react-i18next";
@@ -42,9 +42,29 @@ export function DmsCdkMakesRefetch({ currentUser, bodyshop }) {
setLoading(false);
};
const handleGetCOA = async () => {
try {
setLoading(true);
await axios.post(`cdk/fortellis/getCOA`, {
cdk_dealerid: bodyshop.cdk_dealerid,
bodyshopid: bodyshop.id
});
} catch (error) {
console.error(error);
}
setLoading(false);
};
return (
<Button loading={loading} onClick={handleRefetch}>
{t("jobs.actions.dms.refetchmakesmodels")}
</Button>
<Space>
<Button loading={loading} onClick={handleRefetch}>
{t("jobs.actions.dms.refetchmakesmodels")}
</Button>
<Button loading={loading} onClick={handleGetCOA}>
Get COA
</Button>
</Space>
);
}

View File

@@ -131,6 +131,7 @@ async function GetFortellisMakes(req, cdk_dealerid) {
socket: { emit: () => null },
jobid: null,
body: {},
debug: true,
SubscriptionObject: {
SubscriptionID: cdk_dealerid
}
@@ -147,6 +148,45 @@ async function GetFortellisMakes(req, cdk_dealerid) {
}
}
async function GetFortellisCOA(req) {
const { cdk_dealerid } = req.body;
try {
logger.log("fortellis-get-COA", "DEBUG", req.user?.email, null, {
cdk_dealerid
});
const result = await MakeFortellisCall({
...FortellisActions.GetCOA,
headers: {},
redisHelpers: {
setSessionTransactionData: () => {
return null;
},
getSessionTransactionData: () => {
return null;
}
},
socket: { emit: () => null },
jobid: null,
body: {},
debug: true,
SubscriptionObject: {
SubscriptionID: cdk_dealerid
}
});
return result.data?.data;
} catch (error) {
logger.log("fortellis-get-COA-error", "ERROR", req.user?.email, null, {
cdk_dealerid,
error: error.message,
stack: error.stack
});
throw new Error(error);
}
}
exports.fortellisCOA = GetFortellisCOA
exports.fortellis = async function ReloadFortellisMakes(req, res) {
const { bodyshopid, cdk_dealerid } = req.body;
try {

View File

@@ -1,11 +1,13 @@
const path = require("path");
const fs = require("fs").promises;
require("dotenv").config({
path: path.resolve(process.cwd(), `.env.${process.env.NODE_ENV || "development"}`)
});
const client = require("../graphql-client/graphql-client").client;
// const CalcualteAllocations = require("../cdk/cdk-calculate-allocations").default;
const CreateFortellisLogEvent = require("./fortellis-logger");
const logger = require("../utils/logger");
const { INSERT_IOEVENT } = require("../graphql-client/queries");
const uuid = require("uuid").v4;
const AxiosLib = require("axios").default;
const axios = AxiosLib.create();
@@ -157,6 +159,19 @@ async function MakeFortellisCall({
console.log(`Body Contents: ${JSON.stringify(body, null, 4)}`);
}
//Write to the IOEvent Log
await WriteToIOEventLog({
apiName,
fullUrl,
logger,
socket,
useremail: socket.user?.email,
type,
bodyshopid: ""
//bodyshopid: job.bodyshop.id,
})
try {
let result;
switch (type) {
@@ -297,6 +312,67 @@ function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
async function writeFortellisLogToFile(logObject) {
try {
const logsDir = path.join(process.cwd(), 'logs');
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
const filename = `fortellis-${timestamp}.json`;
const filepath = path.join(logsDir, filename);
console.log(`[writeFortellisLogToFile] Writing to: ${filepath}`);
console.log(`[writeFortellisLogToFile] process.cwd(): ${process.cwd()}`);
// Ensure logs directory exists
await fs.mkdir(logsDir, { recursive: true });
// Write log object to file
await fs.writeFile(filepath, JSON.stringify(logObject, null, 2), 'utf8');
console.log(`Fortellis log written to: ${filepath}`,);
} catch (err) {
console.error('Failed to write Fortellis log to file:', err);
}
}
async function WriteToIOEventLog({ apiName, type, fullUrl, bodyshopid, useremail, logger, socket }) {
try {
await client.request(INSERT_IOEVENT, {
event: {
operationname: `fortellis-${apiName}-${type}`,
//time,
//dbevent,
env: isProduction ? "production" : "test",
variables: { fullUrl, type, apiName },
bodyshopid: socket.bodyshopId,
useremail
}
});
} catch (error) {
logger.log(
"fortellis-tracking-error",
"ERROR",
socket?.user?.email,
socket?.recordid,
{
operationname: `fortellis-${apiName}-`,
//time,
//dbevent,
env: isProduction ? "production" : "test",
variables: {},
bodyshopid,
useremail,
error: error.message,
stack: error.stack
},
true
);
}
}
const isProduction = process.env.NODE_ENV === "production";
//Get requests should have the trailing slash as they are used that way in the calls.
@@ -373,8 +449,10 @@ const FortellisActions = {
},
GetCOA: {
type: "get",
apiName: "CDK Drive Post Accounts GL WIP",
url: `https://api.fortellis.io/cdk-test/drive/chartofaccounts/v2/bulk/`,
apiName: "CDK Drive Get Chart of Accounts",
url: isProduction
? "https://api.fortellis.io/cdk/drive/chartofaccounts/v2/bulk"
: "https://api.fortellis.io/cdk-test/drive/chartofaccounts/v2/bulk",
waitForResult: true
},
StartWip: {

View File

@@ -189,7 +189,17 @@ async function FortellisSelectedCustomer({ socket, redisHelpers, selectedCustome
let DMSCust;
if (selectedCustomerId) {
CreateFortellisLogEvent(socket, "DEBUG", `{3.1} Querying the Customer using Customer ID: ${selectedCustomerId}`);
DMSCust = await QueryDmsCustomerById({ socket, redisHelpers, JobData, CustomerId: selectedCustomerId }); //TODO: Optimize here - if selected ID, we already have the customer information. Prevent an extra call to Fortellis.
//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),
@@ -197,6 +207,8 @@ async function FortellisSelectedCustomer({ socket, redisHelpers, selectedCustome
DMSCust,
defaultFortellisTTL
);
} else {
CreateFortellisLogEvent(socket, "DEBUG", `{3.2} Creating new customer.`);
const DMSCustomerInsertResponse = await InsertDmsCustomer({ socket, redisHelpers, JobData });
@@ -215,26 +227,32 @@ async function FortellisSelectedCustomer({ socket, redisHelpers, selectedCustome
CreateFortellisLogEvent(socket, "DEBUG", `{4.1} Inserting new vehicle with ID: ID ${DMSVid.vehiclesVehId}`);
DMSVeh = await InsertDmsVehicle({ socket, redisHelpers, JobData, txEnvelope, DMSVid, DMSCust });
} else {
CreateFortellisLogEvent(socket, "DEBUG", `{4.2} Querying Existing Vehicle using ID ${DMSVid.vehiclesVehId}`);
DMSVeh = await QueryDmsVehicleById({ socket, redisHelpers, JobData, DMSVid });
CreateFortellisLogEvent(socket, "DEBUG", `{4.3} Updating Existing Vehicle to associate to owner.`);
DMSVeh = await UpdateDmsVehicle({
socket,
redisHelpers,
JobData,
DMSVeh,
DMSCust,
selectedCustomerId,
txEnvelope
});
await setSessionTransactionData(
DMSVeh = await getSessionTransactionData(
socket.id,
getTransactionType(jobid),
FortellisCacheEnums.DMSVeh,
DMSVeh,
defaultFortellisTTL
); //TODO: This should be immutable probably.F
FortellisCacheEnums.DMSVeh
)
CreateFortellisLogEvent(socket, "DEBUG", `{4.3} Updating Existing Vehicle to associate to owner.`);
//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")) {
DMSVeh = await UpdateDmsVehicle({
socket,
redisHelpers,
JobData,
DMSVeh,
DMSCust,
selectedCustomerId,
txEnvelope
});
await setSessionTransactionData(
socket.id,
getTransactionType(jobid),
FortellisCacheEnums.DMSVeh,
DMSVeh,
defaultFortellisTTL
);
}
}
// const DMSVehHistory = await InsertServiceVehicleHistory({ socket, redisHelpers, JobData });
// await setSessionTransactionData(socket.id, getTransactionType(jobid), FortellisCacheEnums.DMSVehHistory, DMSVehHistory, defaultFortellisTTL);
@@ -293,7 +311,6 @@ async function FortellisSelectedCustomer({ socket, redisHelpers, selectedCustome
// //Get the error code
CreateFortellisLogEvent(socket, "DEBUG", `{6.1} Getting errors for Transaction ID ${DMSTransHeader.transID}`);
await QueryDmsErrWip({ socket, redisHelpers, JobData });
const DmsError = await QueryDmsErrWip({ socket, redisHelpers, JobData });
// //Delete the transaction
@@ -301,7 +318,7 @@ async function FortellisSelectedCustomer({ socket, redisHelpers, selectedCustome
await DeleteDmsWip({ socket, redisHelpers, JobData });
DmsError.errLine.map(
DmsError.errMsg.map(
(e) =>
e !== null &&
e !== "" &&
@@ -310,7 +327,7 @@ async function FortellisSelectedCustomer({ socket, redisHelpers, selectedCustome
await InsertFailedExportLog({
socket,
JobData,
error: DmsError.errLine
error: DmsError.errMsg
});
}
}
@@ -1027,7 +1044,6 @@ async function InsertDmsStartWip({ socket, redisHelpers, JobData }) {
// userID: "partprgm",
// userName: "PROGRAM, PARTNER"
},
//overrideDepartmentId: "D100152198" //TODO: REMOVE AFTER TESTING
});
return result;
} catch (error) {

View File

@@ -9,6 +9,7 @@ router.use(validateFirebaseIdTokenMiddleware);
router.post("/getvehicles", withUserGraphQLClientMiddleware, cdkGetMake.default);
router.post("/fortellis/getvehicles", withUserGraphQLClientMiddleware, cdkGetMake.fortellis);
router.post("/fortellis/getCOA", withUserGraphQLClientMiddleware, cdkGetMake.fortellisCOA);
router.post("/calculate-allocations", withUserGraphQLClientMiddleware, cdkCalculateAllocations.defaultRoute);
module.exports = router;