+ Start a Discussion
SkyhSkyh 

Trigger - updating records with same prefix

I have an object MPIAccounts__c. On this object is text a field called Alert__c. The MPIAccounts__c follows a naming convention e.g 91234-0001, 91234-0002,91234-0003,76111-4556,76111-3120,76111-9458


I want to write a trigger such that if the text in Alert__c is changed on a record, I want to find all records in MPIAccounts__c that has the same prefix as this record and updated these with the same alert text.

e.g if the Alert__c on 91234-0001 is changed to 'This is an invoice', 91234-0002, 91234-0003 should also have it's Alert__c updated to 'This is an invoice'

 

similarly if the Alert__c on 76111-9458 is changed to 'This is the balance', 76111-4556,76111-3120 should also have it's Alert__c updated to 'This is the balance'.

 

Note: The MPIAccounts__c are not children of the same parent. So it's not like updating the children of the same same parent.

 

Any suggestions?

 

Thanks heaps!!

 

Best Answer chosen by Admin (Salesforce Developers) 
k_bentsenk_bentsen

The more I started thinking about it, the more I realized bulkifying this trigger will be trickier than I initially thought, but still definitely doable. Here's how I'm thinking to do it at a high-level:

 

1. Loop through the Trigger.new list, comparing the Alert__c value with what's in the Trigger.old value. If they aren't the same, perform the prefix truncation, add it to a list of strings; add said string to a map as such: <truncatedString, Alert__c>

 

2. If the list of strings is not empty, iterate through it building your query string. Each iteration should append something like 'Name LIKE ' + prefix + '% OR '. After you're done iterating, chop off the last 4 characters of the built string so there's no extra OR conditional operator.

 

3. Use the built string to query all the related MPIAccount records and put them in a list. 

 

4. Iterate through list of MPIAccounts. Each iteration should perform the same prefix truncation that was done in step 1. Use the truncated prefix string to get the Alert__c value from the previously created map. Apply said value to current MPIAccount record in list.

 

5. Update MPIAccount list.

 

You'll also need to create a simple external class with a static variable to ensure that the trigger doesn't re-invoke itself when you perform the DML update in step 5, something like what's shown in the "Using Static Methods and Variables" section at: http://www.salesforce.com/us/developer/docs/apexcode/Content/apex_classes_static.htm

 

Let me know if you don't really follow along, and I'd be happy to take a crack at writing the trigger for you. You'll just be responsible for debugging and validating the logic :)

 

All Answers

k_bentsenk_bentsen

My initial thought is to use dynamic SOQL... truncate the name of the MPIAccount__c record that fired the trigger by using the dash as a delimiter, then use what's on the left as part of the query string. 

SkyhSkyh
Thanks for the response. I am ok with stripping off the prefix. My concern is the governor limits. Any thoughts ?
k_bentsenk_bentsen

The more I started thinking about it, the more I realized bulkifying this trigger will be trickier than I initially thought, but still definitely doable. Here's how I'm thinking to do it at a high-level:

 

1. Loop through the Trigger.new list, comparing the Alert__c value with what's in the Trigger.old value. If they aren't the same, perform the prefix truncation, add it to a list of strings; add said string to a map as such: <truncatedString, Alert__c>

 

2. If the list of strings is not empty, iterate through it building your query string. Each iteration should append something like 'Name LIKE ' + prefix + '% OR '. After you're done iterating, chop off the last 4 characters of the built string so there's no extra OR conditional operator.

 

3. Use the built string to query all the related MPIAccount records and put them in a list. 

 

4. Iterate through list of MPIAccounts. Each iteration should perform the same prefix truncation that was done in step 1. Use the truncated prefix string to get the Alert__c value from the previously created map. Apply said value to current MPIAccount record in list.

 

5. Update MPIAccount list.

 

You'll also need to create a simple external class with a static variable to ensure that the trigger doesn't re-invoke itself when you perform the DML update in step 5, something like what's shown in the "Using Static Methods and Variables" section at: http://www.salesforce.com/us/developer/docs/apexcode/Content/apex_classes_static.htm

 

Let me know if you don't really follow along, and I'd be happy to take a crack at writing the trigger for you. You'll just be responsible for debugging and validating the logic :)

 

This was selected as the best answer
SkyhSkyh
Thanks heaps!!!
I will give it a shot. If need help will reach out.

you are right. the challenge is bulkifying and trigger reinvoking itself .
SkyhSkyh

 

I am on the right path but my problem now is assigning the Alert__c value of the updated MyAccount to all other MyAccounts whose names start with the same prefix as this one.

 

Based on suggestions by @  and trying to do as suggested in the blog http://blog.jeffdouglas.com/2009/10/02/preventing-recursive-future-method-calls-in-salesforce/

  

  

 

trigger AlertForAccount on MyAccount__c (after insert, after update) {

	// list of string to hold prefix of account names
	list<string> prefixnames = new list<string>();

	// map to hold record id and prefixed name
	map<ID, string> MapofMyAcc = new map<ID, String>();

		
	if (!ProcessorControl.inFutureContext) {
	
		// loop through MyAccount__c to compare old and new alert values
		//if not the same then perform prefix truncation
		for (MyAccount__c myacc: Trigger.New){ 
			
			if(trigger.isUpdate){
			
				MyAccount__c alertBeforeUpdate = System.Trigger.oldMap.get(myacc.Id); 
				system.debug('What is the value before update: ' +alertBeforeUpdate);
					
				if(myacc.Alert__c!=null && myacc.Alert__c!=alertBeforeUpdate.Alert__c){ 
					 
					string [] splitparts = mapacc.Name.split('-');
					system.debug('when split: >>> ' +splitparts);
					string part1 = splitparts.get(0);
					system.debug('part1 is>>> ' +splitparts);
					string part2 = splitparts.get(1);
					system.debug('part2 is>>> ' +splitparts);
					string combinefirsttwoparts = part1 + '-' + Part2 + '%';
					system.debug('==========' +combinefirsttwoparts);
						
					// add the prefix to the list 
					prefixnames.add(combinefirsttwoparts);
						
					// also add to the map so we can reference it as needed
					MapofMyAcc.put(myacc.id,combinefirsttwoparts);
					system.debug('Retrieve the value for each key:' +MapofMyAcc.get(myacc.id));
				}
			}
		}
		
		
		if (!prefixnames.isEmpty()){
			MyAccountProcessor.processAccounts(prefixnames);
		}
		
	}
}

 

public with sharing class MyAccountProcessor {  
	
	@future
	public static void processAccounts(list<String> names) {
          // list to store the myaccounts to update
          List<MyAccount__c> myaccountsToUpdate = new List<MyAccount__c>();
          // iterate through the list of myaccounts to process
          

for (MyAccount__c a : [Select Id, Name,Alert__c From MyAccount__c where Name like :names])
{

   
a.Alert__c = 'here I want to assign the new Alert__c value' ;
// how can i get the updatedrecordid.Alert__c

// add the myaccounts to the list to update myccountsToUpdate.add(a); } ProcessorControl.inFutureContext = true; // update the myaccounts update myaccountsToUpdate; } }

 

 

public with sharing class ProcessorControl { 
	public static boolean inFutureContext = false;
	
}

 

  

k_bentsenk_bentsen

Sorry for the late reply. Here are a few comments:

 

1. I'm not sure why you're splitting the account name by the "-" and then putting it back together before adding it to your list of string names. In your code below, I had thought to do something more along the lines of:

 

prefixnames.add(part1);

 

2. Also, I would suggest creating a map something along the lines of:

 

map<String, String> AlertsMap = new Map<String, String>();

 

Then within your loop, you do:

 

AlertsMap.add(part1, myacc.Alert__c)

 

This way, you'll have the Alert__c value at the ready for all MyAccount__c records with a matching prefix.

 

3. Your SOQL query is not going to work. You cannot use the LIKE operator on a list of strings. It must be a hardcoded string such as LIKE 'Dave%' or possibly a single string value, such a String str = 'Dave%', LIKE :str but I've never tried/tested this way so I'm not sure. This is why I suggested to use dynamic SOQL, because you will need to build your SOQL query string with your list of prefixes:

 

String queryStr = 'SELECT Name, (other fields) FROM MyAccount__c where Name ';

for(String s : prefixnames)

  queryStr += 'LIKE \'' + s + '%\' OR Name ';

queryStr = queryStr.substring(0, queryString.length()-9); // chop off the last 'OR Name clause

List<MyAccount__c> myRelAccList = Database.query(queryStr);

 

Keep in mind I'm writing this code in the forum page box, so I can't guarantee accurate syntax, but hopefully you can interpret the behavior I'm trying to relay.

 

4. This is more of a personal preference for aesthetics/cleanliness, but I'm not sure why half-way through your trigger, you move into a processor class. I would just keep everything in the trigger, or move everything into the processor class and just have your trigger be one line:

 

MyAccountProcessor.processAccounts(Trigger.new);

 

Again, just sharing my preference :) Hope this info helps you along.

SkyhSkyh

Thanks  k_bentsen. Much appreciated.

 

I am still struggling.. In the process of rewriting the code.

  

I am splitting the account  name becuase it is in the format  'x-xxxxx-xxxxxxx'      e.g 1-90111-7766554 ,  2-88777-6545654

 

and I want to apply the Alert__c update to all accounts that has the same prefix before the second dash.

 

i.e if i change the alert on 2-88777-6545654, all accounts with the prefix 2-88777 will get the same Alert__c value

 

 

 

k_bentsenk_bentsen

Then then code you have should work just fine for getting the account prefix. In your original post, the naming convention did not include the leading single digit, so I was not accounting for that.

 

What are you still struggling with?

SkyhSkyh

Thank you for all your help. Truly appreciated!!!!

 

Finally I got it. I know there are asthetic blemishes as you pointed out earlier.  

 

If possible can you please go through my code and give some feedback and how i could improve it from functionality point of view and if the code needs improvement in regards to  governor limits.

 

Thanks once again.

 

trigger AlertForAccount on MyAccount__c (after insert, after update) {

    // list of string to hold prefix of mapics account names
    list<string> prefixnames = new list<string>();

    // map to hold prefix name and id of the Alert 
    map<String, String> AlertMap = new map<String, String>();
      
    
    if (!ProcessorControl.inFutureContext) {
    
        // loop through MyAccount__c to compare old and new alert values
        //if not the same then perform prefix truncation
        for (MyAccount__c mapacc: Trigger.New){ 
            
            if(trigger.isUpdate){
            
                MyAccount__c alertBeforeUpdate = System.Trigger.oldMap.get(mapacc.Id); 
                system.debug('What is the value before update: ' +alertBeforeUpdate);
                    
                if(mapacc.Alert__c!=null && mapacc.Alert__c!=alertBeforeUpdate.Alert__c){ 
                     
                    string [] splitparts = mapacc.Name.split('-');
                    system.debug('when split: >>> ' +splitparts);
                    string part1 = splitparts.get(0);
                    system.debug('part1 is>>> ' +splitparts);
                    string part2 = splitparts.get(1);
                    system.debug('part2 is>>> ' +splitparts);
                    string combinefirsttwoparts = part1 + '-' + Part2 + '%';
                    system.debug('==========' +combinefirsttwoparts);
                        
                    // add the prefix to the list 
                    prefixnames.add(combinefirsttwoparts);
                        
                    // also add to the map so we have the  Alert value ready to be referenced in AlertMapicsAccountProcessor class 
                    AlertMap.put(combinefirsttwoparts,mapacc.Alert__c);
                    system.debug('Id of Alert is :' +AlertMap.get(combinefirsttwoparts));
                    
                }
            }
        }
        
        // Ensuring the list and map is not empty
        if (!prefixnames.isEmpty() && !AlertMap.isEmpty()){
        	
            MyAccountProcessor.processAccounts(prefixnames, AlertMap);
        }
    }
}

 

public with sharing class MyAccountProcessor {  
	
	@future
	public static void processAccounts(list<String> names, Map<string,String> rmaalert) {
		
		
          // list to store the My accounts to update
          List<MyAccount__c> MyaccountsToUpdate = new List<MyAccount__c>();
          
          // iterate through the list of My accounts to process
          for (MyAccount__c a : [Select Id, Name,Alert__c From MyAccount__c where Name like :names]) {
          	
               string [] truncateagain = a.Name.split('-'); 
               string tpart1 = truncateagain.get(0);
               string tpart2 = truncateagain.get(1);
               string addfirsttwoparts = tpart1 + '-' + tPart2 + '%';
               
               a.Alert__c = rmaalert.get(addfirsttwoparts);
               
               // add the My accounts to the list to update
               MyaccountsToUpdate.add(a);
          }
         
          ProcessorControl.inFutureContext = true;
          // update the My accounts
            if(!MyaccountsToUpdate.isEmpty()){
          	update MyaccountsToUpdate;
          }
    }
}

 

public with sharing class ProcessorControl { 
	public static boolean inFutureContext = false;
	
}

 

 

 

k_bentsenk_bentsen

I'm definitely glad to have helped. I take it that this trigger is functioning as intended? If so, then I'll take away something from this in that I had no idea you could use a list of strings on the right side of a LIKE operator.

 

Otherwise, everything looks good. You have no SOQL or DML inside loops, which is generally the biggest concern for triggers. 

SkyhSkyh

Yes so far so good. it is working as intended.

 

Thanks for all your help.