Skip to content

[upstream #4628] [RFC] feat(core): Add partitionKey to StockLevel for sub-location stock tracking - #37

Open
ayim wants to merge 9 commits into
masterfrom
mirror/upstream-4628
Open

[upstream #4628] [RFC] feat(core): Add partitionKey to StockLevel for sub-location stock tracking#37
ayim wants to merge 9 commits into
masterfrom
mirror/upstream-4628

Conversation

@ayim

@ayim ayim commented Jul 28, 2026

Copy link
Copy Markdown
Owner

Mirrored from vendurehq#4628 for the Overwatch review demo.

Original author: @niko91i


Context

This PR is a working proof-of-concept related to a discussion on Discord:
https://discord.com/channels/1100672177260478564/1488690570296692909

The current StockLevel entity enforces a unique constraint on [productVariantId, stockLocationId],
meaning only one stock record can exist per variant per location. This forces use cases like batch/lot
tracking, serial number management, or expiration-based stock rotation to create one StockLocation
per batch — polluting a table designed for physical locations.

At scale (e.g. 10,000 vendors × 30 products × 6 batches/year = 1.8M rows/year), the stock_location
table becomes unmanageable for its original purpose.

Proposed solution

Add an optional partitionKey column to StockLevel, allowing multiple stock records for the same
variant+location pair, each identified by a unique key.

  • partitionKey defaults to '' (empty string) — existing behavior is 100% unchanged
  • The unique index becomes [productVariantId, stockLocationId, partitionKey]
  • LocationWithQuantity gains optional partitionKey and stockLevelId fields
  • StockLevelService methods accept an optional partitionKey parameter
  • StockMovementService propagates partitionKey through all allocation/sale/release/cancellation flows

Changes

  • stock-level.entity.ts — New partitionKey column (default '') + updated unique index
  • stock-location-strategy.ts — Optional stockLevelId and partitionKey on LocationWithQuantity
  • stock-level.service.ts — Optional partitionKey parameter on getStockLevel, updateStockOnHand, updateStockAllocated
  • stock-movement.service.ts — Propagate partitionKey through 5 call sites
  • GraphQL schema — partitionKey on StockLevel type and StockLevelInput

What does NOT change

  • StockLocation entity — untouched
  • StockLocationStrategy interface — method signatures unchanged
  • DefaultStockLocationStrategy / MultiChannelStockLocationStrategy — no modifications
  • All existing applications without partitionKey work identically

Test plan

  • 822 existing unit tests pass (zero regression)
  • 11 new unit tests for StockLevelService with/without partitionKey
  • 5 new E2E tests verifying partition creation, coexistence, and targeted updates
  • 59 existing stock E2E tests pass (stock-control, stock-location, multi-location)

niko91i added 9 commits April 4, 2026 17:47
  - Default partitionKey to '' instead of omitting from where clause,
    preventing non-deterministic cross-partition matches
  - Add JSDoc documenting default partition behavior, asymmetric
    creation semantics, and stockLevelId/partitionKey precedence rule
  - Add GraphQL description to StockLevelInput.partitionKey field
  - Fix non-deterministic unit test assertion
  - Capture initial stock values in E2E test for precise comparison
  The documented precedence rule (stockLevelId > partitionKey) was not
  implemented in the service layer. Rather than adding complexity,
  remove stockLevelId entirely — partitionKey alone is sufficient to
  identify a specific StockLevel partition.
  StockMovement records now track which stock partition they affect,
  enabling per-batch/lot movement history and full traceability.
…eview feedback

  Replace hardcoded empty-string defaults with a named constant as suggested by @DanielBiegler.
  Exported from @vendure/core for use in custom StockLocationStrategy implementations.
  The MultiChannelStockLocationStrategy.forAllocation() previously used
  find() to get a single StockLevel per location, ignoring other partitions.
  Now uses filter() to iterate over all partitions at each location,
  allocating from each until the requested quantity is fulfilled.

  Updated BaseStockLocationStrategy.getLocationsBasedOnAllocations() to
  group by locationId+partitionKey for correct sale/release/cancellation.

  Added 8 unit tests covering multi-partition allocation, partition-aware
  sale/release/cancellation, and single-partition backward compatibility.
  StockLocationService.delete() now matches and creates StockLevel records
  by partitionKey when transferring stock to another location, preventing
  partition merge and preserving batch traceability.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants