Skip to content

Commit 19f9910

Browse files
authored
Merge pull request #1674 from Northeastern-Electric-Racing/#1535-other-options-reimbursement-product
#1535 - Add Other Reasons to Reimbursement Products
2 parents c7711ba + 812e01c commit 19f9910

23 files changed

Lines changed: 508 additions & 186 deletions

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

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,14 +52,23 @@ export default class ReimbursementRequestsController {
5252

5353
static async createReimbursementRequest(req: Request, res: Response, next: NextFunction) {
5454
try {
55-
const { dateOfExpense, vendorId, account, reimbursementProducts, expenseTypeId, totalCost } = req.body;
55+
const {
56+
dateOfExpense,
57+
vendorId,
58+
account,
59+
otherReimbursementProducts,
60+
wbsReimbursementProducts,
61+
expenseTypeId,
62+
totalCost
63+
} = req.body;
5664
const user = await getCurrentUserWithUserSettings(res);
5765
const createdReimbursementRequest = await ReimbursementRequestService.createReimbursementRequest(
5866
user,
5967
dateOfExpense,
6068
vendorId,
6169
account,
62-
reimbursementProducts,
70+
otherReimbursementProducts,
71+
wbsReimbursementProducts,
6372
expenseTypeId,
6473
totalCost
6574
);
@@ -84,8 +93,16 @@ export default class ReimbursementRequestsController {
8493
static async editReimbursementRequest(req: Request, res: Response, next: NextFunction) {
8594
try {
8695
const { requestId } = req.params;
87-
const { dateOfExpense, vendorId, account, expenseTypeId, totalCost, reimbursementProducts, receiptPictures } =
88-
req.body;
96+
const {
97+
dateOfExpense,
98+
vendorId,
99+
account,
100+
expenseTypeId,
101+
totalCost,
102+
otherReimbursementProducts,
103+
wbsReimbursementProducts,
104+
receiptPictures
105+
} = req.body;
89106
const user = await getCurrentUser(res);
90107
const updatedReimbursementRequestId = await ReimbursementRequestService.editReimbursementRequest(
91108
requestId,
@@ -94,7 +111,8 @@ export default class ReimbursementRequestsController {
94111
account,
95112
expenseTypeId,
96113
totalCost,
97-
reimbursementProducts,
114+
otherReimbursementProducts,
115+
wbsReimbursementProducts,
98116
receiptPictures,
99117
user
100118
);
Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
import { Prisma } from '@prisma/client';
22

3-
const reimbursementProductQueryArgs = Prisma.validator<Prisma.Reimbursement_ProductArgs>()({
3+
export const reimbursementProductReasonQueryArgs = Prisma.validator<Prisma.Reimbursement_Product_ReasonArgs>()({
44
include: {
55
wbsElement: true
66
}
77
});
88

9-
export default reimbursementProductQueryArgs;
9+
export const reimbursementProductQueryArgs = Prisma.validator<Prisma.Reimbursement_ProductArgs>()({
10+
include: {
11+
reimbursementProductReason: { ...reimbursementProductReasonQueryArgs }
12+
}
13+
});

src/backend/src/prisma-query-args/reimbursement-requests.query-args.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Prisma } from '@prisma/client';
2-
import reimbursementProductQueryArgs from './reimbursement-products.query-args';
32
import reimbursementStatusQueryArgs from './reimbursement-statuses.query-args';
43
import receiptQueryArgs from './receipt-query.args';
4+
import { reimbursementProductQueryArgs } from './reimbursement-products.query-args';
55

66
const reimbursementRequestQueryArgs = Prisma.validator<Prisma.Reimbursement_RequestArgs>()({
77
include: {
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
Warnings:
3+
4+
- You are about to drop the column `wbsElementId` on the `Reimbursement_Product` table. All the data in the column will be lost.
5+
- A unique constraint covering the columns `[reimbursementProductReasonId]` on the table `Reimbursement_Product` will be added. If there are existing duplicate values, this will fail.
6+
- Added the required column `reimbursementProductReasonId` to the `Reimbursement_Product` table without a default value. This is not possible if the table is not empty.
7+
8+
*/
9+
-- CreateEnum
10+
CREATE TYPE "Other_Reimbursement_Product_Reason" AS ENUM ('TOOLS_AND_EQUIPMENT', 'COMPETITION', 'CONSUMABLES', 'GENERAL_STOCK', 'SUBSCRIPTIONS_AND_MEMBERSHIPS');
11+
12+
-- AlterTable
13+
ALTER TABLE "Reimbursement_Product"
14+
ADD COLUMN "reimbursementProductReasonId" TEXT;
15+
16+
-- CreateTable
17+
CREATE TABLE "Reimbursement_Product_Reason" (
18+
"reimbursementProductReasonId" TEXT NOT NULL,
19+
"wbsElementId" INTEGER,
20+
"otherReason" "Other_Reimbursement_Product_Reason",
21+
"reimbursementProductId" TEXT,
22+
23+
CONSTRAINT "Reimbursement_Product_Reason_pkey" PRIMARY KEY ("reimbursementProductReasonId")
24+
);
25+
26+
INSERT INTO "Reimbursement_Product_Reason" ("reimbursementProductReasonId", "wbsElementId", "reimbursementProductId") SELECT gen_random_uuid(), "wbsElementId", "reimbursementProductId" FROM "Reimbursement_Product";
27+
28+
UPDATE "Reimbursement_Product"
29+
SET "reimbursementProductReasonId" = (
30+
SELECT "reimbursementProductReasonId"
31+
FROM "Reimbursement_Product_Reason"
32+
WHERE "Reimbursement_Product_Reason"."reimbursementProductId" = "Reimbursement_Product"."reimbursementProductId"
33+
);
34+
35+
-- CreateIndex
36+
CREATE UNIQUE INDEX "Reimbursement_Product_reimbursementProductReasonId_key" ON "Reimbursement_Product"("reimbursementProductReasonId");
37+
38+
39+
-- DropForeignKey
40+
ALTER TABLE "Reimbursement_Product" DROP CONSTRAINT "Reimbursement_Product_wbsElementId_fkey";
41+
42+
-- AlterTable
43+
ALTER TABLE "Reimbursement_Product" DROP COLUMN "wbsElementId";
44+
45+
-- AddForeignKey
46+
ALTER TABLE "Reimbursement_Product_Reason" ADD CONSTRAINT "Reimbursement_Product_Reason_wbsElementId_fkey" FOREIGN KEY ("wbsElementId") REFERENCES "WBS_Element"("wbsElementId") ON DELETE SET NULL ON UPDATE CASCADE;
47+
48+
-- AlterTable
49+
ALTER TABLE "Reimbursement_Product" ALTER COLUMN "reimbursementProductReasonId" SET NOT NULL;
50+
51+
-- AddForeignKey
52+
ALTER TABLE "Reimbursement_Product" ADD CONSTRAINT "Reimbursement_Product_reimbursementProductReasonId_fkey" FOREIGN KEY ("reimbursementProductReasonId") REFERENCES "Reimbursement_Product_Reason"("reimbursementProductReasonId") ON DELETE RESTRICT ON UPDATE CASCADE;
53+
54+
-- AlterTable
55+
ALTER TABLE "Reimbursement_Product_Reason"
56+
DROP COLUMN "reimbursementProductId";

src/backend/src/prisma/schema.prisma

Lines changed: 48 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -234,30 +234,30 @@ model Change {
234234
}
235235

236236
model WBS_Element {
237-
wbsElementId Int @id @default(autoincrement())
238-
dateCreated DateTime @default(now())
239-
dateDeleted DateTime?
240-
carNumber Int
241-
projectNumber Int
242-
workPackageNumber Int
243-
name String
244-
status WBS_Element_Status @default(INACTIVE)
245-
projectLeadId Int?
246-
projectLead User? @relation(name: "projectLead", fields: [projectLeadId], references: [userId])
247-
projectManagerId Int?
248-
projectManager User? @relation(name: "projectManager", fields: [projectManagerId], references: [userId])
249-
changeRequests Change_Request[]
250-
changes Change[]
251-
deletedByUserId Int?
252-
deletedBy User? @relation(name: "deletedWbsElements", fields: [deletedByUserId], references: [userId])
253-
project Project?
254-
workPackage Work_Package?
255-
blocking Work_Package[] @relation("blockedBy")
256-
tasks Task[]
257-
reimbursementProducts Reimbursement_Product[]
258-
links Link[] @relation(name: "links")
259-
assemblies Assembly[]
260-
materials Material[]
237+
wbsElementId Int @id @default(autoincrement())
238+
dateCreated DateTime @default(now())
239+
dateDeleted DateTime?
240+
carNumber Int
241+
projectNumber Int
242+
workPackageNumber Int
243+
name String
244+
status WBS_Element_Status @default(INACTIVE)
245+
projectLeadId Int?
246+
projectLead User? @relation(name: "projectLead", fields: [projectLeadId], references: [userId])
247+
projectManagerId Int?
248+
projectManager User? @relation(name: "projectManager", fields: [projectManagerId], references: [userId])
249+
changeRequests Change_Request[]
250+
changes Change[]
251+
deletedByUserId Int?
252+
deletedBy User? @relation(name: "deletedWbsElements", fields: [deletedByUserId], references: [userId])
253+
project Project?
254+
workPackage Work_Package?
255+
blocking Work_Package[] @relation("blockedBy")
256+
tasks Task[]
257+
links Link[] @relation(name: "links")
258+
assemblies Assembly[]
259+
materials Material[]
260+
reimbursementProductReasons Reimbursement_Product_Reason[]
261261
262262
@@unique([carNumber, projectNumber, workPackageNumber], name: "wbsNumber")
263263
}
@@ -411,15 +411,31 @@ model Reimbursement_Request {
411411
expenseType Expense_Type @relation(fields: [expenseTypeId], references: [expenseTypeId])
412412
}
413413

414+
enum Other_Reimbursement_Product_Reason {
415+
TOOLS_AND_EQUIPMENT
416+
COMPETITION
417+
CONSUMABLES
418+
GENERAL_STOCK
419+
SUBSCRIPTIONS_AND_MEMBERSHIPS
420+
}
421+
422+
model Reimbursement_Product_Reason {
423+
reimbursementProductReasonId String @id @default(uuid())
424+
wbsElementId Int?
425+
wbsElement WBS_Element? @relation(fields: [wbsElementId], references: [wbsElementId])
426+
otherReason Other_Reimbursement_Product_Reason?
427+
reimbursementProduct Reimbursement_Product?
428+
}
429+
414430
model Reimbursement_Product {
415-
reimbursementProductId String @id @default(uuid())
416-
name String
417-
dateDeleted DateTime?
418-
cost Int
419-
wbsElementId Int
420-
wbsElement WBS_Element @relation(fields: [wbsElementId], references: [wbsElementId])
421-
reimbursementRequestId String
422-
reimbursementRequest Reimbursement_Request @relation(fields: [reimbursementRequestId], references: [reimbursementRequestId])
431+
reimbursementProductId String @id @default(uuid())
432+
name String
433+
dateDeleted DateTime?
434+
cost Int
435+
reimbursementProductReasonId String @unique
436+
reimbursementProductReason Reimbursement_Product_Reason @relation(fields: [reimbursementProductReasonId], references: [reimbursementProductReasonId])
437+
reimbursementRequestId String
438+
reimbursementRequest Reimbursement_Request @relation(fields: [reimbursementRequestId], references: [reimbursementRequestId])
423439
}
424440

425441
model Vendor {

src/backend/src/prisma/seed.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -860,10 +860,11 @@ const performSeed: () => Promise<void> = async () => {
860860
new Date(),
861861
vendor.vendorId,
862862
ClubAccount.CASH,
863+
[],
863864
[
864865
{
865866
name: 'GLUE',
866-
wbsNum: {
867+
reason: {
867868
carNumber: 1,
868869
projectNumber: 1,
869870
workPackageNumber: 0

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

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
import express from 'express';
77
import { body } from 'express-validator';
8-
import { intMinZero, isAccount, isDate, nonEmptyString } from '../utils/validation.utils';
8+
import { intMinZero, isAccount, isDate, nonEmptyString, validateReimbursementProducts } from '../utils/validation.utils';
99
import { validateInputs } from '../utils/utils';
1010
import ReimbursementRequestController from '../controllers/reimbursement-requests.controllers';
1111
import multer, { memoryStorage } from 'multer';
@@ -29,14 +29,9 @@ reimbursementRequestsRouter.post(
2929
isDate(body('dateOfExpense')),
3030
nonEmptyString(body('vendorId')),
3131
isAccount(body('account')),
32-
body('reimbursementProducts').isArray(),
33-
nonEmptyString(body('reimbursementProducts.*.name')),
34-
intMinZero(body('reimbursementProducts.*.cost')),
35-
intMinZero(body('reimbursementProducts.*.wbsNum.carNumber')),
36-
intMinZero(body('reimbursementProducts.*.wbsNum.projectNumber')),
37-
intMinZero(body('reimbursementProducts.*.wbsNum.workPackageNumber')),
3832
nonEmptyString(body('expenseTypeId')),
3933
intMinZero(body('totalCost')),
34+
validateReimbursementProducts(),
4035
validateInputs,
4136
ReimbursementRequestController.createReimbursementRequest
4237
);
@@ -53,15 +48,9 @@ reimbursementRequestsRouter.post(
5348
body('receiptPictures').isArray(),
5449
nonEmptyString(body('receiptPictures.*.name')),
5550
nonEmptyString(body('receiptPictures.*.googleFileId')),
56-
body('reimbursementProducts').isArray(),
57-
nonEmptyString(body('reimbursementProducts.*.id').optional()),
58-
nonEmptyString(body('reimbursementProducts.*.name')),
59-
intMinZero(body('reimbursementProducts.*.cost')),
60-
intMinZero(body('reimbursementProducts.*.wbsNum.carNumber')),
61-
intMinZero(body('reimbursementProducts.*.wbsNum.projectNumber')),
62-
intMinZero(body('reimbursementProducts.*.wbsNum.workPackageNumber')),
6351
nonEmptyString(body('expenseTypeId')),
6452
intMinZero(body('totalCost')),
53+
validateReimbursementProducts(),
6554
validateInputs,
6655
ReimbursementRequestController.editReimbursementRequest
6756
);

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

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,19 @@ import {
1010
ClubAccount,
1111
ExpenseType,
1212
Reimbursement,
13-
ReimbursementProductCreateArgs,
1413
ReimbursementReceiptCreateArgs,
1514
ReimbursementRequest,
1615
ReimbursementStatusType,
1716
Vendor,
1817
isAdmin,
1918
isGuest,
20-
isHead
19+
isHead,
20+
WbsReimbursementProductCreateArgs,
21+
OtherReimbursementProductCreateArgs
2122
} from 'shared';
2223
import prisma from '../prisma/prisma';
2324
import {
25+
createReimbursementProducts,
2426
isUserLeadOrHeadOfFinanceTeam,
2527
removeDeletedReceiptPictures,
2628
updateReimbursementProducts,
@@ -111,7 +113,8 @@ export default class ReimbursementRequestService {
111113
dateOfExpense: Date,
112114
vendorId: string,
113115
account: ClubAccount,
114-
reimbursementProducts: ReimbursementProductCreateArgs[],
116+
otherReimbursementProducts: OtherReimbursementProductCreateArgs[],
117+
wbsReimbursementProducts: WbsReimbursementProductCreateArgs[],
115118
expenseTypeId: string,
116119
totalCost: number
117120
): Promise<Reimbursement_Request> {
@@ -137,7 +140,10 @@ export default class ReimbursementRequestService {
137140
throw new HttpException(400, 'The submitted refund source is not allowed to be used with the submitted expense type');
138141
}
139142

140-
const validatedReimbursementProudcts = await validateReimbursementProducts(reimbursementProducts);
143+
const validatedReimbursementProducts = await validateReimbursementProducts(
144+
otherReimbursementProducts,
145+
wbsReimbursementProducts
146+
);
141147

142148
const createdReimbursementRequest = await prisma.reimbursement_Request.create({
143149
data: {
@@ -152,21 +158,16 @@ export default class ReimbursementRequestService {
152158
type: ReimbursementStatusType.PENDING_FINANCE,
153159
userId: recipient.userId
154160
}
155-
},
156-
reimbursementProducts: {
157-
createMany: {
158-
data: validatedReimbursementProudcts.map((reimbursementProductInfo) => {
159-
return {
160-
name: reimbursementProductInfo.name,
161-
cost: reimbursementProductInfo.cost,
162-
wbsElementId: reimbursementProductInfo.wbsElementId
163-
};
164-
})
165-
}
166161
}
167162
}
168163
});
169164

165+
await createReimbursementProducts(
166+
validatedReimbursementProducts.validatedOtherReimbursementProducts,
167+
validatedReimbursementProducts.validatedWbsReimbursementProducts,
168+
createdReimbursementRequest.reimbursementRequestId
169+
);
170+
170171
return createdReimbursementRequest;
171172
}
172173

@@ -243,7 +244,8 @@ export default class ReimbursementRequestService {
243244
account: ClubAccount,
244245
expenseTypeId: string,
245246
totalCost: number,
246-
reimbursementProducts: ReimbursementProductCreateArgs[],
247+
otherReimbursementProducts: OtherReimbursementProductCreateArgs[],
248+
wbsReimbursementProducts: WbsReimbursementProductCreateArgs[],
247249
receiptPictures: ReimbursementReceiptCreateArgs[],
248250
submitter: User
249251
): Promise<Reimbursement_Request> {
@@ -277,7 +279,8 @@ export default class ReimbursementRequestService {
277279

278280
await updateReimbursementProducts(
279281
oldReimbursementRequest.reimbursementProducts,
280-
reimbursementProducts,
282+
otherReimbursementProducts,
283+
wbsReimbursementProducts,
281284
oldReimbursementRequest.reimbursementRequestId
282285
);
283286

0 commit comments

Comments
 (0)