Anda di halaman 1dari 210

COURSE: 80312

DEVELOPMENT III
IN MICROSOFT DYNAMICS AX 2012

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Last Revision: August 2011


This courseware is provided as-is. Information and views expressed in this courseware, including URL and other
Internet Web site references, may change without notice.
Unless otherwise noted, the examples depicted herein are provided for illustration only and are fictitious. No real
association or connection is intended or should be inferred.
This courseware does not provide you with any legal rights to any intellectual property in any Microsoft product.
Complying with all applicable copyright laws is the responsibility of the user. Without limiting the rights under
copyright, no part of this courseware may be reproduced, stored in or introduced into a retrieval system, or
transmitted in any form or by any means or for any purpose, without the express written permission of Microsoft
Corporation.
Copyright 2011 Microsoft Corporation. All rights reserved.
Microsoft, Microsoft Dynamics, Microsoft PowerPoint, Microsoft SQL Server data management software
and Microsoft Dynamics AX are trademarks of the Microsoft group of companies. All other trademarks are
property of their respective owners.
This course content is designed for Microsoft Dynamics AX 2012.

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Table of Contents
Introduction

0-1

Welcome ............................................................................................................ 0-1


Microsoft Dynamics Courseware Contents ........................................................ 0-2
Documentation Conventions .............................................................................. 0-3
Student Objectives ............................................................................................. 0-4

Chapter 1: X++ Unit Test Framework

1-1

Objectives ........................................................................................................... 1-1


Introduction ......................................................................................................... 1-1
Creating Test Cases ........................................................................................... 1-2
Adding Methods to Test Cases .......................................................................... 1-3
Running Test Cases ........................................................................................... 1-5
Build Test Projects and Suites ........................................................................... 1-7
Lab 1.1 - Create a Test Case ........................................................................... 1-11
Summary .......................................................................................................... 1-13
Test Your Knowledge ....................................................................................... 1-14
Quick Interaction: Lessons Learned ................................................................. 1-16
Solutions ........................................................................................................... 1-17

Chapter 2: Working with Data

2-1

Objectives ........................................................................................................... 2-1


Introduction ......................................................................................................... 2-1
While Select ....................................................................................................... 2-2
Query .................................................................................................................. 2-9
Lab 2.1 - Fetching Data .................................................................................... 2-12
Lab 2.2 - Converting Queries ........................................................................... 2-15
Caching ............................................................................................................ 2-17
Locking ............................................................................................................. 2-23
Lab 2.3 - Reducing Locking .............................................................................. 2-25
Temporary Tables ............................................................................................ 2-27
Lab 2.4 - Temporary Tables ............................................................................. 2-28
InitFrom ............................................................................................................ 2-31
Parm Tables ..................................................................................................... 2-31
Date Effectiveness ........................................................................................... 2-32
Computed Columns in Views ........................................................................... 2-34
Data Integration ................................................................................................ 2-36
Lab 2.5 - Integrating External Data................................................................... 2-44
Summary .......................................................................................................... 2-46
Test Your Knowledge ....................................................................................... 2-47
Quick Interaction: Lessons Learned ................................................................. 2-50
Solutions ........................................................................................................... 2-51

Chapter 3: Classes

3-1

Objectives ........................................................................................................... 3-1


Introduction ......................................................................................................... 3-1
Collection Classes .............................................................................................. 3-2
Lab 3.1 - Create a Map .................................................................................... 3-12
Application Object Classes ............................................................................... 3-14
Lab 3.2 - Create a Query From Code ............................................................... 3-19

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Development III in Microsoft Dynamics AX 2012


Application Substituted Kernel Classes ............................................................ 3-21
Lab 3.3 - Create a Global method .................................................................... 3-24
RunBase Framework ........................................................................................ 3-25
Lab 3.4 - Make a RunBase Class ..................................................................... 3-37
Args Object ....................................................................................................... 3-39
Lab 3.5 - Using Args ......................................................................................... 3-42
Summary .......................................................................................................... 3-44
Test Your Knowledge ....................................................................................... 3-45
Quick Interaction: Lessons Learned ................................................................. 3-47
Solutions ........................................................................................................... 3-48

Chapter 4: Forms

4-1

Objectives ........................................................................................................... 4-1


Introduction ......................................................................................................... 4-1
Architecture ........................................................................................................ 4-2
Data Sources ...................................................................................................... 4-6
Lab 4.1: Create a form ..................................................................................... 4-16
Form Controls ................................................................................................... 4-18
Lab 4.2 - Use Unbound Controls ...................................................................... 4-22
Form Methods .................................................................................................. 4-23
Lab 4.3 - Initialize a Form ................................................................................. 4-25
Placement of Code ........................................................................................... 4-26
Additional Controls ........................................................................................... 4-26
Lab 4.4 - Add a window control ........................................................................ 4-35
Summary .......................................................................................................... 4-37
Test Your Knowledge ....................................................................................... 4-38
Quick Interaction: Lessons Learned ................................................................. 4-39
Solutions ........................................................................................................... 4-40

Chapter 5: Visual Studio Integration

5-1

Objectives ........................................................................................................... 5-1


Introduction ......................................................................................................... 5-1
Application Explorer ........................................................................................... 5-2
Visual Studio Projects ........................................................................................ 5-3
Managed code projects ...................................................................................... 5-3
Deploying Managed Code .................................................................................. 5-6
Visual Studio Debugging Experience for X++ .................................................... 5-7
Lab 5.1 - Create a Managed Code Project ......................................................... 5-9
Lab 5.2 - Create an Event Handler in Managed Code ..................................... 5-10
Summary .......................................................................................................... 5-11
Test Your Knowledge ....................................................................................... 5-12
Quick Interaction: Lessons Learned ................................................................. 5-14
Solutions ........................................................................................................... 5-15

Chapter 6: Workflow

6-1

Objectives ........................................................................................................... 6-1


Introduction ......................................................................................................... 6-1
Workflow Configuration ...................................................................................... 6-2
Create a Workflow Category .............................................................................. 6-2
Create a Query ................................................................................................... 6-3

ii

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Table of Contents
Create a Workflow Type ..................................................................................... 6-4
Enable Workflow on a Form ............................................................................... 6-5
Create a Workflow Approval ............................................................................... 6-8
Create Event Handlers ....................................................................................... 6-9
Author a Workflow ............................................................................................ 6-12
Lab 6.1 - Add Another Condition to the Submit Action ..................................... 6-15
Lab 6.2 - Enable Resubmit ............................................................................... 6-17
Summary .......................................................................................................... 6-20
Test Your Knowledge ....................................................................................... 6-21
Quick Interaction: Lessons Learned ................................................................. 6-22
Solutions ........................................................................................................... 6-23

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

iii

Development III in Microsoft Dynamics AX 2012

iv

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Introduction

INTRODUCTION
Welcome
We know training is a vital component of retaining the value of your Microsoft
Dynamics AX 2012. investment. Our quality training from industry experts
keeps you up-to-date on your solution and helps you develop the skills necessary
for fully maximizing the value of your solution. Whether you choose Online
Training, Classroom Training, or Training Materials; there is a type of training to
meet everyone's needs. Choose the training type that best suits you so you can
stay ahead of the competition.

Online Training
Online Training delivers convenient, in-depth training to you in the comfort of
your own home or office. Online training provides immediate access to training
24 hours-a-day. It is perfect for the customer who does not have the time or
budget to travel. Our newest online training option, eCourses, combine the
efficiency of online training with the in-depth product coverage of classroom
training, with at least two weeks to complete each course.

Classroom Training
Classroom Training provides serious, in-depth learning through hands-on
interaction. From demonstrations to presentations to classroom activities, you
receive hands-on experience with instruction from our certified staff of experts.
Regularly scheduled throughout North America, you can be sure you will find a
class convenient for you.

Training Materials
Training Materials enable you to learn at your own pace, on your own time with
information-packed training manuals. Our wide variety of training manuals
feature an abundance of tips, tricks, and insights you can refer to again and again:

Microsoft Dynamics Courseware


The Microsoft Dynamics Courseware consists of detailed training manuals,
designed from a training perspective. These manuals include advanced topics as
well as training objectives, exercises, interactions and quizzes.
Look for a complete list of manuals available for purchase on the Microsoft
Dynamics website: www.microsoft.com/Dynamics.

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

0-1

Development III in Microsoft Dynamics AX 2012

Microsoft Dynamics Courseware Contents


Test Your Skills
Within the Microsoft Dynamics Training Materials you find a variety of different
exercises. These exercises are offered in three levels to accommodate the variety
of knowledge and expertise of each student. We suggest you try the level three
exercises first, if you need help completing the task look to the information in the
level two exercises. If you need further assistance each step of the task is outlined
in the level one exercise.

Challenge Yourself!
Level 3 exercises are the most challenging. These exercises are designed for the
experienced student who requires little instruction to complete the required task.

Need a Little Help?


Level 2 exercises are designed to challenge students, while providing some
assistance. These exercises do not provide step by step instructions, however, do
provide you with helpful hints and more information to complete the exercise.

Step by Step
Level 1 exercises are geared towards new users who require detailed instructions
and explanations to complete the exercise. Level 1 exercises guide you through
the task, step by step, including navigation.

Quick Interaction: Lessons Learned


At the end of each chapter within the Microsoft Dynamics Training Material, you
find a Quick Interaction: Lessons Learned page. This interaction is designed to
provide the student with a moment to reflect on the material they have learned.
By outlining three key points from the chapter, the student is maximizing
knowledge retention, and providing themselves with an excellent resource for
reviewing key points after class.

0-2

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Introduction

Documentation Conventions
The following conventions and icons are used throughout this documentation to
help you quickly and effectively navigate through the information.
CAUTION: Cautions are found throughout the training manual and are preceded by
the word CAUTION in bold. Cautions are used to remind you of a specific result of a
specific action which may be undesirable.
HINT: Hints are found throughout the training manual and are preceded by the word
HINT in bold. Hints are used to suggest time-saving features or alternative methods for
accomplishing a specific task.
NOTE: Notes are found throughout the training manual and are preceded by the word
NOTE in bold. Notes are used to provide information which, while not critical, may be
valuable to an end user.
BEYOND THE BASICS: Advanced information found throughout the training manual
is preceded by the words BEYOND THE BASICS in bold. Beyond the Basics provides
additional detail, outside of standard functionality, that may help you to more optimally
use the application.
EXAMPLE: Examples are found throughout the training manual and are preceded by
the word EXAMPLE in bold. Examples bring to light business scenarios that may better
explain how an application can be used to address a business problem.

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

0-3

Development III in Microsoft Dynamics AX 2012

Student Objectives
What do you hope to learn by participating in this course?
List three main objectives below.
1.

2.

3.

0-4

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 1: X++ Unit Test Framework

CHAPTER 1: X++ UNIT TEST FRAMEWORK


Objectives
The objectives are:

Create a test case.

Add methods to a test case.

Run a test case.

Build a test project and suite.

Isolate test cases appropriately.

Introduction
The X++ Unit Test framework allows for unit tests to be created along with the
code they are designed to test.
A unit test is code that verifies that some specific application code has been
implemented correctly. If you adhere to the principles of test-driven
development, it is best for the developer who is writing the application code to
write the unit tests either before or during development.

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

1-1

Development III in Microsoft Dynamics AX 2012


Scenario
Isaac, the systems developer, is about to start a development project based on
some written specifications. Before he begins writing code, he has been asked to
create a Unit Test suite, to test the project during its lifecycle.

Creating Test Cases


A unit test, in the context of the Unit Test framework, includes test cases, how
test cases are staged with data, and the organization of test cases. A test case is a
class that extends the SysTestCase class. You can add test methods to test each
requirement of the feature code.
The following example is of how to create a test case. In this example you will be
testing an existing system class, so the results should be successful. You will test
the SysDictTable class, to make sure its methods to return the table name and
group for a given table name, are correct. The following is the test case class
declaration.
[SysTestTargetAttribute('SysDictTable',
UtilElementType::Class)]
class SysDictTableTest extends SysTestCase
{
SysDictTable sysDictTable;
}

Note the attribute used on the class. The SysTestTargetAttribute attribute is


attached to the test case to specify which element is being tested. In this case, it is
the SysDictTable class being tested. There are a number of predefined attributes
available in the Unit Test framework:

1-2

Attribute

Description

Applied to

SysTestMethodAttribute

Indicates that a method is


a unit test.

Method

SysTestCheckInAttribute

Indicates the test is a


check-in unit test. A
check-in test is run when
checking in code to a
version control system to
ensure a proper level of
quality.

Method or Class

SysTestNonCheckInAttribute

Indicates the test is not a


check-in test.

Method

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 1: X++ Unit Test Framework


Attribute

Description

Applied to

SysTestTargetAttribute

Indicates the application


object that is being tested
by the case. This attribute
takes two parameters:
Name of element, and
UtilElementType value.

Class

SysTestInactiveTestAttribute

Indicates the class or


method is inactive.

Method

Another variable has been created in the ClassDeclaration, of the type of the class
being tested.

Adding Methods to Test Cases


The next step in creating a test case, is to add some methods to the test case class.
Each method should assert that one of the known requirements is true.
There are two new methods added in the following example: one to assert that
the table's name is as expected, and the other to assert that the table's group value
is as expected.
[SysTestMethodAttribute]
public void testTableName()
{
// Verify that the tables name is set correctly.
this.assertEquals("CustTable", sysDictTable.name());
}
[SysTestMethodAttribute]
public void testTableGroup()
{
// Verify that the tables group is set correctly.
this.assertEquals(TableGroup::Main,
sysDictTable.tableGroup());
}

A test method will typically contain several assertions that are required to hold
true, for the test to be successful.
Note the use of the assertEquals method. This method, and other assert methods,
are available as part of the SysTestCase framework. These methods also have an
optional string parameter called _message, to specify the message that would go
into the infolog, if the assertion fails. The following is a list of available methods:
Method

Description

assertEquals

Tests if two values are equal.

assertNotEqual

Tests if two values are different.

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

1-3

Development III in Microsoft Dynamics AX 2012


Method

Description

assertTrue

Tests if the value is true.

assertFalse

Tests if the value is false.

assertNull

Tests if the value is null.

assertNotNull

Tests if the value is not null.

assertSame

Tests if the objects referenced are the same.

assertNotSame

Tests if the objects referenced are not the same.

assertRealEquals

Tests if real values differ by the amount specified in


the delta.

assertExpectedInfolog
Message

Tests for an infolog message that is expected.

fail

Enables a developer to create custom validation


logic and then call the fail method to integrate with
the Unit Test framework.

Setup Method
The next step is to override the setUp method of the SysTestClass. This will tell
the Unit Test framework what parameters need to be setup before the test is run.
In this case, you need to instantiate the SysDictTable object using the table name.
public void setUp()
{
sysDictTable = new SysDictTable(TableNum(CustTable));
super();
}

The setUp method can also be used to insert or update any data necessary for the
test.

TearDown Method
At the completion of a test run, it may be necessary to "undo" any data created in
the setUp method. This can be done in the tearDown method, which is called at
the end of the test. The tearDown method can be overridden on any class
extending SysTestCase.

1-4

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 1: X++ Unit Test Framework

Running Test Cases


When the test class has methods added to it, and appropriate setUp and
tearDown methods, it can be run.
To run the test case, right click the SysDictTableTest class, click Add-Ins and
then Run tests. The Unit Test toolbar will appear, and the test case will be added
to the list of tests. The test will automatically be run, and the results displayed in
the toolbar, and in the infolog (if it failed).

FIGURE 1.1 UNIT TEST TOOLBAR

The SysDictTableTest test should pass on the first run. Try changing the
expected values in the test* methods, or the table id used in the setUp method, to
emulate a test that fails, then run the test again. To run the test again, click the
Run button on the Unit Test toolbar.
If the test fails, click the Details button to see more details of the results.

Reviewing Test Results


To review test results, the Test jobs form can be opened from the Tools menu
(Tools > Unit test > Test jobs).
The Test jobs form lists all previous runs of tests, and displays information about
the test.

FIGURE 1.2 TEST JOBS FORM

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

1-5

Development III in Microsoft Dynamics AX 2012


Click the Tests button on the Test jobs form, to open another form where test
information is available at the method level.

FIGURE 1.3 TEST FORM SHOWS INFORMATION AT THE METHOD LEVEL

1-6

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 1: X++ Unit Test Framework


Listeners
Test results can be displayed in various formats, to suit the requirement. There
are several standard listeners that capture test results and format their results in
different ways. These can be configured on the Unit Test Parameters window,
as seen in the figure below.

FIGURE 1.4 LISTENER CONFIGURATION

Standard listeners include: Database, Infolog, Infolog (result only), Message


window, Print window, Progress bar, Text file, and XML file.
NOTE: It is also possible to create new listeners, by extending the
SysTestListener interface. This can be explored further on MSDN at this location
http://go.microsoft.com/fwlink/?LinkID=225151&clcid=0x409,by following the
"How to: Display Test Case Results" topic in the "Unit Test Framework" section.

Build Test Projects and Suites


Test classes can be organized in two ways:

Test projects

Test suites

Test Projects
Test projects are groupings of test classes and appear in the Development
Project tree along with other project types.

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

1-7

Development III in Microsoft Dynamics AX 2012


Use the following procedure to create a new Test project:
1. Open the Project tree.
2. Right-click either the Private or Shared node, point to New, and
then click Test project. This creates a project with a test suite.
3. Open the AOT.
4. In the AOT, select one or more test classes to include in the test
project. Drag them to the TestSuite node in the Test project.

FIGURE 1.5 TEST PROJECT

Once test classes have been added to a Test project, the entire collection of tests
can be run at the same time, by right-clicking the root node of the project, and
clicking Run.

Test Suites
Collections of tests can also be created with X++ code. These collections extend
the SysTestSuite class, and are referred to as Suites.
Use the following procedure to create a test suite class:
1. In the AOT, create a new Class.
2. Open the new class in the Code Editor.
3. Edit the ClassDeclaration so that the class extends the SysTestSuite
class.
4. Override the new method on the class.
5. In the new method, call the add method to add test cases to the suite,
as shown.
public void new()
{
// Create an instance of a test suite.
SysTestSuite suiteDictTableTest = new
SysTestSuite(classstr(SysDictTableTest));
;
super();

1-8

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 1: X++ Unit Test Framework


this.add(suiteDictTableTest);
}

The setUp and tearDown methods can also be used in the scope of a Test Suite.
Both methods are available to override on any class extending the SysTestSuite
class. In this way, the setting up of data and variables can be done before the
entire suite of tests is run.

Isolation
The isolation level of a test case varies based on the changes that the test case
will make to the data. Each test case could have different needs for isolation
based on what data it will change. The Unit Test framework provides four test
suite base classes that provide different levels of isolation. The following table
describes the test suites by the level of isolation that they provide.
Test suite class

Description

SysTestSuite

Default test suite. No isolation.

SysTestSuiteCompanyIsolate
Class

Constructs an empty company account for


each test class. All test methods on the class
are run within the company, then it is
deleted.

SysTestSuiteCompanyIsolate
Method

Constructs an empty company account for


each test method, and then deletes it at the
end of the method.

SysTestSuiteTTS

Wraps each test method in a transaction.


After the method is complete, the transaction
is aborted.
Note: Tests that need to commit data will not
work in this suite. Also, the
ParmExceptionExpected exception is not
supported in this suite.

SysTestSuiteCompIsolate
ClassWithTts

This is a combination of
SysTestSuiteCompanyIsolateClass and
SysTestSuiteTTS.

To apply a specific test suite to a test class, override the createSuite method on
the test class, and return an object of the type of the suite required.

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

1-9

Development III in Microsoft Dynamics AX 2012


The following example demonstrates how a test suite can be applied to a test
class.
class SysTestSuite createSuite()
{
// Isolation level: construct a company account for the
entire test class.
return new SysTestSuiteCompanyIsolateClass(this);
}

1-10

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 1: X++ Unit Test Framework

Lab 1.1 - Create a Test Case


During this lab, you will create a test case and add it to a test suite.
Scenario
Isaac is about to start a development project based on some written
specifications. Before he begins writing code, he has been asked to create a Unit
Test suite, to test the project during its lifecycle.

Challenge Yourself!
Create a test case class, and add a test method to it. The test case should check
that when an address record's Zip code is changed, the correct City is
automatically updated. Once complete, create a test suite class that enforces TTS
isolation, and link the test case class to it. Finally, run the test.

Step by Step
1.
2.
3.
4.

Open the AOT.


Create a new class.
Rename the class to MyTestSuite.
Edit the ClassDeclaration so that it extends the SysTestCase class,
and add a variable logisticsPostalAddress of type
LogisticsPostalAddress.
5. Add a public method called testCity.
6. Add a line of code to the method: this.assertEquals("New York",
logisticsPostalAddress.City);
7. Override the setUp method on the class. It should look like the
following code.
public void setUp()
{
ttsBegin;
select firstonly forupdate logisticsPostalAddress
where logisticsPostalAddress.City != "New York"
&& logisticsPostalAddress.CountryRegionId == "USA";
logisticsPostalAddress.validTimeStateUpdateMode(ValidTimeSt
ateUpdate::EffectiveBased);
logisticsPostalAddress.ZipCode = "10001";
logisticsPostalAddress.ZipCodeRecId = 0;
logisticsPostalAddress.LogisticsPostalAddressMap::modifiedF
ieldZipCode();

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

1-11

Development III in Microsoft Dynamics AX 2012


logisticsPostalAddress.update();
ttsCommit;
super();
}

8.
9.
10.
11.

Create a new class.


Name the class MyTestSuiteTTS.
Edit the ClassDeclaration so that it extends SysTestSuiteTTS.
Go back to the MyTestSuite class, and override its createSuite
method.
12. Change the method to read: return new MyTestSuiteTTS(this);
13. Run the test class.

1-12

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 1: X++ Unit Test Framework

Summary
This lesson explains how to use the Unit Test framework to test X++ code during
the development lifecycle.
The concepts of test cases, assertions, projects, suites and isolation are
introduced, demonstrating the rich features available to build robust logic tests.

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

1-13

Development III in Microsoft Dynamics AX 2012

Test Your Knowledge


Test your knowledge with the following questions.
1. What class does a test class need to extend?

2. What is the setUp method on the SysTestCase class used for?

1-14

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 1: X++ Unit Test Framework


3. Where can the Test jobs form be opened from?

4. What is meant by "isolation" in the context of the Unit Test framework?

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

1-15

Development III in Microsoft Dynamics AX 2012

Quick Interaction: Lessons Learned


Take a moment and write down three key points you have learned from this
chapter
1.

2.

3.

1-16

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 1: X++ Unit Test Framework

Solutions
Test Your Knowledge
1. What class does a test class need to extend?
MODEL ANSWER:
SysTestCase
2. What is the setUp method on the SysTestCase class used for?
MODEL ANSWER:
It is used to prepare variables and data that are required for the test.
3. Where can the Test jobs form be opened from?
MODEL ANSWER:
Tools > Unit test > Test jobs.
4. What is meant by "isolation" in the context of the Unit Test framework?
MODEL ANSWER:
Isolation refers to the scope in which company data should be created and
destroyed during a test run.

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

1-17

Development III in Microsoft Dynamics AX 2012

1-18

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 2: Working with Data

CHAPTER 2: WORKING WITH DATA


Objectives
The objectives are:

Program optimal database access using a "while select" statement.

Program optimal database access using queries.

Describe the caching mechanisms in Microsoft Dynamics AX.

Prevent and resolve database locking.

Use temporary tables in classes, forms, and reports.

List the reasons for using InitFrom<tablename> methods.

Use ParmId and ParmTables.

Discuss date effectiveness and describe how to build date effective


forms.

Add a computed column to a view.

Employ the various techniques available for integrating external data


with Microsoft Dynamics AX.

Introduction
A Microsoft Dynamics AX application processes large amounts of data. Most
functions involve sending data between the client and Application Object Server
(AOS) and between the AOS and database server. It is important to use the
correct approach to database access when developing in Microsoft Dynamics
AX. Almost every performance bottleneck is associated with database traffic.

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

2-1

Development III in Microsoft Dynamics AX 2012

While Select
This section describes the different qualifiers and options that can be used in the
select statement, to achieve optimal database access performance.
The complete syntax for the select statement is as follows.

[while] select [reverse] [firstfast]


[firstonly] [firstOnly10] [firstOnly100] [firstOnly1000]
[forupdate] [nofetch] [crosscompany]
[forcelitterals | forceplaceholders] [forcenestedloop]
[forceselectorder]
[repeatableRead] [validTimeState]
[ * | <fieldlist> from] <tablebuffer>
[ index [hint] <indexname> ]
[ group by {<field>} ]
[ order by {<field> [asc][desc]} ]
[ where <expression> ]
[ outer | exists | notexists ] join [reverse]
[ * | <fieldlist> from] <tablebuffer>
[ index <indexname> ]
[sum] [avg] [minof] [maxof] [count]
[ group by {<field>} ]
[ order by {<field> [asc][desc]} ]
[ where <expression> ]
]
<fieldlist> ::= <field> | <fieldlist> , <field>
<field> ::= fieldname | <function>(<field>)

General Optimization
To optimize general performance, the following tools and keywords may be
used.

Fieldlist
One way to optimize communication with the database is to specify which fields
are returned. For example, for a table with 40 fields, reading the information
from only four fields will reduce the amount of data sent from the database server
by up to 90 percent.

2-2

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 2: Working with Data


The following illustrates using a field list.
while select amountMST from ledgerTrans
{
amountMST += ledgerTrans.amountMST;
}

NOTE: Use this optimization with care. If the record returned from the database
is subsequently passed as a parameter to other methods, that method may have
been written on the assumption that all fields are set. Only use field lists when
controlling access to the information locally.

Aggregation
To obtain a sum of records, consider instructing the database to calculate the sum
and only return the result, as an alternative to reading all the records and making
the aggregation yourself. To receive a sum specified for one or more fields in the
table, combine the aggregation with a group by clause. The following
aggregation clauses are available.
Aggregation clause

Description

sum

Returns the sum of the values in a field.

avg

Returns the average of the values in a field.

maxof

Returns the maximum of the values in a field.

minof

Returns the minimum of the values in a field.

count

Returns the number of records that satisfy the statement.

The following illustrates using aggregate functions:


select sum(qty) from inventTrans;
qty = inventTrans.amountMST;
select count(recId) from inventTrans;
countInventTrans = ledgerTrans.recId;

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

2-3

Development III in Microsoft Dynamics AX 2012


Join
To read records in a main table and then process related records in a transaction
table for each main table record, one solution is to make one "while select"
statement which loops over the records in the main table and another nested
"while select" statement which loops over the transaction records related to the
current record in the main table. The following is an example of a nested "while
select" statement
while select InventTable
{
while select InventTrans
where InventTrans.itemId == inventTable.itemId
{
qty += inventTrans.qty;
}
}

To process 500 records in the main table, this approach would have 501 SQL
statements executed on the database.
Alternatively, making a single "while select" statement with a join clause reduces
the number of SQL statements to just 1.
The following example illustrates using a join clause (and fieldlists for extra
performance).
while select recId from inventTable
join qty from inventTrans
where inventTrans.itemId == inventTable.itemId
{
qty += inventTrans.qty;
}

ForceLiterals
ForceLiterals instructs the kernel to reveal the actual values used in the "where"
clauses to the database server at the time of optimization. This is the default
behavior in all "join" statements involving more than one table from the
following table groups:

2-4

Miscellaneous

Main

Transaction

Worksheet Header

Worksheet Line

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 2: Working with Data


The advantage of using this keyword is that the server now gets all information
needed to calculate the optimal access plan for a statement. The disadvantage is
that the access plan cannot be reused with other search values and that the
optimization may use more CPU resources on the database server. High
frequency queries should not use literals.
The following X++ statement is an example of how to use this keyword.
static void DemoForceLiterals()
{
InventTrans inventTrans;
;
while select forceliterals inventTrans
order by itemId
where inventTrans.DatePhysical >= mkdate(21,12,2012)
{
}
}

It is not possible to determine whether an index on itemId or an index on


DatePhysical should be used without considering the actual value of 21\12\2012.
Therefore, the keyword should be used as shown in the previous code sample.

ForcePlaceholders
ForcePlaceholders instructs the kernel not to reveal the actual values used in
where clauses to the database server at the time of optimization. This is the
default in all non-join statements. The advantage of using this keyword is that the
kernel can reuse the access plan for other similar statements with other search
values. The disadvantage is that the access plan is computed without considering
that data distribution might not be even, or that the access plan is an "on average"
access plan.
The following X++ statement is an example of when to use this keyword.
static void DemoForcePlaceholders()
{
SalesTable salesTable;
SalesLine salesLine;
;
while select forcePlaceholders salesLine
join salesTable
where salesTable.SalesId == salesLine.SalesId
&& salesTable.SalesId == '10'
{
}
}

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

2-5

Development III in Microsoft Dynamics AX 2012


In the previous code example, the database server automatically chooses to
search the SalesTable using an index on salesId. The database server uses the fact
that the salesId column is a unique field and does not need the actual search value
to compute the optimal access plan.

FirstFast
FirstFast instructs the SQL-database to prioritize fetching the first few rows fast
over fetching the complete result set. This also means that the SQL-database
might select an index fitting the order by clause over an index fitting the "where"
clause. The FirstFast hint is automatically issued from all forms, but is rarely
used directly from X++.

Firstonly
When Microsoft Dynamics AX fetches data from the database, it transfers a
package of records in each fetch. This is called read-ahead caching and is
performed to minimize calls to the database. If it is known that only one record
will be fetched, you can disable the read-ahead caching with this qualifier.
NOTE: It is best practice to use this in the "find" methods on the tables.
The following example illustrates the use of FirstOnly.
static CustTable findCustTable(CustAccount _custAccount)
{
CustTable custTable;
;
select firstonly custTable
where custTable.AccountNum == _custAccount;
return custTable;
}

NOTE: It is important that "find" methods are designed to be executed on the


tier where the method is called. This is the default behavior on static table
methods. If the method is bound on the server it will always require a round-trip
to the AOS even if the data was cached on the client.

Access Plan Repair


The following keywords are categorized as access plan repair keywords and
should not be used unless required to fix specific performance problems.

Index or Index Hint Keywords


This keyword can be used either in the form index <indexName> or index hint
<indexName>. If only index is used, Microsoft Dynamics AX generates an
order by clause that corresponds to the index components.

2-6

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 2: Working with Data


An incorrectly used index hint can affect performance, so index hints should
only be applied to SQL statements that do not have dynamic where or order by
clauses and where the effect of the hint can be verified.
Microsoft Dynamics AX automatically removes index hints referring to a
disabled index.
By default index hints are disabled on the AOS.

ForceSelectOrder
This keyword forces the database server to access the tables in a join in the given
order. If two tables are joined the first table in the statement is always accessed
first. This keyword is frequently combined with the forceNestedLoop keyword.
One situation where it can be interesting to force a select order is when you use
index hint on a join. The following construction is an example of the
ForceSelectOrder.
static void DemoForceSelectOrder()
{
InventTrans inventTrans;
InventDim inventDim;
;
while select inventTrans
index hint ItemIdx
where inventTrans.ItemId == 'X'
join inventDim
where inventDim.inventDimId ==
inventTrans.inventDimId
&& inventDim.inventBatchId == 'Y'
{
}

Give the database a hint on using the index ItemIdx on the table InventTrans.
This works well if the database searches this table first. But if the database, with
the help of the generated statistics, starts with the table InventDim and then finds
records in InventTrans for each occurrence of InventDimId, the use of the
index ItemIdx may not be an appropriate approach. To hint indexes in a join,
consider specifying forceSelectOrder, as shown in the following example.
static void DemoForceSelectOrder()
{
InventTrans inventTrans;
InventDim inventDim;
;
while select forceSelectOrder inventTrans
index hint ItemIdx
where inventTrans.ItemId == 'X'
join inventDim

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

2-7

Development III in Microsoft Dynamics AX 2012


where inventDim.inventDimId ==
inventTrans.inventDimId
&& inventDim.inventBatchId == 'Y'
{
}
}

ForceNestedLoops
This keyword forces the database server to use a nested-loop algorithm to process
a given SQL statement that contains a join. This means that a record from the
first table is fetched before trying to fetch any records from the second table.
Generally other join algorithms like hash-joins, merge-joins, and others are also
considered. This keyword is frequently combined with the forceSelectOrder
keyword.
Review the previous example with the tables InventTrans and InventDim. You
could risk that the database finds all InventTrans records by the index ItemIdx
and all the InventDim records by the BatchId. (If you hint the index DimIdIdx
this will be used for this search.) The two collections of records are hashed
together. For the database to find the inventTrans and then the inventDim for
each inventTrans, specify forceNestedLoops, as shown in the following
example.
static void DemoForceSelectOrder()
{
InventTrans inventTrans;
InventDim inventDim;
;
while select forceSelectOrder forceNestedLoop
inventTrans
index hint ItemIdx
where inventTrans.ItemId == 'X'
join inventDim
where inventDim.inventDimId ==
inventTrans.inventDimId
&& inventDim.inventBatchId == 'Y'
{
}

2-8

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 2: Working with Data


Cross Company
By default, select statements in X++ adhere to the DataAreaId structure of the
application. For example, if there are two company accounts, 001 and 002, a
select statement run in company 002 will only return records stamped with
dataAreaId equal to 002. The crossCompany keyword allows the statement to
fetch records regardless of the dataAreaId. The following example shows a
statement that will count all customer records from all company accounts.
select crossCompany count(recId) from custTable;
countCustTable = custTable.recId;

You also have the option of adding a container variable of company identifiers
immediately after the crosscompany keyword (separated by a colon). The
container restricts the selected rows to those with a dataAreaId that match a value
in the container. The following example illustrates the use of the crossCompany
keyword.
CustTable custTable;
container conCompanies = [ '001', '002', 'dat' ];
;
while select crosscompany : conCompanies * from custTable
order by dataAreaId
{
//do something
}

Query
A query is an object-oriented interface to the SQL database. A query is composed
of objects from different classes.
Various objects are available to manipulate a query object from the AOT.
Use the queryRun object to execute the query and fetch data.
The query object is the definition master. It has its own properties and has one or
more related data sources.
The queryBuildDataSource defines access to a single table in the query. If one
data source is added below another data source, they form a join between the two
tables.
The queryBuildFieldList object defines which fields to fetch from the database.
The default is a dynamic field list that is equal to a "select * from ". Each data
source has only one queryBuildFieldList object which contains information
about all selected fields. You can also specify aggregate functions like sum,
count, and avg with the field list object.

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

2-9

Development III in Microsoft Dynamics AX 2012


The queryBuildRange object contains a limitation of the query on a single field.
The queryFilter object is used to filter the result set of an outer join. It filters the
data at a later stage than the queryBuildRange object and filters the parent table
based on the child table results.
The queryBuildDynaLink objects can only exist on the outer data source of a
query. The objects contain information about a relation to an external record.
When the query is run, this information is converted to additional entries in the
"where" section of the SQL statement. The function is used by forms when two
data sources are synchronized. The subordinate data source contains DynaLink(s)
to the master data source. The function is used even if the two data sources are
placed in two different forms, but are synchronized.
The queryBuildLink objects can only exist on inner data sources. The objects
specify the relation between the two tables in the join.
The following is a sample of code using some of these QueryBuild objects, to
build a query and loop through it:
Query
q;
QueryRun
qr;
QueryBuildDatasource
QueryBuildRange
;

qbds1, qbds2;
qbr;

q
= new Query();
qbds1
= q.addDataSource(tablenum(InventTable));
qbds2
= qbds1.addDataSource(tablenum(InventTrans));
qbds2.relations(TRUE); //this enforces a relationship
between this datasource and its parent. Relationships
defined in the Data Dictionary are used by default.
qbr
= qbds1.addRange(fieldnum(InventTable, ItemId));
qbr.value(SysQuery::value("1706")); //SysQuery object
provides various static methods to assist in defining Query
criteria. The SysQuery::value() method should always be
used when defining a singular value for a range.
qr = new QueryRun(q);
while(qr.next())
{
//do something
}

You can build a query in the AOT using MorphX or as shown in the previous
topic, by dynamically creating the query by X++ code. Both approaches are used
in the standard application. One advantage of making the query dynamic is that it
is not public in the AOT and is protected against unintentional AOT changes.
Alternatively, an advantage of creating the query in the AOT is that it can be
reused in various places, saving lines of identical code, and making widereaching query adjustment easier.

2-10

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 2: Working with Data


Forms and reports use queries for fetching data. These queries are not public
below the Query-node in the AOT. Forms generate the queries dynamically from
the setting of the data source, whereas Reports have an integrated query-node. In
both cases, you can edit the query for modification.
There are alternative ways of specifying existing objects in a query. Best practice
is to use the intrinsic functions which use table and field ID as arguments
(tableNum and fieldNum).

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

2-11

Development III in Microsoft Dynamics AX 2012

Lab 2.1 - Fetching Data


Scenario
As part of a larger modification, you need to develop code which queries the
database for specific records. Use AOT queries, "while select" statements, and
query objects to develop the most efficient data fetching code.

Challenge Yourself!
Make the following query "Count the number of customers from all companies,
limited to a specific currency." For this example, make USD the limiting value.
Perform the following actions:
1. Create a job which makes the above select using "while select". The
currency to use can be defined in the code.
2. Create a new query in the AOT. Prompt the user for the currency to
use when the query is run.
3. Create a job which executes the query defined in step 2.
4. Create a new job which builds the same query dynamically using the
query classes. Prompt the user for the currency to use when the query
is run.
5. Verify that the three implementations return the same data.

Step by Step
1. The following shows query made in a job using "while select".
static void SelectCustomerJob(Args _args)
{
CustTable custTable;
Counter recordsFound;
;
while select crosscompany custTable
where custTable.Currency =="USD"
{
recordsFound++;
}
info(strFmt("Customers found: %1", recordsFound));
}

2-12

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 2: Working with Data


2. The following shows a query built in AOT:

FIGURE 2.1

3. The following shows the job that executes query from step 2.
static void SelectCustomerRunQuery(Args _args)
{
QueryRun queryRun;
Counter recordsFound;
;
queryRun = new QueryRun(queryStr(CustTableByCurrency));

if (queryRun.prompt())
{
while (queryRun.next())
{
recordsFound++;
}
}
info(strFmt("Customers found: %1", recordsFound));

4. The following shows the job that dynamically builds the query
static void SelectCustomerQuery(Args _args)
{
Query query;
QueryBuildDataSource queryBuildDatasource;
QueryFilter queryFilter;
QueryRun queryRun;
Counter recordsFound;
;
query = new Query();
query.allowCrossCompany(TRUE);
queryBuildDataSource = query.addDataSource(tableNum(
CustTable));
queryFilter =
query.addQueryFilter(queryBuildDataSource,

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

2-13

Development III in Microsoft Dynamics AX 2012


identifierStr(Currency));
queryRun = new QueryRun(query);
if (queryRun.prompt())
{
while (queryRun.next())
{
recordsFound++;
}
}
info(strFmt("Customers found: %1", recordsFound));
}

2-14

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 2: Working with Data

Lab 2.2 - Converting Queries


Scenario
Your development manager has asked that you re-write all your queries using
Query objects instead of "while select" statements.
Make a job that dynamically builds a query, using query objects, with the same
structure as the following statement.
inventTrans

inventTrans;

inventDim inventDim;

while select sum(qty) from inventTrans


where inventTrans.ItemId =="1706"
join inventDim
group by inventBatchId
where inventDim.InventDimId ==
inventTrans.InventDimId
{
info(strFmt("Batch %1, qty %2",
inventDim.inventBatchId, inventTrans.qty));
}

Enable the user to enter the item id when the query is run.
Perform the following actions:
1. Create a new job.
2. Use the Query, QueryBuildDataSource classes, and more, to create a
query dynamically.
3. Create another job that uses "while select" as shown.
4. Verify that the two implementations return the same data, using item
number 1706.

Step by Step
1.
2.
3.
4.
5.
6.

In the AOT, create a new job.


Copy the code above in to the job.
Press F5 to run the code
Note the result.
Create another new job.
Add the following code.

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

2-15

Development III in Microsoft Dynamics AX 2012


static void InventTransBuildQuery(Args _args)
{
Query query;
QueryBuildDataSource queryBuildDataSource;
QueryBuildRange queryBuildRange;
QueryRun queryRun;
Qty total;
InventTrans inventTrans;
inventDim inventDim;
query = new Query();
queryBuildDataSource =
query.addDataSource(tableNum(InventTrans));
queryBuildDataSource.addSelectionField(fieldNum(InventTrans
,Qty),SelectionField::Sum);
queryBuildRange =
queryBuildDataSource.addRange(fieldNum(InventTrans,
ItemId));
queryBuildDataSource =
queryBuildDataSource.addDataSource(tableNum(InventDim));
queryBuildDataSource.addGroupByField(fieldNum(InventDim,
InventBatchId));
queryBuildDataSource.relations(true);
queryRun = new QueryRun(query);
if (queryRun.prompt())
{
while (queryRun.next())
{
inventTrans =
queryRun.get(tableNum(InventTrans));
inventDim = queryRun.get(tableNum(inventDim));
info(strFmt("Batch %1, qty %2",
inventDim.inventBatchId, inventTrans.qty));
}

7. Press F5 to run the job.


8. In the prompt, enter 1706 in the Item number field
9. Note the result.

2-16

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 2: Working with Data

Caching
The previous sections have discussed how to optimize communication with the
database by structuring SQL statements that make the database access data in the
correct way. But the most optimal strategy is to have no communication with the
database or to minimize the number of communications with the database server.
The concept of caching is to remember the information retrieved from the
database and use this memory when the same data is needed at a later time.
However, this strategy has one large drawback, if the remembered information is
no longer valid, this could compromise the consistency of the database, as the
updates will be made based on invalid data.
This section covers data caching done by Microsoft Dynamics AX AOS and/or
client. Microsoft Dynamics AX supports the following kinds of caching:

Read-ahead caching

Single-record caching

Entire table caching

Record view caching

Display method caching

Global object cache

Date effective join cache

Unique index cache extension

Cache lookup for table groups

The remainder of this section explains the first five above mentioned data
catching.

Read-ahead Caching
The SQL interface in Microsoft Dynamics AX provides read-ahead caching and
uses a buffer to pre-fetch rows. The read-ahead adapts to the queries executed.
As an example, a query to show rows in a form pre-fetches in chunks that
correspond to the number of rows currently visible in the form, whereas a multirow select with the FirstOnly keyword will not pre-fetch.

Single-Record Caching
Microsoft Dynamics AX offers record caching. Rows selected through a cachekey are cached and successive look-ups specifying the cache-key are then served
from the record cache. The record-cache holds the rows that match frequently
issued single-row queries.
The cache-key is the Primary Key on the cached table. If a Primary Key does not
exist, the first unique index is used.

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

2-17

Development III in Microsoft Dynamics AX 2012


Because only single-row queries go into the cache, the cache is small with a
default value of 100 rows on the client and 2000 rows on the AOS.
Four single record cache settings are used with tables:
CacheLookup

Result

None

No data is cached or retrieved from cache for this table.


No caching should be used for tables that are heavily
updated or where it is unacceptable to read outdated
data.

NotInTTS

All successful caching key selects are cached.


When in TTS (after ttsbegin), the record is read once
from the database and subsequently from cache. The
record is select-locked when read in TTS which ensures
that the record cached is not updated as long as the TTS
is active.
A typical example of its use is the CustTable in the
standard application. It is acceptable to read outdated
data from cache outside TTS, but when data is used for
validation or creating references it is ensured that it is
real-time data.

Found

All successful caching key selects are cached. All


caching key selects are returned from cache if the record
exists there. A select forupdate in TTS forces reading
from the database and replaces the record in the cache.
This is typically used for static tables like ZipCodes
where the normal case is that the record exists.

FoundAndEmpty

All selects on caching keys are cached, even selects not


returning data.
All caching key selects are returned from caching if the
record exists there or the record is marked as nonexisting in the cache.
An example of this kind of caching is found in the
InventTxt table in the standard application. For this
table it is a normal case that no record exists. Therefore,
in this case it is of great value to register the nonexisting keys.

Important features about single-record caching are as follows:

2-18

The caching only works when selecting exactly one record, with a
distinct where on the primary key.

The cache will not support fetch of multiple records in a query or a


"while select".

The cache does not support data retrieved by joining more than one
table.

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 2: Working with Data


The cache on the client is not updated with changes done from other clients. This
means that one client can cache and use invalid information.
The single record caching is selected by the CacheLookup property setting on the
table.
You can use the AOT find tool to list some examples of this setting.

Entire Table Caching


The entire table caching loads all the records in the table the first time one record
is fetched. There is no limitation on the number of records cached.
An individual cache is maintained for each data area in Microsoft Dynamics AX.
The AOS holds the cache. This is shared between all clients connected to the
AOS. The client supplements this AOS caching with a FoundAndEmpty single
record caching if possible.
The entire table caching is not used when making a join with other tables which
is not entire table cached.

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

2-19

Development III in Microsoft Dynamics AX 2012


The entire table caching is selected by the CacheLookup property setting on the
table. The individual users can disable the caching clicking Tools > Options >
Preload. This form also provides an overview of which tables are configured by
using entire table caching.

FIGURE 2.2 PRELOAD TABLES

Record View Caching


Use record view caching to cache the records from a select statement (a result
set). Result set caching is made available through the RecordViewCache class.
The cache is instantiated using a select with the nofetch option. If the
instantiating select is a join, a temporary table or is instantiated without the
nofetch option, the cache is not instantiated and an error is reported. The select
clause of the select defines the result set and only "==" predicates are accepted.

2-20

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 2: Working with Data


The following example illustrates the instantiating of a record view cache:
SalesLine salesLine;
RecordViewCache salesLineCache;
;
select noFetch salesLine
where salesLine.SalesId =="100";
salesLineCache = new recordViewCache(salesLine);

The cache is not shared between Microsoft Dynamics AX clients, and is


deactivated as soon as the cache object goes out of scope or is destroyed.
The cache is used when a select is from the same table, not a join and at least
matches the where clause that the cache was instantiated with.
If the table is not set up for Optimistic concurrency then using the forupdate
option on the select within TTS results in database locks on all the rows that are
put into the cache. A subsequent select forupdate within the same TTS is
selected from the cache and not from the database as the row is already locked.
Updates and deletes that match the instantiating where clause simultaneously
maintain the database and the cache. Updates and deletes are not in the above
category do not affect the result-set.
The reread method retrieves data from the database and updates the copy data.
The cache can be instantiated using a select without a where clause. This
effectively creates a copy of the table in memory and functions like a personal
table copy.
The cache is useful for caching tables that are not of static nature, or that contain
so many records that the other caching methods would be impractical.
Because of concurrency issues, the forupdate keyword on the instantiating X++
select should only be used when all the records in the result set will be updated.
Otherwise, it is a better strategy to select forupdate the records that are updated
or use Optimistic concurrency on the tables.
Do not use field-lists unless you are certain that the omitted fields will never be
used. There is no warning or error when selecting fields from the cache that were
not included in the select that instantiated the result set.
Inserts normally do not affect the cache even when the record would match the
where clause defining the result-set.
When you use this form of caching, consider the memory consumption, as there
is no internal limitation to the memory allocated for a single table. Different
application behavior, with regard to exploiting caching when it is running on
server versus client, could be an option.

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

2-21

Development III in Microsoft Dynamics AX 2012


One example of using record view caching is the class InventMovement. This
holds information about inventory transactions related to one SalesLine,
PurchLine and more. The cache is defined as a RecordViewCache object in
\Classes\InventMovement\classDeclaration.
The cache is initialized by the method
\Data\Dictionary\Tables\InventTrans\Methods\viewCacheInventTransOrigin
public static RecordViewCache
viewCacheInventTransOrigin(InventTransOriginId
_inventTransOriginId, boolean _forupdate = false)
{
InventTrans inventTrans;
;
inventTrans.selectForUpdate(_forupdate);
select nofetch inventTrans where
inventTrans.InventTransOrigin == _inventTransOriginId;
return new RecordViewCache(inventTrans);
}

According to the cross reference, this method is called from


\Classes\InventMovement\viewCacheInventTransId.
void viewCacheInventTransOrigin()
{
if (viewCacheInventTrans)
return;
viewCacheInventTrans
= null;
viewCacheInventTrans
=
InventTrans::viewCacheInventTransOrigin(this.InventTransOri
ginId(),true);
}

Display Method Caching


Caching display methods improves the performance of display functions if they
are calculated on the AOS and improves performance when records are
transferred from the server to the client.
Only methods that are explicitly added to the cache are affected by the new
caching mechanism. To sign up a method for caching, the method
cacheAddMethod on the form data source should be called after super() in the
init method of the data source.

2-22

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 2: Working with Data


The call to cacheAddMethod also determines how frequently the cached display
method value is updated. The value is filled in upon fetching data from the backend, and it is refreshed when reread is called on the data source. Additionally, by
default, the display method values are also updated when the record is written to
the database. But that can be changed using the _updateOnWrite parameter in the
cacheAddMethod.
Only display methods that are of display type can be cached. Edit methods
cannot be cached. Only display methods placed on the table, can be cached.
One example of the initialization is found in \Forms\PurchTable\Data
Sources\PurchLine\Methods\init.
purchLine_ds.cacheAddMethod(tablemethodstr(PurchLine,
receivedInTotal));
purchLine_ds.cacheAddMethod(tablemethodstr(PurchLine,
invoicedInTotal));
purchLine_ds.cacheAddMethod(tablemethodstr(PurchLine,
itemName));
purchLine_ds.cacheAddMethod(tablemethodstr(PurchLine,
displayBudgetCheckResult));
purchLine_ds.cacheAddMethod(tablemethodstr(PurchLine,
calcPendingQtyPurchDisplay));

Display methods not based on cached data, are candidates for display method
caching.

Locking
So far you have seen that the database server can access data faster when the
number of requests to the database is minimized, by using efficient queries and
caching.
Another issue is if multiple users want to lock the same record at the same time.
Only one user can lock the record, and the rest wait in a queue until the record is
released. This has a substantial effect on the time used to perform a function.
Locks are typically done in transactions where more than one record is locked at
a time. The following example illustrates two different processes which lock
different items:
Process 1

Process 2

Item C - Locked

Item A - Locked

Item B - Locked

Item G - Locked

Item G - Waiting for lock from


Process 2

Item A - Waiting for lock from


Process 1

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

2-23

Development III in Microsoft Dynamics AX 2012


This situation is called a dead lock; one of the transactions must be aborted to
resolve the issue.
Locking is the biggest issue with scalability, which means that many users are
working with the same functions and data.
To reduce locking, consider using Optimistic Concurrency Control (OCC). When
the risk of overlapping transactions modifying the same data is small, optimistic
locking can increase concurrency and thus transaction throughput. Enable OCC
for a table by setting the property OccEnabled to Yes.
NOTE: Only a few tables in the standard application do not use Optimistic
Concurrency Control.
Strategies to use to avoid or reduce the locking problem are as follows:

Keep the transactions as short in time as possible. Do not


compromise the integrity of the data.

Try to avoid locking central resources.

Try to lock central resources in the same order. This avoids the dead
lock situation discussed previously. This serializes the process,
which means that only one user can perform this at a time.

Never implement dialog with the user inside a transaction.

Locking is held for the time it takes to finish the process. If the process has poor
performance because of missing optimization of AOS and database
communication, the locking is a bigger issue and needs to be addressed first.

2-24

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 2: Working with Data

Lab 2.3 - Reducing Locking


Scenario
Some users have been reporting performance issues and deadlock messages,
when using some of your modifications. You have been given the task of
optimizing your code, to reduce any locking.

Challenge Yourself!
Modify the following job to reduce locking:
InventTable inventTable;
VendTable vendTable;
;
ttsbegin;
while select forupdate inventTable
{
if
(VendTable::find(inventTable.PrimaryVendorId).Blocked
== CustVendorBlocked::All)
{
inventTable.PrimaryVendorId ="";
inventTable.update();
}
}
ttscommit;

Perform the following actions:


1. Create a new job with the above contents.
2. Try to reduce the locking.
3. Try to redesign the code to obtain a better performance.

Step by Step
Solution 1: A new InventTable table variable is declared. Updates are done
against this variable, so that you do not need to lock the whole InventTable
forupdate.
static void Locking_Solution_1(Args _args)
{
InventTable inventTable;
InventTable inventTableUpdate;
VendTable vendTable;
;
ttsbegin;
// solution #1

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

2-25

Development III in Microsoft Dynamics AX 2012


while select inventTable
{
if
(VendTable::find(InventTable.PrimaryVendorId).Blocked ==
CustVendorBlocked::All)
{
inventTableUpdate =
InventTable::find(inventTable.ItemId, true);
inventTableUpdate.PrimaryVendorId ="";
inventTableUpdate.update();
}
}
ttscommit;
}

Solution 2: If and find on VendTable are replaced with exist join on VendTable.
static void Locking_Solution_2(Args _args)
{
InventTable inventTable;
VendTable vendTable;
;
ttsbegin;
// solution #2
while select forupdate inventTable
exists join vendTable
where vendTable.AccountNum ==
inventTable.PrimaryVendorId &&
vendTable.Blocked == CustVendorBlocked::All
{
inventTable.PrimaryVendorId ="";
inventTable.update();
}
ttscommit;
}

2-26

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 2: Working with Data

Temporary Tables
A temporary table is defined in the AOT as a normal table, but with the
TableType property set to either InMemory or TempDB. Data is not persisted in
a temporary table, and only remains while the table is in scope. If TableType is
set to InMemory, the data is stored in memory or in a temporary file if memory is
full. TempDb means it is stored in a table in SQL, and can be joined to regular
tables at the database tier.
A typical example is a class which initializes a temporary table and inserts some
records, which should be shown in a form. The class has a variable _tmpTable
which holds the data and the form has a data source tmpTable_DS which should
show the same data. This is solved by using the method
tmptable_DS.setTmpData(_tmpTable). This disregards the individual file
allocated to tmptable_DS. Now, both _tmpTable and tmptable_DS will access
the same pool of data, as if they were normal table variables. The allocated file
now shared between _tmpTable and tmptable_DS is deleted once both variables
have gone out of scope.
Instead of making a dedicated definition of a temporary table in the AOT,
consider making a temporary instance of a database table (in other words, a nontemporary table which is part of the SQL database). Do this using the .setTmp()
method before accessing the variable. Be careful with this option, as you can
activate other methods which act as if it is real database data and cause
subsequent updates in other tables. See the following example, which copies all
customers from Australia to a temporary table.
static void CopyPersistedTableToTemp(Args _args)
{
CustTable custTable;
CustTable tmpCustTable;
;
tmpCustTable.setTmp();
while select custTable
where custTable.CountryRegionId == "AU"
{
tmpCustTable.data(custTable.data());
tmpCustTable.doInsert();
}
}

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

2-27

Development III in Microsoft Dynamics AX 2012

Lab 2.4 - Temporary Tables


Scenario
As part of a larger modification, you need to implement some temporary tables,
to achieve the required outcome.

Challenge Yourself!
Make a job which works with two individual instances of temporary data (for
example, TmpSum).
Perform the following steps:
1. Make a job with two variables A and B of type TmpSum.
2. Insert some records in variable A. Does this have an effect on the
contents of variable B?
3. Copy all records from A to B. Verify that the contents of the two
tables are equal.
4. Delete one record from A. Does this have an effect on the contents of
variable B?
5. Call the B.setTempData(A) method. Repeat step 2 and 4.

Step by Step
If you declare two temporary table variables A and B, inserts, updates, and
deletes on table A do not have impact on B. This only happens if the method
variableB.setTempData(variableA) is set before updating.
1. Open the AOT create a new job.
2. Copy the following code in to the job.
3. Read through the code carefully, and try to understand what each
line is doing and what the result will be.
4. Press F5 to run the job. Verify the results are what you expected.
static void Temporary(Args _args)
{
TmpSum a;
TmpSum b;
Counter counter;
void countRecords(Tmpsum _tmptable, str _step, str
_name)
{
select count(RecId) from _tmptable;
info(strFmt("Step %1 - %2 records in %3", _step,
_tmptable.RecId, _name));
}
;
ttsbegin;

2-28

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 2: Working with Data


// Insert records in 'a'
for (counter = 1; counter<= 1000; counter++)
{
a.Key = num2Str(counter,1,0,0,0);
a.Balance01 = counter;
a.insert();
}
// Verify that inserts in 'a' do not affect 'b'
countRecords(a,"2","a");
countRecords(b,"2","b");
// Copy all records from 'a' to 'b'
b.skipDataMethods(true);
insert_recordSet b (Key, Balance01)
select Key, Balance01 from a;
countRecords(a,"3","a");
countRecords(b,"3","b");
// Delete one record from 'a'
select forupdate a;
a.delete();
/* Verify that the contents of the two tables are not
equal,
delete from a do not effect b */
countRecords(a, "4","a");
countRecords(b, "4","b");
// Call setTmpData method
b.setTmpData(a);
// Insert 10 new records in a
for (counter = 1001; counter<= 1010; counter++)
{
a.Key = num2Str(counter,1,0,0,0);
a.Balance01 = counter;
a.insert();
}
equal,

/* Verify that the contents of the two tabels are


inserts in 'a' effect 'b' */
countRecords(a,"5a","a");
countRecords(b,"5b","b");
// Delete one record from a
select forupdate a;
a.delete();

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

2-29

Development III in Microsoft Dynamics AX 2012


equal,

/* Verify that the contents of the two tabels are


delete from 'a' effect 'b' */
countRecords(a,"5b","a");
countRecords(b,"5b","b");
ttscommit;

2-30

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 2: Working with Data

InitFrom
Often, a record has default values for fields initialized with data copied from
another table. For example, when a sales order is created and a customer account
is specified, fields including Invoice Account, Delivery Address, Tax Group, and
Currency are copied from the CustTable record to the SalesTable. This is done
in the SalesTable.InitFromCustTable() method which contains a list of field
assignments between the two tables. This is a common technique of initializing
fields in one table from another table, and is always the first place to analyze.
For example, to add the new field "Fragile" to the BOM table that defaults from
another new field "Fragile" in the InventTable, consider adding the following
line to BOM.InitFromInventTable():
this.fragile = inventTable.fragile;

Parm Tables
When sales orders, purchase orders, or production orders are updated to a new
status, Microsoft Dynamics AX uses Parm tables to store what will be updated.
This allows the user to view and modify what will be updated without affecting
the original order. It also enables the update to be processed by the batch system.

FIGURE 2.3 SALESEDITLINES FORM

The user can view and edit the Parm tables in the SalesEditLines or
PurchEditLines forms. In the previous figure, the SalesParmLine table appears
on the Lines tab, and the SalesParmTable appears on the SalesOrders tab.

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

2-31

Development III in Microsoft Dynamics AX 2012


To use the Parm tables, a ParmId is created. This is a sequential number taken
from the System ID number sequence set up on the Company Info table. The
ParmId ensures the tables are linked correctly using a unique identifier for each
update - one order can be updated multiple times.
The table records are then created using data from the appropriate tables. For
example, PurchParmTable is initialized using data from PurchTable, and
PurchParmLine is initialized using data from PurchLine.
Note the use of InitFrom methods on most of these tables.
In the SalesFormLetter.CreateParmLine() method the SalesParmLine record
is set from SalesLine table by use of the SalesParmLine.initFromSalesLine()
method.

Date Effectiveness
Date effectiveness allows the application to associate valid from and to dates
with the application artifacts. For example, an agreement can be valid between a
range of dates. Similarly, interest rates are assigned based on start date and end
date association.
The date effectiveness feature is a central framework that is supported in all
framework components. It helps developers write less code, to develop more
consistent code, and to create forms that manage viewing and editing of current,
past, and future records.

Scenario: Create a Date Effective Table


Isaac, the Developer, wants to build a table to hold descriptions of an item that
are only valid for a specified period of time. There must always be one and only
one description so no gaps or overlaps are allowed

Procedure: Create a Date Effective Table


Perform the following steps to create a table that is enabled for date effectiveness
and does not enable date gaps.
1.
2.
3.
4.
5.
6.

Open the AOT.


Create a project.
Create a new Table.
Right-click the table and select Properties.
Set the name property to ItemDescEffective.
Drag Extended Data Type ItemId to the Fields node on the new
table.
7. Click Yes to create the Foreign Key relation.
8. Drag the Extended Data Type Name to the Fields node on the new
table.
9. Enter Date in the ValidTimeStateFieldType property.
2-32

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 2: Working with Data


10.
11.
12.
13.
14.
15.
16.
17.
18.
19.

Verify the ValidFrom and ValidTo date fields are created.


Right-click the Indexes node and select New Index.
Rename the index to ItemDateIdx.
Drag the date fields and the ItemId field to the new index node.
Right-click the new index and select Properties.
Set AllowDuplicates to No.
Set AlternateKey and ValidTimeStateKey to "Yes".
Set ValidTimeStateMode to NoGap.
Save the table.
If the compiler reports an error, right-click the table and select
Compile. If it still reports an error, click reset on the compiler form
and compile the table again.

Procedure: Demonstrate Date Gap


Perform the following steps to demonstrate the date effectiveness and make sure
the table does not enable date gaps.
1. Open the AOT.
2. Select the table that is created in the "Create a Date Effective Table"
procedure.
3. Right-click the table and select Open.
4. Press Ctrl-N to create a new record.
5. Select an item, enter an ItemName and enter a ValidFrom date and
ValidTo date.
6. Press Ctrl-N to create another new record.
7. Enter a second record for the same item but with a ValidFrom date
and ValidTo date starting some days after the ValidTo date of the
first record.
8. Click Yes and accept the adjustment to the effective dates.
9. Verify the system changed the first record and adjusts the ValidTo
date to fill the gap between the two records.
10. Press Ctrl+F4 to close the table browser.

Procedure: Demonstrate Date Overlap


To demonstrate the date effectiveness and to make sure that the table does not
enable date overlaps, follow these steps:
1.
2.
3.
4.

Open the AOT.


Select the table created in the previous procedure.
Right-click the table and select Open to open the table.
Press CTRL+N to create a third record for the same item as in the
previous procedure but with a ValidFrom date and ValidTo date
overlapping the second records time frame.

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

2-33

Development III in Microsoft Dynamics AX 2012


5. Click Yes and accept the adjustment to the effective dates.
6. Verify the system changes the second record and adjusts the
ValidTo date to resolve the date overlap.
7. Press Ctrl+F4 to close the table browser.

Selecting Date Effective Data


When selecting data in X++, a keyword validTimeState can be used to specify
dates so that the data returned is valid for those dates. There can be one or two
dates specified. If one is specified, all records that are valid on that date are
returned. If two are specified, all records that are valid within the date range are
returned. The two options are shown in the following example:
select validTimeState(asAtDate) from ExchangeRates;
select validTimeState(fromDate, toDate) from ExchangeRates;

Computed Columns in Views


A view is a selected number of fields that come from one or more related tables.
Microsoft Dynamics AX can also display a computed column based on fields in
those tables. A computed column generates T-SQL code at compile time and
adds it to the T-SQL code generated by the view, which increases performance.

Demonstration: Computed Column


During the following demonstration you examine a computed column in the
standard application.
1. Open the AOT, expand the Data Dictionary > Views node.
2. Find the InventTableExpanded node
3. In the Fields node, find the ProductName field, and open the
properties sheet.
4. Note the property ViewMethod is set to productName. This refers
to a static method on the view.
5. Expand the Methods node on the InventTableExpanded view.
6. Open the productName method. The code in the method is as
follows:
TableName viewName = identifierstr(InventTableExpanded);
str translatedNameField =
SysComputedColumn::returnField(viewName,
identifierstr(EcoResProductTranslations),
fieldstr(EcoResProductTranslations, ProductName));

2-34

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 2: Working with Data


str productDisplayNumberField =
SysComputedColumn::returnField(viewName,
identifierstr(EcoResProduct), fieldstr(EcoResProduct,
DisplayProductNumber));
return SysComputedColumn::if(
SysComputedColumn::isNullExpression(translatedNameField),
productDisplayNumberField,
translatedNameField);

7. The three variables that are declared are used to hold the physical
names of the view and the fields that are to be used.
8. The SysComputedColumn::if() method then converts three
expressions in to the T-SQL expression. By placing the cursor on the
if() method and pressing F12, you can see the method.
9. To view the final T-SQL code that is generated, you need to turn on
SQL-Tracing. Go to Tools > Options > SQL.
10. Check SQL Trace, and then check Multiple SQL Statements >
infolog.
11. Close the user options form.
12. In the AOT, right-click on view InventTableExpanded and select
Synchronize.

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

2-35

Development III in Microsoft Dynamics AX 2012


13. An infolog box is displayed with a number of SQL commands. One
of them is creating the view and is as follows:

FIGURE 2.4 CREATE VIEW

14. Note that one of the fields has the following : ,(CAST ((CASE
WHEN T5.PRODUCTNAME IS NULL THEN
T2.DISPLAYPRODUCTNUMBER ELSE T5.PRODUCTNAME
END) AS NVARCHAR(60))) AS PRODUCTNAME,

Data Integration
External data comes in many different formats. This section describes
mechanisms to read and write data in four different, but commonly used, formats.
With all external interaction in X++, code access permission must be asserted.

IO classes
The standard classes that extend the IO class are useful for importing and
exporting data to and from a simple text file format. Most commonly, these are
TXT or CSV files.

2-36

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 2: Working with Data


The CommaIO and Comma7IO classes are used to import and export comma
separated data.
The TextIO and AsciiIO classes are used to import and export text-based data.
The BinaryIO class is used to import and export binary data.
The following code is an example of the CommaIO class:
FileName fileName = 'c:\\test.csv';
FileIoPermission permission;
CommaIO commaIO;
container readCon;
int i;
int level;
str column1;
str column2;
str column3;
;
permission= new FileIoPermission(filename,'r');
permission.assert();
commaIO = new CommaIo(filename, 'r');
commaIO.inFieldDelimiter(';'); // Delimiter...
if (commaIO)
{
while (commaIO.status() == IO_Status::OK)
{
readCon = commaIO.read();
column1 = conPeek(readCon, 1);
column2 = conPeek(readCon, 2);
column3 = conPeek(readCon, 3);
info(strFmt("%1 - %2 - %3", column1, column2,
column3));
}
}

In this example, a specific field delimiter character has been specified. By


default, the field delimiter character for CommaIO is the comma character, but in
this case the code specifies the semi-colon character as the field delimiter.
The commaIO object is looped over, using a "while" statement, which continues
until the IO_status is no longer OK, which indicates the end of the file.
The CommaIO.read() method returns data from a single record (or row) from the
file, in the form of a container, where each container element is a single field
value. In this example, there are at least three fields per row.

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

2-37

Development III in Microsoft Dynamics AX 2012


FileIO
FileIO is commonly used to export data to a TXT file. There are three different
methods to write text to an FileIO object:

FileIO.write(str) - writes a string.

FileIO.writeExp(container) - writes the contents of a container.


Elements are separated by character defined in
FileIO.outFieldDelimiter().

FileIO.writeChar(int) - writes a unicode character represented by


an integer.

The following is sample code which writes a simple string to a text file:
FileName fileName = 'c:\\test.txt';
FileIoPermission permission;
FileIO fileIO;
str outputText;
#File
;
permission= new FileIoPermission(filename,#io_write);
permission.assert();
fileIO= new FileIO(filename, #io_write);
if (fileIO)
{
outputText = "text that will go into the text file.";
fileIO.write(outputText); //write the text to the
file.
fileIO.finalize();
//finish the file.
}

Creating XML
Extensible Markup Language (XML) is a text-based standard for representing
data and is commonly used for data exchange and business-to-business
communication.
The structure of XML files is similar to HTML in that it uses tags to define
sections of the file. Each tag defines either single or groups of data elements, and
can also have attributes (for example, a date or reference ID) attached to the tag.
The following is an example of a simple XML file:
<note>
<appl>Microsoft Dynamics AX</appl>
<version>2012</version>
<body>This is a simple XML file</body>
</note>

2-38

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 2: Working with Data


Since it is a simple text file, it is possible to create an XML file using Microsoft
Dynamics AX AsciiIO class, and to ensure that all the tags are correctly
formatted and positioned. Be careful though as the tags are case-sensitive.
Alternatively, Microsoft Dynamics AX includes a wrapper for the COM object
microsoft.xmldom, which creates the file using tag names and data.
The following example creates an XML file containing employee names and
addresses:
static void XMLWriteEmplAddr(Args _args)
{
FileIoPermission permission;
XMLDocument xmlDoc = XMLDocument::newBlank();
XMLNode
rootNode;
XMLNode
NodeEmpl, NodeName, NodeAddr;
XMLElement
xmlElement;
XMLText
xmlText;
HCMWorker HCMWorker;
;
permission= new
FileIoPermission('c:\\Empl_Address_List.xml','w');
permission.assert();
xmlDoc

= XMLDocument::newBlank();

// Create first line containing version info


rootNode
= xmlDoc.documentElement();
xmlElement = xmlDoc.createElement(EmployeeList);
rootNode
= xmlDoc.appendChild(xmlElement);
while select EmplTable
{
// Create a node for the Employee record
xmlElement = xmlDoc.createElement(Employee);
NodeEmpl
= rootNode.appendChild(xmlElement);
// Create a node for the name
xmlElement = xmlDoc.createElement(EmplName);
NodeName
= NodeEmpl.appendChild(xmlElement);
xmlText
=
xmlDoc.createTextNode(EmplTable.Name());
NodeName.appendChild(xmlText);
// Create a node for the address
xmlElement = xmlDoc.createElement(EmplAddr);
NodeAddr
= NodeEmpl.appendChild(xmlElement);
xmlText
=
xmlDoc.createTextNode(EmplTable.Address);
NodeAddr.appendChild(xmlText);
}

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

2-39

Development III in Microsoft Dynamics AX 2012

// Save the file


xmldoc.save('c:\\Empl_Address_List.xml');

Reading XML
XML files can be read in an easy way or a hard way. The hard way is to read the
file line by line and determine the correct tag each time.
The easy way is to let the microsoft.xmldom COM object do the work. The
following example reads an XML file containing customer account number and
transaction details.
static void XMLReadCustTrans(Args _args)
{
FileIoPermission permission;
XMLDocument
doc;
XMLNode
rootNode, AccNode, NameNode, transNode;
XMLNodeList
custTransList;
XMLParseError xmlError;
int
i;
;
permission= new
FileIoPermission("C:\\CustTrans.xml",'r');
permission.assert();
// Get the XML document
doc = new XMLDocument();
doc.load("C:\\CustTrans.xml");
xmlError = doc.parseError();
if (xmlError && xmlError.errorCode() != 0)
throw error(strFmt("Error: %1",xmlError.reason()));
rootNode = doc.documentElement();
// Get the customer account and name from the document
AccNode =
rootNode.selectSingleNode("//CustomerAccount");
NameNode = rootNode.selectSingleNode("//CustomerName");
setprefix(AccNode.text()+ ' ' + NameNode.text());
// Select all the customer transactions into a nodelist
custTransList =
rootNode.selectNodes("//TransactionDetail");
for (i = 0; i < custTransList.length(); i++)
{
transNode = custTransList.item(i);

2-40

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 2: Working with Data


info(transNode.selectSingleNode("TransText").text()
+ '
' +
transNode.selectSingleNode("TransAmount").text());
}
}

ODBC
Sometimes, data that you need to access from Microsoft Dynamics AX may be
stored in an alternative database. This is common when fetching data from an
external application, either on a regular basis, or during data conversion.
An Open DataBase Connection (ODBC) is a simple way for Microsoft Dynamics
AX to access an external database, and perform queries on it to fetch the required
data.
For this to work, an ODBC DataSource Name (DSN) needs to be created in
Windows. A DSN acts as a thin database client, usually including all
authentication information like username and password. The DSN needs to be
created on the tier where the X++ code will call it from (in other words, either the
client or the AOS). Best practice is to keep the DSN on the AOS, to help
scalability.
Once a DSN is available in Windows, code can be written to leverage its access
to the external database:
static void TestODBC()
{
LoginProperty loginProperty;
OdbcConnection odbcConnection;
Statement statement;
ResultSet resultSet;
str sql, criteria;
SqlStatementExecutePermission perm;
;
//Set information on the ODBC
loginProperty = new LoginProperty();
loginProperty.setDSN("dsn");
loginProperty.setDatabase("databaseName");
//Create connection to external DB
odbcConnection = new OdbcConnection(loginProperty);
if (odbcConnection)
{
sql = "SELECT * FROM MYTABLE WHERE FIELD =" +
criteria + " ORDER BY FIELD1,FIELD2 ASC"
//assert permission for sql string
perm = new SqlStatementExecutePermission(sql);

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

2-41

Development III in Microsoft Dynamics AX 2012


perm.assert();

//Prepare statement
statement = odbcConnection.createStatement();
resultSet = statement.executeQuery(sql);
//Running statement
while (resultSet.next())
{
//It is not possible to get field 3 and then 1.
Always get fields in numerical order: 1,2,3,4,5,6
print resultSet.getString(1);
print resultSet.getString(3);
}
//Shutting down the connection
resultSet.close();
statement.close();

}
else
error("Failed to log on to the database");
}

The previous code uses a variable called "criteria", and includes it in the SQL
string that is executed against the example external database. It then prints the
string values from columns 1 and 3 in the result set. Columns in the result set can
only be called in numerical order. If you need to print column 3 value first, first
fetch the column 1 value and store it in a local variable, then fetch and print the
column 3 value, then print the local variable.
More methods are available on the ResultSet object, to fetch other types from the
record. These include, getReal(), getDate(), getInt().
The string that is passed to the statement.executeQuery() method must be an
SQL query in the format that the external database can understand. For example,
if you are querying an Oracle database, the string must contain a valid Oracle
SQL query.

Microsoft Excel
Microsoft Dynamics AX includes built-in administration tools for importing and
exporting table data using the Microsoft Excel spreadsheet format. However,
sometimes it may be necessary to perform similar Microsoft Excel data import
and export functions from a custom function. Often, this can be done using
Comma Separated Value (CSV) files and the CommaIO class, but sometimes
using XLS or XLSX formatted files is a requirement. To make this possible,
there are various classes to perform Microsoft Excel integration. These all start
with the prefix SysExcel and can be found in the Class branch of the AOT.

2-42

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 2: Working with Data


The following is an example of some X++ code that integrates with an XLS file,
and pulls a value from a specific cell.
SysExcelApplication ExcelApp;
Str cellValue;
;
//Creates the instance
ExcelApp = SysExcelApplication::construct();
//Opens the file
ExcelApp.workbooks().open('c:\test.xls');
//Shows the value of the selected cell.
cellValue = ExcelApp.activeSheet().cells().item(1,
1).value().toString(); //both columns and rows are
referenced numerically, eg. A=1, B=2 etc
info(strfmt("Value of cell A1 = %1", cellValue));

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

2-43

Development III in Microsoft Dynamics AX 2012

Lab 2.5 - Integrating External Data


Scenario
Your company needs to import seasonal sales prices every quarter. The sales
prices are maintained in a spreadsheet that need to be imported in to AX once
they are complete. You have been asked to write a program to import the sales
prices.

Challenge Yourself!
To complete this lab, you need to create an external data file. Create a new file
with the following structure:
Item number, Date, Amount
For example your file could look like this:

FIGURE 2.5 SALES PRICE IMPORT FILE

Save the file as a CSV file named SalesPrice.csv in the root C drive directory.
Write a job in X++ that will import the data from the SalesPrice.csv file that you
just created. The job will need to set some values for other fields - the price
should be for All customer accounts, use the current companies default currency,
be applicable for all quantities and for any inventory dimension.

Step by Step
1. Open the AOT and create a new Job.
2. Copy the following code to the job.
static void ImportSalesPrice(Args _args)
{
CommaIO
commaIO;
container
readCon;
str
column1, column2, column3;
PriceDiscTable priceDiscTable;
#File
;

2-44

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 2: Working with Data


commaIO = new CommaIO('c:\\salesprice.csv', #io_read);
if (commaIO)
{
while (commaIO.status() == IO_Status::OK)
{
readCon = commaIO.read();
column1 = conPeek(readCon, 1);
column2 = conPeek(readCon, 2);
column3 = conPeek(readCon, 3);
priceDiscTable.relation =
PriceType::PriceSales;
priceDiscTable.AccountCode =
TableGroupAll::All;
priceDiscTable.Currency =
Ledger::accountingCurrency();
priceDiscTable.ItemRelation = column1;
priceDiscTable.FromDate =
str2date(column2,123);
priceDiscTable.Amount = str2num(column3);
priceDiscTable.InventDimId =
InventDim::findOrCreateBlank().inventDimId;
priceDiscTable.QuantityAmountFrom = 1;
pricedisctable.insert();
}
}
}

3. Press F5 to run the job.


4. You can check that the prices have been imported by navigating to
Product information management > Released products. Find the
items that you have set the price for in the file, and then click Sell >
Sales price.

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

2-45

Development III in Microsoft Dynamics AX 2012

Summary
Using the most efficient approaches to database communication will result in an
application that runs with the best performance possible. Using the tools
described in this chapter, modifications made to the standard application can
operate at the peak performance expected by the users.

2-46

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 2: Working with Data

Test Your Knowledge


Test your knowledge with the following questions.
1. Match the select statement clauses, with their correct purpose:
_____ 1. Fieldlist
_____ 2. FirstOnly
_____ 3. FirstFast
_____ 4.
ForceNestedLoop
_____ 5.
ForceSelectOrder
_____ 6.
CrossCompany
_____ 7. Index

a. This keyword forces the database to use a


specific index as the order by clause.
b. This keyword forces the database to prioritize
fetching the first few rows fast over fetching the
complete result set.
c. This keyword forces the database to return
records regardless of dataAreaId, or for a set of
specific dataAreaId's.
d. This keyword forces the database server to use a
nested-loop algorithm to process a given SQL
statement that contains a join.
e. Used to specify fields which will be fetched from
the database.
f. This keyword forces the database server to
access the tables in a join in the given order.
g. This keyword forces the database to return only
the first found record.

2. You have the following X++ code, with two QueryBuildDatasource objects.
Modify the second line and add a third line, so that these two datasources are
joined:
qbds1 = query.addDatasource(tableNum(CustTable));
qbds2 = query.addDatasource(tableNum(CustTrans));

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

2-47

Development III in Microsoft Dynamics AX 2012


3. Which of the following are true for avoiding locking: (Select all that apply)
( ) Keep the transactions as short in time as possible.
( ) Try to lock central resources.
( ) Try to lock central resources in the same order.
( ) Try to implement dialogs with the user inside transactions.
4. A temporary table is declared in an object running on the client. The first
record is inserted from a method running on the AOS. Where is the data in
the temporary table physically stored?

5. If you create a new field on CustTable and SalesTable, where is it most


appropriate to set the field on SalesTable from the field on CustTable?

2-48

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 2: Working with Data


6. Why does Microsoft Dynamics AX use Parm tables?

7. Which line of code adds a new node into an XML file:


( ) Node = rootNode.AddChild(xmlDoc.createElement("Customer"));
( ) Node = rootNode.AppendChild(xmlDoc.createElement("Customer"));
( ) Node = AddChild(rootNode).(xmlDoc.createElement("Customer"));
( ) Node = rootNode.(xmlDoc.create("Customer"). appendChild());
8. Identify the problem with the following block of ODBC code, where the
resultSet has already been successfully fetched from the external database:
while (resultSet.next())
{
print resultSet.getString(2);
print resultSet.getString(5);
print resultSet.getReal(4);
print resultSet.getInt(7);
}

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

2-49

Development III in Microsoft Dynamics AX 2012

Quick Interaction: Lessons Learned


Take a moment and write down three key points you have learned from this
chapter
1.

2.

3.

2-50

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 2: Working with Data

Solutions
Test Your Knowledge
1. Match the select statement clauses, with their correct purpose:
e 1. Fieldlist
g 2. FirstOnly
b 3. FirstFast
d 4.
ForceNestedLoop
f 5.
ForceSelectOrder
c 6.
CrossCompany
a 7. Index

a. This keyword forces the database to use a


specific index as the order by clause.
b. This keyword forces the database to prioritize
fetching the first few rows fast over fetching the
complete result set.
c. This keyword forces the database to return
records regardless of dataAreaId, or for a set of
specific dataAreaId's.
d. This keyword forces the database server to use a
nested-loop algorithm to process a given SQL
statement that contains a join.
e. Used to specify fields which will be fetched from
the database.
f. This keyword forces the database server to access
the tables in a join in the given order.
g. This keyword forces the database to return only
the first found record.

2. You have the following X++ code, with two QueryBuildDatasource objects.
Modify the second line and add a third line, so that these two datasources are
joined:
qbds1 = query.addDatasource(tableNum(CustTable));
qbds2 = query.addDatasource(tableNum(CustTrans));
MODEL ANSWER:
qbds2 = qbds1.addDatasource(tableNum(CustTrans));
qbds2.relations(TRUE);
3. Which of the following are true for avoiding locking: (Select all that apply)
() Keep the transactions as short in time as possible.
( ) Try to lock central resources.
() Try to lock central resources in the same order.
( ) Try to implement dialogs with the user inside transactions.

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

2-51

Development III in Microsoft Dynamics AX 2012


4. A temporary table is declared in an object running on the client. The first
record is inserted from a method running on the AOS. Where is the data in
the temporary table physically stored?
MODEL ANSWER:
The data in the temporary table are stored in a file on the AOS because the
first record is inserted from a method running on the AOS.
5. If you create a new field on CustTable and SalesTable, where is it most
appropriate to set the field on SalesTable from the field on CustTable?
MODEL ANSWER:
SalesTable.InitFromCustTable()
6. Why does Microsoft Dynamics AX use Parm tables?
MODEL ANSWER:
Microsoft Dynamics AX uses Parm tables to store data that will be updated
on sales orders, purchase orders. This allows the user to view and modify
what will be updated without affecting the original order. It also enables the
update to be processed by the batch system.
7. Which line of code adds a new node into an XML file:
( ) Node = rootNode.AddChild(xmlDoc.createElement("Customer"));
() Node =
rootNode.AppendChild(xmlDoc.createElement("Customer"));
( ) Node = AddChild(rootNode).(xmlDoc.createElement("Customer"));
( ) Node = rootNode.(xmlDoc.create("Customer"). appendChild());
8. Identify the problem with the following block of ODBC code, where the
resultSet has already been successfully fetched from the external database:
while (resultSet.next())
{
print resultSet.getString(2);
print resultSet.getString(5);
print resultSet.getReal(4);
print resultSet.getInt(7);
}
MODEL ANSWER:
All columns in the result set need to be called in numerical order. Column 4
cannot be called after column 5.

2-52

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 3: Classes

CHAPTER 3: CLASSES
Objectives
The objectives are:

Use collection classes to store data in X++.

List which application objects control different Graphical User


Interface (GUI) components.

Modify and use the Application Substituted Kernel Classes.

Extend the RunBase framework to create new batch processes.

Transfer information using the Args object.

Introduction
Microsoft Dynamics AX provides a large range of standard system classes that
you can use while developing X++ code. This lesson introduces some of the most
commonly used system classes, and demonstrates ways they can be used to
support modifications.

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

3-1

Development III in Microsoft Dynamics AX 2012

Collection Classes
X++ contains two compound data types: arrays and containers. They are useful
for aggregating values of simpler data types. However, you cannot store objects
in arrays or containers. The Microsoft Dynamics AX collection classes have been
designed for storing objects.
The following collection classes are available:

List

Map

Set

Array

Struct

The following classes also aide in aggregating data:

RecordSortedList

RecordInsertList

List
A List object contains members that are accessed sequentially. Lists are
structures that can contain members of any X++ type. All the members in the
same list must be of the same type.
The following methods are commonly used on List objects:

addEnd(anytype) - adds a member to the end of the list.

addStart(anytype) - adds a member to the start of the list.

elements() - returns the number of members contained in the list.

getEnumerator() - returns a ListEnumerator object for this List


object.

typeId() - returns the Type that the List contains.

The ListEnumerator class allows you to traverse through the elements within a
list. The following methods are commonly used on ListEnumerator objects:

3-2

current() - retrieves the value of the item currently pointed to in the


list.

moveNext() - moves the enumerator to the next item in the list. List
enumerators start before the first element in the list, so moveNext()
must be called to make it point to the first element in the list.

reset() - moves the enumerator to the start of the list.

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 3: Classes
The following example demonstrates how to create a list, then move through it
and extract values from it.
1. Create a new List object, specifying the data type it will contain.
2. Add members to the List object, using the List.addEnd() and
List.addStart() methods.
3. Set the ListEnumerator object using the List.getEnumerator()
method.
4. Go to the start of the list, using ListEnumerator.reset().
5. Move through the members of the list, using
ListEnumerator.moveNext().
6. Pull the value of the current member in the list, using
ListEnumerator.current().
List
integerList = new List(Types::Integer);
ListEnumerator enumerator;
// Add some elements to the list
integerList.addEnd(1);
integerList.addEnd(2);
integerList.addStart(3);
// Set the enumerator
enumerator = integerList.getEnumerator();
// Go to beginning of enumerator
enumerator.reset();
//Go to the first element in the List
enumerator.moveNext();
// Print contents of first and second elements
info(strfmt("First element is %1", enumerator.current()));
enumerator.moveNext();
info(strfmt("Second element is %1", enumerator.current()));

When the code is executed, the result in the infolog should be as follows:

First element is 3

Second element is 1

Map
A Map object associates one value (the key) with another value. Both the key
and value can be of any valid X++ type, including objects. The types of the key
and value are specified in the declaration of the map. The way in which maps are
implemented means that access to the values is very fast.

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

3-3

Development III in Microsoft Dynamics AX 2012


Multiple keys can map to the same value, but one key can map to only one value
at a time. If a [key, value] pair is added where the key already exists, it will
replace the existing pair with that key value.
The following methods are commonly used on Map objects:

insert(_key, _value) - inserts a member (pair) into the map, where


_key and _value can be anytype.

remove(_key) - removes a [key, value] pair from a map.

exists(_key) - determines whether a particular value exists as a key


in the map.

lookup(_key) - returns the value mapped to by a particular key


value.

elements() - returns the number of members contained in the map.

getEnumerator() - returns a MapEnumerator object for this Map


object.

The MapEnumerator class allows you to traverse through the members of a


map. The following methods are commonly used on MapEnumerator objects:

currentKey() - retrieves the key of the pair currently pointed to in


the map.

currentValue() - retrieves the value of the pair currently pointed to


in the map.

moveNext() - moves the enumerator to the next pair in the map. Map
enumerators start before the first pair in the map, so moveNext()
must be called to make it point to the first pair in the map.

reset() - moves the enumerator to the start of the map.

The following example demonstrates how to create a map of Customers per


State, then move through it and extract values from it.
1. Create a new Map object, specifying the data type it will contain.
2. Loop through the records in the CustTable. For each, use
Map.exists() to check if CustTable.stateName() already exists as a
key in the map.
3. If it does not already exist, use Map.insert() to insert a pair of
(CustTable.stateName(), 1).
4. If it does already exist, use Map.insert() to re-insert the pair with the
same key (which will overwrite the existing pair), but the new value
equals the existing pair value plus 1.
5. Set the MapEnumerator object using the Map.getEnumerator()
method.
6. Go to the start of the map, using MapEnumerator.reset().

3-4

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 3: Classes
7. Move through the members of the map, using
MapEnumerator.moveNext().
8. Pull the value of the current member in the map, using
MapEnumerator.currentKey() and
MapEnumerator.currentValue().
Map
mapStateNumbers;
MapEnumerator enumerator;
CustTable
custTable;
mapStateNumbers = new Map(Types::String, Types::Integer);
while select custTable
{
if(mapStateNumbers.exists(custTable.stateName()))
{
mapStateNumbers.insert(custTable.stateName(),
mapStateNumbers.lookup(custTable.stateName())+1);
}
else
{
mapStateNumbers.insert(custTable.StateName(), 1);
}
}
enumerator = mapStateNumbers.getEnumerator();
while (enumerator.moveNext())
{
info(strfmt("%1 customers are located in %2.",
enumerator.currentValue(), enumerator.currentKey());
}

Set
A Set is used for the storage and retrieval of data from a collection in which the
members are unique. The values of the members serve as the key according to
which the data is automatically ordered. Thus, it differs from a List collection
class where the members are placed into a specific position, and not ordered
automatically by their value.
Set members may be of any X++ type. All members in the set must have the
same type.
When a value that is already stored in the set is added again, it is ignored and
does not increase the number of members in the set.
The following methods are commonly used on Set objects:

add(anytype) - inserts a value into the set.

remove(anytype) - removes a value from the set.

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

3-5

Development III in Microsoft Dynamics AX 2012

in(anytype) - determines whether a particular value is a member of


the set.

elements() - returns the number of members contained in the set.

getEnumerator() - returns a SetEnumerator object for this Set


object.

The SetEnumerator class allows you to traverse through the members within a
set. The following methods are commonly used on SetEnumerator objects:

current() - retrieves the value currently pointed to in the set.

moveNext() - moves the enumerator to the next value in the set. Set
enumerators start before the first value in the set, so moveNext()
must be called to make it point to the first value in the set.

reset() - moves the enumerator to the start of the set.

The following example demonstrates how to create two sets, and then compares
each set to detect any shared members and remove them.
1. Create two new Set objects, specifying the data type they will
contain.
2. Add members to the Set objects, using the Set.insert() method.
3. Set a SetEnumerator object for one of the Sets, using the
Set.getEnumerator() method.
4. Go to the start of the set, using SetEnumerator.reset().
5. Move through the members in the set, using
SetEnumerator.moveNext().
6. Pull the value of the current member in the set, using
SetEnumerator.current().
7. Search for the value in the second set, using Set.in(). If found,
remove it using Set.remove().
Set
Set
SetEnumerator
Int

setOne;
setTwo;
enumerator;
value;

setOne = new Set(types::Integer);


setOne.add(1);
setOne.add(2);
setOne.add(3);
setTwo = new Set(Types::Integer);
setTwo.add(3);
setTwo.add(4);
setTwo.add(5);
enumerator = setOne.getEnumerator();

3-6

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 3: Classes
while (enumerator.moveNext())
{
value = enumerator.current();

if (setTwo.in(value))
{
setTwo.remove(value);
}

Array
Array objects may hold values of any single type, including objects and records.
Objects of this type may be transferred to functions and methods. The values are
stored sequentially.
The following methods are commonly used on Array objects:

exists(int) - checks if a value exists in a specific position in the


array.

lastIndex() - retrieves the highest index that is used to store a value


in the array.

value(int _index, [anytype _value]) - gets or sets the value of the


array member that is stored at the specified index.

typeId() - returns the data type that the Array contains.

The following is an example using an Array object that holds three different
Query objects:
Array array = new Array (Types::Class);
array.value(1, new Query());
array.value(2, new Query());
array.value(3, new Query());

Struct
Struct objects (short for structures) are entities that can hold a number of values
of any X++ type. Structs are used to group information about a specific entity.
For example, there may be a need to store information about a customer's name,
group and address and then treat this compound information as one item.
The following methods are commonly used on struct objects:

add(str _fieldName, anytype _value) - adds a new field to the


struct and assigns the specified value to it.

exists(str _fieldName) - determines whether a particular field exists


in a struct.

fieldName(int _index) - returns the name of the field in the struct at


the position specified.

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

3-7

Development III in Microsoft Dynamics AX 2012

fields() - returns the number of fields in the struct.

index(str _fieldName) - calculates the position of a field in the


struct based on its name.

remove(str _fieldName) - removes a field from a struct.

value(str _fieldName, anytype _value) - gets or sets the value for a


specific field.

valueIndex(int _index, anytype _value) - gets or sets the value of


the field at a specific index position in a struct.

The fields in a struct are specified by a data type and a name. Fields can be
added when the struct is created by using the new() method, or they can be
created after the struct has been created using the add() method. If a field is
added using the add() method, the value is specified for the field at the same
time, and the data type is inferred from this value. If a field is added during the
instantiation of the struct, the value() or the valueIndex() method needs to be
used to assign a value to it.
The fields in a struct are arranged in alphabetical order of the field names.
The following is an example using the Struct object.
Struct struct = new Struct();
int i;
// Add new fields and values
struct.add("Group", "DOM");
struct.add("Name", "Jane Doe");
struct.add("Shoesize", 45);
// Prints the type and name of all items in the struct
for (i = 1 ; i <= struct.fields() ; i++)
{
info(strfmt("%1 : %2", struct.fieldType(i),
struct.fieldName(i)));
}

The result in the infolog should be:

3-8

String : Group

String : Name

Integer : Shoesize

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 3: Classes
RecordSortedList
The RecordSortedList class inserts multiple records in a single database trip,
and can hold a subset of data from a table in a particular sort order that does not
exist as an index.
A RecordSortedList object holds records from a single table. The list has a
unique key that is defined by the fields listed by using the sortOrder method.
Records are automatically sorted as they are inserted, they do not have to be
inserted in sort sequence.
RecordSortedList objects are particularly useful for passing a result-set as a
parameter.
There is no limit to the size of a RecordSortedList object, but they are
completely memory-based, so there are potential memory consumption problems.
Use a RecordSortedList in preference to a temporary table when:

Only one sort order is needed.

The number of records is not too high (to avoid memory problems).

Compared to temporary tables, RecordSortedList objects:

Are faster

Are not disk-based

Only have one index

Cannot be used in forms

Require a call between the client and server per (grouped) read

The following methods are commonly used on RecordSortedList objects:

del(common) - removes a record that has a key that matches the key
fields in the recordBuffer from the RecordSortedList.

find(common) - sets the recordBuffer to the contents of the record


that has a key that matches the key fields in the recordBuffer, and
positions the list to the record returned.

first() - positions the list to the first record in the list, and copies its
contents to the recordBuffer.

ins(common) - inserts a new record in a RecordSortedList, unless it


is a duplicate.

insertDatabase() - inserts multiple records on a single trip to the


database.

len() - returns the current number of records in a RecordSortedList


object.

next() - sets the record buffer to the contents of the next record in the
RecordSortedList and positions the list to the record returned.

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

3-9

Development III in Microsoft Dynamics AX 2012

sortOrder(fieldId, ...) - defines the fields on which the records are


sorted.

sortOrderFromContainer(container) - defines the field(s) on


which the records are sorted.

The following code sample demonstrates how to insert multiple CustTable


records in the same database trip, using RecordSortedList.
RecordSortedList recordSortedList;
CustTable
custTable;
recordSortedList = new
RecordSortedList(tablenum(CustTable));
custTable.clear();
custTable.AccountNum = "123";
custTable.CustGroup = "DOM";
custTable.Currency = "USD";
recordSortedList.ins(custTable);
custTable.clear();
custTable.AccountNum = "456";
custTable.CustGroup = "DOM";
custTable.Currency = "USD";
recordSortedList.ins(custTable);
custTable.clear();
custTable.AccountNum ="789";
custTable.CustGroup ="INT";
custTable.Currency ="USD";
recordSortedList.ins(custTable);
recordSortedList.insertDatabase();

RecordInsertList
The RecordInsertList class provides array insert capabilities in the kernel. This
allows you to insert more than one record into the database at a time, which
reduces communication between the application and the database.
Records are inserted only when the kernel finds the time appropriate, but they are
inserted no later than the call to the add() and insertDatabase() methods. These
same methods also return the accumulated number of records that are currently
inserted, which allows you to keep track of when the records are actually
inserted.
The array insert operation automatically falls back to classic record-by-record
inserts when non-SQL based tables are used (for example, temporary tables), or
when the insert method on the table has been overridden.

3-10

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 3: Classes
RecordInsertList is similar to RecordSortedList, but it has built-in client/server
support (it automatically packs data from one tier to another when needed), and it
lacks the sort order features available in RecordSortedList.
The following methods are commonly used on RecordInsertList objects:

add(common) - inserts a new record in a RecordInsertList.

insertDatabase() - inserts multiple records on a single trip to the


database.

The following is an example using RecordInsertList to copy a bill of materials


(BOM) from one BOM to another.
void copyBOM(BOMId _fromBomId, BOMId _toBomId)
{
RecordInsertList bomList;
BOM
bom, newBom;
bomList = new RecordInsertList(tableNum(BOM));
while select bom
where bom.BOMId == _fromBomId
{
newBom.data(bom);
newBom.BOMId = _toBomId;
bomList.add(newBOM);
}
bomList.insertDatabase();
}

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

3-11

Development III in Microsoft Dynamics AX 2012

Lab 3.1 - Create a Map


Scenario
Isaac, the Systems Developer, has been asked to create a function that will
quickly inform a user how many customers and vendors are located in each
country. This is a key business indicator for the company.

Challenge Yourself!
Make a map that holds a Country Name and a count of all Customers that have a
postal address in that Country. Enumerate through the map and print the result.

Need a Little Help?


Create a class that has a method that will search through all customers and find
the country in the postal address. The custTable table has a method called
postalAddress(), that will return a record with the address. For each country,
lookup in the map to see if it has already been counted. If it has, then add one to
the total for that country. If it has not, add a new key\value pair in to the map.
Create another method that will enumerate through the key\value pairs and
output both values to the infolog.
Create a method that will call both methods and a main method so that the class
can be called from a menu.

Step by Step
1. In the AOT create a new class called CreateCountryCountMap.
2. Add the following code to the classDeclaration and to four new
methods called searchCustomers, loopCountries, run and main.
3. Save your changes to the class.
4. Right-click on the class and select Open.
5. Verify the results are what you would expect.
public class CreateCountryCountMap
{
Map countryMap;
}
private void searchCustomers()
{
CustTable custTable;
Counter cnt;
LogisticsAddressCountryRegionId countryId;
LogisticsAddressCountryRegionName countryName;
countryMap = new Map(Types::String, Types::Integer);

3-12

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 3: Classes
while select custTable
{
countryId =
custTable.postalAddress().CountryRegionId;
countryName =
LogisticsAddressCountryRegion::find(countryId).displayName(
);
cnt = 0;
if (countryMap.exists(countryName))
{
cnt = countryMap.lookup(countryName);
countryMap.remove(countryId);
}

cnt++;
countryMap.insert(countryName, cnt);

private void loopCountries()


{
MapEnumerator mapEnumerator = new
MapEnumerator(countryMap);
while (mapEnumerator.moveNext())
{
info(strFmt("There are %1 customers in %2",
mapEnumerator.currentValue(), mapEnumerator.currentKey()));
}
}
private void run()
{
this.searchCustomers();
this.loopCountries();
}
static void main(Args _args)
{
CreateCountryCountMap countryCount = new
CreateCountryCountMap();
countryCount.run();
}

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

3-13

Development III in Microsoft Dynamics AX 2012

Application Object Classes


These classes hold functions that are activated whenever you use the AOT to
create your application. For example, the system uses the FormDesign class
when you define the layout of your form in the Designs node in the AOT. When
you click New CheckBox in the AOT, the system activates the controlName
method with a CheckBox type control as parameter.
These classes also enable you to create and modify application objects.

Form Classes
The Form classes enable you to manipulate, create, modify, or run forms by
using X++ code. You can also modify forms during run time so, for example, one
or more controls are hidden on a form, depending on the user's selections in the
preceding form.
The Form classes are all system classes and are prefixed with Form. For
example, FormRun, FormStringControl.
The most important classes are described in the following table:

3-14

System class name

Description

Form

This class contains property methods for the form


name and other form properties and methods that
enable addition of data sources and controls to the
form.
Use this class to create a form from your X++
code rather than by using the AOT.

FormRun

This class contains methods for executing a form.

FormDesign

This class contains property methods for the


properties on the Design node for a form and
methods for creating controls.
Used at form runtime.

FormBuildDesign

This class contains property methods for the


properties on the Design node for a form and
methods for creating controls.
Use this class to create the graphical layout of a
form by using X++ code.
Used to design a form.

FormDataSource

This class contains property methods for the


properties on a form data source and methods for
changing the behavior of a data source (such as
caching, validation, and so on) and notification
methods for events on records in forms.
Used at form run-time.

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 3: Classes
System class name

Description

FormBuildDataSource

This class contains property methods for the


properties on a form data source. Use this class to
manipulate the properties on a form data source
by using X++ code.
Used to design a form.

FormControl

FormControl is the parent class for all the other


form control classes. Use methods on the class for
the relevant control type rather than this parent
class.
Used at form run-time.

Form<control
name>Control
(e.g. FormStringControl,
FormTreeControl, etc)

These classes contain property methods for an


individual control type and methods to manipulate
that control type.
Used at form run-time.

FormBuild<control
name>Control
(e.g.
FormBuildStringControl
,
FormBuildTreeControl,
and so on.)

These classes contain property methods for an


individual control type.
Used to design a form.

The following example demonstrates how to create and then run a form using
X++ code:
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.

Create a Form object.


Add a datasource to the form using Form.addDataSource().
Add a design to the form using Form.addDesign().
Add the caption "Customer information" to the design using
FormBuildDesign.caption().
Add a Tab to the design using FormBuildDesign.addControl().
Add two Tab Pages to the Tab using
FormBuildTabControl.addControl().
Add the captions "Overview" and "General" to the Tab Pages using
FormBuildTabPageControl.caption().
Add a grid to the first Tab Page using
FormBuildTabPageControl.addControl().
Add two strings to the second Tab Page using
FormBuildTabPageControl.addControl().
Add data fields (for AccountNum, Phone, Name and Address) to the
grid using FormBuildGridControl.addDataField(), referencing the
FormBuildDataSource object.

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

3-15

Development III in Microsoft Dynamics AX 2012


11. Set data sources to the string controls using
FormBuildStringControl.dataSource() and .dataField(),
referencing the FormBuildDataSource object.
12. Create a new Args object.
13. Pass Form object into Args.object().
14. Use ClassFactory.formRunClass(args) to create a FormRun class.
15. Run and detach the FormRun object.
16. The method should look as follows:
static void createForm()
{
Args
Form
FormRun
FormBuildDesign
FormBuildDataSource
FormBuildGridControl
FormBuildStringControl
FormBuildStringControl
FormBuildTabControl
FormBuildTabPageControl
FormBuildTabPageControl
DictTable
int
FormControlType
FormControlType
FormControlType
FormControlType

args;
form;
formRun;
fbDesign;
fbDS;
fbGrid;
fbStr1;
fbStr2;
fbTab;
fbTabPage1;
fbTabPage2;
dictTable;
idx, idx2, idx3;

fctTabPage
fctTab
fctGrid
fctString

=
=
=
=

FormControlType::TabPage;
FormControlType::Tab;
FormControlType::Grid;
FormControlType::String;

// Create the form header.


form = new Form();
// Add a data source to the form.
dictTable = new DictTable(tablenum(CustTable));
fbDS = form.addDataSource(dictTable.name());
fbDS.table(dictTable.id());
// Create the form design.
fbDesign = form.addDesign("Design");
fbDesign.caption("Customer information");
// Add tab
fbTab = fbDesign.addControl(fctTab, "Overview");
// Add tab pages
fbTabPage1 = fbTab.addControl(fctTabPage, "Overview");
fbTabPage1.caption("Overview");

3-16

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 3: Classes
fbTabPage2 = fbTab.addControl(fctTabPage,"General");
fbTabPage2.caption("General");
// Add grid onto tab 1 and data fields onto grid.
fbGrid = fbTabPage1.addControl(fctGrid,"Table Grid");
fbGrid.addDataField(fbDS.id(),
dictTable.fieldName2Id("AccountNum"));
fbGrid.addDataField(fbDS.id(),
dictTable.fieldName2Id("CustGroup"));
fbGrid.addDataField(fbDS.id(),
dictTable.fieldName2Id("Currency"));
// Add string fields to tab 2, and assign datasource
fbStr1 = fbTabPage2.addControl(fctString,"String 1");
fbStr1.dataSource(fbDS.id());
fbStr1.dataField(dictTable.fieldName2Id("AccountNum"));
fbStr2 = fbTabPage2.addControl(fctString,"String 2");
fbStr2.dataSource(fbDS.id());
fbStr2.dataField(dictTable.fieldName2Id("CustGroup"));

// Create the run-time form.


args = new Args();
args.object(form);
formRun = classfactory.formRunClass(args);
formRun.run();
formRun.detach();

Query Classes
Query classes were introduced in "Chapter 2: Working with Data." This lesson
provides a demonstration using these classes.
This example demonstrates how to use the Query classes to build a query.
1.
2.
3.
4.
5.

Add a class to the AOT called QueryClassDemo.


Add a class method called createQueryRun().
Create a new Query object.
Add a new datasource for CustTable using Query.addDataSource().
Add a new range on CustTable.AccountNum using
QueryBuildDataSource.addRange().
6. Set a criteria on the range using QueryBuildRange.value().
7. Make the query sort by CustTable.CustGroup, using
QueryBuildDataSource.addOrderByField().

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

3-17

Development III in Microsoft Dynamics AX 2012


8. Create a QueryRun object based on the query.
9. Return the QueryRun object.
public QueryRun createQueryRun()
{
Query
query;
QueryRun
qr;
QueryBuildDataSource qbds;
QueryBuildRange
qbr;
query = new Query();
qbds = query.addDataSource(TableNum(CustTable));
qbr = qbds.addRange(FieldNum(CustTable, AccountNum));
qbr.value('4005');
qbds.addOrderByField(FieldNum(CustTable, CustGroup));
qr = new QueryRun(query);
return qr;
}

3-18

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 3: Classes

Lab 3.2 - Create a Query From Code


Scenario
You have been asked to create a function that will quickly show the user a list of
items that have been sold on a sales order. This is a key business indicator for
your company.

Challenge Yourself!
Create methods that build and use a query to display data. It should prompt the
user for the sales order number and then list all item id's and item names of lines
on that sales order.

Step by Step
1. In the AOT create a new class called SalesListItems.
2. Add the following code to the classDeclartion and to four new
methods called buildQuery, listItems, run and main.
3. Save your changes to the class.
4. Right-click on the class and select Open.
5. Verify the results are what you would expect.
class SalesListItems
{
QueryRun queryRun;
}
private void buildQuery()
{
Query query;
QueryBuildDataSource dataSource;
query = new query();
dataSource = query.addDataSource(tableNum(SalesLine));
query.addQueryFilter(dataSource, fieldStr(SalesLine,
SalesId));
}

queryRun = new QueryRun(query);

private void listItems()


{
SalesLine salesLine;
while (queryRun.next())
{
salesLine = queryRun.get(tableNum(SalesLine));

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

3-19

Development III in Microsoft Dynamics AX 2012


info(strFmt("%1 %2",salesLine.ItemId,
salesLine.itemName()));
}
}
private void run()
{
this.buildQuery();

if (queryRun.prompt())
this.listItems();

static void main(Args _args)


{
SalesListItems salesListItems = new SalesListItems();
salesListItems.run();
}

3-20

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 3: Classes

Application Substituted Kernel Classes


Application substituted kernel classes extend system classes whose names begin
with "x". They are listed at the bottom of the Classes node in the AOT. To
indicate that the classes are special, their icons differ from those of the other
application classes.
Most application substituted kernel classes have a global variable used to access
the class. The global variables do not need a variable declaration and are preinstantiated by the client.
Each class serves a different purpose. The extent to which you will need to use
them vary.
When using the classes:

Always use the application classes rather than using the system
classes directly.

Use the global variables to access the classes, when available.

Never instantiate these classes (except for the Session class). Use the
global variables listed below instead.

Specify the application class name to access static methods.

Application class

System class

Global variable

Runs on

Session

xSession

(none)

Client and server

Application

xApplication

appl

Server

Company

xCompany

appl.company

Server

Info

xInfo

Infolog

Client

ClassFactory

xClassFactory

classFactory

Client and server

Global

xGlobal

(none)

Client and server

VersionControl

xVersionControl

versionControl

Client

Five of these classes are described further.

xApplication
The xApplication class provides access to information about the current
application. It also provides some application level functions.
The Application class extends the xApplication class, and allows its methods to
be overridden.

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

3-21

Development III in Microsoft Dynamics AX 2012


Some of the more commonly used and overridden methods on Application are:

buildNo() - returns the build number of the application in its current


state.

releaseVersion() - returns the release version of the application.

company([dataAreaId]) - gets or sets the dataArea that the user is


currently using.

deleteCompany(dataAreaId) - deletes a company account indicated


by the dataAreaId.

xCompany
The xCompany class provides access to information about the current company
(dataArea). It also provides some application level functions.
The Company class extends the xCompany class, and allows its methods to be
overridden.
Some of the more commonly used and overridden methods on Company are:

new() - this is called when a user changes company in their client.

xInfo
The xInfo class provides access to the InfoLog framework.
The Info class extends the xInfo class, and allows its methods to be overridden.
Some of the more commonly used and overridden methods on Info are:

add() - adds a message to the infolog, to display it to the user.

currentAOLayer() - returns the current layer the user is working in.

createWorkspaceWindow()- opens a new workspace window.

createDevelopmentWorkspaceWindow() - opens a new


development workspace window.

infologData() - returns an InfoLogData container object holding the


messages currently in the infolog (which could be saved or displayed
somewhere other than the infolog, for example, in a log).

xGlobal
The xGlobal class provides a large selection of functions, designed to be used
anywhere in the application.
The Global class extends the xGlobal class, and allows its methods to be
overridden, and for extra global methods to be created. Many of the methods on
Global provide useful data manipulation functions. (For example,
numeralsToTxt(), str2con(), utcDateTime2SystemDateTime()).

3-22

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 3: Classes
The static methods on Global are special in that they can be called from any
place in the application, without having to specify the Global class. For example,
Global::time2StrHMS() can be called as time2StrHMS(). In this way, they
become more like the system functions.

xClassFactory
The xClassFactory class is used by the system to create objects from classes.
One analogy of this is, a Class is a blueprint, xClassFactory is a factory and an
Object is the finished product.
The ClassFactory class extends the xClassFactory class, and allows its methods
to be overridden.
Some of the more commonly used methods on ClassFactory are:

formRunClass(args) - creates a FormRun object for a specific


form design, using the formstr value in the args parameter.

createClass(classId) - creates an object from a class Id.

queryRunClass(args) - creates a QueryRun object for a specific


query design, using the querystr value in the args parameter.

The following example demonstrates how to use ClassFactory to create and run
the CustTable form using X++:
1. Create a new Args object, passing in formStr(CustTable).
2. Call ClassFactory.formRunClass() passing in the args object, and
placing returned object into a FormRun variable.
3. Call the init(), run() and detach() methods on the FormRun object.
Args
args;
FormRun formRun;
args = new Args(formstr(CustTable));
formRun = classFactory.formRunClass(args);
formRun.init();
formRun.run();
formRun.detach();

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

3-23

Development III in Microsoft Dynamics AX 2012

Lab 3.3 - Create a Global method


Scenario
Since your company's recent upgrade to Microsoft Dynamics AX 2012, there has
been a request for a function to convert a Date type value to an equivalent
utcDateTime type value. You have been given the task of creating a new function
to perform this conversion.

Challenge Yourself!
Create a global method which converts a date value to its equivalent
utcDateTime value. Write a method to accept a date value and set the time
component equal to midnight. Use the new global method to output the
utcDateTime of a specified date to the screen using a job.

Step by Step
1.
2.
3.
4.
5.
6.
7.
8.

Open the AOT.


Find the class called Global.
Right-click Global class and select New > Method.
Copy the code shown below for the method date2utcDateTime in to
the new method.
Save your changes to the Global class.
Create a new job.
Copy the code below for JobDate2UTC to the new job.
Press F5 to run the job and see the results.

public static utcDateTime date2utcDateTime(date _date)


{
return DateTimeUtil::newDateTime(_date, 0);
}
static void JobDate2UTC(Args _args)
{
print date2utcDateTime(mkDate(28,9,12));
pause;
}

3-24

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 3: Classes

RunBase Framework
The functions in Microsoft Dynamics AX can be categorized into three basic
groups:

Data entry/queries

Reporting

Data manipulation

Data entry/queries are implemented by developing forms, reporting is


implemented by reports and data manipulation is implemented by classes.
The RunBase class is an abstract class which defines a common structure for all
data manipulation functions in Microsoft Dynamics AX. Using this framework
has the following advantages:

The framework ensures that all updates are structured equally.

Sharing common code through inheritance.

A common dialog layout and functions for all updates.

Automatic "memory" of the user selections from last run.

Easy implementation of batch execution (when using child class,


RunBaseBatch).

Built-in support for common tasks like query criteria selection and
progress bar.

The following task is an example of data manipulation:

The user is prompted to enter a customer account and a period


specified by a from date and a to date.

The data manipulation sums up all customer transactions specified


by the account and period.

The sum is presented in the infolog.

NOTE: The Business Operation Framework (BOF) is a new way to build


operations and take advantage of services and CIL execution. RunBase continues
to be a supported pattern for Microsoft Dynamics AX 2012.
When building new operations that need to be used interactively and from batch,
the BOF should be used.
More information about the BOF can be found here:
What's New: Services & AIF for Developers in Microsoft Dynamics AX 2012 http://go.microsoft.com/fwlink/?LinkID=224714&clcid=0x409

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

3-25

Development III in Microsoft Dynamics AX 2012


Business Operations Framework (BOF) http://go.microsoft.com/fwlink/?LinkID=224713&clcid=0x409

The Minimum RunBase Implementation


The minimum implementation of a RunBase class consists of the following
components:

ClassDeclaration

pack

unpack

run

description (static)

main (static)

The following dialog methods are not explicitly required in a minimum


implementation, but we will explore them as part of this example:

dialog

getFromDialog

If the dialog methods are not overridden, this provides a standard blank dialog.

ClassDeclaration
The classDeclaration consists of three types of definitions:

Variables used to create fields in the dialog.

Variables used within the data manipulation .

Local macro to define which variables to pack (in other words,


remember for next time, and/or use on the batch server).

In this example the class declaration is as follows.


public class DemoRunBase extends RunBase
{
DialogField dialogAccount;
DialogField dialogFromDate;
DialogField dialogToDate;
CustAccount custAccount;
FromDate
fromDate;
ToDate
toDate;
#DEFINE.CurrentVersion(1)
#LOCALMACRO.CurrentList
custAccount,

3-26

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 3: Classes
fromDate,
toDate
#ENDMACRO
}

The individual fields of the dialog will be initialized in the method dialog().
When the dialog is accepted by the user, the contents of the dialog fields are read
in the method getFromDialog(). As both methods have to access the same
variables, they are defined as members of the class.
The manipulation in the method run() uses the information from the dialog. This
information is stored in variables with specific data types. These variables are
initialized in the method getFromDialog() and read in the method run().
The information for the last used dialog values is packed into a container
structure. The variables that should be packed are defined in the CurrentList
macro in the classDeclaration.

Dialog
This method builds a dialog and initializes the fields that will be used to capture
data from the user. These variables can be automatically initialized with the same
data selected in the last run.
Typically, developers initialize the dialog object by a call to super() and then add
fields and other controls, afterward. The dialog() method for this example has the
following contents.
protected Object dialog()
{
DialogRunBase dialog;
DialogGroup groupPeriod;
dialog = super();
dialogAccount =
dialog.addFieldValue(extendedTypeStr(CustAccount),
custAccount);
groupPeriod = dialog.addGroup("Period");
dialogFromDate =
dialog.addFieldValue(extendedTypeStr(FromDate), fromDate,
"Period from");
dialogToDate =
dialog.addFieldValue(extendedTypeStr(ToDate), toDate,
"Period to");
return dialog;

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

3-27

Development III in Microsoft Dynamics AX 2012


Visual elements are added to the dialog design using the add*() methods on the
Dialog object. This code first adds one field of type CustAccount, and references
the custAccount variable (the value of which will be used as the default for the
field, if it has a value). Then, a group is added to the design, with a caption
"Period". The two fields added after that will be included in the just created
group (by default). These two fields also reference class variables, and include a
third text parameter to override the label on the fields.
When the dialog is rendered, it will appear as follows.

FIGURE 3.1 DIALOG FROM RUNBASE

GetFromDialog
This method is called immediately after the dialog is accepted by the user, and
before the run() method is called. It is used to transfer the information from the
dialog fields into the class variables.
The method has the following content.
public boolean getFromDialog()
{
boolean ret;
ret = super();
custAccount = dialogAccount.value();
fromDate
= dialogFromDate.value();
toDate
= dialogToDate.value();
return ret;
}

3-28

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 3: Classes
Pack
The task for this method is to return a container with the information. This can be
used to initialize a similar data manipulation on another computer and/or at
another time. The information packed should be sufficient to communicate
information from the dialog to the data manipulation in the run-method.
The container should contain a version number as the first element. This number
should control how the rest of the container is structured. If you change the
contents of the container, you should increment the version number.
The pack method frequently has the following basic contents. However, it cannot
be inherited as it contains references to macros defined locally in the class
declaration.
public container pack()
{
return [#CurrentVersion,#CurrentList];
}

The pack/unpack method is used in the following situations:

To save and restore the dialog between each use of the class. The
information is usually saved on a per user/company basis.

To save and restore the specification of the manipulation to execute


on a batch server.

To transfer the object from the client to the server. This is done to
optimize both the user dialog and the data manipulation in an AOS
environment.

Unpack
The unpack() method is the counterpart to the pack() method. It receives a
container as a parameter and restores the type specific variables of the class. The
method returns a Boolean with the value true if the information could be restored.
The unpack() method handles the current version number as a minimum, which
is defined in the ClassDeclaration. You can select to support unpacking of older
versions by extending the switch statement.
The unpack() method has the following content.
public boolean unpack(container _packedClass)
{
Version version = RunBase::getVersion(_packedClass);
switch (version)
{
case(#CurrentVersion) :

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

3-29

Development III in Microsoft Dynamics AX 2012


[version,#CurrentList] = _packedClass;
break;
default :
return false;

}
return true;
}

Run
The run() method controls the data manipulation. The method can use the
variables defined in the classDeclaration and initialized from the dialog. The
method does not receive any formal parameters.
The content of the run() method is unique for each data manipulation, based on
requirements. It will typically contain some local variables and a "try-catch"
statement with TTS calls that wrap all physical data manipulation into one logical
transaction.
The method for our example has the following content.
public void run()
{
CustTrans custTrans;
select
where
&&
&&

sum(AmountMST) from custTrans


custTrans.AccountNum == ledgerAccount
custTrans.TransDate >= fromDate
custTrans.TransDate <= toDate;

info(strFmt("Sum equals %1", custTrans.AmountMST));


}

Description
The description() method returns a descriptive name for the data manipulation.
The value is used to identify the job in the batch queue and is used as the caption
in the dialog.
The method for this example is as follows.
static client server ClassDescription description()
{
return "Sum customer transactions";
}

3-30

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 3: Classes
This method is static, which means that it can be executed on the opposite tier of
the object. In this case, because the method does not contain user interface or
data interface, the specification of both client and server results in an execution
of the method on the tier it is called from regardless of the settings on the class
properties.

Main
The main() method is the entry point when the class is executed from a menu
item. The method is static. It defines and initializes the object. Notice that new()
should never accept any parameters. The main method receives one formal
parameter that is an args() object. This object is described further.
The main() method is also responsible for calling prompt(), which executes the
dialog, and then calling run() to perform the manipulation.
static void main(Args _args)
{
DemoRunBase demoRunBase;
demoRunBase = new DemoRunBase();
if (demoRunBase.prompt())
{
demoRunBase.run();
}
}

With a main() method in place, a RunBase class can now be called from a menu
item in the AOT.

Execution Without Dialog


The RunBase framework is designed to implement classes which are executed by
menu items. It is also easy to execute the methods directly from X++. To
manually control the input with code, instead of using the dialog interface,
supplement the class with methods to initialize the variables without user
interaction.
If the previous example is changed to initialize, specify input for, and execute the
data manipulation without a dialog, it will require extra methods to set the type
specific variables.

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

3-31

Development III in Microsoft Dynamics AX 2012


The following parm method can get or set the custAccount variable:
public CustAccount parmCustAccount(CustAccount _custAccount
= custAccount)
{
custAccount = _custAccount;
return custAccount;
}

Parm methods would also be required for the toDate and fromDate variables.
The following illustrates how you can execute the class directly from X++,
instead of from a menu item.
static void DemoRunBaseJob(Args _args)
{
CustAccount custAccount;
FromDate
fromDate;
ToDate
toDate;
DemoRunBase demoRunBase;
custAccount = "4001";
fromDate
= mkdate(1,1,2006);
toDate
= mkdate(31,12,2006);
demoRunBase = new DemoRunBase();
demoRunBase.parmCustAccount(custAccount);
demoRunBase.parmFromDate(fromDate);
demoRunBase.parmToDate(toDate);
demoRunBase.run();
}

This approach can also be used to transfer information from the main() method
to the object. If you combine a dialog with the above approach, call the method
getLast() on the RunBase object before activating the parm*() methods.
Otherwise, the prompt() method restores the choices from the last execution and
overwrites the initialization.

Integration of a Query
To incorporate a query into the dialog of a RunBase class, implement the
following methods and modifications. The following examples will replace the
CustAccount, FromDate and ToDate dialog fields, with a single query on the
CustTrans table, to allow the user to specify their own criteria.

3-32

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 3: Classes
classDeclaration
The classDeclaration needs some additional variables defined to incorporate a
query. Define a class variable of the type QueryRun. This variable holds a
handle to the object which executes the query.
The classDeclaration now resembles the following content.
public class DemoRunBaseQuery extends RunBase
{
QueryRun queryRun;
#DEFINE.CurrentVersion(1)
#LOCALMACRO.CurrentList
#ENDMACRO
}

InitParmDefault
The RunBase framework calls the initParmDefault() method when initializing a
new object of a RunBase class, if the class does not have previous initialization
data stored in Usage data.
The following content shows you how to build a query dynamically.
public void initParmDefault()
{
Query
query;
QueryBuildDataSource dataSource;
super();
query = new Query();
dataSource = query.addDataSource(tableNum(CustTrans));
dataSource.addRange(fieldNum(CustTrans, AccountNum));
dataSource.addRange(fieldNum(CustTrans, TransDate));
queryRun = new QueryRun(query);
}

If a class has data in Usage data, new objects are initialized with the unpack
method.

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

3-33

Development III in Microsoft Dynamics AX 2012


QueryRun
The queryRun() method returns the handle to the queryRun. The method usually
resembles the following content.
public QueryRun queryRun()
{
return queryRun;
}

ShowQueryValues
The showQueryValues() method indicates if the query should be active in the
dialog. If the user must access the query in the dialog, override this method and
return true.
public boolean showQueryValues()
{
return true;
}

This method should now include the information stored in the queryRun object.
Do this by adding the result of the queryRun.pack() method to the container:
public container pack()
{
return [#CurrentVersion, queryRun.pack()];
}

When combining dialog fields with a query, the first container still contains the
#CurrentList macro:
public container pack()
{
return [#CurrentVersion, #CurrentList,
queryRun.pack()];
}

The unpack() method re-initializes the queryRun object from the saved
information. Do this as shown by the following content.
public boolean unpack(container _packedClass)
{
Version version = RunBase::getVersion(_packedClass);
container packedQuery;
switch (version)
{

3-34

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 3: Classes
case #CurrentVersion:
[version, packedQuery] = _packedClass;
if (packedQuery)
{
queryRun = new QueryRun(packedQuery);
}
break;
default :
return false;

}
return true;

When combining dialog fields with a query, the unpack of the container contains
the #CurrentList macro.
[version, #CurrentList, packedQuery] = _packedClass;

Run
The run() method can use the queryRun object directly as it is initialized after
the user has accepted the dialog.
The run() method is implemented as shown in the following content.
public void run()
{
AmountMST amountMST;
CustTrans custTrans;
while (queryRun.next())
{
custTrans = queryRun.get(tableNum(CustTrans));
}
}

amountMST += custTrans.AmountMST;

info(strFmt("Sum equals %1", amountMST));

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

3-35

Development III in Microsoft Dynamics AX 2012


When this version of the class is run from a menu item, the dialog has the query
select button and criteria preview, as shown in the following figure:

FIGURE 3.2 DIALOG WITH QUERY

3-36

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 3: Classes

Lab 3.4 - Make a RunBase Class


Scenario
You have been asked to create a function that will quickly calculate the average
sales price of an item, between a range of dates. This is a key business indicator
for your company.

Challenge Yourself!
Make a new class called SalesCalcAverageSalesPrice, using the RunBase
framework. The class should prompt for an Item Number, and To and From
dates. The run() method should calculate the average sales price of all sales lines
for that Item, with order creation dates between the dialog To and From dates.
Put the result in the infolog.

Step by Step
1. In the AOT create a new class called SalesCalcAverageSalesPrice.
2. Add the following code to the classDeclartion
3. Override the methods run, pack, unpack, dialog and getFromDialog
and enter the code shown below.
4. Create a new method called main and enter the code shown below.
5. Save your changes to the class.
6. Right-click on the class and select Open.
7. Enter from and to dates in the dialog box and enter an item id.
8. Verify the results are what you would expect.
class SalesCalcAverageSalesPrice extends RunBase
{
DialogField dlgFromDate, dlgToDate, dlgItemId;
FromDate fromDate;
ToDate toDate;
ItemId itemId;
}
protected Object dialog()
{
Dialog dlg = super();
dlgFromDate = dlg.addField(identifierStr(FromDate));
dlgToDate = dlg.addField(identifierStr(ToDate));
dlgItemId = dlg.addField(identifierStr(itemId));
return dlg;
}

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

3-37

Development III in Microsoft Dynamics AX 2012


public boolean getFromDialog()
{
boolean ret;
fromDate = dlgFromDate.value();
toDate = dlgToDate.value();
itemId = dlgItemId.value();
ret = super();
}

return ret;

public container pack()


{
return conNull();
}
public boolean unpack(container packedClass)
{
return true;
}
public void run()
{
CustInvoiceTrans custInvoiceTrans;
select sum(SalesPrice), sum(Qty) from custInvoiceTrans
where
custInvoiceTrans.ItemId == ItemId
&&
custInvoiceTrans.InvoiceDate >= fromDate
&&
custInvoiceTrans.InvoiceDate <= ToDate;
if (custInvoiceTrans.Qty)
info(strFmt("Average sales price is %1",
custInvoiceTrans.SalesPrice / custInvoiceTrans.Qty));
else
info("No sales found");
}
static void main(Args _args)
{
SalesCalcAverageSalesPrice SalesCalcAverageSalesPrice
= new SalesCalcAverageSalesPrice();
if (SalesCalcAverageSalesPrice.prompt())
SalesCalcAverageSalesPrice.run();
}

3-38

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 3: Classes

Args Object
The Args class defines information communicated between running application
objects. The application object that initiates the communication (the caller),
initializes an Args object containing some information and then passes it to the
application object that is being called.
Information transferred by the args object includes the following:
Method

Data Type

Information

record

Common

A handle to the record that the caller is


currently using. This information is
typically used to access the values of the
caller's record.

parmEnumType

Int

The Id of the enum type that is specified


on the parmEnum.

parmEnum

AnyType

An enumerated value.

parmObject

Object

A handle to an object that the caller wants


to transfer to the called object. This
option can be used to transfer any object.

parm

Str

A string which can be used to transfer


miscellaneous information. It is best
practice not to use this method. Try to
build the communication on the other
parm methods available.

Communication through the args object can occur automatically without any
X++ programming.
If the caller is activating the called object by a menu item the Args object is
automatically initialized and sent as a parameter to the object called. AOT
properties of the menu item will be used.
If the called object is a form or a report, it can automatically apply to the
information send by the Args object. Forms will automatically try to make a
delayed synchronize with the args.record().
However, you can program one or both sides of the communication yourself.

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

3-39

Development III in Microsoft Dynamics AX 2012


The following is an example of code on the caller side
(\Classes\LedgerConsolidate\dialog).
Object dialog()
{
Args args;
FormRun formRun;
mayBeExecuted = false;
args = new Args();
args.name(formStr(LedgerConsolidate));
args.caller(this);
formRun = ClassFactory::formRunClassOnClient(args);
formRun.init();
return formRun;
}

This code shows an example of how to replace a dialog object with a form from
the AOT, within the RunBase framework. The form called can access the
LedgerConsolidate class through args.caller(). This way, the form and class can
exchange information.
The following is an example of programming the called side
(Classes\BankChequeCancel\main).
public static void main(Args args)
{
BankChequeCancel chequeCancel;
Args localArgs = new Args();
;
if (args.record())
{
switch (args.dataset())
{
case tablenum(BankChequeTable) :
chequeCancel =
BankChequeCancel::newBankChequeTable(args.record());
break;
default :
throw
error(strfmt("@SYS22828","@SYS22512"));
}
if (chequeCancel.prompt())
{
localArgs.caller(chequeCancel);
localArgs.record(args.record());

3-40

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 3: Classes

BankChequeCancel::serverRun(localArgs);

Notice that this method also declares, initializes, and uses a local Args object for
calling the static method BankChequeCancel::serverRun().

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

3-41

Development III in Microsoft Dynamics AX 2012

Lab 3.5 - Using Args


Scenario
You have been asked to add the average sales price calculation to the Released
products form. The current item id in the form should be used for the item id in
the calculation

Challenge Yourself!
Modify the class SalesCalcAverageSalesPrice so when it is called with an active
InventTable record, the class should set the item id based on this record and not
prompt the user for the item id.

Step by Step
1. In the AOT, find the SalesCalcAverageSalesPrice class.
2. Add a new method parmItemId as shown below.
3. Modify the main, dialog and getFromDialog methods as shown
below.
4. Drag the class to the Action node of the Menu Items node in the
AOT.
5. In the property sheet of the menu item just created, set the label to
Average sales price.
6. In another AOT, find the form
EcoResProductPerCompanyListPage.
7. Drag the menu item to the node Designs > Design > ActionPane >
ActionPaneTabSell > ButtonGroupSellOrders.
8. Saves your changes to the form.
9. In the application workspace navigate to Product information
management > Common > Released products.
10. Select item 1001 and click Sell > Average sales price.
11. Enter a From date and a To date and click OK.
12. The infolog will display the average sales price for that item. Click
OK to close the infolog.
private itemId parmItemId(itemId _itemId = itemId)
{
itemId = _itemId;
return itemId;
}
static void main(Args _args)
{
SalesCalcAverageSalesPrice SalesCalcAverageSalesPrice
= new SalesCalcAverageSalesPrice();
InventTable inventTable;

3-42

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 3: Classes
if (_args.dataset() == tableNum(InventTable))
{
InventTable = _args.record();
SalesCalcAverageSalesPrice.parmItemId(InventTable.ItemId);
}

if (SalesCalcAverageSalesPrice.prompt())
SalesCalcAverageSalesPrice.run();

protected Object dialog()


{
Dialog dlg = super();
dlgFromDate = dlg.addField(identifierStr(FromDate));
dlgToDate = dlg.addField(identifierStr(ToDate));
if (!itemId)
dlgItemId = dlg.addField(identifierStr(itemId));
}

return dlg;

public boolean getFromDialog()


{
boolean ret;
fromDate = dlgFromDate.value();
toDate = dlgToDate.value();
if (!itemId)
itemId = dlgItemId.value();
ret = super();
}

return ret;

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

3-43

Development III in Microsoft Dynamics AX 2012

Summary
Using system classes in conjunction with your modifications will help leverage
existing frameworks, and access important system data, to ultimately provide a
richer and more consistent Microsoft Dynamics AX enhancement for end users.

3-44

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 3: Classes

Test Your Knowledge


Test your knowledge with the following questions.
1. Which of the following are Collection classes? (Select all that apply)
( ) Struct
( ) Map
( ) Container
( ) List
2. What class is used to iterate through the elements stored in a Map object?

3. RecordSortedList and RecordInsertList have some similarities but have


different uses. Which of the following is a major difference between the two
classes?
( ) RecordInsertList lacks the sort order features that are available in
RecordSortedList.
( ) RecordSortedList lacks the sort order features that are available in
RecordInsertList.
( ) RecordInsertList cannot insert multiple records into the database in
one trip.
( ) RecordSortedList cannot insert multiple records into the database in
one trip.
4. Match the following Application Object classes to their description:
_____ 1. Form
_____ 2. FormRun
_____ 3.
FormBuildDesign
_____ 4.
FormBuildDataSource
_____ 5.
FormBuildStringControl

a. This class contains property methods for


the properties on a form data source. Use
this class to manipulate the properties on a
form data source by using X++ code.
b. This is not a real class.
c. This class contains property methods for a
form tree control.
d. This is not a real class.

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

3-45

Development III in Microsoft Dynamics AX 2012


_____ 6.
FormBuildTreeControl
_____ 7.
FormQueryDataSource
_____ 8. FormExecute

e. This class contains property methods for


the properties on the Design node for a
form and methods for creating controls.
f. This class contains methods for executing a
form.
g. This class contains property methods for a
form string control.
h. This class contains property methods for
the form name and other form properties
and methods that enable addition of data
sources and controls to the form.

5. What does the ClassFactory.formRunClass() method do?

6. In which situations are the pack/unpack methods used in the RunBase


framework.

7. Which method on Args should only be used when no other methods will
suffice?
( ) parm()
( ) parmEnum()
( ) parmEnumType()
( ) record()

3-46

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 3: Classes

Quick Interaction: Lessons Learned


Take a moment and write down three key points you have learned from this
chapter
1.

2.

3.

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

3-47

Development III in Microsoft Dynamics AX 2012

Solutions
Test Your Knowledge
1. Which of the following are Collection classes? (Select all that apply)
() Struct
() Map
( ) Container
() List
2. What class is used to iterate through the elements stored in a Map object?
MODEL ANSWER:
MapEnumerator.
MapIterator is also correct, but a secondary choice.
3. RecordSortedList and RecordInsertList have some similarities but have
different uses. Which of the following is a major difference between the two
classes?
() RecordInsertList lacks the sort order features that are available in
RecordSortedList.
( ) RecordSortedList lacks the sort order features that are available in
RecordInsertList.
( ) RecordInsertList cannot insert multiple records into the database in
one trip.
( ) RecordSortedList cannot insert multiple records into the database in
one trip.

3-48

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 3: Classes
4. Match the following Application Object classes to their description:
h 1. Form
f 2. FormRun
e 3. FormBuildDesign
a 4.
FormBuildDataSource
g 5.
FormBuildStringControl
c 6.
FormBuildTreeControl
b 7.
FormQueryDataSource
d 8. FormExecute

a. This class contains property methods for


the properties on a form data source. Use
this class to manipulate the properties on a
form data source by using X++ code.
b. This is not a real class.
c. This class contains property methods for a
form tree control.
d. This is not a real class.
e. This class contains property methods for
the properties on the Design node for a
form and methods for creating controls.
f. This class contains methods for executing a
form.
g. This class contains property methods for a
form string control.
h. This class contains property methods for
the form name and other form properties
and methods that enable addition of data
sources and controls to the form.

5. What does the ClassFactory.formRunClass() method do?


MODEL ANSWER:
Given a reference to a form in the AOT, this method will return a FormRun
object, which is a runnable form object.
6. In which situations are the pack/unpack methods used in the RunBase
framework.
MODEL ANSWER:
To save and restore user input and to transfer the object from the client to the
server.
7. Which method on Args should only be used when no other methods will
suffice?
() parm()
( ) parmEnum()
( ) parmEnumType()
( ) record()

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

3-49

Development III in Microsoft Dynamics AX 2012

3-50

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 4: Forms

CHAPTER 4: FORMS
Objectives
The objectives are:

Identify the main sections that make up a form.

Add data sources to a form to define what data is displayed by the


form.

Add controls to a form to display data.

Modify form methods to the control how the form behaves when it
opens and closes.

Make decisions about where to place the code.

Make runtime modification of the fetch of data.

Introduction
This lesson provides a comprehensive foundation for using forms to interact with
the end-user.
The following topics will be discussed:

Forms architecture

Fetch of data

Code placement in forms

Advanced controls

Special forms

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

4-1

Development III in Microsoft Dynamics AX 2012


Scenario
Isaac, the systems developer, is required to create a new form that has multiple
data sources linked together, fields that are calculated from other fields, fields
that are not linked to a data source, and to initialize the fields on the form when
the form opens. He has also been asked to investigate other types of forms and
form controls.

Architecture
A form supports the interaction between the user and the database. It focuses on
the following actions.

Displaying and receiving data

Filtering data

Sorting data

Modifying data

The application and business logic are not integrated in forms but programmed
on the underlying tables and classes.
The following figure illustrates the structure of a form as displayed in the AOT.

FIGURE 4.1 THE SECTIONS OF A FORM

A form consists of five components in the AOT:

Methods

Data sources

Parts

Designs

Permissions

All of these components have been described in previous Microsoft Dynamics


AX 2012 development courses. This course goes in to more detail about the three
main sections - Methods, Data sources and Design.

4-2

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 4: Forms
Methods
System generated methods, also known as system methods, standard methods or
object methods, attached to the form are used to control the startup and shut
down of the form. These events are frequently overridden to make initialization
of the form. By default, the system methods are not displayed. To override a
system method, right-click on the Methods node, select Override Method and
then select the methods to override.
Other methods can be added and called from anywhere in the form. It is good
practice to add methods here rather than writing large chunks of code in the
objects deeper in the form design, as it makes locating code simpler.
Variables defined in the class declaration of the individual form can be accessed
from all methods in the form and can be used to hold the status of the form
controls.
The following illustration shows the CustTable form methods. The form system
methods task, run, init and close have been overridden. All other methods have
been added to the Methods node and are specific to this form.

FIGURE 4.2 CUSTTABLE FORM METHODS

Data Sources
Data source(s) define the interface between the form and the database. An entry
is added for each table. The properties of the individual data sources determine
the relationship between the data sources. Each data source also has system
methods that can be overridden to control the display of data or act on an event.

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

4-3

Development III in Microsoft Dynamics AX 2012


The data source contains all fields in the table. Again, all fields have properties
and system methods that can be overridden. These methods control the following
functions.

Field specific navigation (filter, lookup, jump to main table and


more)

Validating the field

Modifying the field

Use the list of fields as a source for drag-and-drop operation when you build the
design of the form. You cannot drag fields directly from the underlying table as
the form must know which data source the field is linked to.
Field groups display below the list of fields. They can also be used as the source
for drag-and-drop operation.
Notice that after accessing the form in the AOT the definition of the data source
is cached locally in the definition of the form. If changes are made when you
create a new field in the underlying table, you must restore the form to access the
new field in the form. Remember to save the form before clicking Restore from
the shortcut menu.
The following illustration shows the data source on the CustTable form.

FIGURE 4.3 DATASOURCES ON CUSTTABLE FORM

Design
The design of the form defines the interface between the form and the user and
controls the layout and presentation of the data. The design consists of
components called controls. Some controls are used to edit and display data,
while others are used to control the layout of the form.

4-4

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 4: Forms
The following illustrates the normal design of a form:
Design
Tab
TabPage
Grid
Fields to be displayed on Grid
TabPage
Field Groups
Fields in Field Group
All controls on the design have properties and methods that can be overridden.
Under the Designs node, as well as the Design node, there is also a DesignsList
node. Design is the hierarchy of controls built from the DesignList using the
HierarchyParent and ElementPosition properties. DesignList is the flat list of all
controls that populate metadata store.

Handles to the Objects in a Form


When programming in a form you can reference the individual objects, for
example the data sources or controls of the form, using the kernel classes used to
build them, or by overriding the methods on the object itself.
All methods in the form are related to an object. The object can be accessed from
the methods associated with it, by using the handle this. Note that this reference
is relative to where you are programming.
When referencing an object from a method that is not associated with that object,
you need to use the object handle. The following table lists the objects and how
to access them:
Object

Access from X++

FormRun

Element

FormDataSource

<name of data source>_DS

Active record in data


source

<name of data source>

FormDataObject

FormDataSource.Object(<fieldId>)

FormDesign

FormRun.design()

FormControl

Name assigned to control, when property


AutoDeclaration is set to Yes.
or
element.control(Control::<name of control>)

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

4-5

Development III in Microsoft Dynamics AX 2012


Object

Access from X++

Query

<name of data source>_Q


or
FormDataSource.Query()

QueryRun

<name of data source>_QR


or
FormDataSource.QueryRun()

The method control() on the element object can be used if the control is included
in an auto field group. In this situation you cannot use the auto declaration
feature.

Data Sources
Adding a data source to a form allows the form to display the data from the table
specified on the data source. A table in a data source can be sorted and filtered by
the user, which is an important feature of Microsoft Dynamics AX.
Forms can contain any number of data sources, and each data source can be
joined to another data source on the form using the defined relations between the
tables. Relations or Dynalinks can also be added in the form code.
A form data source produces a query that can be manipulated the same as any
other query in the system.

Joins
To display records from two tables on one form, you can limit the records in one
of the tables to the related records in the other table. You can do this by
specifying a join between the two tables.
The join is specified on the JoinSource property of the second or joined table.
Enter the data source name of the main data source here.
You can also specify the type of join or link. The types of link are described later
in the course.

4-6

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 4: Forms
An example of a form that uses join is the form CustInvoiceJour. The
CustInvoiceTrans data source is joined to the CustInvoiceJour datasource, and
the InventDim data source is joined to the CustInvoiceTrans datasource. The
following figure shows the queries in the form and the properties of the
InventDim datasource.

FIGURE 4.4 CUSTINVOICEJOUR FORM DATASOURCE JOINS

The InventDim join to CustInvoiceTrans uses an InnerJoin because the data is


to be displayed in the same grid as the CustInvoiceTrans record.

Reference Data Sources


Reference data sources are data sources that are joined to the parent data source
through a surrogate key relation - the RecId relation.

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

4-7

Development III in Microsoft Dynamics AX 2012


By adding the surrogate key field from the data source to the form design, the
Reference data source is automatically created, and the AutoIdentifcation field
group on the reference data source table is used instead of the surrogate key.

FIGURE 4.5 REFERENCE DATA SOURCE ON PURCHTABLE FORM

Modification from X++


Use the query object to modify the query of a form data source. The query object
can be retrieved using either <name of data source>_Q or
FormDataSource.Query()
To make a permanent modification of the query, this is typically implemented in
FormDataSource.Init() after the call to super().
To filter records in a form perform the following steps.
1. In the ClassDeclaration, declare the relevant QueryBuildRange or
QueryFilter objects.
2. In FormDataSource.Init, initialize the range object.
3. In FormDataSource.ExecuteQuery, assign the actual values to the
ranges before call of super().
Manipulate the sorting of data by adding sortfields or an index to the data source
of the query. As an alternative, specify an index in the properties of the data
source.

4-8

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 4: Forms
Combine the sorting with some aggregated fields, which makes the data source
display aggregate information from the table instead of transactions. Perform the
following steps to show the sum of quantity of inventory transactions shown per
Item Id:
1. Group the data by item id using the addGroupByField on the
datasource.
2. Add Sum(Qty) as a SelectionField

Common Methods to Override


FormDataSource.Init
This method initializes the data source and is called from the super() of
FormRun.Init(). The method is only called once when the form opens.
The main task for this method is to initialize the query used for fetching data.
To modify or replace the query automatically created by the form, do this after
the super() call of this method.

FormDataSource.InitValue
This method is used to initialize a new record with default values. The super() of
this method calls the initValue() method on the underlying table.
If you have initialization that applies system-wide, put the code on the table.

FormDataSource.Active
This event is called when a new record becomes active in the data source. This
method is typically overridden to change properties which depend on the
contents of the current record: Commonly this method will:

Modify permissions to the data source

Modify permissions to the fields

Enable/Disable buttons

FormDataSource.LinkActive
This method is the engine that joins data sources. This method is called on the
joined data source every time that the active record in the main data source is
changed. The method is also called when the form is opened as the system tries
to join the calling data source to the main data source of the called form.

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

4-9

Development III in Microsoft Dynamics AX 2012


FormDataSource.ValidateWrite
This method validates an insert or update of a record.The super() of this method
calls the corresponding method on the underlying table.
If you need to distinguish between an insert and update, make a condition on the
RecId field, which only has a value if it is an update.

FormDataSource.Write
This method controls the insert and update of records. The super() of this method
calls the corresponding method on the underlying table.
If you have form-specific tasks to perform in relation to the commitment of the
record, add it here.

Example of Modifying a Data Source Query


The following code illustrates the necessary modifications on a form that
contains one data source related to the table InventTrans:

public void init()


{
QueryBuildDataSource dataSource;
super();
dataSource =
this.query().dataSourceTable(tableNum(InventTrans));
dataSource.addGroupByField(fieldNum(InventTrans,
ItemId));
dataSource.addSelectionField(fieldNum(InventTrans,
Qty), SelectionField::Sum);
}

4-10

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 4: Forms
List of Datasource Properties
The following list describes each property available on a form data source. The
list can also be found in the Developer Help.
Property

Description

Name

The Name of the data source is used when referencing the


data source from code and also when setting the data source
for a control in the design.

Table

Sets the table used in the data source

Index

Use this property to specify the sorting of the data. It affects


the query that is created automatically.
When no index hints are specified, the database
management system locates an applicable access path. It is
based on the information in the supplied query.
The sort order for a form can be changed by a user by using
the query dialog.

CounterField

Use this property to specify a real field in the table that is


used to save the sequence of the records. This option is used
to let the user control the sequence of data. This field is
usually called LineNum.

AllowCheck

Specifies whether security checking occurs early, before the


form opens.
Yes Before the form opens, check that the user has access
to the form. A message is displayed if the form cannot open.
Yes is the default, and is generally recommended.
No After the form opens, check whether the user has
access to the form. If the user lacks access to the form, or to
the underlying data sources, the form does not display any
data.

AllowEdit,
AllowCreate,
AllowDelete

These properties are used to reduce the access to data. You


can only reduce the rights of the current user in relation to
the general security system.
Note that the AllowEdit also affects edit methods. You can
also set this property individually on fields.

StartPosition

This option specifies if the form sets the first or the last
record active, when the form is opened. Only change this
property when you have to as the user pays a performance
penalty by finding the last record.

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

4-11

Development III in Microsoft Dynamics AX 2012


Property

Description

AutoSearch,
AutoNotify,
AutoQuery

The properties specify whether the form:


Automatically creates a query (AutoQuery)

Performs the initial search (AutoSearch)

Notifies the system when the active record is changed


(AutoNotify)

This last option is necessary if other data sources are linked


to this data source.

4-12

Cross
Company
AutoQuery

Specifies whether the data source retrieves data from more


than one company database. To specify which companies to
search, the query.addCompanyRange(<company id>)
method should be called before the query is executed.

OnlyFetch
Active

This option can be used to include a field list that only


contains the fields displayed.
NOTE: Be careful with this option if the data could be
changed or used as parameter to method calls. Normally
this option is only used in lookup form and for tables that
contains large fields like containers.

JoinSource

Use the LinkType to select whether the data source is


joined to another data source. The joining of data is a
mechanism which automatically limits the data in one data
source to the data displayed in another data source (the
controlling data source). This requires a relation between the
two tables.
An example is the SalesTable form that contains both Sales
orders and lines. When a new sales order is selected the data
source with the lines only shows the lines related to that
sales order.

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 4: Forms
Property

Description

LinkType

The LinkType property determines how two data sources


are joined. The following list describes each option:
Passive: The query on the joined data source is only
executed when the form is opened. A later change in the
controlling data source does not change the view.

Delayed: The query on the joined data source is


executed every time that the controlling data source is
changed. The query execution is delayed to avoid the
fetch of data, if the controlling data source is changed
multiple times in a short time. This is the case when the
user is scrolling through data on a grid.

Active: This option is similar to Delayed, except there is


no delay before the query is executed.

InnerJoin: Selects records from the main table that have


matching records in the joined table and vice versa. If
the joined table does not have any records related to the
main table record, the main table record is not
displayed. Each match returns a result set of the main
table record and joined table record joined together as
one record. This is useful when wanting to display
records from both tables in a grid.

OuterJoin: Selects records from the main table whether


they have matching records in the joined table. Each
match returns a result set of the main table record and
joined table record joined together as one record. If
there is no match, the fields from the joined table will be
empty.

ExistsJoin: Selects a record from the main table only if


there is a matching record in the joined table. As soon as
a matching record is found, the main table record is
returned. The record in the joined table is never
retrieved.

NotExistsJoin: Select records from the main table that


do not have a match in the joined table.

HINT: Use the form tutorial tutorial_Form_Join to see the


different results produced when you change the LinkType
property.

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

4-13

Development III in Microsoft Dynamics AX 2012


Property

Description

DelayActive

This enables you to delay the execution of the data source


active method. If this property is set to Yes, the active
method is activated only after a delay of 20 milliseconds.
When a user scrolls through a data source, the active method
is not called on every record, but only on the final record
that the user selects

InsertAtEnd,
InsertIf
Empty

These properties are used to specify whether the form


automatically creates a new record in the following
situations:
If the user browses past the last record in the data source
(InsertAtEnd)

If the forms do not find any data (InsertIfEmpty)

The new record is saved in the database only if the user


changes any of the fields manually. The automatic creation
of a new record in the two situations is similar to the event
related to CTRL+N.
Optional
RecordMode

4-14

Specifies the create and delete behavior for records on an


outer joined table.
This can be set to one of the following options:
ImplicitCreate When no record is saved in the
database, create an outer joined record and joined tables
as soon as the parent record becomes active. If the outer
joined record or its children are not changed, they will
be deleted when the parent record is no longer active.

ExplicitCreate When no record is saved in the


database, treat this record as disabled until the user
explicitly triggers creation with the Optional Record
checkbox. When the record exists, unchecking the
checkbox will delete the record.

None No special create or delete happens with an


outer joined record.

ValidTime
StateAuto
Query

Specifies the types of queries for date effectivity. (AsOfDate


and DateRange)

ValidTime
StateUpdate

Specifies the types of updates for date effectivity.

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 4: Forms
List of Datasource Field Properties
The following list describes each property available on each field on a form data
source. The list can also be found in the Developer Help.
Property

Description

AllowEdit

This property specifies whether the field can be changed. It


cannot remove a restriction imposed by the table field,
security settings, or the data source.

Enabled

This property specifies whether the user can access the


control. If the field is disabled it is dimmed and the user
cannot give focus to the control.

Skip

Skip removes the control from the sequence that is used by


pressing TAB in the form.

Visible

This property can be used to remove a field from the user


interface. The same property exists on the database field
which should be used if the option applies across the entire
system.

AllowAdd

This property specifies whether the field can be added to the


design by the user. There are three options:
No: The user cannot add this field

Mandatory

Restricted: The user can add this field and edit it if a


copy of the field already exists in the form and it can be
edited. For example, if a user wants to move a copy of a
field into a grid on the first tab page and the field already
exists on another tab page within the form. This is the
default value.

Yes: This field can be added freely.

This option determines whether it is required to fill in data in


this field. The same property exists on the database field
which should be used if the option applies across the entire
system.

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

4-15

Development III in Microsoft Dynamics AX 2012

Lab 4.1: Create a form


Scenario
You have been asked to create a new form to show a list of customer invoices
that have been posted within the last month.
The list needs to have the following fields: Customer Account, Customer Name,
Customer Default Currency, Invoice Id, Invoice Date and Invoice Amount. You
should be able to filter and sort by any of the fields in the list.
The list must include a line for every invoice that has been posted, but not show
any customers that have not been invoiced this month.

Challenge Yourself!
Create a new form that has two data sources, CustTable and CustInvoiceJour.
CustInvoiceJour should be joined to CustTable. Ensure the join displays all
invoices but not customers that have not been invoiced. Add a filter that only
retrieves records posted after the first day of the current month.
You may have to post some invoices to get some that were during this month.

Step By Step
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.

4-16

Create a new form.


Add a data source that uses CustTable.
Add a data source that uses CustInvoiceJour.
Set the JoinSource property on the CustInvoiceJour data source to
CustTable.
Set the LinkType property on the CustInvoiceJour data source to
InnerJoin.
Add a Grid control to the Design node.
Drag the fields AccountNum, Currency from the CustTable data
source to the Grid control.
Add a StringEdit control to the grid and set the data source property
to CustTable and the DataMethod property to Name.
Drag the fields InvoiceId, InvoiceDate and InvoiceAmount from
the CustInvoiceJour data source to the GridControl.
Add the following code to the system methods on the form and the
data source. Note that the first method is the ClassDeclaration.

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 4: Forms
public class FormRun extends ObjectRun
{
QueryBuildRange
QBRInvoiceDate;
}
// this is the init method on the CustInvoiceJour data
source
public void init()
{
super();
QBRInvoiceDate =
this.query().dataSourceTable(tableNum(CustInvoiceJour)).add
Range(fieldNum(CustInvoiceJour, InvoiceDate));
}
// this is the executeQuery method on the CustTable
dataSource.
public void executeQuery()
{
fromDate
fromDate =
mkDate(1,mthofyr(systemDateGet()), year(systemDateGet())) ;
QBRInvoiceDate.value(sysQuery::range(fromDate,
systemDateGet()));
}

super();

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

4-17

Development III in Microsoft Dynamics AX 2012

Form Controls
Form controls refers to all of the objects in a form design. Tabs, tab pages, field
groups, grids, fields and buttons are all examples of form controls. Controls are
arranged on the form using Intellimorph technology.
All data is displayed through field controls. As with fields on a table, each field is
a specific data type, and can also be an Extended Data Type. There are three
types of field controls: Bound, Unbound and Calculated.
Bound controls are associated with a field in an underlying table. Use bound
controls to display, enter, and update values from fields in the database.
Unbound controls do not have a data source, but can be a specific Extended Data
Types.
Calculated controls use a method as the data source. An example of a calculated
control is the sum of two fields on a form.

IntelliMorph
IntelliMorph is a technology that simplifies the modification of forms. For
example, IntelliMorph lets users hide and show fields. Individual users can safely
and swiftly rearrange fields to best suit their work preferences without modifying
the form through the AOT (Application Object Tree). IntelliMorph also makes it
possible to quickly redesign forms. Field labels can be modified without
disturbing field data or underlying business logic.
When the form is displayed the width of the labels are adjusted to the actual
language. Fields that are not active because of configuration or security are
removed. The formatting of date, time and numbers is performed according to the
settings in the operating system.
The design of the form should be made to support IntelliMorph. This includes the
following guidelines for the properties on the controls:

4-18

Limit the change of properties to a minimum. A property on a


control shadows a later system-wide modification at the dictionary
level.

Do not put controls at absolute coordinates. This prevents the form


from rearranging the fields if they are added or removed.

Use the values column width and column height for the properties
width and height to support manual resizing of the form.

Use database field groups. This enables a later modification to the


forms at the dictionary level.

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 4: Forms
Display Methods
A display method is used to display a calculation or data from another table. The
method modifier Display is added to the beginning of the method definition line
to allow the returned value to be displayed on a form (or report).
Display methods can be created either on a table or on a form data source. If it is
created on the form data source, then it takes the table as a parameter. If possible
a display method should be created on the table itself, but there are situations
where it has to be on the form data source, for example, if it uses an unbound
control on the form.
The following example is from the InventJournalTable form, and displays the
user ID of the user that is locking a journal.
//BP Deviation documented
display JournalBlockInUseUserId inUseUserId(JournalTableMap
_journalTable)
{
return
journalFormTable.datasourceMethodInUseUserId(_journalTable)
;
}

Edit Methods
Edit methods are similar to display methods in that they can return calculated
values or data from another table. They can also be edited. An edit method takes
two more parameters than a display method. The first parameter is Boolean and
is True if the user has edited the field. The last parameter is the value that has
been entered in the case that a user has edited the field. If the edit method is on a
form data source, the second parameter is the table.
The following example is from the form CustTransOpen which is used to settle
transactions against each other. This method is used to mark which transactions
are to be settled.
// BP Deviation documented
public edit NoYes editMarkTrans(boolean _set, CustTransOpen
_custTransOpen, NoYes _markTrans)
{
NoYes returnMarkTrans;
;
if (_set)
{
manager.updateTransMarked(_custTransOpen,
_markTrans);
element.updateDesignDynamic();
this.refresh();
}

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

4-19

Development III in Microsoft Dynamics AX 2012


returnMarkTrans =
manager.getTransMarked(_custTransOpen);
}

return returnMarkTrans;

Unbound Controls
Unbound fields are fields of a specific type, but not attached directly to a table.
These fields inherit the properties of the data type including any relations to a
table. An unbound control can be used for filtering record to be displayed or to
display images or static text.
The following illustration shows the properties of an unbound control in which
an item ID can be entered. The lookup of the item table is still available through
the relations on the Extended Data Type.

FIGURE 4.6 AN UNBOUND CONTROL

4-20

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 4: Forms
Common Methods to Override
FormDataObject.Modified
Use this method when the user changes the contents of the field. This event can
be used to make calculations or other operations which depend on the field.
For system-wide functionality during a manual change of a database field,
override the modifiedField method on the table.

FormDataObject.Validate
Use this method to validate the contents of the field. This event can be used to
supplement or remove the ordinary validation.
For system-wide functionality during the validation of a database field,
implement this by overriding the validateField method on the table.

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

4-21

Development III in Microsoft Dynamics AX 2012

Lab 4.2 - Use Unbound Controls


Scenario
You have been asked to modify the form created in Lab 4.1 so that the user can
enter a date in a field on the form. The form will then display all invoices posted
after that date.

Challenge Yourself!
Add a new date control field to the form, and filter the records based on this date.

Step by Step
1. Add a new DateEdit control to the design of the form created in Lab
4.1. Rename the control to FromDate.
2. Open the properties of the FromDate control.
3. Set the ExtendedDataType property to FromDate.
4. Set AutoDeclaration to Yes.
5. Close the properties form.
6. Expand the FromDate node.
7. Right-click on the methods node and select Override Method >
Modified.
8. Modify the code as shown below.
9. Edit the ExecuteQuery() method on the CustTable data source.
10. Modify the code as follows.
public boolean modified()
{
boolean ret;
ret = super();
CustTable_ds.executeQuery();
}

return ret;

public void executeQuery()


{
QBRInvoiceDate.value(sysQuery::range(fromDate.dateValue(),
systemDateGet()));
super();
}

4-22

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 4: Forms

Form Methods
Form methods control how the form opens, runs and closes. These methods can
be overridden as with other form system methods.

Common Methods to Override


FormRun.Init
This method initializes the form and its objects. This is the first event you can
override in connection with the startup of the form.
Never remove the super() call from this method, as it removes the initialization of
all the objects.
To make some manual initializations, formRun.init is typically used. This could
include the following tasks:

Verification of the contents of the args-object received

Initialization of supporting classes

Dynamically changes to the design

If your manipulation needs access to the objects of the form, wait until the
super() call has executed.

FormRun.Run
The run method is called immediately after the init method. The super() call
makes the form window appear on the screen and performs a database search for
the data to be displayed in the form.
Typical reasons for overriding the formRun.run() method include, activating or
de-activating some fields, setting default values for controls or modifying the
query.
You must call super() if you override formRun.run() method, and you should
place your code before the super() call.

FormRun.Close
This method shuts down the form. If you have cleanup in connection with
finishing the execution of the form, place it here.
One function available for implementation is when saving some options for
future use as in \Forms\ForecastPurch\Methods\close.

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

4-23

Development III in Microsoft Dynamics AX 2012


FormRun.ClosedOK
The formRun.closedOK() method is called when a Command Button with the
Type set to OK is clicked. The call to super() will close the form.
The formRun.ClosedOK() method is used when code is to be executed when
the user accepts the data that they have entered in the form is correct, and wants
to exit, as with many standard windows forms.

4-24

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 4: Forms

Lab 4.3 - Initialize a Form


Scenario
You have been asked to default the date field added in the Lab 4.2 to the first day
of the month.

Challenge Yourself!
When the form opens, the date field should be set to the first day of the current
month. Ensure that the records are then filtered to only show invoices posted
after this date.

Step by Step
1. Right-click on the methods node of the form you created in the Lab
4.1, select Override Method > Run.
2. Add the code to this method as shown below.
3. Save the method and run the form.
public void run()
{
FromDate.dateValue(systemDateGet() dayofmth(systemDateGet()) + 1);
super();
}

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

4-25

Development III in Microsoft Dynamics AX 2012

Placement of Code
The form offers many opportunities to place code on events. Try to minimize the
X++ code in forms for the following reasons:

Forms do not support inheritance. You cannot share logic


implemented in a form with other application objects.

The X++ code implemented in forms is always executed on the


client. This means that you cannot tune an application by specifying
where to execute the code.

To program system-wide logic, which is closely related to a record, put the logic
in object methods on the table. The table which inherits from the kernel class
xRecord has many events you can override and supplement with logic.
If designing logic which is more related to the function of the form, than the
individual records in the data source, consider making a class which supports the
form. If the form can work with different variations of functions or data, this
approach supports using polymorphism.
If putting the X++ code in the form, avoid programming on the design and put
your logic on the data source. Many events on the design are dependent on
exactly how the user navigates the form. Code on the form controls does not take
the possibility of the user modifying the contents of the form into account.

Additional Controls
The following introduces additional controls that can be used on forms.

Segmented Entry Control


Segmented entry controls are used to enter the main account and dimension
combinations that make up a segmented entry. To add a segmented entry control
to a form, drag the field that holds a foreign key to the
DimensionAttributeValueCombination table to the design of the form. This
creates a control of type Segment Entry. Next, override the following methods on
the form, data source and new form control.
<Form Methods>
public class FormRun extends ObjectRun
{
LedgerDimensionDefaultAccountController
ledgerDimensionDefaultAccountController;
}
public void init()
{
super();
ledgerDimensionDefaultAccountController =
LedgerDimensionDefaultAccountController::construct(myTable_

4-26

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 4: Forms
ds,
fieldstr(MyTable, LedgerDimension));
}

<Form Control methods>


public void jumpRef()
{
ledgerDimensionDefaultAccountController.jumpRef();
}
public void
loadAutoCompleteData(LoadAutoCompleteDataEventArgs _e)
{
super(_e);
ledgerDimensionDefaultAccountController.loadAutoCompleteDat
a(_e);
}
public void
segmentValueChanged(SegmentValueChangedEventArgs _e)
{
super(_e);
ledgerDimensionDefaultAccountController.segmentValueChanged
(_e);
}
public void loadSegments()
{
super();
// (Optional parm*() specification should go here, see
the Control options section.)
ledgerDimensionDefaultAccountController.parmControl(this);
ledgerDimensionDefaultAccountController.loadSegments();
}
public boolean validate()
{
boolean isValid;
isValid = super();
isValid =
ledgerDimensionDefaultAccountController.validate() &&
isValid;
return isValid;
}

<Form datasource methods>

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

4-27

Development III in Microsoft Dynamics AX 2012


public Common resolveReference(FormReferenceControl
_formReferenceControl)
{
return
ledgerDimensionDefaultAccountController.resolveReference();
}

ListView
The listview control is used to present the user with a list of options. Select one
or more of them. One example of using listview can be found in the selection of
payment file formats in the vendor module as shown in the following figure.

FIGURE 4.7 FILE FORMATS FORM

The forms tutorial_Form_ListControl and


tutorial_Form_ListControl_CheckBox illustrate many of the functions related
to the listview control and how they are programmed in X++.
NOTE: Examine the tutorials in Microsoft Dynamics AX. There are several
application objects in the AOT which are created to illustrate how to design and
implement special functions. All these application objects are prefixed with
'tutorial'.

4-28

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 4: Forms
Table
The table control looks like a spreadsheet. The grid is predefined with fields as
columns and records as row. In the table you control which type of data (control)
and which data (value) is presented in each cell.
The table control has related controls as a grid. Decide which of these controls
should be used in each cell of the table. Use the following two options for
specifying which control to use for each cell:

Override the method editControl() and return a handle to the


control. This method has the actual row and column as parameters.
The kernel calls this method when the information is needed.

Call the method cell() of the table control. Specify the actual row and
column as parameters. This returns a FormTableCell object. On this
object call the method editControl with a handle to the control as a
parameter. Use this to specify all the controls in a loop construction.

The specification of data in the table is performed in a similar manner.

Override the method data() and return the actual value of the cell.
The method is similar to the editControl().

Obtain the formTableCell object and call the method data().

The labels of the row and columns are specified by overriding the methods
rowLabel and colLabel of the table control.
An example of a table control is the form SysDateLookup which is used to
select a value in a date field.
The forms tutorial_Form_Table illustrates many of the functions related to the
table control and how they are programmed in X++.

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

4-29

Development III in Microsoft Dynamics AX 2012


Tree
The tree control is used to show hierarchical organized data as a bill of material,
projects, directory structure on a hard disc, and more.
An example from the standard application is the project table. Click Edit on the
All Projects list page.

FIGURE 4.8 TREE VIEW

The forms tutorial_Form_TreeControl illustrates many of the functions related to


the table control and shows how they are programmed in X++.
When working with tree structures with significant amount of nodes, only build
the visible part of the tree and extend the depth of the tree as the nodes are
expanded.

4-30

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 4: Forms
Window
The window control can be used to display images. An example of this is the
form displaying the company logo.

FIGURE 4.9 COMPANY LOGO

To display an image in a grid, you need to add a window control to the grid and
set the method to be a method that returns the resource Id of the image.
Use the class ImageList to build a list of images that the window can display and
then set that ImageList on the control.
Most resource Ids are listed in the macros ResAppl and Resource. You can add a
new image through the Resource node in the AOT.
For an example to show an image in a grid, examine the code in the Init form
method and the errorExists method in the LedgerJournalTrans datasource in the
LedgerJouranlTransDaily form. Also look at the form
Tutorial_Form_Windowingrid.

Managed Host
Microsoft Dynamics AX supports the display of .net managed controls. Actions
taken by the user on those controls are also handled. The following procedure
shows how to add a windows calendar control to a form and handle an event that
arises from a user action.
1. In the AOT, right-click on the Forms node and select New Form.
2. Rename the new form to DateDifferenceCalculator.
3. Right-click on the Design node and select New Control >
ManagedHost. The Managed control selector form appears.
4. In the list find the System.Windows.Forms record.
5. In the Controls list, select MonthCalendar. Click OK.
6. Rename the new ManagedHost control to CalendarControl.
7. Right-click CalendarControl and select Events. The Events form is
displayed.
8. Highlight DateSelected and click Add.
9. Click Close.

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

4-31

Development III in Microsoft Dynamics AX 2012


10. Open the Init method of the form. Note that code has been added to
initiate the control and also the DateSelected event handler.
11. Open the CalendarControl_DateSelected method. Add the
following code.
utcDateTime dt;
date selectedDate;
dt = e.get_Start();
selectedDate = DateTimeUtil::date(dt);
info(strFmt("Number of days until %1 is %2", selectedDate,
selectedDate - systemDateGet()));

12. Save your changes to the form.


13. Open the form and click on different dates to see the number of days
from now until that date.

ActiveX
The ActiveX control is used to host an ActiveX component. ActiveX is a
component of another program which is installed individually and can be
integrated in other applications such as Microsoft Dynamics AX or Web pages.
There are many ActiveX programs available.

4-32

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 4: Forms
When you create the control, a list of ActiveX controls installed on the actual
computer displays.

FIGURE 4.10 ACTIVEX BROWSER

Select from the list or click 'Enter class ID' to identify the ActiveX with unique
class ID.
An ActiveX control has auto declaration set to Yes as default, as you have to
address the control to access the specific methods of the ActiveX. When you
write X++ the specific methods are included in the list of methods.

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

4-33

Development III in Microsoft Dynamics AX 2012


ActiveX Methods
Open the ActiveX explorer from the context menu of the control. The explorer
lists all events and methods that are defined on the ActiveX.

FIGURE 4.11 ACTIVEX EXPLORER METHODS

The parameters to the methods and a help text is displayed in the window of the
explorer.

ActiveX Events
Add an event to the AOT by double-clicking the event. Afterward, you can edit
and delete the event directly from the methods-node of the control.
See the documentation of the individual ActiveX for a description of the
component.

4-34

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 4: Forms

Lab 4.4 - Add a window control


Scenario
You have been asked to add a visual alert to the invoice list form if an invoice
has not yet been paid. The grid should contain a field that has a Warning icon if
the invoice has not yet been fully settled.

Challenge Yourself!
Add a window control to the grid of the form created in Labs 4.1, 4.2, and 4.3. If
the invoice has been fully paid then the window should be blank otherwise the
window should show a Red Error icon.

Step by Step
1. Expand the designs node of the form created in Labs 4.1, 4.2, and
4.3.
2. Right-click on the Grid control, select New Control > Window.
3. In the properties of the new Window Control, rename it to
WindowOpen, set the DataSource to CustInvoiceJour, and set
AutoDeclaration to Yes.
4. Modify the ClassDeclaration as shown below.
5. Modify the run method as shown below.
6. Create a new method in the CustInvoiceJour data source and add
the code as shown below.
7. In the properties on the WindowOpen control, set the dataMethod
property to invoiceIsOpen.
8. Save and run the form.
public class FormRun extends ObjectRun
{
QueryBuildRange
QBRInvoiceDate;
imageListAppl
imageListAppl;
}
public void run()
{
#resAppl
imageListAppl = new ImagelistAppl(
Imagelist::smallIconWidth(), Imagelist::smallIconHeight());
imageListAppl.add(#ImageError);
windowOpen.imageList(ImageListAppl.imageList());
FromDate.dateValue(systemDateGet() dayofmth(systemDateGet()) + 1);

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

4-35

Development III in Microsoft Dynamics AX 2012


}

super();

//BP Deviation Documented


display ImageRes invoiceIsOpen(CustInvoiceJour
_CustInvoiceJour)
{
ImageRes
res = -1;
#resAppl;
if (!_CustInvoiceJour.custTrans().Closed)
res = imageListAppl.image(#ImageError);
return res;
}

4-36

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 4: Forms

Summary
This lesson discusses how to join data sources on a form, add controls that can
display the data from the data source as well as data from other tables and
calculations. It also discusses the methods and properties available when creating
a form and the additional options for control types and form types.

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

4-37

Development III in Microsoft Dynamics AX 2012

Test Your Knowledge


Test your knowledge with the following questions.
1. Which kernel class documents the individual field in a data source and how
do you get a handle to it?

2. Which methods do you override to make changes to the structure of the


query which fetches data to the data source?

3. What are the characteristics of a table control?

4-38

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 4: Forms

Quick Interaction: Lessons Learned


Take a moment and write down three key points you have learned from this
chapter
1.

2.

3.

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

4-39

Development III in Microsoft Dynamics AX 2012

Solutions
Test Your Knowledge
1. Which kernel class documents the individual field in a data source and how
do you get a handle to it?
MODEL ANSWER:
The kernel class FormDataObject documents fields on a data source. You
can get a handle by using FormDataSource.Object(<fielded>)
2. Which methods do you override to make changes to the structure of the
query which fetches data to the data source?
MODEL ANSWER:
You override FormDataSource.executeQuery and FormDataSource.init().
3. What are the characteristics of a table control?
MODEL ANSWER:
The developer can select which data type the rows and columns in the table
should have and the number of columns and rows.

4-40

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 5: Visual Studio Integration

CHAPTER 5: VISUAL STUDIO INTEGRATION


Objectives
The objectives are:

Explore the Application Object Tree (AOT) from Visual Studio.

Create a project in Visual Studio.

Write .NET managed code that uses X++ objects.

Deploy managed code.

Debug code using Visual Studio.

Introduction
With Visual Studio tools built specifically for Microsoft Dynamics AX
development, managed code that seamlessly uses elements from the AOT and
interacts with existing Microsoft Dynamics AX business logic can be written.

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

5-1

Development III in Microsoft Dynamics AX 2012


Scenario
Isaac, the systems developer, has been asked to write an event handler for a
Microsoft Dynamics AX event. Instead of X++, Isaac has been asked to write
this event handler in managed code, using Visual Studio.

Application Explorer
The Application Explorer in Visual Studio is a representation of the Microsoft
Dynamics AX AOT. It is organized in the same structure as the AOT.

FIGURE 5.1 VISUAL STUDIO APPLICATION EXPLORER

Like other window panes in Visual Studio, it can be toggled between visible and
invisible. To show the Application Explorer, select it from the View menu in
Visual Studio. The Application Explorer can also be docked or undocked, like
other panes in Visual Studio.
After discussing how to create a project, the Application Explorer can be used
to leverage AOT elements within Visual Studio.

5-2

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 5: Visual Studio Integration

Visual Studio Projects


There are various types of Visual Studio projects that can be used to interact with
Microsoft Dynamics AX. The use of the Application Explorer within Visual
Studio provides a strong Microsoft Dynamics AX developer experience, with
many familiar concepts.
Similar to development projects in Microsoft Dynamics AX, projects in Visual
Studio allow a developer to create a subset of elements, including those from the
AOT, to work with. All AOT elements added to a project in Visual Studio have
knowledge of their metadata in the Microsoft Dynamics AX AOT.

Types of Projects
There are three types of Visual Studio projects that can be used to develop
customizations for Microsoft Dynamics AX:

Report modeling projects

Enterprise Portal Web application projects

Managed code projects

The Report modeling and Enterprise Portal Web application project types
have their own templates provided under the Microsoft Dynamics AX template
group. These topics are more thoroughly covered in the "Reporting in Microsoft
Dynamics AX 2012" and "MorphX and Enterprise Portal Development in
Microsoft Dynamics AX 2012" courses.

Managed code projects


Managed code projects are .NET Class Library projects in Visual Studio that
have Microsoft Dynamics AX elements included.

Create a Managed Code Project


Perform the following steps to create a Managed code project:
1. Open Visual Studio.
2. Click File > New > Project.
3. Select Visual C# and then Windows from the Installed Templates
list.
4. Select Class Library template.
5. Enter a project name into the Name field and then click OK.
6. Verify the project is created and is visible in the Solution Explorer.
7. Save the project.
8. Click Build and then Build Solution.

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

5-3

Development III in Microsoft Dynamics AX 2012


9. Right-click on the project in the Solution Explorer and select Add
<Project Name> to AOT.
10. Notice the project icon now has the Microsoft Dynamics icon
incorporated, indicating this project is now connected with the AOT.
NOTE: VB.NET could be used in this procedure, in place of C#.

Add an AOT Element to a Managed Code Project


Once a Managed code project is created in Visual Studio, Microsoft Dynamics
AX elements from the Application Explorer can be added to it. Simply drag an
element from the Application Explorer, into the project in the Solution Explorer.
Once dropped into the Solution Explorer, a node representing the element will be
in the project. These are referred to as proxies.
To demonstrate the connectivity with the AOT, the following code could now be
written in the C# class library.
using
using
using
using

System;
System.Collections.Generic;
System.Linq;
System.Text;

namespace ClassLibrary1
{
public class Class1
{
public string GetCustomerPaymentMode(string
accountNum,
string
dataAreaId)
{
string paymentMode = String.Empty;
CustTable custTable = new CustTable();
// Search for the customer.
custTable = CustTable.findByCompany(dataAreaId,
accountNum);
if (custTable.Found)
{
// Get the value for the customer's payment

mode.

paymentMode = custTable.PaymMode;

return paymentMode;

5-4

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 5: Visual Studio Integration


This example illustrates how the CustTable element is now used in the C# code.
An object of type CustTable is instantiated and used to fetch a record from the
database. Notice the use of a method on the table object: findByCompany().
Also notice that Visual Studio accepts the use of table fields, like
CustTable.PaymMode, as well as table methods.

Create Event Handlers in Managed Code


In the AOT, you can associate a class method with an event handler. An event
handler is code that runs before the associated method runs or after the associated
method has finished running. The event handler itself is also a class method, and
it can be written in either X++ or managed code.
To create an Event Handler in managed code, perform the following procedure.
This procedure assumes the method that will have its events handled has already
been created in the AOT, in this case, SalesFormLetter.run():
1. Open Visual Studio.
2. Create a Managed Code project.
3. Rename the class to a meaningful name. In the example the class will
be named MyPreEventHandler.
4. Save the project.
5. Build the class by clicking Build and then Build
MyPreEventHandler.
6. Right-click on the project in the Solution Explorer and select Add
<Project Name> to AOT.
7. If the Application Explorer is not visible, open it.
8. In the code editor, open the MyPreEventHandler class and place
the cursor somewhere inside the class.
9. In the Application Explorer, locate the SalesFormLetter class.
Expand it and locate the run method.
10. Right-click the run method and select Add pre event handler.
11. Notice that an event handler method is created, as shown.
// Do not change the name of the event handler method. If
you rename the method, the event handler will not work.
[Microsoft.Dynamics.AX.ManagedInterop.XppClassDefiningEvent
Attribute(@"\Classes\SalesFormLetter")]
static public void PreRun(XppPrePostArgs args)
{
}

The method created is empty, and ready for the pre-event code to be added. Do
not change the name of the method, or the event handler will not work.

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

5-5

Development III in Microsoft Dynamics AX 2012

Deploying Managed Code


For managed code to be executed by the Microsoft Dynamics AX client and
server, it must first be deployed to where it can be executed.
The following are the basic steps for deploying managed code:
1.
2.
3.
4.

Build the project.


Right-click the project and add it to the AOT.
Set the deployment properties for the project.
Right-click the project and deploy the project.

The following deployment properties are available in the Properties pane for the
project:

Deploy to Server

Deploy to Client

Deploy to EP

Deploy to SSRS

The first two properties are described in more detail below.

Deploy to Server
If the Deploy to Server property is set to Yes, the system deploys the project
output (a DLL, for example) by copying it to the server's \bin directory. The
server \bin directory might be found at a file location such as the following:
C:\Program Files\Microsoft Dynamics
AX\60\Server\MicrosoftDynamicsAX\Bin\VSAssemblies
After the managed code has been deployed, the AOS must be restarted so that the
assembly can be loaded by the AOS. If you have hot swapping enabled, the
AOS does not have to be restarted. Hot swapping allows multiple application
domains on a single AOS, allowing new versions of managed code to be
deployed and be immediately available to any new client connections. Hot
swapping can be enabled by following these steps:
1. Open the Microsoft Dynamics Server Configuration Utility by
clicking Start > Administrative Tools > Microsoft Dynamics AX
2012 Server Configuration. This must be run with administrative
credentials.
2. On the Application Object Server tab, select Allow hot swapping
of assemblies when the server is running.
3. Click OK. You will see a prompt that asks whether you want to
restart the AOS service. The change will only take effect after the
AOS is restarted.

5-6

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 5: Visual Studio Integration


NOTE: Hot swapping should only be enabled in development environments.

Deploy to Client
If the Deploy to Client property is set to Yes, the system deploys the project
output (a DLL, for example) by copying it to the client \bin directory. The client
\bin directory might be found at a file location such as the following:
C:\Program Files (x86)\Microsoft Dynamics AX\60\Client\Bin

Visual Studio Debugging Experience for X++


With a tight integration between X++ and Visual Studio, an integrated debugging
experience is essential for developers. It is possible to use the Visual Studio
debugger to debug managed code that is called from X++. The opposite is also
true. X++ code that is called from managed code can be debugged using the
Microsoft Dynamics AX debugger.

Debugging Managed Code Called from X++


Perform the following procedure to debug managed code called from X++ code.
The procedure continues with the example of the MyPreEventHandler managed
code, on the SalesFormLetter.run() event:
1. Create a job in Microsoft Dynamics AX that executes
SalesFormLetter.run().
2. In Visual Studio, put a breakpoint on a line of code within the
preRun method of the MyPreEventHandler class.
3. Select Debug and then Attach to Process.
4. From the Available Processes field, select the Microsoft Dynamics
AX client process (Ax32.exe) and click Attach. The client process is
selected since the code being debugged runs on the client.
5. In Microsoft Dynamics AX, run the job.
6. The Visual Studio debugger will appear, waiting at the breakpoint.

Debugging X++ Called from Managed Code


Perform the following procedure to debug X++ code called from managed code.
1. Create managed code in Visual Studio that calls a method on a
Microsoft Dynamics AX proxy (table or class).
2. On the Visual Studio project's properties, set Debug Target to
Client and Startup Element to the AOT element that contains the
X++ code you wish to debug. The format of this property should be
"AOT Node\Element Name". For example, Classes\SalesLineType.
Acceptable element types are job, menu item, class and form.

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

5-7

Development III in Microsoft Dynamics AX 2012


3. In that startup element in Microsoft Dynamics AX, place a
breakpoint in the X++ code you wish to debug.
4. Press F5 in Visual Studio to debug the managed code.
5. When code execution reaches a breakpoint in X++ code, context will
switch to the Microsoft Dynamics AX debugger.

5-8

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 5: Visual Studio Integration

Lab 5.1 - Create a Managed Code Project


You have been asked to write business logic for Microsoft Dynamics AX using
managed C# code in Visual Studio.
Scenario
Before any managed code can be written, you need to create a Managed Code
project in Visual Studio that can use the Microsoft Dynamics AX integrated tool
set. Create this managed code project, and add the SalesTableType X++ class to
the project.

Challenge Yourself!
1. Create a managed code project that uses the C# language.
2. Add the SalesTableType X++ class to the project.

Step by Step
1. Open Visual Studio.
2. Click File > New > Project.
3. Select Visual C# and then Windows from the Installed Templates
list.
4. Select Class Library template.
5. Enter a project name into the Name field and then click OK.
6. Verify the project is created and is visible in the Solution Explorer.
7. Save the project.
8. Click Build and then Build Solution.
9. Right-click on the project in the Solution Explorer and select Add
<Project Name> to AOT. Notice the project icon now has the
Microsoft Dynamics icon incorporated, indicating this project is now
connected with the AOT.
10. Open the Application Explorer in Visual Studio.
11. In the Classes node, find the SalesTableType class.
12. Drag the SalesTableType class onto the project in the Solution
Explorer. The class should now appear as a proxy in the projects
contents.

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

5-9

Development III in Microsoft Dynamics AX 2012

Lab 5.2 - Create an Event Handler in Managed Code


You have been asked to write an event handler for a Microsoft Dynamics AX
event, using managed C# code in Visual Studio.
Scenario
The event that you have been asked to handle, is the ValidateWrite method on
the SalesTableType (called to validate a SalesTable record before it is written).
You have been asked to create a post-event handler using managed code. You
will also need to create a managed code project to hold this event handler (the
project created in Lab 5.1 can be used for this purpose).

Challenge Yourself!
1. Use the Application Explorer to find the method that needs to be
handled.
2. Create a post-event handler.
3. Ensure the event handler will be deployed on the Server and Client.

Step by Step
1. Open Visual Studio.
2. If you are reusing the project from Lab 5.1, skip to step 4. Otherwise,
create a Managed Code project.
3. Right-click on the project in the Solution Explorer and select Add
<Project Name> to AOT.
4. If the Application Explorer is not visible, open it.
5. In the code editor, open the class and place the cursor somewhere
inside the class.
6. In the Application Explorer, locate the SalesTableType class.
Expand it and locate the validateWrite method.
7. Right click the validateWrite method and select Add post-event
handler.
8. Notice that an event handler method is created in the class.

5-10

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 5: Visual Studio Integration

Summary
By using the integrated Microsoft Dynamics AX tool set inside Visual Studio,
developers can now write managed code in any .NET language to enhance or
modify Microsoft Dynamics AX business logic. The Application Explorer tool
offers a development environment similar to that within Microsoft Dynamics
AX, making switching between the two easier and more familiar.

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

5-11

Development III in Microsoft Dynamics AX 2012

Test Your Knowledge


Test your knowledge with the following questions.
1. What type of Visual Studio projects can be used to integrate with Microsoft
Dynamics AX?

2. What key step in creating a managed code project will connect the project
with the AX tool set within Visual Studio?

3. What four deployment properties are available for a Visual Studio project
that has been added to the Microsoft Dynamics AX AOT?

5-12

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 5: Visual Studio Integration


4. What needs to happen before the Visual Studio debugger will activate on
breakpoints inside managed code called from the Microsoft Dynamics AX
client or server?

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

5-13

Development III in Microsoft Dynamics AX 2012

Quick Interaction: Lessons Learned


Take a moment and write down three key points you have learned from this
chapter
1.

2.

3.

5-14

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 5: Visual Studio Integration

Solutions
Test Your Knowledge
1. What type of Visual Studio projects can be used to integrate with Microsoft
Dynamics AX?
MODEL ANSWER:
Report modeling projects, Enterprise Portal Web application projects, and
Managed code projects.
2. What key step in creating a managed code project will connect the project
with the AX tool set within Visual Studio?
MODEL ANSWER:
Right-clicking the project and selecting Add <Project Name> to AOT.
3. What four deployment properties are available for a Visual Studio project
that has been added to the Microsoft Dynamics AX AOT?
MODEL ANSWER:
Deploy to Server, Deploy to Client, Deploy to EP, Deploy to SSRS.
4. What needs to happen before the Visual Studio debugger will activate on
breakpoints inside managed code called from the Microsoft Dynamics AX
client or server?
MODEL ANSWER:
The Visual Studio Debugger needs to be attached to either the Server or
Client process for Microsoft Dynamics AX.

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

5-15

Development III in Microsoft Dynamics AX 2012

5-16

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 6: Workflow

CHAPTER 6: WORKFLOW
Objectives
The objectives are:

Configure how the workflow engine is executed on a server.

Specify which application module a workflow is applicable to using


a workflow category.

Link tables to workflows using a query.

Create a new workflow type.

Apply a workflow to a form.

Define what happens when the workflow is approved or denied.

Create Event Handlers and apply them to a workflow.

Configure a workflow.

Introduction
Workflow is a system in Microsoft Dynamics AX 2012 that allows business
processes to be automated. For example, a purchase requisition may need to be
approved by a number of different employees according to the requisition's total
amount. Each employee has to approve it before the next employee in the
approval route.
A Workflow in Microsoft Dynamics AX uses a combination of Application
Object Tree (AOT) elements created by IT and additional set up that a user can
control. This course introduces the development side of creating a workflow. To
create a workflow you will need to use skills developed from this class and the
Morph X development class.

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

6-1

Development III in Microsoft Dynamics AX 2012


Scenario
Isaac, the systems developer, has been asked to create a new workflow that will
be used to approve a new sales order for a customer that has reached their credit
limit. The requirement is that when a new sales order is entered which takes the
customer over their credit limit, the sales order is submitted to the Accounts
Receivable (AR) manager. They will either approve or deny the sales order. Until
it is approved, the sales order cannot be picked, packed or invoiced.

Workflow Configuration
There are three batch jobs that manage workflow processing. These jobs are all
run on the Application Object Server (AOS) using the Batch system. To set this
up, run System Administration > Setup > Workflow > Workflow
infrastructure configuration. Enter batch groups for each process. Batch groups
can be used to control which AOS instance each workflow batch job is run on.

Create a Workflow Category


A workflow category is used to determine the module in which the workflow will
be available. Modules are defined by the ModuleAxapta enum.

Demonstration: Creating a Workflow Category


Perform the following steps to create a category that allows the workflow to be
authored from the Sales and Marketing module.
1. Open the AOT.
2. Expand the Workflow node.
3. Right-click on the Workflow Categories node and select New
Workflow Category. A new workflow category called Workflow
Category1 will be created.
4. Right-click on the newly created workflow category and select
Properties.
5. Change the name property to SalesCategory.
6. Change the label property to Sales workflows.

6-2

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 6: Workflow
7. Change the Module property to SalesOrder.
8. Right-click on the newly created workflow category and select Save.

FIGURE 6.1 WORKFLOW TYPE WIZARD

Create a Query
A workflow uses a query to define the data that is used by the workflow. It can
define one or more tables and all or selected fields on that table.

Demonstration: Creating a Query


A query defines what tables are used to determine that a workflow can be
initiated. Use a class to bind that query to the workflow type. Perform the
following steps to create a query.
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.

Open the AOT.


Right-click on the Queries node and select New Query.
Rename the query to SalesCreditLimitApproval.
Expand the newly created query.
Open another AOT window.
Expand Data Dictionary > Tables.
Find the table SalesTable.
Drag the SalesTable table to the Data Sources node of the
SalesCreditLimitApproval query.
Expand the SalesTable_1 node
Right-click on the Fields node and select Properties.
Set the Dynamics property to Yes and close the property sheet.
Right click on the SalesCreditLimitApproval query and select
Save.

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

6-3

Development III in Microsoft Dynamics AX 2012

Create a Workflow Type


A workflow type defines the business document being acted upon, the supported
AOT elements, and event handlers and menu items for the workflow. Workflows
are created based on a type, and many workflows can be based on the same type.

Demonstration: Creating a Workflow Type


Perform the following steps to create a workflow type and bind it to the
workflow category created in the previous demonstration.
1. Open the AOT.
2. Expand the Workflow node.
3. Right-click on the Workflow Types node and select Add-ins >
Workflow type wizard.
4. Click Next.
5. Enter SalesCreditLimitAppr in the name.
6. Enter SalesCategory in the Category.
7. Enter SalesCreditLimitApproval in the query.
8. Enter SalesTable in the Document menu item.

FIGURE 6.2 WORKFLOW TYPE

9. Click Next.
10. Click Finish. A development project with a number of newly created
elements will be displayed.

6-4

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 6: Workflow

Enable Workflow on a Form


Now that both the workflow type and approval element are defined, you can
specify which forms will use it.

Demonstration: Add a WorkflowState Field


Any form that uses the same table in the datasource as the query referenced in a
workflow document can be enabled for a workflow. This demonstration shows
how to enable workflow on the SalesTableListPage form.
You can specify conditions under which a workflow is eligible for submission.
One of these conditions must be that it has not already been submitted. To test
this condition, use a new field on the SalesTable table.
Perform the following steps to add a workflow state field.
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.

Open the AOT.


Expand Data Dictionary.
Right-click on Base Enums and select New Base Enum.
Rename the new enum to SalesCreditLimitApprovalStatus
Add four elements to the Enum called NotSubmitted, Submitted,
Approved, Rejected.
Expand Tables > SalesTable.
Right-click on Fields and select New > Enum.
Right-click on the newly created field and select Properties.
Change the Name property to CreditLimitApprovalStatus.
Change the EnumType property to
SalesCreditLimitApprovalStatus.
Right-click on the SalesTable node and select Save.

Demonstration: Enable Workflow on the Form


Workflow on the form is enabled using properties on the design node, and by
overriding a form method. Perform the following steps to enable workflow on a
form.
1.
2.
3.
4.
5.
6.
7.

Open the AOT.


Expand Tables > SalesTable.
Create a new method and add the method in the following code.
Save the changes made to the table.
Expand Forms > SalesTableListPage > Designs.
Right-click on the design node and select Properties.
Change the WorkflowEnabled property to Yes.

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

6-5

Development III in Microsoft Dynamics AX 2012


8. Change the WorkflowDatasource property to SalesTable.
9. Change the WorkflowType property to SalesCreditLimitAppr
10. Save your changes to the form.
boolean canSubmitToWorkflow(str _workflowType = '')
{
amountMST
creditBalance;
custTable
custTable;
;
if (!this.CreditLimitApprovalStatus ==
SalesCreditLimitApprovalStatus::NotSubmitted)
return false;
custTable = this.custTable_InvoiceAccount();
if (!custTable.CreditMax)
return false;
creditBalance = custTable.CreditMax custTable.balanceMST();
if (this.amountRemainSalesFinancial() +
this.amountRemainSalesPhysical() < creditBalance)
return false;
return true;
}

Demonstration: Create a Submit to Workflow Class


To submit a document for workflow processing, call standard code to prompt the
user for a comment and to process the submission. Perform the following steps to
create a submit to workflow class.
1. Press Ctrl+Shift+P to open the development projects window.
2. Expand Private.
3. Double-click on SalesCreditLimitApprWFType developement
project.
4. In the development project find the class
SalesCreditLimitApprSubmitManager.
5. Create a new method called submit, and copy the following code for
this method.
6. Modify the code for the main method as shown in the following
code.
7. Press F8 to save and compile the code.
8. Find the menu item SalesCreditLimitApprSubmitMenuItem.

6-6

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 6: Workflow
9. Change the Label property to Submit.
10. Right-click on the SalesCreditLimitApprSubmitMenuItem menu
item and select Save.
void submit(Args args)
{
// Variable declaration.
recId recId = args.record().RecId;
WorkflowCorrelationId workflowCorrelationId;
// Hardcoded type name
WorkflowTypeName workflowTypeName =
workflowtypestr(SalesCreditLimitAppr);
// Initial note is the information that users enter
when they
// submit the document for workflow.
WorkflowComment note ="";
WorkflowSubmitDialog workflowSubmitDialog;
SalesTable SalesTable;
// Opens the submit to workflow dialog.
workflowSubmitDialog =
WorkflowSubmitDialog::construct(args.caller().getActiveWork
flowConfiguration());
workflowSubmitDialog.run();
if (workflowSubmitDialog.parmIsClosedOK())
{
recId = args.record().RecId;
SalesTable = args.record();
// Get comments from the submit to workflow dialog.
note = workflowSubmitDialog.parmWorkflowComment();
try
{

ttsbegin;

workflowCorrelationId =
Workflow::activateFromWorkflowType(workflowTypeName,
recId,
note,
NoYes::No);
SalesTable.CreditLimitApprovalStatus =
SalesCreditLimitApprovalStatus::Submitted;
// Send an Infolog message.
info("Submitted to workflow.");

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

6-7

Development III in Microsoft Dynamics AX 2012


ttscommit;
}
catch(exception::Error)
{
info("Error on workflow activation.");
}

}
args.caller().updateWorkFlowControls();

Create a Workflow Approval


A workflow element may contain a number of outcomes. The workflow may be
approved, rejected, returned or a change may be requested. The Workflow
Approval element determines which of these outcomes is allowed and what
happens in the event of each outcome.
Each outcome can trigger the execution of business logic by specifying a menu
item for each item.

Demonstration: Creating a Workflow Approval


Perform the following steps to create a workflow approval element and set key
properties.
1.
2.
3.
4.
5.
6.

6-8

Open the AOT.


Expand the Workflow node.
Right-click on Approvals and select Add-ins > Approval wizard.
Click Next.
Enter SalesCLApproval in Name.
Enter SalesCreditLimitApprDocument in Workflow document.

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 6: Workflow
7. Enter Overview in Document preview field group.
8. Enter SalesTableListPage in Document menu item.

FIGURE 6.3 WORKFLOW APPROVAL

9. Click Next.
10. Click Finish. A development project with a number of newly created
elements is displayed.
11. Drag SalesCLApproval approval to the Supported elements node
on the SalesCreditLimitAppr workflow type.
12. Save your changes to the SalesCreditLimitAppr workflow type

Create Event Handlers


Event handlers are used to execute business logic at specific points in the life of a
workflow. They can be implemented at the workflow level, for example when the
workflow is started or completed, or at an element level, for example when
anyone approves or rejects a step in the approval.
Event handlers are implemented by creating a class that implements one or more
of the EventHandler interfaces. The interfaces at the workflow level are as
follows:
Event

Description

WorkflowStarted
EventHandler

This event occurs when the workflow instance starts.

WorkflowCompleted
EventHandler

This event occurs when the workflow instance ends


after it is completed.

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

6-9

Development III in Microsoft Dynamics AX 2012


Event

Description

WorkflowCanceled
EventHandler

This event occurs when the workflow instance ends


after it is canceled. Use this event handler to perform
any clean up operations needed.

WorkflowConfig
DataChangeEvent
Handler

This event occurs when a workflow changes. Use this


event handler to identify when a workflow has
changed. For example, if you create an association
between the application data and a workflow, this
event would be raised if the workflow was deleted or
updated so that application code could be invoked or
respond in some way.

Demonstration: Write code for Event Handler


The wizards run in the previous demonstrations create event handler classes that
need to be completed. This can include basic steps to set the status of the
approval, or include more complex business logic. The following code needs to
be added to the completed method of the SalesCreditLimitApprEventHandler
class.
public void completed(WorkflowEventArgs _workflowEventArgs)
{
SalesTable SalesTable;
select forupdate SalesTable where SalesTable.RecId ==
_workflowEventArgs.parmWorkflowContext().parmRecId();
if(SalesTable.RecId)
{
SalesTable.CreditLimitApprovalStatus =
SalesCreditLimitApprovalStatus::Approved;
SalesTable.write();
}
}

Element Level Event Handlers


The event handler interfaces at the workflow element level are as follows:

6-10

Event

Description

WorkflowElement
StartedEventHandler

This event occurs when the task or approval starts.


For approvals, you can use this event to transition
the workflow document state from Submitted to
PendingApproval.

WorkflowElement
CanceledEventHandler

This event occurs when the task or approval is


canceled.
For approvals, you can use this event to transition
the workflow document state from the current state
to Canceled.

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 6: Workflow
Event

Description

WorkflowElement
CompletedEvent
Handler

This event occurs when the task or approval is


completed.
For approvals, you can use this event to transition
the workflow document state from
PendingApproval to Approved.

WorkflowElement
ReturnedEventHandler

This event occurs when the task or approval is


returned to the originator.
For approvals, you can use this event to transition
the workflow document state from the current state
to RequestChange.

WorkflowElemChange
RequestedEvent
Handler

This event occurs when an approver requests a


change to the task or approval.
For approvals, you can use this event to transition
the workflow document state from
PendingApproval to RequestChange.

Demonstration: Add Element Level Event Handlers


Perform the following steps to add element or outcome level event handlers.
1. In the AOT locate the class SalesCLApprovalEventHandler. This
class was created by the Approval element wizard.
2. Add the following code in the returned method
public void returned(WorkflowElementEventArgs
_workflowElementEventArgs)
{
SalesTable SalesTable;
ttsbegin;
select forupdate SalesTable
where SalesTable.RecId ==
_workflowElementEventArgs.parmWorkflowContext().parmRecId()
;
SalesTable.CreditLimitApprovalStatus =
SalesCreditLimitApprovalStatus::Rejected;
SalesTable.update();
ttscommit;
}

3. Save your changes to the class.


4. In the AOT, right-click on the AOT node and select Incremental
CIL generation from X++

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

6-11

Development III in Microsoft Dynamics AX 2012


NOTE: Code run in batch jobs must first be compiled to the Common
Intermediate Language (CIL) of the .NET framework. This increases the
performance of the batch job.

Author a Workflow
Now that you have created a workflow type and enabled it on a form, you can
configure it for use.

Demonstration: Authoring a Workflow


Perform the following steps to configure a workflow from the main menu.
1. The workflow category we created in the first procedure needs to be
added to the main menu. Create a new display menu item called
WorkflowConfigurationSales.
2. Set the label to Sales workflows.
3. Set the object to WorkflowTableListPage.
4. Set the EnumTypeParameter to ModuleAxapta.
5. Set the EnumParameter to SalesOrder.
6. Add the new menu item to SalesAndMarketting > Setup menu.
7. In the property sheet for the new node in the menu, set the property
IsDisplayedInContentArea to Yes.
8. Save your changes to the menu.
9. Open the main menu and select Sales and Marketting > Setup >
Sales workflows.
10. Click New.
11. Select SalesCreditLimitApprType and click Create workflow.
The workflow editor window opens.
12. Drag SalesCLApprovalApproval approval from the Workflow
elements window to the Workflow window.

6-12

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 6: Workflow
13. Drag the bottom node of the Start box to the top node of the
Approval box.
14. Drag the bottom node of the Approval box to the top node of the
End box.

FIGURE 6.4 WORKFLOW EDITOR

15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.

Double click on the Approval box.


Click Step 1
Click Assignment.
On the Assignment type tab, select User
On the User tab, double click on Sammy
Click on Basic Settings and then enter a subject and instructions for
the approval
Click on Close
Click on Save and Close
Enter some notes for the new workflow if you wish.
Select to Activate the new version
Click OK.

Demonstration: Test the Workflow


Perform the following steps to test the workflow by setting a credit limit and
creating a sales order.
1. In the application workspace, navigate to Accounts receivable >
Common > Customers > All customers.
2. Select a customer and click Edit.

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

6-13

Development III in Microsoft Dynamics AX 2012


3. In the Credit and collections fasttab, set a credit limit.
4. Close the Customers form.
5. Go to Sales and marketting > Common > Sales orders > All sales
orders.
6. Click New sales order. Enter the customer account modified that
you modified the credit limit for and click ok.
7. Enter items and quantities in the sales lines so that the balance of the
customer plus the total amount on the lines is greater than the credit
limit.
8. The workflow Submit button and dialog should appear.
9. Click the Submit button and enter a comment.
10. Wait for the batch job to process the workflow request. This should
take one to two minutes.
11. Select Actions > History. You will see that the document is waiting
for approval by the person you assigned to approve it.
12. Logon to windows as the Sammy using the Switch User option on
the Start menu.
13. Start the Microsoft Dynamics AX client.
14. Open the Sales order form.
15. Click the workflow Actions button and select Approve.
16. Wait one to two minutes for the workflow engine to process the
approval.
17. The workflow has now been approved.

6-14

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 6: Workflow

Lab 6.1 - Add Another Condition to the Submit Action


Scenario
Isaac has been asked to ensure that, once a credit limit has been reached, the sales
order cannot be posted until the workflow has been approved.

Challenge Yourself!
Add conditions to the posting functions on the sales order form that will prevent
posting to picking, packing or invoicing until the workflow has been approved. If
the credit limit has not been reached, then the postings should be allowed.

Step by Step
1. Add the following method CanPostCreditLimit to the salesTable
table.
2. Add the following code to the methods canPickingListBeUpdate(),
canPackingSlipBeUpdated() and canInvoiceBeUpdated() in the
salesTableType class.
boolean canPostCreditLimit()
{
amountMST
creditBalance;
custTable
custTable;
;
if (this.CreditLimitApprovalStatus ==
SalesCreditLimitApprovalStatus::Approved)
return true;
if (this.CreditLimitApprovalStatus ==
SalesCreditLimitApprovalStatus::Rejected
|| this.CreditLimitApprovalStatus ==
SalesCreditLimitApprovalStatus::Submitted)
return false;
custTable = this.custTable_InvoiceAccount();
if (!custTable.CreditMax)
return true;
creditBalance = custTable.CreditMax custTable.balanceMST();
if (this.amountRemainSalesFinancial() +
this.amountRemainSalesPhysical() < creditBalance)
return true;
return false;
}

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

6-15

Development III in Microsoft Dynamics AX 2012


boolean canPickingListBeUpdated()
{
......
ok = ok && salesTable.canPostCreditLimit();
return ok;
}
boolean canPackingslipBeUpdated()
{
......
ok = ok && salesTable.canPostCreditLimit();
return ok;
}
boolean canInvoiceBeUpdated()
{
......
ok = ok && salesTable.canPostCreditLimit();
return ok;
}

6-16

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 6: Workflow

Lab 6.2 - Enable Resubmit


Scenario
Isaac is required to ensure the workflow can be resubmitted after it has been
rejected.

Challenge Yourself!
When a workflow is rejected, it can be resubmitted. Modify the Submit to
Workflow class so that it can resubmit the workflow after a rejection
Use the PurchReqWorkflow class as model.

Step by Step
1. Find the class SalesCLApprovalResubmitActionMgr.
2. Modify the Main method and add a new method Resubmit as shown
in the following code:
public static void main(Args _args)
{
SalesCLApprovalResubmitActionMgr
SalesCLApprovalResubmitActionMgr = new
SalesCLApprovalResubmitActionMgr();
SalesCLApprovalResubmitActionMgr.resubmit(_args);
}
private void resubmit(Args _args)
{
// Variable declaration.
recId
_recId = _args.record().RecId;
WorkflowCorrelationId
_workflowCorrelationId;
// Hardcoded type name
WorkflowTypeName
_workflowTypeName =
workflowtypestr(SalesCreditLimitAppr);
// Initial note is the information that users enter
when they
// submit the document for workflow.
WorkflowComment
_initialNote ="";
WorkflowWorkItemActionDialog
WorkflowWorkItemActionDialog;
SalesTable
SalesTable;
;

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

6-17

Development III in Microsoft Dynamics AX 2012


// Opens the submit to workflow dialog.
workflowWorkItemActionDialog =
WorkflowWorkItemActionDialog::construct(
_args.caller().getActiveWorkflowWorkItem(),
WorkflowWorkItemActionType::Resubmit,
new MenuFunction(menuitemactionstr(PurchReqReSubmit),
MenuItemType::Action));
workflowWorkItemActionDialog.run();
if (WorkflowWorkItemActionDialog.parmIsClosedOK())
{
_recId = _args.record().RecId;
SalesTable = _args.record();
// Get comments from the submit to workflow dialog.
_initialNote =
workflowWorkItemActionDialog.parmWorkflowComment();
try
{

ttsbegin;

WorkflowWorkItemActionManager::dispatchWorkItemAction(
_args.caller().getActiveWorkflowWorkItem(),
_initialNote,
curUserId(),
WorkflowWorkItemActionType::Resubmit,
_args.menuItemName(),
false);
SalesTable.CreditLimitApprovalStatus =
SalesCreditLimitApprovalStatus::Submitted;
// Send an Infolog message.
info("Resubmitted to workflow.");

6-18

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 6: Workflow
}

ttscommit;

catch(exception::Error)
{
info("Error on workflow activation.");
}

_args.caller().updateWorkFlowControls();
}

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

6-19

Development III in Microsoft Dynamics AX 2012

Summary
The workflow system is highly configurable and flexible. By using Morph X and
some standard code patterns, it can be enabled for virtually any part of the
Microsoft Dynamics AX application.
This lesson explores some of the possibilities the workflow system offers, and
explores some of the different ways it can be used to cover most workflow
requirements.

6-20

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 6: Workflow

Test Your Knowledge


Test your knowledge with the following questions.
1. What application element is used to define to which module a workflow is
applicable?
( ) Workflow type
( ) Workflow category
( ) A field in the workflow configuration
( ) SalesTable
2. What type of AOT element needs to be created to specify which tables will
be processed by a workflow?
( ) Extended data type
( ) Class
( ) Form
( ) Query
3. Which three properties on a form design need to be modified to allow the
form to use a workflow?
( ) WorkflowType
( ) WorkflowEnabled
( ) WorkflowDocument
( ) WorkflowDatasource

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

6-21

Development III in Microsoft Dynamics AX 2012

Quick Interaction: Lessons Learned


Take a moment and write down three key points you have learned from this
chapter
1.

2.

3.

6-22

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Chapter 6: Workflow

Solutions
Test Your Knowledge
1. What application element is used to define to which module a workflow is
applicable?
( ) Workflow type
() Workflow category
( ) A field in the workflow configuration
( ) SalesTable
2. What type of AOT element needs to be created to specify which tables will
be processed by a workflow?
( ) Extended data type
( ) Class
( ) Form
() Query
3. Which three properties on a form design need to be modified to allow the
form to use a workflow?
() WorkflowType
() WorkflowEnabled
( ) WorkflowDocument
() WorkflowDatasource

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

6-23

Development III in Microsoft Dynamics AX 2012

6-24

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement

Anda mungkin juga menyukai