报价验证插件不会阻止在 InvalidPluginExecutionException 上关闭报价
Quote Validation Plugin Not Preventing Closing of Quote on InvalidPluginExecutionException
我正在编写一个插件来在保存之前验证报价。我们要强制要求必须有一个报价产品线
可以激活、赢得或丢失报价之前的报价项目。草稿模式没有这个要求
我编写了以下代码,当按下功能区上的 "Close Quote" 按钮并选择 Won
作为原因时,会弹出一个包含错误消息的业务流程错误框。
但是,关闭错误消息并刷新页面后,报价将设置为已关闭。为什么即使抛出异常,报价也会关闭?
仅供参考,插件阶段设置为预操作。
这是我的源代码(更新于 10/02/2017):
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Query;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ValbrunaPlugins
{
public class QuoteValidation : IPlugin
{
private ITracingService tracingService;
public void Execute(IServiceProvider serviceProvider)
{
// retrieve the context, factory, and service
IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
IOrganizationServiceFactory factory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
IOrganizationService service = factory.CreateOrganizationService(context.UserId);
bool isCorrectEvent = context.MessageName == "SetStateDynamicEntity" || context.MessageName == "SetState" || context.MessageName == "Win" || context.MessageName == "Close";
bool hasEnityMoniker = context.InputParameters.Contains("EntityMoniker");
// ensure we are handling the correct event and we were passed an entity from the context
if (!isCorrectEvent || !hasEnityMoniker) return;
// get the reference to the quote entity
EntityReference quoteEntityReference = (EntityReference)context.InputParameters["EntityMoniker"];
Entity quoteEntity = null;
try
{
// get the quote entity from the entity reference
quoteEntity = ActualEntity.GetActualEntity(quoteEntityReference, service);
} catch (Exception ex)
{
throw new InvalidPluginExecutionException("Quote with id " + quoteEntityReference.Id + " not found.");
}
// ensure that we have the correct entity
if (quoteEntity.LogicalName != "quote") return;
// write query to retrieve all the details for this quote
QueryExpression retrieveQuoteDetailsQuery = new QueryExpression
{
EntityName = "quotedetail",
ColumnSet = new ColumnSet(),
Criteria = new FilterExpression
{
Conditions =
{
new ConditionExpression
{
AttributeName = "quoteid",
Operator = ConditionOperator.Equal,
Values = { (Guid)quoteEntity.Id }
}
}
}
};
// execute the query to retrieve the details for this quote
EntityCollection quoteDetails = service.RetrieveMultiple(retrieveQuoteDetailsQuery);
// retrieve the current status of the quote
// 0 - Draft
// 1 - Active
// 2 - Won
// 3 - Closed
int quoteStatus = ((OptionSetValue)(quoteEntity.Attributes["statecode"])).Value;
// if not in draft mode
if (quoteStatus != 0)
{
// if the amount of details for the quote is less than 1
if (quoteDetails.Entities.Count < 1)
{
throw new InvalidPluginExecutionException("There must be a quote product line item on a quote before a quote can be activated, won, or lost while not in draft mode.");
}
}
}
}
}
2017 年 10 月 2 日更新:
我为 SetState 创建了一个单独的步骤并更新了我的源代码。
但是,我仍然遇到同样的问题。当我关闭报价时出现错误,但当我刷新页面时,报价已设置为关闭。
注意:报价处于活动状态,没有报价详情,因此无法赢得报价。
按应有的方式显示业务流程错误。但是,当我刷新页面时,引用的状态已设置为 "Closed"。抛出异常为什么要这么做?
您必须为 SetState
和 SetStateDynamicEntity
消息注册插件步骤。
Why do I need to register on SetState and SetStateDynamicEntity
separately?
As I mentioned earlier there are multiple messages that
perform the same action in CRM. One such example is SetStateRequest
and SetStateDyanmicEntityRequest . If you want to write a plug-in on
SetState, then you need to register it on both messages.
我正在编写一个插件来在保存之前验证报价。我们要强制要求必须有一个报价产品线
可以激活、赢得或丢失报价之前的报价项目。草稿模式没有这个要求
我编写了以下代码,当按下功能区上的 "Close Quote" 按钮并选择 Won
作为原因时,会弹出一个包含错误消息的业务流程错误框。
但是,关闭错误消息并刷新页面后,报价将设置为已关闭。为什么即使抛出异常,报价也会关闭?
仅供参考,插件阶段设置为预操作。
这是我的源代码(更新于 10/02/2017):
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Query;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ValbrunaPlugins
{
public class QuoteValidation : IPlugin
{
private ITracingService tracingService;
public void Execute(IServiceProvider serviceProvider)
{
// retrieve the context, factory, and service
IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
IOrganizationServiceFactory factory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
IOrganizationService service = factory.CreateOrganizationService(context.UserId);
bool isCorrectEvent = context.MessageName == "SetStateDynamicEntity" || context.MessageName == "SetState" || context.MessageName == "Win" || context.MessageName == "Close";
bool hasEnityMoniker = context.InputParameters.Contains("EntityMoniker");
// ensure we are handling the correct event and we were passed an entity from the context
if (!isCorrectEvent || !hasEnityMoniker) return;
// get the reference to the quote entity
EntityReference quoteEntityReference = (EntityReference)context.InputParameters["EntityMoniker"];
Entity quoteEntity = null;
try
{
// get the quote entity from the entity reference
quoteEntity = ActualEntity.GetActualEntity(quoteEntityReference, service);
} catch (Exception ex)
{
throw new InvalidPluginExecutionException("Quote with id " + quoteEntityReference.Id + " not found.");
}
// ensure that we have the correct entity
if (quoteEntity.LogicalName != "quote") return;
// write query to retrieve all the details for this quote
QueryExpression retrieveQuoteDetailsQuery = new QueryExpression
{
EntityName = "quotedetail",
ColumnSet = new ColumnSet(),
Criteria = new FilterExpression
{
Conditions =
{
new ConditionExpression
{
AttributeName = "quoteid",
Operator = ConditionOperator.Equal,
Values = { (Guid)quoteEntity.Id }
}
}
}
};
// execute the query to retrieve the details for this quote
EntityCollection quoteDetails = service.RetrieveMultiple(retrieveQuoteDetailsQuery);
// retrieve the current status of the quote
// 0 - Draft
// 1 - Active
// 2 - Won
// 3 - Closed
int quoteStatus = ((OptionSetValue)(quoteEntity.Attributes["statecode"])).Value;
// if not in draft mode
if (quoteStatus != 0)
{
// if the amount of details for the quote is less than 1
if (quoteDetails.Entities.Count < 1)
{
throw new InvalidPluginExecutionException("There must be a quote product line item on a quote before a quote can be activated, won, or lost while not in draft mode.");
}
}
}
}
}
2017 年 10 月 2 日更新:
我为 SetState 创建了一个单独的步骤并更新了我的源代码。
但是,我仍然遇到同样的问题。当我关闭报价时出现错误,但当我刷新页面时,报价已设置为关闭。
注意:报价处于活动状态,没有报价详情,因此无法赢得报价。
按应有的方式显示业务流程错误。但是,当我刷新页面时,引用的状态已设置为 "Closed"。抛出异常为什么要这么做?
您必须为 SetState
和 SetStateDynamicEntity
消息注册插件步骤。
Why do I need to register on SetState and SetStateDynamicEntity separately?
As I mentioned earlier there are multiple messages that perform the same action in CRM. One such example is SetStateRequest and SetStateDyanmicEntityRequest . If you want to write a plug-in on SetState, then you need to register it on both messages.