+ Start a Discussion
cbrocbro 

TRIGGER - attempt to Create a New Record on Custom Object when field on Standard Object changes.

I am trying to get this trigger to fire a new record on the custom object SVOC__c every time the field Account.SVOC__c is filled in OR changed with a number/text.  or goes from nothing to something. Also, this field should always be unique (it already is set to "do not allow duplicate values"  -->  but i need to know if someone is trying to create the same number/text combination.

 

It compiles fine, and it runs, but is not creating the new record.

 

In debugging the trigger is not going through past the second debug method.

 

Any help would be greatly appreciated.

 

Thanks,

Chris 

 

trigger AccountSVOC on Account (after update) 
{
    List <SVOC__c> SVOCs = new List <SVOC__c> ();
    Map <Id, Account> oldmap = new Map <Id, Account>();
    Map <Id, Account> newmap = new Map <Id, Account>();

System.debug('Chris in trigger');

    for(Account a: Trigger.new) 
    {
System.debug('Chris in account loop');
     if ((Trigger.oldMap.get(a.Id).SVOC__c != Trigger.newMap.get(a.Id).SVOC__c) && Trigger.newMap.get(a.Id).SVOC__c != null )
        { 
System.debug('Chris in status changed');
        SVOC__c SVOC = new SVOC__c ();
        SVOC.SVOC_ID__c = a.Id;

System.debug('Chris creating new SVOC and adding to list');
        SVOCs.add(SVOC);
        }
    }

    if(SVOCs.size()>0)
    {
System.debug('Chris has values to insert = '+ SVOCs.size());
     try{
        insert SVOCs;
        }catch (System.Dmlexception e)  
             {
             system.debug (e); 
             }
    }

}

Juan SpagnoliJuan Spagnoli

Just cleaning up the trigger..

 

trigger AccountSVOC on Account (after update) 
{
    List <SVOC__c> SVOCs = new List <SVOC__c> ();
   
    System.debug('Chris in trigger');

    for(Account a: Trigger.new) 
    {
        System.debug('Chris in account loop');
        if( (a.SVOC__c != null ) && (a.SVOC__c != Trigger.oldMap.get(a.Id).SVOC__c) )
        { 
            System.debug('Chris in status changed');
            SVOC__c SVOC = new SVOC__c ();
            SVOC.SVOC_ID__c = a.Id;

            System.debug('Chris creating new SVOC and adding to list');
            SVOCs.add(SVOC);
        }
    }

    if(SVOCs.size()>0)
    {
        System.debug('Chris has values to insert = '+ SVOCs.size());
        try{
            insert SVOCs;
        }catch (System.Dmlexception e)  
        {
             system.debug (e); 
        }
    }
}

 

Try it that way.

 

Best Regards.

cbrocbro

So it's working fine...  for sure

 

In the case that it is a duplicate Account.SVOC__c, it doesn't create a new SVOC__c Record (b/c the SVOC__c custom object has the SVOC__c.SVOC_ID__c custom field set to not allow duplicate values) - which is great!!!

 

However:

 

I need to return an error to the Account when trying to save something that is already being used by the SVOC__c custom object at the Account.SVOC__c field.  or - return an error that states that "SVOC is currently in use, please choose a unique SVOC number"... 

 

Is there a way that I can do that?

 

Thanks in advance for all of your time & help,

Chris

Vinit_KumarVinit_Kumar

Hi Chris.

 

Please try below :-

 

trigger AccountSVOC on Account (after update)
{
List <SVOC__c> SVOCs = new List <SVOC__c> ();
Map<Id,Account> accMap = new Map<Id,Account>([select SVOC__c from Account]);

System.debug('Chris in trigger');

for(Account a: Trigger.new)
{
System.debug('Chris in account loop');
if( (a.SVOC__c != null ) && (a.SVOC__c != Trigger.oldMap.get(a.Id).SVOC__c) )
{
if(!accMap.containsKey(a.SVOC__c)){
System.debug('Chris in status changed');
SVOC__c SVOC = new SVOC__c ();
SVOC.SVOC_ID__c = a.Id;

System.debug('Chris creating new SVOC and adding to list');
SVOCs.add(SVOC);
}
else
a.addError('Please select a different SVOC');
}
}

if(SVOCs.size()>0)
{
System.debug('Chris has values to insert = '+ SVOCs.size());
try{
insert SVOCs;
}catch (System.Dmlexception e)
{
system.debug (e);
}
}
}

Juan SpagnoliJuan Spagnoli

But Chris, you said that Account.SVOC__c is an unique field, right? So it isn't a lookup field.. I think with that unique attribute is not possible that someone save an Account with a repeated value, therefore this trigger will never be execute in that case.

 

I'm right?

 

Regards

cbrocbro

Account.SVOC__c is not a unique field, which is why I want to create this 1:1 SVOC__c custom object on the backend that holds a unique field (being pulled from the Account object (Account.SVOC__c field)).  The Account object has reached its limit of 3 unique IDs, unfortunatelty - and I can't remove any of these unique IDs (or make any of these existing fields not unique).

Juan SpagnoliJuan Spagnoli

Ahhh ok! You can ask to support for more unique's fields logging a case.  Do you know that?

 

One question, What type is the Account.SVOC__c field? Is a Text Field?

cbrocbro

Yes, text field.  I can just ask SFDC support for more unique fields?  lol...

cbrocbro

@Vinit

 

This code is not working... (it's good to know this solution for future cases, anyway where I want to do this).  I would like to solve this issue - even though the solution seems to be just to ask SFDC support for more unique IDs.  lol.

 

"Error:Apex trigger AccountSVOC caused an unexpected exception, contact your administrator: AccountSVOC: execution of AfterUpdate caused by: System.StringException: Invalid id: 13579: External entry point"

 

Plus it's, on existing records, it's now returning the SVOC__c Custom Object's SFDC Id to the Account - and it needs to return the SVOC text, not the Id.

 

:)

Chris

Juan SpagnoliJuan Spagnoli

Yes, you can. :D

 

Anyway... be very careful if you are saving IDs in Text Field, is not the same. SOQL isn't case sensitive so if you execute a query filtering by this field, you can get unexpected results. For example:

 

//Supose myIdList has ['006Q000000BJRKQ','006Q00000b00RKQ']

List<MyObject__c> objects = [Select Name, MyTextField From MyObject Where MyTextField IN :myIdList];

 I could get :

 

[{Name='record1',MyTextField='006Q000000BJRKQ'},{Name='record2',MyTextField='006q000000bJRKQ'},,{Name='record3',MyTextField='006q000000bJrKQ'}.........]

 

So you cannot trust the query, you will need to re-filter in Apex (Apex is CaseSensitive)

 

cbrocbro

Here is the code I'm using now, which is working, but again - is not a 1:1 relationship.  

 

If I update the Account.SVOC__c field, it creates a NEW SVOC__c Record.

 

I only want to be able to create 1 of these SVOC__c records per Account.  

 

If there is an update, I just want it to update the existing SVOC__c Record.

 

Thanks in advance for any more help.

 

FYI:

Account.SVOC__c = Text Field

SVOC__c.SVOC_ID__c = Text Field (Unique ID) 

 

Which is why the code is written as it is below.

 

 

My code:

trigger AccountSVOC on Account (after update) 
{
    List <SVOC__c> SVOCs = new List <SVOC__c> ();
   
    System.debug('Chris in trigger');

    for(Account a: Trigger.new) 
    {
        System.debug('Chris in account loop');
        if( (a.SVOC__c != null ) && (a.SVOC__c != Trigger.oldMap.get(a.Id).SVOC__c) )
        { 
            System.debug('Chris in status changed');
            SVOC__c SVOC = new SVOC__c ();
            SVOC.Account__c = a.Id;
            SVOC.SVOC_ID__c = a.SVOC__c;

            System.debug('Chris creating new SVOC and adding to list');
            SVOCs.add(SVOC);
        }
    }

    if(SVOCs.size()>0)
    {
        System.debug('Chris has values to insert = '+ SVOCs.size());
        try{
            insert SVOCs;
        }catch (System.Dmlexception e)  
        {
             system.debug (e); 
        }
    }
}

 

cbrocbro

Thanks Juan, 

 

I have requested a new Unique ID field - with business case, lol.  They really don't want to give it up, huh?  

 

Anyway - see my new and improved request above.  Any more help would be appreciated...

 

Thanks a million!

Juan SpagnoliJuan Spagnoli

It's really complicate, you have to consider a lot of things..

 

If I update the Account.SVOC__c field, it creates a NEW SVOC__c Record.

If the SVOC__c doesn't exist. 

 

I only want to be able to create 1 of these SVOC__c records per Account.  

You can check it on the Before Insert event, you could query if there is another Account with the same SVOC___c.

  

If there is an update, I just want it to update the existing SVOC__c Record.

But what happens when the SVOC__c value is changed to another value? What happens with the old SVOC record?, and what do you have to do with the new one? What happens if the account is deleted?

 

You have a lot of event that you need to take care for keeping the data integrity

cbrocbro

It's really complicate, you have to consider a lot of things..  yes, agreed

 

If I update the Account.SVOC__c field, it creates a NEW SVOC__c Record.

If the SVOC__c doesn't exist. Yes.

 

I only want to be able to create 1 of these SVOC__c records per Account.  

You can check it on the Before Insert event, you could query if there is another Account with the same SVOC___c.  That sounds like a good idea, how would I do that?  I'm sorry for so many questions.  I am still a noob, for sure.

  

If there is an update, I just want it to update the existing SVOC__c Record.

But what happens when the SVOC__c value is changed to another value? It should change on the existing SVOC object to whatever it has been changed to on the Account.  

 

What happens with the old SVOC record? "There can be only one!" (Highlander - at 1:40 or so), so there shouldn't be an old SVOC record... I mean that's what I want to make happen.

 

and what do you have to do with the new one? Nothing, there should not be a new one, it should update the existing SVOC Record.

 

What happens if the account is deleted?  The SVOC Record should be deleted.  :)

 

You have a lot of event that you need to take care for keeping the data integrity.  Yes

 

 

sobroachsobroach

Try comparing maps:

if( Trigger.newMap.get(a,Id).SVOC__c != null && (Trigger.newMap.get(a,Id).SVOC__c != Trigger.oldMap.get(a.Id).SVOC__c) )

 

If you don't want it to run more than once,  add a boolean to a public class where you keep constants.  

 

i.e.

 

public class Constants

{

public static boolean THISTRIGGERNAME_FIRST_RUN = True; 

 

}

 

at the end of your trigger (before closing) set:

THISTRIGGERNAME_FIRST_RUN = False;

 

if(!THISTRIGGERNAME_FIRST_RUN){
}else{
trigger AccountSVOC on Account (after update) { List <SVOC__c> SVOCs = new List <SVOC__c> (); System.debug('Chris in trigger'); for(Account a: Trigger.new) { System.debug('Chris in account loop'); if( Trigger.newMap.get(a,Id).SVOC__c != null && (Trigger.newMap.get(a,Id).SVOC__c != Trigger.oldMap.get(a.Id).SVOC__c) ) { System.debug('Chris in status changed'); SVOC__c SVOC = new SVOC__c (); SVOC.Account__c = a.Id; SVOC.SVOC_ID__c = a.SVOC__c; System.debug('Chris creating new SVOC and adding to list'); SVOCs.add(SVOC); } } if(SVOCs.size()>0) { System.debug('Chris has values to insert = '+ SVOCs.size()); try{ insert SVOCs; }catch (System.Dmlexception e) { system.debug (e); }
THISTRIGGERNAME_FIRST_RUN = False; } }

 

cbrocbro

When I try the above with this in my Constants class:

 

public class Constants{
public static boolean ASSET_FIRST_RUN = True;
private static boolean ACCOUNTSVOC_FIRST_RUN = True;
}

 I get the error: Error: Compile Error: unexpected token: private at line 1 column 0

 

 

 

I've tried the following

with this in my Constants Class:

 

public class Constants{
public static boolean ASSET_FIRST_RUN = True;
public static boolean ACCOUNTSVOC_FIRST_RUN = True;
}

 

Here is my code, but it's still duplicating whenever I edit an existing Account.SVOC__c:

 

trigger AccountSVOC on Account (after update) 
{
    List <SVOC__c> SVOCs = new List <SVOC__c> ();
   
    System.debug('Chris in trigger');
    if(Constants.ACCOUNTSVOC_FIRST_RUN == False){ }else 
    for(Account a: Trigger.new) 
    {
        System.debug('Chris in account loop');
        if( Trigger.newMap.get(a.Id).SVOC__c != null && (Trigger.newMap.get(a.Id).SVOC__c != Trigger.oldMap.get(a.Id).SVOC__c) )
        { 
            System.debug('Chris in status changed');
            SVOC__c SVOC = new SVOC__c ();
            SVOC.Account__c = a.Id;
            SVOC.SVOC_ID__c = a.SVOC__c;

            System.debug('Chris creating new SVOC and adding to list');
            SVOCs.add(SVOC);
        }
        Constants.ACCOUNTSVOC_FIRST_RUN = False;  
    }

    if(SVOCs.size()>0)
    {
        System.debug('Chris has values to insert = '+ SVOCs.size());
        try{
            insert SVOCs;
        }catch (System.Dmlexception e)  
        {
             system.debug (e); 
        }
    }
}

 

 

 

 

sobroachsobroach

You need to check that it is True or don't let it run.  

 

if(!Constants.ACCOUNTSVOC_FIRST_RUN){ //if it isn't the first run - do nothing
}else{ //otherwise run

for(Account a: Trigger.new) 
    {
        System.debug('Chris in account loop');
        if( Trigger.newMap.get(a.Id).SVOC__c != null && (Trigger.newMap.get(a.Id).SVOC__c != Trigger.oldMap.get(a.Id).SVOC__c) )
        { 
            System.debug('Chris in status changed');
            SVOC__c SVOC = new SVOC__c ();
            SVOC.Account__c = a.Id;
            SVOC.SVOC_ID__c = a.SVOC__c;

            System.debug('Chris creating new SVOC and adding to list');
            SVOCs.add(SVOC);
        }
        //then change it to false within the context of this trigger so it doesn't run again
} if(SVOCs.size()>0) { System.debug('Chris has values to insert = '+ SVOCs.size()); try{ insert SVOCs; }catch (System.Dmlexception e) { system.debug (e); }
      Constants.ACCOUNTSVOC_FIRST_RUN = False;
    } 

}

cbrocbro

 Hello,

 

I am still getting duplicate SVOC Records...  

  • Original is not being updated, but the new duplicate has the correct info.
  • If I set the class to 'ACCOUNTSVOC_FIRST_RUN = True' - instead of 'False'--> it will update an existing SVOC Record, but if one does not exist, it will not create one.

 

 

public class:

 

public class Constants{
public static boolean ASSET_FIRST_RUN = True;
public static boolean ACCOUNTSVOC_FIRST_RUN = False;
}

 

 

 

Trigger:

 

trigger AccountSVOC on Account (after update) 
{
    List <SVOC__c> SVOCs = new List <SVOC__c> ();
   
    System.debug('Chris in trigger');
    if(Constants.ACCOUNTSVOC_FIRST_RUN)//if it isn't the first run - do nothing
    { }else //otherwise run
    for(Account a: Trigger.new) 
    {
        System.debug('Chris in account loop');
        if( Trigger.newMap.get(a.Id).SVOC__c != null && (Trigger.newMap.get(a.Id).SVOC__c != Trigger.oldMap.get(a.Id).SVOC__c) )
        { 
            System.debug('Chris in status changed');
            SVOC__c SVOC = new SVOC__c ();
            SVOC.Account__c = a.Id;
            SVOC.SVOC_ID__c = a.SVOC__c;

            System.debug('Chris creating new SVOC and adding to list');
            SVOCs.add(SVOC);
        }
        //then change it to false within the context of this trigger so it doesn't run again
        Constants.ACCOUNTSVOC_FIRST_RUN = False;  
    }

    if(SVOCs.size()>0)
    {
        System.debug('Chris has values to insert = '+ SVOCs.size());
        try{
            insert SVOCs;
        }catch (System.Dmlexception e)  
        {
             system.debug (e); 
        }
    }
}

 

Juan SpagnoliJuan Spagnoli

Chris, about this topic:

 

You can check it on the Before Insert event, you could query if there is another Account with the same SVOC___c.  That sounds like a good idea, how would I do that?  I'm sorry for so many questions.  I am still a noob, for sure.

 

SVOC___c contains an ID or a alphanumeric text? Is it CaseSensitive or "AAA" is the same of "aaa"?

cbrocbro

SVOC__c is an alphanumeric text field... on the Account object.  It is an alphnumeric key assigned somewhere else (a parent company) - and given to us.  However, that is a whole other story that I will be working to automate in the future...  

 

It is not currently case sensitive - "AAA" is the same as "aaa".  

 

At this point, I don't have the reqs on whether or not it needs to be - but, it is something that I will find out.

 

But, that being said, for now it will remain case insensitive.

 

Thanks,
Chris

Juan SpagnoliJuan Spagnoli

I did it in a notepad, so maybe you will have to correct the syntaxis:

 

trigger AccountSVOC on Account (before insert, before update, after update) 
{
	//BEFORE SECTION
	if(trigger.IsBefore)
    	{
		Set<String> svocs = new Set<String>();
		Map<String, Account> accountsBySvoc = new Map<String, Account>();

		for(Account a: Trigger.new) 
		{
			if(a.SVOC__c != null){
				if(svocs.contains(a.SVOC__c)){
					//We have 2 different records with same SVOC in the trigger!
					throw new MyException('ERROR MESSAGE');	
				}
				svocs.add(a.SVOC__c);	
				accountsBySvoc.put(a.SVOC__c, a);		
			}
  		}

		List<Account> accounts = [Select SVOC__c From Account Where SVOC__c IN :svocs];
		if(accounts.size() > 0){
			//can be repeated
			if(trigger.IsInsert){
				//are repeated for sure
				throw new MyException('ERROR MESSAGE');			
			}else{
				//check if it not the same record
				for(Account a :accounts){
					Account triggerAccount = accountsBySvoc.get(a.SVOC__c);
					if(a.ID != triggerAccount.ID){
						//Is not same object, It's repeated
						throw new MyException('ERROR MESSAGE');	
					}				
				}
			}
		}
    	}
	else
	//AFTER SECTION
	{

	    List <SVOC__c> SVOCs = new List <SVOC__c> ();
	   
	    System.debug('Chris in trigger');

	    for(Account a: Trigger.new) 
	    {
		System.debug('Chris in account loop');
		if( (a.SVOC__c != null ) && (a.SVOC__c != Trigger.oldMap.get(a.Id).SVOC__c) )
		{ 
		    System.debug('Chris in status changed');
		    SVOC__c SVOC = new SVOC__c ();
		    SVOC.Account__c = a.Id;
		    SVOC.SVOC_ID__c = a.SVOC__c;

		    System.debug('Chris creating new SVOC and adding to list');
		    SVOCs.add(SVOC);
		}
	    }

	    if(SVOCs.size()>0)
	    {
		System.debug('Chris has values to insert = '+ SVOCs.size());
		try{
		    insert SVOCs;
		}catch (System.Dmlexception e)  
		{
		     system.debug (e); 
		}
	    }
	}
}

 It's make sense to you?

 

PD: You will need to create a class "MyException" (or whatever) extended from Standard Exception Class

cbrocbro

This works, except that it is still creating duplicate SVOC__c records when the Account.SVOC__c field is changed.

 

Thanks, Juan for the effort you put into this, however there is still not a 1:1 relationship... can anyone help to prevent duplicate records on this field change?  

 

I just want to make sure to update the existing SVOC__c record if this field on the Account changes instead of creating a NEW SVOC__c record.

 

:)