Study guide
6.3 Document Reports & Request Page Programming
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.3 Document Reports & Request Page Programming
Key Takeaways Explain This Topic AI
Professional Business Central document reports utilize the Integer dataitem loop pattern (CopyLoop) to render the original document and multiple numbered copies in a single print job.
PageLoop integer dataitems isolate line item pagination, running balances, and transport headers/footers per document copy.
The requestpage block defines user options, date ranges, and output filters, allowing users to customize execution parameters prior to dataset generation.
Setting SaveValues = true on the request page persists user selections across sessions on a per-user, per-report basis in the tenant database.
The OnQueryClosePage trigger on request pages validates user parameters before processing begins, returning false or throwing an error to prevent report execution if inputs are invalid.
Last updated: August 2026
6.3 Document Reports & Request Page Programming
Commercial ERP document reports—such as Sales Invoices, Purchase Orders, and Order Confirmations—require complex operational capabilities that extend far beyond simple tabular data dumps. In enterprise environments, document reports must reliably handle multi-copy generation with dynamic watermarks ("ORIGINAL", "COPY 1", "COPY 2"), per-copy page numbering resets, transport running balances across multi-page tables, and interactive request page parameters. For developers preparing for the MB-820 certification exam, mastering the structural patterns and lifecycle triggers of document reporting is a crucial requirement.
1. The Professional Document Report Pattern ( CopyLoop & PageLoop )
In standard Business Central document reporting, invoices, shipments, and orders must frequently be printed in duplicate or triplicate for logistics, accounting, and customer archiving. Rather than requiring users to manually run the report multiple times, the report architecture utilizes nested Integer DataItem loops (conventionally named CopyLoop and PageLoop ). report 50120 "Standard Sales Order Conf."
- {
- UsageCategory = Documents;
- ApplicationArea = Basic, Suite;
- DefaultRenderingLayout = RDLC_Layout;
- dataset
- {
- dataitem(Header; "Sales Header")
- {
- DataItemTableView = sorting("Document Type", "No.") where("Document Type" = const(Order));
- RequestFilterFields = "No.", "Sell-to Customer No.", "Posting Date";
- dataitem(CopyLoop; Integer)
- {
- DataItemTableView = sorting(Number);
- column(CopyNo; Number) { }
- column(CopyText; CopyText) { }
- column(OutputNo; OutputNo) { }
- dataitem(PageLoop; Integer)
- {
- DataItemTableView = sorting(Number);
- column(CompanyInfoName; CompanyInfo.Name) { }
- column(CompanyInfoAddress; CompanyInfo.Address) { }
- column(CompanyInfoVATRegNo; CompanyInfo."VAT Registration No.") { }
- column(DocumentTitle; DocumentTitle) { }
- column(BillToCustomerNo; Header."Bill-to Customer No.") { IncludeCaption = true; }
- column(BillToCustName; Header."Bill-to Name") { }
- dataitem(Line; "Sales Line")
- {
- DataItemLink = "Document Type" = field("Document Type"),
- "Document No." = field("No.");
- DataItemLinkReference = Header;
- DataItemTableView = sorting("Document Type", "Document No.", "Line No.");
- column(LineNo; "Line No.") { }
- column(ItemNo; "No.") { IncludeCaption = true; }
- column(Description; Description) { IncludeCaption = true; }
- column(Quantity; Quantity) { IncludeCaption = true; }
- column(UnitPrice; "Unit Price") { IncludeCaption = true; }
- column(LineAmount; "Line Amount") { IncludeCaption = true; }
- }
- dataitem(VATCounter; Integer)
- {
- DataItemTableView = sorting(Number);
- column(VATBase; TempVATAmountLine."VAT Base") { AutoFormatType = 1; }
- column(VATAmount; TempVATAmountLine."VAT Amount") { AutoFormatType = 1; }
- column(VATIdentifier; TempVATAmountLine."VAT Identifier") { }
- trigger OnPreDataItem()
- begin
- VATCounter.SetRange(Number, 1, TempVATAmountLine.Count());
- end;
- trigger OnAfterGetRecord()
- begin
- TempVATAmountLine.GetLine(Number);
- end;
- }
- trigger OnPreDataItem()
- begin
- PageLoop.SetRange(Number, 1, 1);
- end;
- }
- trigger OnPreDataItem()
- begin
- NoOfLoops := 1 + Abs(NoOfCopies);
- CopyLoop.SetRange(Number, 1, NoOfLoops);
- OutputNo := 1;
- end;
- trigger OnAfterGetRecord()
- begin
- if Number > 1 then begin
- CopyText := FormatDocument.GetCopyText();
- OutputNo += 1;
- end else
- CopyText := '';
- end;
- }
- }
- }
- requestpage
- {
- // Request page implementation
- }
- var
- CompanyInfo: Record "Company Information";
- TempVATAmountLine: Record "VAT Amount Line" temporary;
- FormatDocument: Codeunit "Format Document";
- NoOfCopies: Integer;
- NoOfLoops: Integer;
- OutputNo: Integer;
- CopyText: Text[30];
- DocumentTitle: Text[50];
- }
- Breakdown of the Multi-Loop Hierarchy
DataItem Level DataItem Name & Type Architectural Purpose & Execution Mechanics
Level 1 (Root) Header ( Sales Header ) Queries and iterates through the selected sales orders based on table filters and request page criteria.
Level 2 (Copy) CopyLoop ( Integer ) Evaluates NoOfCopies in OnPreDataItem and sets its range from 1 to (1 + NoOfCopies) . Iteration 1 represents the "ORIGINAL" document; iterations 2+ represent duplicate copies with CopyText watermarks.
Level 3 (Page) PageLoop ( Integer ) Filtered strictly to SetRange(Number, 1, 1) to execute once per copy. Acts as the primary grouping anchor for page headers, customer billing information, and subtotal resets in the layout.
Level 4 (Lines) Line ( Sales Line ) Linked directly to Header via DataItemLinkReference = Header and DataItemLink . Emits line item details for every copy loop iteration.
Level 4 (Summary) VATCounter ( Integer ) Iterates through the in-memory TempVATAmountLine temporary table buffer to render tax summary grids after all document lines have processed.
Loading diagram... Document Report DataItem Hierarchy & Iteration Execution Tree
2. Pagination, Page Breaks & Transport Balances
In commercial document printing, long sales orders and invoices regularly span multiple physical pages. Adhering to international accounting and tax standards requires precise control over page breaks and carried-forward totals.
Transport Headers & Transport Footers (Carried-Forward Balances)
When line items overflow onto a second or third page:
Transport Footer (Page Bottom) : Displays a running subtotal of all lines printed up to the bottom of the current page, labeled as "Carried Forward: $X,XXX.XX" .
Transport Header (Page Top) : Displays the identical carried-forward balance at the top of the next page, labeled as "Brought Forward: $X,XXX.XX" .
Implementation : In AL and RDLC, developers track cumulative line amounts in AL variables or RDLC running total expressions ( =RunningValue(Fields!LineAmount.Value, Sum, "PageLoopGroup") ), toggling visibility based on page position.
RDLC Grouping and Page Breaks
To guarantee that multiple documents (or copies of the same document) print cleanly without page number bleeding:
Group Break Placement : On the Tablix group bound to Header and CopyLoop , developers configure PageBreak.BreakLocation = Between (or After ).
Page Number Reset : Setting PageBreak.ResetPageNumber = True ensures each invoice and each copy begins on Page 1, resetting the built-in Globals!PageNumber counter.
KeepTogether Property : Applying KeepTogether = True to individual Tablix rows or entire summary sections (e.g., VAT Breakdown or Total Summary grids) prevents the rendering engine from splitting a single summary block across physical page breaks.
3. Report Request Page Architecture & UI Design
The requestpage block allows developers to capture user options, specify runtime filters, and validate parameters prior to dataset generation. requestpage
- {
- SaveValues = true;
- SaveLayout = false;
- layout
- {
- area(Content)
- {
- group(Options)
- {
- Caption = 'Options';
- field(NoOfCopiesField; NoOfCopies)
- {
- ApplicationArea = All;
- Caption = 'No. of Copies';
- ToolTip = 'Specifies the number of copies to print in addition to the original document.';
- MinValue = 0;
- MaxValue = 10;
- }
- field(ShowInternalInfoField; ShowInternalInfo)
- {
- ApplicationArea = All;
- Caption = 'Show Internal Information';
- ToolTip = 'Specifies if internal notes and hidden work lines should be included.';
- }
- field(LogInteractionField; LogInteraction)
- {
- ApplicationArea = All;
- Caption = 'Log Interaction';
- ToolTip = 'Specifies if an interaction entry should be created for this customer.';
- }
- field(DisplayInLCYField; DisplayInLCY)
- {
- ApplicationArea = All;
- Caption = 'Show Amounts in LCY';
- ToolTip = 'Specifies if foreign currency amounts should be converted to local currency.';
- }
- }
- }
- }
- actions
- {
- area(Processing)
- {
- action(ResetDefaults)
- {
- ApplicationArea = All;
- Caption = 'Reset to Defaults';
- Image = ResetStatus;
- trigger OnAction()
- begin
- NoOfCopies := 0;
- ShowInternalInfo := false;
- LogInteraction := true;
- DisplayInLCY := false;
- end;
- }
- }
- }
- trigger OnOpenPage()
- begin
- if not SaveValuesLoaded then
- InitDefaults();
- end;
- trigger OnQueryClosePage(CloseAction: Action): Boolean
- begin
- if CloseAction in [Action::OK, Action::LookupOK, Action::Preview] then begin
- if (StartDate <> 0D) and (EndDate <> 0D) and (EndDate < StartDate) then
- Error('End Date (%1) cannot be earlier than Start Date (%2).', EndDate, StartDate);
- end;
- exit(true);
- end;
- }
- Request Page Lifecycle and Property Mechanics
SaveValues = true : When enabled, Business Central stores the user-entered values of request page controls and dataitem filters in the tenant database per user ID and object ID. When that same user runs the report again, the previous settings are preloaded automatically.
OnOpenPage : Trigger executed when the request page is instantiated in the client. Used to initialize default filter dates, lookup values, and dynamic control visibility.
OnQueryClosePage(CloseAction: Action): Boolean : Trigger executed when the user clicks Print , Preview , Schedule , or OK . It receives the CloseAction parameter. If validation logic finds invalid inputs (e.g., an illegal date range or missing required parameter), raising an Error() or returning false halts closure and prevents report dataset processing.
Programmatic Invocation and Parameter Passing
Reports can also be executed programmatically in AL without displaying the request page UI: // Execute report directly using stored filters or default parameters
Report.Run(Report::"Standard Sales - Invoice", false, false, SalesHeaderRec);
- // Run request page programmatically to capture parameter XML
- var
- ParametersXml: Text;
- begin
- ParametersXml := Report.RunRequestPage(Report::"Sales Order Summary");
- Report.Execute(Report::"Sales Order Summary", ParametersXml);
- end;