Files
bodyshop/server/data/arms.js

971 lines
30 KiB
JavaScript

const path = require("path");
const queries = require("../graphql-client/queries");
const Dinero = require("dinero.js");
const moment = require("moment-timezone");
const fs = require("fs");
const storage = require("node-persist");
const _ = require("lodash");
const logger = require("../utils/logger");
require("dotenv").config({
path: path.resolve(process.cwd(), `.env.${process.env.NODE_ENV || "development"}`)
});
const soap = require("soap");
const { sendServerEmail } = require("../email/sendemail");
const entegralEndpoint =
process.env.NODE_ENV === "production"
? "https://ws.entegral.com/RepairOrderFolderService/RepairOrderFolderService.asmx?op=RepairOrderFolderAddRq"
: "https://uat-ws.armsbusinesssolutions.net/RepairOrderFolderService/RepairOrderFolderService.asmx?WSDL";
const client = require("../graphql-client/graphql-client").client;
const { v4 } = require("uuid");
const momentFormat = "yyyy-MM-DDTHH:mm:ss.SSS";
function pollFunc(fn, timeout, interval) {
var startTime = new Date().getTime();
(interval = interval || 1000), (canPoll = true);
(function p() {
canPoll = timeout === 0 ? true : new Date().getTime() - startTime <= timeout;
if (fn() && canPoll) {
// ensures the function exucutes
setTimeout(p, interval);
}
})();
}
pollFunc(getEntegralShopData, 0, 5 * 60 * 1000); //Set the metadata to refresh every 5 minutes.
async function getEntegralShopData() {
// await storage.init({ logging: true });
// const { bodyshops } = await client.request(queries.GET_ENTEGRAL_SHOPS);
// logger.log("set-entegral-shops-local-storage", "DEBUG", "API", null, null);
// await storage.setItem("entegralShops", bodyshops);
// return true; //Continue execution.
}
exports.default = async (req, res) => {
res.sendStatus(401);
return;
//Query for the List of Bodyshop Clients.
const job = req.body.event.data.new;
logger.log("arms-job-update", "DEBUG", "api", job.id, null);
let allEntegralShops = await storage.getItem("entegralShops");
if (!allEntegralShops) {
await getEntegralShopData();
allEntegralShops = await storage.getItem("entegralShops");
}
//Is this job part of an entegral shop?
const bodyshop = allEntegralShops.find((b) => b.id === job.shopid);
if (!bodyshop) {
//This job is not for entegral based shops.
res.sendStatus(200);
return;
}
if (process.env.NODE_ENV === "PRODUCTION") {
res.sendStatus(200);
return;
}
//TODO: Check if an update should even be sent.
if (false) {
res.sendStatus(200);
return;
}
try {
const transId = v4(); // Can this actually be the job id?
let obj = {
RqUID: transId,
DocumentInfo: {
BMSVer: "4.0.0",
DocumentType: "RO",
DocumentVerCode: "EM",
DocumentVerNum: GetSupplementNumber(job.joblines), //TODO Get Supplement Number
DocumentStatus: GetDocumentstatus(job, bodyshop),
CreateDateTime: moment().format(momentFormat),
TransmitDateTime: moment().format(momentFormat) // Omitted from ARMS docs
},
EventInfo: {
AssignmentEvent: {
CreateDateTime: job.asgn_date && moment(job.asgn_date).format(momentFormat)
},
// EstimateEvent: {
// UploadDateTime: moment().format(momentFormat),
// },
RepairEvent: {
CreatedDateTime: (job.date_open ? moment(job.date_open).tz(bodyshop.timezone) : moment()).format(
momentFormat
),
ArrivalDateTime: job.actual_in && moment(job.actual_in).tz(bodyshop.timezone).format(momentFormat),
ArrivalOdometerReading: job.kmin,
TargetCompletionDateTime:
job.scheduled_completion && moment(job.scheduled_completion).tz(bodyshop.timezone).format(momentFormat),
ActualCompletionDateTime:
job.actual_completion && moment(job.actual_completion).tz(bodyshop.timezone).format(momentFormat),
ActualPickUpDateTime:
job.actual_delivery && moment(job.actual_delivery).tz(bodyshop.timezone).format(momentFormat),
CloseDateTime: job.date_exported && moment(job.date_exported).tz(bodyshop.timezone).format(momentFormat)
}
},
RepairOrderHeader: {
AdminInfo: {
InsuranceCompany: {
Party: {
OrgInfo: {
CompanyName: job.ins_co_nm,
IDInfo: {
IDQualifierCode: "US"
//IDNum: 44, // ** Not sure where to get this entegral ID from?
}
// Communications: [
// {
// CommQualifier: "WA",
// Address: {
// Address1: job.ins_addr1,
// Address2: job.ins_addr2,
// City: job.ins_city,
// StateProvince: job.ins_st,
// PostalCode: job.ins_zip,
// CountryCode: job.ins_ctry,
// },
// },
// {
// CommQualifier: "WP",
// CommPhone: job.ins_ph1,
// },
// {
// CommQualifier: "WF",
// CommPhone: job.ins_ph2,
// },
// ],
}
// ContactInfo: {
// ContactJobTitle: "Adjuster",
// ContactName: {
// FirstName: job.est_ct_fn,
// LastName: job.est_ct_ln,
// },
// },
}
},
// InsuranceAgent: {
// Party: {
// OrgInfo: {
// CompanyName: "Nationwide Insurance",
// Communications: {
// CommQualifier: "WP",
// CommPhone: "714-5551212",
// },
// },
// ContactInfo: {
// ContactJobTitle: "Insurance Agent",
// ContactName: {
// FirstName: "Paul",
// LastName: "White",
// },
// },
// },
// },
// Insured: {
// Party: {
// PersonInfo: {
// PersonName: {
// FirstName: job.insd_fn,
// LastName: job.insd_ln,
// },
// },
// },
// },
Owner: {
Party: {
PersonInfo: {
PersonName: {
FirstName: job.ownr_co_nm ? "N/A" : job.ownr_fn,
LastName: job.ownr_co_nm ? job.ownr_co_nm : job.ownr_ln
}
// Communications: [
// {
// CommQualifier: "HA",
// Address: {
// Address1: job.ownr_addr1,
// City: job.ownr_city,
// StateProvince: job.ownr_st,
// PostalCode: job.ownr_zip,
// CountryCode: job.ownr_ctry,
// },
// },
// {
// CommQualifier: "HP",
// CommPhone: job.ownr_ph1,
// },
// {
// CommQualifier: "WP",
// CommPhone: job.ownr_ph2,
// },
// {
// CommQualifier: "CP",
// CommPhone: job.ownr_ph1,
// },
// {
// CommQualifier: "EM",
// CommEmail: job.ownr_ea,
// },
// ],
}
}
},
// Claimant: {
// Party: {
// PersonInfo: {
// PersonName: {
// FirstName: job.clm_ct_fn,
// LastName: job.clm_ct_ln,
// },
// },
// },
// OwnerInd: true,
// },
// Estimator: {
// Party: {
// PersonInfo: {
// PersonName: {
// FirstName: job.est_ct_fn,
// LastName: job.est_ct_ln,
// },
// // IDInfo: {
// // IDQualifierCode: "US",
// // IDNum: 2941,
// // },
// },
// },
// },
RepairFacility: {
Party: {
OrgInfo: {
CompanyName: process.env.NODE_ENV === "production" ? bodyshop.shopname : "IMEX Test Canadian Shop",
IDInfo: {
IDQualifierCode: "US",
IDNum: bodyshop.entegral_id
}
}
}
}
},
RepairOrderIDs: {
RepairOrderNum: job.ro_number
// VendorCode: "C",
// EstimateDocumentID: "1223HJ76",
},
RepairOrderType: "DirectRepairProgram", //Need to get from Entegral
//ReferralSourceType: "Yellow Pages",
VehicleInfo: {
VINInfo: {
VIN: {
VINNum: job.v_vin
}
},
License: {
LicensePlateNum: job.plate_no
},
VehicleDesc: {
//ProductionDate: "2009-10",
ModelYear:
parseInt(job.v_model_yr) < 1900
? parseInt(job.v_model_yr) < moment().tz(bodyshop.timezone).format("YY")
? `20${job.v_model_yr}`
: `19${job.v_model_yr}`
: job.v_model_yr,
MakeDesc: job.v_make_desc,
ModelName: job.v_model_desc
}
// Paint: {
// Exterior: {
// Color: {
// ColorName: job.v_color,
// // OEMColorCode: "1M3",
// },
// },
// },
// Body: {
// BodyStyle: "2 Door Convertible",
// Trim: {
// TrimCode: "1B3",
// },
// },
// Condition: {
// DrivableInd: job.driveable ? "Y" : "N",
// },
},
ClaimInfo: {
ClaimNum: job.clm_no,
PolicyInfo: {
PolicyNum: job.policy_no
},
LossInfo: {
Facts: {
LossDateTime:
job.loss_date &&
moment(job.loss_date)
//.tz(bodyshop.timezone)
.format(momentFormat),
LossDescCode: "Collision",
PrimaryPOI: {
POICode: job.area_of_damage && job.area_of_damage.impact1
},
SecondaryPOI: {
POICode: job.area_of_damage && job.area_of_damage.impact2
}
},
TotalLossInd: job.tlos_ind
}
}
},
ProfileInfo: {
ProfileName: "ImEX",
RateInfo: [
{
RateType: "PA",
RateDesc: "Parts Tax",
TaxInfo: {
TaxType: "LS",
TaxableInd: true,
TaxTierInfo: {
TierNum: 1,
Percentage: job.parts_tax_rates.PAN.prt_tax_rt * 100 //TODO Find the best place to take the tax rates for parts.
}
}
},
{
RateType: "LA",
RateDesc: "Labor Tax",
TaxInfo: {
TaxType: "LS",
TaxableInd: true,
TaxTierInfo: {
TierNum: 1,
Percentage: job.parts_tax_rates.PAN.prt_tax_rt * 100 //TODO Find the best place to take the tax rates for labor.
}
}
},
{
RateType: "LAB",
RateDesc: "Body Labor",
RateTierInfo: {
TierNum: 1,
Rate: job.rate_lab
}
},
{
RateType: "LAS",
RateDesc: "Structural Labor",
RateTierInfo: {
TierNum: 1,
Rate: job.rate_las
}
},
{
RateType: "LAR",
RateDesc: "Refinish Labor",
RateTierInfo: {
TierNum: 1,
Rate: job.rate_lar
}
},
{
RateType: "LAG",
RateDesc: "Glass Labor",
RateTierInfo: {
TierNum: 1,
Rate: job.rate_lag
}
},
{
RateType: "LAF",
RateDesc: "Frame Labor",
RateTierInfo: {
TierNum: 1,
Rate: job.rate_laf
}
},
{
RateType: "LAM",
RateDesc: "Mechancial Labor",
RateTierInfo: {
TierNum: 1,
Rate: job.rate_lam
}
},
{
RateType: "LAU",
RateDesc: "User Defined Labor",
RateTierInfo: {
TierNum: 1,
Rate: job.rate_lau
}
},
{
RateType: "MAPA",
RateDesc: "Paint Materials",
RateTierInfo: {
TierNum: 1,
Rate: job.rate_mapa,
ThresholdAmt: 0
},
MaterialCalcSettings: {
CalcMethodCode: 2,
CalcMaxAmt: 9999.99 //TODO Find threshold amts.
}
},
{
RateType: "MASH",
RateDesc: "Shop Materials",
RateTierInfo: {
TierNum: 1,
Rate: job.rate_mash
},
MaterialCalcSettings: {
CalcMethodCode: 4,
CalcMaxAmt: 9999.99 //TODO Find threshold amounts.
}
},
{
RateType: "MAHW",
RateDesc: "Hazardous Wastes Removal",
RateTierInfo: {
TierNum: 1,
Rate: job.rate_mahw
},
MaterialCalcSettings: {
//Todo Capture Calc Settings
CalcMethodCode: 2,
CalcMaxAmt: 10
}
},
{
RateType: "MA2S",
RateDesc: "Two Stage Paint",
RateTierInfo: {
TierNum: 1,
Rate: job.rate_ma2s
},
MaterialCalcSettings: {
CalcMethodCode: 1,
CalcMaxAmt: 999999.99
}
},
{
RateType: "MA2T",
RateDesc: "Two Tone Paint",
RateTierInfo: {
TierNum: 1,
Rate: job.rate_ma2t
},
MaterialCalcSettings: {
CalcMethodCode: 1,
CalcMaxAmt: 999999.99
}
},
{
RateType: "MA3S",
RateDesc: "Three Stage Paint",
RateTierInfo: {
TierNum: 1,
Rate: job.rate_ma3s
},
MaterialCalcSettings: {
CalcMethodCode: 1,
CalcMaxAmt: 999999.99
}
}
]
},
//StorageDuration: 17,
RepairTotalsInfo: {
LaborTotalsInfo: [
{
TotalType: "LAB",
TotalTypeDesc: "Body Labor",
TotalHours: job.job_totals.rates.lab.hours,
TotalAmt: Dinero(job.job_totals.rates.lab.total).toFormat("0.00")
},
{
TotalType: "LAF",
TotalTypeDesc: "Frame Labor",
TotalHours: job.job_totals.rates.laf.hours,
TotalAmt: Dinero(job.job_totals.rates.laf.total).toFormat("0.00")
},
{
TotalType: "LAM",
TotalTypeDesc: "Mechanical Labor",
TotalHours: job.job_totals.rates.lam.hours,
TotalAmt: Dinero(job.job_totals.rates.lam.total).toFormat("0.00")
},
{
TotalType: "LAR",
TotalTypeDesc: "Refinish Labor",
TotalHours: job.job_totals.rates.lar.hours,
TotalAmt: Dinero(job.job_totals.rates.lar.total).toFormat("0.00")
}
],
PartsTotalsInfo: [
{
TotalType: "PAA",
TotalTypeDesc: "Aftermarket Parts",
TotalAmt: Dinero(job.job_totals.parts.parts.list.PAA && job.job_totals.parts.parts.list.PAA.total).toFormat(
"0.00"
)
},
{
TotalType: "PAC",
TotalTypeDesc: "Re-Chromed Parts",
TotalAmt: Dinero(job.job_totals.parts.parts.list.PAC && job.job_totals.parts.parts.list.PAC.total).toFormat(
"0.00"
)
},
{
TotalType: "PAG",
TotalTypeDesc: "Glass Parts",
TotalAmt: Dinero(job.job_totals.parts.parts.list.PAG && job.job_totals.parts.parts.list.PAG.total).toFormat(
"0.00"
)
},
{
TotalType: "PAL",
TotalTypeDesc: "LKQ/Used Parts",
TotalAmt: Dinero(job.job_totals.parts.parts.list.PAL && job.job_totals.parts.parts.list.PAL.total).toFormat(
"0.00"
)
},
{
TotalType: "PAM",
TotalTypeDesc: "Remanufactured Parts",
TotalAmt: Dinero(job.job_totals.parts.parts.list.PAM && job.job_totals.parts.parts.list.PAM.total).toFormat(
"0.00"
)
},
{
TotalType: "PAN",
TotalTypeDesc: "New Parts",
TotalAmt: Dinero(job.job_totals.parts.parts.list.PAN && job.job_totals.parts.parts.list.PAN.total).toFormat(
"0.00"
)
},
{
TotalType: "PAR",
TotalTypeDesc: "Recored Parts",
TotalAmt: Dinero(job.job_totals.parts.parts.list.PAR && job.job_totals.parts.parts.list.PAR.total).toFormat(
"0.00"
)
}
],
OtherChargesTotalsInfo: [
{
TotalType: "OTSL",
TotalTypeDesc: "Sublet",
TotalAmt: Dinero(job.job_totals.parts.sublets.total).toFormat("0.00")
},
{
TotalType: "MAPA",
TotalTypeDesc: "Paint Materials",
TotalAmt: Dinero(job.job_totals.rates.mapa.total).toFormat("0.00")
},
{
TotalType: "MASH",
TotalTypeDesc: "Shop Materials",
TotalAmt: Dinero(job.job_totals.rates.mash.total).toFormat("0.00")
},
// {
// TotalType: "MAHW",
// TotalTypeDesc: "Hazardous Wastes Removal",
// TotalAmt: Dinero(job.job_totals.rates.mahw.total).toFormat(
// 0.0
// ),
// },
{
TotalType: "OTST",
TotalTypeDesc: "Storage",
TotalAmt: Dinero(job.job_totals.additional.storage).toFormat("0.00")
},
{
TotalType: "OTTW",
TotalTypeDesc: "Towing",
TotalAmt: Dinero(job.job_totals.additional.towing).toFormat("0.00")
},
{
TotalType: "OTAC",
TotalTypeDesc: "Additional Charges",
TotalAmt: Dinero(job.job_totals.additional.additionalCosts)
.add(Dinero(job.job_totals.additional.pvrt))
.toFormat("0.00")
}
],
SummaryTotalsInfo: [
{
TotalType: "TOT",
TotalSubType: "TT",
TotalTypeDesc: "Gross Total",
TotalAmt: Dinero(job.job_totals.totals.total_repairs).toFormat("0.00")
},
{
TotalType: "TOT",
TotalSubType: "T2",
TotalTypeDesc: "Net Total",
TotalAmt: Dinero(job.job_totals.totals.subtotal).toFormat("0.00")
},
{
TotalType: "TOT",
TotalSubType: "SM",
TotalTypeDesc: "Supplement Total",
TotalAmt: job.cieca_ttl ? job.cieca_ttl.data.supp_amt : Dinero().toFormat("0.00")
},
{
TotalType: "TOT",
TotalSubType: "F7",
TotalTypeDesc: "Sales Tax",
TotalAmt: Dinero(job.job_totals.totals.state_tax).toFormat("0.00")
},
{
TotalType: "TOT",
TotalSubType: "GST",
TotalTypeDesc: "GST Tax",
TotalAmt: Dinero(job.job_totals.totals.federal_tax).toFormat("0.00")
},
{
TotalType: "TOT",
TotalSubType: "D8",
TotalTypeDesc: "Bottom Line Discount",
TotalAmt: Dinero(job.job_totals.additional.adjustments).toFormat("0.00")
},
{
TotalType: "TOT",
TotalSubType: "D2",
TotalTypeDesc: "Deductible",
TotalAmt: Dinero({
amount: Math.round((job.ded_amt || 0) * 100)
}).toFormat("0.00")
},
{
TotalType: "TOT",
TotalSubType: "BTR",
TotalTypeDesc: "Betterment",
TotalAmt: Dinero(job.job_totals.totals.custPayable.dep_taxes).toFormat("0.00")
},
{
TotalType: "TOT",
TotalSubType: "AA",
TotalTypeDesc: "Appearance Allowance",
TotalAmt: Dinero().toFormat("0.00")
},
{
TotalType: "TOT",
TotalSubType: "DEPOSIT",
TotalTypeDesc: "Deposit",
TotalAmt: Dinero().toFormat("0.00")
},
{
TotalType: "TOT",
TotalSubType: "INS",
TotalTypeDesc: "Insurance Pay",
TotalAmt: Dinero(job.job_totals.totals.total_repairs)
.subtract(Dinero(job.job_totals.totals.custPayable.total))
.toFormat("0.00")
},
{
TotalType: "TOT",
TotalSubType: "CUST",
TotalTypeDesc: "Customer Pay",
TotalAmt: Dinero(job.job_totals.totals.custPayable.total).toFormat("0.00")
}
]
// RepairTotalsType: 1,
},
// RepairLabor: {
// LaborAllocations: {
// LaborAllocation: [
// {
// LaborAllocationUUID:
// "426cce3a-efa7-44d9-b76e-50b9102c4198",
// LaborType: "LAB",
// Technician: {
// Employee: {
// PersonInfo: {
// PersonName: {
// FirstName: "Jose",
// LastName: "Gonzalez",
// },
// IDInfo: {
// IDQualifierCode: "US",
// IDNum: 2987,
// },
// },
// },
// },
// AllocatedHours: 3.5,
// },
// {
// LaborAllocationUUID:
// "426cce3a-efa7-44d9-b76e-50b9102c4199",
// LaborType: "LAR",
// Technician: {
// Employee: {
// PersonInfo: {
// PersonName: {
// FirstName: "Rcardo",
// LastName: "Himenez",
// },
// IDInfo: {
// IDQualifierCode: "US",
// IDNum: 2989,
// },
// },
// },
// },
// AllocatedHours: 5.5,
// },
// ],
// },
// },
ProductionStatus: {
ProductionStage: {
ProductionStageCode: GetProductionStageCode(job, bodyshop),
ProductionStageDateTime: moment().tz(bodyshop.timezone).format(momentFormat)
// ProductionStageStatusComment:
// "Going to be painted this afternoon",
},
RepairStatus: {
RepairStatusCode: GetRepairStatusCode(job),
RepairStatusDateTime: moment().tz(bodyshop.timezone).format(momentFormat)
// RepairStatusMemo: "Waiting on back ordered parts",
}
}
// RepairOrderNotes: {
// RepairOrderNote: {
// LineSequenceNum: 1,
// Note: "Revision Requested : approved--but needs est separated.8/22/2008 11:58:53 AM",
// CreateDateTime: "2008-08-22T11:58:53",
// AuthoredBy: {
// FirstName: {
// "#text": "Elizabeth/FirstName>",
// LastName: "Unis",
// },
// },
// RepairOrderNote: {
// LineSequenceNum: 2,
// Note: "Approved : 8/26/2008 12:21:08 PM",
// CreateDateTime: "2008-08-26T12:21:08",
// AuthoredBy: {
// FirstName: {
// "#text": "Elizabeth/FirstName>",
// LastName: "Unis",
// },
// },
// },
// },
// },
};
deleteNullKeys(obj);
try {
const entegralSoapClient = await soap.createClientAsync(entegralEndpoint, {
ignoredNamespaces: true,
wsdl_options: {
// useEmptyTag: true,
},
wsdl_headers: {
Authorization: `Basic ${new Buffer.from(
`${process.env.ENTEGRAL_USER}:${process.env.ENTEGRAL_PASSWORD}`
).toString("base64")}`
}
});
entegralSoapClient.setSecurity(
new soap.BasicAuthSecurity(process.env.ENTEGRAL_USER, process.env.ENTEGRAL_PASSWORD)
);
const entegralResponse = await entegralSoapClient.RepairOrderFolderAddRqAsync(
obj,
function (err, result, rawResponse, soapHeader, rawRequest) {
fs.writeFileSync(`./logs/arms-request.xml`, rawRequest);
fs.writeFileSync(`./logs/arms-response.xml`, rawResponse);
logger.log("arms-job-xml-request", "DEBUG", "api", job.id, {
xml: rawRequest
});
logger.log("arms-job-xml-response", "DEBUG", "api", job.id, {
xml: rawResponse
});
if (err) {
sendServerEmail({
subject: `ARMS Update Failed: ${bodyshop.shopname} - ${job.ro_number}`,
text: `Error: ${JSON.stringify(error)}`
});
}
res.status(200).json(err || result);
}
);
const [result, rawResponse, , rawRequest] = entegralResponse;
} catch (error) {
logger.log("arms-failed-job-upload", "ERROR", "api", job.shopid, {
job: JSON.stringify({ id: job.id, ro_number: job.ro_number }),
error: error.message || JSON.stringify(error)
});
//console.log(error);
}
} catch (error) {
logger.log("arms-failed-job", "ERROR", "api", job.shopid, {
job: JSON.stringify({ id: job.id, ro_number: job.ro_number }),
error: error.message || JSON.stringify(error)
});
}
res.sendStatus(200);
return;
const allErrors = [];
try {
for (const bodyshop of bodyshops) {
logger.log("arms-start-shop-extract", "DEBUG", "api", bodyshop.id, {
shopname: bodyshop.shopname
});
const erroredJobs = [];
try {
const { jobs } = await client.request(queries.ENTEGRAL_EXPORT, {
bodyshopid: bodyshop.id
});
const jobsToPush = [];
if (erroredJobs.length > 0) {
logger.log("arms-failed-jobs", "ERROR", "api", bodyshop.id, {
count: erroredJobs.length,
jobs: JSON.stringify(erroredJobs.map((j) => j.job.ro_number))
});
allErrors = [...allErrors, ...erroredJobs];
}
logger.log("arms-end-shop-extract", "DEBUG", "api", bodyshop.id, {
shopname: bodyshop.shopname
});
try {
const entegralSoapClient = await soap.createClientAsync(entegralEndpoint, {
ignoredNamespaces: true,
wsdl_options: {
// useEmptyTag: true,
},
wsdl_headers: {
Authorization: `Basic ${new Buffer.from(
`${process.env.ENTEGRAL_USER}:${process.env.ENTEGRAL_PASSWORD}`
).toString("base64")}`
}
});
entegralSoapClient.setSecurity(
new soap.BasicAuthSecurity(process.env.ENTEGRAL_USER, process.env.ENTEGRAL_PASSWORD)
);
const entegralResponse = await entegralSoapClient.RepairOrderFolderAddRqAsync(
[jobsToPush[0]],
function (err, result, rawResponse, soapHeader, rawRequest) {
fs.writeFileSync(`./logs/arms-request.xml`, rawRequest);
fs.writeFileSync(`./logs/arms-response.xml`, rawResponse);
res.json(err || result);
}
);
const [result, rawResponse, , rawRequest] = entegralResponse;
} catch (error) {
fs.writeFileSync(`./logs/${xmlObj.filename}`, xmlObj.xml);
//console.log(error);
logger.log("arms-error-shop", "ERROR", "api", bodyshop.id, {
error
});
}
} catch (error) {
//Error at the shop level.
logger.log("arms-error-shop", "ERROR", "api", bodyshop.id, {
error
});
allErrors.push({
bodyshopid: bodyshop.id,
imexshopid: bodyshop.imexshopid,
fatal: true,
errors: [error.toString()]
});
} finally {
allErrors.push({
bodyshopid: bodyshop.id,
imexshopid: bodyshop.imexshopid,
errors: erroredJobs
});
}
}
res.sendStatus(200);
} catch (error) {
res.status(200).json(error);
}
};
function GetSupplementNumber(joblines) {
if (!joblines) return 0;
const max = _.max(joblines.map((jl) => parseInt((jl.line_ind || "0").replace(/[^\d.-]/g, ""))));
return max || 0;
}
function GetDocumentstatus(job, bodyshop) {
switch (job.status) {
case bodyshop.md_ro_statuses.default_void:
return "V";
case bodyshop.md_ro_statuses.default_invoiced:
case bodyshop.md_ro_statuses.default_exported:
return "Z";
default:
return "O";
}
}
function GetRepairStatusCode(job) {
return "25";
}
function GetProductionStageCode(job, bodyshop) {
const result = (bodyshop.features?.entegral).find((k) => k.status === job.status);
return result?.code || "33";
}
function isEmpty(obj) {
for (var key in obj) return false;
return true;
}
function deleteNullKeys(app) {
for (var key in app) {
if (app[key] !== null && typeof app[key] === "object") {
deleteNullKeys(app[key]);
if (isEmpty(app[key])) {
delete app[key];
}
}
if (app[key] === null) {
delete app[key];
}
}
}