# Fleet Module

**Folder:** `CRUD/fleet/` · **Main table:** `delivery_vehicle` (6 lorries) · **Reached from:** navbar → Fleet

Everything about the company's **own road vehicles** — the lorries that deliver and collect plant. Not to be confused with the [Plant List](plant-list.md), which covers the machines being hired out.

The module carries the vehicle records plus five compliance areas that hang off them: **maintenance**, **mileage**, **PCNs** (penalty charge notices), **recalls**, and **FORS** (Fleet Operator Recognition Scheme, including driver training and RIDDOR incidents).

Nearly every vehicle-specific page takes the same parameter: **`?id_delivery_vehicle=<id>`**.

> Documented to the format set by [sites.md](sites.md).

---

## How the pieces fit together

```mermaid
flowchart TD
    D[Fleet Dashboard<br/>fleet_dashboard.php] --> L[Vehicle List<br/>fleet_index.php]
    D --> M[Maintenance<br/>calendar / schedule / forecast]
    D --> R[Fleet Reports<br/>fleet_reports.php]
    D --> CT[Compliance Tasks<br/>compliance_tasks/]

    L --> V[Edit Vehicle<br/>fleet_update.php]
    V --> DOC[Documents<br/>fleet_documents.php]
    V --> AL[Alerts<br/>fleet_alerts.php]
    V --> MO[Monthly Readings<br/>fleet_monthly.php]
    V --> RC[Recall Checks<br/>recalls/]

    MI[Mileage<br/>mileage/] --> MIR[Recorder]
    MI --> MID[Detail]
    PC[PCNs<br/>pcn/] --> PCR[Recorder]

    F[FORS<br/>fors/index.php] --> TR[Training<br/>courses / matrix / CPC]
    F --> RI[RIDDOR<br/>incidents / matrix]
    F --> CR[Compliance Report]
```

---

## Fleet Dashboard — `fleet_dashboard.php`

![Fleet Dashboard](../screenshots/fleet__dashboard.png)

The hub — headline compliance status and links into each area.

## Vehicle List — `fleet_index.php`

![Vehicle List](../screenshots/fleet__vehicle_list.png)

All lorries with registration, type and status. Row actions lead to edit, documents, alerts and monthly readings.

## Add / Edit Vehicle — `fleet_create.php`, `fleet_update.php`

![Edit Vehicle](../screenshots/fleet__vehicle_edit.png)

Vehicle identity and compliance dates (`?id_delivery_vehicle=<id>` to edit). `fleet_toggle_active.php` retires a vehicle without deleting it.

## Vehicle Documents — `fleet_documents.php`

![Vehicle Documents](../screenshots/fleet__documents.png)

Certificates and paperwork per vehicle (MOT, insurance, plating), stored in `vehicle_document` (66 rows).

| Endpoint | Purpose |
|---|---|
| `fleet_documents_upload.php` | Validated upload; returns JSON |
| `fleet_documents_update.php` | Update document metadata |
| `fleet_documents_delete.php` | Remove a document |

## Alerts — `fleet_alerts.php`

![Fleet Alerts](../screenshots/fleet__alerts.png)

Reminders against a vehicle (`vehicle_alert`, 16 rows) — created via `fleet_alert_create.php` / `fleet_alerts_save.php`, cleared by `fleet_alerts_complete.php`, removed by `fleet_alerts_delete.php`.

## Monthly Readings — `fleet_monthly.php`

Per-vehicle monthly odometer readings (`vehicle_monthly_reading`, 142 rows). Saved by `fleet_monthly_save.php`, removed by `fleet_monthly_delete.php`. `fleet_tonnes_capture.php` records tonnage carried.

## Maintenance

| Page | Purpose |
|---|---|
| `fleet_maintenance_calendar.php` | Calendar view of upcoming work |
| `fleet_maintenance_schedule.php` | The schedule itself |
| `fleet_maintenance_forecast.php` | Forward projection of what falls due |

![Maintenance Calendar](../screenshots/fleet__maintenance_calendar.png)

## Reports & Settings

![Fleet Reports](../screenshots/fleet__reports.png)

`fleet_reports.php` covers fleet-wide reporting; `fleet_settings.php` / `fleet_settings_save.php` hold module configuration.

## Maps

- `map_ulez_zones.php` — ULEZ / clean-air zone map.
- `map_police_embargo.php` — police embargo areas.

---

## Compliance Tasks — `compliance_tasks/`

![Compliance Tasks](../screenshots/fleet__compliance_tasks.png)

Recurring compliance jobs driven by `compliance_task_types` (12 types) with completions recorded in `compliance_task_completions`. `compliance_tasks_helpers.php` holds the shared logic.

---

## FORS — `fors/`

![FORS](../screenshots/fleet__fors_index.png)

FORS accreditation evidence: driver training, CPC hours and RIDDOR incidents.

### Training
| Page | Purpose |
|---|---|
| `fors/training_courses.php` | Course catalogue (`training_courses`, 20 rows) |
| `fors/employee_training.php` | Assign courses to staff (`employee_training_assignments`, 33 rows) |
| `fors/training_matrix.php` | Who has what, at a glance |
| `fors/cpc_tracking.php` | Driver CPC hours (`cpc_training_hours`) |

![Training Matrix](../screenshots/fleet__training_matrix.png)

### RIDDOR — `fors/riddor/`
Reportable incidents (`riddor_incidents`).

| Page | Purpose |
|---|---|
| `riddor_incident_recorder.php` | Record an incident |
| `riddor_incident_detail.php` | View / edit one incident |
| `riddor_management.php` | Incident list |
| `riddor_matrix.php` | Incident overview grid |

![RIDDOR Matrix](../screenshots/fleet__riddor_matrix.png)

> There is a **second RIDDOR area** under [Health & Safety](../README.md) at `CRUD/hs/riddor/`. Worth confirming which is authoritative when that module is reviewed.

### `fors/compliance_report.php`
Pulls the above together into the FORS evidence report.

---

## Mileage — `mileage/`

![Mileage](../screenshots/fleet__mileage.png)

Tracks missing/recorded mileage per vehicle per month (`vehicle_mileage`, 24 rows).

| Page | Purpose |
|---|---|
| `mileage/mileage_management.php` | Overview |
| `mileage/mileage_recorder.php` | Record a month's mileage (units: miles / km / mins) |
| `mileage/mileage_detail.php` | Detail for one record (`?id=`) |

## PCNs — `pcn/`

![PCNs](../screenshots/fleet__pcn.png)

Penalty charge notices (`pcn_records`, plus `pcn_payments`, `pcn_appeals`, `pcn_statistics`).

| Page | Purpose |
|---|---|
| `pcn/pcn_management.php` | PCN list |
| `pcn/pcn_recorder.php` | Log a new PCN |
| `pcn/pcn_detail.php` | One PCN — payments and appeal status |

## Recalls — `recalls/`

![Recalls](../screenshots/fleet__recalls.png)

Manufacturer safety recalls (`vehicle_recall_checks`).

| Page | Purpose |
|---|---|
| `recalls/recalls_dashboard.php` | Recall status across the fleet |
| `recalls/record_recall_check.php` | Record a check (`?id_delivery_vehicle=`) |
| `recalls/recall_check_history.php` | Check history for a vehicle |

---

## Database

| Table | Rows | Notes |
|---|---|---|
| `delivery_vehicle` | 6 | The lorries — master record |
| `vehicle_document` | 66 | Certificates per vehicle |
| `vehicle_monthly_reading` | 142 | Monthly odometer readings |
| `vehicle_mileage` | 24 | Missing/recorded mileage by month |
| `vehicle_alert` | 16 | Reminders against a vehicle |
| `training_courses` / `employee_training_assignments` / `training_completions` | 20 / 33 / 21 | FORS driver training |
| `compliance_task_types` / `compliance_task_completions` | 12 / 0 | Recurring compliance jobs |
| `pcn_records` / `pcn_payments` / `pcn_appeals` | 0 / 0 / 0 | Empty locally |
| `riddor_incidents` / `riddor_compliance` | 0 / 0 | Empty locally |
| `vehicle_recall_checks` / `cpc_training_hours` | 0 / 0 | Empty locally |

> Several tables are empty in the local database. The pages render correctly regardless — but it does mean **local testing cannot exercise the populated states** of PCNs, RIDDOR, recalls or CPC.

---

## Known issues

Found during the review pass on 2026-07-21.

| # | Issue | Status |
|---|---|---|
| 1 | `mileage/mileage_recorder.php:277` raised **`Undefined array key "mileage_unit"`** on every GET. The `?? 'miles'` default guarded only the `in_array()` check; the ternary's true branch re-read `$_POST['mileage_unit']` unguarded, and since `'miles'` is a valid value the check always passed on a fresh page load. | **Fixed** — value read once into a variable, then validated. No other instances of the pattern exist in the codebase |
| 2 | **12 files run `ALTER TABLE` / `CREATE TABLE` on every page load**, wrapped in `try/catch` (4 in this module — `fleet_documents.php`, `fleet_documents_update.php`, `mileage_detail.php`, `mileage_recorder.php`). Schema changes therefore depend on someone visiting a page, and every request attempts a DDL statement. | **Open** — architectural; works, but worth converting to proper one-off migrations |
| 3 | `fleet_reports_api.php` (101 lines) is unreferenced. | **Open** — candidate for deletion |
| 4 | `map_ultra_low_emission_zone.php` (110 lines) is unreferenced and appears superseded by `map_ulez_zones.php` (243 lines). They are **not** duplicates — they differ substantially — so the orphan looks like an earlier, smaller version. | **Open** — confirm then delete |
| 5 | RIDDOR exists in **two** places: `CRUD/fleet/fors/riddor/` and `CRUD/hs/riddor/`. | **Open** — clarify which is authoritative when H&S is reviewed |

**Not** an issue: unlike Customers and Plant List, this module has **no near-duplicate clone pages**, and its parameter naming is consistent (`?id_delivery_vehicle=` throughout).
