1

I am trying to add a dialog box to the SO301000 screen, this dialog box (I think its also considered a smart panel but the difference is lost on me) is just supposed to show a list of orders that a customer has made.

What I have working:

1: I am able to pull all of the orders that a customer has made.

2: I am able to open/close the dialog box after clicking the action.

3: An order IS able to be put into grid.

What doesn't work:

1: I am unable to get more than one order into the grid.

I have no need to edit the orders in this grid, I just want to puke out quick information.

 public class SOOrderEntry_Extension : PXGraphExtension<SOOrderEntry>
    {

        public PXFilter<MSCustomerOrderDac> MSCustomerViewForm;
        public PXFilter<MSCustomerOrderDac> MSCustomerOrderViews; //Issue.


        public PXAction<SOOrder> ViewCustomerOrders;
        [PXUIField(DisplayName = "View Custoemr", MapEnableRights = PXCacheRights.Select, MapViewRights = PXCacheRights.Select)]
        [PXButton(Category = "Other")]
        [PXUIEnabled(typeof(Where<SOOrder.customerID.IsNotNull>))]
        protected virtual IEnumerable viewCustomerOrders(PXAdapter adapter)
        {
            MSCustomerOrderViews.AllowInsert=true;

            PXSelectBase<SOOrder> orders =
                    new PXSelectReadonly<SOOrder,
                    Where<SOOrder.customerID, Equal<Current<SOOrder.customerID>>>>(Base);
            int linenumber = 0;

             foreach (SOOrder order in orders.Select())
             {
                MSCustomerOrderDac newOrder = new MSCustomerOrderDac();
                newOrder.OrderNumber = order.OrderNbr;
                newOrder.LineNbr = linenumber++;
                newOrder = MSCustomerOrderViews.Insert(newOrder);

             }

            if (MSCustomerViewForm.AskExt(true) != WebDialogResult.OK) //need this to show the form
            {}
            
            return adapter.Get();
        }

        [PXVirtual]
        [Serializable]
        public class MSCustomerOrderDac : IBqlTable
        {

            #region OrderNumber 
            [PXString]
            [PXUIField(DisplayName = "Order Number")]
            public virtual String OrderNumber { get; set; }
            public abstract class orderNumber  : PX.Data.BQL.BqlString.Field<orderNumber> { }
            #endregion

            [PXInt(IsKey = true)]
            public virtual Int32? LineNbr { get; set; }
            public abstract class lineNbr : PX.Data.BQL.BqlInt.Field<lineNbr> { }



        }
    }

This is the whole of my code, I also tried breaking the loop and adding more than 1 items manually but that made no difference.

I also found this thread on the community forums: https://community.acumatica.com/customizations-187/dialog-with-grid-with-in-memory-dac-from-action-button-8578

However I think he and I were having different issues.

Also, I have just noticed that the order that it is pushing is aways the same one.

Shoonly
  • 15
  • 1
  • 4

3 Answers3

2

The following code snippits will give you the desired functionality.

Create a graph extension that has an declared data view that will retrieve the information desired, in this case SOOrders related to the Customer.

public class SOOrderEntryExtension : PXGraphExtension<SOOrderEntry>
{
    [PXFilterable]
    [PXCopyPasteHiddenView]
    public PXSelectReadonly<SOOrder2, Where<SOOrder2.customerID, Equal<Current<SOOrder.customerID>>>> RelatedOrders;

    public PXAction<SOOrder> ViewCustomerOrders;
    [PXUIField(DisplayName = "View Customer Orders", MapEnableRights = PXCacheRights.Update, MapViewRights = PXCacheRights.Update)]
    [PXButton]
    protected virtual void viewCustomerOrders()
    {
             RelatedOrders.AskExt(true);
    }

    protected virtual void __(Events.RowSelected<SOOrder> e)
    {
        if(e.Row is SOOrder row)
        {
            ViewCustomerOrders.SetVisible(true);
            ViewCustomerOrders.SetEnabled(true);
        }
    }
}

If multiple views are declared on a graph / extension that utilize the same DAC issues may occur. To overcome this issue a derived DAC needs to be created [SOOrder2] with the key / search criteria fields redeclared which will allow the framework to properly segregate the cache types.

[Serializable]
[PXHidden]
public class SOOrder2 : SOOrder
{
    #region DocType
    public new abstract class orderType : BqlString.Field<orderType>
    {
    }
    #endregion

    #region RefNbr
    public new abstract class orderNbr : BqlString.Field<orderNbr>
    {
    }
    #endregion

    #region CustomerID
    public new abstract class customerID : BqlInt.Field<customerID>
    {
    }
    #endregion
}

The smart panel that your view utilizes can be found below :

<px:PXSmartPanel runat="server" ID="CustomerRelatedOrderPnl" Height="550px" Width="950px" CaptionVisible="True" Caption="Related Orders" Key="RelatedOrders" AutoCallBack-Target="CustomerRelatedOrderGrid" AutoCallBack-Command="Refresh">
    <px:PXGrid runat="server" ID="CustomerRelatedOrderGrid" SyncPosition="True" Height="100%" SkinID="Inquire" Width="100%" DataSourceID="ds" NoteIndicator="False">
        <AutoSize Enabled="True" />
        <Levels>
            <px:PXGridLevel DataMember="RelatedOrders">
                <Columns>
                    <px:PXGridColumn DataField="OrderType" Width="140" />
                    <px:PXGridColumn DataField="OrderNbr" Width="140" />
                    <px:PXGridColumn DataField="CustomerID" Width="140" />
                    <px:PXGridColumn DataField="Status" Width="140" />
                    <px:PXGridColumn DataField="OrderDate" Width="140" />
                    <px:PXGridColumn DataField="CuryOrderTotal" Width="140" />
                </Columns>
            </px:PXGridLevel>
        </Levels>
    </px:PXGrid>
    <px:PXPanel runat="server" ID="CustomerRelatedOrderButtonPnl">
        <px:PXButton runat="server" DialogResult="OK" Text="Ok" CommandSourceID="ds" ID="CustomerRelatedOrderOK" />
    </px:PXPanel>
</px:PXSmartPanel>

The results of this code can be seen here :

RelatedOrdersPopUp

Joshua Van Hoesen
  • 1,684
  • 15
  • 30
  • Awesome thank you so much! I actually did figure this out in a slightly different way, do you know if there is a huge performance hit if you do the PXSelect in in the action as opposed to in the class Def? I'm going to post my code here for completeness if you want to take a look. – Shoonly Feb 10 '23 at 17:09
  • I know it has been a while, but I am having many issues with the derived DAC. It has consistently caused other customizations which rely on SOOrders we have to break. To be more specific it has been breaking our order importers, are there other fields that I must overwrite? Or do I need to decorate the DAC with a few more things? The error:an error occured during the processing of OrderDate. Unable to cast object of type SOOrder to type SOOrder2 (Note, I have it renamed in our instance as there were collisions, but to stay consistent with the answer Im calling it SOORder2) – Shoonly Feb 24 '23 at 16:37
  • @Shoonly The error reads as if you're trying to utilize SOOrder2 in a context beyond that which is in the example. I did make a modification to my answer adding the [PXCopyPasteHiddenView] attribute which will prevent issues when trying to copy and paste SO Orders. – Joshua Van Hoesen Feb 24 '23 at 19:43
  • Van Hoessen, I did the same thing for copy and paste. But how would I disable this for everything except screen 301000? I'm going to try to use a [PXProjection(fill in here)] but I havent had time to implement it yet. I just feel like I am missing something as I know other extension do basically the same things, but don't break other random parts of the code base. Thank you so much for your help by the way I am just feeling mildly disheartened. – Shoonly Feb 27 '23 at 14:52
  • @Shoonly We are now reaching outside the bounds of this stack-overflow question. My contact information is in my profile. Lets connect, identify the issue and then we can create another stack-overlfow question / answer to educate the community on what we found. – Joshua Van Hoesen Feb 27 '23 at 15:13
2

Not an answer, but I don't have enough reputation to comment.

Some extra information :

  • A PXFilter View can only have 1 instance of the DAC on the Graph.
  • You don't need to create a PXSelectBase instance unless you're going to be modifying the query based on conditions, or unless you're going to pass it somewhere else, such as into a PXFieldScope. You can simply do this (Using the proper separate DAC to prevent caching issues) :
foreach
(
    SOOrder2 soOrder in PXSelectReadonly
    <
        SOOrder2,
        Where
        <
            SOOrder2.customerID, Equal<Current<SOOrder.customerID>>
        >
    >.Select(Base)
)
{
    ...
}
  • By performing your Select and Insert inside the Action Delegate rather than having a View like in Josh's answer, or having a View Delegate, your logic will insert records every single time someone pushes the button, even if they already exist in the Cache.
  • You shouldn't need to check the result of AskExt.
  • I'm not 100% sure about this : If User A has SOOrderEntry open on an SOOrder and sits there for a bit, then User B creates a new SOOrder for the same Customer, if User A opens the popup, then it could be possible that Acumatica may not run the query again, and the new SOOrder may not pop up. If you experience this behavior, then you would have to Clear the View before calling AskExt. Acumatica should then run the query again, and the new SOOrder should appear.
  • Acumatica may try to persist your View, so you should put a PXVirtual Attribute on it, following the instructions in its summary tags.
  • You may want to add conditions to your query to exclude the current record from the results, unless it is your desire to include the current record in your popup.
  • Yeah the limit on the comments is irritating thanks for the response though! All of those make sense, I think I have the virtual on the view unless what you are talking about is the PXFilter. All of those are great points thank you, I am still learning Acumatica so everything is appreciated – Shoonly Feb 10 '23 at 17:23
0

Since @Joshua Van Hoesen gave such a detailed answer, I am going to post the solution I came to for completeness's sake and if anyone wants to see potentially another way to do it.

public class SOOrderEntry_Extension : PXGraphExtension<SOOrderEntry>
    // It might be best practice to move these to their own file, but I think its better to keep it all together.
    {
        // This is just for the form so the cache for the grid doesnt get over written.
        public PXFilter<MSCustomerOrderDac> MSCustomerViewForm; 
        
        // This is the for the grid, we have to use PXSelect because it is able to grab more than one item.
        // Since our DAC is virtual we must mark it as such AND create a delagate.
        [PXVirtualDAC]
        public PXSelect<MSCustomerOrderDac> MSCustomerOrderViews;

        // This delegate overrides the database call
        // first letter in lower case (Your view has it Upper case sicne hte names must be the same)
        public IEnumerable mSCustomerOrderViews()
        {
            // This just grabs the current view and ensures we dont go to the db, theoretically we could include anything.
            return MSCustomerOrderViews.Cache.Cached;
        }

        // This is our action, were need the map rights so we can interact with things.
        public PXAction<SOOrder> ViewCustomerOrders;
        [PXUIField(DisplayName = "View Customer Order History", MapEnableRights = PXCacheRights.Select, MapViewRights = PXCacheRights.Select)]
        [PXButton(Category = "Other",CommitChanges =true)]
        [PXUIEnabled(typeof(Where<SOOrder.customerID.IsNotNull>))]
        // This is the code that runs for the action that fills the grid.
        protected virtual IEnumerable viewCustomerOrders(PXAdapter adapter)
        {
            PXSelectBase<SOOrder> orders =
                    new PXSelectReadonly<SOOrder,
                    Where<SOOrder.customerID, Equal<Current<SOOrder.customerID>>>>(Base);

            int linenumber = 0;
            foreach (SOOrder order in orders.Select())
            {
                linenumber++;
                MSCustomerOrderViews.Current = MSCustomerOrderViews.Insert(new MSCustomerOrderDac() { LineNbr = linenumber });
                var newOrder = MSCustomerOrderViews.Current;
                newOrder.OrderNumber = order.OrderNbr;
                MSCustomerOrderViews.Update(newOrder);

            }
            if (MSCustomerViewForm.AskExt(true) != WebDialogResult.OK) {} // This is how we get the form to show up, we want it after the loop so it is filled once it pops up.
            return adapter.Get();
        }

        // This is our virtual DAC. When we create a Virtual DAC we HAVE to have a key column, in this case it is the line number. 
        [PXVirtual]
        [Serializable]
        [PXHidden]
        public class MSCustomerOrderDac : IBqlTable
        {
            #region OrderNumber 
            [PXString]
            [PXUIField(DisplayName = "Order Number")]
            public virtual String OrderNumber { get; set; }
            public abstract class orderNumber  : PX.Data.BQL.BqlString.Field<orderNumber> { }
            #endregion
            #region LineNumber
            [PXInt(IsKey = true)]
            public virtual Int32? LineNbr { get; set; }
            public abstract class lineNbr : PX.Data.BQL.BqlInt.Field<lineNbr> { }
            #endregion

        }
    }

The comments are just for future me. If anyone knows potential performance hits that could happen on mine please let me know!

Shoonly
  • 15
  • 1
  • 4
  • As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Mario Mateaș Feb 14 '23 at 14:31