Processing Forms: Implementation of Processing Operations
A processing operation is implemented as a method that is invoked from a processing or data entry form. On a processing form, you specify the method that is invoked when a user clicks Process or Process All on the form toolbar. On a data entry form, you define a button that invokes the processing method in a separate thread.
- Define a non-static method that uses a single record as the input parameter. This way can be used to process a single record independently from other records of the same class.
- Define a static method that uses a list of records as the input parameter.
This way can be used to process a list of records. In this method, you can
reorder the records in the list before processing, as well as check
dependencies between records during processing.Note:If you are using the static method, you can pass a cancellation token in the action delegate and initiate the cooperative cancellation by using the ThrowIfCancellationRequested() method according to the cooperative cancellation pattern in .NET.
The following sections describe these ways of defining a processing method and the ways to display errors and warnings.
Using a Non-Static Processing Method
In a simple case, to process a single record, you can define a non-static processing method in the data entry graph, as the following code shows.
// The data entry graph
public class SalesOrderEntry : PXGraph<SalesOrderEntry, SalesOrder>
{
...
// A non-static processing method that works with a single record
public void ApproveOrder(SalesOrder order, bool isMassProcess = false)
{
// Process the record here
}
}
To make the system invoke the method in a separate thread, you can use the PXLongOperation.StartOperation() method. Within the method that you pass to StartOperation(), you have to create a new instance of the graph and invoke the processing method on that instance, as the following code shows.
public PXAction<SalesOrder> Approve;
[PXProcessButton]
[PXUIField(DisplayName = "Approve")]
protected virtual IEnumerable approve(PXAdapter adapter)
{
Actions.PressSave();
SalesOrder order = Orders.Current;
PXLongOperation.StartOperation(this, delegate()
{
SalesOrderEntry graph = PXGraph.CreateInstance<SalesOrderEntry>();
graph.ApproveOrder(order);
});
return adapter.Get();
}
new T()
graph constructor.The method passed into PXLongOperation.StartOperation() matches the following delegate type, which uses no input parameters.
delegate void PXToggleAsyncDelegate();
delegate()
) is used
to shorten the code in the example.Using a Static Processing Method
In a general case, to process a list of records that may depend on one another, you have to define the static processing method, as the following code shows. The processing method can have a second parameter of the CancellationToken type; you can later use this parameter to initiate the cooperative cancellation by calling the ThrowIfCancellationRequested method.
// The processing graph
public class ReorderProcess : PXGraph<ReorderProcess>
{
...
// Static processing method that works with a list of records
public static void Process(List<ProductReorder> products,
CancellationToken ct = default)
{
foreach (var order in list)
{
ct.ThrowIfCancellationRequested();
// Process the single record
Process(order);
}
}
}
// The data entry graph
public class SalesOrderEntry : PXGraph<SalesOrderEntry, SalesOrder>
{
...
// Static processing method that works with a list of records
public static void ReleaseDocs(List<ProductReorder> products)
{
// Process the records here
}
}
You can invoke the static processing method in the data entry graph, within the method passed in the PXLongOperation.StartOperation() method, as shown in the following code example.
public PXAction<SalesOrder> Release;
[PXProcessButton]
[PXUIField(DisplayName = "Release")]
protected virtual IEnumerable release(PXAdapter adapter)
{
Actions.PressSave();
SalesOrder order = Orders.Current;
List<SalesOrder> list = new List<SalesOrder>();
list.Add(order);
PXLongOperation.StartOperation(this, delegate()
{
SalesOrderEntry.ReleaseDocs(list);
});
return list;
}
The PXLongOperation.StartOperation() method creates a separate thread and executes the specified delegate asynchronously in this thread.
Displaying Messages and Processing Errors
To display a message on a form from a processing method, use the following static methods of the PXProcessing class:
- SetInfo(): Displays a green check mark for the processed row in the grid, which denotes the successful processing of the record.
- SetWarning(): Displays an exclamation mark for the processed row in the grid, which indicates that a warning has occurred during processing.
- SetError(): Displays a red X for the processed row in the grid, which denotes an error that has occurred during processing.
In each of these methods, you have to specify the initial object index in the list that is passed to the processing method. You can also specify the message text that appears for the row.
In the following example, you show the green check mark, which represents success, or the X icon, which represents an error, on a form for each processed data record. You then return the error to the UI if the processing of at least one record fails. (The following code attempts to process all records from the list.)
public static void Process(List<ProductReorder> products,
CancellationToken ct = default)
{
ReceiptEntry graph = PXGraph.CreateInstance<ReceiptEntry>();
bool erroroccurred = false;
// Reordered list
List<ProductReorder> productsToProceed =
products.OrderBy(item => item.SupplierID).ToList();
...
// Process records
foreach (ProductReorder rec in productsToProceed)
{
try
{
// Set the green check mark for the item by the initial index in
// the products list, not in productsToProceed
PXProcessing<ProductReorder>.SetInfo(
products.IndexOf(rec),
String.Format("The receipt {0} has been created",
doc.DocNbr));
}
catch (Exception e)
{
// Set the error flag
erroroccurred = true;
// Set the error for the item by the initial index in
// the products list, not in productsToProceed
PXProcessing<ProductReorder>.SetError(
products.IndexOf(rec), "A receipt cannot be created");
...
}
}
// Throw the error if at least one record has not been processed
if (erroroccurred)
throw new PXException("At least one product has not been processed.");
}
To display the error as a result of processing all records, you have to
throw the error at the end of a processing method.