+ Start a Discussion
Douglas MolinaDouglas Molina 

Problem generating PDF attachment with onComplete commandButton

Hi!

In my org, I have the Opportunity object, that has a related list from a custom object named Proposta__c.
Proposta__c stores some Opportunity fields values, and a custom PDF as attachment, that contains other Opportunity informations.
Proposta__c records are generated only by a button, embedded in the Opportunity layout, as a Visualforce page commandButton (not a object's custom buttom).
So, in the Opportunity, the user clicks on this button, it calls a Apex extension class that I wrote, that creates a Proposta__c record with its fields, and generates/attach the PDF on this record.
Until here, everything is working as expected.

But, after create a Proposta__c record, I need open It, so I decided to use the onComplete commandButton property to do It.
The onComplete works fine to open the new Proposta__c record, but, when I use onComplete, the PDF attachment is generated as a blank page.
Even if I include the onComplete with no actions on it, the PDF still generate as a blank page.
If I remove only the onComplete property, the PDF is generated perfectly, with all its contents.

The point is: How can I generate correctly the regs and PDF, and then open it? maybe with a onComplete alternative..

Bellow are some tests fragments codes, to make it clearer:

proposta_pdf: The Apex extension class, that generates the Proposta__c record and its PDF attachment:
public class proposta_pdf{
    
    public     Opportunity     opp_temp, opp_reg;
    public     Id              prop_reg_id {get;set;}
    public     String          url_base {get;set;}
 
    // constructor ==============================
    public proposta_pdf(ApexPages.StandardController controller) {
        
        // Get base URL (+prefix, if community)
        this.url_base = URL.getSalesforceBaseUrl().toExternalForm() + Site.getPathPrefix();
        
        // Get other values
        this.opp_temp = (Opportunity)controller.getRecord();
        this.opp_reg = [
            SELECT     Id, Name, Amount, Prop_Versao__c
            FROM     Opportunity 
            WHERE     Id =: this.opp_temp.Id
        ];
    }
       
    // geenerate PDF bin ========================
    public Blob gera_prop_blob(){
        PageReference prop_pag = Page.Proposta_Mestre;
        prop_pag.getParameters().put('id',opp_reg.Id);
        
        Blob prop_blob;
        if(!Test.isRunningTest())
            prop_blob = prop_pag.getContentAsPDF();
        else
            prop_blob = Blob.valueOf('Este e um teste.');

        return prop_blob;
    }

    // create Proposta__c reg + PDF attachment ======
    public void salva_proposta_registro(){

        // validation
        if(this.opp_reg.Prop_Versao__c == NULL)
            this.opp_reg.Prop_Versao__c = 0;
        else
            this.opp_reg.Prop_Versao__c++;
        
        // create/save Proposta__c reg
        Proposta__c prop_reg = new Proposta__c(
            Oportunidade__c     = this.opp_reg.Id,
            Name                = this.opp_reg.Name + ' Ver:' + this.opp_reg.Prop_Versao__c,
            Versao__c             = this.opp_reg.Prop_Versao__c,
            Valor__c            = this.opp_reg.Amount
        );
        insert prop_reg;
                
        // update Proposta__c version stored in a Opportunity field
        update this.opp_reg;
                
        // create/save/attach PDF in Proposta__c reg
        Attachment prop_anex = new Attachment(
            ParentId             = prop_reg.Id,
            Body                 = gera_prop_blob(),
            Name                 = this.opp_reg.Name + ' Ver:' + this.opp_reg.Prop_Versao__c + '.pdf'
        );
        insert prop_anex;
        
        // retur ID to open new reg
        prop_reg_id = prop_reg.Id;
    }

}

Proposta_Mestre: The Visualforce page, rendered as PDF. The Proposta__c attachment:
<apex:page standardController="Opportunity" renderAs="pdf" applyHtmlTag="false" showHeader="false">
	
	Testing Opportunity My PDF
	<br/>ID: {!Opportunity.Id}
	<br/>Name: {!Opportunity.Name}
	<br/>
	<br/>End.
	
</apex:page>

proposta_botoes: The visualforce page that contains the button, embedded in Opportunity layout:
<apex:page standardController="Opportunity" extensions="proposta_pdf">
    <apex:form>
        <apex:inputHidden value="{!Opportunity.Id}"/>          
        <apex:commandButton 
            value="Save" 
            action="{!salva_proposta_registro}" 
            onComplete="window.top.location.replace('{!url_base}/{!prop_reg_id}');"
        />
    </apex:form>
</apex:page>

This is what the Opportunity detail page looks like:
User-added image

And the Proposta__c record, created by the button:
User-added image

When I do NOT use the onComplete in the button, the PDF is generated in the right way:
User-added image

And finally, if I add the onComplete in the commandButton, only a blank page is generated:
User-added image

Additionally, I already tried:
 - Add the extensions="proposta_pdf" in the Visualforce PDF page
 - Use the controller.addFields() method to pass the required fields
With no sucess.

I'm preety new in Apex development, so this can be just a detail that I didn't notice, or some mistake.. but after days searching a solution, I'm still not able to figure out how to fix it.

I'd apreciate some sugestions..
And sorry for my bad english, I'm still beginner :)
Thanks.
Tintu_BabuTintu_Babu
Hi Douglas,
Instead of using on complete why don't you try returning pagereference from salva_proposta_registro method 


public PageReference  salva_proposta_registro(){
PageReference pdf = Page.Proposta_Mestre;
 //your insertion logic here 
 
prop_reg_id = prop_reg.Id;

eturn new PageReference('/'+prop_reg_id);
}

Let me know this works .
Douglas MolinaDouglas Molina
Hi TintuB!

Thank you, your suggestion solved part of the problem.
I made the changes you said, and added this lines in my embedded visualforce page, to open the next page in the parent window, Instead of opening inside the iframe, where the visualforce button page is located in:
 
<head>
	<base target="_parent"/>	
</head>

But now, I'm facing another problem..
This functionality will be avaliable for partner users, in a Force.com site. (something like https://myorgname.force.com/sitename/)
In Internal Org, this works fine, but, when I log in to this partner site, and try to do the same, I recieve this error:

User-added image
At the top, It says "The page you submitted was invalid for your session. Please click Save again to confirm your change"

I gave a search, and found some topics covering the same situation, some of then refering it as a kind of bug, and linking it to this know issue:

Spring 14 regression- getContentAsPDF() not working with PageReference(‘partialURL’):
https://success.salesforce.com/issues_view?id=a1p30000000T3HFAA0

They say, as a workaround: "Use Page.existingPageName" instead, but i think it wont work in my case.

I already tried the follow:
 - Disable "PageReference getContent() and getContentAsPDF() Methods Behave as Callouts" critical update, but it's not avaliable to disable anymore
 - The same with "Enable CSRF protection on GET and POST requests", no more avaliable.
 - Give the users/site profiles, all access to the visualforce pages and apex classes.
 - Check the "Available for Salesforce mobile apps and Lightning Pages" and uncheck the "Require CSRF protection on GET requests" properties in all visualforce pages.
 - Remove the "<base target="_parent"/>", the problem continues, but inside the a iframe.

Any other sugestion to fix or workaround It?

Thanks.

 
Douglas MolinaDouglas Molina
Still not solved, and I have no more ideas to fix it..
Some other thing I could try ?