Catalogue & product data
Cart and catalog promotion rules
Generated from docs/plan/core/cart-and-catalog-promotion-rules/PLAN.md in the AstroBaaS repository. Nothing described below is implemented — it is the written plan for it.
Cart price rules (buy-X-get-Y, quantity discounts, free shipping) and catalog price rules (scheduled bulk discounts) form the core promotion engine. Cart rules apply at checkout time; catalog rules apply at index time. Both are free tier-0 features because they are base ecommerce mechanics. Without them, every promotion requires a coupon code, which kills impulse conversion.
The problem
Every discount requires a coupon code. I can’t offer ‘buy 2 get 1 free’ or ‘free shipping on orders >€50’ without a code. Most customers don’t know or remember codes. I need automatic discounts that apply at checkout without codes.
What it does
- Cart price rule: condition (cart subtotal >€50, product in category, qty ≥2), action (free shipping, €5 off, 10% off, buy-X-get-Y)
- Buy-X-get-Y rules: ‘buy 2 Sunglasses get 1 Accessory 50% off’ (with category/product/brand matching)
- Quantity-based discounts: ‘qty ≥3 = 5% off, qty ≥5 = 10% off’
- BOGO (Buy One Get One) rules: ‘buy 1 premium product, get accessory free’
- Free shipping on condition: ‘free shipping if cart >€50 or includes category:Organic’
- Multiple rules per cart: if two rules apply, highest discount wins (or both apply if merchant chooses stacking)
- Rule scheduling: recurring (every weekend), one-time (Black Friday), always-on (loyalty discount)
- Coupon codes (optional): merchant can gate rules behind coupon code if desired (e.g., newsletter-only discount)
- Rule preview in cart: show discount calculation (‘€10 off because subtotal >€50’) and what discount rules apply
- Rule analytics: track revenue impact per rule, which rules drive most conversions
What it deliberately does NOT do
Each boundary carries its reason. A boundary without a reason gets crossed by the next person who reads this.
- Customer segment-based rules (VIP gets different discounts) — that is customer segments, separate module.
- Loyalty points or tiered rewards — that is loyalty module.
- Affiliate or referral discounts — that is affiliate module.
- Subscription discounts or recurring deals — that is subscriptions module.
- Time-zone aware scheduling (customer timezone, not UTC) — core uses UTC only; timezone conversion is paid.
Data model
New CART_PRICE_RULE table (rule_id, name, conditions JSON, action_type (free_shipping|fixed_off|percent_off|buy_x_get_y), action_params JSON, start_at, end_at, recurrence, priority, enabled). New CART_PRICE_RULE_LOG table (log_id, order_id, rule_id, discount_amount_minor, applied_at). Migration required: none (new tables).
API
- POST /admin/cart-price-rules (create rule)
- GET /admin/cart-price-rules (list rules)
- PATCH /admin/cart-price-rules/:id (update rule)
- DELETE /admin/cart-price-rules/:id (delete rule)
- POST /cart/calculate-discounts (return applied rules + total discount for current cart)
- GET /admin/cart-price-rules/analytics (revenue impact per rule, conversion rate per rule)
Every route added here must also appear in src/pages/openapi.json.ts — a test
fails the build if it does not.
Admin
Cart Price Rules page with table of active/scheduled/expired rules. Create rule button. Rule builder: condition picker (cart subtotal, product in category, qty ≥), action type (free shipping, % off, fixed off, buy-X-get-Y), schedule (date/time range, recurrence). Rule preview shows matching cart scenarios. Analytics: show estimated revenue impact per rule and conversion rate.
The seam — why this is core
Core owns cart rule engine, condition matching, and discount application at checkout. Core does not own customer segments (that is loyalty/segments module) or subscription discounts (that is subscriptions module). Core owns the interface; fulfillment and payment consume discounted order total.
Base promotion mechanics. Requiring a code for every discount kills conversion.
Dependencies
- flexible-product-attributes (for condition matching)
- Product categories (core, for category matching)
- Cart system (core, existing)
Acceptance checks
Each of these must be able to fail. Before claiming this is done, break the code deliberately and watch each one go red.
- Rule created: ‘if cart subtotal >€50, apply 10% off’. Cart with €60 subtotal shows €6 discount, total €54.
- Buy-X-get-Y rule: ‘buy 2 Sunglasses, get 1 Accessory 50% off’. Cart with 2 sunglasses + 1 accessory (€20): accessory becomes €10, shows ‘€10 off buy-2-get-1 rule’.
- Free shipping rule: ‘if cart >€100 OR includes category:Organic, free shipping’. Cart €80 with organic product: free shipping shown even though subtotal <€100.
- Multiple rules: rule A (€5 off), rule B (10% off). Highest discount wins; only rule B applied. Response shows which rule was applied.
- Scheduled rule: ‘weekend free shipping, Fri 5PM–Sun 11:59PM’. Friday at 5PM, rule activates; Monday 8AM, rule deactivates.
- Cart preview shows: ‘Subtotal €100, Discount -€10 (cart rule: spend >€50), Shipping -€5 (free shipping), Tax +€20, Total €105’.
- Rule analytics: ‘1,000 carts matched free-shipping rule, 200 converted to orders (20% conversion vs. 15% baseline)’.
Risks
If rules are not applied atomically, discount calculation is inconsistent (one cart shows €10 off, another shows €5 off for same products). Mitigate: apply rules in deterministic order, test across all rule combinations. If multiple rules stack unpredictably, merchant loses track of actual discount. Mitigate: define stacking order clearly (highest discount wins, or document stacking), log all applied rules in order. If rule end time passes and discount is not auto-deactivated, discounts carry over (revenue loss). Mitigate: schedule automatic deactivation at end_at timestamp, test expiry logic.
Commercial context
| Suggested price | Core |
| Rival anchor | Magento Open Source: cart price rules and catalog price rules ship free. |
The anchor is what the nearest equivalent charges on Shopify or Magento today. It is context for a pricing decision, not the decision.
Generated from the commerce plan. See docs/COMMERCE-PLAN.md for the full
catalogue and ../../AI-GUIDE.md for how to work on this repository.