You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Dynamics CRM商机子网格同步问题:预计时间变更未同步至子网格

Fixing Dynamics CRM Plugin to Sync Opportunity's Estimated Close Date to Opportunity Products

Let's break down what's going wrong with your plugin and fix it step by step:

Key Issues in Your Original Code

  • Incorrect trigger condition: You're checking if the name field exists in the target entity, but your requirement is to react when estimatedclosedate changes. This means your code only runs when the opportunity's name is updated, not when the close date is modified.
  • Wrong FetchXML query: You're querying the opportunity entity instead of opportunityproduct (the related line items). The code also references contacts unnecessarily—you need to target linked opportunity products, not contacts.
  • Invalid update logic: You're trying to update an opportunityproduct using the ID of an opportunity record. The contactEnt variable holds an opportunity record, so you're attempting to modify a non-existent opportunity product with an invalid ID.

Corrected Plugin Code

public void Execute(IServiceProvider serviceProvider)
{
    // Extract core services from the provider
    ITracingService tracingService = (ITracingService)serviceProvider.GetService(typeof(ITracingService));
    IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
    IOrganizationServiceFactory serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
    IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);

    try
    {
        // Verify we're handling an Opportunity Update message with a valid target
        if (context.MessageName == "Update" && context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity opportunityEntity)
        {
            // Get the new estimated close date (check Target first, then Post Image as fallback)
            DateTime? newCloseDate = null;
            if (opportunityEntity.Contains("estimatedclosedate"))
            {
                newCloseDate = (DateTime?)opportunityEntity["estimatedclosedate"];
            }
            else if (context.PostEntityImages.Contains("PostImage") && context.PostEntityImages["PostImage"].Contains("estimatedclosedate"))
            {
                newCloseDate = (DateTime?)context.PostEntityImages["PostImage"]["estimatedclosedate"];
            }

            if (newCloseDate.HasValue)
            {
                tracingService.Trace($"Syncing close date {newCloseDate.Value} to products for Opportunity ID: {opportunityEntity.Id}");

                // Fetch all opportunity products linked to this opportunity
                string fetchXml = $@"
                <fetch no-lock='true'>
                    <entity name='opportunityproduct'>
                        <attribute name='opportunityproductid'/>
                        <filter>
                            <condition attribute='opportunityid' operator='eq' value='{opportunityEntity.Id}'/>
                        </filter>
                    </entity>
                </fetch>";

                EntityCollection opportunityProducts = service.RetrieveMultiple(new FetchExpression(fetchXml));
                tracingService.Trace($"Found {opportunityProducts.Entities.Count} products to update");

                // Update each product's new_valid_to field
                foreach (Entity product in opportunityProducts.Entities)
                {
                    Entity productToUpdate = new Entity("opportunityproduct", product.Id);
                    productToUpdate["new_valid_to"] = newCloseDate.Value;
                    service.Update(productToUpdate);
                    tracingService.Trace($"Updated Product ID: {product.Id} with new_valid_to: {newCloseDate.Value}");
                }
            }
            else
            {
                tracingService.Trace("No valid estimatedclosedate value found to sync");
            }
        }
    }
    catch (Exception ex)
    {
        tracingService.Trace($"Plugin error: {ex.Message}");
        throw new InvalidPluginExecutionException("Failed to sync opportunity close date to products: " + ex.Message, ex);
    }
}

Critical Improvements Explained

  • Targeted trigger: We first confirm this is an Update message for an Opportunity, then check if estimatedclosedate is present (either in the Target or Post Image, to cover cases where only changed fields are sent).
  • Fixed product query: The FetchXML now correctly pulls all opportunityproduct records linked to the updated Opportunity, using their valid IDs.
  • Safe update logic: We use the actual opportunity product IDs to create valid update requests, setting new_valid_to to the opportunity's new close date.
  • Error handling: Added try/catch with tracing to help debug issues, and throws a meaningful exception if syncing fails.

Plugin Registration Requirements

To ensure the plugin works correctly:

  • Message: Register on the Update message of the opportunity entity.
  • Stage: Use the Post-operation stage (to work with the final updated Opportunity data).
  • Filtering Attributes: Set estimatedclosedate as the filtering attribute—this ensures the plugin only runs when this specific field changes, improving performance.
  • Post Image: Add a Post Image that includes the estimatedclosedate field (this acts as a fallback if the Target doesn't include the field, e.g., when other fields are updated alongside it).

内容的提问来源于stack exchange,提问作者Ahmed Mehany

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.11 08:00:19