AstroBaaS

Pricing & promotions

B2B Wholesale Pricing & Custom Price Lists

Free — GPL coresize XLplanned, not built

Generated from docs/plan/core/b2b-wholesale-pricing-custom-price-lists/PLAN.md in the AstroBaaS repository. Nothing described below is implemented — it is the written plan for it.

Core free module that allows merchants to assign custom price lists to wholesale customer accounts. Each account (e.g., ‘Retailer XYZ’) sees prices from their assigned list (e.g., ‘wholesale_list_v3’) at checkout. Merchants can bulk-upload price lists, and price lists support per-product discounts or per-category markups. Accounts are identified by email domain or explicit account assignment.

The problem

A distributor sells to 50 wholesale retailers at different prices; Retailer A gets 40% off, Retailer B gets 30% off. Currently, they manually manage 50 SKUs and create separate purchase orders for each retailer, error-prone and labor-intensive. A price-list system where each customer account sees custom prices would streamline this.

What it does

  • Create price lists: ‘wholesale_list_v3’ with rules like {product_id: ‘shoe-001’, discount_pct: 40} or {category: ‘shoes’, markup_pct: -40}
  • Assign price list to customer account: customer.assigned_price_list_id = ‘wholesale_list_v3’
  • Bulk upload: CSV format [product_id, price_minor_units] → creates or updates price list
  • Storefront pricing: when logged-in customer adds product to cart, system checks their price_list_id and applies custom prices
  • Price list versioning: merchant creates ‘wholesale_list_v4’, old orders still reference ‘v3’, new orders use ‘v4’
  • Admin dashboard: list all price lists, customer assignments, and bulk upload interface
  • Audit log: track price list changes (created, deleted, price updated) and who made the change
  • Fallback: if customer has no price list, use standard product price

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.

  • Time-based price list switching (see scheduled-promotions-manager for time-based pricing)
  • AI price optimization (paid module owns this)
  • Tiered pricing within a price list (e.g., ‘buy 10–50 units at €5, buy 50+ at €4’; use quantity-discounts for this)

Data model

Add price_lists table: { id, shop_id, list_name, version, created_at, is_active }. Add price_list_rules table: { id, list_id, product_id (optional), category (optional), discount_or_markup_pct, override_price_minor_units (if exact price, not %) }. Add customer.assigned_price_list_id. Schema migration: new tables, add field to customer.

API

  • POST /admin/price-lists — create new price list
  • PUT /admin/price-lists/:id — edit list name or activate/deactivate
  • POST /admin/price-lists/:id/upload — bulk upload CSV [product_id, price]
  • GET /admin/price-lists/:id/rules — read rules in price list
  • PUT /admin/customers/:id/price-list — assign price list to customer
  • GET /products/:id?price_list=wholesale_v3 — get product pricing for specific price list (admin only)

Every route added here must also appear in src/pages/openapi.json.ts — a test fails the build if it does not.

Admin

Add ‘Price Lists’ section in Commerce > Pricing. List all price lists (name, version, active status). Click to edit: view rules table. ‘Upload CSV’ button for bulk import. ‘Create new list’ form. Customer admin: field ‘Assigned price list’ with dropdown of available lists. Toggle ‘active’ to deactivate a list (old orders keep their version).

The seam — why this is core

Core owns price list structure and customer assignment. Merchants manually create and manage price lists. Paid AI module owns price optimization (analyzes competitor prices, recommends discounts to win orders).

Core owns customer segmentation, price list primitive, and variant pricing model; paid modules own AI pricing rules, volume breakpoint logic, and complex net-price calculations

Dependencies

  • Customer system (must have assigned_price_list_id field)
  • Product system (price list rules reference products)
  • Checkout flow (to apply custom prices based on customer’s price list)

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.

  • Merchant creates ‘wholesale_v1’ with rules: product ‘shoe-001’ = €10 (40% off €16.67 standard)
  • Customer ‘retailer@example.com’ assigned to ‘wholesale_v1’; adding shoe-001 to cart shows €10, not €16.67
  • Bulk upload: CSV with [shoe-001, shoe-002, shoe-003] and [1000, 1200, 1500] creates price list with 3 products
  • Merchant creates ‘wholesale_v2’, reassigns customer; customer’s old order still references v1 pricing, new orders use v2
  • Customer without assigned price list sees standard prices

Risks

If price list is applied retroactively (old orders’ prices recalculated), customer sees different invoices (never do this; price list version is immutable per order). If bulk upload doesn’t validate product IDs (ensure every product_id in CSV exists before importing).

Commercial context

Suggested pricefree (core)
Rival anchor9 apps (BSS, SparkLayer, Clay, Massy, etc.); all freemium with core pricing rules free, paid $29-199/mo for advanced logic

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.