top of page

Automating Document Attachment to Opportunities from Salesforce Quotes

Updated: Dec 1, 2023


Github Link : https://github.com/AourLegacy/AourFactory/tree/LinkDocument

When working with Quotes in Salesforce, there often comes a need to attach documents – such as proposals or contracts – directly to the related Opportunities for easy access and organization. To streamline this process, you can utilize Salesforce’s powerful automation capabilities by creating a Screen Flow, which can be initiated by a button on the Quote object. This Screen Flow will gather the necessary document information and then pass it to a custom Apex class, which will programmatically link the document to the corresponding Opportunity.


Step 1: Creating the Screen Flow

The journey begins with a Screen Flow. This intuitive interface is designed within the Salesforce Flow Builder and is typically composed of a 'File Upload' screen component that allows users to upload documents. The Flow is configured to capture two essential pieces of information: the `Record ID` of the Quote and the `ContentDocumentId` of the uploaded file.

Once the user uploads a file and submits the Flow, these two key pieces of information are captured as variables within the Flow, ready to be passed to the custom Apex class that does the heavy lifting.





Step 2: Triggering the Flow with a Button

To provide a seamless user experience, a custom button is added to the Quote object. When clicked, this button launches the Screen Flow. It's a small but crucial touchpoint that ensures that the Flow is easily accessible when viewing a Quote.

Step 3: The Apex Class – A Deeper Dive

Upon completion of the Flow, the captured variables (`recordId` and `contentDocumentId`) are handed off to the custom Apex class, `ContentDocumentLinkManager`.

Let's break down what this Apex class does:


- Input Variables: Through `@InvocableVariable` annotations, the class defines a structure to handle inputs coming from the Flow, encapsulated in the `FlowInputs` inner class.

- Validation: The class first validates that the inputs are not null, ensuring there's a Quote record and a document to work with.


- Fetching the Quote: An SOQL query retrieves the `SBQQ__Quote__c` record using the provided `recordId`. This query is crucial to identify the related `Opportunity`.

- Verifying the Opportunity: Another SOQL query fetches the related `Opportunity` using the `SBQQ__Opportunity2__c` field from the `Quote`. If an `Opportunity` is related, the process continues.


- Creating the Link: The class then creates a new instance of `ContentDocumentLink`. This is the Salesforce object that represents a link between a document and an entity (such as an `Opportunity`). The class sets the `ContentDocumentId` with the uploaded document ID and `LinkedEntityId` with the `Opportunity ID`.

- Assigning Access:Before the record is inserted, the `ShareType` and `Visibility` are set to determine the level of access users have to the document.

- Insertion: Finally, the `ContentDocumentLink` record is inserted, linking the document to the Opportunity.


public class ContentDocumentLinkManager {
    // Wrapper class to handle inputs from the Flow
    public class FlowInputs {
        @InvocableVariable
        public String recordId;

        @InvocableVariable
        public List<String> contentDocumentId;
    }

    @InvocableMethod(label='Link Document to Parent Opportunity' description='Links a document to the Opportunity related to the provided Quote record.')
    public static void linkDocumentToOpportunity(List<FlowInputs> inputs) {
        try {
            // Ensure we have inputs and a record ID to work with
            if(inputs == null || inputs.isEmpty() || inputs[0].recordId == null) {
                throw new IllegalArgumentException('The input list is empty or does not contain a recordId');
            }
            
            Id recordId = inputs[0].recordId;
            Id contentDocumentId = inputs[0].contentDocumentId[0];
            
            // Retrieve the related Opportunity from the Quote
            SBQQ__Quote__c quote = [SELECT Id, SBQQ__Opportunity2__c FROM SBQQ__Quote__c WHERE Id = :recordId LIMIT 1];
            if (quote != null && quote.SBQQ__Opportunity2__c != null) {
                // Create a new ContentDocumentLink instance and populate its fields
                ContentDocumentLink docLink = new ContentDocumentLink();
                docLink.ContentDocumentId = contentDocumentId;
                docLink.LinkedEntityId = quote.SBQQ__Opportunity2__c;
                docLink.ShareType = 'V'; // V for Viewer permission, can be set to 'C' for Collaborator or 'I' for Inferred permission
                docLink.Visibility = 'AllUsers'; // Control the visibility of the linked document
                
                // Insert the new ContentDocumentLink
                insert docLink;
            } else {
                System.debug('Quote not found or does not have a related opportunity.');
            }

        } catch (Exception e) {
            System.debug(LoggingLevel.ERROR, 'The following exception has occurred: ' + e.getMessage());
        }
    }
}

Step 4: Exception Handling

To ensure robustness, the entire linking process is wrapped in a `try-catch` block. If anything goes awry, the exception is caught, and a descriptive error message is logged, making debugging and monitoring simpler.

Conclusion

By leveraging Salesforce's automation capabilities through a combination of Screen Flows and Apex, the process of attaching documents to Opportunities becomes a breeze, saving time and minimizing the room for human error. This automated solution exemplifies the power of Salesforce's customization options and the efficiency gains that can be achieved by understanding and utilizing these tools to their fullest potential.

Remember, before implementing this solution in a production environment, it’s vital to perform thorough testing in a sandbox or developer org. Proper testing ensures that the automation works as intended and that all necessary permissions are correctly configured.

2 views0 comments

Recent Posts

See All

Comments

Rated 0 out of 5 stars.
No ratings yet

Add a rating
bottom of page