Stock level on Product instead product variation

Created on 24 February 2021, over 3 years ago
Updated 28 August 2024, 3 months ago

Hi!

Problem : I sell ticket with variation product( member, non-member ) with 2 different price

I need stock, but i need the stock on the whole ticket product. eg: 100

maybe i will sell 99 member ticket and 1 non-member... i dont want split the stock 50/50 on the product attribute

It's is possible?

i also try to install the rules module for a simple decrementation of a field_my_custom_stock on the product but the "paid in full" don't exist anymore. (i know why now)

Suggestion are Welcome!

Ben

Problem/Motivation

Steps to reproduce

Proposed resolution

Remaining tasks

User interface changes

API changes

Data model changes

πŸ’¬ Support request
Status

Needs review

Version

1.0

Component

Code

Created by

πŸ‡¨πŸ‡¦Canada bensti

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

Not all content is available!

It's likely this issue predates Contrib.social: some issue and comment data are missing.

  • πŸ‡«πŸ‡·France cassien

    I'm interested.
    I need to offer subscription products with multiple recurrence periods. If the product is released for the day, it must also be released for the week and the months since it is temporarily unavailable.

  • πŸ‡¦πŸ‡ΉAustria mvonfrie

    I'm interested as well.
    I have a voucher product with the value chosen by the user. To make it easier for both the users and companies providing the vouchers, there will be a product X with variations like 10€, 20€ and 50€. There is no separate stock for the different variations but only a total value per product.

    Example: Stock for product X is 180€, then this can be:

    • 18 x 10€
    • 9 x 20€
    • 3 x 50€ + 3 x 10€
    • 3 x 50€ + 10€ + 20€
    • ... any other possible combinations
  • πŸ‡¦πŸ‡ΉAustria mvonfrie

    I installed commerce_stock as the dev version is D10 compatible now and looked a bit into the code to understand how things work and how to quirk a solution for my example into my site. As I have a deadline for the project I can't wait until this feature is implemented but have to find a custom solution in the meantime.

    But I have an idea for a concept how to implement this properly. This can't be fully implemented in commerce_stock though as it would require changes in commerce/commerce_product which then are permanent and would make things even more complicated. So the concept will always require custom code by the site developer.

    Currently all stock logic is connected to the PurchasableEntityInterface which basically (only) is the product variation entity (but can be extended by contrib modules to nodes for example). But of course we don't want (and don't need) to make the product entity purchasable in order to let it handle the stock of it's variations.

    So we need two new interfaces plus traits providing their implementations for commerce products:

    
    /**
     * Defines the interface for stockable entities.
     *
     * The entity type implementing this interface should always be implementing \Drupal\commerce\PurchasableEntityInterface
     * as well. An implementation for commerce product variations is provided by StockableProductVariationTrait.
     */
    interface StockableEntityInterface extends \Drupal\Core\Entity\ContentEntityInterface {
    
      /*
       * Returns the entity handling the stock level for this entity.
       *
       *  @return StockedEntityInterface|null
       */
      public function getStockedEntity(): ?StockedEntityInterface;
    }
    
    /**
     * The entity type handling the stock level for a purchasable entity, for example the commerce product for its product variations.
     *
     * An implementation for commerce products is provided by StockedProductTrait.
     */
    interface StockedEntityInterface extends \Drupal\Core\Entity\ContentEntityInterface {
      // Everything we need here for stock handling...
    }
    
    /**
     * Implementation of StockableEntityInterface for commerce products.
     */
    trait StockableProductVariationTrait {
    
      /**
       * {@inheritdoc}
       */
      public function getStockedEntity(): ?StockedEntityInterface {
        $stocked_entity = $this->getProduct();
        return $stocked_entity instanceof StockedEntityInterface ? $stocked_entity : NULL;
      }
    }
    
    /**
     * Implementation of StockedEntityInterface for commerce products.
     */
    trait StockedProductTrait {
      // Everything we need here for stock handling...
    }
    

    Then stock checking and stock processing has to check: The given entity or purchased entity of the given order item

    • implements PurchasableEntityInterface but not StockableEntityInterface --> use it for stock handling
    • implements both interfaces --> use StockableEntityInterface::getStockedEntity()
      • the return value implements StockedEntityInterface --> use that entity for stock handling
      • the return value is NULL --> fall back to the parent entity implementing PurchasableEntityInterface
    • implements StockableEntityInterface but not PurchasableEntityInterface
      • the return value implements StockedEntityInterface --> use that entity for stock handling
      • the return value is NULL --> don't handle stock for it.

    Of course this requires changes to all sub-modules of commerce_stock and is quite complex. But the same time it is very flexible as it allows to decide whether the product variation or product should handle the stock (or maybe a totally different entity implementing StockedEntityInterface and being return by StockableEntityInterface::getStockedEntity()) on a per-bundle basis.

    The site developer would just need to create custom product and product variation classes implementing the interfaces and registering them properly. This can be done using hooks or more easier with the Bundle Class Annotations β†’ module.

    
    /**
     * Custom product variation implementation using delegating its stock handling.
     *
     * @Bundle(
     *      entity_type = "commerce_product_variation",
     *      bundle = "my_product_variation_bundle",
     *      label = @Translation("Stock delegating product variation"),
     * )
    class StockableProductVariation extends \Drupal\commerce_product\Entity\ProductVariation implements \Drupal\commerce_stock\Entity\StockableEntityInterface {
    
      use \Drupal\commerce_stock\Entity\StockableProductVariationTrait;
    }
    
    /**
     * Custom product implementation handling stock for its variations.
     *
     * @Bundle(
     *      entity_type = "commerce_product",
     *      bundle = "my_product_bundle",
     *      label = @Translation("Stock handling product"),
     * )
    class StockableProductVariation extends \Drupal\commerce_product\Entity\Product implements \Drupal\commerce_stock\Entity\StockedEntityInterface {
    
      use \Drupal\commerce_stock\Entity\StockedProductTrait;
    }
    

    It would even be possible to mix this for a product type, for example a book product type with three different variation types book, ebook (like ePub, PDF, ...) and amazon_kindle_book. book and ebook variations could handle their stock on product level by implementing StockableEntityInterface (i. e. using local stock) while amazon_kindle_book handles its stock on the variation by using a custom amazon kindle stock implementation.

  • πŸ‡§πŸ‡ͺBelgium matthieu_collet

    Hello @mvonfrie
    Two months later do you think your solution is working ? We have the same need

  • πŸ‡©πŸ‡ͺGermany a.dmitriiev

    I am trying now to implement this without too much changes. I ended up creating new Stock Service that will return stock of the product whenever the stock of its product variation is requested.

    I still had to override couple of classes and one of them needs the patch that is attached. I will write more when I finish.

  • πŸ‡©πŸ‡ͺGermany a.dmitriiev

    More places where the changes are needed in a new patch

  • πŸ‡©πŸ‡ͺGermany a.dmitriiev

    I assume that product stock is using commerce_stock_local module. Here is the module for tracking stock for product and not product variation based on local stock.

    What module has:
    1. Field storage configs for field_stock_level and commerce_stock_always_in_stock for product entity type.
    2. New service to set for product variation here /admin/commerce/config/stock/settings called "Product local stock". It should still be attached to product variation, because of the module architecture, and I didn't want to change it.
    3. New widget "Simple stock transaction for products" that you should use for field "Stock Level" for product entity type.
    4. Overrides for queue worker class and stock level field type (to use new class for computed stock level processor.

    After you install the module, go to product type that you want to have stock level management. Add 2 fields "Stock Level" and "Always in stock?" from existing fields (the storages are installed with the module). Set widget "Simple stock transaction for products" that you should use for field "Stock Level" for product entity type. Then you need to set here /admin/commerce/config/stock/settings the service called "Product local stock" for your product variation that corresponds to product type.

    On product edit form you will now see two new fields. And that is basically it. Stock level will change whenever any variation that belongs to product will be bought, but level will be attached to the product and not to variation.

    When checking the availability of the product variation, then the stock level for product will be checked as well.

    P.S. Don't forget to apply the patch from #16 to commerce_stock module. Without it the module commerce_stock_product from the comment will not work properly.

  • πŸ‡ΊπŸ‡ΈUnited States rsnyd

    @a.dmitriiev,
    Thank you for the patch and module. I'm working on this same challenge. I've updated commerce_stock with the patch from #16, downloaded, unzipped and installed your commerce_stock_product from #17. I've added the two reusable fields to my default product bundle and set the field widget to "Simple stock transaction for products". Finally, I've configured commerce_stock here: /admin/commerce/config/stock/settings to use the "Product local stock" service for both the default service and the Product Variation.

    The fields show on my product edit form, but I'm unable to change the value of the stock level. No errors, but if I enter 5 into the field and save, Current stock level: remains at 0. I've also tried adding a stock level field to the product variation type for the default product bundle, but that doesn't seem to work either. Can you please suggest what I may be doing wrong?

  • πŸ‡ͺπŸ‡ΈSpain uridrupal

    @rsnyd
    I couldn't make it work, but you're using the wrong Field Widget. You need to use the proper one, I think it's called "Product stock level". Also, the stock is set at variation if I recall correctly, and then it will show properly.

  • πŸ‡©πŸ‡ͺGermany a.dmitriiev

    For product type that should have the stock per product you should have these fields:

    Manage form display page has these 2 fields with corresponding widgets:

    On Stock configuration page the settings are:

    See setting for product variation "Side event".

    As stock management is local, you need also to create location here `/admin/commerce/commerce_stock_location` of any type from here `/admin/commerce/config/commerce_stock_location_type`

    No settings on product variation type are needed.

    Then you can manage stock on product page (not product variation):

    And when you fill the field "Stock level adjustment (all languages)" with 10, to increase the stock on 10 items, and save the product, it will be displayed like this afterwards:

  • πŸ‡ΊπŸ‡ΈUnited States rsnyd

    @a.dmitriiev/@UriDrupal,

    I've followed the screen captures below, installed the fields on the product type, set the configuration for my product variation type, and I've ensured the patch was installed. The field shows on the product edit form, but will not save the value entered. After debugging, I found this error:

    Error message
    Warning: Undefined variable $multiplier1 in Drupal\commerce_stock_product\ProductLocalStockUpdater->createTransaction() (line 69 of modules/custom/commerce_stock_product/src/ProductLocalStockUpdater.php).
    Drupal\commerce_stock_product\ProductLocalStockUpdater->createTransaction(Object, '1', '', 2, 0, NULL, 1, Array) (Line: 125)
    Drupal\commerce_stock\StockServiceManager->createTransaction(Object, '1', '', 2, 0, NULL, 1, Array) (Line: 182)
    Drupal\commerce_stock_field\Plugin\Field\FieldType\StockLevel->createTransaction(Object, Array) (Line: 42)
    Drupal\commerce_stock_product\Plugin\Field\FieldType\StockLevel->postSave(1)
    call_user_func_array(Array, Array) (Line: 233)
    Drupal\Core\Field\FieldItemList->delegateMethod('postSave', 1) (Line: 198)
    Drupal\Core\Field\FieldItemList->postSave(1)
    call_user_func_array(Array, Array) (Line: 938)
    Drupal\Core\Entity\ContentEntityStorageBase->invokeFieldMethod('postSave', Object, 1) (Line: 984)
    Drupal\Core\Entity\ContentEntityStorageBase->invokeFieldPostSave(Object, 1) (Line: 896)
    Drupal\Core\Entity\ContentEntityStorageBase->invokeHook('update', Object) (Line: 56)
    Drupal\commerce\CommerceContentEntityStorage->invokeHook('update', Object) (Line: 564)
    Drupal\Core\Entity\EntityStorageBase->doPostSave(Object, 1) (Line: 781)
    Drupal\Core\Entity\ContentEntityStorageBase->doPostSave(Object, 1) (Line: 489)
    Drupal\Core\Entity\EntityStorageBase->save(Object) (Line: 806)
    Drupal\Core\Entity\Sql\SqlContentEntityStorage->save(Object) (Line: 352)
    Drupal\Core\Entity\EntityBase->save() (Line: 237)
    Drupal\commerce_product\Form\ProductForm->save(Array, Object)

    Removing the $multiplier variable for now allows me to save the value. I will continue testing. @UriDrupal, does this also help you to get it working?

  • πŸ‡©πŸ‡ͺGermany a.dmitriiev

    @rsnyd, I think multiplier is a leftover of my custom code, as the module was copied from solution where there is also people count and quantity is multiplied in case it is a ticket for couples, family, etc.

  • Status changed to Needs review 3 months ago
  • πŸ‡ΈπŸ‡°Slovakia kaszarobert

    Removing that invalid $multiplier variable fixes the problem and your solution with the attached module from commerce_stock_product.tar_.gz + patch #16 works fine. With needing patch #16 it seems the module is not flexible enough for setting up other entities for stock level management. There are entity_id and entity_type columns in the commerce_stock_transaction database table, so the schema can handle this use case but the module cannot.

Production build 0.71.5 2024