# Business rules — legitimate_licences

Evidence date: 2026-10-10.

Source: `main` at `707aa72`. Rules are taken from enums, controllers, and `DBUp/Scripts/2023/2205-EmailSenderUpdate-Legitimate.sql`. Where the code implies a rule that is not written as a requirement, the row is marked **Inferred**.

Do not treat system-setting numbers as fixed. `GazettePeriodDays`, `RenewalGracePeriod`, and `UpliftmentNotificationDays` are read from `System.Settings`. Their values are not in this repository.

## Identity and access

| Id | Rule | Provenance |
|---|---|---|
| BR-001 | Anonymous users cannot open the application except paths `Auth`, `Content`, `Scripts`, `Images`, and `SignalR` | Implemented (`Web.config` authorization and `location` blocks) |
| BR-002 | A forms session lasts 30 minutes and slides on activity | Implemented |
| BR-003 | Password reset requires the membership question and answer. Password retrieval is disabled | Implemented |
| BR-004 | Membership `passwordFormat` is `Clear` | Implemented. Human review required. Secrets are not copied here |
| BR-005 | Screen actions check a securable id. Failure on the inter-department page redirects to the dashboard. Failure on the release POST throws "Security error" | Implemented |
| BR-006 | API calls other than a path containing `/1.0/login` require headers `api_Key` and `api_User`. The user header must match a security user. Otherwise the response is HTTP 401 | Implemented |
| BR-007 | Fingerprint login is used only when `FingerprintAuthenticationEnabled` is true. The committed web config sets it to false, so username and password are the path that runs | Implemented |
| BR-008 | `SignInUser` reports `passwordExpired: false` even after a successful login | Implemented. Whether expiry is enforced elsewhere is **Unknown / Requires Confirmation** |
| BR-009 | The system user id default in code is 1 | Implemented (`Defaults.SystemUserID`) |

## Divisions

| Id | Rule | Provenance |
|---|---|---|
| BR-010 | Division 1 is the Provincial Regulatory Entity. Division 2 is the National Public Transport Regulator. Any other division id, including TAT = 3, is labelled `TAT` in the SQL case expression | Documented in the 2023 script. TAT expansion is **Unknown / Requires Confirmation** |
| BR-011 | A brand-new gazette detail (id `-1`) is created in province KwaZulu-Natal, status Awaiting gazette finalisation, print date now, expiry now plus `GazettePeriodDays` | Implemented in `LegitimateDatabase.GetGazetteDetail` |
| BR-012 | The open gazette for a division and gazette type is the row still in Awaiting gazette finalisation | Implemented in `GetCurrentGazetteDetail` |

## Applications

| Id | Rule | Provenance |
|---|---|---|
| BR-013 | An application moves through statuses None, Data capture, Verification, Payment, Gazetting, Adjudication, Finalised, Cancelled | Implemented enum |
| BR-014 | Sub-statuses cover correction, gazette printing, gazette expiry, automated or manual adjudication scheduling, agenda finalisation, and adjudication outcome | Implemented enum |
| BR-015 | Request types are the set in `ApplicationRequestTypeEnum`. Ids 12, 19, 20, and 21 are not defined | Implemented |
| BR-016 | Renewal applications use request type 8. The upcoming-expiry SMS skips a licence that already has a non-cancelled renewal application whose licence row is not split into a new application | Implemented in `App.Pr_Svc_UpcomingExpiryNotifications` |
| BR-017 | Adjudication decisions are Approved, Postponed, Refused, Cancelled, Withdrawn, Struck off. Decision ids 3 and 7 are not defined | Implemented |
| BR-018 | Adjudication can be a hearing or a meeting | Implemented |
| BR-019 | A user with the bypass securable can bypass adjudication or bypass gazette expiry. Those are separate securables (1025, 1033, and 1044) | Implemented as permissions. The effect of each bypass inside SQL was not fully traced |
| BR-020 | Referrals are to a tourism authority or a planning authority | Implemented |
| BR-021 | Payments are cash, card, or EFT | Implemented enum. Amounts are **Unknown / Requires Confirmation** |

## Licences, routes, people, vehicles

| Id | Rule | Provenance |
|---|---|---|
| BR-022 | Operating-licence statuses include Awaiting issuing, Issued, Expiring within 60 days, Expired, Temporarily suspended, Archived, Awaiting new vehicle. The same ids are used for accreditations | Implemented enums. The 60-day label is the enum name. The SMS window is `RenewalGracePeriod`, which may not be 60 |
| BR-023 | In `Pr_Svc_SendUpliftmentNotifications`, `LicenceStatusID = 1` is commented as awaiting upliftment. In `LicenceStatusEnum`, id 1 is named Awaiting issuing | Implemented in two places that do not use the same words. **Requires Confirmation** which label is shown to users |
| BR-024 | Upliftment SMS is sent when days since issue reach `UpliftmentNotificationDays`, the reminder has not been sent, the number does not match `LTOKZN%`, and the licence is not in `Verified.LicenceExceptions`. The message text still says "more than 30 days" | Implemented. Copy and setting can diverge |
| BR-025 | Expiry SMS skips operating-licence numbers matching `LT%` and skips `Verified.LicenceExceptions` | Implemented |
| BR-026 | Route status is Active, Inactive, Not verified, or Cancelled | Implemented |
| BR-027 | Route type is standard, radius, area of operation, temporary, or charter | Implemented |
| BR-028 | A route change on a licence is one of: existing route on the licence, add a brand-new route, add an existing unlinked route, remove a route, add an existing linked route | Implemented |
| BR-029 | Vehicle type is motor car, minibus, bus, midibus, or other. Which service types apply is loaded from data and grouped in `ApplicationLogic` | Implemented |
| BR-030 | Service types include scheduled, unscheduled, charter, tourist, staff, learners, courtesy, other, and temporary service | Implemented |
| BR-031 | Association and member verified status is Active or Inactive | Implemented |
| BR-032 | Identity can be an RSA ID, passport, founding statement, temporary ID, foreign ID, certificate of incorporation, or a taxi association registration number | Implemented |
| BR-033 | Stationery moves through Awaiting issuing, Issued for use, Used, Defaced, Lost, Transferred to external party, In transit. Batch types are from GPW, internal transfer, or external transfer | Implemented |

## Issuing and printing

| Id | Rule | Provenance |
|---|---|---|
| BR-034 | A test licence print and an actual licence print are different securables | Implemented (2011 and 2012) |
| BR-035 | Issue without print is its own securable (2023) | Implemented |
| BR-036 | The desktop program refuses to run with no arguments, with an argument count other than three after the protocol prefix, or with a verb other than `print` or `scan` | Implemented |
| BR-037 | Before printing, the desktop compares its product version with `desktopAppVersion` returned by the API | Implemented |
| BR-038 | Operating-licence, receipt, payment-advice, accreditation certificate, accreditation decal, and temporary-replacement printers are separate classes | Implemented |

## Verification queue

| Id | Rule | Provenance |
|---|---|---|
| BR-039 | A dequeued system trigger loads verification JSON and pushes it to SignalR `AddVerificationToList` | Implemented |
| BR-040 | If the SignalR invoke throws, the handler records "Error sending data" and still sets the trigger status to Completed | Implemented. **Inferred:** a failed push is not retried by this handler |

## Data correction

| Id | Rule | Provenance |
|---|---|---|
| BR-041 | Purification covers duplicate national route numbers and duplicate board route numbers | Implemented procedure names |
| BR-042 | Separate securables exist to add and delete blocked licences and blocked ID numbers | Implemented |
| BR-043 | A business-rule bypass request has a type id and can be listed while in progress for the current user. Each office location can store a bypass approver user id | Implemented. The rules themselves are rows in `BusinessRuleBypassRequestType` and were not enumerated from data |
| BR-044 | TAT applications pending data release are released one application at a time, with a comment, by a user who has `InterDepartmentModule_ReleaseData` | Implemented |

## Id collisions (implemented)

These enums assign the same integer to two names. Behaviour that keys only on the integer cannot tell the names apart.

| Id | Names in `Enums.cs` |
|---|---|
| 1000 | `EntityEnum.ApplicationRequestDetails` and `EntityEnum.ApplicationtDetails` (spelling as in code) |
| 1023 | `ApplicationsModule_Purification_OverwriteOriginatingLicenceNumbers` and `ApplicationsModule_Purification_UpdateVehiclesLinkedToOperatingLicencesIncorrectly` |
| 1025 | `EntityEnum.TemporaryReplacementLicences` and `EntityEnum.ApplicationLicenceTemporaryReplacement`; also securable `ApplicationsModule_Adjudication_BypassAdjudication` |
| 2012 | `OperatingLicencesModule_LicenceIssueRequest_PrintLicence_Actual` and `RoutesModule_SendToPurification` |

Provenance: **Implemented**. Whether the database securable table uses these same numbers is **Unknown / Requires Confirmation**.

## Notifications wording

| Id | Rule | Provenance |
|---|---|---|
| BR-045 | Some SMS bodies say "Provincial Regulatory Entity". Inspection and gazette messages in the same script say "National Public Transport Regulator" | Documented in the script. Which template runs for which division was not fully traced |
| BR-046 | A periodic summary email uses subject `Legiti-mate status update` and sender setting `SenderEmailAddress` | Implemented |
| BR-047 | Message type 1 is email and message type 2 is SMS in the inserts that were read | Implemented in that script |

## Rules not established

- Fee amounts, gazette day count, renewal grace days, and upliftment day count.
- The full workflow step graph per application type.
- Which business-rule bypass types exist.
- Whether OLAS, LTPS, and NTLIS are still live source systems or historical imports.
- What TAT stands for, and who may release TAT data besides the securable check.
- Production board: print code hard-codes KZN PRE on some layouts and NPTR on others. Which layout is used for which division is only partly visible (NPTR string in `OperatingLicencePrinter`, KZN string in the printer control and payment advice).
