Skip to content

Commit 8aeecbc

Browse files
authored
Merge pull request #2092 from Northeastern-Electric-Racing/2082-finance-fixes-add-ability-to-generate-cash-vs-budget-receipts
#2082 add ability to generate cash vs budget receipts
2 parents 03d9a76 + ca1d912 commit 8aeecbc

3 files changed

Lines changed: 63 additions & 38 deletions

File tree

src/frontend/src/hooks/finance.hooks.ts

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,13 @@ export interface EditReimbursementRequestPayload extends CreateReimbursementRequ
5656
receiptPictures: ReimbursementReceiptCreateArgs[];
5757
}
5858

59+
export interface DownloadReceiptsFormInput {
60+
fileIds: string[];
61+
startDate: Date;
62+
endDate: Date;
63+
refundSource: string;
64+
}
65+
5966
export interface ExpenseTypePayload {
6067
code: number;
6168
name: string;
@@ -265,7 +272,6 @@ export const useSingleReimbursementRequest = (id: string) => {
265272

266273
/**
267274
* Custom react hook to delete a single reimbursement request
268-
*
269275
* @param id id of the reimbursement request to delete
270276
* @returns the deleted reimbursement request
271277
*/
@@ -334,13 +340,19 @@ export const useDenyReimbursementRequest = (id: string) => {
334340
*
335341
* @param fileIds The google file ids to fetch the images for
336342
*/
337-
export const useDownloadPDFOfImages = (startDate: Date, endDate: Date) => {
338-
return useMutation(['reimbursement-requests'], async (formData: { fileIds: string[] }) => {
343+
export const useDownloadPDFOfImages = () => {
344+
return useMutation(['reimbursement-requests'], async (formData: DownloadReceiptsFormInput) => {
339345
const promises = formData.fileIds.map((fileId) => {
340346
return downloadGoogleImage(fileId);
341347
});
348+
342349
const blobs = await Promise.all(promises);
343-
await downloadBlobsToPdf(blobs, `receipts-${startDate.toLocaleDateString()}-${endDate.toLocaleDateString()}.pdf`);
350+
const pdfName = `${formData.startDate.toLocaleDateString()}-${formData.endDate.toLocaleDateString()}.pdf`;
351+
352+
const pdfFileName =
353+
formData.refundSource !== 'BOTH' ? `receipts-${formData.refundSource}-${pdfName}` : `receipts-${pdfName}`;
354+
355+
await downloadBlobsToPdf(blobs, pdfFileName);
344356
});
345357
};
346358

src/frontend/src/pages/FinancePage/FinanceComponents/GenerateReceiptsModal.tsx

Lines changed: 47 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,64 +1,67 @@
1-
import { FormControl, FormLabel, TextField } from '@mui/material';
1+
import { FormControl, FormLabel, MenuItem, Select, TextField } from '@mui/material';
22
import NERFormModal from '../../../components/NERFormModal';
33
import { Controller, useForm } from 'react-hook-form';
44
import { DatePicker } from '@mui/x-date-pickers';
55
import LoadingIndicator from '../../../components/LoadingIndicator';
66
import { yupResolver } from '@hookform/resolvers/yup';
77
import * as yup from 'yup';
88
import { useDownloadPDFOfImages } from '../../../hooks/finance.hooks';
9-
import { useState } from 'react';
109
import { useToast } from '../../../hooks/toasts.hooks';
1110
import { ReimbursementRequest } from 'shared';
11+
import { useState } from 'react';
1212

1313
const schema = yup.object().shape({
1414
startDate: yup.date().required('Start Date is required'),
15-
endDate: yup.date().min(yup.ref('startDate'), `end date can't be before start date`).required('End Date is required')
15+
endDate: yup.date().min(yup.ref('startDate'), `end date can't be before start date`).required('End Date is required'),
16+
refundSource: yup.string().required()
1617
});
1718

1819
interface GenerateReceiptsFormInput {
1920
startDate: Date;
2021
endDate: Date;
22+
refundSource: string;
2123
}
2224

2325
interface GenerateReceiptsModalProps {
2426
open: boolean;
2527
setOpen: (val: boolean) => void;
2628
allReimbursementRequests?: ReimbursementRequest[];
27-
startDate: Date;
28-
endDate: Date;
29-
setStartDate: (val: Date) => void;
30-
setEndDate: (val: Date) => void;
3129
}
3230

33-
const GenerateReceiptsModal = ({
34-
open,
35-
setOpen,
36-
allReimbursementRequests,
37-
startDate,
38-
setStartDate,
39-
endDate,
40-
setEndDate
41-
}: GenerateReceiptsModalProps) => {
31+
const GenerateReceiptsModal = ({ open, setOpen, allReimbursementRequests }: GenerateReceiptsModalProps) => {
4232
const toast = useToast();
43-
const [startDatePickerOpen, setStartDatePickerOpen] = useState(false);
33+
4434
const [endDatePickerOpen, setEndDatePickerOpen] = useState(false);
35+
const [startDatePickerOpen, setStartDatePickerOpen] = useState(false);
36+
37+
const { mutateAsync, isLoading } = useDownloadPDFOfImages();
4538

46-
const { mutateAsync, isLoading } = useDownloadPDFOfImages(startDate, endDate);
39+
const refundSourceOptions = ['BUDGET', 'CASH', 'BOTH'];
4740

4841
const onGenerateReceiptsSubmit = async (data: GenerateReceiptsFormInput) => {
4942
if (!allReimbursementRequests) return;
43+
5044
const filteredRequests = allReimbursementRequests
5145
.filter(
5246
(val: ReimbursementRequest) => new Date(val.dateCreated.toDateString()) >= new Date(data.startDate.toDateString())
5347
)
5448
.filter(
5549
(val: ReimbursementRequest) => new Date(val.dateCreated.toDateString()) <= new Date(data.endDate.toDateString())
5650
)
57-
.filter((val: ReimbursementRequest) => !val.dateDeleted);
51+
.filter((val: ReimbursementRequest) => !val.dateDeleted)
52+
.filter(
53+
(val: ReimbursementRequest) =>
54+
!val.dateDeleted && (data.refundSource === 'BOTH' || val.account === data.refundSource)
55+
);
56+
5857
const receipts = filteredRequests?.flatMap((request: ReimbursementRequest) => request.receiptPictures);
58+
5959
try {
6060
await mutateAsync({
61-
fileIds: receipts.map((receipt) => receipt.googleFileId)
61+
fileIds: receipts.map((receipt) => receipt.googleFileId),
62+
startDate: data.startDate,
63+
endDate: data.endDate,
64+
refundSource: data.refundSource
6265
});
6366
} catch (error: unknown) {
6467
if (error instanceof Error) {
@@ -74,7 +77,12 @@ const GenerateReceiptsModal = ({
7477
reset,
7578
formState: { errors, isValid }
7679
} = useForm({
77-
resolver: yupResolver(schema)
80+
resolver: yupResolver(schema),
81+
defaultValues: {
82+
startDate: new Date(),
83+
endDate: new Date(),
84+
refundSource: refundSourceOptions[2]
85+
}
7886
});
7987

8088
return (
@@ -104,9 +112,7 @@ const GenerateReceiptsModal = ({
104112
onClose={() => setStartDatePickerOpen(false)}
105113
onOpen={() => setStartDatePickerOpen(true)}
106114
onChange={(newValue) => {
107-
const newDate = newValue ?? new Date();
108-
setStartDate(newDate);
109-
onChange(newDate);
115+
onChange(newValue ?? new Date());
110116
}}
111117
PopperProps={{
112118
placement: 'right'
@@ -136,9 +142,7 @@ const GenerateReceiptsModal = ({
136142
onClose={() => setEndDatePickerOpen(false)}
137143
onOpen={() => setEndDatePickerOpen(true)}
138144
onChange={(newValue) => {
139-
const newDate = newValue ?? new Date();
140-
setEndDate(newDate);
141-
onChange(newDate);
145+
onChange(newValue ?? new Date());
142146
}}
143147
PopperProps={{
144148
placement: 'right'
@@ -156,6 +160,22 @@ const GenerateReceiptsModal = ({
156160
)}
157161
/>
158162
</FormControl>
163+
<FormControl fullWidth>
164+
<FormLabel>Receipt Type</FormLabel>
165+
<Controller
166+
name="refundSource"
167+
control={control}
168+
render={({ field: { onChange, value } }) => (
169+
<Select value={value} onChange={(event) => onChange(event.target.value)}>
170+
{refundSourceOptions.map((status) => (
171+
<MenuItem key={status} value={status}>
172+
{status}
173+
</MenuItem>
174+
))}
175+
</Select>
176+
)}
177+
/>
178+
</FormControl>
159179
</>
160180
)}
161181
</NERFormModal>

src/frontend/src/pages/FinancePage/FinancePage.tsx

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,6 @@ const FinancePage = () => {
6262
const [accountCreditModalShow, setAccountCreditModalShow] = useState<boolean>(false);
6363
const [showTotalAmountSpent, setShowTotalAmountSpent] = useState(false);
6464

65-
const [startDate, setStartDate] = useState(new Date());
66-
const [endDate, setEndDate] = useState(new Date());
67-
6865
if (isFinance && allReimbursementRequestsIsError) return <ErrorPage message={allReimbursementRequestsError?.message} />;
6966
if (userReimbursementRequestIsError) return <ErrorPage message={userReimbursementRequestError?.message} />;
7067
if (isFinance && allPendingAdvisorListIsError) return <ErrorPage message={allPendingAdvisorListError?.message} />;
@@ -164,10 +161,6 @@ const FinancePage = () => {
164161
open={showGenerateReceipts}
165162
setOpen={setShowGenerateReceipts}
166163
allReimbursementRequests={allReimbursementRequests}
167-
startDate={startDate}
168-
endDate={endDate}
169-
setStartDate={setStartDate}
170-
setEndDate={setEndDate}
171164
/>
172165
<Grid container>
173166
<Grid item xs={12} sm={12} md={4}>

0 commit comments

Comments
 (0)