I'm trying to link incoming cases (through the UI and through our REST API). Essentially, I'm checking for a combination of first name, last name and email on the incoming case, and if that combination exists on a Person Account - link it to that existing person account, else create a new person account. My only concern is that if there are two or more person accounts (duplicates), the case is linked to one of them - I wish to avoid this step altogether, meaning: If there is more than person account match, don't link the case to any account at all.

public with sharing class CaseAccountLink {
public static void linkCasesToAccount(List<Case> caseList) {

List<Case> casesReceived = [SELECT Id, Web_First_Name__c, Web_Last_Name__c, SuppliedEmail, Privacy_Policy__c, Country__c, Market__c, RecordTypeId
                                FROM Case
                                WHERE Id IN :caseList
                                AND AccountId = null];

    List<String> caseEmails = Collection.of(caseEmails).pluckStrings(Case.SuppliedEmail);

    List<Account> accountsToCheck = [SELECT Id, FirstName, LastName, PersonEmail
                                        FROM Account
                                        WHERE PersonEmail
                                        IN :caseEmails];

    Map<ps_CustomerDetailsKey, Id> existingAccountIdsByKey = new Map<ps_CustomerDetailsKey, Id>(); // Just a custom map

    for (Account eachAcc : accountsToCheck) {
        existingAccountIdsByKey.put(new ps_CustomerDetailsKey(eachAcc.FirstName, eachAcc.LastName, eachAcc.PersonEmail), eachAcc.Id);

    Map<ps_CustomerDetailsKey, Account> newAccountsByKey = new 
    Map<ps_CustomerDetailsKey, Account>();  
    List<Case> linkCaseToAccount = new List<Case>();
    List<Case> casesToUpdate = new List<Case>();

    for (Case eachCase :casesReceived) {
        ps_CustomerDetailsKey caseKey = new ps_CustomerDetailsKey(eachCase.Web_First_Name__c, eachCase.Web_Last_Name__c, eachCase.SuppliedEmail);

        Id foundAccountId = existingAccountIdsByKey.get(caseKey);
        if (foundAccountId != null) {
            // The case maps to an account and the foundAccountId specifies which one
            eachCase.AccountId = foundAccountId;


        } else {
            // No account for the case is found
            Account newAccount = newAccountsByKey.get(caseKey);
            if (newAccount == null) {
                    new Account(
                        FirstName = eachCase.Web_First_Name__c,
                        LastName = eachCase.Web_Last_Name__c,
                        PersonEmail = eachCase.SuppliedEmail

    Database.insert(newAccountsByKey.values(), false);

    // newly created cases (and accounts) that couldn't be linked
    // needs to be re-checked and linked
    for (Case eachCase : casesReceived) {
        ps_CustomerDetailsKey key = new ps_CustomerDetailsKey(eachCase.Web_First_Name__c, eachCase.Web_Last_Name__c, eachCase.SuppliedEmail);
        Account newAccount = newAccountsByKey.get(key);
        if (newAccount != null) {
            // Should always be the case!
            eachCase.AccountId = newAccount.Id;

    update casesToUpdate;


public class ps_CustomerDetailsKey {
    List<Object> key;

public ps_CustomerDetailsKey(String first, String last, String email) {
    this.key = new List<Object>{ first, last, email };

public Boolean equals(Object other) {
    if(other instanceof ps_CustomerDetailsKey) {
        return this.key.equals(((ps_CustomerDetailsKey) other).key);
    return false;

public override Integer hashCode() {
    return key.hashCode();

public String getFirst() {
    return (String) key [0];

public String getLast() {
    return (String) key [1];

public String getEmail() {
    return (String) key [2];
  • what's ps_CustomerDetailsKey? Can you add another field like a flag for duplicate? – Kris Goncalves Jan 16 '20 at 14:36
  • I'll update the original post – erikvm Jan 16 '20 at 15:25
  • something else to to think about - while emails will always be stored in the DB as lowercase, first and last names will be of mixed case and hence the hashcodes will be different for Napoleon Bonaparte emperor@france.co.fr and napoleon bonaparte emperor@france.co.fr – cropredy Jan 17 '20 at 0:42

I see two options here.

  1. Convert your Map<ps_CustomerDetailsKey, Id> existingAccountIdsByKey into a Map<ps_CustomerDetailsKey, List<Id>> existingAccountIdsByKey - that is, a multimap. Accumulate Account ids for each composite key. Then, alter your logic slightly:

    List<Id> foundAccountIds = existingAccountIdsByKey.get(caseKey);
    if (foundAccountIds != null && foundAccountIds.size() == 1) {
        eachCase.AccountId = foundAccountIds[0];
  2. Implement a separate Set<ps_CustomerDetailsKey> duplicatedKeys. As you're processing Person Accounts, add keys to this Set if their keys are already represented:

    for (Account eachAcc : accountsToCheck) {
        ps_CustomerDetailsKey key = new ps_CustomerDetailsKey(eachAcc.FirstName, eachAcc.LastName, eachAcc.PersonEmail);
        if (existingAccountIdsByKey.containsKey(key)) {
        } else {
            existingAccountIdsByKey.put(new ps_CustomerDetailsKey(eachAcc.FirstName, eachAcc.LastName, eachAcc.PersonEmail), eachAcc.Id);

    Then, similarly alter your logic:

    Id foundAccountId = (duplicatedKeys.contains(caseKey) ? null : existingAccountIdsByKey.get(caseKey));
    if (foundAccountId != null) {
  • Thanks David - not entirely sure I understand though - what makes 1 work different than the current solution? What makes having a list and checking eachCase.AccountId = foundAccountIds[0]; any different? – erikvm Jan 16 '20 at 15:23
  • @erikvm I apologize - I typed this code too quickly and left out a crucial clause (edited above). You'd also be checking the list's size(). – David Reed Jan 16 '20 at 15:25
  • Thanks for clarifying. However, in the (Account eachAcc : accountsToCheck) clause - I'm only storing one unique value (even if there are two duplicate accounts, perhaps I should change this logic too) – erikvm Jan 16 '20 at 15:28
  • Right, so if you wanted to do (1), you'd change it to a multimap (Map<ps_CustomerDetailsKey, List<Id>> existingAccountIdsByKey) and add each one to the child List, allowing you to accumulate multiple values per key. – David Reed Jan 16 '20 at 15:31
  • Would I need to change that in the ps_CustomerDetailsKey class as well? – erikvm Jan 16 '20 at 15:32

You should be able to check for this when you're building your map. I'd add any key that is a duplicate to a set and then just iterate again over the keyset to remove those values so later on you can essentially "skip" those cases from your logic of creating an account if one doesn't exist.

Set<ps_CustomerDetailsKey> removeDups = new Set<ps_CustomerDetailsKey>();

for (Account eachAcc : accountsToCheck) {
    ps_CustomerDetailsKey detail = new ps_CustomerDetailsKey(eachAcc.FirstName, eachAcc.LastName, eachAcc.PersonEmail);
    //if it was already added, just add it to the set to remove later
    } else {
        existingAccountIdsByKey.put(detail, eachAcc.Id);

    //Loop through and remove the keys that were duplicates
    for(ps_CustomerDetailsKey key : existingAccountIdsByKey.keySet()){

Just know that you'll have to change your later code where you retrieve the accountIds by using the key since you deleted those keys above.

Now, you can just add a check here to not even bother with the if/else logic concerning needing to create an account since you want these (the dups) to be null

for (Case eachCase :casesReceived) {
ps_CustomerDetailsKey caseKey = new ps_CustomerDetailsKey(eachCase.Web_First_Name__c, eachCase.Web_Last_Name__c, eachCase.SuppliedEmail);


//now do logic around whether newAccount is null and create a case if one doesn't exist

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.