0

My requirement is that as and when an attachment is added to a contact record I want this attachment inserted on the Contacts Account record as well.

I have written a trigger for the above req as below. But I am facing an issue.

  1. The issue is that when I add an attachment to the contact record, 2 attachments are created on the contact's Account record.
  2. And when I add another attachment on the contact record, all the attachments are copied again on the Account record making it 4 attachments on Account record

    Trigger CopyContactAttachtoAccount on Attachment (after insert)     
    {
    
    set<id> conids=new set<id>();
    for(attachment an:trigger.new)
    {
    
    if(an.ParentId.getSobjectType() == contact.SobjectType)
    
    {
    
      conids.add(an.ParentId);
    }
    
    }
    list<Contact> conAccmap =new list<contact>([Select Account.id,Account.name 
     from Contact where Id In:conids]);
    
    list<attachment> accatt=new list<attachment>();
    
     for(contact ct:conAccmap){
    
      for (attachment am :[select id, name,parentId, body from Attachment where 
      ParentId in :conids])
    {
    
            Attachment newFile = New Attachment(Name = am.name, body = 
            am.body,ParentId=ct.Accountid);
            accatt.add(newFile);
    
      }
    
      }
    
    if(accatt.size()>0){
    
         insert accatt;}
    
    }
    

doubt If i add one attachment-'Doc2' to the contact it creates as below screenshot in Account record.

Here 'Meenatest1' is the contact and dell is the 'account'

Is it possible to have only one attachment with parentid as account in the account record?

enter image description here

2

You are querying the Attachment object using the contact ID, so whatever attachments are present for that contact, all will be retrieved.

Some minor modifications are required to the code, I had implemented a similar functionality. Have modified the code for your requirement.

trigger createAttachmentForAccount on Attachment (after insert) {
    Set<ID> attId = new Set<ID>();
    Set<ID> contactID = new Set<ID>();
    /* ******This block collects all the attachment IDs*****************/
    for(Attachment att : trigger.New){
         //Check if added attachment is related to Contact or not
         if(att.ParentId.getSobjectType() == Contact.SobjectType){
              attId.add(att.Id);
              contactID.add(att.ParentId);
         }
    }
    /*****************************************/

    /*******************Below block creates the attachment on Account***************/
    list<attachment> accatt=new list<attachment>();
    List<Attachment> attachmentList = [SELECT Name, Id, ParentID,Body FROM Attachment WHERE ID IN :attID];
    Map<Id, Contact> contactDetailMap = new Map<Id, Contact>([Select Id, Account.id,Account.name from Contact where Id IN :contactID]);
    for(Attachment att: attachmentList) {
        Attachment newFile = new Attachment(Name = att.name, body = 
        att.body,ParentId= contactDetailMap.get(att.ParentID).Accountid);
        accatt.add(newFile);
    }

    if(accatt.size()>0){
     insert accatt;
     DeleteAttachmentsForContact.deleteAttachment(attId); //Add this line if you want to delete the attachment from Contact Object
    }

}

So here, I am querying the Attachment based on Attachment ID itself and not on the basis of contact ID.

If you want to delete the attachment created on Contact object. Add below class with future method.

global class DeleteAttachmentsForContact {

    @future (callout=true)
     public static void deleteAttachment(Set<Id> attId) {
        delete [SELECT Name, Id, ParentID FROM Attachment WHERE ID IN :attID];
    }
}

See if this helps!

P.S. - I haven't tested the code, but hope you get the logic around this. Also instead of querying the Attachments again, you can run the second for loop over the Trigger.new variable itself.

5
  • Thanks for your answer!.. I tried with your code as well.. It does create an attachment in the Account record but it creates 2 attachments if I add one.. one attachment with parentid as contact ID and one attcahment with parentid as the account ID – Meena Nov 10 '17 at 9:45
  • So do you want to avoid the attachment to get added under Contact, where you originally added the Attachment? – Hemant Jain Nov 10 '17 at 11:18
  • No, I mean i dont want two attachments in Account record (one with parentid as contact ID and one with parentid as the account ID) ..if i add one attachment in contact record – Meena Nov 10 '17 at 11:25
  • So I have edited the response to delete the attachment from Contact after moving it to Account Object. A class with future method will do that – Hemant Jain Nov 10 '17 at 11:29
  • I have edited my first post..plz see the doubt section – Meena Nov 10 '17 at 11:41
1

There are quite a few problems in your code... but before we jump into the problems, I have re-factored your code (not tested though)

Trigger CopyContactAttachtoAccount on Attachment (after insert)     
{
    set<id> conids=new set<id>();
    Map<id, attachment> contactAttachments = new Map<id, attachment>();
    Map<id, contact> contactsWithAccount = new Map<id, contact>();
    for(attachment an:trigger.new)
    {
        if(an.ParentId.getSobjectType() == contact.SobjectType) contactAttachments.put(an.ParentId, an);
            //conids.add(an.ParentId);
    }
    // not required, as using soql 
    // list<Contact> conAccmap =new list<contact>([Select Account.id,Account.name from Contact where Id In:conids]);

    list<attachment> accatt=new list<attachment>();
    //for(contact ct:conAccmap){
    // read "Explanation" why I have replaced Soql For loop with your approach
    if (contactAttachments.keySet().size() > 0) // Read "Explanation" why this check is introduced.
    {
        contactsWithAccount = new Map<Id, Contact>([SELECT Id, Account.id FROM Contact WHERE Id IN :contactAttachments.keySet()]);
        // for (attachment am :[select id, name,parentId, body from Attachment where ParentId in :conids]) // soql inside for loop is bad, bad thing.
        for (attachment am: contactAttachments.values() ) 
        {
            if (contactsWithAccount.containsKey(am.ParentId) )
            {
                Attachment newFile = New Attachment(Name = am.name, body = am.body, ParentId=contactsWithAccount(am.ParentId);
                accatt.add(newFile);
            }
        }
    }
    insert accatt;
}

Explanation on the changes

  • Always use SOQL-For-Loop, as Salesforce used its own mechanism to query data in chunks, when used this way. Read more here.
  • Introduced an if check on line 16 - this is checking for the size of the list as it is going to feed our SOQL query. What happens if we don't have this if check even if the ids are null? No record will be fetched, however, a soql will be counted against the limits. This check will ensure that our SOQL limits are reserved.
  • Commented line 20 - because it is using SOQL inside for loop (outer loop) and hence is gonna make soql calls for all the records present in trigger.new context. Remember, we only have 100 SOQL limits for one transaction. Please go through apex best practices.
6
  • Thanks for your answer!.. I tried your code It does create an attachment in the Account record but it creates 2 attachments if I add one.. one attachment with parentid as contact ID and one attcahment with parentid as the account ID – Meena Nov 10 '17 at 9:46
  • Attachment with contact is the default. Thats what triggered the event. What is your requirement then? Can you be more specific please? – Mahmood Nov 10 '17 at 10:18
  • I have edited my first post..plz see the doubt section – Meena Nov 10 '17 at 11:41
  • Account is the parent, and this is default functionality of Salesforce. It will display attachments,notes of children as well in the related-list of parent. And what you are seeing in the related list of account is perfectly fine and as-designed behavior. – Mahmood Nov 10 '17 at 11:54
  • okay..Thank you! – Meena Nov 10 '17 at 12:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.