Skip to content

fix(slots): include guest availability when rescheduling round-robin bookings#28806

Open
dhelland wants to merge 2 commits intocalcom:mainfrom
dhelland:baz/issue-16378-guest-reschedule-availability
Open

fix(slots): include guest availability when rescheduling round-robin bookings#28806
dhelland wants to merge 2 commits intocalcom:mainfrom
dhelland:baz/issue-16378-guest-reschedule-availability

Conversation

@dhelland
Copy link
Copy Markdown

@dhelland dhelland commented Apr 9, 2026

Fixes #16378

This updates the reschedule availability flow so that when a host reschedules a booking, the attendee's availability is also considered if the attendee is a Cal.com user.

Instead of checking only existing guest bookings, this adds the guest into the normal availability pipeline as a fixed participant. That means their calendar conflicts, working hours, OOO, and booking limits are all respected during slot calculation.

It also applies through the round-robin fallback paths, so guest availability is not dropped when fallback host selection is used.

Tests added:

  • _getRescheduledGuestUser coverage for lookup and guard cases
  • service-level test proving the guest is included in the round-robin slot calculation flow and fallback paths

/claim #16378

dhelland added 2 commits April 8, 2026 21:37
…bookings (calcom#16378)

When a guest (attendee who is also a Cal.com user) reschedules a
round-robin booking, their availability was not being checked, which
could lead to double-bookings.

Changes:
- Add UserRepository.findAvailabilityUserByEmail() to look up a Cal.com
  user by email including their credentials and selected calendars
- Add AvailableSlotsService._getRescheduledGuestUser() which:
    - Returns null for collective events (all attendees must attend, so
      guest availability is irrelevant for slot selection)
    - Finds the non-organizer attendee from the original booking
    - Looks up that attendee as a Cal.com user
    - Returns null if they are not a Cal.com user or their account is locked
- Include the rescheduled guest as a fixed host in slot availability
  calculations (both the main two-week window and the fallback RR path)
- Add unit tests covering all edge cases of _getRescheduledGuestUser
@dhelland dhelland requested a review from a team as a code owner April 9, 2026 05:31
@CLAassistant
Copy link
Copy Markdown

CLAassistant commented Apr 9, 2026

CLA assistant check
All committers have signed the CLA.

@github-actions github-actions bot added $200 bookings area: bookings, availability, timezones, double booking Medium priority Created by Linear-GitHub Sync ✨ feature New feature or request 💎 Bounty A bounty on Algora.io 🧹 Improvements Improvements to existing features. Mostly UX/UI labels Apr 9, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 9, 2026

📝 Walkthrough

Walkthrough

The changes implement rescheduled guest user inclusion in slot availability calculations. A new repository method queries users by email with availability data. Utility logic detects rescheduled guest requirements and retrieves the guest's availability information, excluding organizer attendees. The guest is conditionally injected into host calculations for non-collective scheduling types. Test coverage validates the guest lookup method's behavior across various scenarios including missing reschedule IDs, collective scheduling, locked users, and absent attendees, and confirms guest integration into slot calculations during primary and fallback path executions.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: adding guest availability consideration to the rescheduling flow for round-robin bookings.
Description check ✅ Passed The description is directly related to the changeset, explaining the purpose (guest availability in reschedule flow), implementation approach (fixed participant in pipeline), scope (round-robin fallback paths), and tests added.
Linked Issues check ✅ Passed The PR fully implements the objectives from issue #16378: looking up the guest's email, determining Cal.com user status, retrieving their availability, and enforcing it during reschedule slot calculation.
Out of Scope Changes check ✅ Passed All changes align with the linked issue scope: new UserRepository method for lookup, new AvailableSlotsService method for guest retrieval, integration into slot calculation, and comprehensive test coverage for the feature.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
packages/trpc/server/routers/viewer/slots/_getAvailableSlots.rescheduleGuest.test.ts (1)

65-121: Avoid as any in this test harness.

The repeated (service as any) casts throw away the type coverage this test should give us. Please cast once to a narrow test-only shape or use typed bracket access for the private members you need to stub/call. As per coding guidelines, "Never use as any - use proper type-safe solutions instead."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/trpc/server/routers/viewer/slots/_getAvailableSlots.rescheduleGuest.test.ts`
around lines 65 - 121, The test currently uses repeated (service as any) casts
which lose type safety; create a single narrow test-only typed reference (e.g.,
const svc = service as unknown as Partial<AvailableSlotsService> & {
getRegularOrDynamicEventType(...): Promise<any>;
resolveOrganizationIdForBlocking(...): Promise<any>;
getRescheduledGuestUser(...): Promise<any>;
checkRestrictionScheduleEnabled(...): Promise<any>;
_getReservedSlotsAndCleanupExpired(...): Promise<any>;
calculateHostsAndAvailabilities(...): Promise<any>; _getAvailableSlots(...):
Promise<any>; }) and use svc to assign vi.fn() stubs and call
_getAvailableSlots, or alternatively use typed bracket access
(service['calculateHostsAndAvailabilities'] = ...) for each private member;
replace every (service as any) occurrence with that single typed reference so
the test keeps static typing while still stubbing getRegularOrDynamicEventType,
resolveOrganizationIdForBlocking, getRescheduledGuestUser,
checkRestrictionScheduleEnabled, _getReservedSlotsAndCleanupExpired,
calculateHostsAndAvailabilities and calling _getAvailableSlots.
packages/trpc/server/routers/viewer/slots/_getRescheduledGuestUser.test.ts (1)

33-124: Please remove the as any calls here as well.

These casts make the test less useful by opting out of type checking around the exact helper contract you're validating. A small test-only interface for _getRescheduledGuestUser would keep the setup explicit without dropping to any. As per coding guidelines, "Never use as any - use proper type-safe solutions instead."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/trpc/server/routers/viewer/slots/_getRescheduledGuestUser.test.ts`
around lines 33 - 124, Remove the unsafe "as any" casts around service when
calling _getRescheduledGuestUser; instead define a small test-only
interface/type that declares _getRescheduledGuestUser's exact signature (e.g.,
TestService { _getRescheduledGuestUser(args: { rescheduleUid: string | null;
organizerEmails: string[]; schedulingType: SchedulingType }): Promise<User |
null> }) and cast service to that TestService type for the calls in these tests,
then replace every `(service as any)._getRescheduledGuestUser` use with
`(service as TestService)._getRescheduledGuestUser`; this preserves type
checking while keeping the test explicit about the helper contract.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/trpc/server/routers/viewer/slots/util.ts`:
- Around line 1299-1306: The organizerEmails array is currently built only from
allHosts so hosts that exist solely in eligibleFallbackRRHosts are omitted,
causing getRescheduledGuestUser (this.getRescheduledGuestUser) to misidentify
attendees; update the organizerEmails construction to include emails from every
possible organizer host by combining allHosts and eligibleFallbackRRHosts (and
any other host collections used earlier), then map to
host.user.email.toLowerCase(), filter falsy values, and dedupe with Set before
passing organizerEmails into this.getRescheduledGuestUser({ rescheduleUid:
input.rescheduleUid, organizerEmails, schedulingType: eventType.schedulingType
}).

---

Nitpick comments:
In
`@packages/trpc/server/routers/viewer/slots/_getAvailableSlots.rescheduleGuest.test.ts`:
- Around line 65-121: The test currently uses repeated (service as any) casts
which lose type safety; create a single narrow test-only typed reference (e.g.,
const svc = service as unknown as Partial<AvailableSlotsService> & {
getRegularOrDynamicEventType(...): Promise<any>;
resolveOrganizationIdForBlocking(...): Promise<any>;
getRescheduledGuestUser(...): Promise<any>;
checkRestrictionScheduleEnabled(...): Promise<any>;
_getReservedSlotsAndCleanupExpired(...): Promise<any>;
calculateHostsAndAvailabilities(...): Promise<any>; _getAvailableSlots(...):
Promise<any>; }) and use svc to assign vi.fn() stubs and call
_getAvailableSlots, or alternatively use typed bracket access
(service['calculateHostsAndAvailabilities'] = ...) for each private member;
replace every (service as any) occurrence with that single typed reference so
the test keeps static typing while still stubbing getRegularOrDynamicEventType,
resolveOrganizationIdForBlocking, getRescheduledGuestUser,
checkRestrictionScheduleEnabled, _getReservedSlotsAndCleanupExpired,
calculateHostsAndAvailabilities and calling _getAvailableSlots.

In `@packages/trpc/server/routers/viewer/slots/_getRescheduledGuestUser.test.ts`:
- Around line 33-124: Remove the unsafe "as any" casts around service when
calling _getRescheduledGuestUser; instead define a small test-only
interface/type that declares _getRescheduledGuestUser's exact signature (e.g.,
TestService { _getRescheduledGuestUser(args: { rescheduleUid: string | null;
organizerEmails: string[]; schedulingType: SchedulingType }): Promise<User |
null> }) and cast service to that TestService type for the calls in these tests,
then replace every `(service as any)._getRescheduledGuestUser` use with
`(service as TestService)._getRescheduledGuestUser`; this preserves type
checking while keeping the test explicit about the helper contract.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 57385b23-3821-41da-a639-e8d4749ffb04

📥 Commits

Reviewing files that changed from the base of the PR and between f3e07c5 and 02a2ccc.

📒 Files selected for processing (4)
  • packages/features/users/repositories/UserRepository.ts
  • packages/trpc/server/routers/viewer/slots/_getAvailableSlots.rescheduleGuest.test.ts
  • packages/trpc/server/routers/viewer/slots/_getRescheduledGuestUser.test.ts
  • packages/trpc/server/routers/viewer/slots/util.ts

Comment on lines +1299 to +1306
const organizerEmails = Array.from(
new Set(allHosts.map((host) => host.user.email.toLowerCase()).filter(Boolean))
);
const rescheduledGuestUser = await this.getRescheduledGuestUser({
rescheduleUid: input.rescheduleUid,
organizerEmails,
schedulingType: eventType.schedulingType,
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Build organizerEmails from every possible organizer host.

Right now this list comes from allHosts, so a round-robin organizer who only exists in eligibleFallbackRRHosts (or was filtered out before this point) is invisible to _getRescheduledGuestUser(). Then their attendee row can be mistaken for the guest, and the host gets appended again as a fixed participant. That breaks exactly the fallback path this PR is trying to fix.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/trpc/server/routers/viewer/slots/util.ts` around lines 1299 - 1306,
The organizerEmails array is currently built only from allHosts so hosts that
exist solely in eligibleFallbackRRHosts are omitted, causing
getRescheduledGuestUser (this.getRescheduledGuestUser) to misidentify attendees;
update the organizerEmails construction to include emails from every possible
organizer host by combining allHosts and eligibleFallbackRRHosts (and any other
host collections used earlier), then map to host.user.email.toLowerCase(),
filter falsy values, and dedupe with Set before passing organizerEmails into
this.getRescheduledGuestUser({ rescheduleUid: input.rescheduleUid,
organizerEmails, schedulingType: eventType.schedulingType }).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bookings area: bookings, availability, timezones, double booking 🙋 Bounty claim 💎 Bounty A bounty on Algora.io ✨ feature New feature or request 🧹 Improvements Improvements to existing features. Mostly UX/UI Medium priority Created by Linear-GitHub Sync size/L $200

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[CAL-4531] Take into account guest's availability when rescheduling

2 participants