As an Acumatica developer, you probably know Acuminator is an extension for Visual Studio IDE. Its purpose is to simplify development with the Acumatica Framework and boost developer’s productivity. Acuminator achieves this goal by:
- Static analysis of your source code, verifying that it satisfies the guidelines and conventions of the Acumatica Framework;
- Syntax highlighting and BQL formatting, improving your code’s readability;
- Code Map, providing a quick overview of the code structure for DACs and Graphs, allowing you to navigate between code segments.
In this post, we want to highlight our two latest releases of Acuminator and briefly describe the new functionality and features.
Acuminator version 2.3.0 was released in the summer and some of its functionality was shown at our Virtual DevCon this past June. Soon after we released it, we delivered a minor release, Acuminator 2.3.1, containing some important corrections to the code analysis rules. All the new changes in Acuminator can be grouped into the following three categories:
- New static code analysis rules;
- Code Map enhancements; and
- Bug fixes;
We will cover the first two items in this list and not go into details of the bug fixes. You can read the Acuminator release notes if you want all technical details.
Static Code Analysis
The static code analysis is a very useful feature for any developer. The Acumatica Framework has a lot of rules and conventions, however the flexibility inherent therein also makes it easy to violate some of these. Sometimes it’s not so obvious what is the root cause of any particular problem you may run into and you may need several hours to debug the issue just to discover that you violated some convention.
Moreover, you may need some time to create a request for technical support and wait several days for an investigation just to be told that you violated some rule you didn’t even know existed.
Now imagine that instead of hours of exhausting debugging, you are provided a warning right at the moment you typed incorrect code in the code editor itself. This would certainly make your life as a developer easier and save a lot of time. Of course, the analysis offered by only applies to the framework rules supported by Acuminator. Therefore, we constantly add new diagnostics to the tool.
In the last two releases, all the changes we made in static code analysis can be divided by their purpose in two specific areas. First, check that all DAC and Graph extensions allow you to turn them off. To achieve this you need to implement the IsActive method. Second, support for the Acumatica DAC Referential Integrity API with a set of new diagnostics. Now Acuminator will check the declarations of primary, unique and foreign keys in your DACs.
Let’s now dig into the details!
Check for the ability to turn off DAC and Graph extensions
Acumatica Framework allows you to specify logic for the DAC and Graph extensions which will determine if the extension is active. This is done with a declaration of a public static bool-returning parameterless method called IsActive. This method should contain simple logic to determine if the extension is enabled. There are some restrictions on the code in IsActive method, for instance, you cannot create graphs inside this method.
Examine the following use-case. It is very common for the extensions to execute some logic only if some feature is enabled. Consider the Graph extension ARInvoiceEntryAdvanceTaxesExt for ARInvoiceEntry Graph which will calculate some advanced taxes for ARInvoice if the feature Advanced Taxes is turned on. Here is how such extension might look like:
As you can see, the IsActive method just checks if the Graph extension is enabled.
If the IsActive method returns false for an extension, this extension is not loaded. It means the following:
- Form controls bound to views defined in the disabled Graph extension are automatically hidden. Fields defined in the DAC extension that should be visible on forms will be automatically hidden.
- Event handlers implemented in the Graph extension are ignored. Overridden event handlers and methods with the PXOverride attribute are also ignored.
- Views defined in the Graph extension will not be available.
- If a field is used in a generic inquiry as a column in the results grid or in a report as a text box, the field is also hidden. If the field is used in a configuration of a generic inquiry or a report, an error is thrown that the field is not found.
- Database fields defined in the DAC extension will not be present in SQL queries generated by the system. So, if you have a BQL query that explicitly adds the OrderBy, GroupBy, Where, and other statements to such a field, an error is thrown that the field is not found.
- If in a Graph or a Graph extension an event handler is declared for a field of a disabled DAC extension, the event handler is not executed.
If you didn’t declare an IsActive method and instead manually checked the feature switch in the code of your extension, it will be always active and loaded. You can see from the list above that keeping extension active when it is not needed reduces the performance of the system. A lot of resources are not disabled for the extension and extension’s event handlers are executed without a necessity.
In fact, the Acumatica Framework guidelines now highly recommend to declare the IsActive method in extensions wherever possible. To check this recommendation, we added a new PX1016 diagnostic to Acuminator. It will check your DAC and Graph extensions and display an error if there is no IsActive method declaration. If your extension should be always active you can just suppress the diagnostic with an Acuminator suppression comment like this:
DAC Referential Integrity API
The DAC Referential Integrity API (or PK/FK API for short) allows you to specify a primary key and a set of foreign and unique keys in the DAC. This can make your code less error-prone by providing static info about the DAC keys to the compiler and using its checks later.
The declaration of a primary key is rather simple. Here is the primary key for InventoryItem DAC:
And here is the example of a foreign key declaration for the SOLine DAC which uses a primary key of SOOrder DAC:
The declaration of a unique key is the same as the declaration of a primary key. The primary key differs from unique keys only by its name. From the API’s point of view, they are identical.
The PK/FK API can make your code much shorter. The only way to find a record by its keys before this API was to write a simple BQL select which is a bit verbose especially if the DAC has a composite key. Primary, foreign, and unique keys give you a simple and concise way to find a DAC record by its keys with just one line of code:
This is much shorter than the previous approach with a BQL select:
There are more examples and information about primary and foreign keys on our help portal:
- To Define a Primary Key
- To Define a Foreign Key
- Relationship Between Data with PrimaryKeyOf and ForeignKeyOf
However, to efficiently use this API some preparatory work has to be done in the form of declaring keys to the DACs. Acumatica developers already integrated this API into the majority of product modules in Acumatica 2020R2 and we continue to integrate this further in other modules over time. It is important to point out that you will also need to declare primary, foreign, and unique keys in your DACs to use all the capabilities of the PK/FK API. Of course, there are some rules and code style conventions which you need to conform to as outlined below.
- You cannot use unbound DAC fields in a key declaration. This applies to primary, unique, and foreign keys.
- Usually the primary key is the first member of a DAC followed by unique keys, foreign keys, and DAC fields & properties. But this order is not strict and is not enforced.
- For primary keys:
- A DAC can contain no more than one primary key. If you want to declare a key for another combination of DAC fields,allowing you to identify a separate DAC entity, you can create a unique key.
- A DAC’s primary key should be named PK.
- For foreign keys:
- All foreign keys can have arbitrary names but they must be declared in a nested public static class named FK.
- Multiple foreign keys declared in a DAC should not use the same set of fields.
- For unique keys:
- A single unique key in a DAC declaration must have the name UK.
- Multiple unique keys can have arbitrary names but they must be declared inside a nested public static class named UK.
- A DAC declaration should include at least one primary key declaration if you want to declare a unique key.
- Multiple unique keys declared in a DAC should not use the same set of fields.
This is the place where Acuminator comes to the rescue and provides even greater value to Acumatica developers. In the last two releases, we added a suite of diagnostics to support the usage of the PK/FK API. They will help you to easily declare primary and foregn keys and check that you didn’t violate any code conventions.
Help with the declaration of primary and foreign keys
The first two diagnostics PX1033 and PX1034 check your DAC for the presence of primary (PX1033) and foreign (PX1034) keys. The severity of these diagnostics is “Informational”, because they just suggest you to generate a primary or foreign key. There will be no error or warning and actually it is rather hard to see them. You need to look for three gray dots under the DAC name, which you can see in the red box in the screenshot below.
Or you can just see them in the “Error List” tool window:
If there is no primary key the PX1033 diagnostic will suggest to generate a new primary key with a Find method for you. It will use all DAC properties marked with IsKey = true for the primary key declaration.
Unfortunately, the generation of a foreign key is a much more complex task. However, the PX1034 diagnostic’s code fix will try its best to give you some useful information. It will generate a nested public static FK class with lots of information inside the comment. In the generated comment, there will be examples of foreign key declarations and a list of fields of a DAC which may hold a foreign reference to another DAC. When Acuminator fills this list, it looks for the following attributes which indicate the presence of a relationship between two DACs:
However, these fields are just potential candidates for a foreign key. You don’t have to declare a foreign key for them all.
Check for duplicate keys
There isn’t much sense in having more than one foreign or unique key for the same set of DAC fields. That’s why the PX1035 diagnostic will show you a warning if you have two foreign keys or two unique keys with the same set of used DAC fields. The check verifies foreign keys and unique keys separately. The code fix suggests removing all duplicate keys with the same set of fields except the one which you used to apply the code fix.
Check for Code Style
The PX1036 diagnostic will display a warning to you if your key declarations violate code style conventions. This is a rather complex check. If there is no primary key in the DAC it will look for a suitable unique key with a set of fields consisting of all DAC properties marked with IsKey = true. If there is an appropriate DAC the diagnostic will suggest a code fix which will turn this key into a primary key. This key will be placed as a first member of a DAC to keep the recommended order of keys.
If there is a primary key in the DAC then the diagnostic will check the names of unique keys. For a single unique it will check that its name is UK. In case of a different name the code fix will rename the key into UK. If there are multiple unique keys then it will be checked that they all are declared in a public static UK class. If this is not the case then code fix places all incorrectly declared unique keys into UK class. If there is no UK class Acuminator will generate one for you.
For foreign keys the check is similar to the case of multiple unique keys. The diagnostic verifies that all foreign keys are declared inside a public static FK class and the code fix places all incorrectly declared keys into FK class. Again, if there is no FK class then Acuminator will create one.
Check for unbound DAC fields
The last diagnostic PX1037 will show you an error for any unbound DAC field used in the key declaration. This applies for all kinds of keys. There are no code fixes for this error – you will need to rework your business logic manually in this case.
This diagnostic was created in order to avoid a run-time error. Internally, keys generate their own anonymous views which use fields specified in the key declaration. If you use unbound DAC fields in the key declaration, then this anonymous view will contain these fields and they will get into the SQL query sent to the database on the attempt to retrieve a DAC entity via the key. But the database does not have such columns because these fields are unbound. Therefore, in this instance the SQL query will cause a run-time error thrown from the database.
Code Map Enhancements
The second major part of Acuminator’s new features is the improvements in the Code Map tool window. This tool window gives you a quick overview of Graph and DAC structures and provides navigation to its component parts. It also works with Graph and DAC extensions.
In the last two releases, we greatly improved Code Map performance. Before it could take a couple of minutes to collect all information from a very big code file. Now it takes only about 2 seconds to complete!
During our work on Static Code Analysis, we learned how to collect a lot of data about Graphs and DACs from code. Some of this information can be quite useful for developers and therefore we add it to Code Map. This time we added some details and indicators to the Code Map tree control. Now for each tree node corresponding to a graph or graph extension, we now display an indicator which will tell you what exactly this node represents:
The Code Map will now also indicate if the Graph is a processing. Moreover, we display several indicators for Graph Views. At a glance, you can quickly see:
- Filter views
- Processing views
- Setup views
- Views derived from PXSelectReadOnly types which are not merged with the cache
- Custom views derived from the base PXSelect types
The Code Map for DACs also has one new indicator which will show if the DAC property is auto numbered as indicated after “string Bound” highlighted below in the lower right of the screenshot.
In this post there was a lot of information about Acuminator and even more information about new Acumatica APIs. Let us sum it all up with a quick recap of key points. The last two versions of Acuminator (2.3 and 2.3.1) provided important bug fixes and introduced new functionality and features Code Map tool window and static code analysis:
- The Code Map tool window:
- Now works many times faster than before;
- Displays a lot of new information about Graphs, Graph extensions and their views;
- Can tell you if DAC property is auto numbered;
- There are several new diagnostics which aim to support two of the less known Acumatica APIs:
- One of these APIs is the ability to turn off DAC and Graph extensions to improve system performance. The usage of this API is a recommended practice therefore Acuminator will report an error if your extensions are always turned on;
- Another supported API is a DAC Referential Integrity API which allows you to specify primary, unique and foreign keys of a DAC. There was a huge amount of work done by Acumatica developers to add these keys to all parts of the Acumatica ERP.
To allow you to use this API effectively Acuminator introduces a suite of new diagnostics which will help you to declare new primary and foreign keys and check that existing keys do not violate API conventions.
That’s all I wanted to share in this post. Thank you for your time! If you encounter an issue or have a new suggestion for Acuminator you can add them to the issues section on Acuminator’s GitHub page.