## 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.
Complete and secure public customer/driver registration, authoritative limited-backoffice department scope, one-time employee QR login, and pricing concurrency for the Sæby demo.
## What changed
- validate every normalized order-booking item against active customer
product rules before reservation and persistence
- return a structured HTTP 400 response containing the rejected product
and matching rule metadata
- document the rejection response in both OpenAPI specifications
- add API coverage for restricted base products, restricted add-ons, and
allowed neighboring products
## Why
Frontend rule guidance alone cannot prevent stale or crafted requests
from persisting restricted booking products. The booking write boundary
must enforce the same customer rules.
## Validation
- full backend API suite
- focused order-booking API coverage
- PHP syntax checks
- OpenAPI and diff checks
## Related frontend PR
The coordinated frontend PR provides fail-closed selection, recovery,
and responsive booking-page behavior.
- Return empty arrays for empty inputs in Redis `mget`, `db_object_t`, and `users_o` operations.
- Refactor safety seal validation logic to handle numeric strings and improve clarity.
- Add unit and API tests to verify handling of empty inputs and numeric safety seal strings.
- Deleted `complete_booking_f` and `generate_booking_wash_certificate_f` classes.
- Updated tests to ensure legacy booking completion routes are disabled.
- Introduced tests for POST `/order-bookings/complete` to enforce POS-based booking completion management.
- Added `/collected-invoices/split-by-month` route with API and unit tests for splitting collections into monthly periods.
- Refactored impacted files to exclude legacy references and ensure continued compatibility with POS processes.
Transitioned from obsolete gateway object classes (`edge_gateway_shell_action_jobs_o`, `edge_gateway_shell_events_o`, `edge_gateway_shell_sessions_o`, `edge_gateway_update_jobs_o`) to the new agent implementation (`edge-gateway-agent/agent.php`).
- Add reusable helpers: `isOwnCustomerContext`, `resolveEffectiveCustomerNumber`, and `allowOwnOrDepartmentAccess` in `route_t` for streamlined permission checks.
- Replace inlined permission logic in `orderBookingRoute` with common helpers for creating, viewing, editing, and deleting bookings.
- Localize permission names, descriptions, and labels to Danish across relevant modules.
- Improve error handling and simplify context resolution for subuser and department-level permissions.
- Introduce `hasPermission` method in `subusers_o` for permission checks tied to customer context.
- Update `/subusers/me` route to return subuser grants with normalized permissions and metadata.
- Add `get_subuser_customer_number_target` in `authentication` to resolve customer context from request headers.
- Refactor route-level permission checks to handle subuser grants dynamically.
- Introduce CLI test scripts for subuser grants and permission node mappings.
- Add test coverage for subuser grants and permission nodes in new test classes.
- Add token creation for subusers in `subusers_o` during session generation.
- Refactor `orderBookingRoute` to use dynamic subuser permission definitions with `subusers_permission_node_key`.
- Added boolean to integer conversion in `db_object_t` during database operations.
- Improved handling of null values, numeric types, and sanitized inputs in `db_object_t`.
- Enhanced safety seal parameter validation in `orderBookingRoute` with support for nullable integers.
- Updated `getTargetItems` to retrieve product names for each item in the order.
- Added transaction check in `orderBookingRoute` to block edits on bookings tied to transactions.
- Introduced `hasTransaction` method in `order_bookings_o` to verify transaction association.
- Updated `getUserId` logic in `userRoute` to allow users to fetch their own user ID without requiring additional permissions.
- Corrected `customer_number` type in `orderBookingRoute` to always return an array.
- Added functionality to create and attach wash certificates during booking completion.
- Enhanced `completeBooking` to generate and associate wash certificates with orders based on booking items.
- Updated safety seal parameter handling in relevant methods.
- Integrated wash certificate PDF generation with customer branding and order attachment logic.
- Improved file retrieval logic in the `file_server` to handle missing certificates via alternative store lookup.
- Updated `completeBooking` method call to directly use `$user->id` without `value()` extraction.
- Added `completeBooking` method skeleton in `order_bookings_o` with placeholders for future implementation.
- Implemented `onAfterNewBooking` and `notifyNewBooking` methods to handle post-booking notifications.
- Added Slack and SMS notifications for departments upon creating new bookings.
- Enhanced error handling and logging for failed encoding and notification delivery.
- Updated `db_object_t` to JSON encode objects and arrays before database operations.
- Introduced `/order-bookings` DELETE route to enable deletion of order bookings.
- Implemented robust permission checks for deleting own and departmental bookings.
- Included detailed access control and error handling for unauthorized operations.
- Ensured proper success and error response handling.
- Added `order_id` extraction logic with validation in `getTargetOrderId` method.
- Included `order_id` in the data update process for order bookings.
- Updated `order_bookings_o` class to support `order_id` property.
- Added optional `required` parameter to `getTarget*` methods for flexible parameter validation.
- Improved null handling and safety checks in parameter extraction processes.
- Enhanced error handling for non-existing order bookings with proper response codes.
- Fixed logical operator issue in permission checks for customer number validation.
- Added default parameter `false` to all `getTarget*` methods for enhanced flexibility.
- Improved conditional checks before constructing update data array with `isset` for null safety.
- Enhanced parameter validation by ensuring customer number is not empty during permission checks.
- Added parameter extraction and validation for updating order bookings.
- Enhanced permission checks for editing own and departmental bookings.
- Implemented detailed access control logic based on user permissions.
- Built logic for updating booking details with strict type handling.
- Introduced a new `/order-bookings` PUT route to enable editing of existing bookings.
- Added permission `edit_bookings` for access control.
- Adjusted parameter type handling with explicit casting for validation.
- Introduced `order_bookings_o` class for interacting with the `order_bookings` database table.
- Added `hasDepartmentAccess` method to handle department-level permissions.
- Implemented new routes for creating and retrieving order bookings, including pagination support.
- Enhanced access control to differentiate between own and departmental bookings based on permissions.
- Updated input validation and improved error handling for order bookings.
- Introduced a new `orderBookingRoute` to process and validate order booking data.
- Added methods for parameter validation, customer and department identification, and item verification.
- Implemented functionality to handle booking details such as notes, PO numbers, pickup preferences, and datetime inputs.