function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
Mathew Andresen 5Mathew Andresen 5 

Save PDF as attachment

Hi,

I have a Visualforce page that creates a PDF contract of an opportunity, and I'm trying to save the PDF as an attachment on the opportunity.  I think I'm almost there, but I'm getting an error 

"DML currently not allowed 
An unexpected error has occurred. Your development organization has been notified."


here is my controller extension
public class savePDF {

        // The extension constructor initializes the private member
    // variable pageOpp by using the getRecord method from the standard
    // controller.
    Private Opportunity pageOpp;
    public savePDF(ApexPages.StandardController stdController) {
        this.pageOpp = (Opportunity)stdController.getRecord();
        system.debug('id = ' + pageOpp.id);
        
         PageReference pdf = Page.TWC_Contract;
    // create the new attachment
    Attachment attach = new Attachment();

    // the contents of the attachment from the pdf
    Blob body;

    try {

        // returns the output of the page as a PDF
        body = pdf.getContent();

    // need to pass unit test -- current bug    
    } catch (VisualforceException e) {
        body = Blob.valueOf('Some Text');
    }

    attach.Body = body;
    // add the user entered name
    attach.Name = 'test';
    attach.IsPrivate = false;
    // attach the pdf to the account
    attach.ParentId = pageOpp.Id;
    insert attach;

    }
  
}
I kind of hypridized the code based on the post here
http://blog.jeffdouglas.com/2010/07/14/attach-a-pdf-to-a-record-in-salesforce/


Thanks,

 
Best Answer chosen by Mathew Andresen 5
Mathew Andresen 5Mathew Andresen 5
I think I got it all figured out.  It seems that if you have renderAs=”PDF” on, things don’t work out right.  So I have it rendering as a HTML page, and then saving as a PDF.  So that is successfully working right now.  When I click my button on the opportunity page it generates the contract, and then saves it as a PDF attachment on the calling Opportunity
 
Below is what worked for me.  Note the calling of the method from the action function

VF page (to call the method). Note, I left out the rest of the contract code 
<apex:page standardController="Opportunity" showHeader="false"  extensions="savePDF"  >

   
  <apex:form >
    <apex:actionFunction name="saveAttachement" action="{!saveAttachement}" rerender="allPanel"/>


        <script>
           window.onload=function()
           {
             saveAttachement();
           };
        </script>

  </apex:form>

</apex:page>

And the Controller
public class savePDF {

  
        // The extension constructor initializes the private member
    // variable pageOpp by using the getRecord method from the standard
    // controller.
    Date myDate = system.today();
    Private Opportunity pageOpp;
    public Boolean initialised{get; set;}

    //Constructor
    public savePDF(ApexPages.StandardController stdController) {
        this.pageOpp = (Opportunity)stdController.getRecord();
        id PageOppId = ApexPages.currentPAge().getParameters().get('id');
        initialised=false;
        system.debug('id = ' + pageOpp.id + '     id2' +PageOppId);
    }

//method to insert attachement call it on page on click of button 
public void saveAttachement() {
                
    if (!initialised && pageOpp.ID !=NULL) {

     PageReference pdf = Page.TWC_Contract;
    // create the new attachment
    Attachment attach = new Attachment();

    // the contents of the attachment from the pdf
    Blob body;

    try {

        // returns the output of the page as a PDF
        body = pdf.getContentAsPDF();
        system.debug('body should be fine');

        // need to pass unit test -- current bug    
        } catch (VisualforceException e) {
            system.debug('in the catch block');
             body = Blob.valueOf('Some Text');
        }

        attach.Body = body;
    // add the user entered name
    attach.Name = opportunity.account.name + ' ' + myDate + '.pdf';
    attach.IsPrivate = false;
    // attach the pdf to the account
    attach.ParentId = pageOpp.Id;
    insert attach;
   system.debug(attach);
initialised=true;
  } else system.debug('tried to run twice');
} 
}



 
 

All Answers

Abhi_TripathiAbhi_Tripathi
You are performing DML operation inside contructor which is not allowed, removed the insert from the constructor and put it inside a method
Like below
 
public class savePDF {

        // The extension constructor initializes the private member
    // variable pageOpp by using the getRecord method from the standard
    // controller.
    Private Opportunity pageOpp;

    //Constructor
    public savePDF(ApexPages.StandardController stdController) {
        this.pageOpp = (Opportunity)stdController.getRecord();
        system.debug('id = ' + pageOpp.id);
    }

//method to insert attachement call it on page on click of button 
public void saveAttachement() {

         PageReference pdf = Page.TWC_Contract;
    // create the new attachment
    Attachment attach = new Attachment();

    // the contents of the attachment from the pdf
    Blob body;

    try {

        // returns the output of the page as a PDF
        body = pdf.getContent();

        // need to pass unit test -- current bug    
        } catch (VisualforceException e) {
             body = Blob.valueOf('Some Text');
        }

        attach.Body = body;
    // add the user entered name
    attach.Name = 'test';
    attach.IsPrivate = false;
    // attach the pdf to the account
    attach.ParentId = pageOpp.Id;
    insert attach;

  }
  
}

 
Mathew Andresen 5Mathew Andresen 5
Ok, the first part makes sense.  Don't call it in a constructor.  But I don't understand how to call the other method then.  It's a PDF so it's not like I can have another button on the page to call it.  So I'm not quite clear on how to call this new method
Mathew Andresen 5Mathew Andresen 5
Well, I think I made some progress.  (kind of)

I put an action item in the <apex:page> block, to call the saveAttachement method.  So this is now calling.  However it's running twice.  In addition, body = pdf.getContent();  is getting an error.

I tried pdf.getContentAsPDF();

But then I get the error
Too many nested getContent calls. 
 
public class savePDF {

        // The extension constructor initializes the private member
    // variable pageOpp by using the getRecord method from the standard
    // controller.
    Private Opportunity pageOpp;

    //Constructor
    public savePDF(ApexPages.StandardController stdController) {
        this.pageOpp = (Opportunity)stdController.getRecord();
        id PageOppId = ApexPages.currentPAge().getParameters().get('id');
        system.debug('id = ' + pageOpp.id + '     id2' +PageOppId);
    }

//method to insert attachement call it on page on click of button 
public void saveAttachement() {

    system.debug('testing the method');
     PageReference pdf = Page.TWC_Contract;
    // create the new attachment
    Attachment attach = new Attachment();

    // the contents of the attachment from the pdf
    Blob body;

    try {

        // returns the output of the page as a PDF
        body = pdf.getContentAsPDF();

        // need to pass unit test -- current bug    
        } catch (VisualforceException e) {
            system.debug('in the catch block');
             body = Blob.valueOf('Some Text');
        }

        attach.Body = body;
    // add the user entered name
    attach.Name = 'test';
    attach.IsPrivate = false;
    // attach the pdf to the account
    attach.ParentId = pageOpp.Id;
    insert attach;
   system.debug(attach);

  }
  
}

 
Mathew Andresen 5Mathew Andresen 5
I think I got it all figured out.  It seems that if you have renderAs=”PDF” on, things don’t work out right.  So I have it rendering as a HTML page, and then saving as a PDF.  So that is successfully working right now.  When I click my button on the opportunity page it generates the contract, and then saves it as a PDF attachment on the calling Opportunity
 
Below is what worked for me.  Note the calling of the method from the action function

VF page (to call the method). Note, I left out the rest of the contract code 
<apex:page standardController="Opportunity" showHeader="false"  extensions="savePDF"  >

   
  <apex:form >
    <apex:actionFunction name="saveAttachement" action="{!saveAttachement}" rerender="allPanel"/>


        <script>
           window.onload=function()
           {
             saveAttachement();
           };
        </script>

  </apex:form>

</apex:page>

And the Controller
public class savePDF {

  
        // The extension constructor initializes the private member
    // variable pageOpp by using the getRecord method from the standard
    // controller.
    Date myDate = system.today();
    Private Opportunity pageOpp;
    public Boolean initialised{get; set;}

    //Constructor
    public savePDF(ApexPages.StandardController stdController) {
        this.pageOpp = (Opportunity)stdController.getRecord();
        id PageOppId = ApexPages.currentPAge().getParameters().get('id');
        initialised=false;
        system.debug('id = ' + pageOpp.id + '     id2' +PageOppId);
    }

//method to insert attachement call it on page on click of button 
public void saveAttachement() {
                
    if (!initialised && pageOpp.ID !=NULL) {

     PageReference pdf = Page.TWC_Contract;
    // create the new attachment
    Attachment attach = new Attachment();

    // the contents of the attachment from the pdf
    Blob body;

    try {

        // returns the output of the page as a PDF
        body = pdf.getContentAsPDF();
        system.debug('body should be fine');

        // need to pass unit test -- current bug    
        } catch (VisualforceException e) {
            system.debug('in the catch block');
             body = Blob.valueOf('Some Text');
        }

        attach.Body = body;
    // add the user entered name
    attach.Name = opportunity.account.name + ' ' + myDate + '.pdf';
    attach.IsPrivate = false;
    // attach the pdf to the account
    attach.ParentId = pageOpp.Id;
    insert attach;
   system.debug(attach);
initialised=true;
  } else system.debug('tried to run twice');
} 
}



 
 
This was selected as the best answer
learn sfdc 24learn sfdc 24
attached file in attachment is blank.
Ankit Arora 30Ankit Arora 30
@Everyone

I want to convert VF to PDF and then save it to Notes and attachments and below are the VF pages and apex class.

When I run the process, a pdf is created successfully at the right location under the Custom__c but its blank. When I "login to community as a user", again the same vf page is blank but when I create a custom link of vf page in Custom__c object, I see the entire letter in pdf. Before putting the extension and action tag, I was able to see the same page from the custom link and from "login to community as a user". Can you please tell me what I am doing wrong, why its showing blank page.
Also VF page render the output dynamically, its not a static page.
Please help me.

1st Visualforce Page: (Letter)
<apex:page standardcontroller="Custom__c" extensions="attachPDFToCustom" action="{!attachPDF}" standardStylesheets="false" showHeader="false" applyHtmlTag="false" applyBodyTag="false" docType="html-5.0" >
<apex:include pageName="Letter_V2"/>

2nd Visualforce Page: (Letter_V2)
<apex:page renderAs="pdf"> </apex:page>

Apex Class:
public class attachPDFToCustom {

public attachPDFToCustom(ApexPages.StandardController standardPageController) {
    }
 
public void attachPDF() {
PageReference pdfPage = Page.Letter_V2;
Attachment attach = new Attachment();
Blob pdfBlob;
      try {
             pdfBlob = pdfPage.getContent();
           }
       catch (VisualforceException e) {
             pdfBlob = Blob.valueOf('Some Text');
           }
                
attach.parentId = ApexPages.currentPage().getParameters().get('id');
attach.Name = 'Letter - '+ system.Now() + ' .pdf';
attach.body = pdfBlob;
insert attach;
    }
}


Thank you

Regards,
Ankit
supriya jain 27supriya jain 27
Has anyone found the solution, I have the same requirement to render vf as pdf page but have buttons for explicitly Saving to attachment and also for sending the pdf file as email