Monday, May 11, 2015

Tutorial: Caching display methods

Overview

Caching is an important part of the Microsoft Dynamics AX application. It allows us to significantly speed up certain processes by avoiding redundant round trips to the server and the database. Display/Edit methods are a good example of where caching is helpful, as it significantly increases the overall responsiveness of a form.
In AX 2012 R3 there are a number of ways you can specify that a display method needs to be cached. Below is a list, as well as a brief description and an xpo with a tutorial form to show the various options.
  • Invoke cacheAddMethod(MethodName) in the init() method of the data source
This is the only option that was available in older versions of Dynamics AX.
All cached data methods are calculated and cached in executeQuery of the data source they belong to. That is why it is important to call the above method in the init() of the data source, once the data source is initialized, but the data hasn't been retrieved yet. The optional second parameter controls whether or not the display method value should be refreshed when data is written to DB
  • Add the SysClientCacheDataMethodAttribute attribute on the display method
Now that attributes are available, caching a display method is as easy as adding an attribute on the method. It has an optional parameter same as the above method. Note, that marking a display method with this attribute will mean that it is cached on all forms the method is used on. So make sure that is what you want to happen before adding it.
  • Set the “Cache data method” property on the display method form control to Yes
This is, again, a new capability in the latest version of AX, that is pretty much identical to calling the cacheAddMethod, just much easier.
  • Set the “Cache data method” property on the display method form control to Auto, where the data source is read-only
Now, this is something that is done automatically by the kernel. If the data source which the display method belongs too is read-only, and Cache data method property on the corresponding control is set to Auto, the kernel will cache this method. This is smart, as the value can’t really change. You can still change the property to No, if there’s a reason why you want the display method not to be cached.

Additional info

On top of the above, I would also like to mention a few other things.

  • There are 2 kernel-defined methods, called titleFields and parentTitleFields, which are always being cached automatically by the kernel. The titleFields method corresponds to the 2 Title Fields that you can set on the table level. Then, if you have this table as your TitleDatasource on the form design, the 2 fields selected will be shown in the caption of the form. parentTitleFields is very similar, only it displays the title fields of the parent data source table, which is relevant in a header-lines (dynalinked datasources) kind of scenario. As a fallback, it shows the titleFields.
  • There are methods for managing the cached methods, one of them being cacheCalculateMethod, which asks the server to re-calculate the method value and update the cache for the current record. Note that it will only take effect if the method has previously been cached. In the tutorial project I am using this method in the form method runHack to refresh the value of 2 of the display methods. 

Example

You can download the xpo project with the tutorial from my OneDrive.
Upon opening the form in the project, some data will be pulled into the form. More specifically, the first 3 items/customers/vendors/sites/warehouses. For each of them a display method is present, and one of the above approaches is used to cache it.

Step 1



None of the display methods are cached. As you can see, nothing extra was done in executeQuery of the main data source, but itemName was cached for all 3 records for the read-only data source, as the control has "Cache data method" property set to Auto. This happens even though the tab page is not the active one, so it wasn't really yet necessary to calculate these values. Maybe the user won't even navigate to that tab page. You should consider this carefully when designing a form, and always aim at increasing the overall responsiveness of a form by only executing the appropriate display methods.

Step 2 and 3



inventSiteName and vendName were cached (through one of the options above). As you can see, now their values are calculated once for each of the records shown. InventLocationName and custName are still not cached.
Clicking on Refresh result button will show any extra calls to the display methods since the form started. As you’ll quickly notice, the non-cached methods are being called every time something hints at covering up the value of the control, or rather every time AX thinks it needs to recalculate the value, which is VERY OFTEN. That should make it pretty clear how useful caching is for display methods.

Step 4



Now inventLocationName was also cached, and the only method that is remaining and not cached is custName. You can see how more and more methods are executed in one batch on the server as part of the executeQuery method on the data source.

Step 5



As the last step, I have added 2 standalone controls that show the Title fields of the main data source table, and started logging the time method caption is invoked. As I have written above, titleFields and parentTitleFields are cached automatically, but you can't really see it, as it is not possible to override it. caption is the closest you can get to it, so I figured it's worth showing that. You can see the method is evaluated right after all data source queries have executed (as it relies on the currently selected record). It is evaluated first time based on the selected data source record, and then again when focus actually goes to the grid, so that the first record is explicitly selected. It will be invoked again whenever the active record changes. Note that unlike custName method, it does not get constantly refreshed.

Play around with the form and let me know if you have any questions or comments.
Thanks

Wednesday, March 04, 2015

Walkthrough: Fixing the error message "WorkGroupingId is not found in the map"

Introduction

Since the release of the new Warehouse management solution with Microsoft Dynamics AX 2012 R3, a number of people have reported a sporadic error that was difficult to reproduce, but it was happening consistently on their environments.

The error message shown to the user on the mobile device was also not very helpful:
The value "WorkGroupingId" is not found in the map.
The challenge for us is usually to reproduce the problem, so we can actually debug through the code and see the root cause. We have finally managed to reproduce the problem recently, and have produced a simple fix, that will prevent people from getting into this situation in the future.
I don't yet know if this will be released as a hotfix for 6.3, but I will make sure to update this post with the link if that happens.

You can also just get the code from this post below, but that means you will need to apply it yourself.

But the primary purpose of this blog post is to release a small job to update the data that was messed up. Since we will not update all consumers of the mobile device menu items impacted, but only prevent future setup like that.

Of course, please have your development team review the job and the fix before applying this on your environment, and make sure to run it on your test environment first.
Neither Microsoft nor I will be held accountable for any data loss you might experience as a result of incorrectly applying the fix.

Example of scenario that is broken

I have a purchase order for one of my vendors, for 1 item that is WHS-enabled, as shown below:

Purchase order details
Simple purchase order
I now want to use the mobile device to receiving this purchase order in my WHS-enabled warehouse 42. That assumes that I have Location directives, Work templates, Work users, etc. set up already. If you are not familiar with the inbound flow in Warehouse management, please look at some my other posts, where this is described. For this post, I only will include the configuration screen for the Mobile device menu items.
In my company, we used to normally receive small inbound orders, so we configured the mobile device to allow first grouping multiple incoming orders to put them away all at once, and then grouping the puts into one once you are at the location (assuming, of course, that the put-away location is the same for multiple orders). The following menu item configuration corresponds to that:

Mobile device menu items
Mobile device menu items - User grouping
But then, since we got more and more deliveries that were much larger in size, where grouping them for put away on one pallet was no longer an option, we decided to change the menu item setup to just order by order, as shown below:

Mobile device menu items
Mobile device menu items - User directed

But suddenly, when processing putaway for incoming orders, our workers started getting the above error about "WorkGroupingID not found in map":

Warehouse mobile device portal
Warehouse mobile device portal flow

Product bug fix

The fix, as I mentioned above, will only address the part, that touches the configuration of the mobile device menu items, as that is where the root cause of the issue is. When you change the "Directed By" to something other than "User grouping" or "System grouping", the "Group putaway" gets hidden, but still contains the value it had before. Then the mobile device code relies only on this value (in some flows), and does not check that the "Directed by" is also appropriate.

Here's the fix. I am inserting it as an image so it's more difficult to copy to make sure you don't overwrite whatever changes you have in that method already.

Code comparison tool
Code changes required to fix "WorkGroupingId" bug

Data fix

The above fix means that people who have invalid setup will still be hitting the exact same issue when doing putaway. So below is a small job that fixes the invalid data by unchecking the "Group putaway" check-box for menu items where it was set incorrectly.

static void dataFix_WHSRFMenuItemTable_WorkGroupId(Args _args)
{
    WHSRFMenuItemTable menuItemTable;

    ttsBegin;

    update_recordSet menuItemTable
        setting GroupPutaway = NoYes::No
        where menuItemTable.MenuItemDirectedBy != WHSMenuItemDirectedBy::SystemGrouping
            && menuItemTable.MenuItemDirectedBy != WHSMenuItemDirectedBy::UserGrouping
            && menuItemTable.GroupPutaway == NoYes::Yes;

    ttsCommit;

    info(strfmt('Fixed %1 menu item(s).', menuItemTable.RowCount()));
}

When you run the above job, you'll at the end get an infolog message informing you how many records were fixed.

Result

After applying the above fix and running the above data fix job, you should be able to successfully complete the putaway.


Monday, March 02, 2015

Tutorial: Using Labor Standards in the new Warehouse Management solution

This post consists of two parts: An introduction to labor standards from a business standpoint, written up for me by Steve Mulaik from the Progress group, and then a short example of how all of this looks in AX today.

Introduction

One of the less well known but very distinguishing features of the new Warehouse management solution in Dynamics AX 6.3 is its support for multi-variable, engineered labor standards.  Labor standards are very important in warehouses that have lots of people.  A common misconception is that they are used to increase the pace of the workforce.  In reality, pace rarely increases much if at all.  Two other positive effects result from the proper use of labor standards.

First, the utilization of the workforce improves, i.e. the time when workers are working.  With labor standards a warehouse manager can better estimate how many people are really needed to do a job and he can schedule accordingly.  A surprising outcome from standards projects is that they reveal how much supervisors “hedge” labor to cover the unknown.  Supervisors are much more willing to move or trade resources from their area to other areas after labor standards are implemented; they can see how much their department’s performance suffers if they do not.
AX can provide feedback on historical performance for a given warehouse task and that information can be used to develop a good planning number for how much a person can get done in a given area in a given segment of time.  No one can remain busy and perform well in a warehouse or warehouse area that has 30 people that really needs 25.


Second, labor standards can drive improvements in productivity through better methods.  A standard should be set around the best way to do a job.  A lot of companies have no idea what the best way is to do a job until they do a labor standards project.  Once identified, companies teach everyone the best methods and measure each person’s productivity.  Associates that are not meeting performance often times are not following the proper procedure.

What is a labor standard?

At this point, you might be wondering “What exactly is a labor standard?”  A labor standard is a formula used to calculate the estimated time it should take to perform a given class of work.  Labor standards are derived from timestudies of associates actually performing the work.  A timestudy is used to calculate statistically valid estimates for each element of a given class of work.  An “element” is a step a worker performs when doing a job.  For example, in picking there is an element for “Scan location” to confirm that the picker picked from the right location. 

You can see an example of how a timestudy looks and is then used here.

Once the timestudy is complete, as a result, we are getting out estimated times for executing a task, for example, 13.29 seconds for every line the user picks and 3.57 seconds for every unit he picks (See above timestudy example for more details).

It is worth noting that in many other systems this formula can only have one variable, e.g.
Estimated Picking Time = 18.26 seconds * # of lines picked
but in AX we can support two variables per standard (One for the Pick and one for the Put).  This means standards implemented in AX in most instances will be more accurate than in these other systems.

Labor Standards in Dynamics AX

So, let's use the results of the timestudy we got above (or one like it), and configure labor standards in AX, and then execute some work and see how the actual time is calculated accordingly

First thing we need to do is choose, which warehouses we want to enable for these calculation (note, that since they are done for every work line created/executed, you should only enable it on those warehouses where you actually need the data).

Labor standards configuration
Allow labor standards on Warehouse

The story behind the example

Our company sells fancy speakers. They are fancy, because they are relatively small, and are usually sold in cases, where one case holds 10 speakers. The orders are typically for 50-60 speakers (or 5-6 cases), because that is how many are installed in the smart houses they are made for. Here's how the product definition looks in AX:

Released product details
Released product details - FancySpeaker

The warehouse workers do case picking, going out to the picking location for the fancy speakers, picking up the 5 cases, stowing them on the pallet on their forklift, and then delivering the pallet for that order to the outbound dock, where it is loaded onto the truck. 
The work template supporting such an operation is shown below:

Work templates
Work template setup

The warehouse worker (John) based on our timestudy needs 36 seconds in total to drive to a selected location (about 20 sec) and then, after picking, to the dock to deliver the goods (about 16 sec). At the picking location, he needs about 9 seconds per case to pick it up from the location and put it on the pallet on the forklift. 

Sammy, the shipping clerk, who does the loading in our company, needs about 7.5 minutes per pallet (with 5 CS on it) that he needs to load on the truck.That is based on the volume of the pallet stacked with cases.

Labor standards configuration

We configure these times in AX using the Labor standards form, available under Setup in Warehouse management module.

Labor standards
Labor standards configuration

This is pretty flexible, allowing for separate configurations based on the Work trans type such as Sales and Purchase orders, Replenishment, etc., and Work classes, meaning that each unique warehouse operation can be timed separately. On top of that you can set up filters per Item/Location, to which the standards should apply. 

All the configuration values are specified in a uniform fashion, as X units per hour. This could be 
  • X transactions per hour (which is equivalent to X locations visited, as each work line corresponds to a specific location), where I specified John needs 36 seconds per location => 3600 sec (1 hour) / 36 seconds = 100;
  • it could also be in specific units, like in my example with 9 seconds per case (3600 sec / 9 CS = 400 CS per hour);
  • it could be based on weight/volume, like in my example, where Sammy loads pallets with approx volume of 0.5 m3 (based on product volume information and typical order quantity) in 5 min each => 6 m3 per hour (see note below);
  • it could also be work units, which could be applicable to my example as well, because we know the order is usually just the 1 pallet, so they would have 1 work per order, and thus we could base the calculation on the number of work orders processed per hour. 
The Work types we can select are Pick, Put, or Pick and put, which is useful for companies who don't want to spend a lot of time measuring each part of the picking process, but want to estimate the time for the entire operation at once. 
Note: I have used the Pick&Put for my Load work class, and have doubled the estimated work per hour (12 m3 instead of 6 m3 I calculated above). That is due to the fact that AX will stamp the individual work lines with the time, virtually doubling the estimated time for the operation.

Walkthrough for estimating the work for a sales order

Our company received an order for 5 cases of FancySpeakers from one of our frequent customers, Forest Wholesales, and don't ask me why they need speakers :).

Sales order with one line
Sales order for FancySpeaker

We have sufficient inventory in our warehouse, so we have accepted the order and are starting the picking process

Inventory on hand
Available on-hand

I will skip the part where we release the sales order to warehouse, add the created shipment to a wave, process and release the wave, thus creating the necessary picking work for the warehouse crew. Here's the work that will be created as a result of these steps:

Work header and lines
Work for FancyProduct order

As you can see on the screenshot above, the Estimated time for each work line has now been calculated, as well as a summed up total on the work header.
Note: This field, along with Actual time, Work in process and Closed work, are available out of the box in the product, but are not by default shown on the Work details screen. I have added them through Personalization on that form.
If we look at the calculations, you can see we are only picking from 1 location, and, as calculated above, it should take John 36 seconds to get there and then to the dock, 45 seconds to actually pick the 5 cases (9 sec per case), and then it should take Sammy 5 minutes (300 sec) to load the 5 cases on the pallet into the truck.

Walkthrough for capturing the actual picking time

In order to execute the work we will use the Warehouse mobile device portal shipped with Dynamics AX 2012 R3. For that, we need all the relevant work user, mobile device menu and menu item setup, etc. We will need to separate menu items, one for the picking operations done by John and one Sammy doing the Loading, which is also clear from my use of 2 separate work classes as part of the work template. Below are the mobile device screens John and Sammy go through. I have omitted the way they got to these. In my system I used user directed and user grouped execution modes, but in your system this could be completely different. If you are not familiar with this configuration, please refer to the following article on TechNet.

Warehouse mobile device portal Sales Pick
Mobile device screens for John
Warehouse mobile device portal Load
Mobile device screens for Sammy
I wanted to show these screens, as it is important in terms of understanding, how the Actual Time when executing work is calculated. 

When the work user sees the first of the two screens, that means that the selected work line with Work type = Pick is now in progress, its state is updated accordingly, along with the timestamp of when work execution started. Confirming the pick by clicking OK (or simply scanning in the required data with an auto-submit) marks the approx. time when the corresponding work line is finished (or, in terms of AX, closed). Same for the second screens in each image, with the only difference being that it is now a Put line.

Depending on the way your warehouse workers go through these screens, you might need to adjust your labor standards configuration to improve accuracy of measurements. 

OK, so now the work has been completed and the speakers have been loaded on the truck. Let's see what the work looks like:

Work header and lines
Work completed for FancySpeaker order

As you can see above, the Work in process and Closed work now contain the time stamps for when the work was started and when it was completed, as well as the calculated value for Actual Time for each of the work lines. The work header is also updated correspondingly. 

Reporting on productivity

Now that we have the standards setup in AX, it will calculate estimated times for each work line for each work class with a standard. Now you will want to start reporting on work user productivity. To do that, we need to introduce two more topics: employee efficiency and employee utilization.  Employee utilization measures the time that an associate was working. We can’t really see this in AX but we can estimate it. It probably is equal to the time they were on the clock minus two breaks of 15 minutes each and some time at the start and end of their shift.

Time on the Clock480minutes
Start of Shift-8
Break 1-15
Break 2-15
Personal Time-12
End of Shift-8
Off Standard0
422minutes

This would suggest that most employees are about 87% utilized on most days if a warehouse is well run. At some sites the employees are given cards to fill out when they don’t have anything to do or have to do something that doesn't have a standard and supervisors will subtract this “off standard” time from the available minutes to work when calculating an associate’s performance.

To report on associate productivity we can print a simple report that shows the total actual (whether that is captured by the system or taken based on worker utilization) and estimated minutes associated with each work user (for example, on a given day). We can then export this to Excel, and divide the Estimated time * 100 / Actual time, getting the worker efficiency in %.

Reports available out of the box in Dynamics AX

In Dynamics AX 2012 R3 there are 2 simple reports that can be printed, that show two different slices of the captured execution time data.

SSRS Report: Labor by work transaction type
Labor by work transaction type
Labor by work transaction type could be interesting for warehouse managers when investigating inefficiencies in certain warehouse processes. They could for example see that as of this week, it takes much longer to complete picks for transfer orders and dig deeper to figure out the problem with the process.

SSRS Report: Labor by user
Labor by user
Labor by user can help us measure worker productivity, as described above. As we can see from the above report printout (of course, it is only for 1 work order, so is not really sufficient for such measurements), John needed a bit of extra time to perform his picks, which earns him an efficiency score of 1.35min/1.42min = 95%. Sammy was on the mark, getting 5.00min/4.98min = 100.4%.

Assuming that the defined standards are good, the efficiency should be around 90% to 100%. Sometimes some associates can earn over a 100%, but scores over 120% should be scrutinized because usually it is impossible for a human to outperform a well-designed standard by more than 120 to 130%. More likely if the number is low like 70% or even 50%, it usually points to a problem in employee utilization. The associates are not working all the time or there are too many people to keep busy all the time. Labor standards thus lead to changes in supervisor behavior as much as they do associate behavior. Supervisors have to run a much tighter ship under labor standards.

Conclusion

Labor standards are one of the best ways that AX can really improve a warehouse. It is in every warehouse manager’s interest to investigate using them. Even if your workforce will not accept being measured, you still could get great value from standards. While a popular opinion is that labor standards drive workers to “sweat more” the reality is the improvements in productivity rarely come about from driving up worker pace. The improvements which can be 20 to 30% or more usually come about from teaching supervisors how many people are needed to get a certain amount of work complete and in teaching associates the best way to do a job.

Try them out!

Monday, February 16, 2015

Microsoft Dynamics Salary Survey 2015

Participate in the survey
Win one of these cool prizes!

Find out How Your Salary Compares to Others in the Industry. Take Microsoft Dynamics Salary Survey 2014-2015


Take part in Nigel Frank’s industry acclaimed annual Microsoft Dynamics Salary Survey and find out latest Dynamics compensation trends. You will automatically be entered into a prize draw to win a Microsoft Surface Pro 3, a Nokia Lumia 1520 or an Xbox One.




Once the Salary Survey report has been compiled, you'll be one of the first to receive it via email. The results of the survey give an unparalleled insight into global salary trends for Dynamics professionals. It will allow you to benchmark your team’s, company’s and your own salary against your peers. 

Saturday, January 17, 2015

Tutorial: InventDimCtrl_Frm classes and the amazing things you can do with their help

Preamble

I was making some adjustments to one of the forms in the application today. Specifically, it was the PdsCustSellableDays form, which is a setup form you can open for a selected customer through the Sell\Setup\Sellable Days action pane button. You can read more about this functionality here:

Working on these changes brought back the memory of what it is that I have always enjoyed most about developing customizations in AX: Making a lot of difference with only a few small targeted code changes.

So I was inspired to use this as an example for describing some of the capabilities of the InventDimCtrl class hierarchy. There are a lot of classes in the hierarchy, targeted to various forms in the application that display Inventory dimensions one way or the other. The main purpose of this class hierarchy is to control the display and behavior of the inventory dimensions as well as the “Dimension display” button that is normally also present on such forms.

Most of you probably know that the way AX developers early on decided to handle inventory dimensions is through a “reference” table InventDim, and a lot of generically written code that would heavily exercise reflection and traversing form controls. This seemed like a good idea at that time, I bet, but over the years, in my opinion, it got completely out of control, and now is a pretty large overhead for almost all inventory related operations in our product. But, at the same time, it provides a nice level of flexibility to how dimensions are displayed on a particular form.

That being said, let’s start from the beginning and briefly explain the existing form behavior and the things I was set to change in it.

Existing behavior and task

Overview of the existing behavior

  • Only product dimensions are displayed in the main grid
  • The relationship to the products is implemented via a “standard” pattern of TableGroupAll. So you can set up Sellable days for a specific item, an item group, or all items.
  • Viewing Inventory\Dimension display shows the standard dialog with all inventory dimensions. However, storage and tracking dimension check-boxes are disabled for editing.
  • Upon adding a grid row and selecting a released product master, all product dimension values are shown, not just those from the released product variants.
  • Only the product dimensions active for the selected product are enabled for editing. If Item code is set to Group or All, all the dimensions are disabled for editing.

Original version of the form

Task

  • Storage and tracking dimensions cannot be selected in Dimensions Display, so should not be shown at all
  • Lookup for configuration and other product dimensions should only show those relevant for the released product master.

 

Explanation

OK, so let’s take a closer look at how such types of forms are built

  • A form-level variable of type InventDimCtrl_Frm is declared. This object is used to control the behavior of the inventory dimension controls on the form.
  • A form method called inventDimSetupObject simply returns the above reference. Through this method other AX forms and classes can access the current state of the form when it comes to the dimensions
    • This includes information about which inventory dimensions are visible in the grid, which of them are enabled for editing, which of them should be marked as mandatory, which of them should be shown when the Dimension Display button is pressed, whether or not the “large” version of the dialog is to be shown (that includes On-hand information like Closed/ClosedQty), whether Item number should be shown on the Dimension Display dialog, etc.You can browse the methods on this class to get a feeling for what else it can do.
    • Since we can only select product dimensions in the form, we want spread that over to the Dimension Display dialog as well.
  • A form method called updateDesign() exists, with a certain pattern for handling various “events” that happen on the form. The main purpose here is to update the inventDimSetupObject with the latest information from the form, and through it refresh the display of the inventory dimension controls.
    • In the PdsCustSellableDays form this happens when the form is opened (Init), whenever the item code or reference is changed (FieldChange), whenever the active record is changed (Active) or whenever the parent customer record is changed in turn refreshing the Sellable Days view (LinkActive).
      • This method is then called from the appropriate event handles or the form, as you can see if you open the form code.
    • In PdsCustSellableDays the intention is to only enable the active product dimensions for the selected item, and disable all of them if we selected all items or an item group instead. But only product dimensions are to be shown ever. This is achieved by using the following methods:
      • parmDimParmEnabled(InventDimParm) controls which dimensions can be edited
      • parmDimParmVisible(InventDimParm) controls which dimensions are shown
      • parmDimParmVisibleGrid(InventDimParm) controls which dimensions are shown in the grid.
      • parmDimParmLockedRightClick(InventDimParm) controls which dimensions can be selected through the Dimension Display button
      • formSetControls(boolean) actually executes the update of the inventory dimension controls based on the above settings. The first parameter controls whether the form is locked for the duration of the update (preventing unnecessary flicker, especially in cases where controls are being made visible/invisible in the grid).
      • formActiveSetup(InventDimGroupSetup), which is not used here, but is frequent in other forms, should be called when the active record is changed, and resets the enabled fields based on the now selected item dimension group information.
      • dimFieldsActive(InventDimParm) that is also used in the form is actually an inquiry-type method, and does not impact the state of the object in any way. Its purpose is to return a container of active dimension field IDs based on an InventDimParm buffer.
  • Lookups for all the product dimensions are based on the form called InventProductDimensionLookup, which is another example of generic handling of inventory dimensions, and has all the above properties as well.
    • It can show up to 3 tab pages, including the selected product dimension values, all relevant product variants and the on-hand information
    • In the current version of the form, all existing product dimension values are shown, regardless of the product. This is one of the things we need to fix.

 

Changes made and result

OK, so in order to accomplish the 2 tasks we set for ourselves above, we only need to change code in 3 places on the form. Let’s walk through each one in turn.

  • Modify the classDeclaration, and replace the type InventDimCtrl_Frm with InventDimCtrl_Frm_ProductDim. If we take a look at this class (it is only available starting from AX 2012 R3 CU8, so I am also including the code for this class below, for reference), we can see a few methods were overridden:
    • mustEnableField() makes sure that only the active product dimensions based on dimension group setup are enabled. This is used by the formActiveSetup method, and is not as interesting for our example.
    • mustShowGridField() makes sure that all product dimensions are shown in the grid, and only product dimensions. This was controlled by explicitly calling parmDimParmVisibleGrid() before. We’ll remove that code, as you’ll see later.
    • setupShowAllProductDimensions() – this method is important for our example. This method, if it returns true, will ensure only product dimensions are shown on the Dimension Display dialog.
  • Modify the updateDesign method as below
    • Remove the lock/unlock method calls, since this is already going to happen within formSetControls().
    • Initialize the inventDimSetupObject using the right class InventDimCtrl_Frm_ProductDim
    • Remove the variables inventDimParmDefault and inventDimParmDisenabled, and all code related to them. This code is now redundant.
    • Remove the call to dimFieldsActive. As I mentioned above, it does not do anything useful for us.
    • We can also add 1 extra line clearing the inventDim values that do not apply – this is important for when changing the item number where we already have product dimensions selected. Just a bug fix, not really related to this tutorial.
  • Add a new method itemId(), which returns the currently selected item number
    • This is necessary for the product dimension lookups to function properly. The lookup tries to find the item information from the calling form through the data source joined to InventDim as a parent, assuming it has an ItemId field. While that is true in most cases, our form uses a TableGroupAll pattern, so the field is a bit different. By creating an itemId() method on the form, we ensure that the lookup can find the right item number to reference when retrieving product dimension group information.
    • In updateDesign, after initializing the object, invoke parmSkipOnHandLookUp(true), which will ensure that the on-hand tab page is not shown on the lookups. Since this is not necessary for this type of setup form, it’s a good idea, since it will improve the lookup loading time. This is also demonstrating how the lookup form uses the inventDimSetupObject of the calling form to get the expected behavior settings.
    • The lookup will now also show the Combinations tab page, if the selected product has more than 1 product dimension active.

That’s it. With very few changes, we were able to drastically change the look and feel of this form. Here’s how it looks now:

Updated form behavior

 

Download the project

You can download the project with the changes I have made from my OneDrive.