6

I'm trying to programmatically generate and insert a Quote PDF, given a Quote Id and a Template Id, within an @AuraEnabled method in my Lightning component's server-side controller. (N.b. - the component is a Quick Action; the Quote already exists).

Programmatic Quote generation is not too controversial, one simply uses getContentAsPDF() on the URL for the Quote template viewer, /quote/quoteTemplateDataViewer.apexp. Loading such a URL directly in the UI works fine. But when I do this in my controller,

@AuraEnabled
public static void insertQuotePDF(String recordId) {
        // Logic to find template Id omitted...

        PageReference pr = new PageReference('/quote/quoteTemplateDataViewer.apexp?id=' + recordId + '&summlid=' + templateId); 

        QuoteDocument qd = new QuoteDocument(); 
        Blob b = pr.getContentAsPDF();

        qd.Document = b;
        qd.QuoteId = recordId;
        insert qd;
}

It executes cleanly (no exceptions) and inserts what appears to be a quote PDF, but the content is completely blank. If I switch from getContentAsPDF() to getContent(), I find it's actually an HTML file with no readable contents containing this script:

<script>
if (this.SfdcApp && this.SfdcApp.projectOneNavigator) { SfdcApp.projectOneNavigator.handleRedirect('https://MYDOMAIN.my.salesforce.com?ec=302&startURL=%2Fquote%2FquoteTemplateDataViewer.apexp%3Fsummlid%3D<TEMPLATE ID>%26id%3D<RECORD ID>%26inline%3D1'); }  else 
if (window.location.replace){ 
window.location.replace('https://MYDOMAIN.my.salesforce.com?ec=302&startURL=%2Fquote%2FquoteTemplateDataViewer.apexp%3Fsummlid%3D<TEMPLATE ID>%26id%3D<RECORD ID>%26inline%3D1');
} else {;
window.location.href ='https://MYDOMAIN.my.salesforce.com?ec=302&startURL=%2Fquote%2FquoteTemplateDataViewer.apexp%3Fsummlid%3D<TEMPLATE ID>%26id%3D<RECORD ID>%26inline%3D1';
} 
</script>

There's no actual content there. If I try using the location noted in the script, I just get back a page saying "you have to log in to Salesforce".

I think I'm missing something essential here. Is it possible to generate a Quote PDF while inside a Lightning component's Apex action?

6

I've found a solution by experiment (with a hat tip to @KeithC, who got me speculating on a productive path), but I don't understand why it works/the direct path doesn't work. I would be happy to accept an answer from anyone who's able to help me understand the principle behind the behavior.

I was able to successfully complete the quote generation and insertion by the simple expedient of breaking the actual getContentAsPDF() callout and QuoteDocument insert into a separate @future method, as follows:

@AuraEnabled
public static void getQuoteTemplate(Id recordId) {
    insertQuoteTemplate(recordId);
}

@future(callout=true)
public static void insertQuoteTemplate(Id recordId) {
    // Logic to find template Id omitted...

    PageReference pr = new PageReference('/quote/quoteTemplateDataViewer.apexp?id=' + recordId + '&summlid=' + templateId); 

    QuoteDocument qd = new QuoteDocument(); 
    Blob b = pr.getContentAsPDF();

    qd.Document = b;
    qd.QuoteId = recordId;
    insert qd;
}

This is a little unfortunate, as the client-side component controller no longer has access to the result of the operation, but it does work.

  • You may want to look into the new interface, lightning:isUrlAddressable and the component lightning: navigation. Both are related to pagereference in lightning form. Not sure it applies exactly to your use case but do check them out. – tsalb Jun 17 '18 at 0:35
  • @david - we created a VF page to renderaspdf and then we get the contents of it in apex and send it to external system to store it. Once you pass the id to VF page it can get data using its own controller and use that to create PDF. will that not work for you? – RedDevil Jun 17 '18 at 14:31
  • @RedDevil In this case, I specifically have to render the native Quote Template with built-in functionality, not a custom Visualforce page. Have you had success using getContentsAsPDF() with a custom VF page in an @AuraEnabled method? That would be an interesting difference. – David Reed Jun 17 '18 at 14:56
4

The getContentAsPDF call operates in a separate transaction. So if you make that call from within the transaction that sets up the data, that getContentAsPDF call will not see the data at all as the transaction that sets up the data won't have committed.

Making two sequential calls from the Lightning Component - one to setup the data and then after that one to generate the PDF - is one way to work-around the problem.

  • Keith, I am not sure I follow you. I should clarify that the Quote is preexisting (the Lightning Component is actually surfaced as a Quick Action on the Quote page). – David Reed Jun 16 '18 at 23:50
  • @DavidReed My bad; I was assuming that "// Logic omitted..." was setting up the data that the PDF was generated from. If that data pre-exists then yes it's something else. – Keith C Jun 17 '18 at 0:00
  • @DavidReed Undeleted this for a bit of context to your answer. – Keith C Jun 17 '18 at 0:38

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.