Skip to main content

Lucille Order Center documentation

Lucille Order Center is an internal web application for the Lucille restaurant group. Chefs log in, browse one unified ingredient catalog, add what their kitchen needs to a cart, and submit. Behind that single action the system resolves every line item to the supplier that actually fulfils it and dispatches to each of them in parallel — an X12 850 EDI file dropped on Sysco's SFTP server, a structured order email to Costco Business Center, and a generated PDF purchase order emailed to the group's own Central Kitchen.

The chef never chooses a supplier, and never learns which one filled which line. That abstraction is the product, and it is enforced in the API's serialisation layer rather than by hiding fields in the UI. Everything else in this documentation exists to explain how it is kept true.

Why the project exists

Ordering at Lucille is spread across email, WhatsApp, SMS, phone calls and one supplier portal per vendor. Orders get missed, nobody has a consolidated view of what was bought, and every week costs the kitchens hours of coordination. The Order Center replaces that with one interface, one append-only audit trail, and automatic recovery when a supplier reports a back order.

The build is a proof of concept in scope but production-grade in construction: three supplier integrations, roughly 40 catalog items per supplier (about 120 in the seed), five seeded restaurant locations, two roles with real server-side enforcement, and an audit trail that a database trigger makes impossible to rewrite.

How this documentation is organised

SectionWritten forStart at
ProductChefs, administrators, the operations team, anyone deciding what the system should doProduct overview
TechnicalEngineers building, operating or extending the systemArchitecture

If you are a chef

Read the chef guide. It walks through signing in, choosing a restaurant when you are assigned to more than one, browsing the catalog, building a cart, reviewing and submitting it, and reading an order afterwards — including what a back order looks like and why you usually do not have to do anything about it.

If you are the administrator

Read the administrator guide. It covers the five admin screens as they are built: catalog items with their mappings, supplier records and connection config, the SKU-to-supplier mappings with their all-important alternate supplier, restaurants and chef assignment, and cross-restaurant order oversight with the global audit feed. Pair it with roles and permissions for exactly what each role can reach, and order lifecycle for the status values and the reroute flow.

If you are an engineer

Start with architecture, then go where your task takes you:

The system in one diagram

Browser (desktop primary, responsive to 375px)
│ HTTPS

[ Frontend SPA ] orders.<domain>
React 18 + Vite, TanStack Query, Zustand
│ REST/JSON (VITE_API_BASE_URL)

[ API ] api.<domain> [ Docs ] docs.<domain>
Fastify 4, Prisma 6, Zod validation Docusaurus (this site)
│ │
│ enqueue └──► [ PostgreSQL 15 ]
▼ ▲
[ Redis ] ──► [ BullMQ worker ] ─────┘
2 replicas
├──► Sysco SFTP + X12 850
├──► Costco SMTP email
└──► Central Kitchen PDF + SMTP, archived to object storage

[ sysco-inbound-poll CronJob ] every 15 minutes → 855 / 997 → API → reroute jobs
[ Zitadel ] OIDC: Authorization Code + PKCE, project-role claims

The three applications are deployed on separate subdomains — the API at api.<domain>, the SPA at orders.<domain> and this documentation at docs.<domain>. An earlier sketch put the API and the SPA on one orders subdomain; that is not expressible for two separate Deployments in the infrastructure templates, so the split is the real layout and the SPA reaches the API through VITE_API_BASE_URL.

Conventions used here

These pages are written against the source in this repository — the route definitions and their guards, the shared Zod enums, the domain modules and the frontend pages — not against the original plan. Where the plan or the PRD describes something that is not built, the page says so explicitly and says why, rather than describing it as though it exists. Two examples you will meet: delivery dates are not modelled anywhere, and the human-readable purchase-order number exists in the database but is not yet returned by any endpoint.

Where a value is genuinely undetermined — most notably Sysco's partner-specific EDI identifiers, which only Sysco's Trading Partner Implementation Guide can supply — the page says so instead of inventing a plausible-looking answer; those values are driven from supplier configuration precisely so the guide can be applied without a code change. Diagrams are ASCII inside fenced code blocks rather than Mermaid, because the Mermaid theme is not part of this workspace's dependency set.

note

This site is internal. It documents a system reachable only from inside Lucille's network, and it references credential shapes only — never credential values. Secrets live in Vault and are injected into pods at runtime; supplier configuration stores the names of those secrets, and the API rejects anything that looks like a plaintext credential.