Customers & accounts
B2B suite (company accounts, quotes, requisition lists)
Indicative price, not an offer: €199–599/month
Generated from docs/plan/paid/b2b-suite/PLAN.md in the AstroBaaS repository. Nothing described below is implemented — it is the written plan for it.
B2B Suite provides company account management, bulk ordering, purchase orders, approval workflows, and tiered pricing per customer or customer group, enabling merchants to serve B2B buyers with complex business processes.
The problem
No company entity, no VAT ID field, no purchase orders, no requisition lists, no shared catalogs per customer. B2B merchants locked out.
What it does
- Company accounts: entity with multiple employees
- VAT ID and company registration number
- Shared company address book
- Requisition lists: named carts (Monthly Supplies) reusable
- Purchase order mode: PO number during checkout
- Approval workflows: order over 500 EUR requires manager approval
- Tiered pricing: price by order quantity (1-10=50 EUR, 11-100=45 EUR, 100+=40 EUR)
- Tiered pricing per customer: override global pricing
- Bulk pricing editor: CSV upload of customer/sku/price
- B2B customer roles: employee, manager, buyer, admin
- Invoice payment terms: Net 30/60/90
- Sales rep assignment
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.
- EDI integration — data import/export out of scope
- Accounting integration (erp-accounting-bridge is separate) — order/PO only
- Credit limit enforcement — separate credit-terms module
- Negotiated pricing per order (dynamic at checkout) — pricing set ahead
- Complex multi-level approval (manager to director to CFO) — single-level
- Employee invitation workflow — separate b2b-onboarding module
Data model
Company: id, store_id, name, vat_id, registration_number, industry, employee_count, created_at. CompanyEmployee: id, company_id, customer_id, role, created_at. CompanyAddress: id, company_id, type, street, city, postal_code, country. RequisitionList: id, company_id, name, description, items (JSON), created_by, created_at, is_shared. TieredPrice: id, store_id, sku, min_qty, max_qty, price, customer_id, company_id. Order: +po_number, +requires_approval, +approved_by, +payment_terms, +due_date. Migration: add Company, CompanyEmployee, CompanyAddress, RequisitionList, TieredPrice; add columns to Order.
API
- POST /admin/companies
- GET /admin/companies
- GET /admin/companies/{id}
- PATCH /admin/companies/{id}
- POST /admin/companies/{id}/employees
- DELETE /admin/companies/{id}/employees/{employee_id}
- GET /admin/companies/{id}/requisition-lists
- POST /storefront/requisition-lists
- GET /storefront/requisition-lists
- PATCH /storefront/requisition-lists/{id}
- POST /storefront/requisition-lists/{id}/checkout
- POST /admin/tiered-prices
- POST /admin/orders/{id}/approve
- POST /admin/orders/{id}/deny
Every route added here must also appear in src/pages/openapi.json.ts — a test
fails the build if it does not.
Admin
Company management: list companies, employees, total orders, revenue. Detail: info, employees (roles), addresses, custom tiered prices. Price bulk editor: CSV or form grid. Approval queue: awaiting-approval orders, approve/deny buttons. Requisition lists: view created by employees. Sales rep assignment.
The seam — why this is paid
Core owns: Company table, employee mgmt, address book, order data (po_number, requires_approval, approved_by). Paid module owns: approval workflow UI, tiered price editor, requisition lists, payment terms, B2B role system, sales rep assignment.
Adobe-tier feature requiring deep product/customer modeling. Substantial feature set worth high support commitment.
Dependencies
- customer-profiles
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.
- Create company ACME Corp with VAT DE123456789; add/remove employees
- Set tiered price: 1-10=50 EUR, 11-100=45 EUR, 100+=40 EUR; order 15 units shows 45 EUR
- Set custom price for ACME WIDGET: 1-50=40 EUR (override global); ACME order shows 40 EUR
- Requisition Monthly Office Supplies (10 items); add to cart causes all 10 added
- Order 600 EUR (threshold 500 EUR) awaits approval; manager gets email
- Manager clicks Approve in email causes order processed
- Order includes PO PO-2024-09-001; invoice shows PO
- Payment terms Net 60; invoice due 60 days after order
Risks
Approval stuck if manager doesn’t click (no auto-escalation). Tiered calc wrong (wrong bracket applied). Requisition list race condition (edited while adding). Employee approves own order. No price for custom tier. CSV import 10k prices hangs. Missing prices crash checkout.
Commercial context
| Suggested price | €199–599/month |
| Rival anchor | Adobe Commerce only (not Magento Open Source). Magento OSS merchants cannot do B2B. |
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.