Skip to main content

Product Bundles

Overview​

Product Bundles sells several products as one purchase. A bundle is either fixed — a set list of products the customer buys together — or mix-and-match, where the customer chooses from a pool up to a quantity the bundle sets.

The discount is expressed on the bundle: a percentage off, a fixed amount off, or a per-item reduction. Bundle contents are re-priced whenever the cart totals are calculated, so the saving follows the products rather than being written once.

The cart treats a bundle as a group: the child lines are tied to their parent, removing the parent removes the children, and the bundle's own quantity control governs the whole group.

It is for stores selling kits, hampers and build-your-own sets.

Availability​

ItemValue
Module keybundles
TierPremium
Entitlement keybundles
Admin tabbundles
Enabled optionaiowc_module_enabled_bundles (off until turned on)
REST namespaceaiowc/v1

Enabling the module creates the three tables below, seeds the defaults and schedules the inventory sync.

registerHooks() performs no licence check of its own; the module registry applies the entitlement gate centrally before calling it.

Settings​

Stored in the bundled option row aiowc_bun_settings.

Stored keyDefaultMeaning
enable_quantity_editingtrueWhether the customer may change item quantities inside a bundle.
default_pricing_typeper_itemThe discount type a new bundle starts with.
show_savingstrueWhether the saving is shown against the bundle price.
min_discount_display5The smallest saving worth displaying; below this the saving is not shown.

Bundles​

FieldMeaning
title, slug, descriptionWhat the bundle is called and how it is described.
bundle_typefixed or mix_match.
discount_typepercentage, fixed or per_item.
discount_valueThe amount applied, read according to the discount type.
min_quantity, max_quantityFor a mix-and-match bundle, how many items must and may be chosen. 0 means no maximum.
is_activeWhether the bundle is offered.

Each bundle item names a product, optionally a variation, a default quantity, its own minimum and maximum, an optional per-item discount override, and whether the item is optional — an optional item may be left out of the bundle by the customer.

For a mix-and-match bundle, the pool of products the customer may choose from is kept in its own table, separate from the fixed item list.

Stock​

A bundle's availability is derived from its contents: the module carries a stock service that resolves what can actually be built from the components, and a background job keeps that in step with product stock. woocommerce_check_cart_items re-checks the cart, so a bundle whose component ran out after being added is caught before payment.

Admin screen​

The Bundles tab lists the bundles and creates, edits and deletes them through a validated form — a bundle needs a name, and the discount is checked as a number within sane bounds, with the reason shown inline when it is not. Bundle items are managed per bundle, and the module's four settings are edited from the same screen.

REST API endpoints​

All routes are on aiowc/v1 and answer with the { success, data, message } envelope. Every route requires manage_woocommerce except the cart route.

MethodPathPurpose
GET/bundlesEvery bundle.
POST/bundlesCreate a bundle.
GET/bundles/{id}One bundle, with its items.
PATCH / PUT /POST/bundles/{id}Update a bundle. All three verbs are registered.
DELETE/bundles/{id}Delete a bundle.
POST/bundles/{id}/itemsAdd an item to a bundle.
PATCH / PUT /POST/bundles/items/{id}Update a bundle item.
DELETE/bundles/items/{id}Remove a bundle item.
GET / POST/bundles/settingsRead and write the module's settings.
POST/bundles/{id}/cartAdd a configured bundle to the cart. Nonce, rate-limited.

WooCommerce integration​

HookWhat the module does
woocommerce_before_add_to_cart_form / _after_add_to_cart_formDraws the bundle builder around the add-to-cart form.
woocommerce_before_calculate_totalsApplies the bundle discount to the grouped lines.
woocommerce_get_price_htmlShows the bundle price, and the saving where it clears min_discount_display.
woocommerce_before_shop_loop_item_titleMarks bundles in the shop listing.
woocommerce_cart_item_classStyles the parent and child lines as one group.
woocommerce_cart_item_quantityGives the group one quantity control, honouring enable_quantity_editing.
woocommerce_cart_item_remove_link / _removedRemoves the whole bundle when the parent line is removed.
woocommerce_check_cart_itemsRe-checks that the bundle can still be built from available stock.
woocommerce_get_item_dataLists the bundle contents under the cart line.
woocommerce_checkout_create_order_line_itemWrites the bundle structure onto the order.
woocommerce_order_item_name, _display_meta_key, _hidden_order_itemmeta, _after_order_itemmetaPresents the bundle on the order without exposing its internal meta keys.

Database schema​

TableHolds
{prefix}aiowc_bundlesThe bundle: title, slug, description, discount type and value, minimum and maximum quantity, bundle type, active flag.
{prefix}aiowc_bundle_itemsThe contents: product and variation, default, minimum and maximum quantity, discount override, optional flag, sort order.
{prefix}aiowc_bundle_productsThe pool a mix-and-match bundle draws from.

These names are this module's. The separate Product Bundles (Enhanced) module keeps its own tables under a different prefix, so the two never share a row.

Background jobs​

HookIntervalWork
aiowc_bundle_inventory_sync6 hoursBrings each bundle's availability back in step with component stock.

The job runs on Action Scheduler.

Action hooks for integrators​

HookFired when
aiowc_track_eventThe module is enabled or disabled.
aiowc_capture_errorAn error is caught while pricing or building.

The module exposes no filter for overriding bundle pricing.

Entitlement limits​

bundles is an on/off grant with no cap on the number of bundles or items.

Health check​

The module reports a warning when its tables are missing or WooCommerce is inactive, and otherwise reports that it is functioning normally. A disabled module reports that it is disabled.

Known gaps​

  • Two bundle modules ship, and both can be on at once. This one and Product Bundles (Enhanced) both hook the cart and both price bundles, with separate tables and separate admin screens. Nothing detects that both are active or reconciles their behaviour, so a store should pick one.
  • Bundle availability is refreshed on a six-hour cycle rather than when component stock changes, so a bundle can show as available for up to six hours after its components ran out. The cart re-check is what actually prevents an oversell.
  • There is no filter for bundle pricing, so a discount the three types cannot express requires modifying the module.
  • Deleting a bundle does not affect orders already placed, which keep their own copy of the bundle structure.