Изменение этапа бизнес-процесса в подключаемом модуле C#

Я следую статье это, чтобы изменить этап потока бизнес-процессов в подключаемом модуле C#. Я могу переместить этап на следующий этап, но я получаю сообщение об ошибке, когда пытаюсь вернуться к предыдущему этапу. Ошибка ниже - это то, что я получаю в пользовательском интерфейсе от Dynamics. Когда я отлаживаю плагин, я получаю исключение FaultException<OrganizationServiceFault>, которое не содержит никакой информации. Почему я получаю сообщение об ошибке и как я могу изменить свой код, чтобы успешно вернуться к предыдущему этапу в моем потоке бизнес-процессов?

Ошибка

Unhandled Exception: System.ServiceModel.FaultException`1[[Microsoft.Xrm.Sdk.OrganizationServiceFault, Microsoft.Xrm.Sdk, Version=8.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]: An unexpected error occurred.
Detail: <OrganizationServiceFault xmlns = "http://schemas.microsoft.com/xrm/2011/Contracts" xmlns:i = "http://www.w3.org/2001/XMLSchema-instance">
  <ActivityId>5df51362-b7c1-4817-a8d0-de2d63b15c17</ActivityId>
  <ErrorCode>-2147220970</ErrorCode>
  <ErrorDetails xmlns:a = "http://schemas.datacontract.org/2004/07/System.Collections.Generic" />
  <Message>An unexpected error occurred.</Message>
  <Timestamp>2018-07-19T18:55:42.6625925Z</Timestamp>
  <ExceptionSource i:nil = "true" />
  <InnerFault>
    <ActivityId>5df51362-b7c1-4817-a8d0-de2d63b15c17</ActivityId>
    <ErrorCode>-2147220970</ErrorCode>
    <ErrorDetails xmlns:a = "http://schemas.datacontract.org/2004/07/System.Collections.Generic" />
    <Message>System.NullReferenceException: Microsoft Dynamics CRM has experienced an error. Reference number for administrators or support: #0D309052</Message>
    <Timestamp>2018-07-19T18:55:42.6625925Z</Timestamp>
    <ExceptionSource i:nil = "true" />
    <InnerFault i:nil = "true" />
    <OriginalException i:nil = "true" />
    <TraceText i:nil = "true" />
  </InnerFault>
  <OriginalException i:nil = "true" />
  <TraceText i:nil = "true" />
</OrganizationServiceFault>

Плагин

if (localContext == null)
{
    throw new ArgumentNullException("localContext");
}

IPluginExecutionContext context = localContext.PluginExecutionContext;
IOrganizationService service = localContext.OrganizationService;

Client client = (Client)service.Retrieve(
    Client.LogicalName,
    new Guid("75FE165F-848B-E811-80F3-005056B33317"),
    new ColumnSet(new String[]{
        Client.Properties.ClientId
    })
);

client.ChangeStage(service);

Изменить этап

public void ChangeStage(IOrganizationService service)
{
    // Get Process Instances
    RetrieveProcessInstancesRequest processInstanceRequest = new RetrieveProcessInstancesRequest
    {
        EntityId = this.Id,
        EntityLogicalName = this.LogicalName
    };

    RetrieveProcessInstancesResponse processInstanceResponse = (RetrieveProcessInstancesResponse)service.Execute(processInstanceRequest);

    // Declare variables to store values returned in response
    int processCount = processInstanceResponse.Processes.Entities.Count;
    Entity activeProcessInstance = processInstanceResponse.Processes.Entities[0]; // First record is the active process instance
    Guid activeProcessInstanceID = activeProcessInstance.Id; // Id of the active process instance, which will be used later to retrieve the active path of the process instance

    // Retrieve the active stage ID of in the active process instance
    Guid activeStageID = new Guid(activeProcessInstance.Attributes["processstageid"].ToString());

    // Retrieve the process stages in the active path of the current process instance
    RetrieveActivePathRequest pathReq = new RetrieveActivePathRequest
    {
        ProcessInstanceId = activeProcessInstanceID
    };
    RetrieveActivePathResponse pathResp = (RetrieveActivePathResponse)service.Execute(pathReq);

    string activeStageName = "";
    int activeStagePosition = -1;

    Console.WriteLine("\nRetrieved stages in the active path of the process instance:");
    for (int i = 0; i < pathResp.ProcessStages.Entities.Count; i++)
    {
        // Retrieve the active stage name and active stage position based on the activeStageId for the process instance
        if (pathResp.ProcessStages.Entities[i].Attributes["processstageid"].ToString() == activeStageID.ToString())
        {
            activeStageName = pathResp.ProcessStages.Entities[i].Attributes["stagename"].ToString();
            activeStagePosition = i;
        }
    }

    // Retrieve the stage ID of the next stage that you want to set as active
    activeStageID = (Guid)pathResp.ProcessStages.Entities[activeStagePosition - 1].Attributes["processstageid"];

    // Retrieve the process instance record to update its active stage
    ColumnSet cols1 = new ColumnSet();
    cols1.AddColumn("activestageid");
    Entity retrievedProcessInstance = service.Retrieve("ccseq_bpf_clientsetup", activeProcessInstanceID, cols1);

    // Set the next stage as the active stage
    retrievedProcessInstance["activestageid"] = new EntityReference(ProcessStage.LogicalName, activeStageID);
    service.Update(retrievedProcessInstance);
}

Обновлять

Я нашел статью это, в которой объясняется, как обновить Stage с помощью веб-API. Когда я пробую этот метод, я получаю сообщение об ошибке:

An undeclared property 'activestageid' which only has property annotations in the payload but no property value was found in the payload. In OData, only declared navigation properties and declared named streams can be represented as properties without values.

Я безуспешно пробовал несколько разновидностей activestageid (ActiveStageId, _activestageid_value).


Обновление 2

Основываясь на отзывах Аруна, я безуспешно попробовал следующие вызовы веб-API. Идентификатор в скобках в URL-адресе (ccseq_bpf_clientsetups (###)) я извлек из BusinessProcessFlowInstanceId в таблице ccseq_bpf_clientsetups. Идентификатор этапов процесса, который я извлек из ProcessStageId в таблице ProcessStageBase.

// Attempt 1
PATCH /COHEN/api/data/v8.2/ccseq_bpf_clientsetups(bc892aec-2594-e811-80f4-005056b33317) HTTP/1.1
{ "[email protected]": "/processstages(70018854-db7c-4612-915b-2ad7870a8574)"}

// Attempt 2
PATCH /COHEN/api/data/v8.2/ccseq_bpf_clientsetups(bc892aec-2594-e811-80f4-005056b33317) HTTP/1.1
{ "[email protected]": "/processstages(70018854-db7c-4612-915b-2ad7870a8574)"}

// Attempt 3
PATCH /COHEN/api/data/v8.2/ccseq_bpf_clientsetups(bc892aec-2594-e811-80f4-005056b33317) HTTP/1.1
{ "[email protected]": "/processstages(70018854-db7c-4612-915b-2ad7870a8574)"}

Обновление 3

Я загрузил CRM Rest Builder от jLattimer и попытался запустить JavaScript, созданный его инструментом. Код был идентичен тому, что я написал ранее, и, к сожалению, не работал. На данный момент я вполне уверен, что изменение этапов не поддерживается в версии 8.2 веб-API.

Есть ли обязательные поля на текущем этапе? Это может помешать вам покинуть активную стадию.

jasonscript 20.07.2018 04:25

@jasonscript Нет, на текущем этапе нет обязательных полей. Мы пробовали отступить в законченном БПФ, а также на незавершенном БПФ. В обоих случаях на текущем этапе не было обязательных полей. В случае неполного BPF на предыдущем этапе было обязательное поле, к которому мы пытались вернуться, но оно имеет указанное значение

Tim Hutchison 20.07.2018 15:14
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
8
2
8 497
3

Ответы 3

У меня есть код, который пытается переместить этап потока бизнес-процесса вперед в качестве настраиваемого этапа рабочего процесса (а не плагина). Я разместил это ниже.

Разница, которую я вижу:

  • Я двигаюсь вперед (не назад)
  • Я, наверное, не следую лучшей практике :)
  • Я не извлекаю active path, я просто получаю все доступные этапы процесса
  • Я также устанавливаю свойство TraversedPath

Код:

var activeInstancesRequest = new RetrieveProcessInstancesRequest
{
    EntityId          = TargetEntity.Id,
    EntityLogicalName = TargetEntity.LogicalName
};
var activeInstancesResponse = (RetrieveProcessInstancesResponse)base.OrgService.Execute(activeInstancesRequest);
var process = activeInstancesResponse.Processes.Entities.Select(x => x.ToEntity<BusinessProcessFlowInstance>()).ToList();
var stages = base.XrmContext.ProcessStageSet
    .Where(s => s.ProcessId.Id == process.FirstOrDefault().ProcessId.Id)
    .Select(s => new ProcessStage
    {
        ProcessStageId = s.ProcessStageId,
        StageName = s.StageName
    })
    .ToList();

var targetStage = stages.Where(stage => stage.StageName == targetStageName).FirstOrDefault();
if (targetStage != null)
{
    crmWorkflowContext.Trace($"BPF contains target stage (\"{targetStageName}\"). Attempting to update BPF");

    // Setting the Traversed Path is necessary for the Business Process Flow to show the active Stage
    // If this is not updated then although the new Stage is set as current, the previous Stage remains actively selected
    var traversedPath = $"{bpf.TraversedPath},{targetStage.ProcessStageId.Value}";
    var update = new BusinessProcessFlowInstance()
    {
        BusinessProcessFlowInstanceId = bpf.BusinessProcessFlowInstanceId,
        ProcessStageId                = targetStage.ProcessStageId,
        TraversedPath                 = traversedPath   
    };

    xrmContext.Attach(update);
    xrmContext.UpdateObject(update);
}

Я тестировал быстро, я мог двигаться вперед / назад в Lead to Opportunity Sales Process в онлайн-организации vanilla v9.

Я просто успешно протестировал запрос Web API PATCH от CRM REST builder.

Разработать, чтобы предложить (вперед)

var entity = {};
entity["[email protected]"] = "/processstages(3A275C22-FC45-4E89-97FC-41E5EC578743)";

var req = new XMLHttpRequest();
req.open("PATCH", Xrm.Page.context.getClientUrl() + "/api/data/v8.2/leadtoopportunitysalesprocesses(1674DB10-1994-E811-A969-000D3A1A9FA9)", true);

Предлагаю разработать (назад)

var entity = {};
entity["[email protected]"] = "/processstages(BFC9108C-8389-406B-9166-2C3298A2E41F)";

var req = new XMLHttpRequest();
req.open("PATCH", Xrm.Page.context.getClientUrl() + "/api/data/v8.2/leadtoopportunitysalesprocesses(1674DB10-1994-E811-A969-000D3A1A9FA9)", true);

Это не что иное, как отмеченное ниже как необходимое.

Не требуется
1574DB10-1994-E811-A969-000D3A1A9FA9 - leadid
919E14D1-6489-4852-ABD0-A63A6ECAAC5D - processid
f99b4d48-7aad-456e-864a-8e7d543f7495, bfc9108c-8389-406b-9166-2c3298a2e41f - traversedpath

Нужный
1674DB10-1994-E811-A969-000D3A1A9FA9 - businessprocessflowinstanceid
BFC9108C-8389-406B-9166-2C3298A2E41F - activestageidразвивать
3A275C22-FC45-4E89-97FC-41E5EC578743 - activestageidпредлагать


Обновлять:

Я также успешно протестировал приведенный ниже фрагмент в версии 8 [Версия 1612 (8.2.2.2160) (БД 8.2.2.2160) онлайн]

Фактически он двигался вперед / назад безtraversedpath.

var entity = {};
entity["[email protected]"] = "/processstages(BFC9108C-8389-406B-9166-2C3298A2E41F)";
entity.traversedpath = "f99b4d48-7aad-456e-864a-8e7d543f7495,bfc9108c-8389-406b-9166-2c3298a2e41f";

var req = new XMLHttpRequest();
req.open("PATCH", Xrm.Page.context.getClientUrl() + "/api/data/v8.0/leadtoopportunitysalesprocesses(E5B70E69-2094-E811-8145-C4346BDCF2F1)", true);

Но получил ошибку Bad request со следующим:

entity.activestageid = {
            Id: "3A275C22-FC45-4E89-97FC-41E5EC578743",
            LogicalName: "processstage"
        };

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

Tim Hutchison 30.07.2018 19:33

@TimHutchison Я проверил, что пройденный путь быстро вычисляется как в прямом, так и в обратном направлении. Я также помню, что делал это в v8 вручную ..

Arun Vinoth 30.07.2018 19:38

@TimHutchison Я попробовал тот же запрос (+ пройденный путь) от построителя CRM Rest в одной из резервных организаций v8, получаю ошибку неверного запроса. :(

Arun Vinoth 30.07.2018 19:58

Я обновил свой ответ своей попыткой вашего решения. Есть дополнительные мысли?

Tim Hutchison 30.07.2018 20:57

@TimHutchison забудьте про свой BPF, даже изменение OOB BPF не работает. :) пробовал? этот бесполезный Плохой запрос продолжает поступать ..

Arun Vinoth 30.07.2018 21:55

Я не совсем понимаю, что ты говоришь, Арун. Что вы имеете в виду, говоря «забудьте про свой BPF, даже если изменение OOB BPF не работает»?

Tim Hutchison 30.07.2018 22:35

@TimHutchison Я получил ошибку в версии 8 для OOB Lead BPF. Неважно, умеет разбираться. см. обновление в ответе, и вы также можете быстро протестировать в своей организации, импортировав конструктор CRM Rest. Это поможет идентифицировать проблему с окружающей средой, логической проблемой и ошибкой версии продукта OOB.

Arun Vinoth 30.07.2018 22:57

@TimHutchison. У вас была возможность проверить код с другим OOB BPF в вашей среде?

Arun Vinoth 03.08.2018 15:27

Я не смог вернуться к этой части головоломки, поскольку у нас не хватает времени, и другие вещи стали более приоритетными. Я назначил награду в знак признательности за вашу помощь в этом вопросе и потому, что ожидаю, что этот ответ в конечном итоге приведет меня к решению. Спасибо!

Tim Hutchison 03.08.2018 16:08

@TimHutchison, спасибо, извините за это, на самом деле я хотел решить эту проблему, указав / угловую основную причину в вашем сценарии BPF или версии среды, тогда мы бы продолжили исправление ошибки продукта или обходной путь ..

Arun Vinoth 03.08.2018 16:26

У меня была такая же проблема. Это намного проще, чем вы думаете. ОК, откройте расширенный поиск и выберите {ваш BPF}. Добавьте два столбца в запрос: {ваш объект} {пройденный путь}.

Хорошо, поэтому посмотрите на пройденный путь для объекта, который фактически находится на предыдущем этапе (тот, к которому вы хотите вернуться).

С помощью вашего кода вам нужно динамически разбить пройденный путь (.Split (',')) или что-то подобное ... удалить последний этап (тот, в котором вы сейчас находитесь), и вуаля! Вы готовите на бензине.

если текущий пройденный путь был массивом:

string[] currentPath = {"A", "B", "C", "D"};

ваш предыдущий путь должен быть:

string[] previousPath = {"A", "B", "C"};

Вот как вы могли бы сделать это в коде, предполагая, что «entity» - это ваша извлеченная сущность:

string traversedPath = (string) entity["traversedpath"];
string[] stages = traversedPath.Split(',');
string newPath = "";
//use length - 1 to omit last stage (your current stage)
for (int i = 0; i < stages.Length - 1; i++) 
{ 
     if (i != stages.Length - 1)
     newPath += stages[i] + ",";
     else
     newPath += stages[i];

}
entity["processid"] = new Guid("BPF Guid") //may be optional
entity["stageid"] = new Guid("previous stage guid");
entity["traversedpath"] = newPath;
service.Update(entity);

По сути, пройденный путь не + = 'ваш предыдущий этап' до конца пройденного пути. Он хочет установить пройденный путь на ОРИГИНАЛЬНЫЙ пройденный путь для «вашего предыдущего этапа». Узнайте, какой пройденный путь соответствует желаемой стадии, и либо жестко запрограммируйте эту присоску в строку (если она только собирается перейти на эту стадию, когда-либо) .. или сделайте это программно с помощью метода .Split (',') на Атрибут Entity ["traversedpath"] в коде.

Помните, вы вычитаете, а не добавляете к пройденному пути. Мне потребовалось много недействительных ошибок пройденного пути, чтобы прийти к такому выводу ... и это работает. Удачи!

Другие вопросы по теме