Add reporting review guidance and evaluation fixtures (#183)

* knowledge(performance): add job queue reliability guidance

* Address Job Queue review feedback

* Address Job Queue routing review feedback

* Encode job queue conflict in fixtures

* Add reporting review guidance and fixtures

* Fix reporting article reference
This commit is contained in:
Stefano Demiliani 2026-09-15 10:38:32 +02:00 • committed by GitHub
parent b74967bc5b
commit b545b22fb9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
31 changed files with 827 additions and 1 deletions

View file

@ -0,0 +1,16 @@
codeunit 50102 "Run Customer Reports"
{
procedure RunBlockedAndUnblockedCustomers()
var
Customer: Record Customer;
CustomerList: Report "Customer - List";
begin
Customer.SetRange(Blocked, Customer.Blocked::All);
CustomerList.SetTableView(Customer);
CustomerList.RunModal();
Customer.SetRange(Blocked, Customer.Blocked::" ");
CustomerList.SetTableView(Customer);
CustomerList.RunModal();
end;
}

View file

@ -0,0 +1,17 @@
codeunit 50102 "Run Customer Reports"
{
procedure RunBlockedAndUnblockedCustomers()
var
Customer: Record Customer;
CustomerList: Report "Customer - List";
begin
Customer.SetRange(Blocked, Customer.Blocked::All);
CustomerList.SetTableView(Customer);
CustomerList.RunModal();
Clear(CustomerList);
Customer.SetRange(Blocked, Customer.Blocked::" ");
CustomerList.SetTableView(Customer);
CustomerList.RunModal();
end;
}

View file

@ -0,0 +1,32 @@
---
bc-version: [all]
domain: reporting
keywords: [report, runmodal, clear, settableview, instance, state, filters]
technologies: [al]
countries: [w1]
application-area: [all]
---
# Clear a Report variable before an independent RunModal execution
## Description
`Report.Run()` automatically clears the report variable after execution, but `Report.RunModal()` does not. Reconfiguring and running the same variable for an independent operation can therefore retain filters and other instance state from the previous run.
## Best Practice
Call `Clear(ReportVariable)` before configuring a new, logically independent `RunModal()` execution on a reused report variable. No clear is required after a single execution, and retaining state is valid when the subsequent run intentionally continues with the same configuration.
See sample: [`clear-report-variable-before-independent-runmodal.good.al`](clear-report-variable-before-independent-runmodal.good.al).
## Anti Pattern
Run the same report variable modally for two independent views without clearing it between runs. The second `SetTableView` can only narrow the existing report view, so filters retained by the instance can make the second result incomplete or empty.
See sample: [`clear-report-variable-before-independent-runmodal.bad.al`](clear-report-variable-before-independent-runmodal.bad.al).
## References
`Report.RunModal()` method — https://learn.microsoft.com/dynamics365/business-central/dev-itpro/developer/methods-auto/report/reportinstance-runmodal-method
`Report.Run()` method — https://learn.microsoft.com/dynamics365/business-central/dev-itpro/developer/methods-auto/report/reportinstance-run-method

View file

@ -0,0 +1,31 @@
report 50105 "Customer Entry Review"
{
ProcessingOnly = true;
dataset
{
dataitem(Customer; Customer)
{
trigger OnAfterGetRecord()
var
EntryNo: Integer;
begin
repeat
EntryNo += 1;
if EntryNo = 5 then
CurrReport.Break();
until EntryNo = 10;
MarkCustomerReviewed();
end;
}
}
local procedure MarkCustomerReviewed()
begin
ReviewedCustomerCount += 1;
end;
var
ReviewedCustomerCount: Integer;
}

View file

@ -0,0 +1,31 @@
report 50105 "Customer Entry Review"
{
ProcessingOnly = true;
dataset
{
dataitem(Customer; Customer)
{
trigger OnAfterGetRecord()
var
EntryNo: Integer;
StopReview: Boolean;
begin
repeat
EntryNo += 1;
StopReview := EntryNo = 5;
until StopReview or (EntryNo = 10);
MarkCustomerReviewed();
end;
}
}
local procedure MarkCustomerReviewed()
begin
ReviewedCustomerCount += 1;
end;
var
ReviewedCustomerCount: Integer;
}

View file

@ -0,0 +1,30 @@
---
bc-version: [all]
domain: reporting
keywords: [report, currreport, break, loop, trigger, control-flow]
technologies: [al]
countries: [w1]
application-area: [all]
---
# CurrReport.Break ends the current trigger
## Description
`CurrReport.Break()` inside a report dataitem trigger does more than leave an AL loop. It terminates the current trigger and omits the current record from the dataset. The report runtime still invokes the remaining triggers for that record. Consequently, statements after the loop in the current trigger do not run, while later report triggers can still produce side effects.
## Best Practice
Use an explicit loop condition or the AL `break` statement when only the loop must end and the current trigger must continue. Use `CurrReport.Break()` only when ending the trigger and omitting the current record are both intended, and keep subsequent report triggers safe for that omitted record.
See sample: [`currreport-break-ends-the-current-trigger.good.al`](currreport-break-ends-the-current-trigger.good.al).
## Anti Pattern
Call `CurrReport.Break()` inside a loop and rely on statements after the loop to finish processing the current record. Those statements are unreachable when the call executes, the record is omitted, and remaining report triggers still run.
See sample: [`currreport-break-ends-the-current-trigger.bad.al`](currreport-break-ends-the-current-trigger.bad.al).
## References
`Report.Break()` method — https://learn.microsoft.com/dynamics365/business-central/dev-itpro/developer/methods-auto/report/reportinstance-break-method

View file

@ -0,0 +1,27 @@
report 50101 "Update Customer Review"
{
ProcessingOnly = true;
dataset
{
dataitem(Customer; Customer)
{
trigger OnAfterGetRecord()
begin
"Last Date Modified" := Today();
Modify();
if Blocked <> Blocked::" " then
CurrReport.Quit();
end;
}
}
trigger OnPostReport()
begin
Message(CompletedMsg);
end;
var
CompletedMsg: Label 'Customer review completed.';
}

View file

@ -0,0 +1,28 @@
report 50101 "Update Customer Review"
{
ProcessingOnly = true;
dataset
{
dataitem(Customer; Customer)
{
trigger OnAfterGetRecord()
begin
if Blocked <> Blocked::" " then
Error(BlockedCustomerErr, "No.");
"Last Date Modified" := Today();
Modify();
end;
}
}
trigger OnPostReport()
begin
Message(CompletedMsg);
end;
var
BlockedCustomerErr: Label 'Customer %1 is blocked.', Comment = '%1 = customer number';
CompletedMsg: Label 'Customer review completed.';
}

View file

@ -0,0 +1,30 @@
---
bc-version: [all]
domain: reporting
keywords: [report, currreport, quit, rollback, onpostreport, transaction, control-flow]
technologies: [al]
countries: [w1]
application-area: [all]
---
# CurrReport.Quit rolls back report changes and skips OnPostReport
## Description
`CurrReport.Quit()` aborts the report without committing database changes made during its execution. It also prevents `OnPostReport` from running. It is therefore not a normal early-return mechanism for a processing report that expects earlier writes or finalization in `OnPostReport` to survive.
## Best Practice
Use `CurrReport.Quit()` only when silently aborting the report, rolling back its database changes, and skipping `OnPostReport` are all intentional. When processing must stop with a failure, raise an error. When completed work and `OnPostReport` must be preserved, structure the dataitem control flow without `Quit()`.
See sample: [`currreport-quit-rolls-back-and-skips-onpostreport.good.al`](currreport-quit-rolls-back-and-skips-onpostreport.good.al).
## Anti Pattern
Modify data and then call `CurrReport.Quit()` while relying on those writes or on `OnPostReport` finalization. The report exits without committing its changes and never invokes `OnPostReport`.
See sample: [`currreport-quit-rolls-back-and-skips-onpostreport.bad.al`](currreport-quit-rolls-back-and-skips-onpostreport.bad.al).
## References
`Report.Quit()` method — https://learn.microsoft.com/dynamics365/business-central/dev-itpro/developer/methods-auto/report/reportinstance-quit-method

View file

@ -0,0 +1,26 @@
report 50100 "Released Customer List"
{
ProcessingOnly = true;
dataset
{
dataitem(Customer; Customer)
{
trigger OnAfterGetRecord()
begin
if Blocked <> Blocked::" " then
CurrReport.Skip();
CountIncludedCustomer();
end;
}
}
local procedure CountIncludedCustomer()
begin
IncludedCustomerCount += 1;
end;
var
IncludedCustomerCount: Integer;
}

View file

@ -0,0 +1,28 @@
report 50100 "Released Customer List"
{
ProcessingOnly = true;
dataset
{
dataitem(Customer; Customer)
{
trigger OnAfterGetRecord()
begin
if Blocked <> Blocked::" " then begin
CurrReport.Skip();
exit;
end;
CountIncludedCustomer();
end;
}
}
local procedure CountIncludedCustomer()
begin
IncludedCustomerCount += 1;
end;
var
IncludedCustomerCount: Integer;
}

View file

@ -0,0 +1,30 @@
---
bc-version: [all]
domain: reporting
keywords: [report, currreport, skip, trigger, onaftergetrecord, control-flow]
technologies: [al]
countries: [w1]
application-area: [all]
---
# CurrReport.Skip omits the record but does not stop trigger code
## Description
`CurrReport.Skip()` omits the current record from the report dataset and continues processing with the next record. It does not terminate the current trigger, and the remaining triggers for the current record still run. Code placed after `Skip()` can therefore produce side effects for a record that never appears in the output.
## Best Practice
When no further code in the current trigger should run for a skipped record, call `CurrReport.Skip()` and then exit the trigger explicitly. Keep later record triggers safe for skipped records because the report runtime still invokes them.
See sample: [`currreport-skip-does-not-stop-trigger-code.good.al`](currreport-skip-does-not-stop-trigger-code.good.al).
## Anti Pattern
Call `CurrReport.Skip()` and rely on it to bypass subsequent statements or later record triggers. The record is removed from the dataset, but those statements and triggers can still update state, write data, or perform expensive work.
See sample: [`currreport-skip-does-not-stop-trigger-code.bad.al`](currreport-skip-does-not-stop-trigger-code.bad.al).
## References
`Report.Skip()` method — https://learn.microsoft.com/dynamics365/business-central/dev-itpro/developer/methods-auto/report/reportinstance-skip-method

View file

@ -0,0 +1,14 @@
codeunit 50106 "Download Customer Reports"
{
procedure DownloadReports(var Customer: Record Customer)
var
CustomerView: Record Customer;
begin
if Customer.FindSet() then
repeat
CustomerView := Customer;
CustomerView.SetRecFilter();
Report.Run(Report::"Customer - List", false, false, CustomerView);
until Customer.Next() = 0;
end;
}

View file

@ -0,0 +1,37 @@
codeunit 50106 "Download Customer Reports"
{
procedure DownloadReports(var Customer: Record Customer)
var
CustomerView: Record Customer;
CustomerList: Report "Customer - List";
DataCompression: Codeunit "Data Compression";
ReportTempBlob: Codeunit "Temp Blob";
ZipTempBlob: Codeunit "Temp Blob";
ReportInStream: InStream;
ZipInStream: InStream;
ReportOutStream: OutStream;
ZipOutStream: OutStream;
ZipFileName: Text;
begin
DataCompression.CreateZipArchive();
if Customer.FindSet() then
repeat
Clear(CustomerList);
Clear(ReportTempBlob);
CustomerView := Customer;
CustomerView.SetRecFilter();
CustomerList.SetTableView(CustomerView);
ReportTempBlob.CreateOutStream(ReportOutStream);
CustomerList.SaveAs('', ReportFormat::Pdf, ReportOutStream);
ReportTempBlob.CreateInStream(ReportInStream);
DataCompression.AddEntry(ReportInStream, Customer."No." + '.pdf');
until Customer.Next() = 0;
ZipTempBlob.CreateOutStream(ZipOutStream);
DataCompression.SaveZipArchive(ZipOutStream);
DataCompression.CloseZipArchive();
ZipTempBlob.CreateInStream(ZipInStream);
ZipFileName := 'CustomerReports.zip';
DownloadFromStream(ZipInStream, '', '', '*.zip', ZipFileName);
end;
}

View file

@ -0,0 +1,32 @@
---
bc-version: [all]
domain: reporting
keywords: [report, run, saveas, downloadfromstream, web-client, loop, zip, data-compression]
technologies: [al]
countries: [w1]
application-area: [all]
---
# Report output in a loop needs one client download
## Description
The Business Central Web client can deliver only one file per request. When AL generates or downloads a report file repeatedly in the same request, only the last file is delivered to the browser. Earlier report output is silently unavailable to the user even though every iteration ran.
## Best Practice
Generate each report into a stream, add the streams to one archive, and call `DownloadFromStream` once after the loop. A direct report run or download inside a loop is valid only when the execution context does not use the Web client or the loop is guaranteed to execute at most once.
See sample: [`report-output-in-a-loop-needs-one-client-download.good.al`](report-output-in-a-loop-needs-one-client-download.good.al).
## Anti Pattern
Call `Report.Run`, `Report.RunModal`, or `DownloadFromStream` repeatedly in a loop initiated by one Web client action and expect every generated file to reach the browser. The client receives only the last download.
See sample: [`report-output-in-a-loop-needs-one-client-download.bad.al`](report-output-in-a-loop-needs-one-client-download.bad.al).
## References
`File.DownloadFromStream` method — https://learn.microsoft.com/en-us/dynamics365/business-central/dev-itpro/developer/methods-auto/file/file-downloadfromstream-method
`Data Compression` codeunit — https://learn.microsoft.com/dynamics365/business-central/application/system-application/codeunit/system.io.data-compression

View file

@ -0,0 +1,30 @@
report 50103 "Base Customer Export"
{
ProcessingOnly = true;
dataset
{
dataitem(Customer; Customer)
{
trigger OnPreDataItem()
begin
SetRange(Blocked, Blocked::" ");
SetRange("Country/Region Code");
end;
}
}
}
reportextension 50104 "Local Customer Export" extends "Base Customer Export"
{
dataset
{
modify(Customer)
{
trigger OnBeforePreDataItem()
begin
SetFilter("Country/Region Code", '<>%1', '');
end;
}
}
}

View file

@ -0,0 +1,30 @@
report 50103 "Base Customer Export"
{
ProcessingOnly = true;
dataset
{
dataitem(Customer; Customer)
{
trigger OnPreDataItem()
begin
SetRange(Blocked, Blocked::" ");
SetRange("Country/Region Code");
end;
}
}
}
reportextension 50104 "Local Customer Export" extends "Base Customer Export"
{
dataset
{
modify(Customer)
{
trigger OnAfterPreDataItem()
begin
SetFilter("Country/Region Code", '<>%1', '');
end;
}
}
}

View file

@ -0,0 +1,32 @@
---
bc-version: [19..]
domain: reporting
keywords: [reportextension, report, dataitem, trigger-order, onbeforepredataitem, onafterpredataitem, onbeforeaftergetrecord, onafteraftergetrecord]
technologies: [al]
countries: [w1]
application-area: [all]
---
# Choose ReportExtension dataitem triggers by their order around the base trigger
## Description
ReportExtension dataitem triggers run at defined points around the corresponding base-report trigger. `OnBeforePreDataItem` and `OnBeforeAfterGetRecord` run before the base trigger; `OnAfterPreDataItem` and `OnAfterAfterGetRecord` run after it. A filter or calculated value can be overwritten when an extension uses a before-trigger even though its result must be final after base processing.
## Best Practice
Choose the before or after trigger from the required ordering relative to base behavior. Use an after-trigger when the extension must observe or refine the final view or value produced by the base trigger. A before-trigger is valid when the base report must consume the extension's state.
See sample: [`reportextension-dataitem-trigger-order-is-explicit.good.al`](reportextension-dataitem-trigger-order-is-explicit.good.al).
## Anti Pattern
Place extension logic in a before-trigger while relying on its filter or value to survive a base trigger that can replace it. Do not report a before-trigger merely because an after-trigger exists; the defect requires visible base behavior or another reliable source showing that ordering changes the result.
See sample: [`reportextension-dataitem-trigger-order-is-explicit.bad.al`](reportextension-dataitem-trigger-order-is-explicit.bad.al).
## References
`OnBeforePreDataItem` report-extension trigger — https://learn.microsoft.com/dynamics365/business-central/dev-itpro/developer/triggers-auto/reportextensiondatasetmodify/devenv-onbeforepredataitem-reportextensiondatasetmodify-trigger
`OnAfterPreDataItem` report-extension trigger — https://learn.microsoft.com/dynamics365/business-central/dev-itpro/developer/triggers-auto/reportextensiondatasetmodify/devenv-onafterpredataitem-reportextensiondatasetmodify-trigger

View file

@ -0,0 +1,31 @@
report 50110 "Customer Export"
{
ProcessingOnly = true;
dataset
{
dataitem(Customer; Customer)
{
}
}
trigger OnPreReport()
var
ExportSetup: Record "Customer Export Setup";
begin
ExportSetup.Get();
ExportSetup.TestField("Export Date");
end;
}
reportextension 50111 "Customer Export Extension" extends "Customer Export"
{
trigger OnPreReport()
var
ExportSetup: Record "Customer Export Setup";
begin
ExportSetup.Get();
ExportSetup.Validate("Export Date", Today());
ExportSetup.Modify(true);
end;
}

View file

@ -0,0 +1,37 @@
report 50110 "Customer Export"
{
ProcessingOnly = true;
dataset
{
dataitem(Customer; Customer)
{
}
}
trigger OnPreReport()
var
ExportDate: Date;
begin
OnBeforeResolveExportDate(ExportDate);
if ExportDate = 0D then
Error(ExportDateRequiredErr);
end;
[IntegrationEvent(false, false)]
local procedure OnBeforeResolveExportDate(var ExportDate: Date)
begin
end;
var
ExportDateRequiredErr: Label 'An export date is required.';
}
codeunit 50111 "Customer Export Extension"
{
[EventSubscriber(ObjectType::Report, Report::"Customer Export", 'OnBeforeResolveExportDate', '', false, false)]
local procedure SetExportDate(var ExportDate: Date)
begin
ExportDate := Today();
end;
}

View file

@ -0,0 +1,30 @@
---
bc-version: [18..]
domain: reporting
keywords: [reportextension, report, trigger-order, onprereport, onpostreport, base-report, integration-event]
technologies: [al]
countries: [w1]
application-area: [all]
---
# ReportExtension report triggers run after base report triggers
## Description
`OnPreReport` and `OnPostReport` on a ReportExtension run after the corresponding triggers on the base report. An extension `OnPreReport` cannot prepare state that the base `OnPreReport` must consume, and an extension `OnPostReport` cannot affect finalization that the base `OnPostReport` has already completed.
## Best Practice
Use a base-report event at the required execution point when extension logic must run before or within a base trigger. Use ReportExtension `OnPreReport` and `OnPostReport` only for work that is correct after the corresponding base trigger. Report a violation only when the base trigger and extension dependency are both visible or otherwise established.
See sample: [`reportextension-report-triggers-run-after-base-triggers.good.al`](reportextension-report-triggers-run-after-base-triggers.good.al).
## Anti Pattern
Initialize data in a ReportExtension `OnPreReport` and rely on the base report's `OnPreReport` to consume it, or perform extension `OnPostReport` work that the base `OnPostReport` needed beforehand. The base trigger has already run.
See sample: [`reportextension-report-triggers-run-after-base-triggers.bad.al`](reportextension-report-triggers-run-after-base-triggers.bad.al).
## References
Report extension object — https://learn.microsoft.com/dynamics365/business-central/dev-itpro/developer/devenv-report-ext-object

View file

@ -0,0 +1,26 @@
report 50107 "Selected Sales Orders"
{
ProcessingOnly = true;
dataset
{
dataitem(SalesHeader; "Sales Header")
{
DataItemTableView = where("Document Type" = const(Order), Status = const(Open));
}
}
}
codeunit 50108 "Run Selected Sales Orders"
{
procedure RunReleasedOrders()
var
SalesHeader: Record "Sales Header";
SelectedSalesOrders: Report "Selected Sales Orders";
begin
SalesHeader.SetRange("Document Type", SalesHeader."Document Type"::Order);
SalesHeader.SetRange(Status, SalesHeader.Status::Released);
SelectedSalesOrders.SetTableView(SalesHeader);
SelectedSalesOrders.RunModal();
end;
}

View file

@ -0,0 +1,26 @@
report 50107 "Selected Sales Orders"
{
ProcessingOnly = true;
dataset
{
dataitem(SalesHeader; "Sales Header")
{
DataItemTableView = where("Document Type" = const(Order));
}
}
}
codeunit 50108 "Run Selected Sales Orders"
{
procedure RunReleasedOrders()
var
SalesHeader: Record "Sales Header";
SelectedSalesOrders: Report "Selected Sales Orders";
begin
SalesHeader.SetRange("Document Type", SalesHeader."Document Type"::Order);
SalesHeader.SetRange(Status, SalesHeader.Status::Released);
SelectedSalesOrders.SetTableView(SalesHeader);
SelectedSalesOrders.RunModal();
end;
}

View file

@ -0,0 +1,30 @@
---
bc-version: [all]
domain: reporting
keywords: [report, settableview, dataitemtableview, filter, view, narrowing]
technologies: [al]
countries: [w1]
application-area: [all]
---
# SetTableView cannot broaden DataItemTableView
## Description
`Report.SetTableView()` applies the supplied record view by narrowing the view already defined by the report dataitem's `DataItemTableView`. It cannot remove or broaden a static dataitem filter. A caller that requests records excluded by `DataItemTableView` therefore produces an empty dataset rather than overriding the report filter.
## Best Practice
Keep only invariant restrictions in `DataItemTableView`. When callers must select among values, leave that dimension open in the static view and pass the required filter through `SetTableView`. Review this as a defect only when the report definition and caller together show a contradictory filter.
See sample: [`settableview-cannot-broaden-dataitemtableview.good.al`](settableview-cannot-broaden-dataitemtableview.good.al).
## Anti Pattern
Define a static filter in `DataItemTableView` and call `SetTableView` with a mutually exclusive filter while expecting the runtime view to replace the static one. The filters are intersected and no records are selected.
See sample: [`settableview-cannot-broaden-dataitemtableview.bad.al`](settableview-cannot-broaden-dataitemtableview.bad.al).
## References
`Report.SetTableView()` method — https://learn.microsoft.com/dynamics365/business-central/dev-itpro/developer/methods-auto/report/reportinstance-settableview-method

View file

@ -0,0 +1,17 @@
codeunit 50109 "Export Customer Report"
{
procedure ExportReport()
var
TempBlob: Codeunit "Temp Blob";
ReportOutStream: OutStream;
RequestPageParameters: Text;
begin
RequestPageParameters := Report.RunRequestPage(Report::"Customer - List");
TempBlob.CreateOutStream(ReportOutStream);
Report.SaveAs(
Report::"Customer - List",
RequestPageParameters,
ReportFormat::Pdf,
ReportOutStream);
end;
}

View file

@ -0,0 +1,20 @@
codeunit 50109 "Export Customer Report"
{
procedure ExportReport()
var
TempBlob: Codeunit "Temp Blob";
ReportOutStream: OutStream;
RequestPageParameters: Text;
begin
RequestPageParameters := Report.RunRequestPage(Report::"Customer - List");
if RequestPageParameters = '' then
exit;
TempBlob.CreateOutStream(ReportOutStream);
Report.SaveAs(
Report::"Customer - List",
RequestPageParameters,
ReportFormat::Pdf,
ReportOutStream);
end;
}

View file

@ -0,0 +1,30 @@
---
bc-version: [all]
domain: reporting
keywords: [report, runrequestpage, cancel, parameters, saveas, execute, print]
technologies: [al]
countries: [w1]
application-area: [all]
---
# Stop when RunRequestPage returns empty parameters
## Description
`Report.RunRequestPage()` returns an empty string when the user chooses **Cancel**. Passing that value to `Report.Execute`, `Report.Print`, or `Report.SaveAs` ignores the cancellation and can run the report with default parameters instead.
## Best Practice
Test the returned parameter string immediately after `RunRequestPage()` and exit when it is empty. Pass the value to `Execute`, `Print`, or `SaveAs` only after the user has confirmed the request page.
See sample: [`stop-when-runrequestpage-returns-empty-parameters.good.al`](stop-when-runrequestpage-returns-empty-parameters.good.al).
## Anti Pattern
Call `RunRequestPage()` and unconditionally pass its return value to a report execution method. Choosing **Cancel** can still execute, print, or save the report.
See sample: [`stop-when-runrequestpage-returns-empty-parameters.bad.al`](stop-when-runrequestpage-returns-empty-parameters.bad.al).
## References
`Report.RunRequestPage()` method — https://learn.microsoft.com/dynamics365/business-central/dev-itpro/developer/methods-auto/report/report-runrequestpage-method