feature/IO-3357-Reynolds-and-Reynolds-DMS-API-Integration - Checkpoint
This commit is contained in:
@@ -37,8 +37,8 @@ export function DmsCustomerSelector(props) {
|
|||||||
const rrProps = {
|
const rrProps = {
|
||||||
rrOpenRoLimit: rrOptions.openRoLimit,
|
rrOpenRoLimit: rrOptions.openRoLimit,
|
||||||
onRrOpenRoFinished: rrOptions.onOpenRoFinished,
|
onRrOpenRoFinished: rrOptions.onOpenRoFinished,
|
||||||
rrCashierPending: rrOptions.cashierPending,
|
rrValidationPending: rrOptions.validationPending,
|
||||||
onRrCashierFinished: rrOptions.onCashierFinished
|
onValidationFinished: rrOptions.onValidationFinished
|
||||||
};
|
};
|
||||||
return <RRCustomerSelector {...base} {...rrProps} />;
|
return <RRCustomerSelector {...base} {...rrProps} />;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,8 +49,8 @@ export default function RRCustomerSelector({
|
|||||||
socket,
|
socket,
|
||||||
rrOpenRoLimit = false,
|
rrOpenRoLimit = false,
|
||||||
onRrOpenRoFinished,
|
onRrOpenRoFinished,
|
||||||
rrCashierPending = false,
|
rrValidationPending = false,
|
||||||
onRrCashierFinished
|
onValidationFinished
|
||||||
}) {
|
}) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
@@ -58,10 +58,10 @@ export default function RRCustomerSelector({
|
|||||||
const [selectedCustomer, setSelectedCustomer] = useState(null);
|
const [selectedCustomer, setSelectedCustomer] = useState(null);
|
||||||
const [refreshing, setRefreshing] = useState(false);
|
const [refreshing, setRefreshing] = useState(false);
|
||||||
|
|
||||||
// Show dialog automatically when cashiering is pending
|
// Show dialog automatically when validation is pending
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (rrCashierPending) setOpen(true);
|
if (rrValidationPending) setOpen(true);
|
||||||
}, [rrCashierPending]);
|
}, [rrValidationPending]);
|
||||||
|
|
||||||
// Listen for RR customer selection list
|
// Listen for RR customer selection list
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -196,22 +196,21 @@ export default function RRCustomerSelector({
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Cashiering step banner */}
|
{/* Validation step banner */}
|
||||||
{rrCashierPending && (
|
{rrValidationPending && (
|
||||||
<Alert
|
<Alert
|
||||||
type="info"
|
type="info"
|
||||||
showIcon
|
showIcon
|
||||||
message="Complete cashiering in Reynolds"
|
message="Complete Validation in Reynolds"
|
||||||
description={
|
description={
|
||||||
<div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
|
<div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
|
||||||
<div>
|
<div>
|
||||||
We created the Repair Order in Reynolds. Please complete the cashiering/closeout steps in
|
We created the Repair Order. Please validate the totals and taxes in the DMS system. When done,
|
||||||
Reynolds. When done, click <strong>Finished/Close</strong> to finalize and mark this export as
|
click <strong>Finished</strong> to finalize and mark this export as complete.
|
||||||
complete.
|
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<Space>
|
<Space>
|
||||||
<Button type="primary" onClick={onRrCashierFinished}>
|
<Button type="primary" onClick={onValidationFinished}>
|
||||||
Finished / Close
|
Finished / Close
|
||||||
</Button>
|
</Button>
|
||||||
</Space>
|
</Space>
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ const DMS_SOCKET_EVENTS = {
|
|||||||
[DMS_MAP.reynolds]: {
|
[DMS_MAP.reynolds]: {
|
||||||
log: "rr-log-event",
|
log: "rr-log-event",
|
||||||
partialResult: "rr-export-job:result",
|
partialResult: "rr-export-job:result",
|
||||||
cashierNeeded: "rr-cashiering-required",
|
validationNeeded: "rr-validation-required",
|
||||||
exportSuccess: "export-success",
|
exportSuccess: "export-success",
|
||||||
exportFailed: "export-failed"
|
exportFailed: "export-failed"
|
||||||
},
|
},
|
||||||
@@ -104,7 +104,7 @@ export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader, inse
|
|||||||
const [rrOpenRoLimit, setRrOpenRoLimit] = useState(false);
|
const [rrOpenRoLimit, setRrOpenRoLimit] = useState(false);
|
||||||
const clearRrOpenRoLimit = () => setRrOpenRoLimit(false);
|
const clearRrOpenRoLimit = () => setRrOpenRoLimit(false);
|
||||||
|
|
||||||
const [rrCashierPending, setRrCashierPending] = useState(false);
|
const [rrValidationPending, setrrValidationPending] = useState(false);
|
||||||
|
|
||||||
const { loading, error, data } = useQuery(QUERY_JOB_EXPORT_DMS, {
|
const { loading, error, data } = useQuery(QUERY_JOB_EXPORT_DMS, {
|
||||||
variables: { id: jobId },
|
variables: { id: jobId },
|
||||||
@@ -259,8 +259,8 @@ export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader, inse
|
|||||||
const jobIdResolved = payload?.jobId ?? payload;
|
const jobIdResolved = payload?.jobId ?? payload;
|
||||||
notification.success({ message: t("jobs.successes.exported") });
|
notification.success({ message: t("jobs.successes.exported") });
|
||||||
|
|
||||||
// Clear RR cashier flag if any
|
// Clear RR Validation flag if any
|
||||||
setRrCashierPending(false);
|
setrrValidationPending(false);
|
||||||
|
|
||||||
insertAuditTrail({
|
insertAuditTrail({
|
||||||
jobid: jobIdResolved,
|
jobid: jobIdResolved,
|
||||||
@@ -276,40 +276,41 @@ export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader, inse
|
|||||||
// RR-only extras
|
// RR-only extras
|
||||||
|
|
||||||
const onPartialResult = () => {
|
const onPartialResult = () => {
|
||||||
setRrCashierPending(true);
|
setrrValidationPending(true);
|
||||||
setLogs((prev) => [
|
setLogs((prev) => [
|
||||||
...prev,
|
...prev,
|
||||||
{
|
{
|
||||||
timestamp: new Date(),
|
timestamp: new Date(),
|
||||||
level: "INFO",
|
level: "INFO",
|
||||||
message:
|
message:
|
||||||
"Repair Order created in Reynolds. Complete cashiering in Reynolds, then click Finished/Close to finalize."
|
"Repair Order created in Reynolds. Complete validation in Reynolds, then click Finished/Close to finalize."
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
notification.info({
|
notification.info({
|
||||||
message: "Reynolds RO created",
|
message: "Reynolds RO created",
|
||||||
description:
|
description:
|
||||||
"Complete cashiering in Reynolds, then click Finished/Close to finalize and mark this export complete.",
|
"Complete validation in Reynolds, then click Finished/Close to finalize and mark this export complete.",
|
||||||
duration: 8
|
duration: 8
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const onCashierRequired = (payload) => {
|
const onValidationRequired = (payload) => {
|
||||||
setRrCashierPending(true);
|
setrrValidationPending(true);
|
||||||
setLogs((prev) => [
|
setLogs((prev) => [
|
||||||
...prev,
|
...prev,
|
||||||
{
|
{
|
||||||
timestamp: new Date(),
|
timestamp: new Date(),
|
||||||
level: "INFO",
|
level: "INFO",
|
||||||
message:
|
message:
|
||||||
"Repair Order created in Reynolds. Complete cashiering in Reynolds, then click Finished/Close to finalize.",
|
"Repair Order created in Reynolds. Complete validation in Reynolds, then click Finished/Close to finalize.",
|
||||||
meta: { payload }
|
meta: { payload }
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (mode === DMS_MAP.reynolds && channels.partialResult) activeSocket.on(channels.partialResult, onPartialResult);
|
if (mode === DMS_MAP.reynolds && channels.partialResult) activeSocket.on(channels.partialResult, onPartialResult);
|
||||||
if (mode === DMS_MAP.reynolds && channels.cashierNeeded) activeSocket.on(channels.cashierNeeded, onCashierRequired);
|
if (mode === DMS_MAP.reynolds && channels.validationNeeded)
|
||||||
|
activeSocket.on(channels.validationrNeeded, onValidationRequired);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
activeSocket.off("connect", onConnect);
|
activeSocket.off("connect", onConnect);
|
||||||
@@ -323,8 +324,8 @@ export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader, inse
|
|||||||
|
|
||||||
if (mode === DMS_MAP.reynolds && channels.partialResult)
|
if (mode === DMS_MAP.reynolds && channels.partialResult)
|
||||||
activeSocket.off(channels.partialResult, onPartialResult);
|
activeSocket.off(channels.partialResult, onPartialResult);
|
||||||
if (mode === DMS_MAP.reynolds && channels.cashierNeeded)
|
if (mode === DMS_MAP.reynolds && channels.validationNeeded)
|
||||||
activeSocket.off(channels.cashierNeeded, onCashierRequired);
|
activeSocket.off(channels.validationNeeded, onValidationRequired);
|
||||||
|
|
||||||
// Only tear down legacy socket listeners; don't disconnect WSS from here
|
// Only tear down legacy socket listeners; don't disconnect WSS from here
|
||||||
if (!isWssMode(mode)) {
|
if (!isWssMode(mode)) {
|
||||||
@@ -335,7 +336,7 @@ export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader, inse
|
|||||||
}, [mode, activeSocket, channels, logLevel, notification, t, insertAuditTrail, history]);
|
}, [mode, activeSocket, channels, logLevel, notification, t, insertAuditTrail, history]);
|
||||||
|
|
||||||
// RR finalize callback (unchanged public behavior)
|
// RR finalize callback (unchanged public behavior)
|
||||||
const handleRrCashierFinished = () => {
|
const handleRrValidationFinished = () => {
|
||||||
if (!jobId) return;
|
if (!jobId) return;
|
||||||
if (!isWssMode(mode)) return; // RR is WSS-only
|
if (!isWssMode(mode)) return; // RR is WSS-only
|
||||||
activeSocket.emit("rr-finalize-repair-order", { jobId }, (ack) => {
|
activeSocket.emit("rr-finalize-repair-order", { jobId }, (ack) => {
|
||||||
@@ -385,8 +386,8 @@ export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader, inse
|
|||||||
rrOptions={{
|
rrOptions={{
|
||||||
openRoLimit: rrOpenRoLimit,
|
openRoLimit: rrOpenRoLimit,
|
||||||
onOpenRoFinished: clearRrOpenRoLimit,
|
onOpenRoFinished: clearRrOpenRoLimit,
|
||||||
cashierPending: rrCashierPending,
|
validationPending: rrValidationPending,
|
||||||
onCashierFinished: handleRrCashierFinished
|
onValidationFinished: handleRrValidationFinished
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
const { RRClient } = require("./lib/index.cjs");
|
const { RRClient } = require("./lib/index.cjs");
|
||||||
const { getRRConfigFromBodyshop } = require("./rr-config");
|
const { getRRConfigFromBodyshop } = require("./rr-config");
|
||||||
const RRLogger = require("./rr-logger");
|
const RRLogger = require("./rr-logger");
|
||||||
|
const InstanceManager = require("../utils/instanceMgr").default;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Country code map for normalization
|
* Country code map for normalization
|
||||||
@@ -189,7 +190,7 @@ const buildCustomerPayloadFromJob = (job, overrides = {}) => {
|
|||||||
firstName: firstName || undefined,
|
firstName: firstName || undefined,
|
||||||
lastName: lastName || undefined,
|
lastName: lastName || undefined,
|
||||||
customerName: companyName || undefined,
|
customerName: companyName || undefined,
|
||||||
createdBy: overrides.createdBy || "ImEX Online",
|
createdBy: overrides.createdBy || InstanceManager({ imex: "ImEX Online", rome: "Rome Online" }),
|
||||||
customerType: overrides.customerType || "R", // Retail default
|
customerType: overrides.customerType || "R", // Retail default
|
||||||
addresses,
|
addresses,
|
||||||
phones,
|
phones,
|
||||||
|
|||||||
@@ -122,6 +122,7 @@ const markRRExportSuccess = async ({ socket, jobId, job, bodyshop, result, metaE
|
|||||||
const insertRRFailedExportLog = async ({ socket, jobId, job, bodyshop, error, classification, result }) => {
|
const insertRRFailedExportLog = async ({ socket, jobId, job, bodyshop, error, classification, result }) => {
|
||||||
const endpoint = process.env.GRAPHQL_ENDPOINT;
|
const endpoint = process.env.GRAPHQL_ENDPOINT;
|
||||||
if (!endpoint) throw new Error("GRAPHQL_ENDPOINT not configured");
|
if (!endpoint) throw new Error("GRAPHQL_ENDPOINT not configured");
|
||||||
|
|
||||||
const token = getAuthToken(socket);
|
const token = getAuthToken(socket);
|
||||||
if (!token) throw new Error("Auth token missing on socket");
|
if (!token) throw new Error("Auth token missing on socket");
|
||||||
|
|
||||||
|
|||||||
@@ -72,14 +72,7 @@ const exportJobToRR = async (args) => {
|
|||||||
const roStatus = data?.roStatus || null;
|
const roStatus = data?.roStatus || null;
|
||||||
|
|
||||||
// Extract canonical roNo you'll need for finalize step
|
// Extract canonical roNo you'll need for finalize step
|
||||||
const roNo =
|
const roNo = data?.dmsRoNo ?? data?.outsdRoNo ?? roStatus?.dmsRoNo ?? null;
|
||||||
data?.dmsRoNo ??
|
|
||||||
data?.outsdRoNo ??
|
|
||||||
roStatus?.dmsRoNo ??
|
|
||||||
roStatus?.DMSRoNo ??
|
|
||||||
roStatus?.outsdRoNo ??
|
|
||||||
roStatus?.OutsdRoNo ??
|
|
||||||
null;
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: rrRes?.success === true || roStatus?.status === "Success",
|
success: rrRes?.success === true || roStatus?.status === "Success",
|
||||||
|
|||||||
@@ -194,10 +194,13 @@ const normalizeVehicleCandidates = (res) => {
|
|||||||
for (const sv of serv) {
|
for (const sv of serv) {
|
||||||
const v = sv?.Vehicle || {};
|
const v = sv?.Vehicle || {};
|
||||||
const vin = v?.Vin || v?.VIN || v?.vin;
|
const vin = v?.Vin || v?.VIN || v?.vin;
|
||||||
|
|
||||||
if (!vin) continue;
|
if (!vin) continue;
|
||||||
|
|
||||||
const year = v?.VehicleYr || v?.ModelYear || v?.Year;
|
const year = v?.VehicleYr || v?.ModelYear || v?.Year;
|
||||||
const make = v?.VehicleMake || v?.MakeName || v?.Make;
|
const make = v?.VehicleMake || v?.MakeName || v?.Make;
|
||||||
const model = v?.MdlNo || v?.ModelDesc || v?.Model;
|
const model = v?.MdlNo || v?.ModelDesc || v?.Model;
|
||||||
|
|
||||||
const label = [year, make, model, vin].filter(Boolean).join(" ");
|
const label = [year, make, model, vin].filter(Boolean).join(" ");
|
||||||
out.push({ vin, year, make, model, label, _blk: blk });
|
out.push({ vin, year, make, model, label, _blk: blk });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,8 +32,7 @@ const ADVISORS_CACHE_TTL = 7 * 24 * 60 * 60; // seconds
|
|||||||
* @param job
|
* @param job
|
||||||
* @returns {*|null}
|
* @returns {*|null}
|
||||||
*/
|
*/
|
||||||
const resolveJobId = (explicit, payload, job) =>
|
const resolveJobId = (explicit, payload, job) => explicit || payload?.jobId || job?.id || null;
|
||||||
explicit || payload?.jobId || payload?.jobid || job?.id || job?.jobId || job?.jobid || null;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolve VIN from tx/job shapes
|
* Resolve VIN from tx/job shapes
|
||||||
@@ -218,6 +217,7 @@ const registerRREvents = ({ socket, redisHelpers }) => {
|
|||||||
try {
|
try {
|
||||||
const { bodyshopId } = await getSessionOrSocket(redisHelpers, socket);
|
const { bodyshopId } = await getSessionOrSocket(redisHelpers, socket);
|
||||||
const bodyshop = await getBodyshopForSocket({ bodyshopId, socket });
|
const bodyshop = await getBodyshopForSocket({ bodyshopId, socket });
|
||||||
|
|
||||||
CreateRRLogEvent(socket, "DEBUG", "rr-lookup-combined: begin", { jobid, params });
|
CreateRRLogEvent(socket, "DEBUG", "rr-lookup-combined: begin", { jobid, params });
|
||||||
|
|
||||||
const res = await rrCombinedSearch(bodyshop, params || {});
|
const res = await rrCombinedSearch(bodyshop, params || {});
|
||||||
@@ -230,13 +230,14 @@ const registerRREvents = ({ socket, redisHelpers }) => {
|
|||||||
|
|
||||||
const normalized = sortVehicleOwnerFirst(normalizeCustomerCandidates(res, { ownersSet }));
|
const normalized = sortVehicleOwnerFirst(normalizeCustomerCandidates(res, { ownersSet }));
|
||||||
const rid = resolveJobId(jobid, { jobid }, null);
|
const rid = resolveJobId(jobid, { jobid }, null);
|
||||||
|
|
||||||
const decorated = normalized.map((c) => (c.vinOwner != null ? c : { ...c, vinOwner: !!c.isVehicleOwner }));
|
const decorated = normalized.map((c) => (c.vinOwner != null ? c : { ...c, vinOwner: !!c.isVehicleOwner }));
|
||||||
|
|
||||||
cb?.({ jobid: rid, data: decorated });
|
cb?.({ jobid: rid, data: decorated });
|
||||||
|
|
||||||
socket.emit("rr-select-customer", decorated);
|
socket.emit("rr-select-customer", decorated);
|
||||||
CreateRRLogEvent(socket, "DEBUG", "rr-lookup-combined: emitted rr-select-customer", {
|
CreateRRLogEvent(socket, "DEBUG", "rr-lookup-combined: emitted rr-select-customer", {
|
||||||
count: decorated.length
|
count: decorated.length,
|
||||||
|
res
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
CreateRRLogEvent(socket, "ERROR", "RR combined lookup error", { error: e.message, jobid });
|
CreateRRLogEvent(socket, "ERROR", "RR combined lookup error", { error: e.message, jobid });
|
||||||
@@ -320,8 +321,10 @@ const registerRREvents = ({ socket, redisHelpers }) => {
|
|||||||
|
|
||||||
socket.on("rr-export-job", async ({ jobid, jobId, txEnvelope } = {}) => {
|
socket.on("rr-export-job", async ({ jobid, jobId, txEnvelope } = {}) => {
|
||||||
const rid = resolveJobId(jobid || jobId, { jobId, jobid }, null);
|
const rid = resolveJobId(jobid || jobId, { jobId, jobid }, null);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (!rid) throw new Error("RR export: jobid required");
|
if (!rid) throw new Error("RR export: jobid required");
|
||||||
|
|
||||||
CreateRRLogEvent(socket, "DEBUG", `{1} Received RR export request`, { jobid: rid });
|
CreateRRLogEvent(socket, "DEBUG", `{1} Received RR export request`, { jobid: rid });
|
||||||
|
|
||||||
await redisHelpers.setSessionTransactionData(
|
await redisHelpers.setSessionTransactionData(
|
||||||
@@ -331,6 +334,7 @@ const registerRREvents = ({ socket, redisHelpers }) => {
|
|||||||
txEnvelope || {},
|
txEnvelope || {},
|
||||||
defaultRRTTL
|
defaultRRTTL
|
||||||
);
|
);
|
||||||
|
|
||||||
CreateRRLogEvent(socket, "DEBUG", `{1.1} Cached txEnvelope`, { hasTxEnvelope: !!txEnvelope });
|
CreateRRLogEvent(socket, "DEBUG", `{1.1} Cached txEnvelope`, { hasTxEnvelope: !!txEnvelope });
|
||||||
|
|
||||||
const job = await QueryJobData({ redisHelpers }, rid);
|
const job = await QueryJobData({ redisHelpers }, rid);
|
||||||
@@ -341,12 +345,14 @@ const registerRREvents = ({ socket, redisHelpers }) => {
|
|||||||
job,
|
job,
|
||||||
defaultRRTTL
|
defaultRRTTL
|
||||||
);
|
);
|
||||||
|
|
||||||
CreateRRLogEvent(socket, "DEBUG", `{1.2} Cached JobData`, { vin: job?.v_vin, ro: job?.ro_number });
|
CreateRRLogEvent(socket, "DEBUG", `{1.2} Cached JobData`, { vin: job?.v_vin, ro: job?.ro_number });
|
||||||
|
|
||||||
const adv = readAdvisorNo(
|
const adv = readAdvisorNo(
|
||||||
{ txEnvelope },
|
{ txEnvelope },
|
||||||
await redisHelpers.getSessionTransactionData(socket.id, getTransactionType(rid), RRCacheEnums.AdvisorNo)
|
await redisHelpers.getSessionTransactionData(socket.id, getTransactionType(rid), RRCacheEnums.AdvisorNo)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (adv) {
|
if (adv) {
|
||||||
await redisHelpers.setSessionTransactionData(
|
await redisHelpers.setSessionTransactionData(
|
||||||
socket.id,
|
socket.id,
|
||||||
@@ -355,6 +361,7 @@ const registerRREvents = ({ socket, redisHelpers }) => {
|
|||||||
String(adv),
|
String(adv),
|
||||||
defaultRRTTL
|
defaultRRTTL
|
||||||
);
|
);
|
||||||
|
|
||||||
CreateRRLogEvent(socket, "DEBUG", `{1.3} Cached advisorNo`, { advisorNo: String(adv) });
|
CreateRRLogEvent(socket, "DEBUG", `{1.3} Cached advisorNo`, { advisorNo: String(adv) });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -362,9 +369,10 @@ const registerRREvents = ({ socket, redisHelpers }) => {
|
|||||||
const bodyshop = await getBodyshopForSocket({ bodyshopId, socket });
|
const bodyshop = await getBodyshopForSocket({ bodyshopId, socket });
|
||||||
|
|
||||||
CreateRRLogEvent(socket, "DEBUG", `{2} Running multi-search (Full Name + VIN)`);
|
CreateRRLogEvent(socket, "DEBUG", `{2} Running multi-search (Full Name + VIN)`);
|
||||||
const candidates = await rrMultiCustomerSearch({ bodyshop, job, socket, redisHelpers });
|
|
||||||
|
|
||||||
|
const candidates = await rrMultiCustomerSearch({ bodyshop, job, socket, redisHelpers });
|
||||||
const decorated = candidates.map((c) => (c.vinOwner != null ? c : { ...c, vinOwner: !!c.isVehicleOwner }));
|
const decorated = candidates.map((c) => (c.vinOwner != null ? c : { ...c, vinOwner: !!c.isVehicleOwner }));
|
||||||
|
|
||||||
socket.emit("rr-select-customer", decorated);
|
socket.emit("rr-select-customer", decorated);
|
||||||
CreateRRLogEvent(socket, "DEBUG", `{2.1} Emitted rr-select-customer`, {
|
CreateRRLogEvent(socket, "DEBUG", `{2.1} Emitted rr-select-customer`, {
|
||||||
count: decorated.length,
|
count: decorated.length,
|
||||||
@@ -376,6 +384,7 @@ const registerRREvents = ({ socket, redisHelpers }) => {
|
|||||||
stack: error.stack,
|
stack: error.stack,
|
||||||
jobid: rid
|
jobid: rid
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
socket.emit("export-failed", { vendor: "rr", jobId: rid, error: error.message });
|
socket.emit("export-failed", { vendor: "rr", jobId: rid, error: error.message });
|
||||||
} catch {
|
} catch {
|
||||||
@@ -399,24 +408,31 @@ const registerRREvents = ({ socket, redisHelpers }) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const ns = getTransactionType(rid);
|
const ns = getTransactionType(rid);
|
||||||
|
|
||||||
let selectedCustNo =
|
let selectedCustNo =
|
||||||
(custNo && String(custNo)) ||
|
(custNo && String(custNo)) ||
|
||||||
(selectedCustomerId && String(selectedCustomerId)) ||
|
(selectedCustomerId && String(selectedCustomerId)) ||
|
||||||
(await redisHelpers.getSessionTransactionData(socket.id, ns, RRCacheEnums.SelectedCustomer));
|
(await redisHelpers.getSessionTransactionData(socket.id, ns, RRCacheEnums.SelectedCustomer));
|
||||||
|
|
||||||
job = await redisHelpers.getSessionTransactionData(socket.id, ns, RRCacheEnums.JobData);
|
job = await redisHelpers.getSessionTransactionData(socket.id, ns, RRCacheEnums.JobData);
|
||||||
|
|
||||||
const txEnvelope = (await redisHelpers.getSessionTransactionData(socket.id, ns, RRCacheEnums.txEnvelope)) || {};
|
const txEnvelope = (await redisHelpers.getSessionTransactionData(socket.id, ns, RRCacheEnums.txEnvelope)) || {};
|
||||||
|
|
||||||
if (!job) throw new Error("Staged JobData not found (run rr-export-job first).");
|
if (!job) throw new Error("Staged JobData not found (run rr-export-job first).");
|
||||||
|
|
||||||
const { bodyshopId } = await getSessionOrSocket(redisHelpers, socket);
|
const { bodyshopId } = await getSessionOrSocket(redisHelpers, socket);
|
||||||
|
|
||||||
bodyshop = await getBodyshopForSocket({ bodyshopId, socket });
|
bodyshop = await getBodyshopForSocket({ bodyshopId, socket });
|
||||||
|
|
||||||
// Create customer (if requested or none chosen)
|
// Create customer (if requested or none chosen)
|
||||||
if (create === true || !selectedCustNo) {
|
if (create === true || !selectedCustNo) {
|
||||||
CreateRRLogEvent(socket, "DEBUG", `{3.1} Creating RR customer`);
|
CreateRRLogEvent(socket, "DEBUG", `{3.1} Creating RR customer`);
|
||||||
|
|
||||||
const created = await createRRCustomer({ bodyshop, job, socket });
|
const created = await createRRCustomer({ bodyshop, job, socket });
|
||||||
selectedCustNo = String(created?.customerNo);
|
selectedCustNo = String(created?.customerNo);
|
||||||
|
|
||||||
if (!selectedCustNo) throw new Error("RR create customer returned no custNo");
|
if (!selectedCustNo) throw new Error("RR create customer returned no custNo");
|
||||||
|
|
||||||
CreateRRLogEvent(socket, "DEBUG", `{3.2} Created customer`, { custNo: selectedCustNo });
|
CreateRRLogEvent(socket, "DEBUG", `{3.2} Created customer`, { custNo: selectedCustNo });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -426,6 +442,7 @@ const registerRREvents = ({ socket, redisHelpers }) => {
|
|||||||
if (vehQ && vehQ.kind === "vin" && job?.v_vin) {
|
if (vehQ && vehQ.kind === "vin" && job?.v_vin) {
|
||||||
const resVin = await rrCombinedSearch(bodyshop, vehQ);
|
const resVin = await rrCombinedSearch(bodyshop, vehQ);
|
||||||
const blocksVin = Array.isArray(resVin?.data) ? resVin.data : Array.isArray(resVin) ? resVin : [];
|
const blocksVin = Array.isArray(resVin?.data) ? resVin.data : Array.isArray(resVin) ? resVin : [];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await redisHelpers.setSessionTransactionData(
|
await redisHelpers.setSessionTransactionData(
|
||||||
socket.id,
|
socket.id,
|
||||||
@@ -437,9 +454,12 @@ const registerRREvents = ({ socket, redisHelpers }) => {
|
|||||||
} catch {
|
} catch {
|
||||||
//
|
//
|
||||||
}
|
}
|
||||||
|
|
||||||
const ownersSet = ownersFromVinBlocks(blocksVin, job.v_vin);
|
const ownersSet = ownersFromVinBlocks(blocksVin, job.v_vin);
|
||||||
|
|
||||||
if (ownersSet?.size) {
|
if (ownersSet?.size) {
|
||||||
const sel = String(selectedCustNo);
|
const sel = String(selectedCustNo);
|
||||||
|
|
||||||
if (!ownersSet.has(sel)) {
|
if (!ownersSet.has(sel)) {
|
||||||
const [existingOwner] = Array.from(ownersSet).map(String);
|
const [existingOwner] = Array.from(ownersSet).map(String);
|
||||||
CreateRRLogEvent(socket, "DEBUG", `{3.2a} VIN exists; switching to VIN owner`, {
|
CreateRRLogEvent(socket, "DEBUG", `{3.2a} VIN exists; switching to VIN owner`, {
|
||||||
@@ -471,6 +491,7 @@ const registerRREvents = ({ socket, redisHelpers }) => {
|
|||||||
|
|
||||||
// Cache final/effective customer selection
|
// Cache final/effective customer selection
|
||||||
const effectiveCustNo = String(selectedCustNo);
|
const effectiveCustNo = String(selectedCustNo);
|
||||||
|
|
||||||
await redisHelpers.setSessionTransactionData(
|
await redisHelpers.setSessionTransactionData(
|
||||||
socket.id,
|
socket.id,
|
||||||
ns,
|
ns,
|
||||||
@@ -478,6 +499,7 @@ const registerRREvents = ({ socket, redisHelpers }) => {
|
|||||||
effectiveCustNo,
|
effectiveCustNo,
|
||||||
defaultRRTTL
|
defaultRRTTL
|
||||||
);
|
);
|
||||||
|
|
||||||
CreateRRLogEvent(socket, "DEBUG", `{3.3} Cached selected customer`, { custNo: effectiveCustNo });
|
CreateRRLogEvent(socket, "DEBUG", `{3.3} Cached selected customer`, { custNo: effectiveCustNo });
|
||||||
|
|
||||||
// Build client & routing
|
// Build client & routing
|
||||||
@@ -489,12 +511,13 @@ const registerRREvents = ({ socket, redisHelpers }) => {
|
|||||||
const tx = {
|
const tx = {
|
||||||
jobData: {
|
jobData: {
|
||||||
...job,
|
...job,
|
||||||
vin: job?.v_vin || job?.vin || job?.vehicleVin || undefined
|
vin: job?.v_vin
|
||||||
},
|
},
|
||||||
txEnvelope
|
txEnvelope
|
||||||
};
|
};
|
||||||
|
|
||||||
const vin = resolveVin({ tx, job });
|
const vin = resolveVin({ tx, job });
|
||||||
|
|
||||||
if (!vin) {
|
if (!vin) {
|
||||||
CreateRRLogEvent(socket, "ERROR", "{3.x} No VIN found for ensureRRServiceVehicle", { jobid: rid });
|
CreateRRLogEvent(socket, "ERROR", "{3.x} No VIN found for ensureRRServiceVehicle", { jobid: rid });
|
||||||
throw new Error("ensureRRServiceVehicle: vin required");
|
throw new Error("ensureRRServiceVehicle: vin required");
|
||||||
@@ -524,9 +547,9 @@ const registerRREvents = ({ socket, redisHelpers }) => {
|
|||||||
|
|
||||||
CreateRRLogEvent(socket, "DEBUG", "{3.4} ensureRRServiceVehicle: done", ensured);
|
CreateRRLogEvent(socket, "DEBUG", "{3.4} ensureRRServiceVehicle: done", ensured);
|
||||||
|
|
||||||
// Advisor no
|
|
||||||
const cachedAdvisor = await redisHelpers.getSessionTransactionData(socket.id, ns, RRCacheEnums.AdvisorNo);
|
const cachedAdvisor = await redisHelpers.getSessionTransactionData(socket.id, ns, RRCacheEnums.AdvisorNo);
|
||||||
const advisorNo = readAdvisorNo({ txEnvelope }, cachedAdvisor);
|
const advisorNo = readAdvisorNo({ txEnvelope }, cachedAdvisor);
|
||||||
|
|
||||||
if (!advisorNo) {
|
if (!advisorNo) {
|
||||||
CreateRRLogEvent(socket, "ERROR", `Advisor is required (advisorNo)`);
|
CreateRRLogEvent(socket, "ERROR", `Advisor is required (advisorNo)`);
|
||||||
await insertRRFailedExportLog({
|
await insertRRFailedExportLog({
|
||||||
@@ -540,6 +563,7 @@ const registerRREvents = ({ socket, redisHelpers }) => {
|
|||||||
socket.emit("export-failed", { vendor: "rr", jobId: rid, 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)." });
|
return ack?.({ ok: false, error: "Advisor is required (advisorNo)." });
|
||||||
}
|
}
|
||||||
|
|
||||||
await redisHelpers.setSessionTransactionData(
|
await redisHelpers.setSessionTransactionData(
|
||||||
socket.id,
|
socket.id,
|
||||||
ns,
|
ns,
|
||||||
@@ -572,17 +596,9 @@ const registerRREvents = ({ socket, redisHelpers }) => {
|
|||||||
const data = result?.data || {};
|
const data = result?.data || {};
|
||||||
|
|
||||||
// Prefer explicit return from export function; then fall back to fields
|
// Prefer explicit return from export function; then fall back to fields
|
||||||
const dmsRoNo =
|
const dmsRoNo = result?.roNo ?? data?.dmsRoNo ?? null;
|
||||||
result?.roNo ?? data?.dmsRoNo ?? data?.DMSRoNo ?? data?.roStatus?.dmsRoNo ?? data?.roStatus?.DMSRoNo ?? null;
|
|
||||||
|
|
||||||
const outsdRoNo =
|
const outsdRoNo = data?.outsdRoNo ?? job?.ro_number ?? job?.id ?? null;
|
||||||
data?.outsdRoNo ??
|
|
||||||
data?.OutsdRoNo ??
|
|
||||||
data?.roStatus?.outsdRoNo ??
|
|
||||||
data?.roStatus?.OutsdRoNo ??
|
|
||||||
job?.ro_number ??
|
|
||||||
job?.id ??
|
|
||||||
null;
|
|
||||||
|
|
||||||
await redisHelpers.setSessionTransactionData(
|
await redisHelpers.setSessionTransactionData(
|
||||||
socket.id,
|
socket.id,
|
||||||
@@ -598,13 +614,13 @@ const registerRREvents = ({ socket, redisHelpers }) => {
|
|||||||
defaultRRTTL
|
defaultRRTTL
|
||||||
);
|
);
|
||||||
|
|
||||||
CreateRRLogEvent(socket, "INFO", `{5} RO created. Waiting for cashiering.`, {
|
CreateRRLogEvent(socket, "INFO", `{5} RO created. Waiting for validation.`, {
|
||||||
dmsRoNo: dmsRoNo || null,
|
dmsRoNo: dmsRoNo || null,
|
||||||
outsdRoNo: outsdRoNo || null
|
outsdRoNo: outsdRoNo || null
|
||||||
});
|
});
|
||||||
|
|
||||||
// Tell FE to prompt for "Finished/Close"
|
// Tell FE to prompt for "Finished/Close"
|
||||||
socket.emit("rr-cashiering-required", { jobId: rid, dmsRoNo, outsdRoNo });
|
socket.emit("rr-validation-required", { jobId: rid, dmsRoNo, outsdRoNo });
|
||||||
|
|
||||||
// Still emit info result if you want
|
// Still emit info result if you want
|
||||||
socket.emit("rr-export-job:result", { jobId: rid, bodyshopId: bodyshop?.id, result });
|
socket.emit("rr-export-job:result", { jobId: rid, bodyshopId: bodyshop?.id, result });
|
||||||
@@ -616,6 +632,7 @@ const registerRREvents = ({ socket, redisHelpers }) => {
|
|||||||
const vendorStatusCode = Number(
|
const vendorStatusCode = Number(
|
||||||
result?.roStatus?.statusCode ?? result?.roStatus?.StatusCode ?? result?.statusBlocks?.transaction?.statusCode
|
result?.roStatus?.statusCode ?? result?.roStatus?.StatusCode ?? result?.statusBlocks?.transaction?.statusCode
|
||||||
);
|
);
|
||||||
|
|
||||||
const cls = classifyRRVendorError({
|
const cls = classifyRRVendorError({
|
||||||
code: vendorStatusCode,
|
code: vendorStatusCode,
|
||||||
message: result?.roStatus?.message ?? result?.roStatus?.Message ?? result?.error ?? "RR export failed"
|
message: result?.roStatus?.message ?? result?.roStatus?.Message ?? result?.error ?? "RR export failed"
|
||||||
|
|||||||
@@ -61,7 +61,6 @@ const normalizeCustomerCandidates = (res, { ownersSet = null } = {}) => {
|
|||||||
|
|
||||||
const chosen = arr.find((a) => (a?.Type || a?.type || "").toString().toUpperCase() === "P") || arr[0];
|
const chosen = arr.find((a) => (a?.Type || a?.type || "").toString().toUpperCase() === "P") || arr[0];
|
||||||
|
|
||||||
// NEW: include County
|
|
||||||
const line1 = chosen?.Addr1 ?? chosen?.AddressLine1 ?? chosen?.Line1 ?? chosen?.Street1 ?? undefined;
|
const line1 = chosen?.Addr1 ?? chosen?.AddressLine1 ?? chosen?.Line1 ?? chosen?.Street1 ?? undefined;
|
||||||
const line2 = chosen?.Addr2 ?? chosen?.AddressLine2 ?? chosen?.Line2 ?? chosen?.Street2 ?? undefined;
|
const line2 = chosen?.Addr2 ?? chosen?.AddressLine2 ?? chosen?.Line2 ?? chosen?.Street2 ?? undefined;
|
||||||
const city = chosen?.City ?? chosen?.city ?? undefined;
|
const city = chosen?.City ?? chosen?.city ?? undefined;
|
||||||
@@ -152,10 +151,7 @@ const readAdvisorNo = (payload, cached) => {
|
|||||||
const tx = payload?.txEnvelope || payload?.envelope || {};
|
const tx = payload?.txEnvelope || payload?.envelope || {};
|
||||||
|
|
||||||
const get = (v) => (v != null && String(v).trim() !== "" ? String(v).trim() : null);
|
const get = (v) => (v != null && String(v).trim() !== "" ? String(v).trim() : null);
|
||||||
|
return get(tx?.advisorNo) || get(payload?.advisorNo) || get(cached) || null;
|
||||||
let value = get(tx?.advisorNo) || get(payload?.advisorNo) || get(cached) || null;
|
|
||||||
|
|
||||||
return value;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -170,7 +166,7 @@ const RRCacheEnums = {
|
|||||||
VINCandidates: "RR.VINCandidates",
|
VINCandidates: "RR.VINCandidates",
|
||||||
SelectedVin: "RR.SelectedVin",
|
SelectedVin: "RR.SelectedVin",
|
||||||
ExportResult: "RR.ExportResult",
|
ExportResult: "RR.ExportResult",
|
||||||
PendingRO: "RR.PendingRO" // NEW: cache created RO to finalize later
|
PendingRO: "RR.PendingRO"
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user