import { DBFFile } from "dbffile"; import log from "electron-log/main"; import _ from "lodash"; import deepLowerCaseKeys from "../../util/deepLowercaseKeys"; import errorTypeCheck from "../../util/errorTypeCheck"; import { DecodedPfl, JobLaborRateFields, DecodedPflLine, } from "./decode-pfl.interface"; import { platform } from "@electron-toolkit/utils"; import { findFileCaseInsensitive } from "./decoder-utils"; const DecodePfl = async ( extensionlessFilePath: string, ): Promise => { let dbf: DBFFile | null = null; if (platform.isWindows) { try { dbf = await DBFFile.open(`${extensionlessFilePath}.PFL`); } catch (error) { //PFL File only has 1 location. log.error("Error opening PFL File.", errorTypeCheck(error)); } if (!dbf) { log.error(`Could not find any PFL files at ${extensionlessFilePath}`); throw new Error( `Could not find any PFL files at ${extensionlessFilePath}`, ); } } else { const possibleExtensions: string[] = [".pfl"]; const filePath = await findFileCaseInsensitive( extensionlessFilePath, possibleExtensions, ); try { if (!filePath) { log.error(`Could not find any PFL files at ${extensionlessFilePath}`); throw new Error( `Could not find any PFL files at ${extensionlessFilePath}`, ); } dbf = await DBFFile.open(filePath); } catch (error) { log.error("Error opening PFL File.", errorTypeCheck(error)); throw error; } } const rawDBFRecord = await dbf.readRecords(); //AD2 will always have only 1 row. //Commented lines have been cross referenced with existing partner fields. const jobLaborRates: JobLaborRateFields = { rate_laa: 0, rate_lab: 0, rate_lad: 0, rate_las: 0, rate_lar: 0, rate_lae: 0, rate_lag: 0, rate_laf: 0, rate_lam: 0, rate_lau: 0, rate_la1: 0, rate_la2: 0, rate_la3: 0, rate_la4: 0, }; const rawPflData: DecodedPflLine[] = rawDBFRecord.map((record) => { const singleLineData: DecodedPflLine = deepLowerCaseKeys( _.pick(record, [ //TODO: Add typings for EMS File Formats. "LBR_TYPE", "LBR_DESC", "LBR_RATE", "LBR_TAX_IN", "LBR_TAXP", "LBR_ADJP", "LBR_TX_TY1", "LBR_TX_IN1", "LBR_TX_TY2", "LBR_TX_IN2", "LBR_TX_TY3", "LBR_TX_IN3", "LBR_TX_TY4", "LBR_TX_IN4", "LBR_TX_TY5", "LBR_TX_IN5", ]), ); //Apply line by line adjustments. //Set the job.rate_ field based on the value. jobLaborRates[`rate_${singleLineData.lbr_type.toLowerCase()}`] = singleLineData.lbr_rate; //For Mitchell, Alum is stored under LA3 instead of LAA. Shift it back over. //The old partner had a check for this, but it always was true. Matching that logic. if (singleLineData.lbr_type === "LA3") { jobLaborRates[`rate_laa`] = singleLineData.lbr_rate; } //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 business logic transfomrations. //We don't have an inspection date, we instead have `date_estimated` const pflObj = _.keyBy(rawPflData, "lbr_type"); return { ...jobLaborRates, cieca_pfl: pflObj }; }; export default DecodePfl;