Идея
Обсуждение

Корректность работы детали "Участники" в объекте "Активности"

Добрый вечер, Коллеги!

 

Подскажите корректно ли работает деталь "Участники" в следующем кейсе:

Шаг1. При создании задачи автор указывает в поле "Ответственный" значение "Исполнитель1". В результате Система автоматически добавляет в деталь "Участники" запись с "Исполнителем1"

Шаг2. Автор заменяет в поле "Ответственный" значение "Исполнитель1". В результате Система автоматически добавляет в деталь "Участники" запись с "Исполнителем2" (удаляя "Исполнителя1") и "Автора"

---------------------------------------------------------------------

Вопрос почему Система ведет себя по-разному:

по логике для всех случаев должен быть один вариант:

либо автора задачи Система сразу добавляет в участники, либо никогда (только вручную или через созданный БП).

Сейчас реализован промежуточный вариант - такое поведение преследует какую-то логику или реализация содержит ошибку?

 

//подскажите куда надо смотреть, чтобы настроить кейс:

- автор при создании задачи добавляется в деталь "Участники"

либо

- автор при изменении ответственного не добавляется в деталь "Участники"

 

7 комментариев

Обратите внимание, что там ещё разная роль (видно, если вытянуть колонку):

Логика реализована во встроенном БП объекта 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);
	//...своя логика...
}

 

Показать все комментарии