Skip to content

Commit 04f3364

Browse files
committed
misc changes
1 parent 59ee09b commit 04f3364

9 files changed

Lines changed: 94 additions & 47 deletions

File tree

src/backend/src/services/organizations.services.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { getProjectPreviewQueryArgs } from '../prisma-query-args/projects.query-
1717
import { projectPreviewTransformer } from '../transformers/projects.transformer';
1818
import { getUserQueryArgs } from '../prisma-query-args/user.query-args';
1919
import { userTransformer } from '../transformers/user.transformer';
20+
import { organizationTransformer } from '../transformers/organizationTransformer';
2021

2122
export default class OrganizationsService {
2223
/**
@@ -37,7 +38,7 @@ export default class OrganizationsService {
3738
include: {
3839
contacts: {
3940
include: {
40-
user: true
41+
user: true
4142
}
4243
}
4344
}
@@ -51,7 +52,7 @@ export default class OrganizationsService {
5152
throw new DeletedException('Organization', organizationId);
5253
}
5354

54-
return organization;
55+
return organizationTransformer(organization);
5556
}
5657

5758
/**
@@ -526,7 +527,7 @@ export default class OrganizationsService {
526527
});
527528

528529
if (users.length !== userIdsNoDuplicates.length) {
529-
throw new NotFoundException('User', 'one or more user IDs');
530+
throw new HttpException(404, 'One or more users not found');
530531
}
531532

532533
const updatedOrg = await prisma.organization.update({

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

Lines changed: 75 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ import {
3333
validateReimbursementProducts,
3434
validateUserEditRRPermissions,
3535
validateRefund,
36-
isUserHeadOrOnFinance,
37-
validateUserIsPartOfFinanceTeamOrHead
36+
validateUserIsPartOfFinanceTeamOrHead,
37+
isUserOnFinanceTeam
3838
} from '../utils/reimbursement-requests.utils';
3939
import {
4040
AccessDeniedAdminOnlyException,
@@ -171,7 +171,12 @@ export default class ReimbursementRequestService {
171171
* @returns All the reimbursements in the database
172172
*/
173173
static async getAllReimbursements(user: User, organization: Organization): Promise<Reimbursement[]> {
174-
await isUserHeadOrOnFinance(user, organization.organizationId);
174+
const isUserAuthorized =
175+
(await isUserOnFinanceTeam(user, organization.organizationId)) ||
176+
(await userHasPermission(user.userId, organization.organizationId, isHead));
177+
if (!isUserAuthorized) {
178+
throw new AccessDeniedException(`You are not a member of the finance team!`);
179+
}
175180

176181
const reimbursements = await prisma.reimbursement.findMany({
177182
where: {
@@ -789,7 +794,12 @@ export default class ReimbursementRequestService {
789794
* @returns the 'deleted' account code
790795
*/
791796
static async deleteAccountCode(accountCodeId: string, submitter: User, organization: Organization) {
792-
await isUserHeadOrOnFinance(submitter, organization.organizationId);
797+
const isUserAuthorized =
798+
(await isUserOnFinanceTeam(submitter, organization.organizationId)) ||
799+
(await userHasPermission(submitter.userId, organization.organizationId, isHead));
800+
if (!isUserAuthorized) {
801+
throw new AccessDeniedException(`You are not a member of the finance team!`);
802+
}
793803

794804
const accountCode = await ReimbursementRequestService.getSingleAccountCode(accountCodeId, organization);
795805

@@ -896,7 +906,12 @@ export default class ReimbursementRequestService {
896906
* @returns an array of the prisma version of the reimbursement requests transformed to the shared version
897907
*/
898908
static async getAllReimbursementRequests(user: User, organization: Organization): Promise<ReimbursementRequest[]> {
899-
await isUserHeadOrOnFinance(user, organization.organizationId);
909+
const isUserAuthorized =
910+
(await isUserOnFinanceTeam(user, organization.organizationId)) ||
911+
(await userHasPermission(user.userId, organization.organizationId, isHead));
912+
if (!isUserAuthorized) {
913+
throw new AccessDeniedException(`You are not a member of the finance team!`);
914+
}
900915

901916
const reimbursementRequests = await prisma.reimbursement_Request.findMany({
902917
where: { dateDeleted: null, accountCode: { organizationId: organization.organizationId } },
@@ -1106,12 +1121,15 @@ export default class ReimbursementRequestService {
11061121
},
11071122
...getReimbursementStatusQueryArgs(organization.organizationId)
11081123
});
1109-
1110-
await sendReimbursementRequestLeadershipApprovedNotification(
1111-
reimbursementRequest.notificationSlackThreads,
1112-
submitter.userId,
1113-
reimbursementRequest.recipientId
1114-
);
1124+
try {
1125+
await sendReimbursementRequestLeadershipApprovedNotification(
1126+
reimbursementRequest.notificationSlackThreads,
1127+
submitter.userId,
1128+
reimbursementRequest.recipientId
1129+
);
1130+
} catch (e: unknown) {
1131+
console.error('Error sending reimbursement request leadership approved notification:', e);
1132+
}
11151133

11161134
return reimbursementStatusTransformer(reimbursementStatus);
11171135
}
@@ -1192,11 +1210,15 @@ export default class ReimbursementRequestService {
11921210
...getReimbursementStatusQueryArgs(organization.organizationId)
11931211
});
11941212

1195-
await sendPendingSaboSubmissionNotification(
1196-
reimbursementRequest.notificationSlackThreads,
1197-
submitter.userId,
1198-
reimbursementRequest.recipientId
1199-
);
1213+
try {
1214+
await sendPendingSaboSubmissionNotification(
1215+
reimbursementRequest.notificationSlackThreads,
1216+
submitter.userId,
1217+
reimbursementRequest.recipientId
1218+
);
1219+
} catch (e: unknown) {
1220+
console.error('Error sending pending SABO submission notification:', e);
1221+
}
12001222

12011223
return reimbursementStatusTransformer(reimbursementStatus);
12021224
}
@@ -1270,7 +1292,11 @@ export default class ReimbursementRequestService {
12701292
...getReimbursementStatusQueryArgs(organization.organizationId)
12711293
});
12721294

1273-
await sendSubmittedToSaboNotification(reimbursementRequest.notificationSlackThreads);
1295+
try {
1296+
await sendSubmittedToSaboNotification(reimbursementRequest.notificationSlackThreads);
1297+
} catch (e: unknown) {
1298+
console.error('Error sending submitted to SABO notification:', e);
1299+
}
12741300

12751301
return reimbursementStatusTransformer(reimbursementStatus);
12761302
}
@@ -1339,7 +1365,11 @@ export default class ReimbursementRequestService {
13391365
'Reimbursement Request successfully updated, however no slack message was sent as recipient is missing their settings!'
13401366
);
13411367

1342-
await sendReimbursementRequestDeniedNotification(recipientSettings.slackId, reimbursementRequestId);
1368+
try {
1369+
await sendReimbursementRequestDeniedNotification(recipientSettings.slackId, reimbursementRequestId);
1370+
} catch (e: unknown) {
1371+
console.error('Error sending reimbursement request denied notification:', e);
1372+
}
13431373

13441374
return reimbursementStatusTransformer(reimbursementStatus);
13451375
}
@@ -1401,8 +1431,12 @@ export default class ReimbursementRequestService {
14011431
throw new InvalidOrganizationException('Vendor');
14021432
}
14031433

1404-
if (existingVendor.addedByUserId !== submitter.userId) {
1405-
await isUserHeadOrOnFinance(submitter, organization.organizationId);
1434+
const isUserAuthorized =
1435+
existingVendor.addedByUserId === submitter.userId ||
1436+
(await isUserOnFinanceTeam(submitter, organization.organizationId)) ||
1437+
(await userHasPermission(submitter.userId, organization.organizationId, isHead));
1438+
if (!isUserAuthorized) {
1439+
throw new AccessDeniedException(`You are not a member of the finance team!`);
14061440
}
14071441

14081442
const users = await getUsers(twoFactorContacts);
@@ -1453,8 +1487,12 @@ export default class ReimbursementRequestService {
14531487
throw new InvalidOrganizationException('Vendor');
14541488
}
14551489

1456-
if (existingVendor.addedByUserId !== submitter.userId) {
1457-
await isUserHeadOrOnFinance(submitter, organization.organizationId);
1490+
const isUserAuthorized =
1491+
existingVendor.addedByUserId === submitter.userId ||
1492+
(await isUserOnFinanceTeam(submitter, organization.organizationId)) ||
1493+
(await userHasPermission(submitter.userId, organization.organizationId, isHead));
1494+
if (!isUserAuthorized) {
1495+
throw new AccessDeniedException(`You are not a member of the finance team!`);
14581496
}
14591497

14601498
const deletedVendor = await prisma.vendor.update({
@@ -1582,7 +1620,11 @@ export default class ReimbursementRequestService {
15821620
...getReimbursementStatusQueryArgs(organization.organizationId)
15831621
});
15841622

1585-
await sendReimbursementRequestPendingFinanceNotification(reimbursementRequest.notificationSlackThreads);
1623+
try {
1624+
await sendReimbursementRequestPendingFinanceNotification(reimbursementRequest.notificationSlackThreads);
1625+
} catch (e: unknown) {
1626+
console.error('Error sending reimbursement request pending finance notification:', e);
1627+
}
15861628

15871629
return reimbursementStatusTransformer(updatedReimbursementStatus);
15881630
}
@@ -1636,7 +1678,11 @@ export default class ReimbursementRequestService {
16361678
...getReimbursementStatusQueryArgs(organization.organizationId)
16371679
});
16381680

1639-
await sendReimbursementRequestChangesRequestedNotification(reimbursementRequest.notificationSlackThreads, user.userId);
1681+
try {
1682+
await sendReimbursementRequestChangesRequestedNotification(reimbursementRequest.notificationSlackThreads, user.userId);
1683+
} catch (e: unknown) {
1684+
console.error('Error sending reimbursement request changes requested notification:', e);
1685+
}
16401686

16411687
return reimbursementStatusTransformer(deletedStatus);
16421688
}
@@ -1995,7 +2041,11 @@ export default class ReimbursementRequestService {
19952041
comment += ` ${[...new Set(restOfTags)].join(' ')}`;
19962042
}
19972043

1998-
await sendThreadResponse(reimbursementRequest.notificationSlackThreads, comment);
2044+
try {
2045+
await sendThreadResponse(reimbursementRequest.notificationSlackThreads, comment);
2046+
} catch (e: unknown) {
2047+
console.error('Error sending thread response:', e);
2048+
}
19992049

20002050
return reimbursementRequestCommentTransformer(createdComment);
20012051
}

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

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -387,10 +387,6 @@ export const isAuthUserHeadOfFinance = (user: Prisma.UserGetPayload<AuthUserQuer
387387
return user.teamsAsHead.some((team) => team.financeTeam);
388388
};
389389

390-
export const isUserHeadOrOnFinance = async (submitter: User, organizationId: string) => {
391-
await validateUserIsPartOfFinanceTeamOrHead(submitter, organizationId);
392-
};
393-
394390
// const isTeamIdInList = (teamId: string, teamsList: Team[]) => {
395391
// return teamsList.map((team) => team.teamId).includes(teamId);
396392
// };

src/backend/src/utils/slack.utils.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ interface SlackMessageThread {
3838
changeRequestId: string | null;
3939
}
4040

41-
const DEV_TESTING_OVERRIDE = true;
41+
const DEV_TESTING_OVERRIDE = process.env.SEND_SLACK_MESSAGES_IN_DEV === 'true';
4242

4343
// build the "due" string for the upcoming deadlines slack message
4444
export const buildDueString = (daysUntilDeadline: number): string => {
@@ -62,7 +62,7 @@ export const sendSlackUpcomingDeadlineNotification = async (
6262
const endDate = calculateEndDate(workPackage.startDate, workPackage.duration);
6363

6464
const { lead, manager } = workPackage.wbsElement;
65-
const slackId = await getUserSlackId(lead?.userId);
65+
const slackId = lead ? await getUserSlackId(lead?.userId) : undefined;
6666
const daysUntilDeadline = daysBetween(endDate, new Date());
6767

6868
const userString = lead ? buildUserString(userTransformer(lead), slackId) : 'No Lead Set';
@@ -230,7 +230,7 @@ export const sendPendingSaboSubmissionNotification = async (
230230
) => {
231231
await sendThreadResponse(
232232
threads,
233-
`${await getUserSlackMentionOrName(financeUserId)} has added this reimbursement request to Concur. ${await getUserSlackMentionOrName(pendingSubmissionFromId)}, please approve the request in Concur and mark it as submitted on Finishline.`
233+
`${await getUserSlackMentionOrName(financeUserId)} has added this reimbursement request to Concur. ${await getUserSlackMentionOrName(pendingSubmissionFromId)}, please check your email to approve the request in Concur and mark it as submitted on Finishline.`
234234
);
235235
};
236236

src/backend/src/utils/users.utils.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,11 @@ type UserWithSettings = {
2121
export const getUserFullName = async (userId: string | null) => {
2222
if (!userId) return 'no one';
2323
const user = await prisma.user.findUnique({ where: { userId } });
24-
if (!user) return 'no one';
24+
if (!user) throw new NotFoundException('User', userId);
2525
return `${user.firstName} ${user.lastName}`;
2626
};
2727

28-
export const getUserSlackMentionOrName = async (userId: string | null) => {
29-
if (!userId) return 'no one';
28+
export const getUserSlackMentionOrName = async (userId: string) => {
3029
const user = await prisma.user.findUnique({
3130
where: { userId },
3231
include: {
@@ -35,12 +34,11 @@ export const getUserSlackMentionOrName = async (userId: string | null) => {
3534
}
3635
}
3736
});
38-
if (!user) return 'no one';
37+
if (!user) throw new NotFoundException('User', userId);
3938
return user.userSettings?.slackId ? `<@${user.userSettings.slackId}>` : `${user.firstName} ${user.lastName}`;
4039
};
4140

42-
export const getUserSlackId = async (userId?: string): Promise<string | undefined> => {
43-
if (!userId) return undefined;
41+
export const getUserSlackId = async (userId: string): Promise<string | undefined> => {
4442
const user = await prisma.user.findUnique({ where: { userId }, include: { userSettings: true } });
4543
if (!user) throw new NotFoundException('User', userId);
4644
return user.userSettings?.slackId;

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -207,10 +207,10 @@ export const inputReimbursementRequestInSabo = (id: string) => {
207207
};
208208

209209
/**
210-
* Mark Reimbursement Request as Approved by SABO
210+
* Mark Reimbursement Request as submitted to SABO
211211
* This should be called after the user has approved the request in Concur
212212
*
213-
* @param id of the reimbursement request being marked as approved by SABO
213+
* @param id of the reimbursement request being marked as submitted to SABO
214214
* @returns the sabo submitted reimbursement status
215215
*/
216216
export const markReimbursementRequestAsSaboSubmitted = (id: string) => {

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -578,10 +578,10 @@ export const useInputReimbursementRequestInSabo = (id: string) => {
578578
};
579579

580580
/**
581-
* Custom react hook to mark a reimbursement request as approved by SABO
581+
* Custom react hook to mark a reimbursement request as submitted to SABO
582582
* This should be called after the user has approved the request in Concur
583583
*
584-
* @param id id of the reimbursement request to mark as approved by SABO
584+
* @param id id of the reimbursement request to mark as submitted to SABO
585585
* @returns the created sabo submitted reimbursement status
586586
*/
587587
export const useMarkReimbursementRequestAsSaboSubmitted = (id: string) => {

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -533,7 +533,7 @@ const ReimbursementRequestDetailsView: React.FC<ReimbursementRequestDetailsViewP
533533
items={[
534534
{
535535
resolved: false,
536-
detail: `I certify that I have a Concur account${financeDelegates.length > 0 ? ', and have assigned ' + financeDelegates.map(fullNamePipe).join(', ') + ' as delegate(s) to submit this reimbursement request on my behalf.' : '.'}`,
536+
detail: `I certify that I have a Concur account${financeDelegates.length > 0 ? ', and have assigned ' + financeDelegates.map(fullNamePipe).join(', ') + ' as delegate(s) to prepare this reimbursement request on my behalf.' : '.'}`,
537537
id: '1'
538538
},
539539
{

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { imagePreviewUrl, isReimbursementRequestPendingSaboSubmission } from '..
1111
import { useToast } from '../../../hooks/toasts.hooks';
1212
import { codeAndRefundSourceName } from '../../../utils/pipes';
1313
import CopyToClipboardButton from '../../../components/CopyToClipboardButton';
14+
import { useCurrentOrganization } from '../../../hooks/organizations.hooks';
1415

1516
interface SubmitToSaboModalProps {
1617
open: boolean;
@@ -26,6 +27,7 @@ const SubmitToSaboModal = ({ open, setOpen, reimbursementRequest }: SubmitToSabo
2627
const { data: userInfo, isLoading, isError, error } = useUserSecureSettings(recipient.userId);
2728
const toast = useToast();
2829
const isPendingSaboSubmission = isReimbursementRequestPendingSaboSubmission(reimbursementRequest);
30+
const organization = useCurrentOrganization();
2931
if (!user.isFinance) return <></>;
3032
if (isLoading || !userInfo) return <LoadingIndicator />;
3133
if (isError) return <ErrorPage error={error} message={error.message} />;
@@ -133,7 +135,7 @@ const SubmitToSaboModal = ({ open, setOpen, reimbursementRequest }: SubmitToSabo
133135
<Grid item xs={8}>
134136
<Stack>
135137
<Box display="flex" alignItems="center">
136-
<Typography>{treasurerName}</Typography>
138+
<Typography>{organization.treas}</Typography>
137139
<CopyToClipboardButton msg={treasurerName} />
138140
</Box>
139141
<Box display="flex" alignItems="center">

0 commit comments

Comments
 (0)