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
NPoulosNPoulos 

APEX Validation: Attach File before setting to Closed Won

Hello all:

 

I would like to require that a user upload an attachment to opportunities before being able to set them to Closed - Won. I am unsure how to implement this in APEX.

 

Any guidance would be greatly appreciated.

 

Thanks.

Ritesh AswaneyRitesh Aswaney

You could have a Boolean (Checkbox) field on Opportunity.


Write a trigger on Attachment which updates the relevant Opportunity to set this checkbox, when the User uploads an attachment.

 

Then have a validation rule on Opportunity, which prevents a Stage change to Closed-Won unless this Checbox is checked.

NPoulosNPoulos

Definitely a great idea, thanks.

 

Are there any examples of APEX code referencing an Attachment? Or anything else similar I could glean from? I'm a little lost on the syntax around this.

 

Again, thanks for your help!

Ritesh AswaneyRitesh Aswaney

Here's something to get you started ....

 

trigger AttachmentAfter on Attachment (after insert) {

    String oppKeyPrefix = Opportunity.SObjectType.getDescribe().getKeyPrefix();
    
    Id[] oppIds = new Id[]{};
    
    for(Attachment att : trigger.new)
    {
        if(att.ParentID != null && ((String)att.ParentId).startsWith(oppKeyPrefix))
            oppIds.add(att.ParentId);
    }
    
    Opportunity[] oppsToUpdate = new Opportunity[]{};
    
    for(Opportunity opp : [Select Id, Name, Attachment__c from Opportunity where Id in :oppIds]){
        opp.Attachment__c = true;
        oppsToUpdate.add(opp);
    }
    
    if(oppsToUpdate != null && !oppsToUpdate.isEmpty())
        Database.update(oppsToUpdate);
}

sfdcfoxsfdcfox

You can also approach this the other direction:

 

 

trigger closeWonMustAttach on Opportunity (before insert, before update) {
  // Find all opportunities and their list of attachments...
  Map<Id,Opportunity> opportunityAttachments = new Map<Id,Opportunity>(
    [SELECT Id,(SELECT Id FROM Attachments) FROM Opportunity WHERE Id IN :Trigger.new]
  );
  // For each opportunity being created or modified...
  for(Opportunity opp:Trigger.new) {
    if(opp.StageName=='Closed Won' && // If it is changing to closed/won...
      (!opportunityAttachments.containsKey(opp.id) || // And the opportunity was not found (Insert)...
       opportunityAttachments.get(opp.id).Attachments == null || // Or the opportunity attachment list was null...
       opportunityAttachments.get(opp.id).Attachments.size()==0)) { // Or the attachment list has no entries...
      opp.StageName.addError('You must first attach a file to this opportunity before changing to Closed/Won.'); // So we prevent saving here.
    }
  }
}

This is a real-time solution, as opposed to the other suggestion, which, as it stands, could be fooled by attaching a file then deleting it... or if field security was not configured correctly, could be bypassed entirely through an import or data loader operation.

 

Saurav RajputSaurav Rajput
I tried the above code and it is still giving error, even after I uploaded the file.