From e7c4797fef4b177397a09f7cb866843cc38c8a6f Mon Sep 17 00:00:00 2001 From: Patrick Fic Date: Mon, 17 Mar 2025 10:49:02 -0700 Subject: [PATCH] IO-2776 Add additional redis helpers, restructure some fortellis calls. --- .../dms-post-form/dms-post-form.component.jsx | 43 +- server/fortellis/fortellis-helpers.js | 253 ++++ server/fortellis/fortellis-logger.js | 9 + server/fortellis/fortellis.js | 1047 +++++++++++++++++ server/utils/redisHelpers.js | 54 +- server/web-sockets/redisSocketEvents.js | 46 +- 6 files changed, 1435 insertions(+), 17 deletions(-) create mode 100644 server/fortellis/fortellis-helpers.js create mode 100644 server/fortellis/fortellis-logger.js create mode 100644 server/fortellis/fortellis.js diff --git a/client/src/components/dms-post-form/dms-post-form.component.jsx b/client/src/components/dms-post-form/dms-post-form.component.jsx index 533a611bb..419fd771c 100644 --- a/client/src/components/dms-post-form/dms-post-form.component.jsx +++ b/client/src/components/dms-post-form/dms-post-form.component.jsx @@ -27,6 +27,7 @@ import DmsCdkMakesRefetch from "../dms-cdk-makes/dms-cdk-makes.refetch.component import CurrencyInput from "../form-items-formatted/currency-form-item.component"; import LayoutFormRow from "../layout-form-row/layout-form-row.component"; import DateTimePicker from "../form-date-time-picker/form-date-time-picker.component.jsx"; +import { useSocket } from "../../contexts/SocketIO/useSocket.jsx"; const mapStateToProps = createStructuredSelector({ bodyshop: selectBodyshop @@ -39,6 +40,7 @@ export default connect(mapStateToProps, mapDispatchToProps)(DmsPostForm); export function DmsPostForm({ bodyshop, socket, job, logsRef }) { const [form] = Form.useForm(); const { t } = useTranslation(); + const { socket: wsssocket } = useSocket(); const handlePayerSelect = (value, index) => { form.setFieldsValue({ @@ -59,22 +61,37 @@ export function DmsPostForm({ bodyshop, socket, job, logsRef }) { }; const handleFinish = (values) => { - socket.emit(`${determineDmsType(bodyshop)}-export-job`, { - jobid: job.id, - txEnvelope: values - }); - console.log(logsRef); - if (logsRef) { - console.log("executing", logsRef); - logsRef.curent && - logsRef.current.scrollIntoView({ - behavior: "smooth" - }); + //TODO: Add this as a split instead. + if (true) { + wsssocket.emit("fortellis-export-job", { jobid: job.id, txEnvelope: values }); + } else { + socket.emit(`${determineDmsType(bodyshop)}-export-job`, { + jobid: job.id, + txEnvelope: values + }); + console.log(logsRef); + if (logsRef) { + console.log("executing", logsRef); + logsRef.curent && + logsRef.current.scrollIntoView({ + behavior: "smooth" + }); + } } }; return ( +
- + - + diff --git a/server/fortellis/fortellis-helpers.js b/server/fortellis/fortellis-helpers.js new file mode 100644 index 000000000..f406d381a --- /dev/null +++ b/server/fortellis/fortellis-helpers.js @@ -0,0 +1,253 @@ +const path = require("path"); +require("dotenv").config({ + path: path.resolve(process.cwd(), `.env.${process.env.NODE_ENV || "development"}`) +}); + +const GraphQLClient = require("graphql-request").GraphQLClient; +// const CalcualteAllocations = require("../cdk/cdk-calculate-allocations").default; +const InstanceMgr = require("../utils/instanceMgr").default; +const CreateFortellisLogEvent = require("./fortellis-logger"); +const queries = require("../graphql-client/queries"); +const logger = require("../utils/logger"); +const uuid = require("uuid").v4; +const AxiosLib = require("axios").default; +const axios = AxiosLib.create(); + +const getTransactionType = (jobid) => `fortellis:${jobid}`; +const defaultFortellisTTL = 60 * 60; + +async function GetAuthToken() { + //Done with 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 { + data: { access_token, expires_in, token_type } + } = await axios.post( + process.env.FORTELLIS_AUTH_URL, + {}, + { + auth: { + username: process.env.FORTELLIS_KEY, + password: process.env.FORTELLIS_SECRET + }, + params: { + grant_type: "client_credentials", + scope: "anonymous" + } + } + ); + return access_token; +} + +async function FetchSubscriptions({ redisHelpers, socket, jobid }) { + try { + const { setSessionTransactionData, getSessionTransactionData } = redisHelpers; + + //Get Subscription ID from Transaction Envelope + const { SubscriptionID } = await getSessionTransactionData(socket.id, getTransactionType(jobid), `txEnvelope`); + if (!SubscriptionID) { + throw new Error("Subscription ID not found in transaction envelope."); + } + + //Check to See if the subscription meta is in the Redis Cache. + const SubscriptionMetaFromCache = await getSessionTransactionData( + socket.id, + getTransactionType(jobid), + FortellisCacheEnums.SubscriptionMeta + ); + + // If it is, return it. + if (SubscriptionMetaFromCache) { + return SubscriptionMetaFromCache; + } else { + const access_token = await GetAuthToken(); + const subscriptions = await axios.get(`https://subscriptions.fortellis.io/v1/solution/subscriptions`, { + headers: { Authorization: `Bearer ${access_token}` } + }); + const SubscriptionMeta = subscriptions.data.subscriptions.find((s) => s.subscriptionId === SubscriptionID); + await setSessionTransactionData( + socket.id, + getTransactionType(jobid), + FortellisCacheEnums.SubscriptionMeta, + SubscriptionMeta, + defaultFortellisTTL + ); + return SubscriptionMeta; + } + } catch (error) { + CreateFortellisLogEvent(socket, "ERROR", `Error fetching subscription metadata`, { + error: error.message, + stack: error.stack + }); + } +} + +async function GetDepartmentId({ apiName, debug = false, SubscriptionMeta }) { + if (!apiName) throw new Error("apiName not provided. Unable to get department without apiName."); + if (debug) { + console.log("API Names & Departments "); + console.log("==========="); + console.log( + JSON.stringify( + SubscriptionMeta.apiDmsInfo.map((a) => ({ + name: a.name, + departments: a.departments.map((d) => d.id) + })), + null, + 4 + ) + ); + console.log("==========="); + } + const departmentIds2 = SubscriptionMeta.apiDmsInfo //Get the subscription object. + .find((info) => info.name === apiName)?.departments; //Departments are categorized by API name and have an array of departments. + + return departmentIds2[0].id; //TODO: This makes the assumption that there is only 1 department. +} + +//Highest level function call to make a call to fortellis. This should be the only call required, and it will handle all the logic for making the call. +async function MakeFortellisCall({ + apiName, + url, + headers = {}, + body = {}, + type = "post", + debug = true, + jobid, + redisHelpers, + socket +}) { + const { setSessionTransactionData, getSessionTransactionData } = redisHelpers; + + if (debug) logger.log(`Executing ${type} to ${url}`); + const ReqId = uuid(); + const access_token = await GetAuthToken(); + const SubscriptionMeta = await FetchSubscriptions({ redisHelpers, socket, jobid }); + const DepartmentId = await GetDepartmentId({ apiName, debug, SubscriptionMeta }); + + if (debug) { + console.log( + `ReqID: ${ReqId} | SubscriptionID: ${SubscriptionMeta.subscriptionId} | DepartmentId: ${DepartmentId}` + ); + console.log(`Body Contents: ${JSON.stringify(body, null, 4)}`); + } + + try { + let result; + switch (type) { + case "post": + default: + result = await axios.post(url, body, { + headers: { + Authorization: `Bearer ${access_token}`, + "Subscription-Id": SubscriptionMeta.subscriptionId, + "Request-Id": ReqId, + "Department-Id": DepartmentId, + ...headers + } + }); + break; + case "get": + result = await axios.get(url, { + headers: { + Authorization: `Bearer ${access_token}`, + "Subscription-Id": SubscriptionMeta.subscriptionId, + "Request-Id": ReqId, + "Department-Id": DepartmentId, + ...headers + } + }); + break; + } + + if (debug) { + console.log(`ReqID: ${ReqId} Data`); + console.log(JSON.stringify(result.data, null, 4)); + } + + if (result.data.checkStatusAfterSeconds) { + return DelayedCallback({ + delayMeta: result.data, + access_token, + SubscriptionID: SubscriptionMeta.subscriptionId, + ReqId, + departmentIds: DepartmentId + }); + } + return result.data; + } catch (error) { + console.log(`ReqID: ${ReqId} Error`, error.response?.data); + //console.log(`ReqID: ${ReqId} Full Error`, JSON.stringify(error, null, 4)); + } +} + +//Some Fortellis calls return a batch result that isn't ready immediately. +//This function will check the status of the call and wait until it is ready. +//It will try 5 times before giving up. +async function DelayedCallback({ delayMeta, access_token, SubscriptionID, ReqId, departmentIds }) { + for (let index = 0; index < 5; index++) { + await sleep(delayMeta.checkStatusAfterSeconds * 1000); + //Check to see if the call is ready. + const statusResult = await axios.get(delayMeta._links.status.href, { + headers: { + Authorization: `Bearer ${access_token}`, + "Subscription-Id": SubscriptionID, + "Request-Id": ReqId, + "Department-Id": departmentIds[0].id + } + }); + + //TODO: Add a check if the status result is not ready, to try again. + if (statusResult.data.status === "complete") { + //This may have to check again if it isn't ready. + const batchResult = await axios.get(statusResult.data._links.result.href, { + headers: { + Authorization: `Bearer ${access_token}`, + "Subscription-Id": SubscriptionID, + "Request-Id": ReqId + //"Department-Id": departmentIds[0].id + } + }); + return batchResult; + } else { + return "Error!!! Still need to implement batch waiting."; + } + } +} +function sleep(ms) { + return new Promise((resolve) => setTimeout(resolve, ms)); +} + +const isProduction = process.env.NODE_ENV === "production"; + +const FortellisActions = { + QueryVehicles: { + url: isProduction + ? "https://api.fortellis.io/cdkdrive/service/v1/vehicles/" + : "https://api.fortellis.io/cdk-test/cdkdrive/service/v1/vehicles/", + type: "get", + apiName: "Service Vehicle - Query Vehicles" + }, + GetCOA: { + type: "get", + apiName: "CDK Drive Post Accounts GL WIP", + url: `https://api.fortellis.io/cdk-test/drive/chartofaccounts/v2/bulk`, + waitForResult: true + } +}; + +const FortellisCacheEnums = { + txEnvelope: "txEnvelope", + SubscriptionMeta: "SubscriptionMeta", + DepartmentId: "DepartmentId" +}; + +module.exports = { + GetAuthToken, + FortellisCacheEnums, + MakeFortellisCall, + FortellisActions, + getTransactionType, + defaultFortellisTTL +}; diff --git a/server/fortellis/fortellis-logger.js b/server/fortellis/fortellis-logger.js new file mode 100644 index 000000000..16af8b60c --- /dev/null +++ b/server/fortellis/fortellis-logger.js @@ -0,0 +1,9 @@ +const logger = require("../utils/logger"); + +const CreateFortellisLogEvent = (socket, level, message, txnDetails) => { + //TODO: Add detaisl to track the whole transaction between Fortellis and the server. + logger.log("fortellis-log-event", level, socket?.user?.email, null, { wsmessage: message, txnDetails }); + socket.emit("fortellis-log-event", { level, message, txnDetails }); +}; + +module.exports = CreateFortellisLogEvent; diff --git a/server/fortellis/fortellis.js b/server/fortellis/fortellis.js new file mode 100644 index 000000000..156a3fc14 --- /dev/null +++ b/server/fortellis/fortellis.js @@ -0,0 +1,1047 @@ +const path = require("path"); +require("dotenv").config({ + path: path.resolve(process.cwd(), `.env.${process.env.NODE_ENV || "development"}`) +}); +const GraphQLClient = require("graphql-request").GraphQLClient; +// const CalcualteAllocations = require("../cdk/cdk-calculate-allocations").default; +const InstanceMgr = require("../utils/instanceMgr").default; +const CreateFortellisLogEvent = require("./fortellis-logger"); +const queries = require("../graphql-client/queries"); +const { + MakeFortellisCall, + FortellisActions, + getTransactionType, + defaultFortellisTTL, + FortellisCacheEnums +} = require("./fortellis-helpers"); + +// const moment = require("moment-timezone"); + +// const replaceSpecialRegex = /[^a-zA-Z0-9 .,\n #]+/g; + +async function FortellisJobExport({ + socket, + redisHelpers, + ioHelpers: { getBodyshopRoom, getBodyshopConversationRoom }, + txEnvelope, + jobid +}) { + const { + setSessionData, + getSessionData, + addUserSocketMapping, + removeUserSocketMapping, + refreshUserSocketTTL, + getUserSocketMappingByBodyshop, + setSessionTransactionData, + getSessionTransactionData, + clearSessionTransactionData + } = redisHelpers; + // ////Store the following information into the redis store for this transaction. + // socket.logEvents = []; + // socket.recordid = jobid; + // socket.txEnvelope = txEnvelope; + try { + CreateFortellisLogEvent(socket, "debug", `Received Job export request for id ${jobid}`); + await setSessionTransactionData( + socket.id, + getTransactionType(jobid), + FortellisCacheEnums.txEnvelope, + txEnvelope, + defaultFortellisTTL + ); + + const JobData = await QueryJobData({ socket, jobid }); //TODO: Need to remove unnecessary stuff here to reduce the payload. + await setSessionTransactionData(socket.id, getTransactionType(jobid), `JobData`, JobData, defaultFortellisTTL); + + // const DealerId = JobData.bodyshop.cdk_dealerid; + + CreateFortellisLogEvent(socket, "DEBUG", `{1} Begin Calculate DMS Vehicle ID using VIN: ${JobData.v_vin}`); + const DMSVid = await CalculateDmsVid({ socket, JobData, redisHelpers }); + await setSessionTransactionData(socket.id, getTransactionType(jobid), `DMSVid`, DMSVid, defaultFortellisTTL); + + if (socket.DMSVid.newId === "N") { + CreateFortellisLogEvent( + socket, + "DEBUG", + `{2.1} Querying the Vehicle using the DMSVid: ${socket.DMSVid.vehiclesVehId}` + ); + // socket.DMSVeh = await QueryDmsVehicleById(socket, JobData, socket.DMSVid); + + // const DMSVehCustomer = + // socket.DMSVeh && socket.DMSVeh.owners && socket.DMSVeh.owners.find((o) => o.id.assigningPartyId === "CURRENT"); + + // if (DMSVehCustomer && DMSVehCustomer.id && DMSVehCustomer.id.value) { + // CdkBase.createLogEvent( + // socket, + // "DEBUG", + // `{2.2} Querying the Customer using the ID from DMSVeh: ${DMSVehCustomer.id.value}` + // ); + // socket.DMSVehCustomer = await QueryDmsCustomerById(socket, JobData, DMSVehCustomer.id.value); + } + + // CdkBase.createLogEvent(socket, "DEBUG", `{2.3} Querying the Customer using the name.`); + + // socket.DMSCustList = await QueryDmsCustomerByName(socket, JobData); + + // socket.emit("cdk-select-customer", [ + // ...(socket.DMSVehCustomer ? [{ ...socket.DMSVehCustomer, vinOwner: true }] : []), + // ...socket.DMSCustList + // ]); + } catch (error) { + CreateFortellisLogEvent(socket, "ERROR", `Error in FortellisJobExport - ${error}`, { + error: error.message, + stack: error.stack + }); + //CdkBase.createLogEvent(socket, "ERROR", `Error encountered in CdkJobExport. ${error}`); + } + + // async function CdkSelectedCustomer(socket, selectedCustomerId) { + // try { + // socket.selectedCustomerId = selectedCustomerId; + // if (selectedCustomerId) { + // CdkBase.createLogEvent(socket, "DEBUG", `{3.1} Querying the Customer using Customer ID: ${selectedCustomerId}`); + // socket.DMSCust = await QueryDmsCustomerById(socket, socket.JobData, selectedCustomerId); + // } else { + // CdkBase.createLogEvent(socket, "DEBUG", `{3.2} Generating a new customer ID.`); + // const newCustomerId = await GenerateDmsCustomerNumber(socket); + // CdkBase.createLogEvent(socket, "DEBUG", `{3.3} Inserting new customer with ID: ${newCustomerId}`); + // socket.DMSCust = await InsertDmsCustomer(socket, newCustomerId); + // } + + // if (socket.DMSVid.newId === "Y") { + // CdkBase.createLogEvent(socket, "DEBUG", `{4.1} Inserting new vehicle with ID: ID ${socket.DMSVid.vehiclesVehId}`); + // socket.DMSVeh = await InsertDmsVehicle(socket); + // } else { + // CdkBase.createLogEvent( + // socket, + // "DEBUG", + // `{4.2} Querying Existing Vehicle using ID ${socket.DMSVid.vehiclesVehId}` + // ); + // socket.DMSVeh = await QueryDmsVehicleById(socket, socket.JobData, socket.DMSVid); + // CdkBase.createLogEvent(socket, "DEBUG", `{4.3} Updating Existing Vehicle to associate to owner.`); + // socket.DMSVeh = await UpdateDmsVehicle(socket); + // } + + // CdkBase.createLogEvent(socket, "DEBUG", `{5} Creating Transaction header with Dms Start WIP`); + // socket.DMSTransHeader = await InsertDmsStartWip(socket); + // CdkBase.createLogEvent(socket, "DEBUG", `{5.1} Creating Transaction with ID ${socket.DMSTransHeader.transID}`); + + // socket.DMSBatchTxn = await InsertDmsBatchWip(socket); + // CdkBase.createLogEvent( + // socket, + // "DEBUG", + // `{6} Attempting to post Transaction with ID ${socket.DMSTransHeader.transID}` + // ); + // socket.DmsBatchTxnPost = await PostDmsBatchWip(socket); + // if (socket.DmsBatchTxnPost.code === "success") { + // //something + // CdkBase.createLogEvent(socket, "DEBUG", `{6} Successfully posted sransaction to DMS.`); + + // await MarkJobExported(socket, socket.JobData.id); + + // CdkBase.createLogEvent(socket, "DEBUG", `{5} Updating Service Vehicle History.`); + // socket.DMSVehHistory = await InsertServiceVehicleHistory(socket); + // socket.emit("export-success", socket.JobData.id); + // } else { + // //Get the error code + // CdkBase.createLogEvent( + // socket, + // "DEBUG", + // `{6.1} Getting errors for Transaction ID ${socket.DMSTransHeader.transID}` + // ); + // socket.DmsError = await QueryDmsErrWip(socket); + // //Delete the transaction + // CdkBase.createLogEvent(socket, "DEBUG", `{6.2} Deleting Transaction ID ${socket.DMSTransHeader.transID}`); + // socket.DmsBatchTxnPost = await DeleteDmsWip(socket); + + // socket.DmsError.errMsg + // .split("|") + // .map( + // (e) => + // e !== null && + // e !== "" && + // CdkBase.createLogEvent(socket, "ERROR", `Error(s) encountered in posting transaction. ${e}`) + // ); + // } + // } catch (error) { + // CdkBase.createLogEvent(socket, "ERROR", `Error encountered in CdkSelectedCustomer. ${error}`); + // await InsertFailedExportLog(socket, error); + // } finally { + // //Ensure we always insert logEvents + // //GQL to insert logevents. + + // CdkBase.createLogEvent(socket, "DEBUG", `Capturing log events to database.`); + // } +} + +// exports.CdkSelectedCustomer = CdkSelectedCustomer; + +async function QueryJobData({ socket, jobid }) { + const client = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, {}); + const result = await client + .setHeaders({ Authorization: `Bearer ${socket.handshake?.auth?.token}` }) + .request(queries.QUERY_JOBS_FOR_CDK_EXPORT, { id: jobid }); + return result.jobs_by_pk; +} + +async function CalculateDmsVid({ socket, JobData, redisHelpers }) { + try { + const result = await MakeFortellisCall({ + ...FortellisActions.QueryVehicles, + headers: {}, + redisHelpers, + socket, + jobid: JobData.id, + body: { + vin: JobData.v_vin + //Include the contents of the call here. + } + }); + // const soapClientVehicleInsertUpdate = await soap.createClientAsync(CdkWsdl.VehicleInsertUpdate); + // const soapResponseVehicleInsertUpdate = await soapClientVehicleInsertUpdate.getVehIdsAsync({ + // arg0: CDK_CREDENTIALS, + // arg1: { id: JobData.bodyshop.cdk_dealerid }, + // arg2: { VIN: JobData.v_vin } + // }); + // const [result, rawResponse, , rawRequest] = soapResponseVehicleInsertUpdate; + // CdkBase.createXmlEvent(socket, rawRequest, `soapClientVehicleInsertUpdate.getVehIdsAsync request.`); + // CdkBase.createXmlEvent(socket, rawResponse, `soapClientVehicleInsertUpdate.getVehIdsAsync response.`); + // CdkBase.createLogEvent( + // socket, + // "SILLY", + // `soapClientVehicleInsertUpdate.getVehIdsAsync Result ${JSON.stringify(result, null, 2)}` + // ); + // CheckCdkResponseForError(socket, soapResponseVehicleInsertUpdate); + // //if (result && result.return && result.return.length > 1) { + // return result.return.find((r) => r.vehiclesVehId); + // //} + //return result && result.return && result.return[0]; + } catch (error) { + CreateFortellisLogEvent(socket, "ERROR", `Error in CalculateDmsVid - ${error}`, { request: error.request }); + // CdkBase.createXmlEvent(socket, error.request, `soapClientVehicleInsertUpdate.getVehIdsAsync request.`, true); + + // CdkBase.createXmlEvent( + // socket, + // error.response && error.response.data, + // `soapClientVehicleInsertUpdate.getVehIdsAsync response.`, + // true + // ); + // CdkBase.createLogEvent(socket, "ERROR", `{1} Error in CalculateDmsVid - ${error}`); + // throw new Error(error); + } +} + +// async function QueryDmsVehicleById(socket, JobData, DMSVid) { +// try { +// const soapClientVehicleInsertUpdate = await soap.createClientAsync(CdkWsdl.VehicleInsertUpdate); + +// const soapResponseVehicleInsertUpdate = await soapClientVehicleInsertUpdate.readAsync({ +// arg0: CDK_CREDENTIALS, +// arg1: { id: JobData.bodyshop.cdk_dealerid }, +// arg2: { +// fileType: "VEHICLES", +// vehiclesVehicleId: DMSVid.vehiclesVehId +// } +// }); + +// const [result, rawResponse, , rawRequest] = soapResponseVehicleInsertUpdate; + +// CdkBase.createXmlEvent(socket, rawRequest, `soapClientVehicleInsertUpdate.readAsync request.`); + +// CdkBase.createLogEvent( +// socket, +// "SILLY", +// `soapClientVehicleInsertUpdate.readAsync Result ${JSON.stringify(result, null, 2)}` +// ); +// CdkBase.createXmlEvent(socket, rawResponse, `soapClientVehicleInsertUpdate.readAsync response.`); +// CheckCdkResponseForError(socket, soapResponseVehicleInsertUpdate); +// const VehicleFromDMS = result && result.return && result.return.vehicle; +// return VehicleFromDMS; +// } catch (error) { +// CdkBase.createLogEvent(socket, "ERROR", `Error in QueryDmsVehicleById - ${error}`); +// throw new Error(error); +// } +// } + +// async function QueryDmsCustomerById(socket, JobData, CustomerId) { +// try { +// const soapClientCustomerInsertUpdate = await soap.createClientAsync(CdkWsdl.CustomerInsertUpdate); +// const soapResponseCustomerInsertUpdate = await soapClientCustomerInsertUpdate.readAsync({ +// arg0: CDK_CREDENTIALS, +// arg1: { dealerId: JobData.bodyshop.cdk_dealerid }, //TODO: Verify why this does not follow the other standards. +// arg2: { +// // userId: CustomerId, +// }, +// arg3: CustomerId +// }); + +// const [result, rawResponse, , rawRequest] = soapResponseCustomerInsertUpdate; + +// CdkBase.createXmlEvent(socket, rawRequest, `soapClientCustomerInsertUpdate.readAsync request.`); + +// CdkBase.createXmlEvent(socket, rawResponse, `soapClientCustomerInsertUpdate.readAsync response.`); +// CdkBase.createLogEvent( +// socket, +// "SILLY", +// `soapClientCustomerInsertUpdate.readAsync Result ${JSON.stringify(result, null, 2)}` +// ); +// CheckCdkResponseForError(socket, soapResponseCustomerInsertUpdate); +// const CustomersFromDms = result && result.return && result.return.customerParty; +// return CustomersFromDms; +// } catch (error) { +// CdkBase.createXmlEvent(socket, error.request, `soapClientCustomerInsertUpdate.readAsync request.`, true); + +// CdkBase.createXmlEvent( +// socket, +// error.response && error.response.data, +// `soapClientCustomerInsertUpdate.readAsync response.`, +// true +// ); + +// CdkBase.createLogEvent(socket, "ERROR", `Error in QueryDmsCustomerById - ${error}`); +// throw new Error(error); +// } +// } + +// async function QueryDmsCustomerByName(socket, JobData) { +// const ownerName = ( +// JobData.ownr_co_nm && JobData.ownr_co_nm.trim() !== "" +// ? JobData.ownr_co_nm +// : `${JobData.ownr_ln},${JobData.ownr_fn}` +// ).replace(replaceSpecialRegex, ""); + +// CdkBase.createLogEvent(socket, "DEBUG", `Begin Query DMS Customer by Name using: ${ownerName}`); + +// try { +// const soapClientCustomerSearch = await soap.createClientAsync(CdkWsdl.CustomerSearch); +// const soapResponseCustomerSearch = await soapClientCustomerSearch.executeSearchAsync({ +// arg0: CDK_CREDENTIALS, +// arg1: { dealerId: JobData.bodyshop.cdk_dealerid }, //TODO: Verify why this does not follow the other standards. +// arg2: { +// verb: "EXACT", +// key: ownerName +// } +// }); + +// const [result, rawResponse, , rawRequest] = soapResponseCustomerSearch; + +// CdkBase.createXmlEvent(socket, rawRequest, `soapClientCustomerSearch.executeSearchBulkAsync request.`); + +// CdkBase.createXmlEvent(socket, rawResponse, `soapClientCustomerSearch.executeSearchBulkAsync response.`); + +// CdkBase.createLogEvent( +// socket, +// "SILLY", +// `soapClientCustomerSearch.executeSearchBulkAsync Result ${JSON.stringify(result, null, 2)}` +// ); +// CheckCdkResponseForError(socket, soapResponseCustomerSearch); +// const CustomersFromDms = (result && result.return) || []; +// return CustomersFromDms; +// } catch (error) { +// CdkBase.createXmlEvent(socket, error.request, `soapClientCustomerSearch.executeSearchBulkAsync request.`, true); + +// CdkBase.createXmlEvent( +// socket, +// error.response && error.response.data, +// `soapClientCustomerSearch.executeSearchBulkAsync response.`, +// true +// ); + +// CdkBase.createLogEvent(socket, "ERROR", `Error in QueryDmsCustomerByName - ${error}`); +// throw new Error(error); +// } +// } + +// async function GenerateDmsCustomerNumber(socket) { +// try { +// const soapClientCustomerInsertUpdate = await soap.createClientAsync(CdkWsdl.CustomerInsertUpdate); +// const soapResponseCustomerInsertUpdate = await soapClientCustomerInsertUpdate.getCustomerNumberAsync( +// { +// arg0: CDK_CREDENTIALS, +// arg1: { dealerId: socket.JobData.bodyshop.cdk_dealerid }, //TODO: Verify why this does not follow the other standards. +// arg2: { userId: null } +// }, + +// {} +// ); + +// const [result, rawResponse, , rawRequest] = soapResponseCustomerInsertUpdate; + +// CdkBase.createXmlEvent(socket, rawRequest, `soapClientCustomerInsertUpdate.getCustomerNumberAsync request.`); + +// CdkBase.createXmlEvent(socket, rawResponse, `soapClientCustomerInsertUpdate.getCustomerNumberAsync response.`); + +// CdkBase.createLogEvent( +// socket, +// "SILLY", +// `soapClientCustomerInsertUpdate.getCustomerNumberAsync Result ${JSON.stringify(result, null, 2)}` +// ); +// CheckCdkResponseForError(socket, soapResponseCustomerInsertUpdate); +// const customerNumber = result && result.return && result.return.customerNumber; +// return customerNumber; +// } catch (error) { +// CdkBase.createXmlEvent( +// socket, +// error.request, +// `soapClientCustomerInsertUpdate.getCustomerNumberAsync request.`, +// true +// ); + +// CdkBase.createXmlEvent( +// socket, +// error.response && error.response.data, +// `soapClientCustomerInsertUpdate.getCustomerNumberAsync response.`, +// true +// ); +// CdkBase.createLogEvent(socket, "ERROR", `Error in GenerateDmsCustomerNumber - ${error}`); +// throw new Error(error); +// } +// } + +// async function InsertDmsCustomer(socket, newCustomerNumber) { +// try { +// const soapClientCustomerInsertUpdate = await soap.createClientAsync(CdkWsdl.CustomerInsertUpdate); +// const soapResponseCustomerInsertUpdate = await soapClientCustomerInsertUpdate.insertAsync( +// { +// arg0: CDK_CREDENTIALS, +// arg1: { dealerId: socket.JobData.bodyshop.cdk_dealerid }, +// arg2: { userId: null }, +// arg3: { +// //Copied the required fields from the other integration. +// //TODO: Verify whether we need to bring more information in. +// id: { value: newCustomerNumber }, +// address: { +// addressLine: socket.JobData.ownr_addr1 && socket.JobData.ownr_addr1.replace(replaceSpecialRegex, ""), +// city: socket.JobData.ownr_city && socket.JobData.ownr_city.replace(replaceSpecialRegex, ""), +// country: socket.JobData.ownr_ctry && socket.JobData.ownr_ctry.replace(replaceSpecialRegex, ""), +// postalCode: InstanceMgr({ +// imex: +// socket.JobData.ownr_zip && +// socket.JobData.ownr_zip //TODO Need to remove for US Based customers. +// .toUpperCase() +// .replace(/\W/g, "") +// .replace(/(...)/, "$1 "), +// rome: socket.JobData.ownr_zip +// }), +// stateOrProvince: socket.JobData.ownr_st && socket.JobData.ownr_st.replace(replaceSpecialRegex, "") +// }, +// contactInfo: { +// mainTelephoneNumber: { +// main: true, +// value: socket.JobData.ownr_ph1 && socket.JobData.ownr_ph1.replace(replaceSpecialRegex, "") +// }, +// email: { +// desc: socket.JobData.ownr_ea ? "Other" : "CustomerDeclined", +// value: socket.JobData.ownr_ea ? socket.JobData.ownr_ea : null +// } +// }, +// demographics: null, +// name1: { +// companyName: +// socket.JobData.ownr_co_nm && socket.JobData.ownr_co_nm.replace(replaceSpecialRegex, "").toUpperCase(), + +// firstName: socket.JobData.ownr_fn && socket.JobData.ownr_fn.replace(replaceSpecialRegex, "").toUpperCase(), +// fullname: null, +// lastName: socket.JobData.ownr_ln && socket.JobData.ownr_ln.replace(replaceSpecialRegex, "").toUpperCase(), +// middleName: null, +// nameType: +// socket.JobData.ownr_co_nm && String(socket.JobData.ownr_co_nm).trim() !== "" ? "Business" : "Person", +// suffix: null, +// title: null +// } +// } +// }, + +// {} +// ); + +// const [result, rawResponse, , rawRequest] = soapResponseCustomerInsertUpdate; +// CdkBase.createXmlEvent(socket, rawRequest, `soapClientCustomerInsertUpdate.insertAsync request.`); + +// CdkBase.createXmlEvent(socket, rawResponse, `soapClientCustomerInsertUpdate.insertAsync response.`); +// CdkBase.createLogEvent( +// socket, +// "SILLY", +// `soapClientCustomerInsertUpdate.insertAsync Result ${JSON.stringify(result, null, 2)}` +// ); +// CheckCdkResponseForError(socket, soapResponseCustomerInsertUpdate); +// const customer = result && result.return && result.return.customerParty; +// return customer; +// } catch (error) { +// CdkBase.createXmlEvent(socket, error.request, `soapClientCustomerInsertUpdate.insertAsync request.`, true); + +// CdkBase.createXmlEvent( +// socket, +// error.response && error.response.data, +// `soapClientCustomerInsertUpdate.insertAsync response.`, +// true +// ); +// CdkBase.createLogEvent(socket, "ERROR", `Error in InsertDmsCustomer - ${error}`); +// throw new Error(error); +// } +// } + +// async function InsertDmsVehicle(socket) { +// try { +// const soapClientVehicleInsertUpdate = await soap.createClientAsync(CdkWsdl.VehicleInsertUpdate); + +// const soapResponseVehicleInsertUpdate = await soapClientVehicleInsertUpdate.insertAsync({ +// arg0: CDK_CREDENTIALS, +// arg1: { id: socket.JobData.bodyshop.cdk_dealerid }, +// arg2: { +// dealer: { +// dealerNumber: socket.JobData.bodyshop.cdk_dealerid, +// ...(socket.txEnvelope.inservicedate && { +// inServiceDate: +// socket.txEnvelope.dms_unsold === true +// ? "" +// : moment(socket.txEnvelope.inservicedate) +// //.tz(socket.JobData.bodyshop.timezone) +// .startOf("day") +// .toISOString() +// }), +// vehicleId: socket.DMSVid.vehiclesVehId +// }, +// manufacturer: {}, +// vehicle: { +// deliveryDate: +// socket.txEnvelope.dms_unsold === true +// ? "" +// : moment() +// // .tz(socket.JobData.bodyshop.timezone) +// .format("YYYYMMDD"), +// licensePlateNo: +// socket.JobData.plate_no === null +// ? null +// : String(socket.JobData.plate_no).replace(/([^\w]|_)/g, "").length === 0 +// ? null +// : String(socket.JobData.plate_no) +// .replace(/([^\w]|_)/g, "") +// .toUpperCase(), +// make: socket.txEnvelope.dms_make, +// modelAbrev: socket.txEnvelope.dms_model, +// modelYear: socket.JobData.v_model_yr, +// odometerStatus: socket.txEnvelope.kmout, +// saleClassValue: "MISC", +// VIN: socket.JobData.v_vin +// }, +// owners: { +// id: { +// assigningPartyId: "CURRENT", +// value: socket.DMSCust.id.value +// } +// } +// }, +// arg3: "VEHICLES" +// }); + +// const [result, rawResponse, , rawRequest] = soapResponseVehicleInsertUpdate; + +// CdkBase.createXmlEvent(socket, rawRequest, `soapClientVehicleInsertUpdate.insertAsync request.`); + +// CdkBase.createLogEvent( +// socket, +// "SILLY", +// `soapClientVehicleInsertUpdate.insertAsync Result ${JSON.stringify(result, null, 2)}` +// ); +// CdkBase.createXmlEvent(socket, rawResponse, `soapClientVehicleInsertUpdate.insertAsync response.`); +// CheckCdkResponseForError(socket, soapResponseVehicleInsertUpdate); +// const VehicleFromDMS = result && result.return && result.return.vehicle; +// return VehicleFromDMS; +// } catch (error) { +// CdkBase.createLogEvent(socket, "ERROR", `Error in InsertDmsVehicle - ${error}`); +// throw new Error(error); +// } +// } + +// async function UpdateDmsVehicle(socket) { +// try { +// const soapClientVehicleInsertUpdate = await soap.createClientAsync(CdkWsdl.VehicleInsertUpdate); + +// let ids = []; + +// //if it's a generic customer, don't update the vehicle owners. + +// if (socket.selectedCustomerId === socket.JobData.bodyshop.cdk_configuration.generic_customer_number) { +// ids = socket.DMSVeh && socket.DMSVeh.owners && socket.DMSVeh.owners; +// } else { +// const existingOwnerinVeh = +// socket.DMSVeh && +// socket.DMSVeh.owners && +// socket.DMSVeh.owners.find((o) => o.id.value === socket.DMSCust.id.value); + +// if (existingOwnerinVeh) { +// ids = socket.DMSVeh.owners.map((o) => { +// return { +// id: { +// assigningPartyId: o.id.value === socket.DMSCust.id.value ? "CURRENT" : "PREVIOUS", +// value: o.id.value +// } +// }; +// }); +// } else { +// const oldOwner = +// socket.DMSVeh && +// socket.DMSVeh.owners && +// socket.DMSVeh.owners.find((o) => o.id.assigningPartyId === "CURRENT"); + +// ids = [ +// { +// id: { +// assigningPartyId: "CURRENT", +// value: socket.DMSCust.id.value +// } +// }, +// ...(oldOwner +// ? [ +// { +// id: { +// assigningPartyId: "PREVIOUS", +// value: oldOwner.id.value +// } +// } +// ] +// : []) +// ]; +// } +// } + +// const soapResponseVehicleInsertUpdate = await soapClientVehicleInsertUpdate.updateAsync({ +// arg0: CDK_CREDENTIALS, +// arg1: { id: socket.JobData.bodyshop.cdk_dealerid }, +// arg2: { +// ...socket.DMSVeh, +// dealer: { +// ...socket.DMSVeh.dealer, +// ...((socket.txEnvelope.inservicedate || socket.DMSVeh.dealer.inServiceDate) && { +// inServiceDate: +// socket.txEnvelope.dms_unsold === true +// ? "" +// : moment(socket.DMSVeh.dealer.inServiceDate || socket.txEnvelope.inservicedate) +// // .tz(socket.JobData.bodyshop.timezone) +// .toISOString() +// }) +// }, +// vehicle: { +// ...socket.DMSVeh.vehicle, +// ...(socket.txEnvelope.dms_model_override +// ? { +// make: socket.txEnvelope.dms_make, +// modelAbrev: socket.txEnvelope.dms_model +// } +// : {}), +// deliveryDate: +// socket.txEnvelope.dms_unsold === true +// ? "" +// : moment(socket.DMSVeh.vehicle.deliveryDate) +// //.tz(socket.JobData.bodyshop.timezone) +// .toISOString() +// }, +// owners: ids +// }, +// arg3: "VEHICLES" +// }); +// const [result, rawResponse, , rawRequest] = soapResponseVehicleInsertUpdate; + +// CdkBase.createXmlEvent(socket, rawRequest, `soapClientVehicleInsertUpdate.updateAsync request.`); + +// CdkBase.createLogEvent( +// socket, +// "DEBUG", +// `soapClientVehicleInsertUpdate.updateAsync Result ${JSON.stringify(result, null, 2)}` +// ); +// CdkBase.createXmlEvent(socket, rawResponse, `soapClientVehicleInsertUpdate.updateAsync response.`); +// CheckCdkResponseForError(socket, soapResponseVehicleInsertUpdate); +// const VehicleFromDMS = result && result.return && result.return.vehicle; +// return VehicleFromDMS; +// } catch (error) { +// CdkBase.createLogEvent(socket, "ERROR", `Error in UpdateDmsVehicle - ${error}`); +// throw new Error(error); +// } +// } + +// async function InsertServiceVehicleHistory(socket) { +// try { +// const soapClientServiceHistoryInsert = await soap.createClientAsync(CdkWsdl.ServiceHistoryInsert); + +// const soapResponseServiceHistoryInsert = await soapClientServiceHistoryInsert.serviceHistoryHeaderInsertAsync({ +// authToken: CDK_CREDENTIALS, +// dealerId: { dealerId: socket.JobData.bodyshop.cdk_dealerid }, +// serviceHistoryHeader: { +// vehID: socket.DMSVid.vehiclesVehId, +// roNumber: socket.JobData.ro_number.match(/\d+/g), +// mileage: socket.txEnvelope.kmout, +// openDate: moment(socket.JobData.actual_in).tz(socket.JobData.bodyshop.timezone).format("YYYY-MM-DD"), +// openTime: moment(socket.JobData.actual_in).tz(socket.JobData.bodyshop.timezone).format("HH:mm:ss"), +// closeDate: moment(socket.JobData.invoice_date).tz(socket.JobData.bodyshop.timezone).format("YYYY-MM-DD"), +// closeTime: moment(socket.JobData.invoice_date).tz(socket.JobData.bodyshop.timezone).format("HH:mm:ss"), +// comments: socket.txEnvelope.story, +// cashierID: socket.JobData.bodyshop.cdk_configuration.cashierid +// } +// }); + +// const [result, rawResponse, , rawRequest] = soapResponseServiceHistoryInsert; + +// CdkBase.createXmlEvent(socket, rawRequest, `soapClientServiceHistoryInsert.serviceHistoryHeaderInsert request.`); + +// CdkBase.createLogEvent( +// socket, +// "SILLY", +// `soapClientServiceHistoryInsert.serviceHistoryHeaderInsert Result ${JSON.stringify(result, null, 2)}` +// ); +// CdkBase.createXmlEvent(socket, rawResponse, `soapClientServiceHistoryInsert.serviceHistoryHeaderInsert response.`); +// CheckCdkResponseForError(socket, soapResponseServiceHistoryInsert); +// return result && result.return; +// } catch (error) { +// CdkBase.createLogEvent(socket, "ERROR", `Error in InsertServiceVehicleHistory - ${error}`); +// throw new Error(error); +// } +// } + +// async function InsertDmsStartWip(socket) { +// try { +// const soapClientAccountingGLInsertUpdate = await soap.createClientAsync(CdkWsdl.AccountingGLInsertUpdate); + +// const soapResponseAccountingGLInsertUpdate = await soapClientAccountingGLInsertUpdate.doStartWIPAsync({ +// arg0: CDK_CREDENTIALS, +// arg1: { dealerId: socket.JobData.bodyshop.cdk_dealerid }, +// arg2: { +// acctgDate: moment().tz(socket.JobData.bodyshop.timezone).format("YYYY-MM-DD"), +// //socket.JobData.invoice_date +// desc: socket.txEnvelope.story && socket.txEnvelope.story.replace(replaceSpecialRegex, ""), +// docType: 10 || 7, //Need to check what this usually would be? Apparently it is almost always 10 or 7. +// //1 Cash Receipt , 2 Check, 3 Journal Voucher, 4 Parts invoice, 5 Payable Invoice, 6 Recurring Entry, 7 Repair Order Invoice, 8 Vehicle Purchase Invoice, 9 Vehicle Sale Invoice, 10 Other, 11 Payroll, 12 Finance Charge, 13 FMLR Invoice, 14 Parts Credit Memo, 15 Manufacturer Document, 16 FMLR Credit Memo +// m13Flag: 0, +// refer: socket.JobData.ro_number, +// srcCo: socket.JobData.bodyshop.cdk_configuration.srcco, +// srcJrnl: socket.txEnvelope.journal, +// userID: socket.JobData.bodyshop.cdk_configuration.cashierid //Where is this coming from? +// //userName: "IMEX", +// } +// }); + +// const [result, rawResponse, , rawRequest] = soapResponseAccountingGLInsertUpdate; + +// CdkBase.createXmlEvent(socket, rawRequest, `soapClientAccountingGLInsertUpdate.doStartWIPAsync request.`); + +// CdkBase.createLogEvent( +// socket, +// "SILLY", +// `soapClientAccountingGLInsertUpdate.doStartWIPAsync Result ${JSON.stringify(result, null, 2)}` +// ); +// CdkBase.createXmlEvent(socket, rawResponse, `soapClientAccountingGLInsertUpdate.doStartWIPAsync response.`); +// CheckCdkResponseForError(socket, soapResponseAccountingGLInsertUpdate); +// const TransactionHeader = result && result.return; +// return TransactionHeader; +// } catch (error) { +// CdkBase.createLogEvent(socket, "ERROR", `Error in InsertDmsStartWip - ${error}`); +// throw new Error(error); +// } +// } + +// async function InsertDmsBatchWip(socket) { +// try { +// const soapClientAccountingGLInsertUpdate = await soap.createClientAsync(CdkWsdl.AccountingGLInsertUpdate); + +// const soapResponseAccountingGLInsertUpdate = await soapClientAccountingGLInsertUpdate.doTransBatchWIPAsync({ +// arg0: CDK_CREDENTIALS, +// arg1: { dealerId: socket.JobData.bodyshop.cdk_dealerid }, +// arg2: { +// transWIPs: await GenerateTransWips(socket) +// } +// }); + +// const [result, rawResponse, , rawRequest] = soapResponseAccountingGLInsertUpdate; + +// CdkBase.createXmlEvent(socket, rawRequest, `soapClientAccountingGLInsertUpdate.doTransBatchWIPAsync request.`); + +// CdkBase.createLogEvent( +// socket, +// "SILLY", +// `soapClientAccountingGLInsertUpdate.doTransBatchWIPAsync Result ${JSON.stringify(result, null, 2)}` +// ); +// CdkBase.createXmlEvent(socket, rawResponse, `soapClientAccountingGLInsertUpdate.doTransBatchWIPAsync response.`); +// CheckCdkResponseForError(socket, soapResponseAccountingGLInsertUpdate); +// const BatchWipResult = result && result.return; +// return BatchWipResult; +// } catch (error) { +// CdkBase.createLogEvent(socket, "ERROR", `Error in InsertDmsBatchWip - ${error}`); +// throw new Error(error); +// } +// } + +// async function GenerateTransWips(socket) { +// const allocations = await CalcualteAllocations(socket, socket.JobData.id); +// const wips = []; +// allocations.forEach((alloc) => { +// //Add the sale item from each allocation. +// if (alloc.sale.getAmount() > 0 && !alloc.tax) { +// const item = { +// acct: alloc.profitCenter.dms_acctnumber, +// cntl: +// alloc.profitCenter.dms_control_override && +// alloc.profitCenter.dms_control_override !== null && +// alloc.profitCenter.dms_control_override !== undefined && +// alloc.profitCenter.dms_control_override?.trim() !== "" +// ? alloc.profitCenter.dms_control_override +// : socket.JobData.ro_number, +// cntl2: null, +// credtMemoNo: null, +// postAmt: alloc.sale.multiply(-1).getAmount(), +// postDesc: null, +// prod: null, +// statCnt: 1, +// transID: socket.DMSTransHeader.transID, +// trgtCoID: socket.JobData.bodyshop.cdk_configuration.srcco +// }; +// wips.push(item); +// } + +// //Add the cost Item. +// if (alloc.cost.getAmount() > 0 && !alloc.tax) { +// const item = { +// acct: alloc.costCenter.dms_acctnumber, +// cntl: +// alloc.costCenter.dms_control_override && +// alloc.costCenter.dms_control_override !== null && +// alloc.costCenter.dms_control_override !== undefined && +// alloc.costCenter.dms_control_override?.trim() !== "" +// ? alloc.costCenter.dms_control_override +// : socket.JobData.ro_number, +// cntl2: null, +// credtMemoNo: null, +// postAmt: alloc.cost.getAmount(), +// postDesc: null, +// prod: null, +// statCnt: 1, +// transID: socket.DMSTransHeader.transID, +// trgtCoID: socket.JobData.bodyshop.cdk_configuration.srcco +// }; +// wips.push(item); + +// const itemWip = { +// acct: alloc.costCenter.dms_wip_acctnumber, +// cntl: +// alloc.costCenter.dms_control_override && +// alloc.costCenter.dms_control_override !== null && +// alloc.costCenter.dms_control_override !== undefined && +// alloc.costCenter.dms_control_override?.trim() !== "" +// ? alloc.costCenter.dms_control_override +// : socket.JobData.ro_number, +// cntl2: null, +// credtMemoNo: null, +// postAmt: alloc.cost.multiply(-1).getAmount(), +// postDesc: null, +// prod: null, +// statCnt: 1, +// transID: socket.DMSTransHeader.transID, +// trgtCoID: socket.JobData.bodyshop.cdk_configuration.srcco +// }; +// wips.push(itemWip); +// //Add to the WIP account. +// } + +// if (alloc.tax) { +// // if (alloc.cost.getAmount() > 0) { +// // const item = { +// // acct: alloc.costCenter.dms_acctnumber, +// // cntl: socket.JobData.ro_number, +// // cntl2: null, +// // credtMemoNo: null, +// // postAmt: alloc.cost.getAmount(), +// // postDesc: null, +// // prod: null, +// // statCnt: 1, +// // transID: socket.DMSTransHeader.transID, +// // trgtCoID: socket.JobData.bodyshop.cdk_configuration.srcco, +// // }; + +// // wips.push(item); +// // } + +// if (alloc.sale.getAmount() > 0) { +// const item2 = { +// acct: alloc.profitCenter.dms_acctnumber, +// cntl: +// alloc.profitCenter.dms_control_override && +// alloc.profitCenter.dms_control_override !== null && +// alloc.profitCenter.dms_control_override !== undefined && +// alloc.profitCenter.dms_control_override?.trim() !== "" +// ? alloc.profitCenter.dms_control_override +// : socket.JobData.ro_number, +// cntl2: null, +// credtMemoNo: null, +// postAmt: alloc.sale.multiply(-1).getAmount(), +// postDesc: null, +// prod: null, +// statCnt: 1, +// transID: socket.DMSTransHeader.transID, +// trgtCoID: socket.JobData.bodyshop.cdk_configuration.srcco +// }; +// wips.push(item2); +// } +// } +// }); + +// socket.txEnvelope.payers.forEach((payer) => { +// const item = { +// acct: payer.dms_acctnumber, +// cntl: payer.controlnumber, +// cntl2: null, +// credtMemoNo: null, +// postAmt: Math.round(payer.amount * 100), +// postDesc: null, +// prod: null, +// statCnt: 1, +// transID: socket.DMSTransHeader.transID, +// trgtCoID: socket.JobData.bodyshop.cdk_configuration.srcco +// }; + +// wips.push(item); +// }); +// socket.transWips = wips; +// return wips; +// } + +// async function PostDmsBatchWip(socket) { +// try { +// const soapClientAccountingGLInsertUpdate = await soap.createClientAsync(CdkWsdl.AccountingGLInsertUpdate); + +// const soapResponseAccountingGLInsertUpdate = await soapClientAccountingGLInsertUpdate.doPostBatchWIPAsync({ +// arg0: CDK_CREDENTIALS, +// arg1: { dealerId: socket.JobData.bodyshop.cdk_dealerid }, +// arg2: { +// postWIP: { opCode: "P", transID: socket.DMSTransHeader.transID } +// } +// }); + +// const [result, rawResponse, , rawRequest] = soapResponseAccountingGLInsertUpdate; + +// CdkBase.createXmlEvent(socket, rawRequest, `soapClientAccountingGLInsertUpdate.doPostBatchWIPAsync request.`); + +// CdkBase.createLogEvent( +// socket, +// "SILLY", +// `soapClientAccountingGLInsertUpdate.doPostBatchWIPAsync Result ${JSON.stringify(result, null, 2)}` +// ); +// CdkBase.createXmlEvent(socket, rawResponse, `soapClientAccountingGLInsertUpdate.doPostBatchWIPAsync response.`); +// // CheckCdkResponseForError(socket, soapResponseAccountingGLInsertUpdate); +// const PostResult = result && result.return; +// return PostResult; +// } catch (error) { +// CdkBase.createLogEvent(socket, "ERROR", `Error in PostDmsBatchWip - ${error}`); +// throw new Error(error); +// } +// } + +// async function QueryDmsErrWip(socket) { +// try { +// const soapClientAccountingGLInsertUpdate = await soap.createClientAsync(CdkWsdl.AccountingGLInsertUpdate); + +// const soapResponseAccountingGLInsertUpdate = await soapClientAccountingGLInsertUpdate.doErrWIPAsync({ +// arg0: CDK_CREDENTIALS, +// arg1: { dealerId: socket.JobData.bodyshop.cdk_dealerid }, +// arg2: socket.DMSTransHeader.transID +// }); + +// const [result, rawResponse, , rawRequest] = soapResponseAccountingGLInsertUpdate; + +// CdkBase.createXmlEvent(socket, rawRequest, `soapClientAccountingGLInsertUpdate.doErrWIPAsync request.`); + +// CdkBase.createLogEvent( +// socket, +// "DEBUG", +// `soapClientAccountingGLInsertUpdate.doErrWIPAsync Result ${JSON.stringify(result, null, 2)}` +// ); +// CdkBase.createXmlEvent(socket, rawResponse, `soapClientAccountingGLInsertUpdate.doErrWIPAsync response.`); +// CheckCdkResponseForError(socket, soapResponseAccountingGLInsertUpdate); +// const PostResult = result && result.return; +// return PostResult; +// } catch (error) { +// CdkBase.createLogEvent(socket, "ERROR", `Error in QueryDmsErrWip - ${error}`); +// throw new Error(error); +// } +// } + +// async function DeleteDmsWip(socket) { +// try { +// const soapClientAccountingGLInsertUpdate = await soap.createClientAsync(CdkWsdl.AccountingGLInsertUpdate); + +// const soapResponseAccountingGLInsertUpdate = await soapClientAccountingGLInsertUpdate.doPostBatchWIPAsync({ +// arg0: CDK_CREDENTIALS, +// arg1: { dealerId: socket.JobData.bodyshop.cdk_dealerid }, +// arg2: { +// postWIP: { opCode: "D", transID: socket.DMSTransHeader.transID } +// } +// }); + +// const [result, rawResponse, , rawRequest] = soapResponseAccountingGLInsertUpdate; + +// CdkBase.createXmlEvent(socket, rawRequest, `soapClientAccountingGLInsertUpdate.doPostBatchWIPAsync request.`); + +// CdkBase.createLogEvent( +// socket, +// "SILLY", +// `soapClientAccountingGLInsertUpdate.doPostBatchWIPAsync Result ${JSON.stringify(result, null, 2)}` +// ); +// CdkBase.createXmlEvent(socket, rawResponse, `soapClientAccountingGLInsertUpdate.doPostBatchWIPAsync response.`); +// CheckCdkResponseForError(socket, soapResponseAccountingGLInsertUpdate); +// const PostResult = result && result.return; +// return PostResult; +// } catch (error) { +// CdkBase.createLogEvent(socket, "ERROR", `Error in PostDmsBatchWip - ${error}`); +// throw new Error(error); +// } +// } + +// async function MarkJobExported(socket, jobid) { +// CdkBase.createLogEvent(socket, "DEBUG", `Marking job as exported for id ${jobid}`); +// const client = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, {}); +// const result = await client +// .setHeaders({ Authorization: `Bearer ${socket.handshake.auth.token}` }) +// .request(queries.MARK_JOB_EXPORTED, { +// jobId: jobid, +// job: { +// status: socket.JobData.bodyshop.md_ro_statuses.default_exported || "Exported*", +// date_exported: new Date() +// }, +// log: { +// bodyshopid: socket.JobData.bodyshop.id, +// jobid: jobid, +// successful: true, +// useremail: socket.user.email, +// metadata: socket.transWips +// }, +// bill: { +// exported: true, +// exported_at: new Date() +// } +// }); + +// return result; +// } + +// async function InsertFailedExportLog(socket, error) { +// try { +// const client = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, {}); +// const result = await client +// .setHeaders({ Authorization: `Bearer ${socket.handshake.auth.token}` }) +// .request(queries.INSERT_EXPORT_LOG, { +// log: { +// bodyshopid: socket.JobData.bodyshop.id, +// jobid: socket.JobData.id, +// successful: false, +// message: JSON.stringify(error), +// useremail: socket.user.email +// } +// }); + +// return result; +// } catch (error2) { +// CdkBase.createLogEvent(socket, "ERROR", `Error in InsertFailedExportLog - ${error} - ${JSON.stringify(error2)}`); +// } +// } + +exports.getTransactionType = getTransactionType; +exports.default = FortellisJobExport; diff --git a/server/utils/redisHelpers.js b/server/utils/redisHelpers.js index 593ce13a6..7ce1066d2 100644 --- a/server/utils/redisHelpers.js +++ b/server/utils/redisHelpers.js @@ -19,6 +19,7 @@ const getBodyshopCacheKey = (bodyshopId) => `bodyshop-cache:${bodyshopId}`; const getUserSocketMappingKey = (email) => `user:${process.env?.NODE_ENV === "production" ? "prod" : "dev"}:${email}:socketMapping`; +const getSocketTransactionkey = ({ socketId, transactionType }) => `socket:${socketId}:${transactionType}`; /** * Fetch bodyshop data from the database * @param bodyshopId @@ -51,9 +52,12 @@ const fetchBodyshopFromDB = async (bodyshopId, logger) => { */ const applyRedisHelpers = ({ pubClient, app, logger }) => { // Store session data in Redis - const setSessionData = async (socketId, key, value) => { + const setSessionData = async (socketId, key, value, ttl) => { try { - await pubClient.hset(`socket:${socketId}`, key, JSON.stringify(value)); // Use Redis pubClient + await pubClient.hset(`socket:${socketId}`, key, JSON.stringify(value), ttl); // Use Redis pubClient + if (ttl && typeof ttl === "number") { + await pubClient.expire(`socket:${socketId}`, ttl); + } } catch (error) { logger.log(`Error Setting Session Data for socket ${socketId}: ${error}`, "ERROR", "redis"); } @@ -69,6 +73,35 @@ const applyRedisHelpers = ({ pubClient, app, logger }) => { } }; + const setSessionTransactionData = async (socketId, transactionType, key, value, ttl) => { + try { + await pubClient.hset(getSocketTransactionkey({ socketId, transactionType }), key, JSON.stringify(value)); // Use Redis pubClient + if (ttl && typeof ttl === "number") { + await pubClient.expire(getSocketTransactionkey({ socketId, transactionType }), ttl); + } + } catch (error) { + logger.log( + `Error Setting Session Data for socket transaction ${socketId}:${transactionType}: ${error}`, + "ERROR", + "redis" + ); + } + }; + + // Retrieve session transaction data from Redis + const getSessionTransactionData = async (socketId, transactionType, key) => { + try { + const data = await pubClient.hget(getSocketTransactionkey({ socketId, transactionType }), key); + return data ? JSON.parse(data) : null; + } catch (error) { + logger.log( + `Error Getting Session Data for socket transaction ${socketId}:${transactionType}: ${error}`, + "ERROR", + "redis" + ); + } + }; + // Clear session data from Redis const clearSessionData = async (socketId) => { try { @@ -77,6 +110,18 @@ const applyRedisHelpers = ({ pubClient, app, logger }) => { logger.log(`Error Clearing Session Data for socket ${socketId}: ${error}`, "ERROR", "redis"); } }; + // Clear session data from Redis + const clearSessionTransactionData = async (socketId, transactionType) => { + try { + await pubClient.del(getSocketTransactionkey({ socketId, transactionType })); + } catch (error) { + logger.log( + `Error Clearing Session Transaction Data for socket ${socketId}:${transactionType}: ${error}`, + "ERROR", + "redis" + ); + } + }; /** * Add a socket mapping for a user @@ -394,7 +439,10 @@ const applyRedisHelpers = ({ pubClient, app, logger }) => { getUserSocketMapping, refreshUserSocketTTL, getBodyshopFromRedis, - updateOrInvalidateBodyshopFromRedis + updateOrInvalidateBodyshopFromRedis, + setSessionTransactionData, + getSessionTransactionData, + clearSessionTransactionData // setMultipleSessionData, // getMultipleSessionData, // setMultipleFromArraySessionData, diff --git a/server/web-sockets/redisSocketEvents.js b/server/web-sockets/redisSocketEvents.js index f59723f11..8a9292c3b 100644 --- a/server/web-sockets/redisSocketEvents.js +++ b/server/web-sockets/redisSocketEvents.js @@ -1,8 +1,20 @@ const { admin } = require("../firebase/firebase-handler"); +const FortellisJobExport = require("../fortellis/fortellis").default; +const FortellisLogger = require("../fortellis/fortellis-logger"); const redisSocketEvents = ({ io, - redisHelpers: { addUserSocketMapping, removeUserSocketMapping, refreshUserSocketTTL, getUserSocketMappingByBodyshop }, + redisHelpers: { + setSessionData, + getSessionData, + addUserSocketMapping, + removeUserSocketMapping, + refreshUserSocketTTL, + getUserSocketMappingByBodyshop, + setSessionTransactionData, + getSessionTransactionData, + clearSessionTransactionData + }, ioHelpers: { getBodyshopRoom, getBodyshopConversationRoom }, logger }) => { @@ -231,12 +243,44 @@ const redisSocketEvents = ({ }); }; + //Fortellis/CDK Handlers + const registerFortellisEvents = (socket) => { + socket.on("fortellis-export-job", async ({ jobid, txEnvelope }) => { + try { + await FortellisJobExport({ + socket, + redisHelpers: { + setSessionData, + getSessionData, + addUserSocketMapping, + removeUserSocketMapping, + refreshUserSocketTTL, + getUserSocketMappingByBodyshop, + setSessionTransactionData, + getSessionTransactionData, + clearSessionTransactionData + }, + ioHelpers: { getBodyshopRoom, getBodyshopConversationRoom }, + jobid, + txEnvelope + }); + } catch (error) { + FortellisLogger(socket, "error", `Error during Fortellis export : ${error.message}`); + logger.log("fortellis-job-export-error", "error", null, null, { + message: error.message, + stack: error.stack + }); + } + }); + }; + // Call Handlers registerRoomAndBroadcastEvents(socket); registerUpdateEvents(socket); registerMessagingEvents(socket); registerDisconnectEvents(socket); registerSyncEvents(socket); + registerFortellisEvents(socket); }; // Associate Middleware and Handlers