From b312532121d3e48dc94f46be53d65754cfa62a6d Mon Sep 17 00:00:00 2001 From: Patrick Fic Date: Fri, 21 Mar 2025 15:17:00 -0700 Subject: [PATCH] Succesful test imports to IO. --- .vscode/settings.json | 4 ++ src/main/decoder/decode-pfh.ts | 12 ++-- src/main/decoder/decode-pfm.ts | 4 +- src/main/decoder/decode-veh.ts | 76 +++++++++++++---------- src/main/decoder/decoder.ts | 30 +++++++-- src/util/translations/en-US/main.json | 6 +- src/util/translations/en-US/renderer.json | 36 +++++------ src/util/typeCaster.ts | 62 ++++++++++++++++++ 8 files changed, 165 insertions(+), 65 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 src/util/typeCaster.ts diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..ff05bd0 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,4 @@ +{ + "prettier.trailingComma": "all", + "editor.formatOnSave": true +} diff --git a/src/main/decoder/decode-pfh.ts b/src/main/decoder/decode-pfh.ts index 38c6949..d2aff67 100644 --- a/src/main/decoder/decode-pfh.ts +++ b/src/main/decoder/decode-pfh.ts @@ -33,18 +33,18 @@ const DecodePfh = async ( //TODO: Several of these fields will fail. Should extend schema to capture them. //"ID_PRO_NAM", //Remove "TAX_PRETHR", - "TAX_THRAMT", //Remove + //"TAX_THRAMT", //Remove "TAX_PSTTHR", - "TAX_TOW_IN", //Remove + //"TAX_TOW_IN", //Remove "TAX_TOW_RT", - "TAX_STR_IN", //Remove + //"TAX_STR_IN", //Remove "TAX_STR_RT", - "TAX_SUB_IN", //Remove + //"TAX_SUB_IN", //Remove "TAX_SUB_RT", - "TAX_BTR_IN", //Remove + //"TAX_BTR_IN", //Remove "TAX_LBR_RT", "TAX_GST_RT", - "TAX_GST_IN", //Remove + //"TAX_GST_IN", //Remove "ADJ_G_DISC", "ADJ_TOWDIS", "ADJ_STRDIS", diff --git a/src/main/decoder/decode-pfm.ts b/src/main/decoder/decode-pfm.ts index c7dbdfb..eea61dc 100644 --- a/src/main/decoder/decode-pfm.ts +++ b/src/main/decoder/decode-pfm.ts @@ -137,9 +137,9 @@ const DecodePfm = async ( ...jobMaterialRates, materials: { mash: mashLine, - mapa: mapaLine, + mapa: mapaLine, //TODO: Need to verify if more fields are to come in here. }, - cieca_pfm: rawPfmData, + //cieca_pfm: rawPfmData, //TODO: Not currently captured. This may have valu in the future. }; }; diff --git a/src/main/decoder/decode-veh.ts b/src/main/decoder/decode-veh.ts index f6e35fe..c503c8e 100644 --- a/src/main/decoder/decode-veh.ts +++ b/src/main/decoder/decode-veh.ts @@ -5,6 +5,7 @@ import deepLowerCaseKeys from "../../util/deepLowercaseKeys"; import { DecodedVeh, VehicleRecordInterface } from "./decode-veh.interface"; import errorTypeCheck from "../../util/errorTypeCheck"; import store from "../store/store"; +import typeCaster from "../../util/typeCaster"; const DecodeVeh = async ( extensionlessFilePath: string, @@ -28,35 +29,43 @@ const DecodeVeh = async ( //AD2 will always have only 1 row. //Commented lines have been cross referenced with existing partner fields. - const rawVehData: VehicleRecordInterface = deepLowerCaseKeys( - _.pick(rawDBFRecord[0], [ - //TODO: Add typings for EMS File Formats. - "IMPACT_1", - "IMPACT_2", - "DB_V_CODE", - "PLATE_NO", - "PLATE_ST", - "V_VIN", - "V_COND", - "V_PROD_DT", - "V_MODEL_YR", - "V_MAKECODE", - "V_MAKEDESC", - "V_MODEL", - "V_TYPE", - "V_BSTYLE", - "V_TRIMCODE", - "TRIM_COLOR", - "V_MLDGCODE", - "V_ENGINE", - "V_MILEAGE", - "V_COLOR", - "V_TONE", - "V_STAGE", - "PAINT_CD1", - "PAINT_CD2", - "PAINT_CD3", - ]), + //typeCaster is required as the previous partner sent some of these values toString, and the database was made accordingly rather than keeping their original type. + //Alternative is to change the database schema to match the original type. + const rawVehData: VehicleRecordInterface = typeCaster( + deepLowerCaseKeys( + _.pick(rawDBFRecord[0], [ + //TODO: Add typings for EMS File Formats. + "IMPACT_1", + "IMPACT_2", + "DB_V_CODE", + "PLATE_NO", + "PLATE_ST", + "V_VIN", + "V_COND", + "V_PROD_DT", + "V_MODEL_YR", + "V_MAKECODE", + "V_MAKEDESC", + "V_MODEL", + "V_TYPE", + "V_BSTYLE", + "V_TRIMCODE", + "TRIM_COLOR", + "V_MLDGCODE", + "V_ENGINE", + "V_MILEAGE", + "V_COLOR", + "V_TONE", + "V_STAGE", + "PAINT_CD1", + "PAINT_CD2", + "PAINT_CD3", + ]), + ), + { + v_tone: "string", + v_stage: "string", + }, ); //Apply business logic transfomrations. @@ -69,13 +78,16 @@ const DecodeVeh = async ( delete rawVehData.v_model; //Consolidate Area of Damage. - rawVehData.area_of_damage = { + const area_of_damage = { impact1: rawVehData.impact_1 ?? "", impact2: rawVehData.impact_2 ?? "", }; delete rawVehData.impact_1; delete rawVehData.impact_2; + const kmin = rawVehData.v_mileage ?? 0; + delete rawVehData.v_mileage; + //Consolidate Paint Code information. rawVehData.v_paint_codes = { paint_cd1: rawVehData.paint_cd1 ?? "", @@ -97,8 +109,8 @@ const DecodeVeh = async ( v_make_desc: rawVehData.v_make_desc, v_model_desc: rawVehData.v_model_desc, v_color: rawVehData.v_color, - kmin: rawVehData.v_mileage, - area_of_damage: rawVehData.area_of_damage, + kmin: kmin, + area_of_damage: area_of_damage, vehicle: { data: rawVehData, }, diff --git a/src/main/decoder/decoder.ts b/src/main/decoder/decoder.ts index 9b51407..2c14ddb 100644 --- a/src/main/decoder/decoder.ts +++ b/src/main/decoder/decoder.ts @@ -1,10 +1,13 @@ import { UUID } from "crypto"; +import { Notification } from "electron"; import log from "electron-log/main"; import fs from "fs"; import path from "path"; import errorTypeCheck from "../../util/errorTypeCheck"; import client from "../graphql/graphql-client"; import { + INSERT_AVAILABLE_JOB_TYPED, + InsertAvailableJobResult, QUERY_JOB_BY_CLM_NO_TYPED, QUERY_VEHICLE_BY_VIN_TYPED, QueryJobByClmNoResult, @@ -77,6 +80,7 @@ async function ImportJob(filepath: string): Promise { ...stl, ...ttl, ...pfp, + shopid: store.get("app.bodyshop.id") as UUID, }; // Save jobObject to a timestamped JSON file @@ -100,11 +104,8 @@ async function ImportJob(filepath: string): Promise { jobObject, }); - //Build the request object - - //Insert it const newAvailableJob: AvailableJobSchema = { - uploaded_by: store.get("app.user.email"), + uploaded_by: store.get("user.email"), bodyshopid: store.get("app.bodyshop.id"), cieca_id: jobObject.ciecaid, est_data: jobObject, @@ -141,8 +142,28 @@ async function ImportJob(filepath: string): Promise { newAvailableJob.issupplement = true; newAvailableJob.jobid = existingJobRecord.jobs[0].id; } + + const insertRecordResult: InsertAvailableJobResult = await client.request( + INSERT_AVAILABLE_JOB_TYPED, + { + jobInput: [newAvailableJob], + }, + ); + new Notification({ + title: "Job Imported", + body: `Job ${newAvailableJob.cieca_id} imported successfully`, + actions: [{ text: "View Job", type: "button" }], + subtitle: newAvailableJob.ownr_name, + }).show(); + + log.debug("Job inserted", insertRecordResult); } catch (error) { log.error("Error encountered while decoding job. ", errorTypeCheck(error)); + new Notification({ + title: "Job Upload Failure", + body: errorTypeCheck(error).message, //TODO: Remove after debug. + actions: [{ text: "View Job", type: "button" }], + }).show(); } } @@ -163,6 +184,7 @@ export interface RawJobDataObject DecodedTtl, DecodedPfp { vehicleid?: UUID; + shopid: UUID; } export interface AvailableJobSchema { diff --git a/src/util/translations/en-US/main.json b/src/util/translations/en-US/main.json index 6dd0159..8dba742 100644 --- a/src/util/translations/en-US/main.json +++ b/src/util/translations/en-US/main.json @@ -1,5 +1,5 @@ { - "toolbar": { - "help": "Help" - } + "toolbar": { + "help": "Help" + } } diff --git a/src/util/translations/en-US/renderer.json b/src/util/translations/en-US/renderer.json index 0f62d18..0d8881d 100644 --- a/src/util/translations/en-US/renderer.json +++ b/src/util/translations/en-US/renderer.json @@ -1,20 +1,20 @@ { - "translation": { - "navigation": { - "home": "Home", - "settings": "Settings" - }, - "settings": { - "actions": { - "addpath": "Add path", - "startwatcher": "Start Watcher", - "stopwatcher": "Stop Watcher\n" - }, - "labels": { - "started": "Started", - "stopped": "Stopped", - "watcherstatus": "Watcher Status" - } - } - } + "translation": { + "navigation": { + "home": "Home", + "settings": "Settings" + }, + "settings": { + "actions": { + "addpath": "Add path", + "startwatcher": "Start Watcher", + "stopwatcher": "Stop Watcher\n" + }, + "labels": { + "started": "Started", + "stopped": "Stopped", + "watcherstatus": "Watcher Status" + } + } + } } diff --git a/src/util/typeCaster.ts b/src/util/typeCaster.ts new file mode 100644 index 0000000..3026455 --- /dev/null +++ b/src/util/typeCaster.ts @@ -0,0 +1,62 @@ +/** + * Casts specified properties of an object to the desired types + * + * @param obj The object whose properties need to be cast + * @param typeMappings An object where keys are property names from the source object + * and values are the type to cast to ('string', 'number', 'boolean', etc.) + * @returns A new object with the specified properties cast to their desired types + */ +function typeCaster( + obj: T, + typeMappings: Partial< + Record + >, +): T { + const result = { ...obj }; + + for (const key in typeMappings) { + if (Object.prototype.hasOwnProperty.call(obj, key)) { + const targetType = typeMappings[key]; + const value = obj[key]; + + switch (targetType) { + case "string": + (result as any)[key] = String(value); + break; + case "number": + (result as any)[key] = Number(value); + break; + case "boolean": + (result as any)[key] = Boolean(value); + break; + case "object": + if (value && typeof value !== "object") { + try { + (result as any)[key] = JSON.parse(String(value)); + } catch { + (result as any)[key] = {}; + } + } + break; + case "array": + if (Array.isArray(value)) { + (result as any)[key] = value; + } else if (value && typeof value === "string") { + try { + const parsed = JSON.parse(value); + (result as any)[key] = Array.isArray(parsed) ? parsed : [parsed]; + } catch { + (result as any)[key] = [value]; + } + } else { + (result as any)[key] = value ? [value] : []; + } + break; + } + } + } + + return result; +} + +export default typeCaster;