Succesful test imports to IO.

This commit is contained in:
Patrick Fic
2025-03-21 15:17:00 -07:00
parent d14137dc44
commit b312532121
8 changed files with 165 additions and 65 deletions

4
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,4 @@
{
"prettier.trailingComma": "all",
"editor.formatOnSave": true
}

View File

@@ -33,18 +33,18 @@ const DecodePfh = async (
//TODO: Several of these fields will fail. Should extend schema to capture them. //TODO: Several of these fields will fail. Should extend schema to capture them.
//"ID_PRO_NAM", //Remove //"ID_PRO_NAM", //Remove
"TAX_PRETHR", "TAX_PRETHR",
"TAX_THRAMT", //Remove //"TAX_THRAMT", //Remove
"TAX_PSTTHR", "TAX_PSTTHR",
"TAX_TOW_IN", //Remove //"TAX_TOW_IN", //Remove
"TAX_TOW_RT", "TAX_TOW_RT",
"TAX_STR_IN", //Remove //"TAX_STR_IN", //Remove
"TAX_STR_RT", "TAX_STR_RT",
"TAX_SUB_IN", //Remove //"TAX_SUB_IN", //Remove
"TAX_SUB_RT", "TAX_SUB_RT",
"TAX_BTR_IN", //Remove //"TAX_BTR_IN", //Remove
"TAX_LBR_RT", "TAX_LBR_RT",
"TAX_GST_RT", "TAX_GST_RT",
"TAX_GST_IN", //Remove //"TAX_GST_IN", //Remove
"ADJ_G_DISC", "ADJ_G_DISC",
"ADJ_TOWDIS", "ADJ_TOWDIS",
"ADJ_STRDIS", "ADJ_STRDIS",

View File

@@ -137,9 +137,9 @@ const DecodePfm = async (
...jobMaterialRates, ...jobMaterialRates,
materials: { materials: {
mash: mashLine, 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.
}; };
}; };

View File

@@ -5,6 +5,7 @@ import deepLowerCaseKeys from "../../util/deepLowercaseKeys";
import { DecodedVeh, VehicleRecordInterface } from "./decode-veh.interface"; import { DecodedVeh, VehicleRecordInterface } from "./decode-veh.interface";
import errorTypeCheck from "../../util/errorTypeCheck"; import errorTypeCheck from "../../util/errorTypeCheck";
import store from "../store/store"; import store from "../store/store";
import typeCaster from "../../util/typeCaster";
const DecodeVeh = async ( const DecodeVeh = async (
extensionlessFilePath: string, extensionlessFilePath: string,
@@ -28,35 +29,43 @@ const DecodeVeh = async (
//AD2 will always have only 1 row. //AD2 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 rawVehData: VehicleRecordInterface = deepLowerCaseKeys( //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.
_.pick(rawDBFRecord[0], [ //Alternative is to change the database schema to match the original type.
//TODO: Add typings for EMS File Formats. const rawVehData: VehicleRecordInterface = typeCaster(
"IMPACT_1", deepLowerCaseKeys(
"IMPACT_2", _.pick(rawDBFRecord[0], [
"DB_V_CODE", //TODO: Add typings for EMS File Formats.
"PLATE_NO", "IMPACT_1",
"PLATE_ST", "IMPACT_2",
"V_VIN", "DB_V_CODE",
"V_COND", "PLATE_NO",
"V_PROD_DT", "PLATE_ST",
"V_MODEL_YR", "V_VIN",
"V_MAKECODE", "V_COND",
"V_MAKEDESC", "V_PROD_DT",
"V_MODEL", "V_MODEL_YR",
"V_TYPE", "V_MAKECODE",
"V_BSTYLE", "V_MAKEDESC",
"V_TRIMCODE", "V_MODEL",
"TRIM_COLOR", "V_TYPE",
"V_MLDGCODE", "V_BSTYLE",
"V_ENGINE", "V_TRIMCODE",
"V_MILEAGE", "TRIM_COLOR",
"V_COLOR", "V_MLDGCODE",
"V_TONE", "V_ENGINE",
"V_STAGE", "V_MILEAGE",
"PAINT_CD1", "V_COLOR",
"PAINT_CD2", "V_TONE",
"PAINT_CD3", "V_STAGE",
]), "PAINT_CD1",
"PAINT_CD2",
"PAINT_CD3",
]),
),
{
v_tone: "string",
v_stage: "string",
},
); );
//Apply business logic transfomrations. //Apply business logic transfomrations.
@@ -69,13 +78,16 @@ const DecodeVeh = async (
delete rawVehData.v_model; delete rawVehData.v_model;
//Consolidate Area of Damage. //Consolidate Area of Damage.
rawVehData.area_of_damage = { const area_of_damage = {
impact1: rawVehData.impact_1 ?? "", impact1: rawVehData.impact_1 ?? "",
impact2: rawVehData.impact_2 ?? "", impact2: rawVehData.impact_2 ?? "",
}; };
delete rawVehData.impact_1; delete rawVehData.impact_1;
delete rawVehData.impact_2; delete rawVehData.impact_2;
const kmin = rawVehData.v_mileage ?? 0;
delete rawVehData.v_mileage;
//Consolidate Paint Code information. //Consolidate Paint Code information.
rawVehData.v_paint_codes = { rawVehData.v_paint_codes = {
paint_cd1: rawVehData.paint_cd1 ?? "", paint_cd1: rawVehData.paint_cd1 ?? "",
@@ -97,8 +109,8 @@ const DecodeVeh = async (
v_make_desc: rawVehData.v_make_desc, v_make_desc: rawVehData.v_make_desc,
v_model_desc: rawVehData.v_model_desc, v_model_desc: rawVehData.v_model_desc,
v_color: rawVehData.v_color, v_color: rawVehData.v_color,
kmin: rawVehData.v_mileage, kmin: kmin,
area_of_damage: rawVehData.area_of_damage, area_of_damage: area_of_damage,
vehicle: { vehicle: {
data: rawVehData, data: rawVehData,
}, },

View File

@@ -1,10 +1,13 @@
import { UUID } from "crypto"; import { UUID } from "crypto";
import { Notification } from "electron";
import log from "electron-log/main"; import log from "electron-log/main";
import fs from "fs"; import fs from "fs";
import path from "path"; import path from "path";
import errorTypeCheck from "../../util/errorTypeCheck"; import errorTypeCheck from "../../util/errorTypeCheck";
import client from "../graphql/graphql-client"; import client from "../graphql/graphql-client";
import { import {
INSERT_AVAILABLE_JOB_TYPED,
InsertAvailableJobResult,
QUERY_JOB_BY_CLM_NO_TYPED, QUERY_JOB_BY_CLM_NO_TYPED,
QUERY_VEHICLE_BY_VIN_TYPED, QUERY_VEHICLE_BY_VIN_TYPED,
QueryJobByClmNoResult, QueryJobByClmNoResult,
@@ -77,6 +80,7 @@ async function ImportJob(filepath: string): Promise<void> {
...stl, ...stl,
...ttl, ...ttl,
...pfp, ...pfp,
shopid: store.get("app.bodyshop.id") as UUID,
}; };
// Save jobObject to a timestamped JSON file // Save jobObject to a timestamped JSON file
@@ -100,11 +104,8 @@ async function ImportJob(filepath: string): Promise<void> {
jobObject, jobObject,
}); });
//Build the request object
//Insert it
const newAvailableJob: AvailableJobSchema = { const newAvailableJob: AvailableJobSchema = {
uploaded_by: store.get("app.user.email"), uploaded_by: store.get("user.email"),
bodyshopid: store.get("app.bodyshop.id"), bodyshopid: store.get("app.bodyshop.id"),
cieca_id: jobObject.ciecaid, cieca_id: jobObject.ciecaid,
est_data: jobObject, est_data: jobObject,
@@ -141,8 +142,28 @@ async function ImportJob(filepath: string): Promise<void> {
newAvailableJob.issupplement = true; newAvailableJob.issupplement = true;
newAvailableJob.jobid = existingJobRecord.jobs[0].id; 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) { } catch (error) {
log.error("Error encountered while decoding job. ", errorTypeCheck(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, DecodedTtl,
DecodedPfp { DecodedPfp {
vehicleid?: UUID; vehicleid?: UUID;
shopid: UUID;
} }
export interface AvailableJobSchema { export interface AvailableJobSchema {

View File

@@ -1,5 +1,5 @@
{ {
"toolbar": { "toolbar": {
"help": "Help" "help": "Help"
} }
} }

View File

@@ -1,20 +1,20 @@
{ {
"translation": { "translation": {
"navigation": { "navigation": {
"home": "Home", "home": "Home",
"settings": "Settings" "settings": "Settings"
}, },
"settings": { "settings": {
"actions": { "actions": {
"addpath": "Add path", "addpath": "Add path",
"startwatcher": "Start Watcher", "startwatcher": "Start Watcher",
"stopwatcher": "Stop Watcher\n" "stopwatcher": "Stop Watcher\n"
}, },
"labels": { "labels": {
"started": "Started", "started": "Started",
"stopped": "Stopped", "stopped": "Stopped",
"watcherstatus": "Watcher Status" "watcherstatus": "Watcher Status"
} }
} }
} }
} }

62
src/util/typeCaster.ts Normal file
View File

@@ -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<T extends object, K extends keyof T>(
obj: T,
typeMappings: Partial<
Record<K, "string" | "number" | "boolean" | "object" | "array">
>,
): 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;