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

This commit is contained in:
Dave
2025-10-20 11:23:10 -04:00
parent a4da874a1a
commit 319f3220ed
6 changed files with 721 additions and 425 deletions

View File

@@ -32,6 +32,22 @@ function getDSB(cfg) {
return { dealerNumber, storeNumber, branchNumber };
}
/**
* Normalize an address-like object to the template's <Address> block.
*/
function mapAddress(addr) {
if (!addr) return undefined;
const out = {
Line1: addr.line1,
Line2: addr.line2,
City: addr.city,
State: addr.state,
PostalCode: addr.postal_code || addr.postalCode,
Country: addr.country
};
return hasAny(out) ? out : undefined;
}
//
// ===================== CUSTOMER =====================
//
@@ -39,71 +55,36 @@ function getDSB(cfg) {
/**
* Map internal customer record to Rome CustomerInsertRq.
*/
function mapCustomerInsert(customer, bodyshopConfig) {
if (!customer) return {};
const { dealerNumber, storeNumber, branchNumber } = getDSB(bodyshopConfig);
function mapCustomerInsert(src) {
const name = src.company_name?.trim() || [src.first_name, src.last_name].filter(Boolean).join(" ").trim();
return {
DealerCode: bodyshopConfig?.dealer_code || "ROME",
DealerNumber: dealerNumber,
StoreNumber: storeNumber,
BranchNumber: branchNumber,
RequestId: `CUST-INSERT-${customer.id}`,
Environment: process.env.NODE_ENV,
CustomerId: customer.external_id || undefined,
CustomerType: customer.type || "RETAIL",
CompanyName: customer.company_name,
FirstName: customer.first_name,
MiddleName: customer.middle_name,
LastName: customer.last_name,
PreferredName: customer.display_name || customer.first_name,
ActiveFlag: customer.active ? "true" : "false",
CustomerGroup: customer.group_name,
TaxExempt: customer.tax_exempt ? "true" : "false",
DiscountLevel: num(customer.discount_level),
PreferredLanguage: customer.language || "EN",
Addresses: (customer.addresses || []).map((a) => ({
AddressType: a.type || "BILLING",
AddressLine1: a.line1,
AddressLine2: a.line2,
CustomerNumber: src.external_id, // optional
CustomerType: src.type === "BUSINESS" ? "BUSINESS" : "RETAIL",
CustomerName: name,
DisplayName: src.display_name || name,
Language: src.language || "EN",
TaxExempt: src.tax_exempt ? "Y" : "N",
Active: src.active ? "Y" : "N",
Addresses: (src.addresses || []).map((a) => ({
Type: a.type || "P",
Line1: a.line1,
Line2: a.line2,
City: a.city,
State: a.state,
PostalCode: a.postal_code,
Country: a.country || "US"
Country: a.country
})),
Phones: (customer.phones || []).map((p) => ({
PhoneNumber: p.number,
PhoneType: p.type || "MOBILE",
Preferred: p.preferred ? "true" : "false"
Phones: (src.phones || []).map((p) => ({
Type: p.type || "H",
Number: p.number,
Extension: p.extension,
Preferred: p.preferred ? "Y" : "N"
})),
Emails: (customer.emails || []).map((e) => ({
EmailAddress: e.address,
EmailType: e.type || "WORK",
Preferred: e.preferred ? "true" : "false"
})),
Insurance: customer.insurance
? {
CompanyName: customer.insurance.company,
PolicyNumber: customer.insurance.policy,
ExpirationDate: formatDate(customer.insurance.expiration_date),
ContactName: customer.insurance.contact_name,
ContactPhone: customer.insurance.contact_phone
}
: undefined,
LinkedAccounts: (customer.linked_accounts || []).map((a) => ({
Type: a.type,
AccountNumber: a.account_number,
CreditLimit: num(a.credit_limit)
})),
Notes: customer.notes?.length ? { Items: customer.notes.map((n) => n.text || n) } : undefined
Emails: (src.emails || []).map((e) => ({
Type: e.type || "W",
Address: e.address,
Preferred: e.preferred ? "Y" : "N"
}))
};
}
@@ -184,6 +165,8 @@ function mapServiceVehicle(vehicle, ownerCustomer, bodyshopConfig) {
/**
* Map internal job to Rome RepairOrderInsertRq.
* NOTE: The CreateRepairOrder.xml template expects *flat* fields for Customer and ServiceVehicle
* (no {{#Customer}} or {{#ServiceVehicle}} sections). Therefore, we flatten those values here.
*/
function mapRepairOrderCreate(job, bodyshopConfig) {
if (!job) return {};
@@ -192,7 +175,11 @@ function mapRepairOrderCreate(job, bodyshopConfig) {
const cust = job.customer || {};
const veh = job.vehicle || {};
// Prefer a concrete address on the customer, fall back to job-level
const customerAddress = cust.address || job.customer_address || job.address || undefined;
return {
// Routing/meta we keep available for logging or other templates
DealerCode: bodyshopConfig?.dealer_code || "ROME",
DealerNumber: dealerNumber,
StoreNumber: storeNumber,
@@ -200,6 +187,7 @@ function mapRepairOrderCreate(job, bodyshopConfig) {
RequestId: `RO-${job.id}`,
Environment: process.env.NODE_ENV,
// Header fields
RepairOrderNumber: job.ro_number,
DmsRepairOrderId: job.external_id,
@@ -215,26 +203,26 @@ function mapRepairOrderCreate(job, bodyshopConfig) {
ROType: job.ro_type,
Status: job.status,
IsBodyShop: "true",
DRPFlag: job.drp_flag ? "true" : "false",
DRPFlag: toBoolStr(!!job.drp_flag) || "false",
Customer: {
CustomerId: cust.external_id,
CustomerName: cust.full_name,
PhoneNumber: cust.phone,
EmailAddress: cust.email
},
// Customer block is FLAT (template does not use {{#Customer}} section)
CustomerId: cust.external_id,
CustomerName: cust.full_name || [cust.first_name, cust.last_name].filter(Boolean).join(" ").trim() || undefined,
PhoneNumber: cust.phone,
EmailAddress: cust.email,
Address: mapAddress(customerAddress),
Vehicle: {
VehicleId: veh.external_id,
VIN: veh.vin,
LicensePlate: veh.license_plate,
Year: num(veh.year),
Make: veh.make,
Model: veh.model,
Odometer: num(veh.odometer),
Color: veh.color
},
// ServiceVehicle block is FLAT (template does not use {{#ServiceVehicle}} section)
VehicleId: veh.external_id,
VIN: veh.vin,
LicensePlate: veh.license_plate,
Year: num(veh.year),
Make: veh.make,
Model: veh.model,
Odometer: num(veh.odometer),
Color: veh.color,
// Lines
JobLines: (job.joblines || []).map((l, i) => ({
Sequence: i + 1,
ParentSequence: l.parent_sequence,
@@ -264,16 +252,28 @@ function mapRepairOrderCreate(job, bodyshopConfig) {
: undefined
})),
Totals: {
// Totals
Totals: hasAny({
Currency: job.currency || "CAD",
LaborTotal: num(job.totals?.labor),
PartsTotal: num(job.totals?.parts),
MiscTotal: num(job.totals?.misc),
DiscountTotal: num(job.totals?.discount),
TaxTotal: num(job.totals?.tax),
GrandTotal: num(job.totals?.grand)
},
LaborTotal: job.totals?.labor,
PartsTotal: job.totals?.parts,
MiscTotal: job.totals?.misc,
DiscountTotal: job.totals?.discount,
TaxTotal: job.totals?.tax,
GrandTotal: job.totals?.grand
})
? {
Currency: job.currency || "CAD",
LaborTotal: num(job.totals?.labor),
PartsTotal: num(job.totals?.parts),
MiscTotal: num(job.totals?.misc),
DiscountTotal: num(job.totals?.discount),
TaxTotal: num(job.totals?.tax),
GrandTotal: num(job.totals?.grand)
}
: undefined,
// Payments
Payments: job.payments?.length
? {
Items: job.payments.map((p) => ({
@@ -287,6 +287,7 @@ function mapRepairOrderCreate(job, bodyshopConfig) {
}
: undefined,
// Insurance
Insurance: job.insurance
? {
CompanyName: job.insurance.company,
@@ -296,6 +297,7 @@ function mapRepairOrderCreate(job, bodyshopConfig) {
}
: undefined,
// Notes
Notes: job.notes?.length ? { Items: job.notes.map((n) => n.text || n) } : undefined
};
}