Цель: Добавить возможность голосования в Directum RХ по протоколу совещания.
В задачу согласования по регламенту настроить этап «Задание» с возможность голосования по пунктам решений, добавленных в карточке протокола совещания. Отображение состояния и статистики голосования в соответствующей строке решения.
Требуемые модификации:
1. В карточку вида документа добавить чек-бокс «Требуется голосование».
2. Создать справочник «Матрицы согласования», содержащий перечень голосующих.
3. В карточку протокола совещания добавить вкладку «Решения». Коллекция решений имеет автоматическую нумерацию с возможностью внесения текста решения, выбора матрицы, отображения состояния результатов голосования.
4. В карточку этапа «Задание» добавить чек-бокс «Голосование».
5. В карточку задания добавить коллекцию, заполняемую текстами решений из протокола совещания с возможностью голосовать «За», «Против», «Воздержался».
6. В карточке задачи на согласование по регламенту отображать закрытое поле, содержащее список голосующих.
7. Доработать схему задачи на согласование для заполнения коллекции решений в задании.
8. Написать асинхронный обработчик передачи результатов голосования из задания в карточку протокола совещания.
9. Создать роль согласования «Участники голосования», для выбора ее в этапе задания.
Модификации должны учитывать ограничения по типу сущности: проверки соответствия настроек вида документа и типа этапа, видимости полей, фильтрацию выбора, обработку запуска задач, блокировки сущностей.
Код и Постфикс компании: centrvd
Имя решение: Solution
Имя модуля: SolutionDatabook
Перекрыть справочник видов документов (DocumentKind), добавить логическое свойство «Требуется голосование» (NeedVoitingcentrvd). MeetingMinutesTypeGuid – константа для хранения GUID’a базового типа Minutes.
На событии «Показ формы» написать отображение, только для типа «Протокол совещания»
public override void Showing(Sungero.Presentation.FormShowingEventArgs e)
{
base.Showing(e);
//Видимость чекбокса Требуется голосование только для протоколов совещаний.
_obj.State.Properties.NeedVoitingcentrvd.IsVisible = _obj.DocumentType != null ? _obj.DocumentType.DocumentTypeGuid.Equals(SolutionDataBook.PublicConstants.Module.MeetingMinutesTypeGuid.ToString()) : false;
}
При смене типа необходимо очищать значение:
public override void DocumentTypeValueInput(Sungero.Docflow.Client.DocumentKindDocumentTypeValueInputEventArgs e)
{
base.DocumentTypeValueInput(e);
if (!Equals(e.NewValue, e.OldValue))
{
_obj.NeedVoitingcentrvd = false;
}
}
Чтобы в будущей коллекции решений можно было выбирать голосующих, необходимо создать матрицу голосования ApprovalMatrix – справочник, содержащий как минимум поля:
Выдать права всем пользователям на чтение. Создавать матрицы смогут Администраторы:
/// <summary>
/// Выдача прав на справочник Матрицы голосования.
/// </summary>
public static void GrantRightsOnApprovalMatrix()
{
var allUsers = Roles.AllUsers;
centrvd.SolutionDataBook.ApprovalMatrices.AccessRights.Grant(allUsers, DefaultAccessRightsTypes.Read);
centrvd.SolutionDataBook.ApprovalMatrices.AccessRights.Save();
}
Перекрыть протокол совещания (Minutes). Добавить вкладку «Решения» (Decisions) в карточку протокола. Во вкладку добавить коллекцию (Decisionscentrvd) записей по решениям со свойствами:
На «Показе формы» добавить условие видимости и блокировки вкладки, если есть запущенные задачи:
public override void Showing(Sungero.Presentation.FormShowingEventArgs e)
{
base.Showing(e);
_obj.State.Pages.Decisions.IsVisible = centrvd.Solution.DocumentKinds.As(_obj.DocumentKind).NeedVoitingcentrvd.GetValueOrDefault();
_obj.State.Properties.Decisionscentrvd.IsEnabled = !Functions.Minutes.AnyVoitingMinutesTasks(_obj);
}
Добавим разделяемую функцию AnyVoitingMinutesTasks():
/// <summary>
/// Есть ли активные задачи с этапом голосование по протоколу с видом, требующим голосование.
/// </summary>
public virtual bool AnyVoitingMinutesTasks()
{
var documentsGroupGuid = Sungero.Docflow.PublicConstants.Module.TaskMainGroup.ApprovalTask;
var votingStage = centrvd.Solution.ApprovalStage.CustomStageTypecentrvd.Voting;
var minutesTasks = centrvd.Solution.ApprovalTasks.GetAll()
.Where(t => t.Status == Sungero.Docflow.ApprovalTask.Status.InProcess)
.Where(t => t.AttachmentDetails.Any(g => g.GroupId == documentsGroupGuid && _obj.Id == g.AttachmentId));
var hasVotingStage = false;
foreach (var task in minutesTasks)
{
if (Functions.ApprovalTask.HasCustomStage(task, votingStage))
{
hasVotingStage = true;
break;
}
}
return centrvd.Solution.DocumentKinds.As(_obj.DocumentKind).NeedVoitingcentrvd.GetValueOrDefault() && hasVotingStage;
}
* Функцию HasCustomStage() добавим позже в задаче на согласование.
При добавлении в коллекцию прописать автоматическую нумерацию, т.к. пользователи не могут менять порядковый номер. По желанию можно добавить текст «Не проводилось» в результаты голосования.
public virtual void DecisionscentrvdAdded(Sungero.Domain.Shared.CollectionPropertyAddedEventArgs e)
{
_added.Number = (_obj.Decisionscentrvd.Max(a => a.Number) ?? 0) + 1;
_added.Result = centrvd.Solution.Minuteses.Resources.NoResults;
}
При удалении из коллекции записи, переписывать порядковые номера решений:
public virtual void DecisionscentrvdDeleted(Sungero.Domain.Shared.CollectionPropertyDeletedEventArgs e)
{
int minNumber = 1;
foreach (var decision in _obj.Decisionscentrvd)
decision.Number = minNumber++;
}
Чтобы заполнялось поле Голосующие (Voitings) и служебное поле VotersEmployeesIds (необходимое в схеме задачи при создании заданий) добавим код на событии «Изменение значения свойства» у поля Matrix
public virtual void DecisionscentrvdMatrixChanged(centrvd.Solution.Shared.MinutesDecisionscentrvdMatrixChangedEventArgs e)
{
if (!Equals(e.NewValue, e.OldValue))
{
_obj.Voitings = e.NewValue != null ? e.NewValue.Approvers.Select(a => a.Approver.Person.ShortName).Aggregate((x,y) => $"{x}, {y}") : string.Empty;
_obj.VotersEmployeesIds = e.NewValue != null ? string.Join(",", e.NewValue.Approvers.Select(a => a.Approver.Id.ToString())) : string.Empty;
}
}
Для этапа необходима дополнительная настройка, которая бы учитывала какого типа задания создавать. Перекроем справочник ApprovalStage. Создадим свойство-перечисление «Тип этапа» (CustomStageTypecentrvd). Добавляем значение перечисления Votingcentrvd с отображаемыми именами Voting/Голосование».
*Перечисление вместо логического поля заведено для того, чтобы не плодить логические поля, а в проекте может быть много кастомных типов этапов со своей логикой.
Добавьте фильтрацию на выбор из перечисления CustomStageTypecentrvd, чтобы «Голосование» не отображалось для этапов, кроме «Задание» или без «Разрешения отправки на доработку»:
public virtual IEnumerable<Enumeration> CustomStageTypecentrvdFiltering(IEnumerable<Enumeration> query)
{
if (_obj.StageType != StageType.SimpleAgr || _obj.AllowSendToRework.GetValueOrDefault())
query = query.Where(q => !Equals(q, CustomStageTypecentrvd.Voting));
return query;
}
Чтобы настроить карточку задания (в зависимости от настроек этапа), перекроем задание ApprovalSimpleAssignment и добавим свойства:
Чтобы коллекция отображалась только, если задание с типом «Голосование» в событие «Показ формы» добавим код:
public override void Showing(Sungero.Presentation.FormShowingEventArgs e)
{
base.Showing(e);
_obj.State.Properties.Voitingcentrvd.IsVisible = _obj.CustomStageTypecentrvd == centrvd.Solution.ApprovalSimpleAssignment.CustomStageTypecentrvd.Voting;
}
Чтобы нельзя было поставить галочки в несколько полей по решению, заполним обработчик «Изменение контрола» для каждого логического свойства коллекции:
public virtual void VoitingcentrvdVoteAgainstValueInput(Sungero.Presentation.BooleanValueInputEventArgs e)
{
if (e.NewValue == true)
{
_obj.VoteFor = false;
_obj.VoteAbstain = false;
}
}
public virtual void VoitingcentrvdVoteAbstainValueInput(Sungero.Presentation.BooleanValueInputEventArgs e)
{
if (e.NewValue == true)
{
_obj.VoteFor = false;
_obj.VoteAgainst = false;
}
}
public virtual void VoitingcentrvdVoteForValueInput(Sungero.Presentation.BooleanValueInputEventArgs e)
{
if (e.NewValue == true)
{
_obj.VoteAgainst = false;
_obj.VoteAbstain = false;
}
}
*Логические null поля в коллекциях выглядят как false, т.е. вместо «-» выглядят как пустой квадрат, поэтому нет необходимости заполнять их false значениями с точки зрения пользователя.
Чтобы пользователи не могли добавлять, копировать или удалять строки решений (дочерней коллекции) в задании, отключим эту возможность для типа этапа «Голосование». При прочих условиях эта возможность останется для других коллекций.
partial class ApprovalSimpleAssignmentAnyChildEntityCollectionActions
{
public override void DeleteChildEntity(Sungero.Domain.Client.ExecuteChildCollectionActionArgs e)
{
base.DeleteChildEntity(e);
}
public override bool CanDeleteChildEntity(Sungero.Domain.Client.CanExecuteChildCollectionActionArgs e)
{
var assignment = centrvd.Solution.ApprovalSimpleAssignments.As(e.RootEntity);
var isVoiting = assignment != null ? assignment.CustomStageTypecentrvd == centrvd.Solution.ApprovalSimpleAssignment.CustomStageTypecentrvd.Voting : false;
return base.CanDeleteChildEntity(e) && !isVoiting;
}
}
partial class ApprovalSimpleAssignmentAnyChildEntityActions
{
public override void CopyChildEntity(Sungero.Domain.Client.ExecuteChildCollectionActionArgs e)
{
base.CopyChildEntity(e);
}
public override bool CanCopyChildEntity(Sungero.Domain.Client.CanExecuteChildCollectionActionArgs e)
{
var assignment = centrvd.Solution.ApprovalSimpleAssignments.As(e.RootEntity);
var isVoiting = assignment != null ? assignment.CustomStageTypecentrvd == centrvd.Solution.ApprovalSimpleAssignment.CustomStageTypecentrvd.Voting : false;
return base.CanCopyChildEntity(e) && !isVoiting;
}
public override void AddChildEntity(Sungero.Domain.Client.ExecuteChildCollectionActionArgs e)
{
base.AddChildEntity(e);
}
public override bool CanAddChildEntity(Sungero.Domain.Client.CanExecuteChildCollectionActionArgs e)
{
var assignment = centrvd.Solution.ApprovalSimpleAssignments.As(e.RootEntity);
var isVoiting = assignment != null ? assignment.CustomStageTypecentrvd == centrvd.Solution.ApprovalSimpleAssignment.CustomStageTypecentrvd.Voting : false;
return base.CanAddChildEntity(e) && !isVoiting;
}
}
Перед выполнением задания необходимо проверять, по всем ли пунктам проголосовал исполнитель. Для этого на событии «До выполнения» добавим код, который выводит ошибку, если есть хоть одна строка без галочек:
public override void BeforeComplete(Sungero.Workflow.Server.BeforeCompleteEventArgs e)
{
base.BeforeComplete(e);
var isVoiting = _obj.CustomStageTypecentrvd == centrvd.Solution.ApprovalSimpleAssignment.CustomStageTypecentrvd.Voting;
if (isVoiting && _obj.Voitingcentrvd.Any(d => d.VoteAbstain.GetValueOrDefault()
&& d.VoteAgainst.GetValueOrDefault()
&& d.VoteFor.GetValueOrDefault()))
e.AddError(centrvd.Solution.ApprovalSimpleAssignments.Resources.ErrorVoteAllDecisions);
}
Чтобы в задаче на согласование ApprovalTask дополнительно отображалось поле Голосующие:
В разделяемые функции ApprolaTask добавим логическую функцию HasCustomStage(), которая определяет, есть ли в задаче этап модифицированный этап, в нашем случае - голосование.
/// <summary>
///Есть ли в схеме задачи этап с типом Рублево.
/// </summary>
/// <param name="typeValue">Тип этапа Рублево.</param>
/// <returns>True, если этап есть. False, если этап отсутствует.</returns>
[Public]
public bool HasCustomStage(Sungero.Core.Enumeration typeValue)
{
if (_obj.ApprovalRule != null)
{
foreach (var stage in _obj.ApprovalRule.Stages)
{
var customStage = centrvd.Solution.ApprovalStages.As(stage.Stage);
if (customStage != null && customStage.CustomStageTypecentrvd == typeValue)
return true;
}
}
return false;
}
В разделяемые функции ApprolaTask добавим функцию FillVoters(), которая будет заполнять поле «Голосующие» из выбранной матрицы протокола:
/// <summary>
///Заполнить голосующих.
/// </summary>
public void FillVoters()
{
var document = _obj.DocumentGroup.OfficialDocuments.FirstOrDefault();
var minutes = centrvd.Solution.Minuteses.As(document);
var isVoitingKind = minutes != null ? centrvd.Solution.DocumentKinds.As(minutes.DocumentKind).NeedVoitingcentrvd.GetValueOrDefault() : false;
var votingStage = centrvd.Solution.ApprovalStage.CustomStageTypecentrvd.Voting;
if (isVoitingKind && this.HasCustomStage(votingStage) && minutes.Decisionscentrvd.Any())
{
_obj.ApprovalMatrixcentrvd = minutes.Decisionscentrvd.FirstOrDefault().Matrix;
foreach (var employee in _obj.ApprovalMatrixcentrvd.Approvers)
{
var voter = _obj.Voterscentrvd.AddNew();
voter.Voter = employee.Approver;
}
}
}
На событие «Изменение значения свойства» регламента добавим код отображения созданной группы:
public override void ApprovalRuleChanged(Sungero.Docflow.Shared.ApprovalTaskApprovalRuleChangedEventArgs e)
{
base.ApprovalRuleChanged(e);
Functions.ApprovalTask.FillVoters(_obj);
}
Пропишем отображение поля Голосующие, перекрыв базовую функцию SetVisibleProperties():
public override void SetVisibleProperties(Sungero.Docflow.Structures.ApprovalTask.RefreshParameters refreshParameters)
{
base.SetVisibleProperties(refreshParameters);
var document = _obj.DocumentGroup.OfficialDocuments.FirstOrDefault();
var minutes = centrvd.Solution.Minuteses.As(document);
var isVotingKind = minutes != null ? centrvd.Solution.DocumentKinds.As(minutes.DocumentKind).NeedVoitingcentrvd.GetValueOrDefault() : false;
var votingStage = centrvd.Solution.ApprovalStage.CustomStageTypecentrvd.Voting;
bool isVoting = isVotingKind && this.HasCustomStage(votingStage);
_obj.State.Properties.Voterscentrvd.IsVisible = isVoting;
}
На действие «Отправить» добавляем проверку на уже запущенные задачи по голосованию и наличие голосующих:
public override void Start(Sungero.Domain.Client.ExecuteActionArgs e)
{
var document = _obj.DocumentGroup.OfficialDocuments.FirstOrDefault();
//Проверка на наличие активных задач по протоколу, содержащих этап голосование.
var minutes = centrvd.Solution.Minuteses.As(document);
var votingStage = centrvd.Solution.ApprovalStage.CustomStageTypecentrvd.Voting;
var hasVotingStage = Functions.ApprovalTask.HasCustomStage(_obj, votingStage);
if (minutes != null && Functions.Minutes.AnyVoitingMinutesTasks(minutes) && hasVotingStage)
{
Dialogs.ShowMessage(centrvd.Solution.ApprovalTasks.Resources.ErrorHasVotingTaskInProcess);
return;
}
//Проверка на наличие голосующих перед запуском задачи на голосование.
if (minutes != null && hasVotingStage && !_obj.Voterscentrvd.Any())
{
Dialogs.ShowMessage(centrvd.Solution.ApprovalTasks.Resources.ErrorFillDesicionTab);
return;
}
base.Start(e);
}
Необходимо предусмотреть обнуление результатов в протоколе и заполнение поля «Результаты» значением «Запущено голосование» при старте (рестарте) задачи, поэтому на событие «До старта»:
public override void BeforeStart(Sungero.Workflow.Server.BeforeStartEventArgs e)
{
base.BeforeStart(e);
var document = _obj.DocumentGroup.OfficialDocuments.FirstOrDefault();
//Для протокола совещания с типом голосование очищаем результаты голосования.
var minutes = centrvd.Solution.Minuteses.As(document);
var isVoitingKind = minutes != null ? centrvd.Solution.DocumentKinds.As(minutes.DocumentKind).NeedVoitingcentrvd.GetValueOrDefault() : false;
var votingStage = centrvd.Solution.ApprovalStage.CustomStageTypecentrvd.Voting;
if (isVoitingKind && Functions.ApprovalTask.HasCustomStage(_obj, votingStage))
{
foreach (var decision in minutes.Decisionscentrvd)
{
decision.VoteAbstain = null;
decision.VoteAgainst = null;
decision.VoteFor = null;
decision.Result = centrvd.Solution.Minuteses.Resources.VotingStarted;
}
}
}
Теперь в задаче на согласование ApprovalTask необходимо передать тип из настроек этапа в задание. Перекроем ApprovalTask, найдем в схеме блок «Задание» ApprovalSimpleAssignment. Переходим на «Старт задания» и находим этап простого задания и передаем значение CustomStageTypecentrvd:
public override void StartAssignment30(Sungero.Docflow.IApprovalSimpleAssignment assignment, Sungero.Docflow.Server.ApprovalSimpleAssignmentArguments e)
{
base.StartAssignment30(assignment, e);
var stage = _obj.ApprovalRule.Stages
.Where(s => s.Stage != null)
.Where(s => s.Stage.StageType == Sungero.Docflow.ApprovalStage.StageType.SimpleAgr)
.FirstOrDefault(s => s.Number == _obj.StageNumber);
if (stage != null)
{
var CustomStage = centrvd.Solution.ApprovalStages.As(stage.Stage);
var CustomAssignment = centrvd.Solution.ApprovalSimpleAssignments.As(assignment);
if(CustomStage.CustomStageTypecentrvd == centrvd.Solution.ApprovalStage.CustomStageTypecentrvd.Voting)
CustomAssignment.CustomStageTypecentrvd = centrvd.Solution.ApprovalSimpleAssignment.CustomStageTypecentrvd.Voting;
//Заполнить таблицу для задания с типом этапа Голосование для Протокола совещания.
var minutes = _obj.DocumentGroup.OfficialDocuments.FirstOrDefault() != null ? centrvd.Solution.Minuteses.As(_obj.DocumentGroup.OfficialDocuments.FirstOrDefault()): null;
if(minutes != null && CustomStage.CustomStageTypecentrvd == centrvd.Solution.ApprovalStage.CustomStageTypecentrvd.Voting)
{
var performerId = Sungero.Company.Employees.As(assignment.Performer) != null ? Sungero.Company.Employees.As(assignment.Performer).Id.ToString() : string.Empty;
int number = 1;
foreach (var decision in minutes.Decisionscentrvd.Where(d => d.VotersEmployeesIds.Contains(performerId)))
{
var voiting = CustomAssignment.Voitingcentrvd.AddNew();
voiting.Number = number++;
voiting.Decision = decision.Decision;
voiting.MinutedDecisionId = decision.Id; //Пробрасываем ИД строки из таблицы решений Протокола.
}
}
}
}
Переходим на «Выполнение задания» и асинхронно передаем результаты голосования в протокол. Нельзя результаты передавать сразу, т.к. карточка протокола может быть заблокирована.
public override void CompleteAssignment30(Sungero.Docflow.IApprovalSimpleAssignment assignment, Sungero.Docflow.Server.ApprovalSimpleAssignmentArguments e)
{
base.CompleteAssignment30(assignment, e);
var CustomAssignment = centrvd.Solution.ApprovalSimpleAssignments.As(assignment);
//Передача результатов голосования в протокол.
if (CustomAssignment != null
&& CustomAssignment.CustomStageTypecentrvd == centrvd.Solution.ApprovalSimpleAssignment.CustomStageTypecentrvd.Voting
&& centrvd.Solution.Minuteses.Is(_obj.DocumentGroup.OfficialDocuments.FirstOrDefault())
&& assignment.Result == centrvd.Solution.ApprovalSimpleAssignment.Result.Complete)
{
var minutesId = CustomAssignment.DocumentGroup.OfficialDocuments.FirstOrDefault().Id;
var asyncAddVoitingResults = centrvd.SolutionDataBook.AsyncHandlers.AddVoitingResults.Create();
asyncAddVoitingResults.AssignmentID = assignment.Id;
asyncAddVoitingResults.MinutesID = minutesId;
asyncAddVoitingResults.ExecuteAsync();
}
}
//Пересчитать результаты голосования в протоколе совещания.
public virtual void AddVoitingResults(centrvd.SolutionDataBook.Server.AsyncHandlerInvokeArgs.AddVoitingResultsInvokeArgs args)
{
if (args.RetryIteration > 100)
{
Logger.DebugFormat("AddVoitingResults: превышено количество попыток (100) пересчета голосования по протоколу id - {0} из задания id - {1}. Обратитесь к администратору.", args.MinutesID, args.AssignmentID);
args.Retry = false;
return;
}
Logger.DebugFormat("AddVoitingResults: запуск пересчета голосования по протоколу id - {0} из задания id - {1}.", args.MinutesID, args.AssignmentID);
long minutesId = args.MinutesID;
long voitingAssignmnetId = args.AssignmentID;
var minutes = centrvd.Solution.Minuteses.GetAll().Where(m => m.Id == minutesId).FirstOrDefault();
var voitingAssignmnet = centrvd.Solution.ApprovalSimpleAssignments.GetAll().Where(a => a.Id == voitingAssignmnetId).FirstOrDefault();
if (minutes == null)
{
Logger.DebugFormat("AddVoitingResults: Не найден протокол совещания id - {0}.", minutesId);
return;
}
if (voitingAssignmnet == null)
{
Logger.DebugFormat("AddVoitingResults: Не найдено задание голосования id - {0}.", voitingAssignmnetId);
return;
}
if (!Locks.TryLock(minutes))
{
Logger.DebugFormat("AddVoitingResults: заблокирован протокол совещания id - {0}.", minutesId);
args.Retry = true;
return;
}
foreach (var vote in voitingAssignmnet.Voitingcentrvd)
{
var decision = minutes.Decisionscentrvd.Where(d => d.Id == vote.MinutedDecisionId).FirstOrDefault();
if (decision != null)
{
if (vote.VoteFor.GetValueOrDefault())
decision.VoteFor = decision.VoteFor == null ? 1 : decision.VoteFor++;
if (vote.VoteAbstain.GetValueOrDefault())
decision.VoteAbstain = decision.VoteAbstain == null ? 1 : decision.VoteAbstain++;
if (vote.VoteAgainst.GetValueOrDefault())
decision.VoteAgainst = decision.VoteAgainst == null ? 1 : decision.VoteAgainst++;
decision.Result = centrvd.SolutionDataBook.Resources.ResultsFormat(decision.VoteFor ?? 0, decision.VoteAgainst ?? 0, decision.VoteAbstain ?? 0);
}
}
minutes.Save();
Locks.Unlock(minutes);
Logger.DebugFormat("AddVoitingResults: выполнен пересчет голосования по протоколу id - {0} из задания id - {1}.", args.MinutesID, args.AssignmentID);
}
Создадим свой справочник CustomApprovalRole на основе Sungero.Docflow.ApprovalRoleBase.
Добавим в перечисление Type имя MinutesVoters со значениями Voters/Голосующие.
Чтобы при согласовании протокола для новой роли вычислялись голосующие, заполненные в поле «Матрица» на вкладке «Решения» карточки протокола, переопределим функцию GetRolePerformers(). Для этого в серверных функциях созданного типа справочника PurchaseApprovalRole напишим:
///Получить список исполнителей из роли.
/// <param name="task">Задача.</param>
/// <returns>Список исполнителей.</returns>
[Remote(IsPure = true), Public]
public override List<Sungero.Company.IEmployee> GetRolePerformers(Sungero.Docflow.IApprovalTask task)
{
//result используется для возврата списка исполнителей.
var result = new List<Sungero.Company.IEmployee>();
//Роль Участники голосования.
if (_obj.Type == SolutionDataBook.CustomApprovalRole.Type.MinutesVoters)
{
var document = task.DocumentGroup.OfficialDocuments.FirstOrDefault();
var minutes = document != null ? centrvd.Solution.Minuteses.As(document) : null;
if (minutes != null)
{
foreach (var decision in minutes.Decisionscentrvd)
result.AddRange(decision.Matrix.Approvers.Select(a => a.Approver));
}
return result;
}
return base.GetRolePerformers(task);
}
В этом же справочнике ограничим доступность роли «Голосующие» только для типа «Протоколы совещаний» перекрыв разделяемую функцию Filter():
public override List<Sungero.Docflow.IDocumentKind> Filter(List<Sungero.Docflow.IDocumentKind> kinds)
{
var query = base.Filter(kinds);
//Роль Участники голосовани доступна только для Протоколов совещаний.
if (_obj.Type == centrvd.SolutionDataBook.CustomApprovalRole.Type.MinutesVoters)
query = query.Where(k => k.DocumentType.DocumentTypeGuid == "bb4780ff-b2c3-4044-a390-e9e110791bf6").ToList();
return query;
}
Чтобы правильно определялись исполнители этапа голосования, в перекрытом справочнике ApprovalStage создадим серверную функцию GetStageRecipients():
/// <summary>
/// Получить исполнителей этапа без раскрытия групп и ролей.
/// </summary>
/// <param name="task">Задача.</param>
/// <param name="additionalApprovers">Доп.согласующие.</param>
/// <returns>Исполнители.</returns>
[Remote(IsPure = true), Public]
public override List<IRecipient> GetStageRecipients(Sungero.Docflow.IApprovalTask task,
List<IRecipient> additionalApprovers)
{
var recipients = base.GetStageRecipients(task, additionalApprovers);
//Роль Участники голосования.
var votersRole = _obj.ApprovalRoles
.Where(x => x.ApprovalRole.Type == centrvd.SolutionDataBook.CustomApprovalRole.Type.MinutesVoters)
.Select(x => centrvd.SolutionDataBook.CustomApprovalRoles.As(x.ApprovalRole)).Where(x => x != null)
.SingleOrDefault();
if (votersRole != null)
recipients.AddRange(centrvd.SolutionDataBook.PublicFunctions.CustomApprovalRole.Remote.GetRolePerformers(votersRole, task));
return recipients;
}
Ограничим доступность роли только для типа «Простое задание» (без доработки), перекрыв разделяемую функцию GetPossibleRoles() в этом же справочнике:
public override List<Enumeration?> GetPossibleRoles()
{
var baseRoles = base.GetPossibleRoles();
//Использовать роль Участники голосования в этапах.
if (_obj.StageType == Sungero.Docflow.ApprovalStage.StageType.SimpleAgr)
baseRoles.Add(centrvd.SolutionDataBook.CustomApprovalRole.Type.MinutesVoters);
return baseRoles;
}
Создадим роль через функции инициализации модуля:
/// <summary>
/// Обработчик события инициализации.
/// </summary>
public override void Initializing(Sungero.Domain.ModuleInitializingEventArgs e)
{
CreateCustomApprovalRole(SolutionDataBook.CustomApprovalRole.Type.MinutesVoters, centrvd.SolutionDataBook.Resources.MinutesVotersRoleName);
}
/// <summary>
/// Создание роли согласования для ролей справочника CustomApprovalRole.
/// </summary>
public static void CreateCustomApprovalRole(Enumeration roleType, string description)
{
var role = CustomApprovalRoles.GetAll().Where(r => Equals(r.Type, roleType) && r.Status == Sungero.Docflow.ApprovalRole.Status.Active).FirstOrDefault();
// Проверяет наличие роли.
if (role == null)
{
role = CustomApprovalRoles.Create();
role.Type = roleType;
}
role.Description = description;
role.Save();
InitializationLogger.Debug(String.Format("Создана роль '{0}'", description));
}
Самое крутое, что в одном задании сразу идет голосование по каждому пункту протокола.
Добрый день.
А как это голосование будет отображаться в мобильных приложениях? И будет ли?
Авторизуйтесь, чтобы написать комментарий