Корректность работы детали "Участники" в объекте "Активности"
Добрый вечер, Коллеги!
Подскажите корректно ли работает деталь "Участники" в следующем кейсе:
Шаг1. При создании задачи автор указывает в поле "Ответственный" значение "Исполнитель1". В результате Система автоматически добавляет в деталь "Участники" запись с "Исполнителем1"
Шаг2. Автор заменяет в поле "Ответственный" значение "Исполнитель1". В результате Система автоматически добавляет в деталь "Участники" запись с "Исполнителем2" (удаляя "Исполнителя1") и "Автора"
---------------------------------------------------------------------
Вопрос почему Система ведет себя по-разному:
по логике для всех случаев должен быть один вариант:
либо автора задачи Система сразу добавляет в участники, либо никогда (только вручную или через созданный БП).
Сейчас реализован промежуточный вариант - такое поведение преследует какую-то логику или реализация содержит ошибку?
//подскажите куда надо смотреть, чтобы настроить кейс:
- автор при создании задачи добавляется в деталь "Участники"
либо
- автор при изменении ответственного не добавляется в деталь "Участники"
Понравилась ли вам эта идея?
Обратите внимание, что там ещё разная роль (видно, если вытянуть колонку):

Логика реализована во встроенном БП объекта Activity пакета Base:
public virtual void AddActivityParticipantToInsertedValues(Guid participantId, Dictionary<string, object> participantParams, bool overrideExistingParticipant) {
var insertedValues = (InsertedValues as Dictionary<Guid, object>) ?? new Dictionary<Guid, object>();
InsertedValues = insertedValues;
if (overrideExistingParticipant || !insertedValues.ContainsKey(participantId)) {
insertedValues[participantId] = participantParams;
}
}
public virtual bool OnActivitySaved(ProcessExecutingContext context) {
SetActivityParticipantRightsOnSaved();
string typeColumnValueName = Entity.Schema.Columns.FindByName("Type").ColumnValueName;
Guid typeId = Entity.GetTypedColumnValue<Guid>(typeColumnValueName);
if (typeId == Terrasoft.Configuration.ActivityConsts.EmailTypeUId) {
InitializeEmailParticipantHelper().InitializeParameters(Entity);
AutoEmailRelationProceed();
InitializeEmailParticipantHelper().SetEmailParticipants();
} else {
UpdateParticipantsByOwnerContact();
SynchronizeActivityOnSaved();
AutoEmailRelationProceed();
CreateActivityParticipantsFromInsertedValues();
}
return true;
}
...
public virtual void RemoveEmailParticipants() {
var recepientEmails = RecepientsEmailsForDelete as List<string>;
if ((recepientEmails != null) && (recepientEmails.Count > 0)) {
var queryParameters = recepientEmails.Select(item => new QueryParameter(item)).ToList();
new Delete(UserConnection)
.From("ActivityParticipant")
.Where("ActivityId").IsEqual(Column.Parameter(Entity.PrimaryColumnValue))
.And("ParticipantId").In(
new Select(UserConnection)
.Column("Id")
.From("Contact")
.Where("Email").In(queryParameters)).Execute();
}
}
public virtual void UpdateParticipantsByOwnerContact() {
DeleteOldOwnerAndContactParticipants();
var participantRoles = ActivityUtils.GetParticipantsRoles(UserConnection);
if ((newOwnerId != oldOwnerId) && (newOwnerId != Guid.Empty) && (newOwnerId != SenderId)) {
AddActivityParticipantToInsertedValues(
newOwnerId,
new Dictionary<string, object> {
{"RoleId", participantRoles["Responsible"]}
},
true
);
if (oldOwnerId != Guid.Empty) {
var authorColumn = Entity.Schema.Columns.FindByName("Author");
if (authorColumn != null) {
var authorId = Entity.GetTypedColumnValue<Guid>(authorColumn.ColumnValueName);
AddActivityParticipantToInsertedValues(
authorId,
new Dictionary<string, object> {
{"RoleId", participantRoles["Participant"]}
},
false
);
}
}
}
if ((newContactId != oldContactId) && (newContactId != Guid.Empty) && (newContactId != newOwnerId) && (newContactId != SenderId)) {
AddActivityParticipantToInsertedValues(
newContactId,
new Dictionary<string, object> {
{"RoleId", participantRoles["Participant"]}
},
false
);
}
}
Подробнее все вызовы этих функций см. там. Функции виртуальные, значит, их можно переопределить в этой схеме в своём пакете.
Зверев Александр,
Спасибо за ответ, то что нужно (+ отдельное спасибо за то, что обрати внимание на роль).
Еще остался вопрос, - все таки это корректная работа, что автор при создании задачи участников в деталь не добавляется, но добавляется только после изменения значения в поле "Ответственный"? Какой бизнес-смысл такого базового поведения Системы (я бы даже сказал не очевидного)
+ влияет ли роль "Участник" и "Ответственный" на какие либо процессы? На сколько я успел протестировать - нет.
По том, как надо правильнее, уточняю.
Посмотреть все места, где используется объект справочника ParticipantRole, можно выборкой по тексту C# и JS-схем.
select *, cast(source as varchar(max)) from sysschemasource where source like '%participantRole%'
select *, cast(content as varchar(max)), (select name from sysschema where id = [SysSchemaId]) from sysschemacontent where content like '%participantRole%'
Как минимум, в Activity пакета Exchange для синхронизации встреч тоже добавляют именно «Участник»:
public virtual void SynchronizeParticipants() {
if (string.IsNullOrEmpty(Entity.Sender) && string.IsNullOrEmpty(Entity.Recepient)) {
return;
}
if (Entity.GetTypedColumnValue<Guid>("ActivityCategoryId") == ExchangeConsts.ActivityMeetingCategoryId) {
var recepientsEmails = (List<string>)RecepientsEmails;
Dictionary<Guid, string> contactDictionary = ContactUtilities.GetContactsByEmails(UserConnection, recepientsEmails);
if (contactDictionary.Count > 0) {
var participantRoles = ActivityUtils.GetParticipantsRoles(UserConnection);
foreach (var contactId in contactDictionary.Keys) {
AddActivityParticipantToInsertedValues(
contactId,
new Dictionary<string, object> {
{"RoleId", participantRoles["Participant"]}
},
false
);
}
var insertedValues = InsertedValues as Dictionary<Guid, object>;
if (insertedValues != null) {
UpdateContactAndAccountByParticipant(insertedValues.Keys.ToList());
}
}
Entity.SetColumnValue("Sender", string.Empty);
Entity.SetColumnValue("Recepient", string.Empty);
}
}Но это вставка, а не считывание.
Ещё константы ролей для писем используется в CommunicationPanelEmailSchema в условии фильтрации входящих/исходящих:
/**
* Adds requested email type filters.
* @private
* @param {Terrasoft.FilterGroup} filters Emails query filters.
* @param {Terrasoft.EntitySchemaQuery} esq Emails query.
*/
_addFilterByEmailType: function(filters, esq) {
var roleColumn = "Role";
var emailType = this.get("EmailType");
if (!this.getIsFeatureEnabled("SharedMailboxes") || emailType === EmailConstants.emailType.DRAFT) {
var currentUserContact = Terrasoft.SysValue.CURRENT_USER_CONTACT.value;
filters.add("currentContactFilter", this.Terrasoft.createColumnFilterWithParameter(
Terrasoft.ComparisonType.EQUAL, "Owner", currentUserContact));
}
var participantRoles = ConfigurationConstants.Activity.ParticipantRole;
switch (emailType) {
case EmailConstants.emailType.INCOMING:
this._addIncomingFilter(filters, roleColumn, [
participantRoles.To, participantRoles.CC,
participantRoles.BCC
]);
break;
case EmailConstants.emailType.OUTGOING:
this._addOutgoingFilter(filters, roleColumn, [participantRoles.From]);
break;
case EmailConstants.emailType.DRAFT:
this._addDraftFilter(filters);
break;
default:
break;
}
},Но там из того же справочника другие значения ролей.
Передал им информацию, это ошибочное поведение платформы, будут исправлять в плановом порядке. Спасибо, что нашли.
Зверев Александр пишет:
Функции виртуальные, значит, их можно переопределить в этой схеме в своём пакете.
А не подскажите, как это сделать? Нам как раз надо заменить заполнение ActivityParticipants и раздачу прав в проекте, то есть, функции SetActivityParticipantRightsOnSaved, DeleteOldOwnerAndContactParticipants, UpdateParticipantsByOwnerContact
Владимир, я имел в виду создать в пакете Custom замещение объекта Activity и там в БП справа на вкладке «Методы» вписать свой вариант этой функции (можно с вызовом в начале или конце базового).
Например, в той же схеме Activity в пакете NUI так переопределяют PrepereSynchronizeSubjectRemindingUserTask, заданный в Base:
public override void PrepereSynchronizeSubjectRemindingUserTask(SynchronizeSubjectRemindingUserTask userTask, Guid contact, DateTime remindTime, bool active, Guid source) {
base.PrepereSynchronizeSubjectRemindingUserTask(userTask, contact, remindTime, active, source);
//...своя логика...
}