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 281f846e4..213fcb475 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
@@ -119,7 +119,7 @@ export function DmsPostForm({ bodyshop, socket, job, logsRef }) {
if (dms === "rr") {
// values will now include advisorNo from the RR dropdown
wsssocket.emit("rr-export-job", {
- bodyshopId: bodyshop?.id || bodyshop?.bodyshopid || bodyshop?.uuid,
+ bodyshopId: bodyshop?.id,
jobId: job.id,
job,
txEnvelope: values
diff --git a/client/src/graphql/jobs.queries.js b/client/src/graphql/jobs.queries.js
index f9f1cd55f..d7874e14a 100644
--- a/client/src/graphql/jobs.queries.js
+++ b/client/src/graphql/jobs.queries.js
@@ -713,7 +713,7 @@ export const GET_JOB_BY_PK = gql`
v_model_yr
v_model_desc
v_vin
- notes(where:{pinned: {_eq: true}}, order_by: {updated_at: desc}) {
+ notes(where: { pinned: { _eq: true } }, order_by: { updated_at: desc }) {
created_at
created_by
critical
@@ -999,7 +999,6 @@ export const QUERY_JOB_CARD_DETAILS = gql`
key
type
}
-
}
}
`;
@@ -2171,6 +2170,8 @@ export const QUERY_JOB_EXPORT_DMS = gql`
ownr_fn
ownr_ln
ownr_co_nm
+ ownr_ph1
+ ownr_ph2
ins_co_nm
kmin
kmout
@@ -2179,6 +2180,10 @@ export const QUERY_JOB_EXPORT_DMS = gql`
v_model_desc
area_of_damage
date_exported
+ v_vin
+ plate_no
+ plate_st
+ ownr_co_nm
}
}
`;
@@ -2413,7 +2418,7 @@ export const QUERY_PARTS_QUEUE_CARD_DETAILS = gql`
start
status
}
- notes(where:{pinned: {_eq: true}}, order_by: {updated_at: desc}) {
+ notes(where: { pinned: { _eq: true } }, order_by: { updated_at: desc }) {
created_at
created_by
critical
diff --git a/server/fortellis/fortellis.js b/server/fortellis/fortellis.js
index 817aad0a7..eae5016eb 100644
--- a/server/fortellis/fortellis.js
+++ b/server/fortellis/fortellis.js
@@ -297,10 +297,12 @@ async function FortellisSelectedCustomer({ socket, redisHelpers, selectedCustome
await QueryDmsErrWip({ socket, redisHelpers, JobData });
const DmsError = await QueryDmsErrWip({ socket, redisHelpers, JobData });
- // //Delete the transaction
+
+ //Delete the transaction
CreateFortellisLogEvent(socket, "DEBUG", `{{ 6.2 } Deleting Transaction ID ${socket.DMSTransHeader.transID}`);
- const DmsBatchTxnDelete = await DeleteDmsWip({ socket, redisHelpers, JobData });
+ // Delete DMS Wip
+ await DeleteDmsWip({ socket, redisHelpers, JobData });
DmsError.errMsg
.split("|")
@@ -318,8 +320,12 @@ async function FortellisSelectedCustomer({ socket, redisHelpers, selectedCustome
"ERROR",
`DMS Batch Return code was not successful: ${DMSBatchTxn.rtnCode} - ${DMSBatchTxn.sendline}`
);
- await InsertFailedExportLog({ socket, JobData, error: `DMS Batch Return code was not successful: ${DMSBatchTxn.rtnCode} - ${DMSBatchTxn.sendline}` });
+ await InsertFailedExportLog({
+ socket,
+ JobData,
+ error: `DMS Batch Return code was not successful: ${DMSBatchTxn.rtnCode} - ${DMSBatchTxn.sendline}`
+ });
}
} catch (error) {
// CdkBase.createLogEvent(socket, "ERROR", `Error encountered in CdkSelectedCustomer.${ error } `);
@@ -1018,13 +1024,12 @@ async function InsertDmsStartWip({ socket, redisHelpers, JobData }) {
// "rtnCode": "",
// "sendline": "",
// "groupName": "",
- "srcCo": JobData.bodyshop.cdk_configuration.srcco,
- "srcJrnl": txEnvelope.journal,
- "transID": "",
- "userID": "csr" || JobData.bodyshop.cdk_configuration.cashierid,
- "userName": "BSMS"
- },
-
+ srcCo: JobData.bodyshop.cdk_configuration.srcco,
+ srcJrnl: txEnvelope.journal,
+ transID: "",
+ userID: "csr" || JobData.bodyshop.cdk_configuration.cashierid,
+ userName: "BSMS"
+ }
});
return result;
} catch (error) {
@@ -1238,8 +1243,7 @@ async function QueryDmsErrWip({ socket, redisHelpers, JobData }) {
socket,
jobid: JobData.id,
requestPathParams: DMSTransHeader.transID,
- body: {
- }
+ body: {}
});
return result;
} catch (error) {
@@ -1252,11 +1256,13 @@ async function QueryDmsErrWip({ socket, redisHelpers, JobData }) {
}
async function DeleteDmsWip({ socket, redisHelpers, JobData }) {
-
let DMSTransHeader;
try {
- DMSTransHeader = await redisHelpers.getSessionTransactionData(socket.id, getTransactionType(JobData.id), FortellisCacheEnums.DMSTransHeader);
-
+ DMSTransHeader = await redisHelpers.getSessionTransactionData(
+ socket.id,
+ getTransactionType(JobData.id),
+ FortellisCacheEnums.DMSTransHeader
+ );
const result = await MakeFortellisCall({
...FortellisActions.PostBatchWip,
@@ -1265,8 +1271,8 @@ async function DeleteDmsWip({ socket, redisHelpers, JobData }) {
socket,
jobid: JobData.id,
body: {
- "opCode": "D",
- "transID": DMSTransHeader.transID
+ opCode: "D",
+ transID: DMSTransHeader.transID
}
});
return result;
diff --git a/server/rr/lib/index.cjs b/server/rr/lib/index.cjs
index fb12eb2c1..1e4ac1bb3 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 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;
+"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 _.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:_.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"),j=require("fast-xml-parser"),_=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 j.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("string"==typeof t){const e=t.trim();if(!e)throw new k("combinedSearch name string is empty");o.LName=e}else{if(!t||"object"!=typeof t)throw new k("combinedSearch name requires { fname/lname/mname } object or a last-name string");{const e=null!=t.fname&&String(t.fname).trim(),r=null!=t.mname&&String(t.mname).trim(),n=null!=t.lname&&String(t.lname).trim(),a=!e&&!r&&!n&&null!=t.name&&String(t.name).trim();if(e||r||n)o.FullName={FName:e||void 0,MName:r||void 0,LName:n||void 0};else{if(!a)throw new k("combinedSearch name requires string last name, or object with any of { fname, lname, mname }");o.LName=String(a)}}}}else if("nameRecId"===n){const t=N(e.nameRecId??e.custId,"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??e.stock,"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:_.default.render('\n\n {{{ApplicationArea}}}\n \n \n {{#Phone}}{{/Phone}}\n {{#PartVIN}}{{/PartVIN}}\n {{#LicenseNum}}{{/LicenseNum}}\n {{#LName}}{{/LName}}\n {{#FullName}}\n \n {{/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:_.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:_.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:_.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:_.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:_.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:_.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
new file mode 100644
index 000000000..e933f40bb
--- /dev/null
+++ b/server/rr/lib/index.mjs
@@ -0,0 +1 @@
+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 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 g(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=N(t,"DMSRecKey");return{dmsRecKey:null!=r?String(r):void 0,status:N(t,"Status"),statusCode:N(t,"StatusCode")}}function T(e){if(null==e)return;const t=String(e).toUpperCase().replace(/[^A-Z0-9 ]+/g,"");if(!t)throw new B(`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 B("ibFlag must be 'I' or 'B'");const r=e.lastName||e.customerName;if(!r)throw new B("lastName or customerName required");if("I"===t&&!e.firstName)throw new B("firstName required when ibFlag='I'");const n=b(e.customerType);if(n&&!["R","W","I"].includes(n))throw new B("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 B("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 B("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 j extends Error{constructor(e,t={}){super(e),this.name="RRTransportError",this.meta=t}}class _ extends Error{constructor(e,t={}){super(e),this.name="RRVendorStatusError",this.meta=t,this.retryable=!!t.retryable}}class B extends Error{constructor(e,t={}){super(e),this.name="RRValidationError",this.meta=t}}const q=Object.freeze({__proto__:null,RRTransportError:j,RRVendorStatusError:_,RRValidationError:B}),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:N,creationDateTime:g,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=g,"string"==typeof x?x:(x instanceof Date?x:new Date).toISOString().replace(/\.\d{3}Z$/,"Z")),bodId:N,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 j(`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 j(`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:N}=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"===N.kind){const e=i?.StatusCode;throw new _(`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:s,xml:{request:b,response:t},parsed:r};if("function"==typeof d)try{g.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"),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=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 j||e instanceof _)throw e;throw new j(`Unexpected error: ${e?.message||e}`,{cause:e})}}async combinedSearch(t,r){return this._send(function(t={},r={}){if(!t||"object"!=typeof t)throw new B("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 B("combinedSearch phone value missing");o.Phone=e}else if("license"===n){const e=y(t.license,"license");if(!e)throw new B("combinedSearch license value missing");o.LicenseNum=e}else if("vin"===n){const e=y(t.vin,"vin");if(!e)throw new B("combinedSearch vin value missing");o.PartVIN=e}else if("name"===n){const e=t.name;if("string"==typeof e){const t=e.trim();if(!t)throw new B("combinedSearch name string is empty");o.LName=t}else{if(!e||"object"!=typeof e)throw new B("combinedSearch name requires { fname/lname/mname } object or a last-name string");{const t=null!=e.fname&&String(e.fname).trim(),r=null!=e.mname&&String(e.mname).trim(),n=null!=e.lname&&String(e.lname).trim(),a=!t&&!r&&!n&&null!=e.name&&String(e.name).trim();if(t||r||n)o.FullName={FName:t||void 0,MName:r||void 0,LName:n||void 0};else{if(!a)throw new B("combinedSearch name requires string last name, or object with any of { fname, lname, mname }");o.LName=String(a)}}}}else if("nameRecId"===n){const e=y(t.nameRecId??t.custId,"custId");if(!e)throw new B("combinedSearch nameRecId value missing");o.NameRecId=e}else{if("stkNo"!==n)throw new B(`Unsupported CombinedSearch kind: ${t.kind}`);{const e=y(t.stkNo??t.stock,"stkNo");if(!e)throw new B("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 B("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}}\n \n {{/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 B("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:g,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 B("routing.dealerNumber required");if(!t.nameRecId)throw new B("nameRecId or customerId required");const n=b(t.ibFlag);if("I"!==n&&"B"!==n)throw new B("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:g,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 B("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 B("insertServiceVehicle: vin is required");const r=e.vehicleServInfo?.customerNo;if(!r)throw new B("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 B("routing.dealerNumber required");if(!t?.customerNo)throw new B("customerNo (CustNo) required");if(!t?.departmentType)throw new B("departmentType (DeptType) required");if(!t?.vin)throw new B("vin (Vin) required");if(!t?.outsdRoNo)throw new B("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 B("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 B("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 B("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 B("routing.dealerNumber required");if(!t?.finalUpdate)throw new B("finalUpdate (FinalUpdate) required");if(!["Y","N"].includes(t.finalUpdate))throw new B("finalUpdate must be 'Y' or 'N'");if(!t?.outsdRoNo)throw new B(" 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 B("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 B("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 B("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 B("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
new file mode 100644
index 000000000..9dcc48a23
--- /dev/null
+++ b/server/rr/lib/types.cjs
@@ -0,0 +1,3 @@
+'use strict';
+// CJS access point for JSDoc typedefs
+module.exports = require('./types.js');
diff --git a/server/rr/rr-logger-event.js b/server/rr/rr-logger-event.js
new file mode 100644
index 000000000..aaa18b985
--- /dev/null
+++ b/server/rr/rr-logger-event.js
@@ -0,0 +1,43 @@
+// File: server/rr/rr-logger-event.js
+// Fortellis-style log helper for RR flows.
+// Usage: CreateRRLogEvent(socket, "DEBUG"|"INFO"|"WARN"|"ERROR", message, details?)
+
+const RRLogger = require("../rr/rr-logger");
+
+// Normalize level to upper + provide console + socket event with coherent payload
+function CreateRRLogEvent(socket, level = "DEBUG", message = "", details = {}) {
+ const lvl = String(level || "DEBUG").toUpperCase();
+ const ts = Date.now();
+
+ // Console (uses existing RRLogger, which also emits "RR:LOG" to sockets for live tail)
+ try {
+ const log = RRLogger(socket);
+ const fn = lvl === "ERROR" ? "error" : lvl === "WARN" ? "warn" : lvl === "INFO" ? "info" : "debug";
+ log(fn, message, { ts, ...safeJson(details) });
+ } catch {
+ /* ignore console/log failures */
+ }
+
+ // Structured RR event for FE debug panel (parity with Fortellis' CreateFortellisLogEvent)
+ try {
+ socket?.emit?.("rr-log-event", {
+ level: lvl,
+ message,
+ ts,
+ ...safeJson(details)
+ });
+ } catch {
+ /* ignore socket emit failures */
+ }
+}
+
+// Best-effort ensure details are JSON-safe (avoid circular / BigInt)
+function safeJson(obj) {
+ try {
+ return JSON.parse(JSON.stringify(obj ?? {}));
+ } catch {
+ return { _unsafe: true };
+ }
+}
+
+module.exports = CreateRRLogEvent;
diff --git a/server/rr/rr-logger.js b/server/rr/rr-logger.js
index 3efb86ccc..ec9384ac2 100644
--- a/server/rr/rr-logger.js
+++ b/server/rr/rr-logger.js
@@ -1,66 +1,56 @@
-const util = require("util");
-const appLogger = require("../utils/logger");
+// File: server/rr/rr-logger.js
+// Console logger for RR flows with safe JSON. No socket emission by default.
-function RRLogger(socket, baseCtx = {}) {
- const levels = new Set(["error", "warn", "info", "http", "verbose", "debug", "silly"]);
+const baseLogger = require("../utils/logger");
- const safeString = (v) => {
- if (v instanceof Error) return v.message;
- if (typeof v === "string") return v;
+function RRLogger(_socket, defaults = {}) {
+ function safeSerialize(value) {
try {
- return JSON.stringify(v);
- } catch {
- return util.inspect(v, { depth: 2, maxArrayLength: 50 });
- }
- };
-
- return function log(levelOrMsg, msgOrCtx, ctx) {
- let level = "info";
- let message = undefined;
- let meta = {};
-
- if (typeof levelOrMsg === "string" && levels.has(levelOrMsg)) {
- level = levelOrMsg;
- message = msgOrCtx;
- meta = ctx || {};
- } else {
- message = levelOrMsg;
- meta = msgOrCtx || {};
- }
-
- // Prepare console line + metadata
- const emitError = message instanceof Error;
- if (emitError) {
- meta.err = {
- name: message.name,
- message: message.message,
- stack: message.stack
- };
- message = message.message;
- if (level === "info") level = "error";
- }
-
- const messageString = safeString(message);
- const line = `[RR] ${new Date().toISOString()} [${String(level).toUpperCase()}] ${messageString}`;
- const loggerFn = appLogger?.logger?.[level] || appLogger?.logger?.info || ((...args) => console.log(...args));
-
- loggerFn(line, { ...baseCtx, ...meta });
-
- // Always emit a STRING for `message` to sockets to avoid React crashes
- // If the original message was an object, include it in `details`
- const details = message && typeof message !== "string" && !emitError ? message : undefined;
-
- try {
- socket?.emit?.("rr-log-event", {
- level,
- message: messageString, // <-- normalized string for UI
- ctx: { ...baseCtx, ...meta },
- ...(details ? { details } : {}),
- ts: Date.now()
+ const seen = new WeakSet();
+ return JSON.stringify(value, (key, val) => {
+ if (typeof val === "bigint") return val.toString();
+ if (val instanceof Error) return { name: val.name, message: val.message, stack: val.stack };
+ if (typeof val === "function") return undefined;
+ if (typeof val === "object" && val !== null) {
+ if (seen.has(val)) return "[Circular]";
+ seen.add(val);
+ if (val instanceof Date) return val.toISOString();
+ if (val instanceof Map) return Object.fromEntries(val);
+ if (val instanceof Set) return Array.from(val);
+ if (typeof Buffer !== "undefined" && Buffer.isBuffer?.(val)) return ``;
+ }
+ return val;
});
} catch {
- /* ignore socket emission errors */
+ try {
+ return String(value);
+ } catch {
+ return "[Unserializable]";
+ }
}
+ }
+
+ return function log(level = "info", message = "", ctx = {}) {
+ const lvl = String(level || "info").toLowerCase();
+ const iso = new Date().toISOString();
+
+ const msgStr = typeof message === "string" ? message : safeSerialize(message);
+ const mergedCtx = ctx && typeof ctx === "object" ? { ...defaults, ...ctx } : { ...defaults };
+
+ const ctxStr = Object.keys(mergedCtx || {}).length ? ` ${safeSerialize(mergedCtx)}` : "";
+ const line = `[RR] ${iso} [${lvl.toUpperCase()}] ${msgStr}${ctxStr}`;
+
+ const logger = baseLogger?.logger || baseLogger || console;
+ const fn = (logger[lvl] || logger.log || logger.info || console[lvl] || console.log).bind(logger);
+
+ try {
+ fn(line);
+ } catch {
+ try {
+ console.log(line);
+ } catch {}
+ }
+ // INTENTIONALLY no socket emit here to avoid FE duplicates.
};
}
diff --git a/server/rr/rr-lookup.js b/server/rr/rr-lookup.js
index c9ed36287..b439f9e13 100644
--- a/server/rr/rr-lookup.js
+++ b/server/rr/rr-lookup.js
@@ -41,76 +41,90 @@ function buildClientAndOpts(bodyshop) {
*/
function toCombinedSearchPayload(args = {}) {
const q = { ...args };
- let kind = (q.kind || "").toString().trim().toLowerCase();
+ // Decide kind if not provided
+ let kind = (q.kind || "").toString().trim();
if (!kind) {
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.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.nameRecId || q.custId) kind = "nameRecId";
+ else if (typeof q.name === "string" || (q.name && (q.name.fname || q.name.lname || q.name.mname || q.name.name))) {
+ kind = "name";
+ } else if (q.stkNo || q.stock) kind = "stkNo";
}
- // Map loose aliases into the RR builder’s expected fields
- const payload = { maxResults: q.maxResults || q.maxRecs || 50, kind };
+ const payload = {
+ maxResults: q.maxResults || q.maxRecs || 50,
+ kind
+ };
- switch (kind) {
+ switch ((kind || "").toLowerCase()) {
case "phone":
payload.kind = "phone";
- payload.phone = q.phone;
+ payload.phone = String(q.phone ?? "").trim();
break;
+
case "license":
payload.kind = "license";
- payload.license = q.license;
+ payload.license = String(q.license ?? "").trim();
break;
+
case "vin":
payload.kind = "vin";
- payload.vin = q.vin;
+ payload.vin = String(q.vin ?? "").trim();
break;
case "namerecid":
payload.kind = "nameRecId";
- payload.nameRecId = q.nameRecId || q.custId;
+ payload.nameRecId = String(q.nameRecId ?? q.custId ?? "").trim();
+ break;
+
+ case "stkno":
+ case "stkNo":
+ payload.kind = "stkNo";
+ payload.stkNo = String(q.stkNo ?? q.stock ?? "").trim();
break;
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
+ const n = q.name;
+ // STRING => last-name-only intent
+ if (typeof n === "string") {
+ const last = n.trim();
+ if (last) payload.name = { name: last }; //
+ break;
+ }
+ // OBJECT => always treat as FullName (even if only one of the parts is present)
+ const fname = n?.fname && String(n.fname).trim();
+ const lname = n?.lname && String(n.lname).trim();
+ const mname = n?.mname && String(n.mname).trim();
+ const lastOnly = n?.name && String(n.name).trim();
+
+ if (fname || lname || mname) {
+ const full = {};
+ if (fname) full.fname = fname;
+ if (mname) full.mname = mname;
+ if (lname) full.lname = lname;
+ payload.name = full; // will render
+ } else if (lastOnly) {
+ payload.name = { name: lastOnly }; // explicit last-only
}
break;
}
- case "stkno":
- payload.kind = "stkNo";
- payload.stkNo = q.stkNo || q.stock;
- break;
+
default:
- // Let the RR builder throw the canonical “Unsupported CombinedSearch kind”
- payload.kind = q.kind; // may be undefined; RR lib will validate
+ payload.kind = kind;
}
- // 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 || "ANY";
payload.model = q.model || "ANY";
payload.year = q.year || "ANY";
}
+ if (q.vin && payload.kind !== "vin") payload.vin = String(q.vin).trim();
+ if (q.phone && payload.kind !== "phone") payload.phone = String(q.phone).trim();
+ if (q.license && payload.kind !== "license") payload.license = String(q.license).trim();
+
return payload;
}
/**
diff --git a/server/web-sockets/rr-register-socket-events.js b/server/web-sockets/rr-register-socket-events.js
index 1a427a100..7cb3e0e20 100644
--- a/server/web-sockets/rr-register-socket-events.js
+++ b/server/web-sockets/rr-register-socket-events.js
@@ -1,7 +1,7 @@
// File: server/rr/rr-register-socket-events.js
-// PATCH: add helper + modify rr-export-job customer preselect logic
+// RR events aligned to Fortellis flow with Fortellis-style logging via CreateRRLogEvent
-const RRLogger = require("../rr/rr-logger");
+const CreateRRLogEvent = require("../rr/rr-logger-event");
const { rrCombinedSearch, rrGetAdvisors, rrGetParts } = require("../rr/rr-lookup");
const { QueryJobData } = require("../rr/rr-job-helpers");
const { exportJobToRR } = require("../rr/rr-job-export");
@@ -11,8 +11,8 @@ const { createRRCustomer } = require("../rr/rr-customers");
const { GraphQLClient } = require("graphql-request");
const queries = require("../graphql-client/queries");
-// Use the same namespacing/TTL approach as Fortellis
-const { getTransactionType, defaultFortellisTTL } = require("../fortellis/fortellis-helpers");
+const getTransactionType = (jobid) => `rr:${jobid}`;
+const defaultRRTTL = 60 * 60;
// ---------------- cache keys (RR) ----------------
const RRCacheEnums = {
@@ -20,12 +20,10 @@ const RRCacheEnums = {
JobData: "RR.JobData",
SelectedCustomer: "RR.SelectedCustomer",
AdvisorNo: "RR.AdvisorNo",
- // Vehicle keys reserved for future multi-vehicle UX parity
VINCandidates: "RR.VINCandidates",
SelectedVin: "RR.SelectedVin",
ExportResult: "RR.ExportResult"
};
-const defaultRRTTL = defaultFortellisTTL || 60 * 60; // fallback 1h
// ---------------- utils ----------------
function resolveJobId(explicit, payload, job) {
@@ -34,31 +32,40 @@ function resolveJobId(explicit, payload, job) {
const digitsOnly = (s) => String(s || "").replace(/\D/g, "");
const makeVehicleSearchPayloadFromJob = (job) => {
- const vin = job?.v_vin || job?.vehicle?.vin || job?.vehicle_vin || job?.vin;
- if (vin) return { kind: "vin", vin: String(vin).trim() };
- const plate = job?.plate_no || job?.vehicle?.plate || job?.vehicle_plate || job?.plate;
- if (plate) return { kind: "license", license: String(plate).trim() };
+ const vin = job?.v_vin;
+ if (vin) return { kind: "vin", vin: String(vin).trim(), maxResults: 50 };
+ const plate = job?.plate_no;
+ if (plate) return { kind: "license", license: String(plate).trim(), maxResults: 50 };
return null;
};
const makeCustomerSearchPayloadFromJob = (job) => {
const phone = job?.ownr_ph1 || job?.customer?.mobile || job?.customer?.home_phone || job?.customer?.phone;
const d = digitsOnly(phone);
- if (d.length >= 7) return { kind: "phone", phone: d };
+ if (d.length >= 7) return { kind: "phone", phone: d, maxResults: 50 };
- const lastName = job?.ownr_ln || job?.customer?.last_name || job?.customer?.lastName;
- const company = job?.ownr_co_nm || job?.customer?.company_name || job?.customer?.companyName;
- const lnOrCompany = lastName || company;
- if (lnOrCompany) return { kind: "name", name: { name: String(lnOrCompany).trim() } };
+ const firstName = job?.ownr_fn;
+ const lastName = job?.ownr_ln;
+ const company = job?.ownr_co_nm;
- const vin = job?.v_vin || job?.vehicle?.vin || job?.vehicle_vin || job?.vin;
- if (vin) return { kind: "vin", vin: String(vin).trim() };
+ if (firstName || lastName) {
+ const nameObj = {};
+ if (firstName) nameObj.fname = String(firstName).trim();
+ if (lastName) nameObj.lname = String(lastName).trim();
+ return { kind: "name", name: nameObj, maxResults: 50 };
+ }
+ if (company) {
+ return { kind: "name", name: { name: String(company).trim() }, maxResults: 50 };
+ }
+
+ const vin = job?.v_vin || job?.vehicle?.vin || job?.vin;
+ if (vin) return { kind: "vin", vin: String(vin).trim(), maxResults: 50 };
return null;
};
-// Normalize to the RR table shape expected by FE (custNo + name)
-const normalizeCustomerCandidates = (res) => {
+// Normalize candidates FE expects: { custNo, name } and flag vinOwner when sourced via VIN
+const normalizeCustomerCandidates = (res, { markVinOwner = false } = {}) => {
const blocks = Array.isArray(res?.data) ? res.data : Array.isArray(res) ? res : [];
const out = [];
for (const blk of blocks) {
@@ -68,12 +75,16 @@ const normalizeCustomerCandidates = (res) => {
const nci = blk?.NameContactId;
const ind = nci?.NameId?.IndName;
const bus = nci?.NameId?.BusName;
- const personal = [ind?.FName, ind?.LName].filter(Boolean).join(" ").trim();
- const company = bus?.CompanyName;
+ const personal = [ind?.FirstName || ind?.FName, ind?.LastName || ind?.LName].filter(Boolean).join(" ").trim();
+ const company = bus?.CompanyName || bus?.BName;
const name = (personal || company || "").trim();
for (const custNo of custNos) {
- out.push({ custNo: String(custNo), name: name || `Customer ${custNo}` });
+ out.push({
+ custNo: String(custNo),
+ name: name || `Customer ${custNo}`,
+ ...(markVinOwner ? { vinOwner: true } : {})
+ });
}
}
const seen = new Set();
@@ -90,7 +101,7 @@ async function getSessionOrSocket(redisHelpers, socket) {
try {
sess = await redisHelpers.getSessionData(socket.id);
} catch {
- /* ignore */
+ //
}
const bodyshopId = sess?.bodyshopId ?? socket.bodyshopId;
const email = sess?.email ?? socket.user?.email;
@@ -101,14 +112,11 @@ async function getSessionOrSocket(redisHelpers, socket) {
async function getBodyshopForSocket({ bodyshopId, socket }) {
const endpoint = process.env.GRAPHQL_ENDPOINT;
if (!endpoint) throw new Error("GRAPHQL_ENDPOINT not configured");
-
const token = (socket?.data && socket.data.authToken) || (socket?.handshake?.auth && socket.handshake.auth.token);
-
const client = new GraphQLClient(endpoint, {});
const res = await client
.setHeaders({ Authorization: `Bearer ${token}` })
.request(queries.GET_BODYSHOP_BY_ID, { id: bodyshopId });
-
const bodyshop = res?.bodyshops_by_pk;
if (!bodyshop) throw new Error(`Bodyshop not found: ${bodyshopId}`);
return bodyshop;
@@ -117,6 +125,7 @@ async function getBodyshopForSocket({ bodyshopId, socket }) {
function readAdvisorNo(payload, cached) {
const v =
(payload?.txEnvelope?.advisorNo != null && String(payload.txEnvelope.advisorNo)) ||
+ (payload?.txEnvelope?.advNo != null && String(payload.txEnvelope.advNo)) ||
(payload?.advisorNo != null && String(payload.advisorNo)) ||
(payload?.advNo != null && String(payload.advNo)) ||
(cached != null && String(cached)) ||
@@ -124,264 +133,212 @@ function readAdvisorNo(payload, cached) {
return v && v.trim() !== "" ? v : null;
}
-// ---------------- register handlers ----------------
-function registerRREvents({ socket, redisHelpers }) {
- const log = RRLogger(socket);
+// VIN + Name merge; keep vinOwner flag if any source came from VIN
+async function rrMultiCustomerSearch(bodyshop, job, socket) {
+ const queries = [];
+ const vehQ = makeVehicleSearchPayloadFromJob(job);
+ if (vehQ) queries.push({ q: vehQ, fromVin: vehQ.kind === "vin" });
+ const custQ = makeCustomerSearchPayloadFromJob(job);
+ if (custQ) queries.push({ q: custQ, fromVin: false });
- // --- NEW helper: run name + vin searches and merge ---
- async function rrMultiCustomerSearch(bodyshop, job) {
- const queries = [];
+ if (!queries.length) return [];
- // 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
- });
+ const all = [];
+ for (const { q, fromVin } of queries) {
+ try {
+ CreateRRLogEvent(socket, "DEBUG", `{RR-SEARCH} Executing ${q.kind} query`, { q });
+ const res = await rrCombinedSearch(bodyshop, q);
+ const norm = normalizeCustomerCandidates(res, { markVinOwner: !!fromVin });
+ all.push(...norm);
+ } catch (e) {
+ CreateRRLogEvent(socket, "WARN", "Multi-search subquery failed", { kind: q.kind, error: e.message });
}
-
- 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) ---------
+ const byCust = new Map();
+ for (const c of all) {
+ const key = c?.custNo && String(c.custNo).trim();
+ if (!key) continue;
+ const prev = byCust.get(key);
+ if (!prev) byCust.set(key, c);
+ else if (c.vinOwner && !prev.vinOwner) byCust.set(key, { ...prev, vinOwner: true });
+ }
+ return Array.from(byCust.values());
+}
+
+// ---------------- register handlers ----------------
+function registerRREvents({ socket, redisHelpers }) {
+ // ---------- Lookup passthrough ----------
socket.on("rr-lookup-combined", async ({ jobid, params } = {}, cb) => {
try {
const { bodyshopId } = await getSessionOrSocket(redisHelpers, socket);
const bodyshop = await getBodyshopForSocket({ bodyshopId, socket });
-
+ CreateRRLogEvent(socket, "DEBUG", "rr-lookup-combined: begin", { jobid, params });
const res = await rrCombinedSearch(bodyshop, params || {});
const normalized = normalizeCustomerCandidates(res);
-
const rid = resolveJobId(jobid, { jobid }, null);
cb?.({ jobid: rid, data: normalized });
-
- // FE expects { custNo, name }
socket.emit("rr-select-customer", normalized);
+ CreateRRLogEvent(socket, "DEBUG", "rr-lookup-combined: emitted rr-select-customer", { count: normalized.length });
} catch (e) {
- log("error", `RR combined lookup error: ${e.message}`, { jobid });
+ CreateRRLogEvent(socket, "ERROR", "RR combined lookup error", { error: e.message, jobid });
cb?.({ jobid, error: e.message });
}
});
- // --------- Advisors ---------
+ // ---------- Advisors ----------
socket.on("rr-get-advisors", async (args = {}, ack) => {
try {
const { bodyshopId } = await getSessionOrSocket(redisHelpers, socket);
const bodyshop = await getBodyshopForSocket({ bodyshopId, socket });
+ CreateRRLogEvent(socket, "DEBUG", "rr-get-advisors: begin", { args });
const res = await rrGetAdvisors(bodyshop, args);
ack?.({ ok: true, result: res });
socket.emit("rr-get-advisors:result", res);
+ CreateRRLogEvent(socket, "DEBUG", "rr-get-advisors: success", {
+ count: Array.isArray(res) ? res.length : undefined
+ });
} catch (err) {
- log("error", err?.message || "get advisors failed", { err });
+ CreateRRLogEvent(socket, "ERROR", "rr-get-advisors: failed", { error: err?.message });
ack?.({ ok: false, error: err?.message || "get advisors failed" });
}
});
- // --------- Parts ---------
+ // ---------- Parts ----------
socket.on("rr-get-parts", async (args = {}, ack) => {
try {
const { bodyshopId } = await getSessionOrSocket(redisHelpers, socket);
const bodyshop = await getBodyshopForSocket({ bodyshopId, socket });
+ CreateRRLogEvent(socket, "DEBUG", "rr-get-parts: begin", { args });
const res = await rrGetParts(bodyshop, args);
ack?.({ ok: true, result: res });
socket.emit("rr-get-parts:result", res);
+ CreateRRLogEvent(socket, "DEBUG", "rr-get-parts: success", {
+ count: Array.isArray(res) ? res.length : undefined
+ });
} catch (err) {
- log("error", err?.message || "get parts failed", { err });
+ CreateRRLogEvent(socket, "ERROR", "rr-get-parts: failed", { error: err?.message });
ack?.({ ok: false, error: err?.message || "get parts failed" });
}
});
- // --------- Persist customer selection / create-intent (table-first UX) ---------
- socket.on("rr-selected-customer", async ({ jobId, custNo, create } = {}, ack) => {
- const ns = getTransactionType(jobId || "unknown");
+ // ================= Fortellis-style two-step export =================
+ // 1) Stage export -> search -> emit rr-select-customer
+ socket.on("rr-export-job", async ({ jobid, jobId, txEnvelope } = {}) => {
+ const rid = resolveJobId(jobid || jobId, { jobId, jobid }, null);
try {
- const { bodyshopId } = await getSessionOrSocket(redisHelpers, socket);
- const bodyshop = await getBodyshopForSocket({ bodyshopId, socket });
- if (!jobId) throw new Error("jobId required");
+ if (!rid) throw new Error("RR export: jobid required");
+ CreateRRLogEvent(socket, "DEBUG", `{1} Received RR export request`, { jobid: rid });
- // If caller passed a selection, just persist it.
- if (custNo && create !== true) {
+ await redisHelpers.setSessionTransactionData(
+ socket.id,
+ getTransactionType(rid),
+ RRCacheEnums.txEnvelope,
+ txEnvelope || {},
+ defaultRRTTL
+ );
+ CreateRRLogEvent(socket, "DEBUG", `{1.1} Cached txEnvelope`, { hasTxEnvelope: !!txEnvelope });
+
+ const job = await QueryJobData({ redisHelpers }, rid);
+ await redisHelpers.setSessionTransactionData(
+ socket.id,
+ getTransactionType(rid),
+ RRCacheEnums.JobData,
+ job,
+ defaultRRTTL
+ );
+ CreateRRLogEvent(socket, "DEBUG", `{1.2} Cached JobData`, { vin: job?.v_vin, ro: job?.ro_number });
+
+ const adv = readAdvisorNo(
+ { txEnvelope },
+ await redisHelpers.getSessionTransactionData(socket.id, getTransactionType(rid), RRCacheEnums.AdvisorNo)
+ );
+ if (adv) {
await redisHelpers.setSessionTransactionData(
socket.id,
- ns,
- RRCacheEnums.SelectedCustomer,
- String(custNo),
+ getTransactionType(rid),
+ RRCacheEnums.AdvisorNo,
+ String(adv),
defaultRRTTL
);
- log("info", "rr-selected-customer", { jobId, custNo: String(custNo) });
- return ack?.({ ok: true });
+ CreateRRLogEvent(socket, "DEBUG", `{1.3} Cached advisorNo`, { advisorNo: String(adv) });
}
- // No custNo (or create: true) => create immediately from JobData (Fortellis parity)
- const job = await QueryJobData({ redisHelpers }, jobId);
- const created = await createRRCustomer({ bodyshop, job, socket });
- const newCustNo = String(
- created?.custNo || created?.customerNo || created?.CustomerNo || created?.dmsRecKey || ""
- );
- if (!newCustNo) throw new Error("RR create customer returned no custNo");
+ const { bodyshopId } = await getSessionOrSocket(redisHelpers, socket);
+ const bodyshop = await getBodyshopForSocket({ bodyshopId, socket });
+
+ CreateRRLogEvent(socket, "DEBUG", `{2} Running multi-search (VIN + Name)`);
+ const candidates = await rrMultiCustomerSearch(bodyshop, job, socket);
+
+ socket.emit("rr-select-customer", candidates);
+ CreateRRLogEvent(socket, "DEBUG", `{2.1} Emitted rr-select-customer`, {
+ count: candidates.length,
+ anyVinOwner: candidates.some((c) => c.vinOwner)
+ });
+ } catch (error) {
+ CreateRRLogEvent(socket, "ERROR", `Error during RR export (prepare)`, {
+ error: error.message,
+ stack: error.stack,
+ jobid: rid
+ });
+ try {
+ socket.emit("export-failed", { vendor: "rr", jobId: rid, error: error.message });
+ } catch {
+ //
+ }
+ }
+ });
+
+ // 2) Selection (or create) -> export
+ socket.on("rr-selected-customer", async ({ jobid, jobId, selectedCustomerId, custNo, create } = {}, ack) => {
+ const rid = resolveJobId(jobid || jobId, { jobid, jobId }, null);
+ try {
+ if (!rid) throw new Error("jobid required");
+ CreateRRLogEvent(socket, "DEBUG", `{3} rr-selected-customer`, {
+ jobid: rid,
+ custNo,
+ selectedCustomerId,
+ create: !!create
+ });
+
+ const ns = getTransactionType(rid);
+ let selectedCustNo =
+ (custNo && String(custNo)) ||
+ (selectedCustomerId && String(selectedCustomerId)) ||
+ (await redisHelpers.getSessionTransactionData(socket.id, ns, RRCacheEnums.SelectedCustomer));
+
+ const job = await redisHelpers.getSessionTransactionData(socket.id, ns, RRCacheEnums.JobData);
+ const txEnvelope = (await redisHelpers.getSessionTransactionData(socket.id, ns, RRCacheEnums.txEnvelope)) || {};
+ if (!job) throw new Error("Staged JobData not found (run rr-export-job first).");
+
+ const { bodyshopId } = await getSessionOrSocket(redisHelpers, socket);
+ const bodyshop = await getBodyshopForSocket({ bodyshopId, socket });
+
+ if (create === true || !selectedCustNo) {
+ CreateRRLogEvent(socket, "DEBUG", `{3.1} Creating RR customer`);
+ const created = await createRRCustomer({ bodyshop, job, socket });
+ selectedCustNo = String(
+ created?.custNo || created?.customerNo || created?.CustomerNo || created?.dmsRecKey || ""
+ );
+ if (!selectedCustNo) throw new Error("RR create customer returned no custNo");
+ CreateRRLogEvent(socket, "DEBUG", `{3.2} Created customer`, { custNo: selectedCustNo });
+ }
await redisHelpers.setSessionTransactionData(
socket.id,
ns,
RRCacheEnums.SelectedCustomer,
- newCustNo,
+ String(selectedCustNo),
defaultRRTTL
);
-
- log("info", "rr-create-customer:success", { jobId, custNo: newCustNo });
- return ack?.({ ok: true, custNo: newCustNo });
- } catch (err) {
- log("error", err?.message || "select/create customer failed", { err, jobId });
- return ack?.({ ok: false, error: err?.message || "select/create customer failed" });
- }
- });
-
- // --------- Optional explicit create-customer (form) ---------
- socket.on("rr-create-customer", async ({ jobId, fields } = {}, ack) => {
- const ns = getTransactionType(jobId || "unknown");
- try {
- const { bodyshopId } = await getSessionOrSocket(redisHelpers, socket);
- const bodyshop = await getBodyshopForSocket({ bodyshopId, socket });
-
- if (!jobId) throw new Error("jobId required");
- const job = await QueryJobData({ redisHelpers }, jobId);
-
- const created = await createRRCustomer({ bodyshop, job, overrides: fields || {}, socket });
- const custNo = String(created?.custNo || created?.customerNo || created?.CustomerNo || created?.dmsRecKey || "");
- if (!custNo) throw new Error("RR create customer returned no custNo");
-
- await redisHelpers.setSessionTransactionData(socket.id, ns, RRCacheEnums.SelectedCustomer, custNo, defaultRRTTL);
- await redisHelpers.setSessionTransactionData(socket.id, ns, "RR.CreateCustomerIntent", false, defaultRRTTL);
-
- log("info", "rr-create-customer:success", { jobId, custNo });
- socket.emit("rr-customer-created", { custNo });
- ack?.({ ok: true, custNo });
- } catch (err) {
- log("error", err?.message || "create customer failed", { err, jobId });
- ack?.({ ok: false, error: err?.message || "create customer failed" });
- }
- });
-
- // --------- Vehicle selection hooks (reserved for future parity) ---------
- socket.on("rr-selected-vehicle", async ({ jobId, vin } = {}, ack) => {
- const ns = getTransactionType(jobId || "unknown");
- try {
- await getSessionOrSocket(redisHelpers, socket);
- if (!vin) throw new Error("vin required");
- await redisHelpers.setSessionTransactionData(socket.id, ns, RRCacheEnums.SelectedVin, String(vin), defaultRRTTL);
- log("info", "rr-selected-vehicle", { jobId, vin: String(vin) });
- ack?.({ ok: true });
- } catch (err) {
- log("error", err?.message || "select vehicle failed", { err, jobId });
- ack?.({ ok: false, error: err?.message || "select vehicle failed" });
- }
- });
-
- socket.on("rr-create-vehicle", async ({ jobId, vehicle } = {}, ack) => {
- const ns = getTransactionType(jobId || "unknown");
- try {
- await getSessionOrSocket(redisHelpers, socket);
- if (!vehicle?.vin) throw new Error("vehicle.vin required");
- await redisHelpers.setSessionTransactionData(
- socket.id,
- ns,
- RRCacheEnums.SelectedVin,
- String(vehicle.vin),
- defaultRRTTL
- );
- log("info", "rr-create-vehicle", { jobId, vin: String(vehicle.vin) });
- ack?.({ ok: true });
- } catch (err) {
- log("error", err?.message || "create vehicle failed", { err, jobId });
- ack?.({ ok: false, error: err?.message || "create vehicle failed" });
- }
- });
-
- // --------- Main export (Fortellis-style staging) ---------
- socket.on("rr-export-job", async (payload = {}) => {
- const _log = RRLogger(socket, { ns: "rr" });
- try {
- // (existing preamble unchanged)
- let job = payload.job || payload.txEnvelope?.job;
- const jobId = payload.jobId || payload.jobid || payload.txEnvelope?.jobId || job?.id;
- if (!job) {
- if (!jobId) throw new Error("RR export: job or jobId required");
- job = await QueryJobData({ redisHelpers }, jobId);
- }
- const ns = getTransactionType(job.id);
-
- await redisHelpers.setSessionTransactionData(
- socket.id,
- ns,
- RRCacheEnums.txEnvelope,
- payload.txEnvelope || {},
- defaultRRTTL
- );
- await redisHelpers.setSessionTransactionData(socket.id, ns, RRCacheEnums.JobData, job, defaultRRTTL);
-
- let bodyshopId = payload.bodyshopId || payload.bodyshopid || payload.bodyshopUUID || job?.bodyshop?.id;
- if (!bodyshopId) {
- const sess = await getSessionOrSocket(redisHelpers, socket);
- bodyshopId = sess.bodyshopId;
- }
- if (!bodyshopId) throw new Error("RR export: bodyshopId required");
-
- const bodyshop =
- job?.bodyshop && (job.bodyshop.rr_dealerid || job.bodyshop.rr_configuration)
- ? job.bodyshop
- : await getBodyshopForSocket({ bodyshopId, socket });
+ CreateRRLogEvent(socket, "DEBUG", `{3.3} Cached selected customer`, { custNo: String(selectedCustNo) });
const cachedAdvisor = await redisHelpers.getSessionTransactionData(socket.id, ns, RRCacheEnums.AdvisorNo);
- const advisorNo = readAdvisorNo(payload, cachedAdvisor);
+ const advisorNo = readAdvisorNo({ txEnvelope }, cachedAdvisor);
if (!advisorNo) {
- socket.emit("export-failed", { vendor: "rr", jobId, error: "Advisor is required (advisorNo)." });
- return;
+ CreateRRLogEvent(socket, "ERROR", `Advisor is required (advisorNo)`);
+ socket.emit("export-failed", { vendor: "rr", jobId: rid, error: "Advisor is required (advisorNo)." });
+ return ack?.({ ok: false, error: "Advisor is required (advisorNo)." });
}
await redisHelpers.setSessionTransactionData(
socket.id,
@@ -391,141 +348,29 @@ function registerRREvents({ socket, redisHelpers }) {
defaultRRTTL
);
- let selectedCust = null;
- if (payload.selectedCustomer) {
- if (typeof payload.selectedCustomer === "object" && payload.selectedCustomer.custNo) {
- selectedCust = { custNo: String(payload.selectedCustomer.custNo) };
- } else if (typeof payload.selectedCustomer === "string" || typeof payload.selectedCustomer === "number") {
- selectedCust = { custNo: String(payload.selectedCustomer) };
- }
- if (selectedCust?.custNo) {
- await redisHelpers.setSessionTransactionData(
- socket.id,
- ns,
- RRCacheEnums.SelectedCustomer,
- selectedCust.custNo,
- defaultRRTTL
- );
- }
- }
- if (!selectedCust) {
- const cached = await redisHelpers.getSessionTransactionData(socket.id, ns, RRCacheEnums.SelectedCustomer);
- if (cached) selectedCust = { custNo: String(cached) };
- }
-
- const forceCreate = payload.forceCreate === true;
- const autoCreateOnNoMatch = payload.autoCreateOnNoMatch !== false;
-
- // --- MODIFIED: multi-search (name + vin) when beginning selection ---
- if (!selectedCust && !forceCreate) {
- const mergedCandidates = await rrMultiCustomerSearch(bodyshop, job);
-
- 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 (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;
- 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(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()
- });
- return;
- }
- }
- }
- }
-
- if (!selectedCust?.custNo) throw new Error("RR export: selected customer missing custNo");
-
- // (rest of existing export logic unchanged)
+ CreateRRLogEvent(socket, "DEBUG", `{4} Performing RR export`);
const result = await exportJobToRR({
bodyshop,
job,
- selectedCustomer: selectedCust,
+ selectedCustomer: { custNo: String(selectedCustNo) },
advisorNo: String(advisorNo),
- existing: payload.existing,
+ existing: txEnvelope?.existing,
socket
});
if (result?.success) {
- socket.emit("export-success", { vendor: "rr", jobId, roStatus: result.roStatus });
+ CreateRRLogEvent(socket, "DEBUG", `{5} Export success`, { roStatus: result.roStatus });
+ socket.emit("export-success", { vendor: "rr", jobId: rid, roStatus: result.roStatus });
+ ack?.({ ok: true, result });
} else {
+ CreateRRLogEvent(socket, "ERROR", `Export failed`, { roStatus: result?.roStatus, error: result?.error });
socket.emit("export-failed", {
vendor: "rr",
- jobId,
+ jobId: rid,
roStatus: result?.roStatus,
error: result?.error || "RR export failed"
});
+ ack?.({ ok: false, error: result?.error || "RR export failed", result });
}
await redisHelpers.setSessionTransactionData(
@@ -535,24 +380,32 @@ function registerRREvents({ socket, redisHelpers }) {
result || {},
defaultRRTTL
);
- socket.emit("rr-export-job:result", { jobId, bodyshopId, result });
+ socket.emit("rr-export-job:result", { jobId: rid, bodyshopId, result });
} catch (error) {
- const jobId = payload.jobId || payload.jobid || payload.txEnvelope?.jobId || payload?.job?.id;
- _log("error", `Error during RR export: ${error.message}`, { jobId, stack: error.stack });
+ CreateRRLogEvent(socket, "ERROR", `Error during RR export (selected-customer)`, {
+ error: error.message,
+ stack: error.stack,
+ jobid: rid
+ });
try {
- socket.emit("export-failed", { vendor: "rr", jobId, error: error.message });
- } catch {}
+ socket.emit("export-failed", { vendor: "rr", jobId: rid, error: error.message });
+ } catch {
+ //
+ }
+ ack?.({ ok: false, error: error.message });
}
});
- // --------- Allocations (reuse CDK calculator) ---------
+ // ---------- Allocations (parity) ----------
socket.on("rr-calculate-allocations", async (jobid, cb) => {
try {
+ CreateRRLogEvent(socket, "DEBUG", "rr-calculate-allocations: begin", { jobid });
const allocations = await CdkCalculateAllocations(socket, jobid);
cb?.(allocations);
socket.emit("rr-calculate-allocations:result", allocations);
+ CreateRRLogEvent(socket, "DEBUG", "rr-calculate-allocations: success", { items: allocations?.length });
} catch (e) {
- log("error", `RR allocations error: ${e.message}`, { jobid });
+ CreateRRLogEvent(socket, "ERROR", "rr-calculate-allocations: failed", { error: e.message, jobid });
cb?.({ ok: false, error: e.message });
}
});