Sunday, April 29, 2012

Extending CrmSvcUtil part 2

Last month, I wrote a blog on how to extend CrmSvcUtil. The blog was about filtering the classes generated by CrmSvcUtil. Here is the link to that blog. I have updated the code to generate the early bind classes for a specific solution. Here is the code..
//<snippetBasicFilteringService>

using System;
using Microsoft.Crm.Services.Utility;
using Microsoft.Xrm.Sdk.Metadata;
using System.Collections.Generic;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Query;
using Microsoft.Xrm.Sdk.Client;
using Microsoft.Xrm.Sdk.Discovery;
using Microsoft.Xrm.Client;
using System.Xml;
using System.Xml.Linq;
namespace CRMExtensions
{
    /// <summary>
    /// Sample extension for the CrmSvcUtil.exe tool that generates early-bound
    /// classes for custom entities.
    /// </summary>
    public sealed class BasicFilteringService : ICodeWriterFilterService
    {
        //To store the guids belongs to a specified 
        public List<Guid> li = new List<Guid>();
       
        //Put the uniquename of the solution here
        public string sSolution = "ActivityFeeds";

        public BasicFilteringService(ICodeWriterFilterService defaultService)
        {
            this.DefaultService = defaultService;

            // Call the function to generate the the guids for the solution
            getDefaultEntities();

        }

        private ICodeWriterFilterService DefaultService { get; set; }

        bool ICodeWriterFilterService.GenerateAttribute(AttributeMetadata attributeMetadata, IServiceProvider services)
        {
            return this.DefaultService.GenerateAttribute(attributeMetadata, services);
        }

        bool ICodeWriterFilterService.GenerateEntity(EntityMetadata entityMetadata, IServiceProvider services)
        {
            //if (!entityMetadata.IsCustomEntity.GetValueOrDefault()) { return false; }
            //if (entityMetadata.LogicalName!="account") { return false; }
            if (li.Contains(entityMetadata.MetadataId.Value)==false) { return false; }
            
            return this.DefaultService.GenerateEntity(entityMetadata, services);
        }

        bool ICodeWriterFilterService.GenerateOption(OptionMetadata optionMetadata, IServiceProvider services)
        {
            return this.DefaultService.GenerateOption(optionMetadata, services);
        }

        bool ICodeWriterFilterService.GenerateOptionSet(OptionSetMetadataBase optionSetMetadata, IServiceProvider services)
        {
            return this.DefaultService.GenerateOptionSet(optionSetMetadata, services);
        }

        bool ICodeWriterFilterService.GenerateRelationship(RelationshipMetadataBase relationshipMetadata, EntityMetadata otherEntityMetadata,
        IServiceProvider services)
        {
            return this.DefaultService.GenerateRelationship(relationshipMetadata, otherEntityMetadata, services);
        }

        bool ICodeWriterFilterService.GenerateServiceContext(IServiceProvider services)
        {
            return this.DefaultService.GenerateServiceContext(services);
        }

        // This function will add guids of solution entities
        public void getDefaultEntities()
        {
            //you can put this in the config file
            //change the connection details with your crm details
            var connection = CrmConnection.Parse("Url=https://org.crm5.dynamics.com; Username=user@live.com; Password=password; DeviceID=deviceid; DevicePassword=password");
            var context = new CrmOrganizationServiceContext(connection);
                         
            QueryExpression query = new QueryExpression()
            {
                Distinct = false,
                EntityName = "solutioncomponent",
                ColumnSet = new ColumnSet(true),
                LinkEntities = 
            {
                new LinkEntity 
                {
                    JoinOperator = JoinOperator.Inner,
                    LinkFromAttributeName = "solutionid",
                    LinkFromEntityName = "solutioncomponent",
                    LinkToAttributeName = "solutionid",
                    LinkToEntityName = "solution",
                    LinkCriteria = 
                    {
                        Conditions = 
                        {
                            new ConditionExpression("uniquename", ConditionOperator.Equal, sSolution)
                        }
                    }
                }
            },
                Criteria =
                {
                    Filters = 
                {
                    new FilterExpression
                    {
                        FilterOperator = LogicalOperator.And,
                        Conditions = 
                        {
                            new ConditionExpression("componenttype", ConditionOperator.Equal, 1)

                        },
                    }
                }
                }
            };

            EntityCollection ec = context.RetrieveMultiple(query);

            foreach (Entity entity in ec.Entities)
            {
                Guid id = (Guid)entity.Attributes["objectid"];
                li.Add(id);

            }
        }//end function
    }
    //</snippetBasicFilteringService>
}

Please provide some feedback.

Friday, April 27, 2012

How to update the parent record when children records are created or updated

Few months ago, I wrote a blog on “How to update the children records when parent record is updated”. Here is link to that blog. In this blog I am posting a code on “How to update the parent record when child record is created or updated”. The scenario is that I want to display the sum of total of "Estimated Revenue” of the opportunities on the parent account record. Here are the steps
  • Create a new attribute named “new_oppamount” on the account entity and place it on the account entity form.
  • Publish the account entity
  • Create the plugin
  • Register this plugin on postcreate, postupdate , postsetstate and postsetstatedynamic  events of opportunity entity.
Here is the code. The code is using the fetch xml to get the sum of “Estimated Revenue” field of the open opportunities. Follow the steps mentioned in here and replace the ExecutePostAccountUpdateContacts method with follwing method
protected void ExecutePostOpportunityCreate(LocalPluginContext localContext)
{
    if (localContext == null)
    {
        throw new ArgumentNullException("localContext");
    }
            

    IPluginExecutionContext context = localContext.PluginExecutionContext;

    //Get a IOrganizationService
    IOrganizationService service = localContext.OrganizationService;

    //create a service context
    var ServiceContext = new OrganizationServiceContext(service);
    //ITracingService tracingService = localContext.TracingService;

    // The InputParameters collection contains all the data passed in the message request.
    if (context.InputParameters.Contains("Target") &&
    context.InputParameters["Target"] is Entity)
    {
        // Obtain the target entity from the input parmameters.
        Entity entity = (Entity)context.InputParameters["Target"];

        //get the customerid
        EntityReference a = (EntityReference)entity.Attributes["customerid"];
                
        decimal totalAmount=0;
                
        try
        {   
            //fetchxml to get the sum total of estimatedvalue
            string estimatedvalue_sum = string.Format(@" 
            <fetch distinct='false' mapping='logical' aggregate='true'> 
                <entity name='opportunity'> 
                    <attribute name='estimatedvalue' alias='estimatedvalue_sum' aggregate='sum' /> 
                    <filter type='and'>
                        <condition attribute='statecode' operator='eq' value='Open' />
                            <condition attribute='customerid' operator='eq' value='{0}' uiname='' uitype='' />
                    </filter>
                </entity>
            </fetch>", a.Id);
            EntityCollection estimatedvalue_sum_result = service.RetrieveMultiple(new FetchExpression(estimatedvalue_sum));

            foreach (var c in estimatedvalue_sum_result.Entities)
            {
                totalAmount = ((Money)((AliasedValue)c["estimatedvalue_sum"]).Value).Value;
            }
                    
            //updating the field on the account
            Entity acc = new Entity("account");
            acc.Id = a.Id;
            acc.Attributes.Add("new_oppamount", new Money(totalAmount));
            service.Update(acc);
                    
                    
        }
        catch (FaultException ex)
        {
                throw new InvalidPluginExecutionException("An error occurred in the plug-in.", ex);
        }
    }

}
Good Luck…

Sunday, April 15, 2012

CRM 2011 Postcode Lookup Solution Version 2.0

This is an extension of a blog I wrote back in June 2011. Here is the link to that blog. There were a few cons to that solution. One of the biggest cons was that the solution was hard coded to fill, only the built-in adress1_city, address1_stateorprovince, address1_country fields. I have updated the code to fill the city/suburb, state and country values to any field including custom fields. These fields have to be text fields.
The updated solution will work with the existing built-in entities as well as the custom entities.Here is the link to download the solution.
The solution consists of 4 files:
  • new_Json2.js (Javascript webresource)
  • new_PostCodeScript(Javascript webresource)
  • new_postcode(custom entity to store postcode information)
  • new_SuburbOptions (HTML webresource)

How does it work:

  • User enters the postcode on postal code field and on onchange event of the field. The solution will retrieve postcode records related to entered postcode.
  • If there is only one record related to entered postcode, the solution will populate the state, suburb and country fields for you.
  • If there are more than one records related to entered postcode, the solution will prompt you to select the appropriate entry.
In the following screen shot, I entered 2000 in postal code field and system prompt to pick the relevant city/suburb.
image

Installation Directions:

  1. Install the postcode solution.
  2. Open the CRM entity form in customization mode.
  3. Double click on attribute that represents the postalcode attribute.
  4. Add new_Json2.js and new_PostCodeScript to form libraries.
  5. Call loadPostCodeRequest function from new_PostCodeScript. Check the  “Pass execution context as first parameter” and pass the 3 attributes that represent the city, state and country attributes in “Comma separated list of parameters that will be passed to the function” as shown in the following screen shot.event
  6. Save the changes and publish the entity.
  7. Test the solution.