feat(bookings): session-to-session move for class bookings

Class customers can move a booking to another future session of the
same template from the manage page. Full target sessions place the
booking on the waitlist; freeing a confirmed seat drains the old
session's queue via the existing promotion path. Reminders re-arm
for the new time.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
Tomas DvorakandDevin committed 2026-09-18 15:42:04 +02:00
1 parent 6c3b2bf6bf
commit e5cb9bd543
10 files changed
+927 -10

No files matched your search

+116
View File
@@ -396,6 +396,97 @@ paths:
description: Invalid manage token
"409":
description: New slot conflicts or booking already cancelled
/v1/public/bookings/{reference}/sessions:
get:
tags: [Public Booking]
operationId: listSessionMoveTargets
summary: Upcoming sessions a class booking can move to
description: >-
Class bookings cannot reschedule to arbitrary windows — they move
between sessions of the same template. Returns upcoming same-template
sessions with live capacity info.
parameters:
- in: path
name: reference
required: true
schema:
type: string
- in: query
name: token
required: true
schema:
type: string
responses:
"200":
description: Move targets with capacity info
content:
application/json:
schema:
type: object
properties:
sessions:
type: array
items:
$ref: "#/components/schemas/SessionMoveTarget"
"400":
description: Not a live class booking
"403":
description: Invalid manage token
"404":
description: Booking not found
"409":
description: Booking already cancelled
/v1/public/bookings/{reference}/move:
post:
tags: [Public Booking]
operationId: moveBookingToSession
summary: Move a class booking to another session of the same template
description: >-
Retargets the booking to the target session. Confirmed bookings free
their old seat (draining its waitlist); waitlisted bookings release
their queue position. The booking is confirmed when the target has a
free seat, otherwise it joins the target's waitlist.
parameters:
- in: path
name: reference
required: true
schema:
type: string
- in: query
name: token
required: true
schema:
type: string
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [classSessionId]
properties:
classSessionId:
type: string
format: uuid
responses:
"200":
description: Booking moved; status is the resulting booking state
content:
application/json:
schema:
type: object
properties:
status:
type: string
enum: [confirmed, waitlisted]
"400":
description: Not a live class booking, or target is not an upcoming same-template session
"403":
description: Invalid manage token
"404":
description: Booking not found
"409":
description: Booking already cancelled
/v1/public/bookings/{reference}/cancel:
post:
tags: [Public Booking]
@@ -1902,6 +1993,31 @@ components:
waitlistPosition:
type: integer
description: 1-based queue spot for waitlisted class bookings; absent otherwise
SessionMoveTarget:
type: object
properties:
sessionId:
type: string
format: uuid
title:
type: string
startsAt:
type: string
format: date-time
endsAt:
type: string
format: date-time
location:
type: string
capacity:
type: integer
confirmedCount:
type: integer
waitlistCount:
type: integer
remaining:
type: integer
description: Free confirmed seats; 0 or negative means the move lands on the waitlist
CalendarFeedEntry:
type: object
properties: