Stock storage refactoring #3172
Replies: 4 comments 5 replies
-
sounds good 👌 but can we please use lists of simple DTOs for the return value of the |
Beta Was this translation helpful? Give feedback.
-
Sounds great. Is there any chance to implement the stock amount as floating point number? We provide with our plugins solutions for merchants with need allow decimal input for quantity (think of bulk ware, goods sold by meter etc.) but there are always limitations. think this has do be supported by Shopware in the core. |
Beta Was this translation helpful? Give feedback.
-
This is not planned and not intended 😉. Dealing with floating prices and floating quantity would lead to many issues and would increase the complexity of the system. We recommend therefore a way where you convert this int values in the view layer to allow float inputs there. |
Beta Was this translation helpful? Give feedback.
-
The new stock handling is now available. Introduced with this commit Thanks for all your input |
Beta Was this translation helpful? Give feedback.
-
Context
The stock handling in Shopware 6 is currently not very flexible and does not support many common use cases.
Decision
We have only one field
stock
in the product definition which always has a real time calculated value.The
stock
value should be correctly updated as an order and its line items transition through the various states. Eg, stock is decremented when an order is placed. If it is cancelled, the stock is increased, and so on.We have a clear API for manipulating stock which can be extended and supports arbitrary data, which could, for example, support features such as multi warehouse inventory.
We have a way to disable the stock handling behavior of Shopware.
New feature flag
We introduce a new feature flag
STOCK_HANDLING
to allow people to opt in to the new stock handling behavior immediately. Otherwise, it will be enabled by default in 6.6.Abstract Stock Storage
We will introduce a new
AbstractStockStorage
. The API will be as follows:The
alter
method receives a list of changes. Each change corresponds to a line item change. It contains the line item ID, the product ID and the before and after quantity of the line item.This API encapsulates all the scenarios an order may transition through.
Introduce a new
BeforeWriteEvent
eventWe will introduce a new event to the
EntityWriteGateway
service. Much likeBeforeDeleteEvent
it will be dispatched before any commands are written. It allows for subscribers to add success and error callbacks via the methods:public function addSuccess(\Closure $callback): void
public function addError(\Closure $callback): void
These callbacks will be executed after the writes have been written to the database and if/when an error occurs, respectively.
Update
\Shopware\Core\Content\Product\DataAbstractionLayer\ProductIndexer
We update
\Shopware\Core\Content\Product\DataAbstractionLayer\ProductIndexer
to depend on\Shopware\Core\Content\Product\DataAbstractionLayer\AbstractStockStorage
as well asShopware\Core\Content\Product\DataAbstractionLayer\StockUpdater
.Iff
STOCK_HANDLING
is enabled then we call the\Shopware\Core\Content\Product\DataAbstractionLayer\AbstractStockStorage::index
method with the IDs of the product which have changed.Otherwise, we call
Shopware\Core\Content\Product\DataAbstractionLayer\StockUpdater::update
Deprecate
Shopware\Core\Content\Product\DataAbstractionLayer\StockUpdater
It will be removed with 6.6.
Introduce
Shopware\Core\Content\Product\Subscriber\StockSubscriber
We introduce a new subscriber which listens to the various required events and interacts with
\Shopware\Core\Content\Product\DataAbstractionLayer\AbstractStockStorage
via it's new API (alter
).All of Shopware's internal business rules for handling stock are located in this subscriber.
The subscriber listens to various events and calls
\Shopware\Core\Content\Product\DataAbstractionLayer\AbstractStockStorage::alter
with the appropriate changesets for the following scenarios:It is possible to disable Shopware's internal stock handling by setting the configuration
shopware.stock.enable_stock_management
to false.Introduce new core stock storage implementation.
We introduce a new implementation of
\Shopware\Core\Content\Product\DataAbstractionLayer\AbstractStockStorage
for managing the stock levels. It is responsible for incrementing/decrementing stock values based on the provided changesets.The new API's will directly increment and decrement the
stock
column on theproduct
table rather than usingavailable_stock
. Therefore, thestock
value will always be a realtime representation of the available stock.The
alter
method will directly update the stock values based on the given deltas in the changesets.The new implementation solves the issue of the current slow stock calculation process which works like so:
stock
vsavailable_stock
is the difference between orders in progress and completed orders.available_stock
is calculated from thestock
value minus open order quantities. This calculation is preformed inShopware\Core\Content\Product\DataAbstractionLayer\StockUpdater::updateAvailableStockAndSales
. It is slow because theSUM
may run over millions of rows.Deprecate StockUpdate Filters
We will deprecate all stock update filters. They will be removed in 6.6.
The same behaviour can be implemented with decorators.
The following classes will be deprecated:
ProductDefinition
updatesIn Shopware version 6.6 or if the
STOCK_HANDLING
feature flag is enabled:availableStock
field is made write protected and will be updated to directly mirror thestock
value.We decide not to remove the
availableStock
field, simply deprecating it with no plan to remove. This is because many integrations rely on this field and it is simple for us to maintain as a mirror ofstock
.To mirror the value we implement a new listener
AvailableStockMirrorSubscriber
for theBeforeWriteEvent
event. It simply updates the payload, copying anystock
value updates to theavailable_stock
field.It is possible to disable this behaviour by setting the configuration
shopware.stock.enable_available_stock_mirror
to false.Update stock loading to use
AbstractStockStorage::load
We update the various locations in Shopware where stock is loaded and augment the product with any stock information that is loaded from the stock storage.
This includes:
Pseudocode for setting the values on the product looks like:
However, in order to support this API, we must update
\Shopware\Core\Content\Product\SalesChannel\Detail\AvailableCombinationLoader::load
because it currently does not pass along theSalesChannelContext
which is necessary forAbstractStockStorage::load
.Therefore, we deprecate
load
inAbstractAvailableCombinationLoader
for 6.6 and introduce:public function loadCombinations(string $productId, SalesChannelContext $salesChannelContext): AvailableCombinationResult
.It is introduced as not abstract and throws a deprecation error if called (eg when the method is not implemented in concrete implementations) in 6.6, otherwise it forwards to
load
. It will be made abstract in 6.6.AvailableCombinationLoader
implements the newloadCombinations
method andload
is deprecated for 6.6.Finally,
ProductConfiguratorLoader
is updated to callloadCombinations
instead ofload
.Stock changing scenarios
The following table contains all the scenarios that should trigger stock changes. All implementations of
AbstractStockStorage
should be able to handler these scenarios.Product 2: 5
Product 2: 55
Product 2: 50
Product 2: -5
Product 2: 5
Product 2: 5
Product 2: 50
Product 2: 55
Product 2: +5
Product 2: 5
Product 2: 5
Product 2: 55
Product 2: 50
Product 2: -5
Product 2: 5
Product 2: 8
Product 3: 1
Product 2: 50
Product 3: 5
Product 2: 47
Product 3: 4
Product 3: -1
Product 2: 8
Product 3: 1
Product 2: 8
Product 2: 47
Product 3: 4
Product 2: 50
Product 3: 5
Product 2: 5
Product 2: 8
Product 2: 50
Product 2: 47
Product 2: 5
Product 2: 1
Product 2: 50
Product 2: 54
Product 2: 5
Product 3: 5
Product 2: 50
Product 3: 10
Product 2: 55
Product 3: 5
Product 3: -5
It is the role of
\Shopware\Core\Content\Product\Subscriber\StockSubscriber
to listen to the required shopware events for these scenarios and then interact with the stock storage implementation.BeforeWriteEvent
-> No items will exist pre insertion, so we know it's a decrement operation)StateMachineTransitionEvent -> $event->getToPlace()->getTechnicalName() === OrderStates::STATE_CANCELLED
)StateMachineTransitionEvent -> $event->getFromPlace()->getTechnicalName() === OrderStates::STATE_CANCELLED
)BeforeWriteEvent
-> filter for order line item writes and diff old and new state)BeforeWriteEvent
-> filter for order line item writes and diff old and new state)BeforeWriteEvent
-> filter for order line item writes and diff old and new state)BeforeWriteEvent
-> filter for order line item writes and diff old and new state)BeforeWriteEvent
-> filter for order line item writes and diff old and new state)Consequences
AbstractStockStorage
.AbstractStockStorage
will provide the required methods for handling stock updates.StockSubscriber
and implement their own solution.Beta Was this translation helpful? Give feedback.
All reactions