Skip to content

Commit 0275106

Browse files
committed
Merge branch 'develop' into slack-rr-integrations
2 parents 48e6654 + e671c91 commit 0275106

56 files changed

Lines changed: 1364 additions & 1358 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

docker-compose.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ services:
4040
container_name: frontend-dev
4141
build:
4242
context: ./
43-
dockerfile: devContainerization/Dockerfile.frontend.dev
43+
dockerfile: src/frontend/Dockerfile
4444
ports:
4545
- '3000:3000'
4646
volumes:

src/backend/Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ FROM node:20
33

44
WORKDIR /base
55

6-
COPY package.json .yarn tsconfig.build.json ./
6+
COPY package.json tsconfig.build.json ./
77
COPY ./src/backend src/backend
88
COPY ./src/shared src/shared
99

src/backend/src/controllers/users.controllers.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,15 @@ export default class UsersController {
2121
}
2222
}
2323

24+
static async getAllMembers(req: Request, res: Response, next: NextFunction) {
25+
try {
26+
const users = await UsersService.getAllOrgMembers(req.organization.organizationId);
27+
res.status(200).json(users);
28+
} catch (error: unknown) {
29+
next(error);
30+
}
31+
}
32+
2433
static async getCurrentUser(req: Request, res: Response, next: NextFunction) {
2534
try {
2635
const user = await UsersService.getCurrentUser(req.currentUser);

src/backend/src/routes/users.routes.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ const userRouter = express.Router();
88

99
userRouter.get('/', UsersController.getAllUsers);
1010
userRouter.get('/organization', UsersController.getAllOrgUsers);
11+
userRouter.get('/members', UsersController.getAllMembers);
1112
userRouter.post(
1213
'/scheduleSettings',
1314
body('userIds').isArray(),

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,14 @@ export default class ChangeRequestsService {
280280
if (reviewer.userId === foundCR.submitterId)
281281
throw new AccessDeniedException("You can't review your own change request!");
282282

283+
// verify that if there are requested reviewers, the reviewer is one of them
284+
if (foundCR.requestedReviewers.length > 0) {
285+
const isRequestedReviewer = foundCR.requestedReviewers.some((user) => user.userId === reviewer.userId);
286+
if (!isRequestedReviewer) {
287+
throw new AccessDeniedException('Only requested reviewers can review this change request!');
288+
}
289+
}
290+
283291
// ScopeChange Request That Has Been Accepted Being Reviewed
284292
if (foundCR.scopeChangeRequest && accepted) {
285293
await this.reviewScopeChangeRequest(foundCR, reviewer, psId, organization);

src/backend/src/services/finance.services.ts

Lines changed: 61 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -434,13 +434,31 @@ export default class FinanceServices {
434434
},
435435
select: {
436436
reimbursementStatuses: true,
437-
totalCost: true
437+
totalCost: true,
438+
reimbursementProducts: {
439+
where: {
440+
reimbursementProductReason: {
441+
wbsElement: {
442+
project: {
443+
projectId
444+
}
445+
}
446+
}
447+
},
448+
select: {
449+
cost: true
450+
}
451+
}
438452
}
439453
});
440454

441455
const { pendingFinance, pendingLeadership, submittedToSabo, reimbursed } = computeRRTotals(reimbursementRequests);
442456

443-
const totalBalance = reimbursementRequests.reduce((acc, curr) => acc + curr.totalCost, 0) / 100;
457+
const totalBalance =
458+
reimbursementRequests.reduce((acc, curr) => {
459+
const projectProductsCost = curr.reimbursementProducts.reduce((prodAcc, prod) => prodAcc + prod.cost, 0);
460+
return acc + projectProductsCost;
461+
}, 0) / 100;
444462

445463
const available = project.budget - totalBalance;
446464

@@ -507,15 +525,37 @@ export default class FinanceServices {
507525
},
508526
select: {
509527
reimbursementStatuses: true,
510-
totalCost: true
528+
totalCost: true,
529+
reimbursementProducts: {
530+
where: {
531+
reimbursementProductReason: {
532+
wbsElement: {
533+
project: {
534+
teams: {
535+
some: {
536+
teamId
537+
}
538+
}
539+
}
540+
}
541+
}
542+
},
543+
select: {
544+
cost: true
545+
}
546+
}
511547
}
512548
});
513549

514550
const totalBudget = team.projects.reduce((acc, curr) => acc + curr.budget, 0);
515551

516552
const { pendingFinance, pendingLeadership, submittedToSabo, reimbursed } = computeRRTotals(reimbursementRequests);
517553

518-
const totalBalance = reimbursementRequests.reduce((acc, curr) => acc + curr.totalCost, 0) / 100;
554+
const totalBalance =
555+
reimbursementRequests.reduce((acc, curr) => {
556+
const teamProductsCost = curr.reimbursementProducts.reduce((prodAcc, prod) => prodAcc + prod.cost, 0);
557+
return acc + teamProductsCost;
558+
}, 0) / 100;
519559

520560
const available = totalBudget - totalBalance;
521561

@@ -910,7 +950,17 @@ export default class FinanceServices {
910950
},
911951
select: {
912952
reimbursementStatuses: true,
913-
totalCost: true
953+
totalCost: true,
954+
reimbursementProducts: {
955+
where: {
956+
reimbursementProductReason: {
957+
otherReasonId
958+
}
959+
},
960+
select: {
961+
cost: true
962+
}
963+
}
914964
}
915965
});
916966

@@ -935,7 +985,8 @@ export default class FinanceServices {
935985
const lastStatus = req.reimbursementStatuses.at(-1)?.type;
936986

937987
if (lastStatus && totals[lastStatus] !== undefined) {
938-
totals[lastStatus] += req.totalCost;
988+
const categoryProductsCost = req.reimbursementProducts.reduce((prodAcc, prod) => prodAcc + prod.cost, 0);
989+
totals[lastStatus] += categoryProductsCost;
939990
}
940991
});
941992

@@ -944,7 +995,10 @@ export default class FinanceServices {
944995
const submittedToSabo = totals[Reimbursement_Status_Type.SABO_SUBMITTED] ?? 0;
945996
const reimbursed = totals[Reimbursement_Status_Type.REIMBURSED] ?? 0;
946997

947-
const totalBalance = reimbursementRequests.reduce((acc, curr) => acc + curr.totalCost, 0);
998+
const totalBalance = reimbursementRequests.reduce((acc, curr) => {
999+
const categoryProductsCost = curr.reimbursementProducts.reduce((prodAcc, prod) => prodAcc + prod.cost, 0);
1000+
return acc + categoryProductsCost;
1001+
}, 0);
9481002

9491003
const available = totalBudget - totalBalance;
9501004

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@ export default class ReimbursementRequestService {
242242
dateOfExpense?: Date
243243
): Promise<ReimbursementRequest> {
244244
if (await userHasPermission(recipient.userId, organization.organizationId, isGuest))
245-
throw new AccessDeniedGuestException('Guests cannot create a reimbursement request');
245+
throw new AccessDeniedGuestException('create a reimbursement request');
246246

247247
if (!recipient.userSecureSettings) throw new HttpException(500, 'User does not have their finance settings set up');
248248

src/backend/src/services/users.services.ts

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { User_Settings, Organization } from '@prisma/client';
1+
import { User_Settings, Organization, Role_Type } from '@prisma/client';
22
import { OAuth2Client } from 'google-auth-library/build/src/auth/oauth2client';
33
import {
44
Role,
@@ -59,6 +59,28 @@ export default class UsersService {
5959
return users.map(userTransformer);
6060
}
6161

62+
static async getAllOrgMembers(organizationId: string): Promise<User[]> {
63+
const users = await prisma.user.findMany({
64+
where: {
65+
organizations: {
66+
some: {
67+
organizationId,
68+
roles: {}
69+
}
70+
},
71+
roles: {
72+
some: {
73+
AND: [{ NOT: { roleType: Role_Type.GUEST } }, { organizationId }]
74+
}
75+
}
76+
},
77+
...getUserQueryArgs(organizationId),
78+
orderBy: { lastName: 'asc' }
79+
});
80+
81+
return users.map(userTransformer);
82+
}
83+
6284
static async getCurrentUser(user: User): Promise<AuthenticatedUser> {
6385
const userWithOrgs = await prisma.user.findUnique({ where: { userId: user.userId }, include: { organizations: true } });
6486

src/backend/src/utils/finance.utils.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ export const computeRRTotals = (
114114
reimbursementStatusId: string;
115115
userId: string;
116116
}[];
117+
reimbursementProducts?: { cost: number }[];
117118
}[]
118119
): {
119120
pendingFinance: number;
@@ -132,7 +133,11 @@ export const computeRRTotals = (
132133
const lastStatus = req.reimbursementStatuses.at(-1)?.type;
133134

134135
if (lastStatus && totals[lastStatus] !== undefined) {
135-
totals[lastStatus] += req.totalCost;
136+
// If reimbursementProducts are provided, sum their costs; otherwise use totalCost
137+
const costToAdd = req.reimbursementProducts
138+
? req.reimbursementProducts.reduce((acc, prod) => acc + prod.cost, 0)
139+
: req.totalCost;
140+
totals[lastStatus] += costToAdd;
136141
}
137142
});
138143

0 commit comments

Comments
 (0)