Стараюсь писать про Аксапту, хотя частенько тянет в Офис
Контроль возможности создания журнала
Запись от Gustav размещена 28.07.2009 в 16:41
Ну, что... попробуем создать первую, так сказать, содержательную запись. Итак...
Возникла необходимость не разрешать пользователям создание журналов (в различных модулях), если не выполнено некоторое условие. В нашем приложении Axapta от GMCS этим условием является заполненность поля "Первичная группа пользователя" формы "Параметры" (верхнее меню \ Сервис \ Параметры \ вкладка Разное; поле UserGroupDim в таблице SysUserInfo). Значение этого поля фактически определяет конкретный филиал нашего холдинга, к которому принадлежит пользователь.
Функционал приложения глобально кастомизирован так, что при заполненном поле пользователи видят только данные своего филиала и не видят других (своеобразный аналог механизма RLS). Однако, иногда им необходимо видеть данные всех филиалов одновременно, и они самостоятельно очищают это поле (те, у которых на это есть соответствующее право).
Неприятности начинаются, когда пользователю снова необходимо переключиться с аналитической работы на операционную и ввести какой-нибудь журнал. Если пользователь забыл включить конкретный филиал, то созданный журнал сохранится и разнесется "вне филиала" со всеми вытекающими последствиями - конечно, не супер-пагубными, но всё же вызывающими раздражение. Коррекцией ситуации обычно занимается системный администратор, который по звонку пользователя, допустившего оплошность, средствами СУБД или джобиком Аксапты прописывает недостающие филиальные данные в журнальные таблицы.
И вот было решено избавить администратора от этих неконструктивных действий и контролировать пользователя при создании журнала. Возник вопрос - в каком конкретном месте этот контроль выполнять? Я не смог найти (или плохо искал) единого предка для всех журналов (какой-нибудь JournalTable-папа), чтобы в каком-либо подобающем методе (new, construct, create, insert и т.п.) которого вставить необходимую проверку. Попробовал самое начало метода construct класса JournalTableData - получилось не очень удачно, ну в смысле - совсем не получилось:
Тогда я решил просто навставлять проверочный код в начало метода insert тех таблиц, которые присутствуют в вышеприведенном методе construct, благо что из этого перечня живыми в нашем приложении оказалось всего две: LedgerJournalTable и InventJournalTable.
Сказано - сделано. В начало методов insert двух указанных таблиц был добавлен одинаковый фрагмент:
а сам статический метод определен как:
Получилось, конечно, несколько громоздковато, но, с другой стороны, производимый эффект совпадает с ожиданиями заказчика
Происходит следующее. В зависимости от конкретной формы, для которой срабатывает insert, пользователю "без филиала" разрешается создать "запись" в гриде формы, и внешне она выглядит как обычная запись, до тех пор пока пользователь не перейдет на другую запись или не попробует перейти в строки журнала при помощи соответствующей кнопки. Тут и вылетает предупреждение, а поле описания журнала заполняется просьбой об удалении записи. Если пользователь закроет форму и потом откроет ее, то "записи" этой уже не будет.
Буду рад, если кто-нибудь из читателей укажет путь к какому-нибудь более правильному методу решения этой задачи. Я почти уверен, что он существует!
Возникла необходимость не разрешать пользователям создание журналов (в различных модулях), если не выполнено некоторое условие. В нашем приложении Axapta от GMCS этим условием является заполненность поля "Первичная группа пользователя" формы "Параметры" (верхнее меню \ Сервис \ Параметры \ вкладка Разное; поле UserGroupDim в таблице SysUserInfo). Значение этого поля фактически определяет конкретный филиал нашего холдинга, к которому принадлежит пользователь.
Функционал приложения глобально кастомизирован так, что при заполненном поле пользователи видят только данные своего филиала и не видят других (своеобразный аналог механизма RLS). Однако, иногда им необходимо видеть данные всех филиалов одновременно, и они самостоятельно очищают это поле (те, у которых на это есть соответствующее право).
Неприятности начинаются, когда пользователю снова необходимо переключиться с аналитической работы на операционную и ввести какой-нибудь журнал. Если пользователь забыл включить конкретный филиал, то созданный журнал сохранится и разнесется "вне филиала" со всеми вытекающими последствиями - конечно, не супер-пагубными, но всё же вызывающими раздражение. Коррекцией ситуации обычно занимается системный администратор, который по звонку пользователя, допустившего оплошность, средствами СУБД или джобиком Аксапты прописывает недостающие филиальные данные в журнальные таблицы.
И вот было решено избавить администратора от этих неконструктивных действий и контролировать пользователя при создании журнала. Возник вопрос - в каком конкретном месте этот контроль выполнять? Я не смог найти (или плохо искал) единого предка для всех журналов (какой-нибудь JournalTable-папа), чтобы в каком-либо подобающем методе (new, construct, create, insert и т.п.) которого вставить необходимую проверку. Попробовал самое начало метода construct класса JournalTableData - получилось не очень удачно, ну в смысле - совсем не получилось:
X++:
private static JournalTableData construct( JournalTableMap _journalTable ) { // мои эксперименты --> ; box::info('Привет из констракта'); // return; // мои эксперименты <-- switch (_journalTable.tableId) { case tableNum(TutorialJournalTable): return new tutorialJournalTableData(_journalTable); case tableNum(ProjJournalTable): return new ProjJournalTableData(_journalTable); case tableNum(LedgerJournalTable): return new LedgerJournalTableData(_journalTable); case tableNum(WMSJournalTable): return new WMSJournalTableData(_journalTable); case tableNum(InventJournalTable): return new InventJournalTableData(_journalTable); case tableNum(ProdJournalTable): return prodJournalTableData::newTable(_journalTable); case tableNum(RPayJournalTable): return new RPayJournalTableData(_journalTable); case tableNum(RHRMOrderTable): return new RHRMJournalTableData(_journalTable); default : return new JournalTableData(_journalTable); } }
Сказано - сделано. В начало методов insert двух указанных таблиц был добавлен одинаковый фрагмент:
X++:
if (KKu::userUnknownFilialToCreateJournal(this)) return; // да - прерываемся
X++:
static boolean userUnknownFilialToCreateJournal(Common _common) { #define.alarmJournalDescr('ПОЖАЛУЙСТА, УДАЛИТЕ ЭТУ НЕПРАВИЛЬНУЮ ЗАПИСЬ БЕЗ ФИЛИАЛА!') ; if (SysUserInfo::find(curUserId()).UserGroupDim) { return false; } else { box::stop(strFmt('%1\n\n%2\n%3\n\n%4\n%5', 'Невозможно создать журнал - не указан филиал текущего пользователя!', 'Пожалуйста, укажите филиал в поле "Первичная группа пользователя" формы "Параметры",', 'после чего начните создание журнала заново.', 'Если Вы работаете в форме-списке журналов, то удалите неправильную запись без филиала', 'или закройте и снова откройте форму.')); switch (_common.TableId) { case tableNum(LedgerJournalTable): _common.(fieldNum(LedgerJournalTable, Name)) = #alarmJournalDescr; break; case tableNum(InventJournalTable): _common.(fieldNum(InventJournalTable, Description)) = #alarmJournalDescr; break; } return true; // Неизвестный филиал пользователя для создания журнала } }
Происходит следующее. В зависимости от конкретной формы, для которой срабатывает insert, пользователю "без филиала" разрешается создать "запись" в гриде формы, и внешне она выглядит как обычная запись, до тех пор пока пользователь не перейдет на другую запись или не попробует перейти в строки журнала при помощи соответствующей кнопки. Тут и вылетает предупреждение, а поле описания журнала заполняется просьбой об удалении записи. Если пользователь закроет форму и потом откроет ее, то "записи" этой уже не будет.
Буду рад, если кто-нибудь из читателей укажет путь к какому-нибудь более правильному методу решения этой задачи. Я почти уверен, что он существует!
Всего комментариев 7
Комментарии
-
Возможно окажется полезным, один из следующих вариантов (если устраивает проверка в момент validateWrite и создание записей происходит на форме):
1. journalTableData\validateWritePre (если честно, я не совсем понимаю, почему он не подошел сразу)
2. journalFormTable\datasourceValidateWritePost (можно конечно и другой более лучший метод данного класса попробывать)
3. classFactory\createRecord (разумеется здесь нужно делать заглушку на те таблицы, которые нужно проверять, в момент создания записи)Запись от SRF размещена 03.08.2009 в 09:39
Обновил(-а) SRF 03.08.2009 в 09:44 -
Цитата:
Цитата:
Цитата:
Цитата:
SRF, большое Вам спасибо! К сожалению, функционал блогов пока не позволяет количественно повлиять на репутацию, по надеюсь, что именно "пока".
P.S. В результате модификации статический метод выродился и стал безобразно прост:
И к двум уже имеющимся местам его вызова (в методах insert таблиц LedgerJournalTable и InventJournallTable) добавился вызов в методе datasourceCreatePre класса JournalFormTable:X++:static boolean userUnknownFilialToCreateJournal2() { ; if (SysUserInfo::find(curUserId()).UserGroupDim) { return false; } else { box::stop(strFmt('%1\n\n%2\n%3', 'Невозможно создать журнал - не указан филиал текущего пользователя!', 'Пожалуйста, укажите филиал в поле "Первичная группа пользователя" формы "Параметры",', 'после чего начните создание журнала заново.')); return true; // Неизвестный филиал пользователя для создания журнала } }
X++:boolean datasourceCreatePre() { boolean allowCreate = ctrlAllOpenPosted.selection() == AllOpenPosted::Posted ? false : true; if (formRunLines && allowCreate) { formRunLines.close(); formRunLines = null; } // KKu, 03.08.2009 --> Неизвестен филиал пользователя для создания журнала? if (KKu::userUnknownFilialToCreateJournal2()) return false; // да - прерываемся // KKu, 03.08.2009 <-- return allowCreate; }
Запись от Gustav размещена 03.08.2009 в 11:36
Обновил(-а) Gustav 03.08.2009 в 12:20 -
Цитата:
Так вот, во всех, перечисленных таблицах, кроме LedgerJournalTable, метод validateWrite содержит вызов :X++:private static JournalTableData construct( JournalTableMap _journalTable ) { switch (_journalTable.tableId) { case tableNum(TutorialJournalTable): return new tutorialJournalTableData(_journalTable); case tableNum(ProjJournalTable): return new ProjJournalTableData(_journalTable); case tableNum(LedgerJournalTable): return new LedgerJournalTableData(_journalTable); case tableNum(WMSJournalTable): return new WMSJournalTableData(_journalTable); case tableNum(InventJournalTable): return new InventJournalTableData(_journalTable); case tableNum(ProdJournalTable): return prodJournalTableData::newTable(_journalTable); case tableNum(RPayJournalTable): return new RPayJournalTableData(_journalTable); case tableNum(RHRMOrderTable): return new RHRMJournalTableData(_journalTable); default : return new JournalTableData(_journalTable); } }
Для сравнения залез в AX 4.0 и бинго! данный вызов имеется и на таблице LedgerJournalTable.X++:ret = _journalTableData.validateWritePre(ret);
Так что по всей видимости для AX 3.0 наблюдается баг, в отношении создания журналов.
Думаю, если добавить данный вызов и в AX 3.0 в метод validateWrite таблицы LedgerJournalTable, то можно использовать JournalTableData\validateWritePre, причем, этим способом можно защиться и в случае, когда журнал создается из кода, конечно, если идет вызов метода validateWriteЗапись от SRF размещена 03.08.2009 в 12:15 -
Отчего столь содержательное обсуждение не выносится в форум? И как быть с разрастанием информационного поля, т.е. если раньше достаточно было мониторить форум, то теперь еще и блоги шерстить? Есть ли глобальный поиск по контенту (форум+блоги)?
Запись от player размещена 11.08.2009 в 07:20 -
Думаю, что не выдам страшную тайну, сказав о том, что эти вопросы уже поднимались в модераторских кулуарах и даже зафиксированы в плане на доработку. Функционал блогов проходит обкатку, поэтому блоги пока и не афишируются широко. Но не писать же все время тестово о погоде или ау-ау. Вот я и решил начать писать по существу - надо же посмотреть, как будет выглядеть в блоге реальное обсуждение по профессиональной тематике.
Запись от Gustav размещена 11.08.2009 в 11:24 -
А пользователям удобно каждый раз залезать в Сервис/Параметры чтобы снять фильтр по филиалу?
И потом еще раз, чтобы вернуть фильтр.
Может на форме галочку добавить "А можно всех посмотреть?"?Запись от Кирилл размещена 14.10.2009 в 16:32
Обновил(-а) Кирилл 14.10.2009 в 16:36 -
Цитата:
А так они просто знают, что для смены филиала им надо выполнить такую-то команду, находящуюся в интерфейсе там-то. Не так же трудно щёлкнуть по "Сервис", а потом по "Параметры"? А далее даже сделано такое облегчение: им не надо раскрывать список филиалов в StringEdit с lookup, а достаточно пощелкать двойными щелчками, как в ComboBox'е, для переключения филиалов по кругу. Сделано это при помощи такого несложного метода:
Пользователям нравитсяX++:// для того, чтобы первичная группа пользователя // менялась по кругу при очередном щелчке public int mouseDblClick(int _x, int _y, int _button, boolean _Ctrl, boolean _Shift) { int ret; str groupToFind; container filials = ['Head','Branch1','Branch2','']; int pos; ret = super(_x, _y, _button, _Ctrl, _Shift); groupToFind = SysUserInfo.UserGroupDim; pos = conFind(filials, groupToFind); pos++; if (pos > conLen(filials)) pos = 1; SysUserInfo.UserGroupDim = conPeek(filials, pos); SysUserInfo.write(); SysUserInfo_ds.reread(); SysUserInfo_ds.refresh(); return ret; }
Запись от Gustav размещена 15.10.2009 в 10:30