Skip to content

Commit e7716c9

Browse files
authored
[COMP-717] Ability to reopen an IR (#645)
* COMP-717 ability to reopen an IR * COMP-717 linting
1 parent e2dd771 commit e7716c9

File tree

5 files changed

+131
-9
lines changed

5 files changed

+131
-9
lines changed

compliance-api/src/compliance_api/schemas/inspection_record.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ class ResetInspectionRecordFieldSchema(BaseSchema):
116116
"preliminary_review_details",
117117
"finding_statement",
118118
"enforcement_summary",
119+
"date_issued",
119120
]
120121
),
121122
metadata={"description": "The field to reset"},

compliance-api/src/compliance_api/services/inspection.py

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1377,15 +1377,8 @@ def _build_query_for_enforcement_actions_and_requirement_details(inspection_ids)
13771377
return results
13781378

13791379

1380-
def _bulk_fetch_enforcement_actions_and_requirement_details(
1381-
inspection_ids,
1382-
): # pylint: disable=too-many-locals,too-many-branches
1383-
"""Bulk fetch all enforcement actions and build requirement details in a single optimized query.
1384-
1385-
Returns a dict with two keys per inspection:
1386-
- 'enforcement_actions': dict of enforcement lists (orders, warning_letters, etc.)
1387-
- 'requirement_details': list of requirement detail objects
1388-
"""
1380+
def initialize_result_data(inspection_ids):
1381+
"""Initialize the result data structure for _bulk_fetch_enforcement_actions_and_requirement_details."""
13891382
result_data = {}
13901383
# Initialize data structure for each inspection
13911384
for inspection_id in inspection_ids:
@@ -1400,6 +1393,19 @@ def _bulk_fetch_enforcement_actions_and_requirement_details(
14001393
},
14011394
"requirement_details": [],
14021395
}
1396+
return result_data
1397+
1398+
1399+
def _bulk_fetch_enforcement_actions_and_requirement_details(
1400+
inspection_ids,
1401+
): # pylint: disable=too-many-locals,too-many-branches
1402+
"""Bulk fetch all enforcement actions and build requirement details in a single optimized query.
1403+
1404+
Returns a dict with two keys per inspection:
1405+
- 'enforcement_actions': dict of enforcement lists (orders, warning_letters, etc.)
1406+
- 'requirement_details': list of requirement detail objects
1407+
"""
1408+
result_data = initialize_result_data(inspection_ids)
14031409

14041410
if not inspection_ids:
14051411
return result_data

compliance-api/src/compliance_api/services/inspection_record/inspection_record.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,8 @@ def reset_field(
251251
ir_data = ir_builder.build_finding_statement().build()
252252
elif field_name == "enforcement_summary":
253253
ir_data = ir_builder.build_enforcement_summary().build()
254+
elif field_name == "date_issued":
255+
ir_data = {"date_issued": None}
254256
change_info[f"{field_name}_changed"] = False
255257
update_data = {
256258
field_name: ir_data.get(field_name),
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import { DialogContent, Typography } from "@mui/material";
2+
import ModalTitleBar from "@/components/Shared/Modals/ModalTitleBar";
3+
import ModalActions from "@/components/Shared/Modals/ModalActions";
4+
import { FC } from "react";
5+
import {
6+
useResetInspectionRecord,
7+
useUpdateInspectionRecord,
8+
} from "@/hooks/useInspectionReports";
9+
import { useReportStore } from "./reportStore";
10+
import { InspectionRecord } from "@/models/InspectionRecord";
11+
import { IRProgressEnum } from "@/utils/constants";
12+
import { notify } from "@/store/snackbarStore";
13+
14+
type ReopenIRModalProps = {
15+
onSubmit: (message: string) => void;
16+
};
17+
18+
const ReopenIRModal: FC<ReopenIRModalProps> = ({ onSubmit }) => {
19+
const { inspectionData, inspectionReportsData, setInspectionReportsData } =
20+
useReportStore();
21+
22+
const handleOnUpdateSuccess = (data: InspectionRecord) => {
23+
setInspectionReportsData(data);
24+
onSubmit("Inspection Record Reopened");
25+
};
26+
27+
const handleOnResetSuccess = (data: InspectionRecord) => {
28+
setInspectionReportsData(data);
29+
notify.success("Enforcement summary updated");
30+
};
31+
32+
const { mutate: updateInspectionRecord, isPending } =
33+
useUpdateInspectionRecord(handleOnUpdateSuccess);
34+
35+
const { mutate: resetInspectionRecord } =
36+
useResetInspectionRecord(handleOnResetSuccess);
37+
38+
const onSubmitHandler = () => {
39+
resetInspectionRecord({
40+
inspectionId: inspectionData?.id ?? 0,
41+
inspectionRecordId: inspectionReportsData?.id ?? 0,
42+
resetPayload: {
43+
field_name: "date_issued",
44+
},
45+
});
46+
updateInspectionRecord({
47+
inspectionId: inspectionData?.id ?? 0,
48+
inspectionRecordId: inspectionReportsData?.id ?? 0,
49+
updateRecord: {
50+
field_name: "ir_progress",
51+
value: IRProgressEnum.FINAL_APPROVED,
52+
},
53+
});
54+
};
55+
56+
return (
57+
<>
58+
<ModalTitleBar title={"Reopen IR?"} />
59+
<DialogContent dividers>
60+
<Typography variant="body1" mb={2}>
61+
You are about to reopen Inspection Record:{" "}
62+
<b>{inspectionData?.ir_number}</b>.
63+
</Typography>
64+
<Typography variant="body2" mb={1.5}>
65+
Once reopened, you can make changes to the IR. You need to issue the
66+
IR again in order to close it.
67+
</Typography>
68+
</DialogContent>
69+
<ModalActions
70+
primaryActionButtonText={"Reopen"}
71+
isLoading={isPending}
72+
onPrimaryAction={onSubmitHandler}
73+
/>
74+
</>
75+
);
76+
};
77+
78+
export default ReopenIRModal;

compliance-web/src/components/App/Inspections/Profile/Reports/ReportTopSection.tsx

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { Box, Typography, Button, Chip, Link } from "@mui/material";
22
import { SendRounded } from "@mui/icons-material";
33
import {
44
APPROVAL_STATUS,
5+
InspectionStatusEnum,
56
IRProgressEnum,
67
STAFF_USER_POSITION,
78
VARIANT_COLORS,
@@ -24,6 +25,7 @@ import { useModal } from "@/store/modalStore";
2425
import OfficerStepper from "./OfficerSteppr/OfficerStepper";
2526
import PreviewDownloadButton from "./PreviewDownloadButton";
2627
import IssueIRModal from "./IssueIRModal";
28+
import ReopenIRModal from "./ReopenIRModal";
2729
import { StaffUser } from "@/models/Staff";
2830
import ConfirmationModal from "@/components/Shared/Popups/ConfirmationModal";
2931

@@ -147,6 +149,13 @@ export default function ReportTopSection() {
147149
[irProgressId, inspectionReportsData?.intended_issuance_date]
148150
);
149151

152+
const isShowReopenIRButton = useMemo(
153+
() =>
154+
irProgressId === IRProgressEnum.ISSUED &&
155+
inspectionData?.inspection_status === InspectionStatusEnum.OPEN,
156+
[irProgressId, inspectionData?.inspection_status]
157+
);
158+
150159
const onSuccess = (data: IRApproval) => {
151160
setIRApprovalsData([data]);
152161
notify.success("Approval request sent");
@@ -207,6 +216,29 @@ export default function ReportTopSection() {
207216
inspectionData,
208217
]);
209218

219+
const handleReopenIR = useCallback(() => {
220+
setOpen({
221+
content: (
222+
<ReopenIRModal
223+
onSubmit={(message) => {
224+
notify.success(message);
225+
setClose();
226+
refetchInspectionReportsData();
227+
queryClient.invalidateQueries({
228+
queryKey: ["inspection", inspectionData?.ir_number],
229+
});
230+
}}
231+
/>
232+
),
233+
});
234+
}, [
235+
setOpen,
236+
setClose,
237+
refetchInspectionReportsData,
238+
queryClient,
239+
inspectionData,
240+
]);
241+
210242
const handleApproval = useCallback(
211243
(isApprove: boolean) => {
212244
updateIRApprovalStatus({
@@ -353,6 +385,9 @@ export default function ReportTopSection() {
353385
{isShowIssueIRButton && (
354386
<Button onClick={handleIssueIR}>Issue IR</Button>
355387
)}
388+
{isShowReopenIRButton && (
389+
<Button onClick={handleReopenIR}>Reopen IR</Button>
390+
)}
356391
</>
357392
)}
358393
<PreviewDownloadButton />

0 commit comments

Comments
 (0)