18 · Customers CRM & storefront

Who customers are in the tenant DB, staff CRM, customer self-service login, and the public online store that reuses the POS cart/finalize spine.

POS attaches customers for loyalty Storefront reuses /api/pos allowGuestCheckout mostly config/UI — not hard POS gate

How customers sit in the platform

flowchart TB
  CRM[(Customers · tenant main)]
  POS[POS cart · customerId]
  LOY[LoyaltyAccount]
  GC[Gift card optional customerId]
  TIX[Service tickets · portal]
  SF[Storefront order]
  AP[Appointments]
  INV[Invoices]

  CRM --> POS
  CRM --> LOY
  CRM --> GC
  CRM --> TIX
  CRM --> AP
  POS --> INV
  SF --> POS
  SF -.->|optional login| CRM
          

Same person can be staff-CRM customer, loyalty member, ticket requester, and storefront shopper — linkage quality depends on email/phone match and whether POS attaches them at sale time.

Staff CRM

SurfacePath
Customer list/customers
Create / edit/customers/new · /customers/:id
POS search/attachModern/Mobile customer search → cart attach

API — /api/customers

Session + tenantResolver. Direct SQL multi-tenant.

MethodPathPurpose
GET/searchQuick POS search (name/email/phone)
GET/List
GET/:idDetail
POST/Create
PUT/PATCH/:idUpdate
DELETE/:idDelete
GET/:id/statsPer-customer stats

Record shape (core)

orgId, name, email, phone, notes, metadata/address — table Customers on tenant main DB.

Also: /api/simple simple-customers (search/list) — lighter path used in some tools.

Customer-facing accounts

SurfacePathRole
Register/:tenantSlug/customer/registerCreate account
Login/:tenantSlug/customer/loginCustomer session
Dashboard/:tenantSlug/customer/dashboardCustomerProtected shell

API — mounted at /customer

PathPurpose
POST /customer/registerPublic registration (password)
POST /customer/loginPublic login → session.customer
…/customer/ticketsLogged-in customer ticket list/create/comments
Two “customer” concepts can blur (1) CRM row used by staff POS/loyalty · (2) portal login session under /customer. They should join on email/tenant; do not assume every CRM row has a portal password.

Customer analytics

/api/customer-analytics (session + tenant)

  • GET /:customerId — detail analytics
  • GET /insights/overview · segments · products

Feeds marketing-style insights; separate from sales reports main DB path (see analytics gaps in audit).

Storefront (online store)

SurfacePath
Public shop/:tenantSlug/store · /embed/store · staff preview /store
Admin settings/admin/storefront

Config (tenant)

Table-ish storefront_settings via /api/storefront:

  • enabled, storeName/description, colors, logo, custom CSS/JS
  • productsPerPage, currency, taxIncluded, shippingEnabled
  • allowGuestCheckout, requireAccount
  • shopCatalogId — full catalog when no menu assignment
  • Menu items from catalog assignments targeting storefront

Public APIs

MethodPathAuth
GET/api/storefront/:tenantSlug/configPublic
GET/api/storefront/:tenantSlug/menu-itemsPublic
DELETE…/carts/:cartId/items/:lineIdStorefront cart helper
GET/PUT/api/storefront/configSession admin

Storefront checkout path

flowchart TD
  CFG[Load public config + menu-items]
  CART[pos.createCart / posApi cart ops]
  ADD[addItems · tip · tender card/crypto]
  FIN[posApi.finalize]
  INV[Core Invoicing]

  CFG --> CART --> ADD --> FIN --> INV
          
Good design No separate checkout stack — same cart/tender/finalize as POS (DocHub 05 / 14).
Auth reality check /api/pos/* is mounted with requireSessionAuth. Storefront UI calls those endpoints. True anonymous public checkout only works if something establishes a session (staff preview, kiosk device flow, or a future guest/session design). allowGuestCheckout is stored and returned in config; it is not a server-side gate inside pos.routes createCart/finalize (audit S9-4b).

Gaps & product notes

ItemNotes
Guest checkout enforcementConfig flag vs POS session requirement — reconcile for real public e-com
CRM ↔ portal accountUnify identity so loyalty works after storefront login
Storefront gift/loyalty tendersUI path is mainly card/crypto; gift/loyalty stronger on Modern POS
ShippingFlag exists; not a full fulfillment engine in this pass
Master-admin “customers” placeholderTenant CRM is under /customers, not master-admin customers tile

File anchors

ConcernPath
Customers APIbackend/routes/customers.routes.js
Public customerpublic-customer.routes.js · mount /customer
Simple customerssimple-customers.routes.js
AnalyticscustomerAnalytics.routes.js · service
Staff CRM UIpages/customers/Customers.jsx · CustomerEditor.jsx
Customer portal UICustomerLogin/Register · CustomerDashboard.jsx
Storefront APIstorefront.routes.js
Storefront UIpages/store/Storefront.jsx
Admin settingsStorefrontSettings.jsx
OAO19-storefront.html

Related DocHub

E-commerce roadmap Guest session + card rail through Core Invoicing — see 20 · Crypto & payments.