> **SUPERSEDED — HISTORICAL RECORD ONLY (archived 2026-07-18).** This document is part of the original "superpowers" wayleave build, whose design diverged from the projects-module patterns it should have mirrored. Do NOT use it as a pattern source for wayleave or any new module. The wayleave module is being realigned to the projects module (meta-driven fields, OpenLayers, shared JS) — see CLAUDE.md.

# Wayleave Module — Design Spec
**Date:** 2026-04-20  
**Status:** Approved for implementation planning

---

## Overview

A full wayleave agreement management module for GeoLynx, structured to mirror the projects module (DDL layout, dynamic fields, file uploads, map, journal). The defining differentiator is a flexible **coverage model**: each wayleave agreement can define its premises scope via a drawn polygon, directly added UPRNs, and/or attached GeoLynx stocklists — in any combination. All three methods are resolved into a single unified premise list that drives all downstream functionality.

---

## Coverage Model

A wayleave agreement resolves its covered premises from three independent sources:

| Source | How it works |
|---|---|
| **Polygon** | A boundary drawn on the map. PostGIS spatial query (`ST_Within` / `ST_Intersects`) against the ABP/UPRNs layer derives matching UPRNs at query time (live, not snapshot). |
| **Direct UPRNs** | Individual UPRNs added manually via the premise list editor (reusing stocklist premise editor pattern). |
| **Stocklist** | A reference to an existing GeoLynx stocklist. UPRNs are resolved dynamically from the stocklist at query time, so stocklist changes are reflected automatically. |

The **resolved premise list** is the de-duplicated union of all three sources. All tabs (Premises, Releases, Projects) work from this resolved list. Source of each UPRN is tracked (polygon-derived / direct / stocklist) for transparency.

Multiple polygons, multiple direct UPRNs, and multiple stocklists may be attached to a single agreement simultaneously.

---

## Database Schema (`wayleave` schema — extends existing empty schema)

Mirrors `projects` schema structure exactly:

### Core
- `wayleave.agreements` — main entity (agreement_id, agreement_name, agreement_status_id, agreement_type, wayleave_team, bd_manager, ecd_date, signed_date, geom, parent_agreement_id, company_id, account_id, created_datetime, created_user, modified_datetime, modified_user)
- `wayleave.agreements_history` — full column mirror + history_action / history_datetime / history_user
- `wayleave.agreement_status` — lookup (id, desc): Draft, In Progress, On Hold, Signed, Complete, Cancelled

### Coverage
- `wayleave.agreement_uprns` — (agreement_id, uprn, added_user, added_datetime) — direct UPRNs
- `wayleave.agreement_stocklists` — (agreement_id, stocklist_id, added_user, added_datetime) — stocklist links
- `wayleave.agreement_polygons` — (id, agreement_id, geom, label, added_user, added_datetime) — all drawn polygon boundaries. `agreements.geom` is maintained as the convex hull / bounding union of all polygons for spatial indexing and map list views.
- `wayleave.agreement_polygon_uprns` — tracks every UPRN derived from polygon spatial joins, with approval state:
  - `id` bigserial PK
  - `agreement_id` integer
  - `uprn` bigint
  - `actioned_user_id` integer — user whose polygon change caused this UPRN to appear/disappear
  - `action_datetime` timestamp
  - `is_assigned` boolean — true if the UPRN currently falls within the polygon boundary
  - `is_approved` boolean — true if a user has explicitly confirmed this UPRN's in/out status

### Dynamic Fields (mirrors projects exactly)
- `wayleave.agreement_fields` — field definitions (field_id, field_section, field_category, field_sub_category, field_display_order, field_data_type, field_name, field_input_type, field_form_id, field_required, field_active, field_spacer_after, field_type)
- `wayleave.agreement_field_sections`
- `wayleave.agreement_field_category`
- `wayleave.agreement_field_sub_category`
- `wayleave.agreement_field_dropdown_options`
- `wayleave.agreement_field_values_boolean` + `_history`
- `wayleave.agreement_field_values_date` + `_history`
- `wayleave.agreement_field_values_int` + `_history`
- `wayleave.agreement_field_values_numeric` + `_history`
- `wayleave.agreement_field_values_text` + `_history`

### Supporting
- `wayleave.agreement_file_uploads` — (id, agreement_id, file_name, file_type, file_description, file_size, upload_user, upload_datetime, is_deleted, uuid)
- `wayleave.agreement_journal` — (id, agreement_id, user_id, log_datetime, log_text)
- `wayleave.agreement_releases` — (id, agreement_id, uprn, release_date, sales_ref, pic_ref, released_user, released_datetime)

---

## Editor Tabs (`?do=wayleaveedit`)

Mirrors the project editor structure:

| Tab | Content |
|---|---|
| **Main Details** | Agreement name, type, status, wayleave team, BD manager, ECD/signed dates, parent agreement link, dynamic fields |
| **Coverage** | Three panels: Draw/manage polygons on embedded Leaflet map (Geoman); Add/remove direct UPRNs (premise list editor); Attach/detach GeoLynx stocklists |
| **Premises** | Resolved premise list (Tabulator.js) showing all UPRNs from all sources, with source indicator column (Polygon / Direct / Stocklist name). Count summary at top. |
| **Land Registry** | Link freeholder/leasehold accounts from Land Registry module |
| **Files** | Upload and manage documents (signed agreements, handover packs) — mirrors project file uploads |
| **Projects** | List of GeoLynx projects that share at least one UPRN with this agreement's resolved premise list (UPRN join against `projects.project_premises`) |
| **Releases** | Track individual premise release dates, sales ref, PIC ref |
| **Map** | Full-width Leaflet map showing agreement polygon(s), all resolved premises as points, release status overlay |
| **Journal & Audit Log** | Timestamped log of all field changes + free-text notes |

---

## List View (`?do=wayleave`)

Tabulator.js table replacing the current static HTML table. Columns: Agreement Name, Type, Team, BD Manager, Status, ECD Date, Signed Date, Premise Count (from resolved list). Searchable, sortable, filterable by status/team. "New Agreement" button opens a creation modal (name + type, then redirects to editor).

---

## Backend PHP Endpoints (`www/fn/`)

| File | Purpose |
|---|---|
| `wayleave_list_load.php` | Paginated list for Tabulator with resolved premise counts |
| `wayleave_load.php` | Load single agreement main details |
| `wayleave_save.php` | Save main details + trigger journal entry |
| `wayleave_journal_save.php` | Add journal entries |
| `wayleave_coverage_polygon_save.php` | Add/update/delete polygon geometries, then runs PostGIS spatial join and writes pending changes to `agreement_polygon_uprns` |
| `wayleave_coverage_polygon_uprns_load.php` | Returns pending (unapproved) UPRN adds/removes for the Coverage tab confirmation UI |
| `wayleave_coverage_polygon_uprns_approve.php` | Sets `is_approved=true` on confirmed UPRNs, or marks rejected ones as `is_assigned=false, is_approved=false` |
| `wayleave_coverage_uprn_save.php` | Add/remove direct UPRNs |
| `wayleave_coverage_stocklist_save.php` | Attach/detach stocklists |
| `wayleave_premises_load.php` | Return resolved premise list (union of all three sources) |
| `wayleave_file_upload.php` | Handle file uploads |
| `wayleave_files_load.php` | List uploaded files |
| `wayleave_releases_load.php` | Load releases for agreement |
| `wayleave_releases_save.php` | Save/update releases |
| `wayleave_projects_load.php` | Spatial/UPRN join to find linked projects |
| `wayleave_field_values_load.php` | Load dynamic field values |
| `wayleave_field_values_save.php` | Save dynamic field values + history |
| `wayleave_landreg_load.php` | Load linked land registry accounts |
| `wayleave_landreg_save.php` | Link/unlink land registry accounts |
| `wayleave_map_load.php` | GeoJSON for map tab (polygons + resolved premises) |

---

## Frontend JS/CSS

- `www/js/wayleave_list.js` — list page (Tabulator, search, new agreement modal)
- `www/js/wayleave_edit.js` — editor (tab management, all tab logic, Leaflet/Geoman for Coverage and Map tabs)
- `www/css/wayleave_edit.css` — editor styles
- `www/css/wayleave_list.css` — list styles

---

## Router (`global_functions.php`)

Existing routes `wayleave` and `wayleaveedit` already registered. JS and CSS file references need updating to point to the new files above.

---

## Key Design Decisions

1. **Polygon resolution is snapshot with approval workflow, not live.** When a polygon is drawn or modified, a PostGIS spatial join runs immediately and writes results into `agreement_polygon_uprns`. The four possible states are:

   | `is_assigned` | `is_approved` | Meaning |
   |---|---|---|
   | `true` | `false` | In polygon, pending approval to add |
   | `true` | `true` | In polygon, active in wayleave |
   | `false` | `true` | Left polygon but was previously approved — pending removal confirmation |
   | `false` | `false` | Not in polygon, confirmed out / never approved |

   When a polygon changes: new UPRNs entering the boundary are inserted as `is_assigned=true, is_approved=false`. UPRNs that were `is_assigned=true, is_approved=true` (active) but no longer fall within the boundary are updated to `is_assigned=false, is_approved=true` — preserving the fact they were once confirmed and flagging them for explicit removal review. The user must approve both additions and removals. The resolved premise list only includes polygon-derived UPRNs where `is_assigned=true AND is_approved=true`. This provides a full audit trail and avoids surprise premise count shifts on page load.

2. **Stocklist resolution is live.** The `agreement_stocklists` table stores a reference, not a copy. Premises tab queries the stocklist's current resolved premise list at load time.

3. **Direct UPRNs are the override mechanism.** Properties outside a polygon but inside the agreement (e.g. outlying units) can be added individually. Properties inside the polygon but excluded can be handled via a future exclusion list (out of scope for v1).

4. **Dynamic fields mirror projects exactly.** Same table structure, same PHP pattern, same frontend rendering. This allows future admin tooling to manage both project and wayleave fields from a single interface.

5. **Parent agreement link** on `agreements` supports the header/child pattern for large landowners (e.g. national housing association as parent, individual estate agreements as children) without requiring a separate entity type.

---

## Out of Scope (v1)

- UPRN exclusion list (polygon-in but excluded)
- Agreement-to-agreement comparison / overlap detection
- Public-facing wayleave status portal
- Bulk import of agreements from CSV
- E-signature integration
