[T06] Import Manufactured Numbers

Summary

Tenant-scoped entry bookend of the manufactured-numbers async import flow. A stock operator uploads the CSV/XLSX their manufacturer returned (sequence + number + barcode triples) and hands off to C05 for processing. The exit bookend is T07. Mirrors E06 in structure but with one defining difference: the column schema is fixed (sequence, number, barcode are non-negotiable), so there is no source-system dropdown and C05’s column-mapping stage is auto-applied silently — see C05's Fixed-schema imports design decision.

Actor & Context

Actor: stock operator (tenant-admin assistant). Same actor as C02; T06 is the file-based alternative when the manufacturer ships a digital deliverable instead of physical-sequence-only stock.

Frequency: bursty per delivery — once per manufactured batch, then idle.

Precondition:

  • User has TENANT_ADMIN permission.

  • WS3 (US #478a) is in production: RaceNumber.tag_id FK exists.

  • ImportType.NUMBER_ONBOARDING is registered and NumberOnboardingRowProcessor is live (Track 3 backend deliverable).

  • The operator has a CSV/XLSX file conforming to the fixed schema.

Entry point: tenant sidebar → InventoryOnboardImport (route /inventory/numbers/onboard/import). Resume from My imports for a paused job.

Main Flow

  1. Upload screen. Single full-screen page.

    • Page title: "Import manufactured numbers — <tenant name>".

    • Fixed-schema banner — prominent, top-of-page: "This import expects a fixed schema. Required columns: sequence, number, barcode. Other columns are ignored. No mapping step required." A small Download template button next to the banner serves a tenant-neutral CSV template with the three headers and one example row.

    • File picker (CSV/XLSX) with drag-and-drop zone.

    • NumberType picker (REQUIRED) — dropdown listing the tenant’s NumberType rows. Drives which inventory the imported rows land under. Helper text: "Which catalogue does this batch belong to?"

    • No source-system dropdown. Numbers do not have a source-system concept; the file is always understood as "raw manufacturer output". The C05 target-field filter does not apply to T06.

    • Recent imports list below the inputs — date, filename, NumberType, row count, status. Clicking a row opens the matching T07 read-only.

    • Right-rail "About this screen" panel — collapsible; default expanded; per-screen localStorage key. Copy explains the fixed schema, what barcode may be (RFID/chip vs absent), and what state the rows will land in.

    • Primary action: Upload — disabled until file picked AND NumberType picked. Tooltip on the disabled button explains whichever requirement is missing.

  2. Submit. POST /api/imports?importType=NUMBER_ONBOARDING&numberTypeId=X (multipart). On 201, take the returned uuid and write localStorage.setItem('importCaller:' + uuid, 't06'). Navigate to C05 at /imports/{uuid}.

  3. Mapping flow (delegated). C05 auto-applies the fixed-schema column mapping silently — Stage 1 (Columns) renders briefly with a stage-bar pill labelled Auto-applied and immediately advances. Stage 2 (Cells) only runs if any rows reference an unknown FK (e.g. a numberTypeKey column that didn’t match server-side; for T06’s three-column schema this is rare). Stages 3-5 (Verifying / Processing / Done) follow the standard C05 contract — note that Verifying is always skipped for T06 because the fingerprint check is identity-flag-driven and T06 has no is_self/trustPKs flags. The Verifying pill renders SKIPPED with the standard "skipped — file is from an external system" tooltip.

  4. Terminal redirect. C05 reads importCaller:{uuid} on COMPLETED / FAILED / CANCELLED and redirects to T07 at /inventory/numbers/onboard/imports/{uuid}/summary.

Alternative Flows

  • AF-1 — File missing required column. The fixed-schema validator rejects the upload at POST /api/imports with 400 + a message identifying the absent column. Upload screen surfaces the rejection inline; file is not consumed.

  • AF-2 — Barcode column has empty cells. Permitted: rows without a barcode are accepted as MANUFACTURED with tag_id left NULL — these get paired later via C02. The summary screen reports the count under "rows imported without a tag".

  • AF-3 — Duplicate (numberType, sequence) within the file. Detected during validation; the import job lands in PROCESSING but the duplicate rows accumulate in the row-results bucket and surface in T07's "duplicate sequence" section.

  • AF-4 — (numberType, sequence) already exists in the database. Idempotent — the row is treated as a no-op (state-log entry recorded for audit, no schema change). T07 reports the count under "rows already on file".

  • AF-5 — Cancel during PROCESSING. Standard C05 cancel; T07 renders with CANCELLED state and the partial breakdown.

  • AF-6 — Pause / resume. Standard async-import behaviour; resume via My imports.

Acceptance Criteria

  • Use-case page authored (this page).

  • Status design-todo → in-design → handoff-ready after Claude Design pass and Coordination Backlog resolution.

  • :design-url: populated.

  • Cross-references C05, T07, C02, T02.

  • Fixed-schema banner is visible at the top of the upload screen and identifies the three required columns.

  • No source-system dropdown is rendered.

  • NumberType picker is required; Upload button is disabled without it.

  • On submit the caller key importCaller:{uuid} is written with literal 't06'.

  • C05 column-mapping stage transparently auto-applies (per the Fixed-schema imports decision in C05).

  • Rows without a barcode are accepted and counted separately in T07.

API Surface

Call Purpose

POST /api/imports?importType=NUMBER_ONBOARDING&numberTypeId=X

Create the import job. Multipart upload (file). Server validates the fixed-schema header up-front and returns 400 with the missing column name if invalid.

GET /api/number-types?tenantId=<implicit>

Populate the NumberType picker.

GET /api/imports?importType=NUMBER_ONBOARDING&size=N

Drives the "Recent imports" list (filtered to T06’s importType).

GET /api/race-numbers/templates/manufactured-numbers.csv

Serves the static "Download template" CSV. Tenant-neutral; schema only. (Optional — could be a shipped static asset rather than a dynamic endpoint.)

(delegates to C05 for column / cell / progress)

Shared mapping flow with auto-applied column mapping.

Out of Scope

  • The mapping middle steps — C05.

  • The summary screen — T07.

  • Scanner-based onboarding — C02.

  • Manufacturer-data-file export (the upstream "send sequence to manufacturer" tool) — separate Track 3 screen, not yet authored.

  • Per-tenant fixed-schema variants (e.g. additional colour / additionalInfo optional columns) — backend already accepts these but the .adoc spec keeps the banner copy strict to avoid operator confusion. The optional columns are listed in the Notes section as a forward-looking item.

Design Anchors

Design Decisions

  • Fixed schema, no source-system selector (2026-05-07). Numbers do not have a provenance concept analogous to participants — there is no "WPCA Legacy vs Entry Ninja" axis for manufactured stock. The schema is dictated by the manufacturer’s deliverable shape and is the same across every tenant. Implication for C05: the Fixed-schema imports path applies (see C05’s design decision); column mapping auto-applies silently and the operator never sees Stage 1 as a working step.

  • NumberType is the one required selector (2026-05-07). The CSV doesn’t carry NumberType per row because manufactured batches are NumberType-homogeneous (one batch = one bib design = one type). Putting NumberType on the upload screen avoids a mandatory numberTypeKey column inside the file that the operator would have to populate identically on every row.

  • Barcode column is optional per row (2026-05-07). A row without a barcode is acceptable — it lands as MANUFACTURED with tag_id NULL and is later paired via C02. This is the default state for tag-less stock (race medals, T-shirts that double as numbers, etc.) and the mixed case where some bibs have RFID chips and some don’t.

  • Idempotent re-import (2026-05-07). (numberTypeId, sequence) is the natural key. Re-importing the same file (or a corrected file with overlapping sequences) is a no-op for already-imported rows, with a state-log entry recorded for audit. Rationale: manufacturers occasionally re-send the file with corrections; making T06 idempotent removes a footgun.

  • No update mode (2026-05-07). Unlike E06's Overwrite / Append distinction, T06 has no such toggle. The semantics are always "create-on-miss, no-op-on-hit". If the operator wants to change a paired tag, that’s C02's re-pair flow (AF-2). If the operator wants to dispose of stock, that’s T04. Each lifecycle action has one canonical screen.

  • No identity flags / no fingerprint stage (2026-05-07). T06 carries no trustPKs / updatePII / is_self flags. The C05 Verifying pill renders SKIPPED with the existing tooltip pattern (per project memory feedback_explain_disabled_ui.md). Tooltip text: "Skipped — manufactured-number imports do not need fingerprint verification."

Notes

Backend dependency: requires ImportType.NUMBER_ONBOARDING registered in ImportFieldDefinitionRegistry plus the new NumberOnboardingRowProcessor. Both are Track 3 backend deliverables (see plan § Track 3). Until they land, this page documents the target shape; the screen cannot be implemented end-to-end.

The fixed schema may grow optional columns over time:

  • colour — for tenants that order multi-colour bibs and want the colour recorded against each RaceNumber.

  • additionalInfo — free-text per-row note (e.g. batch-internal lot number).

These are out of the current spec. The banner copy stays strict ("Required columns: sequence, number, `barcode`") so the operator’s mental model is uncluttered; if a future delivery introduces an optional column, the banner is updated explicitly rather than hedging now.

Resolved Decisions

Q-E3 and Q-E4 (the questions parked during initial authoring) were resolved by Coordination Decision CD-8 in the number/tag UI orchestration on 2026-05-07. Both took the proposed Default leans:

  • Q-E3 (backend endpoint): the template CSV ships as GET /api/race-numbers/templates/manufactured-numbers.csv, served by admin-service through the gateway. The SPA download link in the upload screen targets that path. Implementation lives in the backend US (US #733).

  • Q-E4 (row-level validation): sequence validation is row-level (positive integer, up-to-bound check) and surfaced in T07's "Other issues" bucket. Matches the rest of the import flow’s validate-at-row-level convention.