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

[Fix] Various bug fixes #236

Merged
merged 1 commit into from
Jul 12, 2022
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
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
import { useMutation, useQuery } from '@apollo/client';
import Address from '@components/admin/Address';
import { permitHolderInformationSchema } from '@lib/applicants/validation';
import { titlecase } from '@tools/string';

type PersonalInformationProps = {
readonly applicantId: number;
Expand Down Expand Up @@ -138,7 +139,7 @@ export default function PermitHolderInformationCard(props: PersonalInformationPr
Date of Birth: {formatDateYYYYMMDD(new Date(dateOfBirth))}
</Text>
<Text as="p" textStyle="body-regular">
Gender: {gender === 'OTHER' ? otherGender : gender}
Gender: {gender === 'OTHER' ? otherGender : titlecase(gender)}
</Text>
</VStack>

Expand Down
19 changes: 12 additions & 7 deletions components/admin/requests/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import PermitTypeBadge from '@components/admin/PermitTypeBadge';
import { ApplicationStatus, ApplicationType, PermitType } from '@lib/graphql/types';
import { titlecase } from '@tools/string';
import { formatDateYYYYMMDD } from '@lib/utils/date';
import { getPermanentPermitExpiryDate } from '@lib/utils/permit-expiry';

type RequestHeaderProps = {
readonly applicationType: ApplicationType;
Expand Down Expand Up @@ -86,21 +87,25 @@ export default function RequestHeader({
</Text>
)}
</Box>
<Box>
<VStack alignItems="flex-end" spacing="0">
<Flex alignItems="center">
<Text textStyle="heading" as="h3" marginRight={3} textTransform="capitalize">
Permit Type:
</Text>
<PermitTypeBadge variant={permitType} />
</Flex>
<HStack justifyContent="flex-end">
{permitType === 'TEMPORARY' && !!temporaryPermitExpiry && (
<Text textStyle="caption" as="p" mt="12px">
This permit will expire: {formatDateYYYYMMDD(temporaryPermitExpiry)}
</Text>
)}
<Text textStyle="caption" as="p" mt="12px">
{permitType === 'TEMPORARY' && !!temporaryPermitExpiry
? `This permit will expire: ${formatDateYYYYMMDD(temporaryPermitExpiry)}`
: permitType === 'PERMANENT'
? `This permit will expire: ${formatDateYYYYMMDD(
getPermanentPermitExpiryDate()
)} (expected)`
: null}
</Text>
</HStack>
</Box>
</VStack>
</Flex>
{applicationStatus === 'REJECTED' && (
<Alert status="error">
Expand Down
20 changes: 11 additions & 9 deletions components/admin/requests/guardian-information/Card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -98,17 +98,19 @@ export default function GuardianInformationCard({
<Text as="p" textStyle="body-regular">
This permit holder does not have a guardian/POA
</Text>
<EditGuardianInformationModal
guardianInformation={INITIAL_GUARDIAN_INFORMATION}
onSave={handleSave}
>
<Button height="50px" leftIcon={<AddIcon height="14px" width="14px" />}>
Add a Guardian/POA
</Button>
</EditGuardianInformationModal>
{!editDisabled && (
<EditGuardianInformationModal
guardianInformation={INITIAL_GUARDIAN_INFORMATION}
onSave={handleSave}
>
<Button height="50px" leftIcon={<AddIcon height="14px" width="14px" />}>
Add a Guardian/POA
</Button>
</EditGuardianInformationModal>
)}
</VStack>
);
}, []);
}, [editDisabled]);

/** Render guardian information */
const _renderGuardianInformation = useCallback((guardian: GuardianCardData) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@ export default function ReviewInformationStep({
<>
<PersonalInformationCard applicationId={applicationId} editDisabled isSubsection />
<DoctorInformationCard applicationId={applicationId} editDisabled isSubsection />
<AdditionalInformationCard applicationId={applicationId} isSubsection />
<GuardianInformationCard applicationId={applicationId} isSubsection />
<AdditionalInformationCard applicationId={applicationId} editDisabled isSubsection />
<GuardianInformationCard applicationId={applicationId} editDisabled isSubsection />
<PaymentInformationCard applicationId={applicationId} editDisabled isSubsection />
</>
);
Expand All @@ -79,7 +79,7 @@ export default function ReviewInformationStep({
<>
<PersonalInformationCard applicationId={applicationId} editDisabled isSubsection />
<DoctorInformationCard applicationId={applicationId} editDisabled isSubsection />
<AdditionalInformationCard applicationId={applicationId} isSubsection />
<AdditionalInformationCard applicationId={applicationId} editDisabled isSubsection />
<PaymentInformationCard applicationId={applicationId} editDisabled isSubsection />
</>
);
Expand Down
27 changes: 20 additions & 7 deletions lib/application-processing/resolvers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -550,7 +550,6 @@ export const completeApplication: Resolver<
rcdPermitId: appNumber,
// TODO: Replace with type of most recent permit
type: 'PERMANENT',
// ? Original permit expiry or 3 years by default?
expiryDate: getPermanentPermitExpiryDate(),
applicant: { connect: { id: applicantId } },
application: { connect: { id } },
Expand Down Expand Up @@ -612,6 +611,19 @@ export const updateApplicationProcessingAssignAppNumber: Resolver<
throw new ApolloError('Cannot update APP number of already-reviewed application');
}

if (appNumber !== null) {
// Check that APP number does not exist yet
const existingPermit = await prisma.permit.findUnique({
where: { rcdPermitId: appNumber },
});

if (existingPermit !== null) {
throw new ApolloError(
`Could not assign APP number: a permit with APP number ${appNumber} already exists`
);
}
}

let updatedApplicationProcessing;
try {
updatedApplicationProcessing = await prisma.application.update({
Expand Down Expand Up @@ -887,20 +899,21 @@ export const updateApplicationProcessingGenerateInvoice: Resolver<
throw new ApolloError('Error creating invoice record in DB');
}

// file name format: PP-Receipt-P<YYYYMMDD>-<invoice number>.pdf
const createdAtYYYMMDD = formatDateYYYYMMDD(invoice.createdAt).replace(/-/g, '');
const receiptNumber = `${createdAtYYYMMDD}-${invoice.invoiceNumber}`;
const fileName = `PP-Receipt-P${receiptNumber}.pdf`;
const s3InvoiceKey = `rcd/invoices/${fileName}`;

// Generate application invoice
const pdfDoc = generateApplicationInvoicePdf(
application,
session,
// TODO: Remove typecast when backend guard is implemented
application.applicationProcessing.appNumber as number,
invoice.invoiceNumber
receiptNumber
);

// file name format: PP-Receipt-P<YYYYMMDD>-<invoice number>.pdf
const createdAtYYYMMDD = formatDateYYYYMMDD(invoice.createdAt).replace(/-/g, '');
const fileName = `PP-Receipt-P${createdAtYYYMMDD}-${invoice.invoiceNumber}.pdf`;
const s3InvoiceKey = `rcd/invoices/${fileName}`;

// Upload pdf to s3
let uploadedPdf;
let signedUrl;
Expand Down
8 changes: 4 additions & 4 deletions lib/invoices/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ import { PaymentType } from '@lib/graphql/types';
* @param application application object
* @param session session object containing employee information
* @param appNumber APP (parking permit) number
* @param invoiceNumber invoice number
* @param receiptNumber receipt number
*/
export const generateApplicationInvoicePdf = (
application: Application,
session: Session,
appNumber: number,
invoiceNumber: number
receiptNumber: string
): PDFKit.PDFDocument => {
const {
applicantId,
Expand Down Expand Up @@ -70,7 +70,7 @@ export const generateApplicationInvoicePdf = (
applicantName,
userNumber: applicantId,
permitType,
receiptNumber: invoiceNumber,
receiptNumber,
dateIssued: new Date(),
issuedBy: employeeInitials,
paymentItems,
Expand All @@ -96,7 +96,7 @@ const pdfDefinition = (input: {
applicantName: string;
userNumber: number | null;
permitType: string;
receiptNumber: number;
receiptNumber: string;
dateIssued: Date;
issuedBy: string;
paymentItems: Array<{
Expand Down
38 changes: 24 additions & 14 deletions lib/reports/resolvers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { APPLICATIONS_COLUMNS, PERMIT_HOLDERS_COLUMNS } from '@tools/admin/repor
import { Prisma } from '@prisma/client';
import { getSignedUrlForS3, serverUploadToS3 } from '@lib/utils/s3-utils';
import { ApolloError } from 'apollo-server-micro';
import moment from 'moment';

/**
* Generates csv with permit holders' info, given a start date, end date, and values from
Expand All @@ -28,7 +29,7 @@ export const generatePermitHoldersReport: Resolver<
GeneratePermitHoldersReportResult
> = async (_, args, { prisma, session }) => {
const {
input: { startDate, endDate, columns },
input: { startDate, endDate: inputEndDate, columns },
} = args;

const columnsSet = new Set(columns);
Expand All @@ -38,13 +39,16 @@ export const generatePermitHoldersReport: Resolver<
throw new ApolloError('Not authenticated');
}

// Calculate end date as beginning of the next day
const endDate = moment.utc(inputEndDate).add(1, 'd').toDate();

const applicants = await prisma.applicant.findMany({
where: {
permits: {
some: {
expiryDate: {
gte: startDate,
lte: endDate,
lt: endDate,
},
},
},
Expand Down Expand Up @@ -179,7 +183,7 @@ export const generateApplicationsReport: Resolver<
GenerateApplicationsReportResult
> = async (_, args, { prisma, session }) => {
const {
input: { startDate, endDate, columns },
input: { startDate, endDate: inputEndDate, columns },
} = args;

if (!session) {
Expand All @@ -189,11 +193,14 @@ export const generateApplicationsReport: Resolver<

const columnsSet = new Set(columns);

// Calculate end date as beginning of the next day
const endDate = moment.utc(inputEndDate).add(1, 'd').toDate();

const applications = await prisma.application.findMany({
where: {
createdAt: {
gte: startDate,
lte: endDate,
lt: endDate,
},
},
select: {
Expand Down Expand Up @@ -318,7 +325,7 @@ export const generateAccountantReport: Resolver<
GenerateAccountantReportResult
> = async (_, args, { prisma, session }) => {
const {
input: { startDate, endDate },
input: { startDate, endDate: inputEndDate },
} = args;

if (!session) {
Expand All @@ -336,12 +343,15 @@ export const generateAccountantReport: Resolver<
CHEQUE: 'Cheque',
};

// Calculate end date as beginning of the next day
const endDate = moment.utc(inputEndDate).add(1, 'd').toDate();

const paymentMethodGroups = await prisma.application.groupBy({
by: ['paymentMethod'],
where: {
createdAt: {
gte: startDate,
lte: endDate,
lt: endDate,
},
},
_sum: {
Expand Down Expand Up @@ -374,23 +384,23 @@ export const generateAccountantReport: Resolver<
csvAccountantReportRows.push({
rowName: paymentTypeToString[paymentMethodGroup.paymentMethod],
countIssued: paymentMethodGroup._count.paymentMethod,
processingFee: paymentMethodGroup._sum.processingFee || 0,
donationAmount: paymentMethodGroup._sum.donationAmount || 0,
totalAmount: Prisma.Decimal.add(
processingFee: `$${paymentMethodGroup._sum.processingFee || 0}`,
donationAmount: `$${paymentMethodGroup._sum.donationAmount || 0}`,
totalAmount: `$${Prisma.Decimal.add(
paymentMethodGroup._sum.donationAmount || 0,
paymentMethodGroup._sum.processingFee || 0
),
)}`,
});
}
csvAccountantReportRows.push({
rowName: 'Total',
countIssued: totalAggregate._count.paymentMethod || 0,
processingFee: totalAggregate._sum.processingFee || 0,
donationAmount: totalAggregate._sum.donationAmount || 0,
totalAmount: Prisma.Decimal.add(
processingFee: `$${totalAggregate._sum.processingFee || 0}`,
donationAmount: `$${totalAggregate._sum.donationAmount || 0}`,
totalAmount: `$${Prisma.Decimal.add(
totalAggregate._sum.donationAmount || 0,
totalAggregate._sum.processingFee || 0
),
)}`,
});

const csvHeaders = [
Expand Down
12 changes: 7 additions & 5 deletions lib/utils/permit-expiry.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import { PermitStatus } from '@lib/graphql/types';
import moment from 'moment';

/**
* Get the expiry date of a permit 3 years after today or a provided application date
* Get the expiry date of a permit (last day of month 3 years after today or a provided application date)
* @param applicationDate Date of application (optional), default is today
* @returns Date object of day 3 years from applicationDate or today
* @returns Date object of last day of month of day 3 years from applicationDate or today
*/
export const getPermanentPermitExpiryDate = (applicationDate?: Date): Date => {
return applicationDate
? new Date(new Date(applicationDate.getTime()).setFullYear(applicationDate.getFullYear() + 3))
: new Date(new Date().setFullYear(new Date().getFullYear() + 3));
return (applicationDate ? moment.utc(applicationDate) : moment.utc())
.add(3, 'y')
.endOf('month')
.toDate();
};

/**
Expand Down
Loading