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
AnetaAneta 

Trigger on Attachments

Hello,

I'm brand new to development and triggers and I'm seeking some help in modifying the below simple trigger.

This is a trigger on the Attachemnt object that checks to count the number of attachments. If more than one then it flags a checkbox Attachment_Added__c on a custom object - Proposal. It works fine, but i would like to change it so that when the attachment is deleted and there are no attachments under the Proposal object then the checkbox on the proposal get cleared. Can someone please help?

Thank you in advance!

 

My current trigger:

// Trigger to update the Attachment__c custom checkbox field in Custom Object(Custom_Obj__c) if it has any attachment.
trigger CountAttachments on Attachment (after insert) 
{          
List<Proposal__c> co = [select id, Attachment_Added__c from Proposal__c where id =: Trigger.New[0].ParentId];          
If(co.size()>0)          
{              
co[0].Attachment_Added__c = True;              
update co;          
} 
}

 

Best Answer chosen by Admin (Salesforce Developers) 
ashishkrashishkr

before delete is all you need, all you need is "before delete"

 

trigger CountAttachments on Attachment (after insert, before delete) 
{  

if(trigger.isinsert){        
List<Proposal__c> co = [select id, Attachment_Added__c from Proposal__c where id =: Trigger.New[0].ParentId];          
If(co.size()>0)          
{              
co[0].Attachment_Added__c = True;              
update co;          
} 

}


if(trigger.isdelete){

List<Proposal__c> co = [select id, Attachment_Added__c from Proposal__c where id =: Trigger.old[0].ParentId];          
If(co.size()>0)          
{              
co[0].Attachment_Added__c = false;              
update co;          
} 

}
}

 Notice the use of trigger.old within the trigger.isdelete block. trigger.new is not available with delete triggers. Refer here.

I'm assuming undelete is NOT going to happen here.

All Answers

ashishkrashishkr

before delete is all you need, all you need is "before delete"

 

trigger CountAttachments on Attachment (after insert, before delete) 
{  

if(trigger.isinsert){        
List<Proposal__c> co = [select id, Attachment_Added__c from Proposal__c where id =: Trigger.New[0].ParentId];          
If(co.size()>0)          
{              
co[0].Attachment_Added__c = True;              
update co;          
} 

}


if(trigger.isdelete){

List<Proposal__c> co = [select id, Attachment_Added__c from Proposal__c where id =: Trigger.old[0].ParentId];          
If(co.size()>0)          
{              
co[0].Attachment_Added__c = false;              
update co;          
} 

}
}

 Notice the use of trigger.old within the trigger.isdelete block. trigger.new is not available with delete triggers. Refer here.

I'm assuming undelete is NOT going to happen here.

This was selected as the best answer
ashishkrashishkr

Also these triggers are not BULKIFIED. Read to know more.

AnetaAneta
Thank you so much - this works perfect!!!
AnetaAneta

This works almost perfect with one exception... I works great when there's only one attachment, when I have more than one then it clears the checkbox anyways. Is there a way to fix it so that it counts the attachemnts before delete and if there's just one and it gets deleted then it should clear the checkbox on Proposal. If there is more than one attachement then it should keep the checkbox checked. Thoughts, ideas?

ashishkrashishkr

Is attachment a detail in a master-detail relation or does it have a lookup relation with the parent?

 

If it's a detail, then you can use roll-up summary field in the parent to keep a count of children. 

If it's a lookup reation, then you've two options: 

1) keep a count of attachments by using triggers on insert, delete and undelete of attachments.

 

or 

 

2) In the above trigger, query the Attachments objects with given paent id to see how may are returned, as below:

 

trigger CountAttachments on Attachment (after insert, before delete) 
{  

if(trigger.isinsert){        
List<Proposal__c> co = [select id, Attachment_Added__c from Proposal__c where id =: Trigger.New[0].ParentId];          
If(co.size()>0)          
{              
co[0].Attachment_Added__c = True;              
update co;          
} 

}


if(trigger.isdelete){

List<Proposal__c> co = [select id, Attachment_Added__c from Proposal__c where id =: Trigger.old[0].ParentId]; 
        
If(co.size()>0)          
{  
List<Attachment> allChildren = [Select id from Attachment where parentid = :co[0].id]; 
if(allChildren != null && allChildren.size() == 0)            
co[0].Attachment_Added__c = false;              
update co;          
} 

}
}

 Here, I must point out that I'm not sure whether sfdc considers the record being deleted when returning in the soql. So, you'll have to experiment with the "== 0" part. [Above code assumes sfdc does not count them in the query. I may be wrong here.]

 

 

 

ashishkrashishkr
Correction:
if(allChildren != null && allChildren.size() == 0)
will work.
AnetaAneta

Thank you so much for your help.

This works great now - the only thing that I changed was added "after delete" condition as it didn't clear the flag without it when the last attachment was deleted.

 

Now it works like this:

No attachment: "Attachemnt Added" checkbox on Proposal - not checked 

First attachement added - checkbox checked

Second attachment added - checkbox stays checked

First attachment deleted - checkbox stays checked

Last attachment deleted - checkbox cleared

 

Now, the question is - do I need a test class as well for this to deploy it in the production? If so, can you please help with that?

 

Here's the final trigger code that works:

 

trigger CountAttachments on Attachment (after insert, before delete, after delete) 
{  
if(trigger.isinsert){        
List<Proposal__c> co = [select id, Attachment_Added__c from Proposal__c where id =: Trigger.New[0].ParentId];          
If(co.size()>0)          
{              
co[0].Attachment_Added__c = True;              
update co;          
} 
}
if(trigger.isdelete){

List<Proposal__c> co = [select id, Attachment_Added__c from Proposal__c where id =: Trigger.old[0].ParentId]; 
        
If(co.size()>0)          
{  
List<Attachment> allChildren = [Select id from Attachment where parentid = :co[0].id]; 
if(allChildren != null && allChildren.size() == 0)            
co[0].Attachment_Added__c = false;              
update co;          
} 
}
}

 

ashishkrashishkr

Yes, from theTesting Best Practices, every trigger must have some code coverage to be deployed in production. You can "cheat" the deployment by just inserting an Attachment record in your test method. As such the after insert trigger would be fired and your trigger would get at least 1% coverage. But to write a more robust test class, you should go through the Introduction to Test Classes.

AnetaAneta

Thank you!

 

Here's the test class that I have - hope someone else can fidn it useful:

 

@isTest
public class AttachmentTriggerTest
{
    static testMethod void attTriggerTest1()
    {
        test.startTest();

        Account acct = new Account(Name = 'Test Account ');

        insert acct;

        Opportunity Opp = new Opportunity(AccountId = acct.Id, Name = 'Test Opportunity', StageName ='1- Lead Gen' , CloseDate = date.today());

        insert Opp;

        Proposal__c ps = new Proposal__c(Opportunity_Name__c = Opp.Id,  Name = 'Test Proposal', Account__c = acct.Id, Proposal_Status__c ='new');

        insert ps;

        Attachment att = new Attachment(Name='textAtt', ParentId = ps.Id, body = Blob.valueOf('Some Text'), Description='TestAttachment');
        insert att;

        delete att;
        
        test.stopTest();
    }
}