feature/IO-3205-Paint-Scale-Integrations: Pre Change Checkpoint

This commit is contained in:
Dave Richer
2025-04-29 13:10:18 -04:00
parent 9a33993dea
commit 2c2652d07e
4 changed files with 143 additions and 191 deletions

View File

@@ -5,3 +5,5 @@ VITE_GRAPHQL_ENDPOINT_TEST=https://db.test.bodyshop.app/v1/graphql
VITE_COMPANY=IMEX
VITE_FE_URL=https://imex.online
VITE_FE_URL_TEST=https://test.imex.online
VITE_API_URL="http://localhost:4000"
VITE_API_TEST_URL="http://api.test.imex.online"

View File

@@ -5,3 +5,5 @@ VITE_GRAPHQL_ENDPOINT_TEST=https://db.test.bodyshop.app/v1/graphql
VITE_COMPANY=IMEX
VITE_FE_URL=https://imex.online
VITE_FE_URL_TEST=https://test.imex.online
VITE_API_URL="http://localhost:4000"
VITE_API_TEST_URL="http://api.test.imex.online"

View File

@@ -5,3 +5,5 @@ VITE_GRAPHQL_ENDPOINT_TEST=https://db.test.romeonline.io/v1/graphql
VITE_COMPANY=ROME
VITE_FE_URL=https://romeonline.io
VITE_FE_URL_TEST=https://test.romeonline.io
VITE_API_URL="http://localhost:4000"
VITE_API_TEST_URL="http://api.test.romeonline.io"

View File

@@ -119,7 +119,6 @@ const initializeCronTasks = async () => {
}
};
// Log all IPC messages and their payloads
const logIpcMessages = (): void => {
Object.keys(ipcTypes.toMain).forEach((key) => {
@@ -180,6 +179,7 @@ const inputTypeHandlers: Record<
// Upload file to API
const formData = new FormData();
formData.append(
"file",
new Blob([await fs.readFile(filePath)]),
@@ -190,17 +190,20 @@ const inputTypeHandlers: Record<
(store.get("app.bodyshop") as BodyShop)?.shopname || "",
);
// TODO Clear up how to fetch API URL
const response = await axios.post(
"https://your-api-base-url/mixdata/upload",
formData,
{
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "multipart/form-data",
},
const baseURL = store.get("app.isTest")
? import.meta.env.VITE_API_TEST_URL
: import.meta.env.VITE_API_URL;
const finalUrl = `${baseURL}/mixdata/upload`;
log.debug(`Uploading file to ${finalUrl}`);
const response = await axios.post(finalUrl, formData, {
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "multipart/form-data",
},
);
});
if (response.status === 200) {
log.info(`Successful upload of ${filePath}`);
@@ -234,69 +237,69 @@ const outputTypeHandlers: Record<
// GraphQL query for jobs
const query = `
query PpgData($today: date!, $todayplus5: date!, $shopid: uuid!) {
bodyshops_by_pk(id: $shopid) {
imexshopid
shopname
query PpgData($today: date!, $todayplus5: date!, $shopid: uuid!) {
bodyshops_by_pk(id: $shopid) {
imexshopid
shopname
}
jobs(where: {
_and: [
{ shop_id: { _eq: $shopid } },
{ _or: [
{ status: { _eq: "Active" } },
{ scheduled_date: { _gte: $today, _lte: $todayplus5 } }
] }
]
}) {
ro_number
ownr_ln
ownr_fn
plate_no
v_vin
v_model_yr
v_make_desc
v_model_desc
vehicle {
v_paint_codes {
paint_cd1
}
}
jobs(where: {
_and: [
{ shop_id: { _eq: $shopid } },
{ _or: [
{ status: { _eq: "Active" } },
{ scheduled_date: { _gte: $today, _lte: $todayplus5 } }
] }
]
}) {
ro_number
ownr_ln
ownr_fn
plate_no
v_vin
v_model_yr
v_make_desc
v_model_desc
vehicle {
v_paint_codes {
paint_cd1
larhrs_aggregate: estimate_lines_aggregate(where: { line_type: { _eq: "Paint" } }) {
aggregate {
sum {
mod_lb_hrs
}
}
larhrs_aggregate: estimate_lines_aggregate(where: { line_type: { _eq: "Paint" } }) {
aggregate {
sum {
mod_lb_hrs
}
}
}
ins_co_nm
est_ct_ln
est_ct_fn
job_totals {
rates {
mapa {
total {
amount
}
}
}
totals {
subtotal {
}
ins_co_nm
est_ct_ln
est_ct_fn
job_totals {
rates {
mapa {
total {
amount
}
}
}
rate_mapa
labhrs_aggregate: estimate_lines_aggregate(where: { line_type: { _eq: "Body" } }) {
aggregate {
sum {
mod_lb_hrs
}
totals {
subtotal {
amount
}
}
rate_lab
}
rate_mapa
labhrs_aggregate: estimate_lines_aggregate(where: { line_type: { _eq: "Body" } }) {
aggregate {
sum {
mod_lb_hrs
}
}
}
rate_lab
}
`;
}
`;
const variables = {
today: new Date().toISOString().split("T")[0],
@@ -311,137 +314,80 @@ const outputTypeHandlers: Record<
variables,
)) as GraphQLResponse;
// Generate XML
const doc = create({ version: "1.0" })
.ele("PPG")
.ele("Header")
.ele("Protocol")
.ele("Message")
.txt("PaintShopInterface")
.up()
.ele("Name")
.txt("PPG")
.up()
.ele("Version")
.txt("1.5.0")
.up()
.up()
.ele("Transaction")
.ele("TransactionID")
.txt("")
.up()
.ele("TransactionDate")
.txt(new Date().toISOString().replace("T", " ").substring(0, 19))
.up()
.up()
.ele("Product")
.ele("Name")
.txt("ImEX Online")
.up()
.ele("Version")
.txt("")
.up()
.up()
.up()
.ele("DataInterface")
.ele("ROData")
.ele("ShopInfo")
.ele("ShopID")
.txt(response.bodyshops_by_pk?.imexshopid || "")
.up()
.ele("ShopName")
.txt(response.bodyshops_by_pk?.shopname || "")
.up()
.up()
.ele("RepairOrders")
.ele("ROCount")
.txt((response.jobs?.length || 0).toString())
.up()
.up()
.up()
.up();
// Build JSON structure
const jsonData = {
PPG: {
Header: {
Protocol: {
Message: "PaintShopInterface",
Name: "PPG",
Version: "1.5.0",
},
Transaction: {
TransactionID: "",
TransactionDate: new Date()
.toISOString()
.replace("T", " ")
.substring(0, 19),
},
Product: {
Name: "ImEX Online",
Version: "",
},
},
DataInterface: {
ROData: {
ShopInfo: {
ShopID: response.bodyshops_by_pk?.imexshopid || "",
ShopName: response.bodyshops_by_pk?.shopname || "",
},
RepairOrders: {
ROCount: (response.jobs?.length || 0).toString(),
RO:
response.jobs?.map((job) => ({
RONumber: job.ro_number || "",
ROStatus: "Open",
Customer: `${job.ownr_ln || ""}, ${job.ownr_fn || ""}`,
ROPainterNotes: "",
LicensePlateNum: job.plate_no || "",
VIN: job.v_vin || "",
ModelYear: job.v_model_yr || "",
MakeDesc: job.v_make_desc || "",
ModelName: job.v_model_desc || "",
OEMColorCode: job.vehicle?.v_paint_codes?.paint_cd1 || "",
RefinishLaborHours:
job.larhrs_aggregate?.aggregate?.sum?.mod_lb_hrs || 0,
InsuranceCompanyName: job.ins_co_nm || "",
EstimatorName: `${job.est_ct_ln || ""}, ${job.est_ct_fn || ""}`,
PaintMaterialsRevenue: (
(job.job_totals?.rates?.mapa?.total?.amount || 0) / 100
).toFixed(2),
PaintMaterialsRate: job.rate_mapa || 0,
BodyHours:
job.labhrs_aggregate?.aggregate?.sum?.mod_lb_hrs || 0,
BodyLaborRate: job.rate_lab || 0,
TotalCostOfRepairs: (
(job.job_totals?.totals?.subtotal?.amount || 0) / 100
).toFixed(2),
})) || [],
},
},
},
},
};
const repairOrders = (doc.root() as any).findOne("RepairOrders");
(response as GraphQLResponse).jobs?.forEach((job: any) => {
repairOrders
.ele("RO")
.ele("RONumber")
.txt(job.ro_number || "")
.up()
.ele("ROStatus")
.txt("Open")
.up()
.ele("Customer")
.txt(`${job.ownr_ln || ""}, ${job.ownr_fn || ""}`)
.up()
.ele("ROPainterNotes")
.txt("")
.up()
.ele("LicensePlateNum")
.txt(job.plate_no || "")
.up()
.ele("VIN")
.txt(job.v_vin || "")
.up()
.ele("ModelYear")
.txt(job.v_model_yr || "")
.up()
.ele("MakeDesc")
.txt(job.v_make_desc || "")
.up()
.ele("ModelName")
.txt(job.v_model_desc || "")
.up()
.ele("OEMColorCode")
.txt(job.vehicle?.v_paint_codes?.paint_cd1 || "")
.up()
.ele("RefinishLaborHours")
.txt(job.larhrs_aggregate?.aggregate?.sum?.mod_lb_hrs || 0)
.up()
.ele("InsuranceCompanyName")
.txt(job.ins_co_nm || "")
.up()
.ele("EstimatorName")
.txt(`${job.est_ct_ln || ""}, ${job.est_ct_fn || ""}`)
.up()
.ele("PaintMaterialsRevenue")
.txt(
((job.job_totals?.rates?.mapa?.total?.amount || 0) / 100).toFixed(
2,
),
)
.up()
.ele("PaintMaterialsRate")
.txt(job.rate_mapa || 0)
.up()
.ele("BodyHours")
.txt(job.labhrs_aggregate?.aggregate?.sum?.mod_lb_hrs || 0)
.up()
.ele("BodyLaborRate")
.txt(job.rate_lab || 0)
.up()
.ele("TotalCostOfRepairs")
.txt(
((job.job_totals?.totals?.subtotal?.amount || 0) / 100).toFixed(2),
)
.up();
});
// Convert JSON to XML
const doc = create({ version: "1.0" }, jsonData);
const xmlString = doc.end({ prettyPrint: true });
// Save XML to file
const xmlString = doc.end({ prettyPrint: true });
const timestamp = new Date()
.toISOString()
.replace(/[-:]/g, "")
.replace("T", "")
.substring(0, 14);
const outputPath = path.join(config.path!, `PPGPaint_${timestamp}.xml`);
const outputPath = path.join(config.path!, `PPGPaint.xml`);
await fs.writeFile(outputPath, xmlString);
log.info(`Saved PPG output XML to ${outputPath}`);
} catch (error) {
log.error(`Error generating PPG output for config ${config.id}:`, error);
}
},
// Add other output type handlers as needed
}, // Add other output type handlers as needed
};
// Default handler for unsupported types
@@ -469,7 +415,7 @@ const handlePaintScaleInputCron = async (configs: PaintScaleConfig[]) => {
await handler(config);
});
log.info(
`Started input cron task for config ${config.id} (type: ${config.type}) with interval ${config.pollingInterval}s`,
`Started input cron task for config ${config.id} (type: ${config.type}) with interval ${config.pollingInterval}m`,
);
});
};
@@ -492,7 +438,7 @@ const handlePaintScaleOutputCron = async (configs: PaintScaleConfig[]) => {
await handler(config);
});
log.info(
`Started output cron task for config ${config.id} (type: ${config.type}) with interval ${config.pollingInterval}s`,
`Started output cron task for config ${config.id} (type: ${config.type}) with interval ${config.pollingInterval}m`,
);
});
};