segunda-feira, 26 de dezembro de 2016

Dica rápida: Link da instância Salesforce em uma Fórmula

Caso precise do Link da instância da sua Org Salesforce, em uma fórmula, vai uma dica rápida para se ter esse link:

 LEFT($Api.Partner_Server_URL_260, FIND( '/services', $Api.Partner_Server_URL_260))  

Após essas instruções, basta concatenar o que deseja para formar sua URL com a instância Salesforce.

Essa era rapidinha ;)












domingo, 11 de dezembro de 2016

Atribuindo Tarefas para um Papel - Workflow

A pouco me deparei com uma situação inusitada. Criei uma nova regra de fluxo de trabalho (Workflow), com uma ação de criação de uma Tarefa (Task). Essa tarefa era atribuída a um Papel (Role).

















Porém, no momento que a regra de fluxo de trabalho era executada, percebi que a Tarefa era criada, porém não atribuía corretamento ao usuário do Papel. Essa tarefa acabava sendo atribuída a mim. Fui pesquisar o ocorrido e descobri que o Salesforce só atribui corretamente uma tarefa a um usuário de um papel, se existir apenas 1 usuário com aquele papel. No caso da minha Org, existiam 2 usuários com o mesmo papel. Então o Salesforce atribui a tarefa ao proprietário do registro relacionado à tarefa. No caso eu. Pelo que li também, caso não exista nenhum usuário com o referido papel, o comportamento do Salesforce será o mesmo. Veja a referência:

https://help.salesforce.com/HTViewSolution?id=000213466&language=en_US


Bem, por agora é isso!



domingo, 4 de dezembro de 2016

Inserindo posts no Chatter com arquivo anexo

Para inserir uma mensagem no Chatter, com um arquivo anexo, via Apex (em uma trigger ou classe), é necessário usar o objeto FeedItem. Sem mais delongas, vamos fazer um exemplo que permita inserir um texto no Chatter e um arquivo TXT anexo, com os dados de algumas Contas (Account).


   String conteudoArquivoTxt = 'Segue a relação de Contas: \r\n';  
   List<Account> listaDeContas = [SELECT id, name, website FROM Account Limit 10];  
   for (Account conta : listaDeContas){  
     conteudoArquivoTxt += 'Nome: ' + conta.name + ' | Website: ' + conta.Website + '\r\n';  
   }  
   FeedItem post = new FeedItem();  
   post.ParentId = UserInfo.getUserId();  
   post.Body = 'Lista de Contas. Favor conferir!';  
   post.Type = 'ContentPost';  
   post.ContentData = Blob.valueOf(conteudoArquivoTxt);  
   post.ContentFileName = 'RelacaoContas.txt';  
   insert post;  

A primeira coisa que é importante ressaltar nesse código é: Você não conseguirá rodá-lo na versão de API 36.0 ou superior. Esse código é válido até a versão 35.0. Isto é, se tentar no console do desenvolvedor, na parte de "Open Execute Anonymous Window", ele não funcionará, pois este usa a API mais recente. Então, para inciar os testes, use esse código em uma acionador (trigger) ou classe. No entando, quando for salvar esse código em uma classe ou trigger, altere a API de execução da trigger ou classe para 35 ou inferior:


Veja que, na classe ou trigger, basta acessar a guia Version Settings. E na API do Salesforce, marcar a versão 35 ou inferior.

Bem, agora que nosso código já funciona, vamos passar alguns itens importantes do código:


   String conteudoArquivoTxt = 'Segue a relação de Contas: \r\n';   
   List<Account> listaDeContas = [SELECT id, name, website FROM Account Limit 10];   
   for (Account conta : listaDeContas){   
    conteudoArquivoTxt += 'Nome: ' + conta.name + ' | Website: ' + conta.Website + '\r\n';   
   }   

Inicialmente eu salvo na variável "conteudoArquivoTxt" o conteúdo que desejo exibir no meu arquivo TXT que será salvo junto com a postagem no Chatter. Posteriormente coloco mais conteúdo nessa variável, iterando em uma lista de contas.

Em seguida, instancio um objeto do tipo "FeedItem", que será responsável pela postagem no Chatter:


   FeedItem post = new FeedItem();   

Logo após, através da propriedade "ParentId", eu digo aonde irá ser publicado a postagem. Nesse caso, estou indicando que será publicada na linha do tempo do usuário que está executando o código:



   post.ParentId = UserInfo.getUserId();    

Nas propriedade Body e Type, eu informo o conteúdo da postagem e o tipo de conteúdo, respectivamente. No caso do Type, há uma lista de opções para se usar. Usamos o ContentPost, pois significa que estamos postando texto com um arquivo anexo.



   post.Body = 'Lista de Contas. Favor conferir!';   
   post.Type = 'ContentPost';   

Para conhecer os outros tipos (Type) de conteúdo disponíveis para esse campo, veja a documentação oficial do Salesforce, relacionado ao FeedItem:
https://developer.salesforce.com/docs/atlas.en-us.api.meta/api/sforce_api_objects_feeditem.htm


Nos campos ContentData e ContentFileName, eu informo o corpo do arquivo texto que será anexado e o nome do arquivo txt, respectivamente. Repare que foi necessário converter a String da minha variável conteudoArquivoTxt para o tipo Blob.


   post.ContentData = Blob.valueOf(conteudoArquivoTxt);   
   post.ContentFileName = 'RelacaoContas.txt';   

E para concluir o código, dou o Insert no objeto post (tipo FeedItem) para realizar a postagem no Chatter, de fato:


   insert post;   

Veja o resultado da postagem no Chatter:









terça-feira, 4 de outubro de 2016

Campo fórmula - Semana do ano

Hoje precisei criar um campo fórmula que me indicava se uma Oportunidade, no Salesforce, estava com data de fechamento para essa semana (semana atual). A forma mais fácil de fazer, ao meu ver, seria verificando se a semana (do ano), de fechamento era a mesma semana (do ano) da data atual. Após algumas pesquisas descobri que não há uma fórmula nativa no Salesforce que me desse a semana do ano. Então após algumas pesquisas cheguei nessa fórmula:


 MOD( FLOOR(( TODAY() - DATEVALUE("2006-01-01" )) / 7),52) + 1  

Essa fórmula me retorna a semana do ano, na data atual - TODAY(). Para retornar essa informação, ela diminui a data atual pela data de 01/01/2006 (ano em que o 1º dia do ano foi no domingo, porém poderia ser qualquer outro ano com essa característica) para retornar a quantidade de dias entre a data (01/01/2006) e a data atual. Em seguida divide por 7 (dias na semana) para calcular a quantidade de semanas no período. Por fim, pega o resto da divisão por 52 (que é a quantidade de semanas no ano). E esse resto mais 1 é exatamente a semana do ano.

Bem, na minha situação, criei uma fórmula, do tipo Caixa de seleção (boolean - verdadeiro ou falso), verificando se a semana da data de fechamento da Oportunidade no SFDC era a mesma da semana da data atual:


 (MOD(FLOOR( ( CloseDate - DATEVALUE("2006-01-01" ))/7),52) + 1 )   
 =   
 (MOD(FLOOR( ( TODAY() - DATEVALUE("2006-01-01" ))/7),52) + 1 )   

Outros anos que comecem no domingo, caso precise usar:

2012
2006
1995
1989
1984
1978
1967
1961
1956
1950
1939
1933
1928
1922
1911
1905

Bem, por hoje é apenas isso!



quarta-feira, 7 de setembro de 2016

Exclusão de registros no PageBlockTable - SFDC

Hoje precisei de criar uma página Visualforce, que possuía uma lista de registros em um PageBlockTable. Nessa tabela (PageBlockTable) eu precisei também incluir uma funcionalidade de exclusão de registros e é isso que vou compartilhar hoje.

Criei inicialmente no Salesforce, uma página Visualforce com uma tabela de contas. Essa tabela foi criada através um pageBlockTable com uma Column que exibe o nome (name) da Conta e outra coluna com o Link para exclusão. Veja:


 <apex:page controller="AccountController">  
   <apex:form>    
     <apex:PageBlock>  
       <apex:pageBlockTable id="Contas" value="{!listaContas}" var="itemConta" title="Lista de Contas">        
         <apex:column headerValue="Nome" value="{!itemConta.name}" />          
         <apex:column headerValue="">  
           <apex:commandLink value="Apagar" action="{!Excluir}" onclick="return window.confirm('Deseja realmente remover essa Conta?');">  
             <apex:param name="contaId" value="{!itemConta.Id}" assignTo="{!idAccountDelete}"/>  
           </apex:commandLink>  
         </apex:column>          
       </apex:pageBlockTable>   
     </apex:PageBlock>      
   </apex:form>  
 </apex:page>  

Repare que na segunda coluna no meu Visualforce, eu uso o apex:CommandLink para criar um link de exclusão do registro:


 <apex:commandLink value="Apagar" action="{!Excluir}" onclick="return window.confirm('Deseja realmente remover essa Conta?');">  
    <apex:param name="contaId" value="{!itemConta.Id}" assignTo="{!idAccountDelete}"/>  
 </apex:commandLink>  


Nesse CommandLink eu referencio o método (action) que será chamado na minha classe controller (irei falar dela mais a frente). Esse método, dei o nome de Excluir. Também coloquei um recurso JavaScript para confirmar a exclusão (window.confirm), através da propriedade onclick.
Logo em seguida uso o apex:param para atribuir o ID daquela conta da linha (da tabela), à propriedade idAccountDelete. Essa será usada para definir a exclusão.

Veja agora como ficou minha classe controller. Inicialmente defino a lista que será usada para popular a PageBlockTable e a propriedade (acima descrita) que armazenará o ID da Conta que será excluída. Em seguida o construtor da classe controller e o método de exclusão:


 public class AccountController {  
   public Id idAccountDelete { get; set; }  
   public List<Account> listaContas { get; set; }  
   public AccountController(){  
     this.listaContas = [SELECT id, name FROM Account ORDER BY name];  
   }  
   public PageReference Excluir(){  
     Account conta = [SELECT id FROM Account WHERE id = :this.idAccountDelete];  
     delete conta;  
     return null;  
   }  
 }  

Veja que no construtor - public AccountController() - busco os dados que serão exibidos na tabela. Já no método de exclusão - public PageReference Excluir() - Eu apago a conta, baseada na propriedade idAccountDelete, que recebeu o ID da conta da linha (da tabela), em que se clicou no link "Apagar".

O resultado final foi:


Página Visualforce renderizada


Mensagem para confirmação da exclusão



sábado, 27 de agosto de 2016

Removendo link da data atual do

A ideia desse post é mostrar a forma de remover o link da data atual, no componente <apex:Inputfield /> quando esse está referenciado uma data, em uma página Visualforce, no Salesforce.










Bem, para remover a data atual, precisamos usar o seguinte código CSS:


   <style>  
     div.hideCurrDate span.dateInput span.dateFormat{  
       display:none;  
     }  
   </style>  

E colocarmos o nosso <apex:Inputfield /> dentro de uma div, referenciando a classe CSS:


      <div class="hideCurrDate">  
           <apex:inputField value="{!acc.DataUltimaCompra__c}"/>  
      </div>  


Dessa forma, temos o seguinte resultado:









quinta-feira, 25 de agosto de 2016

Validação de CPF e CNPJ com regra de validação

Hoje vai uma dica rápida para criar uma regra de validação para validar um campo que contenha CPF ou CPNJ no Salesforce. Criamos para esse exemplo um campo cuja o nome de API é CNPJ_CPF__c, no objeto que desejo realizar a validação (Account - Conta, por exemplo).

Dei um nome para a regra de validação e na formula para validação, inseri o seguinte código:


   

 IF( LEN( CNPJ_CPF__c )= 14   
 ,   
 NOT(   
 OR(   
 LEN(CNPJ_CPF__c)=0,   
 AND( NOT(CNPJ_CPF__c = "00000000000000"),   
 MOD(MOD(11-MOD(VALUE(MID(CNPJ_CPF__c,1,1))*5+VALUE(MID(CNPJ_CPF__c,2,1))*4+VALUE(MID(CNPJ_CPF__c,3,1))*3+VALUE(MID(CNPJ_CPF__c,4,1))*2+VALUE(MID(CNPJ_CPF__c,5,1))*9+VALUE(MID(CNPJ_CPF__c,6,1))*8+VALUE(MID(CNPJ_CPF__c,7,1))*7+VALUE(MID(CNPJ_CPF__c,8,1))*6+VALUE(MID(CNPJ_CPF__c,9,1))*5+VALUE(MID(CNPJ_CPF__c,10,1))*4+VALUE(MID(CNPJ_CPF__c,11,1))*3+VALUE(MID(CNPJ_CPF__c,12,1))*2,11),11),10) =   
 VALUE(MID(CNPJ_CPF__c,13,1)),   
 MOD(MOD(11-MOD(VALUE(MID(CNPJ_CPF__c,1,1))*6+VALUE(MID(CNPJ_CPF__c,2,1))*5+VALUE(MID(CNPJ_CPF__c,3,1))*4+VALUE(MID(CNPJ_CPF__c,4,1))*3+VALUE(MID(CNPJ_CPF__c,5,1))*2+VALUE(MID(CNPJ_CPF__c,6,1))*9+VALUE(MID(CNPJ_CPF__c,7,1))*8+VALUE(MID(CNPJ_CPF__c,8,1))*7+VALUE(MID(CNPJ_CPF__c,9,1))*6+VALUE(MID(CNPJ_CPF__c,10,1))*5+VALUE(MID(CNPJ_CPF__c,11,1))*4+VALUE(MID(CNPJ_CPF__c,12,1))*3+VALUE(MID(CNPJ_CPF__c,13,1))*2,11),11),10) =   
 VALUE(MID(CNPJ_CPF__c,14,1))   
 )))   
 ,
 IF(LEN(CNPJ_CPF__c) < 11, TRUE, 
 NOT AND (CNPJ_CPF__c <> '00000000000',CNPJ_CPF__c <> '11111111111',   
 CNPJ_CPF__c <> '22222222222',CNPJ_CPF__c <> '33333333333',CNPJ_CPF__c <> '44444444444',CNPJ_CPF__c <> '55555555555',CNPJ_CPF__c <> '66666666666',CNPJ_CPF__c <> '77777777777',CNPJ_CPF__c <> '88888888888',CNPJ_CPF__c <> '99999999999',   
 IF ((MID(CNPJ_CPF__c , 10, 1) =   
 IF (MOD(((VALUE(MID(CNPJ_CPF__c , 1, 1))*10)+   
 (VALUE(MID(CNPJ_CPF__c , 2, 1))*9)+   
 (VALUE(MID(CNPJ_CPF__c , 3, 1))*8)+   
 (VALUE(MID(CNPJ_CPF__c , 4, 1))*7)+   
 (VALUE(MID(CNPJ_CPF__c , 5, 1))*6)+   
 (VALUE(MID(CNPJ_CPF__c , 6, 1))*5)+   
 (VALUE(MID(CNPJ_CPF__c , 7, 1))*4)+   
 (VALUE(MID(CNPJ_CPF__c , 8, 1))*3)+   
 (VALUE(MID(CNPJ_CPF__c , 9, 1))*2)),11) < 2,'0',   
 TEXT(11 - MOD(((VALUE(MID(CNPJ_CPF__c , 1, 1))*10)+   
 (VALUE(MID(CNPJ_CPF__c , 2, 1))*9)+   
 (VALUE(MID(CNPJ_CPF__c , 3, 1))*8)+   
 (VALUE(MID(CNPJ_CPF__c , 4, 1))*7)+   
 (VALUE(MID(CNPJ_CPF__c , 5, 1))*6)+   
 (VALUE(MID(CNPJ_CPF__c , 6, 1))*5)+   
 (VALUE(MID(CNPJ_CPF__c , 7, 1))*4)+   
 (VALUE(MID(CNPJ_CPF__c , 8, 1))*3)+   
 (VALUE(MID(CNPJ_CPF__c , 9, 1))*2)),11))   
 )),TRUE,FALSE),   
 IF ((MID(CNPJ_CPF__c , 11, 1) =   
 IF (MOD(((VALUE(MID(CNPJ_CPF__c , 1, 1))*11)+   
 (VALUE(MID(CNPJ_CPF__c , 2, 1))*10)+   
 (VALUE(MID(CNPJ_CPF__c , 3, 1))*9)+   
 (VALUE(MID(CNPJ_CPF__c , 4, 1))*8)+   
 (VALUE(MID(CNPJ_CPF__c , 5, 1))*7)+   
 (VALUE(MID(CNPJ_CPF__c , 6, 1))*6)+   
 (VALUE(MID(CNPJ_CPF__c , 7, 1))*5)+   
 (VALUE(MID(CNPJ_CPF__c , 8, 1))*4)+   
 (VALUE(MID(CNPJ_CPF__c , 9, 1))*3)+   
 (VALUE(MID(CNPJ_CPF__c , 10, 1))*2)),11) < 2,'0',   
 TEXT(11 - MOD(((VALUE(MID(CNPJ_CPF__c , 1, 1))*11)+   
 (VALUE(MID(CNPJ_CPF__c , 2, 1))*10)+   
 (VALUE(MID(CNPJ_CPF__c , 3, 1))*9)+   
 (VALUE(MID(CNPJ_CPF__c , 4, 1))*8)+   
 (VALUE(MID(CNPJ_CPF__c , 5, 1))*7)+   
 (VALUE(MID(CNPJ_CPF__c , 6, 1))*6)+   
 (VALUE(MID(CNPJ_CPF__c , 7, 1))*5)+   
 (VALUE(MID(CNPJ_CPF__c , 8, 1))*4)+   
 (VALUE(MID(CNPJ_CPF__c , 9, 1))*3)+   
 (VALUE(MID(CNPJ_CPF__c , 10, 1))*2)),11))   
 )),TRUE,FALSE)   
 )   
 )
 )


Coloquei a mensagem de "CPF ou CNPJ inválido!", preenchi onde eu gostaria que exibisse o erro e salvei.




Observação importante: Essa validação não permite a utilização pontos, hífens e barras na composição do CPF/CNPJ. Apenas números. Apesar disso, o campo criado é Texto. Agora nada de CPFs ou CNPJs inválidos na sua Org.

Agradeço ao Allan Pecli por me alertar e corrigir alguns itens nessa validação. Se alguém perceber algum problema e quiser colaborar, fique a vontade para me notificar. Valeu Allan!


* Está precisando de um suporte ou consultoria para customização de regras de validação da sua plataforma Salesforce? Entre em contato conosco - CbCloud: http://www.cbcloud.com.br/




terça-feira, 21 de junho de 2016

Removendo ou inserindo menu lateral de criação rápida - Salesforce

Ao inciarmos os trabalhos em uma nova Org, nos deparamos com a opção de criação de registros, de forma rápida, na barra lateral, ao acessarmos uma guia. Veja:




Para remover esse opção (ou inserir, caso não esteja aparecendo para você), acesse a parte de configurações do Salesforce: Criar / Personalização / Interface do Usuário. Marque ou desmarque (de acordo com seu objetivo) a opção "Mostrar criação rápida".




Por fim, clique em "Salvar" no final da página.





sábado, 14 de maio de 2016

Campo fórmula - Convertendo texto para número

Essa é uma dica rápida na criação de campos fórmula (formula fields) no Salesforce! 

Caso precise converter uma campo texto para numérico, na fórmula, utilize a função VALUE(texto):


 VALUE('05')  

Vamos criar uma formula com na Oportunidade. Pegaremos o valor total da oportunidade (Amount) e dividir por um campo que criamos (ValorTotal__c), que representa a quantidade de parcelas:


 2000 / VALUE(QuantidadeParcelas__c)  



segunda-feira, 9 de maio de 2016

Campo fórmula - Convertendo Lista de Opções para Texto

Essa é uma dica rápida para utilização de campo Lista de Opções em campos formula (formula fields) no Salesforce. Como é conhecido, ao tentar referenciar diretamente um campo Lista de Opções em uma fórmula, o Salesforce acusa um problema. Isto acontece porque o campo Lista de Opções armazena uma referência ao valor e não ele diretamente. Então precisamos converter esse valor para utilizar em nossas fórmulas. Conversão essa que não é necessária em códigos Apex.

Bem, para utilizar o campo Lista de Opções em uma fórmula, utilize a função TEXT(CampoListaOpcoes__c):


 TEXT(CampoListaOpcoes__c)  

Ou


 "Valor da Lista de Opções: " & TEXT(CampoListaOpcoes__c)  





domingo, 8 de maio de 2016

Convertendo List em Set e Set em List no Apex

Em algumas situações, me vi obrigado a converter uma collection List em um Set ou um Set em List em uma classe Apex. Creio que você também! É claro que, por serem estruturas diferentes, a quantidade de registros de um List (que permite itens repetidos) pode ser maior que de um Set (que não permite repetidos). Mas vamos ao código.

Inicialmente a conversão de List para Set:

 List<Integer> listDeInteiros = new List<Integer> {1, 2, 3, 3};  
 Set<Integer> setDeInteiros = new Set<Integer>(listDeInteiros);  
 system.assertEquals(4, listDeInteiros.size());  
 system.assertEquals(3, setDeInteiros.size());  



Agora a conversão de Set para List:


 Set<Integer> setDeInteiros = new Set<Integer> {1, 2, 3, 3};  
 Set<Integer> listDeInteiros = new Set<Integer>(setDeInteiros);  
 system.assertEquals(3, setDeInteiros.size());  
 system.assertEquals(3, listDeInteiros.size());  


É possível também, caso já possua um Set em uso, acrescentar toda a List ao Set:


 List<Integer> listDeInteiros = new List<Integer>{1, 2, 3};  
 Set<Integer> setDeInteiros = new Set<Integer> {0};  
 setDeInteiros.addAll(listDeInteiros);  
 system.assertEquals(4, setDeInteiros.size());  



E o contrário... Set ao List:


 Set<Integer> setDeInteiros = new Set<Integer>{1, 2, 3};  
 List<Integer> listDeInteiros = new List<Integer> {0};  
 listDeInteiros.addAll(setDeInteiros);  
 system.assertEquals(4, listDeInteiros.size());  





sexta-feira, 6 de maio de 2016

Apex: Acessando ID de um Tipo de Registro, pelo nome, sem query SOQL

É comum ao desenvolver uma classe ou trigger Apex, no Salesforce, precisarmos de pegar o ID de um Tipo de Registro, para inserirmos um novo registro daquele tipo ou mesmo para fazermos uma consulta de registros daquele tipo. 
Bem, há uma forma bem simples de buscar um determinado Tipo de Registro, pelo nome, sem a necessidade de uma consulta SOQL. Veja:


 Schema.Sobjecttype.Account.getRecordTypeInfosByName().get('Pessoa Física').getRecordTypeId();  



Observe que basta colocar o nome do Objeto (Account, nesse exemplo) e no get o nome do tipo de registro (Pessoa Física).


 Id idRtPF = Schema.Sobjecttype.Account.getRecordTypeInfosByName().get('Pessoa Física').getRecordTypeId();  
 Account conta = new Account(Name='Joao Silva', RecordTypeId = idRtPF);  
 insert conta;  


Ao usar essa instrução, ao invés de fazer uma consulta ao objeto RecordType, você se distanciará dos limites de governança (governor limits) e deixará seu código mais elegante.


ATUALIZAÇÃO IMPORTANTE:

Na release Summer ’18 a Salesforce adicionou o método Schema.DescribeSObjectResult.getRecordTypeInfosByDeveloperName(). Com esse, é possível pegar o id do Tipo de Registro pelo Developer Name. Isso é interessante para evitar problemas com Orgs multi idioma ou alterações do Label do tipo de registro. Veja o exemplo anterior com o novo método:


 Id idRtPF = Schema.SObjectType.Account.getRecordTypeInfosByDeveloperName().get('PessoaFisica').getRecordTypeId();
 Account conta = new Account(Name='Joao Silva', RecordTypeId = idRtPF);  
 insert conta;  



* Precisa de um suporte ou consultoria com a plataforma Salesforce? Entre em contato conosco - CbCloud: http://www.cbcloud.com.br/ - Somos parceiros Gold Salesforce.



quinta-feira, 5 de maio de 2016

Dica rápida Apex: Como acessar a URL da instancia da sua Org - SFDC

Essa é uma dica rápida, para programador Salesforce Apex. Para se ter acesso a URL básica da sua Org Salesforce, basta usar o seguinte comando em seu código:


 String url = System.Url.getSalesforceBaseURL().toExternalForm();  



Ao usar o , teremos o seguinte retorno (de acordo com o servidor que está sua instância):


 https://na17.salesforce.com (Produção)  
 https://cs20.salesforce.com (Sandbox)  


E usar da forma que precisar. Por exemplo:


 String url = System.Url.getSalesforceBaseURL().toExternalForm();  
 PageReference pr = new PageReference(url);  




quinta-feira, 28 de abril de 2016

Simulando códigos APEX para testes com Test.isRunningTest() - SFDC

Em algumas situações (talvez até raras), temos dificuldade ou até mesmo impossibilidade de gerar uma massa de dados que nos permita cobrir (com testes unitários) o nosso código Apex, no Salesforce. 

Em casos como esse, seria ideal fazermos um código "fake" apenas para nossa classe de testes unitários e o código real para nossa aplicação. Para essas situações, usamos o método estático isRunningTest, da classe Test. Ele retorna true, se estiver sendo executado apartir de uma classe de testes e false, se for apartir de uma aplicação normal. Com o isRunningTest, portanto, é possível verificar, em meio à nossa lógica de negócio, se vamos usar um código real ou apenas para testes.

Um exemplo clássico é quando precisamos acessar o PriceBook padrão (StandardPriceBook). Como não conseguimos criá-lo em uma classe de testes, para ser usado na classe de regra de negócio, usaremos uma combinação do isRunningTest com getStandardPricebookId, da classe Test (que já conhecemos melhor através do post Criando registros do catálogo de preço (PriceBookEntry) padrão em classes de teste Apex). Veja a seguir como ficou meu método em uma classe de regra de negócio:


 id idPriceBook = null;  
 if (Test.isRunningTest())  
      idPriceBook = Test.getStandardPricebookId();  
 else {  
   PriceBook pb = [SELECT id FROM PriceBook2 WHERE IsActive = true AND IsStandard = true];  
   idPriceBook = pb.id;  
 }  

Perceba que quando esse código for chamado por uma classe de testes, irá executar o IF, porém, se for chamado por uma execução comum, irá executar o ELSE. E dessa forma conseguirei cobrir completamente a minha classe de negócio.

E esse mesmo código, de forma mais resumida:


 id idPriceBook = Test.isRunningTest() ? Test.getStandardPricebookId() : [SELECT id FROM PriceBook2 WHERE IsActive = true AND IsStandard = true][0].id;  


Por hoje é só!



sábado, 23 de abril de 2016

Lendo parâmetros de uma URL no Salesforce

Esse mais um post rápido e simples para exemplificar como ler os dados passados pela URL, em páginas Visualforce ou classes controller Apex. Para contextualizar estamos falando da QueryString, que são aqueles parâmetros que passamos em uma URL:

 https://c.cs20.visual.force.com/apex/CotacaoAceite?Id=01pm0000000Bf0q&Valor=3&Outros=Teste  

Nessa URL acima, o que seriam os parâmetros?


Parâmetro Valor
ID 01pm0000000Bf0q
Valor 3
Outros Teste

*O símbolo & é entendido com um separador entre os parâmetros.

Bem, agora vamos ao que interessa. Como ler os valores em uma página Visualforce ou em uma classe controller Apex do SFDC?

Visualforce:


 {!$CurrentPage.parameters.ID}  
 <br/>  
 {!$CurrentPage.parameters.Valor}  
 <br/>  
 {!$CurrentPage.parameters.Outros}  
 <br/>  


Apex:


 Id idItem = ApexPages.CurrentPage().GetParameters().get('id');  
 String valor = ApexPages.CurrentPage().GetParameters().get('Valor');  
 String outros = ApexPages.CurrentPage().GetParameters().get('Outros');  


Bem, é isso aí!




quarta-feira, 20 de abril de 2016

GUID no Salesforce

Hoje irei compartilhar uma pequena função Apex para gerar um código GUID no Salesforce. GUID significa Globally unique identifier. Em português Indentificador global único. Isto é, uma código único que é gerado para identificar alguma coisa. Normalmente é usado para identificar registros em sistemas diferentes, em processos de integração. Bem, o que vamos gerar a seguir ficará assim:


 4b033118-86a6-8941-c6e2-ad1015509c6c  

O SFDC, por padrão, diferente de outras plataformas de desenvolvimento, não possui uma classe padrão para gerar os GUID. Após algumas pesquisas encontrei em alguns fóruns um código simples e interessante, que compartilho a seguir:


 global class Guid{   
   global static String generate(){   
     Blob b = Crypto.GenerateAESKey(128);  
     String h = EncodingUtil.ConvertTohex(b);  
     String guid = h.SubString(0,8)+ '-' + h.SubString(8,12) + '-' + h.SubString(12,16) + '-' + h.SubString(16,20) + '-' + h.substring(20);  
     return guid;  
   }  
 }  


Li também que esse código não seria tão perfeito, pois poderia gerar GUID repetidos. Dessa forma, fiz a classe de teste a seguir e não constatei nenhum código repetido:


 @isTest  
 private class GuidTest{  
   static testMethod void test(){  
     boolean erro = false;  
     Set<String> lista = new Set<String>();  
     for (integer i = 0; i < 100000; i++){  
       String uid = Guid.generate();  
       if (lista.contains(uid)){  
         System.debug(i);  
         System.debug(lista.Size());  
         System.debug('GUID: ' + uid);  
         erro = true;  
         break;  
       }  
       else  
         lista.add(uid);  
     }  
     System.assert(!erro, 'A classe GUID gerou um valor duplicado!');  
   }    
 }  

Veja que gerei cem mil UIDs e nenhum duplicou. Me pareceu bem confiável! Porém, se alguém encontrar alguma falha, comente aqui no post. 

Por hoje é só!




terça-feira, 19 de abril de 2016

Habilitando envio de e-mails na Org Salesforce

Uma situação corriqueira que nos deparamos ao inciar os trabalhos em uma nova Org Salesforce, é que alguns e-mails que programamos ou configuramos o envio, acabam não sendo enviados. 

Acesse a opção de Configuração da sua Org e siga o passo-a-passo para habilitar o envio de e-mail:



Menu Administrar / Administração de email / Capacidade de entrega.

Em seguida, marque a opção "Todos os emails" na opção Nível de acesso.




Por fim, basta clicar em Salvar para que sua Org envie emails normalmente! 



quarta-feira, 6 de abril de 2016

Tamanho médio de um registro no Salesforce

Esses dias me deparei com uma situação onde a ORG de um dos nossos clientes estava quase atingindo o limite (em MBs) do espaço disponível na ORG. Ao acessar a opção de "Uso do armazenamento" (Menu: Configurações/Gerenciamento de dados/Uso do armazenamento) verifiquei em um dos objetos que aproximadamente 50 mil registros consumiam 100 mb de espaço. Ao verificar outros objetos, tanto maiores como menores, a proporcionalidade era a mesma. 

Resolvi realizar algumas pesquisas e encontrei diversos artigos e pessoas comentando exatamente o mesmo. Para cada registro (na grande maioria dos casos) no Salesforce, consome-se 2KB de espaço na sua ORG.

É necessário olhar o lado positivo de tudo, certo? Então, a parte positiva é que fica fácil calcular quanto de espaço será necessário, em cada novo projeto, expansão ou para política de expurgo. 

O ruim é que se consome muito espaço para registros pequenos. Em caso de objetos apenas para relacionamento (N-para-N), por exemplo, é um espaço muito grande por registro.

Vi uma ideia cobrando uma solução para o problema. Mas ainda está pouco votada. Quem quiser apoiar a iniciativa: Charge fairly for storing small objects. Eu apoiei!



quinta-feira, 31 de março de 2016

Criando registros do catálogo de preço (PriceBookEntry) padrão em classes de teste Apex

Hoje, ao precisar de criar uma classe de testes no Apex, que envolvia criar uma oportunidade e seus respectivos produtos, me deparei com uma facilidade interessante do Salesforce: A referencia do catálogo de preços padrão.

Para começar, precisei criar na minha classe de testes, um produto com seu preço padrão. Dessa forma, foi-se necessário inserir um registro do objeto Product2 e outro no PriceBookEntry referente ao produto e o respectivo catálogo de preços padrão (ou personalizado, se for o caso).

Então criei meu produto na minha classe de testes:


 Product2 produto = new Product2(name = 'Produto teste');  
 insert produto;  

Agora, para criar o PriceBookEntry, precisarei passar o ID do Produto, o preço e o ID do catálogo de preços. O ID do produto eu tenho, pois acabei de inserir o produto. O preço eu vou definir. E o ID do catálogo de preço padrão? Para essa, o Salesforce tem um método para retornar o ID, sem precisar de consultas SOQL:

Test.getStandardPricebookId()

Com esse método acima, você obtem o ID Catálogo de preços padrão, sem precisar consultas SOQL. Veja como ficou a criação do PriceBookEntry:


 Id pricebookId = Test.getStandardPricebookId();  
 PriceBookEntry pbe = new PriceBookEntry(Pricebook2Id = pricebookId, Product2Id = produto.id, UnitPrice = 500, IsActive = true);  
 insert pbe;  


Depois disso, foi só inserir os respectivos dados na OpportunityLineItem da Opportunity. Mas isso é papo para outro post!

domingo, 27 de março de 2016

Quociente e resto de uma divisão em Apex - SFDC

Essa é uma dica rápida sobre como obter o resultado inteiro (quociente) e o resto (módulo) de uma divisão.
Em muitas linguagens de programação, usamos a barra (/) para obter o quociente da divisão. No Apex da Salesforce também. E para obtermos o resto (ou módulo) usamos a palavra reservada mod ou simplesmente o símbolo de percentual (%). Porém, no Apex é diferente. É necessário usar a biblioteca Math.mod(dividendo, divisor).

Escrevi o código a seguir no console do desenvolvedor e usei o System.debug para exibir o resultado:

 System.debug('Quociente: ' + (10/3));  
 System.debug('Resto: ' + Math.mod(10,3));  
 System.debug('Quociente: ' + (25/5));  
 System.debug('Resto: ' + Math.mod(25,5));  
 System.debug('Quociente: ' + (12/15));  
 System.debug('Resto: ' + Math.mod(12,15));  
 System.debug('Quociente: ' + (1000/400));  
 System.debug('Resto: ' + Math.mod(1000,400));  

Ao executar temos:





domingo, 13 de março de 2016

Alterando logomarca da Organização

Um "problema" que me deparei recentemente foi a alteração de logomarca do Salesforce, no canto superior esquerdo da tela. Essa operação já realizei algumas vezes sem muitos problemas. Normalmente acesso a opção de Documentos (nas guias-menu- ou naquela opção "+"). Lá cadastro uma imagem com a logomarca do cliente que usará a Org. 
Em seguida, acesso as configurações do SFDC: Configurar aplicativo / Criar / Aplicativos. Seleciono o aplicativo que desejo alterar a logomarca e clico em Editar.



Em seguida, seleciono a opção Inserir uma imagem e seleciono a imagem que inseri na opção Documentos:


Ao clicar em Salvar, a imagem já será inserida no lugar da imagem padrão do Salesforce, no canto superior esquerdo da tela.

Bem, aí começa o tal "problema" que relatei inicialmente. Essa solução só funciona para aplicativos customizados. Os aplicativos padrão da Salesforce, não é permitido a alteração da logomarca. Isto é, será impossível alterar a logomarca de aplicativos Vendas, Call Center, Marketing, Comunidades, etc. Para resolver essa situação, temos duas opções:
1ª) Criarmos um novo aplicativo personalizado e colocarmos as guias e permissões do aplicativo padrão (uma espécie de clone do aplicativo padrão);
2º) Deixaremos a logomarca padrão da Salesforce no canto superior esquerdo da página, porém colocaremos a nossa logomarca na barra lateral da página. E é esse processo que demonstraremos a seguir.

Logomarca na barra lateral

Antes de iniciar esse procedimento, insira a imagem desejada da opção de Documentos, do Salesforce. Caso não saiba o procedimento, é o mesmo que demonstramos acima, nesse mesmo post. 

Iniciando, para inserir sua logomarca na barra lateral, acesse as configurações, menu Criar | Personalização | Início | Componentes da Página Inicial. Em seguida, clique em Novo, na opção Componentes personalizados.


Ao clicar em Novo, pode ser exibido uma mensagem informativa. Caso isso aconteça, clique em Avançar. Ao iniciar o Wizard (de 2 etapas), será necessário preencher alguns campos de configuração do componente que estamos criando. Preencha então o nome do componente e marque a opção Imagem. Em seguida, Avançar.


Na página a seguir (etapa 2 do Wizard), será necessário selecionar a imagem que inseriu na opção Documentos. Clique em Inserir uma imagem, selecione a desejada e clique em Salvar.


Com o componente criado, acesse a opção de configuração Criar | Personalização | Início | Layouts da home page e clique em Novo



Na página seguinte, você deverá dar um nome para seu novo Layout de página inicial e clonar uma página existente. Em seguida clique em Salvar.


Será exibido um Wizard para seleção dos componentes dessa nova página inicial. Entre os desejados, selecione o componente que criamos anteriormente e clique em Avançar.


Para finalizar o Wizard, selecione os itens que ficarão na coluna da esquerda e da direita na página inicial. Para dar um destaque, coloque a logomarca na primeira posição na coluna da esquerda. E clique em Salvar e atribuir para atribuir essa página inicial aos perfis desejados. 


Após atribuir os perfis, clique em Salvar. Se atribuiu esse layout de página inicial ao seu perfil, basta clicar em Início para ver como ficou o resultado!


Antes de finalizar, para que essa logomarca fique disponível em todas as páginas laterais e não somente na página inicial do sistema, é necessário acessar as configurações Criar | Personalização | Interface do usuário e marcar a opção Mostrar componentes personalizados da barra lateral em todas as páginas.



quinta-feira, 18 de fevereiro de 2016

Apagando Campanhas no Salesforce

Recentemente estava realizando alguns testes e precisei apagar várias Campanhas que havia criado. Qual a forma mais rápida de apagar, pensei... Abri o Console do desenvolvedor, na janela de execução de código anônimo, escrevi o código a seguir e executei:

 List<Campaign> campanhas = [SELECT id FROM Campaign];  
 delete campanhas;  

Após tentar executar recebi a seguinte mensagem de erro:

DML operation DELETE not allowed on Campaign

Verifiquei se meu usuário possuía permissão no objeto e estava tudo certo. Então fui pesquisar o motivo. Descobri que o motivo era porque meu usuário não estava configurado como "Usuário do marketing". Então acessei o cadastro de usuários do SFDC e cliquei em Editar (o meu usuário). Em seguida marquei a opção "Usuário do marketing:



Pronto, executei novamente o código e consegui apagar as campanhas normalmente.

Finalizando... Com essa opção de  "Usuário do marketing" marcada você poderá:

- Criar uma nova campanha;
- Editar uma campanha existente;
- Apagar uma campanha;
- Atualizar estatísticas da campanha;
- Importar leads em uma campanha;
- Atualizar em massa, os status de membros de uma campanha;
- Alterar configurações avançadas de uma campanha;

- Rodar relatórios de campanhas por múltiplas campanhas.