Skip to content

Commit 9342980

Browse files
authored
Merge pull request #1708 from Northeastern-Electric-Racing/#1611-deny-reimbursement-request-frontend
#1611 Deny Reimbursement Request Frontent
2 parents d7b5228 + 2ce8b82 commit 9342980

6 files changed

Lines changed: 105 additions & 4 deletions

File tree

src/backend/src/services/reimbursement-requests.services.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -694,6 +694,10 @@ export default class ReimbursementRequestService {
694694
throw new HttpException(400, 'This reimbursement request has already been approved');
695695
}
696696

697+
if (reimbursementRequest.reimbursementStatuses.some((status) => status.type === ReimbursementStatusType.DENIED)) {
698+
throw new HttpException(400, 'This reimbursement request has already been denied');
699+
}
700+
697701
const reimbursementStatus = await prisma.reimbursement_Status.create({
698702
data: {
699703
type: ReimbursementStatusType.SABO_SUBMITTED,

src/frontend/src/apis/finance.api.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,16 @@ export const approveReimbursementRequest = (id: string) => {
158158
return axios.post(apiUrls.financeApproveReimbursementRequest(id));
159159
};
160160

161+
/**
162+
* Deny Reimbursement Request
163+
*
164+
* @param id of the reimbursement request being denied by finance
165+
* @returns the denied reimbursement status
166+
*/
167+
export const denyReimbursementRequest = (id: string) => {
168+
return axios.post(apiUrls.financeDenyReimbursementRequest(id));
169+
};
170+
161171
/**
162172
* Downloads a given fileId from google drive into a blob
163173
*

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

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
approveReimbursementRequest,
88
createReimbursementRequest,
99
deleteReimbursementRequest,
10+
denyReimbursementRequest,
1011
downloadBlobsToPdf,
1112
downloadGoogleImage,
1213
editReimbursementRequest,
@@ -262,6 +263,28 @@ export const useApproveReimbursementRequest = (id: string) => {
262263
);
263264
};
264265

266+
/**
267+
* Custom react hook to deny a reimbursement request for the finance team
268+
*
269+
* @param id id of the reimbursement request to deny
270+
* @returns the denied reimbursement request status
271+
*/
272+
export const useDenyReimbursementRequest = (id: string) => {
273+
const queryClient = useQueryClient();
274+
return useMutation<ReimbursementStatus, Error>(
275+
['reimbursement-requests', 'edit'],
276+
async () => {
277+
const { data } = await denyReimbursementRequest(id);
278+
return data;
279+
},
280+
{
281+
onSuccess: () => {
282+
queryClient.invalidateQueries(['reimbursement-requests', id]);
283+
}
284+
}
285+
);
286+
};
287+
265288
/**
266289
* Custom react hook to download images from google drive into a pdf
267290
*

src/frontend/src/pages/FinancePage/ReimbursementRequestDetailPage/ReimbursementRequestDetailsView.tsx

Lines changed: 56 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import { expenseTypePipe } from '../../../utils/pipes';
77
import { Edit } from '@mui/icons-material';
88
import CheckIcon from '@mui/icons-material/Check';
9+
import CloseIcon from '@mui/icons-material/Close';
910
import ConfirmationNumberIcon from '@mui/icons-material/ConfirmationNumber';
1011
import DeleteIcon from '@mui/icons-material/Delete';
1112
import LocalShippingIcon from '@mui/icons-material/LocalShipping';
@@ -18,7 +19,11 @@ import ActionsMenu, { ButtonInfo } from '../../../components/ActionsMenu';
1819
import NERModal from '../../../components/NERModal';
1920
import PageLayout from '../../../components/PageLayout';
2021
import VerticalDetailDisplay from '../../../components/VerticalDetailDisplay';
21-
import { useDeleteReimbursementRequest, useMarkReimbursementRequestAsDelivered } from '../../../hooks/finance.hooks';
22+
import {
23+
useDeleteReimbursementRequest,
24+
useDenyReimbursementRequest,
25+
useMarkReimbursementRequestAsDelivered
26+
} from '../../../hooks/finance.hooks';
2227
import { useToast } from '../../../hooks/toasts.hooks';
2328
import { useCurrentUser } from '../../../hooks/users.hooks';
2429
import {
@@ -33,7 +38,9 @@ import {
3338
imageDownloadUrl,
3439
imageFileUrl,
3540
isReimbursementRequestAdvisorApproved,
36-
isReimbursementRequestSaboSubmitted
41+
isReimbursementRequestReimbursed,
42+
isReimbursementRequestSaboSubmitted,
43+
isReimbursementRequestDenied
3744
} from '../../../utils/reimbursement-request.utils';
3845
import { routes } from '../../../utils/routes';
3946
import AddSABONumberModal from './AddSABONumberModal';
@@ -53,11 +60,13 @@ const ReimbursementRequestDetailsView: React.FC<ReimbursementRequestDetailsViewP
5360
const [addSaboNumberModalShow, setAddSaboNumberModalShow] = useState<boolean>(false);
5461
const toast = useToast();
5562
const [showDeleteModal, setShowDeleteModal] = useState(false);
63+
const [showDenyModal, setShowDenyModal] = useState(false);
5664
const [showMarkDelivered, setShowMarkDelivered] = useState(false);
5765
const [showSubmitToSaboModal, setShowSubmitToSaboModal] = useState(false);
5866
const { mutateAsync: deleteReimbursementRequest } = useDeleteReimbursementRequest(
5967
reimbursementRequest.reimbursementRequestId
6068
);
69+
const { mutateAsync: denyReimbursementRequest } = useDenyReimbursementRequest(reimbursementRequest.reimbursementRequestId);
6170
const { mutateAsync: markDelivered } = useMarkReimbursementRequestAsDelivered(reimbursementRequest.reimbursementRequestId);
6271

6372
const handleDelete = () => {
@@ -71,6 +80,17 @@ const ReimbursementRequestDetailsView: React.FC<ReimbursementRequestDetailsViewP
7180
}
7281
};
7382

83+
const handleDeny = () => {
84+
try {
85+
denyReimbursementRequest();
86+
setShowDenyModal(false);
87+
} catch (e: unknown) {
88+
if (e instanceof Error) {
89+
toast.error(e.message, 3000);
90+
}
91+
}
92+
};
93+
7494
const handleMarkDelivered = () => {
7595
try {
7696
markDelivered();
@@ -97,6 +117,21 @@ const ReimbursementRequestDetailsView: React.FC<ReimbursementRequestDetailsViewP
97117
);
98118
};
99119

120+
const DenyModal = () => {
121+
return (
122+
<NERModal
123+
open={showDenyModal}
124+
onHide={() => setShowDenyModal(false)}
125+
title="Warning!"
126+
cancelText="No"
127+
submitText="Yes"
128+
onSubmit={handleDeny}
129+
>
130+
<Typography>Are you sure you want to deny this reimbursement request?</Typography>
131+
</NERModal>
132+
);
133+
};
134+
100135
const MarkDeliveredModal = () => (
101136
<NERModal
102137
open={showMarkDelivered}
@@ -225,13 +260,29 @@ const ReimbursementRequestDetailsView: React.FC<ReimbursementRequestDetailsViewP
225260
title: 'Approve',
226261
onClick: () => setShowSubmitToSaboModal(true),
227262
icon: <CheckIcon />,
228-
disabled: !user.isFinance || isReimbursementRequestSaboSubmitted(reimbursementRequest)
263+
disabled:
264+
!user.isFinance ||
265+
isReimbursementRequestSaboSubmitted(reimbursementRequest) ||
266+
isReimbursementRequestDenied(reimbursementRequest)
267+
},
268+
{
269+
title: 'Deny',
270+
onClick: () => setShowDenyModal(true),
271+
icon: <CloseIcon />,
272+
disabled:
273+
!user.isFinance ||
274+
isReimbursementRequestReimbursed(reimbursementRequest) ||
275+
isReimbursementRequestDenied(reimbursementRequest)
229276
}
230277
];
231278

232279
return (
233280
<PageLayout
234-
title={`${fullNamePipe(reimbursementRequest.recipient)}'s Reimbursement Request`}
281+
title={`${
282+
isReimbursementRequestDenied(reimbursementRequest)
283+
? `${fullNamePipe(reimbursementRequest.recipient)}'s Reimbursement Request - Denied`
284+
: `${fullNamePipe(reimbursementRequest.recipient)}'s Reimbursement Request`
285+
}`}
235286
previousPages={[
236287
{
237288
name: 'Finance',
@@ -241,6 +292,7 @@ const ReimbursementRequestDetailsView: React.FC<ReimbursementRequestDetailsViewP
241292
headerRight={<ActionsMenu buttons={buttons} />}
242293
>
243294
<DeleteModal />
295+
<DenyModal />
244296
<MarkDeliveredModal />
245297
<SubmitToSaboModal
246298
open={showSubmitToSaboModal}

src/frontend/src/utils/reimbursement-request.utils.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,16 @@ export const isReimbursementRequestSaboSubmitted = (reimbursementRequest: Reimbu
8686
.includes(ReimbursementStatusType.SABO_SUBMITTED);
8787
};
8888

89+
export const isReimbursementRequestDenied = (reimbursementRequest: ReimbursementRequest) => {
90+
return reimbursementRequest.reimbursementStatuses.map((status) => status.type).includes(ReimbursementStatusType.DENIED);
91+
};
92+
93+
export const isReimbursementRequestReimbursed = (reimbursementRequest: ReimbursementRequest) => {
94+
return reimbursementRequest.reimbursementStatuses
95+
.map((status) => status.type)
96+
.includes(ReimbursementStatusType.REIMBURSED);
97+
};
98+
8999
export const getReimbursementRequestDateSubmittedToSabo = (reimbursementRequest: ReimbursementRequest) => {
90100
const saboStatus = reimbursementRequest.reimbursementStatuses.find(
91101
(status) => status.type === ReimbursementStatusType.SABO_SUBMITTED

src/frontend/src/utils/urls.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ const financeSetSaboNumber = (id: string) => `${financeEndpoints()}/${id}/set-sa
101101
const financeDeleteReimbursement = (id: string) => `${financeEndpoints()}/${id}/delete`;
102102
const financeMarkAsDelivered = (id: string) => `${financeEndpoints()}/${id}/delivered`;
103103
const financeApproveReimbursementRequest = (id: string) => `${financeEndpoints()}/${id}/approve`;
104+
const financeDenyReimbursementRequest = (id: string) => `${financeEndpoints()}/${id}/deny`;
104105
const financeGetPendingAdvisorList = () => `${financeEndpoints()}/pending-advisor/list`;
105106
const financeSendPendingAdvisorList = () => `${financeEndpoints()}/pending-advisor/send`;
106107
const financeEditExpenseType = (expenseId: string) => `${financeEndpoints()}/${expenseId}/expense-types/edit`;
@@ -204,6 +205,7 @@ export const apiUrls = {
204205
financeDeleteReimbursement,
205206
financeMarkAsDelivered,
206207
financeApproveReimbursementRequest,
208+
financeDenyReimbursementRequest,
207209
financeGetPendingAdvisorList,
208210
financeSendPendingAdvisorList,
209211
financeEditExpenseType,

0 commit comments

Comments
 (0)