Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OFMCC-6328 - Funding envelope change request tables #404

Merged
merged 6 commits into from
Nov 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 17 additions & 3 deletions backend/src/components/fundingAgreements.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
const { getOperation, patchOperationWithObjectId, getOperationWithObjectId, handleError } = require('./utils')
const { MappableObjectForFront, MappableObjectForBack } = require('../util/mapping/MappableObject')
const { buildDateFilterQuery, buildFilterQuery } = require('../util/common')
const { FundingAgreementMappings } = require('../util/mapping/Mappings')
const { FundingAgreementMappings, FundingReallocationRequestMappings } = require('../util/mapping/Mappings')
const HttpStatus = require('http-status-codes')
const log = require('./logger')
const { isEmpty } = require('lodash')
Expand Down Expand Up @@ -86,10 +86,24 @@ async function updateFundingAgreement(req, res) {
}
}

async function getFundingReallocationRequests(req, res) {
try {
const fundingReallocationRequests = []
const operation = `ofm_funding_envelope_changes?$select=ofm_funding_envelope_changeid,_ofm_funding_value,ofm_funding_envelope_from,ofm_funding_envelope_to,ofm_amount_base,createdon,statuscode
&$filter=(_ofm_funding_value eq ${req?.params?.fundingAgreementId})&pageSize=500`
const response = await getOperation(operation)
response?.value?.forEach((reallocationRequest) => fundingReallocationRequests.push(new MappableObjectForFront(reallocationRequest, FundingReallocationRequestMappings).toJSON()))
return res.status(HttpStatus.OK).json(fundingReallocationRequests)
} catch (e) {
handleError(res, e)
}
}

module.exports = {
getFundingAgreements,
updateFundingAgreement,
getFundingAgreementById,
getRawFundingAgreementById,
getFundingReallocationRequests,
getFundingPDFById,
getRawFundingAgreementById,
updateFundingAgreement,
}
18 changes: 17 additions & 1 deletion backend/src/routes/fundingAgreements.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ const passport = require('passport')
const router = express.Router()
const auth = require('../components/auth')
const isValidBackendToken = auth.isValidBackendToken()
const { getFundingAgreements, updateFundingAgreement, getFundingAgreementById, getFundingPDFById } = require('../components/fundingAgreements')
const { getFundingAgreements, updateFundingAgreement, getFundingAgreementById, getFundingPDFById, getFundingReallocationRequests } = require('../components/fundingAgreements')
const { param, query, validationResult, oneOf } = require('express-validator')
const validateExpenseAuthority = require('../middlewares/validateExpenseAuthority.js')
const validateFacility = require('../middlewares/validateFacility.js')
Expand Down Expand Up @@ -76,3 +76,19 @@ router.patch(
return updateFundingAgreement(req, res)
},
)

/**
* Get the list of Funding Reallocation Requests
*/
router.get(
'/:fundingAgreementId/funding-reallocation-requests',
passport.authenticate('jwt', { session: false }),
isValidBackendToken,
// TODO (vietle-cgi) - update permission once we receive confirmation for this requirement
validatePermission(PERMISSIONS.VIEW_FUNDING_AGREEMENT),
[param('fundingAgreementId', 'URL param: [fundingAgreementId] is required').notEmpty().isUUID()],
(req, res) => {
validationResult(req).throw()
return getFundingReallocationRequests(req, res)
},
)
14 changes: 14 additions & 0 deletions backend/src/util/mapping/Mappings.js
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,19 @@ const FundingAgreementMappings = [
{ back: 'ofm_envelope_facility', front: 'envelopeFacility' },
]

const FundingReallocationRequestMappings = [
{ back: 'ofm_funding_envelope_changeid', front: 'fundingEnvelopeId' },
{ back: '_ofm_funding_value', front: 'fundingId' },
{ back: 'ofm_funding_envelope_from', front: 'envelopeCodeFrom' },
{ back: 'ofm_funding_envelope_from@OData.Community.Display.V1.FormattedValue', front: 'envelopeNameFrom' },
{ back: 'ofm_funding_envelope_to', front: 'envelopeCodeTo' },
{ back: 'ofm_funding_envelope_to@OData.Community.Display.V1.FormattedValue', front: 'envelopeNameTo' },
{ back: 'ofm_amount_base', front: 'amount' },
{ back: 'createdon', front: 'date' },
{ back: 'statuscode', front: 'statusCode' },
{ back: '[email protected]', front: 'statusName' },
]

const PaymentMappings = [
{ back: 'ofm_paymentid', front: 'paymentId' },
{ back: 'ofm_name', front: 'paymentNumber' },
Expand Down Expand Up @@ -493,6 +506,7 @@ module.exports = {
FacilityIntakeMappings,
FacilityMappings,
FundingAgreementMappings,
FundingReallocationRequestMappings,
LicenceMappings,
LicenceDetailsMappings,
NotificationMappings,
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/funding/BaseFundingCard.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<template>
<v-card elevation="2" class="ma-2">
<v-card-title class="card-title"><strong>Base Funding Paid</strong></v-card-title>
<v-card-title class="card-title"><strong>Base Funding</strong></v-card-title>
<v-skeleton-loader :loading="loading" type="table-tbody" class="pa-6">
<v-container fluid class="pa-0">
<div>The base funding shown is only from the current fiscal year and does not include changes from funding re-allocation requests.</div>
Expand Down
21 changes: 18 additions & 3 deletions frontend/src/components/funding/FundingAllocationTab.vue
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
<template>
<v-container fluid class="pa-0">
<div class="ma-2">View or submit a change for your centre's current monthly funding allocations. Funds can be re-allocated based on the rules below.</div>
<v-row v-if="hasPermission(PERMISSIONS.SUBMIT_CHANGE_REQUEST)" no-gutters class="ma-2 justify-end">
<!-- TODO (vietle-cgi) - update permission once we receive confirmation for this requirement -->
<v-row v-if="hasPermission(PERMISSIONS.SUBMIT_CHANGE_REQUEST)" no-gutters class="mx-2 my-6 my-sm-2 justify-end">
<AppButton :loading="loading" @click="toggleAssistanceRequestDialog()">Request to Re-allocate Funds</AppButton>
</v-row>
<h2 class="ma-2">Funding Re-Allocation Rules</h2>
<AppAlertBanner type="info" class="ma-2 mb-4">Note: Total funds re-allocated cannot be more than the base funding for each envelope.</AppAlertBanner>
<FundingAllocationInfoTable class="pa-2" />
<FundingSearchCard :loading="loading" :select-single-facility="true" :show-date-filter="false" :show-reset-button="false" class="my-10" @search="loadFundingDetails" />
<BaseFundingCard :loading="loading" :funding-details="fundingDetails" />
<FundingReallocationRequestsTable :loading="loading" :funding-reallocation-requests="fundingReallocationRequests" class="mt-8" />
<NewRequestDialog
class="pa-0"
:show="showAssistanceRequestDialog"
Expand All @@ -23,6 +25,7 @@ import { mapState } from 'pinia'

import BaseFundingCard from '@/components/funding/BaseFundingCard.vue'
import FundingAllocationInfoTable from '@/components/funding/FundingAllocationInfoTable.vue'
import FundingReallocationRequestsTable from '@/components/funding/FundingReallocationRequestsTable.vue'
import FundingSearchCard from '@/components/funding/FundingSearchCard.vue'
import NewRequestDialog from '@/components/messages/NewRequestDialog.vue'
import AppAlertBanner from '@/components/ui/AppAlertBanner.vue'
Expand All @@ -35,14 +38,15 @@ import { FUNDING_AGREEMENT_STATUS_CODES, REQUEST_CATEGORY_NAMES } from '@/utils/

export default {
name: 'FundingAllocationTab',
components: { AppAlertBanner, AppButton, BaseFundingCard, FundingAllocationInfoTable, FundingSearchCard, NewRequestDialog },
components: { AppAlertBanner, AppButton, BaseFundingCard, FundingAllocationInfoTable, FundingReallocationRequestsTable, FundingSearchCard, NewRequestDialog },
mixins: [alertMixin, permissionsMixin],
data() {
return {
loading: false,
showAssistanceRequestDialog: false,
selectedFacility: null,
fundingDetails: {},
fundingReallocationRequests: [],
}
},

Expand All @@ -60,13 +64,24 @@ export default {
try {
this.loading = true
this.fundingDetails = await FundingAgreementService.getFundingEnvelopesByFacilityIdAndStatus(this.selectedFacility?.facilityId, FUNDING_AGREEMENT_STATUS_CODES.ACTIVE)
this.fundingReallocationRequests = await FundingAgreementService.getFundingReallocationRequestsByFundingAgreementId(this.fundingDetails?.fundingId)
this.sortFundingReallocationRequests()
} catch (error) {
this.setFailureAlert('Failed to load funding details', error)
this.setFailureAlert('Failed to load funding re-allocation requests', error)
} finally {
this.loading = false
}
},

// OFMCC-6328 - Sort by status first (In Progress > Approved > Ineligible > Cancelled), and then sort by Date (newest first)
sortFundingReallocationRequests() {
this.fundingReallocationRequests?.sort((a, b) => {
const dateA = new Date(a.date)
const dateB = new Date(b.date)
return a.statusCode > b.statusCode || dateB - dateA
})
},

toggleAssistanceRequestDialog() {
this.showAssistanceRequestDialog = !this.showAssistanceRequestDialog
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<template>
<v-container fluid class="px-2">
<h3>Funding Re-allocation Requests</h3>
<div class="pt-2 pb-4">The funding re-allocation requests shown include all fiscal years of the selected facility.</div>
<v-skeleton-loader :loading="loading" type="table-tbody">
<v-data-table
id="funding-requests-table"
:headers="fundingRequestsHeaders"
:items="fundingReallocationRequests"
item-key="fundingEnvelopeId"
density="compact"
:mobile="null"
mobile-breakpoint="md"
class="soft-outline">
<template #no-data>The selected facility has not submitted a funding re-allocation request.</template>
<template #[`item.date`]="{ item }">
{{ format.formatDate(item?.date) }}
</template>
<template #[`item.amount`]="{ item }">$ {{ format.formatDecimalNumber(item?.amount) }}</template>
<template #[`item.statusCode`]="{ item }">
<div class="min-width-column">
<span :class="getStatusClass(item?.statusCode)">{{ item?.statusName }}</span>
</div>
</template>
</v-data-table>
</v-skeleton-loader>
</v-container>
</template>

<script>
import { FUNDING_REALLOCATION_REQUEST_STATUS_CODES } from '@/utils/constants'
import format from '@/utils/format'

export default {
name: 'FundingReallocationRequestsTable',
props: {
loading: {
type: Boolean,
default: true,
},
fundingReallocationRequests: {
type: Array,
default: () => [],
},
},
data() {
return {
fundingRequestsHeaders: [
{ title: 'Date', key: 'date' },
{ title: 'From', key: 'envelopeNameFrom' },
{ title: 'To', key: 'envelopeNameTo' },
{ title: 'Amount', key: 'amount' },
{ title: 'Status', key: 'statusCode' },
],
}
},
created() {
this.format = format
},
methods: {
getStatusClass(statusCode) {
switch (statusCode) {
case FUNDING_REALLOCATION_REQUEST_STATUS_CODES.IN_PROGRESS:
return 'status-blue'
case FUNDING_REALLOCATION_REQUEST_STATUS_CODES.APPROVED:
return 'status-green'
case FUNDING_REALLOCATION_REQUEST_STATUS_CODES.CANCELLED:
return 'status-gray'
case FUNDING_REALLOCATION_REQUEST_STATUS_CODES.INELIGIBLE:
return 'status-red'
default:
return ''
}
},
},
}
</script>
<style scoped>
.min-width-column {
min-width: 105px;
}
</style>
9 changes: 8 additions & 1 deletion frontend/src/components/funding/FundingSearchCard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
:rules="rules.required"
density="compact"
variant="outlined"
return-object />
return-object
class="wrapped-select" />
<v-select
v-else
v-model="selectedFacilities"
Expand Down Expand Up @@ -261,3 +262,9 @@ export default {
},
}
</script>
<style scoped>
:deep(.wrapped-select .v-select__selection-text) {
white-space: normal;
overflow-wrap: break-word;
}
</style>
11 changes: 11 additions & 0 deletions frontend/src/services/fundingAgreementService.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,4 +94,15 @@ export default {
throw error
}
},

async getFundingReallocationRequestsByFundingAgreementId(fundingAgreementId) {
try {
if (!fundingAgreementId) return
const response = await ApiService.apiAxios.get(`${ApiRoutes.FUNDING_AGREEMENTS}/${fundingAgreementId}/funding-reallocation-requests`)
return response?.data
} catch (error) {
console.log(`Failed to get the funding reallocation requests by funding agreement id - ${error}`)
throw error
}
},
}
7 changes: 7 additions & 0 deletions frontend/src/utils/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,13 @@ export const FUNDING_AGREEMENT_STATUS_CODES = Object.freeze({
CANCELLED: 10,
})

export const FUNDING_REALLOCATION_REQUEST_STATUS_CODES = Object.freeze({
IN_PROGRESS: 1,
APPROVED: 2,
INELIGIBLE: 3,
CANCELLED: 4,
})

export const PAYMENT_STATUS_CODES = Object.freeze({
PENDING_PAYMENT: 1,
PAID: 2, // INACTIVE state
Expand Down
Loading