Hospital Case Stock
U10 case requirements, Inventory commands, readiness, retry recovery, and release checks.
U10 connects Hospital case requirements to the reusable Inventory domain. The implementation is local. It is not a staging or pilot acceptance record. Use the manual QA guide before release.
Ownership
Clinical owns hospital_case_stock_requirements and immutable hospital_case_stock_commands.
These records contain the case, actor, reason, actual-use time, and readiness policy reference.
Inventory owns items, lots, locations, holds, custody, movements, and balances.
Hospital does not copy source balances or put patient identifiers in Inventory commands.
The source key is a digest of the Clinical command identity.
Requirements retain their original quantity, base unit, policy version, and rule snapshot. To correct a planned need, add a new requirement with a reason. Do not edit history. Release an obsolete hold before reserving stock for a replacement requirement.
HTTP Surface
All paths below are under /api/clinical/facilities/:facility_id.
| Method | Path | Purpose |
|---|---|---|
| GET | /case_stock | Paginated case requirements and current source quantities. Supports case_id, search, and page. |
| GET | /case_stock/:id | One requirement, command history, current holds, and outstanding custody. |
| GET | /surgical_cases/:case_id/stock_commands | Protected command history. operation_key selects an original result. |
| POST | /surgical_cases/:case_id/stock_commands | Apply one reasoned, idempotent case stock command. |
The POST body contains only operation_key, kind, and input.
Protected command history returns the stable operation_key and the workbench displays it.
The actor and tenant come from authenticated context. Unknown body fields fail validation.
Quantities and versions are decimal strings in the TypeScript DTOs.
Use hospitalStockApi.ts and stockTypes.ts for the current consumer contract.
stockSchemas.ts validates rows, command outcomes, and pagination at the HTTP boundary with Zod.
Malformed data returns INVALID_RESPONSE, not a partially rendered stock record.
A malformed write response remains uncertain. Preserve the original key for lookup and retry.
Use the Inventory source contract for stock lookups.
Commands are add_requirement, reserve, issue, consume, release, return_unused,
reverse, void, and reconcile.
Every command needs a reason and a stable operation key.
Except add_requirement, every command needs the current requirement ID and version.
Reservation operations also need the current reservation version.
reserve needs an explicit expiry. No setting invents a hold duration.
consume needs an actual-use timestamp with an offset, not a future time.
issue moves stock to OT or ward custody. It does not consume that stock.
return_unused uses original case custody and a destination store.
The source applies return limits and quarantines material when required.
reverse corrects an erroneous consumed quantity. It is not a physical return of used material.
void closes an erroneous unconfirmed use. It does not erase the original record.
Transactions And Obligations
The command locks the case, then the Inventory facility lock. It calls the source in process on the same database connection and transaction. There is no internal HTTP hop or independent source commit. Required Clinical audit failure rolls back the joined source effect.
Known stock shortages, ineligible stock, stale stock versions, and the source write stop can
leave an actual-use command with outcome: pending and no Inventory operation.
This is a durable clinical obligation, not confirmed consumption.
Invalid identity, quantity, or permission does not create a confirmed use or a pending obligation.
An unknown source outcome does not claim a successful posting.
Reconciliation retains the original actual-use quantity, unit, timestamp, and source key. It can use an explicitly selected current stock allocation and version. The new Clinical reconciliation command links to the original obligation. The original pending record remains unchanged. One confirmed reconciliation closes it.
Case cancellation releases unconsumed holds through the same command service. It never restores consumed stock. Outstanding custody needs an explicit unused return. Failed known cleanup remains visible through source quantities and a protected case event. Use the stock screen to release the remaining hold or return eligible unused stock.
Current Readiness
Readiness uses confirmed source movements and current eligible, unexpired holds. It subtracts recording corrections and checks unresolved actual-use obligations. A hold stops clearing readiness at its expiry, even before an expiry job runs. Old needs cannot clear a newly bound policy, including a new version of the same rule. The SQL list projection and case detail use the same stock expression. The policy validator rejects Inventory manual-review and override rules. U10 does not add an Inventory review role or an override for missing physical stock. Such rules need an approved, implemented review workflow before activation.
Security And UI
Both Clinical tables have forced tenant RLS and scoped foreign keys.
Read access requires view_hospital, view_surgical_cases, view_inventory, active facility
access, and the cases and inventory modules.
Writes also require an active tenant, manage_surgical_cases, and manage_inventory_consumption.
Recording-error corrections also require manage_inventory_adjustments.
Every retry checks current authority before it returns an old result.
Shell owns role management. Admin owns feature-flag management.
The live workbench uses paginated source lookup, command history, and current quantities.
The board computes shared display abilities once per request. Every write still checks
current authority under the command locks; display abilities do not authorize writes.
The case-detail handoff retains caseId in the URL.
Queries include tenant, facility, and profile context. A context change resets the editor.
Mock mode remains a separate prototype; no mock write runs in HTTP mode.
An uncertain submission retains its complete command in memory. Reload recovery stores the stable key and structured stock fields in session storage, scoped to the active context. It stores a SHA-256 digest instead of the free-text reason. It does not store patient names. A saved-result check can resolve a committed command. If no result exists, the user must enter the original reason before the identical retry. A failed lookup is not proof of rollback. Do not submit a replacement key. This includes a 403 after permission revocation and a conflict during recovery. The recovery panel shows the server error but retains the uncertain key. An authorized operator must confirm the original result. Do not clear that evidence only to unlock the workbench. A known rejection of an initial submission can clear its local intent. Session storage does not survive every browser-session reset. Preserve the command key in the QA evidence and use protected command history if that local recovery record is lost.
Deployment
For local security tests, use a disposable test database. Prepare its schema first,
then run RAILS_ENV=test bin/rails db:rls_test_role:provision against that same database.
A schema reload can remove the test grants. Provision them again after any reload.
The test role must be NOSUPERUSER and NOBYPASSRLS. Do not count skipped RLS tests
as security verification. Do not provision this role on staging or production.
Apply migration 20261012000003_add_hospital_case_stock.rb after the Inventory source and U2 migrations.
The schema dump must contain only additive U10 objects.
The migration is forward-only because clinical and stock history must remain intact.
Deploy rollback must stop the new UI and writes, then use a reviewed forward migration if needed.
Do not drop the history tables to roll back an application build.
Local Verification
The U10 regression run passed 656 backend tests and 3,435 assertions, with no failures, errors, or skips. This includes Hospital, Inventory, Identity RBAC, enforced RLS, required audit rollback, and independent-connection concurrency tests. The Hospital frontend passed 181 tests. Type-check, lint, and production build passed. All 26 changed Ruby files passed lint. The additive SQL load matches the migrated schema. Desktop and mobile layout checks used synthetic API fixtures, not live backend requests.
The scoped CodeRabbit review found one repeated-policy-query issue. The correction and its regression passed a follow-up review with no findings. The full CE multi-agent review could not run under the current execution boundary; the scoped review was the fallback. These results are not whole-PR CI or staging acceptance.
Complete full-PR CI, clinical policy approval, pilot stock/expiry approval, and manual QA before release. Billing charges, MAR stock mapping, pharmacy review, and inventory dashboard metrics are separate units. U10 does not complete them.