From f7e819d6a34eb6db06d54d4695efa33a0dbad698 Mon Sep 17 00:00:00 2001
From: Dheepak Ramanathan <54600590+dheepak-aot@users.noreply.github.com>
Date: Tue, 27 Jun 2023 17:04:07 -0600
Subject: [PATCH] #2033 - Validating FT and PT offering minimum length (#2041)
Validating FT and PT offering minimum length
---
...s-valid-offering-period-for-funded-days.ts | 31 ++++--
...tion-program-offering-validation.models.ts | 18 +++-
.../custom-validators/period-min-length.ts | 28 ++++--
.../system-configurations-constants.ts | 10 +-
.../educationprogramoffering.json | 99 +++++++++++++++----
5 files changed, 148 insertions(+), 38 deletions(-)
diff --git a/sources/packages/backend/apps/api/src/services/education-program-offering/custom-validators/has-valid-offering-period-for-funded-days.ts b/sources/packages/backend/apps/api/src/services/education-program-offering/custom-validators/has-valid-offering-period-for-funded-days.ts
index 70c530675d..0e1df4d363 100644
--- a/sources/packages/backend/apps/api/src/services/education-program-offering/custom-validators/has-valid-offering-period-for-funded-days.ts
+++ b/sources/packages/backend/apps/api/src/services/education-program-offering/custom-validators/has-valid-offering-period-for-funded-days.ts
@@ -5,10 +5,7 @@ import {
ValidationOptions,
ValidationArguments,
} from "class-validator";
-import {
- OFFERING_STUDY_PERIOD_MAX_DAYS,
- OFFERING_STUDY_PERIOD_MIN_DAYS,
-} from "../../../utilities";
+import { OFFERING_STUDY_PERIOD_MAX_DAYS } from "../../../utilities";
import { StudyBreak } from "../education-program-offering-validation.models";
import { OfferingCalculationValidationBaseConstraint } from "./offering-calculation-validation-base-constraint";
@@ -23,20 +20,32 @@ class HasValidOfferingPeriodForFundedDaysConstraint
implements ValidatorConstraintInterface
{
validate(studyBreaks: StudyBreak[], args: ValidationArguments): boolean {
+ const offeringMinDaysAllowedValue = this.getOfferingMinAllowedDays(args);
const calculatedStudyBreaksAndWeeks = this.getCalculatedStudyBreaks(
studyBreaks,
args,
);
return (
calculatedStudyBreaksAndWeeks.fundedStudyPeriodDays >=
- OFFERING_STUDY_PERIOD_MIN_DAYS &&
+ offeringMinDaysAllowedValue &&
calculatedStudyBreaksAndWeeks.fundedStudyPeriodDays <=
OFFERING_STUDY_PERIOD_MAX_DAYS
);
}
- defaultMessage() {
- return `The funded study amount of days is ineligible for StudentAid BC funding. Your dates must be between ${OFFERING_STUDY_PERIOD_MIN_DAYS} to ${OFFERING_STUDY_PERIOD_MAX_DAYS} days.`;
+ defaultMessage(args: ValidationArguments) {
+ const offeringMinDaysAllowedValue = this.getOfferingMinAllowedDays(args);
+ return `The funded study amount of days is ineligible for StudentAid BC funding. Your dates must be between ${offeringMinDaysAllowedValue} to ${OFFERING_STUDY_PERIOD_MAX_DAYS} days.`;
+ }
+
+ /**
+ * Get offering minimum allowed days from args.
+ * @param args validation arguments.
+ * @returns minimum allowed days.
+ */
+ private getOfferingMinAllowedDays(args: ValidationArguments): number {
+ const [, , offeringMinDaysAllowed] = args.constraints;
+ return offeringMinDaysAllowed(args.object) as number;
}
}
@@ -46,12 +55,14 @@ class HasValidOfferingPeriodForFundedDaysConstraint
* allowed study period amount of days.
* @param startPeriodProperty property of the model that identifies the offering start date.
* @param endPeriodProperty property of the model that identifies the offering end date.
+ * @param offeringMinDaysAllowed study period minimum length in number of days.
* @param validationOptions validations options.
* @returns true if the study period is valid, otherwise, false.
*/
export function HasValidOfferingPeriodForFundedDays(
startPeriodProperty: (targetObject: unknown) => Date | string,
endPeriodProperty: (targetObject: unknown) => Date | string,
+ offeringMinDaysAllowed: (targetObject: unknown) => number,
validationOptions?: ValidationOptions,
) {
return (object: unknown, propertyName: string) => {
@@ -60,7 +71,11 @@ export function HasValidOfferingPeriodForFundedDays(
target: object.constructor,
propertyName,
options: validationOptions,
- constraints: [startPeriodProperty, endPeriodProperty],
+ constraints: [
+ startPeriodProperty,
+ endPeriodProperty,
+ offeringMinDaysAllowed,
+ ],
validator: HasValidOfferingPeriodForFundedDaysConstraint,
});
};
diff --git a/sources/packages/backend/apps/api/src/services/education-program-offering/education-program-offering-validation.models.ts b/sources/packages/backend/apps/api/src/services/education-program-offering/education-program-offering-validation.models.ts
index f56071bb5a..9465175cfd 100644
--- a/sources/packages/backend/apps/api/src/services/education-program-offering/education-program-offering-validation.models.ts
+++ b/sources/packages/backend/apps/api/src/services/education-program-offering/education-program-offering-validation.models.ts
@@ -46,7 +46,8 @@ import {
OFFERING_STUDY_BREAK_MAX_DAYS,
OFFERING_STUDY_BREAK_MIN_DAYS,
OFFERING_STUDY_PERIOD_MAX_DAYS,
- OFFERING_STUDY_PERIOD_MIN_DAYS,
+ OFFERING_STUDY_PERIOD_MIN_DAYS_FULL_TIME,
+ OFFERING_STUDY_PERIOD_MIN_DAYS_PART_TIME,
OFFERING_YEAR_OF_STUDY_MAX_VALUE,
OFFERING_YEAR_OF_STUDY_MIN_VALUE,
} from "../../utilities";
@@ -319,6 +320,18 @@ const studyStartDateProperty = (offering: OfferingValidationModel) =>
const studyEndDateProperty = (offering: OfferingValidationModel) =>
offering.studyEndDate;
+/**
+ * Get study period minimum length based on offering intensity.
+ * @param offering offering.
+ * @returns minimum study period length in number of days.
+ */
+const studyPeriodMinLength = (offering: OfferingValidationModel) => {
+ if (offering.offeringIntensity === OfferingIntensity.fullTime) {
+ return OFFERING_STUDY_PERIOD_MIN_DAYS_FULL_TIME;
+ }
+ return OFFERING_STUDY_PERIOD_MIN_DAYS_PART_TIME;
+};
+
/**
* Complete offering data with all validations needed to ensure data
* consistency. Program data and locations data need to be present to
@@ -353,7 +366,7 @@ export class OfferingValidationModel {
@IsDateAfter(studyStartDateProperty, userFriendlyNames.studyEndDate)
@PeriodMinLength(
studyStartDateProperty,
- OFFERING_STUDY_PERIOD_MIN_DAYS,
+ studyPeriodMinLength,
userFriendlyNames.studyEndDate,
{
context: ValidationContext.CreateWarning(
@@ -595,6 +608,7 @@ export class OfferingValidationModel {
@HasValidOfferingPeriodForFundedDays(
studyStartDateProperty,
studyEndDateProperty,
+ studyPeriodMinLength,
{
context: ValidationContext.CreateWarning(
OfferingValidationWarnings.InvalidStudyDatesPeriodLength,
diff --git a/sources/packages/backend/apps/api/src/utilities/class-validation/custom-validators/period-min-length.ts b/sources/packages/backend/apps/api/src/utilities/class-validation/custom-validators/period-min-length.ts
index ef217b02c9..a2c683284a 100644
--- a/sources/packages/backend/apps/api/src/utilities/class-validation/custom-validators/period-min-length.ts
+++ b/sources/packages/backend/apps/api/src/utilities/class-validation/custom-validators/period-min-length.ts
@@ -15,23 +15,38 @@ import { dateDifference, getDateOnlyFormat } from "@sims/utilities";
@ValidatorConstraint()
class PeriodMinLengthConstraint implements ValidatorConstraintInterface {
validate(value: Date | string, args: ValidationArguments): boolean {
- const [startDateProperty, minDaysAllowed] = args.constraints;
+ const [startDateProperty] = args.constraints;
+ const minDaysAllowedValue = this.getMinAllowedDays(args);
const periodStartDate = startDateProperty(args.object);
if (!periodStartDate) {
// The related property does not exists in the provided object to be compared.
return false;
}
- return dateDifference(value, periodStartDate) >= minDaysAllowed;
+ return dateDifference(value, periodStartDate) >= minDaysAllowedValue;
}
defaultMessage(args: ValidationArguments) {
- const [startDateProperty, minDaysAllowed, propertyDisplayName] =
- args.constraints;
+ const [startDateProperty, , propertyDisplayName] = args.constraints;
const startDate = getDateOnlyFormat(startDateProperty(args.object));
const endDate = getDateOnlyFormat(args.value);
+ const minDaysAllowedValue = this.getMinAllowedDays(args);
return `${
propertyDisplayName ?? args.property
- }, the number of day(s) between ${startDate} and ${endDate} must be at least ${minDaysAllowed}.`;
+ }, the number of day(s) between ${startDate} and ${endDate} must be at least ${minDaysAllowedValue}.`;
+ }
+
+ /**
+ * Get minimum allowed days from args.
+ * @param args validation arguments.
+ * @returns minimum allowed days.
+ */
+ private getMinAllowedDays(args: ValidationArguments): number {
+ const [, minDaysAllowed] = args.constraints;
+ const minDaysAllowedValue =
+ minDaysAllowed instanceof Function
+ ? minDaysAllowed(args.object)
+ : minDaysAllowed;
+ return minDaysAllowedValue as number;
}
}
@@ -42,6 +57,7 @@ class PeriodMinLengthConstraint implements ValidatorConstraintInterface {
* @param startDateProperty indicates the property that define the
* start of a period.
* @param minDaysAllowed min allowed days to the period be considered valid.
+ * This could be a number or a function that returns a value of minimum allowed days.
* @param propertyDisplayName user-friendly property name to be added to the
* validation message.
* @param validationOptions validations options.
@@ -49,7 +65,7 @@ class PeriodMinLengthConstraint implements ValidatorConstraintInterface {
*/
export function PeriodMinLength(
startDateProperty: (targetObject: unknown) => Date | string,
- minDaysAllowed: number,
+ minDaysAllowed: ((targetObject: unknown) => number) | number,
propertyDisplayName?: string,
validationOptions?: ValidationOptions,
) {
diff --git a/sources/packages/backend/apps/api/src/utilities/system-configurations-constants.ts b/sources/packages/backend/apps/api/src/utilities/system-configurations-constants.ts
index c335e60576..1456b8a28d 100644
--- a/sources/packages/backend/apps/api/src/utilities/system-configurations-constants.ts
+++ b/sources/packages/backend/apps/api/src/utilities/system-configurations-constants.ts
@@ -60,9 +60,15 @@ export const OFFERING_COURSE_LOAD_MIN_VALUE = 20;
*/
export const OFFERING_COURSE_LOAD_MAX_VALUE = 59;
/**
- * Minimum amount of days to an offering study period.
+ * Minimum amount of days required for a full time offering study period.
*/
-export const OFFERING_STUDY_PERIOD_MIN_DAYS = 42;
+export const OFFERING_STUDY_PERIOD_MIN_DAYS_FULL_TIME = 84;
+
+/**
+ * Minimum amount of days required for a part time offering study period.
+ */
+export const OFFERING_STUDY_PERIOD_MIN_DAYS_PART_TIME = 42;
+
/**
* Maximum amount of days to an offering study period.
*/
diff --git a/sources/packages/forms/src/form-definitions/educationprogramoffering.json b/sources/packages/forms/src/form-definitions/educationprogramoffering.json
index 73ea35b9dd..dd27c392ed 100644
--- a/sources/packages/forms/src/form-definitions/educationprogramoffering.json
+++ b/sources/packages/forms/src/form-definitions/educationprogramoffering.json
@@ -1,9 +1,9 @@
{
"title": "Education Program Offering",
- "display": "form",
- "type": "form",
"name": "educationprogramoffering",
"path": "educationprogramoffering",
+ "type": "form",
+ "display": "form",
"tags": [
"common"
],
@@ -170,7 +170,8 @@
"showWordCount": false,
"allowMultipleMasks": false,
"addons": [],
- "id": "ec8hh1"
+ "id": "ec8hh1",
+ "isNew": false
},
{
"label": "HTML",
@@ -836,7 +837,8 @@
"inputType": "radio",
"fieldSet": false,
"id": "epwmfzr",
- "defaultValue": ""
+ "defaultValue": "",
+ "lockKey": true
},
{
"label": "Study period intensity popup",
@@ -1022,7 +1024,8 @@
"allowMultipleMasks": false,
"addons": [],
"id": "emuvfb",
- "defaultValue": ""
+ "defaultValue": "",
+ "inputType": "number"
}
],
"placeholder": "",
@@ -2085,7 +2088,7 @@
"value": ""
}
],
- "content": "This study period is ineligible for StudentAid BC funding\n
\nYour dates must be between 42 to 365 days.",
+ "content": "This study period is ineligible for StudentAid BC funding\n
\nYour dates must be between {{data.studyPeriodMinDays}} to {{data.studyPeriodMaxDays}} days.",
"refreshOnChange": true,
"customClass": "banner-warning ",
"hidden": false,
@@ -2094,7 +2097,7 @@
"tags": [],
"properties": {},
"conditional": {
- "show": null,
+ "show": "",
"when": null,
"eq": "",
"json": ""
@@ -2153,7 +2156,8 @@
"showWordCount": false,
"allowMultipleMasks": false,
"addons": [],
- "id": "ebhbfou"
+ "id": "ebhbfou",
+ "isNew": false
}
],
"placeholder": "",
@@ -2375,7 +2379,8 @@
"allowMultipleMasks": false,
"addons": [],
"tag": "p",
- "id": "eity25i"
+ "id": "eity25i",
+ "className": ""
},
{
"label": "No study breaks",
@@ -2650,7 +2655,8 @@
"addons": [],
"inputType": "text",
"id": "eufdf4l0000000",
- "defaultValue": ""
+ "defaultValue": "",
+ "inDataGrid": true
},
{
"label": "Break end date",
@@ -2760,7 +2766,8 @@
"addons": [],
"inputType": "text",
"id": "endqveo000000",
- "defaultValue": ""
+ "defaultValue": "",
+ "inDataGrid": true
},
{
"label": "Total break days",
@@ -2837,7 +2844,9 @@
"showWordCount": false,
"properties": {},
"allowMultipleMasks": false,
- "addons": []
+ "addons": [],
+ "inDataGrid": true,
+ "inputType": "number"
},
{
"label": "Total eligible break days",
@@ -2913,7 +2922,9 @@
"showWordCount": false,
"properties": {},
"allowMultipleMasks": false,
- "addons": []
+ "addons": [],
+ "inDataGrid": true,
+ "inputType": "number"
},
{
"label": "Total ineligible break days",
@@ -2989,7 +3000,9 @@
"showWordCount": false,
"properties": {},
"allowMultipleMasks": false,
- "addons": []
+ "addons": [],
+ "inDataGrid": true,
+ "inputType": "number"
}
],
"placeholder": "",
@@ -4169,7 +4182,8 @@
"allowMultipleMasks": false,
"addons": [],
"tag": "p",
- "id": "e6zws5q"
+ "id": "e6zws5q",
+ "className": ""
},
{
"collapsible": false,
@@ -4264,7 +4278,8 @@
"properties": {},
"allowMultipleMasks": false,
"addons": [],
- "id": "ey5wqhj"
+ "id": "ey5wqhj",
+ "inputType": "number"
}
],
"width": 6,
@@ -4355,7 +4370,8 @@
"properties": {},
"allowMultipleMasks": false,
"addons": [],
- "id": "ezezla"
+ "id": "ezezla",
+ "inputType": "number"
}
],
"width": 6,
@@ -4520,7 +4536,8 @@
"properties": {},
"allowMultipleMasks": false,
"addons": [],
- "id": "eont0uf"
+ "id": "eont0uf",
+ "inputType": "number"
}
],
"width": 6,
@@ -4611,7 +4628,8 @@
"properties": {},
"allowMultipleMasks": false,
"addons": [],
- "id": "esc41a"
+ "id": "esc41a",
+ "inputType": "number"
}
],
"width": 6,
@@ -5184,7 +5202,8 @@
"allowMultipleMasks": false,
"addons": [],
"tag": "p",
- "id": "e7678z"
+ "id": "e7678z",
+ "className": ""
},
{
"label": "I confirm this study period offering meets the policies outlined in the StudentAid BC policy manual.",
@@ -5725,6 +5744,46 @@
"addons": [],
"inputType": "hidden",
"id": "ejidpjh"
+ },
+ {
+ "input": true,
+ "tableView": true,
+ "key": "studyPeriodMinDays",
+ "label": "Study period minimum days",
+ "protected": false,
+ "unique": false,
+ "persistent": true,
+ "type": "hidden",
+ "tags": [],
+ "conditional": {
+ "show": "",
+ "when": null,
+ "eq": ""
+ },
+ "properties": {},
+ "lockKey": true,
+ "customDefaultValue": "",
+ "calculateValue": "value = data.offeringIntensity === 'Full Time' ? 84 : 42;"
+ },
+ {
+ "input": true,
+ "tableView": true,
+ "key": "studyPeriodMaxDays",
+ "label": "Study period maximum days",
+ "protected": false,
+ "unique": false,
+ "persistent": true,
+ "type": "hidden",
+ "tags": [],
+ "conditional": {
+ "show": "",
+ "when": null,
+ "eq": ""
+ },
+ "properties": {},
+ "defaultValue": "365",
+ "lockKey": true,
+ "isNew": false
}
]
}
\ No newline at end of file