diff --git a/.gitignore b/.gitignore index 3e33d8a2b..590787f08 100644 --- a/.gitignore +++ b/.gitignore @@ -114,7 +114,7 @@ firebase/.env !.elasticbeanstalk/*.cfg.yml !.elasticbeanstalk/*.global.yml logs/oAuthClient-log.log - +logs/* .node-persist/** diff --git a/client/src/components/dms-cdk-makes/dms-cdk-makes.refetch.component.jsx b/client/src/components/dms-cdk-makes/dms-cdk-makes.refetch.component.jsx index 641f0e8c1..0020cbfd5 100644 --- a/client/src/components/dms-cdk-makes/dms-cdk-makes.refetch.component.jsx +++ b/client/src/components/dms-cdk-makes/dms-cdk-makes.refetch.component.jsx @@ -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 ( - + + + + ); } diff --git a/server/cdk/cdk-get-makes.js b/server/cdk/cdk-get-makes.js index 962a1af9d..319ecd131 100644 --- a/server/cdk/cdk-get-makes.js +++ b/server/cdk/cdk-get-makes.js @@ -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 { diff --git a/server/fortellis/fortellis-helpers.js b/server/fortellis/fortellis-helpers.js index b179de3e3..62ac1c0e2 100644 --- a/server/fortellis/fortellis-helpers.js +++ b/server/fortellis/fortellis-helpers.js @@ -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: { diff --git a/server/fortellis/fortellis.js b/server/fortellis/fortellis.js index 7862dbda8..bb808e36b 100644 --- a/server/fortellis/fortellis.js +++ b/server/fortellis/fortellis.js @@ -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) { diff --git a/server/routes/cdkRoutes.js b/server/routes/cdkRoutes.js index 056142842..6a32a3af2 100644 --- a/server/routes/cdkRoutes.js +++ b/server/routes/cdkRoutes.js @@ -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;