Skip to content

Conversation

@alishaz-polymath
Copy link
Member

@alishaz-polymath alishaz-polymath commented Oct 31, 2025

What does this PR do?

This PR introduces full support for booking reassignment within Managed Events, enabling both manual and auto reassignment of bookings created under a parent + child event-type model.
Previously, reassignment logic existed only for Round Robin event types (where bookings reference a single event type with multiple hosts). Managed Events differ in that each booking belongs to a child event type (one per user), so reassignment requires cancelling the old booking and creating a new one with the new host.

Adds two new routes: managedEventManualReassign (explicit user choice) and managedEventReassign (auto-select available user)

Core logic ensures:

  • validation that booking is on a managed event child
  • lookup of target child event type for new user (same parent)
  • availability check for new user at booking time
  • transactional update: original booking cancelled + new booking created (with both userId and eventTypeId updated)
  • audit metadata recorded (previousUserId, previousEventTypeId, reassignedAt, reassignReason, reassignById)
  • reuse of existing calendar/event deletion & creation logic and workflow-reminder scheduling
  • UI: updates to ReassignDialog.tsx detect managed context and show child-user list, availability indicators
  • Integration Tests: covering manual and auto flows, success & error paths, data integrity (time, attendees), audit records, cancellation of original booking
  • Performance: design reuses existing Round-Robin querying patterns (indexed by parent/child) so no new scale risks
  • No feature flag required (logic extends existing pattern)

Here’s a polished, reviewer-friendly PR description for Cal.com’s PR #24809 — “feat: managed event reassignment”. It’s structured so that anyone reading it (even without context) can quickly understand what changed, why it matters, and how to test / validate it. Feel free to copy-paste and adapt as needed (or paste into a GitHub PR template).


Core changes & functionality

  • New backend endpoints

    • managedEventManualReassign — explicitly reassign a booking to a chosen user.
    • managedEventReassign — automatically pick an available user (fallback / auto-assign).
    • getManagedEventUsersToReassign — lists eligible users (with availability), supports pagination.
  • Transactional reassignment logic

    • Validates booking is on a managed child event type.
    • Finds target child event type under the same parent for the new user.
    • Checks user availability at booking time.
    • Cancels the original booking + creates a new booking in a single database transaction (thus atomic).
    • Preserves attendees, responses, and payment data.
    • Preserves iCalUID, increments iCalSequence, sets new idempotencyKey.
    • Adds audit metadata: previousUserId, previousEventTypeId, reassignedAt, reason, reassignedById.
    • Reuses existing calendar/event-delete & creation logic; workflow reminders are updated.
    • Sends managed-event notification emails (original host + new host + attendees) with context-aware copy.
  • Frontend / UI updates

    • A new ReassignDialog.tsx for managed events that shows: paginated eligible users, availability status, and manual / auto options.
    • UI fallbacks to original Round-Robin behavior when event-type is not managed.
  • Full test coverage

    • Integration tests covering: manual & auto reassignment, success and error paths, data integrity (time, attendees), audit records, original booking cancellation.
    • Edge cases: no available users — returns clear, actionable error.
  • Performance / no new scale risk

    • Uses existing Round-Robin query patterns (fuzzy search for LIKE patterns), reuses indices — no additional load or complexity to scale logic.

🚧 Notes & Follow-ups

  • Post-merge: Consider adding a BOOKING_REASSIGNED webhook trigger to allow external systems to respond to reassignment (out of scope for this PR).
  • Improved DI implementation for managedEventManualReassignment and managedEventReassignment functions

📝 Checklist (completed / for reviewers)

  • Code implementation complete
  • Backend + API route handlers added
  • Frontend UI changes (Reassign dialog)
  • Integration tests covering main & edge cases
  • Data integrity & audit metadata handling
  • Backwards compatibility (non-managed events untouched)
  • Performance considered, no extra indices needed

👇 TL;DR

This PR adds fully-functional managed-event reassignment — manual or automatic — without breaking existing booking flows, preserving data integrity, availability constraints, audit history, and notifications.


Visual Demo (For contributors especially)

A visual demonstration is strongly recommended, for both the original and new change (video / image - any one).

Video Demo (if applicable):

Loom

Image Demo (if applicable):

  • Add side-by-side screenshots of the original and updated change.
  • Highlight any significant change(s).

Mandatory Tasks (DO NOT REMOVE)

  • I have self-reviewed the code (A decent size PR without self-review might be rejected).
  • N/A - I have updated the developer docs in /docs if this PR makes changes that would require a documentation change. If N/A, write N/A here and check the checkbox.
  • I confirm automated tests are in place that prove my fix is effective or that my feature works.

How should this be tested?

  1. Create a parent event type with schedulingType = 'MANAGED' and child event types (one per user).
  2. Book a slot using one child event type.
  3. Invoke managedEventManualReassign to transfer the booking to another child/user. Confirm:
    • original booking status = CANCELLED
    • new booking created with correct userId, eventTypeId, startTime, endTime
    • attendees list copied, workflow reminders updated, calendar event recreated
    • audit record present in assignmentReason or booking.metadata.reassignment
  4. Invoke managedEventReassign (auto) under availability constraints. Confirm:
    • Booking reassigned to a free user
    • If no user free → endpoint returns clear error “User unavailable for reassignment”
  5. Verify that denormalized tables (e.g., BookingDenormalized) reflect the new booking correctly.
  6. Check that external integrations (webhooks) see a cancelled + created booking sequence, with appropriate metadata.
  7. UI: In Admin → Reassign dialog for managed event bookings:
    • shows list of eligible users with availability indicators
    • prompts for reason (when manual)
    • shows confirmation message on success
    • handles error when no eligible user found

Checklist


Summary by cubic

Adds managed event reassignment so bookings on managed event types can be reassigned automatically to the best available user or manually to a specific teammate. Updates the UI and APIs while keeping round-robin behavior unchanged for non-managed events.

  • New Features

    • UI: Reassign action and dialog detect managed events, add “Auto” option, update copy, and show a paginated list of eligible users with availability. Falls back to existing round-robin for non-managed events.
    • API: New endpoints
      • viewer.teams.managedEvents.managedEventReassign (auto)
      • viewer.teams.managedEvents.managedEventManualReassign (manual)
      • viewer.teams.managedEvents.getManagedEventUsersToReassign (infinite list + availability)
    • Engine: Validates booking (not cancelled/ended/recurring), finds target child event type, checks availability, and reassigns by:
      • Cancelling the original booking and creating a new one on the target child event type in a single transaction.
      • Preserving attendees, responses, and payment; preserves iCal UID, increments iCal sequence, and sets an idempotency key.
      • Updating metadata with reassignment details and recording assignment reasons (auto/manual).
      • Deleting old calendar events, creating new ones for the new host, and cancelling/rescheduling workflow reminders.
      • Sending managed-event emails to the new and original host, and updates to attendees, with contextual reassignment copy (generic subtitle for managed events, RR subtitle unchanged).
      • Returning clear errors (e.g., no available users, permission checks).
  • Bug Fixes

    • Permissions: Booking access checks now consider the parent team for managed child event types.
    • Success page: Fixed location text for cancelled/rejected bookings.
    • API v1: Booking GET now returns complex metadata objects to expose reassignment details.

Written for commit 46f5302. Summary will update automatically on new commits.

@keithwillcode keithwillcode added core area: core, team members only enterprise area: enterprise, audit log, organisation, SAML, SSO labels Oct 31, 2025
@vercel
Copy link

vercel bot commented Oct 31, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
cal-companion Error Error Nov 28, 2025 7:08pm
2 Skipped Deployments
Project Deployment Preview Comments Updated (UTC)
cal Ignored Ignored Nov 28, 2025 7:08pm
cal-eu Ignored Ignored Nov 28, 2025 7:08pm

cubic-dev-ai[bot]

This comment was marked as resolved.

cubic-dev-ai[bot]

This comment was marked as resolved.

cubic-dev-ai[bot]

This comment was marked as outdated.

cubic-dev-ai[bot]

This comment was marked as outdated.

cubic-dev-ai[bot]

This comment was marked as outdated.

cubic-dev-ai[bot]

This comment was marked as outdated.

cubic-dev-ai[bot]

This comment was marked as resolved.

@github-actions github-actions bot added the Stale label Nov 26, 2025
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 15 files (reviewed changes from recent commits).

Prompt for AI agents (all 1 issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="packages/features/ee/managed-event-types/reassignment/utils/buildNewBookingPlan.ts">

<violation number="1" location="packages/features/ee/managed-event-types/reassignment/utils/buildNewBookingPlan.ts:26">
JSDoc claims this is a &quot;pure function&quot; but it uses `Date.now()` on line 53, making it non-deterministic. Consider updating the comment to remove the &quot;pure function&quot; claim, or document that the UID generation is intentionally unique per call.</violation>
</file>

Reply to cubic to teach it or ask questions. Re-run a review with @cubic-dev-ai review this PR

/**
* Builds the new booking plan for a reassigned booking
*
* This is a pure function that constructs the booking data structure needed
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Nov 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

JSDoc claims this is a "pure function" but it uses Date.now() on line 53, making it non-deterministic. Consider updating the comment to remove the "pure function" claim, or document that the UID generation is intentionally unique per call.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/features/ee/managed-event-types/reassignment/utils/buildNewBookingPlan.ts, line 26:

<comment>JSDoc claims this is a &quot;pure function&quot; but it uses `Date.now()` on line 53, making it non-deterministic. Consider updating the comment to remove the &quot;pure function&quot; claim, or document that the UID generation is intentionally unique per call.</comment>

<file context>
@@ -0,0 +1,103 @@
+/**
+ * Builds the new booking plan for a reassigned booking
+ * 
+ * This is a pure function that constructs the booking data structure needed
+ * for the reassignment transaction. It generates a new UID, computes the booking
+ * title, and assembles all fields from the original booking.
</file context>

✅ Addressed in 0d46dab

cubic-dev-ai[bot]

This comment was marked as resolved.


export const OrganizerReassignedEmail = (props: React.ComponentProps<typeof OrganizerScheduledEmail>) => {
const t = props.teamMember?.language.translate || props.calEvent.organizer.language.translate;
const isRoundRobin = props.calEvent.schedulingType === SchedulingType.ROUND_ROBIN;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is still not working for me

@github-actions github-actions bot marked this pull request as draft November 27, 2025 11:59
cubic-dev-ai[bot]

This comment was marked as resolved.

Copy link
Contributor

@ThyMinimalDev ThyMinimalDev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@alishaz-polymath alishaz-polymath merged commit 0103321 into main Dec 1, 2025
88 of 93 checks passed
@alishaz-polymath alishaz-polymath deleted the feat/managed-event-reassignment branch December 1, 2025 10:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

core area: core, team members only enterprise area: enterprise, audit log, organisation, SAML, SSO event-types area: event types, event-types ✨ feature New feature or request High priority Created by Linear-GitHub Sync ready-for-e2e Requested size/XXL Stale teams area: teams, round robin, collective, managed event-types

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Reassign managed event types

7 participants