PRD: Combos & bundles
| Module | Products | PRD ID | PRD-BND-001 |
| Status | Ready to dev | FEAT | BND |
| Epic | — | Plane | BANA-1551 |
| Date | 2026-06-15 | Version | v1.0 |
| Packages | @nx/core · @nx/commerce · @nx/sale | URD | BND |
| Surface | Client · Owner/MgrSale · POS | ||
| Owner | Phát Nguyễn | ||
What combos & bundles are
The catalogue can already describe and price a single sellable variant (VAR, FAR), but it cannot yet describe a relationship between two variants - "this meal is a burger, fries, and a drink", "throw in a free sauce", "people who buy this also buy that". Merchants sell these every day. A POS that only rings up flat variants forces staff to key each part by hand and guess the price.
A combo is more than a label. The kitchen must see every physical part to make it, inventory must deduct each part's stock, and the bill must show one price - not the sum of the parts. Without a first-class bundle, a "combo" is either one un-decomposable line (kitchen and stock go blind) or a pile of separately-priced lines staff discount to zero by hand.
This PRD makes the bundle relationship first-class: the owner declares combo / add-on / FBT relationships on a variant, and at the point of sale the system expands a combo into a priced lead plus zero-priced parts, attaches add-ons and FBT items on demand, and refuses misconfigured graphs.
Three bundle types
A bundle always has a direction: from a lead variant to a related variant. The bundle type decides how the related side is priced and when it attaches to the cart.
| Type | Price of the related side | When it attaches |
|---|---|---|
| Combo | Zero - the combo itself carries the price | The system expands it automatically the moment the lead (combo-typed) variant hits the cart |
| Add-on | Always free | Staff attach it manually at the point of sale, onto an existing lead line |
| Frequently-bought-together (FBT) | Its own price, or an override | Staff attach it at the point of sale as an up-sell suggestion |
One combo, from cart to kitchen
An owner builds a "Lunch Combo" variant at one price and declares its components: 1 burger, 1 fries, 1 drink. A cashier adds the combo to the cart.
| Step | What happens |
|---|---|
| 1 | The system loads the whole combo graph, checking depth and cycles |
| 2 | Creates one lead line carrying the combo's price |
| 3 | Creates three zero-priced component lines (burger, fries, drink), linked back to the lead |
| 4 | Reserves stock for each component |
Ordering two combos doubles every component line automatically. The cashier attaches a paid extra sauce (FBT) to the combo lead and a free napkin set (add-on) to the burger line - both attach to an existing top-level lead line; neither attaches to the combo any other way.
Where this is easy to get wrong
Combo component lines are not directly editable
An owner or cashier never edits a component line inside a combo by hand - every change goes through the lead line, and the children scale automatically to the new combo quantity.
A combo also never expands forever: expansion has a fixed depth ceiling (5 levels), a cycle guard, and refuses empty combos. A misconfigured graph can never hang the cart or leave a combo with nothing for the kitchen to make.