CRM 2013 Javascript Object Model - Atributos

by Pedro Azevedo 15. March 2014 23:52

Boas pessoal,

 Como prometido vamos dar casos de chamadas de cada um dos métodos disponíveis ao nível do atributo. Para termos acesso a um atributo utilizámos a função Xrm.Page.data.entity.attributes.get(“…”) ou o “atalho” Xrm.Page.getAttribute(“…”).

 getAttributeType()

 Valores que pode devolver este método:

  • boolean
  • datetime
  • decimal
  • double
  • integer
  • lookup
  • memo
  • money
  • optionset
  • string
function getAttributeTypeTest() {

                var exampleOfAttributeTest = "subject do tipo " + Xrm.Page.getAttribute("subject").getAttributeType() + "\n";

                exampleOfAttributeTest += "fullname do tipo " + Xrm.Page.getAttribute("fullname").getAttributeType() + "\n";

                exampleOfAttributeTest += "telephone1 do tipo " + Xrm.Page.getAttribute("telephone1").getAttributeType() + "\n";

                exampleOfAttributeTest += "emailaddress1 do tipo " + Xrm.Page.getAttribute("emailaddress1").getAttributeType() + "\n";

                exampleOfAttributeTest += "websiteurl do tipo " + Xrm.Page.getAttribute("websiteurl").getAttributeType() + "\n";

                exampleOfAttributeTest += "revenue do tipo " + Xrm.Page.getAttribute("revenue").getAttributeType() + "\n";

                exampleOfAttributeTest += "numberofemployees do tipo " + Xrm.Page.getAttribute("numberofemployees").getAttributeType() + "\n";

                exampleOfAttributeTest += "industrycode do tipo " + Xrm.Page.getAttribute("industrycode").getAttributeType() + "\n";

                exampleOfAttributeTest += "campaignid do tipo " + Xrm.Page.getAttribute("campaignid").getAttributeType() + "\n";

                exampleOfAttributeTest += "lastusedincampaign do tipo " + Xrm.Page.getAttribute("lastusedincampaign").getAttributeType() + "\n";

                exampleOfAttributeTest += "decisionmaker do tipo " + Xrm.Page.getAttribute("decisionmaker").getAttributeType() + "\n";

               

                Xrm.Utility.alertDialog(exampleOfAttributeTest);

}

getFormat()

Este método pode retornar os valores presentes na coluna “Valor do formato”, os outros valores representam o mapeamento que pode haver:

Tipo de Campo

Opção do Formato

Tipo de Atributo

Valor do formato

Date and Time

Date Only

datetime

date

Date and Time

Date and Time

datetime

datetime

Whole Number

Duration

integer

duration

Single Line of Text

E-mail

string

email

Whole Number

Language

optionset

language

Whole Number

None

integer

none

Single Line of Text

Text Area

string

textarea

Single Line of Text

Text

string

text

Single Line of Text

Ticker Symbol

string

tickersymbol

Single Line of Text

Phone

string

phone

Whole Number

Time Zone

optionset

timezone

Single Line of Text

Url

string

url

function getFormatTest() {

                var exampleOfAttributeTest = "subject tem o formato " + Xrm.Page.getAttribute("subject").getFormat() + "\n";

                exampleOfAttributeTest += "fullname tem o formato " + Xrm.Page.getAttribute("fullname").getFormat() + "\n";

                exampleOfAttributeTest += "telephone1 tem o formato " + Xrm.Page.getAttribute("telephone1").getFormat() + "\n";

                exampleOfAttributeTest += "emailaddress1 tem o formato " + Xrm.Page.getAttribute("emailaddress1").getFormat() + "\n";

                exampleOfAttributeTest += "websiteurl tem o formato " + Xrm.Page.getAttribute("websiteurl").getFormat() + "\n";

                exampleOfAttributeTest += "revenue tem o formato " + Xrm.Page.getAttribute("revenue").getFormat() + "\n";

                exampleOfAttributeTest += "numberofemployees tem o formato " + Xrm.Page.getAttribute("numberofemployees").getFormat() + "\n";

                exampleOfAttributeTest += "industrycode tem o formato " + Xrm.Page.getAttribute("industrycode").getFormat() + "\n";

                exampleOfAttributeTest += "campaignid tem o formato " + Xrm.Page.getAttribute("campaignid").getFormat() + "\n";

                exampleOfAttributeTest += "lastusedincampaign tem o formato " + Xrm.Page.getAttribute("lastusedincampaign").getFormat() + "\n";

                exampleOfAttributeTest += "decisionmaker tem o formato " + Xrm.Page.getAttribute("decisionmaker").getFormat() + "\n";

               

                Xrm.Utility.alertDialog(exampleOfAttributeTest);

} 

getIsDirty()

function getIsDirtyTest() {

                Xrm.Utility.alertDialog(Xrm.Page.getAttribute("subject").getIsDirty());

}

De referir se tiver o autosave activado poderá não apanhar que o campo está dirty porque quando ocorre um save esse campo deixa de estar nesse estado.

getIsPartyList()

function getIsPartyListTest() {
                Xrm.Utility.alertDialog(Xrm.Page.getAttribute("ownerid").getIsPartyList());

}

getMaxLength()

function getMaxLengthTest() {

                var exampleOfAttributeTest = "subject tem o tamanho máximo " + Xrm.Page.getAttribute("subject").getMaxLength() + "\n";

                exampleOfAttributeTest += "fullname tem o tamanho máximo " + Xrm.Page.getAttribute("fullname").getMaxLength() + "\n";

                exampleOfAttributeTest += "telephone1 tem o tamanho máximo " + Xrm.Page.getAttribute("telephone1").getMaxLength() + "\n";

                exampleOfAttributeTest += "emailaddress1 tem o tamanho máximo " + Xrm.Page.getAttribute("emailaddress1").getMaxLength() + "\n";

                exampleOfAttributeTest += "websiteurl tem o tamanho máximo " + Xrm.Page.getAttribute("websiteurl").getMaxLength() + "\n";

               

                Xrm.Utility.alertDialog(exampleOfAttributeTest);

} 

Se estavam com atenção utilizei o mesmo código que nos métodos anteriores, mas retirei alguns dos campos pois não contêm este método, só os atributos do tipo string e memo têm este método.

getName()

function getNameTest() {

                Xrm.Utility.alertDialog(Xrm.Page.getAttribute("subject").getName());

}

getParent()

getRequiredLevel()

Possíveis valores de retorno:

  • none
  • required
  • recommended
function getRequiredLevelTest() {         

                var exampleOfAttributeTest = "subject tem este nível de obrigatoriedade " + Xrm.Page.getAttribute("subject").getRequiredLevel() + "\n";

                exampleOfAttributeTest += "telephone1 tem este nível de obrigatoriedade " + Xrm.Page.getAttribute("telephone1").getRequiredLevel() + "\n";

                exampleOfAttributeTest += "companyname tem este nível de obrigatoriedade " + Xrm.Page.getAttribute("emailaddress1").getRequiredLevel() + "\n";

               

                Xrm.Utility.alertDialog(exampleOfAttributeTest);

}

setSubmitMode(“mode”)

Este método define o modo que um campo se vai comportar aquando da sua gravação na BD. Por defeito tem o valor dirty e só vai ser gravado caso existe alterações nesse campo. Se queremos que o campo seja sempre gravado na BD utilizamos o valor always e never para nunca ser gravado. Este método é muito utilizado quando um controlo está disabled na UI, pois estando disabled o CRM coloca-o com o valor never, mas se o alterarmos em background teremos que colocar o valor always.

function setSubmitModeTest() {

                Xrm.Page.getAttribute("telephone1").setSubmitMode("always");

                Xrm.Page.getAttribute("companyname").setSubmitMode("never");

}

getSubmitMode()

function getSubmitModeTest() {           

                var exampleOfAttributeTest = "subject tem este modo de submissão " + Xrm.Page.getAttribute("subject").getSubmitMode() + "\n";

                exampleOfAttributeTest += "telephone1 tem este modo de submissão " + Xrm.Page.getAttribute("telephone1").getSubmitMode() + "\n";

                exampleOfAttributeTest += "companyname tem este modo de submissão " + Xrm.Page.getAttribute("companyname").getSubmitMode() + "\n";

               

                Xrm.Utility.alertDialog(exampleOfAttributeTest);

}

getUserPrivilege()

Esta função retorna um objecto que nos diz que operações se pode realizar sobre o atributo mediante o nível de segurança (Field Level Security) sobre esse mesmo atributo. Este objecto tem três propriedades: canRead, canUpdate, canCreate.

function getUserPrivilegeTest() {

                var exampleOfAttributeTest = "Para o campo subject ele pode ler " + Xrm.Page.getAttribute("subject").getUserPrivilege()["canRead"] + "\n";

                exampleOfAttributeTest += "Para o campo subject ele pode criar " + Xrm.Page.getAttribute("subject").getUserPrivilege()["canCreate"] + "\n";

                exampleOfAttributeTest += "Para o campo subject ele pode actualizar " + Xrm.Page.getAttribute("subject").getUserPrivilege()["canUpdate"] + "\n";

                Xrm.Utility.alertDialog(exampleOfAttributeTest);

}

getValue()

function getValueTest() {

                Xrm.Utility.alertDialog(Xrm.Page.getAttribute("subject").getValue());

}

Aqui gostava de ressalvar uma particularidade, quando estamos a tratar de lookups para obter informação teremos que fazer algo do género:

Xrm.Page.getAttribute("subject").getValue()[0].id //Obter o guid desse registo
Xrm.Page.getAttribute("subject").getValue()[0].name //Obter o guid o texto do registo
Xrm.Page.getAttribute("subject").getValue()[0]entityType //Obter o tipo de entidade desse registo

setValue(value)

function setValueTest() {

                Xrm.Page.getAttribute("subject").setValue("Novo Valor");

}

setRequiredLevel()

function setRequiredLevelTest() {

                Xrm.Page.getAttribute("jobtitle").setRequiredLevel("required");

} 

 

getMax()

function getMaxTest() {

                var exampleOfAttributeTest = "Valor máximo do campo revenue " + Xrm.Page.getAttribute("revenue").getMax() + "\n";

                exampleOfAttributeTest += "Valor máximo do campo número de empregados " + Xrm.Page.getAttribute("numberofemployees").getMax() + "\n";

                Xrm.Utility.alertDialog(exampleOfAttributeTest);

}

Como podemos ver este método funciona apenas para campos numéricos.

getMin()

function getMinTest() {

                var exampleOfAttributeTest = "Valor mínimo do campo revenue " + Xrm.Page.getAttribute("revenue").getMin() + "\n";

                exampleOfAttributeTest += "Valor mínimo do campo número de empregados " + Xrm.Page.getAttribute("numberofemployees").getMin() + "\n";

                Xrm.Utility.alertDialog(exampleOfAttributeTest);

}

getPrecision()

function getPrecisionTest() {

                var exampleOfAttributeTest = "Número de casas decimais no campo revenue " + Xrm.Page.getAttribute("revenue").getPrecision() + "\n";

                exampleOfAttributeTest += "Número de casas decimais no campo número de empregados " + Xrm.Page.getAttribute("numberofemployees").getPrecision() + "\n";

                Xrm.Utility.alertDialog(exampleOfAttributeTest);

}

setPrecision(value)

function setPrecisionTest() {

                Xrm.Page.getAttribute("new_float").setPrecision("2");

                Xrm.Utility.alertDialog(Xrm.Page.getAttribute("new_float").getPrecision());

}
 

Espero que tenham gostado deste nova categoria de posts. Mais virão...

 

Até a próxima.

Tags: , , , ,

Esconder botão dinâmicamente ribbon ou barra de comandos

by Pedro Azevedo 19. February 2014 23:30

Boas Pessoal,

Já queria fazer este post há muito tempo, mas fui adiando e nunca cheguei a criar sobre a versão CRM 2011. Por isso estou agora a fazê-lo sobre a versão CRM 2013 mas é similar a anterior, por isso poderão aplicar os mesmos conceitos sobre o CRM 2011.

Criei neste momento por causa de uma dúvida nos fóruns do MSDN, em que se quer inibir a criação de uma encomenda numa proposta. Neste caso o critério é que a razão de estado da oportunidade esteja preenchida e tem como requisito que seja feito via Javascript e não por plugin.

Para além de responder a questão do fórum este post tem como objetivo de ser um manual genérico para desabilitar um botão dinamicamente.

Bom o primeiro requisito é instalar esta ferramenta que podem ver aqui um pequeno manual.

A primeira coisa a fazer é criar uma solução que tenha a entidade Proposta (quote), depois desta solução criada teremos que fazer load dela na Ribbon Workbench e terá um layout deste género:

 

O que nós queremos é o botão que temos selecionado “Create Order” estar visível apenas quando uma determinada função Javascript retornar true. Para isso sobre este botão “Create Order” e com o botão direito do rato, carregar “Customise Button”, quando fazemos isto ele deve acrescentar o comando de criar encomenda na tab “Solution Elements”:

Para já aqui é tudo, voltamos mais a frente. Agora vamos criar uma regra de enable, na entrada “Enable Rules” escolham “Add New”:

 

Ele cria uma nova entrada, se quiserem podem modificar o nome, sobre esta nova entrada carreguem em “Add Rule”:

 

Com isto deve aparecer uma lista de regras que podemos aplicar, no nosso caso será uma regra de Javascript.

 

Depois de confirmarmos que queremos uma regra de Javascript, basta dizer qual a função a ser chamada e sobre qual o recurso web queremos aplicar.

Como podem comprovar vou chamar a função Javascript enablequotebutton do recurso web que especifiquei, este recurso web terá que estar na solução que carregamos dentro da ferramenta, neste caso na solução TesteRibbon.

O código presente neste webresource é o seguinte:

function enablequotebutton(){
  LoadWebResource('new_json');
  LoadWebResource('new_jquery');
  LoadWebResource('new_xrmservicetoolkit');
 
  var opId = Xrm.Page.getAttribute("opportunityid").getValue()[0].id;
  var cols = ["statuscode"];
  var retrievedOpportunity = XrmServiceToolkit.Soap.Retrieve("opportunity", opId, cols);

  if(retrievedOpportunity.attributes["statuscode"].value != 1){
    return true;
  }
  else{
    return false;
  }
}

function LoadWebResource(resource) {
  httpRequest = new XMLHttpRequest();
  httpRequest.open("GET", "/webresources/" + resource, false);
  httpRequest.send(null);
  try
    {
      eval(httpRequest.responseText); 
    }
    catch (e)
    {
      alert("Erro");
    }
}

Como podem ver no troço de código anterior estou a utilizar uma função auxiliar para poder carregar bibliotecas Javascript, pois estou a utilizar a biblioteca XrmServiceToolkit para poder ir buscar propriedades da oportunidade associada a proposta.

Depois disto não esqueçam de publicar a solução na Ribbon Workbench.

Até a próxima.

Tags: , , , , ,

CRM 2011 - Listar todas as opções num WebResource (Recurso Web)

by Pedro Azevedo 12. December 2013 01:39

Boas pessoal,

Foi colocada uma questão nos fóruns da Microsoft. Basicamente a questão era como obter todos os estados de uma entidade e listar em um Recurso Web. Como o código e os passos eram bastantes decidi colocar aqui no blog e ajudar futuramente outros programadores.

No final deste desenvolvimento como resultado teremos um Recurso Web a listar num OptionSet todas as opções de um OptionSet criado no CRM. Para isso teremos que aceder aos Metadados do CRM. Neste caso vou usar directamente uma mensagem SOAP para obter estes dados, usando a mensagem RetrieveAttributeRequest. Mas chamando através do Javasript.

O primeiro passo foi criar o Recurso Web no tipo HTML, neste Recurso Web coloquei o seguinte código:

<HTML xmlns:b xmlns:a xmlns:soapenv>
	<HEAD>
         </HEAD>
	<BODY>
		<SELECT id=myList/>
	</BODY>
</HTML>

Até agora nada de especial, vamos colocar algum código Javascript, como vamos estar em um Recurso Web vamos ter que obter informação do contexto para isso teremos que referenciar este aspx ClientGlobalContext.js.aspx (já está no CRM não é necessário referenciar por exemplo noutro Recurso Web).

Com esta referência poderemos então obter o contexto desta maneira:

var context = GetGlobalContext();
var serverUrl = context.getServerUrl();

NOTA: O método getServerUrl encontra-se deprecated com a vinda do UR12, para rollups superiores deve-se usar o método getClientUrl.

Agora "basta" construirmos o pedido SOAP para obter a informação que pretendemos:

var SoapPath = serverUrl + "/XRMServices/2011/Organization.svc/web";
var MetadataId = "00000000-0000-0000-0000-000000000000";
				
var request = " <soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\"><soapenv:Body>"
request += "<Execute xmlns=\"http://schemas.microsoft.com/xrm/2011/Contracts/Services\" xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\">";
request += "<request i:type=\"a:RetrieveAttributeRequest\" xmlns:a=\"http://schemas.microsoft.com/xrm/2011/Contracts\">";
request += "<a:Parameters xmlns:b=\"http://schemas.datacontract.org/2004/07/System.Collections.Generic\">";
request += "<a:KeyValuePairOfstringanyType>";
request += "<b:key>EntityLogicalName</b:key>";
request += "<b:value i:type=\"c:string\" xmlns:c=\"http://www.w3.org/2001/XMLSchema\">" + entityLogicalName + "</b:value>";
request += "</a:KeyValuePairOfstringanyType>";
request += "<a:KeyValuePairOfstringanyType>";
request += "<b:key>MetadataId</b:key>";
request += "<b:value i:type=\"ser:guid\"  xmlns:ser=\"http://schemas.microsoft.com/2003/10/Serialization/\">" + MetadataId + "</b:value>";
request += "</a:KeyValuePairOfstringanyType>";
request += "<a:KeyValuePairOfstringanyType>";
request += "<b:key>RetrieveAsIfPublished</b:key>";
request += "<b:value i:type=\"c:boolean\" xmlns:c=\"http://www.w3.org/2001/XMLSchema\">" + retrieveAsIfPublished + "</b:value>";
request += "</a:KeyValuePairOfstringanyType>";
request += "<a:KeyValuePairOfstringanyType>";
request += "<b:key>LogicalName</b:key>";
request += "<b:value i:type=\"c:string\"   xmlns:c=\"http://www.w3.org/2001/XMLSchema\">" + attributeLogicalName + "</b:value>";
request += "</a:KeyValuePairOfstringanyType>";
request += "</a:Parameters>";
request += "<a:RequestId i:nil=\"true\" /><a:RequestName>RetrieveAttribute</a:RequestName></request>";
request += "</Execute>";
request += "</soapenv:Body></soapenv:Envelope>";
				
var req = new XMLHttpRequest();
req.open("POST", SoapPath, false);
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");
req.send(request);

Com este pedido podemos aceder a todas as opções do OptionSet escolhido. A seguir vou colocar todo o código, inclusivé o que permite fazer parsing dos resultados e que coloca noutra OptionSet.

<HTML xmlns:b xmlns:a xmlns:soapenv><HEAD>
<SCRIPT src="ClientGlobalContext.js.aspx"></SCRIPT>

<SCRIPT type=text/javascript>
			function getOptionSet (entityLogicalName, attributeLogicalName, retrieveAsIfPublished) {				
				var context = GetGlobalContext();
                var serverUrl = context.getServerUrl();
	            var SoapPath = serverUrl + "/XRMServices/2011/Organization.svc/web";
				var MetadataId = "00000000-0000-0000-0000-000000000000";
				
                var request = " <soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\"><soapenv:Body>"
				request += "<Execute xmlns=\"http://schemas.microsoft.com/xrm/2011/Contracts/Services\" xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\">";
				request += "<request i:type=\"a:RetrieveAttributeRequest\" xmlns:a=\"http://schemas.microsoft.com/xrm/2011/Contracts\">";
				request += "<a:Parameters xmlns:b=\"http://schemas.datacontract.org/2004/07/System.Collections.Generic\">";
				request += "<a:KeyValuePairOfstringanyType>";
				request += "<b:key>EntityLogicalName</b:key>";
				request += "<b:value i:type=\"c:string\" xmlns:c=\"http://www.w3.org/2001/XMLSchema\">" + entityLogicalName + "</b:value>";
				request += "</a:KeyValuePairOfstringanyType>";
				request += "<a:KeyValuePairOfstringanyType>";
				request += "<b:key>MetadataId</b:key>";
				request += "<b:value i:type=\"ser:guid\"  xmlns:ser=\"http://schemas.microsoft.com/2003/10/Serialization/\">" + MetadataId + "</b:value>";
				request += "</a:KeyValuePairOfstringanyType>";
				request += "<a:KeyValuePairOfstringanyType>";
				request += "<b:key>RetrieveAsIfPublished</b:key>";
				request += "<b:value i:type=\"c:boolean\" xmlns:c=\"http://www.w3.org/2001/XMLSchema\">" + retrieveAsIfPublished + "</b:value>";
				request += "</a:KeyValuePairOfstringanyType>";
				request += "<a:KeyValuePairOfstringanyType>";
				request += "<b:key>LogicalName</b:key>";
				request += "<b:value i:type=\"c:string\"   xmlns:c=\"http://www.w3.org/2001/XMLSchema\">" + attributeLogicalName + "</b:value>";
				request += "</a:KeyValuePairOfstringanyType>";
				request += "</a:Parameters>";
				request += "<a:RequestId i:nil=\"true\" /><a:RequestName>RetrieveAttribute</a:RequestName></request>";
				request += "</Execute>";
				request += "</soapenv:Body></soapenv:Envelope>";
				
				var req = new XMLHttpRequest();
				req.open("POST", SoapPath, false);
				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");
				req.send(request);

				if (req.responseXML != null) {
					var attributeData = req.responseXML.selectSingleNode("//b:value");
					if (attributeData != null) {
						var attributeType = attributeData.selectSingleNode("c:AttributeType").text;
						switch (attributeType) {
							case "Picklist":
								return attributeData;
								break;
							default:
								break;
						}
					}
				}
			}
		</SCRIPT>

<META charset=utf-8></HEAD>
<BODY contentEditable=true><SELECT id=myList></SELECT>
<SCRIPT>
	var optionSet = getOptionSet("ret_accao", "ret_estadocadastro", true);
	var options = optionSet.selectSingleNode("c:OptionSet//c:Options");
    var myPickList = document.getElementById("myList");
	for (var i = 0; i < options.childNodes.length; i++) {
		var textPick = options.childNodes[i].selectSingleNode("c:Label").selectSingleNode("a:UserLocalizedLabel").selectSingleNode("a:Label").text;
        var option = document.createElement("option");
        option.text = textPick;
        myPickList.add(option, myPickList.options[null]);
	}
</SCRIPT>
</BODY></HTML>

Se não quiserem andarem a fazer copy paste do código tenho aqui a solution que contém este código. É fazer download e importá-lo na vossa organização e lançar num botão por exemplo.

NOTA: Atenção ao cache dos web resources, façam iisreset e caso não resulte forcem o reload (CTRL+F5) na própria páguina que contiver este web resource

ListadeEstados_1_0_0_0.zip (3,29 kb)

Tags: , , , ,

CRM 2013 - Obter tipo de formulário

by Pedro Azevedo 17. November 2013 11:28

 

Boas Pessoal,

Hoje deixo aqui um post rápido, apenas porque precisei desta informação. E decidi criar uma categoria de posts sobre funções javascript da API Javascript do CRM 2013. Por isso já falei com getSaveMode. Desta vez vou citar outro método o getFormType que nos dá qual o tipo de formulário que estamos a trabalhar.

Por exemplo se estamos num form de criação, actualização ou se estamos em readonly. Isto é útil.

Aqui ficam os vários valores possíveis:

Form Type

Value

Undefined

0

Create

1

Update

2

Read Only

3

Disabled

4

Quick Create

5

Bulk Edit

6

Read Optimized

11

 

Vejam aqui um pequeno troço que como utilizar esta função:

function alertFormType()

{

    var formType = Xrm.Page.ui.getFormType();

    alert(formType);

}

 

Fonte: http://msdn.microsoft.com/en-us/library/gg327828.aspx#BKMK_getFormType~

Até a próxima.

Tags: , ,

CRM 2013 - Obter o modo ou razão de um registo gravado

by Pedro Azevedo 17. November 2013 09:27

 

Boas pessoal,

Na sequência dos posts de manusear o AutoSave achei pertinente mostrar que como é que no CRM 2013 se obtêm o modo ou a razão que um registo é gravado, através da função getSaveMode, eis os valores que a função retorna:

 

Entity

Event Mode

Value

All

Guardar

1

All

Guardar e Fechar

2

All

Guardar e Novo

59

All

AutoSave

70

Activities

Guardar como Concluída

58

All

Ativar

5

All

Reativar

6

User or Team owned entities

Atribuir

47

Email (E-mail)

Enviar

7

Lead

Qualificar

16

Lead

Desqualificar

15

 

O problema é que esta lista é muito diminuta comparada com a que existia no CRM 2011, podemos comparar no seguinte artigo. Apanhar quando por exemplo uma Proposta era ganha ou perdida era muito usado, pois permitia agirmos mediante um destes eventos. No CRM 2013 não é lançado nenhum evento de guardar.

Para experimentarem coloquem o seguinte código no onsave do formulário:

function onclosequote(context)
{
   var saveMode = context.getEventArgs().getSaveMode();
   alert(saveMode);
}

E não se esqueçam de dizer para passar no primeiro parâmetro o contexto para que o método consiga executar.

Neste momento e dependendo do que queremos fazer podemos contornar, tenho contornado este problema utilizando plugins. Ou no onload do evento por exemplo da encomenda, onde verifico qual o tipo de formulário e se tem alguma proposta associada.

Até a próxima.

Tags: , ,

CRM 2013 - Gerir funcionalidade AutoSave

by Pedro Azevedo 13. November 2013 07:32

Boas Pessoal,

Como prometido aqui estou eu a falar sobre como podemos gerir o Autosave, como é que o podemos ativar ou desativar e como vamos lidar com ele via código.

Como eu apresentei esta nova funcionalidade parece que parecia que era inevitável utilizar, mas esta decisão terá que ser realizada com muita ponderação. Já que como referi no último post vai passar pelos eventos (OnSave, Plugins e Workflows) de um guardar “normal”, ou seja, de 30 em 30 segundos poderá executar estes eventos. Por essa razão deveremos ponderar quando utilizar e sensibilizar todos os utilizadores desta nova funcionalidade.

Vamos então ver onde podemos ativar ou desativar esta funcionalidade. Se formos a barra de navegação e escolhermos Definições -> Administração. Temos a opção Definições de Sistema e aqui podemos ver esta opção:

De realçar que este parâmetro apenas vai fazer efeito sobre os 30 segundos, ou seja, o formulário vai deixar de gravar de 30 em 30 segundos. Mas ele vai gravar sempre automaticamente quando fecharmos uma tab, se utilizarmos a barra navegação ou utilizarmos os botões de back e foward do browser.

Infelizmente esta funcionalidade só pode ser configurada ao nível da organização e não a cada entidade. O que podia fazer sentido, porque mediante a entidade e a complexidade dos eventos que ocorrem esta funcionalidade poderia ser útil.

Uma maneira de lidarmos com o problema anterior é deixarmos este setting ativo e controlarmos o autosave através de código em cada entidade.

A maneira de controlarmos através de código é no evento onsave colocarmos o seguinte código:

function stopAutoSave(contexto) {

    var eArgs = contexto.getEventArgs();

    if (eArgs.getSaveMode() == 70) { //AutoSave

        eArgs.preventDefault();

    }

    else if (eArgs.getSaveMode() == 1) { //Guardar

        alert(‘Guardar e Fechar’);

    }

    else if (eArgs.getSaveMode() == 2) { //Guardar e Fechar

        alert(‘Guardar e Fechar’);

    }

    else if (eArgs.getSaveMode() == 59) { //Guardar e Novo

        alert(‘Guardar e Fechar’);

    }

}

Para mais informação sobre os valores do método getSaveMode estará neste post.

O código que referi é genérico para qualquer entidade, por isso basta fazermos upload como Recurso Web:

 

Depois em qualquer entidade poderemos adicionar este Recurso Web e depois é chamar o método stopAutoSave no OnSave do formulário:

 

Quando se adiciona o método não esquecer de marcar a opção para passar o conecto no primeiro parâmetro, e que se vocês repararem já estamos a espera de o receber. Finalmente não esquecer de publicar estas alterações.

 

 

Até a próxima

Tags: , , ,

Estamos preparados para migrar para o CRM 2013 (Orion)?

by Pedro Azevedo 23. October 2013 05:49

Boas Pessoal,

O CRM 2011 permitiu que continuássemos a usar o código CRM 4.0 pelo menos até ao UR12. Com a vinda do CRM 2013 esta compatibilidade foi completamente quebrada e para fazer upgrade para o CRM 2013 primeiro teremos que migrar para o CRM 2011 e depois para o CRM 2013 pelo menos é o aconselhável.

Para quem tem o código todo 100% compatível com o CRM 2011 incluindo os seus endpoints pouco ou nada terá que fazer. Mas para não garantir realize estes passos:

1 - A primeira coisa que deve fazer é se modo HTC está aplicado, se tiver desligue e teste novamente, caso corra tudo bem então o seu código tem boas probabilidades de funcionar no CRM 2013.

2 - Correr a ferramenta Custom Code Validation Tool for Microsoft Dynamics CRM 2013 que valida os web resources que fundamentalmente vai verificar se ainda estamos a usar a API do CRM 4.0 ou algum endpoint dessa versão.

3 - Para o código server-side (.Net) e se mais uma vez estamos a usar os WebServices do CRM 2011 também podemos estar descansados, já que não será necessário fazer mais nada. Mesmo assim existe uma ferramenta chamada Legacy Feature Check Tool que permite validar este mesmo código, vejam o readme que explica como executar.

Em futuros posts vou falar melhor sobre o CRM 2013 e todas as suas funcionalidades.

Até a próxima.

Tags: , , ,

Obter dados de um SubGrid

by Pedro Azevedo 8. August 2013 01:12

Boas pessoal,

Tinha um requisito que era calcular o valor médio de uma lista. Neste caso vou demonstrar através da lista de oportunidades em que o objectivo é calcular a média do valor dos produtos inseridos.

Bom a minha primeira solução foi obter os registos directamente a grid, já que lá estavam os dados, assim fiz então baseei-me em alguns blogs ora vejam:

http://lakshmanindian.wordpress.com/2012/05/25/retrieve-subgrid-rows-in-crm-2011-using-jscript/

http://stackoverflow.com/questions/17651752/how-to-retrieve-all-record-ids-of-a-sub-grid-in-crm-2011-using-javascript

http://crmbusiness.wordpress.com/2011/05/19/crm-2011-javascript-and-subgrids-code-example/

Mas sempre tive problemas:

  • ·         Com o ir buscar a grid, com o código pré e após UR12
  • ·         Como ir buscar todos os elementos da grid, pois alguns dos métodos só vão buscar os registos que estão visíveis.
  • ·         Ter os campos necessários na lista

Então passei a enveredar por outra solução que é realizar uma query odata para obter todos os registos e apenas os campos que necessito. Tenho um overhead de obter todos os dados novamente, mas tenho uma solução que sei que vai perdurar no tempo pois qualquer mudança na forma de obter os registos de uma subgrid vou ficar imune.

Independentemente da forma como vou buscar os produtos da oportunidade terei que adicionar um Recurso Web em que neste caso terá apenas uma caixa de texto. Aqui podia não ter optado por um Recurso Web e ter colocado num campo que criasse como atributo da oportunidade. O Recurso Web permite ter campos dinâmicos, no requisito que referi tinha que realizar várias análises de qual o produto com maior valor, qual o produto com mais unidades, qual o produto que demorava mais a instalar. Por essa razão e porque o vosso caso pode ser semelhante vou manter a solução de um Recurso Web. Podem ver neste artigo um pouco mais sobre Recurso Web.

Vamos então adicionar um Recurso Web a nossa solução:

E colocar o seguinte código:

<HTML><HEAD><BASE>
<META charset=utf-8></HEAD>
<BODY contentEditable=true><INPUT id=quantity name=quantity>

<SCRIPT type=text/javascript src="ret_jquery"></SCRIPT>
<SCRIPT src="ClientGlobalContext.js.aspx"></SCRIPT>
<SCRIPT type=text/javascript>
  $(document).ready(function(){
    var entityId = window.parent.Xrm.Page.data.entity.getId();
    var serverUrl = window.location.protocol + "//" + window.location.host + "/" + Xrm.Page.context.getOrgUniqueName();
    var ODATA_ENDPOINT = "/XRMServices/2011/OrganizationData.svc";

    var odataSetName = "OpportunitySet";
    var relName = "/product_opportunities";

    var odataSelect = serverUrl + ODATA_ENDPOINT + "/" + odataSetName + "(guid'" + entityId.substr(1,36) + "')" + relName;

    return $.ajax({
       type: "GET",
       contentType: "application/json; charset=utf-8",
       datatype: "json",
       url: odataSelect,
       beforeSend: function (XMLHttpRequest) { XMLHttpRequest.setRequestHeader("Accept", "application/json"); },
       success: function (data, textStatus, XmlHttpRequest){
              RetrieveMultipleEntities(data.d.results);
       },
       error: function (XmlHttpRequest, textStatus, errorThrown) { alert('TextStatus:' + textStatus + "\nerror:" + errorThrown); }
    });
  });

  function RetrieveMultipleEntities(ManyEntities)
  {
    var total = 0;
    for( i=0; i< ManyEntities.length; i++){
      var Entity = ManyEntities[i];
      total = total + parseFloat(Entity.BaseAmount.Value)
    }
    $('#quantity').val(total/ManyEntities.length);
  }
</SCRIPT>

<P><FONT size=2 face=Tahoma></FONT> </P></BODY></HTML>

E o nosso desenvolvimento acabou, basta adicionarmos este Recurso Web dentro do formulário (que vamos fazer mais tarde) e este código funciona as mil maravilhas. Vamos explorar alguns pormenores que apesar de já estar explicado em outros artigos que escrevi, nunca é demais referir:

·         Este código pressupõe termos adicionado um Recurso Web com a biblioteca jQuery, vejam aqui como referencio:

<SCRIPT type=text/javascript src="ret_jquery"></SCRIPT>

·         A referência a biblioteca para podermos aceder ao contexto da entidade:

<SCRIPT src="ClientGlobalContext.js.aspx"></SCRIPT>

·         A construção do URL para obter o endpoint OData:

var serverUrl = window.location.protocol + "//" + window.location.host + "/" + Xrm.Page.context.getOrgUniqueName();
var ODATA_ENDPOINT = "/XRMServices/2011/OrganizationData.svc";

Vejam a utilização do Xrm.Page.context para obter o contexto, que sem a referenciação a biblioteca anterior não funcionava. O resultado da variável deve ficar algo do género:

http://<server>/<organization>/XRMServices/2011/OrganizationData.svc

·         Por último a obtenção do GUID do registo onde estamos:

var entityId = window.parent.Xrm.Page.data.entity.getId();

Vejam como eu referencio o Xrm.Page para poder aceder por exemplo aos campos do formulário.

Se tivéssemos optado por não utilizar um Recurso Web o código era semelhante, não teríamos que referenciar as duas bibliotecas Javascript (apesar de termos que incluir a biblioteca jQuery na solução) e na obtenção do id do registo podemos retirar a parte do window.parent.

Como tinha referenciado agora basta adicionarmos o Recurso Web ao formulário: 

 

Vejam o resultado final:

 

Até a próxima.

Tags: , , , ,

Customizar caixa de pesquisa de um lookup

by Pedro Azevedo 29. April 2013 00:49

Boas pessoal,

Uma das perguntas mais realizadas na comunidade é como modificar a janela de pesquisa de um lookup. Vamos ver as várias maneiras de customizar esta janela.

Por exemplo na entidade contacto temos um lookup para dizer qual o cliente que está afecto, podemos escolher entre a entidade cliente e contacto:

A entidade por defeito é o cliente e se quisermos que a entidade por defeito seja a entidade contacto, então podemos fazer isto:

document.getElementById("parentcustomerid").setAttribute("defaulttype", "2");

E o resultado é este:


Se quisermos dizer que a entidade contacto não faz sentido neste caso então podemos limitar a escolha a apenas a entidade cliente:

document.getElementById("parentcustomerid").setAttribute("lookuptypes", "1");

Vejam o resultado:

Agora imaginem que em vez de restringir quero colocar outra entidade, por exemplo o cliente potencial:

document.getElementById("parentcustomerid").setAttribute("lookuptypes", "1, 2, 4");

document.getElementById("parentcustomerid").setAttribute("lookuptypenames", "account:1,contact:2,lead:4");

document.getElementById("parentcustomerid").setAttribute("lookuptypeIcons", "/_imgs/ico_16_2.gif:/_imgs/ico_16_4.gif:/_imgs/ico_16_8.gif");

 

Neste caso estamos a dizer quais as entidades, os nomes das entidades e os respectivos icons, vejam o resultado:

 

Agora quero desabilitar a escolha de uma vista:

document.getElementById("parentcustomerid").setAttribute("disableViewPicker", "1");

Vejam o resultado:

Agora quero mudar qual a vista por defeito:

Xrm.Page.getControl("parentcustomerid").setDefaultView("00000000-0000-0000-00AA-000010001033");

Vejam o resultado:

 Todo este código pode ser colocado no evento onload do formulário.

 

Algumas perguntas poderão estar a ser feitas:

·         Que código são estes 1, 2 e 4?

São os códigos das entidades, eu neste post criei apenas para referência rápida sobre estes códigos.

·         Como posso obter o guid de uma vista?

Temos algumas hipóteses, abrir a vista para editar e copiar o id do URL. Estando a ver os registos de uma vista podemos carregar em “Copiar uma Hiperligação” e escolher opção “da Vista Actual”

Ainda existe outro método o addCustomView que está explicado neste post.

 

Espero que estas dicas rápidas sejam úteis.

 

Até a próxima

Tags: , , , ,

Como modificar o filtro de um lookup no Dynamics CRM 2011

by Pedro Azevedo 11. February 2013 10:50

Boas pessoal,

Foi-me reportado um problema na escolha dos produtos no pipeline de vendas, aquando da escolha do produto estavam aparecer produtos inativos, ou seja, listava todos os produtos sem filtro. Bem este será o mote para apresentar várias soluções que segui para poder resolver este problema.

 

Aparentemente este problema seria de fácil resolução, primeira tentativa modificar a vista que esta associado por defeito ao lookup. Para acedermos a esta configuração, vamos a customização do formulário e duplo click no lookup e aparece a caixa em baixo. Mas esta (como podem verificar) encontrava-se bloqueada, vejam na imagem:

 

A segunda opção foi modificar esta vista e colocar mais um critério que seria o produto estar activo. Para isso teremos que ir a entidade produto e escolher a vista, infelizmente não é possível editar os critérios de pesquisa, veja na imagem:

Mais uma vez a solução não poderá ser aplicada. Tinha na manga uma última solução, para isso teria que recorrer ao Javascript.

O objectivo será adicionarmos uma nova vista por intermédio do Javascript com os critérios que queremos e depois colocar essa vista como defeito, já que não conseguimos seleccionar a vista.

 

O primeiro passo é criar este filtro no Advanced Find:

Quando abrimos o XML temos este aspecto: 

<fetch version="1.0" output-format="xml-platform" mapping="logical" distinct="false">
  <entity name="product">
    <attribute name="name" />
    <attribute name="productnumber" />
    <attribute name="ret_codigofinanceiro" />
    <attribute name="productid" />
    <order attribute="name" descending="false" />
    <filter type="and">
      <condition attribute="statecode" operator="eq" value="0" />
    </filter>
  </entity>
</fetch>

 

De seguida temos que definir qual o layout da nossa vista:

<grid name='resultset' object='1' jump='name' select='1' icon='1' preview='1'>
  <row name='result' id='productid'>
    <cell name='name' width='300' />
    <cell name='productnumber' width='100' />
    <cell name='ret_codigofinanceiro' width='100' />
  </row>
</grid>

 

Agora teremos que aplicar dois métodos que nos vai possibilitar inserir a vista e coloca-la como a vista por defeito.

Para adicionar uma nova vista utilizamos a função addCustomView que tem a seguinte assinatura:

controlObj.addCustomView(viewId, entityName, viewDisplayName, fetchXml, layoutXml, isDefault)

 

Vejam aqui mais detalhes sobre esta função.

Para colocar esta vista como a vista por defeito teremos que utilizar a função setDefaultView (já agora para podermos obter a vista por defeito temos a função getDefaultView) com a seguinte assinatura:

controlObj.setDefaultView(viewGuid)

 

Vejam aqui mais detalhes.

Vejam a seguir todo o código:

NOTA: Quando vamos colocar o FetchXML em código Javascript devemos substituir as aspas por plicas:

 

var fetchXml = "<fetch version='1.0' output-format='xml-platform' mapping='logical' distinct='false'>" +
                        "<entity name='product'>" +
                            "<attribute name='name' />" +
                            "<attribute name='productnumber' />" +
                            "<attribute name='ret_codigofinanceiro' />" +
                            "<attribute name='productid' /> " +
                            "<order attribute='name' descending='false' />" +
                            "<filter type='and'>" +
                                "<condition attribute='statecode' operator='eq' value='0' />" +
                            "</filter>" +
                        "</entity>" +
                   "</fetch>";
 
var layoutXml = "<grid name='resultset' object='1' jump='name' select='1' icon='1' preview='1'>" +
                        "<row name='result' id='productid'>" +
                            "<cell name='name' width='300' />" +
                            "<cell name='productnumber' width='100' />" +
                            "<cell name='ret_codigofinanceiro' width='100' />" +
                        "</row>" +
                    "</grid>";
 
var viewId = "{1DFB2B35-B07C-44D1-868D-258DEEAB88E2}";
var entityName = "product";
var viewDisplayName = "Produtos Activos";
 
Xrm.Page.getControl("productid").addCustomView(viewId, entityName, viewDisplayName, fetchXml, layoutXml, true);
Xrm.Page.getControl("productid").setDefaultView(viewId);

 

NOTA: Esta abordagem pode ser reutilizada em outras situações, imaginem que podem por exemplo filtrar um lookup com base noutro valor do formulário. Para isso bastava por exemplo no FetchXml substituir o 0 por uma expressão válida.

 

Até aqui tudo bem o problema é que a história não terminou por aqui, como no lookup a seleção da vista está desativa o código acima não vai fazer nada, porque o controlo está disabled. A solução passa por habilitar este campo momentaneamente e executar o nosso código e coloca-la novamente no seu estado anterior.

Para isso recorri ao jQuery para realizar esta operação. Não se esqueçam que têm que adicionar a biblioteca do jQuery como WebResource e depois referenciá-la no formulário, há tempos escrevi um post de como adicionar esta e outras bibliotecas, vejam aqui.

Complementem o código anterior com isto:

 

//…
//o mesmo código
 
$("#productid").attr("disableViewPicker", "0");
Xrm.Page.getControl("productid").addCustomView(viewId, entityName, viewDisplayName, fetchXml, layoutXml, true);
Xrm.Page.getControl("productid").setDefaultView(viewId);
$("#productid").attr("disableViewPicker", "1");

 

Para finalizar pegamos no código completo e colocámos o mesmo dentro de uma função de depois dentro de um WebResource. Nas propriedades do formulário (que contém o lookup) acrescentamos este WebResource e depois adicionamos um novo processamento no evento OnLoad do formulário para executar o método.

Até a próxima.

Tags: , , ,

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