feature/IO-3357-Reynolds-and-Reynolds-DMS-API-Integration - Enhance logging

This commit is contained in:
Dave
2025-11-13 15:39:29 -05:00
parent 9c2c0b665d
commit 4c250f6189
9 changed files with 98 additions and 128 deletions

View File

@@ -129,6 +129,8 @@ const normalizeLog = (input) => {
*/ */
const logLevelColor = (level) => { const logLevelColor = (level) => {
switch ((level || "").toUpperCase()) { switch ((level || "").toUpperCase()) {
case "SILLY":
return "purple";
case "DEBUG": case "DEBUG":
return "orange"; return "orange";
case "INFO": case "INFO":

View File

@@ -412,6 +412,7 @@ export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader, inse
setActiveLogLevel(value); setActiveLogLevel(value);
}} }}
> >
<Select.Option key="SILLY">SILLY</Select.Option>
<Select.Option key="DEBUG">DEBUG</Select.Option> <Select.Option key="DEBUG">DEBUG</Select.Option>
<Select.Option key="INFO">INFO</Select.Option> <Select.Option key="INFO">INFO</Select.Option>
<Select.Option key="WARN">WARN</Select.Option> <Select.Option key="WARN">WARN</Select.Option>

View File

@@ -1,6 +1,6 @@
const { RRClient } = require("./lib/index.cjs"); const { RRClient } = require("./lib/index.cjs");
const { getRRConfigFromBodyshop } = require("./rr-config"); const { getRRConfigFromBodyshop } = require("./rr-config");
const RRLogger = require("./rr-logger"); const CreateRRLogEvent = require("./rr-logger-event");
const InstanceManager = require("../utils/instanceMgr").default; const InstanceManager = require("../utils/instanceMgr").default;
/** /**
@@ -206,30 +206,35 @@ const buildCustomerPayloadFromJob = (job, overrides = {}) => {
* Maps data.dmsRecKey -> customerNo for compatibility with existing callers. * Maps data.dmsRecKey -> customerNo for compatibility with existing callers.
*/ */
const createRRCustomer = async ({ bodyshop, job, overrides = {}, socket }) => { const createRRCustomer = async ({ bodyshop, job, overrides = {}, socket }) => {
const log = RRLogger(socket, { ns: "rr" });
const { client, opts } = buildClientAndOpts(bodyshop); const { client, opts } = buildClientAndOpts(bodyshop);
const payload = buildCustomerPayloadFromJob(job, overrides); const payload = buildCustomerPayloadFromJob(job, overrides);
let res; let response;
try { try {
const safePayload = sanitizeRRCustomerPayload(payload); 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) { } 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; throw e;
} }
const data = res?.data ?? res; const data = response?.data ?? response;
const trx = res?.statusBlocks?.transaction; const trx = response?.statusBlocks?.transaction;
let customerNo = data?.dmsRecKey; let customerNo = data?.dmsRecKey;
if (!customerNo) { if (!customerNo) {
log("error", "RR insertCustomer returned no dmsRecKey/custNo", { CreateRRLogEvent(socket, "ERROR", "RR insertCustomer returned no dmsRecKey/custNo", {
status: trx?.status, status: trx?.status,
statusCode: trx?.statusCode, statusCode: trx?.statusCode,
message: trx?.message, message: trx?.message,
data data
}); });
throw new Error( throw new Error(
`RR insertCustomer returned no dmsRecKey (status=${trx?.status ?? "?"} code=${trx?.statusCode ?? "?"}${ `RR insertCustomer returned no dmsRecKey (status=${trx?.status ?? "?"} code=${trx?.statusCode ?? "?"}${
trx?.message ? ` msg=${trx.message}` : "" trx?.message ? ` msg=${trx.message}` : ""

View File

@@ -1,16 +1,15 @@
const { buildRRRepairOrderPayload } = require("./rr-job-helpers"); const { buildRRRepairOrderPayload } = require("./rr-job-helpers");
const { buildClientAndOpts } = require("./rr-lookup"); const { buildClientAndOpts } = require("./rr-lookup");
const { ensureRRServiceVehicle } = require("./rr-service-vehicles"); 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). * Export a job to Reynolds & Reynolds as a Repair Order (create or update).
* @param args * @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 exportJobToRR = async (args) => {
const { bodyshop, job, advisorNo, selectedCustomer, existing, socket } = 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 (!bodyshop) throw new Error("exportJobToRR: bodyshop is required");
if (!job) throw new Error("exportJobToRR: job is required"); if (!job) throw new Error("exportJobToRR: job is required");
@@ -48,9 +47,9 @@ const exportJobToRR = async (args) => {
socket socket
}); });
svId = svRes?.svId || null; 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) { } 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" // Canonical update key is "roNo" (prefer DMS RO number); accept fallbacks from "existing"
const roNoForUpdate = existing?.roNo || existing?.dmsRoNo || existing?.dmsRepairOrderId || null; 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.updateRepairOrder({ ...payload, roNo: String(roNoForUpdate) }, finalOpts) // ✅ use roNo on update
: await client.createRepairOrder(payload, finalOpts); : 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; const roStatus = data?.roStatus || null;
// Extract canonical roNo you'll need for finalize step // Extract canonical roNo you'll need for finalize step
const roNo = data?.dmsRoNo ?? data?.outsdRoNo ?? roStatus?.dmsRoNo ?? null; const roNo = data?.dmsRoNo ?? data?.outsdRoNo ?? roStatus?.dmsRoNo ?? null;
return { return {
success: rrRes?.success === true || roStatus?.status === "Success", success: response?.success === true || roStatus?.status === "Success",
data, data,
roStatus, roStatus,
statusBlocks: rrRes?.statusBlocks || [], statusBlocks: response?.statusBlocks || [],
xml: rrRes?.xml,
parsed: rrRes?.parsed,
customerNo: String(selected), customerNo: String(selected),
svId, svId,
roNo roNo
@@ -90,11 +91,10 @@ const exportJobToRR = async (args) => {
/** /**
* Finalize an RR Repair Order by sending finalUpdate: "Y". * Finalize an RR Repair Order by sending finalUpdate: "Y".
* @param args * @param args
* @returns {Promise<{success, data: *, roStatus: *, statusBlocks, xml: *, parsed: any}>} * @returns {Promise<{success, data: *, roStatus: *, statusBlocks}>}
*/ */
const finalizeRRRepairOrder = async (args) => { const finalizeRRRepairOrder = async (args) => {
const { bodyshop, job, advisorNo, customerNo, roNo, vin, socket } = 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 (!bodyshop) throw new Error("finalizeRRRepairOrder: bodyshop is required");
if (!job) throw new Error("finalizeRRRepairOrder: job is required"); if (!job) throw new Error("finalizeRRRepairOrder: job is required");
@@ -142,7 +142,7 @@ const finalizeRRRepairOrder = async (args) => {
estimate: { estimateType: "Final" } estimate: { estimateType: "Final" }
}; };
log("info", "RR finalize updateRepairOrder", { CreateRRLogEvent(socket, "INFO", "Finalizing RR Repair Order", {
roNo: roNoToSend, roNo: roNoToSend,
outsdRoNo: String(externalRo), outsdRoNo: String(externalRo),
customerNo: String(customerNo), customerNo: String(customerNo),
@@ -157,9 +157,7 @@ const finalizeRRRepairOrder = async (args) => {
success: rrRes?.success === true || roStatus?.status === "Success", success: rrRes?.success === true || roStatus?.status === "Success",
data, data,
roStatus, roStatus,
statusBlocks: rrRes?.statusBlocks || [], statusBlocks: rrRes?.statusBlocks || []
xml: rrRes?.xml,
parsed: rrRes?.parsed
}; };
}; };

View File

@@ -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 `<Buffer len=${val.length}>`;
}
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;

View File

@@ -136,7 +136,7 @@ const rrCombinedSearch = async (bodyshop, args = {}) => {
const { client, opts } = buildClientAndOpts(bodyshop); const { client, opts } = buildClientAndOpts(bodyshop);
const payload = toCombinedSearchPayload(args); const payload = toCombinedSearchPayload(args);
const res = await client.combinedSearch(payload, opts); 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 advisorNumber: args.advisorNumber ? String(args.advisorNumber) : undefined
}; };
const res = await client.getAdvisors(payload, opts); return client.getAdvisors(payload, opts);
return res?.data ?? res;
}; };
module.exports = { module.exports = {

View File

@@ -177,16 +177,20 @@ const rrMultiCustomerSearch = async ({ bodyshop, job, socket, redisHelpers }) =>
for (const { q, fromVin } of queriesList) { for (const { q, fromVin } of queriesList) {
try { try {
CreateRRLogEvent(socket, "DEBUG", `{RR-SEARCH} Executing ${q.kind} query`, { q }); 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) { if (fromVin) {
const blocks = Array.isArray(res?.data) ? res.data : Array.isArray(res) ? res : []; const multiBlocks = Array.isArray(multiResponse?.data) ? multiResponse.data : [];
ownersSet = ownersFromVinBlocks(blocks, job?.v_vin); ownersSet = ownersFromVinBlocks(multiBlocks, job?.v_vin);
try { try {
await redisHelpers.setSessionTransactionData( await redisHelpers.setSessionTransactionData(
socket.id, socket.id,
getTransactionType(job.id), getTransactionType(job.id),
RRCacheEnums.VINCandidates, RRCacheEnums.VINCandidates,
blocks, multiBlocks,
defaultRRTTL defaultRRTTL
); );
} catch { } 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); merged.push(...norm);
} catch (e) { } catch (e) {
CreateRRLogEvent(socket, "WARN", "Multi-search subquery failed", { kind: q.kind, error: e.message }); 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 }); 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; let ownersSet = null;
if ((params?.kind || "").toLowerCase() === "vin") { 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); ownersSet = ownersFromVinBlocks(blocks);
} }
const normalized = sortVehicleOwnerFirst(normalizeCustomerCandidates(res, { ownersSet })); const normalized = sortVehicleOwnerFirst(normalizeCustomerCandidates(response, { ownersSet }));
const rid = resolveJobId(jobid, { jobid }, null); const rid = resolveJobId(jobid, { jobid }, null);
const decorated = normalized.map((c) => (c.vinOwner != null ? c : { ...c, vinOwner: !!c.isVehicleOwner })); const decorated = normalized.map((c) => (c.vinOwner != null ? c : { ...c, vinOwner: !!c.isVehicleOwner }));
cb?.({ jobid: rid, data: decorated }); cb?.({ jobid: rid, data: decorated });
socket.emit("rr-select-customer", decorated); socket.emit("rr-select-customer", decorated);
CreateRRLogEvent(socket, "DEBUG", "rr-lookup-combined: emitted rr-select-customer", { CreateRRLogEvent(socket, "DEBUG", "rr-lookup-combined: emitted rr-select-customer", {
count: decorated.length, count: decorated.length
res
}); });
} catch (e) { } catch (e) {
CreateRRLogEvent(socket, "ERROR", "RR combined lookup error", { error: e.message, jobid }); 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 // 2) Fetch + cache when no cache or forced refresh
if (!result) { if (!result) {
const live = await rrGetAdvisors(bodyshop, { departmentType: requestedDept }); const getAdvisorsCall = await rrGetAdvisors(bodyshop, { departmentType: requestedDept });
result = Array.isArray(live) ? live : []; result = Array.isArray(getAdvisorsCall?.data) ? getAdvisorsCall.data : [];
try { try {
await redisHelpers.setProviderCache(ns, field, result, ADVISORS_CACHE_TTL); 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", { CreateRRLogEvent(socket, "DEBUG", "rr-get-advisors: cache populated", {
ns, ns,
field, field,
@@ -440,22 +452,25 @@ const registerRREvents = ({ socket, redisHelpers }) => {
try { try {
const vehQ = makeVehicleSearchPayloadFromJob(job); const vehQ = makeVehicleSearchPayloadFromJob(job);
if (vehQ && vehQ.kind === "vin" && job?.v_vin) { if (vehQ && vehQ.kind === "vin" && job?.v_vin) {
const resVin = await rrCombinedSearch(bodyshop, vehQ); const vinResponse = await rrCombinedSearch(bodyshop, vehQ);
const blocksVin = Array.isArray(resVin?.data) ? resVin.data : Array.isArray(resVin) ? resVin : [];
CreateRRLogEvent(socket, "SILLY", `VIN owner pre-check response`, { response: vinResponse });
const vinBlocks = Array.isArray(vinResponse?.data) ? vinResponse.data : [];
try { try {
await redisHelpers.setSessionTransactionData( await redisHelpers.setSessionTransactionData(
socket.id, socket.id,
ns, ns,
RRCacheEnums.VINCandidates, RRCacheEnums.VINCandidates,
blocksVin, vinBlocks,
defaultRRTTL defaultRRTTL
); );
} catch { } catch {
// //
} }
const ownersSet = ownersFromVinBlocks(blocksVin, job.v_vin); const ownersSet = ownersFromVinBlocks(vinBlocks, job.v_vin);
if (ownersSet?.size) { if (ownersSet?.size) {
const sel = String(selectedCustNo); const sel = String(selectedCustNo);

View File

@@ -1,5 +1,5 @@
const RRLogger = require("./rr-logger");
const { buildClientAndOpts, rrCombinedSearch } = require("./rr-lookup"); const { buildClientAndOpts, rrCombinedSearch } = require("./rr-lookup");
const CreateRRLogEvent = require("./rr-logger-event");
/** /**
* Pick and normalize VIN from inputs * Pick and normalize VIN from inputs
@@ -36,7 +36,7 @@ const pickCustNo = ({ selectedCustomerNo, custNo, customerNo }) => {
* @returns {Set<any>} * @returns {Set<any>}
*/ */
const ownersFromCombined = (res, wantedVin) => { 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(); const owners = new Set();
for (const blk of blocks) { for (const blk of blocks) {
const serv = Array.isArray(blk?.ServVehicle) ? blk.ServVehicle : []; const serv = Array.isArray(blk?.ServVehicle) ? blk.ServVehicle : [];
@@ -72,7 +72,6 @@ const isAlreadyExistsError = (e) => {
* - selectedCustomerNo / custNo / customerNo * - selectedCustomerNo / custNo / customerNo
* - license (optional) * - license (optional)
* - socket (for logging) * - socket (for logging)
* - logNs (namespace for logs)
* *
* Returns: { created:boolean, exists:boolean, vin, customerNo, svId?, status? } * Returns: { created:boolean, exists:boolean, vin, customerNo, svId?, status? }
*/ */
@@ -87,12 +86,9 @@ const ensureRRServiceVehicle = async (args = {}) => {
custNo, custNo,
customerNo, customerNo,
license, license,
socket, socket
logNs = "rr-service-vehicles"
} = args; } = args;
const log = RRLogger(socket, { ns: logNs });
// Build/derive essentials // Build/derive essentials
let client = inClient; let client = inClient;
let routing = inRouting; let routing = inRouting;
@@ -116,17 +112,24 @@ const ensureRRServiceVehicle = async (args = {}) => {
try { try {
let owners = new Set(); let owners = new Set();
if (bodyshop) { if (bodyshop) {
const comb = await rrCombinedSearch(bodyshop, { kind: "vin", vin: vinStr, maxResults: 50 }); const combinedSearchResponse = await rrCombinedSearch(bodyshop, { kind: "vin", vin: vinStr, maxResults: 50 });
owners = ownersFromCombined(comb, vinStr);
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 // Short-circuit: VIN exists anywhere -> don't try to insert
if (owners.size > 0) { if (owners.size > 0) {
const ownedBySame = owners.has(custNoStr); 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, vin: vinStr,
selectedCustomerNo: custNoStr, selectedCustomerNo: custNoStr,
owners: Array.from(owners) owners: Array.from(owners)
}); });
return { return {
created: false, created: false,
exists: true, exists: true,
@@ -137,7 +140,10 @@ const ensureRRServiceVehicle = async (args = {}) => {
} }
} catch (e) { } catch (e) {
// Preflight shouldn't be fatal; log and continue to insert (idempotency will still be handled) // 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) --- // --- 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, vin: vinStr,
selectedCustomerNo: custNoStr, selectedCustomerNo: custNoStr,
payloadShape: Object.keys(insertPayload).filter((k) => insertPayload[k] != null) 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 { 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 data = res?.data ?? {};
const svId = data?.dmsRecKey || data?.svId || undefined; 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 { return {
created: true, created: true,
exists: false, exists: false,
@@ -198,12 +200,13 @@ const ensureRRServiceVehicle = async (args = {}) => {
} catch (e) { } catch (e) {
if (isAlreadyExistsError(e)) { if (isAlreadyExistsError(e)) {
// Treat as idempotent success // 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, vin: vinStr,
customerNo: custNoStr, customerNo: custNoStr,
code: e?.code, code: e?.code,
status: e?.meta?.status || e?.status status: e?.meta?.status || e?.status
}); });
return { return {
created: false, created: false,
exists: true, exists: true,
@@ -212,11 +215,13 @@ const ensureRRServiceVehicle = async (args = {}) => {
status: e?.meta?.status || e?.status status: e?.meta?.status || e?.status
}; };
} }
log("error", "{SV} insertServiceVehicle: failure", {
CreateRRLogEvent(socket, "error", "{SV} insertServiceVehicle: failure", {
message: e?.message, message: e?.message,
code: e?.code, code: e?.code,
status: e?.meta?.status || e?.status status: e?.meta?.status || e?.status
}); });
throw e; throw e;
} }
}; };

View File

@@ -52,7 +52,7 @@ const makeVehicleSearchPayloadFromJob = (job) => {
* @returns {any[]} * @returns {any[]}
*/ */
const normalizeCustomerCandidates = (res, { ownersSet = null } = {}) => { 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 out = [];
const pickAddr = (addrArr) => { const pickAddr = (addrArr) => {