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
anil87anil87 

INVALID_FIELD_FOR_INSERT_UPDATE, Business Account may not use Person Account field PersonEmail:

Hi All.

          i want  When opportunity is updated & Email or Fax fields are changed

 
if Account - Record type is Person Account, 
then update Email & Fax fields on the Person Account Record.
 
else
 
if Account - Record type is Business Account, 
then update Email & Fax fields on the contact record on the Account.
 
Here is my approach but  i'm getting the folloeing error
 
System.DmlException: Update failed. First exception on row 0 with id 001c0000008xwoaAAA; first error: INVALID_FIELD_FOR_INSERT_UPDATE, Business Account may not use Person Account field PersonEmail: [PersonEmail]: Trigger.UpdateEmailFax: line 37, column 1

 

trigger UpdateEmailFax on Opportunity (after update)
{
    List<Account> acc = new List<Account>();
    List<Contact> con = new List<Contact>();
    List<Id> aid = new List<Id>();
    Map<Id,Opportunity> accMap = new Map<Id,Opportunity>();
    Integer i = 0;
    for(Opportunity o : trigger.new)
    {
        system.debug('Inside For loop : ' + i + ' Account id : ' + o.AccountId);
        if(o.AccountId!=null)
        {
            system.debug('Old value - Email : ' + trigger.old[i].Email__c + ', Fax : ' + trigger.old[i].Fax__c);
            if((trigger.old[i].Email__c!=o.Email__c) || (trigger.old[i].Fax__c!=o.Fax__c))
            {
                if(o.Account.RecordType.Name == 'Business_Account')
                {
                    system.debug('Business_Account - Record Type : ' + o.Account.RecordType.Name);
                    accMap.put(o.AccountId,o);
                    aid.add(o.Account.id);
                }
                else
                {
                    system.debug('Person_Account  - Record Type : ' + o.Account.RecordType.Name);
                    Account a = new Account();
                    //o.Account.PersonEmail = o.Email__c;
                    //o.Account.Fax = o.Fax__c;
                    a.id = o.AccountId;
                    a.PersonEmail = o.Email__c;
                    a.Fax = o.Fax__c;
                    acc.add(a);
                }
            }
        }
       i++;
    }
    update acc; // ERROR IS HERE
    //con = [select id,name,Account.id, Email , Fax from Contact where Account.id in :aid];
    for(Contact c : [select id,name,Account.id, Email , Fax from Contact where Account.id in :aid])
    {
        if(c.Account!=null)
        {
            c.Email  = accMap.get(c.Account.id).Email__c;  
            c.Fax = accMap.get(c.Account.id).Fax__c;
            con.add(c);
        }
    }
    update con;
}

sfdcfoxsfdcfox

You're not querying for the account at all, so when you try to access that information, the if statement is false (o.Account.RecordType is unknown, as you haven't queried for it), so it branches to the ELSE statement, which assumes that it is a person account (which, it apparently is not). I would recommend the following changes:

 

trigger updateemailfax on opportunity (after update) {
  map<id,account> acc = new map<id,account>(), upacc = new map<id,account>();
  map<id,opportunity> opp = new map<id,opportunity>();
  contact[] con = new contact[0];
  set<id> busacc = new set<id>();

  for(opportunity o:trigger.new) {
    acc.put(o.accountid,null);
    opp.put(o.accountid,opp);
  }
acc.remove(null);
opp.remove(null); acc.putall([select id,recordtype.ispersontype from account where id in :acc.keyset()]); for(opportunity o:trigger.new) { if(o.accountid!=null && (o.email__c!=trigger.oldmap.get(o.id).email__c || o.fax__c!=trigger.oldmap.get(o.id).fax__c)) { if(acc.get(o.accountid).recordtype.ispersontype) { upacc.put(o.accountid,new account(id=o.accountid,personemail=o.email__c,fax=o.fax__c)); }
else { busacc.add(o.accountid);
}
}
} if(!busacc.isempty()) { con.addall([select id,accountid from contact where id in :busacc]);
} for(contact c:con) { c.email = opp.get(c.accountid).email; c.fax = opp.get(c.accountid).fax;
} update upacc.values(); update con; }

1) Notice how I use queries to find the record type. As a bonus, I use the IsPersonType flag instead of depending on the name of the record type being a specific value.

 

2) Notice the use of maps so we don't need to iterate over items unnecessarily. And, we don't need an index counter, either now.

 

3) Changed the need for if-then statements by optimising queries, reducing the database rows returned and script statements executed. I do this by specifically making sure I removed the need to query for contacts with blank accountids.

 

 

ForceLoverForceLover

Hi sfdcfox,

Thanks for replying me it solved the problem with small changes but now i'm getting another problem because i have another trigger on contact which will fire when a contact is updated .i need to perform

 

- When Contact is updated & Email or Fax fields are changed, update the Email & Fax from Contact into the Opportunity.so i written a trigger on contact which is updating fine individually 
 
  when i try update a businees account i'm getting this error,
 
Error:Apex trigger updateemailfax caused an unexpected exception, contact your administrator: updateemailfax: execution of AfterUpdate caused by: System.DmlException: Update failed. First exception on row 0 with id 003c00000065ftLAAQ; first error: CANNOT_INSERT_UPDATE_ACTIVATE_ENTITY, Contacttoopp: execution of AfterUpdate caused by: System.ListException: Duplicate id in list: 006c00000037Nn0AAE Trigger.Contacttoopp: line 50, column 1: []: Trigger.updateemailfax: line 38, column 1
 
here is my trigger on opportunity
 

trigger updateemailfax on opportunity (after update) {  

map<id,account> acc = new map<id,account>(), upacc = new map<id,account>();  

map<id,opportunity> opp = new map<id,opportunity>();  

contact[] con = new contact[0];  

set<id> busacc = new set<id>();

  for(opportunity o:trigger.new) {    

acc.put(o.accountid,null);    

opp.put(o.accountid,o);   }  

acc.remove(null);  

opp.remove(null);  

acc.putall([select id,recordtype.ispersontype from account where id in :acc.keyset()]);  

for(opportunity o:trigger.new) {    

if(o.accountid!=null && (o.email__c!=trigger.oldmap.get(o.id).email__c || o.fax__c!=trigger.oldmap.get(o.id).fax__c)) {      

if(acc.get(o.accountid).recordtype.ispersontype) {        

upacc.put(o.accountid,new account(id=o.accountid,personemail=o.email__c,fax=o.fax__c));       }      

else {        

busacc.add(o.accountid);       }     }   }  

if(!busacc.isempty()) {    

con.addall([select id,accountid from contact where accountid in :busacc]);    

System.debug('busacc set size is'+busacc.size());   }      

for(contact c:con) {    

c.email = opp.get(c.accountid).email__c;    

c.fax = opp.get(c.accountid).fax__c;   }

  update upacc.values();  

update con;  

}

 

Here is my contact trigger

 

trigger Contacttoopp on Contact (after update) {    

// map tracks records on ContactID

    Map<String, Contact> ContactMap = new Map<String, Contact>();      

  List<Opportunity> lstToUpdate = new List<Opportunity>();       

  for( Contact con : Trigger.new )     {       

  if( (Trigger.oldMap.get(con.id).Email != con.Email ) || (Trigger.oldMap.get(con.id).Fax != con.Fax ) )

        {               ContactMap.put( Con.ID, con );                  

}              

  if( ContactMap.keySet().size() > 0 )      {  

 //map to keep track of the opportunity contact roles    

map<Id, Id> oppycontactroles = new map<Id, Id>();

  

    //select OpportunityContactRoles for the contacts with contact role required

    List<OpportunityContactRole> ContactRoles = [select OpportunityID, ContactID from OpportunityContactRole where contactID in :ContactMap.keySet() ];

    for (OpportunityContactRole ocr : ContactRoles) {        

//puts the contact roles in the map with the opportunity ID as the key        

oppycontactroles.put(ocr.OpportunityId,ocr.ContactID);    

}

        // update opportunity Prior Email,Fax field from the matching record        

for( Opportunity opp : [Select Email__c, Fax__c, Id From Opportunity Where ID IN : oppycontactroles.keySet()])         {           

                       opp.Email__c  =  ContactMap.get(oppycontactroles.get(opp.id)).Email;            

opp.Fax__c = ContactMap.get(oppycontactroles.get(opp.id)).Fax;

            lstToUpdate.add(opp);         }

    }  }  

 update lstToupdate;

 }

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 
 
sfdcfoxsfdcfox

You've got a recursive trigger problem: updateEmailFax updates contacts, so when ContactToOpp is called as a result, the opportunities are updated again, which will call updateEmailFax again. You'll need a static variable to prevent the recursion:

 

public class recursion {
  public static void contacts, opps;
  static {
    contacts = opps = false;
  }
}

With this in place, on line 2 of updateEmailFax, add:

 

if(recursion.contacts) return;
recursion.opps = true;

 And on the ContactToOpp trigger:

 

if(recursion.opps) return;
recursion.contacts = true;

This should break the chain and allow the save to complete.

ForceLoverForceLover

Hi Sfdcfox,

 

Thanks for replying can you please look into dis thread

 

http://boards.developerforce.com/t5/Apex-Code-Development/Need-urgent-Help-to-Increase-Test-Coverage/td-p/582745

 

i writtem a Followuptaskhelper class to avoid recuesion.i'm getting only 30% of code covereage when include this lines in my trigger please help me out in this ,how to increase my code coverage.

 

Here is my class, the above thread contains my trigger and test class i need my trigger to cover above 75%.

 

public class FollowUpTaskHelper {

    // Static variables are local to the context of a Web request  
    
    // (or testMethod during a runTests call)  
    
    // Therefore, this variable will be initialized as false  
    
    // at the beginning of each Web request which accesses it.  
    

    private static boolean alreadyCreatedTasks = false;


    public static boolean hasAlreadyCreatedFollowUpTasks() {
        return alreadyCreatedTasks;
    }

    // By setting the variable to true, it maintains this  
    
    // new value throughout the duration of the request  
    
    // (or testMethod)  
    
    public static void setAlreadyCreatedFollowUpTasks() {
        alreadyCreatedTasks = true;
    }


    public static String getFollowUpSubject(String subject) {
        return 'Follow Up: ' + subject;
    }

}