Skip to content
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

#1978 - Request an Offering Change: Student View Request #2196

Merged
merged 33 commits into from
Aug 31, 2023
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
8707332
#1978 - Request an Offering Change: Student View Request
sh16011993 Aug 16, 2023
30b7be9
Code Update
sh16011993 Aug 16, 2023
cd7ee07
Code Update
sh16011993 Aug 16, 2023
3256149
Code update
sh16011993 Aug 16, 2023
5f83c73
Code Update
sh16011993 Aug 17, 2023
4421a3a
Code Update
sh16011993 Aug 17, 2023
802bdd6
Code Update
sh16011993 Aug 22, 2023
5684742
Code Update
sh16011993 Aug 22, 2023
d365524
Code Update
sh16011993 Aug 22, 2023
866cb8a
Code Update
sh16011993 Aug 23, 2023
de02138
Code update
sh16011993 Aug 24, 2023
fe1cd55
Code update
sh16011993 Aug 24, 2023
1386b79
Code Update
sh16011993 Aug 24, 2023
c67a4a5
Merge branch 'main' into 1978_request_offering_change_student_view_re…
sh16011993 Aug 25, 2023
84ccbbb
Code Update
sh16011993 Aug 25, 2023
332a873
code update
sh16011993 Aug 25, 2023
919ca6e
code update
sh16011993 Aug 25, 2023
466f57b
code update
sh16011993 Aug 25, 2023
6b2f130
Code Update as per review comments
sh16011993 Aug 25, 2023
e231129
Merge branch 'main' into 1978_request_offering_change_student_view_re…
sh16011993 Aug 25, 2023
a0437e5
Code update - Unused comment
sh16011993 Aug 25, 2023
1a19253
Code update
sh16011993 Aug 25, 2023
defece4
Code update
sh16011993 Aug 29, 2023
44c8612
Code Update
sh16011993 Aug 30, 2023
0ab2cb7
code update
sh16011993 Aug 30, 2023
0ec2d78
Code Update
sh16011993 Aug 30, 2023
7d002cb
Code update
sh16011993 Aug 30, 2023
c353925
Code Update
sh16011993 Aug 30, 2023
b7eeac1
Code Update
sh16011993 Aug 30, 2023
1239af6
Code update
sh16011993 Aug 31, 2023
04ef5cc
Code update
sh16011993 Aug 31, 2023
15b23de
Code update
sh16011993 Aug 31, 2023
09b31aa
Code Update
sh16011993 Aug 31, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ import {
NoteInstitutionsController,
RestrictionControllerService,
ApplicationOfferingChangeRequestInstitutionsController,
ApplicationOfferingChangeRequestControllerService,
} from "./route-controllers";
import { AuthModule } from "./auth/auth.module";
import {
Expand Down Expand Up @@ -162,6 +163,7 @@ import {
ApplicationExceptionControllerService,
StudentAppealControllerService,
ApplicationOfferingChangeRequestService,
ApplicationOfferingChangeRequestControllerService,
],
})
export class AppInstitutionsModule {}
4 changes: 4 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,8 @@ import {
EducationProgramStudentsController,
StudentAccountApplicationStudentsController,
EducationProgramOfferingStudentsController,
ApplicationOfferingChangeRequestStudentsController,
ApplicationOfferingChangeRequestControllerService,
EducationProgramOfferingControllerService,
RestrictionStudentsController,
ProgramYearStudentsController,
Expand Down Expand Up @@ -77,6 +79,7 @@ import { ATBCIntegrationModule } from "@sims/integrations/atbc-integration";
ATBCStudentController,
ProgramYearStudentsController,
OverawardStudentsController,
ApplicationOfferingChangeRequestStudentsController,
],
providers: [
WorkflowClientService,
Expand Down Expand Up @@ -117,6 +120,7 @@ import { ATBCIntegrationModule } from "@sims/integrations/atbc-integration";
OverawardControllerService,
StudentAppealControllerService,
ConfirmationOfEnrollmentService,
ApplicationOfferingChangeRequestControllerService,
ApplicationOfferingChangeRequestService,
],
})
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import { Injectable, NotFoundException } from "@nestjs/common";
import { ApplicationOfferingChangeRequestService } from "../../services";
import {
ApplicationOfferingChangesAPIOutDTO,
ApplicationOfferingDetailsAPIOutDTO,
} from "./models/application-offering-change-request.dto";
import { getUserFullName } from "../../utilities";

@Injectable()
export class ApplicationOfferingChangeRequestControllerService {
constructor(
private readonly applicationOfferingChangeRequestService: ApplicationOfferingChangeRequestService,
) {}

/**
* Get the Application Offering Change Request by its id for the institution.
* @param id the Application Offering Change Request id.
* @param options method options:
* - `locationId`: location id for institution authorization.
* @returns application offering change request.
*/
async getById(
Copy link
Contributor

@guru-aot guru-aot Aug 25, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I get your point, there is nothing wrong in creating a overridden method, but the reason for naming the methods for the process they do is what we are following as a standard. So it would be better to make them explanatory and explaing the paramaters wherever possible. Also the variable names are mentioned as proper applicationOfferingChangeRequestId rather than id, its again for the same purpose and even if the developer misses the comments on the top of the method the calling class will have an idea when we try to implement them.
Also this will create consistency of the existing code.

id: number,
options?: {
locationId?: number;
},
): Promise<ApplicationOfferingChangesAPIOutDTO>;

/**
* Get the Application Offering Change Request by its id for the student.
* @param id the Application Offering Change Request id.
* @param options method options:
* - `studentId`: student id for student authorization.
* - `applicationOfferingDetails`: boolean true indicates that only the required application offering details for the student are requested.
* @returns application offering change request.
*/
async getById(
id: number,
options?: {
studentId?: number;
applicationOfferingDetails?: boolean;
},
): Promise<ApplicationOfferingDetailsAPIOutDTO>;

/**
* Get the Application Offering Change Request by its id.
* @param id the Application Offering Change Request id.
* @param options method options:
* - `studentId`: student id for student authorization.
* - `locationId`: location id for institution authorization.
* - `applicationOfferingDetails`: boolean true indicates that only the required application offering details for the student are requested.
* @returns application offering change request.
*/
async getById(
id: number,
options?: {
studentId?: number;
locationId?: number;
applicationOfferingDetails?: boolean;
},
): Promise<
ApplicationOfferingChangesAPIOutDTO | ApplicationOfferingDetailsAPIOutDTO
> {
const request = await this.applicationOfferingChangeRequestService.getById(
id,
{ studentId: options?.studentId, locationId: options?.locationId },
);
if (!request) {
throw new NotFoundException(
"Not able to find an Application Offering Change Request.",
);
}
if (options?.applicationOfferingDetails) {
return {
status: request.applicationOfferingChangeRequestStatus,
applicationNumber: request.application.applicationNumber,
locationName: request.application.location.name,
requestedOfferingId: request.requestedOffering.id,
activeOfferingId: request.activeOffering.id,
reason: request.reason,
};
}
Copy link
Contributor

@guru-aot guru-aot Aug 25, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rather than returning the same paramterers in 2 places, you can create a

const applicationOfferingDetails = {
status: request.applicationOfferingChangeRequestStatus,
applicationNumber: request.application.applicationNumber,
locationName: request.application.location.name,
activeOfferingId: request.activeOffering.id,
requestedOfferingId: request.requestedOffering.id,
reason: request.reason,
};

if (options?.applicationOfferingDetails) {
return applicationOfferingDetails;
}

return {
...applicationOfferingDetails,
id: request.id,
requestedOfferingDescription: request.requestedOffering.name,
requestedOfferingProgramId: request.requestedOffering.educationProgram.id,
requestedOfferingProgramName: request.requestedOffering.educationProgram.name,
assessedNoteDescription: request.assessedNote?.description,
studentFullName: getUserFullName(request.application.student.user),
};

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),
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import {
OFFERING_PROGRAM_YEAR_MISMATCH,
} from "../../constants";
import { InjectLogger, LoggerService } from "@sims/utilities/logger";
import { ApplicationOfferingChangeRequestControllerService } from "./application-offering-change-request.controller.service";

/**
* Application offering change request controller for institutions client.
Expand All @@ -65,6 +66,7 @@ import { InjectLogger, LoggerService } from "@sims/utilities/logger";
@HasLocationAccess("locationId")
export class ApplicationOfferingChangeRequestInstitutionsController extends BaseController {
constructor(
private readonly applicationOfferingChangeRequestControllerService: ApplicationOfferingChangeRequestControllerService,
private readonly applicationOfferingChangeRequestService: ApplicationOfferingChangeRequestService,
private readonly applicationService: ApplicationService,
) {
Expand Down Expand Up @@ -243,31 +245,10 @@ export class ApplicationOfferingChangeRequestInstitutionsController extends Base
applicationOfferingChangeRequestId: number,
@Param("locationId", ParseIntPipe) locationId: number,
): Promise<ApplicationOfferingChangesAPIOutDTO> {
const request = await this.applicationOfferingChangeRequestService.getById(
return this.applicationOfferingChangeRequestControllerService.getById(
applicationOfferingChangeRequestId,
{ locationId },
);
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),
};
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
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 { ClientTypeBaseRoute } from "../../types";
import BaseController from "../BaseController";
import {
ApplicationOfferingChangeRequestStatusAPIOutDTO,
ApplicationOfferingDetailsAPIOutDTO,
StudentApplicationOfferingChangeRequestAPIInDTO,
} from "./models/application-offering-change-request.dto";
import { StudentUserToken } from "../../auth";
import { ApplicationOfferingChangeRequestControllerService } from "./application-offering-change-request.controller.service";
import { ApplicationOfferingChangeRequestService } from "../../services";

/**
* Application offering change request controller for students client.
*/
@AllowAuthorizedParty(AuthorizedParties.student)
@Controller("application-offering-change-request")
@ApiTags(`${ClientTypeBaseRoute.Student}-application-offering-change-request`)
export class ApplicationOfferingChangeRequestStudentsController extends BaseController {
constructor(
private readonly applicationOfferingChangeRequestService: ApplicationOfferingChangeRequestService,
private readonly applicationOfferingChangeRequestControllerService: ApplicationOfferingChangeRequestControllerService,
) {
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(":applicationOfferingChangeRequestId")
@ApiNotFoundResponse({
description: "Not able to find an Application Offering Change Request.",
})
async getById(
@Param("applicationOfferingChangeRequestId", ParseIntPipe)
applicationOfferingChangeRequestId: number,
@UserToken()
studentUserToken: StudentUserToken,
): Promise<ApplicationOfferingDetailsAPIOutDTO> {
return this.applicationOfferingChangeRequestControllerService.getById(
applicationOfferingChangeRequestId,
{
studentId: studentUserToken.studentId,
applicationOfferingDetails: true,
},
);
}

/**
* 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(
":applicationOfferingChangeRequestId/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<ApplicationOfferingChangeRequestStatusAPIOutDTO> {
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 { status: 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(":applicationOfferingChangeRequestId")
@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: StudentApplicationOfferingChangeRequestAPIInDTO,
): Promise<void> {
const studentAuthorized =
await this.applicationOfferingChangeRequestService.getById(
applicationOfferingChangeRequestId,
{ studentId: userToken.studentId },
);
if (!studentAuthorized) {
throw new UnauthorizedException(
"Student is not authorized for the provided offering.",
);
}
await this.applicationOfferingChangeRequestService.updateApplicationOfferingChangeRequestStatus(
applicationOfferingChangeRequestId,
payload,
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,14 @@ import {
REASON_MAX_LENGTH,
} from "@sims/sims-db";
import {
IsBoolean,
IsEnum,
IsIn,
IsNotEmpty,
IsOptional,
IsPositive,
MaxLength,
ValidateIf,
} from "class-validator";
import { PaginationOptionsAPIInDTO } from "../../models/pagination.dto";

Expand Down Expand Up @@ -106,6 +109,22 @@ export class ApplicationOfferingChangesAPIOutDTO {
studentFullName: string;
}

export interface ApplicationOfferingDetailsAPIOutDTO {
applicationNumber: string;
locationName: string;
status: ApplicationOfferingChangeRequestStatus;
requestedOfferingId: number;
activeOfferingId: number;
reason: string;
}

/**
* Application Offering Change Request Status.
*/
export class ApplicationOfferingChangeRequestStatusAPIOutDTO {
status: ApplicationOfferingChangeRequestStatus;
}

/**
* Information provided by the institution to create a new Application Offering Change Request.
*/
Expand All @@ -119,6 +138,22 @@ export class CreateApplicationOfferingChangeRequestAPIInDTO {
reason: string;
}

/**
* Details to update the application offering change request by student.
*/
export class StudentApplicationOfferingChangeRequestAPIInDTO {
@IsOptional()
@IsBoolean()
@ValidateIf(
(value: StudentApplicationOfferingChangeRequestAPIInDTO) =>
value.applicationOfferingChangeRequestStatus ===
ApplicationOfferingChangeRequestStatus.InProgressWithSABC,
)
studentConsent: boolean;
@IsEnum(ApplicationOfferingChangeRequestStatus)
applicationOfferingChangeRequestStatus: ApplicationOfferingChangeRequestStatus;
}

/**
* All in progress application offering change details.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { ApplicationOfferingChangeRequestStatus } from "@sims/sims-db";

export interface StudentApplicationOfferingChangeRequest {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we still need it?

studentConsent: boolean;
applicationOfferingChangeRequestStatus: ApplicationOfferingChangeRequestStatus;
}
Loading