* feat(salesforce): add field rules for round robin skip
- Add rrSkipFieldRules schema to appDataSchema in zod.ts
- Implement applyFieldRules method in CrmService.ts for post-query filtering
- Integrate field rules into getContacts method when forRoundRobinSkip=true
- Add UI component for configuring field rules in EventTypeAppCardInterface
- Add translations for new UI strings
- Add comprehensive tests for field rules functionality
Field rules allow users to specify conditions based on Salesforce record fields
with 'ignore' or 'must_include' actions. Rules are evaluated with AND logic
and gracefully handle missing fields by skipping those rules.
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: add children prop to Section.SubSectionHeader for field rules
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* feat(salesforce): add edit button for field rules
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* feat(salesforce): apply field rules to GraphQL results
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* feat(salesforce): add Redis caching for field validation
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix(salesforce): remove Redis caching to fix ERR_INVALID_THIS error
- Removed Redis caching for field validation that was causing ERR_INVALID_THIS errors
- Simplified field rules filtering to try query directly and skip filtering if it fails
- Restored ensureFieldsExistOnObject method for other uses (write to record)
- Field rules now gracefully handle invalid fields by skipping filtering
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* chore: reformat getAttributes.ts
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat(salesforce): dynamically build GraphQL query for field rules with multi-edge iteration
- Add buildDynamicAccountQuery to inject field rule fields into GraphQL query
- Add passesFieldRules to evaluate ignore/must_include rules against UIAPI nodes
- Validate field rules via getObjectFieldNames (in-memory + Redis cache) before branching
- Remove applyFieldRulesToGraphQLRecords (used jsforce conn in GraphQL path)
- Iterate all edges in Tiers 1 and 2 instead of only the first result
- Rank Tier 3 accounts by contact count and fallback to next on field rule failure
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat(salesforce): add field rule routing trace steps
Add trace calls alongside field rule info logs for routing visibility:
- fieldRulesValidated: records validation result in CrmService
- fieldRuleFilteredRecord: records when account is filtered at each tier
- fieldRuleEvaluated: records individual rule evaluation details
- allRecordsFilteredByFieldRules: records when SOQL records are all filtered
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix(salesforce): remove duplicate field validation and fix disabled Select options
applyFieldRules now receives pre-validated rules from the early
validation block instead of re-validating internally. Also adds
the missing options prop to the disabled Select in the field rules UI.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat(salesforce): include failed rule details in field rule trace steps
Rename passesFieldRules to getFailingFieldRule so the caller receives
the specific rule that caused filtering. The fieldRuleFilteredRecord
trace step now includes failedRule with field, value, and action.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* test(salesforce): add GraphQL path tests for field rules
Cover field rule filtering across all three GraphQL resolution tiers
(contact, account, related contacts) with ignore and must_include
actions, multi-edge fallback, case-insensitivity, and dynamic query
selection.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Abstract new field rules settings
* Reduce extra SOQL call
* fix(salesforce): fix type errors in CrmService and GraphQL client
- Add missing import for getRedisService from @calcom/features/di/containers/Redis
- Fix ensureFieldsExistOnObject to properly return Field[] instead of incomplete function
- Use Array.from() instead of spread operator for Set and Map iterators to fix downlevelIteration errors
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-01 23:03:19 -05:00
Benny JooGitHubunknown <>Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* refactor: move Booker hooks from packages/features to apps/web/modules
Migrate the following Booker hooks:
- useOverlayCalendar
- useSkipConfirmStep
- useInitializeWeekStart
- useIsQuickAvailabilityCheckFeatureEnabled
- useDecoyBooking
These hooks are only imported by apps/web files, making them safe to move
without creating circular dependencies.
Part of the tRPC-driven UI migration from packages/features to apps/web/modules.
Co-Authored-By: benny@cal.com <sldisek783@gmail.com>
* fix: revert useIsQuickAvailabilityCheckFeatureEnabled move and fix import paths
- Reverted useIsQuickAvailabilityCheckFeatureEnabled.ts back to packages/features since it's imported by useSlots.ts in packages/features (would create circular dependency)
- Fixed import paths in useOverlayCalendar.ts to use @calcom/features paths
- Fixed import paths in useSkipConfirmStep.ts to use @calcom/features paths
- Updated Booker.tsx to import useIsQuickAvailabilityCheckFeatureEnabled from original location
Co-Authored-By: benny@cal.com <sldisek783@gmail.com>
* fix: use @calcom/web alias for hook imports to fix vitest resolution
Co-Authored-By: benny@cal.com <sldisek783@gmail.com>
* refactor
* migrate more hooks
* fix
* mv types
* fix
* fix
* fix
* fix
* revert: remove formatting-only changes to make PR easier to review
Co-Authored-By: benny@cal.com <sldisek783@gmail.com>
* fix
* fix
* fix
* revert: restore original import ordering in moved hooks
Co-Authored-By: benny@cal.com <sldisek783@gmail.com>
* fix imports
* fix
* fix
* wip
* wip
* wip
* wip
* wip
* wip
* fix: add isBrowser guard to useSlotsViewOnSmallScreen hook
Address Cubic AI review feedback (confidence 9/10) by adding the isBrowser
guard to the useSlotsViewOnSmallScreen hook to follow the file's client-only
contract and avoid executing during SSR/prerendering.
Co-Authored-By: unknown <>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-01-31 07:33:33 -03:00
sean-brydonGitHubunknown <>Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat: TOUTOC in handleSeats
* fix type check
* refactor: replace Prisma include with select in integration tests
Address Cubic AI review feedback (confidence 9/10) to use select instead
of include in Prisma queries to limit returned fields and avoid pulling
full related records.
Co-Authored-By: unknown <>
* fix seat count
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Adds encrypted credential storage using a new keyring system:
- New `encryptedKey` column with AES-256-GCM encryption
- Decryption in getCalendarsEvents with fallback to legacy `key`
- buildCredentialCreateData service for credential creation
- Phase 1: Google Calendar only, other integrations follow
2026-01-30 09:15:13 -03:00
Benny JooGitHubbenny@cal.com <sldisek783@gmail.com>benny@cal.com <sldisek783@gmail.com>benny@cal.com <sldisek783@gmail.com>benny@cal.com <sldisek783@gmail.com>Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: allow wildcard prefix in normalizeDomain function
The normalizeDomain function now accepts domains with *. prefix
(e.g., *.cal.com) for wildcard domain matching in the watchlist.
The domain part after *. is validated using the existing regex.
Co-Authored-By: alex@cal.com <me@alexvanandel.com>
* fix: allow wildcard domains in validation checks
Update WatchlistOperationsService and CreateBlocklistEntryModal to
accept wildcard domain prefixes (*.) in domain validation. This ensures
wildcard domains like *.cal.com can be added through both the API and UI.
Co-Authored-By: alex@cal.com <me@alexvanandel.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-01-29 21:56:55 -03:00
Alex van AndelGitHubalex@cal.com <me@alexvanandel.com>Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat: add wildcard domain matching to watchlist
When a domain is blocked in the watchlist (e.g., cal.com), all subdomains
are now also blocked (e.g., app.cal.com, sub.app.cal.com).
Changes:
- Add getParentDomains() utility to extract all parent domains
- Update GlobalBlockingService.isBlocked() and areBlocked() for wildcard matching
- Update OrganizationBlockingService.isBlocked() and areBlocked() for wildcard matching
- Add comprehensive tests for wildcard domain matching
- More specific subdomain matches take precedence over parent domain matches
- Email matches still take precedence over domain matches
Co-Authored-By: alex@cal.com <me@alexvanandel.com>
* feat: make wildcard domain matching configurable
- *.cal.com blocks all subdomains (app.cal.com, sub.app.cal.com, etc.)
- cal.com only blocks exact matches (not subdomains)
- Updated getWildcardPatternsForDomain to generate wildcard patterns
- Added domainMatchesWatchlistEntry for configurable matching logic
- Updated GlobalBlockingService and OrganizationBlockingService
- Updated tests to verify new behavior
Co-Authored-By: alex@cal.com <me@alexvanandel.com>
* refactor: simplify wildcard pattern generation to strip only first part
- Only strips the first part before the first dot
- Handles multi-part TLDs correctly (e.g., .co.uk)
- Returns empty array for two-part domains (e.g., cal.com -> no pattern)
- app.cal.com -> *.cal.com
- bloody-hell.cal.co.uk -> *.cal.co.uk
Co-Authored-By: alex@cal.com <me@alexvanandel.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-01-29 23:59:51 +00:00
Joe Au-YeungGitHubClaude Opus 4.5joe@cal.com <j.auyeung419@gmail.com>joe@cal.com <j.auyeung419@gmail.com>Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat: Add routing trace presenters
Add domain-specific presenters (SalesforceRoutingTracePresenter,
RoutingFormTracePresenter) that format trace steps into human-readable
strings, and a core RoutingTracePresenter that delegates to them based
on step domain. Includes unit tests for all presenters.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat: Add findByBookingUid to RoutingTraceRepository
Add method to look up a routing trace by booking UID, needed by the
routing trace presenter tRPC endpoint.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat: Add getRoutingTrace tRPC endpoint
Expose routing trace data for a booking via viewer.bookings.getRoutingTrace.
Tries the permanent RoutingTrace first (round robin bookings), then falls
back to PendingRoutingTrace via the booking's form response relation.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat: Add routing trace side sheet to booking list item
Add a route icon button on booking list items that came from routing
forms. Clicking it opens a side sheet displaying the full routing trace
as human-readable steps. Adds RoutingTraceSheet component, store state,
and translation key.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* refactor: Move routing trace action to dropdown menu
Move the routing trace button from a standalone icon on the booking list
item into the actions dropdown menu alongside "Report wrong assignment".
The RoutingTraceSheet is now rendered from BookingActionsDropdown.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: Improve routing trace UI and handle unnamed routes
- Redesign RoutingTraceSheet with vertical timeline layout, domain badges,
skeleton loading state, and millisecond timestamps
- Use Salesforce app-store icon for Salesforce steps
- Fall back to "Unnamed route" when route name is missing or matches route ID
- Fix dropdown menu icon to git-merge (valid icon, unique in menu)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* test: Add tests for getRoutingTrace handler and findByBookingUid repository method
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* Abstract functions
* Fix build error
* Add permission check
* fix: Update getRoutingTrace tests to include ctx and mock BookingAccessService
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
## What does this PR do?
Replaces direct instantiation of `FeaturesRepository` with the dependency-injected version using `getFeaturesRepository()` from the DI container in the feature flags router.
**Key improvement:** The DI-ed version uses Redis caching, replacing the previous in-memory 5-minute cache implementation which was not ideal. This follows the established DI pattern in the codebase for loose coupling, better testability, and more robust caching across instances.
## Type of change
- [x] Refactor (non-breaking change that improves code quality)
## Mandatory Tasks (DO NOT REMOVE)
- [x] I have self-reviewed the code (A decent size PR without self-review might be rejected).
- [x] I have updated the developer docs in /docs if this PR makes changes that would require a [documentation change](https://cal.com/docs). N/A - no documentation changes needed.
- [x] I confirm automated tests are in place that prove my fix is effective or that my feature works.
## How should this be tested?
1. Verify feature flags functionality works as expected:
- Feature flag list endpoint should return all features
- Team feature check should work correctly
2. No environment variables needed beyond standard setup
## Checklist
- [x] My code follows the style guidelines of this project
- [x] I have checked if my changes generate no new warnings
## Human Review Checklist
- [ ] Verify `getFeaturesRepository()` returns a properly configured instance from the DI container
- [ ] Confirm the DI container properly injects the Prisma client
- [ ] Verify Redis caching is working as expected for feature flag data
---
**Link to Devin run:** https://app.devin.ai/sessions/2ff6e4551c8649e4b6178b31a2195327
**Requested by:** @eunjae-lee
2026-01-29 16:47:46 +01:00
Benny JooGitHubbenny@cal.com <sldisek783@gmail.com>benny@cal.com <sldisek783@gmail.com>Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat: Add infrastructure for no-show audit integration
- Add Prisma migrations for SYSTEM source and NO_SHOW_UPDATED audit action
- Add NoShowUpdatedAuditActionService with array-based attendeesNoShow schema
- Update BookingAuditActionServiceRegistry to include NO_SHOW_UPDATED
- Update BookingAuditTaskConsumer and BookingAuditViewerService
- Add AttendeeRepository methods for no-show queries
- Update IAuditActionService interface with values array support
- Update locales with no-show audit translation keys
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Add NO_SHOW_UPDATED to BookingAuditAction and SYSTEM to ActionSource types
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Remove HOST_NO_SHOW_UPDATED and ATTENDEE_NO_SHOW_UPDATED from BookingAuditAction type to match Prisma schema
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Update BookingAuditActionSchema to use NO_SHOW_UPDATED instead of HOST_NO_SHOW_UPDATED and ATTENDEE_NO_SHOW_UPDATED
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* refactor: Remove deprecated no-show audit services and unify to NoShowUpdatedAuditActionService
- Delete HostNoShowUpdatedAuditActionService and AttendeeNoShowUpdatedAuditActionService
- Update BookingAuditProducerService.interface.ts to use queueNoShowUpdatedAudit
- Update BookingAuditTaskerProducerService.ts to use queueNoShowUpdatedAudit
- Update BookingEventHandlerService.ts to use onNoShowUpdated
- Add integration tests for NoShowUpdatedAuditActionService
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Add data migration step for deprecated no-show enum values
Addresses Cubic AI review feedback (confidence 9/10): The migration now
includes an UPDATE statement to convert existing records using the
deprecated 'host_no_show_updated' or 'attendee_no_show_updated' enum
values to the new unified 'no_show_updated' value before the type cast.
This prevents migration failures if any existing data uses the old values.
Co-Authored-By: unknown <>
* fix: Use CASE expression in USING clause for enum migration
Fixes PostgreSQL error 'unsafe use of new value of enum type' by avoiding
the ADD VALUE statement and instead using a CASE expression in the ALTER
TABLE USING clause to convert deprecated enum values (host_no_show_updated,
attendee_no_show_updated) to the new unified value (no_show_updated) during
the type conversion.
Co-Authored-By: unknown <>
* fix: Replace hardcoded color with semantic text-success class
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Remove color class completely from display fields
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-01-29 08:47:26 -03:00
Syed Ali ShahbazGitHubali@cal.com <alishahbaz7@gmail.com>unknown <>Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat: implement webhook tasker with async/sync fallback
This PR implements a webhook tasker with async/sync fallback architecture
to fix failing E2E tests. The solution follows the existing proration
tasker pattern and uses Dependency Injection with @evyweb/ioctopus.
Key changes:
- Create IWebhookTasker interface for webhook delivery
- Implement WebhookSyncTasker for immediate execution (E2E tests)
- Implement WebhookAsyncTasker for queued execution (production)
- Create main WebhookTasker class extending Tasker<IWebhookTasker>
- Add DI modules and tokens for all tasker components
- Update WebhookTaskerProducerService to use new WebhookTasker
- Add unit tests for sync and async taskers
The ENABLE_ASYNC_TASKER flag automatically selects the appropriate mode:
- Production: Uses WebhookAsyncTasker to queue tasks
- E2E Tests: Uses WebhookSyncTasker for immediate execution
This ensures webhooks are delivered immediately in E2E tests without
requiring the cron job that processes queued tasks.
Co-Authored-By: ali@cal.com <alishahbaz7@gmail.com>
* fix: update WebhookTaskerProducerService tests for new interface
Update tests to use the new deps-based constructor and
mockWebhookTasker.deliverWebhook instead of mockTasker.create
Co-Authored-By: ali@cal.com <alishahbaz7@gmail.com>
* fix: use moduleLoader pattern for WebhookProducerService in container
Co-Authored-By: ali@cal.com <alishahbaz7@gmail.com>
* refactor: replace InternalTasker with Trigger.dev for webhook delivery
This commit refactors the WebhookTasker to use Trigger.dev instead of
InternalTasker, following the pattern established in BookingEmailAndSmsTasker
and PlatformOrganizationBillingTasker (PR #26803).
Changes:
- Replace WebhookAsyncTasker with WebhookTriggerTasker that uses trigger.dev
- Create trigger.dev task files (deliver-webhook.ts, config.ts, schema.ts)
- Update DI modules to use WebhookTriggerTasker
- Remove old InternalTasker-based implementation
- Update unit tests for new implementation
The WebhookSyncTasker continues to execute webhooks immediately for E2E tests
where ENABLE_ASYNC_TASKER is automatically false.
Co-Authored-By: ali@cal.com <alishahbaz7@gmail.com>
* fix: update comments to reflect Trigger.dev usage instead of InternalTasker
Co-Authored-By: unknown <>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat: add invoice URL to proration and test seed scripts
- Add invoiceUrl field to MonthlyProration schema
- Update billing service to return hosted_invoice_url after finalizing
- Save invoice URL when creating proration invoices
- Add seed script for testing proration with real Stripe data
- Add cleanup script for test data removal
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat: add due invoice banner and invitation blocking for orgs
- Add DueInvoiceBanner component showing overdue prorations
- Add DueInvoiceService for checking blocking status and banner data
- Block invitations when proration invoices are 7+ days overdue
- Allow sub-team invites for existing org members (exception)
- Show banner to users with billing management permissions
- Link directly to Stripe invoice URL when available
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: set subscriptionId in team metadata in seed script
The seed script was not setting subscriptionId on the team metadata,
causing checkIfOrgNeedsUpgrade to treat the org as needing upgrade
and showing the "trialing" banner.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* push trigger test to script
* fix mocks
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* feat: add organization.passwordReset PBAC permission
Allow org admins/owners to reset passwords for members of their
organization via a new PBAC permission. Previously this was only
available to system-level admins.
- Add PasswordReset to CustomAction enum and PERMISSION_REGISTRY
- Create migration to grant permission to admin_role (owner has wildcard)
- Add org-scoped tRPC endpoint using createOrgPbacProcedure
- Handler validates org membership, prevents self-targeting, and blocks
resetting owner passwords
- Wire permission through MemberPermissions, getOrgMembersPageData, and
the org members table UI dropdown
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: use targeted select in org password reset to avoid over-fetching
Replace findById with findForPasswordReset repository method that only
selects email, name, and locale instead of the full userSelect which
includes sensitive fields like twoFactorSecret and backupCodes.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* test: add unit tests for sendPasswordReset handler
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
## What does this PR do?
Adds a `displayLocations` property to the `OptInFeatureConfig` interface to control where opt-in features are displayed (settings page, banner, or both), and applies this filtering at the callers' side.
## How to test locally
```
update "Feature" set enabled = true where slug='bookings-v3';
```
Run it to globally enable the flag, and then go to the settings page to see the "Features" menu.
---
**Changes:**
- Added `OptInFeatureDisplayLocation` type with values `"settings"` | `"banner"`
- Added optional `displayLocations` property to `OptInFeatureConfig` interface
- Added helper functions:
- `getFeatureDisplayLocations()` - returns display locations with default of `['settings']`
- `shouldDisplayFeatureAt()` - checks if a feature should display at a specific location
- `getOptInFeaturesForLocation()` - filters features by location
- Updated `getOptInFeaturesForScope()` to accept an optional `displayLocation` parameter for filtering
- Simplified `HAS_*_OPT_IN_FEATURES` constants to use `getOptInFeaturesForScope(scope, "settings").length > 0`
- Updated `FeatureOptInService.listFeaturesForUser()` to filter by 'settings' location
- Updated `FeatureOptInService.listFeaturesForTeam()` to filter by 'settings' location
- Updated `useFeatureOptInBanner` hook to check for 'banner' location before showing
**Default behavior:** If `displayLocations` is omitted, features default to `['settings']` only.
## Mandatory Tasks (DO NOT REMOVE)
- [x] I have self-reviewed the code (A decent size PR without self-review might be rejected).
- [x] I have updated the developer docs in /docs if this PR makes changes that would require a [documentation change](https://cal.com/docs). N/A - internal config change only.
- [x] I confirm automated tests are in place that prove my fix is effective or that my feature works.
## How should this be tested?
1. Verify the helper functions work as expected:
- `getFeatureDisplayLocations({ slug: "test", ... })` should return `["settings"]` (default)
- `getFeatureDisplayLocations({ slug: "test", displayLocations: ["banner"] })` should return `["banner"]`
- `shouldDisplayFeatureAt(feature, "settings")` should return `true` for features without `displayLocations`
- `getOptInFeaturesForScope("user", "banner")` should only return user-scoped features with `"banner"` in their `displayLocations`
2. Verify caller-side filtering:
- `listFeaturesForUser()` and `listFeaturesForTeam()` should only return features with 'settings' in displayLocations
- Banner hook should only show features with 'banner' in displayLocations
- `HAS_*_OPT_IN_FEATURES` constants should only be true if there are features with 'settings' location
## Checklist
- [x] My code follows the style guidelines of this project
- [x] I have commented my code, particularly in hard-to-understand areas
- [x] My changes generate no new warnings
---
### Human Review Checklist
- [ ] Verify the default behavior (defaulting to `['settings']`) matches requirements
- [ ] Confirm the filtering logic is applied correctly in all callers (service methods, banner hook, constants)
- [ ] Verify the banner hook correctly prevents showing features without 'banner' in displayLocations
- [ ] Note: The test mock returns all features regardless of `displayLocation` parameter - the filtering logic in `getOptInFeaturesForScope` isn't directly tested. Consider if this is acceptable or if tests should be added.
**Link to Devin run:** https://app.devin.ai/sessions/a064ee43a56d458caf2892b55959f1ea
**Requested by:** @eunjae-lee
* fix: exclude third-party video apps from areCalendarEventsEnabled setting
When areCalendarEventsEnabled is false, video meeting creation for third-party
video apps (like Daily.co) was being skipped, causing the meetingUrl field in
webhook payloads to contain the location identifier (integrations:daily) instead
of the actual video meeting URL.
This fix adds a skipCalendarEvent option to EventManager.create() that:
- Skips calendar event creation when true
- Skips CRM event creation when true (CRM events are tied to calendar events)
- Still creates video meetings for third-party video apps
The calling code in handleConfirmation.ts and RegularBookingService.ts now uses
this option when areCalendarEventsEnabled is false, ensuring video meetings are
still created while respecting the platform customer's preference to manage
their own calendar events.
Co-Authored-By: morgan@cal.com <morgan@cal.com>
* test: update E2E tests to verify skipCalendarEvent option is passed when areCalendarEventsEnabled is false
Co-Authored-By: morgan@cal.com <morgan@cal.com>
* refactor: address PR review feedback - remove comments and update E2E tests to spy on createAllCalendarEvents
Co-Authored-By: morgan@cal.com <morgan@cal.com>
* test: add spy for createAllCRMEvents in e2e tests when areCalendarEventsEnabled is false
Co-Authored-By: lauris@cal.com <lauris.skraucis@gmail.com>
* test: add missing spy for createAllCRMEvents in beforeEach hook
Co-Authored-By: morgan@cal.com <morgan@cal.com>
* fix: use videoCallUrl from metadata for webhook payload location
Co-Authored-By: lauris@cal.com <lauris.skraucis@gmail.com>
* fix: simplify webhook location to use metadata?.videoCallUrl || evt.location
Co-Authored-By: lauris@cal.com <lauris.skraucis@gmail.com>
* docs: add description about video call link limitations when calendar events disabled
Co-Authored-By: lauris@cal.com <lauris.skraucis@gmail.com>
* i18n: add translation key for calendar events disabled video limitation
Co-Authored-By: lauris@cal.com <lauris.skraucis@gmail.com>
* fix: update translation text to accurately reflect video app behavior
Co-Authored-By: lauris@cal.com <lauris.skraucis@gmail.com>
* Update common.json
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Lauris Skraucis <lauris.skraucis@gmail.com>
* refactor: join teams with feature enabled directly in findNextSubscriptionBatch query
- Changed findNextSubscriptionBatch to accept featureId instead of teamIds
- Join TeamFeatures table directly in the query instead of separate queries
- Updated CalendarSubscriptionService.checkForNewSubscriptions to pass featureId
- Removed separate getTeamsWithFeatureEnabled call
- Updated tests to reflect the new query structure
Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com>
* refactor: change featureId to featureIds array for flexibility
- Changed findNextSubscriptionBatch to accept featureIds array instead of single featureId
- Updated Prisma query to use 'in' operator for multiple feature IDs
- Updated CalendarSubscriptionService to pass array with single feature
- Updated all tests to use featureIds array
Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-01-28 17:52:00 -03:00
Joe Au-YeungGitHubClaude Opus 4.5joe@cal.com <j.auyeung419@gmail.com>joe@cal.com <j.auyeung419@gmail.com>joe@cal.com <j.auyeung419@gmail.com>joe@cal.com <j.auyeung419@gmail.com>Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>Volnei Munhoz
* Add DB schema
* Init zod schema
* Init RoutingTrace and PendingRoutingTrace repository interfaces
* Create PrismaPendingRoutingTraceRepository
* Init RoutingTraceService
* Create RoutingTraceService container
* User routing trace service in routing
* Create RoutingTraceRepository and PrismaRoutingTraceRepoistory
* Add findByFormResponseId and findByQueuedFormResponseId to PendingRoutingTraceRepository
* Update DI containers
* RoutingTraceService create process booking method
* Use pending routing trace rather than URL params
* Fix schema
* Fix writing assignment reason for routed booking
* Remove from service
* Refactor RoutingTraceService to not rely on async local storage
* Pass RoutingTraceService through routing call
* Add attribute-logic-evaluated to routing trace step
* Add routing trace to trpc endpoint
* Add CRM routing trace step
* Fix extracting routing trace to assignment reason
* Add back CRM params to prevent refetching
* test: Add unit tests for RoutingTraceService and repositories
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* test: Add missing mock for RoutingTraceService in getRoutedUrl tests
Also fix pre-existing lint issues in the test file:
- Add explicit types to mockForm and mockSerializableForm variables
- Add explicit type to url parameter in mockContext
- Replace 'as any' with 'as unknown as InstanceType<typeof UserRepository>'
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* Link pending form submission to routing trace
* Clean up
* Add lookup field assignment reasons
* Rename to PendingRoutingTrace
* Add migration file
* fix: Update RoutingTraceService tests to use assignmentReasonRepository mock
- Add getStepsCount() method back to RoutingTraceService
- Add queuedFormResponseId support to processForBooking method
- Update tests to use mockAssignmentReasonRepository instead of prisma mock
- Remove test for missing routingTraceRepository (all deps now required)
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: Remove PII (organizer email) from log payload in RoutingTraceService
Addresses Cubic AI review feedback with confidence 9/10.
Logging PII violates sensitive information logging rules.
Co-Authored-By: unknown <>
* Write field values at the time of routing
* Write attributes used to route
* feat: add CRM routing trace service
Add CrmRoutingTraceService as a reusable wrapper around RoutingTraceService
for CRM-specific tracing. Also adds CrmRoutingTraceServiceInterface type
to support passing trace services through the CRM call chain.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat: add Salesforce routing trace infrastructure
Add SalesforceRoutingTrace static class with 19 trace methods covering:
- Account resolution (SOQL path): searching by website, contact domain
- Lookup field queries
- Owner lookups (contact, lead, account)
- Validation and skip scenarios
- GraphQL three-tier resolution
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat: wire CRM trace service through call chain
Pass crmTrace parameter through the CRM call chain:
- routerGetCrmContactOwnerEmail creates CrmRoutingTraceService
- Passes to app booking form handlers and CRM round robin skip
- CrmManager.getContacts accepts and forwards crmTrace
- All handlers accept optional crmTrace parameter
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat: add trace instrumentation to Salesforce CRM service
Instrument Salesforce CRM methods with routing trace steps:
- getContacts: trace owner lookups for contact/lead/account
- getAccountIdBasedOnEmailDomainOfContacts: trace website and domain searches
- findUserEmailFromLookupField: trace lookup field queries
- GetAccountRecordsForRRSkip (GraphQL): trace three-tier resolution
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* refactor: rename SalesforceRoutingTrace to SalesforceRoutingTraceService
Consistent naming with CrmRoutingTraceService.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* test: add unit tests for CrmRoutingTraceService and SalesforceRoutingTraceService
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* refactor: use AsyncLocalStorage for CRM routing trace
Replace explicit crmTrace parameter passing with AsyncLocalStorage:
- Add AsyncLocalStorage to RoutingTraceService with getCurrent() and runAsync()
- Update SalesforceRoutingTraceService to auto-resolve trace from AsyncLocalStorage
- Remove CrmRoutingTraceService wrapper (no longer needed)
- Remove crmTrace parameter from all CRM method signatures
- Wrap CRM operations in routingTraceService.runAsync() context
This is cleaner than threading crmTrace through 5+ function layers.
The trace context is available within the withReporting wrapper.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Use async local storage for `CrmRoutingTraceService`
* fix: correct template literal syntax in RoutingTraceService
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: use narrowed eventTypeId variable in nested async function
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: update SalesforceRoutingTraceService tests to use AsyncLocalStorage API
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* refactor: rename crmTrace to crmRoutingTraceService for consistency
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Volnei Munhoz <volnei@cal.com>
2026-01-28 17:28:35 -03:00
Joe Au-YeungGitHubClaude Opus 4.5joe@cal.com <j.auyeung419@gmail.com>joe@cal.com <j.auyeung419@gmail.com>joe@cal.com <j.auyeung419@gmail.com>Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* Add DB schema
* Init zod schema
* Init RoutingTrace and PendingRoutingTrace repository interfaces
* Create PrismaPendingRoutingTraceRepository
* Init RoutingTraceService
* Create RoutingTraceService container
* User routing trace service in routing
* Create RoutingTraceRepository and PrismaRoutingTraceRepoistory
* Add findByFormResponseId and findByQueuedFormResponseId to PendingRoutingTraceRepository
* Update DI containers
* RoutingTraceService create process booking method
* Use pending routing trace rather than URL params
* Fix schema
* Fix writing assignment reason for routed booking
* Remove from service
* Refactor RoutingTraceService to not rely on async local storage
* Pass RoutingTraceService through routing call
* Add attribute-logic-evaluated to routing trace step
* Add routing trace to trpc endpoint
* Add CRM routing trace step
* Fix extracting routing trace to assignment reason
* Add back CRM params to prevent refetching
* test: Add unit tests for RoutingTraceService and repositories
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* test: Add missing mock for RoutingTraceService in getRoutedUrl tests
Also fix pre-existing lint issues in the test file:
- Add explicit types to mockForm and mockSerializableForm variables
- Add explicit type to url parameter in mockContext
- Replace 'as any' with 'as unknown as InstanceType<typeof UserRepository>'
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* Link pending form submission to routing trace
* Clean up
* Add lookup field assignment reasons
* Rename to PendingRoutingTrace
* Add migration file
* fix: Update RoutingTraceService tests to use assignmentReasonRepository mock
- Add getStepsCount() method back to RoutingTraceService
- Add queuedFormResponseId support to processForBooking method
- Update tests to use mockAssignmentReasonRepository instead of prisma mock
- Remove test for missing routingTraceRepository (all deps now required)
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: Remove PII (organizer email) from log payload in RoutingTraceService
Addresses Cubic AI review feedback with confidence 9/10.
Logging PII violates sensitive information logging rules.
Co-Authored-By: unknown <>
* Write field values at the time of routing
* Write attributes used to route
* fix: Add CHECK constraint to ensure at least one response ID is set in routing trace tables
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: Address PR review comments for routing trace feature
- Add checkedFallback to routing trace step data (Comment 11)
- Extract hardcoded domain/step strings to constants (Comment 13)
- Use @default(now()) for createdAt in PendingRoutingTrace and RoutingTrace (Comment 15)
- Add DEFAULT CURRENT_TIMESTAMP to migration for createdAt fields
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: Use UTC-safe timezone expression for createdAt defaults in routing trace tables
Addresses Cubic AI review feedback (confidence 9/10) to prevent timezone
issues by using dbgenerated("timezone('UTC', now())") instead of bare now()
for the createdAt fields in PendingRoutingTrace and RoutingTrace models.
Co-Authored-By: unknown <>
* fix: Align migration SQL with Prisma's expected timezone syntax
Use 'UTC'::text cast in timezone() function to match Prisma's generated SQL.
Co-Authored-By: unknown <>
* fix: Revert migration SQL to match Prisma schema timezone syntax
Remove ::text cast from timezone() function to match what Prisma generates
from the schema definition.
Co-Authored-By: unknown <>
* fix: Use explicit ::text cast in timezone() for PostgreSQL compatibility
PostgreSQL normalizes timezone('UTC', now()) to timezone('UTC'::text, now())
internally. Update both schema and migration to use the explicit cast to
ensure they match and pass the migration check.
Co-Authored-By: unknown <>
* feat: Add RoutingFormTraceService domain class for type-safe routing traces
- Create RoutingFormTraceService class with routeMatched, fallbackRouteUsed,
attributeLogicEvaluated, and attributeFallbackUsed methods
- Add ROUTING_FORM_STEPS constants
- Add unit tests
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat: Integrate RoutingFormTraceService for routing form tracing
- Add routingFormTraceService parameter to findMatchingRoute
- Record route_matched or fallback_route_used step when route is selected
- Use "Default Route" as name for form-level fallback routes
- Refactor findTeamMembersMatchingAttributeLogic to use RoutingFormTraceService
- Update handleResponse to accept and pass routingFormTraceService
- Create RoutingFormTraceService in getRoutedUrl and pass through the flow
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* test: Add test coverage for RoutingFormTraceService integration
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* feat: Integrate RoutingFormTraceService for routing form tracing
- Add routingFormTraceService parameter to findMatchingRoute
- Record route_matched or fallback_route_used step when route is selected
- Use "Default Route" as name for form-level fallback routes
- Refactor findTeamMembersMatchingAttributeLogic to use RoutingFormTraceService
- Update handleResponse to accept and pass routingFormTraceService
- Create RoutingFormTraceService in getRoutedUrl and pass through the flow
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: Restore schema and migration from main branch
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: Use correct parameter name routingFormTraceService in tests
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: Use 'default_route' instead of 'Default Route' for fallback route name
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-28 17:15:22 -03:00
Joe Au-YeungGitHubunknown <>joe@cal.com <j.auyeung419@gmail.com>joe@cal.com <j.auyeung419@gmail.com>Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>Pedro Castro
* Add DB schema
* Init zod schema
* Init RoutingTrace and PendingRoutingTrace repository interfaces
* Create PrismaPendingRoutingTraceRepository
* Init RoutingTraceService
* Create RoutingTraceService container
* User routing trace service in routing
* Create RoutingTraceRepository and PrismaRoutingTraceRepoistory
* Add findByFormResponseId and findByQueuedFormResponseId to PendingRoutingTraceRepository
* Update DI containers
* RoutingTraceService create process booking method
* Use pending routing trace rather than URL params
* Fix schema
* Fix writing assignment reason for routed booking
* Remove from service
* Refactor RoutingTraceService to not rely on async local storage
* Pass RoutingTraceService through routing call
* Add attribute-logic-evaluated to routing trace step
* Add routing trace to trpc endpoint
* Add CRM routing trace step
* Fix extracting routing trace to assignment reason
* Add back CRM params to prevent refetching
* test: Add unit tests for RoutingTraceService and repositories
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* test: Add missing mock for RoutingTraceService in getRoutedUrl tests
Also fix pre-existing lint issues in the test file:
- Add explicit types to mockForm and mockSerializableForm variables
- Add explicit type to url parameter in mockContext
- Replace 'as any' with 'as unknown as InstanceType<typeof UserRepository>'
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* Link pending form submission to routing trace
* Clean up
* Add lookup field assignment reasons
* Rename to PendingRoutingTrace
* Add migration file
* fix: Update RoutingTraceService tests to use assignmentReasonRepository mock
- Add getStepsCount() method back to RoutingTraceService
- Add queuedFormResponseId support to processForBooking method
- Update tests to use mockAssignmentReasonRepository instead of prisma mock
- Remove test for missing routingTraceRepository (all deps now required)
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: Remove PII (organizer email) from log payload in RoutingTraceService
Addresses Cubic AI review feedback with confidence 9/10.
Logging PII violates sensitive information logging rules.
Co-Authored-By: unknown <>
* Write field values at the time of routing
* Write attributes used to route
* fix: Add CHECK constraint to ensure at least one response ID is set in routing trace tables
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: Address PR review comments for routing trace feature
- Add checkedFallback to routing trace step data (Comment 11)
- Extract hardcoded domain/step strings to constants (Comment 13)
- Use @default(now()) for createdAt in PendingRoutingTrace and RoutingTrace (Comment 15)
- Add DEFAULT CURRENT_TIMESTAMP to migration for createdAt fields
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: Use UTC-safe timezone expression for createdAt defaults in routing trace tables
Addresses Cubic AI review feedback (confidence 9/10) to prevent timezone
issues by using dbgenerated("timezone('UTC', now())") instead of bare now()
for the createdAt fields in PendingRoutingTrace and RoutingTrace models.
Co-Authored-By: unknown <>
* fix: Align migration SQL with Prisma's expected timezone syntax
Use 'UTC'::text cast in timezone() function to match Prisma's generated SQL.
Co-Authored-By: unknown <>
* fix: Revert migration SQL to match Prisma schema timezone syntax
Remove ::text cast from timezone() function to match what Prisma generates
from the schema definition.
Co-Authored-By: unknown <>
* fix: Use explicit ::text cast in timezone() for PostgreSQL compatibility
PostgreSQL normalizes timezone('UTC', now()) to timezone('UTC'::text, now())
internally. Update both schema and migration to use the explicit cast to
ensure they match and pass the migration check.
Co-Authored-By: unknown <>
* fix: Use CURRENT_TIMESTAMP for createdAt defaults (standard Cal.com pattern)
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: Use @default(now()) for createdAt in routing trace tables to match migration
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Pedro Castro <pedro@cal.com>
2026-01-28 12:45:15 -05:00
+3
Alex van AndelGitHubalex@cal.com <me@alexvanandel.com>alex@cal.com <me@alexvanandel.com>alex@cal.com <me@alexvanandel.com>alex@cal.com <me@alexvanandel.com>alex@cal.com <me@alexvanandel.com>alex@cal.com <me@alexvanandel.com>alex@cal.com <me@alexvanandel.com>alex@cal.com <me@alexvanandel.com>cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
* chore: Disables syncing of calendarList on overlay calendar fetch
* Unwrap ToggledConnectedCalendars
* Pick the right type for connectedCalendars
* further type amends
* Type fixes, tons of em
* Some further fixups consistent with what was before
* fix: resolve type incompatibility in getConnectedDestinationCalendars return type
Co-Authored-By: alex@cal.com <me@alexvanandel.com>
* fix: update DestinationCalendarProps to accept tRPC handler return type
Co-Authored-By: alex@cal.com <me@alexvanandel.com>
* fix: use generic type for DestinationCalendarProps to accept tRPC enriched types
Co-Authored-By: alex@cal.com <me@alexvanandel.com>
* fix: simplify DestinationCalendarProps type for better compatibility
Co-Authored-By: alex@cal.com <me@alexvanandel.com>
* fix: export ConnectedCalendar type for consistent type inference
Co-Authored-By: alex@cal.com <me@alexvanandel.com>
* fix: use permissive type for connectedCalendars to accept tRPC enriched types
Co-Authored-By: alex@cal.com <me@alexvanandel.com>
* fix: export ConnectedCalendar and ConnectedDestinationCalendars types from platform-libraries
Co-Authored-By: alex@cal.com <me@alexvanandel.com>
* fix: update ConnectedCalendar type to use boolean | null for primary field
Co-Authored-By: alex@cal.com <me@alexvanandel.com>
* Update ConnectedCalendarItem
* Undo some of Devins fixes, more fixes
* Fixup the destination calendar return type, historically not null
* Change init to undefined to deal with null
* Approach to connect selected calendars with the right credential
* This return type is used way too much everywhere, not refactoring
* Add the selectedCalendars to the type
* Actually fix overlay calendar
* set calendarsToLoad param as required
* Apply suggestion from @cubic-dev-ai[bot]
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
* Add new translation for 'Calendar Settings'
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
* fix: use FeatureOptInService for bookings-v3 check
* refactor: simplify FeatureOptInService API and fix bookings page
- Add resolveFeatureStates method that takes only userId and featureIds
- Update listFeaturesForUser to take only userId parameter
- Remove getUserOrgAndTeamIds from _router.ts (now internal to service)
- Fix bookings page to use new resolveFeatureStates method
- Parallelize getUserFeaturesStatus and resolveFeatureStates calls
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* check session?.user?.id first
* remove comment
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat: add invoice URL to proration and test seed scripts
- Add invoiceUrl field to MonthlyProration schema
- Update billing service to return hosted_invoice_url after finalizing
- Save invoice URL when creating proration invoices
- Add seed script for testing proration with real Stripe data
- Add cleanup script for test data removal
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* add invoice url
* add invoice URL to tests
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-28 09:42:57 +00:00
Keith WilliamsGitHubDevin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* wip flow
* add tests
* WIP migrateing view
* push back step
* fix tests and logic for adding new members to existing teams
* few UI fixes
* type fixes
* fix nits
* few UI + re-route fixes
* fix teamId when migrating
2026-01-26 17:03:12 +01:00
Eunjae LeeGitHubunknown <>Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: use createPortal for FeatureOptInBanner to avoid Intercom widget conflict
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* fix: guard portal rendering against non-browser environments
Add typeof document check before accessing document.body in createPortal
to prevent SSR/test crashes when document is undefined.
Co-Authored-By: unknown <>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-01-26 14:07:29 +00:00
Benny JooGitHubbenny@cal.com <sldisek783@gmail.com>benny@cal.com <sldisek783@gmail.com>Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* refactor: move booking-audit client components from packages/features to apps/web/modules
This is part of the larger effort to move tRPC-dependent UI components from packages/features to apps/web/modules to eliminate circular dependencies.
Changes:
- Move BookingHistory.tsx and BookingHistoryPage.tsx to apps/web/modules/booking-audit/components/
- Update imports in apps/web to use the new location
Co-Authored-By: benny@cal.com <sldisek783@gmail.com>
* refactor: move formbricks client from packages/features to apps/web/modules
Co-Authored-By: benny@cal.com <sldisek783@gmail.com>
* refactor: move hooks and stores from packages/features to apps/web/modules
- Move useAppsData hook from packages/features/apps/hooks to apps/web/modules/apps/hooks
- Move onboardingStore from packages/features/ee/organizations/lib to apps/web/modules/ee/organizations/lib
- Move useWelcomeModal hook from packages/features/ee/organizations/hooks to apps/web/modules/ee/organizations/hooks
- Move useAgentsData hook from packages/features/ee/workflows/hooks to apps/web/modules/ee/workflows/hooks
- Update all import paths in consuming files
Co-Authored-By: benny@cal.com <sldisek783@gmail.com>
* refactor: move onboardingStore test file to apps/web/modules
Co-Authored-By: benny@cal.com <sldisek783@gmail.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-01-26 13:11:14 +00:00
Benny JooGitHubbenny@cal.com <sldisek783@gmail.com>benny@cal.com <sldisek783@gmail.com>Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* chore: save progress
* chore:
* feat: add input dialog
* fix: type error
* feat: mass apply dialog
* test: per host location
* test: fix test
* fix: address Cubic AI review feedback (confidence >= 9/10)
- Remove PII (address, phone number) from tracing logs in RegularBookingService.ts
- Constrain HostLocation.type to EventLocationType["type"] for compile-time validation
Co-Authored-By: unknown <>
* fix: translation
* refactor: improvements
* fix: correct grammar in custom host locations tooltip
Change 'custom host locations is enabled' to 'custom host locations are enabled' (plural subject requires plural verb).
Addresses Cubic AI review feedback (confidence 9/10).
Co-Authored-By: unknown <>
* refactor: improvements
* fix: auth
* fix: check
* refactor: improvements
* fix: address Cubic AI review feedback (confidence >= 9/10)
- Add scheduleId to newly created hosts in update.handler.ts to persist
host-specific schedules during create operations
- Change host location deletion filter from !host.location to
host.location === null to only delete when explicitly set to null
- Fix static-link per-host locations to use actual link instead of type
in locationBodyString for bookingLocationService.ts
Co-Authored-By: unknown <>
* fix: preserve existing host scheduleId when not explicitly provided
Change scheduleId handling for existing hosts from 'host.scheduleId ?? null'
to 'host.scheduleId === undefined ? undefined : host.scheduleId' so that
when the client doesn't provide a scheduleId, the existing value is preserved
instead of being cleared to null.
Co-Authored-By: unknown <>
* fix; type erro
* fix; type erro
* fix; type erro
* refactor: move repository
* refactor: move repository
* fix: add singular/plural translations for location_applied_to_hosts
Addresses Cubic AI review feedback (confidence 9/10) to fix '1 hosts' rendering as '1 host' by using i18next plural format with _one and _other suffixes.
Co-Authored-By: unknown <>
* refactor: feedback
* fix: type err
* fix: use uuid in schema and remove attendee locaiton
* fix: type err
* fix: type err
* fix: validate eventTypeId as integer in massApplyHostLocation schema
Co-Authored-By: unknown <>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat: add seat tracking infrastructure for monthly proration
Add seat change logging infrastructure with operationId for idempotency.
This PR adds the foundation for monthly proration billing by tracking
seat additions and removals, gated behind the monthly-proration feature flag.
- Add operationId field to SeatChangeLog for idempotency
- Update SeatChangeLogRepository to support upsert with operationId
- Add feature flag guard in SeatChangeTrackingService
- Integrate seat tracking in team member invites
- Integrate seat tracking in bulk user deletions
- Integrate seat tracking in team service operations
- Integrate seat tracking in DSYNC user creation
When monthly-proration feature flag is disabled, seat logging is skipped
and behavior remains unchanged.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
* feat: add monthly proration processing
Add monthly proration billing processing that works on top of the seat
tracking infrastructure. This PR implements the core proration logic,
webhook handlers, and integration with Stripe billing.
- Enhance MonthlyProrationService to process seat change logs
- Add payment webhook handlers (invoice.payment_succeeded, invoice.payment_failed)
- Update subscription webhook to sync billing period on renewals
- Update TeamBillingService to skip real-time updates when proration enabled
- Enhance StripeBillingService with proration capabilities
- Add Tasker enhancements for processing queues
- Update team creation/upgrade routes
Depends on: feat/monthly-proration-seat-tracking
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
* fix: remove unused logger from SeatChangeTrackingService
* fix: description for calculation
* fix null check on trial
* feat: add scheduled trigger.dev task for monthly proration
* feat: add custom month key support and use batchTrigger
* feat: add isValidMonthKey and return result from batch task
* fix merge artifact
---------
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat: add graceful error handling for audit log enrichment
- Add hasError field to EnrichedAuditLog type
- Create buildFallbackAuditLog() method for failed enrichments
- Wrap enrichAuditLog() in try-catch to handle errors gracefully
- Add booking_audit_action.error_processing translation key
- Update BookingHistory.tsx to show warning icon for error logs
- Hide 'Show details' button for logs with hasError
- Add comprehensive test cases for error handling scenarios
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: include actionType in error_processing translation and allow expanded content for error logs
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
## What does this PR do?
Implements booking audit logging for round-robin reassignment events (both manual and automatic). This is part of the booking audit integration plan (PR 7).
Changes:
- Added audit logging to `roundRobinManualReassignment.ts` for manual host reassignments
- Added audit logging to `roundRobinReassignment.ts` for automatic round-robin reassignments
- Updated tRPC handlers to pass `actionSource: "WEBAPP"` and `reassignedByUuid`
- Updated API v2 bookings service to pass `actionSource: "API_V2"` and `reassignedByUuid`
- Updated `ReassignmentAuditActionService.ts` with proper field schemas and translation keys
The audit logging uses `BookingEventHandlerService.onReassignment()` with proper actor identification and action source tracking.
## Updates since last revision
Addressed review feedback:
- Renamed `title` field to `hostName` for semantic clarity (tracks host name changes, not booking titles)
- Fixed `assignedById` schema: changed from `NumberChangeSchema` to `z.number()` (no old/new pattern needed - it's always the user who performed the reassignment)
- Fixed `reassignmentReason` schema: changed from `StringChangeSchema` to `z.string().nullable()` (no old/new pattern needed)
- Added `ValidActionSource` type that excludes `UNKNOWN` - clients must pass explicit action sources
- Made `actionSource` required in both reassignment functions (no longer optional)
- Added integration tests for `ReassignmentAuditActionService` (15 tests covering all methods)
- Updated `roundRobinManualReassign.handler.ts` to pass required `actionSource` and `reassignedByUuid` params
**Latest fixes:**
- Fixed `hasAttendeeUpdated` check in `ReassignmentAuditActionService.ts`: changed from `!== null` to `!= null` to properly handle undefined values
- Updated test expectations in `ReassignmentAuditActionService.test.ts` to match the new display JSON field names (`hostAttendeeUserUuidNew`/`hostAttendeeUserUuidOld` instead of `newAssignedRRHostUuid`/`previousAssignedRRHostUuid`)
- Fixed async `getDisplayFields` tests to properly await the Promise and include the `previous_assignee` field
- Fixed `hasAttendeeUpdated` to check for `hostAttendeeUpdated` object presence instead of optional `id` field - host changes with only `withUserUuid` populated were being ignored (identified by Cubic AI, confidence 9/10)
- Fixed test expectation in `getDisplayJson` test: removed incorrect null expectations for `hostAttendeeIdUpdated`, `hostAttendeeUserUuidNew`, `hostAttendeeUserUuidOld` - the implementation uses conditional spreading to omit these fields when `hostAttendeeUpdated` is not present, rather than setting them to null
## Mandatory Tasks (DO NOT REMOVE)
- [x] I have self-reviewed the code (A decent size PR without self-review might be rejected).
- [x] I have updated the developer docs in /docs if this PR makes changes that would require a [documentation change](https://cal.com/docs). N/A - no documentation changes needed.
- [x] I confirm automated tests are in place that prove my fix is effective or that my feature works.
## How should this be tested?
1. Trigger a manual round-robin reassignment via the webapp and verify audit logs are created with `actionSource: "WEBAPP"`
2. Trigger an automatic round-robin reassignment and verify audit logs are created
3. Use API v2 to reassign a booking and verify audit logs are created with `actionSource: "API_V2"`
4. Verify the audit data contains correct values for `organizerUuid`, `hostAttendeeUpdated`, `reassignmentReason`, and `reassignmentType`
## Checklist
- [x] My code follows the style guidelines of this project
- [x] I have checked if my changes generate no new warnings
## Human Review Checklist
- [x] Verify the `hasAttendeeUpdated` fix is correct: now checks `fields.hostAttendeeUpdated != null` to detect any host attendee update regardless of whether `id` is populated
- [x] Verify `getDisplayJson` test fix: fields are correctly omitted (not set to null) when `hostAttendeeUpdated` is not present, matching the conditional spreading implementation
- [ ] Verify all callers of reassignment functions pass required `actionSource` and `reassignedByUuid`
- [ ] Confirm `getDisplayFields` is properly awaited in all call sites (it's now async)
- [ ] Check that `ValidActionSource` type properly excludes `UNKNOWN` for client-side validation
## Important Notes for Reviewer
1. **Dependency on base PR**: The translation key changes use the format from base PR (#26046). This PR should be merged after the base PR.
2. **Schema changes**: The `organizerUuid` and `hostAttendeeUpdated` fields track both organizer changes and round-robin host attendee changes separately for complete audit trail.
---
Link to Devin run: https://app.devin.ai/sessions/e4353e2ec6ea4a51ab33313bdc630aba
Requested by: @hariombalhara