Print this page

PDF generated via Visualforce with two Apex DML statements in the same transaction has missing data

Knowledge Article Number 000232398
Description
After the  Summer 15 release of  critical updates applied, the calls from PagePreference.getContent() & getContentAsPDF() are handled as callout. This means that the transactions between caller and callee are different, and the values which are updated on the caller transaction cannot be referred to the callee transaction. For example, the caller  transaction being the record inserted into the database and the callee being logic to render the values in a PDF. This has been considered as working as designed behavior due enhancements made to improve the semantics of the transaction for the calling page. For more information, please review this help documentation for more information.

In the case below,  the pdfPage.getContent() does a call out to W2908182Pdf. Then,  the PDF  is rendered by W2908182Pdf and is executed as another request in other thread.When calling pdfPage.getContent(), a transaction which inserted an Account is not still committed. So, the W2908182Pdf Visualforce page cannot get the record. Therefore, the rendered PDF file does not have data to display in the fields.

Example:
Note: There are all sample codes. Please test and verify before using. 

1. Create the  following Visualforce page
Name : W2908182Pdf
<apex:page standardController="Account" renderAs="PDF">
    <h1>PDF</h1>
    <apex:outputLabel value="Account Name"></apex:outputLabel>
    <div>
        <apex:outputText value="{!Account.Name}"></apex:outputText>
    </div>
</apex:page>

2. Create the following Apex class
public class W2908182 {
    
    public Account acct { set; get; }
    
    public W2908182(ApexPages.StandardController controller) {
        acct = (Account)controller.getRecord();
    }
    
    public PageReference save() {
        return null;
    }
    
    public PageReference create() {
        acct.Name='Test W2908182';
        insert acct;
        
        Attachment pdfFIle = new Attachment(name='test.pdf');
        PageReference pdfPage = new PageReference('/apex/W2908182Pdf');
        pdfPage.getParameters().put('id', acct.Id);
        pdfFIle.body = pdfPage.getContent(); 
        pdfFIle.parentid = acct.Id; 
        pdfFIle.isprivate=true;
        insert pdfFIle;
        
        PageReference acctPage = new PageReference('/' + acct.id);
        return acctPage;
    }
}

3. Create the following Visualforce page
Name : W2908182
<apex:page standardController="Account" extensions="W2908182">
    <apex:form >
        <apex:commandButton value="TEST" action="{!create}"/>
    </apex:form>
</apex:page>


4. Access the Visualforce page which was created in Step 3
You will see the "TEST" button.

5. Click  on the "TEST" button.
You will see it is navigated to a created Account page.

6. Check an attached PDF file in the created Account
You will see that the "Account Name" is not rendered in the pdf.
 
 

 
 
Resolution By separating the server calls using Visualforce Remoting and allowing the calls to be executed by Javascript, this resolves the issue. Even the use of @future methods can help separate the transaction with the use of this method it can run on the  asynchronously.

Example:

1. Create the Visualforce page - same as original one
Name : W2908182Pdf
<apex:page standardController="Account" renderAs="PDF">
<h1>PDF</h1>
<apex:outputLabel value="Account Name"></apex:outputLabel>
<div>
<apex:outputText value="{!Account.Name}"></apex:outputText>
</div>
</apex:page>
2. Create the following Apex class
global class W2908182 {
public W2908182(ApexPages.StandardController controller) {
}

@RemoteAction
global static Account createOne() {
Account ac = new Account();
ac.Name='Test W2908182';
insert ac;
return ac;
}

@RemoteAction
global static String storePDF(String acctId) {
Attachment pdfFIle = new Attachment(name='test.pdf');
PageReference pdfPage = new PageReference('/apex/W2908182Pdf');
pdfPage.getParameters().put('id', acctId);
pdfFIle.body = pdfPage.getContent(); 
pdfFIle.parentid = acctId; 
pdfFIle.isprivate=true;
insert pdfFIle;
return acctId;
} 
}
3. Create the following Visualforce page
Name : W2908182
<apex:page standardController="Account" extensions="W2908182">
<script type="text/javascript">
function test() {
Visualforce.remoting.Manager.invokeAction(
'{!$RemoteAction.W2908182.createOne}',
function(result, event){
if (event.status) {
storePDF(result.Id);
} else if (event.type === 'exception') {
//Need to do some error handling
} else {
//Need to do some error handling
}
}, 
{escape: true}
);
}
function storePDF(id) {
Visualforce.remoting.Manager.invokeAction(
'{!$RemoteAction.W2908182.storePDF}',
id,
function(result, event){
if (event.status) {
window.location='/' + result;
} else if (event.type === 'exception') {
//Need to do some error handling
} else {
//Need to do some error handling
}
}, 
{escape: true}
);
}
</script>
<button onClick="test();">TEST</button>
</apex:page>

4. Access the Visualforce page which was created in Step 3
You will see the "TEST" button.

5. Click  on the "TEST" button
You will see it is navigated to a created Account page.

6. Check an attached pdf in the created Account








 




promote demote