WMSI/WMS2 item migration

Introduction

This blog describes the new capabilities that allow you to migrate existing items with open inventory transactions so they can use the new storage dimensions. This can be needed when you upgrade from older versions of Microsoft Dynamics 365 for Finance and Operations that supported the pallet dimension or if you want to use the Warehouse management functionality for existing items that were previously using WMS1 processes.

Typical migration scenarios:

  • You have existing items with the location dimensions enabled.
  • You have existing items with the location dimensions enabled and the pallet dimensions active.
  • You have existing items with the location enabled which are catch-weight items.

The goal when migrating items to use warehouse management enabled processes

The following setup is required to use an item in warehouse management processes:

  1. The item must use a storage dimension group that is set up to use warehouse management processes which means that the Inventory status dimension, the location and the license plate must be active.
  2. A reservation hierarchy must be assigned.
  3. A unit sequence group must be assigned.

The goal of the migration is to enable the items to meet the above  criteria and ensure that all related data is consistent with the items’ new settings allowing business processes to proceed after the migration.

 

High-level overview of the process

The upgrade process – blocking items

If you upgrade from a version that supported pallets the upgrade will identify the items that had the pallet dimension active and create a record in a new table called InventUpdateBlockedItem. This is done to block the items from all inventory processes because the item is configured using unsupported settings.

Items that are blocked must be migrated. The blocked items can be viewed in the Items blocked for inventory updates form.

The migration cockpit Change storage dimension group for items

The migration cockpit is simple. It allows you to enter the item ID of the items you want to be migrated and the new storage dimension group. If the item is to be downgraded from a group with the pallet dimension active, this is all you need.

If the item should be converted to use warehouse management enabled processes, you need to assign a reservation hierarchy and a unit sequence group.

The illustration below is a screenshot of the form.

Using entities to populate the data

You can use both OData which allows you to use Excel directly and data management to import and export the data.

OData

You can use the OData approach by clicking the Open in Microsoft Office icon and export the data to Excel. Once the information has been entered, the changes can be synchronized back.

Data management

For larger datasets, data management is the most effective. The entity is called Item storage dimension group change request and follows normal data management patterns.

 

Validation

Before the migration is started, the validation should be performed to ensure that the data is ready for migration. Several conditions are validated:

  • A default inventory status value must be defined
  • Inventory on-hand on pallets must not exist on non-license-plate-tracked locations
  • Inventory on-hand without pallets must not exists on license-plate-tracked locations
  • That the combination of the selected storage dimension group and reservation hierarchy is valid
  • If migrating to use warehouse management processes, the item cannot be enabled with catch weight

 

The list is not exhaustive, but it covers the most important validation points.

Starting the migration

The migration is started by clicking the Process changes button.

The migration supports parallel processing for parts of the process. The batch framework is used and the different steps will be handled by different batch tasks.

The recommendation is to set the Recommended number of parallel tasks field to two times the number of cores that are available. It is recommended to have a dedicated batch server for the migration since the migration is a heavy process due to the updates to the inventory dimensions.

Un-supported scenarios

Catch weight enabled items:  Catch weight enabled items are not supported in the new WHS. If such items exist, and they have the pallet dimension active, they will need to be downgraded to a dimensions group where only the location is active., Otherwise an ISV solution should be used.

Reserve Ordered transactions: Because of the way reservations work for warehouse management enabled items there are certain constraints on the state of the inventory transactions. If there are Reserved ordered transactions with a “hole” in the dimensions, the item cannot be converted.

A “hole” could exist for an item that has the batch dimension active and has the batch placed below the location in the reservation hierarchy. If a reservation exists on site, warehouse, batch, then the location is missing, which is what we refer to as a “hole”. This is not supported.

The mitigation is to either assign the missing dimensions or clear the dimensions so the “hole” is removed.

Customization that involves inventory dimensions

If you have customizations related to inventory dimensions that fall in one of these two categories:

  • New inventory dimensions field on existing table
  • New table with and inventory dimensions

You need to do something before you can use the migration process. Otherwise you will get an error like the one below:

Since a new inventory dimensions field exists, the system needs to know what actions should be taken for the dimensions in the table. This is done by implementing an event handler.

Three situations where an inventory dimension field is added:

  1. You do not want the data updated
  2. You want the data updated and the table has the itemId on it
  3. You want the data updated but the table does not have the itemId on it

 

In the below examples we will assume that we have added a table looking like this:

Handling situation 1 and 2

The below eventhandler illustrates how an eventhandler for situation 1 and 2 can be implemented. Here we want to update the dimensions related to the InventDimIdAllDimensions but not the dimensions in InventDimIdOnlyProductDimensions

[SubscribesTo(classStr(InventItemInventoryDimensionConversionTaskInitiator), delegateStr(InventItemInventoryDimensionConversionTaskInitiator, tableWithInventDimIdDiscoveredDelegate))]
    public static void tableWithInventDimIdDiscoveredStorageConversionDelegateHandler(
        TableId _updateTableTableId,
        FieldId _inventDimIdFieldId,
        InventItemInventoryDimensionConversionType _conversionType,
        EventHandlerResult _result)
    {
        if (_updateTableTableId == tableNum(MyOwnTableWithInventDimId))        {
            
            if (_inventDimIdFieldId == fieldNum(MyOwnTableWithInventDimId, InventDimIdAllDimensions))
            {
                InventItemInventoryDimensionConversionTaskCreator creator = InventItemInventoryDimensionConversionTaskCreator::newStorageConversion();
                creator.createTasksForTableWithItemId(
                    _updateTableTableId, //tableId of the table that should be updated
                    _inventDimIdFieldId, //fieldId of the inventDimId field that should be updated
                    fieldNum(MyOwnTableWithInventDimId, ItemId),  //fieldId of ItemId field
                    fieldNum(MyOwnTableWithInventDimId, DataAreaId)); //field id of DataAreaId
                _result.booleanResult(true); //we need to update
            }
            else if (_inventDimIdFieldId == fieldNum(MyOwnTableWithInventDimId, InventDimIdOnlyProductDimensions))
            {
                _result.booleanResult(false); //no update needed
            }
        }

Handling situation 3

Situation 3 is different since the itemId is on a different table. Here we need to write code specifically to the datamodel for the involved tables. The best approach is to follow the existing examples in the code. The InventBatchJournalResult table is a good example. This table is more complex because it has an InventDimId, but the itemId is on the inventBatchJournal table. The code below shows how this scenario is handled.

public class InventItemInventDimConversionInventBatchJournalLinePopulationTaskProcessor implements InventItemInventoryDimensionConversionITaskProcessor
{
    public boolean process(InventItemInventoryDimensionConversionTask _conversionTask)
    {
        var queryBuilder = InventItemInventoryDimensionChangePopulatorItemIdTableJoinedQueryBuilder::newFromParameters(
            _conversionTask.UpdateTableName,
            _conversionTask.InventDimIdFieldId,
            _conversionTask.DataAreaIdFieldId,
            tableStr(InventBatchJournal),
            fieldNum(InventBatchJournal, ItemId));

        InventItemInventoryDimensionChangePopulator::newFromQueryBuilder(queryBuilder).populateDimensionChanges();

        return true;
    }

}

Additional information

You can find more information as part of the product documentation:

https://docs.microsoft.com/en-us/dynamics365/unified-operations/supply-chain/warehousing/upgrade-migration-warehouse-management-processes