Files
imexrps/electron/decoder/decoder.js
2021-05-20 15:12:50 -07:00

505 lines
14 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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",
"GLASS_FLAG",
// "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",
]),
function (result, val, key) {
//Required because unq_seq gets pulled as a numeric instaed of a string.
if (key === "UNQ_SEQ") {
result[key.toLowerCase()] = val.toString();
return;
}
result[key.toLowerCase()] = val;
return;
}
);
})
// .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;
// }
jobline.ignore = false;
//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-46 Ignore NA Line Items.
//Removed on 05/20. We are seeing more $0DB lines than NA lines and they are getting incorrectly ignored.
// if (
// jobline.part_type === "PAN" &&
// jobline.price_j === true &&
// jobline.db_price === 0
// ) {
// 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;
}
//05/20
//Well have to apply a rule that will not count any A/M, Reman or new part price that is manually changed to $0.00. Only recycled parts that are changed to $0.00 can be counted towards RPS.
// This is separate from $0.00 DB prices that need to be updated to the manually entered price.
if (
jobline.part_type !== "PAL" &&
jobline.act_price === 0 &&
jobline.price_j
) {
log.info(
`Jobline '${jobline.line_desc}' ignored because it was manually changed to 0..`
);
jobline.ignore = true;
}
// 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;
// }
//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;
}
//Check to see if this is a discount line i.e. a 900511
if (jobline.db_ref === "900511" && jobline.prt_dsmk_p !== 50) {
jobline.ignore = false;
console.log("dsmk_d", jobline.prt_dsmk_p);
//Calculate the discount to be added as a negative.
jobline.act_price = jobline.prt_dsmk_m;
}
//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.prt_dsmk_p;
delete jobline.glass_flag;
delete jobline.price_j;
return jobline;
});
return { joblines: { data: joblines } };
}
exports.DecodeEstimate = DecodeEstimate;
exports.ImportJob = ImportJob;