diff --git a/forms-flow-web/src/actions/FOI/foiActionConstants.js b/forms-flow-web/src/actions/FOI/foiActionConstants.js index beaca7433..8a94f7f00 100644 --- a/forms-flow-web/src/actions/FOI/foiActionConstants.js +++ b/forms-flow-web/src/actions/FOI/foiActionConstants.js @@ -89,6 +89,11 @@ const FOI_ACTION_CONSTANTS = { "FOI_PDF_STITCHED_STATUS_FOR_RESPONSEPACKAGE", FOI_PDF_STITCHED_RECORD_FOR_RESPONSEPACKAGE: "FOI_PDF_STITCHED_RECORD_FOR_RESPONSEPACKAGE", + + OIPC_OUTCOMES: "OIPC_OUTCOMES", + OIPC_STATUSES: "OIPC_STATUSES", + OIPC_REVIEWTYPES: "OIPC_REVIEWTYPES", + OIPC_INQUIRYOUTCOMES: "OIPC_INQUIRYOUTCOMES", }; export default FOI_ACTION_CONSTANTS; diff --git a/forms-flow-web/src/actions/FOI/foiRequestActions.js b/forms-flow-web/src/actions/FOI/foiRequestActions.js index 8cb6826c1..ad9a7beb7 100644 --- a/forms-flow-web/src/actions/FOI/foiRequestActions.js +++ b/forms-flow-web/src/actions/FOI/foiRequestActions.js @@ -425,3 +425,31 @@ export const setConversionFormats = (data) => (dispatch) => { payload: data, }); }; + +export const setOIPCOutcomes = (data) => (dispatch) => { + dispatch({ + type: FOI_ACTION_CONSTANTS.OIPC_OUTCOMES, + payload: data, + }); +}; + +export const setOIPCStatuses = (data) => (dispatch) => { + dispatch({ + type: FOI_ACTION_CONSTANTS.OIPC_STATUSES, + payload: data, + }); +}; + +export const setOIPCReviewtypes = (data) => (dispatch) => { + dispatch({ + type: FOI_ACTION_CONSTANTS.OIPC_REVIEWTYPES, + payload: data, + }); +}; + +export const setOIPCInquiryoutcomes = (data) => (dispatch) => { + dispatch({ + type: FOI_ACTION_CONSTANTS.OIPC_INQUIRYOUTCOMES, + payload: data, + }); +}; \ No newline at end of file diff --git a/forms-flow-web/src/apiManager/endpoints/index.js b/forms-flow-web/src/apiManager/endpoints/index.js index 111ee62ec..9e2ddb7d7 100644 --- a/forms-flow-web/src/apiManager/endpoints/index.js +++ b/forms-flow-web/src/apiManager/endpoints/index.js @@ -133,5 +133,10 @@ const API = { FOI_DOWNLOAD_RECORDS_FOR_RESPONSEPACKAGE: `${FOI_BASE_API_URL}/api/foirecord//ministryrequest//download/responsepackage`, FOI_PDF_STITCH_STATUS_FOR_RESPONSEPACKAGE: `${FOI_BASE_API_URL}/api/foirecord//ministryrequest//responsepackage/pdfstitchjobstatus`, + + FOI_GET_OIPC_OUTCOMES: `${FOI_BASE_API_URL}/api/foiflow/oipc/outcomes`, + FOI_GET_OIPC_STATUSES: `${FOI_BASE_API_URL}/api/foiflow/oipc/statuses`, + FOI_GET_OIPC_REVIEWTYPES: `${FOI_BASE_API_URL}/api/foiflow/oipc/reviewtypes`, + FOI_GET_OIPC_INQUIRYOUTCOMES: `${FOI_BASE_API_URL}/api/foiflow/oipc/inquiryoutcomes`, }; export default API; diff --git a/forms-flow-web/src/apiManager/services/FOI/foiMasterDataServices.js b/forms-flow-web/src/apiManager/services/FOI/foiMasterDataServices.js index b8eb8d385..5399f22da 100644 --- a/forms-flow-web/src/apiManager/services/FOI/foiMasterDataServices.js +++ b/forms-flow-web/src/apiManager/services/FOI/foiMasterDataServices.js @@ -22,6 +22,10 @@ import { setFOISubjectCodeList, setCommentTagListLoader, setFOIAdminProgramAreaList, + setOIPCOutcomes, + setOIPCStatuses, + setOIPCReviewtypes, + setOIPCInquiryoutcomes, } from "../../../actions/FOI/foiRequestActions"; import { fnDone, catchError } from "./foiServicesUtil"; import UserService from "../../../services/UserService"; @@ -508,4 +512,92 @@ import { }); }; }; + + export const fetchOIPCOutcomes = () => { + return (dispatch) => { + httpGETRequest(API.FOI_GET_OIPC_OUTCOMES, {}, UserService.getToken()) + .then((res) => { + if (res.data) { + const oipcOutcomes = res.data; + dispatch(setOIPCOutcomes(oipcOutcomes)); + dispatch(setFOILoader(false)); + } else { + console.log("Error while fetching OIPC outcomes master data", res); + dispatch(serviceActionError(res)); + dispatch(setFOILoader(false)); + } + }) + .catch((error) => { + console.log("Error while fetching OIPC outcomes master data", error); + dispatch(serviceActionError(error)); + dispatch(setFOILoader(false)); + }); + }; + }; + + export const fetchOIPCStatuses = () => { + return (dispatch) => { + httpGETRequest(API.FOI_GET_OIPC_STATUSES, {}, UserService.getToken()) + .then((res) => { + if (res.data) { + const oipcStatuses = res.data; + dispatch(setOIPCStatuses(oipcStatuses)); + dispatch(setFOILoader(false)); + } else { + console.log("Error while fetching OIPC statuses master data", res); + dispatch(serviceActionError(res)); + dispatch(setFOILoader(false)); + } + }) + .catch((error) => { + console.log("Error while fetching OIPC statuses master data", error); + dispatch(serviceActionError(error)); + dispatch(setFOILoader(false)); + }); + }; + }; + + export const fetchOIPCReviewtypes = () => { + return (dispatch) => { + httpGETRequest(API.FOI_GET_OIPC_REVIEWTYPES, {}, UserService.getToken()) + .then((res) => { + if (res.data) { + const oipcReviewtypes = res.data; + dispatch(setOIPCReviewtypes(oipcReviewtypes)); + dispatch(setFOILoader(false)); + } else { + console.log("Error while fetching OIPC reviewtypes master data", res); + dispatch(serviceActionError(res)); + dispatch(setFOILoader(false)); + } + }) + .catch((error) => { + console.log("Error while fetching OIPC reviewtypes master data", error); + dispatch(serviceActionError(error)); + dispatch(setFOILoader(false)); + }); + }; + }; + + export const fetchOIPCInquiryoutcomes = () => { + return (dispatch) => { + httpGETRequest(API.FOI_GET_OIPC_INQUIRYOUTCOMES, {}, UserService.getToken()) + .then((res) => { + if (res.data) { + const oipcInquiryoutcomes = res.data; + dispatch(setOIPCInquiryoutcomes(oipcInquiryoutcomes)); + dispatch(setFOILoader(false)); + } else { + console.log("Error while fetching OIPC inqiuryoutcomes master data", res); + dispatch(serviceActionError(res)); + dispatch(setFOILoader(false)); + } + }) + .catch((error) => { + console.log("Error while fetching OIPC inqiuryoutcomes master data", error); + dispatch(serviceActionError(error)); + dispatch(setFOILoader(false)); + }); + }; + }; \ No newline at end of file diff --git a/forms-flow-web/src/components/FOI/Dashboard/IAO/columns.js b/forms-flow-web/src/components/FOI/Dashboard/IAO/columns.js index 4cb4166be..213b3dd1d 100644 --- a/forms-flow-web/src/components/FOI/Dashboard/IAO/columns.js +++ b/forms-flow-web/src/components/FOI/Dashboard/IAO/columns.js @@ -5,6 +5,7 @@ import { // onBehalfFullName, displayIcon, displayHeaderIcon, + displayQueueFlagIcons, cellTooltipRender } from "../utils"; import { @@ -15,6 +16,15 @@ import { } from "../../../../helper/FOI/helper"; const ProcessingTeamColumns = [ + { + field: "flags", + // renderHeader: displayHeaderIcon, + headerName: "FLAGS", + headerAlign: "left", + renderCell: displayQueueFlagIcons, + // cellClassName: 'foi-dashboard-', + // flex: 1, + }, { field: "axisRequestId", headerName: "ID NUMBER", @@ -89,12 +99,10 @@ const ProcessingTeamColumns = [ const IntakeTeamColumns = [ { - field: "isiaorestricted", - renderHeader: displayHeaderIcon, + field: "flags", + headerName: "FLAGS", headerAlign: "left", - renderCell:displayIcon, - cellClassName: 'foi-dashboard-restricted', - width: 60, + renderCell: displayQueueFlagIcons, }, { field: "axisRequestId", @@ -161,12 +169,10 @@ const IntakeTeamColumns = [ const FlexTeamColumns = [ { - field: "isiaorestricted", - renderHeader: displayHeaderIcon, + field: "flags", + headerName: "FLAGS", headerAlign: "left", - renderCell:displayIcon, - cellClassName: 'foi-dashboard-restricted', - width: 60, + renderCell: displayQueueFlagIcons, }, { field: "axisRequestId", diff --git a/forms-flow-web/src/components/FOI/Dashboard/Ministry/AdvancedSearch/DataGridAdvancedSearch.js b/forms-flow-web/src/components/FOI/Dashboard/Ministry/AdvancedSearch/DataGridAdvancedSearch.js index 11be2fbeb..fa6955135 100644 --- a/forms-flow-web/src/components/FOI/Dashboard/Ministry/AdvancedSearch/DataGridAdvancedSearch.js +++ b/forms-flow-web/src/components/FOI/Dashboard/Ministry/AdvancedSearch/DataGridAdvancedSearch.js @@ -8,7 +8,8 @@ import { updateSortModel, getLDD, getRecordsDue, - LightTooltip + LightTooltip, + displayQueueFlagIcons } from "../../utils"; import { ActionContext } from "./ActionContext"; import { ConditionalComponent } from "../../../../../helper/FOI/helper"; @@ -94,6 +95,12 @@ const DataGridAdvancedSearch = ({ userDetail }) => { }; const columns = React.useRef([ + { + field: "flags", + headerName: "FLAGS", + headerAlign: "left", + renderCell: displayQueueFlagIcons, + }, { field: "axisRequestId", headerName: "ID NUMBER", diff --git a/forms-flow-web/src/components/FOI/Dashboard/Ministry/Queue.js b/forms-flow-web/src/components/FOI/Dashboard/Ministry/Queue.js index 7f775e9bb..a8b92a7ac 100644 --- a/forms-flow-web/src/components/FOI/Dashboard/Ministry/Queue.js +++ b/forms-flow-web/src/components/FOI/Dashboard/Ministry/Queue.js @@ -6,7 +6,7 @@ import { useDispatch, useSelector } from "react-redux"; import { push } from "connected-react-router"; import { fetchFOIMinistryRequestListByPage } from "../../../../apiManager/services/FOI/foiRequestServices"; import Loading from "../../../../containers/Loading"; -import { debounce, ClickableChip, displayIconMinistry, displayHeaderIcon, cellTooltipRender } from "../utils"; +import { debounce, ClickableChip, cellTooltipRender, displayQueueFlagIcons } from "../utils"; import Grid from "@mui/material/Grid"; import Stack from "@mui/material/Stack"; import SearchIcon from "@material-ui/icons/Search"; @@ -112,6 +112,12 @@ const Queue = ({ userDetail, tableInfo }) => { } const columns = React.useRef([ + { + field: "flags", + headerName: "FLAGS", + headerAlign: "left", + renderCell: displayQueueFlagIcons, + }, { field: "axisRequestId", headerName: "ID NUMBER", @@ -165,14 +171,6 @@ const Queue = ({ userDetail, tableInfo }) => { width: 0, hide: true, renderCell: (_params) => , - }, - { - field: "isministryrestricted", - renderHeader: displayHeaderIcon, - headerAlign: "left", - renderCell: displayIconMinistry, - cellClassName: 'foi-dashboard-restricted', - flex: 1, } ]); diff --git a/forms-flow-web/src/components/FOI/Dashboard/dashboard.scss b/forms-flow-web/src/components/FOI/Dashboard/dashboard.scss index 92123f7a5..1cf6da99d 100644 --- a/forms-flow-web/src/components/FOI/Dashboard/dashboard.scss +++ b/forms-flow-web/src/components/FOI/Dashboard/dashboard.scss @@ -246,9 +246,28 @@ nav.MuiPagination-root { padding-right: 15px !important; } -.foi-dashboard-restricted { +.dashboard-flag-restricted { font-size: 10px; color: #A0192F; + padding: 2px; +} + +.dashboard-flag-oipcreview { + font-size: 10px; + color: #fa7c16; + padding: 2px; +} + +.dashboard-flag-phasedrelease { + font-size: 10px; + color: #9207b7; + padding: 2px; +} + +.dashboard-flag-placeholder { + font-size: 10px; + color: #c1bfbf; + padding: 2px; } .table-cell-truncate { diff --git a/forms-flow-web/src/components/FOI/Dashboard/utils.js b/forms-flow-web/src/components/FOI/Dashboard/utils.js index 0bb6d1a6a..a8374d54c 100644 --- a/forms-flow-web/src/components/FOI/Dashboard/utils.js +++ b/forms-flow-web/src/components/FOI/Dashboard/utils.js @@ -219,9 +219,48 @@ export const displayIconMinistry = (params) => { ); }; +export const displayQueueFlagIcons = (params) => { + let restricted = + + if (params?.row?.isiaorestricted || params?.row?.isministryrestricted) { + restricted = + Restricted + + }> + + + } else { + restricted = + } + + const oipcreview = params?.row?.isoipcreview ? + + OIPC + + }> + : + + + const phasedrelease = params?.row?.isphasedrelease ? + + Phased Release + + }> + : + + + return
+ {restricted} + {oipcreview} +
+} + export const displayHeaderIcon = (params) => { return ( - + ); }; diff --git a/forms-flow-web/src/components/FOI/FOIRequest/BottomButtonGroup/index.js b/forms-flow-web/src/components/FOI/FOIRequest/BottomButtonGroup/index.js index 5c301c061..7a9e7c217 100644 --- a/forms-flow-web/src/components/FOI/FOIRequest/BottomButtonGroup/index.js +++ b/forms-flow-web/src/components/FOI/FOIRequest/BottomButtonGroup/index.js @@ -75,7 +75,8 @@ const BottomButtonGroup = React.memo( requestState, axisSyncedData, axisMessage, - attachmentsArray + attachmentsArray, + oipcData, }) => { /** * Bottom Button Group of Review request Page @@ -117,7 +118,18 @@ const BottomButtonGroup = React.memo( if (urlIndexCreateRequest > -1) { saveRequestObject.requeststatusid = StateEnum.intakeinprogress.id; setIsAddRequest(false); - } + } + + //add oipc Data to save request object and sync/validate isoipcreview attribute + if (requestState.toLowerCase() !== StateEnum.intakeinprogress.name.toLowerCase() && requestState.toLowerCase() !== StateEnum.unopened.name.toLowerCase()) { + saveRequestObject.oipcdetails = oipcData ? oipcData : []; + // if (oipcData?.length > 0) { + // saveRequestObject.isoipcreview = true; + // } else { + // saveRequestObject.isoipcreview = false; + // } + } + dispatch(setFOILoader(setLoader)) dispatch( saveRequestDetails( diff --git a/forms-flow-web/src/components/FOI/FOIRequest/FOIRequest.js b/forms-flow-web/src/components/FOI/FOIRequest/FOIRequest.js index d754731d2..64f95c009 100644 --- a/forms-flow-web/src/components/FOI/FOIRequest/FOIRequest.js +++ b/forms-flow-web/src/components/FOI/FOIRequest/FOIRequest.js @@ -24,6 +24,10 @@ import { fetchFOIMinistryAssignedToList, fetchFOISubjectCodeList, fetchFOIPersonalDivisionsAndSections, + fetchOIPCOutcomes, + fetchOIPCStatuses, + fetchOIPCReviewtypes, + fetchOIPCInquiryoutcomes } from "../../../apiManager/services/FOI/foiMasterDataServices"; import { fetchFOIRequestDetailsWrapper, @@ -89,6 +93,8 @@ import { UnsavedModal } from "../customComponents"; import { DISABLE_GATHERINGRECORDS_TAB } from "../../../constants/constants"; import _ from "lodash"; import { MinistryNeedsScanning } from "../../../constants/FOI/enum"; +import OIPCDetails from "./OIPCDetails/Index"; +import useOIPCHook from "./OIPCDetails/oipcHook"; const useStyles = makeStyles((theme) => ({ root: { @@ -251,7 +257,8 @@ const FOIRequest = React.memo(({ userDetail }) => { const [isIAORestricted, setIsIAORestricted] = useState(false); const [redactedSections, setRedactedSections] = useState(""); const [isMCFPersonal, setIsMCFPersonal] = useState(bcgovcode.replaceAll('"', '') == "MCF" && requestDetails.requestType == FOI_COMPONENT_CONSTANTS.REQUEST_TYPE_PERSONAL); - + const {oipcData, addOIPC, removeOIPC, updateOIPC, isOIPCReview, setIsOIPCReview} = useOIPCHook(); + useEffect(() => { if (window.location.href.indexOf("comments") > -1) { tabclick("Comments"); @@ -291,6 +298,11 @@ const FOIRequest = React.memo(({ userDetail }) => { dispatch(fetchFOIDeliveryModeList()); dispatch(fetchFOISubjectCodeList()); dispatch(fetchClosingReasonList()); + + dispatch(fetchOIPCOutcomes()); + dispatch(fetchOIPCStatuses()); + dispatch(fetchOIPCReviewtypes()); + dispatch(fetchOIPCInquiryoutcomes()); if (bcgovcode) dispatch(fetchFOIMinistryAssignedToList(bcgovcode)); }, [requestId, ministryId, comment, attachments]); @@ -325,8 +337,22 @@ const FOIRequest = React.memo(({ userDetail }) => { setIsMCFPersonal(true); } } + if(requestDetails.isoipcreview) { + setIsOIPCReview(true); + } else { + setIsOIPCReview(false); + } }, [requestDetails]); + //useEffect to manage isoipcreview attribute for requestdetails state + useEffect(() => { + if(Object.keys(requestDetails).length !== 0 && oipcData?.length <= 0) { + requestDetails.isoipcreview = false; + setIsOIPCReview(false); + } + }, [oipcData]) + + useEffect(() => { if (isIAORestricted) dispatch(fetchRestrictedRequestCommentTagList(requestId, ministryId)); @@ -621,6 +647,19 @@ const FOIRequest = React.memo(({ userDetail }) => { setAssignedToValue(value); }; + const oipcSectionRef = React.useRef(null); + const handleOipcReviewFlagChange = (isSelected) => { + setIsOIPCReview(isSelected); + requestDetails.isoipcreview = isSelected; + oipcSectionRef.current.scrollIntoView(); + //timeout to allow react state to update after setState call + if (isSelected) { + setTimeout(() => { + oipcSectionRef.current.scrollIntoView(); + }, (10)); + } + } + //handle email validation const [validation, setValidation] = React.useState({}); const handleEmailValidation = (validationObj) => { @@ -650,7 +689,9 @@ const FOIRequest = React.memo(({ userDetail }) => { requiredRequestDetailsValues, requiredAxisDetails, isAddRequest, - _currentrequestStatus + _currentrequestStatus, + oipcData, + requestDetails.isoipcreview, ); const classes = useStyles(); @@ -761,7 +802,7 @@ const FOIRequest = React.memo(({ userDetail }) => { }; } }, [editorChange]); - + const tabclick = (param) => { if (param === "Comments") { sessionStorage.setItem("foicommentcategory", 1); @@ -1092,6 +1133,8 @@ const FOIRequest = React.memo(({ userDetail }) => { userDetail={userDetail} disableInput={disableInput} isAddRequest={isAddRequest} + handleOipcReviewFlagChange={handleOipcReviewFlagChange} + showOipcReviewFlag={requestState.toLowerCase() !== StateEnum.intakeinprogress.name.toLowerCase() && requestState.toLowerCase() !== StateEnum.unopened.name.toLowerCase()} /> {(isAddRequest || requestState === StateEnum.unopened.name) && ( @@ -1207,6 +1250,15 @@ const FOIRequest = React.memo(({ userDetail }) => { divisions={requestDetails.divisions} /> )} +
+ {isOIPCReview && requestState && requestState.toLowerCase() !== StateEnum.intakeinprogress.name.toLowerCase() && requestState.toLowerCase() !== StateEnum.unopened.name.toLowerCase() && ( + + )} { axisSyncedData={axisSyncedData} axisMessage={axisMessage} attachmentsArray={requestAttachments} + oipcData={oipcData} /> diff --git a/forms-flow-web/src/components/FOI/FOIRequest/FOIRequestHeader/index.js b/forms-flow-web/src/components/FOI/FOIRequest/FOIRequestHeader/index.js index 4ce2c617b..69c3ed18f 100644 --- a/forms-flow-web/src/components/FOI/FOIRequest/FOIRequestHeader/index.js +++ b/forms-flow-web/src/components/FOI/FOIRequest/FOIRequestHeader/index.js @@ -22,6 +22,7 @@ import { toast } from "react-toastify"; import _ from 'lodash'; import RequestRestriction from "../../customComponents/RequestRestriction"; import ConfirmModal from "../../customComponents/ConfirmModal"; +import RequestFlag from '../../customComponents/RequestFlag'; const useStyles = makeStyles((theme) => ({ formControl: { @@ -35,6 +36,9 @@ const useStyles = makeStyles((theme) => ({ fontWeight: theme.typography.fontWeightBold, opacity: 1, }, + blankrow: { + padding: 25 + } })); const FOIRequestHeader = React.memo( ({ @@ -48,6 +52,8 @@ const FOIRequestHeader = React.memo( userDetail, disableInput, isAddRequest, + handleOipcReviewFlagChange, + showOipcReviewFlag }) => { /** * Header of Review request in the UI @@ -259,11 +265,9 @@ const FOIRequestHeader = React.memo(
-
-
+
{window.location.href.indexOf(FOI_COMPONENT_CONSTANTS.ADDREQUEST) === -1 && ( -
-
)} {!isAddRequest && status.toLowerCase() !== StateEnum.unopened.name.toLowerCase() && (isIAORestrictedFileManager() || (isLoaded && isRequestWatcherOrAssignee(requestWatchers,assigneeObj,userDetail?.preferred_username))) && @@ -282,10 +285,26 @@ const FOIRequestHeader = React.memo( isIAORestrictedFileManager={isIAORestrictedFileManager()} requestDetails={requestDetails} /> + } -
-
+
+
+ + {/* */} +
+
{showMinistryAssignedTo && ( <> diff --git a/forms-flow-web/src/components/FOI/FOIRequest/MinistryReview/MinistryReview.js b/forms-flow-web/src/components/FOI/FOIRequest/MinistryReview/MinistryReview.js index 6b779db48..8e8cc00c1 100644 --- a/forms-flow-web/src/components/FOI/FOIRequest/MinistryReview/MinistryReview.js +++ b/forms-flow-web/src/components/FOI/FOIRequest/MinistryReview/MinistryReview.js @@ -18,6 +18,10 @@ import { import { fetchFOIMinistryAssignedToList, fetchFOIPersonalDivisionsAndSections, + fetchOIPCInquiryoutcomes, + fetchOIPCOutcomes, + fetchOIPCReviewtypes, + fetchOIPCStatuses, } from "../../../../apiManager/services/FOI/foiMasterDataServices"; import { fetchFOIRequestAttachmentsList } from "../../../../apiManager/services/FOI/foiAttachmentServices"; @@ -62,6 +66,8 @@ import { UnsavedModal } from "../../customComponents"; import { DISABLE_GATHERINGRECORDS_TAB } from "../../../../constants/constants"; import _ from "lodash"; import { MinistryNeedsScanning } from "../../../../constants/FOI/enum"; +import OIPCDetails from "../OIPCDetails/Index"; +import useOIPCHook from "../OIPCDetails/oipcHook"; const useStyles = makeStyles((theme) => ({ root: { @@ -108,6 +114,8 @@ const MinistryReview = React.memo(({ userDetail }) => { const [_currentrequestStatus, setcurrentrequestStatus] = React.useState(""); const [_tabStatus, settabStatus] = React.useState(requestState); + const {oipcData, addOIPC, removeOIPC, updateOIPC, isOIPCReview, setIsOIPCReview} = useOIPCHook(); + //gets the request detail from the store const IsDivisionalCoordinator = () => { return userDetail?.role?.includes("DivisionalCoordinator"); @@ -210,6 +218,12 @@ const MinistryReview = React.memo(({ userDetail }) => { dispatch(fetchPDFStitchStatusForHarms(requestId, ministryId)); dispatch(fetchPDFStitchStatusForRedlines(requestId, ministryId)); dispatch(fetchPDFStitchStatusForResponsePackage(requestId, ministryId)); + + dispatch(fetchOIPCOutcomes()); + dispatch(fetchOIPCStatuses()); + dispatch(fetchOIPCReviewtypes()); + dispatch(fetchOIPCInquiryoutcomes()); + fetchCFRForm(ministryId, dispatch); if (bcgovcode) dispatch(fetchFOIMinistryAssignedToList(bcgovcode)); } @@ -318,6 +332,11 @@ const MinistryReview = React.memo(({ userDetail }) => { hasincompleteDivstage || !hasReceivedDate; + const isOipcReviewValidationError = (oipcData?.length > 0 && requestDetails.isoipcreview && oipcData?.some((oipc) => { + return oipc.oipcno === "" || oipc.receiveddate === null || oipc.receiveddate === "" || oipc.reviewtypeid === null || oipc.reasonid === null || oipc.statusid === null || + oipc.inquiryattributes?.orderno === "" || oipc.inquiryattributes?.inquiryoutcome === null || oipc.inquiryattributes?.inquirydate === null || oipc.inquiryattributes?.inquirydate === ""; + })) + const createMinistrySaveRequestObject = (_propName, _value, _value2) => { const requestObject = { ...saveMinistryRequestObject }; setUnSavedRequest(true); @@ -582,6 +601,19 @@ const MinistryReview = React.memo(({ userDetail }) => { (state) => state.foiRequests.showEventQueue ); + const oipcSectionRef = React.useRef(null); + const handleOipcReviewFlagChange = (isSelected) => { + setIsOIPCReview(isSelected); + requestDetails.isoipcreview = isSelected; + oipcSectionRef.current.scrollIntoView(); + //timeout to allow react state to update after setState call + if (isSelected) { + setTimeout(() => { + oipcSectionRef.current.scrollIntoView(); + }, (10)); + } + } + return !isLoading && requestDetails && Object.keys(requestDetails).length !== 0 && @@ -752,6 +784,7 @@ const MinistryReview = React.memo(({ userDetail }) => { setSaveMinistryRequestObject } ministryAssigneeValue={ministryAssignedToValue} + handleOipcReviewFlagChange={handleOipcReviewFlagChange} /> @@ -767,11 +800,20 @@ const MinistryReview = React.memo(({ userDetail }) => { /> {divisionsBox} {/* */} +
+ {isOIPCReview && requestState && requestState.toLowerCase() !== StateEnum.intakeinprogress.name.toLowerCase() && requestState.toLowerCase() !== StateEnum.unopened.name.toLowerCase() && ( + + )} { const { requestId, ministryId } = useParams(); const _requestDetails = requestDetails; @@ -101,6 +103,22 @@ const RequestHeader = React.memo(({ ) ); + const requestFlagsBox = ( +
+ + {/* console.log('selected')} + /> */} +
+ ); return ( <>
@@ -128,23 +146,24 @@ const RequestHeader = React.memo(({
-
-
-
- {watcherBox} -
- { +
+ {watcherBox} + { (isLoaded && (isRequestWatcherOrMinistryAssignee(requestWatchers,ministryAssigneeValue,userDetail?.preferred_username) || isMinistryRestrictedFileManager())) && - - } + + } +
+
+
+ {requestFlagsBox}
-
+
{ + + const handleSave = () => { + setShowModal(false); + const newOIPCObj = oipc; + newOIPCObj.outcomeid = null; + setOipc(newOIPCObj); + updateOIPC(newOIPCObj); + }; + const handleClose = () => { + setShowModal(false); + }; + + return ( +
+ + +

Amend Outcome

+ + Close + + +
+ + + + Are you sure you want to amend this completed OIPC review? You will need to re-select an outcome to close it again. + + + + + + + +
+
+ ); +}; + +export default AmendModal; \ No newline at end of file diff --git a/forms-flow-web/src/components/FOI/FOIRequest/OIPCDetails/Index.jsx b/forms-flow-web/src/components/FOI/FOIRequest/OIPCDetails/Index.jsx new file mode 100644 index 000000000..2d02e79e8 --- /dev/null +++ b/forms-flow-web/src/components/FOI/FOIRequest/OIPCDetails/Index.jsx @@ -0,0 +1,47 @@ +import OIPCDetailsList from "./OIPCDetailsList"; +import Accordion from '@material-ui/core/Accordion'; +import AccordionSummary from '@material-ui/core/AccordionSummary'; +import AccordionDetails from '@material-ui/core/AccordionDetails'; +import Typography from '@material-ui/core/Typography'; +import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; +import { makeStyles } from '@material-ui/styles'; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { faCirclePlus } from '@fortawesome/free-solid-svg-icons'; + +const OIPCDetails = (props) => { + const { oipcData, addOIPC, removeOIPC, updateOIPC } = props; + + //Styling + const useStyles = makeStyles({ + heading: { + color: '#FFF', + fontSize: '16px !important', + fontWeight: 'bold !important' + }, + accordionSummary: { + flexDirection: 'row-reverse' + } + }); + const classes = useStyles(); + + return ( +
+ + }> + OIPC DETAILS + + + +
+ +

Add Additional OIPC Complaint

+
+
+
+
+ ); +} + +export default OIPCDetails; \ No newline at end of file diff --git a/forms-flow-web/src/components/FOI/FOIRequest/OIPCDetails/OIPCDetailsList.jsx b/forms-flow-web/src/components/FOI/FOIRequest/OIPCDetails/OIPCDetailsList.jsx new file mode 100644 index 000000000..a4c825ce8 --- /dev/null +++ b/forms-flow-web/src/components/FOI/FOIRequest/OIPCDetails/OIPCDetailsList.jsx @@ -0,0 +1,24 @@ +import OIPCItem from "./OIPCItem"; +import Divider from '@material-ui/core/Divider'; +import './oipcdetails.scss'; + +const OIPCDetailsList = (props) => { + const {oipcData, removeOIPC, updateOIPC} = props; + + const OIPCItems = oipcData?.map((oipcObj, index) => { + return ( + <> + + {index !== (oipcData?.length - 1) && } + + ); + }); + + return ( +
+ {OIPCItems} +
+ ); +} + +export default OIPCDetailsList; \ No newline at end of file diff --git a/forms-flow-web/src/components/FOI/FOIRequest/OIPCDetails/OIPCItem.jsx b/forms-flow-web/src/components/FOI/FOIRequest/OIPCDetails/OIPCItem.jsx new file mode 100644 index 000000000..483518590 --- /dev/null +++ b/forms-flow-web/src/components/FOI/FOIRequest/OIPCDetails/OIPCItem.jsx @@ -0,0 +1,398 @@ +import { TextField, FormControlLabel, MenuItem, Grid, Checkbox } from '@material-ui/core'; +import { useState, useEffect } from "react"; +import { formatDate } from "../../../../helper/FOI/helper"; +import { useSelector } from "react-redux"; +import RemoveOIPCModal from './RemoveOIPCModal'; +import OutcomeModal from './OutcomeModal'; +import AmendModal from './AmendModal'; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { faTrash } from '@fortawesome/free-solid-svg-icons'; + +const OIPCItem = (props) => { + const {oipcObj, updateOIPC, removeOIPC} = props; + + //App State + const oipcOutcomes = useSelector(state=> state.foiRequests.oipcOutcomes); + const oipcStatuses = useSelector(state=> state.foiRequests.oipcStatuses); + const oipcReviewtypes = useSelector(state=> state.foiRequests.oipcReviewtypes); + const oipcInquiryoutcomes = useSelector(state=> state.foiRequests.oipcInquiryoutcomes); + + //Local State + const [oipc, setOipc] = useState(oipcObj); + const [showDeleteModal, setShowDeleteModal] = useState(false); + const [showOutcomeModal, setShowOutcomeModal] = useState(false); + const [showAmendModal, setShowAmendModal] = useState(false); + + //Functions + const generateNamesFromOIPCId = (oipcObj) => { + const reviewtype = oipcReviewtypes.find(reviewtype => reviewtype.reviewtypeid === oipcObj.reviewtypeid && reviewtype.reasonid === oipcObj.reasonid); + const status = oipcStatuses.find(status => status.statusid === oipcObj.statusid); + const outcome = oipcOutcomes.find(outcome => outcome.outcomeid === oipcObj.outcomeid); + + setOipc((prev) => { + return {...prev, reviewtypeName: reviewtype ? reviewtype.type_name : ""} + }) + setOipc((prev) => { + return {...prev, statusName: status ? status.name : ""} + }) + setOipc((prev) => { + return {...prev, outcomeName: outcome ? outcome.name : ""} + }) + setOipc((prev) => { + return {...prev, reasonName: reviewtype ? reviewtype.reason_name : ""} + }) + } + const uniqueReviewTypes = (oipcReviewTypes) => { + const uniqeValues = {}; + return oipcReviewTypes.filter(reviewtype => !uniqeValues[reviewtype.type_name] && (uniqeValues[reviewtype.type_name] = true)); + } + const handleReviewType = (value) => { + const newOIPCObj = oipc; + newOIPCObj.reviewtypeid = value; + newOIPCObj.reasonid = null; + setOipc(newOIPCObj); + updateOIPC(newOIPCObj); + } + const handleOIPCNumber = (value) => { + const newOIPCObj = oipc; + newOIPCObj.oipcno = value; + setOipc(newOIPCObj); + updateOIPC(newOIPCObj); + } + const handleReceivedDate = (value) => { + const newOIPCObj = oipc; + newOIPCObj.receiveddate = value; + setOipc(newOIPCObj); + updateOIPC(newOIPCObj); + } + const handleClosedDate = (value) => { + const newOIPCObj = oipc; + newOIPCObj.closeddate = value; + setOipc(newOIPCObj); + updateOIPC(newOIPCObj); + } + const handleReason = (value) => { + const newOIPCObj = oipc; + newOIPCObj.reasonid = value; + setOipc(newOIPCObj); + updateOIPC(newOIPCObj); + } + const handleStatus = (value) => { + const newOIPCObj = oipc; + newOIPCObj.statusid = value; + setOipc(newOIPCObj); + updateOIPC(newOIPCObj); + } + const handleInvestiagtor = (value) => { + const newOIPCObj = oipc; + newOIPCObj.investigator = value; + setOipc(newOIPCObj); + updateOIPC(newOIPCObj); + } + const handleOutcome = (value) => { + if (value === 5) { + return setShowAmendModal(true); + } + const newOIPCObj = oipc; + newOIPCObj.outcomeid = value; + setOipc(newOIPCObj); + setShowOutcomeModal(true); + } + const handleInquiry = (value) => { + const newOIPCObj = oipc; + if (value === false) { + newOIPCObj.inquiryattributes = null; + } else { + newOIPCObj.inquiryattributes = { + inquirydate: null, + orderno: "", + inquiryoutcome: null, + }; + } + newOIPCObj.isinquiry = value; + setOipc(newOIPCObj); + updateOIPC(newOIPCObj); + } + const handleJudicalReview = (value) => { + const newOIPCObj = oipc; + newOIPCObj.isjudicialreview = value; + setOipc(newOIPCObj); + updateOIPC(newOIPCObj); + } + const handleSubsequentAppeal = (value) => { + const newOIPCObj = oipc; + newOIPCObj.issubsequentappeal = value; + setOipc(newOIPCObj); + updateOIPC(newOIPCObj); + } + const handleInquiryFields = (value, attribute) => { + const newOIPCObj = oipc; + if (attribute === "COMPLYDATE") { + newOIPCObj.inquiryattributes.inquirydate = value; + } + if (attribute === "ORDERNO") { + newOIPCObj.inquiryattributes.orderno = value; + } + if (attribute === "INQUIRYOUTCOME") { + newOIPCObj.inquiryattributes.inquiryoutcome = value; + } + setOipc(newOIPCObj); + updateOIPC(newOIPCObj); + } + + useEffect(() => { + setOipc(oipcObj); + generateNamesFromOIPCId(oipc); + }, [oipcObj]) + + return ( + <> +
+ +
+ {showDeleteModal && } + {showOutcomeModal && } + {showAmendModal && } + + + handleOIPCNumber(event.target.value)} + InputLabelProps={{ shrink: true }} + error={(!oipc.outcomeid || oipc.outcomeid === 5) && oipc.oipcno === ""} + required + disabled={oipc.outcomeid && oipc.outcomeid !== 5} + placeholder="OIPC Number" + /> + + + handleReceivedDate(event.target.value)} + InputLabelProps={{ shrink: true }} + InputProps={{inputProps: { max: formatDate(new Date())} }} + type="date" + error={(!oipc.outcomeid || oipc.outcomeid === 5) && oipc.receiveddate === null} + required + disabled={oipc.outcomeid && oipc.outcomeid !== 5} + /> + + + handleReviewType(event.target.value)} + error={(!oipc.outcomeid || oipc.outcomeid === 5) && oipc.reviewtypeid === null} + required + disabled={oipc.outcomeid && oipc.outcomeid !== 5} + > + {uniqueReviewTypes(oipcReviewtypes).map((reviewtype) => { + return {reviewtype.type_name} + })} + + + + handleReason(event.target.value)} + error={(!oipc.outcomeid || oipc.outcomeid === 5) && oipc.reasonid === null} + required + disabled={oipc.outcomeid && oipc.outcomeid !== 5} + > + {oipc.reviewtypeid ? + oipcReviewtypes.map((reviewtype) => { + if (reviewtype.reviewtypeid === oipc.reviewtypeid) { + return {reviewtype.reason_name} + } + }) : null} + + + + handleStatus(event.target.value)} + error={(!oipc.outcomeid || oipc.outcomeid === 5) && oipc.statusid === null} + required + disabled={oipc.outcomeid && oipc.outcomeid !== 5} + > + {oipcStatuses.map((status) => { + return {status.name} + })} + + + + handleInvestiagtor(event.target.value)} + value={oipc.investigator} + InputLabelProps={{ shrink: true }} + disabled={oipc.outcomeid && oipc.outcomeid !== 5} + placeholder="Firstname Lastname" + /> + + + handleOutcome(event.target.value)} + fullWidth + value={oipc.outcomeid} + label="Outcome" + > + {oipcOutcomes.map((outcome) => { + if (outcome.outcomeid !== 5) { + return {outcome.name} + } else { + return {outcome.name} + } + })} + + + + handleClosedDate(event.target.value)} + InputLabelProps={{ shrink: true }} + InputProps={{inputProps: { max: formatDate(new Date())} }} + type="date" + disabled={oipc.outcomeid && oipc.outcomeid !== 5} + /> + + +
+
+

In Inquiry?

+ handleInquiry(true)} + disabled={oipc.outcomeid && oipc.outcomeid !== 5} + />} + label="Yes" + /> + handleInquiry(false)} + disabled={oipc.outcomeid && oipc.outcomeid !== 5} + />} + label="No" + /> +
+
+

In Judicial Review?

+ handleJudicalReview(true)} + disabled={oipc.outcomeid && oipc.outcomeid !== 5} + />} + label="Yes" + /> + handleJudicalReview(false)} + disabled={oipc.outcomeid && oipc.outcomeid !== 5} + />} + label="No" + /> +
+
+

In Subsequent Appeal?

+ handleSubsequentAppeal(true)} + disabled={oipc.outcomeid && oipc.outcomeid !== 5} + />} + label="Yes" + /> + handleSubsequentAppeal(false)} + disabled={oipc.outcomeid && oipc.outcomeid !== 5} + />} + label="No" + /> +
+
+ {oipc.isinquiry && + + + handleInquiryFields(event.target.value, "COMPLYDATE")} + InputLabelProps={{ shrink: true }} + InputProps={{inputProps: { min: oipc.receiveddate ? formatDate(new Date(oipc.receiveddate)) : null } }} + type="date" + error={(!oipc.outcomeid || oipc.outcomeid === 5) && oipc.inquiryattributes.inquirydate === null} + required + disabled={oipc.outcomeid && oipc.outcomeid !== 5} + /> + + + handleInquiryFields(event.target.value, "ORDERNO")} + InputLabelProps={{ shrink: true }} + error={(!oipc.outcomeid || oipc.outcomeid === 5) && oipc.inquiryattributes.orderno === ""} + required + disabled={oipc.outcomeid && oipc.outcomeid !== 5} + placeholder="Order Number" + /> + + + handleInquiryFields(event.target.value, "INQUIRYOUTCOME")} + fullWidth + value={oipc.inquiryattributes.inquiryoutcome} + label="Outcome" + error={(!oipc.outcomeid || oipc.outcomeid === 5) && oipc.inquiryattributes.inquiryoutcome === null} + required + disabled={oipc.outcomeid && oipc.outcomeid !== 5} + > + {oipcInquiryoutcomes.map((inquiryoutcome) => { + return {inquiryoutcome.name} + })} + + + + } + + ); +} + +export default OIPCItem; \ No newline at end of file diff --git a/forms-flow-web/src/components/FOI/FOIRequest/OIPCDetails/OutcomeModal.jsx b/forms-flow-web/src/components/FOI/FOIRequest/OIPCDetails/OutcomeModal.jsx new file mode 100644 index 000000000..34efe936e --- /dev/null +++ b/forms-flow-web/src/components/FOI/FOIRequest/OIPCDetails/OutcomeModal.jsx @@ -0,0 +1,64 @@ +import Dialog from '@material-ui/core/Dialog'; +import DialogActions from '@material-ui/core/DialogActions'; +import DialogContent from '@material-ui/core/DialogContent'; +import DialogContentText from '@material-ui/core/DialogContentText'; +import DialogTitle from '@material-ui/core/DialogTitle'; +import CloseIcon from '@material-ui/icons/Close'; +import IconButton from '@material-ui/core/IconButton'; + +const OutcomeModal= ({ + showModal, + setShowModal, + updateOIPC, + oipc, + setOipc, +}) =>{ + + const handleSave = () => { + setShowModal(false); + updateOIPC(oipc); + }; + const handleClose = () => { + setShowModal(false); + setOipc(prev => ({ ...prev, outcomeid: null })); + }; + + return ( +
+ + +

Close OIPC

+ + Close + + +
+ + + + Are you sure you are ready to select an outcome? This will complete the review and lock all fields. + + + + + + + +
+
+ ); +}; + +export default OutcomeModal; \ No newline at end of file diff --git a/forms-flow-web/src/components/FOI/FOIRequest/OIPCDetails/RemoveOIPCModal.jsx b/forms-flow-web/src/components/FOI/FOIRequest/OIPCDetails/RemoveOIPCModal.jsx new file mode 100644 index 000000000..853c28b54 --- /dev/null +++ b/forms-flow-web/src/components/FOI/FOIRequest/OIPCDetails/RemoveOIPCModal.jsx @@ -0,0 +1,62 @@ +import Dialog from '@material-ui/core/Dialog'; +import DialogActions from '@material-ui/core/DialogActions'; +import DialogContent from '@material-ui/core/DialogContent'; +import DialogContentText from '@material-ui/core/DialogContentText'; +import DialogTitle from '@material-ui/core/DialogTitle'; +import CloseIcon from '@material-ui/icons/Close'; +import IconButton from '@material-ui/core/IconButton'; + +const RemoveOIPCModal= ({ + showModal, + removeOIPC, + setShowModal, + oipcid, +}) =>{ + + const handleSave = () => { + setShowModal(false); + removeOIPC(oipcid) + }; + const handleClose = () => { + setShowModal(false); + }; + + return ( +
+ + +

Remove OIPC

+ + Close + + +
+ + + + Are you sure you want to delete this OIPC Review? + + + + + + + +
+
+ ); +}; + +export default RemoveOIPCModal; diff --git a/forms-flow-web/src/components/FOI/FOIRequest/OIPCDetails/oipcHook.js b/forms-flow-web/src/components/FOI/FOIRequest/OIPCDetails/oipcHook.js new file mode 100644 index 000000000..5cdf4b4a5 --- /dev/null +++ b/forms-flow-web/src/components/FOI/FOIRequest/OIPCDetails/oipcHook.js @@ -0,0 +1,97 @@ +import { useState, useEffect } from "react"; +import { useSelector } from "react-redux"; + +const useOIPCHook = () => { + //OIPC State + const requestDetails = useSelector((state) => state.foiRequests.foiRequestDetail); + const stageOIPCData = (isoipcreview, oipcData) => { + console.log(isoipcreview) + if (isoipcreview) { + if (oipcData?.length > 0) { + return oipcData.map((item, index) => { + item.id = index; + return item; + }); + } else { + return [{ + id: 0, + oipcno: "", + reviewtypeid: null, + reasonid: null, + statusid: null, + isinquiry: false, + inquiryattributes: null, + receiveddate: null, + closeddate: null, + investigator: "", + outcomeid: null, + isjudicialreview: false, + issubsequentappeal: false, + }]; + } + } + } + const [oipcData, setOipcData] = useState(requestDetails.oipcdetails); + const [isOIPCReview, setIsOIPCReview] = useState(requestDetails.isoipcreview); + + useEffect(() => { + const stagedOIPCData = stageOIPCData(isOIPCReview, requestDetails.oipcdetails); + setOipcData(stagedOIPCData); + }, [isOIPCReview]) + + + //OIPC Functions + const addOIPC = () => { + setOipcData((prev) => { + return [...prev, { + id: oipcData?.length > 0 ? oipcData[oipcData.length - 1].id + 1 : 0, + oipcno: "", + reviewtypeid: null, + reasonid: null, + statusid: null, + isinquiry: false, + inquiryattributes: null, + receiveddate: null, + closeddate: null, + investigator: "", + outcomeid: null, + isjudicialreview: false, + issubsequentappeal: false, + }]; + }) + } + const removeOIPC = (oipcId) => { + setOipcData((prev) => { + const previousOIPCData = [...prev]; + return previousOIPCData.filter(oipc => oipcId !== oipc.id); + }); + } + const updateOIPC = (newOIPCObj) => { + setOipcData((prev) => { + const previousOIPCData = [...prev]; + return previousOIPCData.map((oipc) => { + if (oipc.id === newOIPCObj.id) { + return newOIPCObj; + } else { + return oipc; + } + }); + }); + } + const removeAllOIPCs = () => { + setOipcData([]); + } + + return { + oipcData, + addOIPC, + removeOIPC, + updateOIPC, + stageOIPCData, + removeAllOIPCs, + isOIPCReview, + setIsOIPCReview, + }; +}; + +export default useOIPCHook; \ No newline at end of file diff --git a/forms-flow-web/src/components/FOI/FOIRequest/OIPCDetails/oipcdetails.scss b/forms-flow-web/src/components/FOI/FOIRequest/OIPCDetails/oipcdetails.scss new file mode 100644 index 000000000..d28779da2 --- /dev/null +++ b/forms-flow-web/src/components/FOI/FOIRequest/OIPCDetails/oipcdetails.scss @@ -0,0 +1,38 @@ +.align-division{ + text-align: center; +} + +.divisions-row{ + font-weight: bold; + font-size: 16px; + margin-bottom: 20px; +} + +.arrow { + display: inline-flex; + + .line { + margin-top: 14px; + // width: 320px; + width: 258px; + background: #979797; + height: 1px; + float: left; + } + + .point { + width: 0; + height: 0; + border-top: 5px solid transparent; + border-bottom: 5px solid transparent; + border-left: 10px solid #979797; + float: right; + margin-top: 9px; + } +} + +.MuiDivider-root { + margin: 10px 0px 16px 0px !important; + height: 1.5px !important; +} + \ No newline at end of file diff --git a/forms-flow-web/src/components/FOI/FOIRequest/utils.js b/forms-flow-web/src/components/FOI/FOIRequest/utils.js index f4aa33500..2905bfea2 100644 --- a/forms-flow-web/src/components/FOI/FOIRequest/utils.js +++ b/forms-flow-web/src/components/FOI/FOIRequest/utils.js @@ -321,9 +321,10 @@ export const checkValidationError = ( requiredRequestDetailsValues, requiredAxisDetails, isAddRequest, - currentrequestStatus + currentrequestStatus, + oipcData, + isOipcReview ) => { - return ( requiredApplicantDetails.firstName === "" || requiredApplicantDetails.lastName === "" || @@ -346,7 +347,11 @@ export const checkValidationError = ( .includes("select") || !requiredRequestDetailsValues.receivedDate || !requiredRequestDetailsValues.requestStartDate || - !requiredAxisDetails.axisRequestId + !requiredAxisDetails.axisRequestId || + (oipcData?.length > 0 && isOipcReview && oipcData?.some((oipc) => { + return oipc.oipcno === "" || oipc.receiveddate === null || oipc.receiveddate === "" || oipc.reviewtypeid === null || oipc.reasonid === null || oipc.statusid === null || + oipc.inquiryattributes?.orderno === "" || oipc.inquiryattributes?.inquiryoutcome === null || oipc.inquiryattributes?.inquirydate === null || oipc.inquiryattributes?.inquirydate === ""; + })) ); }; diff --git a/forms-flow-web/src/components/FOI/customComponents/RequestFlag.js b/forms-flow-web/src/components/FOI/customComponents/RequestFlag.js new file mode 100644 index 000000000..1109fe8b4 --- /dev/null +++ b/forms-flow-web/src/components/FOI/customComponents/RequestFlag.js @@ -0,0 +1,249 @@ +import { useState } from "react"; +import "./requestflag.scss"; +import OutlinedInput from "@material-ui/core/OutlinedInput"; +import MenuItem from "@material-ui/core/MenuItem"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { faFlag } from "@fortawesome/free-regular-svg-icons"; +import { faFlag as faSolidFlag } from "@fortawesome/free-solid-svg-icons"; +import Dialog from "@material-ui/core/Dialog"; +import DialogActions from "@material-ui/core/DialogActions"; +import DialogContent from "@material-ui/core/DialogContent"; +import DialogContentText from "@material-ui/core/DialogContentText"; +import DialogTitle from "@material-ui/core/DialogTitle"; +import CloseIcon from "@material-ui/icons/Close"; +import IconButton from "@material-ui/core/IconButton"; +import TextField from "@mui/material/TextField"; +import { useParams } from "react-router-dom"; +import { useDispatch } from "react-redux"; + +//Types are: +//oipcreview +//phasedrelease +const RequestFlag = ({ isActive, type, handleSelect, showFlag = true }) => { + const [isSelected, setIsSelected] = useState(isActive || false); + const [modalOpen, setModalOpen] = useState(false); + const [modalHeading, setModalHeading] = useState(""); + const [modalMessage, setModalMessage] = useState(""); + const [modalDescription, setModalDescription] = useState(""); + + const { requestId, ministryId } = useParams(); + + const dispatch = useDispatch(); + + // These need to be set for each type + let options; + let id; + let modalHeadingActive = ""; + let modalHeadingInactive = ""; + let modalMessageActive = ""; + let modalMessageInactive = ""; + let modalDescriptionActive = ""; + let modalDescriptionInactive = ""; + + // css + let iconClass; + let isSelectedBgClass; + let bgClass; + + switch (type) { + //Need to change heading, message, description for modals as well + case "oipcreview": + options = [ + { + value: true, + label: "OIPC Review", + disabled: false, + }, + { + value: false, + label: "No Review", + disabled: false, + }, + ]; + + id = "oipc-review-flag"; + iconClass = "oipc-review-icon"; + isSelectedBgClass = + "linear-gradient(to right, rgba(250,124,22,0.32) 80%, #fa7c16 0%)"; + bgClass = "linear-gradient(to right, #fff 80%, #fa7c16 0%)"; + + //when setting to active + modalHeadingActive = "OIPC Review"; + modalMessageActive = + "Are you sure you want to flag this request as OIPC review?"; + modalDescriptionActive = ( + + This will create a new OIPC review section on this request. + + ); + + //when setting to inactive + modalHeadingActive = "OIPC Review"; + modalMessageInactive = + "Are you sure you want to remove the OIPC review flag from this request?"; + modalDescriptionInactive = ( + + This will remove the OIPC review section from this request. + + ); + break; + + case "phasedrelease": + options = [ + { + value: true, + label: "Phased Release", + disabled: false, + }, + { + value: false, + label: "Single Release", + disabled: false, + }, + ]; + + id = "phased-release-flag"; + iconClass = "phased-release-icon"; + isSelectedBgClass = + "linear-gradient(to right, rgba(146, 7, 183, 0.32) 80%, #9207b7 0%)"; + bgClass = "linear-gradient(to right, #fff 80%, #9207b7 0%)"; + + //when setting to active + modalHeadingActive = "Phased Release"; + modalMessageActive = + "Are you sure you want to flag this request as a Phased Release?"; + modalDescriptionActive = ( + This will tag the request as Phased Release. + ); + + //when setting to inactive + modalHeadingInactive = "Single Release"; + modalMessageInactive = + "Are you sure you want to change this request to Single Release?"; + modalDescriptionInactive = ( + This will tag the request as Single Release. + ); + break; + } + + const handleValueChange = (e) => { + setIsSelected(e.target.value); + if (type == "oipcreview" && !isActive) { + handleSelect(e.target.value) + } else { + setModalOpen(true); + } + + if (e.target.value == true) { + setModalHeading(modalHeadingActive); + setModalMessage(modalMessageActive); + setModalDescription(modalDescriptionActive); + } else { + setModalHeading(modalHeadingInactive); + setModalMessage(modalMessageInactive); + setModalDescription(modalDescriptionInactive); + } + }; + + const handleClose = () => { + setModalOpen(false); + setIsSelected(isActive); + }; + + const handleSave = (e) => { + setModalOpen(false); + handleSelect(isSelected); + }; + + if (!showFlag) return <>; + return ( + <> +
+
+
+ {isSelected ? ( + + ) : ( + + )} + {/* + Unrestricted + */} + } + disabled={false} + > + {options.map((option) => ( + + {option.label} + + ))} + +
+
+
+
+ { + console.log("onClose"); + }} + aria-labelledby="state-change-dialog-title" + aria-describedby="restricted-modal-text" + maxWidth={"md"} + fullWidth={true} + // id="state-change-dialog" + > + +

{modalHeading}

+ + Close + + +
+ + +
+
{modalMessage}
+
+ {modalDescription} +
+
+
+
+ + + + +
+
+ + ); +}; + +export default RequestFlag; diff --git a/forms-flow-web/src/components/FOI/customComponents/requestflag.scss b/forms-flow-web/src/components/FOI/customComponents/requestflag.scss new file mode 100644 index 000000000..f9ac8c120 --- /dev/null +++ b/forms-flow-web/src/components/FOI/customComponents/requestflag.scss @@ -0,0 +1,80 @@ +.request-flag { + + margin-left: -50px; + padding-top: 50px; + + .request-flag-dropdown-all { + padding-top: 10px; + display: flex; + min-width: 200px !important; + } + + .oipc-review-icon{ + color: #fa7c16; + padding-left: 14px; + margin-top: 3px; + } + + .phased-release-icon{ + color: #9207b7; + padding-left: 14px; + margin-top: 3px; + } + + .restrict-label { + border: 1px solid #a0192f; + width: 100px; + border-radius: 40px 0 0 40px; + } + + .request-flag-select{ + border: 1px solid #a0192f; + //padding-top: 8px; + //width:40px; + height: 40px; + border-radius: 40px; + padding-left: 3px; + //background: linear-gradient(to right, rgba(160,25,47,0.32) 80%, #a0192f 0%); + } + + + .request-flag-dropdown { + color: black !important; + width: 155px; + padding-left: 10px; + } + + .request-flag-dropdown .MuiSelect-icon { + color: white !important; + } + + .request-flag-dropdown .MuiOutlinedInput-notchedOutline { + border: none !important; + } + + .request-flag-dropdown .MuiInputBase-input, + .request-flag-dropdown .MuiOutlinedInput-input { + padding: 0px !important + } + + @media (max-width: 1242px) { + margin-left: 0px; + } + +} + +#restricted-modal-text{ + margin: 15px 65px; + display: block; + color: #000; +} + +.modal-msg { + text-align: center; +} + +.modal-msg-description { + padding-top: 25px; +} + + diff --git a/forms-flow-web/src/components/FOI/customComponents/requestrestriction.scss b/forms-flow-web/src/components/FOI/customComponents/requestrestriction.scss index f1520b98b..e67fc9053 100644 --- a/forms-flow-web/src/components/FOI/customComponents/requestrestriction.scss +++ b/forms-flow-web/src/components/FOI/customComponents/requestrestriction.scss @@ -1,7 +1,5 @@ .requestRestriction { - margin-left: 15px; - .restrict-dropdown-all { padding-top: 10px; display: flex; diff --git a/forms-flow-web/src/modules/FOI/foiRequestsReducer.js b/forms-flow-web/src/modules/FOI/foiRequestsReducer.js index 8dd27296b..9edb8ab4e 100644 --- a/forms-flow-web/src/modules/FOI/foiRequestsReducer.js +++ b/forms-flow-web/src/modules/FOI/foiRequestsReducer.js @@ -145,6 +145,10 @@ const initialState = { ".jpg", ], conversionFormats: [], + oipcOutcomes: [], + oipcStatuses: [], + oipcReviewtypes: [], + oipcInquiryoutcomes: [], }; const foiRequests = (state = initialState, action) => { @@ -307,6 +311,14 @@ const foiRequests = (state = initialState, action) => { return { ...state, recordFormats: action.payload }; case FOI_ACTION_CONSTANTS.CONVERSION_FORMATS: return { ...state, conversionFormats: action.payload }; + case FOI_ACTION_CONSTANTS.OIPC_OUTCOMES: + return { ...state, oipcOutcomes: action.payload }; + case FOI_ACTION_CONSTANTS.OIPC_STATUSES: + return { ...state, oipcStatuses: action.payload }; + case FOI_ACTION_CONSTANTS.OIPC_REVIEWTYPES: + return { ...state, oipcReviewtypes: action.payload }; + case FOI_ACTION_CONSTANTS.OIPC_INQUIRYOUTCOMES: + return { ...state, oipcInquiryoutcomes: action.payload }; default: return state; } diff --git a/request-management-api/migrations/versions/3b399ca506fe_create_oipc_tables.py b/request-management-api/migrations/versions/3b399ca506fe_create_oipc_tables.py new file mode 100644 index 000000000..8733a7034 --- /dev/null +++ b/request-management-api/migrations/versions/3b399ca506fe_create_oipc_tables.py @@ -0,0 +1,208 @@ +"""Create OIPC tables + +Revision ID: 3b399ca506fe +Revises: 7fa7236d06fb +Create Date: 2023-11-14 22:29:58.451320 + +""" +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import postgresql +from sqlalchemy import Table, Column, Integer, String, MetaData +meta = MetaData() +from sqlalchemy.sql.schema import ForeignKey + + +# revision identifiers, used by Alembic. +revision = '3b399ca506fe' +down_revision = '7fa7236d06fb' +branch_labels = None +depends_on = None + + +def upgrade(): + reviewtypestable = op.create_table('OIPCReviewTypes', + sa.Column('reviewtypeid', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('name', sa.String(length=100), unique=False, nullable=False), + sa.Column('isactive', sa.Boolean(), unique=False, nullable=False), + sa.PrimaryKeyConstraint('reviewtypeid') + ) + + reasonstable = op.create_table('OIPCReasons', + sa.Column('reasonid', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('name', sa.String(length=100), unique=False, nullable=False), + sa.Column('isactive', sa.Boolean(), unique=False, nullable=False), + sa.PrimaryKeyConstraint('reasonid') + ) + + reviewtypes_reasons_table = op.create_table('OIPCReviewTypesReasons', + sa.Column('reviewtypereasonid', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('reviewtypeid', sa.Integer(), ForeignKey('OIPCReviewTypes.reviewtypeid'), unique=False, nullable=False), + sa.Column('reasonid', sa.Integer(), ForeignKey('OIPCReasons.reasonid'), unique=False, nullable=False), + sa.Column('isactive', sa.Boolean(), unique=False, nullable=False), + sa.PrimaryKeyConstraint('reviewtypereasonid'), + ) + + statusestable = op.create_table('OIPCStatuses', + sa.Column('statusid', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('name', sa.String(length=100), unique=False, nullable=False), + sa.Column('isactive', sa.Boolean(), unique=False, nullable=False), + sa.PrimaryKeyConstraint('statusid') + ) + + outcomestable = op.create_table('OIPCOutcomes', + sa.Column('outcomeid', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('name', sa.String(length=100), unique=False, nullable=False), + sa.Column('isactive', sa.Boolean(), unique=False, nullable=False), + sa.PrimaryKeyConstraint('outcomeid') + ) + + inquiryoutcomestable = op.create_table('OIPCInquiryOutcomes', + sa.Column('inquiryoutcomeid', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('name', sa.String(length=100), unique=False, nullable=False), + sa.Column('isactive', sa.Boolean(), unique=False, nullable=False), + sa.PrimaryKeyConstraint('inquiryoutcomeid') + ) + + op.execute('''INSERT INTO public."OIPCReviewTypes" (name, isactive) + VALUES + ('Complaint', True), + ('Review', True), + ('Investigation', True);commit;''') + + op.execute('''INSERT INTO public."OIPCReasons" (name, isactive) + VALUES + ('Adequate Search', True), + ('Application of Exceptions', True), + ('Deemed Refusal', True), + ('Extension', True), + ('Fee Amount', True), + ('Fee Waiver', True), + ('Records do not Exist', True), + ('Duty to Assist', True), + ('TPN - 22', True), + ('TPN - 21', True), + ('TPN - 18.1', True), + ('Reg 3', True), + ('Reg 4', True), + ('Reg 5', True), + ('s. 43', True), + ('Other', True);commit;''') + + op.execute('''INSERT INTO public."OIPCStatuses" (name, isactive) + VALUES + ('Mediation', True), + ('Investigation', True), + ('Inquiry', True), + ('Awaiting Order', True), + ('Closed', True);commit;''') + + op.execute('''INSERT INTO public."OIPCOutcomes" (name, isactive) + VALUES + ('Abandoned', True), + ('Withdrawn', True), + ('Resolved in Mediation', True), + ('Closed', True), + ('Amend', True);commit;''') + + op.execute('''INSERT INTO public."OIPCInquiryOutcomes" (name, isactive) + VALUES + ('Decision Upheld', True), + ('Decision Partially Upheld', True), + ('Decision Overturned', True);commit;''') + + op.execute('''INSERT INTO public."OIPCReviewTypesReasons" (reviewtypeid, reasonid, isactive) + VALUES + ( + (SELECT reviewtypeid FROM public."OIPCReviewTypes" WHERE name = 'Complaint'), + (SELECT reasonid FROM public."OIPCReasons" WHERE name = 'Adequate Search'), + True + ), + ( + (SELECT reviewtypeid FROM public."OIPCReviewTypes" WHERE name = 'Complaint'), + (SELECT reasonid FROM public."OIPCReasons" WHERE name = 'Extension'), + True + ), + ( + (SELECT reviewtypeid FROM public."OIPCReviewTypes" WHERE name = 'Complaint'), + (SELECT reasonid FROM public."OIPCReasons" WHERE name = 'Fee Amount'), + True + ), + ( + (SELECT reviewtypeid FROM public."OIPCReviewTypes" WHERE name = 'Complaint'), + (SELECT reasonid FROM public."OIPCReasons" WHERE name = 'Fee Waiver'), + True + ), + ( + (SELECT reviewtypeid FROM public."OIPCReviewTypes" WHERE name = 'Complaint'), + (SELECT reasonid FROM public."OIPCReasons" WHERE name = 'Duty to Assist'), + True + ), + ( + (SELECT reviewtypeid FROM public."OIPCReviewTypes" WHERE name = 'Complaint'), + (SELECT reasonid FROM public."OIPCReasons" WHERE name = 'Other'), + True + ), + ( + (SELECT reviewtypeid FROM public."OIPCReviewTypes" WHERE name = 'Review'), + (SELECT reasonid FROM public."OIPCReasons" WHERE name = 'Application of Exceptions'), + True + ), + ( + (SELECT reviewtypeid FROM public."OIPCReviewTypes" WHERE name = 'Review'), + (SELECT reasonid FROM public."OIPCReasons" WHERE name = 'Deemed Refusal'), + True + ), + ( + (SELECT reviewtypeid FROM public."OIPCReviewTypes" WHERE name = 'Review'), + (SELECT reasonid FROM public."OIPCReasons" WHERE name = 'TPN - 22'), + True + ), + ( + (SELECT reviewtypeid FROM public."OIPCReviewTypes" WHERE name = 'Review'), + (SELECT reasonid FROM public."OIPCReasons" WHERE name = 'TPN - 21'), + True + ), + ( + (SELECT reviewtypeid FROM public."OIPCReviewTypes" WHERE name = 'Review'), + (SELECT reasonid FROM public."OIPCReasons" WHERE name = 'TPN - 18.1'), + True + ), + ( + (SELECT reviewtypeid FROM public."OIPCReviewTypes" WHERE name = 'Review'), + (SELECT reasonid FROM public."OIPCReasons" WHERE name = 'Reg 3'), + True + ), + ( + (SELECT reviewtypeid FROM public."OIPCReviewTypes" WHERE name = 'Review'), + (SELECT reasonid FROM public."OIPCReasons" WHERE name = 'Reg 4'), + True + ), + ( + (SELECT reviewtypeid FROM public."OIPCReviewTypes" WHERE name = 'Review'), + (SELECT reasonid FROM public."OIPCReasons" WHERE name = 'Reg 5'), + True + ), + ( + (SELECT reviewtypeid FROM public."OIPCReviewTypes" WHERE name = 'Review'), + (SELECT reasonid FROM public."OIPCReasons" WHERE name = 's. 43'), + True + ), + ( + (SELECT reviewtypeid FROM public."OIPCReviewTypes" WHERE name = 'Review'), + (SELECT reasonid FROM public."OIPCReasons" WHERE name = 'Other'), + True + ), + ( + (SELECT reviewtypeid FROM public."OIPCReviewTypes" WHERE name = 'Investigation'), + (SELECT reasonid FROM public."OIPCReasons" WHERE name = 'Other'), + True + );commit;''') + +def downgrade(): + op.drop_table('OIPCReviewTypesReasons') + op.drop_table('OIPCReviewTypes') + op.drop_table('OIPCReasons') + op.drop_table('OIPCStatuses') + op.drop_table('OIPCOutcomes') + op.drop_table('OIPCInquiryOutcomes') diff --git a/request-management-api/migrations/versions/455a24da8c58_oipc_txn_changes.py b/request-management-api/migrations/versions/455a24da8c58_oipc_txn_changes.py new file mode 100644 index 000000000..20f8ab5c5 --- /dev/null +++ b/request-management-api/migrations/versions/455a24da8c58_oipc_txn_changes.py @@ -0,0 +1,56 @@ +"""empty message + +Revision ID: 455a24da8c58 +Revises: 3b399ca506fe +Create Date: 2023-11-15 12:31:33.274617 + +""" +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import postgresql + +# revision identifiers, used by Alembic. +revision = '455a24da8c58' +down_revision = '3b399ca506fe' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('FOIMinistryRequests', sa.Column('isoipcreview', sa.Boolean, nullable=True,default=False)) + op.create_table('FOIRequestOIPC', + sa.Column('oipcid', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('foiministryrequest_id', sa.Integer(), nullable=False), + sa.Column('foiministryrequestversion_id', sa.Integer(), nullable=False), + sa.Column('oipcno', sa.String(length=250), nullable=False), + sa.Column('investigator', sa.String(length=500), nullable=True), + sa.Column('reviewtypeid', sa.Integer(), nullable=False), + sa.Column('reasonid', sa.Integer(), nullable=False), + sa.Column('statusid', sa.Integer(), nullable=True), + sa.Column('outcomeid', sa.Integer(), nullable=True), + sa.Column('isinquiry', sa.Boolean(), nullable=True), + sa.Column('isjudicialreview', sa.Boolean(), nullable=True), + sa.Column('issubsequentappeal', sa.Boolean(), nullable=True), + sa.Column('inquiryattributes', sa.JSON, nullable=True), + sa.Column('receiveddate', sa.Date(), nullable=True), + sa.Column('closeddate', sa.Date(), nullable=True), + sa.Column('created_at', sa.DateTime(), nullable=False), + sa.Column('createdby', sa.String(length=120), nullable=False), + sa.Column('updated_at', sa.DateTime(), nullable=True), + sa.Column('updatedby', sa.String(length=120), nullable=True), + sa.ForeignKeyConstraint(['foiministryrequest_id', 'foiministryrequestversion_id'], ['FOIMinistryRequests.foiministryrequestid', 'FOIMinistryRequests.version'], ), + sa.ForeignKeyConstraint(['reviewtypeid'], ['OIPCReviewTypes.reviewtypeid']), + sa.ForeignKeyConstraint(['reasonid'], ['OIPCReasons.reasonid']), + sa.ForeignKeyConstraint(['statusid'], ['OIPCStatuses.statusid']), + sa.ForeignKeyConstraint(['outcomeid'], ['OIPCOutcomes.outcomeid']), + sa.PrimaryKeyConstraint('oipcid') + ) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column('FOIMinistryRequests', 'isoipcreview') + op.drop_table('FOIRequestOIPC') + # ### end Alembic commands ### diff --git a/request-management-api/request_api/models/FOIMinistryRequests.py b/request-management-api/request_api/models/FOIMinistryRequests.py index cd6e2a8e9..8abc19dc7 100644 --- a/request-management-api/request_api/models/FOIMinistryRequests.py +++ b/request-management-api/request_api/models/FOIMinistryRequests.py @@ -92,6 +92,10 @@ class FOIMinistryRequest(db.Model): "FOIMinistryRequest.version==FOIMinistryRequestDocument.foiministryrequestversion_id)") extensions = relationship('FOIRequestExtension', primaryjoin="and_(FOIMinistryRequest.foiministryrequestid==FOIRequestExtension.foiministryrequest_id, " "FOIMinistryRequest.version==FOIRequestExtension.foiministryrequestversion_id)") + + oipcreviews = relationship('FOIRequestOIPC', primaryjoin="and_(FOIMinistryRequest.foiministryrequestid==FOIRequestOIPC.foiministryrequest_id, " + "FOIMinistryRequest.version==FOIRequestOIPC.foiministryrequestversion_id)") + assignee = relationship('FOIAssignee', foreign_keys="[FOIMinistryRequest.assignedto]") ministryassignee = relationship('FOIAssignee', foreign_keys="[FOIMinistryRequest.assignedministryperson]") @@ -99,6 +103,8 @@ class FOIMinistryRequest(db.Model): "FOIMinistryRequest.version==FOIMinistryRequestSubjectCode.foiministryrequestversion)") isofflinepayment = db.Column(db.Boolean, unique=False, nullable=True,default=False) + isoipcreview = db.Column(db.Boolean, unique=False, nullable=True,default=False) + @classmethod def getrequest(cls,ministryrequestid): request_schema = FOIMinistryRequestSchema(many=False) @@ -358,7 +364,7 @@ def getrequestssubquery(cls, groups, filterfields, keyword, additionalfilter, us if(len(filterfields) > 0 and keyword is not None): filtercondition = [] - if(keyword != "restricted"): + if(keyword != "restricted" and keyword.lower() != "oipc"): for field in filterfields: filtercondition.append(FOIMinistryRequest.findfield(field, iaoassignee, ministryassignee).ilike('%'+keyword+'%')) else: @@ -366,6 +372,8 @@ def getrequestssubquery(cls, groups, filterfields, keyword, additionalfilter, us filtercondition.append(FOIRestrictedMinistryRequest.isrestricted == True) else: filtercondition.append(ministry_restricted_requests.isrestricted == True) + if (keyword.lower() == "oipc"): + filtercondition.append(FOIMinistryRequest.isoipcreview == True) intakesorting = case([ (and_(FOIMinistryRequest.assignedto == None, FOIMinistryRequest.assignedgroup == 'Intake Team'), # Unassigned requests first @@ -482,7 +490,8 @@ def getrequestssubquery(cls, groups, filterfields, keyword, additionalfilter, us extensions, FOIRestrictedMinistryRequest.isrestricted.label('isiaorestricted'), ministry_restricted_requests.isrestricted.label('isministryrestricted'), - SubjectCode.name.label('subjectcode') + SubjectCode.name.label('subjectcode'), + FOIMinistryRequest.isoipcreview.label('isoipcreview') ] basequery = _session.query( @@ -1001,7 +1010,8 @@ def getbasequery(cls, iaoassignee, ministryassignee, userid=None, requestby='IAO extensions, FOIRestrictedMinistryRequest.isrestricted.label('isiaorestricted'), ministry_restricted_requests.isrestricted.label('isministryrestricted'), - SubjectCode.name.label('subjectcode') + SubjectCode.name.label('subjectcode'), + FOIMinistryRequest.isoipcreview.label('isoipcreview') ] basequery = _session.query( @@ -1307,5 +1317,5 @@ class Meta: 'foirequest.receivedmodeid','requeststatus.requeststatusid','requeststatus.name','programarea.bcgovcode', 'programarea.name','foirequest_id','foirequestversion_id','created_at','updated_at','createdby','assignedministryperson', 'assignedministrygroup','cfrduedate','closedate','closereasonid','closereason.name', - 'assignee.firstname','assignee.lastname','ministryassignee.firstname','ministryassignee.lastname', 'axisrequestid', 'axissyncdate', 'requestpagecount', 'linkedrequests', 'ministrysignoffapproval', 'identityverified','originalldd') + 'assignee.firstname','assignee.lastname','ministryassignee.firstname','ministryassignee.lastname', 'axisrequestid', 'axissyncdate', 'requestpagecount', 'linkedrequests', 'ministrysignoffapproval', 'identityverified','originalldd','isoipcreview') diff --git a/request-management-api/request_api/models/FOIRawRequests.py b/request-management-api/request_api/models/FOIRawRequests.py index 9a0169647..cedb898b5 100644 --- a/request-management-api/request_api/models/FOIRawRequests.py +++ b/request-management-api/request_api/models/FOIRawRequests.py @@ -570,7 +570,8 @@ def getbasequery(cls, additionalfilter=None, userid=None, isiaorestrictedfileman literal(None).label('extensions'), isiaorestricted, literal(None).label('isministryrestricted'), - subjectcode + subjectcode, + literal(None).label('isoipcreview') ] basequery = _session.query(*selectedcolumns).join(subquery_maxversion, and_(*joincondition)).join(FOIAssignee, FOIAssignee.username == FOIRawRequest.assignedto, isouter=True) diff --git a/request-management-api/request_api/models/FOIRequestOIPC.py b/request-management-api/request_api/models/FOIRequestOIPC.py new file mode 100644 index 000000000..a68407b9f --- /dev/null +++ b/request-management-api/request_api/models/FOIRequestOIPC.py @@ -0,0 +1,55 @@ +from flask.app import Flask +from sqlalchemy.sql.schema import ForeignKey +from .db import db, ma +from datetime import datetime +from sqlalchemy.orm import relationship,backref +from .default_method_result import DefaultMethodResult +from sqlalchemy.dialects.postgresql import JSON, UUID +from sqlalchemy.sql.expression import distinct +from sqlalchemy import text, and_, func +import logging +import json +from sqlalchemy.dialects.postgresql import JSON, insert + +class FOIRequestOIPC(db.Model): + # Name of the table in our database + __tablename__ = 'FOIRequestOIPC' + # Defining the columns + oipcid = db.Column(db.Integer, primary_key=True,autoincrement=True) + foiministryrequest_id =db.Column(db.Integer, db.ForeignKey('FOIMinistryRequests.foiministryrequestid')) + foiministryrequestversion_id =db.Column(db.Integer, db.ForeignKey('FOIMinistryRequests.version')) + oipcno = db.Column(db.String(120), unique=False, nullable=True) + reviewtypeid = db.Column(db.Integer,ForeignKey('OIPCReviewTypes.reviewtypeid')) + reviewtype = relationship("OIPCReviewTypes",backref=backref("OIPCReviewTypes"),uselist=False) + reasonid = db.Column(db.Integer,ForeignKey('OIPCReasons.reasonid')) + reason = relationship("OIPCReasons",backref=backref("OIPCReasons"),uselist=False) + statusid = db.Column(db.Integer,ForeignKey('OIPCStatuses.statusid')) + status = relationship("OIPCStatuses",backref=backref("OIPCStatuses"),uselist=False) + outcomeid = db.Column(db.Integer,ForeignKey('OIPCOutcomes.outcomeid')) + outcome = relationship("OIPCOutcomes",backref=backref("OIPCOutcomes"),uselist=False) + isinquiry = db.Column(db.Boolean, unique=False, nullable=True) + inquiryattributes = db.Column(JSON, unique=False, nullable=True) + isjudicialreview = db.Column(db.Boolean, unique=False, nullable=True) + issubsequentappeal = db.Column(db.Boolean, unique=False, nullable=True) + investigator = db.Column(db.String(500), unique=False, nullable=True) + receiveddate = db.Column(db.Date, nullable=True) + closeddate = db.Column(db.Date, nullable=True) + created_at = db.Column(db.DateTime, default=datetime.now) + createdby = db.Column(db.String(120), unique=False, nullable=False) + updated_at = db.Column(db.DateTime, nullable=True) + updatedby = db.Column(db.String(120), unique=False, nullable=True) + + + @classmethod + def getoipc(cls,ministryrequestid,ministryrequestversion): + oipc_schema = FOIRequestOIPCSchema(many=True) + _oipclist = db.session.query(FOIRequestOIPC).filter(FOIRequestOIPC.foiministryrequest_id == ministryrequestid , FOIRequestOIPC.foiministryrequestversion_id == ministryrequestversion).order_by(FOIRequestOIPC.oipcid.asc()).all() + divisioninfos = oipc_schema.dump(_oipclist) + return divisioninfos + + +class FOIRequestOIPCSchema(ma.Schema): + class Meta: + fields = ('oipcid', 'version', 'ministryrequestid', 'investigator','ministryversion','oipcno','reviewtypeid','reasonid','statusid','outcomeid','isinquiry','inquiryattributes','isjudicialreview', + 'issubsequentappeal','isactive','receiveddate','closeddate','created_at','createdby','updated_at','updatedby', + 'reviewtype.name', 'reason.name', 'status.name', 'outcome.name') \ No newline at end of file diff --git a/request-management-api/request_api/models/OIPCInquiryOutcomes.py b/request-management-api/request_api/models/OIPCInquiryOutcomes.py new file mode 100644 index 000000000..a613f0a7f --- /dev/null +++ b/request-management-api/request_api/models/OIPCInquiryOutcomes.py @@ -0,0 +1,19 @@ +from .db import db, ma + +class OIPCInquiryOutcomes(db.Model): + __tablename__ = 'OIPCInquiryOutcomes' + # Defining the columns + inquiryoutcomeid = db.Column(db.Integer, primary_key=True,autoincrement=True) + name = db.Column(db.String(100), unique=False, nullable=False) + isactive = db.Column(db.Boolean, unique=False, nullable=False) + + @classmethod + def getinquiryoutcomes(cls): + type_schema = InquiryOutcomeSchema(many=True) + query = db.session.query(OIPCInquiryOutcomes).filter_by(isactive=True).all() + return type_schema.dump(query) + + +class InquiryOutcomeSchema(ma.Schema): + class Meta: + fields = ('inquiryoutcomeid', 'name','isactive') \ No newline at end of file diff --git a/request-management-api/request_api/models/OIPCOutcomes.py b/request-management-api/request_api/models/OIPCOutcomes.py new file mode 100644 index 000000000..c891a88a8 --- /dev/null +++ b/request-management-api/request_api/models/OIPCOutcomes.py @@ -0,0 +1,19 @@ +from .db import db, ma + +class OIPCOutcomes(db.Model): + __tablename__ = 'OIPCOutcomes' + # Defining the columns + outcomeid = db.Column(db.Integer, primary_key=True,autoincrement=True) + name = db.Column(db.String(100), unique=False, nullable=False) + isactive = db.Column(db.Boolean, unique=False, nullable=False) + + @classmethod + def getoutcomes(cls): + type_schema = OutcomeSchema(many=True) + query = db.session.query(OIPCOutcomes).filter_by(isactive=True).all() + return type_schema.dump(query) + + +class OutcomeSchema(ma.Schema): + class Meta: + fields = ('outcomeid', 'name','isactive') \ No newline at end of file diff --git a/request-management-api/request_api/models/OIPCReasons.py b/request-management-api/request_api/models/OIPCReasons.py new file mode 100644 index 000000000..6eadc03b6 --- /dev/null +++ b/request-management-api/request_api/models/OIPCReasons.py @@ -0,0 +1,20 @@ + +from .db import db, ma + +class OIPCReasons(db.Model): + __tablename__ = 'OIPCReasons' + # Defining the columns + reasonid = db.Column(db.Integer, primary_key=True,autoincrement=True) + name = db.Column(db.String(100), unique=False, nullable=False) + isactive = db.Column(db.Boolean, unique=False, nullable=False) + + @classmethod + def getreasons(cls): + type_schema = ReasonSchema(many=True) + query = db.session.query(OIPCReasons).filter_by(isactive=True).all() + return type_schema.dump(query) + + +class ReasonSchema(ma.Schema): + class Meta: + fields = ('reasonid', 'name','isactive') \ No newline at end of file diff --git a/request-management-api/request_api/models/OIPCReviewTypes.py b/request-management-api/request_api/models/OIPCReviewTypes.py new file mode 100644 index 000000000..bb5317de8 --- /dev/null +++ b/request-management-api/request_api/models/OIPCReviewTypes.py @@ -0,0 +1,19 @@ +from .db import db, ma + +class OIPCReviewTypes(db.Model): + __tablename__ = 'OIPCReviewTypes' + # Defining the columns + reviewtypeid = db.Column(db.Integer, primary_key=True,autoincrement=True) + name = db.Column(db.String(100), unique=False, nullable=False) + isactive = db.Column(db.Boolean, unique=False, nullable=False) + + @classmethod + def getreviewtypes(cls): + type_schema = ReviewTypeSchema(many=True) + query = db.session.query(OIPCReviewTypes).filter_by(isactive=True).all() + return type_schema.dump(query) + + +class ReviewTypeSchema(ma.Schema): + class Meta: + fields = ('reviewtypeid', 'name','isactive') \ No newline at end of file diff --git a/request-management-api/request_api/models/OIPCReviewTypesReasons.py b/request-management-api/request_api/models/OIPCReviewTypesReasons.py new file mode 100644 index 000000000..3fc17e221 --- /dev/null +++ b/request-management-api/request_api/models/OIPCReviewTypesReasons.py @@ -0,0 +1,43 @@ +from .db import db, ma +from sqlalchemy.orm import relationship, backref +from sqlalchemy.sql.schema import ForeignKey +from sqlalchemy import text + +class OIPCReviewTypesReasons(db.Model): + __tablename__ = 'OIPCReviewTypesReasons' + # Defining the columns + reviewtypereasonid = db.Column(db.Integer, primary_key=True,autoincrement=True) + reviewtypeid = db.Column(db.Integer, ForeignKey('FOIReviewTypes')) + relationship("FOIReviewTypes", backref=backref("FOIReviewTypes"), uselist=False) + reasonid = db.Column(db.Integer, ForeignKey('FOIReasons')) + relationship("FOIReasons", backref=backref("FOIReasons"), uselist=False) + isactive = db.Column(db.Boolean, unique=False, nullable=False) + + @classmethod + def getreviewtypeswithreasons(cls): + type_schema = ReviewTypeReasonSchema(many=True) + sql = ''' + SELECT types.reviewtypeid, reasons.reasonid, reasons.name as reason_name, types.name as type_name, typereason.isactive as reviewtypereason_isactive, reasons.isactive as reason_isactive, types.isactive as reviewtype_isactive FROM public."OIPCReviewTypesReasons" typereason + JOIN public."OIPCReasons" reasons + ON reasons.reasonid = typereason.reasonid + JOIN public."OIPCReviewTypes" types + ON types.reviewtypeid = typereason.reviewtypeid + ORDER BY reviewtypereasonid ASC + ''' + rs = db.session.execute(text(sql)) + reviewtypereasons = [] + for row in rs: + reviewtypereasons.append({ + "reviewtypeid": row["reviewtypeid"], + "reasonid": row["reasonid"], + "reason_name": row["reason_name"], + "type_name": row["type_name"], + "reviewtypereason_isactive": row["reviewtypereason_isactive"], + "reviewtype_isactive": row["reviewtype_isactive"], + "reason_isactive": row["reason_isactive"], + }) + return reviewtypereasons + +class ReviewTypeReasonSchema(ma.Schema): + class Meta: + fields = ('reviewtypereasonid', 'reviewtypeid', 'reasonid', 'isactive') \ No newline at end of file diff --git a/request-management-api/request_api/models/OIPCStatuses.py b/request-management-api/request_api/models/OIPCStatuses.py new file mode 100644 index 000000000..dce07aac2 --- /dev/null +++ b/request-management-api/request_api/models/OIPCStatuses.py @@ -0,0 +1,19 @@ +from .db import db, ma + +class OIPCStatuses(db.Model): + __tablename__ = 'OIPCStatuses' + # Defining the columns + statusid = db.Column(db.Integer, primary_key=True,autoincrement=True) + name = db.Column(db.String(100), unique=False, nullable=False) + isactive = db.Column(db.Boolean, unique=False, nullable=False) + + @classmethod + def getstatuses(cls): + type_schema = StatusSchema(many=True) + query = db.session.query(OIPCStatuses).filter_by(isactive=True).all() + return type_schema.dump(query) + + +class StatusSchema(ma.Schema): + class Meta: + fields = ('statusid', 'name','isactive') \ No newline at end of file diff --git a/request-management-api/request_api/resources/foiflowmasterdata.py b/request-management-api/request_api/resources/foiflowmasterdata.py index 6525eef82..297880fbf 100644 --- a/request-management-api/request_api/resources/foiflowmasterdata.py +++ b/request-management-api/request_api/resources/foiflowmasterdata.py @@ -33,6 +33,7 @@ from request_api.services.extensionreasonservice import extensionreasonservice from request_api.services.cacheservice import cacheservice from request_api.services.subjectcodeservice import subjectcodeservice +from request_api.services.oipcservice import oipcservice import json import request_api import requests @@ -380,3 +381,92 @@ def post(): return {"success": resp_flag } , 200 if resp_flag == True else 500 except BusinessException: return "Error happened while clearing cache" , 500 + +@cors_preflight('GET,OPTIONS') +@API.route('/foiflow/oipc/reviewtypes') +class FOIFlowOIPCReviewTypes(Resource): + """Retrieves OIPC review types along with reasons for each type + """ + @staticmethod + @TRACER.trace() + @cross_origin(origins=allowedorigins()) + # @auth.require + @request_api.cache.cached( + key_prefix="oipcreviewtypesreasons", + unless=cache_filter, + response_filter=response_filter + ) + def get(): + try: + data = oipcservice().getreviewtypeswithreasons() + jsondata = json.dumps(data) + return jsondata , 200 + except BusinessException: + return "Error happened while accessing OIPC review types and associated reasons" , 500 + +@cors_preflight('GET,OPTIONS') +@API.route('/foiflow/oipc/statuses') +class FOIFlowOIPCStatuses(Resource): + """Retrieves OIPC statuses + """ + @staticmethod + @TRACER.trace() + @cross_origin(origins=allowedorigins()) + # @auth.require + @request_api.cache.cached( + key_prefix="oipcstatuses", + unless=cache_filter, + response_filter=response_filter + ) + def get(): + try: + data = oipcservice().getstatuses() + jsondata = json.dumps(data) + return jsondata , 200 + except BusinessException: + return "Error happened while accessing OIPC statuses" , 500 + + +@cors_preflight('GET,OPTIONS') +@API.route('/foiflow/oipc/outcomes') +class FOIFlowOIPCOutcomes(Resource): + """Retrieves OIPC outcomes + """ + @staticmethod + @TRACER.trace() + @cross_origin(origins=allowedorigins()) + # @auth.require + @request_api.cache.cached( + key_prefix="oipcoutcomes", + unless=cache_filter, + response_filter=response_filter + ) + def get(): + try: + data = oipcservice().getoutcomes() + jsondata = json.dumps(data) + return jsondata , 200 + except BusinessException: + return "Error happened while accessing OIPC outcomes" , 500 + +@cors_preflight('GET,OPTIONS') +@API.route('/foiflow/oipc/inquiryoutcomes') +class FOIFlowOIPCInquiryOutcomes(Resource): + """Retrieves OIPC inquiry outcomes + """ + @staticmethod + @TRACER.trace() + @cross_origin(origins=allowedorigins()) + # @auth.require + @request_api.cache.cached( + key_prefix="oipcinquiryoutcomes", + unless=cache_filter, + response_filter=response_filter + ) + def get(): + try: + data = oipcservice().getinquiryoutcomes() + jsondata = json.dumps(data) + return jsondata , 200 + except BusinessException: + return "Error happened while accessing OIPC inquiry outcomes" , 500 \ No newline at end of file diff --git a/request-management-api/request_api/schemas/foirequestwrapper.py b/request-management-api/request_api/schemas/foirequestwrapper.py index efdb027ac..4cfc64bea 100644 --- a/request-management-api/request_api/schemas/foirequestwrapper.py +++ b/request-management-api/request_api/schemas/foirequestwrapper.py @@ -53,6 +53,34 @@ class Meta: # pylint: disable=too-few-public-methods documentpath = fields.Str(data_key="documentpath",allow_none=False, validate=[validate.Length(max=1000, error=MAX_EXCEPTION_MESSAGE)]) filename = fields.Str(data_key="filename",allow_none=False, validate=[validate.Length(max=120, error=MAX_EXCEPTION_MESSAGE)]) category = fields.Str(data_key="category",allow_none=False, validate=[validate.Length(max=120, error=MAX_EXCEPTION_MESSAGE)]) + +class FOIOIPCInquirySchema(Schema): + class Meta: # pylint: disable=too-few-public-methods + """Exclude unknown fields in the deserialized output.""" + + unknown = EXCLUDE + inquirydate = fields.Str(data_key="inquirydate",allow_none=True) + orderno = fields.Str(data_key="orderno",allow_none=True) + inquiryoutcome = fields.Int(data_key="inquiryoutcome",allow_none=True) + +class FOIMinistryRequestOIPCSchema(Schema): + class Meta: # pylint: disable=too-few-public-methods + """Exclude unknown fields in the deserialized output.""" + + unknown = EXCLUDE + oipcno = fields.Str(data_key="oipcno") + reviewtypeid = fields.Int(data_key="reviewtypeid") + reasonid = fields.Int(data_key="reasonid") + statusid = fields.Int(data_key="statusid") + outcomeid = fields.Int(data_key="outcomeid",allow_none=True) + investigator = fields.Str(data_key="investigator",allow_none=True, validate=[validate.Length(max=500, error=MAX_EXCEPTION_MESSAGE)]) + isinquiry = fields.Bool(data_key="isinquiry") + isjudicialreview = fields.Bool(data_key="isjudicialreview") + issubsequentappeal = fields.Bool(data_key="issubsequentappeal") + inquiryattributes = fields.Nested(FOIOIPCInquirySchema, data_key="inquiryattributes", allow_none=True) + receiveddate = fields.Str(data_key="receiveddate",allow_none=True) + closeddate = fields.Str(data_key="closeddate",allow_none=True) + class FOIRequestWrapperSchema(Schema): class Meta: # pylint: disable=too-few-public-methods @@ -110,6 +138,7 @@ class Meta: # pylint: disable=too-few-public-methods correctionalServiceNumber = fields.Str(data_key="correctionalServiceNumber",allow_none=True, validate=[validate.Length(max=50, error=MAX_EXCEPTION_MESSAGE)]) publicServiceEmployeeNumber = fields.Str(data_key="publicServiceEmployeeNumber",allow_none=True, validate=[validate.Length(max=50, error=MAX_EXCEPTION_MESSAGE)]) isiaorestricted = fields.Bool(data_key="isiaorestricted") + isoipcreview = fields.Bool(data_key="isoipcreview") selectedMinistries = fields.Nested(FOIMinistryRequestWrapperSchema, many=True) additionalPersonalInfo = fields.Nested(FOIAdditionallPersonalInfoWrapperSchema,required=False,allow_none=True) @@ -120,6 +149,8 @@ class Meta: # pylint: disable=too-few-public-methods linkedRequests = fields.List(fields.Dict(data_key="linkedRequests", required=False)) identityVerified = fields.Str(data_key="identityVerified",allow_none=True) + oipcdetails = fields.Nested(FOIMinistryRequestOIPCSchema, many=True,allow_none=True) + class EditableFOIMinistryRequestWrapperSchema(Schema): class Meta: # pylint: disable=too-few-public-methods diff --git a/request-management-api/request_api/services/dashboardservice.py b/request-management-api/request_api/services/dashboardservice.py index ba2f913d1..5a89ac3c6 100644 --- a/request-management-api/request_api/services/dashboardservice.py +++ b/request-management-api/request_api/services/dashboardservice.py @@ -50,6 +50,8 @@ def __preparefoirequestinfo(self, request, receiveddate, receiveddateuf, idnumbe baserequestinfo.update({'onBehalfFirstName': request.onBehalfFirstName}) baserequestinfo.update({'onBehalfLastName': request.onBehalfLastName}) baserequestinfo.update({'requestPageCount': request.requestPageCount}) + isoipcreview = request.isoipcreview if request.isoipcreview == True else False + baserequestinfo.update({'isoipcreview': isoipcreview}) return baserequestinfo def __preparebaserequestinfo(self, id, requesttype, status, receiveddate, receiveddateuf, assignedgroup, assignedto, idnumber, axisrequestid, version, description, fromdate, todate): @@ -148,6 +150,8 @@ def getministryrequestqueuepagination (self, groups=None, page=1, size=10, sorti isministryrestricted = request.isministryrestricted if request.isministryrestricted == True else False _openrequest.update({'isministryrestricted': isministryrestricted}) + isoipcreview = request.isoipcreview if request.isoipcreview == True else False + _openrequest.update({'isoipcreview': isoipcreview}) requestqueue.append(_openrequest) meta = { diff --git a/request-management-api/request_api/services/external/keycloakadminservice.py b/request-management-api/request_api/services/external/keycloakadminservice.py index 5ec6af21e..3a632d34b 100644 --- a/request-management-api/request_api/services/external/keycloakadminservice.py +++ b/request-management-api/request_api/services/external/keycloakadminservice.py @@ -24,8 +24,8 @@ class KeycloakAdminService: def get_token(self): _accesstoken=None try: - cache_client = redis.from_url(self.cache_redis_url,decode_responses=True) - _accesstoken = cache_client.get("foi:kcsrcacnttoken") + #cache_client = redis.from_url(self.cache_redis_url,decode_responses=True) + _accesstoken = None #cache_client.get("foi:kcsrcacnttoken") if _accesstoken is None: url = '{0}/auth/realms/{1}/protocol/openid-connect/token'.format(self.keycloakhost,self.keycloakrealm) params = { @@ -38,11 +38,11 @@ def get_token(self): } x = requests.post(url, params, verify=True).content.decode('utf-8') _accesstoken = str(ast.literal_eval(x)['access_token']) - cache_client.set("foi:kcsrcacnttoken",_accesstoken,ex=int(self.kctokenexpiry)) + #cache_client.set("foi:kcsrcacnttoken",_accesstoken,ex=int(self.kctokenexpiry)) except BusinessException as exception: print("Error happened while accessing token on KeycloakAdminService {0}".format(exception.message)) - finally: - cache_client = None + # finally: + # cache_client = None return _accesstoken diff --git a/request-management-api/request_api/services/foirequest/requestservicebuilder.py b/request-management-api/request_api/services/foirequest/requestservicebuilder.py index 4f6db7aa5..b6315671b 100644 --- a/request-management-api/request_api/services/foirequest/requestservicebuilder.py +++ b/request-management-api/request_api/services/foirequest/requestservicebuilder.py @@ -8,6 +8,8 @@ from request_api.models.FOIRequestApplicants import FOIRequestApplicant from request_api.models.FOIRequestApplicantMappings import FOIRequestApplicantMapping from request_api.models.FOIRequestTeams import FOIRequestTeam +from request_api.models.FOIRequestOIPC import FOIRequestOIPC + from datetime import datetime as datetime2 from request_api.utils.enums import MinistryTeamWithKeycloackGroup from request_api.services.foirequest.requestserviceconfigurator import requestserviceconfigurator @@ -34,6 +36,10 @@ def createministry(self, requestschema, ministry, activeversion, userid, filenum foiministryrequest.linkedrequests = requestschema.get("linkedRequests") foiministryrequest.identityverified = requestschema.get("identityVerified") foiministryrequest.originalldd = requestschema.get("originalDueDate") + if requestschema.get("isoipcreview") is not None and requestschema.get("isoipcreview") != "": + foiministryrequest.isoipcreview = requestschema.get("isoipcreview") + foiministryrequest.oipcreviews = self.prepareoipc(requestschema, ministryid, activeversion, userid) + if requestschema.get("cfrDueDate") is not None and requestschema.get("cfrDueDate") != "": foiministryrequest.cfrduedate = requestschema.get("cfrDueDate") startdate = "" @@ -126,6 +132,33 @@ def createpersonalattribute(self, name, value,attributetypes, userid): personalattribute.attributevalue = value return personalattribute + def prepareoipc(self, requestschema, ministryrequestid, version, userid): + oipcarr = [] + if 'oipcdetails' in requestschema: + for oipc in requestschema['oipcdetails']: + oipcreview = FOIRequestOIPC() + oipcreview.foiministryrequest_id = ministryrequestid + oipcreview.foiministryrequestversion_id=version + oipcreview.oipcno = oipc["oipcno"] + oipcreview.reviewtypeid = oipc["reviewtypeid"] + oipcreview.reasonid = oipc["reasonid"] + oipcreview.statusid = oipc["statusid"] + oipcreview.outcomeid = oipc["outcomeid"] + oipcreview.investigator = oipc["investigator"] if oipc["investigator"] not in (None, "") else None + oipcreview.isinquiry = oipc["isinquiry"] + oipcreview.isjudicialreview = oipc["isjudicialreview"] + oipcreview.issubsequentappeal = oipc["issubsequentappeal"] + oipcreview.issubsequentappeal = oipc["issubsequentappeal"] + oipcreview.receiveddate = oipc["receiveddate"] if oipc["receiveddate"] not in (None, "") else None + oipcreview.closeddate = oipc["closeddate"] if oipc["closeddate"] not in (None, "") else None + oipcreview.isactive = True + if oipc["isinquiry"] == True: + oipcreview.inquiryattributes = oipc["inquiryattributes"] + oipcreview.createdby=userid + oipcreview.created_at= datetime2.now().isoformat() + oipcarr.append(oipcreview) + return oipcarr + def isNotBlankorNone(self, dataschema, key, location): if location == "main": diff --git a/request-management-api/request_api/services/foirequest/requestservicegetter.py b/request-management-api/request_api/services/foirequest/requestservicegetter.py index e5ae2c249..78181e224 100644 --- a/request-management-api/request_api/services/foirequest/requestservicegetter.py +++ b/request-management-api/request_api/services/foirequest/requestservicegetter.py @@ -8,6 +8,8 @@ from request_api.models.FOIRequestApplicantMappings import FOIRequestApplicantMapping from request_api.models.FOIMinistryRequestSubjectCodes import FOIMinistryRequestSubjectCode from request_api.models.FOIRestrictedMinistryRequests import FOIRestrictedMinistryRequest +from request_api.models.FOIRequestOIPC import FOIRequestOIPC +from request_api.services.oipcservice import oipcservice from dateutil.parser import parse from request_api.services.cfrfeeservice import cfrfeeservice from request_api.services.paymentservice import paymentservice @@ -165,6 +167,8 @@ def __preparebaseinfo(self,request,foiministryrequestid,requestministry,requestm 'assignedministryperson':requestministry["assignedministryperson"], 'selectedMinistries':[{'code':requestministry['programarea.bcgovcode'],'id':requestministry['foiministryrequestid'],'name':requestministry['programarea.name'],'selected':'true'}], 'divisions': self.getdivisions(requestministrydivisions), + 'isoipcreview': requestministry['isoipcreview'], + 'oipcdetails': self.getoipcdetails(foiministryrequestid, requestministry['version']), 'onholdTransitionDate': self.getonholdtransition(foiministryrequestid), 'stateTransition': FOIMinistryRequest.getstatesummary(foiministryrequestid), 'assignedToFirstName': requestministry["assignee.firstname"] if requestministry["assignedto"] != None else None, @@ -208,6 +212,43 @@ def getdivisions(self, ministrydivisions): divisions.append(division) return divisions + def getoipcdetails(self, ministryrequestid, ministryrequestversion): + oipcdetails = [] + _oipclist = FOIRequestOIPC.getoipc(ministryrequestid, ministryrequestversion) + inquiryoutcomes = oipcservice().getinquiryoutcomes() + if _oipclist is not None: + for entry in _oipclist: + oipc = { + "oipcid": entry["oipcid"], + "oipcno": entry["oipcno"], + "reviewtypeid": entry["reviewtypeid"], + "reviewetype": entry["reviewtype.name"], + "reasonid": entry["reasonid"], + "reason": entry["reason.name"], + "statusid": entry["statusid"], + "status":entry["status.name"], + "outcomeid": entry["outcomeid"], + "outcome": entry["outcome.name"] if entry["outcomeid"] not in (None, '') else None, + "investigator": entry["investigator"], + "isinquiry": entry["isinquiry"], + "isjudicialreview": entry["isjudicialreview"], + "issubsequentappeal": entry["issubsequentappeal"], + "inquiryattributes": self.formatinquiryattribute(entry["inquiryattributes"], inquiryoutcomes), + "createdby": entry["createdby"], + "receiveddate" : parse(entry["receiveddate"]).strftime('%b, %d %Y') if entry["receiveddate"] is not None else '', + "closeddate": parse(entry["closeddate"]).strftime('%b, %d %Y') if entry["closeddate"] is not None else '', + "created_at": parse(entry["created_at"]).strftime(self.__genericdateformat()) + } + oipcdetails.append(oipc) + return oipcdetails + + def formatinquiryattribute(self, inquiryattribute, inquiryoutcomes): + if inquiryattribute not in (None, {}): + for outcome in inquiryoutcomes: + if inquiryattribute["inquiryoutcome"] == outcome["inquiryoutcomeid"]: + inquiryattribute["inquiryoutcomename"] = outcome["name"] + return inquiryattribute + def getonholdtransition(self, foiministryrequestid): onholddate = None diff --git a/request-management-api/request_api/services/oipcservice.py b/request-management-api/request_api/services/oipcservice.py new file mode 100644 index 000000000..636044430 --- /dev/null +++ b/request-management-api/request_api/services/oipcservice.py @@ -0,0 +1,27 @@ +from request_api.models.OIPCReviewTypes import OIPCReviewTypes +from request_api.models.OIPCStatuses import OIPCStatuses +from request_api.models.OIPCReasons import OIPCReasons +from request_api.models.OIPCOutcomes import OIPCOutcomes +from request_api.models.OIPCInquiryOutcomes import OIPCInquiryOutcomes +from request_api.models.OIPCReviewTypesReasons import OIPCReviewTypesReasons + +class oipcservice: + """ OIPC service + """ + def getreviewtypes(self): + return OIPCReviewTypes.getreviewtypes() + + def getreviewtypeswithreasons(self): + return OIPCReviewTypesReasons.getreviewtypeswithreasons() + + def getreasons(self): + return OIPCReasons.getreasons() + + def getstatuses(self): + return OIPCStatuses.getstatuses() + + def getoutcomes(self): + return OIPCOutcomes.getoutcomes() + + def getinquiryoutcomes(self): + return OIPCInquiryOutcomes.getinquiryoutcomes() \ No newline at end of file