Merged PR 2257: refactor(representations): isolate finalisation boundary helpers (slice R8)

refactor(representations): isolate finalisation boundary helpers (slice R8)

Related work items: #22441
This commit is contained in:
Robert Bond
2026-04-17 13:23:55 +00:00
parent 7713fc3724
commit b6ca73d76a
3 changed files with 182 additions and 95 deletions
+33 -95
View File
@@ -46,6 +46,11 @@ import {
resolveInitialCaseDataSources, resolveInitialCaseDataSources,
resolveSubmitCaseDetailsSource resolveSubmitCaseDetailsSource
} from "./utils/dataResolution"; } from "./utils/dataResolution";
import {
buildRepresentationUpdateBody,
buildSubmitEnrichedValues,
runFinalisationSequence
} from "./utils/finalisationBoundary";
let MakeRepresentation = (props) => { let MakeRepresentation = (props) => {
let { t } = useTranslation(); let { t } = useTranslation();
@@ -871,24 +876,10 @@ let MakeRepresentation = (props) => {
] ]
}; };
let updateBody = setRepFileName(formattedValues); const updateBody = buildRepresentationUpdateBody({
formattedValues,
// Clean and sanitize the body setRepFileName
const sanitizedBody = Object.entries(updateBody).reduce( });
(acc, [key, val]) => {
if (val !== null && val !== undefined && !key.startsWith("_")) {
acc[key] = val;
}
return acc;
},
{}
);
// Normalize "Yes"/"No" to booleans
const stringified = JSON.stringify(sanitizedBody)
.replace(/:"Yes"/g, ":true")
.replace(/:"No"/g, ":false");
updateBody = JSON.parse(stringified);
const sendEmailIfNeeded = () => { const sendEmailIfNeeded = () => {
if (sendSavedEmail) { if (sendSavedEmail) {
@@ -976,23 +967,6 @@ let MakeRepresentation = (props) => {
) { ) {
const formValues = formObj.representationForm.values; const formValues = formObj.representationForm.values;
const buildSiteAddress = () => {
const {
pinswg_siteaddressline1,
pinswg_siteaddressline2,
pinswg_siteaddresstown,
pinswg_siteaddresspostcode
} = detailsObj;
return [
pinswg_siteaddressline1,
pinswg_siteaddressline2,
pinswg_siteaddresstown,
pinswg_siteaddresspostcode
]
.filter(Boolean)
.join(" ");
};
const representationCapacity = const representationCapacity =
router.query?.state === "edit" router.query?.state === "edit"
? props.props.currentView.representationCapacity ? props.props.currentView.representationCapacity
@@ -1000,55 +974,19 @@ let MakeRepresentation = (props) => {
? "LPA" ? "LPA"
: values.representationCapacity; : values.representationCapacity;
Object.assign(values, { Object.assign(
...formValues, values,
containerID: props.props.accountDetails.containerID, buildSubmitEnrichedValues({
appealType: formValues,
appealType || caseReferenceObj.repDetails.appealType, accountDetails,
incidentID: appealType,
caseReferenceObj.incidentid || caseReferenceObj,
caseReferenceObj.repDetails.incidentid, detailsObj,
caseRef: caseReferenceObj.currentReference, resultsObj,
casereference: caseReferenceObj.currentReference, representationCapacity,
pinswg_questionnaireduedate: locale: router.locale
detailsObj.pinswg_questionnaireduedate, })
pinswg_endofrepresentationperiod: );
detailsObj.pinswg_endofrepresentationperiod,
pinswg_applicationacceptedasvalid:
detailsObj.pinswg_applicationacceptedasvalid,
pinswg_statementduedate: detailsObj.pinswg_statementduedate,
pinswg_statementsduedate: detailsObj.pinswg_statementsduedate,
pinswg_finalcommentsduedate:
detailsObj.pinswg_finalcommentsduedate,
pinswg_name: caseReferenceObj.currentReference,
firstname: accountDetails.firstname,
lastname: accountDetails.lastname,
emailAddress: accountDetails.emailaddress1,
siteAddress: buildSiteAddress(),
pinswg_siteaddressline1: detailsObj.pinswg_siteaddressline1,
pinswg_siteaddressline2: detailsObj.pinswg_siteaddressline2,
pinswg_siteaddresstown: detailsObj.pinswg_siteaddresstown,
pinswg_siteaddresspostcode:
detailsObj.pinswg_siteaddresspostcode,
pinswg_lpareference: resultsObj?.pinswg_lpareference,
_pinswg_appellant_value: detailsObj._pinswg_appellant_value,
"_pinswg_appellant_value@OData.Community.Display.V1.FormattedValue":
detailsObj[
"_pinswg_appellant_value@OData.Community.Display.V1.FormattedValue"
],
_pinswg_localplanningauthority_value:
detailsObj._pinswg_localplanningauthority_value ||
detailsObj._pinswg_associatedlpa_value,
"_pinswg_localplanningauthority_value@OData.Community.Display.V1.FormattedValue":
detailsObj[
"_pinswg_localplanningauthority_value@OData.Community.Display.V1.FormattedValue"
] ||
detailsObj[
"_pinswg_associatedlpa_value@OData.Community.Display.V1.FormattedValue"
],
representationCapacity,
locale: router.locale
});
} }
const shouldSubmit = !currentView.representationSubmit; const shouldSubmit = !currentView.representationSubmit;
@@ -1102,19 +1040,19 @@ let MakeRepresentation = (props) => {
} }
// Final PDF & upload // Final PDF & upload
setFinaliseAppealProcess(true); runFinalisationSequence({
generateRepPDF(
values, values,
props.props.accountDetails.containerID, containerID: props.props.accountDetails.containerID,
props.currentView.caseReference.currentReference currentReference:
).then((data) => { props.currentView.caseReference.currentReference,
setFinaliseAppealProcess,
generateRepPDF,
updateLinks,
uploadRepresentationFiles,
setRepresentationSubmit,
setRepresentationSubmitConfirmation
}).then((data) => {
console.log(data, ""); console.log(data, "");
if (data.status === "success") {
values = updateLinks(values);
uploadRepresentationFiles(values);
setRepresentationSubmit(true);
setRepresentationSubmitConfirmation(true);
}
}); });
} }
}; };
@@ -0,0 +1,126 @@
export const buildRepresentationUpdateBody = ({
formattedValues,
setRepFileName
}) => {
let updateBody = setRepFileName(formattedValues);
const sanitizedBody = Object.entries(updateBody).reduce(
(acc, [key, val]) => {
if (val !== null && val !== undefined && !key.startsWith("_")) {
acc[key] = val;
}
return acc;
},
{}
);
const stringified = JSON.stringify(sanitizedBody)
.replace(/:"Yes"/g, ":true")
.replace(/:"No"/g, ":false");
updateBody = JSON.parse(stringified);
return updateBody;
};
export const buildSubmitEnrichedValues = ({
formValues,
accountDetails,
appealType,
caseReferenceObj,
detailsObj,
resultsObj,
representationCapacity,
locale
}) => {
const buildSiteAddress = () => {
const {
pinswg_siteaddressline1,
pinswg_siteaddressline2,
pinswg_siteaddresstown,
pinswg_siteaddresspostcode
} = detailsObj;
return [
pinswg_siteaddressline1,
pinswg_siteaddressline2,
pinswg_siteaddresstown,
pinswg_siteaddresspostcode
]
.filter(Boolean)
.join(" ");
};
return {
...formValues,
containerID: accountDetails.containerID,
appealType: appealType || caseReferenceObj.repDetails.appealType,
incidentID:
caseReferenceObj.incidentid ||
caseReferenceObj.repDetails.incidentid,
caseRef: caseReferenceObj.currentReference,
casereference: caseReferenceObj.currentReference,
pinswg_questionnaireduedate: detailsObj.pinswg_questionnaireduedate,
pinswg_endofrepresentationperiod:
detailsObj.pinswg_endofrepresentationperiod,
pinswg_applicationacceptedasvalid:
detailsObj.pinswg_applicationacceptedasvalid,
pinswg_statementduedate: detailsObj.pinswg_statementduedate,
pinswg_statementsduedate: detailsObj.pinswg_statementsduedate,
pinswg_finalcommentsduedate: detailsObj.pinswg_finalcommentsduedate,
pinswg_name: caseReferenceObj.currentReference,
firstname: accountDetails.firstname,
lastname: accountDetails.lastname,
emailAddress: accountDetails.emailaddress1,
siteAddress: buildSiteAddress(),
pinswg_siteaddressline1: detailsObj.pinswg_siteaddressline1,
pinswg_siteaddressline2: detailsObj.pinswg_siteaddressline2,
pinswg_siteaddresstown: detailsObj.pinswg_siteaddresstown,
pinswg_siteaddresspostcode: detailsObj.pinswg_siteaddresspostcode,
pinswg_lpareference: resultsObj?.pinswg_lpareference,
_pinswg_appellant_value: detailsObj._pinswg_appellant_value,
"_pinswg_appellant_value@OData.Community.Display.V1.FormattedValue":
detailsObj[
"_pinswg_appellant_value@OData.Community.Display.V1.FormattedValue"
],
_pinswg_localplanningauthority_value:
detailsObj._pinswg_localplanningauthority_value ||
detailsObj._pinswg_associatedlpa_value,
"_pinswg_localplanningauthority_value@OData.Community.Display.V1.FormattedValue":
detailsObj[
"_pinswg_localplanningauthority_value@OData.Community.Display.V1.FormattedValue"
] ||
detailsObj[
"_pinswg_associatedlpa_value@OData.Community.Display.V1.FormattedValue"
],
representationCapacity,
locale
};
};
export const runFinalisationSequence = ({
values,
containerID,
currentReference,
setFinaliseAppealProcess,
generateRepPDF,
updateLinks,
uploadRepresentationFiles,
setRepresentationSubmit,
setRepresentationSubmitConfirmation
}) => {
setFinaliseAppealProcess(true);
return generateRepPDF(values, containerID, currentReference).then(
(data) => {
if (data.status === "success") {
const updatedValues = updateLinks(values);
uploadRepresentationFiles(updatedValues);
setRepresentationSubmit(true);
setRepresentationSubmitConfirmation(true);
}
return data;
}
);
};
@@ -321,6 +321,29 @@ Isolate submission logic.
**Risk Level:** HIGH **Risk Level:** HIGH
(Must only be done after earlier slices stabilised) (Must only be done after earlier slices stabilised)
**Completion notes (this slice):**
- Implemented on feature branch created from `refactor`: `rep-slice-r8-finalisation-boundary`.
- Added a small representation-specific helper module:
- `components/case/representation/utils/finalisationBoundary.js`
- Extracted only approved R8 seams:
- `buildRepresentationUpdateBody(...)` (pure shaping)
- `buildSubmitEnrichedValues(...)` (pure shaping)
- `runFinalisationSequence(...)` (ultra-thin orchestration wrapper)
- Wired `components/case/representation/index.js` to use the new helpers while preserving parent-owned business branching.
- Explicitly preserved R8 constraints:
- no completion-side-effect extraction from `representationComplete.js`
- no route/query changes
- no payload contract changes
- no validation logic changes
- no file upload/PDF/email behaviour changes
- no sequencing drift in finalisation success path
**Validation evidence:**
- `npm run lint` completed (warnings only, no new errors).
- `npm run test:reps` completed: **7 passed (4.4m)**.
--- ---
## Rules ## Rules