From 105ecd4221e0dc2acb22b4cf7c776cc2a6bdae33 Mon Sep 17 00:00:00 2001
From: Patrick Fic <>
Date: Wed, 30 Jun 2021 13:41:00 -0700
Subject: [PATCH 1/4] IO-233 CDK
---
client/src/pages/dms/dms.container.jsx | 2 +-
server/cdk/cdk-job-export.js | 285 +++++++++++++++++++++++--
server/cdk/cdk-wsdl.js | 33 ++-
3 files changed, 298 insertions(+), 22 deletions(-)
diff --git a/client/src/pages/dms/dms.container.jsx b/client/src/pages/dms/dms.container.jsx
index 342035ec5..3af437f24 100644
--- a/client/src/pages/dms/dms.container.jsx
+++ b/client/src/pages/dms/dms.container.jsx
@@ -105,7 +105,7 @@ export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader }) {
{log.level}
{moment(log.timestamp).format("MM/DD/YYYY HH:MM:ss")}
- {log.message}
+ {log.message}
))}
diff --git a/server/cdk/cdk-job-export.js b/server/cdk/cdk-job-export.js
index f4e7ee993..56362086e 100644
--- a/server/cdk/cdk-job-export.js
+++ b/server/cdk/cdk-job-export.js
@@ -13,6 +13,10 @@ const CdkWsdl = require("./cdk-wsdl").default;
const IMEX_CDK_USER = process.env.IMEX_CDK_USER,
IMEX_CDK_PASSWORD = process.env.IMEX_CDK_PASSWORD;
+const CDK_CREDENTIALS = {
+ password: IMEX_CDK_PASSWORD,
+ username: IMEX_CDK_USER,
+};
exports.default = async function (socket, jobid) {
socket.logEvents = [];
@@ -22,24 +26,94 @@ exports.default = async function (socket, jobid) {
"DEBUG",
`Received Job export request for id ${jobid}`
);
-
+ socket["cdk-job-export"] = {};
+ let clVFV, clADPV, clADPC;
const JobData = await QueryJobData(socket, jobid);
console.log(JSON.stringify(JobData, null, 2));
const DealerId = JobData.bodyshop.cdk_dealerid;
-
CdkBase.createLogEvent(
socket,
"TRACE",
`Dealer ID detected: ${JSON.stringify(DealerId)}`
);
- // Begin Calculate VID from DMS {1}
- await DetermineDMSVid(socket, JobData);
+ //{1} Begin Calculate DMS Vehicle Id
+ clVFV = await CalculateDmsVid(socket, JobData);
+ if (clVFV.newId === "Y") {
+ //{1.2} This is a new Vehicle ID
+ CdkBase.createLogEvent(
+ socket,
+ "DEBUG",
+ `{1.2} clVFV DMSVid does *not* exist. clVFV: ${JSON.stringify(
+ clVFV,
+ null,
+ 2
+ )}`
+ );
+
+ //Check if DMSCustId is Empty - which it should always be?
+ //{6.6} Should check to see if a customer exists so that we can marry it to the new vehicle.
+ CdkBase.createLogEvent(
+ socket,
+ "DEBUG",
+ `{6.6} Trying to find customer ID in DMS.`
+ );
+
+ //Array
+ const strIDS = await FindCustomerIdFromDms(socket, JobData);
+ if (strIDS.length > 0) {
+ CdkBase.createLogEvent(socket, "DEBUG", `{8.2} Customer ID(s) found.`);
+ } else {
+ CdkBase.createLogEvent(
+ socket,
+ "DEBUG",
+ `{8.5} Customer ID(s) *not* found.`
+ );
+ }
+ } else {
+ CdkBase.createLogEvent(
+ socket,
+ "DEBUG",
+ `{1.2} clVFV DMSVid does exist. clVFV: ${JSON.stringify(
+ clVFV,
+ null,
+ 2
+ )}`
+ );
+
+ //{2} Begin Find Vehicle in DMS
+ clADPV = await FindVehicleInDms(socket, JobData, clVFV); //TODO: Verify that this should always return a result. If an ID was found previously, it should be correct?
+
+ //{2.2} Check if the vehicle was found in the DMS.
+ if (clADPV.AppErrorNo === "0") {
+ //Vehicle was found.
+ CdkBase.createLogEvent(
+ socket,
+ "DEBUG",
+ `{1.4} Vehicle was found in the DMS. clADPV: ${JSON.stringify(
+ clADPV,
+ null,
+ 2
+ )}`
+ );
+ } else {
+ //Vehicle was not found.
+ CdkBase.createLogEvent(
+ socket,
+ "DEBUG",
+ `{6.4} Vehicle does not exist in DMS. Will have to create one. clVFV: ${JSON.stringify(
+ clVFV,
+ null,
+ 2
+ )}`
+ );
+ }
+ }
} catch (error) {
CdkBase.createLogEvent(
socket,
"ERROR",
- `Error encountered in JobExport. ${error}`
+ `Error encountered in CdkJobExport. ${error}`
);
} finally {
//Ensure we always insert logEvents
@@ -61,27 +135,200 @@ async function QueryJobData(socket, jobid) {
return result.jobs_by_pk;
}
-async function DetermineDMSVid(socket, JobData) {
- CdkBase.createLogEvent(socket, "TRACE", "{1} Begin Determine DMS VehicleID");
+async function FindCustomerIdFromDms(socket, JobData) {
+ const ownerName = `${JobData.ownr_ln},${JobData.ownr_fn}`;
+ CdkBase.createLogEvent(
+ socket,
+ "DEBUG",
+ `{8} Begin Read Customer from DMS using OWNER NAME: ${ownerName}`
+ );
try {
- //Create SOAP Request for
- const soapClient = await soap.createClientAsync(CdkWsdl.VehicleSearch);
- const result = await soapClient.searchIDsByVINAsync(
- {
- arg0: { password: IMEX_CDK_PASSWORD, username: IMEX_CDK_USER },
- arg1: { id: JobData.bodyshop.cdk_dealerid },
- arg2: { VIN: JobData.v_vin },
- },
-
- {}
+ const soapClientCustomerSearch = await soap.createClientAsync(
+ CdkWsdl.CustomerSearch
);
- console.log(result);
+ const soapResponseCustomerSearch =
+ await soapClientCustomerSearch.executeSearchBulkAsync(
+ {
+ arg0: CDK_CREDENTIALS,
+ arg1: { id: JobData.bodyshop.cdk_dealerid },
+ arg2: {
+ verb: "EXACT",
+ key: ownerName,
+ },
+ },
+
+ {}
+ );
+ CheckCdkResponseForError(socket, soapResponseCustomerSearch);
+ const [result, rawResponse, soapheader, rawRequest] =
+ soapResponseCustomerSearch;
+ //result format
+ // return: [
+ // {
+ // code: 'success',
+ // carInvStockNo: '',
+ // errorLevel: '0',
+ // errorMessage: '',
+ // newId: 'Y',
+ // vehiclesVehId: 'HM263407'
+ // }
+ // ]
+ CdkBase.createLogEvent(
+ socket,
+ "TRACE",
+ `soapClientCustomerSearch.executeSearchBulkAsync Result ${JSON.stringify(
+ result,
+ null,
+ 2
+ )}`
+ );
+ const CustomersFromDms = result && result.return;
+ return CustomersFromDms;
} catch (error) {
CdkBase.createLogEvent(
socket,
"ERROR",
- `Error in DetermineDMSVid - ${JSON.stringify(error, null, 2)}`
+ `Error in FindCustomerIdFromDms - ${JSON.stringify(error, null, 2)}`
);
}
}
+
+async function FindVehicleInDms(socket, JobData, clVFV) {
+ CdkBase.createLogEvent(
+ socket,
+ "DEBUG",
+ `{2}/{6} Begin Find Vehicle In DMS using clVFV: ${clVFV}`
+ );
+
+ try {
+ const soapClientVehicleInsertUpdate = await soap.createClientAsync(
+ CdkWsdl.VehicleInsertUpdate
+ );
+ const soapResponseVehicleInsertUpdate =
+ await soapClientVehicleInsertUpdate.readBulkAsync(
+ {
+ arg0: CDK_CREDENTIALS,
+ arg1: { id: JobData.bodyshop.cdk_dealerid },
+ arg2: {
+ fileType: "VEHICLES",
+ vehiclesVehicleId: clVFV.vehiclesVehId,
+ },
+ },
+
+ {}
+ );
+ CheckCdkResponseForError(socket, soapResponseVehicleInsertUpdate);
+ const [result, rawResponse, soapheader, rawRequest] =
+ soapResponseVehicleInsertUpdate;
+ //result format
+ // return: [
+ // {
+ // code: 'success',
+ // carInvStockNo: '',
+ // errorLevel: '0',
+ // errorMessage: '',
+ // newId: 'Y',
+ // vehiclesVehId: 'HM263407'
+ // }
+ // ]
+ CdkBase.createLogEvent(
+ socket,
+ "TRACE",
+ `soapClientVehicleInsertUpdate.readBulkAsync Result ${JSON.stringify(
+ result,
+ null,
+ 2
+ )}`
+ );
+ const VehicleFromDMS = result && result.return && result.return[0];
+ return VehicleFromDMS;
+ } catch (error) {
+ CdkBase.createLogEvent(
+ socket,
+ "ERROR",
+ `Error in FindVehicleInDms - ${JSON.stringify(error, null, 2)}`
+ );
+ }
+}
+
+async function CalculateDmsVid(socket, JobData) {
+ CdkBase.createLogEvent(
+ socket,
+ "TRACE",
+ `{1} Begin Calculate DMS Vehicle ID using VIN: ${JobData.v_vin}`
+ );
+
+ try {
+ 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 },
+ },
+
+ {}
+ );
+ CheckCdkResponseForError(socket, soapResponseVehicleInsertUpdate);
+ const [result, rawResponse, soapheader, rawRequest] =
+ soapResponseVehicleInsertUpdate;
+ //result format
+ // return: [
+ // {
+ // code: 'success',
+ // carInvStockNo: '',
+ // errorLevel: '0',
+ // errorMessage: '',
+ // newId: 'Y',
+ // vehiclesVehId: 'HM263407'
+ // }
+ // ]
+ CdkBase.createLogEvent(
+ socket,
+ "TRACE",
+ `soapClientVehicleInsertUpdate.searchIDsByVINAsync Result ${JSON.stringify(
+ result,
+ null,
+ 2
+ )}`
+ );
+ const DmsVehicle = result && result.return && result.return[0];
+ return DmsVehicle;
+ } catch (error) {
+ CdkBase.createLogEvent(
+ socket,
+ "ERROR",
+ `Error in CalculateDmsVid - ${JSON.stringify(error, null, 2)}`
+ );
+ }
+}
+
+function CheckCdkResponseForError(socket, soapResponse) {
+ const ResultToCheck = Array.isArray(soapResponse[0].return)
+ ? soapResponse[0].return[0]
+ : soapResponse[0].return;
+
+ if (ResultToCheck.errorLevel === 0 || ResultToCheck.errorLevel === "0")
+ //TODO: Verify that this is the best way to detect errors.
+ return;
+ else {
+ CdkBase.createLogEvent(
+ socket,
+ "ERROR",
+ `Error detected in CDK Response - ${JSON.stringify(
+ ResultToCheck,
+ null,
+ 2
+ )}`
+ );
+
+ throw {
+ errorLevel: ResultToCheck.errorLevel,
+ errorMessage: ResultToCheck.errorMessage,
+ };
+ }
+}
diff --git a/server/cdk/cdk-wsdl.js b/server/cdk/cdk-wsdl.js
index acf66a0b5..9364dcdc3 100644
--- a/server/cdk/cdk-wsdl.js
+++ b/server/cdk/cdk-wsdl.js
@@ -1,4 +1,33 @@
+const path = require("path");
+require("dotenv").config({
+ path: path.resolve(
+ process.cwd(),
+ `.env.${process.env.NODE_ENV || "development"}`
+ ),
+});
+
+// const cdkDomain =
+// process.env.NODE_ENV === "production"
+// ? "https://3pa.dmotorworks.com"
+// : "https://uat-3pa.dmotorworks.com";
+
+const cdkDomain = "https://uat-3pa.dmotorworks.com";
exports.default = {
- VehicleSearch:
- "https://uat-3pa.dmotorworks.com/pip-vehicle/services/VehicleSearch?wsdl",
+ // VehicleSearch: `${cdkDomain}/pip-vehicle/services/VehicleSearch?wsdl`,
+ VehicleInsertUpdate: `${cdkDomain}/pip-vehicle/services/VehicleInsertUpdate?wsdl`,
+ CustomerInsertUpdate: `${cdkDomain}/pip-customer/services/CustomerInsertUpdate?wsdl`,
+ CustomerSearch: `${cdkDomain}/pip-customer/services/CustomerSearch?wsdl`,
};
+
+// The following login credentials will be used for all PIPs and all environments (User Acceptance Testing and Production).
+// Only the URLs will change from https://uat-3pa.dmoto... to https://3pa.dmoto... or https://api-dit.connect... to https://api.connect...
+// Accounting GL/Accounting GL WIP Update - https://uat-3pa.dmotorworks.com/pip-accounting-gl/services/AccountingGLInsertUpdate?wsdl
+// Customer Insert Update - https://uat-3pa.dmotorworks.com/pip-customer/services/CustomerInsertUpdate?wsdl
+// Help Database Location - https://uat-3pa.dmotorworks.com/pip-help-database-location/services/HelpDatabaseLocation?wsdl
+// Parts Inventory Insert Update - https://uat-3pa.dmotorworks.com/pip-parts-inventory/services/PartsInventoryInsertUpdate?wsdl
+// Purchase Order Insert - https://uat-3pa.dmotorworks.com/pip-purchase-order/services/PurchaseOrderInsert?wsdl
+// Repair Order MLS Insert Update - https://uat-3pa.dmotorworks.com/pip-repair-order-mls/services/RepairOrderMLSInsertUpdate?wsdl
+// Repair Order Parts Insert Update - https://uat-3pa.dmotorworks.com/pip-repair-order-parts/services/RepairOrderPartsInsertUpdate?wsdl
+// Service History Insert - https://uat-3pa.dmotorworks.com/pip-service-history-insert/services/ServiceHistoryInsert?wsdl
+// Service Repair Order Update - https://uat-3pa.dmotorworks.com/pip-service-repair-order/services/ServiceRepairOrderUpdate?wsdl
+// Service Vehicle Insert Update - https://uat-3pa.dmotorworks.com/pip-vehicle/services/VehicleInsertUpdate?wsdl
From 4ab0947cc8b290cffc9518b645bce566762b6a75 Mon Sep 17 00:00:00 2001
From: Patrick Fic <>
Date: Wed, 30 Jun 2021 16:04:01 -0700
Subject: [PATCH 2/4] IO-233 Add customer insert actions.
---
client/src/pages/dms/dms.container.jsx | 8 +-
server/cdk/cdk-job-export.js | 222 +++++++++++++++++++++----
server/web-sockets/web-socket.js | 4 +-
3 files changed, 202 insertions(+), 32 deletions(-)
diff --git a/client/src/pages/dms/dms.container.jsx b/client/src/pages/dms/dms.container.jsx
index 3af437f24..85fb8e233 100644
--- a/client/src/pages/dms/dms.container.jsx
+++ b/client/src/pages/dms/dms.container.jsx
@@ -55,6 +55,10 @@ export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader }) {
socket.on("connected", () => {
console.log("Connected again.");
});
+ socket.on("reconnect", () => {
+ console.log("Connected again.");
+ setLogs([]);
+ });
socket.on("log-event", (payload) => {
setLogs((logs) => {
@@ -119,11 +123,11 @@ function LogLevelHierarchy(level) {
case "TRACE":
return "pink";
case "DEBUG":
- return "orange";
+ return "green";
case "INFO":
return "blue";
case "WARNING":
- return "yellow";
+ return "orange";
case "ERROR":
return "red";
default:
diff --git a/server/cdk/cdk-job-export.js b/server/cdk/cdk-job-export.js
index 56362086e..178eab6a7 100644
--- a/server/cdk/cdk-job-export.js
+++ b/server/cdk/cdk-job-export.js
@@ -61,20 +61,51 @@ exports.default = async function (socket, jobid) {
//Array
const strIDS = await FindCustomerIdFromDms(socket, JobData);
- if (strIDS.length > 0) {
- CdkBase.createLogEvent(socket, "DEBUG", `{8.2} Customer ID(s) found.`);
+ if (strIDS && strIDS.length > 0) {
+ CdkBase.createLogEvent(
+ socket,
+ "DEBUG",
+ `{8.2} Customer ID(s) found. strIDS: ${JSON.stringify(
+ strIDS,
+ null,
+ 2
+ )}`
+ );
} else {
CdkBase.createLogEvent(
socket,
"DEBUG",
`{8.5} Customer ID(s) *not* found.`
);
+
+ //Create a customer number, then use that to insert the customer record.
+ const newCustomerNumber = await GenerateCustomerNumberFromDms(
+ socket,
+ JobData
+ );
+ CdkBase.createLogEvent(
+ socket,
+ "DEBUG",
+ `{10.1} New Customer number generated. newCustomerNumber: ${newCustomerNumber}`
+ );
+
+ //Use the new customer number to insert the customer record.
+ clADPC = await CreateCustomerInDms(socket, JobData, newCustomerNumber);
+ CdkBase.createLogEvent(
+ socket,
+ "DEBUG",
+ `{11.1} New Customer inserted. customer: ${JSON.stringify(
+ clADPC,
+ null,
+ 2
+ )}`
+ );
}
} else {
CdkBase.createLogEvent(
socket,
"DEBUG",
- `{1.2} clVFV DMSVid does exist. clVFV: ${JSON.stringify(
+ `{1.1} clVFV DMSVid does exist. clVFV: ${JSON.stringify(
clVFV,
null,
2
@@ -135,6 +166,125 @@ async function QueryJobData(socket, jobid) {
return result.jobs_by_pk;
}
+async function CreateCustomerInDms(socket, JobData, newCustomerNumber) {
+ CdkBase.createLogEvent(socket, "DEBUG", `{10} Begin Create Customer in DMS`);
+
+ try {
+ const soapClientCustomerInsertUpdate = await soap.createClientAsync(
+ CdkWsdl.CustomerInsertUpdate
+ );
+ const soapResponseCustomerInsertUpdate =
+ await soapClientCustomerInsertUpdate.insertAsync(
+ {
+ arg0: CDK_CREDENTIALS,
+ arg1: { dealerId: JobData.bodyshop.cdk_dealerid }, //TODO: Verify why this does not follow the other standards.
+ 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: {
+ city: JobData.ownr_city,
+ country: null,
+ postalcode: JobData.ownr_zip,
+ stateOrProvince: JobData.ownr_st,
+ },
+ contactInfo: {
+ mainTelephoneNumber: { main: true, value: JobData.ownr_ph1 },
+ },
+ demographics: null,
+ name1: {
+ companyname: null,
+ firstName: JobData.ownr_fn,
+ fullname: null,
+ lastName: JobData.ownr_ln,
+ middleName: null,
+ nameType: "Person",
+ suffix: null,
+ title: null,
+ },
+ },
+ },
+
+ {}
+ );
+ CheckCdkResponseForError(socket, soapResponseCustomerInsertUpdate);
+ const [result, rawResponse, soapheader, rawRequest] =
+ soapResponseCustomerInsertUpdate;
+
+ CdkBase.createLogEvent(
+ socket,
+ "TRACE",
+ `soapClientCustomerInsertUpdate.insertAsync Result ${JSON.stringify(
+ result,
+ null,
+ 2
+ )}`
+ );
+ const customer = result && result.return;
+ return customer;
+ } catch (error) {
+ CdkBase.createLogEvent(
+ socket,
+ "ERROR",
+ `Error in CreateCustomerInDms - ${JSON.stringify(error, null, 2)}`
+ );
+ throw new Error(error);
+ }
+}
+
+async function GenerateCustomerNumberFromDms(socket, JobData) {
+ CdkBase.createLogEvent(
+ socket,
+ "DEBUG",
+ `{10} Begin Generate Customer Number from DMS`
+ );
+
+ try {
+ const soapClientCustomerInsertUpdate = await soap.createClientAsync(
+ CdkWsdl.CustomerInsertUpdate
+ );
+ const soapResponseCustomerInsertUpdate =
+ await soapClientCustomerInsertUpdate.getCustomerNumberAsync(
+ {
+ arg0: CDK_CREDENTIALS,
+ arg1: { dealerId: JobData.bodyshop.cdk_dealerid }, //TODO: Verify why this does not follow the other standards.
+ arg2: { userId: null },
+ },
+
+ {}
+ );
+ CheckCdkResponseForError(socket, soapResponseCustomerInsertUpdate);
+ const [
+ result, //rawResponse, soapheader, rawRequest
+ ] = soapResponseCustomerInsertUpdate;
+
+ CdkBase.createLogEvent(
+ socket,
+ "TRACE",
+ `soapClientCustomerInsertUpdate.getCustomerNumberAsync Result ${JSON.stringify(
+ result,
+ null,
+ 2
+ )}`
+ );
+ const customerNumber =
+ result && result.return && result.return.customerNumber;
+ return customerNumber;
+ } catch (error) {
+ CdkBase.createLogEvent(
+ socket,
+ "ERROR",
+ `Error in GenerateCustomerNumberFromDms - ${JSON.stringify(
+ error,
+ null,
+ 2
+ )}`
+ );
+ throw new Error(error);
+ }
+}
+
async function FindCustomerIdFromDms(socket, JobData) {
const ownerName = `${JobData.ownr_ln},${JobData.ownr_fn}`;
CdkBase.createLogEvent(
@@ -148,12 +298,12 @@ async function FindCustomerIdFromDms(socket, JobData) {
CdkWsdl.CustomerSearch
);
const soapResponseCustomerSearch =
- await soapClientCustomerSearch.executeSearchBulkAsync(
+ await soapClientCustomerSearch.executeSearchAsync(
{
arg0: CDK_CREDENTIALS,
- arg1: { id: JobData.bodyshop.cdk_dealerid },
+ arg1: { dealerId: JobData.bodyshop.cdk_dealerid }, //TODO: Verify why this does not follow the other standards.
arg2: {
- verb: "EXACT",
+ // verb: "EXACT",
key: ownerName,
},
},
@@ -161,19 +311,9 @@ async function FindCustomerIdFromDms(socket, JobData) {
{}
);
CheckCdkResponseForError(socket, soapResponseCustomerSearch);
- const [result, rawResponse, soapheader, rawRequest] =
- soapResponseCustomerSearch;
- //result format
- // return: [
- // {
- // code: 'success',
- // carInvStockNo: '',
- // errorLevel: '0',
- // errorMessage: '',
- // newId: 'Y',
- // vehiclesVehId: 'HM263407'
- // }
- // ]
+ const [
+ result, // rawResponse, soapheader, rawRequest
+ ] = soapResponseCustomerSearch;
CdkBase.createLogEvent(
socket,
"TRACE",
@@ -191,6 +331,7 @@ async function FindCustomerIdFromDms(socket, JobData) {
"ERROR",
`Error in FindCustomerIdFromDms - ${JSON.stringify(error, null, 2)}`
);
+ throw new Error(error);
}
}
@@ -219,8 +360,9 @@ async function FindVehicleInDms(socket, JobData, clVFV) {
{}
);
CheckCdkResponseForError(socket, soapResponseVehicleInsertUpdate);
- const [result, rawResponse, soapheader, rawRequest] =
- soapResponseVehicleInsertUpdate;
+ const [
+ result, //rawResponse, soapheader, rawRequest
+ ] = soapResponseVehicleInsertUpdate;
//result format
// return: [
// {
@@ -249,6 +391,7 @@ async function FindVehicleInDms(socket, JobData, clVFV) {
"ERROR",
`Error in FindVehicleInDms - ${JSON.stringify(error, null, 2)}`
);
+ throw new Error(error);
}
}
@@ -274,8 +417,9 @@ async function CalculateDmsVid(socket, JobData) {
{}
);
CheckCdkResponseForError(socket, soapResponseVehicleInsertUpdate);
- const [result, rawResponse, soapheader, rawRequest] =
- soapResponseVehicleInsertUpdate;
+ const [
+ result, //rawResponse, soapheader, rawRequest
+ ] = soapResponseVehicleInsertUpdate;
//result format
// return: [
// {
@@ -304,15 +448,32 @@ async function CalculateDmsVid(socket, JobData) {
"ERROR",
`Error in CalculateDmsVid - ${JSON.stringify(error, null, 2)}`
);
+ throw new Error(error);
}
}
function CheckCdkResponseForError(socket, soapResponse) {
- const ResultToCheck = Array.isArray(soapResponse[0].return)
+ if (!soapResponse[0]) {
+ //The response was null, this might be ok, it might not.
+ CdkBase.createLogEvent(
+ socket,
+ "WARNING",
+ `Warning detected in CDK Response - it appears to be null. Stack: ${
+ new Error().stack
+ }`
+ );
+ return;
+ }
+
+ const ResultToCheck = Array.isArray(soapResponse[0] && soapResponse[0].return)
? soapResponse[0].return[0]
: soapResponse[0].return;
- if (ResultToCheck.errorLevel === 0 || ResultToCheck.errorLevel === "0")
+ if (
+ ResultToCheck.errorLevel === 0 ||
+ ResultToCheck.errorLevel === "0" ||
+ ResultToCheck.code === "success"
+ )
//TODO: Verify that this is the best way to detect errors.
return;
else {
@@ -326,9 +487,12 @@ function CheckCdkResponseForError(socket, soapResponse) {
)}`
);
- throw {
- errorLevel: ResultToCheck.errorLevel,
- errorMessage: ResultToCheck.errorMessage,
- };
+ throw new Error(
+ `Error found while validating CDK response for ${JSON.stringify(
+ ResultToCheck,
+ null,
+ 2
+ )}:`
+ );
}
}
diff --git a/server/web-sockets/web-socket.js b/server/web-sockets/web-socket.js
index e3a909889..64e498a90 100644
--- a/server/web-sockets/web-socket.js
+++ b/server/web-sockets/web-socket.js
@@ -1,5 +1,4 @@
const path = require("path");
-const _ = require("lodash");
require("dotenv").config({
path: path.resolve(
process.cwd(),
@@ -72,6 +71,9 @@ function createLogEvent(socket, level, message) {
message,
});
}
+ // if (level === "ERROR") {
+ // throw new Error(message);
+ // }
}
}
From 84b39f3d2b21f09251dddaa9e4da150cc889fcae Mon Sep 17 00:00:00 2001
From: Patrick Fic <>
Date: Fri, 2 Jul 2021 12:53:18 -0700
Subject: [PATCH 3/4] IO-233 WIP CDK
---
.../dms-customer-selector.component.jsx | 79 +++++++++++
client/src/pages/dms/dms.container.jsx | 76 +++++++----
server/cdk/cdk-job-export.js | 128 +++++++++---------
3 files changed, 197 insertions(+), 86 deletions(-)
create mode 100644 client/src/components/dms-customer-selector/dms-customer-selector.component.jsx
diff --git a/client/src/components/dms-customer-selector/dms-customer-selector.component.jsx b/client/src/components/dms-customer-selector/dms-customer-selector.component.jsx
new file mode 100644
index 000000000..e0f41dd97
--- /dev/null
+++ b/client/src/components/dms-customer-selector/dms-customer-selector.component.jsx
@@ -0,0 +1,79 @@
+import React, { useEffect, useState } from "react";
+import { socket } from "../../pages/dms/dms.container";
+import { alphaSort } from "../../utils/sorters";
+import { Table } from "antd";
+import { useTranslation } from "react-i18next";
+
+export default function DmsCustomerSelector() {
+ const { t } = useTranslation();
+ const [customerList, setcustomerList] = useState([]);
+ const [selectedCustomer, setSelectedCustomer] = useState(null);
+ useEffect(() => {
+ socket.on("cdk-select-customer", (customerList) => {
+ setcustomerList(customerList);
+ console.log("Received a customer list.", customerList);
+ });
+
+ return () => {
+ socket.removeListener("cdk-select-customer");
+ };
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, []);
+
+ const columns = [
+ {
+ title: t("owners.fields.ownr_ln"),
+ dataIndex: "ownr_ln",
+ key: "ownr_ln",
+ sorter: (a, b) => alphaSort(a.ownr_ln, b.ownr_ln),
+ },
+ {
+ title: t("owners.fields.ownr_fn"),
+ dataIndex: "ownr_fn",
+ key: "ownr_fn",
+ sorter: (a, b) => alphaSort(a.ownr_fn, b.ownr_fn),
+ },
+ {
+ title: t("owners.fields.ownr_co_nm"),
+ dataIndex: "ownr_co_nm",
+ key: "ownr_co_nm",
+ sorter: (a, b) => alphaSort(a.ownr_co_nm, b.ownr_co_nm),
+ },
+ {
+ title: t("owners.fields.ownr_addr1"),
+ dataIndex: "ownr_addr1",
+ key: "ownr_addr1",
+ sorter: (a, b) => alphaSort(a.ownr_addr1, b.ownr_addr1),
+ },
+ {
+ title: t("owners.fields.ownr_city"),
+ dataIndex: "ownr_city",
+ key: "ownr_city",
+ sorter: (a, b) => alphaSort(a.ownr_city, b.ownr_city),
+ },
+ {
+ title: t("owners.fields.ownr_ea"),
+ dataIndex: "ownr_ea",
+ key: "ownr_ea",
+ sorter: (a, b) => alphaSort(a.ownr_ea, b.ownr_ea),
+ },
+ ];
+
+ return (
+
{
+ setSelectedCustomer(props.id);
+ },
+ type: "radio",
+ selectedRowKeys: [selectedCustomer],
+ }}
+ />
+ );
+}
diff --git a/client/src/pages/dms/dms.container.jsx b/client/src/pages/dms/dms.container.jsx
index 85fb8e233..f171daa75 100644
--- a/client/src/pages/dms/dms.container.jsx
+++ b/client/src/pages/dms/dms.container.jsx
@@ -1,4 +1,4 @@
-import { Result, Timeline, Space, Tag, Divider, Button } from "antd";
+import { Result, Timeline, Space, Tag, Divider, Button, Select } from "antd";
import React, { useEffect, useState } from "react";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
@@ -11,6 +11,7 @@ import { useTranslation } from "react-i18next";
import SocketIO from "socket.io-client";
import { auth } from "../../firebase/firebase.utils";
import moment from "moment";
+import DmsCustomerSelector from "../../components/dms-customer-selector/dms-customer-selector.component";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
@@ -38,6 +39,7 @@ export const socket = SocketIO(
export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader }) {
const { t } = useTranslation();
+ const [logLevel, setLogLevel] = useState("DEBUG");
const [logs, setLogs] = useState([]);
useEffect(() => {
@@ -57,7 +59,16 @@ export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader }) {
});
socket.on("reconnect", () => {
console.log("Connected again.");
- setLogs([]);
+ setLogs((logs) => {
+ return [
+ ...logs,
+ {
+ timestamp: new Date(),
+ level: "WARNING",
+ message: "Reconnected to CDK Export Service",
+ },
+ ];
+ });
});
socket.on("log-event", (payload) => {
@@ -67,12 +78,13 @@ export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader }) {
});
socket.connect();
- socket.emit("set-log-level", "TRACE");
+ socket.emit("set-log-level", logLevel);
return () => {
socket.removeAllListeners();
socket.disconnect();
};
+ // eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
if (!bodyshop.cdk_dealerid) return ;
@@ -81,27 +93,43 @@ export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader }) {
return (
-
-
-
-
+
+
+
+
+
+
+
{logs.map((log, idx) => (
diff --git a/server/cdk/cdk-job-export.js b/server/cdk/cdk-job-export.js
index 178eab6a7..f09af0edc 100644
--- a/server/cdk/cdk-job-export.js
+++ b/server/cdk/cdk-job-export.js
@@ -44,13 +44,13 @@ exports.default = async function (socket, jobid) {
CdkBase.createLogEvent(
socket,
"DEBUG",
- `{1.2} clVFV DMSVid does *not* exist. clVFV: ${JSON.stringify(
- clVFV,
- null,
- 2
- )}`
+ `{1.2} clVFV DMSVid does *not* exist.`
+ );
+ CdkBase.createLogEvent(
+ socket,
+ "TRACE",
+ `{1.2} clVFV: ${JSON.stringify(clVFV, null, 2)}`
);
-
//Check if DMSCustId is Empty - which it should always be?
//{6.6} Should check to see if a customer exists so that we can marry it to the new vehicle.
CdkBase.createLogEvent(
@@ -65,12 +65,30 @@ exports.default = async function (socket, jobid) {
CdkBase.createLogEvent(
socket,
"DEBUG",
- `{8.2} Customer ID(s) found. strIDS: ${JSON.stringify(
- strIDS,
- null,
- 2
- )}`
+ `{8.2} ${strIDS.length} Customer ID(s) found.`
);
+ CdkBase.createLogEvent(
+ socket,
+ "TRACE",
+ `{8.2} strIDS: ${JSON.stringify(strIDS, null, 2)}`
+ );
+ if (strIDS.length > 1) {
+ //We have multiple IDs
+ //TODO: Do we need to let the person select it?
+ CdkBase.createLogEvent(
+ socket,
+ "WARNING",
+ `{F} Mutliple customer ids have been found (${strIDS.length})`
+ );
+ CdkBase.createLogEvent(
+ socket,
+ "DEBUG",
+ `Asking for user intervention to select customer.`
+ );
+ socket.emit("cdk-select-customer", strIDS);
+
+ //TOOD: Need to find a way to wait and determine which customer to use.
+ }
} else {
CdkBase.createLogEvent(
socket,
@@ -94,22 +112,20 @@ exports.default = async function (socket, jobid) {
CdkBase.createLogEvent(
socket,
"DEBUG",
- `{11.1} New Customer inserted. customer: ${JSON.stringify(
- clADPC,
- null,
- 2
- )}`
+ `{11.1} New Customer inserted.`
+ );
+ CdkBase.createLogEvent(
+ socket,
+ "TRACE",
+ `{11.1} clADPC: ${JSON.stringify(clADPC, null, 2)}`
);
}
} else {
+ CdkBase.createLogEvent(socket, "DEBUG", `{1.1} clVFV DMSVid does exist.`);
CdkBase.createLogEvent(
socket,
- "DEBUG",
- `{1.1} clVFV DMSVid does exist. clVFV: ${JSON.stringify(
- clVFV,
- null,
- 2
- )}`
+ "TRACE",
+ `{1.1} clVFV: ${JSON.stringify(clVFV, null, 2)}`
);
//{2} Begin Find Vehicle in DMS
@@ -121,22 +137,24 @@ exports.default = async function (socket, jobid) {
CdkBase.createLogEvent(
socket,
"DEBUG",
- `{1.4} Vehicle was found in the DMS. clADPV: ${JSON.stringify(
- clADPV,
- null,
- 2
- )}`
+ `{1.4} Vehicle was found in the DMS.`
+ );
+ CdkBase.createLogEvent(
+ socket,
+ "TRACE",
+ `{1.4} clADPV: ${JSON.stringify(clADPV, null, 2)}`
);
} else {
//Vehicle was not found.
CdkBase.createLogEvent(
socket,
"DEBUG",
- `{6.4} Vehicle does not exist in DMS. Will have to create one. clVFV: ${JSON.stringify(
- clVFV,
- null,
- 2
- )}`
+ `{6.4} Vehicle does not exist in DMS. Will have to create one.`
+ );
+ CdkBase.createLogEvent(
+ socket,
+ "TRACE",
+ `{6.4} clVFV: ${JSON.stringify(clVFV, null, 2)}`
);
}
}
@@ -167,7 +185,7 @@ async function QueryJobData(socket, jobid) {
}
async function CreateCustomerInDms(socket, JobData, newCustomerNumber) {
- CdkBase.createLogEvent(socket, "DEBUG", `{10} Begin Create Customer in DMS`);
+ CdkBase.createLogEvent(socket, "DEBUG", `{11} Begin Create Customer in DMS`);
try {
const soapClientCustomerInsertUpdate = await soap.createClientAsync(
@@ -209,8 +227,9 @@ async function CreateCustomerInDms(socket, JobData, newCustomerNumber) {
{}
);
CheckCdkResponseForError(socket, soapResponseCustomerInsertUpdate);
- const [result, rawResponse, soapheader, rawRequest] =
- soapResponseCustomerInsertUpdate;
+ const [
+ result, //rawResponse, soapheader, rawRequest
+ ] = soapResponseCustomerInsertUpdate;
CdkBase.createLogEvent(
socket,
@@ -303,7 +322,7 @@ async function FindCustomerIdFromDms(socket, JobData) {
arg0: CDK_CREDENTIALS,
arg1: { dealerId: JobData.bodyshop.cdk_dealerid }, //TODO: Verify why this does not follow the other standards.
arg2: {
- // verb: "EXACT",
+ verb: "EXACT",
key: ownerName,
},
},
@@ -363,17 +382,6 @@ async function FindVehicleInDms(socket, JobData, clVFV) {
const [
result, //rawResponse, soapheader, rawRequest
] = soapResponseVehicleInsertUpdate;
- //result format
- // return: [
- // {
- // code: 'success',
- // carInvStockNo: '',
- // errorLevel: '0',
- // errorMessage: '',
- // newId: 'Y',
- // vehiclesVehId: 'HM263407'
- // }
- // ]
CdkBase.createLogEvent(
socket,
"TRACE",
@@ -420,17 +428,6 @@ async function CalculateDmsVid(socket, JobData) {
const [
result, //rawResponse, soapheader, rawRequest
] = soapResponseVehicleInsertUpdate;
- //result format
- // return: [
- // {
- // code: 'success',
- // carInvStockNo: '',
- // errorLevel: '0',
- // errorMessage: '',
- // newId: 'Y',
- // vehiclesVehId: 'HM263407'
- // }
- // ]
CdkBase.createLogEvent(
socket,
"TRACE",
@@ -465,14 +462,21 @@ function CheckCdkResponseForError(socket, soapResponse) {
return;
}
- const ResultToCheck = Array.isArray(soapResponse[0] && soapResponse[0].return)
- ? soapResponse[0].return[0]
- : soapResponse[0].return;
+ const ResultToCheck = soapResponse[0].return;
+ if (Array.isArray(ResultToCheck)) {
+ ResultToCheck.forEach((result) => checkIndividualResult(socket, result));
+ } else {
+ checkIndividualResult(socket, ResultToCheck);
+ }
+}
+
+function checkIndividualResult(socket, ResultToCheck) {
if (
ResultToCheck.errorLevel === 0 ||
ResultToCheck.errorLevel === "0" ||
- ResultToCheck.code === "success"
+ ResultToCheck.code === "success" ||
+ (!ResultToCheck.code && !ResultToCheck.errorLevel)
)
//TODO: Verify that this is the best way to detect errors.
return;
From f0d6c5e1b1e8bcd1caf017fcbd2b98b62ad56da6 Mon Sep 17 00:00:00 2001
From: Patrick Fic <>
Date: Tue, 6 Jul 2021 09:31:01 -0700
Subject: [PATCH 4/4] IO-233 CDK WIP
---
_reference/Responsibility Center Setup.md | 6 ++
bodyshop_translations.babel | 2 +-
.../dms-customer-selector.component.jsx | 90 +++++++++----------
server/cdk/cdk-job-export.js | 35 +++++---
server/web-sockets/web-socket.js | 9 ++
5 files changed, 82 insertions(+), 60 deletions(-)
create mode 100644 _reference/Responsibility Center Setup.md
diff --git a/_reference/Responsibility Center Setup.md b/_reference/Responsibility Center Setup.md
new file mode 100644
index 000000000..b52ef1189
--- /dev/null
+++ b/_reference/Responsibility Center Setup.md
@@ -0,0 +1,6 @@
+Glass Setup
+
+{"ap":{"name":"Accounts Payable","accountdesc":"Accounts Payable","accountitem":"Accounts Payable","accountname":"Accounts Payable","accountnumber":"20000"},"ar":{"name":"Accounts Receivable","accountdesc":"Accounts Receivable","accountitem":"Accounts Receivable","accountname":"Accounts Receivable","accountnumber":"12000"},"costs":[{"name":"Auto Glass Parts","accountdesc":"Glass","accountitem":"Auto Glass Parts","accountname":"Cost of Goods:Auto Glass Parts","accountnumber":"Glass"},{"name":"Auto Glass Labour","accountdesc":"Auto Glass Labour","accountitem":"Auto Glass Labour","accountname":"Cost of Goods:Auto Glass Labour","accountnumber":"Auto Glass Labour"},{"name":"Flat Glass Parts","accountdesc":"Flat Glass ","accountitem":"Flat Glass Parts","accountname":"Cost of Goods:Flat Glass Parts","accountnumber":"Flat Glass Parts"},{"name":"Flat Glass Labour","accountdesc":"Flat Glass ","accountitem":"Flat Glass Labour","accountname":"Cost of Goods:Flat Glass Labour","accountnumber":"Flat Glass Labour"},{"name":"Home Glass Parts","accountdesc":"Home Glass Parts","accountitem":"Home Glass Parts","accountname":"Cost of Goods:Home Glass Parts","accountnumber":"Home Glass Parts"},{"name":"Home Glass Labour","accountdesc":"Home Glass Labour","accountitem":"Home Glass Labour","accountname":"Cost of Goods:Home Glass Labour","accountnumber":"Home Glass Labour"},{"name":"Misc Parts","accountdesc":"Misc Parts","accountitem":"Misc Parts","accountname":"Cost of Goods:Misc Parts","accountnumber":"Misc Parts"}],"taxes":{"local":{"name":"n","rate":0,"accountdesc":"n","accountitem":"n","accountname":"n","accountnumber":"n"},"state":{"name":"PST","rate":7,"accountdesc":"Ministry of Finance (BC)","accountitem":"PST (BC)","accountname":"PST PAYABLE","accountnumber":"PST PAYABLE"},"federal":{"name":"GST","rate":5,"accountdesc":"Receiver General - GST","accountitem":"GST","accountname":"HST/GST PAYABLE","accountnumber":"HST/GST PAYABLE"}},"refund":{"name":"Refund","accountdesc":"ACCOUNTS RECEIVABLE","accountitem":"BODY SHOP_CUSTPAY","accountname":"ACCOUNTS RECEIVABLE","accountnumber":"ACCOUNTS RECEIVABLE"},"profits":[{"name":"Auto Glass Parts","accountdesc":"Auto Glass","accountitem":"Auto Glass Parts","accountname":"Sales:Auto Glass Parts","accountnumber":"APG"},{"name":"Auto Glass Labour","accountdesc":"Auto Glass Labour","accountitem":"Auto Glass Labour","accountname":"Sales:Auto Glass Labour","accountnumber":"APL"},{"name":"Flat Glass Parts","accountdesc":"Flat Glass","accountitem":"Flat Glass Parts","accountname":"Sales:Flat Glass Parts","accountnumber":"FGP"},{"name":"Flat Glass Labour","accountdesc":"Flat Glass Labour","accountitem":"Flat Glass Labour","accountname":"Sales:Flat Glass Labour","accountnumber":"FGL"},{"name":"Home Glass Parts","accountdesc":"Home Glass Parts","accountitem":"Home Glass Parts","accountname":"Sales:Home Glass Parts","accountnumber":"HGP"},{"name":"Home Glass Labour","accountdesc":"Home Glass Labour","accountitem":"Home Glass Labour","accountname":"Sales:Home Glass Labour","accountnumber":"HGL"},{"name":"Misc Parts","accountdesc":"Misc Parts","accountitem":"Misc Parts","accountname":"Sales:Misc Parts","accountnumber":"MP"}],"defaults":{"costs":{"ATS":"Auto Glass Labour","LAB":"Auto Glass Labour","LAD":"Auto Glass Labour","LAE":"Auto Glass Labour","LAF":"Auto Glass Labour","LAG":"Auto Glass Labour","LAM":"Auto Glass Labour","LAR":"Auto Glass Labour","LAS":"Auto Glass Labour","LAU":"Auto Glass Labour","PAA":"Auto Glass Parts","PAC":"Auto Glass Parts","PAL":"Auto Glass Parts","PAM":"Auto Glass Parts","PAN":"Auto Glass Parts","PAO":"Auto Glass Parts","PAP":"Auto Glass Parts","PAR":"Auto Glass Parts","PAS":"Auto Glass Parts","TOW":"Auto Glass Parts","MAPA":"Auto Glass Labour","MASH":"Auto Glass Labour"},"profits":{"ATS":"Auto Glass Labour","LAB":"Auto Glass Labour","LAD":"Auto Glass Labour","LAE":"Auto Glass Labour","LAF":"Auto Glass Labour","LAG":"Auto Glass Labour","LAM":"Auto Glass Labour","LAR":"Auto Glass Labour","LAS":"Auto Glass Labour","LAU":"Auto Glass Labour","PAA":"Auto Glass Parts","PAC":"Auto Glass Parts","PAL":"Auto Glass Parts","PAM":"Auto Glass Parts","PAN":"Auto Glass Parts","PAO":"Auto Glass Parts","PAP":"Auto Glass Parts","PAR":"Auto Glass Parts","PAS":"Auto Glass Parts","TOW":"Auto Glass Parts","MAPA":"Auto Glass Parts","MASH":"Auto Glass Parts"}},"sales_tax_codes":[{"code":"G","local":false,"state":false,"federal":true,"description":"GST Only"},{"code":"S","state":true,"federal":true,"description":"Standard"},{"code":"E","local":false,"state":false,"federal":false,"description":"Exempt"}]}
+
+Regular Dev Setup
+{"ap": {"name": "AP", "accountdesc": "Pay to Others", "accountitem": "A/P", "accountname": "AP Acc#", "accountnumber": "Accounts Payable"}, "ar": {"name": "AR", "accountdesc": "1100", "accountitem": "A/R", "accountname": "ACCOUNTS RECEIVABLE", "accountnumber": "1100"}, "costs": [{"name": "Aftermarket", "accountdesc": "Aftermarket", "accountitem": "Aftermarketi", "accountname": "BODY SHOP COST:PARTS:AFTERMARKET", "accountnumber": "Aftermarket"}, {"name": "ATP", "accountdesc": "ATP", "accountitem": "BODY SHOP_ATP", "accountname": "BODY SHOP COST:ATP", "accountnumber": "ATP"}, {"name": "Body", "accountdesc": "BODY SHOP COST:LABOR", "accountitem": "BODY SHOP_LAB", "accountname": "BODY SHOP COST:LABOR:BODY", "accountnumber": "5001"}, {"name": "Detail", "accountdesc": "Detailing", "accountitem": "Detaili", "accountname": "BODY SHOP COST:LABOR:DETAIL", "accountnumber": "Detail"}, {"name": "Daignostic", "accountdesc": "Daignostic", "accountitem": "Daignostici", "accountname": "Daignostic", "accountnumber": "Daignostic"}, {"name": "Electrical", "accountdesc": "Electrical", "accountitem": "Electricali", "accountname": "Electrical", "accountnumber": "Electrical"}, {"name": "Chrome", "accountdesc": "Chrome", "accountitem": "Chromei", "accountname": "Chrome", "accountnumber": "Chrome"}, {"name": "Frame", "accountdesc": "Frame", "accountitem": "Framei", "accountname": "BODY SHOP COST:LABOR:Frame", "accountnumber": "Frame"}, {"name": "Mechanical", "accountdesc": "Mechanical", "accountitem": "Mechanicali", "accountname": "BODY SHOP COST:LABOR:MECHANICAL", "accountnumber": "Mechanical"}, {"name": "Refinish", "accountdesc": "Refinish", "accountitem": "BODY SHOP_LAR", "accountname": "BODY SHOP COST:LABOR:REFINISH", "accountnumber": "5003"}, {"name": "Structural", "accountdesc": "Structural", "accountitem": "Structurali", "accountname": "Structural", "accountnumber": "Structural"}, {"name": "Existing", "accountdesc": "Existing", "accountitem": "Existingi", "accountname": "Existing", "accountnumber": "Existing"}, {"name": "Glass", "accountdesc": "Glass", "accountitem": "Glassi", "accountname": "BODY SHOP COST:PARTS:Glass", "accountnumber": "Glass"}, {"name": "LKQ", "accountdesc": "LKQ", "accountitem": "LKQi", "accountname": "BODY SHOP COST:PARTS:LKQ", "accountnumber": "LKQ"}, {"name": "OEM", "accountdesc": "OEM", "accountitem": "OEMi", "accountname": "BODY SHOP COST:PARTS:OEM", "accountnumber": "OEM"}, {"name": "OEM Partial", "accountdesc": "Partial", "accountitem": "Partial", "accountname": "BODY SHOP COST:PARTS:OEM Partial", "accountnumber": "OEM Partial"}, {"name": "Re-cored", "accountdesc": "cored", "accountitem": "coredi", "accountname": "Re-cored", "accountnumber": "Re-cored"}, {"name": "Remanufactured", "accountdesc": "Remanufactured", "accountitem": "Remanufacturedi", "accountname": "BODY SHOP COST:PARTS:LKQ", "accountnumber": "Remanufactured"}, {"name": "Other", "accountdesc": "Other", "accountitem": "Otheri", "accountname": "Other", "accountnumber": "Other"}, {"name": "Sublet", "accountdesc": "Sublet to Other", "accountitem": "Subleti", "accountname": "BODY SHOP COST:SUBLET", "accountnumber": "Sublet"}, {"name": "Towing", "accountdesc": "Towingd", "accountitem": "Towingi", "accountname": "BODY SHOP COST:TOWING", "accountnumber": "Towing"}, {"name": "Paint Cost", "accountdesc": "Paint Material by Labor", "accountitem": "BODY SHOP_MAPA", "accountname": "BODY SHOP COST:PARTS:Materials", "accountnumber": "paint mat"}, {"name": "Shop Cost", "accountdesc": "Shop Materials by Labor", "accountitem": "BODY SHOP_MASH", "accountname": "BODY SHOP COST:PARTS:Materials", "accountnumber": "shop"}], "taxes": {"local": {"name": "n", "rate": 0, "accountdesc": "n", "accountitem": "n", "accountname": "n", "accountnumber": "n"}, "state": {"name": "PST", "rate": 7, "accountdesc": "Ministry of Finance (BC)", "accountitem": "PST On Sales", "accountname": "PST Payable", "accountnumber": "2220"}, "federal": {"name": "GST", "rate": 5, "accountdesc": "Receiver General - GST", "accountitem": "GST On Sales", "accountname": "GST DUE-NET:GST Collected", "accountnumber": "2200a"}}, "refund": {"name": "Refund", "accountdesc": "1100", "accountitem": "BODY SHOP_CUSTPAY", "accountname": "ACCOUNTS RECEIVABLE", "accountnumber": "1100"}, "profits": [{"name": "Aftermarket", "accountdesc": "Aftermarket", "accountitem": "BODY SHOP_PAA", "accountname": "Aftermarket", "accountnumber": "Aftermarket"}, {"name": "ATP", "accountdesc": "ATP", "accountitem": "BODY SHOP_ATP", "accountname": "ATP", "accountnumber": "ATP"}, {"name": "Body", "accountdesc": "BODY SHOP SALESLABOR:BODY", "accountitem": "BODY SHOP_LAB", "accountname": "BODY SHOP SALES:LABOR:BODY", "accountnumber": "5002"}, {"name": "Detail", "accountdesc": "Detail", "accountitem": "BODY SHOP_DET", "accountname": "Detail", "accountnumber": "Detail"}, {"name": "Daignostic", "accountdesc": "Daignostic", "accountitem": "BODY SHOP_LAD", "accountname": "Daignostic", "accountnumber": "Daignostic"}, {"name": "Electrical", "accountdesc": "Electrical", "accountitem": "BODY SHOP_LAE", "accountname": "Electrical", "accountnumber": "Electrical"}, {"name": "Chrome", "accountdesc": "Chrome", "accountitem": "BODY SHOP_PAC", "accountname": "Chrome", "accountnumber": "Chrome"}, {"name": "Frame", "accountdesc": "Frame", "accountitem": "BODY SHOP_LAF", "accountname": "Frame", "accountnumber": "Frame"}, {"name": "Mechanical", "accountdesc": "Mechanical", "accountitem": "BODY SHOP_LAM", "accountname": "Mechanical", "accountnumber": "Mechanical"}, {"name": "Refinish", "accountdesc": "BODY SHOP SALES:LABOR:REFINISH", "accountitem": "BODY SHOP_LAR", "accountname": "BODY SHOP SALES:LABOR:REFINISH", "accountnumber": "5003"}, {"name": "Structural", "accountdesc": "Structural", "accountitem": "BODY SHOP_LAS", "accountname": "Structural", "accountnumber": "Structural"}, {"name": "Existing", "accountdesc": "Existing", "accountitem": "BODY SHOP_PAE", "accountname": "Existing", "accountnumber": "Existing"}, {"name": "Glass", "accountdesc": "Glass", "accountitem": "BODY SHOP_PAG", "accountname": "Glass", "accountnumber": "Glass"}, {"name": "LKQ", "accountdesc": "LKQ", "accountitem": "BODY SHOP_PAL", "accountname": "BODY SHOP SALES:PARTS:LKQ", "accountnumber": "LKQ"}, {"name": "OEM", "accountdesc": "BODY SHOP SALES:PARTS:OEM", "accountitem": "BODY SHOP_PAN", "accountname": "BODY SHOP SALES:PARTS:OEM", "accountnumber": "OEM"}, {"name": "OEM Partial", "accountdesc": "Partial", "accountitem": "BODY SHOP_PAP", "accountname": "OEM Partial", "accountnumber": "OEM Partial"}, {"name": "Re-cored", "accountdesc": "cored", "accountitem": "BODY SHOP_PAO", "accountname": "Re-cored", "accountnumber": "Re-cored"}, {"name": "Remanufactured", "accountdesc": "Remanufactured", "accountitem": "BODY SHOP_PAO", "accountname": "Remanufactured", "accountnumber": "Remanufactured"}, {"name": "Other", "accountdesc": "Other", "accountitem": "BODY SHOP_PAO", "accountname": "Other", "accountnumber": "Other"}, {"name": "Sublet", "accountdesc": "Sublet", "accountitem": "BODY SHOP_PAS", "accountname": "Sublet", "accountnumber": "Sublet"}, {"name": "Towing", "accountdesc": "Towing", "accountitem": "BODY SHOP_TOW", "accountname": "Towing", "accountnumber": "Towing"}, {"name": "Paint Profit", "accountdesc": "Paint Material Costs by Labor", "accountitem": "BODY SHOP_MAPA", "accountname": "paint", "accountnumber": "paint"}, {"name": "Shop Profit", "accountdesc": "Shop Material Costs by Labor", "accountitem": "BODY SHOP_MASH", "accountname": "shop", "accountnumber": "shop"}], "defaults": {"costs": {"ATS": "ATP", "LAB": "Body", "LAD": "Daignostic", "LAE": "Electrical", "LAF": "Frame", "LAG": "Glass", "LAM": "Mechanical", "LAR": "Refinish", "LAS": "Structural", "LAU": "Detail", "PAA": "Aftermarket", "PAC": "Chrome", "PAL": "LKQ", "PAM": "Remanufactured", "PAN": "OEM", "PAO": "Other", "PAP": "OEM Partial", "PAR": "Re-cored", "PAS": "Sublet", "TOW": "Towing", "MAPA": "Paint Cost", "MASH": "Shop Cost"}, "profits": {"ATS": "ATP", "LAB": "Body", "LAD": "Daignostic", "LAE": "Electrical", "LAF": "Frame", "LAG": "Glass", "LAM": "Mechanical", "LAR": "Refinish", "LAS": "Structural", "LAU": "Detail", "PAA": "Aftermarket", "PAC": "Chrome", "PAL": "LKQ", "PAM": "Remanufactured", "PAN": "OEM", "PAO": "Other", "PAP": "OEM Partial", "PAR": "Re-cored", "PAS": "Sublet", "TOW": "Towing", "MAPA": "Paint Profit", "MASH": "Shop Profit"}}, "sales_tax_codes": [{"code": "G", "local": false, "state": false, "federal": true, "description": "GST Only"}, {"code": "S", "state": true, "federal": true, "description": "Both"}, {"code": "E", "local": false, "state": false, "federal": false, "description": "Exempt"}]}
diff --git a/bodyshop_translations.babel b/bodyshop_translations.babel
index f63a4e31b..cf3050035 100644
--- a/bodyshop_translations.babel
+++ b/bodyshop_translations.babel
@@ -1,4 +1,4 @@
-
+