Стоит следующая задача: связать состояния обращений, проблем и конфигурационных единиц так, чтобы закрытии обращения/проблемы закрывались и связанные с ними проблемы/обращения. А между конфигурационными единицами устанавливалась степень воздействия друг на друга (Полностью выводит из строя, частично выводит из строя, не влияет на КЕ), так чтобы при выводе из строя одной КЕ, так же выходили из строя (менялись состояния) КЕ, на которую влияет данная.

Связь между КЕ построена на основе детали «Влияет на конфигурации» и поля «Родительская КЕ» с пометками о степени влияния.

Для этого в объект Конфигурационная единица добавлены следующие логические поля:
1. «Полностью выводит из строя КЕ» FullInfluence
2. «Не влияет на КЕ» NotInfluence
3. «Частично выводит из строя КЕ» PartInfluence
И поле InheritStatus – справочник «ConfigItemStatus». В это поле будут записываться состояния, полученные КЕ в результате применения БП автоматизации.
И добавляем новую деталь «Влияет на конфигурации»:

Далее создаем БП, запускающийся при изменении состояния КЕ:

Автоматизация изменения состояний КЕ построена на основе алгоритма рекурсивного обхода дерева:

В скрипте вызываем метод рекурсивного обхода дерева КЕ:

var confItem = new Terrasoft.Configuration.ConfItem(UserConnection);
confItem.FetchFromDB(StartSignal1.RecordId);

ListGuid> confItemIds = new ListGuid>(); // здесь сохраняем Id пройденных узлов
ListGuid> changedIds = new ListGuid>(); // здесь Id КЕ с измененным состоянием

RecursiveUp(confItemIds, confItem, changedIds);

В переменные БП добавляем уникальные идентификаторы, в которые записываем идентификаторы состояний КЕ (эти состояния должны быть добавлены в справочник ConfigItemStatus):
1) PartWorkId
2) NotWorkId
3) WorkId
В бизнес-процесс добавляем метод:

void RecursiveUp(ListGuid> confItemIds, Terrasoft.Configuration.ConfItem confItem, ListGuid> changedIds)
{

        var updateTemplate = "Update ConfItem Set InheritStatusId = '{0}' Where Id = '{1}'"; // шаблон запроса обновления статуса КЕ
//Находим КЕ, указанные на детали «Влияние», на которые влияет КЕ текущего узла дерева:
        var esq = new EntitySchemaQuery(UserConnection.EntitySchemaManager, "ConfItemInfluence");
        esq.AddAllSchemaColumns();
        esq.Filters.Add(esq.CreateFilterWithParameters(FilterComparisonType.Equal, "ParentConfItem", confItem.Id));
        esq.Filters.Add(esq.CreateFilterWithParameters(FilterComparisonType.Equal, "NotInfluence", 0));

        var entities = esq.GetEntityCollection(UserConnection);

//Обходим все найденные КЕ:
        foreach (var entity in entities)
        {
                var confItemInfluenceDetail = entity as Terrasoft.Configuration.ConfItemInfluence;

                var confItemInfluence = confItemInfluenceDetail.ConfItem;

//Проверяем обходили ли мы уже полученный узел:

                if (!confItemIds.Contains(confItemInfluenceDetail.ConfItemId) && confItemInfluence != null && confItemInfluence.FetchFromDB(confItemInfluenceDetail.ConfItemId))
                {

//Далее в зависимости от состояния КЕ текущего узла, меняем статус КЕ, указанной на детали:
                        if (confItem.InheritStatusId == NotWorkId)
                        {
                                if (confItemInfluenceDetail.FullInfluence)
                                {
                                        (new CustomQuery(UserConnection, String.Format(updateTemplate, NotWorkId, confItemInfluenceDetail.ConfItemId))).Execute();
                                        confItemInfluence.InheritStatusId = NotWorkId;                         
                                }
                                else if (confItemInfluence.InheritStatusId != NotWorkId)
                                {
                                        (new CustomQuery(UserConnection, String.Format(updateTemplate, PartWorkId, confItemInfluenceDetail.ConfItemId))).Execute();
                                        confItemInfluence.InheritStatusId = PartWorkId;
                                }
                        }
                        else if (confItem.InheritStatusId == PartWorkId)
                        {
                                if (confItemInfluenceDetail.FullInfluence && confItemInfluence.InheritStatusId != NotWorkId)
                                {
                                        (new CustomQuery(UserConnection, String.Format(updateTemplate, PartWorkId, confItemInfluence.Id))).Execute();
                                        confItemInfluence.InheritStatusId = PartWorkId;
                                }
                        }
                        else if (confItem.InheritStatusId == WorkId)
                        {
                                (new CustomQuery(UserConnection, String.Format( updateTemplate, WorkId, confItemInfluenceDetail.ConfItemId))).Execute();
                                        confItemInfluence.InheritStatusId = WorkId;
                        }

//Проверяем наличие КЕ, которые влияют на КЕ текущего узла:

                        var notWorkingCount = (new CustomQuery(UserConnection, String.Format("Select Count(1) from ConfItemInfluence ci join ConfItem c on c.Id = ci.ParentConfItemId Where ConfItemId = '{0}' and ci.NotInfluence = 0 and c.InheritStatusId = '{1}'  and ci.FullInfluence = 1", confItemInfluence.Id, NotWorkId))).ExecuteScalarint>();

//Если такие КЕ найдены, то меняем состояния КЕ текущего узла:
                        if (notWorkingCount > 0)
                        {
                                (new CustomQuery(UserConnection, String.Format(updateTemplate, NotWorkId, confItemInfluence.Id))).Execute();
                                CompareStatus(confItemInfluence, NotWorkId, changedIds);
                                confItemInfluence.StatusId = NotWorkId;
                                confItemIds.Add(confItemInfluence.Id);
                        }
                        else if ((new CustomQuery(UserConnection, String.Format("Select Count(1) from ConfItemInfluence ci join ConfItem c on c.Id = ci.ParentConfItemId Where ConfItemId = '{0}' and ci.NotInfluence = 0 and ( (c.InheritStatusId = '{1}') or (c.InheritStatusId = '{2}'  and ci.FullInfluence = 0) )", confItemInfluence.Id, PartWorkId, NotWorkId))).ExecuteScalarint>() > 0)
                        {
                                (new CustomQuery(UserConnection, String.Format updateTemplate, PartWorkId, confItemInfluence.Id))).Execute();
                                confItemInfluence.StatusId = PartWorkId;
                                confItemIds.Add(confItemInfluence.Id);
                        }

//Переходим к следующему узлу дерева:
                        RecursiveUp(confItemIds, confItemInfluence, changedIds);
                       
                }
        }
}

Алгоритм состоит в следующем: Для имеющейся КЕ находим все те КЕ, на которые она оказывает частичное или полное влияние и изменяем состояние связанной КЕ. Вместе с этим запоминаем идентификаторы КЕ, которые мы прошли или состояние которых изменилось (confItemIds /changedIds). Это помогает решить две задачи: по списку измененных КЕ можно отослать уведомления об их изменении, либо, если мы предполагаем, что в дереве могут быть циклические зависимости, не обходить уже пройденные узлы.
Изменения состояний КЕ производятся через CustomQuery, т.к. процесс автоматизации запускается по сигналу изменения состояния и нельзя чтобы при изменении состояния КЕ при обходе дерева процесс снова запускался.
Автоматизация связей проблем/обращений/КЕ происходит с помощью следующего бизнес-процесса:

В данном случае алгоритм простой:
Читаем данные обращения

caseItem.FetchFromDB(recordId);

var problemId = caseItem.ProblemId;
var statusId = caseItem.StatusId;

var closeCaseStatus = new Guid("3E7F420C-F46B-1410-FC9A-0050BA5D6C38");
var newCaseStatus = new Guid("AE5F2F10-F46B-1410-FD9A-0050BA5D6C38");
var workCaseStatus = new Guid("7E9F1204-F46B-1410-FB9A-0050BA5D6C38");

var closeProblemStatus = new Guid("459361BE-C3B5-4D6B-9DEC-C8DCB49AE5B3");
var newProblemStatus = new Guid("340A7737-3CFF-4BED-A292-B83CD25DA3DA");
var workProblemStatus = new Guid("1B79DF4C-17AB-4A75-95E3-C53E3793F1C6");

var workConfItemStatus = new Guid("742E7BCE-6191-4AA5-9D16-DB26CF87BC98");
var notWorkConfItemStatus = new Guid("5C97181E-090F-4893-9A21-DEF3161F2347");

var newProblemStatusId = Guid.Empty;
var newConfItemStatusId = Guid.Empty;

Устанавливаем новые статусы обращений и проблем

if(statusId == closeCaseStatus)
{
        newProblemStatusId = closeProblemStatus;
        newConfItemStatusId = workConfItemStatus;
}
else if(statusId == newCaseStatus)
{
        newProblemStatusId = newProblemStatus;
        newConfItemStatusId = notWorkConfItemStatus;
}
else if(statusId == workCaseStatus)
{
        newProblemStatusId = workProblemStatus;
        newConfItemStatusId = notWorkConfItemStatus;
}

Находим зависимые КЕ обращения:

var confItemList = new ListGuid>();
var esq = new EntitySchemaQuery(UserConnection.EntitySchemaManager, "ConfItemInCase");

...

var entities = esq.GetEntityCollection(UserConnection);
                       
foreach(var entity in entities)
{
        var confItemInCase = entity as Terrasoft.Configuration.ConfItemInCase;
        confItemList.Add(confItemInCase.ConfItemId);
}

и проблемы:
var problem = caseItem.Problem;

if(problem != null && problem.FetchFromDB(problemId))
{

        if(!confItemList.Contains(problem.ConfItemId))
        {
                confItemList.Add(problem.ConfItemId);
        }

        if(newProblemStatusId != Guid.Empty)
        {
               

Обновляем состояние связанных проблем:
new CustomQuery(UserConnection, String.Format("Update Problem Set StatusId = '{0}' Where Id = '{1}'", newProblemStatusId, problemId)).Execute();
                if(statusId == closeCaseStatus)
                {
                        new CustomQuery(UserConnection, String.Format("delete from Reminding Where SubjectId = '{0}'", problemId)).Execute();
                }
        }
}

Проходим все зависимые КЕ и меняем их статус, инициируя процесс автоматизации состояний КЕ:

if(newConfItemStatusId != Guid.Empty)
{
        foreach(var confItemId in confItemList)
        {
                var confItem = new Terrasoft.Configuration.ConfItem(UserConnection);
                if(confItem.FetchFromDB(confItemId))
                {
                        confItem.StatusId = newConfItemStatusId;
                        confItem.Save();
                }
        }
}

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

Нравится

1 комментарий

Очень полезный материал. Спасибо!

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