# Asset Management Module

- **Date:** 2026-07-18
- **Status:** Requirements gathering
- **Status Date:** 2026-07-18
- **Phases:** 5 (provisional — will firm up once open questions are resolved)
- **Phases Complete:** 0
- **Notes:** Requirements gathering in progress. Build phases below are a provisional shape based on cloning the projects module patterns; do not start building until the open questions are answered.

## Plan Phases

0. Requirements gathering & data-model decision (in progress)
1. Database schema + migration (asset tables, field meta tables, EAV value tables, journal)
2. Backend endpoints (load/save/journal, `get_update_form.php?type=asset`, permission module)
3. List page + editor page (routes, templates, JS/CSS, shared field loop)
4. Spatial integration (OpenLayers editor map + map_v5 layer)

## Problem

GeoLynx has no way to record and manage physical assets. The business needs an Asset Management module covering **physical network infrastructure and plant** (poles, chambers, cabinets, ducts, cables, splice closures, equipment).

GeoLynx's USP is geospatial, so assets must be **spatial from day one**: every asset is either physically located (its own geometry) or **locatable via a parent asset that has a location** — e.g. a splice closure inside a chamber inherits the chamber's location. This implies a containment hierarchy (parent asset reference) alongside an optional geometry column.

The module must follow the design patterns established by the **projects module**: fields meta table (static + dynamic), shared editor field loop, journal/audit tables, two-tier RBAC. Longer-term goal: merge/share JS functions across editor modules rather than copying them, so new asset JS should lean on shared `main.js` functions wherever possible instead of wholesale-copying `project_edit_v2.js`.

> **Mapping stack note:** all map work is **OpenLayers** (`www/lib/ol/`) — `map_v5.js` and `project_edit_v2.js` are the patterns to follow. No other mapping library is in use.

## Requirements gathered so far (2026-07-18)

- **Scope:** mixed — physical network infrastructure and plant/equipment in one module.
- **Spatial:** from day one, following the projects module geometry pattern (`geom public.geometry`, British National Grid EPSG:27700 used widely elsewhere in the schema).
- **Location model:** direct geometry **or** inherited from a parent asset with a location (containment hierarchy → `parent_asset_id` style reference).
- **Relationships (confirmed so far):** projects (asset belongs to / installed under a project) and users/teams (assignment/custody). Further links to be discovered.
- **Patterns to follow:** projects module throughout — see reference table below.

## Proposed decisions (pending confirmation)

- **Openreach data is reference + attachment, not a base.** The `strategy.openreach_*` foreign tables are third-party (PIA-leased) infrastructure refreshed in bulk — not company assets. Seeding the register from them would import millions of rows the company doesn't own, which then go stale or vanish on the next Openreach refresh. Instead:
  - Openreach layers stay as map reference/context layers.
  - An asset can be **attached to** an Openreach structure (PIA scenarios: closure on their pole, cable in their duct) — store the Openreach object reference and **snapshot the coordinates at attach time**, so the asset's location survives Openreach data refreshes and removals.
  - This extends the parent-location model: an asset's location parent is either another GeoLynx asset **or** an Openreach structure reference.
  - Follow-up to verify: which Openreach identifier is stable across data refreshes (suitable as the stored reference).

## Open questions (resolve before Phase 1)

- [ ] **Single asset entity vs per-type field sets** — investigate real-world setup and use before deciding. Per-type field visibility would require extending the field meta system with a type/category filter; single-entity keeps the projects pattern intact. (Parked by user 2026-07-18.)
- [ ] **Additional entity links** beyond projects and users/teams (accounts? wayleaves? stocklists?) — discover during requirements. (Parked by user 2026-07-18.)
- [ ] **Asset type taxonomy** — canonical list of asset types; stored as admin dropdown options or a `field_options_source` whitelist table (needed if list views/audit queries consume the values)?
- [ ] **Geometry types** — points only, or also lines (cable/duct runs) and polygons (compounds)? Affects geometry column typmod and editor drawing tools.
- [ ] **Containment rules** — which asset types may contain which; nesting depth; does a child with its own geometry override the inherited location?
- [ ] **Schema name** — the baseline already has an empty `inventory` schema; reuse it or create a new `assets` schema (consistent with `projects`/`accounts` naming)?
- [ ] **Lifecycle model** — status set (planned / installed / in service / faulty / decommissioned?), install/decommission dates, maintenance/inspection history?
- [ ] **Serialised vs bulk** — is every asset individually tracked, or are some bulk quantities (e.g. cable drums)? Any relationship/overlap with the stocklists module?
- [ ] **Map surface** — assets as a layer in `map_v5` (admin_map_layers-managed?), an embedded editor map like `projectedit`, or both? Place/edit geometry from which surface?
- [ ] **Bulk import** — is there an existing asset register (spreadsheets, Openreach data) to import at go-live?
- [ ] **Autocomplete** — new `assetname` autocomplete type will be needed (for parent-asset linking and for other entities referencing assets); confirm helper-id conventions match existing types.

## Patterns to follow (projects module reference)

| Concern | Projects module | Asset equivalent (provisional) |
|---|---|---|
| Routes | `?do=projects`, `?do=projectedit` | `?do=assets`, `?do=assetedit` |
| Router entry | `global_functions.php::load_file()` | add asset routes (template, JS, CSS, nav) |
| Entity table | `projects.projects` (+ `geom public.geometry`) | `<schema>.assets` + `geom` + `parent_asset_id` |
| Field meta | `project_fields` (static + dynamic) | `asset_fields`, seeded static rows by migration |
| Dynamic values | 5 EAV tables `project_field_values_*` | `asset_field_values_text/int/numeric/date/boolean` |
| Dropdowns | `*_field_dropdown_options` / `field_options_source` | same, source whitelist entry if statuses consumed elsewhere |
| Form serving | `get_update_form.php?type=project` | add `type=asset` |
| Save | `project_save.php` (`$staticFields` whitelist, helper-key skip) | `asset_save.php` |
| Save helpers | `checkProjectEditField` / `...CurrentValue` / `...CurrentValueStatic` | `checkAssetEdit*` in `global_functions.php` |
| Journal | `project_journal_save.php` + history table | `asset_journal_save.php` + journal table |
| Autocomplete | `projectname` type, label resolution via `resolveAutocompleteLabel()` | new `assetname` type |
| Permissions | `getModulePermission(..., 'projects')` + item-level | new module key `assets` |
| Editor JS | `project_edit_v2.js` + shared `main.js` field loop / `buildAutocompleteField` | new `asset_edit.js`, maximise reuse of `main.js`; extract shared functions rather than copy |
| Map | OpenLayers (`www/lib/ol/`), patterns in `project_edit_v2.js` | same |

## Testing checklist

To be written once requirements are settled and phases are firm. Will follow the usual shape: migration applies cleanly on fresh baseline, admin fields page shows static rows read-only, dynamic field CRUD, save/journal round-trip, autocomplete label resolution, permission tiers, geometry create/edit on the map.

## Risks

- Building before the single-entity vs per-type decision is made could bake the wrong data model into a migration (migrations are immutable once applied).
- Containment/inherited location has no precedent in the codebase — needs a deliberate design, not a pattern clone.
- JS reuse goal cuts against the existing copy-per-module style; extracting shared functions from `project_edit_v2.js` risks regressions in the projects editor if not done carefully.
