# Business rules — website (1 For The Road)

Evidence date: 2026-09-28. Source: `git@bitbucket.org:overdrive/website.git`, branch `origin/development_russel` at `a87b0c5` unless a rule says `master`.

Rules are quoted from behaviour, not invented. Each rule is marked **Documented** (visible copy), **Implemented** (enforced by code), or **Inferred**.

The FAQ is marketing and operations copy. The PHP enforces only the rules that name a code path. Where the FAQ and the code disagree, both are recorded.

`master` does not contain these rules. It contains Prime Cab placeholder copy. Do not take Prime Cab prices, the US address, or the theme booking form as 1 For The Road policy.

## Service definition

**BR-001 — What the service is.** Drivers take the member home in the member's own vehicle. A second person follows in another vehicle and both leave after the drop-off.

- Provenance: **Documented** (`faq.html` "So how does this work?", `aboutus.html`, homepage hero).
- Code does not check that a vehicle exists before a booking is saved in the paths that were read. Vehicle capture is mandatory on the public registration form only.

**BR-002 — Own vehicle only.** The service does not apply when the member has no vehicle, and it does not apply to driving a friend's car. The FAQ says this is in the contract.

- Provenance: **Documented**. Contract PDF files exist (`pdfs/TermsandConditions.201309.pdf`, `pdfs/TermsandConditons.pdf`). The PDF text was not extracted. **Requires Confirmation** against the signed contract.

**BR-003 — Service area.** "Cape Town and the surrounds."

- Provenance: **Documented** (FAQ item 9).
- The registration form lists all nine South African provinces, with option values `-10, -2, -3, -4, -5, -6, -7, -8, -9`. The form does not block provinces outside the Western Cape.
- **Inferred:** area enforcement, if any, is operational or inside `tblArea` data, not in the public form.

**BR-004 — Driver identification.** The call centre tells the member the driver's name. The member is asked to check the driver's 1 For The Road ID before handing over keys.

- Provenance: **Documented**. Not implemented in the website.

## Hours

**BR-005 — Published hours.** Footer: `10H00 - 02H00`, and immediately under it `CURRENTLY 10H00 - 00H00 Due to COVID19`.

- Provenance: **Documented**.
- The two lines conflict. Which one applies in 2026 is **Unknown / Requires Confirmation**. The branch is dated 2021.

**BR-006 — Call centre close.** "The call centre closes at 2am. If you require a driver later than 2am, you need to pre-book with the call centre at least 2 hours prior."

- Provenance: **Documented** (FAQ item 3).
- Conflicts with the COVID footer line that says closing at midnight.

**BR-007 — Busy periods.** Pre-booking is essential for Christmas and New Year's Eve. Waiting times may be longer if the member does not pre-book. The service will try to accommodate the member.

- Provenance: **Documented**. No date logic in the code that was read implements a public-holiday surcharge or a booking cutoff.

## Booking and cancellation

**BR-008 — How to book.** Phone `0861 622 642`. Pre-booking should be at least 2 hours before the driver is required. Advance bookings are welcomed. A same-evening alternative is described: phone about an hour before leaving, or phone and request a pickup time.

- Provenance: **Documented** (`packages.html`, FAQ items 1 and "So how does this work?").
- There is no public booking form on `development_russel`.

**BR-009 — Arrival window.** Driver should normally arrive within 45 minutes to 1 hour. Delays are communicated by SMS. After the "driver is waiting" SMS, the member has 15 minutes to meet the driver.

- Provenance: **Documented**. SMS sending was not found in this repository.

**BR-010 — Extra drop-offs.** Allowed if requested before the driver arrives. Charged as extra mileage or another round trip per additional drop-off.

- Provenance: **Documented**. The rate for that extra trip is not in the HTML. **Unknown** whether `webadmin` has a specific extra-drop-off field.

**BR-011 — Cancellation fees.**

Cancel by phone at least 2 hours before the booking, or at least before the driver has left. After that:

- Between 1 hour and 1 hour 59 minutes before pickup: 10% of the minimum fee for the package.
- Less than 59 minutes before pickup: 50% of the minimum fee for the package.

- Provenance: **Documented** (FAQ item 4). The FAQ text says "1 hours".
- Webadmin can mark a booking `c: Cancelled` or `cb: Cancelled Billable` and stores a timestamp. It does not calculate 10% or 50% in the status method that was read.
- **Inferred:** "Cancelled Billable" is the staff encoding of a chargeable cancellation. The percentage is not applied by that method.

**BR-012 — Booking status sequence.** Implemented values, from `booking.cls.php`:

1. `1: Initial`
2. `2: Driver Confirmed`
3. `3: Client Confirmed`
4. `4: Driver Collecting`
5. `5: Client Collecting`
6. `6: Completed`
7. `c: Cancelled`
8. `cb: Cancelled Billable`

Delete is blocked when status is `6: Completed` or the literal `cb: CancelledBillable` (no space). The writer stores `cb: Cancelled Billable` (with a space). **Inferred:** a billable cancellation can still be deleted because the strings differ. **Requires Confirmation** before anyone "fixes" it; operations may depend on the current strings.

## Membership and money

**BR-013 — How someone becomes a member.** Choose a package, submit the application with bank details so a debit order can be set up, then phone to book. The welcome email says the service "will be activated shortly and you will be contacted." New clients are saved with `blnActive = 1` immediately.

- Provenance: **Documented** steps on `packages.html`. **Implemented** active flag on insert.
- **Inferred conflict:** the email says activation follows, while the row is already active. What "active" means for being allowed to book is **Requires Confirmation**.

**BR-014 — Packages shown to the public.**

Visible cards:

| Name | Id used by the form | Monthly fee shown | Allowance shown |
|---|---|---|---|
| Basic | 1 | R99.00 | No free kilometres; a per-kilometre charge |
| 20 | 2 | R275.00 | Positioned as the 20 option |
| 40 | 3 | R418.00 | 40 km free, then a discounted per-km rate |
| 80 | 4 | R677.00 | 80 km free, then the best per-km rate described |
| Function | no self-serve id | Not priced | Corporate and private functions; "tailored packages" |
| Family add-on | the row with `blnFamilyDiscountPackage = 1`; code also looks up `PackageID` 7 | R40 per person per month | Same household, on 20/40/80 only, as stated in the copy |

- Provenance: **Documented** and **Implemented** as HTML.
- A commented-out table (not shown to visitors) lists per-km rates R12.71, R13.92, R10.59, R8.47 and minimum trips 15, 5, 15, 15 km, with the same monthly fees as the cards. Older commented copy lists R250, R380, and R615. Those are not live page content.
- Amounts stored on the member come from `tblPackage` at registration (`dblPriceInc`, `dblMinCharge`, `dblFreeKM`, `dblMinimumTrip`, `dblChargePerKM`), not from the HTML. **Requires Confirmation** that the database matches the cards.

**BR-015 — Debit order.** The packages page says the application collects bank details "so we can set up a debit order."

- Provenance: **Documented**.
- **Implemented:** bank name, branch code, account type, account holder, and account number are required by `jsValidate` and stored on `tblClient`.
- Account types offered: Current/Checking, Savings, Transmission.
- No payment-gateway or debit-order bureau integration was identified. **Unknown** how the debit order is lodged.

**BR-016 — Contract term.** Twelve months from signature. Renews automatically for the same period unless written notice arrives 30 days before the end of the initial period or a renewal period.

- Provenance: **Documented** (FAQ, membership section). No renewal job was found in the repository.

**BR-017 — Package changes.** "Normally only permitted at the annual renewal." The member is told to contact the business and that staff will try to accommodate a request.

- Provenance: **Documented**. Webadmin can edit `tblClientPackage`. The anniversary restriction is not enforced in the code that was read.

**BR-018 — Family discount quantity.** The form defaults quantity to 1, `maxlength` 1, and shows R40. The server multiplies the family package's `dblPriceInc` by the submitted quantity when the checkbox is `on`.

- Provenance: **Implemented**.
- **Inferred:** a one-character quantity cannot express 10 or more on the form. The server does not cap it if another client posts a larger value.

**BR-019 — Membership numbers.** New number = max `strMembershipNumber` where the number is > 0 and < 9999999, plus 1. Client save also rejects a duplicate non-empty membership number on a different `ClientID` (admin client class).

- Provenance: **Implemented**.

**BR-020 — Accident risk.** If the driver has an accident in the member's vehicle, the FAQ says the member indemnified 1 For The Road in the contract and must claim on their own insurance.

- Provenance: **Documented**. A file `webadmin/Documents/Indemnification and Waiver.pdf` exists. PDF wording was not extracted.

## Registration validation and abuse

**BR-021 — Required application fields (browser).** Package, name and surname, ID number, email matching `/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i`, cellphone, home address, postal code, province other than `0`, vehicle make and model, registration, colour, bank name, branch code, account type other than `0`, account holder, account number, terms checkbox.

- Provenance: **Implemented** in `jsValidate`. Bypassed if the browser script is skipped. The server does not repeat this list.

**BR-022 — reCAPTCHA.** Register and contact run Google reCAPTCHA v3 and call siteverify. The contact form redirects to an error state when verification fails. Register proceeds with the database write only when `success` is true.

- Provenance: **Implemented**.

**BR-023 — Spam.** If the email contains `googlemail.com`, a spam row is inserted. If `sysSpam` already contains the email, registration redirects to `MID=3` and does not create a client.

- Provenance: **Implemented**.
- **Inferred:** `googlemail.com` is treated as abusive. `gmail.com` is not matched by that substring check.

**BR-024 — Survey options.** Bangersandnash, Facebook, Friends, Twitter, Web, Other. Not required by `jsValidate`.

- Provenance: **Implemented** as a select list.

## Staff access

**BR-025 — Who can sign in.** The email must match `sysUser.strEmail`, the linked `sysSecurityGroup` must be active, the stored MD5 must equal `md5` of the password, and `sysUser.blnActive` must be 1.

- Inactive user, bad password, and unknown user/inactive group are three different messages (see the PRD). Each attempt is written through `tblLoginInsert`.
- Provenance: **Implemented**.
- MD5 without a salt is what the code does. This document does not treat that as an approved security standard.

**BR-026 — What a signed-in user sees.** Menu rows are limited to `refSecurityGroupID` of the session. Admin home blocks are wrapped in `blnView` checks.

- Provenance: **Implemented**. The actual group names and grants live in the database and were not dumped.

## Young adults (14ya)

**BR-027 — Separate booking flow.** `bookingYA` can email a parent ("1 For Young Adults Approval required") and can email `info@14ya.co.za,bookings@14ya.co.za` ("New 14YA Booking Made").

- Provenance: **Implemented**.
- Host configuration also maps `www.14ya.co.za` and `14ya.co.za.www452.jnb1.host-h.net` at the database name `1fortheroad`.
- Product rules of 14ya (age, consent, pricing) are **Unknown / Requires Confirmation** beyond this mail behaviour. A PDF `webadmin/Documents/14YATCs.pdf` exists and was not extracted.

## Default-branch content that is not a business rule

Prime Cab copy on `master` (placeholder phone, `primecab@booking.com`, theme FAQ Latin text, theme payment pages) is template content. It is not a 1 For The Road rule.

The only `master` behaviour aimed at this brand is the Matomo cookie domain `*.1fortheroad.co.za`. That is an analytics setting, not a commercial rule.

## Conflicts to confirm

1. Footer COVID hours (close midnight) versus FAQ (close 02:00).
2. Welcome email ("activated shortly") versus `blnActive = 1` on insert.
3. HTML package prices versus `tblPackage` prices used for billing.
4. Cancelled-billable status string with a space versus the delete guard without a space.
5. FAQ area "Cape Town and the surrounds" versus a province list covering all nine provinces.
6. FAQ cancellation percentages versus webadmin, which stores a status and does not compute those percentages in the method reviewed.
