Views filter don't work with computed fields out of the box.

We will likely need to create a custom filter plugin for this.

First we should investigate, if there are some implemantations in contrib.

Comments

MrPaulDriver created an issue. See original summary.

mrpauldriver’s picture

Title: Views integration: Exposed filter for stock level does not returns empty view » Views integration: Exposed filter for stock level returns no results
guy_schneerson’s picture

Thanks for reporting Paul.
This is because the stock level is a calculated field and does not hold a value that views can interrogate. We will probably need to create a Views Filter Plugin to handle this.

olafkarsten’s picture

Title: Views integration: Exposed filter for stock level returns no results » Views integration: Create Stock Level Filter Plugin
Category: Bug report » Feature request
Issue summary: View changes
langelhc’s picture

Subscribing...

olafkarsten’s picture

@langelhc - no need to comment for subscribing to an issue. Have a look at the right sidepane (or footer on mobile), there is a "Follow" link with a star icon somewhere

alyaj2a’s picture

I have a question, Are you planning to implement the field to be stored in the DB?

olafkarsten’s picture

@alyaj2a

No, the field will not be stored in DB. Commerce stock is transaction based. That means we have a journal with every stock transaction that took place. We query that table just in time we need the level. That is why we will not save the absolute level to db.

alyaj2a’s picture

@olafkarsten , I'm understand, But Is there some way create filter criteria in views with the level stock field? Or use the stock level field in sort criteria?

olafkarsten’s picture

Status: Active » Closed (outdated)

feel free to reopen

czigor’s picture

Status: Closed (outdated) » Active

Reopening this as it's still impossible to add a stock filter to a variation view. E.g. "show variations that have stock larger than 100".

ahkhoon’s picture

I am having the same issue, I am promoting some products at front page with View and I wouldn't want to show products which are currently out of stock. There is no way to filter by stock level.

I know programatically one can get the stock level by:
$stockServiceManager->getStockLevel($variation)

remaye’s picture

Hi, I'm interested too in having access to stock level in a view (filtering, sorting).
Does anybody have a clue on how to use $stockServiceManager->getStockLevel($variation) for that ?
Thanks.

ericchew’s picture

I have a similar situation where I want to create a view for showing variations that are "Low on Stock". We have a variation field called "low_quantity" that i'd like to compare to the current stock level. If the current stock level is less than "low_quantity", then the variation would show in the View table. A few details about my setup...we only have 1 stock location and only hold stock levels on product variations.

I was able to get this to work, but there are a few hoops to jump through...and it is not something that would work for everyone. Here are the details of what I've learned about how this module works and the quirks of using views with it.

  • The "field_stock_level" that you create on a variation can DISPLAY the stock level just fine. This field does not hold a real value in the database...so you can't use it for any meaningful filtering or sorting. The value it actually holds in the database (commerce_product_variation__field_stock_level table) is just the last value it was set to in the widget. For example, if you edit a product variation and use the field widget to add +10 stock, a transaction is created and the fields value in the database is just set to 10. When displaying the field, the value is calculated based on the transaction data, the "field_stock_level" value in the DB is not utilized. If you create a product variation view and try to sort based on stock, you'll see that the order is all wrong because its sorting on the value 10 instead of the real value (unless you only have one transaction on every variation).
  • The closest thing in the database to the real stock level is in the "commerce_stock_location_level" table. This table holds the location_id, entity_id, entity_type, qty, and last_transaction_id. The problem is this table does not contain real-time data, it is updated in the background via a WorkerQueue. There is an issue for improving the worker queue here: https://www.drupal.org/project/commerce_stock/issues/2783289. Below is where you can find the code where this happens

With all that being said, my solution was:

  • Expose commerce_stock_location_level to views using hook_views_data_alter. There is an issue here which talks about doing this: https://www.drupal.org/project/commerce_stock/issues/3053647
  • Create an event subscriber that subscribes to LocalStockTransactionEvents::LOCAL_STOCK_TRANSACTION_INSERT so that on EVERY transaction, the stock location level is updated.
       /** @var \Drupal\commerce_stock_local\LocalStockUpdater $updater */
        $updater = $this->localStockService->getStockUpdater();
        $updater->updateLocationStockLevel($event->getTransactionLocation()->getId(), $event->getEntity());

    the localStockService is injected into the event subscriber, this is the service commerce_stock.local_stock_service

  • Next, I created a view of Product Variations, added a relationship to the commerce_stock_location_level, added the qty field to the view using that relationship. Next, I was able to add the Global: Fields Comparison filter for "Stock Level Qty is less than Low Quantity

I'm not sure if it is possible to create a views filter for something that is not stored in the database, so this was my solution. Hopefully it helps.

*EDIT*
I'd also like to add a few points from my slack conversation with @olafkarsten and @Guy Schneerson.

This was my observation as to how the real-time stock level is calculated / displayed:

As far as I can tell, the stock_location_level holds the value of the stock level as of a certain transaction ID. The stock location checker does the calculation looking at the last transaction where it was set in that table, then calculates the actual amount based on any transactions that happened after.

I asked why the stock_location_level is not updated on every transaction, this was the response from @olafkarsten:

The stock level calculation must be ultrafast :slightly_smiling_face: . The calculation of the stock level is actually cheap. We don't wont to trigger a write each time we calculate the stock level.
[The] goal is a enterprise ready stock management. Thats why it is transaction based. You need to keep certain data for each transaction for whatsoever reason. And the data must be immutable.
Cron as the way to go, was choosen long before we implemented transaction events. And I think we probably keep at that way as long as we have no performance tests. On sites with a lot transactions, we would trigger a db write for each transactions. It's somewhat difficult to decide which way is the right one. The answer depends on ...
If you have a site with not so much transactions, the event subscribe is okay.

josh.stewart’s picture

@ericchew would you be willing to share your code from your solution above? I've got a similar problem that I need to figure out and would love a good boost. Commerce is very new to me and the more code I can look at the better I think. I'm in Drupal slack at jmstewart if that is easy enough. Would appreciate all the help I can get here.

dubs’s picture

In my option, stock filtering is ESSENTIAL for views. There are SO many use cases for requiring stock level information in views.

That being said, here's an alternative solution that works for me. For the reasons mentioned already in related tickets, this may not be the best idea for high traffic sites....

Create a new field on the product variation, and then create an event subscriber for LocalStockTransactionEvents::LOCAL_STOCK_TRANSACTION_INSERT

This simply grabs the current stock level like this: -

  /**
   * @param \Drupal\commerce_stock_local\Event\LocalStockTransactionEvent $event
   *
   * @throws \Drupal\Core\Entity\EntityStorageException
   */
  public function onStockTransactionInsert(LocalStockTransactionEvent $event) {
    /** @var \Drupal\commerce_product\Entity\ProductVariationInterface $variation */
    $variation = $event->getEntity();
    $stockManager = \Drupal::getContainer()->get('commerce_stock.service_manager');
    $stockLevel = $stockManager->getStockLevel($variation);
    $variation->set('field_cached_stock_level', $stockLevel);
    $variation->save();
  }

Then you can use the field in Views for filtering, displaying, etc. One advantage for displaying is no overhead for stock transactions which the default stock field will use.

Bearing in mind the above disclaimed, use at your own risk - it works for me :-)

  • guy_schneerson committed 44a2ec9 on 8.x-1.x
    Issue #3003801 by MrPaulDriver, guy_schneerson: Views integration: Add...
guy_schneerson’s picture

I have updated the views integration so an implicit relationship is added from location level to product variation. This will allow adding a "Quantity" under the "Local Stock Location Level" category to both the sort and filter.
In order for the stock location to be up to date, you will need to set "Stock aggregation mode" to "Real-time" in a new config screen under admin/commerce/config/stock/local_stock_config
The only remaining issue will be that products that have not had any transaction (stock activity) will not show. This has a core issue that once fixed will resolve this Base field filter operators do not include "Is empty (NULL)" and "Is not empty (NOT NULL)"

Please test and let me know if this fixes the issue?

netzkombuese’s picture

Hi.

As this is working now for us and we are able to sort and filter on Quantity we just now get this when running Cron:

Error: Call to a member function id() on null in _commerce_stock_local_update_stock_level_queue() (Zeile 92 in /var/www/vhosts/netzkombuese-entwicklung.de/hexdev/modules/contrib/commerce_stock/modules/local_storage/commerce_stock_local.module)

We just added updates only patch from 22 (https://www.drupal.org/project/commerce_stock/issues/3211778) to the latest dev to be able to run update.php which was not able before the patch.

Any ideas what went wrong here?

Thanks

guy_schneerson’s picture

Status: Needs review » Fixed

I am glad this is working for you so marking it as fixed. but if anyone finds an issue they can reopen.
The 3211778 had a small issue and a patch was provided by @mitrpaka and I just committed that change. A new dev version will automatically be created by drupal.org in the next day and the update hook will start working corectly.

netzkombuese’s picture

Hi and sorry we have to reopen this. We have now installed the last dev version. Unfortunately we still get the same error message when we start the cron.

Error: Call to a member function id() on null in _commerce_stock_local_update_stock_level_queue() (Zeile 92 in /var/www/vhosts/netzkombuese-entwicklung.de/hexdev/modules/contrib/commerce_stock/modules/local_storage/commerce_stock_local.module)

Any ideas whats wrong here?

EDIT:

Just thought we got it by going back to alpha 6 and reinstalling dev but the error reoccured after a few minutes.
So even with the actual dev version we get the error listed above. This does not appear with alpha6 which does not have the needed functionality. What are we doing wrong?

Thanks

netzkombuese’s picture

Status: Fixed » Needs review
guy_schneerson’s picture

Status: Needs review » Fixed

Hi @netzkombuese
The error you are getting is not related to this issue. However, it is a new one and I think I figured it out in https://www.drupal.org/project/commerce_stock/issues/3248964 and will be easy to fix.

Status: Fixed » Closed (fixed)

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