Monday, November 3, 2014

Put custom business process into MS Dynamics AX. Part II: Data entry

It's the second step of the customization of Microsoft Dynamics AX (DAX). Remind that our aim is to put a custom business process into DAX. On this step we already have database structure - defined tables and datatypes. And we will try to give to an end-user an ability to input data in database. In other words we will design some forms.

The form, started from scratch, for Speciality table

So let's start with a form for zzSpeciality table - list of physician's specialities.

Open zzAppointmetsProject, select it it, right click, New -> Form. Rename created form to zzSpecialityForm (Name property). To look at the empty form select zzSpecialityForm, right click and press Open (or use the respective button in toolbar).

All GUI elements are stored in Designs node. Here there are two elements: Design shows form's elements as hierarchy and DesignList shows them as list, sorted alphabetically. It doesn't matter in what node we add an element - both nodes will be synchronized in a wink.

Let's link the form with zzSpeciality table. For this select zzSpeciality node and drag it onto Data Sources node inside zzSpecialityForm.

New data source has been created and also all fields from zzSpeciality table have been added into the data source. Save zzSpecialityForm form.

All that is left to do is adding a visual component to fill zzSpeciality table with data. Select Design node, right click, New Control -> Grid. In the grid set data source created before (DataSource property). And finally drag necessary fields from the data source into Grid node.

Save zzSpecialityForm form and run it. Now we can entry data into zzSpeciality table.

You see an empty record where you can enter code an name. After that to insert one more record shift down (or press Ctrl+N) and new record will be inserted and be ready for filling. To update an existing record focus on needed field and change a value. To delete a record select it and press Alt+F9 (or choose item Delete Record in File menu on the form).

Just for example input the following data
Code  Name
----  ----
D     Dermatology
N     Neurology
PD    Pediatrics

And as finishing touch... You might notice that created form is not resizable. To fix this set properties Height and Width on Design and Grid nodes to Columnf height and Column Width respectively.

A form based on a template for Physician table

We have created previous form completely by hand. But DAX provides a kit of predefined templates to make a process of form's design more easy. Besides if we use templates our custom forms look like native ones. And without fail they content best practices recommended by Microsoft for form's development.

So the next form, for filling zzPhysician table, will be based on one of these template. To get a form similar to one, we have designed before, we should use SimpleList template.

List of templates is not available in Projects window, so we must go to main AOT window, select Forms node, right click, New Form from template -> SimpleList. New form will be created and shown in separate window. Rename the form to zzPhysicianForm (Name property), save it. Then drag zzPhysicianForm node to the project. 

Like we did for previous form include zzPhysician to zzPhysicianForm (drag zzPhysician node onto Data Sources node). But, unlike zzSpecialityForm, this form has already contain grid component. So find it by way Designs -> Design -> Group:GridContainer -> Grid:Grid, and set its DataSource property to zzPhisician. Then drag First_Name, Last_Name and zzSpeciality fields from zzPhisician data source to grid.

If we save the form and open it we will see that it looks close to zzSpecialityForm, but except grid it contains action bar, it is resizable, etc. - all these things were hidden inside SimpleList template.

All almost done... When you try to enter some data you will be surprised how Speciality field behaves. In edit mode the field allows to choose a speciality from dropdown list. But the list contains values of RecId field of zzSpeciality table, and these values are shown when one of specialities is chosen. That won't pass. We need something more informative, speciality's code, or name, or both.

Make lookup field usable

We have to return to zzSpeciality table definition. 

First step is to add fields we want to see in dropdown list. Just select Speciality_Code and Speciality_Name fields and drop them on AutoLookup node. 

Save zzSpeciality table and take a look how zzPhysicianForm has changed. 

Really, when we try to edit Speciality, added fields appear in dropdown list. But RecId is still shown too and its value is used in Speciality field after a choice has been done.

Further we have two ways how to replace RecId values in grid's record - replace by all fields included into AutoLookup group or by one of unique field. 

Anyway we have to replace a visual component that displays zzSpeciality field on zzPhysicianForm. From Int64Edit component to ReferenceGroup one. Just delete Int64Edit:zzPhysician_zzSpeciality element and create ReferenceGroup one (select Grid:Grid element, right click, New Control -> ReferenceGroup). Rename it to zzPhysician_zzSpeciality (Name property). Set DataSource property to zzPhysician and ReferenceField property to zzSpeciality.

To implement the former way set ReplacementFieldGroup property of ReferenceGroup:zzPhysician_zzSpeciality component to AutoLookup. Here is how it looks.

The latter way requires to set replacement key on zzSpeciality table (ReplacementKey property). This property allows be set by unique index only. So let's create such index for Name field. 

Inside zzSpeciality node select Indexes node, right click, New Index. Rename create index to uiSpecialityName (Name property), make it unique (set AllowDuplicates property to No) and make it accessable as replacement key (set AlternateKey to Yes). Add field Speciality_Name to the index (drag and drop this field from Fields node to uiSpecialityName node). Save changes and set ReplacementKey property to uiSpecialityName. 

Note that the field on which the index was built appeared in AutoIdentification field group.

All that remains is to change ReplacementFieldGroup property of ReferenceGroup:zzPhysician_zzSpeciality component to AutoIdentification. Now a choice from list of specialities looks like this.

Inasmuch we have finished with lookup tuning let's fill zzPhysician table with some examplary data.
First Name  Last Name  Speciality
----------  ---------  -----------
Andrew      Sheldon    Dermatology
Katherine   Forman     Pediatrics
Jose        Marlow     Dermatology

Using another template for Appointment table

Now when we have already learnt to design a simple form with grid it would be curious to try another form template. Create a form based on DetailsFormMaster template as we did before, include it to our project and rename it to zzAppointmentForm. Then include into the form zzAppointment table as datasource.

DetailsFormMaster template represents data in two ways: as a grid and as details of a row when an user works with fields of one row only. Besides it has a toolbar where some actions with data are available (create new row, edit row, etc.) 

Inasmuch we are fimiliar with design of grid let's make it for a start. Expand Design node to Grid:HeaderGrid node. Set its DataSource property to zzAppointment and drag Appt_Date, Appt_Amount and Appt_Priority fields from Fields node of zzAppointment datasource to the grid. Save changes and run the form. Opened form is in Details view, yet empty. Switch to Grid mode using toggle on the form's buttom panel. 

Something is missing... And this is Physician field. Like we did it for physician's speciality
  • for zzPhysician table add First_Name, Last_Name and zzSpeciality fields into AutoLookup field's group
  • for zzAppointmentForm create new ReferenceGroup component inside the grid
  • for created component set Label property to Physician, DataSource property to zzAppointment, ReferenceField property to zzPhysician and ReplacementFieldGroup property to AutoLookup
Then save changes, rerun the form and look at result.

Entry some appointments
Appt Date   Appt Amount  Appt Priority  Physician
----------  -----------  -------------  ----------------------------
10.03.2014  300.00       Medium         Andrew Sheldon, Dermatology
21.03.2014  150.00       High           Katherine Forman, Pediatrics
23.03.2014  100.00       Medium         Katherine Forman, Pediatrics
02.04.2014  400.00       Medium         Jose Marlow, Dermatology
05.04.2014  320.00       Medium         Jose Marlow, Dermatology
10.04.2014  200.00       Low            Andrew Sheldon, Dermatology
11.04.2014  100.00       Low            Katherine Forman, Pediatrics

After we have been through with grid mode it's time to focus on details mode. 

Take a glance on the structure of TabPageDetails node. It has one Group component to show some title information about a row and a Tab component with HeaderGeneral tab page where we can put our components to represent row's fields. Drag Appt_Date, Appt_Amount and Appt_Priority fields from Fields node of zzAppointment datasource to TabPage:HeaderGeneral node. 

Then inside Tab:HeaderDetailsTab create new tab page, rename it to HeaderPhysician (Name property) and headline it to Physician (Caption property). Inside created tab page create new ReferenceGroup component, rename it to Physician and set its properties Label, DataSource, ReferenceField and ReplacementFieldGroup to Physician, zzAppointment, zzPhysician and AutoLookup respectively.

Save changes, run the form and look what happened.

This picture shows the form in read and edit modes. To move via table data use a navigation bar on the form's buttom panel.

Additional recipes to make working with data more friendly

If you try to work with the form a bit you might notice some inconveniences. For example, it would be great to have a dropdown calendar to populate a field with date. Or why an error appears when you click on a physician's data in read mode? So let's investigate how to make some embellishments on the form.

Add dropdown calendar to DateEdit component

It's very easy. Select zzApptDate EDT (we use it for Appt_Date field of zzAppointment table) and set FormHelp property to SysDateLookUp. Save changes and run zzAppointmentForm. 

Now we can select date for Appointment Date field from dropdawn calendar, both in Details or Grid mode.

Call related form for lookups

If you open any system forms with details view (as our zzAppointmentForm form), in read mode, you can notice that lookup fields are links at the same time. Clicking on the link could run a form with data of related table. 

To achive it we will use FormRef property for a table that we want to show by clicking on a link. In our case it is zzPhysician table. FormRef property can be set by one of existing display menu items.

To create an item select zzAppointmentProject, right click, New -> Menu Item -> Display. Rename created item to zzPhysicianFormMenuItem (Name property) and assign a label Physicians (Label property). As you see ObjectType is equal to Form, so what we should do - set Object property to zzPhysicianForm. Let's back to zzPhysician table and set zzPhysicianFormMenuItem to FormRef property.

Save changes, run zzAppointmentForm form and click on any part of Physician field.

Field based on display method

One more ability in data representation I want to mention - display method. In particular it allows replace some text by image, just to make our user interface more attractive. Our client is appointment's priority.

Before we have not touched on programing on X++ language - plenty powerful object oriented language, embedded into DAX and is used actually everywhere in DAX. But now we will use it a bit to implement a display mehod. Display method will be used as a calculated (or virtual) field in the control that shows appointment's priority.

In project's tree find zzAppointment table, expand it and select Methods node, right click, New Method. New window of X++ editor will be opened. Replace existing code by the following

display int priorityAsImage(zzAppointment appointment)
    switch (appointment.Appt_Priority)
        case zzApptPriorityEnum::HighPriority :
            return 12719;
        case zzApptPriorityEnum::MediumPriority :
            return 12717;
        default : return 12716;
Save changes and compile the method (Build -> Compile in main menu or a button on toolbar of X++ editor).

Have a walk via method's code. First row contains method's declaration
  • display keyword specifies that the method can be used as display method
  • int is a type of returned value
  • priorityAsImage is method's name
  • appointment is input parameter of zzAppointment record type

All following code in braces is body of the method. According to value of appointment priority the method returns certain code of an image. We can view all available images and their codes in the list of embedded resources (Tools -> Embedded resources in main menu).

Let's return to GUI. Find and select a grid in zzAppointmentForm, right click, New Control -> Window. Move created control between Appt_Priority combobox and ReferenceGroup components. Rename it to PriorityImage (Name property) and link with our display method: set properties DataSource and DataMethod to zzAppointment and priorityAsImage. Also change the following properties: Width and Height to 14, Enabled, AlignControl and ShowLabel to No, Skip to Yes.

Save last changes, run zzAppointmentForm, go to Grid view and look at the result.

Organize end-user menu

When we have forms to enter all our data it would be great to make an access to them in DAX for end-users. The approach we will consider is based on menus. Let's create own menu where we will place links on our forms.

Select zzAppointmentProject node, right click, New -> Menu. Rename it to zzAppointmentMenu (Name property) and set Label property to Appointments. Inside created menu let's add two submenus: Common, where the link on zzAppointmentForm will be placed, and Lookups for links on zzSpecialityForm and zzPhysicianForm.

Twice in a row select zzAppointmentMenu, right click, New -> Submenu. Change names of created submenus to CommonSubmenu and LookupsSubmenu and labeled them as Common and Lookups respectively.

Further we have to create display menu items for all our forms. One of them, for zzPhysicianForm, has already been created to call the form by clicking on Physician field in zzAppointmentForm. So add to our project two new display items and set their properties: Name to zzSpecialityFormMenuItem and zzAppointmentFormMenuItem, Label to Specialities and Appointments and Object to zzSpecialityForm and zzAppointmentForm.

Fill zzAppointmentMenu up by created menu items. Drag zzAppointmentFormMenuItem onto Common submenu, zzSpecialityFormMenuItem and zzPhysicianFormMenuItem onto Lookups submenu. Save all changes.

Last step is to include zzAppointmentMenu into DAX main menu. For this find MainMenu node inside Menus node in AOT (not in our project's tree). Select it, right click, New -> Menu reference.

New window with the list of available menus will be shown. Find zzAppointmentMenu in the list and drag it onto MainMenu in AOT window.

Save changes and restart DAX to rebuild its main menu. In DAX find Appointments menu

and open it.

Here we have finished and have working custom business process inside DAX.

No comments:

Post a Comment