SH-44 and SH-82 both left "blocks, or requires explicit confirmation" to
be decided with the team, and the implementation took the blocking
branch. SH-254 settles it the other way: the approved design offers
"Deactivate anyway" beside the list of open work orders.
Deactivation with open work orders is now permitted, but only when the
caller says it has shown them: ConfirmOpenWorkOrders on the update DTO
and a confirmOpenWorkOrders query parameter on the delete route. Absent
the flag the existing guard still throws, so nothing deactivates by
accident and no caller loses the check by omission.
confirmOpenWorkOrders is a required parameter on DeleteVendorAsync
rather than an optional one, so every call site states its intent.
Three contract gaps found reviewing the frontend consumer:
- Revoking an auto-approved uplift never restored the dispatch NTE. Create
raises NTE for both auto-approved and approved requests, but revoke restored
it only for Approved, so the allowance was freed while the NTE stayed raised
and every create -> auto-approve -> revoke cycle compounded the inflation.
Revoke now compensates for NoApprovalRequired symmetrically.
- Revoke and cancel had no work-order lifecycle check, so a direct API call
could still mutate uplifts on a Completed or Canceled work order; the board
dialog's read-only state is UX only. Both now reject terminal work orders in
the service.
- WorkOrderBoardCancelService read the pending-uplift list outside any gate, so
an in-flight create could commit after that read and leave a pending uplift on
a Canceled work order. The cancel flow now runs inside the same per-work-order
gate as create, so the pending read, withdrawal and status audit serialize
against it.
The 422 still told dispatchers to update Due Date. Point the remedy at Schedule On and make the SH-121 successor visible to EF so G6 lineage is complete.
Auto-approval now uses the WO-scoped $500/$5,000 Emergency cap instead of dispatch NTE, rejects a second open request across dispatches, and cancelling a WO withdraws pending uplifts with audit.
Expose workorders/{id}/uplifts list/create/cancel/revoke for the SH-196 dialog, aggregate upliftSummary on board rows, and add service/controller regression tests.
Add set-once IsAddOn with server cutoff at create, board DTO exposure, legacy type-7 backfill, and Types=AddOn search compat. Aligns with FE PR #61 frozen contract.
Past Due must track the deadline (Due Date), not Schedule On. Keep dueDate and scheduledDate PATCH mutations independent so rescheduling alone does not clear Past Due.
Map board PendingUpliftCount through WorkOrderDetailService.MapInfo and ignore soft-deleted dispatches in the aggregate so SH-188 gating is authoritative for board/search/detail.
Pass ClaimsPrincipal into GetCommentsAsync and filter via
GetAllForAccountAsync so account-scoped callers cannot enumerate
cross-tenant comments. ADR + cross-account tests updated.
Close the remaining SH-221 bypass: board/legacy comments and completion-doc now enforce server-derived account scope, authorize before blob storage, and cover cross-account regressions.
Close the remaining SH-221 read gap so Getworkorders, filtered lists, and GetWorkorderById enforce the same server-derived account boundary as board/media.
Co-authored-by: Cursor <cursoragent@cursor.com>
Seed resolvable Customer accounts and mock account resolution so create-path CI tests match fail-closed account scope.
Co-authored-by: Cursor <cursoragent@cursor.com>
Stamp WorkOrder.AccountId on all create paths and filter board/list/search/detail by server-derived account claims so scoped callers cannot cross accounts.
Co-authored-by: Cursor <cursoragent@cursor.com>
Align DeleteUser with AddUser/EditUser: Admin role at controller and
service entry, Forbidden for non-Admin, and regression coverage.
Co-authored-by: Cursor <cursoragent@cursor.com>