Pages

Tuesday, 11 June 2013

Microsoft dynamic NAV C/AL Programming Guide


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