Files
bodyshop/server/rr/rr-selected-customer.js

101 lines
3.6 KiB
JavaScript

// server/rr/rr-selected-customer.js
const RRLogger = require("./rr-logger");
const { insertCustomer, updateCustomer } = require("./rr-customer");
const { withClient } = require("./withClient");
const { QueryJobData, getTransactionType } = require("./rr-job-helpers");
/**
* Selects/creates/updates the RR customer for a job and persists the choice in the session tx.
* - If selectedCustomerId is given: cache and return.
* - Else: upsert from the job's current customer data, cache resulting dmsRecKey.
*/
async function SelectedCustomer({ socket, jobid, bodyshopId, selectedCustomerId, redisHelpers }) {
const log = RRLogger(socket);
// 1) Load JobData (we'll also use it for bodyshopId if missing)
const JobData = await QueryJobData({ socket, jobid });
const resolvedBodyshopId = bodyshopId || JobData?.bodyshop?.id;
if (!resolvedBodyshopId) throw new Error("Unable to resolve bodyshopId for RR SelectedCustomer");
const txKey = getTransactionType(jobid);
const { setSessionTransactionData, getSessionTransactionData } = redisHelpers || {};
const current = (await getSessionTransactionData?.(txKey)) || {};
// 2) If the UI already chose a DMS customer, just persist it
if (selectedCustomerId) {
await setSessionTransactionData?.(txKey, {
...current,
selectedCustomerId
});
log("info", "RR SelectedCustomer: using provided selectedCustomerId", { selectedCustomerId, jobid });
return { selectedCustomerId };
}
// 3) Otherwise, upsert based on job's customer info (fallback), and cache the new id.
const j = JobData || {};
const c = j.customer || {};
if (!c && !j?.vehicle?.vin) {
log("warn", "RR SelectedCustomer: no customer on job and no VIN; nothing to do", { jobid });
return { selectedCustomerId: null };
}
// Upsert customer: prefer update if we have a NameRecId; else insert.
const customerPayload = mapJobCustomerToRR(c);
const upsert = c?.nameRecId
? await updateCustomer({ bodyshopId: resolvedBodyshopId, payload: customerPayload })
: await insertCustomer({ bodyshopId: resolvedBodyshopId, payload: customerPayload });
const dmsRecKey =
upsert?.data?.dmsRecKey ||
upsert?.data?.DMSRecKey ||
upsert?.data?.dmsRecKeyId ||
c?.customerNo ||
c?.nameRecId ||
null;
// Optionally ensure a ServiceVehicle record exists when VIN present (best effort).
if (j?.vehicle?.vin) {
try {
await withClient(resolvedBodyshopId, async (client, routing) => {
await client.insertServiceVehicle(
{ vin: j.vehicle.vin, vehicleServInfo: { customerNo: dmsRecKey } },
{ routing }
);
});
log("info", "RR SelectedCustomer: ensured ServiceVehicle for VIN", { vin: j.vehicle.vin, dmsRecKey });
} catch (e) {
log("warn", `RR SelectedCustomer: insertServiceVehicle skipped (${e.message})`, { vin: j?.vehicle?.vin });
}
}
// Save in session tx
await setSessionTransactionData?.(txKey, {
...current,
selectedCustomerId: dmsRecKey
});
log("info", "RR SelectedCustomer: upsert complete", { dmsRecKey, jobid });
return { selectedCustomerId: dmsRecKey };
}
function mapJobCustomerToRR(c = {}) {
// Mirrors the mapping used by rr-job-export (kept local to avoid cross-module exports)
return {
nameRecId: c.nameRecId, // for update path
firstName: c.firstName || c.given_name,
lastName: c.lastName || c.family_name || c.last_name,
phone: c.phone || c.mobile,
email: c.email,
address: {
line1: c.address1 || c.address || c.street,
line2: c.address2 || "",
city: c.city,
state: c.province || c.state,
postalCode: c.postal || c.zip
}
};
}
module.exports = { SelectedCustomer };