Merge branch 'feature/IO-2776-cdk-fortellis' into feature/Reynolds-and-Reynolds-DMS-API-Integration

This commit is contained in:
Dave
2025-09-25 12:08:05 -04:00
82 changed files with 1749 additions and 1077 deletions

View File

@@ -1,7 +1,7 @@
const GraphQLClient = require("graphql-request").GraphQLClient;
const queries = require("../../graphql-client/queries");
const WsLogger = require("../../web-sockets/createLogEvent")
const WsLogger = require("../../web-sockets/createLogEvent");
const moment = require("moment");
const Dinero = require("dinero.js");
const AxiosLib = require("axios").default;
@@ -18,8 +18,9 @@ axios.interceptors.request.use((x) => {
...x.headers
};
const printable = `${new Date()} | Request: ${x.method.toUpperCase()} | ${x.url
} | ${JSON.stringify(x.data)} | ${JSON.stringify(headers)}`;
const printable = `${new Date()} | Request: ${x.method.toUpperCase()} | ${
x.url
} | ${JSON.stringify(x.data)} | ${JSON.stringify(headers)}`;
//console.log(printable);
WsLogger.createJsonEvent(socket, "SILLY", `Raw Request: ${printable}`, x.data);
@@ -146,7 +147,9 @@ async function PbsCalculateAllocationsAp(socket, billids) {
...billHash[key],
Amount: billHash[key].Amount.toFormat("0.00")
});
APAmount = APAmount.add(billHash[key].Amount); //Calculate the total expense for the bill iteratively to create the corresponding credit to AP.
//Calculate the total expense for the bill iteratively to
// create the corresponding credit to AP.
APAmount = APAmount.add(billHash[key].Amount);
}
});
@@ -174,9 +177,13 @@ exports.PbsCalculateAllocationsAp = PbsCalculateAllocationsAp;
async function QueryBillData(socket, billids) {
WsLogger.createLogEvent(socket, "DEBUG", `Querying bill data for id(s) ${billids}`);
const client = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, {});
const currentToken =
(socket?.data && socket.data.authToken) || (socket?.handshake?.auth && socket.handshake.auth.token);
const result = await client
.setHeaders({ Authorization: `Bearer ${socket.handshake.auth.token}` })
.setHeaders({ Authorization: `Bearer ${currentToken}` })
.request(queries.GET_PBS_AP_ALLOCATIONS, { billids: billids });
WsLogger.createLogEvent(socket, "SILLY", `Bill data query result ${JSON.stringify(result, null, 2)}`);
return result;
@@ -191,7 +198,32 @@ function getCostAccount(billline, respcenters) {
return respcenters.costs.find((c) => c.name === acctName);
}
exports.PbsExportAp = async function (socket, { billids, txEnvelope }) {
async function MarkApExported(socket, billids) {
WsLogger.createLogEvent(socket, "DEBUG", `Marking bills as exported for id ${billids}`);
const client = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, {});
const currentToken =
(socket?.data && socket.data.authToken) || (socket?.handshake?.auth && socket.handshake.auth.token);
const result = await client
.setHeaders({ Authorization: `Bearer ${currentToken}` })
.request(queries.MARK_BILLS_EXPORTED, {
billids,
bill: {
exported: true,
exported_at: new Date()
},
logs: socket.bills.map((bill) => ({
bodyshopid: socket.bodyshop.id,
billid: bill.id,
successful: true,
useremail: socket.user.email
}))
});
return result;
}
const defaultHandler = async (socket, { billids, txEnvelope }) => {
WsLogger.createLogEvent(socket, "DEBUG", `Exporting selected AP.`);
//apAllocations has the same shap as the lines key for the accounting posting to PBS.
@@ -222,24 +254,4 @@ exports.PbsExportAp = async function (socket, { billids, txEnvelope }) {
socket.emit("ap-export-complete");
};
async function MarkApExported(socket, billids) {
WsLogger.createLogEvent(socket, "DEBUG", `Marking bills as exported for id ${billids}`);
const client = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, {});
const result = await client
.setHeaders({ Authorization: `Bearer ${socket.handshake.auth.token}` })
.request(queries.MARK_BILLS_EXPORTED, {
billids,
bill: {
exported: true,
exported_at: new Date()
},
logs: socket.bills.map((bill) => ({
bodyshopid: socket.bodyshop.id,
billid: bill.id,
successful: true,
useremail: socket.user.email
}))
});
return result;
}
exports.PbsExportAp = defaultHandler;

View File

@@ -2,7 +2,7 @@ const GraphQLClient = require("graphql-request").GraphQLClient;
const AxiosLib = require("axios").default;
const queries = require("../../graphql-client/queries");
const { PBS_ENDPOINTS, PBS_CREDENTIALS } = require("./pbs-constants");
const WsLogger = require("../../web-sockets/createLogEvent")
const WsLogger = require("../../web-sockets/createLogEvent");
//const { CDK_CREDENTIALS, CheckCdkResponseForError } = require("./cdk-wsdl");
const CalculateAllocations = require("../../cdk/cdk-calculate-allocations").default;
@@ -19,8 +19,9 @@ axios.interceptors.request.use((x) => {
...x.headers[x.method],
...x.headers
};
const printable = `${new Date()} | Request: ${x.method.toUpperCase()} | ${x.url
} | ${JSON.stringify(x.data)} | ${JSON.stringify(headers)}`;
const printable = `${new Date()} | Request: ${x.method.toUpperCase()} | ${
x.url
} | ${JSON.stringify(x.data)} | ${JSON.stringify(headers)}`;
//console.log(printable);
WsLogger.createJsonEvent(socket, "SILLY", `Raw Request: ${printable}`, x.data);
@@ -38,7 +39,7 @@ axios.interceptors.response.use((x) => {
return x;
});
exports.default = async function (socket, { txEnvelope, jobid }) {
const defaultHandler = async (socket, { txEnvelope, jobid }) => {
socket.logEvents = [];
socket.recordid = jobid;
socket.txEnvelope = txEnvelope;
@@ -51,7 +52,7 @@ exports.default = async function (socket, { txEnvelope, jobid }) {
//Query for the Vehicle record to get the associated customer.
socket.DmsVeh = await QueryVehicleFromDms(socket);
//Todo: Need to validate the lines and methods below.
if (socket.DmsVeh && socket.DmsVeh.CustomerRef) {
if (socket.DmsVeh?.CustomerRef) {
//Get the associated customer from the Vehicle Record.
socket.DMSVehCustomer = await QueryCustomerBycodeFromDms(socket, socket.DmsVeh.CustomerRef);
}
@@ -66,6 +67,8 @@ exports.default = async function (socket, { txEnvelope, jobid }) {
}
};
exports.default = defaultHandler;
exports.PbsSelectedCustomer = async function PbsSelectedCustomer(socket, selectedCustomerId) {
try {
if (socket.JobData.bodyshop.pbs_configuration.disablecontactvehicle === false) {
@@ -75,7 +78,8 @@ exports.PbsSelectedCustomer = async function PbsSelectedCustomer(socket, selecte
WsLogger.createLogEvent(
socket,
"DEBUG",
`Upserting contact information to DMS for ${socket.JobData.ownr_fn || ""
`Upserting contact information to DMS for ${
socket.JobData.ownr_fn || ""
} ${socket.JobData.ownr_ln || ""} ${socket.JobData.ownr_co_nm || ""}`
);
const ownerRef = await UpsertContactData(socket, selectedCustomerId);
@@ -122,9 +126,13 @@ exports.CheckForErrors = CheckForErrors;
async function QueryJobData(socket, jobid) {
WsLogger.createLogEvent(socket, "DEBUG", `Querying job data for id ${jobid}`);
const client = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, {});
const currentToken =
(socket?.data && socket.data.authToken) || (socket?.handshake?.auth && socket.handshake.auth.token);
const result = await client
.setHeaders({ Authorization: `Bearer ${socket.handshake.auth.token}` })
.setHeaders({ Authorization: `Bearer ${currentToken}` })
.request(queries.QUERY_JOBS_FOR_PBS_EXPORT, { id: jobid });
WsLogger.createLogEvent(socket, "SILLY", `Job data query result ${JSON.stringify(result, null, 2)}`);
return result.jobs_by_pk;
}
@@ -193,7 +201,7 @@ async function QueryCustomersFromDms(socket) {
{ auth: PBS_CREDENTIALS, socket }
);
CheckForErrors(socket, CustomerGetResponse);
return CustomerGetResponse && CustomerGetResponse.Contacts;
return CustomerGetResponse?.Contacts;
} catch (error) {
WsLogger.createLogEvent(socket, "ERROR", `Error in QueryCustomersFromDms - ${error}`);
throw new Error(error);
@@ -226,7 +234,7 @@ async function QueryCustomerBycodeFromDms(socket, CustomerRef) {
{ auth: PBS_CREDENTIALS, socket }
);
CheckForErrors(socket, CustomerGetResponse);
return CustomerGetResponse && CustomerGetResponse.Contacts;
return CustomerGetResponse?.Contacts;
} catch (error) {
WsLogger.createLogEvent(socket, "ERROR", `Error in QueryCustomersFromDms - ${error}`);
throw new Error(error);
@@ -245,15 +253,15 @@ async function UpsertContactData(socket, selectedCustomerId) {
Code: socket.JobData.owner.accountingid,
...(socket.JobData.ownr_co_nm
? {
//LastName: socket.JobData.ownr_ln,
FirstName: socket.JobData.ownr_co_nm,
IsBusiness: true
}
//LastName: socket.JobData.ownr_ln,
FirstName: socket.JobData.ownr_co_nm,
IsBusiness: true
}
: {
LastName: socket.JobData.ownr_ln,
FirstName: socket.JobData.ownr_fn,
IsBusiness: false
}),
LastName: socket.JobData.ownr_ln,
FirstName: socket.JobData.ownr_fn,
IsBusiness: false
}),
//Salutation: "String",
//MiddleName: "String",
@@ -330,10 +338,10 @@ async function UpsertVehicleData(socket, ownerRef) {
//FleetNumber: "String",
//Status: "String",
OwnerRef: ownerRef, // "00000000000000000000000000000000",
ModelNumber: socket.JobData.vehicle && socket.JobData.vehicle.v_makecode,
ModelNumber: socket.JobData.vehicle?.v_makecode,
Make: socket.JobData.v_make_desc,
Model: socket.JobData.v_model_desc,
Trim: socket.JobData.vehicle && socket.JobData.vehicle.v_trimcode,
Trim: socket.JobData.vehicle?.v_trimcode,
//VehicleType: "String",
Year: socket.JobData.v_model_yr,
Odometer: socket.JobData.kmout,
@@ -551,7 +559,8 @@ async function InsertAccountPostingData(socket) {
Posting: {
Reference: socket.JobData.ro_number,
JournalCode: socket.txEnvelope.journal,
TransactionDate: moment(socket.JobData.date_invoiced).tz(socket.JobData.bodyshop.timezone).toISOString(), //"0001-01-01T00:00:00.0000000Z",
//Sample TransactionDate: "0001-01-01T00:00:00.0000000Z",
TransactionDate: moment(socket.JobData.date_invoiced).tz(socket.JobData.bodyshop.timezone).toISOString(),
Description: socket.txEnvelope.story,
//AdditionalInfo: "String",
Source: InstanceManager({ imex: "ImEX Online", rome: "Rome Online" }),
@@ -572,8 +581,11 @@ async function InsertAccountPostingData(socket) {
async function MarkJobExported(socket, jobid) {
WsLogger.createLogEvent(socket, "DEBUG", `Marking job as exported for id ${jobid}`);
const client = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, {});
const currentToken =
(socket?.data && socket.data.authToken) || (socket?.handshake?.auth && socket.handshake.auth.token);
const result = await client
.setHeaders({ Authorization: `Bearer ${socket.handshake.auth.token}` })
.setHeaders({ Authorization: `Bearer ${currentToken}` })
.request(queries.MARK_JOB_EXPORTED, {
jobId: jobid,
job: {
@@ -599,8 +611,11 @@ async function MarkJobExported(socket, jobid) {
async function InsertFailedExportLog(socket, error) {
try {
const client = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, {});
const currentToken =
(socket?.data && socket.data.authToken) || (socket?.handshake?.auth && socket.handshake.auth.token);
const result = await client
.setHeaders({ Authorization: `Bearer ${socket.handshake.auth.token}` })
.setHeaders({ Authorization: `Bearer ${currentToken}` })
.request(queries.INSERT_EXPORT_LOG, {
log: {
bodyshopid: socket.JobData.bodyshop.id,

View File

@@ -5,13 +5,13 @@ const CdkWsdl = require("./cdk-wsdl").default;
const { CDK_CREDENTIALS, CheckCdkResponseForError } = require("./cdk-wsdl");
const CalcualteAllocations = require("./cdk-calculate-allocations").default;
const InstanceMgr = require("../utils/instanceMgr").default;
const WsLogger = require("../web-sockets/createLogEvent")
const WsLogger = require("../web-sockets/createLogEvent");
const moment = require("moment-timezone");
const replaceSpecialRegex = /[^a-zA-Z0-9 .,\n #]+/g;
exports.default = async function (socket, { txEnvelope, jobid }) {
const defaultHandler = async (socket, { txEnvelope, jobid }) => {
////Store the following information into the redis store for this transaction.
socket.logEvents = [];
socket.recordid = jobid;
@@ -38,9 +38,9 @@ exports.default = async function (socket, { txEnvelope, jobid }) {
socket.DMSVeh = await QueryDmsVehicleById(socket, JobData, socket.DMSVid);
const DMSVehCustomer =
socket.DMSVeh && socket.DMSVeh.owners && socket.DMSVeh.owners.find((o) => o.id.assigningPartyId === "CURRENT");
socket.DMSVeh?.owners && socket.DMSVeh.owners.find((o) => o.id.assigningPartyId === "CURRENT");
if (DMSVehCustomer && DMSVehCustomer.id && DMSVehCustomer.id.value) {
if (DMSVehCustomer?.id && DMSVehCustomer.id.value) {
WsLogger.createLogEvent(
socket,
"DEBUG",
@@ -62,6 +62,7 @@ exports.default = async function (socket, { txEnvelope, jobid }) {
WsLogger.createLogEvent(socket, "ERROR", `Error encountered in CdkJobExport. ${error}`);
}
};
exports.default = defaultHandler;
async function CdkSelectedCustomer(socket, selectedCustomerId) {
try {
@@ -77,7 +78,11 @@ async function CdkSelectedCustomer(socket, selectedCustomerId) {
}
if (socket.DMSVid.newId === "Y") {
WsLogger.createLogEvent(socket, "DEBUG", `{4.1} Inserting new vehicle with ID: ID ${socket.DMSVid.vehiclesVehId}`);
WsLogger.createLogEvent(
socket,
"DEBUG",
`{4.1} Inserting new vehicle with ID: ID ${socket.DMSVid.vehiclesVehId}`
);
socket.DMSVeh = await InsertDmsVehicle(socket);
} else {
WsLogger.createLogEvent(
@@ -147,9 +152,13 @@ exports.CdkSelectedCustomer = CdkSelectedCustomer;
async function QueryJobData(socket, jobid) {
WsLogger.createLogEvent(socket, "DEBUG", `Querying job data for id ${jobid}`);
const client = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, {});
const currentToken =
(socket?.data && socket.data.authToken) || (socket?.handshake?.auth && socket.handshake.auth.token);
const result = await client
.setHeaders({ Authorization: `Bearer ${socket.handshake.auth.token}` })
.setHeaders({ Authorization: `Bearer ${currentToken}` })
.request(queries.QUERY_JOBS_FOR_CDK_EXPORT, { id: jobid });
WsLogger.createLogEvent(socket, "SILLY", `Job data query result ${JSON.stringify(result, null, 2)}`);
return result.jobs_by_pk;
}
@@ -185,7 +194,7 @@ async function CalculateDmsVid(socket, JobData) {
WsLogger.createXmlEvent(
socket,
error.response && error.response.data,
error.response?.data,
`soapClientVehicleInsertUpdate.getVehIdsAsync response.`,
true
);
@@ -218,7 +227,7 @@ async function QueryDmsVehicleById(socket, JobData, DMSVid) {
);
WsLogger.createXmlEvent(socket, rawResponse, `soapClientVehicleInsertUpdate.readAsync response.`);
CheckCdkResponseForError(socket, soapResponseVehicleInsertUpdate);
const VehicleFromDMS = result && result.return && result.return.vehicle;
const VehicleFromDMS = result?.return && result.return.vehicle;
return VehicleFromDMS;
} catch (error) {
WsLogger.createLogEvent(socket, "ERROR", `Error in QueryDmsVehicleById - ${error}`);
@@ -249,17 +258,12 @@ async function QueryDmsCustomerById(socket, JobData, CustomerId) {
`soapClientCustomerInsertUpdate.readAsync Result ${JSON.stringify(result, null, 2)}`
);
CheckCdkResponseForError(socket, soapResponseCustomerInsertUpdate);
const CustomersFromDms = result && result.return && result.return.customerParty;
const CustomersFromDms = result?.return && result.return.customerParty;
return CustomersFromDms;
} catch (error) {
WsLogger.createXmlEvent(socket, error.request, `soapClientCustomerInsertUpdate.readAsync request.`, true);
WsLogger.createXmlEvent(
socket,
error.response && error.response.data,
`soapClientCustomerInsertUpdate.readAsync response.`,
true
);
WsLogger.createXmlEvent(socket, error.response?.data, `soapClientCustomerInsertUpdate.readAsync response.`, true);
WsLogger.createLogEvent(socket, "ERROR", `Error in QueryDmsCustomerById - ${error}`);
throw new Error(error);
@@ -298,14 +302,14 @@ async function QueryDmsCustomerByName(socket, JobData) {
`soapClientCustomerSearch.executeSearchBulkAsync Result ${JSON.stringify(result, null, 2)}`
);
CheckCdkResponseForError(socket, soapResponseCustomerSearch);
const CustomersFromDms = (result && result.return) || [];
const CustomersFromDms = result?.return || [];
return CustomersFromDms;
} catch (error) {
WsLogger.createXmlEvent(socket, error.request, `soapClientCustomerSearch.executeSearchBulkAsync request.`, true);
WsLogger.createXmlEvent(
socket,
error.response && error.response.data,
error.response?.data,
`soapClientCustomerSearch.executeSearchBulkAsync response.`,
true
);
@@ -321,7 +325,8 @@ async function GenerateDmsCustomerNumber(socket) {
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.
//TODO: Verify why this does not follow the other standards.
arg1: { dealerId: socket.JobData.bodyshop.cdk_dealerid },
arg2: { userId: null }
},
@@ -340,7 +345,7 @@ async function GenerateDmsCustomerNumber(socket) {
`soapClientCustomerInsertUpdate.getCustomerNumberAsync Result ${JSON.stringify(result, null, 2)}`
);
CheckCdkResponseForError(socket, soapResponseCustomerInsertUpdate);
const customerNumber = result && result.return && result.return.customerNumber;
const customerNumber = result?.return && result.return.customerNumber;
return customerNumber;
} catch (error) {
WsLogger.createXmlEvent(
@@ -352,7 +357,7 @@ async function GenerateDmsCustomerNumber(socket) {
WsLogger.createXmlEvent(
socket,
error.response && error.response.data,
error.response?.data,
`soapClientCustomerInsertUpdate.getCustomerNumberAsync response.`,
true
);
@@ -428,17 +433,12 @@ async function InsertDmsCustomer(socket, newCustomerNumber) {
`soapClientCustomerInsertUpdate.insertAsync Result ${JSON.stringify(result, null, 2)}`
);
CheckCdkResponseForError(socket, soapResponseCustomerInsertUpdate);
const customer = result && result.return && result.return.customerParty;
const customer = result?.return && result.return.customerParty;
return customer;
} catch (error) {
WsLogger.createXmlEvent(socket, error.request, `soapClientCustomerInsertUpdate.insertAsync request.`, true);
WsLogger.createXmlEvent(
socket,
error.response && error.response.data,
`soapClientCustomerInsertUpdate.insertAsync response.`,
true
);
WsLogger.createXmlEvent(socket, error.response?.data, `soapClientCustomerInsertUpdate.insertAsync response.`, true);
WsLogger.createLogEvent(socket, "ERROR", `Error in InsertDmsCustomer - ${error}`);
throw new Error(error);
}
@@ -459,9 +459,9 @@ async function InsertDmsVehicle(socket) {
socket.txEnvelope.dms_unsold === true
? ""
: moment(socket.txEnvelope.inservicedate)
//.tz(socket.JobData.bodyshop.timezone)
.startOf("day")
.toISOString()
//.tz(socket.JobData.bodyshop.timezone)
.startOf("day")
.toISOString()
}),
vehicleId: socket.DMSVid.vehiclesVehId
},
@@ -471,16 +471,16 @@ async function InsertDmsVehicle(socket) {
socket.txEnvelope.dms_unsold === true
? ""
: moment()
// .tz(socket.JobData.bodyshop.timezone)
.format("YYYYMMDD"),
// .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(),
.replace(/([^\w]|_)/g, "")
.toUpperCase(),
make: socket.txEnvelope.dms_make,
modelAbrev: socket.txEnvelope.dms_model,
modelYear: socket.JobData.v_model_yr,
@@ -509,7 +509,7 @@ async function InsertDmsVehicle(socket) {
);
WsLogger.createXmlEvent(socket, rawResponse, `soapClientVehicleInsertUpdate.insertAsync response.`);
CheckCdkResponseForError(socket, soapResponseVehicleInsertUpdate);
const VehicleFromDMS = result && result.return && result.return.vehicle;
const VehicleFromDMS = result?.return && result.return.vehicle;
return VehicleFromDMS;
} catch (error) {
WsLogger.createLogEvent(socket, "ERROR", `Error in InsertDmsVehicle - ${error}`);
@@ -526,12 +526,10 @@ async function UpdateDmsVehicle(socket) {
//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;
ids = 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);
socket.DMSVeh?.owners && socket.DMSVeh.owners.find((o) => o.id.value === socket.DMSCust.id.value);
if (existingOwnerinVeh) {
ids = socket.DMSVeh.owners.map((o) => {
@@ -543,10 +541,7 @@ async function UpdateDmsVehicle(socket) {
};
});
} else {
const oldOwner =
socket.DMSVeh &&
socket.DMSVeh.owners &&
socket.DMSVeh.owners.find((o) => o.id.assigningPartyId === "CURRENT");
const oldOwner = socket.DMSVeh?.owners && socket.DMSVeh.owners.find((o) => o.id.assigningPartyId === "CURRENT");
ids = [
{
@@ -557,13 +552,13 @@ async function UpdateDmsVehicle(socket) {
},
...(oldOwner
? [
{
id: {
assigningPartyId: "PREVIOUS",
value: oldOwner.id.value
{
id: {
assigningPartyId: "PREVIOUS",
value: oldOwner.id.value
}
}
}
]
]
: [])
];
}
@@ -581,24 +576,24 @@ async function UpdateDmsVehicle(socket) {
socket.txEnvelope.dms_unsold === true
? ""
: moment(socket.DMSVeh.dealer.inServiceDate || socket.txEnvelope.inservicedate)
// .tz(socket.JobData.bodyshop.timezone)
.toISOString()
// .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
}
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()
//.tz(socket.JobData.bodyshop.timezone)
.toISOString()
},
owners: ids
},
@@ -615,7 +610,7 @@ async function UpdateDmsVehicle(socket) {
);
WsLogger.createXmlEvent(socket, rawResponse, `soapClientVehicleInsertUpdate.updateAsync response.`);
CheckCdkResponseForError(socket, soapResponseVehicleInsertUpdate);
const VehicleFromDMS = result && result.return && result.return.vehicle;
const VehicleFromDMS = result?.return && result.return.vehicle;
return VehicleFromDMS;
} catch (error) {
WsLogger.createLogEvent(socket, "ERROR", `Error in UpdateDmsVehicle - ${error}`);
@@ -654,7 +649,7 @@ async function InsertServiceVehicleHistory(socket) {
);
WsLogger.createXmlEvent(socket, rawResponse, `soapClientServiceHistoryInsert.serviceHistoryHeaderInsert response.`);
CheckCdkResponseForError(socket, soapResponseServiceHistoryInsert);
return result && result.return;
return result?.return;
} catch (error) {
WsLogger.createLogEvent(socket, "ERROR", `Error in InsertServiceVehicleHistory - ${error}`);
throw new Error(error);
@@ -672,8 +667,10 @@ async function InsertDmsStartWip(socket) {
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
docType: 10, // 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,
@@ -694,7 +691,7 @@ async function InsertDmsStartWip(socket) {
);
WsLogger.createXmlEvent(socket, rawResponse, `soapClientAccountingGLInsertUpdate.doStartWIPAsync response.`);
CheckCdkResponseForError(socket, soapResponseAccountingGLInsertUpdate);
const TransactionHeader = result && result.return;
const TransactionHeader = result?.return;
return TransactionHeader;
} catch (error) {
WsLogger.createLogEvent(socket, "ERROR", `Error in InsertDmsStartWip - ${error}`);
@@ -725,7 +722,7 @@ async function InsertDmsBatchWip(socket) {
);
WsLogger.createXmlEvent(socket, rawResponse, `soapClientAccountingGLInsertUpdate.doTransBatchWIPAsync response.`);
CheckCdkResponseForError(socket, soapResponseAccountingGLInsertUpdate);
const BatchWipResult = result && result.return;
const BatchWipResult = result?.return;
return BatchWipResult;
} catch (error) {
WsLogger.createLogEvent(socket, "ERROR", `Error in InsertDmsBatchWip - ${error}`);
@@ -743,9 +740,9 @@ async function GenerateTransWips(socket) {
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 !== null &&
alloc.profitCenter.dms_control_override !== undefined &&
alloc.profitCenter.dms_control_override?.trim() !== ""
? alloc.profitCenter.dms_control_override
: socket.JobData.ro_number,
cntl2: null,
@@ -766,9 +763,9 @@ async function GenerateTransWips(socket) {
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 !== null &&
alloc.costCenter.dms_control_override !== undefined &&
alloc.costCenter.dms_control_override?.trim() !== ""
? alloc.costCenter.dms_control_override
: socket.JobData.ro_number,
cntl2: null,
@@ -786,9 +783,9 @@ async function GenerateTransWips(socket) {
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 !== null &&
alloc.costCenter.dms_control_override !== undefined &&
alloc.costCenter.dms_control_override?.trim() !== ""
? alloc.costCenter.dms_control_override
: socket.JobData.ro_number,
cntl2: null,
@@ -827,9 +824,9 @@ async function GenerateTransWips(socket) {
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 !== null &&
alloc.profitCenter.dms_control_override !== undefined &&
alloc.profitCenter.dms_control_override?.trim() !== ""
? alloc.profitCenter.dms_control_override
: socket.JobData.ro_number,
cntl2: null,
@@ -889,7 +886,7 @@ async function PostDmsBatchWip(socket) {
);
WsLogger.createXmlEvent(socket, rawResponse, `soapClientAccountingGLInsertUpdate.doPostBatchWIPAsync response.`);
// CheckCdkResponseForError(socket, soapResponseAccountingGLInsertUpdate);
const PostResult = result && result.return;
const PostResult = result?.return;
return PostResult;
} catch (error) {
WsLogger.createLogEvent(socket, "ERROR", `Error in PostDmsBatchWip - ${error}`);
@@ -918,7 +915,7 @@ async function QueryDmsErrWip(socket) {
);
WsLogger.createXmlEvent(socket, rawResponse, `soapClientAccountingGLInsertUpdate.doErrWIPAsync response.`);
CheckCdkResponseForError(socket, soapResponseAccountingGLInsertUpdate);
const PostResult = result && result.return;
const PostResult = result?.return;
return PostResult;
} catch (error) {
WsLogger.createLogEvent(socket, "ERROR", `Error in QueryDmsErrWip - ${error}`);
@@ -949,7 +946,7 @@ async function DeleteDmsWip(socket) {
);
WsLogger.createXmlEvent(socket, rawResponse, `soapClientAccountingGLInsertUpdate.doPostBatchWIPAsync response.`);
CheckCdkResponseForError(socket, soapResponseAccountingGLInsertUpdate);
const PostResult = result && result.return;
const PostResult = result?.return;
return PostResult;
} catch (error) {
WsLogger.createLogEvent(socket, "ERROR", `Error in PostDmsBatchWip - ${error}`);
@@ -960,8 +957,11 @@ async function DeleteDmsWip(socket) {
async function MarkJobExported(socket, jobid) {
WsLogger.createLogEvent(socket, "DEBUG", `Marking job as exported for id ${jobid}`);
const client = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, {});
const currentToken =
(socket?.data && socket.data.authToken) || (socket?.handshake?.auth && socket.handshake.auth.token);
const result = await client
.setHeaders({ Authorization: `Bearer ${socket.handshake.auth.token}` })
.setHeaders({ Authorization: `Bearer ${currentToken}` })
.request(queries.MARK_JOB_EXPORTED, {
jobId: jobid,
job: {
@@ -987,8 +987,11 @@ async function MarkJobExported(socket, jobid) {
async function InsertFailedExportLog(socket, error) {
try {
const client = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, {});
const currentToken =
(socket?.data && socket.data.authToken) || (socket?.handshake?.auth && socket.handshake.auth.token);
const result = await client
.setHeaders({ Authorization: `Bearer ${socket.handshake.auth.token}` })
.setHeaders({ Authorization: `Bearer ${currentToken}` })
.request(queries.INSERT_EXPORT_LOG, {
log: {
bodyshopid: socket.JobData.bodyshop.id,

View File

@@ -6,7 +6,7 @@ const InstanceManager = require("../utils/instanceMgr").default;
const { isString, isEmpty } = require("lodash");
const fs = require("fs");
const client = require("../graphql-client/graphql-client").client;
const { sendServerEmail } = require("../email/sendemail");
const { sendServerEmail, sendMexicoBillingEmail } = require("../email/sendemail");
const { uploadFileToS3 } = require("../utils/s3");
const crypto = require("crypto");
@@ -168,6 +168,32 @@ async function processShopData(shopsToProcess, start, end, skipUpload, ignoreDat
await uploadViaSFTP(jsonObj);
}
await sendMexicoBillingEmail({
subject: `${shopid.toUpperCase()}_Mexico${InstanceManager({
imex: "IO",
rome: "RO"
})}_${moment().format("MMDDYYYY")} ROs ${jsonObj.count} Error ${errorCode(jsonObj)}`,
text: `Errors:\n${JSON.stringify(
erroredJobs.map((ej) => ({
ro_number: ej.job?.ro_number,
jobid: ej.job?.id,
error: ej.error
})),
null,
2
)}\n\nUploaded:\n${JSON.stringify(
{
bodyshopid: bodyshop.id,
imexshopid: shopid,
count: jsonObj.count,
filename: jsonObj.filename,
result: jsonObj.result
},
null,
2
)}`
});
allXMLResults.push({
bodyshopid: bodyshop.id,
imexshopid: shopid,
@@ -402,3 +428,14 @@ const generatePartType = (type) => {
return partTypeMap[type?.toLowerCase()] || null;
};
const errorCode = ({ count, filename, results }) => {
if (count === 0) return 1;
if (!filename) return 3;
const sftpErrorCode = results?.sftpError?.code;
if (sftpErrorCode && ["ECONNREFUSED", "ENOTFOUND", "ETIMEDOUT", "ECONNRESET"].includes(sftpErrorCode)) {
return 4;
}
if (sftpErrorCode) return 7;
return 0;
};

View File

@@ -79,6 +79,41 @@ const sendServerEmail = async ({ subject, text }) => {
}
};
const sendMexicoBillingEmail = async ({ subject, text }) => {
if (process.env.NODE_ENV === undefined) return;
try {
mailer.sendMail(
{
from: InstanceManager({
imex: `ImEX Online API - ${process.env.NODE_ENV} <noreply@imex.online>`,
rome: `Rome Online API - ${process.env.NODE_ENV} <noreply@romeonline.io>`
}),
to: ["mexico@rometech.zohodesk.com"],
subject: subject,
text: text,
ses: {
// optional extra arguments for SendRawEmail
Tags: [
{
Name: "tag_name",
Value: "tag_value"
}
]
}
},
// eslint-disable-next-line no-unused-vars
(err, info) => {
logger.log("server-email-failure", err ? "error" : "debug", null, null, {
message: err?.message,
stack: err?.stack
});
}
);
} catch (error) {
logger.log("server-email-failure", "error", null, null, { message: error?.message, stack: error?.stack });
}
};
const sendWelcomeEmail = async ({ to, resetLink, dateLine, features, bcc }) => {
try {
await mailer.sendMail({
@@ -420,6 +455,7 @@ ${body.bounce?.bouncedRecipients.map(
module.exports = {
sendEmail,
sendServerEmail,
sendMexicoBillingEmail,
sendTaskEmail,
emailBounce,
sendWelcomeEmail

View File

@@ -3,17 +3,29 @@ 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 axiosCurlirize = require('axios-curlirize').default;
// Custom error class for Fortellis API errors
class FortellisApiError extends Error {
constructor(message, details) {
super(message);
this.name = 'FortellisApiError';
this.reqId = details.reqId;
this.url = details.url;
this.apiName = details.apiName;
this.errorData = details.errorData;
this.errorStatus = details.errorStatus;
this.errorStatusText = details.errorStatusText;
this.originalError = details.originalError;
}
}
axiosCurlirize(axios, (result, err) => {
const { command } = result;
console.log("*** ~ axiosCurlirize ~ command:", command);
@@ -206,6 +218,24 @@ async function MakeFortellisCall({
} catch (error) {
console.log(`ReqID: ${ReqId} Error`, error.response?.data);
//console.log(`ReqID: ${ReqId} Full Error`, JSON.stringify(error, null, 4));
const errorDetails = {
reqId: ReqId,
url: fullUrl,
apiName,
errorData: error.response?.data,
errorStatus: error.response?.status,
errorStatusText: error.response?.statusText,
originalError: error
};
// CreateFortellisLogEvent(socket, "ERROR", `Error in MakeFortellisCall for ${apiName}: ${error.message}`, {
// ...errorDetails,
// errorStack: error.stack
// });
// Throw custom error with all the details
throw new FortellisApiError(`Fortellis API call failed for ${apiName}: ${error.message}`, errorDetails);
}
}
@@ -332,6 +362,20 @@ const FortellisActions = {
type: "post",
apiName: "CDK Drive Post Accounts GL WIP",
},
QueryErrorWip: {
url: isProduction
? "https://api.fortellis.io/cdk/drive/glpost/errWIP"
: "https://api.fortellis.io/cdk-test/drive/glpost/errWIP",
type: "get",
apiName: "CDK Drive Post Accounts GL WIP",
},
ServiceHistoryInsert: {
url: isProduction
? "https://api.fortellis.io/cdk/drive/post/service-vehicle-history-mgmt/v2/"
: "https://api.fortellis.io/cdk-test/drive/post/service-vehicle-history-mgmt/v2/",
type: "post",
apiName: "CDK Drive Post Service Vehicle History",
},
};
@@ -349,7 +393,8 @@ const FortellisCacheEnums = {
selectedCustomerId: "selectedCustomerId",
DMSTransHeader: "DMSTransHeader",
transWips: "transWips",
DmsBatchTxnPost: "DmsBatchTxnPost"
DmsBatchTxnPost: "DmsBatchTxnPost",
DMSVehHistory: "DMSVehHistory",
};
function constructFullUrl({ url, pathParams = "", requestSearchParams = [] }) {
@@ -369,5 +414,6 @@ module.exports = {
MakeFortellisCall,
FortellisActions,
getTransactionType,
defaultFortellisTTL
defaultFortellisTTL,
FortellisApiError
};

File diff suppressed because it is too large Load Diff

View File

@@ -36,9 +36,11 @@ const redisSocketEvents = ({
}
try {
const user = await admin.auth().verifyIdToken(token);
const user = await admin.auth().verifyIdToken(token, true);
socket.user = user;
socket.bodyshopId = bodyshopId;
socket.data = socket.data || {};
socket.data.authToken = token;
await addUserSocketMapping(user.email, socket.id, bodyshopId);
next();
} catch (error) {
@@ -69,6 +71,15 @@ const redisSocketEvents = ({
}
socket.user = user;
socket.bodyshopId = bodyshopId;
// 🔑 keep the live token in a mutable place used by downstream code
socket.data = socket.data || {};
socket.data.authToken = token;
// also keep handshake in sync for any legacy reads
if (socket.handshake?.auth) {
socket.handshake.auth.token = token;
socket.handshake.auth.bodyshopId = bodyshopId;
}
await refreshUserSocketTTL(user.email, bodyshopId);
socket.emit("token-updated", { success: true });
} catch (error) {

View File

@@ -9,7 +9,6 @@ const { PbsCalculateAllocationsAp, PbsExportAp } = require("../accounting/pbs/pb
const { createLogEvent } = require("./createLogEvent");
function SetLegacyWebsocketHandlers(io) {
io.use(function (socket, next) {
try {
if (socket.handshake.auth.token) {
@@ -53,6 +52,7 @@ function SetLegacyWebsocketHandlers(io) {
socket.on("cdk-export-job", (jobid) => {
CdkJobExport(socket, jobid);
});
socket.on("cdk-selected-customer", (selectedCustomerId) => {
createLogEvent(socket, "DEBUG", `User selected customer ID ${selectedCustomerId}`);
socket.selectedCustomerId = selectedCustomerId;
@@ -116,7 +116,6 @@ function SetLegacyWebsocketHandlers(io) {
createLogEvent(socket, "DEBUG", `User disconnected.`);
});
});
}
exports.SetLegacyWebsocketHandlers = SetLegacyWebsocketHandlers;
exports.SetLegacyWebsocketHandlers = SetLegacyWebsocketHandlers;