Developer Tips & Tricks (Part IV): Programing Tips – How to Minimize Duplication in your Code

Mark Franks | August 28, 2017

One of the most highly rated sessions delivered in our Virtual DevCon was Sergey Marenich’s Advanced Framework Tips & Tricks. He did a masterful job flushing out a number very useful activities that a developer on our platform can do to be more efficient and productive as well as some good coding practices.  Since different individuals learn and benefit from a variety of modalities, we thought it would be good to share Sergey’s wisdom in written form as well.  I’ll distribute the content in bite size chunks over the next several blogs posts.

Last week, we discussed best practices around Using the PXFormula Attribute to Simplify your Code. Today, I will spend time exploring Sergey’s Programming Best Practices Tips: How to Minimize Duplication in your Code – Tip # 7.

As a review, below are the tips we have been discussing in this series. Note that I’ve skipped Finding More Information Through Communities (Tip #3) and Debugging Acumatica Code since I have covered these topics, respectively in the past: here and here.

Tip 1 – Configuring Your Acumatica Instance

Tip 2 – Configuring Your Tools

Tip 3 – Finding More Information Through Communities

Tip 4 – Disabling Web Site Compilation

Tip 5 – Debugging Acumatica Code

Tip 6 – Auto-Calc with Formulas

Tip 7 – Minimize Duplication

Tip 8 – Benefits of Using Events

Tip 9 – Reuse of BQL Queries

Tip 10 – Modifying Selectors

Tip 11 – Fields from Different Views

More often than not, duplication of code is unavoidable.  But we can reduce or minimize duplication, the more familiar with the framework and systems with which we are working. Here are some things you can do today with relative ease.

When you do any customizations, you can copy and use the original code and make our modifications. But when you do this, you risk potential issues down the road when Acumatica is upgraded. You can reduce this risk by taking advantage of the following four (4) attributes in your code.

PXRestrictorAttribute(Typeof<Where>)

This attribute is used in place of copying a selector. The PXRestrictorAttribute is used on DAC fields represented by lookup controls in the user interface. For example, such fields can have the PXSelector attribute attached to them. The attribute adds the Where<> clause to the BQL command that selects data for the control. As a result, the control lists the data records that satisfy the BQL command and the new restriction. If the user enters a value that is not in the list, the error message configured by the attribute is displayed. A typical example of attribute’s usage is specifying a condition that checks whether a referenced data record is active. This condition could be specified in the PXSelector attribute. But in this case, if an active data record once selected through the lookup control becomes inactive, saving the data record that includes this lookup field will result in an error. Adding the condition through PXRestrictor attribute prevents this error. The lookup field can still hold a reference to the inactive data record. However, the new value can be selected only among the active data records.

The example code below shows the use of the attribute on a lookup field.

[PXDBString(10, IsUnicode = true)]
[PXUIField(DisplayName = “Tax Category”)]
[PXSelector(typeof(TaxCategory.taxCategoryID),
   DescriptionField = typeof(TaxCategory.descr))]
[PXRestrictor(typeof(Where<TaxCategory.active, Equal<True>>),
   “Tax Category ‘{0}’ is inactive”,
typeof(TaxCategory.taxCategoryID))]
public virtual string TaxCategoryID { get; set; }

Note that the error message includes {0}, which will be replaced with the value of the TaxCategoryID field when the error message is displayed.

Refer to the Acumatica Framework Developer’s Guide for more information.

PXMergeAttributes(MergeMethod.Append)

Instead of redefining all of your attributes, you can use the PXMergeAttributes attribute how to apply custom attributes to the existing ones. There are three options: Append, Replace, and Merge. These options are declared by the MergeMethod enumerator as follows:

public enum MergeMethod { Append, Replace, Merge }

The Append option is used to add  custom attributes to the existing ones. The Replace option forces the system to use custom attributes instead of the existing ones. This option is used by default if you do not specify the PXMergeAttributes attribute on your customized field. The Merge option makes the system apply the union of the custom and existing attributes to the customized field.

To define a merge method for a field, insert the PXMergeAttributes attribute of the field in the DAC extension, and define the option as the value of the Method parameter, as shown in the examples below.

For example, suppose that for the customized FieldName field, you have to add the PXDefault attribute and change the value in the PXUIFieldAttribute. Suppose the original code of the FieldName field is the following.

#region FieldName

[PXDBDecimal(2)]
[PXUIField(DisplayName = “Display Name”)]
public virtual decimal? FieldName
{…}
#endregion

In the DAC extension code for the customized field, specify the Merge option, as shown below.

public class DACName_Extension: PXCacheExtension<DACName>
{
#region FieldName
[PXMergeAttributes(Method = MergeMethod.Merge)]
[PXDefault(TypeCode.Decimal, “0.0”)]
[PXUIField(DisplayName = “Name”)]
public string FieldName{get;set;}

#endregion
}

The system will merge attributes for the FieldName field. In the result of the merge, the FieldNamefield will have the following collection of attributes.


[PXDBDecimal(2)]
[PXDefault(TypeCode.Decimal, “0.0”)]
[PXUIField(DisplayName = “Name”)]
public virtual decimal? FieldName
{…}

For more detail, see the Acumatica Customization Guide.

PXCustomizeBaseAttribute(Typeof(BaseAttr), Property, Value)

When customizing Acumatica ERP Platform, you can specify how the system should apply the original and custom attributes to the fields. Thus you can make the your customizations more flexible and use the collections of the original attributes that could be updated between Acumatica ERP versions. To specify the way the system should apply the field attributes in a DAC extension.

Instead of copying an attribute, use the PXCustomizeBaseAttribute attribute. This particular attribute is added to the field for each modified parameter.

For example, to set the Enabled parameter of the PXUIField attribute of a field to false, add the following attribute to the field region of the DAC extension.

[PXCustomizeBaseAttribute(typeof(PXUIFieldAttribute), “Enabled”, false)]

The following example shows you how to change the Required parameter of the PXUIField attribute for the MyField field of the MyDAC data access class in the MyGraph graph extension by using the CacheAttached() event handler.

public class MyGraph_Extension:PXGraphExtension<MyGraph>
{

[PXCustomizeBaseAttribute(typeof(PXUIFieldAttribute), “Required”, true)]
protected void MyDAC_MyField_CacheAttached(PXCache cache)
{
}

}

PXUIEnabledAttribute(Typeof<Where>)

Instead of an additional RowSelected, use the field attribute PXUIEnabledAttribute. It’s a declarative analog of the PXUIFieldAttribute.SetEnabled function, which manages the field at run time. It subscribes to the RowSelected event handler at the attribute level.

Here’s an example of it’s use:

[PXDBString(2, IsFixed = true)]
[PXUIField(DisplayName = “Averaging Convention”)]
[FAAveragingConvention.List]
// …
[PXUIEnabled(typeof(FABookSettings.depreciate))] // IBqlField used
public virtual string AveragingConvention { get; set; }
[PXDBBool]
[PXUIField(DisplayName = “Depreciate”, Visibility = PXUIVisibility.SelectorVisible)]
// …
[PXUIEnabled(typeof(Where<FixedAsset.recordType, NotEqual<FARecordType.assetType>, // IBqlWhere used
   Or<FixedAsset.isAcquired, NotEqual<True>,
   Or<EntryStatus, Equal<EntryStatus.inserted>>>>))]
public virtual bool? Depreciable { get; set; }

Note that the AveragingConvention field is enabled if depreciate is true for the same record. In this case, the field is
enabled if the record does not represent a particular asset ( RecordType != AssetType ), the asset is not acquired (IsAcquired != true), or the record was just inserted (EntryStatus = Inserted).

In summary, not only will you minimize duplication of code using these attributes when doing customization work with the Acumatica Platform, you protect your code from upgrades that might otherwise require modifications – minimizing the need for upgrading your own customizations for your customers.  This along with the other tips in this series allows you to focus on getting new projects to market rather than spending time on maintenance and support – the bane of developers.

To reinforce what was discussed here and to view the wonderful presentation of these Tips that Sergey Marenich shared with us at our Virtual DevCon, go here and enjoy.

 

 

Mark Franks

As a Platform Evangelist, Mark is responsible for showing people the specifics about what makes Acumatica’s Cloud Development Plaform wonderfully attractive to ISV & Partners. He's also passionate about Running, Latin, and his family. | E-mail: mfranks@acumatica.com | Skype: mfranks |

Subscribe to our bi-weekly newsletter

Subscribe