Problem/Motivation

We currently support multiple locations but those need to correlate to stores.

Proposed resolution

The changes have two main layers:

1. Architecture

The interfaces need to have the store provided as a parameter when getting stock location information.
This is relevant to StockServiceConfigInterface getAvailabilityLocations() and getTransactionLocation()

2. Implantation

Each store will have a primary location
Each store will have a list of locations available for fulfilment (this is for checking of stock not for creating transactions)
To support multiple stores you must add the following fields to your store (we may automate this later on):

  • Available stock locations (field_available_stock_locations) – unlimited
  • Stock allocation location (field_stock_allocation_location) - 1

Remaining tasks

PR #27 added support for multiple stores however we have a number of improvements we can make.

  • Currently fields need to be added by hand. We can then look at UI for enabling support by store type and create the fields by code.
  • More testing especially under real life use cases
  • Use at using a resolver for determining the location.
CommentFileSizeAuthor
#20 2858391-19.patch718 bytesedurenye

Comments

guy_schneerson created an issue. See original summary.

guy_schneerson’s picture

A few notes:

  • getLocationList() in StockServiceConfigInterface should be renamed to getAvailabilityLocations() as it returns locations that can be checked for availability.
  • Also the getPrimaryTransactionLocation() can be renamed to getTransactionLocation() although the implementation is currently the same as primary, it may be changed by other services to allow for overflow functionality like returning the first location with sufficient stock from getAvailabilityLocations(). This is not something we will attempt at this point as it can require more information like other order details to find out if an order can be split into multiple fulfilment locations and we are currently only working on individual line items.

also added related issues. read those for related discussions

guy_schneerson’s picture

Even better then providing the store I will use the commerce Context as that holds the store but also the customer and that can potentially be used by other implementations.

guy_schneerson’s picture

Status: Active » Needs review

Created pull request for the first part of the work: architectural changes only no implementation
https://github.com/BBGuy/commerce_stock/pull/27

steveoliver’s picture

Status: Needs review » Active

I posted this in the child issue #2838050: Add further possibilities to filter stock locations available for stock allocation, but it is relevant here because the two issues are so intertwined, at least conceptually and architecturally.

Re-posting here:

OK, It's been a few months, and I haven't worked on it recently, but I'm posting my Work in Progress (WIP) PR -- https://github.com/BBGuy/commerce_stock/pull/35/files -- which addresses at least initially most, if not all, of the issues touched on in this issue.

  1. config/schema/commerce_stock.schema.yml - stock service configurations are sets of stock services with weights, and filters for entity types, order types, stores, locations, etc. This way stock services (here plugins) can be instantiated multiple times, in a list of configured services, where business logic can loop through and find availability and transaction locations.
  2. src/Resolver/ChainStockServiceResolver.php - stock services are resolved at runtime for a given entity, context and quantity.
  3. src/StockAvailabilityChecker.php - stock availability is checked through 'getAvailabilityLocations' method we identified as needing attention in this issue.
  4. src/StockServiceConfig.php (deleted) - stock service configuration is no longer a static object, it is a set of stock service plugins paired with properties for determining at runtime the correct service for a given entity, context, and quantity.
  5. src/StockServiceManager.php - stock service manager is no longer just a static list of services but the entrypoint for getting stock services at runtime.
  6. Each stock service implements one or more interfaces related to the types of transactions supported
  7. Each stock service, when instantiated in a stock service configuration, can be configured to support the available transaction(s) for each case (again, based on criteria in the stock service configuration, e.g. store, customer, order or product type)

I may have forgotten a few notes, but those are the big ones. It still needs more work to finish, but this is the direction I've headed and would love to discuss / get help on. I probably won't be able to work on this for at least another few months. If anyone would like to discuss, please find me on #commerce in drupal.slack.com or in the issue queue here.

guy_schneerson’s picture

Hi @steveoliver I was also busy lately and will not have to much time in the next 9 weeks but I will find some time to go over this as I want us to find a direction to push towards.

heddn’s picture

Priority: Normal » Major
Issue tags: +Contributed project blocker

Tagging. This is blocking #2613264: Drupal 8 port of Commerce Stock. And bumping priority because of that.

guy_schneerson’s picture

finished reviewing other issues this is next on my list

guy_schneerson’s picture

Re https://github.com/BBGuy/commerce_stock/pull/35 I like the resolver aprouch but the commit does much more than add support for multiple stores and it removes key functionality that is currently used. I recommend we look at using a service resolver at as a separate exercise and go with https://github.com/BBGuy/commerce_stock/pull/27 for now.
If anyone has any other ideas please engage with me ASAP as we need to make a move on this.

rob c’s picture

Ive reviewed both, figured 35 needs a reroll anyway and indeed too much in one PR. Do like it more then the other one but until the reason for the change is clear cant disagree. Lets create soms separate (followup) issues for the major things from the PR we really want / need.

guy_schneerson’s picture

Thanks Rob
I have retested PR 27 and it needed one small change to bring it up to API changes in commerce 2.0.

guy_schneerson’s picture

What I am going for now is:
To support multiple stores the user will need to add the following two reference fields to relevant store bundles:

  • Available stock locations (field_available_stock_locations) – unlimited
  • Stock allocation location (field_stock_allocation_location) - 1

At a later stage we may automate the creation of the fields.
The logic will be if fields exists then it is used otherwise use the LocalStockServiceConfig default (currently all enabled locations and transactions created against the first one) so normal for single store shops it will work out of the box and anyone needing multiple store support will need to create the fields.

guy_schneerson’s picture

Status: Active » Needs review

The changes are now working and ready for testing but please note the following:
to support multiple stores you must add the following fields to your store (we may automate this later on):
* field_available_stock_locations, Entity reference to Stock Location, Unlimited
* field_stock_allocation_location, Entity reference to Stock Location, Limited: 1
Issues identified:

  • The Stock Level Formatter does not work because \Drupal::service('commerce_store.current_store')->getStore() dose not return the correct current store
  • The same issue on editing of a product, we do not know what store we are on. this one is more complicated as the product edit page lets you set the store(s) it belongs to.
guy_schneerson’s picture

I have added code to make sure the current store is supported by the PurchasableEntity. If not it will use the first store associated with the product. This solved both issues I have encountered so far although the store selection should ideally be removed from the product edit form as its value in the UI may not be the same as the persistent value and can cause data entry issues.

guy_schneerson’s picture

Following testing by Rob C I have changes the behavior in cases were the current store is not supported by the PurchasableEntity then the edit widget will not be displayed. The previous behavior is still availalbe as an option on the widget by setting "Context fallback" to TRUE.

  • guy_schneerson committed 664f9a0 on 8.x-1.x
    Issue #2858391 by guy_schneerson: implement Support for multiple stores
    
  • guy_schneerson committed 7c16a6e on 8.x-1.x
    Merge branch for Issue #2858391 by guy_schneerson, Rob C: Add Support...
  • guy_schneerson committed 981dd1f on 8.x-1.x
    Issue #2858391 by guy_schneerson, Rob C: by default disable stock...
  • guy_schneerson committed 9d7cf40 on 8.x-1.x
    Issue #2858391: Add Support for multiple stores...
  • guy_schneerson committed f46b9ab on 8.x-1.x
    Issue #2858391 by guy_schneerson, Rob C: Improved conditions
    
  • guy_schneerson committed fac45ca on 8.x-1.x
    Issue #2858391 by guy_schneerson, Rob C: Make sure store is associated...
guy_schneerson’s picture

Issue summary: View changes

Good news, we now have multi store support. See #12 or issue description for setup instructions

edurenye’s picture

The commit 'fac45ca ' is throws an exception when the current store is not set because in '$store_to_use->id()' $store_to_use is null.

So this patch fixes the issue.

edurenye’s picture

StatusFileSize
new718 bytes

Forgot the patch.

guy_schneerson’s picture

Thanks @edurenye
I can not get $store_to_use to be null on my setup but adding the check can only make the code more robust. Would have liked to be able to recreate but not a must.
The check needs adding to both getContext() and isValidContext() as the two share 90% of the code they should probably be combined to make sure they do not drift apart.

rob c’s picture

I had to think about this a bit more and i have some questions / comments:

  1. Should a stock location always have a store id set ?
  2. Should 'commerce_stock_location's be able to work without 'commerce_store' ? (other entity types)
  3. Can an order exist without setting a store id?
  4. Can an order be create by default without an existing store? (out of the box)
  5. Can person at source X select target stock location at ...? (or should that occur at the target store)

What i think / what i've found:

  1. I believe it should. Any stock transaction occurs at a location, that's the store i believe.
  2. I believe it should not and should always use a store. The code that runs when the fields exist actually use store code, so can't depend on non-commerce_store entities.
  3. Yes, but for sure not by default and prolly needs a custom order type + neat overrides to commerce core to pull it off. (users are forced to create a store before creating an order for the default order type).
  4. Nope. On regular commerce installs (if not all) a commerce_store entity exists (at least one). (relevant for out-of-the-box)
  5. Long story short: A person should select the target store and not be aware of the stock locations at that store. The process that runs on the target should assign a stock location when receiving the stock (if none exists yet or multiple exist).

I believe by now that with most code already (possibly) depending on the store entity it should be changed to always use a store. I propose to add a new property on the stock location that holds the store_id for that stock location, remove the available stock location fields on the store and add a 'store' select widget when creating / editing a stock location.

I got like 55 more lines of reasoning, including things like: access to stock locations depending on store, building UI's to transfer / list stock, transferring stock (and what to select), creating stock movement reports per store and more, but let's see about this first, might be enough to expose what i think could use some more work.

guy_schneerson’s picture

Thanks Rob for your thoughts
1. Should a stock location always have a store id set ? No as locations can be not store related like a warehouse or a repair center and the stock transaction system allows for moving stock between those locations supporting back end supply chain functionality that's separated from the store(s).
2. Should 'commerce_stock_location's be able to work without 'commerce_store' ? (other entity types) - Yes same as (1)

3. Can an order exist without setting a store id? & Can an order be create by default without an existing store? (out of the box) Those are commerce core questions and I believe you are corect as list that's my assumption.

5. Can person at source X select target stock location at ...? (or should that occur at the target store) - If I understand you correctly then the answer is both Yes and No.
We have two possible workflows:
Beck end: We are currency providing an API and an admin interface where all is possible

front end / store(s): This is the out of the box logic I am going for
1) A single location for stock allocation should always be available.
2) One or more locations for checking stock availability should always be available.
The reason we are supporting more then one location for checking is that it is common for a store to want to make a product available as long as they have it somewhere. However this aprouch will likely need a richer back-end.

  • guy_schneerson committed a9a7144 on 8.x-1.x
    Issue #2858391 by edurenye, guy_schneerson: check for current store is...
guy_schneerson’s picture

@edurenye thanks for your patch I used it as the bases for the above commit.

rob c’s picture

I figured you would say something like this :)

like a warehouse or a repair center

Exactly why i asked. In my mind any stock location needs a store first. Not to sell products, but to have a location.

The commerce_store module might have been named commerce_location (cause that's how i look at it). A store of type 'warehouse' is very common in my setup, even stock location that's reserved at the manufacturer is a stock location + store of type manufacturer in my system.

And then permissions. The person moving stock might not even be able to view the stock location on the target side, while they can select the store. (and i wonder if they should be able to view the stock location on the target side anyway, but that's more from a process perspective, because moving stock now requires only an action on the source, while the target should be responsible for selecting the final stock location where the goods will be stored when receiving the stock. The source now selects where the product will be stored on the target, feels odd, not their responsibility).

No sour grapes, it's a choice and implementing as i propose might be done in a new independent module, just think we might debate this a bit, so it's clear for everybody how and why. For a simple webshop with only a single group of administrators the current implementation will work fine, but i wonder about more complex systems. I (for example) (with the current system) would need to limit the list of stock locations people can use in some way and everything is attached to a 'store', users, permissions for related entities, etc, except stock. So now i would need to implement something on top of stock and store to hook these 2 up. With the locations attached to a store this is totally different story.

When working with remote stock locations this might become a problem (i see why this choice is made), but stores are about to get a status field (with a bit of luck). And a different store type already offers a way to add per-type stock location settings (for remote stock api's etc).

See #2921000: Add a status field (enabled/disabled) to stores for the store status issue.

guy_schneerson’s picture

Thanks Rob
I think its early days for stores and I currently see them in the more conventional way - A place that sells stuff.
I totally agree that permissions is an issue and as stores mature in that aria I am planning to follow.
My first goal is to make stock work for "mom and pop shop" and give all the base tools for more involved systems.
Any functionality that's blocking custom development will have high priority to go in and I will do my best to follow how the module is used and any common patterns will be candidate for changes/future versions.
I currently can not see why a location should be tagged with a store(s). I also do not believe in duplicate relationships so its Location->store or store->location we should use, unless the relationships are of a different nature.
A site can also resolve such issues in other ways like only allow creating of locations from a store and linking the two. I personally think that for many of my own use cases, a lot of the entity & relationship creation and management will be automated so for a mark place site it may be: A user registers for the service and a store and location are automatically created and linked together.

rob c’s picture

Understood and agreed.

Simpele use case of why:

Create 25 stores with each 5 stock locations with the exact same name. Think people might not be amused with the current UI. Plus more stock locations only makes this worse. No way to group them logically. And prolly sorted by id or name.

Again i understand why the choice is made, so lets continue.

guy_schneerson’s picture

Hi Rob, I think your use case and most of the others I can think of are UI issues not data modeling / API. also in your case if a business has 25 stores and over a hundred locations he can afford the data entry or to pay someone like you or me to develop a kick ass interface :)

olafkarsten’s picture

A store in commerce is a billing location. https://drupalcommerce.org/blog/42419/commerce-2x-stories-stores

So a stock location is not a 1:1 relationship to store. Imagine two stores - a french one and a german one. Two warehouses - one for the french shop and one for the german shop and a repairshop that repairs items from both stores. As you cannot sell products / place orders without a store, each location belongs to at least one store, but can belong to multiple stores. I didn't see, how a stock location can't belong to a store. It's a many2many relationship.

Any functionality that's blocking custom development will have high priority to go in and I will do my best to follow how the module is used and any common patterns will be candidate for changes/future versions.

So we should dig out pr35. I feel this will give us, most if not all of the extension points we need. It uses the plugin system and the resolver pattern. Commerce core is using this all over the place, so it's good for DX too. I will see if I can get steve back on this - at least to get us started again.

olafkarsten’s picture

Status: Needs review » Fixed

I think we can close this one and handle the individual issues.

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.