[T09] Stock Activity

Summary

Tenant-scope list of all operational_batch rows + per-batch drill-down. The destination for every Round A routerLink="['/inventory/stock-activity', batchId]" dead-end:

  • T02 per-entry "Batch #N" chip in the audit-log panel.

  • T03 Done-stage "View batch" link + Review-missing card.

  • T04 dialog "Done" view → "View batch" link.

For STOCK_TAKE batches the drill-down renders the four report categories the 2026-02 stock-take design (operationalised via US #777) requires: In-stock (member + scanned), Missing (member + NOT scanned + flipped via STOCK_TAKE_MISSED), Outsider (scanned but not a member), Unmatched (NOT_FOUND).

T09 v1 ships with a graceful fallback for the body of the report categories — the per-batch state-log endpoint (GET /api/operational-batches/:id/state-log) is not yet exposed on develop. Until it lands the screen renders the header card + close action fully (so the dead-end links resolve) and surfaces an info banner explaining the missing endpoint in the body. See Backend follow-ups below.

Actor & Context

Actor: tenant admin, stock operator, finance / audit reviewer.
Frequency: ad-hoc during incident review; weekly during event seasons.
Precondition: user has TENANT_ADMIN permission; sidebar tenant scope active (see C01 § sidebar).
Entry point: Tenant-admin sidebar Inventory ▸ Stock activity, OR a deep-link from T02 / T03 / T04.

Layout

T09 renders inside C01’s shell — sidebar (tenant scope) + topbar are C01’s. T09 owns the main content area.

List screen — /inventory/stock-activity

Region Content

Header

Title "Operational batches" + record count.

Filter strip

Active filter chips (per the JHipster Tanstack substrate). Per-column popovers expose facets where the back-end criteria carries a matching filter — id (range), name (specified), type (specified), status (specified), opened-on (date range), closed-on (date range). openedBy / closedBy are display-only until the criteria widens.

Table

Columns: # · Name · Type · Status · Full · Opened on · Opened by · Closed on · Closed by. Sort: any sortable column. Default sort: openedOn DESC. Pagination: server-driven, page size 50 (default), 20/50/100 options. Row click → detail screen.

Empty state

"No batches match these filters" + a Clear-filters affordance when any filter is active.

Detail screen — /inventory/stock-activity/:batchId

Region Content

Back link

← Back to stock activity returns to the list (history-preserving — the list’s filter state is reapplied via its URL).

Header card

Batch eyebrow (id + type label) · name · description · status badge · full-mode badge (STOCK_TAKE only) · grid of opened-on / opened-by / closed-on / closed-by / event (when scoped) / attached-row count · (STOCK_TAKE + fullMode) missing count.

Close action

When status = OPEN, a "Close batch" button. Routes mode by batch shape — mode=full-stock-take when type=STOCK_TAKE AND fullMode=true, otherwise mode=normal. Hover copy explains the mode the operator is about to invoke.

Body — non-STOCK_TAKE

Single chronological list of attached state-log entries. Each row: bib link (out to T02 with the per-number audit panel auto-opened via ?focused=N) · state-pill transition · reason · note · actor · timestamp.

Body — STOCK_TAKE

Four-tab strip — In-stock / Missing / Outsider / Unmatched. Each tab shows a count badge + inline explanation of the category + the matching list of state-log entries.

Main Flow

  1. Operator navigates to Inventory ▸ Stock activity (or follows a deep-link from T02 / T03 / T04).

  2. List renders sorted by openedOn DESC. Operator filters by type / status / date as needed.

  3. Operator clicks a row → detail screen.

  4. Detail header card renders immediately from GET /api/operational-batches/{id}.

  5. Body renders the categorised drill-down from GET /api/operational-batches/{id}/state-log (pending — see Backend follow-ups).

  6. (Optional) Operator closes an OPEN batch via the Close-batch button — mode chosen by batch shape; success refreshes the header card with the closed-state DTO.

Alternative Flows

  • AF-1: Batch id in URL does not exist → 404-style alert "Couldn’t load this batch." Header card stays hidden; the Back link remains active.

  • AF-2: STOCK_TAKE state-log endpoint returns 404 → info banner explaining the backend follow-up; header card + close action remain operational.

  • AF-3: STOCK_TAKE state-log endpoint returns 5xx → "Couldn’t load batch activity. Try refreshing." alert; header card unaffected.

  • AF-4: Close action returns 403 → inline "You don’t have permission to close this batch." next to the button; batch stays OPEN.

Acceptance Criteria

  • List rendered at /inventory/stock-activity filterable by id / name / type / status / opened-on / closed-on.

  • Default sort = openedOn DESC; default page size = 50; both pinned on the URL on first paint.

  • Sidebar entry "Stock activity" between Return and Onboard, admin-role gated.

  • Detail screen at /inventory/stock-activity/:batchId renders the header card from GET /api/operational-batches/{id}.

  • Detail screen renders the close-batch action for OPEN batches; full-mode STOCK_TAKE batches route mode=full-stock-take.

  • Detail screen renders four report categories for STOCK_TAKE batches with the bucketing predicates documented above.

  • Non-STOCK_TAKE batches render the single chronological list of state-log entries.

  • Round A dead-end links from T02 / T03 / T04 now resolve to a real screen.

API Surface

Call Purpose

GET /api/operational-batches

List screen. Paginated (X-Total-Count + Link), JHipster criteria filtering.

GET /api/operational-batches/{id}

Detail header card.

POST /api/operational-batches/{id}/close?mode=normal|full-stock-take

Close-batch action (header).

GET /api/operational-batches/{id}/state-log

Pending — backend follow-up. Four-category drill-down body for STOCK_TAKE; chronological list for other types.

Backend follow-ups

  1. Expose GET /api/operational-batches/{id}/state-log — extends OperationalBatchResourceEx with a per-batch state-log surface returning List<RaceNumberStateLogDTO>. Until this lands the detail screen surfaces an info banner in the report body; header + close action remain fully operational. Follow-up US to file post-#748.

  2. Expose GET /api/operational-batches/{id}/members — required to compute the Outsider category (scanned ∉ member set). Until this lands the In-stock category absorbs every resolved STOCK_TAKE_SCAN row and Outsider stays empty by design. Follow-up US to file post-#748.

Out of Scope

  • Stock browsing — T02.

  • Stock return — T03.

  • Bulk actions — T04.

  • Print-batch manifest download — already exposed at GET /api/operational-batches/{id}/manifest; surfacing it from this screen is a Track 3 follow-up.

Design Anchors

  • T02 — Number Stock View

  • T03 — Stock Return

  • T04 — Bulk Flag UNFIT / Dispose

  • design-journal/2026-02/stock-take-tag-number-grouping.adoc — original four-category framing

  • design-journal/2026-03/number-tag-management.adoc § session 11 — full-mode + missing semantics

  • design-journal/2026-05/number-tag-ui-orchestration.adoc § Feature #742 — Round B wire-up

  • design-journal/2026-05/stock-activity-screen-design.adoc — this screen’s decisions log