CRM – Action Calls (USD)

Pessoal,

Seguindo a série de posts sobre o USD, irei escrever neste post sobre as Action Calls!

actioncalls_1

Para ver os posts anteriores, acesse os links (estão por ordem de publicação do mais recente para o mais antigo):

CRM – Toolbars e Buttons (USD)

CRM – Agent Scripts (USD)

CRM – Scriptlets (USD)

CRM – Hosted Controls (USD)

CRM – Componentes do USD

CRM Utilidades do USD (Unified Service Desk)

As Action Calls são a forma USD de criamos interações com todos os demais controles, como por exemplo uma ação visual de um hosted control (UII Action), um botão de menu (Toolbar Button), uma ação de navegação (Windows Navigation Rule).

Não devemos confundir as Action Calls com os Events (falaremos sobre Events no próximo post). As Actions representam chamadas de um controle para realizar funcionalidades “out of the box”, ou seja, algo que não seja implementado nativamente pelo USD.

Então, devemos lembrar, quando criamos Actions, estamos apenas assinando métodos, não estamos acionando! Elas são cérebro do USD, implementarão nossas regras de negócio!

Dificilmente será possível criar um exemplo/customização do USD sem o uso de uma Action Call, eu mesmo, em meus posts anteriores, sem ter detalhado o que realmente são as Action Calls fiz uso delas. Assim, neste post, tentarei fazer algo diferente!

1 – Abrir Página CRM através de um Custom Hosted Control

actioncalls_6 actioncalls_7

Crei um Custom Hosted Control com apenas alguns controles visuais, após iniciarmos um sessão de atendimento, o operador de call center pode selecionar a opção “Ocorrência” e clicar no botão. Automaticamente uma página do CRM é apresentada! Para fazer isso, criei um chamada de Action Call dentro do Hosted Control!

Primeiramente precisamos criar um Custom Hosted Control, abra o Visual Studio e criei um projeto do tipo “USD Custom Hosted Control”:

actioncalls_8

Após o projeto ter sido criado, confirme as referências das DLL’s, caso não sejam localizadas procure na pasta de instalação de seu USD ou no SDK do CRM:

actioncalls_9

Neste exemplo eu inseri alguns controles (textbox, radio e button), apenas para dar um ideia de que podemos criar um WPF novo herdando toda a referência de classes de um WPF para o USD!

actioncalls_10

Em relação ao C#, criei o evento no clique do botão e inseri a chamada para a Action Call:

actioncalls_11

Se notarmos bem, não aciono um Action Call propriamente dita, estou na verdade criando uma Action Call em tempo de execução e ela morrerá após ser executada. Nesse caso, não temos o conceito de reaproveitamento… Para solucionar isso, devemos criar um Event, ele agrupa “N” Action Calls e podemos fazer reuso, mas isso é assunto do próximo post! Por hora, ficamos com o conceito de chamar uma “Action” dentro de um Custom Hosted Control!

Bom, os códigos do Custom Hosted Control!

XAML:

<USD:DynamicsBaseHostedControl x:Class="CustomUSDHostedControl.USDControl" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:USD="clr-namespace:Microsoft.Crm.UnifiedServiceDesk.Dynamics;assembly=Microsoft.Crm.UnifiedServiceDesk.Dynamics" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <ToolBarTray Name="ProgrammableToolbarTray" Grid.Row="0" Focusable="false"/>  <!-- this is where any toolbar assigned to this control will go -->

        <Button Name="button" Click="button_Click" HorizontalAlignment="Left" Margin="89,131,0,0" Grid.Row="1" Content="Ok" VerticalAlignment="Top" Width="52" Height="22" />
        <TextBox x:Name="textBox" HorizontalAlignment="Left" Height="23" Margin="78,47,0,0" Grid.Row="1" TextWrapping="Wrap" Text="TextBox" VerticalAlignment="Top" Width="120"/>
        <RadioButton x:Name="radioButtonInfo" Content="Informação" GroupName="options" HorizontalAlignment="Left" Margin="78,94,0,0" Grid.Row="1" VerticalAlignment="Top"/>
        <RadioButton x:Name="radioButtonOcor" Content="Ocorrência" GroupName="options" HorizontalAlignment="Left" Margin="174,94,0,0" Grid.Row="1" VerticalAlignment="Top" />

    </Grid>
</USD:DynamicsBaseHostedControl>

CS:

// =====================================================================
//  This file is part of the Microsoft Dynamics CRM SDK code samples.
//
//  Copyright (C) Microsoft Corporation.  All rights reserved.
//
//  This source code is intended only as a supplement to Microsoft
//  Development Tools and/or on-line documentation.  See these other
//  materials for detailed information regarding Microsoft code samples.
//
//  THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
//  KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
//  IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
//  PARTICULAR PURPOSE.
// =====================================================================

using System;
using System.Globalization;
using System.Windows;
using Microsoft.Crm.UnifiedServiceDesk.CommonUtility;
using Microsoft.Crm.UnifiedServiceDesk.Dynamics;
using System.Collections.Generic;
using Microsoft.Crm.UnifiedServiceDesk.Dynamics.Utilities;

namespace CustomUSDHostedControl
{
    ///<summary>
    /// Interaction logic for USDControl.xaml
    /// This is a base control for building Unified Service Desk Aware add-ins
    /// See USD API documentation for full API Information available via this control.
    /// </summary>

    public partial class USDControl : DynamicsBaseHostedControl
    {
        #region Vars
        ///<summary>
        /// Log writer for USD 
        /// </summary>

        private TraceLogger LogWriter = null;

        #endregion

        ///<summary>
        /// UII Constructor 
        /// </summary>


        /// <param name="appID">ID of the application</param>
        /// <param name="appName">Name of the application</param>
        /// <param name="initString">Initializing XML for the application</param>
        public USDControl(Guid appID, string appName, string initString)
            : base(appID, appName, initString)
        {
            InitializeComponent();

            // This will create a log writer with the default provider for Unified Service desk
            LogWriter = new TraceLogger();

            #region Enhanced LogProvider Info
            // This will create a log writer with the same name as your hosted control. 
            // LogWriter = new TraceLogger(traceSourceName:"MyTraceSource");

            // If you utilize this feature,  you would need to add a section to the system.diagnostics settings area of the UnifiedServiceDesk.exe.config
            //<source name="MyTraceSource" switchName="MyTraceSwitchName" switchType="System.Diagnostics.SourceSwitch">
            //    <listeners>
            //        <add name="console" type="System.Diagnostics.DefaultTraceListener"/>
            //        <add name="fileListener"/>
            //        <add name="USDDebugListener" />
            //        <remove name="Default"/>
            //    </listeners>
            //</source>

            // and then in the switches area : 
            //<add name="MyTraceSwitchName" value="Verbose"/>

            #endregion

        }

        ///<summary>
        /// Raised when the Desktop Ready event is fired. 
        /// </summary>

        protected override void DesktopReady()
        {
            // this will populate any toolbars assigned to this control in config. 
            PopulateToolbars(ProgrammableToolbarTray);
            base.DesktopReady();
        }

        ///<summary>
        /// Raised when an action is sent to this control
        /// </summary>

        /// <param name="args">args for the action</param>
        protected override void DoAction(Microsoft.Uii.Csr.RequestActionEventArgs args)
        {
            // Log process.
            LogWriter.Log(string.Format(CultureInfo.CurrentCulture, "{0} -- DoAction called for action: {1}", this.ApplicationName, args.Action), System.Diagnostics.TraceEventType.Information);

            #region Example process action
            //// Process Actions. 
            //if (args.Action.Equals("your action name", StringComparison.OrdinalIgnoreCase))
            //{
            //    // Do some work

            //    // Access CRM and fetch a Record
            //    Microsoft.Xrm.Sdk.Messages.RetrieveRequest req = new Microsoft.Xrm.Sdk.Messages.RetrieveRequest(); 
            //    req.Target = new Microsoft.Xrm.Sdk.EntityReference( "account" , Guid.Parse("0EF05F4F-0D39-4219-A3F5-07A0A5E46FD5")); 
            //    req.ColumnSet = new Microsoft.Xrm.Sdk.Query.ColumnSet("accountid" , "name" );
            //    Microsoft.Xrm.Sdk.Messages.RetrieveResponse response = (Microsoft.Xrm.Sdk.Messages.RetrieveResponse)this._client.CrmInterface.ExecuteCrmOrganizationRequest(req, "Requesting Account"); 


            //    // Example of pulling some data out of the passed in data array
            //    List<KeyValuePair<string, string>> actionDataList = Utility.SplitLines(args.Data, CurrentContext, localSession);
            //    string valueIwant = Utility.GetAndRemoveParameter(actionDataList, "mykey"); // asume there is a myKey=<value> in the data. 



            //    // Example of pushing data to USD
            //    string global = Utility.GetAndRemoveParameter(actionDataList, "global"); // Assume there is a global=true/false in the data
            //    bool saveInGlobalSession = false;
            //    if (!String.IsNullOrEmpty(global))
            //        saveInGlobalSession = bool.Parse(global);

            //    Dictionary<string, CRMApplicationData> myDataToSet = new Dictionary<string, CRMApplicationData>();
            //    // add a string: 
            //    myDataToSet.Add("myNewKey", new CRMApplicationData() { name = "myNewKey", type = "string", value = "TEST" });

            //    // add a entity lookup:
            //    myDataToSet.Add("myNewKey", new CRMApplicationData() { name = "myAccount", type = "lookup", value = "account,0EF05F4F-0D39-4219-A3F5-07A0A5E46FD5,MyAccount" }); 

            //    if (saveInGlobalSession) 
            //    {
            //        // add context item to the global session
            //        ((DynamicsCustomerRecord)((AgentDesktopSession)localSessionManager.GlobalSession).Customer.DesktopCustomer).MergeReplacementParameter(this.ApplicationName, myDataToSet, true);
            //    }
            //    else
            //    {
            //        // Add context item to the current session. 
            //        ((DynamicsCustomerRecord)((AgentDesktopSession)localSessionManager.ActiveSession).Customer.DesktopCustomer).MergeReplacementParameter(this.ApplicationName, myDataToSet, true);
            //    }
            //}
            #endregion

            base.DoAction(args);
        }

        ///<summary>
        /// Raised when a context change occurs in USD
        /// </summary>

        /// <param name="context"></param>
        public override void NotifyContextChange(Microsoft.Uii.Csr.Context context)
        {
            base.NotifyContextChange(context);
        }

        #region User Code Area
        private void button_Click(object sender, RoutedEventArgs e)
        {
            // Verify if radio "Ocorrência" is Checked
            if (radioButtonOcor.IsChecked == true)
            {
                // Action
                var openIncident = new ActionDefinition
                {
                    Application = "Incident", // Hosted Control
                    Action = "New_CRM_Page", // Action
                    ActionData = "LogicalName=incident" // Data
                };

                // Execute Actions
                CRMWindowRouter.ExecuteActions(localSessionManager.ActiveSession,
                    new List<ActionDefinition> { openIncident }, string.Empty, new Dictionary<string, string>());

                textBox.Text = "Incident Opened!";
            }
            else
            {
                textBox.Text = "Button Fired!";
            }
        }
        #endregion
    }
}

Compile seu projeto e cole a DLL gerada na raiz da pasta de instalação de seu USD, geralmente fica em “C:\Program Files\Microsoft Dynamics CRM USD\USD”.

Por fim, precisamos criar nosso Custom Hosted Control no CRM. Abra o CRM, navegue em Configurações > Unified Service Desk > Hosted Control > Novo:

actioncalls_12

Precisamos informar os seguintes campos:

  • Name – Nome do hosted control
  • USD Component Type – USD Hosted Control
  • Application is Global – Marcar a caixa de seleção (neste caso, queremos que o hosted control seja inicializado junto com o USD)
  • Diplay Panel – MainPanel
  • Assembly URI – “CustomUSDHostedControl”, o nome de nossa DLL
  • Assembly Type – “CustomUSDHostedControl.USDControl”, o nome de nossa Classe

Pronto! Temos um Custom Hosted Control acionando uma “Action Call”, ou melhor, fazendo uma criação e chamada de Action Call por demanda!

2– Interceptar Action Call em um Custom Hosted Control

actioncalls_13

actioncalls_14

Através do Hosted Control A (SmartCustomUSDHostedControl) faço uma chamada que aciona o Hosted Control B (CustomUSDHostedControl) que recebe como parâmetro o valor do TextBox do Hosted Control A, atribui como valor do Textbox do Hosted Control B!

Precisaremos que o item 1 deste post tenha sido executado. Após isto, abra o “CustomHostedControl” e crie uma UII Action:

actioncalls_15actioncalls_16

A única informação que deve inserir é o nome da UII Action! No meu exemplo “ButtonClick”.

Agora vamos ao Visual Studio, abra o Custom Hosted Control “CustomUSDHostedControl”, precisamos fazer uma edição no método “DoAction”, é o responsável por orquestrar as chamadas (Actions) que são feitas ao Hosted Control. Iremos tratar a ação “ButtonClick”, consumir os dados de foram enviados na ação e atribui ao textbox:

actioncalls_17

Segue o código:

        ///<summary>
        /// Raised when an action is sent to this control
        /// </summary>

        /// <param name="args">args for the action</param>
        protected override void DoAction(Microsoft.Uii.Csr.RequestActionEventArgs args)
        {
            // Log process.
            LogWriter.Log(string.Format(CultureInfo.CurrentCulture, "{0} -- DoAction called for action: {1}", this.ApplicationName, args.Action), System.Diagnostics.TraceEventType.Information);

            #region Example process action
            //// Process Actions. 
            //if (args.Action.Equals("your action name", StringComparison.OrdinalIgnoreCase))
            //{
            //    // Do some work

            //    // Access CRM and fetch a Record
            //    Microsoft.Xrm.Sdk.Messages.RetrieveRequest req = new Microsoft.Xrm.Sdk.Messages.RetrieveRequest(); 
            //    req.Target = new Microsoft.Xrm.Sdk.EntityReference( "account" , Guid.Parse("0EF05F4F-0D39-4219-A3F5-07A0A5E46FD5")); 
            //    req.ColumnSet = new Microsoft.Xrm.Sdk.Query.ColumnSet("accountid" , "name" );
            //    Microsoft.Xrm.Sdk.Messages.RetrieveResponse response = (Microsoft.Xrm.Sdk.Messages.RetrieveResponse)this._client.CrmInterface.ExecuteCrmOrganizationRequest(req, "Requesting Account"); 


            //    // Example of pulling some data out of the passed in data array
            //    List<KeyValuePair<string, string>> actionDataList = Utility.SplitLines(args.Data, CurrentContext, localSession);
            //    string valueIwant = Utility.GetAndRemoveParameter(actionDataList, "mykey"); // asume there is a myKey=<value> in the data. 



            //    // Example of pushing data to USD
            //    string global = Utility.GetAndRemoveParameter(actionDataList, "global"); // Assume there is a global=true/false in the data
            //    bool saveInGlobalSession = false;
            //    if (!String.IsNullOrEmpty(global))
            //        saveInGlobalSession = bool.Parse(global);

            //    Dictionary<string, CRMApplicationData> myDataToSet = new Dictionary<string, CRMApplicationData>();
            //    // add a string: 
            //    myDataToSet.Add("myNewKey", new CRMApplicationData() { name = "myNewKey", type = "string", value = "TEST" });

            //    // add a entity lookup:
            //    myDataToSet.Add("myNewKey", new CRMApplicationData() { name = "myAccount", type = "lookup", value = "account,0EF05F4F-0D39-4219-A3F5-07A0A5E46FD5,MyAccount" }); 

            //    if (saveInGlobalSession) 
            //    {
            //        // add context item to the global session
            //        ((DynamicsCustomerRecord)((AgentDesktopSession)localSessionManager.GlobalSession).Customer.DesktopCustomer).MergeReplacementParameter(this.ApplicationName, myDataToSet, true);
            //    }
            //    else
            //    {
            //        // Add context item to the current session. 
            //        ((DynamicsCustomerRecord)((AgentDesktopSession)localSessionManager.ActiveSession).Customer.DesktopCustomer).MergeReplacementParameter(this.ApplicationName, myDataToSet, true);
            //    }
            //}
            #endregion

            if (args.Action.Equals("ButtonClick", StringComparison.OrdinalIgnoreCase))
            {
                // Get Data Value
                var dataValue = args.DataObject.ToString();

                // Set Data Value in TextBox
                textBox.Text = dataValue;
            }

            base.DoAction(args);
        }

Agora temos que criar o Custom Hosted Control que irá acionar a Action “ButtonClick”. Para isso precisamos criar um novo projeto do tipo “USD Custom Hosted Control”, chamei o meu de “SmartCustomUSDHostedControl”.

Inseri um textbox e um botão:

actioncalls_18

No .cs no clique do botão estou fazendo uma chamada para o controle “CustomHostedControl”:

actioncalls_19

Vamos aos códigos!

XAML:


<USD:DynamicsBaseHostedControl x:Class="SmartCustomUSDHostedControl.USDControl" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:USD="clr-namespace:Microsoft.Crm.UnifiedServiceDesk.Dynamics;assembly=Microsoft.Crm.UnifiedServiceDesk.Dynamics" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <ToolBarTray Name="ProgrammableToolbarTray" Grid.Row="0" Focusable="false"/>
        <Button x:Name="button" Content="Call &quot;CustomUSDHostedControl" HorizontalAlignment="Left" Margin="25,103,0,0" Grid.Row="1" VerticalAlignment="Top" Width="235" Click="button_Click"/>
        <TextBox x:Name="textBox" HorizontalAlignment="Left" Height="23" Margin="32,26,0,0" Grid.Row="1" TextWrapping="Wrap" Text="TextBox" VerticalAlignment="Top" Width="120"/>
        <!-- this is where any toolbar assigned to this control will go -->
        

    </Grid>
</USD:DynamicsBaseHostedControl>

CS:

private void button_Click(object sender, RoutedEventArgs e)
{
    // Action
    var callCustomHC = new ActionDefinition
    {
        Application = "CustomUSDHostedControl", // Hosted Control
        Action = "ButtonClick", // Action
        ActionData = textBox.Text // Data
    };

    // Execute Actions
    CRMWindowRouter.ExecuteActions(localSessionManager.ActiveSession,
        new List<ActionDefinition> { callCustomHC }, string.Empty, new Dictionary<string, string>());
}

Compile os dois projetos e adicione as DLL’s dentro da pasta do USD!

A última etapa é criar o Custom Hosted Control no CRM:

actioncalls_20

Informe os seguintes campos:

  • Name – Nome do hosted control
  • USD Component Type – USD Hosted Control
  • Application is Global – Marcar a caixa de seleção (neste caso, queremos que o hosted control seja inicializado junto com o USD)
  • Diplay Panel – MainPanel
  • Assembly URI – “SmartCustomUSDHostedControl”, o nome de nossa DLL
  • Assembly Type – “SmartCustomUSDHostedControl.USDControl”, o nome de nossa Classe

Bom é isso! Abra seu USD e inicie uma sessão com um cliente para verificar o funcionamento!

Outro post extenso, desculpem… Mas era preciso uma grande quantidade de detalhes para exemplificar um pouco mais o uso das Actions.

Para maiores detalhes, consulte os links:

Action calls

ExecuteAction Class

Create an action call for a UII action

[]’s,

Tiago Cardoso

Anúncios

Sobre Tiago Michelini Cardoso

I have been working with IT since 2006, almost of this time using Microsoft Dynamics CRM/365 as a source of solutions. I graduated in Bachelor of Information Systems at FIAP (Brazil) in 2012. I really love what I do! Technology has been my interest since always. Even in a tool different world of the current. When we didn't have internet, tablets, smartphones e social networks! Although I have worked in some roles, I can't give up "the developer life". Even so far of the greatest developers. Development in general is the thing that I love to work! I started my contributions about Dynamics in 2010. At the beginning, I used to help at MSDN and TechNet forums. But now, I'm dedicating all my time in my personal blog! Currently, I have the enormous honour of being the only Brazilian who got the award for Microsoft MVP (Most Valuable Professional) for Microsoft Dynamics CRM/365 product. I have been receiving the award since 2012.
Esse post foi publicado em Dynamics CRM e marcado , , , , , . Guardar link permanente.

4 respostas para CRM – Action Calls (USD)

  1. Amine disse:

    Hi Tiago,
    Your tutorial is very helpful. I spent too much time trying to find how to fire an action call from a custom hosted control. And you helped me. But in my case, I want to open a standard hosted control with this action call not as you did (you opened a custom hosted control). And it doesn’t work. I want just to open a web page in this hosted control using a “Navigate” action. It looks easy but it doesn’t work at all! Thank you in advance.

    Curtir

    • Hi Amine,

      Thank you so much to read my post! I really appreciated that!

      So, before you “navigate”, you have to “MoveToPanel”. That means: bring my hosted control to screen. Basically, you need something like that:


      // Action
      var move = new ActionDefinition
      {
      Application = "Bing", // Hosted Control
      Action = "MoveToPanel", // Action
      ActionData = "MainPanel" // Data
      };

      // Action
      var navigate = new ActionDefinition
      {
      Application = "Bing", // Hosted Control
      Action = "Navigate", // Action
      ActionData = "url=http://www.bing.com" // Data
      };

      // Execute Actions
      CRMWindowRouter.ExecuteActions(localSessionManager.ActiveSession,
      new List { move,navigate }, string.Empty, new Dictionary());

      Please try that and let me know if it works!

      Regards,
      Tiago

      Curtir

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s