Solved

PXSubstitute on PXProjection throws error when filtering on Warehouse (SiteID) in the result grid

  • 9 October 2023
  • 9 replies
  • 153 views

Userlevel 6
Badge +4

I was asked to restrict results of the Add Item button of the SOOrderEntry screen to only non-stock items and stock items associated to the current branch, filtering out items of other branches that are not extended to the current branch.

My PXProjection inherits from SOSiteStatusSelected and uses PXSubstitute to utilize my select instead of the one in the base projection.  This seems to work well, except when I apply a filter to the Warehouse (Site ID) column in the dialog’s grid.  Other columns seem to work fine, but not Warehouse.  I get the following error:

 

This is because filtering the result grid injects the following into the underlying SQL statement which obviously won’t work because the projection’s class isn’t in the database. 

AND ((SELECT TOP (1) [INSiteExt].[siteCD]
FROM [INSite] AS [INSiteExt]
WHERE ([INSiteExt].[CompanyID] = 4)
AND ([INSiteExt].[BranchID] IS NULL
OR ([INSiteExt].[BranchID] IN (75, 79, 78, 80, 81, 82)))
AND [INSiteExt].[SiteID] = [SOSiteStatusSelectedExt].[SiteID]) <> @P9))

The custom projection is shown below.  The change is noted between the /***/ markers.  The only other change is, of course, extra closing brackets to support my additions within the syntax.

namespace SSCS.SO
{
[Serializable]
[PXSubstitute]
[PXCacheName("SSCS.SO.SOSiteStatusSelected")]

[PXProjection(typeof(Select2<InventoryItem,
LeftJoin<INSiteStatus,
On<INSiteStatus.inventoryID, Equal<InventoryItem.inventoryID>,
And<InventoryItem.stkItem, Equal<boolTrue>,
And<INSiteStatus.siteID, NotEqual<SiteAttribute.transitSiteID>>>>,
LeftJoin<INSubItem,
On<INSiteStatus.FK.SubItem>,
LeftJoin<INSite,
On2<INSiteStatus.FK.Site,
And<INSite.baseCuryID, EqualBaseCuryID<Current2<SOOrder.branchID>>>>,
LeftJoin<INItemXRef,
On<INItemXRef.inventoryID, Equal<InventoryItem.inventoryID>,
And2<Where<INItemXRef.subItemID, Equal<INSiteStatus.subItemID>,
Or<INSiteStatus.subItemID, IsNull>>,
And<Where<CurrentValue<SOSiteStatusFilter.barCode>, IsNotNull,
And<INItemXRef.alternateType, In3<INAlternateType.barcode, INAlternateType.gIN>>>>>>,
LeftJoin<INItemPartNumber,
On<INItemPartNumber.inventoryID, Equal<InventoryItem.inventoryID>,
And<INItemPartNumber.alternateID, Like<CurrentValue<SOSiteStatusFilter.inventory_Wildcard>>,
And2<Where<INItemPartNumber.bAccountID, Equal<Zero>,
Or<INItemPartNumber.bAccountID, Equal<CurrentValue<SOOrder.customerID>>,
Or<INItemPartNumber.alternateType, Equal<INAlternateType.vPN>>>>,
And<Where<INItemPartNumber.subItemID, Equal<INSiteStatus.subItemID>,
Or<INSiteStatus.subItemID, IsNull>>>>>>,
LeftJoin<INItemClass,
On<InventoryItem.FK.ItemClass>,
LeftJoin<INPriceClass,
On<INPriceClass.priceClassID, Equal<InventoryItem.priceClassID>>,
LeftJoin<InventoryItemCurySettings,
On<InventoryItemCurySettings.inventoryID, Equal<InventoryItem.inventoryID>,
And<InventoryItemCurySettings.curyID, EqualBaseCuryID<Current2<SOOrder.branchID>>>>,
LeftJoin<BAccountR,
On<BAccountR.bAccountID, Equal<InventoryItemCurySettings.preferredVendorID>>,
LeftJoin<INItemCustSalesStats,
On<CurrentValue<SOSiteStatusFilter.mode>, Equal<SOAddItemMode.byCustomer>,
And<INItemCustSalesStats.inventoryID, Equal<InventoryItem.inventoryID>,
And<INItemCustSalesStats.subItemID, Equal<INSiteStatus.subItemID>,
And<INItemCustSalesStats.siteID, Equal<INSiteStatus.siteID>,
And<INItemCustSalesStats.bAccountID, Equal<CurrentValue<SOOrder.customerID>>,
And<Where<INItemCustSalesStats.lastDate, GreaterEqual<CurrentValue<SOSiteStatusFilter.historyDate>>,
Or<CurrentValue<SOSiteStatusFilter.dropShipSales>, Equal<True>,
And<INItemCustSalesStats.dropShipLastDate, GreaterEqual<CurrentValue<SOSiteStatusFilter.historyDate>>>>>>>>>>>,
LeftJoin<INUnit,
On<INUnit.inventoryID, Equal<InventoryItem.inventoryID>,
And<INUnit.unitType, Equal<INUnitType.inventoryItem>,
And<INUnit.fromUnit, Equal<InventoryItem.salesUnit>,
And<INUnit.toUnit, Equal<InventoryItem.baseUnit>>>>>
>>>>>>>>>>>,
Where<CurrentValue<SOOrder.customerID>, IsNotNull,
And2<CurrentMatch<InventoryItem, AccessInfo.userName>,
And2<Where<INSiteStatus.siteID, IsNull, Or<INSite.branchID, IsNotNull, And2<CurrentMatch<INSite, AccessInfo.userName>,
And<Where2<FeatureInstalled<FeaturesSet.interBranch>,
Or2<SameOrganizationBranch<INSite.branchID, Current<SOOrder.branchID>>,
Or<CurrentValue<SOOrder.behavior>, Equal<SOBehavior.qT>>>>>>>>,
And2<Where<INSiteStatus.subItemID, IsNull, Or<CurrentMatch<INSubItem, AccessInfo.userName>>>,
And2<Where<CurrentValue<INSiteStatusFilter.onlyAvailable>, Equal<boolFalse>,
Or<INSiteStatus.qtyAvail, Greater<PX.Objects.CS.decimal0>>>,
And2<Where<CurrentValue<SOSiteStatusFilter.mode>, Equal<SOAddItemMode.bySite>,
Or<INItemCustSalesStats.lastQty, Greater<decimal0>,
Or<CurrentValue<SOSiteStatusFilter.dropShipSales>, Equal<True>, And<INItemCustSalesStats.dropShipLastQty, Greater<decimal0>>>>>,

/***/
And2<Where2<Where<INSite.branchID, IsNull, And<InventoryItem.stkItem, Equal<False>>>,
Or<Where<INSite.branchID, IsNotNull, And<INSite.branchID, Equal<CurrentValue<AccessInfo.branchID>>>>>>,

And2<Where<CurrentValue<SSINSetup.assetClassID>, IsNull, Or<InventoryItem.itemClassID, NotEqual<CurrentValue<SSINSetup.assetClassID>>>>,
/***/

And<InventoryItem.isTemplate, Equal<False>,
And<InventoryItem.itemStatus, NotIn3<
InventoryItemStatus.unknown,
InventoryItemStatus.inactive,
InventoryItemStatus.markedForDeletion,
InventoryItemStatus.noSales>>>>>>>>>>>>), Persistent = false)]


public sealed class SOSiteStatusSelectedExt : PX.Objects.SO.SOSiteStatusSelected
{
public static bool IsActive() =>
SSCSAccess.FeatureInstalled<SSCSFeaturesSet.main>();


}
}

Since this seems to come into play only when applying a filter to the grid column, is this an Acumatica bug, or have I missed something in the custom projection?

This is on Build 23.110.0025.

icon

Best answer by vardan22 14 October 2023, 17:44

View original

9 replies

Userlevel 6
Badge +4

Ended up opening a support case with Acumatica.  If I get resolution, I’ll post an update.  The smart panel grid is returning results until I apply a column filter that injects bad code into the SQL statement, so I am pretty certain this is an acumatica bug.

Userlevel 6
Badge +3

I had the very same problem in a few months ago. I ended up overwriting Sitestatus view delegate, and I filtered the results there.

Userlevel 6
Badge +4

I had the very same problem in a few months ago. I ended up overwriting Sitestatus view delegate, and I filtered the results there.

Thanks for the insight.  I commented out my other code and gave this a try.  It works functionally, but do you see performance degradation?

While this solution works for me, it also adds a very noticeable delay in returning results on my dev environment.  I hoped it might just be from usual delays after building the DLL, but I tried using the dialog box a bit with no improvement.  It might not be so obvious on our production server, but I don’t like introducing anything that I can actually observe impacting performance.  As far as I can see, this should hit all the expected indices, but it seems the extra layer of the view delegate is hurting me.  You see something here that I don’t?  I still think the original post is an Acumatica bug, but I’m not above using the view delegate as a workaround if it doesn’t slow us down.  I’d even say it’s a better solution, if performance was equal.  The replaced projection definition performs much better by comparison, except for the column filtering bug.

public IEnumerable sitestatus()
{
return PXSelectJoin<SOSiteStatusSelected,
InnerJoin<InventoryItem, On<InventoryItem.inventoryID, Equal<SOSiteStatusSelected.inventoryID>>,
LeftJoin<SSINSetup, On<True, Equal<True>>,
LeftJoin<INSite, On<INSite.siteID, Equal<SOSiteStatusSelected.siteID>>
>>>,
Where2<Where2<Where<INSite.branchID, IsNull, And<InventoryItem.stkItem, Equal<False>>>,
Or<Where<INSite.branchID, IsNotNull, And<INSite.branchID, Equal<Current<AccessInfo.branchID>>>>>>,
And<Where<SSINSetup.assetClassID, IsNull, Or<InventoryItem.itemClassID, NotEqual<SSINSetup.assetClassID>>>>>>
.Select(Base);
}

 

Userlevel 4
Badge +1

Based on my investigation, I believe there is a conflict between the PXSubstitute attribute and the SubstituteKey property for the PXSelector attribute.

The same error you will get if you try to filter the grid view with InventoryID or ItemClassID fields.

To confirm my assumptions about the SubstituteKey property, I created a cache extension for the SOSiteStatusSelected DAC and changed the InventoryCD field attributes with regular PXSelector. I also added a SubstituteKey property to the attribute and it started throwing the same error message.

But you can fix this in one of the following ways:

  1. On the grid view you can change the visibility for the SiteCD and use it for filtering the data
  2. You can assign value to the ParentType property for the PXSubstitute attribute like this [PXSubstitute(ParentType = typeof(SOSiteStatusSelected))].
Userlevel 6
Badge +3

I think Vardan showed us the solution for this. If it doesn’t work for some reason, then you can try to remove your joins from your view delegate override, use Current<> values from InventoryItem and INSIte tables, and add SSINSetup.assetClassID as a parameter.

Userlevel 6
Badge +4

Based on my investigation, I believe there is a conflict between the PXSubstitute attribute and the SubstituteKey property for the PXSelector attribute.

The same error you will get if you try to filter the grid view with InventoryID or ItemClassID fields.

To confirm my assumptions about the SubstituteKey property, I created a cache extension for the SOSiteStatusSelected DAC and changed the InventoryCD field attributes with regular PXSelector. I also added a SubstituteKey property to the attribute and it started throwing the same error message.

But you can fix this in one of the following ways:

  1. On the grid view you can change the visibility for the SiteCD and use it for filtering the data
  2. You can assign value to the ParentType property for the PXSubstitute attribute like this [PXSubstitute(ParentType = typeof(SOSiteStatusSelected))].

Sounds interesting that there would be a conflict between PXSubstitute and SubstituteKey.  I don’t understand why it would be the case, but it isn’t the first time I’ve had conflicts of 2 things stepping on each other in Acumatica.  This seems like something that Acumatica should fix in the deep inner workings, so I still stand firmly that it must be a bug.  If I have to take option 1 in the end to make this work, I guess I will, but it means loss of functionality which is not desirable.

Option 2 had an unexpected result for me.  when adding ParentType as you indicated, my custom select to override the projection was ignored, meaning I ended up getting all warehouses again. I was very excited that this might be the solution until I saw the result.  ParentType is new to me, so thank you for giving me something new to explore.  It might be the right solution in the end, but I’m just failing to execute properly… so back to the learning curve a little.

I have 3 options presented between you and Zoltan, so I need to do some experimenting.  I’m going to try Zoltan’s suggestion on tweaking the view delegate option.  That gave me the result that I needed, and I hope I can find a way to implement his recommended change to the view delegate to return the performance level.  It also results in the least code, and simplicity is an advantage long term.  If I keep having performance issues, I’ll try these 2 solutions again.  Thank you both for your help.  Will let you know if I make progress or if Acumatica support responds on the case I opened.

Userlevel 4
Badge +1

Based on my investigation, I believe there is a conflict between the PXSubstitute attribute and the SubstituteKey property for the PXSelector attribute.

The same error you will get if you try to filter the grid view with InventoryID or ItemClassID fields.

To confirm my assumptions about the SubstituteKey property, I created a cache extension for the SOSiteStatusSelected DAC and changed the InventoryCD field attributes with regular PXSelector. I also added a SubstituteKey property to the attribute and it started throwing the same error message.

But you can fix this in one of the following ways:

  1. On the grid view you can change the visibility for the SiteCD and use it for filtering the data
  2. You can assign value to the ParentType property for the PXSubstitute attribute like this [PXSubstitute(ParentType = typeof(SOSiteStatusSelected))].

Sounds interesting that there would be a conflict between PXSubstitute and SubstituteKey.  I don’t understand why it would be the case, but it isn’t the first time I’ve had conflicts of 2 things stepping on each other in Acumatica.  This seems like something that Acumatica should fix in the deep inner workings, so I still stand firmly that it must be a bug.  If I have to take option 1 in the end to make this work, I guess I will, but it means loss of functionality which is not desirable.

Option 2 had an unexpected result for me.  when adding ParentType as you indicated, my custom select to override the projection was ignored, meaning I ended up getting all warehouses again. I was very excited that this might be the solution until I saw the result.  ParentType is new to me, so thank you for giving me something new to explore.  It might be the right solution in the end, but I’m just failing to execute properly… so back to the learning curve a little.

I have 3 options presented between you and Zoltan, so I need to do some experimenting.  I’m going to try Zoltan’s suggestion on tweaking the view delegate option.  That gave me the result that I needed, and I hope I can find a way to implement his recommended change to the view delegate to return the performance level.  It also results in the least code, and simplicity is an advantage long term.  If I keep having performance issues, I’ll try these 2 solutions again.  Thank you both for your help.  Will let you know if I make progress or if Acumatica support responds on the case I opened.

 

I actually only paid attention to filtering data by the SiteID field, but that’s still not the point.

I would like to say that there is another option besides these 3, but in my opinion it is a case of simply writing code to force the requirements work.
You can rename your class from SOSiteStatusSelectedExt to SOSiteStatusSelected and it will work as you need as I see.

I also can't figure out why such strange things are happening and I believe there is a bug on Acumatica's side that needs to be fixed.

Userlevel 6
Badge +4

Tried renaming as you suggested, but it still ignored my select and showed all warehouses.

I tried your other suggestion to hide SiteID and use SiteCD instead.  It won’t be a selector, but you can still filter making it a usable option.  That strategy eliminates access to the conflict of PXSubstitute and PXSelector by removing access to the selector.

To add a field label, I applied the following into my custom projection.  Providing in case someone else needs it.

#region SiteCD
[PXString(IsUnicode = true, IsKey = true)]
[PXUIField(DisplayName = IN.Messages.FldSiteID)]
[PXDBCalced(typeof(IsNull<RTrim<INSite.siteCD>, Empty>), typeof(string))]
public new String SiteCD { get; set; }
#endregion

Thanks for the help!  Still hoping Acumatica can resolve the underlying issue as a bug on their side, but this works for now!

Userlevel 6
Badge +4

Option 1 by vardan22 ended up being the solution that works best for me.  I could not get option 2 to work as desired, but marking as best solution because option 1was the best solution.

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