Security change on December 1, 2026: TLS 1.2 will become the minimum standard for 365 business Print Agent. Details and system requirements
365 business development
365 business PDF

Extensibility Events

365 business PDF hooks into the standard report processing of Business Central: whenever a report that is listed in Report Selections has been rendered to PDF, the app takes the document, applies the configured actions to it, and hands it back.

The events on this page let your extension take part in that. They are published by two codeunits:

CodeunitEventRaised
bdev.PDF (5523675)OnBeforePerformAdditionalActionsOnPdfBefore the app does anything to the rendered document.
bdev.Doc. Attachment Impl. (5523596)OnFilterDocumentAttachmentsForSourceWhile document attachments are collected, for source tables the app does not handle itself.
bdev.Doc. Attachment Impl. (5523596)OnAttachDocumentsForSourceThe same moment. Obsolete since 20.6 - it cannot be answered.

OnBeforePerformAdditionalActionsOnPdf - Event

The OnBeforePerformAdditionalActionsOnPdf event is raised after the report has been rendered and the print context has been determined, and before stationery, concatenation, attachments, signing and ZUGFeRD conversion are applied. Use this event to act on the document yourself, to choose a different report selection, or to stop the processing of the app entirely.

Code
/// <summary> /// Event publisher to overwrite report selection or skip further processing. /// </summary> /// <param name="document">Base document to perform additional actions on.</param> /// <param name="reportSelections">Report selection, containing settings for the report.</param> /// <param name="baseRecord">Base record (e.g. Sales Invoice Header) for which the report is being processed.</param> /// <param name="languageCode">Language code of the report being processed.</param> /// <param name="documentPrintIntent">Print intent of the document being processed.</param> /// <param name="objectPayload">JSON object payload containing metadata about the document.</param> /// <param name="skipProcessing">Set to true to skip further processing of the PDF document.</param> [IntegrationEvent(false, false)] local procedure OnBeforePerformAdditionalActionsOnPdf(var document: Codeunit "Temp Blob"; var reportSelections: Record "Report Selections"; baseRecord: Variant; languageCode: Code[10]; documentPrintIntent: Enum "bdev.Pdf Document Print Intent"; objectPayload: JsonObject; var skipProcessing: Boolean) begin end;
ParameterTypeDescription
documentCodeunit "Temp Blob"The rendered PDF document. Changes are used for the processing that follows and end up in the document the user receives.
reportSelectionsRecord "Report Selections"The report selection the app resolved for the report. Its configuration code fields decide which actions run; changing them here changes what the app does.
baseRecordVariantA RecordRef on the record the report was printed for, positioned on the first record of the report filter. It is empty when the base record could not be resolved from the payload.
languageCodeCode[10]The language of the document. Set from the document on a single print, empty on a bulk print.
documentPrintIntentEnum "bdev.Pdf Document Print Intent"Whether the user printed, previewed, downloaded or saved the document.
objectPayloadJsonObjectThe payload Business Central passes to OnDocumentReady, carrying documenttype, intent and filterviews among others.
skipProcessingBooleanSet to true to stop the app from applying any of its actions.

Good to know

OnBeforePerformAdditionalActionsOnPdf has been introduced with 365 business PDF version 20.5, the objectPayload parameter shortly after it.


Please consider update 365 business PDF to get access to the event.

When the event is raised

The event is raised for every report that is listed in Report Selections and renders to PDF - not only for reports that have a 365 business PDF configuration attached. A subscriber therefore has to decide for itself whether the document is one it cares about.

It is not raised when:

  • the rendered document is not a PDF;
  • the report is not listed in Report Selections;
  • the report prints a sales invoice or credit memo and 365 business E-Invoice is installed with its export enabled - the e-document pipeline takes over in that case.

Skipping the processing

Setting skipProcessing to true stops everything the app would do to the document: stationery, concatenation, document attachments, signing, security and ZUGFeRD conversion. The document you leave in document is what is handed back to Business Central.

A document of unchanged size is discarded

The app only writes the document back when its size differs from the rendered one. A subscriber that changes the document without changing its length - overwriting a value in place, for example - leaves the user with the original report. If a change of that kind has to survive, make sure the document you produce differs in size, or write it through a path of your own.

Errors in a subscriber are swallowed

The whole processing, including this event, runs inside a [TryFunction]. An error a subscriber raises does not reach the user: it silently aborts the processing and the user receives the unprocessed report. Do not use Error to communicate with the user from a subscriber - send a Notification or write to the telemetry instead.

Example

The following example archives a copy of the rendered document in a table of its own before the app applies anything to it, and leaves the processing to the app:

Code
codeunit 50000 "My Pdf Subscriber" { [EventSubscriber(ObjectType::Codeunit, Codeunit::"bdev.PDF", 'OnBeforePerformAdditionalActionsOnPdf', '', true, true)] local procedure ArchiveDocumentOnBeforePerformAdditionalActionsOnPdf(var document: Codeunit "Temp Blob"; var reportSelections: Record "Report Selections"; baseRecord: Variant; languageCode: Code[10]; documentPrintIntent: Enum "bdev.Pdf Document Print Intent"; objectPayload: JsonObject; var skipProcessing: Boolean) var salesInvoiceHeader: Record "Sales Invoice Header"; recRef: RecordRef; begin if (documentPrintIntent <> Enum::"bdev.Pdf Document Print Intent"::Print) then exit; if (not baseRecord.IsRecordRef()) then exit; recRef := baseRecord; if (recRef.Number() <> Database::"Sales Invoice Header") then exit; recRef.SetTable(salesInvoiceHeader); ArchiveDocument(salesInvoiceHeader, document); end; }

The next example takes a report out of the hands of the app altogether, for example because another extension already applied a stationery of its own:

Code
[EventSubscriber(ObjectType::Codeunit, Codeunit::"bdev.PDF", 'OnBeforePerformAdditionalActionsOnPdf', '', true, true)] local procedure SkipOwnReportOnBeforePerformAdditionalActionsOnPdf(var document: Codeunit "Temp Blob"; var reportSelections: Record "Report Selections"; baseRecord: Variant; languageCode: Code[10]; documentPrintIntent: Enum "bdev.Pdf Document Print Intent"; objectPayload: JsonObject; var skipProcessing: Boolean) begin skipProcessing := (reportSelections."Report ID" = Report::"My Own Layouted Report"); end;

OnFilterDocumentAttachmentsForSource - Event

Document attachments let a user place further PDF documents before or after a printed document. The app resolves the attachments of Sales Header and Purchase Header itself. For every other source table it raises this event, so that an extension can state which bdev.Document Attachment records belong to the record being printed.

Code
/// <summary> /// Event that is raised for source tables the app does not resolve document attachments for itself. /// Set the filters that select the document attachments of the record being printed, and set /// isHandled to true to state that the attachments have been resolved. /// </summary> [IntegrationEvent(false, false)] local procedure OnFilterDocumentAttachmentsForSource(var docAttachments: Record "bdev.Document Attachment"; var document: Codeunit "Temp Blob"; reportSelections: Record "Report Selections"; var recRef: RecordRef; var isHandled: Boolean) begin end;
ParameterTypeDescription
docAttachmentsRecord "bdev.Document Attachment"The attachment record the app iterates afterwards. Set the filters that select the attachments of your record.
documentCodeunit "Temp Blob"The document being printed.
reportSelectionsRecord "Report Selections"The report selection of the report being printed.
recRefRecordRefThe record the report is printed for. Its table id is what makes the app raise this event rather than handle the source itself.
isHandledBooleanSet to true to state that the attachments of the record have been filtered. Left false, no attachments are added.

Version

OnFilterDocumentAttachmentsForSource has been introduced with 365 business PDF 20.6. It replaces OnAttachDocumentsForSource, whose isHandled is passed by value and can therefore never be answered.

The event is only raised on a single print; on a bulk print no attachments are added at all.

Example

Code
codeunit 50001 "My Doc Attachment Subscriber" { [EventSubscriber(ObjectType::Codeunit, Codeunit::"bdev.Doc. Attachment Impl.", 'OnFilterDocumentAttachmentsForSource', '', true, true)] local procedure FilterBrokerAttachmentsOnFilterDocumentAttachmentsForSource(var docAttachments: Record "bdev.Document Attachment"; var document: Codeunit "Temp Blob"; reportSelections: Record "Report Selections"; var recRef: RecordRef; var isHandled: Boolean) var broker: Record Broker; begin if (recRef.Number() <> Database::Broker) then exit; recRef.SetTable(broker); docAttachments.Reset(); docAttachments.SetRange("Source Type", Database::Broker); docAttachments.SetRange("Source No.", broker."No."); isHandled := true; end; }

The app reads the attachments in the order of the primary key, Position No. last. An entry of the type Document marks where the printed document itself belongs among them; without one, it is appended at the end.

OnAttachDocumentsForSource - Event (deprecated)

Deprecated

OnAttachDocumentsForSource is obsolete as of 365 business PDF version 20.6. Use OnFilterDocumentAttachmentsForSource instead, which can actually be answered.

Code
[Obsolete('This event is obsolete. Use the OnFilterDocumentAttachmentsForSource event instead, which allows to state that the document attachments have been filtered.', '20.6')] [IntegrationEvent(false, false)] local procedure OnAttachDocumentsForSource(var docAttachments: Record "bdev.Document Attachment"; var document: Codeunit "Temp Blob"; reportSelections: Record "Report Selections"; var recRef: RecordRef; isHandled: Boolean) begin end;

isHandled is declared without var: a subscriber receives a copy and cannot hand its value back to the publisher, so the app treats the source as unhandled whatever the subscriber does. Filtering docAttachments here has never had an effect.

The event is still raised, right before OnFilterDocumentAttachmentsForSource, so that existing subscribers keep running. It could not be repaired in place - adding var to a published event changes the signature every subscriber is compiled against, which is a breaking change for dependent extensions.

See also

Last modified on