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:
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.