feature/IO-3357-Reynolds-and-Reynolds-DMS-API-Integration - Expanded Logs / Formatting change
This commit is contained in:
@@ -6,10 +6,7 @@ const CdkCalculateAllocations = require("../cdk/cdk-calculate-allocations").defa
|
||||
const { createRRCustomer } = require("./rr-customers");
|
||||
const { ensureRRServiceVehicle } = require("./rr-service-vehicles");
|
||||
const { classifyRRVendorError } = require("./rr-errors");
|
||||
|
||||
// NEW: export logs (success/failure) parity with Fortellis/PBS
|
||||
const { markRRExportSuccess, insertRRFailedExportLog } = require("./rr-export-logs");
|
||||
|
||||
const {
|
||||
makeVehicleSearchPayloadFromJob,
|
||||
ownersFromVinBlocks,
|
||||
@@ -19,7 +16,6 @@ const {
|
||||
defaultRRTTL,
|
||||
RRCacheEnums
|
||||
} = require("./rr-utils");
|
||||
|
||||
const { GraphQLClient } = require("graphql-request");
|
||||
const queries = require("../graphql-client/queries");
|
||||
|
||||
@@ -36,9 +32,8 @@ const ADVISORS_CACHE_TTL = 7 * 24 * 60 * 60; // seconds
|
||||
* @param job
|
||||
* @returns {*|null}
|
||||
*/
|
||||
function resolveJobId(explicit, payload, job) {
|
||||
return explicit || payload?.jobId || payload?.jobid || job?.id || job?.jobId || job?.jobid || null;
|
||||
}
|
||||
const resolveJobId = (explicit, payload, job) =>
|
||||
explicit || payload?.jobId || payload?.jobid || job?.id || job?.jobId || job?.jobid || null;
|
||||
|
||||
/**
|
||||
* Resolve VIN from tx/job shapes
|
||||
@@ -46,18 +41,15 @@ function resolveJobId(explicit, payload, job) {
|
||||
* @param job
|
||||
* @returns {*|null}
|
||||
*/
|
||||
function resolveVin({ tx, job }) {
|
||||
// Prefer cached tx vin (if we made one), then common job shapes (v_vin for our schema)
|
||||
return tx?.jobData?.vin || job?.v_vin || null;
|
||||
}
|
||||
const resolveVin = ({ tx, job }) => tx?.jobData?.vin || job?.v_vin || null;
|
||||
|
||||
/**
|
||||
* Sort vehicle owners first in the list, preserving original order otherwise.
|
||||
* @param list
|
||||
* @returns {*}
|
||||
*/
|
||||
function sortVehicleOwnerFirst(list) {
|
||||
return list
|
||||
const sortVehicleOwnerFirst = (list) =>
|
||||
list
|
||||
.map((v, i) => ({ v, i }))
|
||||
.sort((a, b) => {
|
||||
const ao = a.v?.isVehicleOwner ? 1 : 0;
|
||||
@@ -66,15 +58,13 @@ function sortVehicleOwnerFirst(list) {
|
||||
return a.i - b.i;
|
||||
})
|
||||
.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
|
||||
* Merge customer candidates by custNo, combining isVehicleOwner flags and filling missing fields.
|
||||
* @param items
|
||||
* @returns {any[]}
|
||||
*/
|
||||
function mergeByCustNo(items = []) {
|
||||
const mergeByCustNo = (items = []) => {
|
||||
const byId = new Map();
|
||||
for (const c of items) {
|
||||
const id = (c?.custNo || "").trim();
|
||||
@@ -93,7 +83,7 @@ function mergeByCustNo(items = []) {
|
||||
}
|
||||
}
|
||||
return Array.from(byId.values());
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get session data or socket fallback
|
||||
@@ -101,7 +91,7 @@ function mergeByCustNo(items = []) {
|
||||
* @param socket
|
||||
* @returns {Promise<{bodyshopId: *, email: *, sess: null}>}
|
||||
*/
|
||||
async function getSessionOrSocket(redisHelpers, socket) {
|
||||
const getSessionOrSocket = async (redisHelpers, socket) => {
|
||||
let sess = null;
|
||||
try {
|
||||
sess = await redisHelpers.getSessionData(socket.id);
|
||||
@@ -112,7 +102,7 @@ async function getSessionOrSocket(redisHelpers, socket) {
|
||||
const email = sess?.email ?? socket.user?.email;
|
||||
if (!bodyshopId) throw new Error("No bodyshopId (session/socket)");
|
||||
return { bodyshopId, email, sess };
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Fetch bodyshop data for socket
|
||||
@@ -120,7 +110,7 @@ async function getSessionOrSocket(redisHelpers, socket) {
|
||||
* @param socket
|
||||
* @returns {Promise<{id: string, intellipay_config: {payment_map: {amex: string}}}|{id: string, intellipay_config: null}|*>}
|
||||
*/
|
||||
async function getBodyshopForSocket({ bodyshopId, socket }) {
|
||||
const getBodyshopForSocket = async ({ bodyshopId, socket }) => {
|
||||
const endpoint = process.env.GRAPHQL_ENDPOINT;
|
||||
if (!endpoint) throw new Error("GRAPHQL_ENDPOINT not configured");
|
||||
const token = (socket?.data && socket.data.authToken) || (socket?.handshake?.auth && socket.handshake.auth.token);
|
||||
@@ -131,12 +121,16 @@ async function getBodyshopForSocket({ bodyshopId, socket }) {
|
||||
const bodyshop = res?.bodyshops_by_pk;
|
||||
if (!bodyshop) throw new Error(`Bodyshop not found: ${bodyshopId}`);
|
||||
return bodyshop;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Build advisors cache namespace + field (per bodyshop + routing + department)
|
||||
* Build advisors cache namespace and field
|
||||
* @param bodyshopId
|
||||
* @param routing
|
||||
* @param departmentType
|
||||
* @returns {{ns: string, field: string}}
|
||||
*/
|
||||
function buildAdvisorsCacheNS({ bodyshopId, routing, departmentType = "B" }) {
|
||||
const buildAdvisorsCacheNS = ({ bodyshopId, routing, departmentType = "B" }) => {
|
||||
const dealer = routing?.dealerNumber || "unknown";
|
||||
const store = routing?.storeNumber || "none";
|
||||
const area = routing?.areaNumber || "none";
|
||||
@@ -145,12 +139,17 @@ function buildAdvisorsCacheNS({ bodyshopId, routing, departmentType = "B" }) {
|
||||
ns: `rr:advisors:${bodyshopId}:${dealer}:${store}:${area}`,
|
||||
field: `dept:${dept}`
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* VIN + Full Name merge (export flow)
|
||||
* Run multi-query customer search (Full Name + VIN) and merge results.
|
||||
* @param bodyshop
|
||||
* @param job
|
||||
* @param socket
|
||||
* @param redisHelpers
|
||||
* @returns {Promise<*|*[]>}
|
||||
*/
|
||||
async function rrMultiCustomerSearch({ bodyshop, job, socket, redisHelpers }) {
|
||||
const rrMultiCustomerSearch = async ({ bodyshop, job, socket, redisHelpers }) => {
|
||||
const queriesList = [];
|
||||
|
||||
// 1) Full Name (preferred)
|
||||
@@ -207,11 +206,14 @@ async function rrMultiCustomerSearch({ bodyshop, job, socket, redisHelpers }) {
|
||||
const deduped = mergeByCustNo(merged);
|
||||
|
||||
return sortVehicleOwnerFirst(deduped);
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------- register handlers ----------------
|
||||
function registerRREvents({ socket, redisHelpers }) {
|
||||
// ---------- Lookup passthrough ----------
|
||||
/**
|
||||
* Register RR socket events
|
||||
* @param socket
|
||||
* @param redisHelpers
|
||||
*/
|
||||
const registerRREvents = ({ socket, redisHelpers }) => {
|
||||
socket.on("rr-lookup-combined", async ({ jobid, params } = {}, cb) => {
|
||||
try {
|
||||
const { bodyshopId } = await getSessionOrSocket(redisHelpers, socket);
|
||||
@@ -242,7 +244,6 @@ function registerRREvents({ socket, redisHelpers }) {
|
||||
}
|
||||
});
|
||||
|
||||
// ---------- Advisors (cached) ----------
|
||||
socket.on("rr-get-advisors", async (args = {}, ack) => {
|
||||
const refresh = !!args?.refresh;
|
||||
const requestedDept = (args?.departmentType || "B").toUpperCase();
|
||||
@@ -317,8 +318,6 @@ function registerRREvents({ socket, redisHelpers }) {
|
||||
}
|
||||
});
|
||||
|
||||
// ================= Fortellis-style two-step export (RR only) =================
|
||||
// 1) Stage export -> search (Full Name + VIN) -> emit rr-select-customer
|
||||
socket.on("rr-export-job", async ({ jobid, jobId, txEnvelope } = {}) => {
|
||||
const rid = resolveJobId(jobid || jobId, { jobId, jobid }, null);
|
||||
try {
|
||||
@@ -385,7 +384,6 @@ function registerRREvents({ socket, redisHelpers }) {
|
||||
}
|
||||
});
|
||||
|
||||
// 2) Selection (or create) -> ensure vehicle -> CREATE RO (do not mark exported)
|
||||
socket.on("rr-selected-customer", async ({ jobid, jobId, selectedCustomerId, custNo, create } = {}, ack) => {
|
||||
const rid = resolveJobId(jobid || jobId, { jobid, jobId }, null);
|
||||
let bodyshop = null;
|
||||
@@ -701,7 +699,6 @@ function registerRREvents({ socket, redisHelpers }) {
|
||||
}
|
||||
});
|
||||
|
||||
// 3) Finalize -> updateRepairOrder(finalUpdate: "Y") -> mark exported
|
||||
socket.on("rr-finalize-repair-order", async ({ jobid, jobId } = {}, ack) => {
|
||||
const rid = resolveJobId(jobid || jobId, { jobid, jobId }, null);
|
||||
let bodyshop = null;
|
||||
@@ -847,7 +844,6 @@ function registerRREvents({ socket, redisHelpers }) {
|
||||
}
|
||||
});
|
||||
|
||||
// ---------- Allocations (parity) ----------
|
||||
socket.on("rr-calculate-allocations", async (jobid, cb) => {
|
||||
try {
|
||||
CreateRRLogEvent(socket, "DEBUG", "rr-calculate-allocations: begin", { jobid });
|
||||
@@ -860,6 +856,6 @@ function registerRREvents({ socket, redisHelpers }) {
|
||||
cb?.({ ok: false, error: e.message });
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = registerRREvents;
|
||||
|
||||
Reference in New Issue
Block a user