TC-PLAT-001: Platform Event Check-in (Super-Admin)

🆕 New on stage — 2026-06-19 → 2026-07-01. First detailed TC for the Platform Event Check-in feature (Jun 19 release #374–#399/#402, Jun 25 release #427/#428 parity). Supersedes the "use the release page's checklist" placeholder note that used to sit here — see the Platform section banner.

Module: Platform Ops — Super-Admin Event Check-in (handlers/platformEventCheckinHandlers.ts, attendanceCsvReconcileHandlers.ts, attendanceReconcileHandlers.ts)

Primary Test User: superadmin@zeniark.com / SuperAdmin123! (Super Admin — admin portal)

Priority: P1 (cross-tenant security surface — isolation + audit are the gate)

Objectives

  1. Open a tenant's event Attendees page from the super-admin portal and confirm it lists registrants with attendance status and type (member/guest) but never a dollar amount, invoice number, or other financial figure.
  2. Mark / undo / bulk-mark check-ins and confirm each write lands both an EventRegistration status change AND a PlatformAuditLog row attributed to the operator.
  3. Use QR scan-to-check-in (checkin-by-code) and confirm it checks the attendee in and writes the same audit trail, tagged via: "qr".
  4. Run the CSV attendance reconcile flow (preview → commit → finalize) and confirm finalize converts every remaining CONFIRMED (unscanned) registration to NO_SHOW.
  5. Cross-tenant isolation: a check-in performed against tenant A never touches tenant B's identical event, verified in tenant B's own org portal (not just re-reading the admin-portal UI).
  6. Access gate: confirm the route-level guard (requirePlatformSupport) returns 403 Forbidden, never a forced logout, for an operator lacking SUPER_ADMIN/PLATFORM_SUPPORT.
Correction vs. the section table (PLAT-CHK-01). The Attendees page is not billing-field-free — it DOES render an invoice status pill (Paid / Unpaid / No invoice) per EventAttendeesPage.tsx. What is redacted is any amount — no amountCents, invoice number, or currency figure is rendered or returned differently for a platform operator vs. what the backend report payload contains generally. See CM-01 below for the exact assertion.

Scenario summary

IDScenarioKey data assertion
CM-01Attendees page loads + field redactionGET /api/platform/tenants/:slug/events/:eventId/attendance200; each attendee row has status, registrationType, invoice status (if any) — but the rendered table shows NO dollar amount / invoice number cell.
CM-02Mark / undo single check-inMark → registration status=CHECKED_IN, checkedInAt set; a new PlatformAuditLog row with action="EVENT_CHECKIN", adminId=operator. Undo → reverts to CONFIRMED, checkedInAt=null, audit row action="EVENT_CHECKIN_UNDO".
CM-03Bulk-mark check-inPOST …/attendance/bulk-mark → response {marked, skipped, results}; already-checked-in rows land in skipped with reason ALREADY_CHECKED_IN, not an error; one audit row per bulk call listing all marked ids.
CM-04QR scan-to-check-inPOST …/attendance/checkin-by-code with a valid displayTicketCode/checkInCode200, checkInStatus:"checked_in"; audit row metadata.via="qr". Wrong-event code → 400 WRONG_EVENT with correctEvent info, not a silent check-in.
CM-05CSV reconcile + finalizePreview → uploadToken + row-match breakdown; commit consumes the token and marks matched rows checked-in; finalize (POST …/reconcile/finalize) sets every remaining CONFIRMED registration to NO_SHOW and returns {noShowCount, reconciledAt}.
CM-06Cross-tenant isolationCheck in an attendee for tenant A → tenant B's identical event, re-checked via tenant B's own org portal, shows zero change (registration count, checked-in count, and the specific registration's status all untouched).
CM-07Access-denied is 403, not logoutA request lacking SUPER_ADMIN/PLATFORM_SUPPORT in its platformRoles claim → 403 {"error":{"code":"FORBIDDEN"}} from requirePlatformRole (rbac.ts); the operator's own valid session is untouched (no token revocation, no forced re-login for other tabs/requests).

➡️ Full click-by-click steps: TC-PLAT-001 (Detailed).