Monday, March 18, 2013

Microsoft Dynamics AX 2012 R2 AIF – Routes Web Service

Microsoft Dynamics AX 2012 R2 AIF – Routes Web Service
 
Purpose: The purpose of this document is to illustrate how to use standard Routes Web Service in Microsoft Dynamics AX 2012 R2.
 
Challenge: Data model changes in Microsoft Dynamics AX 2012 related to high normalization and introduction of surrogate keys made some imports more complex. The data model implementing Routes business entity was significantly changed and now more tables are involved (example of resource requirements tables expansion). Microsoft Dynamics AX 2012 R2 ships with standard Routes Web Service which can be used for Routes import. In fact there're also certain nuances related to number sequences, inventory dimensions, approval and activation processes, etc. when you consume standard Routes Web Service in Microsoft Dynamics AX 2012 R2.
 
Solution: Microsoft Dynamics AX 2012 R2 ships with standard Routes Web Service which I'll use for import of Routes.
 
Assumption: Route operations and other required reference data is already created.
 
Data Model:
 
Table Name
Table Description
RouteVersion
The RouteVersion table contains versions for the routes defined in the RouteTable table. A route version connects to a route and additionally has parameters that define whether the route version is valid from a certain date or for a certain quantity. A route version needs to be approved to be used. Therefore, each route version record is started and approved by the property that is stored.
RouteTable
The RouteTable table contains routes, which in turn contain information that is required to guide an item through production. Each route includes operations to perform, the sequence for the operations, resources involved in completing the operations, and standard times for the setup and run of the production.
Route
The Route table contains the operations that represent the routes. Each record contains information about an operation that belongs to a route. Each operation connects to an operation stored in the RouteOprTable table.
RouteOpr
The RouteOpr table contains route operation relations. Each relation describes the parameters that will be used for a specific entity.
WrkCtrRouteOprActivity
The WrkCtrRouteOprActivity table works as an interface to the RouteOpr table.
WrkCtrActivity
The WrkCtrActivity table is as an interface to activities that are defined in other tables.
WrkCtrActivityRequirementSet
The WrkCtrActivityRequirementSet table contains the requirements that are needed for a single activity.
WrkCtrActivityRequirement
The WrkCtrActivityRequirement table is a supertype for activity requirements.
WrkCtrActivityCapabilityRequirement
The WrkCtrActivityCapabilityRequirement table represents the requirements for a capability that is needed for a single activity.
WrkCtrActivityCourseRequirement
The WrkCtrActivityCourseRequirement table represents the requirements for a course possessed by a resource needed for a single activity.
WrkCtrActivityPersonTitleRequirement
The WrkCtrActivityPersonTitleRequirement table represents the requirements for a specific employee title that a resource must have to perform a single activity.
WrkCtrActivityCertificateRequirement
The WrkCtrActivityCertificateRequirement table represents the requirements for a certificate that is passed by a resource that is needed for a single activity.
WrkCtrActivitySkillRequirement
The WrkCtrActivitySkillRequirement table represents the requirements for a skill possessed by a resource needed for a single activity.
WrkCtrActivityResourceRequirement
The WrkCtrActivityResourceRequirement table represents the requirements for a named resource needed for a single activity.
WrkCtrActivityResourceGroupRequirement
The WrkCtrActivityResourceGroupRequirement table represents the requirements for a resource group that a resource must be a member of in order to perform a single activity.
WrkCtrActivityResourceTypeRequirement
The WrkCtrActivityResourceTypeRequirement table represents the requirements for a resource type that is needed to perform a single activity.
InventDim
The InventDim table contains values for inventory dimensions.
ProdRoute
The ProdRoute table contains production route information. The production route is a list of operations that have to be performed to produce an item in production. When you create a production order for a selected item, a copy of the route for that item is automatically placed in the ProdRoute table. If the item does not have a route, and no route number is assigned when the production order is created, the production route will still be created but it will be blank.
 
Data Model Diagram:
 
<![if !vml]><![endif]>
 
Walkthrough:
 
RoutesService Web Service
 
 
AxdRoutes Query
 
Please note that AxdRoutes Query is used by RoutesService Web Service
 
Production Route Number number sequence
 
Please note that I checked 2 checkboxes in Allow user changes section (To a lower number [V] and To a higher number [V]). Doing so will allow to explicitly assign RouteId from integration client when consuming custom Routes Web Service and at the same time for business users using Microsoft Dynamics AX 2012 UI the system will still automatically assign RouteId from number sequence
 
RoutesService Web Service namespace
 
 
Service group: AlexManufacturingServices
 

Please note that I created a new Service group "AlexManufacturingServices" and included standard Bills of Materials Web Service into this Service group
 
Inbound port: AlexManufacturingServices
 
Please note that Basic Inbound port will be automatically created and activated after you deploy Service group
 
Service reference: AlexManufacturingServices
 
In integration client (.NET Console application) I'll add Service reference using WSDL URI
 
Integration client – Source code
 
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
 
using ConsoleApplication1.ServiceReference1;
 
namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                const string RouteId = "Route_ALEXRTE1";
 
                RoutesServiceClient client = new RoutesServiceClient();
                CallContext context = new CallContext();
 
                AxdRoutes Route = new AxdRoutes();
 
                Route.RouteVersion = new AxdEntity_RouteVersion[1];
                Route.RouteVersion[0] = new AxdEntity_RouteVersion();
 
                Route.RouteVersion[0].RouteId = RouteId;
                Route.RouteVersion[0].Name = "ALEXRTE";
                Route.RouteVersion[0].ItemId = "ALEXPRODUCT";
                //Route.RouteVersion[0].InventDimId = "Ax#1";
                Route.RouteVersion[0].InventDim = new AxdEntity_InventDim[1];
                Route.RouteVersion[0].InventDim[0] = new AxdEntity_InventDim();
                Route.RouteVersion[0].InventDim[0].InventSiteId = "1";
 
                Route.RouteVersion[0].RouteTable = new AxdEntity_RouteTable[1];
                Route.RouteVersion[0].RouteTable[0] = new AxdEntity_RouteTable();
                Route.RouteVersion[0].RouteTable[0].RouteId = RouteId;
                Route.RouteVersion[0].RouteTable[0].Name = "ALEXRTE";
 
                Route.RouteVersion[0].RouteTable[0].Route = new AxdEntity_Route[1];
                Route.RouteVersion[0].RouteTable[0].Route[0] = new AxdEntity_Route();
                Route.RouteVersion[0].RouteTable[0].Route[0].RouteId = RouteId;
                Route.RouteVersion[0].RouteTable[0].Route[0].OprId = "ALEXOPR";
                Route.RouteVersion[0].RouteTable[0].Route[0].OprNum = 10;
                Route.RouteVersion[0].RouteTable[0].Route[0].OprNumNext = 0;
                Route.RouteVersion[0].RouteTable[0].Route[0].OprNumNextSpecified = true;
 
                Route.RouteVersion[0].RouteTable[0].Route[0].RouteOpr = new AxdEntity_RouteOpr[1];
                Route.RouteVersion[0].RouteTable[0].Route[0].RouteOpr[0] = new AxdEntity_RouteOpr();               
                Route.RouteVersion[0].RouteTable[0].Route[0].RouteOpr[0].OprId = "ALEXOPR";                 
                Route.RouteVersion[0].RouteTable[0].Route[0].RouteOpr[0].RouteGroupId = "Proc";
                Route.RouteVersion[0].RouteTable[0].Route[0].RouteOpr[0].ProcessTime = 1;
                Route.RouteVersion[0].RouteTable[0].Route[0].RouteOpr[0].ProcessTimeSpecified = true;               
                Route.RouteVersion[0].RouteTable[0].Route[0].RouteOpr[0].SetUpCategoryId = "Assembly";
                Route.RouteVersion[0].RouteTable[0].Route[0].RouteOpr[0].QtyCategoryId = "Assembly";
                Route.RouteVersion[0].RouteTable[0].Route[0].RouteOpr[0].ProcessCategoryId = "Assembly";               
                
                Route.RouteVersion[0].RouteTable[0].Route[0].RouteOpr[0].WrkCtrRouteOprActivity = new AxdEntity_WrkCtrRouteOprActivity[1];
                Route.RouteVersion[0].RouteTable[0].Route[0].RouteOpr[0].WrkCtrRouteOprActivity[0] = new AxdEntity_WrkCtrRouteOprActivity();               
 
                Route.RouteVersion[0].RouteTable[0].Route[0].RouteOpr[0].WrkCtrRouteOprActivity[0].WrkCtrActivity = new AxdEntity_WrkCtrActivity[1];
                Route.RouteVersion[0].RouteTable[0].Route[0].RouteOpr[0].WrkCtrRouteOprActivity[0].WrkCtrActivity[0] = new AxdEntity_WrkCtrActivity();              
 
                Route.RouteVersion[0].RouteTable[0].Route[0].RouteOpr[0].WrkCtrRouteOprActivity[0].WrkCtrActivity[0].ActivityRequirementSet = new AxdEntity_ActivityRequirementSet[1];
                Route.RouteVersion[0].RouteTable[0].Route[0].RouteOpr[0].WrkCtrRouteOprActivity[0].WrkCtrActivity[0].ActivityRequirementSet[0] = new AxdEntity_ActivityRequirementSet();               
 
                Route.RouteVersion[0].RouteTable[0].Route[0].RouteOpr[0].WrkCtrRouteOprActivity[0].WrkCtrActivity[0].ActivityRequirementSet[0].ActivityRequirement = new AxdEntity_ActivityRequirement[1];
                Route.RouteVersion[0].RouteTable[0].Route[0].RouteOpr[0].WrkCtrRouteOprActivity[0].WrkCtrActivity[0].ActivityRequirementSet[0].ActivityRequirement[0] = new AxdEntity_ActivityRequirement();
                Route.RouteVersion[0].RouteTable[0].Route[0].RouteOpr[0].WrkCtrRouteOprActivity[0].WrkCtrActivity[0].ActivityRequirementSet[0].ActivityRequirement[0].RelationshipType = AxdEnum_WrkCtrActivityRequirementType.ResourceType;
               
                Route.RouteVersion[0].RouteTable[0].Route[0].RouteOpr[0].WrkCtrRouteOprActivity[0].WrkCtrActivity[0].ActivityRequirementSet[0].ActivityRequirement[0].ResourceTypeRequirement = new AxdEntity_ResourceTypeRequirement[1];
                Route.RouteVersion[0].RouteTable[0].Route[0].RouteOpr[0].WrkCtrRouteOprActivity[0].WrkCtrActivity[0].ActivityRequirementSet[0].ActivityRequirement[0].ResourceTypeRequirement[0] = new AxdEntity_ResourceTypeRequirement();
                Route.RouteVersion[0].RouteTable[0].Route[0].RouteOpr[0].WrkCtrRouteOprActivity[0].WrkCtrActivity[0].ActivityRequirementSet[0].ActivityRequirement[0].ResourceTypeRequirement[0].ResourceType = AxdEnum_WrkCtrType.Personnel;
                Route.RouteVersion[0].RouteTable[0].Route[0].RouteOpr[0].WrkCtrRouteOprActivity[0].WrkCtrActivity[0].ActivityRequirementSet[0].ActivityRequirement[0].ResourceTypeRequirement[0].ResourceTypeSpecified = true;
               
                EntityKey[] entityKey = client.create(context, Route);
 
                Console.WriteLine("Done!");
                Console.ReadLine();
            }
            catch (Exception ex)
            {
                Console.WriteLine("Error!" + ex.Message);
                Console.ReadLine();
            }
        }
    }
}
 
XML Request
 
<?xml version="1.0" encoding="UTF-8"?>
<RoutesServiceCreateRequest xmlns="http://tempuri.org">
  <Routes xmlns="http://schemas.microsoft.com/dynamics/2008/01/documents/Routes">
    <SenderId xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"></SenderId>
    <RouteVersion class="entity">
      <_DocumentHash xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"></_DocumentHash>
      <Approver xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"></Approver>
      <InventDimId xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"></InventDimId>
      <ItemId>ALEXPRODUCT</ItemId>
      <Name>ALEXRTE</Name>
      <RouteId>Route_ALEXRTE1</RouteId>
      <RouteTable class="entity">
        <Approver xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"></Approver>
        <ItemGroupId xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"></ItemGroupId>
        <Name>ALEXRTE</Name>
        <RouteId>Route_ALEXRTE1</RouteId>
        <Route class="entity">
          <OprId>ALEXOPR</OprId>
          <OprNum>10</OprNum>
          <OprNumNext>0</OprNumNext>
          <RouteId>Route_ALEXRTE1</RouteId>
          <RouteOpr class="entity">
            <ConfigId xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"></ConfigId>
            <ItemRelation xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"></ItemRelation>
            <OprId>ALEXOPR</OprId>
            <ProcessCategoryId>Assembly</ProcessCategoryId>
            <ProcessTime>1</ProcessTime>
            <PropertyId xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"></PropertyId>
            <QtyCategoryId>Assembly</QtyCategoryId>
            <RouteGroupId>Proc</RouteGroupId>
            <RouteRelation xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"></RouteRelation>
            <SetUpCategoryId>Assembly</SetUpCategoryId>
            <SiteId xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"></SiteId>
            <WrkCtrIdCost xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"></WrkCtrIdCost>
            <WrkCtrRouteOprActivity class="entity">
              <WrkCtrActivity class="entity">
                <EntityType>ProdRoute</EntityType>
                <ActivityRequirementSet class="entity">
                  <Description xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"></Description>
                  <LoadPercent>0</LoadPercent>
                  <Quantity>0</Quantity>
                  <ActivityRequirement class="entity">
                    <RelationshipType>ResourceType</RelationshipType>
                    <ResourceTypeRequirement class="entity">
                      <ResourceType>Personnel</ResourceType>
                    </ResourceTypeRequirement>
                  </ActivityRequirement>
                </ActivityRequirementSet>
              </WrkCtrActivity>
            </WrkCtrRouteOprActivity>
          </RouteOpr>
        </Route>
      </RouteTable>
      <InventDim class="entity">
        <ConfigId xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"></ConfigId>
        <InventBatchId xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"></InventBatchId>
        <InventColorId xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"></InventColorId>
        <InventDimId xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"></InventDimId>
        <InventGTDId_RU xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"></InventGTDId_RU>
        <InventLocationId xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"></InventLocationId>
        <InventOwnerId_RU xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"></InventOwnerId_RU>
        <InventProfileId_RU xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"></InventProfileId_RU>
        <InventSerialId xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"></InventSerialId>
        <InventSiteId>1</InventSiteId>
        <InventSizeId xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"></InventSizeId>
        <InventStyleId xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"></InventStyleId>
        <WMSLocationId xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"></WMSLocationId>
        <WMSPalletId xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"></WMSPalletId>
      </InventDim>
    </RouteVersion>
  </Routes>
</RoutesServiceCreateRequest>
 
XML Response
 
<?xml version="1.0" encoding="UTF-8"?>
<RoutesServiceCreateResponse xmlns="http://tempuri.org">
  <EntityKeyList xmlns="http://schemas.microsoft.com/dynamics/2006/02/documents/EntityKeyList">
    <EntityKey xmlns="http://schemas.microsoft.com/dynamics/2006/02/documents/EntityKey">
      <KeyData>
        <KeyField>
          <Field>RecId</Field>
          <Value>22565424703</Value>
        </KeyField>
      </KeyData>
    </EntityKey>
  </EntityKeyList>
</RoutesServiceCreateResponse>
 
AxdRoutes.prepareForSaveExtended Method – Source code
 
public boolean prepareForSaveExtended(
    AxdStack                    _axBcStack,
    str                         _dataSourceName,
    AxdRecordProcessingContext  _recordProcessingContext,
    AxInternalBase              _childRecord)
{
    AxInternalBase                           parentAxBC;
    AxRouteVersion                           axbc_RouteVersion;
    AxRouteTable                             axbc_RouteTable;
    AxRoute                                  axbc_Route;
    AxRouteOpr                               axbc_RouteOpr;
    AxWrkCtrRouteOprActivity                 axbc_WrkCtrRouteOprActivity;
    AxWrkCtrActivity                         axbc_WrkCtrActivity;
    AxWrkCtrActivityRequirementSet           axbc_ActivityRequirementSet;
    AxWrkCtrActivityRequirement              axbc_ActivityRequirement;
    AxWrkCtrActivityCapabilityRequirement    axbc_CapabilityRequirement;
    AxWrkCtrCapability                       axbc_WrkCtrCapability;
    AxWrkCtrActivityCourseRequirement        axbc_CourseRequirement;
    AxWrkCtrActivityPersonTitleRequirement   axbc_PersonTitleRequirement;
    AxWrkCtrActivityCertificateRequirement   axbc_CertificateRequirement;
    AxWrkCtrActivitySkillRequirement         axbc_SkillRequirement;
    AxWrkCtrActivityResourceRequirement      axbc_ResourceRequirement;
    AxWrkCtrActivityResourceGroupRequirement axbc_ResourceGroupRequirement;
    AxWrkCtrActivityResourceTypeRequirement  axbc_ResourceTypeRequirement;
    AxInventDim                              axbc_InventDim;
 
    WrkCtrRouteOprActivity                   wrkCtrRouteOprActivity;
    WrkCtrActivity                           wrkCtrActivity;
    WrkCtrActivityRequirementSet             wrkCtrActivityRequirementSet;
    WrkCtrCapability                         wrkCtrCapability;
    switch (_dataSourceName)
    {
        // ----------------------------------------------------------------------
        // Process RouteVersion records
        // ----------------------------------------------------------------------
        case #RouteVersion_DataSourceName:
 
            axbc_RouteVersion = _axBcStack.top();
 
            switch (_recordProcessingContext)
            {
                // Propagate children's keys
                case AxdRecordProcessingContext::AfterChildRecordProcessed:
                    switch (_childRecord.dataSourceName())
                    {
                        case #InventDim_DataSourceName:
                            axbc_InventDim = _childRecord;
                            axbc_RouteVersion.parmInventDimId(axbc_InventDim.parmInventDimId());
                            break;
                        case #RouteTable_DataSourceName:
                            axbc_RouteTable = _childRecord;
                            axbc_RouteVersion.parmRouteId(axbc_RouteTable.parmRouteId());
                            break;
                    }
 
                    return false;
 
                // Ensure RouteVersion record is saved
                case AxdRecordProcessingContext::AfterAllChildRecordsProcessed:
                    if (!axbc_RouteVersion.isProcessed())
                    {
                        return true;
                    }
 
                    return false;
            }
 
            return false;
 
        // ----------------------------------------------------------------------
        // Process RouteTable records
        // ----------------------------------------------------------------------
        case #RouteTable_DataSourceName:
 
            axbc_RouteTable = _axBcStack.top();
 
            switch (_recordProcessingContext)
            {
                // Ensure RouteTable record is saved before any of its dependent child records
                case AxdRecordProcessingContext::BeforeChildRecordProcessed:
                    switch (_childRecord.dataSourceName())
                    {
                        case #Route_DataSourceName:
                            if (!axbc_RouteTable.isProcessed())
                            {
                                return true;
                            }
                            break;
                    }
 
                    return false;
 
                // Ensure RouteTable record is saved
                case AxdRecordProcessingContext::AfterAllChildRecordsProcessed:
                    if (!axbc_RouteTable.isProcessed())
                    {
                        return true;
                    }
 
                    return false;
            }
 
            return false;
 
        // ----------------------------------------------------------------------
        // Process Route records
        // ----------------------------------------------------------------------
        case #Route_DataSourceName:
 
            axbc_Route = _axBcStack.top();
 
            switch (_recordProcessingContext)
            {
                // Propagate parent's key
                case AxdRecordProcessingContext::BeforeAnyChildRecordsProcessed:
                    axbc_RouteTable = axbc_Route.parentAxBC();
                    axbc_Route.parmRouteId(axbc_RouteTable.parmRouteId());
                    break;
 
                // Ensure Route record is saved
               case AxdRecordProcessingContext::AfterAllChildRecordsProcessed:
                    if (!axbc_Route.isProcessed())
                    {
                        return true;
                    }
                    break;
            }
 
            return false;
 
        // ----------------------------------------------------------------------
        // Process RouteOpr records
        // ----------------------------------------------------------------------
        case #RouteOpr_DataSourceName:
 
            axbc_RouteOpr = _axBcStack.top();
 
            switch (_recordProcessingContext)
            {
                // Ensure RouteOpr record is saved
                case AxdRecordProcessingContext::BeforeAnyChildRecordsProcessed:
                    if (!axbc_RouteOpr.isProcessed())
                    {
                        return true;
                    }
 
                    break;
            }
 
            return false;
 
        // ----------------------------------------------------------------------
        // Process WrkCtrRouteOprActivity records
        // ----------------------------------------------------------------------
        case #WrkCtrRouteOprActivity_DataSourceName:
 
            axbc_WrkCtrRouteOprActivity = _axBcStack.top();
            parentAxBC    = axbc_WrkCtrRouteOprActivity.parentAxBC();
            switch (_recordProcessingContext)
            {
                // Propagate children's keys
                case AxdRecordProcessingContext::BeforeAnyChildRecordsProcessed:
                    axbc_RouteOpr = parentAxBC;
                    select wrkCtrRouteOprActivity where wrkCtrRouteOprActivity.RouteOpr == axbc_RouteOpr.parmRecId() && wrkCtrRouteOprActivity.RouteOprDataAreaId == axbc_RouteOpr.currentRecord().DataAreaId;
                    _axBcStack.pop();
                    axbc_WrkCtrRouteOprActivity = AxWrkCtrRouteOprActivity::newWrkCtrRouteOprActivity(wrkCtrRouteOprActivity);
                    _axBcStack.push(axbc_WrkCtrRouteOprActivity);
 
            }
 
            return false;
 
        // ----------------------------------------------------------------------
        // Process WrkCtrActivity records
        // ----------------------------------------------------------------------
        case #WrkCtrActivity_DataSourceName:
 
            axbc_WrkCtrActivity = _axBcStack.top();
            parentAxBC    = axbc_WrkCtrActivity.parentAxBC();
            switch (_recordProcessingContext)
            {
                case AxdRecordProcessingContext::BeforeAnyChildRecordsProcessed:
                    axbc_WrkCtrRouteOprActivity = parentAxBC;
 
                    select wrkCtrActivity where wrkCtrActivity.RecId == axbc_WrkCtrRouteOprActivity.parmActivity();
                    _axBcStack.pop();
                    axbc_WrkCtrActivity = AxWrkCtrActivity::newWrkCtrActivity(wrkCtrActivity);
                    _axBcStack.push(axbc_WrkCtrActivity);
                    break;
            }
 
            return false;
 
 
        // ----------------------------------------------------------------------
        // Process ActivityRequirementSet records
        // ----------------------------------------------------------------------
        case #ActivityRequirementSet_DataSourceName:
 
            axbc_ActivityRequirementSet = _axBcStack.top();
 
            switch (_recordProcessingContext)
            {
                // Propagate parent's key
                case AxdRecordProcessingContext::BeforeAnyChildRecordsProcessed:
 
                    axbc_WrkCtrActivity = axbc_ActivityRequirementSet.parentAxBC();
                    select wrkCtrActivityRequirementSet where wrkCtrActivityRequirementSet.Activity == axbc_WrkCtrActivity.parmRecId();
                    _axBcStack.pop();
                    axbc_ActivityRequirementSet = AxWrkCtrActivityRequirementSet::newWrkCtrActivityRequirementSet(wrkCtrActivityRequirementSet);
                    _axBcStack.push(axbc_ActivityRequirementSet);
                    break;
            }
 
            return false;
 
        // ----------------------------------------------------------------------
        // Process ActivityRequirement records
        // ----------------------------------------------------------------------
        case #ActivityRequirement_DataSourceName:
 
            axbc_ActivityRequirement = _axBcStack.top();
 
            switch (_recordProcessingContext)
            {
                // Propagate parent's key
                case AxdRecordProcessingContext::BeforeAnyChildRecordsProcessed:
                    axbc_ActivityRequirementSet = axbc_ActivityRequirement.parentAxBC();
                    axbc_ActivityRequirement.parmActivityRequirementSet(axbc_ActivityRequirementSet.parmRecId());
 
                    return false;
 
                // Ensure ActivityRequirement record is saved before any of its dependent child records
                case AxdRecordProcessingContext::BeforeChildRecordProcessed:
                    switch (_childRecord.dataSourceName())
                    {
                        case #CapabilityRequirement_DataSourceName:
                        case #CertificateRequirement_DataSourceName:
                        case #CourseRequirement_DataSourceName:
                        case #PersonTitleRequirement_DataSourceName:
                        case #ResourceGroupRequirement_DataSourceName:
                        case #ResourceRequirement_DataSourceName:
                        case #ResourceTypeRequirement_DataSourceName:
                        case #SkillRequirement_DataSourceName:
                            if (!axbc_ActivityRequirement.isProcessed())
                            {
                                return true;
                            }
                            break;
                    }
 
                    break;
 
                // Ensure ActivityRequirement record is saved
                case AxdRecordProcessingContext::AfterAllChildRecordsProcessed:
                    if (!axbc_ActivityRequirement.isProcessed())
                    {
                        return true;
                    }
 
                    break;
            }
 
            return false;
 
        // ----------------------------------------------------------------------
        // Process CapabilityRequirement records
        // ----------------------------------------------------------------------
        case #CapabilityRequirement_DataSourceName:
 
            axbc_CapabilityRequirement = _axBcStack.top();
 
            switch (_recordProcessingContext)
            {
                // Propagate parent's key
                case AxdRecordProcessingContext::BeforeAnyChildRecordsProcessed:
                    axbc_ActivityRequirement = axbc_CapabilityRequirement.parentAxBC();
                    axbc_CapabilityRequirement.parmActivityRequirement(axbc_ActivityRequirement.parmRecId());
 
                    break;
                case AxdRecordProcessingContext::BeforeChildRecordProcessed:
                    axbc_ActivityRequirement = axbc_CapabilityRequirement.parentAxBC();
                    axbc_CapabilityRequirement.parmActivityRequirement(axbc_ActivityRequirement.parmRecId());
                    break;
                // Ensure WrkCtrActivityCapabilityRequirement record is saved
                case AxdRecordProcessingContext::AfterAllChildRecordsProcessed:
                    if (!axbc_CapabilityRequirement.isProcessed())
                    {
                        return true;
                    }
 
                    break;
                case AxdRecordProcessingContext::AfterChildRecordProcessed:
                    switch (_childRecord.dataSourceName())
                    {
                        case #WrkCtrCapability_DataSourceName:
                            axbc_WrkCtrCapability = _childRecord;
                            select RecId from wrkCtrCapability where wrkCtrCapability.Name == axbc_WrkCtrCapability.parmName();
                            axbc_CapabilityRequirement.parmCapability(wrkCtrCapability.RecId);
                            break;
                    }
            }
 
            return false;
 
        // ----------------------------------------------------------------------
        // Process WrkCtrCapability records
        // ----------------------------------------------------------------------
        case #WrkCtrCapability_DataSourceName:
 
            return false;
 
        // ----------------------------------------------------------------------
        // Process CourseRequirement records
        // ----------------------------------------------------------------------
        case #CourseRequirement_DataSourceName:
 
            axbc_CourseRequirement = _axBcStack.top();
 
            switch (_recordProcessingContext)
            {
                // Propagate parent's key
                case AxdRecordProcessingContext::BeforeAnyChildRecordsProcessed:
                    axbc_ActivityRequirement = axbc_CourseRequirement.parentAxBC();
                    axbc_CourseRequirement.parmActivityRequirement(axbc_ActivityRequirement.parmRecId());
 
                    break;
 
                // Ensure CourseRequirement record is saved
                case AxdRecordProcessingContext::AfterAllChildRecordsProcessed:
                    if (!axbc_CourseRequirement.isProcessed())
                    {
                        return true;
                    }
 
                    break;
            }
 
            return false;
 
        // ----------------------------------------------------------------------
        // Process PersonTitleRequirement records
        // ----------------------------------------------------------------------
        case #PersonTitleRequirement_DataSourceName:
 
            axbc_PersonTitleRequirement = _axBcStack.top();
 
            switch (_recordProcessingContext)
            {
                // Propagate parent's key
                case AxdRecordProcessingContext::BeforeAnyChildRecordsProcessed:
                    axbc_ActivityRequirement = axbc_PersonTitleRequirement.parentAxBC();
                    axbc_PersonTitleRequirement.parmActivityRequirement(axbc_ActivityRequirement.parmRecId());
 
                    break;
 
                // Ensure PersonTitleRequirement record is saved
                case AxdRecordProcessingContext::AfterAllChildRecordsProcessed:
                    if (!axbc_PersonTitleRequirement.isProcessed())
                    {
                        return true;
                    }
 
                    break;
            }
 
            return false;
 
        // ----------------------------------------------------------------------
        // Process CertificateRequirement records
        // ----------------------------------------------------------------------
        case #CertificateRequirement_DataSourceName:
 
            axbc_CertificateRequirement = _axBcStack.top();
 
            switch (_recordProcessingContext)
            {
                // Propagate parent's key
                case AxdRecordProcessingContext::BeforeAnyChildRecordsProcessed:
                    axbc_ActivityRequirement = axbc_CertificateRequirement.parentAxBC();
                    axbc_CertificateRequirement.parmActivityRequirement(axbc_ActivityRequirement.parmRecId());
 
                    break;
 
                // Ensure CertificateRequirement record is saved
                case AxdRecordProcessingContext::AfterAllChildRecordsProcessed:
                    if (!axbc_CertificateRequirement.isProcessed())
                    {
                        return true;
                    }
 
                    break;
            }
 
            return false;
 
        // ----------------------------------------------------------------------
        // Process SkillRequirement records
        // ----------------------------------------------------------------------
        case #SkillRequirement_DataSourceName:
 
            axbc_SkillRequirement = _axBcStack.top();
 
            switch (_recordProcessingContext)
            {
                // Propagate parent's key
                case AxdRecordProcessingContext::BeforeAnyChildRecordsProcessed:
                    axbc_ActivityRequirement = axbc_SkillRequirement.parentAxBC();
                    axbc_SkillRequirement.parmActivityRequirement(axbc_ActivityRequirement.parmRecId());
 
                    break;
 
                // Ensure SkillRequirement record is saved
                case AxdRecordProcessingContext::AfterAllChildRecordsProcessed:
                    if (!axbc_SkillRequirement.isProcessed())
                    {
                        return true;
                    }
 
                    break;
            }
 
            return false;
 
        // ----------------------------------------------------------------------
        // Process ResourceRequirement records
        // ----------------------------------------------------------------------
        case #ResourceRequirement_DataSourceName:
 
            axbc_ResourceRequirement = _axBcStack.top();
            switch (_recordProcessingContext)
            {
                // Propagate parent's key
                case AxdRecordProcessingContext::BeforeAnyChildRecordsProcessed:
                    axbc_ActivityRequirement = axbc_ResourceRequirement.parentAxBC();
                    axbc_ResourceRequirement.parmActivityRequirement(axbc_ActivityRequirement.parmRecId());
 
                    break;
 
                // Ensure ResourceRequirement record is saved
                case AxdRecordProcessingContext::AfterAllChildRecordsProcessed:
                    if (!axbc_ResourceRequirement.isProcessed())
                    {
                        return true;
                    }
 
                    break;
            }
 
            return false;
 
        // ----------------------------------------------------------------------
        // Process ResourceGroupRequirement records
        // ----------------------------------------------------------------------
        case #ResourceGroupRequirement_DataSourceName:
 
            axbc_ResourceGroupRequirement = _axBcStack.top();
            switch (_recordProcessingContext)
            {
                // Propagate parent's key
                case AxdRecordProcessingContext::BeforeAnyChildRecordsProcessed:
                    axbc_ActivityRequirement = axbc_ResourceGroupRequirement.parentAxBC();
                    axbc_ResourceGroupRequirement.parmActivityRequirement(axbc_ActivityRequirement.parmRecId());
 
                    break;
 
                // Ensure ResourceGroupRequirement record is saved
                case AxdRecordProcessingContext::AfterAllChildRecordsProcessed:
                    if (!axbc_ResourceGroupRequirement.isProcessed())
                    {
                        return true;
                    }
 
                    break;
            }
 
            return false;
 
        // ----------------------------------------------------------------------
        // Process ResourceTypeRequirement records
        // ----------------------------------------------------------------------
        case #ResourceTypeRequirement_DataSourceName:
 
            axbc_ResourceTypeRequirement = _axBcStack.top();
 
            switch (_recordProcessingContext)
            {
                // Propagate parent's key
                case AxdRecordProcessingContext::BeforeAnyChildRecordsProcessed:
                    axbc_ActivityRequirement = axbc_ResourceTypeRequirement.parentAxBC();
                    axbc_ResourceTypeRequirement.parmActivityRequirement(axbc_ActivityRequirement.parmRecId());
 
                    break;
 
                // Ensure ResourceTypeRequirement record is saved
                case AxdRecordProcessingContext::AfterAllChildRecordsProcessed:
                    if (!axbc_ResourceTypeRequirement.isProcessed())
                    {
                        return true;
                    }
 
                    break;
            }
 
            return false;
 
        // ----------------------------------------------------------------------
        // Process InventDim records
        // ----------------------------------------------------------------------
        case #InventDim_DataSourceName:
 
            axbc_InventDim = _axBcStack.top();
            switch (_recordProcessingContext)
            {
                // Ensure InventDim record is saved
                case AxdRecordProcessingContext::AfterAllChildRecordsProcessed:
                    if (!axbc_InventDim.isProcessed())
                    {
                        return true;
                    }
 
                    break;
            }
 
            return false;
 
        // ----------------------------------------------------------------------
        // Unsupported data sources
        // ----------------------------------------------------------------------
        default:
            error(strFmt("@SYS88979",classId2Name(classIdGet(_axBcStack.top()))));
            return false;
    }
 
    return false;
}
 
Remark: The prepareForSaveExtended method is called immediately before saving the top node of the stack
 
Please note that prepareForSaveExtended method usually contains validation business logic or additional business logic which must be executed before the record is saved
 
Result
 
Microsoft Dynamics AX 2012 – Routes (Versions)
 

 
Microsoft Dynamics AX 2012 – Routes (Lines)
 

Please note that after import of Routes all required fields are specified. This is because the query included all required data sources for Routes import. Please refer to walkthrough about custom Routes Web Service in Microsoft Dynamics AX 2012 RTM/FPK for comparative analysis of standard and custom Routes Web Service. Please also consider back-porting of standard Routes Web Service from Microsoft Dynamics AX 2012 R2 to Microsoft Dynamics AX 2012 RTM/FPK if you need to import Routes in Microsoft Dynamics AX 2012 RTM/FPK.
 
This is how required minimum setup should look like
 
General tab
 

 
Setup tab
 

 
Times tab
 

 
Resource requirements tab
 

 
Microsoft Dynamics AX 2012 – Routes (Approve/Activate)
 
 
 
Please note that Routes Approval and Activation processes are implemented in Microsoft Dynamics AX 2012 UI as dedicated functions which also allow to specify who is performing an action (Approver). That's why you are not supposed to approve or activate Route Version, or approve Route when consuming Routes Web Service. Please note that there's no specific code in AxdRoutes.prepareForSaveExtended method which flushes Routes Approval and Activation field values. However anyways Routes Approval and Activation field values will not be taken into account by AIF because of data model setup
 
 
Please note that Active and Approved fields in RouteVersion table and Approved field in RouteTable table have AllowEditOnCreate = No and AllowEdit = No which tells AIF to not to take these field values into consideration upon processing.
 
In fact you can implement automatic Approval and Activation by overriding AxdRoutes.updateNow method where you can use Routes Approval and Activation API
 
public void updateNow()
{
    RouteApprove            routeApprove;
 
    BOMRouteVersionApprove  versionApprove;
    BOMRouteVersionActivate versionActivate;
 
    super();
       
    routeApprove = RouteApprove::newRouteTable(RouteTable));   
    routeApprove.parmRemove(NoYes::No);
    routeApprove.parmApprover(HcmWorker::findByPersonnelNumber("000131").RecId);
    routeApprove.run();
   
    versionApprove = BOMRouteVersionApprove::newRouteVersion(RouteVersion);   
    versionApprove.parmRemove(NoYes::No);
    versionApprove.parmApprover(HcmWorker::findByPersonnelNumber("000131").RecId);
    versionApprove.run();
   
    versionActivate = BOMRouteVersionActivate::newRouteVersion(RouteVersion);  
    versionActivate.run();
}
 
Please note that Approver field in RouteVersion and RouteTable tables is RecID reference to HCMWorker table. This is how you can easily convert current AX user into appropriate RecID reference in HCMWorker table
 
HcmWorker::userId2Worker(curuserid());
 
Summary: Please consider using standard Routes Web Service for import of Routes into Microsoft Dynamics AX 2012 R2. In this walkthrough I demonstrated how to consume standard Routes Web Service for import of Routes.
 
Author: Alex Anikiiev, PhD, MCP
 
Tags: Microsoft Dynamics ERP, Microsoft Dynamics AX 2012 R2, Data Import, Data Conversion, Data Migration, Application Integration Framework, Routes.
 
Note: This document is intended for information purposes only, presented as it is with no warranties from the author. This document may be updated with more content to better outline the concepts and describe the examples.