rrScratch3 - Progress Commit

This commit is contained in:
Dave
2025-11-28 14:41:00 -05:00
parent c1e3c08652
commit 827f1c2c40
6 changed files with 221 additions and 49 deletions

View File

@@ -51,17 +51,20 @@ 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, onAllocationsChange }) {
export function RrAllocationsSummary({ socket, bodyshop, jobId, title, onAllocationsChange, opCode }) {
const { t } = useTranslation();
const [roggPreview, setRoggPreview] = useState(null);
const [rolaborPreview, setRolaborPreview] = useState(null);
const [error, setError] = useState(null);
// Prefer the user-selected OpCode (from DmsContainer), fall back to config default
const effectiveOpCode = useMemo(() => opCode || resolveRROpCodeFromBodyshop(bodyshop), [opCode, bodyshop]);
const fetchAllocations = useCallback(() => {
if (!socket || !jobId) return;
try {
socket.emit("rr-calculate-allocations", jobId, (ack) => {
socket.emit("rr-calculate-allocations", { jobId, opCode: effectiveOpCode }, (ack) => {
if (ack && ack.ok === false) {
setRoggPreview(null);
setRolaborPreview(null);
@@ -101,14 +104,12 @@ export function RrAllocationsSummary({ socket, bodyshop, jobId, title, onAllocat
onAllocationsChange([]);
}
}
}, [socket, jobId, t, onAllocationsChange]);
}, [socket, jobId, t, onAllocationsChange, effectiveOpCode]);
useEffect(() => {
fetchAllocations();
}, [fetchAllocations]);
const opCode = resolveRROpCodeFromBodyshop(bodyshop);
const segmentLabelMap = {
partsExtras: "Parts/Extras",
laborTaxable: "Taxable Labor",
@@ -117,8 +118,11 @@ export function RrAllocationsSummary({ socket, bodyshop, jobId, title, onAllocat
const roggRows = useMemo(() => {
if (!roggPreview || !Array.isArray(roggPreview.ops)) return [];
const rows = [];
roggPreview.ops.forEach((op) => {
const rowOpCode = opCode || op.opCode;
(op.lines || []).forEach((line, idx) => {
const baseDesc = line.itemDesc;
const segmentKind = op.segmentKind;
@@ -128,7 +132,7 @@ export function RrAllocationsSummary({ socket, bodyshop, jobId, title, onAllocat
rows.push({
key: `${op.jobNo}-${idx}`,
opCode: op.opCode,
opCode: rowOpCode,
jobNo: op.jobNo,
breakOut: line.breakOut,
itemType: line.itemType,
@@ -145,22 +149,27 @@ export function RrAllocationsSummary({ socket, bodyshop, jobId, title, onAllocat
});
});
return rows;
}, [roggPreview]);
}, [roggPreview, opCode]);
const rolaborRows = useMemo(() => {
if (!rolaborPreview || !Array.isArray(rolaborPreview.ops)) return [];
return rolaborPreview.ops.map((op, idx) => ({
key: `${op.jobNo}-${idx}`,
opCode: op.opCode,
jobNo: op.jobNo,
custPayTypeFlag: op.custPayTypeFlag,
custTxblNtxblFlag: op.custTxblNtxblFlag,
payType: op.bill?.payType,
amtType: op.amount?.amtType,
custPrice: op.amount?.custPrice,
totalAmt: op.amount?.totalAmt
}));
}, [rolaborPreview]);
return rolaborPreview.ops.map((op, idx) => {
const rowOpCode = opCode || op.opCode;
return {
key: `${op.jobNo}-${idx}`,
opCode: rowOpCode,
jobNo: op.jobNo,
custPayTypeFlag: op.custPayTypeFlag,
custTxblNtxblFlag: op.custTxblNtxblFlag,
payType: op.bill?.payType,
amtType: op.amount?.amtType,
custPrice: op.amount?.custPrice,
totalAmt: op.amount?.totalAmt
};
});
}, [rolaborPreview, opCode]);
// Totals for ROGOG (sum custPrice + dlrCost over all lines)
const roggTotals = useMemo(() => {
@@ -221,9 +230,10 @@ export function RrAllocationsSummary({ socket, bodyshop, jobId, title, onAllocat
children: (
<>
<Typography.Paragraph type="secondary" style={{ marginBottom: 8 }}>
OpCode: <strong>{opCode}</strong>. Only centers with RR GOG mapping (rr_gogcode &amp; rr_item_type) are
included. Totals below reflect exactly what will be sent in ROGOG.
OpCode: <strong>{effectiveOpCode}</strong>. Only centers with RR GOG mapping (rr_gogcode &amp; rr_item_type)
are included. Totals below reflect exactly what will be sent in ROGOG.
</Typography.Paragraph>
<Table
pagination={false}
columns={roggColumns}

View File

@@ -21,10 +21,22 @@ export default connect(mapStateToProps, mapDispatchToProps)(DmsPostForm);
* @param logsRef
* @param key
* @param allocationsSummary
* @param rrOpCodeParts
* @param onChangeRrOpCodeParts
* @returns {JSX.Element|null}
* @constructor
*/
export function DmsPostForm({ mode, bodyshop, socket, job, logsRef, key, allocationsSummary }) {
export function DmsPostForm({
mode,
bodyshop,
socket,
job,
logsRef,
key,
allocationsSummary,
rrOpCodeParts,
onChangeRrOpCodeParts
}) {
switch (mode) {
case DMS_MAP.reynolds:
return (
@@ -35,6 +47,8 @@ export function DmsPostForm({ mode, bodyshop, socket, job, logsRef, key, allocat
logsRef={logsRef}
key={key}
allocationsSummary={allocationsSummary}
opCodeParts={rrOpCodeParts}
onChangeOpCodeParts={onChangeRrOpCodeParts}
/>
);

View File

@@ -27,10 +27,20 @@ import dayjs from "../../utils/day";
* @param job
* @param logsRef
* @param allocationsSummary
* @param opCodeParts
* @param onChangeOpCodeParts
* @returns {JSX.Element}
* @constructor
*/
export default function RRPostForm({ bodyshop, socket, job, logsRef, allocationsSummary }) {
export default function RRPostForm({
bodyshop,
socket,
job,
logsRef,
allocationsSummary,
opCodeParts,
onChangeOpCodeParts
}) {
const [form] = Form.useForm();
const { t } = useTranslation();
@@ -98,19 +108,54 @@ export default function RRPostForm({ bodyshop, socket, job, logsRef, allocations
: job.v_model_yr)) ||
2019
}-01-01`
)
),
opPrefix: opCodeParts?.prefix ?? "",
opBase: opCodeParts?.base ?? "",
opSuffix: opCodeParts?.suffix ?? ""
}),
[job, t]
[job, t, opCodeParts]
);
// Keep the RR OpCode parts in sync with DmsContainer state
const opPrefixWatch = Form.useWatch("opPrefix", form);
const opBaseWatch = Form.useWatch("opBase", form);
const opSuffixWatch = Form.useWatch("opSuffix", form);
useEffect(() => {
if (!onChangeOpCodeParts) return;
onChangeOpCodeParts({
prefix: opPrefixWatch || "",
base: opBaseWatch || "",
suffix: opSuffixWatch || ""
});
}, [opPrefixWatch, opBaseWatch, opSuffixWatch, onChangeOpCodeParts]);
const handleFinish = (values) => {
if (!socket) return;
const { opPrefix, opBase, opSuffix, ...rest } = values;
const combinedOpCode = `${opPrefix || ""}${opBase || ""}${opSuffix || ""}`.trim();
const txEnvelope = {
...rest,
opPrefix,
opBase,
opSuffix
};
if (combinedOpCode) {
txEnvelope.opCode = combinedOpCode;
}
socket.emit("rr-export-job", {
bodyshopId: bodyshop?.id,
jobId: job.id,
job,
txEnvelope: values
txEnvelope
});
logsRef?.current?.scrollIntoView({ behavior: "smooth" });
};
@@ -177,10 +222,39 @@ export default function RRPostForm({ bodyshop, socket, job, logsRef, allocations
</Form.Item>
</Col>
{/* Make Override */}
{/* RR OpCode (prefix / base / suffix) */}
<Col xs={24} sm={12} md={12} lg={8}>
<Form.Item name="makeOverride" label={t("jobs.fields.dms.make_override")}>
<Input allowClear placeholder={t("general.actions.optional")} />
<Form.Item label={t("jobs.fields.dms.rr_opcode", "RR OpCode")}>
<Space.Compact block>
<Form.Item name="opPrefix" noStyle>
<Input
allowClear
maxLength={4}
style={{ width: "30%" }}
placeholder={t("jobs.fields.dms.rr_opcode_prefix", "Prefix")}
/>
</Form.Item>
<Form.Item
name="opBase"
noStyle
rules={[{ required: true, message: t("general.validation.required") }]}
>
<Input
allowClear
maxLength={10}
style={{ width: "40%" }}
placeholder={t("jobs.fields.dms.rr_opcode_base", "Base")}
/>
</Form.Item>
<Form.Item name="opSuffix" noStyle>
<Input
allowClear
maxLength={4}
style={{ width: "30%" }}
placeholder={t("jobs.fields.dms.rr_opcode_suffix", "Suffix")}
/>
</Form.Item>
</Space.Compact>
</Form.Item>
</Col>