AX2009
Стандартный функционал в некоторых случаях некорректно обрабатывает длинные цепочки делегирования. Например, если у пользователя Boss в параметрах настроено активное делегирование по всем конфигурациям (Результат = Все) на пользователя Zam, а у пользователя Zam - делегирование по конкретной конфигурации "Конф0001" на пользователя Buh, то при запуске документооборота по какой-либо
другой конфигурации будет выдана ошибка
"Обнаружено циклическое делегирование: Zam->Boss->Zam". Хотя на самом деле должно было сработать делегирование Boss->Zam. Если документооборот выполняется по конфигурации "Конф0001", цепочка будет определена верно (Boss->Zam->Buh).
Проблема кроется в методе \Classes\SysWorkflowHelper\getDelegate:
X++:
public server static WorkflowUser getDelegate(
WorkflowUser _user,
WorkflowConfigurationTable _configTable)
{
WorkflowTrackingMessage trackingMessage;
userId delegateUser;
Set delegatedUsers = new Set(Types::String);
utcdatetime delegateDate = DateTimeUtil::utcNow();
SetEnumerator userEnumerator;
str users;
//inline method
void findDelegate(WorkflowUser _userId)
{
WorkflowWorkItemDelegationParameters delegationParameters;
while select delegationParameters where
delegationParameters.User == _userId && delegationParameters.Enabled == NoYes::Yes &&
delegationParameters.FromDate < delegateDate && delegationParameters.ToDate > delegateDate
{
switch (delegationParameters.Type)
{
case WorkflowWorkItemDelegationType::All:
delegateUser = delegationParameters.Delegate;
break;
case WorkflowWorkItemDelegationType::Category:
if (delegationParameters.CategoryName == _configTable.CategoryName)
{
delegateUser = delegationParameters.Delegate;
}
break;
case WorkflowWorkItemDelegationType::Configuration:
if (delegationParameters.ConfigurationSequenceNumber == _configTable.SequenceNumber)
{
delegateUser = delegationParameters.Delegate;
}
break;
default:
throw error(strfmt("@SYS122124", enum2str(delegationParameters.Type)));
}
if (delegateUser) //////// <== Вот это проверка срабатывает некорректно!
{
if (delegatedUsers.in(delegateUser))
{
userEnumerator = delegatedUsers.getEnumerator();
while(userEnumerator.moveNext())
{
if (!users)
{
users = userEnumerator.current();
}
else
{
users = users + '->' + userEnumerator.current();
}
}
users = users + '->' + delegateUser;
trackingMessage = strfmt("@SYS122125", users);
throw error(trackingMessage);
}
else
{
delegatedUsers.add(delegateUser);
findDelegate(delegateUser);
}
break;
}
}
}
;
delegatedUsers.add(_user);
findDelegate(_user);
return strlen(delegateUser) > 0 ? delegateUser : _user;
}
Суть бага в том, что в рекурсивной локальной функции findDelegate() используется внешняя переменная delegateUser, которая (по задумке автора) при некоторых условиях (см. switch) может оказаться пустой, что должно означать переход к следующей итерации цикла. И это прекрасно работает при первом вызове findDelegate(), но при следующем (по глубине рекурсии) вызове она хранит значение, оставшееся от прошлого вызова, что и приводит к описанной ошибке.
Лечить можно по-разному:
- завести локальный флаг, устанавливая его внутри switch и проверяя вместо if (delegateUser)
- очищать эту переменную в начале каждой итерации цикла и восстанавливать перед выходом из функции:
X++:
while select ...
{
delegateUser = '';
...
} // while
if (!delegateUser) delegateUser = _userId;
} // findDelegate()
- вообще убрать этот чудной switch и дописать нужные проверки непосредственно к запросу:
X++:
...
&& (delegationParameters.Type == WorkflowWorkItemDelegationType::All
|| ( delegationParameters.Type == WorkflowWorkItemDelegationType::Category
&& delegationParameters.CategoryName == _configTable.CategoryName )
|| ( delegationParameters.Type == WorkflowWorkItemDelegationType::Configuration
&& delegationParameters.ConfigurationSequenceNumber== _configTable.SequenceNumber )
)
{
delegateUser = delegationParameters.Delegate;
...
- Свой вариант)