1032 lines
31 KiB
JavaScript
1032 lines
31 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 uuid = require("uuid").v4;
|
|
|
|
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) => {
|
|
//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 = uuid(); // 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 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 + job.job_totals.rates.la1.hours,
|
|
TotalAmt: Dinero(job.job_totals.rates.lab.total)
|
|
.add(Dinero(job.job_totals.rates.la1.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);
|
|
|
|
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);
|
|
}
|
|
} 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];
|
|
}
|
|
}
|
|
}
|