Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Save more on your purchases! discount-offer-chevron-icon
Savings automatically calculated. No voucher code required.
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Newsletter Hub
Free Learning
Arrow right icon
timer SALE ENDS IN
0 Days
:
00 Hours
:
00 Minutes
:
00 Seconds

Introduction to Microsoft Dynamics NAV 2016

Save for later
  • 540 min read
  • 2017-02-02 00:00:00

article-image

In this article by Alexander Drogin, author of the book, Extending Microsoft Dynamics NAV 2016 Cookbook, we will see the basic introduction to Microsoft Dynamics NAV 2016 and C/AL programming.

(For more resources related to this topic, see here.)

Microsoft Dynamics NAV 2016 has a rich toolset for extending functionality. Besides, a wide number of external tools can be connected to NAV database to enrich data processing and analysis experience. But C/AL, internal NAV application language, is still the primary means of enhancing user experience when it comes to developing new functionality.

C/AL Development is framed around objects representing different kinds of functionality and designers associated with each object type.

Basic C/AL programming

As an example, Let us create a simple function that receives a checksum as a parameter and verifies if a the check sum satisfies given criteria.

It is a typical task for a developer working on an ERP system to implement verification like Luhn algorithm that is widely used to validate identification numbers, such as a credit card number.

How to do it...

  1. In the Object Designer window, create a new codeunit. Assign a number and name (for example, 50000, Luhn Verification).
  2. Click View, then C/AL Globals, in the Globals window open the Functions tab.
  3. In the first line of the table enter the function name: SplitNumber.

Verification function receives a BigInteger number as an input parameter, but the algorithm works with separate digits. Therefore, before starting the validation we  need to convert the number into an array of digits.

  1. Position into the function Split number you just declared and click  View, then C/AL Locals. First tab in the Locals window is Parameters. Enter the first parameter of the function:
  • Name: Digits
  • DataType: Byte
  • Var: Set the checkmark in this field
  1. Still in the C/AL Locals window, click View, Properties to open the variable properties and set Dimensions = 11
  2. Close the variable properties window and add the second function parameter AccountNumber with type BigInteger.

    Parameters window should now look like this:

    introduction-microsoft-dynamics-nav-2016-img-0

  3. Next, navigate to the Variables tab. Insert a variable i of Integer type.
  4. Close the local declarations window to return to the code editor and type the function code
    FOR i := 11 DOWNTO 1 DO BEGIN
      Digits[i] := AccountNumber MOD 10;
      AccountNumber := AccountNumber DIV 10;
    END;
    
  5. Open the C/AL Globals again and insert the second function VerifyCheckSum. This is the main function that implements the verification algorithm.
  6. In the C/AL Locals window, insert a single parameter of this function AccountNumber of type BigInteger.
  7. Navigate to the Return Value tab and fill in the Return Type field. In this case type should be Boolean.
  8. In the C/AL Locals window, declare three local variables as follows:

    Name

    Data Type

    Digits

    Byte

    CheckSum

    Integer

    i

    Integer

  9. Select the Digits variable, open its properties and set Dimensions to 11.
  10. Type the function code:
    SplitNumber(Digits,AccountNumber);
    FOR i := 1 TO 10 DO BEGIN
      IF i MOD 2 = 1 THEN
        CheckSum += (Digits[i] * 2) DIV 10 + 
            (Digits[i] * 2) MOD 10;
      END;
    
      CheckSum := 10 - (CheckSum * 9) MOD 10;
    
      EXIT(CheckSum = Digits[11]);
    
  11. In the OnRun trigger, place the code that will call the verification function:
    IF VerifyCheckSum(79927398712) THEN
        MESSAGE(CorrectCheckSumMsg)
      ELSE
        MESSAGE(WrongCheckSumMsg);
    
  12. To present the execution result to the user, OnRun uses two text constants that we have not declared yet. To do it, open C/AL Globals in the View menu. In the Text Constants tab, enter the values as shown in the following table:

    Name

    Value

    CorrectCheckSumMsg

    Account number has correct checksum

    WrongCheckSumMsg

    Account number has wrong checksum

How it works...

Function SplitNumber described in Step 1 through 8 uses a FOR..DOWNTO loop with a loop control variable to iterate on all digits in the BigInteger number, starting from the last digit. In each step the number is divided by 10 using integer division function DIV. Modulus division function MOD returns the remainder of this division that is placed in the corresponding element of an array.

The Dimensions property of the parameter Digits tells that this variable is an array consisting of 11 elements (value of Dimensions is the number of elements. Variable with undefined dimensions is a scalar).

When a function is called, it can receive arguments either by value or by reference. Var checkmark in the parameter declaration means that the argument will be passed to the function by reference, and all changes made to the Digits array in the function SplitNumber will be reflected in the function VerifyCheckSum that calls SplitNumber. Arrays cannot be function return values in C/AL, so passing an array variable by reference is the only way to send arrays between functions.

Function VerifyCheckSum defined in Step 9 to 13 calls the helper function SplitNumber and then uses the same loop type, but iterates from the first digit to the last (FOR 1 TO 10). This loop computes the checksum, which is compared to the last digit of the account number. If the two values match, checksum is correct. Finally, the function returns the Boolean value conveying the verification result, TRUE or FALSE.

Based on this result, the OnRun function in the codeunit will display one of the two text constants in a message. In the given example checksum is incorrect, so the message will look like this:

introduction-microsoft-dynamics-nav-2016-img-1

To see the message for correct result, replace the last digit in the account number with 3. Correct number is 79927398713.

Accessing the Database in C/AL

Microsoft Dynamics NAV is an information system, and its primary purpose is to collect, store, organize and present data. Therefore C/AL has a rich set of functions for data access and manipulation.

Next example will present a set of basic functions to read data from the NAV database, filter and search records in a table and calculate aggregated values based on database records.

In this example suppose we want to calculate the total amount in all open sales orders and invoices for a certain customer.

Unlock access to the largest independent learning library in Tech for FREE!
Get unlimited access to 7500+ expert-authored eBooks and video courses covering every tech area you can think of.
Renews at $19.99/month. Cancel anytime

How to do it...

  1. In the NAV Object Designer, create a new codeunit object.
  2. Open the codeunit object you just created in code designer, position in the OnRun trigger and open local declarations window (C/AL Locals). Declare local variables:

    Name

    DataType

    Subtype

    SalesLine

    Record

    SalesLine

    StartingDate

    Date

     

    EndingDate

    Date

     

  3. Close the local variables window and declare a global text constant in the C/AL Globals window:

    Name

    ConstValue

    SalesAmountMsg

    Total amount in sales documents: %1

  4. Return to the code editor and type the function code:
    StartingDate := CALCDATE('<-1M>',WORKDATE);
    EndingDate := WORKDATE;
    
    SalesLine.SETRANGE("Sell-to Customer No.",'10000');
    SalesLine.SETFILTER(
      "Document Type",'%1|%2',
      SalesLine."Document Type"::Order,
      SalesLine."Document Type"::Invoice);
    SalesLine.SETRANGE("Posting Date",StartingDate,EndingDate);
    SalesLine.CALCSUMS("Line Amount");
    MESSAGE(SalesAmountMsg,SalesLine."Line Amount");
    
  5. Save the changes, then close the code editor and run the codeunit.

How it works...

Record is a complex data type. Variable declared as record refers to a table in the database. Variable contains a single table record and can move forward and backward through the record set. C/AL record resembles an object in object-oriented languages, although they are not exactly the same. You can call record methods and read fields using dot notation.

For example following are valid statements with customer record variable:

Customer.Name := 'New Customer';
IF Customer.Balance <= 0 THEN
  MESSAGE

Variable we just declared refers to the table Sales Line which stores all open sales documents lines.

Since we want to calculate sales amount in a certain period, first of all we need to define the date range for the calculation.

First line in the code example finds the starting date of the period. In this calculation we refer to the system-defined global variable WORKDATE. If you are an experienced NAV user, you know what a workdate is – this is the default date for all documents created in the system. It does not always match the calendar date, so in the application code we use workdate as the pivot date. Another system variable TODAY stores the actual calendar date, but it is used much less frequently than workdate.

Workdate is the last date of the period we want to analyze. To find the first date, use the CALCDATE function. It calculates a date based on the a formula and a reference date. CALCDATE('<-1M>',WORKDATE) means that the resulting date will be one month earlier than the workdate. In the NAV 9.0 demo database workdate is 25.01.2017, so the result of this CALCDATE will be 25.12.2016.

Next line sets a filter on the SalesLine table. Filtering is used in C/AL to search for records corresponding given criteria. There are two functions to apply filters to a table: SETFILTER and SETRANGE. Both take the field name to which the filter is applied, as the first parameter.

SETRANGE can filter all values within a given range or a single value. In the code example we use it to filter sales lines where the customer code is '10000'. Then we apply one more filter on Posting Date field to filter out all dates less than StartingDate and greater than EndingDate.

Another filter is applied on the Document Type field.

SalesLine.SETFILTER(
  "Document Type",'%1|%2',
  SalesLine."Document Type"::Order,
  SalesLine."Document Type"::Invoice);

We want to see only invoices and orders in the final result, and we can combine these two values in a filter with the SETFILTER function. '%1|%2' is a combination of two placeholders that will be replaced with actual filter values in the runtime.

The last database statement in this example is the CALCSUMS function. SETRANGE itself does not change the state of the record variable – it only prepares filters for the following records search or calculation. Now CALCSUNS will calculate the result based on the record filters. It will find the sum of the Line Amount field in all records within the filtered range.

Only sales lines in which all filtering conditions are satisfied, will be taken into account:

  • Customer No is '10000'
  • Document Type is Order or Invoice
  • Posting Date is between 25.12.2016 and 25.01.2017

Finally, we will show the result as a message with the MESSAGE function. Placeholders %1 in the message text will be replaced with the second parameter of the function (SalesLine."Line Amount"):

introduction-microsoft-dynamics-nav-2016-img-2

Summary

So in this article we covered the introduction of Microsoft Dynamics NAV 2016 and basics of C/AL programming.

Resources for Article:


Further resources on this subject:


Modal Close icon
Modal Close icon