+ Start a Discussion
Zoom_VZoom_V 

Trigger loop problem

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 red 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);
           }
 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,
                    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;
}

 

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 was love it.

 

Thank you very much. 

Best Answer chosen by Admin (Salesforce Developers) 
ForcepowerForcepower

Zoom,

 

I suspect the split may not be receiving the right delimiter. I'd suggest putting in one more debug for your subAccNames.

 

   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);
for (String acctName : subAccNames) {
System.debug('sub acct name ' + acctName);
}
} }

All Answers

Jake GmerekJake Gmerek
Is this statement:

System.debug('SubsidiariesAndBrands ************ ' + subAccIdsByName);

displaying the expected data?
s_k_as_k_a

Hi,

 

In the following line of code in your code is not inserting at all.  you want to create subsidiariesand Brands too.

SubsidiariesAndBrands__c sac = new SubsidiariesAndBrands__c(Name = subsoncontract);
Zoom_VZoom_V

"Is this statement:

System.debug('SubsidiariesAndBrands ************ ' + subAccIdsByName);

displaying the expected data?"

 

 

No. I guess that's incorrect.

 

Thank you

Jake GmerekJake Gmerek
OK so what is happening is that your brand names do not have related records in the SubsidiariesAndBrands__c. You need to either add them at some point in the trigger or prepopulate the object ahead of time and ensure that there is a record for every object on your picklist.
Zoom_VZoom_V

Jake,

 

No, that's not true. All of the values in the Subsidiaries_On_Contract__c field have a corresponding profile. (object name SubsidiariesAndBrands__c). I know this to be true because a query populated the Subsidiaries_On_Contract__c field in the parent object Contract_Overview__c by pulling the names of all the SubsidiaryAndBrands__c records under the Account in the contract.

 

Just ignore that line SKA pointed out. It wasn't doing anything. I took it out. It shouldn't have been in there in the first place. Sorry for the confusion. 

 

Thanks for the insight.

Zoom_VZoom_V

 

Here is a quick rundown of the data model if this will help : 

 

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)


Thank you.

ForcepowerForcepower

Zoom,

 

Puzzling, indeed. May need a couple of debug statements to see what you have in the map and what is being received on the .get

 

// 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);
System.debug('Account Name = ' + subAcc.Name + ' Id = ' + subAcc.id);

}





         for(String subsoncontract: all)
         {
         
            // ############### 
System.debug('subsoncontract = ' + ' Id = ' + subAccIdsByName.get(subsoncontract));

 

 

            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,
                    

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);
         }
 
s_k_as_k_a

Hi Zoom,

 

Can you try this code.  I hope this will solve.

 

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

   
   Map<Id, List<String>> ContractToSubsAccNames =  new Map<Id, List<String>>();
   Map<Id, Contract_Overview__c> contractMap = new Map<Id, Contract_Overview__c>();
   for (Contract_Overview__c newCont : Trigger.New) 
   {
      List<String> subAccNames=new List<String>();
	  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('[','');
         subAccNames = temp.split(',');
	  }
      ContractToSubsAccNames.put(newcont.Id, subAccNames);
      contractMap.put(newcont.Id, newcont);
   }
   System.debug('*******subAccNames List Size::::::'+ subAccNames.size());
   
   //get the map for contract and child object Subs_Serviced_On_Contract__c
   Map<Id, List<Subs_Serviced_On_Contract__c>>  contToSubsMap = new  Map<Id, List<Subs_Serviced_On_Contract__c>>();
   // Replace Subs_Serviced_On_Contracts__r value in below query  with child relationship name
   for(Contract_Overview__c co : [Select Id, (Select  Name,Contract_Overview__c,Account__c,Subsidiaries_and_Brands__c ,Contract_and_Sub__c,Contract_Start_Date__c = newContract.Contract_Start_Date__c, Contract_End_Date__c,logo_Usage_Allowed__c from Subs_Serviced_On_Contracts__r) from Contract_Overview__c where id in :contractMap.keySet()])
	{
        contToSubsMap.put(co.id, co.Subs_Serviced_On_Contracts__r);
	}

   // 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 :ContractToSubsAccNames.Values()]) 
    {
              subAccIdsByName.put(subAcc.Name, subAcc.id);
    }
   System.debug('SubsidiariesAndBrands size ************ ' + subAccIdsByName.size());
   
   //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 (Id ConId : contractMap.ketSet()) 
   {
      
	  if(contToSubsMap.containsKey(ConId.id) && contToSubsMap.get(ConId.id).size() > 0)
	   {
		  // When Update occurs on contract this loop will excecute
		  for(Subs_Serviced_On_Contract__c sscoupdate : contToSubsMap.get(ConId.id))
		   {
			  // contract has already child Subs_Serviced_On_Contract__c records we need to update with update contract values
			  String ssocName = sscoupdate.Name.normalizeSpace();
			  if(subAccIdsByName.containsKey(ssocName))
			   {
				  // Subs_Serviced_On_Contract__c record matches then update
				  sscoupdate.Contract_Overview__c = contractMap.get(ConId.id).Id;
                  sscoupdate.Account__c = contractMap.get(ConId.id).Account__c;
                  sscoupdate.Subsidiaries_and_Brands__c = subAccIdsByName.get(subsoncontract);
				  // GET THE SUB ACCOUNT ID BASED ON NAME
                  sscoupdate.Contract_and_Sub__c = newContract.Name + '~' + ssocName;
                  sscoupdate.Contract_Start_Date__c = contractMap.get(ConId.id).Contract_Start_Date__c;
                  sscoupdate.Contract_End_Date__c = contractMap.get(ConId.id).Contract_End_Date__c;
                  sscoupdate.Logo_Usage_Allowed__c = 'Yes';

				  subs.add(sscoupdate);

			   }else{
				   // Subs_Serviced_On_Contract__c record not matched then insert
				   Subs_Serviced_On_Contract__c ssocnew = new Subs_Serviced_On_Contract__c(
                    Name = subsoncontract,
                    Contract_Overview__c = contractMap.get(ConId.id).Id,
                    Account__c = contractMap.get(ConId.id)t.Account__c,
                    Subsidiaries_and_Brands__c = subAccIdsByName.get(subsoncontract),  // GET THE SUB ACCOUNT ID BASED ON NAME
                    Contract_and_Sub__c = contractMap.get(ConId.id).Name + '~' + subsoncontract,
                    Contract_Start_Date__c = contractMap.get(ConId.id).Contract_Start_Date__c,
                    Contract_End_Date__c = contractMap.get(ConId.id).Contract_End_Date__c,
                    Logo_Usage_Allowed__c = 'Yes');

					subs.add(ssocnew);
			   }
		   }
	   }else if (contToSubsMap.containsKey(ConId.id) && contToSubsMap.get(ConId.id).size() == 0)
	     {
		    // When first insert  occurs on contract and no child records this loop will excecute
		   for(String subsoncontract : ContractToSubsAccNames.get(ConId.id))
		   {
			   subsoncontract = subsoncontract.normalizeSpace();
               Subs_Serviced_On_Contract__c ssoc = new Subs_Serviced_On_Contract__c(
                    Name = subsoncontract,
                    Contract_Overview__c = newContract.Id,
                    Account__c = newContract.Account__c,
                    Subsidiaries_and_Brands__c = subAccIdsByName.get(subsoncontract), // GET THE SUB ACCOUNT ID BASED ON NAME
                    Contract_and_Sub__c = contractMap.get(ConId.id).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);
		   }
		 }
   }
   if(!sub.isEmpty())
	   upsert sub;
}
	    
		        
         

 

 

Jake GmerekJake Gmerek
"No, that's not true. All of the values in the Subsidiaries_On_Contract__c field have a corresponding profile. (object name SubsidiariesAndBrands__c). I know this to be true because a query populated the Subsidiaries_On_Contract__c field in the parent object Contract_Overview__c by pulling the names of all the SubsidiaryAndBrands__c records under the Account in the contract."

If that is true then your method of splitting the data must have issues. I would place a debug statement for string[] all and see if the data that is getting into that array matches the data in your object.


Zoom_VZoom_V

Thanks for the insight Ram.

 

I know this much after putting a bunch of debugs into the code : 

 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);
              System.debug('subacc.Name within FOR: <<<<<<<Account Name = ' + subAcc.Name + ' Id = ' + subAcc.id);
           }
 System.debug('SubsidiariesAndBrands ******ALL MAP subAccIdsByName outside FOR****** = ' + subAccIdsByName);
   //For each position processed by the trigger, add a new  

 

The FOR loop is only passing through once. I only get one value for subAcc.Name (which is properly showing the Name and ID of the Account.). This is odd because subAccNames has 3 account names in it, but the query which uses it to look up the ID's only runs once.

 

So, because of that, subAccIdsByName only has values for one account (Name and ID).

 

So at the end when the values are being plugged into fields Subsidiaries_and_ Brands__c is only being populated for the first account because there are no other id's.

 

So, for some reason that query is only running once. Any ideas on what that's all about ? 

 

Thanks Ram.

 

ForcepowerForcepower

Zoom,

 

I suspect the split may not be receiving the right delimiter. I'd suggest putting in one more debug for your subAccNames.

 

   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);
for (String acctName : subAccNames) {
System.debug('sub acct name ' + acctName);
}
} }
This was selected as the best answer
Zoom_VZoom_V

Anusha,

 

Thank you very much for the code !

 

However, I'm getting an 'unexpected token =' error on the query line : 

 

 for(Contract_Overview__c co : [Select Id, (Select  Name,Contract_Overview__c,Account__c,Subsidiaries_and_Brands__c ,Contract_and_Sub__c,Contract_Start_Date__c = newContract.Contract_Start_Date__c, Contract_End_Date__c,logo_Usage_Allowed__c from Subs_Serviced_On_Contracts__r) from Contract_Overview__c where id in :contractMap.keySet()])
	

 I couldn't really understand the whole query so I didn't really know what needed to be done.

 

Thanks again. I really appreciate you taking the time.

s_k_as_k_a

Zoom,

 

This is the relationship query between Contract_Overview__c and  Subs_Serviced_On_Contract__c. use this query you can get all the child records corresponding to parent record.

 

You can replace Subs_Serviced_On_Contracts__r (because i dont know exact name relationshipname)  in below query with child relationship name . This name you can see on detail page of the look up field on Subs_Serviced_On_Contract__c

 

for(Contract_Overview__c co : [Select Id, (Select Name,Contract_Overview__c,Account__c,Subsidiaries_and_Brands__c ,Contract_and_Sub__c,Contract_Start_Date__c , Contract_End_Date__c,logo_Usage_Allowed__c from Subs_Serviced_On_Contracts__r) from Contract_Overview__c where id in :contractMap.keySet()])

Zoom_VZoom_V

Ram,

 

I think you may have just figured out the problem with that. There is a space being left in front of the account names (acctName) from the 2nd value on.

 

So basically I'm now trying to figure out where to put a normalizeSpace().  In your debug acctName.normalizeSpace() works of course - but I can't figure out where to put it in the actual code. It won't work to use it on temp.

 

Thanks again Ram.

Zoom_VZoom_V

Ska,

 

There's a problem with subsoncontract. I'm getting an error saying it doesn't exist. It's coming from this line : 

 

sscoupdate.Subsidiaries_and_Brands__c = subAccIdsByName.get(subsoncontract);

 

I think it's because it doesn't get identified until down here : 

 

for(String subsoncontract : ContractToSubsAccNames.get(ConId.id))
		   {
			   subsoncontract = subsoncontract.normalizeSpace();
               Subs_Serviced_On_Contract__c ssoc = new Subs_Serviced_On_Contract__c(
                    Name = subsoncontract,
                    Contract_Overview__c = newContract.Id,
                    Account__c = newContract.Account__c,
                    Subsidiaries_and_Brands__c = subAccIdsByName.get(subsoncontract), // GET THE SUB ACCOUNT ID BASED ON NAME
                    Contract_and_Sub__c = contractMap.get(ConId.id).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);

 Thank you very much SKA.

 

 

ForcepowerForcepower

Zoom,

 

Just do this:

 

         //subAccNames.addAll(all);
for (String acctName : all) {
subAccNames.add(acctName.normalizeSpace());
System.debug('sub acct name ' + acctName.normalizeSpace());
}
Zoom_VZoom_V

Ram,

 

Doing that gave this error when I was creating a new record : 

"nsert failed. First exception on row 0; first error: CANNOT_INSERT_UPDATE_ACTIVATE_ENTITY, AutoCreateSubsServOnContrOv: execution of AfterInsert

caused by: System.FinalException: Cannot modify a collection while it is being iterated."

 

Thanks again.

ForcepowerForcepower

Zoom,

 

Interesting. Just want to make sure you changed the iteration collection to "all" rather than "subAccNames" as it was before. If you did and still got the error, I would suggest trying this below (use a temp var).

 

   //subAccNames.addAll(all);
for (String acctName : all) {
String acctNameTemp = acctName;

subAccNames.add(acctNameTemp.normalizeSpace());
System.debug('sub acct name ' + acctNameTemp.normalizeSpace());
}
Zoom_VZoom_V

It worked ! 

 

wow - that was a really stupid mistake on my part. I'm really really sorry about that. I'm gonna blame it on being late in the day and being tired.

 

Thank you so much Ram. You're awesome.

ForcepowerForcepower
Excellent - glad to see it worked, Zoom. You're very welcome. Have a great evening.
Ram