|
03.12.2012, 21:56 | #1 |
Banned
|
Опыт: синхронизация справочников через AIF в AX2012
Поделюсь опытом: задача синхронизации справочников поставщиков и клиентов через AIF (в моем случае - из AX 4.0 в AX 2012) ... в общем случае не решается без интерактивной работы и адаптации ведущей программы (CRM итд.) к ведомой (AX2012). Кое-чего, однако, можно добиться с минимумом программирования.
Пусть ведущая система (здесь: AX 4.0) ничего не знает о ведомой. Сразу возникает вопрос: когда новая запись в справочнике достигает той степени полноты, чтобы передать его в ведомую систему? В худшем случае над записью работает несколько отделов. Можно запускать пересылку в конце рабочего дня, можно проверять заполнение обязательных полей. Так или иначе, ведущая система дает толчок и размещает файл с клиентом/поставщиков в заданном каталоге сети. Для этого в AX 4.0 есть классический код из \Data Dictionary\Tables\CustInvoiceJour\Methods\sendElectronically(). Учетная запись процесса, записывающего файл, должна быть именованной и быть Owner-ом файла: http://msdn.microsoft.com/en-us/libr...=ax.10%29.aspx В AX2012 создается входящий порт, который читает каталог и передает файл сервису CustCustomerService.create, CustCustomerService.update. В параметрах порта добавляем в Pipeline входящее преобразование XSLT к схеме AX2012 (см. ниже). Создается batch job, в котором вручную (!) прописываются классические 2 процесса для чтения: - AifGatewayReceiveService - AifInboundProcessingService Запускаем batch job, и после 100 проб и ошибок получаем первую запись. Характерно, что можно создать CustTable, DirPartyTable и все до единой связанные таблицы, даже стандартную аналитику. Ключевое слово здесь - это "создать". Вслепую обновить удастся только саму CustTable, поскольку AX2012 требует <EntityKeys> того, что надо обновлять. В качестве этих Keys можно взять Primary Key или RecId таблицы. Если для CustTable есть натуральный ключ AccountNum, то для DirPartyTable есть только PartyId = RecId, который внешней программе не известен без дополнительного запроса getKeys и целой программной машинерии для обработки этого запроса. Впрочем, и без этого есть пара подводных камней: 1) При запросе на обновление система требует текущую RecVersion записи или Hash записи, который опять-таки надо получать интерактивным запросом. Тем самым система пытается предотвратить конкурентные обновления. Можно вырвать системе ядовитые зубы, закомментировав все проверки на Hash в классе AxdBaseUpdate. 2) Для обновления AX требует дату и время документа в элементе <ValidAsOfDateTime>. Если ведущая система такой информации не дает, то приходится извращаться, ибо .NET не поддерживает стандарт XSL/XPath 2.0 и, в частности, такую функцию как fn:current-DateTime() 3) Если в атрибуте action не указать "update", чтобы ввести систему в режим "инкрементного обновления", то AX2012 попытается стереть связанные таблицы типа DirPartyTable. Т.е. в XML файле должно стоять <CustTable class="entity" action="update"> А теперь привожу плод бессонных ночей - трансформацию XSLT, которая преобразует "плоскую" запись в формат AX2012, включая адреса и телефоны. Это преобразование реагирует на сигнал <DocPurpose>Duplicate</DocPurpose> и переходит из режима полного create в частичный update: CustTable_AX40_to_AX2012.zip Последний раз редактировалось EVGL; 03.12.2012 в 22:21. |
|
|
За это сообщение автора поблагодарили: Ruff (5), belugin (10). |
04.12.2012, 07:04 | #2 |
Участник
|
Очень интересно и познавательно, но где же обещанный плод бессонных ночей?
PS. Вообще, когда возникает идея из одной системы обновлять данные другой так, чтобы система-публикатор про систему-подписчика не знала, на ум приходит идея информационной шины данных. Впрочем, ее реализация может оказаться более трудоемкой, нежели написание хитровывернутой трансформации XSLT, но чудес ведь не бывает... Последний раз редактировалось gl00mie; 04.12.2012 в 07:20. |
|
04.12.2012, 11:06 | #3 |
Участник
|
Всегда интересовало - что под этим понимают? Но примеров реализации почему-то нигде не видел и не читал ;-(
__________________
Axapta 3.0 sp - хз какой, kr2 |
|
04.12.2012, 11:30 | #4 |
Участник
|
Цитата:
Идею gl00mie уже изложил. Как интегрировать Систему1 и Систему2, так чтобы они друг о друге не знали? Нужно взять Систему3 и интегрировать с ней обе наши Системы 1 и 2. Система3 в таком случае и будет выполнять роль шины данных. |
|
04.12.2012, 11:51 | #5 |
Участник
|
Цитата:
Сообщение от S.Kuskov
Это абстракция. Реализовать которую можно различными способами. Самый простой - это наверное через файловую систему.
Идею gl00mie уже изложил. Как интегрировать Систему1 и Систему2, так чтобы они друг о друге не знали? Нужно взять Систему3 и интегрировать с ней обе наши Системы 1 и 2. Система3 в таком случае и будет выполнять роль шины данных. Последний раз редактировалось ice; 04.12.2012 в 11:55. |
|
05.12.2012, 09:30 | #6 |
Модератор
|
Есть BizTalk (дорогой собака), есть open source системы..
__________________
-ТСЯ или -ТЬСЯ ? |
|
14.12.2012, 19:19 | #7 |
Banned
|
Мне удалось малой кровью запрограммировать "слепое" обновление имени и адреса клиента/поставщика:
DirParty@action = update DirPartyPostalAddressView@action = create \Classes\AxDirPartyTable\parmName X++: public DirPartyName parmName(DirPartyName _name = '') { DirPartyTable itself; AxVendTable axVendTable; if (!prmisDefault(_name)) { this.setField(fieldNum(DirPartyTable, Name), _name); // BEGIN if (_name && parentAxBc && parentAxBc.recordAction() == AxdRecordAction::Update) { switch (classIdGet(parentAxBc)) { case classNum(AxCustTable): itself = parentAxBc.currentRecord().dirPartyTable_FK(); break; case classNum(AxVendTable): axVendTable = parentAxBc; itself = DirPartyTable::findRec(axVendTable.parmParty()); break; } if (itself) { this.parmRecId(itself.RecId); } } // END } return dirPartyTable.Name; } X++: public LogisticsIsPrimaryAddress parmIsPrimary(LogisticsIsPrimaryAddress _isPrimary = NoYes::No) { LogisticsPostalAddress itself; if (!prmisdefault(_isPrimary)) { this.setField(fieldnum(DirPartyPostalAddressView, IsPrimary), _isPrimary); // BEGIN if (_isPrimary && parentAxBc && parentAxBc.parentAxBC() && parentAxBc.parentAxBC().recordAction() == AxdRecordAction::Update) { switch (classIdGet(parentAxBc.parentAxBC())) { case classNum(AxCustTable): case classNum(AxVendTable): itself = parentAxBc.parentAxBC().currentRecord().postalAddress(); break; } if (itself) { itself = LogisticsPostalAddress::findRecId(itself.RecId, true, itself.ValidFrom, itself.ValidTo); itself.delete(); } } // END } return dirPartyPostalAddressView.IsPrimary; } Последний раз редактировалось EVGL; 14.12.2012 в 20:26. |
|
|
За это сообщение автора поблагодарили: gl00mie (1). |
04.12.2012, 22:07 | #8 |
Banned
|
|
|
05.12.2012, 09:21 | #9 |
Модератор
|
На всякий случай - у файлового адаптера в четверке есть забавная фича. Он при генерации файла в качестве его имени использует GUID (кажется, MessageId). Если принимающая сторона сканирует каталог используя findFirst()..findnext(), есть неплохая вероятность того что для одной и той же сущности изменения будут накатываться в произвольном порядке, например:
- поменяли примечание - заблокировали клиента накатилось как - заблокировали клиента - поменяли примечание (и разблокировали клиента) В качестве workaround-а добавляли timestamp в начало имени файла P.S. В 2012 это похоже пофиксили
__________________
-ТСЯ или -ТЬСЯ ? |
|
|
За это сообщение автора поблагодарили: gl00mie (5). |
10.01.2013, 15:30 | #10 |
Moderator
|
Прошу прощения, если пропустил такую информацию. Но вопрос -
есть ли возможность сгенерированный AIF в 2012 xml файл назвать по своему (например CustTable*)? |
|
10.01.2013, 16:06 | #11 |
Banned
|
В стандарте - нет.
|
|
28.08.2020, 08:12 | #12 |
Участник
|
У нас в AX 4.0 появилось требование воскресить работу по превращению данных из накладной в XML-файл посредством кнопки Send XML > Original/Copy. Проблема в том, что AIF мы нигде ни разу не использовали. Поэтому вопрос такой: как настроить механизмы AIF именно для работы данной функциональности? Речь идет о простом сохранении в shared-папку, из которой файл будет извлекаться другой системой (EDI). Видимо надо прописать некие Действия (Actions) и Конечные точки (EndPoints) и скорее всего еще что-то. Поделитесь, у кого есть данный опыт?
__________________
// no comments Последний раз редактировалось dech; 28.08.2020 в 09:12. |
|
28.08.2020, 13:22 | #13 |
Модератор
|
Поиском по AIF в принципе много чего находиться должно. Например, вот это
__________________
-ТСЯ или -ТЬСЯ ? |
|
28.08.2020, 17:52 | #14 |
Участник
|
Как-то отдельно именно для отсылки электронной накладной в общем-то никак. В любом случае придется полноценно настраивать AIF "как по книжке", даже если нужно использовать только эту возможность.
|
|
28.08.2020, 14:08 | #15 |
Участник
|
Для 4-ки как-то сложно уже искать. Даже MSDN отказался поддерживать документацию ниже 2012. Нашел такую весчь: https://ellipsesolutions.com/microso...oice-journals/
Довольно подробно, правда для 2009, но с 4-кой много общего, вполне подойдет.
__________________
// no comments |
|
Теги |
aif, ax2012 |
|
Опции темы | Поиск в этой теме |
Опции просмотра | |
|