diff --git a/.github/workflows/forms-flow-ai-web-cd.yml b/.github/workflows/forms-flow-ai-web-cd.yml index d51098f5f..a3b2e39b1 100644 --- a/.github/workflows/forms-flow-ai-web-cd.yml +++ b/.github/workflows/forms-flow-ai-web-cd.yml @@ -6,6 +6,8 @@ on: - dev - dev-marshal - test-marshal + - dev-rook + - test-rook - main paths: - "apps/forms-flow-ai/forms-flow-ai-web/**" @@ -47,6 +49,18 @@ jobs: echo "TAG_NAME="test"" >> $GITHUB_ENV echo "BRANCH_NAME="test-marshal"" >> $GITHUB_ENV echo "ENV_NAME="test"" >> $GITHUB_ENV + elif [ ${{ github.ref_name }} == 'dev-rook' ]; then + echo "For ${{ github.ref_name }} branch" + echo "TOOLS_NAME=${{secrets.OPENSHIFT4_AI_FRONTEND_REPOSITORY}}" >> $GITHUB_ENV + echo "TAG_NAME="dev"" >> $GITHUB_ENV + echo "BRANCH_NAME="dev-rook"" >> $GITHUB_ENV + echo "ENV_NAME="dev"" >> $GITHUB_ENV + elif [ ${{ github.ref_name }} == 'test-rook' ]; then + echo "For ${{ github.ref_name }} branch" + echo "TOOLS_NAME=${{secrets.OPENSHIFT4_AI_FRONTEND_REPOSITORY}}" >> $GITHUB_ENV + echo "TAG_NAME="test"" >> $GITHUB_ENV + echo "BRANCH_NAME="test-rook"" >> $GITHUB_ENV + echo "ENV_NAME="test"" >> $GITHUB_ENV elif [ ${{ github.ref_name }} == 'main' ]; then echo "For ${{ github.ref_name }} branch" echo "TOOLS_NAME=${{secrets.OPENSHIFT4_AI_FRONTEND_REPOSITORY}}" >> $GITHUB_ENV diff --git a/.github/workflows/forms-flow-ai-web-ci.yml b/.github/workflows/forms-flow-ai-web-ci.yml index dda8f9730..11eb07dfd 100644 --- a/.github/workflows/forms-flow-ai-web-ci.yml +++ b/.github/workflows/forms-flow-ai-web-ci.yml @@ -8,6 +8,8 @@ on: - main - dev-marshal - test-marshal + - dev-rook + - test-rook paths: - "apps/forms-flow-ai/forms-flow-ai-web/**" - "!apps/forms-flow-ai/forms-flow-ai-web/src/forms/**" diff --git a/.github/workflows/forms-flow-bpm-cd.yml b/.github/workflows/forms-flow-bpm-cd.yml index 32c68dea0..4aed6db75 100644 --- a/.github/workflows/forms-flow-bpm-cd.yml +++ b/.github/workflows/forms-flow-bpm-cd.yml @@ -7,6 +7,8 @@ on: - dev - dev-marshal - test-marshal + - dev-rook + - test-rook - main paths: - "apps/forms-flow-ai/forms-flow-bpm/**" @@ -27,37 +29,65 @@ jobs: if: github.event_name == 'push' && github.repository == 'bcgov/foi-flow' steps: - uses: actions/checkout@v2 - - name: Set ENV variables - id: set-variable + - name: Set ENV variables for dev branch + if: ${{ github.ref_name == 'dev' }} + shell: bash run: | - if [ ${{ github.ref_name }} == 'dev' ]; then echo "For ${{ github.ref_name }} branch" echo "TOOLS_NAME=${{secrets.OPENSHIFT4_FRONTEND_REPOSITORY}}" >> $GITHUB_ENV echo "TAG_NAME="dev"" >> $GITHUB_ENV echo "BRANCH_NAME="dev"" >> $GITHUB_ENV echo "ENV_NAME="dev"" >> $GITHUB_ENV - elif [ ${{ github.ref_name }} == 'dev-marshal' ]; then + + - name: Set ENV variables for dev-marshal branch + if: ${{ github.ref_name == 'dev-marshal' }} + shell: bash + run: | + echo "For ${{ github.ref_name }} branch" + echo "TOOLS_NAME=${{secrets.OPENSHIFT4_FRONTEND_REPOSITORY}}" >> $GITHUB_ENV + echo "TAG_NAME="dev-marshal"" >> $GITHUB_ENV + echo "BRANCH_NAME="dev-marshal"" >> $GITHUB_ENV + echo "ENV_NAME="dev"" >> $GITHUB_ENV + + - name: Set ENV variables for test-marshal branch + if: ${{ github.ref_name == 'test-marshal' }} + shell: bash + run: | + echo "For ${{ github.ref_name }} branch" + echo "TOOLS_NAME=${{secrets.OPENSHIFT4_FRONTEND_REPOSITORY}}" >> $GITHUB_ENV + echo "TAG_NAME="test-marshal"" >> $GITHUB_ENV + echo "BRANCH_NAME="test-marshal"" >> $GITHUB_ENV + echo "ENV_NAME="test"" >> $GITHUB_ENV + + - name: Set ENV variables for dev-rook branch + if: ${{ github.ref_name == 'dev-rook' }} + shell: bash + run: | echo "For ${{ github.ref_name }} branch" echo "TOOLS_NAME=${{secrets.OPENSHIFT4_FRONTEND_REPOSITORY}}" >> $GITHUB_ENV - echo "TAG_NAME="dev-marshal"" >> $GITHUB_ENV - echo "BRANCH_NAME="dev-marshal"" >> $GITHUB_ENV + echo "TAG_NAME="dev-rook"" >> $GITHUB_ENV + echo "BRANCH_NAME="dev-rook"" >> $GITHUB_ENV echo "ENV_NAME="dev"" >> $GITHUB_ENV - elif [ ${{ github.ref_name }} == 'test-marshal' ]; then + + - name: Set ENV variables for test-rook branch + if: ${{ github.ref_name == 'test-rook' }} + shell: bash + run: | echo "For ${{ github.ref_name }} branch" echo "TOOLS_NAME=${{secrets.OPENSHIFT4_FRONTEND_REPOSITORY}}" >> $GITHUB_ENV - echo "TAG_NAME="test-marshal"" >> $GITHUB_ENV - echo "BRANCH_NAME="test-marshal"" >> $GITHUB_ENV + echo "TAG_NAME="test-rook"" >> $GITHUB_ENV + echo "BRANCH_NAME="test-rook"" >> $GITHUB_ENV echo "ENV_NAME="test"" >> $GITHUB_ENV - elif [ ${{ github.ref_name }} == 'main' ]; then + + - name: Set ENV variables for main branch + if: ${{ github.ref_name == 'main' }} + shell: bash + run: | echo "For ${{ github.ref_name }} branch" echo "TOOLS_NAME=${{secrets.OPENSHIFT4_FRONTEND_REPOSITORY}}" >> $GITHUB_ENV echo "TAG_NAME="test"" >> $GITHUB_ENV echo "BRANCH_NAME="main"" >> $GITHUB_ENV echo "ENV_NAME="test"" >> $GITHUB_ENV - else - echo "For ${{ github.ref_name }} branch" - fi - shell: bash - name: Login Openshift shell: bash diff --git a/.github/workflows/forms-flow-web-cd.yml b/.github/workflows/forms-flow-web-cd.yml index f869dbf07..733624d41 100644 --- a/.github/workflows/forms-flow-web-cd.yml +++ b/.github/workflows/forms-flow-web-cd.yml @@ -7,6 +7,8 @@ on: - dev - dev-marshal - test-marshal + - dev-rook + - test-rook - main paths: - "forms-flow-web/**" @@ -47,6 +49,18 @@ jobs: echo "TAG_NAME="test-marshal"" >> $GITHUB_ENV echo "BRANCH_NAME="test-marshal"" >> $GITHUB_ENV echo "ENV_NAME="test"" >> $GITHUB_ENV + elif [ ${{ github.ref_name }} == 'dev-rook' ]; then + echo "For ${{ github.ref_name }} branch" + echo "TOOLS_NAME=${{secrets.OPENSHIFT4_FRONTEND_REPOSITORY}}" >> $GITHUB_ENV + echo "TAG_NAME="dev-rook"" >> $GITHUB_ENV + echo "BRANCH_NAME="dev-rook"" >> $GITHUB_ENV + echo "ENV_NAME="dev"" >> $GITHUB_ENV + elif [ ${{ github.ref_name }} == 'test-rook' ]; then + echo "For ${{ github.ref_name }} branch" + echo "TOOLS_NAME=${{secrets.OPENSHIFT4_FRONTEND_REPOSITORY}}" >> $GITHUB_ENV + echo "TAG_NAME="test-rook"" >> $GITHUB_ENV + echo "BRANCH_NAME="test-rook"" >> $GITHUB_ENV + echo "ENV_NAME="test"" >> $GITHUB_ENV elif [ ${{ github.ref_name }} == 'main' ]; then echo "For ${{ github.ref_name }} branch" echo "TOOLS_NAME=${{secrets.OPENSHIFT4_FRONTEND_REPOSITORY}}" >> $GITHUB_ENV diff --git a/.github/workflows/forms-flow-web-ci.yml b/.github/workflows/forms-flow-web-ci.yml index 1613bd2be..1ef259ca2 100644 --- a/.github/workflows/forms-flow-web-ci.yml +++ b/.github/workflows/forms-flow-web-ci.yml @@ -8,6 +8,8 @@ on: - dev - dev-marshal - test-marshal + - dev-rook + - test-rook paths: - "forms-flow-web/**" diff --git a/.github/workflows/notification-manager-cd.yml b/.github/workflows/notification-manager-cd.yml index 1a6db4426..c4daf23de 100644 --- a/.github/workflows/notification-manager-cd.yml +++ b/.github/workflows/notification-manager-cd.yml @@ -7,6 +7,8 @@ on: - dev - dev-marshal - test-marshal + - dev-rook + - test-rook - main paths: - "notification-manager/**" @@ -47,6 +49,18 @@ jobs: echo "TAG_NAME="test-marshal"" >> $GITHUB_ENV echo "BRANCH_NAME="test-marshal"" >> $GITHUB_ENV echo "ENV_NAME="test"" >> $GITHUB_ENV + elif [ ${{ github.ref_name }} == 'dev-rook' ]; then + echo "For ${{ github.ref_name }} branch" + echo "TOOLS_NAME=${{secrets.OPENSHIFT4_FRONTEND_REPOSITORY}}" >> $GITHUB_ENV + echo "TAG_NAME="dev-rook"" >> $GITHUB_ENV + echo "BRANCH_NAME="dev-rook"" >> $GITHUB_ENV + echo "ENV_NAME="dev"" >> $GITHUB_ENV + elif [ ${{ github.ref_name }} == 'test-rook' ]; then + echo "For ${{ github.ref_name }} branch" + echo "TOOLS_NAME=${{secrets.OPENSHIFT4_FRONTEND_REPOSITORY}}" >> $GITHUB_ENV + echo "TAG_NAME="test-rook"" >> $GITHUB_ENV + echo "BRANCH_NAME="test-rook"" >> $GITHUB_ENV + echo "ENV_NAME="test"" >> $GITHUB_ENV elif [ ${{ github.ref_name }} == 'main' ]; then echo "For ${{ github.ref_name }} branch" echo "TOOLS_NAME=${{secrets.OPENSHIFT4_FRONTEND_REPOSITORY}}" >> $GITHUB_ENV diff --git a/.github/workflows/notification-manager-ci.yml b/.github/workflows/notification-manager-ci.yml index 0b66bf390..1b3185813 100644 --- a/.github/workflows/notification-manager-ci.yml +++ b/.github/workflows/notification-manager-ci.yml @@ -8,6 +8,8 @@ on: - dev - dev-marshal - test-marshal + - dev-rook + - test-rook paths: - "notification-manager/**" diff --git a/.github/workflows/request-management-api-cd.yml b/.github/workflows/request-management-api-cd.yml index 0971ad928..908ec9a34 100644 --- a/.github/workflows/request-management-api-cd.yml +++ b/.github/workflows/request-management-api-cd.yml @@ -7,6 +7,8 @@ on: - dev - dev-marshal - test-marshal + - dev-rook + - test-rook - main paths: - "request-management-api/**" @@ -49,6 +51,18 @@ jobs: echo "TAG_NAME="test-marshal"" >> $GITHUB_ENV echo "BRANCH_NAME="test-marshal"" >> $GITHUB_ENV echo "ENV_NAME="test"" >> $GITHUB_ENV + elif [ ${{ github.ref_name }} == 'dev-rook' ]; then + echo "For ${{ github.ref_name }} branch" + echo "TOOLS_NAME=${{secrets.OPENSHIFT4_FRONTEND_REPOSITORY}}" >> $GITHUB_ENV + echo "TAG_NAME="dev-rook"" >> $GITHUB_ENV + echo "BRANCH_NAME="dev-rook"" >> $GITHUB_ENV + echo "ENV_NAME="dev"" >> $GITHUB_ENV + elif [ ${{ github.ref_name }} == 'test-rook' ]; then + echo "For ${{ github.ref_name }} branch" + echo "TOOLS_NAME=${{secrets.OPENSHIFT4_FRONTEND_REPOSITORY}}" >> $GITHUB_ENV + echo "TAG_NAME="test-rook"" >> $GITHUB_ENV + echo "BRANCH_NAME="test-rook"" >> $GITHUB_ENV + echo "ENV_NAME="test"" >> $GITHUB_ENV elif [ ${{ github.ref_name }} == 'main' ]; then echo "For ${{ github.ref_name }} branch" echo "TOOLS_NAME=${{secrets.OPENSHIFT4_FRONTEND_REPOSITORY}}" >> $GITHUB_ENV diff --git a/.github/workflows/request-management-api-ci.yml b/.github/workflows/request-management-api-ci.yml index 0055a4b1c..fd917c956 100644 --- a/.github/workflows/request-management-api-ci.yml +++ b/.github/workflows/request-management-api-ci.yml @@ -7,7 +7,9 @@ on: - main - dev - dev-marshal - - test-marshal + - test-marshal + - dev-rook + - test-rook paths: - "request-management-api/**" - "!request-management-api/request_api/email_templates/**" diff --git a/forms-flow-web/src/actions/FOI/foiActionConstants.js b/forms-flow-web/src/actions/FOI/foiActionConstants.js index beaca7433..f9acfee86 100644 --- a/forms-flow-web/src/actions/FOI/foiActionConstants.js +++ b/forms-flow-web/src/actions/FOI/foiActionConstants.js @@ -89,6 +89,15 @@ const FOI_ACTION_CONSTANTS = { "FOI_PDF_STITCHED_STATUS_FOR_RESPONSEPACKAGE", FOI_PDF_STITCHED_RECORD_FOR_RESPONSEPACKAGE: "FOI_PDF_STITCHED_RECORD_FOR_RESPONSEPACKAGE", + FOI_PDF_STITCHED_STATUS_FOR_OIPCREDLINEREVIEW: "FOI_PDF_STITCHED_STATUS_FOR_OIPCREDLINEREVIEW", + FOI_PDF_STITCHED_RECORD_FOR_OIPCREDLINEREVIEW: "FOI_PDF_STITCHED_RECORD_FOR_OIPCREDLINEREVIEW", + FOI_PDF_STITCHED_STATUS_FOR_OIPCREDLINE: "FOI_PDF_STITCHED_STATUS_FOR_OIPCREDLINE", + FOI_PDF_STITCHED_RECORD_FOR_OIPCREDLINE: "FOI_PDF_STITCHED_RECORD_FOR_OIPCREDLINE", + + 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..55d3aca7b 100644 --- a/forms-flow-web/src/actions/FOI/foiRequestActions.js +++ b/forms-flow-web/src/actions/FOI/foiRequestActions.js @@ -147,6 +147,18 @@ export const setFOIPDFStitchedRecordForResponsePackage = payload: data, }); }; +export const setFOIPDFStitchedRecordForOipcRedlineReview = (data) => (dispatch) => { + dispatch({ + type: FOI_ACTION_CONSTANTS.FOI_PDF_STITCHED_RECORD_FOR_OIPCREDLINEREVIEW, + payload: data, + }); +} +export const setFOIPDFStitchedRecordForOipcRedline = (data) => (dispatch) => { + dispatch({ + type: FOI_ACTION_CONSTANTS.FOI_PDF_STITCHED_RECORD_FOR_OIPCREDLINE, + payload: data, + }); +} export const setFOIPDFStitchStatusForHarms = (data) => (dispatch) => { dispatch({ type: FOI_ACTION_CONSTANTS.FOI_PDF_STITCHED_STATUS_FOR_HARMS, @@ -166,6 +178,18 @@ export const setFOIPDFStitchStatusForResponsePackage = (data) => (dispatch) => { payload: data, }); }; +export const setFOIPDFStitchStatusForOipcRedlineReview = (data) => (dispatch) => { + dispatch({ + type: FOI_ACTION_CONSTANTS.FOI_PDF_STITCHED_STATUS_FOR_OIPCREDLINEREVIEW, + payload: data, + }); +}; +export const setFOIPDFStitchStatusForOipcRedline = (data) => (dispatch) => { + dispatch({ + type: FOI_ACTION_CONSTANTS.FOI_PDF_STITCHED_STATUS_FOR_OIPCREDLINE, + payload: data, + }); +}; export const serviceActionError = (_data) => (dispatch) => { //TODO update to a common file @@ -425,3 +449,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..4a03e6cc0 100644 --- a/forms-flow-web/src/apiManager/endpoints/index.js +++ b/forms-flow-web/src/apiManager/endpoints/index.js @@ -25,6 +25,7 @@ const API = { FOI_GET_RECEIVED_MODELIST: `${FOI_BASE_API_URL}/api/foiflow/receivedmodes`, FOI_POST_REQUEST_POST: `${FOI_BASE_API_URL}/api/foirequests`, FOI_REQUEST_API: `${FOI_BASE_API_URL}/api/foirequests//ministryrequest/`, + FOI_REQUEST_SECTION_API: `${FOI_BASE_API_URL}/api/foirequests//ministryrequest//section`, FOI_MINISTRYVIEW_REQUEST_API: `${FOI_BASE_API_URL}/api/foirequests//ministryrequest//ministry`, FOI_RAW_REQUEST_DESCRIPTION: `${FOI_BASE_API_URL}/api/foiaudit/rawrequest//description`, FOI_MINISTRY_REQUEST_DESCRIPTION: `${FOI_BASE_API_URL}/api/foiaudit/ministryrequest//description`, @@ -133,5 +134,15 @@ 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_DOWNLOAD_RECORDS_FOR_OIPCREDLINEREVIEW: `${FOI_BASE_API_URL}/api/foirecord//ministryrequest//download/oipcreviewredline`, + FOI_PDF_STITCH_STATUS_FOR_OIPCREDLINEREVIEW: `${FOI_BASE_API_URL}/api/foirecord//ministryrequest//oipcreviewredline/pdfstitchjobstatus`, + FOI_DOWNLOAD_RECORDS_FOR_OIPCREDLINE: `${FOI_BASE_API_URL}/api/foirecord//ministryrequest//download/oipcredline`, + FOI_PDF_STITCH_STATUS_FOR_OIPCREDLINE: `${FOI_BASE_API_URL}/api/foirecord//ministryrequest//oipcredline/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/foiAdvancedSearchServices.js b/forms-flow-web/src/apiManager/services/FOI/foiAdvancedSearchServices.js index cbb9fc669..e7fb181d5 100644 --- a/forms-flow-web/src/apiManager/services/FOI/foiAdvancedSearchServices.js +++ b/forms-flow-web/src/apiManager/services/FOI/foiAdvancedSearchServices.js @@ -13,6 +13,7 @@ export const fetchAdvancedSearchData = ({ requestState = [], requestStatus = [], requestType = [], + requestFlags = [], dateRangeType = null, fromDate = null, toDate = null, @@ -41,6 +42,7 @@ export const fetchAdvancedSearchData = ({ requestState: requestState, requestStatus: requestStatus, requestType: requestType, + requestFlags: requestFlags, dateRangeType: dateRangeType, fromDate: fromDate, toDate: toDate, 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/apiManager/services/FOI/foiRecordServices.js b/forms-flow-web/src/apiManager/services/FOI/foiRecordServices.js index b729be708..8c2d48a61 100644 --- a/forms-flow-web/src/apiManager/services/FOI/foiRecordServices.js +++ b/forms-flow-web/src/apiManager/services/FOI/foiRecordServices.js @@ -17,6 +17,10 @@ import { setFOIPDFStitchedRecordForRedlines, setFOIPDFStitchStatusForResponsePackage, setFOIPDFStitchedRecordForResponsePackage, + setFOIPDFStitchedRecordForOipcRedlineReview, + setFOIPDFStitchStatusForOipcRedlineReview, + setFOIPDFStitchStatusForOipcRedline, + setFOIPDFStitchedRecordForOipcRedline, } from "../../../actions/FOI/foiRequestActions"; import { fnDone } from "./foiServicesUtil"; import UserService from "../../../services/UserService"; @@ -479,3 +483,141 @@ export const fetchPDFStitchedRecordForResponsePackage = ( }); }; }; + +export const fetchPDFStitchedStatusForOIPCRedline = ( + requestId, + ministryId, + ...rest) => { + if (!ministryId) { + return () => {}; + } + const done = fnDone(rest); + let apiUrl = replaceUrl( + replaceUrl( + API.FOI_PDF_STITCH_STATUS_FOR_OIPCREDLINE, + "", + ministryId + ), + "", + requestId + ); + return (dispatch) => { + httpGETRequest(apiUrl, {}, UserService.getToken()) + .then((res) => { + if (res.data) { + dispatch(setFOIPDFStitchStatusForOipcRedline(res.data)); + done(null, res.data); + } + }) + .catch((error) => { + console.log("Error in fetching pdfstitch job status", error); + dispatch(serviceActionError(error)); + done(error); + }); + }; + } + +export const fetchPDFStitchedRecordForOIPCRedline = ( + requestId, + ministryId, + ...rest) => { + if (!ministryId) { + return () => {}; + } + const done = fnDone(rest); + let apiUrl = replaceUrl( + replaceUrl( + API.FOI_DOWNLOAD_RECORDS_FOR_OIPCREDLINE, + "", + ministryId + ), + "", + requestId + ); + return (dispatch) => { + httpGETRequest(apiUrl, {}, UserService.getToken()) + .then((res) => { + if (res.data) { + dispatch(setFOIPDFStitchedRecordForOipcRedline(res.data)); + done(null, res.data); + } else { + console.log("Error in fetching records for redlines", res); + dispatch(serviceActionError(res)); + } + }) + .catch((error) => { + console.log("Error in fetching records for redlines", error); + dispatch(serviceActionError(error)); + done(error); + }); + }; + } + +export const fetchPDFStitchedStatusForOIPCRedlineReview = ( + requestId, + ministryId, + ...rest) => { + if (!ministryId) { + return () => {}; + } + const done = fnDone(rest); + let apiUrl = replaceUrl( + replaceUrl( + API.FOI_PDF_STITCH_STATUS_FOR_OIPCREDLINEREVIEW, + "", + ministryId + ), + "", + requestId + ); + return (dispatch) => { + httpGETRequest(apiUrl, {}, UserService.getToken()) + .then((res) => { + if (res.data) { + dispatch(setFOIPDFStitchStatusForOipcRedlineReview(res.data)); + done(null, res.data); + } + }) + .catch((error) => { + console.log("Error in fetching pdfstitch job status", error); + dispatch(serviceActionError(error)); + done(error); + }); + }; + } + +export const fetchPDFStitchedRecordForOIPCRedlineReview = ( + requestId, + ministryId, + ...rest) => { + if (!ministryId) { + return () => {}; + } + const done = fnDone(rest); + let apiUrl = replaceUrl( + replaceUrl( + API.FOI_DOWNLOAD_RECORDS_FOR_OIPCREDLINEREVIEW, + "", + ministryId + ), + "", + requestId + ); + return (dispatch) => { + httpGETRequest(apiUrl, {}, UserService.getToken()) + .then((res) => { + if (res.data) { + dispatch(setFOIPDFStitchedRecordForOipcRedlineReview(res.data)); + done(null, res.data); + } else { + console.log("Error in fetching records for redlines", res); + dispatch(serviceActionError(res)); + } + }) + .catch((error) => { + console.log("Error in fetching records for redlines", error); + dispatch(serviceActionError(error)); + done(error); + }); + }; + } \ No newline at end of file diff --git a/forms-flow-web/src/apiManager/services/FOI/foiRequestServices.js b/forms-flow-web/src/apiManager/services/FOI/foiRequestServices.js index 30bd94e36..50475471c 100644 --- a/forms-flow-web/src/apiManager/services/FOI/foiRequestServices.js +++ b/forms-flow-web/src/apiManager/services/FOI/foiRequestServices.js @@ -527,3 +527,27 @@ export const fetchRestrictedRequestCommentTagList = (requestid, ministryId, ...r } }; +export const deleteOIPCDetails = (requestId, ministryId, ...rest) => { + const done = fnDone(rest); + let apiUrl= replaceUrl(replaceUrl( + API.FOI_REQUEST_SECTION_API, + "", + ministryId),"",requestId + ); + const data = {isoipcreview: false, oipcdetails: []} + return (dispatch) => { + httpPOSTRequest(`${apiUrl}/oipc`, data) + .then((res) => { + if (res.data) { + done(null, res.data); + } else { + dispatch(serviceActionError(res)); + throw new Error(`Error while saving the OIPC Details for the (request# ${requestId}, ministry# ${ministryId})`); + } + }) + .catch((error) => { + done(error); + catchError(error, dispatch); + }); + }; +} \ No newline at end of file diff --git a/forms-flow-web/src/components/FOI/Dashboard/IAO/AdvancedSearch/DataGridAdvancedSearch.js b/forms-flow-web/src/components/FOI/Dashboard/IAO/AdvancedSearch/DataGridAdvancedSearch.js index 8cd13422e..c5b84c935 100644 --- a/forms-flow-web/src/components/FOI/Dashboard/IAO/AdvancedSearch/DataGridAdvancedSearch.js +++ b/forms-flow-web/src/components/FOI/Dashboard/IAO/AdvancedSearch/DataGridAdvancedSearch.js @@ -12,7 +12,8 @@ import { getReceivedDate, // onBehalfFullName, getRecordsDue, - LightTooltip, + LightTooltip, + displayQueueFlagIcons, } from "../../utils"; import { ActionContext } from "./ActionContext"; import { @@ -97,6 +98,12 @@ const DataGridAdvancedSearch = ({ userDetail }) => { const ProcessingTeamColumns = [ + { + field: "flags", + headerName: "FLAGS", + headerAlign: "left", + renderCell: displayQueueFlagIcons, + }, { field: "axisRequestId", headerName: "ID NUMBER", @@ -189,6 +196,12 @@ const DataGridAdvancedSearch = ({ userDetail }) => { ]; const IntakeTeamColumns = [ + { + field: "flags", + headerName: "FLAGS", + headerAlign: "left", + renderCell: displayQueueFlagIcons, + }, { field: "applicantName", headerName: "APPLICANT NAME", @@ -250,6 +263,12 @@ const DataGridAdvancedSearch = ({ userDetail }) => { ]; const FlexTeamColumns = [ + { + field: "flags", + headerName: "FLAGS", + headerAlign: "left", + renderCell: displayQueueFlagIcons, + }, { field: "axisRequestId", headerName: "ID NUMBER", diff --git a/forms-flow-web/src/components/FOI/Dashboard/IAO/AdvancedSearch/SearchComponent.js b/forms-flow-web/src/components/FOI/Dashboard/IAO/AdvancedSearch/SearchComponent.js index 25c7e352b..78b62c522 100644 --- a/forms-flow-web/src/components/FOI/Dashboard/IAO/AdvancedSearch/SearchComponent.js +++ b/forms-flow-web/src/components/FOI/Dashboard/IAO/AdvancedSearch/SearchComponent.js @@ -100,12 +100,12 @@ const AdvancedSearch = ({ userDetail }) => { const tooltipContentRight = { "title": "Advanced Search", - "content": "To conduct an Advanced Search using one of the six filter buttons, you must also enter one or more key words." + "content": "To conduct an Advanced Search using one of the seven filter buttons, you must also enter one or more key words." }; const tooltipContentLeft = { "title": "Advanced Search", - "content": "Use one or more fields from the following sections on their own or to narrow your search: Request State/Status/Type, Date Range, or Public Body." + "content": "Use one or more fields from the following sections on their own or to narrow your search: Request State/Status/Type/Flags, Date Range, or Public Body." }; @@ -180,6 +180,23 @@ const AdvancedSearch = ({ userDetail }) => { } }); + const initialRequestFlags = { + restricted: false, + oipc: false, + phased: false + }; + const [requestFlags, setRequestFlags] = useState(() => { + if (Object.keys(advancedSearchParams).length > 0 && advancedSearchParams.requestFlags.length > 0) { + let savedRequestFlags = {...initialRequestFlags} + advancedSearchParams.requestFlags.forEach(type => { + savedRequestFlags[type] = true; + }); + return savedRequestFlags; + } else { + return initialRequestFlags; + } + }); + const [selectedDateRangeType, setSelectedDateRangeType] = useState(advancedSearchParams?.dateRangeType || ""); const [fromDate, setFromDate] = useState(advancedSearchParams?.fromDate || ""); const [toDate, setToDate] = useState(advancedSearchParams?.toDate || ""); @@ -232,6 +249,7 @@ const AdvancedSearch = ({ userDetail }) => { keywords: keywordsMode ? keywords : [searchText.trim()], requestState: getTrueKeysFromCheckboxObject(requestState), requestType: getTrueKeysFromCheckboxObject(requestTypes), + requestFlags: getTrueKeysFromCheckboxObject(requestFlags), requestStatus: getTrueKeysFromCheckboxObject(requestStatus), dateRangeType: selectedDateRangeType || null, fromDate: fromDate || null, @@ -257,6 +275,7 @@ const AdvancedSearch = ({ userDetail }) => { const noSearchCriteria = () => { let selectedRequestStates = getTrueKeysFromCheckboxObject(requestState); let selectedRequestTypes = getTrueKeysFromCheckboxObject(requestTypes); + let selectedRequestFlags = getTrueKeysFromCheckboxObject(requestFlags); let selectedRequestStatus = getTrueKeysFromCheckboxObject(requestStatus); return ((keywords.length===0 && keywordsMode) || (!searchText && !keywordsMode)) && !fromDate @@ -264,6 +283,7 @@ const AdvancedSearch = ({ userDetail }) => { && selectedPublicBodies.length===0 && selectedRequestStates.length===0 && selectedRequestTypes.length===0 + && selectedRequestFlags.length===0 && selectedRequestStatus.length===0; }; @@ -274,6 +294,7 @@ const AdvancedSearch = ({ userDetail }) => { setSearchFilterSelected(); setRequestState(intitialRequestState); setRequestTypes(initialRequestTypes); + setRequestFlags(initialRequestFlags); setRequestStatus(intitialRequestStatus); setFromDate(""); setToDate(""); @@ -322,6 +343,13 @@ const AdvancedSearch = ({ userDetail }) => { }); }; + const handleRequestFlagsChange = (event) => { + setRequestFlags({ + ...requestFlags, + [event.target.name]: event.target.checked, + }); + }; + const handleSelectedDateRangeTypeChange = (event) => { const type = event.target.value; setSelectedDateRangeType(type); @@ -490,73 +518,84 @@ const AdvancedSearch = ({ userDetail }) => { Filter by + + + + clickSearchFilter(SearchFilter.REQUEST_DESCRIPTION) + } + clicked={ + searchFilterSelected === SearchFilter.REQUEST_DESCRIPTION + } + /> + - - - clickSearchFilter(SearchFilter.REQUEST_DESCRIPTION) - } - clicked={ - searchFilterSelected === SearchFilter.REQUEST_DESCRIPTION - } - /> - + + clickSearchFilter(SearchFilter.ID_NUM)} + clicked={searchFilterSelected === SearchFilter.ID_NUM} + /> + - - clickSearchFilter(SearchFilter.ID_NUM)} - clicked={searchFilterSelected === SearchFilter.ID_NUM} - /> - + + + clickSearchFilter(SearchFilter.AXIS_REQUEST_NUM) + } + clicked={ + searchFilterSelected === SearchFilter.AXIS_REQUEST_NUM + } + /> + - - - clickSearchFilter(SearchFilter.AXIS_REQUEST_NUM) - } - clicked={ - searchFilterSelected === SearchFilter.AXIS_REQUEST_NUM - } - /> - + + clickSearchFilter(SearchFilter.APPLICANT_NAME)} + clicked={searchFilterSelected === SearchFilter.APPLICANT_NAME} + /> + - - clickSearchFilter(SearchFilter.APPLICANT_NAME)} - clicked={searchFilterSelected === SearchFilter.APPLICANT_NAME} - /> - + + clickSearchFilter(SearchFilter.ASSIGNEE_NAME)} + clicked={searchFilterSelected === SearchFilter.ASSIGNEE_NAME} + /> + - - clickSearchFilter(SearchFilter.ASSIGNEE_NAME)} - clicked={searchFilterSelected === SearchFilter.ASSIGNEE_NAME} - /> - + + clickSearchFilter(SearchFilter.SUBJECT_CODE)} + clicked={searchFilterSelected === SearchFilter.SUBJECT_CODE} + /> + - - clickSearchFilter(SearchFilter.SUBJECT_CODE)} - clicked={searchFilterSelected === SearchFilter.SUBJECT_CODE} - /> + + clickSearchFilter(SearchFilter.OIPC_NUMBER)} + clicked={searchFilterSelected === SearchFilter.OIPC_NUMBER} + /> + @@ -730,6 +769,61 @@ const AdvancedSearch = ({ userDetail }) => { /> + + + + Request Flags + + + + + + + } + label="Restricted" + /> + + } + label="OIPC" + /> + + } + label="Phased" + /> + + diff --git a/forms-flow-web/src/components/FOI/Dashboard/IAO/AdvancedSearch/enum.js b/forms-flow-web/src/components/FOI/Dashboard/IAO/AdvancedSearch/enum.js index ad08f9752..ee6686a63 100644 --- a/forms-flow-web/src/components/FOI/Dashboard/IAO/AdvancedSearch/enum.js +++ b/forms-flow-web/src/components/FOI/Dashboard/IAO/AdvancedSearch/enum.js @@ -6,6 +6,7 @@ const SearchFilter = Object.freeze({ APPLICANT_NAME: "applicantname", ASSIGNEE_NAME: "assigneename", SUBJECT_CODE: "subjectcode", + OIPC_NUMBER: "oipc_number" }); const DateRangeTypes = Object.freeze([ 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/AdvancedSearch/SearchComponent.js b/forms-flow-web/src/components/FOI/Dashboard/Ministry/AdvancedSearch/SearchComponent.js index 83325ebde..2b97414b9 100644 --- a/forms-flow-web/src/components/FOI/Dashboard/Ministry/AdvancedSearch/SearchComponent.js +++ b/forms-flow-web/src/components/FOI/Dashboard/Ministry/AdvancedSearch/SearchComponent.js @@ -100,12 +100,12 @@ const AdvancedSearch = ({ userDetail }) => { const tooltipContentRight = { "title": "Advanced Search", - "content": "To conduct an Advanced Search using one of the six filter buttons, you must also enter one or more key words." + "content": "To conduct an Advanced Search using one of the seven filter buttons, you must also enter one or more key words." }; const tooltipContentLeft = { "title": "Advanced Search", - "content": "Use one or more fields from the following sections on their own or to narrow your search: Request State/Status/Type, Date Range, or Public Body." + "content": "Use one or more fields from the following sections on their own or to narrow your search: Request State/Status/Type/Flags, Date Range, or Public Body." }; const [searchFilterSelected, setSearchFilterSelected] = useState(advancedSearchParams?.search || null); @@ -182,6 +182,23 @@ const AdvancedSearch = ({ userDetail }) => { return initialRequestTypes; } }); + + const initialRequestFlags = { + restricted: false, + oipc: false, + phased: false + }; + const [requestFlags, setRequestFlags] = useState(() => { + if (Object.keys(advancedSearchParams).length > 0 && advancedSearchParams.requestFlags.length > 0) { + let savedRequestFlags = {...initialRequestFlags} + advancedSearchParams.requestFlags.forEach(type => { + savedRequestFlags[type] = true; + }); + return savedRequestFlags; + } else { + return initialRequestFlags; + } + }); const [selectedDateRangeType, setSelectedDateRangeType] = useState(advancedSearchParams?.dateRangeType || ""); const [fromDate, setFromDate] = useState(advancedSearchParams?.fromDate || ""); const [toDate, setToDate] = useState(advancedSearchParams?.toDate || ""); @@ -233,6 +250,7 @@ const AdvancedSearch = ({ userDetail }) => { keywords: keywordsMode ? keywords : [searchText.trim()], requestState: getTrueKeysFromCheckboxObject(requestState), requestType: getTrueKeysFromCheckboxObject(requestTypes), + requestFlags: getTrueKeysFromCheckboxObject(requestFlags), requestStatus: getTrueKeysFromCheckboxObject(requestStatus), dateRangeType: selectedDateRangeType || null, fromDate: fromDate || null, @@ -258,6 +276,7 @@ const AdvancedSearch = ({ userDetail }) => { const noSearchCriteria = () => { let selectedRequestStates = getTrueKeysFromCheckboxObject(requestState); let selectedRequestTypes = getTrueKeysFromCheckboxObject(requestTypes); + let selectedRequestFlags = getTrueKeysFromCheckboxObject(requestFlags); let selectedRequestStatus = getTrueKeysFromCheckboxObject(requestStatus); return ((keywords.length===0 && keywordsMode) || (!searchText && !keywordsMode)) && !fromDate @@ -265,6 +284,7 @@ const AdvancedSearch = ({ userDetail }) => { && selectedPublicBodies.length===0 && selectedRequestStates.length===0 && selectedRequestTypes.length===0 + && selectedRequestFlags.length===0 && selectedRequestStatus.length===0; }; @@ -275,6 +295,7 @@ const AdvancedSearch = ({ userDetail }) => { setSearchFilterSelected(); setRequestState(intitialRequestState); setRequestTypes(initialRequestTypes); + setRequestFlags(initialRequestFlags); setRequestStatus(intitialRequestStatus); setFromDate(""); setToDate(""); @@ -335,6 +356,13 @@ const AdvancedSearch = ({ userDetail }) => { }); }; + const handleRequestFlagsChange = (event) => { + setRequestFlags({ + ...requestFlags, + [event.target.name]: event.target.checked, + }); + }; + const handleSelectedDateRangeTypeChange = (event) => { const type = event.target.value; setSelectedDateRangeType(type); @@ -503,73 +531,84 @@ const AdvancedSearch = ({ userDetail }) => { Filter by + + + + clickSearchFilter(SearchFilter.REQUEST_DESCRIPTION) + } + clicked={ + searchFilterSelected === SearchFilter.REQUEST_DESCRIPTION + } + /> + - - - clickSearchFilter(SearchFilter.REQUEST_DESCRIPTION) - } - clicked={ - searchFilterSelected === SearchFilter.REQUEST_DESCRIPTION - } - /> - - - - clickSearchFilter(SearchFilter.ID_NUM)} - clicked={searchFilterSelected === SearchFilter.ID_NUM} - /> - + + clickSearchFilter(SearchFilter.ID_NUM)} + clicked={searchFilterSelected === SearchFilter.ID_NUM} + /> + - - - clickSearchFilter(SearchFilter.AXIS_REQUEST_NUM) - } - clicked={ - searchFilterSelected === SearchFilter.AXIS_REQUEST_NUM - } - /> - + + + clickSearchFilter(SearchFilter.AXIS_REQUEST_NUM) + } + clicked={ + searchFilterSelected === SearchFilter.AXIS_REQUEST_NUM + } + /> + - - clickSearchFilter(SearchFilter.APPLICANT_NAME)} - clicked={searchFilterSelected === SearchFilter.APPLICANT_NAME} - /> - + + clickSearchFilter(SearchFilter.APPLICANT_NAME)} + clicked={searchFilterSelected === SearchFilter.APPLICANT_NAME} + /> + - - clickSearchFilter(SearchFilter.ASSIGNEE_NAME)} - clicked={searchFilterSelected === SearchFilter.ASSIGNEE_NAME} - /> - + + clickSearchFilter(SearchFilter.ASSIGNEE_NAME)} + clicked={searchFilterSelected === SearchFilter.ASSIGNEE_NAME} + /> + - - clickSearchFilter(SearchFilter.SUBJECT_CODE)} - clicked={searchFilterSelected === SearchFilter.SUBJECT_CODE} - /> + + clickSearchFilter(SearchFilter.SUBJECT_CODE)} + clicked={searchFilterSelected === SearchFilter.SUBJECT_CODE} + /> + + + + clickSearchFilter(SearchFilter.OIPC_NUMBER)} + clicked={searchFilterSelected === SearchFilter.OIPC_NUMBER} + /> + @@ -732,6 +771,61 @@ const AdvancedSearch = ({ userDetail }) => { /> + + + + Request Flags + + + + + + + } + label="Restricted" + /> + + } + label="OIPC" + /> + + } + label="Phased" + /> + + diff --git a/forms-flow-web/src/components/FOI/Dashboard/Ministry/AdvancedSearch/enum.js b/forms-flow-web/src/components/FOI/Dashboard/Ministry/AdvancedSearch/enum.js index 95a4f8ca8..4afb63782 100644 --- a/forms-flow-web/src/components/FOI/Dashboard/Ministry/AdvancedSearch/enum.js +++ b/forms-flow-web/src/components/FOI/Dashboard/Ministry/AdvancedSearch/enum.js @@ -6,6 +6,7 @@ const SearchFilter = Object.freeze({ APPLICANT_NAME: "applicantname", ASSIGNEE_NAME: "ministryassigneename", SUBJECT_CODE: "subjectcode", + OIPC_NUMBER: "oipc_number" }); const DateRangeTypes = Object.freeze([ 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 e7bb8b0b5..0f35fba60 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.requeststatuslabel = StateEnum.intakeinprogress.label; 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 1da3db7ab..e6b098eb7 100644 --- a/forms-flow-web/src/components/FOI/FOIRequest/FOIRequest.js +++ b/forms-flow-web/src/components/FOI/FOIRequest/FOIRequest.js @@ -24,12 +24,17 @@ import { fetchFOIMinistryAssignedToList, fetchFOISubjectCodeList, fetchFOIPersonalDivisionsAndSections, + fetchOIPCOutcomes, + fetchOIPCStatuses, + fetchOIPCReviewtypes, + fetchOIPCInquiryoutcomes } from "../../../apiManager/services/FOI/foiMasterDataServices"; import { fetchFOIRequestDetailsWrapper, fetchFOIRequestDescriptionList, fetchRequestDataFromAxis, fetchRestrictedRequestCommentTagList, + deleteOIPCDetails } from "../../../apiManager/services/FOI/foiRequestServices"; import { fetchFOIRequestAttachmentsList } from "../../../apiManager/services/FOI/foiAttachmentServices"; import { fetchCFRForm } from "../../../apiManager/services/FOI/foiCFRFormServices"; @@ -44,6 +49,8 @@ import { fetchRedactedSections, fetchPDFStitchStatusForRedlines, fetchPDFStitchStatusForResponsePackage, + fetchPDFStitchedStatusForOIPCRedlineReview, + fetchPDFStitchedStatusForOIPCRedline, } from "../../../apiManager/services/FOI/foiRecordServices"; import { makeStyles } from "@material-ui/core/styles"; import FOI_COMPONENT_CONSTANTS from "../../../constants/FOI/foiComponentConstants"; @@ -83,12 +90,15 @@ import DivisionalTracking from "./DivisionalTracking"; import RedactionSummary from "./RedactionSummary"; import AxisDetails from "./AxisDetails/AxisDetails"; import AxisMessageBanner from "./AxisDetails/AxisMessageBanner"; +import { toast } from "react-toastify"; import HomeIcon from "@mui/icons-material/Home"; import { RecordsLog } from "../customComponents/Records"; 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: { @@ -157,8 +167,7 @@ const FOIRequest = React.memo(({ userDetail }) => { const [attachments, setAttachments] = useState(requestAttachments); const [comment, setComment] = useState([]); const [requestState, setRequestState] = useState(StateEnum.unopened.name); - const disableInput = - requestState?.toLowerCase() === StateEnum.closed.name.toLowerCase(); + const [disableInput, setDisableInput] = useState(requestState?.toLowerCase() === StateEnum.closed.name.toLowerCase() && !requestDetails?.isoipcreview); const [_tabStatus, settabStatus] = React.useState(requestState); let foitabheaderBG = getTabBG(_tabStatus, requestState); @@ -251,6 +260,24 @@ 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, removeAllOIPCs} = useOIPCHook(); + const [oipcDataInitial, setOipcDataInitial] = useState(oipcData); + + //Update disableInput when requestState changes + useEffect(() => { + setDisableInput(requestState?.toLowerCase() === StateEnum.closed.name.toLowerCase() && !isOIPCReview); + }, [requestState, isOIPCReview]) + + useEffect(() => { + if (!oipcDataInitial) { + setOipcDataInitial(oipcData); + return; + } + //check to see if oipcData has been updated, if so, enable save button + if (JSON.stringify(oipcData) != JSON.stringify(oipcDataInitial)) { + setDisableInput(false) + } + }, [oipcData, requestDetails.isoipcreview]); useEffect(() => { if (window.location.href.indexOf("comments") > -1) { @@ -276,12 +303,15 @@ const FOIRequest = React.memo(({ userDetail }) => { dispatch(fetchPDFStitchStatusForHarms(requestId, ministryId)); dispatch(fetchPDFStitchStatusForRedlines(requestId, ministryId)); dispatch(fetchPDFStitchStatusForResponsePackage(requestId, ministryId)); + dispatch(fetchPDFStitchedStatusForOIPCRedline(requestId, ministryId)); + dispatch(fetchPDFStitchedStatusForOIPCRedlineReview(requestId, ministryId)); fetchCFRForm(ministryId, dispatch); dispatch(fetchApplicantCorrespondence(requestId, ministryId)); dispatch(fetchApplicantCorrespondenceTemplates()); dispatch( fetchRedactedSections(ministryId, (_err, res) => { - setRedactedSections(res.sections); + setRedactedSections(res); + }) ); } @@ -291,6 +321,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 +360,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 +670,61 @@ const FOIRequest = React.memo(({ userDetail }) => { setAssignedToValue(value); }; + const saveOIPCNoReview = () => { + const toastID = toast.loading("Saving request with removed OIPC review...") + removeAllOIPCs(); + setIsOIPCReview(false); + dispatch( + deleteOIPCDetails( + requestId, + ministryId, + (err, _res) => { + if(!err) { + toast.update(toastID, { + type: "success", + render: "OIPC details have been saved successfully.", + position: "top-right", + isLoading: false, + autoClose: 3000, + hideProgressBar: true, + closeOnClick: true, + pauseOnHover: true, + draggable: true, + progress: undefined, + }); + } else { + toast.error( + "Temporarily unable to save the OIPC details. Please try again in a few minutes.", + { + position: "top-right", + autoClose: 3000, + hideProgressBar: true, + closeOnClick: true, + pauseOnHover: true, + draggable: true, + progress: undefined, + } + ); + } + }) + ) + } + + const oipcSectionRef = React.useRef(null); + const handleOipcReviewFlagChange = (isSelected) => { + if (!isSelected) { + saveOIPCNoReview(); + } else { + setIsOIPCReview(isSelected); + requestDetails.isoipcreview = isSelected; + oipcSectionRef.current.scrollIntoView(); + //timeout to allow react state to update after setState call + setTimeout(() => { + oipcSectionRef.current.scrollIntoView(); + }, (10)); + } + } + //handle email validation const [validation, setValidation] = React.useState({}); const handleEmailValidation = (validationObj) => { @@ -650,7 +754,9 @@ const FOIRequest = React.memo(({ userDetail }) => { requiredRequestDetailsValues, requiredAxisDetails, isAddRequest, - _currentrequestStatus + _currentrequestStatus, + oipcData, + requestDetails.isoipcreview, ); const classes = useStyles(); @@ -736,7 +842,7 @@ const FOIRequest = React.memo(({ userDetail }) => { _status, requestExtensions, }); - + setRequestStatus(mappedBottomText); }; @@ -761,7 +867,7 @@ const FOIRequest = React.memo(({ userDetail }) => { }; } }, [editorChange]); - + const tabclick = (param) => { if (param === "Comments") { sessionStorage.setItem("foicommentcategory", 1); @@ -985,7 +1091,8 @@ const FOIRequest = React.memo(({ userDetail }) => {
- {bottomTextArray.length > 0 && + {isOIPCReview && requestDetails.isreopened ? "" + : bottomTextArray.length > 0 && _requestStatus && _requestStatus.toLowerCase().includes("days") && bottomTextArray.map((text) => { @@ -1091,7 +1198,10 @@ const FOIRequest = React.memo(({ userDetail }) => { handlestatusudpate={handlestatusudpate} userDetail={userDetail} disableInput={disableInput} + isMinistry={false} isAddRequest={isAddRequest} + handleOipcReviewFlagChange={handleOipcReviewFlagChange} + showOipcReviewFlag={requestState.toLowerCase() !== StateEnum.intakeinprogress.name.toLowerCase() && requestState.toLowerCase() !== StateEnum.unopened.name.toLowerCase()} /> {(isAddRequest || requestState === StateEnum.unopened.name) && ( @@ -1185,9 +1295,8 @@ const FOIRequest = React.memo(({ userDetail }) => { createSaveRequestObject={createSaveRequestObject} disableInput={disableInput} /> - - {redactedSections.length > 0 && ( - + {Object.keys(redactedSections).length > 0 && ( + )} { 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..8bcf0ac9b 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,8 @@ import { toast } from "react-toastify"; import _ from 'lodash'; import RequestRestriction from "../../customComponents/RequestRestriction"; import ConfirmModal from "../../customComponents/ConfirmModal"; +import RequestFlag from '../../customComponents/RequestFlag'; +import { Grid } from '@material-ui/core'; const useStyles = makeStyles((theme) => ({ formControl: { @@ -35,6 +37,9 @@ const useStyles = makeStyles((theme) => ({ fontWeight: theme.typography.fontWeightBold, opacity: 1, }, + blankrow: { + padding: 25 + } })); const FOIRequestHeader = React.memo( ({ @@ -48,6 +53,9 @@ const FOIRequestHeader = React.memo( userDetail, disableInput, isAddRequest, + handleOipcReviewFlagChange, + showOipcReviewFlag, + isMinistry, }) => { /** * Header of Review request in the UI @@ -259,33 +267,54 @@ const FOIRequestHeader = React.memo(
-
-
- {window.location.href.indexOf(FOI_COMPONENT_CONSTANTS.ADDREQUEST) === - -1 && ( -
- + + +
+ {window.location.href.indexOf(FOI_COMPONENT_CONSTANTS.ADDREQUEST) === + -1 && ( + + )} + {!isAddRequest && status.toLowerCase() !== StateEnum.unopened.name.toLowerCase() && (isIAORestrictedFileManager() || + (isLoaded && isRequestWatcherOrAssignee(requestWatchers,assigneeObj,userDetail?.preferred_username))) && + + + } +
+
+ +
+ + {/* */}
- )} - {!isAddRequest && status.toLowerCase() !== StateEnum.unopened.name.toLowerCase() && (isIAORestrictedFileManager() || - (isLoaded && isRequestWatcherOrAssignee(requestWatchers,assigneeObj,userDetail?.preferred_username))) && - - } -
+ +
-
+
{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 c6032e05e..17fc5ee26 100644 --- a/forms-flow-web/src/components/FOI/FOIRequest/MinistryReview/MinistryReview.js +++ b/forms-flow-web/src/components/FOI/FOIRequest/MinistryReview/MinistryReview.js @@ -29,6 +29,8 @@ import { fetchPDFStitchStatusForHarms, fetchPDFStitchStatusForRedlines, fetchPDFStitchStatusForResponsePackage, + fetchPDFStitchedStatusForOIPCRedlineReview, + fetchPDFStitchedStatusForOIPCRedline, } from "../../../../apiManager/services/FOI/foiRecordServices"; import { fetchCFRForm } from "../../../../apiManager/services/FOI/foiCFRFormServices"; @@ -62,6 +64,8 @@ import { UnsavedModal } from "../../customComponents"; import { DISABLE_GATHERINGRECORDS_TAB } from "../../../../constants/constants"; import _ from "lodash"; import { MinistryNeedsScanning } from "../../../../constants/FOI/enum"; +import {isMinistryLogin} from "../../../../helper/FOI/helper"; +import OIPCDetails from "../OIPCDetails/Index"; const useStyles = makeStyles((theme) => ({ root: { @@ -108,6 +112,7 @@ const MinistryReview = React.memo(({ userDetail }) => { const [_currentrequestStatus, setcurrentrequestStatus] = React.useState(""); const [_tabStatus, settabStatus] = React.useState(requestState); + //gets the request detail from the store const IsDivisionalCoordinator = () => { return userDetail?.role?.includes("DivisionalCoordinator"); @@ -210,6 +215,8 @@ const MinistryReview = React.memo(({ userDetail }) => { dispatch(fetchPDFStitchStatusForHarms(requestId, ministryId)); dispatch(fetchPDFStitchStatusForRedlines(requestId, ministryId)); dispatch(fetchPDFStitchStatusForResponsePackage(requestId, ministryId)); + dispatch(fetchPDFStitchedStatusForOIPCRedline(requestId, ministryId)); + dispatch(fetchPDFStitchedStatusForOIPCRedlineReview(requestId, ministryId)); fetchCFRForm(ministryId, dispatch); if (bcgovcode) dispatch(fetchFOIMinistryAssignedToList(bcgovcode)); } @@ -234,6 +241,10 @@ const MinistryReview = React.memo(({ userDetail }) => { ); const [unSavedRequest, setUnSavedRequest] = React.useState(false); let ministryassignedtousername = "Unassigned"; + + const userGroups = userDetail?.groups?.map(group => group.slice(1)); + const isMinistry = isMinistryLogin(userGroups); + useEffect(() => { const requestDetailsValue = requestDetails; setSaveMinistryRequestObject(requestDetailsValue); @@ -338,6 +349,8 @@ const MinistryReview = React.memo(({ userDetail }) => { dispatch(fetchPDFStitchStatusForHarms(requestId, ministryId)); dispatch(fetchPDFStitchStatusForRedlines(requestId, ministryId)); dispatch(fetchPDFStitchStatusForResponsePackage(requestId, ministryId)); + dispatch(fetchPDFStitchedStatusForOIPCRedline(requestId, ministryId)); + dispatch(fetchPDFStitchedStatusForOIPCRedlineReview(requestId, ministryId)); fetchCFRForm(ministryId, dispatch); setStateChanged(false); setcurrentrequestStatus(_state); @@ -752,6 +765,7 @@ const MinistryReview = React.memo(({ userDetail }) => { setSaveMinistryRequestObject } ministryAssigneeValue={ministryAssignedToValue} + isMinistry={isMinistry} /> @@ -767,6 +781,12 @@ const MinistryReview = React.memo(({ userDetail }) => { /> {divisionsBox} {/* */} + {requestDetails.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,28 @@ const RequestHeader = React.memo(({
-
-
-
+
+ + {watcherBox} -
- { - (isLoaded && (isRequestWatcherOrMinistryAssignee(requestWatchers,ministryAssigneeValue,userDetail?.preferred_username) || - isMinistryRestrictedFileManager())) && - - } -
+ { + (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..438443cd8 --- /dev/null +++ b/forms-flow-web/src/components/FOI/FOIRequest/OIPCDetails/Index.jsx @@ -0,0 +1,53 @@ +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'; +import OIPCDetailsMinistry from "./OIPCDetailsMinistry"; + +const OIPCDetails = (props) => { + const { oipcData, addOIPC, removeOIPC, updateOIPC, isMinistry } = props; + + //Styling + const useStyles = makeStyles({ + heading: { + color: '#FFF', + fontSize: '16px !important', + fontWeight: 'bold !important' + }, + accordionSummary: { + flexDirection: 'row-reverse' + } + }); + const classes = useStyles(); + + return ( + <> + {isMinistry ? + : +
+ + }> + 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/OIPCDetailsMinistry.jsx b/forms-flow-web/src/components/FOI/FOIRequest/OIPCDetails/OIPCDetailsMinistry.jsx new file mode 100644 index 000000000..ae2f181eb --- /dev/null +++ b/forms-flow-web/src/components/FOI/FOIRequest/OIPCDetails/OIPCDetailsMinistry.jsx @@ -0,0 +1,170 @@ +import { Divider, Card, CardContent } from '@material-ui/core'; +import './oipcdetails.scss'; + +const OIPCDetailsMinistry = (props) => { + const {oipcData} = props; + + return ( + + + + {oipcData.map((oipc, index) => { + return ( + <> +
+ +
+
+ Order No +
+
+ {oipc.oipcno} +
+
+ +
+
+ Received Date +
+
+ {oipc.receiveddate} +
+
+ +
+
+ Review Type +
+
+ {oipc.reviewetype} +
+
+ +
+
+ Reason +
+
+ {oipc.reason} +
+
+ +
+
+ Status +
+
+ {oipc.status} +
+
+ + {oipc.investigator && +
+
+ Investigator/Adjudicator +
+
+ {oipc.investigator} +
+
+ } + + {oipc.outcomeid && +
+
+ Outcome +
+
+ {oipc.outcome} +
+
+ } + + {oipc.closedate && +
+
+ Closed Date +
+
+ {oipc.closedate} +
+
+ } + + {oipc.isinquiry && +
+
+ In Inquiry? +
+
+ Yes +
+
+ } + + {oipc.inquiryattributes?.inquirydate && +
+
+ Inquiry Order Comply Date +
+
+ {oipc.inquiryattributes.inquirydate} +
+
+ } + + {oipc.inquiryattributes?.orderno && +
+
+ Inquiry Order No +
+
+ {oipc.inquiryattributes.orderno} +
+
+ } + + {oipc.inquiryattributes?.inquiryoutcome && +
+
+ Inquiry Outcome +
+
+ {oipc.inquiryattributes.inquiryoutcomename} +
+
+ } + + {oipc.isjudicialreview && +
+
+ In Judical Review? +
+
+ Yes +
+
+ } + + {oipc.issubsequentappeal && +
+
+ In Subsequent Appeal? +
+
+ Yes +
+
+ } + +
+ {index !== (oipcData?.length - 1) && } + + ) + })} +
+
+ ); +} + +export default OIPCDetailsMinistry; \ 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..f9e890fbb --- /dev/null +++ b/forms-flow-web/src/components/FOI/FOIRequest/OIPCDetails/OIPCItem.jsx @@ -0,0 +1,395 @@ +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 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); + }, [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 || oipc.receiveddate === ""} + 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} + > + + Select Review Type + + {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} + > + + Select Review Type & Reason + + {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} + > + + Select Status + + {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 ? oipc.outcomeid : -1} + label="Outcome" + > + + Select 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.inquiryattributes.orderno ? (!oipc.outcomeid || oipc.outcomeid === 5) && oipc.inquiryattributes.inquirydate === null || oipc.inquiryattributes.inquirydate === "" : false} + required={oipc.inquiryattributes.orderno} + disabled={oipc.outcomeid && oipc.outcomeid !== 5} + /> + + + handleInquiryFields(event.target.value, "ORDERNO")} + InputLabelProps={{ shrink: true }} + error={oipc.inquiryattributes.inquirydate ? (!oipc.outcomeid || oipc.outcomeid === 5) && oipc.inquiryattributes.orderno === "" : false} + required={oipc.inquiryattributes.inquirydate} + disabled={oipc.outcomeid && oipc.outcomeid !== 5} + placeholder="Order Number" + /> + + + handleInquiryFields(event.target.value, "INQUIRYOUTCOME")} + fullWidth + value={oipc.inquiryattributes.inquiryoutcome ? oipc.inquiryattributes.inquiryoutcome : -1} + label="Inquiry Outcome" + disabled={oipc.outcomeid && oipc.outcomeid !== 5} + > + + Select Inquiry Outcome + + {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..878d8bea8 --- /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, + oipc, +}) =>{ + + const handleSave = () => { + setShowModal(false); + removeOIPC(oipc.id) + }; + 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..965441ece --- /dev/null +++ b/forms-flow-web/src/components/FOI/FOIRequest/OIPCDetails/oipcHook.js @@ -0,0 +1,104 @@ +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) => { + 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); + const [resettoInitial, setResettoInitial] = useState(false); + useEffect(() => { + if (resettoInitial == false) { + 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) => { + if (oipcData.length === 1) { + setOipcData([]); + setResettoInitial(true); + } + else { + 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([]); + setResettoInitial(true); + } + + 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..9d467ac10 --- /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; + height: 1.5px; +} + \ No newline at end of file diff --git a/forms-flow-web/src/components/FOI/FOIRequest/RedactionSummary.js b/forms-flow-web/src/components/FOI/FOIRequest/RedactionSummary.js index 2aa8d67f4..43781127c 100644 --- a/forms-flow-web/src/components/FOI/FOIRequest/RedactionSummary.js +++ b/forms-flow-web/src/components/FOI/FOIRequest/RedactionSummary.js @@ -8,9 +8,8 @@ import Typography from '@material-ui/core/Typography'; import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; -const RedactionSummary = React.memo(({sections}) => { - - const useStyles = makeStyles({ +const RedactionSummary = React.memo(({sections, isoipcreview}) => { + const useStyles = makeStyles({ heading: { color: '#FFF', fontSize: '16px !important', @@ -34,12 +33,25 @@ const RedactionSummary = React.memo(({sections}) => { SUMMARY OF REDACTIONS -
+ + + {sections?.Redline !== "" && ( +
Redaction Sections Applied - {sections} -
+ {sections?.Redline} +
+ )} + {(isoipcreview && sections.OIPC && sections?.OIPC !== "") && ( +
+ + OIPC Redaction Sections Applied + + {sections?.OIPC} +
+ )} +
diff --git a/forms-flow-web/src/components/FOI/FOIRequest/utils.js b/forms-flow-web/src/components/FOI/FOIRequest/utils.js index 1ed724913..dceaabbbf 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,16 @@ export const checkValidationError = ( .includes("select") || !requiredRequestDetailsValues.receivedDate || !requiredRequestDetailsValues.requestStartDate || - !requiredAxisDetails.axisRequestId + !requiredAxisDetails.axisRequestId || + (oipcData?.length > 0 && isOipcReview && oipcData?.some((oipc) => { + if (oipc.inquiryattributes?.inquirydate) { + return oipc.inquiryattributes.orderno === ""; + } + if (oipc.inquiryattributes?.orderno) { + return oipc.inquiryattributes?.inquirydate === null || oipc.inquiryattributes?.inquirydate === ""; + } + return oipc.oipcno === "" || oipc.receiveddate === null || oipc.receiveddate === "" || oipc.reviewtypeid === null || oipc.reasonid === null || oipc.statusid === null; + })) ); }; diff --git a/forms-flow-web/src/components/FOI/customComponents/Attachments/AttachmentModal.js b/forms-flow-web/src/components/FOI/customComponents/Attachments/AttachmentModal.js index 5ac12bd45..b250306fd 100644 --- a/forms-flow-web/src/components/FOI/customComponents/Attachments/AttachmentModal.js +++ b/forms-flow-web/src/components/FOI/customComponents/Attachments/AttachmentModal.js @@ -323,8 +323,8 @@ export default function AttachmentModal({ body: ( <> Replace the existing record with a reformatted or updated - version of the same record.

The original file that was - uploaded will still be available for download. + version of the same record.

If the file being replaced + is also a pdf file, the replaced file will no longer be available. ), }; diff --git a/forms-flow-web/src/components/FOI/customComponents/FileUpload/util.js b/forms-flow-web/src/components/FOI/customComponents/FileUpload/util.js index dbc2c3831..026508ccd 100644 --- a/forms-flow-web/src/components/FOI/customComponents/FileUpload/util.js +++ b/forms-flow-web/src/components/FOI/customComponents/FileUpload/util.js @@ -35,8 +35,16 @@ export const getErrorMessage = (_duplicateFiles, _typeErrorFiles, _overSizedFile if (_overSizedFiles.length > 0) { _errorMessage.push(<>The specified file(s) {_overSizedFiles.join(", ")} could not be uploaded. Only files {maxFileSize}MB or under can be uploaded.); } + const hasOnlyPdfMimeTypes = (mimeTypes) => { + let result = true; + mimeTypes.forEach((mimeType) => { + if (mimeType !== 'application/pdf' && mimeType !== '.pdf') result = false; + }) + return result; + } + if (_typeErrorFiles.length > 0) { - if (mimeTypes.length === 1 && mimeTypes[0] === 'application/pdf') { + if (hasOnlyPdfMimeTypes(mimeTypes)) { _errorMessage.push(<>The specified file(s) {_typeErrorFiles.join(", ")} could not be uploaded. Only PDF filetypes are allowed.); } else { _errorMessage.push(<>The specified file(s) {_typeErrorFiles.join(", ")} could not be uploaded. Only files with the following extensions are allowed: {multipleFiles ? 'Excel (xls, xlsx, macro), pdf, image, word, email' : singleFileUploadAllowedFileExtensions}); diff --git a/forms-flow-web/src/components/FOI/customComponents/Records/index.js b/forms-flow-web/src/components/FOI/customComponents/Records/index.js index 764b4ec1f..e54b1fbba 100644 --- a/forms-flow-web/src/components/FOI/customComponents/Records/index.js +++ b/forms-flow-web/src/components/FOI/customComponents/Records/index.js @@ -30,6 +30,8 @@ import { fetchPDFStitchedRecordForHarms, fetchPDFStitchedRecordForRedlines, fetchPDFStitchedRecordForResponsePackage, + fetchPDFStitchedRecordForOIPCRedline, + fetchPDFStitchedRecordForOIPCRedlineReview, checkForRecordsChange, } from "../../../../apiManager/services/FOI/foiRecordServices"; import { @@ -62,14 +64,6 @@ import TextField from "@mui/material/TextField"; import { saveAs } from "file-saver"; import { downloadZip } from "client-zip"; import { ClickableChip } from "../../Dashboard/utils"; -import CircularProgress from "@mui/material/CircularProgress"; -import AttachmentFilter from "../Attachments/AttachmentFilter"; -import Accordion from "@material-ui/core/Accordion"; -import Stack from "@mui/material/Stack"; -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 SearchIcon from "@material-ui/icons/Search"; import InputAdornment from "@mui/material/InputAdornment"; import InputBase from "@mui/material/InputBase"; @@ -80,15 +74,12 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faCheckCircle, faClone, - faTrashAlt, - faTrashCan, } from "@fortawesome/free-regular-svg-icons"; import { faSpinner, faExclamationCircle, faBan, faArrowTurnUp, - faHistory, faTrash, faPenToSquare, faLinkSlash, @@ -237,6 +228,12 @@ export const RecordsLog = ({ let redlinePdfStitchStatus = useSelector( (state) => state.foiRequests.foiPDFStitchStatusForRedlines ); + let oipcRedlineReviewPdfStitchedStatus = useSelector( + (state) => state.foiRequests.foiPDFStitchStatusForOipcRedlineReview + ); + let oipcRedlinePdfStitchedStatus = useSelector( + (state) => state.foiRequests.foiPDFStitchStatusForOipcRedline + ); let responsePackagePdfStitchStatus = useSelector( (state) => state.foiRequests.foiPDFStitchStatusForResponsePackage ); @@ -247,6 +244,12 @@ export const RecordsLog = ({ let redlinePdfStitchedRecord = useSelector( (state) => state.foiRequests.foiPDFStitchedRecordForRedlines ); + let oipcRedlineReviewPdfStitchedRecord = useSelector( + (state) => state.foiRequests.foiPDFStitchedRecordForOipcRedlineReview + ); + let oipcRedlinePdfStitchedRecord = useSelector( + (state) => state.foiRequests.foiPDFStitchedRecordForOipcRedline + ); let responsePackagePdfStitchedRecord = useSelector( (state) => state.foiRequests.foiPDFStitchedRecordForResponsePackage ); @@ -375,6 +378,18 @@ export const RecordsLog = ({ useState(false); const [isResponsePackageDownloadFailed, setIsResponsePackageDownloadFailed] = useState(false); + const [isOIPCRedlineReviewReady, setIsOIPCRedlineReviewReady] = + useState(false); + const [isOIPCRedlineReviewFailed, setIsOIPCRedlineReviewFailed] = + useState(false); + const [isOIPCRedlineReviewInProgress, setIsOIPCRedlineReviewInProgress] = + useState(false); + const [isOIPCRedlineReady, setIsOIPCRedlineReady] = + useState(false); + const [isOIPCRedlineFailed, setIsOIPCRedlineFailed] = + useState(false); + const [isOIPCRedlineInProgress, setIsOIPCRedlineInProgress] = + useState(false); const [isAllSelected, setIsAllSelected] = useState(false); useEffect(() => { @@ -438,10 +453,29 @@ export const RecordsLog = ({ setIsResponsePackageDownloadFailed, fetchPDFStitchedRecordForResponsePackage ); + + // Update OIPC Review Package PDF Stitch Status + updateStatus( + oipcRedlineReviewPdfStitchedStatus, + setIsOIPCRedlineReviewInProgress, + setIsOIPCRedlineReviewReady, + setIsOIPCRedlineReviewFailed, + fetchPDFStitchedRecordForOIPCRedlineReview + ); + // Update Redline OIPC PDF Stitch Status + updateStatus( + oipcRedlinePdfStitchedStatus, + setIsOIPCRedlineInProgress, + setIsOIPCRedlineReady, + setIsOIPCRedlineFailed, + fetchPDFStitchedRecordForOIPCRedline + ); }, [ pdfStitchStatus, redlinePdfStitchStatus, responsePackagePdfStitchStatus, + oipcRedlinePdfStitchedStatus, + oipcRedlineReviewPdfStitchedStatus, requestId, ministryId, ]); @@ -454,8 +488,14 @@ export const RecordsLog = ({ if (item.id === 3 && isResponsePackageDownloadReady) { item.disabled = false; } + if (item.id === 4 && isOIPCRedlineReady) { + item.disabled = false; + } + if (item.id === 5 && isOIPCRedlineReviewReady) { + item.disabled = false; + } }); - }, [isRedlineDownloadReady, isResponsePackageDownloadReady]); + }, [isRedlineDownloadReady, isResponsePackageDownloadReady, isOIPCRedlineReady, isOIPCRedlineReviewReady]); const addAttachments = () => { setModalFor("add"); @@ -832,6 +872,12 @@ export const RecordsLog = ({ } else if (e.target.value === 3 && isResponsePackageDownloadReady) { const s3filepath = responsePackagePdfStitchedRecord?.finalpackagepath; handleDownloadZipFile(s3filepath, e.target.value); + } else if (e.target.value === 4 && isOIPCRedlineReady) { + const s3filepath = oipcRedlinePdfStitchedRecord?.finalpackagepath; + handleDownloadZipFile(s3filepath, e.target.value); + } else if (e.target.value === 5 && isOIPCRedlineReviewReady) { + const s3filepath = oipcRedlineReviewPdfStitchedRecord?.finalpackagepath; + handleDownloadZipFile(s3filepath, e.target.value); } setCurrentDownload(e.target.value); @@ -1006,6 +1052,14 @@ export const RecordsLog = ({ setIsResponsePackageDownloadInProgress(false); setIsResponsePackageDownloadReady(false); setIsResponsePackageDownloadFailed(true); + } else if (itemid === 4) { + setIsOIPCRedlineInProgress(false); + setIsOIPCRedlineReady(false); + setIsOIPCRedlineFailed(true); + } else if (itemid === 5) { + setIsOIPCRedlineReviewInProgress(false); + setIsOIPCRedlineReviewReady(false); + setIsOIPCRedlineReviewFailed(true); } }; @@ -1013,7 +1067,9 @@ export const RecordsLog = ({ return ( (itemid === 1 && isDownloadReady) || (itemid === 2 && isRedlineDownloadReady) || - (itemid === 3 && isResponsePackageDownloadReady) + (itemid === 3 && isResponsePackageDownloadReady) || + (itemid === 4 && isOIPCRedlineReady) || + (itemid === 5 && isOIPCRedlineReviewReady) ); }; @@ -1021,7 +1077,9 @@ export const RecordsLog = ({ return ( (itemid === 1 && isDownloadFailed) || (itemid === 2 && isRedlineDownloadFailed) || - (itemid === 3 && isResponsePackageDownloadFailed) + (itemid === 3 && isResponsePackageDownloadFailed) || + (itemid === 4 && isOIPCRedlineFailed) || + (itemid === 5 && isOIPCRedlineReviewFailed) ); }; @@ -1029,9 +1087,21 @@ export const RecordsLog = ({ return ( (itemid === 1 && isDownloadInProgress) || (itemid === 2 && isRedlineDownloadInProgress) || - (itemid === 3 && isResponsePackageDownloadInProgress) + (itemid === 3 && isResponsePackageDownloadInProgress) || + (itemid === 4 && isOIPCRedlineInProgress) || + (itemid === 5 && isOIPCRedlineReviewInProgress) ); }; + + const getPackageDatetime = (itemid) => { + return ( + (itemid === 1 && pdfStitchedRecord?.createdat_datetime) || + (itemid === 2 && redlinePdfStitchedRecord?.createdat_datetime) || + (itemid === 3 && responsePackagePdfStitchedRecord?.createdat_datetime) || + (itemid === 4 && oipcRedlinePdfStitchedRecord?.createdat_datetime) || + (itemid === 5 && oipcRedlineReviewPdfStitchedRecord?.createdat_datetime) + ); + } const downloadAllDocuments = async () => { let blobs = []; @@ -1660,7 +1730,9 @@ export const RecordsLog = ({ className={classes.statusIcons} /> ) : null} - {item.label} + + {item.label} + ); } 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..65dd5fd6c --- /dev/null +++ b/forms-flow-web/src/components/FOI/customComponents/RequestFlag.js @@ -0,0 +1,251 @@ +import { useEffect, 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"; + +//Types are: +//oipcreview +//phasedrelease +const RequestFlag = ({ isActive, type, handleSelect, showFlag = true, isDisabled }) => { + const [isSelected, setIsSelected] = useState(isActive || false); + const [modalOpen, setModalOpen] = useState(false); + const [modalHeading, setModalHeading] = useState(""); + const [modalMessage, setModalMessage] = useState(""); + const [modalDescription, setModalDescription] = useState(""); + + useEffect(() => { + if (isActive == null) { + setIsSelected(false); + } else { + setIsSelected(isActive); + } + }, [isActive]) + + // 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 + modalHeadingInactive = "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={isDisabled} + > + {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/Watcher.scss b/forms-flow-web/src/components/FOI/customComponents/Watcher.scss index 975751b07..9f674e35a 100644 --- a/forms-flow-web/src/components/FOI/customComponents/Watcher.scss +++ b/forms-flow-web/src/components/FOI/customComponents/Watcher.scss @@ -27,7 +27,7 @@ } .foi-eye-container { - width: auto; + width: 100px; height: 40px; background-color: #00336652; border-radius: 40px 0px 0px 40px; @@ -38,11 +38,12 @@ .foi-watcher-all { padding-top: 10px; display: flex; + width: 188px; } .foi-watcher-select { padding-top: 8px; - width: 80px; + width: 100px; height: 40px; background-color:#003366; color: white; 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..fcbc352ff --- /dev/null +++ b/forms-flow-web/src/components/FOI/customComponents/requestflag.scss @@ -0,0 +1,78 @@ +.request-flag { + + + .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 #fa7c16; + //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/constants/FOI/enum.js b/forms-flow-web/src/constants/FOI/enum.js index ce2622e6e..ec0ea2aac 100644 --- a/forms-flow-web/src/constants/FOI/enum.js +++ b/forms-flow-web/src/constants/FOI/enum.js @@ -170,6 +170,8 @@ const RecordsDownloadList = [ { id: 1, label: "Download for Harms", disabled: true }, { id: 2, label: "Download Redline for Sign Off", disabled: true }, { id: 3, label: "Download Final Package", disabled: true }, + { id: 4, label: "Download OIPC Redline for Sign Off", disabled: true }, + { id: 5, label: "Download OIPC Redline for OIPC Review", disabled: true }, ]; const RecordDownloadCategory = Object.freeze({ diff --git a/forms-flow-web/src/modules/FOI/foiRequestsReducer.js b/forms-flow-web/src/modules/FOI/foiRequestsReducer.js index 8dd27296b..4da4d7096 100644 --- a/forms-flow-web/src/modules/FOI/foiRequestsReducer.js +++ b/forms-flow-web/src/modules/FOI/foiRequestsReducer.js @@ -57,10 +57,14 @@ const initialState = { }, foiPDFStitchedRecordForHarms: {}, foiPDFStitchedRecordForRedlines: {}, + foiPDFStitchedRecordForOipcRedlineReview: {}, + foiPDFStitchedRecordForOipcRedline: {}, foiPDFStitchedRecordForResponsePackage: {}, foiPDFStitchStatusForHarms: "not started", foiPDFStitchStatusForRedlines: "not started", foiPDFStitchStatusForResponsePackage: "not started", + foiPDFStitchStatusForOipcRedlineReview: "not started", + foiPDFStitchStatusForOipcRedline: "not started", foiRequestCFRForm: { overallsuggestions: "", status: "init", @@ -145,6 +149,10 @@ const initialState = { ".jpg", ], conversionFormats: [], + oipcOutcomes: [], + oipcStatuses: [], + oipcReviewtypes: [], + oipcInquiryoutcomes: [], }; const foiRequests = (state = initialState, action) => { @@ -264,12 +272,26 @@ const foiRequests = (state = initialState, action) => { ...state, foiPDFStitchedRecordForResponsePackage: action.payload, }; + case FOI_ACTION_CONSTANTS.FOI_PDF_STITCHED_RECORD_FOR_OIPCREDLINEREVIEW: + return { + ...state, + foiPDFStitchedRecordForOipcRedlineReview: action.payload, + }; + case FOI_ACTION_CONSTANTS.FOI_PDF_STITCHED_RECORD_FOR_OIPCREDLINE: + return { + ...state, + foiPDFStitchedRecordForOipcRedline: action.payload, + }; case FOI_ACTION_CONSTANTS.FOI_PDF_STITCHED_STATUS_FOR_HARMS: return { ...state, foiPDFStitchStatusForHarms: action.payload }; case FOI_ACTION_CONSTANTS.FOI_PDF_STITCHED_STATUS_FOR_REDLINES: return { ...state, foiPDFStitchStatusForRedlines: action.payload }; case FOI_ACTION_CONSTANTS.FOI_PDF_STITCHED_STATUS_FOR_RESPONSEPACKAGE: return { ...state, foiPDFStitchStatusForResponsePackage: action.payload }; + case FOI_ACTION_CONSTANTS.FOI_PDF_STITCHED_STATUS_FOR_OIPCREDLINEREVIEW: + return { ...state, foiPDFStitchStatusForOipcRedlineReview: action.payload }; + case FOI_ACTION_CONSTANTS.FOI_PDF_STITCHED_STATUS_FOR_OIPCREDLINE: + return { ...state, foiPDFStitchStatusForOipcRedline: action.payload }; case FOI_ACTION_CONSTANTS.FOI_REQUEST_CFR_FORM: return { ...state, @@ -307,6 +329,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/notification-manager/common/notificationtypes.json b/notification-manager/common/notificationtypes.json index 5c04ecec7..b111c5974 100644 --- a/notification-manager/common/notificationtypes.json +++ b/notification-manager/common/notificationtypes.json @@ -1,142 +1,90 @@ { "state": { - "notificationtypeid": 1, "name": "State", - "description": "State", - "active": true, "notificationtypelabel": "state" }, "division": { - "notificationtypeid": 2, "name": "Division", - "description": "Division", - "active": true, "notificationtypelabel": "division" }, "newusercomments": { - "notificationtypeid": 3, "name": "New User Comments", - "description": "New User Comments", - "active": true, "notificationtypelabel": "newusercomments" }, "extension": { - "notificationtypeid": 4, "name": "Extension", - "description": "Extension", - "active": true, "notificationtypelabel": "extension" }, "iaoassignment": { - "notificationtypeid": 5, "name": "IAO Assignment", - "description": "IAO Assignment", - "active": true, "notificationtypelabel": "iaoassignment" }, "ministryassignment": { - "notificationtypeid": 6, "name": "Ministry Assignment", - "description": "Ministry Assignment", - "active": true, "notificationtypelabel": "ministryassignment" }, "cfrduereminder": { - "notificationtypeid": 7, "name": "CFR Due Reminder", - "description": "CFR Due Reminder", - "active": true, "notificationtypelabel": "cfrduereminder" }, "legislativeduereminder": { - "notificationtypeid": 8, "name": "Legislative Due Reminder", - "description": "Legislative Due Reminder", - "active": true, "notificationtypelabel": "legislativeduereminder" }, "replyusercomments": { - "notificationtypeid": 9, "name": "Reply User Comments", - "description": "Reply User Comments", - "active": true, "notificationtypelabel": "replyusercomments" }, "taggedusercomments": { - "notificationtypeid": 10, "name": "Tagged User Comments", - "description": "Tagged User Comments", - "active": true, "notificationtypelabel": "taggedusercomments" }, "cfrfeeform": { - "notificationtypeid": 11, "name": "CFR Fee Form", - "description": "CFR Fee Form", - "active": true, "notificationtypelabel": "cfrfeeform" }, "groupmembers": { - "notificationtypeid": 12, "name": "Group Members", - "description": "Group Members", - "active": true, "notificationtypelabel": "groupmembers" }, "divisionduereminder": { - "notificationtypeid": 13, "name": "Division Due Reminder", - "description": "Division Due Reminder", - "active": true, "notificationtypelabel": "divisionduereminder" }, "watcher": { - "notificationtypeid": 14, "name": "Watcher", - "description": "Watcher", - "active": true, "notificationtypelabel": "watcher" }, "userassignmentremoval": { - "notificationtypeid": 15, "name": "User Assignment Removal", - "description": "User Assignment Removal", - "active": true, "notificationtypelabel": "userassignmentremoval" }, "emailfailure": { - "notificationtypeid": 16, "name": "Email Failure", - "description": "Email Failure", - "active": true, "notificationtypelabel": "emailfailure" }, "payment": { - "notificationtypeid": 17, "name": "Payment", - "description": "Payment", - "active": true, "notificationtypelabel": "payment" }, "records": { "notificationtypeid": 18, - "name": "Records", - "description": "Records", - "active": true, "notificationtypelabel": "records" }, "pdfstitch": { - "notificationtypeid": 19, "name": "PDFStitch", - "description": "PDFStitch", - "active": true, "notificationtypelabel": "pdfstitch" }, "section5pendingreminder": { - "notificationtypeid": 20, "name": "Section 5 Pending Reminder", - "description": "Section 5 Pending Reminder", - "active": true, "notificationtypelabel": "section5pendingreminder" + }, + "oipc": { + "name": "OIPC", + "notificationtypelabel": "oipc" + }, + "oipcduereminder": { + "name": "OIPC Due Reminder", + "notificationtypelabel": "oipcduereminder" } } diff --git a/notification-manager/common/notificationusertypes.json b/notification-manager/common/notificationusertypes.json index 0964c4f59..a0adaa57e 100644 --- a/notification-manager/common/notificationusertypes.json +++ b/notification-manager/common/notificationusertypes.json @@ -1,30 +1,34 @@ { "watcher": { - "notificationusertypeid": 1, "name": "Watcher", - "description": "Watcher", - "isactive": true, "notificationusertypelabel": "watcher" }, "assignee": { - "notificationusertypeid": 2, "name": "Assignee", - "description": "Assignee", - "isactive": true, + "notificationusertypelabel": "assignee" + }, + "groupmembers": { + "name": "Group Members", + "notificationusertypelabel": "assignee" + }, + "groupmember": { + "name": "groupmembers", + "notificationusertypelabel": "assignee" + }, + "comment": { + "name": "Comment", "notificationusertypelabel": "assignee" }, "commentuser": { - "notificationusertypeid": 3, "name": "Comment User", - "description": "Comment User", - "isactive": true, "notificationusertypelabel": "commentuser" }, + "comment tagged user": { + "name": "comment tagged user", + "notificationusertypelabel": "assignee" + }, "triggereduser": { - "notificationusertypeid": 4, "name": "Triggered User", - "description": "Triggered User", - "isactive": true, "notificationusertypelabel": "assignee" } } diff --git a/request-management-api/common/notificationtypes.json b/request-management-api/common/notificationtypes.json index 5c04ecec7..b111c5974 100644 --- a/request-management-api/common/notificationtypes.json +++ b/request-management-api/common/notificationtypes.json @@ -1,142 +1,90 @@ { "state": { - "notificationtypeid": 1, "name": "State", - "description": "State", - "active": true, "notificationtypelabel": "state" }, "division": { - "notificationtypeid": 2, "name": "Division", - "description": "Division", - "active": true, "notificationtypelabel": "division" }, "newusercomments": { - "notificationtypeid": 3, "name": "New User Comments", - "description": "New User Comments", - "active": true, "notificationtypelabel": "newusercomments" }, "extension": { - "notificationtypeid": 4, "name": "Extension", - "description": "Extension", - "active": true, "notificationtypelabel": "extension" }, "iaoassignment": { - "notificationtypeid": 5, "name": "IAO Assignment", - "description": "IAO Assignment", - "active": true, "notificationtypelabel": "iaoassignment" }, "ministryassignment": { - "notificationtypeid": 6, "name": "Ministry Assignment", - "description": "Ministry Assignment", - "active": true, "notificationtypelabel": "ministryassignment" }, "cfrduereminder": { - "notificationtypeid": 7, "name": "CFR Due Reminder", - "description": "CFR Due Reminder", - "active": true, "notificationtypelabel": "cfrduereminder" }, "legislativeduereminder": { - "notificationtypeid": 8, "name": "Legislative Due Reminder", - "description": "Legislative Due Reminder", - "active": true, "notificationtypelabel": "legislativeduereminder" }, "replyusercomments": { - "notificationtypeid": 9, "name": "Reply User Comments", - "description": "Reply User Comments", - "active": true, "notificationtypelabel": "replyusercomments" }, "taggedusercomments": { - "notificationtypeid": 10, "name": "Tagged User Comments", - "description": "Tagged User Comments", - "active": true, "notificationtypelabel": "taggedusercomments" }, "cfrfeeform": { - "notificationtypeid": 11, "name": "CFR Fee Form", - "description": "CFR Fee Form", - "active": true, "notificationtypelabel": "cfrfeeform" }, "groupmembers": { - "notificationtypeid": 12, "name": "Group Members", - "description": "Group Members", - "active": true, "notificationtypelabel": "groupmembers" }, "divisionduereminder": { - "notificationtypeid": 13, "name": "Division Due Reminder", - "description": "Division Due Reminder", - "active": true, "notificationtypelabel": "divisionduereminder" }, "watcher": { - "notificationtypeid": 14, "name": "Watcher", - "description": "Watcher", - "active": true, "notificationtypelabel": "watcher" }, "userassignmentremoval": { - "notificationtypeid": 15, "name": "User Assignment Removal", - "description": "User Assignment Removal", - "active": true, "notificationtypelabel": "userassignmentremoval" }, "emailfailure": { - "notificationtypeid": 16, "name": "Email Failure", - "description": "Email Failure", - "active": true, "notificationtypelabel": "emailfailure" }, "payment": { - "notificationtypeid": 17, "name": "Payment", - "description": "Payment", - "active": true, "notificationtypelabel": "payment" }, "records": { "notificationtypeid": 18, - "name": "Records", - "description": "Records", - "active": true, "notificationtypelabel": "records" }, "pdfstitch": { - "notificationtypeid": 19, "name": "PDFStitch", - "description": "PDFStitch", - "active": true, "notificationtypelabel": "pdfstitch" }, "section5pendingreminder": { - "notificationtypeid": 20, "name": "Section 5 Pending Reminder", - "description": "Section 5 Pending Reminder", - "active": true, "notificationtypelabel": "section5pendingreminder" + }, + "oipc": { + "name": "OIPC", + "notificationtypelabel": "oipc" + }, + "oipcduereminder": { + "name": "OIPC Due Reminder", + "notificationtypelabel": "oipcduereminder" } } diff --git a/request-management-api/common/notificationusertypes.json b/request-management-api/common/notificationusertypes.json index 8efa082d3..a0adaa57e 100644 --- a/request-management-api/common/notificationusertypes.json +++ b/request-management-api/common/notificationusertypes.json @@ -1,58 +1,34 @@ { "watcher": { - "notificationusertypeid": 1, "name": "Watcher", - "description": "Watcher", - "isactive": true, "notificationusertypelabel": "watcher" }, "assignee": { - "notificationusertypeid": 2, "name": "Assignee", - "description": "Assignee", - "isactive": true, "notificationusertypelabel": "assignee" }, "groupmembers": { - "notificationusertypeid": 2, "name": "Group Members", - "description": "Assignee", - "isactive": true, "notificationusertypelabel": "assignee" }, "groupmember": { - "notificationusertypeid": 2, "name": "groupmembers", - "description": "Assignee", - "isactive": true, "notificationusertypelabel": "assignee" }, "comment": { - "notificationusertypeid": 2, "name": "Comment", - "description": "Assignee", - "isactive": true, "notificationusertypelabel": "assignee" }, "commentuser": { - "notificationusertypeid": 3, "name": "Comment User", - "description": "Comment User", - "isactive": true, "notificationusertypelabel": "commentuser" }, "comment tagged user": { - "notificationusertypeid": 3, "name": "comment tagged user", - "description": "Comment User", - "isactive": true, "notificationusertypelabel": "assignee" }, "triggereduser": { - "notificationusertypeid": 4, "name": "Triggered User", - "description": "Triggered User", - "isactive": true, "notificationusertypelabel": "assignee" } } 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..54a8cc277 --- /dev/null +++ b/request-management-api/migrations/versions/3b399ca506fe_create_oipc_tables.py @@ -0,0 +1,208 @@ +"""Create OIPC tables + +Revision ID: 3b399ca506fe +Revises: 29b44e8dc305 +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 = '29b44e8dc305' +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/3e91d4f34699_oipc_notifications.py b/request-management-api/migrations/versions/3e91d4f34699_oipc_notifications.py new file mode 100644 index 000000000..fe04436ed --- /dev/null +++ b/request-management-api/migrations/versions/3e91d4f34699_oipc_notifications.py @@ -0,0 +1,32 @@ +"""empty message + +Revision ID: 3e91d4f34699 +Revises: 455a24da8c58 +Create Date: 2023-11-30 00:11:56.160830 + +""" +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import postgresql + +# revision identifiers, used by Alembic. +revision = '3e91d4f34699' +down_revision = '455a24da8c58' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.execute('Insert into public."NotificationTypes" (name, description, notificationtypelabel, isactive) values (\'OIPC\', \'OIPC\', \'oipc\', true);commit;') + op.execute('Insert into public."NotificationTypes" (name, description, notificationtypelabel, isactive) values (\'OIPC Due Reminder\', \'OIPC Due Reminder\', \'oipcduereminder\', true);commit;') + op.execute('Insert into public."CommentTypes" (name, description, isactive) values (\'OIPC Tracking\', \'OIPC Tracking\', true);commit;') + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.execute('delete from public."NotificationTypes" where name in (\'OIPC\');commit;') + op.execute('delete from public."NotificationTypes" where name in (\'OIPC Due Reminder\');commit;') + op.execute('delete from public."CommentTypes" where name in (\'OIPC Tracking\');commit;') + # ### end Alembic commands ### 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/migrations/versions/b4da31675bd0_oipc_commentype_change.py b/request-management-api/migrations/versions/b4da31675bd0_oipc_commentype_change.py new file mode 100644 index 000000000..243d9d72b --- /dev/null +++ b/request-management-api/migrations/versions/b4da31675bd0_oipc_commentype_change.py @@ -0,0 +1,29 @@ +"""empty message + +Revision ID: b4da31675bd0 +Revises: 3e91d4f34699 +Create Date: 2023-12-12 23:18:19.440544 + +""" +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import postgresql + +# revision identifiers, used by Alembic. +revision = 'b4da31675bd0' +down_revision = '3e91d4f34699' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.execute('update public."FOIRequestComments" set commenttypeid = (select commenttypeid from "CommentTypes" where name = \'System generated\') where commenttypeid = (select commenttypeid from "CommentTypes" where name = \'OIPC Tracking\');commit;') + op.execute('delete from public."CommentTypes" where name in (\'OIPC Tracking\');commit;') + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.execute('delete from public."CommentTypes" where name in (\'OIPC Tracking\');commit;') + # ### end Alembic commands ### diff --git a/request-management-api/request_api/models/FOIMinistryRequests.py b/request-management-api/request_api/models/FOIMinistryRequests.py index 4db017a84..40db46ec4 100644 --- a/request-management-api/request_api/models/FOIMinistryRequests.py +++ b/request-management-api/request_api/models/FOIMinistryRequests.py @@ -28,6 +28,7 @@ from .FOIMinistryRequestSubjectCodes import FOIMinistryRequestSubjectCode from .SubjectCodes import SubjectCode from request_api.utils.enums import StateName +from .FOIRequestOIPC import FOIRequestOIPC class FOIMinistryRequest(db.Model): # Name of the table in our database @@ -93,6 +94,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]") @@ -100,6 +105,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) @@ -252,13 +259,13 @@ def getstatesummary(cls, ministryrequestid): sql =select status, version from (select distinct name as status, version from "FOIMinistryRequests" fm inner join "FOIRequestStatuses" fs2 on fm.requeststatusid = fs2.requeststatusid where foiministryrequestid=:ministryrequestid order by version asc) as fs3 order by version desc; """ - sql = """select fm2.version, fs2."name" as status from "FOIMinistryRequests" fm2 inner join "FOIRequestStatuses" fs2 on fm2.requeststatusid = fs2.requeststatusid + sql = """select fm2.version, fs2."name" as status, fm2.created_at from "FOIMinistryRequests" fm2 inner join "FOIRequestStatuses" fs2 on fm2.requeststatusid = fs2.requeststatusid where fm2.foiministryrequestid=:ministryrequestid order by version desc""" rs = db.session.execute(text(sql), {'ministryrequestid': ministryrequestid}) _tmp_state = None for row in rs: if row["status"] != _tmp_state: - transitions.append({"status": row["status"], "version": row["version"]}) + transitions.append({"status": row["status"], "version": row["version"], "created_at": row["created_at"]}) _tmp_state = row["status"] except Exception as ex: logging.error(ex) @@ -349,6 +356,18 @@ def getrequestssubquery(cls, groups, filterfields, keyword, additionalfilter, us #subquery for getting extension count subquery_extension_count = _session.query(FOIRequestExtension.foiministryrequest_id, func.count(distinct(FOIRequestExtension.foirequestextensionid)).filter(FOIRequestExtension.isactive == True).label('extensions')).group_by(FOIRequestExtension.foiministryrequest_id).subquery() + #subquery for getting all, distinct oipcs for foiministry request + subquery_with_oipc_sql = """ + SELECT distinct on (foiministryrequest_id) foiministryrequest_id, foiministryrequestversion_id, outcomeid + FROM "FOIRequestOIPC" fo + order by foiministryrequest_id, foiministryrequestversion_id desc + """ + subquery_with_oipc = text(subquery_with_oipc_sql).columns(FOIRequestOIPC.foiministryrequest_id, FOIRequestOIPC.foiministryrequestversion_id, FOIRequestOIPC.outcomeid).alias("oipcnoneoutcomes") + joincondition_oipc = [ + subquery_with_oipc.c.foiministryrequest_id == FOIMinistryRequest.foiministryrequestid, + subquery_with_oipc.c.foiministryrequestversion_id == FOIMinistryRequest.version, + ] + #aliase for onbehalf of applicant info onbehalf_applicantmapping = aliased(FOIRequestApplicantMapping) onbehalf_applicant = aliased(FOIRequestApplicant) @@ -368,6 +387,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 @@ -484,7 +505,9 @@ 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'), + literal(None).label('oipc_number'), ] basequery = _session.query( @@ -555,8 +578,14 @@ def getrequestssubquery(cls, groups, filterfields, keyword, additionalfilter, us SubjectCode, SubjectCode.subjectcodeid == FOIMinistryRequestSubjectCode.subjectcodeid, isouter=True - ).filter(FOIMinistryRequest.requeststatuslabel != StateName.closed.name) - + ).join( + subquery_with_oipc, + and_( + *joincondition_oipc + ), + isouter=True + ).filter(or_(FOIMinistryRequest.requeststatuslabel != StateName.closed.name, and_(FOIMinistryRequest.isoipcreview == True, FOIMinistryRequest.requeststatusid == 3, subquery_with_oipc.c.outcomeid == None))) + if(additionalfilter == 'watchingRequests'): #watchby activefilter = and_(FOIMinistryRequest.isactive == True, FOIRequestStatus.isactive == True) @@ -661,7 +690,8 @@ def findfield(cls, x, iaoassignee, ministryassignee): 'ministry': func.upper(ProgramArea.bcgovcode), 'requestPageCount': FOIMinistryRequest.requestpagecount, 'closedate': FOIMinistryRequest.closedate, - 'subjectcode': SubjectCode.name + 'subjectcode': SubjectCode.name, + 'isoipcreview': FOIMinistryRequest.isoipcreview }.get(x, FOIMinistryRequest.axisrequestid) @classmethod @@ -705,7 +735,9 @@ def getgroupfilters(cls, groups): or_(*groupfilter) ) - return ministryfilter + ministryfilterwithclosedoipc = or_(ministryfilter, and_(FOIMinistryRequest.isoipcreview == True, FOIMinistryRequest.requeststatuslabel == StateName.closed.name)) + + return ministryfilterwithclosedoipc @classmethod def getrequestoriginalduedate(cls,ministryrequestid): @@ -784,6 +816,36 @@ def getupcomingdivisionduerecords(cls): finally: db.session.close() return upcomingduerecords + + @classmethod + def getupcomingoipcduerecords(cls): + upcomingduerecords = [] + try: + sql = """select axisrequestid, filenumber, fma.foiministryrequestid , fma.foiministryrequestversion, fma.foirequest_id, + frd.oipcid , frd.inquiryattributes ->> 'orderno'as orderno, + frd.inquiryattributes ->> 'inquirydate' as duedate, frd.created_at, frd.createdby + from "FOIRequestOIPC" frd + inner join (select distinct on (fpa.foiministryrequestid) foiministryrequestid, version as foiministryrequestversion, axisrequestid, filenumber, foirequest_id, requeststatusid + from "FOIMinistryRequests" fpa + order by fpa.foiministryrequestid , fpa.version desc) fma on frd.foiministryrequest_id = fma.foiministryrequestid + and frd.foiministryrequestversion_id = fma.foiministryrequestversion and fma.requeststatusid not in (5,6,4,11,3,15) + and inquiryattributes is not null + and frd.inquiryattributes ->> 'inquirydate' not in ('','null') + and (frd.inquiryattributes ->> 'inquirydate')::date between NOW() - INTERVAL '7 DAY' AND NOW() + INTERVAL '7 DAY' + and frd.outcomeid is null + order by frd.foiministryrequest_id , frd.foiministryrequestversion_id desc;""" + rs = db.session.execute(text(sql)) + for row in rs: + upcomingduerecords.append({"axisrequestid": row["axisrequestid"], "filenumber": row["filenumber"], + "foiministryrequestid": row["foiministryrequestid"], "version": row["foiministryrequestversion"], + "foirequest_id": row["foirequest_id"], "created_at": row["created_at"], "createdby": row["createdby"], + "orderno": row["orderno"],"duedate": row["duedate"]}) + except Exception as ex: + logging.error(ex) + raise ex + finally: + db.session.close() + return upcomingduerecords @classmethod def updateduedate(cls, ministryrequestid, duedate, userid)->DefaultMethodResult: @@ -1006,7 +1068,9 @@ 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'), + literal(None).label('oipc_number') ] basequery = _session.query( @@ -1078,6 +1142,7 @@ def getbasequery(cls, iaoassignee, ministryassignee, userid=None, requestby='IAO SubjectCode.subjectcodeid == FOIMinistryRequestSubjectCode.subjectcodeid, isouter=True ) + if(isiaorestrictedfilemanager == True or isministryrestrictedfilemanager == True): dbquery = basequery.filter(ministryfilter) @@ -1168,6 +1233,11 @@ def getfilterforadvancedsearch(cls, params, iaoassignee, ministryassignee): requesttypecondition = FOIMinistryRequest.getfilterforrequesttype(params, iaoassignee, ministryassignee) filtercondition.append(requesttypecondition) + #request flags: restricted, oipc, phased + if(len(params['requestflags']) > 0): + requestflagscondition = FOIMinistryRequest.getfilterforrequestflags(params, iaoassignee, ministryassignee) + filtercondition.append(requestflagscondition) + #public body: EDUC, etc. if(len(params['publicbody']) > 0): publicbodycondition = FOIMinistryRequest.getfilterforpublicbody(params, iaoassignee, ministryassignee) @@ -1214,6 +1284,26 @@ def getfilterforrequesttype(cls, params, iaoassignee, ministryassignee): requesttypecondition.append(FOIMinistryRequest.findfield('requestType', iaoassignee, ministryassignee) == type) return or_(*requesttypecondition) + @classmethod + def getfilterforrequestflags(cls, params, iaoassignee, ministryassignee): + #request flags: restricted, oipc, phased + requestflagscondition = [] + #alias for getting ministry restricted flag from FOIRestrictedMinistryRequest + ministry_restricted_requests = aliased(FOIRestrictedMinistryRequest) + + for flag in params['requestflags']: + if (flag.lower() == 'restricted'): + if(iaoassignee): + requestflagscondition.append(FOIRestrictedMinistryRequest.isrestricted == True) + elif (ministryassignee): + requestflagscondition.append(ministry_restricted_requests.isrestricted == True) + if (flag.lower() == 'oipc'): + requestflagscondition.append(FOIMinistryRequest.findfield('isoipcreview', iaoassignee, ministryassignee) == True) + if (flag.lower() == 'phased'): + # requestflagscondition.append(FOIMinistryRequest.findfield('isphasedrelease', iaoassignee, ministryassignee) == True) + continue + return or_(*requestflagscondition) + @classmethod def getfilterforpublicbody(cls, params, iaoassignee, ministryassignee): #public body: EDUC, etc. @@ -1249,6 +1339,14 @@ def getfilterforsearch(cls, params, iaoassignee, ministryassignee): searchcondition1.append(FOIMinistryRequest.findfield('assignedministrypersonFirstName', iaoassignee, ministryassignee).ilike('%'+keyword+'%')) searchcondition2.append(FOIMinistryRequest.findfield('assignedministrypersonLastName', iaoassignee, ministryassignee).ilike('%'+keyword+'%')) return or_(and_(*searchcondition1), and_(*searchcondition2)) + elif(params['search'] == 'oipc_number'): + searchcondition1 = [] + searchcondition2 = [] + for keyword in params['keywords']: + oipccondition = FOIRequestOIPC.getrequestidsbyoipcno(keyword) + searchcondition1.append(oipccondition.c.foiministryrequest_id == FOIMinistryRequest.foiministryrequestid) + searchcondition2.append(oipccondition.c.foiministryrequestversion_id == FOIMinistryRequest.version) + return and_(and_(*searchcondition1), and_(*searchcondition2)) else: searchcondition = [] for keyword in params['keywords']: @@ -1266,7 +1364,7 @@ def getaxisrequestidforrequest(cls,requestid, ministryrequestid): def getmetadata(cls,ministryrequestid): requestdetails = {} try: - sql = """select fmr.version, assignedto, fa.firstname, fa.lastname, pa.bcgovcode, fmr.programareaid, f.requesttype + sql = """select fmr.version, assignedto, fa.firstname, fa.lastname, pa.bcgovcode, fmr.programareaid, f.requesttype, fmr.isoipcreview from "FOIMinistryRequests" fmr join "FOIRequests" f on fmr.foirequest_id = f.foirequestid and fmr.foirequestversion_id = f."version" FULL OUTER JOIN "FOIAssignees" fa ON fa.username = fmr.assignedto INNER JOIN "ProgramAreas" pa ON pa.programareaid = fmr.programareaid @@ -1281,6 +1379,7 @@ def getmetadata(cls,ministryrequestid): requestdetails["version"] = row["version"] requestdetails["programareaid"] = row["programareaid"] requestdetails["requesttype"] = row["requesttype"] + requestdetails["isoipcreview"] = row["isoipcreview"] except Exception as ex: logging.error(ex) raise ex @@ -1312,5 +1411,5 @@ class Meta: 'foirequest.receivedmodeid','requeststatus.requeststatusid','requeststatuslabel','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 7a244d0c8..4114a7908 100644 --- a/request-management-api/request_api/models/FOIRawRequests.py +++ b/request-management-api/request_api/models/FOIRawRequests.py @@ -575,7 +575,9 @@ def getbasequery(cls, additionalfilter=None, userid=None, isiaorestrictedfileman literal(None).label('extensions'), isiaorestricted, literal(None).label('isministryrestricted'), - subjectcode + subjectcode, + literal(None).label('isoipcreview'), + literal(None).label('oipc_number') ] basequery = _session.query(*selectedcolumns).join(subquery_maxversion, and_(*joincondition)).join(FOIAssignee, FOIAssignee.username == FOIRawRequest.assignedto, isouter=True) @@ -822,6 +824,11 @@ def getfilterforadvancedsearch(cls, params): if(len(params['requesttype']) > 0): requesttypecondition = FOIRawRequest.getfilterforrequesttype(params) filtercondition.append(or_(*requesttypecondition)) + + #request flags: restricted, oipc, phased + if(len(params['requestflags']) > 0): + requestflagscondition = FOIRawRequest.getfilterforrequestflags(params) + filtercondition.append(or_(*requestflagscondition)) #public body: EDUC, etc. if(len(params['publicbody']) > 0): @@ -902,6 +909,20 @@ def getfilterforrequesttype(cls, params): return or_(*requesttypecondition) + @classmethod + def getfilterforrequestflags(cls, params): + # this search will be done by the ministry union, so returns filter with no results + requestflagscondition = [] + for flag in params['requestflags']: + if (flag.lower() == 'restricted'): # no results for raw restricted + requestflagscondition.append(FOIRawRequest.findfield('axisRequestId') == 'thisismeanttoreturnafilterconditionwith0results') + if (flag.lower() == 'oipc'): # no results for raw oipc + requestflagscondition.append(FOIRawRequest.findfield('axisRequestId') == 'thisismeanttoreturnafilterconditionwith0results') + if (flag.lower() == 'phased'): + # requestflagscondition.append(FOIMinistryRequest.findfield('isphasedrelease', iaoassignee, ministryassignee) == True) + continue + return requestflagscondition + @classmethod def getfilterforpublicbody(cls, params): #public body: EDUC, etc. 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..2d65c413d --- /dev/null +++ b/request-management-api/request_api/models/FOIRequestOIPC.py @@ -0,0 +1,63 @@ +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 + + + @classmethod + def getrequestidsbyoipcno(cls, oipcno): + return db.session.query( + FOIRequestOIPC.foiministryrequest_id, + FOIRequestOIPC.foiministryrequestversion_id + ).filter(FOIRequestOIPC.oipcno.ilike('%'+oipcno+'%')).group_by(FOIRequestOIPC.foiministryrequest_id, FOIRequestOIPC.foiministryrequestversion_id).subquery() + + +class FOIRequestOIPCSchema(ma.Schema): + class Meta: + fields = ('oipcid', 'version', 'foiministryrequest_id', 'investigator', 'foiministryrequestversion_id','oipcno','reviewtypeid','reasonid','statusid','outcomeid','isinquiry','inquiryattributes','isjudicialreview', + 'issubsequentappeal','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..32fe9334b --- /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('OIPCReviewTypes')) + relationship("OIPCReviewTypes", backref=backref("OIPCReviewTypes"), uselist=False) + reasonid = db.Column(db.Integer, ForeignKey('OIPCReasons')) + relationship("OIPCReasons", backref=backref("OIPCReasons"), 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/advancedsearch.py b/request-management-api/request_api/resources/advancedsearch.py index 61233e686..ca5cdc130 100644 --- a/request-management-api/request_api/resources/advancedsearch.py +++ b/request-management-api/request_api/resources/advancedsearch.py @@ -43,6 +43,7 @@ def get(): 'requeststate': flask.request.args.getlist('requestState[]'), 'requeststatus': flask.request.args.getlist('requestStatus[]'), 'requesttype': flask.request.args.getlist('requestType[]'), + 'requestflags': flask.request.args.getlist('requestFlags[]'), 'publicbody': flask.request.args.getlist('publicBodies[]'), 'daterangetype': flask.request.args.get('dateRangeType', None, type=str), diff --git a/request-management-api/request_api/resources/foiflowmasterdata.py b/request-management-api/request_api/resources/foiflowmasterdata.py index 6525eef82..c8fe5cbde 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/resources/foirequest.py b/request-management-api/request_api/resources/foirequest.py index 58cf30376..4d1a1daad 100644 --- a/request-management-api/request_api/resources/foirequest.py +++ b/request-management-api/request_api/resources/foirequest.py @@ -137,7 +137,7 @@ class FOIRequestsById(Resource): def post(foirequestid,foiministryrequestid): """ POST Method for capturing FOI requests before processing""" try: - request_json = request.get_json() + request_json = request.get_json() foirequestschema = FOIRequestWrapperSchema().load(request_json) result = requestservice().saverequestversion(foirequestschema, foirequestid, foiministryrequestid,AuthHelper.getuserid()) if result.success == True: @@ -290,5 +290,36 @@ def get(ministryrequestid,usertype=None): return FOIRequest.get(requestservice().getrequestid(ministryrequestid), ministryrequestid, usertype) except ValueError: return {'status': 500, 'message':"Invalid Request"}, 500 + except BusinessException as exception: + return {'status': exception.status_code, 'message':exception.message}, 500 + +@cors_preflight('POST, DELETE, UPDATE, OPTIONS') +@API.route('/foirequests//ministryrequest//section/') +class FOIRequestsById(Resource): + """Creates a new version of foi request for iao users adjusting speciifc sections of a FOI Request""" + @staticmethod + @TRACER.trace() + @cross_origin(origins=allowedorigins()) + @auth.require + def post(foirequestid,foiministryrequestid,section): + try: + request_json = request.get_json() + if (section == "oipc"): + foirequest = requestservice().getrequest(foirequestid, foiministryrequestid) + foirequest['isoipcreview'] = request_json['isoipcreview'] + foirequest['oipcdetails'] = request_json['oipcdetails'] + foirequestschema = FOIRequestWrapperSchema().load(foirequest) + result = requestservice().saverequestversion(foirequestschema, foirequestid, foiministryrequestid,AuthHelper.getuserid()) + if result.success == True: + asyncio.ensure_future(eventservice().postevent(foiministryrequestid,"ministryrequest",AuthHelper.getuserid(),AuthHelper.getusername(),AuthHelper.isministrymember())) + metadata = json.dumps({"id": result.identifier, "ministries": result.args[0]}) + requestservice().posteventtoworkflow(foiministryrequestid, foirequestschema, json.loads(metadata),"iao") + return {'status': result.success, 'message':result.message,'id':result.identifier, 'ministryRequests': result.args[0]} , 200 + else: + return {'status': False, 'message':EXCEPTION_MESSAGE_NOTFOUND_REQUEST,'id':foirequestid} , 404 + except ValidationError as err: + return {'status': False, 'message': str(err)}, 400 + except KeyError as error: + return {'status': False, 'message': CUSTOM_KEYERROR_MESSAGE + str(error)}, 400 except BusinessException as exception: return {'status': exception.status_code, 'message':exception.message}, 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 ce3ef32f6..99f5a462b 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 @@ -111,6 +139,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) @@ -121,6 +150,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/commons/duecalculator.py b/request-management-api/request_api/services/commons/duecalculator.py index d787bda94..c6ecd061e 100644 --- a/request-management-api/request_api/services/commons/duecalculator.py +++ b/request-management-api/request_api/services/commons/duecalculator.py @@ -20,7 +20,13 @@ def getpreviousbusinessday(self, cfrduedate,ca_holidays): return _prevbusinessday else: return self.getpreviousbusinessday(_prevbusinessday,ca_holidays) - + + def getpreviousbusinessday_by_n(self, duedate, ca_holidays, n): + _prevbusinessday = duedate + for i in range(n): + _prevbusinessday = self.getpreviousbusinessday(_prevbusinessday, ca_holidays) + return _prevbusinessday + def formatduedate(self,input): return datetimehandler().formatdate(input) diff --git a/request-management-api/request_api/services/dashboardservice.py b/request-management-api/request_api/services/dashboardservice.py index a1ba54f0e..96e41b1a9 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 = { @@ -162,7 +166,7 @@ def getministryrequestqueuepagination (self, groups=None, page=1, size=10, sorti return jsonify({'data': requestqueue, 'meta': meta}) - def advancedsearch(self, params={'usertype': 'iao', 'groups':None, 'page':1, 'size':10, 'sortingitems':[], 'sortingorders':[], 'requeststate':[], 'requeststatus':[], 'requesttype':[], 'publicbody':[], 'daterangetype':None, 'fromdate':None, 'todate':None, 'search':None, 'keywords':[], 'userid':None}): + def advancedsearch(self, params={'usertype': 'iao', 'groups':None, 'page':1, 'size':10, 'sortingitems':[], 'sortingorders':[], 'requeststate':[], 'requeststatus':[], 'requesttype':[], 'requestflags':[], 'publicbody':[], 'daterangetype':None, 'fromdate':None, 'todate':None, 'search':None, 'keywords':[], 'userid':None}): userid = AuthHelper.getuserid() if (params['usertype'] == "iao"): diff --git a/request-management-api/request_api/services/events/oipc.py b/request-management-api/request_api/services/events/oipc.py new file mode 100644 index 000000000..df3305dec --- /dev/null +++ b/request-management-api/request_api/services/events/oipc.py @@ -0,0 +1,166 @@ + +from os import stat +from re import VERBOSE +from request_api.models.FOIRequestOIPC import FOIRequestOIPC +from request_api.services.commentservice import commentservice +from request_api.services.oipcservice import oipcservice +from request_api.services.notificationservice import notificationservice +from request_api.models.FOIMinistryRequests import FOIMinistryRequest +import json +from request_api.models.default_method_result import DefaultMethodResult +from enum import Enum +from request_api.exceptions import BusinessException +from dateutil.parser import parse +from request_api.utils.enums import CommentType +from request_api.models.NotificationTypes import NotificationType + +class oipcevent: + """ FOI OIPC Event management service + """ + + def createoipcevent(self, requestid, requesttype, userid): + if requesttype != "ministryrequest": + return DefaultMethodResult(True,'No change',requestid) + ministryrequest = FOIMinistryRequest.getmetadata(requestid) + if ministryrequest["isoipcreview"] in (None, False): + notificationservice().dismissnotifications_by_requestid_type(requestid, "ministryrequest", self.__notificationtype()) + return DefaultMethodResult(True,'Dismiss OIPC events',requestid) + inquiryoutcomes = oipcservice().getinquiryoutcomes() + version = ministryrequest["version"] + curoipcs = FOIRequestOIPC.getoipc(requestid, version) + prevoipcs = FOIRequestOIPC.getoipc(requestid, version-1) + oipcsummary = self.__maintained(curoipcs, prevoipcs, inquiryoutcomes) + if oipcsummary is None or (oipcsummary and len(oipcsummary) <1): + return DefaultMethodResult(True,'No change',requestid) + try: + for oipc in oipcsummary: + self.__createcomment(requestid, oipc, userid) + self.__createnotification(requestid, oipc, userid) + return DefaultMethodResult(True,'Comment posted',requestid) + except BusinessException as exception: + return DefaultMethodResult(False,'unable to post comment - '+exception.message,requestid) + + + def __createcomment(self, requestid, oipc, userid): + comment = {"ministryrequestid": requestid, "comment": self.__preparemessage(oipc)} + commentservice().createministryrequestcomment(comment, userid, CommentType.SystemGenerated.value) + + def __createnotification(self, requestid, oipc, userid): + notificationtype = NotificationType().getnotificationtypeid(self.__notificationtype()) + return notificationservice().createnotification({"message" : self.__preparemessage(oipc)}, requestid, "ministryrequest", notificationtype, userid, False) + + + def __maintained(self,coipcs, poipcs, inquiryoutcomes): + oipcs = [] + for coipc in coipcs: + if self.__isoipcpresent(self.__getoipcnumber(coipc), poipcs) == False: + oipcs.append(self.__createoipcsummary(coipc, EventType.add.value, inquiryoutcomes)) + else: + if self.__isoutcomeclosed(coipc, poipcs) == True: + oipcs.append(self.__createoipcsummary(coipc, EventType.close.value, inquiryoutcomes)) + if self.__isinquirychanged(coipc, poipcs) == True: + oipcs.append(self.__createoipcsummary(coipc, EventType.inquirychange.value, inquiryoutcomes)) + elif self.__isinquiryoutcomechanged(coipc, poipcs, inquiryoutcomes) == True: + oipcs.append(self.__createoipcsummary(coipc, EventType.inquiryoutcome.value, inquiryoutcomes)) + return oipcs + + # def __deleted(self, coipcs, poipcs): + # oipcs = [] + # for poipc in poipcs: + # if self.__isoipcpresent(self.__getoipcnumber(poipc), coipcs) == False: + # oipcs.append(self.__createoipcsummary(poipc, EventType.delete.value)) + # return oipcs + + def __isoipcpresent(self, oipcno, poipcs): + for oipc in poipcs: + if self.__getoipcnumber(oipc) == oipcno: + return True + return False + + def __isoutcomeclosed(self, coipc, poipcs): + for oipc in poipcs: + if self.__getoipcnumber(oipc) == self.__getoipcnumber(coipc) and self.__getoutcome(oipc) != self.__getoutcome(coipc) and self.__getoutcome(coipc) == "Closed": + return True + return False + + def __isinquirychanged(self, coipc, poipcs): + for oipc in poipcs: + if self.__getoipcnumber(oipc) == self.__getoipcnumber(coipc) and self.__getinquiry(oipc) != self.__getinquiry(coipc): + if self.__getinquirycomplydate(coipc) not in (None, "") and self.__getinquiryorderno(coipc) not in (None, "") and (self.__getinquiryorderno(oipc) != self.__getinquiryorderno(coipc) or self.__getinquirycomplydate(oipc) != self.__getinquirycomplydate(coipc)): + return True + return False + + def __isinquiryoutcomechanged(self, coipc, poipcs, inquiryoutcomes): + for oipc in poipcs: + if self.__getoipcnumber(oipc) == self.__getoipcnumber(coipc) and self.__getinquiry(oipc) != self.__getinquiry(coipc): + if self.__getinquiryoutcome(coipc, inquiryoutcomes) not in (None, "") and self.__getinquiryoutcome(oipc, inquiryoutcomes) != self.__getinquiryoutcome(coipc, inquiryoutcomes): + return True + return False + + def __createoipcsummary(self, oipc, event, inquiryoutcomes): + return {'oipcno': self.__getoipcnumber(oipc), + 'reviewtype': self.__getoipcreviewtype(oipc), + 'reason':self.__getreason(oipc), + 'outcome': self.__getoutcome(oipc), + 'inquirycomplydate': self.__getinquirycomplydate(oipc), + 'inquiryorderno': self.__getinquirycomplydate(oipc), + 'inquiryoutcome': self.__getinquiryoutcome(oipc, inquiryoutcomes), + 'event': event} + + def __getoipcnumber(self, dataschema): + return dataschema['oipcno'] + + def __getoipcreviewtype(self, dataschema): + return dataschema['reviewtype.name'] + + def __getreason(self, dataschema): + return dataschema['reason.name'] + + def __getoutcome(self, dataschema): + return dataschema['outcome.name'] if dataschema['outcomeid'] not in (None,"") else None + + def __getinquirycomplydate(self, dataschema): + return self.__getinquiry(dataschema)['inquirydate'] if dataschema['inquiryattributes'] not in (None,"") else None + + def __getinquiryoutcome(self, dataschema, inquiryoutcomes): + if dataschema['inquiryattributes'] not in (None,""): + inquiryoutcomeid = self.__getinquiry(dataschema)['inquiryoutcome'] + if inquiryoutcomeid not in (None,""): + return self.__getinquiryoutcomename(inquiryoutcomeid, inquiryoutcomes) + return None + + def __getinquiryorderno(self, dataschema): + return self.__getinquiry(dataschema)['orderno'] if dataschema['inquiryattributes'] not in (None,"") else None + + def __getinquiry(self, dataschema): + return dataschema['inquiryattributes'] + + def __getinquiryoutcomename(self, inquiryoutcomeid, inquiryoutcomes): + for outcome in inquiryoutcomes: + if inquiryoutcomeid == outcome["inquiryoutcomeid"]: + return outcome["name"] + return None + + def __preparemessage(self, oipc): + if oipc['event'] == EventType.add.value: + return 'OIPC '+ oipc['reviewtype'] +' opened for '+ oipc['reason'] + elif oipc['event'] == EventType.close.value: + return 'OIPC '+ oipc['reviewtype'] +' closed for '+ oipc['reason'] + elif oipc['event'] == EventType.inquirychange.value: + _inquirychange_msg = 'OIPC Inquiry Order '+ oipc['inquiryorderno'] +' compliance date due '+oipc['inquirycomplydate'] + if oipc['inquiryoutcome'] not in (None, ""): + return _inquirychange_msg+' .Inquiry Decision:' + oipc['inquiryoutcome'] + else: + return _inquirychange_msg + elif oipc['event'] == EventType.inquiryoutcome.value: + return 'OIPC '+ oipc['reviewtype'] +' Inquiry Decision: '+ oipc['inquiryoutcome'] + + def __notificationtype(self): + return "OIPC" + +class EventType(Enum): + add = "add" + delete = "delete" + close = "close" + inquirychange = "inquirychange" + inquiryoutcome = "inquiryoutcome" diff --git a/request-management-api/request_api/services/events/oipcduedate.py b/request-management-api/request_api/services/events/oipcduedate.py new file mode 100644 index 000000000..a8db3021a --- /dev/null +++ b/request-management-api/request_api/services/events/oipcduedate.py @@ -0,0 +1,72 @@ + +from os import stat +from re import VERBOSE +from request_api.services.commentservice import commentservice +from request_api.services.commons.duecalculator import duecalculator +from request_api.services.notificationservice import notificationservice +from request_api.models.FOIMinistryRequests import FOIMinistryRequest +from request_api.models.NotificationTypes import NotificationType +import json +from request_api.models.default_method_result import DefaultMethodResult +from request_api.exceptions import BusinessException +from dateutil.parser import parse +from flask import current_app + +class oipcduedateevent(duecalculator): + """ FOI OIPC Due Date Event management service + """ + + + def createdueevent(self): + try: + _today = self.gettoday() + notificationservice().dismissremindernotification("ministryrequest", self.__notificationtype()) + ca_holidays = self.getholidays() + _upcomingdues = FOIMinistryRequest.getupcomingoipcduerecords() + for entry in _upcomingdues: + _duedate = self.formatduedate(entry['duedate']) + message = None + if _duedate == _today: + message = self.__todayduemessage(entry) + elif self.getpreviousbusinessday(entry['duedate'],ca_holidays) == _today: + message = self.__upcomingduemessage(entry) + elif self.getpreviousbusinessday_by_n(entry['duedate'],ca_holidays, 2) == _today: + message = self.__upcomingduemessage(entry) + self.__createnotification(message,entry['foiministryrequestid']) + self.__createcomment(entry, message) + return DefaultMethodResult(True,'OIPC reminder notifications created',_today) + except BusinessException as exception: + current_app.logger.error("%s,%s" % ('OIPC reminder Notification Error', exception.message)) + return DefaultMethodResult(False,'OIPC reminder notifications failed',_today) + + def __createnotification(self, message, requestid): + if message is not None: + notificationtype = NotificationType().getnotificationtypeid(self.__notificationtype()) + return notificationservice().createnotification({"message" : message}, requestid, "ministryrequest", notificationtype, self.__defaultuserid(), False) + + def __createcomment(self, entry, message): + if message is not None: + _comment = self.__preparecomment(entry, message) + return commentservice().createcomments(_comment, self.__defaultuserid(), 2) + + + def __preparecomment(self, foirequest, message): + _comment = dict() + _comment['comment'] = message + _comment['ministryrequestid'] = foirequest["foiministryrequestid"] + _comment['version'] = foirequest["version"] + _comment['taggedusers'] = None + _comment['parentcommentid'] = None + return _comment + + def __upcomingduemessage(self, data): + return 'OIPC Inquiry Order '+data['orderno'] +' compliance date due on ' + parse(str(data['duedate'])).strftime("%Y %b %d").upper() + + def __todayduemessage(self, data): + return 'OIPC Inquiry Order '+data['orderno'] +' compliance due Today' + + def __notificationtype(self): + return "OIPC Due Reminder" + + def __defaultuserid(self): + return "System" \ No newline at end of file diff --git a/request-management-api/request_api/services/eventservice.py b/request-management-api/request_api/services/eventservice.py index 013977901..96ba48dcc 100644 --- a/request-management-api/request_api/services/eventservice.py +++ b/request-management-api/request_api/services/eventservice.py @@ -3,12 +3,14 @@ from re import VERBOSE from request_api.services.events.state import stateevent from request_api.services.events.division import divisionevent +from request_api.services.events.oipc import oipcevent from request_api.services.events.assignment import assignmentevent from request_api.services.events.cfrdate import cfrdateevent from request_api.services.events.comment import commentevent from request_api.services.events.watcher import watcherevent from request_api.services.events.legislativedate import legislativedateevent from request_api.services.events.divisiondate import divisiondateevent +from request_api.services.events.oipcduedate import oipcduedateevent from request_api.services.events.extension import extensionevent from request_api.services.events.cfrfeeform import cfrfeeformevent from request_api.services.events.payment import paymentevent @@ -34,8 +36,9 @@ def posteventsync(self, requestid, requesttype, userid, username, isministryuser stateeventresponse = stateevent().createstatetransitionevent(requestid, requesttype, userid, username) divisioneventresponse = divisionevent().createdivisionevent(requestid, requesttype, userid) assignmentresponse = assignmentevent().createassignmentevent(requestid, requesttype, userid, isministryuser,assigneename,username) - if stateeventresponse.success == False or divisioneventresponse.success == False or assignmentresponse.success == False: - current_app.logger.error("FOI Notification failed for event for request= %s ; state response=%s ; division response=%s ; assignment response=%s" % (requestid, stateeventresponse.message, divisioneventresponse.message, assignmentresponse.message)) + oipcresponse = oipcevent().createoipcevent(requestid, requesttype, userid) + if stateeventresponse.success == False or divisioneventresponse.success == False or assignmentresponse.success == False or oipcresponse.success == False: + current_app.logger.error("FOI Notification failed for event for request= %s ; state response=%s ; division response=%s ; assignment response=%s ; oipc response=%s" % (requestid, stateeventresponse.message, divisioneventresponse.message, assignmentresponse.message, oipcresponse.message)) except BusinessException as exception: self.__logbusinessexception(exception) @@ -58,13 +61,14 @@ def posteventforaxisextension(self, ministryrequestid, extensionids, userid, use def postreminderevent(self): try: - cfreventresponse = cfrdateevent().createdueevent() - legislativeeventresponse = legislativedateevent().createdueevent() - divisioneventresponse = divisiondateevent().createdueevent() + cfreventresponse = cfrdateevent().createdueevent() + legislativeeventresponse = legislativedateevent().createdueevent() + divisioneventresponse = divisiondateevent().createdueevent() + oipceventresponse = oipcduedateevent().createdueevent() paymentremindereventresponse = paymentevent().createpaymentreminderevent() - section5pendingresponse = section5pendingevent().createdueevent() - if cfreventresponse.success == False or legislativeeventresponse.success == False or divisioneventresponse.success == False or paymentremindereventresponse.success == False or section5pendingresponse == False: - current_app.logger.error("FOI Notification failed for reminder event response=%s ; legislative response=%s ; division response=%s ; payment response=%s ; section5pending response=%s" % (cfreventresponse.message, legislativeeventresponse.message, divisioneventresponse.message, paymentremindereventresponse.message, section5pendingresponse.message)) + section5pendingresponse = section5pendingevent().createdueevent() + if cfreventresponse.success == False or legislativeeventresponse.success == False or divisioneventresponse.success == False or paymentremindereventresponse.success == False or section5pendingresponse == False or oipceventresponse == False: + current_app.logger.error("FOI Notification failed for reminder event response=%s ; legislative response=%s ; division response=%s ; payment response=%s ; section5pending response=%s ; oipcduereminder response=%s" % (cfreventresponse.message, legislativeeventresponse.message, divisioneventresponse.message, paymentremindereventresponse.message, section5pendingresponse.message, oipceventresponse.message)) return DefaultMethodResult(False,'Due reminder notifications failed',cfreventresponse.identifier) return DefaultMethodResult(True,'Due reminder notifications created',cfreventresponse.identifier) except BusinessException as exception: diff --git a/request-management-api/request_api/services/foirequest/requestservicebuilder.py b/request-management-api/request_api/services/foirequest/requestservicebuilder.py index d3f5a4d8b..920b6677d 100644 --- a/request-management-api/request_api/services/foirequest/requestservicebuilder.py +++ b/request-management-api/request_api/services/foirequest/requestservicebuilder.py @@ -9,6 +9,8 @@ from request_api.models.FOIRequestApplicantMappings import FOIRequestApplicantMapping from request_api.models.FOIRequestTeams import FOIRequestTeam from request_api.models.FOIRequestStatus import FOIRequestStatus +from request_api.models.FOIRequestOIPC import FOIRequestOIPC + from datetime import datetime as datetime2 from request_api.utils.enums import MinistryTeamWithKeycloackGroup, StateName from request_api.services.foirequest.requestserviceconfigurator import requestserviceconfigurator @@ -36,6 +38,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 = "" @@ -139,6 +145,37 @@ 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 + if oipc["isinquiry"] == True: + oipcreview.inquiryattributes = self.__formatoipcattributes(oipc["inquiryattributes"]) + oipcreview.createdby=userid + oipcreview.created_at= datetime2.now().isoformat() + oipcarr.append(oipcreview) + return oipcarr + + def __formatoipcattributes(self, inquiryattributes): + if inquiryattributes not in (None, "") and inquiryattributes["inquirydate"] in ("","null"): + inquiryattributes["inquirydate"] = None + return inquiryattributes + 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 5e8e95818..635007d45 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 @@ -166,6 +168,9 @@ 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'] if (requestministry['isoipcreview'] not in (None, '') and requestministry['isoipcreview'] in (True, False)) else False, + 'isreopened': self.hasreopened(foiministryrequestid), + 'oipcdetails': self.getoipcdetails(foiministryrequestid, requestministry['version']), 'onholdTransitionDate': self.getonholdtransition(foiministryrequestid), 'stateTransition': FOIMinistryRequest.getstatesummary(foiministryrequestid), 'assignedToFirstName': requestministry["assignee.firstname"] if requestministry["assignedto"] != None else None, @@ -209,6 +214,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 @@ -228,6 +270,14 @@ def getministryrequest(self, foiministryrequestid): def __genericdateformat(self): return '%Y-%m-%d' + def hasreopened(self, requestid): + states = FOIMinistryRequest.getstatesummary(requestid) + if len(states) > 0: + current_state = states[0] + if current_state != "Closed" and any(state['status'] == "Closed" for state in states): + return True + return False + def __prepareapplicant(self,firstname= None, middlename= None, lastname= None, businessname= None): return { 'firstName': firstname, diff --git a/request-management-api/request_api/services/foirequest/requestserviceministrybuilder.py b/request-management-api/request_api/services/foirequest/requestserviceministrybuilder.py index 56db80d2d..4f8257fd6 100644 --- a/request-management-api/request_api/services/foirequest/requestserviceministrybuilder.py +++ b/request-management-api/request_api/services/foirequest/requestserviceministrybuilder.py @@ -12,6 +12,7 @@ from request_api.models.FOIAssignees import FOIAssignee from request_api.models.FOIMinistryRequestSubjectCodes import FOIMinistryRequestSubjectCode from request_api.models.FOIRequestStatus import FOIRequestStatus +from request_api.models.FOIRequestOIPC import FOIRequestOIPC from request_api.services.foirequest.requestserviceconfigurator import requestserviceconfigurator from datetime import datetime as datetime2 @@ -88,6 +89,8 @@ def createfoiministryrequestfromobject(self, ministryschema, requestschema, user foiministryrequest.subjectcode = self.__createministrysubjectcode(requestschema, ministryschema["foiministryrequestid"], ministryschema["version"], userid) foiministryrequest.closedate = requestdict['closedate'] foiministryrequest.closereasonid = requestdict['closereasonid'] + foiministryrequest.isoipcreview = ministryschema["isoipcreview"] + foiministryrequest.oipcreviews = self.createfoirequestoipcs(foiministryrequest.isoipcreview, ministryschema["foirequest_id"], ministryschema["version"]) return foiministryrequest def __getrequeststatusid(self, requeststatuslabel): @@ -321,3 +324,30 @@ def createfoirequestdivision(self, requestschema, requestid, version, userid): def createfoiassigneefromobject(self, username, firstname, middlename, lastname): return FOIAssignee.saveassignee(username, firstname, middlename, lastname) + + def createfoirequestoipcs(self, isoipcreview, requestid, version): + current_oipcs = FOIRequestOIPC.getoipc(requestid, version) + if (isoipcreview == True): + updated_oipcs = [] + for oipc in current_oipcs: + oipcreview = FOIRequestOIPC() + oipcreview.foiministryrequest_id = requestid + oipcreview.foiministryrequestversion_id = version + 1 + oipcreview.oipcno = oipc["oipcno"] + oipcreview.reviewtypeid = oipc["reviewtypeid"] + oipcreview.reasonid = oipc["reasonid"] + oipcreview.statusid = oipc["statusid"] + oipcreview.outcomeid = oipc["outcomeid"] + oipcreview.investigator = oipc["investigator"] + oipcreview.isinquiry = oipc["isinquiry"] + oipcreview.isjudicialreview = oipc["isjudicialreview"] + oipcreview.issubsequentappeal = oipc["issubsequentappeal"] + oipcreview.issubsequentappeal = oipc["issubsequentappeal"] + oipcreview.receiveddate = oipc["receiveddate"] + oipcreview.closeddate = oipc["closeddate"] + oipcreview.inquiryattributes = oipc["inquiryattributes"] + oipcreview.createdby=oipc["createdby"] + oipcreview.created_at= oipc["created_at"] + updated_oipcs.append(oipcreview) + return updated_oipcs + return [] diff --git a/request-management-api/request_api/services/notificationservice.py b/request-management-api/request_api/services/notificationservice.py index 1e2c91a13..89f8643a9 100644 --- a/request-management-api/request_api/services/notificationservice.py +++ b/request-management-api/request_api/services/notificationservice.py @@ -117,6 +117,17 @@ def dismissnotifications_by_requestid_type_userid(self, requestid, requesttype, _ids = FOIRawRequestNotification.getnotificationidsbynumberandtype('U-00' + str(foirequest['requestid']), notificationtypelabels[0]) self.__deletenotificationbyuserandid(requesttype, _ids, userid) + def dismissnotifications_by_requestid_type(self, requestid, requesttype, notificationtype): + notificationtypelabels = self.__getcleanupnotificationids(notificationtype) + foirequest = self.getrequest(requestid, requesttype) + if requesttype == "ministryrequest": + idnumber = foirequest["filenumber"] + _ids = FOIRequestNotification.getnotificationidsbynumberandtype(idnumber, notificationtypelabels) + else: + _ids = FOIRawRequestNotification.getnotificationidsbynumberandtype('U-00' + str(foirequest['requestid']), notificationtypelabels[0]) + self.__deletenotificationids(requesttype, _ids) + + def __createnotification(self, message, requestid, requesttype, notificationtype, userid, foirequest, requestjson=None): notification = self.__preparenotification(message, requesttype, notificationtype, userid, foirequest, requestjson) if notification is not 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 diff --git a/request-management-api/request_api/services/recordservice.py b/request-management-api/request_api/services/recordservice.py index 45a11662a..7ec6df6c3 100644 --- a/request-management-api/request_api/services/recordservice.py +++ b/request-management-api/request_api/services/recordservice.py @@ -181,6 +181,9 @@ def triggerpdfstitchservice(self, requestid, ministryrequestid, recordschema, us def getpdfstitchpackagetodownload(self, ministryid, category): response, err = self.makedocreviewerrequest('GET', '/api/pdfstitch/{0}/{1}'.format(ministryid, category)) + if response is not None and "createdat" in response: + string_datetime = maya.parse(response["createdat"]).datetime(to_timezone='America/Vancouver', naive=False).strftime('%Y %b %d | %I:%M %p').upper() + response["createdat_datetime"] = string_datetime return response def getpdfstichstatus(self, ministryid, category): diff --git a/request-management-api/request_api/services/requestservice.py b/request-management-api/request_api/services/requestservice.py index 445ec9914..547ac5bbf 100644 --- a/request-management-api/request_api/services/requestservice.py +++ b/request-management-api/request_api/services/requestservice.py @@ -252,63 +252,35 @@ def postcorrespondenceeventtoworkflow( attributes, ) - def calculateduedate(self, ministryrequestid, foirequest, paymentdate): + def calculateduedate(self, ministryrequestid, foirequest, paymentdate): duedate_includeoffhold, cfrduedate_includeoffhold = self.__isincludeoffhold() - onhold_extend_days = duecalculator().getbusinessdaysbetween( - foirequest["onholdTransitionDate"], paymentdate - ) + onhold_extend_days = duecalculator().getbusinessdaysbetween(foirequest["onholdTransitionDate"], paymentdate) isoffhold_businessday = duecalculator().isbusinessday(paymentdate) - duedate_extend_days = ( - onhold_extend_days + 1 - if isoffhold_businessday == True and duedate_includeoffhold == True - else onhold_extend_days - ) - cfrduedate_extend_days = ( - onhold_extend_days + 1 - if isoffhold_businessday == True and cfrduedate_includeoffhold == True - else onhold_extend_days - ) - calc_duedate = duecalculator().addbusinessdays( - foirequest["dueDate"], duedate_extend_days - ) - calc_cfrduedate = duecalculator().addbusinessdays( - foirequest["cfrDueDate"], cfrduedate_extend_days - ) + duedate_extend_days = onhold_extend_days + 1 if isoffhold_businessday == True and duedate_includeoffhold == True else onhold_extend_days + cfrduedate_extend_days = onhold_extend_days + 1 if isoffhold_businessday == True and cfrduedate_includeoffhold == True else onhold_extend_days + calc_duedate = duecalculator().addbusinessdays(foirequest["dueDate"], duedate_extend_days) + calc_cfrduedate = duecalculator().addbusinessdays(foirequest["cfrDueDate"], cfrduedate_extend_days) return calc_duedate, calc_cfrduedate - def __skipduedatecalculation( - self, ministryrequestid, offholddate, currentstatus="", nextstatename="" - ): + def __skipduedatecalculation(self, ministryrequestid, offholddate): previousoffholddate = FOIMinistryRequest.getlastoffholddate(ministryrequestid) - if ( - currentstatus not in (None, "") - and currentstatus == StateName.onhold.value - and nextstatename not in (None, "") - and currentstatus == nextstatename - ): - return True - if previousoffholddate not in (None, ""): - previouspaymentdate_pst = datetimehandler().convert_to_pst( - previousoffholddate - ) - if ( - datetimehandler().getdate(previouspaymentdate_pst).date() - == datetimehandler().getdate(offholddate).date() - ): + foiministry_request = FOIMinistryRequest.getrequest(ministryrequestid) + request_reopened = self.__hasreopened(ministryrequestid, "ministryrequest") + if previousoffholddate not in (None, ''): + previouspaymentdate_pst = datetimehandler().convert_to_pst(previousoffholddate) + if datetimehandler().getdate(previouspaymentdate_pst).date() == datetimehandler().getdate(offholddate).date(): return True - return False + if foiministry_request['isoipcreview'] == True and request_reopened: + return True + return False def __isincludeoffhold(self): - payment_config_str = os.getenv("PAYMENT_CONFIG", "") - if payment_config_str in (None, ""): + payment_config_str = os.getenv("PAYMENT_CONFIG",'') + if payment_config_str in (None, ''): return True, True _paymentconfig = json.loads(payment_config_str) - duedate_includeoffhold = ( - True if _paymentconfig["duedate"]["includeoffhold"] == "Y" else False - ) - cfrduedate_includeoffhold = ( - True if _paymentconfig["cfrduedate"]["includeoffhold"] == "Y" else False - ) + duedate_includeoffhold = True if _paymentconfig["duedate"]["includeoffhold"] == "Y" else False + cfrduedate_includeoffhold = True if _paymentconfig["cfrduedate"]["includeoffhold"] == "Y" else False return duedate_includeoffhold, cfrduedate_includeoffhold # intake in progress to open: create a restricted request record for each selected ministries @@ -330,3 +302,15 @@ def saverestrictedrequest(self, ministryrequestid, type, isrestricted, userid): return FOIRestrictedMinistryRequest.saverestrictedrequest( ministryrequestid, type, isrestricted, version, userid ) + + + def __hasreopened(self, requestid, requesttype): + if requesttype == "rawrequest": + states = FOIRawRequest.getstatesummary(requestid) + else: + states = FOIMinistryRequest.getstatesummary(requestid) + if len(states) > 0: + current_state = states[0] + if current_state != "Closed" and any(state['status'] == "Closed" for state in states): + return True + return False