2

Let's say that I have created a report using the Acumatica Report Designer and it is linked to the relevant DAC.

I also have a custom screen with an Action. When the user executes this action, I want to generate the report so that it the user downloads it as pdf.

What is the approach to generate the report as PDF programmatically through the Acumatica API?

RuslanDev
  • 6,718
  • 1
  • 14
  • 27
Joseph Caruana
  • 2,241
  • 3
  • 31
  • 48

3 Answers3

2

Please refer to code snippet below for an example showing how to generate report programmatically as PDF file and show SaveFileDialog to download/save generated PDF:

public PXAction<IOITInboundTestWorkOrder> GenerateReportAndRedirectToFile;
[PXButton]
[PXUIField(DisplayName = "Generate Report and Download as PDF")]
protected void generateReportAndSaveToDB()
{
    Actions.PressSave();
    PXLongOperation.StartOperation(this, () =>
    {
        PX.SM.FileInfo file = null;
        using (Report report = PXReportTools.LoadReport("SO641012", null))
        {
            var orderNbr = ITWO.Current.OrderNbr;
            if (report == null) throw new Exception("Unable to access Acumatica report writter for specified report : " + "SO641012");
            Dictionary<string, string> prams = new Dictionary<string, string>();
            prams["ITWONbr"] = orderNbr;
            PXReportTools.InitReportParameters(report, prams, PXSettingProvider.Instance.Default);
            ReportNode repNode = ReportProcessor.ProcessReport(report);
            IRenderFilter renderFilter = ReportProcessor.GetRenderer(ReportProcessor.FilterPdf);

            using (StreamManager streamMgr = new StreamManager())
            {
                renderFilter.Render(repNode, null, streamMgr);
                string fileName = string.Format("Inbound Test Work Order #{0}.pdf", orderNbr);
                file = new PX.SM.FileInfo(fileName, null, streamMgr.MainStream.GetBytes());
            }
        }
        if (file != null)
        {
            throw new PXRedirectToFileException(file, true);
        }
    });
}
RuslanDev
  • 6,718
  • 1
  • 14
  • 27
1

If you don't want to deal with exposing the URL, the accepted method appears to be to invoke a method to store the file to the Acumatica record and then retrieve the file via the API: Get report output in PDF fromat via Acumatica REST API

June B
  • 141
  • 10
  • 1
    Please upvote the idea to make report generation functionality available through the API without saving a file to Acumatica! https://feedback.acumatica.com/ideas/ACU-I-1852 – June B Dec 04 '18 at 22:27
0

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.

micwallace
  • 480
  • 5
  • 13