Показать сообщение отдельно
Старый 13.11.2014, 21:05   #1  
Ruff is offline
Ruff
Дмитрий Ерин
Аватар для Ruff
1C
 
475 / 396 (14) ++++++
Регистрация: 18.09.2003
Адрес: Тула
! Баг: Циклическое делегирование в WorkFlow
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(), но при следующем (по глубине рекурсии) вызове она хранит значение, оставшееся от прошлого вызова, что и приводит к описанной ошибке.

Лечить можно по-разному:
  1. завести локальный флаг, устанавливая его внутри switch и проверяя вместо if (delegateUser)
  2. очищать эту переменную в начале каждой итерации цикла и восстанавливать перед выходом из функции:
    X++:
    while select ...
    {
       delegateUser = '';
       ...
    } // while
    if (!delegateUser) delegateUser = _userId;
    } // findDelegate()
  3. вообще убрать этот чудной 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;
              ...
  4. Свой вариант)
__________________
За это сообщение автора поблагодарили: Logger (3), S.Kuskov (2).