function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
Kev BrierKev Brier 

Need to validate a Trigger to Insert a Record

Hi,

We've been working on a trigger which creates a contract upon an Opportunity being complete and 'Closed Won'. We've managed to get the record to create and copy over important info, although we can't manage to figure the validation logic. Granted we have little apex knowledge and therefore we've attempted to put most of the logic outside the trigger and within a workflowed checkbox on the Opportunity.

However, this don't eliminate the duplication of records if the user edits a Won opportunity. I know the better versed Salesforce developer would have the list check the record doesn't exist in the first place before the trigger even starts and therefore we're reaching out hoping you guys could support us in finishing this piece of work.

Any help or advice would be gratefully appreciated. 

Kev
Best Answer chosen by Kev Brier
Mahesh DMahesh D
trigger OpportunityTriggerForContract on Opportunity (after insert, after update) {
    
    Map<String, Schema.SObjectType> scheamGlobalDescription = Schema.getGlobalDescribe(); 
    Schema.SObjectType sObjectType = scheamGlobalDescription.get('Opportunity');
    Schema.DescribeSObjectResult sObjectDecription = sObjectType.getDescribe();
    Map<String, Schema.RecordTypeInfo> recordTypeNameMap = sObjectDecription.getRecordTypeInfosByName();
    String rtId = recordTypeNameMap.get('Your RecordType Name').getRecordTypeId();
   List<Contract> conList = new List<Contract>();
   for (Opportunity opp: Trigger.new){
       if(opp.StageName == 'Closed Won' && opp.RecordTypeID == rtId && opp.Record_Complete__c && (Trigger.isInsert || opp.StageName != Trigger.oldMap.get(opp.Id).StageName)) {
           if(opp.AccountId != null) {
               conList.add(new Contract(Name=opp.Name, AccountId = opp.AccountId, CurrencyIsoCode = opp.CurrencyIsoCode, StartDate = opp.Service_Start_Date__c, Contract_Value__c = opp.Total_Performance__c, Description = opp.Opportunity_Description__c, Sales_Lead__c = opp.OwnerID));
           }
       }
   }
   
   // Check if the Contract List is empty or not and insert them accordingly.
   if(!conList.isEmpty()) {
       insert conList;
   }
}

Take this code.

Regards,
Mahesh

All Answers

venkat-Dvenkat-D
In the trigger in Opportunity, check for stage change and new stage is Closed Won

For(Opportunity opp  : Trigger.New){

if (opp.StageName != Trigger.oldMap.get(opp.Id).StageName && opp.StageName == 'Closed Won'){
//your logic to insert contacts
}
}
Mahesh DMahesh D
Hi Kev,

Please check the below code:
 
//Trigger on Opportunity to handle all after insert / update actions.
trigger OpportunityTriggerForContract on Opportunity (after insert, after update) {
    List<Contract> conList = new List<Contract>();
    //Iterating through the input records.
    for (Opportunity opp: Trigger.new){
        // Checking is the Stage is Closed Won 
        // Here if it is update then it will execute only the StageName got changed as part of update.
        if(opp.StageName == 'Closed Won' && (Trigger.isInsert || opp.StageName != Trigger.oldMap.get(opp.Id).StageName)) {
            //Check if opportunity has an account or not.
            if(opp.AccountId != null) {
                // Create a Contract record and add it to the Contract List.
                conList.add(new Contract(Name=opp.Name, AccountId = opp.AccountId));
            }
        }
    }
    
    // Check if the Contract List is empty or not and insert them accordingly.
    if(!conList.isEmpty()) {
        insert conList;
    }
}

I added the comment so that it will be easy for you to understand.
Also tested the above code in my DE environment and everything looks good.

Please do let me know if it helps you.


Regards,
Mahesh


 
Kev BrierKev Brier
Thanks for your comments Venky but unfortunately your addition didn't work for us.

Thank you Mahesh - Your code works perfectly but when we add in our additional logic in the IF for whatever reason the record fails to create, please can you check our amended code attached. Sure we're overlooking something relatively basic.
 
trigger OpportunityTriggerForContract on Opportunity (after insert, after update) {
   List<Contract> conList = new List<Contract>();
   for (Opportunity opp: Trigger.new){
       if(opp.StageName == 'Closed Won' && (Trigger.isInsert || opp.StageName != Trigger.oldMap.get(opp.Id).StageName) && opp.RecordTypeID == '012N00000004pSi' && opp.Record_Complete__c == TRUE) {
           if(opp.AccountId != null) {
               conList.add(new Contract(Name=opp.Name, AccountId = opp.AccountId, CurrencyIsoCode = opp.CurrencyIsoCode, StartDate = opp.Service_Start_Date__c, Contract_Value__c = opp.Total_Performance__c, Description = opp.Opportunity_Description__c, Sales_Lead__c = opp.OwnerID));
           }
       }
   }
   
   // Check if the Contract List is empty or not and insert them accordingly.
   if(!conList.isEmpty()) {
       insert conList;
   }
}

  
Mahesh DMahesh D
Hi Kev,

Please try the below code:
 
trigger OpportunityTriggerForContract on Opportunity (after insert, after update) {
    
    Map<String, Schema.SObjectType> scheamGlobalDescription = Schema.getGlobalDescribe(); 
    Schema.SObjectType sObjectType = scheamGlobalDescription.get('Opportunity');
    Schema.DescribeSObjectResult sObjectDecription = sObjectType.getDescribe();
    Map<String, Schema.RecordTypeInfo> recordTypeNameMap = sObjectDecription.getRecordTypeInfosByName();
    String rtId = recordTypeNameMap.get('Your RecordType Name').getRecordTypeId();
   List<Contract> conList = new List<Contract>();
   for (Opportunity opp: Trigger.new){
       if(opp.StageName == 'Closed Won' && opp.RecordTypeID == rtId && opp.Record_Complete__c && (Trigger.isInsert || opp.StageName != Trigger.oldMap.get(opp.Id).StageName)) {
           if(opp.AccountId != null) {
              // conList.add(new Contract(Name=opp.Name, AccountId = opp.AccountId, CurrencyIsoCode = opp.CurrencyIsoCode, StartDate = opp.Service_Start_Date__c, Contract_Value__c = opp.Total_Performance__c, Description = opp.Opportunity_Description__c, Sales_Lead__c = opp.OwnerID));
           }
       }
   }
   
   // Check if the Contract List is empty or not and insert them accordingly.
   if(!conList.isEmpty()) {
       insert conList;
   }
}

Please do let me know if it helps you.

Regards,
Mahesh
Mahesh DMahesh D
trigger OpportunityTriggerForContract on Opportunity (after insert, after update) {
    
    Map<String, Schema.SObjectType> scheamGlobalDescription = Schema.getGlobalDescribe(); 
    Schema.SObjectType sObjectType = scheamGlobalDescription.get('Opportunity');
    Schema.DescribeSObjectResult sObjectDecription = sObjectType.getDescribe();
    Map<String, Schema.RecordTypeInfo> recordTypeNameMap = sObjectDecription.getRecordTypeInfosByName();
    String rtId = recordTypeNameMap.get('Your RecordType Name').getRecordTypeId();
   List<Contract> conList = new List<Contract>();
   for (Opportunity opp: Trigger.new){
       if(opp.StageName == 'Closed Won' && opp.RecordTypeID == rtId && opp.Record_Complete__c && (Trigger.isInsert || opp.StageName != Trigger.oldMap.get(opp.Id).StageName)) {
           if(opp.AccountId != null) {
               conList.add(new Contract(Name=opp.Name, AccountId = opp.AccountId, CurrencyIsoCode = opp.CurrencyIsoCode, StartDate = opp.Service_Start_Date__c, Contract_Value__c = opp.Total_Performance__c, Description = opp.Opportunity_Description__c, Sales_Lead__c = opp.OwnerID));
           }
       }
   }
   
   // Check if the Contract List is empty or not and insert them accordingly.
   if(!conList.isEmpty()) {
       insert conList;
   }
}

Take this code.

Regards,
Mahesh
This was selected as the best answer
Kev BrierKev Brier
Apologies Mahesh - Does seem that we still hit this error upon attempting to save the Opportunity.

Error: Invalid Data. 
Review all error messages below to correct your data.
Apex trigger OpportunityTriggerForContract caused an unexpected exception, contact your administrator: OpportunityTriggerForContract: execution of AfterInsert caused by: System.NullPointerException: Attempt to de-reference a null object: Trigger.OpportunityTriggerForContract: line 7, column 1


Any further help you could offer would be gratefully received?

Thanks,

Kev
Mahesh DMahesh D
hi Kev,

Did you change the Record Type Name with your record type name.

Also paste the code here.

Regards,
Mahesh
Mahesh DMahesh D
String rtId = recordTypeNameMap.get('Your RecordType Name').getRecordTypeId();

here you have to place your record type name.
Kevin BrierKevin Brier
Apologies Mahesh - Our fault we didn't spot that, made the change, the Opportunity record can now be saved but the Contract isn't inserting.

Amended code below. 

trigger OpportunityTriggerForContract on Opportunity (after insert, after update) {
   
    Map<String, Schema.SObjectType> scheamGlobalDescription = Schema.getGlobalDescribe();
    Schema.SObjectType sObjectType = scheamGlobalDescription.get('Opportunity');
    Schema.DescribeSObjectResult sObjectDecription = sObjectType.getDescribe();
    Map<String, Schema.RecordTypeInfo> recordTypeNameMap = sObjectDecription.getRecordTypeInfosByName();
    String rtId = recordTypeNameMap.get('Region 2').getRecordTypeId();
   List<Contract> conList = new List<Contract>();
   for (Opportunity opp: Trigger.new){
       if(opp.StageName == 'Closed Won' && opp.RecordTypeID == rtId && opp.Record_Complete__c && (Trigger.isInsert || opp.StageName != Trigger.oldMap.get(opp.Id).StageName)) {
           if(opp.AccountId != null) {
               conList.add(new Contract(Name=opp.Name, AccountId = opp.AccountId, CurrencyIsoCode = opp.CurrencyIsoCode, StartDate = opp.Service_Start_Date__c, Contract_Value__c = opp.Total_Performance__c, Description = opp.Opportunity_Description__c, Sales_Lead__c = opp.OwnerID));
           }
       }
   }
  
   // Check if the Contract List is empty or not and insert them accordingly.
   if(!conList.isEmpty()) {
       insert conList;
   }
}
 
Mahesh DMahesh D
Hi Kev,

Few things to remember while testing this code:

(1) Make sure that all the given conditions are satisfied, means
      If you are creating the Opportunity:
            ==> StageName should be Closed Won
            ==> RecordType should be Region 2.
            ==> Record_Complete__c should be true.
            ==> Make sure that Opportunity is having Account.
     If you are updating the existing Opportunity:
           ==> StageName should change from any other stage name to Closed Won, bcoz the code works only if there is a change in the StageName and that to new value should be Closed Won.
           ==> RecordType should be Region 2.
            ==> Record_Complete__c should be true.
            ==> Make sure that Opportunity is having Account.

Regards,
Mahesh
Kevin BrierKevin Brier
Thanks Mahesh, the issue here was the workflowed checkbox and the fact that this didn't update in parallel with the record saving.

If manually checked the trigger fires and record is created.

Appreciate all your support here and will mark as best answer.

Kev