Collect errors

Completed

AL code can capture multiple errors and display them in the user interface. Referred to as collectible errors, this feature can simplify validation scenarios. Specifically, it can simplify scenarios where users are presented with a list of errors to fix.

Normally, when an error occurs in a procedure, the procedure stops on the first error that it encounters. Using collectable errors will essentially postpone error handling to the end of the procedure call. AL code implementation won't stop on errors. Instead, it will continue until the end and will gather errors as they occur.

AL includes several methods, properties, and attributes that are designed specifically for the collectable errors feature.

The AddAction method accepts three parameters:

  • Caption: The text string that appears as the caption of the action in the error UI.

  • CodeunitID: The ID of the Codeunit to run when the action is initiated from the error UI. The codeunit should contain at least one global method to be called by the error action. The global method must have an ErrorInfo data type parameter for accepting the ErrorInfo object.

  • Method Name: The name of the method in the Codeunit, which is specified by the CodeunitID parameter, that you want to run for the action.

The following methods are available on the System data type for handling collected errors. You can invoke these methods by using property access syntax.

If you clear the list of collected errors, changes that are made in the database won't be rolled back. Therefore, in most cases, it makes sense to combine the clear operation with an if Codeunit.Run then ... statement.

The ErrorBehavior attribute specifies the behavior of collectable errors inside the method scope. Adding [ErrorBehavior(ErrorBehavior.Collect)] to a procedure makes it possible to collect and handle errors that are raised in the scope of the procedure.

The following code example illustrates how to use collectable errors. It's built around the DoPost codeunit, which sets basic criteria on what can or can't be included in table fields. This procedure will stop when errors occur.

The PostWithErrorCollect () and PostWithErrorCollectCustomUI () procedures show you ways to collect and present these errors by applying the ErrorBehavior(ErrorBehavior::Collect) attribute.

pageextension 50100 CollectingErrorsExt extends "Customer List"
{
    actions
    {
        addfirst(processing)
        {
            // This action doesn't collect errors. Any procedure will stop on the first error that occurs,
            // and return the error.
            action(Post)
            {
                ApplicationArea = All;
                trigger OnAction()
                var
                    i: Record Integer;
                begin
                    i.Number := -9;
                    Codeunit.Run(Codeunit::DoPost, i);
                end;
            }

            // This action collects errors. The PostWithErrorCollect procedure continues on errors,
            // and displays the errors in a dialog to the user done.
            action(PostWithErrorCollect)
            {
                ApplicationArea = All;
                trigger OnAction()
                begin
                    PostWithErrorCollect();
                end;
            }

            // This action collects errors. The PostWithErrorCollectCustomUI procedure continues on errors,
            // and displays error details in a list page when done.
            // This implementation illustrates how you could design your own UI for displaying and
            // troubleshooting errors.
            action(PostWithErrorCollectCustomUI)
            {
                ApplicationArea = All;
                trigger OnAction()
                begin
                    PostWithErrorCollectCustomUI();
                end;
            }
        }
    }

    [ErrorBehavior(ErrorBehavior::Collect)]
    procedure PostWithErrorCollect()
    var
        i: Record Integer;
    begin
        i.Number := -9;
        Codeunit.Run(Codeunit::DoPost, i);
        // After executing the codeunit, there will be collected errors,
        // and therefore an error dialog will be shown when exiting this procedure.
    end;

    [ErrorBehavior(ErrorBehavior::Collect)]
    procedure PostWithErrorCollectCustomUI()
    var
        errors: Record "Error Message" temporary;
        error: ErrorInfo;
        i: Record Integer;
    begin
        i.Number := -9;
        // By using Codeunit.Run, you ensure any changes to the database within
        // Codeunit::DoPost are rolled back in case of errors.
        if not Codeunit.Run(Codeunit::DoPost, i) then begin
            // If Codeunit.Run fails, a non-collectible error was encountered,
            // add this to the list of errors.
            errors.ID := errors.ID + 1;
            errors.Description := GetLastErrorText();
            errors.Insert();
        end;

        // If there are collected errors, iterate through each of them and
        // add them to "Error Message" record.
        if HasCollectedErrors then
            foreach error in system.GetCollectedErrors() do begin
                errors.ID := errors.ID + 1;
                errors.Description := error.Message;
                errors.Validate("Record ID", error.RecordId);
                errors.Insert();
            end;

        // Clearing the collected errors will ensure the built-in error dialog
        // will not show, but instead show our own custom "Error Messages" page.
        ClearCollectedErrors();

        page.RunModal(page::"Error Messages", errors);
    end;
}


codeunit 50100 DoPost
{
    TableNo = Integer;

    trigger OnRun()
    begin
        if Number mod 2 <> 0 then
            Error(ErrorInfo.Create('Number should be equal', true, Rec, Rec.FieldNo(Number)));

        if Number <= 0 then
            Error(ErrorInfo.Create('Number should be larger than 0', true, Rec, Rec.FieldNo(Number)));

        if Number mod 3 = 0 then
            Error(ErrorInfo.Create('Number should not be divisible by 10', true, Rec, Rec.FieldNo(Number)));

        // Everything was valid, do the actual posting.
    end;
}