From 35571cf40b302e34f4dd11465412e85cbe594f7f Mon Sep 17 00:00:00 2001 From: Leo Huang <77544794+leogjhuang@users.noreply.github.com> Date: Thu, 15 Feb 2024 16:08:23 -0500 Subject: [PATCH] [Release] Version 0.3.0 (#351) * [Improvement] Clarify lowercase extension requirement for POA form upload (#343) * Change text on processing task 2 (#344) * Remove holiday closure banner (#345) * [Feature] Update ci.yml to add pending migrations to db (#339) * Update ci.yml to add pending migrations to db * Add .env file to CI * Update ci.yml * Update ci.yml * Use longer URL * Add to package.json instead * revert ci.yml * Re-add ci.yml * Add space to yarn build * Make title fit on one line (#347) * [Fix] Remove npx from start script (#348) * [Feature] Add AMEX (#346) * Add Amex * Add Amex to popoulate js file * Update populate-db-09-13-21.ts * [Fix] Revert init migration file and add new migration for AMEX paymenttype (#349) * Revert paymenttype edit to init migration file * Create migration to add AMEX paymenttype * [Improvement] Change helper text for date of birth (#350) * Change helper text for date of birth * Remove text wrapper on DOB helper text * [Feature] Add second payment method for permit requests (#338) * Add second payment method types to schema * Update application validation schema to allow second payment methods * Change payment detail components to show second payment method * Consider donations on second payment method in processing tasks * Update data pipelines to generate accountant reports with second payment method included * Update invoice and donation receipt with second payment method included * Rename second payment method variables to improve clarity * Add migration for second payment method --------- Co-authored-by: Sherry Li Co-authored-by: Chinemerem --- .../PoaFormUploadField.tsx | 4 +- .../requests/payment-information/Card.tsx | 97 ++++-- .../requests/payment-information/Form.tsx | 52 ++++ .../admin/requests/processing/TasksCard.tsx | 7 +- .../renewals/IdentityVerification.tsx | 8 +- lib/applications/resolvers.ts | 62 +++- lib/applications/schema.ts | 40 ++- lib/applications/utils.ts | 50 +++- lib/applications/validation.ts | 39 ++- lib/graphql/schema.ts | 1 + lib/graphql/types.ts | 33 +++ lib/invoices/utils.ts | 54 +++- lib/reports/resolvers.ts | 275 +++++++++++++++--- .../oneoff/backup/populate-db-09-13-21.ts | 2 + lib/scripts/oneoff/populate-db-09-13-21.js | 2 + lib/scripts/oneoff/populate-db-10-01-2021.js | 2 + package.json | 2 +- pages/index.tsx | 16 - prisma/dev-seed-utils/applications.ts | 20 ++ .../migration.sql | 2 + .../migration.sql | 5 + prisma/schema.prisma | 5 + prisma/schema.sql | 5 + prisma/types.ts | 4 + tools/admin/requests/create-new.ts | 4 + tools/admin/requests/payment-information.ts | 12 + tools/admin/requests/processing-tasks-card.ts | 7 +- 27 files changed, 699 insertions(+), 111 deletions(-) create mode 100644 prisma/migrations/20240207054756_add_amex_payment_type/migration.sql create mode 100644 prisma/migrations/20240212032557_add_second_payment_method/migration.sql diff --git a/components/admin/requests/guardian-information/PoaFormUploadField.tsx b/components/admin/requests/guardian-information/PoaFormUploadField.tsx index 05e58a3e..39cc45d8 100644 --- a/components/admin/requests/guardian-information/PoaFormUploadField.tsx +++ b/components/admin/requests/guardian-information/PoaFormUploadField.tsx @@ -63,7 +63,9 @@ export const PoaFormUploadField: FC = ({ {'Upload POA File'} - {'Only ONE file can be added. Files must be .pdf and can be a maximum of 5MB in size.'}{' '} + { + 'Only ONE file can be added. Files must have .pdf extension (lowercase) and can be a maximum of 5MB in size.' + }{' '} {(file || initialFileUrl) && ( <> diff --git a/components/admin/requests/payment-information/Card.tsx b/components/admin/requests/payment-information/Card.tsx index c6337956..4b889812 100644 --- a/components/admin/requests/payment-information/Card.tsx +++ b/components/admin/requests/payment-information/Card.tsx @@ -1,6 +1,6 @@ import { FC, useState } from 'react'; import { useQuery, useMutation } from '@tools/hooks/graphql'; -import { Box, Text, Divider, SimpleGrid, VStack, Button } from '@chakra-ui/react'; // Chakra UI +import { Box, Text, Divider, SimpleGrid, VStack, Button, HStack } from '@chakra-ui/react'; // Chakra UI import PermitHolderInfoCard from '@components/admin/LayoutCard'; // Custom Card component import EditPaymentDetailsModal from '@components/admin/requests/payment-information/EditModal'; // Edit modal import { @@ -69,6 +69,10 @@ const Card: FC = props => { paymentMethod, processingFee, donationAmount, + secondPaymentMethod, + secondProcessingFee, + secondDonationAmount, + hasSecondPaymentMethod, shippingAddressSameAsHomeAddress, shippingFullName, shippingAddressLine1, @@ -99,8 +103,12 @@ const Card: FC = props => { = props => { Fees - - - - Permit Fee - - - - - ${processingFee} - - - - - Donation - - - - - ${donationAmount} - - - - - Paid with {titlecase(paymentMethod)} - - - + + + + + Permit Fee + + + + + ${processingFee} + + + + + Donation + + + + + ${donationAmount} + + + + + Paid with {titlecase(paymentMethod)} + + + + {hasSecondPaymentMethod && ( + + + + Permit Fee + + + + + ${secondProcessingFee} + + + + + Donation + + + + + ${secondDonationAmount} + + + + + Paid with {titlecase(secondPaymentMethod ?? '')} + + + + )} + diff --git a/components/admin/requests/payment-information/Form.tsx b/components/admin/requests/payment-information/Form.tsx index 8956af05..c2e29003 100644 --- a/components/admin/requests/payment-information/Form.tsx +++ b/components/admin/requests/payment-information/Form.tsx @@ -27,6 +27,7 @@ export default function PaymentDetailsForm({ paymentInformation }: PaymentDetail required > + {'American Express'} {'Mastercard'} {'Visa'} {'Debit'} @@ -56,6 +57,57 @@ export default function PaymentDetailsForm({ paymentInformation }: PaymentDetail /> + + + {'Add a second payment method'} + + + {paymentInformation.hasSecondPaymentMethod && ( + + + + + {'Mastercard'} + {'Visa'} + {'Debit'} + {'Cash'} + {'Cheque'} + {'E-transfer'} + + + + + + + + + + + {'Donation '} + + {'(optional)'} + + + } + monetaryInput + /> + + + )} diff --git a/components/admin/requests/processing/TasksCard.tsx b/components/admin/requests/processing/TasksCard.tsx index 6d3d482f..4823db52 100644 --- a/components/admin/requests/processing/TasksCard.tsx +++ b/components/admin/requests/processing/TasksCard.tsx @@ -185,6 +185,7 @@ export default function ProcessingTasksCard({ applicationId }: ProcessingTasksCa shopifyConfirmationNumber, shopifyOrderNumber, donationAmount, + secondDonationAmount, processing: { status, appNumber, @@ -332,7 +333,7 @@ export default function ProcessingTasksCard({ applicationId }: ProcessingTasksCa = 20 + Number(donationAmount) + (Number(secondDonationAmount) || 0) >= 20 ? 'Generate invoice and donation receipt' : 'Generate invoice' } @@ -438,7 +439,7 @@ export default function ProcessingTasksCard({ applicationId }: ProcessingTasksCa _hover={!reviewRequestCompleted ? undefined : { bg: 'background.grayHover' }} color="black" onClick={() => { - Number(donationAmount) >= 20 + Number(donationAmount) + (Number(secondDonationAmount) || 0) >= 20 ? handleGenerateInvoice(true) : handleGenerateInvoice(false); }} diff --git a/components/applicant/renewals/IdentityVerification.tsx b/components/applicant/renewals/IdentityVerification.tsx index 2b512c69..754c2b5f 100644 --- a/components/applicant/renewals/IdentityVerification.tsx +++ b/components/applicant/renewals/IdentityVerification.tsx @@ -167,11 +167,9 @@ const IdentityVerification: FC = () => { - Please enter your date of birth in YYYY-MM-DD format. - - For example, if you were born on 20th August 1950, you would enter - 1950-08-20. - + { + 'If you are on a mobile device, please use the date picker to select your date of birth. You can click on the year and month at the top of the pop-up calendar to change them.' + } diff --git a/lib/applications/resolvers.ts b/lib/applications/resolvers.ts index fe11116d..208efe50 100644 --- a/lib/applications/resolvers.ts +++ b/lib/applications/resolvers.ts @@ -270,12 +270,17 @@ export const createNewApplication: Resolver< requiresWiderParkingSpaceReason, otherRequiresWiderParkingSpaceReason, donationAmount, + secondPaymentMethod, + secondProcessingFee, + secondDonationAmount, shippingPostalCode, billingPostalCode, applicantId, ...data } = input; + const { hasSecondPaymentMethod } = input; + const permitHolder = { firstName: input.firstName, middleName: input.middleName, @@ -340,6 +345,10 @@ export const createNewApplication: Resolver< paymentMethod: input.paymentMethod, processingFee: input.processingFee, donationAmount, + secondPaymentMethod, + secondProcessingFee, + secondDonationAmount, + hasSecondPaymentMethod: input.hasSecondPaymentMethod, shippingAddressSameAsHomeAddress: input.shippingAddressSameAsHomeAddress, shippingFullName: input.shippingFullName, shippingAddressLine1: input.shippingAddressLine1, @@ -386,6 +395,9 @@ export const createNewApplication: Resolver< data: { type: 'NEW', donationAmount: donationAmount || 0, + secondPaymentMethod: hasSecondPaymentMethod ? secondPaymentMethod : null, + secondProcessingFee: hasSecondPaymentMethod ? secondProcessingFee || 0 : null, + secondDonationAmount: hasSecondPaymentMethod ? secondDonationAmount || 0 : null, // Connect to applicant if applicant exists in DB ...(applicantId && { applicant: { @@ -496,6 +508,9 @@ export const createRenewalApplication: Resolver< requiresWiderParkingSpaceReason, otherRequiresWiderParkingSpaceReason, donationAmount, + secondPaymentMethod, + secondProcessingFee, + secondDonationAmount, shippingPostalCode, billingPostalCode, ...data @@ -511,6 +526,8 @@ export const createRenewalApplication: Resolver< addressLine2, city, paymentMethod, + processingFee, + hasSecondPaymentMethod, shippingAddressSameAsHomeAddress, shippingFullName, shippingAddressLine1, @@ -559,12 +576,14 @@ export const createRenewalApplication: Resolver< otherRequiresWiderParkingSpaceReason, }; - const { processingFee } = input; - const paymentInformation = { paymentMethod, processingFee, donationAmount, + secondPaymentMethod, + secondProcessingFee, + secondDonationAmount, + hasSecondPaymentMethod, shippingAddressSameAsHomeAddress, shippingFullName, shippingAddressLine1, @@ -612,6 +631,9 @@ export const createRenewalApplication: Resolver< data: { type: 'RENEWAL', donationAmount: donationAmount || 0, + secondPaymentMethod: hasSecondPaymentMethod ? secondPaymentMethod : null, + secondProcessingFee: hasSecondPaymentMethod ? secondProcessingFee || 0 : null, + secondDonationAmount: hasSecondPaymentMethod ? secondDonationAmount || 0 : null, phone: stripPhoneNumber(phone), ...data, postalCode: stripPostalCode(postalCode), @@ -799,9 +821,13 @@ export const createExternalRenewalApplication: Resolver< city: updatedAddress && city ? city : applicant.city, postalCode: updatedAddress && postalCode ? stripPostalCode(postalCode) : applicant.postalCode, + paymentMethod: 'SHOPIFY', processingFee: process.env.PROCESSING_FEE, donationAmount: donationAmount || 0, - paymentMethod: 'SHOPIFY', + secondPaymentMethod: null, + secondProcessingFee: null, + secondDonationAmount: null, + hasSecondPaymentMethod: false, // Set shipping address to be same as home address by default shippingAddressSameAsHomeAddress: true, shippingFullName: null, @@ -925,6 +951,9 @@ export const createReplacementApplication: Resolver< stolenPoliceOfficerName, eventDescription, donationAmount, + secondPaymentMethod, + secondProcessingFee, + secondDonationAmount, shippingPostalCode, billingPostalCode, ...data @@ -942,6 +971,8 @@ export const createReplacementApplication: Resolver< // Remaining Payment Information Fields paymentMethod, + processingFee, + hasSecondPaymentMethod, shippingAddressSameAsHomeAddress, shippingFullName, shippingAddressLine1, @@ -970,12 +1001,14 @@ export const createReplacementApplication: Resolver< postalCode, }; - const { processingFee } = input; - const paymentInformation = { paymentMethod, processingFee, donationAmount, + secondPaymentMethod, + secondProcessingFee, + secondDonationAmount, + hasSecondPaymentMethod, shippingAddressSameAsHomeAddress, shippingFullName, shippingAddressLine1, @@ -1043,6 +1076,9 @@ export const createReplacementApplication: Resolver< data: { type: 'REPLACEMENT', donationAmount: donationAmount || 0, + secondPaymentMethod: hasSecondPaymentMethod ? secondPaymentMethod : null, + secondProcessingFee: hasSecondPaymentMethod ? secondProcessingFee || 0 : null, + secondDonationAmount: hasSecondPaymentMethod ? secondDonationAmount || 0 : null, phone: stripPhoneNumber(phone), postalCode: stripPostalCode(postalCode), shippingPostalCode: shippingPostalCode && stripPostalCode(shippingPostalCode), @@ -1632,7 +1668,18 @@ export const updateApplicationPaymentInformation: Resolver< throw new ApolloError('Application payment information was unable to be updated'); } - const { id, donationAmount, shippingPostalCode, billingPostalCode, ...validatedData } = input; + const { + id, + donationAmount, + secondPaymentMethod, + secondProcessingFee, + secondDonationAmount, + shippingPostalCode, + billingPostalCode, + ...validatedData + } = input; + + const { hasSecondPaymentMethod } = input; const application = await prisma.application.findUnique({ where: { id }, @@ -1669,6 +1716,9 @@ export const updateApplicationPaymentInformation: Resolver< where: { id }, data: { donationAmount: donationAmount || 0, + secondPaymentMethod: hasSecondPaymentMethod ? secondPaymentMethod : null, + secondProcessingFee: hasSecondPaymentMethod ? secondProcessingFee || 0 : null, + secondDonationAmount: hasSecondPaymentMethod ? secondDonationAmount || 0 : null, shippingPostalCode: shippingPostalCode && stripPostalCode(shippingPostalCode), billingPostalCode: billingPostalCode && stripPostalCode(billingPostalCode), ...validatedData, diff --git a/lib/applications/schema.ts b/lib/applications/schema.ts index fdefb491..55e71dc4 100644 --- a/lib/applications/schema.ts +++ b/lib/applications/schema.ts @@ -34,6 +34,10 @@ export default gql` paymentMethod: PaymentType! processingFee: String! # Return monetary value as string donationAmount: String! # Return monetary value as string + secondPaymentMethod: PaymentType + secondProcessingFee: String # Return monetary value as string + secondDonationAmount: String # Return monetary value as string + hasSecondPaymentMethod: Boolean! paidThroughShopify: Boolean! shopifyPaymentStatus: ShopifyPaymentStatus shopifyConfirmationNumber: String @@ -140,6 +144,10 @@ export default gql` paymentMethod: PaymentType! processingFee: String! # Return monetary value as string donationAmount: String! # Return monetary value as string + secondPaymentMethod: PaymentType + secondProcessingFee: String # Return monetary value as string + secondDonationAmount: String # Return monetary value as string + hasSecondPaymentMethod: Boolean! paidThroughShopify: Boolean! shopifyPaymentStatus: ShopifyPaymentStatus shopifyConfirmationNumber: String @@ -221,6 +229,10 @@ export default gql` paymentMethod: PaymentType! processingFee: String! # Return monetary value as string donationAmount: String! # Return monetary value as string + secondPaymentMethod: PaymentType + secondProcessingFee: String # Return monetary value as string + secondDonationAmount: String # Return monetary value as string + hasSecondPaymentMethod: Boolean! paidThroughShopify: Boolean! shopifyPaymentStatus: ShopifyPaymentStatus shopifyConfirmationNumber: String @@ -283,6 +295,10 @@ export default gql` paymentMethod: PaymentType! processingFee: String! # Return monetary value as string donationAmount: String! # Return monetary value as string + secondPaymentMethod: PaymentType + secondProcessingFee: String # Return monetary value as string + secondDonationAmount: String # Return monetary value as string + hasSecondPaymentMethod: Boolean! paidThroughShopify: Boolean! shopifyPaymentStatus: ShopifyPaymentStatus shopifyConfirmationNumber: String @@ -390,6 +406,11 @@ export default gql` paymentMethod: PaymentType! processingFee: String! # Input monetary value as string donationAmount: String # Input monetary value as string + secondPaymentMethod: PaymentType + secondProcessingFee: String # Input monetary value as string + secondDonationAmount: String # Input monetary value as string + hasSecondPaymentMethod: Boolean! + # Shipping information shippingAddressSameAsHomeAddress: Boolean! shippingFullName: String @@ -457,6 +478,11 @@ export default gql` paymentMethod: PaymentType! processingFee: String # Input monetary value as string donationAmount: String # Input monetary value as string + secondPaymentMethod: PaymentType + secondProcessingFee: String # Input monetary value as string + secondDonationAmount: String # Input monetary value as string + hasSecondPaymentMethod: Boolean! + # Shipping information shippingAddressSameAsHomeAddress: Boolean! shippingFullName: String @@ -557,10 +583,14 @@ export default gql` stolenPoliceOfficerName: String eventDescription: String + # Payment information paymentMethod: PaymentType! - # Input monetary value as string - processingFee: String! - donationAmount: String + processingFee: String! # Input monetary value as string + donationAmount: String # Input monetary value as string + secondPaymentMethod: PaymentType + secondProcessingFee: String # Input monetary value as string + secondDonationAmount: String # Input monetary value as string + hasSecondPaymentMethod: Boolean! # Shipping information shippingAddressSameAsHomeAddress: Boolean! @@ -728,6 +758,10 @@ export default gql` paymentMethod: PaymentType! processingFee: String! donationAmount: String + secondPaymentMethod: PaymentType + secondProcessingFee: String + secondDonationAmount: String + hasSecondPaymentMethod: Boolean! # Shipping information shippingAddressSameAsHomeAddress: Boolean! diff --git a/lib/applications/utils.ts b/lib/applications/utils.ts index 017f88cd..a3bf1d1b 100644 --- a/lib/applications/utils.ts +++ b/lib/applications/utils.ts @@ -2,6 +2,7 @@ import { ApolloError } from 'apollo-server-micro'; import { Application, NewApplication, + PaymentType, RenewalApplication, ReplacementApplication, } from '@prisma/client'; @@ -43,10 +44,20 @@ export const flattenApplication = ( renewalApplication: RenewalApplication | null; replacementApplication: ReplacementApplication | null; } -): Omit & +): Omit< + Application, + | 'processingFee' + | 'donationAmount' + | 'secondPaymentMethod' + | 'secondProcessingFee' + | 'secondDonationAmount' +> & (NewApplication | RenewalApplication | ReplacementApplication) & { processingFee: string; donationAmount: string; + secondPaymentMethod: PaymentType | null; + secondProcessingFee: string | null; + secondDonationAmount: string | null; } & BillingAddress & ShippingAddress => { const { @@ -62,10 +73,14 @@ export const flattenApplication = ( postalCode, shippingAddressSameAsHomeAddress, billingAddressSameAsHomeAddress, + hasSecondPaymentMethod, } = application; const { processingFee, donationAmount, + secondPaymentMethod, + secondProcessingFee, + secondDonationAmount, shippingFullName, shippingAddressLine1, shippingAddressLine2, @@ -167,6 +182,17 @@ export const flattenApplication = ( ...newApplication, processingFee: processingFee.toString(), donationAmount: donationAmount.toString(), + secondPaymentMethod: hasSecondPaymentMethod ? secondPaymentMethod : null, + secondProcessingFee: hasSecondPaymentMethod + ? secondProcessingFee + ? secondProcessingFee.toString() + : '0' + : null, + secondDonationAmount: hasSecondPaymentMethod + ? secondDonationAmount + ? secondDonationAmount.toString() + : '0' + : null, }; } else if (type === 'RENEWAL') { if (!renewalApplication) { @@ -179,6 +205,17 @@ export const flattenApplication = ( ...renewalApplication, processingFee: processingFee.toString(), donationAmount: donationAmount.toString(), + secondPaymentMethod: hasSecondPaymentMethod ? secondPaymentMethod : null, + secondProcessingFee: hasSecondPaymentMethod + ? secondProcessingFee + ? secondProcessingFee.toString() + : '0' + : null, + secondDonationAmount: hasSecondPaymentMethod + ? secondDonationAmount + ? secondDonationAmount.toString() + : '0' + : null, }; } @@ -192,5 +229,16 @@ export const flattenApplication = ( ...replacementApplication, processingFee: processingFee.toString(), donationAmount: donationAmount.toString(), + secondPaymentMethod: hasSecondPaymentMethod ? secondPaymentMethod : null, + secondProcessingFee: hasSecondPaymentMethod + ? secondProcessingFee + ? secondProcessingFee.toString() + : '0' + : null, + secondDonationAmount: hasSecondPaymentMethod + ? secondDonationAmount + ? secondDonationAmount.toString() + : '0' + : null, }; }; diff --git a/lib/applications/validation.ts b/lib/applications/validation.ts index bc0e4447..010928ca 100644 --- a/lib/applications/validation.ts +++ b/lib/applications/validation.ts @@ -75,13 +75,46 @@ export const paymentInformationSchema = object({ paymentMethod: mixed() .oneOf(Object.values(PaymentType)) .required('Please select a payment method'), + processingFee: string() + .matches(monetaryValueRegex, 'Please enter a valid amount') + .required('Please enter a permit fee'), donationAmount: string() .matches(monetaryValueRegex, 'Please enter a valid amount') .nullable() .default(null), - processingFee: string() - .matches(monetaryValueRegex, 'Please enter a valid amount') - .required('Please enter a permit fee'), + secondPaymentMethod: mixed() + .nullable() + .default(null) + .when('hasSecondPaymentMethod', { + is: true, + then: mixed() + .oneOf(Object.values(PaymentType)) + .required('Please select a payment method') + .nullable() + .default(null), + }), + secondProcessingFee: string() + .nullable() + .default(null) + .when('hasSecondPaymentMethod', { + is: true, + then: string() + .matches(monetaryValueRegex, 'Please enter a valid amount') + .required('Please enter a permit fee') + .nullable() + .default(null), + }), + secondDonationAmount: string() + .nullable() + .default(null) + .when('hasSecondPaymentMethod', { + is: true, + then: string() + .matches(monetaryValueRegex, 'Please enter a valid amount') + .nullable() + .default(null), + }), + hasSecondPaymentMethod: bool().default(false), shippingAddressSameAsHomeAddress: bool().default(false), shippingFullName: string() .nullable() diff --git a/lib/graphql/schema.ts b/lib/graphql/schema.ts index b4f5a922..06a0ae3f 100644 --- a/lib/graphql/schema.ts +++ b/lib/graphql/schema.ts @@ -148,6 +148,7 @@ export default gql` } enum PaymentType { + AMEX MASTERCARD VISA ETRANSFER diff --git a/lib/graphql/types.ts b/lib/graphql/types.ts index be0edd5a..a960c540 100644 --- a/lib/graphql/types.ts +++ b/lib/graphql/types.ts @@ -92,6 +92,10 @@ export type Application = { paymentMethod: PaymentType; processingFee: Scalars['String']; donationAmount: Scalars['String']; + secondPaymentMethod: Maybe; + secondProcessingFee: Maybe; + secondDonationAmount: Maybe; + hasSecondPaymentMethod: Scalars['Boolean']; paidThroughShopify: Scalars['Boolean']; shopifyPaymentStatus: Maybe; shopifyConfirmationNumber: Maybe; @@ -324,6 +328,10 @@ export type CreateNewApplicationInput = { paymentMethod: PaymentType; processingFee: Scalars['String']; donationAmount: Maybe; + secondPaymentMethod: Maybe; + secondProcessingFee: Maybe; + secondDonationAmount: Maybe; + hasSecondPaymentMethod: Scalars['Boolean']; shippingAddressSameAsHomeAddress: Scalars['Boolean']; shippingFullName: Maybe; shippingAddressLine1: Maybe; @@ -377,6 +385,10 @@ export type CreateRenewalApplicationInput = { paymentMethod: PaymentType; processingFee: Scalars['String']; donationAmount: Maybe; + secondPaymentMethod: Maybe; + secondProcessingFee: Maybe; + secondDonationAmount: Maybe; + hasSecondPaymentMethod: Scalars['Boolean']; shippingAddressSameAsHomeAddress: Scalars['Boolean']; shippingFullName: Maybe; shippingAddressLine1: Maybe; @@ -423,6 +435,10 @@ export type CreateReplacementApplicationInput = { paymentMethod: PaymentType; processingFee: Scalars['String']; donationAmount: Maybe; + secondPaymentMethod: Maybe; + secondProcessingFee: Maybe; + secondDonationAmount: Maybe; + hasSecondPaymentMethod: Scalars['Boolean']; shippingAddressSameAsHomeAddress: Scalars['Boolean']; shippingFullName: Maybe; shippingAddressLine1: Maybe; @@ -868,6 +884,10 @@ export type NewApplication = Application & { paymentMethod: PaymentType; processingFee: Scalars['String']; donationAmount: Scalars['String']; + secondPaymentMethod: Maybe; + secondProcessingFee: Maybe; + secondDonationAmount: Maybe; + hasSecondPaymentMethod: Scalars['Boolean']; paidThroughShopify: Scalars['Boolean']; shopifyPaymentStatus: Maybe; shopifyConfirmationNumber: Maybe; @@ -902,6 +922,7 @@ export type PatientCondition = | 'OTHER'; export type PaymentType = + | 'AMEX' | 'MASTERCARD' | 'VISA' | 'ETRANSFER' @@ -1094,6 +1115,10 @@ export type RenewalApplication = Application & { paymentMethod: PaymentType; processingFee: Scalars['String']; donationAmount: Scalars['String']; + secondPaymentMethod: Maybe; + secondProcessingFee: Maybe; + secondDonationAmount: Maybe; + hasSecondPaymentMethod: Scalars['Boolean']; paidThroughShopify: Scalars['Boolean']; shopifyPaymentStatus: Maybe; shopifyConfirmationNumber: Maybe; @@ -1140,6 +1165,10 @@ export type ReplacementApplication = Application & { paymentMethod: PaymentType; processingFee: Scalars['String']; donationAmount: Scalars['String']; + secondPaymentMethod: Maybe; + secondProcessingFee: Maybe; + secondDonationAmount: Maybe; + hasSecondPaymentMethod: Scalars['Boolean']; paidThroughShopify: Scalars['Boolean']; shopifyPaymentStatus: Maybe; shopifyConfirmationNumber: Maybe; @@ -1371,6 +1400,10 @@ export type UpdateApplicationPaymentInformationInput = { paymentMethod: PaymentType; processingFee: Scalars['String']; donationAmount: Maybe; + secondPaymentMethod: Maybe; + secondProcessingFee: Maybe; + secondDonationAmount: Maybe; + hasSecondPaymentMethod: Scalars['Boolean']; shippingAddressSameAsHomeAddress: Scalars['Boolean']; shippingFullName: Maybe; shippingAddressLine1: Maybe; diff --git a/lib/invoices/utils.ts b/lib/invoices/utils.ts index dad788b3..120c937f 100644 --- a/lib/invoices/utils.ts +++ b/lib/invoices/utils.ts @@ -27,6 +27,9 @@ export const generateApplicationInvoicePdf = ( processingFee, paymentMethod, donationAmount, + secondProcessingFee, + secondPaymentMethod, + secondDonationAmount, } = application; const applicantName = formatFullName(firstName, middleName, lastName); const employeeInitials = `${session.firstName[0].toUpperCase()}${session.lastName[0].toUpperCase()}`; @@ -48,6 +51,24 @@ export const generateApplicationInvoicePdf = ( }); totalAmount = totalAmount.plus(donationAmount); } + if (secondPaymentMethod && secondProcessingFee && !secondProcessingFee.equals(0)) { + paymentItems.push({ + item: `PP # ${appNumber} second payment method`, + amount: secondProcessingFee, + paidBy: secondPaymentMethod, + subtotal: secondProcessingFee, + }); + totalAmount = totalAmount.plus(secondProcessingFee); + } + if (secondPaymentMethod && secondDonationAmount && !secondDonationAmount.equals(0)) { + paymentItems.push({ + item: 'Donation second payment method', + amount: secondDonationAmount, + paidBy: secondPaymentMethod, + subtotal: secondDonationAmount, + }); + totalAmount = totalAmount.plus(secondDonationAmount); + } const address = application.shippingAddressSameAsHomeAddress ? { addressLine1: application.addressLine1, @@ -265,6 +286,9 @@ export const generateDonationInvoicePdf = ( processingFee, paymentMethod, donationAmount, + secondProcessingFee, + secondPaymentMethod, + secondDonationAmount, email, createdAt, } = application; @@ -289,6 +313,24 @@ export const generateDonationInvoicePdf = ( }); totalAmount = totalAmount.plus(donationAmount); } + if (secondPaymentMethod && secondProcessingFee && !secondProcessingFee.equals(0)) { + paymentItems.push({ + item: `PP # ${appNumber} second payment method`, + amount: secondProcessingFee, + paidBy: secondPaymentMethod, + subtotal: secondProcessingFee, + }); + totalAmount = totalAmount.plus(secondProcessingFee); + } + if (secondPaymentMethod && secondDonationAmount && !secondDonationAmount.equals(0)) { + paymentItems.push({ + item: 'Donation second payment method', + amount: secondDonationAmount, + paidBy: secondPaymentMethod, + subtotal: secondDonationAmount, + }); + totalAmount = totalAmount.plus(secondDonationAmount); + } const address = application.shippingAddressSameAsHomeAddress ? { addressLine1: application.addressLine1, @@ -317,6 +359,7 @@ export const generateDonationInvoicePdf = ( dateDonationRecevied: createdAt, issuedBy: employeeInitials, donationAmount, + secondDonationAmount, paymentItems, totalAmount, address, @@ -354,6 +397,7 @@ const donationPdfDefinition = (input: { }>; totalAmount: Prisma.Decimal; donationAmount: Prisma.Decimal; + secondDonationAmount: Prisma.Decimal | null; address: { addressLine1: string; addressLine2: string | null; @@ -371,6 +415,7 @@ const donationPdfDefinition = (input: { permitType, receiptNumber, donationAmount, + secondDonationAmount, totalAmount, dateIssued, issuedBy, @@ -470,11 +515,14 @@ const donationPdfDefinition = (input: { body: [ [{ text: 'Date Donation Received:' }, formatDateYYYYMMDD(dateDonationRecevied)], [{ text: 'Donor Number:' }, `P${appNumber}`], - [{ text: 'Total Amount:' }, `$${donationAmount.toString()}`], + [ + { text: 'Total Amount:' }, + `$${donationAmount.plus(secondDonationAmount || 0).toString()}`, + ], [{ text: 'Value of Product / Services:\n\n' }, ''], [ { text: 'Eligible Amount of Donation for Tax Purposes:' }, - `$${donationAmount.toString()}`, + `$${donationAmount.plus(secondDonationAmount || 0).toString()}`, ], [{ text: '' }, ''], [{ text: 'Where Applicable', bold: true }, ''], @@ -519,7 +567,7 @@ const donationPdfDefinition = (input: { ]), styles: { header: { - fontSize: 25.5, + fontSize: 25, bold: true, alignment: 'center', lineHeight: 1.5, diff --git a/lib/reports/resolvers.ts b/lib/reports/resolvers.ts index 1ab1bb13..aa094600 100644 --- a/lib/reports/resolvers.ts +++ b/lib/reports/resolvers.ts @@ -217,6 +217,9 @@ export const generateApplicationsReport: Resolver< paymentMethod: true, processingFee: true, donationAmount: true, + secondPaymentMethod: true, + secondProcessingFee: true, + secondDonationAmount: true, applicant: { select: { id: true, @@ -246,6 +249,8 @@ export const generateApplicationsReport: Resolver< createdAt, processingFee, donationAmount, + secondProcessingFee, + secondDonationAmount, applicant, newApplication, permit, @@ -270,9 +275,12 @@ export const generateApplicationsReport: Resolver< dateOfBirth: dateOfBirth && formatDateYYYYMMDD(dateOfBirth), applicationDate: createdAt ? formatDateYYYYMMDDLocal(createdAt, true) : null, applicantName: formatFullName(firstName, middleName, lastName), - processingFee: `$${processingFee}`, - donationAmount: `$${donationAmount}`, - totalAmount: `$${processingFee.plus(donationAmount)}`, + processingFee: `$${Prisma.Decimal.add(processingFee, secondProcessingFee || 0)}`, + donationAmount: `$${Prisma.Decimal.add(donationAmount, secondDonationAmount || 0)}`, + totalAmount: `$${Prisma.Decimal.add( + Prisma.Decimal.add(processingFee, donationAmount), + Prisma.Decimal.add(secondProcessingFee || 0, secondDonationAmount || 0) + )}`, rcdPermitId: permit?.rcdPermitId ? `#${permit.rcdPermitId}` : null, }; } @@ -334,6 +342,7 @@ export const generateAccountantReport: Resolver< } const paymentTypeToString: Record = { + AMEX: 'American Express (Office)', MASTERCARD: 'Mastercard (Office)', VISA: 'Visa (Office)', CASH: 'Cash', @@ -362,6 +371,7 @@ export const generateAccountantReport: Resolver< paymentMethod: true, }, }); + const refundMethodGroups = await prisma.application.groupBy({ by: ['paymentMethod'], where: { @@ -417,51 +427,238 @@ export const generateAccountantReport: Resolver< }, }); - const csvAccountantReportRows = []; + const secondPaymentMethodGroups = await prisma.application.groupBy({ + by: ['secondPaymentMethod'], + where: { + createdAt: { + gte: startDate, + lt: endDate, + }, + hasSecondPaymentMethod: true, + }, + _sum: { + secondProcessingFee: true, + secondDonationAmount: true, + }, + _count: { + secondPaymentMethod: true, + }, + }); + + const secondRefundMethodGroups = await prisma.application.groupBy({ + by: ['secondPaymentMethod'], + where: { + createdAt: { + gte: startDate, + lt: endDate, + }, + applicationProcessing: { + paymentRefunded: true, + }, + hasSecondPaymentMethod: true, + }, + _sum: { + secondProcessingFee: true, + secondDonationAmount: true, + }, + _count: { + secondPaymentMethod: true, + }, + }); + + const secondTotalAggregate = await prisma.application.aggregate({ + where: { + createdAt: { + gte: startDate, + lt: endDate, + }, + hasSecondPaymentMethod: true, + }, + _sum: { + secondProcessingFee: true, + secondDonationAmount: true, + }, + _count: { + secondPaymentMethod: true, + }, + }); + + const secondRefundAggregate = await prisma.application.aggregate({ + where: { + createdAt: { + gte: startDate, + lt: endDate, + }, + applicationProcessing: { + paymentRefunded: true, + }, + hasSecondPaymentMethod: true, + }, + _sum: { + secondProcessingFee: true, + secondDonationAmount: true, + }, + _count: { + secondPaymentMethod: true, + }, + }); + + const summedPaymentMethodGroups: { + [key: string]: { + countIssued: Prisma.Decimal; + processingFee: Prisma.Decimal; + donationAmount: Prisma.Decimal; + refundAmount: Prisma.Decimal; + totalAmount: Prisma.Decimal; + }; + } = {}; + for (const paymentMethodGroup of paymentMethodGroups) { const refundMethodGroup = refundMethodGroups.find(group => { return group.paymentMethod == paymentMethodGroup.paymentMethod; }) || { _sum: { processingFee: 0, donationAmount: 0 } }; + const rowName = paymentTypeToString[paymentMethodGroup.paymentMethod]; + const countIssued = paymentMethodGroup._count.paymentMethod || 0; + const processingFee = paymentMethodGroup._sum.processingFee || 0; + const donationAmount = paymentMethodGroup._sum.donationAmount || 0; + const refundAmount = Prisma.Decimal.add( + refundMethodGroup._sum.processingFee || 0, + refundMethodGroup._sum.donationAmount || 0 + ); + const totalAmount = Prisma.Decimal.add( + Prisma.Decimal.add(processingFee, donationAmount), + -refundAmount + ); + + if (!summedPaymentMethodGroups[rowName]) { + summedPaymentMethodGroups[rowName] = { + countIssued: new Prisma.Decimal(0), + processingFee: new Prisma.Decimal(0), + donationAmount: new Prisma.Decimal(0), + refundAmount: new Prisma.Decimal(0), + totalAmount: new Prisma.Decimal(0), + }; + } + + summedPaymentMethodGroups[rowName].countIssued = Prisma.Decimal.add( + summedPaymentMethodGroups[rowName].countIssued, + countIssued + ); + summedPaymentMethodGroups[rowName].processingFee = Prisma.Decimal.add( + summedPaymentMethodGroups[rowName].processingFee, + processingFee + ); + summedPaymentMethodGroups[rowName].donationAmount = Prisma.Decimal.add( + summedPaymentMethodGroups[rowName].donationAmount, + donationAmount + ); + summedPaymentMethodGroups[rowName].refundAmount = Prisma.Decimal.add( + summedPaymentMethodGroups[rowName].refundAmount, + refundAmount + ); + summedPaymentMethodGroups[rowName].totalAmount = Prisma.Decimal.add( + summedPaymentMethodGroups[rowName].totalAmount, + totalAmount + ); + } + + for (const paymentMethodGroup of secondPaymentMethodGroups) { + const refundMethodGroup = secondRefundMethodGroups.find(group => { + return group.secondPaymentMethod == paymentMethodGroup.secondPaymentMethod; + }) || { _sum: { secondProcessingFee: 0, secondDonationAmount: 0 } }; + if (!paymentMethodGroup.secondPaymentMethod) { + continue; + } + const rowName = paymentTypeToString[paymentMethodGroup.secondPaymentMethod]; + const countIssued = paymentMethodGroup._count.secondPaymentMethod || 0; + const processingFee = paymentMethodGroup._sum.secondProcessingFee || 0; + const donationAmount = paymentMethodGroup._sum.secondDonationAmount || 0; + const refundAmount = Prisma.Decimal.add( + refundMethodGroup._sum.secondProcessingFee || 0, + refundMethodGroup._sum.secondDonationAmount || 0 + ); + const totalAmount = Prisma.Decimal.add( + Prisma.Decimal.add(processingFee, donationAmount), + -refundAmount + ); + + if (!summedPaymentMethodGroups[rowName]) { + summedPaymentMethodGroups[rowName] = { + countIssued: new Prisma.Decimal(0), + processingFee: new Prisma.Decimal(0), + donationAmount: new Prisma.Decimal(0), + refundAmount: new Prisma.Decimal(0), + totalAmount: new Prisma.Decimal(0), + }; + } + + summedPaymentMethodGroups[rowName].countIssued = Prisma.Decimal.add( + summedPaymentMethodGroups[rowName].countIssued, + countIssued + ); + summedPaymentMethodGroups[rowName].processingFee = Prisma.Decimal.add( + summedPaymentMethodGroups[rowName].processingFee, + processingFee + ); + summedPaymentMethodGroups[rowName].donationAmount = Prisma.Decimal.add( + summedPaymentMethodGroups[rowName].donationAmount, + donationAmount + ); + summedPaymentMethodGroups[rowName].refundAmount = Prisma.Decimal.add( + summedPaymentMethodGroups[rowName].refundAmount, + refundAmount + ); + summedPaymentMethodGroups[rowName].totalAmount = Prisma.Decimal.add( + summedPaymentMethodGroups[rowName].totalAmount, + totalAmount + ); + } + + const csvAccountantReportRows = []; + for (const rowName in summedPaymentMethodGroups) { + const row = summedPaymentMethodGroups[rowName]; csvAccountantReportRows.push({ - rowName: paymentTypeToString[paymentMethodGroup.paymentMethod], - countIssued: paymentMethodGroup._count.paymentMethod || 0, - processingFee: `$${paymentMethodGroup._sum.processingFee || 0}`, - donationAmount: `$${paymentMethodGroup._sum.donationAmount || 0}`, - refundAmount: `$${Prisma.Decimal.add( - refundMethodGroup._sum.donationAmount || 0, - refundMethodGroup._sum.processingFee || 0 - )}`, - totalAmount: `$${Prisma.Decimal.add( - Prisma.Decimal.add( - paymentMethodGroup._sum.donationAmount || 0, - paymentMethodGroup._sum.processingFee || 0 - ), - -Prisma.Decimal.add( - refundMethodGroup._sum.donationAmount || 0, - refundMethodGroup._sum.processingFee || 0 - ) - )}`, + rowName, + countIssued: row.countIssued, + processingFee: `$${row.processingFee}`, + donationAmount: `$${row.donationAmount}`, + refundAmount: `$${row.refundAmount}`, + totalAmount: `$${row.totalAmount}`, }); } + + const countIssued = + (totalAggregate._count.paymentMethod || 0) + + (secondTotalAggregate._count.secondPaymentMethod || 0); + const processingFee = Prisma.Decimal.add( + totalAggregate._sum.processingFee || 0, + secondTotalAggregate._sum.secondProcessingFee || 0 + ); + const donationAmount = Prisma.Decimal.add( + totalAggregate._sum.donationAmount || 0, + secondTotalAggregate._sum.secondDonationAmount || 0 + ); + const refundAmount = Prisma.Decimal.add( + Prisma.Decimal.add( + refundAggregate._sum.processingFee || 0, + refundAggregate._sum.donationAmount || 0 + ), + Prisma.Decimal.add( + secondRefundAggregate._sum.secondDonationAmount || 0, + secondRefundAggregate._sum.secondProcessingFee || 0 + ) + ); + const totalAmount = Prisma.Decimal.add( + Prisma.Decimal.add(processingFee, donationAmount), + -refundAmount + ); csvAccountantReportRows.push({ rowName: 'Total', - countIssued: totalAggregate._count.paymentMethod || 0, - processingFee: `$${totalAggregate._sum.processingFee || 0}`, - donationAmount: `$${totalAggregate._sum.donationAmount || 0}`, - refundAmount: `$${Prisma.Decimal.add( - refundAggregate._sum.donationAmount || 0, - refundAggregate._sum.processingFee || 0 - )}`, - totalAmount: `$${Prisma.Decimal.add( - Prisma.Decimal.add( - totalAggregate._sum.donationAmount || 0, - totalAggregate._sum.processingFee || 0 - ), - -Prisma.Decimal.add( - refundAggregate._sum.donationAmount || 0, - refundAggregate._sum.processingFee || 0 - ) - )}`, + countIssued, + processingFee: `$${processingFee}`, + donationAmount: `$${donationAmount}`, + refundAmount: `$${refundAmount}`, + totalAmount: `$${totalAmount}`, }); const csvHeaders = [ diff --git a/lib/scripts/oneoff/backup/populate-db-09-13-21.ts b/lib/scripts/oneoff/backup/populate-db-09-13-21.ts index bb501668..dae0220b 100644 --- a/lib/scripts/oneoff/backup/populate-db-09-13-21.ts +++ b/lib/scripts/oneoff/backup/populate-db-09-13-21.ts @@ -77,6 +77,8 @@ type CsvRow = { */ function getPaymentMethod(paymentMethod: string): PaymentType { switch (paymentMethod) { + case 'AMEX': + return PaymentType.Amex; case 'MC': return PaymentType.Mastercard; case 'VISA': diff --git a/lib/scripts/oneoff/populate-db-09-13-21.js b/lib/scripts/oneoff/populate-db-09-13-21.js index 7f994ef3..447a2edb 100644 --- a/lib/scripts/oneoff/populate-db-09-13-21.js +++ b/lib/scripts/oneoff/populate-db-09-13-21.js @@ -15,6 +15,8 @@ const prisma = new PrismaClient(); */ function getPaymentMethod(paymentMethod) { switch (paymentMethod) { + case 'AMEX': + return 'AMEX'; case 'MC': return 'MASTERCARD'; case 'VISA': diff --git a/lib/scripts/oneoff/populate-db-10-01-2021.js b/lib/scripts/oneoff/populate-db-10-01-2021.js index 1a4b1bd5..157bb17c 100644 --- a/lib/scripts/oneoff/populate-db-10-01-2021.js +++ b/lib/scripts/oneoff/populate-db-10-01-2021.js @@ -15,6 +15,8 @@ const prisma = new PrismaClient(); */ function getPaymentMethod(paymentMethod) { switch (paymentMethod) { + case 'AMEX': + return 'AMEX'; case 'MC': return 'MASTERCARD'; case 'VISA': diff --git a/package.json b/package.json index 9c3b56a0..841f85b5 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "dev": "next", "dev:pretty": "next | pino-pretty", "build": "next build", - "start": "next start -p $PORT", + "start": "prisma migrate deploy && prisma generate && next start -p $PORT", "type-check": "tsc", "generate-graphql-types": "node ./lib/scripts/generate-graphql-types.js", "reset-db": "sh lib/scripts/reset-db.sh", diff --git a/pages/index.tsx b/pages/index.tsx index 84bb7713..0f4d74cc 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -10,9 +10,6 @@ import { ListItem, Button, VStack, - Alert, - AlertDescription, - AlertIcon, } from '@chakra-ui/react'; // Chakra UI import Layout from '@components/applicant/Layout'; // Layout wrapper import FAQs from '@components/applicant/FAQs'; @@ -22,19 +19,6 @@ export default function Landing() { return ( - {Date.now() < new Date('2024-01-02T08:00:00.000Z').getTime() && ( - - - - - Please note that the RCD office will be closed between Dec 18 and Jan 1 and will - resume regular business hours on Jan 2. We will process applications received during - the holidays, but all other services will be unavailable. We apologize for any - inconvenience. - - - - )} {t('landing')} diff --git a/prisma/dev-seed-utils/applications.ts b/prisma/dev-seed-utils/applications.ts index c9c0d564..9a577fdc 100644 --- a/prisma/dev-seed-utils/applications.ts +++ b/prisma/dev-seed-utils/applications.ts @@ -25,6 +25,10 @@ const applications: Array = [ paymentMethod: 'CASH', processingFee: new Prisma.Decimal(31), donationAmount: new Prisma.Decimal(0), + secondPaymentMethod: null, + secondProcessingFee: null, + secondDonationAmount: null, + hasSecondPaymentMethod: false, shippingAddressSameAsHomeAddress: false, shippingFullName: 'Applicant One', shippingAddressLine1: '456 Vancouver Rd.', @@ -99,6 +103,10 @@ const applications: Array = [ paymentMethod: 'CHEQUE', processingFee: new Prisma.Decimal(31), donationAmount: new Prisma.Decimal(10), + secondPaymentMethod: null, + secondProcessingFee: null, + secondDonationAmount: null, + hasSecondPaymentMethod: false, shippingAddressSameAsHomeAddress: true, shippingFullName: null, shippingAddressLine1: null, @@ -156,6 +164,10 @@ const applications: Array = [ paymentMethod: 'CHEQUE', processingFee: new Prisma.Decimal(31), donationAmount: new Prisma.Decimal(100), + secondPaymentMethod: null, + secondProcessingFee: null, + secondDonationAmount: null, + hasSecondPaymentMethod: false, shippingAddressSameAsHomeAddress: true, shippingFullName: null, shippingAddressLine1: null, @@ -207,6 +219,10 @@ const applications: Array = [ paymentMethod: 'MASTERCARD', processingFee: new Prisma.Decimal(31), donationAmount: new Prisma.Decimal(0), + secondPaymentMethod: null, + secondProcessingFee: null, + secondDonationAmount: null, + hasSecondPaymentMethod: false, shippingAddressSameAsHomeAddress: true, shippingFullName: null, shippingAddressLine1: null, @@ -281,6 +297,10 @@ const applications: Array = [ paymentMethod: 'SHOPIFY', processingFee: new Prisma.Decimal(31), donationAmount: new Prisma.Decimal(20), + secondPaymentMethod: null, + secondProcessingFee: null, + secondDonationAmount: null, + hasSecondPaymentMethod: false, shippingAddressSameAsHomeAddress: true, shippingFullName: null, shippingAddressLine1: null, diff --git a/prisma/migrations/20240207054756_add_amex_payment_type/migration.sql b/prisma/migrations/20240207054756_add_amex_payment_type/migration.sql new file mode 100644 index 00000000..8c86ce71 --- /dev/null +++ b/prisma/migrations/20240207054756_add_amex_payment_type/migration.sql @@ -0,0 +1,2 @@ +-- AlterEnum +ALTER TYPE "paymenttype" ADD VALUE 'AMEX'; diff --git a/prisma/migrations/20240212032557_add_second_payment_method/migration.sql b/prisma/migrations/20240212032557_add_second_payment_method/migration.sql new file mode 100644 index 00000000..954f62f6 --- /dev/null +++ b/prisma/migrations/20240212032557_add_second_payment_method/migration.sql @@ -0,0 +1,5 @@ +-- AlterTable +ALTER TABLE "applications" ADD COLUMN "has_second_payment_method" BOOLEAN NOT NULL DEFAULT false, +ADD COLUMN "second_donation_amount" MONEY, +ADD COLUMN "second_payment_method" "paymenttype", +ADD COLUMN "second_processing_fee" MONEY; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 5c1c7ba8..2c547faa 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -58,6 +58,10 @@ model Application { paymentMethod PaymentType @map("payment_method") processingFee Decimal @map("processing_fee") @db.Money donationAmount Decimal @default(0.00) @map("donation_amount") @db.Money + secondPaymentMethod PaymentType? @map("second_payment_method") + secondProcessingFee Decimal? @map("second_processing_fee") @db.Money + secondDonationAmount Decimal? @map("second_donation_amount") @db.Money + hasSecondPaymentMethod Boolean @default(false) @map("has_second_payment_method") paidThroughShopify Boolean @default(false) @map("paid_through_shopify") shopifyPaymentStatus ShopifyPaymentStatus? @default(PENDING) @map("shopify_payment_status") shopifyConfirmationNumber String? @unique @map("shopify_confirmation_number") @db.VarChar(255) @@ -357,6 +361,7 @@ enum Gender { } enum PaymentType { + AMEX MASTERCARD VISA ETRANSFER diff --git a/prisma/schema.sql b/prisma/schema.sql index c7821727..189d1654 100644 --- a/prisma/schema.sql +++ b/prisma/schema.sql @@ -20,6 +20,7 @@ CREATE TYPE Province as ENUM( -- Create payment type enum CREATE TYPE PaymentType as ENUM( + 'AMEX', 'MASTERCARD', 'VISA', 'ETRANSFER', @@ -276,6 +277,10 @@ CREATE TABLE applications ( payment_method PaymentType NOT NULL, processing_fee MONEY NOT NULL, donation_amount MONEY NOT NULL DEFAULT 0.00, + second_payment_method PaymentType, + second_processing_fee MONEY, + second_donation_amount MONEY, + has_second_payment_method BOOLEAN NOT NULL DEFAULT false, paid_through_shopify BOOLEAN NOT NULL DEFAULT false, shopify_payment_status ShopifyPaymentStatus DEFAULT 'PENDING', shopify_confirmation_number VARCHAR(255) UNIQUE, diff --git a/prisma/types.ts b/prisma/types.ts index e9b92d47..7e5b48b8 100644 --- a/prisma/types.ts +++ b/prisma/types.ts @@ -80,6 +80,10 @@ export type UpsertApplication = Pick< | 'paymentMethod' | 'processingFee' | 'donationAmount' + | 'secondPaymentMethod' + | 'secondProcessingFee' + | 'secondDonationAmount' + | 'hasSecondPaymentMethod' | 'shippingAddressSameAsHomeAddress' | 'shippingFullName' | 'shippingAddressLine1' diff --git a/tools/admin/requests/create-new.ts b/tools/admin/requests/create-new.ts index be516b0c..ece35305 100644 --- a/tools/admin/requests/create-new.ts +++ b/tools/admin/requests/create-new.ts @@ -196,6 +196,10 @@ export const INITIAL_PAYMENT_DETAILS: PaymentInformationFormData = { paymentMethod: null, processingFee: '31', donationAmount: '', + secondPaymentMethod: null, + secondProcessingFee: null, + secondDonationAmount: null, + hasSecondPaymentMethod: false, shippingAddressSameAsHomeAddress: false, shippingFullName: '', shippingAddressLine1: '', diff --git a/tools/admin/requests/payment-information.ts b/tools/admin/requests/payment-information.ts index b81bafb1..e5bf5235 100644 --- a/tools/admin/requests/payment-information.ts +++ b/tools/admin/requests/payment-information.ts @@ -12,7 +12,10 @@ import { export type PaymentInformationFormData = Pick< Application, | 'donationAmount' + | 'secondDonationAmount' | 'processingFee' + | 'secondProcessingFee' + | 'hasSecondPaymentMethod' | 'shippingAddressSameAsHomeAddress' | 'shippingFullName' | 'shippingAddressLine1' @@ -29,6 +32,7 @@ export type PaymentInformationFormData = Pick< | 'billingPostalCode' > & { paymentMethod: PaymentType | null; + secondPaymentMethod: PaymentType | null; shippingProvince: Province | null; billingProvince: Province | null; }; @@ -39,6 +43,10 @@ export type PaymentInformationCardData = Pick< | 'paymentMethod' | 'processingFee' | 'donationAmount' + | 'secondPaymentMethod' + | 'secondProcessingFee' + | 'secondDonationAmount' + | 'hasSecondPaymentMethod' | 'shippingAddressSameAsHomeAddress' | 'shippingFullName' | 'shippingAddressLine1' @@ -65,6 +73,10 @@ export const GET_PAYMENT_INFORMATION = gql` paymentMethod processingFee donationAmount + secondPaymentMethod + secondProcessingFee + secondDonationAmount + hasSecondPaymentMethod shippingAddressSameAsHomeAddress shippingFullName shippingAddressLine1 diff --git a/tools/admin/requests/processing-tasks-card.ts b/tools/admin/requests/processing-tasks-card.ts index 0fadca2f..70585efd 100644 --- a/tools/admin/requests/processing-tasks-card.ts +++ b/tools/admin/requests/processing-tasks-card.ts @@ -31,6 +31,7 @@ export const GET_APPLICATION_PROCESSING = gql` shopifyConfirmationNumber shopifyOrderNumber donationAmount + secondDonationAmount processing { status appNumber @@ -95,7 +96,11 @@ export type GetApplicationProcessingRequest = QueryApplicationArgs; export type GetApplicationProcessingResponse = { application: Pick< Application, - 'paidThroughShopify' | 'shopifyConfirmationNumber' | 'shopifyOrderNumber' | 'donationAmount' + | 'paidThroughShopify' + | 'shopifyConfirmationNumber' + | 'shopifyOrderNumber' + | 'donationAmount' + | 'secondDonationAmount' > & { processing: Pick< ApplicationProcessing,