Barcode Scan States: To Create the Confirmation State
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.
virtual
methods: Confirm()
: The entry point method of the scan extension componentCanConfirm()
: A method that is used to extend the validation logicConfirmRow()
: The method that contains the main logic of confirmationFindDetailRow()
: 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:
- Prepare an MYOB Acumatica instance by performing the Test Instance: To Deploy an Instance prerequisite activity.
- Create a barcode scan class by performing the Barcode Scan Class: To Create a Barcode Scan Class prerequisite activity.
- Create the scan mode and define its required properties by performing the Barcode Scan Mode: To Define the Required Properties prerequisite activity.
- Create the set of scan states by performing the Barcode Scan States: To Create the Set of Scan States prerequisite activity.
Step 1: Defining the Required Member
- In the
CountMode
class, create theConfirmState
class as follows.public class INScanCount : WMSBase { ... public sealed class CountMode : ScanMode { ... public sealed class ConfirmState : WMSBase.ConfirmationState { } } }
- Define the required member as
follows.
public sealed class ConfirmState : WMSBase.ConfirmationState { public override string Prompt => Basis.Localize(Msg.Prompt, Basis.SightOf<WMSScanHeader.inventoryID>(), Basis.Qty, Basis.UOM); [PXLocalizable] new public abstract class Msg { public const string Prompt = "Confirm counting {0} x {1} {2}."; } }
Step 2: Implementing the Confirmation Process
- Define a scan state-related extension class inside the scan state and place
in the extension class only component related logic, as shown in the
following
code.
public sealed class ConfirmState : WMSBase.ConfirmationState { ... public class Logic : ScanExtension { public virtual FlowStatus Confirm() { if (!CanConfirm(out var error)) return error; return ConfirmRow(); } protected virtual bool CanConfirm(out FlowStatus error) { if (Basis.Document == null) { error = FlowStatus.Fail(RefNbrState.Msg.NotSet); return false; } if (Basis.DocumentIsEditable == false) { error = FlowStatus.Fail(RefNbrState.Msg.InvalidStatus, Basis.DocumentView.Cache.GetStateExt<INPIHeader.status>(Basis.Document)); return false; } if (Basis.InventoryID == null) { error = FlowStatus.Fail(InventoryItemState.Msg.NotSet); return false; } if (Basis.CurrentMode.HasActive<LotSerialState>() && Basis.LotSerialNbr == null) { error = FlowStatus.Fail(LotSerialState.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; } protected virtual FlowStatus ConfirmRow() { INPIDetail row = FindDetailRow(); decimal? newQty = row?.PhysicalQty ?? 0; if (Basis.Remove == true) newQty -= Basis.BaseQty; else newQty += Basis.BaseQty; 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; row.PhysicalQty = Basis.BaseQty; row.BookQty = 0; row = Basis.Details.Insert(row); Basis.SaveChanges(); row = Basis.Details.Locate(row) ?? row; } 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; } protected virtual INPIDetail FindDetailRow() { var findDetailCmd = BqlCommand.CreateInstance(Basis.Details.View.BqlSelect.GetType()); findDetailCmd = findDetailCmd.WhereAnd<Where< INPIDetail.inventoryID.IsEqual<WMSScanHeader.inventoryID.FromCurrent>. And<INPIDetail.siteID.IsEqual<WMSScanHeader.siteID.FromCurrent>> >>(); if (Basis.CurrentMode.HasActive<LocationState>() && Basis.LocationID != null) findDetailCmd = findDetailCmd.WhereAnd<Where<INPIDetail.locationID.IsEqual< WMSScanHeader.locationID.FromCurrent>>>(); if (Basis.CurrentMode.HasActive<LotSerialState>() && Basis.LotSerialNbr != null) findDetailCmd = findDetailCmd.WhereAnd<Where<INPIDetail.lotSerialNbr.IsEqual< WMSScanHeader.lotSerialNbr.FromCurrent>>>(); var findDetailView = Basis.Graph.TypedViews.GetView(findDetailCmd, false); var findResultSet = (PXResult<INPIDetail>)findDetailView.SelectSingle(); return findResultSet; } } ... }
- Implement the PerformConfirmation() method as
follows.
public sealed class ConfirmState : WMSBase.ConfirmationState { ... protected override FlowStatus PerformConfirmation() => Get<Logic>().Confirm(); ... }
- Fix or suppress the PX1016 error that is displayed by
Acuminator for the
Logic
class.