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

This commit is contained in:
Dave
2025-11-05 12:04:14 -05:00
parent da28fe8592
commit 9341806b0f
5 changed files with 49 additions and 51 deletions

View File

@@ -91,9 +91,9 @@ async function createRRCustomer({ bodyshop, job, overrides = {}, socket }) {
const trx = res?.statusBlocks?.transaction; const trx = res?.statusBlocks?.transaction;
// Primary: map dmsRecKey -> custNo // Primary: map dmsRecKey -> custNo
let custNo = data?.dmsRecKey; let customerNo = data?.dmsRecKey;
if (!custNo) { if (!customerNo) {
log("error", "RR insertCustomer returned no dmsRecKey/custNo", { log("error", "RR insertCustomer returned no dmsRecKey/custNo", {
status: trx?.status, status: trx?.status,
statusCode: trx?.statusCode, statusCode: trx?.statusCode,
@@ -108,10 +108,10 @@ async function createRRCustomer({ bodyshop, job, overrides = {}, socket }) {
} }
// Normalize to string for safety // Normalize to string for safety
custNo = String(custNo); customerNo = String(customerNo);
// Preserve existing return shape so callers dont need changes // Preserve existing return shape so callers dont need changes
return { custNo, raw: data }; return { customerNo, raw: data };
} }
module.exports = { module.exports = {

View File

@@ -14,16 +14,17 @@ async function exportJobToRR(args) {
const log = RRLogger(socket, { ns: "rr-export" }); 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");
if (advisorNo == null || String(advisorNo).trim() === "") { if (advisorNo == null || String(advisorNo).trim() === "") {
throw new Error("exportJobToRR: advisorNo is required for RR"); throw new Error("exportJobToRR: advisorNo is required for RR");
} }
// Now strictly require custNo here // Resolve customer number (accept multiple shapes)
const custNo = const selected = selectedCustomer?.customerNo;
(selectedCustomer && (selectedCustomer.custNo || selectedCustomer.CustNo || selectedCustomer.customerNo)) ||
(typeof selectedCustomer === "string" || typeof selectedCustomer === "number" ? String(selectedCustomer) : null); if (!selected) throw new Error("exportJobToRR: selectedCustomer.custNo/customerNo is required");
if (!custNo) throw new Error("exportJobToRR: selectedCustomer.custNo is required");
const { client, opts } = buildClientAndOpts(bodyshop); const { client, opts } = buildClientAndOpts(bodyshop);
const finalOpts = { const finalOpts = {
@@ -42,7 +43,14 @@ async function exportJobToRR(args) {
let svId = null; let svId = null;
if (!existing?.dmsRepairOrderId) { if (!existing?.dmsRepairOrderId) {
try { try {
const svRes = await ensureRRServiceVehicle({ bodyshop, custNo, job, overrides: {}, socket }); // Provide both customerNo and custNo for safety
const svRes = await ensureRRServiceVehicle({
bodyshop,
job,
overrides: {},
customerNo: String(selected),
socket
});
svId = svRes?.svId || null; svId = svRes?.svId || null;
log("info", "RR service vehicle ensured", { created: svRes?.created, svId }); log("info", "RR service vehicle ensured", { created: svRes?.created, svId });
} catch (e) { } catch (e) {
@@ -50,10 +58,11 @@ async function exportJobToRR(args) {
} }
} }
// Build RO payload (now includes both customerNo & custNo; advisorNo & advNo)
const payload = buildRRRepairOrderPayload({ const payload = buildRRRepairOrderPayload({
job, job,
selectedCustomer: { custNo }, selectedCustomer: { customerNo: String(selected) },
advisorNo advisorNo: String(advisorNo)
}); });
const rrRes = existing?.dmsRepairOrderId const rrRes = existing?.dmsRepairOrderId
@@ -69,7 +78,7 @@ async function exportJobToRR(args) {
statusBlocks: rrRes?.statusBlocks || [], statusBlocks: rrRes?.statusBlocks || [],
xml: rrRes?.xml, xml: rrRes?.xml,
parsed: rrRes?.parsed, parsed: rrRes?.parsed,
custNo, customerNo: String(selected),
svId svId
}; };
} }

View File

@@ -1,6 +1,3 @@
// server/rr/rr-job-helpers.js
// Utilities to fetch and map job data into RR payloads using the shared Hasura client.
const client = require("../graphql-client/graphql-client").client; const client = require("../graphql-client/graphql-client").client;
const { GET_JOB_BY_PK } = require("../graphql-client/queries"); const { GET_JOB_BY_PK } = require("../graphql-client/queries");
@@ -52,33 +49,28 @@ async function QueryJobData(ctx = {}, jobId) {
} }
} }
/**
* Build minimal RR RO payload (keys match your RR clients expectations).
* Uses fields that exist in your schema (v_vin, ro_number, owner fields, etc).
*/
/** /**
* Build minimal RR RO payload (keys match RR client expectations). * Build minimal RR RO payload (keys match RR client expectations).
* - Requires advisorNo (maps to advNo) * - Requires advisor number and customer number.
* - Uses custNo (normalized) and a cleaned VIN (17 chars, AZ/09) * - We provide BOTH "customerNo" and "custNo" (and BOTH "advisorNo" and "advNo")
* to be compatible with the compiled RR CJS lib which currently requires
* "customerNo (or CustNo)".
*/ */
function buildRRRepairOrderPayload({ job, selectedCustomer, advisorNo }) { function buildRRRepairOrderPayload({ job, selectedCustomer, advisorNo }) {
// Resolve custNo from object or primitive // Resolve customerNo from object or primitive; accept multiple incoming shapes
const custNoRaw =
(selectedCustomer &&
(selectedCustomer.custNo ||
selectedCustomer.customerNo || // legacy alias, normalized below
selectedCustomer.CustNo)) ||
(typeof selectedCustomer === "string" || typeof selectedCustomer === "number" ? String(selectedCustomer) : null);
const custNo = custNoRaw ? String(custNoRaw).trim() : null; const customerNo = selectedCustomer?.customerNo ? String(selectedCustomer?.customerNo).trim() : null;
if (!custNo) throw new Error("No RR customer selected (custNo missing)");
// Advisor is required for RR if (!customerNo) throw new Error("No RR customer selected (customerNo/CustNo missing)");
const advNo = advisorNo != null && String(advisorNo).trim() !== "" ? String(advisorNo).trim() : null;
if (!advNo) throw new Error("advisorNo is required for RR export"); // Advisor number (accepts advisorNo string, map to both keys)
const adv = advisorNo != null && String(advisorNo).trim() !== "" ? String(advisorNo).trim() : null;
if (!adv) throw new Error("advisorNo is required for RR export");
// Clean/normalize VIN if present // Clean/normalize VIN if present
const vinRaw = job?.v_vin || job?.vehicle?.vin || job?.vin; const vinRaw = job?.v_vin;
const vin = const vin =
typeof vinRaw === "string" typeof vinRaw === "string"
? vinRaw ? vinRaw
@@ -88,17 +80,17 @@ function buildRRRepairOrderPayload({ job, selectedCustomer, advisorNo }) {
: undefined; : undefined;
// Pick a stable external RO number // Pick a stable external RO number
const ro = const ro = job?.ro_number != null ? job.ro_number : job?.id != null ? job.id : null;
job?.ro_number != null ? job.ro_number : job?.job_number != null ? job.job_number : job?.id != null ? job.id : null;
if (ro == null) throw new Error("Missing repair order identifier (ro_number/job_number/id)."); if (ro == null) throw new Error("Missing repair order identifier (ro_number/job_number/id)");
// Provide superset of keys for maximum compatibility with the RR client
return { return {
repairOrderNumber: String(ro), repairOrderNumber: String(ro),
deptType: "B", deptType: "B",
vin, // undefined if absent/empty after cleaning vin,
custNo: String(custNo), customerNo: String(customerNo),
advNo // mapped from advisorNo advisorNo: adv
}; };
} }
@@ -170,6 +162,8 @@ function normalizeCustomerCandidates(res) {
*/ */
function normalizeVehicleCandidates(res) { function normalizeVehicleCandidates(res) {
const blocks = blocksFromCombinedSearchResult(res); const blocks = blocksFromCombinedSearchResult(res);
console.log("Normalized vehicle Candiadates!!!!!!!!!!!!!!!!!!!!!");
console.dir({ res, blocks }, { depth: null });
const out = []; const out = [];
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 : [];

View File

@@ -29,11 +29,11 @@ function buildClientAndOpts(bodyshop) {
function buildServiceVehiclePayload({ job, custNo, overrides = {} }) { function buildServiceVehiclePayload({ job, custNo, overrides = {} }) {
return { return {
custNo: String(custNo), // tie SV to customer customerNo: String(custNo), // tie SV to customer
vin: overrides.vin ?? job?.v_vin, vin: overrides.vin ?? job?.v_vin,
year: overrides.year ?? job?.v_model_yr, year: overrides.year ?? job?.v_model_yr,
make: overrides.make ?? job?.dms_make ?? job?.v_make, make: overrides.make ?? job?.v_make_des,
model: overrides.model ?? job?.dms_model ?? job?.v_model, model: overrides.model ?? job?.v_model_desc,
licensePlate: overrides.plate ?? job?.plate_no licensePlate: overrides.plate ?? job?.plate_no
// add other safe keys your RR client supports (color, mileage, etc.) // add other safe keys your RR client supports (color, mileage, etc.)
}; };
@@ -68,9 +68,6 @@ async function ensureRRServiceVehicle({ bodyshop, custNo, job, overrides = {}, s
const payload = buildServiceVehiclePayload({ job, custNo, overrides }); const payload = buildServiceVehiclePayload({ job, custNo, overrides });
console.log("Inserting RR Service Vehicle with payload:");
console.dir({ payload }, { depth: null });
const res = await client.insertServiceVehicle(payload, opts); const res = await client.insertServiceVehicle(payload, opts);
const data = res?.data ?? res; const data = res?.data ?? res;

View File

@@ -315,9 +315,7 @@ function registerRREvents({ socket, redisHelpers }) {
if (create === true || !selectedCustNo) { if (create === true || !selectedCustNo) {
CreateRRLogEvent(socket, "DEBUG", `{3.1} Creating RR customer`); CreateRRLogEvent(socket, "DEBUG", `{3.1} Creating RR customer`);
const created = await createRRCustomer({ bodyshop, job, socket }); const created = await createRRCustomer({ bodyshop, job, socket });
selectedCustNo = String( selectedCustNo = String(created?.customerNo);
created?.custNo || created?.customerNo || created?.CustomerNo || created?.dmsRecKey || ""
);
if (!selectedCustNo) throw new Error("RR create customer returned no custNo"); if (!selectedCustNo) throw new Error("RR create customer returned no custNo");
CreateRRLogEvent(socket, "DEBUG", `{3.2} Created customer`, { custNo: selectedCustNo }); CreateRRLogEvent(socket, "DEBUG", `{3.2} Created customer`, { custNo: selectedCustNo });
} }
@@ -350,7 +348,7 @@ function registerRREvents({ socket, redisHelpers }) {
const result = await exportJobToRR({ const result = await exportJobToRR({
bodyshop, bodyshop,
job, job,
selectedCustomer: { custNo: String(selectedCustNo) }, selectedCustomer: { customerNo: String(selectedCustNo), custNo: String(selectedCustNo) },
advisorNo: String(advisorNo), advisorNo: String(advisorNo),
existing: txEnvelope?.existing, existing: txEnvelope?.existing,
socket socket