Solved

graph.View.Search is only finding the default SOOrder type


Badge +11

I have a very strange issue that feels like a bug. I am iterating through a list of SOOrders and then using the following line to set the current order as the Document.Current:

foreach (SOOrder order in orders)
{
orderGraph.Document.Current = orderGraph.Document.Search<SOOrder.orderType, SOOrder.orderNbr>(order.OrderType, order.OrderNbr);
// ...
}

However, when performing this search for any order type that is not the Default Sales Order Type on SO Preferences, the value returned by the search is null.

This gets deeper. I tested the following code with a simple action on SOOrderEntry:

public PXAction<SOOrder> ActionName;
[PXButton(CommitChanges = true)]
[PXUIField(DisplayName = "ActionName")]
public virtual void actionName()
{
SOOrder order = Base.Document.Search<SOOrder.orderType, SOOrder.orderNbr>(Base.Document.Current.OrderType, Base.Document.Current.OrderNbr);
throw new PXException($"{order.OrderType} {order.OrderNbr}");
}

Notice I’m performing the search in the Base graph. This works successfully for any order type.

 

I tested the following code (slightly altered to use a new graph instance):

public PXAction<SOOrder> ActionName;
[PXButton(CommitChanges = true)]
[PXUIField(DisplayName = "ActionName")]
public virtual void actionName()
{
var orderGraph = PXGraph.CreateInstance<SOOrderEntry>();
SOOrder order = orderGraph.Document.Search<SOOrder.orderType, SOOrder.orderNbr>(Base.Document.Current.OrderType, Base.Document.Current.OrderNbr);
throw new PXException($"{order.OrderType} {order.OrderNbr}");
}

For any order type that is not the Default Sales Order Type on SO Preferences, the exception is an unhandled object reference error (meaning the order is null), while an order of the default type successfully displays the type and order number.

 

If this is NOT a bug, could someone explain to me why this is happening and what to do instead?

icon

Best answer by Dioris Aguilar 3 August 2023, 23:23

View original

13 replies

Userlevel 7
Badge +5

I’ve had issues where Search<> did not find a record that I know existed - I was working with Sales Orders as well.

If I know the primary key, I always us the PK or UK methods as they have be far more reliable.

I agree - your code should work. Given that you’ve found that the Default Order Type has some impact, I wonder if there is a view that’s not being initialized properly when you make your own graph instance. Or if you created a SOOrder variable and populated the OrderType field before turning around and calling search.

Anyway, I’m making guesses. Hopefully someone has some better insight.

Userlevel 5
Badge +2

@darylbowman 

A Search<field0,..>(field0,.., arguments) call basically does a windowed View.Select(null, arguments, searches...), i.e. it will only look for 1 record.
The columns provided inside the <> are the sort columns(*) of the result and also the "searches" that are provided to the View.Select call, however, any optional or required field declared in the view must be provided as part of arguments, so, to make your case work, you should have to provide the orderType again as argument:

orderGraph.Document.Current = orderGraph.Document.Search<SOOrder.orderType, SOOrder.orderNbr>(order.OrderType, order.OrderNbr, order.OrderType);

You can see an example in PrepareInvoice method in SOOrderEntry:
ie.Document.Current = ie.Document.Search<ARInvoice.docType, ARInvoice.refNbr>(((ARInvoice)created[0]).DocType, ((ARInvoice)created[0]).RefNbr, ((ARInvoice)created[0]).DocType);

(*) => The sort columns are not useful for the Search<> call since it only returns 1 record, however, the Search<> method calls another method (SearchWindowed<>) that requires those sort columns.

Userlevel 7
Badge +8

I had the same issue a few weeks ago on CostProjection which Primary View has a view delegate. I found PK.Find is more reliable than Search. I suggest you do the same.

Userlevel 7
Badge +8

@darylbowman it seems my previous comment is blocked. i suggest going with PK.Find something like 

 

SOOrder order = SOOrder.PK.Find(Base, Base.Order.Current.OrderType, Base.Order.Current.OrderNbr);

 

sorry for possible typos. Writing on my cell.

Badge +11

@darylbowman

… however, any optional or required field declared in the view must be provided as part of arguments, so, to make your case work, you should have to provide the orderType again as argument

I’m not understanding this. What is the reason for providing order type again?

Badge +11

For what it’s worth, I tried implementing this in my project, and using the above method decreased performance by a factor of 2 (it took twice as long to complete).

time without search - 2:04

time with new search - 5:36

Userlevel 7
Badge +5

@aaghaei - there’s a new spam filter/control process in place on the forums and so some posts are being delayed while someone reviews them. My response, above, took a few hours to be approved. 

Badge +11

Same. I submitted one a while ago that hasn’t been approved yet. This is going to get really annoying.

Userlevel 7
Badge +5

I found my original question and it was exactly your question.  Yuriy indicated that Search is mimicking how a UI searches.

Unfortunately, I still don’t fully understand the differences which is worrisome when writing code to locate records that can be broken by a configuration change. Hopefully @Dioris Aguilar can shed some more light.

Userlevel 5
Badge +2

The Search<> method looks for records that exist in a view, so, it depends on the view declaration. For example:

public PXSelect<SOOrder> SOOrderView;

Considering the view does not have any required/optional parameter, basically it has all existing records for SOOrder and no arguments should be passed as the arguments variable in the Search method:

public virtual PXResultset<Table> Search<Field0>(object field0, params object[] arguments)

Since SOOrder has 2 key field, we will be using the definition of Search method with 2 fields inside the <> brackets:

public virtual PXResultset<Table> Search<Field0, Field1>(object field0, object field1, params object[] arguments)

Using this definition, we can use the Search method passing the key fields:

var record = SOOrderView.Search<SOOrder.orderType, SOOrder.orderNbr>(OrderType, OrderNbr);

And we will get a unique record having those key values in the view’s dataset, this case (View with no required/optional parameter) seems to be the equivalent to .Find() since it looks for a record by its key values in the entire dataset for SOOrder.
 

Now, if the view has some parameters, then we need to provide those parameters as arguments in the Search<> call:

public PXSelect<SOOrder, Where<SOOrder.orderType, Equal<Optional<SOOrder.orderType>>, And<SOOrder.status, Equal<Open>>> OpenDocument;

In this case, due to the view definition, a limited amount of records for SOOrder table exist in the view. 

For the Search<> call, we need to provide the optional parameter (orderType):

var record = OpenDocument.Search<SOOrder.orderNbr>(OrderNbr, (arguments->) OrderType);

In this case, it does not make sense to provide the orderType field inside the <> brackets because the view is already filtered by that due to its definition and this value is already passed as argument.

Keep in mind, all values passed inside the <> brackets are used as filters in the final result.

Hope this helps.

Badge +11

@Dioris Aguilar - This is very helpful and informative, but I'm not sure it answers my original question, which is, why does using Base graph work with OrderType and OrderNbr, but using a new graph instance does not? In my reasoning, since the same View shouldn't require different things one way or the other, right?

 

 

Userlevel 5
Badge +2

@darylbowman According to the documentation of the Optional<> parameter:

 

The default value of the field (SOOrder.orderType) will be inserted if null value is passed. 

And this is important to keep in mind because the view used has this parameter:

public PXSelectJoin<SOOrder, LeftJoinSingleTable<Customer, On<Customer.bAccountID, Equal<SOOrder.customerID>>>, Where<SOOrder.orderType, Equal<Optional<SOOrder.orderType>>, And<Where<Customer.bAccountID, IsNull, Or<Match<Customer, Current<AccessInfo.userName>>>>>>> Document;

 

In this case, since you are using the Search call this way:

public PXAction<SOOrder> ActionName;
[PXButton(CommitChanges = true)]
[PXUIField(DisplayName = "ActionName")]
public virtual void actionName()
{
var orderGraph = PXGraph.CreateInstance<SOOrderEntry>();
SOOrder order = orderGraph.Document.Search<SOOrder.orderType, SOOrder.orderNbr>(Base.Document.Current.OrderType, Base.Document.Current.OrderNbr);
throw new PXException($"{order.OrderType} {order.OrderNbr}");
}

No argument is passed in the Search call for the Optional<> parameter of the Document view and since no document is loaded yet, the Current<> parameter is also null and that’s why it fails for orderType <> default order type.

For orderType == default order type works because the Optional<> parameter takes that value (default order type) since no argument is passed in the Search call.

 

For the following example:

public PXAction<SOOrder> ActionName;
[PXButton(CommitChanges = true)]
[PXUIField(DisplayName = "ActionName")]
public virtual void actionName()
{
SOOrder order = Base.Document.Search<SOOrder.orderType, SOOrder.orderNbr>(Base.Document.Current.OrderType, Base.Document.Current.OrderNbr);
throw new PXException($"{order.OrderType} {order.OrderNbr}");
}

the Search always works because there is already a document loaded and the Current<> parameter has a value distinct than null, therefore, no matter no argument is passed for the Optional<> parameter of the Document view in the Search call.

 

Hope this helps.

Badge +11

Thank you for the detailed explanation. That makes perfect sense 👍🏻

Reply


About Acumatica ERP system
Acumatica Cloud ERP provides the best business management solution for transforming your company to thrive in the new digital economy. Built on a future-proof platform with open architecture for rapid integrations, scalability, and ease of use, Acumatica delivers unparalleled value to small and midmarket organizations. Connected Business. Delivered.
© 2008 — 2024  Acumatica, Inc. All rights reserved