feat: Salesforce - pass company booking response to the Lead field (#17860)

* Catch undefined app option properties

* Add `getCompanyNameFromBookingResponse` method

* Pass bookingResponse when Lead is being created

* Pass bookingResponse from CRMManager

* Handle empty company response

* Return the company value
This commit is contained in:
Joe Au-Yeung
2024-11-26 19:14:44 +00:00
committed by GitHub
parent 0b66ddc6db
commit f81716fbb2
3 changed files with 67 additions and 19 deletions
+48 -13
View File
@@ -431,7 +431,11 @@ export default class SalesforceCRMService implements CRM {
: [];
}
async createContacts(contactsToCreate: Attendee[], organizerEmail?: string) {
async createContacts(
contactsToCreate: Attendee[],
organizerEmail?: string,
calEventResponses?: CalEventResponses | null
) {
const conn = await this.conn;
const appOptions = this.getAppOptions();
const createEventOn = appOptions.createEventOn ?? SalesforceRecordEnum.CONTACT;
@@ -474,11 +478,14 @@ export default class SalesforceCRMService implements CRM {
}
}
await this.createAttendeeRecord({ attendee, recordType: SalesforceRecordEnum.LEAD, organizerId }).then(
(result) => {
createdContacts.push(...result);
}
);
await this.createAttendeeRecord({
attendee,
recordType: SalesforceRecordEnum.LEAD,
organizerId,
calEventResponses,
}).then((result) => {
createdContacts.push(...result);
});
}
if (createEventOn === SalesforceRecordEnum.ACCOUNT) {
@@ -522,6 +529,7 @@ export default class SalesforceCRMService implements CRM {
attendee,
recordType: SalesforceRecordEnum.LEAD,
organizerId,
calEventResponses,
})
);
if (result.success) {
@@ -547,7 +555,7 @@ export default class SalesforceCRMService implements CRM {
async handleAttendeeNoShow(bookingUid: string, attendees: { email: string; noShow: boolean }[]) {
const appOptions = this.getAppOptions();
const { sendNoShowAttendeeData, sendNoShowAttendeeDataField } = appOptions;
const { sendNoShowAttendeeData = false, sendNoShowAttendeeDataField = {} } = appOptions;
const conn = await this.conn;
// Check that no show is enabled
if (!sendNoShowAttendeeData && !sendNoShowAttendeeDataField) {
@@ -670,11 +678,13 @@ export default class SalesforceCRMService implements CRM {
recordType,
organizerId,
accountId,
calEventResponses,
}: {
attendee: Attendee;
recordType: SalesforceRecordEnum;
organizerId?: string;
accountId?: string;
calEventResponses?: CalEventResponses | null;
}) {
const conn = await this.conn;
@@ -685,6 +695,7 @@ export default class SalesforceCRMService implements CRM {
attendee,
recordType: recordType,
organizerId,
calEventResponses,
}),
AccountId: accountId,
})
@@ -705,16 +716,19 @@ export default class SalesforceCRMService implements CRM {
attendee,
recordType,
organizerId,
calEventResponses,
}: {
attendee: { email: string; name: string };
recordType: SalesforceRecordEnum;
organizerId?: string;
/**Only Leads have the default company field */
calEventResponses?: CalEventResponses | null;
}) {
const [FirstName, LastName] = attendee.name ? attendee.name.split(" ") : [attendee.email, ""];
// Assume that the first part of the email domain is the company title
const company = attendee.email.split("@")[1].split(".")[0];
const company =
this.getCompanyNameFromBookingResponse(calEventResponses) ?? attendee.email.split("@")[1].split(".")[0];
return {
LastName: LastName || "-",
FirstName,
@@ -805,7 +819,7 @@ export default class SalesforceCRMService implements CRM {
bookingUid?: string | null
) {
const conn = await this.conn;
const { createEventOn, onBookingWriteToRecordFields } = this.getAppOptions();
const { createEventOn, onBookingWriteToRecordFields = {} } = this.getAppOptions();
// Determine record type (Contact or Lead)
const personRecordType = this.determinePersonRecordType(createEventOn);
@@ -885,7 +899,6 @@ export default class SalesforceCRMService implements CRM {
return writeOnRecordBody;
}
private getTextFieldValue({
fieldValue,
fieldLength,
@@ -896,13 +909,12 @@ export default class SalesforceCRMService implements CRM {
calEventResponses?: CalEventResponses | null;
}) {
let valueToWrite = fieldValue.substring(0, fieldLength);
if (!calEventResponses) return valueToWrite;
// Check if we need to replace any values with values from the booking questions
const regexValueToReplace = /\{(.*?)\}/g;
valueToWrite = valueToWrite.replace(regexValueToReplace, (match, captured) => {
return calEventResponses[captured]?.value.toString() ?? match;
return calEventResponses[captured]?.value ? calEventResponses[captured].value.toString() : match;
});
// Trim incase the replacement values increased the length
@@ -1038,4 +1050,27 @@ export default class SalesforceCRMService implements CRM {
return { email: user.Email, recordType: RoutingReasons.ACCOUNT_LOOKUP_FIELD };
}
}
/** Search the booking questions for the Company field value rather than relying on the email domain */
private getCompanyNameFromBookingResponse(calEventResponses?: CalEventResponses | null) {
const appOptions = this.getAppOptions();
const companyFieldName = "Company";
const defaultTextValueLength = 225;
const { onBookingWriteToRecordFields = undefined } = appOptions;
if (!onBookingWriteToRecordFields || !calEventResponses) return;
// Check that we're writing to the Company field
if (!(companyFieldName in onBookingWriteToRecordFields)) return;
const companyValue = this.getTextFieldValue({
fieldValue: onBookingWriteToRecordFields[companyFieldName].value,
fieldLength: defaultTextValueLength,
calEventResponses,
});
if (companyValue === onBookingWriteToRecordFields[companyFieldName]) return;
return companyValue;
}
}
+13 -4
View File
@@ -1,6 +1,6 @@
import getCrm from "@calcom/app-store/_utils/getCrm";
import logger from "@calcom/lib/logger";
import type { CalendarEvent } from "@calcom/types/Calendar";
import type { CalendarEvent, CalEventResponses } from "@calcom/types/Calendar";
import type { CredentialPayload } from "@calcom/types/Credential";
import type { CRM, ContactCreateInput } from "@calcom/types/CrmService";
@@ -42,7 +42,11 @@ export default class CrmManager {
const contactsToCreate = event.attendees.filter(
(attendee) => !contacts.some((contact) => contact.email === attendee.email)
);
const createdContacts = await this.createContacts(contactsToCreate, event.organizer?.email);
const createdContacts = await this.createContacts(
contactsToCreate,
event.organizer?.email,
event.responses
);
contacts = contacts.concat(createdContacts);
return await crmService?.createEvent(event, contacts);
}
@@ -67,9 +71,14 @@ export default class CrmManager {
return contacts;
}
public async createContacts(contactsToCreate: ContactCreateInput[], organizerEmail?: string) {
public async createContacts(
contactsToCreate: ContactCreateInput[],
organizerEmail?: string,
calEventResponses?: CalEventResponses | null
) {
const crmService = await this.getCrmService(this.credential);
const createdContacts = (await crmService?.createContacts(contactsToCreate, organizerEmail)) || [];
const createdContacts =
(await crmService?.createContacts(contactsToCreate, organizerEmail, calEventResponses)) || [];
return createdContacts;
}
+6 -2
View File
@@ -1,4 +1,4 @@
import type { CalendarEvent } from "./Calendar";
import type { CalendarEvent, CalEventResponses } from "./Calendar";
export interface CrmData {
id: string;
@@ -38,7 +38,11 @@ export interface CRM {
includeOwner?: boolean;
forRoundRobinSkip?: boolean;
}) => Promise<Contact[]>;
createContacts: (contactsToCreate: ContactCreateInput[], organizerEmail?: string) => Promise<Contact[]>;
createContacts: (
contactsToCreate: ContactCreateInput[],
organizerEmail?: string,
calEventResponses?: CalEventResponses | null
) => Promise<Contact[]>;
getAppOptions: () => any;
handleAttendeeNoShow?: (
bookingUid: string,