How can I get a Visualforce page to save into files on the opportunity page using an extension controller?

444 views Asked by At

I am new to Salesforce and this is the first time I've ever had to open the developer console, so bear with me, the code may be rough and I appreciate any help. I have a controller and visualforce page that work to compile products from the products list on the opportunity page, create a new (non-repeating) quote number, and compile all this on to a PDF with the company logo and in the desired format; this is all called from the click of a button on the opportunity page. I want a button on the visualforce page to then, after the PDF is made and shown, save the PDF into files.

Visualforce page works as desired in creating my PDF

<apex:page controller="QuoteOppProductCtrl" extensions="QuotePDFSaveExt" applyHtmlTag="false" showHeader="false" renderAs="PDF" action="{!generateAndStoreQuoteNumber}">  
----------------the body of the content of the PDF is here ------------
</apex:page>

Main custom controller works as desired

public with sharing class QuoteOppProductCtrl {
    public Date Today { get { return Date.today(); } }
    public Opportunity opportunity { get; set; }
    public String shipToAccountName { get; set; }
    public String prosthetistName { get; set; }
    public String fittingProsthetistId { get; set; }
    public String prosthetistEmail { get; set; }
    public Decimal subtotal { get; set; }
    public Decimal quoteTotal { get; set; }
    public String quoteNumber { get; set; } // To store the generated quote number
--------- body of controller ---------   

Controller Extension: UPDATED Error is stating that there is a null reference for QuotePDFSaveExt(ApexPages.StandardController ctrl)

public with sharing class QuotePDFSaveExt{
    String oppId{get;set;}
    public QuotePDFSaveExt(ApexPages.StandardController ctrl) {
        oppId = ctrl.getId();
    }
        
    public PageReference savePDF() {
      // Generate the PDF here
       PageReference pdfPage = Page.QuoteOppProductVfp;
      // recordId = opp.Id;
       pdfPage.getParameters().put('Id', oppId);
       pdfPage.setRedirect(true);
        
       PageReference ret;

        try {
      Blob pdfBlob = Test.isRunningTest()
        ? Blob.valueOf('Test PDF Content')
        : pdfPage.getContentAsPDF();

      ContentVersion cv = new ContentVersion(
        Title = 'Quote ' + oppId,
        PathOnClient = 'Quote ' + oppId + '.pdf',
        VersionData = pdfBlob,
        FirstPublishLocationId = oppId
      );
      insert cv;
      ret = new ApexPages.StandardController([SELECT Id FROM ContentDocument WHERE LatestPublishedVersionId = :cv.Id]).view();
        } 
        catch (exception e) {
      ApexPages.addMessages(e);
        }
    return ret;

    }
}

Added VFP to save PDF

<apex:page standardController="Opportunity" extensions="QuotePDFSaveExt">
    <apex:pageMessages />
    <apex:form >
        <apex:pageBlock title="preview and save">
            <apex:pageBlockButtons >
                <apex:commandButton value="Save" action="{!savePDF}" />
            </apex:pageBlockButtons>
            <apex:iframe src="{!URLFOR($Page.QuoteOppProductVfp, null, [id=Opportunity.Id])}" scrolling="true" />
        </apex:pageBlock>
    </apex:form>
</apex:page>
2

There are 2 answers

0
amyverghese On BEST ANSWER

As the VFP and the controller worked in creating the PDF I needed as an output, these remained unchanged from my question. I took the advice of creating a separate controller to create a "save pdf" method, and a separate Visualforce page to have a "Save" button. The button from the opportunity page calls the new Visualforce page.

The additional controller

public class QuotePDFSaveExt {
public ID oppId { get; set; }
public Opportunity opportunity {get;set;}

public QuotePDFSaveExt() {
    oppId = ApexPages.currentPage().getParameters().get('id');
    System.debug('opportunity Id: ' + oppId);
    opportunity = [SELECT Id, Name
                  FROM Opportunity
                  WHERE Id = :oppId];
}

public PageReference savePDF() {
    PageReference pdfPage = Page.QuoteOppProductVfp;
    pdfPage.getParameters().put('Id', oppId);
    pdfPage.setRedirect(true);
    PageReference ret = null;

    try {
        Blob pdfBlob = Test.isRunningTest()
                ? Blob.valueOf('Test PDF Content')
                : pdfPage.getContentAsPDF();

        ContentVersion cv = new ContentVersion(
                Title = 'Quote ' + oppId,
                PathOnClient = 'Quote ' + oppId + '.pdf',
                VersionData = pdfBlob,
                FirstPublishLocationId = oppId
        );
        insert cv;
        System.debug('ContentVersion inserted: ' + cv);
        
        ret = new ApexPages.StandardController([SELECT Id FROM ContentDocument WHERE LatestPublishedVersionId = :cv.Id]).view();
    } catch (Exception e) {
        ApexPages.addMessages(e);
        System.debug('An exception occurred: ' + e.getMessage());
    }
    System.debug('Returning PageReference: ' + ret);
    return ret;
    }
}

The additional Visualforce page

<apex:page controller="QuotePDFSaveExt" >
<apex:pageMessages />
<apex:form >
    <apex:pageBlock title="preview and save">
        <apex:pageBlockButtons >
            <apex:commandButton value="Save" action="{!savePDF}"/>
        </apex:pageBlockButtons>
        <apex:include pageName="QuoteOppProductVfp">
        </apex:include>
    </apex:pageBlock>
</apex:form>
</apex:page>
6
eyescream On

Cut it into 2 pages, it'll be easier to maintain. I'll also use 2 separate apex controllers but it could be just one used in both.

First make your PDF. Simple, single concern, just make it pretty.

public with sharing class PdfPrint {
    // This can be done with pure Visualforce but just for demo sake
    Id accountId;
    public PdfPrint(ApexPages.StandardController ctrl) {
        accountId = ctrl.getId();
    }

    public List<Opportunity> getOpps(){
        return [SELECT Id, Name, StageName, CloseDate
        FROM Opportunity
        WHERE AccountId = :accountId
        ORDER BY Name];
    }
}
<!-- I called the page AccountAndOppsPdf -->
<apex:page standardController="Account" extensions="PdfPrint" readOnly="true" renderAs="pdf">
    <!-- put logo and stuff here, make this pretty -->
    <apex:detail subject="{!Account.Id}" relatedList="false" />
    <apex:pageBlock title="Opps">
        <apex:pageBlockTable value="{!opps}" var="o">
            <apex:column value="{!o.Name}"/>
            <apex:column value="{!o.StageName}"/>
            <apex:column value="{!o.CloseDate}"/>
        </apex:pageBlockTable>
    </apex:pageBlock>
</apex:page>

Once you're happy with it - make the other page that displays 1st one in an iframe.

public with sharing class PdfPreview {
  Id accountId;
  public PdfPreview(ApexPages.StandardController ctrl) {
    accountId = ctrl.getId();
  }

  public Pagereference save() {
    PageReference pdfPage = Page.AccountAndOppsPdf;
    pdfPage.getParameters().put('Id', accountId);
    pdfPage.setRedirect(true);

    PageReference ret;

    try {
      Blob pdfBlob = Test.isRunningTest()
        ? Blob.valueOf('Test PDF Content')
        : pdfPage.getContentAsPDF();

      ContentVersion cv = new ContentVersion(
        Title = 'Quote ' + accountId,
        PathOnClient = 'Quote ' + accountId + '.pdf',
        VersionData = pdfBlob,
        FirstPublishLocationId = accountId
      );
      insert cv;
      ret = new ApexPages.StandardController([SELECT Id FROM ContentDocument WHERE LatestPublishedVersionId = :cv.Id]).view();
    } catch (exception e) {
      ApexPages.addMessages(e);
    }
    return ret;
  }
}
<apex:page standardController="Account" extensions="PdfPreview">
    <apex:pageMessages />
    <apex:form>
        <apex:pageBlock title="preview and save">
            <apex:pageBlockButtons>
                <apex:commandButton value="Save" action="{!save}" />
            </apex:pageBlockButtons>
            <apex:iframe src="{!URLFOR($Page.AccountAndOppsPdf, null, [id=Account.Id])}" scrolling="true" />
        </apex:pageBlock>
    </apex:form>
</apex:page>

demo