147 lines
4.5 KiB
JavaScript
147 lines
4.5 KiB
JavaScript
// File: server/rr/rr-customers.js
|
||
const { RRClient } = require("./lib/index.cjs");
|
||
const { getRRConfigFromBodyshop } = require("./rr-config");
|
||
const RRLogger = require("./rr-logger");
|
||
|
||
// Build client + opts from bodyshop
|
||
function buildClientAndOpts(bodyshop) {
|
||
const cfg = getRRConfigFromBodyshop(bodyshop);
|
||
const client = new RRClient({
|
||
baseUrl: cfg.baseUrl,
|
||
username: cfg.username,
|
||
password: cfg.password,
|
||
timeoutMs: cfg.timeoutMs,
|
||
retries: cfg.retries
|
||
});
|
||
|
||
const opts = {
|
||
routing: cfg.routing,
|
||
envelope: {
|
||
sender: {
|
||
component: "Rome",
|
||
task: "CU",
|
||
referenceId: "Insert",
|
||
creator: "RCI",
|
||
senderName: "RCI"
|
||
}
|
||
}
|
||
};
|
||
return { client, opts };
|
||
}
|
||
|
||
function digitsOnly(s) {
|
||
return String(s || "").replace(/\D/g, "");
|
||
}
|
||
|
||
function uniq(arr) {
|
||
return Array.from(new Set(arr));
|
||
}
|
||
|
||
/**
|
||
* Build a payload that matches the RR client expectations for insert/update:
|
||
* - ibFlag: 'I' (individual) or 'B' (business). If we have a first name, default to 'I', else 'B' if company present.
|
||
* - Must include lastName OR customerName.
|
||
* - addresses[] / phones[] / emails[] per the library’s toView() contract.
|
||
*/
|
||
function buildCustomerPayloadFromJob(job, overrides = {}) {
|
||
// Pull ONLY from job.ownr_* fields (no job.customer.*)
|
||
const firstName = overrides.firstName ?? job?.ownr_fn ?? undefined;
|
||
const lastName = overrides.lastName ?? job?.ownr_ln ?? undefined;
|
||
const companyName = overrides.companyName ?? overrides.company ?? job?.ownr_co_nm ?? undefined;
|
||
|
||
// Decide Individual vs Business (caller can force via overrides.ibFlag)
|
||
const ibFlag = (overrides.ibFlag || (firstName ? "I" : companyName ? "B" : "I")).toUpperCase();
|
||
|
||
// Email(s)
|
||
const email = overrides.email ?? job?.ownr_ea ?? undefined;
|
||
const emails = email ? [{ address: String(email) }] : undefined;
|
||
|
||
// Phones
|
||
const phoneCandidates = [overrides.phone, job?.ownr_ph1, job?.ownr_ph2]
|
||
.map((v) => digitsOnly(v))
|
||
.filter((v) => v && v.length >= 7);
|
||
|
||
const phones = uniq(phoneCandidates).map((num) => ({ number: num }));
|
||
|
||
// Address (include only if line1 exists; template requires Addr1 if address is present)
|
||
const line1 = overrides.addressLine1 ?? job?.ownr_addr1 ?? undefined;
|
||
|
||
const addresses = line1
|
||
? [
|
||
{
|
||
type: overrides.addressType || "P",
|
||
line1,
|
||
line2: overrides.addressLine2 ?? job?.ownr_addr2 ?? undefined,
|
||
city: overrides.city ?? job?.ownr_city ?? undefined,
|
||
state: overrides.state ?? job?.ownr_st ?? undefined,
|
||
postalCode: overrides.postalCode ?? job?.ownr_zip ?? undefined,
|
||
country: (overrides.country ?? job?.ownr_ctry ?? "CA") || undefined
|
||
}
|
||
]
|
||
: undefined;
|
||
|
||
// Enforce lib requirement: lastName OR customerName
|
||
if (!lastName && !companyName) {
|
||
throw new Error(
|
||
"Cannot build RR customer payload: lastName or companyName is required (no ownr_ln / ownr_co_nm on job)."
|
||
);
|
||
}
|
||
|
||
const payload = {
|
||
ibFlag, // 'I' or 'B'
|
||
firstName: firstName || undefined,
|
||
lastName: lastName || undefined,
|
||
customerName: companyName || undefined,
|
||
createdBy: overrides.createdBy || "ImEX Online",
|
||
customerType: overrides.customerType || "R", // Retail default
|
||
addresses,
|
||
phones,
|
||
emails
|
||
};
|
||
|
||
Object.keys(payload).forEach((k) => payload[k] === undefined && delete payload[k]);
|
||
return payload;
|
||
}
|
||
|
||
/**
|
||
* Create a customer in RR and return { customerNo, raw }.
|
||
* Maps data.dmsRecKey -> customerNo for compatibility with existing callers.
|
||
*/
|
||
async function createRRCustomer({ bodyshop, job, overrides = {}, socket }) {
|
||
const log = RRLogger(socket, { ns: "rr" });
|
||
const { client, opts } = buildClientAndOpts(bodyshop);
|
||
const payload = buildCustomerPayloadFromJob(job, overrides);
|
||
|
||
let res;
|
||
try {
|
||
res = await client.insertCustomer(payload, opts);
|
||
} catch (e) {
|
||
log("error", "RR insertCustomer transport error", { message: e?.message, stack: e?.stack, payload });
|
||
throw e;
|
||
}
|
||
|
||
const data = res?.data ?? res;
|
||
const trx = res?.statusBlocks?.transaction;
|
||
|
||
let customerNo = data?.dmsRecKey;
|
||
if (!customerNo) {
|
||
log("error", "RR insertCustomer returned no dmsRecKey/custNo", {
|
||
status: trx?.status,
|
||
statusCode: trx?.statusCode,
|
||
message: trx?.message,
|
||
data
|
||
});
|
||
throw new Error(
|
||
`RR insertCustomer returned no dmsRecKey (status=${trx?.status ?? "?"} code=${trx?.statusCode ?? "?"}${
|
||
trx?.message ? ` msg=${trx.message}` : ""
|
||
})`
|
||
);
|
||
}
|
||
|
||
return { customerNo: String(customerNo), raw: data };
|
||
}
|
||
|
||
module.exports = {
|
||
createRRCustomer
|
||
};
|