quarta-feira, 9 de março de 2011

Nota Fiscal Eletrônica (segunda geração)


Olá amigos desenvolvedores! Quem trabalha com NFe já deve ter sofrido ou está sofrendo com a "Segunda Geração" do projeto. A verdade é que as mudanças, apesar de complicar a vida dos programadores, tornaram o projeto mais seguro. Nesse artigo pretendo apontar quais foram estas principais mudanças.
As novas regras visam impedir a emissão de notas fiscais com erro de preenchimento ou em situação não prevista na legislação, apesar de gerar um certo transtorno para nós programadores, na questão de suporte claro.

Mudanças de acordo com o CRT (Código de Regime Tributário):

1 - Simples Nacional
2 - Simples Nacional - excesso de sublimite da receita bruta
3 - Regime Normal

Acredito que a regra que deve ter maior impacto para os usuário é a regra que exige o uso do CSOSN no caso do emissor ser optante pelo Simples Nacional (CRT=1). Os emissores do Simples Nacional (CRT=1) devem informar o CSOSN ao invés do CST:

NOTA EXPLICATIVA:
O Código de Situação da Operação no Simples Nacional - CSOSN será usado na Nota Fiscal Eletrônica exclusivamente quando o Código de Regime Tributário - CRT for igual a "1", e substituirá os códigos da Tabela B - Tributação pelo ICMS do Anexo Código de Situação Tributária - CST do Convênio s/nº de 15 de dezembro de 1970.

Quando o CRT=1, informar o Código de Situação da Operação - Simples Nacional (CSOSN)
101 - Tributada pelo Simples Nacional com permissão de crédito;
102 - Tributada pelo Simples Nacional sem permissão de crédito;
103 - Isenção do ICMS no Simples Nacional para faixa de receita bruta;
201 - Tributada pelo Simples Nacional com permissão de crédito e com cobrança do ICMS por substituição tributária;
202 - Tributada pelo Simples Nacional sem permissão de crédito e com cobrança do ICMS por substituição tributária;
203 - Isenção do ICMS no Simples Nacional para faixa de receita bruta e com cobrança do ICMS por substituição tributária;
300 - Imune;
400 - Não tributada pelo Simples Nacional;
500 - ICMS cobrado anteriormente por substituição tributária (substituído) ou por antecipação;
900 - Outros.

Exemplo de XML para CRT=1 e CSOSN = 101 (equivalente ao CST=00)

 
    0
    101
    1.00
    10.00
 

De forma geral, as novas regras podem ser lidas através da NT 2010/009 - divulga as situações de "uso indevido" do Ambiente de Autorização da NF-e e as novas regras de validação para reduzir o "uso indevido":

Alguns usuários poderão ter as suas NF-e rejeitadas quando a SEFAZ implementar as regras de validação em ambiente de produção e devem verificar se devem corrigir as suas aplicações.

Seguem abaixo algumas alterações críticas:

1. Especifica layout padrão de DANFE a ser seguido por todos os contribuintes.
2. Obrigatoriedade de inclusão do Código de Regime Tributário (CRT).
3. O DANFE (Documento Auxiliar de Nota Fiscal Eletrônica) utilizado para acompanhar a mercadoria em trânsito deve ser impresso em uma única via.
4. O emissor e o destinatário deverão armazenar a NF-e em arquivo digital pelo prazo estabelecido na legislação tributária, mesmo que fora da empresa. O arquivo deve ser apresentado quando solicitado em operações de fiscalização.
5. Após a concessão da Autorização de Uso da NF-e o emissor poderá corrigir erros em campos específicos da NF-e, por meio de Carta de Correção Eletrônica (CC-e), transmitida à administração tributária do Estado do emissor. A previsão é de que a CC-e esteja disponível ainda este ano.
6. Não será permitida a reutilização, em contingência, de número de NF-e transmitida com tipo de emissão "Normal".
7. Alterado o endereçamento dos web services, que agora são outros, para a versão 2.0.
8. Alterações no Código Fiscal de Operações e Prestações com relação às entradas de mercadorias a serem utilizadas nas prestações de serviços sujeitas ao ICMS e ao ISSQN.
9. Os contribuintes deverão também, incluir, quando for o caso, o Código de Situação da Operação no Simples Nacional (CSOSN). Este campo (campo CRT (C21) da tag enderEmit) foi adicionado na versão 2.0.
10. Obrigatoriedade de utilização da NF-e pelo critério do CNAE (Classificação Nacional de Atividades Econômicas)

Leia mais sobre o assunto das regras de validação no manual:
http://www.nfe.fazenda.gov.br/PORTAL/docs/NT2010.010_RegrasValidacao.pdf

segunda-feira, 7 de março de 2011

Data por Extenso


Vamos aprender como escrever uma data por extenso? Criaremos uma function que receberá uma data como parâmetro e retornará uma string, contendo a data já por escrito

  function {[classe].}DtPorExtenso(dt: TDateTime): string;

Para controle dos dias da semana vamos criar um array:

  var
    Semana: array [1..7] of string;

O mesmo para os meses:

    Mes: array [1..12] of string;

Vamos precisar também de variáveis para receber os parâmetros de data que a função nativa DecodeDate irá retornar. Estas variáveis devem ser do tipo Word:

    Dia, Mes, Ano: Word;

Após declarar as variáveis, vamos iniciar o bloco de instruções. Primeiramente, vamos carregar as array's Semana e Mes.

begin
  Semana[1] := 'Domingo';
  Semana[2] := 'Segunda';
  Semana[3] := 'Terça';
  Semana[4] := 'Quarta';
  Semana[5] := 'Quinta';
  Semana[6] := 'Sexta';
  Semana[7] := 'Sábado';
 
  Mes[1] := 'Janeiro';
  Mes[2] := 'Fevereiro';
  Mes[3] := 'Março';
  Mes[4] := 'Abril';
  Mes[5] := 'Maio';
  Mes[6] := 'Junho';
  Mes[7] := 'Julho';
  Mes[8] := 'Agosto';
  Mes[9] := 'Setembro';
  Mes[10] := 'Outubro';
  Mes[11] := 'Novembro';
  Mes[12] := 'Dezembro';

Após carregarmos os array's, vamos obter o dia, mês e ano da data recebida pela função:

  DecodeDate(dt, Ano, Mes, Dia);

Agora podemos formar a string de data por extenso:

  result := Semana[ DayOfWeek(dt) ] + ', ' +
            IntToStr(Dia) + ' de ' +
            Mes[Mes] + ' de ' +
            IntToStr(Ano);

Reparem que utilizei a função DayOfWeek para obter o id do dia da semana, que é utilizado como índice do array Semana.

Backup com MySQL e Delphi


Mesmo que saibamos tudo sobre o MySQL Server 5.0, comandos, sintaxe, recursos entre outras coisas, tudo ficará perdido se um vírus ou outra praga digital invadir o computador e acabar com os nossos dados. Por isso é bom fazer backups da base de dados

O MySQL Server 5.0 tem um recurso chamado mysqldump, que ajuda a fazer backups da base de dados, mas, o problema é que tudo por meio de linhas de comando, o que um usuário final (na maioria dos casos) não saberá fazer.
Para contornar essa barreira, podemos utilizar os arquivos bat, automatizando o processo de backup, ou melhor, do mysqldump! Veja um exemplo:
  cd C:\Arquivos de programas\MySQL\MySQL Server 5.0\bin
  mysqldump nome_da_base_de_dados > caminho_onde_ficara_salvo_o_bakup
  -u Nome_do_usuario_do_mysql -p senha_do_mysql -x -e -a -v 
  exit

Essa é a sintaxe do comando que deve conter o arquivo bat, e pronto!
Para executar o bat de uma aplicação em Delphi, use a seguinte linha de comando:
  WinExec(Pchar('Caminho onde esta salvo o arquivo bat'), SW_SHOWNORMAL);


Segue um link para baixar um pequeno programa que fiz para demonstrar como fazer a conexão com MySQL Server 5.0 e com o exemplo de arquivo bat para o backup: exemplo_backup_mysql.zip

Um lembrete: para compilar o exemplo, copie-o para o seu C:\

quinta-feira, 3 de março de 2011

Progresso de Transferência FTP com idFTP


Andei procurando muito pela web algum exemplo de como medir o progresso da transferência de um arquivo através do componente idFtp, no Delphi 6. Depois de muito procurar e nada encontrar, um pequeno exemplo que não funcionava muito bem me fez pensar um pouco e me levou, após alguns testes, a um medidor de progresso funcional, feito com um gauge.
Para fazer um exemplo, coloque um componente idFTP, um ListBox e um Button no seu formulário. Configure o seu componente de conexão de acordo com os dados do ftp que irá se conectar e declare uma variável global chamada bytesToTransfer

var
  Form1: TForm1;
  bytesToTransfer: integer;
em seguida, faremos a conexão e o download de todos os arquivos .exe que estiverem no ftp. No exemplo, o código foi implementado no evento onClick do botão:

procedure TForm1.Button1Click(Sender: TObject);
var
  indice: integer;
begin
  try
    //efetua a conexão ao FTP
    if IdFTP1.Connected then
      IdFTP1.Disconnect;
    IdFTP1.Connect();

    //lista todos os arquivos do tipo .exe do ftp no ListBox1
    IdFTP1.List(ListBox1.Items,'*.exe',false);

    //se não houverem arquivos, aborta
    if ListBox1.Items.Count = 0 then
      Abort;

    //para cada ítem do ListBox1
    for indice:=0 to ListBox1.Items.Count -1 do
    begin
      try
        //marca o ítem selecionado
        ListBox1.Selected[indice] := true;
        //captura o tamanho do arquivo para a varíavel global
        bytesToTransfer := IdFTP1.Size(ListBox1.Items.Strings[indice]);
        //inicia a transferência do arquivo
        IdFTP1.Get(ListBox1.Items.Strings[indice],
          '' + ListBox1.Items.Strings[indice],true);
      except
        on e:exception do
          showmessage(e.Message);
      end;
    end;

  finally
    //desconecta
    IdFTP1.Disconnect;
  end;
end;
No eventoWorkBegin do idFTP, que é disparado no momento em que o Download do arquivo é iniciado, faça:

procedure TForm1.IdFTP1WorkBegin(Sender: TObject; AWorkMode: TWorkMode;
   const AWorkCountMax: Integer);
begin
  //limpa a barra de progresso
  Gauge1.Progress := 0;
  //define o tamanho máximo para o Gauge
  if AWorkCountMax > 0 then
    Gauge1.MaxValue := AWorkCountMax
  else
    Gauge1.MaxValue := bytesToTransfer;
end;
E finalmente, a cada conjunto de bytes trazidos pelo componente, incrementamos o Gauge. Isso é feito no evento Work do idFTP:

procedure TForm1.IdFTP1Work(Sender: TObject; AWorkMode: TWorkMode;
   const AWorkCount: Integer);
begin
  //incrementa o Gauge
  Gauge1.Progress := AWorkCount;
end;

quarta-feira, 2 de março de 2011

Técnicas de depuração em Delphi e Prevenção de Bug


Muitas vezes pegamos um sistema legado de outro programador para realizarmos implementação de novas funcionalidades ou corrigir eventuais "bugs". Neste caso você vai precisar depurá-lo para encontrar os "bugs". O Delphi oferece ótimas ferramentas de depuração e quando você sabe como usá-las, você vai economizar muito tempo para corrigir o "bug" e chegar no resultado desejado
Neste caso quero indicar um tutorial que encontrei e ajudou quando o assunto era depuração de programas.
Aqui está o conteúdo:

Project options - antes que você possa começar a utilizar as ferramentas depurador do Delphi, você tem que certificar-se de todas as configurações necessárias são definidas opções depurador .

Breakpoints - Quando pressionar a tecla F5 ou clicando na barra esquerda do seu editor você pode adicionar uma linha vermelha para a sua fonte. Esta linha de origem terão um ponto de interrupção. Ao executar o programa, a execução irá parar quando ele passa a linha de origem. Agora você pode seguir em sua origem, usando algumas teclas de função.

Call stack - a janela Call Stack exibe as chamadas de função que o trouxe para a sua localização atual no programa e os argumentos passados para cada chamada de função.

Local variables - essa janela vai mostrar todas as variáveis locais e o seu valor atual no atual função ou procedimento.

Watches - você adicionar um Watches para controlar os valores das variáveis do programa ou expressões como você passar por cima ou em código de rastreamento.

Idéias para criar os seus próprios recursos de depuração Prevenção Bug Try-Finally Gotchas Try-Except Gotchas

Criando um gerador de senhas


Veja nesta dica um código simples mas bastante útil, que gera senhas aleatórias podendo conter somente numeros, somente letras ou letras e números. O autor também aborda um pouco do comando try..except. Confira!
Primeiramente, vamos ao tutorial: insira em um form um Edit, abaixo dele um RadioGroup e depois outro Edit. Nesse RadioGroup, procure pela propriedade Items no Object Inspector e adicione o seguinte:

Somente números Somente letras Letras e números

Após isso, adicione dois Buttons. No primeiro mude a propriedade Caption para "Gerar" e o segundo "Limpar". No OnClick do botao "Gerar" coloque o seguinte código:

procedure TForm1.Button1Click(Sender: TObject);
const
  letras = 'abcdefghijklmnopqrstuvxwyzABCDEFGHIJKLMNOPQRSTUVXWYZ';
  numeros = '1234567890';
  letrasnumeros = letras + numeros;
var
  i: integer;
begin
  try
    Edit2.Clear;
    for i := 1 to StrToInt(Edit1.Text) do
    begin
      if RadioGroup1.ItemIndex = 0 then
        Edit2.Text := Edit2.Text + numeros[random(length(numeros)) + 1]
      else if RadioGroup1.ItemIndex = 1 then
        Edit2.Text := Edit2.Text + letras[random(length(letras)) + 1]
      else if RadioGroup1.ItemIndex = 2 then
        Edit2.Text := Edit2.Text + letrasnumeros[random(length(letrasnumeros)) + 1];
    end;
  except
    showmessage('Insira somente números no primeiro Edit');
  end;
end;
Explicando

Criamos uma variavel i, do tipo inteira, que irá receber a quantidade de caracteres que o usuário quiser para a sua senha. Por isso convertemos o valor do Edit1.Text de String para Inteiro (StrToInt) dentro do for. Criamos também três constantes, adicionando a cada uma respectivamente as letras, números ou as duas juntas.

O comando "Try" funciona da seguinte maneira: falamos para o Delphi tentar executar esse código e, caso ele não conseguir, podemos utilizar o Except para apresentar, por exemplo, algumas mensagens de erro do que pôde acontecer. No exemplo, deve ser informado no Edit1 a quantidade de caracteres da senha e, caso a pessoa coloque letras ao invés de números, a função StrToInt não conseguirá ser executada. Com o Except, conseguimos informar ao usuário este problema e tiramos a mensagem de erro que o delphi emitiria, em inglês.

No segundo botão, coloque apenas:

procedure TForm1.Button2Click(Sender: TObject);
begin
  Edit2.Clear;
end;
Após isso efetue a seguinte alteração no seu Delphi : em Tool>Options, procure por Language Execeptions e desmarque "Notify on Language Execeptions". Isto fará com que o compilador do Delphi não interrompa o programa com as mensagens do depurador e deixe a mensagem ir diretamente ao programa.

Nesse momento, compile e rode sua aplicação, informe um número no primeiro edit, selecione um método de geração e veja a senha gerada no segundo edit. É o nosso código em funcionamento!

Pesquisa fonética no MySQL

Fonte: www.activedelphi.com.br

Olá pessoal! Há algum tempo atrás precisei fazer uma consulta por fonema em meu banco de dados e não achei muita coisa na internet, mas descobri que o MySQL a partir da versão 3 acrescentou uma função chamada soundex, que outros bancos até já utilizam, como o Oracle, por exemplo. Nesta dica mostrarei uma consulta simples, que pode ser implementada em qualquer linguagem de programação, pois é um código SQL.
Primeiramente vamos criar um banco no mysql:

shell>mysql –useuusario –psuasenha
mysql>create database fonema;
Query OK, 1 row affected (0.01 sec)
Agora vamos selecionar para uso o banco que acabamos de criar:

mysql>use fonema;
Database changed
Criando nossa tabela de teste:

mysql>create table fonetica (
cod INTEGER NOT NULL AUTO_INCREMENT,
nome VARCHAR(60) NOT NULL,
PRIMARY KEY (cod)
)
ENGINE = InnoDB;
Query OK, 0 rows affected (0.08 sec)
Vamos inserir alguns dados nesta tabela. No comando abaixo, dois nomes somente:

mysql>insert into fonetica (nome) values (“JAIME ADRIANO”),(“JAYME ADRIANO”);
Query OK, 2 rows affected (0.09 sec)
Records: 2 Duplicates: 0 Warnings: 0
Agora vamos a nossa consulta:

mysql>select * from fonetica where SOUNDEX(nome) like
    ->CONCAT(SOUNDEX(“JAIME ADRIANO”),”%”);
+-----+---------------+
 cod    nome
+-----+---------------+
 6      JAIME ADRIANO
 7      JAYME ADRIANO
+-----+---------------+
2 rows in set (0.00 sec)
Perceba que o banco retornou Jaime com i e com y, que foneticamente são pronunciados da mesma forma. Faça um teste ao contrario, colocando o nome JAYME na consulta, com y, e verá que ele também retornará os dois nomes com a mesma fonética.

Espero ter ajudado e daí por diante é com vocês! Façam as implementações e melhorias e postem aqui para ajudar outras pessoas. Esta dica foi extraida do meu blog, achei interessante postar aqui!

Curso de Delphi: 7.Consultas SQL