Openfront Coffee Shop

Menu, pickup ordering, POS, KDS, register, inventory, recipes, rewards, catering, subscriptions, and reporting for one cafe.

Source · Live site · Catalog

Openfront Coffee Shop is a self-hosted app for one cafe. Customers browse the menu, order ahead for pickup, ask for catering, and keep a rewards balance. Staff take counter sales on a POS, run a bar display (KDS), manage the register drawer, buy and count stock, build recipes, make prep batches, schedule shifts, and manage rewards, catering, and subscriptions.

Customers pay at pickup or at the counter (cash, or a card terminal the cashier confirms). Online card payment through Stripe Checkout is off by default. It needs STRIPE_SECRET_KEY and STRIPE_WEBHOOK_SECRET on the server and the switch turned on in Store Settings.

Start with Getting started and Deployment. Each dashboard area has its own page, beginning with Orders. Task guides are under How-to guides. Operations are listed in the API reference.

Stack

  • Next.js 16 App Router and React 19.
  • Keystone 6 with a GraphQL API at /api/graphql.
  • Prisma and PostgreSQL.
  • Lists live in features/keystone/models. Custom queries and commands live in features/keystone/mutations and are wired in mutations/index.ts. Platform projection types are in mutations/coffeeBoundarySchema.ts.
  • docs/schema-map.md in the repository maps every list, its states, and its write path.

Tenancy

One store per deployment. StoreSetting is a singleton (singletonKey = "primary"), and every other list has a required store relationship to it. Currency is USD only (the currencyCode field rejects anything else). Money is stored in cents. Stock quantities are decimals in each inventory item's own unit.

Models

AreaLists
StoreStoreSetting (name, address, hours, timezone, pickup lead time, window, and capacity, tax rate in basis points, logo)
MenuMenuCategory, MenuItem (price, availability, bar station: espresso, cold, or brew bar), MenuItemImage, MenuItemModifier (group, min/max selections, price adjustment, in-stock flag, inventory mapping)
OrdersCafeOrder (online, POS, or phone; pickup, counter, or dine-in), OrderItem (price and modifier snapshot, bar status), PosOrder (register receipt)
MoneyPayment, PaymentEvent (webhook replay record), PaymentProvider (enablement only, no secrets), RegisterShift, CashMovement
StockInventoryItem, InventoryLot, StockMovement (ledger), Supplier, PurchaseOrder (one item per PO), WasteLog
ProductionRecipe, Ingredient, PrepBatch, PrepBatchConsumption
PeopleUser, Role, StaffShift
RetentionLoyaltyAccount, LoyaltyEvent, Subscription (a plan record), CateringOrder
SystemCoffeeDomainEvent, CoffeeOutboxDelivery (transactional outbox, hidden and system-only)

Role has read and manage flags for orders, payments, products, inventory, and loyalty, plus canAccessDashboard, canManagePeople, canManageRoles, canManageSettings, canManageOnboarding, canSeeOtherPeople, and canEditOtherPeople.

Main workflows

Online pickup order. The customer builds a cart (kept in the browser), gets a quote (quoteCafeOrder), and places the order (createCafePickupOrder). The order starts as open. It is a request: staff accept it (acceptCafePickupOrder), which consumes stock from lots and prep batches. KDS bumps move each item from queued to preparing, ready, and handed_off. Staff record payment at pickup (recordCafePayment) and complete the order. Online requests pause when too many are open at once.

Counter sale. Open a register shift, ring up items on the POS, and take cash or a card terminal approval. createCafePickupOrder with a register shift creates a paid CafeOrder, a PosOrder, and a Payment, and consumes stock.

Order states. open can only be cancelled or accepted. accepted goes to in_bar_queue or cancelled. paid goes to in_bar_queue. Then preparing, ready, completed. Refunds move a paid order to refunded.

Refund. refundCafePayment refunds a counter payment from the current drawer, or a Stripe payment back to the card. It updates the order, the POS receipt, register totals, and reverses rewards points.

Drawer. openRegisterShift, recordRegisterCashMovement (pay-in, pay-out, drop), and closeRegisterShift with the counted cash and variance.

Stock. Create a purchase order, receive it (creates an active lot and a received movement), or cancel it. Record waste and stock counts. Low stock means at or below the reorder point.

Prep. Complete a prep batch from an active recipe. It consumes the recipe's ingredients. Later orders draw from the batch.

Staff shifts. Schedule a shift, then clock in, clock out, or mark it missed.

Rewards. applyCafeLoyalty links an order to a loyalty account by email and earns 1 point per dollar of subtotal. Staff can adjust points, drink credits, tier, and status with adjustCoffeeLoyaltyAccount.

Catering. Customers send an inquiry from /catering (requestCafeCatering), or staff create one. Status goes inquiry, quoted, confirmed, completed, or cancelled (updateCateringOrderStatus).

Subscriptions. Staff create a subscription against a loyalty account and move it between trialing, active, paused, and cancelled (updateSubscriptionStatus).

GraphQL boundary

Custom operations are the intended write path. Most commands take an idempotencyKey, and many take an expectedStatus or expectedUpdatedAt to reject stale writes.

Customer and public queries: getCoffeeStore, getCoffeeMenu, quoteCafeOrder, getCafeOrder (needs the order secretKey or the owning session), getCafeLoyaltyAccount, getCoffeeCustomerOrders, getCoffeeCustomerCatering.

Customer commands: registerCoffeeCustomer, updateCoffeeCustomerProfile, createCafePickupOrder, applyCafeLoyalty, requestCafeCatering, initiateCafePaymentSession.

Staff projections: getCoffeeOrderQueue, getCoffeeKdsBoard, getCoffeeKdsRecoveryScope, getCoffeeMenuManager, getCoffeeMenuInventoryOptions, getCoffeePosRegister, getCoffeeReports, getCoffeeOperations.

Staff commands:

  • Orders: acceptCafePickupOrder, updateCafeOrderStatus, updateCafeOrderItemStatus, recordCafePayment, refundCafePayment, updatePosOrderStatus.
  • Menu: createCoffeeMenuCategory, createCoffeeMenuItem, updateCoffeeMenuItem, createCoffeeMenuModifier, updateCoffeeMenuModifier, updateCoffeeModifierStock.
  • Register: openRegisterShift, recordRegisterCashMovement, closeRegisterShift.
  • Stock: createCoffeePurchaseOrder, receiveCoffeePurchaseOrder, cancelCoffeePurchaseOrder, completeCoffeePrepBatch, recordCoffeeWaste, recordCoffeeStockCount.
  • People and retention: scheduleCoffeeStaffShift, updateCoffeeStaffShiftStatus, adjustCoffeeLoyaltyAccount, updateCateringOrderStatus, updateSubscriptionStatus.
  • Setup and system: runCoffeeOnboarding, handleCafePaymentWebhook, redirectToInit.

Some records still use generated Keystone CRUD from the platform pages: suppliers, inventory item metadata, recipes and ingredients, catering facts, subscription creation, store settings, and the payment provider switch. Menu item price and availability, modifier stock, stock levels, order state, and payments are command-only.

Onboarding

The dashboard sidebar shows an onboarding card until the operator completes or dismisses it. The card opens a dialog that runs runCoffeeOnboarding with one of three templates:

  • Starter cafe (minimal): 2 categories, 3 menu items, 4 modifiers, 5 inventory items, 2 recipes.
  • Full neighborhood cafe (full): 4 categories, 5 menu items, 10 modifiers, 8 inventory items, 1 supplier, 3 recipes.
  • Custom setup: paste your own JSON, review it, then confirm.

Both templates create the store "Neighborhood Roast" and three payment providers: Manual / Cash (enabled), Stripe (disabled), and PayPal (disabled). Onboarding only creates missing records. It never overwrites existing ones, so running it twice is safe. Template stores get an empty contact email until you set one.

Integrations

  • Payments. features/integrations/payment has Stripe, PayPal, and manual adapters. Webhooks arrive at /api/payment-providers/[providerCode]/webhook and only accept pp_stripe_stripe or pp_paypal_paypal. Stripe Checkout powers online payment and online refunds.
  • Email. SMTP sends password reset emails.
  • Storage. Menu images use S3-compatible storage.
  • Dashboard AI chat. /api/completion uses OpenRouter (OPENROUTER_API_KEY, OPENROUTER_MODEL, OPENROUTER_MAX_TOKENS) and the MCP transport at /api/mcp-transport/[transport].
  • Health. /api/health checks the database and returns 200 or 503.

On this page