From 3277af73f6868c91b660edb8fe2360887f972b72 Mon Sep 17 00:00:00 2001 From: Patrick Fic Date: Wed, 19 Mar 2025 13:26:00 -0700 Subject: [PATCH] Add several EMS file formats. --- src/main/decoder/decode-ad1.ts | 2 +- src/main/decoder/decode-ad2.ts | 2 +- src/main/decoder/decode-lin.ts | 2 +- src/main/decoder/decode-pfh.ts | 2 +- src/main/decoder/decode-pfl.ts | 2 +- src/main/decoder/decode-pfm.interface.ts | 46 +++++++ src/main/decoder/decode-pfm.ts | 107 +++++++++++++++ src/main/decoder/decode-pfo.interface.ts | 28 ++++ src/main/decoder/decode-pfo.ts | 66 +++++++++ src/main/decoder/decode-pft.interface.ts | 143 ++++++++++++++++++++ src/main/decoder/decode-pft.ts | 165 +++++++++++++++++++++++ src/main/decoder/decode-stl.interface.ts | 20 +++ src/main/decoder/decode-stl.ts | 62 +++++++++ src/main/decoder/decode-ttl.interface.ts | 22 +++ src/main/decoder/decode-ttl.ts | 52 +++++++ src/main/decoder/decode-veh.ts | 2 +- src/main/decoder/decoder.ts | 29 +++- 17 files changed, 745 insertions(+), 7 deletions(-) create mode 100644 src/main/decoder/decode-pfm.interface.ts create mode 100644 src/main/decoder/decode-pfm.ts create mode 100644 src/main/decoder/decode-pfo.interface.ts create mode 100644 src/main/decoder/decode-pfo.ts create mode 100644 src/main/decoder/decode-pft.interface.ts create mode 100644 src/main/decoder/decode-pft.ts create mode 100644 src/main/decoder/decode-stl.interface.ts create mode 100644 src/main/decoder/decode-stl.ts create mode 100644 src/main/decoder/decode-ttl.interface.ts create mode 100644 src/main/decoder/decode-ttl.ts diff --git a/src/main/decoder/decode-ad1.ts b/src/main/decoder/decode-ad1.ts index 5aa8f1e..64a8dde 100644 --- a/src/main/decoder/decode-ad1.ts +++ b/src/main/decoder/decode-ad1.ts @@ -8,7 +8,7 @@ import { DecodedAd1, OwnerRecordInterface } from "./decode-ad1.interface"; const DecodeAD1 = async ( extensionlessFilePath: string ): Promise => { - let dbf: DBFFile; + let dbf: DBFFile | null = null; try { dbf = await DBFFile.open(`${extensionlessFilePath}A.AD1`); } catch (error) { diff --git a/src/main/decoder/decode-ad2.ts b/src/main/decoder/decode-ad2.ts index 5ebbe2e..e21426f 100644 --- a/src/main/decoder/decode-ad2.ts +++ b/src/main/decoder/decode-ad2.ts @@ -8,7 +8,7 @@ import errorTypeCheck from "../../util/errorTypeCheck"; const DecodeAD2 = async ( extensionlessFilePath: string ): Promise => { - let dbf: DBFFile; + let dbf: DBFFile | null = null; try { dbf = await DBFFile.open(`${extensionlessFilePath}B.AD2`); } catch (error) { diff --git a/src/main/decoder/decode-lin.ts b/src/main/decoder/decode-lin.ts index 7cfcdd5..339ea47 100644 --- a/src/main/decoder/decode-lin.ts +++ b/src/main/decoder/decode-lin.ts @@ -8,7 +8,7 @@ import errorTypeCheck from "../../util/errorTypeCheck"; const DecodeLin = async ( extensionlessFilePath: string ): Promise => { - let dbf: DBFFile; + let dbf: DBFFile | null = null; try { dbf = await DBFFile.open(`${extensionlessFilePath}.LIN`); } catch (error) { diff --git a/src/main/decoder/decode-pfh.ts b/src/main/decoder/decode-pfh.ts index 303905b..7861b0d 100644 --- a/src/main/decoder/decode-pfh.ts +++ b/src/main/decoder/decode-pfh.ts @@ -8,7 +8,7 @@ import { DecodedPfh } from "./decode-pfh.interface"; const DecodePfh = async ( extensionlessFilePath: string ): Promise => { - let dbf: DBFFile; + let dbf: DBFFile | null = null; try { dbf = await DBFFile.open(`${extensionlessFilePath}.PFH`); } catch (error) { diff --git a/src/main/decoder/decode-pfl.ts b/src/main/decoder/decode-pfl.ts index f29ce41..dc35180 100644 --- a/src/main/decoder/decode-pfl.ts +++ b/src/main/decoder/decode-pfl.ts @@ -12,7 +12,7 @@ import { const DecodePfl = async ( extensionlessFilePath: string ): Promise => { - let dbf: DBFFile; + let dbf: DBFFile | null = null; try { dbf = await DBFFile.open(`${extensionlessFilePath}.PFL`); } catch (error) { diff --git a/src/main/decoder/decode-pfm.interface.ts b/src/main/decoder/decode-pfm.interface.ts new file mode 100644 index 0000000..283538b --- /dev/null +++ b/src/main/decoder/decode-pfm.interface.ts @@ -0,0 +1,46 @@ +export interface DecodedPfmLine { + matl_type?: string; + cal_code?: number; + cal_desc?: string; + cal_maxdlr?: number; + cal_prip?: number; + cal_secp?: number; + mat_calp?: number; + cal_prethr?: number; + cal_pstthr?: number; + cal_thramt?: number; + cal_lbrmin?: number; + cal_lbrrte?: number; + cal_opcode?: string; + tax_ind?: boolean; + mat_taxp?: number; + mat_adjp?: number; + mat_tx_ty1?: string; + mat_tx_in1?: boolean; + mat_tx_ty2?: string; + mat_tx_in2?: boolean; + mat_tx_ty3?: string; + mat_tx_in3?: boolean; + mat_tx_ty4?: string; + mat_tx_in4?: boolean; + mat_tx_ty5?: string; + mat_tx_in5?: boolean; +} + +export interface JobMaterialRateFields { + rate_mapa: number; + tax_paint_mat_rt: number; + rate_mash: number; + tax_shop_mat_rt: number; + rate_mahw: number; + tax_levies_rt: number; + rate_ma2s: number; + rate_ma2t: number; + rate_ma3s: number; + rate_macs: number; + rate_mabl: number; +} + +export interface DecodedPfm extends JobMaterialRateFields { + cieca_pfm: DecodedPfmLine[]; +} diff --git a/src/main/decoder/decode-pfm.ts b/src/main/decoder/decode-pfm.ts new file mode 100644 index 0000000..197cd13 --- /dev/null +++ b/src/main/decoder/decode-pfm.ts @@ -0,0 +1,107 @@ +import { DBFFile } from "dbffile"; +import log from "electron-log/main"; +import _ from "lodash"; +import deepLowerCaseKeys from "../../util/deepLowercaseKeys"; +import errorTypeCheck from "../../util/errorTypeCheck"; +import { + DecodedPfm, + DecodedPfmLine, + JobMaterialRateFields, +} from "./decode-pfm.interface"; + +const DecodePfm = async ( + extensionlessFilePath: string +): Promise => { + let dbf: DBFFile | null = null; + try { + dbf = await DBFFile.open(`${extensionlessFilePath}.PFM`); + } catch (error) { + //PFM File only has 1 location. + log.error("Error opening PFM File.", errorTypeCheck(error)); + } + + if (!dbf) { + log.error(`Could not find any PFM files at ${extensionlessFilePath}`); + throw new Error(`Could not find any PFM 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 jobMaterialRates: JobMaterialRateFields = { + rate_mapa: 0, + tax_paint_mat_rt: 0, + rate_mash: 0, + tax_shop_mat_rt: 0, + rate_mahw: 0, + tax_levies_rt: 0, + rate_ma2s: 0, + rate_ma2t: 0, + rate_ma3s: 0, + rate_macs: 0, + rate_mabl: 0, + }; + + 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", + + "CAL_SECP", + "MAT_CALP", + "CAL_PRETHR", //Mitchell here + "CAL_PSTTHR", + "CAL_THRAMT", + + "CAL_LBRMIN", + + "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", + ]) + ); + + //Also capture the whole object. + //This is segmented because the whole object was not previously captured for ImEX as it wasn't needed. + //Rome needs the whole object to accurately calculate the tax rates. + + return singleLineData; + }); + + //Apply line by line adjustments. + const materialsLine: DecodedPfmLine | undefined = rawPfmData.find( + (line) => line.matl_type === "MAPA" + ); + + if (materialsLine) { + jobMaterialRates.rate_mapa = + materialsLine.cal_lbrrte || materialsLine.cal_prethr || 0; + } + + //Apply business logic transfomrations. + //We don't have an inspection date, we instead have `date_estimated` + + return { ...jobMaterialRates, cieca_pfm: rawPfmData }; +}; + +export default DecodePfm; diff --git a/src/main/decoder/decode-pfo.interface.ts b/src/main/decoder/decode-pfo.interface.ts new file mode 100644 index 0000000..8c12862 --- /dev/null +++ b/src/main/decoder/decode-pfo.interface.ts @@ -0,0 +1,28 @@ +export interface DecodedPfo { + tx_tow_ty?: string; + tow_t_ty1?: string; + tow_t_in1?: boolean; + tow_t_ty2?: string; + tow_t_in2?: boolean; + tow_t_ty3?: string; + tow_t_in3?: boolean; + tow_t_ty4?: string; + tow_t_in4?: boolean; + tow_t_ty5?: string; + tow_t_in5?: boolean; + tow_t_ty6?: string; + tow_t_in6?: boolean; + tx_stor_ty?: string; + stor_t_ty1?: string; + stor_t_in1?: boolean; + stor_t_ty2?: string; + stor_t_in2?: boolean; + stor_t_ty3?: string; + stor_t_in3?: boolean; + stor_t_ty4?: string; + stor_t_in4?: boolean; + stor_t_ty5?: string; + stor_t_in5?: boolean; + stor_t_ty6?: string; + stor_t_in6?: boolean; +} diff --git a/src/main/decoder/decode-pfo.ts b/src/main/decoder/decode-pfo.ts new file mode 100644 index 0000000..56f57f6 --- /dev/null +++ b/src/main/decoder/decode-pfo.ts @@ -0,0 +1,66 @@ +import { DBFFile } from "dbffile"; +import log from "electron-log/main"; +import _ from "lodash"; +import deepLowerCaseKeys from "../../util/deepLowercaseKeys"; +import errorTypeCheck from "../../util/errorTypeCheck"; +import { DecodedPfo } from "./decode-pfo.interface"; + +const DecodePfo = async ( + extensionlessFilePath: string +): Promise => { + let dbf: DBFFile | null = null; + try { + dbf = await DBFFile.open(`${extensionlessFilePath}.PFO`); + } catch (error) { + log.error("Error opening PFO File.", errorTypeCheck(error)); + dbf = await DBFFile.open(`${extensionlessFilePath}.PFO`); + log.log("Trying to find PFO file using regular CIECA Id."); + } + + if (!dbf) { + log.error(`Could not find any PFO files at ${extensionlessFilePath}`); + throw new Error(`Could not find any PFO files at ${extensionlessFilePath}`); + } + + const rawDBFRecord = await dbf.readRecords(1); + + //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", + ]) + ); + + //Apply business logic transfomrations. + + return rawPfoData; +}; +export default DecodePfo; diff --git a/src/main/decoder/decode-pft.interface.ts b/src/main/decoder/decode-pft.interface.ts new file mode 100644 index 0000000..16b5fd7 --- /dev/null +++ b/src/main/decoder/decode-pft.interface.ts @@ -0,0 +1,143 @@ +/** + * Interface representing decoded data from a PFT file + * Contains tax type information with up to 6 tax types and 5 tiers each + */ +export interface DecodedPft { + // Tax Type 1 + tax_type1?: string; + ty1_tier1?: number; + ty1_thres1?: number; + ty1_rate1?: number; + ty1_sur1?: number; + ty1_tier2?: number; + ty1_thres2?: number; + ty1_rate2?: number; + ty1_sur2?: number; + ty1_tier3?: number; + ty1_thres3?: number; + ty1_rate3?: number; + ty1_sur3?: number; + ty1_tier4?: number; + ty1_thres4?: number; + ty1_rate4?: number; + ty1_sur4?: number; + ty1_tier5?: number; + ty1_thres5?: number; + ty1_rate5?: number; + ty1_sur5?: number; + + // Tax Type 2 + tax_type2?: string; + ty2_tier1?: number; + ty2_thres1?: number; + ty2_rate1?: number; + ty2_sur1?: number; + ty2_tier2?: number; + ty2_thres2?: number; + ty2_rate2?: number; + ty2_sur2?: number; + ty2_tier3?: number; + ty2_thres3?: number; + ty2_rate3?: number; + ty2_sur3?: number; + ty2_tier4?: number; + ty2_thres4?: number; + ty2_rate4?: number; + ty2_sur4?: number; + ty2_tier5?: number; + ty2_thres5?: number; + ty2_rate5?: number; + ty2_sur5?: number; + + // Tax Type 3 + tax_type3?: string; + ty3_tier1?: number; + ty3_thres1?: number; + ty3_rate1?: number; + ty3_sur1?: number; + ty3_tier2?: number; + ty3_thres2?: number; + ty3_rate2?: number; + ty3_sur2?: number; + ty3_tier3?: number; + ty3_thres3?: number; + ty3_rate3?: number; + ty3_sur3?: number; + ty3_tier4?: number; + ty3_thres4?: number; + ty3_rate4?: number; + ty3_sur4?: number; + ty3_tier5?: number; + ty3_thres5?: number; + ty3_rate5?: number; + ty3_sur5?: number; + + // Tax Type 4 + tax_type4?: string; + ty4_tier1?: number; + ty4_thres1?: number; + ty4_rate1?: number; + ty4_sur1?: number; + ty4_tier2?: number; + ty4_thres2?: number; + ty4_rate2?: number; + ty4_sur2?: number; + ty4_tier3?: number; + ty4_thres3?: number; + ty4_rate3?: number; + ty4_sur3?: number; + ty4_tier4?: number; + ty4_thres4?: number; + ty4_rate4?: number; + ty4_sur4?: number; + ty4_tier5?: number; + ty4_thres5?: number; + ty4_rate5?: number; + ty4_sur5?: number; + + // Tax Type 5 + tax_type5?: string; + ty5_tier1?: number; + ty5_thres1?: number; + ty5_rate1?: number; + ty5_sur1?: number; + ty5_tier2?: number; + ty5_thres2?: number; + ty5_rate2?: number; + ty5_sur2?: number; + ty5_tier3?: number; + ty5_thres3?: number; + ty5_rate3?: number; + ty5_sur3?: number; + ty5_tier4?: number; + ty5_thres4?: number; + ty5_rate4?: number; + ty5_sur4?: number; + ty5_tier5?: number; + ty5_thres5?: number; + ty5_rate5?: number; + ty5_sur5?: number; + + // Tax Type 6 + tax_type6?: string; + ty6_tier1?: number; + ty6_thres1?: number; + ty6_rate1?: number; + ty6_sur1?: number; + ty6_tier2?: number; + ty6_thres2?: number; + ty6_rate2?: number; + ty6_sur2?: number; + ty6_tier3?: number; + ty6_thres3?: number; + ty6_rate3?: number; + ty6_sur3?: number; + ty6_tier4?: number; + ty6_thres4?: number; + ty6_rate4?: number; + ty6_sur4?: number; + ty6_tier5?: number; + ty6_thres5?: number; + ty6_rate5?: number; + ty6_sur5?: number; +} diff --git a/src/main/decoder/decode-pft.ts b/src/main/decoder/decode-pft.ts new file mode 100644 index 0000000..ac5c0dc --- /dev/null +++ b/src/main/decoder/decode-pft.ts @@ -0,0 +1,165 @@ +import { DBFFile } from "dbffile"; +import log from "electron-log/main"; +import _ from "lodash"; +import deepLowerCaseKeys from "../../util/deepLowercaseKeys"; +import errorTypeCheck from "../../util/errorTypeCheck"; +import { DecodedPft } from "./decode-pft.interface"; + +const DecodePft = async ( + extensionlessFilePath: string +): Promise => { + let dbf: DBFFile | null = null; + try { + dbf = await DBFFile.open(`${extensionlessFilePath}.PFT`); + } catch (error) { + log.error("Error opening PFH File.", errorTypeCheck(error)); + } + + if (!dbf) { + log.error(`Could not find any PFT files at ${extensionlessFilePath}`); + throw new Error(`Could not find any PFT files at ${extensionlessFilePath}`); + } + + const rawDBFRecord = await dbf.readRecords(1); + + //PFT will always have only 1 row. + //Commented lines have been cross referenced with existing partner fields. + + const rawPftData: DecodedPft = deepLowerCaseKeys( + _.pick(rawDBFRecord[0], [ + //TODO: Add typings for EMS File Formats. + "TAX_TYPE1", //The below is is taken from a CCC estimate. Will require validation to ensure it is also accurate for Audatex/Mitchell + "TY1_TIER1", + "TY1_THRES1", + "TY1_RATE1", + "TY1_SUR1", + "TY1_TIER2", + "TY1_THRES2", + "TY1_RATE2", + "TY1_SUR2", + "TY1_TIER3", + "TY1_THRES3", + "TY1_RATE3", + "TY1_SUR3", + "TY1_TIER4", + "TY1_THRES4", + "TY1_RATE4", + "TY1_SUR4", + "TY1_TIER5", + "TY1_THRES5", + "TY1_RATE5", + "TY1_SUR5", + "TAX_TYPE2", + "TY2_TIER1", + "TY2_THRES1", + "TY2_RATE1", + "TY2_SUR1", + "TY2_TIER2", + "TY2_THRES2", + "TY2_RATE2", + "TY2_SUR2", + "TY2_TIER3", + "TY2_THRES3", + "TY2_RATE3", + "TY2_SUR3", + "TY2_TIER4", + "TY2_THRES4", + "TY2_RATE4", + "TY2_SUR4", + "TY2_TIER5", + "TY2_THRES5", + "TY2_RATE5", + "TY2_SUR5", + "TAX_TYPE3", + "TY3_TIER1", + "TY3_THRES1", + "TY3_RATE1", + "TY3_SUR1", + "TY3_TIER2", + "TY3_THRES2", + "TY3_RATE2", + "TY3_SUR2", + "TY3_TIER3", + "TY3_THRES3", + "TY3_RATE3", + "TY3_SUR3", + "TY3_TIER4", + "TY3_THRES4", + "TY3_RATE4", + "TY3_SUR4", + "TY3_TIER5", + "TY3_THRES5", + "TY3_RATE5", + "TY3_SUR5", + "TAX_TYPE4", + "TY4_TIER1", + "TY4_THRES1", + "TY4_RATE1", + "TY4_SUR1", + "TY4_TIER2", + "TY4_THRES2", + "TY4_RATE2", + "TY4_SUR2", + "TY4_TIER3", + "TY4_THRES3", + "TY4_RATE3", + "TY4_SUR3", + "TY4_TIER4", + "TY4_THRES4", + "TY4_RATE4", + "TY4_SUR4", + "TY4_TIER5", + "TY4_THRES5", + "TY4_RATE5", + "TY4_SUR5", + "TAX_TYPE5", + "TY5_TIER1", + "TY5_THRES1", + "TY5_RATE1", + "TY5_SUR1", + "TY5_TIER2", + "TY5_THRES2", + "TY5_RATE2", + "TY5_SUR2", + "TY5_TIER3", + "TY5_THRES3", + "TY5_RATE3", + "TY5_SUR3", + "TY5_TIER4", + "TY5_THRES4", + "TY5_RATE4", + "TY5_SUR4", + "TY5_TIER5", + "TY5_THRES5", + "TY5_RATE5", + "TY5_SUR5", + "TAX_TYPE6", + "TY6_TIER1", + "TY6_THRES1", + "TY6_RATE1", + "TY6_SUR1", + "TY6_TIER2", + "TY6_THRES2", + "TY6_RATE2", + "TY6_SUR2", + "TY6_TIER3", + "TY6_THRES3", + "TY6_RATE3", + "TY6_SUR3", + "TY6_TIER4", + "TY6_THRES4", + "TY6_RATE4", + "TY6_SUR4", + "TY6_TIER5", + "TY6_THRES5", + "TY6_RATE5", + "TY6_SUR5", + ]) + ); + + //Apply business logic transfomrations. + //We don't have an inspection date, we instead have `date_estimated` + + return rawPftData; +}; +export default DecodePft; diff --git a/src/main/decoder/decode-stl.interface.ts b/src/main/decoder/decode-stl.interface.ts new file mode 100644 index 0000000..71d15b4 --- /dev/null +++ b/src/main/decoder/decode-stl.interface.ts @@ -0,0 +1,20 @@ +export interface DecodedStl { + ttl_type?: string; + ttl_typecd?: string; + t_amt?: number; + t_hrs?: number; + t_addlbr?: number; + t_discamt?: number; + t_mkupamt?: number; + t_gdiscamt?: number; + tax_amt?: number; + nt_amt?: number; + nt_hrs?: number; + nt_addlbr?: number; + nt_disc?: number; + nt_mkup?: number; + nt_gdis?: number; + ttl_typamt?: number; + ttl_hrs?: number; + ttl_amt?: number; +} diff --git a/src/main/decoder/decode-stl.ts b/src/main/decoder/decode-stl.ts new file mode 100644 index 0000000..98dc656 --- /dev/null +++ b/src/main/decoder/decode-stl.ts @@ -0,0 +1,62 @@ +import { DBFFile } from "dbffile"; +import log from "electron-log/main"; +import _ from "lodash"; +import deepLowerCaseKeys from "../../util/deepLowercaseKeys"; +import errorTypeCheck from "../../util/errorTypeCheck"; +import { DecodedStl } from "./decode-stl.interface"; + +const DecodeStl = async ( + extensionlessFilePath: string +): Promise => { + let dbf: DBFFile | null = null; + try { + dbf = await DBFFile.open(`${extensionlessFilePath}.STL`); + } catch (error) { + log.error("Error opening STL File.", errorTypeCheck(error)); + } + + if (!dbf) { + log.error(`Could not find any STL files at ${extensionlessFilePath}`); + throw new Error(`Could not find any STL 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 rawStlData: DecodedStl[] = rawDBFRecord.map((record) => { + const singleLineData: DecodedStl = deepLowerCaseKeys( + _.pick(record, [ + //TODO: Add typings for EMS File Formats. + "TTL_TYPE", + "TTL_TYPECD", + "T_AMT", + "T_HRS", + "T_ADDLBR", + "T_DISCAMT", + "T_MKUPAMT", + "T_GDISCAMT", + "TAX_AMT", + "NT_AMT", + "NT_HRS", + "NT_ADDLBR", + "NT_DISC", + "NT_MKUP", + "NT_GDIS", + "TTL_TYPAMT", + "TTL_HRS", + "TTL_AMT", + ]) + ); + //Apply line by line adjustments. + + return singleLineData; + }); + + //Apply business logic transfomrations. + //We don't have an inspection date, we instead have `date_estimated` + + return rawStlData; +}; +export default DecodeStl; diff --git a/src/main/decoder/decode-ttl.interface.ts b/src/main/decoder/decode-ttl.interface.ts new file mode 100644 index 0000000..67e2c8b --- /dev/null +++ b/src/main/decoder/decode-ttl.interface.ts @@ -0,0 +1,22 @@ +export interface DecodedTtl { + clm_total: number; + depreciation_taxes: number; + cieca_ttl: DecodedTtlLine; +} + +export interface DecodedTtlLine { + g_ttl_amt?: number; + g_bett_amt?: number; + g_rpd_amt?: number; + g_ded_amt?: number; + g_cust_amt?: number; + g_aa_amt?: number; + n_ttl_amt?: number; + prev_net?: number; + supp_amt?: number; + n_supp_amt?: number; + g_upd_amt?: number; + g_ttl_disc?: number; + g_tax?: number; + gst_amt?: number; +} diff --git a/src/main/decoder/decode-ttl.ts b/src/main/decoder/decode-ttl.ts new file mode 100644 index 0000000..02b729d --- /dev/null +++ b/src/main/decoder/decode-ttl.ts @@ -0,0 +1,52 @@ +import { DBFFile } from "dbffile"; +import log from "electron-log/main"; +import _ from "lodash"; +import deepLowerCaseKeys from "../../util/deepLowercaseKeys"; +import errorTypeCheck from "../../util/errorTypeCheck"; +import { DecodedTtl, DecodedTtlLine } from "./decode-ttl.interface"; + +const DecodeTtl = async ( + extensionlessFilePath: string +): Promise => { + let dbf: DBFFile | null = null; + try { + dbf = await DBFFile.open(`${extensionlessFilePath}.TTL`); + } catch (error) { + log.error("Error opening TTL File.", errorTypeCheck(error)); + } + + if (!dbf) { + log.error(`Could not find any TTL files at ${extensionlessFilePath}`); + throw new Error(`Could not find any TTL files at ${extensionlessFilePath}`); + } + + const rawDBFRecord = await dbf.readRecords(1); + + //PFT will always have only 1 row. + //Commented lines have been cross referenced with existing partner fields. + + const rawTtlData: DecodedTtlLine = deepLowerCaseKeys( + _.pick(rawDBFRecord[0], [ + //TODO: Add typings for EMS File Formats. + "G_TTL_AMT", + "G_BETT_AMT", + "G_RPD_AMT", + "G_DED_AMT", + "G_CUST_AMT", + "G_AA_AMT", + "N_TTL_AMT", + "PREV_NET", + "SUPP_AMT", + "N_SUPP_AMT", //Previously commented. Possible issue. + "G_UPD_AMT", + "G_TTL_DISC", + "G_TAX", + "GST_AMT", + ]) + ); + + //Apply business logic transfomrations. + + return { clm_total: 0, depreciation_taxes: 0, cieca_ttl: rawTtlData }; +}; +export default DecodeTtl; diff --git a/src/main/decoder/decode-veh.ts b/src/main/decoder/decode-veh.ts index ac82cb4..a00206e 100644 --- a/src/main/decoder/decode-veh.ts +++ b/src/main/decoder/decode-veh.ts @@ -8,7 +8,7 @@ import errorTypeCheck from "../../util/errorTypeCheck"; const DecodeVeh = async ( extensionlessFilePath: string ): Promise => { - let dbf: DBFFile; + let dbf: DBFFile | null = null; try { dbf = await DBFFile.open(`${extensionlessFilePath}V.VEH`); } catch (error) { diff --git a/src/main/decoder/decoder.ts b/src/main/decoder/decoder.ts index fb02516..8a73220 100644 --- a/src/main/decoder/decoder.ts +++ b/src/main/decoder/decoder.ts @@ -11,6 +11,16 @@ import DecodePfh from "./decode-pfh"; import { DecodedPfh } from "./decode-pfh.interface"; import DecodePfl from "./decode-pfl"; import { DecodedPfl } from "./decode-pfl.interface"; +import DecodePfm from "./decode-pfm"; +import { DecodedPfm } from "./decode-pfm.interface"; +import DecodePfo from "./decode-pfo"; +import { DecodedPfo } from "./decode-pfo.interface"; +import DecodePft from "./decode-pft"; +import { DecodedPft } from "./decode-pft.interface"; +import DecodeStl from "./decode-stl"; +import { DecodedStl } from "./decode-stl.interface"; +import DecodeTtl from "./decode-ttl"; +import { DecodedTtl } from "./decode-ttl.interface"; import DecodeVeh from "./decode-veh"; import { DecodedVeh } from "./decode-veh.interface"; @@ -31,7 +41,24 @@ async function ImportJob(filepath: string): Promise { const lin: DecodedLin[] = await DecodeLin(extensionlessFilePath); const pfh: DecodedPfh = await DecodePfh(extensionlessFilePath); const pfl: DecodedPfl = await DecodePfl(extensionlessFilePath); - log.debug("EMS Object", { ad1, ad2, veh, lin, pfh, pfl }); + const pft: DecodedPft = await DecodePft(extensionlessFilePath); + const pfm: DecodedPfm = await DecodePfm(extensionlessFilePath); + 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); // + log.debug("EMS Object", { + ad1, + ad2, + veh, + lin, + pfh, + pfl, + pft, + pfm, + pfo, + stl, + ttl, + }); } catch (error) { log.error("Error encountered while decoding job. ", errorTypeCheck(error)); }