Skip to content

Commit

Permalink
17534 - New NSF Resource Implementation and NSF Invoice PDF Download (b…
Browse files Browse the repository at this point in the history
…cgov#2658)

* NSF Invoices and PDF

* Version update

* Step fix

* Refactoring to use state

* Updating

* Version update and PR feedback
  • Loading branch information
rodrigo-barraza authored Jan 22, 2024
1 parent 78cdb1a commit ea5d9f0
Show file tree
Hide file tree
Showing 8 changed files with 114 additions and 85 deletions.
4 changes: 2 additions & 2 deletions auth-web/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion auth-web/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "auth-web",
"version": "2.4.58",
"version": "2.4.59",
"appName": "Auth Web",
"sbcName": "SBC Common Components",
"private": true,
Expand Down
87 changes: 41 additions & 46 deletions auth-web/src/components/auth/account-freeze/AccountOverview.vue
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { AccessType } from '@/util/constants'
<template>
<div>
<p class="mb-10">
Expand Down Expand Up @@ -28,15 +27,15 @@ import { AccessType } from '@/util/constants'
</div>
</v-col>
<v-col class="text-end">
${{ nsfFee.toFixed(2) }}
${{ nsfAmount.toFixed(2) }}
</v-col>
</v-row>
<v-row>
<v-col cols="9">
Total Transactions
</v-col>
<v-col class="text-end">
${{ totalTransactionAmount.toFixed(2) }}
${{ totalAmount.toFixed(2) }}
</v-col>
</v-row>
<v-divider class="my-2" />
Expand All @@ -45,7 +44,7 @@ import { AccessType } from '@/util/constants'
Total Amount Due
</v-col>
<v-col class="text-end">
${{ totalAmountToPay.toFixed(2) }}
${{ totalAmountRemaining.toFixed(2) }}
</v-col>
</v-row>
</v-card-text>
Expand All @@ -61,7 +60,7 @@ import { AccessType } from '@/util/constants'
large
text
color="primary"
@click="downloadTransactionPDF"
@click="downloadNSFInvoicesPDF"
>
<v-icon class="ml-n2">
mdi-download
Expand All @@ -85,56 +84,52 @@ import { AccessType } from '@/util/constants'
</template>

<script lang="ts">
import { Component, Mixins } from 'vue-property-decorator'
import { mapActions, mapState } from 'pinia'
import { computed, defineComponent, onMounted, reactive, toRefs } from '@vue/composition-api'
import CommonUtils from '@/util/common-util'
import { FailedInvoice } from '@/models/invoice'
import { Organization } from '@/models/Organization'
import Steppable from '@/components/auth/common/stepper/Steppable.vue'
import { useOrgStore } from '@/stores/org'
@Component({
computed: {
...mapState(useOrgStore, [
'currentOrganization'
])
},
methods: {
...mapActions(useOrgStore, [
'calculateFailedInvoices'
])
}
})
export default class AccountOverview extends Mixins(Steppable) {
private readonly currentOrganization!: Organization
private readonly calculateFailedInvoices!: () => FailedInvoice
private formatDate = CommonUtils.formatDisplayDate
private nsfFee: number = 0
private nsfCount: number = 0
private totalTransactionAmount: number = 0
private totalAmountToPay: number = 0
private totalPaidAmount: number = 0
export default defineComponent({
name: 'AccountOverviewView',
emits: ['step-forward'],
setup (_, { emit }) {
const orgStore = useOrgStore()
const currentOrganization = computed(() => orgStore.currentOrganization)
const currentMembership = computed(() => orgStore.currentMembership)
const calculateFailedInvoices: any = orgStore.calculateFailedInvoices
const downloadNSFInvoicesPDF: any = orgStore.downloadNSFInvoicesPDF
const formatDate = CommonUtils.formatDisplayDate
const suspendedDate = (currentOrganization.value?.suspendedOn) ? formatDate(new Date(currentOrganization.value.suspendedOn)) : ''
private goNext () {
this.stepForward()
}
const state = reactive({
nsfAmount: 0,
totalAmount: 0,
totalAmountRemaining: 0,
totalPaidAmount: 0
})
private get suspendedDate () {
return (this.currentOrganization?.suspendedOn) ? this.formatDate(new Date(this.currentOrganization.suspendedOn)) : ''
}
const goNext = () => {
emit('step-forward')
}
private downloadTransactionPDF () {
// download PDF
}
onMounted(async () => {
const failedInvoices: FailedInvoice = await calculateFailedInvoices()
state.totalAmount = failedInvoices?.totalTransactionAmount || 0
state.nsfAmount = failedInvoices?.nsfFee || 0
state.totalAmountRemaining = failedInvoices?.totalAmountToPay || 0
})
async mounted () {
const failedInvoices: FailedInvoice = await this.calculateFailedInvoices()
this.nsfCount = failedInvoices?.nsfCount || 0
this.totalTransactionAmount = failedInvoices?.totalTransactionAmount || 0
this.nsfFee = failedInvoices?.nsfFee || 0
this.totalAmountToPay = failedInvoices?.totalAmountToPay || 0
return {
...toRefs(state),
currentOrganization,
currentMembership,
suspendedDate,
downloadNSFInvoicesPDF,
goNext
}
}
}
})
</script>

<style lang="scss" scoped>
Expand Down
5 changes: 5 additions & 0 deletions auth-web/src/components/auth/common/stepper/Stepper.vue
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
v-bind="getPropsForStep(step)"
keep-alive
@final-step-action="emitFinalStepAction"
@step-forward="emitStepForward"
/>
</template>
</div>
Expand Down Expand Up @@ -170,6 +171,10 @@ export default class Stepper extends Vue {
emitFinalStepAction (stepperData) {
return stepperData
}
@Emit('step-forward')
emitStepForward () : void {
}
}
</script>

Expand Down
9 changes: 9 additions & 0 deletions auth-web/src/models/invoice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,13 @@ export interface FailedInvoice {
nsfFee?:number
totalTransactionAmount?: number
totalAmountToPay?: number
invoices?: InvoiceList[]
}

export interface NonSufficientFundsInvoiceListResponse {
invoices: InvoiceList[]
total: number
totalAmount: number
totalAmountRemaining: number
nsfAmount: number
}
11 changes: 11 additions & 0 deletions auth-web/src/services/payment.services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,4 +192,15 @@ export default class PaymentService {
static removeAccountFees (accountId: string | number): AxiosPromise<any> {
return axios.delete(`${ConfigHelper.getPayAPIURL()}/accounts/${accountId}/fees`)
}

static getNSFInvoices (accountId: string | number): AxiosPromise<any> {
return axios.get(`${ConfigHelper.getPayAPIURL()}/accounts/${accountId}/nsf`)
}

static downloadNSFInvoicesPDF (accountId: string | number): AxiosPromise<any> {
const url = `${ConfigHelper.getPayAPIURL()}/accounts/${accountId}/nsf/statement`
const headers = { 'Accept': 'application/pdf' }
const body = {}
return axios.post(url, body, { headers, responseType: 'blob' as 'json' })
}
}
74 changes: 38 additions & 36 deletions auth-web/src/stores/org.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import {
} from '@/models/Organization'
import { BcolAccountDetails, BcolProfile } from '@/models/bcol'
import { CreateRequestBody as CreateInvitationRequestBody, Invitation } from '@/models/Invitation'
import { FailedInvoice, NonSufficientFundsInvoiceListResponse } from '@/models/invoice'
import { Products, ProductsRequestBody } from '@/models/Staff'
import { StatementFilterParams, StatementNotificationSettings, StatementSettings, StatementsSummary } from '@/models/statement'
import { computed, reactive, toRefs } from '@vue/composition-api'
Expand All @@ -47,7 +48,7 @@ import CommonUtils from '@/util/common-util'
import ConfigHelper from '@/util/config-helper'
import { EmptyResponse } from '@/models/global'
import InvitationService from '@/services/invitation.services'
import { InvoiceList } from '@/models/invoice'

import KeyCloakService from 'sbc-common-components/src/services/keycloak.services'
import OrgService from '@/services/org.services'
import PaymentService from '@/services/payment.services'
Expand Down Expand Up @@ -741,39 +742,39 @@ export const useOrgStore = defineStore('org', () => {
return response?.data
}

async function getFailedInvoices (): Promise<InvoiceList[]> {
const response = await PaymentService.getFailedInvoices(state.currentOrganization.id)
// if there is any failed payment which is partially paid, return only that payment.
let items = response?.data?.items || []
items.forEach(payment => {
if (payment.paidAmount > 0) {
items = [payment]
}
})
return items
}

// to calculate failed invoices. need to move appropriate place since its returning data than commiting to store (which is not standard)
async function calculateFailedInvoices () {
let totalPaidAmount = 0
let totalAmountToPay = 0
let nsfCount = 0
let nsfFee = 0
let totalTransactionAmount = 0
const failedInvoices: InvoiceList[] = await getFailedInvoices()
failedInvoices?.forEach((failedInvoice) => {
totalPaidAmount += failedInvoice?.paidAmount
totalAmountToPay += failedInvoice?.invoices?.map(el => el.total).reduce((accumulator, invoiceTotal) => accumulator + invoiceTotal)
failedInvoice?.invoices?.forEach((invoice) => {
const nsfItems = invoice?.lineItems?.filter(lineItem => (lineItem.description === 'NSF'))
.map(el => el.total)
nsfCount += nsfItems.length
nsfFee += (nsfItems.length) ? nsfItems?.reduce((accumulator, currentValue) => accumulator + currentValue) : 0
})
})
totalTransactionAmount = totalAmountToPay - nsfFee
totalAmountToPay = totalAmountToPay - totalPaidAmount
return { totalTransactionAmount, totalAmountToPay, nsfFee, nsfCount }
async function getNSFInvoices (): Promise<NonSufficientFundsInvoiceListResponse> {
try {
const response = await PaymentService.getNSFInvoices(state.currentOrganization.id)
return response?.data || {}
} catch (error) {
// eslint-disable-next-line no-console
console.error('get NSF invoices operation failed! - ', error)
}
}

async function downloadNSFInvoicesPDF (): Promise<any> {
try {
const response = await PaymentService.downloadNSFInvoicesPDF(state.currentOrganization.id)
CommonUtils.fileDownload(response?.data, `non-sufficient_funds_invoice.pdf`, response?.headers['content-type'])
} catch (error) {
// eslint-disable-next-line no-console
console.error('download NSF invoices PDF operation failed! - ', error)
}
}

async function calculateFailedInvoices (): Promise<FailedInvoice> {
const nsfInvoices = await getNSFInvoices()
const invoices = nsfInvoices?.invoices || []
const totalAmount = nsfInvoices?.totalAmount || 0
const totalAmountRemaining = nsfInvoices?.totalAmountRemaining || 0
const nsfAmount = nsfInvoices?.nsfAmount || 0

return {
invoices: invoices,
nsfFee: nsfAmount,
totalAmountToPay: totalAmountRemaining,
totalTransactionAmount: totalAmount
}
}

async function resetAccountSetupProgress (): Promise<void> {
Expand Down Expand Up @@ -1093,7 +1094,6 @@ export const useOrgStore = defineStore('org', () => {
getStatementRecipients,
updateStatementNotifications,
getOrgPayments,
getFailedInvoices,
calculateFailedInvoices,
resetAccountSetupProgress,
resetAccountWhileSwitchingPremium,
Expand Down Expand Up @@ -1123,6 +1123,8 @@ export const useOrgStore = defineStore('org', () => {
updateOrganizationAccessType,
$reset,
isStaffOrSbcStaff,
removeOrgAccountFees
removeOrgAccountFees,
getNSFInvoices,
downloadNSFInvoicesPDF
}
})
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,12 @@
</div>
<v-card flat>
<Stepper
ref="stepper"
:stepper-configuration="stepperConfig"
:isLoading="isLoading"
:stepperColor="'error'"
@final-step-action="unlockAccount"
@step-forward="handleStepForward"
/>
</v-card>

Expand Down Expand Up @@ -119,6 +121,7 @@ export default class AccountFreezeUnlockView extends Vue {
$refs: {
errorDialog: InstanceType<typeof ModalDialog>
stepper: InstanceType<typeof Stepper>
}
private stepperConfig: Array<StepConfiguration> =
Expand Down Expand Up @@ -157,5 +160,9 @@ export default class AccountFreezeUnlockView extends Vue {
private closeError () {
this.$refs.errorDialog.close()
}
private handleStepForward () {
this.$refs.stepper.stepForward()
}
}
</script>

0 comments on commit ea5d9f0

Please sign in to comment.