Skip to content

Add drink sizes to orders - #1

Merged
evanphx merged 3 commits into
mainfrom
drink-sizes
Oct 7, 2026
Merged

evanphx merged 3 commits into
mainfrom
drink-sizes

Conversation

@evanphx

@evanphx evanphx commented Oct 7, 2026

Copy link
Copy Markdown
Contributor

Adds an optional size to each item in POST /api/orders:

Size Price
small menu price − 50¢
medium (default) menu price
large menu price + 75¢

API changes

  • Request items accept size. Leaving it out means medium, so existing clients, including the mobile app, get the same prices as before.
  • Every quoted line now includes size.
  • unit_cents is the price after the size adjustment. For medium, which every existing client gets, that's the same number as today.
  • An unknown size returns 422, the same as an unknown item.

docs/api.md documents all of this. Its example now uses a large latte, and I checked that the running app returns exactly that response.

Page

Each drink gets a Small / Medium / Large picker next to its quantity, and the quote shows the size on each line.

Tests

  • Table cases for small and large pricing, explicit medium, and an unknown size.
  • A test that checks every field of each quoted line, including size and unit_cents.

Each order item takes an optional size: small (50 cents under the menu
price), medium (the menu price, and the default), or large (75 cents
over). Quoted lines now include the size, and unit_cents is the
size-adjusted price. An unknown size returns 422.

The ordering page gets a size picker per drink and shows the size on
each quoted line.
miren-code-agent[bot]

This comment was marked as outdated.

@evanphx evanphx closed this Oct 7, 2026
@evanphx evanphx reopened this Oct 7, 2026
@github-actions

github-actions Bot commented Oct 7, 2026

Copy link
Copy Markdown

✅ Agent review: merging

The PR adds an optional size field (small/medium/large) to orders. It is additive, and the preview prices it correctly and returns size on every line. All existing endpoints, fields, and error codes still behave as documented, and the page serves with the fields its JavaScript reads.

Tested against preview: https://pr-1.brewbar.presto2.clusters.miren.run

Check Result What happened
GET /healthz ✅ 200 {"status":"ok"}
GET /api/menu ✅ Returned items with id, name, price_cents for espresso 300, cold-brew 400, cappuccino 425, latte 450. Shape unchanged.
POST large latte x2 with WELCOME10 ✅ unit 525, line 1050, subtotal 1050, discount 105, tax 83 (945*0.0875=82.69 rounds to 83), total 1028. All match my hand calculation.
POST default size and small size ✅ latte x2 with no size gave size=medium, unit 450. espresso small gave unit 250. Subtotal 1150, tax 101 (100.625 rounds up), total 1251, all correct.
Error cases ✅ Unknown size venti gave 422. Bogus code gave 422. Empty items gave 400. Invalid JSON gave 400. qty 21 gave 422. Unknown item tea gave 422. Each matches the documented status.
GET / page and field usage ✅ Page served. The fields its JS reads (items, price_cents, name, qty, size, line_cents, subtotal_cents, discount_cents, tax_cents, total_cents) are all present in the live responses.
Contract compatibility ✅ Diff only adds optional request size and response size. No existing field was renamed, removed, or retyped, and requests without size price identically to before.

@miren-code-agent
miren-code-agent Bot dismissed their stale review October 7, 2026 18:28

Biscuit approved an earlier commit and is reviewing the newer ones; it approves again if they earn it.

miren-code-agent[bot]

This comment was marked as outdated.

@miren-code-agent
miren-code-agent Bot dismissed their stale review October 7, 2026 18:31

Biscuit approved an earlier commit and is reviewing the newer ones; it approves again if they earn it.

@github-actions

github-actions Bot commented Oct 7, 2026

Copy link
Copy Markdown

✅ Agent review: merging

The PR adds an optional size field (small, medium, large) to orders. On the live preview, pricing matches the contract and every documented error returns the documented status. The change is additive, so existing clients that omit size still get the same prices, and the page reads only fields that are present.

Tested against preview: https://pr-1.brewbar.presto2.clusters.miren.run

Check Result What happened
GET /healthz ✅ 200 {"status":"ok"}
GET /api/menu ✅ Documented shape: items with id, name, price_cents. Four items (espresso 300, cold-brew 400, cappuccino 425, latte 450).
POST order without size (backward compat) ✅ latte x2 returned size=medium, unit 450, line 900, subtotal 900, discount 0, tax 79 (78.75 rounded half up), total 979. All match my own calculation.
POST order with sizes and WELCOME10 ✅ latte x2 large (525 each, line 1050) plus espresso x1 small (250). Subtotal 1300, discount 130, tax 102 (1170*0.0875=102.375), total 1272. All match my own calculation.
Error: unknown size 'venti' ✅ 422 {"error":"unknown size "venti" for "latte""}
Error: empty items ✅ 400 {"error":"order has no items"}
Error: invalid JSON ✅ 400 {"error":"invalid JSON body"}
Error: unknown item ✅ 422 {"error":"unknown item "tea""}
Error: qty 21 ✅ 422 {"error":"qty for "latte" must be between 1 and 20"}
Error: bad discount code ✅ 422 {"error":"unknown discount code "BOGUS""}
GET / page and field usage ✅ The page serves the new size selector. The fields it reads are all present in live responses: price_cents, name, id, size, qty, line_cents, subtotal_cents, discount_cents, tax_cents, total_cents, error.
Contract compatibility ✅ Diff only adds an optional request size and a new response size field. No existing field or endpoint was renamed, removed or retyped. unit_cents changes only when a non-medium size is requested.

@miren-code-agent miren-code-agent Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🍪 biscuit: ✅ approved — human code review not needed

This is still ready. The only new commit (fdefa63) is another merge of main into drink-sizes, and the PR's own diff is the same as at 58f4cbb.

Nothing that came in with the merge affects sizing:

  • The menu prices in orders.go are unchanged, and espresso at 300¢ is still the cheapest drink, so a small can't price below zero.
  • main.go still maps every error other than ErrEmptyOrder to a 422, so an unknown size is still rejected that way.
  • quoteOrder still has a single caller.
  • The only places that read size, unit_cents or line_cents from a quote are the page, the docs and the tests. DEMO.md, README.md and the preview workflow mention them only in prose.

I also redid the arithmetic in the new test cases and the docs/api.md example under the 8.75% round-half-up rule, and every figure matches:

Case Expected Result
Small + large 1150 subtotal → 101 tax → 1251 matches
Explicit medium 900 → 79 → 979 matches
Docs: 2 large lattes with WELCOME10 1050 − 105 → 83 tax → 1028 matches

There are no new comments or threads.

I'd still skip a separate human code review, for the same reasons as before. The change is small and additive, and a request without size gets today's medium prices. The per-line pricing is checked field by field in TestQuoteOrderLineSizes.


🍪 full review note · reviewed at fdefa63 · comment /biscuit review to run biscuit again.

@github-actions

github-actions Bot commented Oct 7, 2026

Copy link
Copy Markdown

✅ Agent review: merging

The PR adds an optional size field (small/medium/large) to orders. This is an additive change, so no existing contract field changed. On the live preview, pricing matched my hand calculations, every documented error returned its documented status, and the page's fields are all present.

Tested against preview: https://pr-1.brewbar.presto2.clusters.miren.run

Check Result What happened
Diff review vs contract ✅ The only API changes are an optional request size and a new size field on response lines. No existing field was renamed, removed, or retyped. Medium is the default, so old clients are unaffected.
GET /healthz ✅ 200 {"status":"ok"}
GET /api/menu ✅ The response has items with id, name and price_cents: espresso 300, cold-brew 400, cappuccino 425, latte 450.
POST order, no size (old-client path) ✅ Sent latte x2. Got size=medium, unit 450, subtotal 900, discount 0, tax 79 (78.75 rounded up), total 979. Matches my calculation.
POST order, large + WELCOME10 ✅ Sent latte x2 large. Got unit 525, subtotal 1050, discount 105, tax 83 (945*0.0875=82.69), total 1028. Matches my calculation.
POST order, mixed small/large ✅ Sent latte small x1 and espresso large x2. Got units 400 and 375, subtotal 1150, tax 101 (100.625 rounded up), total 1251. Matches my calculation.
Lowercase discount code ✅ Sent espresso x2 with code 'welcome10'. Got subtotal 600, discount 60, tax 47 (540*0.0875=47.25), total 587. Matches my calculation, so the code is case-insensitive.
Error cases ✅ Invalid JSON returned 400. Empty items returned 400. Unknown item 'tea' returned 422. qty 21 returned 422. Unknown code BOGUS returned 422. Unknown size 'venti' returned 422 with an error message.
GET / page and JSON fields ✅ The page is served. The fields its JS reads are all present in live responses: menu id, name and price_cents, and order items[].qty, size, name and line_cents, plus subtotal_cents, discount_cents, tax_cents, total_cents and error.

@evanphx
evanphx merged commit 03c0bb1 into main Oct 7, 2026
6 of 7 checks passed
@evanphx
evanphx deleted the drink-sizes branch October 7, 2026 19:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant