How to invoke the base method of a dataview delegate - acumatica

I have a PXGraph with a data view delegate I want to override so I can add a a few more custom filters to the datas displayed on the grid.
I know how I can override and totaly replace the base delegate, but I dont know how I can execute the base logic of the dataview first, then add my filter to the result.
So the result I want to achieve is :
[PXOverride]
public virtual IEnumerable details()
{
var records = Base.details();
return records.Where(...);
}
I tried just copying the whole original dataview delegate but it calls a lot of privates members of the base graph so i'd have to copy all these members as well and it leads to a lot of ugly duplication in the code.
Edit : my problem is mainly that I can't call Base.details() because the details dataview delegate is declared as protected.

Have a look at T200. It has an example. In your case, in your data view delegate, you should define a new BQL statement which matches the query of your main view. Perhaps you should not include any where statements, since you will add them a little further.
PXSelectBase<YourDAC> query = new <PXSelectReadyOnly<YourDAC,Where<...>.select(yourParams)
Further in your delegate, you can add additional where clauses using WhereAnd...So in your case, you add a filter to the query object like
query.WhereAnd<Where<SomeDAC>
Finally make sure your delegate returns the iterator. So you can call
return query.Select()
That query will execute with your custom filters you added.
Hope this helps

You were on the right track. The only thing left is to convert the IEnumerable collection to a List. You need to do this because Linq Where method doesn't operate on IEnumerable collection.
Here is an example calling Customer Payment Methods screen base details delegate and filtering the results using Linq:
using PX.Data;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace PX.Objects.AR
{
public class CustomerPaymentMethodMaint_Extension : PXGraphExtension<CustomerPaymentMethodMaint>
{
public virtual IEnumerable details()
{
List<CustomerPaymentMethodDetail> records = Base.details().RowCast<CustomerPaymentMethodDetail>().ToList();
return records.Where(record => record.Value == "Bob");
}
}
}
Results without the additional delegate filter:
Results with the additional delegate filter:

Related

Override BQL used for PXProjection on SOShipmentPlan

I have a need to override the Select statement being used for the SOShipmentPlan PXProjection/DAC, namely, removing the
And <INPlanType.isFixed, Equal<boolFalse>
condition.
I can override all of the CreateShipment() logic and bring in any other necessary routines into an SOShipmentEntry_Extension class, to the point where I finally can use my own version of a SOShipmentPlan class, but that all seems needlessly complex when all I want to do is override the select for the PXProjection attribute. Overriding CreateShipment() and supporting routines also seems like a quick way to get in trouble come time for upgrades.
So, is there an easy way to override the PXProjection's BQL, or am I stuck overriding all kinds of code?
UPDATE 1
Based on a link provided below (stackoverflow.com/a/41540659/7376238), I feel like I'm close. Here's the block of code I end up with:
namespace PX.Objects.SO
{
public class SOShipmentEntry_Extension : PXGraphExtension<SOShipmentEntry>
{
#region Event Handlers
#endregion
[Serializable]
[PXProjection(typeof(Select2<SOOrder,
InnerJoin<SOOrderType, On<SOOrder.FK.OrderType>,
InnerJoin<INItemPlan, On<INItemPlan.refNoteID, Equal<SOOrder.noteID>>,
InnerJoin<INPlanType, On<INItemPlan.FK.PlanType>>>>,
Where<INItemPlan.hold, Equal<boolFalse>,
And<INItemPlan.planQty, Greater<decimal0>,
And<INPlanType.isDemand, Equal<boolTrue>,
And<INPlanType.isForDate, Equal<boolTrue>,
And<Where<INItemPlan.fixedSource, IsNull,
Or<INItemPlan.fixedSource, NotEqual<INReplenishmentSource.transfer>>>>>>>>>))]
[PXSubstitute()]
public partial class SOShipmentPlanCst : SOShipmentPlan
{
int x = 0;
}
}
But it doesn't seem to work. Not sure of where I'm supposed to put the code. I've tried putting the class definition inside and outside of public class SOShipmentEntry_Extension : PXGraphExtension<SOShipmentEntry> class (currently inside the extension class as shown). No luck either way.
THIS ANSWER DIDN'T WORK
Fair warning... I have not done this to a PXProjection before, so you'll have to see if this works. The nature of extensions tends to allow overriding views by simply redefining them. I have not done this myself with a projection, but I suspect it will be similar. Give it a try and see if you get the desired results. All I can say about testing it is that "it compiled" when I added to my project and removed the INItemPLanType.isFixed condition.
public class SOShipmentEntry_Extension : PXGraphExtension<SOShipmentEntry>
{
[PXProjection(typeof(Select2<SOOrder,
InnerJoin<SOOrderType, On<SOOrder.FK.OrderType>,
InnerJoin<INItemPlan, On<INItemPlan.refNoteID, Equal<SOOrder.noteID>>,
InnerJoin<INPlanType, On<INItemPlan.FK.PlanType>>>>,
Where<INItemPlan.hold, Equal<boolFalse>,
And<INItemPlan.planQty, Greater<decimal0>,
And<INPlanType.isDemand, Equal<boolTrue>,
And<INPlanType.isForDate, Equal<boolTrue>,
And<Where<INItemPlan.fixedSource, IsNull, Or<INItemPlan.fixedSource, NotEqual<INReplenishmentSource.transfer>>>>>>>>>))]
public partial class SOShipmentPlan : IBqlTable { }
}

How do I create a custom FilterRule with an override for ElementPasses

I want to create my own Boolean operation on an element to pass in as a FilterRule. The ElementPasses member description states:
Derived classes override this method to implement the test that determines whether the given element passes this rule or not.
I have tried to create my own derived class but I can't figure out how to implement it. I would think an interface would be available but I can't find anything. Annoyingly, I remember seeing an example of this but I can't seem to find anything.
This fails with: Static class 'ParameterDefinitionExists' cannot derive from type 'FilterRule'. Static classes must derive from object.
static public class ParameterDefinitionExists : FilterRule
{
public static bool ElementPasses(Element element)
{
return true;
}
}
And this fails with:'FilterRule' does not contain a constructor that takes 0 arguments
static public class ParameterDefinitionExists : FilterRule
{
new public bool ElementPasses(Element element)
{
return true;
}
}
What constructor arguments does it take?
There may be another way to go about it but I can't anything for FilterRules. I'm trying to define and refine a trigger in an updater but maybe I should query the element after it is passed in to the command. I imagine catching it with a filter rule is more efficient.
You have to use one of the Revit API classes derived from FilterRule:
Inheritance Hierarchy
System Object
Autodesk.Revit.DB FilterRule
Autodesk.Revit.DB FilterCategoryRule
Autodesk.Revit.DB FilterInverseRule
Autodesk.Revit.DB FilterValueRule
Autodesk.Revit.DB SharedParameterApplicableRule
Cf. http://www.revitapidocs.com/2017/a8f202ca-3c88-ecc4-fa93-549b26a412d7.htm
The Building Coder provides several examples creating and using parameter filters:
http://thebuildingcoder.typepad.com/blog/2010/08/elementparameterfilter-with-a-shared-parameter.html
Here is the entire topic group on filtering.

Where do I add this "ControllerHelpers" class?

In a pretty old .NET tutorial, "Nerd Dinner", it talks about using a Helper Class for Rule Violations. Everything seems straight forward except I'm not sure where to put this class so I can reference it. I am pretty new at MVC.
All of this below was taken from Nerd Dinner Tutorial:
Using a AddRuleViolations Helper Method
Our initial HTTP-POST Edit implementation used a foreach statement within its catch block to loop over the Dinner object's Rule Violations and add them to the controller's ModelState collection:
catch {
foreach (var issue in dinner.GetRuleViolations()) {
ModelState.AddModelError(issue.PropertyName, issue.ErrorMessage);
}
return View(dinner);
}
We can make this code a little cleaner by adding a "ControllerHelpers" class to the NerdDinner project, and implement an "AddRuleViolations" extension method within it that adds a helper method to the ASP.NET MVC ModelStateDictionary class. This extension method can encapsulate the logic necessary to populate the ModelStateDictionary with a list of RuleViolation errors:
public static class ControllerHelpers {
public static void AddRuleViolations(this ModelStateDictionary modelState, IEnumerable errors) {
foreach (RuleViolation issue in errors) {
modelState.AddModelError(issue.PropertyName, issue.ErrorMessage);
}
}
}

Create SharePoint (2010) ToolPart usable for more than one WebPart

I am using the basic instructions (here) for creating a property driven by a custom ToolPart.
All is good, except for the part where, in order to access the webpart property within the ApplyChanges method I must cast the "this.ParentToolPane.SelectedWebPart" back to a concrete "SimpleWebPart" class.
public override void ApplyChanges()
{
SimpleWebPart wp1 = (SimpleWebPart)this.ParentToolPane.SelectedWebPart;
// Send the custom text to the Web Part.
wp1.Text = Page.Request.Form[inputname];
}
Doing this means that I must pair each toolpart with a specific webpart. Is there a better way?
I cannot create an interface as there is no way of specifying a property in one.
I ineptly tried an passing an event/eventhandler during toolpart creation, but that did not update the webpart property when called.
I could create a base class for all the webparts that have a public "Text" property, but that is fugly.
I could also get desperate and crack open the this.ParentToolPane.SelectedWebPart reference with Reflection and call any properties named "Text" that way.
Either way, I am staring down the barrel of a fair bit of faffing around only to find out each option is a dead end.
Has anyone done this and can recommend the correct method for creating a reusable toolpart?
I have used an interface instead of a specific instance of a webpart.
private class IMyProperty
{
void SetMyProperty(string value);
}
public override void ApplyChanges()
{
IMyProperty wp1 = (IMyProperty)this.ParentToolPane.SelectedWebPart;
// Send the custom text to the Web Part.
wp1.SetMyProperty(Page.Request.Form[inputname]);
}
But this does not give a compile time warning that the toolpart requires the parent webpart to implement the IMyProperty interface.
The simple solution to that is to add a property of the IMyProperty interface in the toolpart constructor and call this reference instead of the this.ParentToolPane.SelectedWebPart property.
public ToolPart1(IContentUrl webPart)
{
// Set default properties
this.Init += new EventHandler(ToolPart1_Init);
parentWebPart = webPart;
}
public override void ApplyChanges()
{
// Send the custom text to the Web Part.
parentWebPart.SetMyProperty(Page.Request.Form[inputname]);
}
public override ToolPart[] GetToolParts()
{
// This is the custom ToolPart.
toolparts[2] = new ToolPart1(this);
return toolparts;
}
This works fine, but I cannot get over the feeling that there is something nasty in the underlying SharePoint code that may trip me up later.

Adding custom control with a collection property to a SharePoint Page Layout

I have created a custom control with a collection property per the example on How do you build an ASP.NET custom control with a collection property?
When the control is added to a common ASP.Net aspx page it works as expected. However, when added to a Page Layout in Sharepoint the following error is thrown:
Unable to cast object of type 'System.Web.UI.CollectionBuilder' to type 'System.Collections.Generic.List`1[mytypes.mytype]'.
The code is pretty much identical to the code provided by the example shown in the link above. I do not think the fault lies in the control as it works fine in a plain web project.
I dont think you can use generic lists in sharepoint. Use an ArrayList or customised List collection instead (use asp:ListItem as an exampe, it has its own collection type)
[ParseChildren(true, "Names")]
public class MyControl : Control {
private List<PersonName> names;
public MyControl() {
names = new List<PersonName>();
}
[PersistenceMode(PersistenceMode.InnerDefaultProperty)]
public List<PersonName> Names {
get { return this.names; }
}
}
public class PersonName {
public string Name { get; set; }
}
UPDATE
Ahh i see the problem now, it is not to do with the generic list, it is because of the way you are doing the initialization.
Create a private variable to hold the list private List<PersonName> names;
Ensure that the property does not have a setter

Resources