Пример разработки нового 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 представляет собой необработанный промежуточный набор данных, плоское представление данных, полученных из исходного файла или базы данных. В качестве исходного объекта данных может быть сущность, представленная в виде нескольких таблиц (например, адреса). Промежуточная таблица нужна для проверки, очистки или преобразования передаваемых данных. Затем можно переместить данные в целевую таблицу или экспортировать их.
Contents
Процесс импорта экспорта.
Шаги, необходимые для импорта или экспорта данных в MicrosoftDynamics AX.
- Определение источника данных для экспорта или импорта и формата исходных данных. Для экспорта источником является AX. Для импорта можно использовать любой из следующих источников:
- AX — импорт данных из другого экземпляра Microsoft Dynamics AX.
- ODBC — импорт данных из другой базы данных, такой как Microsoft SQL Server или Microsoft Access.
- Файл — импорт данных из текстового файла с фиксированной шириной или с разделителями, файла XML или файла Microsoft Excel.
- Выбор объекта, который будет либо источником данных экспорта, либо целью данных импорта. Можно использовать существующий объект или создать собственный объект. Список доступных объектов расположен MainMenu/Структура импорта и экспорта данных/Настройка/Целевые объекты.
- Создание группы обработки для объектов, которые следует импортировать или экспортировать вместе. Группа обработки — это набор объектов, которые должны быть обработаны в нужной последовательности или которые могут быть логически сгруппированы вместе. Объекты в группе обработки экспортируются вместе или импортируются вместе из исходного в промежуточное состояние, а затем из промежуточного в целевое. В группе обработки вы также связываете каждый объект с исходным форматом данных.
- Непосредственно сам импорт или экспорт данных. Сам процесс осуществляется через функции доступные из группы обработки. Во время импорта сначала импортируются данные в промежуточную таблицу, где можно очистить или преобразовать данные по своему усмотрению. Затем идет перенос данных из промежуточной таблицы в целевую таблицу.
Важно! Microsoft настоятельно рекомендует удалить промежуточные данные после завершения миграции (MainMenu/Структура импорта и экспорта данных/Периодические операции/Очистка промежуточных данных).
Во время экспорта также перемещаются данные из источника в промежуточную таблицу. Затем эти данные экспортируются либо в Microsoft Dynamics AX, либо в файл. Первый вариант создает файлы .dat и .def, которые потом можно импортировать в другой экземпляр Microsoft Dynamics AX. Второй вариант создает файл с плоским представлением данных.
Создание пользовательских целевых объектов
Для каждой сущности AX используемой в DMF нужно: промежуточная таблица, проект, запрос, класс и функции. Можно использовать Мастер создания объекта, или создать их вручную. В обоих случаях потребуется существующая таблица в AOT.
Создание пользовательского объекта с помощью мастера
Мастер располагается «MainMenu /Структура импорта и экспорта данных/Обычный/Создать настраиваемый объект для импорта и экспорта данных»
- Выберите таблицу, связанную с объектом, который вы хотите перенести, и нажмите кнопку Далее. Внимание! Мастер может использоваться для создания пользовательских объектов, связанных только с таблицами, которые не наследуют структуры данных из других таблиц. Таблица должна существовать в AOT.
- На странице выбора параметров генерации кода выполните следующие действия:
- Мастер предлагает имена для промежуточной таблицы, запроса и класса. Можно принять эти имена или изменить их.
- Выберите пункт меню отображения для объекта.
- В зависимости от выбора Auto Generate Data добавляется либо метод генерации в класс, либо исходные таблицы для внешних ключей добавляются в запрос для сущности.
- Если объект будет использоваться с источником данных, который содержит связанные данные из двух таблиц, выберите «Является составным объектом». Поле 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]; }
- По завершению мастера, DMF открывает вновь созданный проект для настраиваемого объекта в текущем слое.
- На странице «Поля на целевой таблице» выберите поля. Нажмите кнопку Далее.
- Если в выбранных таблицах используются расширенные типы данных (EDT), дважды выйдет диалоговое окно с вопросом, хотите ли вы добавить отношение ForeignKey из EDT в новую промежуточную таблицу. Каждый раз нажимайте Да, чтобы создать отношение ForeignKey. Также в макрос DMFEntityListName будет добавлена строка с названием основной таблицы источника данных (в данном примере #define.TutorialDMFTargetTable(‘TutorialDMFTargetTable’)
- Добавьте пользовательский объект в качестве нового целевого объекта на форму MainMenu/Структура импорта и экспорта данных/Настройка/Целевые объекты. В зависимости от объекта могут потребоваться некоторые дополнительные действия.
Ручное создание.
Создание промежуточной таблицы для объекта
- В AOT Microsoft Dynamics AX создайте промежуточную таблицу для объекта.
- Настройки свойств таблицы для промежуточной таблицы.
Property name Value SaveDataPerCompany No SupportInheritance No TableType Regular ConfigurationKey DMF ValidTimeStateFieldType None - Создайте стандартные поля
Field group name Label Field ExclusionList Exclusion List DefinitionGroup ** ** ** ** IsSelected ** ** ** ** TransferStatus ** ** ** ** ExecutionId Enabled Enabled Необязательно: Поля из промежуточной таблицы, которые вы хотите включить в шаблон по умолчанию. <<FunctionName_Sequence>>Example – GeneratePostalAddress_2 << Описание функции >>Функция, которая генерирует адрес Поля из промежуточной таблицы, которые являются источником для указанного метода. - При необходимости нужно создать первичный ключ. AllowDuplicates – No и AlternateKey – Yes, добавить поля DefinitionGroup и ExecutionId.
- Указать связь между промежуточной таблицей и целевой таблицей. Это отношение используется для автоматической связи конкретной промежуточной записи со связанной целевой сущностью. Если нельзя определить отношение с помощью узла отношения, нужно определить его с помощью метода addStagingLink () в классе сущности.
- Поля 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
Функции для импорта и экспорта данных
Можно сопоставить данные из промежуточного объекта в целевой объект двумя способами:
- Сопоставление поля в промежуточной таблице непосредственно полю в целевом объекте. В этом случае типы данных для промежуточных и целевых полей должны быть одинаковыми.
- Создать функцию X ++ для преобразования значений поля из промежуточной в целевую.
Функции (public container Generate…(boolean _stagingToTarget = true)) импорта и экспорта данных должны выполнять следующие действия:
- Input / Source – в этом случае переменная entity (промежуточная таблица) не пустая и параметр _stagingToTarget = true, соответственно можно использовать entity для вставки значений в целевую таблицу.
- 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 — имя функции.
Возвращаемое значение – контейнер, в котором должны содержаться:
- Имя источника данных в запросе целевого объекта, который используется в методе
- Имя поля источника данных в запросе целевого объекта, которое должно быть инициализировано функцией
Пример 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 из других таблиц. В этом случае есть два варианта:
- Добавить источник данных ссылочной таблицы к запросу целевой сущности.
- Создать функцию.
Добавление источника данных
Например, целевая таблица 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/Структура импорта и экспорта данных/Настройка/Целевые объекты).
Comments
So empty here ... leave a comment!