1. Общее
    1.1. Архитектура Workflow
    1.2. Жизненный цикл Workflow
    1.3. Концептуальный принцип работы Workflow
    2. Создания нового Workflow
    2.1. Высокоуровневый процесс создания Workflow
    2.2. Создание Категории Workflow (Workflow Category)
    2.3. Создание Типа Workflow (Workflow Type))
    A. SubmitToWorkflowMenuItem
    B. DocumentMenuItem
    C. DisableActivationConditions
    D. CancelMenuItem
    E. AssociationType
    F. Особенности создания Cross-Company Workflow
    2.4. Создание Запроса Workflow (Workflow Query)
    2.5. Создание Документа Workflow (Workflow Document)
    2.6. Создане Поддерживаемых Элементов Workflow (Supported Elements)
    2.6.1. StartedEventHandler, CompletedEventHandler, CanseledEventHandler
    2.6.2. Методы Started, Canceled и Completed
    2.7. Добавить Workflow на форму
    2.8. Добавить/переопределить метод canSubmitToWorkflow()
    2.9. Создание Task, Approval
    2.10. Создание пакетного задания для Wokflow
    2.11. Конфигурирование Workflow в редакторе
    2.12. Настройка элемента
    2.13. Поставщики Workflow
    3. Редактирование существующего Workflow
    3.1. Пример кастомизации Поставщиков
    3.2. Решение
    3.3. Логирование истории изменения статусов WF
    3.3.1. Сигнатура WorkflowTrackingTable.saveTracking()
    3.3.2. Пример в ClassOITWarrTableEH (OITWarranty model)
    3.3.3. Все parm методы класса SysWorkflowTracking
    4. Workflow для построчного утверждения документа
    4.1. Общее
    4.2. Реализация построчного утверждения Workflow для Timesheet
    4.3. Алгоритм работы построчного Workflow
    5. Поиск неисправностей
    5.1. Workflow работал корректно, но по прошествии некоторого времени — не переходит в ожидаемый статус.
    5.2. Удаление существующего Workflow.
    5.3. Лишние записи в таблице WorkflowWorkItemTable
    5.4. После нажатия на кнопку «Submit» у пользователя не отображаются элементы «Approve», «Reject», «Return», а отображаются только «View history»
    5.5. Стек заполнения таблицы WorkflowWorkItemTable (таблица, которая содержит элементы для отображения утверждающему сотруднику)
Исполняемая среда/Runtime
1 X++ workflow runtime Пользователь отправляет на одобрение отчёт о расходах
(или любой другой документ, для которого существует workflow) нажимая на кнопку Подтвердить/Subimt. Такое действие активирует/создаёт сущность/объект workflow при помощи исполняемой среды workflow API (в дальнейшем WF_API). WF_API размещает
сообщение в очереди сообщений. Пакетное задание сообщений считывает данное
сообщение и отправляет запрос на активацию workflow исполняемой/управляемой среде workflow.

Внимание
Пакетное задание сообщений обрабатывает/считывает
сообщения из очереди с минутным интервалом
2 Managed workflow runtime .NET Interop из X++ получает
сообщение и генерирует новую сущность workflow при помощи Windows Workflow Foundation. Эта workflow сущность осуществляет обратный вызов API X++ через .NET Interop to X++ CIL и отправляет уведомление что workflow запущен.После отправки сообщения, исполняемый объект workflow «свёртывается/упаковывается» и хранится в базе
данных Microsoft Dynamics AX. Исполняемая
среда затем удаляет его из памяти. Когда исполняемый объект workflow получает очередное
сообщение от/из X++, то
исполняемая среда восстанавливает объект workflow в состояние присущее объекту до момента «упаковки».Каждая сущность workflow уникальна. Если два пользователя одновременно
отправили на одобрение их отчёт о расходах на одобрение при помощи workflow, то образуются две сущности workflow.
3 X++ workflow runtime Пакетное задание сообщений получает сообщение из
очереди сообщений о том, что workflow started (workflow запущен) и
вызывает обработчик событий приложения для обработки события запуска
workflow (workflow event started). Затем пакетное задание размещает сообщение/уведомление
о том, что событие было обработано.
4 Both Выше обозначенный подход по считыванию сообщений из
очереди сообщений, при необходимости, повторяется по мере всего жизненного
цикла workflow.

Более детальная информация про архитектуру Workflow находится по ссылке:
https://technet.microsoft.com/en-us/library/dd309641.aspx

После того, как Workflow был создан и настроен его концептуальная работа выглядит следующим образом:
По кнопке Submit вызывается класс PurchAgreementWorkflow.main(),
Соответственно Workflow запускается в пакетной обработке.
После завершения пакетной обработки исполняется метод
PurchAgreementWFEventHandler.started()

Далее у пользователя появляется возможность работы с Task’ом или Approval’ом (в зависимости от того что настроено в конфигруаторе)
Выбирая один из доступных в Task/Approval outcomes, в первую очередь отрабатывает класс WorkflowWorkItemActionManager (или наследник данного класса), и уже после пакетной обработки выполняется сначала метод
PurchAgreementWFCompleteEventHand.started()
и в следующую очередь, метод этого же класса (PurchAgreementWFCompleteEventHand), который соответствует выбранному пользователем outcom’у.

Если пользователь нажал на кнопку Cancel уже во время исполнения Task/Approval, то в первую очередь отработает класс WorkflowCancelManager (или наследник данного класса);
Затем отработает пакетное задание;
Затем отработает метод cancel класса обработчика Task’а/Approval’а (в данном случае PurchAgreementWFCompleteEventHand.canceled());
И в последнюю очередь отработает класс обработчик для Workflow (в данном случае PurchAgreementWFEventHandler.canceled());

Для создания нового Workflow или «кастомизации» существующего Workflow необходима работа со следующими объектам (Артефактами):

Артефакт Где будет использоваться, как часто будет использоваться
1 Категория Workflow (2.2.Создание Категории Workflow) «Категория Workflow» означает в каком модуле будет использоваться данный Workflow.

Данный артефакт будет использоваться одноразово при создании «Типа Workflow».

2 Тип Workflow (2.3.Создание Типа Workflow) Данный артефакт будет использоваться каждый раз, когда будет осуществляется настройка формы, для которой будет активироваться Workflow. Другими словами – «Тип Workflow» говорит для формы следующее: на данной форме будет использоваться Workflow для строки такой-то таблицы («такая-то таблица» – это как правило коренная таблица DataSourcеs’а формы»)
3 Запрос Workflow (2.4.Создание Запроса Workflow) Данный Query возвращает строку таблицы, для которой непосредственно осуществляется Workflow (возвращаемая данным Query таблица должна быть такой же, что и та, которая указана в свойстве «WorkflowDatasource» формы, для которой активирован Workflow). Однажды создав Запрос Workflow, и указав его в методе getQueryName() (Документа Workflow == Класс Workflow), больше в коде X++ к данному Query Вы никогда обращаться не будете.
4 Документ Workflow (2.5.Создание Документа Workflow) «Документом Workflow» является класс. Данный класс обязательно должен расширять/наследовать системный класс WorkflowDocument. Созданный Вами класс (Документ Workflow) обязательно должен переопределить метод getQueryName() родительского класса (WorkflowDocument). Данный метод возвращается имя Вашего созданного «Запроса Workflow» и документооборота и осуществляет подсчет любых вычислимых полей, которые могут потребоваться при задании условий в документообороте. Он необходим, поскольку типы документооборота и элементы обязательно должны быть привязаны к конкретному классу документа документооборота.

В Мастерах по созданию (Approval, Task…) либо других артефактах Workflow, где бы Вы ни увидели «Document» или «Workflow Document» будет подразумеваться Ваш созданный Документ Workflow (или — Класс Workflow).

В данном руководстве процесс создания «Документа Workflow» (или — Класс Workflow) не описывается, но описывается «Процесс поиска необходимых артефактов из существующих в системе объектах Workflow».

5 Элемент Workflow (Approvals, Tasks, and Automated Tasks) Задайте документ, с которым будет работать утверждение (Approval).

1. Реализуйте обработчики событий утверждения Started и Canceled.

2. Определите в утверждении пункты меню для Document,DocumentWeb, Resubmit, ResubmitWeb, Delegate и DelegateWeb.

3. Выберите и включите возможные исходы утверждения, остальные – выключите.

4. Задайте пункты меню действия Action и ActionWeb для исходов утверждения.

5. Реализуйте обработчик исходов утверждения.

6. Задайте значение свойству DocumentPreviewFieldGroup на утверждении. Больше информации вы найдете в статье «How to: Create a Workflow Approval» (http://msdn.microsoft.com/en-us/library/cc596847.aspx)

Задайте документ, с которым будет работать задача (Task).

1. Реализуйте обработчики событий задачи Started, Canceled и WorkItemCreated.

2. Определите в задаче пункты меню для Document, DocumentWeb, Resubmit, ResubmitWeb, Delegate и DelegateWeb.

3. Выберите и включите возможные исходы задачи, остальные – выключите.

4. Задайте пункты меню действия Action и ActionWeb для исходов задачи.

5. Реализуйте обработчик исходов задачи.

6. Задайте значение свойству DocumentPreviewFieldGroup на задаче.

Больше информации вы найдете в статье «How to: Create a Workflow Task» (http://msdn.microsoft.com/en-us/library/cc601939.aspx)

1. Задайте документ, с которым будет работать автоматизированная задача.

2. Реализуйте обработчики событий задачи Execution и Canceled.

Больше информации вы найдете в статье «Walkthrough: Adding an Automated Task to a Workflow» (http://msdn.microsoft.com/enus/library/gg862506.aspx)

К данным артефактам, а если быть точнее – то к классам-обработчикам событий данный артефактов, Вы будете обращаться достаточно часто.

6 Таблица, для строки которой будет запущен Workflow К полю данной таблицы (поле хранящее текущий статус Workflow) разработчик будет обращаться регулярно
7 Класс SubmitToWorkflow Этот класс X++ указывается в пункте меню SubmitToWorkflow и ответственен за отображение диалогового окна Отправить в workflow-процесс в интерфейсе пользователя Microsoft Dynamics AX 2012. Это диалоговое окно дает пользователю возможность ввести комментарий, сопровождающий отправку документа. Затем класс SubmitToWorkflow запускает документооборот. Если в записи, отправляемой в документооборот, нужно изменить ее статус, то данный класс – наилучшее место для этого.

Данный класс будет вызываться MenuItemAction, который указывается в свойствах Типа Workflow (SubmitToWorkflowMenuItem)

8 Обработчики событий (EventHandlers) Код обработчика событий составлен из бизнес-логики, на писанной на X++, и ссылок на него в типе документооборота, элементах утверждений и их исходах, задачах и их исходах, автоматизированных задачах. Если документ документооборота имеет какую-либо модель состояний, то в обработчике событий вы можете осуществлять обработку записей документа в соответствии с этой моделью по мере выполнения документооборота. Код X++ обработчика событий создается в узле AOTClasses

В случае использования мастера создания Approval или Task, классы MyWorkflowTaskEventHandler и/или MyWorkflowApprovalEventHandler будут созданы автоматически. Задача разработчика – добавить соответствующий код в методы: started, completed, canceled, returned и прочие.

9 Собственные поставщики документооборота Если функциональность включенных в комплект Microsoft Dynamics AX 2012 поставщиков документооборота не удовлетворяет вашему набору требований, у вас есть возможность разработать своего поставщика документооборота. Все классы X++, реализующие поставщиков, находятся в узле AOTClasses и указываются затем в одном или нескольких поставщиках (из узла AOTWorkflowProviders). Еще больше информации о поставщиках документооборота можно почерпнуть в статье «Workflow Providers Overview» (http://msdn.microsoft.com/en-us/library/cc519521.aspx)

ВАЖНО! Каждый раз, осуществляя какие-либо изменения в Workflow, необходимо осуществлять Generate Incremental CIL

2.1.1. В первую очередь необходимо понять для какого бизнес процесса необходимо реализовать Workflow.

2.1.2. Определившись с процессом, необходимо определиться с таблицей, для строки которой будет осуществляться Workflow (в данном примере будет использоваться таблица PurchAgreementHeader);

2.1.3. В таблицу, которую выбрали для реализации Workflow, необходимо добавить поле, которое будет хранить значение текущего статуса Workflow (например для таблицы PurchAgreementHeader таким полем является WorkflowStatus_PSN);

2.1.3.1. Для поля PurchAgreementHeader.WorkflowStatus_PSN используется BaseEnum PurchAgreementWorkflowStatus. Т.е. для каждого созданного поля таблицы, которое будет хранить статус Workflow, необходимо СОЗДАТЬ СВОЙ BaseEnum, в котором будут указаны соответствующие статусы (количество статусов и их название может варьироваться);

2.1.3.2. Есть таблицы, в которых может быть более одного поля, относящихся к Workflow, например таблица AdvancedLedgerEntryHeader для Workflow содержит два поля: WorkFlowStatus & Необходимость создания более одного поля для хранения статуса Workflow диктуется бизнес процессом (вероятнее всего);

2.1.4. Переопределить метод таблицы canSubmitToWorkflow(). В данном методе необходимо определить условия, при которых будет возможно/невозможно запустить Workflow для текущего курсора таблицы. Данный метод возвращает значение типа boolean.
2.1.4.1. Метод canSubmitToWorkflow() также может быть переопределён в узле AOT/Forms/MyForm/Methods/canSubmitToWorkflow(), однако рекомендуется переопределять данный метод именно на таблице;
2.1.5. Создать Query, в котором в качестве корневого DataSource будет таблица, обозначенная в п.2.1.2.;
2.1.6. Создать класс, который будет расширять класс WorkflowDocument и в методе getQueryName() будет возвращаться имя Query, указанного в п.2.1.5.;
2.1.7. Создать Workflow Category (если Workflow должен будет реализовываться для модуля, которого нет в стандартной версии системы). В противном случае использовать существующий Workflow Category;
2.1.8. Создать «Тип Workflow» – сущность, содержащая в себе практически все метаданные имеющие отношение к данному Workflow.
2.1.9. Выяснить детали бизнес процесса, для того, чтобы определиться какой из элементов Workflow использовать (Approvals, Tasks, Automated Tasks). Могут использоваться и другие элементы, но описание их работы выходит за рамки данного руководства по причине редкости использования.
2.1.10. Определившись с деталями бизнес процесса, приступить к созданию соответствующего элемента: Approvals, Tasks или Automated Tasks. Настоятельно рекомендуется использовать соответствующие мастеры.
2.1.10.1. AOT/Worflow/Approvals/Add-Ins/Approval wizard
2.1.10.2. AOT/Worflow/Approvals/Add-Ins/Task wizard
2.1.10.3. Для Automated Tasks соответствующий мастер отсутствует.
2.1.11. После того как элемент был создан (Approvals, Tasks, Automated Tasks) и в соответствующих классах написан код, определяющий поведение Workflow при тех или иных действия пользователя, необходимо данный/ные элемент/ты добавить в узел AOT/Workflow/Workflow Types/MyWorkflowType/Supported Elements.
2.1.12. Затем необходимо созданный/модифицированный Workflow добавить на форму.
2.1.13. Затем необходимо сконфигурировать Workflow (для примера, описанного в данном руководстве, конфигурирование Workflow осуществляется по пути RURT/Procurement and sourcing/Setup/Procurement and sourcing workflows. По кнопке Edit – редактирование существующего (ранее сконфигурированного Workflow), по кнопке New – конфигурирование нового Workflow)

Для того, чтобы создать необходимую Категорию (Workflow Category), убедитесть в том, что BaseEnum ModuleAxapta содержит элемент, соответствующий модулю, для которого Вы планируете создать Workflow. Если Вы создаёте Workflow для нового модуля, то соответствующий элемент должен быть добавлен в BaseEnum ModuleAxapta.

Категория Workflow указывает принадлежность к определённому модулю (в данном случае – модуль Projects)

Для создания Категории Workflow разверните узел AOTWorkflowWorkflow Categories. Кликнув правой кнопкой мыши по узлу Workflow Categories, Вы создадите новый объект Категории Workflow

В рассматриваемом нами примере, рассматривается AOTWorkflowWorkflow CategoriesPurchAgreementWFApproval

Ниже указано описание данной категории, т.к. именно она (PurchAgreementWFApproval) указана в свойствах узла AOTWorkflowWorkflow Types PurchAgreementType рассматриваемый в данном руководстве

Category==PurchAgreementWFApproval (обнаружив данный узел в AOT по пути: AOT/Workflow/Workflow Categories/PurchAgreementWFApproval), в свойстве Module==PurchaseOrder. Это означает, что Типы Workflow, у которых в свойствах Category==PurchAgreementWFApproval, будут доступны только в модуле Procurement and sourcing

Более детально описано здесь: https://msdn.microsoft.com/en-us/library/cc589698.aspx

2.3.1. Для создания Типа Workflow используйте соответствующий мастер:

[/kc_column_text][kc_single_image image_size=»full» _id=»786334″ image_source=»media_library» image=»3919″ on_click_action=»lightbox» css_custom=»{`kc-css`:{`any`:{`image-style`:{`text-align|`:`left`}}}}»][kc_column_text _id=»649201″]

В появившемся диалоговом окне укажите следующие свойства:
2.3.2. Name — Укажите/Придумайте имя Типа. Как правило имя для Типа подбирается исходя из таких правил: Type (например PurchAgreementType)
2.3.3. Category – Укажите категорию, созданную в п.5;
2.3.4. Query — Укажите запрос, созданный в п.4;
2.3.5. Document menu item – MenuItemDisplay, при помощи которого открывается детальная форма, на которой будет (или уже) активирован Workflow. В данном примере, для PurchAgreementTask указан MenuItemDisplay::PurchAgreementDetails (т.е. детальная форма, а не PurchAgreementListPage), несмотря на то, что Workflow будет активирован как на форме PurchAgreementListPage так и на форме PurchAgreementDetails;
2.3.6. Document web menu item – данное свойство заполняется в том случае, если существует требование использовать данный Workflow в том числе и на Enterprise Portal (EP). Заполняется по той же логике что и п.6.1.4.
2.3.7. Generate menu items? – так как данный Workflow будет использоваться (это частное решение, в том случае если Workflow будет также использоваться и на Enterprise Portal, данное свойство также следует заполнять) только из клиентской части (не из EP), соответственно ставим «галочку» для Rich client.

2.3.8. После нажатия на кнопку «Next» будут созданы соответствующие артефакты (Названия объектов в изображение ниже не относятся к Workflow с типом PurchAgreementType, и размещены в данном руководстве лишь для наглядности)

2.3.9. Кроме того, все артефакты (вновь созданные объекты) будут размещены мастером в отдельный проект, а также появится сообщение, гласящее о том, что необходимо выполнить Incremental CIL

2.3.10. На примере свойств AOT/Workflow/Workflow Types/PurchAgreementType, можно увидеть, что часть объектов была создана мастером автоматически, а именно:

2.3.11. StartedEventHandler (а также CompletedEventHandler и CanceledEventHandler)==PurchAgreementWFEventHandler, где PurchAgreementWFEventHandler – класс, созданный мастером автоматически. Данный класс является классом обработчиком событий запуска Workflow, отмены, завершения.
2.3.12. SubmitToWorkflowMenuItem==PurchAgreementWorkflowSubmit, где PurchAgreementWorkflowSubmitMenuItemAction – также был создан мастером автоматически. Кроме того, данный MenuItemAction вызывает класс PurchAgreementWorkflow (в котором непосредственно осуществляется запуск Workflow) и который также был создан мастером автоматически;
2.3.13. CancelMenuItem==PurchAgreementWorkflowCancel, где PurchAgreementWorkflowCancelMenuItemAction – также был создан мастером автоматически. Кроме того, по умолчанию, тот MenuItemAction, который будет создан мастером для свойства CancelMenuItem, будет вызывать класс WorkflowCancelManager, в нашем примере, MenuItemAction::PurchAgreementWorkflowCancel вызывает класс PurchAgreementWorkflowCancelManager, который является наследником класса WorkflowCancelManager;

Зная «Тип Workflow» (в данном примере «PurchAgreementType») можно быстро выявить остальные объекты относящиеся к данному Workflow из свойств узла AOT/Workflow/Workflow Types/PurchAgreementType:

Более детально описано здесь: https://msdn.microsoft.com/en-us/library/cc594095.aspx

Ниже описаны свойства узла AOTWorkflowWorkflow Types PurchAgreementType

SubmitToWorkflowMenuItem==PurchAgreementWorkflowSubmit,

где PurchAgreementWorkflowSubmit – имя MenuItemAction, который вызывает класс метод PurchAgreementWorkflow.main(), после чего, в методе PurchAgreementWorkflow.submit() осуществляется непосредственно активация Workflow

DocumentMenuItem==PurchAgreementDetails, где PurchAgreementDetails – имя MenuItemDisplay, который вызывает открытие формы PurchAgreement (другими словами: данный Workflow реализован (активирован) как на форме ListPage (PurchAgreementListPage), так и на детальной форме PurchAgreement, но в свойствах DocumentMenuItem указывается именно MenuItemDisplay (PurchAgreementDetails), указывающий на детальную форму (в данном примере — PurchAgreement))

(напомню, мы рассматриваем свойства узла AOT/Workflow/Workflow Types/PurchAgreementType — 2.3. WorkflowType)

DisableActivationConditions==Yes. (Enable and disable activation conditions – это всё объяснение, которое удалось найти).

CancelMenuItem==PurchAgreementWorkflowCancel, где PurchAgreementWorkflowCancel – имя MenuItemAction, который вызывает метод PurchAgreementWorkflowCancelManager.main(), и, соответственно, создаёт объект класса PurchAgreementWorkflowCancelManager.

Данный класс расширяет класс WorkflowCancelManager. Обязательно необходимо переопределить метод updateDocument() родительского класса WorkflowCancelManager. В данном методе, полю строки таблицы (над которой осуществляется Workflow), присваивается соответствующий статус, что-то наподобие PendingCancellation.

AssociationType==Global
Данное свойство может иметь два значения
Global – при таком значении, данный Workflow может быть использован для всех компаний существующих в системе. В то же время, данный Тип Workflow может использовать только такой Документ Workflow, для которого Query возвращает таблицу, у которой свойство SaveDataPerCompany::No (в данном примере используется таблица PurchAgreementHeader)

Company – при таком значении, данный Workflow может быть использован только для одной (текущей компании).

Для того, чтобы «привязать» Workflow к конкретной организации, необходимо выполнить определённые процедуры описанные здесь: https://technet.microsoft.com/en-us/library/hh538464.aspx (Associate a workflow with an organization)

Особенности создания CrossCompany Workflow описаны здесь: https://community.dynamics.com/ax/f/33/p/245202/679231#679231

Для Workflow описываемого в данном руководстве, используется AOT/Queries/PurchAgreementWorkflowDocument

Посмотрев детальнее данный Query по пути AOT/Queries/PurchAgreementWorkflowDocument, мы увидим, что корневой таблицей является таблица PurchAgreementHeader (та же таблица, что указана в свойствах узла AOTFormsPurchAgreementListPageDesignsDesign: WorkflowDatasource==PurchAgreementHeader)

Более детально описано здесь: https://msdn.microsoft.com/en-us/library/hh457520.aspx

Document==PurchAgreementWorkflowDocument, где PurchAgreementWorkflowDocument – название класса, который является Документом Workflow.
(напомню, мы рассматриваем свойства узла AOT/Workflow/Workflow Types/PurchAgreementType — 2.3. WorkflowType)

В classDeclaration класса PurchAgreementWorkflowDocument мы видим что данный класс расширяет класс WorkflowDocument, а также метод getQueryName() возвращает имя Query (Запроса Workflow).

Кроме свойств, указанных и описанных ранее, каждый Тип Wokflow имеет узел Supported Elements (для AOTWorkflowWorkflow TypesPurchAgreementType такими являются: PaurchAgreementApproval (тип Approval), PaurchAgreementTask (тип Task), PaurchAgreementAutomatedTask(тип AutomatedTask)).

(Процесс создания Task и Approval описан детальнее в разделе 2.9.Создание Task, Approval)

Для каждого элемента узла AOTWorkflowApprovalsPurchAgreementApprovalOutcomes определены соответствующие свойства.

Кроме того, после отработки методов класса WorkflowWorkItemActionManager, в пакетном режиме, в среде CIL, отрабатываю методы класса PurchAgreementWFApprovalEventHandler ()

StartedEventHandler, CompletedEventHandler, CanseledEventHandler==PurchAgreementWFEventHandler, где PurchAgreementWFEventHandler – имя класса, который реализует соответствующие интерфейсы (WorkflowStartedEventHandler, WorkflowCanceledEventHandler, WorkflowCompletedEventHandler).

Методы Started, Canceled и Completed отрабатывают соответственно при:
— начале Workflow (после того, как пользователь нажал кнопку «Submit» (Подтвердить));
— отмене Workflow (после того, как пользователь нажал кнопку «Cancel» (Отмена));
— завершении Workflow (после того завершились все настроенные в данном Workflow элементы (Approval, Task, прочее);

Добавление Workflow на форму осуществляется редактированием свойств AOT/Forms/MyForm/Designs/Design, а именно:

WorkflowEnabled==Yes, Yes означает что для данной формы Workflow может быть запущен;

WorkflowDataSource==PurchAgreementHeader, где PurchAgreementHeader – таблица (должна быть коренным DataSource для данной формы), над строкой которой непосредственно будет осуществляться Workflow;

WorkflowType==PurchAgreementType, где PurchAgreementType – Тип Workflow (более детально про Типы Workflow написано в разделе 2.3.Создание Типа Workflow);

Логика одна: необходимо определить условия, при которых для текущей строки можно запустить Workflow.

Т.е. как только данный метод(ды) будет возвращать true, и на форме будет активирован Workflow, а также будет сконфигурирован соответствующий Workflow, на форме появится кнопка «Submit».

Элемент может быть:
— Задачей (Task)
— Утверждением (Approval)
— Вспомогательным документооборотом
— Решением вручную
— Решением по условию
— Параллельным мероприятием со многими ветвями
— Workflow-процессом по строке
— Автоматизированной задачей (Automated Task)

Реализация элементов которые определяются разработчиком:
— Задача (Task)
— Утверждением (Approval)
— Workflow-процесс по строке
— Автоматизированная задача (Automated Task)

Поведение остальных элементов можно управлять только настройкой их свойств. Обычно это делается бизнес-пользователями в графическом интерфейсе редактора документооборота
Для создания Task и/или Approval настоятельно рекомендуется использовать мастер AOT/Worflow/Task(Approvals)/Add-Ins/Task wizard

При использовании мастера, появится диалоговое окно

И для создания Approval, и для создания Task появится окно для ввода одних и тех же параметров.

1.1. Name – Укажите/Придумайте имя Утверждения/Задачи (Approval/Task). Как правило имя для Task’а подбирается исходя из таких правил: Task (например PurchAgreementTask), а для Approval: Approval (например: PurchAgreementApproval).
1.2. Workflow document – укажите имя документа Workflow (класса, созданного в разделе 2.5.Создание Документа Workflow). (ВАЖНО! Необходимо использовать тот же класс, который был указан в свойствах Document для Типа Workflow

1.3. Document preview field group – принимая во внимание пример, описываемый в данном руководстве, для таблицы PurchAgreementHeader была создана группа полей WorkflowPreview, которая и указывалась при создании Task (PurchAgreementTask);

1.4. Document menu item – MenuItemDisplay, при помощи которого открывается детальная форма, на которой будет (или уже) активирован Workflow. В данном примере, для PurchAgreementTask указан MenuItemDisplay::PurchAgreementDetails (т.е. детальная форма, а не PurchAgreementListPage), несмотря на то, что Workflow будет активирован как на форме PurchAgreementListPage так и на форме PurchAgreementDetails.

1.5. DocumentWebMenuItem – данное свойство заполняется в том случае, если существует требование использовать данный Workflow в том числе и на Enterprise Portal (EP). Заполняется по той же логике что и п.8.4.
1.6. Generate menu items? – так как данный Workflow будет использоваться только из клиентской части (не из EP), соответственно ставим «галочку» для Rich client.
1.7. В следующем шаге мастер предложит выбрать возможные варианты действий пользователя

Может быть создано несколько результатов (outcomes) данной задачи, например один из результатов может быть:
1.7.1. «Return» (Возврат) (т.е. после нажатия на кнопку «Return», Workflow «будет возвращён» пользователю, нажавшему кнопку Submit (Подтвердить). Этот пользователь будет иметь возможность откорректировать документ и у него будет возможность снова активировать Workflow для данного документа нажав кнопку Resubmit (Подтвердить повторно)). При данном исходе Workflow НЕ будет завершён;
1.7.2. «Complete» (Завершено) данный результат (outcomes) обязательно должен присутствовать в списке возможных (в противном случае мастер не активизирует кнопку «Next»). При данном исходе Workflow БУДЕТ завершён;
1.7.3. «RequestChange» — в данном случае задача (и Workflow) будет отправлена на доработку пользователю, которого выберет пользователь, нажавший кнопку «RequestChange» (это важная особенность, следует чётко понимать разницу между кнопками «Return» и «RequestChange». Если Пользователь1 нажал кнопку «Submit», а у Пользователя2 есть возможность нажать кнопку «Return» или «RequestChange», то в случае нажатия кнопки «Return» — задача вернётся Пользовалю1, а в случае нажатия «RequestChange» Пользователь2 может выбрать кого угодно: Пользователя1, Пользователя3, ПользователяN…). При данном исходе Workflow НЕ будет завершён;
1.7.4. «Deny» — при нажатии на эту кнопку, данная Задача (Task) или Утверждение (Approval) будут отклонены, и Workflow перейдёт к следующему шагу. Если есть следующие шаги (Task, Approval или другие элементы), то они вступят в действие, если же следующих шагов нет, то Workflow прекратит работу (т.е. сразу перейдёт к шагу «End»);

1.7.5. При выборе элемента используемого для Workflow, важно понимать различие между Task и Approval, а именно:
— для Task может быть несколько outcomes типа Return;
— для Approval может быть только один outcomes каждого типа;

1.7.6. На последнем шаге мастера, отображается новое окно.

В нём сообщается, что после нажатия на кнопку «Finish», будет создан ряд объектов/узлов с соответствующими названиями (кроме непосредственно объекта создаваемого Вами (Task или Approval) и одноимённого проекта) (Названия объектов в таблице ниже, и изображение ниже, не относятся к Workflow с типом PurchAgreementType, и размещены в данном руководстве лишь для наглядности)

# Action Value
1 Create classes TimeSheetLineTestEventHandler
2 TimeSheetLineTestResubmitActionMgr
3 Create menu items TimeSheetLineTestResubmitMenuItem
4 TimeSheetLineTestDelegateMenuItem
5 TimeSheetLineTestQtyOfHoursValidation
6 TimeSheetLineTestQtyOfHoursValidOk
7
8 Create node
9 Name TimeSheetLineTest
10 Label TimeSheetLineTestTask
11 Help Text Help Text for the TimeSheetLineTest task
12 Document TSWorkflowTimesheetLines
13 DocumentPreviewFieldGroup Workflow
14 DocumentMenuItem TSWorkflowApproval
15 StartedEventHandler TimeSheetLineTestEventHandler
16 CanceledEventHandler TimeSheetLineTestEventHandler
17 WorkItemsCreatedEventHandler TimeSheetLineTestEventHandler
18 ResubmitMenuItem TimeSheetLineTestResubmitMenuItem
19 DelegateMenuItem TimeSheetLineTestDelegateMenuItem
20
21 Create outcome 1
22 Name QtyOfHoursValidation
23 Type Return
24 Enabled Yes
25 EventHandler TimeSheetLineTestEventHandler
26 ActionMenuItem TimeSheetLineTestQtyOfHoursValidation
27
28 Create outcome 2
29 Name QtyOfHoursValidOk
30 Type Complete
31 Enabled Yes
32 EventHandler TimeSheetLineTestEventHandler
33 ActionMenuItem TimeSheetLineTestQtyOfHoursValidOk
34
35 Assemble project
36 Set name TimeSheetLineTestTaskElement
37 Add to project TimeSheetLineTest
38 Classes
39 Menu items

После завершения создания проекта и соответствующих узлов (см рисунок выше), появится информационное сообщение, в котором сказано, что необходимо сделать инкрементный CIL для созданных классов

Для того, чтобы данная задача была доступна в редакторе соответствующего WF, необходимо узел Tasks-> TimeSheetLineTest перетянуть в узел WorkflowTypes-> TSTimesheetLineTemplate -> SupportedElements, кроме того необходимо сделать Build->Generate Incremental CIL, в некоторых случаях может понадобится перезагрузка AOS

How to: Add a Task, Automated Task, or Approval to a Workflow Type [AX 2012]

https://msdn.microsoft.com/en-us/library/cc654026.aspx

ВАЖНО!
The Document property setting of the task, automated task, or approval must be same as the Document property setting for the workflow type

В результате окно редактора должно выглядеть следующим образом:

[/kc_column_text][kc_single_image image_size=»full» _id=»558393″ image_source=»media_library» image=»4091″ on_click_action=»lightbox» css_custom=»{`kc-css`:{`any`:{`image-style`:{`text-align|`:`left`,`padding|img`:`inherit inherit 20px inherit`}}}}»][kc_column_text _id=»810876″]

1.8. Иерархия класса WorkflowWorkItemActionManager

[/kc_column_text][kc_single_image image_size=»full» _id=»634869″ image_source=»media_library» image=»4093″ on_click_action=»lightbox» css_custom=»{`kc-css`:{`any`:{`image-style`:{`text-align|`:`left`,`padding|img`:`inherit inherit 20px inherit`}}}}»][kc_column_text _id=»409230″]

Данный класс (или его наследники) играют основную роль при выборе пользователем тех или иных действий предусмотренных в Task или Approval, а именно:

Для AOT/Workflow/Task/PurchAgreementTask существуют следующие outcomes (смотри изображение ниже):

— Complete;
— Reject;
— RequestChange;
Рассмотрим детальнее «RequestChange». Для узла outcomes «RequestChange» указаны следующие свойства:
— Type==RequestChange – этим свойством (собственно, как и другими) определяется поведение Workflow;
— ActionMenuItem==PurchAgreementWFTaskRequestChange, где PurchAgreementWFTaskRequestChange – вызывает метод WorkflowWorkItemActionManager.main() – т.е. данный класс (или его наследники, если есть необходимость создавать наследников) вступают в действие сразу же после нажатия пользователем кнопки «RequestChange»
— EventHandler==PurchAgreementWFCompleteEventHand, где PurchAgreementWFCompleteEventHand – класс, метод PurchAgreementWFCompleteEventHand.changeRequested() вступит в действие после отработки пакетного задания (пакетное задание вызывается методами класса WorkflowWorkItemActionManager).

Другими словами, порядок действий выглядит следующим образом:
1. Отрабатывает класс WorkflowWorkItemActionManager
2. Отрабатывает пакетное задание
3. Отрабатывает соответствующий метод класса EventHandler (в данном примере, для outcome «RequestChange» отработает метод PurchAgreementWFCompleteEventHand.changeRequested())

[/kc_column_text][kc_single_image image_size=»full» _id=»320121″ image_source=»media_library» image=»4094″ on_click_action=»lightbox» css_custom=»{`kc-css`:{`any`:{`image-style`:{`text-align|`:`left`,`padding|img`:`inherit inherit 20px inherit`}}}}»][kc_column_text _id=»326323″]

1.9. Resubmit
После того, как мастер завершит работу, будет создан ряд артефактов автоматически, а именно:

[/kc_column_text][kc_single_image image_size=»full» _id=»375997″ image_source=»media_library» image=»4095″ on_click_action=»lightbox» css_custom=»{`kc-css`:{`any`:{`image-style`:{`text-align|`:`left`,`padding|img`:`inherit inherit 20px inherit`}}}}»][kc_column_text _id=»620190″]

1.9.1. StartedEventHandler(а также CanceledEventHandler)== PurchAgreementWFCompleteEventHand, где PurchAgreementWFCompleteEventHand– класс, созданный мастером автоматически. Данный класс является классом обработчиком событий начала выполнения Задачи (Task или Approval) или отмены выполнения Задачи (Task или Approval).
1.9.2. ResubmitMenuItem – PurchAgreementWorkflowResubmit, где PurchAgreementWorkflowResubmit — MenuItemAction – также был создан мастером автоматически. Кроме того, данный MenuItemAction вызывает класс PurchAgreementWorkflow (тот же класс, который вызывается по нажатию на кнопку Submit (в котором непосредственно осуществляется запуск Workflow)) и который также был создан мастером автоматически;
1.9.3. DelegateMenuItem – PurchAgreementWFDelegate, где PurchAgreementWFDelegate- MenuItemAction – также был создан мастером автоматически. Кроме того, данный MenuItemAction вызывает класс WorkflowWorkItemActionManager (тот же класс (или его наследник), который вызывается каждым outcomes в данном Task, Approval, например в AOT/Workflow/Workflow Tasks/PurchAgreementTask/Outcomes/RequestChange вызывается MenuItemAction::PurchAgreementWFTaskRequestChange, который в свою очередь также вызывает класс WorkflowWorkItemActionManager;

[/kc_column_text][kc_single_image image_size=»full» _id=»220661″ image_source=»media_library» image=»4097″ on_click_action=»lightbox» css_custom=»{`kc-css`:{`any`:{`image-style`:{`text-align|`:`left`,`padding|img`:`inherit inherit 20px inherit`}}}}»][/kc_column][/kc_row][kc_row _id=»217970″][kc_column width=»12/12″ video_mute=»no» _id=»867045″][kc_spacing height=»20″ _id=»319787″][/kc_column][/kc_row][kc_row _id=»511892″][kc_column _id=»469131″][kc_title text=»Mi4xMC4g0KHQvtC30LTQsNC90LjQtSDQv9Cw0LrQtdGC0L3QvtCz0L4g0LfQsNC00LDQvdC40Y8g0LTQu9GPIFdva2Zsb3c=» _id=»527323″ type=»h2″ title_wrap=»yes» before=»PGEgbmFtZT0iMi0xMCI+PC9hPg==»][kc_column_text _id=»967611″]

Если пакетного задания (обрабатывающего действия Workflow) в системе нет, то его необходимо создать:
RURT/System administration/Iquiries/Batch jobs

[/kc_column_text][kc_single_image image_size=»full» _id=»202211″ image_source=»media_library» image=»4100″ on_click_action=»lightbox» css_custom=»{`kc-css`:{`any`:{`image-style`:{`text-align|`:`left`,`padding|img`:`inherit inherit 20px inherit`}}}}»][kc_single_image image_size=»full» _id=»816293″ image_source=»media_library» image=»4101″ on_click_action=»lightbox» css_custom=»{`kc-css`:{`any`:{`image-style`:{`text-align|`:`left`,`padding|img`:`inherit inherit 20px inherit`}}}}»][kc_column_text _id=»765093″]

Как правило, данное пакетное задание уже существует в системе. Для такого задания принимают участие следующие классы:

# Task Description Class Name AX Version
1 Workflow message processing SysWorkflowMessageQueueManager 2012, 2009
2 Workflow due date processing WorkflowWorkItemDueDateJob 2012, 2009
3 Workflow line item notification SysWorkflowNotificationManager 2012
Workflow message processing task SysWorkflowMessageQueueTask 2009

[/kc_column_text][kc_single_image image_size=»full» _id=»943000″ image_source=»media_library» image=»4102″ on_click_action=»lightbox» css_custom=»{`kc-css`:{`any`:{`image-style`:{`text-align|`:`left`,`padding|img`:`inherit inherit 20px inherit`}}}}»][/kc_column][/kc_row][kc_row _id=»571481″][kc_column width=»12/12″ video_mute=»no» _id=»994544″][kc_spacing height=»20″ _id=»207675″][/kc_column][/kc_row][kc_row _id=»428524″][kc_column _id=»87497″][kc_title text=»Mi4xMS4g0JrQvtC90YTQuNCz0YPRgNC40YDQvtCy0LDQvdC40LUgV29ya2Zsb3cg0LIg0YDQtdC00LDQutGC0L7RgNC1″ _id=»161403″ type=»h2″ title_wrap=»yes» before=»PGEgbmFtZT0iMi0xMSI+PC9hPg==»][kc_column_text _id=»280052″]

Конфигурирование Workflow осуществляется в форме WorkflowTableListPage.

Данная форма, как правило, находится в разделе Setup соответствующего модуля, на пример для Workflow описываемого в данном руководстве, форма WorkflowTableListPage вызывается по пути:

RURT/Procurement and sourcing/Setup/Procurement and sourcing workflows

Так выглядит окно формы настройки WF

[/kc_column_text][kc_single_image image_size=»full» _id=»656524″ image_source=»media_library» image=»4105″ on_click_action=»lightbox» css_custom=»{`kc-css`:{`any`:{`image-style`:{`text-align|`:`left`,`padding|img`:`inherit inherit 20px inherit`}}}}»][kc_column_text _id=»623227″]

В боковом левом меню мы видим все элементы, которые находятся в узле AOT/Workflow/Workflow Types/PurchAgreementType/Suppported elements/

[/kc_column_text][kc_single_image image_size=»full» _id=»738141″ image_source=»media_library» image=»4106″ on_click_action=»lightbox» css_custom=»{`kc-css`:{`any`:{`image-style`:{`text-align|`:`left`,`padding|img`:`inherit inherit 20px inherit`}}}}»][kc_column_text _id=»515391″]

Кликнув дважды на элемент Approve a timesheet line (выделен красным прямоугольником) перейдём в режим редактирования элемента:

 

[/kc_column_text][/kc_column][/kc_row][kc_row _id=»646152″][kc_column width=»12/12″ video_mute=»no» _id=»85429″][kc_spacing height=»20″ _id=»274076″][/kc_column][/kc_row][kc_row _id=»943324″][kc_column _id=»880701″][kc_title text=»Mi4xMi4g0J3QsNGB0YLRgNC+0LnQutCwINGN0LvQtdC80LXQvdGC0LA=» _id=»217690″ type=»h2″ title_wrap=»yes» before=»PGEgbmFtZT0iMi0xMiI+PC9hPg==»][kc_column_text _id=»574544″]

«Вытащив» какой-либо из доступных элементов (Task, Approval, Automated Task) на поле редактора (между элементами Start и End), необходимо осуществить настройку этого элемента:

1.9.4. Название Workflow – введите название элемента в поле «Name»

[/kc_column_text][kc_single_image image_size=»full» _id=»890184″ image_source=»media_library» image=»4108″ on_click_action=»lightbox» css_custom=»{`kc-css`:{`any`:{`image-style`:{`text-align|`:`left`,`padding|img`:`inherit inherit 20px inherit`}}}}»][kc_column_text _id=»43331″]

2.12.1. Выбор Владельца Workflow

[/kc_column_text][kc_single_image image_size=»full» _id=»615134″ image_source=»media_library» image=»4109″ on_click_action=»lightbox» css_custom=»{`kc-css`:{`any`:{`image-style`:{`text-align|`:`left`,`padding|img`:`inherit inherit 20px inherit`}}}}»][kc_column_text _id=»764692″]

Дополнительная информация по поводу того, кто такой «Владелец WF» в этом посте https://workflowax.wordpress.com/category/workflow-owners/
Pre-requisites for workflow owner users:
a) Must be a user in Dynamics AX.
b) Must be an active user (necessary for creating the configuration and receiving notifications etc about failed workflows).
NB: making the user inactive after setting them as the workflow owner will work, but nobody will then be aware if issues arise.
c) Must be linked to an employee (otherwise errors will occur stating the user has no relationship defined)
d) Should have email alerts activated and correct email address setup (user options form) so that they can receive notifications of workflow issues.

2.12.2. Выбрать шаблон уведомления по email

Для того, чтобы уведомления по email были доставлены до конкретного пользователя, необходимо осуществить настройку Configure email settings for the workflow system (https://technet.microsoft.com/en-us/library/gg732160.aspx )
Можно создать столько шаблонов email, сколько необходимо, в том числе:
1.1. Шаблон email для каждого Workflow
1.2. Шаблон email для каждого Типа Workflow
Более детальная информация здесь (Configure email functionality in Microsoft Dynamics AX): https://technet.microsoft.com/en-us/library/aa834374.aspx (Create an email template)
Click Organization administration > Setup > E-mail templates

2.12.3. Внесите инструкции для пользователя

[/kc_column_text][kc_single_image image_size=»full» _id=»76823″ image_source=»media_library» image=»4110″ on_click_action=»lightbox» css_custom=»{`kc-css`:{`any`:{`image-style`:{`text-align|`:`left`,`padding|img`:`inherit inherit 20px inherit`}}}}»][kc_column_text _id=»719183″]

Кроме непосредственно текстовой инструкции, можно также использовать шаблон, для этого необходимо выбрать соответствующий шаблон «Insert placeholder». Более детально здесь: https://msdn.microsoft.com/en-us/library/gg188988.aspx (Enter instructions for users)

2.12.4. Укажите условия, при которых данный Workflow будет активен

[/kc_column_text][kc_single_image image_size=»full» _id=»599141″ image_source=»media_library» image=»4116″ on_click_action=»lightbox» css_custom=»{`kc-css`:{`any`:{`image-style`:{`text-align|`:`left`,`padding|img`:`inherit inherit 20px inherit`}}}}»][kc_column_text _id=»272645″]

Может быть задано как одно условие (нажав на кнопку «Add condition»), так и несколько условий (And == «Условие1 И Условие2», OR == «Условие1 ИЛИ Условие2», And either == «Условие1 И ИЛИ Условие2»)

[/kc_column_text][kc_single_image image_size=»full» _id=»904682″ image_source=»media_library» image=»4118″ on_click_action=»lightbox» css_custom=»{`kc-css`:{`any`:{`image-style`:{`text-align|`:`left`,`padding|img`:`inherit inherit 20px inherit`}}}}»][kc_column_text _id=»746483″]

Нажав на кнопку «Test» Вы можете имитировать работу Workflow при заданных условиях, проверив результат (соответствует ли ожиданиям или нет)

2.12.5. Укажите условия, при которых уведомления должны быть отправлены

После того, как пользователь (инициатор/originator) нажал кнопку «Submit», создаётся объект WF. Вы можете настроить отправку уведомлений пользователям при условиях, когда объект WF находится в условиях STARTED, COMPLETED, TERMINATED, или STOPPED по причине ошибки.

[/kc_column_text][kc_single_image image_size=»full» _id=»737796″ image_source=»media_library» image=»4120″ on_click_action=»lightbox» css_custom=»{`kc-css`:{`any`:{`image-style`:{`text-align|`:`left`,`padding|img`:`inherit inherit 20px inherit`}}}}»][kc_column_text _id=»133191″]

  1. Started — отправка уведомления при создании объекта WF (когда пользователь (инициатор/originator) нажал кнопку «Submit»).
    ii. Stopped — отправка уведомления когда процессинг объекта WF остановлен по причине ошибки.
    iii. Completed — отправка уведомления когда процессинг объекта WF завершён.
    iv. Unrecoverable — отправка уведомления когда процессинг объекта WF остановлен по причине не восстанавливаемой ошибки (unrecoverable error).
    v. Terminated — отправка уведомления когда процессинг объекта WF прекращён.

Started — Send notifications when a workflow instance starts.
Stopped — Send notifications when a workflow instance stops because of an error.
Completed — Send notifications when a workflow instance is completed.
Unrecoverable — Send notifications when a workflow instance stops because of an unrecoverable error.
Terminated — Send notifications when a workflow instance is terminated.

Выбрав событие, введите текст поле «Notification text»

[/kc_column_text][kc_single_image image_size=»full» _id=»433013″ image_source=»media_library» image=»4121″ on_click_action=»lightbox» css_custom=»{`kc-css`:{`any`:{`image-style`:{`text-align|`:`left`,`padding|img`:`inherit inherit 20px inherit`}}}}»][kc_column_text _id=»121989″]

Данное текстовое сообщение будет получено пользователем/пользователями, которых Вы укажите на закладке «Recipients»

Option Select this option to send notifications to… To send notifications, do this…
Participant Users who are assigned to a specific group or role a) On the Recipient tab, click Participant.

b) Click the Role based tab.

c) From the Type of participant list, select the type of group or role to send notifications to.

d) From the Participant list, select the group or role to send notifications to.

Workflow user Users who are participants in this workflow a) On the Recipient tab, click Workflow user.

b) Click the Workflow user tab.

c) From the Workflow user list, select a participant in this workflow.

User Specific Microsoft Dynamics AX users a) On the Recipient tab, click User.

b) Click the User tab.

c) The Available users: list includes all Microsoft Dynamics AX users. Select the users to send notifications to, and move those users into the Selected users: list.

При выборе «Participant», для выбора получателей будут доступны существующие Роли

[/kc_column_text][kc_single_image image_size=»full» _id=»642217″ image_source=»media_library» image=»4123″ on_click_action=»lightbox» css_custom=»{`kc-css`:{`any`:{`image-style`:{`text-align|`:`left`,`padding|img`:`inherit inherit 20px inherit`}}}}»][kc_column_text _id=»593381″]

При выборе «User», для выбора получателей будут доступны пользователи системы

[/kc_column_text][kc_single_image image_size=»full» _id=»353782″ image_source=»media_library» image=»4124″ on_click_action=»lightbox» css_custom=»{`kc-css`:{`any`:{`image-style`:{`text-align|`:`left`,`padding|img`:`inherit inherit 20px inherit`}}}}»][kc_column_text _id=»523327″]

При выборе «Workflow user», для выбора получателей будут доступны существующие существующие либо владелец WF (WF owner), либо пользователь (инициатор/originator, человек, нажимающий кнопку «Submit»)

[/kc_column_text][kc_single_image image_size=»full» _id=»261172″ image_source=»media_library» image=»4126″ on_click_action=»lightbox» css_custom=»{`kc-css`:{`any`:{`image-style`:{`text-align|`:`left`,`padding|img`:`inherit inherit 20px inherit`}}}}»][/kc_column][/kc_row][kc_row _id=»173448″][kc_column width=»12/12″ video_mute=»no» _id=»618937″][kc_spacing height=»20″ _id=»953902″][/kc_column][/kc_row][kc_row _id=»279362″][kc_column _id=»24217″][kc_title text=»Mi4xMy4J0J/QvtGB0YLQsNCy0YnQuNC60LggV29ya2Zsb3cNCg==» _id=»272755″ type=»h2″ title_wrap=»yes» before=»PGEgbmFtZT0iMi0xMyI+PC9hPg==»][kc_column_text _id=»378246″]

Что такое поставщики Workflow?

Вы можете использовать Поставщиков Workflow для предоставления сущности Workflow некой специфической информации во время исполнения. Например, можно динамически, в зависимости от некоторых условий, определять – кто должен будет утверждать тот или иной документ или до какой даты такой документ должен быть утверждён. Данный раздел описывает стандартные виды поставщиков доступные в Microsoft Dynamics AX 2012.

В книге «Справочник профессионала AX 2012 R3», на странице 360 обозначены следующие типы поставщиков:

# Название Описание
1 Поставщики управленческой иерархии (Hierarchy Assignment) Определяет user ID на основании поиска в иерархии
2 Поставщики участников (Participant Assignment) Конвертирует логическую роль в одни или более UserId. Например этот поставщик используется для конвертации ролей безопасности Microsoft Dynamics AX в UserId пользователей, которым назначены данные роли
3 Поставщики очереди (Queue Assignment) Направляет объект Workflow (work item) в подходящую/соответствую очередь
4 Поставщики рабочего календаря (Due Date Assignment) Определяет граничную дату для утверждения задачи, или граничную дату для ШАГА в задаче или утверждении

Каждый тип поставщика управляется соответствующим классом

[/kc_column_text][/kc_column][/kc_row][kc_row _id=»207390″][kc_column width=»12/12″ video_mute=»no» _id=»427765″][kc_spacing height=»20″ _id=»635863″][/kc_column][/kc_row][kc_row _id=»975252″][kc_column _id=»313631″][kc_title text=»My4J0KDQtdC00LDQutGC0LjRgNC+0LLQsNC90LjQtSDRgdGD0YnQtdGB0YLQstGD0Y7RidC10LPQviBXb3JrZmxvdw==» _id=»656482″ type=»h2″ title_wrap=»yes» before=»PGEgbmFtZT0iMy0xIj48L2E+»][kc_title text=»My4xLgnQn9GA0LjQvNC10YAg0LrQsNGB0YLQvtC80LjQt9Cw0YbQuNC4INCf0L7RgdGC0LDQstGJ0LjQutC+0LI=» _id=»266396″ type=»h2″ title_wrap=»yes» before=»PGEgbmFtZT0iMy0xIj48L2E+»][kc_column_text _id=»531987″]

Рассмотрим пример «кастомизации» существующего Workflow, при котором требование пользователей звучит следующим образом:

В модуле Travel & Expense, используя Тип WF – TrvLineItemTemplate (см. рис ниже) модифицировать существующий Workflow

[/kc_column_text][kc_single_image image_size=»full» _id=»862953″ image_source=»media_library» image=»4131″ on_click_action=»lightbox» css_custom=»{`kc-css`:{`any`:{`image-style`:{`text-align|`:`left`,`padding|img`:`inherit inherit 20px inherit`}}}}»][kc_column_text _id=»472428″]

Необходимо изменить иерархию для Assignment – дать возможность выбирать Project manager точно так же, как и в Workflow TSTimesheetLineTemplate (см. рис ниже)

[/kc_column_text][kc_single_image image_size=»full» _id=»409708″ image_source=»media_library» image=»4133″ on_click_action=»lightbox» css_custom=»{`kc-css`:{`any`:{`image-style`:{`text-align|`:`left`,`padding|img`:`inherit inherit 20px inherit`}}}}»][/kc_column][/kc_row][kc_row _id=»845726″][kc_column width=»12/12″ video_mute=»no» _id=»197098″][kc_spacing height=»20″ _id=»309320″][/kc_column][/kc_row][kc_row _id=»699248″][kc_column _id=»123617″][kc_title text=»My4yLgnQoNC10YjQtdC90LjQtQ==» _id=»18892″ type=»h2″ title_wrap=»yes» before=»PGEgbmFtZT0iMy0yIj48L2E+»][kc_column_text _id=»924838″]

До внесения изменений, в лукапе поля «Type of participants» отображались следующие значения (см. рис ниже).

[/kc_column_text][kc_single_image image_size=»full» _id=»992623″ image_source=»media_library» image=»4137″ on_click_action=»lightbox» css_custom=»{`kc-css`:{`any`:{`image-style`:{`text-align|`:`left`,`padding|img`:`inherit inherit 20px inherit`}}}}»][kc_column_text _id=»64104″]

Type of participants Provider name
Budget manager participants BudgetManagerParticipantProvider
Expenditure participants TrvExpenditureParticipantProvider
Security role participants WorkflowRoleParticipantProvider
User group participants WorkflowUserGroupParticipantProvider
Project participants ProjWorkflowParticipantProvider

[/kc_column_text][kc_single_image image_size=»full» _id=»626813″ image_source=»media_library» image=»4138″ on_click_action=»lightbox» css_custom=»{`kc-css`:{`any`:{`image-style`:{`text-align|`:`left`,`padding|img`:`inherit inherit 20px inherit`}}}}»][kc_column_text _id=»292272″]

Данные значения («Budget manager participants», «Expenditure participants», «Security role participants», «User group participants ») отображаются в «лукапе» потому, что в узле Workflow Type для провайдеров «Budget manager participants», «Expenditure participants» указан тип документооборота TrvLineItemTemplate (см. рис ниже)

[/kc_column_text][kc_single_image image_size=»full» _id=»136293″ image_source=»media_library» image=»4139″ on_click_action=»lightbox» css_custom=»{`kc-css`:{`any`:{`image-style`:{`text-align|`:`left`,`padding|img`:`inherit inherit 20px inherit`}}}}»][kc_column_text _id=»784135″]

а у провайдеров «Security role participants», « User group participants » в свойстве «AssosiationType» указано значение «Global» (см. рис ниже) — т.е. данные провайдеры отображаются для всех Workflow, всех модулей.

[/kc_column_text][kc_single_image image_size=»full» _id=»965448″ image_source=»media_library» image=»4141″ on_click_action=»lightbox» css_custom=»{`kc-css`:{`any`:{`image-style`:{`text-align|`:`left`,`padding|img`:`inherit inherit 20px inherit`}}}}»][kc_column_text _id=»953150″]

— Для того, чтобы в лукапе поля «Type of participants» появилось значение «Project participants», необходимо в узле Workflow->Providers->ParticipantAssignment->ProjWorkflowParticipantProvider добавить WorkflowTypeTrvLineItemTemplate.
— Таким образом, мы сделали следующее: мы сказали поставщику ProvidersProjWorkflowParticipantProvider чтобы и для документооборота, который использует тип WorkflowTypeTrvLineItemTemplate поставлялись те участники, которые формирует класс ProjWorkflowParticipantProvider

[/kc_column_text][kc_single_image image_size=»full» _id=»992367″ image_source=»media_library» image=»4142″ on_click_action=»lightbox» css_custom=»{`kc-css`:{`any`:{`image-style`:{`text-align|`:`left`,`padding|img`:`inherit inherit 20px inherit`}}}}»][kc_column_text _id=»231646″]

Добавив тип Workflow в соответствующий/одноимённый узел поставщика ProjWorkflowParticipantProvider и осуществив Build->Generate Incremental CIL (кроме этого может также понадобиться очистка caches: Tools->Caches…), мы увидим значение «Project participants» (сравните с рисунком приведённым ранее)

[/kc_column_text][kc_single_image image_size=»full» _id=»738094″ image_source=»media_library» image=»4144″ on_click_action=»lightbox» css_custom=»{`kc-css`:{`any`:{`image-style`:{`text-align|`:`left`,`padding|img`:`inherit inherit 20px inherit`}}}}»][kc_single_image image_size=»full» _id=»850678″ image_source=»media_library» image=»4145″ on_click_action=»lightbox» css_custom=»{`kc-css`:{`any`:{`image-style`:{`text-align|`:`left`,`padding|img`:`inherit inherit 20px inherit`}}}}»][kc_column_text _id=»714105″]

Кроме действий, описанных выше, могут понадобится некоторые модификации в классе ProjWorkflowParticipantProvider, а именно в методах getParticipantTokens() и resolve()
— Метод getParticipantTokens() вызывается из редактора документооборота, для отображения списка token‘ов участников доступных для выбора. Token‘ы участников – это ID ролей пользователей или названия групп пользователей.
— Метод resolve() вызывается в момент, когда запускается/is started задание или ШАГ утверждения и в этот момент определяется пользователь, которому назначено задание. Например, в нашем случае, курсор находится на строке таблицы TrvExpTrans. По полю ProjId находится соответствующая строка в таблице ProjTable, и в том случае, если Type of participant== Project participants и Participant==Project Manager, то пользователю, который указан в значении поля ProjTable.WorkerResponsible будет назначен данный Workflow на одобрение

// 01.07.16 OIRagoza
else if(_context.parmTableId() == tableNum(TrvExpTrans))
{
//we need to add TSTimesheetLine table to the query in order to follow the main logic of this method: after if(queryRun.next()) line
qbds = qbds.addDataSource(tableNum(TSTimesheetLine));
qbds.joinMode(JoinMode::InnerJoin);
qbds.relations(false);
qbds.addLink(fieldNum(TrvExpTrans,ProjId),fieldNum(TSTimesheetLine,ProjId));

//add range on the TrvExpTrans.RecId field
qbds.addRange(dicTable.fieldName2Id(#RecIdField)).value(queryValue(_context.parmRecId()));
}
// 01.07.16 OIRagoza

[/kc_column_text][kc_single_image image_size=»full» _id=»705921″ image_source=»media_library» image=»4148″ on_click_action=»lightbox» css_custom=»{`kc-css`:{`any`:{`image-style`:{`text-align|`:`left`,`padding|img`:`inherit inherit 20px inherit`}}}}»][kc_single_image image_size=»full» _id=»793239″ image_source=»media_library» image=»4149″ on_click_action=»lightbox» css_custom=»{`kc-css`:{`any`:{`image-style`:{`text-align|`:`left`,`padding|img`:`inherit inherit 20px inherit`}}}}»][/kc_column][/kc_row][kc_row _id=»704989″][kc_column width=»12/12″ video_mute=»no» _id=»711495″][kc_spacing height=»20″ _id=»254526″][/kc_column][/kc_row][kc_row _id=»933199″][kc_column _id=»397193″][kc_title text=»My4zLgnQm9C+0LPQuNGA0L7QstCw0L3QuNC1INC40YHRgtC+0YDQuNC4INC40LfQvNC10L3QtdC90LjRjyDRgdGC0LDRgtGD0YHQvtCyIFdGDQo=» _id=»180071″ type=»h2″ title_wrap=»yes» before=»PGEgbmFtZT0iMy0zIj48L2E+»][kc_column_text _id=»574022″ css_custom=»{`kc-css`:{}}»]

Иногда может возникнуть требование записывать историю изменения статусов документа документооборота для в рамках некого WF процесса.

3.3.1. Сигнатура WorkflowTrackingTable.saveTracking()

Одними из способов как можно это сделать – это в своём кастомном классе обработчике создать PostEventHandler для метода таблицы WorkflowTrackingTable.saveTracking()

public static void saveTracking(SysWorkflowTracking _tracking)

{

}

Как видно из сигнатуры метода, в качестве параметра данному методу передаётся объект класса SysWorkflowTracking

Класс SysWorkflowTracking содержит достаточное количество полей и parm методов чтобы извлечь необходимую информацию.

[/kc_column_text][kc_single_image image_size=»full» _id=»637454″ image_source=»media_library» image=»4152″ on_click_action=»lightbox» css_custom=»{`kc-css`:{`any`:{`image-style`:{`text-align|`:`left`,`padding|img`:`inherit inherit 20px inherit`}}}}»][kc_column_text _id=»776596″]

1.1.1. Пример в ClassOITWarrTableEH (OITWarranty model)

Один из примеров реализации вышеописанного подхода: класс OITWarrTableEH (OITWarranty model)

///

/// Event handler for insert method on WorkflowTrackingStatusTable. In order to track the WF custom created field status,

/// the record to OITWarrRequestWFApprovalLog table is inserted when the status of the document is changed

///

///

/// XppPrePostArgs for WorkflowTrackingStatusTable.insert() table method.

///

///

/// 01.09.16 OIT3235_WarrantyRequest_WF_ORAG ORagoza: initial

/// , 31.10.2017, ORAG. Added code for OITWarrClaimWFApprovalLog table filling

///

[PostHandlerFor(tableStr(WorkflowTrackingTable), tableStaticMethodStr(WorkflowTrackingTable, saveTracking))]

public static void wFTrackingStatusTblSaveTrackingPost(XppPrePostArgs _args)

{

WorkflowTrackingStatusTable workflowTrackingStatusTable;

OITWarrRequestWFApprovalLog requestWFApprovalLog;

CustConfirmJour custConfirmJour;

OITWarrClaimWFApprovalLog claimWFApprovalLog;

CustInvoice4PaymJour_RU custInvoice4PaymJour;

SysWorkflowTracking tracking;

#define.tracking(«_tracking»);

tracking = _args.getArg(#tracking);

if (_args.getCalledWhen() == XppEventHandlerCalledWhen::Post)

{

//since this method is being invoked as Post event, we expect that workflowTrackingStatusTable record is exist

workflowTrackingStatusTable = WorkflowTrackingStatusTable::findByCorrelation(tracking.parmCorrelationId());

if (workflowTrackingStatusTable

&& (workflowTrackingStatusTable.ContextTableId == tableNum(CustConfirmJour)

|| (workflowTrackingStatusTable.ContextTableId == tableNum(CustInvoice4PaymJour_RU))))

{

switch (workflowTrackingStatusTable.ContextTableId)

{

case tableNum(CustConfirmJour):

custConfirmJour = CustConfirmJour::findRecId(workflowTrackingStatusTable.ContextRecId);

if (custConfirmJour.RecId != 0)

{

ttsBegin;

requestWFApprovalLog.clear();

requestWFApprovalLog.CorrelationId = workflowTrackingStatusTable.CorrelationId;

requestWFApprovalLog.CustConfirmJour = custConfirmJour.RecId;

requestWFApprovalLog.WorkflowTrackingStatusTable = workflowTrackingStatusTable.RecId;

requestWFApprovalLog.Comment = tracking.parmTrackingComment();

requestWFApprovalLog.User = tracking.parmWorkflowUser();

requestWFApprovalLog.ClientComplaintDescription = custConfirmJour.OITWarrClientComplaintDescription;

requestWFApprovalLog.DiagnosisAndConclusions = custConfirmJour.OITWarrDiagnosisAndConclusions;

requestWFApprovalLog.Company = curExt();

//Submitted

if (tracking.parmTrackingContext() == WorkflowTrackingContext::Workflow &&

tracking.parmTrackingStatus() == WorkflowTrackingStatus::Pending &&

tracking.parmTrackingType() == WorkflowTrackingType::Submission)

{

requestWFApprovalLog.ApprovalDocumentStatusWF = OITWarrApprovalDocumentStatus::Submitted;

requestWFApprovalLog.insert();

}

//Accepted

if (tracking.parmTrackingContext() == WorkflowTrackingContext::WorkItem &&

tracking.parmTrackingStatus() == WorkflowTrackingStatus::Pending &&

tracking.parmTrackingType() == WorkflowTrackingType::Accepted)

{

requestWFApprovalLog.ApprovalDocumentStatusWF = OITWarrApprovalDocumentStatus::Submitted;

requestWFApprovalLog.insert();

}

//ReSubmitted after correction

if (tracking.parmTrackingContext() == WorkflowTrackingContext::Task &&

tracking.parmTrackingStatus() == WorkflowTrackingStatus::Pending &&

tracking.parmTrackingType() == WorkflowTrackingType::Resubmission)

{

requestWFApprovalLog.ApprovalDocumentStatusWF = OITWarrApprovalDocumentStatus::Adjusted;

requestWFApprovalLog.insert();

}

//Approved (WorkflowTasksOITWarrRequestTaskOutcomesApproved == Type-Complete

if (tracking.parmTrackingContext() == WorkflowTrackingContext::WorkItem &&

tracking.parmTrackingStatus() == WorkflowTrackingStatus::Pending &&

tracking.parmTrackingType() == WorkflowTrackingType::Completion) //Type-Complete

{

requestWFApprovalLog.ApprovalDocumentStatusWF = OITWarrApprovalDocumentStatus::Approved;

requestWFApprovalLog.insert();

}

//Correct (WorkflowTasksOITWarrRequestTaskOutcomesCorrect == Type-Return

if (tracking.parmTrackingContext() == WorkflowTrackingContext::WorkItem &&

tracking.parmTrackingStatus() == WorkflowTrackingStatus::Pending &&

tracking.parmTrackingType() == WorkflowTrackingType::Return) //Type-Return

{

requestWFApprovalLog.ApprovalDocumentStatusWF = OITWarrApprovalDocumentStatus::Correct;

requestWFApprovalLog.insert();

}

//Rejected (WorkflowTasksOITWarrRequestTaskOutcomesCorrect == Type-RequestChange

if (tracking.parmTrackingContext() == WorkflowTrackingContext::WorkItem &&

tracking.parmTrackingStatus() == WorkflowTrackingStatus::Pending &&

tracking.parmTrackingType() == WorkflowTrackingType::RequestChange) //Type-RequestChange

{

requestWFApprovalLog.ApprovalDocumentStatusWF = OITWarrApprovalDocumentStatus::Correct;

requestWFApprovalLog.insert();

}

//Cancelled

if (tracking.parmTrackingContext() == WorkflowTrackingContext::Workflow &&

tracking.parmTrackingStatus() == WorkflowTrackingStatus::Cancelled &&

tracking.parmTrackingType() == WorkflowTrackingType::Cancellation)

{

requestWFApprovalLog.ApprovalDocumentStatusWF = OITWarrApprovalDocumentStatus::Cancelled;

requestWFApprovalLog.insert();

}

ttsCommit;

}

break;

case tableNum(CustInvoice4PaymJour_RU):

ttsBegin;

custInvoice4PaymJour = CustInvoice4PaymJour_RU::findRecId(workflowTrackingStatusTable.ContextRecId);

if (custInvoice4PaymJour.RecId != 0)

{

claimWFApprovalLog.clear();

claimWFApprovalLog.CorrelationId = workflowTrackingStatusTable.CorrelationId;

claimWFApprovalLog.CustInvoice4PaymJour = custInvoice4PaymJour.RecId;

claimWFApprovalLog.WorkflowTrackingStatusTable = workflowTrackingStatusTable.RecId;

claimWFApprovalLog.Comment = tracking.parmTrackingComment();

claimWFApprovalLog.User = tracking.parmWorkflowUser();

claimWFApprovalLog.ClientComplaintDescription = custInvoice4PaymJour.OITWarrClientComplaintDescription;

claimWFApprovalLog.RepairsDescription = custInvoice4PaymJour.OITWarrRepairsDescription;

claimWFApprovalLog.Company = curExt();

//Submitted

if (tracking.parmTrackingContext() == WorkflowTrackingContext::Workflow &&

tracking.parmTrackingStatus() == WorkflowTrackingStatus::Pending &&

tracking.parmTrackingType() == WorkflowTrackingType::Submission)

{

claimWFApprovalLog.ClaimsDocStatus = OITWarrWarrantyClaimsDocStatus::Submitted;

claimWFApprovalLog.insert();

}

//Accepted

if (tracking.parmTrackingContext() == WorkflowTrackingContext::WorkItem &&

tracking.parmTrackingStatus() == WorkflowTrackingStatus::Pending &&

tracking.parmTrackingType() == WorkflowTrackingType::Accepted)

{

claimWFApprovalLog.ClaimsDocStatus = OITWarrWarrantyClaimsDocStatus::Submitted;

claimWFApprovalLog.insert();

}

//ReSubmitted after correction

if (tracking.parmTrackingContext() == WorkflowTrackingContext::Task &&

tracking.parmTrackingStatus() == WorkflowTrackingStatus::Pending &&

tracking.parmTrackingType() == WorkflowTrackingType::Resubmission)

{

claimWFApprovalLog.ClaimsDocStatus = OITWarrWarrantyClaimsDocStatus::Adjusted;

claimWFApprovalLog.insert();

}

//Approved (WorkflowTasksOITWarrClaimTaskOutcomesApproved == Type-Complete

if (tracking.parmTrackingContext() == WorkflowTrackingContext::WorkItem &&

tracking.parmTrackingStatus() == WorkflowTrackingStatus::Pending &&

tracking.parmTrackingType() == WorkflowTrackingType::Completion) //Type-Complete

{

claimWFApprovalLog.ClaimsDocStatus = OITWarrWarrantyClaimsDocStatus::ApprovedByDistributor;

claimWFApprovalLog.insert();

}

//Correct (WorkflowTasksOITWarrClaimTaskOutcomesCorrect == Type-Return

if (tracking.parmTrackingContext() == WorkflowTrackingContext::WorkItem &&

tracking.parmTrackingStatus() == WorkflowTrackingStatus::Pending &&

tracking.parmTrackingType() == WorkflowTrackingType::Return) //Type-Return

{

claimWFApprovalLog.ClaimsDocStatus = OITWarrWarrantyClaimsDocStatus::Correct;

claimWFApprovalLog.insert();

}

//Rejected (WorkflowTasksOITWarrRequestTaskOutcomesCorrect == Type-RequestChange

if (tracking.parmTrackingContext() == WorkflowTrackingContext::WorkItem &&

tracking.parmTrackingStatus() == WorkflowTrackingStatus::Pending &&

tracking.parmTrackingType() == WorkflowTrackingType::RequestChange) //Type-RequestChange

{

claimWFApprovalLog.ClaimsDocStatus = OITWarrWarrantyClaimsDocStatus::Rejected;

claimWFApprovalLog.insert();

}

//Cancelled

if (tracking.parmTrackingContext() == WorkflowTrackingContext::Workflow &&

tracking.parmTrackingStatus() == WorkflowTrackingStatus::Cancelled &&

tracking.parmTrackingType() == WorkflowTrackingType::Cancellation)

{

claimWFApprovalLog.ClaimsDocStatus = OITWarrWarrantyClaimsDocStatus::Cancelled;

claimWFApprovalLog.insert();

}

}

ttsCommit;

break;

}

}

}

}

[/kc_column_text][kc_column_text _id=»499309″]

3.3.3. Все parm методы класса SysWorkflowTracking

WorkflowConfigurationId configurationId = tracking.parmConfigurationId();

CompanyId contextCompanyId = tracking.parmContextCompanyId();

RecId contextRecId = tracking.parmContextRecId();

TableId сontextTableId = tracking.parmContextTableId();

WorkflowCorrelationId correlationId = tracking.parmCorrelationId();

WorkflowDateTime dueDate = tracking.parmDueDate();

WorkflowElementId elementId = tracking.parmElementId();

LabelId messageLabelId = tracking.parmMessageLabelId();

WorkflowTrackingName name = tracking.parmName();

WorkflowOutcomeName outcome = tracking.parmOutcome();

RecId parallelBranchId = tracking.parmParallelBranchId();

WorkflowCorrelationId rootCorrelationId = tracking.parmRootCorrelationId();

WorkflowStepId stepId = tracking.parmStepId();

WorkflowActivityInstanceId subWorkflowActivityId = tracking.parmSubWorkflowActivityId();

WorkflowSubWorkflowId subWorkflowId = tracking.parmSubWorkflowId();

WorkflowSequence subWorkSequence= tracking.parmSubWorkSequence();

WorkflowUser toWorkflowUser = tracking.parmToWorkflowUser();

WorkflowComment trackingComment = tracking.parmTrackingComment();

WorkflowTrackingContext trackingContext = tracking.parmTrackingContext();

WorkflowTrackingId trackingId = tracking.parmTrackingId();

WorkflowTrackingMessage trackingMessage = tracking.parmTrackingMessage();

WorkflowTrackingStatus trackingStatus = tracking.parmTrackingStatus();

WorkflowTrackingStatusWorkflowType trackingStatusWorkflowType = tracking.parmTrackingStatusWorkflowType();

WorkflowTrackingType trackingType = tracking.parmTrackingType();

WorkflowInstanceNumber workflowInstanceNumber = tracking.parmWorkflowInstanceNumber();

WorkflowUser workflowOwner = tracking.parmWorkflowOwner();

WorkflowUser workflowUser = tracking.parmWorkflowUser();

WorkflowActivityInstanceId workItemActivityInstanceId = tracking.parmWorkItemActivityInstanceId();

WorkflowWorkItemInstanceId workItemId = tracking.parmWorkItemId();

RecId workItemQueueId = tracking.parmWorkItemQueueId();

[/kc_column_text][/kc_column][/kc_row][kc_row _id=»178353″][kc_column width=»12/12″ video_mute=»no» _id=»507353″][kc_spacing height=»20″ _id=»133748″][/kc_column][/kc_row][kc_row _id=»860972″][kc_column _id=»834708″][kc_title text=»NC4gV29ya2Zsb3cg0LTQu9GPINC/0L7RgdGC0YDQvtGH0L3QvtCz0L4g0YPRgtCy0LXRgNC20LTQtdC90LjRjyDQtNC+0LrRg9C80LXQvdGC0LA=» _id=»774883″ type=»h2″ title_wrap=»yes» before=»PGEgbmFtZT0iNCI+PC9hPg==»][kc_title text=»NC4xLgnQntCx0YnQtdC1″ _id=»475331″ type=»h2″ title_wrap=»yes» before=»PGEgbmFtZT0iNC0xIj48L2E+»][kc_column_text _id=»443805″]

Для реализации требования создания Workflow с построчным утверждением, необходимо реализовать следующие требования:

  1. Создать Workflow (один или более) для обработки строк документа;
  2. Создать Workflow для обработки непосредственно документа;

Например, существует требование создания Workflow для построчного утверждения строк Timesheet, в которых указан «project ID». Пример таблицы соответствия «Код проекта» -> «Руководитель проекта» указана ниже:

Project ID Project manager
1111 Mary
2222 Hans
3333 Jen

Предположим, что в соответствии с требованием, каждая строка Timesheet должна быть утверждена соответствующим руководителем проекта, при таком требовании, процесс утверждения вероятнее всего будет выглядеть следующим образом:

[/kc_column_text][kc_single_image image_size=»full» _id=»843912″ image_source=»media_library» image=»4158″ on_click_action=»lightbox» css_custom=»{`kc-css`:{`any`:{`image-style`:{`text-align|`:`left`,`padding|img`:`inherit inherit 20px inherit`}}}}»][kc_column_text _id=»318547″]

Workflow, который будет использоваться для утверждения всего документа вероятнее всего будет выглядеть следующим образом:

[/kc_column_text][kc_single_image image_size=»full» _id=»869843″ image_source=»media_library» image=»4160″ on_click_action=»lightbox» css_custom=»{`kc-css`:{`any`:{`image-style`:{`text-align|`:`left`,`padding|img`:`inherit inherit 20px inherit`}}}}»][kc_column_text _id=»445196″]

Для того, чтобы понять каким образом осуществляется утверждение всего документа, предположим, что Mike отправил на одобрение Timesheet содержащим 42 часа. Данное действие запустило построчный Workflow, что в свою очередь, запустит Workflow для всего документа. При таком сценарии будут осуществлены следующие события:

  1. Строка документа с проектом 1111 будет отправлена на утверждение к Mary;
  2. Строка документа с проектом 2222 будет отправлена на утверждение к Hans;
  3. Строка документа с проектом 13333 111 будет отправлена на утверждение к Jen;

Когда все строки будут утверждены соответствующими руководителями проектов, документ «перейдёт» на утверждение к Dianne (вице-президент), т.к. задано условие, что все Timesheet содержащие более 40 часов должны быть утверждены Dianne. После того, как Dianne утвердит Timesheet Worflow документа будет завершён.

При условии использования стандартного («не кастомизированного») приложения MSDAX, документ будет отправлен на финальное утверждение только после того, как будут утверждены все строки документа.

Может возникнуть требование: реализовать возможность утверждения документа для тех случаев, когда не все строки документа утверждены. В таком случае необходимо осуществлять модификацию/разработку функционала.

[/kc_column_text][/kc_column][/kc_row][kc_row _id=»18795″][kc_column width=»12/12″ video_mute=»no» _id=»542898″][kc_spacing height=»20″ _id=»103786″][/kc_column][/kc_row][kc_row _id=»780168″][kc_column _id=»809663″][kc_title text=»NC4yLgnQoNC10LDQu9C40LfQsNGG0LjRjyDQv9C+0YHRgtGA0L7Rh9C90L7Qs9C+INGD0YLQstC10YDQttC00LXQvdC40Y8gV29ya2Zsb3cg0LTQu9GPIFRpbWVzaGVldA0K» _id=»391430″ type=»h2″ title_wrap=»yes» before=»PGEgbmFtZT0iNC0yIj48L2E+»][kc_column_text _id=»655395″]

В нашем конкретном примере (построчного утверждения строк Timesheet «некастомизированного» приложения MSDAX), для того, чтобы создать Workflow с построчным утверждением необходимо выполнить ряд условий, рассмотрим, как эти обязательные условия реализованы.

По пути: Project management and accounting/Common/Timesheets/Timesheets for my review (+ двойной клик по строке формы TSTimesheetsApprovalListPage)

[/kc_column_text][kc_single_image image_size=»full» _id=»237552″ image_source=»media_library» image=»4163″ on_click_action=»lightbox» css_custom=»{`kc-css`:{`any`:{`image-style`:{`text-align|`:`left`,`padding|img`:`inherit inherit 20px inherit`}}}}»][kc_column_text _id=»50854″]

Форма View timesheet (TSTimesheetEntry)

[/kc_column_text][kc_single_image image_size=»full» _id=»762246″ image_source=»media_library» image=»4164″ on_click_action=»lightbox» css_custom=»{`kc-css`:{`any`:{`image-style`:{`text-align|`:`left`,`padding|img`:`inherit inherit 20px inherit`}}}}»][kc_column_text _id=»665511″]

Рассмотрим детальнее некоторые таблицы, находящиеся в DataSource формы TSTimesheetEntry, а именно таблицу TSTimesheetTable и TSTimesheetLineЗАГОЛОВОК» и «СТРОКИ» соответственно).

[/kc_column_text][kc_single_image image_size=»full» _id=»431299″ image_source=»media_library» image=»4165″ on_click_action=»lightbox» css_custom=»{`kc-css`:{`any`:{`image-style`:{`text-align|`:`left`,`padding|img`:`inherit inherit 20px inherit`}}}}»][kc_column_text _id=»942528″]

У таблицы TSTimesheetLine есть связь с таблицей TSTimesheetTable по полю TimesheetNbr

[/kc_column_text][kc_single_image image_size=»full» _id=»357889″ image_source=»media_library» image=»4167″ on_click_action=»lightbox» css_custom=»{`kc-css`:{`any`:{`image-style`:{`text-align|`:`left`,`padding|img`:`inherit inherit 20px inherit`}}}}»][kc_column_text _id=»164072″]

Кроме того, у таблицы TSTimesheetLine есть поле TSTimesheetLine.ApprovalStatus (тип TSAppStatus) в котором непосредственно храниться статус утверждения строки. Статус предопределён элементами BE TSAppStatus

[/kc_column_text][kc_single_image image_size=»full» _id=»835753″ image_source=»media_library» image=»4168″ on_click_action=»lightbox» css_custom=»{`kc-css`:{`any`:{`image-style`:{`text-align|`:`left`,`padding|img`:`inherit inherit 20px inherit`}}}}»][kc_single_image image_size=»full» _id=»162612″ image_source=»media_library» image=»4169″ on_click_action=»lightbox» css_custom=»{`kc-css`:{`any`:{`image-style`:{`text-align|`:`left`,`padding|img`:`inherit inherit 20px inherit`}}}}»][kc_column_text _id=»190620″]

Название Элемента EnumValue Метка Рус Label Eng
1 All 0 Все All
2 Create 1 Черновик Draft
3 Submitted 2 Отправлено Submitted
4 Approved 3 Утверждено Approved
5 Returned 4 Возвращено Returned
6 Ready 5 Готово к переносу Ready for transfer
7 Transferred 10 Перенесено Transferred
8 Ledger 6 Разнесено Posted
9 Pending 9 На рассмотрении In review

У таблицы TSTimesheetTable есть следующие поля заслуживающие внимания:

  1. Поле TSTimesheetTable.ApprovalStatus (тип TSAppStatus, точно такой же и у поля таблицы TSTimesheetLine.ApprovalStatus) в котором храниться статус утверждения документа. Статус предопределён элементами BE TSAppStatus

Поле TSTimesheetTable.PostStatus (тип TSPostStatus). Данное поле хранит значение статуса разноски документа

[/kc_column_text][kc_single_image image_size=»full» _id=»240273″ image_source=»media_library» image=»4171″ on_click_action=»lightbox» css_custom=»{`kc-css`:{`any`:{`image-style`:{`text-align|`:`left`,`padding|img`:`inherit inherit 20px inherit`}}}}»][kc_column_text _id=»90474″]

Название Элемента EnumValue Метка Рус Label Eng
1 NotTransferred 0 Не перенесено Not transferred
2 Transferred 2 Перенесено Transferred
3 Posted 3 Разнесено Posted

Для узла Design формы TSTimesheetEntry определены следующие свойства

[/kc_column_text][kc_single_image image_size=»full» _id=»998183″ image_source=»media_library» image=»4173″ on_click_action=»lightbox» css_custom=»{`kc-css`:{`any`:{`image-style`:{`text-align|`:`left`,`padding|img`:`inherit inherit 20px inherit`}}}}»][kc_column_text _id=»43916″]

Где: WorkflowType==TSDocumentTemplate.

При внимательном рассмотрении узла AOTWorkflowWorkflowTypesTSDocumentTemplate, обнаружим следующее

[/kc_column_text][kc_single_image image_size=»full» _id=»861931″ image_source=»media_library» image=»4174″ on_click_action=»lightbox» css_custom=»{`kc-css`:{`any`:{`image-style`:{`text-align|`:`left`,`padding|img`:`inherit inherit 20px inherit`}}}}»][kc_column_text _id=»558539″]

Следует обратить внимание, что узел AOTWorkflowWorkflowTypesTSDocumentTemplateLineItemWorflow, у данного объекта AOT, заполнен (для сравнения – у большинства других объектов AOTWorkflowWorkflowTypes узел LineItemWorflow пуст. (cм. рис ниже). Соответственно вывод следующий: Для реализации Workflow для построчного утверждения, необходимо (помимо других модификаций, обязательных для реализации Workflow для документа) внести ряд дополнительных модификаций.

Примеры узлов AOTWorkflowWorkflowTypes для которых свойство LineItemWorflow НЕ заполнено. Соответственно данные WorkflowTypes используются исключительно за активации Workflow для документов (НЕ подразумевается построчное утверждение документа) Примеры узлов AOTWorkflowWorkflowTypes для которых свойство LineItemWorflow ЗАПОЛНЕНО. Данные WorkflowTypes используются за активации Workflow для строк документа, после утверждения которых появляется возможность утверждения непосредственно документа

[/kc_column_text][kc_column_text _id=»204132″]

В узел WorkflowTypesLineItemWorflow необходимо добавить узел WorkflowTypes, который отвечает за Workflow для строки документа. В нашем конкретном случае в узел AOTWorkflowWorkflowTypesTSDocumentTemplateLineItemWorflow был добавлен узел AOTWorkflowWorkflowTypesTSTimesheetLineTemplate у которого определены следующие свойства:

[/kc_column_text][kc_single_image image_size=»full» _id=»176567″ image_source=»media_library» image=»4179″ on_click_action=»lightbox» css_custom=»{`kc-css`:{`any`:{`image-style`:{`text-align|`:`left`,`padding|img`:`inherit inherit 20px inherit`}}}}»][kc_column_text _id=»69141″]

Попробуем для узла AOTWorkflowWorkflowTypesTSTimesheetLineTemplate создать элемент в узле LineItemWorflow (именно в этом конкретном случае данные действия являются бессмысленными, так как узел TSTimesheetLineTemplate уже является элементом Workflow для строк документа. Другими словами, мы пытаемся создать Workflow для строк документа строки документа таблицы TSTimesheetLine, что в принципе невозможно. Поэтому сделаем допущение, что нижеуказанные действия мы будет делать сугубо в учебных целях).

  1. Делаем клик правой клавишей по узлу «Line Item Workflows»

[/kc_column_text][kc_single_image image_size=»full» _id=»505425″ image_source=»media_library» image=»4181″ on_click_action=»lightbox» css_custom=»{`kc-css`:{`any`:{`image-style`:{`text-align|`:`left`,`padding|img`:`inherit inherit 20px inherit`}}}}»][kc_column_text _id=»678866″]

  1. Переименовываем появившийся узел (в нашем случае мы переименовали в OITTestLineApprovalTemplate)

[/kc_column_text][kc_single_image image_size=»full» _id=»984799″ image_source=»media_library» image=»4183″ on_click_action=»lightbox» css_custom=»{`kc-css`:{`any`:{`image-style`:{`text-align|`:`left`,`padding|img`:`inherit inherit 20px inherit`}}}}»][kc_single_image image_size=»full» _id=»447783″ image_source=»media_library» image=»4184″ on_click_action=»lightbox» css_custom=»{`kc-css`:{`any`:{`image-style`:{`text-align|`:`left`,`padding|img`:`inherit inherit 20px inherit`}}}}»][kc_column_text _id=»440431″]

Особого внимания заслуживает свойство «LineItemWorkflowRelation == TSTimeSheetLine»

Значение TSTimeSheetLine в данном свойстве означает следующее:

Как известно из раздела (2.5.Создание Документа Workflow) документом Workflow является класс (наследник класса WorkflowDocument), в нашем случае для утверждения «Документа» используется класс TSWorkflowTimesheet,

[/kc_column_text][kc_single_image image_size=»full» _id=»98284″ image_source=»media_library» image=»4186″ on_click_action=»lightbox» css_custom=»{`kc-css`:{`any`:{`image-style`:{`text-align|`:`left`,`padding|img`:`inherit inherit 20px inherit`}}}}»][kc_column_text _id=»683390″]

а для «Строк документа» используется класс TSWorkflowTimesheetLines

[/kc_column_text][kc_single_image image_size=»full» _id=»600502″ image_source=»media_library» image=»4187″ on_click_action=»lightbox» css_custom=»{`kc-css`:{`any`:{`image-style`:{`text-align|`:`left`,`padding|img`:`inherit inherit 20px inherit`}}}}»][kc_column_text _id=»323165″]

Так вот, метод класса TSWorkflowTimesheet.getQueryName() возвращает имя Query, который используется для одобрения документа

[/kc_column_text][kc_single_image image_size=»full» _id=»278543″ image_source=»media_library» image=»4189″ on_click_action=»lightbox» css_custom=»{`kc-css`:{`any`:{`image-style`:{`text-align|`:`left`,`padding|img`:`inherit inherit 20px inherit`}}}}»][kc_column_text _id=»623301″]

Развернув узел AOTQueriesTSWorkflowTimesheet обнаружим, что корневой таблицей для данного Query является таблица TSTimesheetTable в то время как связанной является таблица TSTimesheetLine.

[/kc_column_text][kc_single_image image_size=»full» _id=»447238″ image_source=»media_library» image=»4190″ on_click_action=»lightbox» css_custom=»{`kc-css`:{`any`:{`image-style`:{`text-align|`:`left`,`padding|img`:`inherit inherit 20px inherit`}}}}»][kc_column_text _id=»136117″]

Для сравнения, для другого типа Workflow, который также используется для построчного утверждения, а именно для узла AOTWorkflowWorkflowTypesVendInvoiceInfoLineLineItemWorkflowsVendProcessInvoiceLine, в свойстве LineItemWorkflowRelation на выбор доступны следующие опции:

[/kc_column_text][kc_single_image image_size=»full» _id=»404759″ image_source=»media_library» image=»4191″ on_click_action=»lightbox» css_custom=»{`kc-css`:{`any`:{`image-style`:{`text-align|`:`left`,`padding|img`:`inherit inherit 20px inherit`}}}}»][kc_single_image image_size=»full» _id=»845149″ image_source=»media_library» image=»4192″ on_click_action=»lightbox» css_custom=»{`kc-css`:{`any`:{`image-style`:{`text-align|`:`left`,`padding|img`:`inherit inherit 20px inherit`}}}}»][kc_column_text _id=»755102″]

Стоит обратить внимание, что в AOTQueryVendInvoiceDocument в узле DataSource для таблицы VendInvoiceInfoTable отображаются 4 таблицы

[/kc_column_text][kc_single_image image_size=»full» _id=»361837″ image_source=»media_library» image=»4194″ on_click_action=»lightbox» css_custom=»{`kc-css`:{`any`:{`image-style`:{`text-align|`:`left`,`padding|img`:`inherit inherit 20px inherit`}}}}»][kc_column_text _id=»849557″]

В то время как в узле AOTWorkflowWorkflowTypesVendInvoiceInfoLineLineItemWorkflowsVendProcessInvoiceLine, в свойстве LineItemWorkflowRelation на выбор доступны только 3 следующие таблицы:

[/kc_column_text][kc_single_image image_size=»full» _id=»775619″ image_source=»media_library» image=»4195″ on_click_action=»lightbox» css_custom=»{`kc-css`:{`any`:{`image-style`:{`text-align|`:`left`,`padding|img`:`inherit inherit 20px inherit`}}}}»][kc_column_text _id=»970927″]

Таблица (DataSource ) SourceDocumentLine недоступна потому, что в узле AOTQueryVendInvoiceDocumentVendInvoiceInfoTableDataSourceSourceDocumentLine свойство «FetchMode» установлено «1:1»

[/kc_column_text][kc_single_image image_size=»full» _id=»458390″ image_source=»media_library» image=»4197″ on_click_action=»lightbox» css_custom=»{`kc-css`:{`any`:{`image-style`:{`text-align|`:`left`,`padding|img`:`inherit inherit 20px inherit`}}}}»][kc_column_text _id=»767302″]

В то время как у остальных таблиц, это свойство содержит значение «1:n»

[/kc_column_text][kc_single_image image_size=»full» _id=»418817″ image_source=»media_library» image=»4198″ on_click_action=»lightbox» css_custom=»{`kc-css`:{`any`:{`image-style`:{`text-align|`:`left`,`padding|img`:`inherit inherit 20px inherit`}}}}»][kc_column_text _id=»465181″]

Если для таблицы (DataSource) SourceDocumentLine установить свойство «FetchMode» в значение «1:n», то эта таблица также будет доступна для выбора в свойстве LineItemWorkflowRelation.

[/kc_column_text][kc_single_image image_size=»full» _id=»166857″ image_source=»media_library» image=»4199″ on_click_action=»lightbox» css_custom=»{`kc-css`:{`any`:{`image-style`:{`text-align|`:`left`,`padding|img`:`inherit inherit 20px inherit`}}}}»][/kc_column][/kc_row][kc_row _id=»525146″][kc_column width=»12/12″ video_mute=»no» _id=»757938″][kc_spacing height=»20″ _id=»412843″][/kc_column][/kc_row][kc_row _id=»191035″][kc_column _id=»441468″][kc_title text=»NC4zLgnQkNC70LPQvtGA0LjRgtC8INGA0LDQsdC+0YLRiyDQv9C+0YHRgtGA0L7Rh9C90L7Qs9C+IFdvcmtmbG93DQo=» _id=»858144″ type=»h2″ title_wrap=»yes» before=»PGEgbmFtZT0iNC0zIj48L2E+»][kc_column_text _id=»43822″]

При открытии формы по пути: Project management and accounting/Common/Timesheets/Timesheets for my review (+ двойной клик по строке формы TSTimesheetsApprovalListPage), откроется форма View timesheet (FormsTSTimesheetEntry)

  1. При открытии формы, срабатывает метод формы canSubmitToWorkflow()

[/kc_column_text][kc_single_image image_size=»full» _id=»239840″ image_source=»media_library» image=»4203″ on_click_action=»lightbox» css_custom=»{`kc-css`:{`any`:{`image-style`:{`text-align|`:`left`,`padding|img`:`inherit inherit 20px inherit`}}}}»][kc_column_text _id=»492507″]

В данном случае метод таблицы TSTimesheetTable.canSubmitToWorkflow() не вызывается (нет вызова super())

Т.к. для дизайна данной формы применён WorkflowType==TSDocumentTemplate, который предполагает построчное утверждение, то после того, как форма загрузилась, пользователь, который наделён привилегией выполнять манипуляции, связанные с Workflow, может выполнять такие манипуляции выделяя необходимые строки. Выделив некую строку для осуществления Утверждения/Отклонения или других предусмотренных действий, для выделенной строки (таблицы TSTimesheetLine) будет вызван метод TSTimesheetLine.canSubmitToWorkflow().

[/kc_column_text][kc_single_image image_size=»full» _id=»798228″ image_source=»media_library» image=»4205″ on_click_action=»lightbox» css_custom=»{`kc-css`:{`any`:{`image-style`:{`text-align|`:`left`,`padding|img`:`inherit inherit 20px inherit`}}}}»][kc_column_text _id=»493162″]

Если вызванный метод TSTimesheetLine.canSubmitToWorkflow() «вернёт» значение «true», то на форме, для выделенной строки появится меню с предопределённым набором действий:

[/kc_column_text][kc_single_image image_size=»full» _id=»254567″ image_source=»media_library» image=»4206″ on_click_action=»lightbox» css_custom=»{`kc-css`:{`any`:{`image-style`:{`text-align|`:`left`,`padding|img`:`inherit inherit 20px inherit`}}}}»][kc_column_text _id=»856139″]

В зависимости от свойств узла AOTWorkflowApprovalsTSTimesheetLineApprovalOutcomes, вызывается соответствующий класс (в данном примере, для Outcomes==Approve и для Outcomes==Reject вызывается метод main() класса TSWorkflowActionManager

[/kc_column_text][kc_single_image image_size=»full» _id=»299311″ image_source=»media_library» image=»4207″ on_click_action=»lightbox» css_custom=»{`kc-css`:{`any`:{`image-style`:{`text-align|`:`left`,`padding|img`:`inherit inherit 20px inherit`}}}}»][kc_column_text _id=»925649″]

Так в свойствах каждого узла AOTWorkflowWorkflowType указаны классы, методы которых вызываются в среде CIL в результате соответствующих/выбранных действий пользователей, то в данном примере, для AOTWorkflowWorkflowTypeTSTimesheetLineTemplate после нажатия на кнопку «Approve» и отработки методов класса TSWorkflowActionManager будет вызван класс TSWorkflowEventHandler

[/kc_column_text][kc_single_image image_size=»full» _id=»740473″ image_source=»media_library» image=»4209″ on_click_action=»lightbox» css_custom=»{`kc-css`:{`any`:{`image-style`:{`text-align|`:`left`,`padding|img`:`inherit inherit 20px inherit`}}}}»][kc_column_text _id=»875934″]

Большая часть объектов AOT, о которых шла речь в данном разделе отображены на рисунке ниже. Воспользуйтесь данным рисунком для использования существующего кода при разработке собственных реализаций.

[/kc_column_text][kc_single_image image_size=»full» _id=»4713″ image_source=»media_library» image=»4210″ on_click_action=»lightbox» css_custom=»{`kc-css`:{`any`:{`image-style`:{`text-align|`:`left`,`padding|img`:`inherit inherit 20px inherit`}}}}»][/kc_column][/kc_row][kc_row _id=»435073″][kc_column width=»12/12″ video_mute=»no» _id=»668706″][kc_spacing height=»20″ _id=»862303″][/kc_column][/kc_row][kc_row _id=»424406″][kc_column _id=»318713″][kc_title text=»NS4J0J/QvtC40YHQuiDQvdC10LjRgdC/0YDQsNCy0L3QvtGB0YLQtdC5DQo=» _id=»235123″ type=»h2″ title_wrap=»yes» before=»PGEgbmFtZT0iNSI+PC9hPg==»][kc_title text=»NS4xLglXb3JrZmxvdyDRgNCw0LHQvtGC0LDQuyDQutC+0YDRgNC10LrRgtC90L4sINC90L4g0L/QviDQv9GA0L7RiNC10YHRgtCy0LjQuCDQvdC10LrQvtGC0L7RgNC+0LPQviDQstGA0LXQvNC10L3QuCAtINC90LUg0L/QtdGA0LXRhdC+0LTQuNGCINCyINC+0LbQuNC00LDQtdC80YvQuSDRgdGC0LDRgtGD0YEuIA0K» _id=»818042″ type=»h2″ title_wrap=»yes» before=»PGEgbmFtZT0iNS0xIj48L2E+»][kc_column_text _id=»432916″]

  1. При просмотре истории (View history) – ошибки отсутствуют

[/kc_column_text][kc_single_image image_size=»full» _id=»264829″ image_source=»media_library» image=»4213″ on_click_action=»lightbox» css_custom=»{`kc-css`:{`any`:{`image-style`:{`text-align|`:`left`,`padding|img`:`inherit inherit 20px inherit`}}}}»][kc_column_text _id=»950626″]

Необходимо по пути System administration/Inquiries/Batch Jobs/Batch Jobs отфильтровать те пакетные задания, которые созданы для обработки Workflow (в данном примере это задания, в названии которых есть слово «work»,

[/kc_column_text][kc_single_image image_size=»full» _id=»46357″ image_source=»media_library» image=»4214″ on_click_action=»lightbox» css_custom=»{`kc-css`:{`any`:{`image-style`:{`text-align|`:`left`,`padding|img`:`inherit inherit 20px inherit`}}}}»][kc_column_text _id=»728127″]

по нему, собственно, и осуществляется поиск). После чего просмотреть (по нажатию на кнопку «Batch job history») те, которые НЕ НАХОДЯТСЯ в статусе «Withhold».

После того, как откроется форма «Batch job history» отсортировать по убыванию строки по колонке «Actual start date/time».

В примере на скриншоте ошибок в колонке «Status» — нет.

[/kc_column_text][kc_single_image image_size=»full» _id=»41521″ image_source=»media_library» image=»4215″ on_click_action=»lightbox» css_custom=»{`kc-css`:{`any`:{`image-style`:{`text-align|`:`left`,`padding|img`:`inherit inherit 20px inherit`}}}}»][kc_column_text _id=»322946″]

Если бы были ошибки, то статус отображался бы «Error». Один из примеров «Error»:

[/kc_column_text][kc_single_image image_size=»full» _id=»786469″ image_source=»media_library» image=»4217″ on_click_action=»lightbox» css_custom=»{`kc-css`:{`any`:{`image-style`:{`text-align|`:`left`,`padding|img`:`inherit inherit 20px inherit`}}}}»][kc_column_text _id=»411968″]

В данном случае, для некоторых, запущенных в работу, Workflow, были удалены документы документооборота (другими словами – удалены строки из таблицы над которой осуществляется Workflow (вышеописанного примера, Workflow был запущен для таблицы CustConfirmJour)), которые являлись строками запущенных документооборотов.

Ошибка, собственно говоря, возникает потому, что в таблице SysWorkflowMessageTable есть строки для которых нет соответствующих строк в таблице CustConfirmJour.

Данная ошибка устраняется тем, что при помощи job’а удаляются строки из таблицы SysWorkflowMessageTable для которых нет соответствующих строк в таблице CustConfirmJour

ВНИМАНИЕ! Очень осторожно запускать job’ы при помощи которых удаляются строки. На рабочей базе скорее всего таких строк будет немного (одна, максимум — две). Поэтому job можно запустить чтобы найти такую/такие строки и если их окажется более одной, то удалив их, может оказаться ситуация, что для связанных с таблицей SysWorkflowMessageTable других таблиц, эти строки были нужны.

static void Job10(Args _args)

{

SysWorkflowMessageTable sysWorkflowMessageTable;

CustConfirmJour custConfirmJour;

ttsBegin;

while select forUpdate sysWorkflowMessageTable

notexists join custConfirmJour

where sysWorkflowMessageTable.WorkflowContextRecId == custConfirmJour.RecId

{

if (sysWorkflowMessageTable.validateDelete())

{

sysWorkflowMessageTable.delete();

}

}

ttsCommit;

}

Кроме того, несколько советов по устранению подобных ошибок указаны здесь:

https://community.dynamics.com/ax/f/33/t/197730

https://community.dynamics.com/ax/f/33/t/195136

You can check the contents of the table SysWorkflowMessageTable.

If there are records where it cannot find a source document based on the fields WorkflowContextTableId and WorkflowContextRecId or if it cannot find a record in the table WorkflowTrackingStatusTable based on the field CorrelationID, there is an issue and you could possibly delete that record in the SysWorkflowMessageTable.

In this type of error, mostly the batch error will recur every 10 minutes.

  1. При просмотре истории (View history) – ошибки ПРИСУТСТВУЮТ (на screenshot’е ниже их нет, но допустим что статус одной из строк «Stopped (error)»)

[/kc_column_text][kc_single_image image_size=»full» _id=»131303″ image_source=»media_library» image=»4219″ on_click_action=»lightbox» css_custom=»{`kc-css`:{`any`:{`image-style`:{`text-align|`:`left`,`padding|img`:`inherit inherit 20px inherit`}}}}»][kc_column_text _id=»850966″]

В таком случае необходимо более детально смотреть описание ошибки в Event Viewer

[/kc_column_text][kc_single_image image_size=»full» _id=»729767″ image_source=»media_library» image=»4220″ on_click_action=»lightbox» css_custom=»{`kc-css`:{`any`:{`image-style`:{`text-align|`:`left`,`padding|img`:`inherit inherit 20px inherit`}}}}»][/kc_column][/kc_row][kc_row _id=»705432″][kc_column width=»12/12″ video_mute=»no» _id=»717400″][kc_spacing height=»20″ _id=»420891″][/kc_column][/kc_row][kc_row _id=»742683″][kc_column _id=»425297″][kc_title text=»NS4yLgnQo9C00LDQu9C10L3QuNC1INGB0YPRidC10YHRgtCy0YPRjtGJ0LXQs9C+IFdvcmtmbG93LiANCg==» _id=»827281″ type=»h2″ title_wrap=»yes» before=»PGEgbmFtZT0iNS0yIj48L2E+»][kc_column_text _id=»996203″]

  1. В том случае, если количество запущенных сущностей Workflow рано 0 (Instances==0), по кнопке Delete можно удалить существующий Workflow.

[/kc_column_text][kc_single_image image_size=»full» _id=»179983″ image_source=»media_library» image=»4222″ on_click_action=»lightbox» css_custom=»{`kc-css`:{`any`:{`image-style`:{`text-align|`:`left`,`padding|img`:`inherit inherit 20px inherit`}}}}»][kc_column_text _id=»592619″]

  1. Если же количество запущенных сущностей Workflow НЕ РАВНО 0 (на screenshot выше, Instances==4), то
    i. Необходимо обратиться к пользователям, для которых данные Workflow запущены, с просьбой одобрить либо применить Cancel к данных сущностям Workflow.
    ii. Если нет возможности/необходимости обращаться к пользователям, то по пути Home/Iquiries/Workflow/Workflow history открыть форму «Workflow history». На данной форме, в лукапе «Filter by statys» выбрать «All», и отфильтровать записи по необходимому Типу Workflow (Workflow == «Warranty request») и по документу Workflow (Document type == «Подтверждение заказа на продажу»). После чего, для всех строк, которых находятся в статусе «Pending» или «Stopped (error)» нажать кнопку «Recall».

[/kc_column_text][kc_single_image image_size=»full» _id=»439838″ image_source=»media_library» image=»4223″ on_click_action=»lightbox» css_custom=»{`kc-css`:{`any`:{`image-style`:{`text-align|`:`left`,`padding|img`:`inherit inherit 20px inherit`}}}}»][kc_column_text _id=»854332″]

Если же даже после нажатия на кнопку «Recall», строку не удалось перевести в статус «Canceled», то необходимо либо вручную изменить статус (поле WorkflowTrackingStatusTable.TrackingStatus), либо, если и это не помогло, необходимо при помощи job’а удалить строки из таблицы SysWorkflowTable для которых нет соответствующих строк в таблице WorkflowTable

static void Job10(Args _args)

{

WorkflowTable workflowTable;

SysWorkflowTable sysWorkflowTable;

ttsBegin;

while select forUpdate sysWorkflowTable

join workflowTable

where workflowTable.SequenceNumber == sysWorkflowTable.ConfigurationSequenceNumber

{

if (sysWorkflowTable.validateDelete())

{

sysWorkflowTable.delete();

}

}

ttsCommit;

}

Кроме того, несколько советов по устранению подобных ошибок указаны здесь:

https://community.dynamics.com/ax/f/33/t/77311

https://organicax.com/2016/03/25/deleting-a-workflow-configuration-that-has-running-instances/

[/kc_column_text][/kc_column][/kc_row][kc_row _id=»687959″][kc_column width=»12/12″ video_mute=»no» _id=»478238″][kc_spacing height=»20″ _id=»729348″][/kc_column][/kc_row][kc_row _id=»564412″][kc_column _id=»311419″][kc_title text=»NS4zLgnQm9C40YjQvdC40LUg0LfQsNC/0LjRgdC4INCyINGC0LDQsdC70LjRhtC1IFdvcmtmbG93V29ya0l0ZW1UYWJsZQ0K» _id=»875120″ type=»h2″ title_wrap=»yes» before=»PGEgbmFtZT0iNS0zIj48L2E+»][kc_column_text _id=»647209″]

Данная ошибка (см описание ниже) возникла по причине того, что в таблице WorkflowWorkItemTable были записи, для которых не было соответствующих строк в таблице WorkflowVersionTable, поэтому в методе SysWorkflowWorkItem.escalate(), в данном участке кода генерировалась ошибка

WorkflowVersionTable versionTable;

versionTable = WorkflowVersionTable::find(workItemContext.get_ConfigurationId());

if (!versionTable)

{

throw error(«@SYS111590»);

}

// ‘@SYS111590 // Не удалось найти версию workflow-процесса // Failed to find workflow version

> Dynamics.Ax.Application.dll862.netmodule!Dynamics.Ax.Application.SysWorkflowWorkItem.escalate(Dynamics.Ax.Application.WorkflowWorkItemTable _workItem, string _comment) Line 55 X++

Dynamics.Ax.Application.dll656.netmodule!Dynamics.Ax.Application.WorkflowWorkItem.escalateWorkItem(Dynamics.Ax.Application.WorkflowWorkItemTable _workItem) Line 28 X++

Dynamics.Ax.Application.dll219.netmodule!Dynamics.Ax.Application.WorkflowWorkItemDueDateJob.Run() Line 20 X++

Dynamics.Ax.Application.dll1.netmodule!Dynamics.Ax.Application.BatchRun.runJobStaticCode(long batchId) Line 54 X++

Dynamics.Ax.Application.dll1.netmodule!Dynamics.Ax.Application.BatchRun.runJobStatic(long batchId) Line 13 X++

[External Code]

Error 10/3/2016 10:48:05 AM AOS Runtime 0 None

Код экземпляра workflow-процесса: 003784,

Error 10/3/2016 10:47:52 AM AOS Runtime 0 None

X++ Exception: Не удалось найти бизнес-правило.

at SysWorkflow-internalFault

SysWorkflow-fault

SysWorkflowWorkItem-escalate

X++ Exception: Не удалось найти версию workflow-процесса (@SYS111590 «Failed to find workflow version»)

at SysWorkflowWorkItem-escalate

В сообщениях подобного плана

[/kc_column_text][kc_single_image image_size=»full» _id=»416483″ image_source=»media_library» image=»4226″ on_click_action=»lightbox» css_custom=»{`kc-css`:{`any`:{`image-style`:{`text-align|`:`left`,`padding|img`:`inherit inherit 20px inherit`}}}}»][kc_column_text _id=»374673″]

значение 003785 указывает на значение поля WorkflowTrackingStatusTable.InstanceNumber

Наиболее вероятные классы, в которых может генерироваться ошибка:

SysWorkflowQueue.invokeWorkflowEventHandler(),SysWorkflowQueue.invokeElementEventHandler()

SysWorkflowHelper.internalSendNotifications()

try

{

documentData.clear();ё

// verify that the user has access to the data — does a runas

sysWorkflowDocument = SysWorkflowDocument::newDocument(

_context.parmWorkflowContext(),

configTable.workflowTable().TemplateName,

users.getUser(i));

sysWorkflowDocument.parmDisplayMenuItem(menuItemName);

documentData = sysWorkflowDocument.getDocument();

}

catch

{

SysWorkflowHelper::writeEventLogEntry(strFmt(«@SYS136742», _context.parmWorkflowInstanceNumber(), strFmt(«@SYS126833», SysWorkflowHelper::getInfoLogMessage(line))));

infolog.clear(infologLine() —1);

}

SysWorkflowQueue.dispatch()

catch

{

messageText = SysWorkflowHelper::getInfoLogMessage(line);

SysWorkflow::fault(

workflowContext,

curUserId(),

messageText);

SysWorkflowHelper::writeEventLogEntry(strFmt(«@SYS136742», workflowTable.InstanceNumber, messageText));

infolog.clear(infologLine() —1);

}

Таблица, в которой хранятся все существующие WF — WorkflowTable

Отображение контролов (Бизнес правил, тех элементов, который доступны пользователю в меню Workflow) отображаются при таком стеке

[s] ClassesSysWorkflowFormControlsgetActionBarContentForWorkItem 21

[c] ClassesSysWorkflowFormControlsshowWorkItemActions 23

[c] ClassesSysWorkflowFormControlsupdateControls 53

[c] ClassesSysSetupFormRunupdateWorkflowControls 4

[c] ClassesInfoformNotify

Стек заполнения таблицы WorkflowWorkItemTable (таблица, которая содержит элементы для отображения утверждающему сотруднику)

Запись данных в таблицу

ClassesWorkflowWorkItemisUserApprovalOwner

ClassesWorkflowWorkItemisUserTaskOwner

Data DictionaryTablesWorkflowWorkItemTableMethodsexist

Data DictionaryTablesWorkflowWorkItemTableMethodsfind

Data DictionaryTablesWorkflowWorkItemTableMethodsinitFromWorkflowWorkItemTable

Data DictionaryTablesWorkflowWorkItemTableMethodsfindActivityInstanceId

Data DictionaryTablesWorkflowWorkItemTableMethodsfindPendingActivityInstanceId

Data DictionaryTablesWorkflowWorkItemTableMethodsfindRecId

ClassesWorkflowCancelManagergetWorkflowsToCancel

ClassesWorkflowWorkItemdelegateWorkItem

ClassesWorkflowWorkItemtakeAction

ClassesWorkflowWorkItemActionManagermain

ClassesSysWorkflowCLRInteropHelpercreateWorkItemOutcome

ClassesSysWorkflowEventDispatcherraiseWorkItemAcknowledgementEvent 26

ClassesSysWorkflowEventDispatcheronAcknowledgeWorkItem 7

ClassesSysWorkflowEventDispatcherdispatch 119

ClassesSysWorkflowMessageQueueTaskrun 9, 21

ClassesSysWorkflowMessageQueueManagerrun 20

ClassesSysWorkflowEventDispatcherraiseWorkItemAcknowledgementEvent

ClassesSysWorkflowWorkItemServicecompleteStepWorkItems

ClassesSysWorkflowWorkItemServicecompleteStepWorkItems

ClassesSysWorkflowWorkItemServiceremoveWorkItems

ClassesSysWorkflowWorkItemServiceremoveWorkItems

ClassesSysWorkflowEventDispatcheronWorkItemClaimed

ClassesSysWorkflowEventDispatcheronWorkItemComplete

ClassesSysWorkflowEventDispatcheronWorkItemCreate

ClassesWorkflowfindWorkItemsForCurrentUser

ClassesWorkflowWorkItemgetOutcomes

ClassesWorkflowWorkItemgetWorkItems

ClassesWorkflowWorkItemActionDialogparmWorkItem

NO ClassesWorkflowWorkItemDueDateJobrun

Work Item not getting created for all Workflows — How to trouble shoot

https://community.dynamics.com/ax/f/33/t/174087

03.08.2017 | WorkflowWorkItemTable empty

https://community.dynamics.com/ax/f/33/t/244442

I fixed the issue, thanks.

It was a setup issue.

Calendar exist, but time scheduler did’nt exist.

03.08.2017 | ParticipantProvider in Dynamics AX 2009 Workflow Approval

http://www.amerax.net/2010/02/participantprovider-in-dynamics-ax-2009-workflow-approval/

One of the important classes that you need to set at the approval process is the participant provider. The goal of this property is for the workflow to decide who will be the participant user for this step of workflow. To do that, you have to extend WorkflowParticipantProvider class and overwrite the methods: getParticipantTokens that fills the list of options in the Role Based section of and resolvethat will decide the exact user based on some X++ and the selected option in Role Based list.

Dynamics AX 2009 Workflow — Assignment — Role based
By default, Dynamics AX 2009 comes with three participants:
1. WorkflowUserGroupParticipantProvider:
• List all configured User Groups in the system and assigns the workflow step to all users in the selected group
2. ProjWorkflowParticipantProvider:
• This is for Purchase Requisition workflow. List some tokens like Project Manager, Project Controller, Project Sales and others and assigns the workflow step to the assigned person of the Purchase Requisition project.
3. TrvWorkflowProjParticipantProvider:
• This is for Expense Management workflow. List tokens like Project Manager, Project Controller, and Project Sales and assigns the workflow step to the assigned person of the expense project.

Published On: / Categories: Новости /

Развивайте бизнес с нами!

OntargIT является официальным партнером Microsoft по внедрению технологий Dynamics 365. С нашим опытом в различных отраслях, мы обеспечим индивидуальный подход и эффективные решения, которые будут идеально соответствовать потребностям вашей компании. Оставьте заявку сейчас, и наша команда экспертов поможет вам воспользоваться всеми преимуществами Dynamics 365.

Развивайте бизнес с нами!

OntargIT является официальным партнером Microsoft по внедрению технологий Dynamics 365. С нашим опытом в различных отраслях, мы обеспечим индивидуальный подход и эффективные решения, которые будут идеально соответствовать потребностям вашей компании. Оставьте заявку сейчас, и наша команда экспертов поможет вам воспользоваться всеми преимуществами Dynamics 365.