Skip to content

Sample Report over Codeunit

Codeunit 5138176 "SIM_DPS Sample Report over CU" demonstrates how the Business Portals action type Report over Codeunit can select a record through custom logic and return a Business Central report as a PDF download.

The sample uses SIM_DPS OL as its source table. It resolves a record, applies the configured report request-page settings, renders the configured report into SIM_DPS OL."File Blob", and lets Business Portals return that file to the frontend.

When to use Report over Codeunit

A standard Report action runs a report for the dataset record. Use Report over Codeunit when custom AL logic must decide which record is passed to the report. For example, an action can print a sales order selected in an input field instead of the record displayed by the current dataset table.

This sample provides both approaches:

Function Record source
PrintOrderConfirmation Reads a record ID from the SALESORDER action input field.
PrintOrderConfirmationFromTable Uses SIM_DPS OL."Dataset Table Record", which represents the current dataset record.

Function discovery and dispatch

Business Portals runs the codeunit without an action code when it needs the list of available functions. OnRun therefore defaults to GETFUNCTIONS and returns both function names as a comma-separated list.

AL
'GETFUNCTIONS':
    Rec."Function List" := 'PrintOrderConfirmation,PrintOrderConfirmationFromTable';

During report execution, OnRun loads the dataset-table action and its related action setup. The action's Param value selects the procedure to execute.

Resolving the report record

From an input field

PrintOrderConfirmation reads the value of the input field with code SALESORDER and evaluates it as a Business Central RecordId.

The input value must be a formatted RecordId, not only a sales order number such as SO-1001. A missing or invalid value causes Evaluate to raise an error. When adapting the sample, validate the value and provide a user-friendly error message before calling PrintReport.

From the dataset table

PrintOrderConfirmationFromTable passes Dataset Table Record directly to PrintReport. Use this function when the report's main record is the same record represented by the dataset row.

Creating the PDF

The shared PrintReport procedure performs the following steps:

  1. Opens the supplied record ID as a RecordRef.
  2. Uses SIM_DPS Order Function Library.FilterRecRefByKey to create a record reference filtered by the record's primary key.
  3. Loads the SIM_DPS Action identified by Action Code.
  4. Reads the optional request-page XML from Report Settings.
  5. Clears the existing File Blob and creates an output stream for it.
  6. Calls Report.SaveAs with the configured Report ID, PDF format, request-page settings, and filtered record.

After the codeunit finishes, Business Portals reads File Blob, converts the PDF to Base64, and returns it to the frontend as the action's downloadable file. The dataset-table action controls the download filename and can use configured filename placeholders.

Configuration

To configure this sample pattern:

  1. Open the actions for the required dataset table.
  2. Create an action and set Type to Report over Codeunit.
  3. Set ID to codeunit 5138176 or to the ID of the customer-specific copy.
  4. Select PrintOrderConfirmation or PrintOrderConfirmationFromTable in Param.
  5. Select the Business Central report in Report Caption or set its Report ID.
  6. Optionally use Set Report Settings to save filters and request-page options for the report.
  7. Configure the report filename and any filename placeholders on the dataset-table action.
  8. When using PrintOrderConfirmation, configure an action input field with code SALESORDER whose value resolves to a valid Business Central RecordId.

The selected report must support the table represented by the resolved record and must be suitable for unattended Report.SaveAs execution.

Implementation considerations

Before using this code in a production extension, consider the following points:

  • Keep TableNo = "SIM_DPS OL"; Business Portals supplies the action context through Rec.
  • Add each selectable function to both the GETFUNCTIONS list and the dispatch case statement.
  • Validate input-field values before converting them to RecordId.
  • Confirm that Action Code, Report ID, and the resolved record are present and valid.
  • The sample retries the same Report.SaveAs call up to three times. If all attempts fail, it exits without a dedicated error. Replace this with error handling and logging appropriate to the solution.
  • The code clears File Blob before rendering. A failed render therefore leaves no previous file available.
  • Test saved report settings after report layouts, request pages, or Business Central versions change.
  • Apply permission checks appropriate to the records and reports exposed through the portal.
  • Avoid interactive report logic because portal execution does not provide a report request page to the end user.

Complete sample code

AL
codeunit 5138176 "SIM_DPS Sample Report over CU"
{
    TableNo = "SIM_DPS OL";

    trigger OnRun()
    var
        LocalRecordSIMDPSDTAction: Record "SIM_DPS DT Action";
        LocalRecordSIMDPSAction: Record "SIM_DPS Action";
        LocalParamText: Text;
    begin
        LocalParamText := 'GETFUNCTIONS';

        if Rec."Action Code" <> '' then begin
            if not LocalRecordSIMDPSDTAction.Get(Rec."Dataset Code", Rec."Dataset Table Code", Rec."Dataset Table Type", Rec."Action Code") then exit;
            if not LocalRecordSIMDPSAction.Get(LocalRecordSIMDPSDTAction."Action Code") then exit;
            LocalParamText := LocalRecordSIMDPSAction.Param;
        end;

        case LocalParamText of
            'PrintOrderConfirmation':
                PrintOrderConfirmation(Rec);

            'PrintOrderConfirmationFromTable':
                PrintOrderConfirmationFromTable(Rec);

            'GETFUNCTIONS':
                Rec."Function List" := 'PrintOrderConfirmation,PrintOrderConfirmationFromTable';
        end;
    end;

    var
        GlobalCodeunitSIMDPSOrderFunctionLibrary: Codeunit "SIM_DPS Order Function Library";
        GlobalRecIdLabelErr: Label 'You have not selected a valid record.';

    procedure PrintOrderConfirmation(var ParamRecordSIMDPSOL: Record "SIM_DPS OL")
    var
        LocalRecordId: RecordId;
    begin
        Evaluate(LocalRecordId, Format(GlobalCodeunitSIMDPSOrderFunctionLibrary.GetDPSOrderLineInputFieldByCode(ParamRecordSIMDPSOL, 'SALESORDER')));
        PrintReport(ParamRecordSIMDPSOL, LocalRecordId);
    end;

    procedure PrintOrderConfirmationFromTable(var ParamRecordSIMDPSOL: Record "SIM_DPS OL")
    var
        LocalRecordId: RecordId;
    begin
        LocalRecordId := ParamRecordSIMDPSOL."Dataset Table Record";
        PrintReport(ParamRecordSIMDPSOL, LocalRecordId);
    end;

    procedure PrintReport(var ParamRecordSIMDPSOL: Record "SIM_DPS OL"; ParamRecordId: RecordId)
    var
        LocalRecordSIMDPSAction: Record "SIM_DPS Action";
        LocalRecordRef: RecordRef;
        Local2RecordRef: RecordRef;
        LocalVariant: Variant;
        LocalReportRequestPageText: Text;
        LocalInStream: InStream;
        LocalOutStream: OutStream;
    begin
        if LocalRecordRef.Get(ParamRecordId) then begin
            GlobalCodeunitSIMDPSOrderFunctionLibrary.FilterRecRefByKey(LocalRecordRef, Local2RecordRef);
            LocalVariant := Local2RecordRef;

            if LocalRecordSIMDPSAction.Get(ParamRecordSIMDPSOL."Action Code") then begin
                LocalReportRequestPageText := '';
                if LocalRecordSIMDPSAction."Report Settings".HasValue() then begin
                    LocalRecordSIMDPSAction.CalcFields("Report Settings");
                    LocalRecordSIMDPSAction."Report Settings".CreateInStream(LocalInStream);
                    LocalInStream.Read(LocalReportRequestPageText);
                end;

                Clear(LocalOutStream);
                Clear(ParamRecordSIMDPSOL."File Blob");
                ParamRecordSIMDPSOL."File Blob".CreateOutStream(LocalOutStream);
                if not Report.SaveAs(LocalRecordSIMDPSAction."Report ID", LocalReportRequestPageText, ReportFormat::Pdf, LocalOutStream, LocalVariant) then
                    if not Report.SaveAs(LocalRecordSIMDPSAction."Report ID", LocalReportRequestPageText, ReportFormat::Pdf, LocalOutStream, LocalVariant) then
                        if not Report.SaveAs(LocalRecordSIMDPSAction."Report ID", LocalReportRequestPageText, ReportFormat::Pdf, LocalOutStream, LocalVariant) then
                            exit;
            end;
        end else
            Error(GlobalRecIdLabelErr);
    end;
}