Work with conditional statements

Completed

Conditional statements are used heavily in any programming language, including application language (AL). A conditional statement is used to test a condition. Based on the evaluation of that condition, it might run one or more statements.

If statements

The most commonly used conditional statement is the if statement.

var 
    a: Integer;
    b: Integer;
    c: Integer;
begin
    a := 10;
    b := 5;

    if a > b then
        c := a - b;
end;

In the previous example, variables a and b are assigned a value. With the if statement, the condition that a is larger than b is tested. If this condition is true, then the variable c will be assigned the value of a minus b.

Notice the position of the semicolon. A semicolon indicates the start of a new statement. Because if .. then is considered one statement, the semicolon is put after the last statement, not after the then keyword.

You can only put one statement after the then keyword. If you want to run multiple statements when the condition is true, you need to use a compound statement, as shown in the following example.

var
    a: Integer;
    b: Integer;
    c: Integer;
begin
    a := 10;
    b := 5;

    if a > b then begin
        c := a - b;
        Message('%1', c);
    end;
end;

It's important to notice the positions of the different semicolons after each statement within the compound statement, and also at the end of the compound statement.

If-then-else statement

The if statement is frequently combined with an else statement. If the condition isn't evaluated as true, the statement(s) in the else block is run.

var
    a: Integer;
    b: Integer;
    c: Integer;
begin
    a := 10;
    b := 5;

    if a > b then
        c := a - b
    else
        c := a + b;
end;

No semicolon is placed at the end of the statement in the then block; one is only placed at the end of the complete if-then-else statement.

You can also use compound statements in an if-then-else structure.

var
    a: Integer;
    b: Integer;
    c: Integer;
begin
    a := 10;
    b := 5;

    if a > b then begin
        c := a - b;
        Message('%1', c);
    end
    else begin
        c := a + b;
        Message('%1', c);
    end;
end;

Because an if-then-else statement is also a regular statement, you can put if statements in other if statements, which will create nested if statements.

begin
    if Amount <> 0 then
        if Amount > 0 then
            Sales := Sales + Amount
        else
            if Reason = Reason::Return then
                if ReasonForReturn = ReasonForReturn::Defective then
                    Refund := Refund + Amount
                else
                    Credits := Credits + Amount
            else
                Sales := Sales - Amount;
end;

Be careful with nested if statements because they are often difficult to read and can be complex. Looking at the positions of the semicolons can help make them easier to read and understand.

Programming conventions

For successful implementations, follow these principles:

  • If and then should be on the same line, else should be on a separate line.

  • If there are many or long expressions, then should be on a new line and be aligned with if.

  • When you write if expressions with then and else parts, write them so that the then result is more probable than the else one.

  • If the last statement in the then part of an if-then-else statement is an exit or an error, don't continue with an else statement.

Case statement

Another conditional statement that you can use is the case statement. Depending on the value of a condition, the case statement will run other statements.

The next example shows that, depending on the value of Document Type (which is an option field in a table), the case statement will run some statements. The case statement can use an else block that will be run when no other blocks are run. Because you can only run one statement for each block with a case statement, you have to use compound statements to run more statements simultaneously.

var
    a: Integer;
begin
    case "Document Type" of
        "Document Type"::Quote:
            a := 1 + 1;
        "Document Type"::Order:
            a := 2 + 1;
        "Document Type"::Invoice:
            begin
                // Some statement 1;
                // Some statement 2;
                // Some statement 3;
                a := 3 + 1;
            end;
        "Document Type"::"Return Order":
            if Reason = Reason::Return then begin
                // Some statement 1;
                // Some statement 2;
                // Some statement 3;
                a := 4 + 1;                
            end;
        else
            a := 5 + 1;
    end;

end;

Case statements are also called multiple option statements and are typically used when you must choose between more than two different actions. The method of the case statement is as follows:

  • The Expression is evaluated, and the first matching value set executes the associated statement, if there's one.

  • If no value set matches the value of the expression and the optional else part has been omitted, then no action is taken. If the optional else part is used, then the associated statement is executed.

The data type of the value sets must be the same as the data type of Expression or at least be convertible to the same data type.

In most cases, the data type of the value sets is converted to the data type of the evaluated expression. The only exception is if the evaluated expression is a Code variable. If the evaluated expression is a Code variable, then the value sets aren't converted to the Code data type.

Programming conventions - Case statements

When you use a case statement, indent the value sets by four character spaces. If you've two or more value sets on the same line, then separate them by commas without spaces. The last value set on a line is immediately followed by a colon without a preceding space. The action starts on the line after the value set and is further indented by four character spaces. If there's a begin, then it should be put on a separate line unless it follows else. If a begin follows an else, then it should be on the same line as else.

If there are more than two alternatives, use a case statement. Otherwise, use an if-then-else statement.

Preprocessor Directives in AL

In AL, like in other programming languages, preprocessor directives can be used to make code conditional, to suppress warnings, or to enable expand and collapse in code.

Preprocessor directives can be divided into the following groups.

  • Conditional directives
  • Regions
  • Pragmas

Any code can be made conditional, including table fields, and checked using a conditional directive. To check code using a conditional directive, you must define a symbol to check. A symbol returns a boolean value; true or false.

Symbols can be defined at the beginning of a source file and the scope of the specific symbol is the file that it is defined within. You can also define symbols in the app.json file, and then the scope is global for the extension.

The following conditional preprocessor directives are supported in AL.

  • #if - Specifies the beginning of a conditional clause. The #endif clause ends it. Compiles the code between the directives if the specified symbol being checked is defined.

  • #else - Specifies a compound conditional clause. If none of the preceding clauses evaluates to true, the compiler will evaluate code between #else and #endif.

  • #elif - Combines else and if. If #elif is true the compiler evaluates all code between #elif and the next conditional directive.

  • #endif - Specifies the end of a conditional clause that begins with #if.

  • #define - Defines a symbol that can be used to specify conditions for a compilation. For example, #define DEBUG. The scope of the symbol is the file that it was defined in.

  • #undef - Undefines a symbol.

Symbols can be defined globally in the app.json file. A symbol can also be defined using the #define directive in code, but if symbols are defined in the app.json file, they can be used globally.

The following example defines DEBUG as a global symbol. This can then be used from code as illustrated in the Conditional code example below. A symbol has a boolean value, that means it evaluates to true or false.

app.json file

"preprocessorSymbols": [ "DEBUG" ]

al-code using DEBUG symbol

#if DEBUG
    trigger OnOpenPage()
    begin
        Message('Only in debug versions');
    end;
#endif

Suppress AL warnings

In some cases, compiler or analyzer warnings are due to intended code use. To reduce clutter so that developers can focus on warnings that must be addressed, we are adding support to explicitly suppress warnings, either for a whole extension or locally in a code enclosure.

There are two ways to support warnings:

  • Globally (for an extension)

  • Locally

Remember that most warnings are there for a reason, and suppressing them could lead to sudden impact, if warnings are changed to errors (such as obsoletes).

Global

There is a new suppressWarnings property in the app.json manifest so that you can suppress a comma-separated list of warning IDs when you compile the extension:

"suppressWarnings": [Warning ID,Warning ID2,...]

Local

Directives is a new construct in the AL language that specifies how the AL compiler treats an enclosed section of code. The same concept is known in other languages. The specific directive instructions must be supported by the compiler. You can't create custom preprocessing instructions.

One of the new directives is a warning pragma, which you can set around a code section to suppress a comma-separated list of warnings only in that enclosure.

If no end pragma closure is provided, it will be the rest of the file. The restore returns it to whatever global suppression state there is as described above. When no warning numbers are specified, disable disables all warnings and restore enables all warnings.

#pragma warning disable warning-list
#pragma warning restore warning-list