From d45d557a81298be5161bcfc38f8ef3c3e6bd7ed9 Mon Sep 17 00:00:00 2001 From: Dave Date: Fri, 7 Nov 2025 16:09:18 -0500 Subject: [PATCH] feature/IO-3357-Reynolds-and-Reynolds-DMS-API-Integration -Dedupe --- server/rr/rr-lookup.js | 28 ---------- .../web-sockets/rr-register-socket-events.js | 52 ++++++++++++------- 2 files changed, 32 insertions(+), 48 deletions(-) diff --git a/server/rr/rr-lookup.js b/server/rr/rr-lookup.js index 77112c5da..14e81ebaf 100644 --- a/server/rr/rr-lookup.js +++ b/server/rr/rr-lookup.js @@ -160,36 +160,8 @@ async function rrGetAdvisors(bodyshop, args = {}) { return res?.data ?? res; } -/** - * Parts lookup (graceful if the underlying lib exposes a different name) - * @param bodyshop - * @param args - common fields like { partNumber, description, make, model, year } - */ -async function rrGetParts(bodyshop, args = {}) { - const { client, opts } = buildClientAndOpts(bodyshop); - const payload = { - partNumber: args.partNumber ?? args.partNo ?? args.number ?? undefined, - description: args.description ?? undefined, - make: args.make ?? undefined, - model: args.model ?? undefined, - year: args.year ?? undefined - }; - - // Try common method names. If none exist, return an empty list to avoid crashes. - if (typeof client.getParts === "function") { - const res = await client.getParts(payload, opts); - return res?.data ?? res; - } - if (typeof client.getPartNumbers === "function") { - const res = await client.getPartNumbers(payload, opts); - return res?.data ?? res; - } - return []; -} - module.exports = { rrCombinedSearch, rrGetAdvisors, - rrGetParts, buildClientAndOpts }; diff --git a/server/web-sockets/rr-register-socket-events.js b/server/web-sockets/rr-register-socket-events.js index a43d97426..d6bd27680 100644 --- a/server/web-sockets/rr-register-socket-events.js +++ b/server/web-sockets/rr-register-socket-events.js @@ -2,7 +2,7 @@ // RR events aligned to Fortellis flow with Fortellis-style logging via CreateRRLogEvent const CreateRRLogEvent = require("../rr/rr-logger-event"); -const { rrCombinedSearch, rrGetAdvisors, rrGetParts, buildClientAndOpts } = require("../rr/rr-lookup"); +const { rrCombinedSearch, rrGetAdvisors, buildClientAndOpts } = require("../rr/rr-lookup"); const { QueryJobData } = require("../rr/rr-job-helpers"); const { exportJobToRR } = require("../rr/rr-job-export"); const CdkCalculateAllocations = require("../cdk/cdk-calculate-allocations").default; @@ -46,6 +46,33 @@ function sortVehicleOwnerFirst(list) { .map(({ v }) => v); } +/** + * NEW: merge candidates coming from multiple queries (name + vin) by custNo. + * - keeps first non-empty name + * - preserves/ORs vinOwner/isVehicleOwner + * - keeps first non-empty address + */ +function mergeByCustNo(items = []) { + const byId = new Map(); + for (const c of items) { + const id = (c?.custNo || "").trim(); + if (!id) continue; + const prev = byId.get(id); + if (!prev) { + byId.set(id, { ...c, isVehicleOwner: !!(c.vinOwner || c.isVehicleOwner) }); + } else { + byId.set(id, { + ...prev, + name: prev.name || c.name, + isVehicleOwner: !!(prev.isVehicleOwner || prev.vinOwner || c.isVehicleOwner || c.vinOwner), + vinOwner: !!(prev.vinOwner || c.vinOwner || prev.isVehicleOwner || c.isVehicleOwner), + address: prev.address || c.address + }); + } + } + return Array.from(byId.values()); +} + async function getSessionOrSocket(redisHelpers, socket) { let sess = null; try { @@ -142,7 +169,10 @@ async function rrMultiCustomerSearch({ bodyshop, job, socket, redisHelpers }) { } } - return sortVehicleOwnerFirst(merged); + // NEW: dedupe across queries (name + vin) + const deduped = mergeByCustNo(merged); + + return sortVehicleOwnerFirst(deduped); } // ---------------- register handlers ---------------- @@ -253,24 +283,6 @@ function registerRREvents({ socket, redisHelpers }) { } }); - // ---------- Parts ---------- - socket.on("rr-get-parts", async (args = {}, ack) => { - try { - const { bodyshopId } = await getSessionOrSocket(redisHelpers, socket); - const bodyshop = await getBodyshopForSocket({ bodyshopId, socket }); - CreateRRLogEvent(socket, "DEBUG", "rr-get-parts: begin", { args }); - const res = await rrGetParts(bodyshop, args); - ack?.({ ok: true, result: res }); - socket.emit("rr-get-parts:result", res); - CreateRRLogEvent(socket, "DEBUG", "rr-get-parts: success", { - count: Array.isArray(res) ? res.length : undefined - }); - } catch (err) { - CreateRRLogEvent(socket, "ERROR", "rr-get-parts: failed", { error: err?.message }); - ack?.({ ok: false, error: err?.message || "get parts failed" }); - } - }); - // ================= Fortellis-style two-step export ================= // 1) Stage export -> search (Full Name + VIN) -> emit rr-select-customer socket.on("rr-export-job", async ({ jobid, jobId, txEnvelope } = {}) => {