diff --git a/backend/src/components/ministrySDCReports.js b/backend/src/components/ministrySDCReports.js index 4caeae3e..ab81a3aa 100644 --- a/backend/src/components/ministrySDCReports.js +++ b/backend/src/components/ministrySDCReports.js @@ -3,7 +3,7 @@ const { logApiError, errorResponse, getData } = require('./utils'); const config = require('../config/index'); const HttpStatus = require('http-status-codes'); let reportTypes = ['indy-inclusive-ed-enrollment-headcounts', 'school-enrollment-headcounts', 'indy-school-enrollment-headcounts', 'school-address-report', 'fsa-registration-report', - 'offshore-enrollment-headcounts', 'offshore-languages-headcounts']; + 'offshore-enrollment-headcounts', 'offshore-languages-headcounts', 'indy-inclusive-ed-funding-headcounts', 'inclusive-education-variance-headcounts']; async function getMinistrySDCReport(req, res) { try { @@ -49,6 +49,7 @@ function getFileDetails(reportType) { 'fsa-registration-report': { filename: 'FsaRegistrationReport.csv', contentType: 'text/csv' }, 'offshore-enrollment-headcounts': { filename: 'OffshoreSchoolsHeadcounts.csv', contentType: 'text/csv' }, 'offshore-languages-headcounts': { filename: 'OffshoreSpokenLanguageHeadcounts.csv', contentType: 'text/csv' }, + 'indy-inclusive-ed-funding-headcounts': { filename: 'IndependentSchoolsInclusiveEdFundingHeadcounts.csv', contentType: 'text/csv' }, 'DEFAULT': { filename: 'download.pdf', contentType: 'application/pdf' } }; return mappings[reportType] || mappings['DEFAULT']; diff --git a/backend/src/components/sdc/sdc.js b/backend/src/components/sdc/sdc.js index 79c5435a..401e43d4 100644 --- a/backend/src/components/sdc/sdc.js +++ b/backend/src/components/sdc/sdc.js @@ -68,7 +68,7 @@ async function getIndySdcSchoolCollectionMonitoringByCollectionId(req, res) { const data = await getData(`${config.get('sdc:collectionURL')}/${req.params.collectionID}/monitorIndySdcSchoolCollections`); return res.status(HttpStatus.OK).json(data); } catch (e) { - await logApiError(e, 'Error retrieving the district collection monitoring stats'); + await logApiError(e, 'Error retrieving the indy school collection monitoring stats'); return errorResponse(res); } } @@ -888,6 +888,7 @@ async function downloadSdcReport(req, res) { } } + async function moveSld (req, res) { try { await postData(`${config.get('sdc:schoolCollectionStudentURL')}/move-sld`, req.body); @@ -896,6 +897,20 @@ async function moveSld (req, res) { logApiError(e, 'Error attempting to move sld record(s)'); return errorResponse(res); } + +async function getDistrictHeadcounts(req, res) { + try { + const params = { + params: { + type: req.query.type, + } + }; + let headCounts = await getData(`${config.get('sdc:rootURL')}/headcounts/${req.params.sdcDistrictCollectionID}`, params); + return res.status(HttpStatus.OK).json(headCounts); + } catch (e) { + await logApiError(e, 'getDistrictHeadcounts', 'Error getting District headcount.'); + return errorResponse(res); + } } function getFileDetails(reportType) { @@ -941,4 +956,5 @@ module.exports = { downloadSdcReport, updateBandCode, moveSld + getDistrictHeadcounts }; diff --git a/backend/src/components/studentFilters.js b/backend/src/components/studentFilters.js index 573f4cda..983b520b 100644 --- a/backend/src/components/studentFilters.js +++ b/backend/src/components/studentFilters.js @@ -745,7 +745,7 @@ function createNameCriteria(key, nameString) { operation: FILTER_OPERATION.CONTAINS_IGNORE_CASE, value: `%${name}%`, valueType: VALUE_TYPE.STRING, - condition: CONDITION.OR + condition: CONDITION.AND }); }); } else { diff --git a/backend/src/routes/sdc.js b/backend/src/routes/sdc.js index 731ce067..9d2c5560 100644 --- a/backend/src/routes/sdc.js +++ b/backend/src/routes/sdc.js @@ -8,7 +8,7 @@ const { getSnapshotFundingDataForSchool, getAllCollectionsForSchool, getActiveCo getIndySdcSchoolCollectionMonitoringByCollectionId, unsubmitSdcDistrictCollection, unsubmitSdcSchoolCollection, getInDistrictDuplicates, getSDCSchoolCollectionStudentPaginatedSlice, getSDCSchoolCollectionStudentPaginated, getSDCSchoolCollectionStudentDetail, updateStudentPEN, checkDuplicatesInCollection, updateAndValidateSdcSchoolCollectionStudent, resolveDuplicates, postProvincialDuplicates, resolveRemainingDuplicates, getInFlightDistrictProvincialDuplicates, getInFlightSchoolProvincialDuplicates, closeCollection, getCollectionPaginated, getSDCSchoolCollectionDetail, downloadSdcReport, - getCollectionByID, getSdcSchoolCollections, getSdcDistrictCollections, updateBandCode, moveSld + getCollectionByID, getSdcSchoolCollections, getSdcDistrictCollections, updateBandCode, moveSld, getDistrictHeadcounts } = require('../components/sdc/sdc'); const {getCachedSDCData} = require('../components/sdc/sdc-cache'); const constants = require('../util/constants'); @@ -71,6 +71,7 @@ router.post('/move-sld', passport.authenticate('jwt', {session: false}, undefine router.post('/sdcDistrictCollection/:sdcDistrictCollectionID/unsubmit', passport.authenticate('jwt', {session: false}, undefined), permUtils.checkUserHasPermission(PERMISSION.STUDENT_DATA_COLLECTION), extendSession, permUtils.isValidUUIDParam('sdcDistrictCollectionID'), unsubmitSdcDistrictCollection); router.post('/sdcDistrictCollection/resolve-district-duplicates/:sdcDuplicateID/:type', passport.authenticate('jwt', {session: false}, undefined), permUtils.checkUserHasPermission(PERMISSION.STUDENT_DATA_COLLECTION), extendSession, permUtils.isValidUUIDParam('sdcDuplicateID'), resolveDuplicates); router.get('/sdcDistrictCollection/:sdcDistrictCollectionID/report/:reportTypeCode/download', auth.refreshJWT, permUtils.checkUserHasPermission(PERMISSION.STUDENT_DATA_COLLECTION), extendSession, permUtils.isValidUUIDParam('sdcDistrictCollectionID'), downloadSdcReport); +router.get('/sdcSchoolCollectionStudent/getDistrictHeadcounts/:sdcDistrictCollectionID', passport.authenticate('jwt', {session: false}, undefined), permUtils.checkUserHasPermission(PERMISSION.STUDENT_DATA_COLLECTION), extendSession, permUtils.isValidUUIDParam('sdcDistrictCollectionID'), getDistrictHeadcounts); //school collection router.get('/sdcSchoolCollection/:sdcSchoolCollectionID', passport.authenticate('jwt', {session: false}, undefined), permUtils.checkUserHasPermission(PERMISSION.STUDENT_DATA_COLLECTION), extendSession, permUtils.isValidUUIDParam('sdcSchoolCollectionID'), getSDCSchoolCollectionDetail); diff --git a/frontend/src/components/data-collection/DuplicatesPosting.vue b/frontend/src/components/data-collection/DuplicatesPosting.vue index 958d3e1d..aea6b512 100644 --- a/frontend/src/components/data-collection/DuplicatesPosting.vue +++ b/frontend/src/components/data-collection/DuplicatesPosting.vue @@ -26,7 +26,7 @@ color="primary" text="Post Province Duplicates" class="ma-2" - :disabled="isPostProvincialDuplicatesButtonDisabled || isCloseCollectionButtonDisabled" + :disabled="isPostProvincialDuplicatesButtonDisabled" @click="postProvincialDuplicates" /> @@ -201,33 +201,22 @@ export default { submissionDueDate: null, duplicationResolutionDueDate: null, signoffDueDate: null - } + }, + monitorSdcDistrictCollectionsResponse: [], + monitorSdcSchoolCollectionsResponse: [], + isPostProvincialDuplicatesButtonDisabled: false, + isCloseCollectionButtonDisabled: false }; }, computed: { - ...mapState(sdcCollectionStore, ['districtCollectionStatusCodesMap']), - ...mapState(sdcCollectionStore, ['schoolCollectionStatusCodesMap']), - ...mapState(sdcCollectionStore, ['duplicateResolutionCodesMap']), - ...mapState(sdcCollectionStore, ['collectionTypeCodesMap']), - isPostProvincialDuplicatesButtonDisabled() { - const allDistrictsSubmitted = Array.from(this.districtCollectionStatusCodesMap.values()).every(value => value.sdcDistrictCollectionStatusCode === 'SUBMITTED'); - const allIndieSchoolsSubmitted = Array.from(this.schoolCollectionStatusCodesMap.values()).every(value => value.sdcSchoolCollectionStatusCode === 'SUBMITTED'); - const allPenFixesResolved = this.totalPenFixElements === 0; - - return !allDistrictsSubmitted || !allIndieSchoolsSubmitted || !allPenFixesResolved; - }, + ...mapState(sdcCollectionStore, ['duplicateResolutionCodesMap', 'collectionTypeCodesMap']), isResolveRemainingDuplicatesButtonDisabled() { return this.nonAllowableDuplicates?.length === 0 && this.nonAllowableProgramDuplicates?.length === 0; }, - isCloseCollectionButtonDisabled() { - const allDistrictsCompleted = Array.from(this.districtCollectionStatusCodesMap.values()).every(value => value.sdcDistrictCollectionStatusCode === 'COMPLETED'); - const allIndieSchoolsCompleted = Array.from(this.schoolCollectionStatusCodesMap.values()).every(value => value.sdcSchoolCollectionStatusCode === 'COMPLETED'); - return !allDistrictsCompleted || !allIndieSchoolsCompleted; - } }, async created() { - await sdcCollectionStore().getDistrictCollectionStatusCodeMap(); - await sdcCollectionStore().getSchoolCollectionStatusCodeMap(); + await this.getSdcSchoolCollections(); + await this.getSdcDistrictCollectionMonitoring(); await sdcCollectionStore().getDuplicateResolutionCodesMap(); sdcCollectionStore().getCodes().then(() => { this.loadStudents(); @@ -236,8 +225,48 @@ export default { sdcCollectionStore().getCollectionTypeCodesMap().finally(() => { this.getActiveCollection(); }); + + this.checkIsPostProvincialDuplicatesButtonDisabled(); + this.checkIsCloseCollectionButtonDisabled(); }, methods: { + async getSdcSchoolCollections(){ + this.isLoading = true; + await ApiService.apiAxios.get(`${Routes.sdc.BASE_URL}/collection/${this.collectionObject.collectionID}/indySdcSchoolCollectionMonitoring`, { + }).then(response => { + this.monitorSdcSchoolCollectionsResponse = response?.data.monitorSdcSchoolCollections; + }).catch(error => { + console.error(error); + this.setFailureAlert(error?.response?.data?.message ? error?.response?.data?.message : 'An error occurred while trying to get indy school collections. Please try again later.'); + }).finally(() => { + this.isLoading = false; + }); + }, + async getSdcDistrictCollectionMonitoring() { + this.isLoading = true; + await ApiService.apiAxios.get(`${Routes.sdc.BASE_URL}/collection/${this.collectionObject.collectionID}/sdcDistrictCollectionMonitoring`, { + }).then(response => { + this.monitorSdcDistrictCollectionsResponse = response?.data; + }).catch(error => { + console.error(error); + this.setFailureAlert(error?.response?.data?.message ? error?.response?.data?.message : 'An error occurred while trying to get sdc district collections. Please try again later.'); + }).finally(() => { + this.isLoading = false; + }); + }, + checkIsPostProvincialDuplicatesButtonDisabled() { + const districtsNotSubmittedCount = this.monitorSdcDistrictCollectionsResponse.filter(response => response.sdcDistrictCollectionStatusCode !== 'SUBMITTED').length; + const indieSchoolsNotSubmittedCount = this.monitorSdcSchoolCollectionsResponse.filter(response => response.schoolStatus !== 'SUBMITTED').length; + const allPenFixesResolved = this.totalPenFixElements === 0; + + this.isPostProvincialDuplicatesButtonDisabled = districtsNotSubmittedCount > 0 || indieSchoolsNotSubmittedCount > 0 || !allPenFixesResolved; + }, + checkIsCloseCollectionButtonDisabled() { + const districtsNotCompletedCount = this.monitorSdcDistrictCollectionsResponse.filter(response => response.sdcDistrictCollectionStatusCode !== 'COMPLETED').length; + const indieSchoolsNotCompletedCount = this.monitorSdcSchoolCollectionsResponse.filter(response => response.schoolStatus !== 'COMPLETED').length; + + this.isCloseCollectionButtonDisabled = districtsNotCompletedCount > 0 || indieSchoolsNotCompletedCount > 0; + }, getProvincialDuplicates(){ this.isLoading = true; ApiService.apiAxios.get(Routes.sdc.BASE_URL + '/collection/'+ this.collectionID + '/provincial-duplicates').then(response => { diff --git a/frontend/src/components/data-collection/InclusiveEducationVarianceReport.vue b/frontend/src/components/data-collection/InclusiveEducationVarianceReport.vue new file mode 100644 index 00000000..2734793a --- /dev/null +++ b/frontend/src/components/data-collection/InclusiveEducationVarianceReport.vue @@ -0,0 +1,129 @@ + + + diff --git a/frontend/src/components/data-collection/ReportSection.vue b/frontend/src/components/data-collection/ReportSection.vue index 67b31511..439de3b2 100644 --- a/frontend/src/components/data-collection/ReportSection.vue +++ b/frontend/src/components/data-collection/ReportSection.vue @@ -89,6 +89,12 @@ :collection-type="selectedReport?.reportID" /> + + + @@ -102,10 +108,11 @@ import CustomTableSlice from '@/components/common/CustomTableSlice.vue'; import {Routes} from '@/utils/constants'; import {isEmpty, omitBy} from 'lodash'; import FundingPolicyReport from './FundingPolicyReports.vue'; +import InclusiveEducationVarianceReport from './InclusiveEducationVarianceReport.vue'; export default { name: 'ReportSection', - components: {FundingPolicyReport, Spinner, CustomTableSlice}, + components: {FundingPolicyReport, InclusiveEducationVarianceReport, Spinner, CustomTableSlice}, mixins: [alertMixin], props: { reportList: { @@ -151,7 +158,7 @@ export default { if(this.displayAllStudents) { this.loadStudents(); } - } else if (this.selectedReport.reportID === 'FUNDING_POLICY_REPORT_INDY' || this.selectedReport.reportID === 'FUNDING_POLICY_REPORT_DISTRICT') { + } else if (this.selectedReport.reportID === 'FUNDING_POLICY_REPORT_INDY' || this.selectedReport.reportID === 'FUNDING_POLICY_REPORT_DISTRICT' || this.selectedReport.reportID === 'INCLUSIVE_EDUCATION_VARIANCE') { this.displayAllStudents = false; this.reportData = null; } else { diff --git a/frontend/src/components/data-collection/Reports.vue b/frontend/src/components/data-collection/Reports.vue index fba1856f..58ec614a 100644 --- a/frontend/src/components/data-collection/Reports.vue +++ b/frontend/src/components/data-collection/Reports.vue @@ -84,9 +84,9 @@ export default { data() { return { reportView: null, - publicReports: SDC_REPORTS.publicReports, - independentReports: SDC_REPORTS.independentReports, - headcountsReports: SDC_REPORTS.headcountReports + publicReports: [], + independentReports: [], + headcountsReports: [] }; }, computed: { @@ -94,8 +94,19 @@ export default { }, created() { this.setOriginalReportTab(); + this.publicReports = this.getActiveReports(SDC_REPORTS.publicReports); + this.independentReports = this.getActiveReports(SDC_REPORTS.independentReports); + this.headcountsReports = this.getActiveReports(SDC_REPORTS.headcountReports); }, methods: { + getActiveReports(reports){ + return reports.filter(report => { + if(report.onlyForCollection && !report.onlyForCollection.includes(this.collectionObject.collectionTypeCode)){ + return false; + } + return true; + }); + }, setOriginalReportTab(){ if(this.hasAccessToPublicReports()){ this.reportView = 'publicReports'; diff --git a/frontend/src/components/data-collection/common/HeadCountReportComponent.vue b/frontend/src/components/data-collection/common/HeadCountReportComponent.vue new file mode 100644 index 00000000..4ae8352f --- /dev/null +++ b/frontend/src/components/data-collection/common/HeadCountReportComponent.vue @@ -0,0 +1,104 @@ + + + + + + + + + diff --git a/frontend/src/utils/constants.js b/frontend/src/utils/constants.js index b65bd702..1a733f63 100644 --- a/frontend/src/utils/constants.js +++ b/frontend/src/utils/constants.js @@ -420,6 +420,12 @@ export const SDC_REPORTS = Object.freeze( url: object.SDC_MINISTRY_REPORTS + '/headcount/indy-inclusive-ed-enrollment-headcounts/', csvDownloadURL: object.SDC_MINISTRY_REPORTS + '/download/headcount/indy-inclusive-ed-enrollment-headcounts/' }, + { + label: 'Independent School Inclusive Education Funding Headcounts', + url: object.SDC_MINISTRY_REPORTS + '/headcount/indy-inclusive-ed-funding-headcounts/', + csvDownloadURL: object.SDC_MINISTRY_REPORTS + '/download/headcount/indy-inclusive-ed-funding-headcounts/', + onlyForCollection: ['FEBRUARY'] + }, { label: 'Offshore School Enrolment Headcounts', url: object.SDC_MINISTRY_REPORTS + '/headcount/offshore-enrollment-headcounts/', @@ -434,7 +440,11 @@ export const SDC_REPORTS = Object.freeze( label: 'School Enrolment Headcounts', url: object.SDC_MINISTRY_REPORTS + '/headcount/school-enrollment-headcounts/', csvDownloadURL: object.SDC_MINISTRY_REPORTS + '/download/headcount/school-enrollment-headcounts/' - } + }, + { + label: 'Eligible Inclusive Education Variance Headcounts', + reportID: 'INCLUSIVE_EDUCATION_VARIANCE', + }, ] } );