feature/IO-3357-Reynolds-and-Reynolds-DMS-API-Integration - Fixes to Caching of allocations summary

This commit is contained in:
Dave
2025-11-28 11:29:48 -05:00
parent d885bac7d0
commit c1e3c08652
6 changed files with 93 additions and 31 deletions

View File

@@ -24,10 +24,11 @@ export default connect(mapStateToProps, mapDispatchToProps)(DmsAllocationsSummar
* @param bodyshop * @param bodyshop
* @param jobId * @param jobId
* @param title * @param title
* @param onAllocationsChange
* @returns {JSX.Element} * @returns {JSX.Element}
* @constructor * @constructor
*/ */
export function DmsAllocationsSummary({ mode, socket, bodyshop, jobId, title }) { export function DmsAllocationsSummary({ mode, socket, bodyshop, jobId, title, onAllocationsChange }) {
const { t } = useTranslation(); const { t } = useTranslation();
const [allocationsSummary, setAllocationsSummary] = useState([]); const [allocationsSummary, setAllocationsSummary] = useState([]);
@@ -48,11 +49,17 @@ export function DmsAllocationsSummary({ mode, socket, bodyshop, jobId, title })
setAllocationsSummary(list); setAllocationsSummary(list);
// Preserve side-channel used by the post form for discrepancy checks // Preserve side-channel used by the post form for discrepancy checks
socket.allocationsSummary = list; socket.allocationsSummary = list;
if (onAllocationsChange) onAllocationsChange(list);
}); });
} catch { } catch {
// Best-effort; leave table empty on error // Best-effort; leave table empty on error
setAllocationsSummary([]); setAllocationsSummary([]);
socket && (socket.allocationsSummary = []); if (socket) {
socket.allocationsSummary = [];
}
if (onAllocationsChange) {
onAllocationsChange([]);
}
} }
}, [socket, jobId, mode, allocationsEvent]); }, [socket, jobId, mode, allocationsEvent]);

View File

@@ -51,7 +51,7 @@ function normalizeJobAllocations(ack) {
* is now done on the backend via buildRogogFromAllocations/buildRolaborFromRogog. * is now done on the backend via buildRogogFromAllocations/buildRolaborFromRogog.
* This component just renders the preview from `ack.rogg` / `ack.rolabor`. * 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 { t } = useTranslation();
const [roggPreview, setRoggPreview] = useState(null); const [roggPreview, setRoggPreview] = useState(null);
const [rolaborPreview, setRolaborPreview] = useState(null); const [rolaborPreview, setRolaborPreview] = useState(null);
@@ -70,6 +70,9 @@ export function RrAllocationsSummary({ socket, bodyshop, jobId, title }) {
socket.allocationsSummary = []; socket.allocationsSummary = [];
socket.rrAllocationsRaw = ack; socket.rrAllocationsRaw = ack;
} }
if (onAllocationsChange) {
onAllocationsChange([]);
}
return; return;
} }
@@ -83,6 +86,9 @@ export function RrAllocationsSummary({ socket, bodyshop, jobId, title }) {
socket.allocationsSummary = jobAllocRows; socket.allocationsSummary = jobAllocRows;
socket.rrAllocationsRaw = ack; socket.rrAllocationsRaw = ack;
} }
if (onAllocationsChange) {
onAllocationsChange(jobAllocRows);
}
}); });
} catch { } catch {
setRoggPreview(null); setRoggPreview(null);
@@ -91,8 +97,11 @@ export function RrAllocationsSummary({ socket, bodyshop, jobId, title }) {
if (socket) { if (socket) {
socket.allocationsSummary = []; socket.allocationsSummary = [];
} }
if (onAllocationsChange) {
onAllocationsChange([]);
}
} }
}, [socket, jobId, t]); }, [socket, jobId, t, onAllocationsChange]);
useEffect(() => { useEffect(() => {
fetchAllocations(); fetchAllocations();

View File

@@ -39,10 +39,11 @@ import { useSplitTreatments } from "@splitsoftware/splitio-react";
* @param job * @param job
* @param logsRef * @param logsRef
* @param mode * @param mode
* @param allocationsSummary
* @returns {JSX.Element} * @returns {JSX.Element}
* @constructor * @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 [form] = Form.useForm();
const { t } = useTranslation(); const { t } = useTranslation();
const [, /*unused*/ setTick] = useState(0); // handy if you need a forceUpdate later 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 // Totals & discrepancy
const totals = socket?.allocationsSummary const totals = useMemo(() => {
? socket.allocationsSummary.reduce( if (!allocationsSummary || allocationsSummary.length === 0) {
(acc, val) => ({ return { totalSale: Dinero(), totalCost: Dinero() };
totalSale: acc.totalSale.add(Dinero(val.sale)), }
totalCost: acc.totalCost.add(Dinero(val.cost))
}), return allocationsSummary.reduce(
{ totalSale: Dinero(), totalCost: Dinero() } (acc, val) => ({
) totalSale: acc.totalSale.add(Dinero(val.sale)),
: { totalSale: Dinero(), totalCost: Dinero() }; totalCost: acc.totalCost.add(Dinero(val.cost))
}),
{ totalSale: Dinero(), totalCost: Dinero() }
);
}, [allocationsSummary]);
return ( return (
<Card title={t("jobs.labels.dms.postingform")}> <Card title={t("jobs.labels.dms.postingform")}>
@@ -214,7 +219,7 @@ export default function CdkLikePostForm({ bodyshop, socket, job, logsRef, mode }
<Row gutter={[16, 12]}> <Row gutter={[16, 12]}>
<Col span={24}> <Col span={24}>
<Form.Item name="story" label={t("jobs.fields.dms.story")} rules={[{ required: true }]}> <Form.Item name="story" label={t("jobs.fields.dms.story")} rules={[{ required: true }]}>
<Input.TextArea maxLength={Fortellis.treatment === "on" ? 40 : 240} showCount/> <Input.TextArea maxLength={Fortellis.treatment === "on" ? 40 : 240} showCount />
</Form.Item> </Form.Item>
</Col> </Col>
</Row> </Row>
@@ -382,7 +387,10 @@ export default function CdkLikePostForm({ bodyshop, socket, job, logsRef, mode }
const payersOk = const payersOk =
payers.length > 0 && payers.length > 0 &&
payers.every((p) => p?.name && p.dms_acctnumber && (p.amount ?? "") !== "" && p.controlnumber); 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; const disablePost = !payersOk || nonRrDiscrepancyGate;
return ( return (

View File

@@ -19,20 +19,41 @@ export default connect(mapStateToProps, mapDispatchToProps)(DmsPostForm);
* @param socket * @param socket
* @param job * @param job
* @param logsRef * @param logsRef
* @param key
* @param allocationsSummary
* @returns {JSX.Element|null} * @returns {JSX.Element|null}
* @constructor * @constructor
*/ */
export function DmsPostForm({ mode, bodyshop, socket, job, logsRef }) { export function DmsPostForm({ mode, bodyshop, socket, job, logsRef, key, allocationsSummary }) {
switch (mode) { switch (mode) {
case DMS_MAP.reynolds: case DMS_MAP.reynolds:
return <RRPostForm bodyshop={bodyshop} socket={socket} job={job} logsRef={logsRef} />; return (
<RRPostForm
bodyshop={bodyshop}
socket={socket}
job={job}
logsRef={logsRef}
key={key}
allocationsSummary={allocationsSummary}
/>
);
// CDK (legacy /ws), Fortellis (CDK-over-WSS), and PBS share the same UI; // 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. // we pass mode down so the child can choose the correct event name.
case DMS_MAP.fortellis: case DMS_MAP.fortellis:
case DMS_MAP.cdk: case DMS_MAP.cdk:
case DMS_MAP.pbs: case DMS_MAP.pbs:
return <CdkLikePostForm mode={mode} bodyshop={bodyshop} socket={socket} job={job} logsRef={logsRef} />; return (
<CdkLikePostForm
mode={mode}
bodyshop={bodyshop}
socket={socket}
job={job}
logsRef={logsRef}
key={key}
allocationsSummary={allocationsSummary}
/>
);
default: default:
return null; return null;

View File

@@ -26,10 +26,11 @@ import dayjs from "../../utils/day";
* @param socket * @param socket
* @param job * @param job
* @param logsRef * @param logsRef
* @param allocationsSummary
* @returns {JSX.Element} * @returns {JSX.Element}
* @constructor * @constructor
*/ */
export default function RRPostForm({ bodyshop, socket, job, logsRef }) { export default function RRPostForm({ bodyshop, socket, job, logsRef, allocationsSummary }) {
const [form] = Form.useForm(); const [form] = Form.useForm();
const { t } = useTranslation(); const { t } = useTranslation();
@@ -113,16 +114,21 @@ export default function RRPostForm({ bodyshop, socket, job, logsRef }) {
logsRef?.current?.scrollIntoView({ behavior: "smooth" }); logsRef?.current?.scrollIntoView({ behavior: "smooth" });
}; };
// Discrepancy is ignored for RR; we still show totals for operator context // Discrepancy is ignored for RR; we still show totals for operator context.
const totals = socket?.allocationsSummary // Use the lifted allocationsSummary from the container instead of reading from the socket.
? socket.allocationsSummary.reduce( const totals = useMemo(() => {
(acc, val) => ({ if (!allocationsSummary || allocationsSummary.length === 0) {
totalSale: acc.totalSale.add(Dinero(val.sale)), return { totalSale: Dinero(), totalCost: Dinero() };
totalCost: acc.totalCost.add(Dinero(val.cost)) }
}),
{ totalSale: Dinero(), totalCost: Dinero() } return allocationsSummary.reduce(
) (acc, val) => ({
: { totalSale: Dinero(), totalCost: Dinero() }; totalSale: acc.totalSale.add(Dinero(val.sale)),
totalCost: acc.totalCost.add(Dinero(val.cost))
}),
{ totalSale: Dinero(), totalCost: Dinero() }
);
}, [allocationsSummary]);
return ( return (
<Card title={t("jobs.labels.dms.postingform")}> <Card title={t("jobs.labels.dms.postingform")}>

View File

@@ -68,6 +68,7 @@ const DMS_SOCKET_EVENTS = {
export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader, insertAuditTrail }) { export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader, insertAuditTrail }) {
const { t } = useTranslation(); const { t } = useTranslation();
const [resetAfterReconnect, setResetAfterReconnect] = useState(false); const [resetAfterReconnect, setResetAfterReconnect] = useState(false);
const [allocationsSummary, setAllocationsSummary] = useState(null);
const history = useNavigate(); const history = useNavigate();
const search = queryString.parse(useLocation().search); const search = queryString.parse(useLocation().search);
@@ -152,6 +153,7 @@ export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader, inse
setLogs([]); setLogs([]);
setRrOpenRoLimit(false); setRrOpenRoLimit(false);
setrrValidationPending(false); setrrValidationPending(false);
setAllocationsSummary(null);
if (!activeSocket) return; if (!activeSocket) return;
@@ -398,6 +400,7 @@ export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader, inse
{!isRrMode ? ( {!isRrMode ? (
<DmsAllocationsSummary <DmsAllocationsSummary
key={resetKey} key={resetKey}
onAllocationsChange={setAllocationsSummary}
title={ title={
<span> <span>
<Link <Link
@@ -415,6 +418,7 @@ export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader, inse
) : ( ) : (
<RrAllocationsSummary <RrAllocationsSummary
key={resetKey} key={resetKey}
onAllocationsChange={setAllocationsSummary}
title={ title={
<span> <span>
<Link to={`/manage/jobs/${data && data.jobs_by_pk.id}`}> <Link to={`/manage/jobs/${data && data.jobs_by_pk.id}`}>
@@ -432,7 +436,14 @@ export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader, inse
</Col> </Col>
<Col md={24} lg={14} className="dms-equal-height-col"> <Col md={24} lg={14} className="dms-equal-height-col">
<DmsPostForm key={resetKey} socket={activeSocket} job={data?.jobs_by_pk} logsRef={logsRef} mode={mode} /> <DmsPostForm
key={resetKey}
socket={activeSocket}
job={data?.jobs_by_pk}
logsRef={logsRef}
mode={mode}
allocationsSummary={allocationsSummary}
/>
</Col> </Col>
<DmsCustomerSelector <DmsCustomerSelector