From 920ca4ad3f5792e07e369464c077b2928afbceb0 Mon Sep 17 00:00:00 2001 From: Tiago Graf Date: Sun, 15 Mar 2026 17:57:22 -0700 Subject: [PATCH 01/16] Make location code optional --- .../models/institution-location.dto.ts | 18 +++-- .../institution-location.models.ts | 3 +- .../institution-location.service.ts | 56 ++++++++------ .../entities/institution-location.model.ts | 3 +- sources/packages/forms/package-lock.json | 4 +- .../form-definitions/institutionlocation.json | 61 +++++++++++++++- .../modals/ApproveDenyDesignationModal.vue | 73 ++++++++++++------- .../DesignationAgreementForm.models.ts | 1 + .../http/dto/DesignationAgreement.dto.ts | 5 +- .../http/dto/InstitutionLocation.dto.ts | 11 ++- .../aest/institution/DesignationAESTView.vue | 6 +- .../institution/AddInstitutionLocation.vue | 13 ++-- 12 files changed, 179 insertions(+), 75 deletions(-) diff --git a/sources/packages/backend/apps/api/src/route-controllers/institution-locations/models/institution-location.dto.ts b/sources/packages/backend/apps/api/src/route-controllers/institution-locations/models/institution-location.dto.ts index b991c181a7..2d65d659ba 100644 --- a/sources/packages/backend/apps/api/src/route-controllers/institution-locations/models/institution-location.dto.ts +++ b/sources/packages/backend/apps/api/src/route-controllers/institution-locations/models/institution-location.dto.ts @@ -1,4 +1,10 @@ -import { Allow, IsEmail, IsNotEmpty, Length } from "class-validator"; +import { + Allow, + IsEmail, + IsNotEmpty, + IsOptional, + Length, +} from "class-validator"; import { AddressAPIOutDTO, AddressDetailsAPIInDTO, @@ -16,6 +22,8 @@ export class InstitutionLocationFormAPIInDTO extends AddressDetailsAPIInDTO { @Allow() institutionCode: string; @Allow() + noInstitutionCode?: boolean; + @Allow() primaryContactFirstName: string; @Allow() primaryContactLastName: string; @@ -47,8 +55,8 @@ export class AESTInstitutionLocationAPIInDTO extends AddressDetailsAPIInDTO { primaryContactPhone: string; @IsNotEmpty() locationName: string; - @IsNotEmpty() - institutionCode: string; + @IsOptional() + institutionCode?: string | null; } /** @@ -56,7 +64,7 @@ export class AESTInstitutionLocationAPIInDTO extends AddressDetailsAPIInDTO { */ export class InstitutionLocationDetailsAPIOutDTO extends AddressDetailsAPIOutDTO { locationName: string; - institutionCode: string; + institutionCode: string | null; primaryContactFirstName: string; primaryContactLastName: string; primaryContactEmail: string; @@ -80,7 +88,7 @@ export class InstitutionLocationAPIOutDTO { address: AddressAPIOutDTO; }; primaryContact: InstitutionPrimaryContactAPIOutDTO; - institutionCode: string; + institutionCode?: string; designationStatus: DesignationStatus; } diff --git a/sources/packages/backend/apps/api/src/services/institution-location/institution-location.models.ts b/sources/packages/backend/apps/api/src/services/institution-location/institution-location.models.ts index 80e64c2674..46adf926fa 100644 --- a/sources/packages/backend/apps/api/src/services/institution-location/institution-location.models.ts +++ b/sources/packages/backend/apps/api/src/services/institution-location/institution-location.models.ts @@ -18,7 +18,8 @@ export interface LocationWithDesignationStatus { */ export interface InstitutionLocationModel extends AddressInfo { locationName: string; - institutionCode: string; + institutionCode?: string; + noInstitutionCode?: boolean; primaryContactFirstName: string; primaryContactLastName: string; primaryContactEmail: string; diff --git a/sources/packages/backend/apps/api/src/services/institution-location/institution-location.service.ts b/sources/packages/backend/apps/api/src/services/institution-location/institution-location.service.ts index 307d016b15..f8b8d90f00 100644 --- a/sources/packages/backend/apps/api/src/services/institution-location/institution-location.service.ts +++ b/sources/packages/backend/apps/api/src/services/institution-location/institution-location.service.ts @@ -40,16 +40,20 @@ export class InstitutionLocationService extends RecordDataModelService { - const isInstitutionCodeDuplicate = await this.hasLocationCodeForInstitution( - institutionLocationData.institutionCode, - { locationId }, - ); + const hasInstitutionCode = + !!institutionLocationData.institutionCode?.trim(); - if (isInstitutionCodeDuplicate) { - throw new CustomNamedError( - "Duplicate institution location code.", - DUPLICATE_INSTITUTION_LOCATION_CODE, - ); + if (hasInstitutionCode) { + const isInstitutionCodeDuplicate = + await this.hasLocationCodeForInstitution( + institutionLocationData.institutionCode, + { + locationId, + }, + ); + + if (isInstitutionCodeDuplicate) { + throw new CustomNamedError( + "Duplicate institution location code.", + DUPLICATE_INSTITUTION_LOCATION_CODE, + ); + } } const saveLocation: InstitutionLocation = { @@ -131,7 +143,9 @@ export class InstitutionLocationService extends RecordDataModelService { - return this.buildLocationQuery(locationId, undefined).getRawOne(); + return this.buildLocationQuery(locationId).getRawOne(); } /** diff --git a/sources/packages/backend/libs/sims-db/src/entities/institution-location.model.ts b/sources/packages/backend/libs/sims-db/src/entities/institution-location.model.ts index 07ff8f89ce..e8ff2e7583 100644 --- a/sources/packages/backend/libs/sims-db/src/entities/institution-location.model.ts +++ b/sources/packages/backend/libs/sims-db/src/entities/institution-location.model.ts @@ -43,8 +43,9 @@ export class InstitutionLocation extends RecordDataModel { @Column({ name: "institution_code", + nullable: true, }) - institutionCode: string; + institutionCode?: string; @Column({ name: "primary_contact", diff --git a/sources/packages/forms/package-lock.json b/sources/packages/forms/package-lock.json index 490da0b6dd..4e843e6928 100644 --- a/sources/packages/forms/package-lock.json +++ b/sources/packages/forms/package-lock.json @@ -450,6 +450,7 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -776,7 +777,8 @@ "version": "5.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "dev": true + "dev": true, + "peer": true }, "v8-compile-cache-lib": { "version": "3.0.1", diff --git a/sources/packages/forms/src/form-definitions/institutionlocation.json b/sources/packages/forms/src/form-definitions/institutionlocation.json index 4ce5edc04c..44271c8002 100644 --- a/sources/packages/forms/src/form-definitions/institutionlocation.json +++ b/sources/packages/forms/src/form-definitions/institutionlocation.json @@ -73,15 +73,40 @@ }, { "label": "Institution location code", - "tooltip": "Unique 4 digit alpha code assigned the first time an institution is designated in Canada. If your institution has not yet been designated, input a random 4-letter code as a temporary Institution Location Code, and contact SABC to revise this code once your permanent code has been received. This code must be a valid federal education institution code prior to students submitting applications", "tableView": true, "case": "uppercase", "validate": { - "required": true, "minLength": 4, "maxLength": 4, - "pattern": "[A-Z]*" + "pattern": "[A-Z]*", + "custom": "valid = (!data.noInstitutionCode && !input) ? 'Institution location code is required.' : true;" }, + "logic": [ + { + "name": "Disable and clear when no institution location code", + "trigger": { + "type": "javascript", + "javascript": "result = data.noInstitutionCode === true" + }, + "actions": [ + { + "name": "Disable field", + "type": "property", + "property": { + "label": "Disabled", + "value": "disabled", + "type": "boolean" + }, + "state": true + }, + { + "name": "Clear value", + "type": "value", + "value": "value = '';" + } + ] + } + ], "key": "institutionCode", "attributes": { "data-cy": "institutionCode" @@ -89,6 +114,36 @@ "type": "textfield", "input": true }, + { + "label": "I do not have an institution location code.", + "tableView": false, + "defaultValue": false, + "key": "noInstitutionCode", + "attributes": { + "data-cy": "noInstitutionCode" + }, + "type": "checkbox", + "input": true + }, + { + "label": "Missing institution location code", + "tag": "p", + "className": "alert alert-warning fa fa-exclamation-triangle w-100", + "attrs": [ + { + "attr": "", + "value": "" + } + ], + "content": "Missing institution location code
Please note: You will be able to create this location without an institution location code. However, you will not be able to designate this location until you obtain a federally issued code from StudentAid BC. Once you submit your location please email designat@gov.bc.ca and request they provide you with the code and update the location information.", + "refreshOnChange": true, + "hidden": true, + "key": "noInstitutionCodeBanner", + "customConditional": "show = data.noInstitutionCode === true;", + "type": "htmlelement", + "input": false, + "tableView": false + }, { "label": "Address line 1", "tableView": false, diff --git a/sources/packages/web/src/components/aest/institution/modals/ApproveDenyDesignationModal.vue b/sources/packages/web/src/components/aest/institution/modals/ApproveDenyDesignationModal.vue index e2902bf7b6..f4d980bc74 100644 --- a/sources/packages/web/src/components/aest/institution/modals/ApproveDenyDesignationModal.vue +++ b/sources/packages/web/src/components/aest/institution/modals/ApproveDenyDesignationModal.vue @@ -1,6 +1,6 @@ + :primary-label="submitLabel" + @primary-click="submit" + @secondary-click="cancel" + :disable-primary-button="notAllowed" + /> + + @@ -70,16 +93,14 @@