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

Dynamics CRM 2011中跨实体子网格复制特殊服务数据技术问询

Alright, let's work through how to sync those Special Services records from an Opportunity to its related Quotes and Orders in Dynamics CRM 2011. This is a common scenario, and there are two solid approaches depending on whether you need server-side enforceability or just client-side form-based sync.

This server-side method ensures Special Services get synced no matter how the Quote/Order is created—whether via the form, API, or bulk import. It’s the most reliable way to keep data aligned across entities.

Step-by-Step Setup

  • First, Create Required Relationships
    You’ll need to establish 1:N relationships between:

    • Quote (primary) and Special Services (related)
    • Order (primary) and Special Services (related)
      Make sure to set up clear navigation properties so the subgrids on Quote/Order forms can display the synced records.
  • Write the Plugin Logic
    The plugin will trigger when a Quote/Order is created or when its linked Opportunity is updated. Here’s a simplified C# example tailored to CRM 2011:

    using Microsoft.Xrm.Sdk;
    using System.Linq;
    
    public class SyncSpecialServices : IPlugin
    {
        public void Execute(IServiceProvider serviceProvider)
        {
            var context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
            var serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
            var service = serviceFactory.CreateOrganizationService(context.UserId);
    
            // Target is the Quote or Order being created/updated
            if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity target)
            {
                EntityReference oppRef = null;
                string targetEntity = target.LogicalName;
    
                // Grab the linked Opportunity ID from the target record
                if (targetEntity == "quote")
                {
                    oppRef = target.GetAttributeValue<EntityReference>("opportunityid");
                }
                else if (targetEntity == "salesorder") // CRM 2011 uses "salesorder" as Order's logical name
                {
                    oppRef = target.GetAttributeValue<EntityReference>("opportunityid");
                }
    
                if (oppRef != null)
                {
                    // Fetch all Special Services tied to the Opportunity
                    var ssQuery = new QueryExpression("new_services") // Replace with your actual Special Services logical name
                    {
                        ColumnSet = new ColumnSet(true), // Adjust to only fetch needed columns for better performance
                        Criteria = new FilterExpression
                        {
                            Conditions = { new ConditionExpression("opportunityid", ConditionOperator.Equal, oppRef.Id) }
                        }
                    };
    
                    var specialServices = service.RetrieveMultiple(ssQuery).Entities;
    
                    foreach (var ss in specialServices)
                    {
                        // Create a new Special Service linked to the Quote/Order
                        var newSS = new Entity("new_services");
                        // Copy all fields except the original Opportunity reference
                        foreach (var attr in ss.Attributes)
                        {
                            if (attr.Key != "opportunityid")
                            {
                                newSS[attr.Key] = attr.Value;
                            }
                        }
                        // Link to the current Quote/Order
                        if (targetEntity == "quote")
                        {
                            newSS["quoteid"] = new EntityReference("quote", target.Id);
                        }
                        else if (targetEntity == "salesorder")
                        {
                            newSS["salesorderid"] = new EntityReference("salesorder", target.Id);
                        }
    
                        service.Create(newSS);
                    }
                }
            }
        }
    }
    
  • Register the Plugin
    Use the CRM 2011 Plugin Registration Tool to register this plugin:

    • For Quotes: Register on the Create and Update messages (filter the Update message to only trigger when the opportunityid field changes)
    • For Orders: Repeat the same registration steps
      Set the execution stage to Post-Operation to ensure the Quote/Order is already linked to an Opportunity before syncing.

2. JavaScript-Based Solution (Form-Only Sync)

If you only need to sync Special Services when users create/edit Quotes/Orders via the CRM form, this client-side approach works well. It’s simpler to set up but won’t catch syncs from non-form actions.

Step-by-Step Setup

  • Add Subgrids to Quote/Order Forms
    Make sure your Quote and Order forms have a subgrid configured to display Special Services records linked to the current record.

  • Write the Sync Script
    This script will pull Special Services from the linked Opportunity and create copies tied to the current Quote/Order. Add it as a web resource to your CRM:

    function syncSpecialServicesToRecord() {
        var oppLookup = Xrm.Page.getAttribute("opportunityid").getValue();
        if (!oppLookup) return;
    
        var oppId = oppLookup[0].id.replace("{", "").replace("}", "");
        var currentRecordId = Xrm.Page.data.entity.getId().replace("{", "").replace("}", "");
        var currentEntity = Xrm.Page.data.entity.getEntityName();
    
        // Fetch Special Services from Opportunity via OData
        var req = new XMLHttpRequest();
        req.open("GET", Xrm.Page.context.getServerUrl() + "/XRMServices/2011/OrganizationData.svc/new_servicesSet?$filter=opportunityid/Id eq guid'" + oppId + "'", true);
        req.setRequestHeader("Accept", "application/json");
        req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
    
        req.onreadystatechange = function () {
            if (this.readyState === 4) {
                req.onreadystatechange = null;
                if (this.status === 200) {
                    var results = JSON.parse(this.responseText).d.results;
                    for (var i = 0; i < results.length; i++) {
                        var ss = results[i];
                        // Build new Special Service record
                        var newSS = {
                            "new_name": ss.new_name, // Replace with your actual field names
                            "new_description": ss.new_description,
                            "__metadata": { "type": "Microsoft.Crm.Sdk.Data.Services.new_services" }
                        };
                        // Link to current Quote/Order
                        if (currentEntity === "quote") {
                            newSS.quoteid = {
                                "__metadata": { "type": "Microsoft.Crm.Sdk.Data.Services.EntityReference" },
                                "Id": currentRecordId,
                                "LogicalName": "quote"
                            };
                        } else if (currentEntity === "salesorder") {
                            newSS.salesorderid = {
                                "__metadata": { "type": "Microsoft.Crm.Sdk.Data.Services.EntityReference" },
                                "Id": currentRecordId,
                                "LogicalName": "salesorder"
                            };
                        }
    
                        // Create the new record via OData
                        var createReq = new XMLHttpRequest();
                        createReq.open("POST", Xrm.Page.context.getServerUrl() + "/XRMServices/2011/OrganizationData.svc/new_servicesSet", true);
                        createReq.setRequestHeader("Accept", "application/json");
                        createReq.setRequestHeader("Content-Type", "application/json; charset=utf-8");
                        createReq.send(JSON.stringify(newSS));
                    }
                    // Refresh the subgrid to show new records
                    Xrm.Page.getControl("subgrid_services").refresh();
                } else {
                    alert("Error syncing Special Services: " + this.statusText);
                }
            }
        };
        req.send();
    }
    
  • Attach the Script to Form Events

    • For Quotes: Bind the syncSpecialServicesToRecord function to the opportunityid field’s OnChange event and the form’s OnLoad event (if Quotes are created directly from Opportunities)
    • For Orders: Repeat the same event bindings

Key Things to Keep in Mind

  • Logical Names Matter: Replace all placeholder logical names (like new_services) with the actual names from your CRM system. You can find these in the entity customization settings.
  • Avoid Duplicates: Add logic to check if a Special Service already exists for the Quote/Order before creating a new one (e.g., compare a unique identifier field or match on name + description).
  • Performance: If you have hundreds of Special Services per Opportunity, optimize queries to fetch only necessary fields and consider batch processing in plugins to avoid timeouts.
  • Security: Ensure the plugin’s execution user (or the form user for JavaScript) has create permissions for Special Services records.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 07:00:08