You are on page 1of 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 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 Your use of this content is subject to your current services agreement

iii

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 Your use of this content is subject to your current services agreement

0-1

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 Your use of this content is subject to your current services agreement

0-3

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 Your use of this content is subject to your current services agreement

1-1

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 Your use of this content is subject to your current services agreement

1-3

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 proforma 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 POSRegisterId POSPayId Extends SysGroup Num Label Register Id Payment Id Relation POSRegisterTable.RegisterId POSPayTable.PayId

Tables
Table Name POSRegisterTable RegisterId Description POSRegisterId Description Field Name Field Type Comment

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 POSRegisterTrans

Field Name HoldingAccount RegisterId SalesId PayId LineNum PayMode Amount Posted PostedEOD

Field Type LedgerAccount POSRegisterId SalesId POSPayId LineNum CustPayMode AmountMST NoYes NoYes

Comment

POSTable Form
Form Name POSTable Data Sources SalesTable SalesLine Design Header SalesTable.SalesId RegisterId OrderTotal Lines 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. Use Tab and Grid Use Tab Location Control Comments

Microsoft Official Training Materials for Microsoft Dynamics Your use of this content is subject to your current services agreement

1-5

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 POSRegisterTrans Data Sources POSRegisterTrans Design POSRegisterTrans.PayModeId POSRegisterTrans.AmountMST RegisterId, SalesId, LineNum, PayDate, PayTime, and UserId are populated by the system. Use Tab and Grid Location Control Comments

Reports
Element Name POSReciept Data Sources SalesTable SalesTable SalesLine POSRegisterTrans Design Header Inner join to SalesTable Inner join to SalesTable Location Control Comments

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 SalesTable.SalesId Date RegisterId

Comments

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 Your use of this content is subject to your current services agreement

1-7

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 Your use of this content is subject to your current services agreement

2-1

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 Your use of this content is subject to your current services agreement

2-3

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 Your use of this content is subject to your current services agreement

2-5

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 Your use of this content is subject to your current services agreement

2-7

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 Your use of this content is subject to your current services agreement

2-9

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 Your use of this content is subject to your current services agreement

2-11

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 Your use of this content is subject to your current services agreement

2-13

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 Your use of this content is subject to your current services agreement

3-1

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 Your use of this content is subject to your current services agreement

3-3

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 Your use of this content is subject to your current services agreement

3-5

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 Your use of this content is subject to your current services agreement

3-7

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 Your use of this content is subject to your current services agreement

3-9

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 Your use of this content is subject to your current services agreement

3-11

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 Your use of this content is subject to your current services agreement

3-13

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 Your use of this content is subject to your current services agreement

4-1

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 thirdparty 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 Your use of this content is subject to your current services agreement

4-3

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 Your use of this content is subject to your current services agreement

4-5

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 Your use of this content is subject to your current services agreement

4-7

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 user Description (String) Name of the Windows user to use for logging on to Microsoft Dynamics AX 2009. This parameter is optional. (String) The domain associated with the Windows user. This parameter is optional (NetworkCredential) A .NET network credential object. (String) The company to activate. This parameter is optional and is used to override the corresponding parameter in the configuration being used. (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. (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.

domain bcProxyCredentials company

language

objectServer

Microsoft Official Training Materials for Microsoft Dynamics Your use of this content is subject to your current services agreement

4-9

Development IV in Microsoft Dynamics AX2009

Parameter configuration

Description (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 Your use of this content is subject to your current services agreement

4-11

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 Your use of this content is subject to your current services agreement

4-13

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 Your use of this content is subject to your current services agreement

4-15

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 Your use of this content is subject to your current services agreement

4-17

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 Your use of this content is subject to your current services agreement

4-19

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 Your use of this content is subject to your current services agreement

4-21

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-onbehalf" 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 Your use of this content is subject to your current services agreement

5-1

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. 2. 3. 4. Right-click AOT > References. Select Add Reference. The Add Reference form appears. 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 Your use of this content is subject to your current services agreement

5-3

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 emails. 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 Your use of this content is subject to your current services agreement

5-5

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 Your use of this content is subject to your current services agreement

5-7

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 Your use of this content is subject to your current services agreement

5-9

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 Your use of this content is subject to your current services agreement

5-11

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 Your use of this content is subject to your current services agreement

5-13

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 QueryBuildDatasource QueryRun CustTrans ; super();

query = new Query(); qbd; queryRun; custTrans;

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 Your use of this content is subject to your current services agreement

5-15

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 URL _____ 2. .NET code namespace _____ 3. Reference name _____ 4. Service description a. Identifying text for the Web service. b. The location of the Web service. c. What 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. d. Where the generated proxy assembly and other files will reside.

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 Your use of this content is subject to your current services agreement

5-17

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 Your use of this content is subject to your current services agreement

5-19

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 Your use of this content is subject to your current services agreement

5-21

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 Your use of this content is subject to your current services agreement

5-23

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 URL d 2. .NET code namespace c 3. Reference name a 4. Service description a. Identifying text for the Web service. b. The location of the Web service. c. What 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. d. Where the generated proxy assembly and other files will reside.

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 Your use of this content is subject to your current services agreement

6-1

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 o Trans (LedgerVoucherTransObject) Trans

Voucher o o Trans 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 Your use of this content is subject to your current services agreement

6-3

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 Your use of this content is subject to your current services agreement

6-5

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 NumberSeq NumberSequenceCode 'Acco_18'; ledgerAccount // Petty cash ledgerAccount '606300'; // Office Supplies ledgerAccount '606500'; // Postage amountMST amountMST amountMST ; dimension; numSeq; NumberSequenceCode = accountNumPetty = '110180'; accountNumOffsetOffice = accountNumOffsetPostage = amountPetty = 55; amountOffice = 50; amountPostage = 5;

numSeq = NumberSeq::newGetNumFromCode(NumberSequenceCode); ttsbegin; //First Step - Create Voucher ledgerVoucher = ledgerVoucher::newLedgerPost(DetailSummary::Summary,

Microsoft Official Training Materials for Microsoft Dynamics Your use of this content is subject to your current services agreement

6-7

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, TableId 0); ReciID // // Amount 0, //

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, // TableId 0); // ReciID ledgerVoucherTransObject.parmTransTxt("Red stapler"); ledgerVoucher.addTrans(ledgerVoucherTransObject); // Amount 0,

//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 Your use of this content is subject to your current services agreement

6-9

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 Your use of this content is subject to your current services agreement

6-11

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 BankAccountID ledgerAccount amountCur ; 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); LedgerJournalNameId = 'GenJrn'; BankAccountID = 'EUR OPER'; offsetAccount = '601500'; amountCur = 102;

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 Your use of this content is subject to your current services agreement

6-13

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 Your use of this content is subject to your current services agreement

6-15

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 Your use of this content is subject to your current services agreement

6-17

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. 2. 3. 4. 5. 6. 7. 8. 9. Prompt for the RegisterId of the register to process. Prompt for the counted cash amount. Prompt for the cash or bank account and the difference account. Total up the amount entered during that day for that register. Calculate the difference between the counted amount and the posted amount. Post the counted amount to the bank account. Post the inverse of the posted amount to the register holding account. Post the difference to the register difference account. 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 Your use of this content is subject to your current services agreement

6-19

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 Your use of this content is subject to your current services agreement

6-21

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 Your use of this content is subject to your current services agreement

7-1

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 Your use of this content is subject to your current services agreement

7-3

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 Your use of this content is subject to your current services agreement

7-5

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 [number, voucher] = this.allocateNumAndVoucher(); = 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 Your use of this content is subject to your current services agreement

7-7

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 Your use of this content is subject to your current services agreement

7-9

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 Your use of this content is subject to your current services agreement

7-11

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 Your use of this content is subject to your current services agreement

7-13

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 accountCode

= idx mod 3; = 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 Your use of this content is subject to your current services agreement

7-15

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 price markup deliveryDays calendarDays actualPriceTable priceExist = = = = = = = priceDiscTable.priceUnit(); priceDiscTable.price(); priceDiscTable.markup(); priceDiscTable.DeliveryTime; priceDiscTable.CalendarDays; priceDiscTable.data(); 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 Your use of this content is subject to your current services agreement

7-17

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 Your use of this content is subject to your current services agreement

7-19

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

Microsoft Official Training Materials for Microsoft Dynamics Your use of this content is subject to your current services agreement

7-21

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 Your use of this content is subject to your current services agreement

7-23

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 Your use of this content is subject to your current services agreement

7-25

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 Your use of this content is subject to your current services agreement

8-1

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 Your use of this content is subject to your current services agreement

8-3

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 Your use of this content is subject to your current services agreement

8-5

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 Your use of this content is subject to your current services agreement

8-7

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 Your use of this content is subject to your current services agreement

8-9

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 Your use of this content is subject to your current services agreement

8-11

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 Your use of this content is subject to your current services agreement

8-13

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 Your use of this content is subject to your current services agreement

8-15

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 { InventOnhand InventOnhand; ; InventOnhand = InventOnhand::newParameters(_salesLine.ItemId, _salesLine.inventDim(), element.inventDimSetupObject().parmDimParmVisibleGrid()); return inventOnHand.availPhysical(); } _SalesLine)

Microsoft Official Training Materials for Microsoft Dynamics Your use of this content is subject to your current services agreement

8-17

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) InventMov_Sales(buffer); : return new

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 Your use of this content is subject to your current services agreement

8-19

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 Your use of this content is subject to your current services agreement

8-21

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 Your use of this content is subject to your current services agreement

8-23

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 Your use of this content is subject to your current services agreement

8-25

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 Your use of this content is subject to your current services agreement

8-27

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 Your use of this content is subject to your current services agreement

8-29

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 Your use of this content is subject to your current services agreement

8-31

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 subBOMs 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 Your use of this content is subject to your current services agreement

9-1

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 Your use of this content is subject to your current services agreement

9-3

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

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 subclass 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 Your use of this content is subject to your current services agreement

9-5

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 Your use of this content is subject to your current services agreement

9-7

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 Your use of this content is subject to your current services agreement

9-9

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 Your use of this content is subject to your current services agreement

9-11

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 Your use of this content is subject to your current services agreement

9-13

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 Your use of this content is subject to your current services agreement

9-15

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 Your use of this content is subject to your current services agreement

9-17

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 Your use of this content is subject to your current services agreement

9-19

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 Your use of this content is subject to your current services agreement

9-21

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 Your use of this content is subject to your current services agreement

10-1

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 subclasse 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 Your use of this content is subject to your current services agreement

10-3

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 Your use of this content is subject to your current services agreement

10-5

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 Your use of this content is subject to your current services agreement

10-7

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 subclass. 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 Your use of this content is subject to your current services agreement

10-9

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 Your use of this content is subject to your current services agreement

10-11

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 Your use of this content is subject to your current services agreement

10-13

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 Your use of this content is subject to your current services agreement

10-15

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 Your use of this content is subject to your current services agreement

11-1

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 Your use of this content is subject to your current services agreement

11-3

Development IV in Microsoft Dynamics AX2009


5. 6. 7. 8. Change the name property to SalesCreditLimitApproval. Change the label property to Sales credit limit approval. Change the category property to SalesCreditLimitApproval. 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. 2. 3. 4. 5. 6. Open the AOT. Right-click on the Query node and select New Query. Rename the query to SalesCreditLimitApproval. Expand the newly created query. Open another AOT window. 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. 2. 3. 4. 5. 6. 7. Open the AOT. Expand the workflow node. Right-click on approvals and select New Approval. Right-click on the newly created approval and select properties. Change the Name property to SalesCreditLimitApproval. Change the Document property to SalesCreditLimitApproval. Change the ParticipantProvider property to WorkflowUserGroupParticipantProvider.

Microsoft Official Training Materials for Microsoft Dynamics Your use of this content is subject to your current services agreement

11-5

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. 2. 3. 4. 5. Open the AOT. Expand Menu Items. Right-click on Action and select New Menu Item. Right-click on the newly created Action item and select Properties. 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. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. Change the Label property to Approve. Change the ObjectType property to Class. Change the Object property to WorkflowWorkItemActionManager. Right-click on the SalesCreditLimitApprove menu item and select Save. Right-click on Action and select New Menu Item. Right-click on the newly created Action item and select Properties. Change the Name property to SalesCreditLimitReject. Change the Label property to Reject. Change the ObjectType property to Class. Change the Object property to WorkflowWorkItemActionManager. Right-click on the SalesCreditLimitReject menu item and select Save. In the AOT, expand Workflow > Approvals > SalesCreditLimitApproval > Outcomes. Right-click on the Approve node and select Properties. Change the ActionMenuItem property to SalesCreditLimitApprove. Right-click on the Reject node and select Properties. Change the ActionMenuItem property to SalesCreditLimitReject. Right-click on the Deny node and select Properties. Change the Enabled property to No. Right-click on the RequestChange node and select Properties. Change the Enabled property to No Right-click on the SalesCreditLimitApproval approval node and select Save.

FIGURE 11.4

Microsoft Official Training Materials for Microsoft Dynamics Your use of this content is subject to your current services agreement

11-7

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. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. Open the AOT. Expand Data Dictionary. Right-click on Base Enums and select New Rename the new enum to SalesCreditLimitApprovalStatus Add four elements to the Enum called NotSubmitted, Submitted, Approved, Rejected. Expand Tables > SalesTable. Right-click on Fields and select New > Enum. Right-click on the newly created field and select Properties. Change the Name property to CreditLimitApprovalStatus. Change the EnumType property to SalesCreditLimitApprovalStatus. Right-click on the SalesTable node and select Save.

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. Open the AOT. Expand Tables > SalesTable. Create a new method and add the first method in the following code. Save the changes made to the table. Expand Forms > SalesTable > Designs. Right-click on the design node and select Properties. Change the WorkflowEnabled property to Yes. Change the WorkflowDatasource property to SalesTable. 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(); }

1. 2. 3. 4. 5. 6. 7. 8. 9.

Microsoft Official Training Materials for Microsoft Dynamics Your use of this content is subject to your current services agreement

11-9

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 Your use of this content is subject to your current services agreement

11-11

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 WorkflowStartedEventHandler WorkflowCompletedEventHandler Description This event raises when the workflow instance starts. This event raises when the workflow instance ends after it is completed. This event raises when the workflow instance ends after it is canceled. Use this event handler to perform any clean up operations needed. 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.

WorkflowCanceledEventHandler

WorkflowConfigDataChangeEventHandler

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 WorkflowStartedEventHandler, WorkflowCanceledEventHandler, WorkflowCompletedEventHandler { } implements

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 Your use of this content is subject to your current services agreement

11-13

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 WorkflowElementStartedEventHandler Description 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. 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. 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. 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. 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.

WorkflowElementCanceledEventHandler

WorkflowElementCompletedEventHandler

WorkflowElementReturnedEventHandler

WorkflowElemChangeRequestedEventHandler

Microsoft Official Training Materials for Microsoft Dynamics Your use of this content is subject to your current services agreement

11-15

Development IV in Microsoft Dynamics AX2009


Demonstration: Add Element Level Event Handlers
This demonstration shows you how to add element level event handlers. 1. 2. 3. 4. Create a class and add the following ClassDeclaration and methods. Find the SalesCreditLimitApproval Workflow Approval element Expand the node and open the properties for the Reject node. 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 Your use of this content is subject to your current services agreement

11-17

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 Your use of this content is subject to your current services agreement

11-19

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 Your use of this content is subject to your current services agreement

11-21

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 autoescalate user to the workflow originator "@SYS110277");

Microsoft Official Training Materials for Microsoft Dynamics Your use of this content is subject to your current services agreement

11-23

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 Your use of this content is subject to your current services agreement

11-25

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 Your use of this content is subject to your current services agreement

11-27

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 Your use of this content is subject to your current services agreement

11-29

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

You might also like