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,
|
DecodedPfmLine,
|
||||||
JobMaterialRateFields,
|
JobMaterialRateFields,
|
||||||
} from "./decode-pfm.interface";
|
} from "./decode-pfm.interface";
|
||||||
|
import YNBoolConverter from "../../util/ynBoolConverter";
|
||||||
|
|
||||||
const DecodePfm = async (
|
const DecodePfm = async (
|
||||||
extensionlessFilePath: string
|
extensionlessFilePath: string
|
||||||
@@ -45,40 +46,42 @@ const DecodePfm = async (
|
|||||||
};
|
};
|
||||||
|
|
||||||
const rawPfmData: DecodedPfmLine[] = rawDBFRecord.map((record) => {
|
const rawPfmData: DecodedPfmLine[] = rawDBFRecord.map((record) => {
|
||||||
const singleLineData: DecodedPfmLine = deepLowerCaseKeys(
|
const singleLineData: DecodedPfmLine = YNBoolConverter(
|
||||||
_.pick(record, [
|
deepLowerCaseKeys(
|
||||||
//TODO: Add typings for EMS File Formats.
|
_.pick(record, [
|
||||||
"MATL_TYPE",
|
//TODO: Add typings for EMS File Formats.
|
||||||
"CAL_CODE",
|
"MATL_TYPE",
|
||||||
"CAL_DESC",
|
"CAL_CODE",
|
||||||
"CAL_MAXDLR",
|
"CAL_DESC",
|
||||||
"CAL_PRIP",
|
"CAL_MAXDLR",
|
||||||
|
"CAL_PRIP",
|
||||||
|
|
||||||
"CAL_SECP",
|
"CAL_SECP",
|
||||||
"MAT_CALP",
|
"MAT_CALP",
|
||||||
"CAL_PRETHR", //Mitchell here
|
"CAL_PRETHR", //Mitchell here
|
||||||
"CAL_PSTTHR",
|
"CAL_PSTTHR",
|
||||||
"CAL_THRAMT",
|
"CAL_THRAMT",
|
||||||
|
|
||||||
"CAL_LBRMIN",
|
"CAL_LBRMIN",
|
||||||
|
|
||||||
"CAL_LBRRTE", //Audatex puts it here
|
"CAL_LBRRTE", //Audatex puts it here
|
||||||
"CAL_OPCODE",
|
"CAL_OPCODE",
|
||||||
|
|
||||||
"TAX_IND",
|
"TAX_IND",
|
||||||
"MAT_TAXP",
|
"MAT_TAXP",
|
||||||
"MAT_ADJP",
|
"MAT_ADJP",
|
||||||
"MAT_TX_TY1",
|
"MAT_TX_TY1",
|
||||||
"MAT_TX_IN1",
|
"MAT_TX_IN1",
|
||||||
"MAT_TX_TY2",
|
"MAT_TX_TY2",
|
||||||
"MAT_TX_IN2",
|
"MAT_TX_IN2",
|
||||||
"MAT_TX_TY3",
|
"MAT_TX_TY3",
|
||||||
"MAT_TX_IN3",
|
"MAT_TX_IN3",
|
||||||
"MAT_TX_TY4",
|
"MAT_TX_TY4",
|
||||||
"MAT_TX_IN4",
|
"MAT_TX_IN4",
|
||||||
"MAT_TX_TY5",
|
"MAT_TX_TY5",
|
||||||
"MAT_TX_IN5",
|
"MAT_TX_IN5",
|
||||||
])
|
])
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
//Also capture the whole object.
|
//Also capture the whole object.
|
||||||
@@ -89,15 +92,44 @@ const DecodePfm = async (
|
|||||||
});
|
});
|
||||||
|
|
||||||
//Apply line by line adjustments.
|
//Apply line by line adjustments.
|
||||||
const materialsLine: DecodedPfmLine | undefined = rawPfmData.find(
|
const mapaLine: DecodedPfmLine | undefined = rawPfmData.find(
|
||||||
(line) => line.matl_type === "MAPA"
|
(line) => line.matl_type === "MAPA"
|
||||||
);
|
);
|
||||||
|
if (mapaLine) {
|
||||||
if (materialsLine) {
|
|
||||||
jobMaterialRates.rate_mapa =
|
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.
|
//Apply business logic transfomrations.
|
||||||
//We don't have an inspection date, we instead have `date_estimated`
|
//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 deepLowerCaseKeys from "../../util/deepLowercaseKeys";
|
||||||
import errorTypeCheck from "../../util/errorTypeCheck";
|
import errorTypeCheck from "../../util/errorTypeCheck";
|
||||||
import { DecodedPfo } from "./decode-pfo.interface";
|
import { DecodedPfo } from "./decode-pfo.interface";
|
||||||
|
import YNBoolConverter from "../../util/ynBoolConverter";
|
||||||
|
|
||||||
const DecodePfo = async (
|
const DecodePfo = async (
|
||||||
extensionlessFilePath: string
|
extensionlessFilePath: string
|
||||||
@@ -27,36 +28,38 @@ const DecodePfo = async (
|
|||||||
//PFO will always have only 1 row.
|
//PFO will always have only 1 row.
|
||||||
//Commented lines have been cross referenced with existing partner fields.
|
//Commented lines have been cross referenced with existing partner fields.
|
||||||
|
|
||||||
const rawPfoData: DecodedPfo = deepLowerCaseKeys(
|
const rawPfoData: DecodedPfo = YNBoolConverter(
|
||||||
_.pick(rawDBFRecord[0], [
|
deepLowerCaseKeys(
|
||||||
//TODO: Add typings for EMS File Formats.
|
_.pick(rawDBFRecord[0], [
|
||||||
"TX_TOW_TY",
|
//TODO: Add typings for EMS File Formats.
|
||||||
"TOW_T_TY1",
|
"TX_TOW_TY",
|
||||||
"TOW_T_IN1",
|
"TOW_T_TY1",
|
||||||
"TOW_T_TY2",
|
"TOW_T_IN1",
|
||||||
"TOW_T_IN2",
|
"TOW_T_TY2",
|
||||||
"TOW_T_TY3",
|
"TOW_T_IN2",
|
||||||
"TOW_T_IN3",
|
"TOW_T_TY3",
|
||||||
"TOW_T_TY4",
|
"TOW_T_IN3",
|
||||||
"TOW_T_IN4",
|
"TOW_T_TY4",
|
||||||
"TOW_T_TY5",
|
"TOW_T_IN4",
|
||||||
"TOW_T_IN5",
|
"TOW_T_TY5",
|
||||||
"TOW_T_TY6",
|
"TOW_T_IN5",
|
||||||
"TOW_T_IN6",
|
"TOW_T_TY6",
|
||||||
"TX_STOR_TY",
|
"TOW_T_IN6",
|
||||||
"STOR_T_TY1",
|
"TX_STOR_TY",
|
||||||
"STOR_T_IN1",
|
"STOR_T_TY1",
|
||||||
"STOR_T_TY2",
|
"STOR_T_IN1",
|
||||||
"STOR_T_IN2",
|
"STOR_T_TY2",
|
||||||
"STOR_T_TY3",
|
"STOR_T_IN2",
|
||||||
"STOR_T_IN3",
|
"STOR_T_TY3",
|
||||||
"STOR_T_TY4",
|
"STOR_T_IN3",
|
||||||
"STOR_T_IN4",
|
"STOR_T_TY4",
|
||||||
"STOR_T_TY5",
|
"STOR_T_IN4",
|
||||||
"STOR_T_IN5",
|
"STOR_T_TY5",
|
||||||
"STOR_T_TY6",
|
"STOR_T_IN5",
|
||||||
"STOR_T_IN6",
|
"STOR_T_TY6",
|
||||||
])
|
"STOR_T_IN6",
|
||||||
|
])
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
//Apply business logic transfomrations.
|
//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 { DecodedPfm } from "./decode-pfm.interface";
|
||||||
import DecodePfo from "./decode-pfo";
|
import DecodePfo from "./decode-pfo";
|
||||||
import { DecodedPfo } from "./decode-pfo.interface";
|
import { DecodedPfo } from "./decode-pfo.interface";
|
||||||
|
import DecodePfp from "./decode-pfp";
|
||||||
|
import { DecodedPfp } from "./decode-pfp.interface";
|
||||||
import DecodePft from "./decode-pft";
|
import DecodePft from "./decode-pft";
|
||||||
import { DecodedPft } from "./decode-pft.interface";
|
import { DecodedPft } from "./decode-pft.interface";
|
||||||
import DecodeStl from "./decode-stl";
|
import DecodeStl from "./decode-stl";
|
||||||
@@ -23,6 +25,8 @@ import DecodeTtl from "./decode-ttl";
|
|||||||
import { DecodedTtl } from "./decode-ttl.interface";
|
import { DecodedTtl } from "./decode-ttl.interface";
|
||||||
import DecodeVeh from "./decode-veh";
|
import DecodeVeh from "./decode-veh";
|
||||||
import { DecodedVeh } from "./decode-veh.interface";
|
import { DecodedVeh } from "./decode-veh.interface";
|
||||||
|
import { DecodedEnv } from "./decode-env.interface";
|
||||||
|
import DecodeEnv from "./decode-env";
|
||||||
|
|
||||||
async function ImportJob(filepath: string): Promise<void> {
|
async function ImportJob(filepath: string): Promise<void> {
|
||||||
const parsedFilePath = path.parse(filepath);
|
const parsedFilePath = path.parse(filepath);
|
||||||
@@ -35,6 +39,7 @@ async function ImportJob(filepath: string): Promise<void> {
|
|||||||
try {
|
try {
|
||||||
//The below all end up returning parts of the job object.
|
//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.
|
//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 ad1: DecodedAd1 = await DecodeAD1(extensionlessFilePath);
|
||||||
const ad2: DecodedAD2 = await DecodeAD2(extensionlessFilePath);
|
const ad2: DecodedAD2 = await DecodeAD2(extensionlessFilePath);
|
||||||
const veh: DecodedVeh = await DecodeVeh(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 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 stl: DecodedStl[] = await DecodeStl(extensionlessFilePath); // TODO: This will be the `cieca_stl` object
|
||||||
const ttl: DecodedTtl = await DecodeTtl(extensionlessFilePath);
|
const ttl: DecodedTtl = await DecodeTtl(extensionlessFilePath);
|
||||||
|
const pfp: DecodedPfp = await DecodePfp(extensionlessFilePath);
|
||||||
|
|
||||||
log.debug("EMS Object", {
|
log.debug("Job Object", {
|
||||||
ad1,
|
...env,
|
||||||
ad2,
|
...ad1,
|
||||||
veh,
|
...ad2,
|
||||||
lin,
|
...veh,
|
||||||
pfh,
|
joblines: { data: lin },
|
||||||
pfl,
|
...pfh,
|
||||||
pft,
|
cieca_pfl: pfl,
|
||||||
pfm,
|
cieca_pft: pft,
|
||||||
pfo,
|
materials: pfm,
|
||||||
stl,
|
cieca_pfo: pfo,
|
||||||
ttl,
|
...stl,
|
||||||
|
...ttl,
|
||||||
|
parts_tax_rates: pfp,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
log.error("Error encountered while decoding job. ", errorTypeCheck(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