166 lines
5.2 KiB
JavaScript
166 lines
5.2 KiB
JavaScript
/**
|
|
* @file rr-repair-orders.js
|
|
* @description Rome (Reynolds & Reynolds) Repair Order Integration.
|
|
* Handles creation and updates of repair orders (BSMRepairOrderRq/Resp).
|
|
*/
|
|
|
|
"use strict";
|
|
|
|
const { MakeRRCall } = require("./rr-helpers");
|
|
const { mapRepairOrderCreate, mapRepairOrderUpdate } = require("./rr-mappers");
|
|
const RRLogger = require("./rr-logger");
|
|
const { RrApiError } = require("./rr-error");
|
|
|
|
/**
|
|
* Very light sanity checks before we build XML.
|
|
* We keep these minimal because the mapper may derive some fields.
|
|
* Throws RrApiError when a required precondition is missing.
|
|
* @param {"CreateRepairOrder"|"UpdateRepairOrder"} action
|
|
* @param {Object} job
|
|
*/
|
|
function preflight(action, job) {
|
|
if (!job || !job.id) {
|
|
throw new RrApiError("Missing job payload or job.id", "RR_BAD_JOB_PAYLOAD");
|
|
}
|
|
// VIN is almost always required for BSM RO flows
|
|
const vin = job?.vehicle?.vin || job?.vehicle?.VIN || job?.VIN || job?.vin;
|
|
if (!vin) {
|
|
throw new RrApiError("Missing VIN on job.vehicle", "RR_MISSING_VIN");
|
|
}
|
|
|
|
if (action === "UpdateRepairOrder") {
|
|
// If your mapper expects a DMS RO number or an external RO number,
|
|
// you can tighten this guard based on your schema, e.g.:
|
|
// const hasKey = job?.dms_ro_no || job?.external_ro_number || job?.roNumber;
|
|
// if (!hasKey) throw new RrApiError("Missing RO key for update", "RR_MISSING_RO_KEY");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Build an explicit ApplicationArea override so the envelope is always correct,
|
|
* even if the helper falls back to defaults. We include routing info
|
|
* and set Task/ReferenceId for BSM repair orders.
|
|
* @param {"CreateRepairOrder"|"UpdateRepairOrder"} action
|
|
* @param {Object} cfg
|
|
*/
|
|
function buildAppArea(action, cfg) {
|
|
const isCreate = action === "CreateRepairOrder";
|
|
return {
|
|
Sender: {
|
|
Component: "Rome",
|
|
Task: "BSMRO",
|
|
ReferenceId: isCreate ? "Insert" : "Update"
|
|
},
|
|
Destination: {
|
|
DealerNumber: cfg?.DealerNumber || cfg?.dealerNumber,
|
|
StoreNumber: cfg?.StoreNumber || cfg?.storeNumber,
|
|
AreaNumber: cfg?.AreaNumber || cfg?.areaNumber,
|
|
DestinationNameCode: "RR"
|
|
}
|
|
// CreationDateTime and BODId will be provided by rr-helpers if omitted.
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Create a new repair order in Rome.
|
|
* @param {Socket} socket - active socket connection
|
|
* @param {Object} job - Hasura job object (including vehicle, customer, joblines)
|
|
* @param {Object} bodyshopConfig - DMS config for current bodyshop
|
|
* @returns {Promise<Object>} normalized result
|
|
*/
|
|
async function createRepairOrder(socket, job, bodyshopConfig) {
|
|
const action = "CreateRepairOrder";
|
|
const template = "CreateRepairOrder"; // maps to xml-templates/CreateRepairOrder.xml
|
|
|
|
try {
|
|
RRLogger(socket, "info", `Starting RR ${action} for job ${job?.id}`, {
|
|
jobid: job?.id,
|
|
dealer: bodyshopConfig?.DealerNumber || bodyshopConfig?.dealerNumber,
|
|
store: bodyshopConfig?.StoreNumber || bodyshopConfig?.storeNumber,
|
|
area: bodyshopConfig?.AreaNumber || bodyshopConfig?.areaNumber
|
|
});
|
|
|
|
preflight(action, job);
|
|
const data = mapRepairOrderCreate(job, bodyshopConfig);
|
|
|
|
const resultXml = await MakeRRCall({
|
|
action,
|
|
body: { template, data },
|
|
appArea: buildAppArea(action, bodyshopConfig),
|
|
socket,
|
|
dealerConfig: bodyshopConfig,
|
|
jobid: job.id
|
|
});
|
|
|
|
RRLogger(socket, "debug", `${action} completed successfully`, { jobid: job.id });
|
|
|
|
return {
|
|
success: true,
|
|
dms: "Rome",
|
|
jobid: job.id,
|
|
action,
|
|
xml: resultXml
|
|
};
|
|
} catch (error) {
|
|
RRLogger(socket, "error", `Error in ${action} for job ${job?.id}`, {
|
|
message: error?.message,
|
|
stack: error?.stack
|
|
});
|
|
throw new RrApiError(`RR CreateRepairOrder failed: ${error.message}`, "CREATE_RO_ERROR");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Update an existing repair order in Rome.
|
|
* @param {Socket} socket
|
|
* @param {Object} job
|
|
* @param {Object} bodyshopConfig
|
|
* @returns {Promise<Object>}
|
|
*/
|
|
async function updateRepairOrder(socket, job, bodyshopConfig) {
|
|
const action = "UpdateRepairOrder";
|
|
const template = "UpdateRepairOrder";
|
|
|
|
try {
|
|
RRLogger(socket, "info", `Starting RR ${action} for job ${job?.id}`, {
|
|
jobid: job?.id,
|
|
dealer: bodyshopConfig?.DealerNumber || bodyshopConfig?.dealerNumber,
|
|
store: bodyshopConfig?.StoreNumber || bodyshopConfig?.storeNumber,
|
|
area: bodyshopConfig?.AreaNumber || bodyshopConfig?.areaNumber
|
|
});
|
|
|
|
preflight(action, job);
|
|
const data = mapRepairOrderUpdate(job, bodyshopConfig);
|
|
|
|
const resultXml = await MakeRRCall({
|
|
action,
|
|
body: { template, data },
|
|
appArea: buildAppArea(action, bodyshopConfig),
|
|
socket,
|
|
dealerConfig: bodyshopConfig,
|
|
jobid: job.id
|
|
});
|
|
|
|
RRLogger(socket, "debug", `${action} completed successfully`, { jobid: job.id });
|
|
|
|
return {
|
|
success: true,
|
|
dms: "Rome",
|
|
jobid: job.id,
|
|
action,
|
|
xml: resultXml
|
|
};
|
|
} catch (error) {
|
|
RRLogger(socket, "error", `Error in ${action} for job ${job?.id}`, {
|
|
message: error?.message,
|
|
stack: error?.stack
|
|
});
|
|
throw new RrApiError(`RR UpdateRepairOrder failed: ${error.message}`, "UPDATE_RO_ERROR");
|
|
}
|
|
}
|
|
|
|
module.exports = {
|
|
createRepairOrder,
|
|
updateRepairOrder
|
|
};
|