-
Notifications
You must be signed in to change notification settings - Fork 9
#3907 automatic change requests updating roles #3965
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from all commits
e22f234
93ae012
9ceaa61
50865d7
ceb8b09
86bedfd
881fa0f
5b03054
001bd26
a0d2bc9
8c0e9cd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| -- AlterEnum | ||
| ALTER TYPE "CR_Type" ADD VALUE 'LEADERSHIP'; | ||
|
|
||
| -- CreateTable | ||
| CREATE TABLE "Leadership_CR" ( | ||
| "leadershipCrId" TEXT NOT NULL, | ||
| "changeRequestId" TEXT NOT NULL, | ||
| "leadId" TEXT, | ||
| "managerId" TEXT, | ||
|
|
||
| CONSTRAINT "Leadership_CR_pkey" PRIMARY KEY ("leadershipCrId") | ||
| ); | ||
|
|
||
| -- CreateIndex | ||
| CREATE UNIQUE INDEX "Leadership_CR_changeRequestId_key" ON "Leadership_CR"("changeRequestId"); | ||
|
|
||
| -- CreateIndex | ||
| CREATE INDEX "Leadership_CR_changeRequestId_idx" ON "Leadership_CR"("changeRequestId"); | ||
|
|
||
| -- AddForeignKey | ||
| ALTER TABLE "Leadership_CR" ADD CONSTRAINT "Leadership_CR_changeRequestId_fkey" FOREIGN KEY ("changeRequestId") REFERENCES "Change_Request"("crId") ON DELETE RESTRICT ON UPDATE CASCADE; | ||
|
|
||
| -- AddForeignKey | ||
| ALTER TABLE "Leadership_CR" ADD CONSTRAINT "Leadership_CR_leadId_fkey" FOREIGN KEY ("leadId") REFERENCES "User"("userId") ON DELETE SET NULL ON UPDATE CASCADE; | ||
|
|
||
| -- AddForeignKey | ||
| ALTER TABLE "Leadership_CR" ADD CONSTRAINT "Leadership_CR_managerId_fkey" FOREIGN KEY ("managerId") REFERENCES "User"("userId") ON DELETE SET NULL ON UPDATE CASCADE; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1008,6 +1008,135 @@ export default class ChangeRequestsService { | |
| return changeRequestTransformer(createdChangeRequest); | ||
| } | ||
|
|
||
| /** | ||
| * Validates and creates a leadership change request, auto-approved immediately. | ||
| * Updates the lead and/or manager of a project or work package without requiring review. | ||
| * @param submitter the user creating the cr | ||
| * @param carNumber the car number for the wbs element | ||
| * @param projectNumber the project number for the wbs element | ||
| * @param workPackageNumber the work package number for the wbs element | ||
| * @param leadId the id of the new lead | ||
| * @param managerId the id of the new manager | ||
| * @param organization the organization the user is currently in | ||
| * @returns the id of the created cr | ||
| */ | ||
| static async createLeadershipChangeRequest( | ||
| submitter: User, | ||
| carNumber: number, | ||
| projectNumber: number, | ||
| workPackageNumber: number, | ||
| leadId: string | undefined, | ||
| managerId: string | undefined, | ||
| organization: Organization | ||
| ): Promise<string> { | ||
| if (await userHasPermission(submitter.userId, organization.organizationId, isGuest)) | ||
| throw new AccessDeniedGuestException('create leadership change requests'); | ||
|
|
||
| // verify wbs element exists | ||
| const wbsElement = await prisma.wBS_Element.findUnique({ | ||
| where: { | ||
| wbsNumber: { | ||
| carNumber, | ||
| projectNumber, | ||
| workPackageNumber, | ||
| organizationId: organization.organizationId | ||
| } | ||
| } | ||
| }); | ||
|
|
||
| if (!wbsElement) throw new NotFoundException('WBS Element', wbsPipe({ carNumber, projectNumber, workPackageNumber })); | ||
| if (wbsElement.dateDeleted) | ||
| throw new DeletedException('WBS Element', wbsPipe({ carNumber, projectNumber, workPackageNumber })); | ||
| if (wbsElement.organizationId !== organization.organizationId) throw new InvalidOrganizationException('WBS Element'); | ||
|
|
||
| // avoid merge conflicts | ||
| await validateNoUnreviewedOpenCRs(wbsElement.wbsElementId); | ||
|
|
||
| const numChangeRequests = await prisma.change_Request.count({ | ||
| where: { organizationId: organization.organizationId } | ||
| }); | ||
|
|
||
| const createdCR = await prisma.change_Request.create({ | ||
| data: { | ||
| submitter: { connect: { userId: submitter.userId } }, | ||
| wbsElement: { connect: { wbsElementId: wbsElement.wbsElementId } }, | ||
| type: CR_Type.LEADERSHIP, | ||
| organization: { connect: { organizationId: organization.organizationId } }, | ||
| identifier: numChangeRequests + 1, | ||
| leadershipChangeRequest: { | ||
| create: { | ||
| ...(leadId && { lead: { connect: { userId: leadId } } }), | ||
| ...(managerId && { manager: { connect: { userId: managerId } } }) | ||
| } | ||
| } | ||
| } | ||
| }); | ||
|
|
||
| await ChangeRequestsService.applyLeadershipChangeRequest(createdCR.crId, wbsElement, submitter, leadId, managerId); | ||
|
|
||
| return createdCR.crId; | ||
| } | ||
|
|
||
| /** | ||
| * Applies a leadership change request by updating the wbs element's lead/manager | ||
| * and auto-approving the change request. | ||
| */ | ||
| private static async applyLeadershipChangeRequest( | ||
| crId: string, | ||
| wbsElement: { wbsElementId: string; leadId: string | null; managerId: string | null }, | ||
| submitter: User, | ||
| leadId: string | undefined, | ||
| managerId: string | undefined | ||
| ): Promise<void> { | ||
| await prisma.$transaction(async (tx) => { | ||
| await tx.change_Request.update({ | ||
| where: { crId }, | ||
| data: { | ||
| reviewer: { connect: { userId: submitter.userId } }, | ||
| dateReviewed: new Date(), | ||
| accepted: true, | ||
| reviewNotes: 'Auto-approved: leadership change only' | ||
| } | ||
| }); | ||
|
|
||
| await tx.wBS_Element.update({ | ||
| where: { wbsElementId: wbsElement.wbsElementId }, | ||
| data: { | ||
| ...(leadId && { lead: { connect: { userId: leadId } } }), | ||
| ...(managerId && { manager: { connect: { userId: managerId } } }) | ||
| } | ||
| }); | ||
|
|
||
| const changes: { changeRequestId: string; implementerId: string; wbsElementId: string; detail: string }[] = []; | ||
|
|
||
| if (leadId !== undefined) { | ||
| const oldLead = await getUserFullName(wbsElement.leadId ?? null); | ||
| const newLead = await getUserFullName(leadId); | ||
| changes.push({ | ||
| changeRequestId: crId, | ||
| implementerId: submitter.userId, | ||
| wbsElementId: wbsElement.wbsElementId, | ||
| detail: buildChangeDetail('lead', oldLead, newLead) | ||
| }); | ||
| } | ||
|
|
||
| if (managerId !== undefined) { | ||
| const oldManager = await getUserFullName(wbsElement.managerId ?? null); | ||
| const newManager = await getUserFullName(managerId); | ||
| changes.push({ | ||
| changeRequestId: crId, | ||
| implementerId: submitter.userId, | ||
| wbsElementId: wbsElement.wbsElementId, | ||
| detail: buildChangeDetail('manager', oldManager, newManager) | ||
| }); | ||
| } | ||
|
Comment on lines
+1112
to
+1132
|
||
|
|
||
| if (changes.length > 0) { | ||
| await tx.change.createMany({ data: changes }); | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| /** | ||
| * Validates and creates a standard change request | ||
| * @param submitter The user creating the cr | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing validation to ensure at least one of leadId or managerId is provided. A leadership change request with neither field set is meaningless. Add validation to check that at least one field is defined before creating the change request.