diff --git a/apps/web/app/(booking-page-wrapper)/team/[slug]/[type]/queries.ts b/apps/web/app/(booking-page-wrapper)/team/[slug]/[type]/queries.ts index 8eb16223e6..ce6ac0701c 100644 --- a/apps/web/app/(booking-page-wrapper)/team/[slug]/[type]/queries.ts +++ b/apps/web/app/(booking-page-wrapper)/team/[slug]/[type]/queries.ts @@ -122,12 +122,14 @@ export async function getCRMData( const crmContactOwnerEmail = query["cal.crmContactOwnerEmail"]; const crmContactOwnerRecordType = query["cal.crmContactOwnerRecordType"]; const crmAppSlugParam = query["cal.crmAppSlug"]; + const crmRecordIdParam = query["cal.crmRecordId"]; let teamMemberEmail = Array.isArray(crmContactOwnerEmail) ? crmContactOwnerEmail[0] : crmContactOwnerEmail; let crmOwnerRecordType = Array.isArray(crmContactOwnerRecordType) ? crmContactOwnerRecordType[0] : crmContactOwnerRecordType; let crmAppSlug = Array.isArray(crmAppSlugParam) ? crmAppSlugParam[0] : crmAppSlugParam; + let crmRecordId = Array.isArray(crmRecordIdParam) ? crmRecordIdParam[0] : crmRecordIdParam; if (!teamMemberEmail || !crmOwnerRecordType || !crmAppSlug) { const { getTeamMemberEmailForResponseOrContactUsingUrlQuery } = await import( @@ -137,6 +139,7 @@ export async function getCRMData( email, recordType, crmAppSlug: crmAppSlugQuery, + recordId: crmRecordIdQuery, } = await getTeamMemberEmailForResponseOrContactUsingUrlQuery({ query, eventData, @@ -145,11 +148,13 @@ export async function getCRMData( teamMemberEmail = email ?? undefined; crmOwnerRecordType = recordType ?? undefined; crmAppSlug = crmAppSlugQuery ?? undefined; + crmRecordId = crmRecordIdQuery ?? undefined; } return { teamMemberEmail, crmOwnerRecordType, crmAppSlug, + crmRecordId, }; } diff --git a/apps/web/lib/__tests__/getTeamMemberEmailFromCrm.test.ts b/apps/web/lib/__tests__/getTeamMemberEmailFromCrm.test.ts index ed6f82307f..5f73c23bd0 100644 --- a/apps/web/lib/__tests__/getTeamMemberEmailFromCrm.test.ts +++ b/apps/web/lib/__tests__/getTeamMemberEmailFromCrm.test.ts @@ -29,9 +29,9 @@ function mockGetCRMContactOwnerForRRLeadSkip({ }) { vi.mocked(getCRMContactOwnerForRRLeadSkip).mockImplementation((_bookerEmail, _eventMetadata) => { if (_bookerEmail === bookerEmail) { - return Promise.resolve({ email: teamMemberEmail, recordType: null, crmAppSlug: null }); + return Promise.resolve({ email: teamMemberEmail, recordType: null, crmAppSlug: null, recordId: null }); } - return Promise.resolve({ email: null, recordType: null, crmAppSlug: null }); + return Promise.resolve({ email: null, recordType: null, crmAppSlug: null, recordId: null }); }); } @@ -45,9 +45,9 @@ function mockBookingFormHandler({ vi.mocked(bookingFormHandlers.salesforce).mockImplementation( (_bookerEmail, _attributeRoutingConfig, _eventTypeId) => { if (_bookerEmail === bookerEmail) { - return Promise.resolve({ email: teamMemberEmail, recordType: null }); + return Promise.resolve({ email: teamMemberEmail, recordType: null, recordId: null }); } - return Promise.resolve({ email: null, recordType: null }); + return Promise.resolve({ email: null, recordType: null, recordId: null }); } ); } diff --git a/apps/web/lib/team/[slug]/[type]/getServerSideProps.tsx b/apps/web/lib/team/[slug]/[type]/getServerSideProps.tsx index 5a7a37ace9..689a3a1c58 100644 --- a/apps/web/lib/team/[slug]/[type]/getServerSideProps.tsx +++ b/apps/web/lib/team/[slug]/[type]/getServerSideProps.tsx @@ -92,6 +92,7 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) => const crmContactOwnerEmail = query["cal.crmContactOwnerEmail"]; const crmContactOwnerRecordType = query["cal.crmContactOwnerRecordType"]; const crmAppSlugParam = query["cal.crmAppSlug"]; + const crmRecordIdParam = query["cal.crmRecordId"]; // Handle string[] type from query params let teamMemberEmail = Array.isArray(crmContactOwnerEmail) ? crmContactOwnerEmail[0] : crmContactOwnerEmail; @@ -101,6 +102,7 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) => : crmContactOwnerRecordType; let crmAppSlug = Array.isArray(crmAppSlugParam) ? crmAppSlugParam[0] : crmAppSlugParam; + let crmRecordId = Array.isArray(crmRecordIdParam) ? crmRecordIdParam[0] : crmRecordIdParam; if (!teamMemberEmail || !crmOwnerRecordType || !crmAppSlug) { const { getTeamMemberEmailForResponseOrContactUsingUrlQuery } = await import( @@ -110,6 +112,7 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) => email, recordType, crmAppSlug: crmAppSlugQuery, + recordId, } = await getTeamMemberEmailForResponseOrContactUsingUrlQuery({ query, eventData, @@ -118,6 +121,7 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) => teamMemberEmail = email ?? undefined; crmOwnerRecordType = recordType ?? undefined; crmAppSlug = crmAppSlugQuery ?? undefined; + crmRecordId = recordId ?? undefined; } const organizationSettings = getOrganizationSEOSettings(team); @@ -167,6 +171,7 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) => teamMemberEmail, crmOwnerRecordType, crmAppSlug, + crmRecordId, isSEOIndexable: allowSEOIndexing, }, }; diff --git a/apps/web/modules/team/type-view.tsx b/apps/web/modules/team/type-view.tsx index 2aaf43faef..88e6e25988 100644 --- a/apps/web/modules/team/type-view.tsx +++ b/apps/web/modules/team/type-view.tsx @@ -34,6 +34,7 @@ function Type({ teamMemberEmail, crmOwnerRecordType, crmAppSlug, + crmRecordId, isEmbed, useApiV2, }: PageProps) { @@ -64,6 +65,7 @@ function Type({ teamMemberEmail={teamMemberEmail} crmOwnerRecordType={crmOwnerRecordType} crmAppSlug={crmAppSlug} + crmRecordId={crmRecordId} /> diff --git a/packages/app-store/_utils/CRMRoundRobinSkip.ts b/packages/app-store/_utils/CRMRoundRobinSkip.ts index 3a77ee025f..7209b52873 100644 --- a/packages/app-store/_utils/CRMRoundRobinSkip.ts +++ b/packages/app-store/_utils/CRMRoundRobinSkip.ts @@ -10,8 +10,13 @@ import { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils"; export async function getCRMContactOwnerForRRLeadSkip( bookerEmail: string, eventTypeMetadata: Prisma.JsonValue -): Promise<{ email: string | null; recordType: string | null; crmAppSlug: string | null }> { - const nullReturnValue = { email: null, recordType: null, crmAppSlug: "" }; +): Promise<{ + email: string | null; + recordType: string | null; + crmAppSlug: string | null; + recordId: string | null; +}> { + const nullReturnValue = { email: null, recordType: null, crmAppSlug: "", recordId: null }; const parsedEventTypeMetadata = EventTypeMetaDataSchema.safeParse(eventTypeMetadata); if (!parsedEventTypeMetadata.success || !parsedEventTypeMetadata.data?.apps) return nullReturnValue; @@ -24,7 +29,12 @@ export async function getCRMContactOwnerForRRLeadSkip( const endTime = performance.now(); logger.info(`Fetching from CRM took ${endTime - startTime}ms`); if (!contact?.length || !contact[0].ownerEmail) return nullReturnValue; - return { email: contact[0].ownerEmail ?? null, recordType: contact[0].recordType ?? null, crmAppSlug }; + return { + email: contact[0].ownerEmail ?? null, + recordType: contact[0].recordType ?? null, + crmAppSlug, + recordId: contact[0].id ?? null, + }; } async function getCRMManagerWithRRLeadSkip(apps: z.infer) { diff --git a/packages/app-store/routing-forms/appBookingFormHandler.ts b/packages/app-store/routing-forms/appBookingFormHandler.ts index 0193f2226b..13fbe93722 100644 --- a/packages/app-store/routing-forms/appBookingFormHandler.ts +++ b/packages/app-store/routing-forms/appBookingFormHandler.ts @@ -5,7 +5,7 @@ type AppBookingFormHandler = ( attendeeEmail: string, attributeRoutingConfig: AttributeRoutingConfig, eventTypeId: number -) => Promise<{ email: string | null; recordType: string | null }>; +) => Promise<{ email: string | null; recordType: string | null; recordId: string | null }>; const appBookingFormHandler: Record = { salesforce: routingFormBookingFormHandler, diff --git a/packages/app-store/routing-forms/lib/crmRouting/routerGetCrmContactOwnerEmail.ts b/packages/app-store/routing-forms/lib/crmRouting/routerGetCrmContactOwnerEmail.ts index dea8504f99..44a806a79c 100644 --- a/packages/app-store/routing-forms/lib/crmRouting/routerGetCrmContactOwnerEmail.ts +++ b/packages/app-store/routing-forms/lib/crmRouting/routerGetCrmContactOwnerEmail.ts @@ -40,10 +40,16 @@ export default async function routerGetCrmContactOwnerEmail({ const eventTypeMetadata = eventType.metadata; if (!eventTypeMetadata) return null; - let contactOwner: { email: string | null; recordType: string | null; crmAppSlug: string | null } = { + let contactOwner: { + email: string | null; + recordType: string | null; + crmAppSlug: string | null; + recordId: string | null; + } = { email: null, recordType: null, crmAppSlug: null, + recordId: null, }; // Determine if there is a CRM option enabled in the chosen route for (const appSlug of enabledAppSlugs) { diff --git a/packages/app-store/routing-forms/lib/handleResponse.test.ts b/packages/app-store/routing-forms/lib/handleResponse.test.ts index 0413d8f0a2..de80831b79 100644 --- a/packages/app-store/routing-forms/lib/handleResponse.test.ts +++ b/packages/app-store/routing-forms/lib/handleResponse.test.ts @@ -267,6 +267,7 @@ describe("handleResponse", () => { email: "owner@example.com", recordType: "contact", crmAppSlug: "hubspot", + recordId: "123", }); vi.mocked(findTeamMembersMatchingAttributeLogic).mockResolvedValue({ teamMembersMatchingAttributeLogic: [{ userId: 123, result: "MATCH" as any }], diff --git a/packages/app-store/routing-forms/lib/handleResponse.ts b/packages/app-store/routing-forms/lib/handleResponse.ts index 5db85ae45b..06d89dbee9 100644 --- a/packages/app-store/routing-forms/lib/handleResponse.ts +++ b/packages/app-store/routing-forms/lib/handleResponse.ts @@ -96,6 +96,7 @@ const _handleResponse = async ({ let crmContactOwnerEmail: string | null = null; let crmContactOwnerRecordType: string | null = null; let crmAppSlug: string | null = null; + let crmRecordId: string | null = null; let timeTaken: Record = {}; if (chosenRoute) { if (isRouter(chosenRoute)) { @@ -119,6 +120,7 @@ const _handleResponse = async ({ crmContactOwnerEmail = contactOwnerQuery?.email ?? null; crmContactOwnerRecordType = contactOwnerQuery?.recordType ?? null; crmAppSlug = contactOwnerQuery?.crmAppSlug ?? null; + crmRecordId = contactOwnerQuery?.recordId ?? null; })(), (async () => { const teamMembersMatchingAttributeLogicWithResult = @@ -212,6 +214,7 @@ const _handleResponse = async ({ crmContactOwnerEmail, crmContactOwnerRecordType, crmAppSlug, + crmRecordId, attributeRoutingConfig: chosenRoute ? "attributeRoutingConfig" in chosenRoute ? chosenRoute.attributeRoutingConfig diff --git a/packages/app-store/salesforce/lib/assignmentReasonHandler.ts b/packages/app-store/salesforce/lib/assignmentReasonHandler.ts index 50b6c8846a..100cded1f7 100644 --- a/packages/app-store/salesforce/lib/assignmentReasonHandler.ts +++ b/packages/app-store/salesforce/lib/assignmentReasonHandler.ts @@ -8,27 +8,52 @@ export async function assignmentReasonHandler({ recordType, teamMemberEmail, routingFormResponseId, + recordId, }: { recordType: string; teamMemberEmail: string; routingFormResponseId: number; + recordId?: string; }) { const returnObject = { reasonEnum: AssignmentReasonEnum.SALESFORCE_ASSIGNMENT }; switch (recordType) { case SalesforceRecordEnum.CONTACT: - return { ...returnObject, assignmentReason: `Salesforce contact owner: ${teamMemberEmail}` }; + return { + ...returnObject, + assignmentReason: `Salesforce contact owner: ${teamMemberEmail}${ + recordId ? ` (Contact ID: ${recordId})` : "" + }`, + }; case SalesforceRecordEnum.LEAD: - return { ...returnObject, assignmentReason: `Salesforce lead owner: ${teamMemberEmail}` }; + return { + ...returnObject, + assignmentReason: `Salesforce lead owner: ${teamMemberEmail}${ + recordId ? ` (Lead ID: ${recordId})` : "" + }`, + }; case SalesforceRecordEnum.ACCOUNT: - return { ...returnObject, assignmentReason: `Salesforce account owner: ${teamMemberEmail}` }; + return { + ...returnObject, + assignmentReason: `Salesforce account owner: ${teamMemberEmail}${ + recordId ? ` (Account ID: ${recordId})` : "" + }`, + }; case RoutingReasons.ACCOUNT_LOOKUP_FIELD: - const assignmentReason = await handleAccountLookupFieldReason(routingFormResponseId, teamMemberEmail); + const assignmentReason = await handleAccountLookupFieldReason( + routingFormResponseId, + teamMemberEmail, + recordId + ); return { ...returnObject, assignmentReason }; } } -async function handleAccountLookupFieldReason(routingFormResponseId: number, teamMemberEmail: string) { +async function handleAccountLookupFieldReason( + routingFormResponseId: number, + teamMemberEmail: string, + recordId?: string +) { const routingFormResponse = await prisma.app_RoutingForms_FormResponse.findUnique({ where: { id: routingFormResponseId, @@ -67,6 +92,8 @@ async function handleAccountLookupFieldReason(routingFormResponseId: number, tea const accountLookupFieldName = salesforceConfig?.rrSKipToAccountLookupFieldName; return accountLookupFieldName - ? `Salesforce account lookup field: ${accountLookupFieldName} - ${teamMemberEmail}` + ? `Salesforce account lookup field: ${accountLookupFieldName} - ${teamMemberEmail}${ + recordId ? ` (Account ID: ${recordId})` : "" + }` : undefined; } diff --git a/packages/app-store/salesforce/lib/routingFormBookingFormHandler.ts b/packages/app-store/salesforce/lib/routingFormBookingFormHandler.ts index 805cdeffb2..a475f3b9ad 100644 --- a/packages/app-store/salesforce/lib/routingFormBookingFormHandler.ts +++ b/packages/app-store/salesforce/lib/routingFormBookingFormHandler.ts @@ -16,7 +16,7 @@ const routingFormBookingFormHandler = async ( !salesforceSettings.rrSkipToAccountLookupField || !salesforceSettings.rrSKipToAccountLookupFieldName ) - return { email: null, recordType: null }; + return { email: null, recordType: null, recordId: null }; const appData = await EventTypeService.getEventTypeAppDataFromId(eventTypeId, "salesforce"); @@ -24,7 +24,7 @@ const routingFormBookingFormHandler = async ( const credential = await CredentialRepository.findFirstByIdWithKeyAndUser({ id: credentialId }); - if (!credential) return { email: null, recordType: null }; + if (!credential) return { email: null, recordType: null, recordId: null }; const crm = new SalesforceCRMService(credential, {}); @@ -34,7 +34,11 @@ const routingFormBookingFormHandler = async ( SalesforceRecordEnum.ACCOUNT ); - return { email: userLookupEmail?.email ?? null, recordType: RoutingReasons.ACCOUNT_LOOKUP_FIELD as string }; + return { + email: userLookupEmail?.email ?? null, + recordType: RoutingReasons.ACCOUNT_LOOKUP_FIELD as string, + recordId: null, + }; }; export default routingFormBookingFormHandler; diff --git a/packages/features/bookings/Booker/store.ts b/packages/features/bookings/Booker/store.ts index 9bc1603ce2..3f7409caa1 100644 --- a/packages/features/bookings/Booker/store.ts +++ b/packages/features/bookings/Booker/store.ts @@ -36,6 +36,7 @@ type StoreInitializeType = { teamMemberEmail?: string | null; crmOwnerRecordType?: string | null; crmAppSlug?: string | null; + crmRecordId?: string | null; isPlatform?: boolean; allowUpdatingUrlParams?: boolean; }; @@ -163,6 +164,7 @@ export type BookerStore = { teamMemberEmail?: string | null; crmOwnerRecordType?: string | null; crmAppSlug?: string | null; + crmRecordId?: string | null; isPlatform?: boolean; allowUpdatingUrlParams?: boolean; }; @@ -306,6 +308,7 @@ export const useBookerStore = createWithEqualityFn((set, get) => ({ teamMemberEmail, crmOwnerRecordType, crmAppSlug, + crmRecordId, isPlatform = false, allowUpdatingUrlParams = true, }: StoreInitializeType) => { @@ -324,7 +327,8 @@ export const useBookerStore = createWithEqualityFn((set, get) => ({ get().rescheduledBy === rescheduledBy && get().teamMemberEmail === teamMemberEmail && get().crmOwnerRecordType === crmOwnerRecordType && - get().crmAppSlug + get().crmAppSlug === crmAppSlug && + get().crmRecordId === crmRecordId ) return; set({ @@ -347,6 +351,7 @@ export const useBookerStore = createWithEqualityFn((set, get) => ({ teamMemberEmail, crmOwnerRecordType, crmAppSlug, + crmRecordId, isPlatform, allowUpdatingUrlParams, }); @@ -456,6 +461,7 @@ export const useInitializeBookerStore = ({ teamMemberEmail, crmOwnerRecordType, crmAppSlug, + crmRecordId, isPlatform = false, allowUpdatingUrlParams = true, }: StoreInitializeType) => { @@ -479,6 +485,7 @@ export const useInitializeBookerStore = ({ teamMemberEmail, crmOwnerRecordType, crmAppSlug, + crmRecordId, isPlatform, allowUpdatingUrlParams, }); @@ -501,6 +508,7 @@ export const useInitializeBookerStore = ({ teamMemberEmail, crmOwnerRecordType, crmAppSlug, + crmRecordId, isPlatform, allowUpdatingUrlParams, ]); diff --git a/packages/features/bookings/Booker/types.ts b/packages/features/bookings/Booker/types.ts index a804e762d7..bd02590159 100644 --- a/packages/features/bookings/Booker/types.ts +++ b/packages/features/bookings/Booker/types.ts @@ -100,6 +100,7 @@ export interface BookerProps { teamMemberEmail?: string | null; crmOwnerRecordType?: string | null; crmAppSlug?: string | null; + crmRecordId?: string | null; areInstantMeetingParametersSet?: boolean | null; userLocale?: string | null; hasValidLicense?: boolean; diff --git a/packages/features/bookings/lib/book-event-form/booking-to-mutation-input-mapper.tsx b/packages/features/bookings/lib/book-event-form/booking-to-mutation-input-mapper.tsx index 03306bb962..158c4576a3 100644 --- a/packages/features/bookings/lib/book-event-form/booking-to-mutation-input-mapper.tsx +++ b/packages/features/bookings/lib/book-event-form/booking-to-mutation-input-mapper.tsx @@ -27,6 +27,7 @@ export type BookingOptions = { teamMemberEmail?: string | null; crmOwnerRecordType?: string | null; crmAppSlug?: string | null; + crmRecordId?: string | null; orgSlug?: string; routingFormSearchParams?: RoutingFormSearchParams; isDryRunProp?: boolean; @@ -49,6 +50,7 @@ export const mapBookingToMutationInput = ({ teamMemberEmail, crmOwnerRecordType, crmAppSlug, + crmRecordId, orgSlug, routingFormSearchParams, isDryRunProp, @@ -86,6 +88,7 @@ export const mapBookingToMutationInput = ({ teamMemberEmail, crmOwnerRecordType, crmAppSlug, + crmRecordId, orgSlug, routedTeamMemberIds, routingFormResponseId, diff --git a/packages/features/bookings/lib/handleNewBooking.ts b/packages/features/bookings/lib/handleNewBooking.ts index fa1b0cbc52..203841180a 100644 --- a/packages/features/bookings/lib/handleNewBooking.ts +++ b/packages/features/bookings/lib/handleNewBooking.ts @@ -378,6 +378,7 @@ export type PlatformParams = { }; export type BookingHandlerInput = { + // eslint-disable-next-line @typescript-eslint/no-explicit-any bookingData: Record; userId?: number; // These used to come from headers but now we're passing them as params @@ -597,6 +598,7 @@ async function handler( }); const contactOwnerEmail = skipContactOwner ? null : contactOwnerFromReq; + const crmRecordId: string | undefined = reqBody.crmRecordId ?? undefined; let routingFormResponse = null; @@ -1397,6 +1399,7 @@ async function handler( teamMemberEmail: contactOwnerEmail, recordType: reqBody.crmOwnerRecordType, routingFormResponseId, + recordId: crmRecordId, }); } else if (routingFormResponseId && teamId) { assignmentReason = await AssignmentReasonRecorder.routingFormRoute({ diff --git a/packages/features/ee/round-robin/assignmentReason/AssignmentReasonRecorder.ts b/packages/features/ee/round-robin/assignmentReason/AssignmentReasonRecorder.ts index 710f28aad7..82bfc947b8 100644 --- a/packages/features/ee/round-robin/assignmentReason/AssignmentReasonRecorder.ts +++ b/packages/features/ee/round-robin/assignmentReason/AssignmentReasonRecorder.ts @@ -149,18 +149,25 @@ export default class AssignmentReasonRecorder { teamMemberEmail, recordType, routingFormResponseId, + recordId, }: { bookingId: number; crmAppSlug: string; teamMemberEmail: string; recordType: string; routingFormResponseId: number; + recordId?: string; }) { const appAssignmentReasonHandler = (await import("./appAssignmentReasonHandler")).default; const appHandler = appAssignmentReasonHandler[crmAppSlug]; if (!appHandler) return; - const crmRoutingReason = await appHandler({ recordType, teamMemberEmail, routingFormResponseId }); + const crmRoutingReason = await appHandler({ + recordType, + teamMemberEmail, + routingFormResponseId, + recordId, + }); if (!crmRoutingReason || !crmRoutingReason.assignmentReason) return; diff --git a/packages/features/ee/round-robin/assignmentReason/appAssignmentReasonHandler.ts b/packages/features/ee/round-robin/assignmentReason/appAssignmentReasonHandler.ts index 39932c0ed4..d5fa09a331 100644 --- a/packages/features/ee/round-robin/assignmentReason/appAssignmentReasonHandler.ts +++ b/packages/features/ee/round-robin/assignmentReason/appAssignmentReasonHandler.ts @@ -5,10 +5,12 @@ type AppAssignmentReasonHandler = ({ recordType, teamMemberEmail, routingFormResponseId, + recordId, }: { recordType: string; teamMemberEmail: string; routingFormResponseId: number; + recordId?: string; }) => Promise<{ assignmentReason: string | undefined; reasonEnum: AssignmentReasonEnum } | undefined>; const appBookingFormHandler: Record = { diff --git a/packages/lib/server/getTeamMemberEmailFromCrm.ts b/packages/lib/server/getTeamMemberEmailFromCrm.ts index 9749e94406..f10c7beb87 100644 --- a/packages/lib/server/getTeamMemberEmailFromCrm.ts +++ b/packages/lib/server/getTeamMemberEmailFromCrm.ts @@ -21,7 +21,7 @@ interface EventData { length: number; } -const returnNullValue = { email: null, recordType: null, crmAppSlug: null }; +const returnNullValue = { email: null, recordType: null, crmAppSlug: null, recordId: null }; async function findUserByEmailWhoIsAHostOfEventType({ email, @@ -57,7 +57,7 @@ async function getAttributeRoutingConfig( if ("route" in data) { return data.route.attributeRoutingConfig ?? null; } - const { routingFormResponseId, eventTypeId } = data; + const { routingFormResponseId } = data; const routingFormResponseQuery = await prisma.app_RoutingForms_FormResponse.findUnique({ where: { @@ -117,7 +117,12 @@ function getEnabledRoutingFormAppSlugFromQuery(query: ParsedUrlQuery) { async function getOwnerEmailFromCrm( eventData: EventData, email: string -): Promise<{ email: string | null; recordType: string | null; crmAppSlug: string | null }> { +): Promise<{ + email: string | null; + recordType: string | null; + crmAppSlug: string | null; + recordId: string | null; +}> { const crmContactOwner = await getCRMContactOwnerForRRLeadSkip(email, eventData.metadata); if (!crmContactOwner?.email) return returnNullValue; @@ -159,7 +164,7 @@ async function getTeamMemberEmailUsingRoutingFormHandler({ attributeRoutingConfig: AttributeRoutingConfig | null; crmAppSlug: string; }) { - const nullReturnValue = { email: null, skipContactOwner: false, recordType: "" }; + const nullReturnValue = { email: null, skipContactOwner: false, recordType: "", recordId: "" }; if (!attributeRoutingConfig) return nullReturnValue; @@ -171,7 +176,11 @@ async function getTeamMemberEmailUsingRoutingFormHandler({ const appHandler = appBookingFormHandler[crmAppSlug]; if (!appHandler) return nullReturnValue; - const { email: userEmail, recordType } = await appHandler(bookerEmail, attributeRoutingConfig, eventTypeId); + const { + email: userEmail, + recordType, + recordId, + } = await appHandler(bookerEmail, attributeRoutingConfig, eventTypeId); if (!userEmail) return nullReturnValue; @@ -180,7 +189,7 @@ async function getTeamMemberEmailUsingRoutingFormHandler({ if (!userQuery) return nullReturnValue; - return { ...nullReturnValue, email: userEmail, recordType }; + return { ...nullReturnValue, email: userEmail, recordType, recordId }; } async function getTeamMemberEmailForResponseOrContact({ @@ -198,7 +207,12 @@ async function getTeamMemberEmailForResponseOrContact({ */ chosenRoute?: LocalRoute; crmAppSlug?: string; -}) { +}): Promise<{ + email: string | null; + recordType: string | null; + crmAppSlug: string | null; + recordId: string | null; +}> { const eventTypeId = eventData.id; if (eventData.schedulingType !== SchedulingType.ROUND_ROBIN) return returnNullValue; @@ -215,15 +229,17 @@ async function getTeamMemberEmailForResponseOrContact({ safeStringify({ attributeRoutingConfigGetterData, crmAppSlug }) ); const attributeRoutingConfig = await getAttributeRoutingConfig(attributeRoutingConfigGetterData); - const { email, skipContactOwner, recordType } = await getTeamMemberEmailUsingRoutingFormHandler({ - bookerEmail, - eventTypeId, - attributeRoutingConfig, - crmAppSlug, - }); + const { email, skipContactOwner, recordType, recordId } = await getTeamMemberEmailUsingRoutingFormHandler( + { + bookerEmail, + eventTypeId, + attributeRoutingConfig, + crmAppSlug, + } + ); if (skipContactOwner) return returnNullValue; - if (email) return { email, recordType, crmAppSlug }; + if (email) return { email, recordType, crmAppSlug, recordId }; } else { log.debug("Getting the contact owner email from CRM"); return await getOwnerEmailFromCrm(eventData, bookerEmail); @@ -240,7 +256,12 @@ export async function getTeamMemberEmailForResponseOrContactUsingUrlQuery({ query: ParsedUrlQuery; eventData: EventData; chosenRoute?: LocalRoute; -}) { +}): Promise<{ + email: string | null; + recordType: string | null; + crmAppSlug: string | null; + recordId: string | null; +}> { // Without email no lookup is possible if (!query.email || typeof query.email !== "string") { return returnNullValue; diff --git a/packages/platform/atoms/booker/BookerPlatformWrapper.tsx b/packages/platform/atoms/booker/BookerPlatformWrapper.tsx index bf8f32e884..fdbf13a521 100644 --- a/packages/platform/atoms/booker/BookerPlatformWrapper.tsx +++ b/packages/platform/atoms/booker/BookerPlatformWrapper.tsx @@ -158,6 +158,7 @@ export const BookerPlatformWrapper = ( teamMemberEmail, crmAppSlug, crmOwnerRecordType, + crmRecordId: props.crmRecordId, eventId: event?.data?.id, rescheduleUid: props.rescheduleUid ?? null, bookingUid: props.bookingUid ?? null, diff --git a/packages/platform/atoms/booker/BookerWebWrapper.tsx b/packages/platform/atoms/booker/BookerWebWrapper.tsx index 4f7f3fd4b5..64c1e964d5 100644 --- a/packages/platform/atoms/booker/BookerWebWrapper.tsx +++ b/packages/platform/atoms/booker/BookerWebWrapper.tsx @@ -64,7 +64,6 @@ export const BookerWebWrapper = (props: BookerWebWrapperAtomProps) => { // This event isn't processed by BookingPageTagManager because BookingPageTagManager hasn't loaded when it is fired. I think we should have a queue in fire method to handle this. sdkActionManager?.fire("navigatedToBooker", {}); }, []); - useInitializeBookerStore({ ...props, eventId: props.entity.eventTypeId ?? event?.data?.id, diff --git a/packages/platform/atoms/hooks/bookings/useHandleBookEvent.ts b/packages/platform/atoms/hooks/bookings/useHandleBookEvent.ts index 2f0af2fcd1..4ee8b59728 100644 --- a/packages/platform/atoms/hooks/bookings/useHandleBookEvent.ts +++ b/packages/platform/atoms/hooks/bookings/useHandleBookEvent.ts @@ -62,6 +62,7 @@ export const useHandleBookEvent = ({ const teamMemberEmail = useBookerStore((state) => state.teamMemberEmail); const crmOwnerRecordType = useBookerStore((state) => state.crmOwnerRecordType); const crmAppSlug = useBookerStore((state) => state.crmAppSlug); + const crmRecordId = useBookerStore((state) => state.crmRecordId); const handleError = (err: any) => { const errorMessage = err?.message ? t(err.message) : t("can_you_try_again"); showToast(errorMessage, "error"); @@ -108,6 +109,7 @@ export const useHandleBookEvent = ({ teamMemberEmail, crmOwnerRecordType, crmAppSlug, + crmRecordId, orgSlug: orgSlug ? orgSlug : undefined, routingFormSearchParams, isDryRunProp: isBookingDryRun, diff --git a/packages/prisma/zod/custom/booking.ts b/packages/prisma/zod/custom/booking.ts index 9239ec14e8..36484405f3 100644 --- a/packages/prisma/zod/custom/booking.ts +++ b/packages/prisma/zod/custom/booking.ts @@ -23,6 +23,7 @@ export const bookingCreateBodySchema = z.object({ orgSlug: z.string().optional(), teamMemberEmail: z.string().nullish(), crmOwnerRecordType: z.string().nullish(), + crmRecordId: z.string().nullish(), routedTeamMemberIds: z.array(z.number()).nullish(), routingFormResponseId: z.number().optional(), skipContactOwner: z.boolean().optional(),