Showing posts with label Triggers. Show all posts
Showing posts with label Triggers. Show all posts

Saturday, July 1, 2017

Field expression not allowed for generic SObject in Salesforce

Scenario:


Usually when we writing any trigger we will be following some best practices like One Trigger for One Object,Business logic in Handler Class ,Don't write the whole code in Trigger etc..This kind of error will receive when we are writing whole trigger logic in Separate Handler Class(Apex class) and we are referring to that trigger context variables  in that Apex class or  referring your sobjects in Trigger handler


Root Cause : 


Trigger Code

Trigger CaseTrigger on Case (Before Insert,Before Update,After insert) 
{
   if(Trigger.isBefore && (Trigger.isInsert || Trigger.isUpdate))
    {
        CaseTriggerHandler.duplicateCheck(Trigger.new);
    }
}

Handler Class

Public class CaseTriggerHandler
{ 
public static void duplicateCheck(List<Case> TriggerNew)
 {
         for (Case cs : TriggerNew) 
        {
           if(cs.status__c!=Trigger.OldMap.get(cs.Id).status__c)
            {
               System.debug('Status has changed');
            }
        }
 }
}

When your trying save above handler code you will end up with Field expression not allowed for generic SObject .


Solution:

The reason behind is that Trigger.OldMap is a generic variable that means it will be used for all objects so when your using this variable you need to tell the compiler what type of the SObject your referring from Trigger.OldMap. I have highlighted that code change in yellow color in below code.
        

Public class CaseTriggerHandler
{ 
public static void duplicateCheck(List<Case> TriggerNew)
 {
         for (Case cs : TriggerNew) 
        {
           if(cs.status__c!=((Map<Id,Case>)Trigger.OldMap).get(cs.Id).status__c)
            {
               System.debug('Status has changed');
            }
        }
 }
}


Thanks for visiting..hope this helps you!

Monday, June 20, 2016

FIELD_INTEGRITY_EXCEPTION, Related To ID: id value of incorrect type: [WhatId]:

We have a trigger on some custom object and it would sent an email notification based on few changes to record.When we are sending an email with help of Messaging.SingleEmailMessage class and we are also using an email template so to populate the merge fields in template we are setting the whatId to custom object which full fills the merge fields in template but unfortunately we are end up with an below error



Error: Invalid Data. 
Review all error messages below to correct your data.
Apex trigger SummaryTrigger caused an unexpected exception, contact your administrator: SummaryTrigger: execution of AfterInsert caused by: System.EmailException: SendEmail failed. First exception on row 0; first error: FIELD_INTEGRITY_EXCEPTION, Related To ID: id value of incorrect type: a0JJ000000A6TDWMA3: [WhatId]: Class.SummaryTrigHandler.sendEmailNotification: line 101, column 1

Root Cause:
Even after I am setting the correct WhatId I am getting the same error .This issue is not related to object WhatId it's actually related to Activity record whatId.
Because by default the setSaveAsActivity has a value true, which saves the emailing as an activity so If your WhatId custom object not allowed for activity tracking then you will receive this error.

Solution:

  • So if your facing the same issue please goto object detail page (i.e.,the object that your setting as part of whatId) and enable the activities for the object
  • Change in your code like do not save the activity by setting the setSaveAsActivity as false Messaging.SingleEmailMessage email = new Messaging.SingleEmailMessage();  email.setSaveAsActivity(false)



Thanks for visiting....Enjoy!

Thursday, March 17, 2016

Trigger to Auto Populate Contact Mailing Address with Related Account Billing Address

I want to populate Mailing address of contact based on the Billing Address of the related Account of the particular contact.Please find the below source code to do this.

Trigger Code
Trigger CopyBillAddres on Contact(Before insert , before update)
{
 Set<Id> AccountIds =new Set<Id>();
 Map<id,account> mapAcc = new Map<id,account>();
 for(Contact con :Trigger.new)
 { 
    AccountIds.add(con.AccountId);
 }
 
 for(account a : [select id,BillingStreet,BillingCity,BillingState,BillingPostalCode,BillingCountry from account where id in : AccountIds])
 {
    mapAcc.put(a.id,a);
 }
 
 for(Contact con :Trigger.new)
 { 
    con.MailingStreet = mapAcc.get(con.AccountId).BillingStreet;
    con.MailingCity = mapAcc.get(con.AccountId).BillingCity;
    con.MailingState = mapAcc.get(con.AccountId).BillingState;
    con.MailingPostalCode = mapAcc.get(con.AccountId).BillingPostalCode;
    con.mailingCountry = mapAcc.get(con.AccountId).BillingCountry;
}
}

Notes

  • If you want change any field values go with before triggers only.
  • In before triggers if you were making any changes to fields explicitly no need to write DML statement

Enjoy.........!!!

Tuesday, March 1, 2016

Number of Child Record Count on Parent Record Using Trigger

I want to display count of the Contacts related to particular Account on a field in Account object.I want to make sure that it will work properly when I inserted new Contact or Deleted or Undelete also.In this post i'm going to explain how to implement this in Step by Step.

Choose The Right Object for Trigger
If you have doubt on which object I need to write trigger whether it's on Contact or Account object?My answer is blindly Contact because here we are going to track the number of contacts so it means that we need to have a glance on each and every change on a contact object.So we have to write a trigger on Contact Object

Source Code of the Trigger
Trigger ContactCountTrigger on Contact(After insert,After Delete,After Undelete)
{
  Set<Id> setAccountIds = new Set<Id>();
  
  //Whenever your working with After Undelete operation you can access data through 
  //Trigger.new or Trigger.newMap but not with Trigger.old or Trigger.oldmap variables
  if(Trigger.isInsert || Trigger.isUndelete)
  {
   for(Contact con : Trigger.new)
   {
    setAccountIds.add(con.AccountId);
   }
  }
  
  if(Trigger.isDelete)
  {
   //if you use Trigger.new below in place of Trigger.old you will end up with 
   //System.NullPointerException:Attempt to de-reference a null object
   for(Contact con : Trigger.old) 
   {
    setAccountIds.add(con.AccountId);
   }
  }
  
 List<Account> listAccs = [Select id,name,number_of_contacts__c ,(Select id from contacts) from Account where Id in : setAccountIds];
  for(Account acc :listAccs)
  {
   acc.number_of_contacts__c = acc.contacts.size();
  }
  update listAccs;
}


OutPut:




Notes:
1.Whenever your working with Undelete operation in Trigger you have to make sure that you have to use only Trigger.new or Trigger.newMap context variables to get the run time values of records.

2.Apex will treats Undelete operation as an insert operation so if you refer any Trigger.old or Trigger.oldMap you will not get any data so you will end up with an System.NullPointerException:Attempt to de-reference a null object

3. In after delete operation if you refer any Trigger.new you will be end with an same System.NullPointerException:Attempt to de-reference a null object because Trigger.new will support only Insert and Update operation .So we have to Trigger.old here.
Enjoy.......!!!



Monday, February 15, 2016

How to Implement Trigger Switch in Salesforce

Basic limitation of salesfroce we already know that we can't edit the apex code in Production .Recently we came acroos a situavtion where we need to stop the trigger in Production which is not working as expected.The common way to do this is get the trigger to sandbox and deactivate the same trigger in sandbox and then I have to push that same to prod.

But pushing the apex to PROD involves below things
   1.All the test classes will run we have to make sure that none will fail
   2.Code coverage percentage must be 75% or more

All this process is time consuming process and if the impact of that trigger on your business is critical meanwhile you will be losing more profit.So,The best way to stop the trigger immediately is implementing the trigger switch in your organisation

Trigger Switch :

Trigger switch is nothing but it's way to stop the execution of active trigger using the configuarion stuff.The best configuration feature available in salesforce to stop the trigger that we have to use Custom Setting or Custom Metadata Types.

How to Implement Trigger Switch using Custom Settings

1.Create a List custom setting with name Trigger Switch as shown in below image



2. Create two custom fields Active and Trigger Name on clicking on New in Custom Fields section.


3.Create Active field with data type as "Check Box" as shown in image



4.Create Trigger Name field with data type as "Text" as shown in image

5.Make this Trigger Name field as mandatory by selecting Required checkbox  as shown in image


Now custom setting is ready and I want to activate/deactivate one of the trigger in my org lets assume the trigger name is AccountTrigger from Custom settings without touching the active check box at the trigger level.To control this first we need to create a record for that trigger in Custom Setting.

6. Goto Custom Setting--->Click on Manage and give below details
            
            Name              : Account Trigger
            Trigger Name : AccountTrigger
            Active             : Checked

after saving the record it will looks like below            



7. To activate or to deactivate the trigger from custom settings we need to add the below code snippet at the starting of the trigger.

//Mute Trigger using  Custom Setting 
CustomSettingName switchVar = CustomSettingName.getInstance('TriggerName');
if(switchVar != NULL && 'False'.equalsIgnorecase(String.valueOf(switchVar.isActive__c))){ 
       return;
}

AccountTrigger before adding Switch


trigger AccountTrigger on Account (after update,after insert, before insert, before update) {
   
   //After Update Logic
   if (Trigger.isAfter && Trigger.isUpdate) {
    
        AccountUtility.callAfterUpdate(Trigger.New);
        
   } 
   
   //Before Insert or before Update Logic
    if (Trigger.isBefore &&  (Trigger.isUpdate || Trigger.isInsert) ) {
       AccountUtility.callBeforeUpsert(Trigger.New);
        
    }
    
   //After Insert Logic
   if(trigger.isAfter && trigger.isInsert)
   {  
       AccountUtility.callafterinsert(Trigger.New);
   }
    
}

AccountTrigger After adding Switch


trigger AccountTrigger on Account (after update,after insert, before insert, before update) {
   //Mute Trigger using  Custom Setting 
   TriggerSwitch__c switchVar = TriggerSwitch__c.getInstance('AccountTrigger');
   if(switchVar != NULL && 'False'.equalsIgnorecase(String.valueOf(switchVar.isActive__c)))   { 
           return;
    }
    
   //After Update Logic
   if (Trigger.isAfter && Trigger.isUpdate) {
    
        AccountUtility.callAfterUpdate(Trigger.New);
        
   } 
   
   //Before Insert or before Update Logic
    if (Trigger.isBefore &&  (Trigger.isUpdate || Trigger.isInsert) ) {
       AccountUtility.callBeforeUpsert(Trigger.New);
        
    }
    
   //After Insert Logic
   if(trigger.isAfter && trigger.isInsert)
   {  
       AccountUtility.callafterinsert(Trigger.New);
   }
    
}

How to Activate or Deactivate Trigger


To activate or deactivate the trigger from custom setting first add the trigger switch to each and every trigger in your organisation or to few triggers choice is yours.If you want to activate just go to the particular trigger related record in custom setting and check the active checkbox. to deactivate simply go to the trigger related record and unchecked the active checkbox .

Enjoy....!

Thursday, February 4, 2016

How to Delete the Records Permanently from the Recycle Bin in Salesforce

Basically in salesforce when you have deleted any record from an object, it will not be deleted permanently and it will be stored in Recycle bin till 15 days from the deletion date.The number of records stored in recycle bin depends on Number of Licenses your organisation has pursed and multiply it with 5000 records.
If the number of records in recycle bin crosses this limit salesforce automatically deletes the old records from Recycle Bin which are at least in recycle bin for 2 hours.If you want to delete the records permanently from recycle bin before salesforce deletes it you can emptyRecylebin() method.


     EmptyRecycleBinResult[] = DataBase.emptyRecyclebin(List of Ids)

Example:

    1. Account Acc = new Account(Id='0016000000I549m');
        Database.emptyRecycleBin(Acc);


Notes:

1. Maximum number of records per call that we can pass is 200
2. Using this function records deleted can not be undeleted again.
3. Theses deleted records from recycle bin can be accessed using the DataBase.queryAll() for some time typically that would be with in 24 hours or may shorter or longer.

Sunday, January 3, 2016

How to Restrict creation of Contact on Lead Conversion

Here I'm trying to avoid the creation of Contact on Lead conversion.Here the thing is that we can't stop the contact creation on lead conversion because it's standard process .So to achieve this we have an alternative solution is to delete the contact after the lead got converted.

To Delete I have a written a trigger on Lead like below

 trigger noContactOnLeadConversion on Lead (after update)   
 {  
  list<contact> conlead=new list<contact>();  
  for(lead leadRec:Trigger.new)  
  {  
   if(trigger.oldMap.get(leadRec.id)isconverted==false && leadRec.isconverted && leadRec.ConvertedContactId != null)  
   {  
       contact con= new contact(Id=leadRec.ConvertedContactId);  
       conlead.add(con);  
   }  
  }  
  delete conlead;  
 }  





















When I start converting the lead I'm ending up with an error System.DmlException: Delete failed. First exception on row 0 with id 0039000001pvmhtAAA; first error: ENTITY_IS_DELETED, entity is deleted: [] .





















Then I added the logic to stop recursion trigger and tried with before trigger also but still I'm getting the same error with different screen but error is same







WorkAround for the Error 

Move the deletion of contact part to the @future method(i.e.,Asynchronous execution )and use Boolean variable to stop Recursion trigger.Below is the modified code snippet.

Trigger code:
 trigger noContactOnLeadConversion on Lead (after update)   
 {  
 if(!LeadUpdateHandler.stopleadUpdateTrigger)  
 {  
  LeadUpdateHandler.stopleadUpdateTrigger= true;  
  list<contact> conlead=new list<contact>();  
  for(lead leadRec:Trigger.new)  
  {  
   if(trigger.oldMap.get(leadRec.id).isconverted==false && leadRec.isconverted && leadRec.ConvertedContactId != null)  
   {  
    contact con= new contact(Id=leadRec.ConvertedContactId);  
    conlead.add(con);  
   }  
  }  
  LeadUpdateHandler.AsyncExecution(JSON.serialize(conlead));  
  }  
 }  

Handler Class Code:
 global class LeadUpdateHandler  
 {  
   public static boolean stopleadUpdateTrigger = false;  
   @future  
   public static void AsyncExecution(String JsonStr)  
   {  
    List<Contact> conList = new List<Contact>();  
     conList = (List<Contact>)JSON.deserialize(JsonStr, List<Contact>.class);  
     delete conList;  
   }  
 }  

For more update please join this site or just like my Facebook page I Love Coding..You? 

Enjoy.....!

Wednesday, December 30, 2015

Trigger to update Rating field value based on Account Industry field

I'm trying to set Rating field value in Account object to 'Warm' whenever the Industry filed value on Account is Finance or Chemicals or HealthCare.

Possible Solutions:
  1.Using Workflows
  2.Using Triggers

In this post I'm trying to do this with the help of trigger only.Below is code snippet to implement this functionality with the help of trigger.

Source Code:
 trigger updateRatingTrigger on Account (before insert,before update)  
 {  
  if(Trigger.isBefore && (Trigger.isinsert || Trigger.isUpdate))  
  {  
    Set<String> setIndusry = new Set<String>{'Finance','Chemicals','HealthCare'};  
    for(Account acc: Trigger.new)  
    {  
    if(setIndusry.contains(acc.Industry) && acc.Rating!='Warm')  
     acc.Rating= 'Warm';  
    }  
  }  
 }  

Enjoy....!

Monday, June 16, 2014

Trigger Frameworks and Apex Trigger Best Practices

            I’ve read a lot of Apex Trigger code in my career. While I was consulting, I was able to see the good, the bad, and the ugly when it came to Trigger implementations. For the triggers that took a wrong turn at some point, the story was almost always the same. 
            The initial trigger was small, and served a single purpose. Over time, as new business requirements emerged, that once tiny trigger started growing and growing into a monstrosity. New functionality was routinely tacked on and technical debt began to accumulate. Now the logic was hard, if not impossible to follow. It was also becoming increasingly difficult to maintain because even the slightest change might mean rewriting the whole trigger from scratch. The tests were unreliable. Trigger logic was executing out of order and governor limits were getting hit left and right. I took a good look at not only the bad triggers that I saw, but the good ones as well. I started coming up with some best-practices that would help developers avoid some of these pitfalls. So before we get into Trigger Frameworks, which is the subject of this article, let’s first talk about some of the best practices developers should be aware of when it comes to trigger writing.Click Here For More

Friday, May 3, 2013

Bulk Triggers in Salesforce

When you have a need of writing a trigger, it is strongly recommended that you make it bulk enabled, so that the trigger can handle number of records which can be inserted/updated/deleted through data loader or any other data loader.

I will explain how to bulk enable a trigger now in this article. Consider a scenario where you might want to update the opportunity status to "Closed-Won" depending on a Boolean field CloseOpportunities__c on each of the account that has been updated (That is, if you want to close the opportunities related to accounts that are being updated). And you will be uploading hundreds of accounts using a data loader tool.

Here is the trigger.
 Trigger myTrigger on Account(after update)   
 {   
   Set <Id> setAccId = new Set<Id>();   
   for(Account a: Trigger.new)   
   {   
     if(a.CloseOpportunities__c)   
       setAccId.add(a.Id);//set always contains distinct Ids   
   }   
   list <Opportunity> lstOpp = [select Id, StageName from Opportunity where AccountId in : setAccId];   
   for(Integer i = 0; i < lstOpp.size(); i++)   
   {   
     lstOpp[i].StageName = "Closed-Won";   
   }   
   if(lstOpp.size() > 0)   
     update lstOpp;   
 }   

And a more generalized way is something like this

 tirgger MyTrigger on SObject([your events goes here comma seperated])   
 {   
     Set<Id> SetOfIds = new Set<Id>();   
     for(SObject s : trigger.new)   
     {   
        SetOfIds.add(s.idField);   
     }   
     Map<Id,SObject> objMap = new Map<Id,SObject>([select Id, [Other Fields] from SObject where Id in : SetOfIds]);   
     for(SObject s : trigger.new)   
     {   
        //do your processing with map   
     }   
 }