From c1e3c08652fdf5b17898f5708a0da398332d875f Mon Sep 17 00:00:00 2001 From: Dave Date: Fri, 28 Nov 2025 11:29:48 -0500 Subject: [PATCH] feature/IO-3357-Reynolds-and-Reynolds-DMS-API-Integration - Fixes to Caching of allocations summary --- .../dms-allocations-summary.component.jsx | 11 +++++-- .../rr-dms-allocations-summary.component.jsx | 13 ++++++-- .../dms-post-form/cdklike-dms-post-form.jsx | 32 ++++++++++++------- .../dms-post-form/dms-post-form.component.jsx | 27 ++++++++++++++-- .../dms-post-form/rr-dms-post-form.jsx | 28 +++++++++------- client/src/pages/dms/dms.container.jsx | 13 +++++++- 6 files changed, 93 insertions(+), 31 deletions(-) diff --git a/client/src/components/dms-allocations-summary/dms-allocations-summary.component.jsx b/client/src/components/dms-allocations-summary/dms-allocations-summary.component.jsx index bb6b6ef96..c985b7bd5 100644 --- a/client/src/components/dms-allocations-summary/dms-allocations-summary.component.jsx +++ b/client/src/components/dms-allocations-summary/dms-allocations-summary.component.jsx @@ -24,10 +24,11 @@ export default connect(mapStateToProps, mapDispatchToProps)(DmsAllocationsSummar * @param bodyshop * @param jobId * @param title + * @param onAllocationsChange * @returns {JSX.Element} * @constructor */ -export function DmsAllocationsSummary({ mode, socket, bodyshop, jobId, title }) { +export function DmsAllocationsSummary({ mode, socket, bodyshop, jobId, title, onAllocationsChange }) { const { t } = useTranslation(); const [allocationsSummary, setAllocationsSummary] = useState([]); @@ -48,11 +49,17 @@ export function DmsAllocationsSummary({ mode, socket, bodyshop, jobId, title }) setAllocationsSummary(list); // Preserve side-channel used by the post form for discrepancy checks socket.allocationsSummary = list; + if (onAllocationsChange) onAllocationsChange(list); }); } catch { // Best-effort; leave table empty on error setAllocationsSummary([]); - socket && (socket.allocationsSummary = []); + if (socket) { + socket.allocationsSummary = []; + } + if (onAllocationsChange) { + onAllocationsChange([]); + } } }, [socket, jobId, mode, allocationsEvent]); diff --git a/client/src/components/dms-allocations-summary/rr-dms-allocations-summary.component.jsx b/client/src/components/dms-allocations-summary/rr-dms-allocations-summary.component.jsx index da9c485e2..914c64341 100644 --- a/client/src/components/dms-allocations-summary/rr-dms-allocations-summary.component.jsx +++ b/client/src/components/dms-allocations-summary/rr-dms-allocations-summary.component.jsx @@ -51,7 +51,7 @@ function normalizeJobAllocations(ack) { * is now done on the backend via buildRogogFromAllocations/buildRolaborFromRogog. * This component just renders the preview from `ack.rogg` / `ack.rolabor`. */ -export function RrAllocationsSummary({ socket, bodyshop, jobId, title }) { +export function RrAllocationsSummary({ socket, bodyshop, jobId, title, onAllocationsChange }) { const { t } = useTranslation(); const [roggPreview, setRoggPreview] = useState(null); const [rolaborPreview, setRolaborPreview] = useState(null); @@ -70,6 +70,9 @@ export function RrAllocationsSummary({ socket, bodyshop, jobId, title }) { socket.allocationsSummary = []; socket.rrAllocationsRaw = ack; } + if (onAllocationsChange) { + onAllocationsChange([]); + } return; } @@ -83,6 +86,9 @@ export function RrAllocationsSummary({ socket, bodyshop, jobId, title }) { socket.allocationsSummary = jobAllocRows; socket.rrAllocationsRaw = ack; } + if (onAllocationsChange) { + onAllocationsChange(jobAllocRows); + } }); } catch { setRoggPreview(null); @@ -91,8 +97,11 @@ export function RrAllocationsSummary({ socket, bodyshop, jobId, title }) { if (socket) { socket.allocationsSummary = []; } + if (onAllocationsChange) { + onAllocationsChange([]); + } } - }, [socket, jobId, t]); + }, [socket, jobId, t, onAllocationsChange]); useEffect(() => { fetchAllocations(); diff --git a/client/src/components/dms-post-form/cdklike-dms-post-form.jsx b/client/src/components/dms-post-form/cdklike-dms-post-form.jsx index 057387ef1..5bcf7cf1f 100644 --- a/client/src/components/dms-post-form/cdklike-dms-post-form.jsx +++ b/client/src/components/dms-post-form/cdklike-dms-post-form.jsx @@ -39,10 +39,11 @@ import { useSplitTreatments } from "@splitsoftware/splitio-react"; * @param job * @param logsRef * @param mode + * @param allocationsSummary * @returns {JSX.Element} * @constructor */ -export default function CdkLikePostForm({ bodyshop, socket, job, logsRef, mode }) { +export default function CdkLikePostForm({ bodyshop, socket, job, logsRef, mode, allocationsSummary }) { const [form] = Form.useForm(); const { t } = useTranslation(); const [, /*unused*/ setTick] = useState(0); // handy if you need a forceUpdate later @@ -120,15 +121,19 @@ export default function CdkLikePostForm({ bodyshop, socket, job, logsRef, mode } }; // Totals & discrepancy - const totals = socket?.allocationsSummary - ? socket.allocationsSummary.reduce( - (acc, val) => ({ - totalSale: acc.totalSale.add(Dinero(val.sale)), - totalCost: acc.totalCost.add(Dinero(val.cost)) - }), - { totalSale: Dinero(), totalCost: Dinero() } - ) - : { totalSale: Dinero(), totalCost: Dinero() }; + const totals = useMemo(() => { + if (!allocationsSummary || allocationsSummary.length === 0) { + return { totalSale: Dinero(), totalCost: Dinero() }; + } + + return allocationsSummary.reduce( + (acc, val) => ({ + totalSale: acc.totalSale.add(Dinero(val.sale)), + totalCost: acc.totalCost.add(Dinero(val.cost)) + }), + { totalSale: Dinero(), totalCost: Dinero() } + ); + }, [allocationsSummary]); return ( @@ -214,7 +219,7 @@ export default function CdkLikePostForm({ bodyshop, socket, job, logsRef, mode } - + @@ -382,7 +387,10 @@ export default function CdkLikePostForm({ bodyshop, socket, job, logsRef, mode } const payersOk = payers.length > 0 && payers.every((p) => p?.name && p.dms_acctnumber && (p.amount ?? "") !== "" && p.controlnumber); - const nonRrDiscrepancyGate = socket?.allocationsSummary ? discrep.getAmount() !== 0 : true; + + const hasAllocations = allocationsSummary && allocationsSummary.length > 0; + const nonRrDiscrepancyGate = hasAllocations ? discrep.getAmount() !== 0 : true; + const disablePost = !payersOk || nonRrDiscrepancyGate; return ( 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 21dfe66e7..01d92e729 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 @@ -19,20 +19,41 @@ export default connect(mapStateToProps, mapDispatchToProps)(DmsPostForm); * @param socket * @param job * @param logsRef + * @param key + * @param allocationsSummary * @returns {JSX.Element|null} * @constructor */ -export function DmsPostForm({ mode, bodyshop, socket, job, logsRef }) { +export function DmsPostForm({ mode, bodyshop, socket, job, logsRef, key, allocationsSummary }) { switch (mode) { case DMS_MAP.reynolds: - return ; + return ( + + ); // CDK (legacy /ws), Fortellis (CDK-over-WSS), and PBS share the same UI; // we pass mode down so the child can choose the correct event name. case DMS_MAP.fortellis: case DMS_MAP.cdk: case DMS_MAP.pbs: - return ; + return ( + + ); default: return null; diff --git a/client/src/components/dms-post-form/rr-dms-post-form.jsx b/client/src/components/dms-post-form/rr-dms-post-form.jsx index dfd0647e6..eb05eb26a 100644 --- a/client/src/components/dms-post-form/rr-dms-post-form.jsx +++ b/client/src/components/dms-post-form/rr-dms-post-form.jsx @@ -26,10 +26,11 @@ import dayjs from "../../utils/day"; * @param socket * @param job * @param logsRef + * @param allocationsSummary * @returns {JSX.Element} * @constructor */ -export default function RRPostForm({ bodyshop, socket, job, logsRef }) { +export default function RRPostForm({ bodyshop, socket, job, logsRef, allocationsSummary }) { const [form] = Form.useForm(); const { t } = useTranslation(); @@ -113,16 +114,21 @@ export default function RRPostForm({ bodyshop, socket, job, logsRef }) { logsRef?.current?.scrollIntoView({ behavior: "smooth" }); }; - // Discrepancy is ignored for RR; we still show totals for operator context - const totals = socket?.allocationsSummary - ? socket.allocationsSummary.reduce( - (acc, val) => ({ - totalSale: acc.totalSale.add(Dinero(val.sale)), - totalCost: acc.totalCost.add(Dinero(val.cost)) - }), - { totalSale: Dinero(), totalCost: Dinero() } - ) - : { totalSale: Dinero(), totalCost: Dinero() }; + // Discrepancy is ignored for RR; we still show totals for operator context. + // Use the lifted allocationsSummary from the container instead of reading from the socket. + const totals = useMemo(() => { + if (!allocationsSummary || allocationsSummary.length === 0) { + return { totalSale: Dinero(), totalCost: Dinero() }; + } + + return allocationsSummary.reduce( + (acc, val) => ({ + totalSale: acc.totalSale.add(Dinero(val.sale)), + totalCost: acc.totalCost.add(Dinero(val.cost)) + }), + { totalSale: Dinero(), totalCost: Dinero() } + ); + }, [allocationsSummary]); return ( diff --git a/client/src/pages/dms/dms.container.jsx b/client/src/pages/dms/dms.container.jsx index 5b114af18..31b527832 100644 --- a/client/src/pages/dms/dms.container.jsx +++ b/client/src/pages/dms/dms.container.jsx @@ -68,6 +68,7 @@ const DMS_SOCKET_EVENTS = { export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader, insertAuditTrail }) { const { t } = useTranslation(); const [resetAfterReconnect, setResetAfterReconnect] = useState(false); + const [allocationsSummary, setAllocationsSummary] = useState(null); const history = useNavigate(); const search = queryString.parse(useLocation().search); @@ -152,6 +153,7 @@ export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader, inse setLogs([]); setRrOpenRoLimit(false); setrrValidationPending(false); + setAllocationsSummary(null); if (!activeSocket) return; @@ -398,6 +400,7 @@ export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader, inse {!isRrMode ? ( @@ -432,7 +436,14 @@ export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader, inse - +