Sei sulla pagina 1di 240

MICROSOFT DYNAMICS AX 2009

COURSE 80014 DEVELOPMENT IV

Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Last Revision: November 2008

The information contained in this document represents the current view of Microsoft Corporation on the issues
discussed as of the date of publication. Because Microsoft must respond to changing market conditions, it should
not be interpreted to be a commitment on the part of Microsoft, and Microsoft cannot guarantee the accuracy of any
information presented after the date of publication.

This document is for informational purposes only. MICROSOFT MAKES NO WARRANTIES, EXPRESS, IMPLIED
OR STATUTORY, AS TO THE INFORMATION IN THIS DOCUMENT.

Complying with all applicable copyright laws is the responsibility of the user. Without limiting the rights under
copyright, no part of this document may be reproduced, stored in or introduced into a retrieval system, or
transmitted in any form or by any means (electronic, mechanical, photocopying, recording, or otherwise), or for any
purpose, without the express written permission of Microsoft Corporation.

Microsoft may have patents, patent applications, trademarks, copyrights, or other intellectual property rights
covering subject matter in this document. Except as expressly provided in any written license agreement from
Microsoft, the furnishing of this document does not give you any license to these patents, trademarks, copyrights, or
other intellectual property.

2008 Microsoft Corporation. All rights reserved. Microsoft Dynamics, Microsoft PowerPoint Microsoft SQL
Server and Microsoft Dynamics AX MorphX are trademarks or registered trademarks of Microsoft
Corporation. The names of actual companies and products mentioned herein may be the trademarks of their
respective owners.

This course content is designed for Microsoft Dynamics AX 2009.

Microsoft Official Training Materials for Microsoft Dynamics


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

Introduction to Development IV in Microsoft Dynamics AX 2009 0-1


Welcome ............................................................................................................ 0-1
Microsoft Dynamics Courseware Contents ........................................................ 0-2
Documentation Conventions .............................................................................. 0-3
Student Objectives ............................................................................................. 0-4
Chapter 1: Point-of-Sale Project 1-1
Objectives ........................................................................................................... 1-1
Introduction ......................................................................................................... 1-1
Overview ............................................................................................................ 1-2
Functional Design ............................................................................................... 1-3
Technical Design ................................................................................................ 1-4
Summary ............................................................................................................ 1-7
Quick Interaction: Lessons Learned ................................................................... 1-8
Chapter 2: Number Sequences 2-1
Objectives ........................................................................................................... 2-1
Introduction ......................................................................................................... 2-1
Overview ............................................................................................................ 2-2
Assign a New Number ........................................................................................ 2-4
Continuous Number Sequences......................................................................... 2-5
Format a Number ............................................................................................... 2-5
Number Pre-Allocation ....................................................................................... 2-6
Clean Up Process .............................................................................................. 2-6
Form Handler ..................................................................................................... 2-7
NumberSeqReference ........................................................................................ 2-7
Summary ............................................................................................................ 2-8
Test Your Knowledge ......................................................................................... 2-9
Lab 2.1 - Add Pay Id number sequence ........................................................... 2-10
Lab 2.2 - Use Form Handler ............................................................................. 2-12
Quick Interaction: Lessons Learned ................................................................. 2-13
Solutions ........................................................................................................... 2-14
Chapter 3: PrintJobSettings 3-1
Objectives ........................................................................................................... 3-1
Introduction ......................................................................................................... 3-1
Set Print Options ................................................................................................ 3-2
Retrieve Print Options ........................................................................................ 3-4
Use of Pack and Unpack to Store Settings ........................................................ 3-4
Use of SysPrintOptions ...................................................................................... 3-5
Summary ............................................................................................................ 3-6
Test Your Knowledge ......................................................................................... 3-7
Lab 3.1 - Suppress Scaling Message ................................................................. 3-8
Lab 3.2 - Print Job Settings ................................................................................ 3-9
Lab 3.3: Print the receipt to the correct printer ................................................. 3-10
Lab 3.4 - Bypass Print Options ......................................................................... 3-11
Quick Interaction: Lessons Learned ................................................................. 3-12
Solutions ........................................................................................................... 3-13

Microsoft Official Training Materials for Microsoft Dynamics i


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

Chapter 4: Business Connector 4-1


Objectives ........................................................................................................... 4-1
Introduction ......................................................................................................... 4-1
Business Connector Overview ........................................................................... 4-2
Business Connector Setup and Configuration.................................................... 4-3
Business Connector Architecture ....................................................................... 4-7
Summary .......................................................................................................... 4-12
Test Your Knowledge ....................................................................................... 4-13
Lab 4.1 - Business Connector .......................................................................... 4-15
Lab 4.1 - Business Connector (Solution) .......................................................... 4-19
Quick Interaction: Lessons Learned ................................................................. 4-21
Chapter 5: CLR Interoperability 5-1
Objectives ........................................................................................................... 5-1
Introduction ......................................................................................................... 5-1
Reference CLR Assemblies in the AOT ............................................................. 5-2
Adding Assemblies to the Global Assembly Cache or Client ............................. 5-3
Leverage CLR Managed Code Within X++ Code............................................... 5-4
InteropPermission Class .................................................................................... 5-6
Web service references ...................................................................................... 5-6
Implementing a DLL ......................................................................................... 5-12
Summary .......................................................................................................... 5-15
Test Your Knowledge ....................................................................................... 5-16
Lab 5.1 - CLR Interop ....................................................................................... 5-18
Lab 5.1 - CLR Interop (Solution)....................................................................... 5-19
Lab 5.2 - Implementing DLL's........................................................................... 5-21
Lab 5.2 - Implementing DLL's (Solution) .......................................................... 5-22
Quick Interaction: Lessons Learned ................................................................. 5-23
Solutions ........................................................................................................... 5-24
Chapter 6: Ledger 6-1
Objectives ........................................................................................................... 6-1
Introduction ......................................................................................................... 6-1
Scenario ............................................................................................................. 6-2
LedgerVoucher ................................................................................................... 6-2
LedgerJournal .................................................................................................. 6-10
Summary .......................................................................................................... 6-13
Test Your Knowledge ....................................................................................... 6-14
Lab 6.1 Create and Post a Ledger Journal.................................................... 6-16
Lab 6.2 POS - End-of-day routine ................................................................. 6-18
Quick Interaction: Lessons Learned ................................................................. 6-20
Solutions ........................................................................................................... 6-21
Chapter 7: Trade 7-1
Objectives ........................................................................................................... 7-1
Introduction ......................................................................................................... 7-1
TableType and LineType .................................................................................... 7-2
Post and Print Document Updates ..................................................................... 7-5
Posting Transactions ........................................................................................ 7-11

ii Microsoft Official Training Materials for Microsoft Dynamics


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

Settlement ........................................................................................................ 7-13


Trade Agreement ............................................................................................. 7-14
Summary .......................................................................................................... 7-16
Test Your Knowledge ....................................................................................... 7-17
Lab 7.1 - Trade ................................................................................................. 7-19
Lab 7.1 - Trade (Solution) ................................................................................ 7-20
Lab 7.2 - Payment Posting and Invoice Update ............................................... 7-21
ab 7.2 - Payment Posting and Invoice Update (Solution) ................................. 7-22
Lab 7.3 - Settlement ......................................................................................... 7-23
Lab 7.3 (Solution) ............................................................................................. 7-24
Quick Interaction: Lessons Learned ................................................................. 7-25
Solutions ........................................................................................................... 7-26
Chapter 8: Inventory 8-1
Objectives ........................................................................................................... 8-1
Introduction ......................................................................................................... 8-1
Scenario ............................................................................................................. 8-2
Inventory Journals .............................................................................................. 8-2
Inventory Dimensions ......................................................................................... 8-4
InventSum ........................................................................................................ 8-10
Lab 8.1 On-hand Inventory on Sales Order Form ......................................... 8-17
InventMovement ............................................................................................... 8-18
InventUpdate .................................................................................................... 8-20
Summary .......................................................................................................... 8-24
Test Your Knowledge ....................................................................................... 8-25
Lab 8.2 Create an ABC allocation ................................................................. 8-27
Lab 8.3 POS - Display inventory dimensions ................................................ 8-28
Lab 8.4 POS - Display on hand inventory ..................................................... 8-29
Quick Interaction: Lessons Learned ................................................................. 8-30
Solutions ........................................................................................................... 8-31
Chapter 9: Production 9-1
Objectives ........................................................................................................... 9-1
Introduction ......................................................................................................... 9-1
ProdMulti ............................................................................................................ 9-2
ProdStatusType .................................................................................................. 9-4
ProdUpd ............................................................................................................. 9-6
Scheduling .......................................................................................................... 9-8
Summary .......................................................................................................... 9-14
Test Your Knowledge ....................................................................................... 9-15
Lab 9.1 - Production ......................................................................................... 9-16
Lab 9.1 - Production (Solution) ......................................................................... 9-17
Lab 9.2 - Production ......................................................................................... 9-18
Lab 9.2 - Production (Solution) ......................................................................... 9-19
Quick Interaction: Lessons Learned ................................................................. 9-20
Solutions ........................................................................................................... 9-21
Chapter 10: Project Accounting 10-1
Objectives ......................................................................................................... 10-1
Introduction ....................................................................................................... 10-1

Microsoft Official Training Materials for Microsoft Dynamics iii


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

Scenario ........................................................................................................... 10-2


Design .............................................................................................................. 10-2
Posting Transactions ........................................................................................ 10-4
Invoice Proposal ............................................................................................... 10-5
Invoice .............................................................................................................. 10-8
Summary ........................................................................................................ 10-10
Test Your Knowledge ..................................................................................... 10-11
Lab 10.1 - Warranty Item ................................................................................ 10-12
Lab 10.2 - Place Invoice Proposal On Hold.................................................... 10-13
Quick Interaction: Lessons Learned ............................................................... 10-14
Solutions ......................................................................................................... 10-15
Chapter 11: Workflow 11-1
Objectives ......................................................................................................... 11-1
Introduction ....................................................................................................... 11-1
Scenario ........................................................................................................... 11-2
Workflow Installation ........................................................................................ 11-2
Create a Workflow Category ............................................................................ 11-2
Create a Workflow Template ............................................................................ 11-3
Create a Workflow Document .......................................................................... 11-4
Create a Workflow Approval ............................................................................. 11-5
Enable Workflow on a Form ............................................................................. 11-8
Create Event Handlers ................................................................................... 11-12
Configure a Workflow ..................................................................................... 11-17
Lab 11.1 - Add Another Condition to the Submit Action ................................. 11-19
Code Walkthrough: Submitting a workflow ..................................................... 11-20
Code Walkthrough: Workflow Processor ........................................................ 11-22
Lab 11.2 - Enable Resubmit ........................................................................... 11-24
Summary ........................................................................................................ 11-26
Test Your Knowledge ..................................................................................... 11-27
Quick Interaction: Lessons Learned ............................................................... 11-28
Solutions ......................................................................................................... 11-29

iv Microsoft Official Training Materials for Microsoft Dynamics


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

INTRODUCTION TO DEVELOPMENT IV IN
MICROSOFT DYNAMICS AX 2009
Welcome
We know training is a vital component of retaining the value of your Microsoft
Dynamics AX 2009 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 0-1


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

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 0-3


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

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: Point-of-Sale Project

CHAPTER 1: POINT-OF-SALE PROJECT


Objectives
The objectives are:

Create the base functionality for the Point-Of-Sale (POS) module


Review the functional design of the POS project
Review the technical design of the POS project

Introduction
To practice using the knowledge and skills you learn during this course, you will
develop a POS module which encompasses most of the modules that are
discussed.

This lesson describes functional requirements and technical design suggestions as


applied to a fictitious company.

This lesson also describes the basic POS design which is built upon during the
course, adding functionality appropriate to the lesson being reviewed.

Microsoft Official Training Materials for Microsoft Dynamics 1-1


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

Overview
The following is an example of how the POS form looks when completed.

FIGURE 1.1

The process flow of the finished POS module is as follows:

FIGURE 1.2

1-2 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 1: Point-of-Sale Project

An end of day process is used to post the actual money counted to a cash
account, and any difference between the posted amount and the counted amount
to a difference account.

There is a basic receipt printed, and the buyer is also offered the option of having
the receipt emailed to them in XML format.

Additionally, there is a Microsoft C# .NET front end that would be used at a


supermarket-type register. This illuatrates running the Microsoft Dynamics AX
business logic from an external application.

Functional Design
The POS module in this scenario is for The Light Company used in the standard
Microsoft Dynamics AX demo data. It is not a supermarket-type cash register,
where very little information is displayed, but shows more information, which
could include information such as: delivery address, quantity on hand and special
instructions.

Because this POS is for people who walk in and pay for items immediately, do
not create a customer account each time. Instead, use a one-time customer
parameter. This is normally used as a template for creating a new customer for
each sale, but you will use one customer for every sale.

Sales Order
The POS has a Register ID to distinguish each register. Payments can be posted
against each register, which enables an end of day routine to be processed.

The sales order type should be Journal, meaning that inventory transactions only
get posted at the time of physical movement. This increases performance when
entering lines. In this case, the time of physical movement is when an invoice is
posted, as this is the only order update available.

The POS form contains the following fields:

Sales Id
Register Id
Item Id
Quantity
Sales Price
Line Amount
Item Name

Microsoft Official Training Materials for Microsoft Dynamics 1-3


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

Totals and Payment


The POS form also includes a total order amount.

Include a Payment button that opens the Payment Lines form, which contains the
following fields:

Payment Type
Amount

Include the capability to print a receipt from the sales line (for now, it is a pro-
forma receipt). The receipt should show:

Sales Id
Date
Item Id
Qty
Price
Total
Payment Type

The payment form and the pro-forma receipt are called from buttons on the POS
form.

Technical Design
The POS module sits on top of the standard sales module, and uses some of the
sales module code, tables, and procedures.

Create the following elements:

Extended Data Types

Type Name Extends Label Relation


POSRegisterId SysGroup Register Id POSRegisterTable.RegisterId
POSPayId Num Payment Id POSPayTable.PayId

Tables

Table Name Field Name Field Type Comment


POSRegisterTable
RegisterId POSRegisterId
Description Description

1-4 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 1: Point-of-Sale Project

Table Name Field Name Field Type Comment


HoldingAccount LedgerAccount
POSRegisterTrans
RegisterId POSRegisterId
SalesId SalesId
PayId POSPayId
LineNum LineNum
PayMode CustPayMode
Amount AmountMST
Posted NoYes
PostedEOD NoYes

POSTable Form

Form Name Location Control Comments


POSTable
Data
Sources
SalesTable
SalesLine
Design
Header Use Tab
SalesTable.SalesId
RegisterId
OrderTotal
Lines Use Tab and Grid
SalesLine.ItemId
SalesLine.SalesQty
SalesLine.SalesPrice
SalesLine.LineAmount
ItemName

Register Id can be either prompted for when the form is opened or the selection is
mandatory on an order and remembered for subsequent orders.

Microsoft Official Training Materials for Microsoft Dynamics 1-5


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

SalesQty, SalesPrice, and LineAmount are calculated from the item table when
an item id is entered.

SalesPrice and LineAmount are calculated when the quantity is updated.

LineAmount is calculated when SalesPrice is updated.

Order Total = total Line Amount of all lines on the order.

POSRegisterTrans Form
The header section contains two buttons - one to call the POSRegisterTrans form,
and one to call the POSReceipt report.

Form Name Location Control Comments


POSRegisterTrans
Data
Sources
POSRegisterTrans
Design Use Tab
and Grid
POSRegisterTrans.PayModeId
POSRegisterTrans.AmountMST

RegisterId, SalesId, LineNum, PayDate, PayTime, and UserId are populated by


the system.

Reports

Element Name Location Control Comments


POSReciept
Data
Sources
SalesTable
SalesTable
SalesLine Inner join to
SalesTable
POSRegisterTrans Inner join to
SalesTable
Design
Header

1-6 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 1: Point-of-Sale Project

Element Name Location Control Comments


SalesTable.SalesId
Date
RegisterId
Lines
SalesLine.Name
SalesLine.AmountMST
Payment
POSPayTable.PayId
POSPayTable.AmountMST

Summary
This lesson leads you through creating the base elements for the POS module that
will be used throughout the entire Development IV course.

Microsoft Official Training Materials for Microsoft Dynamics 1-7


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

Quick Interaction: Lessons Learned


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

1.

2.

3.

1-8 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 2: Number Sequences

CHAPTER 2: NUMBER SEQUENCES


Objectives
The objectives are:

Use and create number sequences


Assign a new number using a number sequence
Use continuous number sequences
Format a number from a number sequence
Use Number Pre-Allocation
Use the Clean Up Process
Use the Form Handler
Use the NumberSeqReference table and methods

Introduction
Number sequences handle the automatic allocation of ID numbers, vouchers, and
journal numbers. Numbers are seen and referred to by the user; vouchers are used
by the system for tracking and linking transactions posted at the same time. In
some instances you can define a voucher series to follow a number series, for
example, invoice numbers and invoice vouchers. There are many number
sequences needed for the application, and all can be set up so that they have a
unique format and numbering range.

Microsoft Official Training Materials for Microsoft Dynamics 2-1


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

Overview
A number sequence is created under main menu > basic > setup > number
sequences > number sequence. Specify the smallest and largest numbers
allowed for the particular sequences, and also the next number to be used. The
format field controls the length of the number and is used to insert fixed
characters when desired.

The General tab provides the following functions:

Stops the sequence from being used


Specifies that the user should enter the number rather than have the
system assign it
Sets the number sequence to be continuous

Continuous number sequences ensure that no numbers in the sequence are lost. If
a number is not used, such as when a sales order is deleted before it is saved, that
number can be used later.

If a number from a continuous number sequence is used and the connection is


lost completely (if a machine crashes), the status of that number may be
unknown. A clean up job is available to set the status appropriately. This clean up
job is either called automatically by selecting Clean up on the Clean up tab of
the number sequence form, or manually by clicking the Clean up button on the
number sequence form.

Number sequences are assigned to a specific function using various parameter


forms in the application, against the journal names, and also by using a number
sequence group, for example, on the customer table.

Tables
The tables used for number sequences are:

NumberSequenceTable contains the definitions of each number


sequence.
NumberSequenceList holds numbers for continuous number
sequences that have not been completed or are currently reserved.
NumberSequenceReference holds which number sequence is used for
which function.
NumberSequenceGroup is a list of number sequence groups.
NumberSequenceGroupRef contains the number sequence references
specific to a group.

2-2 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 2: Number Sequences

NumberSequenceTTS holds the transaction id of a number before it


has been completely assigned. It is used during the clean-up process,
in case of a system crash.
NumberSequenceHistory holds a log of all changes made to the
number sequence.

Classes
The main classes used for number sequences are:

NumberSeq assigns numbers and vouchers, handles continuous


number sequences, and calls Clean up when appropriate.
NumberSeq_Fast is used for number sequences that are not
continuous. It does not keep a record of the status or store transaction
ids for later clean up, and is better performance-wise.
NumberSeqCleanUp looks for numbers in the list that have not been
completed, looks for the session that created them, and, if the session
is no longer active, frees up the number for later use.
NumberSeqDataArea is used in the clean-up process.
NumberSeqFormHandler is used whenever a number sequence
assigns a number in a form. It handles records being deleted and
ensures that two users cannot use the same number.
NumberSeqReference creates the link between the function and the
number sequence. NumberSeqReference is the super class used, and
there is a sub class for each module.
NumberSeqNumCache contains the method to manipulate the cache
of reserved numbers.
NumberSeqGlobal, a global instance, is available once instantiated. It
is used in with NumberSeqNumCache.

Microsoft Official Training Materials for Microsoft Dynamics 2-3


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

Assign a New Number


To assign a new number, you need to first instantiate the NumberSeq class.

Use one of the static methods attached to the NumberSeq class. There are two
sets of methods; one set is called newGet<function> and the other is
newReserve<function>. These methods are identical; newGetNum is the same as
newReserveNum.

The name of the static methods describes their functions. For example,
newGetNumAndVoucherFromCode() enables the instance to return both a
number and a voucher from a number sequence code (instead of a reference), and
may be used when posting an invoice where an invoice number and a voucher
number are required.

For the method newGetNumAndVoucher the parameters are:

_numberSequenceReference - This finds the number sequence used


for the number.
_voucherSequenceReference - This finds the number sequence used
for the voucher.
_makeDecisionLater - This signifies that the number is assigned
first, then set to be used later. This handles the case where a record is
created in a form, a number is assigned, but the record is never
committed to the database.
_dontThrowOnMissingRefSetUp - This is set to true so that if a
number sequence has not been set up for this reference, it does not
return a number. Otherwise, an error message is displayed.

The following code shows an example of using the


NumberSeq::NewGetNumAndVoucherMethod.

NumberSeq = NumberSeq::NewGetNumAndVoucher(
SalesParameter::NumRefInvoiceId(),
SalesParamters::NumRefInvoiceVoucher(), false, false)

To retrieve the number or voucher, you can use the num() or voucher() methods:

Voucher = NumberSeq.voucher();

An example of this is provided in Microsoft Dynamics AX in the


ProdTableType.Insert() class method. The method uses
NumberSeq::NewGetNum() to assign a new InventTransId.

2-4 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 2: Number Sequences

Continuous Number Sequences


When using a continuous number sequence, the system stores numbers as they
are assigned in NumberSequenceList, and sets a status of Active. Refer to the
NumberSeq.GetNumFromTable() method.

When writing code to assign a new number, decide what to do if the number
sequence has been set up as continuous. You can commit the number
immediately, or place it in the list to be updated later.

In a form, the decision may be made later, as the user may delete the record. In a
process, you do not need the decision to be made later, as this is controlled using
TTS.

If you do not make the decision later, the system creates the number in
NumberSequenceList, and cleans it up later, during the TTSCOMMIT. The clean
up is called in the xApplication.ttsNotifyCommit() method. The message "System
does not support setup of continuous number sequence." displays. This is caused
by attempting to get a new number from a number sequence set up as continuous,
but not doing so inside a TTS. Adding TTSBEGIN and TTSCOMMIT around
the code fixes the problem.

Use NumberSeq.Used() and NumberSeq.abort() to update the numbers sequence


list that the number is either used or aborted.

Format a Number
The method to format a number is called from the num() method, so normally
you do not need to call this method from your code.

If you do require to format a number in your code, this can be done by using the
NumberSeq::numInsertFormat() static method, which takes an integer number
and a format as parameters.

The format is specified using a combination of characters. Any fixed characters


should be entered. Any mandatory numerals can be specified using a "#".

Additionally, to have Microsoft Dynamics AX convert the integer to letters, enter


"&" for each mandatory letter.

Num = NumberSeq::numInsertFormat(30,"&&&");

In this case, Num would be set to "ABD".

Microsoft Official Training Materials for Microsoft Dynamics 2-5


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

Number Pre-Allocation
Processes that use many numbers from a single number sequence can improve
performance by using number pre-allocation. Basically, this pulls a set number of
numbers into the memory and provides faster access. For example, the inventory
close process uses voucher numbers if posting to the ledger. The overall speed
can be increased by allowing pre-allocation on the number sequence for Closing
Vouchers in Inventory Parameters.

Pre-allocation uses a global instance of the class NumberSeqGlobal, which


means that once it is instantiated, it is available until the session is closed. It is
declared in the global class, application.

The pre-allocated numbers are stored in a list, which is mapped to a key of a


combination of company and number sequence code. You can see where it stores
the reserved numbers in the NumberSeqNumCache.fillCache() class method.

This function is only available for non-continuous number sequences; the


numbers are only retrieved in the NumberSeq_Fast class. The getNumInternal()
method, shows retrieving numbers from the cache.

Clean Up Process
Automatic clean up is done by storing a list in memory of number sequences that
must be checked.

NumberSeqDataArea.setClean() shows the trans id and number sequence code


being stored in a list for clean up later.

The clean-up process is called when the current transaction is committed. It goes
through this list (created in setClean()) and finds any entries that have dead
sessions (NumberSeqCleanUp.isProcessDead()).

The manually started clean up process goes through either a specified number
sequence or all number sequences, looks at any records in the
NumberSequenceList, and checks whether they should be deleted.

2-6 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 2: Number Sequences

Form Handler
To help use number sequences in forms correctly, call the
NumberSequenceFormHandler class. Instantiate the class using the static method
NewForm(). The parameters are:

_NumberSequenceCode is the code of the number sequence to be


used.
_callerForm is the calling form.
_FormDataSource is the data source on the form for the record that
is to use the number.
_FieldIdNum is the field id of the field that is to use the number.
_dontThrowOnMissingSetup defines whether an error message
should be displayed if the number sequence has not been set up.

You must add code to call methods in the various datasource methods. The
names of the methods clearly define where they should go. For example:

numberSeqFormHandler.formMethodDataSourceDelete();

should be placed in the data source delete() method.

The CustTable form shows an example of how to use this class correctly.

NumberSeqReference
To create a new number sequence reference; for example, a newly created data
type that needs sequential numbers assigned to it, have the new data type created
in the number sequence references. This displays it in the appropriate modules
parameters form.

Procedure: Create a new number sequence


Creating a new number sequence is achieved by creating a new reference for the
sequence. Use the following steps to create a new number sequence:

1. Create a new Extended Data Type (EDT). Often this EDT extends
num. This is not mandatory, but it is a best practice.
2. Decide which module's parameters this number sequence reference
should be included in, and find the corresponding
NumberSeqReference sub class.
3. The loadModule() method shows a number of blocks of code, which
creates records in the table NumberSequenceReference.

Microsoft Official Training Materials for Microsoft Dynamics 2-7


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

4. Copy one of these blocks and change the following fields:


o DataTypeID is the type Id of the new data type.
o referenceLabel is the description shown in the left column of the
Number sequence tab on the parameters form.
o referenceHelp is the longer description of the reference shown in
the top part of the Number sequence tab of the parameters form.
o sortfield defines the sequence that the references are displayed
on the Number sequence tab of the parameters form.

5. There are a number of wizard fields used for default values when
using the wizard to create number sequences.
6. Create a static method that will be used to retrieve the reference. This
is usually done on the relevant parameters table. Parameters tables
show methods beginning with numRef. Use one of these methods as
a template.
7. The reference can then be referred to using this static method.

For example:

salesParameters::NumRefSalesID()

This returns the record in NumberSequenceReference that contains the reference


for SalesId.

Summary
This lesson provides an overview of how number sequences are used in the
application. It shows how number sequence API works and how to implement
number sequence API in the code.

2-8 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 2: Number Sequences

Test Your Knowledge


1. When assigning numbers from the numberseq class, when would you use
newGetNumAndVoucherFromCode() and newGetNumAndVoucher()?

2. What does it mean that a number sequence is continuous?

3. From which method would you get the number sequence reference used for a
sales order number?

Microsoft Official Training Materials for Microsoft Dynamics 2-9


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

Lab 2.1 - Add Pay Id number sequence


Scenario
To make sure POS transactions can be traced and are unique, a new Number
Sequence needs to be added for the POS module.

Challenge Yourself!
In the POS Module, create a new reference for the new EDT POSPayId. The
POS module should have its own sub class of NumberSeqReference, but should
be displayed in the SalesParameters form.

Need a Little Help?

1. Use NumberSeqReference_Sales as a reference.


2. You need the same methods - loadModule() and
numberSeqModule() - and adapt them accordingly.
3. You also need to modify the methods
NumberSeqReference::construct() and
NumberSeqReference::ModuleList().

Step by Step

1. Modify BaseEnum NumberSeqModule:


a. Add a new element POSModule.
b. Label = POS module.
c. EnumValue = 100.

2. Create new class NumberSeqReference_POSModule as follows:


a. Extend numberSeqReference.
b. Create method "numberSeqModule" \and return enum
NumberSeqModule with the value
NumberSeqModule::POSModule.
c. Create method "loadModule" similar to the method loadModule
on the NumberSeqReference_SalesOrder class.
d. Use the previously created extended datatype POSPayId for the
data type reference.
e. Use logisticBasic: as the configuration key.

2-10 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 2: Number Sequences

3. Modify NumberSeqReference class as follows:


a. Modify the method construct to initialize the newly created class
and return NumberSeqReference_POSModule(_module) if the
module parameter passed into this method is equal to
NumberSeqReference_POSModule::numberSeqModule().

4. Modify SalesParameters table as follows:


a. Create new method "numberSeqModule_POS," to return a
NumberSeqModule enum with the value found in
NumberSeqReference_POSModule::numberSeqModule()
similar to method numberSeqModule on salesParameters.
b. Create new method "numberSeqReference_POS," to return an
object of the type NumberSeqReference, with the value found in
method
NumberSeqReference::construct(SalesParameters::numberSeqM
odule_POS()) similar to method numberSeqReference on
salesParameters.
c. Create new method numRefPOSPayId, which returns a
NumberSequenceReference table with the value found in
NumberSeqReference::findReference(typeId2ExtendedTypeId(t
ypeid(POSPayId))).

5. Modify CustParameters form as follows:


a. Declare variable NumberSeqReference
numberSeqReferencePOSModule in the class declaration.

6. Add following to the method "numberSeqPreInit:"


a. Initialize above declared variable like this:
numberSeqReferencePOSModule =
SalesParameters::numberSeqReference_POS();
numberSeqReferencePOSModule.load();

7. Add following to the tmpIdRef.setTempDate


SalesParameters::numberSeqModule_POS()));

8. Add numberSeqReferencePOSModule.sameAsActive() to the


numberSequenceReference_ds.object method call, and
referenceSameAsLabel.visible method call in the method
numberSeqPostInit.
9. Add SalesParameters::numberSeqModule_POS() to the
this.queryRun statement in the method
NumberSequenceReference:ExecuteQuery.

Microsoft Official Training Materials for Microsoft Dynamics 2-11


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

Lab 2.2 - Use Form Handler


Scenario
The POS form should use the sales id and pay id number sequences. They should
be added using the form handler class.

Challenge Yourself!
Add the NumberSeqFormHandler methods to the forms POSTable and
POSPayTable to create new number for SalesId and POSPayId.

Need a Little Help?


Use the CustTable form as inspriration. Look for all references to
numberSeqFormHandler().

Step by Step

1. Modify the POSTable form as follows:


2. Declare variable NumberSeqFormHandler numberSeqFormHandler
in the class declaration method.
3. Create new method named numberSeqFormHandler, see the
custTable form.Use POS values instead of custTable values.
4. Override the method created on the salesTable datasource, see the
custTable form.
5. Override method delete on the salesTable datasource, see the
custTable form.
6. Override method write on the salesTable datasource, see the
custTable form.
7. Override method validateWrite on the salesTable datasource, see the
custTable form.
8. Override method LinkAcitve on the salesTable datasource, see the
custTable form.

2-12 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 2: Number Sequences

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 2-13


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

Solutions
Test Your Knowledge
1. When assigning numbers from the numberseq class, when would you use
newGetNumAndVoucherFromCode() and newGetNumAndVoucher()?

MODEL ANSWER: NewGetNumAndVoucher() is used to instantiate


NumberSeq from NumberSeqReference.
NewGetNumAndVoucherFromCode() is used to instantiate NumberSeq from
a NumberSeqCode.

2. What does it mean that a number sequence is continuous?

MODEL ANSWER: A continuous number sequence ensures that all numbers


in the sequence are used.

3. From which method would you get the number sequence reference used for a
sales order number?

MODEL ANSWER: SalesParameters.NumRefSalesId().

2-14 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 3: PrintJobSettings

CHAPTER 3: PRINTJOBSETTINGS
Objectives
The objectives are:

Set Print Options


Retrieve Print Options
Use Pack and Unpack to Store Settings
Use SysPrintOptions

Introduction
The PrintJobSettings class is used with report output. It is a system class that
contains methods and variables to hold all the settings that determine where and
how the output is handled. It can be used both to get and set these options, and
can also format all the options such that they can be easily saved and retrieved
later.

This lesson explains and provides examples on how to set and retrieve print
options and how to use pack and unpack to store these settings. Furthermore, the
class SysPrintOptions is briefly introduced.

Microsoft Official Training Materials for Microsoft Dynamics 3-1


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

Set Print Options


When printing a report, the normal print options form is shown.

FIGURE 3.1 THE PRINT OPTIONS SCREEN (SYSPRINTFORM)

All these options, and more, can be set within the code. For example:

printJobSettings.setTarget(PrintMedium::Mail)

sets the Send To option to E-mail recipient.

This is shown in the Cheque_US report in the init() method, where it forces the
target to be a printer.

Print Options methods


Additional options depend on what the target is as to whether it makes sense to
use them. If the target is Screen, it does not make sense to set the Mail To option.

Some examples of the other options are:

printJobSettings.mailTo(str) sets the 'Mail To' option. Str contains


one or more email addresses.
printJobSettings.format(printFormat) sets the file format when
writing to a file or to e-mail.
printJobSettings.allPages(boolean) specifies whether to print all
pages.
printJobSettings.paperOrientation(PrinterOrientation) sets the
orientation of the report, where PrtnterOrientation is an ENUM.
printJobSettings.fitToPage(Boolean) specifies whether the report can
be shrunk to fit the page.

3-2 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 3: PrintJobSettings

Example using Print Options


The following example shows how the prompt method in a report can be
overridden to force a copy of the report to be printed in a different tray.

public Boolean prompt(Boolean enableCopy=TRUE, Boolean


enablePages=TRUE, Boolean enableDevice=TRUE, Boolean
enableProperties=TRUE, Boolean enablePrintTo=TRUE)

Boolean ret;

int trayCount;

int i;

printJobSettings printJobSettings =
element.printJobSettings();

ret = super(enableCopy, enablePages, enableDevice,


enableProperties, enablePrintTo);

if (ret)

// store the name of the printer the user selected


above

deviceName = printJobSettings.deviceName(); // <-


this line may not be needed.

printJobSettings.copies(1); // number of copies to


print

printJobSettings.addTrayPageCopy(0, -1, 1) // from


copy one use this tray

return ret;

Microsoft Official Training Materials for Microsoft Dynamics 3-3


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

Retrieve Print Options


As with setting the print options, it may be necessary to determine what the user
has selected to take appropriate action. For example, during a sales update, in a
normal live situation, you do not print invoices to the Screen. The system checks
what target has been selected and if the user has selected the screen, it confirms
that this is correct.

The code for this can be seen in the SalesFormLetter.validate() class method,
where the instance of PrintJobSettings is instantiated using a container stored for
each document type and user, and if the target is Screen, then a warning is given.

Use of Pack and Unpack to Store Settings


All the settings the user selects or that you set within the code can be stored and
retrieved. Use PrintJobSettings.PackPrintJobSettings() to create a container with
all options. This can be stored in a field of type container for later retrieval.

A container that includes printer options can be used when instantiating


PrintJobSettings which sets all the options.

Calling ReportRun.unpackPrinterSettings(container) retrieves the printer options


for that report from the container.

Last user-selected printer settings are stored in the Usage Data (sysLastValue) as
with values stored in pack and unpack in RunBase. They can be seen in the
Usages Data form, type = Report, name = report name.

Review the clicked method on form ProdParmStartUp, printButton. This


contains an example of both packing and unpacking a print options container.

3-4 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 3: PrintJobSettings

Use of SysPrintOptions
SysPrintOptions is a class that has methods written for using PrintJobSettings.

When printing, processing is done on the Application Object Server (AOS), but
the printer may be local to the client, or not installed on the server. Printing
works differently on the server than on the client. SysPrintOptions can be used to
circumvent these issues.

This class is used by the system to control printing, and is not normally needed to
be called by the developer.

Build list of Printers


The following example shows how to use SysPrintOptions to display a list of all
available printers.

static void ShowListOfPrinters(Args _args)


{
printJobSettings printJobSettings;
sysPrintOptions sysPrintOptions;
map printerMap;
mapIterator mapIterator;
;

printJobSettings =
SysPrintOptions::newPrintJobSettingsOnServer();

sysPrintOptions = new sysPrintOptions();

sysPrintOptions.setPrintJobSettings(printJobSettings);

sysPrintOptions.buildPrinterMap();

printerMap = sysPrintOptions.getPrinterMap();

mapIterator = new mapIterator(printerMap);

mapIterator.begin();
while (mapIterator.more())
{
info(mapIterator.value());
mapIterator.next();
}

Microsoft Official Training Materials for Microsoft Dynamics 3-5


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

Summary
This lesson describes how to use the system class PrintJobSettings in connection
with report output.

The lesson explains and provides examples on how to set and retrieve print
options and how to use pack and unpack to store these settings.

Also, the class SysPrintOptions is briefly introduced.

3-6 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 3: PrintJobSettings

Test Your Knowledge


1. What is the syntax for setting the default printer option to e-mail in the
PrintJobSetting class?

2. Which method would you use to initialize printJobSettings for a specific


report, when this report is called from a class?

3. Where does Microsoft Dynamics AX store the previously selected printer


settings for a specific report?

Microsoft Official Training Materials for Microsoft Dynamics 3-7


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

Lab 3.1 - Suppress Scaling Message


Scenario
When the sales order lines report is run with all options selected, the report needs
to be scaled to fit onto the page. A message is shown saying that the report has
been scaled. Isaac, the systems developer, has been asked to stop the message
from appearing, even if the report has been scaled.

Challenge Yourself!
Use the printJobSettings.suppressScalingMessage() to stop the message from
appearing. The report to modify is SalesLinesExtended.

Step by Step
Add the following code to the init() method of the report SalesLinesExtended. It
should go after the call to super().

element.printJobSettings().suppressScalingMessage(true);

3-8 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 3: PrintJobSettings

Lab 3.2 - Print Job Settings


Scenario
Each POS Register has a printer. A function to set which printer each register
uses is needed.

Challenge Yourself!
In the POS module, enable printer options to be set up and saved for each
register.

Need a Little Help?

1. Add a container field to the register table, to store the print job
settings for the register.
2. To see similar functionality where printer settings are set and saved,
look at the Production order Startup parameters, pick list printer
settings.

Step by Step

1. Create a new container EDT, POSRegisterPrintJobSettings.


2. Create a new field POSRegisterTable.printJobSettins, type =
POSRegisterPrintJobSettings.
3. Create a new method on table POSRegisterTable to call the standard
printer options form and store the return container in
POSRegisterTable.PrintJobSettings.

HINT: Look at the Production order Startup parameterspick list printer settings.

4. Add a button to the POS Register table form to call this new method.

Microsoft Official Training Materials for Microsoft Dynamics 3-9


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

Lab 3.3: Print the receipt to the correct printer


Scenario
When a receipt is printed, the system should automatically pick which printer to
use based which register is being used.

Challenge Yourself!
When the receipt is printed, set the printer options to the options stored against
the appropriate register.

Step By Step

1. Retrieve the register id from the sales order record.


2. In the receipt report, set the printJobSettings from the container
stored against the register table.
3. Use the method this.PrintJobSettings().UnpackPrintJobSettings() to
set the printer settings for the report from the container.

3-10 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 3: PrintJobSettings

Lab 3.4 - Bypass Print Options


Scenario
When a receipt is printed, the user should not have to select which printer to use.
The print options form should not be displayed.

Challenge Yourself!
Remove the ability for the user to change the printer options on the receipt by not
displaying any prompt for the report. Use the print options selected for the
register. As soon as the user clicks the Receipt button, it should print.

Step By Step

1. Override the prompt method on the report, and remove the call to
super(). Return true from the method.

Microsoft Official Training Materials for Microsoft Dynamics 3-11


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

Quick Interaction: Lessons Learned


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

1.

2.

3.

3-12 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 3: PrintJobSettings

Solutions
Test Your Knowledge
1. What is the syntax for setting the default printer option to e-mail in the
PrintJobSetting class?

MODEL ANSWER: PrintJobSettings.SetTarget(PrintMedium::Mail).

2. Which method would you use to initialize printJobSettings for a specific


report, when this report is called from a class?

MODEL ANSWER: ReportRun.UnpackPrintJobSettings.

3. Where does Microsoft Dynamics AX store the previously selected printer


settings for a specific report?

MODEL ANSWER: Usage Data (SysLastValue).

Microsoft Official Training Materials for Microsoft Dynamics 3-13


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

3-14 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 4: Business Connector

CHAPTER 4: BUSINESS CONNECTOR


Objectives
The objectives are:

Describe the purpose of the Business Connector.


Set up and manage the Business Connector.
Debug code through the Business Connector.
Describe the architecture of the Business Connector.
Use the managed classes in the Business Connector.
List various uses of the Business Connector.

Introduction
When using the Microsoft Dynamics AX 2009 Business Connector, other
applications can access Microsoft Dynamics AX 2009 as a .NET object. This
implies that the application can gain access to the data and business logic of
Microsoft Dynamics AX 2009, which enables the use of applications as front
ends, other than Microsoft Dynamics AX 2009.

Microsoft Official Training Materials for Microsoft Dynamics 4-1


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

Business Connector Overview


The Business Connector is a Microsoft Dynamics AX 2009 component that
enables external applications to interact with Application Object Server
instances.

.NET Platform
The Business Connector is based on the .NET platform and provides a set of
managed classes that provide easy access to X++ functionality in Microsoft
Dynamics AX 2009. It supports the functionality in the Enterprise Portal server,
Reporting server, and Application Integration server roles. The Business
Connector can be installed as a stand-alone component and used to develop third-
party applications that integrate with Microsoft Dynamics AX 2009. The
following are some characteristics of the Business Connector:

Requires Microsoft Windows authentication.


Automatically registered during installation.
The Business Connector Proxy user, which "acts-on-behalf-of"
Microsoft Dynamics AX 2009 users who cannot be fully
authenticated.

4-2 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 4: Business Connector

Business Connector Setup and Configuration


Registration
The Business Connector in Microsoft Dynamics AX 2009 is registered
automatically, during installation.

Proxy
The Business Connector Proxy is a Windows domain user account that is used to
enable the Business Connector to "act-on-behalf" of Microsoft Dynamics AX
2009 users who cannot be fully authenticated. The Business Connector Proxy has
unique configuration settings that can be modified. A configuration target field
has been added to the utility to enable you to select the Business Connector
Proxy.

Only Administrators and Power Users on a computer running Microsoft


Dynamics AX 2009 with a Business Connector proxy user can modify the
Business Connector proxy user configuration options.

For more information on the proxy user, consult the Microsoft Dynamics AX
2009 Administrator Guide, found under the Help menu.

Event Monitoring
The Microsoft Dynamics AX 2009 Business Connector interfaces with the Event
Viewer component, which is an integrated part of the Microsoft Windows Vista,
Windows XP, Windows 2000, and Windows NT 4.0 operating system. The
Microsoft Dynamics AX 2009 Business Connector logs specific events to the
Application Log of the Event Viewer. For instance, the Microsoft Dynamics AX
2009 Business Connector logs whenever it is started or stopped. Any unexpected
events are also logged in the Application Log for further investigation by the
administrator.

To open the Event Viewer in Windows Vista, XP and Windows 2000, go to


Control Panel > Administrative Tools > Event Viewer. In the Event Viewer,
select the Application Log folder.

All events logged by the Microsoft Dynamics AX 2009 Business Connector have
a source name of "Microsoft Dynamics AX Business Connector."

Debugging
In Microsoft Dynamics AX 2009 it is possible to enable user breakpoints for
X++ code running in the Business Connector, and global breakpoints for X++
code running in the Business Connector or in a user session. A global breakpoint
is one that is set for a computer, instead of a user, and can be shared between
developers.

Microsoft Official Training Materials for Microsoft Dynamics 4-3


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

Procedure - Enabling Breakpoints in the Business


Connector
Use the following procedure to enable breakpoints in the Business Connector:

1. Open the Microsoft Dynamics AX 2009 Configuration Utility.

FIGURE 4.1 MICROSOFT DYNAMICS AX 2009 CONFIGURATION UTILITY,


DEVELOPER TAB

2. Select the Local Client setting in the Configuration target.


3. Select the Configuration that you want to enable breakpoints for.
4. Select Enable user breakpoints to debug code running in the
Business Connector and/or Enable global breakpoints to debug
code running in the Business Connector or client.
5. Click OK.
6. A warning message may appear explaining that debugging is enabled
but not started.

FIGURE 4.2 WARNING MESSAGE WHEN CLOSING CONFIGURATION TOOL

4-4 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 4: Business Connector

To start debugging, log on to the client, and follow this path: Tools > Options >
Developer tab.

7. Make sure Debug mode is set to When Breakpoint.

FIGURE 4.3 OPTIONS FORM, DEVELOPMENT TAB

8. Debugging must also be enabled on the Application Object Server


(AOS). Open the Microsoft Dynamics AX Server Configuration
tool.

FIGURE 4.4 MICROSOFT DYNAMICS AX SERVER CONFIGURATION


UTILITY

Microsoft Official Training Materials for Microsoft Dynamics 4-5


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

9. On the profile for the AOS instance that the Business Connector will
talk to, select Enable breakpoints to debug X++ code running on
this server.
10. For a .NET application to use the Microsoft Dynamics AX 2009
debugger for displaying breakpoints, an instance of the Microsoft
Dynamics AX 2009 debugger must be running. This can be achieved
by following this path in a Microsoft Dynamics AX client: Tools >
Development tools > Debugger.
11. When running the .NET code in Visual Studio, it must be run in
debug mode for the X++ breakpoints to work.

To ensure that the Visual Studio debugger jumps to the Microsoft Dynamics AX
2009 debugger, when a breakpoint exists in X++ code called from .NET code,
the following must be in place:

Debugging is enabled on Microsoft Dynamics AX 2009 AOS


configuration.
Debugging is enabled on Microsoft Dynamics AX 2009 client
configuration.
Debug mode is set to When Breakpoint in Client Options in
Microsoft Dynamics AX 2009, on a Client running the same
configuration.
An instance of the Microsoft Dynamics AX 2009 Debugger is
running, launched from a Client running the same configuration.
The Visual Studio .NET code is running in Debug mode.

4-6 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 4: Business Connector

Business Connector Architecture


From a simplified architecture viewpoint, the .NET Business Connector consists
of three layers:

Managed classes: Exposes public methods that can be called to


interact with Microsoft Dynamics AX 2009.
Transition layer: Maps the managed classes to the corresponding
interpreter functions.
Interpreter layer: Executes X++ and communicates with the AOS.

FIGURE 4.5 SIMPLIFIED .NET BUSINESS CONNECTOR ARCHITECTURE

Managed Classes
The following managed classes are provided by the .NET Business Connector:

Axapta
AxaptaBuffer
AxaptaContainer
AxaptaObject
AxaptaRecord
VariantWrapper

These classes can be used in Visual Studio projects to interface with Microsoft
Dynamics AX 2009, through the .NET Business Connector. Each class contains a
collection of methods that can be called to perform business logic in Microsoft
Dynamics AX 2009.

Microsoft Official Training Materials for Microsoft Dynamics 4-7


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

All these classes are found in the namespace:

Microsoft.Dynamics.BusinessConnectorNet, and assembly


Microsoft.Dynamics.BusinessConnectorNet (in
microsoft.dynamics.businessconnectornet.dll which is located in the bin directory
in the Microsoft Dynamics AX 2009 Client directory)

Axapta Class - The Axapta class provides the ability to connect to the Microsoft
Dynamics AX 2009 system, create Microsoft Dynamics AX 2009 classes, create
Microsoft Dynamics AX 2009 record, container, and buffer objects, execute
transactions, and perform other Microsoft Dynamics AX 2009 system tasks. The
Axapta class also contains methods to call static class methods, static table
methods, jobs, and TTS commands.

AxaptaBuffer Class - The AxaptaBuffer class provides the ability to add data to
and retrieve data from a Microsoft Dynamics AX 2009 buffer. An AxaptaBuffer
object can be used with AxaptaContainer objects.

AxaptaContainer Class - The AxaptaContainer class provides the ability to read


and write to and from Microsoft Dynamics AX 2009 containers.

AxaptaObject Class - The AxaptaObject class provides the ability to call


Microsoft Dynamics AX 2009 class methods.

AxaptaRecord Class - The AxaptaRecord class provides functionality for reading


and modifying Microsoft Dynamics AX 2009 records.

All of these classes (except Axapta) have a corresponding NotValidException


class (for example, AxaptaContainerNotValidException).

There are 27 exception or invalid classes available in the


Microsoft.Dynamics.BusinessConnectorNet namespace. For more information on
these classes, view the Microsoft Dynamics AX .NET Framework topic in the
Developing for Microsoft Dynamics AX helpbook within the application.

The following are examples of Visual Basic code in Visual Studio, which
execute Microsoft Dynamics AX 2009 business logic through the Business
Connector. (Examples using other .NET languages can be found in the Developer
Help in the Microsoft Dynamics AX 2009 application.)

Subsequent boxes assume that previous variables and objects are still available
(for example, object axapta1).

4-8 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 4: Business Connector

Log on to Microsoft Dynamics AX through the Business


Connector
Dim company As String

Dim language As String

Dim objectServer As String

Dim configuration As String

Dim axapta1 As New Axapta

company ="dmo"

language ="en-us"

objectServer ="objectServerName"

configuration ="configurationName"

axapta1.Logon(company, language, objectServer,


configuration)

Log on as the Business Connector Proxy User to


Microsoft Dynamics AX

Parameter Description
user (String) Name of the Windows user to use for logging
on to Microsoft Dynamics AX 2009. This parameter is
optional.
domain (String) The domain associated with the Windows
user. This parameter is optional
bcProxyCredentials (NetworkCredential) A .NET network credential
object.
company (String) The company to activate. This parameter is
optional and is used to override the corresponding
parameter in the configuration being used.
language (String) The language to use for Microsoft Dynamics
AX 2009 labels. This parameter is optional and is used
to override the corresponding parameter in the
configuration being used.
objectServer (String) Name of the Microsoft Dynamics AX 2009
Object Server to connect to. This parameter is optional
and is used to override the corresponding parameter in
the configuration being used.

Microsoft Official Training Materials for Microsoft Dynamics 4-9


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

Parameter Description
configuration (String) Name of Microsoft Dynamics AX 2009
configuration to use while logging on. This parameter
is optional. If null is specified for this parameter and
Business Connector is being executed within the
context of an interactive Windows account, Business
Connector will use the default configuration
(maintained by the Microsoft Dynamics AX Client
Configuration Utility). If Business Connector is being
executed within the context of a non-interactive
Windows account (such as the Business Connector
Proxy AD user), then this parameter is used to specify
the path which points to an exported configuration file.

The following is a code example using the LogonAs() method:

Dim company As String

Dim language As String

Dim objectServer As String

Dim configuration As String

Dim axapta1 As Axapta

Dim bcProxyCredentials As New


NetworkCredential("proxyUsername","proxyPassword","domain")

Dim username As String

Dim domain As String

company ="dmo"

language ="en-us"

objectServer ="objectServerName"

configuration ="configurationName"

username ="proxyUsername"

domain ="domain"

axapta1.LogonAs(username, domain, bcProxyCredentials,


company, language, objectServer, configuration)

4-10 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 4: Business Connector

Calling calcPrice Method from a Custom X++ Class Called


SalesLine_Net

Dim className As String

Dim methodName As String

Dim itemId As Object

Dim qty As Object

Dim returnValue As Object

Dim salesLine_Net As AxaptaObject

className ="salesLine_Net"

methodName ="calcPrice"

itemId ="001"

qty ="5"

salesLine_Net = axapta1.CreateAxaptaObject(className)

returnValue = salesLine_Net.Call(methodName, itemId, qty)

Executing SQL Statement Against the Microsoft


Dynamics AX InventTable Table

Dim statement As String

Dim inventTableRecord As AxaptaRecord

Dim tableName As String

tableName ="InventTable"

inventTableRecord = axapta1.CreateAxaptaRecord(tableName)

statement ="select * from %1 where %1.itemId=='001'"

axapta1.ExecuteStmt(statement, inventTableRecord)

Microsoft Official Training Materials for Microsoft Dynamics 4-11


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

Summary
The Business Connector provides a conduit between Microsoft Dynamics AX
2009 and other applications. Besides the most common use of supporting the
connection between SharePoint and Microsoft Dynamics AX 2009 for the
Enterprise Portal, the Business Connector also opens up the existing Microsoft
Dynamics AX 2009 business logic and data to external applications within your
business scope.

By understanding how to effectively install and configure the Business


Connector, and then write and debug code on both sides of the Connector (both
.NET and X++), the applications developed for the business are more
coordinated and effective.

4-12 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 4: Business Connector

Test Your Knowledge


1. Describe the purpose of the Business Connector.

2. Do you need to register the .NET connector into the Global Assembly Cache
after it is installed?

3. What is the Business Connector Proxy User account used for?

Microsoft Official Training Materials for Microsoft Dynamics 4-13


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

4. How is debugging through the Business Connector enabled?

5. What is the purpose of the Managed Classes in the .NET Business


Connector?

6. Using a .NET language, in Visual Studio how would you create a new
instance of the SalesFormLetter Microsoft Dynamics AX 2009 class?

4-14 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 4: Business Connector

Lab 4.1 - Business Connector


Scenario
This is a part of the Point-of-Sale Case Study. See Chapter 1 for details.

Challenge Yourself!
Create a front end for the POS module using a .NET form that can be used in a
'supermarket' environment. The form should have a large display, can display
item descriptions and prices, and a large number pad (so touch screens can be
used). There should also be buttons that can complete an order and delete the last
line.

Create the touch screen front end in a .NET form.


Create buttons to add a sales line, delete last sales line, and to
complete an order.

Step by Step
As you may not be familiar with .NET, this form has been created for you. It has
been created using C# .NET as the syntax for C# is similar to X++.

There are two versions available for use:

The first version, POSConsole_Complete is completed and needs no additional


programming in C# .NET. You may still view the C# code for thus console by
following the steps below, using the files from POSConsole_Complete.zip.

To use this version, run the executable POSConsole.exe, included in the file
POSConsole.zip, which then uses the Microsoft Dynamics AX 2009 Business
Connector to communicate with the installation. You still need to write the
necessary code in Microsoft Dynamics AX 2009, as detailed below.

The second version has most of the interface built already, but you need to add
the procedure to call the post invoice function.

Microsoft Official Training Materials for Microsoft Dynamics 4-15


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

Do as follows:

In Visual Studio 2005

1. The following files are included in POSConsole_Incomplete.zip and


contain the C# .NET project solution. Place them in a new directory
called POSConsole in the Visual Studio Projects directory:
o app.ico
o assemblyInfo.cs
o POSConsole.cs
o POSConsole.resx
o POSConsole.csproj
o POSConsole.sln

2. Open the C# .NET development environment: select file > open


solution and select the POSConsole.sln file
3. Add a call to the Microsoft Dynamics AX 2009 class method
CSharp_SalesOrder.completeOrder() to the method
buttonComplete_Click() so the invoice posts when the Complete
Order button is clicked.
4. Select build > build posconsole to build the executable, which can
then be found in the directory Visual Studio Projects \ POSConsole \
Bin \ Debug.

In Microsoft Dynamics AX 2009

The POS executable calls the following class methods. Create these classes and
methods:

1. Class CSharp_SalesOrder which is used in conjunction with creating


and deleting sales orders.
a. CSharp_SalesOrder.checkItemId(itemId _itemId) :returns true if
item id is valid.
b. CSharp_SalesOrder.completeOrder(SalesId _SalesId): posts an
invoice for the specified sales order. The routines to post an
invoice, post a payment, and settle the transactions are written in
chapter 11, so this method can remain empty for now.
c. CSharp_SalesOrder.createSO(): creates a sales order and returns
the sales id. Use a parameter driven customer account.
d. CSharp_SalesOrder.deleteSalesLine(SalesId _salesId, itemId
+itemId): Deletes sales line using sales id and item id.
e. CSharp_SalesOrder.deleteSalesOrder(SalesId _SalesId): deletes
all lines and the salesTable record for the specified sales id.
f. CSharp_SalesOrder.insertSalesLine(itemId _itemId, qty _qty,
salesId _SalesId): Creates a sales line for the specified item, qty
and sales id.

4-16 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 4: Business Connector

2. Class SalesOrderIntegration
a. SalesOrderIntegration.getItemName(ItemId _itemId): returns
item name or specified item id.
b. SalesOrderIntegration.getSalesPrice(ItemId _itemId, qty _qty):
returns the sales price for the item id and the qty specified.
c. SalesOrderIntegration.getSalesTotal(SalesId _salesId): returns
the total amount for the specified sales order.

Do as follows:

In Visual Studio 2005

1. The following files are included in POSConsole_Incomplete.zip and


contain the C# .NET project solution. Place them in a new directory
called POSConsole in the Visual Studio Projects directory:
o app.ico
o assemblyInfo.cs
o POSConsole.cs
o POSConsole.resx
o POSConsole.csproj
o POSConsole.sln

2. Open the C# .NET development environment: select file > open


solution and select the POSConsole.sln file
3. Add a call to the Microsoft Dynamics AX 2009 class method
CSharp_SalesOrder.completeOrder() to the method
buttonComplete_Click() so the invoice posts when the Complete
Order button is clicked.
4. 4. Select build > build posconsole to build the executable, which
can then be found in the directory Visual Studio Projects \
POSConsole \ Bin \ Debug.

Microsoft Official Training Materials for Microsoft Dynamics 4-17


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

In Microsoft Dynamics AX 2009

The POS executable calls the following class methods. Create these classes and
methods:

1. Class CSharp_SalesOrder which is used in conjunction with creating


and deleting sales orders.
a. CSharp_SalesOrder.checkItemId(itemId _itemId) :returns true if
item id is valid.
b. CSharp_SalesOrder.completeOrder(SalesId _SalesId): posts an
invoice for the specified sales order. The routines to post an
invoice, post a payment, and settle the transactions are written in
chapter 11, so this method can remain empty for now.
c. CSharp_SalesOrder.createSO(): creates a sales order and returns
the sales id. Use a parameter driven customer account.
d. CSharp_SalesOrder.deleteSalesLine(SalesId _salesId, itemId
+itemId): Deletes sales line using sales id and item id.
e. CSharp_SalesOrder.deleteSalesOrder(SalesId _SalesId): deletes
all lines and the salesTable record for the specified sales id.
f. CSharp_SalesOrder.insertSalesLine(itemId _itemId, qty _qty,
salesId _SalesId): Creates a sales line for the specified item, qty
and sales id.

2. Class SalesOrderIntegration
a. SalesOrderIntegration.getItemName(ItemId _itemId): returns
item name or specified item id.
b. SalesOrderIntegration.getSalesPrice(ItemId _itemId, qty _qty):
returns the sales price for the item id and the qty specified.
c. SalesOrderIntegration.getSalesTotal(SalesId _salesId): returns
the total amount for the specified sales order.

4-18 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 4: Business Connector

Lab 4.1 - Business Connector (Solution)


Scenario
Either:

Create a front end for the POS module using a .NET form that could be used in a
'supermarket' environment. The form should have a large display, can display
item descriptions and prices, and a large number pad (so touch screens can be
used). There should also be buttons that can complete an order and delete the last
line.

Or:

You may use the front end already created for you in C#. This form already has
most of the interface built already, but you will need to add the procedure to post
the invoice.

Step by Step
Do as follows:

In Visual Studio 2005

1. The following files are included in POSConsole_Incomplete.zip and


contain the C# .NET project solution. Place them in a new directory
called POSConsole in the Visual Studio Projects directory:
o app.ico
o assemblyInfo.cs
o POSConsole.cs
o POSConsole.resx
o POSConsole.csproj
o POSConsole.sln

2. Open the C# .NET development environment: select file > open


solution and select the POSConsole.sln file
3. 3. Add a call to the Microsoft Dynamics AX 2009 class method
CSharp_SalesOrder.completeOrder() to the method
buttonComplete_Click() so the invoice posts when the Complete
Order button is clicked.
4. 4. Select build > build posconsole to build the executable, which
can then be found in the directory Visual Studio Projects \
POSConsole \ Bin \ Debug.

Microsoft Official Training Materials for Microsoft Dynamics 4-19


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

In Microsoft Dynamics AX 2009

The POS executable calls the following class methods. Create these classes and
methods:

1. Class CSharp_SalesOrder which is used in conjunction with creating


and deleting sales orders.
a. CSharp_SalesOrder.checkItemId(itemId _itemId) :returns true if
item id is valid.
b. CSharp_SalesOrder.completeOrder(SalesId _SalesId): posts an
invoice for the specified sales order. The routines to post an
invoice, post a payment, and settle the transactions are written in
chapter 11, so this method can remain empty for now.
c. CSharp_SalesOrder.createSO(): creates a sales order and returns
the sales id. Use a parameter driven customer account.
d. CSharp_SalesOrder.deleteSalesLine(SalesId _salesId, itemId
+itemId): Deletes sales line using sales id and item id.
e. CSharp_SalesOrder.deleteSalesOrder(SalesId _SalesId): deletes
all lines and the salesTable record for the specified sales id.
f. CSharp_SalesOrder.insertSalesLine(itemId _itemId, qty _qty,
salesId _SalesId): Creates a sales line for the specified item, qty
and sales id.

2. Class SalesOrderIntegration
a. SalesOrderIntegration.getItemName(ItemId _itemId): returns
item name or specified item id.
b. SalesOrderIntegration.getSalesPrice(ItemId _itemId, qty _qty):
returns the sales price for the item id and the qty specified.
c. SalesOrderIntegration.getSalesTotal(SalesId _salesId): returns
the total amount for the specified sales order.

4-20 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 4: Business Connector

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 4-21


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

Solutions
Test Your Knowledge
1. Describe the purpose of the Business Connector.

MODEL ANSWER: The Business Connector enables external applications


to interact with Microsoft Dynamics AX 2009 Application Object Server
instances.

2. Do you need to register the .NET connector into the Global Assembly Cache
after it is installed?

MODEL ANSWER: No. It is registered automatically during installation.

3. What is the Business Connector Proxy User account used for?

MODEL ANSWER: It is used to enable the Business Connector to "act-on-


behalf" of Microsoft Dynamics AX users who cannot be fully authenticated

4. How is debugging through the Business Connector enabled?

MODEL ANSWER: Enable debugging on the AOS configuration, Enable


debugging on the Client configuration, Set Debug mode to When Breakpoint,
Open an instance of the Microsoft Dynamics AX debugger.

5. What is the purpose of the Managed Classes in the .NET Business


Connector?

MODEL ANSWER: They expose public methods that can be called to


interact with Microsoft Dynamics AX.

6. Using a .NET language, in Visual Studio how would you create a new
instance of the SalesFormLetter Microsoft Dynamics AX 2009 class?

MODEL ANSWER:

//Visual Basic
Dim salesFormLetter As AxaptaObject
Dim axapta1 As Axapta
Dim className As String

axapta1.Logon("", "", "", "")

className = "salesFormLetter"
salesFormLetter = axapta1.CreateAxaptaObject(className)

4-22 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 5: CLR Interoperability

CHAPTER 5: CLR INTEROPERABILITY


Objectives
The objectives are:

Reference Common Runtime Language (CLR) assemblies in the


Microsoft Dynamics AX 2009 Application Object Tree (AOT).
Add assemblies to the Global Assembly Cache.
Write X++ code that interacts with managed code in external
applications.
Secure CLR Interop code.
Consume external web services from X++ code.
Understand how and where the use of DLL's is implemented in
Microsoft Dynamics AX 2009.

Introduction
The Common Runtime Language (CLR) Interoperability (shortened to Interop)
feature enables X++ developers to add CLR assemblies to the AOT and to write
X++ code that interoperates with objects in these assemblies. This provides
access to a vast array of prefabricated code, to leverage functions already
available in the operating system or external applications. This lesson describes
how to reference assemblies in Microsoft Dynamics AX 2009 and leverage them
in X++ code.

Microsoft Official Training Materials for Microsoft Dynamics 5-1


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

Reference CLR Assemblies in the AOT


CLR assemblies contain managed code designed to be consumed by other
applications. The assemblies often take the form of DLL files. To use the
managed code within an assembly in X++ code, a developer must first create a
reference to the assembly in the Microsoft Dynamics AX 2009 AOT.

To see the assemblies already referenced in the AOT, expand the References
node. The following figure illustrates the sys layer assembly references.

FIGURE 5.1 THE REFERENCES NODE ON THE AOT

Procedure - Reference a New Assembly in the AOT


Use the following procedure to reference a new assembly in the AOT:

1. Right-click AOT > References.


2. Select Add Reference.
3. The Add Reference form appears.
4. The Add reference form contains all the assemblies that have been
registered in the Global Assembly Cache (GAC). However, you are
not restricted to just these assemblies. Click the Browse button to
select an assembly from another location.

5-2 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 5: CLR Interoperability

5. Click the Select button to include one or more assemblies in the


lower window. The assemblies in the lower window are added to the
AOT by clicking OK. In this example choose
Microsoft.Office.Interop.Word and Microsoft.Office.Interop.Excel
which are both available from the GAC list.
6. After selecting all the assemblies, click OK.
7. The assemblies are visible in the AOT > References node. The
following new assemblies are referenced in the AOT:
Microsoft.Office.Interop.Word and Microsoft.Office.Interop.Excel.

Adding Assemblies to the Global Assembly Cache or Client


The Global Assembly Cache (GAC) is a directory in the Windows Server file
system. It provides a controlled central repository for all DLL files on a server, to
avoid problems that can arise when DLL files are stored in different places by
each developer, across different servers (for example, the Development, Test and
Production servers). The GAC is located in the following path:

\%winnt%\assembly

Install assemblies available to all clients into the GAC on each Application
Object Server (AOS) servicing the clients. Remember to install server assemblies
into the GAC on the development, test, and production AOS servers.

IMPORTANT: Code using assemblies installed on the AOS must be set to run on
the server, not on the client.

The GAC enforces that all assemblies installed into it are strongly named. A
strong name consists of the assembly's identity (the simple text name, version
number, and culture information (if provided)), a public key and a digital
signature. It is generated from an assembly file (the file that contains the
assembly manifest, which contains the names and hashes of all the files that
make up the assembly), using the corresponding private key. Microsoft Visual
Studio .NET and other development tools provided in the .NET Framework
SDK can assign strong names to an assembly. Assemblies with the same strong
name are expected to be identical.

HINT: Refer to the MSDN on strongly named assemblies for more information.

Alternatively, it is possible to install assemblies on each of the client machines.


Be sure to install the assemblies in the same location on each machine, to retain
the referenced location in the AOT. The recommended location for client
assemblies is the bin directory on the client. For example:

C:\Program Files\Microsoft Dynamics AX\50\Client\Bin

Microsoft Official Training Materials for Microsoft Dynamics 5-3


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

Leverage CLR Managed Code Within X++ Code


To use the functionality in managed code in an assembly, first reference the
assembly in the AOT, as described in the previous section. Referenced assembly
names can then be used in X++ code.

There are many .NET assemblies available in the Windows operating system.
One example is the System.Net.Mail assembly, which provides code to send e-
mails. The following is an example of X++ code which leverages the code in the
System.Net.Mail assembly, to create an email with a multiple attachments:

System.Net.Mail.MailMessage mailMessage;
System.Net.Mail.Attachment attachment;
System.Net.Mail.AttachmentCollection attachementCollection;
;

mailMessage = new
System.Net.Mail.MailMessage("me@contoso.com","you@contoso.c
om");

attachementCollection = mailMessage.get_Attachments();

attachment = new
System.Net.Mail.Attachment("c:\\myfile.txt");

attachementCollection.Add(attachment);

attachment = new
System.Net.Mail.Attachment("c:\\myphoto.jpg");

attachementCollection.Add(attachment);

Alternatively, custom assemblies can be created in .NET.

NOTE: CLR assemblies and their contents are case sensitive.

5-4 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 5: CLR Interoperability

Refer to this example of a small .NET assembly:

//.NET code
namespace SampleCLR
{
public class HelloWorld
{
public string sayHello()
{
return "Hello CLR Interop World!";
}

public int add(int a, int b)


{
return a + b;
}
}
}

If this assembly was available as a DLL file, it could be added to the References
in the AOT. Once referenced, call the assemblies contents directly in X++ code.
The following X++ code example leverages the .NET code in the previous
assembly:

//X++ code
static void JobCLR(Args _args)
{
SampleCLR.HelloWorld hw;
str s;
int i;
;

hw = new DemoCLR.HelloWorld();
s = hw.sayHello();
info(s);
i = hw.add(18,29);
info(int2str(i));
}

When JobCLR() is run, the result is two lines in the Infolog:

Hello CLR Interop World!


47

Microsoft Official Training Materials for Microsoft Dynamics 5-5


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

InteropPermission Class
Recall from the Writing Secure X++ Code topic in the Development III course,
that classes categorized as "secured" require a permission class to run.

The CLR Interop classes are classified as "secured" APIs. They have a
permission class called InteropPermission. The new method of this class
requires an InteropKind type parameter, which is an enum. When granting
permission to CLR Interop classes, use the InteropKind::CLRInterop value.
The following is an example of CLR Interop code with a permission class:

public server void JobCLRPermissions(Args _args)


{
System.Xml.XmlDocument xmlDoc;
;

new
InteropPermission(InteropKind::CLRInterop).assert();
xmlDoc = new System.Xml.XmlDocument();
//...some xml code here...
xmlDoc.Save('c:\\test.xml');
}

Web service references


The Microsoft Dynamics AX 2009 programmability model now supports
services. This means external Web services can be consumed from X++ code in a
similar way to CLR references. A web reference is created in the AOT, and then
X++ code can refer to the web reference like any other native Microsoft
Dynamics AX 2009 AOT object. This feature is useful for pulling real-time
information from sources published on the web.

To consume an external Web service from X++, a reference to the Web service
must first be created. After creating a reference to the Web service, it can be
invoked from X++ and the available methods can be seen using Intellisense.
Calling and managing external Web services is done entirely within Microsoft
Dynamics AX 2009.

To illustrate how to consume a Web service, the following demonstrations


illustrate how to call the Microsoft Live Search Web service.

5-6 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 5: CLR Interoperability

Demonstration - Create a reference to the Web service


Perform the following steps to create a reference to the Web service:

1. From the Tools menu select Development tools, select Application


Integation Framework, and then select Add service reference.
2. - or -
In the AOT, right-click the References node, and then click Add
service reference. The following screen appears:

FIGURE 5.2 ADD SERVICE REFERENCE DIALOG

3. In the WSDL URL field, enter the location of the service WSDL,
http://soap.search.msn.com/webservices.asmx?wsdl.
This field can contain a path to the local file system, a file share, or
an Internet address. The following are valid formats for the WSDL
URL:
o http://soap.search.msn.com/webservices.asmx?wsdl
o http://localhost/WebServices/SalesTableservice.asmx?WSDL
o c:\WebServices\SalesTableService.wsdl

4. In the .NET code namespace field, enter a unique name for the
.NET namespace such as WindowsLiveSearch. This is the namespace
in which the generated proxy assembly and other files will reside.
5. In the Reference name field, enter a unique name for the Web
service without spaces or special characters such as LiveSearch.
This name is used as the name of the .NET assembly that is created
and the name of the directory that contains the generated Web
reference files.
6. In the Service description field, enter a description for the Web
service.
7. Click OK. The Web reference is generated and the proxy files are
saved to the following directory: <Microsoft Dynamics AX Install
Directory>Application\Appl\DynamicsAx\ServiceReferences\<Web
Service Name>.

Microsoft Official Training Materials for Microsoft Dynamics 5-7


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

Demonstration - Consume a web service from X++


Perform the following steps to consume a Web service from X++

1. In the AOT, navigate to the Classes node, right-click and select New
Class.
2. Right-click the new class and select Properties.
3. Set the Name property to LiveSearchTest.
4. Set the RunOn property to Server.
5. Right-click the class and select New Method. Replace the method
with the following code in the code editor. This adds a main method
that calls the search service with a search string and displays the
results in an Infolog window.

public static void main(Args _args)


{
Container searchResults;
int i;
;

// Call the method to issue the search with the search


criteria.
// This will search for string "Microsoft Dynamics AX
2009".
searchResults = LiveSearchTestClass::
searchLive("Microsoft Dynamics AX 2009");

// Display the search results.


info(strfmt("Total # of hits...%1",
conpeek(searchResults, 1)));
info(conpeek(searchResults, 2));
}

6. Right-click the class and select New Method. Replace the method
with the following code in the code editor. This method creates the
source request and puts it in an array.

public static WindowsLiveSearch.SourceRequest[]


getSourceRequestArray()
{
WindowsLiveSearch.SourceRequest[] array;
WindowsLiveSearch.SourceRequest sourceRequest;
WindowsLiveSearch.SourceType sourceType;

try
{
new
InteropPermission(InteropKind::ClrInterop).assert();

5-8 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 5: CLR Interoperability

// Create the source request and designate the type


// of search.
sourceRequest = new
WindowsLiveSearch.SourceRequest();
sourceType = WindowsLiveSearch.SourceType::Web;

sourceRequest.set_Source(sourceType);

// Add the source request to an array.


array = new WindowsLiveSearch.SourceRequest[1]();
array.SetValue(sourceRequest, 0);
return array;
}
catch(Exception::CLRError)
{
throw error(AifUtil::getClrErrorMessage());
}
}

7. Right-click the class and select New Method. Replace the method
with the following code in the code editor. This is the method that
calls the Live Search Web service and returns the search results in a
container.

public static container searchLive(str searchQuery)


{
#File
WindowsLiveSearch.MSNSearchPortTypeClient
searchService;
ClrObject sourceRequestArray, searchRequestObj;
WindowsLiveSearch.SearchRequest searchRequest;
WindowsLiveSearch.SearchResponse searchResponse;
WindowsLiveSearch.SourceResponse[] sourceResponseArray,
resultsArray;
ArrayIdx idx1, idx2;
int srcResponseLength, resultsLength, totalHits;
WindowsLiveSearch.Result searchResult;
WindowsLiveSearch.SourceResponse sourceResponse;
TextBuffer searchResultBuff;
str title, description, url;
;
try
{
searchResultBuff = new TextBuffer();
new
InteropPermission(InteropKind::ClrInterop).assert();

// Create the search request.


searchRequestObj = new
WindowsLiveSearch.SearchRequest();
// Replace this string with your Application ID.

Microsoft Official Training Materials for Microsoft Dynamics 5-9


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

searchRequestObj.set_AppID("YOUR_LIVESEARCH_APP_ID");
searchRequestObj.set_CultureInfo("en-us");
searchRequestObj.set_Query(searchQuery);

sourceRequestArray =
LiveSearchTestClass::getSourceRequestArray();
searchRequestObj.set_Requests(sourceRequestArray);

//Create the service and call it.


searchRequest = searchRequestObj;
searchService = new
WindowsLiveSearch.MSNSearchPortTypeClient();
searchResponse =
searchService.Search(searchRequest);
sourceResponseArray =
searchResponse.get_Responses();

// Iterate through the search results.


srcResponseLength =
sourceResponseArray.get_Length();
// Iterating through a .NET array so start at zero.
for(idx1=0; idx1 < srcResponseLength ; idx1++)
{
sourceResponse =
sourceResponseArray.GetValue(idx1);

totalHits = ClrInterop::getAnyTypeForObject(
sourceResponse.get_Total());
resultsArray = sourceResponse.get_Results();
resultsLength = resultsArray.get_Length();

// Iterating through .NET array so start at


zero.
for(idx2=0; idx2 < resultsLength ; idx2++)
{
searchResult = resultsArray.GetValue(idx2);
// Gets the title, description, and URL and
appends
// it to the search results.
title = searchResult.get_Title();
searchResultBuff.appendText(title);

searchResultBuff.appendText(#delimiterCRLF);
description =
searchResult.get_Description();
searchResultBuff.appendText(description);

searchResultBuff.appendText(#delimiterCRLF);
url = searchResult.get_Url();
searchResultBuff.appendText(url);

searchResultBuff.appendText(#delimiterCRLF);
searchResultBuff.appendText("--------------
-------

5-10 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 5: CLR Interoperability

---------------------------------------
-----");

searchResultBuff.appendText(#delimiterCRLF);
}
}

CodeAccessPermission::revertAssert();

// Convert the buffer to a string and add it to a


container.
return [totalHits, searchResultBuff.getText()];

}
catch(Exception::CLRError)
{
throw error(AifUtil::getClrErrorMessage());
}
}

You should now have a class named LiveSearchTest with three methods:

getSourceRequestArray
main
SearchLive

8. Compile the class and Run the main method. The search results
should appear in an Infolog window as shown in the following
figure.

FIGURE 5.3 INFOLOG WINDOW WITH SEARCH RESULTS

Microsoft Official Training Materials for Microsoft Dynamics 5-11


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

Implementing a DLL
A Dynamic Link Library (DLL) is a collection of small Windows programs that
can be called upon to complete a particular function. They are often used to find
information about the current environment, such as the amount of free space on a
disk, or which window is currently active.

Each DLL has one or more functions that can be called. It is not always easy to
find all the available functions and how to use them, as documentation or
definition files are often not installed with the DLL.

MSDN describes some of the functions available in a few main Windows DLL's,
such as Kernel32.dll and user32.dll.

One method used to view a list of functions in a DLL is the dumpbin.exe


program. This is shipped with Microsoft Visual Studio .NET. Use the
following line in a command prompt to obtain a list of functions in Kernel32.dll

dumpbin /exports c:\windows\system32\kernel32.dll

The best example of using a DLL within Microsoft Dynamics AX 2009 is the
WinAPI class. This implements useful functions from kernel32.dll, user32.dll,
and others. For example:

{
int fileSize;
;

fileSize = winAPI::FileSize(c:\\myfile.txt);
}

This method uses two other methods to obtain the desired result. Analyze the
getFileSize() method to understand how to implement a specific function.

Declare and instantiate an instance of the class DLL, passing the


name of the DLL as a parameter into the new method.
Declare and instantiate an instance of the class DLLFunction,
passing the DLL object and the name of the function as a parameter
into the new method.
Set the type of the return value.
Set the type of all parameters that function accepts.
Call the function.

5-12 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 5: CLR Interoperability

ChartFX
Another example of using DLLs in Microsoft Dynamics AX 2009 is the use of
ChartFX.dll. This enables a high-quality graphical representation of data on
forms. Follow this example:

The following example uses customer transactions in a graph, where the x-axis is
time, the y-axis is customer balance, and the z-axis is customers. This form is
dynamically linked to the CustTable, so keeping the new form open together with
the customer form, means a new row appears every time you highlight a new
customer. As a result, the new customer's transactions are added to the graph.

Example - Using ChartFX DLL in a form


Perform the following steps to use ChartFX DLL in a form:

1. Create a new form called CustTransGraph.


2. Add CustTable to the data source.
3. Add a new Chart Object type ActiveX control to the design and
change the following properties:
o Name: designGraph
o AutoDeclaration: Yes
o Width: 750
o Height: 375

4. Declare the form variable as follows:

Graphics graphData

5. Override the form's init method and add the following before super():

#MACROLIB.ChartFX
;

Microsoft Official Training Materials for Microsoft Dynamics 5-13


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

6. Add the following after super():

graphData = graphics::newGraphicsTitlesLayout(
designGraph, //ActiveX control
750, //Width
375, //Height
"Customer transaction", //Title
"Date", //Title X-Axis
"Amount", //Title Y-Axis
"Customer", //Title Z-Axis
#Bar | /*#CT_SHOWVALUES |*/ #CT_CLUSTER |
#CT_3D | #CT_TOOL | #CT_SHOWZERO |
#CT_LEGEND, //Type
#CS_ALL, //Style
#CTE_STEPLINES, //ExtType
0); //ExtStyle

7. Override the executeQuery method on the datasource custTable with


the following code:

public void executeQuery()


{
Query query = new Query();
QueryBuildDatasource qbd;
QueryRun queryRun;
CustTrans custTrans;
;
super();

qbd = query.addDataSource(tablenum(CustTrans));
qbd.addRange(fieldnum(CustTrans,
accountNum)).value(SysQuery::value(custTable.AccountNum));

queryRun = new QueryRun(query);

while (queryRun.next())
{
custTrans = queryRun.get(tablenum(custTrans));
graphData.loadData(
date2str(custTrans.TransDate,123,2,2,2,2,2),
custTrans.AccountNum,custTrans.AmountMST);
}

graphData.showGraph();
}

8. Create a display menu item for the form.


9. Add this menu item to the button group on the custTable form.

5-14 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 5: CLR Interoperability

Clicking the button for a customer in the custTable form opens a form similar to
the following (provided some customer transactions exist):

FIGURE 5.4 FORM USING A DLL FILE TO RENDER THE CONTENTS OF AN


ACTIVEX CONTROL

Summary
The Common Runtime Language (CLR) Interoperability (shortened to Interop)
feature enables X++ developers to add CLR assemblies to the AOT and to write
X++ code that interoperates with objects in these assemblies. This lesson
demonstrated how to:

Reference CLR assemblies in Microsoft Dynamics AX 2009 AOT


Add to the Global Assembly Cache
Write X++ code that interacts with managed code in external
applications
Use DLL's within X++ code.

Microsoft Official Training Materials for Microsoft Dynamics 5-15


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

Test Your Knowledge


1. How do you make CLR Interop assemblies visible to Microsoft Dynamics
AX 2009 X++ code?

2. Are CLR Interop assemblies, classes, and methods case-sensitive when


referenced in X++ code?

3. What code do you use, to provide Code Access Security permission to a CLR
Interop class?

5-16 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 5: CLR Interoperability

4. Which node on the AOT are Web service references found?

5. Match the following properties of a Web service reference, to its description.

_____ 1. WSDL a. Identifying text for the Web service.


URL b. The location of the Web service.
_____ 2. .NET c. What is used as the name of the .NET assembly that
code namespace is created and the name of the directory that contains
_____ 3. the generated Web reference files.
Reference name d. Where the generated proxy assembly and other files
_____ 4. Service will reside.
description

6. Which class contains multiple methods, using the kernel32.dll and user32.dll
objects to communicate with the Windows environment?

Microsoft Official Training Materials for Microsoft Dynamics 5-17


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

Lab 5.1 - CLR Interop


Scenario
You are required to use CLR Interop code to send personalized mail messages to
everyone in the company.

Challenge Yourself!

Write a class method that uses CLR Interop code to send a


personalized email to every employee in the company.
The method accepts two parameters - the subject of the email, and
the contents of the email body.
Add a line of code to address the recipient personally with "Dear
<insert name here>" at the start of the email body.
You can assume the email address of the sender is me@abc.com and
that the SMTP server is smtp.abc.com

Need a Little Help?

Use the InteropPermission class and InteropKind::CLRInterop


enum to satisfy Code Access Security.
Use the System.Net.Mail assembly.
Use the System.Net.Mail.MailMessage,
System.Net.Mail.MailAddress and System.Net.Mail.SmtpClient
classes.
Loop through the EmplTable to retrieve all employee names and
email addresses.

5-18 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 5: CLR Interoperability

Lab 5.1 - CLR Interop (Solution)


Scenario
You are required to use CLR Interop code to send personalized mail messages to
everyone in the company.

Write a class method that uses CLR Interop code to send a


personalized email to every employee in the company.
The method accepts two parameters - the subject of the email, and
the contents of the email body.
Add a line of code to address the recipient personally with "Dear
<insert name here>" at the start of the email body.
You can assume the email address of the sender is me@abc.com and
that the SMTP server is smtp.abc.com

Step by Step

Create a new class, and a new method within it.


The code inside should look like this:

void sendPersonalizedEmail(SysEmailSubject _subject,

SysEmailContents _contents)

System.Net.Mail.MailMessage msg;

System.Net.Mail.MailAddress adrRecp, adrSend;

System.Net.Mail.SmtpClient smtpClient;

EmplTable eT;

new InteropPermission(InteropKind::ClrInterop).assert();

smtpClient = new
System.Net.Mail.SmtpClient('smtp.abc.com');

while select Name, Email from emplTable

Microsoft Official Training Materials for Microsoft Dynamics 5-19


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

adrRecp = new
System.Net.Mail.MailAddress(emplTable.Email);

adrSend = new System.Net.Mail.MailAddress('me@abc.com');

msg = new System.Net.Mail.MailMessage(adrSend, adrRecp);

_subject = strfmt('Dear %1, \n', emplTable.Name) +


_subject;

msg.set_Subject(_subject);

msg.set_Body(_contents);

smtpClient.Send(msg);

5-20 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 5: CLR Interoperability

Lab 5.2 - Implementing DLL's


Scenario
Management wants a graphical way to view data.

Challenge Yourself!
Create a new form that shows a graph of the balance of a customer over time.

Need a Little Help?


Follow the ChartFX example to graph the balance of a customer over time.

Microsoft Official Training Materials for Microsoft Dynamics 5-21


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

Lab 5.2 - Implementing DLL's (Solution)


Scenario
Management wants a graphical way to view data.

Create a new form that shows a graph of the balance of a customer over time.

Follow the ChartFX procedure in the chapter for inspiration.

Step by Step

1. Follow the step-by-step example in training manual. However


instead of using transaction amount, use the balance of the customer
on the date of the transaction. Use CustTable.BalancePerDate() to
find the balance.

5-22 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 5: CLR Interoperability

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 5-23


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

Solutions
Test Your Knowledge
1. How do you make CLR Interop assemblies visible to Microsoft Dynamics
AX 2009 X++ code?

MODEL ANSWER: You add references to the assembly files in the AOT,
under the References node.

2. Are CLR Interop assemblies, classes, and methods case-sensitive when


referenced in X++ code?

MODEL ANSWER: Yes. They are case sensitive

3. What code do you use, to provide Code Access Security permission to a CLR
Interop class?

MODEL ANSWER: new InteropPermission(InteropKind)

4. Which node on the AOT are Web service references found?

MODEL ANSWER: The References node.

5. Match the following properties of a Web service reference, to its description.

b 1. WSDL a. Identifying text for the Web service.


URL b. The location of the Web service.
d 2. .NET c. What is used as the name of the .NET assembly that
code namespace is created and the name of the directory that contains
c 3. the generated Web reference files.
Reference name d. Where the generated proxy assembly and other files
a 4. Service will reside.
description

6. Which class contains multiple methods, using the kernel32.dll and user32.dll
objects to communicate with the Windows environment?

MODEL ANSWER: WinAPI

5-24 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 6: Ledger

CHAPTER 6: LEDGER
Objectives
The objectives are:

Create ledger transactions using LedgerVoucher


Create ledger transactions using a Ledger Journal

Introduction
All Microsoft Dynamics AX 2009 modules interface with the General Ledger
module in some way; it is regarded as one of the most important modules. It is
also the module that has the least modifications made to it due to the
commonality in accounting procedures throughout the world.

However, many modifications to other modules or automation of ledger


procedures may require writing code that creates ledger transactions.

There are two methods to consider when posting transactions to the ledger:

1. Use the LedgerVoucher class and sub-classes, which is the more


direct and controllable route.
2. Use a journal which is more straightforward to use.

Since these procedures are used regularly but rarely modified, this lesson
examines how to use both methods, but does not go into details about how these
methods work.

Microsoft Official Training Materials for Microsoft Dynamics 6-1


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

Scenario
Isaac, the Systems Developer, is developing a process to import opening ledger
balances for a new company that is to be migrated to Microsoft Dynamics
AX2009. He has been asked to evaluate the best method to achieve this and to
write a program that can be used during the data migration.

LedgerVoucher
When using LedgerVoucher, remember that all vouchers in Microsoft Dynamics
AX 2009 must balance - an equal credit and debit side. All transactions on one
voucher must be posted on the same date. The idea is to build up a list of
vouchers to be posted and to build up a list of transactions within each voucher.
Once these lists are completed, the system posts them all to the General Ledger
module at once.

A structure is built as follows:

Posting (LedgerVoucher)

Voucher (LedgerVoucherObject)
o Trans (LedgerVoucherTransObject)
o Trans

Voucher
o Trans
o Trans

LedgerVoucher harnesses the posting of multiple vouchers at one time. It checks


that the posting is correct and that the voucher balances. Possible rounding of
postings is carried out if necessary.

The LedgerVoucher class holds all the vouchers in temporary storage (a list
array) until the End method is called. The End method creates ledger transaction
records from the temporary postings.

Each voucher in the LedgerVoucher is contained in a LedgerVoucherObject.


Each LedgerVoucherObject holds transactions in temporary storage (a list array)
until the end() method is called.

Each transaction in each voucher is contained in a LedgerVoucherTransObject.


The procedure is:

1. Instantiate the LedgerVoucher class.

6-2 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 6: Ledger

2. Loop over the vouchers to instantiate LedgerVoucherObject classes,


and register these in the LedgerVoucher class.
3. For each voucher, loop over the transactions to instantiate
LedgerVoucherTransObject classes, and register these in the
LedgerVoucher class.

A detailed description of these steps follows.

Instantiation of LedgerVoucher
LedgerVoucher can be instantiated as follows:

LedgerVoucher::newLedgerPost(_detailSummary, _sysModule,

_voucherSeriesCode,

[_transactionLogType,

_transactionLogText,

_approveJournal,

_posting]);

The method constructs one of the sub-classes, LedgerVoucherPost,


LedgerVoucherPostApprove, LedgerVoucherPostExPosting.

LedgerVoucherPost is used for normal posting to the general ledger.

LedgerVoucherPostApprove is used when the final posting is approved by the


user; for example, if a vendor invoice approval journal needs to bypass some
checks, for instance, when the voucher has already been used.

LedgerVoucherPostExPosting is used in the production module to reduce the


number of transactions created during production order updates.

The parameters are:

_detailSummary Value specifying either summary or details.


Specifying summary results in summarizing the amounts per
account, date, currency, and voucher.
_sysModule System module from which transactions are made.
This value can also be used for posting transactions from custom
modules.
_voucherSeriesCode Number sequence code used for numbering
the vouchers. This does not automatically allocate the voucher
number - it stamps the ledger transaction with the code that was
used.
_transactionLogType Used in the audit trail (optional).

Microsoft Official Training Materials for Microsoft Dynamics 6-3


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

_transactionLogText Used in the audit trail (optional).


_approveJournal True returns LedgerVoucherPostApprove
(optional).
_posting False returns LedgerVoucherPostExPosting (optional).

Instantiation of LedgerVoucherObject
LedgerVoucherObject can be instantiated as follows:

LedgerVoucherObject::newVoucher(_voucher,

[_transDate,

_sysModule,

_ledgerTransType,

_correction,

_operationsTax,

_documentNum,

_documentDate,

_tmpVoucherMap,

_acknowledgementDate]);

The parameters are:

_voucher Number of the voucher.


_transDate Date of transaction. The default value is the current
date. (Optional)
_sysModule System module from which transactions are made.
This value can also be used for posting transactions from other
modules. (Optional)
_LedgerTransType Ledger transaction type. To view all available
ledger transaction types, refer to base enum LedgerTransType.
Default value LedgerTransType:None. (Optional)
_correction Indicates whether the voucher is a reversing entry
(credit note). (Optional)
_operationsTax Indicates the type of operation tax used when
posting a transaction. The default value is OperationsTax::Current.
For all available OperationTax codes, look in the base enum
OperationsTax details. (Optional)
_documentNum Use to stamp a document number onto a
transaction. (Optional)

6-4 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 6: Ledger

_documentDate Use to stamp a document date onto a transaction.


(Optional)
_tmpVoucherMap Use during posting when it is required that all
voucher numbers are continuous (Optional)
_acknowledgementDate Date when the company gains knowledge
of the transaction. (Optional Italy country specific functionality)

Registering the LedgerVoucherObject with the


LedgerVoucher
A LedgerVoucherObject can be added to LedgerVoucher as follows:

ledgerVoucher.AddVoucher(ledgerVoucherObject);

Instantiation of LedgerVoucherTransObject
There are many other constructor methods for this class used in different
situations. The newCreateTrans() method is most commonly used.

LedgerVoucherTransObject::newCreateTrans(

_ledgerVoucherObject,
_ledgerPostingType,

_ledgerAccount,

_dimension,

_currencyCode,

_amountCur,

_sourceRecId,

[_LedgerQty,

_exchRate

_exchRateSecond,

_ExchRatesTriangulation,

_markBridging,

_projLedger,

_amountMST])

Microsoft Official Training Materials for Microsoft Dynamics 6-5


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

_ledgerVoucherObject The object containing the corresponding


voucher.
_ledgerPostingType A type of posting. For all available posting
types, look in base enum LedgerPostingType details.
_ledgerAccount The ledger account number on which transactions
will be made.
_dimension The transaction dimension.
_currencyCode Currency in which transactions are made.
_amountCur The amount in currency (this is converted to MST in
the newCreateTrans method).
_SourceRecid The record ID of the originating record (for
example, SalesLine).
_LedgerQty The quantity, if any, attached to the transaction.
(Optional)
_exchRate Use to specify an exchange rate. If blank, the exchange
rate in the system for the transaction date is used. (Optional)
_exchRateSecond Use to specify an exchange rate for the
secondary currency (if used). If blank, the exchange rate in the
system for the transaction date is used. (Optional)
exchRatesTriangulation Can override currency's setting for Euro
Triangulation. (Optional)
_markBridging No longer used. This was used in a journal to mark
a transaction posted to a temporary bridging account. (Optional)
_projLedger Use in the project module to create project-specific
transactions from the ledger transactions. (Optional)
_amountMST If not specified, currency calculation will be made.
If calculation has already been made, then specifying the
amountMST (monetary standard amount) increases performance.

Providing the Transaction Text


Add transaction text to a transaction by using the following code.

ledgerVoucherTransObject.parmTransTxt(_transTxt);

_transTxt - Transaction text.

Registering the LedgerVoucherTransObject with the


LedgerVoucher
ledgerVoucher.addTrans(ledgerVoucherTransObject);

This adds the transaction initiated in the newCreateTrans() method to the


posting. It has already been added to the LedgerVoucherObject.

6-6 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 6: Ledger

Finalizing the Posting


When all the vouchers and transactions have been added, the whole posting can
be committed.

LedgerVoucher.end();

Final checks are made and the transactions are finalized. Also,
LedgerBalancesTrans and LedgerBalancesDimTrans are updated. These tables
store balances by period and by dimension.

A coding example using the ledgerVoucher class is provided in the application as


the TutorialLedgerVoucher class. The runSalesPost method exemplifies posting
a sales order with its lines. The runJournalPost method exemplifies posting from
a journal.

Example: Using LedgerVoucher


The following job posts a petty cash disbursement for a payment for a stapler and
some stamps. The accounts and amounts are set in the variable declaration.
Usually these would be obtained from parameters, user input or a calculation.

static void ExampleLedgerVoucher(Args _args)


{
LedgerVoucher ledgerVoucher;
LedgerVoucherTransObject ledgerVoucherTransObject;

Dimension dimension;
NumberSeq numSeq;
NumberSequenceCode NumberSequenceCode =
'Acco_18';
ledgerAccount accountNumPetty = '110180';
// Petty cash
ledgerAccount accountNumOffsetOffice =
'606300'; // Office Supplies
ledgerAccount accountNumOffsetPostage =
'606500'; // Postage
amountMST amountPetty = 55;
amountMST amountOffice = 50;
amountMST amountPostage = 5;

numSeq =
NumberSeq::newGetNumFromCode(NumberSequenceCode);

ttsbegin;

//First Step - Create Voucher


ledgerVoucher =
ledgerVoucher::newLedgerPost(DetailSummary::Summary,

Microsoft Official Training Materials for Microsoft Dynamics 6-7


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

SysModule::Ledger,

LedgerParameters::numRefLedgerExchAdjVoucher().NumberSequen
ce) ;

//Second Step - Create Voucher Number

ledgerVoucher.AddVoucher(LedgerVoucherObject::newVoucher(nu
mseq.num(),

today(),

Sysmodule::Ledger,

LedgerTransType::None));

//Create the first Transaction - this is the credit


side of the transaction (-55)
//The other two DR transactions should total to the CR
otherwise it won't post.
ledgerVoucherTransObject =
LedgerVoucherTransObject::newCreateTrans(

ledgerVoucher.findLedgerVoucherObject(),

LedgerPostingType::LedgerJournal,

accountNumPetty, // Ledger Account


dimension,

CompanyInfo::standardCurrency(),
-
amountPetty, // Amount
0, //
TableId
0); //
ReciID

ledgerVoucherTransObject.parmTransTxt("Petty cash
disbursement");
ledgerVoucher.addTrans(ledgerVoucherTransObject);

//Create the second Transaction


ledgerVoucherTransObject =
LedgerVoucherTransObject::newCreateTrans(

ledgerVoucher.findLedgerVoucherObject(),

LedgerPostingType::LedgerJournal,

accountNumOffsetOffice,
dimension,

6-8 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 6: Ledger

CompanyInfo::standardCurrency(),

amountOffice, // Amount
0,
// TableId
0);
// ReciID
ledgerVoucherTransObject.parmTransTxt("Red stapler");
ledgerVoucher.addTrans(ledgerVoucherTransObject);

//Create third Transaction


ledgerVoucherTransObject =
LedgerVoucherTransObject::newCreateTrans(

ledgerVoucher.findLedgerVoucherObject(),

LedgerPostingType::LedgerJournal,

accountNumOffsetPostage,

dimension,

CompanyInfo::standardCurrency(),

amountPostage,

0,
// TableId
0);
// ReciID

ledgerVoucherTransObject.parmTransTxt("Stamps");
ledgerVoucher.addTrans(ledgerVoucherTransObject);

//Last Step - To Balance Voucher and Close


ledgerVoucher.end();
numseq.used();

ttsCommit;
}

Microsoft Official Training Materials for Microsoft Dynamics 6-9


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

LedgerJournal
Journals are the easiest way of posting in Microsoft Dynamics AX2009. Using
journals requires less work when grouping the transactions and vouchers
appropriately. When using a Journal lines table, fill it in with data and call upon
Microsoft Dynamics AX2009's method for posting the journal. All error
checking and posting are performed simultaneously without additional
programming. Journals also present the possibility of not posting immediately
but allowing the user to verify what is to be posted. This is often useful during
data migration.

Ledger Journal structure


The three main journal groups in Microsoft Dynamics AX2009 are Ledger,
Inventory, and Project. The journal framework consists of a number of tables,
maps, classes, and forms designed so that all journals behave similarly and have
the same look and feel across modules.

Within these groups, there are many types that use the same tables, but differ
slightly in what information they require and the end result.

In the ledger module, the tables used are LedgerJournalName,


LedgerJournalTable, and LedgerJournalTrans.

LedgerJournalName contains default data for a specific journal type. There can
be more than one LedgerJournalName per type.

LedgerJournalTable is the header record for the journal. There is one record per
journal.

LedgerJournalTrans contains data used to create the individual transactions that


will be posted. There can be one or more lines per journal.

The steps used to create a journal are:

1. Create a journal table record or use an existing one.


2. Create lines for each transaction to be posted. This could be one line
per pair of balanced transactions, or one line per transaction.
3. Post the journal. This checks the journal before posting. This step is
left out if the journal is to be checked by the user before posting.

A more detailed description of the steps involved follows.

6-10 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 6: Ledger

Creating the Journal


To create a new journal, use a journal name record to get the default data. Use a
journal name of an appropriate type.

ledgerJournalTable.JournalName =

SalesParameters::find().PayJournal;

Where SalesParameters::find().PayJournal is a new field of type


LedgerJournalNameId. Look at Extended Data Type (EDT)
LedgerJournalNameIdApproval for an example of how to create an EDT for a
journal name of a specific type.

ledgerJournalTable.Name = 'Payment Journal'


ledgerJournalTable.insert();

Look in the table method ledgerJournalTable.Insert(). Notice it handles the


creation of the journal number, which is the primary key defining the relationship
between the journal table and the lines, and the initializing from
LedgerJournalName.

Creating Lines
All transactions must be posted in the journal lines table. Fill the record with data
and then insert it. Two fields of interest when creating journal lines are:
LedgerJournalTrans.JournalNum which is mandatory, because transactions are
assigned to one of the journals, and LedgerJournalTrans.Voucher which is the
transaction voucher number.

Fill in other mandatory fields in the journal lines table as necessary, for example,
LedgerJournalTrans.CurrencyCode.

Each journal type requires different fields and field combinations to be correctly
entered. Any errors that show up while posting are displayed by validating a
journal through the journal lines form.

Posting Journals
After transactions are created in the journal, check and post the journal; use the
LedgerJournalCheckPost class. The easiest way is to create an instance of this
class using newLedgerJournalTable static method and then run the class.

Parameters for this method are:

_ledgerJournalTable Record in LedgerJournalTable representing


the journal which should be posted.

Microsoft Official Training Materials for Microsoft Dynamics 6-11


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

_post NoYes parameter for specifying if it checks only a journal or


completes both checking and posting. Yes indicates that after
checking, posting is done.
_transferErrors NoYes parameter for transferring vouchers with
errors to another journal. Yes indicates that all vouchers with errors
are transferred into a new journal with the same name and
description, but with different batch numbers. The default value is
No.

Example: Using Ledger Journal


The following is an example of creating a journal with one journal line and then
posting the journal.

static void ExampleLedgerJournal(Args _args)


{
LedgerJournalName LedgerJournalName;
LedgerJournalTable ledgerJournalTable;
LedgerJournalTrans ledgerJournalTrans;
LedgerJournalCheckPost ledgerJournalCheckPost;
NumberSeq numberseq;

LedgerJournalNameId LedgerJournalNameId = 'GenJrn';


BankAccountID BankAccountID = 'EUR OPER';
ledgerAccount offsetAccount = '601500';
amountCur amountCur = 102;
;

ttsbegin;

// Find a ledgerJournalName record


select firstonly LedgerJournalName
where LedgerJournalName.JournalName ==
LedgerJournalNameId;

//Created the ledgerJournalTable


ledgerJournalTable.JournalName =
LedgerJournalName.JournalName;
ledgerJournalTable.initFromLedgerJournalName();
ledgerJournalTable.Name = 'Hotel';
ledgerJournalTable.insert();

numberseq =
NumberSeq::newGetVoucherFromCode(ledgerJournalName.VoucherS
eries);
ledgerJournalTrans.Voucher = numberseq.voucher();

//Generate the transaction line


ledgerJournalTrans.JournalNum =
ledgerJournalTable.JournalNum;
ledgerJournalTrans.CurrencyCode = 'EUR';
ledgerJournalTrans.ExchRate =
Currency::exchRate(ledgerJournalTrans.CurrencyCode);

6-12 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 6: Ledger

ledgerJournalTrans.AccountNum = BankAccountID;
ledgerJournalTrans.AccountType =
LedgerJournalACType::Bank;
ledgerJournalTrans.AmountCurCredit = amountCur;
ledgerJournalTrans.TransDate = today();
ledgerJournalTrans.Txt = 'Room Stay';
ledgerJournalTrans.OffsetAccount = offsetAccount;
ledgerJournalTrans.OffsetAccountType =
LedgerJournalACType::Ledger;
ledgerJournalTrans.insert();

info(strfmt('Journal Id:
%1',ledgerJournalTable.JournalNum));

//Post the Journal


ledgerJournalCheckPost =
ledgerJournalCheckPost::newLedgerJournalTable(ledgerJournal
Table,NoYes::Yes);
ledgerJournalCheckPost.run();

ttscommit;
}

Summary
This lesson introduces and explains two methods for posting transactions to the
ledger table. The two methods are:

Using the LedgerVoucher class and sub-classes.


Using a journal.

This lesson provides a basic understanding of:

The concepts behind the two main methods of posting transactions.


The best method to use depending on the situation.

Microsoft Official Training Materials for Microsoft Dynamics 6-13


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

Test Your Knowledge


1. Which class holds a list of transactions that are going to be posted for a
particular voucher?

2. The transaction log type and transaction log text are used in the ________
trail.

3. Which table stores default values that initialize a LedgerJournalTable


record?

6-14 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 6: Ledger

4. Which class posts a journal?

Microsoft Official Training Materials for Microsoft Dynamics 6-15


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

Lab 6.1 Create and Post a Ledger Journal


Scenario
During data migration when implementing Microsoft Dynamics AX2009 for a
new customer, you need to enter opening balances for the general ledger. Write a
program that imports these opening balances into a journal and optionally posts it
automatically. One voucher can be used for all transactions, but remember to
ensure that it balances.

Challenge Yourself!

1. Prompt for offset account, posting date, and filename.


2. Create a journal header.
3. Read in the file and create a journal line for each account and
balance.
4. Create a line for the offset account entered by the user for the inverse
of the total amount of the balances imported.
5. Post the journal.

Step by Step

1. Create a new class that extends RunBaseBatch.


2. Prompt for:
o offset account
o posting date
o filename

3. Create a method to generate the journal header.


o Find the first journal name of type = daily.
o Set the journal name of the JournalTable record = journal name
of JournalName record.
o Insert the JournalTable record.

4. Set a class variable to store the voucher number. The voucher


number is obtained from the number sequence attached to the journal
name record.

6-16 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 6: Ledger

5. Create a method to generate the journal lines.


o Accept the account number and amount as parameters.
o Set the voucher number.
o Set the account number, the amount, the date, and the
accounttype.
o Read the file.
o For each line, call the method to create the journal line.
o Create a line for the negative of the total amount of the
transactions for the offset account specified in the prompt.

6. Call LedgerJournalCheckPost (use static method


newLedgerJournalTable), to post the journal.

Microsoft Official Training Materials for Microsoft Dynamics 6-17


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

Lab 6.2 POS - End-of-day routine


Scenario
This is a part of the Point-of-Sale Project.

Payments are entered into the register by clicking Payment on the POS form
which opens the POSPayment form. In a live application this would also open the
cash drawer. In the next lesson you post the payments to the holding account and
the AR sub-ledger.

At any cash register, an End of Day routine counts the actual cash taken. This is
posted to the ledger, and any difference between the counted amount and the
recorded amount is posted to a specified ledger account. Write this routine.

Challenge Yourself!

1. Prompt for the RegisterId of the register to process.


2. Prompt for the counted cash amount.
3. Prompt for the cash or bank account and the difference account.
4. Total up the amount entered during that day for that register.
5. Calculate the difference between the counted amount and the posted
amount.
6. Post the counted amount to the bank account.
7. Post the inverse of the posted amount to the register holding account.
8. Post the difference to the register difference account.
9. Flag the payment lines as having been posted at EOD.

Step by Step

1. Create a new class that extends RunBaseBatch.


a. Prompt for:
o Register Id
o Counted amount

b. Create a method to create the journal header.


c. Find the first journal name of type = daily.
d. Set the journal name of the JournalTable record = journal name
of JournalName record.
e. Insert the JournalTable record.

2. Set a class variable to store the voucher number. The voucher


number is obtained from the number sequence attached to the journal
name record.

6-18 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 6: Ledger

3. Create a method to create the journal lines.


a. Accept the account number and amount as parameters.
b. Set the voucher number.
c. Set the account number, the amount, the date, and the
accounttype.
d. Find the Register table record for the specified register.
e. Create a journal line for the inverse of the total amount posted
for that register for the register holding account.
f. Create a journal line for the total counted amount for the bank
account.
g. Create a journal line for the difference between the posted
amount and the counted amount for the difference account
specified in a parameter.

4. Call LedgerJournalCheckPost (use static method


newLedgerJournalTable), to post the journal.
5. Update the register transactions to reflect that they have been posted.

Microsoft Official Training Materials for Microsoft Dynamics 6-19


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

Quick Interaction: Lessons Learned


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

1.

2.

3.

6-20 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 6: Ledger

Solutions
Test Your Knowledge
1. Which class holds a list of transactions that are going to be posted for a
particular voucher?

MODEL ANSWER: LedgerVoucherObject

2. The transaction log type and transaction log text are used in the ________
trail.

MODEL ANSWER: The transaction log type and transaction log text are
used in the audit trail.

3. Which table stores default values that initialize a LedgerJournalTable


record?

MODEL ANSWER: LedgerJournalName

4. Which class posts a journal?

MODEL ANSWER: LedgerJournalCheckPost

Microsoft Official Training Materials for Microsoft Dynamics 6-21


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

6-22 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 7: Trade

CHAPTER 7: TRADE
Objectives
The objectives are:

Use SalesTableType, SalesLineType, PurchTableType, and


PurchLineType
Post and Print Document Updates
Post Transactions
Use Settlements
Use Trade Agreements

Introduction
The most frequently modified modules in Microsoft Dynamics AX 2009 are the
Accounts Receivable (AR) and Accounts Payable (AP) modules. For instance,
the element that every company modifies is the printed sales invoice. This is
sometimes a rearrangement of the data available to the invoice, but is more often
a complete redesign that includes additional, company-specific data.

The trade modules revolve around buying and selling inventory, and issuing and
receiving payments. The AR and AP modules are similar in their structure
regarding both data dictionary and update procedures, and make good use of
table maps to reduce the amount of coding needed.

In this chapter, where only AR is referenced, similar constructs and procedures


also apply for AP. Recognize that what is true for SalesLine is also true for
PurchLine. Not everything is the same between the two modules, but the basic
ideas are similar.

Microsoft Official Training Materials for Microsoft Dynamics 7-1


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

TableType and LineType


SalesTable, SalesLine, PurchTable, and PurchLine make use of the InventType
construct to control:

What values are acceptable


What happens when any field values are modified
How the different order types, and line types are posted
Where postings to ledger are made

FIGURE 7.1 SALES TABLE TYPE

Demonstration - SalesLineType

1. View the SalesLine.validateWrite() method:

ok = super();

ok = ok &&
this.validateWrite_server(_skipCreditLimitCheck);

return ok;

After calling the system validation using super(), SalesLineType is instantiated


using the SalesLine.Type() method. The SalesLine.Type() method calls
SalesLineType.contrstruct() which constructs the instance according to the value
of SaleLine.SalesType.

After SalesLineType has been instantiated, SalesLineType.ValidateWrite() is


called.

For most sales line types, SalesLineType.ValidateWrite() has not been


overridden, so SalesLineType.ValidateWrite() is called. For a line type of
ReturnItem, the method has been overridden.

7-2 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 7: Trade

2. View the SalesLineType_ReturnItem.ValidateWrite() method:

ok = super(_skipCreditLimitCheck);

if (salesLine.SalesQty > 0
&& salesLine.SkipUpdate == InterCompanySkipUpdate::No)
{
// Quantity of returned items orders must be
negative.
ok = checkFailed("@SYS53512");
}

if (ok && salesLine.ExpectedRetQty == 0 &&


salesLine.returnJournal())
{
// Expected return quantity must not be zero
ok = checkFailed("@SYS105646");
}

if (ok && salesLine.returnJournal() &&


salesLine.ExpectedRetQty * salesLine.LineAmount < 0)
{
//The sign on Quantity and Net amount have to be
the same
ok = checkFailed("@SYS78848");
}
return ok;

After calling super() to provide validation for other line types, the method has
one additional check to ensure that the quantity of the return line is negative.

These inventType sub-class methods are often called from the system methods on
salesLine and salesTable. For example, SalesLine.Update(),calls
SalesLineType.Update(). This can be used when, for instance, creating sales
orders within code, meaning that instantiating SalesTableType or SalesLineType
is not necessary.

The examples below show how a sales order and sales order line are created
within code. The three table methods that are used: SalesTable.InitValue(),
SalesTable.insert(), and SalesLine.CreateLine(), show that they all use the
inventType sub-classes. Note that SalesLine.CreateLine() calls SalesLine.Insert()
which calls SalesLineType.Insert().

Microsoft Official Training Materials for Microsoft Dynamics 7-3


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

Example: Create a Sales Order


static void createSalesTable(CustAccount _custAccount)
{
SalesTable salesTable;
NumberSeq NumberSeq;
;

NumberSeq =
NumberSeq::newGetNumFromCode(SalesParameters::numRefSalesId
().numberSequence);
salesTable.SalesId = NumberSeq.num();
salesTable.initValue();
salesTable.CustAccount = _custAccount;
salesTable.initFromCustTable();
salesTable.insert();
}

Example: Create a Sales Line


static void createSalesLine(SalesId _salesId, ItemId
_itemId)
{
SalesLine salesLine;
;

salesLine.clear();
salesLine.SalesId = _salesId;
salesLine.ItemId = _itemId;
salesLine.createLine(NoYes::Yes, // Validate
NoYes::Yes, // initFromSalesTable
NoYes::Yes, // initFromInventTable
NoYes::Yes, // calcInventQty
NoYes::Yes, // searchMarkup
NoYes::Yes); // searchPrice
}

7-4 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 7: Trade

Post and Print Document Updates


Sales and purchase orders go through different stages or sales statuses. For
instance, a sales order can be Open Order, Delivered, or Invoiced. The status
displayed on the sales order is the lowest status, based on the enum SalesStatus,
of the attached lines. For example, if all lines are delivered, then the status of the
order is delivered, but if one of the lines is still open, then the order is also open.
The table method SalesLine.lowestSalesStatus() shows where this is set. The
SalesStatus field shows the last sales status posted for the order. Each update
involves different postings. For instance, a delivery note update posts the
inventory movement.

Although there are significant differences between what is posted, the procedures
to perform the update are similar, and therefore use a sub-class structure, based
on a class called formLetter (which extends RunBaseBatch).

FIGURE 7.2 SALESFORMLETTER

Orders can be fully or partially updated, and can be updated one order at a time
or in a batch of orders. To handle this parm tables are used. For instance, Sales
order updates, use SalesParmUpdate, SalesParmTable, and SalesParmLine, with
one record in SalesParmUpdate per update, one record in SalesParmTable per
sales order, and one record in SalesParmLine per line to be updated.

When a user updates an order, the system populates these parm tables, and then
displays them for final adjustments before the posting takes place. They are
populated when the SalesFormLetter.dialog method is called - the
SalesFormLetter.ChooseLines() method uses the SalesUpdate query to select
appropriate salesLines records and populates SalesParmLine accordingly.

Once the parm tables are populated, they are displayed using the SalesEditLines
form. When the user makes alterations and clicks OK, the update occurs.

Microsoft Official Training Materials for Microsoft Dynamics 7-5


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

Demonstration - SalesFormLetter

1. View the SalesFormLetter.run() method:

query = this.queryBuild();

if (printout == Printout::After && ! this.proforma())


{
journalList = this.newJournalList();
}

setprefix("@SYS25781");

while (query.next())
{

SalesFormLetter.queryBuild() builds a query based on SalesParmUpdate and


SalesParmTable.

journalList is a list of documents to be printed later.

2. The query is then executed:

infoLogCounter = infolog.num();

infolog.updateViewSet(this);

if (printout == Printout::Current ||
this.proforma())
{
journalList = this.newJournalList();
}

salesParmTable =
query.get(tablenum(SalesParmTable));

if (salesParmTable.SalesId != salesTable.SalesId)
{
salesTable = salesParmTable.salesTable();
}

if (this.checkIfSalesOrderExist(salesTable))
{
try
{
if (batchHeader)
{
formLetterMultiThread =
FormLetterMultiThread::newFormLetter(this);

batchHeader.addRuntimeTask(formLetterMultiThread,this.parmC
urrentBatch().RecId);

7-6 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 7: Trade

batchHeader.addDependency(salesFormLetterEndMultiThread,for
mLetterMultiThread,BatchDependencyStatus::FinishedOrError);
}
else
{
this.createJournal();
}
}

SalesParmTable is retrieved from the query, and a check is made on the sales
order. SalesFormLetter.createJournal() is then called.

3. View the SalesFormLetter.createJournal() method:

salesTotals = SalesTotals::construct(
salesParmTable,
salesParmUpdate.SpecQty,
salesParmUpdate.SumBy,
salesParmUpdate.ParmId,
salesParmUpdate.SumSalesId,
this.documentStatus());
this.initFromSalesTotals(salesTotals);

4. Order totals and tax totals are calculated:

if (salesParmUpdate.Proforma)
this.insertProforma();
else
this.insertJournal();

If the order is to be posted (that is, not pro-forma),


SalesFormLetter.insertJournal() is called:

numberSeq = this.allocateNumAndVoucher();
[number, voucher] = this.getNumAndVoucher();

if (this.updateNow())
{
this.postUpdate();

Posting number (e.g. invoice or delivery note) and voucher are created then
SalesFormLetter.UpdateNow() is called.

The UpdateNow() method is not implemented in SalesFomrLetter, rather in its


sub-classes. The different sub-classes UpdateNow()use a list of SalesParmLine
records (created when the order totals were calculated).

Microsoft Official Training Materials for Microsoft Dynamics 7-7


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

5. View the SalesFormLetter_PackingSlip.UpdateNow() method:

this.initJournal();
transactionTxt = this.initTransactionTxt();
this.initLedgerVoucher(transactionTxt);
this.initRecordListInventReportDimHistory();

salesLine.clear();
salesParmLine.clear();

recordListSalesParmLine.first(salesParmLine);
while (salesParmLine)
{
if (! this.checkDiscardLine())
{

CustPackingSlipJour is initialized using values from the update and from


SalesTable.

6. Transaction text is initialized.

The ledgerVoucher and ledgerVoucherObject classes are instantiated, which may


be used if posting physical movement to ledger.

7. The first SalesParmLine from the list is retrieved:

inventMovement =
InventMovement::construct(salesLine);

this.updateInventory(inventMovement);

InventMovement is instantiated using SalesLine.

The physical update to inventory is slightly different depending on which update


is made, so the updateInventory() method is overridden in
SalesFormLetter_PackingSlip.

8. View the SalesFormLetter_PackingSlip.updateInventory () method:

inventUpd_Physical =
InventUpd_Physical::newSalesPackingSlip(
_inventMovement,
salesParmLine,
number,
salesParmUpdate.ReduceOnHand);

inventUpd_Physical.updateNow(ledgerVoucher);

updateNow = -
inventUpd_Physical.updPhysicalUnit();
updateNowInvent = -inventUpd_Physical.updPhysical();

7-8 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 7: Trade

InventUpd_Physical posts the physical movement of the inventory, and is then


used to return the actual quantity updated. The quantity may be reduced due to
insufficient inventory.

9. Return to SalesFormLetter_PackingSlip.UpdateNow():

if (salesParmLine.DeliverNow != updateNow ||
salesParmLine.InventNow != updateNowInvent)
{
salesParmLine =
SalesParmLine::findRecId(salesParmLine.RecId, true);

info(strfmt("@SYS26397",updateNow));
qtyReduced = true;

salesParmLine.RemainAfter +=
salesParmLine.DeliverNow - updateNow;
salesParmLine.setRemainAfterInvent();

salesParmLine.DeliverNow = updateNow;
salesParmLine.InventNow =
updateNowInvent;
salesParmLine.setLineAmount(salesLine);

salesParmLine.update();
}

10. If the quantity is reduced, the user is informed and the parm table
record is updated to ensure that later updates are correct.

this.writeJournalLine();
this.writeProjTrans(inventMovement);

11. SalesFormLetter_PackingSlip.writeJournalLine() finalizes


CustPackingSlipTrans and commits it to the database:

salesLineType =
SalesLineType::construct(salesLine);

salesLineType.updateSalesLine(inventMovement.transIdSum());
...
salesLine.doUpdate();

Microsoft Official Training Materials for Microsoft Dynamics 7-9


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

12. The status of the SalesLine record is updated (which may also update
the SalesTable record):

if (qtyReduced)
{
salesTotals = SalesTotals::construct(salesTable,
salesParmUpdate.SpecQty, salesParmUpdate.SumBy,
salesParmUpdate.ParmId, salesParmUpdate.SumSalesId,
this.documentStatus());
salesTotals.calc();
this.tax(salesTotals.tax());
}

13. If the quantity is reduced, the totals are recalculated and the tax is
adjusted:

this.writeJournal();
this.writeRecordListInventReportDimHistory();
this.postMarkupTable();
this.updateSalesShippingStat(salesParmTable);

SalesFormLetter_PackingSlip.WriteJournal() finalizes CustPackingSlipJour and


commits it to the database.

14. Return to the SalesFormLetter.run() method:

if (printout == Printout::After && !


salesParmUpdate.Proforma)
this.printJournal();

If the document is to be printed, it is called using printJournal(). The sub-classes


override this method so that the specific report can be called.

15. View the SalesFormLetter_PackingSlip.PrintJournal() method:

if (journalList.len() > 0)
{
if (printFormletter)
{
this.sendAsXML();
custPckSlpJour.printJournal(this, journalList);
}

CustPackingSlipJour.printJournal() contains code that calls the report.

The following is an example of using SalesFormLetter. In this case, the sales


order has been created and will be invoiced.

7-10 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 7: Trade

Example: Posting an Invoice


static void createInvoice(SalesTable _salesTable)
{
SalesFormLetter salesFormLetter;
;

salesFormLetter =
SalesFormLetter::construct(DocumentStatus::Invoice);
salesFormLetter.update(
_salesTable, //SalesTable record to be posted
systemDateGet(), //Transaction date
SalesUpdate::All, //Which qty should be used
AccountOrder::None,
NoYes::No, //Is document a proforma
NoYes::No); //Should document be printed

Posting Transactions
Customer and vendor transactions can be posted using a ledger journal. This can
be manually created or created within code, as with a normal daily journal.

Similar to ledger transactions, customer and vendor transactions can be posted by


using a CustVendVoucher object. This creates the transaction in the sub-ledger
and the AR or AP account. You must still create transactions in another account
to balance the posting.

SalesFormLetter_Invoice.createCustTrans() shows where the invoice posting


creates the customer transaction. The sales revenue transaction is created
elsewhere in the update, but still uses the same LedgerVoucher object.

The following example illustrates how a customer transaction could be posted.


The process is similar to posting ledger transactions using the ledgerVoucher, but
here, one of the LedgerVoucherTransObject instances is replaced with a
CustVendVoucher instance which handles posting to both the AR sub-ledger and
the AR ledger account.

Example: Posting a Customer Transaction


static void createTransActions(
NumberSequenceCode _numberSequenceCode,
CustAccount _custAccount,
AmountMst _amountMST,
LedgerAccount _ledgerAccount)
{
CustTrans custTrans;
CustVendVoucher custVoucher;
LedgerVoucher ledgerVoucher;
LedgerVoucherObject ledgerVoucherObject;
LedgerVoucherTransObject ledgerVoucherTransObject;
NumberSeq numberSeq;
;

Microsoft Official Training Materials for Microsoft Dynamics 7-11


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

ttsbegin;
numberSeq = NumberSeq::newGetVoucherFromCode(

NumberSequenceTable::find(_numberSequenceCode).NumberSequen
ce);

ledgerVoucher = LedgerVoucher::newLedgerPost(
DetailSummary::Detail,
SysModule::Cust,

NumberSequenceTable::find(_numberSequenceCode).NumberSequen
ce);

ledgerVoucherObject = LedgerVoucherObject::newVoucher(
numberSeq.voucher(),
SystemDateGet(),
SysModule::Cust,
LedgerTransType::Payment);

ledgerVoucher.AddVoucher(ledgerVoucherObject);
custVoucher = CustVendVoucher::construct(
SysModule::Cust,
ledgerVoucher,
_custAccount,
_amountMST,
CustTable::find(_custAccount).Currency,
LedgerTransTxt::CustPaymentCust,
CustTable::find(_custAccount).Dimension,
'',
LedgerPostingType::CustPayment);

custVoucher.parmTransTxt('Customer Transaction');
ledgerVoucherTransObject =
LedgerVoucherTransObject::newCreateTrans(
ledgerVoucherObject,
LedgerPostingType::CustPayment,
_ledgerAccount,
CustTable::find(_custAccount).Dimension,
CustTable::find(_custAccount).Currency,
-_amountMST,
0);

ledgerVoucherTransObject.parmTransTxt("Sales
Transaction");
ledgerVoucher.addTrans(ledgerVoucherTransObject);
custVoucher.post(CustTrans);
ledgerVoucher.end();

ttscommit;
}

7-12 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 7: Trade

Settlement
When customer or vendor invoices are paid the payment transactions and the
invoice transactions must be settled against each other to close the transaction.
The CustSettlement and VendSettlement tables hold each settlement that has
taken place and stores the recIds of the transactions that are settled against each
other.

Settlement takes place in the CustVendSettle class, with most of the work done in
CustVendSettle.SettleNow(). This method handles settlement using a FIFO
principle (oldest invoice gets settled first), and using transaction markings.

To mark transactions for settlement, use the SpecTrans table. This stores the
RecId's of the transactions to be settled, and the settle amount. The class
Specification can be used to handle creation of SpecTrans records and also to
handle marked transactions.

Additionally, a method, markForSettlement() on CustTrans and VendTrans can


simplify the process further.

Example: Settling Two Customer Transactions


static void createSettlement(CustTrans _custTrans1,

CustTrans _custTrans2)

CustTable custTable,

CustTable = CustTable::find(CustTrans1.AccountNum);

custTrans1.markForSettlement(custTrans2);

CustTrans::settleTransact(custTable);

Microsoft Official Training Materials for Microsoft Dynamics 7-13


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

Trade Agreement
Trade agreements are flexible in the way they can be set up. They are applicable
to:

Single items
Single customers
Groups of items
Groups of customers
All items
All customers

This means that a single item/customer combination can have multiple prices. To
set up complicated pricing structures, it is important to understand the logic
behind the calculation. Companies often have unique and imaginative pricing
structures, so the calculation is modified regularly.

The trade agreement calculation searches through all combinations of


item/customer/group/all in a particular order and finds the lowest price. The Find
Next field that can be cleared to stop the calculation from looking for any further
prices once it has found one.

There are different methods for calculating the price:

A line discount
A multi-line discount
A total discount

Note that prices cannot be set up for groups of items or all items.

Demonstration - PriceDisc
This demonstration shows the calculation for the item price:

1. View the PriceDisc.findPriceAgreement() method:

while (idx < 9)


{
itemCode = idx mod 3;
accountCode = idx div 3;

itemRelation = itemCode == 0 ? itemId : '';


accountRelation = this.accountRelation(accountCode,
_priceGroupId);

7-14 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 7: Trade

The idx variable controls which combination of items, customers, and


groups to view. The system counts idx from 0 to 9, which means as it
loops itemcode = 0, accountCode = 0, then itemCode = 1, accountCode =
0, then itemCode = 2, accountCode = 0, then itemCode = 0, accountCode
= 1 etc.

For both itemCode and accountCode, 0 = table, 1 = group and


2 = all.

2. The item relation and account relation are set according to the item
code and account code. Since the system is calculating the price, the
itemCode = 1 or 2 is meaningless. Look in PriceDisc.FindLineDisc()
and note the item relation is set in the same way as the account
relation.

if (PriceDiscTable::activation(relation, accountCode,
itemCode, priceParameters))
{
if (PriceDisc::validateRelation(accountCode,
accountRelation) &&
PriceDisc::validateRelation(itemCode,
itemRelation ))
{

3. The trade agreement activation is checked to see if this combination


is allowed, and validation is performed on the values.

while select priceDiscTable


index PriceDiscIdx // equals order by
QuantityAmount
where priceDiscTable.Relation == relation
&& priceDiscTable.ItemCode == itemCode
&& priceDiscTable.ItemRelation == itemRelation
&& priceDiscTable.AccountCode == accountCode
&& priceDiscTable.AccountRelation ==
accountRelation
&& priceDiscTable.UnitId == unitID
&& priceDiscTable.Currency == currency
&& priceDiscTable.InventDimId == _inventDimId
&& (discDate >= priceDiscTable.FromDate
|| ! priceDiscTable.FromDate)
&& (discDate <= priceDiscTable.ToDate
|| ! priceDiscTable.ToDate)
{

Microsoft Official Training Materials for Microsoft Dynamics 7-15


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

4. The trade agreement table is searched for the item\customer


combination and includes the from- and to- date. Also included is the
inventDimId found using active dimensions
(as discussed in Chapter 6).

if (priceDiscTable.QuantityAmount <= absQty)


{
if (priceDiscTable.calcPriceAmount(absQty) <
this.calcPriceAmount(absQty) ||
! priceExist)
{

5. If the quantity calculated on the line is greater than any quantity


break set up on the trade agreement, and if there is a price or a
miscellaneous charge amount, and if this price is less than any
previously found price, then continue.

priceUnit = priceDiscTable.priceUnit();
price = priceDiscTable.price();
markup = priceDiscTable.markup();
deliveryDays = priceDiscTable.DeliveryTime;
calendarDays = priceDiscTable.CalendarDays;
actualPriceTable = priceDiscTable.data();
priceExist = true;

6. Data on the price is stored for later retrieval when the final price is
located.

if (! priceDiscTable.SearchAgain)
{
idx = 9;
break;
}

If the price set to not search for more prices, the loop ends.

To view the priceDisc class, refer to the setPriceAgreement.

Summary
In this lesson, the some of the most commonly used development concepts in the
Accounts Receivable and Accounts Payable modules have been introduced.
Concepts such as document updates, posting transactions, using settlements and
searching trade agreements should now be familiar, and developing
modifications with them is now possible.

7-16 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 7: Trade

Test Your Knowledge


1. True or False: SalesLineType is a sub-class of SalesTableType.

2. In which method in SalesFormLetter are the records in SalesParmLine


created?

3. What is the name of the class used when posting vendor transactions that
handle both posting to the AP sub-ledger and the AP ledger account?

Microsoft Official Training Materials for Microsoft Dynamics 7-17


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

4. What is 8 div 3? (answer without writing a program to test it!)

7-18 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 7: Trade

Lab 7.1 - Trade


Scenario
This is a part of the Point-of-Sale Case Study. See Chapter 1 for details.

Challenge Yourself!
Create a process that can post a payment transaction for the payment entered into
the POS screen.

Need a Little Help?

1. Create a new class and retrieve the sales order id from the caller.
2. Create a new journal header.
3. Loop over all POSRegisterTrans records linked to the sales order,
that have not previously been posted to the ledger, create a journal
line for each.
4. The journal lines should post customer account, and offset to the
register holding account.
5. Flag the payment lines as having been posted to the register.

Microsoft Official Training Materials for Microsoft Dynamics 7-19


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

Lab 7.1 - Trade (Solution)


Scenario
Create a process that can post a payment transaction for the payment entered into
the POS screen.

Step by Step

1. Create a new class, POSPostPayment:


a. Override the new method and set a class variable to hold the
sales id of the order being processed.
b. Create a method to create a LedgerJournalTable record.
o Use a LedgerJournalName record of type Daily.

c. Create a method to create a LedgerJournalTrans record.


o Accept LedgerJournalAccountType, Account, amount and
payment reference as parameters.
o Voucher comes from a class variable.

d. Create a method post the payment lines.


e. Loop through all unposted register transaction records and call
the method to create a journal line for each payment line.
f. Get a payment id from a number sequence and stamp each
payment transaction. Use this id as the payment reference on the
journal line.

7-20 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 7: Trade

Lab 7.2 - Payment Posting and Invoice Update


Scenario
This is a part of the Point-of-Sale Case Study. See Chapter 1 for details.

Challenge Yourself!
When the OK button on the payment lines screen is clicked, the payment posting
routine should be called. This should be followed by the standard invoice update.

Need a Little Help?

1. Call the payment posting class and the invoice posting class when
the Post button is clicked.
2. Create a new class, POSPostInvoice that posts an invoice.
3. Instantiate SalesFormLetter, DocumentStatus::Invoice.
4. Call SalesFormLetter.Update().
L

Microsoft Official Training Materials for Microsoft Dynamics 7-21


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

ab 7.2 - Payment Posting and Invoice Update (Solution)


Scenario
When the OK button on the payment lines screen is clicked, the payment posting
routine should be called. This should be followed by the standard invoice update.

Step by Step

1. Create a new class, POSPostInvoice, which posts an invoice:


o Instantiate SalesFormLetter, DocumetnStatus::Invoice.
o Call SalesFormLetter.Update().
o Call the payment posting class and the invoice posting class
when the Post button is clicked.

7-22 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 7: Trade

Lab 7.3 - Settlement


Scenario
This is a part of the Point-of-Sale Case Study. See Chapter 1 for details.

Challenge Yourself!
When posting an invoice and payment from the POS, they should be settled
against each other. Ensure that this happens. This should happen whether
automatic settlement is enabled or not, and should settle against each other no
matter what other transactions are open for the customer.

Need a Little Help?

1. Find the invoice and payment transactions and mark them against
each other for settlement using CustTrans.markForSettlement().
2. Post the settlement using CustTrans::SettleTransact().

Challenge Yourself! (Optional)


Create a Campaign Price flag on the trade agreements that is only selected if a
similar flag on the sales order header on the POS screen has been selected. It
should only be selected if it is the lowest price available to the customer.

Need a Little Help? (Optional

1. Modify the select statement in priceDisc.FindPriceAgreement to


look for prices with the CampaignFlag equal to a class variable that
is set when the class is instantiated.
2. Instantiate the PriceDisc class using the CampaignPrice flag from
SalesTable.

Microsoft Official Training Materials for Microsoft Dynamics 7-23


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

Lab 7.3 (Solution)


Scenario
When posting an invoice and payment from the POS, they should be settled
against each other. Ensure that this happens. This should happen whether
automatic settlement is enabled or not, and should settle against each other no
matter what other transactions are open for the customer.

Step by Step

1. Create a new class POSSettlement:


o Find the invoice customer transaction associated with the sales
order.
o Find the payment customer transaction associated with the order
(you can use the payment id).
o Mark each transaction to be settled against each other.
o Hint: Use CustTransInvoice.markForSettlement(custTable).
o CustTransPayment.markForSettlement(custTable).
o Post the settlement.
o Hint: Use CustTrans::settleTransact(custTable).

Step by Step (Optional)

1. Create a new enum EDT, POSCampaignPrice, EnumType = NoYes.


2. Add a new field CampaignPrice, to tables SalesTable and
PriceDiscTable.
3. Display the field on forms PriceDiscTable, and Sales Table.
4. In the PriceDisc class:
a. Create a new class variable, CampaignPrice, type = NoYes.
b. Set this variable in the new method from a parameter.
c. Modify the select statement in the findPriceAgreement method
to only find campaign prices when this variable is set.

5. Modify static method newFromSalesPurchLine() to look at the flag


on SalesTable and pass as a parameter in the new() method.

7-24 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 7: Trade

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 7-25


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

Solutions
Test Your Knowledge
1. True or False: SalesLineType is a sub-class of SalesTableType.

MODEL ANSWER: False

2. In which method in SalesFormLetter are the records in SalesParmLine


created?

MODEL ANSWER: SalesFormLetter.CreateParmLine()

3. What is the name of the class used when posting vendor transactions that
handle both posting to the AP sub-ledger and the AP ledger account?

MODEL ANSWER: CustVendVoucher

4. What is 8 div 3? (answer without writing a program to test it!)

MODEL ANSWER: Two

7-26 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 8: Inventory

CHAPTER 8: INVENTORY
Objectives
The objectives are:

Create and post inventory journals


Use inventory dimensions
Use InventSum
Use InventMovement
Use InventUpdate

Introduction
Similar to Ledger, Inventory is closely linked to many modules within Microsoft
Dynamics AX and is a vital module to understand. Unlike the ledger module,
modifications to the inventory system can be significant with regard to the work
required. This lesson discusses both creating and posting inventory transactions,
and using the data correctly after it has been posted.

A significant part of the inventory module is its inventory dimensions. Although


they may seem complicated to use, there are useful classes and methods written
that help you achieve excellent results. This lesson introduces some of the classes
used to manipulate inventory data.

Microsoft Official Training Materials for Microsoft Dynamics 8-1


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

Scenario
Isaac, the Systems Developer, has been asked to add a field on the sales order
that shows the current on-hand inventory of an item, based on the visible
inventory dimensions. To be able to do this he must understand how the
inventory system works in Microsoft Dynamics AX.

Inventory Journals
Inventory Journals are similar to Ledger Journals and can be created in a similar
way. Application elements are similar in design but are physically different
elements.

Tables
In the inentory module, the tables used are InventJournalName,
InventJournalTable, and InventJournalTrans.

InventJournalName contains the default data for a specific journal type. There
can be more than one InventJournalName per type.

InventJournalTable is the header record for the journal. There is one record per
journal.

InventJournalTrans contains data used to create the individual transactions that


will be posted. There can be one or more lines per journal.

Procedure: Creating an Inventory Journal


Use the following steps to create an inventory journal:

1. Create a journal table record or use an existing one. Set the journal
type according to the posting you require, such as profit\loss and
transfer.
2. Create lines for each transaction to be posted.
3. Post the journal. This checks the journal before posting. This step is
omitted if the journal is checked by the user before posting.

A detailed description of these steps follows.

8-2 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 8: Inventory

Creating the Journal Header


To create a new journal, use a journal name record to get the default data. The
journal name is commonly set either from a parameter or by prompting the user
to enter it in a dialog. Use a journal name of an appropriate type.

xInventJournalName = InventJournalName::find(
InventParameters::find().MovementJournalNameId);

InventJournalTable.InitFromInventJournalName(InventJournalN
ame);

InventJournalTable.Name = 'Movement Journal';

InventJournalTable.insert();

Look in the InventJournalTable.Insert() table method. Note that it handles the


creation of the journal number, which is the primary key defining the relationship
between the journal table and the lines.

Creating Lines
All transactions must be posted in the journal lines table. You must fill the record
with data and then insert it. There are two important fields to be filled every time
you insert a new record:

InventJournalTrans.JournalId which is mandatory, because


transactions are assigned to one of the journals
InventJournalTrans.Voucher which is the transaction voucher
number

Other fields in the transaction table are mandatory according to the journal types
and should be filled as necessary.

While developing, test the code by letting it create a journal, and then manually
opening the journal in the journal forms. Run validate and post highlights errors.

Posting Journals
After transactions are created in the journal, check and post the journal using the
InventJournalCheckPost class. Create an instance of this class using
newJournalCheckPost static method and then run the class. Parameters for this
method are:

_journalCheckPostType Check or post parameter for specifying whether to


check a journal only or check and post. Post indicates that after checking to post
to the journal.

_InventJournalTable Record in InventJournalTable representing the journal to


receive the posting.

Microsoft Official Training Materials for Microsoft Dynamics 8-3


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

The following example shows how to post an inventory journal.

Void PostJournal(InventJournalTable _InventJournalTable)

InventJournalCheckPost CheckPost;

CheckPost =
InventJournalCheckPost::newJournalCheckPost(
journalCheckPostType::Post, _InventJournalTable)

CheckPost.run();

Inventory Dimensions
Inventory dimensions are a powerful, versatile, and efficient way to track
variations in inventory movements. They define whether an item is tracked by:

Storage dimension, (warehouse, batch number, location, pallet, or


serial number)
Item dimension (color, size, or configuration)

There are a finite number of combinations of inventory dimensions, and each


combination can be assigned an ID number. Any reference to a particular
combination of dimensions can be referred to by the ID number, rather than
every dimension. This offers many possibilities when grouping and summing
data.

Tables
Available tables are defined as follows:

InventDim holds every combination of inventory dimensions used in the system


and the corresponding inventDimId. The InventDim.findOrCreate() method
looks for a particular combination, and creates it if it does not exist.

InventDimGroup groups items by inventory dimensions used.

InventDimSetup sets up each dimension group. There is one record per inventory
dimension per dimension group.

InventDimParm flags which dimensions to use in different situations. This table


is a temporary table created according to which dimensions are in use.

8-4 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 8: Inventory

Classes
The available classes include:

InventDimGlobal is a global class that holds a cache of dimension field ids and
names. This is used mainly by InventDimSearch.

InventDimSearch creates a list of InventDimSetup records in a list and iterates


through it.

InventDimOnHand finds current inventory levels for specific dimensions.

Procedure: Creating InventDimParm


To create the correct InventDimParm record, the system needs to look at the
dimension group for an item. The flags on the InventDimParm record need to be
set according to the situation in which it will be used.

This example illustrates how InventDimSearch is used to create an


InventDimParm record. The method being analyzed is
InventDimParm.initFinancialInvent().

InventDimParm.initFinancialInvent() is used, for example, in the


InventTrans.CalcCostValue() method to determine which dimensions to use
when calculating cost value.

1. View the InventDimParm.InitFinancialInvent() method:

void initFinancialInvent(InventDimGroupId _dimGroupId)


{
;
InventDimSearch::financialInvent(_dimGroupId, this);
}

A static method on InventDimSearch is called. This method will set the flags for
each inventory dimension that financial inventory option checked.

2. View the InventDimSearch::financialInvent() method:

static void financialInvent(InventDimGroupId _dimGroupId,


InventDimParm _inventDimParm)
{
if (_dimGroupId)

_inventDimParm.data(classfactory.inventDimGlobal().dimSearc
hBase().financialInvent(_dimGroupId));
else
_inventDimParm.clear();
}

Microsoft Official Training Materials for Microsoft Dynamics 8-5


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

If a dimension group is specified, the InventDmParm record is set to the values


returned by the method InventDimSearchBase.financialInvent(). Note the use of
the global cache for inventory dimension setup data. The first time it is accessed,
all the possible lookups for that specific inventory dimension group are found
and stored in maps, and placed in the global cache.

3. View InventDimSearchBase.financialInvent()

public InventDimParm financialInvent(InventDimGroupId


_groupId)
{
if (!_groupId)
return null;
if (!financialInvent.exists(_groupId))
this.initInventDimCache(_groupId);
return financialInvent.lookup(_groupId);
}

financialInvent is a map that contains the setup data for each group for the
financial inventory. The key is the group id, and the value is an InventDimParm
record holding the values for each inventory dimension. In this method, the map
is checked to see if the values have already been created for this group. If not,
then it will call the method to initialize these values.

4. View InventDimSeachBase.initInventDimCache()

if (_dimSearch.first(_dimGroupId))
do
{

After initializing the various maps that are used, InventDimSearch.first() is


called. This iterates through a list of the inventory dimensions that are active.
Inventory dimensions are made inactive through the application configuration
keys.

5. Many setup options are examined. The following code shows where
the flags for the dimension being active and to post financial
inventory are examined, and if both are true, then the
InventDimParm flag for this dimension is set to Yes. Additionally
the field id which is used to refer to which dimension this is for is
stored in a container.

if (_dimSearch.dimActive() &&
_dimSearch.dimFinancialInvent())
{
inventDimParmFinancialInvent.(y) = NoYes::Yes;
conFinancialInventFields += _dimSearch.dimFieldId();
}

8-6 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 8: Inventory

6. The InventDimParm record is then stored in the map to make further


lookups quicker due to not having to go to the database to retrieve
the data.

financialInvent.insert(_dimGroupId,
inventDimParmFinancialInvent);

Example: Using InventDimParm


This example shows how to use InventDim and InventDimParm. The method
being analyzed is PriceDisc.findPrice(). This method is used, for example, when
setting the sales price on a sales line.

Use an item with a dimension group containing dimensions set to Use in price
search, For sales prices. Also create sales prices for the different dimensions, as
shown Select main menu>inventory management>items>general tab

FIGURE 8.1 ITEM NUMBER

Prices are created for sizes allowed for this item.

The Use in sales price search flag means that when looking for a price for the
item, the system:

1. Searches for a match of the dimensions on the sales line to the


dimensions on the price.
2. Searches for a match of only item dimensions (size, colour,
configuration).

Microsoft Official Training Materials for Microsoft Dynamics 8-7


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

3. Searches for a price with blank dimensions.


4. It ignores dimensions altogether.

When entered into sales lines, the price is calculated using the size set on the line:

Procedure - Using InventDimParm


The PriceDisc class is instantiated in the SalesPurchLine.setPriceAgreement()
method. SalesPurchLine is a table map. The itemId and inventDim record of the
sales line is passed into the class at this point.

1. View the PriceDisc.FindPrice() method. Look for the following


code:

switch (moduleType)
{
case ModuleInventPurchSales::Purch:

inventDimAllActivated.copyActivatePurchPriceAll(inventTable
.DimGroupId, inventDim);
break;
case ModuleInventPurchSales::Sales:

inventDimAllActivated.copyActivateSalesPriceAll(inventTable
.DimGroupId, inventDim);
break;
default:

inventDimAllActivated.copyItemDim(inventTable.DimGroupId,
inventDim);
}

For moduleType = sales, calls inventDimParmAll.copyActivateSalesPriceAll()


(InventDimParmAll is instance of InventDimParm).

2. View the InventDimParm.copyActivateSalesPriceAll() method:

container dimFields =
InventDimSearch::activateSalesPriceAllFields(_dimGroupId);
;
len = conlen(dimFields);
for (h=1;h<=len;h++)
{
x = conpeek(dimFields, h);
this.(x) = _fromInventDim.(x);
}

The static method to initialize or retrieve the cached InventDimParm record for
this group, that has the flags set for all dimensions that has the Sales Price option
set, is called. The return value is used to determine which fields to use from the
InventDim record that is referenced on the sales line.

8-8 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 8: Inventory

3. Return to the PriceDisc.findPrice() method:

inventDimItemDimActivated.copyItemDim(inventTable.DimGroupI
d, inventDimAllActivated);

InventDimItemDimActivated is copied from the InventDimAllActivated but only


copies the fields that are item dimensions (color, size, or configuration).

Two InventDim records remain; one holding all values of dimensions only
applicable to the sales price search, and another holding values of item
dimensions applicable to the sales price search.

findAll =
!InventDim::isInventDimEqual(inventDimAllActivated,
inventDimItemDimActivated);
findItemDim =
!InventDim::isInventDimEqual(inventDimItemDimActivated,inve
ntDimNoneActivated);

findAll is set if InventDimAllActiviated = InventDimItemDimActivated.

findItemDim is set if InventDimItemDimActivated is blank.

inventDimAllActivated = findAll ?
InventDim::findDim(inventDimAllActivated) :
inventDimAllActivated;
inventDimItemDimActivated = findItemDim ?
InventDim::findDim(inventDimItemDimActivated) :
inventDimItemDimActivated;

If findAll = true, an existing InventDim record is searched for and placed in


InventDimAllActivated.

If findItemDim = true, an existing InventDim record is searched for and placed in


InventDimItemDimActivated.

return ((findAll && inventDimAllActivated.RecId &&


this.findPriceAgreement(_priceGroupId,
inventDimAllActivated.InventDimId)) ||
(findItemDim && inventDimItemDimActivated.RecId &&
this.findPriceAgreement(_priceGroupId,
inventDimItemDimActivated.InventDimId)) ||

this.findPriceAgreement(_priceGroupId,
InventDim::inventDimIdBlank()) ||
(_useItemPrice && this.findItemPrice()));

Search for a price with the inventory dimension id for the InventDimAllActivated
record. All inventory dimensions match if the found method returns true.

Microsoft Official Training Materials for Microsoft Dynamics 8-9


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

Then, search for a price with the item dimension. If a price is found, the method
returns true.

Then, search for a price with blank item dimensions. If a price is found, the
method returns true.

Finally, search for a price ignoring dimensions all together. If a price is found,
the method returns true.

InventSum
InventSum can be regarded as one of the most important tables in Microsoft
Dynamics AX. It is used to store current values of on-hand inventory. Any
function that creates an inventory transaction, including creating, updating or
deleting a sales line, purchase order line, inventory journal line or production
order, will cause an update to InventSum.

Due to the high number of processes that can update InventSum, optimistic
locking of InventSum is not appropriate. It is likely that a process that has
retrieved an InventSum record, is then going to update it. View the properties of
the table and note that the Occ property is set to No.

Instead, any changes to InventSum, are temporarily stored in another table,


InventSumDelta, and are only applied to InventSum when the current transaction
is committed. This ensures that InventSum records are locked for only a short
amount of time.

Tables
Tables that are associated with InventSum operations are as follows:

InventSum contains current inventory levels per item per inventory dimension
combination.

InventSumDelta contains information about on-hand changes that are not yet
committed to the database.

InventTrans automatically updates InventSum whenever a record is created,


updated, or deleted.

Classes
There are many classes used to retrieve data from InventSum. Some examples
are as follows:

InventOnHand contains calculations for finding inventory levels for different


inventory dimensions.

InventSumDate calculates the inventory level on a particular date.

8-10 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 8: Inventory

Invent Macros
There are some useful macros available when using InventSum:

#InventDimExistsJoin joins any other table, commonly InventSum, to InventDim


when related to InventDim through InventDimId. It uses InventDimParm to flag
the dimensions included in the join. The exists join improves performance and
should only be used if the joined InventDim record will not be used later in the
code.

#InventDimSelect selects records in InventDim according to the flags in


InventDimParm. This is used with a join to another table, commonly InventSum.

Demonstration - Code Walkthrough - Updating InventSum


The following walkthrough demonstrates how the system updates InventSum:

1. View the InventTrans.Insert() method:

if (InventSum::mustInventTransBeUpdated(this))
{
inventSum.initFromInventTrans(this);
inventSum.updateInventTrans(this,NoYes::Yes);
}

inventSum::mustInventTransBeUpdated() returns true if fields relevant to the


update of InventSum have been modified.

inventSum.initFromInventTrans() sets inventSum.itemId and


inventSum.inventDimId from inventTrans.

inventSum.updateInventTrans() is called passing the InventTrans record.

2. View the InventSum.updateInventTrans() method:

if (plus)
inventSum.addInventTransOnSum(inventTrans);
else
inventSum.subInventTransOnSum(inventTrans);

This method is called from the inventTrans.insert(), inventTrans.update() and


inventTrans.delete(). When it is called from inventTrans.insert() and
inventTrans.update(), the plus parameter is set to true. When it is called from
inventTrans.delete(), the plus parameter is set to false. This controls whether the
values in inventTrans are added or subtracted from InventSum.

Microsoft Official Training Materials for Microsoft Dynamics 8-11


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

3. View the InventSum.AddInventTransOnSum() method:

switch(inventTrans.StatusIssue)
{
case StatusIssue::Sold:
this.PostedQty += inventTrans.Qty;
break;
case StatusIssue::Deducted:
this.Deducted -= inventTrans.Qty;
break;
case StatusIssue::Picked:
this.Picked -= inventTrans.Qty;
break;
case StatusIssue::ReservPhysical:
this.ReservPhysical-= inventTrans.Qty;
break;
case StatusIssue::ReservOrdered:
this.ReservOrdered-= inventTrans.Qty;
break;
case StatusIssue::OnOrder:
this.OnOrder -= inventTrans.Qty;
break;
case StatusIssue::QuotationIssue:
this.QuotationIssue-= inventTrans.Qty;
break;
default:
}

According to the status of the InventTrans record, specific fields in InventSum


are updated.

4. Return to the inventSum.updateInventTrans method:

inventSumDelta.initFromInventTrans(inventTrans);
inventSumDelta.initFromInventSum(inventSum);
appl.inventUpdateOnhandGlobal().inventUpdateOnhand().addInv
entSumDelta(inventSumDelta,inventTrans);

The inventSumDelta record is updated using data from inventTrans and


inventSum.

The inventUpdateOnHand class is a class held in the global cache. This is due to
the frequency with which it is contructed. The
inventUpdateOnHand.inventUpdateOnHand() method sets the current ttsId on
the InventSumDelta record and then inserts the record in the database.

5. View the method application.ttsNotifyPreCommit()

this.inventUpdateOnhandGlobal().ttsNotifyPreCommit();

This method is called during a ttsCommit by the system classes that commit a
transaction to the database.

8-12 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 8: Inventory

6. View the method


InventUpdateOnHandGlobal.ttsNotifyPreCommit(). The method
changes company in the case that some of the transaction has been
performed in another company. In each company it calls the
following code.

inventUpdateOnhandMap.lookup(newExt).ttsNotifyPreCommit()

This finds the global instance of the class InventUpdateOnHand and calls the
ttsNotifyPreCommit() method.

7. View the method InventUpdateOnHand.ttsNotifyPreCommit(). The


two method calls to note called from here are as follows:
8. View the method InventUpdateOnHand.insertInventSum()

while select ItemId, InventDimId from inventSumDelta


group by ItemId, InventDimId

where inventSumDelta.ttsId == this.tTSId() &&


inventSumDelta.IsAggregated ==
NoYes::No
notexists join inventSum

where inventSum.ItemId ==
inventSumDelta.ItemId &&
inventSum.InventDimId ==
inventSumDelta.InventDimId

All records in InventSumDelta that were created in the current transaction and do
not have a related InventSum record, are retrieved. An InventSum record created
based on the InventSumDelta record and is added to a recordInsertList. When all
records have been added to the list, the records are committed to the database.

9. View the method InventUpdateOnHand.updateInventSum(). If only


one record is to be updated from InventSumDelta to InventSum, the
updateInventSumSimple() method is called. This method adds the
InventSumDelta record values to InventSum. If more than one
update is needed, the updateInventSumAdvance() method is called.

if (inventSumDeltaCnt == 1)
this.updateInventSumSimple();
else
this.updateInventSumAdvanced();

Microsoft Official Training Materials for Microsoft Dynamics 8-13


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

10. View the method


InventUpdateOnHand.UpdateInventSumAdvanced(). This method
calls the method InventUpudateOnHand.sqlupdateStr(). This method
builds a direct SQL statement that will add all InventSumDelta
records to the corresponding InventSum records in one call to the
database. This ensures that the performance of the write operation to
the database is maximized and the InventSum records are locked for
as short a time as possible.

container con = this.sqlUpdateStr();


Integer i;
str statement;
SqlStatementExecutePermission permission;
;
//Call updates in a cycle for all generated statements
for (i=1;i<=conlen(con);i++)
{
statement = any2str(conpeek(con,i));
permission = new
SqlStatementExecutePermission(statement);
permission.assert();

//BP Deviation documented


new
Connection().createStatement().executeUpdate(statement);
permission = null;
}

When building the SQL statement string, the system uses the field groups called
DeltaFields on InventSum and InventSumDelta to determine which fields need to
be updated. If you need to add more fields to InventSum, and therefore
InventSumDelta, you do not need to alter the SQL statement. Simply add the
fields to the field groups and the code will handle the update for you.

Demonstration - Code Walkthrough - Using InventOnHand


The following demonstrates how to use InventOnHand:

1. View the static method InventOnHand::newInventDim():

InventOnhand inventOnhand = new InventOnhand();


;

inventOnhand.parmInventDim(_inventDim);
inventOnhand.parmInventDimParm(_inventDimParm);
inventOnhand.parmItemId(_itemId);

return inventOnhand;

The class is instantiated and InventDim, InventDimParm, and ItemId class


variables are set from the parameters. There are other constructor methods
available, all of which set different parameters to calculate the InventSum record.

8-14 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 8: Inventory

2. View the InventOnHand.ReservePhysical() method:

this.setInventSum();

return inventSum.ReservPhysical;

The method returns the value of InventSum.ReservPhysical; however, this value


is first calculated with InventOnHand.setInventSum().

3. View the InventOnHand.setInventSum() method:

if (sumRead)
return;

this.findSumJoin();

sumRead = true;

If the calculation has not already been made, InventOnHand.findSumJoin() is


called.

4. View the InventOnHand.findSumJoin() method:

if (inventDimId)
{
inventSum = InventSum::find(itemId,inventDimId);
}
else if (inventDimParm.ItemIdFlag)
{
inventSum = InventSum::findSum(itemId,
inventDimCriteria, inventDimParm, InventSumFields::All);
}

If an InventDimId is specified the InventSum record is found.

If inventory values for a specific itemId are needed, InventSum::findSum() is


called.

5. View the InventSum::findSum() method:

if (_inventDimParm.InventSerialIdFlag &&
_inventDimCriteria.InventSerialId)
{
switch (_sumFields)
{

Different select statements are used because of performance differences between


the dimensions. For example, when a serial number dimension is enabled, the
serial number is unique to an item, whereas a warehouse dimension is used by all
items.

Microsoft Official Training Materials for Microsoft Dynamics 8-15


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

The method selects InventSum, summing the relevant fields (financial, physical,
or all). The join to InventDim ensures all relevant records in InventSum are
summed.

6. Return to the InventOnHand.FindSumJoin() method:

Again, different select statements are used depending on what data is required. If
only the Pallet Id is required, the select statement is different from when any
other dimensions are required.

After the InventSum agregrated record has been calculated, the InventSumDelta
records need to be included. The addInventSumDelta() method is called.

this.addInventSumDelta();

7. View the InventOnHand.addInventSumDelta() method.

if (!itemId ||
InventUpdateOnhandGlobal::mustAddInventSumDeltaOnhand(itemI
d))
inventSum.addInventSumDelta(this.findSumJoinDelta());

If the item has records in InventSumDelta, the inventSum.addInventSumDelta


method is called. This method adds an InventSumDelta record to InventSum. The
InventSumDelta record is found in the InventOnHand.findSumJoinDelta()
method.

8. View InventOnHand.findSumJoinDelta() method.

if (inventDimId)
{
inventSumDelta =
InventSumDelta::findSumDeltaDimId(itemId,inventDimId,Invent
SumFields::All);
}
else if (inventDimParm.ItemIdFlag)
{
inventSumDelta =
InventSumDelta::findSumDelta(itemId,inventDimCriteria,inven
tDimParm,InventSumFields::All);
}

Similar to the InventOnHandFindSumJoin() method, this method retrieves an


agregated InventSumDelta record based on the dimensions required.

8-16 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 8: Inventory

Lab 8.1 On-hand Inventory on Sales Order Form


Scenario
Issac, the systems developer, has been asked to display the current on hand
inventory levels on the Sales Order form. The quantity displayed should be based
on the item id and any inventory dimensions that are shown in the grid. There
should be a single field displaying the physically available quantity and it should
automtically adjust if the user modifies the dimensions that are displayed.

Challenge Yourself!
Create a display method on the SalesLine data source to return the physcally
avaiable inventory based on the current inventory dimensions that are displayed.
Note the following

1. InventDimFormSetup.parmDimParmVisibleGrid returns the


InventDimParm for the visible inventory dimensions.
2. InventOnHand.AvailPhysical() returns the quantity available.

Step by Step

1. Create a display method on the SalesLine datasource in SalesTable


form.
2. Use the InventOnHand class.
3. Instantiate using newParameters static method.
4. Use inventDimFormSetup.parmDimParmVisibleGrid() for
InventDimParm.
5. InventOnHand.availPhysical() returns available quantity.
6. Add field to sales line using the display method.

display inventQty availPhysical(SalesLine _SalesLine)


{
InventOnhand InventOnhand;
;
InventOnhand =
InventOnhand::newParameters(_salesLine.ItemId,

_salesLine.inventDim(),

element.inventDimSetupObject().parmDimParmVisibleGrid());

return inventOnHand.availPhysical();
}

Microsoft Official Training Materials for Microsoft Dynamics 8-17


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

InventMovement
Use the InventMovement class and subclasses to check and prepare data to update
inventory transactions. For instance, it is used to find if and where to post to
Ledger. It is similar to a data carrier used by other classes when updating
inventory data.

The following figure shows the hierarchy tree for the InventMovementClass

FIGURE 8.2 INVENTMOVEMENT STRUCTURE

Any lines that can have inventory transactions attached, for example, SalesLines,
PurchLines, InventJournalTrans, can be used in the construct method of the class,
and the appropriate sub-class is instantiated accordingly. The sub-classes control
how the updates differ.

Demonstration - Code Walkthrough - InventMovement


When a sales line item is modified, the quantity field is set according to certain
parameters. On the SalesTable form, the SalesLine_ds.ItemId.modified()method
calls the static method InventMovement::bufferSetTransQtyUnit(salesLine);

1. View the InventMovement::bufferSetTransQtyUnit() method:

InventMovement movement =
InventMovement::construct(buffer);

InventMovement is instantiated using SalesLine passed in through the buffer


parameter.

8-18 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 8: Inventory

2. View the InventMovement::Construct() method:

InventMovement movement =
InventMovement::constructNoThrow(buffer,subType,childBuffer
);
if (!movement)
throw error("@SYS20765");
return movement;

When instantiating the class, you can throw an error if the class cannot be
instantiated. If you do no want to throw an error, call
InventMovement::ConstructNoThrow directly.

3. View the InventMovement:ConstructNoThrow() method:

case tablenum(SalesLine) : return new


InventMov_Sales(buffer);

Note this method tests the table id of the record that it was called with and
instantiates the relevant sub-class of InventMovement. Some tables, such as
SalesPickingListJournalLine have their own methods written for instantiating
InventMovement. Others, such as InventJournalTrans, can instantiate different
sub-classes depending on values of certain fields, like
InventJournalTrans.JournalType. In this example the table is SalesLines, so
InventMov_Sales is instantiated.

4. Return to the InventMovement::bufferSetTransQtyUnit() method:

qty = UnitConvert::qty(movement.transQtyUnit(),
movement.transUnitId(),

movement.inventTable().inventUnitId(),
movement.itemId()
);

qty =
decround(qty,InventTable::inventDecimals(movement.itemId())
);
movement.setTransQty(qty);

qty =
decround(movement.transQtyUnit(),Unit::decimals(movement.tr
ansUnitId()));
movement.setTransQtyUnit(qty);

The quantity is set according to the values returned from the


movement.transQtyUnit(), movement.transUnitId()methods,and so on. Note that
theses methods return the value of a field from the SalesLine record used to
instantiate the class.

Using this method enables the same methods to be used throughout the
application when creating and updating inventory transactions.

Microsoft Official Training Materials for Microsoft Dynamics 8-19


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

InventUpdate
InventUpdate is the class that creates and updates inventory transactions and is
always used in conjunction with InventMovement. The following figure shows
the hierarchy tree for InventMovement:

FIGURE 8.3 INVENT UPDATE STRUCTURE

The class and subclasses are related to the base enums for inventory receipts and
issues.

The following figure shows the Issue Status:

FIGURE 8.4 ISSUE STATUS

8-20 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 8: Inventory

The following figure shows the Receipt Status:

FIGURE 8.5 RECEIPT STATUS

The class and subclasses are instantiated directly, rather than using a constructor
on the super class. Some of the sub-classes should be instantiated using the new()
method, others have static methods that can be used in certain circumstances.
Once the class has been instantiated, calling updateNow() posts the required
update.

The sub classes are used for different types of required updates.
InventUpd_Estimated creates the initial transaction when, for example, a
salesLine is created. InventUpd_Physical posts the physical movement of the
inventory, in other words, the inventory is received or shipped.
InventUpd_Reservation reserves an outflow against an inflow.

Demonstration: Code Walkthrough - InventUpdate


SalesFormLetter_Invoice.updateInventory() posts the financial movement of a
sales line. The value of the inventory posts to the inventory account and the
inventory transaction is stamped with a financial date.

The following procedure demonstrate how to use InventUpdate:

1. View the SalesFormLetter_Invoice.updateInventory() method:

inventUpd_Financial =
InventUpd_Financial::newSalesInvoice(_inventMovement,

ledgerVoucher,

number,

localSalesParmLine,

salesParmSubLine,

Microsoft Official Training Materials for Microsoft Dynamics 8-21


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

salesParmUpdate.ReduceOnHand);
inventUpd_Financial.updateNow();

localUpdateNow += -
inventUpd_Financial.updFinancialUnit();
localUpdateNowInvent += -
inventUpd_Financial.updFinancial();
localInvoiceUpdatedOnly += -
inventUpd_Financial.updPhysicalUnit();

InventUpd_Financial is instantiated using InventMovement, which has previously


been instantiated with SalesLine, LedgerVoucher, which contains the voucher
number for posting the ledger transactions, Number, which is the invoice number,
and SalesParmLine which holds the quantity and amounts to update.

InventUpdFinancial.UpdateNow() posts the transaction.

2. View the InventUpdFinancial.UpdateNow() method. If the


transaction has not physically posted, this is done first.

if (physical || remainPhysical !=
movement.remainPhysical() ||
physicalUnit || remainPhysicalUnit !=
movement.remainPhysicalUnit())
{
if (financial > 0.0 && movement.mustBeReceived())
{
throw error("@SYS117599");
}

if (financial < 0.0 && movement.mustBeDeducted())


{
throw error("@SYS117598");
}

inventUpd_Physical =
InventUpd_Physical::newInventUpdFinancial(movement,

this,

physical,

physicalUnit,

Currency::mstAmount(this.parmCostAmountCur(),

this.parmCurrencyCode(),

ledgerVoucher.lastTransDate(),

8-22 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 8: Inventory

this.parmExchRatesTriangulation(),

this.parmExchRate(),

this.parmExchRateSecondary()));

Some checking is then performed using inventMovement.

Calculate the cost value of the transaction and post to the general ledger:

costAmountMST =
movement.updateLedgerFinancial(ledgerVoucher, this);

if (financial < 0)
{
this.updateFinancialIssue(costAmountMST);
}
else
{
if (financial > 0)
{
this.updateFinancialReceipt(costAmountMST);
}
}

Create a reference to the posting in InventTransPosting:

this.updateInventTransPosting(ledgerVoucher.lastTransDate()
, ledgerVoucher.lastVoucher());

Some values, such as SalesLine.RemainSalesFinancial are updated to reflect the


values that have been posted.

Create a reference to the posting in InventTransPosting:

this.updateInventTransPosting(ledgerVoucher.lastTransDate()
, ledgerVoucher.lastVoucher());

Some values, such as SalesLine.RemainSalesFinancial are updated to reflect the


values that have been posted.

Microsoft Official Training Materials for Microsoft Dynamics 8-23


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

InventUpd_Reservation
To reserve incoming inventory against an outflow, use the InventUpd_Rservation
class:

void SalesReserveQty(SalesLine _salesLine,

InventQty _reserveQty)

InventUpd_Reservation reservation;

InventMovement movement;

movement = InventMovement::construct(_salesLine);

reservation =
InventUpd_Reservation::newMovement(movement, reserveQty,
true)

reservation.updateNow();

The InventUpd_Reservation class is instantiated using InventMovement. The


second parameter determines the additional quantity to be reserved. To reserve
more against an outflow, the quantity should be negative. It is not possible to
reserve more than what is ordered from the sales order line. You can unreserve
by specifying a positive amount.

Summary
Similar to Ledger, Inventory is closely linked to many modules within Microsoft
Dynamics AX. Modifications to the inventory system can be significant with
regard to work required.

This lesson demonstrate both creating and posting inventory transactions, and
using the data correctly after it has been posted. In addition, this lesson
introduces some of the classes used to manipulate inventory data.

8-24 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 8: Inventory

Test Your Knowledge


1. Is the InventDimParm table a temporary table?

2. In which method is an InventDim record created?

3. Which class is used to create inventory transactions?

Microsoft Official Training Materials for Microsoft Dynamics 8-25


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

4. Why has the Inventory Multi-transaction Tracking system been introduced?

5. True or false: An InventDimId is always unique to an itemId.

8-26 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 8: Inventory

Lab 8.2 Create an ABC allocation


Scenario
Isaac, the systems developer, has been asked by the Sales department to create a
job that will allocate inventory based on an ABC classification set on the
customer. To achieve this he should add a Reservation ABC classification field
to the customer table. The classification should be copied to the sales table and
sales line. The batch job should reallocate any reserved inventory to the sales
lines based on classification, then createdDate.

Challenge Yourself!

1. Write a routine that unreserves all open orders, and then reserve
according to the classification.
2. Reserving a negative quantity removes the reservation.
3. Use InventTransIdSum to find the existing reserved quantity.
4. Attempting to reserve the total quantity on a line reserves any that
are available to be reserved.

Step by Step

1. Create new Enum EDT, ReservationClassification, EnumType =


ABC.
2. Add new field, type = ReservationClassifcation to CustTable,
SalesTable and SalesLine.
3. Ensure SalesTable.ReservationClassification is initialized from
CustTable.ReservationClassificaition.
4. Ensure SalesLine.ReservationClassification is initialized from
SalesTable.ReservationClassificaition.
5. Display all new fields on relevant forms.
6. Create a new class to reserve items according to classification.
7. Find all open sales order lines.
8. Remove any reservations on the lines by reserving a negative
quantity of what is already reserved. Use InventTransIdSum to find
the quantity reserved.
9. Add open lines to a RecordSortedList while looping. The
RecordSortedList should sort by ReservationClassifcation.
10. After all open lines are unreserved, iterate through the list and
attempt to reserve the sales quantity for each line. System reserves
remaining quantity, or available quantity.

Microsoft Official Training Materials for Microsoft Dynamics 8-27


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

Lab 8.3 POS - Display inventory dimensions


Scenario
Add inventory dimensions to the POS form. Use the InventDimCtrl_Frm_Mov as
used by sales table form. Add the Dimension Display button so that the user can
adjust what dimensions are displayed.

Challenge Yourself!
Use the SalesTable form as inspiration. Look for all references to
InventDimSetupObject.

Step by Step
Inspiration for the following may be drawn from the SalesTable form.

1. Declare a form instance of InventDimCtrl_Frm_Mov.


2. Add a method UpdateDesign. Use same functionality as in
SalesTable form.
3. Add inventDimSetupObject form method to return instance of
InventDimCtrl_Frm_Mov.
4. Adda call to UpdateDesign to init method.
5. Set the number of columns in the grid according to the number of
fields already displayed + number of invent dim fields displayed.
6. Add menu item button to call action item InventDimParmFixed.

8-28 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 8: Inventory

Lab 8.4 POS - Display on hand inventory


Scenario
In the POS form, display inventory on hand for dimensions that are displayed.

Challenge Yourself!

1. InventDimFormSetup.parmDimParmVisibleGrid returns the


InventDimParm for the visible inventory dimensions.
2. InventOnHand.AvailPhysical() returns the quantity available.

Step by Step

1. Add a display method to display an inventQty on SalesLine


datasource:
2. Use the InventOnHand class.
3. Instantiate using newParameters static method.
4. Use inventDimFormSetup.parmDimParmVisibleGrid() for
InventDimParm.
5. InventOnHand.availPhysical() returns the available quantity.
6. Add field to sales line using display method.

Microsoft Official Training Materials for Microsoft Dynamics 8-29


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

Quick Interaction: Lessons Learned


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

1.

2.

3.

8-30 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 8: Inventory

Solutions
Test Your Knowledge
1. Is the InventDimParm table a temporary table?

MODEL ANSWER: Yes

2. In which method is an InventDim record created?

MODEL ANSWER: InventDim::FindOrCreate()

3. Which class is used to create inventory transactions?

MODEL ANSWER: InventUpdate

4. Why has the Inventory Multi-transaction Tracking system been introduced?

MODEL ANSWER: To solve deadlocking in InventSum

5. True or false: An InventDimId is always unique to an itemId.

MODEL ANSWER: False

Microsoft Official Training Materials for Microsoft Dynamics 8-31


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

8-32 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 9: Production

CHAPTER 9: PRODUCTION
Objectives
The objectives are:

Use the ProdMulti classes.


Use the ProdStatusType classes.
Use the ProdUpd classes.
Schedule production orders.

Introduction
Production orders in Microsoft Dynamics AX 2009 can have many components
and resources. One production order can be linked to many others through sub-
BOMs and reference production orders.

The Production module is designed to set up items that are produced, indicate
how they are produced, what components they are made up of, how long it takes,
and so on, and then to let the system calculate:

When they can be made


What resources to use
How much it will cost

This is done by updating the production order status at each stage of the process.

The status update process is an important part of the production module and is
often modified to include parameters unique to a company's production line. The
key to modifying the update process is knowing the function of the many classes
involved.

Microsoft Official Training Materials for Microsoft Dynamics 9-1


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

ProdMulti
Production orders can be updated either one at a time, or multiple orders at once.
The RunBaseMultParm structure enables the use of different parm tables and
different updates to run, all using the same structure.

FIGURE 9.1 THE RUNBASEMULTPARM STRUCTURE

Demonstration: Code Walkthrough - ProdMulti


An update to a production order can be called from either the Production table
form or from main menu > periodic > update. Both places call the same menu
items, which run the appropriate ProdMulti class. For instance, updating to
Estimated calls ProdMultiCostEstimation::main().

1. View the ProdMultiCostEstimation::main() method:

prodMultiCostEstimation =
ProdMultiCostEstimation::construct(args);

RunBaseMultiParm::initFromForm(prodMultiCostEstimation,args
);

ProdMultiCostEstimation is instantiated, and then passed into the static method


RunBaseMultiParm::initFromForm(), along with args.

9-2 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 9: Production

2. View the RunBaseMultiParm::initFromForm() method:

FormDataSource fdS;
Common common;
ParmBuffer parmBuffer =
runBaseMultiParm.defaultParmBuffer();
ParmUpdate parmUpdate =
runBaseMultiParm.defaultParmUpdate();
;
RunBaseMultiParm::initParm(runBaseMultiParm); //sets
the parm id

parmBuffer is a table map that is mapped to the different parm tables.


RunBaseMultiParm.defaultParmBuffer() is overridden in the sub-classes and
returns the appropriate parm table.

RunBaseMultiParm::initParm() retrieves saved data (sysLastValue) and sets the


ParmId used in the update.

if (args && args.dataset() &&


args.record().dataSource())
{
//init specific update tables
fdS = args.record().dataSource();
for (common=fdS.getFirst(true)
? fdS.getFirst(true)
:
args.record();common;common=fdS.getNext())
{
parmBuffer.Linenum++;

runBaseMultiParm.insert(common,parmBuffer);
}
}

All selected records in the form data source are added to the appropriate parm
table. If the update has been called from the main menu, there is no data source
and no records are found.

3. Return to the ProdMultiCostEstimation::main() method:

if (! prodMultiCostEstimation.prompt())
return;

prodMultiCostEstimation.run();

The prompt displays the form where the user can change the settings to update
the order. There is also a function to select more records, which must be used
when the update is called from the main menu.

The run() method is then called.

Microsoft Official Training Materials for Microsoft Dynamics 9-3


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

4. View the ProdMultiCostEstimation.run() method:

while (curParmCostEstimation)
{
try
{
prodTable =
this.initProdTable(curParmCostEstimation.ProdId);

prodTable.status().runCostEstimation(curParmCostEstimation,
false,prodPurch,this);
}

All records in the parm table are looped over. The ProdStatusType sub-class is
instantiated using the status of the prodTable record, then the update is called.

ProdStatusType
Production order updates are controlled using the ProdStatusType class and sub-
classes.

FIGURE 9.2 THE PRODSTATUSTYPE CLASS

Each stage of the update process must take place. If the current status of an order
is Estimated, and you try to update it to Report as Finished, then it must be
Scheduled, Released, and Started before it can be reported as Finished.

Before each update is run it checks to see if the previous status has already been
processed. If not, then it runs that update. It also handles what updates can take
place to a production order. For instance, a production order with status Finished,
cannot be Estimated.

Demonstration: Code Walkthrough - ProdStatusType


Update a production order with a status of Released to a status of Report as
Finished. The update to the status is called by ProdMultiReportFinished, which
is discussed in the next section.

9-4 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 9: Production

The following steps demonstrate the use of ProdStatusType:

1. View ProdMultiReportFinish.Run():

try
{

this.initProdTable(prodParmReportFinished.ProdId).status().
runReportFinished(prodParmReportFinished,false,this,sysSign
);
}

ProdMultiReportFinish.Run() instantiates the ProdStatusType_Released sub-


class using the status of the production order, and calls
ProdStatusType_Released.runReportFinished().

2. View ProdStatusType_Released.runReportFinished():

if (!ask)
{

ProdUpdReportFinished::runPreviousJob(prodParmReportFinishe
d,_multi);

prodTable.type().runReportFinished(prodParmReportFinished,_
multi,_sysSign);
}
return true;

ask is set to true only when the method is called to ask whether the update is
allowed, not to run the update.

ProdUpdReportFinished::RunPreviousJob runs the update to status Started.

3. View ProdUpdReportFinished::RunPreviousJob:

ProdParmStartUp prodParmStartUp =
ProdUpdStartUp::initParmBufferFromRepFin(prodParmReportFini
shed);
;
prodParmStartUp.insert();

ProdTable::find(prodParmStartUp.ProdId).status().runStartUp
(prodParmStartUp,false,null,_multi);

A record in ProdParmStartUp is created using the ProdParmReportFinished


record that is used for the update to Report as Finished.

ProdStatusType_Released sub-class is instantiated using the status of the


production order and calls ProdStatusType_Released.runStartUp().

Microsoft Official Training Materials for Microsoft Dynamics 9-5


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

4. View the ProdStatusType_Released.runStartUp() method:

if (!ask)

prodTable.type().runStartUp(prodParmStartUp,_multi);
return true;

Since the status previous to Started is released, and this order is already released,
there is no need to run the previous job.

Once the update to status Started has taken place, it returns to


ProdStatusType_Released.runReportFinished(), then runs the update to Report as
Finished.

ProdUpd
The update to the production order is taken handled by the ProdUpd class.

FIGURE 9.3 THE PRODUPD CLASS

When a production order is updated, there are two events that can occur.

The status is updated to the new status.


Journals can be created and posted to consume items and operations
as required, depending on the settings on the update, the production
parameters, and the item itself. For instance, updating to Started may
post the BOM journal and consume all items in the BOM at this
point.

9-6 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 9: Production

Demonstration: Code Walkthrough - ProdUpd


ProdStatusType.RunStartUp() runs the update to Startup status.

The following steps demonstrate the use of ProdUpd:

1. View the ProdStatusType_Released.RunStartUp() method:

ProdUpdStartUp prodStartUp =
ProdUpdStartUp::newParmBuffer(prodParmStartUp);
;
prodStartUp.run();

ProdUpdStartUp is instantiated using the parm table, and then run.

2. View the ProdUpdStartUp.run() method:

try
{
ttsbegin;

this.setParameters();

if (! this.validate())
throw Exception::Error;

viewCacheProdRoute = null;
viewCacheProdRoute =
ProdRoute::viewCacheProdId(prodTable.ProdId,true);

this.updateProduction();

ProdUpdStartUp.setParameters() retrieves ProdTable and initializes BomCalc. It


is called when component dimensions are used in the calculation of item
consumption.

viewCacheProdRoute activates cache on all prodRoute records for the current


production order.

ProdUpdStartUp.updateProduction updates to prodTable.Status,


prodTable.StartedUpQty, and prodTable.StUpDate.

this.updateBOMConsumption();

ProdUpdStartUp.updateBomConsumption() creates the BOM consumption


journal. If the update is set to post the BOM consumption, it also posts the
journal.

prodTable.status().startUpUpdateRouteJobs(this);

this.updateRouteConsumption();

Microsoft Official Training Materials for Microsoft Dynamics 9-7


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

The production route is updated and the route journal is created and posted.

this.startupReferences();

this.updateJobJournal(ParmJobStatus::Executed);

TransactionLog::create(TransactionLogType::ProdStartUp,
strfmt("@SYS76498", prodTable.ProdId, "@SYS77138"));

ttscommit;

Any references (production orders created due to this production order) are also
updated, and the parm table record is updated to show it has been posted.

Scheduling
Production orders are scheduled using a sequence specified by the user according
to what is currently available with respect to material and work center capacity.
While scheduling a specific production, the system indicates separate operations
and, if job scheduling, separates the jobs. There are a number of factors that
determine when an operation or job can be scheduled. If limited material and
capacity are used, then all the materials and work centers must be available. The
scheduling direction and scheduling date must also be considered.

To handle all the factors involved in an efficient manner, the data is stored in
temporary tables and arrays. This temporary data is handled by classes that have
the postfix Data; for example, production orders to be scheduled are placed into
the WrkCtrMasterData data storage class. Each production order is split into
route operations and held in WrkCtrRouteData.

When all the temporary data has been created, the time to carry out each job or
operation can be calculated, which can be applied back up through the tree of
jobs, operations, routes, and productions to give starting and ending dates and
times.

Demonstration: Code Walkthrough - Scheduling


ProdUpdScheduling controls the update of production orders to scheduled status.
Scheduling can be done by operation or by job. This instance shows scheduling
by job, as this covers both operation and job scheduling.

ProdMultiSchedulingJob selects the production orders, initializes the


ProdParmScheduling records, and allows the user to set the scheduling direction
and sort order.

9-8 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 9: Production

The following steps demonstrate the use of ProdUpdScheduling:

1. View the ProdUpdScheduling_Job.run() method:

setprefix(ProdMultiSchedulingJob::description());

wrkCtrParmSchedule =
WrkCtrParmSchedule::newProdParmScheduling(prodParmSchedulin
g);
wrkCtrMasterData = new WrkCtrMasterData_Prod();
wrkCtrScheduleJob = new
WrkCtrScheduleJobs_Detail(wrkCtrParmSchedule,wrkCtrMasterDa
ta);

super();

Some of the classes to be used are instantiated, and super() is called.

2. View the ProdUpdScheduling.run() method:

setprefix(#PrefixField(ProdParmScheduling,ProdId));

try
{
ttsbegin;

if (! this.validate())
throw Exception::Error;

wrkCtrScheduleJob.run();

After some initial validation, wkCtrScheduleJobs.run() is called.


(wrkCtrScheduleJobs is instantiated as an instance of wrkCtrScheduleJob_Detail
in ProdUpdScheduling_Job.run(), but wkCtrScheduleJobs.run()is not
overridden).

3. View the wkCtrScheduleJobs.run() method:

try
{
ttsbegin;

masterData.load();

wrkCtrMasterData has been instantiated as wrkCtrMasterData_Prod. The load


method creates records in the TmpSchedMasterData temporary table that refer to
each production order to be scheduled. This includes any child or parent
production orders if the Schedule References option was selected in the
Scheduling Update form.

if (!this.runMaster())
{

Microsoft Official Training Materials for Microsoft Dynamics 9-9


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

wrkCtrScheduleJobs.runMaster() runs the scheduling.

4. View wrkCtrScheduleJobs.runMaster():

this.masterDirection(parmSchedule.schedDirection());
this.masterIteration(1);
this.setMasterStartDateTime();

The scheduling direction dictates the sequence that the production orders are
retrieved. Note that if references are not scheduled, only one production order is
in the temporary table.

setMasterStartDateTime initializes the scheduled start and end date and time to
the schedule date and time.

while (doFirst)
{
if (this.masterDirection() ==
SchedDirection::Forward)
doNext = masterData.last();
else
doNext = masterData.first();

If the scheduling direction is forward, the last production is scheduled first.

while (doNext)
{
if (masterData.rec_Iteration() ==
this.masterIteration())
{
setprefix(masterData.prefixNumId());

if (! masterData.check())
throw error("@SYS18447");

routeData = masterData.newRouteData();

If the production order level is the level currently being scheduled, further checks
are made, wrkCtrRouteData is instantiated, and the class array variables are
populated with the data from ProdRoute records attached to the production order.

if (this.masterDirection() ==
SchedDirection::Forward)
{

this.masterSchedDate(masterData.rec_FromDate());

this.masterSchedTime(masterData.rec_FromTime());

this.routeSchedDate(masterData.rec_FromDate());

this.routeSchedTime(masterData.rec_FromTime());

9-10 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 9: Production

}
else
{

this.masterSchedDate(masterData.rec_ToDate());

this.masterSchedTime(masterData.rec_ToTime());

this.routeSchedDate(masterData.rec_ToDate());

this.routeSchedTime(masterData.rec_ToTime());
}

The scheduling date on the master and route data is set according to the
scheduling direction.

if (!this.runRoute())
return false;

RunRoute is called.

5. View the wrkCtrScheduleJobs.runRoute() method:

while (doFirst)
{
if (this.routeDirection() ==
SchedDirection::Forward)
doNext = routeData.last();
else
doNext = routeData.first();

The data in wkrCtrRouteData is looped and initial dates are set.

if (! this.runJobLink())
return false;

wrkCtrScheduleJob.runJobLink() is called.

6. View the wrkCtrScheduleJob.runJobLink() method:

jobLinkData.load();

JobLinkData.load() is called.

7. View the wrkCtrJobLinkData_ProdJob.load() method:

while select forupdate _prodRouteJob


index ProdOprIdx
where _prodRouteJob.ProdId ==
masterData.rec_NumId() &&
_prodRouteJob.OprNum ==
routeData.rec_OprNum()

Microsoft Official Training Materials for Microsoft Dynamics 9-11


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

Jobs attached to the current route are selected.

_calcTime =
_prodJobType.calcWrkCtrHours(masterData.bomCalcData(),

_prodRoute,

_prodRoute,

_prodRoute,

_prodRoute) * 3600;

_jobTime =
_prodJobType.calcJobSchedJobTime(_prodRouteJob,_prodRoute,_
calcTime);

The time needed for each job is calculated.

_currentJobIdx =
jobData.insert(_prodRouteJob.WrkCtrId,

_prodRouteJob.PropertyId,
_currentLinkIdx,

this.rec_JobLastIdx(),

_prodRouteJob.JobType,

_prodRouteJob.OprPriority,

_prodRouteJob.RecId,

_prodRouteJob.JobId,

_prodRouteJob.JobStatus >= ProdJobStatus::Completed ? 0 :


_jobTime,
_calcTime,

_prodRoute.WrkCtrLoadPct,
1,

_prodJobType.scheduleWorkTime(_prodRoute.routeGroup()),

_prodJobType.scheduleCapacity(_prodRoute.routeGroup()),

_prodRouteJob.Locked
);

Job data, including the calculated time is stored using wrkCtrJobData.

9-12 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 9: Production

8. Return to wrkCtrScheduleJobs.runJobLink():

oprNum = routeData.rec_OprNum();
while (oprNum)
{
if (jobLinkData.first(oprNum))
{

routeData.rec_FromDate(jobLinkData.rec_FromDate());

routeData.rec_FromTime(jobLinkData.rec_FromTime());

endDate = jobLinkData.rec_ToDate();
endTime = jobLinkData.rec_ToTime();

while (jobLinkData.next(oprNum))
{
if (endDate < jobLinkData.rec_ToDate()
||
(endDate == jobLinkData.rec_ToDate()
&& endTime < jobLinkData.rec_ToTime()))
{
endDate =
jobLinkData.rec_ToDate();
endTime =
jobLinkData.rec_ToTime();
}
}

routeData.rec_ToDate(jobLinkData.rec_ToDate());

routeData.rec_ToTime(jobLinkData.rec_ToTime());
routeData.rec_EndDate(endDate);
routeData.rec_EndTime(endTime);

Each operation in the route has the start and end date set according to start and
end date of the jobs attached to it.

9. Return to the wrkCtrSchedule.runRoute() method:

if (this.routeSchedOk())
{
routeData.savePosition();

oprNum = routeData.rec_OprNum();
while (oprNum)
{
routeData.update();

Each operation in the route is retrieved and routeDate.update() is called. This


method using JobLinkData.update() and JobData.update() commits the data to
the database.

Microsoft Official Training Materials for Microsoft Dynamics 9-13


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

Summary
Production orders in Microsoft Dynamics AX 2009 can have many components
and resources. The production module is designed to set up items that are
produced, indicate how they are produced, what components they are made up
of, how long it takes, and so on, and then to let the system perform the
calculations. Greater productivity can be achieved by automating these and other
business processes.

9-14 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 9: Production

Test Your Knowledge


1. Which parm table is used when updating the status from Scheduled to
Released?

2. Which class controls whether a production order can be updated from one
status to another?

3. WrkCtrRouteData is a data storage class which holds all


____________ that are in a route.

Microsoft Official Training Materials for Microsoft Dynamics 9-15


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

Lab 9.1 - Production


Scenario
This is part of the POS Case Study, Chapter 1.

Challenge Yourself!
Add a free text field Special Instructions to the production order, which is
copied to the BOM journal and printed on the production order picking list.

Need a Little Help?

1. ProdBOM is initialized from ProdTable in


ProdBOM.initFromProdTable().
2. Add the Special Instructions field to the sales lines. Make sure this
field is copied to the production order when the production order is
created directly from the sales line (sales table form > sales lines >
inquiries > production), and through master planning.

9-16 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 9: Production

Lab 9.1 - Production (Solution)


Scenario
Add a free text field Special Instructions to the production order, which is
copied to the BOM journal and printed on the production order picking list.

Step by Step

1. Create a new string EDT, ProdInstructions.


2. Add new field Instructions to tables ProdTable, ProdBOM and
ProdJournalBOM, type = ProdInstruction.
3. Make sure ProdBOM.Instruction is initialized from
ProdTable.Instruction.
4. Ensure ProdJournalBOM.Instruction is initialized from
ProdBOM.Instrucation.
5. Add ProdTable.Instrution field to ProdPickList report.

Microsoft Official Training Materials for Microsoft Dynamics 9-17


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

Lab 9.2 - Production


Scenario

Challenge Yourself!
Add the Special Instructions field to sales lines. Make sure this field is copied to
the production order when the production order is created both directly from the
sales line (sales table form > sales lines > inquiries > production), and through
master planning.

Need a Little Help?

1. ProdTable is initialized from SalesLine in


ProdTable.initFromSalesLine(). This is for creating a production
order directly from a sales line.
2. In the requirement calculation, view
RecCalc.initTransFromInventTrans(),
ReqCalc.covCreatePlannedOrder() and
ReqTrans.InitFromReqPO().
3. When firming an order from a planned order, view
ProdTable.InitFromReqPO().

9-18 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 9: Production

Lab 9.2 - Production (Solution)


Scenario
Add the Special Instructions field to sales lines. Ensure this field is copied to
the production order when the production order is created both directly from the
sales line (sales table form > sales lines > inquiries > production), and through
master planning.

Step by Step

1. Add new field Instructions to tables SalesTable, ReqTrans and


ReqPO, type = ProdInstruction.
2. Set ReqTrans.Instructions from SalesLine.Instructions using
InventTransId to look up the sales line in
ReqCalc.initTransFromInventTrans().
3. Set ReqPO.Instructions from ReqTrans.Instructions in
ReqCalc.covCreatePlannedOrder().
4. Set ReqTrans.Instructions from ReqPO.Instructions in
ReqTrans.InitFromReqPO().
5. To set the field in ProdTable when firming the order, set
ProdTable.Instructions from ReqPO in
ProdTable.InitFromReqPO().
6. To set the field in ProdTable when creating a production order
directly from a sales order, set ProdTable.Instructions from
SalesLine in ProdTable.InitFromSalesLine().

Microsoft Official Training Materials for Microsoft Dynamics 9-19


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

Quick Interaction: Lessons Learned


Take a moment and write down three Key Points you have learned from this
chapter

1.

2.

3.

9-20 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 9: Production

Solutions
Test Your Knowledge
1. Which parm table is used when updating the status from Scheduled to
Released?

MODEL ANSWER: ProdParmRelease

2. Which class controls whether a production order can be updated from one
status to another?

MODEL ANSWER: ProdStatusType

3. WrkCtrRouteData is a data storage class which holds all


____________ that are in a route.

MODEL ANSWER: WrkCtrRouteData is a data storage class that holds all


operations that are in a route.

Microsoft Official Training Materials for Microsoft Dynamics 9-21


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

9-22 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 10: Project Accounting

CHAPTER 10: PROJECT ACCOUNTING


Objectives
The objectives are:

Know the structure and design of the project module


Post transactions to the project module
Make modifications to the project invoice proposal procedure
Make modifications to the project invoice procedure

Introduction
The Project module is used to estimate and record costs and revenue transactions
against individual projects, which calculates running costs, total costs of the
project, and invoices customers for costs incurred.

This lesson describes how the project module is designed, how transactions are
created and posted, and how invoices are created.

Microsoft Official Training Materials for Microsoft Dynamics 10-1


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

Scenario
The Servicing department has asked Isaac, the System Developer, to make some
modifications to the project module. The project module in Contoso is used to
track time and costs used while installing and servicing home theatre equipment.
Some work is billable, other work is carried out under warranty.

Design
Transactions posted against projects can be one of five different types: Hour
(Employee), Cost, Revenue, Item, and On Account. Rather than use a Type field
on one table and many redundant fields, project transactions are recorded in five
tables.

Once transactions are posted against projects, an invoice proposal can be created.
The system creates a suggested invoice based on user defined criteria. Different
transactions are stored in five different tables. When transactions are invoiced,
they are stored in another set of five tables.

CustTable: Each invoice project is attached to a customer for invoicing and


payment.

ProjInvoiceTable: At least one invoice project is created for each customer.


Invoice projects allows you to create multiple projects for one customer, and
invoice at the same time.

ProjTable: This is the main table for projects. Each project has one record in
ProjTable.

ProjProposalJour: When an invoice proposal is created, the header record is held


in ProjProposalJour.

ProjInvoiceJour: Each invoice created has a header record in ProjInvoiceJour.

ProjEmplTrans, ProjProposalEmpl, ProjInvoiceEmpl: Contain transactions


related to hours spent working on a project by an employee.

ProjCostTrans, ProjProposalCost, ProjInvoiceCost: Contain transactions related


to costs incurred by the project.

ProjRevenueTrans, ProjProposalRevenue, ProjInvoiceRevenue: Contain


transactions related to revenue created by the project.

ProjItemTrans, ProjProposalItem, ProjInvoiceItem: Contain transactions related


to item consumption by the project.

ProjOnAccTrans, ProjProposalOnAcc, ProjInvoiceOnAcc: Contain pre-arranged


invoice amounts.

10-2 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 10: Project Accounting

ProjForecastCost, ProjForecastEmpl, ProjForecastRevenue, ForecastSales,


ProjForecastOnAcc: Contain forecast transactions for the project. Note that
ForecastSales is the same table that is used for item forecasts in the Inventory
and Sales modules. This table is used as it is considered during master planning
calculations.

ProjTransPosting, ProjTransBudget: Contains a summary of all transactions of


each type. These tables are used when calculating totals for the projects.

ProjTrans Class
The ProjTrans super class and its sub-classes control the creation and posting of
transactions. ProjTrans has a sub-class for each transaction type, and each sub-
classe has a sub-class for all the stages in a project.

FIGURE 10.1 PROJTRANS CLASS STRUCTURE

For instance, the ProjTransCostTrans.invoiced() method returns true if the cost


transaction has already been invoiced.

Microsoft Official Training Materials for Microsoft Dynamics 10-3


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

View the ProjTransCostTrans.invoiced() method:

//BP Deviation Documented


display boolean invoiced()
{
return ProjInvoiceCost::find(this.transId()).RecId ?
true : false;
}

If a ProjInvoiceCost record exists with a matching transaction id, the transaction


has already been invoiced.

Posting Transactions
When posting transactions against a project, journals are used. Cost transactions
use a ledger journal, item consumption transactions use an inventory journal, and
hours and revenue transactions use a project journal. On Account transactions are
created directly in the ProjOnAccTrans table. When invoiced, ledger transactions
are attached to the On Account transaction records.

Project journals are handled similar to inventory and ledger journals. You must
use a journal name from an existing ProjJournalName record to create a journal
header in ProjJournalTable, and then add lines in ProjJournalTrans. Use
ProjJournalCheckPost to post the journal.

All transaction types are created in a Proj...Trans table. When ledger or inventory
journals are posted and ProjId is marked on the line, they create ProjCostTrans
or ProjItemTrans records.

10-4 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 10: Project Accounting

Invoice Proposal
Project invoices are created by using an invoice proposal. This proposal is
populated according to selections made by the user, which can be modified if
necessary and then posted.

Invoice proposals are created using the ProjInvoiceChoose super class and sub
classes. On account transactions use a specific sub-class.

When ProjInvoiceChoose is run, the user is prompted to select which type of


transactions are to be invoiced and can also delimit the transactions using the
query. This query is executed to retrieve any un-invoiced transactions and
populate the ProjProposal transaction tables.

Code Walkthrough - ProjInvoiceChoose


On the Invoice Proposal form, clicking Create Invoice instantiates
ProjInvoiceChooseNormal which extends ProjInvoiceChoose.

The prompt is called, followed by run().

1. View the ProjInvoiceChoose.run() method:

this.progressInit("@SYS54552", progressTotal,
#AviFormLetter);
progress.setText("@SYS26577");

this.initQuery();

The progress bar is initialized.

The initQuery() method updates the query according to selections made by the
user. If a transaction type is to be excluded the corresponding datasource is
disabled in the query. From and To dates specified by the user are also entered
into the query.

while (queryRun.next())
{
this.assignTables();

For each transaction found, ProjInvoiceChooseNormal.assignTables() is called.

Microsoft Official Training Materials for Microsoft Dynamics 10-5


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

2. View the ProjInvoiceChooseNormal.assignTables() method:

if(queryRun.changed(tablenum(ProjTable)))
{
pProjTable = queryRun.get(tablenum(ProjTable));
pProjInvoiceTable =
ProjInvoiceTable::find(pProjTable.ProjInvoiceProjId);
}

if (queryEmpl && queryRun.changed(tablenum(ProjEmplTrans)))


pProjEmplTrans =
queryRun.get(tablenum(ProjEmplTrans));

if (queryRevenue || querySubscription)
pProjRevenueTrans =
queryRun.get(tablenum(ProjRevenueTrans));

Records returned by the query are placed in appropriate table variables.

ProjInvoiceChoose.run() then calls ProjInvoiceChooseNormal.doProposal().

3. View the ProjInvoiceChooseNormal.doProposal() method:

if (queryEmpl & queryRun.changed(tablenum(ProjEmplTrans)))


this.doEmpl();

if (queryCost && queryRun.changed(tablenum(ProjCostTrans)))


this.doCost();

if ((queryRevenue || querySubscription) &&


queryRun.changed(tablenum(ProjRevenueTrans)))
this.doRevenue();

For each transaction type, if the record has changed, a method populates the
ProjProposal transaction tables.

4. View the ProjInvoiceChoose.doEmpl() method:

if(this.parmProjEmplTrans() &&
this.parmProjEmplTrans().canBeInvoiced())
{

If a transaction record exists and has not been invoiced, the transaction can be
added to this proposal.

this.setProjProposalJour(this.parmProjEmplTrans().CurrencyI
d);
this.progressUpdate(strfmt("@SYS26810",this.parmProjEmplTra
ns().ProjId,this.parmProjEmplTrans().TransDate));

10-6 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 10: Project Accounting

An invoice proposal header is created and the progress bar is updated.

projProposalEmpl =
ProjProposalEmpl::initProposaleFromTrans(this.parmProjEmplT
rans());
projProposalEmpl.ProposalId =
this.parmProjProposalJour().ProposalId;

if
(CustTable::isCustDKPublic(pProjInvoiceTable.InvoiceAccount
) == NoYes::Yes && pProjInvoiceTable.eInvoiceLineSpec ==
NoYes::Yes)
{
projProposalEmpl.eInvoiceAccountCode =
pProjInvoiceTable.eInvoiceAccountCode;
}

if (projProposalEmpl.validateWrite())
{
projProposalEmpl.insert();

The projProposalEmpl record is initialized from the ProjEmplTrans record and


the invoice proposal header. After validation, the projProposalEmpl record is
committed to the database.

Microsoft Official Training Materials for Microsoft Dynamics 10-7


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

Invoice
Invoicing takes place in the ProjFormLetter class and the
ProjFormLetter_Invoice sub-class. The structure for these classes is similar to
the FormLetter classes used in Accounts Receivable (AR) and Accounts Payable
(AP).

Code Walkthrough ProjFormLetter

1. View the ProjFormLetter.run() method:

if(projProposalJour)
{
try
{
if (batchHeader)
{
formLetterMultiThread =
FormLetterMultiThread::newFormLetter(this);

batchHeader.addRuntimeTask(formLetterMultiThread,this.parmC
urrentBatch().RecId);

batchHeader.addDependency(projFormLetterEndMultiThread,form
LetterMultiThread,BatchDependencyStatus::FinishedOrError);
}
else
{
this.createJournal();
}

Invoices are created from Invoice proposals, so projProposalJour must exist. If


the job is running in batch, some tasks are created to improve performance,
otherwise the createJournal() method is called

2. View the ProjFormLetter.createJournal() method:

projProposalJour.InvoiceDate =
projInvoiceParmTable.InvoiceDate;
projProposalTotals = new
ProjProposalTotals(projProposalJour, parmId);
projProposalTotals.calc();

this.tax(projProposalTotals.tax());

recordListProjProposalCost =
projProposalTotals.recordListProjProposalcost();
recordListProjProposalEmpl =
projProposalTotals.recordListProjProposalEmpl();
recordListProjProposalRevenue =
projProposalTotals.recordListProjProposalRevenue();

10-8 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 10: Project Accounting

recordListProjProposalItem =
projProposalTotals.recordListProjProposalItem_Project();
recordListProjProposalOnAcc =
projProposalTotals.recordListProjProposalOnAcc();

Totals are calculated and the project transactions are put into lists which can be
used by the invoicing process.

if (proforma)
this.insertProforma();
else
this.insertJournal();

If the invoice is to be posted, ProjFormLetter.insertJournal() is called.

3. View the ProjFormLetter.insertJournal() method:

if (this.updateNow())
{

TransactionLog::create(this.transactionLogType(),this.tranS
actionLogTxt());
ttscommit;
this.createPayment();
}

The bulk of the work is done in updateNow(), which is overridden in the sub-
class.

4. View the ProjFormLetter_Invoice.UpdateNow() method:

this.initTransactionTxt(creditNote ?
LedgerTransTxt::ProjectCreditNoteLedger
:
LedgerTransTxt::ProjectInvoiceLedger, projProposalJour);
this.initLedgerVoucher();
this.initMarkup();

projProposalJour =
this.getProjProposalJour(projInvoiceParmTable, true);

Ledger voucher, transaction texts, and the invoice journal are initialized.

this.createProjInvoiceEmpl();
this.createProjInvoiceCost();
this.createProjInvoiceRevenue();
this.createProjInvoiceItem();

this.createProjInvoiceOnAcc();
this.createProjInvoiceSalesLine();

Microsoft Official Training Materials for Microsoft Dynamics 10-9


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

Invoice transactions are created and the necessary ledger and inventory postings
are handled.

projProposalJour.LineProperty =
ProjLinePropertyCode::Invoiced;
projProposalJour.LedgerVoucher =
projInvoiceJour.LedgerVoucher;
projProposalJour.SalesOrderbalance =
projInvoiceJour.SalesOrderbalance;
projProposalJour.update();

this.postMarkupTable();
this.postTax();
this.createCustTrans();

salesFormLetter.parmDeleteFullyInvoiced(true);
salesFormLetter.deleteFullyInvoiced();

this.postEndDisc();
this.postInvoiceRoundOff();

if (projInvoiceJour.CashDiscCode &&
TaxParameters::canApplyCashDiscOnInvoice_ES())
{
this.createCashDisc();
}

ledgerVoucher.end();

The invoice journal is finalized and final postings are made.

Summary
This lesson discusses how to design the project module, how to create and post
transactions, and how to create invoices.

From this you should learn:

About the structure and design of the project module.


How to post transactions to the project module.
How to make modifications to the project invoice proposal
procedure.
How to make modifications to the project invoice procedure.

10-10 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 10: Project Accounting

Test Your Knowledge


1. True or False: A project is linked to the customer table through
ProjTable.CustAccount.

2. In which class would you find a method that returns whether an employee
hours transaction has already been invoiced?

3. When posting a project invoice, which method calls the project totals
calculation method - projProposalTotals.calc()?

Microsoft Official Training Materials for Microsoft Dynamics 10-11


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

Lab 10.1 - Warranty Item


Scenario
The Contoso Company offers repairs to the items that they sell, and requires
employees to record time against a single project, called Warranty Repairs. It is
required that the employee record in which item the repair work is carried out.

Challenge Yourself!
Add a WarrantyItemId field, type = itemId to the journal lines used for
employee time entry, and ensure this item id is also copied to the appropriate
project transactions when the journal is posted.

Step by Step

1. Create a new string EDT, ProjWarrantyItemId.


2. Add a new field, WarrantyItemId, type = ProjWarrantyItemId to
table ProjJournalTrans and ProjEmplTrans.
3. Set ProjEmplTrans.WarrantyItemId =
ProjJournalTrans.WarrantyItemId in static table method
ProjEmplTrans::InitTransFromJournal().

10-12 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 10: Project Accounting

Lab 10.2 - Place Invoice Proposal On Hold


Scenario
Isaac, the Systems Developer, has been asked to create a function that will enable
Invoice Proposals to be placed "On Hold". The invoice proposal will be put on
hold while transactions are investigated in the case that the Prakash, the Project
Manager, believes that there might be an error.

Challenge Yourself!
Enable an invoice proposal to be put on hold.

This requires a new field, OnHold, type = NoYes. If this field is set, then the
invoice cannot be posted, and a new invoice proposal can be created that may
include transactions that were included on the OnHold proposal.

Step by Step

1. Add a new NoYes field, OnHold to ProjProposalJour.


2. Display this field on form ProjInvociceProposal.
3. In the classes, ProjTransCostTrans, ProjTransEmplTrans,
ProjTransItemTrans, ProjTransOnAccTrans,
ProjTransRevenueTrans, modify the method proposal(), to return
false if a proposal transaction exists, but is on hold.

Microsoft Official Training Materials for Microsoft Dynamics 10-13


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

Quick Interaction: Lessons Learned


Take a moment and write down three Key Points you have learned from this
chapter

1.

2.

3.

10-14 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 10: Project Accounting

Solutions
Test Your Knowledge
1. True or False: A project is linked to the customer table through
ProjTable.CustAccount.

MODEL ANSWER: False

2. In which class would you find a method that returns whether an employee
hours transaction has already been invoiced?

MODEL ANSWER: ProjTransEmplTrans

3. When posting a project invoice, which method calls the project totals
calculation method - projProposalTotals.calc()?

MODEL ANSWER: CreateJournal

Microsoft Official Training Materials for Microsoft Dynamics 10-15


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

10-16 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 11: Workflow

CHAPTER 11: WORKFLOW


Objectives
The objectives are:

Identify the components required prior to using workflow


Specify which application module a workflow is applicable to using
a workflow category
Create a new workflow template
Link tables to workflows using a workflow document
Define what happens when the workflow is approved or denied.
Apply a workflow to a form
Create Event Handlers and apply them to a workflow
Configure a workflow
Submit a record for workflow processing
Use the workflow processor

Introduction
Workflow is a module in Microsoft Dynamics AX 2009, that allows flexible
task and approval routes for documents created by users. For example, a purchase
requisition may need to be approved by a number of different employees
according to the requisition's total amount, and each employee has to approve it
before the next employee in the approval route.

A Workflow in Microsoft Dynamics AX uses a combination of AOT elements


created by IT, and configuration that may be set up by a user. This lesson
introduces the development side of creating an workflow, for which you will
need to use skills developed from this class and the Morph X development class.

Microsoft Official Training Materials for Microsoft Dynamics 11-1


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

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 that takes the
customer over their credit limit, the sales order should be 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 Installation
A number of the workflow system components are required to be installed before
you can begin to create and configure workflows in Microsoft Dynamics AX.

Workflow website. This is an IIS website that controls the flow of


the workflows.
Workflow accounts. There are two accounts used - a system account
used to provide access to the workflow tables, and an execution
account that is used to execute business logic.
Microsoft Dynamics AX workflow server component. This is the
workflow engine and is installed using the Microsoft Dynamics AX
installation files. The website and the accounts are required to run
this installation.

NOTE: This course does not cover the installation of the workflow system
components; however you need to be aware of the requirements. For more
information about workflow installation, refer to the Administrator Guide.

Create a Workflow Category


A workflow category defines the module in which the workflow will be
available. Modules are defined by the SysModule enum.

Demonstration: Creating a Workflow Category


This demonstration shows you how to create a category that allows the workflow
to be configured from the Projects module.

1. Open the AOT


2. Expand the Workflow node
3. Right-click on the Workflow Category 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 SalesCreditLimitApproval

11-2 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 11: Workflow

6. Change the label property to Sales credit limit approval.


7. Change the Module property to Cust.
8. Right-click on the newly created workflow category and select Save.

FIGURE 11.1

Create a Workflow Template


A workflow template brings all the different elements of the workflow together.
Workflow configurations are created based on a template, and many
configurations can be based on the same template. The template defines which
actions are allowed and which are required.

Demonstration: Creating a Workflow Template


This demonstration creates a workflow template and binds 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 Templates node and select New
Workflow Template. A new workflow template named
WorkflowTemplate1 will be created.
4. Right-click on the newly created workflow template and select
Properties.

Microsoft Official Training Materials for Microsoft Dynamics 11-3


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

5. Change the name property to SalesCreditLimitApproval.


6. Change the label property to Sales credit limit approval.
7. Change the category property to SalesCreditLimitApproval.
8. Right-click on the newly created workflow template and select Save.

FIGURE 11.2

Create a Workflow Document


A workflow document defines what data is affected by the workflow. It can
define one or more tables and all or selected fields on that table. This is done by
using a query.

Demonstration: Creating a Workflow Document


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

1. Open the AOT.


2. Right-click on the Query node and select New Query.
3. Rename the query to SalesCreditLimitApproval.
4. Expand the newly created query.
5. Open another AOT window.
6. Expand Data Dictionary > Tables.

11-4 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 11: Workflow

7. Find the table SalesTable.


8. Drag the SalesTable table to the Data Sources node of the
SalesCreditLimitApproval query.
9. Right click on the SalesCreditLimitApproval query and select Save.
10. In the AOT, right-click on the Classes node and select New Class.
11. Copy the following code into the ClassDeclaration.
12. Press F8 to save the method
13. Right-click on the class and select override method >
getQueryName.
14. Copy the following code in to the method.
15. Press F8 to save the method
16. Find the SalesCreditLimitApproval workflow template in the AOT.
17. Right-click on the workflow template and select Properties.
18. In the document property, enter SalesCreditLimitApproval.
19. Right-click on the workflow template and select Save.

class ProjTimeApproval extends workFlowDocument


{
}

QueryName getQueryName()
{
return queryStr(SalesCreditLimitApproval);
}

Create a Workflow Approval


An approval route may contain a number of outcomes. It 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 specific code by specifying a menu item for each item.

Demonstration: Creating a Workflow Approval


This demonstration creates a workflow approval and specifies how the approval
route can be defined.

1. Open the AOT.


2. Expand the workflow node.
3. Right-click on approvals and select New Approval.
4. Right-click on the newly created approval and select properties.
5. Change the Name property to SalesCreditLimitApproval.
6. Change the Document property to SalesCreditLimitApproval.
7. Change the ParticipantProvider property to
WorkflowUserGroupParticipantProvider.

Microsoft Official Training Materials for Microsoft Dynamics 11-5


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

8. Change the DueDateProvider to


WorkflowWorkCalendarDueDateProvider.
9. Change the HierarchyProvider to WorkflowLimitHierarchyProvider.
10. Change the DocumentMenuItem to SalesTable.

The Providers specify classes that enable rules to be defined for the workflow.
These providers are standard application classes but can be overridden and
modified, or other providers can be used in their place.

FIGURE 11.3

Demonstration: Creating Approval Outcomes


Use a standard class that acts as an engine for all approval outcomes.

You are not required to do anything but set the workflow to either Approved or
Denied, therefore call the same class from two different menu items. The two
menu items simply allow you to use two different labels. In more complex
workflows it may be necessary to override or copy and modify this class rather
than use it directly.

1. Open the AOT.


2. Expand Menu Items.
3. Right-click on Action and select New Menu Item.
4. Right-click on the newly created Action item and select Properties.
5. Change the Name property to SalesCreditLimitApprove.

11-6 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 11: Workflow

6. Change the Label property to Approve.


7. Change the ObjectType property to Class.
8. Change the Object property to WorkflowWorkItemActionManager.
9. Right-click on the SalesCreditLimitApprove menu item and select
Save.
10. Right-click on Action and select New Menu Item.
11. Right-click on the newly created Action item and select Properties.
12. Change the Name property to SalesCreditLimitReject.
13. Change the Label property to Reject.
14. Change the ObjectType property to Class.
15. Change the Object property to WorkflowWorkItemActionManager.
16. Right-click on the SalesCreditLimitReject menu item and select
Save.
17. In the AOT, expand Workflow > Approvals >
SalesCreditLimitApproval > Outcomes.
18. Right-click on the Approve node and select Properties.
19. Change the ActionMenuItem property to SalesCreditLimitApprove.
20. Right-click on the Reject node and select Properties.
21. Change the ActionMenuItem property to SalesCreditLimitReject.
22. Right-click on the Deny node and select Properties.
23. Change the Enabled property to No.
24. Right-click on the RequestChange node and select Properties.
25. Change the Enabled property to No
26. Right-click on the SalesCreditLimitApproval approval node and
select Save.

FIGURE 11.4

Microsoft Official Training Materials for Microsoft Dynamics 11-7


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

Demonstration: Attaching an Approval to a Template


The approval needs to be attached to the template. This demonstration shows you
how to attach an approval to a template.

1. Open the AOT.


2. Expand Workflow > Workflow Templates >
SalesCreditLimitApproval.
3. Open another AOT window.
4. Expand Workflow > Approvals.
5. Find SalesCreditLimitApproval.
6. Drag the SalesCreditLimitApproval approval to the Required
Elements node of the SalesCreditLimitApproval template.
7. Right-click on the SalesCreditLimitApproval workflow template and
select save.

Enable Workflow on a Form


Now that the workflow template is defined, you can specify which forms will use
this template.

Demonstration: Add a WorkflowState Field


Any form that uses the same table in the datasource as is specified in a workflow
document is able to use that document for workflow. This demonstration shows
how to enable workflow on the Project Hours journal 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.

1. Open the AOT.


2. Expand Data Dictionary.
3. Right-click on Base Enums and select New
4. Rename the new enum to SalesCreditLimitApprovalStatus
5. Add four elements to the Enum called NotSubmitted, Submitted,
Approved, Rejected.
6. Expand Tables > SalesTable.
7. Right-click on Fields and select New > Enum.
8. Right-click on the newly created field and select Properties.
9. Change the Name property to CreditLimitApprovalStatus.
10. Change the EnumType property to SalesCreditLimitApprovalStatus.
11. Right-click on the SalesTable node and select Save.

11-8 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 11: Workflow

Demonstration: Enable Workflow on the Form


Workflow on the form is enabled using properties on the design node, and by
overiding a form method. This demonstration shows you how to enable workflow
on a form.

1. Open the AOT.


2. Expand Tables > SalesTable.
3. Create a new method and add the first method in the following code.
4. Save the changes made to the table.
5. Expand Forms > SalesTable > Designs.
6. Right-click on the design node and select Properties.
7. Change the WorkflowEnabled property to Yes.
8. Change the WorkflowDatasource property to SalesTable.
9. Right-click on the form Methods node and select Override
Method > canSubmitToWorkflow.
10. Copy the second method in the following code into the method.

boolean canSubmitToWorkflow()
{
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;
}

public boolean canSubmitToWorkflow()


{
return salesTable.canSubmitToWorkflow();
}

Microsoft Official Training Materials for Microsoft Dynamics 11-9


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

The canSubmitToWorkflow method returns true if the ProjJournalTable record


has not already been submitted and the total hours entered in the transactions is
greater than 40.

Demonstration: Create a Submit to Workflow Class


To submit a document to workflow, call standard code to prompt the user for a
comment and to process the submisson. This demonstration shows you how to
create a submit to workflow class.

1. Open the AOT and create a new class.


2. Copy the following code in to the classDeclaration, submit method
and the main method.
3. Press F8 to save and compile the code.
4. Open another AOT and expand Menu Items.
5. Drag the SalesCreditLimitSubmit class to the Actions node.
6. Right-click on the newly created Actions node and select Properties.
7. Change the Label property to Submit.
8. Right-click on the SalesCreditLimitSubmit menu item and select
Save.
9. Locate the workflow template node in the AOT.
10. Right-click and select Properties.
11. Change the SubmitToWorkFlowMenuItem property to
SalesCreditLimitSubmit.

class SalesCreditLimitSubmit
{
}

void submit(Args args)


{
// Variable declaration.
recId recId = args.record().RecId;
WorkflowCorrelationId workflowCorrelationId;
// Hardcoded template name
WorkflowTemplateName workflowTemplateName =
workflowtemplatestr(SalesCreditLimitApproval);
// 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());

11-10 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 11: Workflow

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::activateFromWorkflowTemplate(workflowTemplateName
,

recId,

note,

NoYes::No);
SalesTable.CreditLimitApprovalStatus =
SalesCreditLimitApprovalStatus::Submitted;

// Send an Infolog message.


info("Submitted to workflow.");

ttscommit;
}

catch(exception::Error)
{
info("Error on workflow activation.");
}
}

args.caller().updateWorkFlowControls();
}

public static void main(Args _args)


{
SalesCreditLimitSubmit SalesCreditLimitSubmit
= new SalesCreditLimitSubmit();
;

SalesCreditLimitSubmit.submit(_args);

Microsoft Official Training Materials for Microsoft Dynamics 11-11


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

Create Event Handlers


Event handlers are used to execute business logic at specific events in the
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
WorkflowStartedEventHandler This event raises when the
workflow instance starts.
WorkflowCompletedEventHandler This event raises when the
workflow instance ends after it
is completed.
WorkflowCanceledEventHandler This event raises when the
workflow instance ends after it
is canceled. Use this event
handler to perform any clean
up operations needed.
WorkflowConfigDataChangeEventHandler This event raises when the
workflow configuration data
changes. Use this event
handler to identify when a
configuration has changed. For
example, if you create an
association between the
application data and a
workflow configuration, this
event handler would raise if
the configuration was deleted
or updated.

Demonstration: Add Workflow Level Event Handlers


This demonstration shows you how to add workflow level event handlers.

1. Create a new class. Add the following ClassDeclaration and


methods.
2. Open the properties form for the SalesCreditLimitApproval
workflow template.

11-12 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 11: Workflow

3. Set StartedEventHandler, CompletedEventHandler and


CanceledEventHandler properties to SalesCreditLimitEventHandler

class SalesCreditLimitEventHandler implements


WorkflowStartedEventHandler,
WorkflowCanceledEventHandler,
WorkflowCompletedEventHandler
{
}

public void cancelled(WorkflowEventArgs _workflowEventArgs)


{
SalesTable SalesTable;
;

ttsbegin;

select forupdate SalesTable


where SalesTable.RecId ==
_workflowEventArgs.parmWorkflowContext().parmRecId();

SalesTable.CreditLimitApprovalStatus =
SalesCreditLimitApprovalStatus::NotSubmitted;

SalesTable.update();

ttscommit;

public void completed(WorkflowEventArgs _workflowEventArgs)


{
SalesTable SalesTable;
;

ttsbegin;

select forupdate SalesTable


where SalesTable.RecId ==
_workflowEventArgs.parmWorkflowContext().parmRecId();

if (salesTable.CreditLimitApprovalStatus ==
SalesCreditLimitApprovalStatus::Submitted)
{
SalesTable.CreditLimitApprovalStatus =
SalesCreditLimitApprovalStatus::Approved;

SalesTable.update();
}

ttscommit;

Microsoft Official Training Materials for Microsoft Dynamics 11-13


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

public void returned(WorkflowEventArgs _workflowEventArgs)


{
SalesTable SalesTable;
;

ttsbegin;

select forupdate SalesTable


where SalesTable.RecId ==
_workflowEventArgs.parmWorkflowContext().parmRecId();

SalesTable.CreditLimitApprovalStatus =
SalesCreditLimitApprovalStatus::Rejected;

SalesTable.update();

ttscommit;

public void started(WorkflowEventArgs _workflowEventArgs)


{
SalesTable SalesTable;
;

ttsbegin;

select forupdate SalesTable


where SalesTable.RecId ==
_workflowEventArgs.parmWorkflowContext().parmRecId();

SalesTable.CreditLimitApprovalStatus =
SalesCreditLimitApprovalStatus::Submitted;

SalesTable.update();

ttscommit;

11-14 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 11: Workflow

Element Level Event Handlers


The interfaces at the workflow element level are as follows:

Event Description
WorkflowElementStartedEventHandler This event raises when the
task or approval starts.
For approvals, you can use
this event to transition the
workflow document state
from Submitted to
PendingApproval.
WorkflowElementCanceledEventHandler This event raises 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.
WorkflowElementCompletedEventHandler This event raises when the
task or approval is completed.
For approvals, you can use
this event to transition the
workflow document state
from PendingApproval to
Approved.
WorkflowElementReturnedEventHandler This event raises 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.
WorkflowElemChangeRequestedEventHandler This event raises 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.

Microsoft Official Training Materials for Microsoft Dynamics 11-15


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

Demonstration: Add Element Level Event Handlers


This demonstration shows you how to add element level event handlers.

1. Create a class and add the following ClassDeclaration and methods.


2. Find the SalesCreditLimitApproval Workflow Approval element
3. Expand the node and open the properties for the Reject node.
4. Set the EventHandler property to SalesCreditLimitElementHandler

class SalesCreditLimitElementHandler implements


WorkflowElementCompletedEventHandler,
WorkflowElementCanceledEventHandler,
WorkflowElementReturnedEventHandler,
WorkflowElemChangeRequestedEventHandler,
WorkflowElementStartedEventHandler

{
}

public void returned(WorkflowEventArgs _workflowEventArgs)


{
SalesTable SalesTable;
;

ttsbegin;

select forupdate SalesTable


where SalesTable.RecId ==
_workflowEventArgs.parmWorkflowContext().parmRecId();

SalesTable.CreditLimitApprovalStatus =
SalesCreditLimitApprovalStatus::Rejected;

SalesTable.update();

ttscommit;

11-16 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 11: Workflow

Configure a Workflow
Now that you have created a template and enabled it on a form, you can
configure it for use.

Demonstration: Configuring a Workflow


This demonstration shows you how to configure a workflow from the main
menu.

1. Open the main menu and select Accounts Receivable > Setup >
Workflow configurations.
2. Click New.
3. Select Sales credit limit approval and click Create configuration.
4. Enter "Credit limit approval" as the name.
5. Click Create Instruction, enter "Please approve" and then click OK.
6. Click the Details tab, expand SalesCreditLineApproval, and click on
Step 1.
7. Under Step details, click on Assignment tab.
8. Click the Choose button.
9. Select User based, enter a user in the Select users field and then click
OK.
10. Close the approval form.
11. On the configuration form click Set as active.
12. Click the Overview tab.
13. Click Set as default.

The workflow is now ready for use.

Demonstration: Test the Workflow


This demonstration shows you how to can test the workflow by creating a
timesheet

1. Select a customer from the customer table and set a credit limit.
2. Create a new sales order and create lines such that the balance of the
customer plus the total amount on the lines is greater than the credit
limit.
3. The workflow submit button and dialog should appear.
4. Click the submit button and enter a comment.
5. Open the AOT.
6. Expand the Forms node.
7. Find the form Tutorial_WorkFlowProcessor.
8. Right-click on this form and select Open.
9. Click Start.

Microsoft Official Training Materials for Microsoft Dynamics 11-17


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

10. When the form says that it has zero records in queue, click Stop and
go back to the sales table form.
11. Select Actions > History. You will see that the document is waiting
for approval by the person you assigned to approve it.
12. Logon as the user who should approve the sales order
13. Open the sales order form.
14. Click the workflow Actions button and select Approve.
15. Open the Tutorial_WorkflowProcessor form again and click Start,
wait for it to complete and click Stop.
16. The workflow has now been approved.

11-18 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 11: Workflow

Lab 11.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 11-19


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

boolean canPickingListBeUpdated()
{
......

ok = ok && salesTable.canPostCreditLimit();

return ok;

boolean canPackingslipBeUpdated()
{
......

ok = ok && salesTable.canPostCreditLimit();

return ok;

boolean canInvoiceBeUpdated()
{
......

ok = ok && salesTable.canPostCreditLimit();

return ok;

Code Walkthrough: Submitting a workflow


When a record is submitted to workflow, the main() method in the submit to
workflow class is called.

1. View the ProjTimeApprovalsSTWF.main() method created in the


Create A Submit to Workflow Class demonstration. The user is
prompted for a comment while submitting the workflow

workflowSubmitDialog =
WorkflowSubmitDialog::construct(args.caller().getActiveWork
flowConfiguration());
workflowSubmitDialog.run();

11-20 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 11: Workflow

The record is retrieved from the calling form and the recId is passed to the static
method Workflow::activateFromWorkflowTemplate(), which runs the submit
process.

ProjJournalTable = args.record();
// Get comments from the submit to workflow dialog.
_initialNote = workflowSubmitDialog.parmWorkflowComment();

try
{
ttsbegin;

// Activate the workflow.


_workflowCorrelationId =
Workflow::activateFromWorkflowTemplate(_workflowTemplateNam
e, _recId, _initialNote, NoYes::No);

2. View the method Workflow::activateFromWorkflowTemplate()

tableId =
Workflow::getDocumentTableId(_workflowTemplateName);
configTable =
Workflow::findWorkflowConfigToActivateForTemplate(_workflow
TemplateName, _recId, tableId);

The tableId that the workflow is to be performed on is retrieved from the query
specified in the workflow document class.

The workFlowContext class holds all the relevant data for the workflow
submission. The SysWorkFlowEventDispatcher class creates records that will be
read by the Workflow Processor class to determine which actions should be
executed in the next step of the workflow.

workflowContext =
WorkflowContext::newRootWorkflowContext(curext(), tableId,
_recId, correlationId);

try
{

SysWorkflowEventDispatcher::onWorkflowSubmit(workflowContex
t, _submittingUser, configTable.ConfigurationId,
_initialNote, _activatingFromWeb);
}

3. Return to the ProjTimeApprovalsSTWF.main() method

ProjJournalTable.WorkFlowState = true;

The journal is marked as submitted.

Microsoft Official Training Materials for Microsoft Dynamics 11-21


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

Code Walkthrough: Workflow Processor


All workflows are processed through a batch process. To view a simulation of
how the workflows are processed, use the form Tutorial_WorkflowProcessor.

1. View the form method doMessageProcessing on the form


Tutorial_WorkflowProcessor.

while select workflowWorkItemTable where


workflowWorkItemTable.Type ==
WorkflowWorkItemType::WorkItem &&
(workflowWorkItemTable.Status ==
WorkflowWorkItemStatus::Pending ||
workflowWorkItemTable.Status ==
WorkflowWorkItemStatus::Delegated) &&
workflowWorkItemTable.DueDateTime <
DateTimeUtil::getSystemDateTime()
{

WorkflowWorkItem::escalateWorkItem(SysWorkflowWorkItemConte
xt::newWorkflowWorkItemContextFromWorkItem(workflowWorkItem
Table));

cntWorkItems++;
}

All records due for processing are retrieved. The


WorkflowWorkItem::escalateWorkItem() is called

2. View the method WorkflowWorkItem::escalateWorkItem()

try
{
workItemId =
SysWorkflowEventDispatcher::onWorkItemEscalation(_workItemC
ontext);
}

3. View the method


SysWorkflowEventDispatcher::onWorkItemEscalation()

workItemTable =
WorkflowWorkItemTable::findPendingActivityInstanceId(_workI
temContext.parmWorkflowActivityInstanceKey().parmWorkflowAc
tivityInstanceId(), true);

The workItemTable record is retrieved. This is the next pending activity on the
workflow, based on the configuration.

11-22 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 11: Workflow

The action to be performed is examined

switch (stepTable.EscalationType)
{
case WorkflowEscalationType::Action:
workItemInstanceId = workItemTable.Id;

The next step is completed.

workItemTable.Status =
SysWorkflowEventDispatcher::completeWorkItem(
_workItemContext,
workItemTable,
stepTable.EscalationAction,
workItemTable.UserId,
workflowTable.Originator, // always set the auto-
escalate user to the workflow originator
"@SYS110277");

Microsoft Official Training Materials for Microsoft Dynamics 11-23


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

Lab 11.2 - Enable Resubmit


Scenario
Issac is required to ensure the workflow can be resubmitted after it has been
rejected.

Challenge Yourself!
When a workflow is rejected, it should be able to be resubmitted. Modify the
Submit to Workflow class so that it can resubmit the workflow after a rejection

Use the PurchReqWorkflow class as inspriration.

Step by Step

1. Create a new action menu item called SalesCreditLimitResubmit


2. Set the ObjectTypePropety to class, the Object property to
SalesCreditLimitSubmit and the Label property to Resubmit.
3. Modify the Main method on the SalesCreditLimitSubmit class and
add a new method Resubmit as follows:
4. On the SalesCreditLimitApproval approval element, set the
ResubmitMenuItem property to SalesCreditLimitResubmit

public static void main(Args _args)


{
SalesCreditLimitSubmit SalesCreditLimitSubmit
= new SalesCreditLimitSubmit();
;

if (_args.menuItemName() ==
menuitemactionstr(SalesCreditLimitSubmit))
{
SalesCreditLimitSubmit.submit(_args);
}
else
{
SalesCreditLimitSubmit.resubmit(_args);
}

void resubmit(Args args)


{
// Variable declaration.
recId _recId = args.record().RecId;
WorkflowCorrelationId _workflowCorrelationId;
// Hardcoded template name
WorkflowTemplateName _workflowTemplateName =
workflowtemplatestr(SalesCreditLimitApproval);

11-24 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 11: Workflow

// Initial note is the information that users enter


when they
// submit the document for workflow.
WorkflowComment _initialNote ="";
WorkflowWorkItemActionDialog
WorkflowWorkItemActionDialog;
SalesTable SalesTable;
;

// 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.");

Microsoft Official Training Materials for Microsoft Dynamics 11-25


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

ttscommit;
}

catch(exception::Error)
{
info("Error on workflow activation.");

}
}

args.caller().updateWorkFlowControls();
}

Summary
The workflow module is a highly configurable and flexible module. However, by
using Morph X and some standard code templates, it can be configured for any
part of the Microsoft Dynamics AX application.

This lesson explores some of the possibilities the workflow framework offers,
and explores some of the different ways it can be used to cover most workflow
requirements.

11-26 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 11: Workflow

Test Your Knowledge


1. Which application element is used to define to which module a workflow is
applicable?
( ) Workflow template
( ) Workflow category
( ) A field in the workflow configuration
( ) SalesTable

2. Which type of AOT element needs to be created to specify which tables will
be affected by a workflow?
( ) Extended data type
( ) Class
( ) Form
( ) Query

3. There are three types of providers that define what rules the workflow can
follow. What are they?
( ) Participant provider
( ) DueDate provider
( ) Hierarchy provider
( ) Internet provider

4. Which two properties on a form data source need to be modified to allow the
form to use a workflow?
( ) WorkflowTemplate
( ) WorkflowEnabled
( ) WorkflowDocument
( ) WorkflowDatasource

Microsoft Official Training Materials for Microsoft Dynamics 11-27


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

Quick Interaction: Lessons Learned


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

1.

2.

3.

11-28 Microsoft Official Training Materials for Microsoft Dynamics


Your use of this content is subject to your current services agreement
Chapter 11: Workflow

Solutions
Test Your Knowledge
1. Which application element is used to define to which module a workflow is
applicable?
( ) Workflow template
() Workflow category
( ) A field in the workflow configuration
( ) SalesTable

2. Which type of AOT element needs to be created to specify which tables will
be affected by a workflow?
( ) Extended data type
( ) Class
( ) Form
() Query

3. There are three types of providers that define what rules the workflow can
follow. What are they?
() Participant provider
() DueDate provider
() Hierarchy provider
( ) Internet provider

4. Which two properties on a form data source need to be modified to allow the
form to use a workflow?
( ) WorkflowTemplate
() WorkflowEnabled
( ) WorkflowDocument
() WorkflowDatasource

Microsoft Official Training Materials for Microsoft Dynamics 11-29


Your use of this content is subject to your current services agreement
Development IV in Microsoft Dynamics AX2009

11-30 Microsoft Official Training Materials for Microsoft Dynamics


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

Potrebbero piacerti anche