jesse_orange_newable's avatar

When to sync user data with a CRM?

I have a scenario where a bunch of people exist in a CRM (Salesforce) as they went through a process previously as a guest. Having thought about it, it's easier to ask users to create accounts.

Anyway, I've got a rough and ready account sync process that I'd like to roll out.

Here's my thinking:

<?php
namespace App\Listeners;

use App\Models\Salesforce\Contact;
use App\Models\User;
use App\Services\SalesforceService;
use Illuminate\Auth\Events\Registered;

class SyncUserWithSalesforceContact
{
    protected $salesforceService;

    /**
     * Create the event listener.
     */
    public function __construct(SalesforceService $salesforceService)
    {
        $this->salesforceService = $salesforceService;
    }

    /**
     * Handle the event.
     */
    public function handle(Registered $event): void
    {
        $user = $event->user;

        try {
            $this->syncUserWithSalesforce($user);
        } catch (\Exception $e) {
            logger()->error('Error syncing user with Salesforce: ' . $e->getMessage(), ['user_id' => $user->id]);
        }
    }

    private function syncUserWithSalesforce(User $user)
    {
        $existingContact = $this->salesforceService->findContactByEmail($user->email);

        if ($existingContact) {
            $this->handlePreexistingContact($user, $existingContact);
        } else {
            $this->handleContactNotFound($user);
        }
    }

    private function handlePreexistingContact(User $user, Contact $contact)
    {
        $user->update([
            'salesforce_contact_id' => $contact->Id,
            'salesforce_account_id' => $contact->AccountId
        ]);
    }

    private function handleContactNotFound(User $user)
    {
        $contactAttributes = [
            'title' => $user->title,
            'first_name' => $user->first_name,
            'last_name' => $user->last_name,
            'email' => $user->email,
        ];

        $existingAccount = $this->salesforceService->findAccountByNameOrCompanyNumber($user->company_name, $user->companies_house_company_number);

        if ($existingAccount) {
            $contact = $this->salesforceService->createContactInAccount($existingAccount, $contactAttributes);
        } else {
            $accountAttributes = [
                'name' => $user->company_name,
                'company_number' => $user->companies_house_company_number,
            ];

            $newAccount = $this->salesforceService->createAccount($accountAttributes);
            $contact = $this->salesforceService->createContactInAccount($newAccount, $contactAttributes);
        }

        $user->update([
            'salesforce_contact_id' => $contact->Id,
            'salesforce_account_id' => $contact->AccountId,
        ]);
    }
}

I'm sure you can see the intention but there are pitfalls.

  1. What if the user just doesn't use the email we have in the CRM?
  2. What if the account they joined with previously was different? ...

Previously we asked users to enter their own company data which was messy and duplicate prone so I'm trying to use the companies house api with the registration form.

Do you guys think it's a bad idea to do this synchronisation post registering, should it be somewhere else?

Basically to register for an Event in Salesforce you need a Contact, which has to belong to an Account.

0 likes
1 reply
LaryAI's avatar
Level 58

Synchronizing user data with a CRM like Salesforce can be a complex task, especially when dealing with potential discrepancies in user data. Here are some considerations and a refined approach to your synchronization process:

Considerations:

  1. Email Discrepancies: If the user registers with an email different from the one in the CRM, you need a fallback mechanism to identify the user.
  2. Different Accounts: If the user previously joined with a different account, you need to handle merging or updating accounts carefully.
  3. Data Duplication: Using external APIs (like Companies House) can help reduce duplication but requires careful integration.

Solution:

  1. Event Listener: Continue using the Registered event to trigger the synchronization.
  2. Fallback Mechanism: Implement additional checks to identify users, such as matching on other unique identifiers.
  3. Data Validation: Validate and clean data before synchronization to avoid duplicates.

Here's an improved version of your synchronization process:

namespace App\Listeners;

use App\Models\Salesforce\Contact;
use App\Models\User;
use App\Services\SalesforceService;
use Illuminate\Auth\Events\Registered;

class SyncUserWithSalesforceContact
{
    protected $salesforceService;

    /**
     * Create the event listener.
     */
    public function __construct(SalesforceService $salesforceService)
    {
        $this->salesforceService = $salesforceService;
    }

    /**
     * Handle the event.
     */
    public function handle(Registered $event): void
    {
        $user = $event->user;

        try {
            $this->syncUserWithSalesforce($user);
        } catch (\Exception $e) {
            logger()->error('Error syncing user with Salesforce: ' . $e->getMessage(), ['user_id' => $user->id]);
        }
    }

    private function syncUserWithSalesforce(User $user)
    {
        $existingContact = $this->salesforceService->findContactByEmail($user->email);

        if ($existingContact) {
            $this->handlePreexistingContact($user, $existingContact);
        } else {
            $this->handleContactNotFound($user);
        }
    }

    private function handlePreexistingContact(User $user, Contact $contact)
    {
        $user->update([
            'salesforce_contact_id' => $contact->Id,
            'salesforce_account_id' => $contact->AccountId
        ]);
    }

    private function handleContactNotFound(User $user)
    {
        $contactAttributes = [
            'title' => $user->title,
            'first_name' => $user->first_name,
            'last_name' => $user->last_name,
            'email' => $user->email,
        ];

        $existingAccount = $this->salesforceService->findAccountByNameOrCompanyNumber($user->company_name, $user->companies_house_company_number);

        if ($existingAccount) {
            $contact = $this->salesforceService->createContactInAccount($existingAccount, $contactAttributes);
        } else {
            $accountAttributes = [
                'name' => $user->company_name,
                'company_number' => $user->companies_house_company_number,
            ];

            $newAccount = $this->salesforceService->createAccount($accountAttributes);
            $contact = $this->salesforceService->createContactInAccount($newAccount, $contactAttributes);
        }

        $user->update([
            'salesforce_contact_id' => $contact->Id,
            'salesforce_account_id' => $contact->AccountId,
        ]);
    }
}

Additional Improvements:

  1. Fallback Identification: Implement additional checks to identify users, such as matching on other unique identifiers (e.g., phone number, user ID).
  2. Data Cleaning: Ensure data is cleaned and validated before synchronization to avoid duplicates.
  3. Error Handling: Enhance error handling to cover more edge cases and provide more detailed logging.

Example of Fallback Identification:

private function syncUserWithSalesforce(User $user)
{
    $existingContact = $this->salesforceService->findContactByEmail($user->email);

    if (!$existingContact) {
        // Fallback: Try to find by other unique identifiers
        $existingContact = $this->salesforceService->findContactByPhoneNumber($user->phone_number);
    }

    if ($existingContact) {
        $this->handlePreexistingContact($user, $existingContact);
    } else {
        $this->handleContactNotFound($user);
    }
}

By implementing these improvements, you can make your synchronization process more robust and handle various edge cases more effectively.

Please or to participate in this conversation.