Working with DAC Extensions

Acumatica is full of great data structures and business processes, but what can you do when you need a new field on the standard Acumatica table? Acumatica has established a means for the user to define custom fields in a style similar to attributes on classes, but as a developer I prefer my data to be contained within the row directly. Adding a field to a row is handled via a DAC Extension, which involves adding the field to the database and then accessing the new field.

In today’s post, we will look at 2 ways of accessing the new field defined via a DAC extension. First, we will use the method GetExtension<> to generate an object containing the additional field(s). Second, we will create a new DAC class within our business logic to access the entire record in a single object. Which means of accessing the DAC extension will depend upon whether or not you need to update fields in the base DAC, which may need to execute business logic tied to the base DAC. For this post, we will assume that you have already created the DAC extension within your Customization Project.

Using GetExtension<>

#region Example for getting and updating values in a DAC Extension
protected virtual void UpdateDACExtension(InventoryItem item)
	InventoryItemExtension inventoryItemExtension = 
		(inventoryItemExtension.MyCustomField, "MyData");

In the code sample above, the method UpdateDACExtension includes a parameter for InventoryItem for simplicity. Usually, you will be in the middle of your business logic when you need to access the extension, so the key here is the first line within the method. We must initialize a new object of the “…Extension” DAC that was created for the DAC extension. Accessing the extension is a simple as applying the mehtod .GetExtension<InventoryItemExtension>() to our base object. Of course, my example is an extension of InventoryItem, but you will reference the DAC Extension of your base DAC.

Getting the data is nice, and sometimes that is all you need to do. However, if you never populate the field, there is little reason to ever retrieve the DAC Extension in the first place. Notice the to “Caches” lines of code. There are various ways to access the data, but this simplistic option sets the value and then applies the update to the cache. instead of Caches[“InventoryItem”], I could have just as easily (and maybe even a little more friendly for code validation in the IDE) used Caches[typeof(InventoryItem)] which Visual Studio can validate while coding.

Remember that the cache has been updated, but not saved. You must follow up with a save action or explicitly call Caches[typeof(InventoryItem)].Persist(PXDBOperation.Update) if no save operation is otherwise required for your code.

Also, note that we are accessing the cache by the BASE DAC, not by the extension. The Cache retrieves the database row. Acumatica combines the DAC and the DAC extension when it comes time for database interaction. Our business logic access the cache through the DAC definitions, so our code must access the DAC Extension where the extra fields are defined. This can be quite confusing, but after a little use it will make a little more sense.

Via Inheritance

Let’s look at our second option for accessing the new fields. In my case, I had created a custom table to track a physical event tied to a part using a TagID. I then stored the TagID in a new custom field added to a standard Acumatica table… hence, a DAC Extension. I needed to be able to find the Acumatica data per my custom TagID value, so I used this data access strategy to prevent cycling through every Acumatica base DAC and retrieving the DAC extensions until finding my needed record. Clearly, such a method of locating my data would be quite intensive of an operation and result in a bad user experience while waiting to locate the record. To overcome this issue, I simply made my own custom DAC, inheriting from the Base DAC and adding my extra field(s). Acumatica does something similar with POFixedDemand which extends INItemPlan to include additional data gathered in the analysis phase of generating Purchase Requirements.

public class POFixedDemand : INItemPlan

For our example:

public class MyInventoryItem : InventoryItem
	#region MyCustomField
	[PXDBString(30, IsUnicode = true, InputMask = "")]
	[PXUIField(DisplayName = "My Custom Field")]
	public virtual string MyCustomField { get; set; }
	public abstract class myCustomField :
		PX.Data.BQL.BqlString.Field<myCustomField> { }

Note that InventoryItem has a lot of business logic associated to it, so we really don’t want to update InventoryItem through MyInventoryItem. However, now I can get the InventoryID(s) associated to the value I stored in MyCustomField by using a PXSelect on MyInventoryItem. My value might be the NewItemRequest.RequestID value from my NewItemRequest DAC where I captured a custom request to setup an InventoryItem. While I could store the NoteID of the InventoryItem record in my NewItemRequest DAC for a One to One relationship, my request might result in multiple InventoryItem records such as SubItems for different colors of the same requested part. By defining my custom version of my DAC to access my custom field, I can query the database for all InventoryItem records pertaining to the relevant MyCustomField data. Subsequently, I can use that list of Inventory ID’s to perform business logic to the related InventoryItem records to ensure the appropriate business logic fires for those data updates.

Leave a Reply