Process flow control functions are the functions that execute the decision making and resultant logic branches in executable code. IF–THEN-ELSE
, discussed in Chapter 6, Introduction to C/SIDE and C/AL, is also a member of this class of functions. Here we will discuss the following:
REPEAT-UNTIL
WHILE-DO
FOR-TO
and FOR-DOWNTO
CASE-ELSE
WITH-DO
QUIT
, BREAK
, EXIT
, and SKIP
REPEAT–UNTIL
allows us to create a repetitive code loop which REPEATs
a block of code UNTIL
a specific conditional expression evaluates to TRUE
. In that sense, REPEAT–UNTIL
defines a block of code, operating somewhat like the BEGIN–END
compound statement structure which we covered in Chapter 6, Introduction to C/SIDE and C/AL. REPEAT
tells the system to keep reprocessing the block of code, while UNTIL
serves as the exit doorman, checking if the conditions for ending the processing are true. Because the exit condition is not evaluated until the end of the loop, a REPEAT–UNTIL
structure will always process at least once through the contained code.
REPEAT
–UNTIL
is very important in NAV because it is often part of the data input cycle along with the FIND-NEXT
structure, which will be covered shortly.
Here is an example of the REPEAT
–UNTIL
structure to process and sum data in the 10-element array CustSales
:
LoopCount := 0; REPEAT LoopCount := LoopCount + 1; TotCustSales := TotCustSales + CustSales[LoopCount]; UNTIL LoopCount = 10;
A WHILE
–DO
control structure allows us to create a repetitive code loop which will DO (execute) a block of code WHILE a specific conditional expression evaluates to TRUE
. WHILE
–DO
is different from REPEAT
–UNTIL
, both because it may need a BEGIN–END
structure to define the block of code to be executed repetitively (REPEAT
–UNTIL
does not) and it has different timing for the evaluation of the exit condition.
The syntax of the WHILE
–DO
control structure is as follows:
WHILE <Condition> DO <Statement>
Condition
can be any Boolean expression which evaluates to TRUE
or FALSE
. Statement
can be simple or the most complex compound BEGIN–END
statement. Most WHILE
–DO
loops will be based on a BEGIN–END
block of code. Condition
will be evaluated at the beginning of the loop. When it evaluates to FALSE
, the loop will terminate. Thus, a WHILE
–DO
loop can be exited without processing.
A WHILE
-DO
structure to process data in the 10-element array CustSales
is as follows:
LoopCount := 0; WHILE LoopCount < 10 DO BEGIN LoopCount := LoopCount + 1; TotCustSales := TotCustSales + CustSales[LoopCount]; END;
In NAV, REPEAT-UNTIL
is much more frequently used than WHILE-DO
.
The syntax for a
FOR-TO
or FOR-DOWNTO
control statement is as follows:
FOR <Control Variable> := <Start Number> TO <End Number> DO <Statement>
or
FOR <Control Variable> := <Start Number> DOWNTO <End Number> DO <Statement>
A FOR
control structure is used when we wish to execute a block of code a specific number of times.
The Control Variable
is an Integer variable. Start Number
is the beginning count for the FOR
loop and End Number
is the final count for the loop. If we wrote the statement: FOR LoopCount := 5 TO 7 DO [block of code]
then [block of code]
would be executed 3 times.
FOR-TO
increments the Control Variable. FOR-DOWNTO
decrements the Control Variable
.
We must be careful not to manipulate the Control Variable
in the middle of our loop. Doing so will likely yield unpredictable results.
The CASE
–ELSE
statement is a conditional expression very similar to IF–THEN–ELSE,
except that it allows for more than two choices of outcomes for the evaluation of the controlling expression. The syntax of the CASE-ELSE
statement is as follows:
CASE <ExpressionToBeEvaluated> OF <Value Set 1> : <Action Statement 1>; <Value Set 2> : <Action Statement 2>; <Value Set 3> : <Action Statement 3>; ... ... <Value Set n> : <Action Statement n>; [ELSE <Action Statement n + 1>; END;
The ExpressionToBeEvaluated
must not be a record. The data type of the Value
Set
must be capable of being automatically converted to the data type of the ExpressionToBeEvaluated
. Each Value
Set
must be an expression, a set of values, or a range of values. The following example illustrates a typical instance of a CASE
-ELSE
statement:
CASE Customer."Salesperson Code" OF '2','5','9': Customer."Territory Code" := 'EAST'; '16'..'20': Customer."Territory Code" := 'WEST'; 'N': Customer."Territory Code" := 'NORTH'; '27'..'38': Customer."Territory Code" := 'SOUTH'; ELSE Customer."Territory Code" := 'FOREIGN'; END;
In the preceding example, we see several alternatives for the Value
Set
. The first line (EAST
) Value
Set
contains the list of values. If Salesperson
Code
is equal to '2',
'5',
or '9'
, the value EAST
will be assigned to Customer."Territory
Code"
. The second line (WEST
) Value
Set
is a range, any value from '16'
through '20'
. The third line (NORTH
) Value
Set
is just a single value ('N'
). If we look through standard NAV code, we will see that a single value is the most frequently used CASE
structure in NAV. In the fourth line of our example (SOUTH
), the Value
Set
is again a range ('27'..'38'
). If nothing in any Value
Set
matches ExpressionToBeEvaluated
, the ELSE
clause will be executed.
An example of an IF
-THEN
-ELSE
statement equivalent to the preceding CASE
-ELSE
statement is as follows:
IF Customer."Salesperson Code" IN ['2','5','9'] THEN Customer."Territory Code" := 'EAST' ELSE IF Customer."Salesperson Code" IN ['16'..'20'] THEN Customer."Territory Code" := 'WEST' ELSE IF Customer."Salesperson Code" = 'N' THEN Customer."Territory Code" := 'NORTH' ELSE IF Customer."Salesperson Code" IN ['27'..'38'] THEN Customer."Territory Code" := 'SOUTH' ELSE Customer."Territory Code" := 'FOREIGN';
The following is a slightly less intuitive example of the CASE
–ELSE
statement. In this instance, ExpressionToBeEvaluated
is a simple TRUE
and the Value
Set
statements are all conditional expressions. The first line containing a Value
Set
expression that evaluates to TRUE
will be the line whose Action
Statement
is executed. The rules of execution and flow in this instance are same as in the previous example.
CASE TRUE OF Salesline.Quantity < 0: BEGIN CLEAR(Salesline."Line Discount %"); CredTot := CredTot - Salesline.Quantity; END; Salesline.Quantity > QtyBreak[1]: Salesline."Line Discount %" := DiscLevel[1]; Salesline.Quantity > QtyBreak[2]: Salesline."Line Discount %" := DiscLevel[2]; Salesline.Quantity > QtyBreak[3]: Salesline."Line Discount %" := DiscLevel[3]; Salesline.Quantity > QtyBreak[4]: Salesline."Line Discount %" := DiscLevel[4]; ELSE CLEAR(Salesline."Line Discount %"); END;
When we are writing code referring to fields within a record, the most specific syntax for field references is the fully qualified reference [ RecordName.FieldName ]
. When referring to the field City in the record Customer, use the reference Customer.City
.
In many C/AL instances, the record name qualifier is implicit, because the compiler assumes a default record qualifier based on the code context. This happens automatically for variables within a page bounded to a table. The bound table becomes the implicit record qualifier for fields referenced in the Page object. In a Table object, the table is the implicit record qualifier for fields referenced in the C/AL in that object. In Report and XMLport objects, the Data Item record is the implicit record qualifier for the fields referenced within the triggers of that Data Item such as OnAfterGetRecord
and OnAfterImportRecord
.
In all other C/AL code, the only way to have an implicit record qualifier is to use the WITH-DO
statement. WITH-DO
is widely used in the base product in Codeunits and processing Reports. The WITH-DO
syntax is:
WITH <RecordQualifier> DO <Statement>
Typically, the DO
portion of this statement will be followed by a BEGIN-END
code block, allowing for a compound statement. The scope of the WITH-DO
statement is terminated by the end of the DO
statement.
When we execute a WITH-DO
statement, RecordQualifier
becomes the implicit record qualifier used by the compiler until the end of that statement or until that qualifier is overridden by a nested WITH-DO
statement. Where fully qualified, syntax would requires the following form:
Customer.Address := '189 Maple Avenue'; Customer.City := 'Chicago';
The WITH-DO
syntax takes advantage of the implicit record qualification making the code easier to write, and hopefully easier to read. For example:
WITH Customer DO BEGIN Address := '189 Maple Avenue'; City := 'Chicago'; END;
Best practice says that WITH-DO
statements should only be used in functions within a Codeunit or a Report.
WITH-DO
statements nested one within another are legal code, but are not used in standard NAV. They are also not recommended because they can easily confuse the developer, resulting in bugs. The same comments apply to nesting a WITH-DO
statement within a function where there is an automatic implicit record qualifier, such as in a table, Report, or XMLport.
Of course, wherever the references to record variables other than the implicit one occur within the scope of a WITH-DO statement
, we must include the specific qualifiers. This is particularly important when there are variables with the same name (for example, City
) in multiple tables that might be referenced in the same set of C/AL logic.
Some developers maintain that it is always better to use fully qualified variable names to reduce the possibility of inadvertent reference errors. This approach also eliminates any possible misinterpretation of variable references by a maintenance developer who works on this code later.
This group of C/AL functions also control process flow. Each acts to interrupt flow in different places and with different results. To get a full appreciation for how these functions are used, we need to review them in the correct place in code in NAV 2015.
The QUIT
function is the ultimate processing interrupt for Report or XMLport objects. When a QUIT
is executed, processing immediately terminates even for the OnPostObject
triggers. No database changes are committed. QUIT
is often used in reports to terminate processing when the report logic determines that no useful output will be generated by further processing.
The syntax of the QUIT
function is as follows:
CurrReport.QUIT; CurrXMLport.QUIT;
The BREAK
function terminates the DataItem in which it occurs. BREAK
can only be used in Data Item triggers in Reports and XMLports. It can be used to terminate the sequence of processing one DataItem segment of a report while allowing subsequent DataItem processing to continue.
The BREAK
syntax is one of the following:
CurrReport.BREAK; CurrXMLport.BREAK;
EXIT
is used to end the processing within a C/AL trigger. EXIT
works the same whether it is executed within a loop or not. It can be used to end the processing of the trigger or to pass a return value from a local function. A return value cannot be used for system defined triggers or local functions that don't have a return value defined. If EXIT
is used without a return value, a default return value of zero is returned. The syntax for EXIT
is:
EXIT([<ReturnValue>])
When executed, the SKIP
function will skip the remainder of the processing in the current record cycle of the current trigger. Unlike BREAK
, it does not terminate the DataItem processing completely. It can be used only in the OnAfterGetRecord
trigger of a Report or XMLport object. In reports, when the results of processing in the OnAfterGetRecord
trigger are determined not to be useful for the output, the SKIP
function is used to terminate that single iteration of the trigger without interfering with any subsequent processing.
The SKIP
syntax is one of the following:
CurrReport.SKIP; CurrXMLport.SKIP;