# Business rules — supplier.magus.events

Evidence date: 2026-10-01. Source: `master` at `ccfc664` unless noted.

Rules below are taken from controller conditions, view labels, and email or FAQ sentences. A rule that exists only because the code behaves that way is marked **Inferred**. A rule that is written in an email or the FAQ and is not enforced by an action is marked **Documented**. Nothing here is a new product decision.

## Identity and access

**BR-001 — A company registration number can be used once.** **Inferred.** `RegisterController::check_Company` rejects the POST when `Companies_Profile.RegistrationNumber` already exists. The user-facing message says the account already exists and to contact SARU. The same message is used when the person already exists.

**BR-002 — An ID number or email can belong to only one member.** **Inferred.** `check_IDNumber` matches `IDNumber = '{value}' OR Email = '{value}'` on `Company_Members`.

**BR-003 — The first user is an Administrator.** **Implemented.** `Rol` is set to the string `Administrator` on registration. No other role is offered on that form.

**BR-004 — South African addresses use ID type 1.** **Inferred.** If `Country` equals the string `SOUTH AFRICA`, `IDType = 1`. Any other country sets `IDType = 2`. No checksum is applied to the ID number.

**BR-005 — Confirming the email allows login.** **Implemented.** A matching `Validate_Key` sets `Email_Confirmed = 1`, `AllowLogin = 1`, and clears the key.

**BR-006 — An administrator must review the company, and activation can take 48 hours.** **Documented** in the registration email. **Not enforced.** `validateemailAction` activates the account immediately. The email also says the company cannot sign up to an event until extra documents are supplied. This repository does not check documents before `Company_Events` is inserted.

**BR-007 — Login is an email and a password stored on `CompaniesLogin`.** **Implemented.** The adapter does not read `AllowLogin` or `Email_Confirmed`. Those flags are written on `Company_Members` and are not consulted in `LoginNow`.

**BR-008 — One hard-coded password signs in as the stored user.** **Implemented.** If the posted password equals that value, the stored `CompaniesLogin.password` is used as the credential, so the MD5 step is skipped. The value is not recorded here.

**BR-009 — A successful login always selects event 61.** **Implemented.** `EVENT.EID = 61` is assigned in code. The user does not choose an event.

**BR-010 — Registration always links the company to event 44 with status 2.** **Implemented.** This is not event 61. What status 2 means on `Company_Events` is **Unknown / Requires Confirmation**.

**BR-011 — Authenticated pages require a Zend_Auth identity.** **Implemented** for `IndexController`, `CompanyController`, `UsersController`, and `PermitsController`. `LoginController::uploadremotecompanyAction` and `companypicAction` do not.

**BR-012 — Staff profile reads and writes are not limited to the caller's company.** **Inferred** from `UsersController::profileAction`, which loads `getRow($id)` with the request id only.

**BR-013 — Only one administrator is allowed.** **Documented** in the FAQ. **Not enforced.** `UsersController` can insert further members with role label Administrator.

**BR-014 — Staff role labels are a fixed list.** **Implemented.** Administrator, Director, Management, Operations, Driver, Technician, General Staff. No code grants different permissions for those labels.

**BR-015 — Turning on staff login creates a password and does not send it.** **Implemented.** `addAction` and `profileAction` MD5 a random value when `AllowLogin` is set (on edit, only if it was previously 0). No mail call follows. The person cannot know the password from this application unless some other system reads it. **Unknown** whether that happens.

**BR-016 — A password reset confirms the email and allows login.** **Implemented.** The reset email includes the new password in clear text.

**BR-017 — Session cookies last 864000 seconds.** **Implemented** in `application.ini` (`resources.session.cookie_lifetime`). That is 10 days.

## Permits

**BR-018 — A new permit is stored with status 2.** **Implemented** in `addpermitAction` and `duplicatepermitAction`.

**BR-019 — The profile labels status.** **Implemented** in `permits/profile.phtml`:

- Status less than 4, and the form is not otherwise locked: "Pending".
- Status equal to 4: "Approved".
- Status greater than 4, or status equal to 5: "Cancelled".
- If the cutoff lock is on and the request date is still in the future: "Locked/Pending Approval".
- If the cutoff lock is on and the request date is today or earlier: "Locked".

The `Permit_Status` table is not read by this view. Names for statuses 1 and 3 are **Unknown**.

**BR-020 — Cancelling sets status 5.** **Implemented.** `CancelledBy` is the signed-in member id. `CancelMessage` is the posted message. No email is sent, despite a comment that says one should be.

**BR-021 — Nothing in this repository sets Approved.** **Inferred.** No assignment of status `4`, `ApprovedBy`, or `DeclinedBy` was found. The email says an administrator will review the request and may change the date and time. That review is **Documented** and lives outside this tree if it exists at all.

**BR-022 — A permit number is the accreditation prefix plus the numeric id padded to 6 digits.** **Implemented.** `Model_AccreditationPermits::getRef`. The prefix comes from `Accreditation_List` for the event id. The displayed number on the profile appends `/{year of RequestDate}`.

**BR-023 — Each new permit also gets a random 13-digit `PermitRefSerial`.** **Implemented.**

**BR-024 — Occupant type 1 is a permanent member.** **Inferred.** If the posted ID number matches `Company_Members.IDNumber`, the stored first name, surname, and ID replace the posted values. The match is not limited to the signed-in company.

**BR-025 — A driver is an occupant with `Driver = 1`.** **Implemented.** The driver is appended to the same JSON `Accupants` list.

**BR-026 — Dates offered for a new permit must be in the future and before the noon cutoff.** **Implemented** in `createAction` and on the dashboard date list. Stored dates in `LoadinActiveDate` and `LoadOutActiveDate` are `d/m/Y` strings inside JSON. A date is kept when the calendar date is after today and `12:00` on the previous day is still in the future. Timezone is `Africa/Johannesburg`.

**BR-027 — Editing an existing permit uses 13:00 the day before, not noon.** **Implemented** in `profileAction`. The permit is locked when `RequestDate` at 13:00 minus one day is already past. When it is not locked, individual load-in and load-out dates still drop out at 13:00 the day before.

**BR-028 — Cancellations and changes are accepted until 1pm.** **Documented** in the permit email ("up to 1pm daily. Any changes to permit requests for the next day after this time will no be accepted."). The profile lock uses 13:00 the previous day, which matches 1pm. The create form and the dashboard use 12:00 the previous day. The two cutoffs disagree. **Requires confirmation** which clock is the business rule.

**BR-029 — The form locks when status is greater than 4.** **Implemented** in the profile view. `savepermitAction` does not check the lock or the company id.

**BR-030 — Duplicate swaps the first two parts of the posted date.** **Implemented.** `date` is split on `/` and stored as `{part2}/{part1}/{part3}`. If the form posts `d/m/Y`, the stored value is `m/d/Y`. Other dates in this application are handled as `d/m/Y`. **Inferred** inconsistency. Requires confirmation of the posted format.

**BR-031 — The confirmation email calls the request Load-Out only when `Surname` equals 2.** **Implemented.** Otherwise it says Load-In. **Inferred** that `LoadType` was the intended field. Requires confirmation.

**BR-032 — Delivery permits last at most 2 hours and do not enter every area. Work permits require a health and safety file.** **Documented** in the FAQ. **Not enforced** in `addpermitAction`.

**BR-033 — One permit covers one vehicle for one day. Duplicating is how a further day is requested.** **Documented** in the FAQ. The duplicate action is **Implemented**. The create action does not reject a multi-day range because the date model is a single `RequestDate`.

**BR-034 — A permit can be saved before it is submitted.** **Documented** in the FAQ ("you can create as many permit requests as you like and save them"). `addpermitAction` always sets status 2 and sends the review email. There is no separate draft status in this controller. **Documented** versus **Implemented**. Requires confirmation.

**BR-035 — The PDF is only available after approval.** **Documented** in the FAQ (download from the menu next to the approved field). `getpermitpdfAction` does not check status. It downloads whenever the remote HTML can be fetched.

## Company data

**BR-036 — A new company is stored active.** **Implemented.** `Active = 1` in `Create_Company`. No later action in this repository sets `Active` to 0.

**BR-037 — B-BBEE level, company type, and country are chosen from database lists.** **Implemented.** The legal meaning of each B-BBEE row is the row's own text. This repository does not define the levels.

**BR-038 — Registration asks the visitor to accept terms on sarugby.co.za.** **Documented** in the registration view ("By continuing, you're confirming that you've read our Terms & Conditions"), linking to `http://www.sarugby.co.za/`. There is no stored consent flag in `newcompanyAction`.

## Communications

**BR-039 — Supplier mail comes from SA Rugby Event Administration.** **Documented** and **Implemented.** From address `accreditation@sarugby.co.za`. Subject `SARugby Notification`. General contact in the templates is `accreditation@sarugby.co.za`. Load-in/out contact in the permit email is `permits@sarugby.co.za`. The registration footer quotes telephone `+27219287000`.

**BR-040 — Health and safety contact and security contact are fixed addresses.** **Documented** in the FAQ: `HealthandSafety7s@sarugby.co.za` and `security@sarugby.co.za`. A named person is given as "Danie xxxxxx".

**BR-041 — The event named in the FAQ is the HSBC World Rugby Sevens Series.** **Documented.** The FAQ says every staff member must apply on an accreditation portal, and the URL in the answer is the literal text `[www]`.

## Conflicts to confirm

| Topic | Code | Other evidence |
|---|---|---|
| Which event | Login sets 61. Registration writes 44 | No selector in the UI |
| Activation | Email says up to 48 hours and an administrator review | The confirmation link activates immediately |
| Change cutoff | Create and dashboard use 12:00 the day before. Profile uses 13:00 the day before | Email says 1pm |
| One administrator | FAQ | Staff add allows more |
| Draft versus submit | FAQ says a request can be saved without submission | Create always sets status 2 and sends mail |
| Login table | Registration writes `Company_Members` | Authentication reads `CompaniesLogin` |
| Approved download | FAQ says download after approval | PDF action does not check status |
