Skip to content

Commit

Permalink
#1978 - Request an Offering Change: Student View Request
Browse files Browse the repository at this point in the history
  • Loading branch information
sh16011993 committed Aug 16, 2023
1 parent a4ac1f3 commit 8707332
Show file tree
Hide file tree
Showing 45 changed files with 1,223 additions and 62 deletions.
2 changes: 2 additions & 0 deletions sources/packages/backend/apps/api/src/app.students.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import {
EducationProgramStudentsController,
StudentAccountApplicationStudentsController,
EducationProgramOfferingStudentsController,
ApplicationOfferingChangeRequestStudentsController,
EducationProgramOfferingControllerService,
RestrictionStudentsController,
ProgramYearStudentsController,
Expand Down Expand Up @@ -77,6 +78,7 @@ import { ATBCIntegrationModule } from "@sims/integrations/atbc-integration";
ATBCStudentController,
ProgramYearStudentsController,
OverawardStudentsController,
ApplicationOfferingChangeRequestStudentsController,
],
providers: [
WorkflowClientService,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,3 +136,13 @@ export const OFFERING_PROGRAM_YEAR_MISMATCH = "OFFERING_PROGRAM_YEAR_MISMATCH";
*/
export const OFFERING_DOES_NOT_BELONG_TO_LOCATION =
"OFFERING_DOES_NOT_BELONG_TO_LOCATION";
/**
* Student not authorized for the offering details summary.
*/
export const STUDENT_UNAUTHORIZED_FOR_OFFERING =
"STUDENT_NOT_AUTHORIZED_FOR_OFFERING";
/**
* Student not authorized for the application offering change request.
*/
export const STUDENT_UNAUTHORIZED_FOR_APPLICATION_OFFERING_CHANGE_REQUEST =
"STUDENT_NOT_AUTHORIZED_FOR_APPLICATION_OFFERING_CHANGE_REQUEST";
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ import {
ApplicationOfferingChangesAPIOutDTO,
ApplicationOfferingChangeSummaryDetailAPIOutDTO,
CreateApplicationOfferingChangeRequestAPIInDTO,
} from "./models/application-offering-change-request.institutions.dto";
} from "./models/application-offering-change-request.dto";
import {
APPLICATION_NOT_FOUND,
ApplicationOfferingChangeRequestService,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
import {
Body,
Controller,
Get,
NotFoundException,
Param,
ParseIntPipe,
Patch,
UnauthorizedException,
} from "@nestjs/common";
import {
ApiNotFoundResponse,
ApiTags,
ApiUnauthorizedResponse,
} from "@nestjs/swagger";
import { AuthorizedParties } from "../../auth/authorized-parties.enum";
import { AllowAuthorizedParty, UserToken } from "../../auth/decorators";
import { ApiProcessError, ClientTypeBaseRoute } from "../../types";
import { getUserFullName } from "../../utilities";
import BaseController from "../BaseController";
import {
ApplicationOfferingChangesAPIOutDTO,
UpdateApplicationOfferingChangeRequestAPIInDTO,
} from "./models/application-offering-change-request.dto";
import { ApplicationOfferingChangeRequestService } from "../../services";
import { ApplicationOfferingChangeRequestStatus } from "@sims/sims-db";
import { StudentUserToken } from "../../auth";
import { STUDENT_UNAUTHORIZED_FOR_APPLICATION_OFFERING_CHANGE_REQUEST } from "../../constants";

/**
* Application offering change request controller for students client.
*/
@AllowAuthorizedParty(AuthorizedParties.student)
@Controller(
"application-offering-change-request/:applicationOfferingChangeRequestId",
)
@ApiTags(`${ClientTypeBaseRoute.Student}-application-offering-change-request`)
export class ApplicationOfferingChangeRequestStudentsController extends BaseController {
constructor(
private readonly applicationOfferingChangeRequestService: ApplicationOfferingChangeRequestService,
) {
super();
}

/**
* Gets the Application Offering Change Request details.
* @param applicationOfferingChangeRequestId the Application Offering Change Request id.
* @param studentUserToken student user token to authorize the user.
* @returns Application Offering Change Request details.
*/
@Get()
@ApiNotFoundResponse({
description: "Not able to find an Application Offering Change Request.",
})
async getById(
@Param("applicationOfferingChangeRequestId", ParseIntPipe)
applicationOfferingChangeRequestId: number,
@UserToken()
studentUserToken: StudentUserToken,
): Promise<ApplicationOfferingChangesAPIOutDTO> {
const request = await this.applicationOfferingChangeRequestService.getById(
applicationOfferingChangeRequestId,
{ studentId: studentUserToken.studentId },
);
if (!request) {
throw new NotFoundException(
"Not able to find an Application Offering Change Request.",
);
}
return {
id: request.id,
status: request.applicationOfferingChangeRequestStatus,
applicationId: request.application.id,
applicationNumber: request.application.applicationNumber,
locationName: request.application.location.name,
activeOfferingId: request.activeOffering.id,
requestedOfferingId: request.requestedOffering.id,
requestedOfferingDescription: request.requestedOffering.name,
requestedOfferingProgramId: request.requestedOffering.educationProgram.id,
requestedOfferingProgramName:
request.requestedOffering.educationProgram.name,
reason: request.reason,
assessedNoteDescription: request.assessedNote?.description,
studentFullName: getUserFullName(request.application.student.user),
};
}

/**
* Gets the Application Offering Change Request status.
* @param applicationOfferingChangeRequestId the Application Offering Change Request id.
* @param studentUserToken student user token to authorize the user.
* @returns Application Offering Change Request status.
*/
@Get("application-offering-change-request-status")
@ApiNotFoundResponse({
description:
"Not able to get the Application Offering Change Request Status.",
})
async getApplicationOfferingChangeRequestStatusById(
@Param("applicationOfferingChangeRequestId", ParseIntPipe)
applicationOfferingChangeRequestId: number,
@UserToken()
studentUserToken: StudentUserToken,
): Promise<ApplicationOfferingChangeRequestStatus> {
const applicationOfferingChangeRequestStatus =
await this.applicationOfferingChangeRequestService.getApplicationOfferingChangeRequestStatusById(
applicationOfferingChangeRequestId,
{
studentId: studentUserToken.studentId,
},
);
if (!applicationOfferingChangeRequestStatus) {
throw new NotFoundException(
"Not able to get the Application Offering Change Request Status.",
);
}
return applicationOfferingChangeRequestStatus;
}

/**
* Updates an application offering change request status.
* @param applicationOfferingChangeRequestId application offering change request id.
* @param userToken user token to authorize the user.
* @param payload information to update the application offering change request status.
*/
@Patch()
@ApiUnauthorizedResponse({
description:
"The student does not have access to the application offering change request.",
})
async updateApplicationOfferingChangeRequestStatus(
@Param("applicationOfferingChangeRequestId", ParseIntPipe)
applicationOfferingChangeRequestId: number,
@UserToken()
userToken: StudentUserToken,
@Body()
payload: UpdateApplicationOfferingChangeRequestAPIInDTO,
) {
const studentAuthorized =
this.applicationOfferingChangeRequestService.validateStudentForOfferingChangeRequest(
applicationOfferingChangeRequestId,
userToken.studentId,
);
if (studentAuthorized)
await this.applicationOfferingChangeRequestService.updateApplicationOfferingChangeRequestStatus(
applicationOfferingChangeRequestId,
payload.applicationOfferingChangeRequestStatus,
{
studentConsent: payload?.studentConsent,
},
);
else
throw new UnauthorizedException(
new ApiProcessError(
"Student is not authorized for the provided offering.",
STUDENT_UNAUTHORIZED_FOR_APPLICATION_OFFERING_CHANGE_REQUEST,
),
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import {
REASON_MAX_LENGTH,
} from "@sims/sims-db";
import {
IsBoolean,
IsEnum,
IsIn,
IsNotEmpty,
IsOptional,
Expand Down Expand Up @@ -120,3 +122,14 @@ export class CreateApplicationOfferingChangeRequestAPIInDTO {
@MaxLength(REASON_MAX_LENGTH)
reason: string;
}

/**
* Details to update the application offering change request by student.
*/
export class UpdateApplicationOfferingChangeRequestAPIInDTO {
@IsOptional()
@IsBoolean()
studentConsent?: boolean;
@IsEnum(ApplicationOfferingChangeRequestStatus)
applicationOfferingChangeRequestStatus: ApplicationOfferingChangeRequestStatus;
}
Original file line number Diff line number Diff line change
Expand Up @@ -655,6 +655,7 @@ export class ApplicationStudentsController extends BaseController {
assessmentTriggerType: application.currentAssessment.triggerType,
appealStatus: appeal?.status,
scholasticStandingChangeType: scholasticStandingChange?.changeType,
applicationOfferingChangeRequestId: applicationOfferingChangeRequest?.id,
applicationOfferingChangeRequestStatus:
applicationOfferingChangeRequest?.applicationOfferingChangeRequestStatus,
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -175,5 +175,6 @@ export class CompletedApplicationDetailsAPIOutDTO extends EnrolmentApplicationDe
assessmentTriggerType: AssessmentTriggerType;
appealStatus?: StudentAppealStatus;
scholasticStandingChangeType?: StudentScholasticStandingChangeType;
applicationOfferingChangeRequestId?: number;
applicationOfferingChangeRequestStatus?: ApplicationOfferingChangeRequestStatus;
}
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,8 @@ export class AssessmentControllerService {
offeringId: assessment.offering.id,
programId: assessment.offering.educationProgram.id,
studentAppealId: assessment.studentAppeal?.id,
applicationOfferingChangeRequestId:
assessment.applicationOfferingChangeRequest.id,
applicationExceptionId: assessment.application.applicationException?.id,
studentScholasticStandingId: assessment.studentScholasticStanding?.id,
}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export class AssessmentHistorySummaryAPIOutDTO {
offeringId?: number;
programId?: number;
studentAppealId?: number;
applicationOfferingChangeRequestId?: number;
applicationExceptionId?: number;
studentScholasticStandingId?: number;
// This flag decides, the row is unsuccessful week or not.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,21 @@ export class EducationProgramOfferingControllerService {
private readonly programOfferingService: EducationProgramOfferingService,
) {}

/** Validates student authorization for the given education program offering.
* @param offeringId offering id.
* @param studentId student id.
* @returns true is the student is authorized for the given offering, otherwise false.
*/
async validateApplicationOfferingForStudent(
offeringId: number,
studentId: number,
): Promise<boolean> {
return this.offeringService.validateApplicationOfferingForStudent(
offeringId,
studentId,
);
}

/**
* Get summary of offerings for a program and location.
* Pagination, sort and search are available on results.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,31 @@ import {
ParseBoolPipe,
ParseIntPipe,
Query,
UnauthorizedException,
UnprocessableEntityException,
} from "@nestjs/common";
import {
ApiNotFoundResponse,
ApiTags,
ApiUnauthorizedResponse,
ApiUnprocessableEntityResponse,
} from "@nestjs/swagger";
import { AuthorizedParties } from "../../auth/authorized-parties.enum";
import { AllowAuthorizedParty } from "../../auth/decorators";
import { AllowAuthorizedParty, UserToken } from "../../auth/decorators";
import { OfferingIntensity, OfferingTypes } from "@sims/sims-db";
import { EducationProgramOfferingService } from "../../services";
import { ClientTypeBaseRoute } from "../../types";
import { ApiProcessError, ClientTypeBaseRoute } from "../../types";
import BaseController from "../BaseController";
import { OfferingDetailsAPIOutDTO } from "./models/education-program-offering.dto";
import {
EducationProgramOfferingSummaryViewAPIOutDTO,
OfferingDetailsAPIOutDTO,
} from "./models/education-program-offering.dto";
import { OptionItemAPIOutDTO } from "../models/common.dto";
import { EducationProgramOfferingControllerService } from "./education-program-offering.controller.service";
import { ParseEnumQueryPipe } from "../utils/custom-validation-pipe";
import { ApplicationOfferingPurpose } from "@sims/sims-db";
import { StudentUserToken } from "../../auth";
import { STUDENT_UNAUTHORIZED_FOR_OFFERING } from "../../constants";

@AllowAuthorizedParty(AuthorizedParties.student)
@Controller("education-program-offering")
Expand Down Expand Up @@ -93,4 +101,44 @@ export class EducationProgramOfferingStudentsController extends BaseController {
studyEndDate: offering.studyEndDate,
};
}

/**
* Gets the offering simplified details, not including, for instance,
* validations, approvals and extensive data.
* Useful to have an overview of the offering details, for instance,
* when the user needs need to have quick access to data in order to
* support operations like confirmation of enrolment or scholastic
* standing requests or offering change request.
* @param offeringId offering.
* @param purpose indicating the purpose to allow for the appropriate authorization flow to be used.
* @param studentUserToken student user token for authorization.
* @returns offering details.
*/
@ApiUnauthorizedResponse({
description: "The student is not authorized for the provided offering.",
})
@Get("offering/:offeringId/summary-details")
async getOfferingSummaryDetailsById(
@Param("offeringId", ParseIntPipe) offeringId: number,
@Query("purpose", new ParseEnumQueryPipe(ApplicationOfferingPurpose))
purpose: ApplicationOfferingPurpose,
@UserToken() studentUserToken: StudentUserToken,
): Promise<EducationProgramOfferingSummaryViewAPIOutDTO> {
if (
purpose === ApplicationOfferingPurpose.OfferingChange &&
this.educationProgramOfferingControllerService.validateApplicationOfferingForStudent(
offeringId,
studentUserToken.studentId,
)
)
return this.educationProgramOfferingControllerService.getOfferingById(
offeringId,
);
throw new UnauthorizedException(
new ApiProcessError(
"Student is not authorized for the provided offering.",
STUDENT_UNAUTHORIZED_FOR_OFFERING,
),
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,4 @@ export * from "./restriction/restriction.institutions.controller";
export * from "./restriction/restriction.controller.service";
export * from "./note/note.institutions.controller";
export * from "./application-offering-change-request/application-offering-change-request.institutions.controller";
export * from "./application-offering-change-request/application-offering-change-request.students.controller";
Loading

0 comments on commit 8707332

Please sign in to comment.