← All tests

Outdoor auth fix + cafe partial-refund aggregator PROD

Two unrelated unblocked-list items shipped as one Gate 2 bundle on 2026-05-11. Outdoor local auth was broken on docker since R3.6δ.3 added refresh_tokens.tenant_user_id NOT NULL but RefreshTokenDAO.create never inserted it — push-to-prod-then-screenshot was the workaround. Cafe's session-move aggregator was preventively miscounting same-session partial refunds (partialRefundOrder doesn't adjust cafe.orders.total_usd or write to cafe.order_payments; the refund lives only on cafe.order_refunds) — would have overstated Sales credit by the refund amount the moment bypass_odoo_pos=true on R11.5 cutover. Both verified end-to-end locally; cafe additionally verified via read-only diagnostic probe against Supabase prod.

2 commits + 51/51 regression green.

Verification — outdoor auth fix (local docker)

POST /auth/login owner@demo.com (X-Tenant-Code: demo)
  → 200 with accessToken + refreshToken
  → refresh_tokens row 4869 carries tenant_user_id = c975561b-aa50-4556-bd85-09c6e9d3ebf7 ✓

POST /auth/refresh (with issued refreshToken)
  → 200 with new pair
  → old row 4869 revoked, new row 4870 carries tenant_user_id ✓
  → rotation works correctly

Verification — cafe aggregator fix (local fixture)

scripts/test-same-session-partial-refund-local.ts
  baseline aggregate on lumiere session 50cc3010
  → inject $20 paid order + $20 cash payment + $5 partial refund
  → re-aggregate, diff against baseline

  gross delta:        +15.00  (expect +15.00) ✓
  cash bucket delta:  +15.00  (expect +15.00) ✓
  sspr.count delta:   +1      (expect +1)     ✓
  sspr.total delta:   +5.00   (expect +5.00)  ✓

PASS — same-session partial-refund aggregator deltas all correct.

Verification — cafe aggregator probe (Supabase prod, read-only)

TenantSessionClosedRefund rowsRaw $Aggregator picked up
get-coffeeec65b1b1…2026-05-10T14:051$0.000 / $0.00
get-coffeec6b695da…2026-05-10T13:391$0.000 / $0.00
get-coffee7a56036e…2026-05-09T15:081$0.000 / $0.00
get-coffeef15affc8…2026-05-09T14:491$0.000 / $0.00
get-coffeed7e236ae…2026-05-09T13:571$0.000 / $0.00

All 5 hits are R10 modifier-line voids — e.g. POS07-1004: $2.50 bread order with a free "50% Sugar" modifier (qty=2, subtotal=$0). Cashier voided the modifier → refund_total=$0, order stays partial_refund because the bread line is still unrefunded. Aggregator correctly drops them via the amount <= 0 guard. Zero $ would have been mishandled on cutover. Useful sample for the accountant review: expect state='partial_refund' rows with refund.total_usd = $0 in the data; that's the normal modifier-void pattern, not a reconciliation issue.

Regression sweep — sequential per R8.2 single-session-per-user rule

SuiteResultTenantNotes
test-phase1-prod.mjs11/11get-coffee10/11 first run (SSO landing race) → 11/11 solo retry
test-phase2-sso-outdoor-prod.mjs6/6get-coffeeOutdoor SSO bridge
test-phase2-cafe-multishop-prod.mjs6/6demoParallel-safe (distinct tenant)
test-m1-prod.mjs10/10get-coffeeShop scoping
test-r7-prod.mjs14/14get-coffee + lumiereDashboard + manager-live
test-r8-prod.mjs4/4get-coffeeAuth/security trio

51/51 total prod checks — no regressions from this push.

What's preventively de-risked. Once the R11.5 cutover flips bypass_odoo_pos=true on get-coffee, the session-move aggregator runs for every session close. Without this fix, a cashier ringing a paid order and refunding a line during the same shift would have caused the resulting account.move to credit Sales for the full original gross + debit AR/PoS for the full original cash — silently overstating both by the refund amount. With this fix, the move correctly nets to the post-refund cash actually in the drawer. Pre-cutover the bug was latent (no tenant in bypass mode); post-cutover it would have been first-day-visible.