In the previous article, we explained a utility class that helps you generate SSRS and Docentric reports from X++ in memory instead of distributing them to a print destination such as Email or File. Using the same helper class, DocSrsReportGenerator, we will now demonstrate how to send Sales and Project invoice in the single email to the corresponding customer.
We will cover two scenarios:
- Send Sales and Project invoice as separate attachments in the same email.
- Merge Sales and Project invoice and email them as the single attachment.
Extend DSP class for Sales invoice
Assume that we want to email both Sales and Project invoices when printing a Sales invoice. This means that we need to customize the Sales invoice printing pipeline, and we will do that by extending Docentric DSP class for Sales invoice.
Learn more about Docentric DSP classes >>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
class DocSalesInvoiceWithPSAProjInvoiceReportDSP extends DocSalesInvoiceReportDSP { public DocPlaceholderManager overrideReportRunSettings(DocReportRunContext _reportRunContext, boolean _replaceStandardPlaceholders = true) { DocPlaceholderManager placeholderMng = super(_reportRunContext, _replaceStandardPlaceholders); if (_reportRunContext.parmPrintDestination() == DocPrintDestination::Email) { // Generate the related PSAProjInvoice. container psaProjInvoiceReport = this.printPSAProjInvoiceReportToContainer(_reportRunContext); // Comment this line if you don’t want to attach PSAProjInvoice as a separate email attachment. _reportRunContext.emailPrintDestSettings().addAdditionalAttachment('PSAProjInvoice.pdf', psaProjInvoiceReport); // Comment this line, if you don’t want to merge 2 invoices. // To merge it with the main report, we have to wait for its generation, // which will happen later in the pipeline. _reportRunContext.parmCustomSettings(psaProjInvoiceReport); } return placeholderMng; } public container printPSAProjInvoiceReportToContainer(DocReportRunContext _reportRunContext) { // Use here the Sales Invoice data to fetch the corresponding project invoice. ProjInvoiceJour projInvoiceJour; select firstonly projInvoiceJour; Args args = new Args(); args.record(projInvoiceJour); args.parmEnumType(enumNum(PrintCopyOriginal)); args.parmEnum(PrintCopyOriginal::Original); PSAProjAndContractInvoiceController controller = new PSAProjAndContractInvoiceController(); controller.parmArgs(args); controller.parmReportName( PrintMgmtDocType::construct(PrintMgmtDocumentType::ProjectInvoice).getDefaultReportFormat()); DocSrsReportGenerator reportGenerator = new DocSrsReportGenerator(controller); // NOTE: If you want to print PSAProjInvoice using SSRS Report design, uncomment the following line. //reportGenerator.setPrintDestinationSettings_SsrsReport(SRSReportFileFormat::PDF); // Set settings for generation of the Docentric report using the default Docentric template or provide a specific one. reportGenerator.setPrintDestinationSettings_DocentricReport(DocOutputFileFormat::PDF); PSAProjPrintInvoice psaProjPrintInvoice = PSAProjPrintInvoice::construct(); psaProjPrintInvoice.updatePrinterSettingsPrintInvoice(controller.parmReportContract().parmPrintSettings().pack()); args.caller(psaProjPrintInvoice); // Call the initArgs() method via reflection, because it's a protected method. new DictClass(classNum(PSAProjAndContractInvoiceController)). callObject(methodStr(PSAProjAndContractInvoiceController, initArgs), controller, args); container generatedInvoice = reportGenerator.generateReport(); return generatedInvoice; } } |
Once we build the project/model containing the DocSalesInvoiceWithPSAProjInvoiceReportDSP class, it will become available in Docentric report setup, where we have to select it as DSP class for the Sales invoice report.
Merge Sales and Project invoice
If you don’t want to merge Sales and Project invoice, you’re done! However, in order to merge them, you will need to have the following event handler implemented as well.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
[SubscribesTo(classStr(DocReportRunDelegates), delegateStr(DocReportRunDelegates, generateReportContent))] public static void DocReportRunDelegates_generateReportContent(DocPrintReportSettings _printReportSettings, DocPrintedReport _printedReport, DocEventHandlerResult _result) { // If this is not the SalesInvoice report, return. if (_printReportSettings.parmReportId() != ssrsReportStr(SalesInvoice, Report)) return; // If report output format is not PDF, return. if (_printedReport.parmIsDocentricReport()) { if (_printedReport.parmDocentricOutputFileFormat() != DocOutputFileFormat::PDF) return; } else { if (_printedReport.parmSrsOutputFileFormat() != SRSReportFileFormat::PDF) return; } System.Byte[] mainReportSalesInvoice = _printedReport.getReportContentByteArray(); System.Byte[] additionalReportPSAProjInvoice = DocGlobalHelper::convertContainerToBytes(_printReportSettings.parmCustomSettings()); // Merge Sales Invoice and PSA Proj Invoice. if (additionalReportPSAProjInvoice != null) { List mergingReportsList = new List(Types::AnyType); mergingReportsList.addStart(mainReportSalesInvoice); mergingReportsList.addEnd(additionalReportPSAProjInvoice); // Create the merged PDF file from the document contents saved in the list using (System.IO.MemoryStream mergedPDFStream = DocDocumentHelper::mergePdfDocuments(mergingReportsList)) { // Uncomment the next line to download the resulting PDF file to the browser //DocFileMngHelper::sendFileToUser(mergedPDFStream, 'SalesInvoiceWithProjectInvoice.pdf'); _printedReport.setReportContentMemoryStream(mergedPDFStream); } } } |
The above method handles the event published each time a report content is generated, so we first have to check if this is the SalesInvoice report. Next, we will merge the generated report (SalesInvoice) with the one we generated in the DocSalesInvoiceWithPSAProjInvoiceReportDSP class (PSAProjInvoice).
Alternatively, if we didn’t need to send PSAProjInvoice as the additional email attachment, we wouldn’t need the custom DSP class at all, but this event handler would suffice. In this case, it should also contain logic for generating the related Project invoice before merging it with Sales invoice.
In general, the DocReportRunDelegates.generateReportContent event handler enables merging of output documents for any report with other PDF documents, for example T&Cs or specifications, which can be stored in Attachments of the related journal, account or source table records.
The result
When we run Sales invoice from Invoice journal, the result is the following.
As we can see, Docentric Free Edition offers flexible code injection points, which we can use to achieve various custom scenarios such as generating another report and adding it as the additional email attachment, or merging it with the main report document before emailing, saving to SharePoint or embedding into e-Invoice.