Migrated from Notion E2E features — https://app.notion.com/p/3d31f8eaa3ad8116a2ede6e448c8cc64
E2E features¶
Callout Who should read: QA writing or reviewing create/pay / desk-move journeys. Callout As of 2026-09-24 IST —
reservation-browser-testsmain tip4a53eab(2026-09-08 IST). Specs on tip:admin-web-app-flow.spec.ts(Midway lifecycle),admin-buyout-flow.spec.ts(#140),admin-pay-later-create.spec.ts(#143),admin-slot-full.spec.ts(#141),venuplus-booking-flow.spec.ts/ smoke (#142 Datacap gated). Nightly schedule (playwright.yml) runs onlysuite=admin+business_admin— buyout / pay-later / slot-full / PWA are dispatch-only. BA Midway nightly often red (outlet “Select location”). Coverage map: TEST coverage recommendations (2026-09-24). How to use: status machine + golden fixtures first; assert DocTypes not UI toasts. Do not run S-883 /update_bookingunless asked. Magensa insidecreate_bookingis a dead branch for ATP/US goldens. Guest checkout does not useapply_booking_workflow_action(Desk only). Create/pay write passes need Anil approval — Datacap on DEV can still be production.
E2E features (from reservation-api-server + venuplus-app)¶
Read-only mapping. Canonical terms: Booking, Outlet, Customer, Business, Venue. SYS-005: new Outlet of an existing class = zero new tests. Daily = integrative, not cartesian.
Source of truth: reservation-api-server Frappe methods. Apps only call them.
Status machine (BZBooking.booking_status)¶
PWA Datacap guest checkout (current code in booking_transaction.py):
- Cart lock:
BZBookingAvailability.lock_status = Active(cart.add_to_cart/lock_slot, ~600s expiry) create_bookingwithout a payment block → Booking Pending; cart Active locks are Confirmed here (atomic_confirm_cart_locks)- External Datacap capture (venuplus
/booking/[outletId]/payment) — PAN never hits Frappe finalize_booking_after_payment→ Pending → Successful (amounts + confirmation). Does not insert payment rows; re-attaches Confirmed locks.
apply_booking_workflow_action is not on the PWA checkout path anymore. It remains for Desk / admin (Initiated, Holding, Wait, CheckedIn).
Create also supports idempotency_key and cart_id de-dupe.
App routes (venuplus-app)¶
/booking/[outletId]/ steps we treat as one party-package journey (class VP-FRAPPE-PACKAGE-VENUE-DATACAP-PWA):
| Step | Route | Backend |
|---|---|---|
| Catalog | /booking/{outletId}/plans-and-games (ATP; / ?outlet= redirects here) |
pwa.get_outlet_by_id, pwa.get_service_items, customer.get_service_item |
| Slot | same page (not /time-selection — that redirects back) |
pwa.get_schedules / pwa.get_schedule, availability.get_timeslots |
| Lock | Proceed → checkout | cart.add_to_cart (CART-… Active lock). Release: cart.remove_from_cart. Not availability.lock_resources |
| Review | /checkout |
customer.get_addons, pricing_engine.calculate_total |
| Create | checkout pay | booking_transaction.create_booking → Pending + Confirmed locks. No apply_booking_workflow_action |
| Pay | /payment → POST /api/payment/process |
Datacap only: payment.get_webtoken_config → NetEPay tokenize → payment.process_sale → record_external_payment_transaction → finalize_booking_after_payment |
| Confirm | same /payment page (paymentState=success) |
no extra method; email inside finalize. /manage is later email CTA |
Out of this class: game-card-*, United Skates /plans (pwa.get_plans_catalog_for_date), Magensa MagTek (process_mppg_hardware_emv on admin BookingDetail hardware pay).
Admin (admin-web-app) — schedule move¶
| Caller | Method | Lock behavior |
|---|---|---|
| BookingDetail / BookingEditDrawer / BookingRichEditor | bookingzone.api.admin_booking.patch_booking |
in-place Confirmed (locks_rescheduled) |
Public customer portal (customerAccount.reschedule, bookings.requestReschedule) + shims bookings.update / reschedule |
update_booking via shim-router |
#883 path. Not BookingDetail manager save. Map only, do not run |
| Some BookingPage writes | raw frappe.updateDoc("BZBooking") |
bypasses lock move (different defect) |
DocTypes to assert (never UI toast)¶
BZBooking.booking_status/ payment fieldsBZBookingAvailability.lock_status(Active during cart, Confirmed at create_booking, still Confirmed after Successful)BZPaymentTransaction(no PAN/CVV)BZResourceBlockonly when the class actually uses admin blocks (VP Arnot Mall: none)
DEV fixtures (catalog)¶
See ecosystem/ for the full dump. Locks out of this pass.
ATP class VP-FRAPPE-PACKAGE-VENUE-DATACAP-PWA
- Business: Test At The Pier bz-7c4f4061-5ad1-4d1b-a4a8-37cbfd1ced2d
- Outlet: Test At The Pier - Arnot Mall bz-4f8920e9-9644-4169-acb4-ba3193083ee7
- Golden package: The Midway Play bz-3702289d-3778-4d61-9181-28120d5ec1aa (buffer 30, 8–25 guests, $17.99)
- PWA: /booking/{outletId}/plans-and-games
United Skates Test Seaford (not the PWA URL Outlet)
- Business: Test United Skates bz-977016bb-39ae-48b3-a8b7-66a4a6f6827f
- Outlet: bz-192df4d4-cdc0-4789-ae61-4e4f8e98b74e
- Plan class golden: Super Pass bz-19d6fa9b-6b0a-4931-8ea9-2d4507cdc9af
- Package class golden: ULTIMATE BIRTHDAY PARTY PACKAGE bz-2c518af1-7e2d-49b0-b82f-de269142f2f6
- PWA customer link uses different Outlet bz-fbf53de2-8bae-4d9c-8599-882077fef716 (/plans)
IDOR caveat¶
booking.get_booking / customer.get_booking have no in-method owner check. Guest cannot call them. Cross-Customer IDOR depends on Frappe has_permission on BZBooking (UNKNOWN until those hooks are read).
Payment RPC caveat¶
payment.record_external_payment_transaction is defined twice in payment.py; the later definition wins. E2E must use the live signature on the deploy SHA.
Daily integrative scenarios (VP class first)¶
Run against DEV. Assert DocTypes. One golden path per class. No extra tests for another ATP mall.
Do not run S-883 until explicitly asked. Do not charge real cards (Datacap cert / test amount only if a write pass is approved).
Legend: R = read-only (safe now). W = mutates DEV (needs your go-ahead).
S-GP-VP — Golden party book (P0)¶
Class: VP-FRAPPE-PACKAGE-VENUE-DATACAP-PWA
Apps: venuplus-app → reservation-api-server
Mode: W (creates a Booking)
Steps:
1. Catalog Midway Play on Arnot Mall
2. get_timeslots — pick a slot with availableCount > 0
3. Acquire Active lock (cart.add_to_cart / lock_slot, ~600s)
4. create_booking without payment block → BZBooking.booking_status = Pending; lock Confirmed (not still Active)
5. Datacap sandbox capture (no PAN in Frappe) + record_external_payment_transaction (audit only)
6. finalize_booking_after_payment → Successful; lock still Confirmed; payment row exists
Pass: Successful Booking + ≥1 Confirmed BZBookingAvailability matching hold window (guest ± 30 min buffer). Fail if toast-only. Fail if create leaves lock Active.
S-NOTIMESLOT — No capacity¶
Mode: R
get_timeslots on a date with no schedule or availableCount 0.
Pass: HTTP 200 with timeSlots: [] (not 400) when no schedule / online booking off / all full. Do not create_booking.
S-LOCK-ABANDON — Cart lock dies¶
Mode: W (lock only, no pay)
Acquire Active lock, abandon checkout (or wait expiry).
Pass: lock Released/Expired; a second cart can lock the same Venue resource.
S-IDOR — Guest cannot read someone else's Booking¶
Mode: R
Using Successful bz-7f9efdd6-6ec9-4f16-be83-871a01d3a3c2 as victim.
Call get_booking as a different Customer/Guest.
Pass: 403/404, not the DocType payload. Guest must not be able to call get_booking. Cross-Customer IDOR is UNKNOWN without BZBooking permission hooks.
S-ADMIN-PATCH — In-place reschedule (safe path)¶
Mode: W (needs approval)
patch_booking date/time on a Successful Booking with Confirmed lock.
Pass: same availability name; still Confirmed; times moved; no Released row.
S-883 — Occupied-slot update_booking (VP only)¶
Mode: W forbidden until you say so
#883_lock_path=yes on this class.
Admin update_booking onto a slot covered by another Confirmed lock (e.g. 7i0ssj8s59 2026-09-15 10:00–13:00).
Assert BZBookingAvailability.lock_status, not toast.
Current develop: risk of Released with no replacement. PR 885: 400, original Confirmed intact.
S-WORKFLOW — Staff only (not daily PWA)¶
Mode: W (optional nightly)
apply_booking_workflow_action from Desk. Not part of guest checkout.
Explicitly not daily¶
- Another ATP Outlet of this class
- Game-card kiosk (
create_gamecard_only_booking) - FiveStar / United Skates until those Outlet IDs are filled
- 120 styling viewport matrix
- Cartesian pay × day × guest count