# Business rules — FootPrintLive

Evidence date: 2026-10-14. Source: `master` at `e54fddb`.

Rules below are taken from code or from `changelog.txt`. Each rule is marked **Documented** or **Inferred**. A rule that the code enforces without a written policy is **Inferred** even when the behaviour is certain.

No rule was added because it "should" be true for a rugby union.

## Identity and access

**BR-001 — Session required for the back office.**  
A request to the default index with no Zend_Auth identity redirects to `/login/`.  
Provenance: **Inferred** from `IndexController::init` (implemented behaviour, no policy document).

**BR-002 — Username match is the credential identity.**  
The auth adapter reads `UserLogin_view.Username` and `UserLogin_view.Password`.  
Provenance: **Inferred**.

**BR-003 — Stored passwords are MD5.**  
Reset writes `md5` of the submitted password into `Password`. Login compares that column as the credential, so the client must submit the same form the adapter expects (the current `LoginNow` path passes through the password value it loaded from the profile row after a lookup).  
Provenance: **Inferred**. The lookup helpers `UserLogig_Check_Terms` and `AdminLogig_Check_Terms` were not opened; how they hash the typed password is **Unknown / Requires Confirmation** if it differs from the reset writer.

**BR-004 — One hardcoded password acts as an administrator override.**  
If the submitted password equals that string, login loads the account by email and authenticates with the stored password. The same comparison exists on the API login action. The string is not recorded in this knowledge base.  
Provenance: **Inferred** (implemented). It is not described as an approved support procedure anywhere in the repo.

**BR-005 — Terms must already be accepted, or acceptance is stored at login.**  
`checktermsAction` logs the user in only when `tandc` is 1. Otherwise it returns OK 1 without a session. `loginuserAction` calls `LoginNow` with the flag that sets `tandc` to 1.  
Provenance: **Inferred**.

**BR-006 — Incomplete profiles cannot stay on the dashboard.**  
`Status` 0 redirects to `/first`. Completing that form sets `Status` to 1.  
Provenance: **Inferred**.

**BR-007 — Inactive profiles do not get the menu.**  
`Active` 0 shows the red "Inactive" caption and skips the menu partial. `Active` 1 shows "Active" and renders the menu.  
Provenance: **Inferred**.

**BR-008 — Union types above 2 use the union workspace unless the user is an administrator.**  
`UnionType > 2` and `Administrator` not 1 redirects from the default home to `/union/` (or `/first` when status is 0). `/union/` then redirects to `/union/squads/`.  
Provenance: **Inferred**. The names of union type numbers are **Unknown**.

**BR-009 — National-style menus are limited to union type below 3.**  
Referee assessors and the system-settings block (leagues, cities, bonafide uploads, society leagues, national panels, duplicate management, add venue) render only when `UnionType < 3`. The online-user counter uses the same test.  
Provenance: **Inferred**.

**BR-010 — Menu sections follow the Role JSON.**  
A section is rendered only when its key exists. List and add links look for nested `Actions` keys.  
Provenance: **Inferred**. The JSON is not checked again inside every controller. Hiding a menu item is not proof the URL is forbidden.

**BR-011 — A fixed list of user ids may open FP4 Squad.**  
The menu link is rendered only when `UserID` is 1, 582, 989, 1007, 1014, 4156, or 13329.  
Provenance: **Inferred**. Why these accounts were chosen is **Unknown**.

**BR-012 — Only user id 1 sees Add Tourn in that menu.**  
Provenance: **Inferred**.

**BR-013 — Reset links are single use.**  
The reset page requires `ResetKey` to equal the URL key. On save the key is cleared. A mismatch shows the expired-link message.  
Provenance: **Inferred**.

**BR-014 — A reset email includes the new password.**  
`EmailContent` puts the plaintext password in the message and sends it from `support@footprintapp.net`.  
Provenance: **Inferred**.

**BR-015 — Session configuration lifetime is 10 days.**  
`resources.session.cookie_lifetime = 864000`.  
Provenance: **Inferred** from configuration. Whether the server's PHP session GC matches that value is **Unknown**.

## Dashboard

**BR-016 — Ethnicity tiles use fixed member types.**  
Players are `getCount(2)`, coaches `getCount(3)`, referees `getCount(8)`, medical `getCount(5)`.  
Provenance: **Inferred**. Labels in the view were not all opened; the controller variable names are the evidence for those four labels.

**BR-017 — Club dashboard versus default dashboard.**  
`UnionType > 3` renders script `index/clubs`. `UnionType == 11` renders `index/new` and replaces the clubs choice because it is checked later.  
Provenance: **Inferred**.

## Members and documents

**BR-018 — Member Excel columns, Release 1.**  
Order is #, Full Name, Organisation, E-mail, Job Title, Mobile, Gender, ID Number, Age, Ethnicity.  
**Documented** in `changelog.txt` (13/05/2019) and **Inferred** as the enforced column order from `generateExcel`.

**BR-019 — Boksmart column on some exports.**  
If the title argument is 3, 5, or 8, column K is Boksmart. If the title argument is 8, columns L and M are National Panel and League.  
Provenance: **Inferred**. Mapping those integers to job titles is **Requires Confirmation**.

**BR-020 — Competition squad document flags.**  
For each player the action scans `Model_Documents::getForPlayer`:

- Category 1 and file type 4 sets the profile-document flag.
- Category 1 and file type 1 sets the ID-document flag.
- Category 5 and file type 23 (comment: "Checklist Document") counts only when `DateCreated` falls in the current calendar year, and only the latest such file is kept.

The previous check (category 5, file type 16) is commented.  
**Documented** in `changelog.txt` (17/05/2019) and **Inferred** as the rule the method now applies.

**BR-021 — School name replaces organisation name for youth age groups.**  
If squad `AgeGroup` is greater than 0 and less than 14, the displayed union name becomes `PrimarySchool`. If `AgeGroup` is greater than 13 and less than 18, it becomes `SecondarySchool`. Otherwise the profile union name is kept.  
Provenance: **Inferred**.

**BR-022 — Injury flag on a competition squad.**  
If a `PlayerInjury` row has `ReturnToPlay` after today: flag 1 when `ReturnToTraining` is today or earlier, otherwise flag 2. No such row leaves the flag at 0.  
Provenance: **Inferred**.

**BR-023 — Started versus benched.**  
Inside the squad career JSON, position less than 16 increments Started. Position greater than 15 increments Benched.  
Provenance: **Inferred**. Rugby numbering (1–15 starting, 16+ replacements) is a reading of those thresholds, not a comment in the code. The sport interpretation is **Inferred**.

**BR-024 — Demographic buckets on that payload.**  
Counters exist for African, Coloured, Indian, White, and Undefined, incremented from `Profile["Ethnicity"]` when that field is non-empty (on the position-filter branch).  
Provenance: **Inferred**.

## Squad confirmation and captaincy

**BR-025 — A player is confirmed when their user id is a key in `ConfirmedPlayers`.**  
Provenance: **Inferred** from `competitionsquadAction`.

**BR-026 — Captain and vice flags default to 0 when the squad JSON omits them.**  
Provenance: **Inferred**.

## Accreditations and mail

**BR-027 — Queue processing marks every cue row done.**  
`schedual_test` sets application `Cue` to 2 and service `Status` to 2 for each queued row.  
Provenance: **Inferred**.

**BR-028 — Approval mail is skipped unless email is allowed and the accreditation id is above 26.**  
Mail runs only when `BlockEmails` is 0 and `AC > 26`. Other rows are still marked done.  
Provenance: **Inferred**. What `AC` 26 represents is **Unknown**.

**BR-029 — The pass is emailed only to a syntactically valid address.**  
`mypass` returns immediately when `ValidateEmail` fails.  
Provenance: **Inferred**. The validator body was not read.

**BR-030 — Sender depends on `NotSuru`.**  
`NotSuru` 1 sends via `sendMail_FP` from `accreditation@footprintapp.net`. Any other value uses `sendMail` from `accreditation@sarugby.co.za`.  
Provenance: **Inferred**.

**BR-031 — Approval body is a template with placeholders.**  
`EmailApproved` is filled by replacing `[ACCREDITATIONNAME]`, `[CONTACTNAME]`, `[CONTACTTELEPHONE]`, `[CONTACTEMAIL]`, `[REFNUMBER]`, `[APPLICANTNAME]`, `[ZONES]`, and `[REASONNOTE]`. In the code that was read, the contact placeholders are replaced with the literal strings "ContactName", "ContactTelephone", and "ContactEmail", not with profile fields.  
Provenance: **Inferred**. Whether that is intentional is **Requires Confirmation**.

**BR-032 — AppWiz guest push is not part of the running queue.**  
The block that called `senddata_to_Api` for `AC == 23` is commented out.  
Provenance: **Inferred**.

## Live scoring and fixtures

**BR-033 — Teams on the scoring screen sort by jersey ascending.**  
Provenance: **Inferred** from `scoringAction`.

**BR-034 — Commentary groups by `SecondHalf`, and times inside a half sort descending.**  
Provenance: **Inferred**.

**BR-035 — The scoring action does not require a logged-in session.**  
`Live_ScoreController::init` only disables the layout.  
Provenance: **Inferred**.

## API

**BR-036 — API login returns the profile array or `Error` 1.**  
The hardcoded override is the same idea as BR-004.  
Provenance: **Inferred**.

**BR-037 — Commentary upload writes a JSON file named from the fixture id.**  
`uploadcommentaryAction` stores the `comment` parameter in `{fid}comments.json` under the PHP working directory and exits. No auth check is in that controller's `init`.  
Provenance: **Inferred**.

## SMS

**BR-038 — SMS is sent through MyMobileAPI.**  
`MyMobileAPI::sendSms` posts `Type=sendparam` with the destination and the message. A null HTTP response is "SMS connection error." Connect and total timeouts are 2000 ms.  
Provenance: **Inferred**.

**BR-039 — A referee SMS can carry an OTP and a URL.**  
The stored sample includes both. Generation rules for the code were not extracted from `RefereeController`.  
Provenance: **Documented** as a sample message only. The production format is **Requires Confirmation** beyond that file.

## First-profile field cleanup

**BR-040 — Blank posted fields are removed before update.**  
Empty `ClubID` or `SocietyID` becomes 0. `JobType` 2 sets `JobTitle` to 0. Email whitespace is stripped.  
Provenance: **Inferred** from `FirstController::indexAction`.

## Locale and time

**BR-041 — The front controller sets `Africa/Johannesburg`.**  
Login and several module controllers set it again. A committed `error_log` line is stamped `Africa/Harare`, so at least one environment did not use the Johannesburg setting for PHP startup warnings.  
Provenance: **Inferred**.

**BR-042 — Default locale is `af_ZA`.**  
Provenance: **Inferred** from `application.ini`.

## What is not a rule

- The README title does not define eligibility, age grades, or competition law.
- Ethnicity labels in BR-024 are the strings the counter uses. They are not a statement that those are the only legal categories in the `Ethnicity` table.
- Copy controllers and `TestController` contain older or one-off variants of some of the rules above. Where they disagree with `SquadController` and `MembersController`, the changelog names those two files, so this document follows them.  
  Provenance: **Inferred**.
