I've written the below class, called from a Trigger, which reassigns attachments from an inbound Case to an Opportunity identified via an external key. It's all working as expected, passes tests etc, so that's great.

public static void QuotePDFCloning (List<Attachment> atts) {    

Set<String> CaseIds = new Set<String>();
for(Attachment file : atts) {

    // only collect those that are for the Case object (others can be ignored)
    if(file.ParentId.getSObjectType() == Case.getSObjectType()) {

if(!CaseIds.isEmpty()) {

    Set<String> OppIds = new Set<String>();
    // find the Opportunity to which the Case relates
    Case op = [Select Id, PDF_Quote_Number__c from Case where Id in :CaseIds];{

           Set<String> OppId = new Set<String>();
    // find the Opportunity to which the Case relates
    Opportunity opp = [Select Id From Opportunity Where Quote_Reference_Number__c in :OppIds];{

        List<Attachment> attachments = new List<Attachment>();

    for(Attachment file : atts) {
        Attachment newFile = file.clone();
        newFile.ParentId = opp.Id;
    // finally, insert the cloned attachments
    insert attachments;

What I now need to account for is that the timing of the case arriving with the attachment to be cloned; it may arrive before the opportunity exists. What I'd like is where we have the if(!OppIds.isEmpty()){ condition, can I set the ELSE so that it runs the code again in 15 minutes? By this time, the Opportunity should exist and it will run as expected.

I'm thinking store the logic in another class with @future or @invocable? I'm straying into complexities I've not dealt with before, so would appreciate some advice.

1 Answer 1


A regular @future method will often be likely to execute in less than the 15 minute window you're speaking of, making it a poor choice as a solution to the problem you're trying to solve. An @invocable would need to execute from a PB. So the question there is what would cause it to fire? Without knowing that, I couldn't advise you on how that could possible solve your problem.

From the description you've provided, it sounds as though something along the lines of having a pattern where a queuable or schedulable class is ultimately called as a result of your else statement's method that queries for a string that's been written to a temporary object would be a much more robust way of solving this.

Here's how the pattern works. In your else method, you create a DML statement that writes a string you can reference in a query to a temporary object. When the DML write occurs, another trigger on the temporary object executes which starts a queueble that queries the temporary object for records that need to be attached. If it finds one, but can't get the attachment, it schedules itself to run again in 15 minutes. It does that until its either successful or until it's been run 'n' number of times, at which point it writes an error message to the record and sends and email to the admin with the record number and error message. On success, it simply deletes the record and exits quietly without scheduling itself to run again. This kind of pattern is discussed in Dan Appleman's Advanced Apex Programming.

  • Thanks @crmprogdev, I appreciate the response, a schedulable method seems to be the way I have to go.
    – CaptainHaZ
    Commented Jul 26, 2018 at 14:33

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .