Add remaining EMS file parsing.
This commit is contained in:
4
src/main/decoder/decode-env.interface.ts
Normal file
4
src/main/decoder/decode-env.interface.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export interface DecodedEnv {
|
||||
est_system: string;
|
||||
estfile_id: string;
|
||||
}
|
||||
42
src/main/decoder/decode-env.ts
Normal file
42
src/main/decoder/decode-env.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import { DBFFile } from "dbffile";
|
||||
import log from "electron-log/main";
|
||||
import _ from "lodash";
|
||||
import deepLowerCaseKeys from "../../util/deepLowercaseKeys";
|
||||
import errorTypeCheck from "../../util/errorTypeCheck";
|
||||
import { DecodedEnv } from "./decode-env.interface";
|
||||
|
||||
const DecodeEnv = async (
|
||||
extensionlessFilePath: string
|
||||
): Promise<DecodedEnv> => {
|
||||
let dbf: DBFFile | null = null;
|
||||
try {
|
||||
dbf = await DBFFile.open(`${extensionlessFilePath}.ENV`);
|
||||
} catch (error) {
|
||||
log.error("Error opening ENV File.", errorTypeCheck(error));
|
||||
}
|
||||
|
||||
if (!dbf) {
|
||||
log.error(`Could not find any ENV files at ${extensionlessFilePath}`);
|
||||
throw new Error(`Could not find any ENV files at ${extensionlessFilePath}`);
|
||||
}
|
||||
|
||||
const rawDBFRecord = await dbf.readRecords(1);
|
||||
|
||||
//AD2 will always have only 1 row.
|
||||
|
||||
//TODO: Determine if there's any value to capture the whole ENV file.
|
||||
|
||||
const rawEnvData: DecodedEnv = deepLowerCaseKeys(
|
||||
_.pick(rawDBFRecord[0], [
|
||||
//TODO: Add typings for EMS File Formats.
|
||||
//TODO: Several of these fields will fail. Should extend schema to capture them.
|
||||
"EST_SYSTEM",
|
||||
"ESTFILE_ID",
|
||||
])
|
||||
);
|
||||
|
||||
//Apply business logic transfomrations.
|
||||
|
||||
return rawEnvData;
|
||||
};
|
||||
export default DecodeEnv;
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
DecodedPfmLine,
|
||||
JobMaterialRateFields,
|
||||
} from "./decode-pfm.interface";
|
||||
import YNBoolConverter from "../../util/ynBoolConverter";
|
||||
|
||||
const DecodePfm = async (
|
||||
extensionlessFilePath: string
|
||||
@@ -45,40 +46,42 @@ const DecodePfm = async (
|
||||
};
|
||||
|
||||
const rawPfmData: DecodedPfmLine[] = rawDBFRecord.map((record) => {
|
||||
const singleLineData: DecodedPfmLine = deepLowerCaseKeys(
|
||||
_.pick(record, [
|
||||
//TODO: Add typings for EMS File Formats.
|
||||
"MATL_TYPE",
|
||||
"CAL_CODE",
|
||||
"CAL_DESC",
|
||||
"CAL_MAXDLR",
|
||||
"CAL_PRIP",
|
||||
const singleLineData: DecodedPfmLine = YNBoolConverter(
|
||||
deepLowerCaseKeys(
|
||||
_.pick(record, [
|
||||
//TODO: Add typings for EMS File Formats.
|
||||
"MATL_TYPE",
|
||||
"CAL_CODE",
|
||||
"CAL_DESC",
|
||||
"CAL_MAXDLR",
|
||||
"CAL_PRIP",
|
||||
|
||||
"CAL_SECP",
|
||||
"MAT_CALP",
|
||||
"CAL_PRETHR", //Mitchell here
|
||||
"CAL_PSTTHR",
|
||||
"CAL_THRAMT",
|
||||
"CAL_SECP",
|
||||
"MAT_CALP",
|
||||
"CAL_PRETHR", //Mitchell here
|
||||
"CAL_PSTTHR",
|
||||
"CAL_THRAMT",
|
||||
|
||||
"CAL_LBRMIN",
|
||||
"CAL_LBRMIN",
|
||||
|
||||
"CAL_LBRRTE", //Audatex puts it here
|
||||
"CAL_OPCODE",
|
||||
"CAL_LBRRTE", //Audatex puts it here
|
||||
"CAL_OPCODE",
|
||||
|
||||
"TAX_IND",
|
||||
"MAT_TAXP",
|
||||
"MAT_ADJP",
|
||||
"MAT_TX_TY1",
|
||||
"MAT_TX_IN1",
|
||||
"MAT_TX_TY2",
|
||||
"MAT_TX_IN2",
|
||||
"MAT_TX_TY3",
|
||||
"MAT_TX_IN3",
|
||||
"MAT_TX_TY4",
|
||||
"MAT_TX_IN4",
|
||||
"MAT_TX_TY5",
|
||||
"MAT_TX_IN5",
|
||||
])
|
||||
"TAX_IND",
|
||||
"MAT_TAXP",
|
||||
"MAT_ADJP",
|
||||
"MAT_TX_TY1",
|
||||
"MAT_TX_IN1",
|
||||
"MAT_TX_TY2",
|
||||
"MAT_TX_IN2",
|
||||
"MAT_TX_TY3",
|
||||
"MAT_TX_IN3",
|
||||
"MAT_TX_TY4",
|
||||
"MAT_TX_IN4",
|
||||
"MAT_TX_TY5",
|
||||
"MAT_TX_IN5",
|
||||
])
|
||||
)
|
||||
);
|
||||
|
||||
//Also capture the whole object.
|
||||
@@ -89,15 +92,44 @@ const DecodePfm = async (
|
||||
});
|
||||
|
||||
//Apply line by line adjustments.
|
||||
const materialsLine: DecodedPfmLine | undefined = rawPfmData.find(
|
||||
const mapaLine: DecodedPfmLine | undefined = rawPfmData.find(
|
||||
(line) => line.matl_type === "MAPA"
|
||||
);
|
||||
|
||||
if (materialsLine) {
|
||||
if (mapaLine) {
|
||||
jobMaterialRates.rate_mapa =
|
||||
materialsLine.cal_lbrrte || materialsLine.cal_prethr || 0;
|
||||
mapaLine.cal_lbrrte || mapaLine.cal_prethr || 0;
|
||||
jobMaterialRates.tax_paint_mat_rt = mapaLine.mat_taxp ?? 0 / 100;
|
||||
}
|
||||
|
||||
const mashLine: DecodedPfmLine | undefined = rawPfmData.find(
|
||||
(line) => line.matl_type === "MASH"
|
||||
);
|
||||
if (mashLine) {
|
||||
jobMaterialRates.rate_mash =
|
||||
mashLine.cal_lbrrte || mashLine.cal_prethr || 0;
|
||||
jobMaterialRates.tax_shop_mat_rt = mashLine.mat_taxp ?? 0 / 100;
|
||||
}
|
||||
|
||||
const mahwLine: DecodedPfmLine | undefined = rawPfmData.find(
|
||||
(line) => line.matl_type === "MAHW"
|
||||
);
|
||||
if (mahwLine) {
|
||||
jobMaterialRates.rate_mahw =
|
||||
mahwLine.cal_lbrrte || mahwLine.cal_prethr || 0;
|
||||
jobMaterialRates.tax_levies_rt = mahwLine.mat_taxp ?? 0 / 100;
|
||||
}
|
||||
|
||||
const additionalMaterials = ["MA2S", "MA2T", "MA3S", "MACS", "MABL"];
|
||||
additionalMaterials.forEach((type) => {
|
||||
const line: DecodedPfmLine | undefined = rawPfmData.find(
|
||||
(line) => line.matl_type === type
|
||||
);
|
||||
if (line) {
|
||||
jobMaterialRates[`rate_${type.toLowerCase()}`] =
|
||||
line.cal_lbrrte || line.cal_prethr || 0;
|
||||
}
|
||||
});
|
||||
|
||||
//Apply business logic transfomrations.
|
||||
//We don't have an inspection date, we instead have `date_estimated`
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import _ from "lodash";
|
||||
import deepLowerCaseKeys from "../../util/deepLowercaseKeys";
|
||||
import errorTypeCheck from "../../util/errorTypeCheck";
|
||||
import { DecodedPfo } from "./decode-pfo.interface";
|
||||
import YNBoolConverter from "../../util/ynBoolConverter";
|
||||
|
||||
const DecodePfo = async (
|
||||
extensionlessFilePath: string
|
||||
@@ -27,36 +28,38 @@ const DecodePfo = async (
|
||||
//PFO will always have only 1 row.
|
||||
//Commented lines have been cross referenced with existing partner fields.
|
||||
|
||||
const rawPfoData: DecodedPfo = deepLowerCaseKeys(
|
||||
_.pick(rawDBFRecord[0], [
|
||||
//TODO: Add typings for EMS File Formats.
|
||||
"TX_TOW_TY",
|
||||
"TOW_T_TY1",
|
||||
"TOW_T_IN1",
|
||||
"TOW_T_TY2",
|
||||
"TOW_T_IN2",
|
||||
"TOW_T_TY3",
|
||||
"TOW_T_IN3",
|
||||
"TOW_T_TY4",
|
||||
"TOW_T_IN4",
|
||||
"TOW_T_TY5",
|
||||
"TOW_T_IN5",
|
||||
"TOW_T_TY6",
|
||||
"TOW_T_IN6",
|
||||
"TX_STOR_TY",
|
||||
"STOR_T_TY1",
|
||||
"STOR_T_IN1",
|
||||
"STOR_T_TY2",
|
||||
"STOR_T_IN2",
|
||||
"STOR_T_TY3",
|
||||
"STOR_T_IN3",
|
||||
"STOR_T_TY4",
|
||||
"STOR_T_IN4",
|
||||
"STOR_T_TY5",
|
||||
"STOR_T_IN5",
|
||||
"STOR_T_TY6",
|
||||
"STOR_T_IN6",
|
||||
])
|
||||
const rawPfoData: DecodedPfo = YNBoolConverter(
|
||||
deepLowerCaseKeys(
|
||||
_.pick(rawDBFRecord[0], [
|
||||
//TODO: Add typings for EMS File Formats.
|
||||
"TX_TOW_TY",
|
||||
"TOW_T_TY1",
|
||||
"TOW_T_IN1",
|
||||
"TOW_T_TY2",
|
||||
"TOW_T_IN2",
|
||||
"TOW_T_TY3",
|
||||
"TOW_T_IN3",
|
||||
"TOW_T_TY4",
|
||||
"TOW_T_IN4",
|
||||
"TOW_T_TY5",
|
||||
"TOW_T_IN5",
|
||||
"TOW_T_TY6",
|
||||
"TOW_T_IN6",
|
||||
"TX_STOR_TY",
|
||||
"STOR_T_TY1",
|
||||
"STOR_T_IN1",
|
||||
"STOR_T_TY2",
|
||||
"STOR_T_IN2",
|
||||
"STOR_T_TY3",
|
||||
"STOR_T_IN3",
|
||||
"STOR_T_TY4",
|
||||
"STOR_T_IN4",
|
||||
"STOR_T_TY5",
|
||||
"STOR_T_IN5",
|
||||
"STOR_T_TY6",
|
||||
"STOR_T_IN6",
|
||||
])
|
||||
)
|
||||
);
|
||||
|
||||
//Apply business logic transfomrations.
|
||||
|
||||
33
src/main/decoder/decode-pfp.interface.ts
Normal file
33
src/main/decoder/decode-pfp.interface.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
export interface DecodedPfpLine {
|
||||
prt_type: string;
|
||||
prt_tax_in: boolean;
|
||||
prt_tax_rt: number;
|
||||
prt_mkupp: number;
|
||||
prt_mktyp: string;
|
||||
prt_discp: number;
|
||||
prt_tx_ty1: string;
|
||||
prt_tx_in1: boolean;
|
||||
prt_tx_ty2: string;
|
||||
prt_tx_in2: boolean;
|
||||
prt_tx_ty3: string;
|
||||
prt_tx_in3: boolean;
|
||||
prt_tx_ty4: string;
|
||||
prt_tx_in4: boolean;
|
||||
prt_tx_ty5: string;
|
||||
prt_tx_in5: boolean;
|
||||
}
|
||||
|
||||
export interface DecodedPfp {
|
||||
PAA: DecodedPfpLine;
|
||||
PAC: DecodedPfpLine;
|
||||
PAL: DecodedPfpLine;
|
||||
PAG: DecodedPfpLine;
|
||||
PAM: DecodedPfpLine;
|
||||
PAP: DecodedPfpLine;
|
||||
PAN: DecodedPfpLine;
|
||||
PAO: DecodedPfpLine;
|
||||
PAR: DecodedPfpLine;
|
||||
PAS: DecodedPfpLine;
|
||||
PASL: DecodedPfpLine;
|
||||
PAT: DecodedPfpLine;
|
||||
}
|
||||
71
src/main/decoder/decode-pfp.ts
Normal file
71
src/main/decoder/decode-pfp.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
import { DBFFile } from "dbffile";
|
||||
import log from "electron-log/main";
|
||||
import _ from "lodash";
|
||||
import deepLowerCaseKeys from "../../util/deepLowercaseKeys";
|
||||
import errorTypeCheck from "../../util/errorTypeCheck";
|
||||
import YNBoolConverter from "../../util/ynBoolConverter";
|
||||
import { DecodedPfp, DecodedPfpLine } from "./decode-pfp.interface";
|
||||
|
||||
const DecodePfp = async (
|
||||
extensionlessFilePath: string
|
||||
): Promise<DecodedPfp> => {
|
||||
let dbf: DBFFile | null = null;
|
||||
try {
|
||||
dbf = await DBFFile.open(`${extensionlessFilePath}.PFP`);
|
||||
} catch (error) {
|
||||
//PFP File only has 1 location.
|
||||
log.error("Error opening PFP File.", errorTypeCheck(error));
|
||||
}
|
||||
|
||||
if (!dbf) {
|
||||
log.error(`Could not find any PFP files at ${extensionlessFilePath}`);
|
||||
throw new Error(`Could not find any PFP files at ${extensionlessFilePath}`);
|
||||
}
|
||||
|
||||
const rawDBFRecord = await dbf.readRecords();
|
||||
|
||||
//AD2 will always have only 1 row.
|
||||
//Commented lines have been cross referenced with existing partner fields.
|
||||
|
||||
const rawPfpData: DecodedPfpLine[] = rawDBFRecord.map((record) => {
|
||||
const singleLineData: DecodedPfpLine = deepLowerCaseKeys(
|
||||
_.pick(record, [
|
||||
//TODO: Add typings for EMS File Formats.
|
||||
"PRT_TYPE",
|
||||
"PRT_TAX_IN",
|
||||
"PRT_TAX_RT",
|
||||
"PRT_MKUPP",
|
||||
"PRT_MKTYP",
|
||||
"PRT_DISCP",
|
||||
"PRT_TX_TY1",
|
||||
"PRT_TX_IN1",
|
||||
"PRT_TX_TY2",
|
||||
"PRT_TX_IN2",
|
||||
"PRT_TX_TY3",
|
||||
"PRT_TX_IN3",
|
||||
"PRT_TX_TY4",
|
||||
"PRT_TX_IN4",
|
||||
"PRT_TX_TY5",
|
||||
"PRT_TX_IN5",
|
||||
])
|
||||
);
|
||||
|
||||
singleLineData.prt_tax_rt = singleLineData.prt_tax_rt / 100;
|
||||
return YNBoolConverter(singleLineData);
|
||||
});
|
||||
|
||||
//Apply business logic transfomrations.
|
||||
|
||||
//Convert array of lines to a hash object.
|
||||
const parsedPfpFile: DecodedPfp = rawPfpData.reduce(
|
||||
(acc: DecodedPfp, line: DecodedPfpLine) => {
|
||||
acc[line.prt_type] = line;
|
||||
return acc;
|
||||
},
|
||||
{} as DecodedPfp
|
||||
);
|
||||
|
||||
return parsedPfpFile;
|
||||
};
|
||||
|
||||
export default DecodePfp;
|
||||
@@ -15,6 +15,8 @@ import DecodePfm from "./decode-pfm";
|
||||
import { DecodedPfm } from "./decode-pfm.interface";
|
||||
import DecodePfo from "./decode-pfo";
|
||||
import { DecodedPfo } from "./decode-pfo.interface";
|
||||
import DecodePfp from "./decode-pfp";
|
||||
import { DecodedPfp } from "./decode-pfp.interface";
|
||||
import DecodePft from "./decode-pft";
|
||||
import { DecodedPft } from "./decode-pft.interface";
|
||||
import DecodeStl from "./decode-stl";
|
||||
@@ -23,6 +25,8 @@ import DecodeTtl from "./decode-ttl";
|
||||
import { DecodedTtl } from "./decode-ttl.interface";
|
||||
import DecodeVeh from "./decode-veh";
|
||||
import { DecodedVeh } from "./decode-veh.interface";
|
||||
import { DecodedEnv } from "./decode-env.interface";
|
||||
import DecodeEnv from "./decode-env";
|
||||
|
||||
async function ImportJob(filepath: string): Promise<void> {
|
||||
const parsedFilePath = path.parse(filepath);
|
||||
@@ -35,6 +39,7 @@ async function ImportJob(filepath: string): Promise<void> {
|
||||
try {
|
||||
//The below all end up returning parts of the job object.
|
||||
//Some of them return additional info - e.g. owner or vehicle record data at both the job and corresponding table level.
|
||||
const env: DecodedEnv = await DecodeEnv(extensionlessFilePath);
|
||||
const ad1: DecodedAd1 = await DecodeAD1(extensionlessFilePath);
|
||||
const ad2: DecodedAD2 = await DecodeAD2(extensionlessFilePath);
|
||||
const veh: DecodedVeh = await DecodeVeh(extensionlessFilePath);
|
||||
@@ -46,19 +51,22 @@ async function ImportJob(filepath: string): Promise<void> {
|
||||
const pfo: DecodedPfo = await DecodePfo(extensionlessFilePath); // TODO: This will be the `cieca_pfo` object
|
||||
const stl: DecodedStl[] = await DecodeStl(extensionlessFilePath); // TODO: This will be the `cieca_stl` object
|
||||
const ttl: DecodedTtl = await DecodeTtl(extensionlessFilePath);
|
||||
const pfp: DecodedPfp = await DecodePfp(extensionlessFilePath);
|
||||
|
||||
log.debug("EMS Object", {
|
||||
ad1,
|
||||
ad2,
|
||||
veh,
|
||||
lin,
|
||||
pfh,
|
||||
pfl,
|
||||
pft,
|
||||
pfm,
|
||||
pfo,
|
||||
stl,
|
||||
ttl,
|
||||
log.debug("Job Object", {
|
||||
...env,
|
||||
...ad1,
|
||||
...ad2,
|
||||
...veh,
|
||||
joblines: { data: lin },
|
||||
...pfh,
|
||||
cieca_pfl: pfl,
|
||||
cieca_pft: pft,
|
||||
materials: pfm,
|
||||
cieca_pfo: pfo,
|
||||
...stl,
|
||||
...ttl,
|
||||
parts_tax_rates: pfp,
|
||||
});
|
||||
} catch (error) {
|
||||
log.error("Error encountered while decoding job. ", errorTypeCheck(error));
|
||||
|
||||
12
src/util/ynBoolConverter.ts
Normal file
12
src/util/ynBoolConverter.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
const YNBoolConverter = <T extends object>(original: T): T => {
|
||||
Object.keys(original).forEach((key) => {
|
||||
if (original[key] === "Y") {
|
||||
original[key] = true;
|
||||
} else if (original[key] === "N") {
|
||||
original[key] = false;
|
||||
}
|
||||
});
|
||||
return original;
|
||||
};
|
||||
|
||||
export default YNBoolConverter;
|
||||
Reference in New Issue
Block a user