Reusable Business Logic Implementation

Suppose that you want to use the same business logic in multiple places in your application. That is, you have at least two graphs in which you need to insert the logic. The graphs operate with data in the data access classes (DACs) and implement business logic through event handlers, actions, and other methods.

You encapsulate the business logic that you want to reuse in a generic graph extension, which is a graph extension that does not relate to any particular graph and can be used with any base graph. The generic graph extension operates with data by using the mapped cache extensions, which are cache extensions that are not bound to any particular DAC and can extend any DAC.

To connect the mapped cache extensions to a particular DAC, you use a mapping class, which maps the fields of a mapped cache extension to the fields of a DAC. To connect the generic graph extension to a particular base graph, in the base graph, you define an implementation class, which inherits the generic graph extension. The following diagram shows in yellow rectangles the classes that you need to implement to reuse the business logic.
Figure 1. Required classes


These classes are described in detail in the sections below. (For details on the implementation of the classes, see To Implement Reusable Business Logic and To Insert Reusable Business Logic That Has Already Been Declared.)

Mapped Cache Extension

A mapped cache extension is an analog of a data access class (DAC) for a generic graph extension. In the mapped cache extension, you include the main fields that are used in the reusable business logic implementation. You map the fields of a mapped cache extension to the fields of a base DAC by using the mapping class, which is described in the following section. The mapped cache extension can also include the fields that are not mapped to any base DAC fields. (For details, see Mapped Cache Extensions and the Application Database.)

The class of a mapped cache extension inherits from the PXMappedCacheExtension abstract class, which derives from PXCacheExtension and IBqlTable.

The declaration of a field of a mapped cache extension includes the following two required members, which are the same as the required members of a DAC field:
  • A public abstract class (which is also referred to as class field or BQL field).

    You derive the class from the IBqlField interface and assign it a name that starts with a lowercase letter.

  • A public virtual property (which is also referred to as property field).

    You assign the property a name that starts with an uppercase letter. The system assigns the PXMergeAttributes attribute with MergeMethod.Merge to each field of a mapped cache extension automatically. If you define the PXMergeAttributes attribute for a field of a mapped cache extension explicitly, the explicitly defined attribute overrides the automatically defined. You can also define any other attributes for the property field of the mapped cache extension, or not define the attributes at all.

The following code shows an example of a mapped cache extension.
//Mapped cache extension
public class Document : PXMappedCacheExtension
{
  //BAccountID field
  public abstract class bAccountID : IBqlField
  {
  }
  protected Int32? _BAccountID;

  public virtual Int32? BAccountID
  {
    get
    {
      return _BAccountID;
    }
    set
    {
      _BAccountID = value;
    }
  }

  //CuryID field
  public abstract class curyID : IBqlField
  {
  }
  protected String _CuryID;

  public virtual String CuryID
  {
    get
    {
      return _CuryID;
    }
    set
    {
      _CuryID = value;
    }
  }

  ...
}

Mapping Class

A mapping class is a protected class that defines the mapping between the fields of a mapped cache extension and the fields of a DAC. In a generic graph extension, you declare a mapping class for each mapped cache extension that you need to use in the reusable logic implementation.

A mapping class implements the IBqlMapping interface, which has the following two properties:
  • Extension: The mapped cache extension
  • Table: The DAC to which the extension is mapped
In the declaration of the mapping class, you also include declarations of the properties for each field of the mapped cache extension that you want to map to a field of the DAC, as the following code shows.
//A mapping class
protected class DocumentMapping : IBqlMapping
{
  public Type Extension => typeof(Document);
  protected Type _table;
  public Type Table => _table;

  public DocumentMapping(Type table)
  {
    _table = table;
  }
  public Type BAccountID = typeof(Document.bAccountID);
  public Type CuryInfoID = typeof(Document.curyInfoID);
  public Type CuryID = typeof(Document.curyID);
  public Type DocumentDate = typeof(Document.documentDate);
}
If the name of a property field of the DAC is the same as the name of the mapping class property, the DAC field will be automatically mapped to the field of the mapped cache extension by the implementation class (which is described below). If the name of a property field of the DAC differs from the name of the mapping class field, you redefine the mapping manually in the implementation class. If no field in the DAC has the name of the mapping class field, and no mapping is defined in the implementation class, the field of the mapped cache extension is not mapped to any base DAC field, as shown in the following diagram.
Figure 2. Mapping declaration


Generic Graph Extension

A generic graph extension is a public abstract class that encapsulates business logic that can be used in multiple places of an MYOB Acumatica application or an MYOB Acumatica Framework-based application. The class inherits from the PXGraphExtension<TGraph> class, as the following code shows.
public abstract class MultiCurrencyGraph<TGraph, TPrimary> : 
  PXGraphExtension<TGraph>
    where TGraph : PXGraph
    where TPrimary : class, IBqlTable, new()
{
}
In the generic graph extension, you declare the following items:
  • The mapping classes.
  • The protected abstract methods that return the mapping classes. You have to override these methods in the implementation class.
  • The views that can have either mapping-based declaration or standard declaration. You declare a mapping-based view by using the PXSelectExtension<Table> class.
  • The event handlers, actions, and other methods.

Implementation Class

An implementation class defines the implementation of a generic graph extension for a particular graph. You declare the implementation class as a class that derives from the generic graph extension class with the following type parameters:
  • The base graph to which you add reusable logic
  • The main DAC of the primary data view of the base graph
In this class, you can override the mapping defined by the mapping class, override other the methods of the base class, and insert your own views, methods, and event handlers, as the following code shows.
public class MultiCurrency : MultiCurrencyGraph<OpportunityMaint, CROpportunity>
{   
  protected override DocumentMapping GetDocumentMapping()
  {
    return new DocumentMapping(typeof(CROpportunity)) 
    {
      DocumentDate =  typeof(CROpportunity.closeDate)
    };
  }  

  protected override CurySourceMapping GetCurySourceMapping()
  {
    return new CurySourceMapping(typeof(Customer));
  }

  public PXSelect<CRSetup> crCurrency;
  protected PXSelectExtension<CurySource> SourceSetup => 
   new PXSelectExtension<CurySource>(crCurrency);

  protected virtual CurySourceMapping GetSourceSetupMapping()
  { 
    return new CurySourceMapping(typeof(CRSetup)) 
    {
      CuryID = typeof(CRSetup.defaultCuryID), 
       CuryRateTypeID = typeof(CRSetup.defaultRateTypeID)
    };        
  }

  protected override CurySource CurrentSourceSelect()
  {
    ...
  }
}