# Gap Analysis — Master Spec vs Current Portal

**Date:** 2026-07-17  
**System:** AARJAY GDP Compliance Portal (`mvc/`)  
**Method:** Code + schema inspection (not a claim of GDP compliance)

Software cannot make AARJAY “MHRA approved”. This document only compares the Master Spec to what the portal currently implements.

---

## Executive verdict

The portal already has many GDP-related **screens and tables**, but several Master Spec non-negotiables are **not met**:

| Non-negotiable | Current state | Risk |
|---|---|---|
| No hard delete of GDP records | **Done through Phase 0i:** SoftLifecycle void on QMS + temp/batches + commercial + PR/contracts/invoices + Users + Partner Categories + **Customers (0h) + legacy supplier_categories (0i)**. | — |
| Single Contractor Master → site assignment | **Done (1p + 1ar + 1as):** Site Profile pest/fire pickers → `site_contractor_assignments`; free-text NULLed (`148`) then DROPped (`150`). | — |
| Product Master ≠ Stock | **Done (1i/1q/1ba):** Qty from `stock_batches` only; denormalized `products.current_stock` **DROPped** (`241`). | — |
| Training Matrix ≠ Training Record | **Done (2e + 2aa):** role×site catalog/rules; Matrix status workflow + Record varchar statuses; Signoff e-sign stays separate. | Keep Matrix ≠ Record ≠ Signoff distinct |
| E-sign attributable + bound to record/version | **Done (1j–1o):** `EsignService` on SOP/TE/Qual/PR/Validation Pack/CAPA/CC + Deviation/Complaint/Recall/Doc Control + Audit/Risk/MHRA/Falsified/Defective + Returns close. | — |
| TA responsibility matrix AARJAY/Counterparty/Shared/N/A | **Done (Phase 2):** structured matrix rows on TA (`TechnicalAgreementResponsibility`). Legacy summary fields remain as narrative. | — |
| Mapping not claimed as universal MHRA 6-month rule | Scheduling exists; wording must stay SOP/risk-based | Misleading compliance claim |
| Partner Category save/reload | **Done (Phase 0):** party map preferred; legacy synced by slug; CLI test green (`scripts/tests/partner_category_test.php`). | — |
| Returns contractor party FK | **Done:** `returns.contractor_id` + selector for contractor/3PL returns | — |
| Controlled relationships | **Done (3e batches + 3t NS + 3u TE + 3v TR):** `site_id` → `site_profiles` on stock_batches, non_saleable_stock, temperature_excursions, temperature_reviews; UI pickers; legacy `site` snapshot retained. | Optional DROP of snapshot varchar later |

---

## Phase 0 — Repair risks (priority)

### P0-1 Hard delete → Void/Supersede
**Existing problem (historical):** Controllers for deviations, CAPAs, recalls, complaints, audits, SOPs, change controls, risk assessments, falsified medicines, defective stock, document controls, form templates, temperature logs still hard-delete.  
**GDP risk:** Loss of evidence; cannot reconstruct history for MHRA.  
**Solution:** Soft lifecycle (`void` / `superseded` / `obsolete`) + mandatory reason + audit event; retain row.  
**Status:** **Done** through Phase 0i — SoftLifecycle on QMS core + temp/batches + commercial + PR/contracts/invoices + Users + Partner Categories + Customers (0h) + legacy supplier_categories (0i). Notification soft dismiss via `is_dismissed` (Phase 3at / `152`). AI Assistant threads (`154`) + Recall Finder runs (`155`) + QR scan logs (`160`) SoftLifecycle void. Residual thin helpers (Search/Report) have no GDP lifecycle.

### P0-2 Partner Category end-to-end
**Existing problem (historical):** Dual maps (`supplier_category_map` → `supplier_categories` vs `supplier_party_category_map` → `party_categories`). Sync swallows FK errors. Edit reload falls back to legacy IDs that do not match party category checkboxes.  
**Acceptance (Master Spec §5.3):** filter, save, reload, edit, list/export, reject invalid IDs, migrate data, audit, automated test.  
**Status:** **Done (Phase 0)** — party map preferred; legacy synced by slug; CLI test green (`scripts/tests/partner_category_test.php`). Legacy `supplier_categories` status workflow + SoftLifecycle void (0k / 0i).

### P0-3 Returns party / contractor FK
**Existing problem (historical):** Contractor return type without `contractor_id`; party selectors incomplete.  
**Status:** **Done** — `returns.contractor_id` + selector for contractor/3PL returns.

### P0-4 Permissions / uploads / installer / migrations
Partially addressed in migrations 001–002; still need continuous audit of upload paths and undefined controller methods. Not closed.

### P0-5 KPI integrity
Dashboard queries already exclude `void` in places; must stay consistent after void rollout. **Ongoing** — void rollout complete for GDP-critical modules.

---

## Phase 1 — Core masters (planned)

| Spec area | Current | Gap |
|---|---|---|
| Company vs Site | `company_profiles`, `site_profiles` | **Done (1s + 1u + 1y + 1bb–1bg):** WDA licence conditions + covered sites; site GDP activity multi-select; WDA + Site Profile status workflow; company wizard + type; company hub; contractor scope_mode; company/site documents SoftLifecycle. |
| Party / contacts | Customers & suppliers separate; contact directory exists | **Done (1t + 1ae + 1am):** `party_contacts` SoftLifecycle void + `status_changed_at` + empty TRANSITIONS (create=active). Comm logs (**1an**) + accounts comm (**3am**) open→waiting_response→closed. Single Party PK merge still optional. |
| Contractor ↔ Site | Free-text on site; contractor master exists | **Done (1p + 1ar + 1as):** Site form pest/fire → assignments; free-text NULLed (`148`) then DROPped (`150`). |
| Product / storage profile | Product form + categories | **Done (1r + 1v):** Storage Profile CRUD/void; Product requires active profile; FK; nav under Products; **status workflow** active↔inactive. |
| Staff / multi-site roles | Users + training | **Done (1e/1h→1h+++++):** Auth site scope across QMS + SOPs/training/equipment (`028`–`032`). |
| Evidence / Tasks / Signatures | Evidence module exists; tasks fragmented | **Done (1e/1f+/1g/1g+/1h):** Evidence panels; workflow tasks + auto-emit from QMS (`033`); My Tasks site-filtered; SOP signature binding. |
| Evidence / Tasks / Signatures | Evidence module exists; tasks fragmented | **Done (1e/1f+/1g/1j–1n):** Evidence panels; workflow tasks; SOP signature binding; `EsignService` on RP/QMS close-approve gates across core + audit/risk/MHRA/falsified/defective. |

---

## Phase 2 — Quality & Training (planned)

| Spec area | Gap |
|---|---|
| SOP version tree + historic import | **Done (2f):** content hash, supersedes chain, issue-new-version + re-sign queue, historic CSV import (`/sop/import`) with dry-run; history defaults to superseded. |
| Training Matrix vs Record | **Done (2e):** Catalog + role×site rules + Apply Rules → matrix rows with `requirement_id`/`source_rule_id`; FK links, create-record-from-matrix, auto signoff for SOP requirements, soft void. |
| Technical Agreements matrix | **Done (UI):** structured AARJAY/Counterparty/Shared/N/A rows on TA view. |
| Deviation / CAPA / CC / Risk / Complaint / Recall / Audit / Falsified / Defective / Non-saleable / Doc Control / TE / MHRA / SOP / TA / Form Template / Mapping / Review / Training | **Done (2b + 2g–2w + 2x–2y + 2aa + 3t):** CAPA + Deviation + Change Control + Risk + Complaint + Recall + Internal/Supplier Audit + Falsified + Defective + **Non-saleable** + Document Control + Temperature Excursion + MHRA Inspection + **SOP** + **Technical Agreement** + **Form Template** + Temperature Mapping + Temperature Review + **Training Matrix/Records** status workflow engines. |
| Complaints / Returns / Quarantine | **Done (2b + 2j + 2l):** Complaint party/batch + Raise CAPA + **status workflow**; Returns quarantine/release/dispose sync via stock movements + status workflow + SoftLifecycle void. |
| Premises Checks / Cleaning / Pest / Fire | **Done (3c + 2z):** site_id FKs + SoftLifecycle void + **shared status workflow** (`SiteCompliance::TRANSITIONS`; e-sign on close). |
| Premises Equipment / assets | **Done (3j):** equipment_assets status workflow (qualification→active→maintenance/broken→removed/sold/replaced); optional e-sign on removed/sold; hard delete disabled. |
| Goods Receiving / Dispatch / Purchase Request / Sales Order | **Done (3f–3i):** status workflow engines + view Advance; e-sign on GRN accept + dispatch release; stock side-effects preserved; SO safe steps only (dispatch/invoice still drive mid/late statuses). |
| Credit Notes / Customer Invoices / Supplier Invoices / Contractor Invoices | **Done (3l–3n + 3s):** CN + customer/supplier/contractor commercial advance; SoftLifecycle cancel on contractor; Accounts payment posting untouched. |
| Asset Purchases / Accounting Integrations / Temperature Logs | **Done (3aa–3ac):** Temp log active/void SoftLifecycle (`107`); Accounting Integration status advance + form lock (`108`); Asset Purchase draft→capitalised|expensed|sent_to_accountant→disposed + optional e-sign dispose (`109`). |
| Account Reopening / Statements / Email Templates | **Done (3ad–3af):** Reopening open→…→closed + e-sign approve/close (`117`); Statement draft→generated→sent + SoftLifecycle void (`118`); Email Template draft→active↔inactive→superseded (`119`, no void). |
| Purchase Orders / Contracts / Deliveries (legacy) | **Done (3p–3r):** PO draft→submitted→acknowledged + cancel early + GRN/Delivery receive; Contract draft→active→expired|terminated|renewed + e-sign activate/terminate; Delivery forward logistics + PO sync; SoftLifecycle void. |
| Workflow Tasks | **Done (1w):** open→in_progress→completed; cancel branches; SoftLifecycle void; no e-sign on complete. |
| Stock Batch / Batch register | **Done (3o):** status workflow gates (available↔quarantined; →damaged/dead_stock/expired); e-sign on quarantine release; qty changes stay via `recordMovement`; SoftLifecycle void. |
| Production Releases | **Done (4f):** draft→pending_rp→approved\|rejected→released; e-sign on RP decision + released; SoftLifecycle void. |

---

## Phase 3 — Operations & Premises (planned)

Stock transfer, Recall Trace vs Recall case, assets/calibration, mapping vs monitoring, cold-chain API — tables/UI stubs exist; live API hardening incomplete. Premises/Cleaning/Pest/Fire status workflows done (Phase 2z).

**Phase 3a (2026-07-18):** Stock Transfer hardened (site FKs, qty guard, workflow, void). Excursion→`equipment_assets` join fixed. Mapping UI notes risk/SOP-based remapping (not universal MHRA 6-month).

**Phase 3b (2026-07-18):** Recall Finder↔case linkage + results JSON + duplicate guard + Raise CAPA. Calibration certificate upload via UploadHelper. Remaining: cold-chain API (flagged off).

**Phase 3c (2026-07-18):** Premises/Cleaning/Pest/Fire `site_id` → `site_profiles`; SoftLifecycle void + `void_reason`; contractor pickers prefer `site_contractor_assignments` by service type.

**Phase 3d (2026-07-18):** Cold-chain imports: CSV file upload (`UploadHelper`), `source_file_path`, void, feature flag ON locally. API stub blocked (no live HTTP); credentials remain for future connector only.

**Phase 3e (2026-07-18):** `stock_batches.site_id` → `site_profiles` FK cutover; UI site picker; GRN + Stock Transfer receipt write `site_id`; legacy `site` varchar retained as synced snapshot (`migrations/056_phase3e_stock_batch_site_fk.sql`).

**Phase 3t / 3u / 3v (2026-07-18):** Non-saleable stock status + `site_id` (`094`); Temperature Excursion / Review `site_id` FK cutovers (`095`/`096`); TE disposition engines preserved.

**Phase 3f–3i (2026-07-18):** Commercial/ops status workflows — GRN (`072`), Dispatch (`073`), Purchase Request (`074`), Sales Order (`075`); e-sign on GRN accept + dispatch release; stock create/deduct side-effects preserved.

**Phase 3l–3n (2026-07-18):** Finance doc status workflows — Credit Notes (`082`), Customer Invoices (`083`), Supplier Invoices (`084`); SoftLifecycle void on CN; e-sign only when CN GDP-linked via returns; invoice payment posting remains Accounts-only.

**Phase 3s / 1x / 1y (2026-07-18):** Contractor Invoice (`091`) commercial advance + SoftLifecycle cancel; Qualification documents (`092`) active→superseded + e-sign / SoftLifecycle void; Site Profile (`093`) active↔under_review↔inactive (no void).

**Phase 1z / 1aa / 1ab (2026-07-18):** External Certificate (`097`) valid→expiring→expired / →superseded (optional e-sign); Evidence (`098`) active→superseded + SoftLifecycle void; Product Master (`099`) active↔inactive→discontinued + SoftLifecycle void (stock batch-derived).

**Phase 3w / 1ac / 3x (2026-07-18):** Consignment documents (`100`) issued→superseded + SoftLifecycle void; Company Profile (`101`) active↔inactive + form lock (no void); Stock Transfer (`102`) `status_changed_at` harden.

**Phase 3y / 3z / 0h / 0i (2026-07-18):** Premises Equipment (`103`) + MHRA Inspection (`104`) `applySiteId` site snapshot sync (picker required, no free-text); Customer SoftLifecycle ensure (`105`); legacy `supplier_categories` soft void (`106`).

**Phase 3aa / 3ab / 3ac (2026-07-18):** Temperature Log (`107`) active/void SoftLifecycle; Accounting Integration (`108`) status advance + form lock (no e-sign); Asset Purchase (`109`) draft→capitalised|expensed|sent_to_accountant→disposed + SoftLifecycle void + optional e-sign dispose.

**Phase 1ad / 0j / 2ab / 1ae (2026-07-18):** Site–Contractor Assignment (`110`) active→suspended|expired|terminated + SoftLifecycle void; Partner Category (`111`) active↔inactive; Training Requirements catalog (`112`) active↔inactive (Matrix ≠ Record ≠ Signoff); Party Contact (`113`) `status_changed_at` + SoftLifecycle timestamp bump.

**Phase 1af / 1ag / 1ah (2026-07-18):** User (`114`) active↔inactive + SoftLifecycle void + form advance; User–Site Assignment (`115`) active↔suspended + SoftLifecycle void (multi-site sync kept); Supplier (`116`) active↔inactive + SoftLifecycle void + view advance; partner category maps untouched.

**Phase 3ad / 3ae / 3af (2026-07-18):** Account Reopening (`117`) open→under_review→rp_review→approved|rejected→closed + e-sign approve/close + SoftLifecycle void; Account Statement (`118`) draft→generated→sent + SoftLifecycle void; Email Template (`119`) draft→active↔inactive→superseded (no void — not in enum).

**Phase 3ag / 3ah / 1ai / 3ai (2026-07-18):** Data Import (`120`) draft→validated|failed→imported + SoftLifecycle void (pipeline intact); Secure Credential (`121`) placeholder→configured↔rotation_due→disabled|revoked + soft revoke (secrets never logged); Supplier Licence (`122`) status enum + active↔inactive + SoftLifecycle void; Outbound Communication (`123`) draft→queued→sent|failed + SoftLifecycle void.

**Phase 1al / 2ac / 4g / 2ad / 4h (2026-07-18):** Transport Provider (`130`) approval_status workflow + SoftLifecycle void; SOP Signoff (`131`) pending→completed + overdue↔pending + SoftLifecycle void (e-sign kept); Validation Pack (`132`) `status_changed_at` harden; Role×Site Rules (`133`) active↔inactive; Inspection Pack (`134`) `status_changed_at` harden.

**Phase 1am / 1an / 3am (2026-07-18):** Party Contact (`135`) active/void + empty TRANSITIONS + SoftLifecycle void; Communication Log (`136`) open→waiting_response→closed + list advance; Accounts Communication Log (`137`) same path + list advance.

**Phase 4i / 3an / 3ao / 3ap (2026-07-18):** AI Report Draft (`138`) draft→reviewed→approved + SoftLifecycle void; Email SMTP settings (`139`) not_configured→configured↔disabled + form lock (passwords never logged); Backup Register (`140`) scheduled→completed|failed→verified; Developer Access (`141`) pending→active→expired + SoftLifecycle revoke.

**Phase 1ao / 1ap / 3aq / 1aq (2026-07-18):** Tax Category (`142`) active↔inactive + SoftLifecycle void + form lock (VAT line calc untouched); Qualification checklist items (`143`) ITEM_TRANSITIONS + `advanceChecklistItemStatus` + `status_changed_at` (verify/receive side effects kept, no e-sign on verify); Premises Checks (`144`) `applySiteId` snapshot sync + FK (cleaning/pest/fire already site_id-only); Emergency Contacts (`146`) `applySiteId` snapshot sync + FK (no status column — SoftLifecycle skipped).

**Phase 3ar (2026-07-18):** Legacy Invoice (`145`) draft→pending→paid + overdue branch; SoftLifecycle void; form lock + view advance; `status_changed_at` (supplier/customer invoices untouched).

**Phase 2ae / 1ar / 4j / 1as (2026-07-18):** Training Matrix (`147`) `applySiteId` site snapshot sync + FK; Site pest/fire free-text cleared (`148`) then DROPped (`150`); Gap Analysis items (`149`) `status_changed_at` only (RAG colours, not forward workflow).

**Phase 3as / 3at (2026-07-18):** Search index `site_id` (`151`) for site_profiles filter; Notification soft dismiss (`152`) — `is_dismissed` + optional reason + `dismissed_at`; hard delete blocked.

**Phase 4l / 3au (2026-07-18):** AI Assistant threads (`154`) active/void SoftLifecycle + empty TRANSITIONS + void UI; Recall Finder runs (`155`) active/void SoftLifecycle — list excludes void; linkRecall/raise recall unchanged for active runs.

**Phase 3ax (2026-07-18):** QR Trace scan logs (`160`) active/void SoftLifecycle + empty TRANSITIONS + void UI on history; list excludes void; scan INSERT recording unchanged (default active); feature-flag `experimental_qr_labels` unchanged.

**Phase 1aj / 1ak / 0k (2026-07-18):** Customer (`124`) account_status active↔inactive + SoftLifecycle void + form/view advance; Electronic Signature profiles (`125`) active→expired + SoftLifecycle revoke (auth still active-only); legacy Supplier Category (`126`) active↔inactive + `status_changed_at` (Party Category UI unchanged).

**Phase 3aj / 3ak / 3al (2026-07-18):** Email Reminder (`127`) queued→dry_run|sent|failed + SoftLifecycle cancel (no SMTP); Export History (`128`) generated→downloaded|sent|failed + SoftLifecycle void; Scheduled Job (`129`) active↔paused→disabled + `status_changed_at` on jobs/runs.

**Phase 3p–3r (2026-07-18):** Legacy procurement status workflows — Purchase Orders (`088`) draft→submitted→acknowledged + cancel early + GRN/Delivery receive statuses; Contracts (`089`) draft→active→expired|terminated|renewed + e-sign activate/terminate; Deliveries (`090`) forward logistics path + PO receive sync; SoftLifecycle void on all three.

**Phase 1u / 3j / 1v (2026-07-18):** WDA Licence (`076`), Premises Equipment (`077`), Storage Profile (`078`) status workflows; e-sign on WDA suspend/expire; optional e-sign on equipment removed/sold.

**Phase 2aa (2026-07-18):** Training Matrix/Records status workflows (`079`); e-sign on matrix/record complete; Signoff path unchanged.

**Phase 3k (2026-07-18):** Cold-chain Import status workflow (`080`) — draft→processed|failed; CSV path preserved; live API still blocked.

**Phase 4e (2026-07-18):** GDP Gap Analysis status workflow (`081`) — draft→final + e-sign finalize guards.

**Phase 1w / 3o / 4f (2026-07-18):** Workflow Task (`085`), Stock Batch (`086`), Production Release (`087`) status workflows; e-sign on batch quarantine release + PR RP/released; qty stays on `recordMovement`.

Phase 3 Ops/Premises core slice complete for current plan. Remaining optional: live cold-chain API connector (when vendor endpoint known); DROP legacy free-text columns after settlement.

---

## Phase 4 — Inspection intelligence (planned)

GDP Gap Analysis engine, MHRA workspace, inspection pack, validation pack, RP release — early modules only.

**Phase 4a (2026-07-18):** Inspection packs hardened — MHRA inspection FK, evidence readiness %, status transitions, soft void, Raise Pack from MHRA view, export disclaimer (not MHRA approval).

**Phase 4b (2026-07-18):** GDP Gap Analysis depth — real module metrics (not table stubs), soft void + finalize, site/MHRA FKs, deep-links, export disclaimer.

**Phase 4c (2026-07-18):** Validation Packs — URS→IQ/OQ/PQ-UAT evidence register, Change Control link, RP approval gate, soft void.

**Phase 4d (2026-07-18):** Production Releases — RP-controlled production release gate (validation pack required, checklist, approve/reject/released). Phase 4 planned track complete for current plan.

**Phase 4e (2026-07-18):** GDP Gap Analysis status workflow engine — `TRANSITIONS` + `advanceStatus` + e-sign on finalize (`migrations/081_phase4e_gdp_gap_analysis_status_workflow.sql`).

**Phase 4f (2026-07-18):** Production Release status workflow engine — `TRANSITIONS` + `advanceStatus` + e-sign on RP approve/reject/released (`migrations/087_phase4f_production_release_status_workflow.sql`).

---

## Explicit non-claims

- This gap analysis does **not** certify GDP compliance.
- Passing a screen open test does **not** mean a module is complete.
- Temperature mapping every six months (if configured) is an **AARJAY SOP choice**, not a universal MHRA rule. GDP requires initial mapping under representative conditions and repetition based on risk / significant change ([EUR-Lex 2013/C 343/01](https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=oj%3AJOC_2013_343_R_0001_01)).

---

## Deliverables tracking (Master Spec §17)

| # | Deliverable | Status |
|---|---|---|
| 1 | Gap analysis | This file |
| 2–6 | ER / linkage / permissions / workflows / migration plan | Partial — Phase 0 migration `012` starts foundation |
| 7 | Existing-data migration report | Partner category remap in `012` |
| 8–20 | UI system, tests, validation, guides, release | Not started |

---

## Next concrete build steps

**GDP settlement COMPLETE** through floor **244** (SoftLifecycle, site_id, `products.current_stock` DROP, `install.sql` regenerated from live + fresh-import verified).

**Product UX pack 256–265 (2026-07-18):** Technical Agreements bidirectional (direction, multi-site coverage, generate/issue path, SoftLifecycle attachments, inbound RP review); SOP acknowledge + change pack + obsolete status + attachments; Communication reason catalog + mailto/WhatsApp compose links with required summary.

**Forms & GDP reports 266–270 (2026-07-18):** Forms & Templates ops categories + `template_code` / `template_fields_json` + issued blank-copy log (`268`); seed ops blanks (`267`); eight GDP operational reports with CSV (`269`–`270` marker). SoftLifecycle on templates unchanged.

**Phase 1bb–1bg (2026-07-18):** Company & Sites redesign (`250`–`255`) — company wizard + `company_type`; legal fields; company hub; site company FK; contractor `scope_mode` + junction SoftLifecycle; `company_documents` / `site_documents` SoftLifecycle multi-attach. SystemHealth floor **255** (later raised with product packs).

Outside settlement only:

1. **Cold-chain live API** — vendor blocked; CSV path remains the supported import.
2. **Optional deliverables 8–20** — UI system polish, tests, guides, release packaging (Master Spec §17) — separate program.
3. **Deploy checklist only** (not settlement gaps): production URL, HTTPS/HSTS, default admin password — see `MASTER_SPEC.md` deploy note + SystemHealth warns.
