0% found this document useful (0 votes)
102 views

AcumaticaERP WorkflowAPI

Uploaded by

crudbug
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
102 views

AcumaticaERP WorkflowAPI

Uploaded by

crudbug
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 108

Developer Guide

Workflow API Guide


2022 R2
Contents | 2

Contents
Copyright...............................................................................................................................................5
Getting Started with Workflows: General Information............................................................................... 6
Company Story and Customization Description........................................................................................ 7
Business Process Overview............................................................................................................................... 8
Customization Description.............................................................................................................................. 11
Preparing an Instance for Workflow Customization................................................................................. 15
Instance Preparation: General Information....................................................................................................15
Instance Preparation: To Deploy a Test Instance........................................................................................... 15
Instance Preparation: To Enable Workflow Validation.................................................................................. 17
Instance Preparation: To Configure the Instance...........................................................................................17
Preparing a Screen Configuration.......................................................................................................... 20
Screen Configuration: General Information................................................................................................... 20
Screen Configuration: To Prepare a Screen Configuration Without a Predefined Workflow.......................22
Step 1: Defining the Workflow Class......................................................................................................22
Step 2: Defining the Set of States of the Workflow...............................................................................23
Step 3: Overriding the Configure Method............................................................................................. 25
Screen Configuration: To Prepare a Screen Configuration with a Predefined Workflow.............................26
Screen Configuration: Restrictions of the Configure Method....................................................................... 27
Defining Conditions.............................................................................................................................. 29
Conditions: General Information.................................................................................................................... 29
Configuring Field States ....................................................................................................................... 31
Field States: General Information...................................................................................................................31
Field States: Levels of Configuration.............................................................................................................. 32
Implementing Workflow Actions............................................................................................................ 34
Workflow Actions: General Information......................................................................................................... 34
Workflow Actions: Configuration of Actions.................................................................................................. 36
Workflow Actions: Categories of the More Menu........................................................................................... 40
Workflow Actions: To Implement a Simple Action........................................................................................ 41
Step 1: Implementing the Action in the Graph..................................................................................... 42
Step 2: Defining a Category for the Action............................................................................................43
Step 3: Adding the Action to the Workflow...........................................................................................44
Workflow Actions: To Configure the Conditional Appearance of the Action................................................ 44
Step 1: Defining the Action in the Graph...............................................................................................45
Step 2: Configuring the Conditional Appearance of the Action........................................................... 47
Contents | 3

Step 3: Testing the Create Invoice Action..............................................................................................47


Workflow Actions: To Implement an Action with Field Assignments........................................................... 49
Step 1: Adding and Configuring the Action...........................................................................................49
Step 2: Adding a State and a Transition................................................................................................ 50
Step 3: Testing the Field Assignment.................................................................................................... 51
Defining Workflow States...................................................................................................................... 52
Workflow States: General Information........................................................................................................... 52
Workflow States: Configuring Field States..................................................................................................... 53
Workflow States: Configuring Action States...................................................................................................55
Workflow States: To Define a Workflow State................................................................................................56
Implementing Transitions..................................................................................................................... 59
Transitions: General Information....................................................................................................................59
Transitions: To Implement a Simple Transition............................................................................................. 60
Step 1: Defining a Transition..................................................................................................................61
Step 2: Testing the Transition................................................................................................................ 61
Transitions: Transition Groups........................................................................................................................63
Transitions: To Implement a Group of Transitions........................................................................................ 63
Step 1: Adding a Condition Pack........................................................................................................... 64
Step 2: Adding the Pending Payment State (Self-Guided Exercise)..................................................... 65
Step 3: Grouping Transitions and Adding Conditions to Transitions...................................................65
Step 4: Testing Transitions With Conditions......................................................................................... 66
Implementing Dialog Boxes................................................................................................................... 68
Dialog Boxes: General Information.................................................................................................................68
DialogBox: Configuration of the Layout......................................................................................................... 71
Dialog Boxes: To Implement a Transition with a Dialog Box.........................................................................71
Step 1: Defining a Dialog Box.................................................................................................................72
Step 2: Defining the Action That Opens the Dialog Box....................................................................... 73
Step 3: Defining the State and the Transition....................................................................................... 73
Step 4: Testing the Assign Action...........................................................................................................73
Implementing Workflow Events............................................................................................................. 76
Workflow Events: General Information.......................................................................................................... 76
Workflow Events: To Use an Existing Event....................................................................................................78
Step 1: Exploring the Acumatica ERP Source Code.............................................................................. 79
Step 2: Preparing a Project for Debugging............................................................................................80
Step 3: Exploring and Debugging the Code.......................................................................................... 82
Step 4: Defining the Paid State (Self-Guided Exercise)......................................................................... 84
Contents | 4

Step 5: Defining the Event Handler....................................................................................................... 84


Step 6: Overriding the Persist Method...................................................................................................86
Step 7: Testing the Transition................................................................................................................ 87
Workflow Events: To Create a New Event.......................................................................................................88
Step 1: Creating a Custom Field............................................................................................................ 89
Step 2: Deriving the Value of the Field.................................................................................................. 91
Step 3: Exploring the Acumatica ERP Source Code.............................................................................. 92
Step 4: Declaring the Event....................................................................................................................93
Step 5: Firing the Event.......................................................................................................................... 94
Step 6: Testing the Transition................................................................................................................ 96
Customizing a Predefined Workflow.......................................................................................................98
Workflow Customization: General Information............................................................................................. 98
Workflow Customization: To Add an Action to a Workflow...........................................................................99
Step 1: Adding a Graph Action...............................................................................................................99
Step 2: Adding a Workflow Extension..................................................................................................100
Step 3: Defining a Workflow Action..................................................................................................... 101
Step 4: Testing the New Action............................................................................................................ 102
Implementing Workflow Sequences..................................................................................................... 104
Composite States: General Info.................................................................................................................... 104
Composite State: To Update a Composite State..........................................................................................107
Copyright | 5

Copyright

© 2022 Acumatica, Inc.

ALL RIGHTS RESERVED.

No part of this document may be reproduced, copied, or transmitted without the express prior consent of
Acumatica, Inc.
3933 Lake Washington Blvd NE, # 350, Kirkland, WA 98033

Restricted Rights
The product is provided with restricted rights. Use, duplication, or disclosure by the United States Government is
subject to restrictions as set forth in the applicable License and Services Agreement and in subparagraph (c)(1)(ii)
of the Rights in Technical Data and Computer Soware clause at DFARS 252.227-7013 or subparagraphs (c)(1) and
(c)(2) of the Commercial Computer Soware-Restricted Rights at 48 CFR 52.227-19, as applicable.

Disclaimer
Acumatica, Inc. makes no representations or warranties with respect to the contents or use of this document, and
specifically disclaims any express or implied warranties of merchantability or fitness for any particular purpose.
Further, Acumatica, Inc. reserves the right to revise this document and make changes in its content at any time,
without obligation to notify any person or entity of such revisions or changes.

Trademarks
Acumatica is a registered trademark of Acumatica, Inc. HubSpot is a registered trademark of HubSpot, Inc.
Microso Exchange and Microso Exchange Server are registered trademarks of Microso Corporation. All other
product names and services herein are trademarks or service marks of their respective companies.

Soware Version: 2022 R2


Last Updated: 01/30/2023
Getting Started with Workflows: General Information | 6

Getting Started with Workflows: General Information


A workflow is a depiction of the ways the state of a record created on a particular form changes as a result
of specific user interactions on the form and other events. You can use Workflow API to design and tailor the
workflows of forms to meet your company's needs.

Learning Objectives
In this chapterIn this lesson, you will learn what workflows are and how you can use them.

Applicable Scenarios
You might need to create or customize workflows if the movement of records in the company follows an
established sequence of operations. By customizing predefined workflows to represent this sequence, you can
automate the company’s processes, which can speed the processing of records. You may want to instead develop
custom workflows if the predefined workflows are not similar enough to the way the entity is processed in your
company or the form does not already have a workflow.

States and Transitions


A workflow can be described as a state machine, with transitions showing the movement of the record through its
processing in the system as the corresponding work is performed in the company. For example, a workflow can
involve the changing of the status of an opportunity based on user interactions on the Opportunities (CR304000)
form to reflect the progress made with the potential customer that represents an opportunity.
For details on defining states, see Defining Workflow States. For details on defining transitions, see Implementing
Transitions.

Actions and Fields


You can configure action and field properties for a form and its workflows at the same time, depending on the
conditions specified for the form when a record has any state or has a particular state. For details on actions and
their properties, see Implementing Workflow Actions.

Conditions
Conditions can be used in the properties of actions and fields at the form level (that is, for all workflows of a
particular form). At the workflow level, conditions can be used to determine whether transitions are performed.
Also, conditions can be used to determine whether actions are performed automatically. For details on defining
and using conditions, see Defining Conditions.

Customization and Creation of Workflows


You can customize predefined workflows for forms that have them; the resulting customized workflows are
sometimes referred to as inherited because they inherit all modifications of the predefined workflow. You can
also create custom workflows that are not based on existing workflows. For details on customizing a predefined
workflow, see Customizing a Predefined Workflow.
You can define a single workflow for the whole form or multiple workflows, one for a record with each value of
the specific field that identifies the state, such as the record type. You can configure the settings—such as field
properties, conditions, and actions—for the whole form. For each of the workflows of the form, you can specify the
properties of actions and fields for every state the record can have. These properties determine the appearance of
the form when the record has a particular state.
Company Story and Customization Description | 7

Company Story and Customization Description


In the activities of this guidetraining course, you will develop a customization project to support the cell phone
repair shop of the Smart Fix company. Parts of this customization were developed in the T200 Maintenance Forms,
T210 Customized Forms and Master-Detail Relationship, and T220 Data Entry and Setup Forms training courses. The
activities of this guide use the customization project that you get as a result of completing these courses. To deploy
this project without completing these courses, follow the instructions in Instance Preparation: To Deploy a Test
Instance.

The T200 Maintenance Forms training course describes the creation of two simple maintenance forms:
• Repair Services (RS201000): The Smart Fix company uses this form to manage the list of the repair services
the company provides.
• Serviced Devices (RS202000): On this form, the Smart Fix company manages the list of the devices serviced
by the company.
The T210 Customized Forms and Master-Detail Relationship coursecovers the creation of another maintenance form,
Services and Prices (RS203000), and the customization of the Stock Items (IN202500) form of Acumatica ERP. The
Services and Prices form provides users with the ability to define and maintain the price for each repair service
the company provides. The Stock Items form has been customized to give users the ability to mark particular stock
items as repair items—that is, items that are used for repair services.
In the T220 Data Entry and Setup Forms course detailed the creation of the Repair Work Orders (RS301000)
data entry form, which is used to create and manage work orders for repairs. It also covers the creation of the
Repair Work Order Preferences (RS101000) setup form, on which an administrative user specifies the company’s
preferences for the repair work orders.
In the activities of this guidetraining course, you will implement a workflow on the Repair Work Orders form. The
workflow will change the state of a record created on the form—that is, the status of the repair work order and
the related properties of the fields and actions on the form. Also, you will customize the workflow on the Invoices
(SO303000) form by doing the following:
• Add an action that opens the Repair Work Orders form. You will make the action available in one status of an
invoice.
• Add the new state, Postponed, to a composite state of the workflow, a transition from the Postponed state,
and an action which triggers the transition

Types of Repair Work Orders


A repair work order may be created for the following types of services, which are defined on the Repair Services
(RS201000) form:
• Battery Replacement
• Liquid Damage
• Screen Repair
As specified on the Repair Services form, the Battery Replacement service does not require a prepayment. The total
cost of the order must be paid in full aer the repair is completed.
The Liquid Damage service requires prepayment. The percent of the prepayment is specified on the Repair Work
Order Preferences (RS101000) form.
In the activities of this guidetraining course, you will implement the changing of the status for the Battery
Replacement and Liquid Damage services.

This guidetraining course does not cover the implementation of the changing of the status for the
Screen Repair service. You can do this as a self-guided exercise.
Company Story and Customization Description | 8

Business Process Overview

The workflow to be used for repair work orders created on the Repair Work Orders (RS301000) form will differ
slightly depending on the particular service being performed, which can be Battery Replacement, Liquid
Damage, or Screen Repair. This topic describes how the workflow will work for a repair work order for the Battery
Replacement and Liquid Damage service.

The Battery Replacement Service


When a user creates an order for the Battery Replacement service on the Repair Work Orders (RS301000) form,
the order will have the On Hold status. To change the order’s status to Ready for Assignment, a user will click the
Remove Hold button on the form toolbar. Then a user will click the Assign button and specify the assignee in the
Select Assignee dialog box. The order’s status will be changed to Assigned.
When work on the order is completed, a user will complete the assigned order by clicking the Complete button
on the Labor tab, which will give the order the Completed status. Aer that, a user creates an invoice for the order.
When the invoice is fully paid, the system will assign the Paid status to the repair work order.
Thus, based on this workflow, a repair work order for the Battery Replacement service will be able to have the
following statuses:
• On Hold
A newly created order has this status by default.
• Ready for Assignment
This status will be assigned when a user clicks the Remove Hold button.
• Assigned
This status will be assigned when a user clicks the Assign button.
• Completed
This status will be assigned when a user clicks the Complete button on the Labor tab.
• Paid
The system will assign this status to the order when the order is fully paid.
The following diagram shows the planned workflow for a repair work order for the Battery Replacement service.
The system actions that have been or will be implemented in the PhoneRepairShop customization project are
shown in the middle column.
Company Story and Customization Description | 9

The Liquid Damage Service


When a user creates a repair work order for the Liquid Damage service on the Repair Work Orders (RS301000) form,
the order has the On Hold status. To cause the system to change the order’s status to Pending Payment, a user will
click the Remove Hold button on the form toolbar. This status indicates that a user needs to create, release, and
apply a prepayment.

An order for the Liquid Damage service requires prepayment if the Requires Prepayment check box
is selected on the Repair Services (RS201000) form. The required percent to be prepaid has been
specified in the Prepayment Percent box of the Repair Work Order Preferences (RS101000) form.
Company Story and Customization Description | 10

To create and apply the prepayment, a user should first create an invoice for the repair work order, and then create,
release, and apply the prepayment on the Payments and Applications (AR302000) form; the repair work order’s
status will be changed to Ready for Assignment. Then a user will assign the order to an employee, and the order’s
status will be changed to Assigned.
When work on the order is completed, a user can complete the assigned order by clicking the Complete button on
the Labor tab, which will cause the order to be assigned the Completed status. Aer that, a user will apply the rest
of the payment to the invoice created for the order. When the invoice is fully paid, the system will assign the Paid
status to the repair work order.
Thus, based on this workflow, a repair work order for the Liquid Damage service will be able to have the following
statuses:
• On Hold
A newly created order has this status by default.
• Pending Payment
This status will be assigned when a user clicks the Remove Hold button on the form toolbar.
• Ready for Assignment
This status will be assigned to an order when a prepayment for it has been created, released, and applied,
and the prepayment percent is greater than or equal to the required prepayment percent.
• Assigned
This status will be assigned when a user clicks the Assign button.
• Completed
This status will be assigned when a user clicks the Complete button on the Labor tab.
• Paid
The system will assign this status to the order when the order is fully paid. The payment will be applied to
the same invoice to which the prepayment was applied.
The following diagram shows the planned workflow for a repair work order for the Liquid Damage service. The
system actions that have been or will be implemented in the PhoneRepairShop customization project are shown in
the middle column.
Company Story and Customization Description | 11

Customization Description

This topic describes the changes that will be implemented as part of the customization for the Smart Fix company.

Custom Workflow for the Repair Work Orders Form


A review of the company's business processes has illustrated that the workflow for both services (one of which
requires prepayment and one of which does not) can be united into a single workflow, which is shown in the
following diagram. In the activities of this guidetraining course, you will implement this workflow for the Repair
Work Orders (RS301000) form.
Company Story and Customization Description | 12

Figure: The workflow on the Repair Work Orders form

In the workflow, you will implement the following items:


• The following states of the workflow, which correspond to the noted statuses of a repair work order:
• WorkOrderStatusConstants.OnHold (On Hold)
• WorkOrderStatusConstants.PendingPayment (Pending Payment)
• WorkOrderStatusConstants.ReadyForAssignment (Ready for Assignment)
• WorkOrderStatusConstants.Assigned (Assigned)
• WorkOrderStatusConstants.Completed (Completed)
• WorkOrderStatusConstants.Paid (Paid)
• The following actions, which trigger the transitions of a repair work order and correspond to the noted
command and button on the UI:
• ReleaseFromHold (Remove Hold), which triggers a transition from the OnHold state to the
PendingPayment or ReadyForAssignment state
• Assign (Assign), which triggers a transition from the ReadyForAssignment state to the Assigned
state
• Complete (Complete), which triggers a transition from the Assigned state to the Completed status
You will also define the CreateInvoice action, which generates an invoice for the repair work order.
• The transitions between states of a repair work order
• A dialog box that is shown when a user clicks the Assign action
• The following event handlers, which trigger transitions for a repair work order:
• OnInvoiceGotPrepaid, which triggers a transition from the PendingPayment state to the
ReadyForAssignment state
• OnCloseDocument, which triggers a transition from the Completed state to the Paid state
• The conditions that determine which state a record created on the form should transition to from the
OnHold state
Company Story and Customization Description | 13

You will also customize an existing Acumatica ERP graph to implement a transition in your custom workflow.
The resulting Repair Work Orders form will appear as shown in the following screenshot.

Figure: The resulting Repair Work Orders form

Customized Workflow for the Invoices Form


To continue working on a repair work order aer an invoice has been prepaid, a user needs an action that opens
the corresponding repair work order from the Invoices (SO303000) form. Accordingly, you need to customize the
predefined workflow of that form. The command corresponding to the action should be named View Repair
Work Order and should be displayed on the More menu under the Repair Work Orders category, as shown in the
following screenshot.
Company Story and Customization Description | 14

Figure: The View Repair Work Order action

To implement this task, you will extend the graph to define the action and customize the predefined workflow of
the Invoices form. In the customized workflow, you will do the following:
• Define the workflow action
• Define the action category
• Add the action to the workflow state
Preparing an Instance for Workflow Customization | 15

Preparing an Instance for Workflow Customization


In this chapterIn this lesson, you will learn how to configure an instance of Acumatica ERP for testing the activities
of this guidetraining course.

Instance Preparation: General Information

In this chapter, you will learn how to deploy an Acumatica ERP test instance that contains custom and customized
forms. You can use this instance to complete a training course or test a scenario described in this guide. You can use
this instance to complete the training course.

Learning Objectives
In this chapter, you will learn how to do the following:
• Prepare the environment
• Deploy an Acumatica ERP test instance

Applicable Scenarios
You deploy an Acumatica ERP test instance by using the instructions in this chapter in the following cases:
• You want to test the activities described in this guide.
• You need to complete a training course.
You deploy an Acumatica ERP test instance by using the instructions in this chapter, if you need to complete the
training course.

Deploying a Test Instance


You can use the Acumatica ERP Configuration Wizard to deploy instances based on predefined datasets. It also
makes it possible to deploy test instances that contain custom and customized forms.
In the Acumatica ERP Configuration Wizard, you click Deploy New Application Instance for T-series Developer
Courses and select the training course in the list as specified in the Test Instance: To Deploy an Instance activity.
The instance that you prepare contains the data that is required to complete the activities of this guide. The
instance can also include the published customization project.

Instance Preparation: To Deploy a Test Instance

The following activity will walk you through the process of preparing and deploying an Acumatica ERP instance that
you can use to perform the steps in other chapters of this guidelessons of this training course.

Story
Suppose that you need to perform customization tasks for the Smart Fix company, as described in Company Story
and Customization Description, and complete the activities described in other chapters of this guidelessons of this
training course. You need to deploy an instance of Acumatica ERP with the PhoneRepairShop customization project
published.
Preparing an Instance for Workflow Customization | 16

Process Overview
In this activity, you will install tools that will help you to perform customization tasks and then deploy the instance
of Acumatica ERP with the dataset from the T220 Data Entry and Setup Forms course.

Step 1: Preparing the Environment


To prepare the environment, do the following:
1. Make sure the environment that you are going to use conforms to the System Requirements for Acumatica
ERP 2022 R2.
2. Make sure that the Web Server (IIS) features that are listed in Configuring Web Server (IIS) Features are
turned on.
3. Install the Acuminator extension for Visual Studio.
4. Clone or download the customization project and the source code of the extension library from the Help-
and-Training-Examples repository in Acumatica GitHub to a folder on your computer.
5. Install Acumatica ERP. On the Main Soware Configuration page of the installation program, select the
Install Acumatica ERP and Install Debugger Tools check boxes.

If you have already installed Acumatica ERP without debugger tools, you should remove
Acumatica ERP and install it again with the Install Debugger Tools check box selected. The
reinstallation of Acumatica ERP does not affect existing Acumatica ERP instances. For details,
see To Install the Acumatica ERP Tools.

Step 2: Deploying the Instance


To perform customization for the Smart Fix company as described in Company Story and Customization Description,
you need deploy an instance of Acumatica ERP with the customization project that has been developed in the T220
Data Entry and Setup Forms training course.
You deploy an Acumatica ERP instance and configure it as follows:
1. Open the Acumatica ERP Configuration Wizard, and do the following:
a. Click Deploy New Application Instance for T-series Developer Courses.
b. On the Database Configuration page, make sure the name of the database is PhoneRepairShop.
c. On the Instance Configuration page, do the following:
a. In the Local Path of the Instance box, select a folder that is outside of the C:\Program Files
(x86), C:\Program Files, and C:\Users folders. (We recommend that you store the website
folder outside of these folders to avoid an issue with permission to work in these folders when you
perform customization of the website.)
b. In the Training Course box, select the training course you are taking.
The system creates a new Acumatica ERP instance, adds a new tenant, loads the data to it, and publishes
the customization project that is needed for activities of this guide.
2. Make sure a Visual Studio solution is available in the App_Data\Projects\PhoneRepairShop folder
of the Acumatica ERP instance folder. This is the solution of the extension library that you will modify in the
activities of this guide.
3. Sign in to the new tenant by using the following credentials:
• Username: admin
• Password: setup
Preparing an Instance for Workflow Customization | 17

Change the password when the system prompts you to do so.


4. In the top right corner of the Acumatica ERP screen, click the username, and then click My Profile. The User
Profile (SM203010) form opens. On the General Info tab, select YOGIFON in the Default Branch box; then
click Save on the form toolbar.
In subsequent sign-ins to this account, you will be signed in to this branch.
5. Optional: Add the Customization Projects (SM204505) and Generic Inquiry (SM208000) forms to your favorites.
For details about how to add a form to your favorites, see Managing Favorites: General Information.

Instance Preparation: To Enable Workflow Validation

The following activity will walk you through the process of preparing an instance of Acumatica ERP for the
validation of a workflow that you create by using Workflow API.

Story
Suppose that you need to develop a workflow by using Workflow API. In this case, you need to learn how to debug
the code and catch errors.

Process Overview
The code written by using Workflow API is declarative. As a result, the usual approaches to debugging do not work
with the workflow code. To catch errors that occur in a workflow, you need to enable workflow validation in the
web.config file of your instance. This way, when an error occurs on a form that uses the workflow, you will be
able to see detailed information about the error on the System Events tab of the System Monitor (SM201530) form
of Acumatica ERP.

Step: Enabling Workflow Validation


To enable workflow validation, do the following:
1. In the instance folder, open the web.config file.
2. In the appSettings tag of the file, find the EnableWorkflowValidationOnStartup key, and set its
value to True, as the following code shows.

Instance Preparation: To Configure the Instance

Based on the Customization Description, you need to configure the instance to be able to test the logic you
implement while completing the activities in this guidetraining course.

Story
In the Smart Fix company, there are no shipments or sales orders associated with repair work orders. Therefore,
you need to configure Acumatica ERP so that during the creation of an SO invoice on the Invoices (SO303000) form,
stock items can be added directly to the SO invoice without sales orders and shipments being processed.
Aer you perform the customization tasks, you will be testing your changes, including the Create Invoice button
or command (which corresponds to the CreateInvoice action that you will create) of the Repair Work Orders
(RS301000) form. If you click multiple times during testing, you may run into an issue with insufficient stock items
in a warehouse. When an SO invoice is released, the quantity of stock items included in the invoice is checked, and
Preparing an Instance for Workflow Customization | 18

if there are not enough stock items in a warehouse, an invoice cannot be created. You need to prevent this issue
from occurring.

Process Overview
You will enable the Advanced SO Invoices feature on the Enable/Disable Features (CS100000) form to make it
possible to process repair work orders.
On the Item Classes (IN201000) form, you will also change the STOCKITEM item class, which is the class specified for
all the stock items used in the activities of this guidetraining course, to also allow negative quantities for the stock
items. This will prevent possible issues during the testing of the Create Invoice action.

Step 1: Turning on the Feature


To turn on the feature, on the Enable/Disable Features (CS100000) form, do the following:
1. On the form toolbar, click Modify.
2. Select the Advanced SO Invoices check box.
3. Click Enable on the form toolbar.

Step 2: Allowing Negative Quantities for Stock Items


To allow negative quantities for stock items, do the following:
1. On the Item Classes (IN201000) form, in the Item Class Tree, select STOCKITEM. All the stock items used in
the activities of this guidetraining course belong to this class.
2. On the General tab (General section), select the Allow Negative Quantity check box, as shown in the
following screenshot.

Figure: Item Classes form

3. On the form toolbar, click Save.


4. Open the Accounts Receivable Preferences(AR101000) form.
5. On the Data Entry Settings section of the General tab, clear the Validate Document Totals on Entry and
Require Payment Reference on Entry check boxes. This will make it possible for users to skip entering
values during the release of an invoice.
Preparing an Instance for Workflow Customization | 19

6. On the form toolbar, click Save.


Preparing a Screen Configuration | 20

Preparing a Screen Configuration


A screen configuration corresponds to each Acumatica ERP form and consists of the configuration of the elements
of the screen (form), including the definition of the form's workflow or workflows. In this chapter, you will
learn what the components of a screen configuration are and how to prepare a graph extension and a screen
configuration for developing a workflow.

Screen Configuration: General Information

Each form of Acumatica ERP (including existing and custom forms) includes a screen configuration—that is, the
configuration of the screen that corresponds to the form. The screen configuration contains the configuration of
the components of the particular screen, such as actions and fields, and the definitions of workflows.
When a form is created, a screen configuration of this form is empty. To define a workflow or to add configuration of
components of a screen, you need to edit the screen configuration of the corresponding form. Some of the existing
forms of Acumatica ERP have predefined workflows, so their screen configuration is not empty.
In this chapterIn this lesson, you will learn how to edit a screen configuration and start defining a workflow.

Learning Objectives
In this chapter, you will learn how to do the following:
• Determine the workflow's set of states of a record created on this form
• Define the set of states for the workflow
• Override the Configure method that contains the screen configuration
• Locate the class where the predefined workflow is implemented
• Create a graph extension where a customized workflow can be implemented

Applicable Scenarios
You will work with a screen configuration in the following cases:
• You want to create a new workflow. You create a workflow when the form you want to customize does not
have one, or when the existing workflow does not fit your business processes.
• Update (that is, create an extension of) a predefined workflow. Customizing a predefined workflow can save
you time over creating a custom workflow from scratch, especially if you want to make only minor changes
to the functionality of the predefined workflow.
• You want to add or update components of a screen configuration such as actions or fields. You can configure
actions and states of fields in a screen configuration even if they are not used in a workflow.

Components of a Screen Configuration


A screen configuration is defined by the set of components of a screen and the workflows in which these
components are used. To configure a screen, you can define any of the following:
• Conditions: To define conditions in a screen configuration, you use the WithConditions method. For
details, see Defining Conditions.
• Dialog boxes: To define dialog boxes in a screen configuration, you use the WithForms method. For details,
see Implementing Dialog Boxes.
Preparing a Screen Configuration | 21

• Fields: To define the states of fields in a screen configuration, you use the WithFieldStates method. For
details, see Configuring Field States .
• Actions and their categories: To define actions and action categories, you use the WithActions and
WithCategories method. For details, see Implementing Workflow Actions.
• Event handlers: To define event handlers, you use the WithHandlers method. For details, see
Implementing Workflow Events.
• Any number of workflows. For each workflow, you define the following:
• Workflow states, which can include action definitions and field states
To define workflow states, you use the WithStates method. For details, see Defining Workflow States.
• Workflow transitions, which can be triggered based on conditions
To define transitions, you use the WithTransitions method. For details, see Implementing Transitions.
For workflow other than the default workflow, you also define the workflow-identifying field. This field
holds the value that determines the workflow to be used.
The following diagram shows all components of the screen configuration.

Figure: Components of the screen configuration

A new screen has an empty configuration. Therefore, when you implement a workflow, you edit the existing screen
configuration. To edit the screen configuration, you override the Configure method in an extension of the graph
that defines the business logic of the screen.
Preparing a Screen Configuration | 22

Inside the Configure method, you define a workflow by using Workflow API (which is also called the screen
configuration API). Workflow API has a strongly typed syntax. It has many compiler-based validations, which reduce
the likelihood of an error. The API is made mostly of fluent generic methods, which use LINQ expressions.

Screen Configuration: To Prepare a Screen Configuration Without a Predefined


Workflow

This activity will walk you through the process of performing the initial steps for implementing a custom workflow,
which is a workflow for a form that has no predefined workflows.

Learning Objectives
In this activity, you will learn how to do the following:
• Determine the workflow's set of states of a record created on the form
• Define the set of states for the workflow
• Create the screen configuration method

Story
Suppose that you need to define a workflow for the Repair Work Orders (RS301000) form, which has no predefined
workflows.

Process Overview
In this activity, you will prepare the basic components of a workflow by performing the following steps in the
extension library:
• Defining the graph extension where the workflow will be implemented
• Defining a list of the states of the workflow
• Overriding the screen configuration method
• Specifying the state-identifier field

System Preparation
Make sure that you have configured your instance as described in Preparing an Instance for Workflow Customization.
You should have the Repair Work Orders (RS301000) form available in the system and located in the Phone Repair
Shop workspace.

Step 1: Defining the Workflow Class

In this step, you will define the class where the workflow will be implemented in later activities of this guidetraining
course.
A workflow is defined in an extension of the graph for which the workflow should be applied. For example, if
you need to implement a workflow for the Opportunities (CR304000) form, whose business logic is defined in the
OpportunityMaint graph, you need to create an extension of the OpportunityMaint graph. In this step,
you will define the RSSVWorkOrderWorkflow class that will contain a workflow for the Repair Work Orders
(RS301000) form.
Preparing a Screen Configuration | 23

To define the RSSVWorkOrderWorkflow workflow class, do the following:


1. In the PhoneRepairShop_Code project, create the Workflows folder.
2. In the Workflows folder, add a new item named RSSVWorkOrderWorkflow.cs based on the C# class
template .
3. In the RSSVWorkOrderWorkflow.cs file, define the workflow class, as the following code shows.

using PX.Data;
using PX.Data.WorkflowAPI;
using static PX.Data.WorkflowAPI.BoundedTo<PhoneRepairShop.RSSVWorkOrderEntry,
PhoneRepairShop.RSSVWorkOrder>;

namespace PhoneRepairShop.Workflows
{
public class RSSVWorkOrderWorkflow :
PX.Data.PXGraphExtension<RSSVWorkOrderEntry>
{

}
}

4. Use Acuminator to suppress the PX1016 error in a comment. In activities of this guidetraining course, for
simplicity, the graph extension is always active.
5. Save your changes.

Step 2: Defining the Set of States of the Workflow

The first step in creating a workflow is determining the set of states that a record created on the form can have in
the workflow.
In this step, you will define the set of states for a record—in this case, a repair work order. The state is determined
by one of the fields of the record, which is usually the Status field. Therefore, you need to define the set of states
that corresponds to the set of statuses that a repair work order can have.
According to the customization description, which is available in Business Process Overview, a repair work order
can have the following statuses:
• On Hold (the WorkOrderStatusConstants.OnHold constant)
• Ready for Assignment (the WorkOrderStatusConstants.ReadyForAssignment constant)
• Pending Payment (the WorkOrderStatusConstants.PendingPayment constant)
• Assigned (the WorkOrderStatusConstants.Assigned constant)
• Completed (the WorkOrderStatusConstants.Completed constant)
• Paid (the WorkOrderStatusConstants.Paid constant)
Each state is defined by a combination of a constant string value and a class derived from the
BqlString.Constant class. The name of the class starts with a lowercase letter.
To create the set of states, do the following:
1. In the Constants.cs file, make sure that the constants for the Status box are defined as shown in the
following code. These values will be used to indicate the states of the workflow.

//Constants for the statuses of repair work orders


public static class WorkOrderStatusConstants
{
Preparing a Screen Configuration | 24

public const string OnHold = "OH";


public const string PendingPayment = "PP";
public const string ReadyForAssignment = "RA";
public const string Assigned = "AS";
public const string Completed = "CM";
public const string Paid = "PD";
}

2. In the RSSVWorkOrderWorkflow class, define the Constants region and the public static class inside
it, as the following code shows.

#region Constants
public static class States
{
}
#endregion

3. In the States class, define the constant string values that correspond to the repair work order statuses, as
the following code shows.

public const string OnHold = WorkOrderStatusConstants.OnHold;


public const string ReadyForAssignment =
WorkOrderStatusConstants.ReadyForAssignment;
public const string PendingPayment =
WorkOrderStatusConstants.PendingPayment;
public const string Assigned = WorkOrderStatusConstants.Assigned;
public const string Completed = WorkOrderStatusConstants.Completed;
public const string Paid = WorkOrderStatusConstants.Paid;

As the Status field value, you have used the enumeration defined in T220 Data Entry and Setup Forms.
4. In the States class, define the classes for each state of the workflow, as the following code shows.

public class onHold : PX.Data.BQL.BqlString.Constant<onHold>


{
public onHold() : base(OnHold) { }
}

public class readyForAssignment :


PX.Data.BQL.BqlString.Constant<readyForAssignment>
{
public readyForAssignment() : base(ReadyForAssignment) { }
}

public class pendingPayment :


PX.Data.BQL.BqlString.Constant<pendingPayment>
{
public pendingPayment() : base(PendingPayment) { }
}

public class assigned : PX.Data.BQL.BqlString.Constant<assigned>


{
public assigned() : base(Assigned) { }
}

public class completed : PX.Data.BQL.BqlString.Constant<completed>


{
public completed() : base(Completed) { }
Preparing a Screen Configuration | 25

public class paid : PX.Data.BQL.BqlString.Constant<paid>


{
public paid() : base(Paid) { }
}

5. Save your changes.

Step 3: Overriding the Configure Method

To implement a workflow, you will override the Configure method, which accepts the
PXScreenConfiguration instance. Inside the Configure method, you will get the configuration context
object, which you will later use to add the new screen configuration.
Do the following:
1. In the RSSVWorkOrderWorkflow class, override the Configure method, as the following code shows.

public override void Configure(PXScreenConfiguration config)


{
}

2. In the method, get the configuration context object, as the following code shows.

var context = config.GetScreenConfigurationContext<RSSVWorkOrderEntry,


RSSVWorkOrder>();

In the code above, you have gotten the current context of the screen configuration for the Repair
Work Orders (RS301000) form by specifying the form graph and primary DAC as parameters of the
GetScreenConfigurationContext method.
3. Add a template for defining the default workflow, as the following code shows.

context.AddScreenConfigurationFor(screen => screen


.StateIdentifierIs<RSSVWorkOrder.status>()
.AddDefaultFlow(flow => ...));

In the code above, you have added a new screen configuration for the Repair Work Orders form, specified
the field that is the state identifier, and started to add the default workflow.
The state-identifier is the field that holds the state value for the workflow definition. You specify this field by
calling the StateIdentifierIs method and providing the field name as a generic parameter.
In the next activities (such as Workflow States: To Define a Workflow State and Transitions: To Implement
a Simple Transition), you will define a function inside the AddDefaultFlow method that specifies the
settings of the default workflow.
4. Save your changes.

Now you are ready to add the definition of the workflow to the screen configuration.
Preparing a Screen Configuration | 26

Screen Configuration: To Prepare a Screen Configuration with a Predefined


Workflow

Most data entry forms of Acumatica ERP have at least one predefined workflow—that is, a workflow that is
implemented in the original code of Acumatica ERP.
The following activity will walk you through the process of performing the initial steps for customizing a predefined
workflow.

Learning Objectives
In this chapter, you will learn how to do the following:
• Locate a class where the predefined workflow is implemented
• Create a graph extension where a customized workflow can be implemented
• Override the Configure method

Story
Suppose that you need to define a workflow for the Invoices (SO303000) form, which has a predefined workflow—
that is, a workflow that has been implemented by Acumatica developers.

Process Overview
To extend the predefined workflow of the Invoices (SO303000) form, in the source code of Acumatica ERP, you will
first find the class where the workflow is defined. You will then create an extension of this class and prepare the
method where you can later customize the workflow.

System Preparation
As a prerequisite activity, perform the activities of the Preparing an Instance for Workflow Customization chapter to
configure your instance.
Make sure you have enabled the workflow validation as described in Instance Preparation: To Enable Workflow
Validation.

Step 1: Locating the Class that Defines the Workflow


To locate the class where the workflow is defined, you should look into the PX.Objects/<area> folder in the
source code of Acumatica ERP, where <area> is the code of the functional area of the form for which the workflow
is defined. In most cases, workflows of this functional area are located in the Workflows folder of this location.
Sometimes all workflow classes are located at the same level as the graph classes—that is, in the PX.Objects/
<area> folder.
To locate the class where the workflow for the Invoices (SO303000) form is defined, do the following:
1. In the source code of Acumatica ERP, locate the PX.Objects/SO/Workflow folder.
2. In this folder, find the SOInvoiceEntry_Workflow class, which is an extension of the
SOInvoiceEntry graph.
Preparing a Screen Configuration | 27

In the code of the SOInvoiceEntry_Workflow class, notice that the states of the workflow are defined
in the ARDocStatus class. You will need this class if you want to use the states of the workflow in your
code.

Step 2: Extending the Predefined Workflow


To extend a predefined workflow, you need to create an extension of the class where the workflow is defined. In this
extension, you need to override the Configure(PXScreenConfiguration) method, which initializes the
screen configuration.
To extend a predefined workflow, do the following:
1. In the Workflows folder of the PhoneRepairShop_Code project, create the
SOInvoiceRepairOrder_Workflow.cs file.
2. In the SOInvoiceRepairOrder_Workflow.cs file, define the
SOInvoiceRepairOrder_Workflow class, as the following code shows.

In the code above, you have defined an extension of the SOInvoiceEntry_Workflow class. As a
second parameter of the extension, you have specified the graph of the Invoices form. In the extension, you
have overridden the Configure(PXScreenConfiguration) method, which initializes the screen
configuration, and have declared the Configure(WorkflowContext) method, where you can later
update the workflow.
3. Make sure that you have added the needed using directives.
4. Save your changes.

Screen Configuration: Restrictions of the Configure Method

Inside the Configure method, you should not create a graph instance or access the Base graph because doing
either of these things can lead to deadlocks. If you need to read data from the database, you can use database slots
by doing the following:
1. In the graph extension where you need to define the Configure method, you also define a database slot
that depends on the DAC from which you need to read data. In the slot, you cache the data that you need to
access.
2. You attach the PXWorkflowDependsOnType attribute to the Configure method and specify the DAC
from which you want to read data.
3. In the Configure method, you access the data from the slot.

For example, suppose that you need to enable the extension in the Configure method only if the
InspectionEnabled property of SOSetup DAC is true. The following code shows how you can access the
InspectionEnabled property by using a database slot.

private class SOSetupInspection : IPrefetchable


{
public static bool InspectionEnabled =>
PXDatabase.GetSlot<SOSetupInspection>("SOSetupInspection",
typeof(SOSetup))._inspectionEnabled;
private bool _inspectionEnabled;
void IPrefetchable.Prefetch()
{
using (PXDataRecord soSetup =
PXDatabase.SelectSingle<SOSetup>(new PXDataField<SOSetup.inspectionEnabled>()))
Preparing a Screen Configuration | 28

if (soSetup != null) _inspectionEnabled = (bool)soSetup.GetBoolean(0);


}
}

[PXWorkflowDependsOnType(typeof(SOSetup))]
public override void Configure(PXScreenConfiguration config){
//if (!Base.sosetup.Current.InspectionEnabled) return;
if (!SOSetupInspection.InspectionEnabled) return;
}

The code above does the following:


1. In the extension, defines the SOSetupInspection database slot, which depends on the SOSetup table.
In the slot, the SOSetup.InspectionEnabled value is being cached.
2. Attaches the PXWorkflowDependsOnType attribute to the Configure method and specifies that the
workflows of the form depend on data from the SOSetup DAC.
3. In the Configure method, instead of accessing the Base graph, checks the
SOSetupInspection.InspecionEnabled property, which holds the cached value of the
SOSetup.InspectionEnabled property.

For more information on database slots, see Use of Slots to Cache Data Objects.
Defining Conditions | 29

Defining Conditions
In this chapter, you will learn how to define the conditions that can be used in a screen configuration and in a
worfklow. You can use conditions to specify when actions should be performed, when transitions should be
triggered, and when certain fields and dialog boxes are available on the form.

Conditions: General Information

A condition is a Boolean statement that is checked in a workflow. When the statement is true, a workflow
component can change to your specifications.

Learning Objectives
In this chapterIn this lesson, you will learn where and how a condition can be applied.

Applicable Scenarios
You can use a condition in the following ways:
• To change Boolean properties of each action, such as its visibility and availability
• To change Boolean properties of each field, such as its visibility, requirement, and availability
• To check whether a transition should be performed

Declaring of a Condition
You declare a condition in a condition pack, which is a class that inherits from the Condition.Pack class. In
that class, you define a member of the Condition type by calling the GetOrCreate method. As a parameter,
you provide a fluent BQL statement that uses the FromBql or FromBqlType method. An example of a condition
definition is shown in the following code.

public class Conditions : Condition.Pack


{
public Condition IsOnHold =>
GetOrCreate(b => b.FromBql<hold.IsEqual<True>>());
public Condition IsCompleted =>
GetOrCreate(b => b.FromBql<completed.IsEqual<True>>());
public Condition IsOnCreditHold =>
GetOrCreate(b => b.FromBql<creditHold.IsEqual<True>>());
}

The Condition.Pack class provides the GetOrCreate method, which registers a condition definition under
the name of the class property. Later, you can retrieve the condition through this property by using the GetPack
method.
You can combine conditions by using AND, OR, and NOT C# operators to dynamically create and register new
conditions.

Uses of Conditions
You can use conditions in the following ways:
Defining Conditions | 30

• To disable an action depending on a condition: In this case, you should call the IsDisabledWhen method
while adding an action to a screen configuration and provide a condition as a parameter. For an example,
see Step 2 of the Workflow Actions: To Configure the Conditional Appearance of the Action activity.
• To hide a field, disable the field, or make the field required depending on a condition. You do this while
configuring the state of the field in the screen configuration. For details, see Configuration of a Field State.
• To hide a dialog box field depending on a condition: In this case, you call the IsHiddenWhen method
while adding this field to a dialog box and provide a condition as a parameter. For details, see Configuration
of the Fields of a Dialog Box.
• To check whether a transition should be performed: To do this, you call the When method while adding
the transition in a transition group and provide a condition as a parameter. This way, you can also create
multiple transitions with a single initial state if these transitions should be triggered by the same action
or event. The transition is performed when the provided condition is true. An example is shown in the
following code.
.WithTransitions(transitions =>
{
transitions.AddGroupFrom(initialState, ts =>
{
...
ts.Add(t => t
.To<State.balanced>()
.IsTriggeredOn(g => g.initializeState)
.When(conditions.IsBalanced)
}
}

For a full example, see Step 3 of the Transitions: To Implement a Group of Transitions activity.
Configuring Field States | 31

Configuring Field States


This chapter describes how to configure field states in a screen configuration. For details on configuring field states
in a workflow state, see Workflow States: General Information.

Field States: General Information

You can configure the states of fields, including default values and appearance, in a screen configuration.

Learning Objectives
In this chapter, you will learn how to do the following:
• Add a field state to a screen configuration
• Specify the default value of a field
• Specify the set of values for a combo box field

Applicable Scenarios
You define field states in a screen configuration when you need to configure the properties of this field for all
workflows of a form.

Adding or Updating of a Field State


To add a state of a field to a screen configuration or update the state, you call the WithFieldStates
method in the screen configuration (that is, in the AddScreenConfigurationFor or
UpdateScreenConfigurationFor method). Inside the WithFieldStates method, you can do the
following:
• Add a field state by calling the Add method
• Update a field state by calling the Update method
• Replace the entire configuration of the field state by calling the Replace method
• Delete the entire configuration of the field state by calling the Remove method

Configuration of a Field State


You can configure a field state in the following ways:
• Configure the appearance and requirement of the field depending on a condition by calling the following
methods:
• IsHiddenWhen
• IsDisabledWhen
• IsRequiredWhen
• Make a field hidden, disabled, or required by calling the respective method:
• IsHiddenAlways
• IsDisabledAlways
• IsRequiredAlways
These methods may be useful to edit the properties of a field from a predefined screen configuration.
Configuring Field States | 32

• Add a combo box value by calling one of the following methods:


• SetComboValue
• SetComboValues
• Specify a default value for the field by calling one of the following methods:
• WithDefaultValue
This method allows the developer to specify the exact value.
• WithDefaultExpression
This method allows the developer to provide a BQL expression that determines the proper value.
An example of the use of this method is shown in the following code.
context.AddScreenConfigurationFor(screen =>
{
return screen
.StateIdentifierIs<CRCase.status>()
.AddDefaultFlow(DefaultCaseFlow)
...
.WithFieldStates(fields =>
{
fields.Add<CRCase.resolution>(field => field
.SetComboValues(
(_reasonRejected, "Rejected"),
(_reasonResolved, "Resolved"),
...
(_reasonUnassigned, "Unassigned"),
(_reasonAssigned, "Assigned"),
(_reasonUpdated, "Updated"))
.WithDefaultValue("Assigned"));
})
});

In the code above, the CRCase.resolution field is added to the screen configuration. The default value
of the field is specified in the WithDefaultValue method.

If a field has combo box values, the default value should be one of the values listed in the
SetComboValues method.

Field States: Levels of Configuration

You can configure a field on the following levels, which are listed in order:
1. In the definition of a field in a DAC or one of its extensions
2. In a screen configuration
3. In a workflow state

Field configurations defined on each level are applied to the next level. For example, if you specified a default value
for a field in the PXDefault attribute in a DAC, this value is the default value for this field for the whole screen
configuration, and you can change it in the screen configuration. But for some properties (see the table below), the
configuration specified on the next level can only narrow what has been defined in the previous level.
The following table lists all possible configuration types and the levels where they can be changed.
Configuring Field States | 33

Configuration Type DAC Screen Configura- Workflow State Comment


tion

Visibility + + + The condition on


the next level can
Requirement + + + only narrow what
has been defined in
Availability + + + the previous level.

Specify default val- + + +  


ue

Specify combo box + + + On the workflow


value state level, you can
only restrict the set
of values.
Implementing Workflow Actions | 34

Implementing Workflow Actions


In this chapter, you will learn how to define and configure workflow actions. You can implement actions, specify
the categories of the associated commands on the More menu, configure conditional states, and perform field
assignments.

Workflow Actions: General Information

This chapterThis lesson will give you experience with and an understanding of the ways you can define and
configure workflow actions.

Learning Objectives
In this chapterIn this lesson, you will learn how to do the following:
• Define and configure a workflow action
• Define a category for an action
• Configure the conditional appearance of an action

Applicable Scenarios
You define a workflow action when you need to add an action to a form that uses a workflow. You can display an
action and make it available only in particular states, and you can use an action to trigger a transition from one
state to another.

About Workflow Actions


Actions are methods that can be invoked from outside of the graph, from the UI, or through the web service API.
Actions are associated with buttons or commands (or both) on the user interface.
Workflow actions are actions that are added to the screen configuration and to a workflow. The declaration of a
workflow action consists of the following:
• The declaration of an action in a graph.
You can skip the declaration of an action in a graph if the action does not have complex logic—that is, if the
only thing that action does is assign values to fields. In that case, you declare an action in the Configure
method and define the action's logic by using capabilities of the Workflow API.
• Declaration of an action in the screen configuration.

Action Declaration in a Graph


The declaration of an action in a graph consists of the following:
• A field of the PXAction<> type, which is declared as follows.
public PXAction<Shipment> CancelShipment;

In the PXAction<> type parameter, you should specify the main DAC of the primary data view. Otherwise,
the button corresponding to the action cannot be displayed on the form toolbar (and the corresponding
command cannot be displayed on the More menu).
• A method that implements the action; this method has the PXButton and PXUIField attributes. This
method has the following forms of declaration:
Implementing Workflow Actions | 35

• Without parameters and returning void: This standard form of declaration is shown in the following
code example.
[PXButton]
[PXUIField(DisplayName = "Cancel Shipment")]
protected virtual void cancelShipment()
{
...
}

This type of declaration is used for an action that is executed synchronously and is not called from a
processing form.
• With a parameter of the PXAdapter type and returning IEnumerable: You can see an example of this
form of declaration in the following code.
[PXButton]
[PXUIField(DisplayName = "Release")]
protected virtual IEnumerable release(PXAdapter adapter)
{
...
return adapter.Get();
}

This type of declaration should be used when the action initiates a background operation or is called
from a processing form.
The field and the method should have the same name, differing only in the capitalization of the first letter.

A graph includes the Actions collection of all PXAction<> objects defined in the graph.

For more information about actions, see Configuration of Actions and the T230 Actions training course.

Action Declaration in a Workflow


You add an action to the workflow by registering the action in the screen configuration as follows:
1. You call the WithActions method in the lambda expression passed to the
AddScreenConfigurationFor method.
2. In the lambda expression for the WithActions method, you call the Add method as follows:
• In the method, you specify the action member of the PXAction<> type that you defined in the graph.
• You can specify in which category of the More menu the command corresponding to the action should
be displayed by calling the WithCategory method in a lambda expression. If you do not specify a
category for an action, its command will be listed under the Other category on the More menu and
placed on the form toolbar as buttons.
The following code shows an example of an action definition.

context.AddScreenConfigurationFor(screen => screen


...
.WithActions(actions =>
actions.Add(g => g.PutOnHold, c => c
.WithCategory(ProcessingCategory));
Implementing Workflow Actions | 36

Workflow Actions: Configuration of Actions

You can configure workflow actions in the following ways:


• Add, update, or delete an action in the screen configuration by calling the Add, Update, or Delete
method, respectively, in the WithActions method at the screen configuration level.
For example, see Step 3: Adding the Action to the Workflow.
• Add, update, or delete an action in a specific state of a workflow by calling Add, Update, or Delete
method, respectively, in the WithActions method at the state level. By adding an action to a state, you
make it available in this state unless it is restricted by the settings specified in the screen configuration.
For details on configuring an action in a workflow state, see Workflow States: Configuring Action States.
For example, see Workflow States: To Define a Workflow State.
• Assign field values when the action is performed by calling the WithFieldAssignments method while
adding or updating the action in the screen configuration. For a detailed example, see Workflow Actions: To
Implement an Action with Field Assignments.
• Provide the parameters of an action by calling the WithParameterAssignments method.
• Display a dialog box when an action is clicked by calling the WithForm method. For details on defining
dialog boxes, see Implementing Dialog Boxes.
• Configuring the Appearance of an Action
• Configuring Persisting Options
• Configuring Mass Processing
• Configuring the Behavior of an Action
• Mapping a Workflow Action to the Mobile App

Levels of Action Configuration


You can configure an action on the following levels, which are listed in order:
1. In the definition of the action in a graph or in one of its extensions
2. In a screen configuration
3. In a workflow state

Configurations defined on each level are applied to the subsequent level. For example, if you specified a category
for an action in the PXButton attribute in a graph, this category is the default category for this action for the
whole screen configuration, and you can change it in the screen configuration. But for some properties (see the
table below), the configuration specified on the next level can only narrow what has been defined in the previous
level.
The following table lists all possible configuration types and the levels where they can be changed.

Configuration Type Graph Screen Configura- Workflow State Comment


tion

Visibility + +   The condition on


the next level can
Availability + +   only narrow what
has been defined in
the previous level.
Implementing Workflow Actions | 37

Configuration Type Graph Screen Configura- Workflow State Comment


tion

Category + +   The location inside


the category can be
specified only in the
screen configura-
tion.

Connotation +   +  

Display on the form +   +  


toolbar

Assign field values + +    

Provide parameters   +    

Display a workflow   +    
dialog box

Configure the per- + +    


sisting option

Run report, open a + +    


generic inquiry, cre-
ate a new record

Expose the action to   +    


the mobile app

Configuring the Appearance of an Action


You can configure the appearance of an action in a specific state of a workflow and in a screen configuration. The
settings specified in a screen configuration are applied to the action in all states of all workflows defined in the
screen configuration.
To configure an action in a screen configuration, you call configuration methods in a lambda expression provided
for the Add or Update method when you register an action to the screen configuration. You can configure the
action's appearance in a screen configuration in the following ways:
• Specify a category in which the action will be displayed on the More menu by calling the WithCategory
method.
By default, an action is placed in the Other category.
• Specify the location of the action inside the category.
By default, the actions are displayed in the order in which they are added in the screen configuration.
You can specify the action's location in one of the following ways while adding the action to the screen
configuration:
• By specifying the Placement parameter in the WithCategory method.
You can use this method when adding a new action to a category.
• By calling the Place method.
You can use this method when you need to customize the location of a predefined action in a category.
• By calling one of the following methods:
Implementing Workflow Actions | 38

• PlaceInCategory
• PlaceFirst
• PlaceLast
• PlaceBefore
• PlaceAfter
• Disable an action depending on a condition by calling the IsDisabledWhen method.
You can also disable an action unconditionally by calling the IsDisabledAlways method. You may need
this method if you want to disable a predefined action from the form.
• Hide an action if a condition is met by calling the IsHiddenWhen method.
You can also hide an action unconditionally by calling the IsHiddenAlways method. You may need this
method if you want to remove a predefined action from the form.

The IsDisabledXXX and IsHiddenXXX methods cannot change behavior that was implemented in the
code of a graph or a graph extension. Workflow configurations can only restrict what has been defined
in code.

To configure an action in a specific state of a workflow, you call configuration methods in a lambda expression
provided for the Add or Update method when you register an action in a state. You can configure the action's
appearance in a specific state in the following ways:
• Display an action on the form toolbar.
By default, all actions are displayed on the More menu. You can display an action on the form toolbar by
calling the IsDuplicateInToolbar method while adding an action to a screen configuration.
• Specify a connotation for an action in this state by calling the WithConnotation method while adding an
action to the screen configuration. The connotation will be displayed for this action both on the More menu
and on the form toolbar. You can specify different connotations for different states.
For details on configuring actions in a workflow state, see Workflow States: Configuring Action States.

Configuring Persisting Options


In the code of a graph, the changes are usually saved to the database by calling the Persist method. When
adding an action to a screen configuration, you can configure whether and when the current changes in
the cache should be saved to the database—that is, when the system should invoke the Persist method.
By default, the changes are saved to the database aer the action method is performed. To cancel the
saving of changes to the database, call the DoesNotPersist method in the action configuration. To
specify that current changes should be saved to the database before the action method is invoked, call the
WithPersistOptions(ActionPersistOptions.PersistBeforeAction) method.
The following example shows an action from the workflow on the Opportunities (CR304000) form.

.WithActions(actions =>
{
actions.Add(g => g.Open, c => c
.WithForm(formOpen)
.WithFieldAssignments(fields =>
{
fields.Add<CROpportunity.resolution>(f => f.SetFromFormField(formOpen,
ReasonFormField));
fields.Add<CROpportunity.stageID>(f => f.SetFromFormField(formOpen, StageFormField));
fields.Add<CROpportunity.isActive>(f => f.SetFromValue(true));
fields.Add<CROpportunity.closingDate>(f => f.SetFromValue(null));
})
.IsHiddenWhen(conditions.IsInNewState)
.WithPersistOptions(ActionPersistOptions.PersistBeforeAction)
Implementing Workflow Actions | 39

.WithCategory(categoryProcessing)
.IsExposedToMobile(true)
.MassProcessingScreen<UpdateOpportunityMassProcess>());

If you need to save the changes of one entity on a form where this entity is not used, you need to override the
Persist method of this form and manually save changes of this custom entity. This might be useful, for example,
if you need to save the changes of a custom entity on a predefined form. For more information, see Step 6:
Overriding the Persist Method.

Configuring Mass Processing


You can use a workflow action on a mass processing form, which gives a user the ability to process any number
of records simultaneously. To specify that the action should be used for mass processing, you should call the
MassProcessingScreen method when adding the action to the screen configuration and specify the graph of
the processing form as a type parameter of the method. You can also call the InBatchMode() method so that the
action processes the list of records at one time. Otherwise, the action is invoked for each record separately.
An example configuring mass processing is shown in the following code.

actions.Add(g => g.Assign,


c => c.WithCategory(processingCategory, g => g.PutOnHold)
.MassProcessingScreen<RSSVAssignProcess>()
.InBatchMode());

For a detailed example, see Lesson 1.1 of the T240 Certification Test: Processing Forms training course.

Configuring the Behavior of an Action


You can specify the following behavior for a workflow action without implementing an action method in a graph:
• Open a report by calling the IsRunReportScreen method and specifying a configuration of a report in
the parameter. The following code shows an example from the INSiteMaint graph.
actions.AddNew(locationLabels.ActionName, a => a
.WithCategory(PredefinedCategory.Reports)
.DisplayName(Messages.INLocationLabels)
.IsRunReportScreen(report =>
{
return report
.ReportID(locationLabels.ReportID)
.WithWindowMode(PXBaseRedirectException.WindowMode.New)
.WithAssignments(rass =>
{
rass.Add(locationLabels.Parameters.Site, z =>
z.SetFromField<INSite.siteCD>());
});
}));

In the code above, the action opens a report in a new window and assigns a value to a report parameter.
• Open a generic inquiry—for example, a substitute form—by calling the IsSearchRecordScreen
method.
• Open a form where a user can create a new record by calling the IsCreateRecordScreen method.

You should define the behavior of an action either in the code of a graph or in the screen
configuration. If you define the behavior of an action in a workflow, the action method in a graph
should be empty.
Implementing Workflow Actions | 40

Mapping a Workflow Action to the Mobile App


You can map a workflow action to the Acumatica mobile app without using MSDL. To map a workflow action, call
the IsExposedToMobile method while adding an action to a screen configuration.
For this method to work, you must use MSDL to map the screen where the action should be displayed. For more
information about mapping forms to the Acumatica mobile app, see Mobile Framework Guide.

Workflow Actions: Categories of the More Menu

On the More menu of an Acumatica ERP form, the commands corresponding to actions are listed under categories.
You can specify the category of an action by using Workflow API or attributes. If a category is not specified for an
action, its corresponding command is listed under the Other category and placed on the form toolbar as a button.

To Specify a Category for an Action on a Form That Uses a Workflow


To specify a category for an action on a form that uses a workflow, do the following:
1. Define and configure the category, as described in To Create a Category
2. In the action definition, call the WithCategory method, and specify the object that defines the category.
An example is shown in the following code.

actions.Add(g => g.printSalesOrder, c =>


c.WithCategory(ActionCategory.printAndEmailCategory));

You can also specify can specify the location of an action in a category: An action can be placed first, last, or
before or aer another specified action. To specify the location, you use the Placement parameter of the
WithCategory method or the Place method.
In the following code example, the putOnHold action is placed before the releaseFromHold action inside the
processingCategory category.

actions.Add(g => g.putOnHold, c => c


.WithCategory(processingCategory, Placement.Before, g => g.releaseFromHold));

In the following code example, the putOnHold action is placed last inside the processingCategory category.

actions.Add(g => g.putOnHold, c => c


.WithCategory(processingCategory, Placement.Last));

To Specify a Category for an Action on a Form That Does Not Use a Workflow
You can place an action in a category by specifying the category in the PXButton attribute for an action. An
example is shown in the following code.

[PXButton(Category = "Publish")]
protected virtual void ViewXml()

In the code above, the ViewXml action is placed in the Publish category.
If there are multiple actions in the same category, the order of actions in the category will be the same as the
order in which the actions are declared in code: first, the actions declared in the code of the graph in their order of
declaration in code, and then the actions in all graph extensions in the order in which they are declared in code.
Implementing Workflow Actions | 41

To Create a Category
You can create categories and specify the order of the categories on the More menu.
To define a new category, you can use one of the following approaches:
• Define the category by creating a new object that can later be used for multiple actions.
You use this approach for a form that uses the workflow engine.
• Create the category in an action definition.
You use this approach for a form that does not use a workflow. For details, see To Create a Category on a
Form That Does Not Use a Workflow.
To define a category by creating an object, do the following:
1. Call the Categories.CreateNew method, and provide the display name of the category. An example is
shown in the following code.

var processSalesCategory = context.Categories.CreateNew


(ActionCategories.ProcessSalesCategoryID,
category => category.DisplayName(CategoryNames.ProcessSales)
.Place(Placement.Before, labelsCategory));
);

In the following code, you create a new category with the CategoryNames.ProcessSales display
name and place it aer the labelsCategory.
You can specify the location of a category on the More menu by calling the Place method. A category
can be placed first, last, or before or aer a specified category by using the Placement parameter of the
Place method.
2. Add the category to the screen configuration by using the WithCategories method, as the following
code shows.

.WithCategories(categories =>categories.Add(processSalesCategory))

In the following code example, the existing printingEmailingCategory category is placed last on the More
menu.

.WithCategories(
actions.Update(printingEmailingCategory,
category => category.Place(Placement.Last));
);

To Create a Category on a Form That Does Not Use a Workflow


On a form that does not use a workflow, you can define a category in the graph code. To do this, specify the display
name of the new category in the PXButton attribute. For details, see To Specify a Category for an Action on a Form
That Does Not Use a Workflow.

Workflow Actions: To Implement a Simple Action

The following activity will walk you through the process of implementing a workflow action that can later be used
to trigger a transition.
Implementing Workflow Actions | 42

Learning Objectives
In this activity, you will learn how to do the following:
• Define a graph action with an empty method
• Add a workflow action to the screen configuration
• Add a predefined category to the workflow
• Specify a category for an action

Story
On the Repair Work Orders (RS301000) form, you need to create a workflow action that can later be used to trigger
a transition. The command that corresponds to this action should be displayed in the Processing category of the
More menu.

Process Overview
In your extension library, you will implement the ReleaseFromHold action and add it to a workflow on the
Repair Work Orders (RS301000) form. You will also add the Processing category to the workflow and specify this
category for the ReleaseFromHold action.
You can later use the ReleaseFromHold action to trigger a transition from the OnHold state in the following
activities:
• Transitions: To Implement a Simple Transition
• Transitions: To Implement a Group of Transitions

System Preparation
In an instance with the T100 dataset, make sure that you have done the following:
1. Prepared an instance with the PhoneRepairShop customization project and enabled the workflow validation
by performing the prerequisite activities in the Preparing an Instance for Workflow Customization chapter.
2. Prepared the screen configuration and defined the set of states by performing the prerequisite activities in
the Preparing a Screen Configuration chapter.

Step 1: Implementing the Action in the Graph

In this step, you will implement the ReleaseFromHold action to the RSSVWorkOrderEntry graph.
To add a workflow action, you should perform the following tasks:
1. Define the workflow action in a graph.
You will add this definition in the current step.
2. Register this action configuration in the screen configuration. You do this by calling the WithActions
method in the AddScreenConfigurationFor method.
You will perform this task in Step 3: Adding the Action to the Workflow.
Implementing Workflow Actions | 43

Defining the ReleaseFromHold Action


To define the ReleaseFromHold action in the RSSVWorkOrderEntry graph, do the following:
1. In the RSSVWorkOrderEntry graph, add the Actions region.
2. In the Actions region, add the following code.

#region Actions
public PXAction<RSSVWorkOrder> ReleaseFromHold;
[PXButton(), PXUIField(DisplayName = "Remove Hold",
MapEnableRights = PXCacheRights.Select,
MapViewRights = PXCacheRights.Select)]
protected virtual IEnumerable releaseFromHold(PXAdapter adapter)
=> adapter.Get();
#endregion

In the code above, you have added a field of the PXAction<> type and the method that implements the
action. The method is empty because the business logic behind the action will be described in the code of
the workflow.
The action method is decorated with the PXButton attribute, where you specify the display name of the
action and the cache access rights. For details on the PXButton attribute, see PXButtonAttribute Class.
3. Save your changes.

Step 2: Defining a Category for the Action

Acumatica ERP provides a list of predefined categories for the More menu. You can add these categories to a
custom workflow and add actions (which are associated with commands on the More menu) to these categories. If
a command is associated with an action related to changing the status of a record created on the form, it is usually
displayed in the Processing category. To add the Processing category to your workflow, do the following:
1. In the Configure method of the RSSVWorkOrderWorkflow class, define the Processing category as
the following code shows.

#region Categories
var commonCategories = CommonActionCategories.Get(context);
var processingCategory = commonCategories.Processing;
#endregion

In the code above, you have obtained the list of default categories by calling the
CommonActionCategories.Get method.
2. In the Configure method, locate the AddDefaultFlow method, which you have added in Step 3:
Overriding the Configure Method.
3. For the screen parameter, call the WithCategories method aer the AddDefaultFlow method as the
following code shows.

context.AddScreenConfigurationFor(screen =>
screen
.StateIdentifierIs<RSSVWorkOrder.status>()
.AddDefaultFlow(flow => ...)
.WithCategories(categories =>
{
categories.Add(processingCategory);
})
Implementing Workflow Actions | 44

);

In the code above, in the lambda expression for the WithCategories method, you have added the
Processing category to the screen configuration by calling the Add method.

Step 3: Adding the Action to the Workflow

In this step, you will add the ReleaseFromHold action to the workflow and specify a category for the associated
Remove Hold command on the More menu.

Defining the Remove Hold Action in the Workflow


To add the ReleaseFromHold action to the workflow, do the following:
1. In the RSSVWorkOrderWorkflow class, in the Configure method, locate the AddDefaultFlow
method. (You have added this method in Step 3: Overriding the Configure Method.)
2. For the screen parameter, call the WithActions method aer the WithCategories method, as the
following code shows.

.WithActions(actions =>
{ })

3. In the lambda expression of the WithActions method, add the ReleaseFromHold action by calling the
Add method, as the following code shows.

actions.Add(g => g.ReleaseFromHold, c => c


.WithCategory(processingCategory));

In the code above, you have added the ReleaseFromHold action, which you defined in the
RSSVWorkOrderEntry graph in Step 1: Implementing the Action in the Graph, to the screen configuration.
By calling the WithCategory method, you have specified the category in which the command associated
with the action is displayed on the More menu.
4. Save your changes.

You will use this action later to trigger a transition.

Workflow Actions: To Configure the Conditional Appearance of the Action

This activity will walk you through the process of implementing an action that appears conditionally and adding it
to a workflow. The activity will also describe how to implement an action with a long-running operation.

Learning Objectives
In this activity, you will learn how to do the following:
• Define a graph action with a long-running operation
• Add a workflow action to the screen configuration
• Configure the conditional appearance of the action
Implementing Workflow Actions | 45

Story
For a repair work order to be billed and then paid, a user needs to create an invoice for the order. An invoice can
be created for a repair work order only if it has the Pending Payment or Completed status and no invoice has been
created for this repair work order. You need to implement the CreateInvoice action, as well as to configure
the conditional availability of the associated button on the form toolbar and the equivalent command on the More
menu.

Process Overview
In your extension library, first, you will define the CreateInvoice action in a graph. Then you will add this action
to the screen configuration and to the states of the workflow where the action should be available. You will also
define a condition depending on which the action the action should be available.

System Preparation
In an instance with the T100 dataset, make sure that you have done the following:
1. Prepared an instance with the PhoneRepairShop customization project and enabled the workflow validation
by performing the prerequisite activities of the Preparing an Instance for Workflow Customization chapter.
2. Prepared the screen configuration and defined the set of states by performing the prerequisite activities in
the Preparing a Screen Configuration chapter.
3. Defined the Pending Payment and Completed states by performing the Transitions: To Implement a Simple
Transition and Workflow Actions: To Implement an Action with Field Assignments prerequisite activities.

System Preparation
Before you start implementing the CreateInvoice action, make sure that on the Enable/Disable Features
(CS100000) form, the Advanced SO Invoices feature is enabled.

Step 1: Defining the Action in the Graph

To define the CreateInvoice action in the RSSVWorkOrderEntry graph, do the following:


1. In the RSSVWorkOrderEntry graph, add the following code:

The CreateInvoice action performs an asynchronous operation. For details on


implementing actions, see the T230 Actions training course.

private static void CreateInvoice(RSSVWorkOrder workOrder)


{
using (var ts = new PXTransactionScope())
{
// Create an instance of the SOInvoiceEntry graph.
var invoiceEntry = PXGraph.CreateInstance<SOInvoiceEntry>();
// Initialize the summary of the invoice.
var doc = new ARInvoice()
{
DocType = ARDocType.Invoice
};
doc = invoiceEntry.Document.Insert(doc);
doc.CustomerID = workOrder.CustomerID;
Implementing Workflow Actions | 46

invoiceEntry.Document.Update(doc);

// Create an instance of the RSSVWorkOrderEntry graph.


var workOrderEntry = PXGraph.CreateInstance<RSSVWorkOrderEntry>();
workOrderEntry.WorkOrders.Current = workOrder;

// Add the lines associated with the repair items


// (from the Repair Items tab).
foreach (RSSVWorkOrderItem line in
workOrderEntry.RepairItems.Select())
{
var repairTran = invoiceEntry.Transactions.Insert();
repairTran.InventoryID = line.InventoryID;
repairTran.Qty = 1;
repairTran.CuryUnitPrice = line.BasePrice;
invoiceEntry.Transactions.Update(repairTran);
}
// Add the lines associated with labor (from the Labor tab).
foreach (RSSVWorkOrderLabor line in workOrderEntry.Labor.Select())
{
var laborTran = invoiceEntry.Transactions.Insert();
laborTran.InventoryID = line.InventoryID;
laborTran.Qty = line.Quantity;
laborTran.CuryUnitPrice = line.DefaultPrice;
laborTran.CuryExtPrice = line.ExtPrice;
invoiceEntry.Transactions.Update(laborTran);
}

// Save the invoice to the database.


invoiceEntry.Actions.PressSave();

// Assign the generated invoice number and save the changes.


workOrder.InvoiceNbr = invoiceEntry.Document.Current.RefNbr;
workOrderEntry.WorkOrders.Update(workOrder);
workOrderEntry.Actions.PressSave();

ts.Complete();
}
}

public PXAction<RSSVWorkOrder> CreateInvoiceAction;


[PXButton]
[PXUIField(DisplayName = "Create Invoice", Enabled = true)]
protected virtual IEnumerable createInvoiceAction(PXAdapter adapter)
{
// Populate a local list variable.
List<RSSVWorkOrder> list = new List<RSSVWorkOrder>();
foreach (RSSVWorkOrder order in adapter.Get<RSSVWorkOrder>())
{
list.Add(order);
}

// Trigger the Save action to save changes in the database.


Actions.PressSave();

var workOrder = WorkOrders.Current;


PXLongOperation.StartOperation(this, delegate () {
Implementing Workflow Actions | 47

CreateInvoice(workOrder);
});

// Return the local list variable.


return list;
}

2. Add the following using directives to the RSSVWorkOrderEntry graph.

using PX.Objects.AR;
using PX.Objects.SO;
using System.Collections.Generic;

Step 2: Configuring the Conditional Appearance of the Action

The Create Invoice button on the form toolbar and the equivalent command on the More menu should be
available only if the repair work order has the Pending Payment or Completed status and if no invoice for the
order has been created yet (that is, if the RSSSVWorkOrder.InvoiceNbr field is null). You can configure the
availability by combining two capabilities of Workflow API as follows:
1. Configure a condition that checks whether the InvoiceNbr field is not null, and disable the action when
the condition is true by using the IsDisabledWhen method. Do the following:
a. In the Conditions class, define the condition, as the following code shows.

public Condition DisableCreateInvoice => GetOrCreate(b => b.FromBql<


Where<RSSVWorkOrder.invoiceNbr.IsNotNull>>());

b. In the WithActions method of the screen configuration, register the CreateInvoice action, as the
following code shows.

actions.Add(g => g.CreateInvoiceAction, c => c


.WithCategory(processingCategory)
.IsDisabledWhen(conditions.DisableCreateInvoice));

By using the IsDisabledWhen method, you have specified that the action is disabled when the
provided condition is true.
2. Add the action to only the states where it is available (that is, the PendingPayment and Completed
states), as the following code shows.

.WithActions(actions =>
{
actions.Add(g => g.CreateInvoiceAction, a => a
.IsDuplicatedInToolbar()
.WithConnotation(ActionConnotation.Success));
});

Step 3: Testing the Create Invoice Action

To test the Create Invoice button (and the underlying action), do the following:
1. Rebuild the PhoneRepairShop_Code project.
Implementing Workflow Actions | 48

2. In Acumatica ERP, on the Repair Work Orders (RS301000) form, open the 000003 repair work order you used
for testing in Step 3: Testing the Field Assignment open a repair work order in the Completed status .
The repair work order should have the Completed status.
3. On the form toolbar, click the Create Invoice button.
A notification appears indicating the status of the processing, as shown in the following screenshot.

Figure: Creation of an invoice

When the process is complete, the number of the created invoice is displayed in the Invoice Nbr. box, as
shown in the following screenshot.

Figure: The invoice number of the repair work order


Implementing Workflow Actions | 49

Workflow Actions: To Implement an Action with Field Assignments

This activity will walk you through the process of implementing a workflow action that assigns values to DAC fields
without any graph logic.

Learning Objectives
In this activity, you will learn how to do the following:
• Define a graph action with an empty method
• Add a workflow action to the screen configuration
• Configure field assignments that happen when the action is performed
• Configure the location of a command in a category

Story
Suppose that a user should be able to mark a repair work order as completed when it has the Assigned status. To
do that, the user should be able to click the Complete button. The underlying action will cause the date of the
completion to be inserted in the Date Completed box of the Repair Work Orders (RS301000) form, and the repair
work order to be assigned the Completed status. The Complete action should always be displayed last in the
Processing category.

Process Overview
In your extension library, you will define the Complete action, the Completed state, and the transition from the
Assigned state to the Completed state. You will learn how to define the assignment of the Date Completed box
in a workflow action. You will also specify the location of the Complete command on the More menu.

System Preparation
In an instance with the T100 dataset, make sure that you have done the following:
1. Prepared an instance with the PhoneRepairShop customization project and enabled the workflow validation
by performing the prerequisite activities in the Preparing an Instance for Workflow Customization chapter.
2. Prepared the screen configuration and defined the set of states by performing the prerequisite activities in
the Preparing a Screen Configuration chapter.
3. Defined the Assigned state by performing the Step 3: Defining the State and the Transition prerequisite
activity.

Step 1: Adding and Configuring the Action

In this step, you will add the Complete action in a graph and in the screen configuration. You will specify the
location of the associated command by using the WithCategory method and assign the DAC field values by
using the WithFieldAssignments method.
Do the following:
1. In the RSSVWorkOrderEntry graph, declare the Complete action, as the following code shows.

public PXAction<RSSVWorkOrder> Complete;


Implementing Workflow Actions | 50

[PXButton]
[PXUIField(DisplayName = "Complete", Enabled = false)]
protected virtual IEnumerable complete(PXAdapter adapter) => adapter.Get();

2. In the RSSVWorkOrderWorkflow class, add the Complete action in the lambda expression for the
WithActions method, as the following code shows.

actions.Add(g => g.Complete, c => c


.WithCategory(processingCategory, Placement.Last)
.WithFieldAssignments(fas => fas
.Add<RSSVWorkOrder.dateCompleted>(f =>
f.SetFromToday())));

In the code above, you have defined the Complete action. In the WithCategory method, you have
specified that the associated command will be displayed last in the Processing category of the More menu
by specifying the Placement.Last parameter. Also, in the WithFieldAssignments method, by
calling the SetFromToday method, you have specified that the RSSVWorkOrder.dateCompleted
field is assigned the current business date.
3. To make the Complete action available in the Assigned state, add the action to the configuration of the
Assigned state in the WithFlowStates method, as the following code shows.

fss.Add<States.assigned>(flowState =>
{
return flowState
.WithFieldStates(states =>
{
states.AddField<RSSVWorkOrder.customerID>(state
=> state.IsDisabled());
states.AddField<RSSVWorkOrder.serviceID>(state
=> state.IsDisabled());
states.AddField<RSSVWorkOrder.deviceID>(state
=> state.IsDisabled());
})
////////// The added code
.WithActions(actions =>
{
actions.Add(g => g.Complete, a => a
.IsDuplicatedInToolbar()
.WithConnotation(ActionConnotation.Success));
});
////////// The end of added code
});

Step 2: Adding a State and a Transition

In this step, you will define the Completed state and the transition from the Assigned state to the Completed
state. Do the following:
1. In the lambda expression for the WithFlowStates method, add the definition of the Completed state,
as the following code shows.

fss.Add<States.completed>(flowState =>
{
return flowState
.WithFieldStates(states =>
Implementing Workflow Actions | 51

{
states.AddField<RSSVWorkOrder.customerID>(state
=> state.IsDisabled());
states.AddField<RSSVWorkOrder.serviceID>(state
=> state.IsDisabled());
states.AddField<RSSVWorkOrder.deviceID>(state
=> state.IsDisabled());
});
});

In the state configuration, you have made the CustomerID, ServiceID, and DeviceID fields disabled.
2. In the WithTransitions method, define the transition from the Assigned state to the Completed
state, as the following code shows.

transitions.AddGroupFrom<States.assigned>(ts =>
{
ts.Add(t => t.To<States.completed>().IsTriggeredOn(g
=> g.Complete));
});

3. Rebuild the PhoneRepairShop_Code project.

Step 3: Testing the Field Assignment

In this step, you will make sure that the transition from the Assigned state to the Completed state assigns the
value as planned. Do the following:
1. In Acumatica ERP, open the 000003 repair work order, which you have assigned in Step 4: Testing the Assign
Action,a repair work order with the Assigned status on the Repair Work Orders (RS301000) form.
The repair work order should have the Assigned status.
2. On the form toolbar, click Complete. Notice that the document's status has been changed to Completed and
the Date Completed box has the current business date.
Defining Workflow States | 52

Defining Workflow States


In this chapter, you will learn how to define states of a worfklow. The states in a workflow for an Acumatica ERP
form represent the statuses of a record that is created on this form.

Workflow States: General Information

In this chapterIn this lesson, you will learn how to define and configure states of a workflow by using Workflow API.

Learning Objectives
In this chapterIn this lesson, you will learn how to do the following:
• Define the set of states of the workflow
• Add states to the screen configuration
• Configure states in a workflow

Applicable Scenarios
You define and configure states of a workflow when you are defining a new workflow or customizing an existing
workflow.

Workflow States
Each workflow consists of multiple workflow states. Each state corresponds to a value of the state-identifying field.
Currently, the workflow engine supports only state-identifying fields of the String type that have a predefined
set of allowed values. The values of the state-identifying field are usually defined in the PXStringList attribute.
To define the state-identifying field, you need to call the StateIdentifierIs method for the new screen
configuration and provide the field as the parameter. For details, see Step 3: Overriding the Configure Method.

Adding a Workflow State


To define the set of states, you сall the WithFlowStates method in the lambda expression specified for the
AddDefaultFlow method and provide the set of states in the parameter, as the following code shows. You
define each state by calling the Add method and specifying the state-identifier string as a generic parameter. As a
parameter of the Add method, you specify a lambda expression where you can configure the state.

context.AddScreenConfigurationFor(screen =>
screen.StateIdentifierIs<status>()
.AddDefaultFlow(flow =>
flow.WithFlowStates(fss =>
{
fss.Add<State.hold>(flowState => flowState.IsInitial());
fss.Add<State.open>(flowState => { … });
...
})

To add a state to an existing workflow, you call the Add method in the UpdateDefaultFlow
method inside the UpdateScreenConfigurationFor method.
Defining Workflow States | 53

Each workflow must have one state that is marked as the initial state. It provides the default value of the state-
identifier field for a newly created record. You mark a state as initial by calling the IsInitial method, as shown
in the code above.

Configuring States
You can add, update, or delete states in a screen configuration by calling the Add, Update, or Delete method,
respectively.
Inside the lambda expression passed to the Add or Update method, you configure the state. For each workflow
state, you can do the following:
• Specify that a state should be the initial state of a workflow by calling the IsInitial method.

Only one state can be the initial state of a workflow.

• Specify the properties that the specified DAC fields should have in this state by calling the
WithFieldStates method.
You can specify such properties as requirement, visibility, and availability. Settings specified in the
WithFieldStates method override the parameter values specified in the DAC field attributes.
• Specify which actions are available in this state and configure the appearance of these actions in this
state, such as whether the action should be duplicated on the form toolbar. You do this by calling the
WithActions method.
• Specify which event handlers are available in this state by calling the WithEventHandlers method.
• Specify a list of field assignments that will be performed when a record enters the state and when a record
leaves the state.
To provide a list of fields whose values should be assigned when a record enters the state, use the
flowState.WithOnEnterAssignments method when defining the state. The field assignments listed
in the method are applied to the state for which the method was called.
To provide the list of fields whose values should be assigned to a state when a record leaves a state, use the
flowState.WithOnLeaveAssignments method when defining the state. The field assignments listed
in the method are applied to the state for which the method was called.
An example of using these methods is shown in the following code.
flowStates.Add<State.hold>(flowState =>
{
return flowState
.WithActions(actions => …);
.WithOnEnterAssignments(fields => fields.Add<inclCustOpenOrders>(false));
.WithOnLeaveAssignments(fields => fields.Add<inclCustOpenOrders>(true));
...
}

In the code above, the inclCustOpenOrders field is assigned false when the record state is changed to
hold (aer the transition to this state is completed), and the inclCustOpenOrders field is assigned true
before the hold state is changed to some other state (before the transition is started).

Workflow States: Configuring Field States

You can configure a state of a field in a screen configuration and in a workflow state.
The description of field states in a workflow state is applied aer the system applies the description of field states
in the screen configuration and in the DAC so the configurations described in the workflow state can override other
Defining Workflow States | 54

field state configuration. For example, if a field was visible under a condition in a screen configuration, it can be
made invisible in a workflow state.
For details on configuring the state of a field in the screen configuration, see Configuring Field States .

Adding or Updating a State of a Field in a Workflow State


To add or update a state of a field in a workflow state, you call the WithFieldStates method in a lambda
expression for the Add or Update method that defines a workflow state. Inside the WithFieldStates method,
you can do the following:
• Add a single field or all fields from a specified DAC by using the AddField, AddAllFields, or AddTable
method. When adding a field, you can specify how the workflow will affect this field in this state.
For example, the following code adds fields and tables to a workflow state and specifies how these fields
and tables should be affected (in this case, hidden).
.WithFieldStates(fields => {
fields.AddField<printed>(c => c.IsHidden());
fields.AddField<emailed>(c => c.IsHidden());
fields.AddField<dontEmail>(c => c.IsHidden());
fields.AddField<dontPrint>(c => c.IsHidden());

fields.AddTable<SOOrder>(c => c.IsHidden());


fields.AddTable<SOLine>(c => c.IsHidden());
fields.AddTable<SOLineSplit>(c => c.IsHidden());
fields.AddTable<SOAddress>(c => c.IsHidden());
}

If you need to add multiple fields or multiple tables, you can use the AddFields,
AddAllFieldsFromTables, and AddTables methods. For example, the following code adds the fields
and tables from the previous code example by using only two method calls. If the fields and properties have
the same configuration, such as being hidden, it can be assigned to all of them in a single method.
.WithFieldStates(fields => {
fields.AddFields<BqlFields.FilledWith<printed, emailed, dontEmail, dontPrint>>(c =>
c.IsHidden());
fields.AddTables<BqlTables.FilledWith<SOOrder, SOLine, SOLineSplit, SOAddress>>(c
=>
c.IsHidden());
}

If you need to configure the state of a DAC, use the AddTable or AddTables method.
If you need to configure the states of all fields inside the DAC, use the AddAllFields or
AddAllFieldsFromTables methods. If you use the AddTable or AddTables method,
you can also restrict adding or removing records from the specified tables.

• Update a field state by calling the Update method.


• Replace the entire configuration of the field state by calling the Replace method.
• Delete the entire configuration of the field state by calling the Remove method.

Configuring a State of a Field in a Workflow State


You can configure a field state in the following ways:
• Configure the appearance and requirement of the field (or all fields in a DAC) by calling the following
methods:
Defining Workflow States | 55

• IsHidden
• IsDisabled
• IsRequired
• For a combo box field, specify which values of the combo box field should be available by calling the
ComboBoxValues method.
Only the value defined in the state of a field in a screen configuration can be restricted. For details on
defining the list of combo box values in a screen configuration, see Field States: General Information.
• Specify a default value for the field by calling the DefaultValue method.

Settings a default value is available only for the initial state of a workflow.

An example of configuring field states in a workflow state is shown in the following code.

fss.Add<State.onHold>(flowState =>
{
return flowState
.WithFieldStates(states => {
states.AddField<dontEmail>(state =>
state.DefaultValue(true));
states.AddTable<ARInvoice>(state =>
state.IsDisabled());
});
});

In the example above, field states have been configured in the following ways:
• The dontEmail field has been assigned a default value of true.
• The ARInvoice DAC has been disabled.

Workflow States: Configuring Action States

You can configure an action in the following locations:


• A definition of the action in a graph or in one of its extensions
• A screen configuration
• A workflow state
This topic describes the configuration of actions in a workflow state. For details on configuring actions in a screen
configuration, see Workflow Actions: Configuration of Actions.

Adding or Updating an Action in a Workflow State


To add or update a state of a field in a workflow state, you call the WithActions method in a lambda expression
for the Add or Update method that defines a workflow state. Inside the WithActions method, you can do the
following:
• Add a new action to the workflow state by calling the Add method.

When you add an action in a workflow state, the action will be available in this state unless it is
restricted by the settings defined in the screen configuration,—for example, by the condition in
the IsHiddenWhen method.

• Update an action if it exists in the state of the base workflow by calling the Update method.
Defining Workflow States | 56

• Replace the entire configuration of the action by calling the Replace method.
• Delete the entire configuration of the action by calling the Remove method.

Configuring an Action in a Workflow State


You can configure an action in the following ways:
• Display an action on the form toolbar.
By default, all actions are displayed on the More menu. You can display an action on the form toolbar by
calling the IsDuplicateInToolbar method while adding an action to a screen configuration.
• Specify a connotation for an action by calling the WithConnotation method while adding an action to
the screen configuration. The connotation will be displayed for this action both on the More menu and on
the form toolbar.
You can also cancel the connotation by calling the WithNoConnotation method.
• Specify that the action should be executed automatically when a provided condition is true by calling the
IsAutoAction method.
An example of configuring an action in a workflow state is shown in the following code.

sss.Add<State.hold>(flowState =>
{
return flowState
.WithActions(actions =>
{
actions.Add(g => g.releaseFromHold,
a => a.IsDuplicatedInToolbar()
.WithConnotation(ActionConnotation.Success));
});
});

In the example above, the following changes have been made to the releaseFromHold action:
• The action is displayed on the form toolbar.
• The action is displayed with the Success connotation.

Workflow States: To Define a Workflow State

The following activity will walk you through the process of defining a state.

Story
Suppose that on the Repair Work Orders (RS301000) form, you need to add the On Hold and Ready for
Assignment states to the workflow. The On Hold state should be the initial state of the workflow.

Process Overview
To define the states, in the Configure method of the RSSVWorkOrderWorkflow class, you will сall the
WithFlowStates method in the lambda expression specified for the AddDefaultFlow method and provide
the set of states in the parameter as the following code shows. You will define each state by calling the Add method
and specifying the state identifier string as a generic parameter. As a parameter of the Add method, you will specify
a lambda expression where you can configure the state.
Defining Workflow States | 57

System Preparation
In an instance with the T100 dataset, make sure that you have done the following:
1. Prepared an instance with the PhoneRepairShop customization project and enabled the workflow validation
by performing the prerequisite activities in the Preparing an Instance for Workflow Customization chapter.
2. Prepared the screen configuration and defined the set of states by performing the prerequisite activities in
the Preparing a Screen Configuration chapter.

Step: Defining the On Hold and Ready for Assignment States


To add the OnHold and ReadyForAssignment states, do the following:
1. In the RSSVWorkOrderWorkflow class, in the Configure method, locate the AddDefaultFlow
method. (You have added this method in Step 3: Overriding the Configure Method.)
2. Call the WithFlowStates method, as the following code shows.

.AddDefaultFlow(flow => flow


.WithFlowStates(fss =>
{ }))

3. Inside the lambda expression of the WithFlowStates method, add the OnHold state by calling the Add
method, as the following code shows.

fss.Add<States.onHold>(flowState =>
{
return flowState
.IsInitial()
.WithActions(actions =>
{
actions.Add(g => g.ReleaseFromHold, a => a
.IsDuplicatedInToolbar()
.WithConnotation(ActionConnotation.Success));
});
});

In the code above, you have added the OnHold state by specifying the States.onHold value as the
generic parameter of the Add method. In the lambda expression passed to the Add method, you have done
the following:
• Specified that the OnHold state is the initial state of the workflow by calling the IsInitial method.
• Specified the actions that are available in the OnHold state by calling the WithActions method and
adding the action definition in the lambda expression. In this case, the ReleaseFromHold action is
available in the OnHold state. By calling the IsDuplicatedInToolbar method, you have specified
that the action should be shown as a button on the toolbar for this state.
• Specified the Success connotation for the action by calling the WithConnotation method. The button
corresponding to this action will be highlighted in green when it is displayed on the form toolbar. For
details, see To Add a Connotation.
4. Aer the OnHold state, define the ReadyForAssignment state, as the following code shows.

fss.Add<States.readyForAssignment>(flowState =>
{
return flowState
.WithFieldStates(states =>
{
Defining Workflow States | 58

states.AddField<RSSVWorkOrder.customerID>(state
=> state.IsDisabled());
states.AddField<RSSVWorkOrder.serviceID>(state
=> state.IsDisabled());
states.AddField<RSSVWorkOrder.deviceID>(state
=> state.IsDisabled());
}); ;
});

In the code above, you have added the ReadyForAssignment state by specifying the
States.readyForAssignment value as the generic parameter of the Add method. By calling the
WithFieldStates method, you have specified the states of DAC fields in this states. In this case, you
have specified that the customerID, serviceID, and deviceID fields of the RSSVWorkOrder DAC
should be disabled in this state.
5. Save your changes.
Implementing Transitions | 59

Implementing Transitions
In this chapter, you will learn how to implement transitions in a worfklow. Transitions are used to link the states of
the workflow, and they define the changes that a record proceeds through.

Transitions: General Information

This chapterThis lesson describes how to create different types of transitions by using Workflow API.

Learning Objectives
In this chapterIn this lesson, you will learn how to do the following:
• Define a simple transition
• Define a transition group
• Define a condition pack
• Define transitions that are triggered depending on a condition

Applicable Scenarios
You implement a transition when you need to implement a change of state for a record.

Transitions
In a workflow, all states are linked with transitions. Transitions define the changes of the lifecycle of a record. To
implement a transition, you define the following parts of the screen configuration:
• The initial state of the transition
• The target state of the transition
• The action or event that triggers the transition
• The conditions that are checked before the transition is applied (optional)
• The transition itself

Definition of a Transition
Aer you have defined all the entities required for a transition (the initial state, the target state, and the entity
that triggers the transition), you can add the transition definition to the workflow. You do this by calling the
WithTransitions method in the lambda expression specified for the AddDefaultFlow method and
providing the set of transitions in the parameter. You define each transition by calling the Add method. In the Add
method, you specify a lambda expression in which you specify the following transition parameters:
• From: The initial state of the transition
• To: The target state of the transition
• IsTriggeredOn: The trigger of the transition

Configuring a Transition
You can configure a transition in the following ways:
• Add, update, or delete a transition by calling the Add, Update, or Delete method.
Implementing Transitions | 60

• Specify a condition on which a transition is performed by calling the When method. For an example of this
implementation, see Transitions: To Implement a Group of Transitions.
• Specify the field assignments that are executed if the current transition matches all criteria and is applied to
the current record. You do this by calling the WithFieldAssignments method.

Transitions: To Implement a Simple Transition

This activity will walk you through the process of implementing a transition that is triggered by an action.

Learning Objectives
In this activity, you will learn how to define a transition that is triggered by an action.

Story
Suppose that a user should be able to remove a repair work order from hold when the order has the OnHold status.
The user will be able to do this by clicking the Remove Hold button on the form toolbar or the corresponding
command on the More menu. As a result, the status of the repair work order should be changed to Ready For
Assignment.
You need to implement a simple transition from the OnHold state to the ReadyForAssignment state. This
transition is performed without the system considering the prepayment requirements.

Process Overview
To implement a transition from the OnHold state to the ReadyForAssignment state, you will define the
following components in the screen configuration:
• The OnHold state, which is the source state of the transition.
• The ReadyForAssignment state, which is the target state of the transition.
In this activity, you will use the implementation of these states from Workflow States: To Define a Workflow
State.
• The ReleaseFromHold action, which is the element that triggers the transition.
In this activity, you will use the implementation of the ReleaseFromHold action from Workflow Actions:
To Implement a Simple Action.
• The transition.

System Preparation
In an instance with the T100 dataset, make sure that you have done the following:
1. Prepared an instance with the PhoneRepairShop customization project and enabled the workflow validation
by performing the prerequisite activities in the Preparing an Instance for Workflow Customization chapter.
2. Prepared the screen configuration and defined the set of states by performing the prerequisite activities in
the Preparing a Screen Configuration chapter.
3. Defined the ReleaseFromHold action by performing the Workflow Actions: To Implement a Simple Action
activity.
4. Defined the OnHold and ReadyForAssignment states by performing the Workflow States: To Define a
Workflow State activity.
Implementing Transitions | 61

Step 1: Defining a Transition

In this step, you will define a transition from the OnHold state to the ReadyForAssignment state in the
workflow. Do the following:
1. In the RSSVWorkOrderWorkflow class, in the Configure method, locate the AddDefaultFlow
method, which you have added in Step 3: Overriding the Configure Method.
2. Call the WithTransitions method in the lambda expression for the AddDefaultFlow method, as the
following code shows.

.WithTransitions(transitions =>
{ }))

3. Inside the lambda expression of the WithTransitions method, add the transition by calling the Add
method, as the following code shows.

transitions.Add(t => t.From<States.onHold>()


.To<States.readyForAssignment>()
.IsTriggeredOn(g => g.ReleaseFromHold));

In the code above, you have specified the following:


• The source state of the transition (which is OnHold) as a type parameter of the From method
• The target state of the transition (which is ReadyForAssignment) as a type parameter of the To
method
• The entity that triggers the transition (which is the ReleaseFromHold action) in the
IsTriggeredOn method
4. Save your changes.

Step 2: Testing the Transition

In this step, you will test the transition from the OnHold state to the ReadyForAssignment state. Do the
following:
1. Rebuild the PhoneRepairShop_Code project.
2. In Acumatica ERP, open the Repair Work Orders (RS301000) form.
3. On the form, add a new record, and specify the following settings:
• Customer ID: C000000001
• Service: Battery Replacement
• Device: Nokia 3310
• Description: Battery replacement, Nokia 3310
4. Make sure that the record has the On Hold status and that the Remove Hold button is displayed on the form
toolbar and highlighted in green, as shown in the following screenshot.
Implementing Transitions | 62

Figure: A new record on the Repair Work Orders form

The More menu is not displayed because the workflow includes only one action and this
action fits on the form toolbar. If there was only one workflow action and it could not be
displayed on the form toolbar, then the More menu would be displayed. You will test the More
menu and see the configured category in Exercise 1.1: Implement the PutOnHold Transition.

5. Save the repair work order.


The repair work order is assigned the 000003 number.
6. On the form toolbar, click Remove Hold.
7. Notice that the status of the record has been changed to Ready for Assignment and that the Customer ID,
Service ID, and Device ID boxes are unavailable, as shown in the following screenshot.
Implementing Transitions | 63

Figure: A record with the Ready for Assignment status

Transitions: Transition Groups

You can group transitions by their source states. In this case, you can structure your code and do not have to
specify the source state of the transition of the From parameter. You can group the transitions by calling the
AddGroupFrom method in the lambda expression of the WithTransitions method and adding the transitions
in the same lambda expression. An example is shown in the following code.

.WithTransitions(transitions =>
{
transitions.AddGroupFrom<State.open>(ts => {
ts.Add(t => t.To<State.hold>().IsTriggeredOn(g => g.putOnHold)
.WithFieldAssignments(fas => fas.Add<hold>(true)));
ts.Add(t => t.To<State.confirmed>().IsTriggeredOn(g => g.confirmShipmentAction));
});
})

Transitions: To Implement a Group of Transitions

The following activity will walk you through the process of implementing a group of transitions—that is, a set of
transitions that have the same source state.

Story
Suppose that a user should be able to remove a repair work order from hold when the order has the On Hold status.
The user should be able to do this by clicking the Remove Hold button on the form toolbar or the equivalent
command on the More menu. The status of the repair work order should be changed to Ready for Assignment or
Pending Payment, depending on whether the repair work order requires a prepayment.
Implementing Transitions | 64

Process Overview
In Transitions: To Implement a Simple Transition, you have implemented an unconditional transition from the
OnHold state to the ReadyForAssignment state.
Here you will add a transition from the OnHold state to the PendingPayment state and modify the transition
from the OnHold state to the ReadyForAssignment state. You will also define the conditions on which these
transitions are performed.

Learning Objectives
In this activity, you will learn how to do the following:
• Define a condition pack
• Define a transition group
• Define transitions that are triggered depending on a condition

System Preparation
In an instance with the T100 dataset, make sure that you have done the following:
1. Prepared an instance with the PhoneRepairShop customization project and enabled the workflow validation
by performing the prerequisite activities in the Preparing an Instance for Workflow Customization chapter.
2. Prepared the screen configuration and defined the set of states by performing the prerequisite activities in
the Preparing a Screen Configuration chapter.
3. Defined the OnHold and ReadyForAssignment states by performing the Workflow States: To Define a
Workflow State prerequisite activity.
4. Implemented a transition from the OnHold state to the ReadyForAssignment state by performing the
Transitions: To Implement a Simple Transition activity.

Step 1: Adding a Condition Pack

To implement a transition from the OnHold state to the ReadyForAssignment or PendingPayment state,
based on the properties of the current repair work order, you need to define two conditions: one that is true when
a repair work order requires prepayment, and another that is true when it does not require prepayment. You will
declare these conditions in this step. Each condition will be checked in the corresponding transition.
A repair work order requires prepayment when the service specified for the order requires prepayment. The service
requires prepayment if the Prepayment check box is selected for the service on the Repair Services (RS201000)
form. This means that to check whether a repair work order requires prepayment, you need to check the value of
the prepayment property of the RSSVRepairService DAC record, which has the same serviceID value as
the repair work order.
To declare the conditions, do the following:
1. In the RSSVWorkOrderWorkflow class, add the Conditions region.
2. In the Conditions region, declare a condition pack, as the following code shows.

public class Conditions : Condition.Pack


{ }
Implementing Transitions | 65

3. In the Conditions class, declare a condition that is true when the repair work order requires prepayment,
as the following code shows.

public Condition RequiresPrepayment => GetOrCreate(b => b.FromBql<


Where<Selector<RSSVWorkOrder.serviceID, RSSVRepairService.prepayment>,
Equal<True>>>());

In the code above, you have created a condition by calling the GetOrCreate method and
providing a BQL statement in the lambda expression. In the BQL statement, you have selected the
RSSVRepairService record by using the RSSVWorkOrder.serviceID value and checked whether
the RSSVRepairService.prepayment value equals true.
4. Aer the RequiresPrepayment condition, declare a condition that is true when the repair work order
does not require prepayment, as the following code shows.

public Condition DoesNotRequirePrepayment => GetOrCreate(b => b.FromBql<


Where<Selector<RSSVWorkOrder.serviceID, RSSVRepairService.prepayment>,
Equal<False>>>());

In the code above, you have created the same condition as the previous one, except that you have checked
whether the RSSVRepairService.prepayment value equals false.
5. In the Configure method, create an instance of the Conditions class, as the following code shows.

public override void Configure(PXScreenConfiguration config)


{
var context = config.GetScreenConfigurationContext<RSSVWorkOrderEntry,
RSSVWorkOrder>();

///////// The added line


var conditions = context.Conditions.GetPack<Conditions>(); ...
}

6. Save your changes.

Step 2: Adding the Pending Payment State (Self-Guided Exercise)

To define a transition from the OnHold state to the PendingPayment state, you first need to define the
PendingPayment state. In this step, you will define the PendingPayment state of the workflow as a self-
guided exercise. You have learned how to add a state in Workflow States: To Define a Workflow State.
While defining a state, you need to use the States.pendingPayment class, which you added in Step 2: Defining
the Set of States of the Workflow. In the PendingPayment state, make the Customer ID, Service ID, and Device ID
boxes of the Repair Work Orders (RS301000) form disabled for the state.

Step 3: Grouping Transitions and Adding Conditions to Transitions

In this step, you will group transitions and add a condition to each transition.
To specify the conditions you defined in Step 1: Adding a Condition Pack, do the following:
1. In the lambda expression provided for the WithTransitions method, define a group of transitions, as
the following code shows.

.WithTransitions(transitions =>
{
Implementing Transitions | 66

////////// The modified code


transitions.AddGroupFrom<States.onHold>(ts =>
{
});
})

In the code above, you have defined a group of transitions whose initial state is OnHold.
2. Move the definition of the transition that you added in Step 1: Defining a Transition inside the group, and
remove the call of the From method because it is no longer necessary. The resulting code is shown below.

transitions.AddGroupFrom<States.onHold>(ts =>
{
ts.Add(t => t.To<States.readyForAssignment>()
.IsTriggeredOn(g => g.ReleaseFromHold)
);
});

3. Aer the IsTriggeredOn method call, add the When method call, and specify the
DoesNotRequirePrepayment condition, as the following code shows.

ts.Add(t => t.To<States.readyForAssignment>()


.IsTriggeredOn(g => g.ReleaseFromHold)
.When(conditions.DoesNotRequirePrepayment));

4. Aer this transition, add the new transition from the OnHold state to the PendingPayment state, as the
following code shows. Specify the RequiresPrepayment condition.

ts.Add(t => t.To<States.pendingPayment>()


.IsTriggeredOn(g => g.ReleaseFromHold)
.When(conditions.RequiresPrepayment));

5. Save your changes.

The full code of the section should look as follows.

.WithTransitions(transitions =>
{
////////// The modified code
transitions.AddGroupFrom<States.onHold>(ts =>
{
ts.Add(t => t.To<States.readyForAssignment>()
.IsTriggeredOn(g => g.ReleaseFromHold)
.When(conditions.DoesNotRequirePrepayment));
ts.Add(t => t.To<States.pendingPayment>()
.IsTriggeredOn(g => g.ReleaseFromHold)
.When(conditions.RequiresPrepayment));
});
////////// The end of modified code
})

Step 4: Testing Transitions With Conditions

In this step, you will test transitions that depend on a condition. Do the following:
1. Rebuild the PhoneRepairShop_Code project.
Implementing Transitions | 67

2. In Acumatica ERP, create a new record on the Repair Work Orders (RS301000) form.
3. On the form, specify the following values:
• Customer ID: C000000001
• Service: Liquid Damage
• Device: Nokia 3310
• Description: Liquid Damage, Nokia 3310
4. On the form toolbar, click Remove Hold.
The record is saved and assigned the 000004 number.
5. Notice that the repair work order status has been changed to Pending Payment (in contrast to what has been
tested in Step 2: Testing the Transition), because the service specified for the order requires prepayment. You
can see that the service requires prepayment on the Repair Services (RS201000) form.
Implementing Dialog Boxes | 68

Implementing Dialog Boxes


In this chapter, you will learn how to implement a dialog box that can be shown when a user clicks a button on the
form toolbar or a command on the More menu to invoke a workflow action. In the dialog box, the user provides
particular field values, which can be used in a transition that is triggered by this action.

Dialog Boxes: General Information

A dialog box is a screen configuration block that can be displayed to a user to provide particular field values when a
user clicks an action (a button or a menu command). A dialog box is referred to in code as a form.
This chapterThis lesson describes how to define a dialog box that can be used in a workflow by using Workflow API.

Learning Objectives
In this chapterIn this lesson, you will learn how to do the following:
• Define a dialog box in a workflow
• Specify a dialog box for an action
• Assign values specified in the dialog box to the DAC fields

Applicable Scenarios
You define a dialog box in a workflow when you need a user to provide particular field values when the user clicks a
button or a menu command that corresponds to an action. These field values can later be used in a transition that
is triggered by this action.

Defining of a Dialog Box


To define a dialog box, you should do the following:
1. You define the dialog box in the Configure method by using the context.Forms.Create method.
In the method parameters, you provide the internal name of the dialog box and its configuration. In
the configuration, you provide a lambda expression where you specify the title of the dialog box in the
Prompt method and the fields that should be displayed in the dialog box in the WithFields method. The
following code shows an example of a dialog box definition.

public override void Configure(PXScreenConfiguration config)


{
...
var formClose = context.Forms.Create("FormClose",
form => form.Prompt("Select Reason").WithFields(fields => {
fields.Add("Reason", field => { … });
}));
}

In the code above, the FormClose dialog box with the Select Reason title is declared.
2. In the WithFields method, you add fields by using the Add method, as described above.
3. You register the dialog box in the screen configuration by specifying the dialog box name in the WithForms
method. An example is shown in the following code.

.AddDefaultFlow(flow => flow


Implementing Dialog Boxes | 69

...
.WithForms(forms => forms.Add(formClose)))

Every dialog box has the OK and Cancel buttons. You cannot modify the buttons of a dialog box.

Configuration of the Fields of a Dialog Box


A dialog box can contain the following types of fields:
• A custom check box field, which is defined with the WithCheckBoxField method. An example is shown
in the following code.
fields.Add("InspectionPassed", field => field.WithCheckBoxField());

• A custom combo box field, which is defined with the WithComboBoxField method. The list of combo box
values is defined with the ComboBoxValues method. An example is shown in the following code.
fields.Add("InspectionType", field =>
field.WithComboBoxField().ComboBoxValues(("M", "Manual"),("A", "Auto")));

• A field that copies its state from the specified DAC field. It is defined with the WithSchemaOf method. An
example is shown in the following code.
fields.Add("Reason", field => field.WithSchemaOf<RequestForInformation.reason>();

You can configure the parts of a dialog box in the following ways:
• Specify the label text for each field by calling the Prompt method
• Specify that the value of any field is required by calling the IsRequired method
• Specify the default value of any field by calling one of the following methods:
• DefaultValue
• DefaultExpression
• DefaultValuefromSchemaField
• Specify the location of a button on the dialog box by calling one of the following methods:
• PlaceBefore
• PlaceAfter
• Hide any field by calling one of the following methods:
• IsHiddenWhen
This method hides a field if a condition is met. When using this method, you provide a condition as a
parameter.

The condition specified in the IsHiddenWhen method is not checked during the mass
processing of records.

• IsHiddenAlways
This method hides the field unconditionally. This method may be useful to hide a field in a dialog box of
a custom workflow if this field is included in this dialog box in a predefined workflow.
An example of using these methods is shown in the following code.
.WithForms(forms => {
forms.Update("FormOpen", form =>
form.WithFields(fields =>
{
fields.Add("Reason", field => field.WithSchemaOf<CROpportunity.resolution>()
Implementing Dialog Boxes | 70

.Prompt("Reason")
.IsHiddenWhen(hideReason));

fields.Update("Stage", field => field.WithPrompt("Stage ID").IsHiddenAlways());


}
));
})

In the code above, the FormOpen dialog box has been updated in the following ways:
• The Reason field of the FormOpen dialog box has been added; it will be hidden when the
hideReason condition is True.
• The Stage field is always hidden.
For a combo box field, you can do the following:
• Define a set of combo box values by calling the ComboBoxValues method
• Specify that the set of combo box values of a dialog box field should be taken from a source or target state
by calling the ComboBoxValuesSource method. In the method parameter, you should specify from
where the values should be taken (a source state, a target state, or explicit values specified in the dialog box
definition).
For example, suppose that in some state, a set of values for the CRLead.resolution field is specified.
The workflow includes a transition where this state is a target state, and a dialog box that is shown during
this transition. Further suppose that the dialog box includes the Reason field, whose configuration is
provided by the CRLead.resolution field (through the use of the WithSchemaOf method). To specify
that the values of the Reason field should be taken from the target state of a transition where the dialog
box is used, you should use the following code in the dialog box.
.WithForms(forms => {
forms.Add("FormOpen", form =>
form.WithFields(fields =>
{
fields.Add("Reason", field => field
.WithSchemaOf<CRLead.resolution>()
.IsRequired()
.Prompt("Reason")
.ComboBoxValuesSource(ComboBoxValuesSource.TargetState));
}
));
})

• Add a new value and its display name by calling the ComboBoxValue method.
You should use this method when updating a predefined dialog box field.
• Restrict the set of predefined combo box values by calling the OnlyComboBoxValues method.

Linking a Dialog Box to an Action


To display a dialog box when a user clicks a button that corresponds to an action, you need to specify this dialog
box in the action configuration. You do this by calling the WithForm method inside the lambda expression
provided for the Add method.
To insert data specified by a user in a dialog box field in a DAC field, you need to call the
WithFieldAssignments method. In the lambda expression provided for the method, you add the DAC field by
calling the Add method. In the lambda expression for the Add method, you call the SetFromFormField method
and provide the dialog box name and the dialog box field name as parameters.
An example of an action with a dialog box is shown in the following code.
Implementing Dialog Boxes | 71

actions.Add(g => g.close, c => c


.WithForm(formClose)
.WithFieldAssignments(fields =>
{
fields.Add<RequestForInformation.reason>(f =>
f.SetFromFormField(formClose, "Reason"));
}));

In the code above, the RequestForInformation.reason field has been updated with the value specified in
the Reason field of the formClose dialog box.

The original code of the action is executed only aer the dialog box is closed and field assignments
are applied. If an action triggers a transition, the transition is performed only if a user clicks OK in the
dialog box.

DialogBox: Configuration of the Layout

You can configure the layout of controls in a dialog box in the following ways:
• Split controls into multiple columns by calling the ColumnsCount method in a dialog box configuration.
By default, all controls are displayed in a single column.
• Span multiple columns of a control by calling the ColumnSpan method in the configuration of a dialog box
field.
• Specify the size of a control by calling the ControlSize method in the configuration of a dialog box field.
• Arrange the order of controls by calling the PlaceBefore and PlaceAfter methods in the configuration
of a dialog box field.
These methods may be useful if you are updating a dialog box in a predefined workflow. By default, all fields
are displayed in the order in which they are added to the dialog box.

Dialog Boxes: To Implement a Transition with a Dialog Box

This activity will walk you through the process of creating a dialog box and implementing a transition that uses the
values the user provides in this dialog box.

Story
Suppose that on the Repair Work Orders (RS301000) form, a user should be able to assign a repair work order when
it has the Ready For Assignment status. To do this, the user will click the Assign button on the form toolbar (or the
equivalent command on the More menu) and enter the name of the assignee in the dialog box. When the user clicks
OK, the repair work order will get the Assigned status.

Process Overview
You will define the Assign dialog box in a workflow. To do this, you will define the Assign action, register it in a
workflow, and specify the dialog box for it. You will also define the Assigned state and the transition from the
ReadyForAssignment state to the Assigned state.
Implementing Dialog Boxes | 72

System Preparation
In an instance with the T100 dataset, make sure that you have done the following:
1. Prepared an instance with the PhoneRepairShop customization project and enabled the workflow validation
by performing the activities in the Preparing an Instance for Workflow Customization chapter.
2. Prepared the screen configuration and defined the set of states by performing the activities in the Preparing
a Screen Configuration chapter.
3. Implemented the ReadyForAssignment state by performing the Workflow States: To Define a Workflow
State activity.

Step 1: Defining a Dialog Box

In this step, you will define and configure the Assign dialog box.
Do the following:
1. In the Configure method of the RSSVWorkOrderWorkflow class, define the Assign dialog box, as the
following code shows.

public override void Configure(PXScreenConfiguration config)


{
var context = config.GetScreenConfigurationContext<RSSVWorkOrderEntry,
RSSVWorkOrder>();

////////// The added code


var formAssign = context.Forms.Create("FormAssign", form =>
form.Prompt("Assign").WithFields(fields =>
{ }));
...
}

In the code above, you have declared the FormAssign dialog box with the Select Assignee title.
2. Inside the lambda expression for the WithFields method, add the Assignee field, which will be
displayed in the dialog box.

fields.Add("Assignee", field => field


.WithSchemaOf<RSSVWorkOrder.assignee>()
.IsRequired()
.Prompt("Assignee"));

In the code above, you have added the Assignee field to the dialog box. You have copied the
field configuration from the RSSVWorkOrder.assignee field by specifying this DAC field in the
WithSchemaOf method. You have specified that the field is required by calling the IsRequired method,
and you have specified the UI name of the field in the Prompt method.
3. Register the dialog box in the screen configuration by calling the WithForms method in the lambda
expression for the AddScreenConfigurationFor method, as the following code shows.

.WithForms(forms => forms.Add(formAssign))


Implementing Dialog Boxes | 73

Step 2: Defining the Action That Opens the Dialog Box

In this step, you will add the Assign action to the RSSVWorkOrderEntry graph and register the action in the
screen configuration. Do the following:
1. In the RSSVWorkOrderEntry graph, define the Assign action, as the following code shows.

public PXAction<RSSVWorkOrder> Assign;


[PXButton]
[PXUIField(DisplayName = "Assign", Enabled = false)]
protected virtual IEnumerable assign(PXAdapter adapter) => adapter.Get();

2. In the RSSVWorkOrderWorkflow class, in the lambda expression for the WithActions method at the
screen configuration level, add the Assign action, as the following code shows.

actions.Add(g => g.Assign, c => c


.WithCategory(processingCategory)
.WithForm(formAssign)
.WithFieldAssignments(fields => {
fields.Add<RSSVWorkOrder.assignee>(f =>
f.SetFromFormField(formAssign, "Assignee"));
}));

In the code above, you have added the Assign action to the screen configuration. You have specified the
name of the dialog box for the action in the WithForm method and the DAC field that should be updated
from the dialog box in the WithFieldAssignments method.
3. Save your changes.

Step 3: Defining the State and the Transition

In this step, you will define the Assigned state and the transition from the ReadyForAssignment state to
the Assigned state on your own. You have learned how to define a state in Workflow States: To Define a Workflow
State.

While defining a state, you need to use the States.assigned class, which you added in Workflow States: To
Define a Workflow State. In the Assigned state, make the Customer ID, Service ID, and Device ID boxes disabled
for the state. Also, do not forget to add the Assign action to the ReadyForAssignment state, display it on the
form toolbar, and specify the Success connotation for it.
You have learned how to define a transition in Step 3: Grouping Transitions and Adding Conditions to Transitions.For
details on defining a transition, see Transitions: To Implement a Simple Transition. The transition should be defined
inside a group and is performed without any conditions.

Step 4: Testing the Assign Action

In this step, you will test the Assign button (and the associated action) and the Select Assignee dialog box. Do the
following:
1. Rebuild the PhoneRepairShop_Code project.
Implementing Dialog Boxes | 74

2. In Acumatica ERP, open the 000003 repair work order, which you have created in Step 2: Testing the
Transition,a repair work order with the On Hold status on the Repair Work Orders (RS301000) form.
The repair work order should have the On Hold status because of the testing in Exercise 1.1: Implement the
PutOnHold Transition.
3. On the form toolbar, click Remove Hold.
The status of the repair work order changes to Ready for Assignment. The Assign button is displayed on the
form toolbar, as shown in the following screenshot.

Figure: The Assign button

4. On the form toolbar, click Assign.


The Assign dialog box appears, as shown in the following screenshot.

Figure: The Assign dialog box


Implementing Dialog Boxes | 75

5. In the Assign dialog box, select Andrews, Michael.


6. Click OK.
7. Notice that the status of the record has been changed to Assigned and that the specified assignee is
displayed in the Assignee box.
Implementing Workflow Events | 76

Implementing Workflow Events


In this chapter, you will learn how to use events to trigger a transition of a workflow. You will learn about using
Workflow API to work with existing events and to create new ones.

Workflow Events: General Information

A workflow event is an event that can be used to trigger a transition. Workflow events introduce cross-graph
interactions and are similar to .NET events.
In this chapterIn this lesson, you will learn how to use existing events and create your own events in a custom
workflow by using Workflow API.

Learning Objectives
In this chapterIn this lesson, you will learn how to do the following:
• Explore the code of the predefined workflow to find the event
• Create a custom event handler
• Bind the event handler to an event
• Register an event handler in a particular state
• Create a transition triggered by an event
• Create a custom event
• Override a method to fire the event

Applicable Scenarios
You use workflow events when you need to implement cross-graph interaction for forms that use workflows.
For example, in Acumatica ERP, most of the predefined data entry forms have workflows defined. If the workflow of
a custom form is dependent on transitions that happen on an existing Acumatica ERP form, you can implement the
interaction between workflows by using workflow events.

Defining of a Workflow Event


To define a workflow event, you need to define the following entities:
• The event itself.
An event is an object in code that corresponds to a DAC and has a name; it can be defined anywhere.
However, we strongly recommend that you place it in a nested class in the DAC the event relates to. To
define a new event, you need to create a new class that inherits from the PXEntityEvent.Container
class. As the type parameter of the PXEntityEvent class, you specify the name of the primary DAC of the
form where the event is fired. In the class, you create the event as a field of the PXEntityEvent type.
This class has one or two generic parameters. The first parameter must be the same one that you used in the
class declaration. This parameter shows that this event happens on records of the specified DAC. Also, you
can use the second parameter to provide additional information about the event that occurred. The second
parameter usually holds an additional DAC record that corresponds to the event.
An example of an event declaration is shown in the following code. Among other events, the
InvoiceLinked event is declared. This event shows that the invoice, which is defined as the second type
parameter, is linked with an SOOrderShipment record.
Implementing Workflow Events | 77

public partial class SOOrderShipment : IBqlTable{


public class Events : PXEntityEvent<SOOrderShipment>.Container<Events>{
public PXEntityEvent<SOOrderShipment, SOShipment> ShipmentLinked;
public PXEntityEvent<SOOrderShipment, SOShipment> ShipmentUnlinked;
public PXEntityEvent<SOOrderShipment, SOInvoice> InvoiceLinked;
public PXEntityEvent<SOOrderShipment, SOInvoice> InvoiceUnlinked;
}
}

• An event handler.
You can define an event handler as a member of a graph or a graph extension. You do this by declaring a
member of the PXWorkflowEventHandler type, as the following code example shows.
public PXWorkflowEventHandler<SOShipment, SOOrderShipment, SOInvoice> OnInvoiceLinked;
public PXWorkflowEventHandler<SOShipment, SOInvoice> OnInvoiceReleased;

In the first type parameter, you need to specify the primary DAC to indicate that this handler is used to
manipulate the records of the primary DAC. In the second type parameter, you need to specify the DAC
where the event for this handler is defined. The optional third parameter and can be used to further restrict
the handler to only those events that provide additional information or a record.
• The binding of the event handler to an event.
To bind the event handler to an event, in the screen configuration, you call the WithHandlers method,
and add the handler by using the Add method, as the following code example shows.
.WithHandlers(handlers =>
{
handlers.Add(handler =>
{
return handler
.WithTargetOf<SOOrder>()
.OfEntityEvent<SOOrder.Events>(e => e.ShipmentCreationFailed) //
PXEntityEvent<SOOrder>
.Is(g => g.OnShipmentCreationFailed) // PXWorkflowEventHandler<SOOrder, SOOrder
>
.UsesTargetAsPrimaryEntity()
.DisplayName("Shipment Creation Failed");
});
})

In the OfEntityEvent method, you select the workflow event declared in the DAC. The event must
match the type specified in the WithTargetOf method and the type of the current screen configuration
context. In the Is method, you specify the event handler that matches the event signature. By calling
the UsesTargetAsPrimaryEntity method, you specify that the workflow should use the record on
which the event is fired. For a cross-graph execution, you need to call the UsesPrimaryEntityGetter
method instead and select the DAC object that should be used to get the source document for the event.
• The registration of an event handler in the workflow state where the corresponding event should be fired.
To register an event handler in a workflow state, you call the WithEventHandlers method in the state
definition, as the following code example shows.
flowStates.Add<State.completed>(flowState => {
return flowState
...
.WithEventHandlers(handlers =>
{
handlers.Add(g => g.OnInvoiceUnlinked);
handlers.Add(g => g.OnInvoiceCancelled);
Implementing Workflow Events | 78

})
});

Firing of a Workflow Event


Aer you have declared a workflow event, you need to fire it. To fire a workflow event, you select an event from
the list of events declared in the class derived from the PXEntityEvent class and call the FireOn method. In
the FireOn method parameters, you provide the graph that will be used as a host for event execution, as well as
the instances of DACs that were declared in the PXEntityEvent. An example of firing the InvoiceLinked is
shown in the following code.

public static SOOrderShipment LinkInvoice(this SOOrderShipment self,


SOInvoice invoice, PXGraph graph)
{
if (self is null || invoice is null)
return self;

self.InvoiceType = invoice.DocType;
self.InvoiceNbr = invoice.RefNbr;
self = (SOOrderShipment)graph.Caches<SOOrderShipment>().Update(self);

SOOrderShipment.Events
.Select(e => e.InvoiceLinked) //PXEntityEvent<SOOrderShipment, SOInvoice>
.FireOn(graph, self, invoice);

return self;
}

In the code above, the InvoicedLinked event is selected from the list of SOOrderShipment events and the
FireOn method is called. As parameters, the following have been provided: the instance of the graph that will
be used as a host for event execution, and the instances of SOOrderShipment and SOInvoice DACs that are
linked with each other. The signature of the InvoiceLinked event restricts the FireOn method to an overload
that has these exact parameters.

Triggering of a Transition by an Event


To define a transition that is triggered by an event, you specify the event handler name in the IsTriggeredOn
method of the transition definition, as the following code shows.

transitions.AddGroupFrom<States.completed>(ts =>
{
ts.Add(t => t.To<States.linked>().IsTriggeredOn(g => g.OnInvoiceLinked));
});

Workflow Events: To Use an Existing Event

The following activity will walk you through the process of creating a custom event handler for an existing event
and implementing a transition that is triggered by this event.

Learning Objectives
In this activity, you will learn how to do the following:
Implementing Workflow Events | 79

• Explore the code of the predefined workflow to find the event


• Create a custom event handler
• Bind the event handler to an event
• Register an event handler in a particular state
• Create a transition triggered by an event
• Override the Persist method to save changes in a custom entity

Story
Suppose that on the Repair Work Orders (RS301000) form, a repair work order should be assigned the Paid status
when the invoice created for the order is fully paid—that is, when the invoice is assigned the Closed status. You
need to implement the needed changes for this system behavior.

Process Overview
To change the state of the repair work order from Completed to Paid, you will use an existing event that is
fired when the invoice document is assigned the Closed state. First, you will explore the code of the Invoices
(SO303000) form to find the event that is fired. Then, in your extension library, you will create a custom event
handler for this event, bind it to the workflow on the Repair Work Orders (RS301000) form, and implement the
transition. You will also implement the Paid state and override the Persist method on the Invoices form.

System Preparation
In an instance with the T100 dataset, make sure that you have done the following:
• Prepared an instance with the PhoneRepairShop customization project and enabled the workflow validation
by performing the activities in the Preparing an Instance for Workflow Customization chapter.
• Prepared the screen configuration and defined the set of states by performing the activities in the Preparing
a Screen Configuration chapter.
• Implemented the Completed state by performing the activities in the Workflow States: To Define a
Workflow State chapter.
• Implemented the CreateInvoice action by performing the Workflow Actions: To Configure the Conditional
Appearance of the Action activity.

Step 1: Exploring the Acumatica ERP Source Code

To learn whether you can use the code of a predefined workflow in your customization, you need first to explore
this code. In this step, you will explore the source code of Acumatica ERP and search for the event that you can use.
To change the status of a repair work order whose invoice has been paid, you need to use an event that is fired
when the invoice is fully paid and closed. The change of the invoice's status occurs as a result of the release of
a payment. Therefore, to find the event, you should first learn the code location of the Release action on the
Payments and Applications (AR302000) form. In this step, you will locate the Release action.

To locate the Release action, do the following:


1. In Acumatica ERP, open the Payments and Applications form.
2. To inspect the Release command on the More menu, press Ctrl + Alt, and click Release.
3. In the Element Properties dialog box, which opens, learn the name of the graph where the action is
defined: ARPaymentEntry.
4. Click Actions > View Business Logic Source.
Implementing Workflow Events | 80

The Source Code browser opens.


5. In the Graph Name box, learn the full name of the graph: PX.Objects.AR.ARPaymentEntry.
You will need this name later to find the graph in the source code.

Step 2: Preparing a Project for Debugging

To find the event that you can use in the custom workflow, it is helpful to debug the Acumatica ERP source
code with breakpoints and see which breakpoint is hit in which scenario. In this step, you will prepare the
PhoneRepairShop_Code for debugging in Visual Studio.
To prepare the PhoneRepairShop_Code project for the debugging of the Acumatica ERP code, you should do
the following:
1. Make sure the Acumatica program database (PDB) files are located in the Bin folder of the Acumatica ERP
instance folder that you are using for this training coursethis activity (for example, in PhoneRepairShop
\Bin).
The PDB files were copied to the Files\Bin folder of the Acumatica ERP installation folder (such as
C:\Program Files\Acumatica ERP\Files\Bin) during the installation process if the Install
Debugger Tools check box was selected in the Acumatica ERP Installation wizard. With the check box
selected when you create a new instance or update an existing one, the PDB files are copied to the Bin
folder of the instance. If you did not select the Install Debugger Tools check box during installation, you
should remove Acumatica ERP and install it again with the Install Debugger Tools check box selected. For
details, see To Install the Acumatica ERP Tools.

A PDB file holds debugging and project state information that allows incremental linking of a
debug configuration of your program. In general, a PDB file contains the link between compiler
instructions and some lines in source code.

2. Configure the web.config file of the instance by doing the following:


a. In the file system, open in the text editor the web.config file, which is located in the root folder of the
PhoneRepairShop instance.
b. In the <system.web> tag of the file, locate the <compilation> element.
c. Set the debug attribute of the element to True, as shown in the following code.

<system.web>
<compilation debug="True" ...>

d. Save your changes.


3. Configure the PhoneRepairShop_Code project for debugging by doing the following:
a. In Visual Studio, open the PhoneRepairShop_Code solution, which includes both the
PhoneRepairShop_Code project and the PhoneRepairShop website.
b. On the main menu, select Tools > Options.
c. In the Debugging > General section, clear the Enable Just My Code check box, as shown in the
following screenshot.
Implementing Workflow Events | 81

Figure: The cleared Enable Just My Code check box

d. In the Debugging > Symbols section, in the Symbols file (.pdb) locations list, add the path to the
location of the PDB files that you discovered in Instruction 1 of this step, such as C:\Training
\PhoneRepairShop\Bin.
e. Click OK.
4. Open the Acumatica ERP source code files. For the PhoneRepairShop instance, all files are located in the
PhoneRepairShop/App_Data/CodeRepository folder.
5. To view the source code of the Release action of the Payments and Applications (AR302000) form, open
the PX.Objects.AR.ARPaymentEntry graph: In the Solution Explorer, select PhoneRepairShop >
App_Data > CodeRepository > PX.Objects > AR > ARPaymentEntry.cs, and go to the definition of the
Release action—that is, the IEnumerable Release(PXAdapter adapter) method. The code
should look as shown in the following screenshot.

Figure: The source code of the Release action

6. Add a breakpoint inside the Release method, as shown in the screenshot above.
7. Attach the Visual Studio debugger to a running process.
8. Start debugging by doing the following:
a. In Acumatica ERP, open the Payments and Applications form.
b. Create a payment.
Implementing Workflow Events | 82

c. On the form toolbar, click the Release button.


Wait until the breakpoint is hit.
d. In Visual Studio, view the debug information for the Release method.

If an invoice was created for a repair work order with only non-stock items, by default, an AR invoice
would be created instead of the SO invoice. There is no difference for the release of a payment for AR
and SO invoices, so you don't need to customize the closing of AR invoices as well.

Step 3: Exploring and Debugging the Code

Now that you have prepared the PhoneRepairShop_Code project for debugging in Visual Studio, you can
continue exploring the code of the Release action to find the event that you should use in the custom workflow.
Do the following:
1. In the code of the Release method, find the call of the ARDocumentRelease.ReleaseDoc method.
The ReleaseDoc static method of the ARDocumentRelease static class is used to process the list of
records in an invoice which are displayed on the Details tab of the form.

You may notice that the ReleaseDoc operation starts within the StartOperation static
method of the PXLongOperation static class to be executed in a background thread.

2. Go to the definition of the ARDocumentRelease.ReleaseDoc method, which is in the


ARDocumentRelease.cs file.
Notice that for the created instance, the ReleaseDoc method invokes the
ARReleaseProcess.ReleaseDocProc static method, which is applied for each document in the list of
the documents to be released.
3. Go to the definition of the ARReleaseProcess.ReleaseDocProc method.
Notice that the ARReleaseProcess.ReleaseDocProc method receives the document as a record of
the ARRegister data access class. During the document processing, the method creates a working copy
of the record, specifies the fields of the record in the copy, and then restores the record in the cache from
the completely ready copy. Inside the method, you can find the call to the ProcessPayment method.
4. Go to the definition of the ARReleaseProcess.ProcessPayment method, and explore its code. The
method processes the release of a payment.
Continue exploring the methods that are called inside the ARReleaseProcess.ProcessPayments
method. You can find the ARReleaseProcess.CloseInvoiceAndClearBalances method. The call
hierarchy is shown in the following screenshot.
Implementing Workflow Events | 83

Figure: The call hierarchy for the CloseInvoiceAndClearBalances method

Notice the following line in the code of the CloseInvoiceAndClearBalances method.

RaiseInvoiceEvent(ardoc, PXEntityEventBase<ARInvoice>.Container<ARInvoice.Events>
.Select((ARInvoice.Events ev) => ev.CloseDocument));

If you go to the definition of the RaiseInvoiceEvent method, you can find that the CloseDocument
event is fired in this method, as shown in the following line of the method.

invEvent.FireOn(this, (ARInvoice)doc);

5. To make sure that CloseDocument is the event that changes the invoice status to Closed, do the
following:
a. Navigate to the ARInvoiceEntry_Workflow.cs file, which contains the definition of the workflow
for the Invoices (SO303000) form.
b. In the WithHandlers method, locate the handler for the CloseDocument event. The handler's name
is OnCloseDocument.
c. Locate the transition from the Open state to the Closed state. Notice that the transition is triggered by
the OnCloseDocument event handler, as shown in the following screenshot.

Figure: Transitions from the Open state

6. To make sure that the firing of the CloseDocument event in the


ARReleaseProcess.CloseInvoiceAndClearBalances method indeed happens when an invoice
is fully paid, do the following:
a. Add a breakpoint to the line that fires the OnCloseDocument event of the
ARReleaseProcess.CloseInvoiceAndClearBalances method.
b. Prepare a payment for debugging by doing the following:
Implementing Workflow Events | 84

a. Open the invoice you created in Workflow Actions: To Configure the Conditional Appearance of the
Action by searching for its number on the Invoices form. The invoice should have the INV000049
number.
b. On the form toolbar of the Invoices form, click Remove Hold.
The invoice is assigned the Balanced status.
c. On the form toolbar, click Release. The invoice is released.
d. On the More menu, click Pay.
The Payments and Applications (AR302000) form opens.
c. On the form toolbar of the Payments and Applications form, click Remove Hold, and then click Release.
d. In Visual Studio, notice that a breakpoint in the
ARReleaseProcess.CloseInvoiceAndClearBalances method was hit when an invoice was
closed.
e. Stop debugging.

Step 4: Defining the Paid State (Self-Guided Exercise)

To define a transition from the Completed state to the Paid state, you first need to define the Paid state. In this
step, you will define the Paid state of the workflow as a self-guided exercise. You have learned how to add a state
in Workflow States: To Define a Workflow State.
While defining a state, you need to use the States.paid class, which you added in Step 2: Defining the Set of
States of the Workflow. In the Paid state, make the Customer ID, Service ID, and Device ID boxes disabled.

Step 5: Defining the Event Handler

In this step, you will define an event handler for the ARInvoiceEntry.OnCloseDocument event. Do the
following:
1. In the RSSVWorkOrderEntry graph, define an event handler, as the following code shows.

#region Workflow Event Handlers


public PXWorkflowEventHandler<RSSVWorkOrder, ARInvoice> OnCloseDocument;
#endregion

In the first type parameter of the handler, you have specified the primary DAC of the form where the handler
is used. As the second type parameter, you have specified the primary DAC of the form where the event is
fired.

Add the using directive for the PX.Data.WorkflowAPI namespace if it has not been
added earlier.

2. In the RSSVWorkOrderWorkflow class, bind the OnCloseDocument event handler to the


CloseDocument event in the screen configuration: Aer the AddDefaultFlow method, call the
WithHandlers method. In the method, add the OnCloseDocument event handler, as the following
code shows.

.WithHandlers(handlers =>
{
handlers.Add(handler => handler
.WithTargetOf<ARInvoice>()
Implementing Workflow Events | 85

.OfEntityEvent<ARInvoice.Events>(e => e.CloseDocument)


.Is(g => g.OnCloseDocument)
.UsesPrimaryEntityGetter<
SelectFrom<RSSVWorkOrder>.
Where<RSSVWorkOrder.invoiceNbr
.IsEqual<ARRegister.refNbr.FromCurrent>>>());
})

In the code above, you have added a handler for an event that changes the state of the invoice. In the
UsesPrimaryEntityGetter method, you have selected the repair work order whose state should be
updated by the number of the invoice that has been closed.

Make sure that you have added the using directives, which are shown in the following code.

using PX.Objects.AR;
using PX.Data.BQL.Fluent;

3. In the definition of the Completed state, add the event handler using the WithEventHandlers
method, as the following code shows.

fss.Add<States.completed>(flowState =>
{
return flowState
.WithFieldStates(states =>
{
states.AddField<RSSVWorkOrder.customerID>(state =>
state.IsDisabled());
states.AddField<RSSVWorkOrder.serviceID>(state =>
state.IsDisabled());
states.AddField<RSSVWorkOrder.deviceID>(state =>
state.IsDisabled());
})
.WithActions(actions =>
{
actions.Add(g => g.CreateInvoiceAction, a => a
.IsDuplicatedInToolbar()
.WithConnotation(ActionConnotation.Success));
})
////////// The added code
.WithEventHandlers(handlers =>
{
handlers.Add(g => g.OnCloseDocument);
});
////////// The end of added code
});

4. In the lambda expression for the WithTransitions method, add the transition from the Completed
state to the Paid state, as the following code shows.

transitions.AddGroupFrom<States.completed>(ts =>
{
ts.Add(t => t.To<States.paid>()
.IsTriggeredOn(g => g.OnCloseDocument));
});

5. Save your changes.


Implementing Workflow Events | 86

Step 6: Overriding the Persist Method

The Persist method of any graph saves changes to the database. In this activity, you are customizing an
existing workflow to update another entity. For the changes to be saved to the database, you need to override the
Persist method of the form where you are customizing the workflow and specify which changes should be saved
to the database.
In this step, you will override the Persist method of the ARReleaseProcess graph so that the changes to a
repair work order that are triggered by the event on the Invoices (SO303000) form are saved to the database. Do the
following:
1. In the PhoneRepairShop_Code project, create a file named ARReleaseProcess.cs based on a C#
template.
2. In the ARReleaseProcess.cs file, declare the ARReleaseProcess graph extension, as the following
code shows.

public class ARReleaseProcess_Extension : PXGraphExtension<ARReleaseProcess>


{
}

Add the using directives shown in the following code.

using PX.Data;
using PX.Data.BQL.Fluent;

3. Use Acuminator to suppress the PX1016 error in a comment. In this activity, for simplicity, the extension is
always active.
4. In the ARReleaseProcess_Extension, add the following code.

public SelectFrom<RSSVWorkOrder>.View UpdWorkOrder;

[PXOverride]
public virtual void Persist(Action baseMethod)
{
using (PXTransactionScope ts = new PXTransactionScope())
{
baseMethod();
UpdWorkOrder.Cache.Persist(PXDBOperation.Update);
ts.Complete(Base);
}
UpdWorkOrder.Cache.Persisted(false);
}

Add the PX.Data.BQL.Fluent namespace in the using directive.

In the code above, you have declared a view for repair work orders and overridden the Persist method.
In the overridden method, you have done the following: opened a transaction that saves changes to the
database, called the base method, saved your changes in PXCache to the database, and completed the
transaction. Finally, you have called the Persisted method to mark the saving of changes to the database
as completed. For details, see Persisted Method.
5. Save your changes.
Implementing Workflow Events | 87

Step 7: Testing the Transition

To test the transition from the Completed state to the Paid state, do the following:
1. Rebuild the PhoneRepairShop_Code project.
2. In Acumatica ERP, on the Repair Work Orders (RS301000) form, create a repair work order with the following
settings:
• Customer ID: C000000001
• Service: Battery Replacement
• Device: Nokia 3310
• Description: Battery replacement, Nokia 3310
3. Save the record.
The repair work order is assigned the 000005 number.
4. Create an invoice for the order as follows:
a. On the form toolbar, click Remove Hold and then Assign.
b. In the Assign dialog box, select Andrews, Michael in the Assignee box, and click OK.
c. On the form toolbar, click Complete.
d. On the form toolbar, click Create Invoice.
Notice the invoice number in the Invoice Nbr. box.
5. In Acumatica ERP, on the Repair Work Orders (RS301000) form, open a repair work order that has the
Completed status.
6. On the form toolbar, click Create Invoice.
Note the invoice number shown in the Invoice Nbr box.
7. On the Invoices (SO303000) form, open the invoice created in the previous instruction.
8. On the form toolbar, click Remove Hold and then Release.
The invoice is assigned the Open status.
9. On the More menu, click Pay.
The Payments and Applications (AR302000) form opens.
10.On the form toolbar, click Remove Hold and then Release.
The invoice is now fully paid.
11.On the Repair Work Orders (RS301000) form, open the 000005 repair work order for which you created the
invoice in Instruction 3. Make sure that it has the Paid status, as shown in the following screenshot.
Implementing Workflow Events | 88

Figure: The repair work order with the Paid status

Workflow Events: To Create a New Event

This activity will walk you through the process of creating a custom workflow event and implementing a transition
by using this event.

Learning Objectives
In this activity, you will learn how to do the following:
• Derive a value of a field and copy it to a custom field
• Explore the code of the predefined workflow to find where an event should be fired
• Create a custom event
• Fire a custom event in a graph extension
• Create a custom event handler
• Bind the event handler to an event
• Register an event handler in a particular state
• Create a transition triggered by an event
• Override the Persist method to save changes in a custom entity

Story
Suppose that a repair work order should be assigned the Ready for Assignment status when the invoice created for
the order is prepaid in the specified percent. For a repair work order to be prepaid, a user will first create an invoice
for this order and then apply a prepayment to the invoice. If the percent of the applied prepayment is greater than
or equal to the required prepayment percentage, which is specified on the Payments and Applications (AR302000)
Implementing Workflow Events | 89

form, the system will change the status of the order from Pending Payment to Ready for Assignment to indicate that
the prepayment is applied. You need to implement the needed changes for this system behavior.

Process Overview
To implement the needed state (status) change, in your extension library, you will define a new event and fire it
when an invoice is prepaid. You will define the event handler, register it in the screen configuration for your custom
workflow, and configure a transition.

System Preparation
In an instance with the T100 dataset, make sure that you have done the following:
• Prepared an instance with the PhoneRepairShop customization project and enabled the workflow validation
by performing the prerequisite activities in the Preparing an Instance for Workflow Customization chapter.
• Prepared the screen configuration and defined the set of states by performing the prerequisite activities in
the Preparing a Screen Configuration chapter.
• Implemented the PendingPayment and ReadyForAssignment states by performing the Workflow
States: To Define a Workflow State prerequisite activity.
• Implemented the CreateInvoice action by performing the Workflow Actions: To Configure the Conditional
Appearance of the Action prerequisite activity.

Step 1: Creating a Custom Field

In Acumatica ERP, a user can create a payment for an invoice by clicking the Pay button or command on the
Invoices (SO303000) form. When the payment is created, it is opened on the Payments and Applications (AR302000)
form.
The workflow for the Battery Replacement service involves one payment, which is made upon completion of the
work. Conversely, the workflow for the Liquid Damage service involves both a prepayment before the repair work is
assigned and a final payment aer the work is complete.
For the creation of the prepayment, you now need to have the default prepayment percentage for the payment
displayed on the Payments and Applications form to facilitate the entry of the prepayment amount, and to make
it possible for a user to change that percentage for the current payment. To do that, you need to derive the value
of the Prepayment Percent element on the Repair Work Order Preferences (RS101000) form and assign it to the
corresponding custom field of the Payments and Applications form.
You will create the custom field in this step and derive the field value in the next step.

Adding a Custom Field to the Payments and Applications Form


To display and modify the prepayment percentage on the Payments and Applications (AR302000) form, you need to
add the Prepayment Percent box to the form. Complete the following general tasks:
1. By using the Element Inspector, learn the name of the DAC and graph that define the Summary area of the
Payments and Applications form. In this case, the DAC is ARPayment and the graph is ARPaymentEntry.
You need the DAC name to know which database table and DAC to extend. You will need the graph name in
the next step.
2. Add a column named UsrPrepaymentPercent to the ARPayment table with the same parameters as
are specified for the PrepaymentPercent field of the RSSVSetup table. The data type of the column is
decimal(9, 6).
3. Create an extension of the ARPayment DAC, and add the UsrPrepaymentPercent field to the
extension.
Implementing Workflow Events | 90

If you create a DAC extension by using the Customization Project Editor, it creates an extension
of the base DAC. So in this case, the system will create an extension of the ARRegister DAC
because the ARRegister DAC is the base DAC for the ARPayment DAC.

The code for the field is shown below.

#region PrepaymentPercent
[PXDBDecimal()]
[PXDefault(TypeCode.Decimal, "0.0", PersistingCheck = PXPersistingCheck.Nothing)]
[PXUIField(DisplayName = "Prepayment Percent")]
public Decimal? UsrPrepaymentPercent { get; set; }
public abstract class usrPrepaymentPercent :
PX.Data.BQL.BqlDecimal.Field<usrPrepaymentPercent> { }
#endregion

4. Add a box for the UsrPrepaymentPercent field to the Summary area of the Payments and Applications
form. On the Customized Screens page, the location of the element appears as shown in the following
screenshot.

Figure: Prepayment Percent element

5. Correct the width for the Prepayment Percent label. To do this, in the Column element that is the parent
to the Prepayment Percent element, for the LabelsWidth property, specify the M value.
6. Publish the customization project.
Implementing Workflow Events | 91

Step 2: Deriving the Value of the Field

You can implement the deriving of a field value from the RSSVSetup DAC and the copying of it to the ARPayment
DAC by using one of the following:
• The FieldDefaulting event
• The PXDefault attribute
To populate the UsrPrepaymentPercent field of the ARPayment extension when a payment is created, you
can use the FieldDefaulting event. Do the following:
1. Create an extension of the ARPaymentEntry graph, as shown in the following code.
You learned the name of the graph to extend in Instruction 1 of the previous step.

namespace PX.Objects.AR
{
public class ARPaymentEntry_Extension : PXGraphExtension<ARPaymentEntry>
{
}
}

2. Add the following using directives.

using PX.Data;
using PX.Data.BQL.Fluent;
using PhoneRepairShop;

3. Use Acuminator to suppress the PX1016 error in a comment. In this course, for simplicity, the extension is
always active.
4. Define the FieldDefaulting event handler for the UsrPrepaymentPercent field of the
ARPayment extension, as shown in the following code.

public virtual void _(Events.FieldDefaulting<ARPayment,


ARPaymentExt.usrPrepaymentPercent> e)
{
ARPayment payment = (ARPayment)e.Row;
RSSVSetup setupRecord = SelectFrom<RSSVSetup>.View.Select(Base);
if (setupRecord != null)
{
e.NewValue = setupRecord.PrepaymentPercent;
}
}

In the code above, you have selected the record with the repair work order preferences and assigned the
PrepaymentPercent field value to the UsrPrepaymentPercent field of the ARPayment DAC. You
have checked for the null value of setupRecord so that the NullReferenceException exception is
not thrown if the data on the form has not been filled in yet.

In the event handler, specify ARRegisterExt.usrPrepaymentPercent instead of


ARPaymentExt.usrPrepaymentPercent if the usrPrepaymentPercent field
belongs to the ARRegisterExt DAC extension.

Another way to derive the default value is to use the PXDefault attribute, which performs the same logic. If you
use this approach, the PXDefault attribute for the UsrPrepaymentPercent field should look as follows.
Implementing Workflow Events | 92

[PXDefault(typeof(Select<RSSVSetup>), SourceField = typeof(RSSVSetup.prepaymentPercent),


PersistingCheck = PXPersistingCheck.Nothing)]

This approach provides the following advantages:


• You do not need to create a graph extension.
• Your logic is written in declarative style.

You need to specify the SourceField parameter if the field names are not identical.

Step 3: Exploring the Acumatica ERP Source Code

In this step, you will explore the source code of the Invoices (SO303000) and Payments and Applications (AR302000)
forms of Acumatica ERP. You will explore the code of these forms to learn the field names and methods you
need to use in order to determine where and when to fire the event that triggers the transition from the
PendingPayment state to the ReadyForAssignment state.

Learning Field Names


To determine whether an invoice has been prepaid and whether the event that triggers the transition from
the PendingPayment state to the ReadyForAssignment state should be fired, you need to calculate the
percentage of the invoice amount that has been prepaid.
To calculate this percentage, you need the outstanding amount of the invoice and the original amount of the
invoice. These values are displayed in the Balance and Amount boxes, respectively, of the Invoices (SO303000)
form. Thus, to calculate the percentage, you should learn the field names of these boxes by doing the following:
1. In Acumatica ERP, open the Invoices form.
2. To open the Element Inspector for the Balance box, press Ctrl + Alt and click the box.
In the Element Properties dialog box, which opens, notice that the Balance box is defined by the
CuryDocBal field of the ARInvoice data access class.
3. To open the Element Inspector for the Amount box, press Ctrl + Alt and click the box.
In the Element Properties dialog box, which opens, notice that the Amount box is defined by the
CuryOrigDocAmt field of the ARInvoice data access class.

If you do not see the Amount box on the form, you need to select the Validate Document
Totals on Entry check box on the Accounts Receivable Preferences(AR101000) form, open or
refresh the Invoices form, and then perform this instruction.

Exploring the Source Code of the Release Method


To fire the event that triggers the transition from the PendingPayment to ReadyForAssignment state,
you need to first override the method where the outstanding amount of the invoice (which you just explored) is
calculated and updated. This recalculation is performed on release of the prepayment that is applied to an invoice.
Thus, to find the method to override, you need to explore the code of the action that is associated with the Release
button on the Payments and Applications (AR302000) form.
You have already started exploring the methods invoked in the code of the Release action in Step 3: Exploring
and Debugging the Code. You might have noticed in the code of the Release button that the amounts of the
invoice If you explore the code of the Release button (as described in Step 3: Exploring and Debugging the Code),
you can notice that the amounts of the invoice, including the CuryDocBal field value, are recalculated in the
Implementing Workflow Events | 93

UpdateBalances method of the ARReleaseProcess class. To make sure this is the right method to override,
add a breakpoint to the UpdateBalances method, run the application in debug mode, and trace how the
CuryDocBal value is changed in the method.
As a result of this debugging, you can see that UpdateBalances is the method that you should override
to calculate the prepaid percentage of the invoice and to fire the event that triggers the transition from the
PendingPayment state to the ReadyForAssignment state.

Step 4: Declaring the Event

In this step, you will declare the event that triggers the transition from the PendingPayment state to the
ReadyForAssignment state, define an event handler for this event, and register the event handler in the screen
configuration. You will also define the transition triggered by the event.
Do the following:
1. In the RSSVWorkOrder DAC, declare the class that contains the custom event, as the following code
shows.

public class MyEvents : PXEntityEvent<ARRegister>.Container<MyEvents>


{
public PXEntityEvent<ARRegister> InvoiceGotPrepaid;
}

Make sure that you have added the PX.Data.WorkflowAPI using directive.

2. In the RSSVWorkOrderEntry graph, declare an event handler for the InvoiceGotPrepaid event, as
the following code shows.

public PXWorkflowEventHandler<RSSVWorkOrder, ARRegister> OnInvoiceGotPrepaid;

3. In the RSSVWorkOrderWorkflow class, bind the OnInvoiceGotPrepaid event handler to


the InvoiceGotPrepaid event in the screen configuration: In the lambda expression for the
WithHandlers method, add the handler, as the following code shows.

handlers.Add(handler => handler


.WithTargetOf<ARRegister>()
.OfEntityEvent<RSSVWorkOrder.MyEvents>(e => e.InvoiceGotPrepaid)
.Is(g => g.OnInvoiceGotPrepaid)
.UsesPrimaryEntityGetter<
SelectFrom<RSSVWorkOrder>.
Where<RSSVWorkOrder.invoiceNbr
.IsEqual<ARRegister.refNbr.FromCurrent>>>());

4. In the definition of the PendingPayment state, add the event handler using the WithEventHandlers
method, as the following code shows.

fss.Add<States.pendingPayment>(flowState =>
{
return flowState
.WithFieldStates(states =>
{
states.AddField<RSSVWorkOrder.customerID>(state
=> state.IsDisabled());
states.AddField<RSSVWorkOrder.serviceID>(state
=> state.IsDisabled());
Implementing Workflow Events | 94

states.AddField<RSSVWorkOrder.deviceID>(state
=> state.IsDisabled());
})
.WithActions(actions =>
{
actions.Add(g => g.PutOnHold,
a => a.IsDuplicatedInToolbar());
actions.Add(g => g.CreateInvoiceAction, a => a
.IsDuplicatedInToolbar()
.WithConnotation(ActionConnotation.Success));
})
////////// The added code
.WithEventHandlers(handlers =>
{
handlers.Add(g => g.OnInvoiceGotPrepaid);
});
////////// The end of added code
});

5. In the lambda expression for the WithTransitions method, declare the transition
from the PendingPayment state to the ReadyForAssignment state in the
transitions.AddGroupFrom<States.pendingPayment> group, as the following code shows.

ts.Add(t => t.To<States.readyForAssignment>()


.IsTriggeredOn(g => g.OnInvoiceGotPrepaid));

6. Save your changes.

Step 5: Firing the Event

As you learned in Step 3: Exploring the Acumatica ERP Source Code, you need to override the UpdateBalances
method to fire the event that triggers the transition from the PendingPayment state to the
ReadyForAssignment state. However, you should first calculate the prepaid percentage value based on the
values of the Balance and Amount boxes of an invoice on the Invoices (SO303000) form. These values have been
calculated in the base UpdateBalances method.

You can work with the fields of the ARRegister adjddoc parameter instead of selecting the
corresponding ARInvoice parameter because ARRegister is the base class for the ARInvoice
class and contains the same essential fields.

The ARRegister entity has been modified in the base method and its value has been updated in PXCache.
But the value passed as the parameter to the overridden method has not been updated. Thus, in the overridden
method, you should use the cached value. To get the cached value of the entity, you need to use the Locate
method. For details, see Locate(Object) Method.

Firing the Event


To fire the event that triggers the transition between states, add the following code to the
ARReleaseProcess_Extension class of the PhoneRepairShop_Code project.
public delegate void UpdateBalancesDelegate(ARAdjust adj,
ARRegister adjddoc, ARTran adjdtran);
[PXOverride]
public virtual void UpdateBalances(ARAdjust adj,
ARRegister adjddoc, ARTran adjdtran,
Implementing Workflow Events | 95

UpdateBalancesDelegate baseMethod)
{
baseMethod(adj, adjddoc, adjdtran);

ARRegister ardoc = adjddoc;


ARRegister cached = (ARRegister)Base.ARDocument.Cache.Locate(ardoc);
if (cached != null)
{
ardoc = cached;
}

RSSVWorkOrder order = SelectFrom<RSSVWorkOrder>.


Where<RSSVWorkOrder.invoiceNbr.
IsEqual<ARRegister.refNbr.FromCurrent>>
.View.SelectSingleBound(Base, new[] { ardoc });

if (order != null &&


order.Status == WorkOrderStatusConstants.PendingPayment)
{
var payment = SelectFrom<ARPayment>.
Where<ARPayment.docType.
IsEqual<ARAdjust.adjgDocType.FromCurrent>.
And<ARPayment.refNbr.
IsEqual<ARAdjust.adjgRefNbr.FromCurrent>>>
.View.SelectSingleBound(Base, new[] { ardoc });

if (payment != null)
{
var paidPercent = (ardoc.CuryOrigDocAmt - ardoc.CuryDocBal) * 100
/ ardoc.CuryOrigDocAmt;
var paymentExt = PXCache<ARPayment>.
GetExtension<ARPaymentExt>(payment);
if (paidPercent >= paymentExt.UsrPrepaymentPercent)
{
RSSVWorkOrder.MyEvents
.Select(e => e.InvoiceGotPrepaid)
.FireOn(Base, ardoc);
// No need to call the Persist method.
}
}
}
}

In the code above, first you have called the base method, and then you have obtained the cached value of
the invoice. You have selected the repair work order with the same invoice number as the invoice passed as a
parameter. Then you have selected the prepayment and its extension (which contains the prepayment percent)
and calculated the prepaid percentage for the invoice. If the prepaid percentage is greater than the required
percentage, you have fired the InvoiceGotPrepaid event.

Both SO and AR payments and SO and AR invoices should be selected from the ARPayment and
ARInvoice tables, respectively. To avoid selecting a document of the wrong type, when you select
payments or invoices, you should check not only the refNbr of the document but also its docType.

At the end of the method, there is no need to call the Persist method, because it is called at the end of the
release process.
Implementing Workflow Events | 96

Step 6: Testing the Transition

In this step, you will test the transition from the PendingPayment state to the ReadyForAssignment state.
Do the following:
1. Rebuild the PhoneRepairShop_Code project.
2. In Acumatica ERP, on the Repair Work Orders (RS301000) form, open the 000004 repair work order. It should
have repair work order with the Pending Payment status.
3. On the form toolbar, click Create Invoice.
In the Invoice Nbr. box, note the number of the invoice created for the repair work order.
4. On the Invoices (SO303000) form, open the invoice created in the previous instruction.
5. On the form toolbar, click Remove Hold and then Release.
The invoice is assigned the Open status.
6. On the More menu, click Pay.
The Payments and Applications (AR302000) form opens. Notice that the Prepayment Percent box contains
10.00, which has been copied from the Repair Work Order Preferences (RS101000) form.
7. On the Payments and Applications form, change the existing values to the following values, as shown in the
following screenshot:
• Prepayment Percent: 5.00
• Amount Paid (in the only row of the Documents to Apply tab: 3.00

As you have noticed previously, the total amount of the invoice is $50.00. The paid amount
($3.00) is greater than the amount ($2.50) corresponding to the prepayment percent you
specified on the form (which is 5%). Thus, $3 is enough to prepay the work order.

Figure: The prepayment for the created invoice

8. On the form toolbar, click Remove Hold and then Release.


The prepayment is applied.
Implementing Workflow Events | 97

9. On the Repair Work Orders form, open the 000004 repair work order for which you created the invoice in
Instruction 3.
Notice that the status of the created order has changed to Ready for Assignment, as shown in the following
screenshot.

Figure: The repair work order with the Ready for Assignment status
Customizing a Predefined Workflow | 98

Customizing a Predefined Workflow


In this chapter, you will learn how to customize a predefined workflow, which is a workflow that is provided as part
of Acumatica ERP for a form. You customize a workflow by creating an extension of the workflow and modifying the
screen configuration in this extension.

Workflow Customization: General Information

In this chapterIn this lesson, you will learn how to modify an existing screen configuration and customize a
predefined workflow by using Workflow API.

Learning Objectives
In this chapterIn this lesson, you will learn how to do the following:
• Customize an existing workflow
• Add a new action to the workflow
• Define a new category in the workflow

Applicable Scenarios
You customize an existing workflow when you need to modify a workflow defined for an Acumatica ERP form.

Customization of Existing Workflows


By using Workflow API, you can not only create your own workflows but also customize workflows defined in the
source code of Acumatica ERP. Workflow API provides a set of methods to update and remove workflows and
elements of a workflow, such as states and transitions.
To define a new workflow and screen configuration, you use the AddScreenConfigurationFor and
AddDefaultFlow methods to add a workflow to a form, and Add methods to add actions, states, transitions,
and other elements of the screen configuration to the workflow. For each of these Add methods, Workflow API
provides the Update method for updating the element and the Delete method for deleting the element from the
workflow. Similarly, for the AddScreenConfigurationFor and AddDefaultFlow methods, Workflow API
provides the UpdateScreenConfigurationFor and UpdateDefaultFlow methods, which allow you to
update the existing screen configuration and the existing default workflow, respectively. To access the elements of
the screen configuration, you use the same methods as the methods that you use while adding the workflow, such
as WithFlowStates, WithActions, and WithTransitions.
An example of an updated workflow is shown in the following code.

context.UpdateScreenConfigurationFor(screen =>
{
return screen
.UpdateDefaultFlow(InjectApprovalWorkflow)
.WithActions(actions =>
{
actions.Add(approve);
actions.Add(reject);
actions.Update(
g => g.putOnHold,
a => a.WithFieldAssignments(fas =>
{
Customizing a Predefined Workflow | 99

fas.Add<ARRegister.approved>(f => f.SetFromValue(false));


fas.Add<ARRegister.rejected>(f => f.SetFromValue(false));
}));
actions.Update(
g => g.releaseFromCreditHold,
a => (BoundedTo<ARInvoiceEntry, ARInvoice>.
ActionDefinition.ConfiguratorAction)a.InFolder(approvalCategory, reject));
});
});

Workflow Customization: To Add an Action to a Workflow

This activity will walk you through the process of adding an action to an existing workflow.

Story
To quickly continue working on a repair work order aer an invoice has been prepaid, a user needs to be able to
invoke a command that opens the corresponding repair work order from the Invoices (SO303000) form. You will
customize the workflow on the Invoices form to add the View Repair Work Order command and its underlying
action, which opens the repair work order. The View Repair Work Order command should be displayed under a
new category called Repair Orders.

Process Overview
To define a new action, in your extension library, you will first define the new action in a graph extension, and then
you will create an extension for the updated workflow. In this extension, you will add a new action category and the
new action to the screen configuration.

System Preparation
In an instance with the T100 dataset, make sure that you have done the following:
• Prepared an instance with the PhoneRepairShop customization project and enabled the workflow validation
by performing the prerequisite activities in the Preparing an Instance for Workflow Customization chapter
• Performed the Step 4: Defining the Paid State (Self-Guided Exercise) prerequisite activity to implement the
workflow for the Repair Work Orders (RS301000) form; the workflow includes the Paid state

Step 1: Adding a Graph Action

In this step, you will create an extension of a graph of the Invoices (SO303000) form. In the extension, you will add
an action that opens a repair work order. Do the following:
1. Learn the name of the graph where the action should be added:
a. On the Invoices form, click Ctrl + Alt, and click the Summary area of the form.
b. In the Element Properties dialog box, notice the name in the Business Logic box: SOInvoiceEntry.
2. In the PhoneRepairShop_Code project, create an extension of the SOInvoiceEntry graph, as the
following code shows.

namespace PX.Objects.SO
{
public class SOInvoiceEntry_Extension : PXGraphExtension<SOInvoiceEntry>
Customizing a Predefined Workflow | 100

{
}
}

Use Acuminator to suppress the PX1016 error in a comment. In this activity, for simplicity, the
extension is always active.

3. In the graph extension, implement the viewOrder action, as the following code shows.

public PXAction<ARInvoice> ViewOrder;


[PXButton, PXUIField(DisplayName = "View Repair Work Order")]
protected virtual IEnumerable viewOrder(PXAdapter adapter)
{
var orderEntry = PXGraph.CreateInstance<RSSVWorkOrderEntry>();
var order = orderEntry.WorkOrders.Search<RSSVWorkOrder.invoiceNbr>(
Base.Document.Current.RefNbr);
if (order == null)
return adapter.Get();

orderEntry.WorkOrders.Current = order;
throw new PXRedirectRequiredException(orderEntry, true,
nameof(ViewOrder))
{
Mode = PXBaseRedirectException.WindowMode.NewWindow
};
}

4. Add the following using directives.

using PhoneRepairShop;
using PX.Data;
using PX.Objects.AR;
using System.Collections;

5. Save your changes.

Step 2: Adding a Workflow Extension

In this step, you will create an extension for the workflow defined for the Invoices (SO303000) form. Do the
following:
1. Find the name of the class that defines the workflow for the Invoices form. To do this, explore the contents
of the PX.Objects/SO/Workflow folder of the Acumatica ERP source code. There you can find the class
you need to extend: SOInvoiceEntry_Workflow, which is an extension of the SOInvoiceEntry
graph.
In the code of the SOInvoiceEntry_Workflow class, notice that the states of the workflow are defined
in the ARDocStatus class. You will need this class in the next step.
2. In the Workflows folder of the PhoneRepairShop_Code project, create the
SOInvoiceRepairOrder_Workflow.cs file.
3. In the SOInvoiceRepairOrder_Workflow.cs file, define the
SOInvoiceRepairOrder_Workflow class, as the following code shows.

public class SOInvoiceOrder_Workflow : PXGraphExtension<SOInvoiceEntry_Workflow,


Customizing a Predefined Workflow | 101

SOInvoiceEntry>
{
public override void Configure(PXScreenConfiguration config)
{
Configure(config.GetScreenConfigurationContext<SOInvoiceEntry,
ARInvoice>());
}

protected virtual void Configure(WorkflowContext<SOInvoiceEntry,


ARInvoice> context)
{
}
}

In the code above, you have defined an extension of the SOInvoiceEntry_Workflow class. As a
second parameter of the extension, you have specified the graph of the Invoices form. In the extension, you
have overridden the Configure(PXScreenConfiguration) method, which initializes the screen
configuration, and declared the Configure(WorkflowContext) method where you will update the
workflow.
4. Make sure that you have added the needed using directives.
5. Save your changes.

Step 3: Defining a Workflow Action

The View Repair Work Order command should be displayed in its own category on the More menu of the Invoices
(SO301000) form. The action is displayed when the invoice is prepaid—that is, when the invoice has the Open
status. In this step, you will define a custom category, add the action to the workflow, and make it available in the
Open state of the workflow.
Do the following:
1. In the SOInvoiceRepairOrder_Workflow.cs file, define the class that contains information for
custom categories, as the following code shows.

public static class ActionCategories


{
public const string RepairCategoryID = "Repair Orders Category";

[PXLocalizable]
public static class DisplayNames
{
public const string RepairOrders = "Repair Orders";
}
}

In the code above, you have defined a category with the Repair Orders display name and the string
identifier for this category.

Add the PX.Common using directive to use the PXLocalizable attribute.

2. In the Configure(WorkflowContext) method, add the definition of the Repair Orders category and
the viewOrder action, as the following code shows.

var repairCategory = context.Categories.CreateNew(


Customizing a Predefined Workflow | 102

ActionCategories.RepairCategoryID,
category => category.DisplayName(
ActionCategories.DisplayNames.RepairOrders));

var viewOrder = context.ActionDefinitions


.CreateExisting<SOInvoiceEntry_Extension>(g => g.ViewOrder,
a => a.WithCategory(repairCategory));

In the code above, you have created a workflow definition corresponding to the Repair Orders category
of the More menu by using the Categories.CreateNew method. You have also created a workflow
definition of the viewOrder action by using the ActionDefinitions.CreateExisting method. As
the type parameter of the CreateExisting method, you have specified the extension where the action is
defined.
3. Aer the definitions of the category and the action, update the default workflow, as the following code
shows.

context.UpdateScreenConfigurationFor(screen => screen


.UpdateDefaultFlow(flow =>
{
return flow
.WithFlowStates(flowStates =>
{
flowStates.Update<ARDocStatus.open>(flowState =>
{
return flowState.WithActions(actions =>
actions.Add(viewOrder));
});
});
})
.WithCategories(categories =>
{
categories.Add(repairCategory);
})
.WithActions(actions =>
{
actions.Add(viewOrder);
})
);

In the code above, you have updated the screen configuration by calling the
UpdateScreenConfigurationFor method. In the lambda expression for the method, you have
updated the default workflow by calling the UpdateDefaultFlow method. In the lambda expression
for the UpdateDefaultFlow method, you have added the viewOrder action to the Open state of the
workflow.
In the WithCategories method, you have added the Repair Orders category to the screen
configuration. In the WithActions method, you have added the viewOrder action to the screen
configuration.
4. Save your changes.

Step 4: Testing the New Action

In this step, you will test the View Repair Work Order command. Do the following:
1. Rebuild the PhoneRepairShop_Code project.
Customizing a Predefined Workflow | 103

2. In Acumatica ERP, open the 000004 repair work order for which you created an invoice in Step 6: Testing the
Transition. Notice the number of the invoice.with the Completed status. Create an invoice for this order by
clicking Create Invoice on the form toolbar.
3. On the Invoices (SO303000) form, open the invoice that you created for the repair work order.
The invoice has the Open status.
4. On the More menu, find the View Repair Work Order command. Notice that it is displayed under the Repair
Orders category, as shown in the following screenshot.

Figure: The More menu of the Invoices form

5. Click the View Repair Work Order command.


In a new window, the Repair Work Orders (RS301000) form opens with the 000004 repair work order for
which the opened invoice was created.
6. Return to the Invoices form.
7. Select the INV000050 invoice with the Closed status that you fully paid in Step 7: Testing the Transition. Notice
that the View Repair Work Order command is unavailable on the More menu because this invoice has the
Closed status.
Implementing Workflow Sequences | 104

Implementing Workflow Sequences

Composite States: General Info

This chapterlesson will give you a comprehensive understanding of what a composite state is and how to
implement and use it in a workflow.

Learning Objectives
In this chapterlesson, you will learn how to do the following:
• Implement a composite state
• Define a transition for a composite state
• Update a composite state in a predefined workflow

Applicable Scenarios
You implement a composite state when you have a set of states with common properties and you want to simplify
the process of moving an entity though all these states.

About Composite States


A composite state, which can also be referred to as a parent state, is a state that contains an ordered sequence of
nested states and the transitions to and from these states. With composite states, you can specify common settings
(actions, fields to be updated, and transitions) for a group of states only once—in the composite state that includes
these states.
For each of the states inside the composite state, you can do the following:
• Specify whether this state should be skipped by using a skip condition
• Specify a transition to the next state inside the composite state instead of a transition to a specific state
• Specify a transition to the state that is the next one aer the composite state if this composite state is itself a
nested state in another composite state
• Override the settings inherited from the composite state, if needed
These capabilities make workflow customization much easier. You do not need to explicitly specify target states
for transitions. Thus, if you add or remove states in the workflow or change the order of states, you do not need to
modify all the affected transitions.
A record created on a form cannot be in a composite state; it can only be in one of its nested states. When a record
enters any nested state in a composite state, the system checks the skip condition specified for this state, if one has
been defined. If the condition is fulfilled, the system does the following for the current state:
1. Assigns the default values for the fields as specified on the State Properties tab of the Workflow (Tree View)
page for the form if the current state is the initial state of the workflow
2. Does not check the fields that should be updated when the record enters the state and leaves it
3. Does not check any of the workflow settings, and moves the record to the next state inside the composite
state
Implementing Workflow Sequences | 105

If no skip condition is specified, the system uses the typical workflow for this state. This means that the transitions
are triggered only by actions or event handlers, and the system does not check the skip condition again while the
record remains in this state.

Implementation of a Composite State


You add a composite state inside the AddFlowStates method by using the AddSequence method. You specify
the name of the composite state as the type parameter of the method. In the lambda expression provided for the
AddSequence method, you can add states that are a part of the composite state by using the WithStates
method. The states in a composite state are declared the same way as they are declared in the AddFlowStates
method.
To specify that a state should be skipped in the composite state, you call the IsSkippedWhen method in the state
definition. In the parameter of the IsSkippedWhen method, you specify the condition that determines whether
the state should be skipped.
The following code shows an example of the declaration of a composite state.

.WithFlowStates(fss =>
{
fss.Add(initialState, flowState => flowState.IsInitial(g => g.initializeState));
fss.AddSequence<State.HoldToBalance>(seq =>
seq.WithStates(sss =>
{
sss.Add<State.hold>(flowState =>
{
return flowState
.IsSkippedWhen(conditions.IsNotOnHold)
.WithActions(actions =>
{
...
});
});
sss.Add<State.creditHold>(flowState =>
{
return flowState
.IsSkippedWhen(conditions.IsCreditHoldChecked)
.WithActions(actions =>
{
...
});
});
sss.Add<State.balanced>(flowState =>
{
return flowState
.WithActions(actions =>
{
...
});
});
}));
}

In the code above, the HoldToBalance composite state is declared. The composite state includes the following
states:
• hold, which is skipped when the IsNotOnHold condition is true
• creditHold, which is skipped when the IsCreditHoldChecked condition is true
Implementing Workflow Sequences | 106

• balanced, which is never skipped

Declaring of a Transition for a Composite State


You can define the following transitions involving a composite state:
• A transition from any state of a workflow to a composite state.
• A transition from a composite state to any state of a workflow.
This transition is inherited by the states of the composite state.
• A transition from a specific state of the composite state to any state of the workflow.
• A transition from a composite state to itself so that the workflow engine can search for the proper state and
check conditions again.
To define this transition, in the Add method for the transition, specify the target state, which is the same as
the source state.
• A transition from one state of a composite state to the state defined aer it.
To define this transition, in the Add method for the transition, call the ToNext method.
• If a composite state is defined inside another composite state, a transition from a state in the child
composite state to the next state in the parent composite state.
To define this state, in the Add method for a transition, call the ToParentNext method.

A composite state cannot be the initial state of a workflow. You must specify a transition from an
initial state to the composite state.

The following code shows an example of transitions that include a composite state.

.WithTransitions(transitions =>
{
transitions.AddGroupFrom(initialState, ts =>
{
ts.Add(t => t.To<State.HoldToBalance>()
.IsTriggeredOn(g => g.initializeState)); // To composite state
});
transitions.AddGroupFrom<State.HoldToBalance>(ts =>
{
ts.Add(t => t
.To<State.HoldToBalance>()
.IsTriggeredOn(g => g.OnUpdateStatus)
.When(conditions.IsARInvoice));
ts.Add(t => t
.To<State.open>()
.IsTriggeredOn(g => g.OnReleaseDocument)
.When(conditions.IsOpen));
}
}

In the code above, the following transitions are declared:


• A transition from the initial state to the HoldToBalance composite state
• A transition from the HoldToBalance composite state to itself
• A transition from the HoldToBalance composite state
Implementing Workflow Sequences | 107

Customization of an Existing Composite State


You can customize a composite state that has been defined in the source code of Acumatica ERP. To update a
composite state, use the UpdateSequence method in the WithFlowStates method. In the WithStates
method of the UpdateSequence method, you can add, update, or delete a state by using the Add, Update,
or Delete methods, respectively. To specify the location of a state in the updated composite state, use the
PlaceAfter method.
The following code shows an example of an updated composite state.

.WithFlowStates(states =>
{
states.UpdateSequence<State.HoldToBalance>(seq =>
seq.WithStates(sss =>
{
sss.Add<State.pendingApproval>(flowState =>
{
return flowState
.IsSkippedWhen(conditions.IsApproved)
.WithActions(actions =>
{
...
})
.PlaceAfter<State.hold>();
});
}));
})
.WithTransitions(transitions =>
{
transitions.AddGroupFrom<State.pendingApproval>(ts =>
{
ts.Add(t => t
.To<State.HoldToBalance>()
.IsTriggeredOn(g => g.OnUpdateStatus));
ts.Add(t => t
.ToNext()
.IsTriggeredOn(aproveAction)
.When(conditions.IsApproved));
ts.Add(t => t
.To<State.rejected>()
.IsTriggeredOn(rejectAction)
.When(conditions.IsRejected));
});
});

In the code above, the pendingApproval state is added to the HoldToBalance composite state and placed
aer the hold state. Three transitions are added from the pendingApproval state.

Composite State: To Update a Composite State

This activity will walk you through the process of adding a nested state to a composite state of a predefined
workflow.
Implementing Workflow Sequences | 108

Story
On the Invoices (SO303000) form, the default workflow includes a composite state with multiple nested states.
Suppose that you want to add a new state, Postponed, to the composite state in the workflow for the Invoices form
aer the Credit Hold state. Further suppose that you need to specify a skip condition for it and add a transition from
this state to the next state in the workflow.
If a user specifies a discount for an invoice, you want this invoice to be reviewed and the discount to be approved
before the user can proceed with processing the invoice. Aer the discount is approved, the system specifies the
current date in the Cash Discount Date box. The skip condition will check for no cash discount being applied to the
invoice. That is, if the user does not specify any discount, the system will skip the Postponed state.

Process Overview
In the extension of the predefined workflow for the Invoices (SO303000) form, you will implement an action (and
the corresponding button on the form toolbar and command on the More menu) that will trigger a transition from
the new state. You will also add a nested state, specify a condition that the system will use to skip this state, and
then add a transition from the state to the next nested state.

System Preparation
In an instance with the T100 dataset, make sure that you have done the following:
• Prepared an instance with the PhoneRepairShop customization project and enabled the workflow validation
by performing the prerequisite activities in the Preparing an Instance for Workflow Customization chapter.
• Prepared the screen configuration and defined the set of states by performing the prerequisite activities in
the Preparing a Screen Configuration chapter.
• Created an extension of the predefined workflow for the Invoices (SO303000) form, as described in the
Workflow Customization: To Add an Action to a Workflow prerequisite activity.

You might also like