feature/IO-3357-Reynolds-and-Reynolds-DMS-API-Integration - Checkpoint

This commit is contained in:
Dave
2025-10-07 16:45:06 -04:00
parent c149d457e7
commit 2ffc4b81f4
28 changed files with 2594 additions and 1642 deletions

View File

@@ -1,88 +1,143 @@
// -----------------------------------------------------------------------------
// Reynolds & Reynolds (RR) lookup helpers.
// Uses MakeRRCall + RRActions from rr-helpers, and shared response validation
// from rr-error.
//
// Whats still missing / to confirm against the Rome/RR PDFs:
// - Final query param names and value formats for the “combined search”
// (customer + vehicle), advisors directory, and parts lookup.
// - Any RR-required headers (dealer/site/location ids) — add in rr-helpers
// via the MakeRRCall default headers if needed.
// - Final success envelope checks in assertRrOk.
// -----------------------------------------------------------------------------
/**
* @file rr-lookup.js
* @description Reynolds & Reynolds lookup operations
* (Combined Search, Get Advisors, Get Parts) via SOAP/XML templates.
*/
const { MakeRRCall, RRActions } = require("./rr-helpers");
const { assertRrOk } = require("./rr-error");
const { MakeRRCall, RRActions, getDealerConfig } = require("./rr-helpers");
const { assertRrOkXml, extractRrResponseData } = require("./rr-error");
const { mapCombinedSearchVars, mapGetAdvisorsVars, mapGetPartsVars } = require("./rr-mappers");
const RRLogger = require("./rr-logger");
/**
* RR Combined Search (Customer + Vehicle).
* Combined Search
* Maps to "Search Customer Service Vehicle Combined" spec (Rome)
*
* @param {Object} deps
* @param {Socket|ExpressRequest} deps.socket
* @param {Object} deps.redisHelpers
* @param {string|number} deps.jobid - for correlation/logging only
* @param {Array<[string,string]>} [deps.params=[]]
* Example: [["vin", "1HGBH41JXMN109186"], ["lastName","DOE"]]
* @returns {Promise<any>} RR response (envelope TBD)
* @param {object} options
* @param {object} options.socket - Socket or Express req (used for auth + bodyshopId)
* @param {object} options.redisHelpers - (unused, kept for parity)
* @param {string} options.jobid - Job reference for correlation
* @param {Array<[string, string]>} [options.params] - e.g. [["VIN","1HG..."],["LastName","DOE"]]
*/
async function RrCombinedSearch({ socket, redisHelpers, jobid, params = [] }) {
const data = await MakeRRCall({
...RRActions.CombinedSearch, // GET /search/v1/customer-vehicle
requestSearchParams: params,
type: "get",
redisHelpers,
socket,
jobid
});
try {
RRLogger(socket, "info", "Starting RR Combined Search", { jobid, params });
// allowEmpty=true because searches may legitimately return 0 rows
return assertRrOk(data, { apiName: "RR Combined Search", allowEmpty: true });
const bodyshopId = socket?.bodyshopId || socket?.user?.bodyshopid;
const dealerConfig = bodyshopId ? await getDealerConfig(bodyshopId) : {};
// Build Mustache variables for server/rr/xml-templates/CombinedSearch.xml
const variables = mapCombinedSearchVars({ params, dealerConfig });
const xml = await MakeRRCall({
action: RRActions.CombinedSearch,
body: { template: "CombinedSearch", data: variables },
redisHelpers,
socket,
jobid
});
// Validate + normalize
const ok = assertRrOkXml(xml, { apiName: "RR Combined Search", allowEmpty: true });
const normalized = extractRrResponseData(ok, { action: "CombinedSearch" });
RRLogger(socket, "debug", "RR Combined Search complete", {
jobid,
count: Array.isArray(normalized) ? normalized.length : 0
});
return normalized;
} catch (error) {
RRLogger(socket, "error", `RR Combined Search failed: ${error.message}`, { jobid });
throw error;
}
}
/**
* RR Get Advisors.
* Get Advisors
* Maps to "Get Advisors Specification" (Rome)
*
* @param {Object} deps
* @param {Socket|ExpressRequest} deps.socket
* @param {Object} deps.redisHelpers
* @param {string|number} deps.jobid
* @param {Array<[string,string]>} [deps.params=[]]
* Example: [["active","true"]]
* @returns {Promise<any>} RR response (envelope TBD)
* @param {object} options
* @param {object} options.socket
* @param {object} options.redisHelpers
* @param {string} options.jobid
* @param {Array<[string, string]>} [options.params]
*/
async function RrGetAdvisors({ socket, redisHelpers, jobid, params = [] }) {
const data = await MakeRRCall({
...RRActions.GetAdvisors, // GET /advisors/v1
requestSearchParams: params,
type: "get",
redisHelpers,
socket,
jobid
});
return assertRrOk(data, { apiName: "RR Get Advisors", allowEmpty: true });
try {
RRLogger(socket, "info", "Starting RR Get Advisors", { jobid, params });
const bodyshopId = socket?.bodyshopId || socket?.user?.bodyshopid;
const dealerConfig = bodyshopId ? await getDealerConfig(bodyshopId) : {};
// Build Mustache variables for server/rr/xml-templates/GetAdvisors.xml
const variables = mapGetAdvisorsVars({ params, dealerConfig });
const xml = await MakeRRCall({
action: RRActions.GetAdvisors,
body: { template: "GetAdvisors", data: variables },
redisHelpers,
socket,
jobid
});
const ok = assertRrOkXml(xml, { apiName: "RR Get Advisors", allowEmpty: true });
const normalized = extractRrResponseData(ok, { action: "GetAdvisors" });
RRLogger(socket, "debug", "RR Get Advisors complete", {
jobid,
count: Array.isArray(normalized) ? normalized.length : 0
});
return normalized;
} catch (error) {
RRLogger(socket, "error", `RR Get Advisors failed: ${error.message}`, { jobid });
throw error;
}
}
/**
* RR Get Parts.
* Get Parts
* Maps to "Get Part Specification" (Rome)
*
* @param {Object} deps
* @param {Socket|ExpressRequest} deps.socket
* @param {Object} deps.redisHelpers
* @param {string|number} deps.jobid
* @param {Array<[string,string]>} [deps.params=[]]
* Example: [["sku","ABC123"], ["page","1"], ["pageSize","50"]]
* @returns {Promise<any>} RR response (envelope TBD)
* @param {object} options
* @param {object} options.socket
* @param {object} options.redisHelpers
* @param {string} options.jobid
* @param {Array<[string, string]>} [options.params]
*/
async function RrGetParts({ socket, redisHelpers, jobid, params = [] }) {
const data = await MakeRRCall({
...RRActions.GetParts, // GET /parts/v1
requestSearchParams: params,
type: "get",
redisHelpers,
socket,
jobid
});
return assertRrOk(data, { apiName: "RR Get Parts", allowEmpty: true });
try {
RRLogger(socket, "info", "Starting RR Get Parts", { jobid, params });
const bodyshopId = socket?.bodyshopId || socket?.user?.bodyshopid;
const dealerConfig = bodyshopId ? await getDealerConfig(bodyshopId) : {};
// Build Mustache variables for server/rr/xml-templates/GetParts.xml
const variables = mapGetPartsVars({ params, dealerConfig });
const xml = await MakeRRCall({
action: RRActions.GetParts,
body: { template: "GetParts", data: variables },
redisHelpers,
socket,
jobid
});
const ok = assertRrOkXml(xml, { apiName: "RR Get Parts", allowEmpty: true });
const normalized = extractRrResponseData(ok, { action: "GetParts" });
RRLogger(socket, "debug", "RR Get Parts complete", {
jobid,
count: Array.isArray(normalized) ? normalized.length : 0
});
return normalized;
} catch (error) {
RRLogger(socket, "error", `RR Get Parts failed: ${error.message}`, { jobid });
throw error;
}
}
module.exports = { RrCombinedSearch, RrGetAdvisors, RrGetParts };
module.exports = {
RrCombinedSearch,
RrGetAdvisors,
RrGetParts
};