A Tree with a View

Joshua van Hoesen | July 19, 2019

Introduction

With the continued growth of data driven systems and utilization of information to shape a company’s direction, the need to represent data in a myriad of ways is a must for any ERP system. Though Acumatica provides a wide variety of out of the box options, in this article we will cover a custom methodology Acumatica implements and how it may be used in a general fashion.

Overview

The Acumatica TreeView is a powerful and little known data control that is rarely implemented within an instance, typically utilized when manipulating sitemap or approval related entities an example can be found with Screen ID [SM207060].
Below is a simple custom batch inquiry TreeView page that will be used to highlight each component needed for using this data control:

Page Elements

The FormView page will serve as the foundation for implementing the TreeView. Within the FormView data source, declare a DataTrees element and a further nested PXTreeDataMember therein, as shown below; you will require an PXTreeDataMember defined for every TreeView control you wish to implement on the page:


<px:PXDataSource ID="ds" runat="server" Visible="True" Width="100%" TypeName="TreeView.AABatchTreeInq" PrimaryView="Filter" AutoCallBack="True">
        <DataTrees>
            <px:PXTreeDataMember TreeView="Nodes" TreeKeys="NodeID" />
        </DataTrees>
    </px:PXDataSource>

 

Define a split container within the form view, <Template1> will contain the PXTreeView control whereas <Template2> will contain the controls displaying the information referenced from the selected data record as follows:


<asp:Content ID="cont2" ContentPlaceHolderID="phF" runat="Server">
<px:PXFormView ID="form" runat="server" DataSourceID="ds">
</px:PXFormView>
<px:PXSplitContainer runat="server" ID="sp0" SplitterPosition="300">
<Template1>
<px:PXTreeView ID="tree" runat="server" DataSourceID="ds">
</px:PXTreeView>
</Template1>
<Template2>
<px:PXFormView ID="form" runat="server" DataSourceID="ds">
</px:PXFormView>
</Template2>
</px:PXSplitContainer>
</asp:Content>

 

The TreeView declaration can be seen below and contains the following items:


<px:PXTreeView ID="tree" runat="server" DataSourceID="ds" Height="500px" PopulateOnDemand="True" ExpandDepth="1" ShowRootNode="False" AllowCollapse="true" AutoRepaint="true" SyncPosition="True">
      <ToolBarItems>
       <px:PXToolBarButton Tooltip="Reload Tree" ImageKey="Refresh">
        <AutoCallBack Target="tree" Command="Refresh" />
       </px:PXToolBarButton>
      </ToolBarItems>
      <AutoCallBack Target="currentNode" Command="Refresh" ActiveBehavior="true">
       <Behavior RepaintControlsIDs="form,grid" />
      </AutoCallBack>
      <AutoSize Enabled="True" MinHeight="300" />
 <DataBindings>
 <px:PXTreeItemBinding DataMember="Nodes" TextField="DisplayName" ValueField="NodeID" ImageUrlField="Icon" />
 </DataBindings>
 </px:PXTreeView>

AutoCallBack

  1. Target – Reference view of child records
  2. Command – Refresh
  3. ActiveBehavior – true
  4. Behavior – RepaintControlIDs, reference controls to be refreshed on selection of item on TreeView

PXTreeItemBinding

  1. DataMember – Determines view display data is retrieved from.
  2. TextField – Determines DAC field display text is populated from.
  3. ValueField – Determines DAC field selection key is populated from.
  4. ImageUrlField – Determines url of image to be displayed.

Graph Elements

You must declare a view that will be referenced by the PXTreeItemBinding ASP.Net element of the PXTreeView control, this view must implement an IEnumerable with an argument matching the data type and name from the PXTreeItemBinding ValueField. 


using System;
using System.Collections;
using System.Collections.Generic;
using PX.SM;
using PX.Data;
using PX.Objects.GL;
using PX.Web.UI;

namespace TreeView
{
    public class AABatchTreeInq : PXGraph<AABatchTreeInq>
    {
        public PXCancel<AABatchTreeInqFilter> Cancel;

        public PXFilter<AABatchTreeInqFilter> Filter;

        public PXSelect<AABatchNode> Nodes;

        public PXSelectReadonly<Batch, Where<Batch.noteID, Equal<Current<AABatchNode.nodeID>>>> CurrentNode;

        public PXSelectReadonly<GLTran, Where<GLTran.module, Equal<Current<Batch.module>>, And<GLTran.batchNbr, Equal<Current<Batch.batchNbr>>>>> Items;

        static readonly Guid root = new Guid("00000000-0000-0000-0000-000000000000"); //Root
        static readonly Guid ap = new Guid("00000000-0000-0000-0000-000000000001"); //AP
        static readonly Guid ar = new Guid("00000000-0000-0000-0000-000000000002"); //AR
        static readonly Guid gl = new Guid("00000000-0000-0000-0000-000000000003"); //GL

        protected virtual IEnumerable nodes([PXDBGuid] Guid? nodeID)
        {
            List<AABatchNode> batches = new List<AABatchNode>();
            if (nodeID == null)
            {
                batches.Add(new AABatchNode() { DisplayName = BatchModule.AP, Icon = Sprite.Main.GetFullUrl("Folder"), ParentID = root, NodeID = ap });
                batches.Add(new AABatchNode() { DisplayName = BatchModule.AR, Icon = Sprite.Main.GetFullUrl("Folder"), ParentID = root, NodeID = ar });
                batches.Add(new AABatchNode() { DisplayName = BatchModule.GL, Icon = Sprite.Main.GetFullUrl("Folder"), ParentID = root, NodeID = gl });
            }
            else
            {
                bool isChild = (nodeID != root) && (nodeID != gl) && (nodeID != ap) && (nodeID != ar);

                AABatchTreeInqFilter filter = Filter.Current;
                if (filter != null && filter.BranchID.HasValue && filter.LedgerID.HasValue && !isChild)
                {
                    PXSelectBase<Batch> cmd = new PXSelect<Batch, Where<Batch.branchID, Equal<Required<Batch.branchID>>, And<Batch.ledgerID, Equal<Required<Ledger.ledgerID>>>>>(this);
                    if (nodeID == gl)
                    {
                        cmd.WhereAnd<Where<Batch.module, Equal<BatchModule.moduleGL>>>();
                    }
                    else if (nodeID == ap)
                    {
                        cmd.WhereAnd<Where<Batch.module, Equal<BatchModule.moduleAP>>>();
                    }
                    else if (nodeID == ar)
                    {
                        cmd.WhereAnd<Where<Batch.module, Equal<BatchModule.moduleAR>>>();
                    }
                    foreach (Batch batch in cmd.SelectWindowed(0, 10, new object[] { filter.BranchID, filter.LedgerID }))
                    {
                        batches.Add(new AABatchNode() { NodeID = batch.NoteID, DisplayName = batch.BatchNbr, ParentID = nodeID });
                    }
                }
            }
            return batches;
        }

        protected virtual IEnumerable items([PXDBGuid] Guid? nodeID)
        {
            Batch batch = PXSelectReadonly<Batch, Where<Batch.noteID, Equal<Required<Batch.noteID>>>>.Select(this, nodeID);//CurrentNode.Current;
            if (batch != null)
            {
                foreach (GLTran item in PXSelect<GLTran, Where<GLTran.module, Equal<Required<Batch.module>>, And<GLTran.batchNbr, Equal<Required<Batch.batchNbr>>>>>.
                                    Select(this, new object[] { batch.Module, batch.BatchNbr }))
                {
                    yield return item;
                }
            }
        }
    }
}

 

The data access class declaration for the PXTreeView view is simple given the properties will be populated from data selected within the views IEnumerable.


 [Serializable]
 public class AABatchNode : IBqlTable
 {
 #region ParentID
 public abstract class parentID : IBqlField
 {
 }
 [PXGuid]
 public virtual Guid? ParentID { get; set; }
 #endregion
 #region NodeID
 public abstract class nodeID : IBqlField
 {
 }
 [PXGuid]
 public virtual Guid? NodeID { get; set; }
 #endregion
 #region DisplayName
 public abstract class displayName : IBqlField
 {
 }
 [PXDBString(60, IsUnicode = true)]
 [PXUIField(DisplayName = "Name", Visibility = PXUIVisibility.SelectorVisible)]
 public virtual string DisplayName { get; set; }
 #endregion
 #region Icon
 public abstract class icon : IBqlField
 {
 }
 [PXString(250)]
 public virtual string Icon { get; set; }
 #endregion
 }

Conclusion

The TreeView control is an interesting and powerful control to display data in a highly customizable manner. The complexities and difficulties of implementing mostly leave it suited for niche situations where traditional representation would not meet the needs of the client.


*To view and run the complete working code in context, download the deployment package now by clicking on the link.

Joshua van Hoesen

Lead Software Engineer at Accounting System Integrators. He leads the development team, which built NonProfitPlus – business management software engineered with non-profit organizations in mind and powered by Acumatica ERP to deliver a suite of fully integrated applications.

Categories: Developers

Subscribe to our bi-weekly newsletter

Evaluating Cloud Financials Systems?

Don’t go any further until you’ve read Gartner’s analysis of what 10 leading vendors have to offer. You’ll also learn why Gartner named Acumatica a Visionary.