diff --git a/client/src/pages/dms/dms.container.jsx b/client/src/pages/dms/dms.container.jsx index a5144c0f2..fbd8154a5 100644 --- a/client/src/pages/dms/dms.container.jsx +++ b/client/src/pages/dms/dms.container.jsx @@ -142,6 +142,41 @@ export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader, inse isConnected ? "Connected" : "Disconnected" }`; + const resetKey = useMemo(() => `${mode || "none"}-${jobId || "none"}`, [mode, jobId]); + + // 🔄 Hard reset of local + server-side DMS context when the page/job loads + useEffect(() => { + // Clear any local ephemeral state that might be stale + setLogs([]); + setRrOpenRoLimit(false); + setrrValidationPending(false); + + if (!activeSocket) return; + + const emitReset = () => { + // Generic reset; server can branch on `mode` if needed + activeSocket.emit("dms-reset-context", { jobId, mode }); + }; + + if (activeSocket.connected) { + // WSS usually lands here + emitReset(); + return; + } + + // Legacy WS: wait for the connect before emitting reset + const handleConnectOnce = () => { + emitReset(); + activeSocket.off("connect", handleConnectOnce); + }; + + activeSocket.on("connect", handleConnectOnce); + + return () => { + activeSocket.off("connect", handleConnectOnce); + }; + }, [jobId, mode, activeSocket]); + const handleExportFailed = (payload = {}) => { const { title, friendlyMessage, error: errText, severity, errorCode, vendorStatusCode } = payload; @@ -355,6 +390,7 @@ export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader, inse {!isRrMode ? ( ) : ( @@ -388,7 +425,7 @@ export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader, inse - + { const rate = rateMap[key]; if (!rate || !rate.adjustment) return; - const adjMoney = Dinero(rate.adjustment); - if (adjMoney.isZero()) return; + const checkMoney = Dinero(rate.adjustment); + if (checkMoney.isZero()) return; const accountName = selectedDmsAllocationConfig.profits[key.toUpperCase()]; const otherAccount = bodyshop.md_responsibility_centers.profits.find((c) => c.name === accountName); if (otherAccount) { const bucket = ensureCenterBucket(profitCenterHash, accountName); + + // Note: we intentionally use `rate.adjustments` here to mirror CDK behaviour + const adjMoney = Dinero(rate.adjustments); bucket.extrasSale = bucket.extrasSale.add(adjMoney); debugLog("Added rate adjustment", { diff --git a/server/web-sockets/redisSocketEvents.js b/server/web-sockets/redisSocketEvents.js index c2db293c0..c4af2fb55 100644 --- a/server/web-sockets/redisSocketEvents.js +++ b/server/web-sockets/redisSocketEvents.js @@ -67,6 +67,34 @@ const redisSocketEvents = ({ io, redisHelpers, ioHelpers, logger }) => { // Register Socket Events const registerSocketEvents = (socket) => { + // DMS reset events (clear per-socket DMS transactional cache) + const registerDmsResetEvents = (socket) => { + socket.on("dms-reset-context", async ({ jobId, mode } = {}, ack) => { + try { + // This clears all transactional session data for this socket + // (RR txEnvelope/JobData/SelectedCustomer/PendingRO/etc, Fortellis, etc.) + await clearSessionTransactionData(socket.id); + + createLogEvent( + socket, + "debug", + `DMS reset-context: cleared transactional session data` + + (jobId ? ` (jobId=${jobId})` : "") + + (mode ? ` (mode=${mode})` : "") + ); + + if (typeof ack === "function") { + ack({ ok: true }); + } + } catch (error) { + createLogEvent(socket, "error", `DMS reset-context failed: ${error.message}`); + if (typeof ack === "function") { + ack({ ok: false, error: error.message }); + } + } + }); + }; + // Token Update Events const registerUpdateEvents = (socket) => { let latestTokenTimestamp = 0; @@ -164,7 +192,9 @@ const redisSocketEvents = ({ io, redisHelpers, ioHelpers, logger }) => { // Optional: clear transactional session try { await clearSessionTransactionData(socket.id); - } catch {} + } catch { + // + } // Leave all rooms except the default room (socket.id) const rooms = Array.from(socket.rooms).filter((room) => room !== socket.id); for (const room of rooms) { @@ -363,6 +393,7 @@ const redisSocketEvents = ({ io, redisHelpers, ioHelpers, logger }) => { }; // Call Handlers + registerDmsResetEvents(socket); registerRoomAndBroadcastEvents(socket); registerUpdateEvents(socket); registerMessagingEvents(socket); diff --git a/server/web-sockets/web-socket.js b/server/web-sockets/web-socket.js index fbef1cf65..0abcf91bd 100644 --- a/server/web-sockets/web-socket.js +++ b/server/web-sockets/web-socket.js @@ -115,6 +115,33 @@ function SetLegacyWebsocketHandlers(io) { socket.on("disconnect", () => { createLogEvent(socket, "DEBUG", `User disconnected.`); }); + + // DMS reset for legacy WS (CDK / PBS) + socket.on("dms-reset-context", ({ jobId, mode } = {}, ack) => { + try { + // Clear any per-socket DMS state that can leak across jobs + socket.selectedCustomerId = null; // CDK / PBS AR + socket.txEnvelope = null; // PBS AP export + socket.apAllocations = null; // PBS AP allocations + + createLogEvent( + socket, + "DEBUG", + `DMS reset-context (legacy WS): cleared per-socket state` + + (jobId ? ` (jobId=${jobId})` : "") + + (mode ? ` (mode=${mode})` : "") + ); + + if (typeof ack === "function") { + ack({ ok: true }); + } + } catch (error) { + createLogEvent(socket, "ERROR", `DMS reset-context (legacy WS) failed: ${error.message}`); + if (typeof ack === "function") { + ack({ ok: false, error: error.message }); + } + } + }); }); }