Hi There!
As you may know, Acumatica has a pretty nice and easy way to create and validate if a certain feature is enabled. You can read about it here.
However, that way works well only if you have to hide fields, columns, grids, forms. What if you want to hide some records from the grid based on the feature? Normally this is not a trivial task and requires a lot of code in the data view delegates and so on. And even you do all of these things, you won’t be able to use it in the Generic Injuries.
I would like to suggest an alternative approach where we create a virtual field that will evaluate feature availability dynamically. Based on this field value, we can determine if a feature is enabled or disabled and filter data. Even thought this still requires some code to handle, this approach will work in Generic Inquiries, Reports, Dashboards, Business Events and so on.
Another important note – to make this validation fast, we better to create an SQL query, as any non-sql fields will require in-memory filtering, which will be significantly slower
Okay, so the first thing we need to do is to create an attribute that generates dynamic SQL. Since we just want to see if a feature is enabled or not, we can use Switch/Case.
public class FeatureEnabledBoolAttribute : PXDBBoolAttribute { protected Type RecordType; public BCFeatureEnabledBoolAttribute(Type recordType) { RecordType = recordType; } public override void CommandPreparing(PXCache sender, PXCommandPreparingEventArgs e) { //On CommandPreparing the querry will be generated, we need only select, since this is virtual field if ((e.Operation & PXDBOperation.Command) == PXDBOperation.Select && (e.Operation & PXDBOperation.GroupBy) == 0) { Type bqlTable = sender.GetItemType(); //Here you can check in any way if you feature is enabled or disabled List<String> listOfDisabledRecordTypes = GetListOfDisabledTypesByFeatures(); //Here we can build a dynamic Swtch/Case that will validate the feature SQLSwitch switchExpr = new SQLSwitch(); if (listOfDisabledFieatures.Count > 0) { foreach (String type in listOfDisabledRecordTypes) { switchExpr = switchExpr.Case( new Column(RecordType.Name, bqlTable.Name).EQ(new SQLConst(type)), new SQLConst(false)); } } else switchExpr = switchExpr.Case(new SQLConst(1).EQ(1), new SQLConst(true)); switchExpr = switchExpr.Default(new SQLConst(true)); //Finally assemblying query. Query q = new Query().Field(switchExpr); e.Expr = new SubQuery(q); e.Cancel = true; e.BqlTable = bqlTable; } } }
Now, when the attribute is ready, we can use it on the needed DAC.
public class MyDAC : IBqlTable { //......... #region IsFeatureEnabled public abstract class isFeatureEnabled : PX.Data.BQL.BqlString.Field<isFeatureEnabled> { } //Lets use our new attribute on a feield [BCFeatureEnabledBool(typeof(MyDAC.recordType)))] public virtual bool? IsFeatureEnabled { get; set; } #endregion //......... }
And finally, we can use this field as any other normal field in the BQL conditions.
public class MyGraph : IBqlTable { //......... //And now we can use our new field anywhere, even in GI. public PXSelect<MyDAC, Where<MyDAC.isFeatureEnabled, Equal<True>>> MyDataView; //......... }
Since from the UI perspective, it is a normal field, you can use it in where conditions for Reports and Generic Inquiries too. They will automatically call you attribute, when SQL generation is required.
Please note that this approach is not perfect to protect features from unauthorized usage. On the other hand, when you want to hide something to make UI or Report more user-friendly and less crowded, this can work well!
Hope it helps, enjoy it!