Пример разработки нового DMF-объекта MS Dynamics AX 2012

С проведенной нормализацией модель данных в MicrosoftDynamics AX 2012 сильно изменилась. Большинство справочников приобрело сложную структуру, появились суррогатные ключи (по RecId). Все это сделало импорт данных гораздо более сложной задачей, в результате был разработан отдельный модуль Data Import/eXport Framework (DIXF или Data Migration Framework DMF). DMF создает промежуточную таблицу (staging table) для каждой сущности в базе данных Microsoft Dynamics AX. Staging table представляет собой необработанный промежуточный набор данных, плоское представление данных, полученных из исходного файла или базы данных. В качестве исходного объекта данных может быть сущность, представленная в виде нескольких таблиц (например, адреса). Промежуточная таблица нужна для проверки, очистки или преобразования передаваемых данных. Затем можно переместить данные в целевую таблицу или экспортировать их.

Процесс импорта экспорта.

Шаги, необходимые для импорта или экспорта данных в MicrosoftDynamics AX.

  1. Определение источника данных для экспорта или импорта и формата исходных данных. Для экспорта источником является AX. Для импорта можно использовать любой из следующих источников:
    1. AX — импорт данных из другого экземпляра Microsoft Dynamics AX.
    2. ODBC — импорт данных из другой базы данных, такой как Microsoft SQL Server или Microsoft Access.
    3. Файл — импорт данных из текстового файла с фиксированной шириной или с разделителями, файла XML или файла Microsoft Excel.
  2. Выбор объекта, который будет либо источником данных экспорта, либо целью данных импорта. Можно использовать существующий объект или создать собственный объект. Список доступных объектов расположен MainMenu/Структура импорта и экспорта данных/Настройка/Целевые объекты.
  3. Создание группы обработки для объектов, которые следует импортировать или экспортировать вместе. Группа обработки — это набор объектов, которые должны быть обработаны в нужной последовательности или которые могут быть логически сгруппированы вместе. Объекты в группе обработки экспортируются вместе или импортируются вместе из исходного в промежуточное состояние, а затем из промежуточного в целевое. В группе обработки вы также связываете каждый объект с исходным форматом данных.
  4. Непосредственно сам импорт или экспорт данных. Сам процесс осуществляется через функции доступные из группы обработки. Во время импорта сначала импортируются данные в промежуточную таблицу, где можно очистить или преобразовать данные по своему усмотрению. Затем идет перенос данных из промежуточной таблицы в целевую таблицу.

 

Важно! Microsoft настоятельно рекомендует удалить промежуточные данные после завершения миграции (MainMenu/Структура импорта и экспорта данных/Периодические операции/Очистка промежуточных данных).

Во время экспорта также перемещаются данные из источника в промежуточную таблицу. Затем эти данные экспортируются либо в Microsoft Dynamics AX, либо в файл. Первый вариант создает файлы .dat и .def, которые потом можно импортировать в другой экземпляр Microsoft Dynamics AX. Второй вариант создает файл с плоским представлением данных.

Создание пользовательских целевых объектов

Для каждой сущности AX используемой в DMF нужно: промежуточная таблица, проект, запрос, класс и функции. Можно использовать Мастер создания объекта, или создать их вручную. В обоих случаях потребуется существующая таблица в AOT.

Создание пользовательского объекта с помощью мастера

Мастер располагается «MainMenu /Структура импорта и экспорта данных/Обычный/Создать настраиваемый объект для импорта и экспорта данных»

Мастер создания объекта DMF

  1. Выберите таблицу, связанную с объектом, который вы хотите перенести, и нажмите кнопку Далее. Внимание! Мастер может использоваться для создания пользовательских объектов, связанных только с таблицами, которые не наследуют структуры данных из других таблиц. Таблица должна существовать в AOT.
    Таблица должна существовать в AOT
  2. На странице выбора параметров генерации кода выполните следующие действия:
    страницф выбора параметров генерации кода

    1. Мастер предлагает имена для промежуточной таблицы, запроса и класса. Можно принять эти имена или изменить их.
    2. Выберите пункт меню отображения для объекта.
    3. В зависимости от выбора Auto Generate Data добавляется либо метод генерации в класс, либо исходные таблицы для внешних ключей добавляются в запрос для сущности.
    4. Если объект будет использоваться с источником данных, который содержит связанные данные из двух таблиц, выберите «Является составным объектом». Поле RowID создается в промежуточной таблице. Это поле используется для сопоставления строк связанных данных друг с другом. Например, таблицы DMFSalesTableEntity и DMFSalesLineEntity, DMFPurchTableEntity и DMFPurchLineEntity, DMFBOMEntity и DMFBOMVersionEntity связываются по этому полю когда идет одновременный импорт шапки и строк. Эту связь можно увидеть в методе генерации естественного ключа \Classes\DMFBOMVersionEntityClass\GenerateBOMLink (entity = DMFBOMVersionEntity).
      public Container GenerateBOMLink(boolean _stagingToTarget = true)
      {
          container        res;
          BOMTable         bomTable;
          HcmWorker        worker;
          DirPartyTable    partyTable;
          BOMConfigRoute   bOMConfigRoute;
          boolean          validate = true;
      
          if (_stagingToTarget)
          {
              if (this.isCompositeEntity() && entity.RowId)
              {
                  res = [(select firstOnly1 DMFBOMEntity
                      where DMFBOMEntity.RowId == entity.RowId
                         && DMFBOMEntity.DefinitionGroup == entity.DefinitionGroup
                         && DMFBOMEntity.ExecutionId == entity.ExecutionId).BOM_BOMId];
              }
      
  3. По завершению мастера, DMF открывает вновь созданный проект для настраиваемого объекта в текущем слое.
  4. На странице «Поля на целевой таблице» выберите поля. Нажмите кнопку Далее.
    Поля на целевой таблице
  5. Если в выбранных таблицах используются расширенные типы данных (EDT), дважды выйдет диалоговое окно с вопросом, хотите ли вы добавить отношение ForeignKey из EDT в новую промежуточную таблицу. Каждый раз нажимайте Да, чтобы создать отношение ForeignKey. Также в макрос DMFEntityListName будет добавлена строка с названием основной таблицы источника данных (в данном примере #define.TutorialDMFTargetTable(‘TutorialDMFTargetTable’)
  6. Добавьте пользовательский объект в качестве нового целевого объекта на форму MainMenu/Структура импорта и экспорта данных/Настройка/Целевые объекты. В зависимости от объекта могут потребоваться некоторые дополнительные действия.

Ручное создание.

Создание промежуточной таблицы для объекта

  1. В AOT Microsoft Dynamics AX создайте промежуточную таблицу для объекта.
  2. Настройки свойств таблицы для промежуточной таблицы.
    Property name Value
    SaveDataPerCompany No
    SupportInheritance No
    TableType Regular
    ConfigurationKey DMF
    ValidTimeStateFieldType None
  3. Создайте стандартные поля
    Field group name Label Field
    ExclusionList Exclusion List DefinitionGroup
    ** ** ** ** IsSelected
    ** ** ** ** TransferStatus
    ** ** ** ** ExecutionId
    Enabled Enabled Необязательно: Поля из промежуточной таблицы, которые вы хотите включить в шаблон по умолчанию.
    <<FunctionName_Sequence>>Example – GeneratePostalAddress_2 << Описание функции >>Функция, которая генерирует адрес Поля из промежуточной таблицы, которые являются источником для указанного метода.
  4. При необходимости нужно создать первичный ключ. AllowDuplicates – No и AlternateKey – Yes, добавить поля DefinitionGroup и ExecutionId.
  5. Указать связь между промежуточной таблицей и целевой таблицей. Это отношение используется для автоматической связи конкретной промежуточной записи со связанной целевой сущностью. Если нельзя определить отношение с помощью узла отношения, нужно определить его с помощью метода addStagingLink () в классе сущности.
  6. Поля enum в целевом объекте представлены строковыми полями в промежуточной таблице. Нужно создать новый EDT строкового типа соответствующей длины для хранения строк меток значений перечисления. Преобразование между полем enum в целевом объекте и строковым полем в промежуточной таблице автоматически обрабатывается средой DMF.

Создание класса для объекта DMF.

DMFAttribute(true)]
class DMFTutorialDMFTargetTableEntityClass extends DMFEntityBase
{
    DMFTutorialDMFTargetTableEntity entity;
    TutorialDMFTargetTable target;
}
public void new(DMFTutorialDMFTargetTableEntity _entity)
{
    entity = _entity;
}
public static DMFTutorialDMFTargetTableEntityClass construct(DMFTutorialDMFTargetTableEntity _entity)
{
    DMFTutorialDMFTargetTableEntityClass entityClass = new DMFTutorialDMFTargetTableEntityClass(_entity);
    return entityClass;
}
Метод setTargetBuffer.

Целевой объект может иметь несколько источников данных. Одним источником данных является основная таблица, которая представляет сущность. В методе setTargetBuffer параметр _dataSourceName представляет источники данных, которые присутствуют в запросе целевого объекта. В зависимости от количества источников данных в запросе может потребоваться дополнительная табличная переменная для связанного источника данных. Затем дополнительную переменную можно использовать в функциях, которые необходимы для переноса данных. Переменная target должна быть инициализирована главной таблицей, которая представляет целевой объект.

public void setTargetBuffer(Common _common, Name _dataSourceName = "")
{
    switch (_common.TableId)
    {
        case tableNum(TutorialDMFTargetTable) :
            target = _common;
            break;
    }
}
Метод jumpRefMethod

Имя пункта меню в мастере создания объекта нужно для создания метода jumpRefMethod

public container jumpRefMethod(Common _buffer,Object _caller)
{
    MenuItemName           menuItemName;

    menuItemName = menuitemDisplayStr(TutorialDMFTargetTable);
    entity = _buffer;

    return [menuItemName,entity];
}

Свойство RunOn должно быть CalledFrom

Функции для импорта и экспорта данных

Можно сопоставить данные из промежуточного объекта в целевой объект двумя способами:

  1. Сопоставление поля в промежуточной таблице непосредственно полю в целевом объекте. В этом случае типы данных для промежуточных и целевых полей должны быть одинаковыми.
  2. Создать функцию X ++ для преобразования значений поля из промежуточной в целевую.

Функции (public container Generate…(boolean _stagingToTarget = true)) импорта и экспорта данных должны выполнять следующие действия:

  1. Input / Source – в этом случае переменная entity (промежуточная таблица) не пустая и параметр _stagingToTarget = true, соответственно можно использовать entity для вставки значений в целевую таблицу.
  2. Output/Target – это действие должно вернуть значения из целевой таблицы в виде контейнера.

Название метода функции должно быть использовано в методе  getReturnFields. Также название метода функции должно содержать строку generate, чтобы функцию можно было увидеть на форме сопоставления полей целевой и промежуточной таблицы.

Пример  DMFVendorEntityClass.GenerateParty()

/// <summary>
/// Generate Party information.
/// </summary>
/// <param name="_stagingToTarget">
/// Indicates if the method is called to generate target field values (true) or staging table values (false).
/// </param>
/// <returns>
/// Party information.
/// </returns>
[DMFTargetTransformationAttribute(true),DMFTargetTransformationDescAttribute("Функция, создающая запись субъекта"),
DMFTargetTransformationSequenceAttribute(1)
,DMFTargetTransFieldListAttribute([fieldStr(DMFVendorEntity,LanguageId),fieldStr(DMFVendorEntity,Name),fieldStr(DMFVendorEntity,NameAlias),fieldStr(DMFVendorEntity,PartyNumber),fieldStr(DMFVendorEntity,PartyType),fieldStr(DMFVendorEntity,FirstName),fieldStr(DMFVendorEntity,LastName),fieldStr(DMFVendorEntity,MiddleName),fieldStr(DMFVendorEntity,Gender),fieldStr(DMFVendorEntity,KnownAs),fieldStr(DMFVendorEntity,DunsNumber),fieldStr(DMFVendorEntity,ABC),fieldStr(DMFVendorEntity,NumberOfEmployees)])
]
public container GenerateParty(boolean _stagingToTarget = true)
{
    DirOrganization     dirOrganization;
    DirOrganizationBase organizationBase;
    DirDunsNumber       dunsNumber;
    container           res;
    DirPartyType        partyType = DirPartyType::Person;

    if (_stagingToTarget)
    {
        partyRecId = DMFParty::GenerateParty(entity, target.Party, DMFDataSourcePropertiesGlobal);
        res   = [partyRecId];
        party = DirParty::constructFromPartyRecId(partyRecid);
    }
    else
    {
        if (target.Party)
        {
            if (!party)
            {
                partyRecId = target.Party;
                party = DirParty::constructFromPartyRecId(target.Party);
            }
            if (party && party.parmType() == DirPartyType::Organization)
            {
                select firstonly NumberOfEmployees from dirOrganization where dirOrganization.RecId == partyRecId;

                select firstOnly dunsNumberRecid from organizationBase
                    where organizationBase.Recid == partyRecId
                    join DunsNumber from dunsNumber
                        where dunsNumber.Recid == organizationBase.DunsNumberRecId;
            }
        }

        res =  [party.parmLanguageId(),
                party.parmName(),
                party.parmNameAlias(),
                party.parmPartyNumber(),
                enum2str(party.parmType()),
                party.parmFirstName(),
                party.parmLastName(),
                party.parmMiddleName(),
                enum2str(party.parmGender()),
                party.parmKnownAs(),
                dunsNumber.DunsNumber,
                enum2str(party.parmABC()),
                dirOrganization.NumberOfEmployees];
    }
    return res;
}
Метод getReturnFields

Метод getReturnFields используется для указания исходных или целевых полей по умолчанию для функций, которые используются для переноса данных. Параметры для метода:

_entity — название объекта.

_name — имя функции.

Возвращаемое значение – контейнер, в котором должны содержаться:

  1. Имя источника данных в запросе целевого объекта, который используется в методе
  2. Имя поля источника данных в запросе целевого объекта, которое должно быть инициализировано функцией

Пример DMFVendorEntityClass.getReturnFields().

/// <summary>
/// Creates field mapping between staging and target
/// </summary>
/// <param name="_entity">
/// A staging buffer.
/// </param>
/// <param name="_name">
/// A mapping method name.
/// </param>
/// <returns>
/// The list of fields as a container.
/// </returns>
public static container getReturnFields(Name _entity,MethodName _name)
{
    DataSourceName dataSourceName = queryDataSourceStr(DMFVendorTargetEntity,VendTable);
    container con = [dataSourceName];
    Name fieldstrToTargetXML(FieldName _fieldName)
    {
        return DMFTargetXML::findEntityTargetField(_entity,dataSourceName,_fieldName).xmlField;
    }

    switch (_name)
    {
        case methodStr(DMFVendorEntityClass,GenerateParty) :
        con += [fieldstrToTargetXML(fieldStr(VendTable,Party))];
        break;

        case methodStr(DMFVendorEntityClass,generateOffsetLedgerDimension) :
        con += [fieldstrToTargetXML(fieldStr(VendTable,OffsetLedgerDimension))];
        break;

        case methodStr(DMFVendorEntityClass,generateDefaultDimension) :
        con += [fieldstrToTargetXML(fieldStr(VendTable,DefaultDimension))];
        break;

        case methodStr(DMFVendorEntityClass,generateMainContactWorker) :
        con += [fieldstrToTargetXML(fieldStr(VendTable,MainContactWorker))];
        break;


        case methodStr(DMFVendorEntityClass,generateMainContactWorker) :
        con += [fieldstrToTargetXML(fieldStr(VendTable,MainContactWorker))];
        break;

        case methodStr(DMFVendorEntityClass, generateCompanyIdNAF) :
        con += [fieldstrToTargetXML(fieldStr(VendTable, CompanyNAFCode))];
        break;

        case methodStr(DMFVendorEntityClass, generateTax1099Fields) :
        con += [fieldstrToTargetXML(fieldStr(VendTable, Tax1099Fields))];
        break;

        case methodStr(DMFVendorEntityClass, generateLvPaymTransCodes) :
        con += [fieldstrToTargetXML(fieldStr(VendTable, LvPaymTransCodes))];
        break;

        case methodStr(DMFVendorEntityClass,GeneratePostalAddress) :
        case methodStr(DMFVendorEntityClass,GenerateElectronicLocation) :
        case methodStr(DMFVendorEntityClass,generateAddressBook) :
        case methodStr(DMFVendorEntityClass,generateDocumentStatus) :
            break;

        case methodStr(DMFVendorEntityClass,generateVendBankAccount) :
        con += [fieldstrToTargetXML(fieldStr(VendTable, BankAccount))];
        break;

        default :
        con = conNull();
    }

    return con;
}
Метод addStagingLink.

Используется для определения взаимосвязи между промежуточной таблицей и целевой таблицей, когда это отношение не может быть определено с помощью свойства связей на промежуточной таблице. Целевой запрос и промежуточная запись доступны в методе. Следовательно, связь между целью и организацией может быть добавлена с помощью кода.

Пример метода DMFEmployeeEntityClass.addStagingLink()

public Query addStagingLink(Query query, TableId _entityTableId, Common _staging)
{
    QueryBuildDataSource    qbd;

    qbd = query.dataSourceTable(tableNum(HcmWorker));
    qbd.addRange(fieldNum(HcmWorker,PersonnelNumber)).value(_staging.(fieldNum(DMFEmployeeEntity,PersonnelNumber)));
    return query;
}

Запрос целевой сущности

Нужно создать запрос, который должен содержать все таблицы представляющие целевой объект.
Запрос целевой сущности

Обработка поля RefRecId

Если целевая таблица содержит поля, которые являются RecIds из других таблиц. В этом случае есть два варианта:

  1. Добавить источник данных ссылочной таблицы к запросу целевой сущности.
  2. Создать функцию.
Добавление источника данных

Например, целевая таблица VendTable содержит поле VendExceptionGroup, которое представляет собой RecId таблицы VendExceptionGroup. В этом случае промежуточная таблица должна включать поле VendExceptionGroup. Таблица VendExceptionGroup также должна быть добавлена к запросу целевого объекта в источнике данных VendTable. Связь между VendTable и VendExecptionGroup должна быть указана вручную. Связь должна быть между полем RefRecId в VendTable и RecId на VendExceptionGroup. Промежуточное поле DMFVendorEntity.VendExecptionGroup должно быть сопоставлено с запросом целевого поля для DMFVendorTargetEntity(query). DS: VendExceptionGroup.VendExceptionGroup. Во многих случаях, если имя поля в целевом запросе совпадает с промежуточным полем, сопоставление выполняется автоматически. Вы также можете вручную сопоставить целевые поля с промежуточными полями.
Добавление источника данных

Создание функции.

Связанные поля промежуточной таблицы с полем RefRecId целевой должны быть естественными ключами, по которым можно найти RecId таблицы, на которую ссылается целевая таблица. Создается метод в классе сущности для преобразования строкового поля (или набора полей) в RecId. Например, таблица CustTable содержит поле CompanyNAFCode, которое является RecId таблицы CompanyNAFCode. В этом случае нужно создать функцию в DMFCustomerEntityClass для преобразования строки (DMFCustomerEntity.CompanyIdNAF) в RecId (CompanyNAFCode.RecID). При создании функции, нужно создать группу полей для промежуточной таблицы, а возвращаемые поля для цели должны быть указаны в методе getReturnFields в классе сущности.

Пример

/// <summary>
/// This function generate companyIdNAF.
/// </summary>
/// <param name="_stagingToTarget">
/// Indicates if the method is called to generate target field values (true) or staging table values (false).
/// </param>
/// <returns>
/// companyIdNAF.
/// </returns>
[DMFTargetTransformationAttribute(true),DMFTargetTransformationDescAttribute("@DMF325"),
DMFTargetTransformationSequenceAttribute(7)
,DMFTargetTransFieldListAttribute([fieldStr(DMFCustomerEntity,CompanyIdNAF)])
]

public container generateCompanyIdNAF(boolean _stagingToTarget = true)
{
    CompanyNAFCode  companyNAFCode;
    container       res;

    if (_stagingToTarget)
    {
        select firstOnly1 RecId from companyNAFCode where companyNAFCode.CompanyIdNAF == entity.CompanyIdNAF;
        res = [companyNAFCode.RecId];
    }
    else
    {
        if (target.CompanyNAFCode)
        {
            select firstOnly1 CompanyIdNAF, RecId from companyNAFCode where companyNAFCode.RecId == target.CompanyNAFCode;
        }

        res = [companyNAFCode.CompanyIdNAF];
    }

    return res;
}

При изменении состава полей на целевой таблице, добавлении нового источника данных в запрос целевой сущности или создании новой функции нужно обновлять (удалять и снова создавать) Целевые объекты (MainMenu/Структура импорта и экспорта данных/Настройка/Целевые объекты).

Ссылки

  1. https://docs.microsoft.com/en-us/dynamicsax-2012/appuser-itpro/create-a-custom-target-entity-for-the-data-import-export-framework-dixf-dmf
  2. https://docs.microsoft.com/en-us/dynamicsax-2012/appuser-itpro/data-import-export-framework-user-guide-dixf-dmf

Comments

So empty here ... leave a comment!

Добавить комментарий

Sidebar