При работе с базовыми задачами, например, такими как согласование по регламенту, пользователь получает возможность в любой момент времени узнать, на каком этапе находится задача: на этапе согласования, подписания и тд. Но для самостоятельно разработанных задач такой возможности по умолчанию нет, также блоки выполнения таких задач не отображаются в документах, и пользователи не могут легко ориентироваться до тех пор, пока не изучат до конца переписку в задаче, (которая может быть достаточно длинной из-за сложности процесса), что тратит время пользователя.
Поэтому для удобства работы пользователя и ускорения поиска нужной информации имеет смысл настроить вкладку "Регламент" самой задачи и вкладку "Задачи" для записи справочника (для отображения блоков выполнения заданий). Данная вкладка будет содержать Контрол состояния, который будет содержать все необходимые данные (можно настроить под свою задачу).
Примечание: далее описывается построение регламента для справочника, но построение аналогично построению для документа.
Было принято решение, что будет происходить работа со справочником, а не документом, так как для работы не требуется текст документа. Пример карточки записи такого справочника:
Была разработана задача "Согласование замещения вакантных должностей" - ApprovalVacantPostTask, в рамках которой согласуется запись справочника "Вакантные должности" - ProcedureFillVacantPostsDatabook.
Для того, чтобы в разработанной задаче отображалась информация, связанная сущностью (справочником в данном случае), требуется добавить Контрол состояния - настройка Контрола состояния описана ниже (см. пункт "Настройка вкладки "Регламент" в задаче").
Для настройки отображения схемы в задаче, вам необходимо выполнить следующие шаги:
В результате создана функция GetApprovalVacantPostTaskState(), которая будет отвечать за отображение схемы в Контроле состояния, но перед основной разработкой, правильнее будет указать функцию GetStateViewVacantPost, которую ищет для отрисовки справочник. Итак, в разработанной задаче в разделе серверных функций укажите:
// <summary>
/// Построить модель состояния.
/// </summary>
/// <param name="databook">Справочник.</param>
/// <returns>Схема модели состояния.</returns>
public Sungero.Core.StateView GetStateViewVacantPost(IProcedureFillVacantPostsDatabook databook)
{
if (_obj.ProcedureFillVacantPostsDatabook.Equals(databook))
return this.GetApprovalVacantPostTaskState();
else
return StateView.Create();
}
Если наша задача имеет ссылку на справочник, который был передан, то вызывается функция для построения модели - GetApprovalVacantPostTaskState.
И универсальный вариант для отображения модели будет представлен ниже:
/// <summary>
/// Регламент задачи согласования
/// </summary>
[Remote(IsPure = true)]
public StateView GetApprovalVacantPostTaskState()
{
var stateView = StateView.Create();
StateBlock blockAssignment;
//Получение всех зависимых заданий
var assignments = Sungero.Workflow.Assignments.GetAll()
.Where(a => Equals(a.Task, _obj))
.OrderBy(y => y.Created);
//Блок о действии
Sungero.Docflow.PublicFunctions.OfficialDocument
.AddUserActionBlock(stateView,
_obj.Author,
_obj.Started.HasValue ?
Sungero.Docflow.ApprovalTasks.Resources.StateViewDocumentSentForApproval :
Sungero.Docflow.ApprovalTasks.Resources.StateViewTaskDrawCreated,
_obj.Created.Value,
_obj,
string.Empty,
_obj.Author);
// Добавить основной блок для задачи.
var taskBlock = this.AddTaskBlock(stateView);
//Добавление блока по заданию
foreach (var assignment in assignments)
{
blockAssignment = AddBlockAssignment(stateView, true, taskBlock, assignment, false);
}
return stateView;
}
Блок о действии включает информацию о том, кто задачу создал и когда - эта информация высвечивается над блоком задачи.
Основным блоком является блок задачи (вызов AddTaskBlock), а уже ниже вложенными отображаются подблоки заданий (вызов AddBlockAssignment).
Блок задачи строится следующим образом:
/// <summary>
/// Добавить основной блок задачи согласования.
/// </summary>
/// <param name="stateView">Схема представления.</param>
/// <returns>Добавленный блок.</returns>
private StateBlock AddTaskBlock(StateView stateView)
{
var taskBlock = stateView.AddBlock();
var isDraft = _obj.Status == Sungero.Workflow.Task.Status.Draft;
var headerStyle = Sungero.Docflow.PublicFunctions.Module.CreateHeaderStyle(isDraft);
var labelStyle = Sungero.Docflow.PublicFunctions.Module.CreateStyle(false, isDraft, false);
taskBlock.Entity = _obj;
taskBlock.AssignIcon(Sungero.Docflow.OfficialDocuments.Info.Actions.SendForFreeApproval, StateBlockIconSize.Large);
taskBlock.IsExpanded = _obj.Status == Sungero.Workflow.Task.Status.InProcess;
taskBlock.AddLabel(_obj.Subject, headerStyle);
var status = string.Empty;
if (_obj.Status == Sungero.Workflow.Task.Status.InProcess)
status = Sungero.Docflow.ApprovalTasks.Resources.StateViewInProcess;
else if (_obj.Status == Sungero.Workflow.Task.Status.Completed)
status = Sungero.Docflow.ApprovalTasks.Resources.StateViewCompleted;
else if (_obj.Status == Sungero.Workflow.Task.Status.Aborted)
status = Sungero.Docflow.ApprovalTasks.Resources.StateViewAborted;
else if (_obj.Status == Sungero.Workflow.Task.Status.Suspended)
status = Sungero.Docflow.ApprovalTasks.Resources.StateViewSuspended;
else if (_obj.Status == Sungero.Workflow.Task.Status.Draft)
status = Sungero.Docflow.ApprovalTasks.Resources.StateViewDraft;
var taskText = _obj.ActiveText;
if (taskText != string.Empty)
{
taskBlock.AddLineBreak();
taskBlock.AddLabel("_______________________", labelStyle);
taskBlock.AddLineBreak();
taskBlock.AddLabel(taskText);
}
Sungero.Docflow.PublicFunctions.Module.AddInfoToRightContent(taskBlock, status, labelStyle);
return taskBlock;
}
При построении блока задачи получаем стили для заголовка задачи и обычного текста, указываем иконку и ее размер, если к задаче прикладывается текст, то его отображаем после черты, и добавляем колонку со статусом задачи.
Далее к основному блоку задачи добавляем подблоки с информацией по заданиям:
/// <summary>
/// Добавление блока
/// </summary>
/// <param name="stateView">Модель</param>
/// <param name="isShowBorder">Линия блока</param>
/// <param name="mainBlock">Главный блок</param>
/// <param name="assignment">Сущность (задание)</param>
/// <param name="isExpanded">Признак того, что дочерние блоки развернуты</param>
/// <returns></returns>
[Public]
public static Sungero.Core.StateBlock AddBlockAssignment(StateView stateView, bool isShowBorder, Sungero.Core.StateBlock mainBlock, Sungero.Workflow.IAssignment assignment, bool isExpanded)
{
StateBlockLabelStyle style = StateBlockLabelStyle.Create();
StateBlockLabelStyle styleHeader = StateBlockLabelStyle.Create();
StateBlockLabelStyle styleBoottom = StateBlockLabelStyle.Create();
styleBoottom.Color = Colors.Common.Gray;
StateBlock block;
if (mainBlock != null)
block = mainBlock.AddChildBlock();
else
block = stateView.AddBlock();
block.AddLabel(assignment.Subject, styleHeader);
string position = Sungero.Company.Employees.Is(assignment.Performer) &&
Sungero.Company.Employees.As(assignment.Performer).JobTitle != null ?
" (" + Sungero.Company.Employees.As(assignment.Performer).JobTitle.Name + ")":
string.Empty;
string label = string.Format("{0, -1}{1}", assignment.Performer.Name, position);
if (assignment.Deadline.HasValue)
label = SC.VariousProcessesModule.ApprovalBookingRequestTasks.Resources.DeadlineFormat(label, assignment.Deadline.Value.ToShortDateString());
//Создано
if (assignment.Created.HasValue)
{
block.AddLineBreak();
block.AddLabel(SC.VariousProcessesModule.ApprovalBookingRequestTasks.Resources.CreatedFormat(assignment.Created.Value), styleBoottom);
}
//Участник с должностью
block.AddLineBreak();
block.AddLabel(SC.VariousProcessesModule.ApprovalBookingRequestTasks.Resources.ToWhomFormat(label), styleBoottom);
block.ShowBorder = isShowBorder;
block.Entity = assignment;
block.IsExpanded = isExpanded;
if (assignment.Status == Sungero.Workflow.Task.Status.InProcess)
{
style.Color = Colors.Common.Gray;
}
if (assignment.ActiveText != null)
{
block.AddLineBreak();
block.AddLabel(SC.VariousProcessesModule.ApprovalBookingRequestTasks.Resources.CommentFormat(assignment.ActiveText), style);
}
if (assignment.Status == Sungero.Workflow.Task.Status.InProcess)
{
block.BorderColor = Colors.Common.DarkBlue;
}
if (assignment.Status == Sungero.Workflow.Task.Status.InProcess &&
assignment.Deadline.HasValue &&
assignment.Deadline < Calendar.Now)
{
style.Color = Colors.Common.Red;
styleBoottom.Color = Colors.Common.Red;
styleHeader.Color = Colors.Common.Red;
block.Background = Colors.FromRgb(255, 200, 200);
}
//Статус
var column = block.AddContent();
if (assignment.Result != null)
column.AddLabel(assignment.Info.Properties.Result.GetLocalizedValue(assignment.Result), style);
else
column.AddLabel(assignment.Info.Properties.Status.GetLocalizedValue(assignment.Status.Value), style);
block.AssignIcon(StateBlockIconType.OfEntity, StateBlockIconSize.Large);
return block;
}
При формировании блока задания указывается дополнительный текст под заголовком: время создания, исполнитель, комментарий при наличии, а в правой колонке отображается, или результат выполнения задания, или статус (если результата нет), также, если идет просрочка, то блок выделяется красным.
Схема во вкладке будет выглядеть следующим образом:
Если у задания идет просрочка:
Для отображения модели в заданиях разработанной задачи достаточно создать вкладку, добавить Контрол состояния, создать функцию (см. пункт "Настройка вкладки "Регламент" в задаче"), и вызвать основную функцию для отрисовки, которая прописана в задаче:
/// <summary>
/// Вызов модели из основной задачи
/// </summary>
[Remote]
public StateView GetAcquaintanceVacantPostAssignmentState()
{
var stateView = StateView.Create();
var task = ApprovalVacantPostTasks.As(_obj.Task);
if (task != null)
stateView = Functions.ApprovalVacantPostTask.GetApprovalVacantPostTaskState(task);
return stateView;
}
Вкладка Tasks (Задачи) во всех документах, которые наследуются от OfficialDocument, строится следующим образом:
Чтобы ваши задачи отображались в базовой вкладке Tasks (Задачи) какого-либо документа, настройка в документе не требуется, так как достаточно прописать в вашей ЗАДАЧЕ функцию GetStateView с передачей документа (см. пункт "Разработка StateView для отображения схемы").
Но так как в данном случае рассматривается работа со справочником, то далее рассмотрим его настройку - в отличии от документа у справочника нет этих настроек по умолчанию, но ничего не мешает их прописать.
Создадим вкладку "Задачи", добавим Группу контролов, Контрол состояния и создадим функцию (см. пункт "Настройка вкладки "Регламент" в задаче"). Функция GetProcedureFillVacantPostsDatabookState() будет создана в серверном разделе и вся последующая разработка будет там.
Функция, которую мы только создали, имеет следующий вид:
/// <summary>
/// Отображение задач по согласованию записи справочника.
/// </summary>
/// <returns>История согласования.</returns>
[Remote(IsPure = true)]
public Sungero.Core.StateView GetProcedureFillVacantPostsDatabookState()
{
// Переполучить справочник для отображения ПО, когда справочник еще не сохранен.
var databook = ProcedureFillVacantPostsDatabooks.GetAll(a => a.Id == _obj.Id).FirstOrDefault();
if (databook != null)
return GetStateView(databook);
return GetStateView(_obj);
}
Если вакансия найдена, то для нее выполнится построение Контрола состояния (если задач для отображения найдено не будет, то будет отображаться текст по умолчанию):
/// <summary>
/// Построить модель состояния справочника.
/// </summary>
/// <param name="databook">Справочник.</param>
/// <returns>Схема модели состояния.</returns>
/// <remarks>По идее, одноименная функция ожидается у всех сущностей, которым нужно представление состояния.</remarks>
[Public]
public static Sungero.Core.StateView GetStateView(IProcedureFillVacantPostsDatabook databook)
{
var stateView = StateView.Create();
stateView.AddDefaultLabel(Sungero.Docflow.OfficialDocuments.Resources.StateViewDefault);
AddTasksViews(stateView, databook);
stateView.IsPrintable = true;
return stateView;
}
Далее нужно определить - если справочник будет передаваться во вложении, то соответственно проверять вложения задачи (так реализовано для документа OfficialDocument), в данном случае справочник не передается во вложении, но задача имеет свойство, которые ссылается на наш справочник.
В функции AddTasksViews идет поиск задач для отображения во вкладке (можно было сделать поиск по всем задачам и проверять свойство, но так как требуется отображать схему только для выбранных задач, то tasks вычисляется так, как указано ниже):
/// <summary>
/// Добавить информацию о задачах, в которые вложен справочник.
/// </summary>
/// <param name="stateView">Схема представления.</param>
/// <param name="databook">Справочник.</param>
private static void AddTasksViews(StateView stateView, IProcedureFillVacantPostsDatabook databook)
{
var tasks = ApprovalVacantPostTasks.GetAll()
.Where(task => task.ProcedureFillVacantPostsDatabook.Equals(databook))
.OrderBy(task => task.Created)
.ToList();
foreach (var task in tasks)
{
if (stateView.Blocks.Any(b => b.HasEntity(task)))
continue;
AddTaskViewXml(stateView, task, databook);
}
}
Построение регламента для найденной задачи:
/// <summary>
/// Построение модели задачи, в которую вложен справочник.
/// </summary>
/// <param name="stateView">Схема представления.</param>
/// <param name="task">Задача.</param>
/// <param name="databook">Справочник.</param>
private static void AddTaskViewXml(StateView stateView, Sungero.Workflow.ITask task, IProcedureFillVacantPostsDatabook databook)
{
// Добавить предметное отображения для прикладных задач.
var taskStateView = Sungero.Docflow.PublicFunctions.Module.GetServerEntityFunctionResult(task, "GetStateViewVacantPost", new List<object>() { databook });
if (taskStateView != null)
{
// Избавиться от дублирующих блоков, если таковые были.
List<StateBlock> blockWhiteList = new List<StateBlock>() { };
foreach (var block in ((StateView)taskStateView).Blocks)
{
if (block.Entity == null || !stateView.Blocks.Any(b => b.HasEntity(block.Entity)))
blockWhiteList.Add(block);
}
foreach (var block in blockWhiteList)
stateView.AddBlock(block);
}
}
В функцию AddTaskViewXml передается задача и некоторая сущность (обычно это документ, но в данном случае справочник), далее в этой задаче происходит поиск функции с наименованием GetStateView, которая принимает конкретно эту сущность и, если такая функция найдена, то она возвращает StateView модель, которая далее и строится во вкладке.
Настройка справочника закончена, если бы работа велась с документом, то вместо справочника передавался бы соответствующий документ .
Расскажите пожалуйста что делают функции
SC.VariousProcessesModule.ApprovalBookingRequestTasks.Resources.DeadlineFormat(label, assignment.Deadline.Value.ToShortDateString())
SC.VariousProcessesModule.ApprovalBookingRequestTasks.Resources.CreatedFormat(assignment.Created.Value)
SC.VariousProcessesModule.ApprovalBookingRequestTasks.Resources.ToWhomFormat(label)
Еще не хватает обработки подзадач и данное решение не показывает в контроле состояния отправленные уведомления
Поясните плиз происхождение абривиатуры SC if (assignment.Deadline.HasValue) label = SC.VariousProcessesModule.ApprovalBookingRequestTasks?
Присоединяюсь к вопросу Павла.
https://club.directum.ru/post/319040#doc320925
Павел, нужно было мне подробнее расписать. Deadline имеет значение "{0}. Срок: {1}" и используется затем в ToWhomFormat, у которого значение "Кому: {0}" - при использовании в блоке добавляется подпись формата: "Кому: <ФИО исполнителя> (<Должность>). Срок: <Срок из задания в формате даты без времени>".
Created имеет значение "Создано: {0}" - проставляется до строки "Кому"
Павел, спасибо за замечание
Mikhail, SC - код компании, подставляется автоматически при создании объекта в системе, VariousProcessesModule - созданный модуль
Авторизуйтесь, чтобы написать комментарий