Limpar contactos ou contas de atividade marketing falhadas

by Pedro Azevedo 11. December 2014 11:10

Boas pessoal,

Fui deparado com um problema que apareceu no fórum do Dynamics CRM em português. O objetivo do desafio passava por ter uma maneira mais expedita para eliminar\desativar contas ou contactos que tenham dado erro aquando de uma distribuição de uma campanha.

O primeiro desafio foi obter estes erros. Na interface gráfica os erros aparecem no seletor “Falhas”. Então a minha primeira tentativa foi através da interface REST e sobre a entidade CampaignActivity (Atividades de Campanha) mas sem sucesso porque aqui não aparecem os erros.

 

Depois de ver melhor a imagem reparei que a vista se chama “Vista associada de falhas de operações em massa”. Logo fui estudar a entidade BulkOperation mas aqui mais uma vez só encontrei a atividade chamada “Distribuição de Email” e não as atividades geradas. Depois de ver todas as relações da entidade, cheguei a entidade BulkOperationLog esta sim tem todas as atividades que foram geradas, as que correram bem e as que deram erro. Como não existe muita informação sobre esta entidade, executei uma chamada para ver o esquema desta entidade:

Esta entidade tem um campo o ErrorNumber que se tiver valor diferente de 0 é porque esta atividade deu erro. Infelizmente só tem mesmo o número do erro e não tem qualquer descrição. Bem este foi outro desafio. Eu já tinha escrito um post sobre erros e explicado como chegar a descrição. Para já vai ficar com o número do erro mas é meu objetivo de criar algo para me dar também a descrição do erro.

Tendo então descoberto qual a entidade que tinha a informação que necessitava e para além do erro ainda possuí qual o cliente\contacto através do campo RegardingObjectId. Podem ver a pré-visualização do resultado final:

Para listar esta informação utilizei um plugin jQuery chamado jqGrid e usei esta versão, já que a versão que tinha utilizado até aqui passou a ter uma licença paga. Para aqui também já evoluí este componente do jqGrid para facilitar listar entidades do CRM, mais tarde vou escrever um post sobre este componente.

Falando mais concretamente da implementação a primeira coisa foi colocar um botão ao nível da atividade de campanha. Botão este que irá chamar um recurso web html, este botão foi criado mais uma vez com recurso a ferramenta Ribbon Workbench:

Este botão vai chamar uma função Javascript onde é passado qual a campanha que estamos a falar neste momento e com isso mostrar todas as atividades que falharam:

function openWebResource(campaignId) {
    var customParameters = encodeURIComponent("?campaignId=" + campaignId);
    Xrm.Utility.openWebResource("new_failedcampaign/failedcampaign.html", customParameters, 300,300);
}

Este recurso web tem o plugin jqGrid e a sua inicialização, o método fetchGridData vai ser responsável por ir obter as atividades com falha e preencher a tabela.

$("#jqGrid").jqGrid({
        datatype: "local",
        height: '100%',
        colModel: [ {label: 'Id Registo', name: 'ObjectId', hidden: true},
                    {label: 'Tipo Registo', name: 'TypeRegister', width: 90},
                    {label: 'Nome do Registo', name: 'NomeRegisto', width: 200},
                    {label: 'Erro', name: 'ErrorNumber', width: 140 }
        ],
        viewrecords: true, // show the current page, data rang and total records on the toolbar
        caption: 'Carregar Atividades de Campanha Falhadas',
        pager: "#jqGridPager",
        gridComplete: initGrid
    });

    fetchGridData();
});

function fetchGridData() {
    var campaignActivityId;
    if (location.search != "") {
        vals = decodeURIComponent(location.search).split("=");
        campaignActivityId = vals[2].toString().split(',');
    }

    var gridArrayData = [];
    var odataquery = Xrm.Page.context.getClientUrl() + "/XRMServices/2011/OrganizationData.svc";
    odataquery += "/CampaignActivitySet(guid'" + campaignActivityId + "')";
    odataquery += "?$expand=CampaignActivity_BulkOperations";
    odataquery += "&$select=CampaignActivity_BulkOperations/ActivityId";

    $.getJSON(odataquery, function(data) {
        if(data.d.CampaignActivity_BulkOperations) {
            var bulkOperations = data.d.CampaignActivity_BulkOperations.results;
            for (var idxBulkOper = 0; idxBulkOper < bulkOperations.length; ++idxBulkOper) {
                var activityId = bulkOperations[idxBulkOper].ActivityId;

                var odataquery2 = Xrm.Page.context.getClientUrl() + "/XRMServices/2011/OrganizationData.svc";
                odataquery2 += "/BulkOperationSet(guid'" + activityId + "')";
                odataquery2 += "?$expand=BulkOperation_logs";
                odataquery2 += "&$select=BulkOperation_logs/ErrorNumber,BulkOperation_logs/RegardingObjectId";

                $.getJSON(odataquery2, function(data) {
                    var bulkLogs = data.d.BulkOperation_logs.results;

                    for (var i = 0; i < bulkLogs.length; i++) {
                        gridArrayData.push({
                            ObjectId: bulkLogs[i].RegardingObjectId.Id,
                            TypeRegister: bulkLogs[i].RegardingObjectId.LogicalName,
                            NomeRegisto: bulkLogs[i].RegardingObjectId.Name,
                            ErrorNumber: bulkLogs[i].ErrorNumber
                        });
                    };

                    $("#jqGrid").jqGrid('setGridParam', { data: gridArrayData});
                    $("#jqGrid").trigger('reloadGrid');
                });
            }
        }
    });
}

Quero realçar o seguinte código:

var odataquery = Xrm.Page.context.getClientUrl() + "/XRMServices/2011/OrganizationData.svc";
odataquery += "/CampaignActivitySet(guid'" + campaignActivityId + "')";
odataquery += "?$expand=CampaignActivity_BulkOperations";
odataquery += "&$select=CampaignActivity_BulkOperations/ActivityId";

Este código vai obter todas as atividade de campanha e sobre cada uma obtenho os erros que aconteceram, como se pode ver a seguir:

var odataquery2 = Xrm.Page.context.getClientUrl() + "/XRMServices/2011/OrganizationData.svc";
odataquery2 += "/BulkOperationSet(guid'" + activityId + "')";
odataquery2 += "?$expand=BulkOperation_logs";
odataquery2 += "&$select=BulkOperation_logs/ErrorNumber,BulkOperation_logs/RegardingObjectId";

Sobre o plugin jqGrid usei a extensão Context Menu para poder realizar ações sobre os registos listados, entre eles o inativar e o apagar. Para inativar usei a mensagem SetStateRequest, vejam aqui a função que utilizei:

function SetStateRequest(active, entity) {
    var grid = $("#jqGrid");
    var rowKey = grid.getGridParam("selrow");
    var rowData = grid.getRowData(rowKey);
    var entityId = rowData.ObjectId;

    var state, status;
    if(active){
        state = 0;
        status = 1;
    }
    else{
        state = 1;
        status = 2;
    }

   var requestMain = ""
   requestMain += "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\">";
   requestMain += "  <s:Body>";
   requestMain += "    <Execute xmlns=\"http://schemas.microsoft.com/xrm/2011/Contracts/Services\" xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\">";
   requestMain += "      <request i:type=\"b:SetStateRequest\" xmlns:a=\"http://schemas.microsoft.com/xrm/2011/Contracts\" xmlns:b=\"http://schemas.microsoft.com/crm/2011/Contracts\">";
   requestMain += "        <a:Parameters xmlns:c=\"http://schemas.datacontract.org/2004/07/System.Collections.Generic\">";
   requestMain += "          <a:KeyValuePairOfstringanyType>";
   requestMain += "            <c:key>EntityMoniker</c:key>";
   requestMain += "            <c:value i:type=\"a:EntityReference\">";
   requestMain += "              <a:Id>" + entityId + "</a:Id>";
   requestMain += "              <a:LogicalName>" + entity + "</a:LogicalName>";
   requestMain += "              <a:Name i:nil=\"true\" />";
   requestMain += "            </c:value>";
   requestMain += "          </a:KeyValuePairOfstringanyType>";
   requestMain += "          <a:KeyValuePairOfstringanyType>";
   requestMain += "            <c:key>State</c:key>";
   requestMain += "            <c:value i:type=\"a:OptionSetValue\">";
   requestMain += "              <a:Value>" + state + "</a:Value>";
   requestMain += "            </c:value>";
   requestMain += "          </a:KeyValuePairOfstringanyType>";
   requestMain += "          <a:KeyValuePairOfstringanyType>";
   requestMain += "            <c:key>Status</c:key>";
   requestMain += "            <c:value i:type=\"a:OptionSetValue\">";
   requestMain += "              <a:Value>" + status + "</a:Value>";
   requestMain += "            </c:value>";
   requestMain += "          </a:KeyValuePairOfstringanyType>";
   requestMain += "        </a:Parameters>";
   requestMain += "        <a:RequestId i:nil=\"true\" />";
   requestMain += "        <a:RequestName>SetState</a:RequestName>";
   requestMain += "      </request>";
   requestMain += "    </Execute>";
   requestMain += "  </s:Body>";
   requestMain += "</s:Envelope>";
   var req = new XMLHttpRequest();
   req.open("POST", _getServerUrl(), true)
   // Responses will return XML. It isn't possible to return JSON.
   req.setRequestHeader("Accept", "application/xml, text/xml, */*");
   req.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
   req.setRequestHeader("SOAPAction", "http://schemas.microsoft.com/xrm/2011/Contracts/Services/IOrganizationService/Execute");
   var successCallback = null;
   var errorCallback = null;
   req.onreadystatechange = function () { SetStateResponse(req, successCallback, errorCallback); };
   req.send(requestMain);
}

Para apagar o registo usei a interface REST:

function DeleteEntity(accountId, entity) {
    var serverUrl = Xrm.Page.context.getClientUrl();
    var ODATA_ENDPOINT = "/XRMServices/2011/OrganizationData.svc/" + entity + "Set";
    var ODataPath = serverUrl + ODATA_ENDPOINT;
    $.ajax({
        type: "POST",
        contentType: "application/json; charset=utf-8",
        datatype: "json",
        url: ODataPath + "(guid'" + accountId + "')",
        beforeSend: function (XMLHttpRequest) {
            XMLHttpRequest.setRequestHeader("Accept", "application/json");
            XMLHttpRequest.setRequestHeader("X-HTTP-Method", "DELETE");
        },
        error: function (xmlHttpRequest, textStatus, errorThrown) {
            alert("Status: " + textStatus + "; ErrorThrown: " + errorThrown);
        }
    });
} 

Reparem que para apagar temos que especificar o header “X-HTTP-Method” como delete.

Nesta solução a grande dificuldade foi mesmo a obtenção do tipo de erros referentes a esta comunicação o resto são coisas que já tínhamos visto anteriormente. Deixo-vos o ficheiro HTML do recurso web que usei, bem como a solução específica para o Dynamics CRM 2015. O meu objetivo é tornar esta solução num género de AddOn tanto para qualquer versão do CRM.

 

Até a próxima.

Tags: , , , , , , ,

Add comment

  Country flag

biuquote
  • Comment
  • Preview
Loading

About

Muito bem casado, Pai babado e um gosto muito grande pela tecnologia.

Tenho um lema "Sharing is Learning"

Mais aqui -> http://www.psazevedo.com

Month List