+ Start a Discussion
Christine KleinChristine Klein 

Help with Contracts Trigger

I'm updaing the contracts object for our org.  There is a section on the object about Contact Details such as First Name, Last Name, Email, Title, Phone and Fax.  Currently these are just text fields where a rep has to enter the data.

We are adding a lookup field called Existing Contact where the rep can search for a contact in Salesforce.  If the contact exists then populate the contact detail fields with information.  If the information doesn't exist on the contact record then we still need to leave the contact details free text so the rep can manually enter information (I've tried to get them to update the contact record but apparently that's too much work).

Below is the code that I have at the moment.  It works fine when you're updating an existing contract but when you create a new contract I get the following error:

Apex trigger ContractsTrigger caused an unexpected exception, contact your administrator: ContractsTrigger: execution of BeforeInsert caused by: System.ListException: List index out of bounds: 0: Class.ContractsTriggerHandler.getContactValues: line 86, column 1.

Any help would be greatly appreciated.

public with sharing class ContractsTriggerHandler {
    private boolean triggerIsExecuting = false;
   
    private Contract contracts = null;
    private integer batchSize = 0;

  public boolean isTriggerContext{
        get { return triggerIsExecuting;}
    }
   
    public boolean isVisualforcePageContext{
        get { return !isTriggerContext;}
    }
   
    public boolean isWebServiceContext{
        get { return !isTriggerContext;}
    }
   
    public boolean isExecuteAnonymousContext{
        get { return !isTriggerContext;}
    }

   public ContractsTriggerHandler(boolean isExecuting, integer size){
     triggerIsExecuting = isExecuting;
     BatchSize = size;
   }
  
public void setContract(Contract value) {
  if(value == null) {
   contracts = new Contract();
  }
  else {
    contracts = value;
  }
}    
  
   public void onBeforeInsert(Contract[] newContract){

    if(newContract != null)
    {
   getContactValues(null,newContract,null);
    }
    }
  public void onBeforeUpdate(Contract[] oldContract, Contract[] updatedContract, Map<ID, Contract> oldContractMap) {
   getContactValues(oldContract,updatedContract,oldContractMap);
    }
   
public void getContactValues(Contract[] oldContract, Contract[] updatedContract, Map<ID, Contract> oldContractMap)
{ 
  for(Contract contract : updatedContract)
  {
   List<Contract> UpdateCont = [SELECT  Contract.ID
               ,Contract.Email__c
            ,Existing_Contact__r.Email
            ,Name__c
            ,Title__c
            ,Phone__c
            ,Fax__c
                  ,Existing_Contact__r.Title
                  ,Existing_Contact__r.Phone
                  ,Existing_Contact__r.Fax
                  ,Existing_Contact__r.Name
            FROM Contract Where ID = :contract.Id];

   if(!Util.IsEmptyOrNull(UpdateCont[0].Existing_Contact__c))
   {
    if(!Util.IsEmptyOrNull(UpdateCont[0].Existing_Contact__r.Email))
    {
     contract.Email__c = UpdateCont[0].Existing_Contact__r.Email;
    }
    else
    {
     contract.Email__c = contract.Email__c;
    }
    if(!Util.IsEmptyOrNull(UpdateCont[0].Existing_Contact__r.Name))
    {
     contract.Name__c  = UpdateCont[0].Existing_Contact__r.Name;
    }
    else
    {
     contract.Name__c = contract.Name__c;
    }
    if(!Util.IsEmptyOrNull(UpdateCont[0].Existing_Contact__r.Title))
    {
     contract.Title__c = UpdateCont[0].Existing_Contact__r.Title;
    }
    else
    {
     contract.Title__c = contract.Title__c;
    }
    if(!Util.IsEmptyOrNull(UpdateCont[0].Existing_Contact__r.Phone))
    {
     contract.Phone__c = UpdateCont[0].Existing_Contact__r.Phone;
    }
    else
    {
     contract.Phone__c = contract.Phone__c;
    }
    if(!Util.IsEmptyOrNull(UpdateCont[0].Existing_Contact__r.Fax))
    {
     contract.Fax__c = UpdateCont[0].Existing_Contact__r.Fax;
    }
    else
    {
     contract.Fax__c = contract.Fax__c;
    }                   

    System.debug('Email'+contract.Email__c);
   }
  }
}
Best Answer chosen by Christine Klein
rsfdcrsfdc
Actually you don't need to query for same record. All the fields are already in the list passed as 'Trigger.new'. So all you have to check is if it that existing contract. If true then you query the contract record with existing contract id and compare for the current record in loop.

public void getContactValues(Contract[] oldContract, Contract[] updatedContract, Map<ID, Contract> oldContractMap)
{ 
  for(Contract eachContract : updatedContract)
  {
  
   if(!Util.IsEmptyOrNull(UpdateCont[0].Existing_Contact__c))
   {
      // query eachContract.Existing_Conract 
      Contract existingContract  = [SELECT  Contract.ID  ,Contract.Email__c            ,Existing_Contact__r.Email
            ,Name__c
            ,Title__c
            ,Phone__c
            ,Fax__c
                 
            FROM Contract Where ID = :existingContract.Id];

    if(!Util.IsEmptyOrNull(existingContract.Email))
    {
     eachContract.Email__c = existingContract.Email;
    }
    else
    { 
      // and  so on for all fields.
    }

    System.debug('Email'+eachContract.Email__c);
   }

  }
}
Although this should work. you might want to bulkify the loop and creating a map, example existingToNewMap<existingContractId, newContract> and issue a SOQL for all existing contract where ID IN :existingToNewMap.keySet() and process the fields in a new loop.

All Answers

rsfdcrsfdc
Could you share ContractsTriggerHandler class as well. My guess is line 86 is a query that not not returning any records.
Christine KleinChristine Klein
trigger ContractsTrigger on Contract (after delete, after insert, after undelete, 
after update, before delete, before insert, before update) {
    ContractsTriggerHandler handler = new ContractsTriggerHandler(Trigger.isExecuting, Trigger.size);
    // calls that are commented out are not currently needed
    if(Trigger.isInsert && Trigger.isBefore){
        handler.onBeforeInsert(Trigger.new);
    }
    else if(Trigger.isInsert && Trigger.isAfter){
        //handler.onAfterInsert(Trigger.new);
    }
    else if(Trigger.isUpdate && Trigger.isBefore){
        handler.onBeforeUpdate(Trigger.old, Trigger.new, Trigger.newMap);
    }
    else if(Trigger.isUpdate && Trigger.isAfter){
       handler.onAfterUpdate(Trigger.old, Trigger.new, Trigger.oldMap);
    }
    else if(Trigger.isDelete && Trigger.isBefore){
        //handler.onBeforeDelete(Trigger.old, Trigger.oldMap);
    }
    else if(Trigger.isDelete && Trigger.isAfter){ 
        //handler.onAfterDelete(Trigger.old, Trigger.oldMap);
        
    }
    else if(Trigger.isUnDelete){
        //handler.onUndelete(Trigger.new);    
    }

}

rsfdcrsfdc
"FROM Contract Where ID = :contract.Id];"

I think there is not ID during Before insert event. Can we confirm that using debug statement
Christine KleinChristine Klein
That is the issue but I'm not sure how to get this to work when creating a new contract
rsfdcrsfdc
Actually you don't need to query for same record. All the fields are already in the list passed as 'Trigger.new'. So all you have to check is if it that existing contract. If true then you query the contract record with existing contract id and compare for the current record in loop.

public void getContactValues(Contract[] oldContract, Contract[] updatedContract, Map<ID, Contract> oldContractMap)
{ 
  for(Contract eachContract : updatedContract)
  {
  
   if(!Util.IsEmptyOrNull(UpdateCont[0].Existing_Contact__c))
   {
      // query eachContract.Existing_Conract 
      Contract existingContract  = [SELECT  Contract.ID  ,Contract.Email__c            ,Existing_Contact__r.Email
            ,Name__c
            ,Title__c
            ,Phone__c
            ,Fax__c
                 
            FROM Contract Where ID = :existingContract.Id];

    if(!Util.IsEmptyOrNull(existingContract.Email))
    {
     eachContract.Email__c = existingContract.Email;
    }
    else
    { 
      // and  so on for all fields.
    }

    System.debug('Email'+eachContract.Email__c);
   }

  }
}
Although this should work. you might want to bulkify the loop and creating a map, example existingToNewMap<existingContractId, newContract> and issue a SOQL for all existing contract where ID IN :existingToNewMap.keySet() and process the fields in a new loop.
This was selected as the best answer
Christine KleinChristine Klein
Thanks for your help :)  I will give this a try.  I also need to read up on maps...they confuse me as I don't understand them LOL.