Reader small image

You're reading from  Extending Microsoft Dynamics 365 Finance and Supply Chain Management Cookbook - Second Edition

Product typeBook
Published inMar 2020
PublisherPackt
ISBN-139781838643812
Edition2nd Edition
Right arrow
Author (1)
Simon Buxton
Simon Buxton
author image
Simon Buxton

Simon Buxton has worked with Dynamics 365 Finance and Supply Chain Management since its earliest incarnations, starting with the product in early 1999 when Dynamics 365 Finance and Supply Chain Management was known as Damgaard Axapta 1.5. Simon has been the technical lead on many highly challenging technical projects in countries all around the world. These projects included complex integrations with on-premises and external systems, ISV solutions, and many technically challenging customer solutions. Now working with Binary, he was part of a team that implemented the first Dynamics 365 Finance and Supply Chain Management implementation as part of the Community Technical Preview (CTP) program, which led to the close working relationship with Microsoft that made this book possible
Read more about Simon Buxton

Right arrow

Creating enumerated types

Enumerated types are called base enums in SCM, which are similar to enumerated types in C#. They are commonly referred to as enums. An enum is an integer referenced as a symbol, which can be used in code to control logic. It can also be used as a field in a table that provides a fixed drop-down list to the user. When used as a field, it has the ability to provide user-friendly labels for each symbol.

Base enums are usually only used as a drop-down list if we need to understand, in code, what each value means. They should contain a small number of options, and when used as a field, the list cannot be searched or extended by the user.

All enums have to be defined in the data model before we use them and can't be defined within a class or method.

Base enums are given the ability to be extensible in this release; the mechanics of this are covered in more detail in the There's more... section.

Getting ready

The following tasks continue the vehicle management project, ConVehicleManagement, which was created in Chapter 1, Starting a New Project, although this is not a prerequisite. We just need an SCM project that was created as an extension project.

How to do it...

In order to create a new Enum, follow these steps:

  1. Either right-click on the project (or any sub folder in the project) and choose Add New item....
Always try to use keyboard shortcuts, which are displayed next to the option. In this case, try Ctrl + Shift + A.
  1. This will open the Add New Item window. From the left-hand list, select Data Types, which is under the Dynamics 365 Items node.
  2. This will filter the available options in the right-hand list. From this list, choose Base Enum.
  3. Enter a name, prefixed appropriately. In this example, we are creating an enum to store the type of vehicle; so, we will enter ConVMSVehicleType as Name and click Add.
This should have created a folder called Base Enums or added the new Enum to the folder if it already existed. This is regardless of the folder we selected when the Enum was created. If this did not happen, the Organize projects by element type setting in Dynamics 365 | Options was not checked. You can remove the base enum and add it back from Application Explorer after the setting is checked.
  1. We will now have a new tab open with our empty enum. Before we continue, we must create labels for the Label and Help properties. Double-click on the label file so we can add a new label.
This was created in Chapter 1, Starting a New Project; if you don't have a label file, one must be created before we continue. We should always maintain the en-us label file, as this drives the labels displayed on the designers in Visual Studio (for example, the form designer). Since we develop in English, we would maintain en-gb, en-ie, and en-gb as standard, as it is usually just a copy and paste with minor changes for en-us.
  1. This will add an empty line for us. On this empty line, click on Label ID and enter a short name, in the style of an identifier, such as VehicleType.
  1. Enter Vehicle type in the Label column. You can add a comment in order to provide context for other developers, in case it is reused.
  2. Press New and repeat for the next label. Use VehicleTypeHT for the Label ID and enter the text: Defines the basic type of vehicle, used to govern static vehicle type specific properties for the Label property. It would be tempting to just write The type of vehicle, but this provides nothing to the user in addition to what the field name suggests. The result should be shown as follows:
We could repeat this for each label file language we created, but this is more efficiently done in bulk. We could also use a translator to assist after project completion in order to avoid translation errors. Most online translation tools aren't appropriate for this task as they won't have the necessary context or industry knowledge.
  1. Without left-clicking first, right-click on Label ID for VehicleType, and choose Copy. Left-clicking first might select the text in the field, which is undesirable. We should end up with @ConVMS:VehicleType in the paste buffer.
  2. Reselect the editor tab for the Enum, and look for the property sheet. If this isn't visible - it's usually at the lower right of the screen - right-click on the Enum inside the tab and select Properties.
  3. In the property sheet, click in the Value field for the Label property and choose paste (or press Ctrl + V).
You could instead type in Vehicle type in the Label field, click on the ellipsis (...), and search for the label. This is OK when trying to use standard labels, but when we are creating labels, copy and paste is much faster.
  1. Since we just added HT to the label for the help text label, select the Repeat this process for the Help property and paste again. Then, click on the property's value control and add HT to the end of the label; remember that Label ID is case-sensitive.
These properties do not have to be populated, but it will cause a best practice deviation warning when the project is built if these properties are left empty. The time spent here is of great value to a user, reducing mistakes and subsequent support calls.
  1. This enum is used to define the type, and if we want the end-user customer (or other partners if we are an ISV), we should make this an extensible enum. Locate the Is Extensible property and set it to true. See the There's more... section for more information on this.
  2. We will no longer be able to set the Use Enum Value property, so we will now add the elements to the Enum. The options we will add should be created as follows:
Name (symbol) Label
NotSelected Not selected
Bike Motorbike
Car Car
Truck Truck
The Label column in the preceding table shows the literal to use; the actual value will be the label ID, such as @ConVMS:Motorbike.
  1. To add each symbol, or element as it is referred to in the editor, right-click on the Enum and choose New Element.
  2. Complete the property sheet as per the table, and repeat for each required element.
For non-extensible enums, we can control the ranking, which is done by setting the Enum value property. For readability, we would alter the order in the designer with the Alt + up and Alt + down keys. This property is not visible in our case as we set the Is Extensible property.
  1. Press the Save or Save all buttons in the toolbar.

How it works...

Enums are stored as integers in the database, and we can assign an integer value to a field based on an enum.

When the values of the enum are shown to the user, SCM will look this up using the enum's definition from the field in the user's language. If a label is not defined for the user's language, the label ID (@Con:MyLabelId) is shown to the user. If a label was not defined in the enum element, the symbol (MyEnum:Value) is shown.

For standard, non-extensible enums, the following lines of code are effectively the same:

InventTable.ABCRevenue = 1;
InventTable.ABCRevenue = ABC::B;

Of course, we would never write the first option—we use enumerated types specifically to avoid having to remember what each number means. If the enum was created as extensible, the values associated with the symbol are not assigned at design time, they are installation-specific. Extensible enums are implemented as a class, and we, therefore, have a slight performance hit as we are no longer dealing with numbers.

We use enums for many purposes, ranging from table inheritance (different fields are relevant to a type of vehicle) to providing an option list to a user, and even controlling which type of class is created to handle logic specific to the vehicle type.

There's more...

Base enums are therefore a great way to provide a link between logic and the user interface. However, there are limitations; the options are defined in code and the user cannot add to the list. The problem with making the options user-definable is that we can't use the values in code, unless we want to add the options to a parameter form, which is not very extendable.

What we can do is use the code pattern exemplified in the item model group form. Here, we have a user-definable list, with an Enum that determines the inventory model that the items will use.

Using enums for comparison and status

It is very common to use an enum to define the status of a record, and we will cover state machines in Chapter 15, State Machines. When defining an enum for status, we order the elements so that we can compare them in code.

Given that standard enums are essentially a symbol representing an integer, we can use relative comparisons. These are commonly used in status fields. Let's look at the following vehicle status:

Symbol Value
Created 0
Review 1
Active 2
Removed 3


If we want all vehicles not removed from service, we could write the following:

select * from vehicles where vehicles.VehicleStatus < VehicleStatus::Removed;

If we want a list of vehicles that have ever been active (for example, every status on or after Active), we could write the following:

select * from vehicles where vehicles.VehicleStatus >= VehicleStatus::Active;

If we want all vehicles not yet active, we could write the following:

select * from vehicles where vehicles.VehicleStatus < VehicleStatus::Active;

We could also use query ranges to create lists so that users can see pertinent lists of vehicles using the same concept.

Extensibility in base enums

There are two notable limitations when an enum is created as extensible.

One is that we can't use it in conjunction with the mandatory property on fields in order to force a user to select a value. The first element is probably zero (which is not valid for mandatory fields), and will usually work—we can't guarantee this behavior and must not use this technique.

The other limitation when using comparisons on enums is using relative comparisons, such as greater than. For example, the options for the SalesStatus base enum are as follows:

  • None
  • Backorder
  • Delivered
  • Invoiced
  • Canceled

This enum was changed from a standard to an extensible enum in an early application update. Prior to the update, the following code would normally return a list of the SalesTable records that are not yet invoiced with SalesId and SalesName populated:

select SalesId, SalesName from salesTable where salesTable.SalesStatus < SalesStatus::Invoiced;

This will now fail with a compilation error. Extensible enums are designed to be extended by third parties, who may add their own options to the type. This means that there is no ranking in extensible enums, which means that we can't use greater than or less than expressions. We must, therefore, write code such as the following:

select SalesId, SalesName 
from salesTable
where salesTable.SalesStatus == SalesStatus::Backorder
|| salesTable.SalesStatus == SalesStatus::Delivered;

The code upgrade tool in the Life Cycle Service (LCS) would look for this and assist the developer in correcting the code so that it is built correctly.

The actual values of the SalesStatus enum are stored in tables in SQL, where the values are set when the database is synchronized. The values will not change after this, but new elements that are subsequently added will be appended to this table. Since the development, OneBox VMs come with SQL Server Management Studio; you can open this, and use the following Transact-SQL against the database AxDB in order to see the way in which this data is stored:

SELECT  E.[NAME], V.NAME AS SYMBOL, V.ENUMVALUE AS VALUE
FROM ENUMIDTABLE E
JOIN ENUMVALUETABLE V ON V.ENUMID = E.ID
WHERE E.[NAME] = 'SalesStatus'

This will show the following result if this enum has not been extended:

NAME SYMBOL VALUE
SalesStatus None 0
SalesStatus Backorder 1
SalesStatus Delivered 2
SalesStatus Invoiced 3
SalesStatus Canceled 4


This can occasionally be useful during development and testing when extensible enums are used.

The lesson here is that we are free to use comparisons on the enums that we control, but we can't assume that a third party (ISV or Microsoft) will not change a standard enum to be extensible. This also means when referencing enum values in query ranges, we must use the SalesStatus::Backorder format and not 1. Even if the enum is not extensible, we should still use this format.

Previous PageNext Page
You have been reading a chapter from
Extending Microsoft Dynamics 365 Finance and Supply Chain Management Cookbook - Second Edition
Published in: Mar 2020Publisher: PacktISBN-13: 9781838643812
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
undefined
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at €14.99/month. Cancel anytime

Author (1)

author image
Simon Buxton

Simon Buxton has worked with Dynamics 365 Finance and Supply Chain Management since its earliest incarnations, starting with the product in early 1999 when Dynamics 365 Finance and Supply Chain Management was known as Damgaard Axapta 1.5. Simon has been the technical lead on many highly challenging technical projects in countries all around the world. These projects included complex integrations with on-premises and external systems, ISV solutions, and many technically challenging customer solutions. Now working with Binary, he was part of a team that implemented the first Dynamics 365 Finance and Supply Chain Management implementation as part of the Community Technical Preview (CTP) program, which led to the close working relationship with Microsoft that made this book possible
Read more about Simon Buxton