Merge remote-tracking branch 'origin/hotfix/2026-03-03' into release/2026-02-27
This commit is contained in:
@@ -48,6 +48,46 @@ const resolveJobId = (explicit, payload, job) => explicit || payload?.jobId || j
|
||||
*/
|
||||
const resolveVin = ({ tx, job }) => tx?.jobData?.vin || job?.v_vin || null;
|
||||
|
||||
/**
|
||||
* Extract request/response XML from RR response/result shapes.
|
||||
* @param rrObj
|
||||
* @returns {{requestXml: string|null, responseXml: string|null}}
|
||||
*/
|
||||
const extractRRXmlPair = (rrObj) => {
|
||||
const xml = rrObj?.xml;
|
||||
|
||||
let requestXml = null;
|
||||
let responseXml = null;
|
||||
|
||||
if (typeof xml === "string") {
|
||||
requestXml = xml;
|
||||
} else {
|
||||
if (typeof xml?.request === "string") requestXml = xml.request;
|
||||
else if (typeof xml?.req === "string") requestXml = xml.req;
|
||||
else if (typeof xml?.starXml === "string") requestXml = xml.starXml;
|
||||
if (typeof xml?.response === "string") responseXml = xml.response;
|
||||
}
|
||||
|
||||
if (!requestXml && typeof rrObj?.requestXml === "string") requestXml = rrObj.requestXml;
|
||||
if (!responseXml && typeof rrObj?.responseXml === "string") responseXml = rrObj.responseXml;
|
||||
|
||||
return { requestXml, responseXml };
|
||||
};
|
||||
|
||||
/**
|
||||
* Add Reynolds request/response XML to RR log metadata when available.
|
||||
* @param rrObj
|
||||
* @param meta
|
||||
* @returns {*}
|
||||
*/
|
||||
const withRRRequestXml = (rrObj, meta = {}) => {
|
||||
const { requestXml, responseXml } = extractRRXmlPair(rrObj);
|
||||
const xmlMeta = {};
|
||||
if (requestXml) xmlMeta.requestXml = requestXml;
|
||||
if (responseXml) xmlMeta.responseXml = responseXml;
|
||||
return Object.keys(xmlMeta).length ? { ...meta, ...xmlMeta } : meta;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sort vehicle owners first in the list, preserving original order otherwise.
|
||||
* @param list
|
||||
@@ -154,15 +194,13 @@ const setJobDmsIdForSocket = async ({ socket, jobId, dmsId, dmsCustomerId, dmsAd
|
||||
if (!token) throw new Error("Missing auth token for setJobDmsIdForSocket");
|
||||
|
||||
const client = new GraphQLClient(endpoint, {});
|
||||
await client
|
||||
.setHeaders({ Authorization: `Bearer ${token}` })
|
||||
.request(queries.SET_JOB_DMS_ID, {
|
||||
id: jobId,
|
||||
dms_id: String(dmsId),
|
||||
dms_customer_id: dmsCustomerId ? String(dmsCustomerId) : null,
|
||||
dms_advisor_id: dmsAdvisorId ? String(dmsAdvisorId) : null,
|
||||
kmin: mileageIn != null && mileageIn > 0 ? parseInt(mileageIn, 10) : null
|
||||
});
|
||||
await client.setHeaders({ Authorization: `Bearer ${token}` }).request(queries.SET_JOB_DMS_ID, {
|
||||
id: jobId,
|
||||
dms_id: String(dmsId),
|
||||
dms_customer_id: dmsCustomerId ? String(dmsCustomerId) : null,
|
||||
dms_advisor_id: dmsAdvisorId ? String(dmsAdvisorId) : null,
|
||||
kmin: mileageIn != null && mileageIn > 0 ? parseInt(mileageIn, 10) : null
|
||||
});
|
||||
|
||||
CreateRRLogEvent(socket, "INFO", "Linked job.dms_id to RR RO", {
|
||||
jobId,
|
||||
@@ -511,7 +549,11 @@ const registerRREvents = ({ socket, redisHelpers }) => {
|
||||
});
|
||||
|
||||
// Filter out invalid values
|
||||
if (selectedCustNo === "undefined" || selectedCustNo === "null" || (selectedCustNo && selectedCustNo.trim() === "")) {
|
||||
if (
|
||||
selectedCustNo === "undefined" ||
|
||||
selectedCustNo === "null" ||
|
||||
(selectedCustNo && selectedCustNo.trim() === "")
|
||||
) {
|
||||
selectedCustNo = null;
|
||||
}
|
||||
|
||||
@@ -705,42 +747,52 @@ const registerRREvents = ({ socket, redisHelpers }) => {
|
||||
|
||||
const outsdRoNo = data?.outsdRoNo ?? job?.ro_number ?? job?.id ?? null;
|
||||
|
||||
CreateRRLogEvent(socket, "DEBUG", "Early RO created - checking dmsRoNo", {
|
||||
dmsRoNo,
|
||||
resultRoNo: result?.roNo,
|
||||
dataRoNo: data?.dmsRoNo,
|
||||
jobId: rid
|
||||
});
|
||||
CreateRRLogEvent(
|
||||
socket,
|
||||
"DEBUG",
|
||||
"Early RO created - checking dmsRoNo",
|
||||
withRRRequestXml(result, {
|
||||
dmsRoNo,
|
||||
resultRoNo: result?.roNo,
|
||||
dataRoNo: data?.dmsRoNo,
|
||||
jobId: rid
|
||||
})
|
||||
);
|
||||
|
||||
// ✅ Persist DMS RO number, customer ID, advisor ID, and mileage on the job
|
||||
if (dmsRoNo) {
|
||||
const mileageIn = txEnvelope?.kmin ?? null;
|
||||
CreateRRLogEvent(socket, "DEBUG", "Calling setJobDmsIdForSocket", {
|
||||
jobId: rid,
|
||||
CreateRRLogEvent(socket, "DEBUG", "Calling setJobDmsIdForSocket", {
|
||||
jobId: rid,
|
||||
dmsId: dmsRoNo,
|
||||
customerId: effectiveCustNo,
|
||||
advisorId: String(advisorNo),
|
||||
mileageIn
|
||||
});
|
||||
await setJobDmsIdForSocket({
|
||||
socket,
|
||||
jobId: rid,
|
||||
await setJobDmsIdForSocket({
|
||||
socket,
|
||||
jobId: rid,
|
||||
dmsId: dmsRoNo,
|
||||
dmsCustomerId: effectiveCustNo,
|
||||
dmsAdvisorId: String(advisorNo),
|
||||
mileageIn
|
||||
});
|
||||
} else {
|
||||
CreateRRLogEvent(socket, "WARN", "RR early RO creation succeeded but no DMS RO number was returned", {
|
||||
jobId: rid,
|
||||
resultPreview: {
|
||||
roNo: result?.roNo,
|
||||
data: {
|
||||
dmsRoNo: data?.dmsRoNo,
|
||||
outsdRoNo: data?.outsdRoNo
|
||||
CreateRRLogEvent(
|
||||
socket,
|
||||
"WARN",
|
||||
"RR early RO creation succeeded but no DMS RO number was returned",
|
||||
withRRRequestXml(result, {
|
||||
jobId: rid,
|
||||
resultPreview: {
|
||||
roNo: result?.roNo,
|
||||
data: {
|
||||
dmsRoNo: data?.dmsRoNo,
|
||||
outsdRoNo: data?.outsdRoNo
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
await redisHelpers.setSessionTransactionData(
|
||||
@@ -758,10 +810,15 @@ const registerRREvents = ({ socket, redisHelpers }) => {
|
||||
defaultRRTTL
|
||||
);
|
||||
|
||||
CreateRRLogEvent(socket, "INFO", `{EARLY-5} Minimal RO created successfully`, {
|
||||
dmsRoNo: dmsRoNo || null,
|
||||
outsdRoNo: outsdRoNo || null
|
||||
});
|
||||
CreateRRLogEvent(
|
||||
socket,
|
||||
"INFO",
|
||||
`{EARLY-5} Minimal RO created successfully`,
|
||||
withRRRequestXml(result, {
|
||||
dmsRoNo: dmsRoNo || null,
|
||||
outsdRoNo: outsdRoNo || null
|
||||
})
|
||||
);
|
||||
|
||||
// Mark success in export logs
|
||||
await markRRExportSuccess({
|
||||
@@ -810,11 +867,16 @@ const registerRREvents = ({ socket, redisHelpers }) => {
|
||||
message: vendorMessage
|
||||
});
|
||||
|
||||
CreateRRLogEvent(socket, "ERROR", `Early RO creation failed`, {
|
||||
roStatus: result?.roStatus,
|
||||
statusBlocks: result?.statusBlocks,
|
||||
classification: cls
|
||||
});
|
||||
CreateRRLogEvent(
|
||||
socket,
|
||||
"ERROR",
|
||||
`Early RO creation failed`,
|
||||
withRRRequestXml(result, {
|
||||
roStatus: result?.roStatus,
|
||||
statusBlocks: result?.statusBlocks,
|
||||
classification: cls
|
||||
})
|
||||
);
|
||||
|
||||
await insertRRFailedExportLog({
|
||||
socket,
|
||||
@@ -940,14 +1002,14 @@ const registerRREvents = ({ socket, redisHelpers }) => {
|
||||
|
||||
// Check if this job already has an early RO - if so, use stored IDs and skip customer search
|
||||
const hasEarlyRO = !!job?.dms_id;
|
||||
|
||||
|
||||
if (hasEarlyRO) {
|
||||
CreateRRLogEvent(socket, "DEBUG", `{2} Early RO exists - using stored customer/advisor`, {
|
||||
dms_id: job.dms_id,
|
||||
dms_customer_id: job.dms_customer_id,
|
||||
dms_advisor_id: job.dms_advisor_id
|
||||
});
|
||||
|
||||
|
||||
// Cache the stored customer/advisor IDs for the next step
|
||||
if (job.dms_customer_id) {
|
||||
await redisHelpers.setSessionTransactionData(
|
||||
@@ -967,18 +1029,18 @@ const registerRREvents = ({ socket, redisHelpers }) => {
|
||||
defaultRRTTL
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// Emit empty customer list to frontend (won't show modal)
|
||||
socket.emit("rr-select-customer", []);
|
||||
|
||||
|
||||
// Continue directly with the export by calling the selected customer handler logic inline
|
||||
// This is essentially the same as if user selected the stored customer
|
||||
const selectedCustNo = job.dms_customer_id;
|
||||
|
||||
|
||||
if (!selectedCustNo) {
|
||||
throw new Error("Early RO exists but no customer ID stored");
|
||||
}
|
||||
|
||||
|
||||
// Continue with ensureRRServiceVehicle and export (same as rr-selected-customer handler)
|
||||
const { client, opts } = await buildClientAndOpts(bodyshop);
|
||||
const routing = opts?.routing || client?.opts?.routing || null;
|
||||
@@ -1011,7 +1073,12 @@ const registerRREvents = ({ socket, redisHelpers }) => {
|
||||
redisHelpers
|
||||
});
|
||||
|
||||
const advisorNo = job.dms_advisor_id || readAdvisorNo({ txEnvelope }, await redisHelpers.getSessionTransactionData(socket.id, getTransactionType(rid), RRCacheEnums.AdvisorNo));
|
||||
const advisorNo =
|
||||
job.dms_advisor_id ||
|
||||
readAdvisorNo(
|
||||
{ txEnvelope },
|
||||
await redisHelpers.getSessionTransactionData(socket.id, getTransactionType(rid), RRCacheEnums.AdvisorNo)
|
||||
);
|
||||
|
||||
if (!advisorNo) {
|
||||
throw new Error("Advisor is required (advisorNo).");
|
||||
@@ -1059,15 +1126,20 @@ const registerRREvents = ({ socket, redisHelpers }) => {
|
||||
defaultRRTTL
|
||||
);
|
||||
|
||||
CreateRRLogEvent(socket, "INFO", `RR Repair Order updated successfully`, {
|
||||
dmsRoNo,
|
||||
jobId: rid
|
||||
});
|
||||
CreateRRLogEvent(
|
||||
socket,
|
||||
"INFO",
|
||||
`RR Repair Order updated successfully`,
|
||||
withRRRequestXml(result, {
|
||||
dmsRoNo,
|
||||
jobId: rid
|
||||
})
|
||||
);
|
||||
|
||||
// For early RO flow, only emit validation-required (not export-job:result)
|
||||
// since the export is not complete yet - we're just waiting for validation
|
||||
socket.emit("rr-validation-required", { dmsRoNo, jobId: rid });
|
||||
|
||||
|
||||
return ack?.({ ok: true, skipCustomerSelection: true, dmsRoNo });
|
||||
}
|
||||
|
||||
@@ -1277,25 +1349,25 @@ const registerRREvents = ({ socket, redisHelpers }) => {
|
||||
// When updating an early RO, use stored customer/advisor IDs
|
||||
let finalEffectiveCustNo = effectiveCustNo;
|
||||
let finalAdvisorNo = advisorNo;
|
||||
|
||||
|
||||
if (shouldUpdate && job?.dms_customer_id) {
|
||||
CreateRRLogEvent(socket, "DEBUG", `Using stored customer ID from early RO`, {
|
||||
CreateRRLogEvent(socket, "DEBUG", `Using stored customer ID from early RO`, {
|
||||
storedCustomerId: job.dms_customer_id,
|
||||
originalCustomerId: effectiveCustNo
|
||||
originalCustomerId: effectiveCustNo
|
||||
});
|
||||
finalEffectiveCustNo = String(job.dms_customer_id);
|
||||
}
|
||||
|
||||
|
||||
if (shouldUpdate && job?.dms_advisor_id) {
|
||||
CreateRRLogEvent(socket, "DEBUG", `Using stored advisor ID from early RO`, {
|
||||
CreateRRLogEvent(socket, "DEBUG", `Using stored advisor ID from early RO`, {
|
||||
storedAdvisorId: job.dms_advisor_id,
|
||||
originalAdvisorId: advisorNo
|
||||
originalAdvisorId: advisorNo
|
||||
});
|
||||
finalAdvisorNo = String(job.dms_advisor_id);
|
||||
}
|
||||
|
||||
let result;
|
||||
|
||||
|
||||
if (shouldUpdate) {
|
||||
// UPDATE existing RO with full data
|
||||
CreateRRLogEvent(socket, "DEBUG", `{4} Updating existing RR RO with full data`, { dmsRoNo: existingDmsId });
|
||||
@@ -1344,16 +1416,21 @@ const registerRREvents = ({ socket, redisHelpers }) => {
|
||||
if (dmsRoNo) {
|
||||
await setJobDmsIdForSocket({ socket, jobId: rid, dmsId: dmsRoNo });
|
||||
} else {
|
||||
CreateRRLogEvent(socket, "WARN", "RR export succeeded but no DMS RO number was returned", {
|
||||
jobId: rid,
|
||||
resultPreview: {
|
||||
roNo: result?.roNo,
|
||||
data: {
|
||||
dmsRoNo: data?.dmsRoNo,
|
||||
outsdRoNo: data?.outsdRoNo
|
||||
CreateRRLogEvent(
|
||||
socket,
|
||||
"WARN",
|
||||
"RR export succeeded but no DMS RO number was returned",
|
||||
withRRRequestXml(result, {
|
||||
jobId: rid,
|
||||
resultPreview: {
|
||||
roNo: result?.roNo,
|
||||
data: {
|
||||
dmsRoNo: data?.dmsRoNo,
|
||||
outsdRoNo: data?.outsdRoNo
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
await redisHelpers.setSessionTransactionData(
|
||||
@@ -1370,10 +1447,15 @@ const registerRREvents = ({ socket, redisHelpers }) => {
|
||||
defaultRRTTL
|
||||
);
|
||||
|
||||
CreateRRLogEvent(socket, "INFO", `{5} RO created. Waiting for validation.`, {
|
||||
dmsRoNo: dmsRoNo || null,
|
||||
outsdRoNo: outsdRoNo || null
|
||||
});
|
||||
CreateRRLogEvent(
|
||||
socket,
|
||||
"INFO",
|
||||
`{5} RO created. Waiting for validation.`,
|
||||
withRRRequestXml(result, {
|
||||
dmsRoNo: dmsRoNo || null,
|
||||
outsdRoNo: outsdRoNo || null
|
||||
})
|
||||
);
|
||||
|
||||
// Tell FE to prompt for "Finished/Close"
|
||||
socket.emit("rr-validation-required", { jobId: rid, dmsRoNo, outsdRoNo });
|
||||
@@ -1412,11 +1494,16 @@ const registerRREvents = ({ socket, redisHelpers }) => {
|
||||
message: vendorMessage
|
||||
});
|
||||
|
||||
CreateRRLogEvent(socket, "ERROR", `Export failed (step 1)`, {
|
||||
roStatus: result?.roStatus,
|
||||
statusBlocks: result?.statusBlocks,
|
||||
classification: cls
|
||||
});
|
||||
CreateRRLogEvent(
|
||||
socket,
|
||||
"ERROR",
|
||||
`Export failed (step 1)`,
|
||||
withRRRequestXml(result, {
|
||||
roStatus: result?.roStatus,
|
||||
statusBlocks: result?.statusBlocks,
|
||||
classification: cls
|
||||
})
|
||||
);
|
||||
|
||||
await insertRRFailedExportLog({
|
||||
socket,
|
||||
@@ -1541,7 +1628,12 @@ const registerRREvents = ({ socket, redisHelpers }) => {
|
||||
});
|
||||
|
||||
if (finalizeResult?.success) {
|
||||
CreateRRLogEvent(socket, "INFO", `{7} Finalize success; marking exported`, { dmsRoNo, outsdRoNo });
|
||||
CreateRRLogEvent(
|
||||
socket,
|
||||
"INFO",
|
||||
`{7} Finalize success; marking exported`,
|
||||
withRRRequestXml(finalizeResult, { dmsRoNo, outsdRoNo })
|
||||
);
|
||||
|
||||
// ✅ Mark exported + success log
|
||||
await markRRExportSuccess({
|
||||
@@ -1584,6 +1676,17 @@ const registerRREvents = ({ socket, redisHelpers }) => {
|
||||
message: vendorMessage
|
||||
});
|
||||
|
||||
CreateRRLogEvent(
|
||||
socket,
|
||||
"ERROR",
|
||||
"Finalize failed",
|
||||
withRRRequestXml(finalizeResult, {
|
||||
roStatus: finalizeResult?.roStatus,
|
||||
statusBlocks: finalizeResult?.statusBlocks,
|
||||
classification: cls
|
||||
})
|
||||
);
|
||||
|
||||
await insertRRFailedExportLog({
|
||||
socket,
|
||||
jobId: rid,
|
||||
|
||||
Reference in New Issue
Block a user