Product Catalog API

This is the API that is used to list available products from the database.

It has methods to return a queryset of products or shop products with prices annotated.

The product catalog object receives a context parameter in the object constructor in order to configure the product filtering:

# configure a context for a shop and only pull purchasable products
catalog_context = ProductCatalogContext(shop=my_shop, purchasable_only=False)

# create the catalog instance with the configured context
catalog = ProductCatalog(context=catalog_context)

# iterate over all the products and print their price (for the given context)
for product in catalog.get_products_queryset():
    print(product.catalog_price)

The Catalog API was built to be a layer on top of all the custom product pricing module to simplify and make the access to the prices performant in huge catalogs.

The Catalog API should always be used while rendering products on a template or returning them through a REST API, as it is possible to filter, sort and paginate the queryset easily.

To access the products prices in a quick way, they are indexed in the ProductCatalogPrice model. Every time a product is saved in the admin, a signal (on_object_saved) is triggered and the ProductCatalog will index the changed product.

The ProductCatalog will call the current pricing module to index prices for the given shop product and after that it will trigger the signal index_catalog_shop_product which can be then handled by other apps to execute other procedures like bumping caches.

The indexed prices contain a shop, a supplier and optionally a price rule. The price rule is basically a context when the price should be available, e.g., when a specific contact group or a specific contact is doing the request.

The discounted prices will also be indexed. The ProductCatalog will call every discount module to index the product discounted price. Every discount module should index its own discounted price. The best discounted price will be used. Discounted prices also have their catalog rules, which means discounts can be indexed by contact group and/or contact.

It’s the responsibility of the supplier modules to listen to the index_catalog_shop_product signal and index the availability of the product, updating the ProductCatalogPrice model instance accordingly. If the supplier modules don’t do that, the product won’t be marked as available (purchasable) and it won’t be visible in listing when the ProductCatalogContext is configured to have purchasable_only flag set to True.

As the pricing and discount index task is a heavy process, Shuup must be configured to have tasks running in a background executor, like Celery.

How prices are indexed

After a product is saved in admin, the ProductCatalog will call the current pricing module to index the price of the changed shop product.

The index price (saved inside ProductCatalogPrice) should always match the price of the product returned by the get_price_info() of the pricing module.

The pricing module is responsible by indexing all possible prices for all different contexts, which can be a different price for a specific group and/or contact.

How discounted prices are indexed

After a product is saved in admin, the ProductCatalog will call all the active discount modules to index the price of the changed shop product.

The index price (saved inside ProductCatalogDiscountedPrice) should always match the discounted price of the product returned by the discount_price() of the discount module.

The discount module is responsible by indexing all possible discounted prices for all different contexts, which can be a different discounted price for a specific group, contact, date range and time range.

How availability of products are indexed

After a product is saved in admin, the ProductCatalog will call all the suppliers of the shop product to update their availability. The availability is saved inside the ProductCatalogPrice model, as a product without price is also not available for purchase.

The supplier module must update the is_available flag of every existing ProductCatalogPrice instance related to the shop product.