Link to home
Create AccountLog in
Avatar of apollo7
apollo7Flag for United States of America

asked on

CRM 2011 Opportunity Bug

I am working on a CRM 2011 environment that has been highly customized by different developers and I am trying to troubleshoot errors that happen sporadically.  The error that I am working on now happens when I try to change the owner on an Opportunity.  

It doesn't happen all the time but when it does the Opportunity form "freezes" after the Owner is changed (not on Save, the Save button becomes disabled as soon as the owner is changed)

After a few minutes, a Business Process error pops up,  If I download the Error Log it seems to indicate a plug-in problem, however, there are only two plug-ins Apollo.Crm.Plugin and Apollo.Crm.Workflow.

Can someone give an idea where to start with this?

Thanks

 User generated image
If I click Download Error Log, I get the following:

Unhandled Exception: System.ServiceModel.FaultException`1
[[Microsoft.Xrm.Sdk.OrganizationServiceFault, Microsoft.Xrm.Sdk, Version=5.0.0.0, 
Culture=neutral, PublicKeyToken=31bf3856ad364e35]]: Method not found: 'Boolean 
Apollo.Crm.DataLayer.SalesOrder.get_IsAutomatedNetOff()'.Detail: 
<OrganizationServiceFault xmlns:i="http://www.w3.org/2001/XMLSchema-instance" 
xmlns="http://schemas.microsoft.com/xrm/2011/Contracts">
  <ErrorCode>-2147220891</ErrorCode>
  <ErrorDetails 
xmlns:d2p1="http://schemas.datacontract.org/2004/07/System.Collections.Generic">
    <KeyValuePairOfstringanyType>
      <d2p1:key>OperationStatus</d2p1:key>
      <d2p1:value xmlns:d4p1="http://www.w3.org/2001/XMLSchema" 
i:type="d4p1:string">0</d2p1:value>
    </KeyValuePairOfstringanyType>
  </ErrorDetails>
  <Message>Method not found: 'Boolean Apollo.Crm.DataLayer.SalesOrder.get_IsAutomatedNetOff
()'.</Message>
  <Timestamp>2016-02-08T20:03:23.7576248Z</Timestamp>
  <InnerFault i:nil="true" />
  <TraceText>
[Apollo.Crm.Plugin: Apollo.Crm.Plugin.Security.GranularSecurity]
[406435b9-ca5c-e311-ad79-463500000031: Apollo.Crm.Plugin.Security.GranularSecurity: Assign of opportunity]
[Apollo.Crm.Plugin: Apollo.Crm.Plugin.OrderLineAllocation]
[97b86809-4016-e311-a85d-463500000031: Apollo.Crm.Plugin.OrderLineAllocation: Update of salesorder]
</TraceText>
</OrganizationServiceFault>

Open in new window

Avatar of Feridun Kadir
Feridun Kadir
Flag of United Kingdom of Great Britain and Northern Ireland image

It does sound like something is happening on change of owner. If you open the default solution and navigate to Plug-in Assemblies do you see anything listed?

The plug-in registration tool described here, https://msdn.microsoft.com/en-us/library/hh237515(v=crm.6).aspx might give you more information.
Avatar of apollo7

ASKER

Yes, there are the two plugins, as well as Workflows and Sdk Message Processing Steps,
so there is plugin Apollo.Crm.Plugin and also Apollo.Crm.Plug.Approve.Func under Sdk Message Processing Steps
I suspect one of these plugins is responsible for the problem, are you able to discuss this with any of the customizers of your system?  

Although, I'm not especially familiar with coding for CRM the error message suggests that one of the plug-ins that is run when the opportunity owner is changed is referring to some code that is not present. Perhaps some other customizations have been removed that contained the relevant code.

I think this is going to be difficult to unpick without you discovering what was done to your system.
Hi,

As I see the message,
Method not found: 'Boolean Apollo.Crm.DataLayer.SalesOrder.get_IsAutomatedNetOff()'

I believe you are missing something and the code should be written this way:

Boolean automated = Apollo.Crm.DataLayer.SalesOrder.get_IsAutomatedNetOff();
or
bool automated = Apollo.Crm.DataLayer.SalesOrder.get_IsAutomatedNetOff();

This is a wild guess as get_IsAutomatedNetOff() method might return a true/false value.
Avatar of apollo7

ASKER

So, would my next move be to connect to the plug-in using the PlugInRegistrationTool and look for methods that are disabled or otherwise not working?  Or is it best to open the plug-in in Visual Studio, attach to the process and run debug while changing the owner of an opportunity?

Thanks
Hi,

If you can post the plugin code here, I can suggest you what should be your next move. However, I strongly feel some keywords are missing from the code and plugin is deployed.

You can open plugin code to the VS and change the code with the above mentioned code and redeploy the plugin.
I think you need to fully document your system and get an understanding of what plugins you have in your CRM system and then try and establish what they are for.  Then you can consider whether they are still needed and if not remove them.
Avatar of apollo7

ASKER

Thanks feridun and rikin, I am working on getting the code in this thread to have your help understanding and also correcting my immediate problem.  It is one of many plugins with the source in TFS.

I will get something up here today.

Thanks
Avatar of apollo7

ASKER

Hi

I attached code from the plug-in that if referenced in the error posted earlier.  Let me know if this helps clarify what is happening when I try to change an owner in an Opportunity and the entire screen freeze and business process error log is displayed.  The error log specifically shows '[Apollo.Crm.Plugin: Apollo.Crm.Plugin.Security.GranularSecurity]'

Thanks

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using app.Crm;
using apollo.Crm.Security;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Query;
using Microsoft.Crm.Sdk.Messages;

namespace apollo.Crm.Plugin.Security
{
    /// <summary>
    /// Applies the default share state to an entity record. Relies on the following configuration settings:
    /// - DefaultShare - Global, BusinessUnitHierarchy, BusinessUnit,
    /// <settings>
    ///     <setting name="DefaultShare">
    ///         <value>BusinessUnitHierarchy</value>
    ///     </setting>
    /// </settings>
    /// This plugin should be registered with a PostImage called Target.
    /// </summary>
    public class GranularSecurity
        : IPlugin
    {
        #region ---------------------------------------- Constructor ------------------------------------------------
        public GranularSecurity(string config, string secureConfig)
        {
            this.Configuration = new app.Crm.Configuration.Plugin(config);
            this.SecureConfiguration = new app.Crm.Configuration.Plugin(secureConfig);
        }
        #endregion
        #region ---------------------------------------- Properties -------------------------------------------------
        public app.Crm.Configuration.Plugin Configuration
        { get; set; }
        public app.Crm.Configuration.Plugin SecureConfiguration
        { get; set; }
        #endregion
        #region ---------------------------------------- Methods ----------------------------------------------------
        public void Execute(IServiceProvider serviceProvider)
        {
            try
            {
                // Obtain the execution context from the service provider.
                IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
                // Get a reference to the Organization service.
                IOrganizationServiceFactory factory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
                IOrganizationService service = factory.CreateOrganizationService(context.UserId);
                EntityReference entity = new EntityReference(context.PrimaryEntityName, context.PrimaryEntityId);
                if ((context.MessageName.Equals("Create") || context.MessageName.Equals("Update") || context.MessageName.Equals("Assign")) && !context.SharedVariables.Contains("GranularSecurity"))
                {
                    app.Crm.Configuration.AppSettings.GetInstance().Context.ApplyNew();
                    // Used to determine if the plugin has been called within the current execution context
                    context.SharedVariables.Add("GranularSecurity", true);
                    Common.LogTrace(string.Format("Executing GranularSecurity plugin for {2} of {0} entity id {1}.", context.PrimaryEntityName, context.PrimaryEntityId, context.MessageName));
                    // Get Input Parameters
                    Entity input = null;
                    if (context.MessageName != "Assign") input = (Entity)context.InputParameters["Target"];
                    // Get Post Image
                    Entity postImage = (Entity)context.PostEntityImages["Target"];
                    // Get Pre Image if available
                    Entity preImage = null;
                    if (context.MessageName.Equals("Assign") || context.MessageName.Equals("Update")) preImage = (Entity)context.PreEntityImages["Target"];
                    if (context.PrimaryEntityName == "opportunity")
                    {
                        applyGranularSecurity(service, entity, input, preImage, postImage, context.MessageName);
                    }
                    else //if ((context.MessageName.Equals("Create") || context.MessageName.Equals("Assign")) && (context.PrimaryEntityName == "quote" || context.PrimaryEntityName == "salesorder"))
                    {
                        applyParentalSecurity(service, entity, postImage);
                    }
                }
            }
            catch (Exception ex)
            {
                Common.LogException(ex);
                throw;
            }
        }

        /// <summary>
        /// Applies granular security to a record based on secure record attributes and plugin configuration.
        /// See opportunity for an example.
        /// </summary>
        /// <param name="service"></param>
        /// <param name="entity"></param>
        /// <param name="input"></param>
        /// <param name="preImage"></param>
        /// <param name="postImage"></param>
        private void applyGranularSecurity(IOrganizationService service, EntityReference entity, Entity input, Entity preImage, Entity postImage, string messageName)
        {
            // Get the record owner
            DataLayer.SystemUser owner = new DataLayer.SystemUser(service, postImage.GetAttributeValue<EntityReference>("ownerid").Id);
            // Has the record security status changed?
            // NB. Even if the attribute has not changed it is being included in the input attributes 
            // --> pre image hack is to work around this.
            if (secureStatusHasChanged(input, preImage))
            {
                // Apply Security Settings
                if (input.GetAttributeValue<bool>("app_securityfirewalled") != true)
                {
                    SecurityUtility.ApplyCascadeDefaultShares(service, Configuration, entity, owner);
                }
                else
                {
                    SecurityUtility.RemoveCascadeAllShares(service, entity, owner);
                    // Now ensure that the record is still visible to management
                    if (postImage.GetAttributeValue<bool>("app_securityvisibletomanagement") == true)
                    {
                        SecurityUtility.ApplyCascadeManagementShare(service, entity, owner);
                    }
                }
            }
            else if (visibleToManagementStatusHasChanged(input, preImage))
            {
                // The visible to managment status has changed but only apply management 
                // shares though if the record is secure.
                if (postImage.GetAttributeValue<bool>("app_securityfirewalled") == true)
                {
                    if (input.GetAttributeValue<bool>("app_securityvisibletomanagement") == true)
                    {
                        SecurityUtility.ApplyCascadeManagementShare(service, entity, owner);
                    }
                    else
                    {
                        SecurityUtility.RemoveCascadeManagementShare(service, entity, owner);
                    }
                }
            }
            else if (messageName.Equals("Assign"))
            {
                // cascade re-assign to quotes, orders
                SecurityUtility.CascadeAssign(service, entity, owner);
                // remove all current shares
                SecurityUtility.RemoveCascadeAllShares(service, entity, owner);
                // apply shares
                if (postImage.GetAttributeValue<bool>("app_securityfirewalled") == true)
                {
                    if (postImage.GetAttributeValue<bool>("app_securityfirewalled") == true)
                    {
                        // apply management share if required
                        SecurityUtility.ApplyCascadeManagementShare(service, entity, owner);
                    }
                }
                else
                {
                    // apply default shares
                    SecurityUtility.ApplyCascadeDefaultShares(service, Configuration, entity, owner);
                }
            }
        }
        private void applyParentalSecurity(IOrganizationService service, EntityReference entity, Entity postImage)
        {
            if (postImage.GetAttributeValue<EntityReference>("opportunityid") != null)
            {
                // Cascade parental shares where the record has an opportunity id
                DataLayer.Opportunity opportunity = new DataLayer.Opportunity(service, postImage.GetAttributeValue<EntityReference>("opportunityid").Id);
                if (!opportunity.IsSecureRecord)
                {
                    SecurityUtility.ApplyDefaultShares(service, Configuration, entity, opportunity.Owner);
                }
                else if (opportunity.IsSecureRecord && opportunity.IsVisibleToManagement)
                {
                    SecurityUtility.ApplyManagementShare(service, entity, opportunity.Owner);
                }
            }
            else
            {
                // If the record is owned by a user -> Apply Default Shares
                if (postImage.GetAttributeValue<int>("owneridtype") == 8)
                {
                    DataLayer.SystemUser owner = new DataLayer.SystemUser(service, postImage.GetAttributeValue<Guid>("ownerid"));
                    SecurityUtility.ApplyDefaultShares(service, Configuration, entity, owner);
                }
            }
        }
        /// <summary>
        /// Compares the input parameters to the pre image to determine if the value has changed.
        /// This should not be necessary but is required as Opportunity is including parameters 
        /// that have not changed in the inputparameters. 
        /// <param name="opportunityInput"></param>
        /// <param name="opportunityPreImage"></param>
        /// <returns></returns>
        private bool secureStatusHasChanged(Entity opportunityInput, Entity opportunityPreImage)
        {
            bool retVal = false;
            if (opportunityInput != null)
            {
                if (opportunityInput.Attributes.Contains("app_securityfirewalled"))
                {
                    if (opportunityPreImage == null)
                    {
                        retVal = true;
                    }
                    else
                    {
                        if (opportunityPreImage.GetAttributeValue<bool>("app_securityfirewalled") != opportunityInput.GetAttributeValue<bool>("app_securityfirewalled"))
                        {
                            retVal = true;
                        }
                    }
                }
            }
            return retVal;
        }
        /// <summary>
        /// Compares the input parameters to the pre image to determine if the value has changed.
        /// This should not be necessary but is required as Opportunity is including parameters 
        /// that have not changed in the inputparameters. #DodgyJavascript
        /// </summary>
        /// <param name="opportunityInput"></param>
        /// <param name="opportunityPreImage"></param>
        /// <returns></returns>
        private bool visibleToManagementStatusHasChanged(Entity opportunityInput, Entity opportunityPreImage)
        {
            bool retVal = false;
            if (opportunityInput != null)
            {
                if (opportunityInput.Attributes.Contains("app_securityvisibletomanagement"))
                {
                    if (opportunityPreImage == null)
                    {
                        retVal = true;
                    }
                    else
                    {
                        if (opportunityPreImage.GetAttributeValue<bool>("app_securityvisibletomanagement") != opportunityInput.GetAttributeValue<bool>("app_securityvisibletomanagement"))
                        {
                            retVal = true;
                        }
                    }
                }
            }
            return retVal;
        }
        #endregion
    }
}

Open in new window

Avatar of apollo7

ASKER

Any ideas on my next move?  Let me know if you need more detail.  I am looking at workflows that appear to kicked off by the plug-in. When the error does not occur, the workflow(s) succeed.  
When I get the error, the workflows go into a waiting stage.
Sorry, I can't help here, coding is not part of my skillset. :(
Hi,

The above class is just calling the method from other class. Do you have the code for "Apollo.Crm.DataLayer.SalesOrder" class? The error says a method in the class is missing. Can you check it?

Since you have code, are you able to debug the plugin?
Avatar of apollo7

ASKER

I ran a debug on the plugin and received 1 error:

Error      6      'Apollo.Crm.DataLayer.SalesOrder' does not contain a definition for 'IsAutomatedNetOff' and no extension method 'IsAutomatedNetOff' accepting a first argument of type 'Apollo.Crm.DataLayer.SalesOrder' could be found (are you missing a using directive or an assembly reference?)      E:\TFS_DT\Apollo\TrunkWiPro\Apollo.Crm.Plugin\Apollo.Crm.Plugin\OrderLineAllocation.cs      75      39      Apollo.Crm.Plugin
ASKER CERTIFIED SOLUTION
Avatar of Rikin Shah
Rikin Shah
Flag of India image

Link to home
membership
Create a free account to see this answer
Signing up is free and takes 30 seconds. No credit card required.
See answer
Avatar of apollo7

ASKER

Rikin

Yes, it is an assembly deployed to the GAC but I am just using "View" when I open the the plug-in in TFS.  

I am new to this project and just got the permission today to check-in/check-out so it is very possible that I am not seeing the most recent version (for one thing, there are pending changes)
Avatar of apollo7

ASKER

So would the next step to "get the latest version" and deploy to the GAC?
You need to figure it out first, is the code for the assembly available in the same project? Or is it an assembly added as a reference?

Check in GAC if the assembly is deployed over there or not.

And yes, get the latest version of the code/assembly or both.
Avatar of apollo7

ASKER

This is the correct answer, we were looking at the wrong source code.