terça-feira, 13 de setembro de 2011

>> CRM 4.0 - FetchXML via JScript

Segue código aprimorado (extraído do SDK - http://technet.microsoft.com/en-us/library/cc677073.aspx) para busca de qualquer informação do CRM, via JScript.



window.ExecFetchXML = function (entityName, column, conditionAttribute, operator, value, fieldOrderName, fieldOrderValue, page, count) {

var fetchMapping = "logical";
var filterType = "and";
var authenticationHeader = GenerateAuthenticationHeader();
var columnSplit = column.split(",");
var conditionAttributeSplit = conditionAttribute.split(",");
var operatorSplit = operator.split(",");
var valueSplit = value.split(",");

var xml = "<?xml version='1.0' encoding='utf-8'?>" +
"<soap:Envelope xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/'" +
" xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'" +
" xmlns:xsd='http://www.w3.org/2001/XMLSchema'>" +
authenticationHeader +
"<soap:Body>" +
"<Fetch xmlns='http://schemas.microsoft.com/crm/2007/WebServices'>" +
"<fetchXml>&lt;fetch mapping='" + fetchMapping + "'";

if (page != null) xml += " page='" + page + "' ";
if (count != null) xml += " count='" + count + "' ";

xml += "&gt;&lt;entity name='" + entityName + "'&gt;";

if (columnSplit.length > 1) {
for (i = 0; i < columnSplit.length; i++) {
xml += "&lt;attribute name='" + String(columnSplit[i]) + "'/&gt;";
}
}
else {
xml += "&lt;attribute name='" + column + "'/&gt;";
}

if (fieldOrderName != null && fieldOrderValue != null) {
xml += "&lt;order attribute='" + fieldOrderName +
"' descending='" + fieldOrderValue + "'/&gt;";
}

if (conditionAttribute != null && operator != null && value != null) {
xml += "&lt;filter type='" + filterType + "'&gt;";

if (conditionAttributeSplit.length > 1) {
for (i = 0; i < conditionAttributeSplit.length; i++) {
xml += "&lt;condition attribute='" + conditionAttributeSplit[i] + "'" +
" operator='" + operatorSplit[i] + "' value='" + valueSplit[i] + "'/&gt;";
}
}
else {
xml += "&lt;condition attribute='" + conditionAttribute + "'" +
" operator='" + operator + "' value='" + value + "'/&gt;";
}

xml += "&lt;/filter&gt;";
}

xml += "&lt;/entity&gt;" +
"&lt;/fetch&gt;</fetchXml>" +
"</Fetch>" +
"</soap:Body>" +
"</soap:Envelope>";

// Prepare the xmlHttpObject and send the request.
var xHReq = new ActiveXObject("Msxml2.XMLHTTP");
xHReq.Open("POST", "/mscrmservices/2007/CrmService.asmx", false);
xHReq.setRequestHeader("SOAPAction", "http://schemas.microsoft.com/crm/2007/WebServices/Fetch");
xHReq.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
xHReq.setRequestHeader("Content-Length", xml.length);
xHReq.send(xml);

// Capture the result.
var resultXml = xHReq.responseXML;

// Check for errors.
var errorCount = resultXml.selectNodes('//error').length;
if (errorCount == 0) {
// Capture the result and UnEncode it.
var resultSet = new String();
resultSet = resultXml.text;

resultSet = resultSet.replace("<resultset morerecords=\"0\" />", "");

if (resultSet != "") {
// Create an XML document that you can parse.
var oXmlDoc = new ActiveXObject("Microsoft.XMLDOM");
oXmlDoc.async = false;
// Load the XML document that has the UnEncoded results.
oXmlDoc.loadXML(resultSet);

var retValue = null;

if (oXmlDoc.getElementsByTagName('result') != null) {

var objLen = oXmlDoc.getElementsByTagName('result').length;
retValue = new Array(objLen);

// Loop nas Linhas
for (j = 0; j < objLen; j++) {

retValue[j] = new Object();

if (columnSplit.length > 1) {
for (i = 0; i < columnSplit.length; i++) {
if (oXmlDoc.getElementsByTagName('result')[j].selectSingleNode(String(columnSplit[i])))
eval("retValue[" + String(j) + "]." + String(columnSplit[i]) + "= '" + oXmlDoc.getElementsByTagName('result')[j].selectSingleNode('./' + String(columnSplit[i])).text + "'");
}
}
else {

if (oXmlDoc.getElementsByTagName('result')[j].selectSingleNode(column))
eval("retValue[" + String(j) + "]." + column + "= '" + oXmlDoc.getElementsByTagName('result')[j].selectSingleNode(column).text + "'");

}
}
}
}
}

return retValue;

}



E as formas de utilização:



var result1 = window.ExecFetchXML('account', 'accountid, name', 'name', 'like', '%TESTE%', null, null, null, null);
alert(result1[0].accountid);
alert(result1[0].name);


/*
- Ordenado por [name];
- Ascendente [true];
- Retorna 2 páginas, com 10 clientes por página.
*/
var result2 = window.ExecFetchXML('account', 'accountid, name', 'name', 'eq', 'TESTE', 'name', 'true', 2, 10);

/*
- Busca por vários campos (name like '%TESTE%' e cnpj='12345678900');
*/
var result3 = window.ExecFetchXML('account', 'accountid, name', 'name,cnpj', 'like,eq', '%TESTE%,12345678900', null, null, null, null);

quinta-feira, 31 de março de 2011

>> CRM 4.0 - Erro de Abertura de Popups

Se você está se deparando com o seguinte erro no CRM:


Não foi possível abrir uma janela do Microsoft Dynamics CRM. É possível que ela tenha sido bloqueada por um bloqueador de pop-ups. Adicione este servidor Microsoft Dynamics CRM a lista de sites aos quais o seu bloqueador de pop-ups permite abrir novas janelas: <servidorcrm>




e já revisou (muitas vezes) as configurações do Internet Explorer, porém não encontrou a solução, segue mais uma dica importante:

- Pode acontecer que, ao tentar salvar um registro na entidade do CRM, algum [PLUGIN] esteja sendo executado; e se este plugin estiver usando a função:


// Gera um retorno de mensagem para o usuário do CRM (em um Popup).
throw new InvalidPluginExecutionException(MensagemErro);


Pode acontecer que a variável [MensagemErro] exceda o tamanho de [1900] caracteres, o que vai impossibilitar a passagem desta mensagem para a camada de [interface] do CRM, a qual deveria mostrá-la para o usuário.

Portanto, uma cadeia de caracteres muito grande, gera um erro "mentiroso" para o usuário!

Fiquem atentos CRM Developers!

[]s

terça-feira, 22 de março de 2011

>> DICA: Internet Information Services EXPRESS!

Quem desenvolve aplicações WEB, e já teve a necessidade de executar este tipo de aplicação em uma máquina [não-servidora], ou seja, que não contenha o [Internet Information Services] da Microsoft, deve estar familiarizado com o termo [Cassini Web Server], que justamente faz o papel do IIS.

Porém, como se sabe, esta aplicação é limitada, e consegue gerenciar aplicações que rodam sob o .NET Framework, até a versão 2.0 .

A boa notícia é que a Microsoft lançou uma versão [gratuita] do IIS, chamada IIS Express. Inclusive, nesta versão, é possível rodar aplicações que utilizam o .NET Framework 4.0 .

O blog do [Scott Guthrie] explica muito bem esta tecnologia.

[]s

quinta-feira, 9 de dezembro de 2010

>> Microsoft Office - Tecnologia OpenXML

Olá Pessoal,

Gostaria de partilhar com vocês uma nova tecnologia desenvolvida pela Microsoft para manipulação de qualquer documento do Office, chamada [Office Open XML - OOXML]. Quem ainda não usou vai realmente se surpreender, principalmente se está acostumado a usar a tecnologia anterior, chamada [Office Interop Automation].

Veja alguma das principais vantagens desta nova tecnologia:

- Manipula documentos do Office no formato ZIP [XML-based]. Para comprovar isto, basta renomear algum documento Office para [.zip] e notar que seu conteúdo é representado por um pacote de XMLs e outros arquivos de recursos relacionados;

- Esta tecnologia foi padronizada pela [ECMA], que regulariza e organiza padrões [mundiais] no uso de softwares;

- OOXML pode ser usado a partir da versão 2007 do Office;

- E o que considero como principal vantagem, possui a melhor performance já vista na manipulação de documentos.
Por exemplo, foi feita uma comparação pela Microsoft no uso da tecnologia anterior com a OOXML (http://msdn.microsoft.com/en-us/library/ff191178.aspx). Foi desenvolvido um código para inserir uma imagem em cada uma das 300 linhas em uma [tabela do Word]. Na tecnologia anterior o tempo gasto para realizar esta tarefa foi de [60 segundos]. Na tecnologia OOXML este tempo caiu para incríveis [5 segundos]!.

- Diferentemente da versão anterior, OOXML NÃO EXIGE a instalação do Microsoft Office no servidor onde seu código será instalado. Enorme vantagem!...

A utilização deste componente no seu projeto .NET é muito simples, basta adicionar uma referência do arquivo [Document.OpenXml.dll] no seu projeto e sair codificando.

Para ilustrar o uso deste componente, note o código abaixo, que usa [Linq To Object] do .NET Framework para recuperar todas as [tables] de um documento do Office Word:



using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;

using (WordprocessingDocument wordDocument = WordprocessingDocument.Open('c:\WordDoc.docx', true))
{
List<Table> listTable = wordDocument.MainDocumentPart.Document.Body.Elements<Table>().ToList<Table>();
}


É claro que, quando você começa a estudar mais a fundo esta tecnologia, nota que, para algumas tarefas, o desenvolvimento se torna mais complexo, até porque você vai manipular uma estrutura de arquivos proprietários do pacote ZIP-XML-Based. Por exemplo, se precisar adicionar um texto em uma célula de uma tabela do Office Word, vai notar que precisa seguir algumas regras de uso de objetos dentro da célula. Veja o código abaixo:



using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;

using (WordprocessingDocument doc = WordprocessingDocument.Open('c:\WordDoc.docx', true))
{
Table table = doc.MainDocumentPart.Document.Body.Elements<table>().First();
TableRow row = table.Elements<tablerow>().ElementAt(1);
TableCell cell = row.Elements<tablecell>().ElementAt(2);
Paragraph p = cell.Elements<paragraph>().First();
Run r = p.Elements<run>().First();
Text t = r.Elements<text>().First();
t.Text = 'Some Text';
}


No exemplo acima, o código realiza os seguintes passos:
- Acessa a [primeira table] que encontrar no documento;
- Acessa a [segunda] linha;
- E altera o conteúdo da [terceira] coluna.

Notamos também que existe uma [hierarquia] de objetos a serem acessados, como por exemplo, a classe [Paragraph] que contém a classe [Run], que por sua vez contém a classe [Text].

Assim como em outros produtos Microsoft, a documentação referente ao OOXML é muito boa. Basta baixar o SDK, acessando:

http://www.microsoft.com/downloads/en/details.aspx?FamilyId=C6E744E5-36E9-45F5-8D8C-331DF206E0D0&displaylang=en

Note que existem dois tipos de instalador:

1 - OpenXMLSDKTool.msi

Esta ferramenta é muito útil porque permite a visualização do conteúdo de qualquer documento Office, e um incrível recurso que possibilita gerar [código .NET] a partir de qualquer parte do documento aberto. Desta forma o processo de aprendizado é acelerado, pois você analisa um código pronto.

Segue tela do OpenXMLTool, com a utilização do [Reflect Code]:




2 - OpenXMLSDKv2.msi

Possui a documentação completa do OOXML, com diversos exemplos práticos.

Tela principal do SDK:



That´s It!

quinta-feira, 21 de outubro de 2010

>> CRM 4.0 - Link Dinâmico no Corpo do Email

Olá Pessoal,

Pouco tempo atrás me surgiu uma necessidade interessante, a criação de um [Link Dinâmico] no corpo de um email no CRM.

Eu consegui criar uma solução simples e funcional que gostaria de partilhar com vocês.

Para esta solução existem diversos cenários possíveis para a configuração do Link Dinâmico, porém, vamos usar um cenário em que deveremos enviar um email para um contato que foi criado no CRM.

No corpo do email enviaremos um link que apontará para o formulário do CRM, no registro daquele contato criado.

Para o envio do email utilizaremos o recurso do CRM de Workflow. Veja como deverá ficar o workflow completo:



1 - Como primeiro passo, vamos criar dois campos na entidade de Contato, por exemplo, [new_url_ctt] e [new_url_link] do tipo [ntext] e adicioná-los no formulário do Contato (importante adicionar no formulário para podermos utilizá-los nos passos a frente);

2 - Adicione no [OnLoad] do Formulário de Contato o JScript abaixo, para preenchimento do campo [new_url_ctt]:


if(crmForm.FormType != 1)
{
if(crmForm.all.new_url_ctt.DataValue == null)
{
crmForm.all.new_url_ctt.DataValue = 'http://localhost:5555/sfa/conts/edit.aspx?id='
+ crmForm.ObjectId;

crmForm.Save();
}
}


3 - Vamos agora desenvolver um workflow para preencher o campo [new_url_link] e enviar o email;
3.1 - Crie, portanto, um workflow para ser disparado no evento de [criação de novo registro];
3.2 - Clique no botão [Adicionar Etapa] e escolha [Aguardar Condição]; para a condição coloque [até que url_ctt "contenha dados"];
3.3 - Uma vez que a condição foi satisfeita, adicione a etapa [Atualizar Registro]. No botão [Definir Propriedades], atualize o campo [new_url_link] conforme mostra a imagem abaixo:



3.4 - E como passo final adicione a etapa [Enviar Email], usando o campo [new_url_link] que foi preenchido no passo anterior, conforme a imagem:



Quando o email for criado pelo CRM, o link aparecerá no corpo do email, como mostra a figura:



That´s It!

quarta-feira, 7 de julho de 2010

>> CRM 4.0 - Dica para Esconder uma Opção do Menu Vertical do Formulário

Uma dica rápida!

Segue código JScript para desabilitar uma opção de menu vertical do formulário do CRM.

A idéia é colar o código abaixo no [OnLoad] do formulário e usar a função, passando como parâmetro o [nome] do item do menu que deseja esconder. Por exemplo, para esconder a opção [mais endereços], use a função da seguinte maneira:


crmForm.disableVerticalMenuItem("crmNavBar", "mais endereços");



crmForm.disableVerticalMenuItem = function (navBar, menuItem)
{
menuItem = menuItem.toLowerCase().replace(/^\s+|\s+$/g, '');
il=document.getElementById(navBar).getElementsByTagName('li');

for(i=0;i < il.length;i++)
{
liItem = il[i].innerText.toLowerCase().replace(/^\s+|\s+$/g, '');

if (liItem == menuItem)
{
anchor = il[i].getElementsByTagName('a')[0];
anchor.parentNode.removeChild(anchor);
}
}
}

crmForm.disableVerticalMenuItem("crmNavBar", "mais endereços");

sexta-feira, 26 de março de 2010

>> CRM 4.0 - Exibição de Relatórios no IFRAME (OnLine e OffLine Access)

Sem dúvida alguma, um dos grandes recursos do Dynamics CRM para o desenvolvedor é a possibilidade de integração do [Reporting Services] no produto. Inclusive, o processo de publicação dos relatórios desenvolvidos no Dynamics CRM fica fácil, pois utilizamos a própria interface do produto para isto.

Pensando nisto, imagine o desenvolvimento de um relatório para o Dynamics CRM, porém, com a necessidade de visualização deste relatório em um [IFrame]; e mais, o relatório deve funcionar para as versões do Dynamics CRM [OnLine] e para [Outlook OFFLine]!.

Pois bem, segue uma forma [simples] de resolver esta questão:

1 - Desenvolva normalmente seu relatório (usando, é claro, as FilteredViews);
2 - Efetue o Upload do relatório no Dynamics CRM;
3 - Adicione o [IFrame] na entidade que deseja visualizar o relatório;

A idéia agora é preencher o IFrame dinamicamente, no [OnLoad] do formulário.

Usando o importante recurso do Dynamics CRM chamado [URL Addressable Forms], também podemos nos utilizar disto para os relatórios.

Portanto, no [OnLoad] do formulário, adicione o seguinte código:


var reportID = getReportIDByName("NOMEDOSEURELATORIO");

var urlReport = SERVER_URL + "/crmreports/viewer/viewer.aspx?action=run&id=" + reportID + "&context=records&recordstype=1&records=" + crmForm.ObjectId + "&helpID=NOMEDOSEURELATORIO.rdl";

crmForm.all.IFRAME_report.src=urlReport;

function getReportIDByName(reportname)
{
return ExecFetchXML('report', 'reportid', 'name', 'eq', reportname);
}

function ExecFetchXML(entityName, column, conditionAttribute, operator, value)
{
var guid = "";
var fetchMapping = "logical";
var filterType = "and";
var authenticationHeader = GenerateAuthenticationHeader();

// Prepare the SOAP message.
var xml = "<?xml version='1.0' encoding='utf-8'?>"+
"<soap:Envelope xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/'"+
" xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'"+
" xmlns:xsd='http://www.w3.org/2001/XMLSchema'>"+
authenticationHeader+
"<soap:Body>"+
"<Fetch xmlns='http://schemas.microsoft.com/crm/2007/WebServices'>"+
"<fetchXml><fetch mapping='"+fetchMapping+"'>"+
"<entity name='"+entityName+"'>"+
"<attribute name='"+column+"'/>"+
"<filter type='"+filterType+"'>"+
"<condition attribute='"+conditionAttribute+"'"+
" operator='"+operator+"' value='"+value+"'/>"+
"</filter>"+
"</entity>"+
"</fetch></fetchXml>"+
"</Fetch>"+
"</soap:Body>"+
"</soap:Envelope>";
// Prepare the xmlHttpObject and send the request.
var xHReq = new ActiveXObject("Msxml2.XMLHTTP");
xHReq.Open("POST", "/mscrmservices/2007/CrmService.asmx", false);
xHReq.setRequestHeader("SOAPAction","http://schemas.microsoft.com/crm/2007/WebServices/Fetch");
xHReq.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
xHReq.setRequestHeader("Content-Length", xml.length);
xHReq.send(xml);
// Capture the result.
var resultXml = xHReq.responseXML;

// Check for errors.
var errorCount = resultXml.selectNodes('//error').length;
if (errorCount == 0)
{
// Capture the result and UnEncode it.
var resultSet = new String();
resultSet = resultXml.text;

// Create an XML document that you can parse.
var oXmlDoc = new ActiveXObject("Microsoft.XMLDOM");
oXmlDoc.async = false;
// Load the XML document that has the UnEncoded results.
oXmlDoc.loadXML(resultSet);

if (oXmlDoc.getElementsByTagName('result') != null)
guid = oXmlDoc.getElementsByTagName('result')[0].selectSingleNode('./' + column).nodeTypedValue;
}

return guid;
}


Note o uso dos seguintes recursos:

- Obtenção do [ID] do relatório adicionado no CRM, através da função [getReportIDByName];

- URL de relatórios do CRM: [/crmreports/viewer/viewer.aspx]. Note os parâmetros preenchidos para a execução dinâmica do relatório;

- O relatório será também executado no modo OFFLINE (através do uso da variável Global [SERVER_URL] do CRM);

- E a utilização de um FetchXML (função ExecFetchXML) para busca de informação na Entidade [ReportBase] do CRM.

Segue exemplo, em uma Conta, da execução de um relatório em um IFrame:



That´s It!

Abraços.