Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating TreeView for Acumatica

In Acumatica I would like to create a TreeView for a Bill of Material Screen. This is what the screen currently looks like and I need help populating the tree seen to the left of the grids. I would like the top value of the tree to be the BOM Item and then the materials that make up that item would open below that for each item. Here is my code so far.

<DataTrees>
        <px:PXTreeDataMember TreeKeys="BOMID" TreeView="Nodes" />
        <px:PXTreeDataMember TreeView="_AMBOMTree_Tree_" TreeKeys="OberNbr" />
        <px:PXTreeDataMember TreeKeys="Key" TreeView="CacheTree" />
        <px:PXTreeDataMember TreeKeys="Key" TreeView="EntityItems" />
    </DataTrees>

This is for the Data Tree itself.

<px:PXTreeSelector CommitChanges="True" SuppressLabel="False" ID="edGraphType" runat="server" DataField="GraphType" PopulateOnDemand="True"
            ShowRootNode="False" TreeDataSourceID="ds" TreeDataMember="CacheTree" InitialExpandLevel="0" MinDropWidth="297" MaxDropWidth="500"
            TextField="Name" Size="XL">
            <Images>
                <ParentImages Normal="tree@Folder" Selected="tree@FolderS" />
                <LeafImages Normal="tree@Screen" Selected="tree@Screen" />
            </Images>
            <DataBindings>
                <px:PXTreeItemBinding DataMember="CacheTree" TextField="Name" ValueField="SubKey" ImageUrlField="Icon" />
            </DataBindings>
        </px:PXTreeSelector>

Here is the TreeSelector. Now I pulled this from a different screen and wondering exactly how this all works. Since I want the Item to show up at the top level then go to the Materials that make up that item. I want the grids to populate if an item is selected. I know the DataField = GraphType is not correct as this was pulled from a different screen, but I do not know the value that is supposed to be here.

<px:PXTreeView ID="tree" runat="server" DataSourceID="ds" Height="500px" PopulateOnDemand="True" ShowRootNode="False"
                    ExpandDepth="1" AutoRepaint="true" Caption="Tree" AllowCollapse="true" PreserveExpanded="true">
                    <ToolBarItems>
                        <px:PXToolBarButton Tooltip="Reload Tree" ImageKey="Refresh">
                            <AutoCallBack Target="tree" Command="Refresh" />
                        </px:PXToolBarButton>
                    </ToolBarItems>
                    <AutoCallBack Target="grid" Command="Refresh" ActiveBehavior="True">
                        <Behavior RepaintControlsIDs="gridMatl" />
                        <Behavior RepaintControlsIDs="gridStep" />
                        <Behavior RepaintControlsIDs="gridTool" />
                        <Behavior RepaintControlsIDs="gridOvhd" />
                    </AutoCallBack>
                    <AutoSize Enabled="True" MinHeight="300" />
                    <DataBindings>
                        <px:PXTreeItemBinding DataMember="Nodes" TextField="Name" ValueField="AssignmentRouteID" ImageUrlField="Icon" />
                    </DataBindings>
                </px:PXTreeView>

Then this is the Tree View itself.

If someone can help me code up this tree that would be great or at least point me in the right direction.

like image 694
Dane Avatar asked Apr 09 '26 10:04

Dane


1 Answers

To add a Data Tree in Acumatica, there is 3 things to do:

  1. In the ASPX Page, declare the PXDataTreeMember in the PXDataSource:

    <px:PXDataSource ID="ds" runat="server" Visible="True" PrimaryView="Document" SuspendUnloading="False" TypeName="PX.TreeDemo.TreeEntry">
      <CallbackCommands>
            <px:PXDSCallbackCommand CommitChanges="True" Name="MyAction" />
            <px:PXDSCallbackCommand CommitChanges="True" Name="MyOtherAction" Visible="False" />
            <px:PXDSCallbackCommand CommitChanges="True" Name="SomeOtherOtherAction" Visible="False" />        
        </CallbackCommands>
        <DataTrees>
            <px:PXTreeDataMember TreeView="Nodes" TreeKeys="NodeID" />
        </DataTrees>
    </px:PXDataSource>
    
  2. Add the PXTreeView (Usually with a PXSplitContainer) with the linked FormView that will display the selected record.

    <px:PXSplitContainer runat="server" ID="sp1" SplitterPosition="300">
        <AutoSize Enabled="true" Container="Window" />
        <Template1>
            <px:PXTreeView ID="tree" runat="server" DataSourceID="ds" Height="180px"
                ShowRootNode="False" AllowCollapse="False" Caption="Tree Demo" AutoRepaint="True"
                SyncPosition="True" ExpandDepth="4" DataMember="Nodes" KeepPosition="True" 
                SyncPositionWithGraph="True" PreserveExpanded="True" PopulateOnDemand="true" SelectFirstNode="True">
                <ToolBarItems>
                    <px:PXToolBarButton Text="Add Tree Node" Tooltip="Add Tree Node">
                        <AutoCallBack Command="AddNode" Enabled="True" Target="ds" />
                        <Images Normal="main@AddNew" />
                    </px:PXToolBarButton>
    
                    <px:PXToolBarButton Text="Delete Tree Node" Tooltip="Delete Tree Node">
                        <AutoCallBack Command="DeleteNode" Enabled="True" Target="ds" />
                        <Images Normal="main@Remove" />
                    </px:PXToolBarButton>
                </ToolBarItems>
                <AutoCallBack Target="formTree" Command="Refresh" Enabled="True" />
                <DataBindings>
                    <px:PXTreeItemBinding DataMember="Nodes" TextField="DisplayName" ValueField="NodeID" />
                </DataBindings>
                <AutoSize Enabled="True" />
            </px:PXTreeView>
        </Template1>
        <Template2>
            <px:PXFormView ID="formTree" runat="server" DataSourceID="ds" DataMember="CurrentNode" 
                        Caption="Node Info" Width="100%" >
                <Template>
                    <px:PXLayoutRule ID="PXLayoutRule1" runat="server" StartColumn="True" LabelsWidth="S" ControlSize="SM" />
                    <px:PXSelector ID="edNodeID" runat="server" DataField="NodeID" CommitChanges="True" AutoRefresh="True" />                   
                </Template>
            </px:PXFormView>
        </Template2>
    </px:PXSplitContainer>
    
  3. Create the DataViews, DataViews delegate and Tree Actions.

    public PXSelect<TreeNode> Nodes;
    public PXSelect<TreeNode,
               Where<TreeNode.parentNodeID,
                   Equal<Optional<TreeNode.nodeID>>>> ChildNodes;
    public PXSelect<TreeNode, 
                Where<TreeNode.nodeID, 
                    Equal<Current<TreeNode.nodeID>>>> CurrentNode;
    
    #endregion
    
    #region Delegates
    
    protected virtual IEnumerable nodes(
        [PXInt]
        int? nodeID
    )
    {
        if (nodeID == null)
        {
            yield return new TreeNode()
            {
                ParentNodeID = 0,
                NodeID = 0
            };
        }
        else
        {
            foreach (TreeNode node in ChildNodes.Select(nodeID))
            {
                yield return node;
            }
        }
    
    }
    protected virtual IEnumerable currentNode()
    {
        if (Nodes.Current != null)
        {
            var isNotRoot = Nodes.Current.NodeID != 0;
    
            //Situation where we would want to limit the recursion to one 
            AddNode.SetEnabled(!isNotRoot);
            DeleteNode.SetEnabled(isNotRoot);
    
            Caches[typeof(TreeNode)].AllowInsert = isNotRoot;
            Caches[typeof(TreeNode)].AllowDelete = isNotRoot;
            Caches[typeof(TreeNode)].AllowUpdate = isNotRoot;
    
            foreach (TreeNode item in PXSelect<TreeNode,
                                                    Where<TreeNode.nodeID, 
                                                        Equal<Required<TreeNode.nodeID>>>>.
                                                Select(this, Nodes.Current.NodeID))
            {
                yield return item;
            }
        }
    }
    
    
    public PXAction<NodeSetup> AddNode;
    [PXUIField(DisplayName = " ", MapEnableRights = PXCacheRights.Select, MapViewRights = PXCacheRights.Select, Enabled = true)]
    [PXButton()]
    public virtual IEnumerable addNode(PXAdapter adapter)
    {
        var selectedNode = Nodes.Current;
        if (selectedNode.ParentNodeID == 0)
        {
            var inserted = (TreeNode)Caches[typeof(TreeNode)].Insert(new TreeNode
            {
                ParentNodeID = Nodes.Current.NodeID
            });
    
            inserted.TempChildID = inserted.NodeID;
            inserted.TempParentID = inserted.ParentNodeID;
    
            Nodes.Cache.ActiveRow = inserted;
        }
        return adapter.Get();
    }
    
    public PXAction<NodeSetup> DeleteNode;
    [PXUIField(DisplayName = " ", MapEnableRights = PXCacheRights.Select, MapViewRights = PXCacheRights.Select, Enabled = true)]
    [PXButton()]
    public virtual IEnumerable deleteNode(PXAdapter adapter)
    {
        var selectedNode = Nodes.Current;
        if(selectedNode.NodeID != 0)
        {
            if(selectedNode.ParentNodeID == 0)
            {
                var childrenNodes = ChildNodes
                                     .Select(selectedNode.NodeID)
                                     .Select(br => (TreeNode)br).ToList();
    
                if (childrenNodes.Any())
                {
                    if (Document.Ask(Messages.ValidationDeleteChildren, MessageButtons.YesNo) == WebDialogResult.Yes)
                    {
                        foreach(var childrenNode in childrenNodes)
                        {
                            Caches[typeof(TreeNode)].Delete(childrenNode);
                        }
                        Caches[typeof(TreeNode)].Delete(selectedNode);
                    }
                }
                else
                {
                    Caches[typeof(TreeNode)].Delete(selectedNode);
                }
            }
            else
            {
                Caches[typeof(TreeNode)].Delete(selectedNode);
            }
        }
    
        return adapter.Get();
    }
    

A easiest way to approach the problem is usually to work with a single ID. Because your objects won't all be the same (BOM / MATL / ETC), you would need to be able to figure it out from the ID. The solution I would suggest is to format the NodeID to have all the information necessary to get the record you want (E.I. BOM-BM00001_MATL-INVIDANDOTHERKEYFIELD). This way you will know what level you are and return the right children (In the Dataview delegate nodes(int? nodeID)).

like image 97
Philippe Avatar answered Apr 11 '26 23:04

Philippe



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!