diff --git a/eslint.config.mjs b/eslint.config.mjs index e4776d2..4beaad2 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,31 +1,31 @@ -import tseslint from '@electron-toolkit/eslint-config-ts' -import eslintConfigPrettier from '@electron-toolkit/eslint-config-prettier' -import eslintPluginReact from 'eslint-plugin-react' -import eslintPluginReactHooks from 'eslint-plugin-react-hooks' -import eslintPluginReactRefresh from 'eslint-plugin-react-refresh' +import tseslint from "@electron-toolkit/eslint-config-ts"; +import eslintConfigPrettier from "@electron-toolkit/eslint-config-prettier"; +import eslintPluginReact from "eslint-plugin-react"; +import eslintPluginReactHooks from "eslint-plugin-react-hooks"; +import eslintPluginReactRefresh from "eslint-plugin-react-refresh"; export default tseslint.config( - { ignores: ['**/node_modules', '**/dist', '**/out'] }, + { ignores: ["**/node_modules", "**/dist", "**/out"] }, tseslint.configs.recommended, eslintPluginReact.configs.flat.recommended, - eslintPluginReact.configs.flat['jsx-runtime'], + eslintPluginReact.configs.flat["jsx-runtime"], { settings: { react: { - version: 'detect' - } - } + version: "detect", + }, + }, }, { - files: ['**/*.{ts,tsx}'], + files: ["**/*.{ts,tsx}"], plugins: { - 'react-hooks': eslintPluginReactHooks, - 'react-refresh': eslintPluginReactRefresh + "react-hooks": eslintPluginReactHooks, + "react-refresh": eslintPluginReactRefresh, }, rules: { ...eslintPluginReactHooks.configs.recommended.rules, - ...eslintPluginReactRefresh.configs.vite.rules - } + ...eslintPluginReactRefresh.configs.vite.rules, + }, }, eslintConfigPrettier -) +); diff --git a/src/main/decoder/decode-ad1.interface.ts b/src/main/decoder/decode-ad1.interface.ts index d83ca12..5d91dc8 100644 --- a/src/main/decoder/decode-ad1.interface.ts +++ b/src/main/decoder/decode-ad1.interface.ts @@ -1,4 +1,4 @@ -interface ParsedAD1 { +export interface ParsedAD1 { // Insurance company information ins_co_id?: string; ins_co_nm?: string; @@ -129,7 +129,7 @@ interface ParsedAD1 { }; } -interface OwnerRecordInterface { +export interface OwnerRecordInterface { ownr_ln: string; ownr_fn: string; ownr_title: string; diff --git a/src/main/decoder/decode-ad1.ts b/src/main/decoder/decode-ad1.ts index 69e2aa1..352cce0 100644 --- a/src/main/decoder/decode-ad1.ts +++ b/src/main/decoder/decode-ad1.ts @@ -2,6 +2,7 @@ import { DBFFile } from "dbffile"; import log from "electron-log/main"; import _ from "lodash"; import deepLowerCaseKeys from "../../util/deepLowercaseKeys"; +import { OwnerRecordInterface, ParsedAD1 } from "./decode-ad1.interface"; const DecodeAD1 = async (extensionlessFilePath: string): Promise => { let dbf; @@ -15,9 +16,7 @@ const DecodeAD1 = async (extensionlessFilePath: string): Promise => { if (!dbf) { log.error(`Could not find any AD1 files at ${extensionlessFilePath}`); - return { - id: 0, - }; + return null; } const rawDBFRecord = await dbf.readRecords(1); @@ -200,20 +199,3 @@ const DecodeAD1 = async (extensionlessFilePath: string): Promise => { return { ...rawAd1Data, owner: { data: ownerRecord } }; }; export default DecodeAD1; - -interface OwnerRecordInterface { - ownr_ln: string; - ownr_fn: string; - ownr_title: string; - ownr_co_nm: string; - ownr_addr1: string; - ownr_addr2: string; - ownr_city: string; - ownr_st: string; - ownr_zip: string; - ownr_ctry: string; - ownr_ph1: string; - ownr_ph2: string; - ownr_ea: string; - shopid: string; -} diff --git a/src/main/decoder/decode-ad2.interface.ts b/src/main/decoder/decode-ad2.interface.ts new file mode 100644 index 0000000..b901e87 --- /dev/null +++ b/src/main/decoder/decode-ad2.interface.ts @@ -0,0 +1,29 @@ +export interface ParsedAD2 { + clmt_ln?: string; + clmt_fn?: string; + clmt_title?: string; + clmt_co_nm?: string; + clmt_addr1?: string; + clmt_addr2?: string; + clmt_city?: string; + clmt_st?: string; + clmt_zip?: string; + clmt_ctry?: string; + clmt_ph1?: string; + clmt_ph2?: string; + clmt_ea?: string; + est_co_id?: string; + est_co_nm?: string; + est_addr1?: string; + est_addr2?: string; + est_city?: string; + est_st?: string; + est_zip?: string; + est_ctry?: string; + est_ph1?: string; + est_ct_ln?: string; + est_ct_fn?: string; + est_ea?: string; + date_estimated?: Date; // This is transformed from insp_date + //insp_date?: string; // This exists initially but gets deleted +} diff --git a/src/main/decoder/decode-ad2.ts b/src/main/decoder/decode-ad2.ts new file mode 100644 index 0000000..1753640 --- /dev/null +++ b/src/main/decoder/decode-ad2.ts @@ -0,0 +1,147 @@ +import { DBFFile } from "dbffile"; +import log from "electron-log/main"; +import _ from "lodash"; +import deepLowerCaseKeys from "../../util/deepLowercaseKeys"; +import { ParsedAD2 } from "./decode-ad2.interface"; + +const DecodeAD2 = async (extensionlessFilePath: string): Promise => { + 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`); + log.log("Found AD2 file using regular CIECA Id."); + } + + if (!dbf) { + log.error(`Could not find any AD2 files at ${extensionlessFilePath}`); + return { + id: 0, + }; + } + + const rawDBFRecord = await dbf.readRecords(1); + + //AD2 will always have only 1 row. + //Commented lines have been cross referenced with existing partner fields. + + const rawAd2Data = deepLowerCaseKeys( + _.pick(rawDBFRecord[0], [ + //TODO: Add typings for EMS File Formats. + "CLMT_LN", + "CLMT_FN", + "CLMT_TITLE", + "CLMT_CO_NM", + "CLMT_ADDR1", + "CLMT_ADDR2", + "CLMT_CITY", + "CLMT_ST", + "CLMT_ZIP", + "CLMT_CTRY", + "CLMT_PH1", + //"CLMT_PH1X", + "CLMT_PH2", + //"CLMT_PH2X", + //"CLMT_FAX", + //"CLMT_FAXX", + "CLMT_EA", + "EST_CO_ID", + "EST_CO_NM", + "EST_ADDR1", + "EST_ADDR2", + "EST_CITY", + "EST_ST", + "EST_ZIP", + "EST_CTRY", + "EST_PH1", + //"EST_PH1X", + //"EST_PH2", + //"EST_PH2X", + //"EST_FAX", + //"EST_FAXX", + "EST_CT_LN", + "EST_CT_FN", + "EST_EA", + //"EST_LIC_NO", + //"EST_FILENO", + // "INSP_CT_LN", + // "INSP_CT_FN", + // "INSP_ADDR1", + // "INSP_ADDR2", + // "INSP_CITY", + // "INSP_ST", + // "INSP_ZIP", + // "INSP_CTRY", + // "INSP_PH1", + // "INSP_PH1X", + // "INSP_PH2", + // "INSP_PH2X", + // "INSP_FAX", + // "INSP_FAXX", + // "INSP_EA", + // "INSP_CODE", + // "INSP_DESC", + "INSP_DATE", //RENAME TO date_estimated + // "INSP_TIME", + // "RF_CO_ID", + // "RF_CO_NM", + // "RF_ADDR1", + // "RF_ADDR2", + // "RF_CITY", + // "RF_ST", + // "RF_ZIP", + // "RF_CTRY", + // "RF_PH1", + // "RF_PH1X", + // "RF_PH2", + // "RF_PH2X", + // "RF_FAX", + // "RF_FAXX", + // "RF_CT_LN", + // "RF_CT_FN", + // "RF_EA", + // "RF_TAX_ID", + // "RF_LIC_NO", + // "RF_BAR_NO", + // "RO_IN_DATE", + // "RO_IN_TIME", + // "TAR_DATE", + // "TAR_TIME", + // "RO_CMPDATE", + // "RO_CMPTIME", + // "DATE_OUT", + // "TIME_OUT", + // "RF_ESTIMTR", + // "MKTG_TYPE", + // "MKTG_SRC", + // "LOC_NM", + // "LOC_ADDR1", + // "LOC_ADDR2", + // "LOC_CITY", + // "LOC_ST", + // "LOC_ZIP", + // "LOC_CTRY", + // "LOC_PH1", + // "LOC_PH1X", + // "LOC_PH2", + // "LOC_PH2X", + // "LOC_FAX", + // "LOC_FAXX", + // "LOC_CT_LN", + // "LOC_CT_FN", + // "LOC_TITLE", + // "LOC_PH", + // "LOC_PHX", + // "LOC_EA", + ]) + ); + + //Apply business logic transfomrations. + //We don't have an inspection date, we instead have `date_estimated` + rawAd2Data.date_estimated = rawAd2Data.insp_date; + delete rawAd2Data.insp_date; + + return null; +}; +export default DecodeAD2; diff --git a/src/main/decoder/decode-veh.interface.ts b/src/main/decoder/decode-veh.interface.ts new file mode 100644 index 0000000..fc9f5d0 --- /dev/null +++ b/src/main/decoder/decode-veh.interface.ts @@ -0,0 +1 @@ +export interface ParsedVeh {} diff --git a/src/main/decoder/decode-veh.ts b/src/main/decoder/decode-veh.ts new file mode 100644 index 0000000..213cabb --- /dev/null +++ b/src/main/decoder/decode-veh.ts @@ -0,0 +1,148 @@ +import { DBFFile } from "dbffile"; +import log from "electron-log/main"; +import _ from "lodash"; +import deepLowerCaseKeys from "../../util/deepLowercaseKeys"; +import { ParsedAD2 } from "./decode-ad2.interface"; +import { ParsedVeh } from "./decode-veh.interface"; + +const DecodeVeh = async (extensionlessFilePath: string): Promise => { + 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`); + log.log("Found AD2 file using regular CIECA Id."); + } + + if (!dbf) { + log.error(`Could not find any AD2 files at ${extensionlessFilePath}`); + return { + id: 0, + }; + } + + const rawDBFRecord = await dbf.readRecords(1); + + //AD2 will always have only 1 row. + //Commented lines have been cross referenced with existing partner fields. + + const rawAd2Data = deepLowerCaseKeys( + _.pick(rawDBFRecord[0], [ + //TODO: Add typings for EMS File Formats. + "CLMT_LN", + "CLMT_FN", + "CLMT_TITLE", + "CLMT_CO_NM", + "CLMT_ADDR1", + "CLMT_ADDR2", + "CLMT_CITY", + "CLMT_ST", + "CLMT_ZIP", + "CLMT_CTRY", + "CLMT_PH1", + //"CLMT_PH1X", + "CLMT_PH2", + //"CLMT_PH2X", + //"CLMT_FAX", + //"CLMT_FAXX", + "CLMT_EA", + "EST_CO_ID", + "EST_CO_NM", + "EST_ADDR1", + "EST_ADDR2", + "EST_CITY", + "EST_ST", + "EST_ZIP", + "EST_CTRY", + "EST_PH1", + //"EST_PH1X", + //"EST_PH2", + //"EST_PH2X", + //"EST_FAX", + //"EST_FAXX", + "EST_CT_LN", + "EST_CT_FN", + "EST_EA", + //"EST_LIC_NO", + //"EST_FILENO", + // "INSP_CT_LN", + // "INSP_CT_FN", + // "INSP_ADDR1", + // "INSP_ADDR2", + // "INSP_CITY", + // "INSP_ST", + // "INSP_ZIP", + // "INSP_CTRY", + // "INSP_PH1", + // "INSP_PH1X", + // "INSP_PH2", + // "INSP_PH2X", + // "INSP_FAX", + // "INSP_FAXX", + // "INSP_EA", + // "INSP_CODE", + // "INSP_DESC", + "INSP_DATE", //RENAME TO date_estimated + // "INSP_TIME", + // "RF_CO_ID", + // "RF_CO_NM", + // "RF_ADDR1", + // "RF_ADDR2", + // "RF_CITY", + // "RF_ST", + // "RF_ZIP", + // "RF_CTRY", + // "RF_PH1", + // "RF_PH1X", + // "RF_PH2", + // "RF_PH2X", + // "RF_FAX", + // "RF_FAXX", + // "RF_CT_LN", + // "RF_CT_FN", + // "RF_EA", + // "RF_TAX_ID", + // "RF_LIC_NO", + // "RF_BAR_NO", + // "RO_IN_DATE", + // "RO_IN_TIME", + // "TAR_DATE", + // "TAR_TIME", + // "RO_CMPDATE", + // "RO_CMPTIME", + // "DATE_OUT", + // "TIME_OUT", + // "RF_ESTIMTR", + // "MKTG_TYPE", + // "MKTG_SRC", + // "LOC_NM", + // "LOC_ADDR1", + // "LOC_ADDR2", + // "LOC_CITY", + // "LOC_ST", + // "LOC_ZIP", + // "LOC_CTRY", + // "LOC_PH1", + // "LOC_PH1X", + // "LOC_PH2", + // "LOC_PH2X", + // "LOC_FAX", + // "LOC_FAXX", + // "LOC_CT_LN", + // "LOC_CT_FN", + // "LOC_TITLE", + // "LOC_PH", + // "LOC_PHX", + // "LOC_EA", + ]) + ); + + //Apply business logic transfomrations. + //We don't have an inspection date, we instead have `date_estimated` + rawAd2Data.date_estimated = rawAd2Data.insp_date; + delete rawAd2Data.insp_date; + + return null; +}; +export default DecodeVeh; diff --git a/src/main/decoder/decoder.ts b/src/main/decoder/decoder.ts index 2e23fd9..b7eb24b 100644 --- a/src/main/decoder/decoder.ts +++ b/src/main/decoder/decoder.ts @@ -1,6 +1,12 @@ import log from "electron-log/main"; import path from "path"; +import errorTypeCheck from "../../util/errorTypeCheck"; import DecodeAD1 from "./decode-ad1"; +import { ParsedAD1 } from "./decode-ad1.interface"; +import DecodeAD2 from "./decode-ad2"; +import { ParsedAD2 } from "./decode-ad2.interface"; +import DecodeVeh from "./decode-veh"; +import { ParsedVeh } from "./decode-veh.interface"; async function ImportJob(filepath: string): Promise { const parsedFilePath = path.parse(filepath); @@ -10,10 +16,14 @@ async function ImportJob(filepath: string): Promise { ); log.debug("Importing Job", extensionlessFilePath); - const decodedJob = {}; - - const ad1: ParsedAD1 = await DecodeAD1(extensionlessFilePath); - log.debug("AD1", ad1); + try { + const ad1: ParsedAD1 = await DecodeAD1(extensionlessFilePath); + const ad2: ParsedAD2 = await DecodeAD2(extensionlessFilePath); + const veh: ParsedVeh = await DecodeVeh(extensionlessFilePath); + log.debug("EMS Object", { ad1, ad2, veh }); + } catch (error) { + log.error("Error encountered while decoding job. ", errorTypeCheck(error)); + } } export default ImportJob; diff --git a/src/util/errorTypeCheck.ts b/src/util/errorTypeCheck.ts index 9c97729..8ed8853 100644 --- a/src/util/errorTypeCheck.ts +++ b/src/util/errorTypeCheck.ts @@ -1,4 +1,5 @@ -function errorTypeCheck(passedError: any): ParsedError { +//Type checking here allows us to skip the boilerplate in every catch block. +function errorTypeCheck(passedError: Error | unknown): ParsedError { const errorMessage = passedError instanceof Error ? passedError.message : String(passedError); const errorStack = @@ -13,7 +14,7 @@ function errorTypeCheck(passedError: any): ParsedError { } export default errorTypeCheck; -interface ParsedError { +export interface ParsedError { message: string; stack: string; }