|
|
|
|
@@ -32,8 +32,7 @@ const ADVISORS_CACHE_TTL = 7 * 24 * 60 * 60; // seconds
|
|
|
|
|
* @param job
|
|
|
|
|
* @returns {*|null}
|
|
|
|
|
*/
|
|
|
|
|
const resolveJobId = (explicit, payload, job) =>
|
|
|
|
|
explicit || payload?.jobId || payload?.jobid || job?.id || job?.jobId || job?.jobid || null;
|
|
|
|
|
const resolveJobId = (explicit, payload, job) => explicit || payload?.jobId || job?.id || null;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Resolve VIN from tx/job shapes
|
|
|
|
|
@@ -218,6 +217,7 @@ const registerRREvents = ({ socket, redisHelpers }) => {
|
|
|
|
|
try {
|
|
|
|
|
const { bodyshopId } = await getSessionOrSocket(redisHelpers, socket);
|
|
|
|
|
const bodyshop = await getBodyshopForSocket({ bodyshopId, socket });
|
|
|
|
|
|
|
|
|
|
CreateRRLogEvent(socket, "DEBUG", "rr-lookup-combined: begin", { jobid, params });
|
|
|
|
|
|
|
|
|
|
const res = await rrCombinedSearch(bodyshop, params || {});
|
|
|
|
|
@@ -230,13 +230,14 @@ const registerRREvents = ({ socket, redisHelpers }) => {
|
|
|
|
|
|
|
|
|
|
const normalized = sortVehicleOwnerFirst(normalizeCustomerCandidates(res, { ownersSet }));
|
|
|
|
|
const rid = resolveJobId(jobid, { jobid }, null);
|
|
|
|
|
|
|
|
|
|
const decorated = normalized.map((c) => (c.vinOwner != null ? c : { ...c, vinOwner: !!c.isVehicleOwner }));
|
|
|
|
|
|
|
|
|
|
cb?.({ jobid: rid, data: decorated });
|
|
|
|
|
|
|
|
|
|
socket.emit("rr-select-customer", decorated);
|
|
|
|
|
CreateRRLogEvent(socket, "DEBUG", "rr-lookup-combined: emitted rr-select-customer", {
|
|
|
|
|
count: decorated.length
|
|
|
|
|
count: decorated.length,
|
|
|
|
|
res
|
|
|
|
|
});
|
|
|
|
|
} catch (e) {
|
|
|
|
|
CreateRRLogEvent(socket, "ERROR", "RR combined lookup error", { error: e.message, jobid });
|
|
|
|
|
@@ -320,8 +321,10 @@ const registerRREvents = ({ socket, redisHelpers }) => {
|
|
|
|
|
|
|
|
|
|
socket.on("rr-export-job", async ({ jobid, jobId, txEnvelope } = {}) => {
|
|
|
|
|
const rid = resolveJobId(jobid || jobId, { jobId, jobid }, null);
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
if (!rid) throw new Error("RR export: jobid required");
|
|
|
|
|
|
|
|
|
|
CreateRRLogEvent(socket, "DEBUG", `{1} Received RR export request`, { jobid: rid });
|
|
|
|
|
|
|
|
|
|
await redisHelpers.setSessionTransactionData(
|
|
|
|
|
@@ -331,6 +334,7 @@ const registerRREvents = ({ socket, redisHelpers }) => {
|
|
|
|
|
txEnvelope || {},
|
|
|
|
|
defaultRRTTL
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
CreateRRLogEvent(socket, "DEBUG", `{1.1} Cached txEnvelope`, { hasTxEnvelope: !!txEnvelope });
|
|
|
|
|
|
|
|
|
|
const job = await QueryJobData({ redisHelpers }, rid);
|
|
|
|
|
@@ -341,12 +345,14 @@ const registerRREvents = ({ socket, redisHelpers }) => {
|
|
|
|
|
job,
|
|
|
|
|
defaultRRTTL
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
CreateRRLogEvent(socket, "DEBUG", `{1.2} Cached JobData`, { vin: job?.v_vin, ro: job?.ro_number });
|
|
|
|
|
|
|
|
|
|
const adv = readAdvisorNo(
|
|
|
|
|
{ txEnvelope },
|
|
|
|
|
await redisHelpers.getSessionTransactionData(socket.id, getTransactionType(rid), RRCacheEnums.AdvisorNo)
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (adv) {
|
|
|
|
|
await redisHelpers.setSessionTransactionData(
|
|
|
|
|
socket.id,
|
|
|
|
|
@@ -355,6 +361,7 @@ const registerRREvents = ({ socket, redisHelpers }) => {
|
|
|
|
|
String(adv),
|
|
|
|
|
defaultRRTTL
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
CreateRRLogEvent(socket, "DEBUG", `{1.3} Cached advisorNo`, { advisorNo: String(adv) });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -362,9 +369,10 @@ const registerRREvents = ({ socket, redisHelpers }) => {
|
|
|
|
|
const bodyshop = await getBodyshopForSocket({ bodyshopId, socket });
|
|
|
|
|
|
|
|
|
|
CreateRRLogEvent(socket, "DEBUG", `{2} Running multi-search (Full Name + VIN)`);
|
|
|
|
|
const candidates = await rrMultiCustomerSearch({ bodyshop, job, socket, redisHelpers });
|
|
|
|
|
|
|
|
|
|
const candidates = await rrMultiCustomerSearch({ bodyshop, job, socket, redisHelpers });
|
|
|
|
|
const decorated = candidates.map((c) => (c.vinOwner != null ? c : { ...c, vinOwner: !!c.isVehicleOwner }));
|
|
|
|
|
|
|
|
|
|
socket.emit("rr-select-customer", decorated);
|
|
|
|
|
CreateRRLogEvent(socket, "DEBUG", `{2.1} Emitted rr-select-customer`, {
|
|
|
|
|
count: decorated.length,
|
|
|
|
|
@@ -376,6 +384,7 @@ const registerRREvents = ({ socket, redisHelpers }) => {
|
|
|
|
|
stack: error.stack,
|
|
|
|
|
jobid: rid
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
socket.emit("export-failed", { vendor: "rr", jobId: rid, error: error.message });
|
|
|
|
|
} catch {
|
|
|
|
|
@@ -399,24 +408,31 @@ const registerRREvents = ({ socket, redisHelpers }) => {
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const ns = getTransactionType(rid);
|
|
|
|
|
|
|
|
|
|
let selectedCustNo =
|
|
|
|
|
(custNo && String(custNo)) ||
|
|
|
|
|
(selectedCustomerId && String(selectedCustomerId)) ||
|
|
|
|
|
(await redisHelpers.getSessionTransactionData(socket.id, ns, RRCacheEnums.SelectedCustomer));
|
|
|
|
|
|
|
|
|
|
job = await redisHelpers.getSessionTransactionData(socket.id, ns, RRCacheEnums.JobData);
|
|
|
|
|
|
|
|
|
|
const txEnvelope = (await redisHelpers.getSessionTransactionData(socket.id, ns, RRCacheEnums.txEnvelope)) || {};
|
|
|
|
|
|
|
|
|
|
if (!job) throw new Error("Staged JobData not found (run rr-export-job first).");
|
|
|
|
|
|
|
|
|
|
const { bodyshopId } = await getSessionOrSocket(redisHelpers, socket);
|
|
|
|
|
|
|
|
|
|
bodyshop = await getBodyshopForSocket({ bodyshopId, socket });
|
|
|
|
|
|
|
|
|
|
// Create customer (if requested or none chosen)
|
|
|
|
|
if (create === true || !selectedCustNo) {
|
|
|
|
|
CreateRRLogEvent(socket, "DEBUG", `{3.1} Creating RR customer`);
|
|
|
|
|
|
|
|
|
|
const created = await createRRCustomer({ bodyshop, job, socket });
|
|
|
|
|
selectedCustNo = String(created?.customerNo);
|
|
|
|
|
|
|
|
|
|
if (!selectedCustNo) throw new Error("RR create customer returned no custNo");
|
|
|
|
|
|
|
|
|
|
CreateRRLogEvent(socket, "DEBUG", `{3.2} Created customer`, { custNo: selectedCustNo });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -426,6 +442,7 @@ const registerRREvents = ({ socket, redisHelpers }) => {
|
|
|
|
|
if (vehQ && vehQ.kind === "vin" && job?.v_vin) {
|
|
|
|
|
const resVin = await rrCombinedSearch(bodyshop, vehQ);
|
|
|
|
|
const blocksVin = Array.isArray(resVin?.data) ? resVin.data : Array.isArray(resVin) ? resVin : [];
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
await redisHelpers.setSessionTransactionData(
|
|
|
|
|
socket.id,
|
|
|
|
|
@@ -437,9 +454,12 @@ const registerRREvents = ({ socket, redisHelpers }) => {
|
|
|
|
|
} catch {
|
|
|
|
|
//
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const ownersSet = ownersFromVinBlocks(blocksVin, job.v_vin);
|
|
|
|
|
|
|
|
|
|
if (ownersSet?.size) {
|
|
|
|
|
const sel = String(selectedCustNo);
|
|
|
|
|
|
|
|
|
|
if (!ownersSet.has(sel)) {
|
|
|
|
|
const [existingOwner] = Array.from(ownersSet).map(String);
|
|
|
|
|
CreateRRLogEvent(socket, "DEBUG", `{3.2a} VIN exists; switching to VIN owner`, {
|
|
|
|
|
@@ -471,6 +491,7 @@ const registerRREvents = ({ socket, redisHelpers }) => {
|
|
|
|
|
|
|
|
|
|
// Cache final/effective customer selection
|
|
|
|
|
const effectiveCustNo = String(selectedCustNo);
|
|
|
|
|
|
|
|
|
|
await redisHelpers.setSessionTransactionData(
|
|
|
|
|
socket.id,
|
|
|
|
|
ns,
|
|
|
|
|
@@ -478,6 +499,7 @@ const registerRREvents = ({ socket, redisHelpers }) => {
|
|
|
|
|
effectiveCustNo,
|
|
|
|
|
defaultRRTTL
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
CreateRRLogEvent(socket, "DEBUG", `{3.3} Cached selected customer`, { custNo: effectiveCustNo });
|
|
|
|
|
|
|
|
|
|
// Build client & routing
|
|
|
|
|
@@ -489,12 +511,13 @@ const registerRREvents = ({ socket, redisHelpers }) => {
|
|
|
|
|
const tx = {
|
|
|
|
|
jobData: {
|
|
|
|
|
...job,
|
|
|
|
|
vin: job?.v_vin || job?.vin || job?.vehicleVin || undefined
|
|
|
|
|
vin: job?.v_vin
|
|
|
|
|
},
|
|
|
|
|
txEnvelope
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const vin = resolveVin({ tx, job });
|
|
|
|
|
|
|
|
|
|
if (!vin) {
|
|
|
|
|
CreateRRLogEvent(socket, "ERROR", "{3.x} No VIN found for ensureRRServiceVehicle", { jobid: rid });
|
|
|
|
|
throw new Error("ensureRRServiceVehicle: vin required");
|
|
|
|
|
@@ -524,9 +547,9 @@ const registerRREvents = ({ socket, redisHelpers }) => {
|
|
|
|
|
|
|
|
|
|
CreateRRLogEvent(socket, "DEBUG", "{3.4} ensureRRServiceVehicle: done", ensured);
|
|
|
|
|
|
|
|
|
|
// Advisor no
|
|
|
|
|
const cachedAdvisor = await redisHelpers.getSessionTransactionData(socket.id, ns, RRCacheEnums.AdvisorNo);
|
|
|
|
|
const advisorNo = readAdvisorNo({ txEnvelope }, cachedAdvisor);
|
|
|
|
|
|
|
|
|
|
if (!advisorNo) {
|
|
|
|
|
CreateRRLogEvent(socket, "ERROR", `Advisor is required (advisorNo)`);
|
|
|
|
|
await insertRRFailedExportLog({
|
|
|
|
|
@@ -540,6 +563,7 @@ const registerRREvents = ({ socket, redisHelpers }) => {
|
|
|
|
|
socket.emit("export-failed", { vendor: "rr", jobId: rid, error: "Advisor is required (advisorNo)." });
|
|
|
|
|
return ack?.({ ok: false, error: "Advisor is required (advisorNo)." });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
await redisHelpers.setSessionTransactionData(
|
|
|
|
|
socket.id,
|
|
|
|
|
ns,
|
|
|
|
|
@@ -572,17 +596,9 @@ const registerRREvents = ({ socket, redisHelpers }) => {
|
|
|
|
|
const data = result?.data || {};
|
|
|
|
|
|
|
|
|
|
// Prefer explicit return from export function; then fall back to fields
|
|
|
|
|
const dmsRoNo =
|
|
|
|
|
result?.roNo ?? data?.dmsRoNo ?? data?.DMSRoNo ?? data?.roStatus?.dmsRoNo ?? data?.roStatus?.DMSRoNo ?? null;
|
|
|
|
|
const dmsRoNo = result?.roNo ?? data?.dmsRoNo ?? null;
|
|
|
|
|
|
|
|
|
|
const outsdRoNo =
|
|
|
|
|
data?.outsdRoNo ??
|
|
|
|
|
data?.OutsdRoNo ??
|
|
|
|
|
data?.roStatus?.outsdRoNo ??
|
|
|
|
|
data?.roStatus?.OutsdRoNo ??
|
|
|
|
|
job?.ro_number ??
|
|
|
|
|
job?.id ??
|
|
|
|
|
null;
|
|
|
|
|
const outsdRoNo = data?.outsdRoNo ?? job?.ro_number ?? job?.id ?? null;
|
|
|
|
|
|
|
|
|
|
await redisHelpers.setSessionTransactionData(
|
|
|
|
|
socket.id,
|
|
|
|
|
@@ -598,13 +614,13 @@ const registerRREvents = ({ socket, redisHelpers }) => {
|
|
|
|
|
defaultRRTTL
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
CreateRRLogEvent(socket, "INFO", `{5} RO created. Waiting for cashiering.`, {
|
|
|
|
|
CreateRRLogEvent(socket, "INFO", `{5} RO created. Waiting for validation.`, {
|
|
|
|
|
dmsRoNo: dmsRoNo || null,
|
|
|
|
|
outsdRoNo: outsdRoNo || null
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Tell FE to prompt for "Finished/Close"
|
|
|
|
|
socket.emit("rr-cashiering-required", { jobId: rid, dmsRoNo, outsdRoNo });
|
|
|
|
|
socket.emit("rr-validation-required", { jobId: rid, dmsRoNo, outsdRoNo });
|
|
|
|
|
|
|
|
|
|
// Still emit info result if you want
|
|
|
|
|
socket.emit("rr-export-job:result", { jobId: rid, bodyshopId: bodyshop?.id, result });
|
|
|
|
|
@@ -616,6 +632,7 @@ const registerRREvents = ({ socket, redisHelpers }) => {
|
|
|
|
|
const vendorStatusCode = Number(
|
|
|
|
|
result?.roStatus?.statusCode ?? result?.roStatus?.StatusCode ?? result?.statusBlocks?.transaction?.statusCode
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const cls = classifyRRVendorError({
|
|
|
|
|
code: vendorStatusCode,
|
|
|
|
|
message: result?.roStatus?.message ?? result?.roStatus?.Message ?? result?.error ?? "RR export failed"
|
|
|
|
|
|