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.

State: planningType: investigationPrepared: 2026-08-10Scope: Size v1, generic foundationProduct code changed: none

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.

Highest operational risk: wrong-size fulfillmentToday a positive parent total can mask a stockout in the requested size. Every quantity-changing path needs explicit variant context and availability checks at that same grain.

Verified current evidence

591,380parent stock rows observed
41,200parents with active inventory
6,741active serialized inventory rows
895,642product identifier rows

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.

Planning gateTask-0206 stays in planning. Six product decisions—especially barcode policy, migration handling, and customer-document presentation—must be recorded before it can move to ready. No branch, worktree, CI, push, PR, or product-code work is authorized.

Evidence legend and current-state facts

Verified from code/schema/read-only metadataRecommended design / planned behaviorProduct decision requiredFurther technical investigation needed before buildHigh-risk control
Current findingEvidence statusPlanning implication
StockItem / stockslc is keyed by parent stock_no and owns parent catalog, on-hand, allocation, price/cost, supplier, barcode, UOM, and reorder values.VerifiedA 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.VerifiedAdd 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.VerifiedPreserve 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.VerifiedThis 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 investigationComplete 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 / factAuthoritative owner after changeGrainCompatibility behavior
Parent stockExisting stock recordOne stock_noUnchanged for a non-variant product; family catalog identity for a variant product.
Variant set / valuesNew variant-set, attribute, variant, and value tablesParent × enabled attribute/value combinationSize only in v1; relational shape can support future axes without enabling them.
Inventory blocks and detail ledgerExisting inventory system extended with stock_variant_idLocation × parent × variant × cost block × serialLegacy null is allowed only for products never converted to variant mode.
Operational documentsPO, receipt, order, pick, invoice/history, count, variance, transfer/requisition/EOQ recordsDocument line × parent × variantClosed parent-only history remains valid and explicitly parent-only.
Position/read modelVariant position projection/APIWarehouse × inventory location × parent × variantParent on_hand/allocated remains a derived rollup until consumers migrate.
Identifier registryNew variant identifier records; existing parent identifiers retainedVariant × identifier type × normalized valueParent identifiers still resolve legacy items and may open a controlled picker for variant parents.
Database invariant, not UI conventionA simple variant foreign key is insufficient: it could point to a variant owned by a different parent. Enforce parent/variant consistency with a composite constraint or trigger, and include the variant key in the same locking transaction used for inventory reduction.

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.

1

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.

2

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.

3

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”.

4

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.

5

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.

6

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/resultNon-variant parentVariant-enabled parentRequired response
Typed parent stock numberCurrent stock result and normal transaction behavior.Parent family result; show Size matrix on demand.Browse may remain parent-level; mutation forces selection.
Scanned parent barcodeCurrent 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 barcodeNot applicable in current model.Exact variant lookup.Resolve one active sellable variant; display parent + size confirmation.
Unknown / duplicate / inactive barcodeExisting 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 quantityCurrent 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.
Barcode decision still requiredRecommended policy: every active sellable size has a unique variant barcode where supplier data permits; a parent barcode always opens the Size chooser. The product owner must decide whether any parent scan can map to a default size.

Before and after workflow

BEFORE — parent stock number is the only quantity identity Parent stockstock_no · price · UPC · reorderone family-level identity PO / receive / countstock_no + quantityno Size on transaction Inventory blockslocation · stock · cost · serialno variant key Parent availabilitypositive family total can hidea requested-size stockout AFTER — explicit variant before any quantity changes Parent stockcatalog/defaults/parent reportone parent stock_no Size variantsimmutable ID · active stateUPC/SKU can resolve one Required selectionchooser or exact barcodewrong/inactive rejected Variant ledgerparent + variant + locationcost block + serial retained Fulfill exact Sizeavailable / allocated / pickreturn / count / transfer Variant demand, reorder, margin, auditauthoritative operational and analytical grain Parent rollup compatibilitysum variants + legacy rows for familiar reads

Operational behavior by workflow

WorkflowRequired behavior for a variant parentRisk/controlStatus
Product setupEnable 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 / POSearch 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
ReceivingReceive 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 entryParent 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 / fulfillReserve, 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 / varianceParent 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 / requisitionMove the same variant from origin to destination; requisition demand is variant-specific.Transfer cannot convert Size; any substitution is a separate, approved transaction.Proposed
ReturnsReturn 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.

QuestionSafe proposed answerWhat 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.
Inventory correctness ruleFor a variant-enabled parent, the system must never subtract Size 9 to fulfill a Size 10 demand just because the parent total is positive. This applies equally to allocation, pick, count variance, transfer, return, and adjustment.

Pricing, weighted/standard cost, accounting, and reporting grain

TopicProposed behaviorEvidence / decision status
Sell price & discountsInherit 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 costKeep 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 costIf 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 costKeep 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 journalsCarry 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 reportingDefault 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 reportingOffer 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
Cost policy decisionThe plan supports inherited parent pricing in v1. It does not yet approve variant price or standard-cost overrides. Product and accounting owners must decide whether any Size-specific price/cost exception is required at launch and how it is governed.

Comprehensive impact matrix

AreaWhat changesControl / compatibilityConfidence
Data / schemaVariant-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
InventoryVariant-tagged inventory blocks, details, positions and locks.Parent totals remain derived projections; no second ledger.Current gap verified
AllocationReduce/reserve by variant as well as parent/location/cost/serial.Prevent cross-size fulfillment and negative-size masking.Current gap verified
ReceivingPO-selected Size creates/updates matching variant inventory/cost block; split cartons and mismatch flow.Permission + reason + requested/received audit.Design
PurchasingPO 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 entryParent search opens chooser; exact barcode selects Size; line/pick/invoice carry variant.Variant availability is authority; substitute/partial/backorder explicit.High risk
FulfillmentPick, ship, unpick, return, and backorder become variant-grain.Show Size on warehouse/customer documents per product decision.Design
ReturnsReturn matching sold/picked Size; exception resolution if unknown.Never put returned units into another Size.Trace legacy paths
Adjustments / counts / transfersAll mutation forms accept resolved variant; import supports variantId/barcode.Ambiguous parent count queues for resolution; transfer preserves Size.Design
Barcode / SKUVariant identifier registry and exact scanner resolution; optional per-size supplier SKU.Normalized uniqueness; parent barcode opens chooser, never a guess.Policy needed
Pricing / costParent defaults inherited; receipt-lot cost receives Size provenance; optional overrides future-ready.Effective dates, permissions, invoice snapshot, accounting review.Policy needed
ReplenishmentDemand, 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
SearchSmall parent result with lazy availability matrix; exact indexed barcode lookup.Avoid broad joins that multiply stock-list rows.Design
ReportingExplicit parent rollup and Size drilldown dimensions; historical labeling.No fabricated size split for parent-only pre-rollout history.Design
API / imports / exportsAdd 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 / auditVariant setup, retire/reactivate, identifier/price/replenishment override, mismatch resolution, cost viewing.Server/API enforcement plus reason, actor, source, label snapshot.Design
IntegrationsExternal 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
PerformanceNew indexes, position projection, lazy matrix fetch, exact identifier lookup, reconciliation jobs.Query-plan/load/concurrency tests and parent-parity metrics before caching.Design
Migration / rollbackControlled 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 trainingStock 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

1

Foundation / dark read

Deploy schema, integrity, position projection, flags, permissions, telemetry. No product enters variant mode and null remains legacy.

2

Read-only pilot

Selected locations configure/display size matrices. Compare parent rollups against current totals; alert on parity differences, no mutations.

3

Transaction pilot

Use pre-cleaned parents. Reconcile active, reserved, on-order, and on-PO quantities; dry-run count and receipt; enable controlled flows.

4

Reporting / replenishment

Move pilot families to Size-grain demand, EOQ, reports, exports, and integration contracts while retaining parent parity dashboards.

5

Controlled expansion

Expand only after staff training, integration confirmation, reconciliation metrics, and a rollback/kill-switch rehearsal.

6

Failure-safe rollback

Disable new variant creation/selection if required, preserve posted facts, use cohort exception queues. Never erase or reparent historical variant movements.

Conversion gateDo not enable variant mode for a parent until all active, reserved, on-order, and on-PO quantity can be assigned to an explicit Size. The recommended policy is to block activation rather than manufacture an “Unspecified” Size. Whether a temporary quarantined exception is allowed is a product decision.

Phased implementation slices

SliceDeliverableExit evidence
A — Domain foundationSchema/migrations, constraints, audit metadata, flags, permissions, positions API/read model.Composite integrity and parent-total parity tests; no legacy-flow change.
B — Stock setup & identifiersSize matrix, variant lifecycle, SKU/barcode resolution, label/scan behavior.Browser/accessibility proof; parent barcode requires choice; identifier uniqueness tests.
C — Purchase & receivingVariant PO lines, split/mismatch receipt, inventory block creation, accounting provenance.Receive/unreceive/cost tests and audited mismatch workflow.
D — Sales, allocation, counts & transfersSize 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 & integrationsVariant demand/reorder/EOQ, reports/exports, OpenAPI/events, partner versions.Parent rollup parity and pilot Size drilldown evidence.
F — Pilot & rolloutConversion/reconciliation, training/manuals, observability, staged enablement.Dry-run, read-only parity, pilot acceptance, rollback rehearsal.

Risks and mitigations

RiskMitigation
Parent total permits sale of unavailable SizeVariant is mandatory for every mutable flow; parent total is read-only compatibility data for variant products.
Partial conversion leaves unknown inventoryHard conversion gate, auditable exception queue, idempotent/resumable backfill; no automatic Size assignment.
Barcode collision or ambiguous parent scanNormalized unique identifier registry; exact variant resolution; required picker/error, never availability-driven guessing.
Cost/financial traceability disappearsCarry variant through detail/document/journal provenance; reconcile parent totals and cost before pilot expansion.
Performance degrades in stock search/reportingIndexes, lazy availability matrix, exact identifier lookup, bounded read model, query plans/load tests.
Integration drops SizeCapability/version flags, additive reads, reject unsafe writes, staged partner validation and monitoring.
Staff picks wrong SizeSize-first UI, barcode confirmation, mismatch permission/reason, labels, training and pilot walkthroughs.
Scope expands to color/widthGeneric model but Size-only release; separate product decision before enabling additional axes.

Product decisions still required

DecisionRecommended answerWhy it blocks planning completion
Variant attributes in v1Size only; generic internal model, no Color/Width activation.Controls data model, UI, migration, labels, reporting, and scope.
Parent barcode policyEvery 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 pricingInherit parent price in v1; defer overrides absent a concrete business case.Determines price table, permissions, snapshot, and customer-display scope.
Replenishment target policyVariant/location target and EOQ for enabled families, starting with pilot locations.Determines buyer workflow and authoritative configuration grain.
Migration exception policyPrefer no activation until physical quantities are manually mapped; decide whether a quarantined unclassified exception is allowed.Sets rollout safety and operational burden.
Customer document presentationShow 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 exceptionsParent 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

LayerRequired proof
Database/modelOwnership, normalized value/identifier uniqueness, inactive/reused rejection, composite parent consistency, null-legacy compatibility, immutable display snapshot.
Inventory ledgerReceive/correction, cost layers, allocation, pick/ship, return, transfer, adjustment, variance/count preserve Size; parent total equals variants + legacy; no cross-size negative masking.
Documents/accountingPO/order/receipt/invoice/journal retain parent + variant provenance; inherited price and approved override snapshots; closed-history immutability.
Barcode/search/UIParent scan requires choice; exact Size barcode skips chooser; unknown/ambiguous/inactive behavior; location-specific availability; keyboard/scanner and accessibility browser tests.
API/import/integrationLegacy reads remain valid; variant writes require variantId; idempotency; malformed/ambiguous CSV rejection; OpenAPI/event/version contract tests; partner capability tests.
Performance/concurrencyHot-path indexes/query plans; concurrent allocation, variance, receive and transfer for same/different Sizes; matrix/search load; reconciliation checkpoints.
Pilot operationsRead-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