diff --git a/client/src/components/dms-post-form/dms-post-form.component.jsx b/client/src/components/dms-post-form/dms-post-form.component.jsx
index 98ab67031..281f846e4 100644
--- a/client/src/components/dms-post-form/dms-post-form.component.jsx
+++ b/client/src/components/dms-post-form/dms-post-form.component.jsx
@@ -1,3 +1,4 @@
+// DmsPostForm updated
import { DeleteFilled, DownOutlined, ReloadOutlined } from "@ant-design/icons";
import {
Button,
@@ -270,116 +271,118 @@ export function DmsPostForm({ bodyshop, socket, job, logsRef }) {
/>
-
- {(fields, { add, remove }) => (
-
- {fields.map((field, index) => (
-
-
-
-
-
+ {dms !== "rr" && (
+
+ {(fields, { add, remove }) => (
+
- }
- key={`${index}controlnumber`}
- name={[field.name, "controlnumber"]}
- rules={[{ required: true }]}
- >
-
-
-
-
- {() => {
- const payers = form.getFieldValue("payers");
- const row = payers?.[index];
- const cdkPayer =
- bodyshop.cdk_configuration.payers &&
- bodyshop.cdk_configuration.payers.find((i) => i && row && i.name === row.name);
- if (i18n.exists(`jobs.fields.${cdkPayer?.control_type}`))
- return {cdkPayer && t(`jobs.fields.${cdkPayer?.control_type}`)}
;
- else if (i18n.exists(`jobs.fields.dms.control_type.${cdkPayer?.control_type}`)) {
- return {cdkPayer && t(`jobs.fields.dms.control_type.${cdkPayer?.control_type}`)}
;
- } else {
- return null;
+
+ {t("jobs.fields.dms.payer.controlnumber")}{" "}
+ ({
+ key: idx,
+ label: key.name,
+ onClick: () => {
+ form.setFieldsValue({
+ payers: form.getFieldValue("payers").map((row, mapIndex) => {
+ if (index !== mapIndex) return row;
+ return {
+ ...row,
+ controlnumber: key.controlnumber
+ };
+ })
+ });
+ }
+ })) ?? []
+ }}
+ >
+ e.preventDefault()}>
+
+
+
+
}
- }}
-
+ key={`${index}controlnumber`}
+ name={[field.name, "controlnumber"]}
+ rules={[{ required: true }]}
+ >
+
+
- remove(field.name)} />
-
+
+ {() => {
+ const payers = form.getFieldValue("payers");
+ const row = payers?.[index];
+ const cdkPayer =
+ bodyshop.cdk_configuration.payers &&
+ bodyshop.cdk_configuration.payers.find((i) => i && row && i.name === row.name);
+ if (i18n.exists(`jobs.fields.${cdkPayer?.control_type}`))
+ return {cdkPayer && t(`jobs.fields.${cdkPayer?.control_type}`)}
;
+ else if (i18n.exists(`jobs.fields.dms.control_type.${cdkPayer?.control_type}`)) {
+ return {cdkPayer && t(`jobs.fields.dms.control_type.${cdkPayer?.control_type}`)}
;
+ } else {
+ return null;
+ }
+ }}
+
+
+ remove(field.name)} />
+
+
+ ))}
+
+
- ))}
-
-
-
-
- )}
-
+
+ )}
+
+ )}
{() => {
@@ -391,15 +394,16 @@ export function DmsPostForm({ bodyshop, socket, job, logsRef }) {
});
// 2) Subtotal from socket.allocationsSummary (existing behavior)
- const totals =
- socket.allocationsSummary &&
- socket.allocationsSummary.reduce(
- (acc, val) => ({
- totalSale: acc.totalSale.add(Dinero(val.sale)),
- totalCost: acc.totalCost.add(Dinero(val.cost))
- }),
- { totalSale: Dinero(), totalCost: Dinero() }
- );
+ const totals = socket
+ ? socket.allocationsSummary &&
+ socket.allocationsSummary.reduce(
+ (acc, val) => ({
+ totalSale: acc.totalSale.add(Dinero(val.sale)),
+ totalCost: acc.totalCost.add(Dinero(val.cost))
+ }),
+ { totalSale: Dinero(), totalCost: Dinero() }
+ )
+ : { totalSale: Dinero(), totalCost: Dinero() };
const discrep = totals ? totals.totalSale.subtract(totalAllocated) : Dinero();
@@ -408,8 +412,9 @@ export function DmsPostForm({ bodyshop, socket, job, logsRef }) {
// Require at least one complete payer row
const payersOk =
- payers.length > 0 &&
- payers.every((p) => p?.name && p.dms_acctnumber && (p.amount ?? "") !== "" && p.controlnumber);
+ dms === "rr" ||
+ (payers.length > 0 &&
+ payers.every((p) => p?.name && p.dms_acctnumber && (p.amount ?? "") !== "" && p.controlnumber));
// 4) Disable rules:
// - For non-RR: keep the original discrepancy rule (must have summary and zero discrepancy)
diff --git a/client/src/pages/dms/dms.container.jsx b/client/src/pages/dms/dms.container.jsx
index 0a8282ccc..cba7a4b84 100644
--- a/client/src/pages/dms/dms.container.jsx
+++ b/client/src/pages/dms/dms.container.jsx
@@ -1,7 +1,8 @@
+// DmsContainer updated
import { useQuery } from "@apollo/client";
import { Button, Card, Col, Result, Row, Select, Space } from "antd";
import queryString from "query-string";
-import { useEffect, useRef, useState } from "react";
+import { useEffect, useMemo, useRef, useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { Link, useLocation, useNavigate } from "react-router-dom";
@@ -66,6 +67,10 @@ export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader, inse
// New unified wss socket (Fortellis, RR)
const { socket: wsssocket } = useSocket();
+ const activeSocket = useMemo(() => {
+ return dms === "rr" || (dms === "cdk" && Fortellis.treatment === "on") ? wsssocket : socket;
+ }, [dms, Fortellis.treatment, wsssocket, socket]);
+
const { loading, error, data } = useQuery(QUERY_JOB_EXPORT_DMS, {
variables: { id: jobId },
skip: !jobId,
@@ -229,12 +234,12 @@ export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader, inse
} ${data.jobs_by_pk.v_make_desc || ""} ${data.jobs_by_pk.v_model_desc || ""}`}
}
- socket={socket}
+ socket={activeSocket}
jobId={jobId}
/>
-
+
diff --git a/server/rr/lib/README.md b/server/rr/lib/README.md
new file mode 100644
index 000000000..0be036360
--- /dev/null
+++ b/server/rr/lib/README.md
@@ -0,0 +1,658 @@
+# rr-rome-client
+
+A minimal Node.js wrapper for Reynolds & Reynolds (Rome/RIH) STAR `ProcessMessage` over SOAP, with WS-Security UsernameToken.
+
+## Contents
+- Overview
+- Install
+- Supported Node Version
+- Peer / External Dependencies
+- Quick Start
+- Configuration & Environment Variables
+- Client API Methods
+- RRResult Structure
+- Types & IntelliSense
+- Errors & Validation
+- Retry Strategy
+- Debug / Dump Flags
+- Live Test Runner (`scripts/run-live.mjs`)
+- Bundling & Upload Helper (`scripts/bundle-for-upload.mjs`)
+- XML Templates & XSDs
+- Logging
+- Build & Development
+- Design Notes / Non-goals
+- License
+
+## Overview
+`rr-rome-client` builds and sends STAR XML payloads (Customer, Service Vehicle, Combined Search, Advisors, Parts, BSM Repair Orders) inside a SOAP envelope using the STAR Transport `ProcessMessage` operation. It applies a WS-Security UsernameToken header and provides:
+- High-level `RRClient` methods for each supported Rome operation.
+- JSDoc typedefs for all payloads and response shapes (usable in JS and TS).
+- Structured parsing of response status blocks and operation-specific data.
+- Automatic BODId & CreationDateTime generation, with ability to override.
+- Basic input validation and error classification.
+- Exponential backoff + jitter for retryable transport/vendor lock scenarios.
+
+## Install
+```bash
+npm i rr-rome-client
+```
+(You may also need peer dependencies; see below.)
+
+## Supported Node Version
+`package.json` declares `"engines": { "node": ">=22.0.0" }`. The build targets modern Node 22 features (native ESM, improved performance). Earlier Node versions are not officially supported/tested.
+
+## Peer / External Dependencies
+Rollup externalizes runtime libraries (they are listed in `dependencies` but not bundled). Ensure these are available in your environment:
+Required:
+- axios – HTTP transport
+- fast-xml-parser – STAR XML parsing
+- mustache – XML templating
+- uuid – BODId generation
+Optional (only if you use env loader helpers or live scripts):
+- dotenv
+- dotenv-expand
+
+Install (versions per `package.json`):
+```bash
+npm install axios@^1.7.7 fast-xml-parser@^4.5.0 mustache@^4.2.0 uuid@^9.0.1
+# Optional env loader
+npm install dotenv@^17.2.3 dotenv-expand@^12.0.3
+```
+TypeScript users (optional):
+```bash
+npm install -D @types/node
+```
+Browser bundling is not officially supported; you would need polyfills for core modules if attempting.
+
+## Quick Start
+```js
+import { RRClient } from 'rr-rome-client';
+import { loadEnv } from 'rr-rome-client/src/util/config.js'; // optional helper
+
+// Load routing & credentials from environment variables (see below)
+const { baseUrl, username, password, routing } = loadEnv();
+
+const client = new RRClient({ baseUrl, username, password });
+
+// Minimal createRepairOrder example
+const result = await client.createRepairOrder({
+ customerNo: '12345',
+ departmentType: 'S', // DeptType
+ vin: '1ABCDEF2GHIJ34567',
+ outsdRoNo: 'EXT-RO-99'
+}, { routing });
+
+if (result.success) {
+ console.log('RO status:', result.data); // {status, date, time, outsdRoNo, dmsRoNo, errorMessage}
+} else {
+ console.error('Failure:', result.statusBlocks?.transaction);
+}
+```
+
+## Configuration & Environment Variables
+You can manually provide configuration or use the helper `loadEnv(env)` in `src/util/config.js`.
+
+Recognized environment variables:
+- RR_BASE_URL – SOAP endpoint URL (required)
+- RR_USERNAME – WS-Security UsernameToken username (required)
+- RR_PASSWORD – WS-Security UsernameToken password (required)
+- RR_DEALER_NUMBER – DealerNumber for Destination (required per call)
+- RR_STORE_NUMBER – StoreNumber (optional)
+- RR_AREANUMBER – AreaNumber (optional)
+
+Example `.env`:
+```
+RR_BASE_URL=https://rome.example.com/soap
+RR_USERNAME=integratorUser
+RR_PASSWORD=superSecret
+RR_DEALER_NUMBER=1234
+RR_STORE_NUMBER=01
+RR_AREANUMBER=1
+```
+`loadEnv()` returns `{ baseUrl, username, password, routing: { dealerNumber, storeNumber, areaNumber } }`.
+
+Per-call options object shape (`CallOptions`):
+```js
+{
+ routing: { dealerNumber: '1234', storeNumber?: '01', areaNumber?: '1' },
+ envelope?: { bodId?, creationDateTime?, sender?: { component?, task?, referenceId? } }
+}
+```
+If omitted, `RRClient` auto-generates `bodId` (UUID) and `creationDateTime` when sending.
+
+## Client API Methods
+Each method returns a `Promise>` where `T` is operation-specific data (or array). `success` is true for vendor SUCCESS and NO_MATCH results; FAIL triggers an `RRVendorStatusError` exception before a `RRResult` is returned.
+
+All methods require `opts.routing.dealerNumber`.
+
+### combinedSearch(payload: CombinedSearchQuery, opts)
+Search customer + service vehicle combinations by exactly one criterion: `phone | license | vin | name | nameRecId | stkNo` plus optional `make`, `model`, `year`, `maxResults` (capped at 50).
+Minimal:
+```js
+const res = await client.combinedSearch({ kind: 'vin', vin: '12345' }, { routing });
+res.data; // Array
+```
+Errors: Throws `RRValidationError` if missing or multiple criteria.
+
+### insertCustomer(payload: InsertCustomerPayload, opts)
+Insert a customer record. Required: `lastName` (or `customerName`). If individual (`ibFlag='I'` or inferred by presence of `firstName`), then `firstName` required.
+```js
+const res = await client.insertCustomer({ firstName: 'Jane', lastName: 'Doe', phones:[{number:'5551234567'}] }, { routing });
+res.data; // { dmsRecKey, status, statusCode }
+```
+
+### updateCustomer(payload: UpdateCustomerPayload, opts)
+Update existing customer by `nameRecId` plus required `ibFlag`. Other fields optional.
+```js
+const res = await client.updateCustomer({ nameRecId:'998877', ibFlag:'I', lastName:'Doe' }, { routing });
+```
+
+### insertServiceVehicle(payload: InsertServiceVehiclePayload, opts)
+Insert a service vehicle linked to a customer. Required: `vin`, `vehicleServInfo.customerNo`.
+```js
+const res = await client.insertServiceVehicle({
+ vin:'1HGCM82633A004352',
+ vehicleServInfo:{ customerNo:'12345' }
+}, { routing });
+res.data; // { status, statusCode }
+```
+
+### getAdvisors(payload: GetAdvisorsParams, opts)
+Fetch advisors for a department. Department values normalized: S/P/B or long names.
+```js
+const res = await client.getAdvisors({ department:'SERVICE' }, { routing });
+res.data; // AdvisorRow[]
+```
+
+### createRepairOrder(payload: CreateRepairOrderPayload, opts)
+Required: `customerNo`, `departmentType`, `vin`, `outsdRoNo`. Advisor optional. Complex nested labor/parts/misc blocks supported via payload.
+```js
+const res = await client.createRepairOrder({
+ customerNo:'12345', departmentType:'S', vin:'1ABCDEF2GHIJ34567', outsdRoNo:'EXT-RO-99'
+}, { routing });
+res.data; // { status, date, time, outsdRoNo, dmsRoNo, errorMessage }
+```
+
+### updateRepairOrder(payload: UpdateRepairOrderPayload, opts)
+Required: `finalUpdate ('Y'|'N')`, `outsdRoNo`. May include `roNo` and nested sections.
+```js
+const res = await client.updateRepairOrder({ finalUpdate:'N', outsdRoNo:'EXT-RO-99' }, { routing });
+```
+
+### getParts(payload: GetPartsParams, opts)
+Required: `roNumber` (internal ERA RO number).
+```js
+const res = await client.getParts({ roNumber:'938275' }, { routing });
+res.data; // PartRow[]
+```
+
+## Payload Schema Reference
+Comprehensive field-level summary sourced from `src/types.js`, operation builders, and validation logic. Types reflect accepted JS types (string|number where applicable). Constraints list enumerations, inference rules, and validation notes. Required = must be supplied by caller (or inferred automatically). Optional fields omitted become absent in generated XML.
+
+### CombinedSearchQuery
+Only one criterion permitted; `maxResults` capped at 50.
+
+| Field | Type | Required | Constraints / Notes |
+|-------|------|----------|----------------------|
+| kind | 'phone'|'license'|'vin'|'name'|'nameRecId'|'stkNo' | Yes | Determines which single criterion block is emitted |
+| phone | string\|number\|{phone:string} | Conditionally (if kind==='phone') | Value mapped to `` |
+| license | string\|number\|{license:string} | Conditionally (kind==='license') | Value mapped to `` |
+| vin | string\|number\|{vin:string} | Conditionally (kind==='vin') | Value mapped to `` (partial VIN allowed) |
+| name | {fname,lname,mname} or {name} | Conditionally (kind==='name') | Either FullName triple or LName only; must supply all three for FullName |
+| nameRecId | string\|number\|{custId:string}|{nameRecId:string} | Conditionally (kind==='nameRecId') | Emits `` |
+| stkNo | string\|number\|{stkNo:string} | Conditionally (kind==='stkNo') | Emits `` |
+| maxResults | number | No | Capped at 50 (default 50) -> `MaxRecs` attribute |
+| make | string | No | Defaults 'ANY' -> `VehData MakePfx` |
+| model | string\|number | No | Defaults 'ANY' -> `VehData Model` |
+| year | string\|number | No | Defaults 'ANY' -> `VehData Year` |
+
+### InsertCustomerPayload
+`ibFlag` inferred as 'I' if `firstName` present, else 'B'. Business requires `lastName` / `customerName`; individual requires `firstName` + last name.
+
+| Field | Type | Required | Constraints / Notes |
+|-------|------|----------|----------------------|
+| ibFlag | 'I'|'B' | Auto / For update must supply | Inferred if omitted (firstName present => 'I') |
+| customerType | 'R'|'W'|'I'|'Retail'|'Wholesale'|'Internal' | No | Normalized to 'R','W','I'; must be one of listed |
+| createdBy | string | No | Optional CreatedBy attribute |
+| customerName | string | Conditional | Alias for `lastName` when business |
+| lastName | string | Yes (unless `customerName` provided) | Required base name; sanitized to A-Z0-9 space |
+| firstName | string | Required when ibFlag='I' | Sanitized; required for individuals |
+| midName | string | No | Sanitized |
+| salut | string | No | Sanitized |
+| suffix | string | No | Sanitized |
+| addresses | CustomerAddress[] | No | Each entry requires `line1`; Type defaults 'P' |
+| phones | CustomerPhone[] | No | Each entry requires `number`; Type defaults 'H' |
+| emails | CustomerEmail[] | No | First entry used -> `` |
+| personal.gender | 'M'|'F'|'U' | No | Optional |
+| personal.otherName | string | No | Sanitized alnum/space |
+| personal.anniversaryDate | string | No | Included if non-empty |
+| personal.employerName | string | No | Sanitized |
+| personal.employerPhone | string | No | Raw string |
+| personal.occupation | string | No | Sanitized |
+| personal.optOut | string | No | Pass-through |
+| personal.optOutUse | string | No | Pass-through |
+| personal.birthDates[].type | 'P'|'S' | No | Defaults 'P'; entry must have `date` |
+| personal.birthDates[].date | string | Conditional | Included only if non-empty |
+| personal.ssns[].type | 'P'|'S' | No | Defaults 'P'; entry must have `ssn` |
+| personal.ssns[].ssn | string | Conditional | Included only if non-empty |
+| personal.driver.type | 'P'|'S' | No | Defaults 'P' |
+| personal.driver.licenseNumber | string | Conditional | Required to emit DriverInfo |
+| personal.driver.licenseState | string | No | Optional |
+| personal.driver.licenseExpDate | string | No | Optional |
+| personal.children[].name | string | No | Sanitized; optional list |
+| dms.taxExemptNum | string | No | Optional |
+| dms.salesTerritory | string | No | Optional |
+| dms.deliveryRoute | string | No | Optional |
+| dms.salesmanNum | string | No | Optional |
+| dms.lastContactMethod | string | No | Optional |
+| dms.followups[].type | string | Conditional | Must have both type & value |
+| dms.followups[].value | string | Conditional | Must have both type & value |
+
+### UpdateCustomerPayload
+Extends InsertCustomerPayload plus:
+
+| Field | Type | Required | Constraints |
+|-------|------|----------|-------------|
+| nameRecId | string\|number | Yes | Required identifier |
+| ibFlag | 'I'|'B' | Yes | Must be explicitly provided on update |
+
+### InsertServiceVehiclePayload
+
+| Field | Type | Required | Constraints / Notes |
+|-------|------|----------|----------------------|
+| vin | string | Yes | Must be provided |
+| modelDesc | string | No | Optional attribute |
+| carline | string | No | Optional attribute |
+| extClrDesc | string | No | Optional attribute |
+| intClrDesc | string | No | Optional attribute |
+| trimDesc | string | No | Optional attribute |
+| bodyStyle | string | No | Optional attribute |
+| engineDesc | string | No | Optional attribute |
+| transDesc | string | No | Optional attribute |
+| year | string\|number | No | Emits `` element if present |
+| odometer | string\|number | No | Emits `` |
+| odometerUnits | string | No | Emits `` |
+| vehicleDetail.licNo | string | No | Emits `` |
+| vehicleServInfo.customerNo | string\|number | Yes | Required; becomes `CustomerNo` attribute |
+| vehicleServInfo.salesmanNo | string\|number | No | Optional element |
+| vehicleServInfo.inServiceDate | string\|number | No | Optional element |
+| vehicleServInfo.mileage | string\|number | No | Optional element |
+| vehicleServInfo.teamCode | string | No | Optional element |
+| vehicleServInfo.vehExtWarranty.contractNumber | string | Conditional | Included if any warranty field present |
+| vehicleServInfo.vehExtWarranty.expirationDate | string | Conditional | "" excluded |
+| vehicleServInfo.vehExtWarranty.expirationMileage | string\|number | Conditional | "" excluded |
+| vehicleServInfo.advisor.contactInfo.nameRecId | string\|number | No | Advisor block included only if provided |
+
+### CreateRepairOrderPayload
+
+| Field | Type | Required | Constraints / Notes |
+|-------|------|----------|----------------------|
+| customerNo | string\|number | Yes | CustNo |
+| departmentType | string\|number | Yes | DeptType |
+| vin | string | Yes | Vin |
+| outsdRoNo | string\|number | Yes | External RO identifier |
+| advisorNo | string\|number | No | AdvNo |
+| tagNo | string\|number | No | TagNo |
+| mileageIn | string\|number | No | MileageIn |
+| roComment | string | No | `` |
+| estimate.parts | string\|number | No | EstPartsAmt |
+| estimate.labor | string\|number | No | EstLaborAmt |
+| estimate.total | string\|number | No | EstTotalAmt |
+| tax.payType | 'All'|'Cust'|'Intr'|'Warr' | No | Enumeration validated |
+| tax.taxCode | string | No | TaxCode |
+| tax.txblGrossAmt | string\|number | No | TxblGrossAmt |
+| tax.grossTaxAmt | string\|number | No | GrossTaxAmt |
+| rolabor.ops[].opCode | string | No | Optional |
+| rolabor.ops[].jobNo | string\|number | No | Optional |
+| rolabor.ops[].custPayTypeFlag | string | No | Freeform (not validated) |
+| rolabor.ops[].warrPayTypeFlag | string | No | Freeform |
+| rolabor.ops[].intrPayTypeFlag | string | No | Freeform |
+| rolabor.ops[].custTxblNtxblFlag | 'T'|'N' | No | Enumerated & validated |
+| rolabor.ops[].warrTxblNtxblFlag | 'T'|'N' | No | Enumerated & validated |
+| rolabor.ops[].intrTxblNtxblFlag | 'T'|'N' | No | Enumerated & validated |
+| rolabor.ops[].vlrCode | string | No | Optional |
+| rolabor.ops[].bill.payType | 'All'|'Cust'|'Intr'|'Warr' | No | Enumeration |
+| rolabor.ops[].bill.jobTotalHrs | string\|number | No | Optional |
+| rolabor.ops[].bill.billTime | string\|number | No | Optional |
+| rolabor.ops[].bill.billRate | string\|number | No | Optional |
+| rolabor.ops[].ccc.cause | string | No | Optional |
+| rolabor.ops[].ccc.complaint | string | No | Optional |
+| rolabor.ops[].ccc.correction | string | No | Optional |
+| rolabor.ops[].amount.payType | 'All'|'Cust'|'Intr'|'Warr' | No | Enumeration |
+| rolabor.ops[].amount.amtType | string | No | Optional |
+| rolabor.ops[].amount.custPrice | string\|number | No | Optional |
+| rolabor.ops[].amount.totalAmt | string\|number | No | Optional |
+| ropart.jobs[].opCode | string | No | Optional |
+| ropart.jobs[].jobNo | string\|number | No | Optional |
+| ropart.jobs[].lines[].partNo | string | No | Optional |
+| ropart.jobs[].lines[].partNoDesc | string | No | Optional |
+| ropart.jobs[].lines[].partQty | string\|number | No | Emits QtyOrd |
+| ropart.jobs[].lines[].sale | string\|number | No | Sale |
+| ropart.jobs[].lines[].cost | string\|number | No | Cost |
+| ropart.jobs[].lines[].addDeleteFlag | string | No | AddDeleteFlag |
+| rogg.ops[].lines[].breakOut | string | No | Optional |
+| rogg.ops[].lines[].itemType | 'G'|'P'|'S'|'F' | No | Enumerated & validated |
+| rogg.ops[].lines[].itemDesc | string | No | Optional |
+| rogg.ops[].lines[].custQty | string\|number | No | Optional |
+| rogg.ops[].lines[].warrQty | string\|number | No | Optional |
+| rogg.ops[].lines[].intrQty | string\|number | No | Optional |
+| rogg.ops[].lines[].custPayTypeFlag | string | No | Optional |
+| rogg.ops[].lines[].warrPayTypeFlag | string | No | Optional |
+| rogg.ops[].lines[].intrPayTypeFlag | string | No | Optional |
+| rogg.ops[].lines[].custTxblNtxblFlag | 'T'|'N' | No | Enumerated |
+| rogg.ops[].lines[].warrTxblNtxblFlag | 'T'|'N' | No | Enumerated |
+| rogg.ops[].lines[].intrTxblNtxblFlag | 'T'|'N' | No | Enumerated |
+| rogg.ops[].lines[].amount.payType | 'All'|'Cust'|'Intr'|'Warr' | No | Enumeration |
+| rogg.ops[].lines[].amount.amtType | string | No | Optional |
+| rogg.ops[].lines[].amount.custPrice | string\|number | No | Optional |
+| rogg.ops[].lines[].amount.dlrCost | string\|number | No | Optional |
+| romisc.ops[].lines[].miscCode | string | No | Optional |
+| romisc.ops[].lines[].custPayTypeFlag | string | No | Optional |
+| romisc.ops[].lines[].warrPayTypeFlag | string | No | Optional |
+| romisc.ops[].lines[].intrPayTypeFlag | string | No | Optional |
+| romisc.ops[].lines[].custTxblNtxblFlag | 'T'|'N' | No | Enumerated |
+| romisc.ops[].lines[].warrTxblNtxblFlag | 'T'|'N' | No | Enumerated |
+| romisc.ops[].lines[].intrTxblNtxblFlag | 'T'|'N' | No | Enumerated |
+| romisc.ops[].lines[].codeAmt | string\|number | No | Optional |
+
+### UpdateRepairOrderPayload
+Adds required `finalUpdate` and supports most Create fields + mileageOut.
+
+| Field | Type | Required | Constraints / Notes |
+|-------|------|----------|----------------------|
+| finalUpdate | 'Y'|'N' | Yes | Must be 'Y' or 'N' (validated) |
+| outsdRoNo | string\|number | Yes | External RO identifier (validation requires present) |
+| roNo | string\|number | No | Optional internal RoNo |
+| mileageOut | string\|number | No | MileageOut |
+| (other fields) | see CreateRepairOrderPayload | Conditional | Same validations apply |
+
+### GetAdvisorsParams
+
+| Field | Type | Required | Constraints / Notes |
+|-------|------|----------|----------------------|
+| department | 'S'|'P'|'B'|'SERVICE'|'PARTS'|'BODY'|'BODYSHOP'|'BODY SHOP' | Yes | Normalized to S/P/B |
+| advisorNumber | string\|number | No | Optional filter |
+| maxResults | number | Ignored | Not in XSD (builder ignores) |
+
+### GetPartsParams
+
+| Field | Type | Required | Constraints / Notes |
+|-------|------|----------|----------------------|
+| roNumber | string\|number | Yes | Required; becomes `` |
+
+### Common Routing & Envelope Fields
+
+| Field | Type | Required | Constraints / Notes |
+|-------|------|----------|----------------------|
+| routing.dealerNumber | string | Yes | Required for all requests (Destination) |
+| routing.storeNumber | string | No | Optional Destination StoreNumber |
+| routing.areaNumber | string | No | Optional Destination AreaNumber |
+| envelope.bodId | string | Auto | UUID generated if omitted |
+| envelope.creationDateTime | Date\|string | Auto | Uses current time; formatted without milliseconds |
+| envelope.sender.component | string | No | Defaults 'Rome' if omitted |
+| envelope.sender.task | string | No | Op-specific defaults (e.g. 'CU','SV','BSMRO','CVC','RCT') |
+| envelope.sender.referenceId | string | No | Op-specific defaults ('Insert','Update','Query') |
+
+### AdvisorRow (response convenience)
+
+| Field | Type | Notes |
+|-------|------|-------|
+| advisorId | string\|number\|undefined | AdvisorNumber attribute |
+| firstName | string\|undefined | FirstName attribute |
+| lastName | string\|undefined | LastName attribute |
+| department | 'S'|'P'|'B'|undefined | Normalized department passed through |
+
+### PartRow (response convenience)
+
+| Field | Type | Notes |
+|-------|------|-------|
+| partNumber | string\|undefined | PartNumber |
+| partDescription | string\|undefined | PartDescription |
+| quantityOrdered | string\|number\|undefined | QuantityOrdered |
+| quantityShipped | string\|number\|undefined | QuantityShipped |
+| price | string\|number\|undefined | Price |
+| cost | string\|number\|undefined | Cost |
+| processedFlag | string\|undefined | ProcessedFlag |
+| addOrDelete | string\|undefined | AddOrDelete |
+
+### CombinedSearch Response Shapes
+Hierarchical breakdown of the parsed array returned under `RRResult.data` for `combinedSearch` (each entry is a `CombinedSearchBlock`). Attribute-centric; absent fields are simply omitted.
+
+#### CombinedSearchBlock
+| Field | Type | Notes |
+|-------|------|-------|
+| NameContactId | CombinedSearchNameContactId | Customer name/contact composite |
+| ServVehicle | CombinedSearchServVehicle[] | Zero or more service vehicle blocks |
+| Message | CombinedSearchMessage[] | Optional informational messages |
+
+#### NameContactId
+| Field | Type | Notes |
+|-------|------|-------|
+| NameId | CombinedSearchNameId | Includes identifiers & individual/business name choice |
+| Address | Object[] | Each has address-related attributes (lines, city, state, zip, etc.) |
+| ContactOptions | Object[] | Arbitrary contact option attributes as present |
+| Phone | Object[] | Each phone entry carries attributes like Type, Num, Ext |
+| Email | Object[] | Each email entry carries attributes (MailTo, etc.) |
+
+#### NameId
+| Field | Type | Notes |
+|-------|------|-------|
+| NameRecId | string\|number | Identifier used for subsequent operations |
+| IBFlag | 'I'|'B' | Individual or Business flag |
+| IndName | Object | Present for individual: attributes like FName, LName, MName |
+| BusName | Object | Present for business: attributes like Name (business name) |
+
+#### ServVehicle (CombinedSearchServVehicle)
+| Field | Type | Notes |
+|-------|------|-------|
+| Vehicle | CombinedSearchVehicle | VIN + descriptive attributes |
+| VehicleServInfo | CombinedSearchVehicleServInfo | Service info attributes & nested warranty/advisor/comments |
+
+#### Vehicle (CombinedSearchVehicle)
+| Attribute | Type | Notes |
+|-----------|------|-------|
+| Vin | string | Vehicle identification number (may be partial in search results) |
+| VehicleMake | string | Make code / description |
+| VehicleYr | string\|number | Year |
+| MdlNo | string | Model code |
+| ModelDesc | string | Model description |
+| Carline | string | Carline description |
+| ExtClrDesc | string | Exterior color |
+| IntClrDesc | string | Interior color |
+| MakeName | string | Full make name |
+| VehicleDetail.LicNo | string | License plate (if present) |
+
+#### VehicleServInfo (CombinedSearchVehicleServInfo)
+| Attribute | Type | Notes |
+|-----------|------|-------|
+| CustomerNo | string\|number | Linked customer number |
+| SalesmanNo | string\|number | Optional salesman number |
+| InServiceDate | string\|number | Date vehicle placed in service |
+| Mileage | string\|number | Current mileage |
+| TeamCode | string | Team/department code |
+| VehExtWarranty.ContractNumber | string | Extended warranty contract number |
+| VehExtWarranty.ExpirationDate | string | Warranty expiration date |
+| VehExtWarranty.ExpirationMileage | string\|number | Warranty mileage limit |
+| Advisor.ContactInfo.NameRecId | string\|number | Advisor reference (if present) |
+| VehServComments[] | string[] | Freeform service comments (array of raw text) |
+
+#### Message (CombinedSearchMessage)
+| Field | Type | Notes |
+|-------|------|-------|
+| MessageNo | string\|number | Optional message number (if provided) |
+| Text | string | Message text content |
+
+### CustomerResponseData
+Returned as `result.data` for `insertCustomer` / `updateCustomer`.
+| Field | Type | Notes |
+|-------|------|-------|
+| dmsRecKey | string\|undefined | DMS record key identifier (if provided in TransStatus) |
+| status | string\|undefined | Vendor status string |
+| statusCode | string\|undefined | Vendor status code |
+
+### ServiceVehicleResponseData
+Returned as `result.data` for `insertServiceVehicle`.
+| Field | Type | Notes |
+|-------|------|-------|
+| status | string\|undefined | GenTransStatus Status |
+| statusCode | string\|undefined | GenTransStatus StatusCode |
+
+### RepairOrderData
+Returned as `result.data` for `createRepairOrder` / `updateRepairOrder` (parsed from RoRecordStatus).
+| Field | Type | Notes |
+|-------|------|-------|
+| status | string\|undefined | RoRecordStatus Status |
+| date | string\|undefined | Date attribute/text |
+| time | string\|undefined | Time attribute/text |
+| outsdRoNo | string\|undefined | External RO number (OutsdRoNo) |
+| dmsRoNo | string\|undefined | Internal DMS RO number (DMSRoNo) |
+| errorMessage | string\|undefined | ErrorMessage if provided |
+
+## RRResult Structure
+Each successful call resolves to:
+```ts
+interface RRResult {
+ success: boolean; // SUCCESS or NO_MATCH
+ data?: T; // op-specific parsed convenience data
+ parsed: any; // entire parsed STAR payload root
+ xml: { request: string; response: string }; // raw SOAP envelopes
+ statusBlocks?: { transaction?: {status,statusCode,message}; roRecord?: {status,date,time,outsdRoNo,dmsRoNo,errorMessage} };
+ applicationArea?: any; // raw ApplicationArea node
+}
+```
+Use `statusBlocks.transaction` for generic status; `data` for normalized op output.
+
+`Convenience data` refers to the distilled, operation-specific subset placed on `RRResult.data` by a dedicated `postParse` function (e.g., extracting only RoRecordStatus identifiers or customer DMS keys). It is intentionally smaller and flatter than `RRResult.parsed`, which contains the entire parsed STAR payload tree. Use `data` for common identifiers/status checks; fall back to `parsed` when you need full raw XML-derived detail.
+
+## Types & IntelliSense
+Rich JSDoc typedefs ship with the package.
+- ESM: `import { RRClient } from 'rr-rome-client';` optionally import `'rr-rome-client/types'` to prompt editor indexing.
+- CJS: `const { RRClient } = require('rr-rome-client'); require('rr-rome-client/types');`
+
+Selected typedef categories (see `src/types.js`):
+- Routing / Envelope / CallOptions
+- Customer / Service Vehicle / Repair Order payload blocks
+- Combined Search structures
+- Advisor and Parts row shapes
+- `RRResult` generic helper
+
+The build also emits a TypeScript declaration bundle (`dist/types/index.d.ts`) generated via `tsconfig.types.json` (processing only `src/types.js`).
+
+## Errors & Validation
+Three custom error classes (`src/errors.js`):
+- `RRTransportError` – Non-2xx HTTP status or network failure; `meta.status` / `meta.body` may be attached.
+- `RRVendorStatusError` – Vendor FAIL status (non-success & not NO_MATCH). Includes `meta.status` (raw status object) and full response XML. `retryable` may be set (currently determined by vendor message lock wording or explicit flag).
+- `RRValidationError` – Input validation failures (missing required fields, invalid enumeration values, etc.).
+
+Enumerated validations (examples):
+- Customer: `ibFlag`, required `firstName` if individual, allowed `customerType` values.
+- Service Vehicle: mandatory `vin` and `vehicleServInfo.customerNo`.
+- Repair Orders: required header fields; enumerations for tax pay type (`All|Cust|Intr|Warr`), taxable flags (`T|N`), item types (`G|P|S|F`).
+
+Error Handling Example:
+```js
+try {
+ await client.createRepairOrder(/* ... */);
+} catch (e) {
+ if (e instanceof RRVendorStatusError) {
+ console.error('Vendor fail', e.meta.status);
+ } else if (e instanceof RRValidationError) {
+ console.error('Bad input', e.message);
+ } else if (e instanceof RRTransportError) {
+ console.error('HTTP/Network', e.message, e.meta.status);
+ } else {
+ console.error('Unexpected', e);
+ }
+}
+```
+
+## Retry Strategy
+`withBackoff(fn, {max, logger})` (used internally) retries on:
+- Transport/network errors (`RRTransportError`).
+- Vendor status errors indicating record lock/in use (message matching `/lock|in use|record.*busy/i`) or explicit `retryable` flag.
+Backoff: exponential starting at 400ms, capped at 10s, plus up to 250ms jitter.
+Configure via `new RRClient({ retries: { max: 5 }, ... })`.
+
+## Debug / Dump Flags
+Set these environment variables to inspect request/response internals:
+- `RR_DEBUG` – Enables debug logging (`defaultLogger.debug`).
+- `RR_DUMP_ENVELOPE=1` – Prints outgoing SOAP envelope.
+- `RR_DUMP_XML=1` – Prints full response XML.
+- `RR_DUMP_STATUS=1` – Logs parsed status blocks.
+- `RR_DUMP_APPLICATION=1` – Logs raw ApplicationArea block.
+- `RR_DUMP_DATA=1` – Logs `result.data` (postParse convenience output).
+
+## Live Test Runner (`scripts/run-live.mjs`)
+Provides curated integration tests against a live Rome system using Vitest. Loads `.env` then `.env.local` (override). Usage:
+```
+node scripts/run-live.mjs [flags] [-- ...vitestArgs]
+```
+Available tests (see script for full list & flags):
+- `combinedSearch` – VIN or phone search.
+- `insertCustomer`, `updateCustomer` – Customer record operations (use `--write` to enable live writes).
+- `insertServiceVehicle` – Add vehicle (requires or discovers customer).
+- `getAdvisors` – Advisor listing by department.
+- `createRepairOrder`, `updateRepairOrder` – BSM RO operations.
+- `getParts` – Retrieve RO parts lines.
+- `all` – Run all sequentially.
+
+Common flags:
+- `--dump` → `RR_DUMP_ENVELOPE`
+- `--write` → `RR_LIVE_WRITES` (enables writes)
+- Operation-specific flags map to `RR_TEST_*` env variables (see script comments for details).
+- Arbitrary env: `--set=KEY=VALUE`.
+
+Example:
+```
+node scripts/run-live.mjs createRepairOrder --write --dump --customerNo=1134485 --vin=1ABCDEF2GHIJ34567 --ro=BSM123
+```
+
+## Bundling & Upload Helper (`scripts/bundle-for-upload.mjs`)
+Creates text bundles of project files for transport/support purposes.
+- Includes key directories (`src/`, `test/`, `schemas/`) and specific root files.
+- Excludes large/irrelevant directories (`node_modules`, `dist`, etc.).
+- Options: `--max-bytes`, `--pattern=globish`, `--with-env` (include `.env` – caution), `--list-only`.
+Generates `bundles/bundle--N.txt` with file boundary markers.
+
+## XML Templates & XSDs
+Templates reside in `src/templates/templateMap.js` using Mustache. Each operation builder renders a STAR root element plus `ApplicationArea`. XSD files (under `schemas/`) accompany operations:
+- Customer Insert/Update: `rey_RomeCustomerInsertReq.xsd`, `rey_RomeCustomerUpdateReq.xsd`
+- Service Vehicle Insert: `rey_RomeServVehicleInsertReq.xsd`
+- Combined Search: `rey_RomeCustServVehCombReq.xsd`
+- Advisors: `rey_RomeGetAdvisorsReq.xsd`
+- Parts: `rey_RomeGetPartsReq.xsd`
+- Repair Orders Create/Update: `rey_RomeCreateBSMRepairOrderReq.xsd`, `rey_RomeUpdateBSMRepairOrderReq.xsd`
+Response XSDs also present for repair orders and others.
+
+Builders attach an `xsdFilename` hint and `elementName`; no runtime XSD validation is performed (the project does not contain a validation module beyond these references).
+
+## Logging
+Default logger (`src/logger.js`): logs to console via `info`, `warn`, `error`; `debug` gated by `RR_DEBUG`.
+Provide a custom logger with matching method names in `RRClientConfig`:
+```js
+const logger = { info:()=>{}, warn:()=>{}, error:console.error, debug:()=>{} };
+const client = new RRClient({ baseUrl, username, password, logger });
+```
+
+## Build & Development
+Scripts:
+- `npm run build` – Rollup builds `dist/index.cjs` & `dist/index.mjs`, copies JSDoc types (`scripts/postbuild-copy-types.mjs`), emits `.d.ts` (via `tsc -p tsconfig.types.json`).
+- `npm test` – Runs unit tests (`vitest` with `vitest.config.unit.mjs`).
+- `npm run bundle` – Invoke bundle creation (see above).
+- `npm run live:` – Convenience commands mapping to `scripts/run-live.mjs` (e.g. `npm run live:getParts`).
+
+Rollup configuration (`rollup.config.mjs`):
+- Externalizes Node core modules and listed dependencies.
+- Applies terser minification (2 passes, hoisting) for compact output.
+- Generates both CJS and ESM entrypoints.
+
+Tree-shaking is enabled (`treeshake.moduleSideEffects = false`). `sideEffects: false` in `package.json` allows downstream bundlers to drop unused exports.
+
+## Design Notes / Non-goals
+- WS-Security: Implements only UsernameToken with password type Text or Digest (configurable via `wssePasswordType`). No nonce or timestamp included.
+- Idempotency: `bodId` generated per request unless provided; `ensureBodAndDates` centralizes creation.
+- Parsing: Focused on extracting status and op-specific identifiers; raw parsed STAR tree still accessible via `result.parsed`.
+- Status Handling: Treats `NO_MATCH` (codes like 2 or 213) as non-error with `success=true` so callers can differentiate empty results from failures.
+- Validation: Enforces only practical minimum & enumerations; does not attempt full schema compliance.
+- No automatic `fetch` transport fallback yet (axios chosen for reliability). You can replace `postSoap` if providing a compatible function returning raw response text and error semantics.
+- No built-in XSD validator; XSDs present for reference only.
\ No newline at end of file
diff --git a/server/rr/lib/index.cjs b/server/rr/lib/index.cjs
index a77ec9741..fb12eb2c1 100644
--- a/server/rr/lib/index.cjs
+++ b/server/rr/lib/index.cjs
@@ -1 +1 @@
-"use strict";function e(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}function t({routing:e={},sender:t={},creationDateTime:r,bodId:n}={}){const o={Sender:{Component:t.component??"Rome",Task:t.task??"CU",ReferenceId:t.referenceId??"Query",CreatorNameCode:t.creator??"RCI",SenderNameCode:t.senderName??"RCI"},CreationDateTime:r??(new Date).toISOString().replace(/\.\d{3}Z$/,"Z"),BODId:n??E.v4(),Destination:{DestinationNameCode:"RR",DealerNumber:e.dealerNumber||"",StoreNumber:e.storeNumber||"",AreaNumber:e.areaNumber||""}},a={bod:"{{#BODId}}{{BODId}}{{/BODId}}",creation:"{{CreationDateTime}}",sender:"\n \n {{Sender.Component}}\n {{Sender.Task}}\n {{#Sender.ReferenceId}}{{Sender.ReferenceId}}{{/Sender.ReferenceId}}\n {{#Sender.CreatorNameCode}}{{Sender.CreatorNameCode}}{{/Sender.CreatorNameCode}}\n {{#Sender.SenderNameCode}}{{Sender.SenderNameCode}}{{/Sender.SenderNameCode}}\n ".trim(),dest:"\n \n {{Destination.DestinationNameCode}}\n {{#Destination.DealerNumber}}{{Destination.DealerNumber}}{{/Destination.DealerNumber}}\n {{#Destination.StoreNumber}}{{Destination.StoreNumber}}{{/Destination.StoreNumber}}\n {{#Destination.AreaNumber}}{{Destination.AreaNumber}}{{/Destination.AreaNumber}}\n ".trim()},s=`\n${["bod","creation","sender","dest"].map(e=>a[e]).join("\n")}\n`;return j.default.render(s,o).trim()}function r(e,t=2){const r=" ".repeat(t);return String(e).split("\n").map(e=>e.length?r+e:e).join("\n")}function n(e){return String(e??"").replace(/&/g,"&").replace(//g,">")}function o(e,t){if(null==e)return null;if(Array.isArray(e)){for(const r of e){const e=o(r,t);if(null!=e)return e}return null}if("object"!=typeof e)return null;for(const[r,n]of Object.entries(e)){if(t(r,n))return n;const e=o(n,t);if(null!=e)return e}return null}function a(e,t,r=[]){if(null==e)return r;if(Array.isArray(e)){for(const n of e)a(n,t,r);return r}if("object"!=typeof e)return r;for(const[n,o]of Object.entries(e))t(n,o)&&r.push(o),a(o,t,r);return r}function s(e){return null==e?[]:Array.isArray(e)?e:[e]}function i(e,t){if(e&&"object"==typeof e)return function(e){if(null!=e)return"string"==typeof e||"number"==typeof e||"boolean"==typeof e?String(e):"object"==typeof e&&"#text"in e?String(e["#text"]):void 0}(e[t])}function l(e,t){const r=i(e,t);if(Q(r))return r;const n=function(e,t){if(e&&"object"==typeof e)return e.$&&Q(e.$[t])?e.$[t]:Q(e[`@_${t}`])?e[`@_${t}`]:Q(e[`@${t}`])?e[`@${t}`]:e._attributes&&Q(e._attributes[t])?e._attributes[t]:e.attributes&&Q(e.attributes[t])?e.attributes[t]:void 0}(e,t);return Q(n)?n:void 0}function c(e,t){if(e&&"object"==typeof e)return null!=e[`@_${t}`]?e[`@_${t}`]:void 0}function m(e){if(null!=e){if("string"==typeof e)return e;if("number"==typeof e)return String(e);if("object"==typeof e){if(null!=e._)return String(e._);if(null!=e["#text"])return String(e["#text"]);if(null!=e.text)return String(e.text)}}}function u(e){return a(e,e=>/(GenTransStatus|TransStatus)$/i.test(e)).flatMap(s)[0]}function p(e){if(e)return{status:(c(e,"Status")||e.Status||m(e)||"").toString().trim()||void 0,statusCode:(c(e,"StatusCode")||e.StatusCode||"").toString().trim()||void 0,message:e.Message&&m(e.Message)||e.GenTransStatus&&m(e.GenTransStatus)||e.TransStatus&&m(e.TransStatus)||m(e)||void 0}}function d(e){if(e)return{status:(c(e,"Status")||e.Status||m(e)||"").toString().trim()||void 0,date:(c(e,"Date")||e.Date||"").toString().trim()||void 0,time:(c(e,"Time")||e.Time||"").toString().trim()||void 0,outsdRoNo:(c(e,"OutsdRoNo")||e.OutsdRoNo||"").toString().trim()||void 0,dmsRoNo:(c(e,"DMSRoNo")||e.DMSRoNo||"").toString().trim()||void 0,errorMessage:(c(e,"ErrorMessage")||e.ErrorMessage||"").toString().trim()||void 0}}function y(e){return s((e?.rey_RomeCustServVehComb??e??{}).CustServVehComb).map(e=>{const t=e?.NameContactId??void 0,r=t?.NameId??void 0,n=r?.IndName?H(r.IndName):void 0,o=r?.BusName?H(r.BusName):void 0,a=t&&{NameId:r&&{...H(r)||{},...n?{IndName:n}:{},...o?{BusName:o}:{}},Address:s(t?.Address).map(e=>H(e)||{}),ContactOptions:s(t?.ContactOptions).map(e=>H(e)||{}),Phone:s(t?.Phone).map(e=>H(e)||{}),Email:s(t?.Email).map(e=>H(e)||{})},i=s(e?.ServVehicle).map(e=>{const t=e?.Vehicle,r=t?.VehicleDetail,n=t&&{...H(t)||{},...r?{VehicleDetail:H(r)}:{}},o=e?.VehicleServInfo,a=o?.VehExtWarranty,i=o?.Advisor,l=i?.ContactInfo,c=o&&{...H(o)||{},...a?{VehExtWarranty:H(a)}:{},...i?{Advisor:l?{ContactInfo:H(l)||{}}:{Advisor:{}}}:{},...e?.VehicleServInfo?.VehServComments?{VehServComments:s(e.VehicleServInfo.VehServComments).map(e=>Y(e))}:{}};return{...n?{Vehicle:n}:{},...c?{VehicleServInfo:c}:{}}}),l=s(e?.Message).map(e=>({...H(e)||{},Text:Y(e)})),c={};return a&&(c.NameContactId=a),i.length&&(c.ServVehicle=i),l.length&&(c.Message=l),c})}function g(e,t){if(null!=e)return String("string"==typeof e||"number"==typeof e?e:e[t]||"")}function N(e,...t){if(e)for(const r of t){if(e.$&&null!=e.$[r])return e.$[r];if(null!=e[`@_${r}`])return e[`@_${r}`];if(null!=e[r]&&"object"!=typeof e[r])return e[r]}}function T(e){const t=function(e){const t=o(e,e=>"rey_RomeCustomerResponse"===e||e.endsWith(":rey_RomeCustomerResponse"));return t?a(t,e=>"TransStatus"===e||e.endsWith(":TransStatus")).flatMap(s)[0]:void console.log("No rey_RomeCustomerResponse found in root")}(e);if(!t)return{dmsRecKey:void 0};const r=N(t,"DMSRecKey");return{dmsRecKey:null!=r?String(r):void 0,status:N(t,"Status"),statusCode:N(t,"StatusCode")}}function b(e){if(null==e)return;const t=String(e).toUpperCase().replace(/[^A-Z0-9 ]+/g,"");if(!t)throw new k(`Invalid string: ${e}. Must contain A-Z, 0-9, or space`);return t}function x(e){return e?String(e).toUpperCase():void 0}function C(e={}){const t=x(e.ibFlag)||(e.firstName?"I":"B");if("I"!==t&&"B"!==t)throw new k("ibFlag must be 'I' or 'B'");const r=e.lastName||e.customerName;if(!r)throw new k("lastName or customerName required");if("I"===t&&!e.firstName)throw new k("firstName required when ibFlag='I'");const n=x(e.customerType);if(n&&!["R","W","I"].includes(n))throw new k("customerType must be 'R', 'W', 'I', Retail, Wholesale, or Internal");const o=(e.addresses||[]).map(e=>{const t={Type:x(e.type)||"P",Addr1:e.line1?String(e.line1):void 0,Addr2:e.line2?String(e.line2):void 0,City:e.city?String(e.city):void 0,State:e.state?String(e.state):void 0,Zip:e.postalCode?String(e.postalCode):void 0,County:e.county?String(e.county):void 0,Country:e.country?String(e.country):void 0};if(!t.Addr1)throw new k("Address requires line1");return t}),a=(e.phones||[]).map(e=>{const t={Type:x(e.type)||"H",Num:e.number?String(e.number):void 0,Ext:e.extension?String(e.extension):void 0};if(!t.Num)throw new k("Phone requires number");return t}),s=e.emails?.[0]?.address?{MailTo:String(e.emails[0].address)}:void 0,i=e.personal,l=i?{Gender:x(i.gender),OtherName:b(i.otherName),AnniversaryDate:i.anniversaryDate?String(i.anniversaryDate):void 0,EmployerName:b(i.employerName),EmployerPhone:i.employerPhone?String(i.employerPhone):void 0,Occupation:b(i.occupation),OptOut:i.optOut?String(i.optOut):void 0,OptOutUse:i.optOutUse?String(i.optOutUse):void 0,BirthDates:(i.birthDates||[]).map(e=>({Type:x(e.type)||"P",date:e.date?String(e.date):void 0})).filter(e=>e.date),SSNs:(i.ssns||[]).map(e=>({Type:x(e.type)||"P",ssn:e.ssn?String(e.ssn):void 0})).filter(e=>e.ssn),DriverInfo:i.driver?[{Type:x(i.driver.type)||"P",LicNum:i.driver.licenseNumber?String(i.driver.licenseNumber):void 0,LicState:i.driver.licenseState?String(i.driver.licenseState):void 0,LicExpDate:i.driver.licenseExpDate?String(i.driver.licenseExpDate):void 0}].filter(e=>e.LicNum):void 0,CustChildren:(i.children||[]).map(e=>({ChildName:b(e.name)})).filter(e=>e.ChildName)}:void 0,c=e.dms,m=c?{TaxExemptNum:c.taxExemptNum?String(c.taxExemptNum):void 0,SalesTerritory:c.salesTerritory?String(c.salesTerritory):void 0,DeliveryRoute:c.deliveryRoute?String(c.deliveryRoute):void 0,SalesmanNum:c.salesmanNum?String(c.salesmanNum):void 0,LastContactMethod:c.lastContactMethod?String(c.lastContactMethod):void 0,Followups:(c.followups||[]).map(e=>({Type:x(e.type),Value:x(e.value)})).filter(e=>e.Type&&e.Value)}:void 0;return{custCateg:n||"R",createdBy:e.createdBy?String(e.createdBy):void 0,contactInfo:{IBFlag:t,LastName:b(r),FirstName:b(e.firstName),MidName:b(e.midName),Salut:b(e.salut),Suffix:b(e.suffix),Addresses:o,Phones:a,Email:s},custPersonal:l,dmsCustInfo:m}}function f(e){const t=o(e,e=>"rey_RomeServVehicleInsertResponse"===e||e.endsWith(":rey_RomeServVehicleInsertResponse"))||e,r=o(t,e=>"GenTransStatus"===e||e.endsWith(":GenTransStatus"));return{status:r?l(r,"Status"):void 0,statusCode:r?l(r,"StatusCode"):void 0}}function R(e,t){const r=Array.isArray(t)?t.map(e=>e.toLowerCase()):[String(t).toLowerCase()],n=[e];for(;n.length;){const e=n.pop();if(Z(e))for(const t of Object.keys(e)){const o=e[t],a=X(t).toLowerCase();if(r.includes(a))return o;if(Z(o))n.push(o);else if(Array.isArray(o))for(const e of o)Z(e)&&n.push(e)}}}function S(e,t){if(e&&Z(e)){if(null!=e.$?.[t])return e.$[t];if(null!=e[`@${t}`])return e[`@${t}`];if(null!=e[`@_${t}`])return e[`@_${t}`]}}function v(e){if(null!=e){if("string"==typeof e)return e;if("number"==typeof e)return String(e);if(Z(e)){if(null!=e._)return String(e._);if(null!=e["#text"])return String(e["#text"]);if(null!=e.text)return String(e.text)}}}function O(e){const t=R(e,["CreateBSMRepairOrderResp","UpdateBSMRepairOrderResp"])||e,r=R(t,"RoRecordStatus")||{};return{status:S(r,"Status")||v(R(r,"Status")),date:S(r,"Date")||v(R(r,"Date")),time:S(r,"Time")||v(R(r,"Time")),outsdRoNo:S(r,"OutsdRoNo")||v(R(r,"OutsdRoNo")),dmsRoNo:S(r,"DMSRoNo")||v(R(r,"DMSRoNo")),errorMessage:S(r,"ErrorMessage")||v(R(r,"ErrorMessage"))}}function F(e={},r={}){const n=function(e){if(!e)throw new Error("department is required (S, P, B, SERVICE, PARTS, BODY)");const t=String(e).trim().toUpperCase();if("S"===t||"P"===t||"B"===t)return t;if("SERVICE"===t)return"S";if("PART"===t||"PARTS"===t)return"P";if("BODY"===t||"BODYSHOP"===t||"BODY SHOP"===t)return"B";throw new Error(`Invalid department: ${e}. Must be S, P, B, SERVICE, PARTS, BODY, BODYSHOP, or BODY SHOP`)}(e.department),o=e.advisorNumber?String(e.advisorNumber).trim():void 0,a=t({routing:r.routing,sender:r?.envelope?.sender,creationDateTime:r?.envelope?.creationDateTime,bodId:r?.envelope?.bodId});return{starXml:j.default.render('\n\n {{{ApplicationArea}}}\n \n \n \n\n',{ApplicationArea:a,DepartmentType:n,AdvisorNumber:o}),routing:r.routing,envelope:r.envelope,xsdFilename:"rey_RomeGetAdvisorsReq.xsd",elementName:"rey_RomeGetAdvisorsReq",postParse:e=>function(e,t={}){const r=t?.department;var n;return(e=>{const t=e?.Advisor;return t?Array.isArray(t)?t:[t]:[]})((n=e,n?.rey_RomeGetAdvisorsResp??n??{})).map(e=>({advisorId:J(e,"AdvisorNumber"),firstName:J(e,"FirstName"),lastName:J(e,"LastName"),department:r}))}(e,{department:n})}}function I(e){return null==e?void 0:String(e)}function h(e,t){if(e)return null!=e[t]?"object"!=typeof e[t]?I(e[t]):I(e[t]["#text"]):null!=e[`@_${t}`]?I(e[`@_${t}`]):void 0}function w(e){const t=e?.meta?.statusBlocks?.transaction?.message;if(t)return String(t);return String((e?.meta?.status?.Message??e?.meta?.status?.message)||e?.message||"")}function P(e,t){return Math.min(1e4,e*Math.pow(2,t))}function D(e){return e+Math.floor(250*Math.random())}function A(e){return new Promise(t=>setTimeout(t,e))}function M(e){return/lock|in use|record.*busy/i.test(String(e||""))}Object.defineProperty(exports,"__esModule",{value:!0});const V=require("mustache"),E=require("uuid"),L=require("axios"),_=require("fast-xml-parser"),j=e(V),B=e(L);class q extends Error{constructor(e,t={}){super(e),this.name="RRTransportError",this.meta=t}}class U extends Error{constructor(e,t={}){super(e),this.name="RRVendorStatusError",this.meta=t,this.retryable=!!t.retryable}}class k extends Error{constructor(e,t={}){super(e),this.name="RRValidationError",this.meta=t}}const G=Object.freeze({__proto__:null,RRTransportError:q,RRVendorStatusError:U,RRValidationError:k}),$={info:(...e)=>console.log("[rr-rome]",...e),warn:(...e)=>console.warn("[rr-rome]",...e),error:(...e)=>console.error("[rr-rome]",...e),debug:(...e)=>{process.env.RR_DEBUG&&console.log("[rr-rome][debug]",...e)}},Q=e=>null!=e&&""!==String(e).trim(),W=new _.XMLParser({ignoreAttributes:!1,attributeNamePrefix:"@_",parseAttributeValue:!1,parseTagValue:!1,isArray:e=>["Advisor"].includes(e)}),H=e=>{if(!e||"object"!=typeof e)return;const t={};for(const[r,n]of Object.entries(e))r.startsWith("@_")&&(t[r.slice(2)]=n);return Object.keys(t).length?t:void 0},Y=e=>{if(null!=e)return"string"==typeof e?e:e["#text"]},X=e=>{if("string"!=typeof e)return"";let t=e.includes("}")?e.split("}").pop():e;return t=t.includes(":")?t.split(":").pop():t,t.startsWith("rey_")&&(t=t.slice(4)),t},Z=e=>e&&"object"==typeof e&&!Array.isArray(e),J=(e,t)=>((e,t)=>e?.[`@_${t}`])(e,t)??i(e,t);exports.RRClient=class{constructor(e){if(!e?.baseUrl)throw new Error("RRClient requires baseUrl");if(!e?.username)throw new Error("RRClient requires username");if(!e?.password)throw new Error("RRClient requires password");this.cfg={wssePasswordType:"Text",timeoutMs:3e4,logger:$,retries:{max:3},...e},this.mask={password:!0}}async _send(e){const{starXml:t,routing:o,envelope:i,postParse:l}=e,y=this.cfg.logger||$,{bodId:g,creationDateTime:N,sender:T}=function(e){return{bodId:e?.bodId||E.v4(),creationDateTime:e?.creationDateTime||new Date,sender:e?.sender||{}}}(i),b=function({username:e,password:t,wssePasswordType:o="Text",starContentXml:a}){const s=function(e){return`\n \n \n \n${r(e,10)}\n \n \n \n `.trim()}(a);return`\n \n \n${r(function(e,t,r){const o="Digest"===r?' Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest"':' Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText"';return`\n \n \n ${n(e)}\n ${n(t)}\n \n \n `.trim()}(e,t,o),8)}\n \n \n${r(s,8)}\n \n \n `.trim()}({username:this.cfg.username,password:this.cfg.password,wssePasswordType:this.cfg.wssePasswordType||"Text",routing:o,sender:T,creationDateTime:(x=N,"string"==typeof x?x:(x instanceof Date?x:new Date).toISOString().replace(/\.\d{3}Z$/,"Z")),bodId:g,starContentXml:t});var x;"1"===process.env.RR_DUMP_ENVELOPE&&console.log(`[rr] Soap envelope about to send (${e?.elementName}): \n`+b+"\n");const C=async()=>{const t=await async function({baseUrl:e,envelopeXml:t,timeoutMs:r=3e4,logger:n}){try{const n=await B.default.post(e,t,{timeout:r,headers:{"Content-Type":"text/xml; charset=utf-8",SOAPAction:"http://www.starstandards.org/webservices/2005/10/transport/ProcessMessage"},responseType:"text",validateStatus:()=>!0});if(n.status>=200&&n.status<300)return String(n.data??"");throw new q(`HTTP ${n.status}: ${function(e){const t=String(e??"");return t.length>1024?t.slice(0,1024)+"…":t}(n.data)}`,{status:n.status,body:n.data})}catch(e){if(B.default.isAxiosError(e))throw new q(`Network error: ${e.message}`,{cause:e});throw e}}({baseUrl:this.cfg.baseUrl,envelopeXml:b,timeoutMs:this.cfg.timeoutMs,logger:y}),r=function(e){const t=W.parse(e),r=t?.Envelope||t?.["soapenv:Envelope"]||t,n=r?.Body||r?.["soapenv:Body"]||r?.["S:Body"]||r?.["soap:Body"],o=n?.ProcessMessageResponse||n?.["ns2:ProcessMessageResponse"]||n?.["trans:ProcessMessageResponse"]||n,a=o?.payload||o?.["ns2:payload"]||o;return a?.content||a?.["ns2:content"]||a}(t),n=function(e){const t=u(e),r=a(e,e=>/RoRecordStatus$/i.test(e)).flatMap(s)[0];return{transaction:p(t),roRecord:d(r)}}(r),o=function(e){const t=String("ApplicationArea").toLowerCase(),r=[e];for(;r.length;){const e=r.pop();if(e&&"object"==typeof e)for(const[n,o]of Object.entries(e)){if(n.toLowerCase().endsWith(t))return o;o&&"object"==typeof o&&r.push(o)}}}(r),{status:i,norm:g}=function(e){const t=u(e)||a(e,e=>/Status$/i.test(e)).flatMap(s)[0]||{},r=(c(t,"Status")||t.Status||"").toString().trim();let n=(c(t,"StatusCode")||t.StatusCode||"").toString().trim();const o=t.Message&&m(t.Message)||m(t)||"";!n&&/success/i.test(r)&&(n="0");const i=Number(n);let l="FAIL";return/success/i.test(r)||0===i?l="SUCCESS":2!==i&&213!==i||(l="NO_MATCH"),{status:{Status:r,StatusCode:n,Message:o},norm:{kind:l,code:Number.isFinite(i)?i:void 0,message:o}}}(r);if("1"===process.env.RR_DUMP_STATUS&&(console.log("[rr] Status blocks:"),console.dir(n,{depth:null,colors:!0})),"1"===process.env.RR_DUMP_APPLICATION&&(console.log("[rr] ApplicationArea:"),console.dir(o,{depth:null,colors:!0})),"FAIL"===g.kind){const e=i?.StatusCode;throw new U(`Vendor status failure: ${e??""} ${i?.Message||i?.["#text"]||""}`.trim(),{status:i,resXml:t})}const N={success:"SUCCESS"===g.kind||"NO_MATCH"===g.kind,statusBlocks:n,applicationArea:o,xml:{request:b,response:t},parsed:r};if("function"==typeof l)try{N.data=l(r)}catch(e){y?.warn?.(`postParse failed: ${e?.message||e}`)}return"1"===process.env.RR_DUMP_XML&&console.log(`[rr] Full response XML (${e?.elementName}):\n`+t+"\n"),N?.data&&"1"===process.env.RR_DUMP_DATA&&(console.log(`[rr] Parsed response data (${e?.elementName}):\n`),console.dir(N.data,{depth:null,colors:!0})),N};try{return await async function(e,{max:t=3,logger:r}){let n,o=0;for(;o=t)break;const s=D(P(400,o));r?.warn?.(`Retrying attempt ${o}/${t} in ${s}ms: ${e.message}`),await A(s)}throw n}(C,{max:this.cfg.retries?.max??3,logger:y})}catch(e){if(e instanceof q||e instanceof U)throw e;throw new q(`Unexpected error: ${e?.message||e}`,{cause:e})}}async combinedSearch(e,r){return this._send(function(e={},r={}){if(!e||"object"!=typeof e)throw new k("combinedSearch requires a query object");const n=function(e){if(!e)return"";const t=String(e).trim().toLowerCase();return"telephone"===t?"phone":"lic"===t||"plate"===t?"license":"vin"===t||"partvin"===t?"vin":"name"===t?"name":"nameRecId"===t||"custId"===t?"nameRecId":"stkNo"===t||"stock"===t?"stkNo":t}(e.kind),o={MaxRecs:Math.min(Number(e.maxResults||e.maxRecs||50),50)};if("phone"===n){const t=g(e.phone,"phone");if(!t)throw new k("combinedSearch phone value missing");o.Phone=t}else if("license"===n){const t=g(e.license,"license");if(!t)throw new k("combinedSearch license value missing");o.LicenseNum=t}else if("vin"===n){const t=g(e.vin,"vin");if(!t)throw new k("combinedSearch vin value missing");o.PartVIN=t}else if("name"===n){const t=e.name;if(!t||"object"!=typeof t)throw new k("combinedSearch name requires { fname, lname, mname } or { name }");if(t.fname&&t.lname&&t.mname)o.FullName={FName:String(t.fname),LName:String(t.lname),MName:String(t.mname)};else{if(!t.name)throw new k("combinedSearch name requires { fname, lname, mname } or { name }");o.LName=String(t.name)}}else if("nameRecId"===n){const t=g(e.nameRecId,"custId");if(!t)throw new k("combinedSearch nameRecId value missing");o.NameRecId=t}else{if("stkNo"!==n)throw new k(`Unsupported CombinedSearch kind: ${e.kind}`);{const t=g(e.stkNo,"stkNo");if(!t)throw new k("combinedSearch stkNo value missing");o.StkNo=t}}if(1!==[o.Phone,o.LicenseNum,o.PartVIN,o.LName,o.FullName,o.NameRecId,o.StkNo].filter(Boolean).length)throw new k("combinedSearch requires exactly one search criterion");o.VehData={MakePfx:e.make||"ANY",Model:e.model||"ANY",Year:e.year||"ANY"};const a=t({routing:r.routing,sender:{component:"Rome",task:"CVC",referenceId:"Query",creator:"RCI",senderName:"RCI"},order:"creation-bod-sender-destination"});return{starXml:j.default.render('\n\n {{{ApplicationArea}}}\n \n \n {{#Phone}}{{/Phone}}\n {{#PartVIN}}{{/PartVIN}}\n {{#LicenseNum}}{{/LicenseNum}}\n {{#LName}}{{/LName}}\n {{#FullName}}{{/FullName}}\n {{#NameRecId}}{{/NameRecId}}\n {{#StkNo}}{{/StkNo}}\n \n \n \n\n',{ApplicationArea:a,...o}),routing:r.routing,envelope:r.envelope,elementName:"rey_RomeCustServVehCombReq",xsdFilename:"rey_RomeCustServVehCombReq.xsd",postParse:y}}(e,r))}async insertCustomer(e,r){return this._send(function(e,r){if(!r?.routing?.dealerNumber)throw new k("routing.dealerNumber required");const n=C(e),o=t({routing:r.routing,sender:{component:"Rome",task:"CU",referenceId:"Insert"},order:"sender-creation-bod-destination"});return{starXml:j.default.render('\n\n {{{ApplicationArea}}}\n \n \n {{#C.contactInfo.Addresses}}\n \n {{/C.contactInfo.Addresses}}\n {{#C.contactInfo.Phones}}\n \n {{/C.contactInfo.Phones}}\n {{#C.contactInfo.Email}}\n \n {{/C.contactInfo.Email}}\n \n {{#C.custPersonal}}\n \n {{#BirthDates}}\n \n {{/BirthDates}}\n {{#SSNs}}\n \n {{/SSNs}}\n {{#DriverInfo}}\n \n {{/DriverInfo}}\n {{#CustChildren}}\n \n {{/CustChildren}}\n \n {{/C.custPersonal}}\n {{#C.dmsCustInfo}}\n \n {{#Followups}}\n \n {{/Followups}}\n \n {{/C.dmsCustInfo}}\n \n\n',{ApplicationArea:o,C:n}),routing:r.routing,envelope:r.envelope,postParse:T,xsdFilename:"rey_RomeCustomerInsertReq.xsd",elementName:"rey_RomeCustomerInsertReq"}}(e,r))}async updateCustomer(e,r){return this._send(function(e,r){if(!r?.routing?.dealerNumber)throw new k("routing.dealerNumber required");if(!e.nameRecId)throw new k("nameRecId or customerId required");const n=x(e.ibFlag);if("I"!==n&&"B"!==n)throw new k("ibFlag required ('I' or 'B')");const o=C(e);o.contactInfo.NameRecId=String(e.nameRecId);const a=t({routing:r.routing,sender:{component:"Rome",task:"CU",referenceId:"Update"},order:"sender-creation-bod-destination"});return{starXml:j.default.render('\n\n {{{ApplicationArea}}}\n \n \n {{#C.contactInfo.Addresses}}\n \n {{/C.contactInfo.Addresses}}\n {{#C.contactInfo.Phones}}\n \n {{/C.contactInfo.Phones}}\n {{#C.contactInfo.Email}}\n \n {{/C.contactInfo.Email}}\n \n {{#C.custPersonal}}\n \n {{#BirthDates}}\n \n {{/BirthDates}}\n {{#SSNs}}\n \n {{/SSNs}}\n {{#DriverInfo}}\n \n {{/DriverInfo}}\n {{#CustChildren}}\n \n {{/CustChildren}}\n \n {{/C.custPersonal}}\n {{#C.dmsCustInfo}}\n \n {{#Followups}}\n \n {{/Followups}}\n \n {{/C.dmsCustInfo}}\n \n\n',{ApplicationArea:a,C:o}),routing:r.routing,envelope:r.envelope,postParse:T,xsdFilename:"rey_RomeCustomerUpdateReq.xsd",elementName:"rey_RomeCustomerUpdateReq"}}(e,r))}async insertServiceVehicle(e,r){return this._send(function(e={},r={}){if(!r?.routing?.dealerNumber)throw new k("routing.dealerNumber is required");const n=t({routing:r.routing,sender:r?.envelope?.sender??{component:"Rome",task:"SV",referenceId:"Insert"},creationDateTime:r?.envelope?.creationDateTime,bodId:r?.envelope?.bodId}),o=function(e={}){const t=e.vin;if(!t)throw new k("insertServiceVehicle: vin is required");const r=e.vehicleServInfo?.customerNo;if(!r)throw new k("insertServiceVehicle: vehicleServInfo.customerNo (or customerNo) is required");return{VIN:String(t),ModelDesc:e.modelDesc,Carline:e.carline,ExtClrDesc:e.extClrDesc,IntClrDesc:e.intClrDesc,TrimDesc:e.trimDesc,BodyStyle:e.bodyStyle,EngineDesc:e.engineDesc,TransDesc:e.transDesc,Year:e.year,Odometer:e.odometer,OdometerUnits:e.odometerUnits,LicNo:e.vehicleDetail?.licNo,CustomerNo:String(r),vehicleServInfo:{salesmanNo:e.vehicleServInfo?.salesmanNo,inServiceDate:e.vehicleServInfo?.inServiceDate,mileage:e.vehicleServInfo?.mileage,teamCode:e.vehicleServInfo?.teamCode,vehExtWarranty:(()=>{const t=e.vehicleServInfo?.vehExtWarranty;if(!t)return;const r={contractNumber:t.contractNumber,expirationDate:t.expirationDate,expirationMileage:t.expirationMileage};return Object.values(r).some(e=>null!=e&&""!==e)?r:void 0})(),advisor:(()=>{const t=e.vehicleServInfo?.advisor,r=t?.contactInfo?.nameRecId;return r?{contactInfo:{nameRecId:String(r)}}:void 0})()}}}(e);return{starXml:j.default.render('\n\n {{{ApplicationArea}}}\n\n \n {{#V.Year}}{{.}}{{/V.Year}}\n {{#V.Odometer}}{{.}}{{/V.Odometer}}\n {{#V.OdometerUnits}}{{.}}{{/V.OdometerUnits}}\n\n {{#V.LicNo}}{{/V.LicNo}}\n \n\n \n {{#V.vehicleServInfo.salesmanNo}}{{.}}{{/V.vehicleServInfo.salesmanNo}}\n {{#V.vehicleServInfo.inServiceDate}}{{.}}{{/V.vehicleServInfo.inServiceDate}}\n {{#V.vehicleServInfo.mileage}}{{.}}{{/V.vehicleServInfo.mileage}}\n {{#V.vehicleServInfo.teamCode}}{{.}}{{/V.vehicleServInfo.teamCode}}\n\n {{#V.vehicleServInfo.vehExtWarranty}}\n \n {{#contractNumber}}{{.}}{{/contractNumber}}\n {{#expirationDate}}{{.}}{{/expirationDate}}\n {{#expirationMileage}}{{.}}{{/expirationMileage}}\n \n {{/V.vehicleServInfo.vehExtWarranty}}\n\n {{#V.vehicleServInfo.advisor}}\n \n \n \n {{/V.vehicleServInfo.advisor}}\n \n\n',{ApplicationArea:n,V:o}),routing:r.routing,envelope:r.envelope,postParse:f,xsdFilename:"rey_RomeServVehicleInsertReq.xsd",elementName:"rey_RomeServVehicleInsertReq"}}(e,r))}async getAdvisors(e,t){return this._send(F(e,t))}async createRepairOrder(e,r){return this._send(function(e={},r={}){if(!r?.routing?.dealerNumber)throw new k("routing.dealerNumber required");if(!e?.customerNo)throw new k("customerNo (CustNo) required");if(!e?.departmentType)throw new k("departmentType (DeptType) required");if(!e?.vin)throw new k("vin (Vin) required");if(!e?.outsdRoNo)throw new k("outsdRoNo required");const n={customerNo:e.customerNo,advisorNo:e.advisorNo,tagNo:e.tagNo,outsdRoNo:e.outsdRoNo,departmentType:e.departmentType,vin:e.vin,mileageIn:e.mileageIn,roComment:e.roComment,estimate:e.estimate?{parts:e.estimate.parts,labor:e.estimate.labor,total:e.estimate.total}:void 0,tax:e.tax?{payType:e.tax.payType,taxCode:e.tax.taxCode,txblGrossAmt:e.tax.txblGrossAmt,grossTaxAmt:e.tax.grossTaxAmt}:void 0,rolabor:e.rolabor?{ops:e.rolabor.ops?.map(e=>({opCode:e.opCode,jobNo:e.jobNo,custPayTypeFlag:e.custPayTypeFlag,custTxblNtxblFlag:e.custTxblNtxblFlag,warrPayTypeFlag:e.warrPayTypeFlag,warrTxblNtxblFlag:e.warrTxblNtxblFlag,intrPayTypeFlag:e.intrPayTypeFlag,intrTxblNtxblFlag:e.intrTxblNtxblFlag,vlrCode:e.vlrCode,bill:e.bill?{payType:e.bill.payType,jobTotalHrs:e.bill.jobTotalHrs,billTime:e.bill.billTime,billRate:e.bill.billRate}:void 0,ccc:e.ccc?{cause:e.ccc.cause,complaint:e.ccc.complaint,correction:e.ccc.correction}:void 0,amount:e.amount?{payType:e.amount.payType,amtType:e.amount.amtType,custPrice:e.amount.custPrice,totalAmt:e.amount.totalAmt}:void 0}))}:void 0,ropart:e.ropart?{jobs:e.ropart.jobs?.map(e=>({opCode:e.opCode,jobNo:e.jobNo,lines:e.lines?.map(e=>({partNo:e.partNo,partNoDesc:e.partNoDesc,qtyOrd:e.partQty,sale:e.sale,cost:e.cost,addDeleteFlag:e.addDeleteFlag}))}))}:void 0,rogg:e.rogg?{roNo:e.rogg.roNo,ops:e.rogg.ops?.map(e=>({opCode:e.opCode,jobNo:e.jobNo,lines:e.lines?.map(e=>({breakOut:e.breakOut,itemType:e.itemType,itemDesc:e.itemDesc,custQty:e.custQty,warrQty:e.warrQty,intrQty:e.intrQty,custPayTypeFlag:e.custPayTypeFlag,warrPayTypeFlag:e.warrPayTypeFlag,intrPayTypeFlag:e.intrPayTypeFlag,custTxblNtxblFlag:e.custTxblNtxblFlag,warrTxblNtxblFlag:e.warrTxblNtxblFlag,intrTxblNtxblFlag:e.intrTxblNtxblFlag,amount:e.amount?{payType:e.amount.payType,amtType:e.amount.amtType,custPrice:e.amount.custPrice,dlrCost:e.amount.dlrCost}:void 0}))}))}:void 0,romisc:e.romisc?{roNo:e.romisc.roNo,ops:e.romisc.ops?.map(e=>({opCode:e.opCode,jobNo:e.jobNo,lines:e.lines?.map(e=>({miscCode:e.miscCode,custPayTypeFlag:e.custPayTypeFlag,warrPayTypeFlag:e.warrPayTypeFlag,intrPayTypeFlag:e.intrPayTypeFlag,custTxblNtxblFlag:e.custTxblNtxblFlag,warrTxblNtxblFlag:e.warrTxblNtxblFlag,intrTxblNtxblFlag:e.intrTxblNtxblFlag,codeAmt:e.codeAmt}))}))}:void 0};if(n.tax?.payType&&!["All","Cust","Intr","Warr"].includes(n.tax.payType))throw new k("tax.payType must be one of: All, Cust, Intr, Warr");if(n.rolabor?.ops?.some(e=>e.custTxblNtxblFlag&&!["T","N"].includes(e.custTxblNtxblFlag)||e.warrTxblNtxblFlag&&!["T","N"].includes(e.warrTxblNtxblFlag)||e.intrTxblNtxblFlag&&!["T","N"].includes(e.intrTxblNtxblFlag)))throw new k("Taxable flags (CustTxblNTxblFlag, WarrTxblNTxblFlag, IntrTxblNTxblFlag) must be 'T' or 'N'");if(n.rogg?.ops?.some(e=>e.lines?.some(e=>e.itemType&&!["G","P","S","F"].includes(e.itemType))))throw new k("rogg.ops.lines.itemType must be one of: G, P, S, F");const o={...r?.envelope||{},sender:{component:r?.envelope?.sender?.component??"Rome",task:r?.envelope?.sender?.task??"BSMRO",referenceId:r?.envelope?.sender?.referenceId??"Insert",creatorNameCode:r?.envelope?.sender?.creatorNameCode??"RCI",senderNameCode:r?.envelope?.sender?.senderNameCode??"RCI"}},a=t({routing:r.routing,sender:o.sender,creationDateTime:o.creationDateTime,bodId:o.bodId});return{starXml:j.default.render('\n\n {{{ApplicationArea}}}\n \n \n {{#RO.roComment}}\n \n {{/RO.roComment}}\n\n {{#RO.estimate}}\n \n {{/RO.estimate}}\n\n {{#RO.tax}}\n \n {{/RO.tax}}\n \n\n {{#RO.rolabor}}\n \n {{#RO.rolabor.ops}}\n \n {{#bill}}\n \n {{/bill}}\n\n {{#ccc}}\n \n {{/ccc}}\n\n {{#amount}}\n \n {{/amount}}\n \n {{/RO.rolabor.ops}}\n \n {{/RO.rolabor}}\n\n {{#RO.ropart}}\n \n {{#RO.ropart.jobs}}\n \n {{#lines}}\n \n {{/lines}}\n \n {{/RO.ropart.jobs}}\n \n {{/RO.ropart}}\n\n {{#RO.rogg}}\n \n {{#RO.rogg.ops}}\n \n {{#lines}}\n \n {{#amount}}\n \n {{/amount}}\n \n {{/lines}}\n \n {{/RO.rogg.ops}}\n \n {{/RO.rogg}}\n\n {{#RO.romisc}}\n \n {{#RO.romisc.ops}}\n \n {{#lines}}\n \n {{/lines}}\n \n {{/RO.romisc.ops}}\n \n {{/RO.romisc}}\n \n\n',{ApplicationArea:a,RO:n}),routing:r.routing,envelope:o,postParse:O,xsdFilename:"rey_RomeCreateBSMRepairOrderReq.xsd"}}(e,r))}async updateRepairOrder(e,r){return this._send(function(e={},r={}){if(!r?.routing?.dealerNumber)throw new k("routing.dealerNumber required");if(!e?.finalUpdate)throw new k("finalUpdate (FinalUpdate) required");if(!["Y","N"].includes(e.finalUpdate))throw new k("finalUpdate must be 'Y' or 'N'");if(!e?.outsdRoNo)throw new k(" outsdRoNo");const n={finalUpdate:e.finalUpdate||"N",roNo:e.roNo,customerNo:e.customerNo,tagNo:e.tagNo,outsdRoNo:e.outsdRoNo,departmentType:e.departmentType,vin:e.vin,mileageIn:e.mileageIn,mileageOut:e.mileageOut,roComment:e.roComment,estimate:e.estimate?{estimateType:e.estimate.estimateType}:void 0,tax:e.tax?{payType:e.tax.payType,taxCode:e.tax.taxCode,txblGrossAmt:e.tax.txblGrossAmt,grossTaxAmt:e.tax.grossTaxAmt}:void 0,rolabor:e.rolabor?{ops:e.rolabor.ops?.map(e=>({opCode:e.opCode,jobNo:e.jobNo,custPayTypeFlag:e.custPayTypeFlag,custTxblNtxblFlag:e.custTxblNtxblFlag,warrPayTypeFlag:e.warrPayTypeFlag,warrTxblNtxblFlag:e.warrTxblNtxblFlag,intrPayTypeFlag:e.intrPayTypeFlag,intrTxblNtxblFlag:e.intrTxblNtxblFlag,vlrCode:e.vlrCode,bill:e.bill?{payType:e.bill.payType,jobTotalHrs:e.bill.jobTotalHrs,billTime:e.bill.billTime,billRate:e.bill.billRate}:void 0,ccc:e.ccc?{cause:e.ccc.cause,complaint:e.ccc.complaint,correction:e.ccc.correction}:void 0,amount:e.amount?{payType:e.amount.payType,amtType:e.amount.amtType,custPrice:e.amount.custPrice}:void 0}))}:void 0,ropart:e.ropart?{jobs:e.ropart.jobs?.map(e=>({opCode:e.opCode,jobNo:e.jobNo,lines:e.lines?.map(e=>({partNo:e.partNo,partNoDesc:e.partNoDesc,qtyOrd:e.partQty,sale:e.sale,cost:e.cost,addDeleteFlag:e.addDeleteFlag}))}))}:void 0,rogg:e.rogg?{roNo:e.rogg.roNo,ops:e.rogg.ops?.map(e=>({opCode:e.opCode,jobNo:e.jobNo,lines:e.lines?.map(e=>({breakOut:e.breakOut,itemType:e.itemType,itemDesc:e.itemDesc,custQty:e.custQty,warrQty:e.warrQty,intrQty:e.intrQty,custPayTypeFlag:e.custPayTypeFlag,warrPayTypeFlag:e.warrPayTypeFlag,intrPayTypeFlag:e.intrPayTypeFlag,custTxblNtxblFlag:e.custTxblNtxblFlag,warrTxblNtxblFlag:e.warrTxblNtxblFlag,intrTxblNtxblFlag:e.intrTxblNtxblFlag,amount:e.amount?{payType:e.amount.payType,amtType:e.amount.amtType,custPrice:e.amount.custPrice,dlrCost:e.amount.dlrCost}:void 0}))}))}:void 0,romisc:e.romisc?{roNo:e.romisc.roNo,ops:e.romisc.ops?.map(e=>({opCode:e.opCode,jobNo:e.jobNo,lines:e.lines?.map(e=>({miscCode:e.miscCode,custPayTypeFlag:e.custPayTypeFlag,warrPayTypeFlag:e.warrPayTypeFlag,intrPayTypeFlag:e.intrPayTypeFlag,custTxblNtxblFlag:e.custTxblNtxblFlag,warrTxblNtxblFlag:e.warrTxblNtxblFlag,intrTxblNtxblFlag:e.intrTxblNtxblFlag,codeAmt:e.codeAmt}))}))}:void 0};if(n.tax?.payType&&!["All","Cust","Intr","Warr"].includes(n.tax.payType))throw new k("tax.payType must be one of: All, Cust, Intr, Warr");if(n.rolabor?.ops?.some(e=>e.custTxblNtxblFlag&&!["T","N"].includes(e.custTxblNtxblFlag)||e.warrTxblNtxblFlag&&!["T","N"].includes(e.warrTxblNtxblFlag)||e.intrTxblNtxblFlag&&!["T","N"].includes(e.intrTxblNtxblFlag)))throw new k("Taxable flags (CustTxblNTxblFlag, WarrTxblNTxblFlag, IntrTxblNTxblFlag) must be 'T' or 'N'");if(n.rogg?.ops?.some(e=>e.lines?.some(e=>e.itemType&&!["G","P","S","F"].includes(e.itemType))))throw new k("rogg.ops.lines.itemType must be one of: G, P, S, F");const o={...r?.envelope||{},sender:{component:r?.envelope?.sender?.component??"Rome",task:r?.envelope?.sender?.task??"BSMRO",referenceId:r?.envelope?.sender?.referenceId??"Update",creatorNameCode:r?.envelope?.sender?.creatorNameCode??"RCI",senderNameCode:r?.envelope?.sender?.senderNameCode??"RCI"}},a=t({routing:r.routing,sender:o.sender,creationDateTime:o.creationDateTime,bodId:o.bodId});return{starXml:j.default.render('\n\n {{{ApplicationArea}}}\n \n \n {{#RO.roComment}}\n \n {{/RO.roComment}}\n\n {{#RO.estimate}}\n \n {{/RO.estimate}}\n\n {{#RO.tax}}\n \n {{/RO.tax}}\n \n\n {{#RO.rolabor}}\n \n {{#RO.rolabor.ops}}\n \n {{#bill}}\n \n {{/bill}}\n\n {{#ccc}}\n \n {{/ccc}}\n\n {{#amount}}\n \n {{/amount}}\n \n {{/RO.rolabor.ops}}\n \n {{/RO.rolabor}}\n\n {{#RO.ropart}}\n \n {{#RO.ropart.jobs}}\n \n {{#lines}}\n \n {{/lines}}\n \n {{/RO.ropart.jobs}}\n \n {{/RO.ropart}}\n\n {{#RO.rogg}}\n \n {{#RO.rogg.ops}}\n \n {{#lines}}\n \n {{#amount}}\n \n {{/amount}}\n \n {{/lines}}\n \n {{/RO.rogg.ops}}\n \n {{/RO.rogg}}\n\n {{#RO.romisc}}\n \n {{#RO.romisc.ops}}\n \n {{#lines}}\n \n {{/lines}}\n \n {{/RO.romisc.ops}}\n \n {{/RO.romisc}}\n \n\n',{ApplicationArea:a,RO:n}),routing:r.routing,envelope:o,postParse:O,xsdFilename:"rey_RomeUpdateBSMRepairOrderReq.xsd",elementName:"rey_RomeUpdateBSMRepairOrderReq"}}(e,r))}async getParts(e,r){return this._send(function(e={},r={}){const n=e.roNumber?String(e.roNumber).trim():void 0;if(!n)throw new k("getParts: roNumber required");const o=t({routing:r.routing,sender:{component:"Rome",task:"RCT",referenceId:"Query",creator:"RCI",senderName:"RCI"}});return{starXml:j.default.render('\n\n {{{ApplicationArea}}}\n \n\n',{ApplicationArea:o,RoNumber:n}),routing:r.routing,envelope:r.envelope,xsdFilename:"rey_RomeGetPartsReq.xsd",elementName:"rey_RomeGetPartsReq",postParse:e=>function(e){return e?.rey_RomeGetPartsResp?.RoParts?.map(e=>({partNumber:h(e,"PartNumber"),partDescription:h(e,"PartDescription"),quantityOrdered:h(e,"QuantityOrdered"),quantityShipped:h(e,"QuantityShipped"),price:h(e,"Price"),cost:h(e,"Cost"),processedFlag:h(e,"ProcessedFlag"),addOrDelete:h(e,"AddOrDelete")}))||[]}(e)}}(e,r))}},exports.errors=G;
+"use strict";function e(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}function t({routing:e={},sender:t={},creationDateTime:r,bodId:n}={}){const o={Sender:{Component:t.component??"Rome",Task:t.task??"CU",ReferenceId:t.referenceId??"Query",CreatorNameCode:t.creator??"RCI",SenderNameCode:t.senderName??"RCI"},CreationDateTime:r??(new Date).toISOString().replace(/\.\d{3}Z$/,"Z"),BODId:n??E.v4(),Destination:{DestinationNameCode:"RR",DealerNumber:e.dealerNumber||"",StoreNumber:e.storeNumber||"",AreaNumber:e.areaNumber||""}},a={bod:"{{#BODId}}{{BODId}}{{/BODId}}",creation:"{{CreationDateTime}}",sender:"\n \n {{Sender.Component}}\n {{Sender.Task}}\n {{#Sender.ReferenceId}}{{Sender.ReferenceId}}{{/Sender.ReferenceId}}\n {{#Sender.CreatorNameCode}}{{Sender.CreatorNameCode}}{{/Sender.CreatorNameCode}}\n {{#Sender.SenderNameCode}}{{Sender.SenderNameCode}}{{/Sender.SenderNameCode}}\n ".trim(),dest:"\n \n {{Destination.DestinationNameCode}}\n {{#Destination.DealerNumber}}{{Destination.DealerNumber}}{{/Destination.DealerNumber}}\n {{#Destination.StoreNumber}}{{Destination.StoreNumber}}{{/Destination.StoreNumber}}\n {{#Destination.AreaNumber}}{{Destination.AreaNumber}}{{/Destination.AreaNumber}}\n ".trim()},s=`\n${["bod","creation","sender","dest"].map(e=>a[e]).join("\n")}\n`;return j.default.render(s,o).trim()}function r(e,t=2){const r=" ".repeat(t);return String(e).split("\n").map(e=>e.length?r+e:e).join("\n")}function n(e){return String(e??"").replace(/&/g,"&").replace(//g,">")}function o(e,t){if(null==e)return null;if(Array.isArray(e)){for(const r of e){const e=o(r,t);if(null!=e)return e}return null}if("object"!=typeof e)return null;for(const[r,n]of Object.entries(e)){if(t(r,n))return n;const e=o(n,t);if(null!=e)return e}return null}function a(e,t,r=[]){if(null==e)return r;if(Array.isArray(e)){for(const n of e)a(n,t,r);return r}if("object"!=typeof e)return r;for(const[n,o]of Object.entries(e))t(n,o)&&r.push(o),a(o,t,r);return r}function s(e){return null==e?[]:Array.isArray(e)?e:[e]}function i(e,t){if(e&&"object"==typeof e)return function(e){if(null!=e)return"string"==typeof e||"number"==typeof e||"boolean"==typeof e?String(e):"object"==typeof e&&"#text"in e?String(e["#text"]):void 0}(e[t])}function l(e,t){const r=i(e,t);if(Q(r))return r;const n=function(e,t){if(e&&"object"==typeof e)return e.$&&Q(e.$[t])?e.$[t]:Q(e[`@_${t}`])?e[`@_${t}`]:Q(e[`@${t}`])?e[`@${t}`]:e._attributes&&Q(e._attributes[t])?e._attributes[t]:e.attributes&&Q(e.attributes[t])?e.attributes[t]:void 0}(e,t);return Q(n)?n:void 0}function c(e,t){if(e&&"object"==typeof e)return null!=e[`@_${t}`]?e[`@_${t}`]:void 0}function m(e){if(null!=e){if("string"==typeof e)return e;if("number"==typeof e)return String(e);if("object"==typeof e){if(null!=e._)return String(e._);if(null!=e["#text"])return String(e["#text"]);if(null!=e.text)return String(e.text)}}}function u(e){return a(e,e=>/(GenTransStatus|TransStatus)$/i.test(e)).flatMap(s)[0]}function p(e){if(e)return{status:(c(e,"Status")||e.Status||m(e)||"").toString().trim()||void 0,statusCode:(c(e,"StatusCode")||e.StatusCode||"").toString().trim()||void 0,message:e.Message&&m(e.Message)||e.GenTransStatus&&m(e.GenTransStatus)||e.TransStatus&&m(e.TransStatus)||m(e)||void 0}}function d(e){if(e)return{status:(c(e,"Status")||e.Status||m(e)||"").toString().trim()||void 0,date:(c(e,"Date")||e.Date||"").toString().trim()||void 0,time:(c(e,"Time")||e.Time||"").toString().trim()||void 0,outsdRoNo:(c(e,"OutsdRoNo")||e.OutsdRoNo||"").toString().trim()||void 0,dmsRoNo:(c(e,"DMSRoNo")||e.DMSRoNo||"").toString().trim()||void 0,errorMessage:(c(e,"ErrorMessage")||e.ErrorMessage||"").toString().trim()||void 0}}function y(e){return s((e?.rey_RomeCustServVehComb??e??{}).CustServVehComb).map(e=>{const t=e?.NameContactId??void 0,r=t?.NameId??void 0,n=r?.IndName?H(r.IndName):void 0,o=r?.BusName?H(r.BusName):void 0,a=t&&{NameId:r&&{...H(r)||{},...n?{IndName:n}:{},...o?{BusName:o}:{}},Address:s(t?.Address).map(e=>H(e)||{}),ContactOptions:s(t?.ContactOptions).map(e=>H(e)||{}),Phone:s(t?.Phone).map(e=>H(e)||{}),Email:s(t?.Email).map(e=>H(e)||{})},i=s(e?.ServVehicle).map(e=>{const t=e?.Vehicle,r=t?.VehicleDetail,n=t&&{...H(t)||{},...r?{VehicleDetail:H(r)}:{}},o=e?.VehicleServInfo,a=o?.VehExtWarranty,i=o?.Advisor,l=i?.ContactInfo,c=o&&{...H(o)||{},...a?{VehExtWarranty:H(a)}:{},...i?{Advisor:l?{ContactInfo:H(l)||{}}:{Advisor:{}}}:{},...e?.VehicleServInfo?.VehServComments?{VehServComments:s(e.VehicleServInfo.VehServComments).map(e=>Y(e))}:{}};return{...n?{Vehicle:n}:{},...c?{VehicleServInfo:c}:{}}}),l=s(e?.Message).map(e=>({...H(e)||{},Text:Y(e)})),c={};return a&&(c.NameContactId=a),i.length&&(c.ServVehicle=i),l.length&&(c.Message=l),c})}function N(e,t){if(null!=e)return String("string"==typeof e||"number"==typeof e?e:e[t]||"")}function g(e,...t){if(e)for(const r of t){if(e.$&&null!=e.$[r])return e.$[r];if(null!=e[`@_${r}`])return e[`@_${r}`];if(null!=e[r]&&"object"!=typeof e[r])return e[r]}}function T(e){const t=function(e){const t=o(e,e=>"rey_RomeCustomerResponse"===e||e.endsWith(":rey_RomeCustomerResponse"));return t?a(t,e=>"TransStatus"===e||e.endsWith(":TransStatus")).flatMap(s)[0]:void console.log("No rey_RomeCustomerResponse found in root")}(e);if(!t)return{dmsRecKey:void 0};const r=g(t,"DMSRecKey");return{dmsRecKey:null!=r?String(r):void 0,status:g(t,"Status"),statusCode:g(t,"StatusCode")}}function b(e){if(null==e)return;const t=String(e).toUpperCase().replace(/[^A-Z0-9 ]+/g,"");if(!t)throw new k(`Invalid string: ${e}. Must contain A-Z, 0-9, or space`);return t}function x(e){return e?String(e).toUpperCase():void 0}function C(e={}){const t=x(e.ibFlag)||(e.firstName?"I":"B");if("I"!==t&&"B"!==t)throw new k("ibFlag must be 'I' or 'B'");const r=e.lastName||e.customerName;if(!r)throw new k("lastName or customerName required");if("I"===t&&!e.firstName)throw new k("firstName required when ibFlag='I'");const n=x(e.customerType);if(n&&!["R","W","I"].includes(n))throw new k("customerType must be 'R', 'W', 'I', Retail, Wholesale, or Internal");const o=(e.addresses||[]).map(e=>{const t={Type:x(e.type)||"P",Addr1:e.line1?String(e.line1):void 0,Addr2:e.line2?String(e.line2):void 0,City:e.city?String(e.city):void 0,State:e.state?String(e.state):void 0,Zip:e.postalCode?String(e.postalCode):void 0,County:e.county?String(e.county):void 0,Country:e.country?String(e.country):void 0};if(!t.Addr1)throw new k("Address requires line1");return t}),a=(e.phones||[]).map(e=>{const t={Type:x(e.type)||"H",Num:e.number?String(e.number):void 0,Ext:e.extension?String(e.extension):void 0};if(!t.Num)throw new k("Phone requires number");return t}),s=e.emails?.[0]?.address?{MailTo:String(e.emails[0].address)}:void 0,i=e.personal,l=i?{Gender:x(i.gender),OtherName:b(i.otherName),AnniversaryDate:i.anniversaryDate?String(i.anniversaryDate):void 0,EmployerName:b(i.employerName),EmployerPhone:i.employerPhone?String(i.employerPhone):void 0,Occupation:b(i.occupation),OptOut:i.optOut?String(i.optOut):void 0,OptOutUse:i.optOutUse?String(i.optOutUse):void 0,BirthDates:(i.birthDates||[]).map(e=>({Type:x(e.type)||"P",date:e.date?String(e.date):void 0})).filter(e=>e.date),SSNs:(i.ssns||[]).map(e=>({Type:x(e.type)||"P",ssn:e.ssn?String(e.ssn):void 0})).filter(e=>e.ssn),DriverInfo:i.driver?[{Type:x(i.driver.type)||"P",LicNum:i.driver.licenseNumber?String(i.driver.licenseNumber):void 0,LicState:i.driver.licenseState?String(i.driver.licenseState):void 0,LicExpDate:i.driver.licenseExpDate?String(i.driver.licenseExpDate):void 0}].filter(e=>e.LicNum):void 0,CustChildren:(i.children||[]).map(e=>({ChildName:b(e.name)})).filter(e=>e.ChildName)}:void 0,c=e.dms,m=c?{TaxExemptNum:c.taxExemptNum?String(c.taxExemptNum):void 0,SalesTerritory:c.salesTerritory?String(c.salesTerritory):void 0,DeliveryRoute:c.deliveryRoute?String(c.deliveryRoute):void 0,SalesmanNum:c.salesmanNum?String(c.salesmanNum):void 0,LastContactMethod:c.lastContactMethod?String(c.lastContactMethod):void 0,Followups:(c.followups||[]).map(e=>({Type:x(e.type),Value:x(e.value)})).filter(e=>e.Type&&e.Value)}:void 0;return{custCateg:n||"R",createdBy:e.createdBy?String(e.createdBy):void 0,contactInfo:{IBFlag:t,LastName:b(r),FirstName:b(e.firstName),MidName:b(e.midName),Salut:b(e.salut),Suffix:b(e.suffix),Addresses:o,Phones:a,Email:s},custPersonal:l,dmsCustInfo:m}}function f(e){const t=o(e,e=>"rey_RomeServVehicleInsertResponse"===e||e.endsWith(":rey_RomeServVehicleInsertResponse"))||e,r=o(t,e=>"GenTransStatus"===e||e.endsWith(":GenTransStatus"));return{status:r?l(r,"Status"):void 0,statusCode:r?l(r,"StatusCode"):void 0}}function R(e,t){const r=Array.isArray(t)?t.map(e=>e.toLowerCase()):[String(t).toLowerCase()],n=[e];for(;n.length;){const e=n.pop();if(Z(e))for(const t of Object.keys(e)){const o=e[t],a=X(t).toLowerCase();if(r.includes(a))return o;if(Z(o))n.push(o);else if(Array.isArray(o))for(const e of o)Z(e)&&n.push(e)}}}function S(e,t){if(e&&Z(e)){if(null!=e.$?.[t])return e.$[t];if(null!=e[`@${t}`])return e[`@${t}`];if(null!=e[`@_${t}`])return e[`@_${t}`]}}function v(e){if(null!=e){if("string"==typeof e)return e;if("number"==typeof e)return String(e);if(Z(e)){if(null!=e._)return String(e._);if(null!=e["#text"])return String(e["#text"]);if(null!=e.text)return String(e.text)}}}function O(e){const t=R(e,["CreateBSMRepairOrderResp","UpdateBSMRepairOrderResp"])||e,r=R(t,"RoRecordStatus")||{};return{status:S(r,"Status")||v(R(r,"Status")),date:S(r,"Date")||v(R(r,"Date")),time:S(r,"Time")||v(R(r,"Time")),outsdRoNo:S(r,"OutsdRoNo")||v(R(r,"OutsdRoNo")),dmsRoNo:S(r,"DMSRoNo")||v(R(r,"DMSRoNo")),errorMessage:S(r,"ErrorMessage")||v(R(r,"ErrorMessage"))}}function F(e={},r={}){const n=function(e){if(!e)throw new Error("department is required (S, P, B, SERVICE, PARTS, BODY)");const t=String(e).trim().toUpperCase();if("S"===t||"P"===t||"B"===t)return t;if("SERVICE"===t)return"S";if("PART"===t||"PARTS"===t)return"P";if("BODY"===t||"BODYSHOP"===t||"BODY SHOP"===t)return"B";throw new Error(`Invalid department: ${e}. Must be S, P, B, SERVICE, PARTS, BODY, BODYSHOP, or BODY SHOP`)}(e.department),o=e.advisorNumber?String(e.advisorNumber).trim():void 0,a=t({routing:r.routing,sender:r?.envelope?.sender,creationDateTime:r?.envelope?.creationDateTime,bodId:r?.envelope?.bodId});return{starXml:j.default.render('\n\n {{{ApplicationArea}}}\n \n \n \n\n',{ApplicationArea:a,DepartmentType:n,AdvisorNumber:o}),routing:r.routing,envelope:r.envelope,xsdFilename:"rey_RomeGetAdvisorsReq.xsd",elementName:"rey_RomeGetAdvisorsReq",postParse:e=>function(e,t={}){const r=t?.department;var n;return(e=>{const t=e?.Advisor;return t?Array.isArray(t)?t:[t]:[]})((n=e,n?.rey_RomeGetAdvisorsResp??n??{})).map(e=>({advisorId:J(e,"AdvisorNumber"),firstName:J(e,"FirstName"),lastName:J(e,"LastName"),department:r}))}(e,{department:n})}}function I(e){return null==e?void 0:String(e)}function h(e,t){if(e)return null!=e[t]?"object"!=typeof e[t]?I(e[t]):I(e[t]["#text"]):null!=e[`@_${t}`]?I(e[`@_${t}`]):void 0}function w(e){const t=e?.meta?.statusBlocks?.transaction?.message;if(t)return String(t);return String((e?.meta?.status?.Message??e?.meta?.status?.message)||e?.message||"")}function P(e,t){return Math.min(1e4,e*Math.pow(2,t))}function D(e){return e+Math.floor(250*Math.random())}function A(e){return new Promise(t=>setTimeout(t,e))}function M(e){return/lock|in use|record.*busy/i.test(String(e||""))}Object.defineProperty(exports,"__esModule",{value:!0});const V=require("mustache"),E=require("uuid"),L=require("axios"),_=require("fast-xml-parser"),j=e(V),B=e(L);class q extends Error{constructor(e,t={}){super(e),this.name="RRTransportError",this.meta=t}}class U extends Error{constructor(e,t={}){super(e),this.name="RRVendorStatusError",this.meta=t,this.retryable=!!t.retryable}}class k extends Error{constructor(e,t={}){super(e),this.name="RRValidationError",this.meta=t}}const G=Object.freeze({__proto__:null,RRTransportError:q,RRVendorStatusError:U,RRValidationError:k}),$={info:(...e)=>console.log("[rr-rome]",...e),warn:(...e)=>console.warn("[rr-rome]",...e),error:(...e)=>console.error("[rr-rome]",...e),debug:(...e)=>{process.env.RR_DEBUG&&console.log("[rr-rome][debug]",...e)}},Q=e=>null!=e&&""!==String(e).trim(),W=new _.XMLParser({ignoreAttributes:!1,attributeNamePrefix:"@_",parseAttributeValue:!1,parseTagValue:!1,isArray:e=>["Advisor"].includes(e)}),H=e=>{if(!e||"object"!=typeof e)return;const t={};for(const[r,n]of Object.entries(e))r.startsWith("@_")&&(t[r.slice(2)]=n);return Object.keys(t).length?t:void 0},Y=e=>{if(null!=e)return"string"==typeof e?e:e["#text"]},X=e=>{if("string"!=typeof e)return"";let t=e.includes("}")?e.split("}").pop():e;return t=t.includes(":")?t.split(":").pop():t,t.startsWith("rey_")&&(t=t.slice(4)),t},Z=e=>e&&"object"==typeof e&&!Array.isArray(e),J=(e,t)=>((e,t)=>e?.[`@_${t}`])(e,t)??i(e,t);exports.RRClient=class{constructor(e){if(!e?.baseUrl)throw new Error("RRClient requires baseUrl");if(!e?.username)throw new Error("RRClient requires username");if(!e?.password)throw new Error("RRClient requires password");this.cfg={wssePasswordType:"Text",timeoutMs:3e4,logger:$,retries:{max:3},...e},this.mask={password:!0}}async _send(e){const{starXml:t,routing:o,envelope:i,postParse:l}=e,y=this.cfg.logger||$,{bodId:N,creationDateTime:g,sender:T}=function(e){return{bodId:e?.bodId||E.v4(),creationDateTime:e?.creationDateTime||new Date,sender:e?.sender||{}}}(i),b=function({username:e,password:t,wssePasswordType:o="Text",starContentXml:a}){const s=function(e){return`\n \n \n \n${r(e,10)}\n \n \n \n `.trim()}(a);return`\n \n \n${r(function(e,t,r){const o="Digest"===r?' Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest"':' Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText"';return`\n \n \n ${n(e)}\n ${n(t)}\n \n \n `.trim()}(e,t,o),8)}\n \n \n${r(s,8)}\n \n \n `.trim()}({username:this.cfg.username,password:this.cfg.password,wssePasswordType:this.cfg.wssePasswordType||"Text",routing:o,sender:T,creationDateTime:(x=g,"string"==typeof x?x:(x instanceof Date?x:new Date).toISOString().replace(/\.\d{3}Z$/,"Z")),bodId:N,starContentXml:t});var x;"1"===process.env.RR_DUMP_ENVELOPE&&console.log(`[rr] Soap envelope about to send (${e?.elementName}): \n`+b+"\n");const C=async()=>{const t=await async function({baseUrl:e,envelopeXml:t,timeoutMs:r=3e4,logger:n}){try{const n=await B.default.post(e,t,{timeout:r,headers:{"Content-Type":"text/xml; charset=utf-8",SOAPAction:"http://www.starstandards.org/webservices/2005/10/transport/ProcessMessage"},responseType:"text",validateStatus:()=>!0});if(n.status>=200&&n.status<300)return String(n.data??"");throw new q(`HTTP ${n.status}: ${function(e){const t=String(e??"");return t.length>1024?t.slice(0,1024)+"…":t}(n.data)}`,{status:n.status,body:n.data})}catch(e){if(B.default.isAxiosError(e))throw new q(`Network error: ${e.message}`,{cause:e});throw e}}({baseUrl:this.cfg.baseUrl,envelopeXml:b,timeoutMs:this.cfg.timeoutMs,logger:y}),r=function(e){const t=W.parse(e),r=t?.Envelope||t?.["soapenv:Envelope"]||t,n=r?.Body||r?.["soapenv:Body"]||r?.["S:Body"]||r?.["soap:Body"],o=n?.ProcessMessageResponse||n?.["ns2:ProcessMessageResponse"]||n?.["trans:ProcessMessageResponse"]||n,a=o?.payload||o?.["ns2:payload"]||o;return a?.content||a?.["ns2:content"]||a}(t),n=function(e){const t=u(e),r=a(e,e=>/RoRecordStatus$/i.test(e)).flatMap(s)[0];return{transaction:p(t),roRecord:d(r)}}(r),o=function(e){const t=String("ApplicationArea").toLowerCase(),r=[e];for(;r.length;){const e=r.pop();if(e&&"object"==typeof e)for(const[n,o]of Object.entries(e)){if(n.toLowerCase().endsWith(t))return o;o&&"object"==typeof o&&r.push(o)}}}(r),{status:i,norm:N}=function(e){const t=u(e)||a(e,e=>/Status$/i.test(e)).flatMap(s)[0]||{},r=(c(t,"Status")||t.Status||"").toString().trim();let n=(c(t,"StatusCode")||t.StatusCode||"").toString().trim();const o=t.Message&&m(t.Message)||m(t)||"";!n&&/success/i.test(r)&&(n="0");const i=Number(n);let l="FAIL";return/success/i.test(r)||0===i?l="SUCCESS":2!==i&&213!==i||(l="NO_MATCH"),{status:{Status:r,StatusCode:n,Message:o},norm:{kind:l,code:Number.isFinite(i)?i:void 0,message:o}}}(r);if("1"===process.env.RR_DUMP_STATUS&&(console.log("[rr] Status blocks:"),console.dir(n,{depth:null,colors:!0})),"1"===process.env.RR_DUMP_APPLICATION&&(console.log("[rr] ApplicationArea:"),console.dir(o,{depth:null,colors:!0})),"FAIL"===N.kind){const e=i?.StatusCode;throw new U(`Vendor status failure: ${e??""} ${i?.Message||i?.["#text"]||""}`.trim(),{status:i,resXml:t})}const g={success:"SUCCESS"===N.kind||"NO_MATCH"===N.kind,statusBlocks:n,applicationArea:o,xml:{request:b,response:t},parsed:r};if("function"==typeof l)try{g.data=l(r)}catch(e){y?.warn?.(`postParse failed: ${e?.message||e}`)}return"1"===process.env.RR_DUMP_XML&&console.log(`[rr] Full response XML (${e?.elementName}):\n`+t+"\n"),g?.data&&"1"===process.env.RR_DUMP_DATA&&(console.log(`[rr] Parsed response data (${e?.elementName}):\n`),console.dir(g.data,{depth:null,colors:!0})),g};try{return await async function(e,{max:t=3,logger:r}){let n,o=0;for(;o=t)break;const s=D(P(400,o));r?.warn?.(`Retrying attempt ${o}/${t} in ${s}ms: ${e.message}`),await A(s)}throw n}(C,{max:this.cfg.retries?.max??3,logger:y})}catch(e){if(e instanceof q||e instanceof U)throw e;throw new q(`Unexpected error: ${e?.message||e}`,{cause:e})}}async combinedSearch(e,r){return this._send(function(e={},r={}){if(!e||"object"!=typeof e)throw new k("combinedSearch requires a query object");const n=function(e){if(!e)return"";const t=String(e).trim().toLowerCase();return"telephone"===t?"phone":"lic"===t||"plate"===t?"license":"vin"===t||"partvin"===t?"vin":"name"===t?"name":"nameRecId"===t||"custId"===t?"nameRecId":"stkNo"===t||"stock"===t?"stkNo":t}(e.kind),o={MaxRecs:Math.min(Number(e.maxResults||e.maxRecs||50),50)};if("phone"===n){const t=N(e.phone,"phone");if(!t)throw new k("combinedSearch phone value missing");o.Phone=t}else if("license"===n){const t=N(e.license,"license");if(!t)throw new k("combinedSearch license value missing");o.LicenseNum=t}else if("vin"===n){const t=N(e.vin,"vin");if(!t)throw new k("combinedSearch vin value missing");o.PartVIN=t}else if("name"===n){const t=e.name;if(!t||"object"!=typeof t)throw new k("combinedSearch name requires { fname, lname, mname } or { name }");if(t.fname&&t.lname)o.FullName={FName:String(t.fname),LName:String(t.lname),MName:t.mname?String(t.mname):void 0};else{if(!t.name)throw new k("combinedSearch name requires { fname, lname[, mname] } or { name }");o.LName=String(t.name)}}else if("nameRecId"===n){const t=N(e.nameRecId,"custId");if(!t)throw new k("combinedSearch nameRecId value missing");o.NameRecId=t}else{if("stkNo"!==n)throw new k(`Unsupported CombinedSearch kind: ${e.kind}`);{const t=N(e.stkNo,"stkNo");if(!t)throw new k("combinedSearch stkNo value missing");o.StkNo=t}}if(1!==[o.Phone,o.LicenseNum,o.PartVIN,o.LName,o.FullName,o.NameRecId,o.StkNo].filter(Boolean).length)throw new k("combinedSearch requires exactly one search criterion");o.VehData={MakePfx:e.make||"ANY",Model:e.model||"ANY",Year:e.year||"ANY"};const a=t({routing:r.routing,sender:{component:"Rome",task:"CVC",referenceId:"Query",creator:"RCI",senderName:"RCI"},order:"creation-bod-sender-destination"});return{starXml:j.default.render('\n\n {{{ApplicationArea}}}\n \n \n {{#Phone}}{{/Phone}}\n {{#PartVIN}}{{/PartVIN}}\n {{#LicenseNum}}{{/LicenseNum}}\n {{#FName}}{{/FName}}\n {{#LName}}{{/LName}}\n {{#FullName}}{{/FullName}}\n {{#NameRecId}}{{/NameRecId}}\n {{#StkNo}}{{/StkNo}}\n \n \n \n\n',{ApplicationArea:a,...o}),routing:r.routing,envelope:r.envelope,elementName:"rey_RomeCustServVehCombReq",xsdFilename:"rey_RomeCustServVehCombReq.xsd",postParse:y}}(e,r))}async insertCustomer(e,r){return this._send(function(e,r){if(!r?.routing?.dealerNumber)throw new k("routing.dealerNumber required");const n=C(e),o=t({routing:r.routing,sender:{component:"Rome",task:"CU",referenceId:"Insert"},order:"sender-creation-bod-destination"});return{starXml:j.default.render('\n\n {{{ApplicationArea}}}\n \n \n {{#C.contactInfo.Addresses}}\n \n {{/C.contactInfo.Addresses}}\n {{#C.contactInfo.Phones}}\n \n {{/C.contactInfo.Phones}}\n {{#C.contactInfo.Email}}\n \n {{/C.contactInfo.Email}}\n \n {{#C.custPersonal}}\n \n {{#BirthDates}}\n \n {{/BirthDates}}\n {{#SSNs}}\n \n {{/SSNs}}\n {{#DriverInfo}}\n \n {{/DriverInfo}}\n {{#CustChildren}}\n \n {{/CustChildren}}\n \n {{/C.custPersonal}}\n {{#C.dmsCustInfo}}\n \n {{#Followups}}\n \n {{/Followups}}\n \n {{/C.dmsCustInfo}}\n \n\n',{ApplicationArea:o,C:n}),routing:r.routing,envelope:r.envelope,postParse:T,xsdFilename:"rey_RomeCustomerInsertReq.xsd",elementName:"rey_RomeCustomerInsertReq"}}(e,r))}async updateCustomer(e,r){return this._send(function(e,r){if(!r?.routing?.dealerNumber)throw new k("routing.dealerNumber required");if(!e.nameRecId)throw new k("nameRecId or customerId required");const n=x(e.ibFlag);if("I"!==n&&"B"!==n)throw new k("ibFlag required ('I' or 'B')");const o=C(e);o.contactInfo.NameRecId=String(e.nameRecId);const a=t({routing:r.routing,sender:{component:"Rome",task:"CU",referenceId:"Update"},order:"sender-creation-bod-destination"});return{starXml:j.default.render('\n\n {{{ApplicationArea}}}\n \n \n {{#C.contactInfo.Addresses}}\n \n {{/C.contactInfo.Addresses}}\n {{#C.contactInfo.Phones}}\n \n {{/C.contactInfo.Phones}}\n {{#C.contactInfo.Email}}\n \n {{/C.contactInfo.Email}}\n \n {{#C.custPersonal}}\n \n {{#BirthDates}}\n \n {{/BirthDates}}\n {{#SSNs}}\n \n {{/SSNs}}\n {{#DriverInfo}}\n \n {{/DriverInfo}}\n {{#CustChildren}}\n \n {{/CustChildren}}\n \n {{/C.custPersonal}}\n {{#C.dmsCustInfo}}\n \n {{#Followups}}\n \n {{/Followups}}\n \n {{/C.dmsCustInfo}}\n \n\n',{ApplicationArea:a,C:o}),routing:r.routing,envelope:r.envelope,postParse:T,xsdFilename:"rey_RomeCustomerUpdateReq.xsd",elementName:"rey_RomeCustomerUpdateReq"}}(e,r))}async insertServiceVehicle(e,r){return this._send(function(e={},r={}){if(!r?.routing?.dealerNumber)throw new k("routing.dealerNumber is required");const n=t({routing:r.routing,sender:r?.envelope?.sender??{component:"Rome",task:"SV",referenceId:"Insert"},creationDateTime:r?.envelope?.creationDateTime,bodId:r?.envelope?.bodId}),o=function(e={}){const t=e.vin;if(!t)throw new k("insertServiceVehicle: vin is required");const r=e.vehicleServInfo?.customerNo;if(!r)throw new k("insertServiceVehicle: vehicleServInfo.customerNo (or customerNo) is required");return{VIN:String(t),ModelDesc:e.modelDesc,Carline:e.carline,ExtClrDesc:e.extClrDesc,IntClrDesc:e.intClrDesc,TrimDesc:e.trimDesc,BodyStyle:e.bodyStyle,EngineDesc:e.engineDesc,TransDesc:e.transDesc,Year:e.year,Odometer:e.odometer,OdometerUnits:e.odometerUnits,LicNo:e.vehicleDetail?.licNo,CustomerNo:String(r),vehicleServInfo:{salesmanNo:e.vehicleServInfo?.salesmanNo,inServiceDate:e.vehicleServInfo?.inServiceDate,mileage:e.vehicleServInfo?.mileage,teamCode:e.vehicleServInfo?.teamCode,vehExtWarranty:(()=>{const t=e.vehicleServInfo?.vehExtWarranty;if(!t)return;const r={contractNumber:t.contractNumber,expirationDate:t.expirationDate,expirationMileage:t.expirationMileage};return Object.values(r).some(e=>null!=e&&""!==e)?r:void 0})(),advisor:(()=>{const t=e.vehicleServInfo?.advisor,r=t?.contactInfo?.nameRecId;return r?{contactInfo:{nameRecId:String(r)}}:void 0})()}}}(e);return{starXml:j.default.render('\n\n {{{ApplicationArea}}}\n\n \n {{#V.Year}}{{.}}{{/V.Year}}\n {{#V.Odometer}}{{.}}{{/V.Odometer}}\n {{#V.OdometerUnits}}{{.}}{{/V.OdometerUnits}}\n\n {{#V.LicNo}}{{/V.LicNo}}\n \n\n \n {{#V.vehicleServInfo.salesmanNo}}{{.}}{{/V.vehicleServInfo.salesmanNo}}\n {{#V.vehicleServInfo.inServiceDate}}{{.}}{{/V.vehicleServInfo.inServiceDate}}\n {{#V.vehicleServInfo.mileage}}{{.}}{{/V.vehicleServInfo.mileage}}\n {{#V.vehicleServInfo.teamCode}}{{.}}{{/V.vehicleServInfo.teamCode}}\n\n {{#V.vehicleServInfo.vehExtWarranty}}\n \n {{#contractNumber}}{{.}}{{/contractNumber}}\n {{#expirationDate}}{{.}}{{/expirationDate}}\n {{#expirationMileage}}{{.}}{{/expirationMileage}}\n \n {{/V.vehicleServInfo.vehExtWarranty}}\n\n {{#V.vehicleServInfo.advisor}}\n \n \n \n {{/V.vehicleServInfo.advisor}}\n \n\n',{ApplicationArea:n,V:o}),routing:r.routing,envelope:r.envelope,postParse:f,xsdFilename:"rey_RomeServVehicleInsertReq.xsd",elementName:"rey_RomeServVehicleInsertReq"}}(e,r))}async getAdvisors(e,t){return this._send(F(e,t))}async createRepairOrder(e,r){return this._send(function(e={},r={}){if(!r?.routing?.dealerNumber)throw new k("routing.dealerNumber required");if(!e?.customerNo)throw new k("customerNo (CustNo) required");if(!e?.departmentType)throw new k("departmentType (DeptType) required");if(!e?.vin)throw new k("vin (Vin) required");if(!e?.outsdRoNo)throw new k("outsdRoNo required");const n={customerNo:e.customerNo,advisorNo:e.advisorNo,tagNo:e.tagNo,outsdRoNo:e.outsdRoNo,departmentType:e.departmentType,vin:e.vin,mileageIn:e.mileageIn,roComment:e.roComment,estimate:e.estimate?{parts:e.estimate.parts,labor:e.estimate.labor,total:e.estimate.total}:void 0,tax:e.tax?{payType:e.tax.payType,taxCode:e.tax.taxCode,txblGrossAmt:e.tax.txblGrossAmt,grossTaxAmt:e.tax.grossTaxAmt}:void 0,rolabor:e.rolabor?{ops:e.rolabor.ops?.map(e=>({opCode:e.opCode,jobNo:e.jobNo,custPayTypeFlag:e.custPayTypeFlag,custTxblNtxblFlag:e.custTxblNtxblFlag,warrPayTypeFlag:e.warrPayTypeFlag,warrTxblNtxblFlag:e.warrTxblNtxblFlag,intrPayTypeFlag:e.intrPayTypeFlag,intrTxblNtxblFlag:e.intrTxblNtxblFlag,vlrCode:e.vlrCode,bill:e.bill?{payType:e.bill.payType,jobTotalHrs:e.bill.jobTotalHrs,billTime:e.bill.billTime,billRate:e.bill.billRate}:void 0,ccc:e.ccc?{cause:e.ccc.cause,complaint:e.ccc.complaint,correction:e.ccc.correction}:void 0,amount:e.amount?{payType:e.amount.payType,amtType:e.amount.amtType,custPrice:e.amount.custPrice,totalAmt:e.amount.totalAmt}:void 0}))}:void 0,ropart:e.ropart?{jobs:e.ropart.jobs?.map(e=>({opCode:e.opCode,jobNo:e.jobNo,lines:e.lines?.map(e=>({partNo:e.partNo,partNoDesc:e.partNoDesc,qtyOrd:e.partQty,sale:e.sale,cost:e.cost,addDeleteFlag:e.addDeleteFlag}))}))}:void 0,rogg:e.rogg?{roNo:e.rogg.roNo,ops:e.rogg.ops?.map(e=>({opCode:e.opCode,jobNo:e.jobNo,lines:e.lines?.map(e=>({breakOut:e.breakOut,itemType:e.itemType,itemDesc:e.itemDesc,custQty:e.custQty,warrQty:e.warrQty,intrQty:e.intrQty,custPayTypeFlag:e.custPayTypeFlag,warrPayTypeFlag:e.warrPayTypeFlag,intrPayTypeFlag:e.intrPayTypeFlag,custTxblNtxblFlag:e.custTxblNtxblFlag,warrTxblNtxblFlag:e.warrTxblNtxblFlag,intrTxblNtxblFlag:e.intrTxblNtxblFlag,amount:e.amount?{payType:e.amount.payType,amtType:e.amount.amtType,custPrice:e.amount.custPrice,dlrCost:e.amount.dlrCost}:void 0}))}))}:void 0,romisc:e.romisc?{roNo:e.romisc.roNo,ops:e.romisc.ops?.map(e=>({opCode:e.opCode,jobNo:e.jobNo,lines:e.lines?.map(e=>({miscCode:e.miscCode,custPayTypeFlag:e.custPayTypeFlag,warrPayTypeFlag:e.warrPayTypeFlag,intrPayTypeFlag:e.intrPayTypeFlag,custTxblNtxblFlag:e.custTxblNtxblFlag,warrTxblNtxblFlag:e.warrTxblNtxblFlag,intrTxblNtxblFlag:e.intrTxblNtxblFlag,codeAmt:e.codeAmt}))}))}:void 0};if(n.tax?.payType&&!["All","Cust","Intr","Warr"].includes(n.tax.payType))throw new k("tax.payType must be one of: All, Cust, Intr, Warr");if(n.rolabor?.ops?.some(e=>e.custTxblNtxblFlag&&!["T","N"].includes(e.custTxblNtxblFlag)||e.warrTxblNtxblFlag&&!["T","N"].includes(e.warrTxblNtxblFlag)||e.intrTxblNtxblFlag&&!["T","N"].includes(e.intrTxblNtxblFlag)))throw new k("Taxable flags (CustTxblNTxblFlag, WarrTxblNTxblFlag, IntrTxblNTxblFlag) must be 'T' or 'N'");if(n.rogg?.ops?.some(e=>e.lines?.some(e=>e.itemType&&!["G","P","S","F"].includes(e.itemType))))throw new k("rogg.ops.lines.itemType must be one of: G, P, S, F");const o={...r?.envelope||{},sender:{component:r?.envelope?.sender?.component??"Rome",task:r?.envelope?.sender?.task??"BSMRO",referenceId:r?.envelope?.sender?.referenceId??"Insert",creatorNameCode:r?.envelope?.sender?.creatorNameCode??"RCI",senderNameCode:r?.envelope?.sender?.senderNameCode??"RCI"}},a=t({routing:r.routing,sender:o.sender,creationDateTime:o.creationDateTime,bodId:o.bodId});return{starXml:j.default.render('\n\n {{{ApplicationArea}}}\n \n \n {{#RO.roComment}}\n \n {{/RO.roComment}}\n\n {{#RO.estimate}}\n \n {{/RO.estimate}}\n\n {{#RO.tax}}\n \n {{/RO.tax}}\n \n\n {{#RO.rolabor}}\n \n {{#RO.rolabor.ops}}\n \n {{#bill}}\n \n {{/bill}}\n\n {{#ccc}}\n \n {{/ccc}}\n\n {{#amount}}\n \n {{/amount}}\n \n {{/RO.rolabor.ops}}\n \n {{/RO.rolabor}}\n\n {{#RO.ropart}}\n \n {{#RO.ropart.jobs}}\n \n {{#lines}}\n \n {{/lines}}\n \n {{/RO.ropart.jobs}}\n \n {{/RO.ropart}}\n\n {{#RO.rogg}}\n \n {{#RO.rogg.ops}}\n \n {{#lines}}\n \n {{#amount}}\n \n {{/amount}}\n \n {{/lines}}\n \n {{/RO.rogg.ops}}\n \n {{/RO.rogg}}\n\n {{#RO.romisc}}\n \n {{#RO.romisc.ops}}\n \n {{#lines}}\n \n {{/lines}}\n \n {{/RO.romisc.ops}}\n \n {{/RO.romisc}}\n \n\n',{ApplicationArea:a,RO:n}),routing:r.routing,envelope:o,postParse:O,xsdFilename:"rey_RomeCreateBSMRepairOrderReq.xsd"}}(e,r))}async updateRepairOrder(e,r){return this._send(function(e={},r={}){if(!r?.routing?.dealerNumber)throw new k("routing.dealerNumber required");if(!e?.finalUpdate)throw new k("finalUpdate (FinalUpdate) required");if(!["Y","N"].includes(e.finalUpdate))throw new k("finalUpdate must be 'Y' or 'N'");if(!e?.outsdRoNo)throw new k(" outsdRoNo");const n={finalUpdate:e.finalUpdate||"N",roNo:e.roNo,customerNo:e.customerNo,tagNo:e.tagNo,outsdRoNo:e.outsdRoNo,departmentType:e.departmentType,vin:e.vin,mileageIn:e.mileageIn,mileageOut:e.mileageOut,roComment:e.roComment,estimate:e.estimate?{estimateType:e.estimate.estimateType}:void 0,tax:e.tax?{payType:e.tax.payType,taxCode:e.tax.taxCode,txblGrossAmt:e.tax.txblGrossAmt,grossTaxAmt:e.tax.grossTaxAmt}:void 0,rolabor:e.rolabor?{ops:e.rolabor.ops?.map(e=>({opCode:e.opCode,jobNo:e.jobNo,custPayTypeFlag:e.custPayTypeFlag,custTxblNtxblFlag:e.custTxblNtxblFlag,warrPayTypeFlag:e.warrPayTypeFlag,warrTxblNtxblFlag:e.warrTxblNtxblFlag,intrPayTypeFlag:e.intrPayTypeFlag,intrTxblNtxblFlag:e.intrTxblNtxblFlag,vlrCode:e.vlrCode,bill:e.bill?{payType:e.bill.payType,jobTotalHrs:e.bill.jobTotalHrs,billTime:e.bill.billTime,billRate:e.bill.billRate}:void 0,ccc:e.ccc?{cause:e.ccc.cause,complaint:e.ccc.complaint,correction:e.ccc.correction}:void 0,amount:e.amount?{payType:e.amount.payType,amtType:e.amount.amtType,custPrice:e.amount.custPrice}:void 0}))}:void 0,ropart:e.ropart?{jobs:e.ropart.jobs?.map(e=>({opCode:e.opCode,jobNo:e.jobNo,lines:e.lines?.map(e=>({partNo:e.partNo,partNoDesc:e.partNoDesc,qtyOrd:e.partQty,sale:e.sale,cost:e.cost,addDeleteFlag:e.addDeleteFlag}))}))}:void 0,rogg:e.rogg?{roNo:e.rogg.roNo,ops:e.rogg.ops?.map(e=>({opCode:e.opCode,jobNo:e.jobNo,lines:e.lines?.map(e=>({breakOut:e.breakOut,itemType:e.itemType,itemDesc:e.itemDesc,custQty:e.custQty,warrQty:e.warrQty,intrQty:e.intrQty,custPayTypeFlag:e.custPayTypeFlag,warrPayTypeFlag:e.warrPayTypeFlag,intrPayTypeFlag:e.intrPayTypeFlag,custTxblNtxblFlag:e.custTxblNtxblFlag,warrTxblNtxblFlag:e.warrTxblNtxblFlag,intrTxblNtxblFlag:e.intrTxblNtxblFlag,amount:e.amount?{payType:e.amount.payType,amtType:e.amount.amtType,custPrice:e.amount.custPrice,dlrCost:e.amount.dlrCost}:void 0}))}))}:void 0,romisc:e.romisc?{roNo:e.romisc.roNo,ops:e.romisc.ops?.map(e=>({opCode:e.opCode,jobNo:e.jobNo,lines:e.lines?.map(e=>({miscCode:e.miscCode,custPayTypeFlag:e.custPayTypeFlag,warrPayTypeFlag:e.warrPayTypeFlag,intrPayTypeFlag:e.intrPayTypeFlag,custTxblNtxblFlag:e.custTxblNtxblFlag,warrTxblNtxblFlag:e.warrTxblNtxblFlag,intrTxblNtxblFlag:e.intrTxblNtxblFlag,codeAmt:e.codeAmt}))}))}:void 0};if(n.tax?.payType&&!["All","Cust","Intr","Warr"].includes(n.tax.payType))throw new k("tax.payType must be one of: All, Cust, Intr, Warr");if(n.rolabor?.ops?.some(e=>e.custTxblNtxblFlag&&!["T","N"].includes(e.custTxblNtxblFlag)||e.warrTxblNtxblFlag&&!["T","N"].includes(e.warrTxblNtxblFlag)||e.intrTxblNtxblFlag&&!["T","N"].includes(e.intrTxblNtxblFlag)))throw new k("Taxable flags (CustTxblNTxblFlag, WarrTxblNTxblFlag, IntrTxblNTxblFlag) must be 'T' or 'N'");if(n.rogg?.ops?.some(e=>e.lines?.some(e=>e.itemType&&!["G","P","S","F"].includes(e.itemType))))throw new k("rogg.ops.lines.itemType must be one of: G, P, S, F");const o={...r?.envelope||{},sender:{component:r?.envelope?.sender?.component??"Rome",task:r?.envelope?.sender?.task??"BSMRO",referenceId:r?.envelope?.sender?.referenceId??"Update",creatorNameCode:r?.envelope?.sender?.creatorNameCode??"RCI",senderNameCode:r?.envelope?.sender?.senderNameCode??"RCI"}},a=t({routing:r.routing,sender:o.sender,creationDateTime:o.creationDateTime,bodId:o.bodId});return{starXml:j.default.render('\n\n {{{ApplicationArea}}}\n \n \n {{#RO.roComment}}\n \n {{/RO.roComment}}\n\n {{#RO.estimate}}\n \n {{/RO.estimate}}\n\n {{#RO.tax}}\n \n {{/RO.tax}}\n \n\n {{#RO.rolabor}}\n \n {{#RO.rolabor.ops}}\n \n {{#bill}}\n \n {{/bill}}\n\n {{#ccc}}\n \n {{/ccc}}\n\n {{#amount}}\n \n {{/amount}}\n \n {{/RO.rolabor.ops}}\n \n {{/RO.rolabor}}\n\n {{#RO.ropart}}\n \n {{#RO.ropart.jobs}}\n \n {{#lines}}\n \n {{/lines}}\n \n {{/RO.ropart.jobs}}\n \n {{/RO.ropart}}\n\n {{#RO.rogg}}\n \n {{#RO.rogg.ops}}\n \n {{#lines}}\n \n {{#amount}}\n \n {{/amount}}\n \n {{/lines}}\n \n {{/RO.rogg.ops}}\n \n {{/RO.rogg}}\n\n {{#RO.romisc}}\n \n {{#RO.romisc.ops}}\n \n {{#lines}}\n \n {{/lines}}\n \n {{/RO.romisc.ops}}\n \n {{/RO.romisc}}\n \n\n',{ApplicationArea:a,RO:n}),routing:r.routing,envelope:o,postParse:O,xsdFilename:"rey_RomeUpdateBSMRepairOrderReq.xsd",elementName:"rey_RomeUpdateBSMRepairOrderReq"}}(e,r))}async getParts(e,r){return this._send(function(e={},r={}){const n=e.roNumber?String(e.roNumber).trim():void 0;if(!n)throw new k("getParts: roNumber required");const o=t({routing:r.routing,sender:{component:"Rome",task:"RCT",referenceId:"Query",creator:"RCI",senderName:"RCI"}});return{starXml:j.default.render('\n\n {{{ApplicationArea}}}\n \n\n',{ApplicationArea:o,RoNumber:n}),routing:r.routing,envelope:r.envelope,xsdFilename:"rey_RomeGetPartsReq.xsd",elementName:"rey_RomeGetPartsReq",postParse:e=>function(e){return e?.rey_RomeGetPartsResp?.RoParts?.map(e=>({partNumber:h(e,"PartNumber"),partDescription:h(e,"PartDescription"),quantityOrdered:h(e,"QuantityOrdered"),quantityShipped:h(e,"QuantityShipped"),price:h(e,"Price"),cost:h(e,"Cost"),processedFlag:h(e,"ProcessedFlag"),addOrDelete:h(e,"AddOrDelete")}))||[]}(e)}}(e,r))}},exports.errors=G;
diff --git a/server/rr/lib/index.mjs b/server/rr/lib/index.mjs
deleted file mode 100644
index b76b4e31a..000000000
--- a/server/rr/lib/index.mjs
+++ /dev/null
@@ -1 +0,0 @@
-function e({routing:e={},sender:t={},creationDateTime:r,bodId:n}={}){const o={Sender:{Component:t.component??"Rome",Task:t.task??"CU",ReferenceId:t.referenceId??"Query",CreatorNameCode:t.creator??"RCI",SenderNameCode:t.senderName??"RCI"},CreationDateTime:r??(new Date).toISOString().replace(/\.\d{3}Z$/,"Z"),BODId:n??V(),Destination:{DestinationNameCode:"RR",DealerNumber:e.dealerNumber||"",StoreNumber:e.storeNumber||"",AreaNumber:e.areaNumber||""}},a={bod:"{{#BODId}}{{BODId}}{{/BODId}}",creation:"{{CreationDateTime}}",sender:"\n \n {{Sender.Component}}\n {{Sender.Task}}\n {{#Sender.ReferenceId}}{{Sender.ReferenceId}}{{/Sender.ReferenceId}}\n {{#Sender.CreatorNameCode}}{{Sender.CreatorNameCode}}{{/Sender.CreatorNameCode}}\n {{#Sender.SenderNameCode}}{{Sender.SenderNameCode}}{{/Sender.SenderNameCode}}\n ".trim(),dest:"\n \n {{Destination.DestinationNameCode}}\n {{#Destination.DealerNumber}}{{Destination.DealerNumber}}{{/Destination.DealerNumber}}\n {{#Destination.StoreNumber}}{{Destination.StoreNumber}}{{/Destination.StoreNumber}}\n {{#Destination.AreaNumber}}{{Destination.AreaNumber}}{{/Destination.AreaNumber}}\n ".trim()},s=`\n${["bod","creation","sender","dest"].map(e=>a[e]).join("\n")}\n`;return M.render(s,o).trim()}function t(e,t=2){const r=" ".repeat(t);return String(e).split("\n").map(e=>e.length?r+e:e).join("\n")}function r(e){return String(e??"").replace(/&/g,"&").replace(//g,">")}function n(e,t){if(null==e)return null;if(Array.isArray(e)){for(const r of e){const e=n(r,t);if(null!=e)return e}return null}if("object"!=typeof e)return null;for(const[r,o]of Object.entries(e)){if(t(r,o))return o;const e=n(o,t);if(null!=e)return e}return null}function o(e,t,r=[]){if(null==e)return r;if(Array.isArray(e)){for(const n of e)o(n,t,r);return r}if("object"!=typeof e)return r;for(const[n,a]of Object.entries(e))t(n,a)&&r.push(a),o(a,t,r);return r}function a(e){return null==e?[]:Array.isArray(e)?e:[e]}function s(e,t){if(e&&"object"==typeof e)return function(e){if(null!=e)return"string"==typeof e||"number"==typeof e||"boolean"==typeof e?String(e):"object"==typeof e&&"#text"in e?String(e["#text"]):void 0}(e[t])}function i(e,t){const r=s(e,t);if(k(r))return r;const n=function(e,t){if(e&&"object"==typeof e)return e.$&&k(e.$[t])?e.$[t]:k(e[`@_${t}`])?e[`@_${t}`]:k(e[`@${t}`])?e[`@${t}`]:e._attributes&&k(e._attributes[t])?e._attributes[t]:e.attributes&&k(e.attributes[t])?e.attributes[t]:void 0}(e,t);return k(n)?n:void 0}function l(e,t){if(e&&"object"==typeof e)return null!=e[`@_${t}`]?e[`@_${t}`]:void 0}function c(e){if(null!=e){if("string"==typeof e)return e;if("number"==typeof e)return String(e);if("object"==typeof e){if(null!=e._)return String(e._);if(null!=e["#text"])return String(e["#text"]);if(null!=e.text)return String(e.text)}}}function m(e){return o(e,e=>/(GenTransStatus|TransStatus)$/i.test(e)).flatMap(a)[0]}function p(e){if(e)return{status:(l(e,"Status")||e.Status||c(e)||"").toString().trim()||void 0,statusCode:(l(e,"StatusCode")||e.StatusCode||"").toString().trim()||void 0,message:e.Message&&c(e.Message)||e.GenTransStatus&&c(e.GenTransStatus)||e.TransStatus&&c(e.TransStatus)||c(e)||void 0}}function u(e){if(e)return{status:(l(e,"Status")||e.Status||c(e)||"").toString().trim()||void 0,date:(l(e,"Date")||e.Date||"").toString().trim()||void 0,time:(l(e,"Time")||e.Time||"").toString().trim()||void 0,outsdRoNo:(l(e,"OutsdRoNo")||e.OutsdRoNo||"").toString().trim()||void 0,dmsRoNo:(l(e,"DMSRoNo")||e.DMSRoNo||"").toString().trim()||void 0,errorMessage:(l(e,"ErrorMessage")||e.ErrorMessage||"").toString().trim()||void 0}}function d(e){return a((e?.rey_RomeCustServVehComb??e??{}).CustServVehComb).map(e=>{const t=e?.NameContactId??void 0,r=t?.NameId??void 0,n=r?.IndName?$(r.IndName):void 0,o=r?.BusName?$(r.BusName):void 0,s=t&&{NameId:r&&{...$(r)||{},...n?{IndName:n}:{},...o?{BusName:o}:{}},Address:a(t?.Address).map(e=>$(e)||{}),ContactOptions:a(t?.ContactOptions).map(e=>$(e)||{}),Phone:a(t?.Phone).map(e=>$(e)||{}),Email:a(t?.Email).map(e=>$(e)||{})},i=a(e?.ServVehicle).map(e=>{const t=e?.Vehicle,r=t?.VehicleDetail,n=t&&{...$(t)||{},...r?{VehicleDetail:$(r)}:{}},o=e?.VehicleServInfo,s=o?.VehExtWarranty,i=o?.Advisor,l=i?.ContactInfo,c=o&&{...$(o)||{},...s?{VehExtWarranty:$(s)}:{},...i?{Advisor:l?{ContactInfo:$(l)||{}}:{Advisor:{}}}:{},...e?.VehicleServInfo?.VehServComments?{VehServComments:a(e.VehicleServInfo.VehServComments).map(e=>Q(e))}:{}};return{...n?{Vehicle:n}:{},...c?{VehicleServInfo:c}:{}}}),l=a(e?.Message).map(e=>({...$(e)||{},Text:Q(e)})),c={};return s&&(c.NameContactId=s),i.length&&(c.ServVehicle=i),l.length&&(c.Message=l),c})}function y(e,t){if(null!=e)return String("string"==typeof e||"number"==typeof e?e:e[t]||"")}function g(e,...t){if(e)for(const r of t){if(e.$&&null!=e.$[r])return e.$[r];if(null!=e[`@_${r}`])return e[`@_${r}`];if(null!=e[r]&&"object"!=typeof e[r])return e[r]}}function N(e){const t=function(e){const t=n(e,e=>"rey_RomeCustomerResponse"===e||e.endsWith(":rey_RomeCustomerResponse"));return t?o(t,e=>"TransStatus"===e||e.endsWith(":TransStatus")).flatMap(a)[0]:void console.log("No rey_RomeCustomerResponse found in root")}(e);if(!t)return{dmsRecKey:void 0};const r=g(t,"DMSRecKey");return{dmsRecKey:null!=r?String(r):void 0,status:g(t,"Status"),statusCode:g(t,"StatusCode")}}function T(e){if(null==e)return;const t=String(e).toUpperCase().replace(/[^A-Z0-9 ]+/g,"");if(!t)throw new j(`Invalid string: ${e}. Must contain A-Z, 0-9, or space`);return t}function b(e){return e?String(e).toUpperCase():void 0}function x(e={}){const t=b(e.ibFlag)||(e.firstName?"I":"B");if("I"!==t&&"B"!==t)throw new j("ibFlag must be 'I' or 'B'");const r=e.lastName||e.customerName;if(!r)throw new j("lastName or customerName required");if("I"===t&&!e.firstName)throw new j("firstName required when ibFlag='I'");const n=b(e.customerType);if(n&&!["R","W","I"].includes(n))throw new j("customerType must be 'R', 'W', 'I', Retail, Wholesale, or Internal");const o=(e.addresses||[]).map(e=>{const t={Type:b(e.type)||"P",Addr1:e.line1?String(e.line1):void 0,Addr2:e.line2?String(e.line2):void 0,City:e.city?String(e.city):void 0,State:e.state?String(e.state):void 0,Zip:e.postalCode?String(e.postalCode):void 0,County:e.county?String(e.county):void 0,Country:e.country?String(e.country):void 0};if(!t.Addr1)throw new j("Address requires line1");return t}),a=(e.phones||[]).map(e=>{const t={Type:b(e.type)||"H",Num:e.number?String(e.number):void 0,Ext:e.extension?String(e.extension):void 0};if(!t.Num)throw new j("Phone requires number");return t}),s=e.emails?.[0]?.address?{MailTo:String(e.emails[0].address)}:void 0,i=e.personal,l=i?{Gender:b(i.gender),OtherName:T(i.otherName),AnniversaryDate:i.anniversaryDate?String(i.anniversaryDate):void 0,EmployerName:T(i.employerName),EmployerPhone:i.employerPhone?String(i.employerPhone):void 0,Occupation:T(i.occupation),OptOut:i.optOut?String(i.optOut):void 0,OptOutUse:i.optOutUse?String(i.optOutUse):void 0,BirthDates:(i.birthDates||[]).map(e=>({Type:b(e.type)||"P",date:e.date?String(e.date):void 0})).filter(e=>e.date),SSNs:(i.ssns||[]).map(e=>({Type:b(e.type)||"P",ssn:e.ssn?String(e.ssn):void 0})).filter(e=>e.ssn),DriverInfo:i.driver?[{Type:b(i.driver.type)||"P",LicNum:i.driver.licenseNumber?String(i.driver.licenseNumber):void 0,LicState:i.driver.licenseState?String(i.driver.licenseState):void 0,LicExpDate:i.driver.licenseExpDate?String(i.driver.licenseExpDate):void 0}].filter(e=>e.LicNum):void 0,CustChildren:(i.children||[]).map(e=>({ChildName:T(e.name)})).filter(e=>e.ChildName)}:void 0,c=e.dms,m=c?{TaxExemptNum:c.taxExemptNum?String(c.taxExemptNum):void 0,SalesTerritory:c.salesTerritory?String(c.salesTerritory):void 0,DeliveryRoute:c.deliveryRoute?String(c.deliveryRoute):void 0,SalesmanNum:c.salesmanNum?String(c.salesmanNum):void 0,LastContactMethod:c.lastContactMethod?String(c.lastContactMethod):void 0,Followups:(c.followups||[]).map(e=>({Type:b(e.type),Value:b(e.value)})).filter(e=>e.Type&&e.Value)}:void 0;return{custCateg:n||"R",createdBy:e.createdBy?String(e.createdBy):void 0,contactInfo:{IBFlag:t,LastName:T(r),FirstName:T(e.firstName),MidName:T(e.midName),Salut:T(e.salut),Suffix:T(e.suffix),Addresses:o,Phones:a,Email:s},custPersonal:l,dmsCustInfo:m}}function C(e){const t=n(e,e=>"rey_RomeServVehicleInsertResponse"===e||e.endsWith(":rey_RomeServVehicleInsertResponse"))||e,r=n(t,e=>"GenTransStatus"===e||e.endsWith(":GenTransStatus"));return{status:r?i(r,"Status"):void 0,statusCode:r?i(r,"StatusCode"):void 0}}function R(e,t){const r=Array.isArray(t)?t.map(e=>e.toLowerCase()):[String(t).toLowerCase()],n=[e];for(;n.length;){const e=n.pop();if(H(e))for(const t of Object.keys(e)){const o=e[t],a=W(t).toLowerCase();if(r.includes(a))return o;if(H(o))n.push(o);else if(Array.isArray(o))for(const e of o)H(e)&&n.push(e)}}}function f(e,t){if(e&&H(e)){if(null!=e.$?.[t])return e.$[t];if(null!=e[`@${t}`])return e[`@${t}`];if(null!=e[`@_${t}`])return e[`@_${t}`]}}function S(e){if(null!=e){if("string"==typeof e)return e;if("number"==typeof e)return String(e);if(H(e)){if(null!=e._)return String(e._);if(null!=e["#text"])return String(e["#text"]);if(null!=e.text)return String(e.text)}}}function v(e){const t=R(e,["CreateBSMRepairOrderResp","UpdateBSMRepairOrderResp"])||e,r=R(t,"RoRecordStatus")||{};return{status:f(r,"Status")||S(R(r,"Status")),date:f(r,"Date")||S(R(r,"Date")),time:f(r,"Time")||S(R(r,"Time")),outsdRoNo:f(r,"OutsdRoNo")||S(R(r,"OutsdRoNo")),dmsRoNo:f(r,"DMSRoNo")||S(R(r,"DMSRoNo")),errorMessage:f(r,"ErrorMessage")||S(R(r,"ErrorMessage"))}}function O(t={},r={}){const n=function(e){if(!e)throw new Error("department is required (S, P, B, SERVICE, PARTS, BODY)");const t=String(e).trim().toUpperCase();if("S"===t||"P"===t||"B"===t)return t;if("SERVICE"===t)return"S";if("PART"===t||"PARTS"===t)return"P";if("BODY"===t||"BODYSHOP"===t||"BODY SHOP"===t)return"B";throw new Error(`Invalid department: ${e}. Must be S, P, B, SERVICE, PARTS, BODY, BODYSHOP, or BODY SHOP`)}(t.department),o=t.advisorNumber?String(t.advisorNumber).trim():void 0,a=e({routing:r.routing,sender:r?.envelope?.sender,creationDateTime:r?.envelope?.creationDateTime,bodId:r?.envelope?.bodId});return{starXml:M.render('\n\n {{{ApplicationArea}}}\n \n \n \n\n',{ApplicationArea:a,DepartmentType:n,AdvisorNumber:o}),routing:r.routing,envelope:r.envelope,xsdFilename:"rey_RomeGetAdvisorsReq.xsd",elementName:"rey_RomeGetAdvisorsReq",postParse:e=>function(e,t={}){const r=t?.department;var n;return(e=>{const t=e?.Advisor;return t?Array.isArray(t)?t:[t]:[]})((n=e,n?.rey_RomeGetAdvisorsResp??n??{})).map(e=>({advisorId:Y(e,"AdvisorNumber"),firstName:Y(e,"FirstName"),lastName:Y(e,"LastName"),department:r}))}(e,{department:n})}}function F(e){return null==e?void 0:String(e)}function I(e,t){if(e)return null!=e[t]?"object"!=typeof e[t]?F(e[t]):F(e[t]["#text"]):null!=e[`@_${t}`]?F(e[`@_${t}`]):void 0}function h(e){const t=e?.meta?.statusBlocks?.transaction?.message;if(t)return String(t);return String((e?.meta?.status?.Message??e?.meta?.status?.message)||e?.message||"")}function w(e,t){return Math.min(1e4,e*Math.pow(2,t))}function P(e){return e+Math.floor(250*Math.random())}function D(e){return new Promise(t=>setTimeout(t,e))}function A(e){return/lock|in use|record.*busy/i.test(String(e||""))}import M from"mustache";import{v4 as V}from"uuid";import E from"axios";import{XMLParser as L}from"fast-xml-parser";class _ extends Error{constructor(e,t={}){super(e),this.name="RRTransportError",this.meta=t}}class B extends Error{constructor(e,t={}){super(e),this.name="RRVendorStatusError",this.meta=t,this.retryable=!!t.retryable}}class j extends Error{constructor(e,t={}){super(e),this.name="RRValidationError",this.meta=t}}const q=Object.freeze({__proto__:null,RRTransportError:_,RRVendorStatusError:B,RRValidationError:j}),U={info:(...e)=>console.log("[rr-rome]",...e),warn:(...e)=>console.warn("[rr-rome]",...e),error:(...e)=>console.error("[rr-rome]",...e),debug:(...e)=>{process.env.RR_DEBUG&&console.log("[rr-rome][debug]",...e)}},k=e=>null!=e&&""!==String(e).trim(),G=new L({ignoreAttributes:!1,attributeNamePrefix:"@_",parseAttributeValue:!1,parseTagValue:!1,isArray:e=>["Advisor"].includes(e)}),$=e=>{if(!e||"object"!=typeof e)return;const t={};for(const[r,n]of Object.entries(e))r.startsWith("@_")&&(t[r.slice(2)]=n);return Object.keys(t).length?t:void 0},Q=e=>{if(null!=e)return"string"==typeof e?e:e["#text"]},W=e=>{if("string"!=typeof e)return"";let t=e.includes("}")?e.split("}").pop():e;return t=t.includes(":")?t.split(":").pop():t,t.startsWith("rey_")&&(t=t.slice(4)),t},H=e=>e&&"object"==typeof e&&!Array.isArray(e),Y=(e,t)=>((e,t)=>e?.[`@_${t}`])(e,t)??s(e,t);class X{constructor(e){if(!e?.baseUrl)throw new Error("RRClient requires baseUrl");if(!e?.username)throw new Error("RRClient requires username");if(!e?.password)throw new Error("RRClient requires password");this.cfg={wssePasswordType:"Text",timeoutMs:3e4,logger:U,retries:{max:3},...e},this.mask={password:!0}}async _send(e){const{starXml:n,routing:s,envelope:i,postParse:d}=e,y=this.cfg.logger||U,{bodId:g,creationDateTime:N,sender:T}=function(e){return{bodId:e?.bodId||V(),creationDateTime:e?.creationDateTime||new Date,sender:e?.sender||{}}}(i),b=function({username:e,password:n,wssePasswordType:o="Text",starContentXml:a}){const s=function(e){return`\n \n \n \n${t(e,10)}\n \n \n \n `.trim()}(a);return`\n \n \n${t(function(e,t,n){const o="Digest"===n?' Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest"':' Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText"';return`\n \n \n ${r(e)}\n ${r(t)}\n \n \n `.trim()}(e,n,o),8)}\n \n \n${t(s,8)}\n \n \n `.trim()}({username:this.cfg.username,password:this.cfg.password,wssePasswordType:this.cfg.wssePasswordType||"Text",routing:s,sender:T,creationDateTime:(x=N,"string"==typeof x?x:(x instanceof Date?x:new Date).toISOString().replace(/\.\d{3}Z$/,"Z")),bodId:g,starContentXml:n});var x;"1"===process.env.RR_DUMP_ENVELOPE&&console.log(`[rr] Soap envelope about to send (${e?.elementName}): \n`+b+"\n");const C=async()=>{const t=await async function({baseUrl:e,envelopeXml:t,timeoutMs:r=3e4,logger:n}){try{const n=await E.post(e,t,{timeout:r,headers:{"Content-Type":"text/xml; charset=utf-8",SOAPAction:"http://www.starstandards.org/webservices/2005/10/transport/ProcessMessage"},responseType:"text",validateStatus:()=>!0});if(n.status>=200&&n.status<300)return String(n.data??"");throw new _(`HTTP ${n.status}: ${function(e){const t=String(e??"");return t.length>1024?t.slice(0,1024)+"…":t}(n.data)}`,{status:n.status,body:n.data})}catch(e){if(E.isAxiosError(e))throw new _(`Network error: ${e.message}`,{cause:e});throw e}}({baseUrl:this.cfg.baseUrl,envelopeXml:b,timeoutMs:this.cfg.timeoutMs,logger:y}),r=function(e){const t=G.parse(e),r=t?.Envelope||t?.["soapenv:Envelope"]||t,n=r?.Body||r?.["soapenv:Body"]||r?.["S:Body"]||r?.["soap:Body"],o=n?.ProcessMessageResponse||n?.["ns2:ProcessMessageResponse"]||n?.["trans:ProcessMessageResponse"]||n,a=o?.payload||o?.["ns2:payload"]||o;return a?.content||a?.["ns2:content"]||a}(t),n=function(e){const t=m(e),r=o(e,e=>/RoRecordStatus$/i.test(e)).flatMap(a)[0];return{transaction:p(t),roRecord:u(r)}}(r),s=function(e){const t=String("ApplicationArea").toLowerCase(),r=[e];for(;r.length;){const e=r.pop();if(e&&"object"==typeof e)for(const[n,o]of Object.entries(e)){if(n.toLowerCase().endsWith(t))return o;o&&"object"==typeof o&&r.push(o)}}}(r),{status:i,norm:g}=function(e){const t=m(e)||o(e,e=>/Status$/i.test(e)).flatMap(a)[0]||{},r=(l(t,"Status")||t.Status||"").toString().trim();let n=(l(t,"StatusCode")||t.StatusCode||"").toString().trim();const s=t.Message&&c(t.Message)||c(t)||"";!n&&/success/i.test(r)&&(n="0");const i=Number(n);let p="FAIL";return/success/i.test(r)||0===i?p="SUCCESS":2!==i&&213!==i||(p="NO_MATCH"),{status:{Status:r,StatusCode:n,Message:s},norm:{kind:p,code:Number.isFinite(i)?i:void 0,message:s}}}(r);if("1"===process.env.RR_DUMP_STATUS&&(console.log("[rr] Status blocks:"),console.dir(n,{depth:null,colors:!0})),"1"===process.env.RR_DUMP_APPLICATION&&(console.log("[rr] ApplicationArea:"),console.dir(s,{depth:null,colors:!0})),"FAIL"===g.kind){const e=i?.StatusCode;throw new B(`Vendor status failure: ${e??""} ${i?.Message||i?.["#text"]||""}`.trim(),{status:i,resXml:t})}const N={success:"SUCCESS"===g.kind||"NO_MATCH"===g.kind,statusBlocks:n,applicationArea:s,xml:{request:b,response:t},parsed:r};if("function"==typeof d)try{N.data=d(r)}catch(e){y?.warn?.(`postParse failed: ${e?.message||e}`)}return"1"===process.env.RR_DUMP_XML&&console.log(`[rr] Full response XML (${e?.elementName}):\n`+t+"\n"),N?.data&&"1"===process.env.RR_DUMP_DATA&&(console.log(`[rr] Parsed response data (${e?.elementName}):\n`),console.dir(N.data,{depth:null,colors:!0})),N};try{return await async function(e,{max:t=3,logger:r}){let n,o=0;for(;o=t)break;const s=P(w(400,o));r?.warn?.(`Retrying attempt ${o}/${t} in ${s}ms: ${e.message}`),await D(s)}throw n}(C,{max:this.cfg.retries?.max??3,logger:y})}catch(e){if(e instanceof _||e instanceof B)throw e;throw new _(`Unexpected error: ${e?.message||e}`,{cause:e})}}async combinedSearch(t,r){return this._send(function(t={},r={}){if(!t||"object"!=typeof t)throw new j("combinedSearch requires a query object");const n=function(e){if(!e)return"";const t=String(e).trim().toLowerCase();return"telephone"===t?"phone":"lic"===t||"plate"===t?"license":"vin"===t||"partvin"===t?"vin":"name"===t?"name":"nameRecId"===t||"custId"===t?"nameRecId":"stkNo"===t||"stock"===t?"stkNo":t}(t.kind),o={MaxRecs:Math.min(Number(t.maxResults||t.maxRecs||50),50)};if("phone"===n){const e=y(t.phone,"phone");if(!e)throw new j("combinedSearch phone value missing");o.Phone=e}else if("license"===n){const e=y(t.license,"license");if(!e)throw new j("combinedSearch license value missing");o.LicenseNum=e}else if("vin"===n){const e=y(t.vin,"vin");if(!e)throw new j("combinedSearch vin value missing");o.PartVIN=e}else if("name"===n){const e=t.name;if(!e||"object"!=typeof e)throw new j("combinedSearch name requires { fname, lname, mname } or { name }");if(e.fname&&e.lname&&e.mname)o.FullName={FName:String(e.fname),LName:String(e.lname),MName:String(e.mname)};else{if(!e.name)throw new j("combinedSearch name requires { fname, lname, mname } or { name }");o.LName=String(e.name)}}else if("nameRecId"===n){const e=y(t.nameRecId,"custId");if(!e)throw new j("combinedSearch nameRecId value missing");o.NameRecId=e}else{if("stkNo"!==n)throw new j(`Unsupported CombinedSearch kind: ${t.kind}`);{const e=y(t.stkNo,"stkNo");if(!e)throw new j("combinedSearch stkNo value missing");o.StkNo=e}}if(1!==[o.Phone,o.LicenseNum,o.PartVIN,o.LName,o.FullName,o.NameRecId,o.StkNo].filter(Boolean).length)throw new j("combinedSearch requires exactly one search criterion");o.VehData={MakePfx:t.make||"ANY",Model:t.model||"ANY",Year:t.year||"ANY"};const a=e({routing:r.routing,sender:{component:"Rome",task:"CVC",referenceId:"Query",creator:"RCI",senderName:"RCI"},order:"creation-bod-sender-destination"});return{starXml:M.render('\n\n {{{ApplicationArea}}}\n \n \n {{#Phone}}{{/Phone}}\n {{#PartVIN}}{{/PartVIN}}\n {{#LicenseNum}}{{/LicenseNum}}\n {{#LName}}{{/LName}}\n {{#FullName}}{{/FullName}}\n {{#NameRecId}}{{/NameRecId}}\n {{#StkNo}}{{/StkNo}}\n \n \n \n\n',{ApplicationArea:a,...o}),routing:r.routing,envelope:r.envelope,elementName:"rey_RomeCustServVehCombReq",xsdFilename:"rey_RomeCustServVehCombReq.xsd",postParse:d}}(t,r))}async insertCustomer(t,r){return this._send(function(t,r){if(!r?.routing?.dealerNumber)throw new j("routing.dealerNumber required");const n=x(t),o=e({routing:r.routing,sender:{component:"Rome",task:"CU",referenceId:"Insert"},order:"sender-creation-bod-destination"});return{starXml:M.render('\n\n {{{ApplicationArea}}}\n \n \n {{#C.contactInfo.Addresses}}\n \n {{/C.contactInfo.Addresses}}\n {{#C.contactInfo.Phones}}\n \n {{/C.contactInfo.Phones}}\n {{#C.contactInfo.Email}}\n \n {{/C.contactInfo.Email}}\n \n {{#C.custPersonal}}\n \n {{#BirthDates}}\n \n {{/BirthDates}}\n {{#SSNs}}\n \n {{/SSNs}}\n {{#DriverInfo}}\n \n {{/DriverInfo}}\n {{#CustChildren}}\n \n {{/CustChildren}}\n \n {{/C.custPersonal}}\n {{#C.dmsCustInfo}}\n \n {{#Followups}}\n \n {{/Followups}}\n \n {{/C.dmsCustInfo}}\n \n\n',{ApplicationArea:o,C:n}),routing:r.routing,envelope:r.envelope,postParse:N,xsdFilename:"rey_RomeCustomerInsertReq.xsd",elementName:"rey_RomeCustomerInsertReq"}}(t,r))}async updateCustomer(t,r){return this._send(function(t,r){if(!r?.routing?.dealerNumber)throw new j("routing.dealerNumber required");if(!t.nameRecId)throw new j("nameRecId or customerId required");const n=b(t.ibFlag);if("I"!==n&&"B"!==n)throw new j("ibFlag required ('I' or 'B')");const o=x(t);o.contactInfo.NameRecId=String(t.nameRecId);const a=e({routing:r.routing,sender:{component:"Rome",task:"CU",referenceId:"Update"},order:"sender-creation-bod-destination"});return{starXml:M.render('\n\n {{{ApplicationArea}}}\n \n \n {{#C.contactInfo.Addresses}}\n \n {{/C.contactInfo.Addresses}}\n {{#C.contactInfo.Phones}}\n \n {{/C.contactInfo.Phones}}\n {{#C.contactInfo.Email}}\n \n {{/C.contactInfo.Email}}\n \n {{#C.custPersonal}}\n \n {{#BirthDates}}\n \n {{/BirthDates}}\n {{#SSNs}}\n \n {{/SSNs}}\n {{#DriverInfo}}\n \n {{/DriverInfo}}\n {{#CustChildren}}\n \n {{/CustChildren}}\n \n {{/C.custPersonal}}\n {{#C.dmsCustInfo}}\n \n {{#Followups}}\n \n {{/Followups}}\n \n {{/C.dmsCustInfo}}\n \n\n',{ApplicationArea:a,C:o}),routing:r.routing,envelope:r.envelope,postParse:N,xsdFilename:"rey_RomeCustomerUpdateReq.xsd",elementName:"rey_RomeCustomerUpdateReq"}}(t,r))}async insertServiceVehicle(t,r){return this._send(function(t={},r={}){if(!r?.routing?.dealerNumber)throw new j("routing.dealerNumber is required");const n=e({routing:r.routing,sender:r?.envelope?.sender??{component:"Rome",task:"SV",referenceId:"Insert"},creationDateTime:r?.envelope?.creationDateTime,bodId:r?.envelope?.bodId}),o=function(e={}){const t=e.vin;if(!t)throw new j("insertServiceVehicle: vin is required");const r=e.vehicleServInfo?.customerNo;if(!r)throw new j("insertServiceVehicle: vehicleServInfo.customerNo (or customerNo) is required");return{VIN:String(t),ModelDesc:e.modelDesc,Carline:e.carline,ExtClrDesc:e.extClrDesc,IntClrDesc:e.intClrDesc,TrimDesc:e.trimDesc,BodyStyle:e.bodyStyle,EngineDesc:e.engineDesc,TransDesc:e.transDesc,Year:e.year,Odometer:e.odometer,OdometerUnits:e.odometerUnits,LicNo:e.vehicleDetail?.licNo,CustomerNo:String(r),vehicleServInfo:{salesmanNo:e.vehicleServInfo?.salesmanNo,inServiceDate:e.vehicleServInfo?.inServiceDate,mileage:e.vehicleServInfo?.mileage,teamCode:e.vehicleServInfo?.teamCode,vehExtWarranty:(()=>{const t=e.vehicleServInfo?.vehExtWarranty;if(!t)return;const r={contractNumber:t.contractNumber,expirationDate:t.expirationDate,expirationMileage:t.expirationMileage};return Object.values(r).some(e=>null!=e&&""!==e)?r:void 0})(),advisor:(()=>{const t=e.vehicleServInfo?.advisor,r=t?.contactInfo?.nameRecId;return r?{contactInfo:{nameRecId:String(r)}}:void 0})()}}}(t);return{starXml:M.render('\n\n {{{ApplicationArea}}}\n\n \n {{#V.Year}}{{.}}{{/V.Year}}\n {{#V.Odometer}}{{.}}{{/V.Odometer}}\n {{#V.OdometerUnits}}{{.}}{{/V.OdometerUnits}}\n\n {{#V.LicNo}}{{/V.LicNo}}\n \n\n \n {{#V.vehicleServInfo.salesmanNo}}{{.}}{{/V.vehicleServInfo.salesmanNo}}\n {{#V.vehicleServInfo.inServiceDate}}{{.}}{{/V.vehicleServInfo.inServiceDate}}\n {{#V.vehicleServInfo.mileage}}{{.}}{{/V.vehicleServInfo.mileage}}\n {{#V.vehicleServInfo.teamCode}}{{.}}{{/V.vehicleServInfo.teamCode}}\n\n {{#V.vehicleServInfo.vehExtWarranty}}\n \n {{#contractNumber}}{{.}}{{/contractNumber}}\n {{#expirationDate}}{{.}}{{/expirationDate}}\n {{#expirationMileage}}{{.}}{{/expirationMileage}}\n \n {{/V.vehicleServInfo.vehExtWarranty}}\n\n {{#V.vehicleServInfo.advisor}}\n \n \n \n {{/V.vehicleServInfo.advisor}}\n \n\n',{ApplicationArea:n,V:o}),routing:r.routing,envelope:r.envelope,postParse:C,xsdFilename:"rey_RomeServVehicleInsertReq.xsd",elementName:"rey_RomeServVehicleInsertReq"}}(t,r))}async getAdvisors(e,t){return this._send(O(e,t))}async createRepairOrder(t,r){return this._send(function(t={},r={}){if(!r?.routing?.dealerNumber)throw new j("routing.dealerNumber required");if(!t?.customerNo)throw new j("customerNo (CustNo) required");if(!t?.departmentType)throw new j("departmentType (DeptType) required");if(!t?.vin)throw new j("vin (Vin) required");if(!t?.outsdRoNo)throw new j("outsdRoNo required");const n={customerNo:t.customerNo,advisorNo:t.advisorNo,tagNo:t.tagNo,outsdRoNo:t.outsdRoNo,departmentType:t.departmentType,vin:t.vin,mileageIn:t.mileageIn,roComment:t.roComment,estimate:t.estimate?{parts:t.estimate.parts,labor:t.estimate.labor,total:t.estimate.total}:void 0,tax:t.tax?{payType:t.tax.payType,taxCode:t.tax.taxCode,txblGrossAmt:t.tax.txblGrossAmt,grossTaxAmt:t.tax.grossTaxAmt}:void 0,rolabor:t.rolabor?{ops:t.rolabor.ops?.map(e=>({opCode:e.opCode,jobNo:e.jobNo,custPayTypeFlag:e.custPayTypeFlag,custTxblNtxblFlag:e.custTxblNtxblFlag,warrPayTypeFlag:e.warrPayTypeFlag,warrTxblNtxblFlag:e.warrTxblNtxblFlag,intrPayTypeFlag:e.intrPayTypeFlag,intrTxblNtxblFlag:e.intrTxblNtxblFlag,vlrCode:e.vlrCode,bill:e.bill?{payType:e.bill.payType,jobTotalHrs:e.bill.jobTotalHrs,billTime:e.bill.billTime,billRate:e.bill.billRate}:void 0,ccc:e.ccc?{cause:e.ccc.cause,complaint:e.ccc.complaint,correction:e.ccc.correction}:void 0,amount:e.amount?{payType:e.amount.payType,amtType:e.amount.amtType,custPrice:e.amount.custPrice,totalAmt:e.amount.totalAmt}:void 0}))}:void 0,ropart:t.ropart?{jobs:t.ropart.jobs?.map(e=>({opCode:e.opCode,jobNo:e.jobNo,lines:e.lines?.map(e=>({partNo:e.partNo,partNoDesc:e.partNoDesc,qtyOrd:e.partQty,sale:e.sale,cost:e.cost,addDeleteFlag:e.addDeleteFlag}))}))}:void 0,rogg:t.rogg?{roNo:t.rogg.roNo,ops:t.rogg.ops?.map(e=>({opCode:e.opCode,jobNo:e.jobNo,lines:e.lines?.map(e=>({breakOut:e.breakOut,itemType:e.itemType,itemDesc:e.itemDesc,custQty:e.custQty,warrQty:e.warrQty,intrQty:e.intrQty,custPayTypeFlag:e.custPayTypeFlag,warrPayTypeFlag:e.warrPayTypeFlag,intrPayTypeFlag:e.intrPayTypeFlag,custTxblNtxblFlag:e.custTxblNtxblFlag,warrTxblNtxblFlag:e.warrTxblNtxblFlag,intrTxblNtxblFlag:e.intrTxblNtxblFlag,amount:e.amount?{payType:e.amount.payType,amtType:e.amount.amtType,custPrice:e.amount.custPrice,dlrCost:e.amount.dlrCost}:void 0}))}))}:void 0,romisc:t.romisc?{roNo:t.romisc.roNo,ops:t.romisc.ops?.map(e=>({opCode:e.opCode,jobNo:e.jobNo,lines:e.lines?.map(e=>({miscCode:e.miscCode,custPayTypeFlag:e.custPayTypeFlag,warrPayTypeFlag:e.warrPayTypeFlag,intrPayTypeFlag:e.intrPayTypeFlag,custTxblNtxblFlag:e.custTxblNtxblFlag,warrTxblNtxblFlag:e.warrTxblNtxblFlag,intrTxblNtxblFlag:e.intrTxblNtxblFlag,codeAmt:e.codeAmt}))}))}:void 0};if(n.tax?.payType&&!["All","Cust","Intr","Warr"].includes(n.tax.payType))throw new j("tax.payType must be one of: All, Cust, Intr, Warr");if(n.rolabor?.ops?.some(e=>e.custTxblNtxblFlag&&!["T","N"].includes(e.custTxblNtxblFlag)||e.warrTxblNtxblFlag&&!["T","N"].includes(e.warrTxblNtxblFlag)||e.intrTxblNtxblFlag&&!["T","N"].includes(e.intrTxblNtxblFlag)))throw new j("Taxable flags (CustTxblNTxblFlag, WarrTxblNTxblFlag, IntrTxblNTxblFlag) must be 'T' or 'N'");if(n.rogg?.ops?.some(e=>e.lines?.some(e=>e.itemType&&!["G","P","S","F"].includes(e.itemType))))throw new j("rogg.ops.lines.itemType must be one of: G, P, S, F");const o={...r?.envelope||{},sender:{component:r?.envelope?.sender?.component??"Rome",task:r?.envelope?.sender?.task??"BSMRO",referenceId:r?.envelope?.sender?.referenceId??"Insert",creatorNameCode:r?.envelope?.sender?.creatorNameCode??"RCI",senderNameCode:r?.envelope?.sender?.senderNameCode??"RCI"}},a=e({routing:r.routing,sender:o.sender,creationDateTime:o.creationDateTime,bodId:o.bodId});return{starXml:M.render('\n\n {{{ApplicationArea}}}\n \n \n {{#RO.roComment}}\n \n {{/RO.roComment}}\n\n {{#RO.estimate}}\n \n {{/RO.estimate}}\n\n {{#RO.tax}}\n \n {{/RO.tax}}\n \n\n {{#RO.rolabor}}\n \n {{#RO.rolabor.ops}}\n \n {{#bill}}\n \n {{/bill}}\n\n {{#ccc}}\n \n {{/ccc}}\n\n {{#amount}}\n \n {{/amount}}\n \n {{/RO.rolabor.ops}}\n \n {{/RO.rolabor}}\n\n {{#RO.ropart}}\n \n {{#RO.ropart.jobs}}\n \n {{#lines}}\n \n {{/lines}}\n \n {{/RO.ropart.jobs}}\n \n {{/RO.ropart}}\n\n {{#RO.rogg}}\n \n {{#RO.rogg.ops}}\n \n {{#lines}}\n \n {{#amount}}\n \n {{/amount}}\n \n {{/lines}}\n \n {{/RO.rogg.ops}}\n \n {{/RO.rogg}}\n\n {{#RO.romisc}}\n \n {{#RO.romisc.ops}}\n \n {{#lines}}\n \n {{/lines}}\n \n {{/RO.romisc.ops}}\n \n {{/RO.romisc}}\n \n\n',{ApplicationArea:a,RO:n}),routing:r.routing,envelope:o,postParse:v,xsdFilename:"rey_RomeCreateBSMRepairOrderReq.xsd"}}(t,r))}async updateRepairOrder(t,r){return this._send(function(t={},r={}){if(!r?.routing?.dealerNumber)throw new j("routing.dealerNumber required");if(!t?.finalUpdate)throw new j("finalUpdate (FinalUpdate) required");if(!["Y","N"].includes(t.finalUpdate))throw new j("finalUpdate must be 'Y' or 'N'");if(!t?.outsdRoNo)throw new j(" outsdRoNo");const n={finalUpdate:t.finalUpdate||"N",roNo:t.roNo,customerNo:t.customerNo,tagNo:t.tagNo,outsdRoNo:t.outsdRoNo,departmentType:t.departmentType,vin:t.vin,mileageIn:t.mileageIn,mileageOut:t.mileageOut,roComment:t.roComment,estimate:t.estimate?{estimateType:t.estimate.estimateType}:void 0,tax:t.tax?{payType:t.tax.payType,taxCode:t.tax.taxCode,txblGrossAmt:t.tax.txblGrossAmt,grossTaxAmt:t.tax.grossTaxAmt}:void 0,rolabor:t.rolabor?{ops:t.rolabor.ops?.map(e=>({opCode:e.opCode,jobNo:e.jobNo,custPayTypeFlag:e.custPayTypeFlag,custTxblNtxblFlag:e.custTxblNtxblFlag,warrPayTypeFlag:e.warrPayTypeFlag,warrTxblNtxblFlag:e.warrTxblNtxblFlag,intrPayTypeFlag:e.intrPayTypeFlag,intrTxblNtxblFlag:e.intrTxblNtxblFlag,vlrCode:e.vlrCode,bill:e.bill?{payType:e.bill.payType,jobTotalHrs:e.bill.jobTotalHrs,billTime:e.bill.billTime,billRate:e.bill.billRate}:void 0,ccc:e.ccc?{cause:e.ccc.cause,complaint:e.ccc.complaint,correction:e.ccc.correction}:void 0,amount:e.amount?{payType:e.amount.payType,amtType:e.amount.amtType,custPrice:e.amount.custPrice}:void 0}))}:void 0,ropart:t.ropart?{jobs:t.ropart.jobs?.map(e=>({opCode:e.opCode,jobNo:e.jobNo,lines:e.lines?.map(e=>({partNo:e.partNo,partNoDesc:e.partNoDesc,qtyOrd:e.partQty,sale:e.sale,cost:e.cost,addDeleteFlag:e.addDeleteFlag}))}))}:void 0,rogg:t.rogg?{roNo:t.rogg.roNo,ops:t.rogg.ops?.map(e=>({opCode:e.opCode,jobNo:e.jobNo,lines:e.lines?.map(e=>({breakOut:e.breakOut,itemType:e.itemType,itemDesc:e.itemDesc,custQty:e.custQty,warrQty:e.warrQty,intrQty:e.intrQty,custPayTypeFlag:e.custPayTypeFlag,warrPayTypeFlag:e.warrPayTypeFlag,intrPayTypeFlag:e.intrPayTypeFlag,custTxblNtxblFlag:e.custTxblNtxblFlag,warrTxblNtxblFlag:e.warrTxblNtxblFlag,intrTxblNtxblFlag:e.intrTxblNtxblFlag,amount:e.amount?{payType:e.amount.payType,amtType:e.amount.amtType,custPrice:e.amount.custPrice,dlrCost:e.amount.dlrCost}:void 0}))}))}:void 0,romisc:t.romisc?{roNo:t.romisc.roNo,ops:t.romisc.ops?.map(e=>({opCode:e.opCode,jobNo:e.jobNo,lines:e.lines?.map(e=>({miscCode:e.miscCode,custPayTypeFlag:e.custPayTypeFlag,warrPayTypeFlag:e.warrPayTypeFlag,intrPayTypeFlag:e.intrPayTypeFlag,custTxblNtxblFlag:e.custTxblNtxblFlag,warrTxblNtxblFlag:e.warrTxblNtxblFlag,intrTxblNtxblFlag:e.intrTxblNtxblFlag,codeAmt:e.codeAmt}))}))}:void 0};if(n.tax?.payType&&!["All","Cust","Intr","Warr"].includes(n.tax.payType))throw new j("tax.payType must be one of: All, Cust, Intr, Warr");if(n.rolabor?.ops?.some(e=>e.custTxblNtxblFlag&&!["T","N"].includes(e.custTxblNtxblFlag)||e.warrTxblNtxblFlag&&!["T","N"].includes(e.warrTxblNtxblFlag)||e.intrTxblNtxblFlag&&!["T","N"].includes(e.intrTxblNtxblFlag)))throw new j("Taxable flags (CustTxblNTxblFlag, WarrTxblNTxblFlag, IntrTxblNTxblFlag) must be 'T' or 'N'");if(n.rogg?.ops?.some(e=>e.lines?.some(e=>e.itemType&&!["G","P","S","F"].includes(e.itemType))))throw new j("rogg.ops.lines.itemType must be one of: G, P, S, F");const o={...r?.envelope||{},sender:{component:r?.envelope?.sender?.component??"Rome",task:r?.envelope?.sender?.task??"BSMRO",referenceId:r?.envelope?.sender?.referenceId??"Update",creatorNameCode:r?.envelope?.sender?.creatorNameCode??"RCI",senderNameCode:r?.envelope?.sender?.senderNameCode??"RCI"}},a=e({routing:r.routing,sender:o.sender,creationDateTime:o.creationDateTime,bodId:o.bodId});return{starXml:M.render('\n\n {{{ApplicationArea}}}\n \n \n {{#RO.roComment}}\n \n {{/RO.roComment}}\n\n {{#RO.estimate}}\n \n {{/RO.estimate}}\n\n {{#RO.tax}}\n \n {{/RO.tax}}\n \n\n {{#RO.rolabor}}\n \n {{#RO.rolabor.ops}}\n \n {{#bill}}\n \n {{/bill}}\n\n {{#ccc}}\n \n {{/ccc}}\n\n {{#amount}}\n \n {{/amount}}\n \n {{/RO.rolabor.ops}}\n \n {{/RO.rolabor}}\n\n {{#RO.ropart}}\n \n {{#RO.ropart.jobs}}\n \n {{#lines}}\n \n {{/lines}}\n \n {{/RO.ropart.jobs}}\n \n {{/RO.ropart}}\n\n {{#RO.rogg}}\n \n {{#RO.rogg.ops}}\n \n {{#lines}}\n \n {{#amount}}\n \n {{/amount}}\n \n {{/lines}}\n \n {{/RO.rogg.ops}}\n \n {{/RO.rogg}}\n\n {{#RO.romisc}}\n \n {{#RO.romisc.ops}}\n \n {{#lines}}\n \n {{/lines}}\n \n {{/RO.romisc.ops}}\n \n {{/RO.romisc}}\n \n\n',{ApplicationArea:a,RO:n}),routing:r.routing,envelope:o,postParse:v,xsdFilename:"rey_RomeUpdateBSMRepairOrderReq.xsd",elementName:"rey_RomeUpdateBSMRepairOrderReq"}}(t,r))}async getParts(t,r){return this._send(function(t={},r={}){const n=t.roNumber?String(t.roNumber).trim():void 0;if(!n)throw new j("getParts: roNumber required");const o=e({routing:r.routing,sender:{component:"Rome",task:"RCT",referenceId:"Query",creator:"RCI",senderName:"RCI"}});return{starXml:M.render('\n\n {{{ApplicationArea}}}\n \n\n',{ApplicationArea:o,RoNumber:n}),routing:r.routing,envelope:r.envelope,xsdFilename:"rey_RomeGetPartsReq.xsd",elementName:"rey_RomeGetPartsReq",postParse:e=>function(e){return e?.rey_RomeGetPartsResp?.RoParts?.map(e=>({partNumber:I(e,"PartNumber"),partDescription:I(e,"PartDescription"),quantityOrdered:I(e,"QuantityOrdered"),quantityShipped:I(e,"QuantityShipped"),price:I(e,"Price"),cost:I(e,"Cost"),processedFlag:I(e,"ProcessedFlag"),addOrDelete:I(e,"AddOrDelete")}))||[]}(e)}}(t,r))}}export{X as RRClient,q as errors};
diff --git a/server/rr/lib/types.cjs b/server/rr/lib/types.cjs
deleted file mode 100644
index 9dcc48a23..000000000
--- a/server/rr/lib/types.cjs
+++ /dev/null
@@ -1,3 +0,0 @@
-'use strict';
-// CJS access point for JSDoc typedefs
-module.exports = require('./types.js');
diff --git a/server/rr/rr-lookup.js b/server/rr/rr-lookup.js
index 0eeb7ad2d..c9ed36287 100644
--- a/server/rr/rr-lookup.js
+++ b/server/rr/rr-lookup.js
@@ -47,9 +47,9 @@ function toCombinedSearchPayload(args = {}) {
if (q.phone) kind = "phone";
else if (q.license) kind = "license";
else if (q.vin) kind = "vin";
- else if (q.nameRecId || q.custId) kind = "nameRecId";
+ else if (q.nameRecId || q.custId) kind = "namerecid";
else if (q.name && (q.name.fname || q.name.lname || q.name.mname || q.name.name)) kind = "name";
- else if (q.stkNo || q.stock) kind = "stkNo";
+ else if (q.stkNo || q.stock) kind = "stkno";
}
// Map loose aliases into the RR builder’s expected fields
@@ -57,21 +57,41 @@ function toCombinedSearchPayload(args = {}) {
switch (kind) {
case "phone":
+ payload.kind = "phone";
payload.phone = q.phone;
break;
case "license":
+ payload.kind = "license";
payload.license = q.license;
break;
case "vin":
+ payload.kind = "vin";
payload.vin = q.vin;
break;
case "namerecid":
+ payload.kind = "nameRecId";
payload.nameRecId = q.nameRecId || q.custId;
break;
- case "name":
- payload.name = q.name; // { fname, lname, mname } or { name }
+ case "name": {
+ payload.kind = "name";
+ const name = q.name;
+ if (name.name) {
+ payload.name = { name: name.name }; // For LName
+ } else if (name.fname && name.lname) {
+ payload.name = {
+ fname: name.fname,
+ lname: name.lname,
+ ...(name.mname ? { mname: name.mname } : {})
+ }; // For FullName
+ } else if (name.lname) {
+ payload.name = { name: name.lname }; // Fallback to LName if only lname
+ } else {
+ // Invalid; but to handle gracefully, perhaps throw or skip
+ }
break;
+ }
case "stkno":
+ payload.kind = "stkNo";
payload.stkNo = q.stkNo || q.stock;
break;
default:
@@ -79,16 +99,20 @@ function toCombinedSearchPayload(args = {}) {
payload.kind = q.kind; // may be undefined; RR lib will validate
}
+ // Add compatible secondary fields for combinations
+ if (q.vin && kind !== "vin") payload.vin = q.vin;
+ if (q.phone && kind !== "phone") payload.phone = q.phone;
+ if (q.license && kind !== "license") payload.license = q.license;
+
// Optional vehicle narrowing; the RR builder defaults to ANY/ANY/ANY if omitted
if (q.make || q.model || q.year) {
- payload.make = q.make;
- payload.model = q.model;
- payload.year = q.year;
+ payload.make = q.make || "ANY";
+ payload.model = q.model || "ANY";
+ payload.year = q.year || "ANY";
}
return payload;
}
-
/**
* Combined customer/service/vehicle search
* @param bodyshop - bodyshop row (must include rr_dealerid & rr_configuration with store/branch)
diff --git a/server/web-sockets/rr-register-socket-events.js b/server/web-sockets/rr-register-socket-events.js
index 8cc371d6d..1a427a100 100644
--- a/server/web-sockets/rr-register-socket-events.js
+++ b/server/web-sockets/rr-register-socket-events.js
@@ -1,3 +1,6 @@
+// File: server/rr/rr-register-socket-events.js
+// PATCH: add helper + modify rr-export-job customer preselect logic
+
const RRLogger = require("../rr/rr-logger");
const { rrCombinedSearch, rrGetAdvisors, rrGetParts } = require("../rr/rr-lookup");
const { QueryJobData } = require("../rr/rr-job-helpers");
@@ -125,6 +128,67 @@ function readAdvisorNo(payload, cached) {
function registerRREvents({ socket, redisHelpers }) {
const log = RRLogger(socket);
+ // --- NEW helper: run name + vin searches and merge ---
+ async function rrMultiCustomerSearch(bodyshop, job) {
+ const queries = [];
+
+ // Extract fields
+ const fname = (job?.ownr_fn || job?.customer?.first_name || job?.customer?.firstName || "").trim();
+ const lname = (job?.ownr_ln || job?.customer?.last_name || job?.customer?.lastName || "").trim();
+ const vin = (job?.v_vin || job?.vehicle?.vin || job?.vin || "").trim();
+
+ // Build combined query if possible
+ if (vin && (fname || lname)) {
+ const nameObj = {};
+ if (fname) nameObj.fname = fname;
+ if (lname) nameObj.lname = lname;
+ queries.push({
+ kind: "name",
+ name: nameObj,
+ vin: vin,
+ maxResults: 50
+ });
+ } else if (vin) {
+ queries.push({ kind: "vin", vin: vin, maxResults: 50 });
+ } else if (fname || lname) {
+ // Standalone name: prefer FullName if both, else LName if only lname (though spec prefers combos)
+ // Note: Standalone Last Name not in spec; use with caution or add MMY if available
+ const nameObj = {};
+ if (fname) nameObj.fname = fname;
+ if (lname) nameObj.lname = lname;
+ queries.push({
+ kind: "name",
+ name: nameObj,
+ maxResults: 50
+ });
+ }
+
+ if (!queries.length) return [];
+
+ // Execute searches serially (could be Promise.all; keep serial for vendor rate safety)
+ const all = [];
+ for (const q of queries) {
+ try {
+ const res = await rrCombinedSearch(bodyshop, q);
+ const norm = normalizeCustomerCandidates(res);
+ all.push(...norm);
+ } catch (e) {
+ log("warn", "multi-search subquery failed", { kind: q.kind, error: e.message });
+ }
+ }
+
+ // De-dupe by custNo
+ const seen = new Set();
+ const merged = [];
+ for (const c of all) {
+ const k = c && c.custNo && String(c.custNo).trim();
+ if (!k || seen.has(k)) continue;
+ seen.add(k);
+ merged.push({ custNo: String(c.custNo), name: c.name });
+ }
+ return merged;
+ }
+
// --------- Lookups (customer search → open table) ---------
socket.on("rr-lookup-combined", async ({ jobid, params } = {}, cb) => {
try {
@@ -282,9 +346,8 @@ function registerRREvents({ socket, redisHelpers }) {
// --------- Main export (Fortellis-style staging) ---------
socket.on("rr-export-job", async (payload = {}) => {
const _log = RRLogger(socket, { ns: "rr" });
-
try {
- // 1) Resolve job
+ // (existing preamble unchanged)
let job = payload.job || payload.txEnvelope?.job;
const jobId = payload.jobId || payload.jobid || payload.txEnvelope?.jobId || job?.id;
if (!job) {
@@ -293,7 +356,6 @@ function registerRREvents({ socket, redisHelpers }) {
}
const ns = getTransactionType(job.id);
- // Persist txEnvelope + job
await redisHelpers.setSessionTransactionData(
socket.id,
ns,
@@ -303,7 +365,6 @@ function registerRREvents({ socket, redisHelpers }) {
);
await redisHelpers.setSessionTransactionData(socket.id, ns, RRCacheEnums.JobData, job, defaultRRTTL);
- // 2) Resolve bodyshop
let bodyshopId = payload.bodyshopId || payload.bodyshopid || payload.bodyshopUUID || job?.bodyshop?.id;
if (!bodyshopId) {
const sess = await getSessionOrSocket(redisHelpers, socket);
@@ -316,7 +377,6 @@ function registerRREvents({ socket, redisHelpers }) {
? job.bodyshop
: await getBodyshopForSocket({ bodyshopId, socket });
- // 3) Resolve advisor number (from form or cache)
const cachedAdvisor = await redisHelpers.getSessionTransactionData(socket.id, ns, RRCacheEnums.AdvisorNo);
const advisorNo = readAdvisorNo(payload, cachedAdvisor);
if (!advisorNo) {
@@ -331,9 +391,7 @@ function registerRREvents({ socket, redisHelpers }) {
defaultRRTTL
);
- // 4) Resolve selected customer (payload → cache)
let selectedCust = null;
-
if (payload.selectedCustomer) {
if (typeof payload.selectedCustomer === "object" && payload.selectedCustomer.custNo) {
selectedCust = { custNo: String(payload.selectedCustomer.custNo) };
@@ -350,47 +408,74 @@ function registerRREvents({ socket, redisHelpers }) {
);
}
}
-
if (!selectedCust) {
const cached = await redisHelpers.getSessionTransactionData(socket.id, ns, RRCacheEnums.SelectedCustomer);
if (cached) selectedCust = { custNo: String(cached) };
}
- // Flags
const forceCreate = payload.forceCreate === true;
- const autoCreateOnNoMatch = payload.autoCreateOnNoMatch !== false; // default TRUE
+ const autoCreateOnNoMatch = payload.autoCreateOnNoMatch !== false;
- // 5) If no selection & not "forceCreate", try auto-search
+ // --- MODIFIED: multi-search (name + vin) when beginning selection ---
if (!selectedCust && !forceCreate) {
- const customerQuery = makeCustomerSearchPayloadFromJob(job);
- const vehicleQuery = makeVehicleSearchPayloadFromJob(job);
- const query = customerQuery || vehicleQuery;
+ const mergedCandidates = await rrMultiCustomerSearch(bodyshop, job);
- if (query) {
- _log("info", "rr-export-job:customer-preselect-search", { query, jobId });
- const searchRes = await rrCombinedSearch(bodyshop, query);
- const candidates = normalizeCustomerCandidates(searchRes);
+ if (mergedCandidates.length === 1) {
+ selectedCust = { custNo: String(mergedCandidates[0].custNo) };
+ await redisHelpers.setSessionTransactionData(
+ socket.id,
+ ns,
+ RRCacheEnums.SelectedCustomer,
+ selectedCust.custNo,
+ defaultRRTTL
+ );
+ _log("info", "rr-export-job:auto-selected-customer(multi)", { jobId, custNo: selectedCust.custNo });
+ } else if (mergedCandidates.length > 1) {
+ socket.emit("rr-select-customer", mergedCandidates);
+ socket.emit("rr-log-event", {
+ level: "info",
+ message: "RR: customer selection required (multi-search)",
+ ts: Date.now()
+ });
+ return; // wait for user selection
+ } else {
+ // Fallback to original single-query logic (phone etc.) only if no multi results
+ // (retain legacy behavior)
+ const fallbackQuery = makeCustomerSearchPayloadFromJob(job) || makeVehicleSearchPayloadFromJob(job) || null;
- if (candidates.length === 1) {
- selectedCust = { custNo: String(candidates[0].custNo) };
- await redisHelpers.setSessionTransactionData(
- socket.id,
- ns,
- RRCacheEnums.SelectedCustomer,
- selectedCust.custNo,
- defaultRRTTL
- );
- _log("info", "rr-export-job:auto-selected-customer", { jobId, custNo: selectedCust.custNo });
- } else if (candidates.length > 1) {
- // multiple matches → table and stop
- socket.emit("rr-select-customer", candidates); // FE expects [{custNo,name}]
- socket.emit("rr-log-event", {
- level: "info",
- message: "RR: customer selection required",
- ts: Date.now()
- });
- return;
- } else {
+ if (fallbackQuery) {
+ _log("info", "rr-export-job:fallback-single-query", { fallbackQuery, jobId });
+ try {
+ const searchRes = await rrCombinedSearch(bodyshop, fallbackQuery);
+ const candidates = normalizeCustomerCandidates(searchRes);
+ if (candidates.length === 1) {
+ selectedCust = { custNo: String(candidates[0].custNo) };
+ await redisHelpers.setSessionTransactionData(
+ socket.id,
+ ns,
+ RRCacheEnums.SelectedCustomer,
+ selectedCust.custNo,
+ defaultRRTTL
+ );
+ _log("info", "rr-export-job:auto-selected-customer(fallback)", {
+ jobId,
+ custNo: selectedCust.custNo
+ });
+ } else if (candidates.length > 1) {
+ socket.emit("rr-select-customer", candidates);
+ socket.emit("rr-log-event", {
+ level: "info",
+ message: "RR: customer selection required",
+ ts: Date.now()
+ });
+ return;
+ }
+ } catch (e) {
+ _log("warn", "rr-export-job:fallback-query failed", { error: e.message });
+ }
+ }
+
+ if (!selectedCust) {
if (autoCreateOnNoMatch) {
const created = await createRRCustomer({ bodyshop, job, socket });
const custNo = created?.custNo || created?.customerNo || created?.CustomerNo || created?.dmsRecKey;
@@ -403,39 +488,26 @@ function registerRREvents({ socket, redisHelpers }) {
selectedCust.custNo,
defaultRRTTL
);
- _log("info", "rr-export-job:auto-created-customer", { jobId, custNo: selectedCust.custNo });
+ _log("info", "rr-export-job:auto-created-customer(multi-zero)", {
+ jobId,
+ custNo: selectedCust.custNo
+ });
} else {
socket.emit("rr-customer-create-required");
- socket.emit("rr-log-event", { level: "info", message: "RR: create customer required", ts: Date.now() });
+ socket.emit("rr-log-event", {
+ level: "info",
+ message: "RR: create customer required",
+ ts: Date.now()
+ });
return;
}
}
- } else {
- // no usable query → create or prompt
- if (autoCreateOnNoMatch) {
- const created = await createRRCustomer({ bodyshop, job, socket });
- const custNo = created?.custNo || created?.customerNo || created?.CustomerNo || created?.dmsRecKey;
- if (!custNo) throw new Error("RR create customer returned no custNo");
- selectedCust = { custNo: String(custNo) };
- await redisHelpers.setSessionTransactionData(
- socket.id,
- ns,
- RRCacheEnums.SelectedCustomer,
- selectedCust.custNo,
- defaultRRTTL
- );
- _log("info", "rr-export-job:auto-created-customer(no-query)", { jobId, custNo: selectedCust.custNo });
- } else {
- socket.emit("rr-customer-create-required");
- socket.emit("rr-log-event", { level: "info", message: "RR: create customer required", ts: Date.now() });
- return;
- }
}
}
if (!selectedCust?.custNo) throw new Error("RR export: selected customer missing custNo");
- // 6) Perform export (ensure SV + create/update RO inside exportJobToRR)
+ // (rest of existing export logic unchanged)
const result = await exportJobToRR({
bodyshop,
job,
@@ -469,9 +541,7 @@ function registerRREvents({ socket, redisHelpers }) {
_log("error", `Error during RR export: ${error.message}`, { jobId, stack: error.stack });
try {
socket.emit("export-failed", { vendor: "rr", jobId, error: error.message });
- } catch {
- /* ignore */
- }
+ } catch {}
}
});