Bundles
A bundle is a SKU sold as a single unit but composed of multiple component SKUs. Quantaprice prices bundles deterministically and integrates them into the standard pricing pipeline — a bundle price is read exactly like any other price, with no special handling by the caller.
A bundle is defined at the article/catalog level (its component list), while its pricing rule is defined per pricelist and follows the normal inheritance hierarchy.
Defining a bundle
A bundle article carries a list of components, each a (sku, quantity):
{
"sku": "BUNDLE-START-KIT",
"components": [
{ "sku": "COMP-A", "quantity": 2 },
{ "sku": "COMP-B", "quantity": 1 }
]
}
Validation:
- At least one component.
- Each component quantity is positive (fractional quantities allowed).
- A component cannot be the bundle itself.
- A component cannot itself be a bundle (no nesting).
- A component article must already exist.
Pricing methods
The bundle's price on a pricelist is determined by its bundle pricing rule (method):
sum_of_parts
The price is the sum of each component's effective price × its quantity, optionally adjusted by a discount:
base = Σ ( component.quantity × effectivePrice(component) )
discount_type = "percentage" → price = base × (1 − discount_value/100)
discount_type = "absolute" → price = base − discount_value (clamped to ≥ 0)
none → price = base
effectivePrice(component) resolves each component using normal pricelist inheritance (the
component's own price on this pricelist, else inherited from a parent). So a customer with negotiated
component prices automatically gets a bundle priced from their component prices.
fixed
The bundle has an explicit fixed_price; component prices do not affect it. The response still
includes a per-component VAT breakdown (derived proportionally from the components) so tax is
computed correctly across mixed VAT classes.
Inheritance
The rule is inherited, not the resolved price. Resolving a bundle rule for a pricelist:
- If the pricelist has its own bundle rule, use it.
- Otherwise inherit the rule from the nearest parent pricelist that has one.
- For
sum_of_parts, component prices are then resolved at the requesting pricelist level (which itself follows normal inheritance).
A child pricelist may override the rule (switch method, change the discount, set a fixed price). There are no per-component overrides at the pricelist level — components are defined on the article.
Scheduling
Bundle pricing is time-aware. Both rules and component prices can be future-dated, and the bundle reflects them automatically at the scheduled moment — no manual activation.
- Scheduled fixed price — set a
fixedrule with a futureeffective_from; the bundle switches to that price at that time. - Scheduled discount / method change — schedule a future
sum_of_partsdiscount change (e.g. 10% today, 25% from Black Friday) or a method switch. - Scheduled component prices — a future-dated price change on any component (its own or inherited) is reflected in the bundle from the moment it takes effect.
Scheduled bundle prices are precomputed within a bounded horizon (default 90 days) and a maximum number of future versions per bundle/pricelist; both are configurable per plan. Beyond those limits the schedule still applies — it is materialized progressively as time advances.
Historical pricing
Because each change produces a new effective-dated version, bundle prices support "as of" evaluation for past dates just like regular prices.
Reading a bundle price
A bundle price is requested and returned exactly like a regular price. The response additionally includes a per-component breakdown so the caller can see how the price was composed:
{
"sku": "BUNDLE-START-KIT",
"price": { "ex_vat": "180.00", "vat": "45.00", "inc_vat": "225.00" },
"bundle": [
{ "sku": "COMP-A", "quantity": 2, "price": "120.00", "vat_rate": "25.0", "vat_amount": "30.00", "pricelist": "RETAIL" },
{ "sku": "COMP-B", "quantity": 1, "price": "60.00", "vat_rate": "25.0", "vat_amount": "15.00", "pricelist": "RETAIL" }
]
}
bundle is null for regular (non-bundle) prices. Each component entry reports the pricelist its
price was resolved from and its own VAT contribution (VAT is computed per component, so bundles with
mixed VAT classes are handled correctly).
If a sum_of_parts component cannot be resolved anywhere in the hierarchy, the bundle is treated as
unavailable (no price) rather than returning a partial sum.
Bundle Rules API
Bundle rules are managed under /bundle-rules.
Set or update a rule
PUT /bundle-rules/{bundleSku}/{pricelistCode}
{
"method": "sum_of_parts",
"discount_type": "percentage",
"discount_value": 10,
"effective_from": "2026-11-27T00:00:00Z"
}
{
"method": "fixed",
"fixed_price": "499.00"
}
effective_from is optional and defaults to now; a future value schedules the rule. Setting a rule
triggers recomputation of the bundle price on this pricelist and its descendants.
Validation: discount_value is 0–100 for percentage and ≥ 0 for absolute; fixed_price is
required and ≥ 0 for fixed.
Get the effective rule
GET /bundle-rules/{bundleSku}/{pricelistCode}
Returns the rule effective now, resolving inheritance up the parent chain. The response includes
inherited: true|false, the method, discount/fixed fields, and effective_from.
List rule versions
GET /bundle-rules/{bundleSku}
Lists every pricelist/version that has an explicit rule for the bundle.
Remove a rule
DELETE /bundle-rules/{bundleSku}/{pricelistCode}?effective_from=...
Removes a rule version (defaults to the version effective now). The bundle then inherits from the parent, or becomes unavailable if no rule remains anywhere in the hierarchy.
Design principles
- Deterministic — same inputs always produce the same result, identically across nodes.
- O(1) reads — bundle prices are materialized on write; reads are a single keyed lookup.
- Composable — bundles integrate with inheritance, scheduling, history, VAT, and rounding.
See also
- Pricelist Inheritance — how bundle rules are inherited through the pricelist hierarchy.
- VAT — how per-component VAT is resolved for bundles with mixed VAT classes.
- Querying Prices — reading bundle prices via the query API.
- OpenAPI Reference — full spec for
/bundle-rulesendpoints.
Summary
Bundles are SKUs composed of component SKUs, priced per pricelist by an inherited rule
(sum_of_parts with an optional discount, or a fixed price). Rules and component prices are
time-aware, so fixed prices, discounts, and component price changes can all be scheduled and are
reflected automatically. Prices are materialized for O(1) reads and returned with a per-component
breakdown.