Research Evidence
This guide is written from the live module source, not from assumptions. Every claim in the other pages traces back to a file listed below, with a note on what that file proves.
????
Method. Before writing, each configuration, controller, model interface, migration, cron
job and seeder under
modules/Movements/ was read directly. The planning docs under
modules/Movements/specs/ were not used as a source — only the shipped code is
authoritative. Where a behavior depends on another module (Announcements, Timeline, Compliance,
mPDF), the module's own guarded call sites were the evidence.
Manifest & configuration
| File | What it proves |
|---|---|
module.json | Slug movements, category Human Resource, version 1.0.0, the 10 permission keys, 6 tables, and the 12 settings keys. Also the "known risks" (immutable rows, first-run baselining, transactional selection, salary gating, session-only self-service). |
config/module_config.php | module_key = movements, icon fa-exchange, category Human Resource — the Manage Modules registration. |
config/menu.php | The four HR Operations sidebar items, their URLs and per-item permissions, and that the Internal Job Portal has no permission. |
config/default_menu.php | The default-menu slice (position 21, after core Human Resource) mirroring the same four items. |
config/permissions.php | The exact permission keys, labels, and the "Human Resource: Mobility" category; that movements is the module-level key. |
config/routes.php | The canonical hyphenated URLs and their underscore aliases mapping to each controller. |
config/settings.php | The Mobility Settings component tab injected into core Settings. |
config/profile_tabs.php | The three profile tabs, their gates, and that the two "My…" tabs are self_only and resolve the employee from the session. |
config/system_logs.php | Every audit-log event key, its category, and its severity. |
language/english/movements_lang.php | Every UI label and message quoted in this guide, plus the movement types, stages, statuses, and the nav-description tooltips. |
Controllers
| File | What it proves |
|---|---|
controllers/Movements.php | Movements are record/view/reverse/delete only (no edit); the "from" values are re-resolved server-side; the reversal swaps from/to and links reversed_movement_id; the my_* endpoints derive the user from the session and honor the self-tab + salary-visibility settings. |
controllers/Internal_hiring.php | Server-validated status transitions; publish gating; the publication side effects (announcement/timeline); the ordered pipeline; and the one-transaction selection — application → selected, employee master update, department membership move (FIND_IN_SET), movement record, slot fill — with rollback on any failure and a policy re-check. |
controllers/Internal_jobs.php | The self-service portal takes identity from $this->login_user->id only; the server re-runs the policy on submit; withdrawal honors the grace window; and override requests require a genuine policy block. IDOR guards on every mutation. |
controllers/Movements_settings.php | The twelve setting keys and their sanitizers (int / bool / enum), and that saving writes the update:movements_settings audit event. |
Policy engine, helper & jobs
| File | What it proves |
|---|---|
libraries/Mobility_policy.php | The pure, unit-testable rule set: two structural rules (opening open, one active application) and three policy rules (usage limit, tenure, disciplinary); the rolling window computed local→UTC; tenure fails open on unknown hire dates; and that an approved override bypasses only the policy rules. |
helpers/movements_helper.php | The movement type / source / stage options, the opening transition map, the salary formatter (honoring the view-salary decision), and the idempotency key builder. |
jobs/CaptureEmployeeMovementsJob.php | Daily 02:20 (20 2 * * *); self-gates on module_movements + movements_auto_capture; hashes department/position/salary; first run baselines only; classifies transfer / role change / salary adjustment; idempotent recording. |
jobs/CloseExpiredOpeningsJob.php | Daily 01:10 (10 1 * * *); closes published openings past their closing date; self-gates and skips when tables aren't migrated. |
Schema & seed data
| File | What it proves |
|---|---|
migrations/20260716115339_create_movements_module_tables.php | All six tables and their columns (every field cited on the Reference page), the indexes and unique idempotency indexes, the standard audit block on every table, the reversible down(), and the insert-only seeding of the 12 settings defaults (3, 6, on, 0, 0, on, on, on, off, on, on, hr_only). |
seeders/MovementsDemoSeeder.php | The exact demo slice (2 openings, 1 application + 2 stages, 2 movements, 1 override); idempotency by stable markers; schema-drift filtering; dry-run support; and the soft-delete unseed() (children before parents). |
????
Also present but out of scope for this guide: the models under
models/ (one per table), the views under views/movements/, and the
planning documents under modules/Movements/specs/. The models were consulted only for
their public method names; the specs directory is design material and was intentionally not used
as a factual source.