Take the 2-minute tour ×
Salesforce Stack Exchange is a question and answer site for Salesforce administrators, implementation experts, developers and anybody in-between. It's 100% free, no registration required.

We have a scenario where we have to add dynamic content in attachments from apex. On click of a button, I insert a list of Contacts. And in the next line, I pass it's id as parameter for a visualforce page which is rendered as PDF and used for the attachment. So this is done for each Contact in the list. But, when the .getContent() method is called, I get a visualforce exception : Unable to retrieve object. The same code works when called from a button on the record detail page. But calling this just after performing insert gives me this error.

/* code snippet */
insert lstContacts;

for(Contact c : lstContacts)
{
     sendEmail(c.Id);
}

...
...
private void sendEmail(String recordId)
{
   Pagereference pdf = Page.AttachmentPage;
   pdf.getParameters().put('id',recordId); 
   pdf.setRedirect(true);
   // Take the PDF content
   Blob b = pdf.getContent(); // HERE I GET THE EXCEPTION
}

It is a StandardController page and works fine for records already existing. Is there a limitation for not being able to access a record's standard controller right after inserting it? Need some help here.

share|improve this question
    
Have you tried removing the setRedirect call? –  Andrew Fawcett Dec 14 '12 at 10:03
    
Yes, I did that and it still doesn't work. –  Vishal Dec 14 '12 at 10:04

2 Answers 2

up vote 1 down vote accepted

I have seen this before. I haven't seen where it is documented, though.

When you pass the ID to the PDF controller directly after the insert from the same controller method the record is not available. You can output the ID in the PDF and see that it is clearly getting passed through. You can select the record back in the method where you did the insert, but for some reason the record is not available in the PDF controller. I suspect there is something going on behind the scenes with how the transactions and/or transaction contexts are handled.

You can work around it by setting the parameter and then forwarding to a blank page with an action that fires and gets the PDF for you. So, after the insert you forward to Page.IntermeditateRedirect and then in that page have something like:

<apex:page action="{!getPdf}" controller="YourPdfController/>

Or, if you want to use the same controller then have an action but set different modes and use the action as a controlling method. A co-worker of mine came up with an approach like that, implemented roughly as follows:

<apex:page action="{!handleMode}" controller="YourController"/>

Then in the controller:

public String mode { get; set; }
public YourController() {
     mode = System.currentPageReference().getParameters().get('mode');
 }
 public PageReference handleMode() {
   if (Mode  == 'renderPdf') {
       return rednerPdf();
   } else if (mode == 'insertRecord') {
       return insertRecord();
   }
 }
 public PageReference insertRecord() {
    // do the insert of the rec...then redirect to the same page
    PageReference pageRef = new PageReference('url');
    pageRef.getParameters().put('id', rec.Id);
    pageRef.getParameters().put('mode', 'renderPdf');
    pageRef.setRedirect(true);

   // after the redirect the handleMode will get called and the rednerPdf will get executed.
 }
 public PageReference renderPdf() {
    // will be able to select a record and see it based on the id parameter
 }
share|improve this answer

The problem here is that it is highly likely getContent runs in separate transaction context than the current request where your inserting your record. So in effect you have not really inserted the record until the current request scope completes (Apex auto commits on success of requests).

I've hit this problem before when attempting to dynamically create records and based PDF content on them. The only workaround is to create logic flow after the current request completes that leads to your send email code. Allow the page to rerender after inserting the record and then use either a button or the 'action' attribute on the apex:page tag to invoke your sendEmail method. At this stage the platform will have committed your record and it will be available to your emailing logic.

share|improve this answer
    
Gah! Just saw you posted that a minute ago. I agree with you. I think it has to do with the commits going on and the transaction contexts of the implementation. I could not find any documentation on it, though. –  Peter Knolle Dec 14 '12 at 10:21
    
Ah no worries, i think between us we have may have cracked this one for the Vishal. ;-) –  Andrew Fawcett Dec 14 '12 at 10:22
    
Yeah, thanks a lot guys! Since there is no documentation for this one, I was going crazy since the morning. Happy weekend! –  Vishal Dec 14 '12 at 11:26

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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