Strip out partner related functionality.

This commit is contained in:
Patrick Fic
2025-12-08 13:53:15 -08:00
parent 267ef714a7
commit 39d81bbc6a
54 changed files with 79 additions and 3889 deletions

View File

@@ -1,158 +0,0 @@
import { DBFFile } from "dbffile";
import errorTypeCheck from "../../util/errorTypeCheck";
import { ad1FieldLineDescriptors } from "../util/ems-interface/fielddescriptors/ad1-field-descriptors";
import {
deleteEmsFileIfExists,
generateEmsOutFilePath,
} from "../util/ems-util";
import { EmsPartsOrder } from "./ems-parts-order-interfaces";
const EmsPartsOrderGenerateAd1File = async (
partsOrder: EmsPartsOrder,
): Promise<boolean> => {
try {
const records = [
{
INS_CO_ID: partsOrder.job.ins_co_nm,
INS_CO_NM: partsOrder.job.ins_co_nm,
INS_ADDR1: partsOrder.job.ins_addr1,
INS_ADDR2: partsOrder.job.ins_addr2,
INS_CITY: partsOrder.job.ins_city,
INS_ST: partsOrder.job.ins_st,
INS_ZIP: partsOrder.job.ins_zip,
INS_CTRY: partsOrder.job.ins_ctry,
INS_PH1: partsOrder.job.ins_ph1,
INS_PH1X: partsOrder.job.ins_ph1x,
INS_PH2: partsOrder.job.ins_ph2,
INS_PH2X: partsOrder.job.ins_ph2x,
INS_FAX: partsOrder.job.ins_fax,
INS_FAXX: partsOrder.job.ins_faxx,
INS_CT_LN: partsOrder.job.ins_ct_ln,
INS_CT_FN: partsOrder.job.ins_ct_fn,
INS_TITLE: partsOrder.job.ins_title,
INS_CT_PH: partsOrder.job.ins_ct_ph,
INS_CT_PHX: partsOrder.job.ins_ct_phx,
INS_EA: partsOrder.job.ins_ea,
INS_MEMO: partsOrder.job.ins_memo,
POLICY_NO: partsOrder.job.policy_no,
DED_AMT: partsOrder.job.ded_amt,
DED_STATUS: partsOrder.job.ded_status,
ASGN_NO: partsOrder.job.asgn_no,
ASGN_DATE: partsOrder.job.asgn_date
? new Date(partsOrder.job.asgn_date)
: null,
ASGN_TYPE: partsOrder.job.asgn_type,
CLM_NO: partsOrder.job.clm_no,
CLM_OFC_ID: partsOrder.job.clm_ofc_id,
CLM_OFC_NM: partsOrder.job.clm_ofc_nm,
CLM_ADDR1: partsOrder.job.clm_addr1,
CLM_ADDR2: partsOrder.job.clm_addr2,
CLM_CITY: partsOrder.job.clm_city,
CLM_ST: partsOrder.job.clm_st,
CLM_ZIP: partsOrder.job.clm_zip,
CLM_CTRY: partsOrder.job.clm_ctry,
CLM_PH1: partsOrder.job.clm_ph1,
CLM_PH1X: partsOrder.job.clm_ph1x,
CLM_PH2: partsOrder.job.clm_ph2,
CLM_PH2X: partsOrder.job.clm_ph2x,
CLM_FAX: partsOrder.job.clm_fax,
CLM_FAXX: partsOrder.job.clm_faxx,
CLM_CT_LN: partsOrder.job.clm_ct_ln,
CLM_CT_FN: partsOrder.job.clm_ct_fn,
CLM_TITLE: partsOrder.job.clm_title,
CLM_CT_PH: partsOrder.job.clm_ct_ph,
CLM_CT_PHX: partsOrder.job.clm_ct_phx,
CLM_EA: partsOrder.job.clm_ea,
PAYEE_NMS: partsOrder.job.payee_nms,
PAY_TYPE: partsOrder.job.pay_type,
PAY_DATE: partsOrder.job.pay_date,
PAY_CHKNM: null, // Explicitly set to null as in original code
PAY_AMT: null, // Explicitly set to null as in original code
PAY_MEMO: partsOrder.job.pay_memo,
AGT_CO_ID: partsOrder.job.agt_co_id,
AGT_CO_NM: partsOrder.job.agt_co_nm,
AGT_ADDR1: partsOrder.job.agt_addr1,
AGT_ADDR2: partsOrder.job.agt_addr2,
AGT_CITY: partsOrder.job.agt_city,
AGT_ST: partsOrder.job.agt_st,
AGT_ZIP: partsOrder.job.agt_zip,
AGT_CTRY: partsOrder.job.agt_ctry,
AGT_PH1: partsOrder.job.agt_ph1,
AGT_PH1X: partsOrder.job.agt_ph1x,
AGT_PH2: partsOrder.job.agt_ph2,
AGT_PH2X: partsOrder.job.agt_ph2x,
AGT_FAX: partsOrder.job.agt_fax,
AGT_FAXX: partsOrder.job.agt_faxx,
AGT_CT_LN: partsOrder.job.agt_ct_ln,
AGT_CT_FN: partsOrder.job.agt_ct_fn,
AGT_CT_PH: partsOrder.job.agt_ct_ph,
AGT_CT_PHX: partsOrder.job.agt_ct_phx,
AGT_EA: partsOrder.job.agt_ea,
AGT_LIC_NO: partsOrder.job.agt_lic_no,
LOSS_DATE: partsOrder.job.loss_date
? new Date(partsOrder.job.loss_date)
: null,
LOSS_CAT: null, // Explicitly set to null as in original code
LOSS_TYPE: null, // Explicitly set to null as in original code
LOSS_DESC: partsOrder.job.loss_desc,
THEFT_IND: null, // Explicitly set to null as in original code
CAT_NO: partsOrder.job.cat_no,
TLOS_IND: null, // Explicitly set to null as in original code
LOSS_MEMO: partsOrder.job.loss_memo,
CUST_PR: partsOrder.job.cust_pr,
INSD_LN: partsOrder.job.insd_ln,
INSD_FN: partsOrder.job.insd_fn,
INSD_TITLE: partsOrder.job.insd_title,
INSD_CO_NM: partsOrder.job.insd_co_nm,
INSD_ADDR1: partsOrder.job.insd_addr1,
INSD_ADDR2: partsOrder.job.insd_addr2,
INSD_CITY: partsOrder.job.insd_city,
INSD_ST: partsOrder.job.insd_st,
INSD_ZIP: partsOrder.job.insd_zip,
INSD_CTRY: partsOrder.job.insd_ctry,
INSD_PH1: partsOrder.job.insd_ph1,
INSD_PH1X: partsOrder.job.insd_ph1x,
INSD_PH2: partsOrder.job.insd_ph2,
INSD_PH2X: partsOrder.job.insd_ph2x,
INSD_FAX: partsOrder.job.insd_fax,
INSD_FAXX: partsOrder.job.insd_faxx,
INSD_EA: partsOrder.job.insd_ea,
OWNR_LN: partsOrder.job.ownr_ln,
OWNR_FN: partsOrder.job.ownr_fn,
OWNR_TITLE: partsOrder.job.ownr_title,
OWNR_CO_NM: partsOrder.job.ownr_co_nm,
OWNR_ADDR1: partsOrder.job.ownr_addr1,
OWNR_ADDR2: partsOrder.job.ownr_addr2,
OWNR_CITY: partsOrder.job.ownr_city,
OWNR_ST: partsOrder.job.ownr_st,
OWNR_ZIP: partsOrder.job.ownr_zip,
OWNR_CTRY: partsOrder.job.ownr_ctry,
OWNR_PH1: partsOrder.job.ownr_ph1,
OWNR_PH1X: partsOrder.job.ownr_ph1x,
OWNR_PH2: partsOrder.job.ownr_ph2,
OWNR_PH2X: partsOrder.job.ownr_ph2x,
OWNR_FAX: partsOrder.job.ownr_fax,
OWNR_FAXX: partsOrder.job.ownr_faxx,
OWNR_EA: partsOrder.job.ownr_ea,
},
];
await deleteEmsFileIfExists(
generateEmsOutFilePath(`${partsOrder.job.ciecaid}.AD1`),
);
const dbf: DBFFile = await DBFFile.create(
generateEmsOutFilePath(`${partsOrder.job.ciecaid}.AD1`),
ad1FieldLineDescriptors,
);
await dbf.appendRecords(records);
console.log(`${records.length} AD1 file records added.`);
return true;
} catch (error) {
console.error("Error generating AD1 file:", errorTypeCheck(error));
return false;
}
};
export default EmsPartsOrderGenerateAd1File;

View File

@@ -1,67 +0,0 @@
import { DBFFile } from "dbffile";
import errorTypeCheck from "../../util/errorTypeCheck";
import { ad2FieldLineDescriptors } from "../util/ems-interface/fielddescriptors/ad2-field-descriptors";
import {
deleteEmsFileIfExists,
generateEmsOutFilePath,
} from "../util/ems-util";
import { EmsPartsOrder } from "./ems-parts-order-interfaces";
const EmsPartsOrderGenerateAd2File = async (
partsOrder: EmsPartsOrder,
): Promise<boolean> => {
try {
const records = [
{
EST_CO_NM: partsOrder.job.est_co_nm,
EST_ADDR1: partsOrder.job.est_addr1,
EST_ADDR2: partsOrder.job.est_addr2,
EST_CITY: partsOrder.job.est_city,
EST_ST: partsOrder.job.est_st,
EST_ZIP: partsOrder.job.est_zip,
EST_CTRY: partsOrder.job.est_ctry,
EST_PH1: partsOrder.job.est_ph1,
EST_CT_LN: partsOrder.job.est_ct_ln,
EST_CT_FN: partsOrder.job.est_ct_fn,
EST_EA: partsOrder.job.est_ea,
CLMT_ADDR1: partsOrder.job.clm_addr1,
CLMT_ADDR2: partsOrder.job.clm_addr2,
CLMT_CITY: partsOrder.job.clm_city,
CLMT_ST: partsOrder.job.clm_st,
CLMT_ZIP: partsOrder.job.clm_zip,
CLMT_CTRY: partsOrder.job.clm_ctry,
CLMT_PH1: partsOrder.job.clm_ph1,
CLMT_PH1X: partsOrder.job.clm_ph1x,
CLMT_PH2: partsOrder.job.clm_ph2,
CLMT_PH2X: partsOrder.job.clm_ph2x,
CLMT_FAX: partsOrder.job.clm_fax,
CLMT_FAXX: partsOrder.job.clm_faxx,
CLMT_LN: partsOrder.job.clm_ct_ln,
CLMT_FN: partsOrder.job.clm_ct_fn,
CLMT_TITLE: partsOrder.job.clm_title,
CLMT_CT_PH: partsOrder.job.clm_ct_ph,
CLMT_CT_PHX: partsOrder.job.clm_ct_phx,
CLMT_EA: partsOrder.job.clm_ea,
RF_CO_NM: partsOrder.job.bodyshop.shopname,
},
];
await deleteEmsFileIfExists(
generateEmsOutFilePath(`${partsOrder.job.ciecaid}.AD2`),
);
const dbf: DBFFile = await DBFFile.create(
generateEmsOutFilePath(`${partsOrder.job.ciecaid}.AD2`),
ad2FieldLineDescriptors,
);
await dbf.appendRecords(records);
console.log(`${records.length} AD2 file records added.`);
return true;
} catch (error) {
console.error("Error generating AD2 file:", errorTypeCheck(error));
return false;
}
};
export default EmsPartsOrderGenerateAd2File;

View File

@@ -1,80 +0,0 @@
import { DBFFile } from "dbffile";
import errorTypeCheck from "../../util/errorTypeCheck";
import { envFieldLineDescriptors } from "../util/ems-interface/fielddescriptors/env-field-descriptor";
import {
deleteEmsFileIfExists,
generateEmsOutFilePath,
} from "../util/ems-util";
import { EmsPartsOrder } from "./ems-parts-order-interfaces";
const EmsPartsOrderGenerateEnvFile = async (
partsOrder: EmsPartsOrder,
): Promise<boolean> => {
try {
const dateNow = new Date();
const formatTime = (date: Date): string =>
`${date.getHours().toString().padStart(2, "0")}${date.getMinutes().toString().padStart(2, "0")}${date.getSeconds().toString().padStart(2, "0")}`;
const {
job: { ro_number, ciecaid },
} = partsOrder;
// Find the highest line_ind value
const lineInds = partsOrder.parts_order_lines.map(
(line) => line.jobline.line_ind,
);
const getNumber = (str: string): number => {
const match = str.match(/(\d+)$/);
return match ? parseInt(match[1], 10) : 0;
};
const highestLineInd = lineInds.reduce(
(max, current) => (getNumber(current) > getNumber(max) ? current : max),
lineInds[0] || "",
);
const records = [
{
EST_SYSTEM: "M",
SW_VERSION: "25.3",
DB_VERSION: "OCT_25_V",
DB_DATE: dateNow,
RO_ID: ro_number,
ESTFILE_ID: ciecaid,
SUPP_NO: highestLineInd ? getNumber(highestLineInd).toString() : "1",
EST_CTRY: "CAN",
TOP_SECRET: "00000000-0000-0000-0000-000000000000",
TRANS_TYPE: highestLineInd ? highestLineInd.charAt(0) : "S",
STATUS: false,
CREATE_DT: dateNow,
CREATE_TM: formatTime(dateNow),
TRANSMT_DT: dateNow,
TRANSMT_TM: formatTime(dateNow),
INCL_ADMIN: true,
INCL_VEH: true,
INCL_EST: true,
INCL_PROFL: false,
INCL_TOTAL: false,
INCL_VENDR: false,
EMS_VER: "2.0",
},
];
await deleteEmsFileIfExists(
generateEmsOutFilePath(`${partsOrder.job.ciecaid}.ENV`),
);
const dbf: DBFFile = await DBFFile.create(
generateEmsOutFilePath(`${partsOrder.job.ciecaid}.ENV`),
envFieldLineDescriptors,
);
await dbf.appendRecords(records);
console.log(`${records.length} ENV file records added.`);
return true;
} catch (error) {
console.error("Error generating ENV file:", errorTypeCheck(error));
return false;
}
};
export default EmsPartsOrderGenerateEnvFile;

View File

@@ -1,85 +0,0 @@
import { DBFFile } from "dbffile";
import errorTypeCheck from "../../util/errorTypeCheck";
import {
deleteEmsFileIfExists,
generateEmsOutFilePath,
} from "../util/ems-util";
import { EmsPartsOrder } from "./ems-parts-order-interfaces";
import { linFieldLineDescriptors } from "../util/ems-interface/fielddescriptors/lin-field-descriptors";
const EmsPartsOrderGenerateLinFile = async (
partsOrder: EmsPartsOrder,
): Promise<boolean> => {
try {
const records = partsOrder.parts_order_lines.map((partsOrderLine) => ({
LINE_NO: partsOrderLine.jobline?.line_no,
LINE_IND: partsOrderLine.jobline?.line_ind,
LINE_REF: partsOrderLine.jobline?.line_ref,
TRAN_CODE: partsOrderLine.jobline?.tran_code ?? "1",
DB_REF: partsOrderLine.jobline?.db_ref,
UNQ_SEQ: partsOrderLine.jobline?.unq_seq,
PART_DES_J: false,
LINE_DESC: partsOrderLine.jobline?.line_desc,
PART_TYPE:
partsOrderLine.priceChange === true
? partsOrderLine.part_type
: partsOrderLine.jobline?.part_type,
GLASS_FLAG: partsOrderLine.jobline?.glass_flag,
OEM_PARTNO: partsOrderLine.jobline?.oem_partno,
PRICE_INC: partsOrderLine.jobline?.price_inc,
ALT_PART_I: partsOrderLine.jobline?.alt_part_i,
TAX_PART: partsOrderLine.jobline?.tax_part,
DB_PRICE: partsOrderLine.jobline?.db_price,
ACT_PRICE:
partsOrderLine.priceChange === true
? partsOrderLine.act_price
: partsOrderLine.jobline?.act_price,
PRICE_J: partsOrderLine.jobline?.price_j,
CERT_PART: partsOrderLine.jobline?.cert_part,
PART_QTY: partsOrderLine.jobline?.part_qty,
ALT_CO_ID: partsOrderLine.jobline?.alt_co_id,
ALT_PARTNO: partsOrderLine.jobline?.alt_partno,
ALT_OVERRD: partsOrderLine.jobline?.alt_overrd,
ALT_PARTM: partsOrderLine.jobline?.alt_partm,
PRT_DSMK_P: partsOrderLine.jobline?.prt_dsmk_p,
PRT_DSMK_M: partsOrderLine.jobline?.prt_dsmk_m,
MOD_LBR_TY: partsOrderLine.jobline?.mod_lbr_ty,
DB_HRS: partsOrderLine.jobline?.db_hrs,
MOD_LB_HRS: partsOrderLine.jobline?.mod_lb_hrs,
LBR_INC: partsOrderLine.jobline?.lbr_inc,
LBR_OP: partsOrderLine.jobline?.lbr_op,
LBR_HRS_J: partsOrderLine.jobline?.lbr_hrs_j,
LBR_TYP_J: partsOrderLine.jobline?.lbr_typ_j,
LBR_OP_J: partsOrderLine.jobline?.lbr_op_j,
PAINT_STG: partsOrderLine.jobline?.paint_stg,
PAINT_TONE: partsOrderLine.jobline?.paint_tone,
LBR_TAX: partsOrderLine.jobline?.lbr_tax,
LBR_AMT: partsOrderLine.jobline?.lbr_amt,
MISC_AMT: partsOrderLine.jobline?.misc_amt,
MISC_SUBLT: partsOrderLine.jobline?.misc_sublt,
MISC_TAX: partsOrderLine.jobline?.misc_tax,
BETT_TYPE: partsOrderLine.jobline?.bett_type,
BETT_PCTG: partsOrderLine.jobline?.bett_pctg,
BETT_AMT: partsOrderLine.jobline?.bett_amt,
BETT_TAX: partsOrderLine.jobline?.bett_tax,
}));
await deleteEmsFileIfExists(
generateEmsOutFilePath(`${partsOrder.job.ciecaid}.LIN`),
);
const dbf: DBFFile = await DBFFile.create(
generateEmsOutFilePath(`${partsOrder.job.ciecaid}.LIN`),
linFieldLineDescriptors,
);
await dbf.appendRecords(records);
console.log(`${records.length} LIN file records added.`);
return true;
} catch (error) {
console.error("Error generating LIN file:", errorTypeCheck(error));
return false;
}
};
export default EmsPartsOrderGenerateLinFile;

View File

@@ -1,59 +0,0 @@
import { DBFFile } from "dbffile";
import errorTypeCheck from "../../util/errorTypeCheck";
import {
deleteEmsFileIfExists,
generateEmsOutFilePath,
} from "../util/ems-util";
import { EmsPartsOrder } from "./ems-parts-order-interfaces";
import { pfhFieldLineDescriptors } from "../util/ems-interface/fielddescriptors/pfh-field-descriptors";
const EmsPartsOrderGeneratePfhFile = async (
partsOrder: EmsPartsOrder,
): Promise<boolean> => {
try {
const records = [
{
ID_PRO_NAM: "REPAIR FACILITY", // Job.id_pro_nam?.Value
TAX_PRETHR: (partsOrder.job.tax_prethr || 0) * 100,
TAX_THRAMT: (partsOrder.job.tax_thramt || 0) * 100,
TAX_PSTTHR: (partsOrder.job.tax_pstthr || 0) * 100,
TAX_TOW_IN: true, // Job.tax_tow_in?.Value
TAX_TOW_RT: (partsOrder.job.tax_tow_rt || 0) * 100,
TAX_STR_IN: true, // Job.tax_str_in?.Value
TAX_STR_RT: (partsOrder.job.tax_str_rt || 0) * 100,
TAX_SUB_IN: true, // Job.tax_sub_in?.Value
TAX_SUB_RT: (partsOrder.job.tax_sub_rt || 0) * 100,
TAX_BTR_IN: true, // Job.tax_btr_in?.Value
TAX_LBR_RT:
(partsOrder.job.bodyshop?.bill_tax_rates?.state_tax_rate || 0) * 100,
TAX_GST_RT:
(partsOrder.job.bodyshop?.bill_tax_rates?.federal_tax_rate || 0) *
100,
TAX_GST_IN: true, // Job.tax_gst_in?.Value
ADJ_G_DISC: (partsOrder.job.adj_g_disc || 0) * 100,
ADJ_TOWDIS: (partsOrder.job.adj_towdis || 0) * 100,
ADJ_STRDIS: (partsOrder.job.adj_strdis || 0) * 100,
ADJ_BTR_IN: null, // Job.adj_btr_in?.Value
TAX_PREDIS: (partsOrder.job.tax_predis || 0) * 100,
},
];
await deleteEmsFileIfExists(
generateEmsOutFilePath(`${partsOrder.job.ciecaid}.PFH`),
);
const dbf: DBFFile = await DBFFile.create(
generateEmsOutFilePath(`${partsOrder.job.ciecaid}.PFH`),
pfhFieldLineDescriptors,
);
await dbf.appendRecords(records);
console.log(`${records.length} PFH file records added.`);
return true;
} catch (error) {
console.error("Error generating PFH file:", errorTypeCheck(error));
return false;
}
};
export default EmsPartsOrderGeneratePfhFile;

View File

@@ -1,302 +0,0 @@
import { DBFFile } from "dbffile";
import errorTypeCheck from "../../util/errorTypeCheck";
import { DecodedPflLine } from "../decoder/decode-pfl.interface";
import { pflFieldLineDescriptors } from "../util/ems-interface/fielddescriptors/pfl-field-descriptors";
import {
deleteEmsFileIfExists,
generateEmsOutFilePath,
} from "../util/ems-util";
import uppercaseObjectKeys from "../util/uppercaseObjectKeys";
import { EmsPartsOrder } from "./ems-parts-order-interfaces";
import _ from "lodash";
const EmsPartsOrderGeneratePflFile = async (
partsOrder: EmsPartsOrder,
): Promise<boolean> => {
try {
let records;
if (partsOrder.job.cieca_pfl && !_.isEmpty(partsOrder.job.cieca_pfl)) {
records = Object.keys(partsOrder.job.cieca_pfl).map((key) => {
const record: DecodedPflLine = partsOrder.job.cieca_pfl[key];
return uppercaseObjectKeys(record);
});
} else {
//We don't have the PFL data for an old job, so make it manually.
records = [
{
LBR_TYPE: "LAA",
LBR_DESC: "",
LBR_RATE: partsOrder.job.rate_laa,
LBR_TAX_IN: true,
LBR_TAXP: null, // Job.bodyshop.bill_tax_rates.state_tax_rate?.Value ?? 0,
LBR_ADJP: 0,
LBR_TX_TY1: null, //partsOrder.job.lbr_tx_ty1,
LBR_TX_IN1: null, //partsOrder.job.lbr_tx_in1,
LBR_TX_TY2: null, //partsOrder.job.lbr_tx_ty2,
LBR_TX_IN2: null, //partsOrder.job.lbr_tx_in2,
LBR_TX_TY3: null, //partsOrder.job.lbr_tx_ty3,
LBR_TX_IN3: null, //partsOrder.job.lbr_tx_in3,
LBR_TX_TY4: null, //partsOrder.job.lbr_tx_ty4,
LBR_TX_IN4: null, //partsOrder.job.lbr_tx_in4,
LBR_TX_TY5: null, //partsOrder.job.lbr_tx_ty5,
LBR_TX_IN5: null, //partsOrder.job.lbr_tx_in5,
},
{
LBR_TYPE: "LAB",
LBR_DESC: "",
LBR_RATE: partsOrder.job.rate_lab,
LBR_TAX_IN: true,
LBR_TAXP: null, // Job.bodyshop.bill_tax_rates.state_tax_rate?.Value ?? 0,
LBR_ADJP: 0,
LBR_TX_TY1: null, //partsOrder.job.lbr_tx_ty1,
LBR_TX_IN1: null, //partsOrder.job.lbr_tx_in1,
LBR_TX_TY2: null, //partsOrder.job.lbr_tx_ty2,
LBR_TX_IN2: null, //partsOrder.job.lbr_tx_in2,
LBR_TX_TY3: null, //partsOrder.job.lbr_tx_ty3,
LBR_TX_IN3: null, //partsOrder.job.lbr_tx_in3,
LBR_TX_TY4: null, //partsOrder.job.lbr_tx_ty4,
LBR_TX_IN4: null, //partsOrder.job.lbr_tx_in4,
LBR_TX_TY5: null, //partsOrder.job.lbr_tx_ty5,
LBR_TX_IN5: null, //partsOrder.job.lbr_tx_in5,
},
{
LBR_TYPE: "LAD",
LBR_DESC: "",
LBR_RATE: partsOrder.job.rate_lad,
LBR_TAX_IN: true,
LBR_TAXP: null, // Job.bodyshop.bill_tax_rates.state_tax_rate?.Value ?? 0,
LBR_ADJP: 0,
LBR_TX_TY1: null, //partsOrder.job.lbr_tx_ty1,
LBR_TX_IN1: null, //partsOrder.job.lbr_tx_in1,
LBR_TX_TY2: null, //partsOrder.job.lbr_tx_ty2,
LBR_TX_IN2: null, //partsOrder.job.lbr_tx_in2,
LBR_TX_TY3: null, //partsOrder.job.lbr_tx_ty3,
LBR_TX_IN3: null, //partsOrder.job.lbr_tx_in3,
LBR_TX_TY4: null, //partsOrder.job.lbr_tx_ty4,
LBR_TX_IN4: null, //partsOrder.job.lbr_tx_in4,
LBR_TX_TY5: null, //partsOrder.job.lbr_tx_ty5,
LBR_TX_IN5: null, //partsOrder.job.lbr_tx_in5,
},
{
LBR_TYPE: "LAE",
LBR_DESC: "",
LBR_RATE: partsOrder.job.rate_lae,
LBR_TAX_IN: true,
LBR_TAXP: null, // Job.bodyshop.bill_tax_rates.state_tax_rate?.Value ?? 0,
LBR_ADJP: 0,
LBR_TX_TY1: null, //partsOrder.job.lbr_tx_ty1,
LBR_TX_IN1: null, //partsOrder.job.lbr_tx_in1,
LBR_TX_TY2: null, //partsOrder.job.lbr_tx_ty2,
LBR_TX_IN2: null, //partsOrder.job.lbr_tx_in2,
LBR_TX_TY3: null, //partsOrder.job.lbr_tx_ty3,
LBR_TX_IN3: null, //partsOrder.job.lbr_tx_in3,
LBR_TX_TY4: null, //partsOrder.job.lbr_tx_ty4,
LBR_TX_IN4: null, //partsOrder.job.lbr_tx_in4,
LBR_TX_TY5: null, //partsOrder.job.lbr_tx_ty5,
LBR_TX_IN5: null, //partsOrder.job.lbr_tx_in5,
},
{
LBR_TYPE: "LAF",
LBR_DESC: "",
LBR_RATE: partsOrder.job.rate_laf,
LBR_TAX_IN: true,
LBR_TAXP: null, // Job.bodyshop.bill_tax_rates.state_tax_rate?.Value ?? 0,
LBR_ADJP: 0,
LBR_TX_TY1: null, //partsOrder.job.lbr_tx_ty1,
LBR_TX_IN1: null, //partsOrder.job.lbr_tx_in1,
LBR_TX_TY2: null, //partsOrder.job.lbr_tx_ty2,
LBR_TX_IN2: null, //partsOrder.job.lbr_tx_in2,
LBR_TX_TY3: null, //partsOrder.job.lbr_tx_ty3,
LBR_TX_IN3: null, //partsOrder.job.lbr_tx_in3,
LBR_TX_TY4: null, //partsOrder.job.lbr_tx_ty4,
LBR_TX_IN4: null, //partsOrder.job.lbr_tx_in4,
LBR_TX_TY5: null, //partsOrder.job.lbr_tx_ty5,
LBR_TX_IN5: null, //partsOrder.job.lbr_tx_in5,
},
{
LBR_TYPE: "LAG",
LBR_DESC: "",
LBR_RATE: partsOrder.job.rate_lag,
LBR_TAX_IN: true,
LBR_TAXP: null, // Job.bodyshop.bill_tax_rates.state_tax_rate?.Value ?? 0,
LBR_ADJP: 0,
LBR_TX_TY1: null, //partsOrder.job.lbr_tx_ty1,
LBR_TX_IN1: null, //partsOrder.job.lbr_tx_in1,
LBR_TX_TY2: null, //partsOrder.job.lbr_tx_ty2,
LBR_TX_IN2: null, //partsOrder.job.lbr_tx_in2,
LBR_TX_TY3: null, //partsOrder.job.lbr_tx_ty3,
LBR_TX_IN3: null, //partsOrder.job.lbr_tx_in3,
LBR_TX_TY4: null, //partsOrder.job.lbr_tx_ty4,
LBR_TX_IN4: null, //partsOrder.job.lbr_tx_in4,
LBR_TX_TY5: null, //partsOrder.job.lbr_tx_ty5,
LBR_TX_IN5: null, //partsOrder.job.lbr_tx_in5,
},
{
LBR_TYPE: "LAM",
LBR_DESC: "",
LBR_RATE: partsOrder.job.rate_lam,
LBR_TAX_IN: true,
LBR_TAXP: null, // Job.bodyshop.bill_tax_rates.state_tax_rate?.Value ?? 0,
LBR_ADJP: 0,
LBR_TX_TY1: null, //partsOrder.job.lbr_tx_ty1,
LBR_TX_IN1: null, //partsOrder.job.lbr_tx_in1,
LBR_TX_TY2: null, //partsOrder.job.lbr_tx_ty2,
LBR_TX_IN2: null, //partsOrder.job.lbr_tx_in2,
LBR_TX_TY3: null, //partsOrder.job.lbr_tx_ty3,
LBR_TX_IN3: null, //partsOrder.job.lbr_tx_in3,
LBR_TX_TY4: null, //partsOrder.job.lbr_tx_ty4,
LBR_TX_IN4: null, //partsOrder.job.lbr_tx_in4,
LBR_TX_TY5: null, //partsOrder.job.lbr_tx_ty5,
LBR_TX_IN5: null, //partsOrder.job.lbr_tx_in5,
},
{
LBR_TYPE: "LAR",
LBR_DESC: "",
LBR_RATE: partsOrder.job.rate_lar,
LBR_TAX_IN: true,
LBR_TAXP: null, // Job.bodyshop.bill_tax_rates.state_tax_rate?.Value ?? 0,
LBR_ADJP: 0,
LBR_TX_TY1: null, //partsOrder.job.lbr_tx_ty1,
LBR_TX_IN1: null, //partsOrder.job.lbr_tx_in1,
LBR_TX_TY2: null, //partsOrder.job.lbr_tx_ty2,
LBR_TX_IN2: null, //partsOrder.job.lbr_tx_in2,
LBR_TX_TY3: null, //partsOrder.job.lbr_tx_ty3,
LBR_TX_IN3: null, //partsOrder.job.lbr_tx_in3,
LBR_TX_TY4: null, //partsOrder.job.lbr_tx_ty4,
LBR_TX_IN4: null, //partsOrder.job.lbr_tx_in4,
LBR_TX_TY5: null, //partsOrder.job.lbr_tx_ty5,
LBR_TX_IN5: null, //partsOrder.job.lbr_tx_in5,
},
{
LBR_TYPE: "LAS",
LBR_DESC: "",
LBR_RATE: partsOrder.job.rate_las,
LBR_TAX_IN: true,
LBR_TAXP: null, // Job.bodyshop.bill_tax_rates.state_tax_rate?.Value ?? 0,
LBR_ADJP: 0,
LBR_TX_TY1: null, //partsOrder.job.lbr_tx_ty1,
LBR_TX_IN1: null, //partsOrder.job.lbr_tx_in1,
LBR_TX_TY2: null, //partsOrder.job.lbr_tx_ty2,
LBR_TX_IN2: null, //partsOrder.job.lbr_tx_in2,
LBR_TX_TY3: null, //partsOrder.job.lbr_tx_ty3,
LBR_TX_IN3: null, //partsOrder.job.lbr_tx_in3,
LBR_TX_TY4: null, //partsOrder.job.lbr_tx_ty4,
LBR_TX_IN4: null, //partsOrder.job.lbr_tx_in4,
LBR_TX_TY5: null, //partsOrder.job.lbr_tx_ty5,
LBR_TX_IN5: null, //partsOrder.job.lbr_tx_in5,
},
{
LBR_TYPE: "LAU",
LBR_DESC: "",
LBR_RATE: partsOrder.job.rate_lau,
LBR_TAX_IN: true,
LBR_TAXP: null, // Job.bodyshop.bill_tax_rates.state_tax_rate?.Value ?? 0,
LBR_ADJP: 0,
LBR_TX_TY1: null, //partsOrder.job.lbr_tx_ty1,
LBR_TX_IN1: null, //partsOrder.job.lbr_tx_in1,
LBR_TX_TY2: null, //partsOrder.job.lbr_tx_ty2,
LBR_TX_IN2: null, //partsOrder.job.lbr_tx_in2,
LBR_TX_TY3: null, //partsOrder.job.lbr_tx_ty3,
LBR_TX_IN3: null, //partsOrder.job.lbr_tx_in3,
LBR_TX_TY4: null, //partsOrder.job.lbr_tx_ty4,
LBR_TX_IN4: null, //partsOrder.job.lbr_tx_in4,
LBR_TX_TY5: null, //partsOrder.job.lbr_tx_ty5,
LBR_TX_IN5: null, //partsOrder.job.lbr_tx_in5,
},
{
LBR_TYPE: "LA1",
LBR_DESC: "",
LBR_RATE: partsOrder.job.rate_la1,
LBR_TAX_IN: true,
LBR_TAXP: null, // Job.bodyshop.bill_tax_rates.state_tax_rate?.Value ?? 0,
LBR_ADJP: 0,
LBR_TX_TY1: null, //partsOrder.job.lbr_tx_ty1,
LBR_TX_IN1: null, //partsOrder.job.lbr_tx_in1,
LBR_TX_TY2: null, //partsOrder.job.lbr_tx_ty2,
LBR_TX_IN2: null, //partsOrder.job.lbr_tx_in2,
LBR_TX_TY3: null, //partsOrder.job.lbr_tx_ty3,
LBR_TX_IN3: null, //partsOrder.job.lbr_tx_in3,
LBR_TX_TY4: null, //partsOrder.job.lbr_tx_ty4,
LBR_TX_IN4: null, //partsOrder.job.lbr_tx_in4,
LBR_TX_TY5: null, //partsOrder.job.lbr_tx_ty5,
LBR_TX_IN5: null, //partsOrder.job.lbr_tx_in5,
},
{
LBR_TYPE: "LA2",
LBR_DESC: "",
LBR_RATE: partsOrder.job.rate_la2,
LBR_TAX_IN: true,
LBR_TAXP: null, // Job.bodyshop.bill_tax_rates.state_tax_rate?.Value ?? 0,
LBR_ADJP: 0,
LBR_TX_TY1: null, //partsOrder.job.lbr_tx_ty1,
LBR_TX_IN1: null, //partsOrder.job.lbr_tx_in1,
LBR_TX_TY2: null, //partsOrder.job.lbr_tx_ty2,
LBR_TX_IN2: null, //partsOrder.job.lbr_tx_in2,
LBR_TX_TY3: null, //partsOrder.job.lbr_tx_ty3,
LBR_TX_IN3: null, //partsOrder.job.lbr_tx_in3,
LBR_TX_TY4: null, //partsOrder.job.lbr_tx_ty4,
LBR_TX_IN4: null, //partsOrder.job.lbr_tx_in4,
LBR_TX_TY5: null, //partsOrder.job.lbr_tx_ty5,
LBR_TX_IN5: null, //partsOrder.job.lbr_tx_in5,
},
{
LBR_TYPE: "LA3",
LBR_DESC: "",
LBR_RATE: partsOrder.job.rate_la3,
LBR_TAX_IN: true,
LBR_TAXP: null, // Job.bodyshop.bill_tax_rates.state_tax_rate?.Value ?? 0,
LBR_ADJP: 0,
LBR_TX_TY1: null, //partsOrder.job.lbr_tx_ty1,
LBR_TX_IN1: null, //partsOrder.job.lbr_tx_in1,
LBR_TX_TY2: null, //partsOrder.job.lbr_tx_ty2,
LBR_TX_IN2: null, //partsOrder.job.lbr_tx_in2,
LBR_TX_TY3: null, //partsOrder.job.lbr_tx_ty3,
LBR_TX_IN3: null, //partsOrder.job.lbr_tx_in3,
LBR_TX_TY4: null, //partsOrder.job.lbr_tx_ty4,
LBR_TX_IN4: null, //partsOrder.job.lbr_tx_in4,
LBR_TX_TY5: null, //partsOrder.job.lbr_tx_ty5,
LBR_TX_IN5: null, //partsOrder.job.lbr_tx_in5,
},
{
LBR_TYPE: "LA4",
LBR_DESC: "",
LBR_RATE: partsOrder.job.rate_la4,
LBR_TAX_IN: true,
LBR_TAXP: null, // Job.bodyshop.bill_tax_rates.state_tax_rate?.Value ?? 0,
LBR_ADJP: 0,
LBR_TX_TY1: null, //partsOrder.job.lbr_tx_ty1,
LBR_TX_IN1: null, //partsOrder.job.lbr_tx_in1,
LBR_TX_TY2: null, //partsOrder.job.lbr_tx_ty2,
LBR_TX_IN2: null, //partsOrder.job.lbr_tx_in2,
LBR_TX_TY3: null, //partsOrder.job.lbr_tx_ty3,
LBR_TX_IN3: null, //partsOrder.job.lbr_tx_in3,
LBR_TX_TY4: null, //partsOrder.job.lbr_tx_ty4,
LBR_TX_IN4: null, //partsOrder.job.lbr_tx_in4,
LBR_TX_TY5: null, //partsOrder.job.lbr_tx_ty5,
LBR_TX_IN5: null, //partsOrder.job.lbr_tx_in5,
},
];
}
await deleteEmsFileIfExists(
generateEmsOutFilePath(`${partsOrder.job.ciecaid}.PFL`),
);
const dbf: DBFFile = await DBFFile.create(
generateEmsOutFilePath(`${partsOrder.job.ciecaid}.PFL`),
pflFieldLineDescriptors,
);
await dbf.appendRecords(records);
console.log(`${records.length} PFL file records added.`);
return true;
} catch (error) {
console.error("Error generating PFL file:", errorTypeCheck(error));
return false;
}
};
export default EmsPartsOrderGeneratePflFile;

View File

@@ -1,105 +0,0 @@
import { DBFFile } from "dbffile";
import _ from "lodash";
import errorTypeCheck from "../../util/errorTypeCheck";
import { DecodedPfmLine } from "../decoder/decode-pfm.interface";
import { pfmFieldLineDescriptors } from "../util/ems-interface/fielddescriptors/pfm-field-descriptors";
import {
deleteEmsFileIfExists,
generateEmsOutFilePath,
} from "../util/ems-util";
import uppercaseObjectKeys from "../util/uppercaseObjectKeys";
import { EmsPartsOrder } from "./ems-parts-order-interfaces";
const EmsPartsOrderGeneratePfmFile = async (
partsOrder: EmsPartsOrder,
): Promise<boolean> => {
try {
let records;
if (partsOrder.job.materials && !_.isEmpty(partsOrder.job.materials)) {
records = Object.keys(partsOrder.job.materials).map((key) => {
const record: DecodedPfmLine = partsOrder.job.materials[key];
return uppercaseObjectKeys(record);
});
} else {
//Older records may not have materials, especially for ImEX.
records = [
{
MATL_TYPE: "MAPA",
CAL_CODE: null,
CAL_DESC: null,
CAL_MAXDLR: 0,
CAL_PRIP: 0,
CAL_SECP: 0,
MAT_CALP: 0,
CAL_PRETHR: 0,
CAL_PSTTHR: 0,
CAL_THRAMT: 0,
CAL_LBRMIN: 0,
CAL_LBRMAX: 0,
CAL_LBRRTE: partsOrder.job.rate_mapa,
CAL_OPCODE: null,
TAX_IND: true,
MAT_TAXP: null,
MAT_ADJP: null,
MAT_TX_TY1: null,
MAT_TX_IN1: null,
MAT_TX_TY2: null,
MAT_TX_IN2: null,
MAT_TX_TY3: null,
MAT_TX_IN3: null,
MAT_TX_TY4: null,
MAT_TX_IN4: null,
MAT_TX_TY5: null,
MAT_TX_IN5: null,
},
{
MATL_TYPE: "MASH",
CAL_CODE: null,
CAL_DESC: null,
CAL_MAXDLR: 0,
CAL_PRIP: 0,
CAL_SECP: 0,
MAT_CALP: 0,
CAL_PRETHR: 0,
CAL_PSTTHR: 0,
CAL_THRAMT: 0,
CAL_LBRMIN: 0,
CAL_LBRMAX: 0,
CAL_LBRRTE: partsOrder.job.rate_mash,
CAL_OPCODE: null,
TAX_IND: true,
MAT_TAXP: null,
MAT_ADJP: null,
MAT_TX_TY1: null,
MAT_TX_IN1: null,
MAT_TX_TY2: null,
MAT_TX_IN2: null,
MAT_TX_TY3: null,
MAT_TX_IN3: null,
MAT_TX_TY4: null,
MAT_TX_IN4: null,
MAT_TX_TY5: null,
MAT_TX_IN5: null,
},
];
}
await deleteEmsFileIfExists(
generateEmsOutFilePath(`${partsOrder.job.ciecaid}.PFM`),
);
const dbf: DBFFile = await DBFFile.create(
generateEmsOutFilePath(`${partsOrder.job.ciecaid}.PFM`),
pfmFieldLineDescriptors,
);
await dbf.appendRecords(records);
console.log(`${records.length} PFM file records added.`);
return true;
} catch (error) {
console.error("Error generating PFM file:", errorTypeCheck(error));
return false;
}
};
export default EmsPartsOrderGeneratePfmFile;

View File

@@ -1,34 +0,0 @@
import { DBFFile } from "dbffile";
import errorTypeCheck from "../../util/errorTypeCheck";
import { pfoFieldLineDescriptors } from "../util/ems-interface/fielddescriptors/pfo-field-descriptors";
import {
deleteEmsFileIfExists,
generateEmsOutFilePath,
} from "../util/ems-util";
import { EmsPartsOrder } from "./ems-parts-order-interfaces";
const EmsPartsOrderGeneratePfoFile = async (
partsOrder: EmsPartsOrder,
): Promise<boolean> => {
try {
const records = []; //This was kept blank previously as well.
await deleteEmsFileIfExists(
generateEmsOutFilePath(`${partsOrder.job.ciecaid}.PFO`),
);
const dbf: DBFFile = await DBFFile.create(
generateEmsOutFilePath(`${partsOrder.job.ciecaid}.PFO`),
pfoFieldLineDescriptors,
);
await dbf.appendRecords(records);
console.log(`${records.length} PFO file records added.`);
return true;
} catch (error) {
console.error("Error generating PFO file:", errorTypeCheck(error));
return false;
}
};
export default EmsPartsOrderGeneratePfoFile;

View File

@@ -1,39 +0,0 @@
import { DBFFile } from "dbffile";
import errorTypeCheck from "../../util/errorTypeCheck";
import { DecodedPfpLine } from "../decoder/decode-pfp.interface";
import { pfpFieldLineDescriptors } from "../util/ems-interface/fielddescriptors/pfp-field-descriptors";
import {
deleteEmsFileIfExists,
generateEmsOutFilePath,
} from "../util/ems-util";
import uppercaseObjectKeys from "../util/uppercaseObjectKeys";
import { EmsPartsOrder } from "./ems-parts-order-interfaces";
const EmsPartsOrderGeneratePfpFile = async (
partsOrder: EmsPartsOrder,
): Promise<boolean> => {
try {
const records = Object.keys(partsOrder.job.parts_tax_rates).map((key) => {
const record: DecodedPfpLine = partsOrder.job.parts_tax_rates[key];
return uppercaseObjectKeys(record);
});
await deleteEmsFileIfExists(
generateEmsOutFilePath(`${partsOrder.job.ciecaid}.PFP`),
);
const dbf: DBFFile = await DBFFile.create(
generateEmsOutFilePath(`${partsOrder.job.ciecaid}.PFP`),
pfpFieldLineDescriptors,
);
await dbf.appendRecords(records);
console.log(`${records.length} PFP file records added.`);
return true;
} catch (error) {
console.error("Error generating PFP file:", errorTypeCheck(error));
return false;
}
};
export default EmsPartsOrderGeneratePfpFile;

View File

@@ -1,34 +0,0 @@
import { DBFFile } from "dbffile";
import errorTypeCheck from "../../util/errorTypeCheck";
import { pftFieldLineDescriptors } from "../util/ems-interface/fielddescriptors/pft-field-descriptor";
import {
deleteEmsFileIfExists,
generateEmsOutFilePath,
} from "../util/ems-util";
import { EmsPartsOrder } from "./ems-parts-order-interfaces";
const EmsPartsOrderGeneratePftFile = async (
partsOrder: EmsPartsOrder,
): Promise<boolean> => {
try {
const records = []; //Left blank intentionally as per previous code.
await deleteEmsFileIfExists(
generateEmsOutFilePath(`${partsOrder.job.ciecaid}.PFT`),
);
const dbf: DBFFile = await DBFFile.create(
generateEmsOutFilePath(`${partsOrder.job.ciecaid}.PFT`),
pftFieldLineDescriptors,
);
await dbf.appendRecords(records);
console.log(`${records.length} PFT file records added.`);
return true;
} catch (error) {
console.error("Error generating PFT file:", errorTypeCheck(error));
return false;
}
};
export default EmsPartsOrderGeneratePftFile;

View File

@@ -1,40 +0,0 @@
import { DBFFile } from "dbffile";
import errorTypeCheck from "../../util/errorTypeCheck";
import { DecodedStlLine } from "../decoder/decode-stl.interface";
import { stlFieldLineDescriptors } from "../util/ems-interface/fielddescriptors/stl-field-descriptors";
import {
deleteEmsFileIfExists,
generateEmsOutFilePath,
} from "../util/ems-util";
import uppercaseObjectKeys from "../util/uppercaseObjectKeys";
import { EmsPartsOrder } from "./ems-parts-order-interfaces";
const EmsPartsOrderGenerateStlFile = async (
partsOrder: EmsPartsOrder,
): Promise<boolean> => {
try {
//TODO: Add CIECA STL to parts order.
const records = Object.keys(partsOrder.job.cieca_stl?.data).map((key) => {
const record: DecodedStlLine = partsOrder.job.cieca_stl.data[key];
return uppercaseObjectKeys(record);
});
await deleteEmsFileIfExists(
generateEmsOutFilePath(`${partsOrder.job.ciecaid}.STL`),
);
const dbf: DBFFile = await DBFFile.create(
generateEmsOutFilePath(`${partsOrder.job.ciecaid}.STL`),
stlFieldLineDescriptors,
);
await dbf.appendRecords(records);
console.log(`${records.length} STL file records added.`);
return true;
} catch (error) {
console.error("Error generating STL file:", errorTypeCheck(error));
return false;
}
};
export default EmsPartsOrderGenerateStlFile;

View File

@@ -1,36 +0,0 @@
import { DBFFile } from "dbffile";
import errorTypeCheck from "../../util/errorTypeCheck";
import { ttlFieldLineDescriptors } from "../util/ems-interface/fielddescriptors/ttl-field-descriptors";
import {
deleteEmsFileIfExists,
generateEmsOutFilePath,
} from "../util/ems-util";
import uppercaseObjectKeys from "../util/uppercaseObjectKeys";
import { EmsPartsOrder } from "./ems-parts-order-interfaces";
const EmsPartsOrderGenerateTtlFile = async (
partsOrder: EmsPartsOrder,
): Promise<boolean> => {
try {
//TODO: Add CIECA STL to parts order.
const records = uppercaseObjectKeys(partsOrder.job.cieca_ttl?.data);
await deleteEmsFileIfExists(
generateEmsOutFilePath(`${partsOrder.job.ciecaid}.TTL`),
);
const dbf: DBFFile = await DBFFile.create(
generateEmsOutFilePath(`${partsOrder.job.ciecaid}.TTL`),
ttlFieldLineDescriptors,
);
await dbf.appendRecords([records]);
console.log(`${records.length} TTL file records added.`);
return true;
} catch (error) {
console.error("Error generating TTL file:", errorTypeCheck(error));
return false;
}
};
export default EmsPartsOrderGenerateTtlFile;

View File

@@ -1,65 +0,0 @@
import { DBFFile } from "dbffile";
import errorTypeCheck from "../../util/errorTypeCheck";
import { vehFieldLineDescriptors } from "../util/ems-interface/fielddescriptors/veh-field-descriptors";
import {
deleteEmsFileIfExists,
generateEmsOutFilePath,
} from "../util/ems-util";
import { EmsPartsOrder } from "./ems-parts-order-interfaces";
const EmsPartsOrderGenerateVehFile = async (
partsOrder: EmsPartsOrder,
): Promise<boolean> => {
try {
const records = [
{
IMPACT_1: partsOrder.job.area_of_damage?.impact1 || null,
IMPACT_2: partsOrder.job.area_of_damage?.impact2 || null,
DMG_MEMO: null,
DB_V_CODE: "",
PLATE_NO: partsOrder.job.plate_no || null,
PLATE_ST: partsOrder.job.plate_st || null,
V_VIN: partsOrder.job.v_vin || null,
V_COND: "",
V_PROD_DT: "",
V_MODEL_YR: partsOrder.job.v_model_yr || null,
V_MAKECODE: "",
V_MAKEDESC: partsOrder.job.v_make_desc || null,
V_MODEL: partsOrder.job.v_model_desc || null,
V_TYPE: partsOrder.job.vehicle?.v_type || null,
V_BSTYLE: partsOrder.job.vehicle?.v_bstyle || null,
V_TRIMCODE: partsOrder.job.vehicle?.v_trimcode || null,
TRIM_COLOR: partsOrder.job.vehicle?.trim_color || null,
V_MLDGCODE: partsOrder.job.vehicle?.v_mldgcode || null,
V_ENGINE: partsOrder.job.vehicle?.v_engine || null,
V_MILEAGE: partsOrder.job.vehicle?.v_mileage || null,
V_OPTIONS: null,
V_COLOR: partsOrder.job.vehicle?.v_color || null,
V_TONE: Number(partsOrder.job.vehicle?.v_tone) || null,
V_STAGE: null,
PAINT_CD1: partsOrder.job.vehicle?.v_paint_codes?.paint_cd1 || "",
PAINT_CD2: partsOrder.job.vehicle?.v_paint_codes?.paint_cd2 || "",
PAINT_CD3: partsOrder.job.vehicle?.v_paint_codes?.paint_cd3 || "",
V_MEMO: null,
},
];
await deleteEmsFileIfExists(
generateEmsOutFilePath(`${partsOrder.job.ciecaid}.VEH`),
);
const dbf: DBFFile = await DBFFile.create(
generateEmsOutFilePath(`${partsOrder.job.ciecaid}.VEH`),
vehFieldLineDescriptors,
);
await dbf.appendRecords(records);
console.log(`${records.length} VEH file records added.`);
return true;
} catch (error) {
console.error("Error generating VEH file:", errorTypeCheck(error));
return false;
}
};
export default EmsPartsOrderGenerateVehFile;

View File

@@ -1,83 +0,0 @@
import log from "electron-log/main";
import express from "express";
import _ from "lodash";
import errorTypeCheck from "../../util/errorTypeCheck";
import store from "../store/store";
import createdDirectoryIfNotExist from "../util/createDirectoryIfNotExist";
import EmsPartsOrderGenerateAd1File from "./ems-parts-order-generate-ad1";
import EmsPartsOrderGenerateAd2File from "./ems-parts-order-generate-ad2";
import EmsPartsOrderGenerateEnvFile from "./ems-parts-order-generate-env";
import EmsPartsOrderGenerateLinFile from "./ems-parts-order-generate-lin";
import EmsPartsOrderGeneratePfhFile from "./ems-parts-order-generate-pfh";
import EmsPartsOrderGeneratePflFile from "./ems-parts-order-generate-pfl";
import EmsPartsOrderGeneratePfmFile from "./ems-parts-order-generate-pfm";
import EmsPartsOrderGeneratePfoFile from "./ems-parts-order-generate-pfo";
import EmsPartsOrderGeneratePfpFile from "./ems-parts-order-generate-pfp";
import EmsPartsOrderGeneratePftFile from "./ems-parts-order-generate-pft";
import EmsPartsOrderGenerateStlFile from "./ems-parts-order-generate-stl";
import EmsPartsOrderGenerateTtlFile from "./ems-parts-order-generate-ttl";
import EmsPartsOrderGenerateVehFile from "./ems-parts-order-generate-veh";
import { EmsPartsOrder } from "./ems-parts-order-interfaces";
const handleEMSPartsOrder = async (
req: express.Request,
res: express.Response,
): Promise<void> => {
//Route handler here only.
const partsOrderBody = req.body as EmsPartsOrder;
try {
await generateEMSPartsOrder(partsOrderBody);
res.status(200).json({ success: true });
} catch (error) {
log.error("Error generating parts price change", errorTypeCheck(error));
res.status(500).json({
success: false,
error: "Error generating parts price change.",
...errorTypeCheck(error),
});
}
return;
};
const generateEMSPartsOrder = async (
partsOrder: EmsPartsOrder,
): Promise<void> => {
log.debug(" Generating parts price change");
//Check to make sure that the EMS Output file path exists. If it doesn't, create it. If it's not set, abandon ship.
const emsOutFilePath: string | null = store.get("settings.emsOutFilePath");
if (_.isEmpty(emsOutFilePath) || emsOutFilePath === null) {
log.error("EMS Out file path is not set");
throw new Error("EMS Out file path is not set");
}
try {
createdDirectoryIfNotExist(emsOutFilePath);
//Generate all required files: ad1, ad2, veh, lin, pfh, pfl, pfm,pfo, pfp, pft, stl, ttl
await EmsPartsOrderGenerateAd1File(partsOrder);
await EmsPartsOrderGenerateAd2File(partsOrder);
await EmsPartsOrderGenerateVehFile(partsOrder);
await EmsPartsOrderGenerateLinFile(partsOrder);
await EmsPartsOrderGeneratePfhFile(partsOrder);
await EmsPartsOrderGeneratePflFile(partsOrder);
await EmsPartsOrderGeneratePfmFile(partsOrder);
await EmsPartsOrderGeneratePfoFile(partsOrder);
await EmsPartsOrderGeneratePfpFile(partsOrder);
await EmsPartsOrderGeneratePftFile(partsOrder);
await EmsPartsOrderGenerateStlFile(partsOrder);
await EmsPartsOrderGenerateTtlFile(partsOrder);
await EmsPartsOrderGenerateEnvFile(partsOrder);
log.info(
"EMS Parts Order files generated successfully for " +
partsOrder.job.ciecaid,
);
} catch (error) {
log.error("Error generating parts price change", errorTypeCheck(error));
throw error;
}
};
export { handleEMSPartsOrder };

View File

@@ -1,322 +0,0 @@
import { CiecaPfl } from "../decoder/decode-pfl.interface";
import { DecodedPfmLine } from "../decoder/decode-pfm.interface";
import { DecodedPfpLine } from "../decoder/decode-pfp.interface";
import { DecodedStlLine } from "../decoder/decode-stl.interface";
import { DecodedTtlLine } from "../decoder/decode-ttl.interface";
export interface TaxRate {
prt_type: string;
prt_discp: number;
prt_mktyp: boolean;
prt_mkupp: number;
prt_tax_in: boolean;
prt_tax_rt: number;
}
export interface BillTaxRates {
local_tax_rate: number;
state_tax_rate: number;
federal_tax_rate: number;
}
export interface PaintCodes {
paint_cd1: string | null;
paint_cd2: string | null;
paint_cd3: string | null;
}
export interface AreaOfDamage {
impact1: string;
impact2: string | null;
}
// Jobline export interface
export interface Jobline {
tran_code: string;
act_price: number;
db_ref: string;
db_price: number;
db_hrs: number;
glass_flag: boolean;
id: string;
lbr_amt: number;
lbr_hrs_j: boolean;
lbr_inc: boolean;
lbr_op: string;
lbr_op_j: boolean;
lbr_tax: boolean;
lbr_typ_j: boolean;
line_desc: string;
line_ind: string;
line_no: number;
line_ref: number;
location: string | null;
misc_amt: number;
misc_sublt: boolean;
misc_tax: boolean;
mod_lb_hrs: number;
mod_lbr_ty: string;
oem_partno: string;
op_code_desc: string;
paint_stg: number;
paint_tone: number;
part_qty: number;
part_type: string;
price_inc: boolean;
price_j: boolean;
prt_dsmk_m: number;
prt_dsmk_p: number;
tax_part: boolean;
unq_seq: number;
alt_co_id: string | null;
alt_overrd: boolean;
alt_part_i: boolean;
alt_partm: string | null;
alt_partno: string | null;
bett_amt: number;
bett_pctg: number;
bett_tax: boolean;
bett_type: string | null;
cert_part: boolean;
est_seq: string | null;
part_descj: boolean;
}
// Parts Order Line export interface
export interface PartsOrderLine {
jobline: Jobline;
act_price: number;
id: string;
db_price: number;
line_desc: string;
quantity: number;
part_type: string;
priceChange: boolean;
}
// Vehicle export interface
export interface Vehicle {
v_bstyle: string;
v_type: string;
v_trimcode: string | null;
v_tone: string;
v_stage: string;
v_prod_dt: string | null;
v_options: string | null;
v_paint_codes: PaintCodes;
v_model_yr: string;
v_model_desc: string;
v_mldgcode: string | null;
v_makecode: string;
v_make_desc: string;
v_engine: string;
v_cond: string;
v_color: string | null;
trim_color: string | null;
shopid: string;
plate_no: string;
plate_st: string;
db_v_code: string;
v_vin: string;
}
// Bodyshop export interface
export interface Bodyshop {
shopname: string;
bill_tax_rates: BillTaxRates;
}
// Job export interface
export interface Job {
bodyshop: Bodyshop;
ro_number: string;
clm_no: string;
asgn_no: string;
asgn_date: string;
state_tax_rate: number | null;
area_of_damage: AreaOfDamage;
asgn_type: string | null;
ciecaid: string;
cieca_pfl: CiecaPfl;
clm_addr1: string | null;
clm_city: string | null;
clm_addr2: string | null;
clm_ct_fn: string | null;
clm_ct_ln: string | null;
clm_ct_ph: string | null;
clm_ct_phx: string | null;
clm_ctry: string | null;
clm_ea: string | null;
clm_fax: string | null;
clm_faxx: string | null;
clm_ofc_id: string | null;
clm_ofc_nm: string | null;
clm_ph1: string | null;
clm_ph1x: string | null;
clm_ph2: string | null;
clm_ph2x: string | null;
clm_st: string | null;
clm_title: string | null;
clm_total: number;
clm_zip: string | null;
ded_amt: number;
est_addr1: string | null;
est_addr2: string | null;
est_city: string | null;
est_co_nm: string | null;
est_ct_fn: string;
est_ctry: string | null;
est_ct_ln: string;
est_ea: string;
est_ph1: string | null;
est_st: string | null;
est_zip: string | null;
g_bett_amt: number;
id: string;
ins_addr1: string | null;
ins_city: string | null;
ins_addr2: string | null;
ins_co_id: string | null;
ins_co_nm: string;
ins_ct_fn: string | null;
ins_ct_ln: string | null;
ins_ct_ph: string | null;
ins_ct_phx: string | null;
ins_ctry: string | null;
ins_ea: string | null;
ins_fax: string | null;
ins_faxx: string | null;
ins_memo: string | null;
ins_ph1: string | null;
ins_ph1x: string | null;
ins_ph2: string | null;
ins_ph2x: string | null;
ins_st: string | null;
ins_title: string | null;
ins_zip: string | null;
insd_addr1: string;
insd_addr2: string | null;
insd_city: string;
insd_co_nm: string | null;
insd_ctry: string | null;
insd_ea: string | null;
insd_fax: string | null;
insd_faxx: string | null;
insd_fn: string;
insd_ln: string;
insd_ph1: string;
insd_ph1x: string | null;
insd_ph2: string;
insd_ph2x: string | null;
insd_st: string;
insd_title: string | null;
insd_zip: string;
loss_cat: string;
loss_date: string;
loss_desc: string;
loss_of_use: string | null;
loss_type: string;
ownr_addr1: string;
ownr_addr2: string | null;
ownr_city: string;
ownr_co_nm: string | null;
ownr_ctry: string | null;
ownr_ea: string | null;
ownr_fax: string | null;
ownr_faxx: string | null;
ownr_ph1: string;
ownr_fn: string;
ownr_ln: string;
ownr_ph1x: string | null;
ownr_ph2: string;
ownr_ph2x: string | null;
ownr_st: string;
ownr_title: string | null;
ownr_zip: string;
parts_tax_rates: Record<string, DecodedPfpLine>;
pay_amt: number;
pay_date: string | null;
pay_type: string | null;
pay_chknm: string;
payee_nms: string | null;
plate_no: string;
plate_st: string;
po_number: string | null;
policy_no: string;
tax_lbr_rt: number;
tax_levies_rt: number;
tax_paint_mat_rt: number;
tax_predis: number;
tax_prethr: number;
tax_pstthr: number;
tax_registration_number: string | null;
tax_str_rt: number;
tax_shop_mat_rt: number;
tax_sub_rt: number;
tax_thramt: number;
tax_tow_rt: number;
theft_ind: boolean;
tlos_ind: boolean;
towin: boolean;
v_color: string | null;
v_make_desc: string;
v_model_desc: string;
v_model_yr: string;
v_vin: string;
vehicle: Vehicle;
agt_zip: string | null;
agt_st: string | null;
agt_ph2x: string | null;
agt_ph2: string | null;
agt_ph1x: string | null;
agt_ph1: string | null;
agt_lic_no: string | null;
agt_faxx: string | null;
agt_fax: string | null;
agt_ea: string | null;
agt_ctry: string | null;
agt_ct_phx: string | null;
agt_ct_ph: string | null;
agt_ct_ln: string | null;
agt_ct_fn: string | null;
agt_co_nm: string | null;
agt_co_id: string | null;
agt_city: string | null;
agt_addr1: string | null;
agt_addr2: string | null;
adj_g_disc: number;
rate_matd: number | null;
rate_mash: number;
rate_mapa: number;
rate_mahw: number;
rate_macs: number;
rate_mabl: number | null;
rate_ma3s: number;
rate_ma2t: number;
rate_ma2s: number;
rate_lau: number;
rate_las: number;
rate_lar: number;
rate_lam: number;
rate_lag: number;
rate_laf: number;
rate_lae: number | null;
rate_lad: number | null;
rate_lab: number;
rate_laa: number;
rate_la4: number;
rate_la3: number;
rate_la2: number;
rate_la1: number;
materials: Record<string, DecodedPfmLine>;
cieca_stl: {
data: Array<DecodedStlLine>;
};
cieca_ttl: { data: DecodedTtlLine };
}
// Main Parts Order export interface
export interface EmsPartsOrder {
parts_order_lines: PartsOrderLine[];
job: Job;
}

View File

@@ -1,210 +0,0 @@
import cors from "cors";
import { app } from "electron";
import log from "electron-log/main";
import express from "express";
import http from "http";
import errorTypeCheck from "../../util/errorTypeCheck";
import ImportJob from "../decoder/decoder";
import folderScan from "../decoder/folder-scan";
import { handleEMSPartsOrder } from "../ems-parts-order/ems-parts-order-handler";
import { handleShopMetaDataFetch } from "../ipc/ipcMainHandler.user";
import { handlePartsPriceChangeRequest } from "../ppc/ppc-handler";
import { handleQuickBookRequest } from "../quickbooks-desktop/quickbooks-desktop";
export default class LocalServer {
private readonly app: express.Application;
private server: http.Server | null;
private PORT = 1337;
constructor() {
this.server = null;
this.app = express();
this.configureMiddleware();
this.configureRoutes();
}
private configureMiddleware(): void {
const allowedOrigins = [
"http://localhost",
"https://localhost",
"http://localhost:3000",
"https://localhost:3000",
"https://test.imex.online",
"https://imex.online",
"https://test.romeonline.io",
"https://romeonline.io",
"https://www.test.imex.online",
"https://www.imex.online",
"https://www.test.romeonline.io",
"https://www.romeonline.io",
];
this.app.use(
cors({
origin: (origin, callback) => {
// Allow requests with no origin (like mobile apps, curl requests)
if (!origin) return callback(null, true);
if (allowedOrigins.indexOf(origin) !== -1) {
return callback(null, true);
} else {
return callback(null, false);
}
},
credentials: true,
}),
);
// Parse JSON bodies
this.app.use(express.json());
this.app.use(express.urlencoded());
//Add logger Middleware
this.app.use((req, res, next) => {
const startTime = Date.now();
const requestId = Math.random().toString(36).substring(2, 15);
// Log request details
log.info(
`[HTTP Server] [${requestId}] Request: ${req.method} ${req.url}`,
);
log.info(
`[HTTP Server] [${requestId}] Headers: ${JSON.stringify(req.headers)}`,
);
// Log request body if it exists
if (req.body && Object.keys(req.body).length > 0) {
log.info(
`[HTTP Server] [${requestId}] Body: ${JSON.stringify(req.body)}`,
);
}
// Capture the original methods
const originalSend = res.send;
const originalJson = res.json;
// Override send method to log response
res.send = function (body): express.Response {
log.info(`[HTTP Server] [${requestId}] Response body: ${body}`);
log.info(
`[HTTP Server] [${requestId}] Response time: ${Date.now() - startTime}ms`,
);
return originalSend.call(this, body);
};
// Override json method to log response
res.json = function (body): express.Response {
log.info(
`[HTTP Server] [${requestId}] Response body: ${JSON.stringify(body)}`,
);
log.info(
`[HTTP Server] [${requestId}] Response time: ${Date.now() - startTime}ms`,
);
return originalJson.call(this, body);
};
next();
});
}
private configureRoutes(): void {
// Basic health check endpoint
this.app.get("/health", (_req: express.Request, res: express.Response) => {
res.status(200).json({ status: "ok" });
});
this.app.post("/ping", (_req, res) => {
res.status(200).json({
appVer: app.getVersion(),
qbPath: app.getPath("userData"), //TODO: Resolve to actual QB file path.
});
});
this.app.post("/qb", handleQuickBookRequest);
this.app.post("/scan", async (_req, res): Promise<void> => {
log.debug("[HTTP Server] Scan request received");
const files = await folderScan();
res.status(200).json(files);
return;
});
this.app.post("/ppc", handlePartsPriceChangeRequest);
this.app.post("/oec", handleEMSPartsOrder);
this.app.post(
"/import",
async (req: express.Request, res: express.Response) => {
log.debug("[HTTP Server] Import request received");
const { filepath } = req.body;
if (!filepath) {
res.status(400).json({ error: "filepath is required" });
return;
}
try {
await ImportJob(filepath);
res.status(200).json({ success: true });
} catch (error) {
log.error(
"[HTTP Server] Error importing file",
errorTypeCheck(error),
);
res.status(500).json({
success: false,
error: "Error importing file",
...errorTypeCheck(error),
});
}
},
);
this.app.post(
"/refresh",
async (_req: express.Request, res: express.Response) => {
log.debug("[HTTP Server] Refresh request received");
try {
await handleShopMetaDataFetch(true);
res.status(200).json({ success: true });
} catch (error) {
log.error(
"[HTTP Server] Error refreshing shop metadata",
errorTypeCheck(error),
);
res.status(500).json({
success: false,
error: "Error importing file",
...errorTypeCheck(error),
});
}
},
);
// Add more routes as needed
}
public start(): void {
try {
this.server = http.createServer(this.app);
this.server.on("error", (error: NodeJS.ErrnoException) => {
if (error.code === "EADDRINUSE") {
log.error(
`[HTTP Server] Port ${this.PORT} is already in use. Please use a different port.`,
);
} else {
log.error(`[HTTP Server] Server error: ${error.message}`);
}
});
this.server.listen(this.PORT, () => {
log.info(
`[HTTP Server] Local HTTP server running on port ${this.PORT}`,
);
});
} catch (error: unknown) {
log.error("[HTTP Server] Error starting server", errorTypeCheck(error));
}
}
public stop(): void {
if (this.server) {
this.server.close();
log.info("[HTTP Server] Local HTTP server stopped");
}
}
}

View File

@@ -14,7 +14,6 @@ import log from "electron-log/main";
import { autoUpdater } from "electron-updater";
import path, { join } from "path";
import imexAppIcon from "../../resources/icon.png?asset";
import romeAppIcon from "../../resources/ro-icon.png?asset";
import {
default as ErrorTypeCheck,
@@ -22,11 +21,8 @@ import {
} from "../util/errorTypeCheck";
import ipcTypes from "../util/ipcTypes.json";
import ImportJob from "./decoder/decoder";
import LocalServer from "./http-server/http-server";
import store from "./store/store";
import { checkForAppUpdates } from "./util/checkForAppUpdates";
import { getMainWindow } from "./util/toRenderer";
import { GetAllEnvFiles } from "./watcher/watcher";
import { dumpMemoryStatsToFile } from "../util/memUsage";
import {
isKeepAliveAgentInstalled,
setupKeepAliveAgent,
@@ -35,11 +31,13 @@ import {
isKeepAliveTaskInstalled,
setupKeepAliveTask,
} from "./setup-keep-alive-task";
import store from "./store/store";
import { checkForAppUpdates } from "./util/checkForAppUpdates";
import ensureWindowOnScreen from "./util/ensureWindowOnScreen";
import ongoingMemoryDump, { dumpMemoryStatsToFile } from "../util/memUsage";
import { getMainWindow } from "./util/toRenderer";
import { GetAllEnvFiles } from "./watcher/watcher";
const appIconToUse =
import.meta.env.VITE_COMPANY === "IMEX" ? imexAppIcon : romeAppIcon;
const appIconToUse = imexAppIcon;
Sentry.init({
dsn: "https://ba41d22656999a8c1fd63bcb7df98650@o492140.ingest.us.sentry.io/4509074139447296",
@@ -54,11 +52,10 @@ log.transports.console.format =
"[{y}-{m}-{d} {h}:{i}:{s}.{ms}] [{level}] [PID:{processId}] {text}";
log.transports.file.maxSize = 50 * 1024 * 1024; // 50 MB
const isMac: boolean = process.platform === "darwin";
const protocol: string = "imexmedia";
const protocol: string = "esdp";
let isAppQuitting = false; //Needed on Mac as an override to allow us to fully quit the app.
let isKeepAliveLaunch = false; // Track if launched via keep-alive
// Initialize the server
const localServer = new LocalServer();
const gotTheLock = app.requestSingleInstanceLock();
if (!gotTheLock) {
@@ -95,7 +92,7 @@ function createWindow(): void {
icon: appIconToUse,
}
: {}),
title: "Shop Partner",
title: "EMS Uploader",
webPreferences: {
preload: join(__dirname, "../preload/index.js"),
sandbox: false,
@@ -425,13 +422,6 @@ function createWindow(): void {
if (!isKeepAliveLaunch) {
mainWindow.show(); // Show only if not a keep-alive launch
}
//Start the HTTP server.
// Start the local HTTP server
try {
localServer.start();
} catch (error) {
log.error("Failed to start HTTP server:", errorTypeCheck(error));
}
});
mainWindow.on("close", (event: Electron.Event) => {
@@ -476,7 +466,7 @@ app.whenReady().then(async () => {
log.debug("App is ready, initializing shortcuts and protocol handlers.");
if (platform.isWindows) {
app.setAppUserModelId("Shop Partner");
app.setAppUserModelId("esdp");
}
app.on("browser-window-created", (_, window) => {
@@ -508,17 +498,8 @@ app.whenReady().then(async () => {
//Dynamically load ipcMain handlers once ready.
try {
const { initializeCronTasks } = await import("./ipc/ipcMainConfig");
await import("./ipc/ipcMainConfig");
log.debug("Successfully loaded ipcMainConfig");
try {
await initializeCronTasks();
log.info("Cron tasks initialized successfully");
} catch (error) {
log.warn("Non-fatal: Failed to initialize cron tasks", {
...ErrorTypeCheck(error),
});
}
} catch (error) {
log.error("Fatal: Failed to load ipcMainConfig", {
...ErrorTypeCheck(error),
@@ -599,7 +580,6 @@ app.whenReady().then(async () => {
//The update itself will run when the bodyshop record is queried to know what release channel to use.
openMainWindow();
ongoingMemoryDump();
app.on("activate", function () {
openMainWindow();
@@ -661,7 +641,6 @@ ipcMain.on(ipcTypes.toMain.updates.apply, () => {
});
function preQuitMethods(): void {
localServer.stop();
const currentSetting = store.get("app.openOnStartup") as boolean;
if (!import.meta.env.DEV) {
app.setLoginItemSettings({

View File

@@ -27,27 +27,6 @@ import {
ipcMainHandleAuthStateChanged,
ipMainHandleResetPassword,
} from "./ipcMainHandler.user";
import cron from "node-cron";
import { PaintScaleConfig, PaintScaleType } from "../../util/types/paintScale";
import { ppgInputHandler, ppgOutputHandler } from "./paintScaleHandlers/PPG";
const initializeCronTasks = async () => {
try {
// Fetch input and output configurations
const inputConfigs = await SettingsPaintScaleInputConfigsGet();
const outputConfigs = await SettingsPaintScaleOutputConfigsGet();
// Start input cron tasks
await handlePaintScaleInputCron(inputConfigs);
log.info("Initialized input cron tasks on app startup");
// Start output cron tasks
await handlePaintScaleOutputCron(outputConfigs);
log.info("Initialized output cron tasks on app startup");
} catch (error) {
log.error("Error initializing cron tasks on app startup:", error);
}
};
// Log all IPC messages and their payloads
const logIpcMessages = (): void => {
@@ -71,75 +50,6 @@ const logIpcMessages = (): void => {
});
};
// Input handler map
const inputTypeHandlers: Partial<
Record<PaintScaleType, (config: PaintScaleConfig) => Promise<void>>
> = {
[PaintScaleType.PPG]: ppgInputHandler,
// Add other input type handlers as needed
};
// Output handler map
const outputTypeHandlers: Partial<
Record<PaintScaleType, (config: PaintScaleConfig) => Promise<void>>
> = {
[PaintScaleType.PPG]: ppgOutputHandler,
// Add other output type handlers as needed
};
// Default handler for unsupported types
const defaultHandler = async (config: PaintScaleConfig) => {
log.debug(
`No handler defined for type ${config.type} in config ${config.id}`,
);
};
// Input cron job management
let inputCronTasks: { [id: string]: cron.ScheduledTask } = {};
const handlePaintScaleInputCron = async (configs: PaintScaleConfig[]) => {
Object.values(inputCronTasks).forEach((task) => task.stop());
inputCronTasks = {};
const validConfigs = configs.filter(
(config) => config.path && config.path.trim() !== "",
);
validConfigs.forEach((config) => {
const cronExpression = `*/${config.pollingInterval} * * * *`;
inputCronTasks[config.id] = cron.schedule(cronExpression, async () => {
const handler = inputTypeHandlers[config.type] || defaultHandler;
await handler(config);
});
log.info(
`Started input cron task for config ${config.id} (type: ${config.type}) with interval ${config.pollingInterval}m`,
);
});
};
// Output cron job management
let outputCronTasks: { [id: string]: cron.ScheduledTask } = {};
const handlePaintScaleOutputCron = async (configs: PaintScaleConfig[]) => {
Object.values(outputCronTasks).forEach((task) => task.stop());
outputCronTasks = {};
const validConfigs = configs.filter(
(config) => config.path && config.path.trim() !== "",
);
validConfigs.forEach((config) => {
const cronExpression = `*/${config.pollingInterval} * * * *`;
outputCronTasks[config.id] = cron.schedule(cronExpression, async () => {
const handler = outputTypeHandlers[config.type] || defaultHandler;
await handler(config);
});
log.info(
`Started output cron task for config ${config.id} (type: ${config.type}) with interval ${config.pollingInterval}m`,
);
});
};
// Existing IPC handlers...
ipcMain.on(ipcTypes.toMain.test, () =>
@@ -230,25 +140,6 @@ ipcMain.handle(
SettingsPaintScaleOutputPathSet,
);
// IPC handlers for updating paint scale cron
ipcMain.on(
ipcTypes.toMain.settings.paintScale.updateInputCron,
(_event, configs: PaintScaleConfig[]) => {
handlePaintScaleInputCron(configs).catch((error) => {
log.error(`Error handling paint scale input cron for configs: ${error}`);
});
},
);
ipcMain.on(
ipcTypes.toMain.settings.paintScale.updateOutputCron,
(_event, configs: PaintScaleConfig[]) => {
handlePaintScaleOutputCron(configs).catch((error) => {
log.error(`Error handling paint scale output cron for configs: ${error}`);
});
},
);
ipcMain.handle(ipcTypes.toMain.user.getActiveShop, () => {
return store.get("app.bodyshop.shopname");
});
@@ -273,6 +164,4 @@ ipcMain.on(ipcTypes.toMain.updates.download, () => {
});
});
export { initializeCronTasks };
logIpcMessages();

View File

@@ -1,34 +0,0 @@
import { DBFFile } from "dbffile";
import { envFieldLineDescriptors } from "../util/ems-interface/fielddescriptors/env-field-descriptor";
import { deleteEmsFileIfExists, generatePpcFilePath } from "../util/ems-util";
import { PpcJob } from "./ppc-handler";
const GenerateEnvFile = async (job: PpcJob): Promise<boolean> => {
const records = [
{
EST_SYSTEM: "C",
RO_ID: job.ro_number,
ESTFILE_ID: job.ciecaid,
STATUS: false,
INCL_ADMIN: true,
INCL_VEH: true,
INCL_EST: true,
INCL_PROFL: true,
INCL_TOTAL: true,
INCL_VENDR: false,
},
];
await deleteEmsFileIfExists(generatePpcFilePath(`${job.ciecaid}.ENV`));
const dbf = await DBFFile.create(
generatePpcFilePath(`${job.ciecaid}.ENV`),
envFieldLineDescriptors,
);
await dbf.appendRecords(records);
console.log(`${records.length} LIN file records added.`);
return true;
};
export default GenerateEnvFile;

View File

@@ -1,34 +0,0 @@
import { DBFFile } from "dbffile";
import { linFieldDescriptors } from "../util/ems-interface/fielddescriptors/lin-field-descriptor";
import { deleteEmsFileIfExists, generatePpcFilePath } from "../util/ems-util";
import { PpcJob } from "./ppc-handler";
import errorTypeCheck from "../../util/errorTypeCheck";
const GenerateLinFile = async (job: PpcJob): Promise<boolean> => {
try {
const records = job.joblines.map((line) => {
return {
//TODO: There are missing types here. May require server side updates, but we are missing things like LINE_NO, LINE_IND, etc.
TRAN_CODE: "2",
UNQ_SEQ: line.unq_seq,
ACT_PRICE: line.act_price,
};
});
await deleteEmsFileIfExists(generatePpcFilePath(`${job.ciecaid}.LIN`));
const dbf = await DBFFile.create(
generatePpcFilePath(`${job.ciecaid}.LIN`),
linFieldDescriptors,
);
await dbf.appendRecords(records);
console.log(`${records.length} LIN file records added.`);
return true;
} catch (error) {
console.error("Error generating PPC LIN file", errorTypeCheck(error));
throw error;
}
};
export default GenerateLinFile;

View File

@@ -1,67 +0,0 @@
import { UUID } from "crypto";
import log from "electron-log/main";
import express from "express";
import _ from "lodash";
import errorTypeCheck from "../../util/errorTypeCheck";
import store from "../store/store";
import createdDirectoryIfNotExist from "../util/createDirectoryIfNotExist";
import GenerateEnvFile from "./ppc-generate-env";
import GenerateLinFile from "./ppc-generate-lin";
const handlePartsPriceChangeRequest = async (
req: express.Request,
res: express.Response,
): Promise<void> => {
//Route handler here only.
const job = req.body as PpcJob;
try {
await generatePartsPriceChange(job);
res.status(200).json({ success: true });
} catch (error) {
log.error("Error generating parts price change", errorTypeCheck(error));
res.status(500).json({
success: false,
error: "Error generating parts price change.",
...errorTypeCheck(error),
});
}
return;
};
const generatePartsPriceChange = async (job: PpcJob): Promise<void> => {
log.debug(" Generating parts price change");
//Check to make sure that the PPC Output file path exists. If it doesn't, create it. If it's not set, abandon ship.
const ppcOutFilePath: string | null = store.get("settings.ppcFilePath");
if (_.isEmpty(ppcOutFilePath) || ppcOutFilePath === null) {
log.error("PPC file path is not set");
throw new Error("PPC file path is not set");
}
try {
createdDirectoryIfNotExist(ppcOutFilePath);
await GenerateLinFile(job);
await GenerateEnvFile(job);
} catch (error) {
log.error("Error generating parts price change", errorTypeCheck(error));
throw error;
}
};
export interface PpcJob {
id: UUID;
ciecaid: string;
ro_number: string;
joblines: {
removed: boolean;
act_price_before_ppc: number | null;
id: string;
act_price: number;
unq_seq: string; //TODO: Might be a number.
}[];
bodyshop: {
timezone: string;
};
}
export { handlePartsPriceChangeRequest };

View File

@@ -1,30 +0,0 @@
using System;
using Interop.QBFC16; // Ensure this matches your DLL version
public class QuickBooksConnector
{
public string ProcessQBXML(string qbxmlRequest)
{
try
{
QBSessionManager sessionManager = new QBSessionManager();
sessionManager.OpenConnection("", "YourAppName");
sessionManager.BeginSession("", ENOpenMode.omDontCare);
IMsgSetRequest requestMsgSet = sessionManager.CreateMsgSetRequest("US", 13, 0);
requestMsgSet.AppendXML(qbxmlRequest);
IMsgSetResponse responseMsgSet = sessionManager.DoRequests(requestMsgSet);
string qbxmlResponse = responseMsgSet.ToXMLString();
sessionManager.EndSession();
sessionManager.CloseConnection();
return qbxmlResponse;
}
catch (Exception ex)
{
return $"Error: {ex.Message}";
}
}
}

View File

@@ -1,130 +0,0 @@
import log from "electron-log/main";
import { UUID } from "crypto";
import { Request, Response } from "express";
import _ from "lodash";
import errorTypeCheck from "../../util/errorTypeCheck";
// eslint-disable-next-line @typescript-eslint/no-explicit-any
let Winax: any; // Declare Winax as any to avoid TypeScript errors on non-Windows platforms
if (process.platform === "win32") {
// eslint-disable-next-line @typescript-eslint/no-require-imports
Winax = require("winax");
}
export async function handleQuickBookRequest(
req: Request,
res: Response,
): Promise<void> {
if (process.platform !== "win32") {
res.status(500).json({
error: "QuickBooks Desktop integration is only available on Windows",
});
return;
}
const QbFilePath: string = `C:\\Users\\PatrickFic\\Development\\FRODO COLLISION.QBW`;
// ||
// (store.get("settings.qbFilePath") as string) F
if (_.isEmpty(QbFilePath)) {
res.status(400).json({ error: "Quickbooks file path not set" });
return;
}
const qbxmlRequestList = req.body as Array<{
id: UUID;
okStatusCodes: Array<string>;
qbxml: string;
}>;
const returnResponse: Array<{
Id: UUID;
Success: boolean;
ErrorMessage: string;
}> = [];
//Connect to the QuickBooks File
let requestProcessor;
try {
requestProcessor = new Winax.Object("QBXMLRP2.RequestProcessor.2");
requestProcessor.OpenConnection(QbFilePath, "ShopPartnerActualRequest");
} catch (error) {
log.error(
"Error instnatiating QuickBooks Request Processor",
QbFilePath,
errorTypeCheck(error),
);
res.status(500).json({ error: "Error connecting to QuickBooks" });
return;
}
const ticket = requestProcessor.BeginSession(QbFilePath, 2); //2 indicated qbFileOpenModeDoNotCare
log.info("Quickbooks Ticket", ticket);
for (const qbxmlRequest of qbxmlRequestList) {
try {
//TODO: Refactor to not create a new connection every time.
const QuickBooksResponse = requestProcessor.ProcessRequest(
ticket,
qbxmlRequest.qbxml,
);
log.info("QuickBooks Raw Response: ", QuickBooksResponse);
returnResponse.push({
Id: qbxmlRequest.id,
Success:
QuickBooksResponse.StatusCode === "0" ||
qbxmlRequest.okStatusCodes.includes(QuickBooksResponse.StatusCode),
ErrorMessage: QuickBooksResponse,
});
} catch (error) {
log.error(
"Error running transaction",
ticket,
qbxmlRequest,
errorTypeCheck(error),
);
}
}
requestProcessor.EndSession(ticket);
requestProcessor.CloseConnection();
res.json(qbxmlRequestList);
}
//This set of functions works.
export function TestQB(): void {
if (process.platform !== "win32") {
log.warn("TestQB is only available on Windows");
return;
}
let requestProcessor, ticket;
try {
requestProcessor = new Winax.Object("QBXMLRP.RequestProcessor.1");
requestProcessor.OpenConnection("", "ShopPartnerOneoFf");
ticket = requestProcessor.BeginSession("", 2); //2 indicated qbFileOOpenModeDoNotCare
requestProcessor.ProcessRequest(
ticket,
`<?qbxml version="16.0"?>
<QBXML>
<QBXMLMsgsRq onError="stopOnError">
<AccountQueryRq requestID="1"> </AccountQueryRq>
</QBXMLMsgsRq>
</QBXML>`,
);
} catch (error) {
log.error(
"Error instnatiating QuickBooks Request Processor",
errorTypeCheck(error),
);
return;
}
log.log("Ticket", ticket);
requestProcessor.EndSession(ticket);
requestProcessor.CloseConnection();
return;
}

View File

@@ -5,11 +5,7 @@ const store = new Store({
settings: {
runOnStartup: true,
filepaths: [],
ppcFilePath: null,
emsOutFilePath: null,
qbFilePath: "",
runWatcherOnStartup: true,
enableMemDebug: false,
polling: {
enabled: false,
interval: 30000,
@@ -25,9 +21,6 @@ const store = new Store({
user: null,
isTest: false,
bodyshop: {},
masterdata: {
opcodes: null,
},
},
},
});