quarta-feira, 25 de janeiro de 2017

CSS: Quebra de página em PageBlockTable (renderAs="PDF")

É... o título não ficou muito legal! Tentei resumir em poucas palavras, para o título, o problema e a solução que encontrei e que compartilharei com vocês logo a seguir:

Bem, estava criando uma página Visualforce, renderizada em PDF que, entre outras coisas, possui uma tabela <apex:PageBlockTable />. O problema acontecia quando a tabela estava no final da página e o conteúdo dessa tabela não cabia na mesma página. A tabela era simplesmente cortada e mal dividida, deixando um aspecto, digamos, bem amador. Veja:




Porém dando uma pesquisa, achei uma solução interessante que, caso essa "quebra de página" aconteca, o cabeçalho é copiado para a página seguinte, deixando um aspecto melhor para a continuação da tabela. O CSS em questão é:


     table {  
       -fs-table-paginate: paginate;  
     }  

Observe que o cabeçalho é inserido automaticamente na segunda página, após inserir o CSS no cabeçalho da sua Visualforce:



Bom, por hoje é apenas isso! 




* Está precisando de um suporte ou uma consultoria na implantação do seu Salesforce? Entre em contato conosco - CbCloud: http://www.cbcloud.com.br/
Somos parceiros gerenciados pela Salesforce!





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: