Change Document — Service Order Lockout v1.1.0.1
v1.1.0.0 → v1.1.0.1
Bestway BC Development | March 16, 2026
Branch: fix/service-order-lockout-defects
1. Background
Two API pages and a Python-based functional test suite were added to the Service Order Lockout extension to support automated pre-deployment verification. The API pages expose the extension's lock state fields and setup configuration over OData v4, giving the test framework a reliable, UI-independent channel to set up preconditions and assert post-conditions without depending on browser automation for every scenario.
A pre-commit review identified two compliance gaps in the initial API page implementations: both pages were missing explicit InsertAllowed = false and DeleteAllowed = false guards. Without these guards, a user with SUPER permissions could insert a new Service Header or delete an existing one directly through the API endpoint — bypassing the standard BC UI workflow entirely. Both gaps were corrected in this version.
2. Summary of Changes
| # | Category | Description |
|---|---|---|
| 1 | Added | API page 52403 ServiceOrderLockoutAPI — OData v4 endpoint exposing lock state fields on Service Header for test automation |
| 2 | Added | API page 52404 ServiceOrderLockoutSetupAPI — OData v4 endpoint exposing the lockout setup singleton for test automation |
| 3 | Fixed | InsertAllowed = false; DeleteAllowed = false added to both API pages — prevents unintended record creation or deletion through the OData endpoint |
| 4 | Added | Python functional test suite (Service Order Lockout/test/) — 12 test areas, ~40 test cases covering all extension behaviors |
| 5 | Added | BcPage helper methods for Service Order navigation, action inspection, and lockout message detection (tools/bc_test_helpers/bc_playwright_helpers.py) |
3. Detailed Changes
3.1 API Pages
Page 52403 — ServiceOrderLockoutAPI
File: Page/ServiceOrderLockoutAPI.Page.al
Read/write OData v4 API page over Service Header. Exposes only the fields used by the locking mechanism: systemId, documentType, no, checkStatus, openByUserID, lockedAt, and status. The checkStatus, openByUserID, and lockedAt fields are writable to allow the test framework to simulate lock state via API PATCH without navigating through the BC UI.
InsertAllowed = false and DeleteAllowed = false are set explicitly. The intent is to allow reads and targeted field updates (PATCH) only — creating or deleting Service Headers through this endpoint is not a supported operation.
OData endpoint pattern: Lockout/Bestway/v1.0/serviceOrderLocks
Page 52404 — ServiceOrderLockoutSetupAPI
File: Page/ServiceOrderLockoutSetupAPI.Page.al
Read/write OData v4 API page over the Service Order Lockout Setup singleton table. Exposes systemId, lockoutEnabled, and staleLockTimeoutHours. Allows the test framework to verify and toggle lockout configuration without navigating through the BC UI.
InsertAllowed = false and DeleteAllowed = false are set explicitly. The setup table is a singleton — creating a second record would break the GetSetup() logic. The OnOpenPage trigger calls Rec.GetSetup() to ensure the singleton record is auto-created on first API access if it does not yet exist.
OData endpoint pattern: Lockout/Bestway/v1.0/lockoutSetups
Note on production presence: These API pages are part of the published extension and are accessible in production over OData. Access is gated by Business Central's standard authentication (OAuth2 / S2S). The pages do not introduce any capabilities beyond what the table data permissions already allow for authenticated callers.
3.2 Python Test Suite
Directory: Service Order Lockout/test/
A Playwright + pytest functional test suite covering all 12 test areas from the UAT test plan. Tests are organized by tier:
- Tier 1 — API-only tests. No browser required. Fast, run first to verify extension connectivity.
- Tier 2 — Single-user browser tests. Require a published extension and authenticated browser session.
- Tier 3 — Multi-user browser tests (Phase 6). Require two separate Entra logins. Marked
@pytest.mark.skipuntil Phase 6 infrastructure is available.
Test setup is handled by fixtures in test/conftest.py. Shared infrastructure (API client, Playwright helpers, test data management) lives in tools/bc_test_helpers/ and is reusable across all BC extensions.
3.3 BcPage Helper Additions
File: tools/bc_test_helpers/bc_playwright_helpers.py
Added methods required by tests 9–12:
| Method | Purpose |
|---|---|
open_service_order(order_no) | Navigate to the Service Orders list and open a specific order by No. |
click_next_record() | Navigate to the next record (alias for navigate_next_record) |
click_previous_record() | Navigate to the previous record (alias for navigate_previous_record) |
close_card() | Close the current card page (alias for close_page) |
get_action(caption) | Find an action button and return a BcAction proxy for visibility/enabled/click inspection |
confirm_dialog() | Confirm a BC Confirm() dialog by clicking Yes |
dismiss_dialog() | Dismiss a BC Confirm() dialog by clicking No |
has_lockout_message() | Check whether the Service Order lockout notification dialog is visible |
Also added BcAction — a lightweight proxy class wrapping a Playwright element, providing is_visible(), is_enabled(), and click(). Tests use get_action() → BcAction to inspect and interact with action buttons in a single call rather than three separate is_action_visible / is_action_enabled / click_action calls.
4. Object Inventory (v1.1.0.1)
All objects use the extension's declared ID range (52400–52499).
| Object Type | ID | Name | Status |
|---|---|---|---|
| Table Extension | 52400 | ServiceHeader (extends Service Header) | Unchanged |
| Table | 52401 | Service Order Lockout Setup | Unchanged |
| Page Extension | 52400 | ServiceOrders (extends Service Orders) | Unchanged |
| Page Extension | 52401 | ServiceOrder (extends Service Order) | Unchanged |
| Page | 52402 | Service Order Lockout Setup | Unchanged |
| Page | 52403 | ServiceOrderLockoutAPI | New |
| Page | 52404 | ServiceOrderLockoutSetupAPI | New |
| Permission Set | 52401 | BW-CS-SERVICE | Unchanged |
| Codeunit | 52402 | Service Order Lockout Upgrade | Unchanged |
5. Final File Structure
Service Order Lockout/
├── app.json
├── Codeunit/
│ └── ServiceOrderLockoutUpgrade.Codeunit.al
├── Page/
│ ├── ServiceOrderLockoutAPI.Page.al (new)
│ ├── ServiceOrderLockoutSetup.Page.al
│ └── ServiceOrderLockoutSetupAPI.Page.al (new)
├── PageExt/
│ ├── ServiceOrder.PageExt.al
│ └── ServiceOrders.PageExt.al
├── PermissionSet/
│ └── BWCSService.PermissionSet.al
├── Table/
│ └── ServiceOrderLockoutSetup.Table.al
├── TableExt/
│ └── ServiceHeader.TableExt.al
├── test/
│ ├── conftest.py
│ ├── pytest.ini
│ ├── requirements.txt
│ └── tests/
│ ├── test_01_live_status.py through test_12_super_user.py
└── docs/
├── CHANGELOG.md
├── CHANGE-v1.1.0.0.md
├── CHANGE-v1.1.0.1.md
└── UAT Test Plan - Service Order Lockout v1.1.0.0.docx
6. Known Limitations
No new known limitations. Existing limitations from v1.1.0.0 apply (sub-second race on concurrent lock acquisition; locking enforced at page layer only — see CHANGE-v1.1.0.0.md §6).
Tier 3 tests deferred: Multi-user browser tests (Phase 6) require two authenticated Entra sessions running concurrently. These tests are marked @pytest.mark.skip(reason="Requires multi-user browser auth — Phase 6") and are not expected to run until that infrastructure is available.
7. Deployment Notes
No pre/post-deployment steps beyond standard extension upgrade. The new API pages are included in the compiled .app and are published automatically.
Post-deployment verification:
- Confirm
GET .../Lockout/Bestway/v1.0/serviceOrderLocksreturns Service Header records - Confirm
GET .../Lockout/Bestway/v1.0/lockoutSetupsreturns the setup singleton - Run the Tier 1 test suite (
pytest -m tier1) to validate API connectivity before browser tests
8. Testing
Tier 1 (API-only) tests in test/tests/test_10_setup_page.py directly validate the new API pages. Tier 2 browser tests (tests 09, 11) use the open_service_order, get_action, and navigation helper methods added in this version. All Tier 3 tests remain skipped pending Phase 6.