diff --git a/client/src/components/dms-log-events/dms-log-events.component.jsx b/client/src/components/dms-log-events/dms-log-events.component.jsx
index d01afb230..25f0981cf 100644
--- a/client/src/components/dms-log-events/dms-log-events.component.jsx
+++ b/client/src/components/dms-log-events/dms-log-events.component.jsx
@@ -129,6 +129,8 @@ const normalizeLog = (input) => {
*/
const logLevelColor = (level) => {
switch ((level || "").toUpperCase()) {
+ case "SILLY":
+ return "purple";
case "DEBUG":
return "orange";
case "INFO":
diff --git a/client/src/pages/dms/dms.container.jsx b/client/src/pages/dms/dms.container.jsx
index e4acf5862..5f81ed8b4 100644
--- a/client/src/pages/dms/dms.container.jsx
+++ b/client/src/pages/dms/dms.container.jsx
@@ -412,6 +412,7 @@ export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader, inse
setActiveLogLevel(value);
}}
>
+ SILLY
DEBUG
INFO
WARN
diff --git a/server/rr/rr-customers.js b/server/rr/rr-customers.js
index f8eec1c54..5ad966974 100644
--- a/server/rr/rr-customers.js
+++ b/server/rr/rr-customers.js
@@ -1,6 +1,6 @@
const { RRClient } = require("./lib/index.cjs");
const { getRRConfigFromBodyshop } = require("./rr-config");
-const RRLogger = require("./rr-logger");
+const CreateRRLogEvent = require("./rr-logger-event");
const InstanceManager = require("../utils/instanceMgr").default;
/**
@@ -206,30 +206,35 @@ const buildCustomerPayloadFromJob = (job, overrides = {}) => {
* Maps data.dmsRecKey -> customerNo for compatibility with existing callers.
*/
const createRRCustomer = async ({ bodyshop, job, overrides = {}, socket }) => {
- const log = RRLogger(socket, { ns: "rr" });
const { client, opts } = buildClientAndOpts(bodyshop);
const payload = buildCustomerPayloadFromJob(job, overrides);
- let res;
+ let response;
try {
const safePayload = sanitizeRRCustomerPayload(payload);
- res = await client.insertCustomer(safePayload, opts);
+ response = await client.insertCustomer(safePayload, opts);
+ CreateRRLogEvent(socket, "SILLY", "RR createRRCustomer called", { response });
} catch (e) {
- log("error", "RR insertCustomer transport error", { message: e?.message, stack: e?.stack, payload });
+ CreateRRLogEvent(socket, "ERROR", "RR insertCustomer transport error", {
+ message: e?.message,
+ stack: e?.stack,
+ payload
+ });
throw e;
}
- const data = res?.data ?? res;
- const trx = res?.statusBlocks?.transaction;
+ const data = response?.data ?? response;
+ const trx = response?.statusBlocks?.transaction;
let customerNo = data?.dmsRecKey;
if (!customerNo) {
- log("error", "RR insertCustomer returned no dmsRecKey/custNo", {
+ CreateRRLogEvent(socket, "ERROR", "RR insertCustomer returned no dmsRecKey/custNo", {
status: trx?.status,
statusCode: trx?.statusCode,
message: trx?.message,
data
});
+
throw new Error(
`RR insertCustomer returned no dmsRecKey (status=${trx?.status ?? "?"} code=${trx?.statusCode ?? "?"}${
trx?.message ? ` msg=${trx.message}` : ""
diff --git a/server/rr/rr-job-export.js b/server/rr/rr-job-export.js
index e34ddbf86..64b38f280 100644
--- a/server/rr/rr-job-export.js
+++ b/server/rr/rr-job-export.js
@@ -1,16 +1,15 @@
const { buildRRRepairOrderPayload } = require("./rr-job-helpers");
const { buildClientAndOpts } = require("./rr-lookup");
const { ensureRRServiceVehicle } = require("./rr-service-vehicles");
-const RRLogger = require("./rr-logger");
+const CreateRRLogEvent = require("./rr-logger-event");
/**
* Export a job to Reynolds & Reynolds as a Repair Order (create or update).
* @param args
- * @returns {Promise<{success, data: *, roStatus: *, statusBlocks, xml: *, parsed: any, customerNo: string, svId: null, roNo: *}>}
+ * @returns {Promise<{success, data: *, roStatus: *, statusBlocks,customerNo: string, svId: null, roNo: *}>}
*/
const exportJobToRR = async (args) => {
const { bodyshop, job, advisorNo, selectedCustomer, existing, socket } = args || {};
- const log = RRLogger(socket, { ns: "rr-export" });
if (!bodyshop) throw new Error("exportJobToRR: bodyshop is required");
if (!job) throw new Error("exportJobToRR: job is required");
@@ -48,9 +47,9 @@ const exportJobToRR = async (args) => {
socket
});
svId = svRes?.svId || null;
- log("info", "RR service vehicle ensured", { created: svRes?.created, svId });
+ CreateRRLogEvent(socket, "INFO", "RR service vehicle ensured", { created: svRes?.created, svId });
} catch (e) {
- log("warn", "RR ensure service vehicle failed; continuing", { error: e?.message });
+ CreateRRLogEvent(socket, "WARN", "RR ensure service vehicle failed; continuing", { error: e?.message });
}
}
@@ -64,23 +63,25 @@ const exportJobToRR = async (args) => {
// Canonical update key is "roNo" (prefer DMS RO number); accept fallbacks from "existing"
const roNoForUpdate = existing?.roNo || existing?.dmsRoNo || existing?.dmsRepairOrderId || null;
- const rrRes = roNoForUpdate
+ const response = roNoForUpdate
? await client.updateRepairOrder({ ...payload, roNo: String(roNoForUpdate) }, finalOpts) // ✅ use roNo on update
: await client.createRepairOrder(payload, finalOpts);
- const data = rrRes?.data || null;
+ CreateRRLogEvent(socket, "INFO", `RR raw Repair Order ${roNoForUpdate ? "updated" : "created"}`, {
+ response
+ });
+
+ const data = response?.data || null;
const roStatus = data?.roStatus || null;
// Extract canonical roNo you'll need for finalize step
const roNo = data?.dmsRoNo ?? data?.outsdRoNo ?? roStatus?.dmsRoNo ?? null;
return {
- success: rrRes?.success === true || roStatus?.status === "Success",
+ success: response?.success === true || roStatus?.status === "Success",
data,
roStatus,
- statusBlocks: rrRes?.statusBlocks || [],
- xml: rrRes?.xml,
- parsed: rrRes?.parsed,
+ statusBlocks: response?.statusBlocks || [],
customerNo: String(selected),
svId,
roNo
@@ -90,11 +91,10 @@ const exportJobToRR = async (args) => {
/**
* Finalize an RR Repair Order by sending finalUpdate: "Y".
* @param args
- * @returns {Promise<{success, data: *, roStatus: *, statusBlocks, xml: *, parsed: any}>}
+ * @returns {Promise<{success, data: *, roStatus: *, statusBlocks}>}
*/
const finalizeRRRepairOrder = async (args) => {
const { bodyshop, job, advisorNo, customerNo, roNo, vin, socket } = args || {};
- const log = RRLogger(socket, { ns: "rr-finalize" });
if (!bodyshop) throw new Error("finalizeRRRepairOrder: bodyshop is required");
if (!job) throw new Error("finalizeRRRepairOrder: job is required");
@@ -142,7 +142,7 @@ const finalizeRRRepairOrder = async (args) => {
estimate: { estimateType: "Final" }
};
- log("info", "RR finalize updateRepairOrder", {
+ CreateRRLogEvent(socket, "INFO", "Finalizing RR Repair Order", {
roNo: roNoToSend,
outsdRoNo: String(externalRo),
customerNo: String(customerNo),
@@ -157,9 +157,7 @@ const finalizeRRRepairOrder = async (args) => {
success: rrRes?.success === true || roStatus?.status === "Success",
data,
roStatus,
- statusBlocks: rrRes?.statusBlocks || [],
- xml: rrRes?.xml,
- parsed: rrRes?.parsed
+ statusBlocks: rrRes?.statusBlocks || []
};
};
diff --git a/server/rr/rr-logger.js b/server/rr/rr-logger.js
deleted file mode 100644
index c76125e1e..000000000
--- a/server/rr/rr-logger.js
+++ /dev/null
@@ -1,55 +0,0 @@
-const baseLogger = require("../utils/logger");
-
-const safeSerialize = (value) => {
- try {
- const seen = new WeakSet();
- return JSON.stringify(value, (key, val) => {
- if (typeof val === "bigint") return val.toString();
- if (val instanceof Error) return { name: val.name, message: val.message, stack: val.stack };
- if (typeof val === "function") return undefined;
- if (typeof val === "object" && val !== null) {
- if (seen.has(val)) return "[Circular]";
- seen.add(val);
- if (val instanceof Date) return val.toISOString();
- if (val instanceof Map) return Object.fromEntries(val);
- if (val instanceof Set) return Array.from(val);
- if (typeof Buffer !== "undefined" && Buffer.isBuffer?.(val)) return ``;
- }
- return val;
- });
- } catch {
- try {
- return String(value);
- } catch {
- return "[Unserializable]";
- }
- }
-};
-
-const RRLogger = (_socket, defaults = {}) => {
- return function log(level = "info", message = "", ctx = {}) {
- const lvl = String(level || "info").toLowerCase();
- const iso = new Date().toISOString();
-
- const msgStr = typeof message === "string" ? message : safeSerialize(message);
- const mergedCtx = ctx && typeof ctx === "object" ? { ...defaults, ...ctx } : { ...defaults };
-
- const ctxStr = Object.keys(mergedCtx || {}).length ? ` ${safeSerialize(mergedCtx)}` : "";
- const line = `[RR] ${iso} [${lvl.toUpperCase()}] ${msgStr}${ctxStr}`;
-
- const logger = baseLogger?.logger || baseLogger || console;
- const fn = (logger[lvl] || logger.log || logger.info || console[lvl] || console.log).bind(logger);
-
- try {
- fn(line);
- } catch {
- try {
- console.log(line);
- } catch {
- // swallow
- }
- }
- };
-};
-
-module.exports = RRLogger;
diff --git a/server/rr/rr-lookup.js b/server/rr/rr-lookup.js
index cf9183f6e..5fa300a92 100644
--- a/server/rr/rr-lookup.js
+++ b/server/rr/rr-lookup.js
@@ -136,7 +136,7 @@ const rrCombinedSearch = async (bodyshop, args = {}) => {
const { client, opts } = buildClientAndOpts(bodyshop);
const payload = toCombinedSearchPayload(args);
const res = await client.combinedSearch(payload, opts);
- return res?.data ?? res; // lib returns { success, data, ... }
+ return res;
};
/**
@@ -156,8 +156,7 @@ const rrGetAdvisors = async (bodyshop, args = {}) => {
advisorNumber: args.advisorNumber ? String(args.advisorNumber) : undefined
};
- const res = await client.getAdvisors(payload, opts);
- return res?.data ?? res;
+ return client.getAdvisors(payload, opts);
};
module.exports = {
diff --git a/server/rr/rr-register-socket-events.js b/server/rr/rr-register-socket-events.js
index 5d799a0dc..5cc9b845d 100644
--- a/server/rr/rr-register-socket-events.js
+++ b/server/rr/rr-register-socket-events.js
@@ -177,16 +177,20 @@ const rrMultiCustomerSearch = async ({ bodyshop, job, socket, redisHelpers }) =>
for (const { q, fromVin } of queriesList) {
try {
CreateRRLogEvent(socket, "DEBUG", `{RR-SEARCH} Executing ${q.kind} query`, { q });
- const res = await rrCombinedSearch(bodyshop, q);
+
+ const multiResponse = await rrCombinedSearch(bodyshop, q);
+
+ CreateRRLogEvent(socket, "SILLY", "Multi Customer Search - raw combined search", { response: multiResponse });
+
if (fromVin) {
- const blocks = Array.isArray(res?.data) ? res.data : Array.isArray(res) ? res : [];
- ownersSet = ownersFromVinBlocks(blocks, job?.v_vin);
+ const multiBlocks = Array.isArray(multiResponse?.data) ? multiResponse.data : [];
+ ownersSet = ownersFromVinBlocks(multiBlocks, job?.v_vin);
try {
await redisHelpers.setSessionTransactionData(
socket.id,
getTransactionType(job.id),
RRCacheEnums.VINCandidates,
- blocks,
+ multiBlocks,
defaultRRTTL
);
} catch {
@@ -194,7 +198,7 @@ const rrMultiCustomerSearch = async ({ bodyshop, job, socket, redisHelpers }) =>
}
}
- const norm = normalizeCustomerCandidates(res, { ownersSet });
+ const norm = normalizeCustomerCandidates(multiResponse, { ownersSet });
merged.push(...norm);
} catch (e) {
CreateRRLogEvent(socket, "WARN", "Multi-search subquery failed", { kind: q.kind, error: e.message });
@@ -220,24 +224,29 @@ const registerRREvents = ({ socket, redisHelpers }) => {
CreateRRLogEvent(socket, "DEBUG", "rr-lookup-combined: begin", { jobid, params });
- const res = await rrCombinedSearch(bodyshop, params || {});
+ const response = await rrCombinedSearch(bodyshop, params || {});
+
+ CreateRRLogEvent(socket, "SILLY", "rr-lookup-combined: received response", {
+ response
+ });
+
let ownersSet = null;
if ((params?.kind || "").toLowerCase() === "vin") {
- const blocks = Array.isArray(res?.data) ? res.data : Array.isArray(res) ? res : [];
+ const blocks = Array.isArray(response?.data) ? response.data : [];
ownersSet = ownersFromVinBlocks(blocks);
}
- const normalized = sortVehicleOwnerFirst(normalizeCustomerCandidates(res, { ownersSet }));
+ const normalized = sortVehicleOwnerFirst(normalizeCustomerCandidates(response, { 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,
- res
+ count: decorated.length
});
} catch (e) {
CreateRRLogEvent(socket, "ERROR", "RR combined lookup error", { error: e.message, jobid });
@@ -291,10 +300,13 @@ const registerRREvents = ({ socket, redisHelpers }) => {
// 2) Fetch + cache when no cache or forced refresh
if (!result) {
- const live = await rrGetAdvisors(bodyshop, { departmentType: requestedDept });
- result = Array.isArray(live) ? live : [];
+ const getAdvisorsCall = await rrGetAdvisors(bodyshop, { departmentType: requestedDept });
+ result = Array.isArray(getAdvisorsCall?.data) ? getAdvisorsCall.data : [];
try {
await redisHelpers.setProviderCache(ns, field, result, ADVISORS_CACHE_TTL);
+ CreateRRLogEvent(socket, "SILLY", "rr-get-advisors: fetched live data", {
+ getAdvisorsCall
+ });
CreateRRLogEvent(socket, "DEBUG", "rr-get-advisors: cache populated", {
ns,
field,
@@ -440,22 +452,25 @@ const registerRREvents = ({ socket, redisHelpers }) => {
try {
const vehQ = makeVehicleSearchPayloadFromJob(job);
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 : [];
+ const vinResponse = await rrCombinedSearch(bodyshop, vehQ);
+
+ CreateRRLogEvent(socket, "SILLY", `VIN owner pre-check response`, { response: vinResponse });
+
+ const vinBlocks = Array.isArray(vinResponse?.data) ? vinResponse.data : [];
try {
await redisHelpers.setSessionTransactionData(
socket.id,
ns,
RRCacheEnums.VINCandidates,
- blocksVin,
+ vinBlocks,
defaultRRTTL
);
} catch {
//
}
- const ownersSet = ownersFromVinBlocks(blocksVin, job.v_vin);
+ const ownersSet = ownersFromVinBlocks(vinBlocks, job.v_vin);
if (ownersSet?.size) {
const sel = String(selectedCustNo);
diff --git a/server/rr/rr-service-vehicles.js b/server/rr/rr-service-vehicles.js
index 877a081c7..bdfa4c78d 100644
--- a/server/rr/rr-service-vehicles.js
+++ b/server/rr/rr-service-vehicles.js
@@ -1,5 +1,5 @@
-const RRLogger = require("./rr-logger");
const { buildClientAndOpts, rrCombinedSearch } = require("./rr-lookup");
+const CreateRRLogEvent = require("./rr-logger-event");
/**
* Pick and normalize VIN from inputs
@@ -36,7 +36,7 @@ const pickCustNo = ({ selectedCustomerNo, custNo, customerNo }) => {
* @returns {Set}
*/
const ownersFromCombined = (res, wantedVin) => {
- const blocks = Array.isArray(res?.data) ? res.data : Array.isArray(res) ? res : [];
+ const blocks = Array.isArray(res?.data) ? res.data : [];
const owners = new Set();
for (const blk of blocks) {
const serv = Array.isArray(blk?.ServVehicle) ? blk.ServVehicle : [];
@@ -72,7 +72,6 @@ const isAlreadyExistsError = (e) => {
* - selectedCustomerNo / custNo / customerNo
* - license (optional)
* - socket (for logging)
- * - logNs (namespace for logs)
*
* Returns: { created:boolean, exists:boolean, vin, customerNo, svId?, status? }
*/
@@ -87,12 +86,9 @@ const ensureRRServiceVehicle = async (args = {}) => {
custNo,
customerNo,
license,
- socket,
- logNs = "rr-service-vehicles"
+ socket
} = args;
- const log = RRLogger(socket, { ns: logNs });
-
// Build/derive essentials
let client = inClient;
let routing = inRouting;
@@ -116,17 +112,24 @@ const ensureRRServiceVehicle = async (args = {}) => {
try {
let owners = new Set();
if (bodyshop) {
- const comb = await rrCombinedSearch(bodyshop, { kind: "vin", vin: vinStr, maxResults: 50 });
- owners = ownersFromCombined(comb, vinStr);
+ const combinedSearchResponse = await rrCombinedSearch(bodyshop, { kind: "vin", vin: vinStr, maxResults: 50 });
+
+ CreateRRLogEvent(socket, "silly", "{SV} Preflight combined search by VIN: raw response", {
+ response: combinedSearchResponse
+ });
+
+ owners = ownersFromCombined(combinedSearchResponse, vinStr);
}
// Short-circuit: VIN exists anywhere -> don't try to insert
if (owners.size > 0) {
const ownedBySame = owners.has(custNoStr);
- log(ownedBySame ? "info" : "warn", "{SV} VIN already present in RR; skipping insert", {
+
+ CreateRRLogEvent(socket, ownedBySame ? "info" : "warn", "{SV} VIN already present in RR; skipping insert", {
vin: vinStr,
selectedCustomerNo: custNoStr,
owners: Array.from(owners)
});
+
return {
created: false,
exists: true,
@@ -137,7 +140,10 @@ const ensureRRServiceVehicle = async (args = {}) => {
}
} catch (e) {
// Preflight shouldn't be fatal; log and continue to insert (idempotency will still be handled)
- log("warn", "{SV} VIN preflight lookup failed; continuing to insert", { vin: vinStr, error: e?.message });
+ CreateRRLogEvent(socket, "WARN", "{SV} VIN preflight lookup failed; continuing to insert", {
+ vin: vinStr,
+ error: e?.message
+ });
}
// --- Attempt insert (idempotent) ---
@@ -163,30 +169,26 @@ const ensureRRServiceVehicle = async (args = {}) => {
}
};
- log("debug", "{SV} Inserting service vehicle", {
+ CreateRRLogEvent(socket, "info", "{SV} Inserting service vehicle", {
vin: vinStr,
selectedCustomerNo: custNoStr,
payloadShape: Object.keys(insertPayload).filter((k) => insertPayload[k] != null)
});
- // Be tolerant to method name differences
- const fnName =
- typeof client.insertServiceVehicle === "function"
- ? "insertServiceVehicle"
- : typeof client.serviceVehicleInsert === "function"
- ? "serviceVehicleInsert"
- : null;
-
- if (!fnName) {
- throw new Error("RR client does not expose insertServiceVehicle/serviceVehicleInsert");
- }
-
try {
- const res = await client[fnName](insertPayload, insertOpts);
+ const res = await client.insertServiceVehicle(insertPayload, insertOpts);
+
+ CreateRRLogEvent(socket, "silly", "{SV} insertServiceVehicle: raw response", { res });
+
const data = res?.data ?? {};
const svId = data?.dmsRecKey || data?.svId || undefined;
- log("info", "{SV} insertServiceVehicle: success", { vin: vinStr, customerNo: custNoStr, svId });
+ CreateRRLogEvent(socket, "info", "{SV} insertServiceVehicle: success", {
+ vin: vinStr,
+ customerNo: custNoStr,
+ svId
+ });
+
return {
created: true,
exists: false,
@@ -198,12 +200,13 @@ const ensureRRServiceVehicle = async (args = {}) => {
} catch (e) {
if (isAlreadyExistsError(e)) {
// Treat as idempotent success
- log("warn", "{SV} insertServiceVehicle: already exists; treating as success", {
+ CreateRRLogEvent(socket, "warn", "{SV} insertServiceVehicle: already exists; treating as success", {
vin: vinStr,
customerNo: custNoStr,
code: e?.code,
status: e?.meta?.status || e?.status
});
+
return {
created: false,
exists: true,
@@ -212,11 +215,13 @@ const ensureRRServiceVehicle = async (args = {}) => {
status: e?.meta?.status || e?.status
};
}
- log("error", "{SV} insertServiceVehicle: failure", {
+
+ CreateRRLogEvent(socket, "error", "{SV} insertServiceVehicle: failure", {
message: e?.message,
code: e?.code,
status: e?.meta?.status || e?.status
});
+
throw e;
}
};
diff --git a/server/rr/rr-utils.js b/server/rr/rr-utils.js
index b39d686e4..dd898b12a 100644
--- a/server/rr/rr-utils.js
+++ b/server/rr/rr-utils.js
@@ -52,7 +52,7 @@ const makeVehicleSearchPayloadFromJob = (job) => {
* @returns {any[]}
*/
const normalizeCustomerCandidates = (res, { ownersSet = null } = {}) => {
- const blocks = Array.isArray(res?.data) ? res.data : Array.isArray(res) ? res : [];
+ const blocks = Array.isArray(res?.data) ? res.data : [];
const out = [];
const pickAddr = (addrArr) => {