Study guide
6.4 Processing-Only Reports & Report Substitutions
Microsoft Dynamics 365 Business Central Developer MB-820
Track your progress as you read
Finished sections get a check mark.
1 Chapter 1: Business Central Architecture & Platform Foundation
1.1 Business Central Architecture & Extensions Model 1.2 Customization Lifecycle: Cloud (SaaS) vs. On-Premises 1.3 Base Application, System Application & AppSource
2 Chapter 2: Development Environment & Configuration
2.1: Development Environment Setup: VS Code, AL: Go! Projects & Sandboxes 2.2: AL Configuration Files: app.json & launch.json Parameters 2.3: Multi-Root Workspaces & Project Dependencies
3 Chapter 3: Extension Lifecycle: Debugging, Deployment, Multilanguage & Maintenance
3.1 Debugging & Snapshot Debugging in Visual Studio Code 3.2 Extension Deployment & Schema Synchronization Modes 3.3 Multilanguage Development with XLIFF & Translation Files 3.4 Extension Maintenance, Deprecation & Breaking Changes
4 Chapter 4: Tables, Table Extensions & Data Modeling
4.1: Building Tables, Fields, Keys & Data Types 4.2: Table Extensions & Table Modifications 4.3: FlowFields, FlowFilters & Table Relations 4.4: Enums, Enum Extensions & Extensible Types
5 Chapter 5: Pages, Page Extensions & Role Centers
5.1: Page Types, Anatomy & Control Structure 5.2: Building Card, List & Document Pages 5.3: Page Extensions & Page Customizations 5.4: Role Centers, Headlines & Cue Data Modeling
6 Chapter 6: Reports & Document Generation
6.1 Report Architecture, DataItems & Dataset Design 6.2 Report Layouts: RDLC, Word, Excel & Multilanguage 6.3 Document Reports & Request Page Programming 6.4 Processing-Only Reports & Report Substitutions
7 Chapter 7: XMLports & Data Exchange
7.1 XMLport Architecture, Formats & Properties 7.2 XMLport Node Hierarchy, Triggers & Stream Manipulation
8 Chapter 8: Codeunits, Events, Triggers & Interfaces
8.1 Codeunit Types, Execution & SingleInstance Codeunits 8.2 Events, Publishers, Subscribers & Event Isolation 8.3 Interfaces, Polymorphism & Dynamic Codeunit Invocation 8.4 Installation & Upgrade Codeunits, Data Transfer & Archive
9 Chapter 9: Entitlements, Permissions & Queries
9.1 Permission Sets & Permission Set Extensions 9.2 Inherent Permissions, Inherent Entitlements & Security Scoping 9.3 Query Objects: Joins, Aggregation & Query as Report Data Source
10 Chapter 10: UI Customization, User Assistance & Onboarding
10.1 Profile Objects & Custom Page Views in AL 10.2 User Assistance: ToolTips, Teaching Tips & In-App Tours 10.3 Assisted Setup, Guided Experience & Onboarding Checklists
11 Chapter 11: Business Central Design Patterns & Data Process Model
11.1 Functional Table Types: Master Data, Supplemental & Setup Patterns 11.2 Document Standards: Header, Line, History & Journal Architectures 11.3 Data Process Model & Posting Routine Architecture
12 Chapter 12: AL Language Fundamentals, Data Types & Variables
12.1 AL Syntax, Variable Scope, Primitive & Complex Data Types 12.2 Expressions, Control Flow Statements & Built-in Functions 12.3 Access Modifiers (Public, Internal, Local) & Code Scoping
13 Chapter 13: Data Manipulation, Streams & Error Handling
13.1 Record Operations: Get, FindSet, ModifyAll & Filter Functions 13.2 Streams, File Management & TempBlob 13.3 Error Handling: ErrorInfo, TryFunctions & Collectible Errors
14 Chapter 14: Testing, Test Toolkit & Automation
14.1 Testing Architecture & the Test Toolkit 14.2 Authoring Test Codeunits & UI Handler Functions
15 Chapter 15: Telemetry, Performance Optimization & Analyzers
15.1 Telemetry Configuration & Azure Application Insights 15.2 Custom Telemetry Signals with Session.LogMessage & KQL 15.3 Performance Profiler, SetLoadFields & Query Optimization 15.4 AL Analyzers: CodeCop, AppSourceCop, UICop & PTECop
16 Chapter 16: REST Web Services & JSON in AL
16.1 HttpClient, HttpRequestMessage & REST API Consumption 16.2 JSON Processing: JsonObject, JsonArray, JsonValue & JsonToken
17 Chapter 17: API Pages, OData, Read Scale-Out & OAuth
17.1 API Pages (PageType=API), OData Web Services & REST Endpoints 17.2 Bound and Unbound OData Actions with [ServiceEnabled] 17.3 Read Scale-Out Architecture & DataAccessIntent Configuration 17.4 Microsoft Entra ID App Registrations & OAuth 2.0 Integration
6.4 Processing-Only Reports & Report Substitutions
Key Takeaways Explain This Topic AI
Setting ProcessingOnly = true creates a report dedicated exclusively to batch data modifications, calculations, and scheduled tasks, completely bypassing layout compilation, document rendering, and print spooling.
The report execution trigger lifecycle follows a deterministic order: OnInitReport -> requestpage triggers -> OnPreReport -> OnPreDataItem -> OnAfterGetRecord (per record) -> OnPostDataItem -> OnPostReport.
Flow control functions CurrReport.Skip(), CurrReport.Break(), and CurrReport.Quit() control iteration execution across individual records, dataitems, and the entire report.
Interactive batch processing reports utilize progress dialogs (Dialog.Open, Dialog.Update) that must strictly be guarded with if GuiAllowed() then to prevent fatal runtime exceptions in headless Job Queue sessions.
The report substitution pattern uses the ReportManagement.OnAfterSubstituteReport event subscriber to redirect standard report invocations to custom report objects without modifying base application code.
Last updated: August 2026
6.4 Processing-Only Reports & Report Substitutions
Not all reports in Business Central generate visual output. Processing-Only reports are specialized AL report objects used for high-performance batch data processing, automated database maintenance, and scheduled transactional routines. Furthermore, when partners need to replace standard Microsoft document reports with custom implementations, Business Central provides an event-driven Report Substitution mechanism that redirects report calls without modifying base code.
1. Processing-Only Reports: Batch Operations
When a report is designed solely to modify records, calculate ledger entries, or perform periodic batch updates, the ProcessingOnly property is set to true . report 50110 "Batch Adjust Item Costs"
- {
- Caption = 'Batch Adjust Item Costs';
- ProcessingOnly = true;
- UseRequestPage = true;
- UsageCategory = Tasks;
- ApplicationArea = Basic, Suite;
- dataset
- {
- dataitem(Item; Item)
- {
- DataItemTableView = sorting("No.") where(Blocked = const(false));
- RequestFilterFields = "No.", "Item Category Code";
- trigger OnPreDataItem()
- begin
- TotalRecords := Item.Count();
- CurrentRecord := 0;
- if GuiAllowed() then
- ProgressDialog.Open('Adjusting item costs...\Progress: @1@@@@@@@@@@@@@@@@@\Current Item: #2###############');
- end;
- trigger OnAfterGetRecord()
- begin
- CurrentRecord += 1;
- if GuiAllowed() then begin
- ProgressDialog.Update(1, Round(CurrentRecord / TotalRecords * 10000, 1));
- ProgressDialog.Update(2, Item."No.");
- end;
- Item.Validate("Unit Cost", Item."Unit Cost" * CostFactor);
- Item.Modify(true);
- end;
- trigger OnPostDataItem()
- begin
- if GuiAllowed() then
- ProgressDialog.Close();
- end;
- }
- }
- var
- ProgressDialog: Dialog;
- CostFactor: Decimal;
- TotalRecords: Integer;
- CurrentRecord: Integer;
- }
- Architectural Characteristics of Processing-Only Reports
No Layout Requirement : The compiler does not generate or validate RDLC, Word, or Excel layouts. Attempting to define a rendering block or layout file on a processing-only report results in a compilation error.
UseRequestPage : When set to true , the user can configure filters and options before processing. When set to false , the report executes immediately upon invocation—ideal for automated background jobs.
Transactional Integrity : All record modifications executed inside OnAfterGetRecord participate in the active database transaction. If an unhandled error occurs, the entire batch rolls back unless explicit Commit() calls are made.
2. Progress Dialogs & Headless Safety
In interactive desktop sessions, batch processes should provide visual feedback using the AL Dialog data type.
Dialog String Formatting
#1###### : Represents a text or value placeholder aligned left.
@1@@@@@@ : Represents a graphical percentage progress bar where the updated value must be an integer between 0 and 10000 (representing 0.00% to 100.00%).
\ : Line break character inside dialog strings.
Exam Watchout — The GuiAllowed() Rule: Business Central frequently executes batch reports in background sessions, Job Queues, or API web service calls. Invoking Dialog.Open() or Message() in a non-interactive session throws a fatal runtime exception. Developers must always guard dialog operations with if GuiAllowed() then .
Loading diagram... Complete Report & DataItem Trigger Execution Lifecycle
3. Report Trigger Lifecycle & Flow Control
Understanding the exact sequence of trigger execution is crucial for initializing variables, applying dynamic filters, and managing state across report dataitems.
Complete Report Execution Sequence
OnInitReport : Runs once when the report is loaded into memory. Used to set default variable values before the request page opens.
requestpage Triggers : OnInit -> OnOpenPage -> (User interactions) -> OnQueryClosePage .
OnPreReport : Runs after the request page closes but before any data is fetched. Used to verify global preconditions, check licensing, or initialize shared data buffers.
OnPreDataItem : Runs once per DataItem before the table cursor is opened. Used to set dynamic filters using AL code ( SetRange , SetFilter ) or apply sorting keys.
OnAfterGetRecord : Runs for every individual record fetched from the DataItem table. Used to perform calculations, evaluate custom business logic, and manipulate export variables.
OnPostDataItem : Runs once per DataItem after all its records have been iterated.
OnPostReport : Runs once after all DataItems have finished processing. Used for cleanup, logging telemetry signals, or triggering outbound email notifications.
Report Flow Control Statements
AL provides three specialized methods on CurrReport to alter loop execution dynamically:
Flow Control Method Behavioral Scope
CurrReport.Skip() Skips the current record in the active DataItem. The report engine abandons the rest of OnAfterGetRecord , does not emit a row to the dataset for this record, and proceeds immediately to the next record.
CurrReport.Break() Immediately terminates iteration of the active DataItem . The report skips all remaining records in this DataItem, executes OnPostDataItem , and proceeds to the next sibling or parent DataItem.
CurrReport.Quit() Immediately terminates execution of the entire report . No further DataItems are processed, OnPostReport is bypassed, and control returns to the caller.
4. Report Substitution Architecture
In standard Business Central implementations, end users trigger standard document reports (such as Report 1306 Standard Sales - Invoice ) from dozens of posted document pages, list actions, and automated posting routines. Modifying every base page action to point to a custom report is impossible in modern extensions.
To solve this, Business Central provides the Report Substitution Pattern via Codeunit 44 ReportManagement . +-----------------------------------------------------------------------+
- | REPORT SUBSTITUTION PATTERN |
- | |
- | User clicks "Print" on Posted Sales Invoice (Calls Report 1306) |
- | │ |
- | v |
- | Codeunit 44 "ReportManagement" invokes OnAfterSubstituteReport |
- | │ |
- | v |
- | Custom Subscriber intercepts: |
- | if ReportId = Report::"Standard Sales - Invoice" then |
- | NewReportId := Report::"Contoso Custom Sales Invoice"; |
- | │ |
- | v |
- | Business Central executes Report 50100 instead of Report 1306 |
- +-----------------------------------------------------------------------+
- Implementing Report Substitution in AL
Developers subscribe to the OnAfterSubstituteReport event published by Codeunit ReportManagement : codeunit 50102 "Report Substitution Handler"
{
[EventSubscriber(ObjectType::Codeunit, Codeunit::ReportManagement, 'OnAfterSubstituteReport', '', false, false)]
- local procedure OnAfterSubstituteReport(ReportId: Integer; var NewReportId: Integer)
- begin
- if ReportId = Report::"Standard Sales - Invoice" then
- NewReportId := Report::"Contoso Custom Invoice";
- if ReportId = Report::"Purchase Order" then
- NewReportId := Report::"Contoso Purchase Order";
- end;
- }
When any codeunit, page action, or user clicks a button calling Report.Run(Report::"Standard Sales - Invoice", ...) , the platform raises OnAfterSubstituteReport .
The subscriber inspects ReportId . If it matches the target base report, it reassigns NewReportId to the custom report object ID.
The platform seamlessly executes the custom report in place of the base report across all standard system entry points.
Report Extensions ( reportextension ) vs. Report Substitution
Customization Goal Recommended Approach
Adding 2-3 custom fields and a modified RDLC/Excel layout to a standard invoice reportextension Object : Keeps the base report architecture intact while adding columns, dataitems, and layouts additively.
Completely replacing the entire invoice calculation engine, layout hierarchy, or dataset structure Report Substitution Pattern : Completely decouples from the standard report by executing an independent custom report object via Codeunit 44.