Programming Conventions
To make it easy to maintain
an application, it is important to
follow a few strict
guidelines when writing C/AL™ code.
This chapter describes
these guidelines.
·
General C/AL Programming Format
·
Multilanguage Functionality
·
C/AL Statements
·
Miscellaneous
·
User-Defined Functions
·
User Messages
·
Table Locking
·
Putting Functions in Objects
1.1 GENERAL C/AL
PROGRAMMING FORMAT
Spacing
There must be exactly one
space character on each side of a binary operator such as assignment or plus.
EXAMPLE
y := (a +
b) / 100;
There must be no space
between a unary operator and its argument.
EXAMPLE
y := -x;
Refer to multiple
dimensions in a record array by using sets of brackets with no space characters
in between.
EXAMPLE
a[i][j] :=
b;
Do not use blank lines at
the beginning or end of any functions.
EXAMPLE
PROCEDURE
P();
BEGIN
x := a;
y := (a +
b) / 100;
END;
Alignment
In general, use an
indentation of two character spaces.
EXAMPLE
IF a
<> '' THEN
Record.TESTFIELD(b);
Splitting Lines
When you split a C/AL
statement into two or more lines, do not align the continuation lines according
to user- or system-defined variables, functions, field names, object names, and
so on. Instead, indent the continuation lines by two characters.
EXAMPLE
Write this:
MyVariable
:=
Variable1 + Variable2 * 2 +
Variable3 * 3;
Do not write
this:
MyVariable
:= Variable1 + Variable2 * 2 +
Variable3 * 3;
The second format might
look clearer in your program, but the alignment will not hold
if the variable name
MyVariable is changed to something shorter or longer in another
national territory version.
Note:
Although the system-defined
variable and function names are not likely to change for
the moment, the rule also
applies when using them.
Here are some more
examples:
EXAMPLE
MyFunction(
Expression1,Expression2,
Expression3,Expression4);
EXAMPLE
ERROR(
StringExpression,Expression1,Expression2,Expression3);
EXAMPLE
IF NOT
SETCURRENTKEY(
aaaaaaaaaa,bbbbbbbbbb,cccccccccc,
dddddddddd,eeeeeeeeee)
THEN
SETCURRENTKEY(bbbbbbbbbb,aaaaaaaaaa);
Aligning Parentheses
A left parenthesis in an
expression should be aligned with a corresponding parenthesis on the line
above.
EXAMPLE
aaaaaaaaaa
:=
((xxxxxxxxxxx / yyyyyyyyyyyyy) -
(1 +
zzzzzzzzzz / 100)) *
100;
EXAMPLE
IF (xxx
<> '') AND
((A = 1) OR
(B = 2))
THEN
...
Using Parentheses
Do not use parentheses in a
function call if the function does not have any parameters.
EXAMPLE
PostLine;
Use parentheses only to
enclose compound expressions inside compound expressions.
EXAMPLE
IF Boolean1
AND Boolean2 AND (x = y) THEN
x := a + b
ELSE
x := b / (a + b);
Comments
Always start comments with
// followed by one space character. Never use curly brackets ({ and }). To
emphasize a comment, put it on a separate line and insert one empty line before
it.
EXAMPLE
x := x + 1;
// Comment
x := x * 2;
If the comment is on the
same line as the C/AL code, add one space character before the comment sign.
EXAMPLE
x :=
'....'; // Comment
Use Symbolic Values
Whenever possible, use the
name of the option for a field instead of just an integer for the value.
EXAMPLE (GOOD)
Table.Field
:= Table.Field::Option;
EXAMPLE (BAD)
Table.Field
:= 1:
EXAMPLE (GOOD)
IF Table.Field IN[Table.Field::Option1,Table.Field::Option2] THEN
EXIT;
Whenever possible, use
option names instead of hard code values in the conditional possibilities in a
CASE statement.
EXAMPLE
CASE xxx OF
xxx::aaa,xxx::bbb:
x := y;
ELSE
y := x;
END;
Parameters
Use parameters whenever you
need to transfer information to a function.
To use a parameter as an
option field, define it in the function. When you call the function, use an
integer as parameter in the call.
EXAMPLE
...
P(0);
...
PROCEDURE
P(MyOption : 'Value0,Value1,Value2');
BEGIN
CASE MyOption OF
MyOption::Value0:
x := x * 10;
MyOption::Value1:
x := x * 15;
MyOption::Value2:
x := x * 20;
END;
END;
Order in Expressions
The
variable you are operating on or comparing to something else must always come first
in expressions.
EXAMPLE (GOOD)
IF x
<> 0 THEN
x := x - 100;
EXAMPLE (GOOD)
IF (Date
< a) OR (Date > b) THEN
Date := c;
EXAMPLE (BAD)
IF 0 > x
THEN
x := x - 100;
Order of Variables
Variables should be listed
in the following order:
1 Record
variables
2 Form
variables
3 Report
variables
4 Dataport
variables
5 Codeunit
variables
6 Dialog,
file and BLOB variables
7 Simple
variables
Record variables are listed
in an order that reflects the hierarchy of the tables used in the database.
Base tables come before journals and other non-posted lines and headers, which
themselves come before ledger entries and posted lines and headers.
EXAMPLE
VAR
GLSetup : Record 98;
UserSetup : Record 91;
ItemPostingGr : Record 94;
Item : Record 27;
ItemJnlTemplate : Record 82;
ItemJnlBatch : Record 233;
ItemJnlLine : Record 83;
ItemReg : Record 46;
ItemLedgEntry : Record 32;
1.2 MULTILANGUAGE FUNCTIONALITY
Navision Attain is
multilanguage enabled. This means changes in the way that developers work in
the application.
The purpose of the
multilanguage-enabled environment is to make translation easier and make it possible
to switch from one language to another in the user interface so that, for
example, a German and a Swede can work side by side in their own languages on
the same database.
When you develop in a
multilanguage-enabled environment, it is important to remember the following
three rules of thumb:
·
Everything has a Name property in English (United States).
·
Text constants replace text strings such as error messages.
·
Everything that the user will see must have a Caption property.
Note
Before you start working in
a multilanguage-enabled database, you should set the application language as
English (United States). You do this by clicking Tools, Languages and selecting
English (United States).
Code Base Language
In Navision Attain, the
code base is English (United States). This means that the
Name property of, for
example, an object must always be English (United States).
The code base in English
(United States) includes, among other things, the following:
·
Object names
·
Field names
·
Function and variable names
·
Comments
·
Option strings
·
Control names
Name Property
As mentioned above, all
code should be in English (United States), abbreviated ENU. The Name property
of an object should always be ENU, but it should also never be visible to the
user. In all references to the user interface, you must use the Caption property
instead.
For example, if you want to
call on a field from the code in connection with a user
message, you will call it
by its Name property but make sure that the Caption property
is displayed:
VATPostingSetup.FIELDCAPTION("VAT
Calculation Type");
Text Constants
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 languages.
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 opening itsProperties window.
When you
are working in the C/AL Editor window and
place the cursor on a text constant, the content of the text constant will be
shown in the message line.
Caption Rather Than Name
The Name
property is only used internally and is not translated, so you must never use
the Name property for the user interface; use the Caption property instead. Remember
to think Caption, not Name!
The
CaptionML property is what makes it possible to change languages. Everything must
have a CaptionML property where the value is set to the correct term in ENU. The
ENU value is followed by whatever translations there may be of that object.
EXAMPLE
In a Canadian database,
Table 37, Field 1 has the following CaptionML values:
ENU=Document
Type;FRC=Type document
Option Buttons
When you
design an option button, fill in the properties for the control as follows:
Property
Value
Name Name of
control, for example Control1.
CaptionML Caption of control
(option button) in ENU and your local language, for
example ENU=Item Ledger Entry.
OptionValue Caption of control
(option button) in ENU, for example ENU=Item
Ledger Entry.
Option Strings
When you design a control
with an option string, fill in the properties for the control as follows:
Property Value
Name Name of
control, for example Control1.
CaptionML Caption of control
in ENU and your local language, for example
ENU=Source Type.
OptionCaptionML Option string in
ENU and your local language, for example
ENU=Sale,Purchase.
1.3 C/AL STATEMENTS
This section describes the
structure of C/AL statements.
IF-THEN-ELSE
IF and THEN should normally
be on the same line. ELSE should be on a separate line.
EXAMPLE
IF x = y
THEN
x := x + 1
ELSE
x := -x - 1;
If there are many or long
expressions, THEN should be on a new line – aligned with IF.
EXAMPLE
IF
(xxxxxxxxxx = yyy) AND
(aaa = bbbbbbbbbb)
THEN
x := a
ELSE
y := b;
When you write IF
expressions with THEN and ELSE parts, try to write them so that the THEN
consequence is more probably than the ELSE one.
EXAMPLE
IF
condition THEN
rule
ELSE
exception
If the IF expression is too
complex, reverse the THEN and ELSE parts.
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.
EXAMPLE
IF x
<> y THEN
EXIT(TRUE);
x := x * 2;
y := y - 1;
EXAMPLE (BAD)
IF x < y
THEN
EXIT(TRUE)
ELSE BEGIN
x := x * 2;
y := y - 1;
END;
BEGIN-END
When BEGIN follows THEN,
ELSE or DO, it should be on the same line, preceded by one space character.
EXAMPLE
IF (x = y)
AND (a = b) THEN BEGIN
x := a;
y := b;
END;
EXAMPLE
IF (xxx =
yyyyyyyyyy) AND
(aaaaaaaaaa = bbb)
THEN BEGIN
x := a;
x := y;
a := y;
END ELSE
BEGIN
y := x;
y := a;
END;
REPEAT-UNTIL
Indentation of REPEAT
statements:
Simple case:
REPEAT
<Statement>;
UNTIL
<expr>;
Complex case:
REPEAT
<Statement>;
UNTIL
<expr> AND
<expr> AND
<expr>;
REPEAT should always be
alone on a line.
EXAMPLE
IF x < y
THEN BEGIN
REPEAT
x := x + 1;
a := a - 1;
UNTIL x = y;
b := x;
END;
WHILE-DO
Indentation of WHILE-DO
statements:
This is the format for
simple cases (one expression after WHILE):
EXAMPLE
WHILE
<expr> DO
<Statement>;
EXAMPLE
WHILE
<expr> DO BEGIN
<Statement>;
<Statement>;
END;
This is the format for
complex cases (several expressions after WHILE):
EXAMPLE
WHILE
<expr> AND
<expr> AND
<expr>
DO
<Statement>;
EXAMPLE
WHILE
<expr> AND
<expr> AND
<expr>
DO BEGIN
<Statement>;
<Statement>:
END;
CASE
When you
use CASE, 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, it should be placed on a separate line unless it follows ELSE.
In this case, it should be on the same line as ELSE.
EXAMPLE
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;
CASE or IF? If there
are more than two alternatives, use a CASE statement. Otherwise, use IF.
WITH-DO
When you
write WITH-DO statement blocks, be careful when creating one WITH-DO block
within another explicit or implicit WITH-DO block. (Implicit WITH-DO blocks exist,
for example, in table objects and in forms that have been attached to a
record.)
The
WITH-DO block that you create within another WITH-DO block must always be attached
to a variable of the same type as the variable that is attached to the surrounding
WITH-DO block. Otherwise it can be difficult to see what variable a member
variable or function refers to. Below is a good example of nesting WITH-DO blocks
(both WITH-DO blocks are attached to a “Customer Ledger Entry” record variable).
There is also a bad example, where you cannot directly tell which record variable
the MyField field
refers to.
EXAMPLE (GOOD)
WITH
CustLedgEntry DO BEGIN
INSERT;
...;
WITH CustLedgEntry2 DO BEGIN
INSERT;
...;
END;
END;
EXAMPLE (BAD)
WITH
CustLedgEntry DO BEGIN
...;
WITH VendLedgEntry DO BEGIN
MyField := <Some Value>;
...;
END;
END;
Within WITH-DO blocks, do
not repeat the name of the object with the member
variable or function. For
example, in the following example do not replace the call of
the member function INSERT
with MyRecord.INSERT.
EXAMPLE
WITH
MyRecord DO BEGIN
INSERT;
...;
END;
1.4 MISCELLANEOUS
Keep it Simple
When you program a solution
in C/SIDE®, try to keep it simple. This applies to everything that becomes
visible either to other programmers or to any users. Below are a few simple
examples of where you should not
complicate a solution.
·
If the default value for a property is adequate for a certain
purpose, do not make the
default value explicit.
·
If a variable can be reset using a statement like
a := 0;
do not use a special C/AL
function to do the job. That is, do not do this:
CLEAR(a);
·
If the contents of a record can be copied using a statement like
MyRec :=
MyRec2;
do not use a special C/AL
function to do the job. That is, do not do this:
MyRec.TRANSFERFIELDS(MyRec2);
Activating Objects
Generally, when you want to
use the value of a field to find a record in a table, or you want to activate
an object identified by the field, make sure that the field actually contains a
value. To do this, use the TESTFIELD function. This will produce more informative
error messages if the value is zero or blank.
EXAMPLE
GLEntry.TESTFIELD("Department Code");
Dept.GET(GLEntry."Department Code");
GenJnlTemplate.TESTFIELD("Report ID");
REPORT.RUN(GenJnlTemplate."Report ID")
Copying Solutions
Sometimes
you may wish to achieve the same degree of functionality as exists somewhere in
the base application. We suggest that you copy (using new ID and Name) objects
from the original solution and change the copies, using the steps below as a
pattern.
You could
use this method, for example, to implement journal functionality (using journal
template, batch and line tables).
Here are the steps you
would follow:
1 Start by copying the command
button in the main menu that activates the journal solution you want to copy.
2 See which objects this command
button calls (and check that these objects are specific to the journal).
3 Make a copy of these objects and
call them from within your application.
4 Then see which objects are
referred to by the objects you have copied (and check that these objects are
specific to the journal), and copy these referred-to objects too.
5 Change the references to point to
the new objects.
6 Continue this recursive process
until you have copied all the journal-specific objects that the main menu
command button points to (directly and indirectly).
7 Modify the new objects
appropriately. For example, you must decide which fields to include in the
journal line table.
Setting Properties
To set properties from
C/AL, use the following style:
"Customer No.".Visible := TRUE;
Cust.MARK := TRUE;
CurrReport.SHOWOUTPUT := TRUE;
Do not write:
Customer." No.".Visible(TRUE);
Cust.MARK(TRUE);
CurrReport.SHOWOUTPUT(TRUE);
Editable=No on FlowField®
Remember
to set the property Editable=No on FlowFields unless you want to be able to
enter values in the field. For example, it is possible to enter values in the Budgeted Amount field in the G/L Account table.
Disabling FIelds
Since a disabled field
cannot be included in a form, never release a table with disabled fields.
Mandatory Primary Key
As
default, set the property NotBlank=Yes on the primary key fields in a table. No
other fields in a table should have this property.
Validating Table Relations
When you
apply the property ValidateTableRelation=No to a field, you should also apply
the property TestTableRelation=No. Otherwise a database test on the field relations
in a database may fail.
Programming Lookups
When
programming lookups, do not filter out records that the user might want to select.
Instead, program the record cursor to be positioned on the most relevant record
for the search, even though it may not be first on the list.
When programming the
OnLookup trigger for a field, remember that the system will not call the code
in the field’s OnValidate trigger unless you call Field.VALIDATE explicitly.
Remember also that if
errors can occur in the validation, you must operate on a copy of the
Rec-variable (as shown in the example below) instead of directly on Rec.
EXAMPLE
Department Code – OnLookup
WITH Cust
DO BEGIN
Cust := Rec;
Dept.Code := "Department Code";
IF FORM.RUNMODAL(O,Dept) = Action::LookupOK
THEN BEGIN
"Department Code" := Dept. Code;
VALIDATE("Department Code"):
Rec := Cust:
END;
END;
Field Lengths
As a rule, use 20
characters for a code field that is likely to be visible to external
companies or organizations.
Otherwise, use 10 characters.
Field Type Code or Text
These are the rules for
choosing between the types Code
and Text:
·
If the field is part of a primary key, it should have type Code.
·
If the field has a TableRelation, it should have type Code.
·
If the field in other ways is assigned values from a Code field,
it should have type Code. (The Document No. field in tables like the G/L Entry table is
copied from the primary key field No. in tables like the Sales Header and Purchase Header.)
·
In all other cases, the field should have type Text.
Fields containing a date
formula must not have data type Code. Instead, use data type DateFormula. All fields with data type Code must be converted into data type DateFormula.
To assign a value to data
type DateFormula, whether it is a field or a variable, you must use the
EVALUATE function.
EXAMPLE
IF
FORMAT(Dateformulavariable) = ' ' THEN
EVALUATE(Dateformulavariable, '1W');
You must use the FORMAT
function to make a comparison with a text string. If you do not use this
function, the IF statement will fail, because you can not compare DateFormula with data type Text.
Designing Journal Forms
The
default order of fields in journals is:
Date, Document Type, Document No., No., Description, Amount
The order
of fields in ledger entry forms should be the same. The order of fields in forms
and reports should also be the same.
Using Subforms
When you
add a subform control to a form you should normally set the properties HorzGlue=Both,
VertGlue=Both and Border=No. Remember that the subform control and the form you
refer to must be the same size.
Making Semi-Editable Tabular Forms
Instead of
setting the property Editable=No on a tabular form, you can set the property LineEditing=Yes on the TableBox. This makes
the form semi-editable.
Making Non-Editable Card Forms
To make it
possible to delete records on a card form but not possible to edit the field on
the form, set the property Editable=No on the card form’s TabControl, and set
the properties InsertAllowed=No and ModifyAllowed=No on the form.
Table Relations to System Tables
To make
Lookup work on a field that has a table relation to a system table, you must always
explicitly set the LookupFormID property on controls showing the field.
Form Evaluation Order
It can be
hard to know exactly how triggers in a form are activated. To test what actually
happens, you can add code to the triggers so that a log is written to an external
text file.
Translating Objects
When you
translate a set of objects or an entire application, you must use correct
andconsistent terminology. You must also prevent the translation from adding
new errors to the application.
Follow these steps when you
translate the objects to identify any errors that you may have introduced.
1 Remove
Date, Time and Modified flags from the objects.
2 Export the
objects to a text file.
3 Use the
Tools, Translate functions to translate the objects.
4 Compile
the translated objects.
5 Translate
the objects back to the original language.
6 Compile
the retranslated objects.
7 Remove
Date, Time and Modified flags from the objects.
8 Export the
objects to a text file.
9 Compare
the original object text file with the new one. Any differences are likely to be
due to conflicting translations.
Table Functions
In C/SIDE
1.10 and later versions, it is possible to write functions in table objects and
call these functions from outside the table. Therefore we recommend that all
small functions that were previously located in separate codeunits now be
placed in the tables.
Following
this recommendation will make your application more object-oriented because
most functions that manipulate data in a specific table will be defined
directly in the table itself.
FormIDs on Tables
Remember
to set the LookupFormID and DrillDownFormID properties on most tables. You
cannot anticipate when a user will need to be able to activate a Lookup or DrillDown
button – for example, if someone makes a report with a filter tab on the table,
the Lookup button on the filter tab will not appear unless the LookupFormID property
is set on the table.
Calculating Totals in Reports
It is
important not to calculate manually a total that you want to show in a TransportHeader
or TransportFooter (because you never know whether the TransportHeader or
TransportFooter will be printed before the current record or after it).Therefore,
use the CurrReport.CREATETOTALS function to calculate totals in a report. Never
have code like Sum := Sum + Number in the
OnAfterGetRecord trigger
for a data item.
Never Stop in OnModify
The
OnModify trigger on a table should never contain code that can stop the user
from recording a change – for example, code for displaying error messages.
That is, if a user has
previously changed the contents of some fields in a record, these changes must
always be accepted by the system.
Similarly, in tables where
records are entered in forms having the property DelayedInsert=Yes, any code in
an OnInsert trigger should always succeed. This applies to journal lines, for
example.
Placing Fields on Forms
Navision
Attain provides you with several features for creating forms that are both useful
and attractive.
If you use
tab controls on card forms, you can show many fields without giving a cluttered
impression. And on tabular forms, the facility of hiding/showing columns lets you
hide many fields that are still included - the user can easily see the fields
when needed.
These
features make it possible to demonstrate most of the functionality of Navision Attain
and thus ensure a match between the program and the market segment.
This
section contains guidelines for which fields to include on forms in Navision
Attain and in which order. You will find a section about card forms and a
section about tabular forms.
For card
forms as well as tabular forms, consistency is important. Similar forms in the application
areas must be composed the same way.
Card Forms
Some card
forms are related to a table that contains only a few fields. It is not hard to
create such forms because it is often obvious how to select and place the
fields.
Most card
forms, however, are related to tables with many fields. It can be difficult to create
these forms, so the guidelines concentrate on them.
Many forms
use several tab controls. How many tabs are needed and what to call the tabs
are specific to each form. Two tabs are often used: "General" as the
first (and maybe only) tab and "Invoicing" or "Posting" as
the second (sometimes third) tab.
All
relevant fields must be included on a card form. Even card forms with many tabs
have a limited space for fields, so you have to consider relevancy carefully.
Which fields to include depends on the purpose of each form. Please note:
·
Dimensions (such as department, project or location) must always
be included.
·
Do not include fields that are automatically filled in and do not
normally need to be changed by the user.
·
Do not place the same field twice on a form - not even on
different tabs.
·
If two or more fields are related according to source or function,
you should group
them.
Where to place a field also
depends on the specific form. Some tabs and fields are used on many forms,
however. For the sake of consistency, please use the location listed here -
unless it is very inappropriate for some reason
Tab Field Place
General No., Name,
other information about The left
column starting from the top.
the account.
Search Name The top of the right column.
Blocked Last Date Modified The last fields in the right column.
Posting or Invoicing General Business Posting Group The
top of the right column. The
General Product Posting Group fields should be grouped together.
Posting group from the actual The top of the right column (though
application area. after any general posting groups).
Department Code The top of the right column (though
Project Code after any posting groups). The fields
should be grouped together.
Tabular Forms
In
general, all fields are included on tabular forms. Some exceptions are
mentioned below. The fields are shown or hidden depending on how relevant they
are and what the layout of the form is.
You must consider the
following points when you create tabular forms:
·
Dimensions (such as department or project) must always be
included. The fields should normally be hidden.
·
FlowFields are calculated even when they are hidden. Therefore, do
not include FlowFields on tabular forms (unless the form is seldom used or the
field is essential).
·
Including more than about 25 fields on a form will affect
performance. Therefore, use the possibility of "including all fields but
hiding most" very carefully. Because performance considerations, tabular
forms should not include fields that may be informative but cannot be changed -
Posting Group, Journal Name, Weights and Source Type, for example.
·
Never include fields that are used internally in the program, such
as Closed by Entry No.
Tabular
forms are used for all the forms in the Setup menu. Creating these forms does not
typically cause problems because they often contain only a code and a few information
fields.
Tabular
forms like journals, sales/purchase lines and ledgers are more difficult to create
and maintain properly because the related tables contain a lot of functionality
and many fields. Which fields to use on a form (and in which order) can be hard
to decide. In W1 the same template is used to compose these forms so that they
look similar. Below is the template.
The template
is divided into sections according to functionality. In each section, the most
common field names are mentioned. Please note that the template does not include
all functionality in W1 and that in certain cases in W1 the order indicated in
the template has not been followed.
Section Example
of Fields
Dates Posting
Date
Document Date
Document Entry
Type
Document Type
Document No.
No. (of Account)
Posting Description
Dimensions Department
Code
Project Code
Business Unit Code
Location Code
Salesperson/Purchaser Code
Work Type Code
Phase Code
Task Code
Step Code
Currency Currency
Code
Exchange Rate
General Posting Setup General
Posting Type
General Business Posting Group
General Product Posting Group
Quantity Quantity
Invoiced Quantity
Remaining Quantity
Unit of Measure Code
Prices/Cost Direct
Unit Cost
Indirect Cost %
Unit Cost
Total Cost
Profit %
Unit Price
Total Price
Price Group Code
Chargeable
The exact name and order depend on the application area.
Amounts Amount
Amount Including VAT
VAT Amount
Remaining Amount
Amounts in LCY must follow each
amount type.
Balancing Account Balancing
Account Type
Balancing Account No.
Balancing General Posting Type
Balancing General Business
Posting Group
Balancing General Product Posting
Group
Sales/Purchase and Sales/Purchase
(LCY)
Discount Profit
(LCY)
Line Discount %
Line Discount Amount
Allow Invoice Discount
Invoice Discount Amount
Payment Terms Payment
Terms Code
Due Date
Payment Discount Date
Payment Discount %
Application Serial
No.
Applies-to Document Type
Applies-to Document No.
Applies-to ID
Applies-to Item Entry
Applies-to Entry
Apply and Close (Job)
Open
Miscellaneous Information Cost
Is Adjusted
Cost Posted to G/L
On Hold
Bank Payment Type
Intrastat Transaction
Type
Transport Method
Country Code
Posting Information Quantity
to Ship
Quantity Shipped
Quantity to Invoice
Quantity Invoiced
Audit Information User
ID
Source Code
Reason Code
Entry No.
1.5 USER-DEFINED FUNCTIONS
When to Create New Functions
Do not
move a piece of code to a new function unless the code is non-trivial and the new
function is afterwards called from more than one place.
When to Create Parameters for a Function
Create for
a function only those parameters that are necessary for it to operate on different
data – depending on where it is called from. If a function also changes a global
variable, however, you can transfer the global variable to the function to indicate
that the function will change this variable.
When to Create Local Variables for a Function
You can
use additional local variables in the same way that you would use them in other
development languages and environments. When you create a user-defined
function, set the property Local=Yes unless you
actually want to access the
function from outside the object.
1.6 USER MESSAGES
When you write messages
users will see, follow these guidelines:
·
Write the messages as correctly as possible according to the
guidelines for your national language. This is the most important rule to
follow.
·
When you write a message that is similar to one in the ETX file,
phrase it to be as similar to the ETX message as possible. This will make
messages consistent throughout the system.
·
Do not use backslashes to indicate line breaks in a message.
Windows will usually do the line formatting. In Dialog.OPEN, however, you must
use backslashes in order for the message to be aligned correctly.
·
Use the C/AL functions FIELDNAME and TABLENAME whenever possible
so the user can always recognize a term that indicates a field or table name.
The only exception to this
is in Dialog.OPEN. Here you can use the field name directly; otherwise it may be
difficult to align correctly. If you refer to a field name without using
FIELDNAME, type the field name without any single or double quotation marks.
·
Try to write all messages (and other sentences) on one line only.
If you need to use more than one line, try to start each new line after a
period rather than in the middle of a sentence.
·
Do not enter the text directly in the C/AL code. Instead, you must
enter it as a text constant so that the message can be translated.
Note
There is
no naming convention for the text constants. However, you can do as the examples
describe.
ERROR, FIELDERROR
The
message must describe what is wrong and also how to solve the problem. Write a short
descriptive message; do not use more words than necessary.
Always end ERROR with a
period. A period is automatically inserted at the end of FIELDERROR. After
ERROR, enter a reference to the text constant and create the text constant in
the C/AL Globals window.
For more information, see
the manual Application Designer’s Guide.
EXAMPLE
In the C/AL Editor, enter
the following:
ERROR(
Text1000);
Then, in the C/AL Globals
window, enter the message as a text constant with name
value=Text1000 and ConstValue=%1 must be specified.
Or:
IF FileName
= ' ' THEN
ERROR(Text1001);
where the ConstValue of the
text constant is:
................. %1 ... %2.',FIELDNAME(Field1),Field1);
Tell the user when he or
she must or cannot do something, so it is clear what action
must be taken.
EXAMPLE
ERROR(Text1002)
ConstValue=You cannot...
EXAMPLE
ERROR(Text1003)
ConstValue=You
must enter the ...
Use the present rather than
the past tense.
EXAMPLE
ERROR(Text1004)
ConstValue=There
is nothing to post.
MESSAGE
Always end
MESSAGE with a period.
Supply the user with a
message when the system has finished doing something. Write the message in the
past tense.
EXAMPLE
In the C/AL Editor, enter
the following:
MESSAGE(Text1000);
Then, in the C/AL Globals
window, enter the message as a text constant with name
value=Text1000 and ConstValue=The journal lines were successfully posted.
CONFIRM
Always end CONFIRM messages
with a question mark.
EXAMPLE
IF NOT
CONFIRM(Text1010) THEN
EXIT;
ConstValue=Do you want to continue?
Dialog.OPEN
Use Dialog.OPEN only to
indicate that the system is doing something. If possible, use a progress
indicator. Enter the actual message as a text constant as mentioned above.
When you enter the message,
use the active voice. (For example, say “Processing items” rather than “Items
are being processed.”)
End a message statement
with an ellipsis (three periods) to indicate that the system is doing
something.
Use two backslashes at the
end of a line in a message to insert extra space before the next lines. These
subsequent lines (if there are any) tell what is actually being processed.
The #-fields are aligned to
the left with at least one space character between the longest text and #.
There is also at least one space character between the #- and @- fields.
The text before #- and
@-fields must always span at least 16 characters, including a final space
character. This means that you will need to add extra space characters if your
text is less than 15 characters long.
EXAMPLE
Window.OPEN(Text1011);
ConstValue=Processing items...
EXAMPLE
Window.OPEN(Text1012)
ConstValue=Processing items...\\Account 123456\Date010101
EXAMPLE
Window.OPEN(Text1012)
ConstValue=Batch Name 123456\\Checking lines #2######
@5@@@@@@@@@@@@@\Checking balance #3######
@5@@@@@@@@@@@@@\Posting lines #4######
@5@@@@@@@@@@@@@
The character lengths for
the # and @-fields are displayed in the table below.
Data Type Field Length
Boolean 8
Code 12
Date 8
Decimal (default) 12
Decimal (percentage) 8
Integer 8
Option Field 12
Progress Indicator 15
Text 25
Time 8
Table Access
If it is necessary to
change the key before accessing a table in the database, first set the correct
key, then set the correct filters, and finally, access the table.
Put only the necessary key
fields in a call of SETCURRENTKEY. That is, if the table order is not important, use only
the fields that are used in the subsequent calls of SETRANGE and SETFILTER.
This makes it possible to change the definition of the key (as long as it still
includes the fields mentioned in the call of SETCURRENTKEY – in the order
given) without having to change any code.
EXAMPLE
Rec.RESET;
Rec.SETCURRENTKEY(Field1,Field2);
Rec.SETRANGE(Field1,x);
Rec.SETRANGE(Field2,y);
Rec.FIND('-');
In the example, a possible
key is Field 1, Field2, Field 3. Without changing the code
above, the key could be
changed to Field1, Field3, Field2.
TABLE LOCKING
To avoid
deadlock and maintain consistency in the database, certain rules must be observed.
The Navision Attain database has a deadlock protection that prevents the entire
system from locking up.
Locking Orders
To prevent
individual users from experiencing deadlock, however, certain tables must be
locked in a specific order. In the base application, there are four main groups
of tables to consider:
·
Journals
·
Non-posted lines and headers
·
Posted lines and headers
·
Ledger entries and registers
Journals
The main rule in Navision
Attain is always to lock tables on the lowest level first. A journal has three
levels: templates are on the highest level and lines on the lowest. Batch names
are in between. When a journal template is to be deleted, the application will
first delete the journal lines and thereby implicitly lock them. It will then
repeat the process with the batch names. Finally, the template can be deleted.
The rule results in
this locking order:
1 Journal
line
2 Batch name
3 Journal
template
Non-Posted Lines and Headers
Because
database consistency in the application is very important, there is another rule
that must be followed when sales lines are locked before the corresponding
sales header. When a user tries to insert a new sales line, the Sales Line table is automatically
locked. Another user cannot delete the sales header at the exact same time,
because the sales lines have to be deleted before a sales header can be deleted.
Because of this, there will never be sales lines without a corresponding sales header.
The same is true for purchase headers and lines. The locking order is as follows:
1 Sales Line table/Purchase Line table
2 Sales Header table/Purchase Header table
Posted Lines and Headers
You must also respect a
locking order when working with both posted headers and lines and with headers
and lines that are not yet posted. The main principles are that posted headers
are locked before posted lines, and purchase tables are locked before sales tables. Posted tables are locked before
non-posted tables. These principles result in the following locking order:
1 Purch. Rcpt. Header
2 Purch. Rcpt. Line
3 Sales Shipment Header
4 Sales Shipment Line
5 Sales Invoice Header/Sales Cr. Memo Header/Purch. Inv. Header/
Purch. Cr. Memo Hdr.
6 Sales Invoice Line/Sales Cr. Memo Line/Purch. Inv. Line/
Purch. Cr. Memo Line
7 Purchase Line
8 Purchase Header
9 Sales Line
10Sales Header
Ledger Entries and Registers
A ledger entry table must
always be locked before its corresponding register table, for
example, G/L Entry before G/L Register.
1.8 PUTTING FUNCTIONS IN OBJECTS
When you write a function,
you will often need user input such as the value, key or filter of a table
record in a form. In these cases, attach the table to a codeunit and put the
function in the OnRun trigger, so that the function can be activated directly
from a form using the Property Sheet. That is, unless it is absolutely
necessary, don’t force people to write C/AL code in order to use your objects
when they design forms.
If you want to provide the
user with more filtering and sorting possibilities or other option settings
before starting a function, use a report object (with the property ProcessingOnly=Yes),
unless a single call of CONFIRM or STRMENU to ask for user information will do.
No comments:
Post a Comment