Professional Documents
Culture Documents
An easy to understand guide for creating new workflow templates in Microsoft Dynamics AX 2009
Created By
Sumit Loya
Table of Contents
1. Workflow Development ........................................................................................................................ 3 1.1 Introduction .................................................................................................................................. 3 Accounts Payable workflow templates ................................................................................. 3 Accounts Receivables workflow templates .......................................................................... 4 Expense Management workflow templates ......................................................................... 4 General Ledger workflow templates..................................................................................... 5
Development of Workflow Artifacts ............................................................................................. 5 Workflow Categories............................................................................................................. 5 Workflow Templates ............................................................................................................. 7 Workflow Documents ........................................................................................................... 8
Create a Query .................................................................................................................................. 8 Create a Class .................................................................................................................................... 9 1.2.4 Template Event Handlers and Cancel Managers ................................................................ 12
Create a Cancel Manager class ....................................................................................................... 12 Create Event Handler Class ............................................................................................................. 14 1.2.5 Workflow Tasks ................................................................................................................... 16
Create Task Event Handlers and Delegate Menu Item ................................................................... 17 Configuring Task Outcomes ............................................................................................................ 21 1.2.6 Workflow Approvals............................................................................................................ 24
Create Approval Event Handlers ..................................................................................................... 26 Configuring Approval Outcomes ..................................................................................................... 28 1.2.7 1.3 Attaching Tasks and Approvals to Workflow Templates .................................................... 32
Enabling Workflows on Forms .................................................................................................... 32 Table Modifications............................................................................................................. 32 Form Modifications ............................................................................................................. 37 Creation of a Submit Class .................................................................................................. 37
Attach Submit Action Menu Item to Workflow Template .............................................................. 44 Attach Resubmit Action Menu Item to Approvals and Tasks ......................................................... 45 1.4 In the End .................................................................................................................................... 46
Page | 2
1. Workflow Development
1.1 Introduction
A workflow is a depiction of a sequence of operations, declared as work of a person, a group of persons, an organization of staff, or one or more simple or complex mechanisms. Workflow may be seen as any abstraction of real work, segregated in work share, work split or other types of ordering. 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. In this article, I am going to discuss about how to develop new workflow templates that can be configured and implemented. By default Microsoft has provided following templates: 1.1.1 Accounts Payable workflow templates The Accounts Payable module provides the following workflow templates. Workflow template Project purchase requisition approval Purchase requisition approval Vendor bank remittance template Vendor disbursement template Vendor draw promissory note template Invoice approval journal template Vendor invoice recording template Invoice register template Vendor redraw promissory Use this template to Create project purchase requisition approval workflows.
Create approval workflows for invoice register journals. Create approval workflows for vendor redraw promissory note journals.
Page | 3
note template Vendor settle promissory note template Create approval workflows for vendor settle promissory note journals.
1.1.2 Accounts Receivables workflow templates The Accounts Receivables module provides the following workflow templates. Workflow template Customer bank remittance template Customer draw bill of exchange template Customer payment template Customer protest bill of exchange template Customer redraw bill of exchange template Customer settle bill of exchange template Use this template to Create approval workflows for customer bank remittance journals.
Create approval workflows for customer payment journals. Create approval workflows for customer protest bill of exchange journals. Create approval workflows for customer redraw bill of exchange journals. Create approval workflows for customer settle bill of exchange journals.
1.1.3 Expense Management workflow templates The Expense Management module provides the following workflow templates. Workflow template Cash advance request template Expense template Non-project line item workflow template Project expense line workflow template Use this template to Create approval workflows for cash advance requests in Expense Management. Create approval workflows for documents in Expense Management. Create approval workflows for non-project line items in Expense Management. Create approval workflows for project expense line items in Expense Management.
Page | 4
1.1.4 General Ledger workflow templates The General Ledger module provides the following workflow templates. Workflow template Allocations template Daily template Eliminations template Fixed assets budget template Post fixed assets template Use this template to Create approval workflows for allocations journals. Create approval workflows for daily journals. Create approval workflows for eliminations journals. Create approval workflows for fixed assets budget journals. Create approval workflows for fixed assets journals.
The sections that follow describe how we can create a new workflow.
Page | 5
workflow category as you can make use of available workflow categories. Following are the available categories in AX 2009: ExpenseManagement LedgerJournalWFApprovalCustomer LedgerJournalWFApprovalLedger LedgerJournalWFApprovalVendor PurchCategory For our example we will be using an existing category PurchCategory. In case these categories are not applicable to your scenario, you can go ahead and create new categories. To create new workflow categories follow the procedure below: 1. 2. 3. 4. 5. 6. Open AOT Expand the Workflow node Right-click on the Workflow Category node and select New Workflow Category. A new workflow category called WorkflowCategory1 will be created. Right-click on the newly created workflow category and select Properties You can specify the following:
Property Description A unique name for the category Name A meaningful label for the category Ex: Purchase order Label Description about the category Help Text The module to which the category is applicable Module See the following screen shot:
Page | 6
1.2.2 Workflow Templates The workflow template is an artifact that governs the creation of workflow configurations for implementing business policies. 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. In this section we will create a new workflow template for our purchase order approval scenario and attach it with the existing workflow category PurchCategory. Follow the procedure below to create a new workflow template: 1. 2. 3. 4. 5. 6. Open the AOT Expand the Workflow node Right-click on the Workflow Templates node and select New Workflow Template A new workflow template named WorkflowTemplate1 will be created Right-click on the newly created workflow template and select Properties Set the following properties Name property to PurchOrderApproval Value PurchOrderApproval Purchase order approval Use this template to create purchase order approval workflows. PurchCategory
Property Name Label HelpText Category 7. Save the newly created workflow template See the screen shot below:
Page | 7
1.2.3 Workflow Documents A workflow document specifies what data will be subjected to workflow approval process. It defines what data is affected and how many tables are involved in the workflow process. It can define one or more tables and all or selected fields on that table. This is done by using a query. A query defines what tables are used to determine that a workflow can be initiated. Use a class to bind that query to the workflow template. In this section we will create a query and a class that will bind the query with the workflow template. Do the following: Create a Query 1. Open the AOT 2. Right-click on the Query node and select New Query 3. Rename the query to PurchOrderDocument 4. Expand the Data Sources node in the newly created query 5. Right-click and select New Data Source 6. Right-click the newly created data source and select Properties 7. Set the following properties Property Value PurchTable Name PurchTable Table 8. Next expand the PurchTable data source and right-click on the Data Sources node inside the PurchTable data source 9. Select New Data Source and set the following Properties Property Value PurchLine Name PurchLine Table OuterJoin JoinMode Yes Relations 10. Next expand the PurchLine data source and right-click on the Data Sources node inside the PurchLine data source 11. Select New Data Source and set the following Properties Property Name Table JoinMode Relations 12. Save the Query See the screen shot below: Value InventDim InventDim InnerJoin Yes
Page | 8
Create a Class 1. In the AOT, right-click on the Classes node and select New Class 2. Create/Modify following methods in the new class and save it Method classDeclaration checkContext getQueryName parmTotalAmount Description A validation method that checks the table using this class is PurchTable only Returns the name of the query as a string Returns the total amount for the purchase order (A corresponding field will be created in the workflow configuration form for this method) Returns the total amount excluding taxes for the purchase order (A corresponding field will be created in the workflow configuration form for this method) Returns an instantiated instance of the class
parmTotalAmountExclTax
construct
Page | 9
class PurchOrderDocument extends WorkflowDocument { } protected void checkContext(tableId _tableId, RecId _recId) { ; if (_tableId != tablenum(PurchTable)) { // Workflow context table id reference: %1 is not valid throw error(strfmt("@SYS107963", _tableId)); } if (_recId == 0) { // Workflow context record id is zero. throw error(strfmt("@SYS107964")); } } QueryName getQueryName() { return querystr(PurchOrderDocument); } public static PurchOrderDocument construct() { return new PurchOrderDocument(); }
Page | 10
public PurchReqAmountApproval parmTotalAmount(CompanyId _companyId, tableId _tableId, RecId _recId) { AmountCur totalAmount; PurchTable purchTable; ; this.checkContext(_tableId, _recId); purchTable = PurchTable::findRecId(_recId); totalAmount = conpeek(PurchTotals::newPurchTable (purchTable).displayFields(), TradeTotals::posTotalAmount()); return Currency::mstAmount(totalAmount, CompanyInfo::find().CurrencyCode); } public PurchReqAmountApprovalExclTax parmTotalAmountExclTax(CompanyId _companyId, tableId _tableId, RecId _recId) { AmountCur totalAmount; PurchTable purchTable; PurchTotals totals; ; this.checkContext(_tableId, _recId); purchTable = PurchTable::findRecId(_recId); totals = PurchTotals::newPurchTable(purchTable); totalAmount = conpeek(totals.displayFields(), TradeTotals::posTotalAmount()) - conpeek(totals.displayFields(), TradeTotals::posTaxTotal()); return Currency::mstAmount(totalAmount, CompanyInfo::find().CurrencyCode); }
Page | 11
Attach the Document with the Workflow Template 1. Traverse to Workflow Workflow Templates PurchOrderApproval 2. Right-click and select Properties 3. For the property Document set value as PurchOrderDocument
1.2.4 Template Event Handlers and Cancel Managers Create a Cancel Manager class As the name suggests the cancel manager class is used for cancelling a workflow. This class should inherit from WorkflowCancelManager class. Do the following: 1. Open the AOT, traverse to Classes node and create a new class 2. Create following methods in the new class and save the class class PurchOrderWorkflowCancelManager extends WorkflowCancelManager { } public static PurchOrderWorkflowCancelManager construct() { return new PurchOrderWorkflowCancelManager(); } public static void main(Args _args) { PurchOrderWorkflowCancelManager workflowCancelManager = PurchOrderWorkflowCancelManager::construct(); ; workflowCancelManager.run(_args); }
Page | 12
//Updates the PurchTable workflow state to pending cancellation public void updateDocument(Args _args) { FormDataSource purchTable_ds; PurchTable purchTable; ; purchTable_ds = _args.record().dataSource(); if (purchTable_ds) { purchTable_ds.reread(); purchTable purchTable.State = purchTable_ds.cursor(); = PurchReqWorkflowState::PendingCancellation;
purchTable_ds.write(); purchTable_ds.refresh(); } }
After the class is ready create a new action type menu item for the class and attach it to the workflow template. Do the following: 1. Open the AOT and Traverse to Menu Items Action 2. Create new Menu Item and set the following properties Property Name Label HelpText Value PurchOrderCancel Cancel Select the action Cancel to cancel the purchase order after it has been submitted. Class PurchOrderWorkflowCancelManager Called from VendMisc
ObjectType Object RunOn SecurityKey 3. Save the menu item 4. Now traverse to Workflow Workflow Templates PurchOrderApproval 5. Right-click and select Properties 6. Set the property CancelMenuItem to PurchOrderCancel See the screen shot below:
Page | 13
Create Event Handler Class Event handler class handles logic to be implemented for the events started, completed and canceled. 1. Open the AOT, traverse to Classes node and create a new class 2. Create following methods in the new class and save the class public class PurchOrderWorkflowEventHandler implements WorkflowStartedEventHandler, WorkflowCanceledEventHandler, WorkflowCompletedEventHandler { } public void canceled(WorkflowEventArgs _workflowEventArgs) { ; PurchTable::setWorkflowState(_workflowEventArgs.parmWorkflowContext().parm RecId(), PurchReqWorkflowState::NotSubmitted); }
Page | 14
public void completed(WorkflowEventArgs _workflowEventArgs) { ; PurchTable::setWorkflowState(_workflowEventArgs.parmWorkflowContext().parm RecId(), PurchReqWorkflowState::WorkflowCompleted); } public void started(WorkflowEventArgs _workflowEventArgs) { ; PurchTable::setWorkflowState(_workflowEventArgs.parmWorkflowContext().parm RecId(), PurchReqWorkflowState::Submitted); } Note: You may get error as the method setWorkflowState is not available. Refer to Table modifications section to resolve this error. 3. Now traverse to Workflow Workflow Templates PurchOrderApproval 4. Right-click and select Properties 5. Set the properties StartedEventHandler, CompletedEventHandler, CanceledEventHandler to PurchOrderWorkflowEventHandler See the screen shot below:
Page | 15
1.2.5 Workflow Tasks Tasks are the most generic workflow elements. Tasks provide application specific behavior that you define in the AOT. Each task supports a single unit of work that is defined by one step. This means a workflow task can have only one step. A task does not have a fixed outcome. However, you can add any number of custom outcomes to the task, such as Completed or Skipped. Each task must have at least one outcome of type Complete and an action menu item for the task work item button to display in the user interface. This procedure describes how to create a new workflow task: 1. In the AOT, expand the Workflow node 2. Right-click the Tasks node and then select New Task. A new workflow task group displays under the Tasks node 3. Right-click the new workflow task and then click Properties 4. In the Properties sheet, set the following properties as required Property Description Value (For our scenario) Name The name that is used to reference the workflow task The label used for the workflow task in the user interface The description of the workflow task shown in the workflow configuration user interface The configuration key assigned to the task PurchOrderComplete
Label
Help Text
ConfigurationKe y Document
The workflow document enabled by the workflow task (This setting must match the Document property setting used in the workflow template for the approval.) The initial set of fields displayed in the unified work list dialog box. Select a field group from the root table specified in the Document property. The workflow task Document property setting must be set before you can select a field group The workflow event handler for the workflow started event
PurchOrderDocument
DocumentFieldP reviewGroup
Workflow (Set this property only after making changes to the top level table of the document. You can use any name but for demonstration purpose I am using Workflow)
StartedEventHa ndler
Page | 16
The workflow event handler for the workflow canceled event The provider for participant resolution of the task. Set this property if the HierarchyProvider property is not set WorkflowUserGroupParticipa ntProvider (This is the standard participant provider. You can extend this provider and create your own provider if needed.) WorkflowWorkCalendarDueD ateProvider (This is the standard participant provider. You can extend this provider and create your own provider if needed.) WorkflowLimitHierarchyProvi der (This is the standard participant provider. You can extend this provider and create your own provider if needed.)
DueDateProvide r
HierarchyProvi der
The provider for the hierarchy resolution. Set this property if the ParticipantProvider property is not set
DocumentWebM The Web menu item that opens the workflow enuItem document form DocumentMenuI The rich client menu item that opens the workflow tem document form PurchTable (This is the existing Display menu item for purchase order form. This means the workflow will be implemented on purchase order form.)
The Web menu item that resubmits the workflow document to the workflow approval The rich client menu item that resubmits the workflow document to the workflow approval The Web menu item that starts the delegate action on a work item The rich client menu item that starts the delegate action on a work item We will set this property after creating a class for submitting the workflow.
Create Task Event Handlers and Delegate Menu Item Now let us create some event handler classes for the task that will set the state of the workflow after each of the events that may happen like started or canceled. Do the following: Microsoft Dynamics AX 2009 Page | 17
1. Open the AOT, traverse to Classes node and create a new class 2. Create following methods in the new class and save the class class PurchOrderCompleteEventHandler implements WorkflowElementCompletedEventHandler, WorkflowElementCanceledEventHandler, WorkflowElementReturnedEventHandler, WorkflowElemChangeRequestedEventHandler, WorkflowElementStartedEventHandler { } public void canceled(WorkflowEventArgs _workflowEventArgs) { ; PurchTable::setWorkflowState(_workflowEventArgs.parmWorkflowContext().parm RecId(), PurchReqWorkflowState::NotSubmitted); } public void completed(WorkflowEventArgs _workflowEventArgs) { ; PurchTable::setWorkflowState(_workflowEventArgs.parmWorkflowContext().parm RecId(), PurchReqWorkflowState::WorkflowCompleted); } public void started(WorkflowEventArgs _workflowEventArgs) { ; PurchTable::setWorkflowState(_workflowEventArgs.parmWorkflowContext().parm RecId(), PurchReqWorkflowState::Submitted); } public void changeRequested(WorkflowElementEventArgs _workflowElementEventArgs) { ; PurchTable::setWorkflowState(_workflowElementEventArgs.parmWorkflowContext ().parmRecId(), PurchReqWorkflowState::ChangeRequest); } public void returned(WorkflowElementEventArgs _workflowElementEventArgs) { ; PurchTable::setWorkflowState(_workflowElementEventArgs.parmWorkflowContext ().parmRecId(), PurchReqWorkflowState::Returned); }
Page | 18
Note: You may get error as the method setWorkflowState is not available. Refer to Table modifications section to resolve this error. 3. Now traverse to Workflow Tasks PurchOrderComplete 4. Right-click and select Properties 5. Set the properties StartedEventHandler and CanceledEventHandler to PurchOrderCompleteEventHandler Now let us set the value for property DelegateMenuItem. For this property we need to create a new action menu item. Follow the procedure below: 1. 2. 3. 4. Open the AOT Traverse to Menu Items Action Right-click and select New Menu Item Set the following properties Value PurchOrderDelegate Delegate Delegate purchase order to another. Class WorkflowWorkItemActionManager (You can create your own class extending this class for specific features while delegating the workflow.) Called from VendMisc Edit
Page | 19
5. Set this menu item as value for DelegateMenuItem property of PurchOrderComplete task See the screen shot below showing the task we created in this section:
Page | 20
Configuring Task Outcomes Our scenario has three outcomes: Complete, Reject, Request change Use the standard WorkflowWorkItemActionManager class that acts as an engine for all task outcomes. You are not required to do anything but set the workflow to either Completed, Rejected or change may be requested, therefore call the same class from three different menu items. The three 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. Follow the procedure below to create three new menu items for 3 outcomes. Note: In our scenario there are three outcomes for this task. But the number of outcomes depends from scenario to scenario and thus the number of menu items to be created also varies from task to task.
1. Open the AOT 2. Expand Menu Items 3. Create three Action type menu items with following details Property Name Label HelpText ObjectType Object RunOn SecurityKey NeededAccessLevel Name Label HelpText ObjectType Object RunOn SecurityKey NeededAccessLevel Name Label HelpText ObjectType Object Microsoft Dynamics AX 2009 Value Task Outcome: Complete PurchOrderTaskComplete Complete Complete purchase order. Class WorkflowWorkItemActionManager Called from VendMisc Edit Task Outcome: Reject PurchOrderTaskReject Reject Reject purchase order. Class WorkflowWorkItemActionManager Called from VendMisc Edit Task Outcome: RequestChange PurchOrderTaskRequestChange Request change Send change request to submitter Class WorkflowWorkItemActionManager Page | 21
4. Once these menu items are created attach these menu items with task outcomes 5. Traverse to AOT Workflows Tasks PurchOrderComplete 6. Expand the Outcomes node and create three new outcomes with following properties
Page | 22
Property Name Type Enabled ActionMenuItem EventHandler Name Type Enabled ActionMenuItem EventHandler Name Type Enabled ActionMenuItem EventHandler
Value Task Outcome: Complete Complete Complete Yes PurchOrderTaskComplete PurchOrderCompleteEventHandler Task Outcome: Reject Reject Return Yes PurchOrderTaskReject PurchOrderCompleteEventHandler Task Outcome: RequestChange RequestChange RequestChange Yes PurchOrderTaskRequestChange PurchOrderCompleteEventHandler
Page | 23
1.2.6 Workflow Approvals Approvals are the specialized type of tasks. The main difference between tasks and approvals is that tasks can have only single step of work but approvals can have multiple steps and sub-workflows as well. Approvals are specialized workflow elements designed to support approval scenarios. Approvals have a set of fixed outcomes that the workflow supports. These outcomes are as follows: Approve - Outcome type Complete. Reject - Outcome type Return to the originator of the workflow. RequestChange - Outcome type ChangeRequested from the originator or another person in the workflow process. Deny - Outcome type Deny. The fixed outcomes listed earlier cannot be deleted in the AOT. However, you can set the workflow outcome Enabled property to No to disable the workflow outcome in the application. Each approval must have one outcome of type Complete and an action menu item for the approval work item button to display in the user interface. This procedure describes how to create a new workflow approval: 1. In the AOT, expand the Workflow node 2. Right-click the Approvals node and then select New Approval. A new workflow approval group displays under the Approvals node 3. Right-click the new workflow approval and then click Properties 4. In the Properties sheet, set the following properties as required Property Description Value (For our scenario) Name The name that is used to reference the workflow task PurchOrderApproval
Page | 24
Label
The label used for the workflow task in the user interface The description of the workflow task shown in the workflow configuration user interface The configuration key assigned to the task
Help Text
ConfigurationKe y Document
The workflow document enabled by the workflow task (This setting must match the Document property setting used in the workflow template for the approval.) The initial set of fields displayed in the unified work list dialog box. Select a field group from the root table specified in the Document property. The workflow task Document property setting must be set before you can select a field group The workflow event handler for the workflow started event
PurchOrderDocument
DocumentFieldP reviewGroup
Workflow (Set this property only after making changes to the top level table of the document. You can use any name but for demonstration purpose I am using Workflow) Refer the PurchReqComplete task in the AOT for more details. We will not be creating this event handler. Refer the PurchReqComplete task in the AOT for more details. We will not be creating this event handler. WorkflowUserGroupParticipa ntProvider (This is the standard participant provider. You can extend this provider and create your own provider if needed.) WorkflowWorkCalendarDueD ateProvider (This is the standard participant provider. You can extend this provider and create your own provider if needed.) WorkflowLimitHierarchyProvi der (This is the standard participant provider. You can extend this provider and create your own provider if
The provider for participant resolution of the task. Set this property if the HierarchyProvider property is not set
DueDateProvide r
HierarchyProvi der
The provider for the hierarchy resolution. Set this property if the ParticipantProvider property is not set
Page | 25
needed.) DocumentWebM The Web menu item that opens the workflow enuItem document form DocumentMenuI The rich client menu item that opens the workflow tem document form PurchTable (This is the existing Display menu item for purchase order form. This means the workflow will be implemented on purchase order form.)
The Web menu item that resubmits the workflow document to the workflow approval The rich client menu item that resubmits the workflow document to the workflow approval The Web menu item that starts the delegate action on a work item The rich client menu item that starts the delegate action on a work item PurchOrderDelegate (Created in the task section. Refer task section on how to create this menu item) We will set this property after creating a class for submitting the workflow.
Create Approval Event Handlers Now let us create an event handler class for the approval that will set the state of the workflow after each of the events that may happen like started or canceled. Do the following: 1. Open the AOT, traverse to Classes node and create a new class 2. Create following methods in the new class and save the class class PurchOrderApprovalEventHandler implements WorkflowElementCompletedEventHandler, WorkflowElementCanceledEventHandler, WorkflowElementReturnedEventHandler, WorkflowElemChangeRequestedEventHandler, WorkflowElementStartedEventHandler { } public void canceled(WorkflowEventArgs _workflowEventArgs) { ; PurchTable::setWorkflowState(_workflowEventArgs.parmWorkflowContext().parm RecId(), PurchReqWorkflowState::NotSubmitted); }
Page | 26
public void changeRequested(WorkflowElementEventArgs _workflowElementEventArgs) { ; PurchTable::setWorkflowState(_workflowElementEventArgs.parmWorkflowContext ().parmRecId(), PurchReqWorkflowState::ChangeRequest); } public void returned(WorkflowElementEventArgs _workflowElementEventArgs) { ; PurchTable::setWorkflowState(_workflowElementEventArgs.parmWorkflowContext ().parmRecId(), PurchReqWorkflowState::Returned); } public void started(WorkflowEventArgs _workflowEventArgs) { ; PurchTable::setWorkflowState(_workflowEventArgs.parmWorkflowContext().parm RecId(), PurchReqWorkflowState::Submitted); } public void completed(WorkflowEventArgs _workflowEventArgs) { ; PurchTable::setWorkflowState(_workflowEventArgs.parmWorkflowContext().parm RecId(), PurchReqWorkflowState::WorkflowCompleted); } Note: You may get error as the method setWorkflowState is not available. Refer to Table modifications section to resolve this error. 3. Now traverse to Workflow Tasks PurchOrderApproval 4. Right-click and select Properties 5. Set the properties StartedEventHandler and CanceledEventHandler to PurchOrderCompleteEventHandler See the screen shot below showing the properties of the approval we created in this section:
Page | 27
Configuring Approval Outcomes Use the standard WorkflowWorkItemActionManager class that acts as an engine for all approval outcomes. You are not required to do anything but set the workflow to either Approve, Reject or change may be requested, therefore call the same class from three different menu items. The three 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. Follow the procedure below to create three new menu items for 3 outcomes. Note: In our scenario there are three outcomes for this approval so we will be disabling the fourth outcome Deny.
1. Open the AOT 2. Expand Menu Items 3. Create three Action type menu items with following details
Page | 28
Property Name Label HelpText ObjectType Object RunOn SecurityKey NeededAccessLevel Name Label HelpText ObjectType Object RunOn SecurityKey NeededAccessLevel Name Label HelpText ObjectType Object RunOn SecurityKey NeededAccessLevel
Value Approval Outcome: Approve PurchOrderApprovalApprove Approve Approve purchase order. Class WorkflowWorkItemActionManager Called from VendMisc Edit Approval Outcome: Reject PurchOrderApprovalReject Reject Reject purchase order. Class WorkflowWorkItemActionManager Called from VendMisc Edit Approval Outcome: RequestChange PurchOrderApprovalRequestChange Request change Send change request to submitter Class WorkflowWorkItemActionManager Called from VendMisc Edit
Page | 29
4. Once these menu items are created attach these menu items with task outcomes 5. Traverse to AOT Workflow Approvals PurchOrderApproval 6. Expand the Outcomes node and create three new outcomes with following properties Property Enabled ActionMenuItem EventHandler Value Approval Outcome: Approve Yes PurchOrderApprovalApprove PurchOrderApprovalEventHandler Approval Outcome: Deny
Page | 30
Enabled ActionMenuItem EventHandler Enabled ActionMenuItem EventHandler Enabled ActionMenuItem EventHandler See the screen shots below:
No
Task Outcome: Reject Yes PurchOrderApprovalReject PurchOrderApprovalEventHandler Task Outcome: RequestChange Yes PurchOrderApprovalRequestChange PurchOrderApprovalEventHandler
Page | 31
1.2.7 Attaching Tasks and Approvals to Workflow Templates The approvals / tasks needs to be attached to the template. The following procedure explains how to attach an approval / task to a template. 1. 2. 3. 4. 5. Open the AOT Expand Workflow Workflow Templates PurchOrderApproval Open another AOT window Expand Workflow Tasks Find PurchOrderComplete task; drag it on to the Required Elements node of the PurchOrderApproval workflow template 6. Next Expand Workflow Approvals 7. Find PurchOrderApproval approval; drag it on to the Required Elements node of the PurchOrderApproval workflow template
Page | 32
o o o
requiredAction The action that needs to be taken next requiredActionDueDate The date by which the action needs to be taken setWorkflowState Sets the state field status
Note the fields SubmittedBy and SubmittedDateTime are not required to be created mandatorily but the State field is necessary to perform different actions based on the status of the workflow. Let us first go ahead and create the three fields. Do the following: 1. Open AOT 2. Traverse to Data Dictionary Tables PurchTable 3. Add three fields with following properties Property Value Field : SubmittedBy SubmittedBy Name No AllowEditOnCreate No AllowEdit PurchReqSubmittedBy ExtendedDataType Field : SubmittedDateTime SubmittedDateTime Name No AllowEditOnCreate No AllowEdit PurchReqSubmittedDateTime ExtendedDataType Field : SubmittedDateTime State Name No AllowEditOnCreate No AllowEdit PurchReqWorkflowState EnumType Next we create the methods mentioned earlier. Add the following methods along with this implementation: public boolean canSubmit() { PurchLine purchLine; boolean ret = true; ; ret = ret && (this.PurchStatus == PurchStatus::Received); ret = ret && (this.State == PurchReqWorkflowState::NotSubmitted); select firstonly RecId from purchLine where purchLine.PurchId == this.PurchId; ret = ret && (purchLine.RecId != 0); return ret; }
Page | 33
display PurchReqMostRecentComment mostRecentComment() { WorkflowTrackingCommentTable workflowTrackingCommentTable; WorkflowWorkItemTable workflowWorkItemTable; WorkflowTrackingTable workflowTrackingTable; DirPartyTable dirPartyTable; EmplTable emplTable; PurchReqMostRecentComment comment; EmplId emplId; UserInfo userInfo; Name name; ; select firstonly workflowWorkItemTable order by CreatedDateTime desc where workflowWorkItemTable.RefTableId == this.TableId && workflowWorkItemTable.RefRecId == this.RecId && workflowWorkItemTable.CompanyId == this.DataAreaId && workflowWorkItemTable.Status == WorkflowWorkItemStatus::Completed; workflowTrackingTable = Workflow::findLastTrackingRecordForWorkItem (workflowWorkItemTable); workflowTrackingCommentTable = WorkflowTrackingCommentTable::find (workflowTrackingTable.TrackingId); emplId = SysCompanyUserInfo::find(workflowTrackingTable.User).EmplId; if (emplId) { select firstonly Name from dirPartyTable exists join emplTable where dirPartyTable.PartyId == emplTable.PartyId && emplTable.EmplId == emplId; name = dirPartyTable.Name + ' (' + emplId + ') : '; } else { select firstonly userInfo where userInfo.Id == workflowTrackingTable.User; name = userInfo.Name + ' (' + workflowTrackingTable.User + ') : '; } comment = strfmt("%1",DateTimeUtil::applyTimeZoneOffset (workflowTrackingCommentTable.CreatedDateTime, DateTimeUtil::getUserPreferredTimeZone())) + ' '; comment += name; comment += workflowTrackingCommentTable.Comment; return comment; }
Page | 34
display PurchReqRequiredAction requiredAction() { WorkflowWorkItemTable workflowWorkItemTable; PurchReqRequiredAction text; ; select firstonly workflowWorkItemTable where workflowWorkItemTable.RefTableId == this.TableId && workflowWorkItemTable.RefRecId == this.RecId && workflowWorkItemTable.CompanyId == this.DataAreaId && workflowWorkItemTable.UserId == curuserid() && workflowWorkItemTable.Status == WorkflowWorkItemStatus::Pending; if (workflowWorkItemTable.RecId) { switch (this.State) { case PurchReqWorkflowState::ChangeRequest : text = "@SYS109048"; break; case PurchReqWorkflowState::PendingApproval : text = "@SYS109902"; break; case PurchReqWorkflowState::PendingComplete : text = "@SYS112063"; break; } } return text; } display PurchReqRequiredActionDueDateStr requiredActionDueDate() { WorkflowWorkItemTable workflowWorkItemTable; PurchReqRequiredActionDueDateStr dueDateStr; ; select firstonly DueDateTime from workflowWorkItemTable where workflowWorkItemTable.RefTableId == this.TableId && workflowWorkItemTable.RefRecId == this.RecId && workflowWorkItemTable.CompanyId == this.DataAreaId && workflowWorkItemTable.UserId == curuserid() && workflowWorkItemTable.Status == WorkflowWorkItemStatus::Pending; if (workflowWorkItemTable.DueDateTime != DateTimeUtil::minValue()) { dueDateStr = strfmt("%1",DateTimeUtil::applyTimeZoneOffset (workflowWorkItemTable.DueDateTime, DateTimeUtil::getUserPreferredTimeZone())); } return dueDateStr; }
Page | 35
static void setWorkflowState(RecId _purchTableRecId, PurchReqWorkflowState _purchReqWorkflowState) { PurchTable purchTable; ; ttsbegin; purchTable purchTable.State = PurchTable::findRecId(_purchTableRecId, true); = _purchReqWorkflowState;
switch (_purchReqWorkflowState) { case PurchReqWorkflowState::NotSubmitted: purchTable.SubmittedBy = ''; purchTable.SubmittedDateTime = DateTimeUtil::minValue(); break; } purchTable.update(); ttscommit; } Next we create a new field group. Do the following: 1. Open AOT 2. Traverse to Data Dictionary Tables PurchTable Field Groups 3. Add field group with Name = Workflow and Label = Workflow 4. Add following elements (fields) to the field group a. State b. SubmittedBy c. SubmittedDateTime d. requiredAction (method) e. requiredActionDueDate (method) f. mostRecentComment (method) Modify PurchTableType class This modification is only for our scenario. Traverse to Classes PurchTableType class mayInvoiceBeUpdated method and copy following code before statement return ok; ok = ok && (purchTable.State == PurchReqWorkflowState::WorkflowCompleted);
Page | 36
1.3.2 Form Modifications Workflow on the form is enabled using properties on the design node, and by overriding a form method. Do the following: 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. Open AOT Traverse to Forms node and search for form PurchTable Next expand PurchTable Designs node Right-click on Design node and select Properties Set property WorkflowEnabled to Yes Set property WorkflowDatasource to PurchTable Expand Design Group:Table Tab:TabHeader TabPage:TabHeaderOther From the Data Sources section right-click on PurchTable and select Open New Window Find field group Workflow; Drag it onto the TabPage:TabHeaderOther Now expand the Form Methods node, override method canSubmitToWorkflow and add following implementation
public boolean canSubmitToWorkflow() { ; return purchTable.canSubmit(); } 11. Save the form Now workflow has been enabled on the form. But we still cannot submit as we need to create a class that Submits/Re-Submits a record to workflow. 1.3.3 Creation of a Submit Class In order to submit a record to workflow processor it is necessary that we create a class that prompts user to enter some comment and processes the submission. This class is also responsible to re-submit the record for workflow processing in case it has been already submitted and a change has been requested. Follow the procedure below to create the class: 1. Open the AOT and create a new class 2. Create following methods with the implementation as shown Method classDeclaration dialogOk init parmMenuItemName parmPurchTable parmSubmit Microsoft Dynamics AX 2009 Description Opens a dialog for submitting a purchase order Initializes the submit class Set/Get method for Menu Item Name Set/Get method for PurchTable record Set / Get method for Submit variable Page | 37
Set / Get method for workflow comment Set / Get method for WorkflowConfigurationTable record Set / Get method for EPWorkflowControlContext class object Set / Get method for Workflow template name variable Processes and re-submits the record for workflow processing Activates purchase order workflow Instantiates and returns the class instance Submits a purchase order document and activates corresponding workflow
Here are the implementations for these methods: class PurchOrderWorkflow { PurchTable WorkflowConfigurationTable WorkflowComment boolean WorkflowWorkItemTable userId MenuItemName EPWorkflowControlContext WorkflowTemplateName }
public MenuItemName parmMenuItemName(MenuItemName _menuItemName = menuItemName) { ; menuItemName = _menuItemName; return menuItemName; } public PurchTable parmPurchTable(PurchTable _purchTable = purchTable) { ; purchTable = _purchTable; return purchTable; } public boolean parmSubmit(boolean _submit = submit) { ; submit = _submit; return submit; }
Page | 38
public WorkflowComment parmWorkflowComment(WorkflowComment _workflowComment = workflowComment) { ; workflowComment = _workflowComment; return workflowComment; } public WorkflowConfigurationTable parmWorkflowConfigurationTable(WorkflowConfigurationTable _workflowConfigurationTable = workflowConfigurationTable) { ; workflowConfigurationTable = _workflowConfigurationTable; return workflowConfigurationTable; } public EPWorkflowControlContext parmWorkflowControlContext(EPWorkflowControlContext _workflowControlContext = workflowControlContext) { ; workflowControlContext = _workflowControlContext; return workflowControlContext; } public WorkflowTemplateName parmWorkflowTemplateName(WorkflowTemplateName _workflowTemplateName = workflowTemplateName) { ; workflowTemplateName = _workflowTemplateName; return workflowTemplateName; } public WorkflowWorkItemTable parmWorkflowWorkItemtable(WorkflowWorkItemTable _workflowWorkItemTable = workflowWorkItemTable) { ; workflowWorkItemTable = _workflowWorkItemTable; return workflowWorkItemTable; }
Page | 39
if (menuItemName == menuitemactionstr(PurchOrderSubmitToWorkflow)) { workflowSubmitDialog = WorkflowSubmitDialog::construct(this.parmWorkflowConfigurationTable()); workflowSubmitDialog.run(); this.parmWorkflowComment(workflowSubmitDialog.parmWorkflowComment()); ok = workflowSubmitDialog.parmIsClosedOK(); } else if (menuItemName == menuitemactionstr(PurchOrderReSubmit)) { workflowWorkItemActionDialog = WorkflowWorkItemActionDialog::construct( workflowWorkItemTable, WorkflowWorkItemActionType::Resubmit, new MenuFunction(menuitemactionstr(PurchOrderReSubmit), MenuItemType::Action)); workflowWorkItemActionDialog.run(); this.parmWorkflowComment(workflowWorkItemActionDialog.parmWorkflowComment() ); ok = workflowWorkItemActionDialog.parmIsClosedOK(); userId = workflowWorkItemActionDialog.parmTargetUser(); } return ok; } public static PurchOrderWorkflow construct() { return new PurchOrderWorkflow(); }
Page | 40
Common _documentRecord, MenuItemName _menuItemName, WorkflowConfigurationTable _workflowConfigurationTable, WorkflowWorkItemTable _workflowWorkItemTable, EPWorkflowControlContext _workflowControlContext ) { this.parmPurchTable(_documentRecord); this.parmSubmit(_menuItemName == menuitemactionstr (PurchOrderSubmitToWorkflow)); this.parmMenuItemName(_menuItemName); if (_workflowControlContext) { this.parmWorkflowControlContext(_workflowControlContext); this.parmWorkflowWorkItemtable (_workflowControlContext.getActiveWorkflowWorkItem()); this.parmWorkflowComment(_workflowControlContext.getWorkflowComment()); this.parmWorkflowTemplateName (_workflowControlContext.getActiveWorkflowConfiguration().TemplateName); } else { this.parmWorkflowConfigurationTable(_workflowConfigurationTable); this.parmWorkflowWorkItemtable(_workflowWorkItemTable); this.parmWorkflowTemplateName (this.parmWorkflowConfigurationTable().TemplateName); } }
Page | 41
void reSubmit() { Object purchTable_ds; ; ttsbegin; WorkflowWorkItemActionManager::dispatchWorkItemAction( workflowWorkItemTable, workflowComment, userId, WorkflowWorkItemActionType::Resubmit, menuItemName, false); purchTable_ds purchTable.State = purchTable.dataSource(); = PurchReqWorkflowState::Submitted;
if (purchTable_ds) { purchTable_ds.write(); purchTable_ds.refresh(); } ttscommit; } void submit() { Object NoYes ; EP activatingFromWeb = this.parmWorkflowControlContext() == null ? NoYes::No : NoYes::Yes; Workflow::activateFromWorkflowTemplate( this.parmWorkflowTemplateName(),purchTable.RecId, this.parmWorkflowComment(),activatingFromWeb, curuserid()); purchTable_ds purchTable.SubmittedBy purchTable.SubmittedDateTime purchTable.State if (purchTable_ds) { purchTable_ds.write(); purchTable_ds.refresh(); } } = = = = purchTable.dataSource(); curuserid(); DateTimeUtil::utcNow(); PurchReqWorkflowState::Submitted;
purchTable_ds; activatingFromWeb;
Page | 42
public static void main(Args args) { PurchOrderWorkflow purchOrderWorkflow; PurchTable purchTable; ; purchTable = args.record(); purchOrderWorkflow = PurchOrderWorkflow::construct(); if (args.menuItemName() == menuitemactionstr(PurchOrderSubmitToWorkflow) || args.menuItemName() == menuitemactionstr(PurchOrderReSubmit)) { purchOrderWorkflow.init(args.record(), args.menuItemName(), args.caller().getActiveWorkflowConfiguration(), args.caller().getActiveWorkflowWorkItem(), null); } else { purchOrderWorkflow.init(args.record(), args.menuItemName(), null, null, args.caller()); } if (purchOrderWorkflow.dialogOk()) { if (purchOrderWorkflow.parmSubmit()) { purchOrderWorkflow.submit(); } else { purchOrderWorkflow.reSubmit(); } if (args.menuItemName() == menuitemactionstr(PurchOrderSubmitToWorkflow) || args.menuItemName() == menuitemactionstr(PurchOrderReSubmit)) args.caller().updateWorkflowControls(); } } 3. Once you create this class save it 4. Now go ahead and create two action type menu items for this class (The names of which you have used in the class created above (PurchOrderSubmitToWorkflow, PurchOrderReSubmit) 5. Traverse to Menu Items Action and create the following menu items
Page | 43
Property Name Label HelpText ObjectType Object RunOn SecurityKey NeededAccessLevel Name Label HelpText ObjectType Object RunOn SecurityKey NeededAccessLevel
Value Menu Item: Submit PurchOrderSubmitToWorkflow Submit Submit purchase order workflow Class PurchOrderWorkflow Called from VendMisc Edit Menu Item: Resubmit PurchOrderApprovalReject Submit Resubmit purchase order workflow Class PurchOrderWorkflow Called from VendMisc Edit
Next we will attach these menu items with workflow templates and approvals / tasks Attach Submit Action Menu Item to Workflow Template 1. Open the AOT 2. Traverse to Workflow Workflow Templates 3. Find PurchOrderApproval template and set the property SubmitToWorkflowMenuItem as PurchOrderSubmitToWorkflow 4. Save the template See the screen shot below:
Page | 44
Attach Resubmit Action Menu Item to Approvals and Tasks 1. Open the AOT 2. Traverse to Workflow Tasks 3. Find PurchOrderComplete task and set the property ResubmitMenuItem as PurchOrderReSubmit 4. Save the Task See the screen shot below:
5. Next Traverse to Workflow Approvals 6. Find PurchOrderApproval approval and set the property ResubmitMenuItem as PurchOrderReSubmit 7. Save the Approval See the screen shot below:
Page | 45
Now the new workflow template is created and ready for configuration. Unless you create one active configuration for this workflow, the Submit button will not be visible on the purchase order form. To know more about configuring workflows click here.
Page | 46