Movements Reference Public

Administration & Controls

Configure the Movements module: the mobility policy and privacy settings, the twelve movements_* keys, RBAC permissions, salary visibility, publication automation, the two daily cron jobs, profile tabs, and the demo seeder.

Guide version: r1 Module version: 1.1.0 Updated: 2026-07-22 Estimated time: 8 min 1 views
Guides

Administration & Controls

Everything an administrator tunes: the mobility policy, the twelve settings, the permission matrix, salary and profile privacy, the daily automation, and the demo seeder — with the exact behavior each control produces.

Permissions (RBAC)

The module owns 10 permission keys, all in the Human Resource: Mobility category in the Roles editor. They appear only while the module is enabled and require no edit to core Roles. Keys are frozen once shipped, so existing grants keep resolving across upgrades.

Permission keyLabelGrants
movementsEmployee MovementsThe base key — opens the movement audit table and the HR "Employee Movement" profile tab.
movements_createRecord Manual MovementsThe "Record movement" form.
movements_updateCorrect / Reverse MovementsRecording a reversal (correction) row.
movements_deleteDelete Movement RecordsDeleting a movement (last resort — prefer a reversal).
movements_view_salaryView Salary Columns on MovementsReveals/sets salary everywhere: movement columns, opening ranges, and selection salary.
internal_hiringManage Internal HiringThe Internal Hiring workspace (openings, pipeline, overrides).
internal_hiring_publishPublish Internal OpeningsMoving an opening to Published.
internal_hiring_selectSelect Internal CandidatesThe one-transaction candidate selection.
mobility_overrideDecide Mobility Policy OverridesApproving/rejecting exception requests.
movements_settingsManage Mobility SettingsThe Mobility Settings page.
????
The Internal Job Portal has no permission. Browsing and applying is self-service for any active staff member — it is gated only by the module toggle. The controller enforces session-derived identity and one-application-per-opening rules, so no HR permission is needed or appropriate.

Suggested role presets

HR Manager

All Mobility keys including movements_view_salary, internal_hiring_publish, internal_hiring_select, mobility_override, and movements_settings.

HR / Recruiter

movements+create and internal_hiring — usually without salary and without publish/select/settings.

Every employee

Nothing extra — the job portal and "My…" profile tabs work for all active staff by design.


Settings

All twelve settings are tenant-scoped and live under HR Operations → Mobility Settings (also surfaced as a Mobility Settings tab inside core Settings). They need the movements_settings permission. Each is sanitized on save (integers are floored at 0, booleans normalized, and salary visibility constrained to its two allowed values), and saving writes an audit-log entry.

SettingTypeDefaultEffect
movements_max_actionsInteger3How many mobility actions an employee may take inside the rolling window.
movements_window_monthsInteger6The length of the rolling window in months.
movements_count_withdrawnOn / OffOnWhether a withdrawn application still counts against the limit.
movements_withdrawal_grace_hoursInteger0Withdrawing within this many hours of submitting releases the count (0 disables the grace rule).
movements_min_tenure_monthsInteger0Minimum tenure before applying (0 = no minimum; unknown hire dates are not blocked).
movements_block_disciplinaryOn / OffOnBlock employees with an active disciplinary case (reads the Compliance module when present).
movements_allow_overrideOn / OffOnWhether blocked employees may request an audited policy exception.
movements_auto_post_announcementOn / OffOnWhether publishing an opening (with its own flag ticked) posts to Announcements.
movements_auto_post_timelineOn / OffOffWhether publishing an opening (with its own flag ticked) posts to the Timeline.
movements_auto_captureOn / OffOnWhether the daily job captures Employee Master changes as movements.
movements_self_movement_tabOn / OffOnWhether "My Movement History" shows on employee profiles.
movements_salary_visibilityChoiceHR onlyWho sees salary changes: HR only (permission holders) or the employee and HR.

The mobility policy — how the rules are evaluated

The policy engine is the fairness core of the job portal. The server is the sole authority: it evaluates the policy for the informational eligibility panel, again when the employee submits, and once more when HR selects a candidate. The rules split into two kinds:

Structural rules (never overridable)

1. The opening must be accepting applications — published, within its open/close dates, and with a free slot.
2. One active application per opening — you can't apply twice to the same opening.

Policy rules (an approved override bypasses)

3. Rolling usage limit — fewer than max_actions counted applications in the window.
4. Minimum tenure — met, when set and the hire date is known.
5. No active disciplinary case — when disciplinary blocking is on.

How usage is counted

  • An application is the creation event — there are no drafts. A single application counts once.
  • The window is a rolling period computed from the tenant-local clock and matched against each application's submit time.
  • By default a withdrawn application still counts. If you set a grace window and the employee withdraws within it, the count is released (treating it as an erroneous submission).
  • Minimum tenure fails open: if an employee's hire date is missing or unusable, the tenure rule is skipped rather than blocking them.
⚖️
Overrides bypass policy rules, never structural rules. An approved, unexpired override lets an employee past rules 3–5 for a specific opening — but the opening must still be open and they still can't double-apply. The "Request exception" button appears only when the block is purely policy-based and overrides are enabled.

Salary & profile privacy

Two independent controls govern who sees pay and personal history:

movements_view_salary (permission) The hard gate. Without it, salary columns read Hidden, HR cannot set salary on a manual movement or a selection, and opening salary ranges are not written. movements_salary_visibility (setting) Governs the employee's own view. HR only hides salary figures on the self-service "My Movement History" tab; The employee and HR lets the employee see their own salary changes there. movements_self_movement_tab (setting) Turns the "My Movement History" self-service tab on or off entirely.
????
Self-service endpoints never trust the browser. The portal and the "My…" tabs resolve the employee from the signed-in session, and every mutation re-verifies ownership — an employee can only ever see and act on their own data, regardless of any id in the request.

Publication automation

Publishing an opening can announce it — but only when two switches agree: the tenant-wide setting and the per-opening flag.

ChannelTenant settingPer-opening flagResult on publish
Announcementsmovements_auto_post_announcement"Post to Announcements"An audience-scoped announcement with a link back to the portal is created (needs the Announcements feature present).
Timelinemovements_auto_post_timeline"Post to employee Timeline"A timeline post is created, public or department-scoped to match the opening's audience (needs the Timeline module present).

If the target feature isn't installed, publishing still succeeds — the post is simply skipped.


Cron jobs

Two daily jobs run on ERPat's Advanced Cron runtime. Both self-gate on module_movements and skip cleanly if the module's tables aren't migrated on the current database.

Job (slug)ScheduleBehavior
movements_capture_employee_movements Daily 02:20
20 2 * * *
For each employee, hashes department + position + salary and compares it to the last snapshot. A change is classified (transfer / role change / salary adjustment) and recorded as an Auto Capture movement; the snapshot is updated. Also respects movements_auto_capture. First run baselines only.
movements_close_expired_openings Daily 01:10
10 1 * * *
Moves published openings past their closing date to Closed.
php erpat cron:list       # see both jobs
php erpat cron:run movements_capture_employee_movements
php erpat cron:run movements_close_expired_openings

Profile tabs

The module injects three tabs into the team-member profile page:

TabAudienceGate
Employee MovementHR viewing anyone's profilemovements permission.
My Movement HistoryThe employee, own profile onlymovements_self_movement_tab setting (salary follows movements_salary_visibility).
My Internal ApplicationsThe employee, own profile onlySelf-service — no permission.

Audit trail

Every state change in the module writes a row to ERPat's system activity log, under the Human Resource and Component Settings categories. Warnings are used for the higher-impact events (reversals, deletes, status changes, candidate selection, override decisions, settings changes). This means the module is fully auditable — you can always answer "who changed this, when, and from what." The complete list of logged events is in the Data & Screen Reference.


Demo seeder

The MovementsDemo seeder loads an explorable slice of every surface and is idempotent, connection-agnostic, schema-drift safe, and reversible.

php erpat db:seed MovementsDemo                       # main database
php erpat db:seed MovementsDemo --tenant=<company_key>  # one tenant
php erpat db:seed MovementsDemo --all                  # main + every active tenant
php erpat db:seed MovementsDemo --dry-run              # preview counts, write nothing
php erpat db:seed MovementsDemo --remove               # soft-delete the demo rows
  • It attaches sample rows to the first active staff users already on the database and creates nothing if none exist.
  • It seeds 2 openings (1 published, 1 draft), 1 application with 2 stage rows, 2 movements, and 1 pending override.
  • Remove is a soft delete (sets deleted = 1), matched on stable demo markers (opening codes, references, idempotency keys) — it never hard-deletes and never touches real records.
  • You can also drive it from the Bulk Database Seeder modal in the Tenants console.

Next steps

Was this guide helpful?

Report a content problem