6.1 Report Architecture, DataItems & Dataset Design
Key Takeaways
- A Business Central report dataset is constructed in AL using hierarchical dataitem declarations linked via DataItemLink and filtered via DataItemTableView or runtime request filters.
- The Navision Server Tier (NST) processes nested dataitems iteratively in an outer-to-inner loop, flattening hierarchical relational data into a single two-dimensional rectangular tabular dataset (DataSet_Result) for consumption by layout engines.
- PrintOnlyIfDetail = true suppresses parent records from the output dataset if no matching child records exist, whereas PrintOnlyIfDetail = false emits parent rows with NULL/blank child column values.
- RequestFilterFields automatically generates interactive filtering UI on the request page without declaring custom global variables or page controls, combining with DataItemTableView using logical AND.
- IncludeCaption = true on field column declarations automatically emits localized field captions as ColumnNameCaption into the dataset schema, avoiding manual label declarations.
6.1 Report Architecture, DataItems & Dataset Design
In Microsoft Dynamics 365 Business Central, reports serve two distinct enterprise functions: presenting formatted business documents (such as sales invoices, purchase orders, and picking lists) and executing batch data modifications without a visual layout. For developers preparing for the MB-820 certification exam, understanding the AL report data model—specifically how the Navision Server Tier (NST) constructs, filters, and flattens hierarchical datasets—is a foundational competency in AL development.
1. Report Object Anatomy in AL
A report in AL is defined using the report object keyword. It encapsulates top-level report properties, a single dataset block, an optional requestpage block, an optional rendering block (or legacy layout properties), and report-level triggers.
report 50100 "Sales Order Summary"
{
UsageCategory = ReportsAndAnalysis;
ApplicationArea = All;
Caption = 'Sales Order Summary';
DefaultRenderingLayout = RDLC_Layout;
DataAccessIntent = ReadOnly;
AllowScheduling = true;
PreviewMode = PrintLayout;
dataset
{
// Hierarchical dataitems and columns defined here
}
requestpage
{
// User interaction controls and options
}
rendering
{
// Layout targets (RDLC, Word, Excel)
}
}
Critical Report Properties
| Report Property | Description & Purpose | Runtime Behavior |
|---|---|---|
UsageCategory | Registers the report in the global search (Tell Me feature). Common values include ReportsAndAnalysis, Documents, Tasks, Administration, or None. | When set to a value other than None, the report is discoverable via Tell Me search when the user possesses the matching application area. |
ApplicationArea | Specifies the licensing and UI tier required for the report to appear in search results (e.g., #All, #Basic, #Suite). | Filters object availability based on the tenant's enabled application area tags. |
DefaultRenderingLayout | References the name of the default layout declared inside the rendering block. | Determines which layout is selected automatically when the report is executed without an explicit layout override. |
DataAccessIntent | Controls whether database reads are routed to the primary read-write database or a read-only replica. | When set to ReadOnly, the NST directs SQL read operations to a read-only database replica in cloud (SaaS) environments, significantly reducing transactional lock contention on the primary operational database. |
AllowScheduling | Determines whether users can schedule report execution via the Job Queue from the request page. | If true, adds the Schedule... action to the request page printer options. |
PreviewMode | Specifies the default display mode when previewing the report in the Web Client (Normal or PrintLayout). | PrintLayout renders the document with exact page boundaries matching physical paper output. |
WordMergeDataItem | Identifies the root DataItem for mail-merge processing in Microsoft Word layouts. | Instructs the Word rendering engine to split documents per record of the specified DataItem. |
2. DataItems and Relational Hierarchy
The dataset block defines the data source hierarchy. Each dataitem represents a table cursor that the NST iterates over at runtime. A dataset can contain multiple root DataItems executed sequentially, or deeply nested DataItems establishing parent-child master-detail relationships.
dataset
{
dataitem(Header; "Sales Header")
{
DataItemTableView = sorting("Document Type", "No.") where("Document Type" = const(Order));
RequestFilterFields = "No.", "Sell-to Customer No.", "Posting Date";
PrintOnlyIfDetail = true;
column(OrderNo; "No.") { IncludeCaption = true; }
column(SellToCustNo; "Sell-to Customer No.") { IncludeCaption = true; }
column(SellToCustName; "Sell-to Customer Name") { }
column(PostingDate; "Posting Date") { IncludeCaption = true; }
dataitem(Line; "Sales Line")
{
DataItemLink = "Document Type" = field("Document Type"),
"Document No." = field("No.");
DataItemTableView = sorting("Document Type", "Document No.", "Line No.");
CalcFields = "Reserved Quantity";
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; }
column(ReservedQuantity; "Reserved Quantity") { }
}
}
}
Essential DataItem Properties
| Property | Purpose | Runtime Behavior |
|---|---|---|
DataItemLink | Specifies field-level join conditions between parent and child dataitems. | For every parent record fetched, the child dataitem filters its records where child fields equal parent record fields. |
DataItemLinkReference | Explicitly specifies the parent DataItem identifier when nesting occurs beneath non-immediate ancestors. | Essential when nesting child dataitems beneath intermediate integer loops (e.g., linking Sales Line directly to Sales Header through CopyLoop and PageLoop). |
DataItemTableView | Sets static table keys, sorting orders, and fixed design-time filters. | Enforced at compile-time and cannot be modified or removed by users from the request page. |
RequestFilterFields | Exposes selected fields on the request page filter tab for user input. | Automatically provides interactive filtering UI; values are combined with DataItemTableView using logical AND. |
RequestFilterHeading | Sets a custom display caption for the dataitem tab on the request page. | Overrides the default table caption in the request page UI. |
CalcFields | List of FlowFields to calculate automatically before OnAfterGetRecord. | Optimizes data retrieval by calculating specified FlowFields per record without manual AL Rec.CalcFields() code. |
PrintOnlyIfDetail | Controls parent record suppression when no child records exist. | If true, parent record columns are omitted from the output dataset if the nested child dataitem produces zero records. |
Temporary | Marks the dataitem as an in-memory temporary table cursor. | The report does not query SQL Server directly for this dataitem; records must be populated manually in AL triggers. |
MaxIteration | Restricts the maximum number of records processed by the DataItem. | Useful for batch sampling, debug testing, or limiting output size. |
Exam Watchout —
PrintOnlyIfDetailMechanics: IfPrintOnlyIfDetail = false(default), aSales Headerwith noSales Linerecords will still generate a row in the flattened dataset with header values populated and all line values set toNULL/blank. IfPrintOnlyIfDetail = true, the header is completely suppressed if no matching lines are found.
3. Dataset Flattening & Rectangular Data Architecture
A critical architectural concept tested on the MB-820 exam is that all reporting layout engines (RDLC, Microsoft Word, Microsoft Excel) consume data as a single flat, rectangular two-dimensional table (DataSet_Result), regardless of how deeply nested the AL dataset hierarchy is.
How the NST Flattens Data
When the report runs, the NST executes a nested loop:
- The outer dataitem (
Header) fetches Record 1. - The inner dataitem (
Line) appliesDataItemLinkand fetches its matching records one by one. - For every single inner record retrieved, a full row is emitted to the dataset containing both the parent column values and the child column values.
- If Parent 1 has 3 child lines, 3 rows are written to the dataset. The parent column values (e.g.,
OrderNo,SellToCustName) are repeated identically across all 3 rows. - If a parent dataitem has multiple sibling nested dataitems (e.g.,
dataitem(Line; ...)anddataitem(CommentLine; ...)), the NST performs a Cartesian product / outer join across sibling branches, resulting in sparse rows where non-active branch columns containNULL.
+-----------------------------------------------------------------------------------------+
| Flattened 2D Dataset (DataSet_Result) |
+------------+------------------+---------+-------------+----------+------------+---------+
| OrderNo | SellToCustName | LineNo | ItemNo | Quantity | UnitPrice | LineAmt |
+------------+------------------+---------+-------------+----------+------------+---------+
| 1001 | Adatum Corp. | 10000 | 1896-S | 2 | 150.00 | 300.00 |
| 1001 | Adatum Corp. | 20000 | 1900-S | 1 | 250.00 | 250.00 |
| 1002 | Trey Research | 10000 | 1968-W | 10 | 45.00 | 450.00 |
+------------+------------------+---------+-------------+----------+------------+---------+
Performance & Memory Implications of Dataset Flattening
Because parent data repeats on every child row, emitting dozens of unneeded parent columns can dramatically inflate the memory footprint and network payload transmitted from the Business Central Server tier to the layout rendering engine.
Developers should adhere to the following dataset optimization practices:
- Prune Unused Columns: Only include columns that are directly referenced by active layout designs or required for grouping keys.
- Avoid Cross-Branch Multiplication: Avoid nesting multiple unrelated sibling DataItems under a single parent if both can have large numbers of records; instead, consider using separate integer loops or buffer tables.
- Leverage Partial Records: In report AL triggers (
OnAfterGetRecord), useRec.SetLoadFields()when reading secondary record variables to avoid pulling full table schemas into memory.
4. Column Declarations & Data Mapping
Columns represent the individual data fields exposed to layout designers. A column can source its value from a table field, a global variable, an expression, or a system function.
Column Syntax and Options
column(ColumnName; SourceExpression)
{
IncludeCaption = true;
AutoFormatType = 1;
AutoFormatExpression = Header."Currency Code";
}
ColumnName: The identifier used in the layout file (XML node name). Must be a valid XML element name without spaces or special symbols (e.g., usePostingDate, notPosting Date!).SourceExpression: Table field (e.g.,"No."), variable (e.g.,TotalAmount), expression (e.g.,Format(Today, 0, 4)), or method call.IncludeCaption = true: When enabled on a field column, the AL compiler automatically generates an auxiliary caption column in the dataset schema namedColumnNameCaption(e.g.,OrderNoCaption). This caption reflects the localized caption of the underlying table field based on the user's active runtime language.AutoFormatType&AutoFormatExpression: Formats monetary decimals according to specific currency rules or precision definitions.
RequestFilterFields vs. DataItemTableView
Understanding the distinct roles of RequestFilterFields and DataItemTableView is critical for exam success:
| Feature | DataItemTableView | RequestFilterFields |
|---|---|---|
| Configuration Time | Design-time in AL code. | Design-time in AL code, evaluated at runtime. |
| User Modifiability | Fixed and immutable; users cannot view, edit, or clear these filters. | Interactive; displayed as editable input controls on the request page filter tab. |
| Primary Use Case | Enforcing core business constraints (e.g., where("Document Type" = const(Invoice))). | Providing user flexibility (e.g., filtering by Date Range, Customer No., or Department). |
| Combination Rule | Combined with runtime user filters using a logical AND operator in SQL queries. | Combined with DataItemTableView using a logical AND operator in SQL queries. |
A developer is creating a sales report in AL with a parent dataitem 'Sales Header' and a nested child dataitem 'Sales Line'. The business requirement states that sales orders with no line items must be excluded completely from the generated output. Which configuration should the developer apply?
A developer creates a column column(PostingDate; "Posting Date") in an AL report dataset. The developer wants the localized caption of the Posting Date field to be automatically available in the RDLC and Word layout dataset without manually creating a second column or label. What property should be added to the column definition?
A developer is designing an AL report for Customer Ledger Entries. Users must be able to specify filter criteria for 'Customer No.', 'Posting Date', and 'Document Type' directly on the report request page before running the report, without the developer creating custom global variables or page fields. Which property should the developer configure on the Customer Ledger Entry dataitem?
When an AL report dataset defines a parent DataItem with two sibling child DataItems (such as 'Sales Line' and 'Sales Comment Line'), how does the Business Central Navision Server Tier (NST) represent this data in the output DataSet_Result consumed by layout engines?