0% found this document useful (0 votes)
13 views336 pages

Na Development

This document provides an overview of development guidelines and methodologies for Navision a/s software, including information on complex data types, coding practices, object analysis, and the upgrade process. It emphasizes the importance of adhering to copyright laws and licensing agreements while using the material. The document also includes a detailed table of contents outlining various chapters and labs related to programming and data management within the Navision environment.

Uploaded by

joleb32867
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
13 views336 pages

Na Development

This document provides an overview of development guidelines and methodologies for Navision a/s software, including information on complex data types, coding practices, object analysis, and the upgrade process. It emphasizes the importance of adhering to copyright laws and licensing agreements while using the material. The document also includes a detailed table of contents outlining various chapters and labs related to programming and data management within the Navision environment.

Uploaded by

joleb32867
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 336

Development

.
Development
NOTICE

This material is for informational purposes only. Navision a/s disclaims all
warranties and conditions with regard to use of the material for other
purposes. Navision a/s shall not, at any time, be liable for any special, direct,
indirect or consequential damages, whether in an action of contract,
negligence or other action arising out of or in connection with the use or
performance of the material. This material is subject to change without
notice.

According to Danish copyright legislation it is against the law to reproduce


any part of this material in any form or by any means without the permission
of Navision a/s.

The software described is supplied under license and must be used and
copied in accordance with the enclosed license terms and conditions.

COPYRIGHT NOTICE

Copyright  2001 Navision a/s, Frydenlunds Allé 6, DK-2950 Vedbæk,


Denmark or Copyright  2001 Navision Development a/s, Bregnerødvej 133,
3460 Birkerød, Denmark. All rights reserved.

TRADEMARKS

The trademarks referenced herein and marked with either TM or  are either
trademarks or registered trademarks of Navision a/s or Navision
Development a/s. However, the trademarks Microsoft, Windows, Windows
NT, SQL Server and BackOffice are either registered trademarks or
trademarks of Microsoft Corporation in the United States and/or other
countries.

Any rights not expressly granted herein are reserved.

The trademarks of Navision a/s and Navision Development a/s are listed on
this Web site: http://trademarks.navision.com

The MetaPlus font was used.

Published by Navision a/s.

Published in Denmark 2001.


Table of Contents

Table of Contents

Chapter 1 Complex Data Types

1.1 What is a Complex Data Type?

1.2 Using the Objects You Know

1.3 What About the Table Data Type?

1.4 Member Functions of Record Data Types

1.5 Using Versions, Transactions and Optimistic


Concurrency

Chapter 2 Coding in Tables, Reports and Dataports

2.1 What is a Trigger?

2.2 Table and Field Event Triggers

2.3 Report Event Triggers

2.4 Special Report Functions and Where They Belong

2.5 Processing Only Reports

2.6 Dataports Event Triggers

Chapter 3 Object Analysis

3.1 Exporting Objects as Text

3.2 Editing an Object Using an External Text Editor

3.3 Changing the Text File

3.4 Object Analysis with Developers Toolkit

Chapter 4 Development Methodology

4.1 Implementation Methodology

4.2 Version Control

4.3 Development Documentation

i
Table of Contents

Chapter 5 Posting Routines

5.1 What is the Difference between a Journal and


a Ledger?
5.2 Standard Journal to Ledger Posting Routines

5.3 The Main Three Codeunits

5.4 The Theory behind Document Posting

5.5 The Codeunits used in Document Posting

Chapter 6 Data Conversion

6.1 Transaction Imports

6.2 Importing Documents

6.3 File Handling in Navision

Chapter 7 Coding in forms

7.1 Form Triggers

7.2 Triggers for Rec

7.3 Control Trigger

7.4 Special Form and Control Functions

Chapter 8 Low Impact Programming

8.1 Low Impact on the Application

8.2 Low Impact on the Server

8.3 Low Impact on the Network

8.4 Low Impact on the Database

ii
Table of Contents

Chapter 9 The Upgrade Process

9.1 Why Upgrade

9.2 Definitions

9.3 Types of Upgrades

9.4 Planning for an Upgrade

9.5 Upgrading the Executables

9.6 Upgrading the Objects.

9.7 Tools for Upgrading the Objects

9.8 What If?

9.9 Upgrading the Data

Lab 1 Complex Data Types

1.1 Running Objects

1.2 Using Dialogs

1.3 Getting Started With Record Variables

1.4 Using Streams

Lab 2 Coding in Tables, Reports and Dataports

2.1 Coding in Table and Field Event Triggers

2.2 Coding in Report Event Triggers

2.3 Coding in Dataport Event Triggers

Lab 3 Object Analysis

3.1 Exporting and Changing as Text

3.2 Renumbering Objects

3.3 Analysis with impuls Workbench

Lab 4 Development Methodology

4.1 Internal Documentation

4.2 External Documentation

4.3 Setting the Version List

4.4 Completing Design Specifications

iii
Table of Contents

Lab 5 Posting Routines

5.1 Getting Ready

5.2 Creating Check Line

5.3 Creating Post Line

5.4 Creating Post Batch

5.5 Creating a Starter Routine – Post

5.6 Changing a Document Posting Routine

Lab 6 Data Conversion

6.1 Looking at the Import File

6.2 Debugging the Journal

6.3 Building the Transaction Dataport

6.4 Testing the Transaction Dataport

6.5 Changing the Dataport to Post Directly

6.6 Another Exercise

Lab 7 coding in Forms

7.1 Creating a Menu

7.2 Integration with the Main Menu

7.3 Creating a Statistics Form

7.4 Using a Matrix Box

7.5 Using the Hyperlink triggers

Lab 8 Property Management

8.1 Introduction

8.2 Concept Document

8.3 Design Specifications

Lab 9 The Upgrade Process

9.1 Upgrading a Customized Database

iv
CHAPTER 1
COMPLEX DATA TYPES

This chapter introduces the complex data types


found in C/SIDE. It helps the student focus on the
most used complex data types with special
emphasis on the record data type.

This chapter will cover the following sections:

1.1 What is a Complex Data Type?


1.2 Using the Objects You Know
1.3 What About the Table Data Type?
1.4 Member Functions of Record Data Types
1.5 Using Versions, Transactions and Optimistic
Concurrency
Complex Data Types 1-2

1.1 What is a Complex Data Type?

Complex data types are those that have more than just a value and
operators. Simple data types like integers have a value. You can add or
subtract integers to get new values and so on. Complex data types,
however, have properties or methods along with a value (or values). For
instance, a form data type has a value (the form object), but it also has
methods like the RUN method that displays the form to the user. It also has
properties like LOOKUPMODE.

Remember that Arrays of any type are also called complex, but Arrays are
complex variables not complex data types. A Complex Variable is a
variable that has a complex data type or is an array (has one or more
dimensions defined).

Complex Data Types

Automation Allows access to an external Application’s Object


interface.

BLOB Can only be used as a Field data type.

Codeunit Allows access to a Codeunit object.

Dataport Allows access to a Dataport object.

Dialog Used to display a status window to the user (includes a


Cancel button)

File Allows access to external files.

Form Allows access to a Form object.

InStream Allows streaming of data out of Files and Blobs. This


datatype is also useful when working with Automation
and OCX datatypes.

OCX Allows access to an external OCX control (only non-


visual OCX controls are supported.

OutStream Allows streaming of data into Files and Blobs. This


datatype is also useful when working with Automation
and OCX datatypes.
Complex Data Types 1-3

Record Allows access to Table Data.

Report Allows access to a Report object.

Variant Can hold any C/AL data type and can be assigned to and
from any C/AL data type. You can use the variant data
type to pass Automation variants from one external
component (Automation or OCX) to another.

Note

You will not be learning about OCX, Automation, BLOB or File in this
section. These data types have already been covered or will be covered by
other sections.
1-4 Applied Programming in CSIDE

1.2 Using the Objects You Know

In this section, you will learn how to use the objects you already understand
(forms, reports, dataports and codeunits) within C/AL.

All of these data types have one method in common, the RUN method. You
will use this method and several others in this section.

Accessing Codeunits

As reviewed in the basic C/AL section, a codeunit in Navision can be used


to hold functions that are common to many other objects. An example
would be the NumberSeriesManagement codeunit. It holds functions that
allow you to manipulate Number Series such as getting the next number in
a particular series. By putting common code into Codeunits, the Developer
can take full advantage of code re-use.

You can access the code in a codeunit from other objects in several ways:

Using a Codeunit Variable

By creating a codeunit variable, you have an instance of the codeunit


object. As long as that instance exists, it maintains the values of its global
variables. This is one reason why you would choose this method. The other
reason is more obvious. This is the only way of accessing the functions
within a codeunit.

To use a codeunit variable:

1 Create a variable of type codeunit

2 In the subtype field, put the name or ID of the codeunit you want to
access.

3 Within C/AL, you can call the RUN method of the codeunit or a function
within the codeunit. You can pass a record variable into the RUN
method if the OnRun trigger will accept one.

Examples:

CodeunitVariable.RUN; //No record variable


CodeunitVariable.RUN(Rec); //With record variable
X := CodeunitVariable.AddTen(9001); //Calling a function
Complex Data Types 1-5

Using the CODEUNIT.RUN Function

By using the CODEUNIT.RUN function to create and run a codeunit, you


dynamically create an instance of a codeunit based on the ID. Since the
instance is created, ran and destroyed in this one statement, there is no
codeunit variable to worry about, and the destruction of the codeunit clears
all of its variables as well. The other benefit to using this method is that you
can create any codeunit based on the ID. So, this is a dynamic creation.

The biggest drawback to this function is that you can only run the codeunit.
You can still pass in a record variable, but you cannot call a function within
the codeunit.

To run a codeunit using CODEUNIT.RUN:

. Call the run method of the codeunit using the global codeunit object.

. You must pass in the ID of the codeunit that you wish to run.

. You can pass a record variable if the OnRun trigger of that codeunit will
accept one.

Example:

CODEUNIT.RUN(393); //No record variable


CODEUNIT.RUN(393, Rec); //With record variable

Accessing Forms

Forms can be run in much the same way as codeunits, with a form variable
or using the FORM.RUN function. However, there is another way to run
forms. You can run them modally using the RUNMODAL method instead of
RUN.

Examples (running a form non-modally)


CustomerCardVariable.RUN;
FORM.RUN(FORM::”Customer Card”, CustRec);

Examples (running a form modally)

By running a form modally, you can check what button the user pushed to
close the form. In this example, the customer list form is run modally and if
the user pushed the OK button, a variable is changed.

IF FORM.RUNMODAL(22,CustRec) = Action::LookupOK THEN


CustNo := CustRec.”No.”;
1-6 Applied Programming in CSIDE

Accessing Reports and Dataports

As with forms, reports and dataports can be run with a variable, without a
variable, modally or non-modally.

Examples
ReportVariable.RUN;
REPORT.RUN(ReportID);
ReportVariable.RUNMODAL;
DataportVariable.RUN;
DATAPORT.RUN(DataportID);
DataportVariable.RUNMODAL;

For more information about running a report or dataport, see the online
help pages for the above functions.

Activity

In this activity, you will create a codeunit that runs two reports and then
calls that codeunit from a button on a form.

1 Create a codeunit, with ID 99100 and Name ReportRunTest.

2 Create a function in the codeunit called Go.

3 In that function, write code that will run report 5201- Employee List (run
this report using a variable) and 5200- Employee Labels (run this
report without using a variable).

4 Go to the Employee form (Form 5200) and add a menu item to the
Customer menu button that will call the function in the codeunit you just
wrote.

5 Test your new functionality.


Complex Data Types 1-7

1.3 What About the Table Data Type?

You may have noticed that there was one very important object missing
from the discussion in the last section – Tables. You may be wondering if
you can run tables from code, or better yet insert records. Well, there is no
Table data type that you can create in Navision. But you can access tables
through record data types. These variables are called record variables.

Record variables are the guards that protect the data. They are used to
access data, change and manipulate the data. There are a few concepts
that you must keep in mind when working with record variables:

. The record variable is a place in the memory for ONE record from the
associated table.

. The record variable is also “conscious” of the entire set of records


contained in the table.

. The record variable is NOT the record in the table, or the table itself.

. No table data can be accessed except through a record variable.

The other objects throughout C/SIDE use record variables to access or


change data in a table. Here are some examples:

. Forms use a Record Variable called Rec (table objects themselves also
use a record variable called Rec).

. Reports and Dataports use DataItems that are actually record variables.

. In code, one can create Global or Local Variables that are of type
Record.

Record variables have fields just like the table that they access. For
example, a record variable with a subtype of Customer would have fields
like No., Name, Address, City and so on. But changing the fields of a
record variable does not change the record in the database. You will learn
how to change the database in the next section.
1-8 Applied Programming in CSIDE

1.4 Member Functions of Record Data Types

You cannot run a record variable, but there are many other member
functions that you must master in order to really use record variables
successfully.

Retrieving a Record

In order to retrieve a record or navigate to the next record in a record set,


you must use one of the following method functions. These functions allow
the developer to search for specific records depending on the criteria
entered or move forward or backwards in the record set.

GET

The most basic way to retrieve a record in C/SIDE is with the GET method.
This method uses the primary key values to find the matching record in the
database. Once the record is found, it sets the record variable’s fields to
the values of the fields in the database. Afterward, you have a copy of the
database record in the record variable.

The GET method ignores the record set and goes directly to the table to
retrieve the record. If unsuccessful, an error occurs. You can trap the error
by looking at the return value of the function.

Examples

Customer.GET('50000'); //No. is the primary key field


ItemUnitOfMeasure.GET('70000', 'PCS'); //Compound Primary key
Customer.GET(CustNo); //Using a variable
Customer.GET(Rec.”No.”); //Using a field from another
// Record Variable

//Avoiding the error if it fails


IF Customer.GET(CustNo) THEN ...

FIND

The FIND method is another important way of retrieving records from the
database. It is used to go to a specific record in a record set. Usually this
function is just used to go to the first or the last record within a record set,
but you can use it for searching as well.

An important difference between GET and FIND is that FIND respects (and
is limited by) the current setting of filters. Additionally, FIND can be
instructed to look for records where the key value is equal to, larger than or
smaller than the search string. The parameter for find is a string that can be
Complex Data Types 1-9

any one of the following: +, -, =, <, or >.

Find(‘+’) goes to the bottom of the table and sets the record variable to the
last record. Find(‘-’) goes to the top of the table and sets the record variable
to the first record. If you use =, <, or >, FIND will take into account values
currently stored in the record variable fields which are included in the
current key.

If unsuccessful, an error occurs. You can trap the error by looking at the
return value of the function.

Examples

SalesOrderLine.SETRANGE("No.","No."); //Filter the record set


SalesOrderLine.FIND('-'); //Find the first

Customer.SETCURRENTKEY("Search Name"); //Change the order


Customer.FIND('+'); //Find the last

Customer.SETCURRENTKEY("Search Name"); //Change the order


Customer."Search Name" := 'J'; //Set field
Customer.FIND('=><'); //Find the nearest

//Avoiding the error if it fails


IF Customer.FIND('-') THEN ...

NEXT

Once you have retrieved a record from the database, you may want to
retrieve the very next record or a previous record. The NEXT method
allows you to do that.

The steps parameter allows you to specify direction and number of records
to jump over. A negative number does a previous. A positive number does
a next. The default value is 1. If you enter a 3 for steps, the function will
jump over the next two records and retrieve the third one.

The NEXT method returns a zero if it could not retrieve the specified
record. If zero is returned, the record variable is not changed at all.

Examples

Customer.FIND('-'); //go to First record (10000)


Customer.NEXT; //go to Next record (20000)
Customer.NEXT(-1); //go to Previous record (10000)
Customer.NEXT(-1); //returns zero (stays on 10000)
Customer.NEXT(3); //go to the third record (40000)
Customer.FIND('+'); //go to last record
Customer.NEXT; //returns zero

//Looping thru a table


1-10 Applied Programming in CSIDE

IF Customer.FIND('-') THEN
REPEAT
//work with customer record
UNTIL Customer.NEXT = 0;

Modifying the Database Record

To change the database record that a record variable represents, you must
use one of the following functions: INSERT, MODIFY, RENAME, DELETE,
MODIFYALL, or DELETEALL.

All of these functions look at the values in the primary key fields of the
record variable to determine which record to change.

INSERT

Use this function to insert a record into a table. It takes the values of the
fields in the record variable and inserts those into the table. If you want to
run the OnInsert trigger of the table during the insert, you must pass TRUE
into the function.

If unsuccessful, an error occurs. You can trap the error by looking at the
return value of the function. This is usually not done.

Example:

//Inserting a record into a table


Customer.INIT;
Customer."No." := '4711';
Customer.Name := 'John Doe';
Customer.INSERT(True); //Calling the OnInsert Trigger

//Avoiding the error if it fails


IF Customer.INSERT THEN ...

MODIFY

The MODIFY method allows you to update the database record with the
values in the record variable that have changed. If you want to run the
OnModify trigger of the table during the modification, you must pass TRUE
into the function.

If unsuccessful, an error occurs. You can trap the error by looking at the
return value of the function. This is usually not done.

Example:

Customer.GET('4711');
Customer.Name := 'Richard Roe';
Customer.MODIFY(True); //Calling the OnModify trigger
Complex Data Types 1-11

//Avoiding the error if it fails


IF Customer.MODIFY THEN ...

DELETE

The DELETE method allows you to erase or remove the database record
identified by the primary key values in the record variable. If you want to
run the OnDelete trigger of the table during the deletion, you must pass
TRUE into the function.

If unsuccessful, an error occurs. You can trap the error by looking at the
return value of the function. This is usually not done.

After the successful deletion of the record in the database, the record
variable is not changed. It retains the same values that it had before the
deletion.

Example:

Customer."No." := '4711';
Customer.DELETE(True); //Calling the OnDelete trigger
Customer.GET(‘50000’);
Customer.DELETE; //No trigger called

RENAME

The RENAME method can be used to change the primary key fields of a
record in the database. Like the GET method, you must pass the new
primary key values as parameters to this function. It uses the values in the
primary key fields of the record variable to find the record in the database
and then uses the new values to change the record. This function works
the same as the user changing the primary key fields on a form – the
change is propagated to all related tables. The OnRename trigger is always
called.

If unsuccessful, an error occurs. You can trap the error by looking at the
return value of the function. This is usually not done.

Example:

Customer."No." := '4711';
Customer.RENAME(‘15000’); //Calls the OnRename trigger

ItemUnitOfMeasure.GET(‘70000’,’PCS’);
ItemUnitOfMeasure.RENAME(‘70000’,’PIECES’);
1-12 Applied Programming in CSIDE

Activity

In this activity, you will sort the customer table, find a record and modify the
record. First, you will perform the task as the user might do it. Then, you
will write code to accomplish the same task.

Before You Get Started

1 Create a key in the customer table that contains the “Credit Limit
(LCY)” field.

By Hand

2 Run the customer table from the object designer.

3 Using the sort button on the toolbar, change the form to sort by the
"Credit Limit (LCY)” key that you just created.

4 Using the navigation buttons on the toolbar, go to the first record and
then the last record.

5 Add 100.00 dollars to the Credit Limit of the last record.

In Code

6 Create a codeunit, with ID 99101 and Name ModfiyCustomer.

7 Create a function in the codeunit called AddToCreditLimit.

8 Create a global variable, call it Customer. The type is record and the
subtype is 18.

9 In the AddToCreditLimit function, you can type in the following code:

Customer.SETCURRENTKEY("Credit Limit (LCY)");


Customer.FIND('+');
Customer."Credit Limit (LCY)" := Customer."Credit Limit
(LCY)"
+ 100.00;
Customer.MODIFY;
Complex Data Types 1-13

Defining a Record Set

A Record Variable represents a record set from a single table. You can
easily have two or more Record Variables pointing to the same table
looking at a completely different set of records. But, how do you create a
record set? In this subsection you will learn about the functions that allow
you to change the set of records that the record variable represents.

A set of records can be referred to by the term Record Set. A Record Set is
defined by the Key and Filters that the record variable has been assigned.
This is sometimes called a “View”.

To assign a new key or filter to a Record Variable, use the method


functions SETCURRENTKEY, SETRANGE or SETFILTER. You must
understand all of these functions to effectively use record variables.

SETCURRENTKEY

The SETCURRENTKEY method assigns a new key to a record variable. All


record variables use the primary key by default. If you would like to sort the
records in a different way, you must use this method function. The new key
becomes the current key and is used by FIND, NEXT and other functions
until another key is selected or until the key is reset to the primary key.

What you pass into this function is a list of fields. The function then
searches the tables keys to find a matching key. It starts at the top of the
list of active keys and tries each one until it finds a key that works. This
may or may not be the best key. It is the first key in the list that works.

A run-time error occurs if the function cannot find a Key that contains all the
fields specified in the order specified. You can trap this error by looking at
the return value of the function.

Examples

Customer.SETCURRENTKEY("Search Name"); //Sorts by Search Name


Customer.SETCURRENTKEY("No."); //resets to the primary key
Customer.SETCURRENTKEY(Address,City); //Fails with an error
//Trapping the error
IF Customer.SETCURRENTKEY(Address,City) THEN ...

SETRANGE

The SETRANGE method provides a quick way to set a simple filter on a


field. If you call this function with a field that already has a filter, the system
1-14 Applied Programming in CSIDE

removes that filter before it sets the new one. A range in C/SIDE is of the
form "FromValue ToValue". This is the only type of filter that SETRANGE
can perform.

Once a filter is applied to the record variable, the record set is changed so
that it only includes those records that meet the filter criteria. Using the
FIND or NEXT methods on a filtered record set will only retrieve records in
that record set. The GET method however ignores all filters and gets the
record from the database if it can.

Examples

Customer.SETRANGE("No.",'10000','90000'); //Includes first 5


Customer.SETRANGE("No.",'30000'); //Includes just 30000
Customer.SETRANGE("No."); //removes all filters on No.
Customer.SETRANGE("No.",'111'); //Includes NO records
//Displaying a filtered record set
Customer.SETCURRENTKEY("Search Name"); //Always set a key
Customer.SETRANGE("Search Name",'D','K'); //set filter
FORM.RUN(FORM::"Customer List", Customer); //call form pass rec

SETFILTER

The SETFILTER method provides a way to set a complex filter on a field. If


you call this function with a field that already has a filter, the system
removes that filter before it sets the new one. You can construct filters
using these operators: (), .., &, |, <, <=, >, <>, *.

You can also use replaceable parameters (%1, %2 and so on) just like the
MESSAGE function.

Examples

//Filter down to the first three customers


Customer.SETFILTER("No.", '10000|20000|30000');

//Filter down customers with credit limits over 17,500


Customer.SETFILTER("Credit Limit (LCY)", '>17500');
//Filter down to all customers after 10000 but not 20000
Customer.SETFILTER("No.", '>10000 & <> 20000');

//Filter down to customers with the First letter J or K


Customer.SETFILTER("Name", 'J*|K*');

RESET

The RESET method removes all Filters and resets the key to the primary
key.
Complex Data Types 1-15

Examples

Customer.SETCURRENTKEY("Search Name");
Customer.SETRANGE("Search Name", 'J','K');
Customer.RESET; //Puts the record variable back to original

Other Methods

INIT

Use the INIT method to initialize a record variable. The function does not
initialize primary key fields.

CALCFIELDS

Use this function to force the calculation of FlowFields or BLOBs in a


record variable. By default, the record variable does not calculate these
types of fields. You must specify the FlowFields or BLOBS that you want to
calculate.

Example

GLAccount.SETRANGE("Date Filter",0D,WORKDATE);
GLAccount.CALCFIELDS(Balance,"Balance at Date");

COPY

Use this function to copy a record variable to another record variable. All
fields, filters, marks and keys are included in the copy.

Example

Customer.COPY(CustRec);

COUNT

Use this function to count the number of records in the record set,
represented by the record variable. This will take into account any Filters
that are set and use the current key (which can increase or decrease the
speed depending on the filters set).

Example

X := Customer.COUNT;
1-16 Applied Programming in CSIDE

LOCKTABLE

Use this function to lock the table that the record variable is bound to. This
can protect it from other user’s attempts to write to the same table. If the
wait parameter is True, the function will wait until the table is unlocked to
proceed. Otherwise, if the table is locked, you will receive a run-time error.
The default value for the wait parameter is True.

Note

---------------------------------------------------------------------------------------------------

Do not use CONFIRM in connection with LOCKTABLE.

---------------------------------------------------------------------------------------------------

If the VersionCheck parameter is True, the system will assure that all
modifications are done on the same version of the database as when the
record was read. If that record has been changed, a run-time error occurs.
The default value for this parameter is False.

Whenever a write operation is performed on a table which has not been


explicitly locked, an implicit LOCKTABLE with both parameters set to their
defaults, is automatically performed.

Examples

Customer.LOCKTABLE; //Using the defaults


Customer.LOCKTABLE(FALSE); //Don’t wait if locked
Customer.LOCKTABLE(True,True); //Wait but use same version

VALIDATE

Use this function to call the OnValidate trigger for the field you specify. If
NewValue is passed in, the function will first assign the Field with
NewValue. The VALIDATE method first checks the TableRelation property
and then executes the OnValidate trigger of the field.

Examples

Customer.VALIDATE("Salesperson Code"); //checks tablerelation


// no code in trigger
Customer.VALIDATE("No."); //no tablerelation; executes code.

Activity

In this activity, you will loop through the customer table and set the credit
limit for all customers. You will filter first to avoid changing customers that
already have a credit limit. Can you find problems with this outlined logic?
Complex Data Types 1-17

1 Open the codeunit ModfiyCustomer (99101).

2 Create a function in the codeunit called SetAllCreditLimits.

3 In the function, set the current key to the "Credit Limit (LCY)" field. Use
the Customer record variable that already exists.

4 Set a filter so that the record set includes only records that have a zero
credit limit.

5 Loop through the records using a repeat loop. Don’t forget the Find(‘-‘)
at the beginning.

6 Inside the loop add code to modify credit limit to be the balance * 10. If
the balance is zero, set the credit limit to 10,000. Don’t forget to
calculate the Balance FlowField.

7 Test you function by putting code in the OnRun trigger and running the
codeunit.

What is a Temporary Table?

A Temporary Table is created by C/SIDE when a record variable is created


that has its temporary property set to yes. It is created with the same exact
structure of the real table that is in the subtype property of the record
variable. It is, however, completely empty (no data).

This can be used as a temporary workspace to insert records, manipulate


them and then either put them into a real table or allow them to disappear.
No data in a Temporary Table will ever be stored on the server.

To save records that are in a Temporary Table to the server, you must
copy them to another record variable that is not tied to a temporary table.

Example of Using a Temporary Table

The following code uses two record variables. Customer is a regular record
variable that points to the real customer table. CustTemp is a record
variable that points to a temporary copy of the customer table. Both have
their subtype set to Customer, but CustTemp also has its Temporary
property set to Yes.

You can type in this code to see what it does.

Customer.FIND('-');
REPEAT
1-18 Applied Programming in CSIDE

IF Customer.Name > 'F' THEN BEGIN


CustTemp := Customer;
CustTemp.INSERT;
END;
UNTIL Customer.NEXT = 0;
FORM.RUN(FORM::"Customer List", CustTemp);

Multilanguage Functionality
Navision Attain now has Multilanguage functionality. This section will cover
what will be needed to enable this new feature.

The Name property of an object should always be English – United States


(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.

Note

----------------------------------------------------------------------------------------------------

Accordingly, you must be sure that anything you enter that will end
up in the user interface must be ENU. You can run a test by selecting
all objects in the Object Designer, clicking Tools, Language, Export,
saving the file as a text file, and then opening that file in Notepad.
There must be no other language code than A1033 for ENU in that text
file.

----------------------------------------------------------------------------------------------------

When referring to fields in a message, the code will refer to the caption rather
than the name of the field. By using the FIELDCAPTION, the current caption of a
field will be returned as a string. The field property CaptionML must be
populated to enable this functionality. The TABLECAPTION function can be used
to return the table name. Shown below is code to return the caption of the
Document Type field.

SalesLine.FIELDCAPTION("Document Type");
Complex Data Types 1-19

Error messages and other messages must be entered as text constants in the
C/AL Globals window, Text Constants tab. When creating a text constant
C/SIDE assigns a unique ID, which will be used in your error message.

Once it has been assigned as a Text Constant, it can then be used in code:

ERROR(Text001);

Below is an example of using the FIELDCAPTION function within an error


message. Text Constant 025 value is “Please enter "Yes" in %1 and/or %2
and/or %3”.

ERROR(
Text025,
FIELDCAPTION(Receive),FIELDCAPTION(Invoice),
FIELDCAPTION(Ship));

Which when the code is run the error message translates into:

Please enter “Yes” in Receive and/or Inovice and/or Ship.


1-20 Applied Programming in CSIDE

1.5 Using Versions, Transactions and Optimistic Concurrency

Now that you understand record variables, you can really understand how
to use the database features that the Navision Database has built in. In this
section, we will cover how to actually implement these various features in
C/SIDE and in your C/AL coding.

The Navision Server is Version-Based

This is completely automatic for the Navision Server, and you really do not
have to do anything to implement it. However, you must understand this if
you are to understand the functioning of certain methods in C/AL.

Whenever you start a "process" (see Section 8 – Application Architecture),


the current Version of the database is assigned to that process. This
means that from the beginning to the end of the process, all of the data that
is read is consistent with the version of the data that was read at the
beginning of the process.

The advantage is that, for example, if a Trial Balance report is started from
the General Ledger, it will run through using one version of the database,
being completely in balance and consistent, even if people are still making
entries and posting on other workstations at the same time.

Another example is in the case of a backup. Throughout the process of a


Navision Backup, the user doing the backup is on the same version. Thus,
when complete, the backup is completely internally consistent.
Remembering that the data could be scattered throughout the database on
multiple disk drives, you can see how important this would be.

Suppose, another user posted a General Ledger transaction, and the debit
was in the part of the database that was already backed up while the credit
was in the part of the database that was yet to be backed up, the backup
would be out of balance and useless.

How does this work? Each record is marked with the version number of the
process that created it. If there are multiple versions of that record, the one
that a process will use is the highest version number less than or equal to
the current version that was assigned to this process at the beginning.

What happens when some other user posts while I am using a version?

Whenever a transaction takes place, a new version of the database is


created. However, the old version is still kept as long as possible. As long
as a session is using a particular version, those parts of the database are
Complex Data Types 1-21

not discarded, even though a new version exists.

This is why it is so important to keep a good amount of free space in your


database at all times (at least 20%). This "free" space is not unused; it is
just available for new versions. At a bare minimum, the version that the
posting process was assigned at the beginning must be maintained until
the end, in case of a rollback. If there is not this much available space, the
posting will fail. In addition, if a user is running a long report and needs to
keep a version, but another user's posting process needs the space, the
posting process will get the space, and the report will get an error saying
that there was not enough space.

All Navision Updates are Transaction-Based

This is almost as automatic for the Navision Server as Version Based, but
there are a few things you need to watch if you want it to work as expected.

In order to update the Navision database, a transaction must be started.


This starts a new version for each table that is updated. This version,
however, is not available to anyone else on the system until the transaction
ends. The transaction can end in one of the two ways.

First, if it is completed successfully, it is committed (using an implicit or


explicit COMMIT statement). This sets the current version of the database
to the new version created for this transaction, and the space used by the
old version becomes available.

Second, if it fails due to an error, it is rolled back. This leaves the current
version of the database whatever it was, and the space used for the new
version becomes available.

As you recall, transactions are started during a process when a


LOCKTABLE function is executed, either explicitly or implicitly whenever a
table is updated. From then on through the rest of the process, that table is
using the new version currently being created. Any other tables that have
not been locked or updated are still using the version in effect when the
process started.

Again, this keeps everything consistent throughout the posting process


even if somebody else changes things like posting groups while you are in
the middle of posting. When a table is locked, a new version is being
created for that table. Every record inserted or modified will get the new
version number. Nobody else can access that version and nobody else can
lock the table.
1-22 Applied Programming in CSIDE

Anybody else who starts or is continuing a process, will use the current
version or the version in effect when they started. Anybody else who tries
to lock the table, will wait until the table is available to be locked, and then
lock the table and create a new version. At that point, they will "see"
everything that everybody else did to the database since they started the
process. Until that point, the only thing they could "see" in that table was
the version that was current at the start of the process.

For this reason, it is very important that you explicitly lock a table (using
LOCKTABLE) before you "look at" (GET or FIND) any records from that
table on which future updates will depend.

Here is an example: Suppose you read the last record of a Ledger table to
determine the highest Entry No. in it in order to make sure that the record
you insert has an Entry No. one higher than that. Then you INSERT your
record. Everything should be fine, right? After all, what can happen
between two lines of code?

Well, everything is not fine! Unknown to you or your process, when you
started this, somebody else was in the middle of a 5-minute posting of the
General Journal. Thus, your process was reading the most current version
of the data, 4 minutes old. When you read, you got the highest Entry No. at
that time (basically at the same time as the posting process started since
nobody else can create a version either).

Then, when you did your INSERT, an implicit LOCKTABLE was performed,
and your process sits and waits for the other process to finish with the
table. When the other process is completed, it makes its new updated
version the current version. When you lock, you will now see this new
version. In this new version, 2,000 more entries have been posted, and so
the Entry No. you are trying to insert is no longer unused. You will get an
error saying that you tried to insert a duplicate Ledger entry.

What you should have done is an explicit LOCKTABLE before you did your
read. In this situation, your process would sit and wait until the other
process finished at the LOCKTABLE command. Then, you would see the
new updated version before you read. The read would get the highest Entry
No. in the most updated version in the entire database - the one you are
currently creating. Now, when you add one to that number and use it to
insert a new record, you are guaranteed that you will not get a duplicate
ledger entry.
Complex Data Types 1-23

Navision uses Optimistic Concurrency

First of all, what do we mean by Optimistic Concurrency? This means that


we do not lock a record before we read it for an update. Instead, we leave
the record unlocked while you view it, and only lock it when you are ready
to update. If you don't update, we never have to lock it. If you do update,
and nobody else has edited that record, then we are fine. It is only if
somebody else has updated the record while you were viewing it that we
would get an error telling us that. That is why it is called "optimistic". We
don't lock it in the optimistic hope that we will not get in trouble by not doing
so.

With a Version Based database using Transaction Based updates, and


automatically locking the entire table (not just one record) whenever we
update a record, how can we say that we use Optimistic Concurrency?
Well, it is true that in code you must actually do something special to
implement optimistic concurrency. However, in the most-often used
database updates in the system, updates done by users using forms,
optimistic concurrency is the automatic process used.

Here is how Optimistic Concurrency is implemented. The situation is that it


will be a long time from the time you read a record until the time you need
to update it, and you do not want the table locked that whole time. First,
you would read the record or records without locking. Then later, when you
were ready to update, you would do an explicit LOCKTABLE, with the
VersionCheck parameter set to TRUE. Then, you would do your record
updates (reading and writing any records you wish).

If no other process modified those records, you will complete successfully.


If not, you will get an error telling you that somebody else updated the
record. This is the same error you would get if you were editing a record in
a form and somebody else updated it before you finished editing.

How does this work? Recall that whenever a record is modified, it gets the
version number of the process that modified it. Once you do the
LOCKTABLE with the VersionCheck parameter set to TRUE, every time a
record update is done, the record's version number is checked. If it is still
less than or equal to the version number of the process, the update can go
through without error. However, if it now has a higher version number than
the process, that means that somebody else updated it since the process
began and the error is generated.

During all form updates, an implicit LOCKTABLE with the VersionCheck


parameter set to TRUE is done first. Thus, for most purposes of any
interest to the user, Navision uses Optimistic Concurrency.
1-24 Applied Programming in CSIDE

For an example of Optimistic Concurrency in code, look at any of the "Post


Batch" journal posting routines. Throughout the posting process, we read
the journal table so we know what to post. However, we do not lock it, so
that users can continue to make journal entries in other journal templates
and batches. At the end, when we are ready to update or delete the journal
entries, we do a LOCKTABLE with the VersionCheck parameter set to
TRUE. As long as nobody else was editing this particular journal or batch,
everything will go through without error. Otherwise an error will occur and
the entire posting will be rolled back.

Notes Concerning the SQL Server Version of Navision

There are a few differences between the SQL Server database and the
Navision Server database that you should be aware of.

First of all, SQL Server is not version based. This means that if it is
important for your report that it be consistent from beginning to end, then
the only way to insure that is to lock the tables that you use for your report.
We will do this for you in Account Schedules (financial statements), and a
few other reports that we consider critical, but you will need to make these
changes for any report your client considers critical to be internally
consistent.

SQL Server does not have a backup issue. You will no longer use the
Navision Backup since SQL has its own built in backup procedure. The
SQL backup will guarantee internal consistency by backing up the entire
database and then backing up the transaction log containing all the
transactions that have occurred since the backup started.

Second, SQL Server handles optimistic concurrency differently than the


Navision Server. This will be handled in the forms automatically and we will
modify the code appropriately for the posting routines. You will be able to
see what we did when we release it since it will be conditional, based on
which Server the application is attached to.

All SQL Server updates are Transaction Based. Although it is implemented


differently from the way Navision Server implements it, it will make no
difference to the C/SIDE developer.
Complex Data Types 1-25

Chapter Review

1 Describe the difference between a simple and complex variable.

2 When can the Scope operator (" :: ") be used with complex datatypes?
Give an example.

3 The GET() function requires parameters containing what?

4 What does the VALIDATE() C/AL function do?

5 What should you do prior to using the SETRANGE or SETFILTER


functions?

6 This function clears all fields, filters and resets the key to the primary
key _________________

7 This function clears all Filters and all fields except the primary key
fields __________________

8 This function only clears filters and changes the key back to the
primary key _______________
1-26 Applied Programming in CSIDE
CHAPTER 2
CODING IN TABLES, REPORTS AND
DATAPORTS

This chapter introduces the concept of triggers. It


explains how to use the triggers found in Tables,
Reports, and Dataports.

Now that you know what code to write to perform


most tasks, you need to know where to write that
code. There are many places where you can
place code. However, there is usually only one
correct place.

This chapter will cover the following sections:

2.1 What is a Trigger?


2.2 Table and Field Event Triggers
2.3 Report Event Triggers
2.4 Special Report Functions and Where They
Belong
2.5 Processing Only Reports
2.6 Dataports Event Triggers
2-2 Applied Programming in CSIDE

2.1 WHAT IS A TRIGGER?

There are three kinds of triggers that you can see in C/AL:

Documentation Trigger

The first is the Documentation trigger. This is not really a trigger and there
is not really C/AL code in it. Instead, a programmer can use the
Documentation trigger to write any sort of documentation they want for the
object. You will use this space to document your modifications to standard
objects.

Every object has a Documentation trigger.

Event Trigger

The second kind of trigger is an Event Trigger. The name of these triggers
always starts with “On”. The C/AL code in an event trigger is executed
when the named event occurs.

For example, the code in the OnRun event trigger of a codeunit is executed
whenever the codeunit object is run. In cases where there is no trigger
code, nothing would happen. Each object has its own set of predefined
event triggers that can be programmed. An event trigger is a place for the
Developer to respond to some event or action that has taken place in the
system. These events are usually caused by the User clicking with the
mouse or typing on the keyboard. This is the subject of this Chapter.

Function Trigger

The third kind of trigger is a Function Trigger. These triggers are created
whenever you create a function within an object. The C/AL code in this
function trigger is executed whenever the function is called.

You will learn more about creating and calling functions later in this course.

What causes an Event Trigger to “Fire”?

The code in an Event trigger is executed by the Fin.EXE program when a


specified event happens. When the user leaves a field, presses F3, or
closes a form, Event Triggers fire.

An Event Trigger is a term that is used with Event Driven programming.


The term “fire” is used because the trigger code is not called in a linear or
Coding in Tables, Reports and Dataports 2-3

structured programming fashion. Code in an Event Trigger should stand


completely alone. It should not depend on some prior event occurring. The
User is usually in control of these events.

The programmer cannot decide what event should come next or prior to
another event. This is of course only a general rule, but is a good one to
keep in mind when doing Event Driven programming.

Which Objects have Event Triggers?

In Navision, all objects (Tables, Forms, Reports, Dataports, Codeunits)


have Event Triggers. The type of the object determines what types of Event
Triggers it has.

A form object has many more Event Triggers than a table. A report object
has many Event Triggers as well, but they a separated by the different sub-
objects.
2-4 Applied Programming in CSIDE

2.2 TABLE AND FIELD EVENT TRIGGERS

There are four events that fire for a table object. There are also two event
triggers for every field in a table. In this section, you will learn when these
events take place and how you can use them.

Table Event Triggers

All of the table event triggers fire before the corresponding action takes
place, i.e. the OnInsert trigger will fire before the record is ever inserted into
the database.

For each table event, C/SIDE provides two record variables for you to use.
One is called Rec and can be considered the current record for the event.
The other is called xRec and can be considered the previous version of
Rec. These two record variables allow you to view the current record and
make any needed changes. They are described in more detail for each
particular event.

OnInsert

The OnInsert trigger fires when the user inserts a record or the INSERT
method is called with a parameter of TRUE. On a typical form, the insert
does not occur until the user successfully leaves the Primary Key fields that
are displayed on the form. If no Primary Key fields are being displayed on
the form, the insert will be delayed until at least the record is changed.

Although running a table creates a temporary form for you, this temporary
form has delayed insert turned on. When delayed insert is turned on, a
record is not inserted until the user leaves the entire record.

During this event:

. Rec is the record that is about to be inserted into the table.

. xRec is the last record that the user was on before they press F3.

OnModify

The OnModify trigger fires when the user modifies a record or the MODIFY
method is called with a parameter of TRUE. On a form, the modification
does not occur until the user leaves the record.

During this event:


Coding in Tables, Reports and Dataports 2-5

. Rec holds the value of the fields that are about to be put into the
database.

. xRec holds the previous values of the fields before the user changed the
record.

OnDelete

The OnDelete trigger fires when the user deletes a record or the DELETE
method is called with a parameter of TRUE. On a form, the delete does not
occur until the user presses F4 and clicks the Yes button on the
confirmation dialog (there is no confirmation dialog on records deleted in
code).

During this event:

. Rec holds the value of the fields that are about to be deleted from the
database.

. xRec holds the previous values of the fields before the user changed the
record.

OnRename

The OnRename trigger fires when the user renames a record (this involves
changing any of the Primary Key fields) or the RENAME method is called
(this trigger is always called). On a typical form, the rename does not occur
until the user changes the Primary Key fields and leaves them.

During this event:

. Rec holds the value of the fields that are about to be put into the database.

. xRec holds the previous values of the fields before the user changed the
record.

Field Event Triggers

Every field in a table has its own event triggers. Within these triggers you
also have access to Rec and xRec. There is also a new system variable
provided for you called currFieldNo. It is described in details below.
2-6 Applied Programming in CSIDE

OnValidate

The OnValidate trigger fires when the user changes a field and leaves that
field. This trigger fires before any other trigger can occur, like the OnModify,
OnInsert, or OnRename (this includes form triggers that we will discuss
later). You can use this trigger to make sure what the user entered into this
field is valid or to populate other fields with values based on what the user
entered here.

This is probably the most commonly used trigger in Navision.

During this event:

. Rec holds the values of all the fields that the user has changed or not
changed.

. xRec is identical to Rec except that it holds the previous value of the field
currently being edited.

. CurrFieldNo is set to the Field number of the field that the user changed.
If the field’s validate trigger is called from some other field’s validate
(originally initiated by the user), currFieldNo will be equal to the field
number of the field the user originally changed. If the validate was called
from code and not as a result of the user changing a field, CurrFieldNo is
zero.

OnLookup

The OnLookup trigger fires when the user clicks on the lookup button for
that field. Any code whatsoever in this trigger (even comments) will cause
C/SIDE to run only that trigger and ignore the default lookup that could
have been set up by a table relation. For most fields this trigger is not used
for that very reason.

Most lookups in Navision are done via a table relation. This trigger,
however, does allow the developer to write code for a custom lookup
feature.

Activity

In this activity you will put messages into the triggers above to see when
they fire.

1 Design the Price Group table 6.

2 Add a unique message to each table event trigger. Example


Coding in Tables, Reports and Dataports 2-7

Message(‘OnInsert’);

3 Run the table and note when each fires.

4 Run the Price Groups form 7 and note when each fires.

5 Do the same for the Name field.


2-8 Applied Programming in CSIDE

2.3 REPORT EVENT TRIGGERS

Reports have many triggers that are divided up among the many
components of the report. There are triggers for the report object itself, for
each data item, for each section, and for the request form. This section
ignores the form triggers for the request form, because form triggers are
discussed later.

Report Object Event Triggers

To view these triggers you must click View, C/AL Editor while the report
object is selected.

OnInitReport

The OnInitReport trigger fires when the report object itself is created.

This trigger fires before any other code in the report takes place, and even
comes before the Request Form is displayed to the user.

OnPreReport

The OnPreReport trigger fires right after the user closes the Request Form
by pressing OK/Print/Preview.

This trigger will not be called if the user cancels out of the report.

OnPostReport

The OnPostReport trigger fires right after the last un-indented DataItem
processes its last trigger.

This signals the end of the report after everything has been printed.

DataItem Event Triggers

To view these triggers you must click View, C/AL Editor while the data item
is selected. Each data item has its own set of these triggers.

OnPreDataItem

The OnPreDataItem trigger fires each time the DataItem is processed. This
trigger fires before any records from the associated table have been read.
Coding in Tables, Reports and Dataports 2-9

This is a good place to set additional filters or even keys.

You may also want to initialize some variables here that will be used to
calculate values or totals for this DataItem (see the CREATETOTALS
function later in this chapter).

OnAfterGetRecord

The OnAfterGetRecord trigger fires right after the data item gets a record
from the associated table. This is where most code will be placed in
complex reports.

This trigger fires before any printing for this record has been done
(including headers and footers). You can skip records here if they should
not be part of the report at all (see the SKIP function later in this chapter).

OnPostDataItem

The OnPostDataItem trigger fires right after the last section for the
processing of this DataItem has printed.

This trigger fires each time the DataItem is processed.

Note

Only indented data items are processed more than once in a report. They
are processed completely for each record that is not skipped in the
DataItem that they are indented under.

Section Event Triggers

To view these triggers you must click View, C/AL Editor while the section is
selected. Each section of the report has its own event triggers.

OnPreSection

The OnPreSection trigger fires right before the section is to be printed.

This trigger is not for totaling or setting variables (for the most part). It is
here to allow the developer to decide whether or not to actually print this
section (see the SHOWOUTPUT function later in this chapter).
2-10 Applied Programming in CSIDE

OnPostSection

The OnPostSection trigger fires after the report has determined that this
section will print and on which page it will print.

It actually fires before the section is printed not after. This is probably the
least used trigger in a report.
Coding in Tables, Reports and Dataports 2-11

2.4 SPECIAL REPORT FUNCTIONS AND WHERE THEY BELONG

In this section, you will learn about functions that you can only use in
reports. These functions help you create complex reports.

All of the following functions must be prefixed with “CurrReport.” (without


the quotes).

Skipping Something in the Report

The following functions allow you to skip some or all of a report.

CurrReport.SKIP

Use this function to skip the current record of the current data item. If a
record is skipped, it will not be included in totals and it will not be printed.
Skipping a record in a report is much slower than never reading it at all, so
use filters as much as possible.

CurrReport.BREAK

Use this function to skip the rest of the processing of the DataItem that you
are currently processing. The report will resume processing the next
DataItem. All DataItems indented under the one that caused the break will
also be skipped.

CurrReport.QUIT

This function skips the rest of the entire report. It is not an error, however. It
is a normal ending for a report.

Examples

IF Amount = 0 THEN
CurrReport.SKIP;
IF ReachedMaximum THEN
CurrReport.BREAK;

Grouping and Totaling Functions

The following functions make the report create totals or help when grouping
a complex report.

CurrReport.CREATETOTALS

Use this function to maintain totals for a variable in the same way as totals
2-12 Applied Programming in CSIDE

are maintained for fields by using the TotalFields property. This function
must be used in the OnPreDataItem trigger of the data item on whose
sections you will be displaying the totals.

CurrReport.TOTALSCAUSEDBY

Use this function to determine which field caused a break to occur. The
return value is the field number of the field that the data item is grouped on
that changed and caused a Group Header or Group Footer section to print.
This function is almost always used in the OnPreSection trigger of Group
Header and Group Footer sections. This function must always be used
when grouping more than one field for a single data item.

Examples

//Report has 2 data items


//Customer
// Cust. Ledger Entry (indented and linked)
//In the OnPreDataItem for Customer...
currReport.CREATETOTALS("Cust. Ledger Entry".Amount);

Changing the Printing of the Report

The following functions change the way that the report would normally print
or give you information about how it will print.

CurrReport.NEWPAGE

Use this function to force a page break when printing a report. This is
usually found in the data item triggers.

CurrReport.PAGENO

Use this function to return the current page number of a report and/or to set
a new page number.

CurrReport.PREVIEW

Use this function to determine whether a report is being printed in preview


mode or not.
Coding in Tables, Reports and Dataports 2-13

CurrReport.SHOWOUTPUT

Use this function to return the current setting of whether a section should
be outputted or not, and to change this setting. This function should only be
used in the OnPreSection trigger of a section. If TRUE is passed to the
function, nothing changes and the section will print as normal. If FALSE is
passed to the function, the section will not be printed for this iteration of the
DataItem.

Examples

//In the OnAfterGetRecord Trigger


IF StartNewOrder THEN BEGIN
CurrReport.PageNo = 1;
CurrReport.NewPage;
StartNewOrder := False;
END;
//In OnPreReport trigger
IF currReport.PREVIEW THEN
ERROR('You cannot preview this report');
//In the OnPreSection trigger of a Body section
IF ShowDetailSections THEN
CurrReport.SHOWOUTPUT(True)
ELSE
CurrReport.SHOWOUTPUT(False);

MultiLanguage Functionality
In order to enable Multilanguage functionality all reports MUST contain the
following code in the OnAfterGetRecord() in the first DataItem:

OnAfterGetRecord()
CurrReport.LANGUAGE := Language.GetLanguageID("Language Code");
2-14 Applied Programming in CSIDE

2.5 PROCESSING ONLY REPORTS

A processing only report is one that does not print but instead changes
table data. Printing reports can change records as well. This section
applies to those reports as well.

You can specify a report to be “Processing Only” by changing the


“Processing Only” property of the Report object. The report will function just
as it is supposed to (processing DataItems), but it does not print any
sections.

The Request Form changes slightly as well by removing the Print and
Preview buttons and replacing them with an OK button (Cancel and Help
stay).

Below are some helpful hints when writing a processing only report.

. Change the ProcessingOnly property to Yes.

. Decide on the tables that need to be read – these are the data items.

. Most of the code will go into the OnAfterGetRecord trigger.

. Don’t forget the INSERT or MODIFY functions.

. Use a dialog to show the user the progress, and allow the user to cancel
the report.

Activity

In this activity you will create a processing only report that changes the
prices in the item table.

1 Create a report with ID 99100 and Name Change Item Prices.

2 Add two global variables Adj (integer) and Window (dialog).

3 Add to the request form a text box and label. The sourceexpr of the
text box will be Adj and the caption should be Adjustment Factor.

4 Add the Item table as a data item.

5 In the OnAfterGetRecord trigger, add code to change the Unit Price


field of the item record to itself times Adj.
Coding in Tables, Reports and Dataports 2-15

6 Add code to display and update the window with the Item No.

7 Add code to make sure the user enters a positive number.

8 Test your creation. You may want to add a SLEEP to slow the report
down.
2-16 Applied Programming in CSIDE

2.6 DATAPORTS EVENT TRIGGERS

Most "real-life" Dataports require more work than just filling in the fields.
Many times, they will require code, and that means triggers. Here are some
of the triggers found in Dataports.

Dataport Object Event Triggers

To view these triggers you must click View, C/AL Editor while the dataport
object is selected.

OnInitDataport

This event fires right after the Dataport is created and before the request
form is displayed to the user.

OnPreDataPort

This event fires after the request form is closed and before the data items
are processed.

OnPostDataPort

This event fires after the Dataport has finished importing or creating the file.
All data items have been processed.

Data Item Event Triggers

To view these triggers you must click View, C/AL Editor while the data item
is selected. Each data item has its own set of these triggers.

OnPreDataItem

This event fires before the data item is processed. It is a good place to set
filters or keys for exports. It is also a good place to prepare the table before
importing.

OnBeforeExportRecord

This event fires after a record has been read, but before it is written to the
file. This event only occurs while exporting data.
Coding in Tables, Reports and Dataports 2-17

OnAfterExportRecord

This event fires after a record has been written to the file. It is a good place
to reset the record variable or other variables being used. This event only
occurs while exporting data.

OnBeforeImportRecord

This event fires before the next record is read from the file. This event only
occurs while importing data.

OnAfterImportRecord

This event fires after a record has been read from the file, but before any
AutoSave, AutoReplace or AutoUpdate has been preformed. This event
only occurs while importing data.

OnPostDataItem

This event fires after a data item has been completely processed.

Dataport Field Event Triggers

To view these triggers, you must click View, C/AL Editor while the dataport
field is selected. Each dataport field has its own set of these triggers.

OnBeforeEvaluateField

This event fires after the field has been imported from the file, but before
being assigned to the field or variable set in the dataport field's SourceExpr
property. This event only occurs while importing data.

OnAfterFormatField

This event fires after the value of the dataport field has been formatted, but
before it has been written to the file. This event only occurs while exporting
data.

Activity

In this activity, you will create a dataport that exports and imports the
customer table.

1 Create a dataport with ID 99100 and Name Customer Port


2-18 Applied Programming in CSIDE

2 Add the customer table as a dataitem and use the following fields as
dataport fields: No., Name, Address, City.

3 In the dataport field triggers, make sure the City field is uppercased
before it gets written out to the file.

4 In the data item triggers, make sure that when you import with this
dataport, the Name field is validated. This sets the Search Name field.

5 Run the dataport in CRONUS to export all the customers.

6 Create a new company called TEST.

7 Run the dataport in the new company to import the customers.


Coding in Tables, Reports and Dataports 2-19

CHAPTER REVIEW

1 What function key is used to display trigger codes?

2 The __________________________ table trigger code is executed

when a primary key field is changed and the user moves off of the

record.

3 The __________________________ trigger code is executed when

the value of any non-primary key is changed and the user moves off of

the record.

4 Any code, even remarks, entered into the OnLookup trigger will (BE
ADDED TO - or - REPLACE ) the internal lookup (F6) functionality.

5 TRUE or FALSE – Execution of an ERROR statement in the OnDelete


trigger will exit the table without actually having deleted the record(s)
from the table.

6 In which report trigger would it be most appropriate to place code that


performs calculations on fields within a record?

7 Which report trigger contains code that is run before the Request Form
is shown to the user?

8 What code would you use to keep the user from previewing a report?
What trigger would you put the code in?

9 In which report trigger would it be most appropriate to place code that


creates totals for a variable (CREATETOTALS) or adds further filtering
for a DataItem?
2-20 Applied Programming in CSIDE

10 In which report trigger would it be most appropriate to place code that


informs the user of the completion of a non-printing report?

11 TRUE or FALSE – The code in the OnPrintReport trigger is not


executed if the report is a non-printing report.

12 You are creating a dataport to bring in student information. Because of


the way data is stored in the data file, it is necessary to convert letter
grades (A, B, C, D, F) into their Decimal equivalent (4.0, 3.0, etc.) for
the Grade field. You decide to create a text variable called
“LetterGrade” to hold the text data coming in from the text file. Write a
CASE statement to do this and indicate the correct trigger for this code.

TRIGGER: _________________________

13 Concerning the previous question, how could you do it without creating


a text variable but by sending the data straight into the Grade decimal
field? Which Trigger would you use?

TRIGGER: _________________________
CHAPTER 3
OBJECT ANALYSIS

To become an efficient and effective programmer


in Navision’s C/SIDE environment, you will
occasionally have to leave this environment. This
is especially true for upgrades, object analysis
and many common tasks that are too time
consuming to perform within C/SIDE. In order to
work with objects outside of C/SIDE, you must
export objects as text. A text version of an object
can be confusing if you are not familiar with it.

In this chapter, you will become more familiar with


this format and understand the power that it gives
the developer. We will also look at a tool that
takes this format of your objects and breaks it
down for easy analysis. The tool is Developers
Toolkit, and was created by a Navision
Development Partner that worked closely with
Navision a/s.

This chapter covers the following sections:

3.1 Exporting Objects as Text


3.2 Editing an Object Using an External Text
Editor
3.3 Changing the Text File
3.4 Object Analysis with Developers Toolkit
3-2 Object Analysis and Development Methodology

3.1 EXPORTING OBJECTS AS TEXT

In this section, you will learn how to export objects as text to a file. You will
also learn some important facts about the text version of an object.

Some Facts

The are certain things you should keep in mind when working with the text
version of objects:

. All objects can be exported as text.

. The text representation is complete. Only triggers with code or properties


that have been changed will be shown.

. Every detail of an object can be easily seen and searched when exported
as text.

. You can change the text object and import it back in as an uncompiled
object.

. When you import text objects, you do not get a chance to see the import
worksheet.

Basically, you need to be careful when working with the text version of
objects.

To export an object as text, follow these instructions:

1 Select the object or objects in the Object Designer.

2 Click File, Export.

3 Make sure to select the Text option.

4 Select a filename.

5 Click OK.

Activity – Exporting an object as text

In this activity, you will export the currency table as text.

1 Go to the Object Designer.

2 Click the Table button.


Object Analysis 3-3

3 Select the Currency table (table 4).

4 Click File, Export.

5 On the export dialog, put in a filename (you can use the Assist Edit
button to bring up the File Save dialog). Make sure to give the file a
TXT extension and to select the Text option here.

6 Click OK to export the object to the file you have specified.

The file you create when you export as text is a Text file. You could open
this with WordPad or Notepad, but if you have a true text editor like
Codewright, you can customize the tool to work with Navision a little better.
You will explore this more in the next section.
3-4 Object Analysis and Development Methodology

3.2 EDITING AN OBJECT USING AN EXTERNAL TEXT EDITOR

In this section, you will take a look at the object you exported and the
different parts that make up the text version of an object.

Note

You can open these files with Notepad. This program is acceptable for
small files, but for larger files you would have to use some other program.
We recommend Codewright by Premia or some other robust text editor.
Your class disk contains files for Codewright that will change Codewright to
recognize the Navision Object format and highlight keywords, literals and
so on.

Activity – Opening the Text File

In this activity you will learn how to open the text file you created.

1 Using Windows Explorer, find the file that you exported in the last
activity.

2 Open the file by double clicking (this should open Notepad). You can
also open the file using another text editor if you have it installed.
Object Analysis 3-5

Looking at an object in its text version

To really understand what you will be looking at when you export an object
as text, there are few things that need to be pointed out.

Here is a sample of a table exported as text:


OBJECT Table 4 Currency Object Properties are the same for
{
OBJECT-PROPERTIES all types of objects: Date, Time,
{
Date=08/29/01; Modified Flag, and Version List.
Time=[ 2:00:00 AM];
Version List=NAVW13.01;
}
PROPERTIES
{
OnModify=BEGIN The Properties section
"Last Date Modified" := TODAY;
END; includes only properties that do

OnDelete=BEGIN not have the default value. Here


CurrencyExchRate.SETRANGE("Currency Code",Code); only LookupFormID is listed.
CurrencyExchRate.DELETEALL;
END;
OnRename=BEGIN
"Last Date Modified" := TODAY;
END; It also includes Trigger code.
CaptionML=ENU=Currency;
LookupFormID=Form5;
}
FIELDS Then we have a list of the Subobjects,
{
{ 1 ; ;Code ;Code10 ;NotBlank=Yes } for tables that consists of Fields and
{ 2 ; ;Last Date Modified ;Date ;Editable=No }
{ 3 ; ;Last Date Adjusted ;Date ;Editable=No } Keys. They are grouped according to
{ 6 ; ;Unrealized Gains Acc.;Code20 ;TableRelation="G/L Account";
type. Fields in one bracketed group and
OnValidate=BEGIN
CheckGLAcc("Unrealized Gains Acc."); Keys in another.
END;
}
.
.
.

{ 12; ;Invoice Rounding Type;Option ;OptionString=Nearest,Up,Down }

{ 13; ;Amount Rounding Precision;Decimal ;InitValue=0.01;


For each subobject (Field in this
OnValidate=BEGIN
IF "Amount Rounding Precision" <> 0 THEN case), there is a list of properties
"Invoice Rounding Precision" := ROUND("Invoice Rounding Precision"…
and triggers. Notice that
END;
properties like FieldNo, Name and
DecimalPlaces=2:5;
MinValue=0 } Data type are listed without the
{ 14; ;Unit-Amount Rounding Precision;Decimal;
InitValue=0.00001; property name.
3-6 Object Analysis and Development Methodology

DecimalPlaces=2:5; The brackets that are used


MinValue=0 }
{ 15; ;Description ;Text30 } throughout the text version of
. an object are not the comment
.
. brackets that you would use in
{ 21 ; ;Global Dimension 1 Filter;Code20 ;FieldClass=FlowFilter; code. They are used here like
TableRelation="Dimension
Value".Code WHERE (Global Dimension No.=CONST(1)); they would be in C or C++ to
CaptionML=ENU=Global Dimension
note1theFilter;
beginning and ending
CaptionClass='1,3,1' }
of different areas. A comment
{ 22 ; ;Global Dimension 2 Filter;Code20 ;FieldClass=FlowFilter;
TableRelation="Dimension placed in the code area does
Value".Code WHERE (Global Dimension No.=CONST(2)); not affect the brackets here.
CaptionML=ENU=Global Dimension 2 Filter;
CaptionClass='1,3,2' }
{ 23; ;Date Filter ;Date ;FieldClass=FlowFilter }
{ 24; ;Cust. Ledg. Entries in Filter;Boolean;
FieldClass=FlowField;
CalcFormula=Exist("Cust. Ledger Entry"
WHERE (Customer No.=FIELD(Customer Filter),
Currency Code=FIELD(Code)));
Editable=No }
{ 25; ;Customer Balance ;Decimal ;FieldClass=FlowField;
CalcFormula=Sum("Cust. Ledger Entry".Amount
WHERE (Customer No.=FIELD(Customer Filter),
Department Code=FIELD(Department Filter),
Project Code=FIELD(Project Filter),
Posting Date=FIELD(Date Filter),
Currency Code=FIELD(Code)));
Editable=No;
AutoFormatType=1;
AutoFormatExpr=Code }
.
.
.
{ 45;
;EMU Currency ;Boolean }
{ 46;
;Currency Factor ;Decimal ;Editable=No }
{ 47;
;Residual Gains Account;Code20 ;TableRelation="G/L Account" }
{ 48;
;Residual Losses Account;Code20 ;TableRelation="G/L Account" }
{ 50
; ;Conv. USD Rndg. Debit Acc.;Code20 ;TableRelation="G/L Account";
CaptionML=ENU=Conv. USD Rndg. Debit Acc. }
{ 51 ; ;Conv. USD Rndg. Credit Acc.;Code20 ;TableRelation="G/L Account";
CaptionML=ENU=Conv. USD Rndg. Credit Acc. }
{ 52 ; ;Max. Tax Difference Allowed;Decimal;OnValidate=BEGIN
IF "Max. Tax Difference Allowed" <> ROUND("Max. Tax Difference Allowed","Amount
Rounding Precision") THEN

}
KEYS After subobjects comes
{
{ ;Code } the CODE area. This
} area contains all the
CODE
{ Global variables and
VAR
CurrencyExchRate : Record 330; functions.
GLSetup : Record 98;
PROCEDURE InitRoundingPrecision@2();
BEGIN
GLSetup.GET;
IF GLSetup."Amount Rounding Precision" <> 0 THEN
"Amount Rounding Precision" := GLSetup."Amount Rounding Precision"
Object Analysis 3-7

ELSE
"Amount Rounding Precision" := 0.01;
IF GLSetup."Unit-Amount Rounding Precision" <> 0 THEN Each Function
"Unit-Amount Rounding Precision" := GLSetup."Unit-Amount Rounding Precision"
ELSE can have its
"Unit-Amount Rounding Precision" := 0.00001;
END; own local
variables
LOCAL PROCEDURE CheckGLAcc@1(AccNo : Code[20]);
VAR
GLAcc : Record 15;
BEGIN
IF AccNo <> '' THEN BEGIN
GLAcc.GET(AccNo);
GLAcc.CheckGLAcc;
END;
END;
Note the extra begin and end
BEGIN here. This is where you would find
END.
} the Documentation Trigger text.
}
3-8 Object Analysis and Development Methodology

3.3 CHANGING THE TEXT FILE

In this section, you will change the text file by adding a field and then import
the file back into C/SIDE. You will discover what happens and what does
not happen when you import a text file.

Activity - Adding a field

The easiest way to add to the text file is to copy another part of the same
text file and change a few things. That is exactly what you will do in this
activity.

1 Find the Code field in the FIELDS section and copy that entire line to
the clipboard (Select the line and press Ctrl+C).

2 Go to the bottom of the FIELDS section (just above the KEYS section).

3 Just before the final curly brace that ends the FIELDS section paste
the line you copied (Ctrl+V).

4 Renumber and rename the field. Use the name “New Field”.

5 Save the file and exit the text editor.

6 Open Navision, open the Object Designer.

7 Click File, Import.

8 Choose the file you just changed.

9 Click OK to import the file.

Note

There is no import worksheet for Text files. The objects within the file will
be imported directly and overwrite any existing objects with the same
number. The objects will also come in uncompiled.

10 Find table 4 (Currency) in the Object Designer. Notice the value of the
Modified flag and the Compiled flag.

11 Press F11 to compile table 4 from the Object Designer (this compiles
all objects that are selected in the Object Designer).

12 Design table 4 (Currency) and see if your field has been added.
Object Analysis 3-9

13 Close the table designer.

Why do this?

Sooner or later, you will find out that you either have to manipulate objects
in their text version to accomplish some task or you will realize that some
tasks can be made much simpler by manipulating the text version.

To use many of the tools that Navision gives you, you need to understand
the text version of objects. That’s even true of the next tool you are going to
see - Developers Toolkit.

Example

Renumbering objects is much easier if you export all related objects as text
and renumber everything in the entire file at once.
3-10 Object Analysis and Development Methodology

3.4 OBJECT ANALYSIS WITH DEVELOPERS TOOLKIT

Have you ever had to increase the size of a text field in a database to allow
users to enter more data? If so, you probably had to change quite a bit of
code as well, perhaps even some forms had to change. Well, unless you
are the sole programmer, it’s hard to know where throughout the program,
changes need to be made. It may be some obscure code that manipulates
that field using variables that now need to be bigger or it may be several
forms that all display the field. It might even be a similar field in another
table that should be increased because it can sometimes be assigned the
value from the other table. In Navision, this can be a problem.The
Developers Toolkit, however, makes this task almost enjoyable. With the
Toolkit, the programmer can import a particular customer’s objects and
make queries like “Where is this field used” (you don’t actually type in the
queries). You can even search the entire database for a word or phrase
that you want to change.

Unfortunately, right now the information that we can see in the Toolkitis
read-only. Future versions will allow you to make changes directly in the
tool. In the merge part of the toolkit, there is limited copy and paste
functionality.

The following is an overview of the Developers Toolkit. The instructional


Adobe Acrobat file is located on the Developers Toolkit compact disk
handed out in class.

Some Facts

Before you try to use the Toolkit, there are some things that you should
know about how the tool works.

. The Toolkit takes the Text version of Navision objects, parses everything
out and stores the results into its own database.

. Everything you can see in the Toolkit is read-only.

. The database that the Toolkit uses to store its data is a Navision C/SIDE
database. This is NOT an Attain database.

. The Toolkit uses C/FRONT to read and write to its database.

. Future versions of the Toolkit may allow us to change the objects and
then put them back into the Customer’s database.
Object Analysis 3-11

Installation Procedures

1 Install Developers Toolkit.

The Toolkit requires a separate installation other than the Navision


Client. Because the Toolkit uses C/FRONT, it needs to know the
location of the client and the license file during installation (and will
prompt you for this during the install). Therefore, you need to
ensure the Navision client is installed on the machine first.

2 Create a Developers Toolkit Database. You have to start Navision


Attain to create and manage the Navision Developers Toolkit
database. If you want to create a completely new database, you
will need to:

1 Start Navision Attain. Create a new Navision Attain database in


the directory where you have installed Navision Developers
Toolkit. If you want to import the object data of a standard
Navision Attain application database later on, you should use at
least 350 MB as file size for this new database.
2 Open the Object Designer and import the file DevTool.fob from
the directory where you have installed Navision Developers
Toolkit.
3 Create a new company. You can use different companies to
store object data from different application databases in one
Navision Developers Toolkit database.

3 Export Objects as text from a Navision Attain database.

These will be the objects you would like to analyze in Developers


Toolkit. They can be from any version of Navision. You are required
to export them as text so that additional information can be added
to them when they are imported into Developers Toolkit.

4 Import the Objects into Developers Toolkit

You can now import the text objects into the Toolkit tool. Just open
Developers Toolkit, go to File->Database->Open and open the
database file that you have created. You should be prompted to
open a company (If no Company exists, you must open the
3-12 Object Analysis and Development Methodology

database with Navision and create a company). Now you can go to


File->Import and select the text file that contains all of the objects
you would like to import.

In the above dialog box, you can assign a code to the objects you
are importing (this is required) and a description (optional). On the
Import tab you must specify the filename of the file you are
importing. Once you input the above information, you can click the
Import button. During the import, you will be given progress
indicators that show you where it is in the process.
An import for an entire database can typically take anywhere from 5
to 15 minutes depending on the machine and number of objects.

The above picture shows the System Tab. It includes:


Date in Import File This field is filled in automatically when you
enter the field Import Filename .You can see the date format of
the first object in the import object file.
Object Analysis 3-13

Date Format Enter the date format for the date field in the import
file.You can use your country specific shortcuts for day,month and
year but you have to make sure that the delimiters are the same as
in the field Date in Import File .
Time in Import File This field is filled in automatically when you
enter the field Import Filename.You can see the time format of the
first object in the import object file.
Time Format Enter the time format for the date field in the import
file.You can use your country specific shortcuts for hour,minute and
second,but you have to make sure that the delimiters are the same
as in the field Time in Import File .

Analyzing Objects

There are many ways to analyze your objects within Developers Toolkit. In
this subsection, you will learn about all those different ways.

Object Administrator

The Object Administrator window shows all objects that have been
imported to the Developers Toolkit database. The Object Administrator is
the backbone of Developers Toolkit. All activities are based on the data of
the objects in the Object Administrator. These objects are grouped
according to Navision object types (Table, Form, Report, Dataport and
Codeunit).

Object Views

Object Tree – this shows the result(s) of one or more functions in an


Explorer-type view. You can perform the same functions in the Object Tree
as you can in the Object Administrator.

Object Diagram – this shows the result(s) of one or more functions in a


graphical view. You can also use all of the functions in the Object Diagram
as you can in the Object Administrator.

Object Functions

Relations to Tables - this shows all table relations in the current object
pointing to other tables. In Navision Attain table relations can be defined in
the properties of table field and data controls. That means you can use this
function for Tables, Forms, Reports and Dataports.

Relations From Objects - this shows all relations from objects pointing to
the current object. In Navision Attain, you can only define a relation to a
3-14 Object Analysis and Development Methodology

table. Consequently you can only use this function for tables.

Where Used - this shows all the places where an object or a part of an
object is used. The Where Used function will search in properties and C/AL
code of all object types.

. Where Used Options - You can set options for the where used function to
either increase or limit the areas in the database it will search. Once the
options are set they are then used for every Where Used function from
that point forward.

Where Used With - this is the same as Where Used, but will present you
with the Where Used Options settings. You can then set the Options for
this use only, so that the setting do not effect other Where Used With
functions run later.

Object Tools

Object Bin - this shows an object in the same way as the Object
Administrator, you can use the Object Bin to collect different Objects. You
can also perform and function in the Object Bin as you would in the Object
Administrator.

Code Viewer - this shows all C/AL Code lines for an object. The Code
Viewer window is divided into two parts. The left side shows you the code
structure of this object. For example you can see the list of object trigger,
field trigger and procedures. On the right side you can see the C/AL code
lines corresponding to the marked element on the left side. Keywords like
commands are colored to improve the readability of the code lines.

Source Finder - this searches the company for a specific character string.
To specify where in the database the Source Finder must search, you can
use the settings on the different tabs in the Source Finder window.

Compare Two Versions


You can use the Compare Two Versions window to compare two versions
that you have already imported in Navision Developers Toolkit. The
comparison is based on the object structure and gives you fast access to
specific areas.

To create a new import version follow the steps below:


1 Start Navision Developers Toolkit.
2 Click File,Database,Open to open a Navision Developers Toolkit
database
or click File,Server,Connect to connect to a Navision Developers Toolkit
server database.
Object Analysis 3-15

3 Click File,Import.The Import Version Card window appears:

Activity – Comparing Two Versions


In this activity, you will compare two version of the Field of Dreams Project.

1 If you have not done the Field of Dreams Project, import it from the
class disk located under labs/Day 4. Select all the objects in the Field
of Dreams project and export them as text to a text file.

2 Go into the Sponsor Table and add a field for Company. Set the table
relation to the Customer Table.

3 Go to the Pledge Table and add a field for Date Due.

4 Export the Field of Dreams to a different text file.

5 In the Navision Developers Toolkit, create the two versions and import
the text files.

6 Go to Tools/Compare two versions

Each column shows you all objects of the versions you have selected in each
column header.After you selected a version the comparison will start
automatically.

Both columns are synchronized vertical and horizontal and coloring is used to
show the differences on all levels.When an item in a tree is not expanded, the
item will represent the coloring of details of the item.If you want to set a filter
on object type or object ID,you can click on the Functions button in this window.
3-16 Object Analysis and Development Methodology
CHAPTER 4
DEVELOPMENT METHODOLOGY

When a Navision Solution Center (NSC) gets a


new customer, it is generally because the
customer can get their solutions customized to
their specifications. As a result, there are almost
always one or more development projects that
need to be done, even before the product is
implemented for the first time. In addition, as the
customer's use of Navision grows, they will often
want additional customizations. In this chapter,
we will cover how to do a customization project,
working as a part of the Implementation team.

This chapter covers the following sections:

4.1 Implementation Methodology


4.2 Version Control
4.3 Development Documentation
4-2 Object Analysis and Development Methodology

4.1 IMPLEMENTATION METHODOLOGY

As an important part of the training for a Navision Solution Center, Navision


Software teaches a specific way to sell, consult with, set up, install and
maintain Navision customers. We call this the Navision Implementation
Methodology. As a Navision Attain Developer, you play a key role in two
parts of this Methodology; Data Conversion and Customizations. Here is a
more detailed look at the Navision Implementation Methodology.

The ultimate result of using this methodology is a Navision Solution that fits
the client’s business, delivered on time and on budget.

Phases within the Methodology

The Navision Methodology uses a phased approach to implementation.


Each rectangle in the chart below represents one of these phases, and
they are described as follows:

Sales Phase

The customer is not a customer yet. The Navision representative leads the
process of assessing the potential customer's needs and how they can be
met by the Navision Solution Center. This is where the contact decides to
become a Navision Customer.

Technically part of the Sales Methodology, the Sales Phase is tightly


Development Methodology 4-3

integrated with the implementation process allowing for a smooth, quick


transition after the sale. Clearly defined sales deliverables, such as the
Client Profile Worksheet, enable a clean handoff to the Implementation
Consultant.

The Critical Needs Assessment is a flexible process ensuring Navision is a


good fit for the client. The size, complexity and risk of the project are
factors that help determine the extent of the assessment. Certain key
business challenges and needs are identified, and those are concentrated
on.

During this phase, the Developer will occasionally play a role in building a
prototype as a proof of concept, or to answer technical questions. For the
most part, though, the Developer is not involved in this phase.

Concept Phase

From this point forward, we are working with a customer. They may not
have made a product purchase decision, but they are at least a consulting
customer.

Unarguably, the Concept Phase is the most important phase in the


process. With proper planning, analysis and design, the project has a much
higher chance of being successful. Here, the customer's needs, including
the Critical Needs, are defined fully, and the plans are made for meeting
those needs. For example, decisions are made as to which Navision
granules are to be purchased.

Project Management guidelines for establishing project roles &


responsibilities and communication methods, assure the project starts on
the right foot.

The Developer will work with and assist the Analyst / Consultant in two
activities during this phase, Data Conversion and Customizations.

Development Phase

All purchase decisions have been made; it is time to start implementation.


The Development Phase includes the Detailed Design, Coding and Testing
of modifications. It also includes the preparation of the User Documentation
and data conversions.

Often, the development phase can get out of control from a time & cost
4-4 Object Analysis and Development Methodology

standpoint, due to frequent changes or inaccurate estimates. The


methodology contains tools to help manage the development.

The Developer will take the lead during this phase for the Data Conversion
and Customization activities. However, the Analyst / Consultant will still
work with him, especially during the Detailed Design activity.

Infrastructure and Technical Setup Phase

The Setup Phase includes the hardware and software installation and
configuration tasks. The methodology contains many Setup Checklists that
enable the client’s staff to understand how and why the software is
configured a certain way.

The Developer will play a minimal role here, normally just to answer
questions.

Training Phase

End User Training can be executed using different approaches such as


group training classes, one-on-one training or train-the-trainer.

The Developer will play a minimal role here, normally just to answer
questions.

Deployment Phase

This is sometimes called the “Turnover to the New System” or “Go Live”
phase. Successful Deployment depends largely on the previous phases;
however, a Deployment Checklist is included that helps to assure all the
important details are remembered.

The Developer will play a backup role during this phase. They will normally
only take part if something goes wrong at the last minute. Good planning
and good testing will minimize this.

Roles in the Customization Activity

Customization is a team effort. There are four roles to fill on this team. It is
possible that you will have to play one or more roles in this process
depending on your training, experience, special skills and the needs of the
customer. However, for our purposes, we will concentrate on the Attain
Developer's role in Customizations.
Development Methodology 4-5

The four roles are:

The Navision Implementer's Role

The person filling this role is normally a graduate of the "Implementing


Navision" course, a Navision Certified Implementation Specialist. He or she
will lead the process of developing the customer's total solution. This is the
key player in the Training and Testing phase of development.

Also, once the Analyst / Consultant and the Developer have finished their
jobs, the Implementer will actually install the customization at the user site.
For an installation, this is normally done just before they "Go Live" with the
new system.

The Customer's Role

The person filling this role is normally the Controller or one or more other
Key Users employed by the customer. He will work with the Analyst, the
Implementer and the Developer to specify their needs for the system,
approve of any customizations, interim and final testing of the results.

The Navision Analyst / Consultant's Role

The person filling this role is normally also a graduate of the "Implementing
Navision" course, and therefore a Navision Certified Implementation
Specialist, although a senior Attain Developer, depending on the business
practices at your NSC, can also fill it. This person will analyze the needs of
the customer, match those needs with existing Navision functionality and
determine which additional needs must be customized. He or she will work
with the Customer to design the customizations, resulting in a Concept
Document. They will also work with the Developer to design the
customizations.

The Navision Attain Developer's Role

The person filling this role is normally a Navision Certified Attain Developer.
The Developer will get the Concept Document from the Analyst/
Consultant, and with his help, create detailed Design Specifications. He will
then program the customizations, test them and deliver them to the
Implementer for installation.
4-6 Object Analysis and Development Methodology

The Phases of the Customization Activity

The process of customization involves almost every phase in the


Implementation Methodology. Here is how those phases relate to the
Development phases.

Throughout all phases, project management practices apply. The


developer, like all project participants, must be concerned with and follow
team practices with regard to the management of work scope, time, cost,
risk, communication, quality and customer satisfaction.

Project Orientation

From the standpoint of the Solution Center and the Customer, the whole
customization work, indeed the entire implementation, is one project.
However, from the development point of view, a Project is a smaller entity.

For development purposes, a "Project" is a set of development tasks


whose results will all be delivered together. In other words, all of the parts
of one project are deployed at the same time, and before that time, no parts
of the project will be deployed.

During the Concept Phase, a list of customer Requirements will be


developed. Each development project will implement one or more
Requirements, each of which is either one feature, or a closely related set
of features. Each of these development projects will be delivered
separately, possibly at different times.

Phase 1 – The Concept Phase

During the concept phase, Navision Consultant(s) interview selected client


personnel. The client is also asked to provide representative
documentation of legacy systems and may be asked to schedule and
participate in group sessions for gathering requirements.

Using the information provided by the client, the consultant produces for
the client, a detailed document describing the requirements in detail and a
conceptual design of the solution. This document defines the functionality
required to meet the client needs and highlights specific functionality that
will require Navision software modification. From this "Concept Document",
the consultant develops a cost proposal that includes detailed pricing for
hardware, software, media and other Navision solution components. This
proposal also includes a high level quote for services such as training and
Development Methodology 4-7

system modification. The system modification quotation is subject to


revision after the design is completed.

The Focus study

It is also important to note that the concept phase may be performed as


one complete project phase or as two phases. In the two-phase approach,
a concept document, known as a Focus Study is generated for the 2-3
most critical areas to the customer. From this, the client and the NSC will
decide whether to go forward with the rest of the concept document. The
client always approves the complete concept document before proceeding
to development. The Developer’s Involvement.

It is important that the developer participate in the development of the


concept document. Experienced consultants may limit this involvement to
reviewing the conceptual design and providing a cost estimate for
customization. Less experienced consultants may need technical advice
from the developer throughout the concept analysis phase.

The Conceptual Design Estimate

Normally, the price for the development work is not provided as a fixed bid
at this point. However, it is important that the estimate be higher than the
revised estimates that will be provided after detailed designs are complete.
This way we put ourselves in a position to beat expectations. Provide a
worst case estimate and include contingency for risks such as lack of a
detailed knowledge of what is required, modification complexity, area
familiarity, developer experience and industry experience.

Conceptual Design Review

When reviewing the conceptual design, keep in mind that the consultant is
probably not a developer. Challenge the design if there is a lower risk way
to solve the client’s problem, especially one that will avoid development.
Also, make sure that there is nothing in the requirements that will involve
development that wasn’t flagged.

Conceptual Design Tips

The Requirements need to be defined prior to developing the conceptual


design. Each requirement is separately numbered. A conceptual design
must be presented for each requirement. Once the developer is involved,
he may need to talk with the key users and managers within the client
organization to get a clear picture of particular requirements.
4-8 Object Analysis and Development Methodology

The next step is to decide how you will address the client’s needs with
Navision Software. Again, you need to do this for each requirement. Here
are some of the possibilities, in order of ease of development:

1 Already Included - The need can be addressed by Navision software


without modification. Perhaps all you need to do is see that they
purchase the appropriate granule(s) and specify how the software will
be configured to provide the solution.

2 Available Code- The customer may have a need that can be handled
by an existing Navision enhancement, an NSC developed Add-on, or
objects and code from your own NSC’s application library. In this
situation, you will need to show the customer the solution and sell it to
them. Even so, you still might need to customize it.

3 Minor Change - The customer wants pretty much what the base
application or an add-on does, but just needs it changed a little.
Sometimes this means adding a field or two, sometimes it just requires
a name change (to a table or field or label), and sometimes it means
changing the presentation on a form or a report. These kinds of
changes are easy to do and easy to maintain as long as you follow
Navision guidelines.

4 Brand New - The customer wants a feature that is nothing like what it
provided with Navision software. You may be adding an entirely new
module, or maybe just a new report. It probably will have to be
integrated with Attain, but does not require any modification of the base
application or the add-ons. It may take a lot of work to create this
feature, but when new releases of the base application come out, it will
not take a lot of work to upgrade this feature (probably no work at all).
It is important to follow Navision architectural guidelines when
developing these solutions.

5 Major Change - This is what you want to avoid. The customer wants
something that would require a major change to the base application.
Not only is this going to be a lot of work now, but will also be a lot more
work later when the base application is updated to a new release.
Changes would be required to the posting routines, or to the basic
table relations. If you find that this is what the customer needs, try to
rethink it. Perhaps you can turn this into something brand new that you
can integrate with the base application. Or possibly, with a little
imagination and discussion with the customer, you can turn this into a
minor change with special training. Of course, sometimes you are
stuck, and you have to make a major change to meet the customer's
needs. Just be sure to point out to the customer that this will be
Development Methodology 4-9

expensive now and expensive in the future. Perhaps the customer will
come up with an alternative, once they see the cost.

The client will be asked to approve each modification for detailed design
work. The concept document will then be modified to only include the
approved modifications. Normally, some higher risk modifications will be
postponed to a second project to follow the initial implementation. Once the
Concept Document is approved, we can begin detailed design work.

The Add-on Option

One additional consideration is whether you want to develop the solution as


an add-on to be re-sold by other Solution Centers. This will require a
serious discussion between you and the person responsible for strategy
and marketing in your Solution Center. Market demand and risk are key
considerations here. You must be familiar with the rules regarding the
commercial Navision Add-on Program, have sufficient time for the extra
effort involved, and possibly be willing to underwrite part of the
development cost based on the expected returns from the product.

Concept Approval

Client acceptance of the Concept Document signifies approval to go


forward on setting up the system and performing detailed design for
development work.

Phase 2 – The Detailed Design Phase

For development purposes, the Development Phase of the Implementation


Methodology is broken out into two phases: the Detailed Design Phase and
the Programming Phase. During the Detailed Design Phase,
customizations are first designed and priced. If the client accepts the
customization, then it is developed during the Programming Phase.

Note that during the Development Phase, any Data Conversion routines
are also developed. See section 17 for more information on this subject.

A development specification and a detailed development cost estimate will


be created for each product Requirement. These "Design Specifications"
will be accompanied by a User Document, which includes instructions for
using the enhanced functionality and an exercise that will serve as the
acceptance test script for the customization and as a training tool. The
Design Specification is developed by the developer for the developer and
includes the estimate. The Navision Consultant normally develops the User
4-10 Object Analysis and Development Methodology

Document. The Navision Consultant will conduct a walk-through of the


specification and User Document with the client’s key users. Each
specification will be signed-off individually by a client key user who has
completed Key User training.

The Consultant and the Developer together will often times break up a
large implementation into separate Development Projects, each of which
can be delivered at a different time, or possibly at the same time. Each
Development Project will consist of one or more Requirements. All the
Requirements for one Development Project will be in the same Design
Specification document, and all the Requirements for one Development
Project will be in the same User Document.

The Design Documents

The User Document must be readily understood by both you and your
customer since this is the document that will serve as the User
Documentation, training manual, and customer acceptance test script.
Make this as realistic and as much in context to the client’s operation as
possible. Do include in full detail what the customer will see on the screen
and in reports, and how the customer will operate the features. Do not
include any details on how it will be developed technically.

It should provide:

. Descriptions of the look, feel, functionality and performance of the


application.

. Installation and Setup steps.

. A description of the operating process required to execute the


application’s functionality.

. An exercise that illustrates the process and the expected outcome.

One important goal of the User Document is to get the client to object
HERE to what we plan to do, not later after we have invested in
development.

Where the User Document describes WHAT will be constructed from the
user’s viewpoint. The Design Specification details HOW the solution will be
constructed from the developer’s viewpoint. The Design Specification:

. Serves as Instructions to the developer.


Development Methodology 4-11

. Describes the business functionality to be provided.

. Describes all changes to be made to tables, forms, reports, dataports and


codeunits in detail.

. Lists limitations including what you are not going to do.

. Lists customer responsibilities.

. Lists the objects to be modified.

. Includes a time estimate for the work.

Both documents must be complete and address what will and what won’t
be done so that when the customer approves these documents, there is
nothing that is open to interpretation.

These documents are written with the customer. For larger modifications,
you will write, and then you will show the customer, get feedback, and
adjust the documentation accordingly. And remember, be specific. There
should be no ambiguities in these documents. They only cause trouble
later.

Scheduling

These documents may be delivered in series. That is, one may be


delivered, then another, then another, and so on – it is not necessary to
have them all completed before beginning development on approved
designs. The developer will need to work with the Navision Consultant to
develop an efficient “build” schedule for the modifications. A consideration
in developing this schedule is that two Specifications can be worked on at
the same time only if they are entirely independent. In other words, they
can only be worked at the same time if there are absolutely no objects that
are changed in both.

Prototypes

You might want to use a prototype as a tool to communicate a conceptual


or detailed design. A prototype is an application that looks like what the
customer wants, but which does not actually do anything. You might create
tables, fields, table relations and forms, but you need not create any code.
You can simply tell the customer what will happen when the development is
completed.
4-12 Object Analysis and Development Methodology

If the customer likes what you have done with the prototype, you might
even include screen shots and such from the prototype in your Design
Specification or User Document. However, do not let the prototype
substitute for a design. You must still write everything up.

Also, be careful when using a prototype to “sell” the remainder of the


product as part of a Focus Study or Conceptual Design. Normally, a
prototype should only be developed for this purpose if:

. The client agrees they will proceed with the project if the prototype
successfully demonstrates the ability to satisfy the requirement.

. All competitors are required to supply a prototype.

. You have an experienced developer who can produce the prototype with
little risk.

Design Review

It is usually a good idea to review your design internally before presenting it


to the client. Check to make sure your design:

. Addresses the requirements exactly – no more and no less.

. Specifies a “least-risk” solution.

. Is appropriately designed for reuse.

. Follows standard Navision Standards and System Architecture.

. Follows Navision’s coding standards (See Navision Publications).

. Follows Navision’s Low-Impact design guidelines (see Solution’s


Developer Course).

Note the areas of risk in the design and focus attention on those. If you do
not have multiple developers to do this work but feel you need it, you might
consider subcontracting this review to a solution center experienced with
this type of development.

Customer Approval

Once you have a complete project design, you can present it to the
customer for approval. Focus the discussion on the User Document. You
Development Methodology 4-13

and the client need a signed copy. You should never begin development
without written customer approval of your modification design.

Programming of a customization begins immediately upon sign-off of the


specification.

Programming Phase

This is the "programming" part of the development project. However, you


will notice that it is not strictly programming. There are many things to do
before, during and after the actual programming. At the end of this phase,
you will have a product that is ready for testing by the customer for
installation on his system.

Create the Development Database

The next step is to create the Navision database that will actually be used
to develop this project. There are two ways to do this.

First, if this is the original implementation (first project) for this customer,
you must create the database from scratch using the Navision Attain
Product CD. Perform the following steps:

1 Load an untouched database from the Product CD.

2 Download the Latest Improvements attachment for this release,


expand it and import the object file (.fob) into your development
database.

3 Load any other Improvements from the Product Database.

4 If there are any add-ons, import them as well. Note that if there are
multiple add-ons and they conflict with each other (update the same
object), you will have to resolve these conflicts before you can
continue.

5 Export all objects as text, and then import this text file into your
Compare Tool as your base version.

Second, if this is not the original implementation for this customer, but is
instead a follow-up project, you must start with the database that you
ended with at the end of the last Development Project. If there have been
changes at the customer site (either by you or by the customer), you must
4-14 Object Analysis and Development Methodology

perform the following additional steps:

1 Go to your customer site.

2 Select all modified objects. Using the steps described in the


Deployment phase below, give them their own Version Tag and turn off
the Modified Flag.

3 Export these into an object file (.fob) and bring them back to your
office. Don't forget to tell the customer not to make any modifications
on their own until you complete the project. Otherwise, they may lose
these modifications.

4 Back at your own site, import these changed objects into the database
that you ended with at the end of the last Development cycle.

5 Now, export all objects as text, and then import this text file into your
Compare Tool as your base version.

Program

Finally, we can actually start programming! Some things to keep in mind -


For Customizations, be sure to follow your Design Specification. If you find
you cannot, then be sure to change the Design Specification to match your
actual implementation.

All development must be done at your own site, not at the customer site,
especially not if the customer is already live. However, for lengthy
development tasks, you should show the customer your progress once in a
while (at LEAST every other week) so that you can get their feedback.
Either bring it to them on a portable computer, or invite them to your site to
show it. If you get some feedback, check the feedback against the User
Document. If you did it differently than the User Document says, fix it. If not,
you (and the Consultant) will need to decide what to do next:

. For a larger change (or even a small one) you could decide to record the
request and add it to another project.

. A small change might be handled with a change order.

You must do your internal documentation and your Project Log (as
described in the Documentation part of this section) as you are
programming. Do not wait until you are finished and then try to do this after
the fact.
Development Methodology 4-15

Internal testing

You may decide you need to perform a code review prior to performing
testing. This is intended to make sure you followed sound coding practices.
Whether or not you performed this review, you should always test each
feature as thoroughly as you can as you complete it. Make sure your tests
include the test exercises included as part of your User Document. You
should be testing both to find bugs and to make sure that the results match
the User Document. If you have the customer's license, be sure that at
least part of your testing is done using the customer license.

Also, be sure to include some "regression" testing in your process. This


means to test the normal, unchanged features of the product, to be sure
that they still work correctly after your customizations. Concentrate this kind
of testing in the areas of the product that are at risk, particularly those that
are being modified and also those that interface with those that are being
modified.

Considerations for Multiple Programmers

Sometimes a project is big enough where you will have to have multiple
programmers working on it at the same time. If you can possibly prevent
this, do so. For example, you could have one programmer writing the
Design Specification, while another programmer does the actual
programming (coding) and a third does the testing. Once the first
Requirement was designed, they could be working at the same time,
without interfering with each other.

Sometimes, you just have to have multiple programmers. If this happens,


you should use the following procedures to avoid problems.

The project should be designed so that different Requirements can be


assigned to different programmers. If two Requirements are to be worked
on at the same time, there must be no objects in common. If there are
objects in common, then that part should be treated as a separate
requirement and programmed separately before the other requirements are
assigned and worked on.

Appoint one developer to be in charge of the development database.


When a programmer is about to start coding on a feature, give them a copy
of this database and a list of objects they are allowed to change (the
Design Specification should already contain this).

When the programmer is finished (including testing), they should filter


export all of these objects and give them to the developer in charge as an
4-16 Object Analysis and Development Methodology

object file (.fob).

The developer will then import these changed objects into the development
database. There should be no conflicts since the programmer only changed
the objects that he was assigned to. Don't assign the same object to two
programmers!

When that programmer is ready to work on the next feature, they should
again get a fresh copy of the development database. This is because other
programmers may have checked in their work in the meantime, and their
next feature may depend on the other programmer's work.

User Documentation

The Consultant should update the User Document during this phase as
necessary. If you end up changing functionality, that will require
documenting the changes. If you agreed to provide additional
documentation, you should also do that during this phase. As each feature
is completed and tested, the documentation for it should be written. This
can include user manuals, on-line help, training materials and other
documentation.

Phase 3 – The Testing Phase

Up until this point, you have been testing the objects you have developed
as you go. In addition, you have been getting feedback from your customer.
However, we have now reached the point where we are ready for the
customer to run the new software on their own, at their own site, for
acceptance testing.

This development phase generally takes place during the Training Phase in
the Implementation Methodology and, indeed, this is the time for the
Implementer to train the Key Users and other actual users in the new
functionality.

Customer Acceptance Testing

When the development phase looks complete, then bring the completed
development database to your customer site. Set it up as local database for
each person who needs to look at the completed system. Give the
customer a short training course, and turn them loose on it for testing.

Note that you are NOT installing this on your customer's server or on any of
his live data. This is just for testing purposes.
Development Methodology 4-17

Also note that this testing must be done with the customer's license. If you
have delayed ordering changes to the license for cash flow reasons, the
delay is now over. Any testing done by the customer must be done with
their license, so those problems with the license can be uncovered.
Besides, you must never leave your developer's license or your Solution
Center's license at a customer site for any reason whatsoever.

A key client user who has been through key user training must perform the
test. Have the key user walk through the test exercise in the User
Document.

The customer may find some bugs. If so, review them to make sure they
are really bugs, that is the User Document test scripts will not run properly.
Do not accept a change in requirements as a bug. Also, make sure all valid
bugs are logged and tracked in the project risk/issues log and fix them at
your site. Once rework is complete, bring the copy over again for further
testing. It is usually a good idea to track bug rates and hour expenditures.
Large numbers of bugs usually indicates a design, not a programming
problem.

Once the test executes as specified in the User Document, you should
obtain client sign-off and move on to Deployment. Sometimes, the client
waits until this point to decide they really wanted different functionality. If
so, you should bill the client for your completed work and issue a change
order to conduct new design and development work using the process
described here.

As with the design, you must get the customer to sign off on completion of
the work so that you can then proceed to the Deployment Phase. You must
not implement your changes in the customer's live system without this
approval.

Phase 4 – The Deployment Phase

With development completed and approved on a Development Project, you


are ready to install the new or modified object(s) on your customer's site.
For live Navision customers, this step is the most critical, since you can
affect the actual live data of the customer. Until this step, nothing you did
could cause any permanent harm.
4-18 Object Analysis and Development Methodology

Getting Ready to Go On-Site

Follow these steps to create the installation object file (.fob):

1 Select and compile all objects.

2 Filter on all objects whose Modified flag is checked.

3 On each filtered object, add the Version Tag (see the Version Control
part of this section) to the Version List.

4 Now, filter on all objects whose Version List contains the Version Tag
"*<version tag>*"

5 Remove the filter on the Modified Flag.

6 Turn off (uncheck) the Modified Flag on all objects.

7 Select all of the (filtered) objects.

8 Export using the Navision Attain format into a file named <version
tag>.fob. This is your Installation Object File. Save this permanently.

9 While you still have all filtered objects selected, export using the Text
format into a file named <version tag>.txt. OR

10 Do a Show All (remove all filters), select all objects, and export using
the Text format into a file named <version tag>.txt. Use this second
alternative if the project was a large one and you are expecting another
large project soon.

Now, import this text file (created using either method) into your Compare
Tool and generate your Change Log into a file named <version tab>.log.
Save this permanently.

At the Customer Site

There are two methods of installing at the customer site. If the customer
has never had a live Attain database installed on site yet, you will:

1 Make a complete backup of your development database.

2 Bring the backup file (.fbk) to the customer's site.

3 Install Navision Attain (both client and server) on the customer's server
Development Methodology 4-19

in the usual manner (see the Installation Guide for more info on this).

4 Open Attain in local mode on the server.

5 Create a new database. Be sure to make it large enough, as the


customer will start adding live data after this point.

6 Restore the backup you made of your development system.

7 Exit the client. Then, start the server, pointing it to your customer's new
database.

If the customer already has Navision Software installed, you must select a
good time to do this installation. It can take a long time and nobody must be
on the system the whole time you are installing it. Perhaps a Friday
afternoon, continuing on Saturday.

Also, before you show up, remind the customer that any changes that were
made to their objects since you got them in the first place are subject to
being lost (giving them a chance to back up or document). You and the
customer can go over the list of objects that you changed so that they know
which changes will be lost. Here are the steps you should follow:

1 Bring the Installation Object file (.fob) created above to the customer's
site.

2 Bring down the server (nobody must be on when installing any objects
like this).

3 Make a complete backup of the customer's database, either using the


Navision Attain backup utility, or simply copying the entire database
elsewhere.

4 Bring up the customer's database using the client on the server


machine.

5 Go into the Object Designer. On the Menu Bar, select on File and then
click on Import. Select the Installation Object file you brought with you.

6 There may or may not be conflicts. Even if not, be sure to bring up the
Import Worksheet dialog.

7 Press the Replace All button. Then press the OK button.


4-20 Object Analysis and Development Methodology

The import process may take a while. Especially if you have added or
modified any table keys, since these will all have to be rebuilt.

By the way, if you want to install your project while retaining any changes
that have been done to the customer's database, you must follow the same
procedures as you would for an upgrade. This is covered in another section
of this course.

Once you have completed your installation, go into the Object Designer,
select all objects, and recompile them. This will show any problems
between the modified objects and the unchanged ones.

The Final Test – Setup and System-wide Testing

After installation, the Consultant will coach the client through the remaining
product setup steps and, for new installations, once all setup work is
complete, through the implementation of a short beta and cut-over. The
developer should not be needed during these steps but should budget a
few hours of time in case bugs are found during integrated system testing.

If a bug is found during this phase, you will need to treat it exactly the same
as if the bug were found before you installed. Again, be careful to make
sure it is within the scope of work as described in the User Document. If it
was serious, you should restore the customer's database from the backup
you made. If not, let them continue to work, but it is still not considered
installed. Go back, fix the bug(s) and re-install. Obviously this is a huge
pain. But in reality, it should not happen, since you and the customer
should have already thoroughly tested it.

Final Customer Approval

The project is not considered complete until the customer has given final
approval. However, this is due once all setup processes are signed off, all
development is approved and all bugs are resolved. Usually, once you
have final approval, you would bill and collect any remaining money the
customer owes you.

Phase 5 – The Maintenance Phase

This phase corresponds to the Ongoing Support Phase in the


Implementation Methodology. There are two areas of concern for the Attain
Developer.
Development Methodology 4-21

New Customer Requirements

You may not have implemented the entire customer Requirements yet. Or
the customer may very well come up with new requirements. Each new
Development Project is handled as described above. It is highly
recommended that you not make "on the fly" changes at your customer site
at any time. Even if it seems like it would be easy to do, it will cause
problems in the long run. Better to keep it formal and keep everything well
documented.

Updating for New Release

Navision Software will also be working on improvements to the product. No


matter whether it is a new release, a service pack or just a product
improvement, you should try to keep your customers up to date with the
latest programs from Navision. When any Product Improvement or Service
Pack is released, look it over and see if it affects anything that your
customer purchased. If so, schedule a time to install it. If not, wait to install
until something else is changed which your customer purchased. Then
install both. Never skip an Improvement or Service Pack completely. They
must each be installed in order so that they all work together properly. The
only exception is that a Service Pack generally includes all Improvements
since the previous Service Pack or Release, so you do not need to install
both the Service Pack and the previous Improvements.

When there is a new release, you should try to upgrade the executables as
soon as possible. The application can be upgraded on a scheduled basis
when you have time. Normally, a new release will have many more
changes and will require data conversion and so on.

Of course, these update instructions only apply to those customers that are
on the maintenance plan. With this plan, they get the new code and
software for free, but still must pay the Solution Center to install the
changes. There is more information on how to install upgrades in a later
section of this course.
4-22 Object Analysis and Development Methodology

4.2 VERSION CONTROL

When you are delivering a product to a customer, you may have performed
several modifications. You might have updated the objects with an
improvement you obtained from Navision. You might have an add-on that
you or some other Solution Center created. You have your own
customizations for this customer. Plus, you might have changed the
customizations for this customer.

You have a lot to keep track of. This collection of changed objects is unique
and you need to know where all of these objects came from, and that is not
all. Suppose, that installing an add-on changed an object and customizing it
for your customer further changed it. Now, what will happen when Navision
releases yet another improvement that affects this object? How do you
know which object can just be loaded on and which will take more work
than that?

It is for these reasons that you need a method of Version Control.


Fortunately, in Navision Attain, there are various tools built into C/SIDE that
will help you with your version control. But you must know how these
features work in order to use them effectively. This is what we will be
covering in this section.

Modification Flag

The Modification Flag is a kind of automatic version control. Whenever you


change an object, the modification flag is automatically checked. The only
way it is unchecked is if you deliberately uncheck it.

This is an excellent tool to use while you are in the process of developing a
project. Then, once you have completed the project, you can select all of
the modified objects and set the version tags (see below). Once you have
done that, you can turn the modification flags off. This way, you can tell
automatically if anything has been done to change the objects since the
project was completed.

If you make modifications at the customer site, or if the customer has the
design tools, the modification flag is a good way of telling that changes
have been made on site. Therefore, in either of these situations, it is
strongly recommended that you do not uncheck the modification flag.

One thing to watch out for - Occasionally, the only modification you make to
an object is to change the object name, which you can do from the Object
Designer. If you do this, the modification flag will not be automatically set
Development Methodology 4-23

(nor will the date and time stamps). Therefore, it is recommended that you
actually call up the object (by pressing the Design button) and change the
Name property of the object. Then, when you save it, it will update
everything automatically.

Version Tags

The "version tags" are located in the Object List in the column called
Version List. In order to work correctly with the Import Object utility within
Attain, the version tags require a fairly standardized format. Therefore, first
we must cover how the Import Object utility uses the version tags.

Import Objects

When importing objects, the Import Object Utility checks both the imported
object and the matching object in the database, and checks both the
Version List and the Modification flag. It considers the Version List to be a
list of version tags separated by commas. Thus, NAVW13.00,NS01.01 is
considered two version tags, the first is NAVW13.00 and the second is
NS01.01. Let us first consider a Version List with only one version tag.

Existing Object: Existing Object: Imported Imported Result: Default Result:


Modification Version Tag Object: Object: Version Action Conflict
Flag Modification Tag
Flag

No NAVW11.10 No NAVW11.00 Skip No

No NAVW11.10 No NAVW11.10 Replace No

No NAVW11.10 No NAVW11.11 Replace No

No NAVW11.10 Yes NAVW11.00 Replace Yes

No NAVW11.10 Yes NAVW11.10 Replace No

No NAVW11.10 Yes NAVW11.11 Replace No

Yes NAVW11.10 No NAVW11.00 Skip No

Yes NAVW11.10 No NAVW11.10 Skip No

Yes NAVW11.10 No NAVW11.11 Replace Yes

Yes NAVW11.10 Yes NAVW11.00 Replace Yes

Yes NAVW11.10 Yes NAVW11.10 Replace Yes

Yes NAVW11.10 Yes NAVW11.11 Replace Yes


4-24 Object Analysis and Development Methodology

Note that the Default Action of "Replace" is sometimes "Merge" when you
are dealing with a Table Object.

The situation with multiple version tags is similar, except that each version
tag on the imported object is compared with its corresponding version tag
in the existing object to discover the differences. If there is a version tag
missing, it is considered to be an earlier version than one that exists. The
first is compared with the first, the second with the second, using the above
table. If there is a conflict with a version tag, then there is a conflict on the
import. If any tag calls for a replace, the import calls for a replace.

Navision NTR usage

Now this is how we use version tags at the Navision NTR level for the base
application. The first version tag is usually the world wide base application.
It consists of parts; if the version tag says NAVW13.10.01.25, the "NAV" is
the product (Navision Attain), "W1" indicates that it is a world wide object,
"3" is the major release, ".10" is the minor release, ".01" is the maintenance
release (service pack), and the ".25" is the unreleased (but published)
hotfix number. In all cases, before we publish a release or an improvement,
we turn the Modification flag off. If a level is missing, for example,
NAVW13.10, the numbers are left off. The major and minor release
numbers are always present, but there may not be a service pack or hotfix
as of yet. Since the import utility uses an ASCII compare (character by
character), NAVW13.10 is considered earlier than NAVW13.10.01.

Many objects are added at the NTR during our localization process. If a
base application object exists only in one country, it can of course be
recognized by the fact that its object number is in the country's special
range. It can also be recognized by the version tag, which will say (for
example) NAVUS3.10.01.25. Note that this is exactly the same as the
version tag in the above world wide example, except for the 4th and 5th
characters. The "W1", which indicated world wide, has been changed to
"US" indicating that this base application object is from the United States.

In addition to adding objects during localization, we also modify world wide


base application objects during localization. In this case, there will be two
version tags, one the world wide tag, and the second the NTR tag. For
example, an object might have NAVW13.10.01.25,NAVUS3.10.00.02 in the
Version List field. The first (before the comma) is the world wide version
tag, and the second (after the comma) is the NTR version tag.

For each other product we create (like Commerce Portal), we create a new
version tag as follows: CPOW12.01.02.09, where "CPO" is the three letter
Development Methodology 4-25

product code, "W1" indicates it is world wide, "2" is the major and ".01" is
the minor release number, ".02" is the service pack, and ".09" is the
improvement number. If a base application object is changed, we add the
other product's version tag to the end of the base application version tag,
separated by a comma. If this is a new object, we just use the other product
version tag. In either case, we turn the modification flag off before we
publish any product.

If an NTR creates a separate product, the same rules will hold, except the
the "W1" would be replaced by (for example) "US", indicating the NTR.
Note that in our examples, each of these tags has a "US" in it, to indicate
that it is a United States object. Each country in the Navision World uses
their own two-character country code in their version tags, like "DE" for
Germany, "UK" for United Kingdom, and "DK" for Denmark. The worldwide
version of the application has its own two-letter code, "W1".

Attain Developer Usage

Finally, here is our recommendation on how you should use these version
tags. You should always leave our version tags alone! As you are making
changes, you should leave the modification flag on, and only turn it off
when you have created a new version tag and set all changes to that tag.
Never turn the modification flag off unless you change the version tag. If
you are making minor modifications at a customer site, you do not even
need to create a version tag: just leave the modification flag on.

If you are creating a major modification for a customer, then you should
create a version tag for that customer. Use 2 or 3 characters to represent
the customer and add a "00" (two zeroes) to it. Whenever your
modifications are stable (working and you will not change it for a little
while), you should filter on the modified flag set to yes, and add the new
version tag to the Version List. If it is a base application object, separate
your new tag from our existing one with a comma (no space). If it is a new
object, just use your version tag (erase ours if you copied from one of our
objects). Now, remove the filter from the modification flag, and add a filter
to the Version List, which will be your version tag preceded and followed by
an asterisk (e.g. *XYZ00*"). You can now turn all the modification flags off.
Then, without removing the filter, select all objects and export them all into
an fob file named the same as your version tag (e.g. "XYZ00.FOB"). When
you make a major upgrade for your customer, you would perform the same
steps, except that you would use the next number (e.g. "00" -> "01"). Any
object that changed (even if it had never been changed before) would
receive the new version tag. If one of the customer's version tags was
already on the object, it would merely be changed to get the new number.
4-26 Object Analysis and Development Methodology

If you are creating your own add-on product, a similar process would take
place. The only difference would be that you would take the letters from
your own Solution Center name, and the name of the add-on product, to
create the version tag. Thus, like the base application, there will be a 3
letter add-on product code, followed by a 3 letter NSC code (not 2, since it
might be the same as a country code), followed by the version numbers as
needed. For example, if your NSC was named "Computer Technology
Innovators", and your add-on was named "Bowling Alley Management",
then the first version of this add-on might have a version tag of
"BAMCTI01".

Also, we would recommend bringing your working database up to the latest


released version of Attain, including all published hotfixes, just before you
put your version tags in. This way, any base application object that you
have changed will get the latest base application version tags. Of course, if
you have to apply the change log manually (since you already modified the
base application object with your add-on), you must set our version tag
manually as well. Don't forget to export all changed objects into a single fob
file once you have set all the version tags and turned off the modification
flags. This is the file you will probably ship out to purchasers of your add-
on.

When you are ready to upgrade your add-on product, take your original
version tag and increment the number for each major release. If you are
making a minor release, add a ".01" to it for the first minor release, ".02" for
the second, and so on. Thus, your original add-on product described above
might have a version tag of "BAMCTI01", the first minor modification would
be "BAMCTI01.01" and the second would be "BAMCTI01.02". The next
major release would be "BAMCTI02".

When using multiple version tags, the order is quite important, since the
import utility compares 1st with 1st, 2nd with 2nd, and so on. If the order on
an imported object is different from the order on the existing object, the
comparison will not be done correctly. On the other hand, if the order on
one object is different from the order on a different object, that will not
matter, since each object is compared individually.

However, for consistency and ease of updates, we ask you to use the
following order for version tags:

. Base Application version tags (first world wide, then NTR).

. Other Navision Product version tag.

. Add-On version tags. The specific order is unimportant, but should be


Development Methodology 4-27

consistent. We suggest the following:

o Other Solution Center Add-Ons

o Your own Add-Ons.

. Custom modifications version tag.

This is normally the order you would add them anyway, so it should be
convenient.

Here are some other examples:

A released base application object from an NTR:

NAVUS3.10
The same released base application object, with hotfix number 23:

NAVUS3.10.00.23
The same object, with the 2nd upgrade of your add-on applied:

NAVUS3.10.00.23,BAMCTI02
The same object, installed on your customer site and then customized for
that customer:

NAVUS3.10.00.23,BAMCTI02,XYZ01
4-28 Object Analysis and Development Methodology

4.3 DEVELOPMENT DOCUMENTATION

In the above discussion, we talked about many different documents that the
developer must look at and write. Let us go into more detail on each of
these.

There are several kinds of documentation that we will be describing here.


However, they all have one purpose in common. This documentation is for
the benefit of the Attain Developers. To be more specific, the developers
will use this documentation for the following purposes:

. Organization - The Attain Developer, as part of the development


process, must organize the changes to be made, and organize the
implementation of them.

. Audit - To create a permanent record of what changes were made, who


made them, when they were made, and why they were made.

. Communication - In projects where there is more than one developer,


your documentation is what will keep everyone on track.

. Upgrades - When a new version is released, your documentation will be


indispensable in upgrading your customization to the new version.

There are other kinds of documentation that you will be reading and may
be writing as a Attain Developer. These include Concept Documents, User
Documents, user manuals, proposals, quotes, and so on. These subjects
are covered minimally in this class. We are concentrating on the
documentation that is used strictly by and for the Attain Developers in your
organization.
Development Methodology 4-29

Project Orientation

The documentation that you will write will be based on a project. When you
are going through your Concept Document, you will first divide the job into
various projects. Each project will implement one or more "Requirements"
from the Concept Document, each of which has one feature, or a closely
related set of features. In many cases, a set of modifications for a customer
will become a single project. In some cases, the modifications are so
extensive and complex that you will have to break the job into multiple
projects. Each of these projects could be delivered separately and possibly
at different times.

Once you have determined what is included in your project, you will then
write a Design Specification. This is not the User Document. A Design
Specification is used by a developer and to communicate between
developers. However, the customer will review this document and approve
it.

Once you have a Design Specification, you will then start working on
implementing your project. While you are doing this, you will be creating
documentation internal to each object and you will be creating a Project
Log.

Concept Document

The Concept Document is a document created by the Navision Analyst /


Consultant together with the customer, to specify the customer
requirements and what the Navision Solution Center will do to meet those
requirements. It will contain information on what hardware needs to be
purchased, what Navision granules will be purchased, what customizations
will be done and what other services will be done. For more information on
developing this document, refer to the Implementing Navision course.

For the developer, the most important things here are the requirements that
involve customizations. Each Requirement has a section number (like
4.2.7) which is to be used as a reference in other documents to refer to the
requirement in the Concept Document.

Depending on your organization or experience, this document is the


equivalent of a "Functional Requirements Document" or a "Project
Specification".
4-30 Object Analysis and Development Methodology

User Document

The User Document is a document created by the Navision Analyst /


Consultant, together with the customer, to specify what will be changed in
the product to meet the customer's Requirements. It goes into more detail
than the Concept Document, in that it fully explains to the customer what is
being changed, how it will look when it is done, and how the customer will
run it once it is deployed. For more information on developing this
document, refer to the Implementing Navision course. Like the Design
Specification (see below), the User Document will have a different
"chapter" for each individual customer Requirement that is being handled
by a customization. The Requirement number used in this document is the
same one that was used in the Concept Document, so that everything ties
together.

The developer should note that this document will often include "test
scripts", which will be used by the customer to verify that the customization
has been done correctly. The developer should be familiar with the
contents of the User Document (and will probably help in developing it) so
that he knows what the customer will expect when the customized product
is deployed.

Design Specification

The Design Specification is a document created by the Navision Attain


Developer, together with the Consultant, to specify the exact changes that
will be made to the product in detail. It takes a different point of view than
the User Document and Concept Document, since those are written from
the point of view of the user. The Design Specification is written from the
point of view of the programmer.

As already mentioned, the Design Specification is used by a developer and


to communicate between developers. Whenever you write a design
specification, you should write it as though you were telling another
programmer how to make the modifications, even if on this particular
project you will be doing it all yourself. A design specification must be
detailed and organized, and writing it will help you with your thinking
process. Later, when you are trying to figure out a particular section of
code, your design specification will tell you the why and how of that code.

A design specification can be in any format that accomplishes the primary


goals of organization and communication. However, we have a sample
format which accomplishes these goals, without overdoing the details. You
may use it and adapt it to your own purposes and style. On your class CD
is a Word 97 template called "DesignSpecification.dot". The best way to
Development Methodology 4-31

use this is to copy into your Templates directory, usually found under
"MSOFFICE" on your C drive. Once this is done, when you create a new
document, it will appear as one of the templates and you can select it.
Create a document using this template, and then follow along as we go
through the various parts.

Document Properties

The first thing you should do is fill in certain document properties which are
used in the form itself. Using the Menu Bar, select File and then Properties.
On the Summary tab, the following fields are used:

. In the Subject field, fill in the name of the project as a whole. This will
appear on the cover page.

. In the Company field, fill in the name of your Solution Center. This will
appear in the heading on every page. You may just want to fill this in
directly in the template.

On the Custom tab, there is a table in the middle that lists the Name, Value
and Type of the various properties. One of these properties is named
"Client". Click on Client in the Properties table, and the Name, Type and
Value will be displayed in the boxes above the table. Change the Value
from "Customer Name Here" to the name of your customer, and then press
the Modify button. This name will appear on the cover page. Press the OK
button to leave the Properties dialog.

Requirement Header

Each Requirement in the project will get a separate section in the Design
Specifications, each one starting on a new page. It is recommended before
you start filling out a new section that you first make a copy of the last
section, so that you always have a blank section on the end to use when
creating the next Requirement.

The title of the page includes the Requirement Number and Requirement
Description. The number should be taken from the Concept Document
section number where the Requirement is first described. The Description
should be a 2 or 3 word title, just to identify the requirement. The other
parts of the Requirement Header should be filled in as follows:
4-32 Object Analysis and Development Methodology

. The Summary field should have a 1 or 2 line description of the


Requirement.

. The Status field should indicate the current status of this Requirement in
one word.

. The Assigned To field should indicate the name of the developer in


charge of this Requirement.

. The Hours Estimated field should be taken from the total in the Estimate
part of this Requirement.

. The Estimated Labor field should be the amount you expect to bill for
labor. It could be the number of hours estimated multiplied by an hourly
rate, or it could be a fixed bid.

. The New Table Objects field should include the number of new table
objects being added for this Requirement.

. The Other New Objects field should include the total of the number of
form, report, codeunit and dataport objects being added for this
Requirement.

. The New Object Cost field should be the cost to the customer of the
above new objects.

. The Estimated Billing field should be the sum of the Estimated Labor and
the New Object Cost fields.

Don't forget that there is a major price break for buying 10 tables objects or
100 of any other Objects at the same time. If you can take advantage of a
price break, prorate the cost of the objects over the entire project. For
example, if you have 5 Requirements each requiring one table object, the
normal cost of each table object would be $240. However, taking
advantage of the price break of 10 tables for $720, the price allocated to
each of the 5 Requirements for their table object would be $144, not $240.
This is because the $720 price is divided among 5 tables. Note that the
customer ends up with 5 unused table objects for future projects or for his
own use.

Technical Specifications

This is the actual detailed specification portion of the document. We have


divided it into 4 parts to remind you of the information needed, but you can
set it up any way that works in your organization.
Development Methodology 4-33

Here is where you get down to brass tacks. On an object-by-object basis,


describe the changes that you will be making. Make sure you identify each
object by type, by number and by name. If it is a new object, describe what
it is for, what it will do, and what the programmer will do to create it. If it is
an existing object, describe what the changes are, and what they are for.

You obviously do not need to record the actual code changes (after all, you
can always look at the finished object for that), but you should use sufficient
detail that you could hand to an entry-level programmer and have them
complete the programming correctly. Sometimes that means going into
great detail, while other times it requires only one sentence to describe the
changes.

Limitations

Here is where you specify what you will not be doing, if that is necessary.
You will also describe what parts of this are being left to the customer to
do. If some functionality is being delivered in stages, state here what parts
will not be available until another phase.

Estimate

Once you have completed the Technical Specification, you ought to be able
to give a fairly accurate estimate of what the hours will be to complete this
work.

Impact on Upgrades

This is where you specify what the impact of these changes will be when it
comes time to install an upgrade. This is important as the customer figures
out the total cost of ownership. Include an estimated time to upgrade.

Approval

Indicate to the customer how they should let you know that you can go
ahead with your work. Some sort of written, signed approval is
recommended. Once it is approved, attach the approval document(s) to the
end and record here when it was approved, who approved it and where the
approval document can be found.
4-34 Object Analysis and Development Methodology

Objects

As you actually program the modifications, you should enter in this section,
a list of the actual objects changed and created. Include the Object Type,
Object Number and Object Name. If this is a modified object, indicate the
base version tag before you made your changes otherwise indicate "New".

Attachments

If there are any attachments at the end pertaining to this Requirement,


include them here. If not, erase this part.

Revisions

While implementing the changes you have described, you may find that
you need to change the design. Should this happen (and it often does), you
should revise the Design Specification to reflect these changes. When you
finish, the Design Specification must match the program and the program
must match the Design Specification.

If the change affects the functionality, you will probably need to get
customer approval to make the change. Try to keep these kinds of changes
to a minimum.

Whenever you make a revision to the Design Specification, create another


Part at the end of your Design Specification, describing the change, when it
was made, and why you made it.

To facilitate this, you may want to take advantage of some of Word's


capabilities in tracking changes to documents. Here is how:

. Save Original Version - Go to File, Versions, and press the Save Now
button. Enter a description indicating that this is the original version
approved by the customer.

. Track Changes - Go to Tools, Track Changes, Highlight Changes, and


then check the box that says Track Changes While Editing.

. Make Your Changes

. Review - Print out the specification showing changes. Review with


Consultant and/or Customer as necessary.
Development Methodology 4-35

. Add Revisions Part - Add a new Part (using the Heading 2 Style) to the
end of the Specification called Revision. (whatever revision number it is),
and describe the revision in that part.

. Accept Changes - Go to Tools, Track Changes, Accept or Reject


Changes, and accept all changes.

. Turn Off Track Changes - Go to Tools, Track Changes, Highlight


Changes, and then remove the check mark in the box that says Track
Changes While Editing.

. Save New Version - Go to File, Versions, and press the Save Now
button. Enter a description indicating that this is a revision and which
revision it is.

Internal Documentation

By "Internal Documentation", we mean the documentation that is written


directly into the objects themselves. These include code comments, the
Documentation Trigger of each object and Description fields in the field
definitions. Note that there is some difference between the internal
documentation for a new object, and that for a previously existing object.

Documentation Triggers

If you will look at the code within each object, you will note that there is a
Documentation trigger as the first trigger within the object. This trigger is
never executed, so it does not need to contain actual code. Instead, it is
designed so that you can enter comments about the object as a whole
there.

The use of the Documentation Trigger for a new object (being created for
this project) is optional. If you use it, you should do it the same way as
described below for an existing object. The only difference is that the
"sequence number" should be "00".

For an existing object (one that was in existence before this project), you
must create a heading in the Documentation Trigger, for each modification
you make. Usually, this amounts to one per feature. This heading should
contain some sort of Reference Number, the date when the modification
was completed, who did the modification (Solution Center Name and
individual name), the project, and a short description of the change. Here is
an example:
4-36 Object Analysis and Development Methodology

NJD01 - Navision, John Doe, 02/12/00, Multi-state Payroll


Added the new State and Locality fields.
Modified the trigger code to default these
fields whenever the Employee No. is entered.

Note that the Reference Number (NJD01) is a combination of the initials of


the Solution Center, the initials of the developer, and a sequence number.
This sequence number should start with 01 for each object, so that the first
modification of this object has the number 01, the second modification of
this object has the number 02, and so on. The only exception is that, if this
is a new object, the sequence number should be 00.

Code Comments

There are many theories about how to comment your code, and for normal
purposes (explanations, and so on), you should use whatever method you
think is best. However, for our documentation purposes, you need to mark
each modification with a comment using a special method. This should only
be done when you modify an existing object, not when you create a new
object.

The key is to mark the changed code with the same Reference Number as
you used in the Documentation Trigger of this object. For example, if you
have modified a single line of code, you should mark it like this:

State := "Employee."Default Work State"; // NJD01


If you have added or modified an entire block of code, you should mark
your change as follows:

// NJD01 Start
State := "Employee."Default Work State";
Locality := "Employee."Default Work Locality";
"Work Type Code" := Employee."Default Work Type Code";
// NJD01 End
If, as part of your modification, you have deleted a large block of code, you
should mark this deletion as follows:

{ NJD01 Start Deletion


State := "Employee."Default Work State";
Locality := "Employee."Default Work Locality";
"Work Type Code" := Employee."Default Work Type Code";
NJD01 End Deletion }
Note that this will keep the old code in place, just commented out.

If you have a standard method of commenting your code so that you can
tell how the code works, or what it is doing, you should continue to use that
method. This modification marking is in addition.
Development Methodology 4-37

Field Descriptions

Whenever you add a new field to an existing Table Object, or modify an


existing field in a Table Object, you should mark that field with the same
Reference Number that was recorded in the Documentation Trigger of that
same object. Simply entering the Reference Number into the Description
property of the field should do this marking. Do not do this if you are
creating a new Table Object.

Project Log

The Project Log is your record of everything that has been done for this
customization project as a whole. It should be created while you are
actually doing the modifications. Once you have completed your Design
Specifications, you should create your Project Log as a plain text file and
give it a header that contains the following information:

. Name of Project

. Solution Center

. Name of Project Leader

. Customer

. Date Started

. Date Completed

. Name of Design Specification document

. Version Tag

. Name of Change Log

. A short description of the project (remember, this information will already


be in the Design Specifications).

The Project Log Heading can be in any format you want, but here is an
example that you may use:

Multi-State Payroll Navision John Doe


Customer: Acme Widgets Date Started: 4/08/98
Date Completed: 4/23/98
Version Tag: NS012.01
Design Specification: multi-state.doc
Change Log: multi-state.log
4-38 Object Analysis and Development Methodology

Add ability to have Employees work in multiple states.


Add ability to have Employees work in multiple localities.
Add ability to enter Work Types, and to have Employees
who use multiple Work Types.

After this will be the log itself. There will be a main heading for each object,
which includes the object type, number and name. This heading will also
include the Version Tag of the object before it was changed, or if it is a new
object, the words "New Object". The various object headings should be
entered in order, so that they are easy to find.

Following the object heading will be the log entries. The easiest way to
create these log entries is to copy the information that you wrote into the
Documentation Trigger of the object and indent it. This way, you will have
the Reference Number, the programmer, the date, and a short description
of the modification (you can erase the project name and the Solution
Center name if you wish). Here is an example:

Table 10077 - Time Journal Line - NS012


NJD01 - Navision, John Doe, 02/12/00
Added the new State and Locality fields.
Modified the trigger code to default these fields
whenever the Employee No. is entered.
Here is an example of a new object:

Table 10082 - Split Information - New Object


NJD01 - Navision, John Doe, 02/12/00
New table which links a Split Control with the
various Substitute Controls for a single Employee
or for a standard split (indicated by a blank
Employee No).

Change Log

A Change Log details all of the changes you made for a Project. It tells
another developer, without any ambiguity, every single change that must
be made to go from one version of the product to another. You must create
a Change Log so that other developers can see exactly what was done to
the objects. Even more important, you must have these Change Logs
available when it comes time to upgrade your customizations to a new
release of the Navision product.

There are three recommended methods of creating a Change Log. All three
use plain text files for a simple reason - by using plain text files, it is easy to
copy changes from the Change Log and paste them directly into a Navision
Object.
Development Methodology 4-39

Manual Change Log

The best method for creating a Change Log is to create it manually. Using
a manual change log, you can describe exactly how to make the changes
using C/SIDE, plus for code changes you can copy and paste entries into
C/SIDE, rather than using the text format.

Naturally, the big disadvantage to a manual change log is the time that it
takes to create one. Therefore, we only use it in house for Attain
Improvements, where we know that they will be used many times. And for
Solution Centers, we only recommend a manual change log if you are
making an Add-On product, again, where it will be used many times.

To help you with this, on your class disk there is a file called
TEMPLATE.LOG, which contains the various instructions and templates
that we use to make manual change logs here.

Automatic Change Logs

We also provide two methods of making Automatic Change Logs. They


both work using the text files that you can export from C/SIDE. They
basically compare the text lines and print a report showing the differences
between them. Both of these methods were developed using C/SIDE.

The advantages of an automatic change log are the speed and ease of
creating them, and the completeness of the results (as long as you know
which objects were changed). The disadvantage is the long time it takes to
actually use them and the difficulty in understanding them. For example, if
you insert a single field into a tabular window, it will generate many pages
of changes, since each column had to be moved to make room for a new
field. Once you are used to using these change logs, and you can easily
read the exported text objects from Navision, this disadvantage decreases.

One of the two Automatic Change Logs is the Compare Tool. We have
included the Compare Tool on your class disk, along with documentation
explaining how to use it. It makes it easier to understand Change Log than
the alternative. The Compare Tool is recommended whenever you are
publishing a Change Log, for example if you are including it with an Add-
On.

The alternative method is the Merge Tool. This will be covered later in this
course. Although its change logs are more difficult to read, the advantage
of using this method is its integration with the merging capabilities. If the
main reason for the Change Log is its help when installing upgrades, then
this method is the best.
4-40 Object Analysis and Development Methodology
CHAPTER 5
POSTING ROUTINES

In this section we will be discussing the way that


Journal lines become Ledger entries. First we’ll
discuss Journals and Ledgers. Then we’ll jump
right into the codeunits. The last thing to cover in
this section is how documents are posted to
ledgers. Stay focused. If you ever intend on
changing posting routines, it is crucial that you
understand these basics. There are many more
things about posting routines that we do not have
the time to get into, but this is a good place to
start.

This chapter covers the following sections:

5.1 What is the Difference between a Journal


and a Ledger?
5.2 Standard Journal to Ledger Posting Routines
5.3 The Main Three Codeunits
5.4 The Theory behind Document Posting
5.5 The Codeunits used in Document Posting
5-2 Posting Routines and Data Conversion

5.1 WHAT’S THE DIFFERENCE BETWEEN A JOURNAL AND A LEDGER?

In this section, you will learn the major differences between a journal table
and a ledger table and how they are used in the functional area. You will
also learn how dimensions are associated with these tables.

Journal

A Journal is a temporary work area for the user. Records can be inserted,
modified and deleted at the user’s whim.

There are actually three tables that are used to make up the Journal:

. The main table is the Journal Line table.

. There are two supplemental tables called Journal Template and Journal
Batch. These tables are used primarily as filters on the Journal Line
table.

The Primary Key of the Journal Line table is a compound key (has more
than one field). It consists of Journal Template Name, Journal Batch Name
and Line No. The journal form that the user chooses, sets the Template
Name and this does not change unless the user goes into a different
journal form. The Batch Name may be changed at the top of the form (the
options that are available depend upon the Template chosen), but only one
can be viewed at a time.

The line number keeps each record in the same template and batch
unique. Line number is incremented automatically by the form (see the
AutoSplitKey property).

The user never even sees most of the Primary key fields on the form (other
than the Batch).

The journal form lets the users enter journal lines that will be added to the
detail tables of the system (the ledgers), but nothing happens until the user
decides to Post. They can leave the lines in the journal table as long as
they wish without posting.
Posting Routines 5-3

Ledger

A ledger is a protected table that holds all the transactions for a particular
functional area. These records are permanent and cannot be deleted or
modified except through special objects.

Records also cannot be inserted directly into the table. Records cannot get
into a ledger except through a Posting Routine, and Posting Routines only
post journal lines.

The Primary Key is a simple one – “Entry No.” which is an integer.

Entry numbers start at 1 and have a maximum of 2,147,483,647. Don’t


worry, if the company posts 100,000 entries a day, 365 days a year, it
would still take about 60 years to run out of numbers.

There are also many Secondary Keys and most are compound. They are
set up for reports, forms and FlowFields.

The Ledger table holds the majority of detail information for the functional
area and so it is very important to all other objects.

NOTE

A Ledger table should never be modified directly, especially inserting or


deleting records. There is a linkage between most of the ledger tables back
to the General Ledger. Because of this linkage, any modifications made
directly to the table can be disastrous. Usually the only way to undo the
changes made in this manner, is to restore the most recent backup of the
database.

Dimensions

As you may know, each journal line can now have an unlimited number of
dimensions associated with it. These dimensions are transferred along with
the journal line to the ledger tables.

From most journal forms the user can click the Line button to add
dimensions to the journal line. They are stored in a table called Journal
Line Dimension. This table is subsidiary to the Journal Line table.

When the line is posted to the ledger, those dimensions are copied to the
Ledger Entry Dimension table. From most ledger entries forms, the user
5-4 Posting Routines and Data Conversion

can click the Entry button to view the dimensions for that ledger entry
record. The Ledger Entry Dimension table is subsidiary to the ledger entry
tables.
Posting Routines 5-5

5.2 STANDARD JOURNAL TO LEDGER POSTING ROUTINES

What’s the Job of a Posting Routine?

A posting routine is a group of codeunits that is responsible for making sure


that all transactions that are put into the corresponding ledgers are correct
per line and correct as a whole. The posting routine takes Journal Lines,
checks them, converts them to ledger entries, inserts them into the ledger
table and assures that all transactions made were consistent.

The Standard Codeunits in Journal Posting Routines

The primary codeunit that does the work of posting for a particular journal is
simply called the Posting routine. This codeunit is named after the Journal
name with the addition of "-Post Line". Its main job is to transfer the
information from the Journal record to the Ledger table, though it also does
other things, such as calculations and data checking.

Posting Routine Companion Codeunits

The posting routine Codeunit has two companion Codeunits - one named "-
Check Line" and the other named "-Post Batch".

The Check Line routine is called by the Post Line routine to check each
Journal line before bothering the server with it. Thus, all of its testing
routines either do not touch the server at all, or at most only once per
posting process.

The Post Batch routine repeatedly calls the Check Line routine to test all
lines and then repeatedly calls the Post Line routine to post all lines.

The Post Batch routine is the only one that actually reads or updates the
Journal table; the others simply use the Journal record passed into them.

This way a programmer can call the Post Line routine directly (from another
posting routine) without having to update the Journal table. The Post Batch
routine is used only when the user selects Post within the Journal form.

Standardized Object Numbers

The last digits of the Object Numbers of these Posting routines are
standardized:

The Check Line always ends with 1, the Post Line with 2 and the Post
Batch with 3.
5-6 Posting Routines and Data Conversion

As an example, Gen. Jnl.-Check Line is Codeunit 11, Gen. Jnl.-Post Line is


Codeunit 12, and Gen.Jnl.-Post Batch is 13.

Note that none of these routines have any interface that requires user
input. This is so that they can be called from other applications without
having to worry about messages popping up (except error messages).

The Post Batch routine has a dialog that displays the progress of the
posting and lets the user cancel. The rest of the user interface having to do
with posting, is handled by another set of routines:

The Post routine (which just asks if you want to post and then calls Post
Batch) ends with a 1.

The Post and Print routine (which asks if you want to post, calls Post Batch
and then calls the Register Report) ends with 2.

The Batch Post (which asks if you want to post the selected batches and
then repeatedly calls Post Batch for each one) is called from the Journal
Batches form and ends with a 3.

The Batch Post and Print (which asks and then for each batch calls Post
Batch and then calls the Register Report) ends with a 4.

As an example, Gen. Jnl.-Post is Codeunit 231, Gen. Jnl.-Post+Print is


Codeunit 232, Gen. Jnl.-B.Post is Codeunit 233, and Gen. Jnl.-
B.Post+Print is Codeunit 234.
Posting Routines 5-7

5.3 THE MAIN THREE CODEUNITS

Our focus in this section is on the worker bees of posting routines:

. Post Batch

. Post Line

. Check Line

These three codeunits have the job of taking a batch of journal lines,
checking them, and putting them into the corresponding ledger table. Each
codeunit has a set of responsibilities and together the codeunits do the job
of making the journal entries permanent.

On the next few pages, we will be discussing the responsibilities and parts
of all three of these codeunits. To aid in the discussion, we will be looking
at an example – the Resource Journal Posting Routines.

The three Resource Journal Posting routines are

. Res. Jnl.-Post Batch (codeunit 213)

. Res. Jnl.-Post Line (codeunit 212)

. Res. Jnl.-Check Line (codeunit 211)

1) Open Navision Attain and select the Cronus Company to get ready for
the next few pages.

Check Line

The name of this codeunit explains its function. It is designed to check the
Journal Line that is passed to it. It does so without reading from the server
except perhaps the first time it is called.

2) Go to the object designer and design codeunit 211. Look in the


RunCheck function for any server calls (GET, FIND, INSERT,
MODIFY, DELETE, and so on.). Try to see if they are being called
every time or just once (as they should be).

You may notice that the code in the OnRun trigger of this codeunit is
inserting records. The OnRun trigger of CheckLine is normally never called,
but in prior versions of the product it was. For backward compatibility, the
OnRun trigger loads the temporary dimensions table with the two global
5-8 Posting Routines and Data Conversion

dimensions and then calls the RunCheck function. This is the function that
does all of the real checking. Take a look at this function in codeunit 211.

Before checking any of the fields, this codeunit usually makes sure the
journal line is not empty. It does so by calling the EmptyLine function in the
Journal table. If the line is empty, it is skipped by exiting immediately from
the codeunit. There is no error, and the posting process will continue.

3) Notice the EmptyLine function call. Go to the object designer and


design table Res. Jnl. Line. Find the EmptyLine function and see what
it actually returns.

The codeunit uses functions like TESTFIELD, NORMALDATE, and others


to check the value of fields.

It checks only one Journal Line at a time. If a value is not filled in that
should be, or contains an incorrect value, the codeunit uses a
FIELDERROR to report the problem to the user (the ERROR function is not
used here because it does not provide the user with enough information to
find the problem). FIELDERROR tells the user the record that caused the
problem and which field had the error.

4) Go to the object designer and design codeunit 211. Look for any error
commands (ERROR, FIELDERROR). Which one is used most often?

The last thing that RunCheck will verify is the validity of the dimensions that
are passed into the function in the temporary dimensions table. This is
done by some simple calls to the Dimension Management codunit.

If the codeunit does not stop the process with an error, then the journal line
is accepted for now.

Post Line

This codeunit is responsible for actually writing the journal line to the
ledger. It only posts one Journal Line at a time. It does not look at previous
or upcoming records.

5) Go to the object designer and design codeunit 212. Look in the Code
function for any movement commands for the actual journal line (GET,
FIND, NEXT). Are there any for the journal line? How is the record
read from the database?

You may notice that the code in the OnRun trigger of this codeunit is
inserting records. The OnRun trigger of PostLine is normally never called,
Posting Routines 5-9

but in prior versions of the product it was. For backward compatibility, the
OnRun trigger loads the temporary dimensions table with the two global
dimensions and then calls the RunWithCheck function. This is the function
that is normally called by other functions or triggers. It in turn calls the
workhorse of the PostLine routine, the Code function.Take a look at the
Code function in codeunit 212.

Like Check Line, this codeunit skips any empty lines by exiting. This
assures that empty lines are not inserted into the ledger. The first thing the
codeunit does if the line is not empty is call Check Line to verify that all the
needed journal fields are correct.

6) Notice the EmptyLine function call. This is the same function we have
already looked at.

The second thing it does (only when called for the first time), is Lock the
Ledger table. This prevents anyone else from posting while this posting is
being done. This locktable starts the posting transaction. It also finds the
last entry number that is currently in the ledger the first time through, and
stores that value in a variable. Subsequent calls to this codeunit will not
lock the table or read it for that matter.

7) Notice the line “IF NextEntryNo = 0 THEN BEGIN”. This block of code
is found in almost all Post Line codeunits. Look at the code and figure
out what’s going on. Remember that this block of code is only executed
once.

Next, the important table relations are checked. This requires reading the
database (using GET), therefore it is done here rather that in Check Line.

8) Notice the GET statements found in this codeunit. What types of


records are we getting? Why do we use a GET here?

Before writing to the ledger, the codeunit first writes to the register. It
inserts a new record the first time through and all subsequent calls modify
the record with the new “To Entry No.”.

9) Look at the code that writes to the Register. What information is stored
there?

Then the codeunit takes the next entry number and the values from the
journal line and puts them into a ledger record. It can finally insert the
ledger record.

10) Find the assignments from the journal record to the ledger record. Just
5-10 Posting Routines and Data Conversion

below that, there are some recalculations that must be done before we
store the ledger record. Why do you think these are here?

Near the bottom of the Code function is the call to the Dimension
Management codeunit that copies the dimensions from the temporary
journal line dimension table to the real ledger entry dimension table. The
entry number for this new ledger record is passed into the function to keep
the dimension records associated with this ledger entry.

The very last thing that the codeunit does is to increment the variable that
holds the next entry number by one. Thus, when the codeunit is called the
next time, the next entry number is ready.

When post line is done one journal line has been processed, but more than
one ledger record may have been inserted into more than one ledger.

11) In the resource journal post line codeunit, only one ledger record is
created, but in the general journal post line codeunit, a much more
complicated set of rules are in effect because there are four ledgers
and not just one. The basic structure of the codeunit, however, is still
the same.

Post Batch

This codeunit is responsible for posting the Template and Batch that were
passed to it.

12) In the resource journal post batch codeunit, there is a lot of code that
deals with the Template and Batch parts of the journal lines. The
important thing to remember is that this codeunit only posts one Batch
at a time. Can you find the lines of code that assure this?

Of course, only one record variable for the journal was actually passed in,
but the codeunit starts by filtering down to the template and batch of the
record that was passed in. Then it finds out how many records are in the
record set that the record variable represents. If the answer is none, the
codeunit exits without error and it is up to the calling routine to let the user
know that there was nothing to post.

13) Find the line of code that checks for the existence of records in the
Batch. How does it work?

It can then begin checking each journal line in the record set by calling
Check Line repeatedly for each one. Once all the lines are checked, they
can be posted by calling Post Line repeatedly for every one of them. By
Posting Routines 5-11

now the codeunit has looped through all the records twice, once for Check
Line and a second time for Post Line. To call the appropriate functions in
Check Line and Post Line, Post Batch must fill in a temporary journal line
dimension table with all of the dimensions for the journal line. Then it can
pass this temporary record variable into the RunCheck function of Check
Line (or the RunWithCheck function of Post Line) along with the journal line
record variable.

14) Look for the calls to Checkline and Postline. There are codeunit
variables in the Global Variables window that have these codeunits
defined. How are the codeunits called? What is passed to the
codeunits?

This codeunit may also be responsible for making sure the journal lines
balance. This usually takes place after they are checked and before they
are posted.

15) Resource journal lines do not have to balance, but general journal lines
do.

It may do other special things depending on the Journal Template. For


recurring journals, the journal lines are updated with new dates based on
the date formula. When a recurring journal line is posted, this codeunit
must check the description and document number fields and perhaps
replace any replaceable parameters with the correct values (%1 = day, %2
= week, %3 = month, and so on.).

16) Find some places in the code where it checks to see if this is a
recurring journal. How does it know if it is or is not?

If the template is not recurring the codeunit deletes all the journal lines.

17) Locate the DELETEALL statement. Notice that there is no loop that
deletes all the records.

Finally, once every journal line is Posted and/or deleted (or updated), the
codeunit calls COMMIT. This ends the posting transaction that was started
by Post Line’s locktable.

18) Finally, find the COMMIT at the end of Post Batch (the end of the
OnRun Trigger). Why do we do a commit here?
5-12 Posting Routines and Data Conversion

5.4 THE THEORY BEHIND DOCUMENT POSTING

Now that you understand how one journal line becomes one ledger entry,
we can talk about how one document can create dozens of ledger entries.

What is a Document?

As we have already discussed, a Document is an easy interface for a user


to make many complicated transactions.

In this section, we will look at the major parts of the Sales Posting codeunit
that posts all sales documents.

Breaking it Down

In a Sales document, the user is trying to tie a customer to a series of


transactions. Each individual transaction could be performed by several
journal entries into the correct journals, however, the user needs to total all
of the transactions in order to easily bill the customer and make the
process of taking the order much easier as well.

A data entry person is capable of taking an order over the phone and
creating the document. The user could then tell the customer over the
phone the expected price of the entire order. This is the goal.

So, now we have an order. But Navision is not built around the concept of
an order. We need to take the information that was gathered by the user
and translate that into the correct journal entries. A Sales document could
be as complicated as the following diagram –
Posting Routines 5-13

The diagram shows the posting of a Sales Invoice that has three sales
lines:

. On Line 1, we are selling a G/L Account (perhaps this line adds some
kind of surcharge or freight).

. On Line 2, we are selling an Item (say a computer).

. On Line 3, we are selling a resource (here we are talking about time that
one of our employees has spent custom building the computer).

Note that the Invoice as a whole (header and lines) generates a journal
entry that will debit the Accounts Receivable Account in the G/L. The
corresponding Credits come from the individual lines. Each line could
generate a separate G/L entry for the amount of that line.

Note that if the lines use the same General Posting Setup record,
department, project and so on, they may actually post as one G/L Entry.

At the same time that all of these entries are being posted through the
General Journal, there are entries being made to the Customer, Item, and
Resource Ledgers. The entry to the Customer Ledger is made by the
original General Journal entry. The entries to the Item and Resource
ledgers are made via Journal entries to the Item and Resource Journals
respectively.

There are many journal entries that are automatically made by the Sales
Post routine on behalf of the user. When these journal entries are posted,
they are posted as if the user had put them into the journals themselves.
The biggest difference is that the journal records are posted one at a time,
which allows the Sales Post routine to bypass Post Batch and call Post
Line directly.

In the example, codeunit 12 Gen. Jnl.-Post Line would be called at least


twice, codeunit 22 Item Jnl.-Post Line would be called once, and codeunit
212 Res. Jnl.-Post Line would be called once.
5-14 Posting Routines and Data Conversion

5.5 THE CODEUNITS USED IN DOCUMENT POSTING

A sales document is posted primary by codeunit 80 Sales-Post. You can


however post an entire batch of sales documents by calling report 297
Batch Post Sales Invoices.

Note that this report only does invoices. There is a separate report for each
document type.

These reports call codeunit 80 repeatedly for each document.

For this to work, codeunit 80 must not interact with the user. In fact,
codeunit 80 is never called directly by a form. The form calls codeunit 81
“Sales-Post (Yes/No)” or 82 “Sales-Post + Print” or one of the reports
mentioned above. They in turn interact with the user (getting confirmation
or other information) and then call codeunit 80 appropriately.

Focusing in on Codeunit 80 (Sales-Post)

If we focus in on codeunit 80, we can break it down into some of its major
parts. We will assume the user is shipping and invoicing a Sales Order.

. Determine what the posted document numbers are going to be and


update the Header. This section ends with a COMMIT.

. Insert the Shipment Header and Invoice or Credit Memo Header.

. Process the Sales Lines. This section starts by clearing the Posting
Buffer (a temporary table with a special primary key) and ends with the
UNTIL that goes with REPEAT that loops through all the Sales Lines.

. Inside this section, each line is processed one at a time.

. The line is checked.

. The line is checked with its matching Shipment line (if the line was
previously shipped).

. If the line type is an Item or a Resource, it is posted through the correct


Journal.

. The line is then added to the Posting buffer. It may be inserted or it may
update a row already there.

. If the line is related to a Job, then it posts a Journal line through the Job
Posting Routines 5-15

Journal.

. Then if there is no shipment line, it inserts one.

. Finally it copies the Sales Line to the Invoice Line or Credit Memo Line
(the posted tables).

. Now, it can post to the G/L, all the entries in the Posting Buffer. These
are the Credits that are created from the Sale of the lines.

. Then it can post the Debit to the G/L. The customer entry is made to the
Sales Receivables Account.

. It checks to see if there is a balancing account for the Header. This


corresponds to an automatic payment for the invoice.

. Lastly, it updates and/or deletes the Sales Header and Lines and
commits all changes.
5-16 Posting Routines and Data Conversion
CHAPTER 6
DATA CONVERSION

Almost always, when a Navision Solution Center


(NSC) gets a new customer, that customer has
already been using a computerized accounting
system. As a result, one of the many projects
needed for the new customer, will be a data
conversion project. In this section, we will cover
how to complete a data conversion project and
how to use one of the many tools used for data
conversion, the Navision Dataport Object.

This chapter covers the following sections:

6.1 Transaction Imports


6.2 Importing Documents
6.3 File Handling in Navision
6-2 Posting Routines and Data Conversion

6.1 TRANSACTION IMPORTS

Normally, for a Master or Supplemental table, the only code you will need,
is to fill in data that is required for Navision but missing from the import.
Occasionally, you will need to generate the primary key (say, Customer
Numbers) during the import as well. More often, though, you will need
nothing.

When we get to transaction imports, such as bringing in open invoices or


beginning balances, you will always need trigger code, because whenever
transactions are brought into Navision, you must always check them to see
if they follow the Navision Business Rules. If bad data is brought into
Navision, it will cause you and your customer to spend hours or days fixing
data up that could have been handled automatically during import.

One other important note - Under no circumstances, will you ever import
directly into a Ledger Table! Not only would there be no Business Rules
you could test against (since all posted transactions are assumed correct),
but there would be no way for your customer to fix the inevitable corruption.
In fact, the only sure method we have found for fixing this sort of disaster is
to erase all Registers and all Ledgers and start over.

Do not import into Ledgers!

What should you do instead? Import into the corresponding Journal. When
you import into a Journal, you will have two key choices as to what to do
with the records.

. First, you can store the Journal records into a Journal. This has the
advantage of allowing the customer to review the entries and possibly
make modifications before they are actually posted. The disadvantage is
that it takes more work to program this and more time for the actual data
conversion.

. Second, you could post the Journal Entries as they are being imported.
This has the advantage of being a one-step conversion process, faster
and easier to understand. The disadvantage is that the information must
be correct, because any errors will become permanent. With proper
testing, this should not happen, but sometimes the customers data is
such that you cannot count on it being correct.
Posting Routines 6-3

Should You Save the Journal Lines or Post Them?

Consider the following factors when you make your decision:

1) Will the customer really have time to manually review the imported
data during the data conversion?

For most data conversions, the testing phase is over before the data
conversion begins. The users will go home on Friday after using the old
system for the last time, and come in on Monday using Navision. In this
situation, it is far better to post the entries as you import them, and test
thoroughly before that day.

However, sometimes a customer will want to run parallel for a while, or


does not mind that some data will not be converted yet when they switch to
Navision. This is especially true for historical information like old invoices
and the payments that closed them.

In this situation, it might be desirable to import into the Journal and store
the records there.

2) What is the status of the customer's existing data?

If the data is missing a lot of information needed by Navision, if it is


corrupted, or in some other way not reliable, you will be forced to import
and store the information into the Journal. This will allow the customer to fill
in the missing data and fix the inaccurate data before posting.

However, if the customer's data is in good shape, this should not be


necessary.

3) Finally, what is the status of the testing?

If the data conversion date is looming near and the testing of the data
conversion has not been thorough, you may be forced to fill in the Journal
and store the information there rather than post directly.

You should not take any chances on bad data getting into Navision and,
without thorough testing, you will never know. Should this situation occur,
try to get the conversion date delayed. If you cannot, do not post during the
import.

Keep in mind that this decision can be made independently for each
transaction data type you are importing.
6-4 Posting Routines and Data Conversion

For example, you could decide to import open invoices and post directly so
that the Customer Ledger will be up to date upon conversion. However, you
could decide that the closed invoices and the payments that closed them
could be imported and stored into the journal, so that the customer can
review the results before posting. After all, they do not need this old
information to run their system.
Posting Routines 6-5

What Code in What Trigger?

Why do you need code when importing transactions?

. To make sure that the Business Rules for the application are being
followed.

. To initialize records so that data is not accidentally transferred from one


record to the next.

. To fill in missing information – the record must either be stored or posted.

The following are the main triggers that will be used for these purposes:

. OnPreDataport – Validate any options the user may have entered on the
Request Form.

. OnPreDataItem – Insert code that will be run once for the entire Dataport.

. If you are inserting the records into the Journal, this is where you will
determine what the first line number will be.

. OnBeforeImportRecord – Insert code that will initialize the record to be


imported.

. At the least, put in an INIT function call.

. OnAfterImportRecord – Here is where most of the work will be done.

You will need to validate the information being imported, fill in missing
information and finally store the record in the Journal or post it.

Calling Journal Validation Routines

As you recall, most of the Business Rules are contained within the table
triggers of the Journal table. Therefore, most of the processing that you will
be doing will involve calling the Journal Validation routines (the Journal
fields’ OnValidate triggers).

Which ones should you call? Well that depends on what kind of data is
being imported. You need to call the validation routines whenever there is a
Table Relation that must be tested. You also need to call them whenever
the entry of one field affects what goes into the other fields.
6-6 Posting Routines and Data Conversion

Use the Debugger

The best way to be sure the proper routines are called, is to go into the
journal, insert a new line, clear any fields that contain default values
(dataports don’t default things for you), turn on the debugger and start
making the entry. Do not do any lookups (i.e. do not press F6). Skip any
fields that are not in your customer's data.

Watch what happens as you enter each field. On any field where the
debugger does not come up, there is no validation code for that field, and
unless it has a Table Relation, you can safely skip it. If a trigger comes up
which is not a validation trigger (it does not start with OnValidate), then
ignore that trigger and press F5.

If the debugger does come up on a trigger which starts with OnValidate, it


means that it is probably worthwhile to call the validation code. Write down
that field. Then, press F5 (Run) and see if another field validation code
(OnValidate) comes up. If so, write down that field indented under the first.
Continue to do this until you are back on the journal entry form. Then, look
on the Journal entry form and note any fields that have changed value
since you entered the field. This is data that is filled in automatically as part
of the validation process. Once you have entered all the fields that are in
your customer's data in this manner, turn off the debugger.

If this is a General Journal Line form, enter another line to balance your
entry. Now, try to post. Do you get any errors? Normally, this will be the
result of missing or invalid data. Write down the problem; fix it manually,
then post again. Continue until you have found all of the problems, and the
line posts correctly.

Now, you can go into your dataport and add calls to the validation routines.
The function you will use is the VALIDATE function. For example, if you
needed to Validate the Account No. field, you would use:

VALIDATE("Account No.");

Remember that you should do this in two cases:

1) First, if during your debugging, the debugger came up when you


entered the field, this indicates that there is validation code to be
called.

2) Second, if the field has a Table Relation with another table, the
VALIDATION routine will also test the Table Relation.

If validation code came up from another field when you validate a field (an
Posting Routines 6-7

indented field on your list), you do not need to call the validation code
separately for that field. However, this can affect the order in which you call
validation routines. For example, if you validate the Posting Date in the
General Journal, the validation code for the Payment Terms Code is also
called. Later on, when you enter the Payment Terms Code, it is validated
again. In this case, you should first fill in both fields, then call the validation
routine for the Posting Date only. You will not need to validate the Payment
Terms Code separately, since the Posting Date validation calls it for you.

Activity – Debugging the General Journal

In this scenario, you need to import beginning balances into the General
Ledger.

The customer's system will export this information in variable length field
(comma separated values) format. They have told you that this is the
balance as of June 30, 1999.

The information exported for each line is:

. The G/L Account Number

. The Transaction Description

. The Amount (positive = debit, negative = credit)

Go into the General Journal and, using the procedures outlined in the
previous section, determine which fields need to be validated and which
additional information is needed to post this entry correctly.

Here is some test data you should use:

. Account No. = 11200

. Description = Beginning Balance as of 6/30/99

. Amount = 7324.12

Which of these fields need to be validated?


6-8 Posting Routines and Data Conversion

Which fields are missing and must be filled in before you can post?

What other things did you notice that might affect the import?

Transaction Imports (continued)

One thing you probably should have noticed is that when the Account No.
is validated, the Description field is filled in automatically. If this field had
been imported and then the Account No. validated, then the imported value
would have been lost.

How are you going to get all the data you need to import?

Rather than importing directly into a field, you will often need to import into
a variable and then transfer the contents of that variable into the right field
at the right time.

To import into a variable, create a global variable and put that variable's
identifier into the SourceExpr property of the Dataport Field rather than the
actual field name. Then, in your OnAfterImportRecord trigger, after you call
the validation code for the Account No., transfer the Description from the
variable to the field.

When you import any transactions, there is another field, which, even
though you do not strictly have to fill in, you should fill in every time. This is
the "Source Code" field. This field follows a transaction wherever it goes
and tells where it came from. You should create a Source Code for each
Import routine and set the Source Code for every entry that goes through
that routine to that Source Code.
Posting Routines 6-9

Activity – Creating a Transaction Dataport

This is a continuation of the previous activity.

On your class disk, you will find a file called BEGBAL.CSV. This file
contains Journal Entries in the same format as described before.

We will not be using dimensions for posting at this time. Our customers
and salespersons are however set up for dimensions. Go to table 352 and
delete the dimensions for customers and salespersons. Also, our GL
Accounts are not set up for direct posting. Create a processing only report
that will check on direct posting for posting accounts only.

"G/L Account"."Direct Posting" := TRUE;


"G/L Account".MODIFY;

Write a Dataport to import this file. Fill in all the triggers


to initialize the record, validate the fields necessary, and
fill in the missing fields.
We are not ready to do anything with the record yet.

Therefore, temporarily, at the end of the OnAfterImportRecord trigger enter


the following line of code:

CurrDataport.SKIP;

This will prevent the dataport from doing anything to the record, like storing
it in the journal.

Then you can test your code without causing any harm.

Save this Dataport object as number 91101.

Saving Journal Records

As you remember, one of the options for importing transactions is to store


the Journal record. To do this, you must remember how the primary key for
a Navision Journal table is created.

It contains three fields:

. The Journal Template Name

. The Journal Batch Name

. The Line No.

None of these values will be in any export that you get from the customer.
6-10 Posting Routines and Data Conversion

Therefore, you must set these values yourself.

The Journal Template and Batch will pinpoint in which journal the user will
find the entries when the import is complete. The easiest way to handle this
is to create a batch for import purposes in whichever journal you are using,
and then in your dataport, set the Template and Batch names to the ones
that you selected and created.

As a test at run time, to make sure that the journal batch actually exists for
use by the customer, you might want to do a GET on the Template table
and the Batch table using the values you are setting. The Line Number will
actually be on the line in the journal where the user will find the data. This
must be different for each import line. To do this, you should find the
highest existing line in the journal batch and add 10000 to it for the first line
you import. Each succeeding line should be 10000 beyond that.

Why 10000? As you recall from looking at the AutoSplitKey property used
by all Journals, these are the numbers that the AutoSplitKey property would
have chosen had the user inserted these lines itself.

Here is an example of a piece of code that will find the highest existing line
in a journal batch (if any), and add 10000 to it:

LOCKTABLE;
SETRANGE("Journal Template Name",'GENERAL');
SETRANGE("Journal Batch Name",'IMPORT');
IF FIND('+') THEN
NextLineNo := "Line No." + 10000
ELSE
NextLineNo := 10000;

The LOCKTABLE is used to make sure that we have the latest version of
the Journal table before we start, and to make sure that nobody else tries
to update the Journal table at the same time we do. The SETRANGE
functions set filters on two of the three primary key fields. The FIND
searches for the last record, but note that it is within an IF statement, so
that in case there are no current records in the batch (the normal case), the
code will handle it by starting at 10000.

The other thing you must do when importing transactions is to modify some
of the DataItem properties to remove some of the unwanted automatic
processing.

For importing journals, you should set the AutoSave property to Yes, and
the AutoUpdate and AutoReplace properties to No.
Posting Routines 6-11

Activity – Saving the Journal Records – Part 2

Now, modify your import routine (Dataport 91101) to save the Journal
Records, as described above.

Don't forget to remove the CurrDataport.SKIP function, so that this time the
records will be saved.

Test your Dataport using the BEGBAL.CSV file on your class disk.

Now, go into the journal and see what your entries look like.

. Did you remember to create the batch before you imported? If not, go
ahead and create it now - the entries are already there.

If there are any obvious problems, modify your dataport to fix the problems
and import again.

. Do you now have two copies of the import in the same journal? Good!
This is how the dataport was designed to work. Now, go back and delete
the entire contents of the journal and try again.

Now, try to post the entries. Any problems?

Again, go back and fix your dataport so that all problems are resolved. By
the time you are done, you ought to be able to post the imported file.

One of the problems you can have is that it can insist on a particular
document number. If this happens, you can be tempted to set the
Document No. to this value. Don't! Instead, go into your Journal Batch
setup and remove the value in the "No. Series" field.

Posting Journal Records

The second option for importing transactions is to simply load the journal
record, but rather than saving it, posting it directly.

For this there are only a couple of things you must do. You do not need to
worry about any of the primary key fields any more, since you are never
going to store the record.

Instead, all you need to do is call the posting routine. To find the correct
posting routine, refer to the material on your class disk in the Architecture
section. You just need to look for the Journal Post Line routine.

Create a codeunit type variable for the posting routine. Then, at the end of
6-12 Posting Routines and Data Conversion

your OnAfterImportRecord trigger, after you have set all the fields up
properly, call the codeunit's RUN method, using the journal record as the
parameter.

An example:

GenJnlPostLine.RUN("Gen. Journal Line");

Much easier than saving the record?


Posting Routines 6-13

Oh, one other thing you must do - Go into the DataItem Properties and set
the AutoSave property to No, along with the AutoUpdate and AutoReplace.
Remember that you are no longer saving these journal records anywhere.

Activity – Posting the Journal Lines – Part 3

Now, go back into your Dataport object. Save it as another object number,
so you can keep all the work you did before.

Now, modify your Dataport to remove the code and variables that you
needed simply to store the journal line. Instead, add the code needed to
post the journal line directly.

Don't forget to set the AutoSave property to No.

Now test your import. Since you already tested it when posting, you should
have no troubles now. If you do, go over your work carefully and see where
the problem might be and fix it.
6-14 Posting Routines and Data Conversion

6.2 IMPORTING DOCUMENTS

Importing Documents is a more complex task than importing journal


entries. There are many more fields to import and many more of them are
needed before a document entry is complete. In fact, there is sometimes so
much data that it runs into the limits of the Dataport (600 to 650 characters
per line).

Worst of all, there are two different types of records to be imported, header
data and line data. As you have seen, it would be difficult to import two
different types of data using a Dataport.

For these reasons, we do not generally recommend importing documents.

For historical data, importing the historical transactions through the journals
is usually sufficient. For open Sales Orders or open Purchase Orders, we
recommend that the customer enter them by hand right after the data
conversion. This usually makes a fairly good training and confidence
building exercise.

However, in some situations, depending on the business, the customer


may want to import open orders. For example, if there is a long lead-time
between order and shipment, you can have open orders going back several
months.

Also, some customers would like to see historical documents. For example,
the summary information for invoices are brought in through the journals,
but the customer may want to see the line by line detail that only exists on
the original invoice document.

If the customer wants this, and is willing to pay for it, the best method would
be for the customer to split the information into two files - one holding the
header information and one holding the line information. If this is done, you
can probably use a Dataport to import. If not, you will need to use another,
more complex method outlined as follows.

For Open Orders

For Open Orders, you will have to validate fields for both the header and
the line.

If the customer has partially shipped orders, only bring in the portion that is
still open. It is far too complex to try to simulate a partially shipped order in
Navision without actually shipping it through Navision.
Posting Routines 6-15

When you bring in the lines, you will have to use a similar set of code that
you did with journals. First, you set a filter on the Document Type and the
Document No. fields. Then, you find the existing line with the highest
number, and add 10,000 to that for the Line No. of the line you are
importing.

For Historical Documents

For Historical Documents, the processing is simpler.

For one thing, since you already brought in the transactions (summary
information), these documents are for viewing only. Therefore, there is no
need to validate the fields. When bringing in the lines, you only need to
filter by the Document No., since the table you are importing into implies
the type.

Also, you can enter the line numbers sequentially (1 higher than the
previous highest number), since the user cannot insert.

One thing to note, however, is that the historical tables are protected. You
will have to give your Dataport permission to insert into these tables.

Note that there is never a reason to bring in a document and then post it.
Historical documents are already posted, while open orders have not been
posted yet in your customer's existing system or in Navision.
6-16 Posting Routines and Data Conversion

6.3 FILE HANDLING IN NAVISION

Dataports are not the only method to get data into Navision. Through File
Variables, you can import data from or export data to any file accessible
through your operating system. We only have time to go through a few
basics now, but we have also given you an import skeleton you can use in
the future. The skeleton will work for any fixed field or comma separated
value text files, as long as each line (no per-line limit) ends with a Carriage
Return - Line Feed combination, as most text files do.

This is on your Class Disk, in the Examples directory for this section, and
the file is SkeletonFileImport.fob. It contains a single codeunit that contains
functions that can be called from any Navision Object.

The File Data Type

In order to gain access to an external file from within C/SIDE, you must first
declare a variable of type File. This is a complex data type, which has
numerous methods (functions) used to open, read, write and close external
files.

You would declare one File variable for each file you wish to access at the
same time. You would, of course, be able to use one File variable to handle
multiple files, but you would have to access them one at a time, since each
File variable can be open to only one file at a time.

Opening Files for Import or Export

When you are ready to use a file, you must open it first. Even before you
open it, you must set it up properly. To set it up properly, you should
determine first whether you are going to use it for reading (import) or for
writing (export). Although you could theoretically open a file for writing and
then read it as well, this is normally not done in Navision.

Here are the File Methods used to prepare a file for opening and for
actually opening (and closing) it. As with other methods, these are called
through the File variable.

For example, if the File variable is named "ImportFile", then to use the
Open method, you would enter:

ImportFile.OPEN(Name);

We will skip the "ImportFile." in front of each of these methods:


Posting Routines 6-17

WRITEMODE(NewWriteMode)

Sets the Read/Write status of a File variable before opening the file. If
NewWriteMode is TRUE, you will be able to write to the file; if
NewWriteMode is FALSE, then you will only be able to read from the file.

Note that another way to use this method is like this: IsWriteMode :=
WRITEMODE;. By using this method after the file is open, you can tell
whether a file is available for writing or not.

TEXTMODE(NewTextMode)

Sets the type of data to be read or written. If TRUE, the file will be open in
Text mode and if FALSE, it will be open in Binary mode. These will be
explained a little later. This method can only be used before opening the
file.

Note that another way to use this method is like this: IsTextMode :=
TEXTMODE;. With this method, used only after the file is open, you can tell
whether a file is being read or written in text mode or binary mode.

QUERYREPLACE(NewQueryReplace)

Use this method if you are going to open a file using the CREATE method.
If NewQueryReplace is TRUE, and the file already exists, C/SIDE will ask
the user before a replace will be allowed. If NewQueryReplace is FALSE
and the file already exists, C/SIDE will erase the old file and replace it with
a new (empty) one, without asking the user.

If the file does not already exist, this method has no effect.

OPEN(FileName)

Use this method to open an already existing file. Be sure to set the
WRITEMODE and the TEXTMODE before opening a file. FileName should
be the full path name of the file. If you use OPEN as a statement (without
looking at the return value) and the file indicated by FileName does not
exist, a run-time error will occur.

If you use OPEN as an expression (looking at the return value) and the file
indicated by FileName does not exist, OPEN will return FALSE, but if the
file does exist, it will be opened, and OPEN will return TRUE.
6-18 Posting Routines and Data Conversion

CREATE(FileName)

Use this method to create and open a file. Be sure to set the WRITEMODE,
the TEXTMODE and QUERYREPLACE before creating a file. FileName
should be the full path name of the file. If you use CREATE as a statement
(without looking at the return value), and the file indicated by FileName
cannot be created (say the path does not exist), a run-time error will occur.
If you use CREATE as an expression (looking at the return value) and the
file indicated by FileName cannot be created, CREATE will return FALSE,
but if the file is created, it will be opened, and CREATE will return TRUE.

If the file already exists, it will be cleared and opened. Whether the user is
warned about this or not depends on the parameter to the
QUERYREPLACE method called before calling CREATE.

CLOSE

Use this method to close access to the file through this file variable. Once
this is called, you cannot use this file variable again to access a file unless
it is re-opened or re-created.

If CLOSE is called and the file is not opened, a run-time error will occur.

Methods of Reading Files

There are two methods provided for reading or writing data in external files.
The method is set using the TEXTMODE method described before. There
are two possible settings:

TEXT (TEXTMODE=TRUE)

Each file access will read or write a line of text. The variable used can be of
any type and it will be converted to (if writing) or from (if reading) text during
the processing.

BINARY (TEXTMODE=FALSE)

Each file access will read or write a single variable. The variable will be
read or written in its internal format. For example, a Text is written out as a
null terminated string whose length is the defined length of the Text
variable.

Since Text Mode is limited to one variable per line, note that you cannot
have more than 250 characters per line in the file. Binary mode does not
Posting Routines 6-19

have this limitation, since it does not have lines. However, the internal
format can really only be read if it was written in the same manner from
Navision.

The one variable type that is very useful when using Binary mode is the
Char type. Using this, you can read or write one byte at a time. In the
SkeletonFileImport object, we use Binary Mode and the Char type to import
text files that can be of any length.

Reading or Writing Data in External Files

The following methods are used to read or write data in external files (once
the file variable is open):

[Read :=] READ(variable)

Read a variable from the file. The optional return value indicates the
number of bytes read. If you are using Text mode, a line of text is read and
the text is evaluated into the variable passed in as a parameter. If you are
using Binary mode, the variable is read in its internal format, and thus the
number of bytes read depends on the size of the variable.

WRITE(variable)

Write a variable to the file. If you are using Text mode, the variable is
formatted to text and written out as a line of text. If you are using Binary
mode, the variable is written using its internal format.

There are other File Handling methods that can also be used.

Which Method to Use?

Generally speaking, for most purposes for which you would use the Text
method to read or write data, you would be better off using a Dataport.

The only exception would be to use the Text method to read or write
variables of type Record. This will result in a tab-separated file, one line for
each record, and each field separated from the others by a single tab
character. This unusual format could be useful if you are transferring
information from one Attain database to another one.

Other than this, and especially for data conversion purposes, if a dataport
will not do what you want, then normally you will use the Binary method to
read or write data in external files.
6-20 Posting Routines and Data Conversion

When you have some time, study the Skeleton File Import object on your
class disk to see how this can be done for an import.
CHAPTER 7
CODING IN FORMS

This Chapter explains the various form and


control triggers, their functions and when they are
fired.

This chapter covers the following sections:

7.1 Form Triggers


7.2 Triggers for Rec
7.3 Control Triggers
7.4 Special Form and Control Functions
7-2 Developing Navision Solutions

7.1 FORM TRIGGERS

In this section, you will learn about the form triggers that correspond to the
user’s interactions with the Form itself. These triggers fire when something
happens to the form.

OnInit

This trigger fires as soon as the form object is created and before any
controls are created. You cannot use this trigger to make controls visible or
invisible because the controls do not exist yet. You can use this trigger to
initialize global variables that the form will use.

OnOpenForm

This trigger fires before the form is shown to the user, but after all controls
have been created. You can use this trigger to make controls visible or
invisible. You could also set additional filters on Rec (the forms record
variable).

OnCloseForm

This trigger fires right after the form is no longer visible to the user. You
cannot stop the close of the form from this trigger. You might use this
trigger to set the value of a variable that will be returned from this form.

OnQueryCloseForm

This trigger fires as soon as the user tries to close the form but before the
form is made invisible. If the trigger returns TRUE, the form will be closed.
If the trigger returns FALSE, the form will not close. You can put code in
this trigger to check the status of the form and then return the appropriate
value to allow the form to close or stop the form from closing.

OnActivateForm

This trigger fires whenever the form gains focus. If you attempt to use this
trigger, you should know a couple of things about it.

. When the form is first opened, this trigger fires right before the form is
visible. It is after the OnOpen trigger.

. This trigger fires every time the user returns to this form. So, it may fire
many times depending on the activity of the user.
Coding in Forms 7-3

OnDeactivateForm

This trigger fires whenever the form loses focus. When the form is closing,
this trigger fires after the OnQueryCloseForm but before the OnCloseForm.
Again, the user may cause this trigger to fires many times if they click back
and forth between other forms and this form.

Activity – Adding Messages to the Triggers

In this activity, you will add messages to all of the triggers mentioned above
to see exactly when they fire. Remember that messages will not appear to
the user before the end of the process. It will appear as though several
triggers fire at exactly the same time, but it is just the delay caused by the
message function.

1. From the Object Designer, design form 21 (Customer Card).

2. Add code similar to the following for each of the triggers mentioned in
this section (use the actual name of the trigger in each message). Do
not attempt to use the other triggers yet.

MESSAGE('OnInitForm');

3. Close and save the form.

4. Run the form. Which triggers fired (list them in order)?

5. Try bringing up the customer list form by pressing F5. Which triggers
fired (list them in order)?

6. Close the form. Which triggers fired (list them in order)?


7-4 Developing Navision Solutions

7.2 TRIGGERS FOR REC

In this section, you will learn about the form’s built-in record variables and
the triggers that are associated with them.

The Form’s Record Variables

Every bound form (those with a source table) has two built-in record
variables. One is called Rec. The other is called xRec. You can use these
record variables to access the current and previous records for the form
respectively.

Rec

Rec is the current record that the form is working with. Remember that it is
just a record variable. Anything you can do to a normal record variable, can
be done to Rec. You should be careful, however, when changing Rec in
code. The form uses Rec to know which record it is on, and can get
confused if you change to another record in certain triggers. If you need to
change the underlying table of a form, you may want to consider using a
global record variable that you create instead of Rec. This is usually easier.

Of course, sometimes you want to change Rec. Perhaps, you need to filter
Rec based on the user’s selection in a text box. Perhaps, you need to
initialize Rec with values from another record variable whenever the user
wants to insert. Perhaps, you want to remove filters from Rec or change the
key. Whatever the reason, use care in deciding where to make these
changes. You will find some helpful information in the description of the
triggers in this section.

The most common uses for Rec are to look at the fields and use their
values. Rec is used to get the value that the user just entered, or the values
of all the fields in the record. The fields in Rec are always exactly what the
user sees on the form. If you want to see that values that Rec had before
the user changed them, you would have to use xRec.

xRec

xRec is the previous version of Rec. If the user has changed fields on the
form but not moved off of the record yet, then xRec holds the previous
values for all the fields. Like Rec, xRec is a normal record variable, but it
should never be changed. It is updated by the form whenever a trigger
fires, so any changes you might make would be lost. The only purpose of
xRec is for comparison to Rec.
Coding in Forms 7-5

In some cases, xRec is actually a different record altogether. If the user has
just pressed F3, the value of Rec is an empty record, while the value of
xRec is the record the user was on before they pressed F3.

Automatic Actions

Forms do a lot for the user and you. You need to be aware of what
happens automatically for the user, while they are on the form.

Records are automatically inserted on forms when the user finishes filling in
the primary key fields for a new record (the new record is shown to the user
when they press F3 or move past the last record). The only exception is
when the primary key fields are not on the form or if the DelayedInsert
property of the form is set to Yes. If DelayedInsert is set to yes the Insert
will happen automatically when the user leaves the record. The primary key
fields are not on the form and DelayedInsert is set to No, the automatic
insert could happen at any time while the user is editing fields.

Records are automatically modified on forms when the user changes fields
in Rec and leaves the record. This could mean going to the next, previous,
first, or last records. It could also mean the closing of the form or the
pushing of a button. There is no way for the user to easily cancel the
changing of several fields. When the user closes the form, the record will
be modified.

There is one way to cancel changes on a form. If the user decides that the
changes made should not be saved and the record has not been saved,
the user can press F4 to delete the record. When prompted to delete, if the
user says no, the values that the user entered are lost (Rec is reset). This
is not advisable, because if the user accidentally hit Yes, the entire record
would be lost.

Records are automatically renamed on a form when the user changes the
primary key values of an existing record. The form prompts the user to
confirm the change. If the user clicks Yes, the record is renamed. If the
user clicks No, the record is left exactly as it is and focus is put back into
the control that the user tried to leave. The user must then hit escape to
change the primary key back to its original value or try the rename again
and click Yes.

Triggers for Rec

The following triggers are fired in response to the user changing Rec.
7-6 Developing Navision Solutions

OnFindRecord

This trigger fires whenever the User causes a FIND to occur on Rec. This
happens when the user presses the First or Last buttons or actually uses
the Find dialog.

Adding code to this trigger disables the default code that normally takes
place. That default code would look something like this:
EXIT(FIND(Which));

This trigger is not normally used. You might need to use it to display
something on a form that was not stored in a table, such as an array or
temporary table.

OnNextRecord

This trigger fires whenever the user causes a NEXT to occur on Rec. This
occurs when the user presses the Next or Previous buttons or does
anything that causes a Next or Previous (PgUp, PgDown, etc.).

Adding code to this trigger disables the default code that normally takes
place. That default code would look something like this:
EXIT(NEXT(Steps));

This trigger is not normally used. You might need to use it to display
something on a form that was not stored in a table, such as an array or
temporary table.

OnAfterGetRecord

This trigger fires whenever the form must retrieve (or get) a record from the
database. It fires for every record being displayed. For instance, on a
tabular form that can display 12 records to the user at a time, this trigger
would fire 12 times once for every record on the form.

This trigger fires right after the record is retrieved from the database. On a
tabular form, records are retrieved starting with the current record and
moving out. The current is read first, then the next records (those coming
after the current), and then the previous records (those coming before the
current).

This trigger is used a lot for forms. It fires the most often. You can use it to
remove filters the user might try to set. This is done on card forms to keep
the user from setting a filter on the primary key fields. You could also use it
to calculate a calculated column on a tabular form. Always keep in mind
Coding in Forms 7-7

that this trigger fires a lot. Too much code in this trigger can slow down
your form.

Here is how this trigger looks for the Customer Card form.

OnAfterGetRecord()
SETRANGE("No.");

OnAfterGetCurrRecord

This trigger fires whenever the Form retrieves the record that is currently
selected by the user. It fires right after the OnAfterGetRecord trigger for the
current record. It only fires for that record, however, not any other records
that might be displayed on the form.

OnBeforePutRecord

This trigger fires whenever the user leaves a record. Its name is
misleading. The name suggests that it fires right before the record is
modified or inserted in the database, but it actually fires whether the user
changes the record or not. Simply hitting the Next or Previous buttons on a
form, will cause this trigger to fire before the form leaves the current record.

You can use this trigger to see if the user filled in required fields, but this is
not necessarily designed for that purpose. This trigger is not used in the
base application.

OnNewRecord

This trigger fires whenever the user hits F3 or scrolls onto a new record.
Note that this trigger fires before the user can even see the new blank
record.

This trigger is most often used in journal forms. Because it fires before the
user can see the new blank record, it is the best place to initialize fields. In
most journals there is code in this trigger that copies fields from the
previous record, called xRec.
7-8 Developing Navision Solutions

OnInsertRecord

This trigger fires whenever the user causes the form to automatically insert
the new record. This trigger can stop the insert action. If the trigger returns
FALSE, the record will not be inserted. If the trigger returns TRUE, the
record will be inserted into the database.

This trigger fires before the OnInsert trigger of the source table. If the code
in the trigger returns FALSE, the OnInsert of the table will never occur.

This is a good trigger to use to make sure all required fields have been
filled in. However, the insert may occur right after the primary key fields are
filled in. This is usually too early to test the other fields.

OnModifyRecord

This trigger fires whenever the user leaves a record that has been
modified. This trigger can stop the modify action. If the trigger returns
FALSE, the record will not be modified. If the trigger returns TRUE, the
record will be modified in the database.

This Trigger fires before the OnModify trigger of the corresponding table. If
the code in the trigger returns FALSE, the OnModify of the table will never
occur.

This is a good trigger to use to make sure all required fields have been
filled in.

OnDeleteRecord

This trigger fires whenever the user attempts to delete a record from the
form. This trigger can stop the delete action. If the trigger returns FALSE,
the record will not be deleted. If the trigger returns TRUE, the record will be
deleted in the database.

This Trigger fires before the OnDelete trigger of the corresponding table. If
the code in the trigger returns FALSE, the OnDelete of the table will never
occur.

You can use this trigger to check for related records or other conditions that
would not allow a delete to occur.
Coding in Forms 7-9

Using a Form as a Timer

There is one trigger on a form that does not fit into the prior two catagories.
It is called the OnTimer trigger. This trigger is associated with a property as
well called TimeInterval.

Because the form has the OnTimer trigger and TimeInterval property, each
form can have only one timer event.

The TimeInterval Property

This property determines when the OnTimer trigger will fire. The value in
this property is a number of milliseconds. Once the form is open, the timer
begins. After one time interval (the number of milliseconds in the
TimeInterval property) the OnTimer trigger is fired. The timer begins again
when the trigger finishes. The OnTimer trigger continues to fire until the
form is closed.

The OnTimer Trigger

This trigger fires in response to the time interval being reached. The trigger
will fire whether the form is the active form (has focus) or not.

This is a good place to put code that you want to execute every few
seconds, minutes, or hours. This trigger is not used in the base application.
It could be used if you need to monitor records in a table and periodically
export them to a file.

Activity – Using the OnTimer Trigger

In this activity, you will use the OnTimer trigger and the TimeInterval
property of a form to monitor records in a table. Every few seconds, you will
display the current number of records in the table.

7. From the Object Designer, design form 14 (Salespeople/Purchasers).

8. Change the TimeInterval property of the form to 10000 (10 seconds).

9. Add a global variable called TotalCount of type Integer.

10. Add a textbox to the bottom of the form.

11. Set the SourceExpr property of the textbox to TotalCount

12. Add code to the OnTimer trigger to set TotalCount equal to Rec.Count.
7-10 Developing Navision Solutions

13. Add a line to the bottom of the OnTimer trigger that will UPDATE the
form.

14. Close and save the form.

15. Run the form.

What is the value of TotalCount for the first minute?

What is the value after that?

What happens if you add three records?

Using Hyperlink triggers with forms

OnHyperlink

The C/AL code in this trigger is executed after the OnInit trigger is executed
when the form or report that the URL is pointing to has been activated. The
syntax is Object.OnHyperlink(URL), where Object is a Form or Report data
type and any subtype. The URL is a variable or constant of data type Text
or Code.

OnCreateHyperlink

The C/AL code in this trigger is executed after the user creates a URL to a
report or form. The syntax is Object.OnCreateHyperlink(URL), where
Object is a Form or Report data type and any subtype. The URL is a
variable or constant of data type Text or Code.

A user can create a URL for the active form or report by clicking:

. File, Send, Link by E-Mail

. File, Send, Shortcut to Desktop

. File, Edit, Copy Link

C/SIDE will fill in the URL parameter with the default URL strong to the
form. However, you can change the default value by changing the contents
of the string.
Coding in Forms 7-11

Activity – Using the OnCreateHyperlink Trigger

When you select either send a shortcut to your desktop or Copy Link the
OnCreateHyperlink trigger will be fired. In this activity, you modify the
OnCreateHyperlink trigger to force Navision to open a new instance of
Navision to display the form.

1. From the Object Designer, design form 42 (Sales Order).

2. Add the following code to the OnCreateHyperlink trigger:

URL := URL + '&forcenewinstance=yes';

3. Close and save the form.

4. Run the form.

5. Click on File, Send, Link by E-mail

Notes:

You must use the ampersand symbol (&) to separate commands otherwise
you’ll get a message that the text is too long.

The Send, Link by E-mail function is integrated with your e-mail system and
allows you to send links by e-mail directly from Navision Attain. If you
choose to send a Link by E-mail or Report and your e-mail system isn’t
running – you’ll be prompted to choose the profile. Next, a new e-mail
message will be launched and the link will be automatically attached and
the subject will contain the name of the form or report that you are on at
the time.

The recipients open a link by double-clicking the link in the Attach field.
Obviously for the link to work it is expected that the person has access to
the system that is being referenced.
7-12 Developing Navision Solutions

7.3 CONTROL TRIGGERS

This section divides the control triggers into logical groups. A control may fit
into more than one group. In fact, some controls may fit into all groups.

Common Control Triggers

This group of triggers apply to most controls. The exceptions are container
controls (Tablebox, Tab, Frame) and static controls (Label, Shape, Image,
Matrix). Every other type of control has these two triggers.

OnActivate

This trigger fires when the user enters the control (this is when the control
gains focus). This could be initiated by pressing Tab, Enter, or one of the
arrow keys or by using the mouse.

OnDeactivate

Fires when the user leaves the control (this is when the control loses
focus). This could be initiated by pressing Tab, Enter, or one of the arrow
keys or by using the mouse.

Data Control Triggers

The following triggers are available on all data entry controls (TextBox,
CheckBox, Option Button, PictureBox, Indicator). Both of these triggers fire
before focus is actually changed. If an error occurs, the user is left in the
control that triggered the event.

OnValidate

This trigger fires when the user leaves the control after changing the data.
It fires after the field OnValidate trigger of the source table (if the control is
bound to a field). No table updates are allowed in this trigger.

OnAfterValidate

This trigger fires right after the OnValidate trigger of the control. The
difference is that table updates are allowed in this trigger.
Coding in Forms 7-13

Textbox Triggers

Textboxes are the most widely used control in the application. They can be
found on most forms. For this reason, they have some special triggers that
apply only to textboxes.

OnFormat

This trigger fires when the control is about to display a new value. You can
use this trigger to change the appearance of the text. There are control
functions that allow you to change the fore color, boldness and even the
indentation level. This is where you could display negative values in red, for
instance.

OnBeforeInput

This trigger fires when the user puts focus on the control. It fires before the
OnActivate trigger.

OnInputChange

This trigger fires every time the user inputs or deletes characters. There is
no way to know what exactly was changed.

OnAfterInput

This trigger fires when the user leaves the control after changing the field. It
does not fire if the user never edited the field(pressing F2 counts as
editing). This trigger fires before the OnValidate trigger of the field or
control.

OnLookup

This trigger fires when the user presses the Lookup button. If any code is in
this trigger, the Lookup button will be shown (unless specifically turned off).
This overrides (replaces) any code in the OnLookup trigger of the field as
well as the default code (normal action of the TableRelation property to
allow lookups).

OnDrillDown

This trigger fires when the user presses the DrillDown button. If any code is
in this trigger, the DrillDown button will be shown (unless specifically turned
off). This overrides (replaces) the default DrillDown action of a FlowField.
7-14 Developing Navision Solutions

However, the trigger can be used with any type of field (the button will be
added to the control).

OnAssistEdit

This trigger fires when the user presses the AssistEdit button. If any code is
in this trigger, the AssistEdit button will be shown (unless specifically turned
off). There is no default action for the AssistEdit button. Code must be in
this trigger for the AssistEdit button to do anything.

Button Triggers

The following trigger applies only to buttons (Command Buttons, Check


Boxes, Option Buttons, and Menu Items). Every type of button has at least
this trigger. Menu items have no other triggers except this one.

OnPush

This trigger fires when the user clicks (or perhaps hits the space bar) on the
button control. For check boxes and option buttons, the value of the source
expression is changed before the trigger is fired.

Matrix Box Triggers

These triggers are identical to the corresponding Form triggers mentioned


before, except that these triggers are linked with the MatrixRec record
variable. This is a variable of the Matrix box control and accesses the
MatrixSourceTable (see MatrixSourceTable property of the Matrix box
control).

. OnFindRecord

. OnNextRecord

. OnAfterGetRecord

. OnAfterGetCurrRecord

. OnBeforePutRecord
Coding in Forms 7-15

7.4 SPECIAL FORM AND CONTROL FUNCTIONS

In this section, you will learn about many functions that you can use in form
and control triggers.

Form Functions

Form functions are called through the currForm variable. This variable is a
reference to the instance of the current form. The following list of functions
is not complete. This is a list of the most useful form functions. See the
online help for a complete list.

CurrForm.UPDATE

Use this function to save the current record and then update the controls in
the form. If you set the SaveRecord parameter to FALSE, this function will
not save the record before the system updates the form.

You may want to use this function anytime you change Rec or some other
variable that is displayed on the form. This will force the form to re-read
everything that it is displaying.

CurrForm.Close

Use this function to close the current form. Most buttons that close a form
(OK, Cancel, Yes, and so on) close it through the PushAction property of
the button. If, however, you wanted to run code in the OnPush trigger of the
button and then close the form, you would have to manually close the form
with this function.

CurrForm.Editable

Use this function to return the current setting of the Editable property and to
change the setting of the property. Other properties can also be changed
from within code, but not all of them can. This property is especially useful
if you want to use the same form for viewing and editing, but only some
users can edit the records.

Control Functions

To access the functions of a control you must name the control first. Then
you can use the following syntax:

CurrForm.<ControlName>.<Function>
7-16 Developing Navision Solutions

The following list of control functions is not complete. This is a list of the
most useful control functions. See the online help for a complete list.

Editable

Use this function to return the current setting of the Editable property and to
change the setting of the property. Other properties can also be changed
from within code, but not all of them can. This property is especially useful
if you want to use the same control for viewing and editing, but only some
users can edit the field.

Visible

Use this function to return the current setting of the Visible property and to
change the setting of the property. This property is especially useful if only
some users are allowed to view a particular field.

Be aware that if you make a textbox not visible in a tablebox control, the
user can change that property by going to View, Show Columns.

UpdateEditable

Use this function to make a textbox not editable or editable dynamically. It


does not change the Editable property. The next time the user enters the
textbox, you will have to call this function again to make it editable or not
editable.

This function can only be called from the OnBeforeInput of the control.

UpdateForeColor

Use this function to change the setting of the ForeColor property for a
textbox dynamically. You can use this to make negative amounts red or
any other color. It changes the color of the text for this record.

This function can only be called from the OnFormat trigger of the textbox.
This means that it is called just before the user sees the value for the
record. On a tabular form, this function would be called for every record.

UpdateIndent

Use this function to set the indentation of the text in a textbox. Changing
the indentation of the text, allows you to show data in a sort of tree format.
This technique is used in the Chart of Accounts.
Coding in Forms 7-17

This function can only be called from the OnFormat trigger of the textbox.
This means that it is called just before the user sees the value for the
record. On a tabular form this function would be called for every record.
7-18 Developing Navision Solutions

Activity – Identifying the Advanced Features of Common Navision Forms

In this activity, you will take a look at some of the advanced features on
forms and identify how they work.

1. Go to the Chart of Accounts form on the General Ledger menu.


Notice that the first and second columns in the table box have special
formatting:

a. Where is the code that does this formatting (name the


triggers)?

b. Describe what the code is doing (what are the business


rules?).

2. Go to the Customer Card form on the Sales & Receivables menu.


Notice the Assist Edit button on the Number field:

a. Where is the code that runs when this button is pressed


(name the triggers)?

b. Describe what the code is doing (what are the business


rules?).

3. Stay in the Customer Card form and look at the code in the
OnAfterGetRecord trigger.

a. What is this code doing?

b. Why is it needed here?


Coding in Forms 7-19

4. Go to the General Journal form on the General Ledger menu. Go to


the last line in the Journal and press the Page Down button. Notice that
the record has not been inserted but many fields are already filled in:

a. Where is the code that fills in these fields (name the


triggers)?

b. Describe what the code is doing (what are the business


rules?).
7-20 Developing Navision Solutions
CHAPTER 8
LOW IMPACT PROGRAMMING

Low-Impact Programming is the methodology


used to create modifications in such a way that
they have as little effect as possible on the server
and application.

This chapter covers the following sections:

8.1 Low Impact on the Application


8.2 Low Impact on the Server
8.3 Low Impact on the Network
8.4 Low Impact on the Database
8-2 Developing Navision Solutions

8.1 LOW IMPACT ON THE APPLICATION

In this section, you will learn how to make changes to objects with minimal
impact on the existing application. By making changes in this way, you can
make future upgrades much faster and safer for your customer.

Tables

How can you make changes to tables in the application and allow those
changes to be easily upgraded later? Keep in mind the following points:

. It is better to add a new table than to modify an existing table. If the


functionality that you want to add can be done by adding new tables, then
that will be easier to upgrade.

. It is better to add a new field to an existing table than to modify the


meaning of an existing field. Adding fields to tables is usually not a
problem, but there is a limit to the size of a record. You should never
change the meaning of a field even if the field is not used by the base
system. The upgrade toolkits are not designed to convert the data if you
changed the data type or length of an existing field. There may also be
relationships that are added in the new version of the program that
conflict with your changes. Avoid this at all costs.

. It is better to change the name of an existing field than to add a new field
if the only purpose is to change the name. If your customer prefers the
term client number to customer no., then change the name of the field.
Remember that fields are referenced by their numbers not their names.

Reports

If you want to make upgrades easier when you modify reports, you should
always create a copy of an existing report and modify the copy. This is
especially true if there are changes to the layout, since they are so hard to
document. Then modify the report menus (documenting these changes) to
access these new reports, rather than the original ones. Applies to batch
jobs and Dataports as well.

If you must modify existing reports, document your changes well. You will
most likely have to make those changes again in the next version of the
product.

Codeunits and Code

When making modifications to code in a codeunit or any other object, try to


Low Impact Programming 8-3

write your functionality into a separate function. Then you can call that
function from within the existing triggers or functions.

Forms

This is where most changes should and will be made. It is also fairly hard to
document if you are not careful. Be sure to make a listing before any
changes are made and then the documentation will be easy. Copying a
form can be more complicated than simply modifying it. Many buttons and
properties may be linked to that form.
8-4 Developing Navision Solutions

8.2 LOW IMPACT ON THE SERVER

Always remember that up to 500 users could be attached to the server, so


write your code so that the server is bothered as little as possible. Also
remember that the user has a computer too, so try to spread the
processing as much as possible to the client.

COMMIT

The Commit function is handled automatically by the database for almost


all circumstances, so this function should almost never be used. Even
when the system says you must do so, try to rewrite your code so that it is
unnecessary. There should be few reasons to call up a form object while a
transaction is in progress.

LOCKTABLE

Once a table is locked, none of the other users can use it. Therefore, limit it
only to where it is necessary. Remember that an insert, modify, rename or
delete will lock the table automatically, so you would not need a Locktable
for most modifications.

INSERT, MODIFY, DELETE

Try to structure your code in such a way that you do not have to look at the
return values of these functions. When you use the return value, the server
has to be notified right away so that a response can be obtained. If you do
not look at them, the server is not notified until something else requires it.
Don't worry, if something really bad happens (out of space, and so on), you
will still get the run time error which will roll back your changes.

CALCSUMS, CALCFIELDS

Use these two functions whenever possible rather than spinning through
records to add things up. Doing one CALCSUMS or CALCFIELDS takes
about the same amount of server time as doing one GET.
Low Impact Programming 8-5

Keys

Select Keys carefully so that data is obtained in the most efficient order
possible. Be especially careful to select keys that support the filters you will
be using on the records. In this way, masses of record can be sorted and
skipped, without ever having to read the individual records.

Also be sure to attach SumIndexFields to the most correct key. You may
have a SumIndexField attached to a short key. This key may work for most
sums. Then you may attach the same field to another more complicated
key that is used occasionally when the user sets several FlowFilters. In this
way, the most efficient key will be used.

Validity Testing

Many times, you will be testing user input for validity. Whenever you do so,
test things that are available without bothering the server first. That way, if
they are invalid, you will not waste the server's time.

For example, before doing a GET to test the validity of a user's input, make
sure that the user entered something. If the field is blank, the GET will most
likely fail. So, if the field is blank, you can display an error, without making a
server call.

Temporary Tables

Temporary tables allow you to minimize the impact on the server in two
main areas: reducing the calls to the server and in allowing you to create
records that may not yet exist on the server. Reports often need to read
from a particular table; however, the report may need to read from the table
many different times depending on the processing being done. Therefore,
by reading the data once from the server and storing the data in a
temporary table we are able to minimize the impact on the server as well as
the amount of network traffic. A second example is when working with
dimensions there may be times when you want to create data; however,
you are not ready to insert the information on to the server. Therefore, by
using a temporary table you are able to process the data multiple times
based on the dimensions that you are working with in your code. Since the
processing is taking place on the client the impact on the server is
minimized. Furthermore, once the data has been processed you only have
to process the information once from your temporary table instead of
making multiple calls to the server.
8-6 Developing Navision Solutions

8.3 LOW IMPACT ON NETWORK TRAFFIC

This is closely related to server access, since all server access takes place
over the network. However, it is useful to get the server to do as much work
as possible in one network packet rather than the same amount of server
work, but taking multiple network packets.

MODIFYALL, DELETEALL

Consider setting keys and filters and then using these calls which send only
one command to the server, rather than getting and deleting or modifying
each of many records, which will send more information back and forth
through the network.

CALCSUMS, CALCFIELDS

Remember that each of these functions allows you to use multiple


parameters to sum many fields with one function call (and one network
packet).

Filters

Even if a filter is not on a key, it is better to set a filter and have the server
skip the records itself, rather than having to get each record and test it
yourself before telling the server to get the next record.
Low Impact Programming 8-7

8.4 LOW IMPACT ON DATABASE SPACE

Note that this is lower priority than the above, but still a consideration. The
smaller the database is, the faster backups and database tests will run.
Most databases will grow, but you can impact this growth in the following
ways.

SumIndexFields

Note that each SumIndexField takes up a significant amount of space.


(They save a lot of time during reports and other data presentation
functions, but they take a little extra time during data entry.) Make sure that
the amount of time they save will be worth the space that they take up.

Keys

Each key takes up a significant amount of space, even without sum fields.
Be sure the time they save is worth the space they take. One thing to
consider is that if the use for a key (say, a special report) is only significant
once a month or once a year, you might deactivate that key and set up a
key group so that it can be reactivated without your intervention whenever
the user needs it.

Duplication of Data

Do not store things like customer names, account descriptions, item


descriptions, and so on, in Ledger type records. The same name will then
appear numerous times, taking up excess data space. Store the Customer
Number, Account Number or Item Number, and so on, instead. Also, use
the names or descriptions as defaults for the ledger entry description so
that if the user wants it, they can leave it.

Note

Remember that most of the above rules are broken once in a while in the
standard application and that there will be occasions when you will have to
as well. Just make sure that it is necessary.
8-8 Developing Navision Solutions
CHAPTER 9
THE UPGRADE PROCESS

This chapter introduces the student to how


upgrades are done in Navision Attain. It also
describes the tools that can make the upgrade
process faster and easier.

This chapter covers the following sections:

9.1 Why Upgrade


9.2 Definitions
9.3 Types of Upgrades
9.4 Planning for an Upgrade
9.5 Upgrading the Executables
9.6 Upgrading the Objects.
9.7 Tools for Upgrading the Objects
9.8 What If?
9.9 Upgrading the Data
9-2 Developing Navision Solutions

9.1 WHY UPGRADE?


There are two very important reasons to upgrade your customer’s Navision
installation.

Your Customer has already paid for it.


Our current policy encourages you to sell Upgrade Agreements with every
sale, as such many customer’s will have an upgrade agreement in effect.
Our future policy, soon to be in effect, will require you to sell an Upgrade
Agreement with every sale. Thus, many of your customers have paid for
and will expect an upgrade.

Your Customer will pay for it.


Some Upgrades will be important enough that your customer will want
them even if they have not paid for them in advance with an Upgrade
Agreement. Also, even with the Upgrade Agreement, the customer is only
purchasing the product upgrades and not the installation of the product
upgrades. The installation fees could be worth while, if upgrades are
planned for and implemented properly.

Benefits to the Customer

Gain Access to New Features


First, they may gain access to new features. For example, in version 2.60
we have implemented Electronic Payments to Vendors. If your customer
does not upgrade to version 2.60, they will not be able to use this feature.

Improvements to existing features


For example, in version 2.60, there are improvements to financial reporting,
budgets, job cost, sales order, contact management, accounts payable and
payroll. If your customer would like any of these improvements, they will
want to upgrade to version 2.60.

Improved performance
For example, in version 2.60, SQL option, the performance of long batch
jobs have been improved a substantial amount.
The Upgrade Process 9-3

Removal of Problems
Problems (read “bugs”) are removed with every single upgrade. In version
2.60, there are 51 bug fixes.

. Allow Upgrades to new Operating Systems and Hardware

For example when Navision Financials version 2.60 was released, it added
support for Windows 2000, didn’t include support for SQL 2000.

. Better Support when on the Current Version

For example when Navision Financials version 2.60 was released, it didn’t
include support for SQL 2000; however, service pack C was released to
add that support.

Furthermore, your customer will tend to receive better support when they
are on the current version. As versions become obsolete, fewer and fewer
people have expertise in that version.

Benefits to the Solution Center

. Better Customer Relations

Customer Relations are built on providing services to the customer. An


Upgrade is an opportunity to provide a service to an existing customer that
is NOT in response to a customer complaint. It also gives you a chance to
talk with your customer and find out what new needs they may have which
you could provide for them. Finally, it keeps you in their minds.

. Easier to Support when the Customer is using the Current Version

In many cases, the solution to a problem your customer has is found in a


newer version. In other cases, you may find it hard to support an old
product when nobody at your organization can remember it.

. Remove Problems before the Customer notices them

When your customer finds a problem that turns out to be caused by a bug,
they will often want you to fix the problem for nothing, and even if you do
(you shouldn’t, but…), they will be irritated with you and with the product. If
an upgrade will fix a bug that the customer has not run into yet, they will
pay for it with the upgrade, and there will be one less opportunity for them
to be disappointed.

. Opportunities for Additional Sales


9-4 Developing Navision Solutions

Navision often includes new features that your customer may be interested
in using. However, in order to get this new feature, the customer must
upgrade, which generates service revenue for you.
The Upgrade Process 9-5

9.2 DEFINITIONS
Before we get into the types of upgrades, we need to explain a few
definitions so that we are all talking about the same thing.

Executables
The Navision programs that run under the operating system and include
the client, the server, the C/ODBC drivers and so on. They can be identified
by the fact that you will see them in the Windows or NT Explorer, and they
are files that end with “.exe” or “.dll”, and so on.

Application
The programs that run within the Executables, more specifically within the
client, which is known as C/SIDE. The Application is made of Navision
Objects.

Functional Area
A major division within the Application, like General Ledger or Inventory,
that can be identified by the fact that there is a Main Menu button that
identifies it and by the sub-menu that appears when this button is pressed.

Granule
A set of objects that is purchased separately which contains a set of
features.

Feature
A small set of functions that are part the Application. The General Ledger is
a Functional Area within the Application, Account Schedules is a Granule,
and Date Comparison is a Feature of Account Schedules.

Bug
A piece of the program that does not work as intended. Note that if a piece
of the program is not working the way you would like, that does not make it
a bug. Only if it does not work as it was intended to work by Navision, it is a
bug.
9-6 Developing Navision Solutions

Enhancement
An “enhancement” is when an existing feature is made to work better in
some way, easier to use, faster, performing additional functions, and so on.
If a feature is merely made to work as it was originally intended, then that is
a bug fix, not an enhancement.
The Upgrade Process 9-7

9.3 TYPES OF UPGRADES


There are 4 types of upgrades possible, in order from easiest to most
difficult to install:

Improvements
Improvements are usually bug fixes, but sometimes include minor new
features or feature enhancements; however, no executable changes occur.
If there is a data upgrade, it is only a simple procedure to run which will
correct any damage a bug might have caused.

It is recommended that you have a technical person review each Navision


improvement to see whether it applies to any of your customers. The
person should determine how long it would take to implement the
improvement. Therefore, a determination can be made as to whether you
want to schedule time to install the improvement immediately or to wait until
there is an improvement that you will install, and install this one at the same
time. Do not skip improvements; install them in order.

Service Packs
Service Packs will include all previous Improvements, as well as additional
bug fixes and/or minor feature enhancements. In addition, they could
include changes to the executables. Any data upgrades will be simple
procedures which will correct any bug damage. Sometimes, a Service Pack
will include the release of a new Product CD.

It is recommended that you install every Service Pack, unless you


determine that it is completely irrelevant to your customer.

Minor Release
Minor Releases will include all previous Improvements and Service Packs.
They could also include additional bug fixes, feature enhancements, minor
new features and executables changes. In addition, a Minor Release will
often include the release of one or more new major features. As a result,
there could be minor data upgrades necessary. A Minor Release will
always include a new Product CD.

You cannot install a Minor Release on your customer site unless your
customer has an Upgrade Agreement in force. If they do, you should install
the minor release as soon as it can be scheduled. If not, you should review
the new features and see if there is anything there that could convince your
customer to purchase an Upgrade Agreement.
9-8 Developing Navision Solutions

Version 2.60, was a Minor Release that included all accumulated


improvements since version 2.50. Also included were four (4) new Major
Features, Executables changes, no data upgrade and a new Product CD.
Furthermore, version 2.60 added support for Windows 2000.

Major Release
A Major Release will include all previous Improvements, Service Packs and
Minor Releases. It could also include additional bug fixes, minor and major
features, and executable changes. Usually, there are major functionality
changes included in a Major Release, which means that the way the
application works could have been changed. This means that a Data
Upgrade will also be required. Also, New Functional Areas could be added
to the system, and even new Products or Product Options added. A Major
Release will always include a new Product CD. The last Major Release was
when we went from version 1.20 to version 2.00.

You cannot install a Major Release on your customer site unless your
customer has an Upgrade Agreement in force. Since Navision normally
does not release the upgrade kit until a month after the Major Release, it
gives you time to familiarize yourself with the new features. After becoming
familiar with the release, you should discuss with your customer when they
would like to upgrade. Please note that this will almost be like installing a
new product, it must be well planned and smoothly executed, or it will
become a nightmare to your customer, and to you.

The version tells all


How can you tell what kind of upgrade you need to perform? Besides the
clues we have already given you, the final answer is the new version
number. The version number is found in two places. One is on the Help,
About screen, which states the version for the Application and for the
Executables. The other are the version tags found in the Object Designer,
which tell you the version of each particular object.

Here is an example showing you how this looks on the Help, About screen.

Version US 2.60.B (2.60.B)

The number in parenthesis is the Executables version, while the other


number is the Application version. The version number has several parts.
The Country Version, US, comes first (the world wide version has a W1
here). The Major Release number, 2, is next and the Minor Release
number, .60, follows closely behind. The Service Pack number, .B, and the
Improvement number (none shown) are last.
The Upgrade Process 9-9

Whichever number was the furthest to the left to be changed, is what sort
of release this is. For example, when going from version 2.50 to version
2.60.02, even though both the Minor Release and the Service Pack
numbers changed, the one furthest to the left is the Minor Release number,
so this is a Minor Release.

Please note that on the Help About screen, the Service Pack is usually
displayed as a letter, not a number (B, not 2), and the Improvement number
is usually not displayed at all. The full number is usually only given on the
Version Tags seen in the Object Designer.

Also note that the Application Version is set in Codeunit 1.


9-10 Developing Navision Solutions

9.4 PLANNING FOR UPGRADES


The most important step in the upgrade process is to plan for it in advance.
Well in advance. When you install Navision on your customer’s system, you
know that you will upgrade eventually, so you might as well plan for it.
Start this planning when you are making your initial implementation plans.
Many times, an upgrade may be made very difficult because of a decision
made when you first implement the customer’s system. The largest area is
in customizations. Other areas are also important. For example, an
upgrade of the executables usually requires changes on every single client
machine. Using Microsoft’s Systems Management Server (SMS) to install
all clients from a single source may save hours during every such upgrade.

Throughout the implementation process, keep you customer informed of


upgrade considerations. For example, if they do not know that upgrades
will bring their system down for a while, they will be irritated when an
upgrade is done. If they know in advance, they can help plan by telling you
when might be the best time to upgrade.

There is no overstating the importance of keeping good records. You must


know what is on every customer installation, so you can -

. Make appropriate decisions about whether to install an improvement.

. Make correct plans about how long an upgrade will take.

. Know when to ask your customer about whether they want certain new
features.

. Know when an upgrade will override a customization that you have done.

Customizations
The most important planning you can do for upgrading is when you are
doing customizations. You must consider how you will upgrade your
customizations whenever you modify your customer’s software. The best
way to do this is to use the recommended methodology.

One thing you will find is that we recommend including considerations for
Upgrading with every Design Specification. In addition to helping you plan
ahead, it also helps the customer to make intelligent decisions. If they know
that a particular design will mean extensive upgrade problems every time,
then they may opt for an alternate design, even if it costs more money at
first.
The Upgrade Process 9-11

Also, using the Low Impact programming techniques are part of the Attain
Development course. Only make changes to the base application when
necessary, and then make them in a way that is easy to upgrade. In
addition, document every change. This is not optional, this is not just “nice”,
it is essential. You cannot have a smooth upgrade without knowing what is
on the customer’s system.

If your customer has development tools, you have additional problems.


You need to tell your customer to use the same techniques that you have
learned when programming. You need to be copied on all of their
documentation.

Scheduling
When it is time to actually do an upgrade, the planning starts with
scheduling. Once you decide that an upgrade will be done, you must start
planning a schedule with your customer well in advance. There are several
reasons for this -

The Navision Server (or SQL Server) must be taken down every time you
do an upgrade, no matter how small. Even if there isn’t an Executables
upgrade, there is an object cache on every client. In addition, a backup
must be performed before every upgrade, no matter how small. Treat every
upgrade as though you were implementing a new system, since in some
ways, you are. Because of the need to take down the server for a period of
time, you may need to install the upgrade during off-hours, or even over a
weekend, if there is a data conversion as part of the upgrade.

There are several things you can do to relieve some of these time issues
and make the upgrade less painful for the customer, but they require
planning and scheduling. First of all, enlist the support of your customer’s
IT department for anything that they can do for you. For example, the
customer’s IT department can see that the server is taken down and that
backups are performed before you get there. Furthermore, they can also
distribute Executables changes, and communicate with the users about the
upgrade. Second, do as much as possible at your own site. For example
upgrade all objects, write and test the data upgrade routines, and finally
test the installation. When you’ve done all this at your site, you are ready to
bring everything to your customer’s site for execution. However, make sure
you plan for contingencies - what if things fail.
9-12 Developing Navision Solutions

9.5 UPGRADING THE EXECUTABLES


If there is an upgrade to the executables (the Navision programs that run
on the Operating System), this part of the upgrade will always be done first.
It can be scheduled separately from any other steps. For example, you
could upgrade the Executables portion of a Major Release one weekend,
and not schedule the upgrades for the Objects or Data for another 2 or 3
months.

Upgrading the Executables is very easy, normally just copying the new files
over the old ones. However, when you have 50 or even more Client
machines to upgrade, this can take a lot of time. Microsoft’s Systems
Management Server (SMS) is a tool than can be used to reduce the time
needed a great deal.

Sometimes, the internal database structure could change. The last time
was when the maximum record size went from 2,000 to 4,000 bytes with
version 2.50. When this happens, you will be told, but all you need to do is
make a Navision Backup under the old system, and then restore from the
backup after you have installed the new system.

The first step is to determine what is being upgraded. This could be an


upgrade of the Navision Server, the Client, or both. It could also involve
other Executables, like C/ODBC. Sometimes, a new Customer License is
required. If so, be sure to order one before you proceed. Read the upgrade
kit documentation carefully to determine what exactly is upgraded.

Navision Server
If you need to upgrade the Navision Server, follow these instructions. First,
if you need to do a Navision Backup, then make the backup while the old
system is still running. This must be a complete backup - all companies,
data common to all companies, and application objects. Then, you can
have everybody log off the system and bring down the Navision Server.
Now, make a complete backup copy of all Navision files (executables and
database) using tape or another fixed disk drive. This backup will be used
should anything go wrong with the upgrade.

Now, install the new Server components. This will normally involve copying
the new fields over the old ones. Sometimes it will require a new
installation, but just install the new version over the old version.

Normally, there is a Client also located on the server machine. If so, and if
there is an upgrade to the Client Executables, then do this now. If there is a
new License involved in the upgrade, copy it into both the server directory
The Upgrade Process 9-13

(the one which contains server.exe) and the client directory (the one which
contains fin.exe).

If a Navision Backup was required due to a database structure change, you


should now delete the old database, bring up the Navision Client on the
Server Machine, and create the new database (using the same name and
location as before). Restore the complete Navision Backup into this new
database. Bring down the Server Machine Client.

Now, bring up the Server, and bring up the Server Machine Client. Log onto
the Server from the Server Machine Client. Run a few simple tests to make
sure that the system has been properly restored and that you can still run
basic functionality.

If there are Executable upgrades for Clients, bring down the Server now
and upgrade all of the clients. We will cover this on the next slide. Once this
is done, you can bring the server back up, and you are back on line.

Navision Clients
If there are Executable Changes for a Client, the steps are quite simple.
Bring down the client (exit Navision). Copy the new Client files into the
client directory, or re-install the client; whatever the upgrade kit instructions
say.

If there are other Executable Upgrades, like C/ODBC, C/FRONT, and so


on, then install them now as well. The upgrade kit will include specific
instructions.

If a new Customer License is required, copy it into the Client directory at


this time. Then you can bring the client up.

You need to repeat the above steps for every single Client in the
installation. If there are many Clients, and you do not have SMS to help you
out, you may want to enlist the customer’s help in doing this part of the
upgrade. The steps are very simple, but must be repeated on each system.

If the Server Executables are also being upgraded, you must first bring
down all Clients, upgrade the Server, Upgrade all Clients, bring the Server
back up, and then bring all Clients back up.
9-14 Developing Navision Solutions

9.6 UPGRADING THE OBJECTS


The Object Upgrade is the most difficult part of any upgrade, whether it is
an Improvement or a Major Release. What makes this difficult? Well, first
Navision a/s in Denmark made worldwide changes to the application
objects. Next, the NTR makes changes to these same application objects.
Then, you made custom changes to these same application objects. In fact,
if your customer has the design tools, they may have made their own
changes to these same application objects. All without consulting each
other!

Each NTR is responsible for merging Navision a/s changes with their
changes (if applicable), which is why it takes time between the worldwide
release of a version and the local NTR release of that same version.

It will be your job to merge these changes with the changes that you made
during customization. There are many things that help, but as we already
pointed out, a lot of them depend on you following the rules for
documenting customizations. We will review the Version Tags and
Modification Flag rules as we go through the upgrade steps.

We will review the basic process that you must go through for each
individual application object. For this example, we will use Table 18, the
Customer Table, as a stand-in for any application object.

For each object, you have a possibility of up to 3 versions. First, the old
Base version, which you can get from the CD that you installed from. It is
very important that you document what version you installed from. Second,
you have the new Base version and also you also have the old Customized
version, which is what is running on your customer’s system right now.

What we want when we are through, is a new Customized version of the


object, which will include all of the customizations, plus all of the changes
that we made for this upgrade.

Obviously, there are four possibilities here:

Possibility One
First, neither you nor we made any changes to this object. In this case, the
object on your customer’s system will not change during this upgrade.
The Upgrade Process 9-15

Possibility Two
Second, you customized the object, but we do not change it for this
upgrade. Again, in this case, the object on your customer’s system will
remain unchanged for this upgrade.

Possibility Three
The third possibility is that we change the object for the upgrade, so there
is a new base version, but you did not customize this object. In this case,
the new object will replace the object on your customer’s system.

Possibility Four
Finally, the possibility that causes the problems - you made customizations
to the object, and we also changed the object as part of the upgrade.

In this case, you must first determine which version changed the object the
most, either your changes, or our changes. Whichever one changed the
object the most, you should use that object. Then, you must change that
object so that it includes all of the changes that were made by the other.

Here is an example. In this case, the upgrade you got from us contains
more changes than you made during customization. Therefore, you should
get the object that you got from us, re-do your customizations on that
object, and then replace the object on your customer’s system with the
new, customized object.

Here is another example. In this case, the customizations you made


contain more changes than we did for the upgrade. Therefore, you should
get the object from the customer system, re-do all of our changes on that
object, and then replace the object on your customer’s system with the new
customized object.

An Improvement
Let’s run through a simple example to explain the steps you need to do to
upgrade objects.

The Situation
In this situation, you have customized Financials version 2.50 for your
customer. You have not installed any improvements for version 2.50.
9-16 Developing Navision Solutions

Customizations
You have performed the following small customizations for your customer.
First, you have modified the check printing routines in Purchases and
Payables to add the company logo to the check. Second, you have added
the Customer P.O. Number to the Job Card, and you have printed it out on
the Budget-to-Actual report. Finally, you have modified the Purchasing
system to add two new boolean fields to the Purchase Order Lines, which
are shown as check boxes. The first field is called Expedited and can be
checked by the user to indicate that the line has been expedited. The other
field is called On Time, and is calculated by the system automatically
during posting, by comparing the actual receipt date to the expected receipt
date. The total number of objects you have modified is 20, which is a fairly
small customization.

Since version 2.50 has been released, Navision a/s and your NTR have
released several improvements. Several of the objects have been modified
and some have been modified more than once. Furthermore, there are also
some new objects.

Assumptions
Here are the assumptions that we will use for this exercise. First, you have
followed all of the recommendations for Version Tags, that is, that in the
customer’s system all modified objects have been tagged with an additional
Version Tag (we will use CR01), and that all Modification flags are turned
off.

Second, your customer has no design tools; therefore, you will not be
required to go to the customer site to pick up the objects, since the
customer has not changed them.

Third, you decide to install all improvements, so that the upgrade to version
2.60 will go smoother. This is a good idea since most of the object changes
in version 2.60 are simply to implement the version 2.50 improvements.

Fourth, you kept a copy of the customer’s database in house. This copy
has just objects, either no data or minimal (Cronus) test data. Fifth, you
have a coy of the current base application with all improvements installed.
If you didn’t, you could simply start with a fresh installation of the 2.50 CD
and then download the improvements from the web site and load them in.

We will assume that you have downloaded the Compare Tool and that you
are familiar with how to use it.
The Upgrade Process 9-17

Step by Step
Here are the steps involved in the upgrade -

1. Make a working copy of the customer’s database (objects only).


Simply do a Navision backup, create a new database, and then restore
Application Objects only.

2. Get the Improved Objects from your Base + Improvements database.


To do this, go into the Object Designer and press the All button. Then
put your cursor in the Version Tag column, and set a filter to your
NTR’s code “US2.50*”. This will filter out all but the improved objects.
Then select all objects (ctrl-a) and export. Export as a Navision Object
file, with an extension of .fob.

3. Go back into your Working Copy database and import the Navision
Object file into the Object Designer. Normally, there will be conflicts,
but even if there are not, bring up the Object Import Worksheet
anyway. Look over all of the objects, find the conflicts, and write down
the object type and ID on a Conflict List. Skip any objects that have a
conflict for this step.

Here is an example of the Object Import Worksheet -

Note that Report Number 10210 has been selected above. It has a little
yellow circle with an exclamation point in the “Warning” column. This
means there is a conflict. If you look below, you will see the reason for the
conflict. The existing object has the CR01 Version Tag, which indicates it
has been customized. However, the new object has a different NTR
Version Tag than the existing object, which means it has been upgraded.
Since both changes have updated the same object, there is a conflict. We
write down the object type and ID (Report 10210) and also the Version Tag
9-18 Developing Navision Solutions

of the New Object (2.50.00.42) and then, in the Action column, we click the
AssistEdit button and select Skip for this object.

Note that on this page, there is only one other object conflict Report 10253.
For the remaining objects, there is no conflict; the new objects will simply
replace the old ones since the old ones were not customized.

4. Review the four possibilities again. First, an object might not have
changed in either the customized version or the new base version. In
this case, we would not have imported the object, since only the
changed objects were imported. These will not show up in the Import
Worksheet.

Second, the object might only have changed in the customized version,
but not in the new base version. In this case, the object will not be
imported, since only new base objects are imported. Again, these will
not show up in the Import Worksheet.

Third, the object might have changed only in the new base version, but
not in the customized version. These objects are in the Import
Worksheet, but they show up with no conflicts. They can just be
imported.

Fourth, the object might have been changed in both the customized
version and the new base version. These objects are in the Import
Worksheet. In this case, they show up with a conflict. Skip these
objects (do not import them) and write them down to create a Conflicts
List.

5. Review the Conflicts list. You should look up each object in the
Conflicts List in the Change Log that came with the improvements.
Remember to check all the improvements, since it might have been
modified more than once. If you look at the Change Log in the Latest
Improvements page, where they are all together in one log, and you
start at the bottom with the version you see in the Import Worksheet,
you can find all of the changes more easily.

6. For each object in the Conflicts List, you should ask yourself the
question, is the upgrade modification small? More to the point, is it
smaller than (or even almost as small as) the change you made for
your customization?

If the answer to this question is Yes, then use the Change Log that
came with the Improvement to implement the improvement on the
Customized object in the Working Copy database.
The Upgrade Process 9-19

If the answer to this question is No, then import the new base object
into the Working Copy database, and then use your Change Log to
implement the customization on the New Base object.

Do you not have a change log? Then use the Compare tool to create
one. This will also make it easier for you to decide which change is
smaller.

Either way, set the version tag to reflect both new versions. In this case
(Report 10210), the new version tag will be US2.50.00.42,CR01. This
shows both the new US Version Tag and the Customization Version Tag.

There is one exception to these rules. If this is a table object, we may have
added a new field in a number range that you cannot add. In this situation,
if at all possible, import the object and then implement your customization
on the new object using your change log. If this is not practical, the only
alternative is to set the action to “Merge Existing<-New” and import the new
base object. This will bring in the new field(s). Then, apply the remainder of
the Change Log.

7. Do step 6 for every object on the Conflicts List. The hard part is over.

8. As an optional step, you can change the Version that is displayed on


the Help About screen. Note that this step is optional only for an
improvement. If this is a Service Pack or a Minor or Major release, you
must perform this step.

In the Object Designer, open Codeunit 1 and look for the function called
ApplicationVersion. An example is shown here.
9-20 Developing Navision Solutions

Simply change the displayed version to the new version you wish to
display, reflecting the latest base version tag in all the object. In this case, it
would be US 2.50.00.48. Just change the text between the single quote
marks to this text. After you update Codeunit 1 and save it, don’t forget to
update the version tag of this object as well, to the same version as you put
in those quotes.

9. The next step is to compile all objects. You do this to make sure there
are no conflicts with existing objects that show up. In the Object
Designer, press the All button, select all objects, and press F11 to
compile them. When it is done, any objects that did not compile will be
marked, so you can View, Marked Only, in order to check them out and
fix any problems.

10. Now, you must create the Upgraded Objects file. In the Object
Designer, select the All button, and then filter the Version Tag column
using “US2.50.*”. Once this is done, change all the Modification Flags
to unchecked (off).

11. Select all of the filtered objects (use ctrl-a) and export them as a
Navision Object File, a file that has the extension .fob. This is the
Upgraded Objects file.

12. Now, finally, you can install the Upgraded Objects on you customer
site. First, bring this file to your customer site. You should schedule a
non-busy time to do this. When you load objects, especially table
objects, sometimes tables are automatically converted or re-keyed,
which takes a long time when there is a lot of data.

At the Customer Site, bring down the server. Make a backup copy of the
database (enlist the support of the customer’s IT department for this step).
Start the client up which is located on the Server Machine, and open the
customer’s database in Local mode.

Now go into the Object Designer and Import the Upgraded Objects .fob file.
Remember, even if there are now conflicts, you have already resolved all of
them. Therefore, when you bring up the Import Worksheet, all you need to
do is press the Replace All button, and then do the import.

Once this is completed (it might be 30 seconds, it might be 3 hours or


more), compile all objects, then bring down the local client. Bring the Server
back up, and your customer can continue working on their newly upgraded
system.
The Upgrade Process 9-21

9.7 TOOLS FOR UPGRADING THE OBJECTS


Navision gives you several tools you can use to help you upgrade. Each
one may be best for different circumstances.

Import Worksheet
You have seen how the Import Worksheet can be a useful tool. It is the
best tool to use to rapidly determine object conflicts. It can also be used to
add new Objects and Fields in the base number ranges that you may not
have permissions for. Finally, as you can see, it is the best tool to use to
install the upgraded objects on the customer’s system.

Editing Text Objects


C/SIDE allows you to import Text Objects as well as Navision Objects.
This is the best if you have manually applied an automatic change log to
some objects. It is also used when importing the Merge Tool results.

Compare Tool
The Compare Tool is the best tool to use when you are generating, and
printing an Automatic Change Log. It can also export an automatic change
log, which is in a good format to use for manual updates. Remember that if
you use an automatic change log, it is best to make the change in a text
object file and import it as text. Also, the Compare tool helps you find the
relative size of each modification by reporting what percent of each object
changed.

Merge Tool
The Merge Tool is the best tool for automatically merging changes. The
Merge Tool is used only to merge the objects that have conflicts, in other
words, only load the objects (from both the Customized and the New Base
versions) into the Merge Tool that you will need to compare and merge.
The Merge Tool is also very useful to track and store the various versions
that will exist for your various customers. For it to do this properly, you will
need to disregard my advice and load all changed objects from both the
Customized and New Base versions.

Using the Change Log


Many times when we put out an improvement, we also publish a manually
generated change log. This is the best tool for documenting modifications
that will be implemented many times. However, if you are only
9-22 Developing Navision Solutions

implementing the modification once or twice, it is too much work to create.


Remember that if you use a manual change log, it is best to make the
changes directly in C/SIDE.
The Upgrade Process 9-23

9.8 WHAT IF?


In this section, you will explore the answers to many “What if” questions.
These questions usually pop up during the upgrade process, especially for
difficult upgrades.

A Major Release
Now, we will cover some “What Ifs”, situations that may happen in your
particular upgrade which require some additional information. First, what if
this is a major release, not just an improvement. Well, the good news is
that, except for the data upgrade, a Major Release is just like an
Improvement. Except that it is much, much bigger. Since there are more
objects, there will be more chance of conflicts. Since the changes are more
extensive, there will be more chances that you will have to re-implement
your customization, rather than using a change log to re-implement the
upgrade.

One suggestion - in your planning, include some time to test the upgraded
system, sometime after recompiling all objects but before you set the
version tags, reset the modified flags, and export. Make sure that there are
no obvious problems. Use the Customer License to do the test, in case
there is a problem with it. Once you do the export, you could also test the
object upgrade on a copy of the complete customer database (including
data).

One other possibility is that there could be a major functionality change.


This usually means a change in the way something is done. Like the
Posting Group changes for version 2.00, for those of you who remember.
In this case, the change is so major that you cannot merge the changes,
either with the merge tool or even manually. This is because there is a
whole new way of doing something, not just a change in the details. If this
is the case, you must re-implement your customization from scratch in any
object in which there is a conflict. In some cases, your customization may
need to be changed even in unchanged objects, should it depend on the
old way of doing things.

Field Name Changes


What if you made some major field name changes? The problem here is
that these changes will ripple down into every other object that refers to the
changed field, at least in any automatic change log you generate. This can
create huge change logs where in fact there is no actual change to many
objects. For example, suppose you changed Department to Division. Now
consider the effect on the Customer Table. When you change this field
9-24 Developing Navision Solutions

name there, you will note that it has now changed automatically on the card
form, on the list form, on the Customer List report, and so on, all without
you having to do anything. However, if you now generate an automatic
change log, it will show that the card form changed, the list form changed,
the reports changed, and so on. This “noise” will make it harder for you to
do upgrades.

The Solution is to make the field name changes first. Before you extract the
New Base objects from the new database, make your customized field
name changes to the tables only. Any object that you update, you must
make sure the Modified Flag is checked, even if you just change the table
name. Now, when you extract the new objects, you must get all objects
whose Version Tag OR Modified Flag has been changed. Use the Marking
Facility to do this. First, filter all objects whose Modified Flag is on, select all
of them, and press Ctrl-F1. Then, remove that filter, filter on all objects with
the new Version Tag, select all of them, and press Ctrl-F1. Now, remove
that filter, use View, Marked Only, and then select all filtered objects.

Customer has Design Tools


What if my customer has the Design Tools? The problem here is that your
customer will probably not follow the Navision development rules. These
include rules about setting the Version Tags and Modification Flags,
Internal object documentation, developing on line and not using a Project
Log, so you cannot tell why they made a change.

The Solution has two parts; first, better planning is necessary.

You could try to teach your customer the rules, explain how it will make
upgrading easier. Or, you could just get them to follow one rule - Leave the
Modification Flags alone. This rule is easy to follow, since it does not
require them to do anything.

When you are going to do an object upgrade, you must schedule a time to
pick up the customer database from the customer site before you can
upgrade. This is because your copy will not be up to date. Just use a
Navision Backup. Make sure when you pick up the backup from your client,
you inform them that they should not make any changes until you install the
upgrade. Furthermore, reiterate that any changes that they make will not be
included in the upgrade. You might also want to identify a customer contact
who helped with the development and who can answer any questions
about the changes made.

The second part of the Solution is that certain steps in the Upgrade
Process will be changed. In step 1, when you make the Working Copy of
the customer database, be sure to create a new database and restore the
The Upgrade Process 9-25

Application Objects ONLY from the Navision Backup you got at the
customer site. After this, go through and give your customer’s changed
objects (the ones with the Modification flag on) their own additional Version
Tag, something different from your version tag. Use new letters, based on
the customer name. For example, if Acme Tools is your customer, make
their Version tag AT01 (assuming this is your first upgrade), and then add a
“,AT01” at the end of every modified object with an existing version tag. If
the customer created a new object, set the Version tag to “AT01” alone.

When you reach step 6, (updating each object on the Conflicts list), just
remember that you must take into account customer changes. At the least,
you will need to generate a new change log from the Working Copy.

When you have finished compiling all objects, before you export, you might
want to schedule some time for your customer contact to come in and
check out the upgraded system. It will be easier to see if a customer
change was lost, this way.

Finally, when you do export the objects, be sure to get all objects with
either the New Base Version Tag, or the Customer’s new Version Tag that
you created. You can use the marking technique to do this.

The rest of the steps are the same.

We are not following Navision's Methodology


What if you, the Solution Center, is not following the rules? For example,
what if you went to the customer site and made some changes on the fly?

The answer - see the previous section on what if the customer has the
design tools.

In addition, you should start following the rules from now on. Upgrades are
difficult enough when you follow the rules, without making them even more
difficult.

More than one upgrade to do


Here is a common situation. The customer has not upgraded for a while,
and you have skipped a Minor Release, 2 Service Packs, and a Major
Release. Now, with a Minor Release, the customer sees a feature he just
has to have, and he realizes that now he wants to be upgraded. The
problem, there is no direct upgrade path except from one version to the
very next version. You will have to do multiple upgrades at once.

Object Upgrades can be combined at your site. You simply do the upgrade
steps from 2 through 10 for each upgrade version in between. When you
9-26 Developing Navision Solutions

have upgraded to the final version, you must remember to export all objects
with any Version Tag higher than what the customer had, so they will get all
changed objects.

However, Data Upgrades are usually too complex to combine. Therefore,


you should schedule a separate upgrade for each Data Upgrade you have
to do. This usually corresponds to a Major Release. Thus, when you
combine Object Upgrades, you must still export the modified objects for
each Data Upgrade. Note that technically, you could do all the Data
Upgrades at once, even if they are not combined. However, it is
recommended for safety reasons that you do one Data Upgrade and then
let the system run for at least a couple weeks to shake out any problems
before you do the next upgrade (whether or not it includes a Data
Upgrade).
The Upgrade Process 9-27

9.9 UPGRADING THE DATA


Now, we are finally ready to upgrade the data. You will usually need to do
this every time there is a Major Release, and sometimes during a Minor
Release.

We include Data Conversion routines (usually codeunits) in a Upgrade Kit


which we generally release about one month after we release the product.
You should take advantage of this time lag to familiarize yourself with the
new product and any oddities about it. When you get the Data Conversion
routines, you should try not to modify them. It would be best if any data
conversion routines you needed could be written separately, and then run
in series after ours. However, occasionally, you will need to modify our
Data Conversion routines because otherwise they would not work with your
customizations. This is OK, but just be careful.

You must write (or modify) Data Conversion routines at the same time as
you are creating the Upgraded Objects on your site. However, these
routines must be run on the customer site, in order to upgrade their live
data.

Preparation
There are some additional steps you must do to prepare for a data
upgrade. First, when you make your Working Copy, you should make an
additional copy to use to create the Phase One Conversion objects. Phase
One objects must be created using the old, un-upgraded database.

When you have finished Upgrading the Objects, you should make another
copy of the database, this one to be used to create the Phase Two
Conversion Objects. Phase Two objects must be created on a new,
upgraded database.

One other step which you should do, is create a complete copy of the
customer’s database, so that you can test the upgrade process. This
means you must get the customer database from the customer site so that
you can have complete, current data.

New Field Objects


The first thing you will need are the “New Field” objects. We provide these
so that you can create new fields that you wouldn’t normally have a license
to create. These objects include base tables where the only change is that
they contain new fields necessary during the data conversion. These
objects may also include scratch tables used as a bridge during the
conversion process. These new fields will not have any trigger code.
9-28 Developing Navision Solutions

These objects are always brought into the system using the “Merge New
Into Existing” option in the Object Import Worksheet.

You may need to create some of your own. If so, you may add any field you
wish, but just add the Name, Type and Length. Do not put any trigger code
in. If you find any, remove it.

Preparing for Phase One


Preparing for Phase One of the Conversion. Remember that Phase One of
the Conversion is done on the Old Customer Database, the one without
any upgrades, but with all customizations. Phase one will also be done with
the New Fields objects already loaded.

Therefore, the first step to prepare for this is to bring in the Standard,
Navision-Provided New Fields objects, then bring in the Navision-Provided
Phase One objects. You may modify the standard Conversion Objects, but
you should only do this if they will not work as is with your customizations.
You should create new Conversion Objects (processing-only reports or
codeunits) for your data conversion. What these should do is create or
move data into its final home if possible. If this is not possible, then the
routine should add the data into a scratch table, and remove it from the
original table.

When you have written all of your objects, export our objects and yours into
two .fob files; one for the New Fields objects, and one for the Phase One
Conversion files.

Why use a scratch table?


You may wonder why you need to use a scratch table. The reason is
usually because a field definition has changed. A Field Type cannot be
changed unless no records have data in that field. Therefore, this part of
the conversion process will move that field’s data, along with its primary
key, into a scratch table in the Custom Objects range. Then, the original
field can be cleared, so that when the field definition is changed during the
object upgrade, there will be no field data in any record, and the change will
go though properly.

Another reason for a scratch table might be if there is an extensive change


to the data structure. The old information might be put into a temporary
scratch table and the old table cleared, and then the new data can be
placed properly into the heavily modified table (or a brand new table) later
after it is brought in with the upgrade objects. An example of this situation
would be if in the new data structure, one record will hold information
The Upgrade Process 9-29

which, in the old data structure was held on two different records in two
different tables.

Preparing for Phase Two


Preparing for Phase Two of the Conversion. Remember that Phase Two of
the Conversion is done on the New Customer Database, the one with all of
the upgrades, plus all customizations. A Phase Two is usually only needed
if Phase One put data into scratch tables. Bring in the Standard Navision-
Provided Phase Two objects. Again, you can modify these, but only if they
will not work with your customizations.

You should create your own Data Conversion objects. These should move
data from the scratch tables to their final home. Then they should erase the
data from the scratch tables. If you know how to do it (examine our
objects), you can optionally remove the obsolete fields and tables and
scratch tables.

When you have finished, export all of the Phase Two objects, both Navision
Provided and created by you, into a single Phase 2 fob file.

The Conversion Process


Once you have completed all the preparatory steps, including the object
upgrade, you should bring all of the above mentioned fob files, including
the New Fields, Phase One Conversion, Upgrade Objects and Phase Two
Conversion object files to the customer site. This time, you should also
bring your Attain Developer license on a diskette, because you will need it
to run the data conversion routines.

Prepare the customer’s system as for an object upgrade, by bringing down


the server, making a backup copy of the database, and starting the client
located on the Server Machine. Using the Tools, License Information,
Change option, open your license from the diskette. Now, open the
database in local mode.

From this local client, perform Phase One of the Database Upgrade. First,
bring in the New Fields objects using the Merge New Into Existing option of
the Object Import Worksheet. Then bring in the Phase One Conversion
Objects. Run the Phase One Conversion in every single company in the
database. For each company, run the standard routines, then run your
customized ones.

Once you have converted all companies, then Import the Upgrade Objects
using the Replace All option of the Object Import Worksheet. Now compile
9-30 Developing Navision Solutions

all objects as usual. Note that the Phase One Conversion Objects may not
compile, but that is OK as they will not be used again.

Now, perform Phase Two of the Data Conversion (if any). First, import the
Phase Two Conversion Objects using the Replace All option. Then, run the
Phase Two Conversion routines in every single company in the database.
In each company, run the standard conversion routines, then your
customized ones. Finally, remove any obsolete objects (if any are
mentioned in the upgrade kit) and remove the data conversion objects.

Bring down the local client, then bring the Server back up.

Please note that all of these steps will take a significant amount of time,
especially if your customer has substantial data. You will be able to gauge
the amount of time when you do your test run at your site. Be sure that the
customer can be down long enough for the entire database and object
upgrade. This work will probably be done only on weekends or holidays.
LAB 1
COMPLEX DATA TYPES

The exercises in this chapter will reinforce the


knowledge that you have gained in this part of the
course.

If you have read and understood all of the


material up to this point, you should be able to
complete all of the exercises in this chapter.

This chapter contains the following sections:

1.1 Running Objects


1.2 Using Dialogs
1.3 Getting Started With Record Variables
1.4 Using Streams
1-2 Applied Programming in CSIDE

1.1 Running Objects

In this section, you will run various objects in several different ways.

1.1.1 Running Forms

In this exercise, you will create codeunit and add functions to run different
types of forms.

1 From the Tool menu, choose Object Designer.

2 In the Object Designer, click the Codeunit button.

3 Click New.

4 Save the codeunit with the ID 98100 and the Name RunObject.

5 Add a function to the codeunit called RunFormModal.

6 Put code in the function to open the customer list form modally. Do not
create a variable.

7 Save the codeunit and test your function by putting code in the OnRun
trigger.

8 Add a global variable for form 22 (Customer List) and change your
function to use this variable.

9 Save the codeunit and test your function by putting code in the OnRun
trigger.

10 Create another function called RunFormNonModal.

11 Add code to the function to run the customer card (form 21) non-
modally. Do not create a variable.

12 Save the codeunit and test your function by putting code in the OnRun
trigger.

13 Add a global variable for form 21 (Customer Card) and change your
function to use this variable.

14 Save the codeunit and test your function by putting code in the OnRun
Complex Data Types 1-3

trigger.

15 Try running the following code in the OnRun trigger:

RunFormModal;
RunFormNonModal;

16 Now try reversing the lines like this:

RunFormNonModal;
RunFormModal;

What did you notice? What was the difference in the two?

1.1.2 Running Reports

In this exercise, you will design a codeunit and add functions to run
different types of reports.

1 Design codeunit 98100 (RunObject).

2 Add a function to the codeunit called RunReportModal.

3 Put code in the function to run the customer listing report modally. Do
not create a variable.

4 Save the codeunit and test your function by putting code in the OnRun
trigger.

5 Add a global variable for report 101 (Customer - List) and change your
function to use this variable.

6 Create another function called RunReportNonModal.

7 Add code to the function to run the Customer - Labels report (number
110) non-modally. Do not create a variable.
1-4 Applied Programming in CSIDE

8 Save the codeunit and test your function by putting code in the OnRun
trigger.

9 Add a global variable for report 110 (Customer - Labels) and change
your function to use this variable.

10 Save the codeunit and test your function by putting code in the OnRun
trigger.

11 Try running the following code in the OnRun trigger:

RunReportModal;
RunReportNonModal;

12 Now try reversing the lines like this:

RunReportNonModal;
RunReportModal;

What did you notice? What was the difference in the two?

1.1.3 Running a Codeunit

In this exercise, you will design a form and add code to run your codeunit
(98100).

1 Design form 330 (Main Menu).

2 Add a command button at the bottom of the window next to the Help
button.

3 In the OnPush trigger of the command button, add code to run


codeunit 98100 (RunObject). Do not create a variable.

4 Save and run the form. Test your button.

5 Change the code to use a codeunit variable. Don’t forget to set the
subtype.
Complex Data Types 1-5

6 Save and run the form. Test your button.

7 Change the code so that it calls the RunFormModal function, instead of


the OnRun trigger.

8 Save and run the form. Test your button.


1-6 Applied Programming in CSIDE

1.2 Using Dialogs

In this section, you will use the dialog variable to display information to the
user.

1.2.1 Opening Dialogs

In this exercise, you create a codeunit and add functions to it that


manipulate a dialog variable.

1 In the Object Designer, click the Codeunit button.

2 Click New.

3 Save the codeunit with the ID 98101 and the Name


ComplexDataTypes.

4 Add a global variable called Window that is of type dialog.

5 Add a function to the codeunit called OpenDialog.

6 Add the following code to that function:

Window.OPEN(‘Processing Customers\’ +
‘Customer No. #1#########\’ +
‘Progress @2@@@@@@@@@@@@@@@’)

7 Save the codeunit and test your function by putting code in the OnRun
trigger.

8 Now, add the following line to the bottom of the OnRun trigger:

Window.INPUT;
9 Save the codeunit and test your function by putting code in the OnRun
trigger.

What changed after you added that line to the OnRun trigger?

Why did you not have to use a Window.CLOSE statement?


Complex Data Types 1-7

1.2.2 Updating Dialogs

In this exercise, you add a function to your codeunit that updates a dialog
variable.

1 Design codeunit 98101.

2 Add a global record variable for the Customer table.

3 Add a function to the codeunit called UpdateDialog.

4 Add the following code to that function:

Customer.FIND('-');
REPEAT
Window.UPDATE(1, Customer."No.");
SLEEP(1000);
UNTIL Customer.NEXT = 0;
5 Save the codeunit and test your function by putting code in the OnRun
trigger. You will have to call the OpenDialog function first and then your
new function.

What happens if you comment out the SLEEP statement in the function?

Updating the Pogress Indicator

6 Add a global integer variable called LineNo.

7 Add a global integer variable called TotalLines.

8 Change the code in the function to look like the following:

LineNo := 0;
TotalLines := Customer.COUNT;
Customer.FIND('-');
REPEAT
LineNo := LineNo + 1;
Window.UPDATE(1, Customer."No.");
Window.UPDATE(2, ROUND(LineNo/TotalLines*10000,1));
SLEEP(1000);
UNTIL Customer.NEXT = 0;

9 Save the codeunit and test your function by putting code in the OnRun
trigger. You will have to call the OpenDialog function first and then your
new function.
1-8 Applied Programming in CSIDE

1.3 Getting Started With Record Variables

In this section you will create simple functions that perform simple tasks
with record variables.

1.3.1 Movement

In this exercise, you add a function to your codeunit that moves through a
record set.

1 Design codeunit 98101.

2 Add a function called SimpleMovement.

3 Add code to the function to GET customer ‘50000’.

4 Add a message box to the function to display the customer number


and name after your GET this:

MESSAGE('No.=%1, Name=%2', Customer."No.",


Customer.Name);
5 Save the codeunit and test your function by putting code in the OnRun
trigger.

6 Add code to the function to move to the NEXT customer after the GET
and before the message.

7 Save the codeunit and test your function by putting code in the OnRun
trigger.

8 Change the NEXT statement to go to the previous record.

9 Save the codeunit and test your function by putting code in the OnRun
trigger.

Looping through the Records

10 Create a new function called CustomerLoop.

11 Copy the code from your DialogUpdate function into this function.

12 Save the codeunit and test your function by putting code in the OnRun
trigger. You will have to call the OpenDialog function first and then your
Complex Data Types 1-9

new function.

13 Now change the code so that it starts at the last record and moves
backwards through the table.

1.3.2 Modification

In this exercise, you will change customer records.

1 Design codeunit 98101.

2 Add a function called SimpleModification.

3 Add code to the function to GET customer ‘50000’.

4 Add code to change the City field to ‘NEW City’. Don’t forget to
MODIFY the record.

5 Save the codeunit and test your function by putting code in the OnRun
trigger.

6 Change the function so that it inserts a customer with the following


values:

No. = 'New 001'


Name = 'New customer'
Address = 'Somewhere'
City = 'Someplace'
Phone = '555-5555'
7 Save the codeunit and test your function by putting code in the OnRun
trigger.

8 Check to see if your new customer exists.

9 Comment out the insertion code and add code to DELETE that new
customer. Remember you must set the No. field before calling delete.

10 Save the codeunit and test your function by putting code in the OnRun
trigger.

11 Check to see if your new customer exists.

12 Put back the code you wrote to insert the new customer. Remove the
deletion code.

13 Save the codeunit and test your function by putting code in the OnRun
1-10 Applied Programming in CSIDE

trigger.

14 Make sure the customer exists.

15 Comment out the insertion code and add code to RENAME the new
customer to ‘NEW 111’. Remember to GET the record first.

16 Save the codeunit and test your function by putting code in the OnRun
trigger.

17 Check to see if the customer has been renamed.

1.3.3 Displaying Record Sets

In this exercise, you will display the record set of a record variable by
passing that variable to a form.

1 Design codeunit 98101.

2 Add a function called DisplayRecordSet.

3 Add code to the function to Run the Customer List form and pass in the
Customer record variable. The code should look like the following:

FORM.RUNMODAL(FORM::"Customer List", Customer);

4 Save the codeunit and test your function by putting code in the OnRun
trigger.

Which Customer is the current record when the form is displayed?

5 In the OnRun trigger before you call DisplayRecordSet, add code to


GET customer 50000.

6 Save and run the codeunit.

Which Customer is the current record when the form is displayed?


Complex Data Types 1-11

7 In the OnRun trigger before you call DisplayRecordSet and after the
GET, add code to go to the first customer using find.

8 Save and run the codeunit.

Which Customer is the current record when the form is displayed?

9 In the OnRun trigger before you call DisplayRecordSet, change the


FIND to go to the last cusotmer.

10 Save and run the codeunit.

Which Customer is the current record when the form is displayed?

11 In the OnRun trigger before you call DisplayRecordSet, add code to


change the current key of the record set to “Search Name”. Put this
code before the FIND statement.

12 Save and run the codeunit.

Which Customer is the current record when the form is displayed?

What order are the records in on the form?

13 In the OnRun trigger before you call DisplayRecordSet, add code to set
a filter on the Search Name field (use a setfilter statement). The filter
should be ‘J..L’. Put this code before the FIND statement but after the
SETCURRENTKEY.

14 Save and run the codeunit.

Which Customer is the current record when the form is displayed?

What types of customers are being displayed?


1-12 Applied Programming in CSIDE

15 In the OnRun trigger before you call DisplayRecordSet, change the


setfilter to a setrange. The filter should be ‘J..L’. Put this code before
the FIND statement but after the SETCURRENTKEY.

16 Save and run the codeunit.

Which Customer is the current record when the form is displayed?

Are the same types of customers being displayed with the setrange?

17 In the OnRun trigger before you call DisplayRecordSet, change the


setrange back to a setfilter. Change the filter so that it is ‘J*|L*’. Put this
code before the FIND statement but after the SETCURRENTKEY.

18 Save and run the codeunit.

Which Customer is the current record when the form is displayed?

What types of customers are being displayed now?

Can you convert this setfilter into a setrange statement?

You’re code in the OnRun trigger should now look like the following:

OnRun
Customer.SETCURRENTKEY("Search Name");
Customer.GET('50000');
Customer.SETFILTER("Search Name", 'J*|L*');
Customer.FIND('+');
DisplayRecordSet;

1.3.4 Copying Record Sets

In this exercise, you will copy record variables in several ways and display
the record set to see if it has changed.
Complex Data Types 1-13

1 Design codeunit 98101.

2 Add a function called FilterRecordSet.

3 Add a new global record variable with a name of Cust2 and a subtype
of Customer.

4 Add the following code to the function FilterRecordSet.

Cust2.SETCURRENTKEY("Currency Code");
Cust2.SETFILTER("Currency Code", '=USD');
Cust2.SETRANGE("No.", '50000', 'ZZZ');
Cust2.FIND('-');

5 Change the OnRun trigger to call FilterRecordSet and then


DisplayRecordSet. Remove any other code.

6 Save and run the codeunit.

Did the record set contain the records that you expected?

Why or why not?

7 Design codeunit 98101.

8 Add a function called CopyRecordSet.

9 Add the following code to the function.

CLEAR(Customer);
Customer.COPY(Cust2);

10 Change the OnRun trigger to call FilterRecordSet, then


CopyRecordSet and then DisplayRecordSet. Remove any other code.

11 Save and run the codeunit.

Did the record set contain the records that you expected?

Why or why not?


1-14 Applied Programming in CSIDE

12 Design codeunit 98101.

13 Add a function called AssignRecordSet.

14 Add the following code to the function.

CLEAR(Customer);
Customer := Cust2;

15 Change the OnRun trigger to call FilterRecordSet, then


AssignRecordSet, and then DisplayRecordSet. Remove any other
code.

16 Save and run the codeunit.

Did the record set contain the records that you expected?

Why or why not?

You may also want to experiment with the record variable functions
CopyFilter and CopyFilters.

1.3.5 Using FlowFields

In this exercise, you will display the values of a FlowField from a record
variable in several different ways.

1 Design codeunit 98101.

2 Add a function called DisplayNetChange.

3 Add the following code to the function.


Complex Data Types 1-15

Window.OPEN(‘Customer Net Change = #1###########’);


Customer.FIND('-');
REPEAT
Window.UPDATE(1, Customer."Net Change");
Window.INPUT;
UNTIL Customer.NEXT = 0;

4 Change the OnRun trigger to call DisplayNetChange. Remove any


other code.

5 Save and run the codeunit. You must press the Enter key on the dialog
to move to the next record.

Did the Net Change field have any values?

Why or why not?

6 Design codeunit 98101.

7 Add the following code to the DisplayNetChange function right before


the Window.UPDATE statement.

Customer.CALCFIELDS("Net Change");

8 Save and run the codeunit. You must press the Enter key on the dialog
to move to the next record.

Did the Net Change field have any values?

Why or why not?

1.3.6 Creating a Temporary Table

In this exercise, you will add records to a temporary table and display the
records in a form.

1 Design codeunit 98101.

2 Add a global variable named CustTemp with a subtype of Customer.


1-16 Applied Programming in CSIDE

3 Set the Temporary property of the CustTemp variable to Yes.

4 Add a function called LoadCustTemp.

5 Add the following code to the function.

Customer.FIND('-');
REPEAT
CustTemp := Customer;
CustTemp.INSERT;
UNTIL Customer.NEXT(3) = 0;

6 Change the DisplayRecordSet function to display CustTemp instead of


Customer.

7 Change the OnRun trigger to call LoadCustTemp and then


DisplayRecordSet. Remove any other code.

8 Save and run the codeunit.

Did the record set contain the records you expected?

Why or why not?


Complex Data Types 1-17

1.4 Using Streams

In this section, you will create and use streams with File data types.

1.4.1 Writing to a File with OutStream

In this exercise, you will create a codeunit and add functions to create a
file, create the outstream object, and write to the file.

1 From the Tools menu, choose Object Designer.

2 In the Object Designer, click the Codeunit button.

3 Click New.

4 Save the codeunit with the ID 98104 and the name Streams Test.

5 Add a function to the codeunit called CreateFile.

6 Add two global variables outFile of type File and oStream of type
OutStream.

7 Put code in the function to create a new file. In this example we will use
a hard coded name of a file ('c:\temp\streamtest.txt'), but you may
change this value as needed. The location of the test file is not
particularly important.

a. The code should look something like this:

outFile.WRITEMODE := TRUE;
outFile.TEXTMODE := TRUE;
outFile.QUERYREPLACE := TRUE;
// You may want to change the filename below
outFile.CREATE('c:\temp\streamtest.txt');

8 Save the codeunit and test your function by putting code in the OnRun
trigger to run it.

Was the file created?

9 Create another function called WriteToFile.

10 Add code to the function to create the OutStream object and then use
1-18 Applied Programming in CSIDE

that object to write Hello World into the file.

a. The code should look something like this:

outFile.CREATEOUTSTREAM(oStream);
oStream.WRITETEXT('Hello World');
oStream.WRITETEXT;
oStream.WRITETEXT;
oStream.WRITETEXT('The above two writetext ‘ +
‘lines started new lines in this file.');

11 Save the codeunit and test your function by putting code in the OnRun
trigger that first calls the CreateFile function and then calls the
WriteToFile function. Make sure you answer Yes to the question about
replacing the file since it already exists.

Did the file contain the text you expected?

1.4.2 Reading from a File with InStream

In this exercise, you will modify the codeunit from the previous exercise and
add functions to open a file, create the instream object, and read from the
file.

1 From the Tools menu, choose Object Designer.

2 In the Object Designer, click the Codeunit button.

3 Open codeunit 98104 (Streams Test).

4 Add a function to the codeunit called OpenFile.

5 Add three global variables inFile of type File, iStream of type InStream,
and FirstLine of type Text and length 50.

6 Put code in the function to open an existing file. In this example we will
use a hard coded name of a file ('c:\temp\streamtest.txt'), but you may
change this value as needed. The location of the test file is not
particularly important.
Complex Data Types 1-19

a. The code should look something like this:

inFile.WRITEMODE := FALSE;
inFile.TEXTMODE := TRUE;
// You may want to change the filename below
inFile.OPEN('c:\temp\streamtest.txt');

7 Save the codeunit and test your function by putting code in the OnRun
trigger to run it and remove any other code.

Was the file changed in any way?

8 Create another function called ReadFromFile.

9 Add code to the function to create the InStream object and then use
that object to read Hello World into the text variable. You may also
want to show a message that displays the value of the text variable.

a. The code should look something like this:

inFile.CREATEINSTREAM(iStream);
iStream.READTEXT(FirstLine);
MESSAGE('FirstLine = \' + FirstLine);

10 Save the codeunit and test your function by putting code in the OnRun
trigger that first calls the OpenFile function and then calls the
ReadFromFile function.

Did the message contain the text you expected?


1-20 Applied Programming in CSIDE
LAB 2
CODING IN TABLES, REPORTS AND
DATAPORTS

The exercises in this chapter will reinforce the


knowledge that you have gained in this part of the
course.

If you have read and understood all of the


material up to this point, you should be able to
complete all of the exercises in this chapter.

This chapter contains the following sections:

2.1 Coding in Table and Field Event Triggers


2.2 Coding in Report Event Triggers
2.3 Coding in Dataport Event Triggers
2-2 Applied Programming in CSIDE

2.1 CODING IN TABLE AND FIELD EVENT TRIGGERS

In this section, you will add code to various table and field triggers to
existing tables in the application to change the way those tables work.

2.1.1 Table Triggers

In this exercise, you will add code to the Resource table.

1 From the Tool menu, choose Object Designer.

2 In the Object Designer, click the Table button.

3 Design table 156 (Resource).

4 Put code in one of the table triggers that will default the Name field to
whatever the user entered for the No. field. Do this whenever a new
record is created in the table.

5 Save the table and test your code by running the Resource Card form.

Did your code execute when you expected it to?

Try running the table. Does your code execute at the same time here?

6 Design table 156 (Resource).

7 Put code in one of the table triggers that will default the Resource
Group field to the first resource group in the resource group table. This
should also be done when a new record is created.

8 Save the table and test your code by running the Resource Card form.

Did your code execute when you expected it to?


Coding in Tables, Reports and Dataports 2-3

2.1.2 Field Triggers

In this exercise, you will add code to the fields in the Resource table.

1 Design table 156 (Resource).

2 Put code in one of the field triggers that will ask the user if they are
sure about changing the department code. This code should be
executed as soon as the user changes and leaves the department
code field. (See the Resource Group No. – OnValidate trigger for an
example.)

3 Save the table.

4 Modify Form 76 Resource Card to include Global Dimension 1 and


Global Dimension 2. Save and exit the form.

5 Test your code by running the Resource Card form.

Did your code execute when you expected it to?

Does your code fire if you don’t change the field?

What if you change the field to the same value that it had previously?

1 Design table 156 (Resource).

2 Find the code in this table that sets the Search Name field to the
Name. Change this code to set the Search Name field to the value of
the Type field and the Name field. For example, PERSON: MARY.

3 Save the table and test your code by running the Resource Card form.

Did your code execute when you expected it to?


2-4 Applied Programming in CSIDE

Does your code fire when you insert a new record? Why or why not?

If your code does not fire when a record is inserted, change it so that it
does (use the VALIDATE function). How did you do this?
Coding in Tables, Reports and Dataports 2-5

2.2 CODING IN REPORT EVENT TRIGGERS

In this section, you will add code to reports to change the way that they run.

2.2.1 Showing or Hiding sections

In this exercise, you will change a report to allow the user to dynamically
cause a section to show or not show on the report.

1 From the Tool menu, choose Object Designer.

2 In the Object Designer, click the Report button.

3 Design report 1404 (Bank Acc. - Detail Trial Bal.).

4 Change the report to allow the user to check a checkbox on the


request form if they want to see the detail body section. If the box is not
checked, do not show the ledger table’s body section (see
currReport.SHOWOUTPUT).

5 Save the report and test your code by running it.

Did the section show on the report when you ran with the checkbox
checked?

Did the section show on the report when you ran with the checkbox
unchecked?

Does the report remember whether it was checked or not the next time you
run the report?

Can you change it so that it will?

How did you change it?


2-6 Applied Programming in CSIDE

2.2.2 Showing User Filters

In this exercise, you will add the ability for the user to filter on additional
fields, but you will also have to show the filters on the report.

6 Design report 1404 (Bank Acc. - Detail Trial Bal.).

7 Change the report to allow the user to filter the ledger data item on the
Work Type field.

8 Save the report and test your code by running it.

If you set a filter on the work type field, does the filter show at the top of the
report? What about the filter you set on another data item?

9 Design report 1404 (Bank Acc. - Detail Trial Bal.).

10 Add a global variable to the report called BankAccountLedgerFilters of


type Text250.

11 In the OnPreReport trigger, add code to set the new variable with the
filters that the user might have set on the ledger data item such as
amount greater or less than a certain value.

12 In the sections designer, add another header to the report to display


these filters. It should be on the top of the first page of the report.

13 Add code to the section so that it will only print if the variable is not
blank and if it is the first page of the report. (See the code behind the
other filter section for an example.)

14 Save the report and test your code by running it.

If you set a filter on the work type field, does the filter show at the top of the
report?

2.2.3 Using the Skeleton Report

In this exercise, you will start with the skeleton report to create a simple
report that has a grand total.
Coding in Tables, Reports and Dataports 2-7

1 Design report 50000. (If you have not imported the skeleton report from
the class disk, you need to do that first.)

2 Save the report as 98001 and with a Name of Customer Ledgers by


Dept.

3 Add the Cust. Ledger Entry dataitem to the report and indent it under
department.

4 Link the two data items.

5 Add a Header and Body section on the report for the Ledger dataitem.
(a body section should already exist, if so, use that one).

6 Add the Customer No., Description, and Amount fields to the Cust.
Ledger Entry body section. Move the labels to the header section.

7 Add totals for each department to the report.

8 Add a grand total for the entire report without creating a new variable
(See the currReport.CreateTotals function).

9 Save the report and test your code by running it.

Did the totals show up correctly? Did you remember to set the TotalFields
property?
2-8 Applied Programming in CSIDE

2.3 CODING IN DATAPORT EVENT TRIGGERS

In this section, you will create a couple of dataports that have code.

2.3.1 Exporting Data

In this exercise, you will create an exporting dataport that requires some
coding.

1 From the Tool menu, choose Object Designer.

2 In the Object Designer, click the Dataport button.

3 Click New.

4 Create a dataport that exports from the G/L Account table.

5 Use a fixed format.

6 It should export the account number and the balance for the account.

7 Only export accounts where the Type is Posting.

8 Finally, add code to round the Balance to the nearest 1000 right before
it is exported.

9 Save the dataport with an ID of 98001 and a Name of GL Export 1000.


Test your code by running it.

Did the dataport create the file the way you expected?

Did your code run properly?

2.3.2 Exporting Data with a Column Header

In this exercise, you will create an exporting dataport that requires some
coding.

1 Design dataport 98001.

2 Add code to your dataport so that before it exports any records, it


Coding in Tables, Reports and Dataports 2-9

writes out column headings for the data. (Use the currFile variable and
the WRITE member function)

3 Save the dataport and test your code by running it.

Did the dataport create the file the way you expected?
2-10 Applied Programming in CSIDE
LAB 3
OBJECT ANALYSIS

The exercises in this chapter will reinforce the


knowledge that you have gained in this part of the
course.

If you have read and understood all of the


material up to this point, you should be able to
complete all of the exercises in this chapter.

This chapter covers the following sections:

3.1 Exporting and Changing as Text


3.2 Renumbering Objects
3.3 Analysis with impuls Workbench
3-2 Object Analysis and Development Methodology

3.1 EXPORTING AND CHANGING AS TEXT

In this section, you will export an object as text, change it, and re-import.

3.1.1 Exporting the Object

In this exercise, you will export the customer table.

1 From the Tool menu, choose Object Designer.

2 In the Object Designer, click the Table button.

3 Select table 18 (Customer).

4 Click File, Export.

5 Make sure to select the Text option button.

6 Type in the filename C:\T00018.txt

7 Click OK to export the object to the file.

If you had selected more than one object, how many files would you expect
to get created?

3.1.2 Changing the File

In this exercise, you will add a field to your text version of the customer
table.

1 From your Start menu, click Run.

2 Enter C:\T00018.txt.

3 Click OK to open the file.

4 Find the last field listed in the FIELDS section.

5 Copy that field to create a new field. Use the number 9000, the name
‘New Field’, and the data type Text50.
Object Analysis 3-3

6 Save the file with your changes.

3.1.3 Importing an Object from a Text File

In this exercise, you will import your changed customer table back into
C/SIDE.

1 From Navision, click Tools, Object Designer.

2 Click File, Import.

3 Type C:\T00018.txt as the filename.

4 Click OK to import the text file.

Did you receive any errors?

If so, why?

If you received an error about your license, that’s good. It means you
followed directions. The number that you were given for the field you added
is not in your number range. Go back and change your file so that the
number of the field is in the range 50000 – 99999.

5 Re-import the file.

Did you get the chance to see the import worksheet?

What happens when you run the customer table?

Compile the table by pressing F11 while it is selected in the object


designer. Did it compile successfully?

Can you run the table now?


3-4 Object Analysis and Development Methodology

3.2 RENUMBERING OBJECTS

In this section, you will export several objects as text into one file and
renumber those objects to a new range.

3.2.1 Importing the project

In this exercise, you will import the objects that you need to re-number.

1 In the Object Designer, click File, Import.

2 Find the file on your class disk called ObjectAnalysis321.fob. Put the
full path and name in the filename text box.

3 Click OK to import the file. This should create several objects in the
range 98500-98510. There are tables, forms, reports, dataports, and
codeunits.

4 Click on the Version List column in the Object Designer.

5 Press F7 to filter on that column.

6 Enter the filter OA3.2.1

7 Click OK to filter the objects.

8 Click the All button. You should now see all the objects for this project.

You could try to re-number these objects to another range by hand, but that
would lead to severe problems. All other objects within the project use the
number to reference that object. Re-numbering one object would break all
of these links. You need a way to change the number of all objects and all
references to objects at once.

3.2.2 Exporting All the Objects

In this exercise, you will export all of the objects into one file.

1 With the filter applied to the Object Designer and the All button
selected, click in the upper left of the table box. This selects all objects
within the filter.

2 Click File, Export.


Object Analysis 3-5

3 Make sure to select the Text option button.

4 Type in the filename C:\Renumber.txt

5 Click OK to export all selected objects into one file.

3.2.3 Re-numbering the Objects

In this exercise, you will open the file and re-number the objects.

1 From your Start menu, click Run.

2 Enter C:\Renumber.txt

3 Click OK to open the file.

4 Click File, Save As to rename the file and keep a backup. Use the new
name C:\Renumbered.txt.

5 Using the Find function (F3) or the Replace function (Search, Replace)
of Notepad, find all the object numbers in the range 98500-98510 and
change them to be in the range 68500 – 68510. Be careful that you
only change object references and make sure you change them all.
You may want to search again after you have changed them all.

6 Save the file with your changes. Remember the new filename is
C:\Renumbered.txt.

3.2.4 Importing the Objects

In this exercise, you will import the renumbered objects back into Navision.

1 From the Object Designer, click File, Import.

2 Enter C:\Renumbered.txt in the Filename text box.

3 Click OK to import the objects from the file.

What happened?

What error did you receive?


3-6 Object Analysis and Development Methodology

The error suggests that the objects are duplicate. While the numbers are
unique the names are not. You must remove the old versions of the objects
or rename them before you can import the new objects from the file.

4 Delete the old objects already in the database (the ones we exported
to Renumber.txt). Remember that you have a backup of the text file in
case something goes wrong.

5 Try importing the file again.

What happened this time?

Everything should have imported without a problem. Test all the objects by
compiling them and running them.
Object Analysis 3-7

3.3 ANALYSIS WITH IMPULS WORKBENCH

In this section, you will use impuls Workbench to investigate the impact of
making changes to the system.

3.3.1 Opening impuls Workbench

In this exercise, you will open impuls and the impuls database.

1 Open impuls Workbench from the Start menu.

2 Click File, Database, Open.

3 Select the impuls workbench database that you created in class. (This
should be called Workbench.fdb.)

4 Click OK to open the database.

5 When prompted for company, select only the company.

6 Click OK to open the company.

7 If the Object Administrator does not appear, press F12.

3.3.2 Importing New Objects

In this exercise, you will import the objects that you renumbered in the last
section into impuls.

1 Click File, Import.

2 On the General tab, enter a code (FA100) and description (Fixed Asset
Project version 1.00) for the objects you are importing.

3 On the Import tab, enter the filename of the file (C:\Renumbered.txt)


and set the Update option to Add.

4 On the Keywords tab, you must find and select the Fin.STX file in the
Navision directory. (You should not have to change anything on the
System tab.)

5 Click Import to import the objects. This may take some time, because
as it imports it must build a lot of the relationships between the tables.
3-8 Object Analysis and Development Methodology

6 To check and make sure the objects were imported, expand Tables in
the tree and look near the very bottom for the Asset table (94001). This
was one that you imported.

3.3.3 Using Where Used

In this exercise, you will use the Where Used function to find where in the
system certain things are used.

1 Find the Name field in the Customer table and select it.

2 Right mouse click and click Where Used.

Which table is the last table that uses the customer name?

3 Find the Search Name key in the Customer table and select it.

4 Right mouse click and click Where Used.

Which other objects use this key?

3.3.4 Find Table Relations

In this exercise, you will use the Relations To and Relations From functions
to find out which tables are related to which other tables. Use one of those
functions to answer the following questions.

Using the Relations To function, find out if the Customer table is related to
the Tax Area table. Is it related?

Using the Relations From function, find out if the Item table is related to the
Item Unit of Measure table. Is it related?

The answer to both of the above questions should be Yes.


Object Analysis 3-9

3.3.5 Using the Source Finder

In this exercise, you will search for key words using the Source Finder
function. Answer the following questions by using Source Finder.

How many instances of the MODIFYALL function are there only in the
Table objects?

How many instances of the word Reserve are there in Field Names?
3-10 Object Analysis and Development Methodology
LAB 4
DEVELOPMENT METHODOLOGY

The exercises in this chapter will reinforce the


knowledge that you have gained in this part of the
course.

If you have read and understood all of the


material up to this point, you should be able to
complete all of the exercises in this chapter.

This chapter covers the following sections:

4.1 Internal Documentation


4.2 External Documentation
4.3 Setting the Version List
4.4 Completing Design Specifications
4-2 Object Analysis and Development Methodology

4.1 INTERNAL DOCUMENTATION

In this section, you will internally document objects that you will import from
your class disk. Before starting this lab copy a clean database from your
class disk as a starting point. Open this clean database before you start.

4.1.1 Importing the project

In this exercise, you will import the objects that you need to document.

1 In the Object Designer, click File, Import.

2 Find the file on your class disk called DevMeth411.fob. Put the full path
and name in the filename text box.

3 Click OK to import the file. This should modify several base objects
(T18, T21, T81, T65700, C12).

4 Click on the Modified column in the Object Designer.

5 Press F7 to filter on that column.

6 Enter the filter Yes

7 Click OK to filter the objects.

8 Click the All button. You should now see all the modified objects.

4.1.2 Internally Documenting

In this exercise, you will document all the imported objects.

1 With the filter applied to the Object Designer and the All button
selected, select table 18.

2 Click Design.

3 In the documentation trigger document what was done to this object.


Basically a field was added (it is field number 65701). Remember to
use an internal tag (your NSC initials, your initials, and a number).

4 Save and close the table.

Did you remember to tag the field description with your internal tag?
Development Methodology 4-3

5 Select table 21.

6 Click Design.

7 In the documentation trigger document what was done to this object.


Basically the same field was added.

8 Save and close the table.

Did you remember to tag the field description with your internal tag?

9 Select table 81.

10 Click Design.

11 In the documentation trigger document, what was done to this object?


Basically the same field was added. There was also some code added.
It is currently marked by asterisks (//*************).

12 Save and close the table.

Did you remember to tag the field description with your internal tag?

Did you remember to tag the code with your internal tag?

13 Select codeunit 12.

14 Click Design.

15 In the documentation trigger document, what was done to this object?


Basically there was some code added. It is currently marked by
asterisks (//*************).

16 Save and close the codeunit.

Did you remember to tag the code with your internal tag?
4-4 Object Analysis and Development Methodology

You do not have to document table 65700. The other developer has
already documented this new table.
Development Methodology 4-5

4.2 EXTERNAL DOCUMENTATION

In this section, you will create a project log for all of the objects you just
documented internally.

4.2.1 Create the File

In this exercise, you will create an empty text file.

1 Open Notepad from the Start menu.

2 Click File, New.

3 Click File, Save.

4 Enter the filename C:\CT100.txt.

5 Click OK to save the file.

4.2.2 Adding the Header

In this exercise, you will add a header section to your project log.

1 With Notepad still open, add the following information to the top of the
file:

a. Name of Project = Customer Type Add-on

b. Solution Center

c. Name of Project Leader = James Doe

d. Customer = Electric Company Inc.

e. Date Started

f. Date Completed

g. Version Tag = CT1.00

You can organize this information in any way that you like. You may want
to look at the example in the book. There is other information that would
also be good here, but you do not have that information.
4-6 Object Analysis and Development Methodology

4.2.3 Adding the Objects

In this exercise, you will add an entry for all objects in this project.

1 With Notepad still open, add the following information for every object
that is part of this project:

a. Object ID

b. Object Name

c. Object’s Documentation trigger

Remember to include all objects that will be exported as part of the project,
whether you changed those objects or not.
Development Methodology 4-7

4.3 SETTING THE VERSION LIST

In this section, you will set the version list for all objects that are a part of
the project and then export all of these objects.

4.3.1 Setting the Version List

In this exercise, you will set the Version List for all the objects.

1 With Navision open, filter down to just the objects for this project (using
the modified flag). Make sure the All button is selected.

2 For all modified objects in the list, add the version tag CT1.00 to the
end of the version list. If the version list already has something in it,
you must separate the new version tag with a comma (for example
US2.00,CT1.00).

4.3.2 Unchecking the Modified Flag

In this exercise, you will set the Modified flag for all the objects to No.

1 Remove any filters in the Object Designer.

2 Filter down to just the objects for this project (this time using the
version list – filter using *CT1.00*). Make sure the All button is
selected.

3 Uncheck all of the Modified Flags for the objects within the filter. You
will have to click twice to change the modified flags.

4.3.3 Exporting the objects

In this exercise, you will export the objects to take them to the customer
site.

1 Filter down to just the objects for this project (this time using the
version list – filter using *CT1.00*). Make sure the All button is
selected.

2 Click in the upper left corner of the table box to select all the objects in
the filter.

3 Click File, Export.

4 Enter the filename C:\CT100.FOB.


4-8 Object Analysis and Development Methodology

5 Make sure that the Attain Object format is selected.

6 Click OK to export the objects.

You may want to re-import the objects and use the import worksheet to
check that every object was exported.
Development Methodology 4-9

4.4 COMPLETING DESIGN SPECIFICATIONS

In this section, you will complete the design specs for the all day project.
This exercise must be done before you can begin the all day project.

4.4.1 Reviewing the Documents

Read through the concept document and the design specifications. This
should give you an idea of what needs to be done for this customer.

4.4.2 Finish the Specifications

For all parts of the project 7.1 – 7.7, you need to complete the
specifications.

. Some subsections simply say ‘Complete this section’. Those sections


require you to write the specifications for what needs to be done.

. For all parts, there is a subsection that lists the objects that will be
changed or created. You must analyze the modification and list all of the
objects here.

Complete these specifications before attempting the all day project.


4-10 Object Analysis and Development Methodology
LAB 5
POSTING ROUTINES

The exercises in this chapter will reinforce the


knowledge that you have gained in this part of the
course.

If you have read and understood all of the


material up to this point, you should be able to
complete all of the exercises in this chapter.

This chapter covers the following sections:

5.1 Getting Ready


5.2 Creating Check Line
5.3 Creating Post Line
5.4 Creating Post Batch
5.5 Creating a Starter Routine – Post
5.6 Changing a Document Posting Routine
5-2 Posting Routines and Data Conversion

5.1 GETTING READY

In this section, you will import a journal table and a ledger table (and
associated forms). You will use these as a starting point so that you can
create posting routines.

We will not be using dimensions for posting at this time. Our customers
and salespersons are however set up for dimensions. Go to table 352 and
delete the dimensions for customers and salespersons if you have not
already done so. Also, our GL Accounts are not set up for direct posting.
Create a processing only report that will check on direct posting for posting
accounts only.

"G/L Account"."Direct Posting" := TRUE;


"G/L Account".MODIFY;

5.1.1 Importing the objects

In this exercise, you will import the objects that you need for the rest of the
exercises in this lab.

1. In the Object Designer, click File, Import.

2. Find the file on your class disk called PostingRoutines511.fob. Put the
full path and name in the filename text box.

3. Click OK to import the file. This should import four new objects, two
tables and two forms (T91511, T91512, F91511, F91512).

4. Run F91511, the journal form, and enter some lines for the journal.
This is a journal for entering when items are rented by a customer. If
you have trouble selecting items, you need to enter some rentable
items in your item table (see 5.1.2 below).

5.1.2 Entering Rentable Items

In this exercise you will add items to the item table that have a Base Unit of
Measure of HOUR. These are rentable items.

1. Go to the Item Card.

2. Press F3 to insert a new record.

3. Enter a value in the No. field like CANOE 01.


Posting Routines 5-3

4. Enter a description like Canoe, extra wide.

5. In the Base Unit of Measure field, use the lookup button to go to the
Item Unit of Measure form. This form will be empty for this new item.

6. On the Item Unit of Measure form, enter HOUR in the first column and
leave the second column set to 1.

7. Click OK to set the Base Unit of Measure field.

8. On the Invoice tab, enter a value in the Unit Price field. That is all the
fields we need for rentable items.

9. Repeat steps 2-8 at least two more times changing the No. and
Description fields.

10. Close the Item Card.

11. Now go back to the journal and add several lines for each rentable item
you created.
5-4 Posting Routines and Data Conversion

5.2 CREATING CHECK LINE

In this section you will create and test a check line codeunit for the journal
that you imported.

5.2.1 Creating the Codeunit

In this exercise you will simply create the codeunit.

1. Open the Object Designer.

2. Click the Codeunit button.

3. Click New.

4. Click File, Save As.

5. Enter the ID 91521 for the codeunit.

6. Enter the name of the journal, a dash, and the words Check Line for
the Name. This is the standard name for a check line codeunit. In this
case, the name is ‘Rental Jnl.-Check Line’.

5.2.2 Setting up the RunCheck function

In this exercise you will create and set up the RunCheck trigger for the
codeunit. We will be leaving the OnRun trigger empty.

1. Create a function called RunCheck.

2. Add a parameter to the RunCheck function called RentalJnlLine with a


subtype of 91511 (the rental journal line table). Make sure this
parameter is passed by reference (check the Var column for the
parameter).

3. Inside the function add a WITH DO statement for the RentalJnlLine


record variable. Remember to include a BEGIN and END. All of the
code you add to this function should be within the WITH DO statement.

4. Save and close the Codeunit.

5. Attempt to run the codeunit from the Object Designer.

What happens? Why?


Posting Routines 5-5

5.2.3 Adding code to the RunCheck function

In this exercise, you will actually create the code that does all the work in
the check line codeunit.

1. Design the check line codeunit.

2. Add code to the RunCheck function that skips empty lines. An empty
line is defined as any line where the Customer No., Item No. and
Amount are not filled in. If all of these fields are empty, then exit the
function (it is not an error condition).

3. Add code to test the following fields (make sure they are not empty):

a. Posting Date

b. Customer No.

c. Item No.

d. Actual Hours Rented

e. Amount

4. Add code to make sure the Posting Date field is not a closing date. If
the date is a closing date, then an error should be produced that
notifies the user of the record that caused the problem and what the
problem was.

5. Add code to make sure the Amount field is positive. If the Amount is
not positive, then an error should be produced that notifies the user of
the record that caused the problem and what the problem was.

6. Save and close the codeunit.

5.2.4 Testing the Codeunit

In this exercise, you will add code to a button on the journal form that will
call the check line codeunit that you created. Then you will test your
codeunit.
5-6 Posting Routines and Data Conversion

1. Design form 91511 (Rental Journal).

2. Open the menu designer for the Posting menu button.

3. Open the C/AL editor for the Check Line menu item.

4. In the OnPush trigger, add code to run your check line codeunit.

a. Create a global variable for the check line codeunit.

b. Add code to CLEAR the codeunit first.

c. Call the RunCheck function of the codeunit

d. The only parameter is the journal line record variable.


Pass in the record variable being used on the form (Rec).

5. Save and close the form.

6. Run the form. Check the lines that you had previously entered.

7. Add new lines that are missing information and check those.

Did your check line routine find all the mistakes?

What happens if you check a line that has no customer, item or amount?
Posting Routines 5-7

5.3 CREATING POST LINE

In this section you will create and test a post line codeunit for the journal
that you imported.

5.3.1 Creating the Codeunit

In this exercise you will simply create the codeunit.

1. Open the Object Designer.

2. Click the Codeunit button.

3. Click New.

4. Click File, Save As.

5. Enter the ID 91522 for the codeunit.

6. Enter the name of the journal, a dash, and the words Post Line for the
Name. This is the standard name for a check line codeunit. In this
case, the name is ‘Rental Jnl.-Post Line’.

5.3.2 Setting up the RunWithCheck function

In this exercise you will set up the RunWithCheck function for the codeunit.

1. Create a function called RunWithCheck.

2. Add a parameter to the RunWithCheck function called RentalJnlLine2


with a subtype of 91511 (the rental journal line table). Make sure this
parameter is passed by reference (check the Var column for the
parameter).

3. The first line of code in the RunWithCheck function will be to copy the
local variable RentalJnlLine2 to a global variable called RentalJnlLine.

a. Create the global variable RentalJnlLine as a record


variable of subtype 91511 (the Rental Journal Line table).

b. Add a line of code to the RunWithCheck function to copy


RentalJnlLine2 into RentalJnlLine.

4. The second line of code will be to call the Code function.


5-8 Posting Routines and Data Conversion

a. Create a global function called Code (no parameters or


return value).

b. Change the Local property of the Code function to Yes.

c. Add a line of code to the RunWithCheck function to call


this new function.

5. The last line of code in the RunWithCheck function will copy just the
fields that might have changed back into RentalJnlLine2 from the
global variable RentalJnlLine.

a. Add an assignment statement that will copy the field


values from RentalJnlLine into RentalJnlLine2.

6. Save the Codeunit.

5.3.3 Adding code to the Code function

In this exercise, you will actually create the code that does all the work in
the post line codeunit.

1. Design the post line codeunit.

2. Add code to the Code function that skips empty lines. An empty line is
defined as any line where the Customer No., Item No., and Amount are
not filled in. If all of these fields are empty, then exit the function (it is
not an error condition).

3. Add code to call the check line routine that you built earlier.

a. Create a global variable for the check line codeunit. Don’t


forget to set the subtype.

b. Add code to call the RunCheck method of that codeunit


variable. You must pass in the RentalJnlLine global
variable as a parameter.

4. Add code to lock the ledger table and get the next line number that can
be used in the ledger table.

a. Create a global variable for the Rental Ledger Entry table.

b. Create a global variable to hold the NextEntryNo (integer).

c. Add code to the Code function that will only happen once
Posting Routines 5-9

during the posting.

d. It must lock the ledger table.

e. It must go to the last record in the ledger table and put


that entry number into the NextEntryNo variable.

f. It must increment the NextEntryNo variable by one.

5. Add code to make sure that the Item specified exists in the Item table.
Do the same for the Customer.

a. Create a global record variable for the Item table.

b. Create a global record variable for the Customer table.

c. Add code to the Code function that will attempt to GET the
Item specified from the Item table. If the GET fails an error
should be produced that tells the user that the Item does
not exist.

d. Add code to the Code function that will attempt to GET the
Customer specified from the Customer table. If the GET
fails, an error should be produced that tells the user that
the Customer does not exist.

6. Add code that creates the ledger record and inserts it.

a. First, INIT the ledger record variable.

b. Set the Entry No. field to the NextEntryNo variable.

c. Set the other fields in the ledger record with the


corresponding values from the journal record.

d. Call the INSERT method for the ledger record.

7. Add code to increment the NextEntryNo variable by one. Remember


that the code that set it originally will only be called the first time
through the codeunit. The next time through it will use the value
already in NextEntryNo.

8. Save and close the codeunit.


5-10 Posting Routines and Data Conversion

5.3.4 Testing the Codeunit

In this exercise, you will add code to a button on the journal form that will
call the post line codeunit that you created. Then you will test your
codeunit.

1. Design form 91511 (Rental Journal).

2. Open the menu designer for the Posting menu button.

3. Open the C/AL editor for the Post Line menu item.

4. In the OnPush trigger, add code to run your post line codeunit.

a. Create a global variable for the post line codeunit.

b. Add code to CLEAR the codeunit first.

c. Call the RunWithCheck function of the codeunit.

d. The only parameter is the journal line record variable.


Pass in the record variable being used on the form (Rec).

5. Save and close the form.

6. Run the form. Post some of the lines that you had previously entered.

7. Run the Rental Ledger Entries form to see if the records were indeed
copied to the Ledger.

Did your post line codeunit copy the records correctly?

What happens if you post a line that has no customer, item or amount?
Posting Routines 5-11

5.4 CREATING POST BATCH

In this section you will create and test a post batch codeunit for the journal
that you imported.

5.4.1 Creating the Codeunit

In this exercise you will simply create the codeunit.

1. Open the Object Designer.

2. Click the Codeunit button.

3. Click New.

4. Click File, Save As.

5. Enter the ID 91523 for the codeunit.

6. Enter the name of the journal, a dash, and the words Post Batch for the
Name. This is the standard name for a check line codeunit. In this
case, the name is ‘Rental Jnl.-Post Batch’.

5.4.2 Setting up the OnRun trigger

In this exercise you will set up the OnRun trigger for the codeunit.

1. From within the codeunit, open the properties window.

2. Change the TableNo property to 91511 (the journal table).

3. Press Enter and look at how the OnRun trigger has changed. The
OnRun trigger must have a record variable passed in to it. The record
variable is passed by reference, has a subtype of Rental Journal Line,
and will be referred to by the name Rec.

4. The first line of code in the OnRun trigger will be to copy the local
variable Rec to a global variable called RentalJnlLine.

a. Create the global variable RentalJnlLine as a record


variable of subtype Rental Journal Line.

b. Add a line of code to the OnRun trigger to copy Rec into


RentalJnlLine.
5-12 Posting Routines and Data Conversion

5. The second line of code will be to call the Code function.

a. Create a global function called Code (no parameters or


return value).

b. Change the Local property of the Code function to Yes.

c. Add a line of code to the OnRun trigger to call this new


function.

6. The last line of code in the OnRun trigger will copy just the fields that
might have changed back into Rec from the global variable
RentalJnlLine.

a. Add an assignment statement that will copy the field


values from RentalJnlLine into Rec.

7. Save the Codeunit.

5.4.3 Adding code to the Code function

In this exercise, you will actually create the code that does all the work in
the post batch codeunit.

1. Design the post batch codeunit.

2. Add code to the Code function that makes sure there is something to
post.

a. You can use the FIND(‘-‘) function to see if any records


exist in the Journal table. If FIND returns true, then there
are records. If FIND returns false, then there are no
records.

b. If FIND returns false, set the Line No. field in the journal to
zero and EXIT this codeunit. Remember that post batch
does not display messages to the user.

c. If FIND returns true, continue with the codeunit.

3. Add code to open a dialog and display progress to the user.

a. Create a global variable called Window with a type of


Dialog.

b. Add code to open the Window variable. Use the following


Posting Routines 5-13

open string:

'Processing Rental Journal\' +


'Checking Lines #1##########\' +
'Posting Lines #2########## @3@@@@@@@@@@\'
4. Add code to call the check line routine that you built earlier. This time
you must call the check line codeunit for every line in the journal.

a. Create a global variable for the check line codeunit. Don’t


forget to set the subtype.

b. Create a global variable called LineCount (integer). This


will count the number of lines that you are processing.

c. Add code to loop through all of the journal lines. Use a


REPEAT..UNTIL loop. The FIND(‘-‘) has already been
done. You simply need to add REPEAT UNTIL
RentalJnlLine.NEXT = 0;

d. Within the loop, add code to call the RunCheck method of


the check line codeunit variable. You must pass in the
RentalJnlLine global variable as a parameter.

e. Within the loop, add code to increment LineCount by 1.

f. Within the loop, add code to UPDATE the dialog variable.


Update control id 1 with the value of LineCount.

5. Add code to call the post line routine that you built earlier. You must
call the post line codeunit for every line in the journal.

a. Create a global variable for the post line codeunit. Don’t


forget to set the subtype.

b. Create a global variable called TotalCount (integer). This


will hold the total count of the number of lines that you are
processing.

c. Add code to set the TotalCount variable to the current


value of LineCount.

d. Add code to reset LineCount to zero.

e. Add code to go back to the first journal line. After your first
loop the RentalJnlLine record variable is on the last record
in the journal.
5-14 Posting Routines and Data Conversion

f. Add code to loop through all of the journal lines again.


Use a REPEAT..UNTIL loop. The FIND(‘-‘) has already
been done. You simply need to add REPEAT UNTIL
RentalJnlLine.NEXT = 0;

g. Within the loop, add code to call the RunWithCheck


method of the post line codeunit variable. You must pass
in the RentalJnlLine global variable as a parameter.

h. Within the loop, add code to increment LineCount by 1.

i. Within the loop, add code to UPDATE the dialog variable.


Update control id 2 with the value of LineCount.

j. Within the loop, add code to UPDATE the dialog variable.


Update control id 3 with the value of
ROUND(LineCount/TotalCount*10000,1). This is the ratio
of the line that it is processing to the number of total lines
to be processed. The @ symbols form a progress bar on
the dialog.

6. Add code to delete all the journal records from the journal table.

a. Use the DELETEALL method of the RentalJnlLine global


variable.

7. Save and close the codeunit.

5.4.4 Testing the Codeunit

In this exercise, you will add code to a button on the journal form that will
call the post batch codeunit that you created. Then you will test your
codeunit.

1. Design form 91511 (Rental Journal).

2. Open the menu designer for the Posting menu button.

3. Open the C/AL editor for the Post Batch menu item.

4. In OnPush trigger, add code to run your post line codeunit.

a. Use the CODEUNIT.RUN function

b. The first parameter is the ID of the codeunit.


Posting Routines 5-15

c. The second parameter is the record variable. Pass in the


record variable being used on the form (Rec).

5. Save and close the form.

6. Run the form. Click Posting, Post Batch to post the journal lines.

7. Run the Rental Ledger Entries form to see if the records were indeed
copied to the Ledger.

Did you receive any errors from Check Line or Post Line? What happened
to the journal lines as a result of the error?

If you received an error, correct the journal lines and post again. What
happens to a line that has no customer, item or amount?

Are you able to get a glimpse of the dialog? Try adding a SLEEP(500)
statement in the loops in Post Batch. What did the SLEEP statement seem
to do?
5-16 Posting Routines and Data Conversion

5.5 CREATING A STARTER ROUTINE – POST

In this section you will create and test a start routine called post for the
posting routine codeunits that you have created.

5.5.1 Creating the Codeunit

In this exercise you will simply create the codeunit.

1. Open the Object Designer.

2. Click the Codeunit button.

3. Click New.

4. Click File, Save As.

5. Enter the ID 91524 for the codeunit.

6. Enter the name of the journal, a dash, and the words Post Line for the
Name. This is the standard name for a check line codeunit. In this
case, the name is ‘Rental Jnl.-Post’.

5.5.2 Setting up the OnRun trigger

In this exercise you will set up the OnRun trigger for the codeunit.

1. From within the codeunit, open the properties window.

2. Change the TableNo property to 91511 (the journal table).

3. Press Enter and look at how the OnRun trigger has changed. The
OnRun trigger must have a record variable passed in to it. The record
variable is passed by reference, has a subtype of Rental Journal Line,
and will be referred to by the name Rec.

4. The first line of code in the OnRun trigger will be to copy the local
variable Rec to a global variable called RentalJnlLine.

a. Create the global variable RentalJnlLine as a record


variable of subtype Rental Journal Line.

b. Add a line of code to the OnRun trigger to copy Rec into


RentalJnlLine.
Posting Routines 5-17

5. The second line of code will be to call the Code function.

a. Create a global function called Code (no parameters or


return value).

b. Change the Local property of the Code function to Yes.

c. Add a line of code to the OnRun trigger to call this new


function.

6. The last line of code in the OnRun trigger will copy just the fields that
might have changed back into Rec from the global variable
RentalJnlLine.

a. Add an assignment statement that will copy the field


values from RentalJnlLine into Rec.

7. Save the Codeunit.

5.5.3 Adding code to the Code function

In this exercise, you will actually create the code that does all the work in
the post codeunit.

1. Design the post codeunit.

2. Add code to the Code function that CONFIRMs the user’s decision to
post.

a. Add code that calls the CONFIRM function within an IF


statement. The question should be ‘Do you want to post
the journal lines?’. The default button should be No.

b. If CONFIRM returns false, you must EXIT the Code


function.

c. If CONFIRM returns true, you can continue with the


codeunit.

3. Add code, call the post batch codeunit that you built earlier.

a. Create a global variable for the post batch codeunit. Don’t


forget to set the subtype.

b. Add code to call the run method of that codeunit variable.


You must pass in the RentalJnlLine global variable as a
5-18 Posting Routines and Data Conversion

parameter.

4. Add code to check the Line No. field.

a. If the Line No. field is equal to zero, then display a


message to the user that says ‘There was nothing to
post.’.

b. Otherwise continue with the codeunit.

5. Save and close the codeunit.

5.5.4 Testing the Codeunit

In this exercise, you will add code to a button on the journal form that will
call the post line codeunit that you created. Then you will test your
codeunit.

1. Design form 91511 (Rental Journal).

2. Open the menu designer for the Posting menu button.

3. Open the C/AL editor for the Post menu item.

4. In OnPush trigger, add code to run your post codeunit.

a. Use the CODEUNIT.RUN function

b. The first parameter is the ID of the codeunit.

c. The second parameter is the record variable. Pass in the


record variable being used on the form (Rec).

5. Save and close the form.

6. Run the form. Enter some valid journal lines and press F11 to Post
them.

7. Run the Rental Ledger Entries form to see if the records were indeed
copied to the Ledger.
Posting Routines 5-19

5.6 CHANGING A DOCUMENT POSTING ROUTINE

In this section, you will modify the sales document posting routine.

5.6.1 Modifying the Sales Post codeunit

In this exercise, you will add code to the Sales Post codeunit so that it
rejects any sales lines that have rentable items on them. Rentable items
are those that have a Base Unit of Measure of HOUR. This is a simple
change.

1. Design codeunit 80 (Sales-Post).

2. Find the REPEAT loop that loops through the Sales Line table. This is
a large loop that takes up a lot of the OnRun Trigger.

3. Just inside that loop, add code that checks the Unit of Measure Code
field.

a. If the Unit of Measure Code field is equal to HOUR, then


give the user an error that describes the line that caused
the problem and what the problem is.

b. Otherwise, continue with the codeunit.

c. Document all of your changes in the standard way.

4. Save and close the codeunit.

5. Test your changes by creating a Sales Invoice that has some rentable
items on the lines. You may have to set the Inventory Posting Group
field and the Prod. Posting Group field on the items first.

Did your document post?

If not, try again.


5-20 Posting Routines and Data Conversion
LAB 6
DATA CONVERSION

The exercises in this chapter will reinforce the


knowledge that you have gained in this part of the
course.

If you have read and understood all of the


material up to this point, you should be able to
complete all of the exercises in this chapter.

This chapter covers the following sections:

6.1 Looking at the Import File


6.2 Debugging the Journal
6.3 Building the Transaction Dataport
6.4 Testing the Transaction Dataport
6.5 Changing the Dataport to Post Directly
6.6 Another Exercise
6-2 Posting Routines and Data Conversion

6.1 LOOKING AT THE IMPORT FILE

In this section, you will open the import file and write down the fields and
where they go in the journal. You will also decide what the format is and set
other dataport properties.

6.1.1 Inspecting the File

In this exercise, you will look at the file that you are going to import.

1. Find the file on your class disk called SalesJournal631.txt

2. Double click on the file to open it.

What is the file type (Variable or Fixed)?

If variable, what is the separator and are there any delimiters?

If fixed, fill in the starting position and widths in the table below.

Fill in this table with the necessary information (see information below the
table for the mapping of fields):

Mapping For Fixed

Starting
Field Journal Field Position Width

Customer Number

Name

Invoice Number

Invoice Date

Due Date

Invoice Amount

Description

Sales Account

To fill in the Journal Field column in the above table, use the following
information.
Data Conversion 6-3

. The journal that you will be importing into, is the Sales Journal (the table
is actually General Jnl. Line).

. You are creating Customer invoices in that journal. So, the Account No.
field will hold the customer number.

. The customer name is unimportant.

. The invoice number should be used as the document number.

. The invoice date should be used as the posting date.

. There is a due date field in the table, although it is not shown on the form.

. Be sure the description is brought in correctly from the file.

. The sales accounts in the file do not correspond to the ones in Navision,
so ignore them.

. However, you will have to have a balancing account on all of these


transactions. Use the Accounts Receivable account associated with the
Customer Posting Group for that customer.
6-4 Posting Routines and Data Conversion

6.2 DEBUGGING THE JOURNAL

In this section, you will debug the Sales Journal to find out how your
dataport should validate and fill in fields. Below is a table with some test
data for you to type into the journal.

Field Value in File

Customer Number 10000

Name Cannon

Invoice Number 12345

Invoice Date 03/22/01

Due Date 04/15/01

Invoice Amount 1015.74

Description For Services Rendered

Sales Account FX11345

6.2.1 Getting Ready to Debug

In this exercise, you will get the debugger turned on and the journal setup
for importing.

1. From the Sales & Receivables menu, open the Sales Journal.

2. Click the lookup button on the Batch Name field. This opens the
General Journal Batches form.

3. Press F3 to insert a new batch.

4. Enter IMPORT in the Name field.

5. Enter a description, if you want.

6. Clear the No. Series field. You will not need number series for your
batch.

7. Click OK to select the IMPORT batch. This returns you to an empty


journal.
Data Conversion 6-5

8. Remove the values from the Posting Date, Document Type, Document
No. and any other fields that have a value. The dataport that you will
create will not have these fields automatically filled in and neither
should this experiment.

9. With the journal ready for importing, click Tools, Debugger, Active. This
activates the debugger. Make sure that both Active and Breakpoint on
Triggers are checked on the Debugger submenu.

Now you are ready to import your data (by hand). This is done in the next
exercise.

6.2.2 Entering the Data and Debugging

In this exercise, you will type in information into the journal and observe
what code is executed and what other fields might be filled in automatically.
You will be filling in the following table to help you create your dataport -

Change Other Fields that are


triggers validated
OnValidat
Field e Side effects

Document Type

Document No.

Account Type

Account No.

Description

Amount

Balancing
Account
6-6 Posting Routines and Data Conversion

1. Using the mouse click on the first field that you must change
(Document Type). Fill the fields from left to right.

2. Type an ‘I’ into the field and press Enter to select Invoice. This will
trigger the debugger to open on any code that is executed. In this case,
it stops on the Document Type – OnValidate trigger. So we know that
this field needs to be validated in the dataport. Write Yes in the
‘Change triggers OnValidate’ column of the table above.

3. Press F5 to see what trigger happens next. The next trigger is the
Payment Terms Code – OnValidate. So, validating the Document Type
field also validates Payment Terms Code field. Write the name of the
Payment Terms Code field in the ‘Other Fields that are validated’
column of the table above.

4. Continue pressing F5 and writing down the names of the fields that are
validated in the table. When the debugger window disappears, you are
done debugging that field.

5. Look to see if anything else changed in the journal line (where any
other fields filled in by the code?). If so, note this in the Side Effects
column of the table.

6. Enter the next value for the next field in the table and follow steps 3-5
for that field. Continue until you have debugged all the fields in the
journal record. The information in the table will be used in the next
exercise to build the dataport.
Data Conversion 6-7

6.3 BUILDING THE TRANSACTION DATAPORT

In this section, you will finally build the dataport that imports into the journal.

6.3.1 Creating the Dataport

In this exercise, you will create the dataport and add all the necessary
pieces.

1. From the Object Designer, click Dataport.

2. Click New to open the Dataport Designer.

3. Click File, Save as.

4. Enter the ID 91631 and the Name Sales Journal Import.

5. Open the Properties window for the dataport.

6. Set any properties here such as File Type that you determined in
section 6.1.

7. Add a data item to the dataport for the journal table that you will insert
into.

8. Add the dataport fields that you determined in 6.1 for this data item.
Remember that the order must match the file. If there are any fields
that you wish to skip or cannot read directly into the field, use a global
variable.

9. Close the Dataport Field Designer.

10. In the OnPreDataItem trigger of the data item, add code to lock and
filter the journal table and find the next line number.

11. In the OnAfterGetRecord trigger, add code to validate the fields that
need to be validated. You do not need to validate fields that are
validated by the OnValidate trigger of other fields that you will validate.
In other words, you do not have to validate the Amount field, because it
will be validated when you validate the Account No. field. Remember
that extra validations slow the dataport down.

12. You may also need to add other code to convert things in the file to the
correct data type, or set fields with the values that you stored in
variables. Take into account all of the information that is in the table in
6-8 Posting Routines and Data Conversion

section 6.2.

13. Add code to this trigger to set the primary key fields as well (all three).

14. The last thing you need to do is remove the tab for the data item from
the request form (by setting properties) and set the AutoSave to Yes,
AutoUpdate to No, and AutoReplace to No.

15. Save, and compile your dataport. You will test it in the next section.
Data Conversion 6-9

6.4 TESTING THE TRANSACTION DATAPORT

In this section, you will test your dataport and make any necessary
changes.

6.4.1 Running the Dataport

In this exercise, you will run the dataport created in section 6.3.

1. From the Object Designer, click Dataport.

2. Run dataport 91631.

3. Enter the full path and filename for the import file (this should be on
your class disk with the name SalesJournal631.txt).

4. Click OK to import the file.

Were there any errors?

If so, what are they and how might you fix them?

Once fixed, run the dataport again to see if there are other errors.

6.4.2 Checking the results

In this exercise, you will look at the records that the dataport imported.

1. From the Main Menu, click Sales & Receivables.

2. Click Sales Journal. Make sure the selected batch is IMPORT.

3. Count the records and see if the number matches the number of lines
in the file.
6-10 Posting Routines and Data Conversion

If you do not have any lines in the journal, run table 81 and see if they have
the wrong Template Name or Batch Name. If so, delete them from the table
and correct your dataport to set the Template Name and Batch Name
correctly.

If you have most of the records but not all, try and find out why you are
missing some. You may not be setting the primary key fields correctly.

If you have all the records, try to post. What error did you get?

You may notice that certain lines have negative amounts. These negative
transactions cannot be considered Invoices. They must be entered as
Credit Memos. Erase the journal lines from the journal, design your
dataport and change it to set the Document Type to Credit Memo if the
Amount is negative.

Now, retest your dataport.


Data Conversion 6-11

6.5 CHANGING THE DATAPORT TO POST DIRECTLY

In this section, you will copy your dataport and change it so that it posts
directly without creating journal lines.

6.5.1 Copying the First Dataport

In this exercise, you will save your dataport with a new name and ID.

1. Design dataport 91631.

2. Click File, Save as.

3. Enter the Name Sales Journal Import – Post and the ID 91651.

4. Click OK to save the dataport with the new name and ID.

6.5.2 Removing the Unnecessary Code

In this exercise, you will remove the code that was used to save information
in the journal.

1. With the Dataport Designer still open, select the data item.

2. Click View, C/AL Code.

3. Erase the code in the OnPreDataitem trigger that uses the journal.

4. Erase the code in the OnAfterGetRecord trigger that sets the primary
key values or sets the NextLineNo variable.

5. Close the C/AL Editor.

6. Open the Properties window.

7. Change the AutoSave property to No.

8. Click File, Save.

6.5.3 Adding the Call to the Posting Routine

In this exercise you add code to call codeunit 12 for the imported journal
line.

1. Add a global variable for codeunit 12 called GenJnlPostLine.


6-12 Posting Routines and Data Conversion

2. At the bottom of the OnAfterGetRecord trigger, add code to run


codeunit 12 using the variable you created. Pass the data item as the
record variable parameter.

3. Save and compile the dataport.

That’s it. Run the dataport to test it. Run the general ledger and customer
ledger tables to see the records that you posted.
Data Conversion 6-13

6.6 ANOTHER EXERCISE

In this section, you will work through importing beginning inventory. Follow
the same outline as in 6.1 through 6.5. Using the Item Journal and the
import file ItemJournal661.txt. The scenario is defined below.

6.6.1 Importing Beginning Inventory

In this exercise, you will write a dataport that will import Item Journal entries
for beginning inventory.

The file you will be importing is ItemJournal661.txt from your class disk.
The fields in the file are Item number, beginning quantity, unit of measure
(does not match the Navision Units of measure), Total value of the
inventory. Remember that this is just a test file. The actual file will be much
larger.

Write your dataport to import the file as it is. Do not change the file first to
make the import easier. First, import your values into the Item Journal.

Once they are importing and posting correctly, change your dataport so
that it automatically posts the journal lines as they are brought in.

In the Item Journal, the posting date should be the work date, the entry
type is positive adjustment, and the document number should be BEGINV.

You should also fill in the Item number, quantity, unit of measure and
Amount fields.
6-14 Posting Routines and Data Conversion
LAB 7
CODING IN FORMS

The exercises in this chapter will reinforce the


knowledge that you have gained in this part of the
course.

If you have read and understood all of the


material up to this point, you should be able to
complete all of the exercises in this chapter.

This chapter covers the following sections:

7.1 Creating a Menu


7.2 Integration with the Main Menu
7.3 Creating a Statistics Form
7.4 Using a Matrix Box
7.5 Using the Hyperlink triggers
7-2 Developing Navision Solutions

7.1 CREATING A MENU

In this section, you will create a menu for a functional area. The menu you
create will not include all of the features of a standard functional area’s
menu, but it will include most of them.

On your class disk, a file exists that contains objects for a special functional
area called the Field of Dreams Funding project. The file is named
FieldOfDreams711.fob. Import this file before you begin.

As a developer, it is possible to run the various forms and reports directly


from the object designer window. A user, on the other hand, would need to
access these objects through a menu.

7.1.1 Creating the Form

There are several objects in the Field Of Dreams Funding project. As a


developer, it is possible to run the various forms and reports directly from
the object designer window. A user, on the other hand, would need to
access these objects through a menu.

In this exercise, you will create such a menu. It’s easier to start by copying
one of the existing submenu forms and saving it with a new object number
and name.

1. From the Object Designer, design form 332 (General Ledger Menu).

2. Click File, Save as.

3. Enter the ID 91711 and the Name Field of Dreams Menu.

4. Click OK to save the new form.

5. Change the caption of the large label at the top to Field Of Dreams.

7.1.2 Adding the Buttons

In this exercise, you will change the buttons on the menu form to run the
objects from the new functional area.

1. Remove all of the buttons (they are the captions with dots beside them)
except for Chart of Accounts. Leave Setup and Periodic Activities as
well (they are actually menu buttons).

2. Change the Chart of Accounts button to have a caption of Sponsors


Coding in Forms 7-3

and to run the Sponsor Card form.

3. Copy the Sponsors button, change the caption to Report, and set the
RunObject property to Report Sp. Pledges & Contributions. Place this
button in the upper right corner of the form.

4. Remove all the menu items from setup menu button. This is where you
would add supplemental tables. In this functional area, there is only
one supplemental table – League Team.

5. Add a menu item to the setup menu button that has a caption of
League Teams and runs the League Teams form.

6. Remove all the menu items from the periodic activities menu. This
menu is used for activities (reports or dataports) that only need to be
run once in a while.

7. Add a menu item to the periodic activities menu that has a caption of
Import Sponsors and Teams and runs the Team Sponsor Import
dataport.

8. Save and close the form. Test the form by running it and clicking the
buttons.

Your menu is done.


7-4 Developing Navision Solutions

7.2 INTEGRATION WITH THE MAIN MENU

In this section, you will integrate the menu you created in section 7.1 with
the Main Menu form.

A little exploration of the main menu will reveal that it is actually made up of
many simple controls. It contains a number of command buttons with
picture boxes on the left side and a series of overlapping subform controls
on the right. There are also a few image controls that display background
images or the logo. Each command button has a corresponding subform
control (these are under the logo image) for each functional area
(Inventory, Sales and Receivables, and so on). When the user clicks one of
the command buttons, code behind the button calls a local function on the
form called ChangeSubMenu. This function changes the visible properties
of both the current subform (visible property set to false) and the desired
subform (visible property set to true).

Each of the overlapping subform controls has a SubFormID property that


corresponds to a menu form for that functional area. On top of all the
subform controls is an image control that displays the Navision logo. It is
this control that is visible by default when a user first enters the application.

7.2.1 Adding the Option Button

In this exercise, you will add a Command Button and Picture Box for the
new functional area. The easiest way to do this is to copy existing ones.

1. From the Object Designer, design form 330.

2. Click on the General Ledger button to select it.

3. Click Edit, Copy.

4. Click Edit, Paste. A new button with the caption General Ledger will
appear.

5. Change the caption of the new button to Field of Dreams.

6. With the button selected, press F9 to view the code behind the button.

7. Change the value passed into the ChangeSubMenu function to 91711.


(Any number will do as long as it is unique and you use it consistently.)

8. Now, click on the Picture Box control just to the left of the General
Ledger button to select it.
Coding in Forms 7-5

9. Click Edit, Copy.

10. Click Edit, Paste. A new picture box will appear.

11. Move it so that it is just to the left of the Field Of Dreams button.

12. Change the source expression property of the picture box to “91711 =
CurrentMenuType” without the quotes.

13. Save and close the form. Run the form and click your new button.

What happens?

7.2.2 Adding the Subform Control

In this exercise, you will add a subform control to the Main Menu that will
display your new menu form. The easiest way to do this is to copy an
existing SubForm control and update the properties for the Field of Dreams
Menu.

1. From the Object Designer, design form 330.

2. Click on the Logo image control.

3. Click Format, Send to Back. This will put the logo image control
beneath the subforms.

4. Click on the subform controls to select the top one. There are actually
several subform controls stacked on top of each other.

5. Click Edit, Copy.

6. Click off the form itself (click in the gray area outside the form) to
unselect the controls. This assures that when you paste, it will go into
the top left corner of the form.

7. Click Edit, Paste. A new subform control will appear.

8. Change the name property of the new subform control to


FieldOfDreamsMenu. When you copy a control that has a name, the
name is copied as well. This causes a lot of confusion if the control is
referenced in code.

9. Change the SubFormID property of the new subform control to 91711


(your new menu form).
7-6 Developing Navision Solutions

10. Save and close the form. Run the form and click your new button.

What happens?

Why does your subform not display when the form is first run?

7.2.3 Changing the SetSubMenu Function

In this exercise, you will complete the changes to the main menu by
modifying the code. Remember to document your code changes and make
a note of all your changes in the documentation trigger.

1. From the Object Designer, design form 330.

2. Click on the command button that you added earlier.

3. Press F9 to view the code in the button’s triggers. There is code in the
OnPush trigger to call the ChangeSubMenu function.

4. Scroll the editor down so that you can see the ChangeSubMenu
function. It calls the SetSubMenu function twice. The first call makes
the current subform control (or image control) invisible. The second call
makes the user’s selected subform control visible. Your menu does not
show because the code never references it.

5. Add code to the case statement in SetSubMenu for your subform


control.

6. Save and close the form.

7. If the main menu is already running close it and press F12 to open it
again.

8. Test your modifications, again.

Did they work this time? If not, check your code.


Coding in Forms 7-7

7.3 CREATING A STATISTICS FORM

In this section you will create a statistics form for the Sponsor table in the
Field Of Dreams functional area. It will be run from the Sponsor Card and
Sponsor List in the standard way.

7.3.1 Creating the Form

In this exercise, you will create the statistics form.

1. From the Object Designer, create a new form.

2. Use the form wizard to create a card type of form.

3. The source table is Sponsor.

4. Add to the form only the FlowFields from the table.

5. Click Finish to close the wizard and open the form designer.

6. Click File, Save as.

7. Enter the name Sponsor Statistics and the ID 91731.

8. Click OK to save the form.

9. Above the FlowField textboxes put a label with a caption of Current.

10. Copy the label and the two textboxes to a new column (to the right of
the originals).

11. Create two decimal variables – PriorMthContributions and


PriorMthPledges.

12. Change the source expression of the two new textboxes to use these
variables.

13. Make the two text boxes non-editable.

14. Write code in the OnAfterGetRecord trigger of the form to get the prior
month’s contributions and pledges. Use the date filter to accomplish
this quickly.

15. Save and close the form. Test your changes.


7-8 Developing Navision Solutions

7.3.2 Linking the Form

In this exercise, you will call your statistics form from the card and list
forms.

1. Design the Sponsor Card form.

2. Add a menu item to the Sponsor button with a caption of Statistics, a


short cut of F9, and a push action of RunObject. There is usually a
separator before this type of menu item. Add that as well.

3. Change the RunObject property of the menu item to be your statistics


form.

4. Change the RunFormLink property to link the forms on No. and


DateFilter.

5. Change the RunFormLinkType to OnUpdate.

6. Save and close the form. Test the form by running it and clicking the
button.

What happens if the card form is on the third sponsor?

What happens if you change the sponsor card’s record while the statistics
form is still open?

7. Repeat all of these steps for the Sponsor List form.


Coding in Forms 7-9

7.4 USING A MATRIX BOX

A matrix box should be used in cases where there is a many to many


relationship between two tables. In Navision Attain, it is used in the system
to show totals by time period such as the Budget forms.

From the General Ledger Menu, open the Budgets Window. The
relationship displayed there is between the Chart of Accounts and the Date
table. There are many G/L Entries per date and many dates that entries
were made for each account. A matrix box is the perfect container for such
a relationship.

General Description

A matrix box is a composite control (comprised of more than one control)


that can show information from several tables at the same time. The first
two tables are the vertical and the horizontal table of the matrix box control.
In the matrix part of the control, each cell can be used to display
information that is calculated on basis of fields in these two tables, or
information that is retrieved from other tables (with values from the first two
tables being used to select records). Each cell in the matrix is the
intersection of a record from the vertical and the horizontal table.

The part to the left of the vertical divider bar displays records from the
vertical table, the table that is the source table of the form, in a way similar
to an ordinary table box control. To the right of the divider bar is the matrix
itself. Above the matrix, the records from the horizontal table are displayed
(in the style that is normally used for the labels in a table box). The title
area of this area is the matrix heading. The horizontal table is called the
matrix source table.

In this example, the vertical table is the G/L Account, the horizontal table is
the Date table and the matrix displays budget figures for each combination
of account and period.

Navigating in a Matrix Box

Navigating in a matrix box is a bit more complex than usual, since there are
multiple tables and dimensions involved. The following describes how to
move around, both by using the mouse and by using the keyboard.

Using the Mouse

It is possible to scroll the vertical table vertically by clicking the navigation


7-10 Developing Navision Solutions

buttons at the right of the matrix, and to scroll the horizontal table
horizontally by clicking the navigation buttons at the bottom of the matrix. If
all columns of the vertical table do not fit in the "table box", a normal scroll
bar will be added below and you can scroll the columns horizontally, as in
an ordinary table box.

Resizing of any column is accomplished by placing the cursor in the label


area and then dragging when the cursor changes to the resize cursor
(which it does when it is on top of a column divider). Dragging the vertical
divider bar changes the relative proportions of the table box and matrix box
parts.

The height of the headings, both in the table box and the matrix part of the
control may be adjusted by dragging, and the columns in the table box part
can be resized vertically.

Using the Keyboard

In order to use the keyboard for navigation, first place the focus in the part
of the control to be affected.

Using the keyboard, it is possible to move from one part of the control to
another by pressing CTRL+<arrow>. That is, to move from the table box to
the matrix, press CTRL+RIGHT; to move from the matrix to the matrix
heading, press CTRL+UP; to move from the matrix heading back to the
matrix, press CTRL+DOWN; to move from the matrix to the table box,
press CTRL+LEFT. It is not possible to go directly from the matrix heading
to the table box (you have to go via the matrix).

When focus is placed in the matrix heading, PAGEUP and PAGEDOWN


scrolls the horizontal table (that is, a horizontal scroll).

CTRL+PAGEUP or CTRL+PAGEDOWN in the matrix part (including the


matrix heading) will also scroll the horizontal table.

7.4.1 Creating a Matrix Box

It can be confusing to create a matrix box for the first time.

Follow these step-by-step instructions to guide you through the process.


You will be creating a matrix box where both the vertical and the horizontal
table are based on the Integer table. The matrix cells display the product of
the two integers in the records that intersect at that cell. Remember those
multiplication tables from grade school?
Coding in Forms 7-11

1. Create a new blank form, and select the Integer table as the source
table. The source table for the form will be used as the vertical table of
the matrix box, so selecting the correct table is important.

2. Using the floating toolbar, add a matrix box to the form.

3. Open the Properties window.

4. Set the Name property of the matrix box control to MultTable, and set
the HorzGlue and VertGlue properties to Both. Set the
MatrixSourceTable property to the Integer Table.
7-12 Developing Navision Solutions

5. Insert a text box with a source expression of the Number field.


Coding in Forms 7-13

6. Add a textbox without a source expression to the empty part of the


matrix box control. Check the InMatrix property of this textbox - it
should be Yes.

7. Add a textbox without a source expression to the last empty part (the
matrix heading) of the matrix box control. Check the InMatrixHeading
property of this textbox - it should be Yes.

8. Add a source expression to the textbox in the matrix heading (the last
one added.) The source expression should point to a field from the
MatrixSourceTable. If the matrix box was named MultTable, and the
horizontal table is the Integer table, it could be:

CurrForm.MultTable.MatrixRec.Number

9. Add a source expression to the textbox in the matrix. Here, it could be:

CurrForm.MultTable.MatrixRec.Number * Number
7-14 Developing Navision Solutions

10. Save and close the form. Test the form by running it.

What is 12 * 12?
Coding in Forms 7-15

7.5 USING THE HYPERLINK TRIGGERS

In this section you will see how to use the hyperlink triggers
(OnCreateHyperlink and OnHyperlink) to add the UserID to a Hyperlink and
to display a message of who the user was that created the hyperlink when
the hyperlink is launched. We shall assume that whenever a Hyperlink is
created that the design specifications calls for us to create a newinstance
and to add the UserID to the shortcut.

7.5.1 Adding the UserID to the URL

In this exercise, you will add the UserID to the URL.

1. From the Object Designer, modify form 42 (Sales Order).

2. Add the following code to the OnCreateHyperlink trigger:

IF USERID <> '' THEN


URL := URL + '&forcenewinstance=yes' + ',UserID=' +
USERID;

3. Create the following variables under the OnHyperlink trigger: UIPos,


which is of type Integer and SenderID, which is Text with a length of
30.

4. Add the following code to the trigger:

UIPos :=STRPOS(URL, ',UserID=');


IF UIPos > 0 THEN
UIPos := UIPos + 8;
SenderID := COPYSTR(URL, UIPos, MAXSTRLEN(SenderID));
MESSAGE ('The link was created by ' + SenderID);

5. Save and Close the form.

6. Run form 42 (Sales Order).

7. Click on File, Send, Shortcut to Desktop.

8. Close the form.

9. Go to your desktop and double click on the shortcut. A new instance of


Navision should start up and after logging in you should be brought to
the Sales Order that you want to show. Finally, the UserID of the
person who created the e-mail should be displayed.
7-16 Developing Navision Solutions
LAB 8
PROPERTY MANAGEMENT

The exercises in this chapter will reinforce the


knowledge that you have gained in this part of the
course.

If you have read and understood all of the


material up to this point, you should be able to
complete all of the exercises in this chapter.

This chapter covers the following sections:

8.1 Introduction
8.2 Concept Document
8.3 Design Specifications
7-2 Developing Navision Solutions

8.1 INTRODUCTION

All Day Project - Property Management Add-On

This is the big lab exercise for the Attain Development Course. We have
created a project that will put into practice the lessons you have learned to
date. This project will take all day to complete. In order to make sure that
everyone gets the full benefit of this exercise, you will work individually on
this project. If you have any questions on any subject during this project,
please raise your hand and wait for the instructor to come to you before
asking.

On your class disk, you will find all of the files referred to in this exercise.
There, you can also find the answer objects. This is not a test. In order to
gain full benefit from this exercise, you should not look at the answer
objects until we tell you to.

Do NOT cheat yourself of the benefits of this exercise.

We will first go over the scenario in class and then give you full instructions
as to what you are to do. Then, we will turn you loose to work on the
project. Take breaks whenever you want after we have completed all
instructions.

Scenario

This is the scenario that you will be using for this exercise. Read it
carefully, as it contains useful information that you will need in order to
complete the project successfully.

Background

You work for a Navision Solution Center (NSC) as a Certified Attain


Developer. The NSC also has a Navision Certified Representative who has
been working with the Ted Spinner, the real estate magnate. He has
convinced him to purchase Navision Attain to use to manage his many
commercial and residential properties. You have been assigned by your
boss to perform the modifications necessary for this application.

Ted Spinner Enterprises is a real estate holding company solely owned by


Ted Spinner. It owns a shopping mall, 6 "strip malls", a large downtown
office building, and numerous smaller office buildings throughout the metro
area. It also owns 2 high-rise apartments and many other apartment
houses both in the city proper and also in the suburbs. Many of these
Coding in Forms 7-3

properties have high turnover rates, while in others the tenants stay for
many years. Ted Spinner's empire has been created by acquiring
properties individually, but mostly by purchasing other real estate holding
companies, many of which had their own computer systems before, and
most of which are still using them.

Computerization Project

The accounting department at Ted Spinner Enterprises has been working


with a hodgepodge of other property management systems from the
various acquisitions, plus a homegrown accounting system that they use on
many of the individual properties. Now, they would like to have their own
accounting AND property management system that they can use for all of
their properties. They have chosen Navision Attain because of its flexibility
to do exactly what they want, and because of its expandability, since they
do not want to convert everything right away.

At the beginning, they have decided to computerize their property


management functions. They are mainly interested in managing their
revenues and tracking their tenants. They want their system in place as
soon as possible. If all goes well, they plan on adding other functionality,
like Accounts Payable and Payroll.

Preliminary Analysis

The Navision Solution Center's Navision Certified Implementation


Specialist has already met with the client and discussed their needs. He
has determined that Ted Spinner Enterprises will purchase the following
Accounting Granules now - Basic General Ledger, Basic Receivables,
Basic Fixed Assets and the Maintenance and Insurance granules that go
with Fixed Assets. They will purchase Basic Payables and the Payroll
granules later.

The Implementation Specialist has also determined that they will need to
get some customizations in order to do their tracking the way they desire.
They have determined that the Fixed Assets granules will track their
expenses on a per property basis just fine. However, they wish to track
their revenues on a per tenant space basis. They have agreed to get these
modifications on a time and materials basis. Once these are done
acceptably, they will want to allocate expenses on a per tenant space basis
as well.
7-4 Developing Navision Solutions

The Assignment

The Implementation Specialist has also met with your Senior Developer
and with the client and worked out the Concept Document and most of the
Design Specifications. You are assigned to complete these Design
Specifications and the preliminary modifications within 8 hours. Once these
are completed, of course there will be others before the completed package
is delivered to the customer.

The Navision Representative believes that if this is done well, he can sell
this type of system to other property management companies in the area.
Therefore, the Senior Developer has instructed you to write this in such a
way that it can be used as an add-on and sold to other customers. For this
purpose, he has assigned you Object Numbers from 99300 through 99309
and a Version Tag of NSC013. He wants you to complete all standard
programming documentation (including a Project Log, internal
documentation, and a Change Log) for this project. User manuals and
online help can wait until later.

Instructions

Review the Concept Document and the Design Specifications which follow
this introduction and are also located on your class disk. You will need to
copy the Design Specifications to your own system in order to modify it.

Note

Requirements 7.5 and 7.7 are not completed in the Design Specification.
You must complete them, and complete the estimate for the entire project.
Note that labor is charged at $125.00 / hour.

You may change the estimates on the other requirements of the project,
except for the Specification estimates, since they were already done except
for 7.5 and 7.7.

When you figure out your complete estimate, write it out on a piece of
paper with your name on it and give it to the instructor.

When you are ready to start programming, start with a copy of a fresh,
unmodified database. Make your modifications, making sure to keep the
following things in mind:

. Follow Navision Standards in all aspects of this project.

. Do your internal documentation as you go.


Coding in Forms 7-5

. Keep your project log up to date as you go.

. Track your time on each Requirement as you go.

. Update the Design Specification with the Objects actually used as you
go.

When you are ready for the Dataport, note that the customer has provided
a file on your class disk, called "tspaces.txt". It has the following fields and
fields in it, in this order:

. Fixed Asset No.

. Tenant Space

. Tenant No.

. Monthly Rent

. Deposit

. Size

For the best results, you should create the Fixed Assets that are in the
Dataport before you test-execute the Dataport. These are:

. PARKPLAZA

. MEDOFFICE1

. MEDOFFICE2

. PARKVIEW

Be sure to test your program thoroughly, using the imported Tenant Spaces
and the Automatic Billing to increase the amount of data being tested. Test
as many cases as possible.

Deployment

Once you have completed your programming and completed the testing, go
through the steps necessary to deploy the product, including setting the
Version tags, and exporting all modified or new objects as both an fob and
a txt file. Note the objects you have modified from the base application and
7-6 Developing Navision Solutions

then switch to the original fresh, unmodified Attain database from which
you started. Mark those modified objects and export them as text. Use the
two text files to create a Change Log, using either the Merge Tool or the
Compare Tool.

Billing and Review

Now, complete the billing cycle by coming up with an actual amount to bill
the customer, using your actual hours and actual objects needed to run the
fob file you extracted. Write this up on a piece paper with your name to give
to the instructor. Be prepared to show your project to the class. To do this,
copy the fob file, the updated Design Specification, your Project Log and
your Change Log to a diskette.
Coding in Forms 7-7

8.2 CONCEPT DOCUMENT

It has been determined that the existing Fixed Assets granules will be
sufficient for tracking expenses on a per-property basis. Each building will
be considered one Fixed Asset.

It is also required that revenues and expenses be tracked on a per tenant


basis. For this first phase, we will track revenues only on a per tenant
basis. Expenses per tenant will be added during the second phase.

7.1. Modifications to Basic Receivables


The Basic Receivables granule is perfectly acceptable for our purposes,
except for one thing - our Customers are called "Tenants". Make the
appropriate name changes throughout our system.

Do not change any existing reports at this time.

Limitation: Since Ted Spinner Enterprises is only purchasing Basic General


Ledger, Basic Receivables and Fixed Assets, only these areas will be
modified.

7.2. Modifications to Basic Fixed Assets


The Fixed Assets granules will work as is for tracking expenses on a per
property basis. Modifications are needed to add the ability to track
revenues on a per Tenant Space basis.

7.2.1. Allow Posting of Revenue Entries


Add the fields necessary to support the posting of Fixed Asset (Property)
revenues to the General Ledger. This will include adding a Revenue
Account field to the Fixed Assets Posting Group, and adding a number
series to the Fixed Asset Setup to generate document number
automatically.

7.2.2. Fixed Assets Menu


Add the ability to access the Tenant Journal, which is described in section
7.4.

7.3. Tenant Spaces


Each individual office, apartment, shop or store within a property is called a
7-8 Developing Navision Solutions

Tenant Space. There are multiple Tenant spaces per property (Fixed
Asset).

7.3.1. Tenant Space Maintenance


A table should be created that is Subsidiary to the Fixed Asset table. The
Tenant Space Card should be accessible from the Fixed Asset card and
should allow the entry of the space number, size (in square feet), monthly
rent, amount of security deposit, and Tenant No. if occupied (Tenant No. is
to be blank if unoccupied).

7.3.2. Lookup Tenant Spaces from Tenant Card


Allow user to look up, from the Tenant Card, a list all Tenant Spaces
occupied by that Tenant.

7.3.3. Property Information


On the Fixed Assets Card, add a tab (called Tenant Spaces) which will
contain 4 fields. These fields will show, for this property, the number of
Tenant Spaces, the total size (square feet) of all Tenant Spaces, the total
deposits and the total monthly rent. The last two must only be counted if
the tenant space is actually occupied.

7.3.4. Property List Report


Create a report that will list all properties and for each one will list all
Tenant Spaces.

Important note: Not all Fixed Assets are Properties. Only include those with
Tenant Spaces.

7.4. Tenant Journal Entries


Allow the user to make Journal Entries for Tenant Revenue. This will be
called the Tenant Journal.

7.4.1. Tenant Journal


The user must be able to enter a Fixed Asset, Tenant Space and Date. The
Tenant can be defaulted or entered, and the Amount is the Amount
charged to the Tenant.

Limitation: Only one Journal is needed, so there will not be any Journal
Coding in Forms 7-9

Templates or Journal Batches.

7.4.2. Automatic Filling of Tenant Journal


There will be a way to automatically fill the journal with one or more
properties. Fill the Journal with all the currently rented spaces, and the
Monthly Rent as the amount. Allow the user to modify the entries before
posting. This function should be called from the Journal.

7.5. Tenant Space Ledger


The entries entered into the Journal will be posted into a Ledger. This
ledger can be viewed from the Tenant Space Card

7.5.1. Tenant Space Transaction Report


Create a report that will show Ledger Entries per Tenant Space, including a
total per Tenant Space, a total per Fixed Asset (property) and a Grand
Total. Allow user to enter a Date Filter so this report can be from a
particular Date Range.

7.5.2. Tenant Space Information


Add a FlowField to the Tenant Space Card (not the list) which shows the
total transactions in the Ledger for this Tenant Space. Be sure to take the
Date Filter into account.

7.6. Posting
The Tenant Journal must be able to get posted to the Tenant Ledger. It
should also post to the General Ledger at the same time. The Debit goes to
the Tenant, and the Credit goes to the Revenue Account mentioned in
section 7.2.1. The Document Number should be automatically generated
(see same section). Make sure dimensions have been deleted from table
352 Default Dimension and direct posting has been checked on in table 15
GL Account.

7.7. Data Conversion


A Tenant Space text file will be created by Spinner Enterprises. It will be a
flat file, with fixed length fields, one Tenant Space per line. It must be
imported into Navision to initialize the Tenant Space table. A test file will be
provided.
7-10 Developing Navision Solutions

8.3 Design Specifications


Property Management Project
for Ted Spinner Enterprises

Table of Contents
Requirement Description Page
7.1: Modifications to Basic Receivables 11
7.2: Modifications to Basic Fixed Assets 13
7.3: Tenant Spaces 16
7.4: Tenant Journal 19
7.5: Tenant Space Ledger 22
7.6: Posting 25
7.7: Data Conversion 27
Coding in Forms 7-11

7.1: Modifications to Basic Receivables

Summary: Change "Customer" to "Tenant".


Status: approved
Assigned To: you
Hours Estimated: 0.5
Estimated Labor: $62.50
New Table Objects: 0
Other New Objects: 0
New Object Cost: $0
Estimated Billing: $62.50

Part 1: Technical Specifications

7.7.1. Function
The Basic Receivables granule is perfectly acceptable for Spinner's
purposes, except for one thing - their Customers are called "Tenants".
Make the appropriate name changes throughout the part of the system they
are purchasing.

7.7.2. Value
Spinner's business calls for them to call their customers "Tenants".

7.7.3. New Functionality


Modify the following objects so that every instance (that the end user can
see) of "Customer" is replaced by "Tenant":

Tables:

Customer
Customer Ledger Entry
General Journal Line
Forms:

Customer Card
7-12 Developing Navision Solutions

Customer List
Customer Ledger Entries
Sales & Receivables Menu

Part 2: Limitations
Do not change any existing reports at this time.

Since Ted Spinner Enterprises is only purchasing Basic General Ledger,


Basic Receivables and Fixed Assets, only these areas will be modified.

Part 3: Estimate
Work Element Hours
Specification 0.1
Programming 0.3
Testing
Documentation 0.1
Total 0.5

Part 4: Impact on Upgrades


This Requirement will have some impact on upgrades. We recommend that
whenever an upgrade is received, this requirement be re-implemented
using the Change Log. We estimate that this will take 0.2 hours per
upgrade.

Part 5: Approval
The customer has given us written approval on the Concept and has
waived approval on all Design Specifications.

Part 6: Objects
Object Object Object Name Base Version
Type Number (or "New")
Coding in Forms 7-13

7.2: Modifications to Basic Fixed Assets

Summary: Tracking Revenues per Tenant


Space
Status: approved
Assigned To: you
Hours Estimated: 0.8
Estimated Labor: $100.00
New Table Objects: 0
Other New Objects: 0
New Object Cost: $0
Estimated Billing: $100.00

Part 1: Technical Specifications

7.7.4. Function
Add the fields necessary to support the posting of Fixed Asset (Property)
revenues to the General Ledger. This will include adding a Revenue
Account field to the Fixed Assets Posting Group, and adding a number
series to the Fixed Asset Setup to generate document number
automatically.

Also, add the ability to access the Tenant Journal.

7.7.5. Value
Allows posting of revenues to the General Ledger while we post Tenant
Journal Entries.

7.7.6. Current Functionality


Fixed Asset Posting Groups are only used to post expenses currently. For
this reason, they are assigned at the Depreciation Book level. This
functionality will remain unchanged.

7.7.7. New Functionality


To the FA Posting Group table, add a field called Revenue Account. Like
the other account numbers located there, it should be table related to the
7-14 Developing Navision Solutions

G/L Account Table. The form used to edit these posting groups should be
updated accordingly.

To the Fixed Asset table, add a field called FA Posting Group Code. This
field should be table related to the FA Posting Group table. Add this field to
the Posting tab of the Fixed Asset Card.

Also, to the FA Setup table, add a field to hold the new Tenant Charges No.
Series. Like the other No. Series, it should be table related to the No.
Series table. The Fixed Asset Setup form should be updated to allow the
user to enter this field.

The Fixed Assets Menu should be modified to add access to the Tenant
Journal (see section 7.4 for creation of Journal) from it. Include it in the
same area as the current Journal menu items.

Part 2: Limitations
The current functionality, including the fact that the FA Posting Groups are
assigned to a Depreciation Book for expense posting purposes, will not be
modified.

Part 3: Estimate
Work Element Hours
Specification 0.2
Programming 0.4
Testing
Documentation 0.2
Total 0.8

Part 4: Impact on Upgrades


This Requirement will have some impact on upgrades. We recommend that
whenever an upgrade is received, this requirement be re-implemented
using the Change Log. We estimate that this will take 0.3 hours per
upgrade.

Part 5: Approval
Customer has given us written approval on the Concept and has waived
approval on all Design Specifications.
Coding in Forms 7-15

Part 6: Objects
Object Object Object Name Base Version
Type Number (or "New")
7-16 Developing Navision Solutions

7.3: Tenant Spaces

Summary: Track the Tenant Spaces in each


Property (Fixed Asset)
Status: approved
Assigned To: you
Hours Estimated: 1.8
Estimated Labor: $225.00
New Table Objects: 1
Other New Objects: 4
New Object Cost: $336.00
Estimated Billing: $561.00

Part 1: Technical Specifications

7.7.8. Function
Each individual office, apartment, shop, or store within a property is called
a Tenant Space. Subsidiary table will be created, related to the Fixed Asset
table. This will allow each Tenant Space to have its own data, plus revenue
transactions tracked for it. Tenant Space can be looked up from Fixed
Asset Card and from Tenant Card.

Fixed Asset Card will contain summary information about all Tenant
Spaces.

A Property List report will show all Tenant Spaces per Fixed Asset.

7.7.9. Value
Will allow valuable rental data to be accessed in an easy fashion, both on
screen and in a report.

7.7.10. Current Functionality


None.
Coding in Forms 7-17

7.7.11. New Functionality


Create a Subsidiary Table called FA Tenant Space, related to the Fixed
Asset table. Each record should contain a field to relate back to the
corresponding Fixed Asset record, an identifying code, the size (in square
feet), the monthly rent, and the amount of the security deposit. It should
also contain a Tenant Number to indicate which Tenant is occupying the
Tenant Space. If no Tenant is occupying a Tenant Space, we will leave this
field blank.

Create a Card to view and edit this table, also create a List to look up into
this table. Provide a standard way to get from each one to the other.
Provide a way to view, add and edit all tenant spaces for each Fixed Asset
(property) from the Fixed Asset Card. Also, add a way to look up, from the
Tenant Card, a List of all of the Tenant Spaces that this Tenant is
occupying.

Add a new tab to the Fixed Asset Card called "Tenant Spaces". On this tab,
place four FlowFields, showing the number of Tenant Spaces, the total
size, the total deposits and the total monthly rent. These last two must only
be counted if the tenant space is actually occupied.

Create a report called "Property List" which will list all Fixed Assets
(properties) and for each one, lists each Tenant Space, including the
number, size, tenant, tenant name (or "unoccupied" if there is no tenant),
monthly rent, and deposit. There should be totals for each Fixed Asset (and
a grand total for all Fixed Assets), of the size, the monthly rent and the
deposits. Since some Fixed Assets are not properties, do not print anything
for a Fixed Asset that does not have any Tenant Spaces related to it. Also,
allow the user to filter this report by Fixed Asset, Tenant, and size.

Part 2: Limitations
All navigation keys, field names, and so on, will adhere to Navision
Standards.

Part 3: Estimate
Work Element Hours
Specification 0.5
Programming 1.0
Testing 0.1
Documentation 0.2
Total 1.8
7-18 Developing Navision Solutions

Part 4: Impact on Upgrades


This Requirement will have minimal impact on upgrades. Only the Tenant
Card and Fixed Asset Card changes (adding the FlowFields to the Fixed
Asset table, and menu buttons and fields to the forms) effect the base
application. We recommend that whenever an upgrade is received, this
requirement be re-implemented using the Change Log for those three
objects only. We estimate that this will take 0.3 hours per upgrade.

Part 5: Approval
Customer has given us written approval on the Concept and has waived
approval on all Design Specifications.

Part 6: Objects
Object Object Object Name Base Version
Type Number (or "New")
Coding in Forms 7-19

7.4: Tenant Journal

Summary: Create Tenant Journal to Enter


Revenue Entries
Status: approved
Assigned To: you
Hours Estimated: 1.8
Estimated Labor: $225.00
New Table Objects: 1
Other New Objects: 2
New Object Cost: $288.00
Estimated Billing: $513.00

Part 1: Technical Specifications

7.7.12. Function
Allow the user to make Journal Entries for Tenant Revenue. This will be
called the Tenant Journal. The user must be able to enter a Fixed Asset,
Tenant Space and Date. The Tenant can be defaulted or entered, and the
Amount is the Amount charged to the Tenant.

There will be a way to automatically fill the journal with one or more
properties. Fill the Journal with all the currently rented spaces, and the
Monthly Rent as the amount. Allow the user to modify the entries before
posting. This function should be called from the Journal.

7.7.13. Value
This Journal will allow the user to make Revenue Entries for Tenant
Spaces in an efficient manner consistent with Navision Standards.

7.7.14. Current Functionality


None.

7.7.15. New Functionality


Create a Tenant Journal table in which charges to the Tenants are entered.
The user must enter the Fixed Asset, the Tenant Space and the Date. The
7-20 Developing Navision Solutions

Tenant No. should be filled in automatically, but can be overridden (in case
the Tenant to be charged has already moved out). There should be a single
amount per line, the amount to be charged to the tenant. If the entered
Date is the first of the month, default this amount to the Monthly Rent for
that Tenant Space. Allow for a standard length description. Whenever a
new line is added, the Fixed Asset and the Date should be copied from the
line above. There is only one Journal, so there is no need for any Journal
Templates or Batches.

Create a worksheet window in which the user can make Tenant Journal
entries.

A batch job (a processing only report) should be created which will


automatically create entries into this journal for us. It should ask the user
for a Posting Date on the Option tab of the Request Form and make sure
that this date is on the first of a month. It should then run through all Tenant
Spaces that have a Tenant, filling in the journal with the information on that
Tenant Space record, including the Monthly Rent as the amount. The
description should be set to the name of the Posting Date's month, followed
by the word "Rent" (e.g. "January Rent" or "June Rent"). This report should
allow us to filter the Tenant Spaces by Fixed Asset. We should be able to
call it from the Tenant Journal by selecting a menu button there.

Part 2: Limitations
Only one Journal is needed, so there will not be any Journal Templates or
Journal Batches.

Part 3: Estimate
Work Element Hours
Specification 0.5
Programming 1.0
Testing 0.1
Documentation 0.2
Total 1.8

Part 4: Impact on Upgrades


None. These objects can be carried forward without change during an
upgrade.

Part 5: Approval
Customer has given us written approval on the Concept and has waived
approval on all Design Specifications.
Coding in Forms 7-21

Part 6: Objects
Object Object Object Name Base Version
Type Number (or "New")
7-22 Developing Navision Solutions

7.5: Tenant Space Ledger

Summary: Standard Ledger and Transaction


Report
Status: specification in progress
Assigned To: you
Hours Estimated:
Estimated Labor:
New Table Objects:
Other New Objects:
New Object Cost:
Estimated Billing:

Part 1: Technical Specifications

7.7.16. Function
The entries entered into the Journal will be posted into a Ledger. This
ledger can be viewed from the Tenant Space Card. The Ledger and Ledger
Form will need to be created.

Create a report that will show Ledger Entries per Tenant Space, including a
total per Tenant Space, a total per Fixed Asset (property) and a Grand
Total. Allow user to enter a Date Filter so this report can be from a
particular Date Range.

Add a FlowField to the FA Tenant Space Card (not the list), which shows
the total transactions in the Ledger for this Tenant Space. Be sure to take
the Date Filter into account.

7.7.17. Value
Standard Transaction functionality. Allows user to view or print full detail of
all transactions on a Tenant Space basis.

7.7.18. Current Functionality


None.
Coding in Forms 7-23

7.7.19. New Functionality


Complete this section

Part 2: Limitations
No Registers will be created.

Entries will not be allowed directly into the Ledger through the Ledger
Entries form.

Part 3: Estimate
Complete this section.

Work Element Hours


Specification
Programming
Testing
Documentation
Total

7.8.

Part 4: Impact on Upgrades


Complete this section.

Part 5: Approval
Customer has given us written approval on the Concept and has waived
approval on all Design Specifications.
7-24 Developing Navision Solutions

Part 6: Objects
Object Object Object Name Base Version
Type Number (or "New")
Coding in Forms 7-25

7.6: Posting

Summary: Post from Tenant Journal to Tenant


Ledger
Status: approved
Assigned To: you
Hours Estimated: 2.2
Estimated Labor: $275.00
New Table Objects: 0
Other New Objects: 4
New Object Cost: $96.00
Estimated Billing: $371.00

Part 1: Technical Specifications

7.8.1. Function
The Tenant Journal must be able to be posted to the Tenant Space Ledger.
It should also post to the General Ledger at the same time. The Debit goes
to the Tenant and the Credit goes to the Revenue Account stored in the FA
Posting Group. The Document Number is automatically generated using
the Tenant Charges No. Series on the FA Setup.

7.8.2. Value
Posting will be simple and will update both ledgers in one procedure.

7.8.3. Current Functionality


None.

7.8.4. New Functionality


Create a standard set of four codeunits to post the Tenant Journal.

The first should be a Check Line codeunit. It should test if the line is empty
(no Fixed Asset, No Tenant Space and no Amount), and if it is, nothing
should happen for this line. It then should test to be sure that we entered a
Tenant, a Fixed Asset, a Tenant Space, an amount and a Date, and that
the date is not a Closing Date.
7-26 Developing Navision Solutions

The second should be a Post Line codeunit. It should check to see if the
line is empty, and if it is, nothing should happen for this line. It should then
call the Check Line codeunit. Then check to make sure that the Fixed
Asset, the Tenant and the Tenant Space are all valid. It should create a
Tenant Space Ledger entry for this journal line, copying the information and
inserting the new entry. And it should post this information to the General
Ledger as follows -

The debit (the amount in the journal) should be posted to the Tenant
account. It should be balanced by a credit to the Revenue Account
listed in the related Fixed Asset Posting Group. The description
should be copied from the Tenant Journal. The Document Number
(Doc Type = Invoice) should be generated from the Tenant Charges
No. Series entered in the FA Setup table.

The third codeunit should be a Post Batch codeunit. It should run through
the Tenant Journal (as the user has currently filtered it) and call the Check
Line codeunit for each line. Then it should call the Post Line codeunit for
each line. Finally, it should delete the lines. A dialog window showing the
progress should be displayed while this is taking place.

The fourth should simply ask if the user wants to post. If so, it should call
the Post Batch codeunit. If not, it should simply exit, doing nothing. This
codeunit should be called from the Tenant Journal when the user selects it
from a menu button there.

Part 2: Limitations
No Register is being created at this time.

Payment of these charges is made through the normal procedures of the


Basic Receivables granule.

Dimensions will not be used. Any customers with Dimensions previously


defined will need to delete those Dimensions for posting.

Part 3: Estimate
Work Element Hours
Specification 0.6
Programming 1.2
Testing 0.2
Documentation 0.2
Total 2.2
Coding in Forms 7-27

Part 4: Impact on Upgrades


None. These objects can be carried forward without change during an
upgrade.

Part 5: Approval
Customer has given us written approval on the Concept and has waived
approval on all Design Specifications.

Part 6: Objects
Object Object Object Name Base Version
Type Number (or "New")
7-28 Developing Navision Solutions

7.7: Data Conversion

Summary: Import Tenant Spaces


Status: specification in progress
Assigned To: you
Hours Estimated:
Estimated Labor:
New Table Objects:
Other New Objects: 1
New Object Cost: $24.00
Estimated Billing:

Part 1: Technical Specifications

7.8.5. Function
A Tenant Space text file will be created by Spinner Enterprises. It will be a
flat file, with fixed length fields, one Tenant Space per line. It must be
imported into Navision to initialize the Tenant Space table.

7.8.6. Value
Will be able to bring in data from old systems quite rapidly.

7.8.7. Current Functionality


None.

7.8.8. New Functionality


Complete this section.
Coding in Forms 7-29

Part 2: Limitations
Test file will be provided by the Spinner Enterprises.

Part 3: Estimate
Complete this section.

Work Element Hours


Specification
Programming
Testing
Documentation
Total

Part 4: Impact on Upgrades


None. Once data conversion is complete, this dataport can be safely
erased.

Part 5: Approval
Customer has given us written approval on the Concept and has waived
approval on all Design Specifications.

Part 6: Objects
Object Object Object Name Base Version
Type Number (or "New")
7-30 Developing Navision Solutions
LAB 9
THE UPGRADE PROCESS

The exercises in this chapter will reinforce the


knowledge that you have gained in this part of the
course.

If you have read and understood all of the


material up to this point, you should be able to
complete all of the exercises in this chapter.

This chapter covers the following section:

9.1 Upgrading a Customized Database


9-2 Developing Navision Solutions

9.1 UPGRADING A CUSTOMIZED DATABASE

In this section, you will upgrade a modified Navision Financials 2.60.B


database to Navision Attain 3.01.A. Before the upgrade, read over the
Property Management concept document to become familiar with the
impact the modifications have on the database you will be upgrading (This
document can be found in Lab 8).

9.1.1 Upgrading the customized database

To perform this lab you will need to follow the instructions in the
UpgradeToolkit-Instructions.PDF file. The custom database is called
SpinnerEnterprises.FDB (it is version 2.60.B). If you need a clean 3.01.A
database, use the one on the class disk.

Remember to test the resulting database by posting in several areas


including the Tenant Journal on the Fixed Asset menu.

You might also like