## Root cause
`route_t::hasPermission()` and `requirePermission()` are instance
methods. Route code was invoking them with `self::`; the new XL Vask
hall-scope helper made that call from a genuinely static context,
causing PHP to throw:
`Non-static method routes\\xlvaskUsageLogsRoute::hasPermission() cannot
be called statically`
## Changes
- Invoke route permission methods through `$this` across all 273
executable legacy calls in 45 route classes.
- Make `xlvaskUsageLogsRoute::allowedHallIdsForUser()` an instance
helper and update all 13 callers.
- Preserve the existing all-scope and own-scope hall selection rules.
- Add a token-aware regression test that rejects executable
`self::hasPermission()` and `self::requirePermission()` calls, while
ignoring comments.
- Add focused XL Vask tests for global scanner hall scope and
group-limited own scope.
- Update affected route contract assertions to the instance-call form.
## Verification
- PHP lint: all 53 changed PHP files
- Focused PHPStan: changed XL Vask route and both new regression tests —
clean
- Focused regression slice: 58 passed, 748 assertions
- Full local unit suite: 1,300 passed, 9,442 assertions (1 unrelated
existing warning, 1 environment skip)
- Full local API suite: 285 passed, 11,704 assertions
- Exact-SHA GitHub Tests workflow: all 7 jobs passed (unit, API,
integration, legacy, edge gateway, and supporting checks)
- Independent exact-SHA QA gate: PASS, no findings
- Independent exact-SHA security gate: PASS, no findings
- Independent exact-SHA reviewer gate: PASS, no findings
- Remote comparison: exactly one commit ahead of
`40b104abed7723a7d1b7028190ecda0e7aeef829`; all 53 remote blob hashes
matched the reviewed worktree
## Delivery state
Draft only for human review. No merge or deployment is included. Qodana
is skipped while the PR remains draft and is therefore not represented
as a passed gate.
Ensure proper type casting for 'id' parameter and validate its length for consistency. Address potential type mismatch in department access check with explicit casting. These changes improve data integrity and robustness of the delete operation.
Renamed `phone_number` field to `phone` across the codebase for consistency and simplicity. Introduced a new endpoint to update department notification SMS, supporting edits to fields like `label`, `phone_country_code`, `phone`, and `enabled`.
Added functionality to handle vehicle subscriptions in invoices, including validation and item visibility. Introduced department notification SMS capability with endpoints for creating, retrieving, and deleting SMS records. Enhanced invoice drafting logic to exclude items not flagged for inclusion in invoices.