[SalesForce] Associating new contacts/cases with an account

So I'm trying to associate new contacts and cases with an account automatically. I've been creating contacts and cases without an account using an automated script. I understand that at this point, I have to use an APEX trigger although the use case is very simple.

I'm very new to APEX triggers, so I didn't get any further than the logic.

 trigger associateWithAccount on Contact (before insert) {
//get Company__c from Contact
//lookup Accounts by Account Name using Company__c
//get AccountID from the result of that lookup
//insert AccountID from Account into AccountID in Contact
 Map<String, String> extMap = new Map<String, String>();
 Set<String> extIdSet = new Set<String>();
 for(Contact c : Trigger.new){
     extIdSet.add(c.Company__c);
 }
 for(Account a : [select Id, Name from Account where Name IN :extIdSet]){
            extMap.put(a.Id, a.Name);
 }
 for(Contact c : Trigger.new){
     c.Account = extMap.get(c.Id);
 }
}

However, I keep getting the error that I am illegally assigning from String to Account. Any help?

Best Answer

Let's rewrite this with some better variable names while recognizing that Contacts if created from the UI will most likely have their AccountId already populated

trigger associateWithAccount on Contact (before insert) {

 Map<String, Account[]> accountsByCompanyName;  // could be more than one Account w/ same name
 Set<String> searchCompanyNames = new Set<String>();

 // Step 1 - gather search keys
 for(Contact c : Trigger.new){
     if (c.AccountId == null && String.isNotBlank(c.Company__c)) {
        searchCompanyNames.add(c.Company__c);
     }
 }

 // Step 2 - find Accounts by contact.Company__c
 accountsByCompanyName = Util.pivotSObjectsByString(Account.Name,
     [Select Id, Name From Account Where Name IN :searchCompanyNames]);

 // Step 3 - Associate Contact in trigger.new to Account
 for(Contact c : Trigger.new){
    if (c.AccountId != null) {continue;}  // nothing to do 
    Account[] matchingAccounts = accountsByCompanyName.get(c.Company__c);
    if (matchingAccounts.isEmpty()) {continue;} // no match
    if (matchingAccounts.size() > 1) {continue;} // ambiguous - do nothing
    c.AccountId = matchingAccounts[0].Id; // unambiguous match
 }
} 

And handy Util methods (there are other, better flavors of this like SObjectIndex in GitHub)

public static map<String,List<SObject>> pivotSObjectsByString(Schema.SObjectField fldToken, SObject[] sobjList) {
    map<String,List<SObject>>   res = new map<String,List<SObject>> ();
    for (Sobject sobj: sobjList == null ? new list<Sobject>() : sobjlist) {
        String pivotKey = (String) sobj.get(fldToken);
        if (res.containsKey(pivotKey)) {
            res.get(pivotKey).add(sobj);
        }
        else {
            Sobject[] typedSobjs = makeTypedSobjList(sobj); // do this so resulting list.getSobjectType() doesn't return null
            typedSobjs.add(sobj);
            res.put(pivotKey,typedSobjs);
        }
    }
    return res;
}

public static Sobject[] makeTypedSobjList(Sobject sobj) {
    return (Sobject[]) Type.forName('List<'+sobj.getSObjectType()+'>').newInstance();
}
Related Topic