Files
calendar/packages/app-store/salesforce/lib/assignmentReasonHandler.ts
T
Joe Au-YeungGitHubDevin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>Alex van Andel
3784421f02 feat: include record IDs in Salesforce assignment reason strings (#22561)
* 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>
2025-07-21 14:31:31 +00:00

100 lines
2.7 KiB
TypeScript

import { zodRoutes } from "@calcom/app-store/routing-forms/zod";
import prisma from "@calcom/prisma";
import { AssignmentReasonEnum } from "@calcom/prisma/enums";
import { RoutingReasons, SalesforceRecordEnum } from "./enums";
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}${
recordId ? ` (Contact ID: ${recordId})` : ""
}`,
};
case SalesforceRecordEnum.LEAD:
return {
...returnObject,
assignmentReason: `Salesforce lead owner: ${teamMemberEmail}${
recordId ? ` (Lead ID: ${recordId})` : ""
}`,
};
case SalesforceRecordEnum.ACCOUNT:
return {
...returnObject,
assignmentReason: `Salesforce account owner: ${teamMemberEmail}${
recordId ? ` (Account ID: ${recordId})` : ""
}`,
};
case RoutingReasons.ACCOUNT_LOOKUP_FIELD:
const assignmentReason = await handleAccountLookupFieldReason(
routingFormResponseId,
teamMemberEmail,
recordId
);
return { ...returnObject, assignmentReason };
}
}
async function handleAccountLookupFieldReason(
routingFormResponseId: number,
teamMemberEmail: string,
recordId?: string
) {
const routingFormResponse = await prisma.app_RoutingForms_FormResponse.findUnique({
where: {
id: routingFormResponseId,
},
select: {
chosenRouteId: true,
form: {
select: {
routes: true,
},
},
},
});
if (!routingFormResponse) return;
const { form } = routingFormResponse;
if (!form.routes) return;
const parsedRoutes = zodRoutes.safeParse(form.routes);
if (!parsedRoutes.success || !parsedRoutes.data) return;
const chosenRouteId = routingFormResponse.chosenRouteId;
const takenRoute = parsedRoutes.data.find((route) => route.id === chosenRouteId);
if (!takenRoute || !("attributeRoutingConfig" in takenRoute)) return;
const attributeRoutingConfig = takenRoute?.attributeRoutingConfig;
if (!attributeRoutingConfig) return;
const salesforceConfig = attributeRoutingConfig["salesforce"];
const accountLookupFieldName = salesforceConfig?.rrSKipToAccountLookupFieldName;
return accountLookupFieldName
? `Salesforce account lookup field: ${accountLookupFieldName} - ${teamMemberEmail}${
recordId ? ` (Account ID: ${recordId})` : ""
}`
: undefined;
}