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,
resolveSubmitCaseDetailsSource
} from "./utils/dataResolution";
import {
buildRepresentationUpdateBody,
buildSubmitEnrichedValues,
runFinalisationSequence
} from "./utils/finalisationBoundary";
let MakeRepresentation = (props) => {
let { t } = useTranslation();
@@ -871,24 +876,10 @@ let MakeRepresentation = (props) => {
]
};
let updateBody = setRepFileName(formattedValues);
// Clean and sanitize the body
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 updateBody = buildRepresentationUpdateBody({
formattedValues,
setRepFileName
});
const sendEmailIfNeeded = () => {
if (sendSavedEmail) {
@@ -976,23 +967,6 @@ let MakeRepresentation = (props) => {
) {
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 =
router.query?.state === "edit"
? props.props.currentView.representationCapacity
@@ -1000,55 +974,19 @@ let MakeRepresentation = (props) => {
? "LPA"
: values.representationCapacity;
Object.assign(values, {
...formValues,
containerID: props.props.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: router.locale
});
Object.assign(
values,
buildSubmitEnrichedValues({
formValues,
accountDetails,
appealType,
caseReferenceObj,
detailsObj,
resultsObj,
representationCapacity,
locale: router.locale
})
);
}
const shouldSubmit = !currentView.representationSubmit;
@@ -1102,19 +1040,19 @@ let MakeRepresentation = (props) => {
}
// Final PDF & upload
setFinaliseAppealProcess(true);
generateRepPDF(
runFinalisationSequence({
values,
props.props.accountDetails.containerID,
props.currentView.caseReference.currentReference
).then((data) => {
containerID: props.props.accountDetails.containerID,
currentReference:
props.currentView.caseReference.currentReference,
setFinaliseAppealProcess,
generateRepPDF,
updateLinks,
uploadRepresentationFiles,
setRepresentationSubmit,
setRepresentationSubmitConfirmation
}).then((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
(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