Skip to content

Limits and guarantees

What Product Bundles promises, and exactly where the promise stops. The awkward sentences are here on purpose, so you can find each boundary before you buy rather than after.

At a glance

If you are asking The short answer
Does a bundle keep stock of its own? No. Whether it can be made is worked out from its components every time anybody asks, across the whole cart. More
Can a shopper buy a bundle whose component was disabled or deleted? No, whatever your out-of-stock setting says. The cart keeps the line and checkout refuses it. More
Can a bundle contain a subscription product, or another bundle? No. The bundle form refuses to save either. More
Can I build or edit a bundle order in the admin? No. The order editor refuses a bundle line. More
Can one component of a bundle be returned or refunded? No. The order never recorded what one component cost. More
Does a coupon for a component apply to the bundle? No. Only a coupon naming the bundle itself does. More
Does uninstalling delete my bundles? No table is ever dropped. The bundles stop selling until you install it again. More
Does it install on OpenCart 4.0.2.x or 4.1.0.0? No. It needs 4.1.0.1 or newer, and declines to install below that. More
Does it translate my bundle names? No. They are yours, per language, and a language you left blank is blank. More
Can my ERP or warehouse read what was inside a sold bundle? Yes, once you switch the API on: every component and the options chosen on it, for every bundle an order sold, and what each bundle contains today. It cannot change anything. More

What a bundle is, and what it is not

A bundle is a set of products you already stock, sold as one thing at one price. It gets its own page, its own row in your catalogue and its own line on an order. What it does not have is stock of its own:

  • A bundle is never counted, only derived. Whether it can be made is worked out from its components every time anybody asks: the bundle page, the cart, the wishlist, the compare page. There is no nightly job and no counter to go stale, so a bundle whose components ran out overnight says so the next morning without you touching it.
  • One number on the carrier is a cache, and nothing a shopper reads uses it. The bundle's carrier product carries a quantity, written only when you save the bundle, so that a carrier in OpenCart's own product list does not read as a bundle nobody can buy. It is stale by design and no storefront verdict comes from it.
  • Demand is added up across your whole cart, not per line. The same mug in a starter kit and in a gift set is one demand on one figure. A shopper holding both is limited to the ten mugs you have rather than being sold eleven.

What a bundle may contain

  • Only products you already stock. A component is picked from your catalogue; there is no free-text line in a bundle.
  • Never another bundle. Bundles are not offered in the component picker, and a save that names one anyway is refused.
  • Never a product with subscription plans. OpenCart will not add such a product to a cart without a plan chosen, and a bundle has nowhere to choose one, so the save is refused and names the product.
  • A component cannot be deleted while a bundle holds it. OpenCart's own product delete is refused and names the bundles in the way. Take the product out of those bundles first. Copying a component is allowed, and the copy is in no bundle. This guard is Product Bundles' own, so it holds while Status is on: with it off, the delete goes through and the bundle reads as broken once you switch it back on.
  • A downloadable component makes the bundle downloadable. The buyer gets the file from Account → Downloads once the order reaches your completed status, and, as for any product with a download, cannot buy it as a guest.

What happens when a bundle breaks

A component going away is different from a component running out, and Product Bundles keeps them apart because conflating them is how a store takes payment for a box it cannot pack.

  • Run out of a component and your store's own setting decides. Scarcity is OpenCart's question and it already has a store-wide answer: whether an out-of-stock item can still be bought. A bundle is subject to the same setting, in the same wording you already chose.
  • Lose a component entirely and there is no bypass. Deleted, disabled, dated into the future, taken out of that shop, or carrying an option value your catalogue no longer offers: the bundle cannot be packed at any quantity, so no setting sells through it. The cart keeps the line and says which one is the problem, the customer cannot check out holding it, and adding it again is refused.
  • The line is kept, not deleted. Removing it would destroy what the shopper configured. They see a verdict on the line instead, and can fix it themselves.
  • You find out from your own admin. The bundle list badges it Broken and the bundle form names what is wrong, so the first you hear of it is not a customer who could not pay.
  • The warning is per shop. A component missing from one shop's catalogue is a bundle that cannot be packed there and can be in the next, so the badge and the banner name the shops it is broken in.

What an order records, and what it never re-reads

  • An order freezes what was in the box. At the moment the order is created, Product Bundles writes one row per component (its product, a copy of its name and model, and how many of it one bundle held) plus a row per option value chosen. Nothing downstream ever reads the bundle definition again.
  • So editing a bundle never rewrites history. Change a bundle's components on Tuesday and Monday's order still shows, and still shipped, what it shipped. A bundle you have since deleted renders its past orders exactly as they were.
  • Stock comes off the components, not the bundle, on OpenCart's own order history call, at component quantity × line quantity.
  • Nothing restocks on a return, for bundles or for ordinary products. That is OpenCart's behaviour and it is not changed here. Returns Portal's restock leaves a bundle alone: the carrier tracks no stock, and components go back only on the order-status changes Product Bundles already listens to.

What it does not do

  • You cannot build a bundle order from the admin. The order editor refuses a bundle line rather than mangling it: what goes in the box is chosen on the bundle's own page, and the editor has nowhere to keep that choice. Remove the bundle line to edit the rest of the order.
  • A component cannot be returned on its own. The order never recorded what a component cost, so any per-component refund figure would be invented from today's prices against today's bundle definition.
  • A coupon for a product inside a bundle does not apply to it. OpenCart matches a coupon on the line's own product, and a bundle line's is the bundle. A coupon naming the bundle works normally.
  • A store-level product discount on a bundle is lost, and a product special never applies. Both are set on the bundle's carrier product, which is a row Product Bundles owns and you are not meant to edit.
  • A review on a bundle is about the bundle. It never moves a component's rating, and a component's never moves the bundle's.
  • Its one report counts units, never money. Reports → Bundle components sold lists each product that left your store inside a bundle: how many units of it, and on how many orders, filtered by order status and by date, and downloadable as CSV. Products Purchased counts the bundle's own line, so a product sold only inside bundles reads nought there while its stock drains; this is the other half of that number. It has no price or revenue column, because the order never recorded what one component cost. A bundle line you removed in the order editor is not counted. Bundles themselves still appear in What's Viewed and Products Purchased as themselves.

The API, and what it can read

Off until you switch it on, under API on the settings screen, and while it is off every route answers as though this extension had no API at all. Two read resources: line is one bundle an order sold — core's order line for it, and every component and option that went with it, frozen as sold — and bundle is a bundle as it stands today: its carrier product, the stores that sell it and what is in it. The API is generated from what the extension actually answers.

  • It only ever reads. Nothing can create, edit or delete a bundle through it, and nothing can be added to an order.
  • It answers what OpenCart's own data cannot. OpenCart records a sold bundle as one order line for its carrier product and nothing about what was inside; the components, and the options chosen on each, are this extension's alone. Everything a bundle mirrors onto its carrier — description, categories, price, dimensions — is left to OpenCart's own product, so there is one answer to each question rather than two.
  • There is no money in it. A sold line's price is on OpenCart's own order line, a bundle's price on its carrier product, and a bundle's discount is a percentage in one of its two pricing modes, not an amount.
  • Sold lines sync by order. A line is written once at checkout and never changed, and each new one carries a higher id, so after one full walk an integration reads from the newest id it holds. Editing an order writes its bundle lines again under OpenCart's new order line ids, and deleting an order or erasing a customer removes them, so walk the whole collection now and then and reconcile by absence.
  • "Changed since" works for bundles. An edit, a shop being removed from a bundle and its carrier being created again all move a bundle's modified date. A carrier deleted outside this extension's screens is answered as missing at once, and the date moves the next time the bundle is saved.
  • Option values are what the shopper typed, an engraving or a gift message included, and they come back as stored.
  • The credential is OpenCart's own API user, and it is not scoped. One is enough to read every extension's API on the store and core's own order API besides, across every shop. Restrict it by IP address under System → Users → API, and treat it the way you would treat an admin login.
  • Nothing records that anybody read anything. No read log, no per-credential audit trail, no rate limiting.

What an update and an uninstall keep

  • No table is ever dropped. In OpenCart an upgrade is an uninstall followed by an install, so a table Product Bundles could drop would take every bundle and every order's contents with it on each version bump. It cannot, by construction.
  • An update keeps everything: your bundles, their components, the options on them, which shops they sell in, what they earn, and every past order's record of what shipped. Settings do not: OpenCart deletes an extension's settings at the uninstall step of an update, and Product Bundles keeps no copy, so the module comes back switched off and every other setting at its default. Off means every bundle is off sale until you switch Status on again. Status off says what else stops. The changelog lists each one.
  • An update removes the layout module from your layouts. OpenCart's own uninstall deletes every placement of a module along with it, and Product Bundles keeps no copy of where you had placed it. Place it again at Design → Layouts after updating.
  • Uninstalling keeps your data too, and stops every bundle selling. Each bundle's carrier product is disabled rather than deleted, so past orders still point at it. Install it again and switch Status on, and each carrier is re-enabled to match what its bundle says, so a store that had it off for an afternoon does not come back to a storefront selling none of them.
  • Uninstalling does not remove what it holds about your customers. That is a button on the settings screen, which shows what will go before it goes. See what this holds about a person.
  • Edited language files are overwritten by any upload. That is true of every extension and it is the one thing an update always takes back.

Status off

Status comes back off after every update, and off means Product Bundles is not running, so it is worth knowing exactly what that covers.

  • Every bundle is off sale. Each bundle's carrier product is disabled, the way an uninstall leaves it, so no shopper can reach or buy a bundle. Saving a bundle while it is off keeps its carrier disabled. Switching Status on puts each one back on sale, or not, as the bundle's own Status says.
  • Catalog → Products is not guarded. A product a bundle holds, or a bundle's carrier, can be deleted or copied while it is off.
  • Component stock does not move. An order placed while it was on that reaches Processing, or is cancelled, while it is off takes no components off the shelf and puts none back, and nothing catches that up afterwards. If an order with a bundle in it changed status while the module was off, check its components' stock by hand. Switch Status on straight after an update to keep that window short.

Layout module

Product Bundles has a layout module you place at Design → Layouts, in any position of any layout. It shows nothing until you place it.

  • On a product's own page it is headed Bundles with this product and lists the bundles that product is a component of.
  • On a bundle's page it is headed Bundles and lists your other bundles, leaving out the one being viewed.
  • Anywhere else it is headed Bundles and lists your bundles in the Bundles page's own order.
  • It lists only what the Bundles page would: enabled, available, sold in this shop and packable. Each card is the same card the Bundles page draws, at the same price.
  • Empty, it draws nothing, not even its heading. A product in no bundle gets no box.
  • One limit for every placement. How many cards it shows (4 unless you change it, from 1 to 12) is one setting on the Product Bundles screen, because OpenCart keeps no per-placement settings for a module like this one.
  • "See all bundles" under the cards links to the Bundles page, and is left out while you have that page switched off under Where bundles show.
  • The wording is ours and fixed, like every other string here. See Language.

Requirements

Product Bundles needs OpenCart 4.1.0.1 or newer. It is the one extension sold here that does not go back to 4.0.2.0. Everything a bundle tells your cart travels on a piece of OpenCart that only exists from 4.1 onwards, and on an older store a customer could reach checkout holding a bundle you had no way to pack. Rather than ship that, it declines to install and says which version you need. See the exception on the requirements page.

What a bundle line shows on a returns portal

If you also run Returns Portal, a bundle line shows its contents there too (on the customer's line picker, their confirmation screen, the RMA slip and your own request screen), exactly as it does on the order page. It costs no setting and no configuration in either extension, and installing this one afterwards is enough on its own. The returns queue is deliberately left alone: its job is a fast verdict across many requests, and component rows under every line would spend the width that needs.

The limits are the same four that apply everywhere else a bundle is a bundle:

  • Quantities are per bundle, never per component. A return of two kits reads 3 × Camera Strap beside a quantity of two, the same as on every other screen.
  • A component cannot be returned on its own. product_bundles_order_product has no price column, so the order never recorded what a component cost. Any per-component figure would be recomputed from today's prices against today's bundle definition while the order side is frozen.
  • Excluding one component does not make the bundle non-returnable. The lever is the bundle's own carrier product or its category.
  • A bundle you have edited or deleted since still renders the contents the order shipped, because the order side is frozen.

A return never restocks a bundle or its components, whichever extension handles the return.

Selling a bundle in more than one shop

A bundle is assigned to shops the way a product is: tick them on the bundle's Links tab, and the bundle is sold in those and nowhere else. A new bundle starts in the default shop. The SEO tab is a keyword per shop and language, because OpenCart resolves a URL inside the shop the request arrived at. The same keyword may be reused between shops, and a keyword is needed in every cell.

Reaching a bundle's page in a shop that does not sell it shows the not-found page, which is what OpenCart does with a product that shop does not sell, and the bundles listing shows each shop only its own.

The health warning is per shop. A component missing from one shop's catalogue is a bundle that cannot be packed in that shop and can be in the next, so the banner on the form and the Broken badge on the list name the shops the bundle is broken in.

Four points apply:

  • A component in a shop the bundle is not in is never refused when you save. Which shops a component product is in is edited on OpenCart's own product form, so a bundle form that refused the save could not keep the promise: you could break it a minute later from a screen the bundle never sees. The warning tells you, and the checkout refuses the sale.
  • Un-ticking a shop empties the bundle out of any cart in that shop, with no message to the shopper. This is OpenCart's own behaviour when a product is un-assigned: its cart skips a line whose product is not in the current shop, and it does so before anything this extension runs, so there is no line left to attach a message to. Un-tick a shop when you mean to stop selling there, not to tidy up.
  • A shop added later does not get your existing bundles, exactly as it does not get your existing products: tick it on each bundle you want it to sell. The bundle page's own URLs work in the new shop from the moment it is created, because OpenCart copies the default shop's URL rows into it.
  • A download in a bundle is not scoped to a shop. A buyer who bought a downloadable bundle in one shop is not shown the file in another, but can still fetch it there. This is OpenCart's own inconsistency for ordinary products (its download list and count filter by the order's shop and the query that authorises the fetch does not), and it is reproduced verbatim rather than corrected here.

What a review on a bundle covers

A bundle has customer reviews, and they are OpenCart's own: the same form, the same star rating, the same moderation queue under Catalog → Reviews. There is nothing new to learn or switch on. A bundle's reviews follow the store settings you already set for products, including whether reviews are on at all, whether guests may write one, and whether a shopper has to have bought the thing first. That last one is answered correctly for a bundle: the order line a purchase leaves behind is the bundle's own, so a shopper who bought the kit passes the gate and a shopper who bought one of its components does not.

Two things it deliberately does not do:

  • A review is about the bundle as a whole, and has nothing to do with its components' ratings. A five-star bundle of one-star products is expressible, and so is the reverse. There is no averaging in either direction: a review left on a bundle never moves a component's rating, a review left on a component never moves the bundle's, and a bundle's page does not show its components' reviews. Whether the kit was worth buying as a kit is a different question from whether the lens is any good, and the star rating on a bundle answers the first one only.
  • A bundle's reviews are not scoped to a store. If you run more than one shop from one OpenCart install and a bundle is sold in both, a review written in one shows in the other, and both appear in the one admin review list. This is OpenCart's own behaviour for ordinary products (neither the storefront review query nor the admin list filters by store), and it is reproduced rather than corrected, because a bundle behaving differently from a product here would be the more surprising of the two.

A bundle nobody has reviewed yet shows no stars at all, on its own page and on every card in the store, exactly as an unreviewed product does. Deleting a bundle deletes its reviews with it, and disabling one hides them until you enable it again.

OpenCart 4.1.0.0 is below the floor, not merely untested

Product Bundles needs one piece of OpenCart: the per-line slot a cart keeps alongside each product, which is where a bundle's price, its availability, its weight and its points are written. 4.1.0.0 has that slot, but declares the column behind it one byte wide, so OpenCart writes what the bundle says and the store keeps none of it.

Nothing on any screen would say so. A bundle would be charged at its carrier product's own price, its availability would be OpenCart's answer rather than its components', and the checkout block on a bundle that can no longer be made would never apply. A wrong charge on an order that has already been paid is not acceptable as a documented limitation, so Product Bundles declines to install on 4.1.0.0 rather than working around it, exactly as it declines on 4.0.2.x and for the same reason. 4.1.0.1 widened the column, and that is where the floor is.

Language

Product Bundles talks to your customers all over the store: the bundle page, the contents list under an order line, the picker on the product form's counterpart in the storefront, and the messages the cart raises when a bundle can no longer be packed. Which of those a shopper reads in which language is answered, and counted, on the shared language promise page rather than claimed here.

Four points follow, and the first matters most:

  • A sentence nobody has translated yet reaches a shopper in English, never blank and never as its own name. Every string here is served with English underneath it, string by string rather than language by language, so a half-finished translation is a page in two languages rather than a page with holes in it. That floor is deliberate: a bundle page that briefly says What is in it in English is better for your store than one showing the name of a setting where a heading should be.
  • The screens you work in are translated as far as somebody has read them and no further, and the rest is English. A string a named reviewer has signed off is served in your admin language; a string nobody has read is served in English rather than blank or as its own identifier; and a string whose English has since been reworded goes back to English until it is read again. Which string is in which state is on that page.
  • None of our wording on a bundle page is yours to reword, and that is a decision rather than an oversight. Every string it says to a shopper was read one by one, and each turned out to be a label, a column heading, a button or a refusal: part of the page's structure rather than anything your store is saying. The nearest thing to an exception, the line inviting the first review, is a sentence OpenCart already says in those words on its own product pages and does not let you reword either, so a box here would only let the two disagree. There is therefore no wording panel on the settings screen, and nothing to fill in.
  • Your bundle names, descriptions and keywords are yours, per language, and none of this reaches them. A bundle is something you created, so there is no wording of ours behind its name to fall back to: a bundle named in Dutch and left blank in German is blank in German, on its own page and in the contents under an order line. A bundle you rename does not rename what an order already shipped, because the order side is frozen. That is the same rule as everywhere else on this page.

Anything this page does not describe should be assumed absent. If you need it, ask before you buy.