PRD · approved · companion to prd.md

Cuenta

Her paper invoice, come to life: every screen, behavior, state and check.

The journey

Judge path: Count, Deliver, Invoice and Ledger on the deployed URL with mock data, under three minutes, using Corazon Cocina.

Click a screen in the diagram, or use the tabs below, to see its behavior, states and checks.

FIRST OPEN PHONE · CAFE LAPTOP · KITCHEN PHONE PIN30-day device Countblank steppers Deliverqty typed in Invoicethe Canva form LedgerNet 30 · paid Email pop-up Customersread-only grey ref envelope + "Emailed" Edit (reuses Deliver) Count never feeds an invoice · an invoice never changes a count
Both devices · first open or after 30 days

PIN Gate

Added in 5-buildFour digits with no brake was 10,000 guesses on a public URL. Now: six digits, with attempt limiting.
  • The La Condesa wordmark, one six-digit field, one button. On a phone the field opens the numeric keypad.
  • Wrong: "That's not it." in serif under the field.
  • Right: remembered on that device for 30 days, then lands on Count.
Attempt limiting
  • 5 wrong attempts from one IP within 15 minutes lock that IP for 15 minutes.
  • 20 wrong attempts from anywhere within 15 minutes lock the PIN screen for everyone for 15 minutes.
  • While locked, every attempt (even the right PIN) is refused with "Too many tries. Wait 15 minutes." in the same quiet serif line.
  • Every failed attempt is logged with its IP and time, never the digits tried.
Checks
  • A wrong PIN shows "That's not it." and stays on the PIN screen.
  • Six wrong attempts in a row: the sixth is refused with "Too many tries. Wait 15 minutes."
  • The right PIN during a lockout is still refused; after 15 minutes it works again.
  • The attempted PIN never appears in any log.
  • Reopening on the same device within 30 days goes straight to Count.
  • No screen or data request works without a valid PIN session.

Every screen

Added in 5-buildA "?" on every page, words beside every icon, and a send button that never says "Resend" (also the email provider's name).
How it works
  • A small "?" in the top corner of every page, the PIN screen included, opens a plain-words page.
  • One short section each for Count, Deliver, Invoice page, Email pop-up, Ledger and Customers: what the screen is for and what each button does, using the buttons' exact names.
Words, not just icons
  • No icon without words: every icon comes with words she can read, such as the envelope with "Emailed Oct 17". On a laptop, hovering shows the full detail, and screen readers hear the same words.
  • Button names: the send button reads Send email the first time and Send again after a successful send, on the Invoice page, in the Email pop-up, in the Ledger and on the How it works page.

Operational requirements

Logging, from block 1
  • One JSON line per server event: time, route, outcome, duration, plus the message ID or error for sends.
  • Never logs emails, addresses, phones, invoice bodies, the PIN or env vars. Only the invoice number and cafe name.
  • The host's function logs are the viewer.
Health and client
  • /health: database status and app version (cold start vs outage).
  • Every request failure shows a one-line message, never a blank screen, and logs to the console with the route name.

Boundary

Building now
  • The six-digit PIN gate with attempt limiting · Count with blank steppers, partial save, the grey last count and a read-only Past counts list · Deliver with the menu, grey shelf counts, live totals in cents, the suggested and unique invoice number, special order lines and Edit · the Invoice matching the Canva form, with seeded logos, Print, Download PDF, Void and the status line · Email with the Email pop-up, a real send with the PDF, success, failure and Send again · the How it works page behind a "?" on every page · the Ledger with stats, filters, overdue, Mark paid and its undo, void rows and the "Emailed [date]" label · read-only Customers · structured logging, /health and client error lines · mock seed data (four accounts, five products) · deployed to a public URL
Deferred to Handoff (Block 8)
  • Editing Products and Customers in the app: an admin UI the judge path doesn't need. Seed edits cover it for now.
  • The email template editor: one code file for now.
  • The full printed/emailed history line, and the "(revised)" / "Edited after sending" wording: a second send is just Send again for now.
  • Changing the cafe on an existing invoice: not built (decided in slice 7). Void and recreate covers it.
  • Real data seeding, the production PIN, and Eileen's own acceptance run: the project finish line. It happens in the same production deployment and database, after the video and the tagged submission, in the order in checklist.md > Block 8: Handoff.
Later
  • Stripe · multi-user · low-stock alerts · a bake list · logo upload
  • Overdue reminders (stub, added in 5-build; not built now): a daily Vercel Cron job finds unpaid, non-void, past-due invoices. Ask-first by default: Eileen gets one email listing them, with a single signed link that sends the reminders in one tap. The reminder reuses the invoice PDF with a short Net 30 body, and the Ledger row shows "Reminded [date]". Fully automatic per cafe is a later toggle.
  • Export the Ledger as Excel (.xlsx) (stub, added in 5-build; not built now): respects the cafe filter and a date range. Columns: invoice number, cafe, delivery date, due date, total, status, paid date, reference.
  • A separate demo deployment with mock data, only when a second customer wants a look. Until then there's one deployment, and Handoff makes it Eileen's.
  • Accounts receivable aging report (stub, added in 5-build; not built now): unpaid invoices grouped by how overdue (current, 1 to 30, 31 to 60, 61 to 90, over 90 days), per cafe and in total. Exportable as PDF or Excel, for Eileen's accountant or for following up on overdue bills.
Never
  • Ingredients or recipe costing · accounting or tax · model-generated prices, totals, dates, numbers or email text · inventory decrement · real data in the repo · hard delete · app-invented number suffixes

Build rules for spec & build

One file, one responsibility, and no god files. Every code file starts with a one-sentence Job: comment; if it needs a second sentence, the file gets split. Flag any code file over about 200 lines at each checkpoint.

Open questions