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
SFDC 13SFDC 13 

Display Error if opportunity stage is closed won and related section 0 attachment

Here is the code but it won't work properly if I attached document in the notes and Attachment section then also it will throw an error. if any one know then please help me.
Code::  

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.
    }
  }
}
Best Answer chosen by SFDC 13
agrawal mukulagrawal mukul
Code refactored :

set<Id> oppId=new set<Id>();
        for(Opportunity opp:newRecordList)
        {
            oppId.add(opp.Id);
        }
        
        Map<Id,ContentDocumentLink> opportunityAttachments = new Map<Id,ContentDocumentLink>();
        list<ContentDocumentLink> conList=[SELECT ContentDocumentId,LinkedEntityId FROM ContentDocumentLink WHERE LinkedEntityId IN :oppId];
        for(ContentDocumentLink opp:conList)
        {
            opportunityAttachments.put(opp.LinkedEntityId,opp);
            
        }
        
       
        System.debug('opportunityAttachments'+ opportunityAttachments);
  // For each opportunity being created or modified...
  for(Opportunity opp:newRecordList) {
    if(opp.StageName=='Closed Won' && // If it is changing to closed/won...
      !opportunityAttachments.containsKey(opp.id)) { // 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.
    }
  }
    }

All Answers

agrawal mukulagrawal mukul
Hi SFDC 13,

Found the issue since attachment will not get the record related to opportunity,


For objects such as Account - Contacts, Opportunities, Contracts, Tasks, etc, in lightining experience attachments are not available, only files. So, as you can see when you try to upload a file, only "Upload File" is available and not "Attach File". The last option is only available to Salesforce Classic.
So, in order to get those records you have to query like this:

SELECT ContentDocumentId FROM ContentDocumentLink WHERE LinkedEntityId = '<Opportunity or other object id here>'


Please mark best answer if it solves your query :

Thanks,
Mukul
agrawal mukulagrawal mukul
Code refactored :

set<Id> oppId=new set<Id>();
        for(Opportunity opp:newRecordList)
        {
            oppId.add(opp.Id);
        }
        
        Map<Id,ContentDocumentLink> opportunityAttachments = new Map<Id,ContentDocumentLink>();
        list<ContentDocumentLink> conList=[SELECT ContentDocumentId,LinkedEntityId FROM ContentDocumentLink WHERE LinkedEntityId IN :oppId];
        for(ContentDocumentLink opp:conList)
        {
            opportunityAttachments.put(opp.LinkedEntityId,opp);
            
        }
        
       
        System.debug('opportunityAttachments'+ opportunityAttachments);
  // For each opportunity being created or modified...
  for(Opportunity opp:newRecordList) {
    if(opp.StageName=='Closed Won' && // If it is changing to closed/won...
      !opportunityAttachments.containsKey(opp.id)) { // 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 was selected as the best answer
Suraj Tripathi 47Suraj Tripathi 47
Hi 
Use the below code Snippet for the same and do attach the attachment from salesforce classic. when you attach something from salesforce lightning view it is considered as File but you are using the object of Attachment so you need to attach the attachment which you can add from salesforce classic view .
steps are : Go to salesforce classic >> open any record >> related notes and attachments >> click Attach file >> choose file >> attach file >> done. 

trigger closeWonMustAttach on Opportunity (after insert, before update) {
  // Find all opportunities and their list of attachments...
  set<Id> ids = new Set<Id>();
    for(Opportunity objOpp : trigger.new){
        ids.add(objOpp.Id);
    }
    System.debug('ids >> '+ids);
  Map<Id,List<Attachment>> opportunityAttachments = new Map<Id,List<Attachment>>();
   List<Opportunity> listOpp = [SELECT Id,(SELECT Id FROM Attachments) FROM Opportunity WHERE Id IN : ids];
    System.debug('listOpp >> '+listOpp);
    for(Opportunity objOpp : listOpp){
        for(Attachment objAtt : objOpp.Attachments){
            if(opportunityAttachments.containsKey(objOpp.Id)){
                list<Attachment> listAtt =opportunityAttachments.get(objOpp.Id);
            listAtt.add(objAtt);
            opportunityAttachments.put(objOpp.Id, listAtt);
        }
        else
        {
            opportunityAttachments.put(objOpp.Id, new List<Attachment>{objAtt});
        }

            }
        }
 
    System.debug('opportunityAttachments >> '+opportunityAttachments);
  // 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)...
       // Or the opportunity attachment list was null...
       opportunityAttachments.get(opp.id).size()==0)) { // Or the attachment list has no entries...
      opp.addError('You must first attach a file to this opportunity before changing to Closed/Won.'); // So we prevent saving here.
    }
  }
}


Hope it will help you!
Thanks