POS planning impact report · task-0206
Size-variant inventory under one stock number
A comprehensive planning report for shoes and similar products: retain one parent stock number, but make Size a first-class, independently tracked inventory identity. This is not implementation approval.
Executive summary
Recommended model
Create an opt-in variant set under an existing parent stock_no. Each active size has an immutable stock_variant_id. The parent remains the catalog, supplier-default, parent-price, and familiar reporting identity; operational quantity work becomes size-grain.
For a variant-enabled parent, a parent total may be displayed and rolled up, but it must never authorize receiving, allocating, picking, transferring, counting, adjusting, returning, or selling an unspecified size.
Verified current evidence
Read-only production schema/count metadata, collected 2026-08-10; no business records in the investigation. Current inventory has no variant key. These are scope indicators, not rollout targets.
Evidence legend and current-state facts
| Current finding | Evidence status | Planning implication |
|---|---|---|
StockItem / stockslc is keyed by parent stock_no and owns parent catalog, on-hand, allocation, price/cost, supplier, barcode, UOM, and reorder values. | Verified | A variant must be additive; parent fields become defaults and compatibility rollups, not a second ledger. |
inventory_items identifies physical stock using warehouse/location, stock_no, inventory location, cost/effective date, totals, and optional sn; it has no variant column. | Verified | Add variant provenance to inventory blocks and ledger-detail paths; serial remains an additional dimension, not a size substitute. |
Inventory detail updates sum active inventory by (location_cd, stock_no) back into parent on-hand. | Verified | Preserve the parent projection while adding an authoritative variant position calculation and matching locks. |
Search, allocation, stock move, PO, receipt, order, invoice, count, variance, EOQ, barcode, and API surfaces use stock_no. | Verified | This is a cross-cutting story; stock setup alone cannot safely deliver it. |
| Exact legacy table/foreign-key mapping, accounting posting path, and all partner capability contracts need migration-design verification. | Further investigation | Complete the schema map and integration inventory before generating migrations or committing an interface contract. |
Data model, ownership, and grain
Current: one parent is the operational identity
- Catalog & transaction key: parent
stock_no. - Physical position: warehouse × inventory location × parent stock × cost/effective block × serial (optional).
- Availability: parent on-hand/allocated; sizes cannot be distinguished.
- Result: a user can see stock for the family without proof of the requested size.
Proposed: parent plus immutable size identity
- Catalog owner: one parent
stock_no; name, description, supplier defaults, UOM, default price. - Sellable/operational key: immutable
stock_variant_id, belonging to exactly one parent. - Physical position: warehouse × inventory location × parent × variant × cost/receipt lot × serial (when applicable).
- Compatibility: parent totals = sum of its variants plus legacy null-variant rows.
| Object / fact | Authoritative owner after change | Grain | Compatibility behavior |
|---|---|---|---|
| Parent stock | Existing stock record | One stock_no | Unchanged for a non-variant product; family catalog identity for a variant product. |
| Variant set / values | New variant-set, attribute, variant, and value tables | Parent × enabled attribute/value combination | Size only in v1; relational shape can support future axes without enabling them. |
| Inventory blocks and detail ledger | Existing inventory system extended with stock_variant_id | Location × parent × variant × cost block × serial | Legacy null is allowed only for products never converted to variant mode. |
| Operational documents | PO, receipt, order, pick, invoice/history, count, variance, transfer/requisition/EOQ records | Document line × parent × variant | Closed parent-only history remains valid and explicitly parent-only. |
| Position/read model | Variant position projection/API | Warehouse × inventory location × parent × variant | Parent on_hand/allocated remains a derived rollup until consumers migrate. |
| Identifier registry | New variant identifier records; existing parent identifiers retained | Variant × identifier type × normalized value | Parent identifiers still resolve legacy items and may open a controlled picker for variant parents. |
Parent entry and barcode scanning: required interaction
This is the user-visible control point most likely to prevent wrong-size transactions. It is deliberately explicit: the system never chooses a size based on available quantity, an old default, or a scan heuristic.
User types or scans parent stock
Proposed Resolve the parent. If it is non-variant, preserve today’s behavior. If it has active size variants, return a compact availability matrix rather than adding an unspecified line.
System checks operation context
Proposed Search/browse may show the parent and its rollup. Any mutation—PO, receipt, order, pick, count, adjustment, transfer, return—must require a valid selected variant.
User chooses a Size
Proposed The chooser shows active values, availability relevant to the location, and any restrictions. It stores opaque variantId, not text such as “9”.
Variant barcode is scanned
Proposed Exact normalized lookup resolves one active parent + variant and skips the chooser, subject to the same permission and lifecycle checks.
Ambiguous or unavailable result
Required control Ambiguous parent scan forces selection. Unknown/inactive barcode errors. An unavailable chosen size cannot be allocated/sold; an explicit substitute or backorder flow is required.
Audit the selected intent
Proposed Persist parent, variant ID, immutable label snapshot, actor, timestamp, source, and reason for overrides/mismatches. A renamed size must not rewrite history.
| Input/result | Non-variant parent | Variant-enabled parent | Required response |
|---|---|---|---|
| Typed parent stock number | Current stock result and normal transaction behavior. | Parent family result; show Size matrix on demand. | Browse may remain parent-level; mutation forces selection. |
| Scanned parent barcode | Current parent barcode lookup. | Parent lookup only—not a size guess. | Decision Recommended: required Size chooser; reject a default-size policy unless explicitly approved. |
| Scanned size barcode | Not applicable in current model. | Exact variant lookup. | Resolve one active sellable variant; display parent + size confirmation. |
| Unknown / duplicate / inactive barcode | Existing error behavior is a compatibility baseline to confirm. | Unsafe to add any line. | Structured unknownVariantBarcode / ambiguity / inactive error; no partial posting. |
| Selected size has no available quantity | Current parent availability path. | Parent total may still be positive in a different size. | Block allocation/fulfillment for that size; offer only explicit approved substitute, partial, or backorder behavior. |
Before and after workflow
Operational behavior by workflow
| Workflow | Required behavior for a variant parent | Risk/control | Status |
|---|---|---|---|
| Product setup | Enable Tracks variants; define ordered Size values; create/retire immutable variants; optionally add per-size supplier SKU/barcode; preview matrix. | Publishing blocked until active/reserved/on-order quantities are mapped; no silent “unspecified” Size. | Proposed |
| Purchasing / PO | Search parent, select Size before adding a line; split planned quantity across sizes as separate variant-grain lines visually grouped by parent. | PO line must keep parent + variant. Supplier SKU shown for chosen size; parent grouping is presentation only. | Current parent-only Variant change |
| Receiving | Receive into the PO-selected Size and create/update a variant inventory block with actual cost/receipt provenance. Mixed cartons become split receipts. | Different received Size requires permission, reason, and requested-versus-received snapshot; do not quietly rewrite the PO intent. | Proposed |
| Sales / order entry | Parent search opens Size choice; size barcode adds exact variant. Store variant ID before order line creation; display parent stock + Size. | Do not use parent availability to add/allocate a Size. Unavailable Size is explicit partial/backorder/substitute choice. | Current parent-only High risk |
| Allocation / pick / fulfill | Reserve, pick, ship, unpick, backorder and return against the selected Size’s position/blocks. | Reduction predicates and locks include variant; no cross-size reduction to make a parent total work. | Current predicates lack variant |
| Adjustment / count / variance | Parent scan with several Sizes moves to a resolution queue; variant barcode or explicit Size enables posting; variance applies only to that Size. | Parent-level physical total must be decomposed before apply. Every override has actor/reason/audit. | Proposed |
| Transfer / requisition | Move the same variant from origin to destination; requisition demand is variant-specific. | Transfer cannot convert Size; any substitution is a separate, approved transaction. | Proposed |
| Returns | Return into the sold/picked Size where known; otherwise require a resolution/exception policy. | Prevent Size 10 return from silently increasing Size 9. Exact legacy return behavior needs technical mapping. | Further investigation |
Inventory items, availability, allocation, and cost blocks
Verified inventory_items currently carries parent stock_no, physical location, cost/effective date, totals, and optional serial number. InventoryItemDetail is the change ledger and parent totals are recomputed from inventory blocks by parent.
Proposed Add nullable stock_variant_id to inventory blocks and every ledger/document path that changes or claims inventory. The variant is selected before position lookup and is carried to detail/audit rows. Physical lot and serial behavior remain intact, so the effective grain becomes location × parent × variant × cost/receipt lot × serial.
| Question | Safe proposed answer | What remains to confirm |
|---|---|---|
| Where does a received Size live? | A variant-tagged inventory block under the same parent, location, cost/effective-date model—not a cloned parent stock or UOM. | Exact per-location table names, constraints, and receipt/unreceive write sequence. |
| How is availability calculated? | Variant on-hand minus variant allocations, using the same transactional boundaries/locking semantics as the current inventory system. Parent availability is a display/report rollup. | Exact allocation status arithmetic and every bulk-path query to migrate. |
| Can serial tracking coexist? | Yes. Serial is an optional unit identifier beneath a Size variant; it is not a variant substitute. | Serialized receive/return/count branch coverage and uniqueness constraints. |
| How are parent totals retained? | Atomically derive existing parent fields from all of that parent’s variant blocks plus legacy null-variant blocks during compatibility rollout. | Recompute timing, locking contention, and rollup-view/cache design under load. |
| Can legacy inventory have null variant? | Yes only for a product that has never entered variant mode. Conversion must resolve active/reserved/on-order quantities before enablement. | Whether product policy permits a quarantined unclassified exception path. |
Pricing, weighted/standard cost, accounting, and reporting grain
| Topic | Proposed behavior | Evidence / decision status |
|---|---|---|
| Sell price & discounts | Inherit parent price/discount behavior in Size v1. If allowed later, variant override is an effective-dated, permission-gated record with an invoice snapshot—not a cloned stock master. | Decision Recommended to defer Size-specific pricing unless a concrete business case requires it. |
| Receipt / actual cost | Keep current receipt-lot inventory cost authoritative. The selected variant flows through inventory block/detail and document provenance, so COGS and variance can be filtered by Size. | Current cost blocks exist Confirm accounting write path |
| Weighted average cost | If the current system displays/calculates a parent weighted average, retain it only as a derived parent rollup. A variant weighted average, if displayed, derives from that variant’s own receipts/blocks; never copy parent average into every Size. | Proposed invariant Confirm current costing algorithm and report consumers |
| Standard cost | Keep a parent standard cost as the v1 default if that is current policy. A per-size standard-cost override needs explicit business ownership, effective dating, permissions, and accounting impact approval. | Decision / investigation |
| COGS, variance, financial journals | Carry parent, variant ID, immutable Size label, source document, and cost-block provenance through postings/snapshots; report and audit can then filter by Size without changing closed history. | Proposed Map exact journal/posting schemas |
| Parent reporting | Default familiar reports to parent stock. Totals include all active variants and legacy null-variant inventory; pre-rollout history remains clearly labeled parent-only. | Compatibility policy |
| Variant reporting | Offer explicit Size breakdown for on hand, allocated, available, sales, margin, returns, aging, demand, on-order, exceptions, and replenishment. Use immutable label snapshots for historical readability. | Required for operational truth |
Comprehensive impact matrix
| Area | What changes | Control / compatibility | Confidence |
|---|---|---|---|
| Data / schema | Variant-set, attribute/value, identifier, and replenishment settings tables; add variant keys/snapshots across mutable facts. | Composite parent/variant integrity; legacy null only before conversion. | Need verified |
| Inventory | Variant-tagged inventory blocks, details, positions and locks. | Parent totals remain derived projections; no second ledger. | Current gap verified |
| Allocation | Reduce/reserve by variant as well as parent/location/cost/serial. | Prevent cross-size fulfillment and negative-size masking. | Current gap verified |
| Receiving | PO-selected Size creates/updates matching variant inventory/cost block; split cartons and mismatch flow. | Permission + reason + requested/received audit. | Design |
| Purchasing | PO line carries Size; parent groups line presentation; supplier code can be size-specific. | Require selection before line write; split quantities intentionally. | Current parent-only |
| Sales / order entry | Parent search opens chooser; exact barcode selects Size; line/pick/invoice carry variant. | Variant availability is authority; substitute/partial/backorder explicit. | High risk |
| Fulfillment | Pick, ship, unpick, return, and backorder become variant-grain. | Show Size on warehouse/customer documents per product decision. | Design |
| Returns | Return matching sold/picked Size; exception resolution if unknown. | Never put returned units into another Size. | Trace legacy paths |
| Adjustments / counts / transfers | All mutation forms accept resolved variant; import supports variantId/barcode. | Ambiguous parent count queues for resolution; transfer preserves Size. | Design |
| Barcode / SKU | Variant identifier registry and exact scanner resolution; optional per-size supplier SKU. | Normalized uniqueness; parent barcode opens chooser, never a guess. | Policy needed |
| Pricing / cost | Parent defaults inherited; receipt-lot cost receives Size provenance; optional overrides future-ready. | Effective dates, permissions, invoice snapshot, accounting review. | Policy needed |
| Replenishment | Demand, on-order, safety/reorder, EOQ and exceptions by Size/location. | Do not net a Size 8 surplus against Size 11 shortage; targets remain database-backed. | Current parent-only |
| Search | Small parent result with lazy availability matrix; exact indexed barcode lookup. | Avoid broad joins that multiply stock-list rows. | Design |
| Reporting | Explicit parent rollup and Size drilldown dimensions; historical labeling. | No fabricated size split for parent-only pre-rollout history. | Design |
| API / imports / exports | Add optional read fields; writes require variantId in variant mode; version schemas/events/CSV. | Stock-only legacy reads remain; unsafe mutations rejected with structured errors. | Design |
| Permissions / audit | Variant setup, retire/reactivate, identifier/price/replenishment override, mismatch resolution, cost viewing. | Server/API enforcement plus reason, actor, source, label snapshot. | Design |
| Integrations | External catalog, AQ/Magento, RFID/scanners, batch stock, labels, AI/reorder, warehouse/mobile, BI extracts. | Capability/version flag; additive reads; reject writes from partners that omit required variant context. | Inventory partners |
| Performance | New indexes, position projection, lazy matrix fetch, exact identifier lookup, reconciliation jobs. | Query-plan/load/concurrency tests and parent-parity metrics before caching. | Design |
| Migration / rollback | Controlled conversion, pilot cohort, idempotent checkpointed backfill and exception queue. | Kill switch stops new selection/creation; never erase posted variant history as rollback. | High risk |
| User training | Stock setup, scan confirmation, size matrix, split PO/receipt, mismatch approval, count resolution, reporting grain. | Pilot walkthrough and quick-reference help; role-specific permission training. | Required |
Compatibility contract for existing non-variant stock
What must not change
- Non-variant stock continues to search, scan, sell, receive, count, transfer, report, and integrate by the existing parent
stock_no. - Existing stock-number URLs and stock-only API reads stay valid.
- Parent on-hand, allocation, price/cost displays, and parent reports remain familiar rollups.
- Closed invoices, receipts, inventory details, financial journals, and historical reports retain their original meaning.
What changes only after opt-in conversion
- A variant-enabled parent may return a parent rollup for display but rejects stock-only quantity mutations.
- A Size must be selected/resolved before a mutable line is created.
- Old ambiguous stock-only writes become structured validation errors, not silent fallback to parent inventory.
- Known historical mapping may be backfilled with provenance; unknown history remains parent-only.
Migration, backfill, rollout, and rollback
Foundation / dark read
Deploy schema, integrity, position projection, flags, permissions, telemetry. No product enters variant mode and null remains legacy.
Read-only pilot
Selected locations configure/display size matrices. Compare parent rollups against current totals; alert on parity differences, no mutations.
Transaction pilot
Use pre-cleaned parents. Reconcile active, reserved, on-order, and on-PO quantities; dry-run count and receipt; enable controlled flows.
Reporting / replenishment
Move pilot families to Size-grain demand, EOQ, reports, exports, and integration contracts while retaining parent parity dashboards.
Controlled expansion
Expand only after staff training, integration confirmation, reconciliation metrics, and a rollback/kill-switch rehearsal.
Failure-safe rollback
Disable new variant creation/selection if required, preserve posted facts, use cohort exception queues. Never erase or reparent historical variant movements.
Phased implementation slices
| Slice | Deliverable | Exit evidence |
|---|---|---|
| A — Domain foundation | Schema/migrations, constraints, audit metadata, flags, permissions, positions API/read model. | Composite integrity and parent-total parity tests; no legacy-flow change. |
| B — Stock setup & identifiers | Size matrix, variant lifecycle, SKU/barcode resolution, label/scan behavior. | Browser/accessibility proof; parent barcode requires choice; identifier uniqueness tests. |
| C — Purchase & receiving | Variant PO lines, split/mismatch receipt, inventory block creation, accounting provenance. | Receive/unreceive/cost tests and audited mismatch workflow. |
| D — Sales, allocation, counts & transfers | Size selection/search, reserve/pick/return/backorder, moves, adjustments, counts, mobile/API payloads. | Concurrency and negative-availability proof; count/transfer cannot cross Sizes. |
| E — Replenishment, reports & integrations | Variant demand/reorder/EOQ, reports/exports, OpenAPI/events, partner versions. | Parent rollup parity and pilot Size drilldown evidence. |
| F — Pilot & rollout | Conversion/reconciliation, training/manuals, observability, staged enablement. | Dry-run, read-only parity, pilot acceptance, rollback rehearsal. |
Risks and mitigations
| Risk | Mitigation |
|---|---|
| Parent total permits sale of unavailable Size | Variant is mandatory for every mutable flow; parent total is read-only compatibility data for variant products. |
| Partial conversion leaves unknown inventory | Hard conversion gate, auditable exception queue, idempotent/resumable backfill; no automatic Size assignment. |
| Barcode collision or ambiguous parent scan | Normalized unique identifier registry; exact variant resolution; required picker/error, never availability-driven guessing. |
| Cost/financial traceability disappears | Carry variant through detail/document/journal provenance; reconcile parent totals and cost before pilot expansion. |
| Performance degrades in stock search/reporting | Indexes, lazy availability matrix, exact identifier lookup, bounded read model, query plans/load tests. |
| Integration drops Size | Capability/version flags, additive reads, reject unsafe writes, staged partner validation and monitoring. |
| Staff picks wrong Size | Size-first UI, barcode confirmation, mismatch permission/reason, labels, training and pilot walkthroughs. |
| Scope expands to color/width | Generic model but Size-only release; separate product decision before enabling additional axes. |
Product decisions still required
| Decision | Recommended answer | Why it blocks planning completion |
|---|---|---|
| Variant attributes in v1 | Size only; generic internal model, no Color/Width activation. | Controls data model, UI, migration, labels, reporting, and scope. |
| Parent barcode policy | Every sellable Size gets a unique barcode where possible; parent barcode requires a Size chooser. | Defines scan safety and whether default-size behavior is ever permissible. |
| Size-specific pricing | Inherit parent price in v1; defer overrides absent a concrete business case. | Determines price table, permissions, snapshot, and customer-display scope. |
| Replenishment target policy | Variant/location target and EOQ for enabled families, starting with pilot locations. | Determines buyer workflow and authoritative configuration grain. |
| Migration exception policy | Prefer no activation until physical quantities are manually mapped; decide whether a quarantined unclassified exception is allowed. | Sets rollout safety and operational burden. |
| Customer document presentation | Show parent stock number plus Size on sales/packing/invoice lines; parent grouping only in planning views. | Controls documents, exports, returns, and customer-facing behavior. |
| Cost exceptions | Parent defaults/actual receipt cost in v1; decide whether any Size-specific standard cost or price exception is required. | Requires accounting ownership before implementation. |
Test and verification plan
| Layer | Required proof |
|---|---|
| Database/model | Ownership, normalized value/identifier uniqueness, inactive/reused rejection, composite parent consistency, null-legacy compatibility, immutable display snapshot. |
| Inventory ledger | Receive/correction, cost layers, allocation, pick/ship, return, transfer, adjustment, variance/count preserve Size; parent total equals variants + legacy; no cross-size negative masking. |
| Documents/accounting | PO/order/receipt/invoice/journal retain parent + variant provenance; inherited price and approved override snapshots; closed-history immutability. |
| Barcode/search/UI | Parent scan requires choice; exact Size barcode skips chooser; unknown/ambiguous/inactive behavior; location-specific availability; keyboard/scanner and accessibility browser tests. |
| API/import/integration | Legacy reads remain valid; variant writes require variantId; idempotency; malformed/ambiguous CSV rejection; OpenAPI/event/version contract tests; partner capability tests. |
| Performance/concurrency | Hot-path indexes/query plans; concurrent allocation, variance, receive and transfer for same/different Sizes; matrix/search load; reconciliation checkpoints. |
| Pilot operations | Read-only parent parity, controlled dry-run, metric/error review, user-led receive → sale → count walkthrough with non-sensitive pilot data, kill-switch rehearsal. |
Implementation testing note: No implementation tests were run because this is planning-only. When build is approved, Rails verification must use the repository’s Docker test convention after the implementation batch is complete.
Planning sources
- Validated investigation — repository and read-only schema/count evidence.
- Validated plan — recommendation, slices, gates, risks, and decisions.
- Validated workflow diagram — self-contained before/after visual.