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
Jos Vervoorn 2Jos Vervoorn 2 

Trigger on ContentVersion issue

I wrote a fairly simple trigger on the ContentVersion object which ultimately would allow tracking opportunities without a signed contract. 

So a document is added or changed and the picklist value 'Signed Contract' is selected. This would flip the 'HasSignedQuote__c' bollean on true on the opportunity. 

However I noticed that the LinkedEntityId from the ContentDocumentLink is not always populated and I can't get my finger behind the root cause.
 
trigger ContentVersion_TRIGGER on ContentVersion (After insert, After update) {

      
    Public static Boolean SignedContract = false;

    System.debug(LoggingLevel.Info,'[ContentVersion_TRIGGER]. - TRIGGER ');
    

        Set<Id> contentDocumentIdSet = new Set<Id>();
        for(ContentVersion cv:trigger.new){

            if(cv.ContentDocumentId != null){
                contentDocumentIdSet.add(cv.ContentDocumentId);
                If(cv.File_Type__c == 'Signed Contract'){ //** Based on Content version picklist ...
                   System.debug(LoggingLevel.Info,'[ContentVersion_TRIGGER]. - Set SignedContract = true');   
                   SignedContract = true;
                }
            }
        }
        System.debug(LoggingLevel.Info,'[ContentVersion_TRIGGER].contentDocumentIdSet'+contentDocumentIdSet);   
        ContentDocumentLink cdl = [SELECT ContentDocumentId, LinkedEntityId FROM ContentDocumentLink WHERE ContentDocumentId IN:contentDocumentIdSet Limit 1];
        System.debug(LoggingLevel.Info,'[ContentVersion_TRIGGER].ContentDocumentLink  :'+cdl.LinkedEntityId);      
        List<Opportunity> OppList = [SELECT Id, HasSignedQuote__c FROM Opportunity where Id =:cdl.LinkedEntityId];  
        System.debug(LoggingLevel.Info,'[ContentVersion_TRIGGER].OppList.size() :'+OppList.size());  
        For (Opportunity Opp:OppList){
            If (SignedContract == true){
                System.debug(LoggingLevel.Info,'[ContentVersion_TRIGGER]. - Flag opportunity HasSignedQuote to true ...');   
                Opp.HasSignedQuote__c = true;
            }
        }
        Update OppList;
   
} //** end of Class

 
Best Answer chosen by Jos Vervoorn 2
Shaik Naga janiShaik Naga jani
Hi Jos,
if you are dealing with Parent Data you need to write the trigger on ContentDocumnetLink object, this object having LinkedEntityId(ParentId).
 When you are inserting the file the order of trigger execution of content documents objects
First ContentVersion object trigger will fire at the ContentDocumentLink not fire means you don't have parentId, next ContentDocument object trigger will fire last ContentDocumentLink object trigger will fire.
I updated your code check it working or not(not tested), modify trigger as per your requirement.
trigger ContentDocumentLinkTrigger on ContentDocumentLink (After insert) {
  Public static Boolean SignedContract = false;
    
    String strObjPrefix;
    Set<Id> setCntDocIds = new set<Id>();
    set<Id> setOppIds = new set<Id>();
    map<Id, Opportunity> mapOpps;

    for(ContentDocumentLink clIterator : Trigger.new) {
        strObjPrefix = String.valueOf(clIterator.LinkedEntityId).substring(0, 3);
        if(strObjPrefix == Opportunity.sObjectType.getDescribe().getKeyPrefix()) {
            setCntDocIds.add(clIterator.ContentDocumentId);
            setOppIds.add(clIterator.LinkedEntityId);
        }
    }
    
    list<ContentVersion> lstCntVersions = [Select Id, ContentDocumentId, File_Type__c From ContentVersion WHERE ContentDocumentId IN :setCntDocIds];
    
    for(ContentVersion cvIterator : lstCntVersions) {
        if(cvIterator.File_Type__c == 'Signed Contract') {
            SignedContract = true;
        }
    }
    
    list<Opportunity> lstOppsToUpdate = new list<Opportunity>();
    if(SignedContract && !setOppIds.isEmpty()) {
        mapOpps = new map<Id, Opportunity>([SELECT Id, HasSignedQuote__c FROM Opportunity where Id IN :setOppIds]);
        for(Opportunity oppIterator : mapOpps.values()) {
            oppIterator.HasSignedQuote__c = true;
            lstOppsToUpdate.add(oppIterator);
        }
    }
    
    if(!lstOppsToUpdate.isEmpty()) {
        Database.update(lstOppsToUpdate, false);
    }
   
}
Kindly mark this as solved if the reply was helpful.
Thanks
Shaik

All Answers

Shaik Naga janiShaik Naga jani
Hi Jos,
if you are dealing with Parent Data you need to write the trigger on ContentDocumnetLink object, this object having LinkedEntityId(ParentId).
 When you are inserting the file the order of trigger execution of content documents objects
First ContentVersion object trigger will fire at the ContentDocumentLink not fire means you don't have parentId, next ContentDocument object trigger will fire last ContentDocumentLink object trigger will fire.
I updated your code check it working or not(not tested), modify trigger as per your requirement.
trigger ContentDocumentLinkTrigger on ContentDocumentLink (After insert) {
  Public static Boolean SignedContract = false;
    
    String strObjPrefix;
    Set<Id> setCntDocIds = new set<Id>();
    set<Id> setOppIds = new set<Id>();
    map<Id, Opportunity> mapOpps;

    for(ContentDocumentLink clIterator : Trigger.new) {
        strObjPrefix = String.valueOf(clIterator.LinkedEntityId).substring(0, 3);
        if(strObjPrefix == Opportunity.sObjectType.getDescribe().getKeyPrefix()) {
            setCntDocIds.add(clIterator.ContentDocumentId);
            setOppIds.add(clIterator.LinkedEntityId);
        }
    }
    
    list<ContentVersion> lstCntVersions = [Select Id, ContentDocumentId, File_Type__c From ContentVersion WHERE ContentDocumentId IN :setCntDocIds];
    
    for(ContentVersion cvIterator : lstCntVersions) {
        if(cvIterator.File_Type__c == 'Signed Contract') {
            SignedContract = true;
        }
    }
    
    list<Opportunity> lstOppsToUpdate = new list<Opportunity>();
    if(SignedContract && !setOppIds.isEmpty()) {
        mapOpps = new map<Id, Opportunity>([SELECT Id, HasSignedQuote__c FROM Opportunity where Id IN :setOppIds]);
        for(Opportunity oppIterator : mapOpps.values()) {
            oppIterator.HasSignedQuote__c = true;
            lstOppsToUpdate.add(oppIterator);
        }
    }
    
    if(!lstOppsToUpdate.isEmpty()) {
        Database.update(lstOppsToUpdate, false);
    }
   
}
Kindly mark this as solved if the reply was helpful.
Thanks
Shaik
This was selected as the best answer
Jos Vervoorn 2Jos Vervoorn 2
Thanks Shaik, it works. I'm still not sure why using my trigger code on ContentVersion sometimes worked and sometimes does not. Anyway thanks for the help.
 
Harry1008Harry1008
@Shaik Naga jani., @Jos Vervoom2., Could you please let me know whether I can use the same code that you suggested to change the Opportunity stage from Engagement to Negotiation if the user upload a file on Opportunity.