Parent
Epic: #1245
What to build
B2B portal at b2b.<tenant>.demo.local — the (wholesale)/ route group of template-apparel-erp. Authenticated wholesale customers place line-sheet orders against negotiated terms.
Auth
Uses @happyvertical/smrt-users session. Customers gain access by having customerType: 'wholesale' on their Customer record. Non-wholesale customers visiting the subdomain get redirected to the storefront.
Pages
/ — account summary: outstanding balance, recent orders, payment-due dates
/line-sheet — full line sheet (Styles × Makeups × Colorways × Sizes grid) with wholesale prices and per-unit case packs
/order/new — line-sheet order builder: bulk-quantity entry per size grid, running totals, MOQ enforcement
/orders — order history, status, tracking
/orders/[id] — order detail with downloadable PO confirmation PDF
/invoices — open invoices, due dates, "Pay invoice" link (NET-30: separate flow, no Stripe at point of order)
Tier gate
The (wholesale)/ route group is hidden entirely if the tenant's commerce.wholesalePortal feature is off (Starter tier). The subdomain returns 404 in that case.
Order flow
Creates a WholesaleOrder (Contract STI, already shipped). Status flow: DRAFT (in the builder) → SENT (submitted by buyer) → ACCEPTED (sales team approves) → COMPLETED (fulfilled). Invoice generated on ACCEPTED with NET-30 due date. Stock reserved on ACCEPTED, not on SENT (so buyers can submit speculative orders without holding inventory).
Acceptance criteria
Blocked by
- #1251 — template scaffold
Parent
Epic: #1245
What to build
B2B portal at
b2b.<tenant>.demo.local— the(wholesale)/route group oftemplate-apparel-erp. Authenticated wholesale customers place line-sheet orders against negotiated terms.Auth
Uses
@happyvertical/smrt-userssession. Customers gain access by havingcustomerType: 'wholesale'on theirCustomerrecord. Non-wholesale customers visiting the subdomain get redirected to the storefront.Pages
/— account summary: outstanding balance, recent orders, payment-due dates/line-sheet— full line sheet (Styles × Makeups × Colorways × Sizes grid) with wholesale prices and per-unit case packs/order/new— line-sheet order builder: bulk-quantity entry per size grid, running totals, MOQ enforcement/orders— order history, status, tracking/orders/[id]— order detail with downloadable PO confirmation PDF/invoices— open invoices, due dates, "Pay invoice" link (NET-30: separate flow, no Stripe at point of order)Tier gate
The
(wholesale)/route group is hidden entirely if the tenant'scommerce.wholesalePortalfeature isoff(Starter tier). The subdomain returns 404 in that case.Order flow
Creates a
WholesaleOrder(Contract STI, already shipped). Status flow: DRAFT (in the builder) → SENT (submitted by buyer) → ACCEPTED (sales team approves) → COMPLETED (fulfilled). Invoice generated on ACCEPTED with NET-30 due date. Stock reserved on ACCEPTED, not on SENT (so buyers can submit speculative orders without holding inventory).Acceptance criteria
b2b.<tenant>.demo.localroutes to(wholesale)/Blocked by