Writing customized code is easy; customized code should look like standard code. How to write customized code? Like standard code! You have to keep in mind that any code you write today will probably be maintained by others in the future. If you follow your own programming conventions, we are pretty sure you'll find them easier to write and read. Unfortunately, others may not be used to your conventions, so you'll be making their work harder.
Like it or not, all Dynamics NAV developers are used to reading code from the standard application. If everyone writes customized code like the standard application does, everybody will only be able to read their own code. To make it easy to maintain an application, it is important to follow a few strict guidelines while writing C/AL code. This chapter will cover these guidelines. The information found in this section is taken from C/AL Programming Guide.
In the standard application, all C/AL code is entered in English (United States). If all code is in the same language, it is easier to maintain the applications, including add-ons for several countries.
There must be exactly one space character on each side of binary operators such as assignment or plus, as shown in the following example:
y := (a + b) / 100;
There must not be any space between a unary operator and its argument, as shown in the following example:
y := -x;
In general, use an indentation of two character spaces, as shown in following the example:
IF a <> '' THEN Record.TESTFIELD(b);
When you split a C/AL statement into two or more lines, indent the continuation line by two characters, as shown in the following example:
MyVariable := Variable1 + Variable2 * 2 + Variable3 * 3; MyFunction( Expression1,Expression2, Expression3,Expression4);
Always start comments with //
followed by one character space. Never use curly brackets ({ and }). To emphasize a comment, put it on a separate line and insert one empty line before it, as shown in the following example:
// Comment x := x * 2;
If the comment is on the same line as the C/AL code, add one character space before the comment sign, as shown in the following example:
x := '....'; // Comment
Error messages and other message strings must be entered as text constants. That way, the message can be easily translated and the users can see the same message in their own language.
Text constants will automatically be assigned unique IDs by C/SIDE. You can see the ID by opening the C/AL Globals window, selecting the text constant, and navigating to its Properties window.
When you are working in the C/AL Editor window, place the cursor on a text constant and the content of the text constant will be shown in the message line.
The IF
and THEN
statements should normally be on the same line. The ELSE
statement should be on a separate line as shown in the following example:
IF x = y THEN x := x + 1 ELSE x := -x - 1;
If the last statement in the THEN
part of an IF-THEN-ELSE
statement string is an EXIT
command or an ERROR
command, do not continue with an ELSE
statement, as shown in the following code snippet:
IF x <> y THEN EXIT(TRUE); x := x * 2;
When the BEGIN
statement follows the THEN
, ELSE
, or DO
statement, it should be on the same line, preceded by one character space as shown in the following snippet:
IF (x = y) AND (a = b) THEN BEGIN x := a; y := b; END;
REPEAT
should always be alone on a line. Indentation of REPEAT
statements is shown in the following example snippet:
REPEAT <Statement>; UNTIL <expr>; REPEAT <Statement>; UNTIL <expr> AND <expr>;
When you use a CASE
statement, indent the possibilities by two character spaces. Two or more possibilities on the same line are separated by commas (with no spaces), and the last possibility on a line is immediately followed by a colon (with no preceding space).
The action starts on the line after the possibility, further indented by two character spaces. If there is a BEGIN
statement, it should be placed on a separate line unless it follows the ELSE
statement. In this case, it should be on the same line as the ELSE
statement.
CASE Field OF Field::A: BEGIN x := x + 1; y := -y - 1; END; Field::B: x := y; Field::C,Field::D: y := x; ELSE BEGIN y := x; a := b; END; END;
If there are more than two alternatives, use a CASE
statement; otherwise, use an IF
statement.
Precise and consistent terminology helps the end user work with the application. Rules for naming and abbreviating everything will also help programmers gain an understanding of the base application and develop new features faster.
Remember that the user will see the value of the caption property but not the name property. But you, as a developer, must follow the naming convention in this section both for the name in English (United States) and for the caption in your local language.
Two objects of the same type must not have the same name. In general, each object must be named in a way that leaves no doubt as to what it is concerned with (for example, an object can be specifically related to customers, items, or resources). Do not give a table the name Status
, for example, because the word is too general and could refer to something in almost every table.
The names of table objects are always singular. That is, the table name corresponds to what one record in the table is called.
The name of a page depends on the page type. A card page has the singular form of the table name and a list page has the plural form of the table name. This gives the users an idea of the type of page they have selected or that which will be presented. If a table can be accessed by both a card page and a list page, the page names should explicitly describe the page types (for example, item card and item list). This tells the user that there is more than one way to access the table. Other page types (for example, statistics) are given names that are as descriptive as possible.
The naming of reports is as important as that of pages and tables. For example, users see the caption of a report object when they need to identify a sales invoice, or when they modify or create reports. The caption is also shown in the request page. This is why the caption (and the name in English) should be as descriptive as possible and not include abbreviations. Whenever possible, the caption should be the same as the heading in the actual report.
The name and caption of a field should be as descriptive as possible and should be able to stand alone, that is, the user should not need to see the caption in the context of other fields in order to understand what it is.
The field contents and the field type should be described in the caption. For example, include Date
while you name a date field (for instance, Posting Date
). If the field contains a percentage, include it. This is displayed with the percentage sign, for example, Profit %
. Include Quantity
(or Qty.
) when you name a quantity field, for example, Quantity Shipped
. Replace Quantity
with No.
while referring to the number of entries, for example, No. Printed
and No. of New Records
. Include Amount
(or Amt.
) while you name an amount field, for example, Debit Amount
.
A codeunit is named almost like a report, except that the name begins with the object that the codeunit processes, followed by a dash. The object is normally a record abbreviated as a variable (see rules for this in the next section). The description of the codeunit is written in the imperative (without abbreviations, if possible), for example, Purch-Explode BOM
.
Use the existing terminology whenever possible; for instance, the standard application usually names variables that refer to the General Journal Line
table as GenJnlLine
. Blanks, periods, and other characters (such as parentheses) that would make quotation marks around a variable necessary, must be omitted. For example, the periods and blanks are omitted in the GenJnlBatch
variable. In addition, currency unit signs, such as $
, should be replaced by the corresponding currency unit code, for example, AmountUSD
.
While naming user-defined functions, if possible start with a verb in the imperative, for example, ApplyCustLedgEntry
. Usage of function name prefixes are shown in the following examples:
Post
as a prefixMake
as a prefixInsert
as a prefixCheck
as a prefixEvery Dynamics NAV object allows you to create functions inside itself. Instead of writing all your code in an existing trigger, break up your code into small steps. Write each small step's code in a separate function, give the function a name so that it explains what it does, and call your function from where you need it. This will ensure that you do not not write the same thing twice.
If the functions you are creating are to be called from one single object, create them on the object. If the functions are to be called from many objects, create them in a codeunit to group them all, or create them on a table if the function refers to a record of a table.