我是 Salesforce 的新手,这是我第一次打开开发人员控制台,所以请耐心等待,代码可能很粗糙,我感谢任何帮助。我有一个控制器和 Visualforce 页面,用于编译机会页面上产品列表中的产品,创建新的(非重复)报价编号,并将所有这些编译为带有公司徽标和所需格式的 PDF;这一切都是通过点击机会页面上的按钮来调用的。我想要在 Visualforce 页面上有一个按钮,以便在制作并显示 PDF 后,将 PDF 保存到文件中。
**Visualforce 页面(我已尝试删除 renderAs 语句):**
<apex:page controller="QuoteOppProductCtrl" extensions="QuoteOppProductCtrlExtension" applyHtmlTag="false" showHeader="false" renderAs="PDF" action="{!generateAndStoreQuoteNumber}">
<apex:form >
<apex:pageBlock >
<apex:pageBlockButtons location="TOP">
<apex:commandButton action="{!generateAndUploadPDF}" value="Save PDF"/>
</apex:pageBlockButtons>
</apex:pageBlock>
</apex:form>
<html>
----------------the body of the content of the PDF is here -------------------------
</html>
</apex:page>
**主控制器:**
public 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
public Integer currentYear = Date.today().year();
public String quoteNumberPrefix = 'Q-' + String.valueOf(currentYear).right(2);
public QuoteOppProductCtrl() {
String opportunityId = ApexPages.currentPage().getParameters().get('id');
// Replace the Opportunity query with the hard-coded ID
opportunity = [SELECT Id, Name, ShipToAcct__c, Account.Name, ShipToAcct__r.ShippingStreet, ShipToAcct__r.ShippingCity,
ShipToAcct__r.ShippingState, ShipToAcct__r.ShippingPostalCode,
ShipToAcct__r.ShippingCountry, Opp_No__c, Cable__c, fitting_prosthetist__c,
(SELECT Quantity, UnitPrice, TotalPrice, PricebookEntry.Product2.Name, PricebookEntry.Product2.Description
FROM OpportunityLineItems)
FROM Opportunity
WHERE Id = :opportunityId];
if (opportunity.ShipToAcct__c != null) {
shipToAccountName = opportunity.Account.Name;
}
fittingProsthetistId = opportunity.fitting_prosthetist__c;
if (fittingProsthetistId != null) {
Contact fittingProsthetist = [SELECT Id, Name, Email FROM Contact WHERE Id = :fittingProsthetistId LIMIT 1];
prosthetistName = fittingProsthetist.Name;
prosthetistEmail = fittingProsthetist.Email;
}
// Calculate subtotal and quote total
subtotal = 0;
for (OpportunityLineItem lineItem : opportunity.OpportunityLineItems) {
subtotal += lineItem.TotalPrice;
}
// tax = .1 * subtotal
// Ship_handling = we need to figure this out
quoteTotal = subtotal; // You can adjust this based on your tax and shipping calculations.
}
// Check if a quote number already exists for the current year
public Boolean isQuoteNumberEmpty() {
// Query for existing quote numbers for the current year
List<Quote_Number__c> existingQuoteNumbers = [SELECT ID, Name FROM Quote_Number__c WHERE Name LIKE :quoteNumberPrefix];
// If the list is empty, no quote number exists for the current year
return existingQuoteNumbers.isEmpty();
}
//Creates quote number, and then stores it into the opportunity page
public void generateAndStoreQuoteNumber() {
// Query for the latest quote number for the current year
Quote_Number__c latestQuoteNumber = [SELECT ID, Name
FROM Quote_Number__c
WHERE Name LIKE :quoteNumberPrefix + '%'
ORDER BY Name DESC
LIMIT 1];
if (latestQuoteNumber != null) {
// If a quote number for the current year exists, increment it by 1
Integer lastNumber = Integer.valueOf(latestQuoteNumber.Name.substring(quoteNumberPrefix.length())) + 1;
String newQuoteNumber = quoteNumberPrefix + String.valueOf(lastNumber).leftPad(4,'0');
Quote_Number__c quoteNumberRecord = new Quote_Number__c();
quoteNumberRecord.Opportunity__c = opportunity.Id;
quoteNumberRecord.Name = newQuoteNumber;
insert quoteNumberRecord;
quoteNumber = newQuoteNumber;
}
else {
// If no quote number for the current year exists, start with 0000
String newQuoteNumber = quoteNumberPrefix + '0000';
Quote_Number__c quoteNumberRecord = new Quote_Number__c();
quoteNumberRecord.Opportunity__c = opportunity.Id;
quoteNumberRecord.Name = newQuoteNumber;
insert quoteNumberRecord;
quoteNumber = newQuoteNumber;
}
// Update the Opportunity record with the generated quote number
opportunity.QuoteNum__c = quoteNumber;
update opportunity;
}
}
控制器扩展 - 我认为我的问题出在这段代码中
public class QuoteOppProductCtrlExtension {
private ApexPages.StandardController stdController;
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 recordId {get;set;}
public QuoteOppProductCtrlExtension(QuoteOppProductCtrl Maincontroller) {
String opportunityId = ApexPages.currentPage().getParameters().get('id');
opportunity = [SELECT Id, Name, ShipToAcct__c, Account.Name, ShipToAcct__r.ShippingStreet, ShipToAcct__r.ShippingCity,
ShipToAcct__r.ShippingState, ShipToAcct__r.ShippingPostalCode,
ShipToAcct__r.ShippingCountry, Opp_No__c, Cable__c, fitting_prosthetist__c,
(SELECT Quantity, UnitPrice, TotalPrice, PricebookEntry.Product2.Name, PricebookEntry.Product2.Description
FROM OpportunityLineItems)
FROM Opportunity
WHERE Id = :opportunityId];
if (opportunity.ShipToAcct__c != null) {
shipToAccountName = opportunity.Account.Name;
}
fittingProsthetistId = opportunity.fitting_prosthetist__c;
if (fittingProsthetistId != null) {
Contact fittingProsthetist = [SELECT Id, Name, Email FROM Contact WHERE Id = :fittingProsthetistId LIMIT 1];
prosthetistName = fittingProsthetist.Name;
prosthetistEmail = fittingProsthetist.Email;
}
// Calculate subtotal and quote total
subtotal = 0;
for (OpportunityLineItem lineItem : opportunity.OpportunityLineItems) {
subtotal += lineItem.TotalPrice;
}
quoteTotal = subtotal; // You can adjust this based on your tax and shipping calculations.
}
public QuoteOppProductCtrlExtension(ApexPages.StandardController controller) {
stdController = controller;
}
public ApexPages.PageReference generateAndUploadPDF() {
String recordId = ApexPages.currentPage().getParameters().get('id');
// Generate the PDF here
PageReference pdfPage = Page.QuoteOppProductVfp;
pdfPage.getParameters().put('Id', recordId);
pdfPage.setRedirect(true);
Blob pdfBlob;
if (Test.isRunningTest()) {
pdfBlob = Blob.valueOf('Test PDF Content');
} else {
pdfBlob = pdfPage.getContentAsPDF();
}
Attachment attach = new Attachment(parentId = recordId, Name = 'Quote' + String.valueOf(recordId), body = pdfBlob);
attach.IsPrivate = false;
insert attach;
// Create a ContentVersion record
ContentVersion cv = new ContentVersion(
Title = 'Quote' + String.valueOf(recordId),
VersionData = pdfBlob
);
insert cv;
// Create a ContentDocumentLink to associate the PDF with the record
ContentDocumentLink cdl = new ContentDocumentLink(
ContentDocumentId = cv.ContentDocumentId,
LinkedEntityId = recordId
);
insert cdl;
// Redirect or display a success message
PageReference successPage = new PageReference('/' + recordId);
successPage.setRedirect(true);
return successPage;
}
}
切成2页,更容易维护。我还将使用 2 个独立的 apex 控制器,但也可能只使用一个。
首先制作你的PDF。简单,单一的关注点,让它变得漂亮即可。
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>
一旦您对此感到满意 - 制作在 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>