Na Development
Na 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.
The software described is supplied under license and must be used and
copied in accordance with the enclosed license terms and conditions.
COPYRIGHT NOTICE
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.
The trademarks of Navision a/s and Navision Development a/s are listed on
this Web site: http://trademarks.navision.com
Table of Contents
i
Table of Contents
ii
Table of Contents
9.2 Definitions
iii
Table of Contents
8.1 Introduction
iv
CHAPTER 1
COMPLEX DATA TYPES
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).
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
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
You can access the code in a codeunit from other objects in several ways:
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:
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.
. 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:
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.
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.
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.
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.
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 NOT the record in the table, or the table itself.
. 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
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
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
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
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
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
IF Customer.FIND('-') THEN
REPEAT
//work with customer record
UNTIL Customer.NEXT = 0;
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:
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
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.
1 Create a key in the customer table that contains the “Credit Limit
(LCY)” field.
By Hand
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.
In Code
8 Create a global variable, call it Customer. The type is record and the
subtype is 18.
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”.
SETCURRENTKEY
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
SETRANGE
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
SETFILTER
You can also use replaceable parameters (%1, %2 and so on) just like the
MESSAGE function.
Examples
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
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
---------------------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------------------
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.
Examples
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
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
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.
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.
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.
Customer.FIND('-');
REPEAT
1-18 Applied Programming in CSIDE
Multilanguage Functionality
Navision Attain now has Multilanguage functionality. This section will cover
what will be needed to enable this new feature.
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);
ERROR(
Text025,
FIELDCAPTION(Receive),FIELDCAPTION(Invoice),
FIELDCAPTION(Ship));
Which when the code is run the error message translates into:
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.
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.
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.
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?
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.
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.
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
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.
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.
Chapter Review
2 When can the Scope operator (" :: ") be used with complex datatypes?
Give an example.
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
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.
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.
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.
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
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.
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.
. 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.
. 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).
. 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.
. 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.
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.
. 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.
Message(‘OnInsert’);
4 Run the Price Groups form 7 and note when each fires.
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.
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.
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
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.
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.
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
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
In this section, you will learn about functions that you can only use in
reports. These functions help you create complex reports.
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;
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
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
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
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
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.
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.
. Decide on the tables that need to be read – these are the data items.
. 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.
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.
6 Add code to display and update the window with the Item No.
8 Test your creation. You may want to add a SLEEP to slow the report
down.
2-16 Applied Programming in CSIDE
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.
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.
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.
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.
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.
CHAPTER REVIEW
when a primary key field is changed and the user moves off of the
record.
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.
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?
TRIGGER: _________________________
TRIGGER: _________________________
CHAPTER 3
OBJECT ANALYSIS
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:
. 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.
4 Select a filename.
5 Click OK.
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.
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
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.
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
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.
}
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
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.
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”.
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
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
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.
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.
. The database that the Toolkit uses to store its data is a Navision C/SIDE
database. This is NOT an Attain 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
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
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.
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 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.
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.
5 In the Navision Developers Toolkit, create the two versions and import
the text files.
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
The ultimate result of using this methodology is a Navision Solution that fits
the client’s business, delivered on time and on budget.
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.
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.
The Developer will work with and assist the Analyst / Consultant in two
activities during this phase, Data Conversion and Customizations.
Development Phase
Often, the development phase can get out of control from a time & cost
4-4 Object Analysis and Development Methodology
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.
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
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.
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
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 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 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 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
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.
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
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.
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.
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:
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.
Concept Approval
Note that during the Development Phase, any Data Conversion routines
are also developed. See section 17 for more information on this subject.
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 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:
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:
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
Prototypes
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.
. The client agrees they will proceed with the project if the prototype
successfully demonstrates the ability to satisfy the requirement.
. You have an experienced developer who can produce the prototype with
little risk.
Design Review
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 Phase
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:
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
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
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.
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.
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.
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.
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.
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.
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>*"
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.
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:
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).
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).
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.
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.
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.
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.
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.
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
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?
Modification Flag
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.
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.
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.
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".
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".
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:
This is normally the order you would add them anyway, so it should be
convenient.
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
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 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
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.
User Document
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
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 Status field should indicate the current status of this Requirement in
one word.
. 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
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
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.
. 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.
. 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.
. 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
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
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:
// 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:
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
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
. Customer
. Date Started
. Date Completed
. Version Tag
The Project Log Heading can be in any format you want, but here is an
example that you may use:
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:
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
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.
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, 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:
. 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.
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
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
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.
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.
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
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.
. 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.
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.
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.
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.
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.
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
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?
In this section, we will look at the major parts of the Sales Posting codeunit
that posts all sales documents.
Breaking it Down
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 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.
Note that this report only does invoices. There is a separate report for each
document type.
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.
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.
. 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.
. The line is checked with its matching Shipment line (if the line was
previously shipped).
. 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.
. 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.
. 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
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.
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.
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
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.
In this situation, it might be desirable to import into the Journal and store
the records there.
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
. To make sure that the Business Rules for the application are being
followed.
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.
You will need to validate the information being imported, fill in missing
information and finally store the record in the Journal or post it.
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
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 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.");
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.
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.
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.
. Amount = 7324.12
Which fields are missing and must be filled in before you can post?
What other things did you notice that might affect the import?
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
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.
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.
None of these values will be in any export that you get from the customer.
6-10 Posting Routines and Data Conversion
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
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.
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.
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:
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.
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.
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
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 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.
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, 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 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
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.
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.
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);
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.
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.
The following methods are used to read or write data in external files (once
the file variable is open):
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.
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
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.
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.
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');
5. Try bringing up the customer list form by pressing F5. Which triggers
fired (list them in order)?
In this section, you will learn about the form’s built-in record variables and
the triggers that are associated with them.
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.
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
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.
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.
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.
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.
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.
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:
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
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.
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
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.
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.
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
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.
. OnFindRecord
. OnNextRecord
. OnAfterGetRecord
. OnAfterGetCurrRecord
. OnBeforePutRecord
Coding in Forms 7-15
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
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
In this activity, you will take a look at some of the advanced features on
forms and identify how they work.
3. Stay in the Customer Card form and look at the code in the
OnAfterGetRecord trigger.
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 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.
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
COMMIT
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.
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
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
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
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
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
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
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.
For example when Navision Financials version 2.60 was released, it added
support for Windows 2000, didn’t include support for SQL 2000.
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.
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.
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
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.
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.
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
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.
Here is an example showing you how this looks on the Help, About screen.
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.
. 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.
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
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.
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).
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.
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
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.
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.
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 -
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.
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.
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.
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.
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.
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).
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.
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 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.
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.
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.
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.
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.
which, in the old data structure was held on two different records in two
different tables.
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.
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
In this section, you will run various objects in several different ways.
In this exercise, you will create codeunit and add functions to run different
types of forms.
3 Click New.
4 Save the codeunit with the ID 98100 and the Name RunObject.
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.
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.
RunFormModal;
RunFormNonModal;
RunFormNonModal;
RunFormModal;
What did you notice? What was the difference in the two?
In this exercise, you will design a codeunit and add functions to run
different types of reports.
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.
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.
RunReportModal;
RunReportNonModal;
RunReportNonModal;
RunReportModal;
What did you notice? What was the difference in the two?
In this exercise, you will design a form and add code to run your codeunit
(98100).
2 Add a command button at the bottom of the window next to the Help
button.
5 Change the code to use a codeunit variable. Don’t forget to set the
subtype.
Complex Data Types 1-5
In this section, you will use the dialog variable to display information to the
user.
2 Click New.
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?
In this exercise, you add a function to your codeunit that updates a dialog
variable.
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?
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
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.
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.
9 Save the codeunit and test your function by putting code in the OnRun
trigger.
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
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.
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.
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.
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.
In this exercise, you will display the record set of a record variable by
passing that variable to a form.
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:
4 Save the codeunit and test your function by putting code in the OnRun
trigger.
7 In the OnRun trigger before you call DisplayRecordSet and after the
GET, add code to go to the first customer using find.
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.
Are the same types of customers being displayed with the setrange?
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;
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
3 Add a new global record variable with a name of Cust2 and a subtype
of Customer.
Cust2.SETCURRENTKEY("Currency Code");
Cust2.SETFILTER("Currency Code", '=USD');
Cust2.SETRANGE("No.", '50000', 'ZZZ');
Cust2.FIND('-');
Did the record set contain the records that you expected?
CLEAR(Customer);
Customer.COPY(Cust2);
Did the record set contain the records that you expected?
CLEAR(Customer);
Customer := Cust2;
Did the record set contain the records that you expected?
You may also want to experiment with the record variable functions
CopyFilter and CopyFilters.
In this exercise, you will display the values of a FlowField from a record
variable in several different ways.
5 Save and run the codeunit. You must press the Enter key on the dialog
to move to the next record.
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.
In this exercise, you will add records to a temporary table and display the
records in a form.
Customer.FIND('-');
REPEAT
CustTemp := Customer;
CustTemp.INSERT;
UNTIL Customer.NEXT(3) = 0;
In this section, you will create and use streams with File data types.
In this exercise, you will create a codeunit and add functions to create a
file, create the outstream object, and write to the file.
3 Click New.
4 Save the codeunit with the ID 98104 and the name Streams Test.
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.
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.
10 Add code to the function to create the OutStream object and then use
1-18 Applied Programming in CSIDE
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.
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.
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
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.
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.
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.
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.
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.
Try running the table. Does your code execute at the same time here?
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.
In this exercise, you will add code to the fields in the Resource table.
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.)
What if you change the field to the same value that it had previously?
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.
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
In this section, you will add code to reports to change the way that they run.
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.
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?
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.
7 Change the report to allow the user to filter the ledger data item on the
Work Type field.
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?
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.
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.)
If you set a filter on the work type field, does the filter show at the top of the
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.)
3 Add the Cust. Ledger Entry dataitem to the report and indent it under
department.
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.
8 Add a grand total for the entire report without creating a new variable
(See the currReport.CreateTotals function).
Did the totals show up correctly? Did you remember to set the TotalFields
property?
2-8 Applied Programming in CSIDE
In this section, you will create a couple of dataports that have code.
In this exercise, you will create an exporting dataport that requires some
coding.
3 Click New.
6 It should export the account number and the balance for the account.
8 Finally, add code to round the Balance to the nearest 1000 right before
it is exported.
Did the dataport create the file the way you expected?
In this exercise, you will create an exporting dataport that requires some
coding.
writes out column headings for the data. (Use the currFile variable and
the WRITE member function)
Did the dataport create the file the way you expected?
2-10 Applied Programming in CSIDE
LAB 3
OBJECT ANALYSIS
In this section, you will export an object as text, change it, and re-import.
If you had selected more than one object, how many files would you expect
to get created?
In this exercise, you will add a field to your text version of the customer
table.
2 Enter C:\T00018.txt.
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
In this exercise, you will import your changed customer table back into
C/SIDE.
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.
In this section, you will export several objects as text into one file and
renumber those objects to a new range.
In this exercise, you will import the objects that you need to re-number.
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.
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.
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.
In this exercise, you will open the file and re-number the objects.
2 Enter C:\Renumber.txt
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.
In this exercise, you will import the renumbered objects back into Navision.
What happened?
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.
Everything should have imported without a problem. Test all the objects by
compiling them and running them.
Object Analysis 3-7
In this section, you will use impuls Workbench to investigate the impact of
making changes to the system.
In this exercise, you will open impuls and the impuls database.
3 Select the impuls workbench database that you created in class. (This
should be called Workbench.fdb.)
In this exercise, you will import the objects that you renumbered in the last
section into impuls.
2 On the General tab, enter a code (FA100) and description (Fixed Asset
Project version 1.00) for the objects you are importing.
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.
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.
Which table is the last table that uses the customer name?
3 Find the Search Name key in the Customer table and select it.
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?
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
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.
In this exercise, you will import the objects that you need to document.
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).
8 Click the All button. You should now see all the modified objects.
1 With the filter applied to the Object Designer and the All button
selected, select table 18.
2 Click Design.
Did you remember to tag the field description with your internal tag?
Development Methodology 4-3
6 Click Design.
Did you remember to tag the field description with your internal tag?
10 Click Design.
Did you remember to tag the field description with your internal tag?
Did you remember to tag the code with your internal tag?
14 Click Design.
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
In this section, you will create a project log for all of the objects you just
documented internally.
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:
b. Solution Center
e. Date Started
f. Date Completed
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
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
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
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.
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).
In this exercise, you will set the Modified flag for all the objects to No.
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.
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.
You may want to re-import the objects and use the import worksheet to
check that every object was exported.
Development Methodology 4-9
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.
Read through the concept document and the design specifications. This
should give you an idea of what needs to be done for this customer.
For all parts of the project 7.1 – 7.7, you need to complete the
specifications.
. 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.
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.
In this exercise, you will import the objects that you need for the rest of the
exercises in this lab.
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).
In this exercise you will add items to the item table that have a Base Unit of
Measure of HOUR. These are rentable items.
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.
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.
11. Now go back to the journal and add several lines for each rentable item
you created.
5-4 Posting Routines and Data Conversion
In this section you will create and test a check line codeunit for the journal
that you imported.
3. Click New.
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’.
In this exercise you will create and set up the RunCheck trigger for the
codeunit. We will be leaving the OnRun trigger empty.
In this exercise, you will actually create the code that does all the work in
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.
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.
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
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.
6. Run the form. Check the lines that you had previously entered.
7. Add new lines that are missing information and check those.
What happens if you check a line that has no customer, item or amount?
Posting Routines 5-7
In this section you will create and test a post line codeunit for the journal
that you imported.
3. Click New.
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’.
In this exercise you will set up the RunWithCheck function for the codeunit.
3. The first line of code in the RunWithCheck function will be to copy the
local variable RentalJnlLine2 to a global variable called RentalJnlLine.
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.
In this exercise, you will actually create the code that does all the work in
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.
4. Add code to lock the ledger table and get the next line number that can
be used in the ledger table.
c. Add code to the Code function that will only happen once
Posting Routines 5-9
5. Add code to make sure that the Item specified exists in the Item table.
Do the same for the Customer.
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.
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.
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.
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.
What happens if you post a line that has no customer, item or amount?
Posting Routines 5-11
In this section you will create and test a post batch codeunit for the journal
that you imported.
3. Click New.
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’.
In this exercise you will set up the OnRun trigger for the codeunit.
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.
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.
In this exercise, you will actually create the code that does all the work in
the post batch codeunit.
2. Add code to the Code function that makes sure there is something to
post.
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.
open string:
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.
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
6. Add code to delete all the journal records from the journal table.
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.
3. Open the C/AL editor for the Post Batch menu item.
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
In this section you will create and test a start routine called post for the
posting routine codeunits that you have created.
3. Click New.
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’.
In this exercise you will set up the OnRun trigger for the codeunit.
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.
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.
In this exercise, you will actually create the code that does all the work in
the post codeunit.
2. Add code to the Code function that CONFIRMs the user’s decision to
post.
3. Add code, call the post batch codeunit that you built earlier.
parameter.
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.
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
In this section, you will modify the sales document posting routine.
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.
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.
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.
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.
In this exercise, you will look at the file that you are going to import.
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):
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.
. There is a due date field in the table, although it is not shown on the form.
. The sales accounts in the file do not correspond to the ones in Navision,
so ignore them.
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.
Name Cannon
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.
6. Clear the No. Series field. You will not need number series for your
batch.
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.
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 -
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
In this section, you will finally build the dataport that imports into the journal.
In this exercise, you will create the dataport and add all the necessary
pieces.
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.
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
In this section, you will test your dataport and make any necessary
changes.
In this exercise, you will run the dataport created in section 6.3.
3. Enter the full path and filename for the import file (this should be on
your class disk with the name SalesJournal631.txt).
If so, what are they and how might you fix them?
Once fixed, run the dataport again to see if there are other errors.
In this exercise, you will look at the records that the dataport imported.
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.
In this section, you will copy your dataport and change it so that it posts
directly without creating journal lines.
In this exercise, you will save your dataport with a new name and ID.
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.
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.
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.
In this exercise you add code to call codeunit 12 for the imported journal
line.
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
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.
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
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.
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).
5. Change the caption of the large label at the top to Field Of Dreams.
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).
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.
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).
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.
4. Click Edit, Paste. A new button with the caption General Ledger will
appear.
6. With the button selected, press F9 to view the code behind the button.
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
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?
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.
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.
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.
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?
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.
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.
7. If the main menu is already running close it and press F12 to open it
again.
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.
5. Click Finish to close the wizard and open the form designer.
10. Copy the label and the two textboxes to a new column (to the right of
the originals).
12. Change the source expression of the two new textboxes to use these
variables.
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.
In this exercise, you will call your statistics form from the card and list
forms.
6. Save and close the form. Test the form by running it and clicking the
button.
What happens if you change the sponsor card’s record while the statistics
form is still open?
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
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 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.
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.
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.
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).
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.
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
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
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.
8.1 Introduction
8.2 Concept Document
8.3 Design Specifications
7-2 Developing Navision Solutions
8.1 INTRODUCTION
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.
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
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
Preliminary Analysis
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:
. 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:
. 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.
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
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.
Tenant Space. There are multiple Tenant spaces per property (Fixed
Asset).
Important note: Not all Fixed Assets are Properties. Only include those with
Tenant Spaces.
Limitation: Only one Journal is needed, so there will not be any Journal
Coding in Forms 7-9
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.
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.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".
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.
Part 3: Estimate
Work Element Hours
Specification 0.1
Programming 0.3
Testing
Documentation 0.1
Total 0.5
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.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.
7.7.5. Value
Allows posting of revenues to the General Ledger while we post Tenant
Journal Entries.
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 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.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.
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 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.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.
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.
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 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.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.
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.
7.8.
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
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.
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.
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 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.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.
Part 2: Limitations
Test file will be provided by the Spinner Enterprises.
Part 3: Estimate
Complete this section.
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
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.