Patrol Reference Public

Research & Evidence

The verification trail behind the ERPat Patrol guide: the actual controllers, models, database tables, migrations, cron jobs, config files, and public endpoints each documented claim was checked against.

Guide version: r1 Module version: 2.0.0 Updated: 2026-07-22 Estimated time: 11 min 2 views
Verification & Provenance

Research & Evidence

This page is the audit trail behind the rest of the guide. Every feature, screen, permission, table, and automated job described elsewhere in this documentation was verified against the live module code under modules/Patrol/ before it was written up. Below is the inventory of the actual files that were read, with short notes so a reviewer, administrator, or auditor can trace any claim in the guide straight back to a real file — and confirm the guide does not describe anything the module does not actually do.

Why this page exists. Security documentation is only useful if it is truthful. Rather than ask you to take the guide on trust, this page names the source files behind each section. If you administer or audit this deployment, you can open the same files and confirm every statement. If something in another page ever seems to disagree with the running system, the code is the source of truth — verify against these files.

How this guide was verified

The guide was written after reading the module's real source, not from a specification or a wishlist. The evidence was gathered in this order:

  1. Read the manifest (module.json) and the six configuration sidecars to establish the module's identity, menu, permissions, routes, and audit registrations.
  2. Read all fifteen controllers to confirm each feature area's screens, actions, and permission gates.
  3. Read the models — especially the shared integrity base — to confirm reference numbering, versioning, the tamper-evident hash chain, and privacy behaviour.
  4. Read every migration to confirm the database tables actually exist, and cross-checked the count against module.json's database.tables list.
  5. Read the six cron job classes to confirm each schedule and what each job actually does.
  6. Confirmed the one place Patrol touches ERPat core (a single notifications migration) and the helpers it relies on.

Result: 15 controllers, 37 models, 12 module migrations plus 1 core migration, 6 cron jobs, 6 config sidecars, and 2 helpers — all present and accounted for.

Module identity

The module announces itself through its manifest and module_config.php. These values decide how it appears in Settings → Manage Modules and which enable-toggle gates the whole feature.

Manifest & identity — verified in modules/Patrol/module.json and config/module_config.php
FactValue
Manifest slugpatrol
Version2.0.0
CategorySecurity
Iconfa-shield
NamespaceModules\Patrol
Enable toggle (setting)module_patrol
Declared tables39 patrol_* tables (matches the migrations)
Migration tracking tablemigrations_patrol (run by php erpat migrate:modules)

Controllers verified

Each Patrol screen is backed by one controller. All staff controllers extend App_Controller and gate themselves on the module toggle plus an area permission; the single public controller extends App\Core\GuestController and carries no session. The table below is the evidence for the feature areas documented across Daily Operations and Data & Screen Reference.

Controllers — files under modules/Patrol/controllers/
FileArea permissionCovers
Patrol.phppatrol_dashboardSecurity dashboard landing page
Patrol_logbook.phppatrol_logDigital activity log / blotter (EVT-…)
Patrol_visitors.phppatrol_visitorCheck-in/out, invites (QR), passes (VIS-…)
Patrol_incidents.phppatrol_incidentIncident register, evidence, custody, people, tasks (INC-…)
Patrol_shifts.phppatrol_shiftShifts, roster, guard licenses, "On Duty Now"
Patrol_handovers.phppatrol_handoverShift endorsements (HND-…)
Patrol_routes.phppatrol_routeRoutes & checkpoints (scan codes + token hashes)
Patrol_rounds.phppatrol_runPatrol run execution & observations (RUN-…)
Patrol_assets.phppatrol_assetEquipment register, issue & return
Patrol_reports.phppatrol_report_exportActivity CSV & incident-register PDF exports
Patrol_settings.phppatrol_locationLocations admin: sites, zones, gates, posts
Patrol_public_reports.phppatrol_public_report_triageStaff triage of anonymous public reports (PSR-…)
Patrol_governance.phppatrol_retentionRetention, legal holds, DSAR, disclosures
Patrol_ai.phppatrol_aiHuman-in-the-loop AI assist (ships disabled)
Patrol_report.phppublic — no loginAnonymous public reporting & tracking form

Models verified

Thirty-seven models sit under modules/Patrol/models/. The most important one is the shared integrity base every trustworthy record inherits from.

The integrity base

Patrol_base_model.php is where reference numbering, append-only versioning, the tamper-evident hash chain, and offline idempotency actually live. The activity-log, incident, and handover models extend it — so the guide's "you can't secretly edit a record" claims are grounded here, not asserted.

One model per record type

Visits, invites, passes, evidence, custody, incident people/actions/tasks/links, guards, licenses, shifts, assignments, routes, checkpoints, runs, scans, observations, assets, public reports, retention policies, legal holds, privacy requests, disclosures, and AI jobs/outputs each have a dedicated model.

No forked queries

The Reports controller and the AI feature reuse the activity and incident models rather than writing their own SQL, so exports and analysis always see the same records as the live screens.

Database tables & migrations

The module ships twelve idempotent, reversible migrations, tracked in migrations_patrol. Together they create the 39 patrol_* tables the manifest declares. This table is the evidence that every screen and data field mentioned in Data & Screen Reference is backed by a real table.

Module migrations — files under modules/Patrol/migrations/
MigrationTables created
…114114_create_patrol_location_tablespatrol_sites, patrol_zones, patrol_gates, patrol_posts (seeds "Main Site" / MAIN)
…114121_create_patrol_activity_log_tablepatrol_activity_logs
…114128_create_patrol_visitor_tablespatrol_visits, patrol_visit_events, patrol_visitor_invites, patrol_visitor_passes
…114135_create_patrol_incident_tablespatrol_incidents, patrol_incident_people, patrol_incident_actions, patrol_incident_links
…114142_create_patrol_evidence_tablespatrol_evidence, patrol_evidence_custody
…130811_create_patrol_shift_tablespatrol_guards, patrol_guard_licenses, patrol_shifts, patrol_shift_assignments, patrol_handovers, patrol_handover_items
…133734_create_patrol_route_tablespatrol_routes, patrol_checkpoints, patrol_route_schedules, patrol_runs, patrol_scans, patrol_observations, patrol_devices
…135651_create_patrol_asset_tablespatrol_assets, patrol_asset_assignments
…140853_create_patrol_incident_task_tablepatrol_incident_tasks
…143355_create_patrol_public_report_tablespatrol_public_reports, patrol_public_report_messages
…144453_create_patrol_governance_tablespatrol_retention_policies, patrol_legal_holds, patrol_privacy_requests, patrol_disclosures
…145803_create_patrol_ai_tablespatrol_ai_jobs, patrol_ai_outputs

Schema-present but no dedicated screen yet. Three tables ship in the schema but have no dedicated management screen in this version: patrol_route_schedules and patrol_devices (no UI controller), and patrol_incident_links (written by the escalate/link flow, not a standalone editor). The guide does not document screens for these — because there are none.

Automated jobs verified

Six background jobs live under modules/Patrol/jobs/ (namespace Modules\Patrol\Jobs). Each runs per-tenant, is enabled by default, and self-gates on module_patrol — so switching the module off silently stops all of them. These are the evidence for every "the system does this for you overnight / on a timer" claim in the guide.

Cron jobs — schedules read directly from the job classes
Job classRunsWhat it does
EscalateIncidentsJobevery 5 minEscalates unacknowledged critical, still-open incidents older than patrol_escalation_minutes (15); notifies via patrol_incident_escalated.
PatrolMissedRoundsJobevery 15 minFlags in-progress runs overdue past the route's estimate plus patrol_round_grace_minutes (15).
PatrolAssetOverdueJobhourlyFlags issued equipment past its expected return time.
PatrolChainVerifyJobdaily 02:30Re-walks the hash chains of blotter, incidents, and handovers; raises a critical alert on any break.
PatrolLicenseExpiryJobdaily 03:00Flags guard licenses expiring within patrol_license_expiry_days (30).
PatrolRetentionSweepJobdaily 04:00Applies active retention policies (anonymise / soft-delete / purge) with legal-hold, evidence, and disclosure guards; writes a deletion certificate.

Configuration sidecars verified

The module carries six configuration files under modules/Patrol/config/. These are what make it appear in the sidebar, the Roles editor, and the audit log without touching ERPat core.

Config sidecars present in the module
FileRole
module_config.phpManage-Modules registration; module_key = patrol
menu.phpThe single "Patrol" sidebar group and its 14 sub-items, each permission-gated
default_menu.phpThe module's slice of the default left menu
permissions.phpAll Patrol permission keys, injected into the Roles editor
routes.phpCanonical and public (patrol-report/…) routes
system_logs.phpAudit component/action registrations (category "Security")

What the module deliberately does not ship. There is no dashboard-widget config, no CMS public-page sync config, no profile-tab config, and no REST / end-user API sidecar. The guide therefore documents none of those surfaces — because the module does not expose them. Patrol's only public surface is the anonymous reporting controller below.

Public & staff-triage surface

Patrol's only unauthenticated surface is the public reporting form, confirmed in config/routes.php and controllers/Patrol_report.php. It resolves the tenant from the URL the same way the Kiosk and Cron endpoints do.

Public routes — verified in modules/Patrol/config/routes.php
RoutePurpose
patrol-report · patrol-report/{company_key}Show the public report form
patrol-report/submitSubmit a report; shows the one-time tracking code
patrol-report/track/{company_key}[/{code}]PII-free status lookup
patrol-report/message/{company_key}/{code}Reporter adds a message

The staff side of public reporting (Patrol_public_reports.php) is a normal authenticated screen gated by patrol_public_report_triage; converting a report to an incident additionally requires patrol_incident_create. The tracking token is shown to the reporter once and only its SHA-256 is stored, so even staff cannot recover it.

Tamper-evidence primitives

The guide's strongest claims — reference numbers, no silent edits, a tamper-evident chain, and offline-safe writes — all trace to models/Patrol_base_model.php. Reading that one file confirms the behaviour rather than trusting the prose.

Integrity behaviour → where it is implemented
Claim in the guideEvidence
Every record gets a reference number_generate_reference() mints PREFIX-YYYY-#### at version 1
You can't secretly edit a submitted recordamend() flips the old row to superseded and inserts a new version; content and hash stay byte-intact
Records are tamper-evident_compute_hash() = SHA-256 over key-sorted immutable content plus the previous hash, bucketed per site and day
Works offline without duplicatingget_by_client_id() makes a replayed write with a known client_generated_id a no-op
Nightly integrity checkPatrolChainVerifyJob re-walks those chains and raises a critical alert on any break

Settings & toggles verified

These settings were confirmed by reading how the controllers and jobs call get_setting(). Note which are seeded and which simply fall back to a hard-coded default.

Settings read by the module
SettingDefaultEffect
module_patrolMaster on/off; gates the module and every cron job
module_patrol_aiOff (not seeded)AI assist on/off; off shows the "disabled" page
patrol_ai_dpaOff (not seeded)One-time data-privacy acknowledgement, admin-only
patrol_escalation_minutes15How long a critical incident may sit unacknowledged
patrol_license_expiry_days30Advance warning window for guard licenses
patrol_round_grace_minutes15Grace before a patrol round is "missed"

Core subsystems reused & the single core touch

Patrol is designed to reuse ERPat's existing platform rather than rebuild it. Verified reuse points:

  • File storage — evidence and photos go through general_files / S3, not a bespoke store.
  • Audit — every mutation calls set_system_logs(); components are registered in config/system_logs.php.
  • Attendance — "On Duty Now" reads the core attendance table (open clock-in records); Patrol never rebuilds clock-in.
  • Notifications — incident alerts ride the core notifications system.

The one file Patrol adds to ERPat core. Incident alerts require a single core migration, application/migrations/20260711121838_add_patrol_incident_id_to_notifications.php, run by php erpat migrate:latest. It adds a link column to notifications and seeds two notification settings (patrol_incident_raised, patrol_incident_escalated) with no default recipients. Until an administrator assigns recipients in Settings → Notifications, no one is notified — this is deliberate, to avoid alert spam. See Administration.

Documentation-state caveats

Honesty note for reviewers: the module's own bundled metadata is behind the code, and this guide follows the code.

The module's README, CHANGELOG, and its original single-page docs/index.html describe only "Phase 1" (blotter, visitors, incidents, evidence, and the escalation job) and list shifts & handover, rounds, public reporting, governance / DSAR, and AI assist as "planned." The shipped code implements all of those. Those bundled files are stale and understate the module. This multi-page guide reflects what the code actually does today, verified against the files listed on this page.

Feature → evidence map

A final cross-check: each documented feature area maps to a controller, an area permission, its tables, and (where relevant) a job. If you can open the controller and the tables, the feature is real.

Traceability summary
Feature areaControllerPermissionTimed job
Blotter / activity logPatrol_logbookpatrol_logchain verify
Visitor managementPatrol_visitorspatrol_visitor
Incidents & evidencePatrol_incidentspatrol_incidentescalate
Shifts & handoverPatrol_shifts, Patrol_handoverspatrol_shift, patrol_handoverlicense expiry
Routes & roundsPatrol_routes, Patrol_roundspatrol_route, patrol_runmissed rounds
Assets & equipmentPatrol_assetspatrol_assetasset overdue
Reports & exportsPatrol_reportspatrol_report_export
Locations adminPatrol_settingspatrol_location
Public reportingPatrol_report, Patrol_public_reportspublic / patrol_public_report_triage
Governance & retentionPatrol_governancepatrol_retentionretention sweep
AI assistPatrol_aipatrol_ai

Where to go next

This page proves the module is real; the rest of the guide explains how to use it.

Was this guide helpful?

Report a content problem