AstroBaaS

Catalogue & product data

B2B Wholesale Portal

Free — GPL coresize Lplanned, not built

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

Merged from duplicate proposals: “B2B Wholesale Portal & Registration”

Core infrastructure for B2B wholesale buyers to place bulk orders with custom pricing, net-30 payment terms, and quote-to-order workflow. Adds wholesale buyer roles, bulk-order pages with per-buyer price sheets, and order history for B2B customers. This is the foundational feature set; invoicing and payment terms enforcement belong in accounting integration (paid tier). Opens a new revenue stream and buyer segment for merchants.

The problem

My wholesale buyers need bulk-order pages with custom pricing per buyer, quote-to-order workflow, and 30-day payment terms. I lose revenue because they can’t buy in bulk through my storefront and I can’t offer negotiated rates.

What it does

  • Define buyer roles: ‘wholesale_buyer’ (can access bulk order portal, see custom prices, place orders with net-30 terms)
  • Bulk pricing: per-product price override per buyer (or per buyer tier: ‘Silver’, ‘Gold’, ‘Platinum’), stored as price_override_cents with effective_start/end dates
  • Bulk-order form: search by SKU or product name, add qty, show custom price, calculate line totals and order total at a glance
  • Quote workflow: buyer submits a quote request (cart draft), merchant reviews and issues a quote (PDF, email), buyer accepts quote and converts to order
  • Order placement: buyer can place orders with net-30 payment terms (merchant manually tracks payment, core does not enforce payment gating)
  • Order history: buyer sees all their orders (bulk and retail), filter by date, status, total amount
  • Merchant bulk-pricing dashboard: manage prices per buyer/tier, see override effective dates, audit price changes
  • Buyer tier assignment: assign a buyer to a tier (‘Silver’, ‘Gold’, etc.), inherit that tier’s bulk pricing automatically
  • Bulk discount tiers: optional — offer incrementally better prices based on order quantity (e.g., qty 1-100 gets 5% off, 101-500 gets 10% off)
  • Order approval workflow: optional — merchant can require approval for net-30 orders above a threshold amount
  • Bulk export of order data: buyer can export their order history as CSV
  • Public bulk-order API: GET /wholesale/buyers/:id/pricing, POST /wholesale/orders (requires wholesale_buyer role)

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.

  • Automatic payment gating or credit lines — core does not gate order placement based on payment status; merchant manually reviews credit and approves/rejects
  • Invoice generation or payment reconciliation — that’s accounting integration; core stores orders with net-30 flag; merchant’s ERP reconciles
  • Per-buyer contract terms (e.g., ‘Buyer X gets 15% discount on category Y only’) — stored as simple price overrides; complex rules belong in a rules engine (paid tier)
  • Tiered discounts computed at checkout — merchant sets discount per tier/qty; buyer sees it before submitting order, not computed dynamically
  • Catalog restrictions per buyer — all buyers see all products; access control is at purchase only (net-30 orders require approval)

Data model

New entities: wholesale_buyer_account (id, customer_id, tier: string, created_at, is_active: boolean), wholesale_tier (id, name, discount_percentage: integer, created_at), bulk_price_override (product_id, variant_id, wholesale_buyer_id: nullable, wholesale_tier_id: nullable, price_cents: integer, effective_start_date, effective_end_date, created_by: user_id), bulk_quote (id, wholesale_buyer_id, items: array of {product_id, variant_id, qty, price_cents}, total_cents: integer, created_at, quoted_at: nullable, quoted_by: user_id, accepted_at: nullable), bulk_order (order_id, wholesale_buyer_id, payment_terms: ‘net-30’ | ‘net-60’ | ‘other’, approval_status: ‘pending’ | ‘approved’ | ‘rejected’). No schema migration for existing orders.

API

  • GET /wholesale/buyers/:id/pricing — list all bulk prices for a buyer (custom overrides + tier discounts)
  • GET /wholesale/buyers/:id/bulk-prices/product/:product_id — pricing for a single product
  • POST /wholesale/buyers/:id/bulk-prices — set a custom price override for a product (admin only)
  • DELETE /wholesale/buyers/:id/bulk-prices/:override_id — remove a price override (admin only)
  • GET /wholesale/buyers — list all wholesale buyers with tier and status (admin only)
  • POST /wholesale/buyers — assign a customer to a wholesale tier (admin only)
  • PUT /wholesale/buyers/:id/tier — change a buyer’s tier (admin only)
  • POST /wholesale/quotes — create a quote from a cart (requires wholesale_buyer role)
  • GET /wholesale/quotes/:id — retrieve a quote (PDF or JSON)
  • POST /wholesale/quotes/:id/accept — accept a quote and convert to order
  • POST /wholesale/orders — place a bulk order (requires wholesale_buyer role, net-30 terms applied)
  • GET /wholesale/orders/:buyer_id — list all bulk orders for a buyer (requires wholesale_buyer role)
  • GET /tiers — list all wholesale tiers and their discount percentages

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

Admin

Admin panel gains: (1) Wholesale Buyers section (list, assign tier, active/inactive toggle), (2) Bulk Pricing manager (set price overrides per buyer or per tier, with effective dates), (3) Wholesale Tiers configuration (create/edit tiers with discount %), (4) Quote management (view pending quotes, issue quote PDF, track acceptance), (5) Bulk Order approval workflow (if enabled, flag orders for manual review before fulfillment), (6) Buyer order history and metrics (total orders, avg order value, tier performance). Each wholesale buyer has a dedicated bulk-order portal link.

The seam — why this is core

Core owns the wholesale buyer role, the pricing override schema, the quote and order models, and the bulk-order form UI. The merchant remains responsible for credit decisions and payment terms enforcement; core does not gate fulfillment. Paid tier could own advanced features: credit line gating, automated approval rules based on buyer tier, invoice generation and payment reminders, and per-buyer contract rules.

Specialized for B2B merchants. Enables different revenue stream and customer segment. Justifies paid because it opens new business model and requires support.

Dependencies

  • product-management (core; wholesale buyers browse and order products)
  • customer-accounts (core; wholesale buyers are customers with an extended role)
  • order-system (core; bulk orders are orders with a net-30 payment terms flag)
  • auth-system (core; wholesale_buyer role enforcement)

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.

  • A customer assigned to ‘Gold’ tier automatically sees all Gold tier bulk prices for products they browse, without individual overrides.
  • A custom price override for Buyer X on Product Y (set to €10) overrides the Gold tier price (€12) only for that buyer-product pair.
  • Bulk-order form shows qty field, unit price (custom or tier), and line total (qty × unit price) for each product added.
  • A quote created from a bulk cart generates a PDF with buyer name, items, unit prices, total, and a quote acceptance link.
  • Accepting a quote converts it to an order with payment_terms=‘net-30’ and approval_status=‘pending’ (if approval workflow enabled).
  • A wholesale buyer sees only their own orders in GET /wholesale/orders/:buyer_id; other buyers’ orders are not visible.
  • Changing a tier’s discount % from 10% to 15% does not retroactively apply to already-placed orders.
  • Bulk pricing effective_start_date=‘2026-09-01’ means the price is not shown/used before that date.

Risks

Custom price overrides per buyer can create inconsistency if a merchant forgets to update one buyer after a tier discount change. Net-30 payment terms in core do not enforce payment gating; a merchant could accidentally fulfill orders to non-paying buyers. A buyer tier with 50% discount can be exploited if the merchant’s cost is higher; no safeguard prevents a below-cost price override.

Commercial context

Suggested price€50–100/month
Rival anchorAdobe Commerce B2B (€5k+/year); third-party solutions €200–500/month

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.