658 lines
32 KiB
Markdown
658 lines
32 KiB
Markdown
# 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<RRResult<T>>` 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<CombinedSearchBlock>
|
||
```
|
||
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 `<Phone Num="..."/>` |
|
||
| license | string\|number\|{license:string} | Conditionally (kind==='license') | Value mapped to `<LicenseNum LicNo="..."/>` |
|
||
| vin | string\|number\|{vin:string} | Conditionally (kind==='vin') | Value mapped to `<PartVIN Vin="..."/>` (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 `<NameRecId CustIdStart="..."/>` |
|
||
| stkNo | string\|number\|{stkNo:string} | Conditionally (kind==='stkNo') | Emits `<StkNo VehId="..."/>` |
|
||
| 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 -> `<Email MailTo="..."/>` |
|
||
| 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 `<Year>` element if present |
|
||
| odometer | string\|number | No | Emits `<Odometer>` |
|
||
| odometerUnits | string | No | Emits `<OdometerUnits>` |
|
||
| vehicleDetail.licNo | string | No | Emits `<VehicleDetail LicNo="..."/>` |
|
||
| 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 | `<RoCommentInfo RoComment="..."/>` |
|
||
| 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 `<RoInfo RoNumber="..."/>` |
|
||
|
||
### 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<T> {
|
||
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<T>` 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 <test> [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-<uuid>-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:<op>` – 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. |