2.5 Static enumerations
When you look at the status column on the Sales Order List screen, you will notice that it shows internally stored numeric values instead of user-friendly status descriptions. The list of possible status values and their descriptions is static and is not stored in any of the database tables.
Right now you can only find it in the documentation stored in the Status column of the
SalesOrderHeader database table, which was imported into the model as shown below.
<field name="status" type="tiny int" required="true">
<sql:column name="Status" default="((1))"/>
<summary>Order current status. 1 = In process; 2 = Approved; 3 = Backordered;
4 = Rejected; 5 = Shipped; 6 = Cancelled</summary>
Xomega model allows you to describe this information in a structured way, and use it in the generated screens. It also generates constants in the code that you can use instead of hardcoding these values.
So let's see how we can enhance our model with static lists of values like the one for the sales order status.
Enumerations for numeric values
We will start by adding an
enums element after the
types element in the
sales_order.xom file, and will declare an enumeration named
sales order status in there, listing all its items with their respective names and values.
<enum name="sales order status">
<item name="In process" value="1"/>
<item name="Approved" value="2"/>
<item name="Backordered" value="3"/>
<item name="Rejected" value="4"/>
<item name="Shipped" value="5"/>
<item name="Cancelled" value="6"/>
You can also define and set additional properties for enumeration items, and even inherit one enumeration from another, but, for our simple status enumeration, this basic setup is enough.
Next, we will declare a new logical type, which we can also call
sales order status, and will add a reference to our enumeration inside of it.
<type name="sales order status" base="tiny int enumeration">
<enum ref="sales order status"/>
Since the original type of the status field is
tiny int, we set the base type for our new type to be
tiny int enumeration, which is declared in the framework and combines various enumeration configurations with the
tiny int configurations.
As you add the new type, you will notice a warning on it, telling you that this type is currently not used in the model. So the next logical thing to do will be to set this type on the
status field of the
sales order object as follows.
<object name="sales order">
<field name="status" type="sales order status" required="true">[...]
Enumerations for string codes
To provide another example, let's define one more static enumeration, which will be based on string values now, rather than on numeric values. This is also typical if you store short codes that have longer decodes, e.g. a list of states with NJ as a code and "New Jersey" as a decode.
If you open
sales_territory.xom file, then you will see that sales territories have a
group field, which specifies one of the territories' global regions: North America, Europe, or the Pacific.
<object name="sales territory">
<field name="group" type="string50" required="true">[...]
Note that this field has an auto-generated type
string50, which maps a Unicode string of up to 50 characters long.
As we've done before, we will first define an enumeration
sales territory group in the same file, with all its values listed, as follows.
<enum name="sales territory group">
<item name="North America" value="North America"/>
<item name="Europe" value="Europe"/>
<item name="Pacific" value="Pacific"/>
Then we'll declare a type with the same name that references this enumeration. This time though, we will inherit our new type from the string-based
enumeration type. But to preserve the database properties of our original type, we will set its size to 50 and will use the
nvarchar SQL type, as shown below.
<type name="sales territory group" base="enumeration" size="50">
<enum ref="sales territory group"/>
And finally, we'll set that type on the
group field of the
sales territory object.
<object name="sales territory">
<field name="group" type="sales territory group" required="true">[...]
Adding custom criteria
Now, the reason we've added this territory group enumeration is to be able to add another criteria parameter
global region to the
read list operation on the
sales order object, which would use our new type as follows.
<operation name="read list" type="readlist">
<param name="customer name operator" type="operator">[...]
<param name="customer name" type="string" required="false"/>
<param name="global region" type="sales territory group" required="false"/>
<param name="territory id operator" type="operator">[...]
<param name="territory id" required="false"/>
Unlike other criteria, we did not add a separate operator parameter for simplicity, which implies the equality operator will be used whenever this value is selected.
Because this criteria parameter does not match any of the object's fields, nor does it match any of the output parameters, we will need to provide a custom implementation on how to use it in the implementation of the
read list operation of our service.
Let's build the model, and then navigate to the
ReadListAsync method of the
SalesOrderService class, and provide the custom code for filtering by the global region as follows.
public virtual async Task<Output<ICollection<SalesOrder_ReadListOutput>>>
ReadListAsync(SalesOrder_ReadListInput_Criteria _criteria, CancellationToken token = default)
var src = from obj in ctx.SalesOrder select obj;
// Source filter
if (_criteria != null)
// CUSTOM_CODE_START: add code for GlobalRegion criteria of ReadList operation below
// TODO: src = AddClause(src, "GlobalRegion", o => ???, _criteria.GlobalRegion);
src = AddClause(src, "GlobalRegion", o => o.TerritoryObject.Group,
To use the generated constant for the
IsEqualTo operator, we will add a using statement for the
Enumerations namespace at the top of the file as custom code, so that it doesn't get erased when the code is regenerated, as follows.
// CUSTOM_CODE_START: add namespaces for custom code below
Reviewing the results
Finally, let's build and run the application to review our changes for the static enumerations. After you open the Sales Order List screen, you'll notice that the Status criteria displays a dropdown list with our enumeration values.
Also, you'll notice a new Global Region dropdown list in the search criteria, which allows filtering sales orders by the specified sales territory group. Since there is no operator for the Global Region, its dropdown list will be not required, so it provides a blank option to clear this field.
If you run the search now, the results grid will look like this.
Notice that the Status automatically shows decoded descriptions instead of numeric values, both in the grid and in the criteria summary panel above the grid.