AcumaticaERP WMSEngine
AcumaticaERP WMSEngine
Barcode-Driven Engine
Developer Guide
2022 R2
Contents | 2
Contents
Copyright...............................................................................................................................................4
Barcode-Driven Engine Guide.................................................................................................................. 5
Preparing a Test Instance........................................................................................................................ 6
Test Instance: General Information.................................................................................................................. 6
Test Instance: To Deploy an Instance............................................................................................................... 6
Creating a Barcode Scan Class................................................................................................................. 8
Barcode Scan Class: General Information........................................................................................................8
Barcode Scan Class: To Create a Barcode Scan Class................................................................................... 10
Creating a Barcode Scan Mode...............................................................................................................14
Barcode Scan Mode: General Information..................................................................................................... 14
Barcode Scan Mode: To Define the Required Properties.............................................................................. 17
Implementing Barcode Scan States........................................................................................................ 20
Barcode Scan States: General Information.................................................................................................... 20
Barcode Scan States: To Create the Set of Scan States.................................................................................21
Barcode Scan States: Input State................................................................................................................... 22
Barcode Scan States: To Create the Input State............................................................................................ 24
Barcode Scan States: Confirmation State...................................................................................................... 28
Barcode Scan States: To Create the Confirmation State............................................................................... 29
Implementing Barcode Scan Transitions.................................................................................................34
Barcode Scan Transitions: General Information............................................................................................ 34
Barcode Scan Transitions: To Implement Transitions................................................................................... 35
Barcode Scan Transitions: To Implement a Simple Transition Flow............................................................ 36
Defining the List of Barcode Scan Commands......................................................................................... 39
Barcode Scan Commands: General Information........................................................................................... 39
Barcode Scan Commands: To Define the List of Commands........................................................................40
Barcode Scan Commands: To Implement a Custom Command...................................................................41
Barcode Scan Commands: To Add Quantity Support................................................................................... 44
Defining the Set of Scan Mode Redirects.................................................................................................46
Scan Mode Redirects: General Information................................................................................................... 46
Scan Mode Redirects: To Define the List of Redirects....................................................................................48
Resetting a Barcode Scan Mode............................................................................................................. 51
Reset of Barcode Scan Mode: General Information.......................................................................................51
Reset of Barcode Scan Mode: To Define the Resetting Logic........................................................................ 51
Creating a Custom Barcode-Driven Form................................................................................................ 54
Contents | 3
Copyright
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 Soware clause at DFARS 252.227-7013 or subparagraphs (c)(1) and
(c)(2) of the Commercial Computer Soware-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.
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.
This activity will walk you through the process of preparing the environment and deploying an Acumatica ERP
instance that you can use to perform the activities of this guide.
Story
Suppose that you are preparing to create a custom barcode-driven Acumatica ERP form. Before you can start the
customization, you need to deploy an Acumatica ERP instance by using the Acumatica ERP Configuration Wizard.
Preparing a Test Instance | 7
Process Overview
You will prepare the environment that you will use to customize Acumatica ERP. Then you will deploy an Acumatica
ERP instance with the data for the T210 Customized Forms and Master-Detail Relationship course.
This instance will contain the data that you can use for testing of activities in this guide, the
PhoneRepairShop customization project, and the source code of the extension library of the
customization project.
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.
Learning Objectives
In this chapter, you will learn how to do the following:
• Create a barcode scan class
• Make the barcode scan class usable
Applicable Scenarios
You create a barcode scan class if you are creating a custom barcode-driven Acumatica ERP form.
Core Class
BarcodeDrivenStateMachine<TSelf, TGraph> is the core class of the Acumatica barcode-driven engine.
This is a generic graph extension that connects all components of the barcode-driven engine. The class implements
the IBarcodeDrivenStateMachine interface and inherits from the PXGraphExtension<TGraph> class.
All components can access the core class via their Basis property.
The core class contains the components shown in the following diagram. For details about the properties and
methods of the core class, see BarcodeDrivenStateMachine<TSelf,TGraph> Class.
Creating a Barcode Scan Class | 9
Host Graph
The host graph is a supplementary graph that connects the barcode scan class with the target graph to which the
barcode-driven engine functionality is applied.
Do not apply a barcode scan class directly to a target graph because this can significantly change the
way the form works and break the graph's standard workflow.
You can use one of the following ways to introduce a host graph:
• By making the host graph an empty descendant of an existing graph.
With this approach, you reuse the business logic of the existing graph, while the barcode scan class is
applied only to that descendant and not to the original graph. Therefore, both versions of the graph (the
original one and the one with the barcode scan functionality) are available in the system.
The barcode scan class and all its components can use the inherited functionality of the host graph by
accessing the Graph property of the barcode scan class.
• By making the host graph a new empty graph (that is, a direct descendant of the PXGraph<T> class).
This approach is useful when the original graph you want to utilize is pretty expensive to use, so you want to
instantiate it manually and only when it is actually needed. That is, you want to instantiate it manually only
when the barcode scan class applies the changes accumulated during scan cycles.
This activity will walk you through the process of creating a barcode scan class.
Story
Suppose that you need to create a barcode-driven version of the Physical Inventory Review (IN305000) form. The
form should have only one scan mode. For this form, you need to define a barcode scan class and configure this
mode.
Process Overview
You will define the INScanCount barcode scan class as a descendant of the
WarehouseManagementSystem<TSelf,TGraph> base scan class.
In the INScanCount class, you will create a host class as an empty descendant for INPICountEntry, the graph
object of the Physical Inventory Review (IN305000) form.
You will then implement the CreateScanModes() abstract method of the base class to make the barcode
scan class usable. In the method, you will use the yield return construction to avoid array creation.
Creating a Barcode Scan Class | 11
System Preparation
Before you begin performing the steps of this activity, you need to prepare an Acumatica ERP instance and the
PhoneRepairShop customization project by performing the Test Instance: To Deploy an Instance prerequisite
activity.
using PX.BarcodeProcessing;
using PX.Objects.IN;
using PX.Objects.IN.WMS;
4. In the INScanCount class, add a host class to apply the barcode scan class to, as shown in the following
code.
5. To simplify referencing the nested components of the barcode scan class, add the following line before
the INScanCount class declaration. The WarehouseManagementSystem<TSelf,TGraph> class
is a descendant of the BarcodeDrivenStateMachine<TSelf, TGraph> class, which contains
warehouse-related logic and components.
6. Derive the INScanCount class from WMSBase. The following code shows the result of this step.
using PX.BarcodeProcessing;
using PX.Objects.IN;
using PX.Objects.IN.WMS;
namespace PhoneRepairShop
{
using WMSBase = WarehouseManagementSystem<INScanCount, INScanCount.Host>;
public class INScanCount: WMSBase
{
public class Host : INPICountEntry { }
}
}
1. Add the using directives shown in the following code to the file.
using System.Collections.Generic;
using PX.Data;
2. To make the barcode-driven part of the graph extension work, implement the CreateScanModes()
abstract method as follows.
You will implement the CountMode scan mode in Barcode Scan Mode: To Define the Required
Properties.
3. To facilitate access to the important members of the host graph, add the following members.
...
}
4. Add the following supplementary method, which will be reused in further implementation of the custom
barcode-driven form.
5. To define when the document with which the barcode scan class works can be edited by the barcode
processing, override the DocumentIsEditable member of the WMSBase class.
...
Creating a Barcode Scan Class | 13
}
Creating a Barcode Scan Mode | 14
A barcode scan mode is the main scan component of the Acumatica barcode-driven engine.
Learning Objectives
In this chapter, you will learn how to define the required properties of the barcode scan mode.
Applicable Scenarios
You create a barcode scan mode in the following cases:
• You are creating a custom barcode-driven form. You have defined a barcode scan class for this form and
created an instance of the descendant of the ScanMode class in the CreateScanModes() method
implementation. Now you need to define this descendant itself.
• You need to add a scan mode to an existing barcode-driven form. You have created a scan extension of a
barcode scan class and overridden the CreateScanModes() method to return the new scan mode, as
described in Extension of Scan Components: Customization of Components. Now you need to implement this
new mode.
Scan Mode
ScanMode<TScanBasis> is the main component of the BarcodeDrivenStateMachine and the base class
for all modes. It provides access to all other components and defines their structure and the relationships between
them.
The following diagram shows the structure of the scan mode. For details on the properties and methods, see
ScanMode<TScanBasis> Class.
Creating a Barcode Scan Mode | 16
Creating a Barcode Scan Mode | 17
In the source code of Acumatica ERP, there are multiple exceptions to this approach in
the definition of component classes. Most of them are the components defined in the
WarehouseManagementSystem<TSelf, TGraph> class (such as InventoryItemState,
LocationState, and RemoveCommand). These components can be used either without
changes or by providing its descendant with mode-related modifications. These classes are neither
abstract nor sealed. However, you should not use inheritance to customize their behavior. In
customization scenarios, you should use only the method interception approach.
Because scan components are strictly bound to a specific barcode-driven form, you can nest these components
within the barcode scan class or even within each other. For example, you can nest the following: scan modes
within the barcode scan class; scan states, scan transitions, and scan commands within the scan mode; and scan
extensions within the components that use them.
This activity will walk you through the implementation of the required properties of a scan mode.
Story
Suppose that you are creating a custom barcode-driven form. You have defined a barcode scan class for
this form and created an instance of the descendant of the ScanMode class in the implementation of the
Creating a Barcode Scan Mode | 18
CreateScanModes() method. Now you need to define this descendant itself. You also need to define the
constants for the values of the required properties of the scan mode and add these properties.
Process Overview
You will define the class for the scan mode and define its required properties. For the Code property, you will
provide both the string and the BQL constant. For the Description property, you will use a localizable
message.
System Preparation
Before you begin creating a scan mode, do the following:
1. Prepare an Acumatica ERP instance by performing the Test Instance: To Deploy an Instance prerequisite
activity.
2. Create a barcode scan class by performing the Barcode Scan Class: To Create a Barcode Scan Class
prerequisite activity.
2. In the INScanCount.cs file, add the using directives shown in the following code.
using PX.Common;
using PX.Data.BQL;
3. Define the string constant and the BQL constant for the scan mode identifier, as shown below.
4. Define the localizable message for the name of the scan mode, as shown in the following code.
[PXLocalizable]
new public abstract class Msg
{
public const string Description = "Scan and Count";
}
}
}
Implementing Barcode Scan States | 20
ScanState<TScanState> is a class for a barcode scan state. This class represents a component that contains
the logic for handling a non-fixed input (such as a barcode, quantity, or date) of a barcode-driven Acumatica ERP
form.
Learning Objectives
In this chapter, you will learn how to do the following:
• Create a set of scan states of a scan mode
• Implement the input scan state
• Implement the confirmation scan state
Applicable Scenarios
You create scan states in the following cases:
• You have created a scan mode on a custom form and need to implement the set of scan states of this mode.
• You have added a new scan mode on an existing barcode-driven form and need to implement the set of
scan states of this mode.
• You need to define a new scan state for an existing scan mode of a barcode-driven from.
This activity will walk you through the creation of the set of barcode scan states.
Story
Suppose that you are implementing a scan mode for a custom barcode-driven form. The barcode scan class of this
form uses the WarehouseManagementSystem<TSelf,TGraph> base scan class.
You need to define two new scan states for this scan mode: one input state and one confirmation state. Three other
input states will be inherited from the base scan class.
Process Overview
You will create particular instances of the ScanState<TScanState> class in the
ScanMode<TScanBasis>.CreateStates() method.
You will define the classes for scan states in the following activities: Barcode Scan States: To
Create the Input State, Barcode Scan States: To Create the Confirmation State, and Extension of Scan
Components: To Extend Predefined Scan States for a Custom Form.
System Preparation
Before you begin performing the step of this activity, do the following:
1. Prepare an Acumatica ERP instance as described in the Test Instance: To Deploy an Instance prerequisite
activity.
Implementing Barcode Scan States | 22
2. Create a barcode scan class as described in the Barcode Scan Class: To Create a Barcode Scan Class
prerequisite activity.
3. Define the required properties of the scan mode as described in the Barcode Scan Mode: To Define the
Required Properties prerequisite activity.
...
}
The input state is a barcode scan state that is used to transform a barcode to a particular entity, validate it and then
apply it to the barcode-driven form, without changing a certain document.
Required Properties
For an input state, you must define the following required properties:
• Code, which is the state identifier.
• StatePrompt, which is an input prompt. The input prompt should clearly explain what is expected from a
user.
Cycle-Processing Methods
You can also implement the following additional methods, which affect the order in which the states are executed:
IsStateActive() and IsStateSkippable().
Depending on the purpose of the state, you may need to implement some of the methods shown in the following
code.
In the GetByBarcode() method, you can define how a barcode should be transformed into the entity that it
represents. We recommend that in this method you define the query that requests the entity only by the keys and
that you move all restrictions to the Validate() method.
You can provide the validation of an entity in the Validate() method. If in this method implementation, you
need to use the field's UI representation, you can retrieve it by using the Basis.SightOf<>() method.
You usually implement both the Apply() method and the ClearState() method. The Apply() method
applies the found entity to the state of the barcode-driven form. The method is called when the barcode
is processed by the state upon data input. The ClearState() method reverts the application done by
the Apply() method. The ClearState() method is called once the Basis.Clear<TState>() or
ScanMode.Clear<TState>() method is executed, which usually occurs when the mode is reset. For details on
the resetting of the mode, see Resetting a Barcode Scan Mode.
For details about these methods, see EntityState<TScanBasis,TEntity> Class.
The following diagram illustrates the processing cycle.
Implementing Barcode Scan States | 24
This activity will walk you through the creation of the input scan state.
Implementing Barcode Scan States | 25
Story
Suppose that you are implementing a scan mode for a custom barcode-driven form. The barcode scan class of this
form uses the WarehouseManagementSystem<TSelf,TGraph> base scan class.
For this scan mode, you need to define the input scan state, which handles the reference number of a document.
Process Overview
Because you need the input state to belong to a certain mode, you will nest the input state in the CountMode scan
mode class.
The base barcode scan class already has a blank class for creating a state that handles the reference number
of a document. Therefore, you can skip the implementation of particular properties and methods. You will not
implement the following members:
• Code: It will use the default implementation of WMSBase.RefNbrState<INPIHeader>, which is
RNBR.
• IsStateActive(): It will use the default implementation of the EntityState<TBasis, TEntity>
class, which returns true.
• IsStateSkippable(): It will use the default implementation of the
WMSBase.RefNbrState<INPIHeader> class, which is "Basis.RefNbr != null &&
Basis.Header.ProcessingSucceeded != true".
• SetNextState(): It will use the default implementation, which moves the system to the next state based
on the transition map.
In the Validate() method, you will reuse the method introduced in Barcode Scan Class: To Create a Barcode
Scan Class.
System Preparation
Before you begin performing the steps of this activity, do the following:
1. Prepare an Acumatica ERP instance by performing the Test Instance: To Deploy an Instance prerequisite
activity.
2. Create a barcode scan class by performing the Barcode Scan Class: To Create a Barcode Scan Class
prerequisite activity.
3. Create the scan mode and define its required properties by performing the Barcode Scan Mode: To Define
the Required Properties prerequisite activity.
4. Create the set of scan states by performing the Barcode Scan States: To Create the Set of Scan States
prerequisite activity.
}
}
}
[PXLocalizable]
public abstract class Msg
{
public const string Prompt = "Scan a reference number of the PI count.";
}
}
2. Define the response that is displayed to a user if the state cannot find an entity, as follows.
Basis.IsDocumentStatusEditable(entity.Status)
? Validation.Ok
: Validation.Fail(Msg.InvalidStatus,
Basis.SightOf<INPIHeader.status>(entity));
...
[PXLocalizable]
public abstract class Msg
{
...
public const string InvalidStatus =
"Document has the {0} status, cannot be used for count.";
}
}
5. Define the response that is displayed to the user if the entity was successfully found, validated, and applied
to the state of the barcode-driven form, as follows.
The confirmation state is a special scan state that validates and confirms all the data accumulated by input states
and completes the barcode processing cycle by applying changes based on this data to a certain document. There
are two types of confirmation strategies: explicit and implicit. An explicit confirmation strategy shows the prompt
to confirm an action. An implicit confirmation strategy automatically executes the confirmation process.
Required Properties
For a confirmation state, you must implement the Prompt required property. You use the property to
specify the prompt that is shown to a user to confirm an action. In the Prompt property, you can use the
Basis.Localize() method if you need to insert message parameters. If you do not need to use parameters in
the message, you return the message itself. The system localizes it automatically.
public
PXSetupOptional<INScanSetup,
Where<INScanSetup.branchID.IsEqual<AccessInfo.branchID.FromCurrent>>>
Setup;
...
}
Implementing Barcode Scan States | 29
This activity will walk you through the creation of the confirmation scan state.
Story
Suppose that you are implementing a scan mode for a custom barcode-driven form. The barcode scan class of this
form uses the WarehouseManagementSystem<TSelf,TGraph> base scan class.
For this scan mode, you need to define the confirmation scan state. You need to split the confirmation logic into the
following virtual methods:
• Confirm(): The entry point method of the scan extension component
• CanConfirm(): A method that is used to extend the validation logic
• ConfirmRow(): The method that contains the main logic of confirmation
• FindDetailRow(): The helper method that can be altered
Process Overview
You will create a WMSBase.ConfirmationState class descendant. In this class, you will define a
ScanExtension class that will implement the confirmation logic.
In the PerformConfirmation() method, you will then get the ScanExtension object by using the
Get<TExtension>() method and call its entry point method.
System Preparation
Before you begin performing the steps of this activity, do the following:
1. Prepare an Acumatica ERP instance by performing the Test Instance: To Deploy an Instance prerequisite
activity.
2. Create a barcode scan class by performing the Barcode Scan Class: To Create a Barcode Scan Class
prerequisite activity.
3. Create the scan mode and define its required properties by performing the Barcode Scan Mode: To Define
the Required Properties prerequisite activity.
4. Create the set of scan states by performing the Barcode Scan States: To Create the Set of Scan States
prerequisite activity.
}
}
}
[PXLocalizable]
new public abstract class Msg
{
public const string Prompt = "Confirm counting {0} x {1} {2}.";
}
}
if (Basis.DocumentIsEditable == false)
{
error = FlowStatus.Fail(RefNbrState.Msg.InvalidStatus,
Basis.DocumentView.Cache.GetStateExt<INPIHeader.status>(Basis.Document));
return false;
}
if (Basis.InventoryID == null)
Implementing Barcode Scan States | 31
{
error = FlowStatus.Fail(InventoryItemState.Msg.NotSet);
return false;
}
if (Basis.CurrentMode.HasActive<LotSerialState>() &&
Basis.SelectedLotSerialClass.LotSerTrack ==
INLotSerTrack.SerialNumbered &&
Basis.BaseQty != 1)
{
error =
FlowStatus.Fail(InventoryItemState.Msg.SerialItemNotComplexQty);
return false;
}
error = FlowStatus.Ok;
return true;
}
if (Basis.CurrentMode.HasActive<LotSerialState>() &&
Basis.SelectedLotSerialClass.LotSerTrack ==
INLotSerTrack.SerialNumbered &&
newQty.IsNotIn(0, 1))
{
return
FlowStatus.Fail(InventoryItemState.Msg.SerialItemNotComplexQty);
}
if (row == null)
{
row = (INPIDetail)Basis.Details.Cache.CreateInstance();
row.PIID = Basis.RefNbr;
row.LineNbr =
(int)PXLineNbrAttribute.NewLineNbr<INPIDetail.lineNbr>(Basis.Details.Cache,
Basis.Document);
row.InventoryID = Basis.InventoryID;
row.SubItemID = Basis.SubItemID;
row.SiteID = Basis.SiteID;
row.LocationID = Basis.LocationID;
row.LotSerialNbr = Basis.LotSerialNbr;
Implementing Barcode Scan States | 32
row.PhysicalQty = Basis.BaseQty;
row.BookQty = 0;
row = Basis.Details.Insert(row);
Basis.SaveChanges();
Basis.Details.SetValueExt<INPIDetail.physicalQty>(row, newQty);
row = Basis.Details.Update(row);
Basis.SaveChanges();
Basis.DispatchNext(
null,
Basis.SightOf<WMSScanHeader.inventoryID>(), Basis.Qty, Basis.UOM);
return FlowStatus.Ok;
}
findDetailCmd = findDetailCmd.WhereAnd<Where<
INPIDetail.inventoryID.IsEqual<WMSScanHeader.inventoryID.FromCurrent>.
And<INPIDetail.siteID.IsEqual<WMSScanHeader.siteID.FromCurrent>>
>>();
return findResultSet;
}
}
...
}
3. Fix or suppress the PX1016 error that is displayed by Acuminator for the Logic class.
Implementing Barcode Scan Transitions | 34
ScanTransition<TScanBasis> is a class for a barcode scan transition. This class represents a component
that defines the rules of automatic transitions between scan states.
Learning Objectives
In this chapter, you will learn how to do the following:
• Implement transitions between scan states
• Implement a simple transition flow between scan states
Applicable Scenarios
You implement scan transitions in the following cases:
• You have defined a set of scan states of a custom scan mode. You need to implement transitions between
these states.
• You have defined a new scan state of a predefined scan mode. You need to implement transitions between
the predefined scan states and the new scan state.
Transition Map
You can use the scan transition component to create a complex map of transitions by using conditions and
additional actions that can be performed when the transition is triggered.
Unlike all other scan components, ScanTransition<TScanBasis> is not an abstract class. However, it still
can be configured by a condition or by additional transition logic. Also, in some cases, a transition map does not
contain any branches; it contains only a group of optional states. In these cases, the creation of the transition map
can be simplified by the ScanStateFlow<TScanBasis> class, which you can use to create the transition map
sequentially.
The following diagram shows the classes and interfaces related to scan transitions.
Implementing Barcode Scan Transitions | 35
This activity will walk you through the implementation of scan transitions. This activity shows the general approach
that can be used for producing a branching structure of transitions.
Story
Suppose that you have defined the following scan states for the scan mode of a custom form:
• RefNbrState
• LocationState
• InventoryItemState
• LotSerialState
• ConfirmState
Implementing Barcode Scan Transitions | 36
You will not implement the last transition, which is from LotSerialState to ConfirmState,
because it is performed automatically once the system detects that there is a confirmation state and
that the last input state is trying to move the barcode-driven engine further.
Process Overview
You will use the ScanMode.Transition() helper method to implement the transitions.
System Preparation
Before you begin performing the step of this activity, do the following:
1. Prepare an Acumatica ERP instance by performing the Test Instance: To Deploy an Instance prerequisite
activity.
2. Create a barcode scan class by performing the Barcode Scan Class: To Create a Barcode Scan Class
prerequisite activity.
3. Create the scan mode and define its required properties by performing the Barcode Scan Mode: To Define
the Required Properties prerequisite activity.
4. Create the set of scan states by performing the Barcode Scan States: To Create the Set of Scan States
prerequisite activity.
...
}
This activity will walk you through the implementation of a simple transition flow, which cannot be used for
producing a branching structure of transitions.
Implementing Barcode Scan Transitions | 37
Story
Suppose that you have defined the following scan states for the scan mode of a custom form:
• RefNbrState
• LocationState
• InventoryItemState
• LotSerialState
• ConfirmState
You need to implement transition rules between these states as follows:
1. From RefNbrState to LocationState
2. From LocationState to InventoryItemState
3. From InventoryItemState to LotSerialState
You will not implement the last transition, which is from LotSerialState to ConfirmState,
because it is performed automatically once the system detects that there is a confirmation state and
that the last input state is trying to move the barcode-driven engine further.
Process Overview
You will use the ScanMode.StateFlow() helper method to implement the simple one-by-one chain of input
states.
System Preparation
Before you begin performing the step of this activity, do the following:
1. Prepare an Acumatica ERP instance by performing the Test Instance: To Deploy an Instance prerequisite
activity.
2. Create a barcode scan class by performing the Barcode Scan Class: To Create a Barcode Scan Class
prerequisite activity.
3. Create the scan mode and define its required properties by performing the Barcode Scan Mode: To Define
the Required Properties prerequisite activity.
4. Create the set of scan states by performing the Barcode Scan States: To Create the Set of Scan States
prerequisite activity.
.NextTo<LotSerialState>());
}
...
}
Defining the List of Barcode Scan Commands | 39
A barcode scan command is a piece of logic that can be executed in any scan state either by clicking a button
associated with this command in the UI, or by scanning a fixed barcode associated with it. For example, this logic
can change the current state of the barcode-driven engine or start a long-running process.
Learning Objectives
In this chapter, you will learn how to do the following:
• Define the set of barcode scan commands of a scan mode
• Implement a custom barcode scan command
• Add to a scan mode the logic that handles an input of the quantity
Applicable Scenarios
You define scan commands in the following cases:
• You have created a scan mode on a custom form and need to implement the set of scan commands of this
mode.
• You have added a new scan mode on an existing barcode-driven form and need to implement the set of
scan commands of this mode.
• You have created a scan mode on a custom form and need to add to this scan mode the command for the
input of the quantity
This activity will walk you through the process of defining the list of commands that are available in a scan mode.
Defining the List of Barcode Scan Commands | 41
Story
Suppose that you are creating a custom scan mode for a barcode-driven form. You need to define two new scan
commands for this scan mode. One command, RemoveCommand, will be inherited from the base barcode scan
class, which is the WMSBase class. The other, ConfirmCommand, will be introduced in this scan mode. In this
activity, you will define the list of commands.
You will define the ConfirmCommand class in Barcode Scan Commands: To Implement a Custom
Command.
Process Overview
You will create particular instances of scan commands in the ScanMode<TScanBasis>.CreateCommands()
method.
System Preparation
Before you begin performing the step of this activity, do the following:
1. Prepare an Acumatica ERP instance by performing the Test Instance: To Deploy an Instance prerequisite
activity.
2. Create a barcode scan class by performing the Barcode Scan Class: To Create a Barcode Scan Class
prerequisite activity.
3. Create the scan mode and define its required properties by performing the Barcode Scan Mode: To Define
the Required Properties prerequisite activity.
...
}
This activity will walk you through the process of defining a custom command for a scan mode.
Defining the List of Barcode Scan Commands | 42
Story
Suppose that you are creating a custom scan mode for a barcode-driven form. You have defined the list of
commands for this scan mode and introduced the ConfirmCommand command in this scan mode. You now need
to implement the custom ConfirmCommand command.
Process Overview
You will specify the required properties of the ConfirmCommand scan command, which is a new custom
command, and implement its Process() method.
System Preparation
Before you begin performing the step of this activity, do the following:
1. Prepare an Acumatica ERP instance by performing the Test Instance: To Deploy an Instance prerequisite
activity.
2. Create a barcode scan class by performing the Barcode Scan Class: To Create a Barcode Scan Class
prerequisite activity.
3. Create the scan mode and define its required properties by performing the Barcode Scan Mode: To Define
the Required Properties prerequisite activity.
4. Define the list of commands for the scan mode by performing the Barcode Scan Commands: To Define the
List of Commands prerequisite activity.
[PXLocalizable]
public abstract class Msg
{
public const string DisplayName = "Confirm";
}
}
...
}
Defining the List of Barcode Scan Commands | 43
3. In the CountMode class, add the Process() method implementation, as shown in the following code.
Basis.SaveChanges();
Basis.DocumentView.Current = null;
Basis.CurrentMode.Reset(fullReset: true);
Basis.ReportInfo(Msg.CountConfirmed);
return true;
}
[PXLocalizable]
public abstract class Msg
{
...
public const string CountConfirmed = "The count has been saved.";
}
}
Defining the List of Barcode Scan Commands | 44
...
}
}
This activity will walk you through adding the logic that handles input of a quantity.
Story
Suppose that you are creating a custom scan mode for a barcode-driven form. You need to add the logic that
handles the input of a quantity in this scan mode. You will use the QtySupport extension defined in the
WarehouseManagementSystem<TSelf,TGraph> class. The logic of the QtySupport extension cannot be
used until you create an empty descendant for this extension class.
Process Overview
In this activity, you will do the following:
1. Define an empty descendant of the QtySupport extension.
2. Override the UseQtyCorrectection method of the barcode scan class.
3. Add the QtySupport.SetQtyCommand component in the CreateCommands method.
System Preparation
Before you begin performing the step of this activity, do the following:
1. Prepare an Acumatica ERP instance by performing the Test Instance: To Deploy an Instance prerequisite
activity.
2. Create a barcode scan class by performing the Barcode Scan Class: To Create a Barcode Scan Class
prerequisite activity.
3. Create the scan mode and define its required properties by performing the Barcode Scan Mode: To Define
the Required Properties prerequisite activity.
4. Define the list of commands for the scan mode by performing the Barcode Scan Commands: To Define the
List of Commands prerequisite activity.
2. Fix or suppress the PX1016 error that is displayed by Acuminator for the class.
Defining the List of Barcode Scan Commands | 45
3. In the INScanCount class, define the Setup data view, as shown in the following code.
You can use the ScanRedirect component to give a user the ability to change the scan mode. A user will be able
to change the scan mode in any scan state either by clicking a button associated with this redirect in the UI (only in
the mobile app), or by scanning a barcode associated with it (only in the web application).
Learning Objectives
In this chapter, you will learn how to define the set of mode redirects.
Applicable Scenarios
You perform this business process in the following cases:
• You have created a scan mode on a custom form and need to implement the redirects for this mode.
• You have added a new scan mode on an existing barcode-driven form and need to implement the redirects
for this mode.
• IsPossible: The Boolean value that specifies (if set to true) that it is possible to scan the redirect code
and the redirect button is displayed in the UI. Redirects that are not possible are invisible in the UI.
You should keep in mind that any redirect object refers to two barcode-driven forms: the source and the target.
Sometimes the source and the target can be the same form.
This activity will walk you through the process of defining the list of mode redirects.
Story
Suppose that you are creating a custom scan mode for a barcode-driven form. You need to define the default set of
redirects for the form. You also need to define the redirect class for the custom mode so that redirects to this mode
can be performed from other barcode-driven forms.
Process Overview
You will use the AllWMSRedirects.CreateFor<TScanBasis>() method to specify the default set of mode
redirects for the form.
You will also define the redirect class. In this class, you will specify the Code property and the DisplayName
property.
System Preparation
Before you begin performing the steps of this activity, do the following:
1. Prepare an Acumatica ERP instance by performing the Test Instance: To Deploy an Instance prerequisite
activity.
2. Create a barcode scan class by performing the Barcode Scan Class: To Create a Barcode Scan Class
prerequisite activity.
3. Create the scan mode and define its required properties by performing the Barcode Scan Mode: To Define
the Required Properties prerequisite activity.
...
}
Defining the Set of Scan Mode Redirects | 49
2. Define the localizable message for the name of the mode redirect, as shown in the following code.
3. Define the redirect identifier and the display name of the button that is generated for this redirect, as shown
below.
...
}
}
...
}
Resetting a Barcode Scan Mode | 51
Learning Objectives
In this chapter, you will learn how to implement the resetting logic of the barcode scan mode.
Applicable Scenarios
You need to define the resetting logic for each barcode scan mode that you implement.
You should not call the protected ScanMode.ResetMode() method within any component.
This method is for override only. Moreover, the method can be intercepted. Therefore, in order
to cause all interceptors to be applied, this method should be called indirectly via the public
ScanMode.Reset() method.
This activity will walk you through the resetting of the scan mode logic.
Resetting a Barcode Scan Mode | 52
Story
Suppose that you are creating a custom barcode-driven form. You have defined a barcode scan class for this form
and implemented the scan mode, including its required properties and its states, which are LotSerialState,
InventoryItemState, LocationState, and RefNbrState. Now you need to define how the scan mode is
cleared.
You want LotSerialState and InventoryItemState to be cleared with a so reset every time the cycle
ends because neither of these states should store its data in memory between cycles. (Each cycle demands the
entry of the inventory ID and the lot or serial number.)
LocationState should be cleared only on a hard reset—that is, on mode changes and when a user clicks the
Reset button on the form toolbar—because the user does not enter a location on every cycle. (The location is
stored in memory between cycles. Therefore, the location ID is optional if it was already entered.)
You don't want to clear the current document's number, even when a user clicks the Reset button, because the
document defines the flow itself.
Process Overview
You will override the ScanMode.ResetMode() method. In the method, you will define the clearing condition for
each state of the mode. To define the clearing condition, you will use the when pattern instead of the if pattern.
System Preparation
Before you begin creating a scan mode, do the following:
1. Prepare an Acumatica ERP instance by performing the Test Instance: To Deploy an Instance prerequisite
activity.
2. Create a barcode scan class by performing the Barcode Scan Class: To Create a Barcode Scan Class
prerequisite activity.
3. Define the required properties of the scan mode by performing the Barcode Scan Mode: To Define the
Required Properties prerequisite activity.
4. Define the input states of the scan mode by performing the Barcode Scan States: To Create the Input State
and Extension of Scan Components: To Extend Predefined Scan States for a Custom Form prerequisite
activities.
...
Resetting a Barcode Scan Mode | 53
}
Creating a Custom Barcode-Driven Form | 54
A user interacts with a barcode-driven form mostly via text commands that the user enters or scans into a special
field of the form. The form guides the user through a certain input route by showing different input prompts.
Learning Objectives
In this chapter, you will learn how to create a custom barcode-driven form.
Applicable Scenarios
You create a custom barcode-driven form when you need to simplify the work with the form to the level of
sequential text inputs.
This activity will walk you through the creation of an ASPX page for a custom barcode-driven form.
Story
Suppose that you have implemented the business logic of a custom barcode-driven form. Now you need to create
an ASPX page for the form so that the form is available in the UI of Acumatica ERP.
Process Overview
You will create an ASPX page form from an ASPX template that contains the scripts required for a barcode-driven
form to work and adjust this template for the custom form. You will also add the ASPX and ASPX.CS files to the
customization project, add the form to the site map, and create the Site Map item for the form in the customization
project.
System Preparation
Before you begin performing the steps of this activity, do the following:
1. Prepare an Acumatica ERP instance by performing the Test Instance: To Deploy an Instance prerequisite
activity.
2. Add a barcode scan class and implement its functionality by performing the Barcode Scan Class: To Create a
Barcode Scan Class prerequisite activity.
3. Create a scan mode and implement its required properties by performing the Barcode Scan Mode: To Define
the Required Properties prerequisite activity.
4. To define the scan mode, do the following:
a. Define the list of scan states for the scan mode by performing the Barcode Scan States: To Create the Set
of Scan States prerequisite activity.
b. Define transition rules by performing the Barcode Scan Transitions: To Implement Transitions prerequisite
activity.
c. Define the list of commands by performing the Barcode Scan Commands: To Define the List of Commands
prerequisite activity.
d. Define the list of mode redirects by performing the Scan Mode Redirects: To Define the List of Redirects
prerequisite activity.
e. Define the resetting logic of the scan mode by performing the Reset of Barcode Scan Mode: To Define the
Resetting Logic prerequisite activity.
5. Implement scan states as follows:
Creating a Custom Barcode-Driven Form | 56
a. Create an input state by performing the Barcode Scan States: To Create the Input State prerequisite
activity.
b. Create a confirmation state by performing the Barcode Scan States: To Create the Confirmation State
prerequisite activity.
c. Alter the predefined scan states by performing the Extension of Scan Components: To Extend Predefined
Scan States for a Custom Form prerequisite activity.
6. Implement scan commands as follows:
a. Define custom commands by performing the Barcode Scan Commands: To Add Quantity Support
prerequisite activity.
b. If you want to add logic that handles quantity input, add the quantity support command by performing
the Barcode Scan Commands: To Add Quantity Support prerequisite activity.
This is the minimum of markup needed to make a barcode-driven form work. You can use this
piece of code as a template for creating any new barcode-driven ASPX page.
<script type="text/javascript">
function ActionCallback(callbackContext) { // This script plays the sound that
corresponds to the message type of the barcode-driven engine.
var baseUrl = (location.href.indexOf("HideScript") > 0) ? "../../
Sounds/" : "../../../Sounds/";
var edInfoMessageSoundFile = px_alls["edInfoMessageSoundFile"];
if ((callbackContext.info.name.toLowerCase().startsWith("scan")
|| callbackContext.info.name == "ElapsedTime") &&
callbackContext.control.longRunInProcess == null && edInfoMessageSoundFile != null) {
var soundFile = edInfoMessageSoundFile.getValue();
Creating a Custom Barcode-Driven Form | 57
<%-- Note that the datasource binds to the host graph of the barcode scan class --
%>
<px:PXDataSource ID="ds" runat="server" TypeName="PhoneRepairShopt.INScanCount
+Host" PrimaryView="HeaderView">
<CallbackCommands>
</CallbackCommands>
</px:PXDataSource>
</asp:content>
3. Add the RefNbr and SiteID fields aer the Barcode field.
Creating a Custom Barcode-Driven Form | 58
4. Add a tab item with a grid that shows physical inventory details to the third content placeholder.
5. Optional: Add another tab item that displays the logs of the barcode-driven engine.
<px:PXGridLevel DataMember="Logs">
<Columns>
<px:PXGridColumn DataField="ScanTime" />
<px:PXGridColumn DataField="Mode" />
<px:PXGridColumn DataField="Prompt" />
<px:PXGridColumn DataField="Scan" />
<px:PXGridColumn DataField="Message" />
</Columns>
</px:PXGridLevel>
</Levels>
<AutoSize Container="Window" Enabled="True" MinHeight="400" />
</px:PXGrid>
</Template>
</px:PXTabItem>
using System;
using System.Drawing;
using System.Web.UI.WebControls;
using PX.Data;
using PX.BarcodeProcessing;
if (bdsmLog.MessageType == ScanMessageTypes.Error)
e.Row.Style.CssClass = PickCss.Overpick;
else if (bdsmLog.MessageType == ScanMessageTypes.Warning)
e.Row.Style.CssClass = PickCss.Partial;
}
}
}
The WarehouseManagementSystem<TSelf, TGraph> class has multiple predefined components that can
be used by its descendants. However, sometimes you need to alter the logic of predefined components before you
add them to the components used in the mode you are developing. You can also customize the scan components
that are used on existing barcode-driven forms of Acumatica ERP.
Learning Objectives
In this chapter, you will learn how to do the following:
• Customize a scan component that is used on an existing barcode-driven Acumatica ERP form
• Add a new component that alters a predefined scan component, and use it on a custom barcode-driven
form
Applicable Scenarios
You extend a scan component in the following cases:
• You need to modify the logic of a scan component of an existing barcode-driven Acumatica ERP form
for your business scenario. For example, you may want to change the order of value input, implement
additional entity validation, or add an alternate way to search for entities.
• You have created a scan mode and have inherited a scan component of this mode from the base barcode
scan class. You need to modify this component to use it in your scan mode. For example, you may want to
add additional validation that is specific to this scan mode.
uses the classic overriding technique and thus cannot use simplified overriding strategies that are available
in the following approaches.
• Inject the logic when the component is created.
You can inject the needed changes right inside the component instance when the component is created.
The method interception approach provides multiple overriding strategies that could facilitate logic
altering. When you override the Create method that corresponds to the component (such as the
CreateStates() method for the scan state component), all logic patches made by this approach can be
suppressed. Moreover, with the ByBaseSubstitute interception strategy, you can change the base logic
of a component without affecting already applied logic patches.
• Inject the logic when the component is decorated.
You can inject desired changes right inside the component instance when the component is decorated.
When a component's logic gets intercepted during component decoration, it does not matter which
code creates this component. This means that patches made this way would stay effective even if the
component creation method of a scan mode was completely replaced with another one providing the same
components. However, patches made with this approach can still be suppressed or extended if they were
extracted to a virtual method of the barcode scan class itself or of one of its extensions.
We recommend that you always override the methods of the Decorate family in full form
(that is, by accepting the base method as the last parameter), call the base implementation,
and use its result. Otherwise, you will lose all potential changes that could be made by other
customization projects.
For examples of altering predefined scan state components, see Extension of Scan Components: To Extend
Predefined Scan States for a Custom Form.
A method interception approach provides multiple overriding strategies that could facilitate the customization
process.
Method Interceptors
All components of BarcodeDrivenStateMachine should be either abstract or sealed. An abstract
class should be used as a template for a sealed class. Only sealed classes can be used as scan components.
These components should not contain any customizable logic. All customizable logic should be located in the
Basis class or in its extensions (which are ScanExtension descendants). You perform the configuration of the
components by using method interceptors.
Extending Scan Components | 63
A method interceptor is a special class that can simulate inheritance and overriding in the scope of a particular
method. The following diagram shows the classes related to method interceptors.
A host class of the interceptor class creates a method interceptor for one of its methods by passing the base
method to the interceptor. The host provides the public access to the interceptor so that the interception can be
performed by the external classes (via the By method group). The host calls the target method only through the
interceptor's method invoker, which is provided by the interceptor and is not accessible for external code. The
host may have an additional public method that makes it possible for external code to use the target method
properly.
Interception Approaches
The barcode-driven engine provides the following interception approaches:
• Override: This approach is similar to an original overriding that is used in object-oriented programming
except that the base method is passed as the last parameter of the overriding method. A base method is a
multicast delegate that accumulates all injected delegates that have been applied previously.
• Replace: This approach simplifies the override strategy. The base method and all previously injected
delegates are not invoked because the replacing delegate replaces them completely.
• Prepend: This approach simplifies of the override strategy. The base method and all previously injected
delegates are automatically invoked aer the prepended delegate.
• Append: This approach simplifies of the override strategy. The base method and all previously injected
delegates are automatically invoked before the appended delegate.
• Base substitution: This approach overrides only the original method of a component and does not affect
any injected delegates.
All approaches can accept two versions of related delegates—that is, with or without basis as the first parameter.
Although a delegate can capture the Basis property of the ScanMode instance or the this reference of the
BarcodeDrivenStateMachine instance, we recommend that you use the basis parameter of the delegate
instead. This helps to minimize extra memory allocations that are produced by C# closures.
The following example shows additional validation for a certain entity state.
{
var state = base.DecorateScanState(original); // all other interceptions should also
take place
return state;
}
Within a patch method, only the basis parameter should be used—that is, we recommend that you
do not use the Basis property in this method. Otherwise, the compiler does not create a static
lambda method, which leads to unnecessary memory allocation.
The replace strategy of a method interceptor is not compatible with any other interceptions made by other
extensions. Therefore, we recommend that you use it only when you are creating the topmost extension. In other
cases, use the override approach—that is, get the result of the base method and use it to produce the changes
you want to achieve. The replace strategy is easier to use, while the override approach is more flexible and makes
further customization possible.
Because all scan components have to be sealed classes, they cannot have virtual methods and thus cannot
be customized through class inheritance. Components use method interceptors for customizations instead. A
component can provide its own ScanExtension class where all virtual methods are placed.
Scan Extension
The ScanExtension classes are PXGraphExtension descendants, which can simplify the
creation of customizable logic that should be used in the scope of barcode processing. For example, if a
ScanCommand<TScanBasis> descendant must have complex logic that should be split into multiple methods,
you should create a ScanExtension descendant that holds all these methods. This extension can be placed
inside the ScanCommand<TScanBasis> descendant so that its customizable logic is not mixed with the logic of
other components.
The ScanExtension descendant can be customized as any other PXGraphExtension
can via PXOverrideAttribute. Any component can access the extension by
using the Basis.Get<TScanExtension>() method, which is a shortcut for the
Basis.Graph.FindImplementation<TScanExtension>() call.
Extending Scan Components | 65
When you create an extension for a scan component, you can use a PXGraphExtension<> class
descendant instead of ScanExtension. However, in this case, the definition of the extension lacks
the Basis property that is used by all scan components. You need to define this property manually,
as shown in the following code.
The classes related to scan extensions are shown in the following diagram.
// Overrides MyBarcodeExt.MyCommand.Logic
public class SomeCustomization : PXGraphExtension<
MyBarcodeExt.MyCommand.Logic, // the logic you want to customize
MyBarcodeExt, // optional, is useful if Basis is needed to be used in scope of the
customization
MyBarcodeExt.Host // an actual graph
>
{
public MyBarcodeExt Basis => Base1;
// Overrides MyBarcodeExt.MyCommand.Logic.Bar(string)
[PXOverride]
public virtual int Bar(string input, Func<string, int> base_Bar)
{
return base_Bar(input) + 42;
}
}
ScanExtension<TScanExtension> Class
The ScanExtension<TScanExtension> class is a ScanExtension component that is parameterized with
another ScanExtension component type. This class is useful if you want to customize a ScanExtension
component.
For example, you can define an extension for the INScanCount.CountMode.ConfirmState.Logic class in
the following ways:
• By using the PXGraphExtension<> class
public class AlterCountModeConfirmStateLogic :
PXGraphExtension<INScanCount.CountMode.ConfirmState.Logic,
INScanCount, INScanCount.Host>
{
// some logic
}
Although you can use the way you prefer, we recommend that you use the second one.
Extending Scan Components | 67
This activity will walk you through the process of altering predefined scan states.
Story
Suppose that you are creating a scan mode for a custom barcode-driven form. You have inherited the
following input states from the base barcode scan class: LocationState, InventoryItemState, and
LotSerialState. You need to modify these states to use them in your scan mode as follows:
• For the LocationState scan state, you want to add additional validation that is specific to this current
scan mode.
• For the InventoryItemState scan state, you want to add an additional absence handler that is specific
to this scan mode.
• You want the LotSerialState to be active only when it is already active and when the lot or serial
number is entered on receipt.
Process Overview
You will implement the following ways of altering the logic of the predefined components:
• You will introduce a descendant for the LocationState class.
• You will inject the logic when InventoryItemState is created.
• You will inject the logic when LotSerialState is decorated.
System Preparation
Before you begin performing the steps of this activity, do the following:
1. Prepare an Acumatica ERP instance by performing the Test Instance: To Deploy an Instance prerequisite
activity.
2. Create a barcode scan class by performing the Barcode Scan Class: To Create a Barcode Scan Class
prerequisite activity.
3. Create the scan mode and define its required properties by performing the Barcode Scan Mode: To Define
the Required Properties prerequisite activity.
4. Create the set of scan states by performing the Barcode Scan States: To Create the Set of Scan States
prerequisite activity.
}
...
}
}
You use the same name for the descendant that is used for the predefined class. The code of this activity
includes two versions of the LocationState class: the original WMSBase.LocationState class and
the CountMode-related INScanCount.CountMode.LocationState class. Because the original
class and the descendant have the same name, you will not need to change the CreateStates()
method.
2. In the LocationState class, implement the IsStateSkippable() method so that LocationState
is skipped if the location is already selected, as shown in the following code.
In CountMode, you do not need to ask a user for a location again if the location is already selected.
Therefore, you implement the IsStateSkippable() method so that LocationState is skipped if the
location is already selected.
3. In the INScanCount.cs, add the following using directive.
using PX.Data.BQL.Fluent;
4. In the LocationState class, add the Validate() method, as shown in the following code.
INPIStatusLoc statusLocation =
SelectFrom<INPIStatusLoc>.
Where<
INPIStatusLoc.pIID.IsEqual<WMSScanHeader.refNbr.FromCurrent>.
And<
INPIStatusLoc.locationID.IsEqual<@P.AsInt>.
Or<INPIStatusLoc.locationID.IsNull>>>.
View.ReadOnly
.Select(Basis, location.LocationID);
if (statusLocation == null)
return Validation.Fail(Msg.NotPresent, location.LocationCD);
return Validation.Ok;
}
[PXLocalizable]
public new abstract class Msg : WMSBase.LocationState.Msg
{
Extending Scan Components | 69
public const string NotPresent = "The {0} location is not in the list and
cannot be added.";
}
}
In CountMode, you need only locations that are present in the physical inventory that the user is currently
processing. Therefore, you add additional validation logic in the Validate() method. You execute the
base validation first. Only if the base validation is passed, the custom validation is performed.
In the CreateStates() method, you intercept the HandleAbsence method. You use the ByAppend
interception strategy because you want to register an additional absence handler without affecting the
existing ones. In CountMode, you need the ability to switch the location even if the current input state is
InventoryItemState. You also configure the LocationState state to try handle the input.
return state;
}
In this code, you will intercept the IsStateActive method and use the ByConjoin interception strategy.
In this topic, you can find information about how to customize particular scan components.
Scan Mode
If you need to change the configuration of the scan mode, you override the DecorateScanMode method by
using the PXOverride attribute and intercept the logic of the mode in this method, as shown in the following
code.
public class MyScanExtension : SomeWMS.ScanExtension
{
Extending Scan Components | 71
[PXOverride]
public virtual ScanMode<SomeWMS> DecorateScanMode(
ScanMode<SomeWMS> original,
Func<ScanMode<SomeWMS>, ScanMode<SomeWMS>> base_DecorateScanMode)
{
var mode = base_DecorateScanMode(original);
return mode;
}
}
Scan Command
If you need to change the simple configuration logic of the scan command, you override the
DecorateScanCommand method by using the PXOverride attribute and intercept the logic of the component
in this method, as shown in the following code.
public class MyScanExtension : SomeWMS.ScanExtension
{
[PXOverride]
public virtual ScanCommand<SomeWMS> DecorateScanCommand(
ScanCommand<SomeWMS> original,
Func<ScanCommand<SomeWMS>, ScanCommand<SomeWMS>> base_DecorateScanCommand)
{
var command = base_DecorateScanCommand(original);
{
someCommand
.Intercept.IsEnabled.ByConjoin(basis =>
{
// Add a condition when the command is enabled
})
.Intercept.Process.ByOverride((basis, base_Process) =>
{
// Change the logic of the command
});
}
return command;
}
}
Mode Redirect
If you need to change the simple configuration logic of the mode redirect, you override the
DecorateScanRedirect method by using the PXOverride attribute and intercept the logic of the
component in this method, as shown in the following code.
public class MyScanExtension : SomeWMS.ScanExtension
{
[PXOverride]
public virtual ScanRedirect<SomeWMS> DecorateScanRedirect(
ScanRedirect<SomeWMS> original,
Func<ScanRedirect<SomeWMS>, ScanRedirect<SomeWMS>> base_DecorateScanRedirect)
{
var redirect = base_DecorateScanRedirect(original);
return redirect;
}
}
Scan Question
If you need to change the simple configuration logic of the scan question, you override the
DecorateScanQuestion method by using the PXOverride attribute and intercept the logic of the
componentin this method, as shown in the following code.
public class MyScanExtension : SomeWMS.ScanExtension
{
[PXOverride]
Extending Scan Components | 73
return question;
}
}
Scan State
If you need to change the simple configuration logic of the scan state, you override the DecorateScanState
method by using the PXOverride attribute and intercept the logic of the component in this method, as shown in
the following code.
For a multi-mode barcode-driven form, such as Pick, Pack, and Ship (SO302020) or Receive and Put
Away (PO302020), each mode of the form can have its own InventoryItemState (or any other
state) component. Therefore, we recommend that you always check for the mode of the desired
component, unless you want to patch a component despite the mode it is used in.
.Intercept.GetByBarcode.ByOverride(
(basis, barcode, base_GetByBarcode) =>
{
// Change entity-fetching logic
})
.Intercept.HandleAbsence.ByAppend((basis, barcode) =>
{
// Handle the entity absence
})
.Intercept.ReportMissing.ByReplace((basis, barcode) =>
{
// Report that the entity is missing
})
.Intercept.Validate.ByPrepend((basis, entity) =>
{
// Validate the found entity
})
.Intercept.Apply.ByAppend((basis, entity) =>
{
// Entity-application logic
})
.Intercept.ClearState.ByAppend((basis, entity) =>
{
// Clear the changes made by the Apply method
})
.Intercept.SetNextState.ByOverride((basis, base_SetNextState) =>
{
// Set the next state })
.Intercept.OnTakingOver.ByAppend(basis =>
{
// Do something when the state takes over
})
.Intercept.OnDismissing.ByPrepend(basis =>
{
// Do something when the state gets dismissed
});
}
return state;
}
}
Complex Logic
If you need to change the complex logic used by the barcode-driven engine or by any of its components, you create
a nested extension that overrides the member of the particular ScanExtension component.
public class MyScanExtension : SomeWMS.ScanExtension
{
public class AlterSomeModeSomeCommandLogic :
SomeWMS.ScanExtension<SomeWMS.SomeMode.SomeCommand.Logic>
{
/// <summary>
/// Overrides <see cref="SomeWMS.SomeMode.SomeCommand.Logic.Bar(int)"/>
/// </summary>
[PXOverride]
public virtual void Bar(int input, Action<int> base_Bar)
Extending Scan Components | 75
{
// change the logic of Bar method
}
}
}
// WMS-related methods
// ...
public class Logic : ScanExtension // contains some complex logic that may be
customized in parts
{
public virtual void DoSomething()
{
// ...
Bar(42);
// ...
}
This activity will walk you through changing the order of value input.
Extending Scan Components | 76
Story
Suppose that you need to alter the default order of value input of the Pick mode of the Pick, Pack, and Ship
(SO302020) form from the one listed in the first column of the following table to the one listed in the second
column.
• The reference number of the shipment • The reference number of the shipment
• The location from which the item is picked • The item being picked
• The item being picked • The lot or serial number of the item
• The lot or serial number of the item • The expiration date of the item
• The expiration date of the item • The location from which the item is picked
Process Overview
You will change the transition map for the Pick scan mode. To do this, you will create a scan extension for the
PX.Objects.SO.WMS.PickPackShip class, override its DecorateScanMode method by using the
PXOverride attribute, and intercept the CreateTransitions method in this method.
System Preparation
Before you begin performing the step of this activity, do the following:
1. Prepare an Acumatica ERP instance by performing the Test Instance: To Deploy an Instance prerequisite
activity.
2. Enable the following features on the Enable/Disable Features (CS100000) form, where they are in the
Inventory and Order Management group of features:
• Multiple Warehouse Locations
• Warehouse Management
• Fulfillment
using System;
using PX.Data;
using PX.BarcodeProcessing;
using PX.Objects.SO.WMS;
using PX.Objects.IN.WMS;
6. Search for the mode, and patch its CreateTransitions method via interception functionality.
return mode;
}
}
7. Optional: Extract the exact patching logic to a separate virtual method of the extension so that you can
suppress this patch in the future.
{
var mode = base_DecorateScanMode(original);
return mode;
}
Extension of Scan Components: To Add a New Scan Command for an Existing Form
This activity will walk you through the implementation of a new scan command for an existing Acumatica ERP
form.
Story
Suppose that you need to add the ability to cancel physical inventory count on the Scan and Count (IN305020)
form.
Process Overview
You will add an additional scan command component. For details about the implementation of scan commands,
see Barcode Scan Commands: General Information. You will then override the DecorateScanMode method by
using the PXOverride attribute, and in this method, you will intercept the CreateCommands method. You will
use the ByAppend override strategy, which does not affect the original component.
System Preparation
Before you begin performing the step of this activity, do the following:
1. Prepare an Acumatica ERP instance by performing the Test Instance: To Deploy an Instance prerequisite
activity.
2. Enable the following features on the Enable/Disable Features (CS100000) form, where they are in the
Inventory and Order Management group of features:
• Multiple Warehouse Locations
Extending Scan Components | 79
• Warehouse Management
• Fulfillment
• Inventory Operations
using System;
using PX.Data;
using PX.BarcodeProcessing;
using PX.Common;
using PX.Objects.IN;
using PX.Objects.IN.WMS;
return true;
Extending Scan Components | 80
[PXLocalizable]
public abstract class Msg
{
public const string DisplayName = "Cancel Count";
public const string Success = "Physical inventory count has been
canceled.";
}
}
}
...
}
7. Search for the mode you are looking for, and patch its CreateCommands method via the interception
functionality.
return mode;
}
...
}
Extending Scan Components | 81
8. Optional: Extract the exact patching logic to a separate virtual method of the extension so that you can
suppress this patch in the future.
...
}
This activity will walk you through the implementation of additional entity validation.
Story
Suppose that you need to forbid the receipt of items whose country of origin is Antarctica on the Receive and Put
Away (PO302020) form.
Process Overview
You will override the DecorateScanState method by using the PXOverride attribute, and in this method,
you will intercept the Validation method. You need to add a validation method aer all the already existing
ones. Therefore, you will intercept the Validation method by using the ByAppend strategy.
System Preparation
Before you begin performing the step of this activity, do the following:
1. Prepare an Acumatica ERP instance by performing the Test Instance: To Deploy an Instance prerequisite
activity.
Extending Scan Components | 82
2. Enable the following features on the Enable/Disable Features (CS100000) form, where they are in the
Inventory and Order Management group of features:
• Multiple Warehouse Locations
• Warehouse Management
• Receiving
using System;
using PX.Data;
using PX.BarcodeProcessing;
using PX.Common;
using PX.Objects.PO.WMS;
itemState
.Intercept.Validate.ByAppend((basis, item) =>
{
(var xref, var inventory) = item;
if (inventory.CountryOfOrigin == "AQ")
return Validation.Fail(Msg.CannotReceiveItem,
inventory.InventoryCD);
return Validation.Ok;
});
}
return state;
}
[PXLocalizable]
public abstract class Msg
{
public const string CannotReceiveItem =
"The {0} item cannot be received.";
}
}
7. Optional: Extract the exact patching logic to a separate virtual method of the extension so that you can
suppress this patch in the future.
return state;
}
return Validation.Ok;
});
}
Extending Scan Components | 84
[PXLocalizable]
public abstract class Msg
{
public const string CannotReceiveItem =
"The {0} item cannot be received.";
}
}
This activity will walk you through the implementation of an alternate way to search for entities.
Story
Suppose that on the Receive and Put Away (PO302020) form, you need to be able to search purchase receipts by
their date because you have only one unreleased purchase receipt per day.
Process Overview
You will override the DecorateScanState method by using the PXOverride attribute, and in this method,
you will intercept the HandleAbsence method by using the ByAppend strategy. You will not override the
GetByBarcode method, but instead will override the HandleAbsence method because it contains the logic of
entity searching if the entity has not been found by the GetByBarcode method.
System Preparation
Before you begin performing the step of this activity, do the following:
1. Prepare an Acumatica ERP instance by performing the Test Instance: To Deploy an Instance prerequisite
activity.
2. Enable the following features on the Enable/Disable Features (CS100000) form, where they are in the
Inventory and Order Management group of features:
• Multiple Warehouse Locations
• Warehouse Management
• Receiving
using System;
using PX.Data;
Extending Scan Components | 85
using PX.BarcodeProcessing;
using PX.Objects.PO.WMS;
using PX.Objects.PO;
using PX.Data.BQL.Fluent;
using PX.Objects.AP;
using PX.Data.BQL;
return state;
}
}
And<POReceipt.receiptType.IsEqual<POReceiptType.poreceipt>>.
And<
Vendor.bAccountID.IsNull.
Or<Match<Vendor,
AccessInfo.userName.FromCurrent>>>>.
View.ReadOnly.Select(basis, date);
if (receiptByDate != null)
return AbsenceHandling.ReplaceWith(receiptByDate);
}
return AbsenceHandling.Skipped;
});
}
return state;
}
}
8. Optional: Extract the exact patching logic to a separate virtual method of the extension for being able to
suppress this patch in the future.
return state;
}
Extending Scan Components | 87
if (receiptByDate != null)
return AbsenceHandling.ReplaceWith(receiptByDate);
}
return AbsenceHandling.Skipped;
});
}
}
The barcode-driven engine uses a component-based system to make its customization easier.
Learning Objectives
In this chapter, you will learn how to customize a barcode-driven form.
Applicable Scenarios
You customize a barcode-driven form when you need to modify the behavior of this form.
Customization Approach
When you customize a barcode-driven form, you do the following:
1. Find the barcode scan class you want to customize, and create an extension for it by using the
ScanExtension component of the chosen class. For details about ScanExtension, see Extension of
Scan Components: Scan Extensions.
2. Decide which part of the logic of the form you want to change. Depending on the part you need to change,
use the recommendations in the following topics:
• If you need to customize the logic of the barcode scan class, see Customization of a Barcode-Driven Form:
Barcode Scan Class
• If you need to customize a scan component, see Extension of Scan Components: Customization of
Components
Namespaces
When you are customizing a barcode-driven form, you may need to import the following namespaces.
using System;
using System.Linq;
using System.Collections;
using System.Collections.Generic;
using PX.Common;
using PX.Data;
using PX.Data.BQL;
using PX.Data.BQL.Fluent;
using PX.BarcodeProcessing;
using PX.Objects.IN;
using PX.Objects.IN.WMS;
// XX is the name of the module in which the target class is located
using PX.Objects.XX;
using PX.Objects.XX.WMS;
Customizing a Barcode-Driven Form | 89
Generic Extensions
If you want to create a generic extension for a barcode-driven form, you use the following template. For details on
generic extensions, see Reusable Business Logic Implementation.
If you need to override the logic of the barcode scan class, you override the member of the class by using the
PXOverride attribute, as shown in the following code.
public class MyScanExtension : SomeWMS.ScanExtension
{
[PXOverride]
public virtual int Foo(string input, Func<string, int> base_Foo)
{
// Your logic that overrides the SomeWMS.Foo(string) method
}
}
This activity will walk you through the implementation of input handling without component usage.
Customizing a Barcode-Driven Form | 90
Story
Suppose that you want to add the ability to append text to the note of the document that the user is currently
working with on a barcode-driven form. Also, you want to make this ability generic for any barcode-driven form.
Process Overview
You will create a generic extension for the barcode-driven engine and implement custom logic in it.
System Preparation
Before you begin performing the step of this activity, do the following:
1. Prepare an Acumatica ERP instance by performing the Test Instance: To Deploy an Instance prerequisite
activity.
2. Enable the following features on the Enable/Disable Features (CS100000) form, where they are in the
Inventory and Order Management group of features:
• Multiple Warehouse Locations
• Warehouse Management
• Fulfillment
using System;
using PX.Data;
using PX.BarcodeProcessing;
using PX.Common;
using PX.Objects.IN;
using PX.Objects.IN.WMS;
5. Add additional type parameter that will represent the note owner entity.
{
protected TScanBasis Basis => Base1;
}
[PXOverride]
public virtual bool? ProcessCustomScan(
string barcode,
Func<string, bool?> base_ProcessCustomScan)
{
return base_ProcessCustomScan(barcode);
}
}
[PXOverride]
public virtual bool? ProcessCustomScan(string barcode,
Func<string, bool?> base_ProcessCustomScan)
{
if (barcode.StartsWith(NotePrefix))
{
string value = barcode.Substring(NotePrefix.Length).Trim();
if (!string.IsNullOrEmpty(value))
{
PXCache docCache = Basis.Graph.Caches<TDocument>();
// Use an abstract function to get the entity.
var document = GetNoteOwnerEntity();
$"[{Basis.Graph.Accessinfo.UserName}@{DateTime.Now.ToShortDateString()}]: {value}";
PXNoteAttribute.SetNote(docCache, document,
Customizing a Barcode-Driven Form | 92
string.IsNullOrEmpty(existingValue)
? newLine
: existingValue + Environment.NewLine + newLine);
Basis.SaveChanges();
Basis.ReportInfo(Msg.Success);
return true;
}
else
{
Basis.ReportError(Msg.Fail);
return false;
}
}
}
return base_ProcessCustomScan(barcode);
}
[PXLocalizable]
public abstract class Msg
{
public const string Success = "Your note was successfully added.";
public const string Fail = "The system was not able to add your note.";
}
}
The ProcessCustomScan method will be called before the current ScanState processor attempts to
process a barcode only if none of ScanCommand processors and none of ScanRedirect processors are
able to handle the scanned barcode.
The return value of the ProcessCustomScan method has the bool? type. If the method returns null,
the scanned barcode will be passed to the current ScanState processor. Otherwise, the returned value of
the ProcessCustomScan method would show whether the custom scan handler succeeded or failed.
You use the note:text to append pattern to recognize the wish of a user to append text to the note of the
current entity.
8. Add the end extension for the specific barcode scan class.