Skip to content

Product Bundles ​

Flow ID: CF-24 | Module(s): product_bundles, Product domain | Complexity: Medium

Business Overview ​

Bundles create rule-based discounts when specific product combinations are in the cart. Uses criteria/reference system with two behaviors and two pricing strategies.

Key concepts:

  • Bundle Behavior: OneTime (1, applies once) or Recurring (2, all matching quantities)
  • Pricing Strategy: Constant (1, fixed discount) or Scalar (2, tiered by quantity)
  • Reference Types: ProductId (1), ProductCodeId (2), CategoryId (3), VendorId (4)

API Reference ​

REST Endpoints ​

MethodPathAuthDescription
GET/rest/product/bundleGuestList bundles (active-only for storefront callers)
GET/rest/product/bundle/{id}GuestGet bundle with relations (404 if inactive, for storefront callers)
POST/rest/product/bundleBackendCreate bundle
GET/rest/product/bundle-criteriaGuestList criteria
GET/rest/product/bundle-pricingGuestList pricing tiers
GET/rest/product/bundle-displayGuestList display settings
GET/rest/product/bundle-builder-referenceGuestList references

Storefront read scoping (added #511): index() and item() on /rest/product/bundle are storefront-scoped — enforceStorefrontBundleScope() forces the isActive filter to 1 for non-backend callers (guest and customer) via the domain-layer mandatory-filter mechanism (withMandatoryFilter(), backed by GenerateListRequest::forceFilter()), so a storefront-supplied filter[isActive]=0 is overridden rather than combined with it. show() additionally 404s when the requested bundle is inactive and the caller is not backend. Backend/admin access is unfiltered. Mirrors the Review/Wishlist storefront-scoping pattern (see CF-16, CF-17).


Cart Integration ​

  1. Labeling: labelBundledProducts() — load active bundles → sort by weight → check criteria → label items
  2. Pricing: applyBundlePricingToCartLiveData() — calculate bundle_save_price, adjust final_price
  3. Order: Applied bundles saved to shop_order_basket_options_applied_bundles

Discount formula: instancedProductSavePrice = (price × qty) × (discount/100)


Domain Layer ​

ComponentPath
Bundle Servicesrc/Domains/Product/Bundle/Service.php
Bundle WriteServicesrc/Domains/Product/Bundle/WriteService.php
Sub-entitiesCriteria, Pricing, Display, BuilderReference, CriteriaReference
BundleBehavior Enumsrc/ProductBundles/BundleBehavior.php

Client Extension Points ​

  • PRODUCT_BUNDLES.ENABLED: Feature gate
  • Weight priority: Higher weight = processed first
  • Custom criteria: Override BundleReferenceSet matching
  • HATEOAS API: AdvApiProductBundlesAdmin exposes _links for client discovery