3.3 Grouping fields as child data objects
When working with such a large data object as the SalesOrder, it makes sense to break it up into groups of related fields, and display them in tabbed child panels. For example, the fields highlighted below in yellow are related to the payment information, and can be grouped under the Payment tab.

Let's update our model, so that these fields would be displayed in the Payment child panel.
Before we do that though, we can quickly turn the Shipment Method into a dynamic enumeration to allow selecting it from a list of possible shipping methods. As usual, we will run the “Enumeration Read List” generator on the ship_method.xom file, and update the output of the generated “read list” operation to return only the ID and name, as shown below.

Turning this group of fields into a child object will require a more advanced level of modeling, but it's still pretty straightforward once you get the hang of it. We will start by declaring a new xfk:data-object in the sales_order.xom with a class SalesOrderPaymentObject. Once you do that, you will get a model warning telling you that it doesn't have any properties defined, which we will fix next.

Right above the top level "objects" element, we will add a structs element, and define two structures named "payment info" and "payment update" that are based on the "sales order" object, and will configure them to have their parameters added to the SalesOrderPaymentObject data object.
The first structure, "payment info", will represent the payment fields that we read from the sales order service, so we'll go ahead and move all those read output parameters to this structure. Again, since it has all parameters for our data object, and goes first in the model, the order of these parameters will determine the order of the fields on the screen. While we're at it, we will override the type for the "due date" parameter to be just date, and set the type of the "currency rate" to be a string, so that we could return a user-friendly description of the currency rate.
The second structure, "payment update", will represent a smaller subset of the fields, which can actually be set by the user. This will not include any amounts and the currency rate, which are calculated internally by the system from other data. We will use this structure in both create and update operations. The following picture illustrates this setup.

Next, let's add our SalesOrderPaymentObject as a child of the SalesOrderObject with a name "payment". We will also configure the label for the "ship method id" field to be Shipment Method, since we turned it into a selection control, as shown in the picture below.

Finally, we will move the “ship date” parameter down, and replace the corresponding output parameters in the "read" operation of the sales order object with a reference to the "payment info" structure using the same child name.

And similarly, we will replace those same input parameters in the “create” and “update” operations as the ones we removed from the “read”, with a reference to the "payment update" structure with the same name. We’ll also move the “ship date” parameter down as shown below.

Now that we have updated our service model to return and accept child structures like this, we will need to provide custom implementations for handling these parameters in the generated service. However, those will be pretty large pieces of custom code, which we don't want mixed in with the generated code. Plus, that custom code for the “update” and “create” operations will be pretty much the same. So a better design will be for us to extend the generated partial class SalesOrderService with our own file, where we can implement this custom code in separate methods, and mix in the calls to those methods in the generated file, which we are about to see.
The easiest way to do it is to set the extend attribute to true on the svc:customize element of the sales order object configuration, as shown below.

Once we build the model, we will notice that there is a SalesOrderServiceExtended.cs file nested under the SalesOrderService class, which provides extension of the partial class for our custom methods. Let's open it up, and add a GetPaymentInfo method that populates and returns a PaymentInfo structure for the given SalesOrder object as follows.

Next, we will add a reusable method UpdatePayment, which takes input data as a PaymentUpdate structure, and updates the SalesOrder object provided within the specified context. This method can accept both new and existing SalesOrder objects, and therefore can be used from both create and update operations. The snippet below shows what it would look like.

For the service validation error we added a localizable message to the Resources.resx file in the Services.Entities project, and ran the custom tool on the Messages.tt T4 template nested under it to generate the constants, similar to how we did for the UI validation messages earlier.

All that's left to do now is to set the corresponding custom code for the generated ReadAsync operation of the SalesOrderService class to call our GetPaymentInfo method as follows.

Similarly, for CreateAsync and UpdateAsync operations we will set the custom code to call the UpdatePayment method, as shown below.

Let's run the application, and review the updated Sales Order details screen. The following picture illustrates this.

As you can see, our fields have been moved down from the main panel to a separate Payment tab, where they are stacked up in the order they were listed in the "payment info" structure. The fields that are not listed in the "payment update" structure are displayed as read-only labels. Shipment Method with updated label is a dropdown list with available shipments methods, the Due Date is displayed as a date only, and the Currency Rate is shown as a string that we build in our custom method.
Next: 3.4 Using a multi-value property for a simple sub-object >