ЦВД
Главная
Блог
Модификация "Голосование" для задачи согласования по регламенту
29 марта 2024 г. 5 мин

Модификация "Голосование" для задачи согласования по регламенту

На практике часто возникает необходимость организовать коллегиальное принятие решений — например, при утверждении решений совещания, бюджетов или стратегических инициатив. Стандартные механизмы согласования в Directum RX позволяют настроить маршрут, но не поддерживают полноценное голосование с фиксацией позиций «За», «Против», «Воздержался».
Модификация "Голосование" для задачи согласования по регламенту

Задача: коллегиальное принятие решений

В этой статье — как мы реализовали модификацию для голосования в рамках задачи согласования, сохранив привычный интерфейс и интегрировав результаты прямо в протокол совещания.


Требования

— Голосование должно быть доступно на любом этапе согласования.
— Участники голосуют по каждому решению: «За», «Против», «Воздержался».
— Нельзя выбрать несколько вариантов.
— Результаты агрегируются в протоколе.
— Голосование — опциональная функция, активируемая при необходимости.


Реализация: ключевые компоненты

— Настройка вида документа "Протокол совещания"

— Создание справочника "Матрицы согласования"

— Доработка задачи согласования

— Асинхронный обработчик результатов

— Роль согласования "Участники голосования"

— Добавление признака "Требуется голосование"


В перекрытый справочник DocumentKind добавляем логическое свойство:


/// <summary>

/// Требуется голосование.

/// </summary>

public virtual bool? NeedVoting { get; set; }


На событии Showing формы — показываем только для вида "Протокол совещания":


public override void Showing(Sungero.Presentation.FormShowingEventArgs e)

{

base.Showing(e);

var isMinutes = _obj.DocumentType?.DocumentTypeGuid == Constants.MeetingMinutesTypeGuid;

_obj.State.Properties.NeedVoting.IsVisible = isMinutes;

}


Коллекция решений в протоколе

В карточку Minutes добавляем коллекцию Decisions, где каждое решение содержит:


— Текст решения
— Матрицу голосующих
— Счётчики голосов
— Результат


public virtual void DecisionsAdded(Sungero.Domain.Shared.CollectionPropertyAddedEventArgs e)

{

var added = e.Added as IMinuteDecision;

added.Number = (_obj.Decisions.Max(d => d.Number) ?? 0) + 1;

added.Result = Resources.NoResults;

}


Кастомный тип этапа "Голосование"

/// <summary>

/// Тип этапа.

/// </summary>

public virtual CustomStageType? CustomStageType { get; set; }


Фильтруем отображение — "Голосование" доступно только для этапов типа "Задание":


public virtual IEnumerable<Enumeration> CustomStageTypeFiltering(IEnumerable<Enumeration> query)

{

if (_obj.StageType != StageType.SimpleAgr || _obj.AllowSendToRework == true)

query = query.Where(q => !Equals(q, CustomStageType.Voting));

return query;

}


Коллекция голосов в задании

В перекрытое задание ApprovalSimpleAssignment добавляем коллекцию Voting, содержащую:
— Decision — текст
— VoteFor, VoteAgainst, VoteAbstain — выбор


Обеспечиваем взаоисключающий выбор:


public virtual void VotingVoteAgainstValueInput(Sungero.Presentation.BooleanValueInputEventArgs e)

{

if (e.NewValue == true)

{

_obj.VoteFor = false;

_obj.VoteAbstain = false;

}

}


public virtual void VotingVoteForValueInput(Sungero.Presentation.BooleanValueInputEventArgs e)

{

if (e.NewValue == true)

{

_obj.VoteAgainst = false;

_obj.VoteAbstain = false;

}

}


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

На событии BeforeComplete убеждаемся, что по всем решениям проголосовано:


public override void BeforeComplete(Sungero.Workflow.Server.BeforeCompleteEventArgs e)

{

base.BeforeComplete(e);


if (_obj.CustomStageType != CustomStageType.Voting)

return;


var emptyVotes = _obj.Voting.Where(v => !v.VoteFor && !v.VoteAgainst && !v.VoteAbstain);

if (emptyVotes.Any())

{

e.AddError("Проголосуйте по всем решениям.");

return;

}

}


Асинхронный обработчик результатов

После завершения задания — агрегируем голоса в протоколе:


public void AddVotingResults(long assignmentId, long minutesId)

{

var assignment = ApprovalSimpleAssignment.GetAll().Where(a => a.Id == assignmentId).FirstOrDefault();

var minutes = Minutes.GetAll().Where(m => m.Id == minutesId).FirstOrDefault();


if (assignment == null || minutes == null) return;


foreach (var vote in assignment.Voting)

{

var decision = minutes.Decisions.FirstOrDefault(d => d.Id == vote.MinutedDecisionId);

if (decision != null)

{

if (vote.VoteFor) decision.VoteFor = (decision.VoteFor ?? 0) + 1;

if (vote.VoteAgainst) decision.VoteAgainst = (decision.VoteAgainst ?? 0) + 1;

if (vote.VoteAbstain) decision.VoteAbstain = (decision.VoteAbstain ?? 0) + 1;


decision.Result = $"За: {decision.VoteFor}, Против: {decision.VoteAgainst}, Воздержались: {decision.VoteAbstain}";

}

}

minutes.Save();

}


Роль согласования "Участники голосования"

Ограничиваем доступ роли только для протоколов:


public override List<IDocumentKind> Filter(List<IDocumentKind> kinds)

{

var query = base.Filter(kinds);

if (_obj.Type == RoleType.MinutesVoters)

query = query.Where(k => k.DocumentType.DocumentTypeGuid == Constants.MeetingMinutesTypeGuid);

return query.ToList();

}


Итог

Модификация позволяет:


— Голосовать прямо в задаче согласования.
— Сохранять результаты в протоколе.
— Гибко настраивать состав голосующих.
— Интегрировать в любые бизнес-процессы.


Решение реализовано через low-code подход, без изменения стандартных механизмов Directum RX, что упрощает сопровождение и обновление.

Делаем бизнес эффективнее с 2008-го года
— Получите персональный расчёт стоимости и план внедрения за 1 рабочий день
— Бесплатная консультация по сценариям вашей интеграции
— Демонстрация решения в течение 24 часов