Skip to content

Conversation

@tiago-graf
Copy link
Collaborator

@tiago-graf tiago-graf commented Feb 1, 2026

Summary

Adds a duplicate validation when manually adding a SIN number to a student in the Ministry Portal.

Frontend

Updates the AddNewSIN modal component to include new banner and confirmation checkbox.
Updates the SINManagement view to move the save SIN method to the AddNewSIN component (to allow resubmission without closing the modal)

Backend

Updates the create SIN model to include optional confirmDuplicate property.
Updates the SIN validation service to include new duplicate validation.
Updates the student controller to handle the custom error (duplicate not confirmed).

E2E

Adds new tests for the different validation scenarios.

image image

@tiago-graf tiago-graf added Ministry Ministry Features Web portal Backend Used by the dependabot pull requests to identify PRs related to the backend. E2E/Unit tests labels Feb 1, 2026
@tiago-graf tiago-graf requested a review from Copilot February 2, 2026 22:51
@tiago-graf tiago-graf marked this pull request as ready for review February 2, 2026 22:51
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR implements a duplicate SIN validation check when Ministry users manually add a SIN to a student profile. If a duplicate is detected, the user is warned and must explicitly confirm before proceeding.

Changes:

  • Adds optional confirmDuplicateSIN field to the CreateSINValidation DTO (frontend and backend)
  • Implements duplicate SIN check in the SINValidationService and controller
  • Updates the AddNewSIN modal to display a warning banner and confirmation checkbox when duplicates are detected, and moves the save logic into the modal component to allow resubmission
  • Adds comprehensive e2e tests covering duplicate rejection, duplicate confirmation, and unique SIN scenarios

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
sources/packages/web/src/views/aest/student/SINManagement.vue Moves SIN creation logic to modal component; simplifies parent component's responsibilities
sources/packages/web/src/services/http/dto/Student.dto.ts Adds optional confirmDuplicateSIN field to CreateSINValidationAPIInDTO
sources/packages/web/src/constants/error-code.constants.ts Defines SIN_DUPLICATE_NOT_CONFIRMED error constant
sources/packages/web/src/components/common/sin/AddNewSIN.vue Adds duplicate warning UI, confirmation checkbox, and inline SIN creation logic with error handling
sources/packages/backend/apps/api/src/services/sin-validation/sin-validation.service.ts Implements checkDuplicateSIN method to detect existing SINs across student profiles
sources/packages/backend/apps/api/src/route-controllers/student/student.aest.controller.ts Adds duplicate SIN validation and throws UnprocessableEntityException when not confirmed
sources/packages/backend/apps/api/src/route-controllers/student/models/student.dto.ts Adds optional confirmDuplicateSIN boolean field with validation decorators
sources/packages/backend/apps/api/src/route-controllers/student/tests/e2e/student.aest.controller.createStudentSINValidation.e2e-spec.ts Adds e2e tests for duplicate rejection, duplicate confirmation, and unique SIN scenarios
sources/packages/backend/apps/api/src/constants/error-code.constants.ts Defines SIN_DUPLICATE_NOT_CONFIRMED constant for backend error handling

tiago-graf and others added 2 commits February 2, 2026 16:39
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@tiago-graf tiago-graf changed the title Implements sin duplicate check #4539 - Add Duplicate SIN Validation Feb 3, 2026
@tiago-graf tiago-graf self-assigned this Feb 3, 2026
@dheepak-aot dheepak-aot self-requested a review February 3, 2026 20:15
@weskubo-cgi weskubo-cgi self-requested a review February 3, 2026 21:29
@dheepak-aot dheepak-aot added the SIMS-Api SIMS-Api label Feb 3, 2026
Copy link
Collaborator

@weskubo-cgi weskubo-cgi left a comment

Choose a reason for hiding this comment

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

Changes look great. Just some minor questions/comments to resolve.

@@ -1,6 +1,6 @@
<template>
Copy link
Collaborator

Choose a reason for hiding this comment

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

thought: There's a lot going on in this screen now. Should we do quick review with the business?

Image

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

yeah, i believe it even makes the modal screen scroll a bit when all the banners are displayed

const addNewSINModal = ref(
{} as ModalDialog<CreateSINValidationAPIInDTO | boolean>,
);
const addNewSINModal = ref<InstanceType<typeof AddNewSIN>>();
Copy link
Collaborator

Choose a reason for hiding this comment

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

question: Is there any reason to use this instead of const addNewSINModal = ref({} as ModalDialog<boolean>);

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Not a particular reason, it only changed because the modal wont return the Model anymore, and i like the InstaceType better.

Copy link
Collaborator

Choose a reason for hiding this comment

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

As all the modals follow a common design system with type ModalDialog. I would stick to common interface. The modified approach is better if a component has it's own unique properties which requires to be typed.

Comment on lines 69 to 75
.getRepository(Student)
.createQueryBuilder("student")
.innerJoin("student.sinValidation", "sinValidation")
.select("student.id")
.where("sinValidation.sin = :sin", { sin: normalizedSIN })
.andWhere("student.id != :studentId", { studentId })
.getOne();
Copy link
Collaborator

@dheepak-aot dheepak-aot Feb 3, 2026

Choose a reason for hiding this comment

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

using exists would simplify this.

const exists = await this.dataSource.getRepository(Student).exists({
      where: {
        id: Not(studentId),
        sinValidation: {
          sin: normalizedSIN,
        },
      },
    });

Copy link
Collaborator

Choose a reason for hiding this comment

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

Sorry for not being clear. returning the exists would be good.

return this.dataSource.getRepository(Student).exists({
      where: {
        id: Not(studentId),
        sinValidation: {
          sin: normalizedSIN,
        },
      },
    });

class="mt-4"
v-if="showDuplicateWarning"
:type="BannerTypes.Warning"
header="Duplicate SIN Warning"
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please share the title with business as it wasn't in the AC.

v-if="showDuplicateWarning"
:type="BannerTypes.Warning"
header="Duplicate SIN Warning"
summary="This SIN is currently associated with another student profile. Please investigate and correct any profiles with the incorrect SIN. If this is correct for the current student, please confirm and click 'Add SIN now'."
Copy link
Collaborator

Choose a reason for hiding this comment

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

Minor. The AC has Add SIN now enclosed in double quotes.

@sonarqubecloud
Copy link

sonarqubecloud bot commented Feb 3, 2026

@github-actions
Copy link

github-actions bot commented Feb 3, 2026

Backend Unit Tests Coverage Report

Totals Coverage
Statements: 20.15% ( 4322 / 21444 )
Methods: 9.67% ( 253 / 2616 )
Lines: 24.32% ( 3711 / 15258 )
Branches: 10.03% ( 358 / 3570 )

@github-actions
Copy link

github-actions bot commented Feb 3, 2026

E2E Workflow Workers Coverage Report

Totals Coverage
Statements: 75.41% ( 1055 / 1399 )
Methods: 79.31% ( 115 / 145 )
Lines: 78.79% ( 769 / 976 )
Branches: 61.51% ( 171 / 278 )

@github-actions
Copy link

github-actions bot commented Feb 3, 2026

E2E Queue Consumers Coverage Report

Totals Coverage
Statements: 85.68% ( 1616 / 1886 )
Methods: 85% ( 187 / 220 )
Lines: 88.64% ( 1287 / 1452 )
Branches: 66.36% ( 142 / 214 )

@github-actions
Copy link

github-actions bot commented Feb 3, 2026

E2E SIMS API Coverage Report

Totals Coverage
Statements: 77.52% ( 8903 / 11485 )
Methods: 77% ( 1051 / 1365 )
Lines: 81.59% ( 6460 / 7918 )
Branches: 63.22% ( 1392 / 2202 )

const showDuplicateWarning = ref(false);

const requiredCheckboxRule = (value: boolean) =>
value || "You must confirm to proceed";
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please add a period to the message.


const cancel = () => {
addNewSINForm.value.reset();
showDuplicateWarning.value = false;
Copy link
Collaborator

@dheepak-aot dheepak-aot Feb 4, 2026

Choose a reason for hiding this comment

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

Is the value required to be reset??

Comment on lines +358 to +362
// Check for duplicate SIN if confirmation is not provided.
isDuplicate = await this.sinValidationService.checkDuplicateSIN(
studentId,
payload.sin,
);
Copy link
Collaborator

Choose a reason for hiding this comment

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

The error can be thrown inside the if block as it does not have reason to live outside.

if (!payload.confirmDuplicateSIN) {
      // Check for duplicate SIN if confirmation is not provided.
      const isDuplicate = await this.sinValidationService.checkDuplicateSIN(
        studentId,
        payload.sin,
      );
      if (isDuplicate) {
        throw new UnprocessableEntityException(
          new ApiProcessError(
            "This SIN is currently associated with another student profile and confirmation to allow duplication SIN is missing.",
            SIN_DUPLICATE_NOT_CONFIRMED,
          ),
        );
      }
    }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Backend Used by the dependabot pull requests to identify PRs related to the backend. E2E/Unit tests Ministry Ministry Features SIMS-Api SIMS-Api Web portal

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants