[SalesForce] Problem with a trigger loop

Here is a basic description of my data model :

Contract_Overview__c (parent object with following fields)
– Account__c (main account for contract)

  • Subsidiaries_On_Contract__c (multi-value field showing all of the companies which are subsidiaries to Account which are on this particular contract. This trigger is automatically creating a new Subs_Serviced_On_Contract__c child record for each value in this field)

  • the rest of the fields are not important in the functioning of this trigger

Subs_Serviced_On_Contract__c (child object to Contract_Overview__c. The trigger creates a new one of these for every value in the above named field.

  • Name (name of Subsidiary ; populated by trigger by peeling the name out of the Subsidiaries_On_Contract__c multi-value field in the Contract_Overview__c parent object)
  • Contract_Overview__c (name of the Contract_Overview parent object. This is a lookup field and therefore is a link to the Contract_Overview parent record)
  • Subsidiaries_and_Brands__c (this is the name of the subsidiary. The field is a lookup to the Subsidiary's profile. This is the field I am attempting to populate in order to create a link to the Subsidiary profile.

SubsidiariesandBrands__c (Subsidiary profile object. Object I am attempting to link to with the Subsidiaries_and_Brands__c field in the Subs_Serviced_On_Contract__c)

I am so far only able to properly populate the Subsidiaries_and_Brands__c field on the first record. That field is blank in the rest of the automatically created child records.

In the following trigger I am taking the multi-value field named Subsidiaries_On_Contract__c and creating a new child record for each value within it. That is working fine. However, I am attempting to populate the lookup Subsidiaries_and_Brands field which is a lookup to the Subsidiary and Brands object with the name of the corresponding name from Subsdiaries_On_Contract and it is only working for the first new record which is being created each time.

Here is the code. The portion in bold is the part which is where I am attempting to somehow use the Map method to to connect the name to the corresponding Subsidiaries and Brands object.

trigger AutoCreateSubsServOnContrOv on Contract_Overview__c (after insert, after update) 
{
   List<Subs_Serviced_On_Contract__c> subs = new List<Subs_Serviced_On_Contract__c>();

   // get the full list of sub account names for all records being processed by the trigger

   List<String> subAccNames=new List<String>();

   for (Contract_Overview__c newCont : Trigger.New) 
   {
      if (newCont.Subsidiaries_On_Contract__c != '[]') 
      {
         // split out the multi-select picklist using a comma delimiter
         System.debug('Subsidiaries_On_Contract__c ' + newCont.Subsidiaries_On_Contract__c);

         String temp = newCont.Subsidiaries_On_Contract__c;
         temp = temp.replace(']','');
         temp = temp.replace('[','');
         String[] all = temp.split(',');
         subAccNames.addAll(all);
      }
   }

// get the ids for all sub accounts and store in a map keyed by name

 Map<String, Id> subAccIdsByName=new Map<String, Id>();
   for (SubsidiariesAndBrands__c subAcc : [select id, Name from SubsidiariesAndBrands__c where Name in :subAccNames]) 
           {
              subAccIdsByName.put(subAcc.Name, subAcc.id);
           }

// End 1st bold section

   System.debug('SubsidiariesAndBrands ************ ' + subAccIdsByName);
   //For each position processed by the trigger, add a new  

   //Subs_Serviced_On_Contract record for the specified Subsidiaries_On_Contract__c.  

   //Note that Trigger.New is a list of all the new positions  

   //that are being created.  

   for (Contract_Overview__c newContract : Trigger.New) 
   {
      if (newContract.Subsidiaries_On_Contract__c != '[]') 
      {
         // split out the multi-select picklist using a comma delimiter
         System.debug('Subsidiaries_On_Contract__c ' + newContract.Subsidiaries_On_Contract__c);

         String temp = newContract.Subsidiaries_On_Contract__c;
         temp = temp.replace(']','');
         temp = temp.replace('[','');
         String[] all = temp.split(',');

         for(String subsoncontract: all)
         {

            subsoncontract = subsoncontract.normalizeSpace();
            //
            for(String subsoncontract: newContract.Subsidiaries_On_Contract__c.split(',')){

            Subs_Serviced_On_Contract__c ssoc = new Subs_Serviced_On_Contract__c(
                    Name = subsoncontract,
                    Contract_Overview__c = newContract.Id,
                    Account__c = newContract.Account__c,

// 2nd Bold Section

            Subsidiaries_and_Brands__c = subAccIdsByName.get(subsoncontract),  
            // GET THE SUB ACCOUNT ID BASED ON NAME **
            Contract_and_Sub__c = newContract.Name + '~' + subsoncontract, Contract_Start_Date__c = newContract.Contract_Start_Date__c, Contract_End_Date__c = newContract.Contract_End_Date__c, Logo_Usage_Allowed__c = 'Yes');
            subs.add(ssoc);
         }
      } 
   }

   upsert subs Contract_and_Sub__c;
}

End 2nd Bold Section

Since it is only working for the first value I am assuming I somehow have to reposition that portion of the code within the loop which is creating the records and somehow maybe use a Get to retrieve the individual values, but I can't figure out where\how exactly that should be done.

If you can give me any input I would love it.

Thank you very much.

Best Answer

Not something I can test locally, but some things I changed looking at it:

I use a Set to keep track of all possible names before querying, to filter out duplicates. I calculated the arrays of names once and stored those arrays in an array of the same length and indices as the trigger.new array. (We've found that pre-allocating arrays of known length doesn't make much different to CPU governor usage, especially here as the array is no bigger than 200).

Set<String> allSubAccountNames = new Set<String>();
List<List<String>> subAccountNamesByOverview = new List<List<String>>;

for (Contract_Overview__c newCont : Trigger.New) 
{
   String[] subAccountNames = splitField(newCont.Subsidiaries_On_Contract__c);
   allSubAccountNames.addAll(subAccountNames);
   subAccountNamesByOverview.add(subAccountNames);
}

Map<String, Id> subAccIdsByName=new Map<String, Id>();
for (SubsidiariesAndBrands__c subAcc : [select id, Name from SubsidiariesAndBrands__c where Name in :allSubAccountNames]) 
{
   subAccIdsByName.put(subAcc.Name, subAcc.id);
}

My split function is

private String[] splitField(String value)
{
   value = value.replaceAll('\[|\]','');
   return value.split(',');
}

It may be possible to do this in a single REGEX, but we become more obtuse.

The other main difference is I did an insert not an upsert at the end as they're new records.

List<Subs_Serviced_On_Contract__c> subs = new List<Subs_Serviced_On_Contract__c>();
for (Integer i = 0; i < Trigger.New.size(); i++) 
{
    Contract_Overview__c newContract = Trigger.New[i];
    String[] subAccountNames = subAccountNamesByOverview[i];

    for(String subName : subAccountNames)
    {
        Id subId = subAccIdsByName.get(subName);
        System.assert(subId != null, 'Failed to find subaccount by name ' + subName);

        Subs_Serviced_On_Contract__c ssoc = new Subs_Serviced_On_Contract__c(
            Name = subName,
            Contract_Overview__c = newContract.Id,
            Account__c = newContract.Account__c
            Subsidiaries_and_Brands__c = subId,  
            Contract_and_Sub__c = newContract.Name + '~' + subName,
            Contract_Start_Date__c = newContract.Contract_Start_Date__c,
            Contract_End_Date__c = newContract.Contract_End_Date__c, 
            Logo_Usage_Allowed__c = 'Yes');

        subs.add(ssoc);
    }
}
insert subs;