Present for Craft CMS

Free for Craft Commerce 5

Sell several things as one, and ship them as many

Every other bundle plugin for Craft sells a bundle as one line item, and a single line can never take stock off the right shelves, be partly shipped, or be taxed correctly when its contents are taxed differently. Present puts a box in the cart as a container line plus one real line item per component — the shape WooCommerce Product Bundles uses, for the same reasons.

Present

Features

A bundle that Commerce's own stock, tax, shipping and fulfilment all understand — because to Commerce, its contents are just line items.

Real line items

A box is a container line plus one line item per component. Commerce commits stock per purchasable and records fulfilment per line item, so the components get both — correctly, even when the same variant is also in the cart on its own.

  • Each component keeps its own tax category, shipping category and weight
  • Two identical boxes merge into one line at quantity two
  • A box that has come apart is repaired or removed on the next recalculation

Sells out when its contents do

A bundle holds no stock of its own. Its availability is the fewest boxes any one component can cover, so a box sells out when the tea runs low — without anyone editing it.

  • Untracked and sell-out-of-stock mean unlimited, not zero
  • The same variant in two slots is counted once
  • A Buildable column on the bundles index, and a console check across every bundle

Pricing that adds up

Per-item pricing charges each component at its own sale price, with the bundle price as a fee on top. Fixed pricing spreads one price across the components by line value, in minor units, with largest-remainder allocation — so the parts add up to exactly the price, every time.

  • Slot and option discounts, where null means "not set" and 0 means "none"
  • A catalog sale on a component carries into the box
  • Put a fixed price on the bundle line instead (Pro)

Build your own box

Pro lets the shopper fill it: quantity ranges per slot, a minimum and maximum for the whole box, optional slots, no-repeat pools, and pools drawn from a product type or a relation rather than a list.

  • A drop-in builder in one Twig tag, with no dependencies
  • A JSON API if you would rather build your own
  • The preview and the checkout run the same validation

How many boxes went out?

Pro rolls Commerce's own inventory movements back up into an answer about boxes: a box ships when everything in it has. The order screen shows every component, what is outstanding, and what is holding the rest up.

  • Unfulfilled, partly fulfilled and fulfilled, per box
  • Blockers, worst first: "four mugs short of three boxes"
  • Nothing stored, so it cannot drift from what the warehouse did

Ignored, not obeyed

Pro settings on a Lite install stay in the database untouched, and the control panel says which ones are being ignored. A store that lapses stops selling boxes it can no longer validate, rather than selling them wrongly — and upgrading restores everything.

  • Range slots behave as fixed, query pools fall back to their list
  • Allocation falls back to spreading
  • Nothing is deleted on a downgrade

Three lines to a box builder

One tag renders pickers for every slot with live pricing and live stock. Because the contents are real line items, the cart needs one more loop to fold them under their box.

twig
{% set bundle = craft.present.bundle('GIFTBOX') %}

<h1>{{ bundle.title }}</h1>

{# Pickers for every slot, live pricing, live stock, min/max enforced #}
{{ craft.present.builder(bundle) }}

{# In the cart: fold each box's contents under it #}
{% for item in craft.present.cartLineItems(cart) %}
  {% if craft.present.isComponent(item) %}
    <tr class="component"><td colspan="2">↳ {{ item.qty }} × {{ item.description }}</td></tr>
  {% else %}
    {% set group = craft.present.group(item) %}
    <tr>
      <td>{{ item.qty }} × {{ item.description }}</td>
      <td>{{ (group ? group.total : item.total)|commerceCurrency(cart.currency) }}</td>
    </tr>
  {% endif %}
{% endfor %}

Frequently Asked Questions

The questions worth answering before you install it.

Start with Lite, add a shopper when you need one

Lite is free and sells fixed bundles properly — real line items, the stock rollup and both pricing modes. Pro is $99 with a $79/year renewal, and adds build-your-own-box, partial fulfilment, the JSON API and builder, and the CP configurator.