Alterar o owership de uma entidade após a criação

by Pedro Azevedo 13. January 2015 13:15

Boas pessoal,

Recentemente fui confrontado com uma situação de alterar o ownership de uma entidade já criada de organização para utilizador. Bom se estou com trabalho a escrever um post devem imaginar que não é tão simples como ir a entidade e mudar esta propriedades, pois não esta é uma daquelas propriedades que depois da entidade criada já não dá para editar, bom a primeira solução poderia passar em apagar a entidade criada e criar outra já com esta propriedade correta o problema é que já tinha muitas customizações, bem como alguns formulários, por isso teria que seguir outro caminho.

Então fui explorar se conseguia modificar o XML da solução e assim fiz. Inspirado por um post recente do Hosk que explica muito bem esta problemática, ele também propõe uma solução que seria criar uma nova entidade com propriedade utilizador\equipa e transpor todas as customizações já efetudas na entidade de organização para esta nova entidade. O meu objetivo será mostrar quais as diferenças no ficheiro customizations.xml entre uma entidade em que a propriedade seja organização e outra utilizador\equipa.

Primeiro é necessário uma solução que contenha a entidade de propriedade Organização e que queremos transformar numa entidade de propriedade Utilizador\Equipa. Exportamos a solução e depois de descompactar abrimos o ficheiro customizations.xml e façam as seguintes alterações:

Primeiro vamos identificar onde está declarado que a entidade está com propriedade, Organização e está neste atributo:

<OwnershipTypeMask>OrgOwned</OwnershipTypeMask>

Vamos substituir o valor por UserOwned ficando o atributo com este aspeto:

<OwnershipTypeMask>UserOwned</OwnershipTypeMask>

Agora necessitamos de procurar os campos que terão que existir para suportar esta alteração, no xml e dentro da entidade que está com a propriedade Organização existe um nó com esta estrutura:

<attribute PhysicalName="OrganizationId">
  <Type>lookup</Type>
  <Name>organizationid</Name>
  <LogicalName>organizationid</LogicalName>
  <RequiredLevel>none</RequiredLevel>
  <ImeMode>auto</ImeMode>
  <ValidForReadApi>1</ValidForReadApi>
  <IsCustomField>0</IsCustomField>
  <IsAuditEnabled>1</IsAuditEnabled>
  <IsSecured>0</IsSecured>
  <IntroducedVersion>1.0.0.0</IntroducedVersion>
  <IsCustomizable>1</IsCustomizable>
  <IsRenameable>1</IsRenameable>
  <CanModifySearchSettings>1</CanModifySearchSettings>
  <CanModifyRequirementLevelSettings>1</CanModifyRequirementLevelSettings>
  <CanModifyAdditionalSettings>1</CanModifyAdditionalSettings>
  <SourceType>0</SourceType>
  <ReferencedEntityObjectTypeCode>1019</ReferencedEntityObjectTypeCode>
  <LookupTypes />
  <displaynames>
    <displayname description="ID da Organização" languagecode="2070" />
  </displaynames>
  <Descriptions>
    <Description description="Identificador exclusivo para a organização" languagecode="2070" />
    <Description description="Identificador exclusivo da organização" languagecode="1046" />
  </Descriptions>
</attribute>

Devem substituir este atributo pelos 5 atributos a seguir para que seja possível colocar um utilizador ou equipa como proprietário de um registo desta entidade:

<attribute PhysicalName="OwnerId">
  <Type>owner</Type>
  <Name>ownerid</Name>
  <LogicalName>ownerid</LogicalName>
  <RequiredLevel>systemrequired</RequiredLevel>
  <DisplayMask>ValidForAdvancedFind|ValidForForm|ValidForGrid|RequiredForForm</DisplayMask>
  <ImeMode>auto</ImeMode>
  <ValidForUpdateApi>1</ValidForUpdateApi>
  <ValidForReadApi>1</ValidForReadApi>
  <ValidForCreateApi>1</ValidForCreateApi>
  <IsCustomField>0</IsCustomField>
  <IsAuditEnabled>1</IsAuditEnabled>
  <IsSecured>0</IsSecured>
  <IntroducedVersion>1.0.0.0</IntroducedVersion>
  <IsCustomizable>1</IsCustomizable>
  <IsRenameable>1</IsRenameable>
  <CanModifySearchSettings>1</CanModifySearchSettings>
  <CanModifyRequirementLevelSettings>1</CanModifyRequirementLevelSettings>
  <CanModifyAdditionalSettings>1</CanModifyAdditionalSettings>
  <SourceType>0</SourceType>
  <LookupStyle>single</LookupStyle>
  <LookupTypes>
    <LookupType id="00000000-0000-0000-0000-000000000000">8</LookupType>
    <LookupType id="00000000-0000-0000-0000-000000000000">9</LookupType>
  </LookupTypes>
  <displaynames>
    <displayname description="Proprietário" languagecode="2070" />
  </displaynames>
  <Descriptions>
    <Description description="ID do Proprietário" languagecode="2070" />
  </Descriptions>
</attribute>
 
<attribute PhysicalName="OwnerIdName">
  <Type>nvarchar</Type>
  <Name>owneridname</Name>
  <LogicalName>owneridname</LogicalName>
  <RequiredLevel>systemrequired</RequiredLevel>
  <ImeMode>auto</ImeMode>
  <ValidForReadApi>1</ValidForReadApi>
  <IsCustomField>0</IsCustomField>
  <IsAuditEnabled>0</IsAuditEnabled>
  <IsLogical>1</IsLogical>
  <IsSecured>0</IsSecured>
  <IntroducedVersion>1.0.0.0</IntroducedVersion>
  <IsCustomizable>1</IsCustomizable>
  <IsRenameable>1</IsRenameable>
  <CanModifySearchSettings>1</CanModifySearchSettings>
  <CanModifyRequirementLevelSettings>1</CanModifyRequirementLevelSettings>
  <CanModifyAdditionalSettings>1</CanModifyAdditionalSettings>
  <SourceType>0</SourceType>
  <Format>text</Format>
  <MaxLength>100</MaxLength>
  <Length>320</Length>
  <Descriptions>
    <Description description="Nome do proprietário" languagecode="2070" />
  </Descriptions>
</attribute>
 
<attribute PhysicalName="OwnerIdType">
  <Type>int</Type>
  <Name>owneridtype</Name>
  <LogicalName>owneridtype</LogicalName>
  <RequiredLevel>systemrequired</RequiredLevel>
  <DisplayMask>ObjectTypeCode</DisplayMask>
  <ImeMode>disabled</ImeMode>
  <ValidForUpdateApi>1</ValidForUpdateApi>
  <ValidForReadApi>1</ValidForReadApi>
  <ValidForCreateApi>1</ValidForCreateApi>
  <IsCustomField>0</IsCustomField>
  <IsAuditEnabled>1</IsAuditEnabled>
  <IsSecured>0</IsSecured>
  <IntroducedVersion>1.0.0.0</IntroducedVersion>
  <IsCustomizable>1</IsCustomizable>
  <IsRenameable>1</IsRenameable>
  <CanModifySearchSettings>1</CanModifySearchSettings>
  <CanModifyRequirementLevelSettings>1</CanModifyRequirementLevelSettings>
  <CanModifyAdditionalSettings>1</CanModifyAdditionalSettings>
  <SourceType>0</SourceType>
  <Format></Format>
  <MinValue>-2147483648</MinValue>
  <MaxValue>2147483647</MaxValue>
  <Descriptions>
    <Description description="Tipo de ID de Proprietário" languagecode="1046" />
    <Description description="Tipo de ID do Proprietário" languagecode="2070" />
  </Descriptions>
</attribute>
 
<attribute PhysicalName="OwnerIdYomiName">
  <Type>nvarchar</Type>
  <Name>owneridyominame</Name>
  <LogicalName>owneridyominame</LogicalName>
  <RequiredLevel>systemrequired</RequiredLevel>
  <ImeMode>auto</ImeMode>
  <ValidForReadApi>1</ValidForReadApi>
  <IsCustomField>0</IsCustomField>
  <IsAuditEnabled>0</IsAuditEnabled>
  <IsLogical>1</IsLogical>
  <IsSecured>0</IsSecured>
  <IntroducedVersion>1.0.0.0</IntroducedVersion>
  <IsCustomizable>1</IsCustomizable>
  <IsRenameable>1</IsRenameable>
  <CanModifySearchSettings>1</CanModifySearchSettings>
  <CanModifyRequirementLevelSettings>1</CanModifyRequirementLevelSettings>
  <CanModifyAdditionalSettings>1</CanModifyAdditionalSettings>
  <SourceType>0</SourceType>
  <Format>text</Format>
  <MaxLength>100</MaxLength>
  <Length>320</Length>
  <YomiOf>OwnerIdName</YomiOf>
  <Descriptions>
    <Description description="Nome Yomi do proprietário" languagecode="1046" />
    <Description description="Nome yomi do proprietário" languagecode="2070" />
  </Descriptions>
</attribute>
 
<attribute PhysicalName="OwningBusinessUnit">
  <Type>lookup</Type>
  <Name>owningbusinessunit</Name>
  <LogicalName>owningbusinessunit</LogicalName>
  <RequiredLevel>none</RequiredLevel>
  <ImeMode>auto</ImeMode>
  <ValidForReadApi>1</ValidForReadApi>
  <IsCustomField>0</IsCustomField>
  <IsAuditEnabled>0</IsAuditEnabled>
  <IsSecured>0</IsSecured>
  <IntroducedVersion>1.0.0.0</IntroducedVersion>
  <IsCustomizable>1</IsCustomizable>
  <IsRenameable>1</IsRenameable>
  <CanModifySearchSettings>1</CanModifySearchSettings>
  <CanModifyRequirementLevelSettings>1</CanModifyRequirementLevelSettings>
  <CanModifyAdditionalSettings>1</CanModifyAdditionalSettings>
  <SourceType>0</SourceType>
  <ReferencedEntityObjectTypeCode>10</ReferencedEntityObjectTypeCode>
  <LookupStyle>single</LookupStyle>
  <LookupTypes />
  <displaynames>
    <displayname description="Unidade de Negócio Proprietária" languagecode="2070" />
    <displayname description="Unidade de Negócios Proprietária" languagecode="1046" />
  </displaynames>
  <Descriptions>
    <Description description="Identificador exclusivo da unidade de negócios proprietária do registro" languagecode="1046" />
    <Description description="Identificador rtwexclusivo para a unidade de negócio proprietária do registo" languagecode="2070" />
  </Descriptions>
</attribute>

Criados os campos agora necessitamos de criar as relações para as entidades respetivas, aqui temos que ter cuidado com determinados valores (está assinalado amarelo os valores que temos que ter em consideração), a primeira coisa a fazer é procurar a relação entre a nossa entidade e a entidade Organização que tem a seguinte estrutura:

<EntityRelationship Name="organization_new_entityorganization">
  <EntityRelationshipType>OneToMany</EntityRelationshipType>
  <IsCustomizable>1</IsCustomizable>
  <IntroducedVersion>1.0.0.0</IntroducedVersion>
  <IsHierarchical>0</IsHierarchical>
  <ReferencingEntityName>new_entityorganization</ReferencingEntityName>
  <ReferencedEntityName>Organization</ReferencedEntityName>
  <CascadeAssign>NoCascade</CascadeAssign>
  <CascadeDelete>NoCascade</CascadeDelete>
  <CascadeReparent>NoCascade</CascadeReparent>
  <CascadeShare>NoCascade</CascadeShare>
  <CascadeUnshare>NoCascade</CascadeUnshare>
  <ReferencingAttributeName>OrganizationId</ReferencingAttributeName>
  <RelationshipDescription>
    <Descriptions>
      <Description description="Identificador exclusivo para a organização" languagecode="2070" />
      <Description description="Identificador exclusivo da organização" languagecode="1046" />
    </Descriptions>
  </RelationshipDescription>
  <field name="organizationid" requiredlevel="none" imemode="auto" format="">
    <IsCustomizable>1</IsCustomizable>
    <IsRenameable>1</IsRenameable>
    <CanModifySearchSettings>1</CanModifySearchSettings>
    <CanModifyRequirementLevelSettings>1</CanModifyRequirementLevelSettings>
    <IsSecured>0</IsSecured>
    <IsAuditEnabled>1</IsAuditEnabled>
    <displaynames>
      <displayname description="ID da Organização" languagecode="2070" />
    </displaynames>
  </field>
</EntityRelationship>

Temos que substituir a relação acima com as seguintes relações, atenção que novamente amarelo estão valores que terão que modificar mediante a vossa realidade:

<EntityRelationship Name="owner_new_entityorganization">
  <EntityRelationshipType>OneToMany</EntityRelationshipType>
  <IsCustomizable>1</IsCustomizable>
  <IntroducedVersion>1.0.0.0</IntroducedVersion>
  <IsHierarchical>0</IsHierarchical>
  <ReferencingEntityName>new_entityorganization</ReferencingEntityName>
  <ReferencedEntityName>Owner</ReferencedEntityName>
  <CascadeAssign>NoCascade</CascadeAssign>
  <CascadeDelete>NoCascade</CascadeDelete>
  <CascadeReparent>NoCascade</CascadeReparent>
  <CascadeShare>NoCascade</CascadeShare>
  <CascadeUnshare>NoCascade</CascadeUnshare>
  <ReferencingAttributeName>OwnerId</ReferencingAttributeName>
  <RelationshipDescription>
    <Descriptions>
      <Description description="ID do Proprietário" languagecode="2070" />
    </Descriptions>
  </RelationshipDescription>
  <field name="ownerid" requiredlevel="systemrequired" imemode="auto" lookupstyle="single" lookupbrowse="0" format="" lookuptypes="8, 9">
    <IsCustomizable>1</IsCustomizable>
    <IsRenameable>1</IsRenameable>
    <CanModifySearchSettings>1</CanModifySearchSettings>
    <CanModifyRequirementLevelSettings>1</CanModifyRequirementLevelSettings>
    <IsSecured>0</IsSecured>
    <DisplayMask>ValidForAdvancedFind|ValidForForm|ValidForGrid|RequiredForForm</DisplayMask>
    <IsAuditEnabled>1</IsAuditEnabled>
    <displaynames>
      <displayname description="Proprietário" languagecode="2070" />
    </displaynames>
  </field>
</EntityRelationship>
 
<EntityRelationship Name="team_new_entityorganization">
  <EntityRelationshipType>OneToMany</EntityRelationshipType>
  <IsCustomizable>1</IsCustomizable>
  <IntroducedVersion>1.0.0.0</IntroducedVersion>
  <IsHierarchical>0</IsHierarchical>
  <ReferencingEntityName>new_entityorganization</ReferencingEntityName>
  <ReferencedEntityName>Team</ReferencedEntityName>
  <CascadeAssign>NoCascade</CascadeAssign>
  <CascadeDelete>NoCascade</CascadeDelete>
  <CascadeReparent>NoCascade</CascadeReparent>
  <CascadeShare>NoCascade</CascadeShare>
  <CascadeUnshare>NoCascade</CascadeUnshare>
  <ReferencingAttributeName>OwningTeam</ReferencingAttributeName>
  <RelationshipDescription>
    <Descriptions>
      <Description description="Identificador exclusivo para a equipa proprietária do registo." languagecode="2070" />
      <Description description="Identificador exclusivo da equipe que possui o registro." languagecode="1046" />
    </Descriptions>
  </RelationshipDescription>
  <field name="owningteam" requiredlevel="none" imemode="auto" lookupstyle="single" lookupbrowse="0" format="">
    <IsCustomizable>1</IsCustomizable>
    <IsRenameable>1</IsRenameable>
    <CanModifySearchSettings>1</CanModifySearchSettings>
    <CanModifyRequirementLevelSettings>1</CanModifyRequirementLevelSettings>
    <IsSecured>0</IsSecured>
    <IsAuditEnabled>0</IsAuditEnabled>
    <displaynames>
      <displayname description="Equipa Proprietária" languagecode="2070" />
      <displayname description="Equipe Proprietária" languagecode="1046" />
    </displaynames>
  </field>
</EntityRelationship>
 
<EntityRelationship Name="user_new_entityorganization">
  <EntityRelationshipType>OneToMany</EntityRelationshipType>
  <IsCustomizable>1</IsCustomizable>
  <IntroducedVersion>1.0.0.0</IntroducedVersion>
  <IsHierarchical>0</IsHierarchical>
  <ReferencingEntityName>new_entityorganization</ReferencingEntityName>
  <ReferencedEntityName>SystemUser</ReferencedEntityName>
  <CascadeAssign>NoCascade</CascadeAssign>
  <CascadeDelete>NoCascade</CascadeDelete>
  <CascadeReparent>NoCascade</CascadeReparent>
  <CascadeShare>NoCascade</CascadeShare>
  <CascadeUnshare>NoCascade</CascadeUnshare>
  <ReferencingAttributeName>OwningUser</ReferencingAttributeName>
  <RelationshipDescription>
    <Descriptions>
      <Description description="Identificador exclusivo para o utilizador proprietário do registo." languagecode="2070" />
      <Description description="Identificador exclusivo do usuário proprietário do registro." languagecode="1046" />
    </Descriptions>
  </RelationshipDescription>
  <field name="owninguser" requiredlevel="none" imemode="auto" lookupstyle="single" lookupbrowse="0" format="">
    <IsCustomizable>1</IsCustomizable>
    <IsRenameable>1</IsRenameable>
    <CanModifySearchSettings>1</CanModifySearchSettings>
    <CanModifyRequirementLevelSettings>1</CanModifyRequirementLevelSettings>
    <IsSecured>0</IsSecured>
    <IsAuditEnabled>0</IsAuditEnabled>
    <displaynames>
      <displayname description="Utilizador Proprietário" languagecode="2070" />
      <displayname description="Usuário Proprietário" languagecode="1046" />
    </displaynames>
  </field>
</EntityRelationship>

Estas são as alterações que tem que fazer para mudar a propriedade de uma entidade.

Até a próxima.

Tags: , ,

Colocar uma subgrid disabled in CRM 2013

by Pedro Azevedo 23. August 2014 01:24

Boas pessoal,

Há algum tempo nos fóruns de discussão surgiu a dúvida de como colocar uma grid readonly criei um script na altura e foram surgindo várias dúvidas e eu fui complementando o script. Por isso estou aqui para explicar e partilhar o script. Este post será atualizado sempre que surgir problemas de não funcionar ou quando falhar numa atualização da Microsoft.

Este script não está no seu código mais atualizado mas espero que num futuro próximo espero melhorar muito. Por exemplo uma coisa que não gosto é misturar Javascript puro como JQuery.

Para começar e como primeira alternativa foi tentar usar o seguinte código:

document.getElementById(gridName).disabled = "true";

Mas este código não tem qualquer efeito na grid, pelo menos no CRM Online. Por isso o meu primeiro objetivo foi prevenir que o utilizador pudesse abrir um registo na grid, para isso apliquei o seguinte código:

disableSubgrid = function(gridName) {
	var subgridArea = document.getElementById(gridName + "_divDataArea");
	if (subgridArea != null) {
		var subgridTable = subgridArea.childNodes[0].childNodes[1];
                              
		for(j = 1; j < subgridTable.rows[i].cells.length; j++) { 
			if (subgridTable.rows[i].cells[j].childNodes[0].childNodes[0] != null && subgridTable.rows[i].cells[j].childNodes[0].childNodes[0].nodeName == "A") {
				subgridTable.rows[i].cells[j].childNodes[0].childNodes[0].outerHTML = subgridTable.rows[i].cells[j].childNodes[0].childNodes[0].innerText;
			}
		}
	}
}

Este código retirei desta thread num fórum de discussão mas este não funciona totalmente pois a primeira coluna ficava sem dados, a thread era especifica para a versão 2011. Para além disso não tratava do double-click.

Com base nesse código fiz a minha própria versão para que este comportamento não acontecesse e tratar do double-click:

disableSubgrid = function(gridName) {
	var subgridArea = document.getElementById(gridName + "_divDataArea");
	if (subgridArea != null) {
		var subgridTable = subgridArea.childNodes[0].childNodes[1];
                              
		for(i = 1; i < subgridTable.rows.length; i++) {
			subgridTable.rows[i].ondblclick=function(e){
				if (!e)
					e = window.event;

				if (e.stopPropagation) {
					e.stopPropagation();
				}
				else {
					e.cancelBubble = true;
				}
			};
			
			for(j = 1; j < subgridTable.rows[i].cells.length; j++) { 
				if (subgridTable.rows[i].cells[j].childNodes[0].childNodes[0] != null && subgridTable.rows[i].cells[j].childNodes[0].childNodes[0].nodeName == "A") {
					var node = subgridTable.rows[i].cells[j].childNodes[0].childNodes[0].childNodes[0];
					subgridTable.rows[i].cells[j].childNodes[0].removeChild(subgridTable.rows[i].cells[j].childNodes[0].childNodes[0]);
					subgridTable.rows[i].cells[j].childNodes[0].appendChild(node);
				}
			}
		}
	}
}

 

Este código funciona muito bem, só que tem um problema quando ordenamos ele voltava a recarregar a lista e tanto o link com o double-click passavam a funcionar:

Aqui a minha primeira opção era apanhar o reload da sub-grid mas não conseguir por isso a minha solução foi desativar a funcionalidade de ordenar, com este código em JQuery:

$("#" + gridName + " .ms-crm-List-Sortable").bind("click", function(event){
	event.stopPropagation();
});

Com este código aplicado ele não consegue ordenar logo fica tudo bem J não é a melhor opção mas por agora funciona. Agora só faltava desabilitar os botões de adicionar e de mostrar o detalhe dos registos, aqui também recorri ao JQuery:

$("#" + gridName + "_addImageButtonImage").bind("click", function(event){
	event.stopPropagation();
});

$("#" + gridName + "_openAssociatedGridViewImageButtonImage").bind("click", function(event){
	event.stopPropagation();
});

Et voilà tornamos a nossa subgrid totalmente disabled. Vejam o código completo usado, onde também tentei tornar o mais dinâmico possível para isso bastando passar o nome da sub-grid:

disableSubgrid = function(gridName) {
	var subgridArea = document.getElementById(gridName + "_divDataArea");
	if (subgridArea != null) {
		$("#" + gridName + " .ms-crm-List-Sortable").bind("click", function(event){
			event.stopPropagation();
		});
		
		$("#" + gridName + "_addImageButtonImage").bind("click", function(event){
			event.stopPropagation();
		});

		$("#" + gridName + "_openAssociatedGridViewImageButtonImage").bind("click", function(event){
			event.stopPropagation();
		});

		var subgridTable = subgridArea.childNodes[0].childNodes[1];
                              
		for(i = 1; i < subgridTable.rows.length; i++) {
			subgridTable.rows[i].ondblclick=function(e){
				if (!e)
					e = window.event;

				if (e.stopPropagation) {
					e.stopPropagation();
				}
				else {
					e.cancelBubble = true;
				}
			};
			
			for(j = 1; j < subgridTable.rows[i].cells.length; j++) { 
				if (subgridTable.rows[i].cells[j].childNodes[0].childNodes[0] != null && subgridTable.rows[i].cells[j].childNodes[0].childNodes[0].nodeName == "A") {
					var node = subgridTable.rows[i].cells[j].childNodes[0].childNodes[0].childNodes[0];
					subgridTable.rows[i].cells[j].childNodes[0].removeChild(subgridTable.rows[i].cells[j].childNodes[0].childNodes[0]);
					subgridTable.rows[i].cells[j].childNodes[0].appendChild(node);
				}
			}
		}
	}
	else {
		setTimeout("disableSubgrid('" + gridName + "');", 1500);
	}
}

function onload() {
	setTimeout("disableSubgrid('Contacts');", 1500);
	setTimeout("disableSubgrid('accountopportunitiesgrid');", 1500);
}

 

 

PS: Se tiverem problemas coloquem nos comentários ou coloquem a dúvida no fórum de discussão.

 

Até a próxima

Tags: , , , ,

CRM 2013 Javascript Object Model - Contexto

by Pedro Azevedo 28. May 2014 20:04

Boas pessoal,

Chegou a vez de falarmos sobre o Xrm.Page.context. Aqui temos que obter primeiro o objecto de contexto.

context.client.getClient()

Valores de retorno possíveis deste método:

Cliente

Valor

Browser

Web

Outlook

Outlook

Mobile

Mobile

function getClientTest() {

                Xrm.Utility.alertDialog(Xrm.Page.context.client.getClient());

}

De referir que este método veio tornar o método isOutlookClient obsoleto.

 

context.client.getClientState()

Valores de retorno possíveis deste método:

Cliente

Valor

Web, Outlook, Mobile

Online

Outlook, Mobile

Offline

function getClientStateTest() {

                Xrm.Utility.alertDialog(Xrm.Page.context.client.getClientState());

}

Tal como o método anterior este método veio tornar obsoleto o método isOutlookOnline.

 

context.getClientUrl()

Valores possíveis no retorno:

Cliente

Valor

Microsoft Dynamics CRM (on-premises)

http(s)://server/org

Microsoft Dynamics CRM Online

https://org.crm.dynamics.com

Microsoft Dynamics CRM for Outlook with Offline Access when offline

http://localhost:2525

Na documentação o método getServerURL não vem documentado mas ainda funciona apesar de estar obsoleto deve-se usar sempre o getClientUrl.

 

context.getCurrentTheme()

Este método serve essencialmente para podermos customizar os nossos Recursos Web mediante o theme do Outlook. Valores possíveis de retorno para este método:

Valor

Theme

default

Microsoft Dynamics CRM web application

Office12Blue

Microsoft Dynamics CRM for Outlook 2007 or 2010 Blue Theme

Office14Silver

Microsoft Dynamics CRM for Outlook 2007 or 2010 Silver or Black Theme

function getCurrentThemeTest() {

                Xrm.Utility.alertDialog(Xrm.Page.context.getCurrentTheme());

}

Devem ter atenção que alguns destes métodos não funcionam por exemplo nos tablets por isso os métodos acima passam a ser essenciais. Por exemplo este método em específico não funciona nos tablets.

 

context.getOrgLcid()

Esta função permite devolver a linguagem base da organização.

 

function getOrgLcidTest() {

                Xrm.Utility.alertDialog(Xrm.Page.context.getOrgLcid());

}

Este valor representa o Português de Portugal. Já agora o Português do Brazil é o 1046.

 

context.getOrgUniqueName()

function getOrgUniqueNameTest() {

                Xrm.Utility.alertDialog(Xrm.Page.context.getOrgUniqueName());

}

context.getQueryStringParameters()

Este método serve para ler os query strings passados ao formulário para além dos parâmetros que o CRM envia pode-se passar parâmetros customizados, por exemplo usando a função que já testamos a openEntityForm.

 

function getQueryStringParametersTest() {

                var params = Xrm.Page.context.getQueryStringParameters();

                for(var par in params) {

                               Xrm.Utility.alertDialog("QueryString:" + par + " com o valor " + params[par]);

                }

}

 

 

context.getUserId()
function getUserIdTest() {

                Xrm.Utility.alertDialog(Xrm.Page.context.getUserId());

}

 

context.getUserId() 
function getUserLcidTest() {

                Xrm.Utility.alertDialog(Xrm.Page.context.getUserLcid());

}

 

context.getUserName()
Esta função retorna o nome do utilizador.
function getUserNameTest() {

                    Xrm.Utility.alertDialog(Xrm.Page.context.getUserName());

}

 

context.getUserRoles()

function getUserRolesTest() {

                var roles = Xrm.Page.context.getUserRoles();

                for(var role in roles) {

                               Xrm.Utility.alertDialog("Role ID:" + role);

                }

}

context.prependOrgName(sPath)

 

function prependOrgNameTest() {

Xrm.Utility.alertDialog(getClientUrlTest() + Xrm.Page.context.prependOrgName("/XRMServices/2011/OrganizationData.svc/"));

}

 

Neste contexto ainda tenho a referir que o método getAuthenticationHeader foi retirado definitivamente, este tinha permanecido por causa de compatibilidades com o CRM 4.0.

 

Até a próxima

 

Tags: , , , , , , ,

CRM 2013 Javascript Object Model - Utility

by Pedro Azevedo 22. April 2014 01:22

Boas pessoal,

Como vos tinha prometido aqui vão exemplos de chamada a cada uma das funções Javascript que apresentei no post sobre o novo object modelo do CRM. Vou dividir em vários posts e vou dividir pelo mesmo critério que separei em tabelas:

Por isso vou começar pelo Xrm.Utility

Xrm.Utility.alertDialog(message,onCloseCallback)

function testAlertDialog(){
                Xrm.Utility.alertDialog("Mensagem a dizer que vou mudar o Nome da Empresa", function () {
                               Xrm.Page.getAttribute("companyname").setValue("Novo Nome");
                });
}

Por isso já sabem pessoal deixem de utilizar o alert e passem a usar esta função suportada.

Xrm.Utility.confirmDialog(message,yesCloseCallback,noCloseCallback)

function confirmDialogTest() {
                Xrm.Utility.confirmDialog("Quer mudar o Nome da Empresa?", function () {
                                               Xrm.Page.getAttribute("companyname").setValue("Mudei o nome da empresa");
                               },
                               function () {
                                               Xrm.Page.getAttribute("companyname").setValue("Não mudei o nome da empresa");
                               }
                );
}

 

Xrm.Utility.isActivityType(entityName)

function isActivityTypeTest() {
                Xrm.Utility.alertDialog("Cliente Potencial é uma entidade de actividade" + Xrm.Utility.isActivityType("lead"));
                Xrm.Utility.alertDialog("Email é uma entidade de actividade" + Xrm.Utility.isActivityType("email"));
}

Xrm.Utility.openEntityForm(name,id,parameters)

Esta função já merece uma descrição mais pormenorizada, para além do que o nome sugere que é a abertura de um formulário com um determinado registo, caso não coloquemos nada no parâmetro id ele abre um formulário de criação. No parâmetro parameters pode receber alguns parâmetros:

  • formid para poder especificar um formulário, quando existe mais que um;
  • parâmetros para passar valores por defeito quando existe um formulário de criação;
  • parâmetros query string quando um formulário está configurado para receber query string customizadas – não vou dar nenhum exemplo pois implica outras alterações, já está marcado para realizar um post mais tarde.
function openEntityFormTest() {
                Xrm.Utility.openEntityForm("account");
                Xrm.Utility.openEntityForm("account","D045613F-3CBF-E311-88C5-D89D67632EAC");

                var parameters = {};
                parameters["name"] = "este é o meu nome";
                parameters["telephone1"] = "(351) 123456789";
                Xrm.Utility.openEntityForm("account", null, parameters);
}

 

Xrm.Utility.openWebResource(webResourceName,webResourceData,width, height) e Xrm.Utility.openWebResourceDialog(webResourceName, webResourceData, width, height)

Nesta função apenas referir que se o Recurso Web receber dados teremos que o passar no segundo parâmetro.

function openWebResourceTest() {
                Xrm.Utility.openWebResource("new_controlhtml", null, 600,300);
}

 

Começamos com duas funções que utilizávamos muito os métodos de Javascript, ou seja, alert e confirm. Devemos usar estas novas funções pois assim vamos garantir que vão funcionar em todos os ambientes.

 

PS: Todos os testes foram realizados no evento onload da entidade Oportunidade Potencial.

 

Até a próxima.

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 - 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: , , ,

Referência Rápida - StatusCode vs StateCode

by Pedro Azevedo 17. August 2013 00:16

 

 

Boas pessoal,

Segue mais um post que vai servir de referência, ou seja, informação que eu procuro regularmente. Desta vez saber quais as razões de estado possíveis para um determinado estado:

 

Entidade

Estado (statecode)

Razão do Estado (statuscode)

Account

0 Active

1 Active

1 Inactive

2 Inactive

Activity

0 Open

1 Open

1 Completed

2 Completed

2 Canceled

3 Canceled

3 Scheduled

4 Scheduled

Appointment

0 Open

1 Free

2 Tentative

1 Completed

3 Completed

2 Canceled

4 Canceled

3 Scheduled

5 Busy

6 Out of Office

Article

1 Draft

1 Draft

2 Unapproved

2 Unapproved

3 Published

3 Published

Campaign

0 Active

0 Proposed

1 Ready To Launch

2 Launched

3 Completed

4 Canceled

5 Suspended

Campaign Activity

0 Open

0 In Progress

1 Proposed

4 Pending

5 System Aborted

6 Completed

1 Closed

2 Closed

2 Canceled

3 Canceled

Campaign Response

0 Open

1 Open

1 Closed

2 Closed

2 Canceled

3 Canceled

Case

0 Active

1 In Progress

2 On Hold

3 Waiting for Details

4 Researching

1 Resolved

5 Problem Solved

2 Canceled

6 Canceled

Case Resolution

0 Open

1 Open

1 Completed

2 Closed

2 Canceled

3 Canceled

Contact

0 Active

1 Active

1 Inactive

2 Inactive

Contract

0 Draft

1 Draft

1 Invoiced

2 Invoiced

2 Active

3 Active

3 On Hold

4 On Hold

4 Canceled

5 Canceled

5 Expired

6 Expired

Contract Line

0 Existing

1 New

1 Renewed

2 Renewed

2 Canceled

3 Canceled

3 Expired

4 Expired

Currency

0 Active

0 Active

1 Inactive

1 Inactive

Discount

0 Active

100001 Active

1 Inactive

100002 Inactive

E-mail

0 Open

1 Draft

8 Failed

1 Completed

2 Completed

3 Sent

4 Received

6 Pending Send

7 Sending

2 Canceled

5 Canceled

Esta tabela é útil principalmente quando estamos a mudar o estado e temos colocar uma razão de estado válida, senão estamos sujeitos apanhar este erro comum “State code is invalid or state code is valid but status code is invalid for a specified state code.

 

Até a próxima.

 

Tags: , , ,

Referência Rápida - Códigos de Erro

by Pedro Azevedo 7. June 2013 00:40

Boas pessoal,

Mais um post de referência rápida, desta vez para quando recebermos uma mensagem de erro com um código de erro possamos saber mais informação através da tabela abaixo.

O CRM pode-nos dar informações dos erros de duas maneiras:

  •        Decimal (por exemplo: -2147206371)
  •        Hexadecimal (com ou sem prefixo 0x, por exemplo 0x80043B1D)

     Nesse sentido é necessário passarmos o código de erro na forma de inteiro para Hexadecimal, para isso siga estes passos:

  1.        Abra a calculadora no modo científico ou programador
  2.        Escolha a opção Dec (Decimal)
  3.        Faça copiar e colar na calculadora

4.       Seleccione o Hex (Hexadecimal) para converter para hexadecimal, pois a tabela está neste tipo. Depois é retirar os FFFFFFFF e tem o código que pode pesquisar na tabela.

 

Tabela de referência para o CRM 2011:

ErrorId

ManagedErrorName

ErrorMessage

UnManagedErrorName

800401ff

SearchTextLenExceeded

Search Text Length Exceeded.

IDS_SEARCHTXTLENEXCEEDED

80040200

RequiredFieldMissing

Required field missing.

IDS_REQUIREDFIELDMISSING

80040201

InvalidXml

Invalid XML.

IDS_INVALID_XML

80040202

EmptyXml

Empty XML.

IDS_EMPTY_XML

80040203

InvalidArgument

Invalid argument.

IDS_INVALIDARG

80040204

InvalidUserAuth

User does not have the privilege to act on behalf another user.

IDS_INVALID_USERAUTH

80040205

InvalidParent

The parent object is invalid or missing.

IDS_INVALID_PARENT

80040206

InvalidParentId

The parent id is invalid or missing.

IDS_INVALID_PARENTID

80040207

unManagedidsinvaliduserid

The user id is invalid or missing.

IDS_INVALID_USERID

80040208

unManagedidsinvalidteamid

Invalid team id.

IDS_INVALID_TEAMID

80040209

unManagedidsinvalidbusinessid

Invalid business id.

IDS_INVALID_BUSINESSID

8004020a

unManagedidsinvalidorgid

Invalid organization id.

IDS_INVALID_ORGID

8004020b

unManagedidsinvaliditemid

Invalid item id.

IDS_INVALID_ITEMID

8004020c

InvalidSharee

Invalid share id.

IDS_INVALID_SHAREEID

8004020d

InvalidAccessRights

Invalid access rights.

IDS_INVALID_ACCESSRIGHTS

8004020e

unManagedidsinvalidvisibility

Invalid visibility.

IDS_INVALID_VISIBILITY

8004020f

unManagedidsfailureinittoken

Failure in obtaining user token.

IDS_FAILURE_INITTOKEN

80040210

InvalidAssigneeId

Invalid assignee id.

IDS_INVALID_ASSIGNEEID

80040211

unManagedidsinvalidassociation

Invalid association.

IDS_INVALID_ASSOCIATION

80040212

unManagedidsinvalidowninguser

Item does not have an owning user.

IDS_INVALID_OWNINGUSER

80040213

unManagedidsinvalidvisibilitymodificationaccess

User does not have access to modify the visibility of this item.

IDS_INVALID_VISIBILITY_MODIFICATION_ACCESS

80040214

CannotShareWithOwner

An item cannot be shared with the owning user.

IDS_CANNOT_SHARE_OBJECT_WITH_OWNER

80040215

MissingOwner

Item does not have an owner.

IDS_MISSING_OWNER

80040216

UnExpected

An unexpected error occurred.

IDS_UNEXPECTED

80040217

ObjectDoesNotExist

The specified object was not found.

IDS_OBJECT_DOES_NOT_EXIST

80040218

InvalidPointer

The object is disposed.

IDS_INVALID_POINTER

80040219

NotImplemented

The requested functionality is not yet implemented.

IDS_NOT_IMPLEMENTED

 

Até a próxima.

 

Fontes:

http://mscrmuk.blogspot.pt/2009/01/crm-error-codes.html

http://msdn.microsoft.com/en-us/library/gg328182%28v=crm.5%29.aspx

Tags: , , ,

Como encontrar o URL para os WebServices do CRM

by Pedro Azevedo 25. May 2013 11:59

Boas pessoal,

Aqui vai uma dica rápida mas muito comum e que dá jeito ter sempre a mão. Como encontrar os URLs para os WebServices do CRM. Bom é fácil dentro do CRM se formos as Definições -> Personalização vamos encontrar uma entrada com o nome "Recurso para Programadores".

Aqui vamos encontrar também informações sobre a organização:

É tudo por hoje,

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: , , , ,

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