AXForum  
Вернуться   AXForum > Microsoft Dynamics AX > DAX: Программирование
All
Забыли пароль?
Зарегистрироваться Правила Справка Пользователи Сообщения за день Поиск Все разделы прочитаны

 
 
Опции темы Поиск в этой теме Опции просмотра
Старый 05.06.2012, 18:38   #1  
yuh is offline
yuh
Участник
 
14 / 10 (1) +
Регистрация: 30.08.2011
Аналог OnValidate в AX
Ищутся рекомендации по применению опыта работы с программным импортом данных в Navision к импорту в Axapta.
В Nav при работе практически с любой таблицей (такой как "Customer" или "Item Journal Line"), можно было смело полагаться на триггеры уровня поля (OnValidate) и уровня таблицы (OnInsert, OnModify) для того чтобы смоделировать кодом поведение пользователя вводящего данные вручную. Код в этих триггерах отвечал как за заполнение зависимых полей в текущей строке, так и за создание необходимых попутных записей в связанных таблицах.
Все что нужно было сделать для программного импорта данных это заполнять одно за одним поля и вызывать нужные тригерры в соответствующих местах.
Применима ли такая концепция к AX?
И если да, то как можно узнать, в каких именно классах и методах хранится код подобный OnValidate, OnInsert и OnModify?

Прошу не кидать камнями, если подобные темы уже были; найти не получилось - ткните носом, пожалуйста!
Старый 05.06.2012, 20:40   #2  
gl00mie is offline
gl00mie
Участник
MCBMSS
Most Valuable Professional
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
3,684 / 5798 (201) ++++++++++
Регистрация: 28.11.2005
Адрес: Москва
Записей в блоге: 3
Цитата:
Сообщение от yuh Посмотреть сообщение
В Nav при работе практически с любой таблицей (такой как "Customer" или "Item Journal Line"), можно было смело полагаться на триггеры уровня поля (OnValidate)
См. методы класса xRecord. У любой таблицы есть штатные методы validateField() и validateWrite(). Первый проверяет одно поле, второй - всю запись, они могут быть перекрыты на таблице, и там может быть навернута сколько угодно сложная логика проверки; по умолчанию, т.е. на уровне ядра поля проверяются по relation'ам (связам, прописанным в метаданных расширенных типов, используемых полями, и непосредственно таблиц), а запись проверяется на заполненность обязательных полей. Также ядро умеет проверять поля на соответствие дополнительным свойствам, прописанным в расширенных типах, вроде того, что числовое значение не должно быть отрицательным и т.п.
Цитата:
Сообщение от yuh Посмотреть сообщение
и уровня таблицы (OnInsert, OnModify) для того чтобы смоделировать кодом поведение пользователя вводящего данные вручную. Код в этих триггерах отвечал как за заполнение зависимых полей в текущей строке, так и за создание необходимых попутных записей в связанных таблицах.
Для заполнения связанных полей обычно логика вешается на modifiedField(). Но эта логика не всегда пригодна для создания записей из кода. Коренное отличие программного и ручного создания записей в том, что пользователь вводит значения полей по одному, после каждого изменения дергается тот же modifiedField(), плюс интерфейсная часть может делать недоступными для редактирования те или иные поля. Программно же известные значения полей могут быть "накиданы" кучкой и в совершенно произвольном порядке, и потом логика подтягивания значений связанных полей должна по идее отработать за один раз - подтянув знаничения всех возможные связанных полей и не перезатерев при этом значения тех полей, которые установлены программно - той же логикой импорта.
Триггеры insert/update тоже есть, в них может быть прописана логика заполнения/изменения связанных таблиц и проч. Но вот заполнять зависимые поля код в insert/update не должен, по моему глубокому убеждению (ну, максимум какой-нить следующий по счету LineNum подтянуть). Потому что в Аксапте по записи таблицы невозможно понять, было ли значение того или иного поля установлено явно или у него осталось значение по умолчанию (пустое в общем случае), которое можно перезаписать "правильным" значением.
Цитата:
Сообщение от yuh Посмотреть сообщение
Все что нужно было сделать для программного импорта данных это заполнять одно за одним поля и вызывать нужные тригерры в соответствующих местах.
А если надо установить значение поля А, затем - поля Б, но триггер на изменении поля Б перезатрет значение поля А, которое связано с Б, при том что никто не просил его это делать?
Цитата:
Сообщение от yuh Посмотреть сообщение
Применима ли такая концепция к AX? И если да, то как можно узнать, в каких именно классах и методах хранится код подобный OnValidate, OnInsert и OnModify?
В Аксапте есть отлаженный механизм для программного создания и изменения записей таблиц без необходимости заморачиваться дерганием кучи триггеров и, главное, тем, в каком порядке заполнять поля таблиц. Это т.н. AxBC-классы; по соглашению об именовании они называются так же, как соотв. таблица, но с префиксом Ax, например, для InventTable - AxInventTable.
На мой взгляд, с точки зрения импорта всякого рода справочников использование этих классов - наиболее прямой путь, но есть, как всегда, нюанс: к сожалению, не для всех таблиц в Аксапте реализованы соотв. AxBC-классы, а если они и есть, то не всегда в них зашита вся необходимая логика. См. также обсуждение
В чем преимущество ax-классов перед непосредственной работой с таблицами?
За это сообщение автора поблагодарили: S.Kuskov (3), yuh (1).
Старый 05.06.2012, 20:44   #3  
oip is offline
oip
Axapta
Лучший по профессии 2014
 
2,564 / 1416 (53) ++++++++
Регистрация: 28.11.2005
Записей в блоге: 1
table.validateField(), table.modifiedField(), table.validateWrite(), table.insert() и пр.

Если программист сделал все грамотно и не забыл написать нужный код куда следует и наверняка только в той части, в которой применима к NAV (то есть, думать все равно надо)... Принципиально от NAV отличаться не должно. Есть еще AX* классы, но я сомнвеваюсь, что в кастомизируемом (да и в стандартном) коде эта концепция везде поддерживается и на нее можно полагаться. Не поддерживается. Нельзя.

Только надо не забывать, что методы перекрыты бывают не только на уровне таблиц, но и на уровне источников данных форм. А иногда и на конкретных контролах.

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

Не поверю, что в NAV чтобы создать запись в "Item Journal Line" (и других таблицах) всегда достаточно написать так (псевдокод).

'Item Journal Line'.field1 := 1
'Item Journal Line'.onValidateField1()
'Item Journal Line'.field2 := 2
'Item Journal Line'.onValidateField2()
'Item Journal Line'.field3 := 3
'Item Journal Line'.onValidateField3()
...
'Item Journal Line'.OnInsert()
'Item Journal Line'.insert()
За это сообщение автора поблагодарили: yuh (1).
Старый 06.06.2012, 13:10   #4  
yuh is offline
yuh
Участник
 
14 / 10 (1) +
Регистрация: 30.08.2011
Цитата:
Сообщение от gl00mie Посмотреть сообщение
А если надо установить значение поля А, затем - поля Б, но триггер на изменении поля Б перезатрет значение поля А, которое связано с Б, при том что никто не просил его это делать?
Я думаю, что случай с перезатиранием значений полей страшен только когда мы бессистемно подходим к заполнению записи.
Скажем, мы заполняем журнал для ввода складских остатков. Пример двух зависимых полей - код товара и его стоимость (в простейшем случае, хранится как признак товара в соответствующей записи). Если мы сначала заполним стоимость а потом вернемся и введем код товара, то вполне можно ожидать что система затрет введенное нами значение тем, что записано в справочнике. Но мне кажется что ключ здесь в эмуляции действий пользователя при создании журнала вручную: естественно, что пользователь сначала выбирает товар - и только затем меняет стоимость на требуемую. Значит и наш код должен заполнять поля в том же порядке!
Цитата:
В Аксапте есть отлаженный механизм для программного создания и изменения записей таблиц без необходимости заморачиваться дерганием кучи триггеров и, главное, тем, в каком порядке заполнять поля таблиц. Это т.н. AxBC-классы
Спасибо за наводку, нужно будет попробовать этот подход. Надеюсь, "перезатирание" здесь побеждено.
Старый 06.06.2012, 13:24   #5  
yuh is offline
yuh
Участник
 
14 / 10 (1) +
Регистрация: 30.08.2011
Цитата:
Сообщение от oip Посмотреть сообщение
Не поверю, что в NAV чтобы создать запись в "Item Journal Line" (и других таблицах) всегда достаточно написать так (псевдокод).

'Item Journal Line'.field1 := 1
...
Олег, спасибо за ответ! Я когда это прочитал, чуть было сам не засомневался. А потом еще раз подумал и утвердился: да, именно так и происходит в NAV! Мало того, именно этот код используется самой системой при создании складских проводок из других модулей. Например, когда пользователь проводит продажу товара, программа создает специальный "временный" товарный журнал и заполняет его с помощью тех самых "OnValidate", а потом автоматически его разносит.
Отсюда - другой вопрос: а как это происходит в AX? Нечто подобное? И если да, то какие именно классы используются для создания журналов?
Старый 06.06.2012, 13:39   #6  
oip is offline
oip
Axapta
Лучший по профессии 2014
 
2,564 / 1416 (53) ++++++++
Регистрация: 28.11.2005
Записей в блоге: 1
Цитата:
Сообщение от yuh Посмотреть сообщение
Отсюда - другой вопрос: а как это происходит в AX? Нечто подобное? И если да, то какие именно классы используются для создания журналов?
Например, вот набросок кода создания журнала платежей для какой-то старой версии Аксапты:

X++:
void createIncomingJournal()
{

LedgerJournalNameId incomingName;
LedgerJournalTable incomingJournal;

ledgerJournalTrans incomingTrans;
ledgerJournalTrans ledgerJournalTrans;

LedgerJournalEngine ljeIn;
LedgerJournalEngine ledgerJournalEngine;

NumberSeq numSeq;
ProjTable projTable;
RContractTable rct;
RContractPartnerType rctType;

;

ttsbegin;
select forupdate incomingJournal;
incomingJournal.clear();
incomingJournal.initValue();
incomingJournal.JournalName=incomingName;
incomingJournal.insert();

ljeIn = new BMLedgerJournalEngine(null);
ljeIn.newJournalActive(incomingJournal);

if(ljeIn)
{
while select lines
    {
        ledgerJournalTrans=incomingTrans;
        ledgerJournalEngine=ljeIn;
        select forupdate ledgerJournalTrans;
        ledgerJournalTrans.clear();

        ledgerJournalTrans.TransDate=lines.PaymDate;
        ledgerJournalTrans.currencyCode=lines.CurrencyID;
        ledgerJournalTrans.ExchRate=lines.ExchRate;

        ledgerJournalTrans.initValue();
        ledgerJournalTrans.JournalNum=incomingJournal.JournalNum;
        ledgerJournalEngine.numberSeqFormHandlerJournal().parmLedgerJournalTrans(ledgerJournalTrans);
        ledgerJournalEngine.formMethodDataSourceCreatePost();
        ledgerJournalEngine.initValue(ledgerJournalTrans);

        ledgerJournalTrans.TransDate=lines.PaymDate;
        ledgerJournalTrans.currencyCode=lines.CurrencyID;
        ledgerJournalTrans.ExchRate=lines.ExchRate;

        ledgerJournalTrans.accountType=lines.AccountType;
        ledgerJournalTrans.AccountNum=lines.Account;
        ledgerJournalTrans.offsetAccountType=lines.offsetAccountType;
        ledgerJournalTrans.OffsetAccount=lines.offsetAccount;

        if(AmountDebit)
            ledgerJournalTrans.AmountCurDebit=lines.AmountDebit;
        else if(AmountCredit)
            ledgerJournalTrans.AmountCurCredit=lines.AmountCredit;

        ledgerJournalTrans.Txt=lines.TransTxt;
        ledgerJournalTrans.DocumentNum=lines.DocumentNum;
        ledgerJournalTrans.DocumentDate=lines.PaymDate;

        ledgerJournalTrans.validateWrite();
        ledgerJournalEngine.preWrite(ledgerJournalTrans);
        ledgerJournalEngine.formMethodDataSourceWritePre();
        ledgerJournalTrans.write();
        ledgerJournalEngine.write(ledgerJournalTrans);
    }
}
ttscommit;
if(ljeIn)
    info(strfmt("Создан журнал входящих платежей %1", incomingJournal.JournalNum));
}
Но тут повезло, что был класс BMLedgerJournalEngine. В общем, еще раз повторю - бизнес логика может быть и на форме и в этом случае будут вызываться не только табличные триггеры. Ну вот разве в NAV не так? Обратите внимание на вызов различных "formMethod". Некоторые из подобных классов без открытия формы и не инициализируешь.

Цитата:
Сообщение от yuh Посмотреть сообщение
Олег, спасибо за ответ! Я когда это прочитал, чуть было сам не засомневался. А потом еще раз подумал и утвердился: да, именно так и происходит в NAV!
Поспрашивал коллег, занимающихся NAV. У них другое мнение. В сферическом идеальном случае да, вызывать табличные триггеры достаточно. Но так это и в AX так. А в реальности бывает и код на формах, который при вызове табличных триггеров, естественно, не сработает.
Старый 06.06.2012, 13:40   #7  
oip is offline
oip
Axapta
Лучший по профессии 2014
 
2,564 / 1416 (53) ++++++++
Регистрация: 28.11.2005
Записей в блоге: 1
Вот пример создания строк складского журнала переноса:

X++:
#Define.JournalNum("000015_061")
 #Define.ItemId("ESB-007")
 #Define.LocationId("MW")
 #Define.QtyCounted(101)

 InventJournalTrans      inventJournalTrans;
 InventJournalTable      inventJournalTable;
 InventTable             inventTable;

 InventDim               inventDim;
 ;

 inventTable = inventTable::find(#ItemId);
 if (!inventTable)
  throw error(strfmt(InventTable::txtNotExist(), #ItemId));

 ttsbegin;
  inventJournalTable  = inventJournalTable::find(#JournalNum, true);
  if (!inventJournalTable)
   throw error(strfmt(InventJournalTable::txtNotExist(), #JournalNum));

  inventJournalTrans.clear();
  inventJournalTrans.initFromInventJournalTable(inventJournalTable);
  inventJournalTrans.initFromInventTable(inventTable);
  inventJournalTrans.transDate = systemDateGet();

  inventDim = inventJournalTrans.inventDim();
  inventDim.InventLocationId = #LocationId;
  inventJournalTrans.inventDimId  =  InventDim::findOrCreate(inventDim).inventDimId;
  inventJournalTrans.inventMovement().journalSetInventDimField(inventDim, fieldNum(InventDim, InventLocationId));

  inventJournalTrans.Counted = #QtyCounted;
  inventJournalTrans.inventMovement().JournalSetCounted();
  inventJournalTrans.insertFromCode();
ttscommit;
http://forum.mazzy.ru/index.php?showtopic=4069
Старый 06.06.2012, 13:53   #8  
oip is offline
oip
Axapta
Лучший по профессии 2014
 
2,564 / 1416 (53) ++++++++
Регистрация: 28.11.2005
Записей в блоге: 1
Ну и еще добавлю, что на одном из проектов разовый импорт в AX мы делали с помощью какой-то программки, которая умела запомнинать действия пользователя на компе и повторять их. Мы ей просто показали для одной строки журнала поячеечный копипаст из Excel в форму AX и эта тулза с удовольствием нагенерила нам нужные строки, пока мы пили чайзанимались другими полезными вещами. Это оказалось сильно быстрее, чем разрабатывать программный импорт.
За это сообщение автора поблагодарили: someOne (3).
Старый 06.06.2012, 13:58   #9  
yuh is offline
yuh
Участник
 
14 / 10 (1) +
Регистрация: 30.08.2011
Цитата:
Сообщение от oip Посмотреть сообщение
Поспрашивал коллег, занимающихся NAV. У них другое мнение.
Ну вот, а я бросился было искать мануалы, научающие девелоперов всегда располагать код бизнес-логики в таблицах; при программном создании записей всегда вызывать OnValidate, OnInsert и OnModify;...
По крайней мере, так нас учили раньше и гордо называли это "Navision Way".
Может, с выходом более поздних версий что-то поменялось, и теперь этот путь уже не такой прямой
Старый 06.06.2012, 14:02   #10  
oip is offline
oip
Axapta
Лучший по профессии 2014
 
2,564 / 1416 (53) ++++++++
Регистрация: 28.11.2005
Записей в блоге: 1
В AX тоже есть аналог Navision Way - Best Practices:
Цитата:
No code in the forms!

"The purpose of the forms is to support the presentation tier in the three tier client/server model, so you should not find any business logic in the forms."
Но реальная жизнь всегда интереснее скучной теории. Во многих случаях использование методов на источниках данных форм для бизнес-логики оправдано и иногда даже необходимо.
Старый 06.06.2012, 14:16   #11  
yuh is offline
yuh
Участник
 
14 / 10 (1) +
Регистрация: 30.08.2011
Цитата:
Сообщение от oip Посмотреть сообщение
программки, которая умела запомнинать действия пользователя на компе и повторять их
А как эта чудо-программка называлась, хоть приблизительно?
Старый 06.06.2012, 14:20   #12  
oip is offline
oip
Axapta
Лучший по профессии 2014
 
2,564 / 1416 (53) ++++++++
Регистрация: 28.11.2005
Записей в блоге: 1
Advanced key and mouse recorder
Старый 06.06.2012, 14:43   #13  
gl00mie is offline
gl00mie
Участник
MCBMSS
Most Valuable Professional
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
3,684 / 5798 (201) ++++++++++
Регистрация: 28.11.2005
Адрес: Москва
Записей в блоге: 3
Цитата:
Сообщение от yuh Посмотреть сообщение
Я думаю, что случай с перезатиранием значений полей страшен только когда мы бессистемно подходим к заполнению записи.
Скажем, мы заполняем журнал для ввода складских остатков. Пример двух зависимых полей - код товара и его стоимость (в простейшем случае, хранится как признак товара в соответствующей записи).
А вот зачем мне как программисту, пишущему тот же импорт, заморачиваться тем, какие между полями зависимости? Код товара и стоимость - это слишком простой пример, но ведь зависимости могут быть и куда затейливей, и без ковыряния в коде их подчас не выявить. Например, среди прочего в журнале может быть поле с кодом м... того же склада, и допустим, что у нас есть разделение по товарам, которые могут находиться на том или ином складе. При изменении кода склада проверяется, допустим ли указанный код номенклатуры, и если нет, он затирается, чтобы пользователь выбрал другой из списка доступных для склада (причем затирается все молча - точно так же, как молча затирается, скажем, договор при смене кода контрагента). Тот же программный импорт может быть совершенно не в курсе, что есть такой вот наворот, и сперва прописывать код номенклатуры, потом цену, а потом уже склад. На сохранении записи получим ошибку, что, мол, код номенклатуры не задан (он был стерт при прописывании склада) - и иди потом, ковыряйся, какого фига не задан, если есть и данные, и код, который эти данные в запись прописывает. Как можно заранее предусмотреть, что есть такие зависимости или что они появятся в будущем?
Цитата:
Сообщение от yuh Посмотреть сообщение
Если мы сначала заполним стоимость а потом вернемся и введем код товара, то вполне можно ожидать что система затрет введенное нами значение тем, что записано в справочнике.
С т.з. пользователя - да, с т.з. программного импорта, у которого есть на входе значения определенных полей, - нет. Те значения, которые код явно прописывает в запись, не должны перезатираться, потому что они известны и получены, не важно откуда, в явном виде, при этом корректны ли сочетания соотв. значений - это дело десятое. Вот те поля, которые не были явно инициализированы, бизнес-логика, связанная с той или иной таблицей, пусть заполняет, как хочет.
Цитата:
Сообщение от yuh Посмотреть сообщение
Но мне кажется что ключ здесь в эмуляции действий пользователя при создании журнала вручную: естественно, что пользователь сначала выбирает товар - и только затем меняет стоимость на требуемую. Значит и наш код должен заполнять поля в том же порядке!
Пользователя можно средствами презентационной логики принудить заполнять поля в определенном порядке, а принудить программиста заполнять поля записи в определенном порядке уже куда сложнее, особенно если это сторонний программист, пишущий интеграцию через Business Connector. Более того, сегодня порядок заполнения полей пользователем может быть один, завтра - другой, потому что поменялись зависимости между полями. Поменять поведение пользователя за счет ограничений, накладываемых презентационной логикой, очень просто, а вот что делать с кучей кода, создающего записи в таблице программно? Каждый раз искать его и переписывать - менять порядок заполнения полей? Абсурд...
В этом плане те же AxBC-классы очень грамотно сделаны: накидал полей, дернул один метод - и подтянулись все возможные связанные поля, не перезатирая те, которые заданы явно, "извне". При этом всегда можно понять, какие поля вообще заданы - извне либо изнутри, за счет логики самого класса. В этом случае действия пользователя по заполнению полей одного за другим - это лишь частный случай общей схемы работы (накидали значений полей - дернули обработчик), только значения полей пользователь всегда накидывает по одному.

Последний раз редактировалось gl00mie; 06.06.2012 в 14:46.
 

Похожие темы
Тема Автор Раздел Ответов Посл. сообщение
emeadaxsupport: New Content for Microsoft Dynamics AX 2012 : October 2011 Blog bot DAX Blogs 0 27.10.2011 17:11
axinthefield: Dynamics AX Event IDs Blog bot DAX Blogs 0 01.03.2011 22:11
daxdilip: Whats New in Dynamics AX 2012 (A brief extract from the recently held Tech Conf.) Blog bot DAX Blogs 7 31.01.2011 12:35
axStart: Microsoft Dynamics AX 2009 Hot Topics Web Seminar Series Blog bot DAX Blogs 0 06.08.2008 12:05
Arijit Basu: AX 2009 - Quick Overview Blog bot DAX Blogs 4 19.05.2008 14:47
Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск
Опции просмотра

Ваши права в разделе
Вы не можете создавать новые темы
Вы не можете отвечать в темах
Вы не можете прикреплять вложения
Вы не можете редактировать свои сообщения

BB коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.
Быстрый переход

Рейтинг@Mail.ru
Часовой пояс GMT +3, время: 18:21.