Skip to content

Comments

fix: unconfirmed bookings do not block slots of other event types#23436

Open
bandhan-majumder wants to merge 37 commits intocalcom:mainfrom
bandhan-majumder:fix-23069
Open

fix: unconfirmed bookings do not block slots of other event types#23436
bandhan-majumder wants to merge 37 commits intocalcom:mainfrom
bandhan-majumder:fix-23069

Conversation

@bandhan-majumder
Copy link
Contributor

@bandhan-majumder bandhan-majumder commented Aug 28, 2025

What does this PR do?

This PR does 3 things mentioned here

  • Fixes the feature so other event types have the slots as busy
  • Improves text on setting so users know the effect it will have across event types
  • Clearly shows in troubleshooter that’s it’s unconfirmed but event type X has this setting enabled

What logic changed?

1.

For searching free slot, we were previously searching based on events type like this:

Logic for getting free slot

Before logic
    const currentBookingsAllUsersQueryThree = eventTypeId
      ? this.prismaClient.booking.findMany({
          where: {
            startTime: { lte: endDate },
            endTime: { gte: startDate },
            eventType: {
              id: eventTypeId, // notice this event type id here
              requiresConfirmation: true,
              requiresConfirmationWillBlockSlot: true,
            },
            status: {
              in: [BookingStatus.PENDING]  
           },
      },
      select: bookingsSelect,
    });
After logic (search all the events if they have pending bookings)
 // host-owned PENDING bookings
    const currentBookingsAllUsersQueryThree = this.prismaClient.booking.findMany({
      where: {
        startTime: { lte: endDate },
        endTime: { gte: startDate },
        userId: {
          in: Array.from(userIdAndEmailMap.keys()),
        },
        eventType: {
          requiresConfirmation: true,
          requiresConfirmationWillBlockSlot: true,
        },
        status: {
          in: [BookingStatus.PENDING],
        },
      },
      select: bookingsSelect,
    });

note:-

here, there is a possibility that the host is a participant of a booking that is pending and requires confirmation and requires confirmation book slot enabled, so we are filtering that later on.

I have added tests for this behaviour here in this file: packages/lib/server/repository/booking.test.ts

also have recorded the behavior #23436 (comment) in the below comment


2.

Change in troubleshooter

  1. Whenever a pending booking (e.g: 60 min between free and pro) comes, I modify the title with (unconfirmed but still blocks) (as mentioned here)
image

3.

Change in settings of Advanced section (adding a tooltip)
Dark mode
image
Light mode
image

4

There are some type changes to make eslint happy and some changes to make sure we get the properties of the event type of the booking before reaching the troubleshooter

note:

Previously it was suggested to tell the event type too in the troubleshooter heading (see #23069 (comment)), so I had added those properties

But later, this plan was changed to adding unconfirmed but still blocking at the end of the title (see #23069 (comment)).

So, i have not removed the properties which was added to get more info about the event specially to make sure we have that option ready for the trouble shooter for future..

That's it


Issues fixes

Video Demo (if applicable):

https://www.loom.com/share/b90e2247b74940d0abdfd3c5992ddb66?sid=21404455-9e7b-4eec-a4ea-d86003bc5ebd

Mandatory Tasks (DO NOT REMOVE)

  • I have self-reviewed the code (A decent size PR without self-review might be rejected).
  • 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. Set require confirmation will block slot for an event type
  2. Book a booking of that event
  3. Check trouble shooter as it will show "unconfirmed booking but event [EVENT NAME] has this settings enabled"
  4. Try to book a booking of another any event of the same slot
  5. The slot will not be available across all the events

Checklist

  • I haven't read the contributing guide
  • My code doesn't follow the style guidelines of this project
  • I haven't commented my code, particularly in hard-to-understand areas
  • I haven't checked if my changes generate no new warnings

@bandhan-majumder bandhan-majumder requested a review from a team as a code owner August 28, 2025 23:03
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 28, 2025

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

The PR changes booking lookup semantics used to mark reserved slots: the eventTypeId filter was removed so pending bookings are fetched across event types when the event type flags (requiresConfirmation && requiresConfirmationWillBlockSlot) apply. Repository queries were extended to include attendee-email-based pending bookings and added deduplication logic when aggregating results. No public API signatures were changed.

Assessment against linked issues

Objective Addressed Explanation
Treat unconfirmed bookings as busy across all event types when the "Unconfirmed bookings still block calendar slots." setting is enabled (#23069, CAL-6248) Cross-event-type booking lookup was implemented, but there is no check of the global setting that toggles "Unconfirmed bookings still block calendar slots." — unclear whether behavior is conditional on that setting.

Out-of-scope changes

Code Change Explanation
Removed eventTypeId filter in booking lookup (packages/trpc/server/routers/viewer/slots/util.ts) Introduces cross-event-type blocking unconditionally at the caller level; does not reference the feature toggle from the linked issues, so it may change behavior beyond the stated conditional requirement.
Replaced eventTypeId-based query paths and added attendee-email pending booking query (packages/lib/server/repository/booking.ts) Changes repository aggregation to return cross-event-type PENDING bookings based on event-type flags rather than a specified eventTypeId; this shifts filtering responsibility and can produce broader results regardless of the global setting.

Possibly related PRs

✨ Finishing touches
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

@vercel
Copy link

vercel bot commented Aug 28, 2025

@bandhan-majumder is attempting to deploy a commit to the cal Team on Vercel.

A member of the Team first needs to authorize it.

@graphite-app graphite-app bot added the community Created by Linear-GitHub Sync label Aug 28, 2025
@graphite-app graphite-app bot requested a review from a team August 28, 2025 23:03
@github-actions github-actions bot added bookings area: bookings, availability, timezones, double booking consumer event-types area: event types, event-types Medium priority Created by Linear-GitHub Sync labels Aug 28, 2025
@github-actions
Copy link
Contributor

Hey there and thank you for opening this pull request! 👋🏼

We require pull request titles to follow the Conventional Commits specification and it looks like your proposed title needs to be adjusted.

Details:

No release type found in pull request title "Update util.ts". Add a prefix to indicate what kind of release this pull request corresponds to. For reference, see https://www.conventionalcommits.org/

Available types:
 - feat: A new feature
 - fix: A bug fix
 - docs: Documentation only changes
 - style: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
 - refactor: A code change that neither fixes a bug nor adds a feature
 - perf: A code change that improves performance
 - test: Adding missing tests or correcting existing tests
 - build: Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)
 - ci: Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs)
 - chore: Other changes that don't modify src or test files
 - revert: Reverts a previous commit

@bandhan-majumder bandhan-majumder changed the title Update util.ts fix: unconfirmed bookings do not block slots of other event types Aug 28, 2025
@dosubot dosubot bot added the 🐛 bug Something isn't working label Aug 28, 2025
Copy link
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

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 2997679 and a33f5ee.

📒 Files selected for processing (1)
  • packages/lib/server/repository/booking.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/review.mdc)

**/*.ts: For Prisma queries, only select data you need; never use include, always use select
Ensure the credential.key field is never returned from tRPC endpoints or APIs

Files:

  • packages/lib/server/repository/booking.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/review.mdc)

Flag excessive Day.js use in performance-critical code; prefer native Date or Day.js .utc() in hot paths like loops

Files:

  • packages/lib/server/repository/booking.ts
**/*.{ts,tsx,js,jsx}

⚙️ CodeRabbit configuration file

Flag default exports and encourage named exports. Named exports provide better tree-shaking, easier refactoring, and clearer imports. Exempt main components like pages, layouts, and components that serve as the primary export of a module.

Files:

  • packages/lib/server/repository/booking.ts
🧠 Learnings (3)
📓 Common learnings
Learnt from: supalarry
PR: calcom/cal.com#23217
File: apps/api/v2/src/ee/event-types/event-types_2024_06_14/services/output-event-types.service.ts:93-94
Timestamp: 2025-08-21T13:44:06.805Z
Learning: In apps/api/v2/src/ee/event-types/event-types_2024_06_14/event-types.repository.ts, repository functions that use explicit Prisma select clauses (like getEventTypeWithSeats) are used for specific purposes and don't need to include all EventType fields like bookingRequiresAuthentication. These methods don't feed into the general OutputEventTypesService_2024_06_14 flow.
Learnt from: supalarry
PR: calcom/cal.com#23364
File: apps/api/v2/src/ee/event-types/event-types_2024_06_14/transformers/internal-to-api/internal-to-api.spec.ts:295-296
Timestamp: 2025-08-27T13:32:46.887Z
Learning: In calcom/cal.com, when transforming booking fields from internal to API format, tests in organizations-event-types.e2e-spec.ts already expect name field label and placeholder to be empty strings ("") rather than undefined. PR changes that set these to explicit empty strings are typically fixing implementation to match existing test expectations rather than breaking changes.
📚 Learning: 2025-08-21T13:44:06.805Z
Learnt from: supalarry
PR: calcom/cal.com#23217
File: apps/api/v2/src/ee/event-types/event-types_2024_06_14/services/output-event-types.service.ts:93-94
Timestamp: 2025-08-21T13:44:06.805Z
Learning: In apps/api/v2/src/ee/event-types/event-types_2024_06_14/event-types.repository.ts, repository functions that use explicit Prisma select clauses (like getEventTypeWithSeats) are used for specific purposes and don't need to include all EventType fields like bookingRequiresAuthentication. These methods don't feed into the general OutputEventTypesService_2024_06_14 flow.

Applied to files:

  • packages/lib/server/repository/booking.ts
📚 Learning: 2025-08-22T16:38:00.225Z
Learnt from: bandhan-majumder
PR: calcom/cal.com#23192
File: packages/lib/server/service/InsightsBookingBaseService.ts:814-816
Timestamp: 2025-08-22T16:38:00.225Z
Learning: In the InsightsBookingBaseService (packages/lib/server/service/InsightsBookingBaseService.ts), when filtering for "accepted" bookings in getMembersStatsWithCount(), using `endTime <= now()` in the SQL condition should be avoided as it conflicts with existing date filtering logic. The components already handle completion filtering by setting `endDate: currentTime` in their query parameters, making additional SQL-level endTime filtering unnecessary and potentially problematic.

Applied to files:

  • packages/lib/server/repository/booking.ts
⏰ Context from checks skipped due to timeout of 180000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Tests / Unit
  • GitHub Check: Type check / check-types
  • GitHub Check: Linters / lint

Copy link
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 (1)
packages/lib/server/repository/booking.ts (1)

259-277: Add tests to prevent regressions (pending host vs pending attendee).

Please add integration tests for:

  • Host has PENDING booking on their own event type (flags on) → other event types show slot blocked.
  • Host is attendee on another organizer’s event type with flags on → slot blocked.
  • Same two cases with requiresConfirmationWillBlockSlot = false → slot not blocked.

I can draft these tests if you confirm the preferred suite (e2e vs API unit) and fixtures for 30/60 min types.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between a33f5ee and a6b8311.

📒 Files selected for processing (1)
  • packages/lib/server/repository/booking.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/review.mdc)

**/*.ts: For Prisma queries, only select data you need; never use include, always use select
Ensure the credential.key field is never returned from tRPC endpoints or APIs

Files:

  • packages/lib/server/repository/booking.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/review.mdc)

Flag excessive Day.js use in performance-critical code; prefer native Date or Day.js .utc() in hot paths like loops

Files:

  • packages/lib/server/repository/booking.ts
**/*.{ts,tsx,js,jsx}

⚙️ CodeRabbit configuration file

Flag default exports and encourage named exports. Named exports provide better tree-shaking, easier refactoring, and clearer imports. Exempt main components like pages, layouts, and components that serve as the primary export of a module.

Files:

  • packages/lib/server/repository/booking.ts
🧠 Learnings (3)
📓 Common learnings
Learnt from: supalarry
PR: calcom/cal.com#23217
File: apps/api/v2/src/ee/event-types/event-types_2024_06_14/services/output-event-types.service.ts:93-94
Timestamp: 2025-08-21T13:44:06.805Z
Learning: In apps/api/v2/src/ee/event-types/event-types_2024_06_14/event-types.repository.ts, repository functions that use explicit Prisma select clauses (like getEventTypeWithSeats) are used for specific purposes and don't need to include all EventType fields like bookingRequiresAuthentication. These methods don't feed into the general OutputEventTypesService_2024_06_14 flow.
Learnt from: supalarry
PR: calcom/cal.com#23364
File: apps/api/v2/src/ee/event-types/event-types_2024_06_14/transformers/internal-to-api/internal-to-api.spec.ts:295-296
Timestamp: 2025-08-27T13:32:46.887Z
Learning: In calcom/cal.com, when transforming booking fields from internal to API format, tests in organizations-event-types.e2e-spec.ts already expect name field label and placeholder to be empty strings ("") rather than undefined. PR changes that set these to explicit empty strings are typically fixing implementation to match existing test expectations rather than breaking changes.
📚 Learning: 2025-08-21T13:44:06.805Z
Learnt from: supalarry
PR: calcom/cal.com#23217
File: apps/api/v2/src/ee/event-types/event-types_2024_06_14/services/output-event-types.service.ts:93-94
Timestamp: 2025-08-21T13:44:06.805Z
Learning: In apps/api/v2/src/ee/event-types/event-types_2024_06_14/event-types.repository.ts, repository functions that use explicit Prisma select clauses (like getEventTypeWithSeats) are used for specific purposes and don't need to include all EventType fields like bookingRequiresAuthentication. These methods don't feed into the general OutputEventTypesService_2024_06_14 flow.

Applied to files:

  • packages/lib/server/repository/booking.ts
📚 Learning: 2025-08-22T16:38:00.225Z
Learnt from: bandhan-majumder
PR: calcom/cal.com#23192
File: packages/lib/server/service/InsightsBookingBaseService.ts:814-816
Timestamp: 2025-08-22T16:38:00.225Z
Learning: In the InsightsBookingBaseService (packages/lib/server/service/InsightsBookingBaseService.ts), when filtering for "accepted" bookings in getMembersStatsWithCount(), using `endTime <= now()` in the SQL condition should be avoided as it conflicts with existing date filtering logic. The components already handle completion filtering by setting `endDate: currentTime` in their query parameters, making additional SQL-level endTime filtering unnecessary and potentially problematic.

Applied to files:

  • packages/lib/server/repository/booking.ts
⏰ Context from checks skipped due to timeout of 180000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Install dependencies / Yarn install & cache
🔇 Additional comments (1)
packages/lib/server/repository/booking.ts (1)

259-277: Correctly scoped PENDING-blocking to relevant hosts; aligns with cross–event-type blocking.

Good move removing the eventTypeId restriction and adding userId IN userIdAndEmailMap to avoid global over-blocking. This meets the objective while preventing runaway scans.

@bandhan-majumder
Copy link
Contributor Author

bandhan-majumder commented Aug 29, 2025

I will fix the tests once the changes are confirmed by the reviewer.

Copy link
Contributor

@anikdhabal anikdhabal left a comment

Choose a reason for hiding this comment

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

Pls add tests to verify the fix

Copy link
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 (3)
packages/lib/server/repository/booking.ts (3)

259-278: Host-owned PENDING query: correct semantics; ensure fixtures provide matching EventType rows

The join on eventType.requiresConfirmation && requiresConfirmationWillBlockSlot is right and matches the product requirement. Note this will return zero rows if the referenced EventType doesn’t exist or those flags aren’t true in tests/fixtures. See my test fix suggestion in booking.test.ts.

Minor: reuse a shared time-overlap object to avoid duplication.

-      where: {
-        startTime: { lte: endDate },
-        endTime: { gte: startDate },
+      where: {
+        ...timeOverlapWhere,
         userId: {
           in: Array.from(userIdAndEmailMap.keys()),
         },

Outside this range, define once near sharedQuery:

const timeOverlapWhere = { startTime: { lte: endDate }, endTime: { gte: startDate } } as const;

280-301: Attendee-based PENDING query: good coverage; add early return on empty map

This correctly mirrors the accepted-attendee path for PENDING. Consider short‑circuiting if userIdAndEmailMap.size === 0 to avoid unnecessary scans.

if (userIdAndEmailMap.size === 0) return [];

320-320: Deduplicate potential PENDING overlaps across host/attendee paths

Rare, but if an organizer is also listed as an attendee on the same PENDING booking, it will appear in both resultThree and resultFour. Deduplicate by id before returning.

Apply this diff:

-    return [...resultOne, ...resultTwoWithOrganizersRemoved, ...resultThree, ...resultFour];
+    const pendingById = new Map<number, typeof resultThree[number]>();
+    for (const b of resultThree) pendingById.set(b.id, b);
+    for (const b of resultFour) pendingById.set(b.id, b);
+    return [...resultOne, ...resultTwoWithOrganizersRemoved, ...pendingById.values()];
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between de93601 and cbd3956.

📒 Files selected for processing (2)
  • packages/lib/server/repository/booking.test.ts (1 hunks)
  • packages/lib/server/repository/booking.ts (2 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/review.mdc)

**/*.ts: For Prisma queries, only select data you need; never use include, always use select
Ensure the credential.key field is never returned from tRPC endpoints or APIs

Files:

  • packages/lib/server/repository/booking.test.ts
  • packages/lib/server/repository/booking.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/review.mdc)

Flag excessive Day.js use in performance-critical code; prefer native Date or Day.js .utc() in hot paths like loops

Files:

  • packages/lib/server/repository/booking.test.ts
  • packages/lib/server/repository/booking.ts
**/*.{ts,tsx,js,jsx}

⚙️ CodeRabbit configuration file

Flag default exports and encourage named exports. Named exports provide better tree-shaking, easier refactoring, and clearer imports. Exempt main components like pages, layouts, and components that serve as the primary export of a module.

Files:

  • packages/lib/server/repository/booking.test.ts
  • packages/lib/server/repository/booking.ts
🧠 Learnings (4)
📓 Common learnings
Learnt from: supalarry
PR: calcom/cal.com#23364
File: apps/api/v2/src/ee/event-types/event-types_2024_06_14/transformers/internal-to-api/internal-to-api.spec.ts:295-296
Timestamp: 2025-08-27T13:32:46.887Z
Learning: In calcom/cal.com, when transforming booking fields from internal to API format, tests in organizations-event-types.e2e-spec.ts already expect name field label and placeholder to be empty strings ("") rather than undefined. PR changes that set these to explicit empty strings are typically fixing implementation to match existing test expectations rather than breaking changes.
Learnt from: supalarry
PR: calcom/cal.com#23217
File: apps/api/v2/src/ee/event-types/event-types_2024_06_14/services/output-event-types.service.ts:93-94
Timestamp: 2025-08-21T13:44:06.805Z
Learning: In apps/api/v2/src/ee/event-types/event-types_2024_06_14/event-types.repository.ts, repository functions that use explicit Prisma select clauses (like getEventTypeWithSeats) are used for specific purposes and don't need to include all EventType fields like bookingRequiresAuthentication. These methods don't feed into the general OutputEventTypesService_2024_06_14 flow.
📚 Learning: 2025-08-27T13:32:46.887Z
Learnt from: supalarry
PR: calcom/cal.com#23364
File: apps/api/v2/src/ee/event-types/event-types_2024_06_14/transformers/internal-to-api/internal-to-api.spec.ts:295-296
Timestamp: 2025-08-27T13:32:46.887Z
Learning: In calcom/cal.com, when transforming booking fields from internal to API format, tests in organizations-event-types.e2e-spec.ts already expect name field label and placeholder to be empty strings ("") rather than undefined. PR changes that set these to explicit empty strings are typically fixing implementation to match existing test expectations rather than breaking changes.

Applied to files:

  • packages/lib/server/repository/booking.test.ts
📚 Learning: 2025-08-21T13:44:06.805Z
Learnt from: supalarry
PR: calcom/cal.com#23217
File: apps/api/v2/src/ee/event-types/event-types_2024_06_14/services/output-event-types.service.ts:93-94
Timestamp: 2025-08-21T13:44:06.805Z
Learning: In apps/api/v2/src/ee/event-types/event-types_2024_06_14/event-types.repository.ts, repository functions that use explicit Prisma select clauses (like getEventTypeWithSeats) are used for specific purposes and don't need to include all EventType fields like bookingRequiresAuthentication. These methods don't feed into the general OutputEventTypesService_2024_06_14 flow.

Applied to files:

  • packages/lib/server/repository/booking.test.ts
  • packages/lib/server/repository/booking.ts
📚 Learning: 2025-08-22T16:38:00.225Z
Learnt from: bandhan-majumder
PR: calcom/cal.com#23192
File: packages/lib/server/service/InsightsBookingBaseService.ts:814-816
Timestamp: 2025-08-22T16:38:00.225Z
Learning: In the InsightsBookingBaseService (packages/lib/server/service/InsightsBookingBaseService.ts), when filtering for "accepted" bookings in getMembersStatsWithCount(), using `endTime <= now()` in the SQL condition should be avoided as it conflicts with existing date filtering logic. The components already handle completion filtering by setting `endDate: currentTime` in their query parameters, making additional SQL-level endTime filtering unnecessary and potentially problematic.

Applied to files:

  • packages/lib/server/repository/booking.ts
🧬 Code graph analysis (1)
packages/lib/server/repository/booking.test.ts (1)
packages/lib/server/repository/booking.ts (1)
  • BookingRepository (117-975)
🪛 GitHub Check: Tests / Unit
packages/lib/server/repository/booking.test.ts

[failure] 329-329: packages/lib/server/repository/booking.test.ts > _findAllExistingBookingsForEventTypeBetween > PENDING bookings with requiresConfirmationWillBlockSlot > should include PENDING bookings where user is an attendee
AssertionError: expected [] to have a length of 1 but got +0

  • Expected
  • Received
  • 1
  • 0

❯ packages/lib/server/repository/booking.test.ts:329:24


[failure] 295-295: packages/lib/server/repository/booking.test.ts > _findAllExistingBookingsForEventTypeBetween > PENDING bookings with requiresConfirmationWillBlockSlot > should include PENDING bookings where user is the host/organizer
AssertionError: expected [] to have a length of 1 but got +0

  • Expected
  • Received
  • 1
  • 0

❯ packages/lib/server/repository/booking.test.ts:295:24

⏰ Context from checks skipped due to timeout of 180000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Codacy Static Code Analysis
🔇 Additional comments (1)
packages/lib/server/repository/booking.ts (1)

303-308: Promise.all usage looks good

Parallelizing the four queries is appropriate here.

Copy link
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: 0

🧹 Nitpick comments (6)
packages/lib/server/repository/booking.ts (4)

185-196: Remove unused eventTypeId from params

eventTypeId is commented out in destructuring but still present in the signature. Drop it to avoid confusion and dead params.

-  private async _findAllExistingBookingsForEventTypeBetween({
-    // eventTypeId,
+  private async _findAllExistingBookingsForEventTypeBetween({
     seatedEvent = false,
     startDate,
     endDate,
     userIdAndEmailMap,
   }: {
     startDate: Date;
     endDate: Date;
-    eventTypeId?: number | null;
     seatedEvent?: boolean;
     userIdAndEmailMap: Map<number, string>;
   }) {

197-204: DRY the time-overlap predicate

You repeat the time window in multiple queries. Extract once to avoid drift.

   }) {
-    const sharedQuery = {
+    const sharedQuery = {
       startTime: { lte: endDate },
       endTime: { gte: startDate },
       status: {
         in: [BookingStatus.ACCEPTED],
       },
     };
+    const timeOverlap = { startTime: { lte: endDate }, endTime: { gte: startDate } };
...
-      where: {
-        startTime: { lte: endDate },
-        endTime: { gte: startDate },
+      where: {
+        ...timeOverlap,
         userId: {
           in: Array.from(userIdAndEmailMap.keys()),
         },
...
-      where: {
-        startTime: { lte: endDate },
-        endTime: { gte: startDate },
+      where: {
+        ...timeOverlap,
         attendees: {

Also applies to: 259-266, 282-285


280-301: Email matching may be case-sensitive; confirm normalization

attendees.some.email in [...userIdAndEmailMap.values()] can miss matches if email casing differs (Postgres comparisons are case-sensitive by default; Prisma in doesn’t support mode: 'insensitive'). Ensure emails are lowercased at write-time and that userIdAndEmailMap values are lowercase; otherwise, lowercase the array before use.

-              in: Array.from(userIdAndEmailMap.values()),
+              in: Array.from(userIdAndEmailMap.values()).map((e) => e.toLowerCase()),

If DB isn’t normalized to lowercase, this alone won’t fix it—please confirm DB/email normalization across attendees and users. I can script-check usages if helpful.


314-332: Deduplicate by booking.id across all result sets

Pairwise filtering only removes a subset (e.g., when organizer is also an attendee with the same email). A host-owned PENDING where another organizer is an attendee will still duplicate. Safer: merge then dedupe by id.

-    const resultTwoWithOrganizersRemoved = resultTwo.filter((booking) => {
-      if (!booking.userId) return true;
-      const organizerEmail = userIdAndEmailMap.get(booking.userId);
-      return !booking.attendees.some((attendee) => attendee.email === organizerEmail);
-    });
-
-    // Remove duplicates from resultFour (PENDING attendee bookings) where organizer is also an attendee
-    const resultFourWithOrganizersRemoved = resultFour.filter((booking) => {
-      if (!booking.userId) return true;
-      const organizerEmail = userIdAndEmailMap.get(booking.userId);
-      return !booking.attendees.some((attendee) => attendee.email === organizerEmail);
-    });
-
-    return [
-      ...resultOne,
-      ...resultTwoWithOrganizersRemoved,
-      ...resultThree,
-      ...resultFourWithOrganizersRemoved,
-    ];
+    const merged = [...resultOne, ...resultTwo, ...resultThree, ...resultFour];
+    const dedupedById = Array.from(new Map(merged.map((b) => [b.id, b])).values());
+    return dedupedById;

Optional follow-up: with id-based dedupe, you can also replace One/Two and Three/Four pairs with single OR queries each to reduce round trips.

packages/lib/server/repository/booking.test.ts (2)

258-371: Add test: organizer is attendee on someone else’s PENDING booking

Covers Query Four explicitly; current suite doesn’t validate this path.

+  it("PENDING booking where organizer is an attendee should block", async () => {
+    await prismaMock.eventType.create({
+      data: {
+        id: 10,
+        title: "Other Host ET",
+        slug: "other-host-et",
+        userId: 2,
+        length: 60,
+        requiresConfirmation: true,
+        requiresConfirmationWillBlockSlot: true,
+      },
+    });
+    await prismaMock.booking.create({
+      data: {
+        userId: 2,
+        uid: "pending-other-host",
+        eventTypeId: 10,
+        status: BookingStatus.PENDING,
+        attendees: {
+          create: {
+            email: "test1@example.com", // organizer email
+            noShow: false,
+            name: "Organizer",
+            timeZone: "America/Toronto",
+          },
+        },
+        startTime: new Date("2025-05-01T14:00:00.000Z"),
+        endTime: new Date("2025-05-01T15:00:00.000Z"),
+        title: "PENDING other host",
+      },
+    });
+    const bookingRepo = new BookingRepository(prismaMock);
+    const userIdAndEmailMap = new Map([[1, "test1@example.com"]]);
+    const bookings = await bookingRepo.findAllExistingBookingsForEventTypeBetween({
+      startDate: new Date("2025-05-01T13:00:00.000Z"),
+      endDate: new Date("2025-05-01T16:00:00.000Z"),
+      userIdAndEmailMap,
+    });
+    expect(bookings).toHaveLength(1);
+  });

258-371: Add test: duplicate path (host-owned + attendee-other-organizer) dedupes to one

Prevents double-counting when a host-owned PENDING booking also lists another organizer as attendee. This will catch regressions if id-level dedupe is removed.

+  it("does not return duplicates when a host-owned PENDING also lists another organizer as attendee", async () => {
+    await prismaMock.eventType.create({
+      data: {
+        id: 20,
+        title: "Host ET",
+        slug: "host-et",
+        userId: 1,
+        length: 60,
+        requiresConfirmation: true,
+        requiresConfirmationWillBlockSlot: true,
+      },
+    });
+    await prismaMock.booking.create({
+      data: {
+        userId: 1, // organizer A (host-owned => Query Three)
+        uid: "pending-host-owned",
+        eventTypeId: 20,
+        status: BookingStatus.PENDING,
+        attendees: {
+          create: [
+            { email: "organizer-b@example.com", name: "Organizer B", timeZone: "UTC", noShow: false }, // triggers Query Four for organizer B
+          ],
+        },
+        startTime: new Date("2025-05-01T14:00:00.000Z"),
+        endTime: new Date("2025-05-01T15:00:00.000Z"),
+        title: "Host-owned with another organizer attendee",
+      },
+    });
+    const bookingRepo = new BookingRepository(prismaMock);
+    const userIdAndEmailMap = new Map([
+      [1, "organizer-a@example.com"],
+      [2, "organizer-b@example.com"],
+    ]);
+    const bookings = await bookingRepo.findAllExistingBookingsForEventTypeBetween({
+      startDate: new Date("2025-05-01T13:00:00.000Z"),
+      endDate: new Date("2025-05-01T16:00:00.000Z"),
+      userIdAndEmailMap,
+    });
+    expect(bookings).toHaveLength(1);
+  });
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between cbd3956 and 8987bda.

📒 Files selected for processing (2)
  • packages/lib/server/repository/booking.test.ts (1 hunks)
  • packages/lib/server/repository/booking.ts (3 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/review.mdc)

**/*.ts: For Prisma queries, only select data you need; never use include, always use select
Ensure the credential.key field is never returned from tRPC endpoints or APIs

Files:

  • packages/lib/server/repository/booking.ts
  • packages/lib/server/repository/booking.test.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/review.mdc)

Flag excessive Day.js use in performance-critical code; prefer native Date or Day.js .utc() in hot paths like loops

Files:

  • packages/lib/server/repository/booking.ts
  • packages/lib/server/repository/booking.test.ts
**/*.{ts,tsx,js,jsx}

⚙️ CodeRabbit configuration file

Flag default exports and encourage named exports. Named exports provide better tree-shaking, easier refactoring, and clearer imports. Exempt main components like pages, layouts, and components that serve as the primary export of a module.

Files:

  • packages/lib/server/repository/booking.ts
  • packages/lib/server/repository/booking.test.ts
🧠 Learnings (6)
📓 Common learnings
Learnt from: supalarry
PR: calcom/cal.com#23364
File: apps/api/v2/src/ee/event-types/event-types_2024_06_14/transformers/internal-to-api/internal-to-api.spec.ts:295-296
Timestamp: 2025-08-27T13:32:46.887Z
Learning: In calcom/cal.com, when transforming booking fields from internal to API format, tests in organizations-event-types.e2e-spec.ts already expect name field label and placeholder to be empty strings ("") rather than undefined. PR changes that set these to explicit empty strings are typically fixing implementation to match existing test expectations rather than breaking changes.
Learnt from: supalarry
PR: calcom/cal.com#23217
File: apps/api/v2/src/ee/event-types/event-types_2024_06_14/services/output-event-types.service.ts:93-94
Timestamp: 2025-08-21T13:44:06.805Z
Learning: In apps/api/v2/src/ee/event-types/event-types_2024_06_14/event-types.repository.ts, repository functions that use explicit Prisma select clauses (like getEventTypeWithSeats) are used for specific purposes and don't need to include all EventType fields like bookingRequiresAuthentication. These methods don't feed into the general OutputEventTypesService_2024_06_14 flow.
📚 Learning: 2025-08-21T13:44:06.805Z
Learnt from: supalarry
PR: calcom/cal.com#23217
File: apps/api/v2/src/ee/event-types/event-types_2024_06_14/services/output-event-types.service.ts:93-94
Timestamp: 2025-08-21T13:44:06.805Z
Learning: In apps/api/v2/src/ee/event-types/event-types_2024_06_14/event-types.repository.ts, repository functions that use explicit Prisma select clauses (like getEventTypeWithSeats) are used for specific purposes and don't need to include all EventType fields like bookingRequiresAuthentication. These methods don't feed into the general OutputEventTypesService_2024_06_14 flow.

Applied to files:

  • packages/lib/server/repository/booking.ts
  • packages/lib/server/repository/booking.test.ts
📚 Learning: 2025-08-21T12:28:42.018Z
Learnt from: alishaz-polymath
PR: calcom/cal.com#23247
File: packages/features/webhooks/lib/factory/WebhookPayloadFactory.ts:274-282
Timestamp: 2025-08-21T12:28:42.018Z
Learning: In webhook DTOs in packages/features/webhooks/lib/dto/types.ts, the booking fields are restricted structures containing only specific fields (id, eventTypeId, userId, and sometimes additional fields like startTime or smsReminderNumber) rather than full database booking objects, so there are no security or PII leakage concerns when using these booking objects in webhook payloads.

Applied to files:

  • packages/lib/server/repository/booking.ts
📚 Learning: 2025-08-21T12:28:42.018Z
Learnt from: alishaz-polymath
PR: calcom/cal.com#23247
File: packages/features/webhooks/lib/factory/WebhookPayloadFactory.ts:274-282
Timestamp: 2025-08-21T12:28:42.018Z
Learning: In BookingPaymentInitiatedDTO and other webhook DTOs in packages/features/webhooks/lib/dto/types.ts, the booking field is a restricted structure containing only specific fields (id, eventTypeId, userId) rather than the full database booking object, so there are no security or PII leakage concerns when passing the booking object to buildEventPayload.

Applied to files:

  • packages/lib/server/repository/booking.ts
📚 Learning: 2025-08-27T13:32:46.887Z
Learnt from: supalarry
PR: calcom/cal.com#23364
File: apps/api/v2/src/ee/event-types/event-types_2024_06_14/transformers/internal-to-api/internal-to-api.spec.ts:295-296
Timestamp: 2025-08-27T13:32:46.887Z
Learning: In calcom/cal.com, when transforming booking fields from internal to API format, tests in organizations-event-types.e2e-spec.ts already expect name field label and placeholder to be empty strings ("") rather than undefined. PR changes that set these to explicit empty strings are typically fixing implementation to match existing test expectations rather than breaking changes.

Applied to files:

  • packages/lib/server/repository/booking.ts
  • packages/lib/server/repository/booking.test.ts
📚 Learning: 2025-08-22T16:38:00.225Z
Learnt from: bandhan-majumder
PR: calcom/cal.com#23192
File: packages/lib/server/service/InsightsBookingBaseService.ts:814-816
Timestamp: 2025-08-22T16:38:00.225Z
Learning: In the InsightsBookingBaseService (packages/lib/server/service/InsightsBookingBaseService.ts), when filtering for "accepted" bookings in getMembersStatsWithCount(), using `endTime <= now()` in the SQL condition should be avoided as it conflicts with existing date filtering logic. The components already handle completion filtering by setting `endDate: currentTime` in their query parameters, making additional SQL-level endTime filtering unnecessary and potentially problematic.

Applied to files:

  • packages/lib/server/repository/booking.ts
  • packages/lib/server/repository/booking.test.ts
⏰ Context from checks skipped due to timeout of 180000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Install dependencies / Yarn install & cache
🔇 Additional comments (4)
packages/lib/server/repository/booking.ts (2)

259-278: Scope fix for host-owned PENDING bookings looks good

Limiting to relevant organizer userIds prevents global over-blocking. Matches the PR goal.


303-308: Including the fourth query in aggregation is correct

Extending Promise.all to pick up attendee PENDING bookings completes the cross-event-type coverage.

packages/lib/server/repository/booking.test.ts (2)

258-325: Good cross-event-type PENDING coverage

Test validates that a blocking Event Type A PENDING booking hides slots for other event types. Solid setup with explicit flags.


326-371: Good negative control for non-blocking event types

Verifies requiresConfirmationWillBlockSlot=false does not block. Looks correct.

@bandhan-majumder
Copy link
Contributor Author

done @anikdhabal . Added tests (and a missing filtering logic)

Comment on lines 280 to 288
// when organizer is an attendee on a PENDING booking.
const currentBookingsAllUsersQueryFour = this.prismaClient.booking.findMany({
where: {
startTime: { lte: endDate },
endTime: { gte: startDate },
attendees: {
some: {
email: {
in: Array.from(userIdAndEmailMap.values()),
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you explain why we doing this?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@anikdhabal yeah,, like if a relevant organizer is an attendee on someone else’s event type that has requiresConfirmationWillBlockSlot enabled. It sometimes duplicate the findings (as organizers are attendee in their meeting too) and for that reason, the filtering logic is added to avoid duplication here

this was suggested by coderabbit here

@bandhan-majumder
Copy link
Contributor Author

@anikdhabal @Udit-takkar can you please review this?

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.

No issues found across 7 files

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 7 files

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/eventtypes/components/tabs/advanced/RequiresConfirmationController.tsx">

<violation number="1" location="packages/features/eventtypes/components/tabs/advanced/RequiresConfirmationController.tsx:272">
The tooltip trigger is an aria-hidden, unfocusable `&lt;Icon&gt;` so the new help text cannot be accessed via keyboard or screen readers. Wrap the icon in a focusable control (e.g., a `&lt;button&gt;`/`&lt;span tabIndex={0}&gt;`) with an aria-label before passing it to `&lt;Tooltip&gt;`.</violation>
</file>

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

@mrkylegp
Copy link

@hariombalhara Please check and merge 🙏

@bandhan-majumder
Copy link
Contributor Author

@keithwillcode @emrysal @hariombalhara
Users are asking for this fix very often. Please review it. Fixed the conflicts.

Copy link
Member

Testing again now

Copy link
Member

@hariombalhara hariombalhara left a comment

Choose a reason for hiding this comment

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

Let's fix the unintentended translation change

@bandhan-majumder
Copy link
Contributor Author

@hariombalhara changed to prev. Sorry for that

@@ -566,15 +566,13 @@ export class BookingRepository {
}

private async _findAllExistingBookingsForEventTypeBetween({
Copy link
Member

Choose a reason for hiding this comment

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

nit: We should consider renaming this fn, considering that it returns only blocking bookings

sometjoing like

findAllBlockingBookingsForEventTypeBetween

hariombalhara
hariombalhara previously approved these changes Dec 12, 2025
Copy link
Member

@hariombalhara hariombalhara left a comment

Choose a reason for hiding this comment

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

LGTM !! Tested as well.

cc @emrysal @CarinaWolli for final review as it affects availability

@bandhan-majumder
Copy link
Contributor Author

bandhan-majumder commented Dec 16, 2025

As this is getting conflicts very often, I am keeping it updated. The changes are same (current changes are accepted in conflicts). Prev reviewer @hariombalhara has already approved the changes

@emrysal @keithwillcode @CarinaWolli please give it a final review

@mrkylegp
Copy link

mrkylegp commented Jan 8, 2026

Any updates on this one?

@bandhan-majumder
Copy link
Contributor Author

@keithwillcode @anikdhabal @CarinaWolli @emrysal

can i get the final review on this please?

},
eventType: {
requiresConfirmation: true,
requiresConfirmationWillBlockSlot: true,
Copy link
Contributor

Choose a reason for hiding this comment

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

This will do a joined search for every booking, instead we should filter out bookings by event type settings as this will be more performant.

Copy link
Contributor

@emrysal emrysal left a comment

Choose a reason for hiding this comment

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

Requesting changes for the joined where query, it's suboptimal

@bandhan-majumder
Copy link
Contributor Author

@emrysal are you saying that I should fetch the pending and blocked bookings first like this:

const pendingAndBlockingEventTypes = await this.prismaClient.eventType.findMany({
      where: {
        requiresConfirmation: true,
        requiresConfirmationWillBlockSlot: true,
      },
      select: { id: true },
    });

    const pendingAndBlockingEventTypeIds = pendingAndBlockingEventTypes.map((et) => et.id);

and then just search it like this?

userId: {
       in: Array.from(userIdAndEmailMap.keys()),
     },
     eventTypeId: {
       in: pendingAndBlockingEventTypeIds,
     },

but here, first I will be searching through all the pendingAndBlockingEventTypes across the whole eventType. If I want to reduce or limit it, then it will be as suboptimal...

@bandhan-majumder
Copy link
Contributor Author

@emrysal please let me know if this is what it should be. I will add the change.

@github-actions github-actions bot added the 🧹 Improvements Improvements to existing features. Mostly UX/UI label Feb 8, 2026
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 🐛 bug Something isn't working community Created by Linear-GitHub Sync consumer event-types area: event types, event-types high-risk Requires approval by Foundation team 🧹 Improvements Improvements to existing features. Mostly UX/UI Medium priority Created by Linear-GitHub Sync ready-for-e2e size/L

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Unconfirmed bookings don't block slots of other event types

8 participants