Skip to content

Commit 155fb7a

Browse files
authored
Merge pull request #1701 from Northeastern-Electric-Racing/#1526-Edit-Material-Endpoint
#1526 edit material endpoint
2 parents 7c5a0b4 + a8cdf80 commit 155fb7a

9 files changed

Lines changed: 410 additions & 30 deletions

File tree

src/backend/src/controllers/projects.controllers.ts

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -167,14 +167,14 @@ export default class ProjectsController {
167167
manufacturerName,
168168
manufacturerPartNumber,
169169
quantity,
170-
unitName,
171170
price,
172171
subtotal,
173172
linkUrl,
174173
notes,
175174
wbsNum,
176175
assemblyId,
177-
pdmFileName
176+
pdmFileName,
177+
unitName
178178
);
179179
return res.status(200).json(material);
180180
} catch (error: unknown) {
@@ -213,4 +213,46 @@ export default class ProjectsController {
213213
next(error);
214214
}
215215
}
216+
217+
static async editMaterial(req: Request, res: Response, next: NextFunction) {
218+
try {
219+
const user = await getCurrentUser(res);
220+
const { materialId } = req.params;
221+
const {
222+
name,
223+
assemblyId,
224+
status,
225+
materialTypeName,
226+
manufacturerName,
227+
manufacturerPartNumber,
228+
pdmFileName,
229+
quantity,
230+
unitName,
231+
price,
232+
subtotal,
233+
linkUrl,
234+
notes
235+
} = req.body;
236+
const updatedMaterial = await ProjectsService.editMaterial(
237+
user,
238+
materialId,
239+
name,
240+
status,
241+
materialTypeName,
242+
manufacturerName,
243+
manufacturerPartNumber,
244+
quantity,
245+
price,
246+
subtotal,
247+
linkUrl,
248+
notes,
249+
unitName,
250+
assemblyId,
251+
pdmFileName
252+
);
253+
res.status(200).json(updatedMaterial);
254+
} catch (error: unknown) {
255+
next(error);
256+
}
257+
}
216258
}

src/backend/src/prisma/migrations/20231024124220_add_bom/migration.sql renamed to src/backend/src/prisma/migrations/20231119042617_add_bom/migration.sql

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,6 @@ CREATE TABLE "Manufacturer" (
6868
-- CreateIndex
6969
CREATE UNIQUE INDEX "Assembly_name_key" ON "Assembly"("name");
7070

71-
-- CreateIndex
72-
CREATE UNIQUE INDEX "Material_name_key" ON "Material"("name");
73-
7471
-- AddForeignKey
7572
ALTER TABLE "Assembly" ADD CONSTRAINT "Assembly_userDeletedId_fkey" FOREIGN KEY ("userDeletedId") REFERENCES "User"("userId") ON DELETE SET NULL ON UPDATE CASCADE;
7673

src/backend/src/prisma/schema.prisma

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -491,7 +491,7 @@ model Material {
491491
materialId String @id @default(uuid())
492492
assembly Assembly? @relation(fields: [assemblyId], references: [assemblyId])
493493
assemblyId String?
494-
name String @unique
494+
name String
495495
wbsElement WBS_Element @relation(fields: [wbsElementId], references: [wbsElementId])
496496
wbsElementId Int
497497
dateDeleted DateTime?

src/backend/src/routes/projects.routes.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,13 +74,31 @@ projectRouter.post(
7474
nonEmptyString(body('manufacturerPartNumber')),
7575
nonEmptyString(body('pdmFileName').optional()),
7676
intMinZero(body('quantity')),
77-
nonEmptyString(body('unitName')),
77+
nonEmptyString(body('unitName')).optional(),
7878
intMinZero(body('price')), // in cents
7979
intMinZero(body('subtotal')), // in cents
8080
nonEmptyString(body('linkUrl').isURL()),
8181
body('notes').isString(),
8282
validateInputs,
8383
ProjectsController.createMaterial
8484
);
85+
projectRouter.post(
86+
'/bom/material/:materialId/edit',
87+
nonEmptyString(body('name')),
88+
nonEmptyString(body('assemblyId').optional()),
89+
isMaterialStatus(body('status')),
90+
nonEmptyString(body('materialTypeName')),
91+
nonEmptyString(body('manufacturerName')),
92+
nonEmptyString(body('manufacturerPartNumber')),
93+
nonEmptyString(body('pdmFileName').optional()),
94+
intMinZero(body('quantity')),
95+
body('unitName').optional(),
96+
intMinZero(body('price')), // in cents
97+
intMinZero(body('subtotal')), // in cents
98+
nonEmptyString(body('linkUrl').isURL()),
99+
body('notes').isString(),
100+
validateInputs,
101+
ProjectsController.editMaterial
102+
);
85103

86104
export default projectRouter;

src/backend/src/services/projects.services.ts

Lines changed: 114 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -616,14 +616,14 @@ export default class ProjectsService {
616616
* @param manufacturerName the name of the material's manufacturer
617617
* @param manufacturerPartNumber the manufacturer part number for the material
618618
* @param quantity the quantity of material as a number
619-
* @param unitName the name of the Quantity Unit the quantity is measured in
620619
* @param price the price of the material in whole cents
621620
* @param subtotal the subtotal of the price for the material in whole cents
622621
* @param linkUrl the url for the material's link as a string
623622
* @param notes any notes about the material as a string
624623
* @param wbsNumber the WBS number of the project associated with this material
625624
* @param assemblyId the id of the Assembly for the material
626625
* @param pdmFileName the name of the pdm file for the material
626+
* @param unitName the name of the Quantity Unit the quantity is measured in
627627
* @returns the created material
628628
*/
629629
static async createMaterial(
@@ -634,14 +634,14 @@ export default class ProjectsService {
634634
manufacturerName: string,
635635
manufacturerPartNumber: string,
636636
quantity: number,
637-
unitName: string,
638637
price: number,
639638
subtotal: number,
640639
linkUrl: string,
641640
notes: string,
642641
wbsNumber: WbsNumber,
643642
assemblyId?: string,
644-
pdmFileName?: string
643+
pdmFileName?: string,
644+
unitName?: string
645645
): Promise<Material> {
646646
const project = await prisma.project.findFirst({
647647
where: {
@@ -671,10 +671,12 @@ export default class ProjectsService {
671671
});
672672
if (!manufacturer) throw new NotFoundException('Manufacturer', manufacturerName);
673673

674-
const unit = await prisma.unit.findFirst({
675-
where: { name: unitName }
676-
});
677-
if (!unit) throw new NotFoundException('Unit', unitName);
674+
if (unitName) {
675+
const unit = await prisma.unit.findFirst({
676+
where: { name: unitName }
677+
});
678+
if (!unit) throw new NotFoundException('Unit', unitName);
679+
}
678680

679681
const perms = isLeadership(creator.role) || isUserPartOfTeams(project.teams, creator);
680682

@@ -829,4 +831,109 @@ export default class ProjectsService {
829831

830832
return newMaterialType;
831833
}
834+
835+
/**
836+
* Update a material
837+
* @param submitter the submitter of the request
838+
* @param materialId the material id of the material being edited
839+
* @param name the name of the edited material
840+
* @param status the status of the edited material
841+
* @param materialTypeName the material type of the edited material
842+
* @param manufacturerName the manufacturerName of the edited material
843+
* @param manufacturerPartNumber the manufacturerPartNumber of the edited material
844+
* @param quantity the quantity of the edited material
845+
* @param price the price of the edited material
846+
* @param subtotal the subtotal of the edited material
847+
* @param linkUrl the linkUrl of the edited material
848+
* @param notes the notes of the edited material
849+
* @param unitName the unit name of the edited material
850+
* @param assemblyId the assembly id of the edited material
851+
* @param pdmFileName the pdm file name of the edited material
852+
* @throws if permission denied or material's wbsElement is undefined/deleted
853+
* @returns the updated material
854+
*/
855+
static async editMaterial(
856+
submitter: User,
857+
materialId: string,
858+
name: string,
859+
status: Material_Status,
860+
materialTypeName: string,
861+
manufacturerName: string,
862+
manufacturerPartNumber: string,
863+
quantity: number,
864+
price: number,
865+
subtotal: number,
866+
linkUrl: string,
867+
notes: string,
868+
unitName?: string,
869+
assemblyId?: string,
870+
pdmFileName?: string
871+
): Promise<Material> {
872+
const material = await prisma.material.findUnique({
873+
where: {
874+
materialId
875+
}
876+
});
877+
878+
if (!material) throw new NotFoundException('Material', materialId);
879+
if (material.dateDeleted) throw new DeletedException('Material', materialId);
880+
881+
const project = await prisma.project.findFirst({
882+
where: {
883+
wbsElementId: material.wbsElementId
884+
},
885+
...projectQueryArgs
886+
});
887+
888+
if (!project) throw new NotFoundException('Project', material.wbsElementId);
889+
if (project.wbsElement.dateDeleted) throw new DeletedException('Project', project.projectId);
890+
891+
if (assemblyId) {
892+
const assembly = await prisma.assembly.findFirst({ where: { assemblyId } });
893+
if (!assembly) throw new NotFoundException('Assembly', assemblyId);
894+
}
895+
896+
const materialType = await prisma.material_Type.findFirst({
897+
where: { name: materialTypeName }
898+
});
899+
if (!materialType) throw new NotFoundException('Material Type', materialTypeName);
900+
901+
const manufacturer = await prisma.manufacturer.findFirst({
902+
where: { name: manufacturerName }
903+
});
904+
if (!manufacturer) throw new NotFoundException('Manufacturer', manufacturerName);
905+
906+
if (unitName) {
907+
const unit = await prisma.unit.findFirst({
908+
where: { name: unitName }
909+
});
910+
if (!unit) throw new NotFoundException('Unit', unitName);
911+
}
912+
913+
const perms = isLeadership(submitter.role) || isUserPartOfTeams(project.teams, submitter);
914+
915+
if (!perms) throw new AccessDeniedException('update material');
916+
917+
const updatedMaterial = await prisma.material.update({
918+
where: { materialId },
919+
data: {
920+
name,
921+
status,
922+
materialTypeName,
923+
manufacturerName,
924+
manufacturerPartNumber,
925+
quantity,
926+
unitName,
927+
price,
928+
subtotal,
929+
linkUrl,
930+
notes,
931+
wbsElementId: project.wbsElementId,
932+
assemblyId,
933+
pdmFileName
934+
}
935+
});
936+
937+
return updatedMaterial;
938+
}
832939
}

src/backend/src/utils/errors.utils.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,4 +115,5 @@ type ExceptionObjectNames =
115115
| 'Assembly'
116116
| 'Material Type'
117117
| 'Manufacturer'
118-
| 'Unit';
118+
| 'Unit'
119+
| 'Material';

src/backend/src/utils/projects.utils.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,3 +60,40 @@ export const getUserFullName = async (userId: number | null): Promise<string | n
6060
if (!user) throw new NotFoundException('User', userId);
6161
return `${user.firstName} ${user.lastName}`;
6262
};
63+
64+
/**
65+
* Check if given assembly, material type, manufacturer, and unit exist in the app database
66+
* @param manufacturerName the manufacure of the material to check if it exists
67+
* @param materialTypeName the material type of the material to check if it exists
68+
* @param unitName the unit of the material to check if it exists
69+
* @param assemblyId the assembly of the material to check if it exists
70+
* @throws if any of these properties of the material does not exist in the db
71+
*/
72+
export const checkMaterialInputs = async (
73+
manufacturerName: string,
74+
materialTypeName: string,
75+
unitName?: string,
76+
assemblyId?: string
77+
) => {
78+
if (assemblyId) {
79+
const assembly = await prisma.assembly.findFirst({ where: { assemblyId } });
80+
if (!assembly) throw new NotFoundException('Assembly', assemblyId);
81+
}
82+
83+
const materialType = await prisma.material_Type.findFirst({
84+
where: { name: materialTypeName }
85+
});
86+
if (!materialType) throw new NotFoundException('Material Type', materialTypeName);
87+
88+
const manufacturer = await prisma.manufacturer.findFirst({
89+
where: { name: manufacturerName }
90+
});
91+
if (!manufacturer) throw new NotFoundException('Manufacturer', manufacturerName);
92+
93+
if (unitName) {
94+
const unit = await prisma.unit.findFirst({
95+
where: { name: unitName }
96+
});
97+
if (!unit) throw new NotFoundException('Unit', unitName);
98+
}
99+
};

0 commit comments

Comments
 (0)