477 lines
13 KiB
JavaScript
477 lines
13 KiB
JavaScript
const { DBFFile } = require("dbffile");
|
||
const path = require("path");
|
||
const _ = require("lodash");
|
||
const log = require("electron-log");
|
||
const { store } = require("../electron-store");
|
||
const { BrowserWindow } = require("electron");
|
||
const ipcTypes = require("../../src/ipc.types");
|
||
const {
|
||
NewNotification,
|
||
} = require("../notification-wrapper/notification-wrapper");
|
||
const Nucleus = require("nucleus-nodejs");
|
||
|
||
async function ImportJob(path) {
|
||
const b = BrowserWindow.getAllWindows()[0];
|
||
b.webContents.send(ipcTypes.default.estimate.toRenderer.estimateDecodeStart);
|
||
const newJob = await DecodeEstimate(path);
|
||
|
||
if (newJob && !newJob.ERROR) {
|
||
b.webContents.send(
|
||
ipcTypes.default.estimate.toRenderer.estimateDecodeSuccess,
|
||
newJob
|
||
);
|
||
log.info(`Sent job for upload. ${newJob.clm_no}`);
|
||
NewNotification({
|
||
title: "Job Uploaded",
|
||
body: "A new job has been uploaded.",
|
||
});
|
||
} else {
|
||
log.info(`Ignored job. ${newJob.ERROR}`);
|
||
Nucleus.track("IGNORE_JOB", { reason: newJob.ERROR });
|
||
NewNotification({
|
||
title: "Job Ignored",
|
||
body: newJob.ERROR,
|
||
});
|
||
}
|
||
}
|
||
|
||
async function DecodeEstimate(filePath, includeFilePathInReturnJob = false) {
|
||
const parsedFilePath = path.parse(filePath);
|
||
let extensionlessFilePath = path.join(
|
||
parsedFilePath.dir,
|
||
parsedFilePath.name
|
||
);
|
||
const job = {
|
||
...(await DecodeAd1File(extensionlessFilePath)),
|
||
...(await DecodeVehFile(extensionlessFilePath)),
|
||
...(await DecodeTtlFile(extensionlessFilePath)),
|
||
...(await DecodeLinFile(extensionlessFilePath)),
|
||
...(includeFilePathInReturnJob ? { filePath } : {}),
|
||
};
|
||
|
||
const ad2 = await DecodeAd2File(extensionlessFilePath);
|
||
|
||
if (job.OWNR_FN === "" || !job.OWNR_FN) job.OWNR_FN = ad2.CLMT_FN;
|
||
if (job.OWNR_LN === "" || !job.OWNR_LN) job.OWNR_LN = ad2.CLMT_LN;
|
||
|
||
const accepted_ins_co = store.get("accepted_ins_co");
|
||
|
||
let returnValue;
|
||
//Removed as a part of RPS-40.
|
||
// if (job.V_MILEAGE <= 20000) {
|
||
// returnValue = { ERROR: "Vehicle mileage is less than 20,000kms." };
|
||
// } else
|
||
|
||
if (!accepted_ins_co.includes(job.INS_CO_NM)) {
|
||
returnValue = {
|
||
ERROR: `Insurance Company Name is not valid for RPS. (${
|
||
job.INS_CO_NM || "No name set"
|
||
})`,
|
||
};
|
||
} else if (!job.CLM_NO) {
|
||
log.info("Job ignored. No claim #. " + job.clm_no);
|
||
returnValue = {
|
||
ERROR: `An unique claim number must be set for all jobs sent to RPS.`,
|
||
};
|
||
} else {
|
||
returnValue = _.transform(job, function (result, val, key) {
|
||
result[key.toLowerCase()] = val;
|
||
});
|
||
}
|
||
|
||
return returnValue;
|
||
}
|
||
|
||
async function DecodeAd1File(extensionlessFilePath) {
|
||
let dbf;
|
||
try {
|
||
dbf = await DBFFile.open(`${extensionlessFilePath}A.AD1`);
|
||
} catch (error) {
|
||
log.error("Error opening AD1 File.", error);
|
||
dbf = await DBFFile.open(`${extensionlessFilePath}.AD1`);
|
||
dbf && log.log("Found AD1 file using regular CIECA Id.");
|
||
} finally {
|
||
if (!dbf) return {};
|
||
let records = await dbf.readRecords(1);
|
||
return _.pick(records[0], [
|
||
// "INS_CO_ID",
|
||
"INS_CO_NM",
|
||
// "INS_ADDR1",
|
||
// "INS_ADDR2",
|
||
// "INS_CITY",
|
||
// "INS_ST",
|
||
// "INS_ZIP",
|
||
// "INS_CTRY",
|
||
|
||
// "INS_EA",
|
||
// "POLICY_NO",
|
||
// "DED_AMT",
|
||
// "DED_STATUS",
|
||
// "ASGN_NO",
|
||
//"ASGN_DATE",
|
||
// "ASGN_TYPE",
|
||
"CLM_NO",
|
||
// "CLM_OFC_ID",
|
||
// "CLM_OFC_NM",
|
||
// "CLM_ADDR1",
|
||
// "CLM_ADDR2",
|
||
// "CLM_CITY",
|
||
// "CLM_ST",
|
||
// "CLM_ZIP",
|
||
// "CLM_CTRY",
|
||
// "CLM_PH1",
|
||
// "CLM_PH1X",
|
||
// "CLM_PH2",
|
||
// "CLM_PH2X",
|
||
// "CLM_FAX",
|
||
// "CLM_FAXX",
|
||
// "CLM_CT_LN",
|
||
// "CLM_CT_FN",
|
||
// "CLM_TITLE",
|
||
// "CLM_CT_PH",
|
||
// "CLM_CT_PHX",
|
||
// "CLM_EA",
|
||
// "PAYEE_NMS",
|
||
// "PAY_TYPE",
|
||
// "PAY_DATE",
|
||
// "PAY_CHKNM",
|
||
// "PAY_AMT",
|
||
// "AGT_CO_ID",
|
||
// "AGT_CO_NM",
|
||
// "AGT_ADDR1",
|
||
// "AGT_ADDR2",
|
||
// "AGT_CITY",
|
||
// "AGT_ST",
|
||
// "AGT_ZIP",
|
||
// "AGT_CTRY",
|
||
// "AGT_PH1",
|
||
// "AGT_PH1X",
|
||
// "AGT_PH2",
|
||
// "AGT_PH2X",
|
||
// "AGT_FAX",
|
||
// "AGT_FAXX",
|
||
// "AGT_CT_LN",
|
||
// "AGT_CT_FN",
|
||
// "AGT_CT_PH",
|
||
// "AGT_CT_PHX",
|
||
// "AGT_EA",
|
||
// "AGT_LIC_NO",
|
||
"LOSS_DATE",
|
||
// "LOSS_TYPE",
|
||
// "LOSS_DESC",
|
||
// "THEFT_IND",
|
||
// "CAT_NO",
|
||
// "TLOS_IND",
|
||
// "CUST_PR",
|
||
// "INSD_LN",
|
||
// "INSD_FN",
|
||
// "INSD_TITLE",
|
||
// "INSD_CO_NM",
|
||
// "INSD_ADDR1",
|
||
// "INSD_ADDR2",
|
||
// "INSD_CITY",
|
||
// "INSD_ST",
|
||
// "INSD_ZIP",
|
||
// "INSD_CTRY",
|
||
// "INSD_PH1",
|
||
// "INSD_PH1X",
|
||
// "INSD_PH2",
|
||
// "INSD_PH2X",
|
||
// "INSD_FAX",
|
||
// "INSD_FAXX",
|
||
// "INSD_EA",
|
||
"OWNR_LN",
|
||
"OWNR_FN",
|
||
// "OWNR_TITLE",
|
||
// "OWNR_CO_NM",
|
||
// "OWNR_ADDR1",
|
||
// "OWNR_ADDR2",
|
||
// "OWNR_CITY",
|
||
// "OWNR_ST",
|
||
// "OWNR_ZIP",
|
||
// "OWNR_CTRY",
|
||
// "OWNR_PH1",
|
||
// "OWNR_PH1X",
|
||
// "OWNR_PH2",
|
||
// "OWNR_PH2X",
|
||
// "OWNR_FAX",
|
||
// "OWNR_FAXX",
|
||
// "OWNR_EA",
|
||
// "INS_PH1",
|
||
// "INS_PH1X",
|
||
// "INS_PH2",
|
||
// "INS_PH2X",
|
||
// "INS_FAX",
|
||
// "INS_FAXX",
|
||
// "INS_CT_LN",
|
||
// "INS_CT_FN",
|
||
// "INS_TITLE",
|
||
// "INS_CT_PH",
|
||
// "INS_CT_PHX",
|
||
// "LOSS_CAT",
|
||
]);
|
||
}
|
||
}
|
||
|
||
async function DecodeAd2File(extensionlessFilePath) {
|
||
let dbf;
|
||
try {
|
||
dbf = await DBFFile.open(`${extensionlessFilePath}B.AD2`);
|
||
} catch (error) {
|
||
log.error("Error opening AD2 File.", error);
|
||
dbf = await DBFFile.open(`${extensionlessFilePath}.AD2`);
|
||
dbf && log.log("Found AD2 file using regular CIECA Id.");
|
||
} finally {
|
||
if (!dbf) return {};
|
||
let records = await dbf.readRecords(1);
|
||
return _.pick(records[0], ["CLMT_LN", "CLMT_FN"]);
|
||
}
|
||
}
|
||
|
||
async function DecodeVehFile(extensionlessFilePath) {
|
||
let dbf;
|
||
try {
|
||
dbf = await DBFFile.open(`${extensionlessFilePath}V.VEH`);
|
||
} catch (error) {
|
||
log.error("Error opening VEH File.", error);
|
||
dbf = await DBFFile.open(`${extensionlessFilePath}.VEH`);
|
||
dbf && log.log("Found VEH file using regular CIECA Id.");
|
||
} finally {
|
||
if (!dbf) return {};
|
||
let records = await dbf.readRecords(1);
|
||
return _.pick(records[0], [
|
||
// "IMPACT_1",
|
||
// "IMPACT_2",
|
||
// "DB_V_CODE",
|
||
// "PLATE_NO",
|
||
// "PLATE_ST",
|
||
"V_VIN",
|
||
// "V_COND",
|
||
// "V_PROD_DT",
|
||
"V_MODEL_YR",
|
||
// "V_MAKECODE",
|
||
"V_MAKEDESC",
|
||
"V_MODEL",
|
||
"V_TYPE",
|
||
"V_MILEAGE",
|
||
// "V_BSTYLE",
|
||
// "V_TRIMCODE",
|
||
// "TRIM_COLOR",
|
||
// "V_MLDGCODE",
|
||
// "V_ENGINE",
|
||
// "V_COLOR",
|
||
// "V_TONE",
|
||
// "V_STAGE",
|
||
// "PAINT_CD1",
|
||
// "PAINT_CD2",
|
||
// "PAINT_CD3",
|
||
]);
|
||
}
|
||
}
|
||
async function DecodeTtlFile(extensionlessFilePath) {
|
||
let dbf = await DBFFile.open(`${extensionlessFilePath}.TTL`);
|
||
let records = await dbf.readRecords(1);
|
||
return { clm_total: records[0]["G_TTL_AMT"] };
|
||
}
|
||
|
||
async function DecodeLinFile(extensionlessFilePath) {
|
||
let dbf = await DBFFile.open(`${extensionlessFilePath}.LIN`);
|
||
let records = await dbf.readRecords();
|
||
let joblines = records
|
||
.map((record) => {
|
||
return _.transform(
|
||
_.pick(record, [
|
||
"LINE_NO",
|
||
"LINE_IND",
|
||
// "LINE_REF",
|
||
// "TRAN_CODE",
|
||
"DB_REF",
|
||
"UNQ_SEQ",
|
||
// "WHO_PAYS",
|
||
"LINE_DESC",
|
||
"PART_TYPE",
|
||
// "PART_DESCJ",
|
||
"PRT_DSMK_M",
|
||
"OEM_PARTNO",
|
||
// "PRICE_INC",
|
||
// "ALT_PART_I",
|
||
// "TAX_PART",
|
||
"DB_PRICE",
|
||
"ACT_PRICE",
|
||
"PART_QTY",
|
||
"PRICE_J",
|
||
// "CERT_PART",
|
||
// "ALT_CO_ID",
|
||
// "ALT_PARTNO",
|
||
// "ALT_OVERRD",
|
||
// "ALT_PARTM",
|
||
// "PRT_DSMK_P",
|
||
|
||
// "MOD_LBR_TY",
|
||
// "DB_HRS",
|
||
// "MOD_LB_HRS",
|
||
// "LBR_INC",
|
||
// "LBR_OP",
|
||
// "LBR_HRS_J",
|
||
// "LBR_TYP_J",
|
||
// "LBR_OP_J",
|
||
// "PAINT_STG",
|
||
// "PAINT_TONE",
|
||
// "LBR_TAX",
|
||
// "LBR_AMT",
|
||
// "MISC_AMT",
|
||
// "MISC_SUBLT",
|
||
// "MISC_TAX",
|
||
// "BETT_TYPE",
|
||
// "BETT_PCTG",
|
||
// "BETT_AMT",
|
||
// "BETT_TAX",
|
||
"GLASS_FLAG",
|
||
]),
|
||
function (result, val, key) {
|
||
//Required because unq_seq gets pulled as a numeric instaed of a string.
|
||
|
||
if (key === "UNQ_SEQ") {
|
||
return (result[key.toLowerCase()] = val.toString());
|
||
}
|
||
return (result[key.toLowerCase()] = val);
|
||
}
|
||
);
|
||
})
|
||
// .filter(
|
||
// (jobline) =>
|
||
// jobline.part_type &&
|
||
// !jobline.db_ref.startsWith("900") &&
|
||
// !jobline.line_desc.toLowerCase().startsWith("urethane") &&
|
||
// !jobline.line_desc.toLowerCase().startsWith("wheel") &&
|
||
// !jobline.line_desc.toLowerCase().startsWith("hazardous") &&
|
||
// !jobline.line_desc.toLowerCase().startsWith("detail") &&
|
||
// !jobline.line_desc.toLowerCase().startsWith("clean") &&
|
||
// jobline.part_type.toUpperCase() !== "PAG" &&
|
||
// jobline.part_type.toUpperCase() !== "PAS" &&
|
||
// jobline.part_type.toUpperCase() !== "PASL" &&
|
||
// jobline.part_type.toUpperCase() !== "PAE" &&
|
||
// jobline.glass_flag === false
|
||
// )
|
||
.map((jobline) => {
|
||
//Removed as a result of conversation with Norm.
|
||
// Appears you are calculating based on ‘N.A.’ part values in the Mitchell database.
|
||
// Reminder – if Mitchell DB has $0.00 price updates can be tracked, if it has N.A. it cannot calculate RPS value.
|
||
|
||
// if (
|
||
// (jobline.db_price === null || jobline.db_price === 0) &&
|
||
// !!jobline.act_price &&
|
||
// jobline.act_price > 0
|
||
// ) {
|
||
// // log.info(
|
||
// // "DB Price null/lower than act price",
|
||
// // jobline.line_desc,
|
||
// // jobline.db_price,
|
||
// // jobline.act_price
|
||
// // );
|
||
// jobline.db_price = jobline.act_price;
|
||
// }
|
||
|
||
//*****TODO LINE****
|
||
//Any PAL, Remanufactured, Recycled, Aftermarket, Used,
|
||
// If DB Price 0, ignore them.
|
||
// if (
|
||
// (jobline.part_type === "PAL" ||
|
||
// jobline.part_type === "PAM" ||
|
||
// jobline.part_type === "PAR" ||
|
||
// jobline.part_type === "PAA") &&
|
||
// jobline.db_price === 0
|
||
// ) {
|
||
// jobline.ignore = true;
|
||
// }
|
||
|
||
//Wheel Repair Pricing PRS-82
|
||
if (
|
||
jobline.part_type === "PAN" &&
|
||
jobline.line_desc.toLowerCase().includes("wheel") &&
|
||
Math.abs(jobline.prt_dsmk_m) ===
|
||
Math.round((jobline.act_price / 2) * 100) / 100
|
||
) {
|
||
log.info(`Jobline '${jobline.line_desc}' ignored due to wheel repair.`);
|
||
jobline.ignore = true;
|
||
}
|
||
|
||
//RPS-39 - OEM ON OEM SAVINGS
|
||
if (
|
||
jobline.part_type === "PAN" &&
|
||
jobline.db_price !== jobline.act_price
|
||
) {
|
||
jobline.db_price = jobline.act_price;
|
||
}
|
||
|
||
//$0DB price for aftermarket and recycled parts. RPS-83
|
||
//Norm to double check.
|
||
if (
|
||
(jobline.part_type === "PAA" || jobline.part_type === "PAL") &&
|
||
jobline.db_price === 0 &&
|
||
jobline.db_price !== jobline.act_price
|
||
) {
|
||
jobline.db_price = jobline.act_price;
|
||
}
|
||
|
||
// if (
|
||
// !!jobline.db_price &&
|
||
// jobline.db_price > 0 &&
|
||
// !!jobline.act_price &&
|
||
// jobline.act_price > jobline.db_price
|
||
// ) {
|
||
// //Actual price should never be higher than the DB Price.
|
||
// jobline.db_price = jobline.act_price;
|
||
// }
|
||
|
||
//RPS-46 Ignore NA Line Items.
|
||
if (
|
||
jobline.part_type === "PAN" &&
|
||
jobline.price_j === true &&
|
||
jobline.db_price === 0
|
||
) {
|
||
jobline.ignore = true;
|
||
}
|
||
|
||
//Update as per Norm from Rod & Waldo - Only Recycled Glass should count towards RPS, not A/M or OEM
|
||
if (jobline.glass_flag && jobline.part_type !== "PAL") {
|
||
jobline.ignore = true;
|
||
}
|
||
|
||
//Logic Based Exclusions.
|
||
if (
|
||
!jobline.part_type ||
|
||
jobline.db_ref.startsWith("900") ||
|
||
jobline.line_desc.toLowerCase().startsWith("urethane") ||
|
||
//jobline.line_desc.toLowerCase().includes("wheel") || Removed as a part of RPS-41
|
||
jobline.line_desc.toLowerCase().includes("tire") ||
|
||
jobline.line_desc.toLowerCase().startsWith("hazardous") ||
|
||
jobline.line_desc.toLowerCase().startsWith("detail") ||
|
||
jobline.line_desc.toLowerCase().startsWith("clean") ||
|
||
// jobline.part_type.toUpperCase() === "PAG" ||Removed for RPS-43.
|
||
jobline.part_type.toUpperCase() === "PAS" ||
|
||
jobline.part_type.toUpperCase() === "PASL" ||
|
||
jobline.part_type.toUpperCase() === "PAE"
|
||
//jobline.glass_flag === true //Removed for RPS-43.
|
||
)
|
||
jobline.ignore = true;
|
||
|
||
//RPS-42 Dynamic Inclusion or Exclusion of Lines based on RPS-EXCLUDE or RPS.
|
||
if (jobline.oem_partno.toLowerCase().includes("/rps-exclude")) {
|
||
jobline.ignore = true;
|
||
} else if (jobline.oem_partno.toLowerCase().includes("/rps")) {
|
||
jobline.ignore = false;
|
||
}
|
||
|
||
delete jobline.prt_dsmk_m; //Delete price markup for wheel repair
|
||
delete jobline.glass_flag;
|
||
delete jobline.price_j;
|
||
return jobline;
|
||
});
|
||
|
||
return { joblines: { data: joblines } };
|
||
}
|
||
|
||
exports.DecodeEstimate = DecodeEstimate;
|
||
exports.ImportJob = ImportJob;
|