Skip to content

Dated session swaps

A swap exchanges two tutor slots on specific dated sessions. It never changes course-allocation ownership. Tutors choose sessions, see suitability for both sides, request a trade and obtain the other tutor's consent before an administrator or a coordinator of either involved course approves it.

Session responsibility

Allocation.sessionIds lets an organiser select the weekly sessions a tutor covers from the course detail page. An empty stored list retains the existing behaviour of covering every course session. Explicit selections must belong to the allocation's course. Tutors on one course can trade different assigned sessions, or trade with tutors on another course.

SessionResponsibility identifies one slot by allocation, scheduled session and occurrence date. It stores the original tutor, current covering tutor, time snapshot and the workflow that changed responsibility. Approval writes two rows in a serializable transaction. Pending requests and tutor acceptance do not reserve or change those responsibilities. Competing decisions are rechecked after serialization retries.

Validation requires verified qualifying marks and coverage of the incoming session by the tutor's free-time windows. It checks conflicts on that date against both normal assignments and approved cover. Weekly workload starts from each allocation's weekly hours, at least its selected scheduled session durations, then subtracts outgoing dated responsibilities and adds incoming ones. Exchanges in different weeks are assessed separately.

Connected workflows

  • Calendars retain normal weekly sessions, mark transferred occurrences as cancelled and add incoming cover. The subscribed calendar feed excludes the exact transferred occurrences and adds individual cover events without changing later recurrences.
  • Tutor dashboards show incoming cover and this week's adjusted hours.
  • Timesheets can be opened for courses covered through a swap or volunteer claim. Session entries identify the original slot and actual working tutor. They can only be recorded after the session ends, by its responsible tutor; duplicate session entries are blocked. Preparation and marking remain separate entries.
  • A covering tutor can excuse themselves from the received session. Approval makes that same slot vacant and links the overflow post to it. A volunteer approval fills the slot once, checks session eligibility and does not create a recurring course allocation. Generic overflow posts retain their existing allocation workflow.
  • Session timetable changes and allocation retirement/session-selection changes are blocked while there is affected future approved cover. Session and account deletion preserve swap and cover history.

Review and reversal

Request, consent, cancellation, decline, staff review and reversal each produce immutable audit snapshots. Participants and authorised staff can view the history. Staff give a reason for rejection or reversal. The requested tutor is notified of the proposal, staff are notified when consent makes review necessary, and both tutors are notified of approval, rejection and reversal.

An approved swap can be reversed only before either session begins, provided its responsibility rows still belong to that swap, no hours or excusals have been recorded, and the original tutors remain eligible. The rows become inactive and normal ownership resumes. Earlier audit records are retained. A swap cannot be reversed after a participant has arranged excusal or volunteer cover for it.

Migration and compatibility

Apply toodle-api/prisma/migrations/20261008120000_dated_session_swaps/migration.sql through the project's normal database migration process, regenerate Prisma and release the API and frontend together. The migration adds nullable occurrence fields to existing swaps, session selections to allocations and the responsibility table. Existing course-swap history and existing allocations are preserved. Legacy pending whole-course requests must be cancelled/rejected and resubmitted with dates; they cannot be approved or automatically converted to a session exchange.

The implementation continues to use the project's weekly timetable and free-time model. There is no term-date or dated-availability-exception model; this change does not introduce one.

Verification

Unit and API tests cover dates, roles, marks, availability, clashes, different-week and unequal-duration hours, audit, notifications, changed sessions, rollback and reversal. UI tests cover suitability before submission, pending previews, consent, rejection reasons, reversal, history and incoming cover in tutor workflows.

The opt-in PostgreSQL suite requires a disposable local database named b5_test and schema dated_swap_verification, with the current Prisma schema installed. Set B5_TEST_DATABASE_URL to that local schema and run npm test -- tests/integration/swap.postgres.test.js in toodle-api. The suite truncates its dedicated schema's fixture tables, so its guard rejects other database names, schemas and remote hosts. It checks simultaneous and competing approvals, approval racing an excusal, actual calendar/feed output, timesheet ownership, volunteer coverage and restoration.

The new SQL migration was applied to an isolated schema containing the prior datamodel and produced no Prisma schema drift. Replaying the repository's entire historical migration chain on a fresh database separately exposed a pre-existing failure in 20260925120000_split_organiser_role_admin_lecturer: it assigns LECTURER before that value exists in the old Role enum. That older migration was not edited; fresh-database provisioning needs a separate repair.