0

I am trying to setup these 2 things. I am an Apex newbie so it might be obvious to an experienced developer.

1 below creates an attachment in an opportunity record. Only after it is done I need #2 to run which gets the attachment info and store it in a second object. It just store the docID, parentID of the attachment and a checkbox.

This code works. The only issue is that without flex queue, #2 run before #1 is finished and it can't get the info to store in the second object.

Thanks

G

1) This is a working call out. I just run it as you see it. doco.RenderAPI.renderCreateFileAsync(3, oppID, doco.RenderAPI.Mode.Attachment, doco.RenderAPI.Format.PDF,'filename ' + datetime.now());

2) This is a method that needs to run only after #1 completes.

public Boolean addAttachmentToDAIObject (String oppID)
{
    // Create new attachment document in new Document_Additional_Info__c() Object

    List<Document_Attachment_Info__c> listToUpdate   = new List<Document_Attachment_Info__c>();
    List<Attachment>                  allAttachments = new List<Attachment>();

    allAttachments = [Select ID, ParentID, Name from Attachment Where ParentID = :oppID AND Name LIKE '%filename%'];

    if(allAttachments.size() > 0) 
    { 
        for (Attachment att : allAttachments)
        {

            String docID = String.valueof(att.id);

            Document_Attachment_Info__c daiField = new Document_Attachment_Info__c(Attachment_ID__c = docID, Parent_ID__c = att.ParentId, Is_Portal__c = True);

            listToUpdate.add(daiField);
        }

        try
        {
            insert listToUpdate;

            // Integer iRet = Database.insert(listToUpdate,true);
        }
        catch (Exception e)
        {
            System.debug('ERROR:' + e);
            Opportunity[] opps;
            for( Opportunity opp : opps)
            {
                opp.AddError('DEBUG: Error in code' + e);
            }
        }
    }
    else System.debug('DEBUG: Error in code');

    return True;

 }  
1
1

If you're calling the 1st code using a queuable, you can chain the code to run the 2nd part after the 1st runs. However, you'll need to rewrite it so that it queries what it needs based on the results of what was in the initial asynch job. If you can do it in that manner, that would be the simplest. If not, you'll want to write the data to a custom object or "flag" the record so it can be queried by the "cleanup" that will be called from the initial asynch job (query for asynch job limits first to see if it can run).

EDIT

Okay, here's some sample code by Dan Appleman on how to chain a queuable. You could also chain it with a batch or scheduluable instead.

public without sharing class GoingAsync4
    implements queueable, Database.AllowsCallouts {

        public void execute(QueueableContext context)
       {


        // This is a control utility Dan uses to be able to turn off his queuable classes 
        // at will. Would be wise for you to implement this pattern as well.

        if(!AppConfigSupport.appEnabled) return; // On/off switch
        List<AsyncRequest__c> requests;
        try
        {


            requests = [Select ID, AsyncType__c, Params__c 
                from AsyncRequest__c 
                where Error__c = false And
                CreatedById = :UserInfo.getUserId() 
                Limit 1 for update];
        }
        catch(Exception ex) { return; }
        if(requests.size()==0 ) return;

        AsyncRequest__c currentRequest = requests[0];


        // Because he's chaining the same job, he first checks to see if there's an 
        // instance of it that's currently running as below

        try
        {
            if(currentRequest.AsyncType__c=='Translate Solution') 
                translate(currentRequest);

            // Add more here

            delete currentRequest;
            // Optional
            database.emptyRecycleBin(new List<ID>{currentRequest.id}); 

        }
        catch(Exception ex)
        {
            currentRequest.Error__c = true;
            currentRequest.Error_Message__c = ex.getMessage();
            update currentRequest;
        }


        // If he has more requests to make and wants to chain another queueable 
        // here's what he does as below in this particular use case            


        List<AsyncRequest__c> moreRequests = [Select ID, AsyncType__c, Params__c 
            from AsyncRequest__c 
            where Error__c = false 
            and ID <> :currentRequest.id 
            and CreatedById = :UserInfo.getUserId() 
            Limit 1 ];

        // if no more jobs, he returns
        if(moreRequests.size()==0) return;

        // if more jobs to do, he uses a try-catch block to enque the next one
        try
        {
            enqueGoingAsync4(context.getJobId());
        }
        catch(Exception ex)
        {
            tryToQueue();
        }


}


    if(!AppConfigSupport.appEnabled) return; // On/off switch
    List<AsyncRequest__c> requests;
    try
    {
        requests = [Select ID, AsyncType__c, Params__c 
            from AsyncRequest__c 
            where Error__c = false And
            CreatedById = :UserInfo.getUserId() 
            Limit 1 for update];
    }
    catch(Exception ex) { return; }
    if(requests.size()==0 ) return;

    AsyncRequest__c currentRequest = requests[0];

    try
    {
        if(currentRequest.AsyncType__c=='Translate Solution') 
            translate(currentRequest);

        // Add more here

        delete currentRequest;
        // Optional
        database.emptyRecycleBin(new List<ID>{currentRequest.id}); 

    }
    catch(Exception ex)
    {
        currentRequest.Error__c = true;
        currentRequest.Error_Message__c = ex.getMessage();
        update currentRequest;
    }

    List<AsyncRequest__c> moreRequests = [Select ID, AsyncType__c, Params__c 
        from AsyncRequest__c 
        where Error__c = false 
        and ID <> :currentRequest.id 
        and CreatedById = :UserInfo.getUserId() 
        Limit 1 ];

    if(moreRequests.size()==0) return;

    try
    {
        enqueGoingAsync4(context.getJobId());
    }
    catch(Exception ex)
    {
        tryToQueue();
    }

}

In your case, you'll want to check for available async limits on queuables before making your call to chain your next asynch job to and avoid an exception using a try-catch block of some kind along these lines:

        try  
        {
        if((criteria to detect instance of your 2nd async class == false)&&
          (solutionsToUpdate.size()== allowedCallouts &&
            Limits.getLimitQueueableJobs() - Limits.getQueueableJobs() > 0))
            try
            {
                //if asynch callouts are available, enque the job
                system.enqueueJob(new GoingAsync2ndTypeJob());
            } catch(Exception ex)
            {
                // If they're not, Dan can calls code to do the job in real time or use some other logic to preserve chaining 
                // Alternate chaining mechanism
            }
        }
1
  • Yes but I need to know how to set this up. A code example where to put the methods and call them.
    – sf guest
    Dec 28 '15 at 15:47

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.