I had trouble with the accepted answer since I am using the REST API and for some reason the PXRedirectToFileException does work there (it doesn't return a location header). I came up with this really clunky solution that casues the file URL to be exposed in an exception.
using SiteStatus = PX.Objects.IN.Overrides.INDocumentRelease.SiteStatus;
using System.Linq;
using PX.Common;
using CRLocation = PX.Objects.CR.Standalone.Location;
using PX.Objects.AR.CCPaymentProcessing;
using PX.Objects.AR.CCPaymentProcessing.Common;
using PX.Objects.AR.CCPaymentProcessing.Helpers;
using PX.Objects.Common;
using PX.Objects;
using PX.Objects.SO;
using PX.Reports;
using PX.Reports.Data;
using PX.Data.Reports;
using PX.SM;
namespace PX.Objects.SO
{
public class SOInvoiceEntry_Extension:PXGraphExtension<SOInvoiceEntry>
{
#region Event Handlers
protected virtual void ARInvoice_RowSelected(PXCache cache, PXRowSelectedEventArgs e){
CreateInvoicePDF.SetEnabled(true);
}
public PXAction<ARInvoice> CreateInvoicePDF;
[PXButton]
[PXUIField(DisplayName = "Create Invoice PDF", Enabled = true, Visible = false)]
public virtual void createInvoicePDF()
{
//Report Paramenters
Dictionary<String, String> parameters = new Dictionary<String, String>();
parameters["DocType"] = Base.Document.Current.DocType;
parameters["RefNbr"] = Base.Document.Current.RefNbr;
//Report Processing
PX.Reports.Controls.Report _report = PXReportTools.LoadReport("SO643000",null);
PXReportTools.InitReportParameters(_report, parameters, SettingsProvider.Instance.Default);
ReportNode reportNode = ReportProcessor.ProcessReport(_report);
// Generate PDF
byte[] data = PX.Reports.Mail.Message.GenerateReport(reportNode, ReportProcessor.FilterPdf).First();
FileInfo file = new FileInfo(Guid.NewGuid(), "Invoice" + Base.Document.Current.RefNbr + ".pdf", null, data);
// Store data in session
PXContext.SessionTyped<PXSessionStatePXData>().FileInfo[file.UID.ToString()] = file;
// Include file URL in exception. The client will parse the filname and fetch the URL in a subsequent request.
PXRedirectToFileException e = new PXRedirectToFileException(file.UID, 0, true, true);
string url = e.Url;
throw new FileUrlException(url);
}
#endregion
}
class FileUrlException : PXException {
public FileUrlException(string message) : base(message) {
}
}
}
The downside is that this action can only be used via the API.
I added the action to the web services endpoint. On the other end I used regex to extract the string out of the exception message and performed a get request to fetch the file.