Program and enrolment fields
What goes in each box, what PTRS actually checks, and where the create form and the edit form disagree.
PTRS runs no check in the browser before a save is sent. The check on PTRS's own side, run after the save leaves the screen, is the only rule there is, so a mistake is only discovered after pressing the button. Where no check exists at all, the size of the underlying storage becomes the only limit, and going over it produces a plain server error rather than a message naming the field.
Ten save actions have no check at all
Programs core has 23 save actions in total. Eleven have no check on them whatsoever, and ten of those eleven accept something a person can type in.
| Action | What is unchecked | Only limit |
|---|---|---|
| Replace eligibility rules | The rule's value and its error message | 500 characters each |
| Create a consent form | Title and form content | 250 characters; none at all |
| Correct a consent form | Title and form content | 250 characters; none at all |
| Record a consent | The signature and the expiry date | None; must be a valid date |
| Create a cohort | Name, description, max size | 200; 1,000; none |
| Add a child to a cohort | Everything | Nothing to check |
| Remove a child from a cohort | Everything | Nothing to check |
| Promote from the waitlist | The count to promote | None |
| Bulk Enroll a group | The list of children, and a note | None; 2,000 characters |
| Run the eligibility check | The child's identifier | Nothing to check |
| Snapshot a program | Nothing typed in, just a route | Not applicable |
Every one of these sits in the enrolment and consent half of this module. The scheduling half is the opposite: its five checks match their storage limits exactly. See Where the rules match the storage.
Creating a program
The only screen that creates one is the header's Quick Add → Program dialog.
| Field | On the create form? | PTRS's rule | Limit | If it fails |
|---|---|---|---|---|
| Organisation | No, taken from your sign-in | Required | A location from another organisation is refused as not found, not as a permission failure | |
| Location | Yes, a required choice | Required | Leaving it unset sends a blank value, refused before any other rule runs, with no field named | |
| Name | Yes, required | Required | 200 | A message naming the field |
| Description | Yes | 2,000 | A message naming the field | |
| Program Type | Yes, a required choice | Required, and checked against PTRS's own list | An unrecognised value: a message naming it | |
| Season | Yes, a required choice | Same | Same | |
| Delivery Mode | Yes, a required choice | Same | Same | |
| Age Group | Yes, a required choice | Same | Same | |
| Capacity | Yes, required, defaults to 25 | Must be greater than zero | A message naming the field | |
| Waitlist Enabled | No, always starts off | Every program starts with the waitlist off | ||
| Start Date | Yes, defaults to today | Required, must be a valid date | A message naming the field | |
| End Date | Yes, defaults to today plus three months | Required, valid, and strictly after the start date | A message. A single-day program cannot be created here | |
| Age Min, Age Max | No, derived from the age group | Must not be negative; the maximum must not be below the minimum | ||
| Weekly schedule | No, always starts blank | No rule | See below | |
| Pillars | No | No rule | An unrecognised pillar is a plain server error, not a message | |
| Tags | No | Each required, non-blank | 100 each, unique per program | A message naming the field |
The schedule's own free-text field has no size limit on its own record and no check when it is created, and creating a program copies whatever text was passed into a second box that only holds 500 characters. A value longer than that fails as a plain server error rather than a message, when the program is actually saved.
The Quick Add dialog always sends a blank value here, so nobody using the product today can trigger this. It only affects someone integrating with PTRS directly.
Waitlist Enabled, Age Min/Age Max, and the weekly schedule are not on the create dialog at all. The waitlist setting is the one that matters most: it starts off, and until somebody opens Edit and turns it on, a full program refuses every enrolment instead of placing children on a waitlist.
The age range is worked out from the age group you chose, inside the dialog itself, and the Mixed group maps to 5 to 18, a figure that appears in that one place and nowhere else in the product.
Editing a program
Only the Edit dialog on the program header calls this.
| Difference from creating one | What happens |
|---|---|
| Program Type, Season, Delivery Mode and Age Group can each be left blank | A blank or missing value leaves the stored value unchanged |
| An unrecognised value on any of those four | Silently ignored. PTRS keeps the old value and reports it back as if nothing was wrong, unlike creating a program, which refuses the same input |
| Organisation and Location are not part of this form | A program cannot be moved between sites |
| Pillars or Tags left out entirely | Left unchanged. Sent as an empty list, they are cleared instead |
This is why the create and edit dialogs behave differently on the same four choices, even though both only ever offer PTRS's own recognised values, so nobody clicking through the screen can actually trigger the difference. It only matters to someone sending a request directly.
The four classification choices
All four dropdowns on both dialogs offer exactly what PTRS will accept, with nothing extra and nothing missing. This is the first module read in this portal where that is true of every single dropdown.
| Field | Choices |
|---|---|
| Program Type | Workshop, Course, Project, Ongoing Club, Event Series, Camp |
| Season | School Year, Summer, Year Round, Holiday, Weekend |
| Delivery Mode | In Person, Virtual, Hybrid, Field Based |
| Age Group | Juniors, Tweens, Teens, Senior Teens, Mixed |
The age bands attached to these groups, and the fact that nothing enforces them, are covered on the module landing page.
Program stage
Moving a program's stage takes the new stage and an optional reason, up to 1,000 characters. The move must appear in the table below, or PTRS refuses it with a message naming both stages.
| From | May move to |
|---|---|
| Draft | In Review |
| In Review | Approved, or back to Draft |
| Approved | Active |
| Active | Suspended, or Completed |
| Suspended | Active |
| Completed | Archived |
| Archived | Nowhere |
PTRS's screen holds an identical copy of this table, so the buttons offered always match what would actually be accepted.
Three consequences a reader should keep in mind.
A new program takes three moves to go live: Draft, then In Review, then Approved, then Active.
A suspended program cannot jump straight to Completed or Archived. It must be reactivated first.
Archived is the end, and there is no way to delete a program. A program created by mistake can only be walked all the way to Archived.
The program list's stage filter offers five of the seven stages: All, Draft, Active, Completed and Archived. In Review, Approved and Suspended cannot be filtered for directly, and only appear under All.
Schedule and session fields
Not reachable from any screen today; see the module landing page. Documented here for someone integrating with PTRS directly.
Saving a schedule takes every field as optional; anything left out keeps its old value.
| Field | PTRS's rule | Limit | Note |
|---|---|---|---|
| Free-text schedule label | 500 | 500 | Starts as "Default" when the program is created |
| Recurrence pattern | 500 | 500 | Saved and never actually read by the part of PTRS that would generate sessions |
| Start time, end time | None | A badly formed value is a plain server error | |
| Days of the week | 100 | 100 | This is the one field session generation actually reads. An unrecognised day is dropped; leaving this blank, or leaving only unrecognised values, falls back to Monday through Friday |
| Effective start date, effective end date | None | A badly formed value is a plain server error | |
| Default room | None | Checked to exist, refused as not found otherwise. Not checked to be at the program's own site | |
| Capacity | Must be greater than zero, if given | Saved and read by no decision anywhere |
Generating sessions takes a start and end date; both must be valid dates, correctly refused with a message otherwise, and the end must come after the start. It also requires a saved recurrence pattern and both times, or PTRS refuses it with a message naming what is missing.
Updating a session's stage must be Scheduled, Cancelled or Completed, or PTRS refuses it by name; a cancellation reason is capped at 1,000 characters; times are checked for validity; a room is checked to exist.
Recording a schedule exception requires its type to be Cancelled, Modified or Added; its reason is capped at 1,000 characters; and only one exception is allowed per date per schedule.
Recording a schedule exception, and updating a schedule or a session, all read a date or time directly rather than through PTRS's usual checking step, so a badly formed value on any of those four crashes into a plain server error instead of a clear message.
Generating sessions, reading sessions, reading room bookings, checking for a conflict, and booking a room all read the same kind of value the safer way, and correctly return a clear message on a bad one. The five behave inconsistently with each other for no reason tied to what they do.
Facility booking fields
Nothing in the interface reaches this action; the fields below are for someone integrating directly.
| Field | PTRS's rule | Note |
|---|---|---|
| Room | Required, checked to exist | Refused as not found otherwise |
| Location | Required | Not checked to exist, and not checked to be the room's actual location. A booking can be saved against a site the room is not even in |
| Organisation | Required | Taken from your sign-in, not from what is sent |
| Booking date | Must be a valid date | |
| Start time, end time | Must be valid; end after start | |
| Purpose | 500 | Matches the underlying storage exactly |
| The session or schedule it relates to | None | Not checked to exist |
The booking's status always starts Tentative. Nothing in PTRS ever sets it to Confirmed or Cancelled, and there is no way to update or remove a booking at all.
An overlapping booking in the same room on the same date is correctly refused.
Enrolment fields
Enrolling one child takes a single value, the program's own identifier. See What happens when a child is enrolled for everything else about an enrolment, which PTRS derives rather than takes from what is sent.
Bulk Enroll takes a list of children and an optional note. There is no check on either, so the list has no size limit, and the note has no length rule against its 2,000-character storage.
Promoting from the waitlist takes a count. There is no check on it, so it has no lower or upper bound: a count larger than the whole waitlist promotes everyone on it, and a negative count is a plain server error.
What is stored on an enrolment
| Field | Set by | Read by |
|---|---|---|
| Child, schedule, enrolment date | Either enrolment path | Everywhere |
| Status | Either enrolment path, plus withdrawing and promoting | Everywhere |
| Waitlist position | Bulk Enroll only | The order used to promote, and the child's own Programs tab |
| Notes | Bulk Enroll only | An internal, richer view of the enrolment |
| Cohort | Adding to or removing from a cohort, neither of which anything reaches | The child's own Programs tab |
| Consent status | Nothing, ever | The child's own Programs tab, promotion, and that same richer internal view |
| Eligibility check result | Nothing | Nothing |
Eligibility rule fields
Replacing a program's eligibility rules replaces every rule at once. There is no check on this action at all.
| Field | PTRS's behaviour | Limit |
|---|---|---|
| Rule type | Checked against a six-choice list; refused by name if unrecognised | 50 |
| Comparison | Checked against an eight-choice list; refused by name if unrecognised | 50 |
| Value | Stored as typed, no rule | 500, over which is a plain server error |
| Required | Stored | |
| Error message | Stored as typed, no rule | 500, over which is a plain server error |
The rule builder offers all six rule types and all eight comparisons, in every combination. The engine that would actually run them supports far fewer, and an unsupported combination is reported as passed, not flagged as an error.
| Rule type | Comparisons actually implemented | Comparisons that are not |
|---|---|---|
| Age | Equal to, greater than, less than, at least, at most, between | Not equal to, contains: reported as passed |
| Grade | Equal to, contains | The other six: reported as passed |
| Gender | Equal to | The other seven: reported as passed |
| Membership, attendance history, custom | None | All eight: always passed |
So of the 48 combinations the builder can produce, nine do something, and the other 39 are reported as satisfied regardless of what they say. A required rule reading "not male" would, if it ever ran, let every child through.
Two further quirks in that engine, both dormant because nothing ever runs it:
- A between comparison expects two numbers separated by a dash or a comma. Anything else is reported as failed rather than flagged as an error.
- A contains comparison on grade tests the wrong direction for a single grade: it asks whether the rule's own value contains the child's grade, so a rule reading "10, 11, 12" would match a child in grade 1.
- An age rule whose value is not a number crashes into a plain server error rather than a clear message.
Consent fields
Creating a consent form, and correcting one, both have no check at all.
| Field | PTRS's behaviour | Limit |
|---|---|---|
| Title | Stored as typed, no rule | 250, over which is a plain server error |
| Form content | Stored as typed, no rule | No limit at all |
| Required, Active | Stored |
Correcting a form increases its version number on every save, and a recorded consent points at the form itself, not at a specific version. A form edited after a guardian has already signed leaves no record of the wording that guardian actually agreed to.
Recording a consent has no check at all either.
| Field | PTRS's behaviour |
|---|---|
| Consent form | Must exist and belong to the program named, refused as not found otherwise |
| Child | Must exist, refused as not found otherwise |
| Guardian | Must exist, refused as not found otherwise. The screen sends a blank value, so this is refused before PTRS even gets this far |
| Consent given | Stored |
| Signature | Stored, no rule, no limit at all |
| Expiry date | Must be a valid date, not checked before use; a badly formed value crashes into a plain server error |
Only one record is allowed per form, child and guardian combination; a second attempt is refused as a duplicate. There is no way to correct or withdraw a consent once recorded.
The consent date is always today, stamped by PTRS itself, and is not something a person can type in.
Cohort fields
Creating a cohort has no check at all.
| Field | PTRS's behaviour | Limit |
|---|---|---|
| Name | Stored as typed, no rule | 200, over which is a plain server error |
| Description | Stored as typed, no rule | 1,000, over which is a plain server error |
| Max Size | Stored, no rule | May be left blank. Zero or a negative number is accepted, and then refuses every attempt to add a child |
Adding a child to a cohort checks that the child is not already a member, refused as a duplicate otherwise, and that Max Size has not been reached, refused as full otherwise. It does not check that the enrolment belongs to the cohort's own program. Nothing in the interface reaches it.
Where the rules match the storage
Worth recording, because eight modules read before this one found the opposite. Every length rule declared in this subset matches its underlying storage exactly: thirteen rules across seven checks, with no mismatch in either direction.
| Rule | Storage |
|---|---|
| Program name | 200, matches |
| Program description | 2,000, matches |
| A tag | 100, matches |
| Schedule label | 500, matches |
| Recurrence pattern | 500, matches |
| Days of the week | 100, matches |
| Session cancellation reason | 1,000, matches |
| Schedule exception reason | 1,000, matches |
| Booking purpose | 500, matches |
| Program-stage change reason | 1,000, matches |
| A clone's new name | 200, matches the program name field |
The gap in this module is not in the limits that exist. It is that the entire enrolment and consent half has no checks at all.
Delete behaviour
There is no delete action for a program, a schedule, an enrolment, a booking, a consent form or a consent record anywhere in this subset. The rules below describe what would happen if one were ever added, or if a row were removed directly against the database.
| Relationship | On delete |
|---|---|
| A program's site | Blocked. A site cannot be deleted while a program still points at it |
| A program's original, if it was cloned | Cleared, not blocked |
| An enrolment's child | Blocked |
| An enrolment's schedule | Cascades. Deleting a schedule deletes its enrolments |
| An enrolment's cohort | Cleared, not blocked |
| A consent form's program | Cascades |
| A consent record's form | Cascades |
| A consent record's child or guardian | Blocked |
| A cohort's members | Removable directly, through the action nothing in the interface reaches |
The block between a program and its site is worth noting against Centers, where deleting a site is otherwise unrestricted. Any site with a program on it would now fail that delete outright.
Where to go next
Checked against PTRS on 7 September 2026.