* feat: include record IDs in Salesforce assignment reason strings - Add recordId parameter to assignmentReasonHandler function - Include Contact ID, Lead ID, and Account ID in assignment reason strings - Update entire call chain to pass record IDs from CRM service - Maintain backward compatibility with optional recordId parameter Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com> * fix: resolve lint warnings in assignment reason handler implementation - Change Record<string, any> to Record<string, unknown> in BookingHandlerInput type - Remove unused eventTypeId variable in getAttributeRoutingConfig function Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com> * fix: revert to Record<string, any> with ESLint disable for BookingHandlerInput - Revert from Record<string, unknown> to Record<string, any> to maintain type compatibility - Add ESLint disable comment to suppress no-explicit-any warning - Maintains consistency with handleNewRecurringBooking.ts pattern Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com> * feat: pass CRM record ID from booker state to handleNewBooking - Add crmRecordId field to booker store interface and initialization - Update mapBookingToMutationInput to include record ID from booker state - Modify handleNewBooking to extract record ID from bookingData parameter - Add crmRecordId to BookingCreateBody schema in Prisma layer - Follow existing pattern for CRM fields (teamMemberEmail, crmOwnerRecordType, crmAppSlug) - Ensures record ID flows: booker store → booking form → mapBookingToMutationInput → handleNewBooking This replaces the previous backend CRM service extraction approach with frontend booker state approach as requested by the user. Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com> * Pass crmRecordId as prop --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: Alex van Andel <me@alexvanandel.com>
73 lines
2.3 KiB
TypeScript
73 lines
2.3 KiB
TypeScript
import type { Prisma } from "@prisma/client";
|
|
import type { z } from "zod";
|
|
|
|
import CrmManager from "@calcom/lib/crmManager/crmManager";
|
|
import logger from "@calcom/lib/logger";
|
|
import { prisma } from "@calcom/prisma";
|
|
import type { EventTypeAppMetadataSchema } from "@calcom/prisma/zod-utils";
|
|
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;
|
|
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;
|
|
|
|
const crm = await getCRMManagerWithRRLeadSkip(parsedEventTypeMetadata.data.apps);
|
|
|
|
if (!crm) return nullReturnValue;
|
|
const { crmManager, crmAppSlug } = crm;
|
|
const startTime = performance.now();
|
|
const contact = await crmManager.getContacts({ emails: bookerEmail, forRoundRobinSkip: true });
|
|
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,
|
|
recordId: contact[0].id ?? null,
|
|
};
|
|
}
|
|
|
|
async function getCRMManagerWithRRLeadSkip(apps: z.infer<typeof EventTypeAppMetadataSchema>) {
|
|
let crmRoundRobinLeadSkip;
|
|
for (const appKey in apps) {
|
|
const app = apps[appKey as keyof typeof apps];
|
|
if (
|
|
app.enabled &&
|
|
typeof app.appCategories === "object" &&
|
|
app.appCategories.some((category: string) => category === "crm") &&
|
|
app.roundRobinLeadSkip
|
|
) {
|
|
crmRoundRobinLeadSkip = app;
|
|
break;
|
|
}
|
|
}
|
|
if (!crmRoundRobinLeadSkip) return;
|
|
const crmCredential = await prisma.credential.findUnique({
|
|
where: {
|
|
id: crmRoundRobinLeadSkip.credentialId,
|
|
},
|
|
include: {
|
|
user: {
|
|
select: {
|
|
email: true,
|
|
},
|
|
},
|
|
},
|
|
});
|
|
if (!crmCredential) return;
|
|
return {
|
|
crmManager: new CrmManager(crmCredential, crmRoundRobinLeadSkip),
|
|
crmAppSlug: crmCredential.appId,
|
|
};
|
|
}
|