sexta-feira, 8 de outubro de 2010

Gerador / Validador de CPF e CNPJ no lazarus

Diferentemente do RG um CPF (ou CNPJ) é um identificador único. Um CPF identifica uma pessoa única no Brasil. É uma boa prática associar contas de usuário com seu CPF. Seguro para o usuário, pois diminui o risco de alguém se passar por ele, e seguro para quem presta algum serviço, pois pode identificar um usuário infrator.

Lógico que essa não deveria nem de longe ser a única medida de segurança e identificação de um usuário, mas já é um começo.

Quando você coloca o CPF como campo obrigatório em um formulário de cadastro no seu sistema torna-se difícil testar esse formulário porque você precisa validar o CPF para ver se é correto, mas também precisa de CPF's válidos e únicos para usar nos seus testes. O que fazer?

Pensando nisso coloquei esse gerador de CPF e CNPJ que você pode ver na barra lateral do blog, e fiz esse programinha em lazarus para gerar e/ou validar CPF's e CNPJ's. Você pode copiar esse algoritmo para validar documentos nos seus cadastros.

Funcionamento do CPF:

O CPF é formado por 11 dígitos (excetuando-se as pontuações e separadores), 9 do número do documento e 2 de verificação.

O 10° dígito é baseado em um algoritmo feito com os 9 dígitos precedentes, e o 11° é calculado usando-se os 9 primeiros mais o 10° recém calculado. Ou seja, se seu CPF é 74241980503 (ainda não chegamos nesse número espero) isso quer dizer que fazendo o referido algoritmo com 742419805 você encontrará o 0 e fazendo com 7424198050 você encontrará o 3.

O algoritmo consiste em 3 passos:
1) multiplicar, da esquerda para a direita, cada dígito por um valor n que começará com 10 e decrescerá (n-1 com n nunca sendo < 2) a cada casa para a direita, acumulando-se (somando-se) cada resultado. Por exemplo, o CPF que mencionamos iniciaríamos o calculo assim:
(7 x 10)+(4 x 9)+(2 x 8)+(4 x 7)+(1 x 6)+(9 x 5)+(8 x 4)+(0 x 3)+(5 x 2)  que seria 70+36+16+28+6+45+32+0+10=243
2) Obter o resto da divisão dessa soma por 11, ou seja 243 mod 11 = 1
3) Subtrair o resultado de 11, ou seja, 11 - 1. Se o resultado for maior que 9 colocamos 0. Então 11-1 = 10, portanto 0. Há alguns que verificam se o módulo por 11 é menor que 2, se for o resultado do dígito é 0, senão será 11 subtraindo-se  o módulo. Tanto faz a ordem dos fatores, dá no mesmo.

O 11° dígito obtem-se da mesma maneira, porém começando-se a multiplicação por 11, então se até agora temos 7424198050X devemos fazer:

(7 x 11) + (4 x 10) + (2 x 9) + (4 x 8) + (1 x 7 ) + (9 x 6) + (8 x 5) + (0 x 4) + (5 x 3 ) + (0 x 2) = 283
283 mod 11 = 8
11-8 = 3, por isso o último dígito é 3.

Criamos a seguinte função para calcular o DV do CPF, supondo-se que os 9 primeiros dígitos são dados:


function TfrmDocumentos.CalculaDVCPF(cpf: string): string;
var
   digitos: array [1..11] of integer;
begin

     digitos[1] := StrToInt(cpf[1]);
     digitos[2] := StrToInt(cpf[2]);
     digitos[3] := StrToInt(cpf[3]);
     digitos[4] := StrToInt(cpf[4]);
     digitos[5] := StrToInt(cpf[5]);
     digitos[6] := StrToInt(cpf[6]);
     digitos[7] := StrToInt(cpf[7]);
     digitos[8] := StrToInt(cpf[8]);
     digitos[9] := StrToInt(cpf[9]);

     digitos[10] := 11 -
          (((digitos[1] * 10)+
          (digitos[2]  * 9)+
          (digitos[3]  * 8)+
          (digitos[4]  * 7)+
          (digitos[5]  * 6)+
          (digitos[6]  * 5)+
          (digitos[7]  * 4)+
          (digitos[8]  * 3)+
          (digitos[9]  * 2)) mod 11);

     if digitos[10] > 9 then
        digitos[10] := 0;

     digitos[11] := 11 -
          (((digitos[1] * 11)+
          (digitos[2]  * 10)+
          (digitos[3]  * 9)+
          (digitos[4]  * 8)+
          (digitos[5]  * 7)+
          (digitos[6]  * 6)+
          (digitos[7]  * 5)+
          (digitos[8]  * 4)+
          (digitos[9]  * 3)+
          (digitos[10] * 2)) mod 11);

     if digitos[11] > 9 then
        digitos[11] := 0;

     Result := IntToStr(digitos[10])+IntToStr(digitos[11]);

end;  

Para gerar números randômicos de 9 dígitos, para serem passadas para a função acima, usamos:

function TfrmDocumentos.GeraCPF: string;
var
   iNum: integer;
begin
     Randomize;
     iNum:=random(999999999);
     Result := ZeroEsquerda(IntToStr(iNum), 9);
end;  

A função ZeroEsquerda serve para completar uma string numérica com zeros a esquerda até a quantidade de dígitos definidas no segundo parâmetro e é definida como a seguir:


function TfrmDocumentos.ZeroEsquerda(num: string; tamanho: integer): string;
var i: integer;
    tamAtual: integer;
begin
     Result := num;
     tamAtual:= tamanho - Length(num);
     for i := 1 to tamAtual do
         Result := '0'+Result;
end; 

As funções abaixo são usadas para limpar um CPF ou CNPJ se ele possui caracteres de pontuação, ou para colocar os caracteres de pontuação nas posições corretas de acordo com o tipo de documento.


//elimina caracteres não numéricos da string
function TfrmDocumentos.Limpa(documento: string): string;
var
   i: integer;
begin
     for i := 1 to Length(documento) do
         if documento[i] in ['0'..'9'] then
            Result := Result+documento[i];
end;

//insere as pontuações nas posições corretas
function TfrmDocumentos.Mascara(documento: string): string;
var
   i: integer;
   temp: string;
begin

     temp := Limpa(documento);
     if Length(temp) = 11 then
     begin
               Insert('.', temp, 4);
               Insert('.', temp, 8);
               Insert('-', temp, 12);
     end
     else
     begin
          Insert('.', temp, 3);
          Insert('.', temp, 7);
          Insert('/', temp, 11);
          Insert('-', temp, 16);
     end;

     Result := temp;
end;  

Para gerar um CPF utilizamos:

procedure TfrmDocumentos.btGeraCPFClick(Sender: TObject);
var
   sCPF: string; //9 primeiros digitos
begin
  sCPF:=GeraCPF;
  //edit com o cpf completo
  txtGCPF.Text := sCPF+CalculaDVCPF(sCPF)
end;  

Para verificar se um CPF é válido obtemos os 9 primeiros dígitos, separamos dos dois posteriores e calculamos os DV's para esses 9 dígitos. Se eles forem iguais aos dois últimos dígitos que extraímos então o CPF é válido.


procedure TfrmDocumentos.btValidaCPFClick(Sender: TObject);
var
   temp, sCpf, sDv: string;
begin
     temp := Limpa(txtVCPF.Text);
     sCpf:=Copy(temp, 1, 9);
     sDv := Copy(temp, 10, 2);
     if (Length(temp) = 11) and (CalculaDVCPF(sCpf) = sDv)  then
     begin
        lbMsg.Caption := 'Cpf Válido';
        lbMsg.Font.Color:=clGreen;
     end
     else
     begin
        lbMsg.Caption := 'Cpf Inválido';
        lbMsg.Font.Color:=clRed;
     end;
end;   


A mesma lógica se aplica com o CNPJ. A única diferença é que o CNPJ se divide em três blocos:
O número (8 primeiros dígitos) o número da filial ou famoso "mil ao contrário/mil contra" que são os 4 posteriores e os dois últimos são o DV.

Para calcular o dígito 13 multiplicamos os 12 primeiros por 5,4,3,2,9,8,7,5,6,4,3,2 e depois somamos as parcelas, retiramos o resto da divisão por 11, subtraimos este resultado de 11 e tudo que for maior que 9 passa a ser 0, não for maior que 9 permanece.

Para calcular o 14° dígito multiplicamos os 13 primeiros, incluindo aí o primeiro DV, por 6,5,4,3,2,9,8,7,6,5,4,3,2 e repetimos o processo.

Esse é o algoritmo em lazarus:

function TfrmDocumentos.CalculaDVCNPJ(cnpj: string): string;
var
   digitos: array [1..14] of integer;
begin

     digitos[1] := StrToInt(cnpj[1]);
     digitos[2] := StrToInt(cnpj[2]);
     digitos[3] := StrToInt(cnpj[3]);
     digitos[4] := StrToInt(cnpj[4]);
     digitos[5] := StrToInt(cnpj[5]);
     digitos[6] := StrToInt(cnpj[6]);
     digitos[7] := StrToInt(cnpj[7]);
     digitos[8] := StrToInt(cnpj[8]);
     digitos[9] := StrToInt(cnpj[9]);
     digitos[10] := StrToInt(cnpj[10]);
     digitos[11] := StrToInt(cnpj[11]);
     digitos[12] := StrToInt(cnpj[12]);

     digitos[13] := 11 - ((

          (digitos[1] * 5)+
          (digitos[2] * 4)+
          (digitos[3] * 3)+
          (digitos[4] * 2)+
          (digitos[5] * 9)+
          (digitos[6] * 8)+
          (digitos[7] * 7)+
          (digitos[8] * 6)+
          (digitos[9] * 5)+
          (digitos[10] * 4)+
          (digitos[11] * 3)+
          (digitos[12] * 2)

          ) mod 11);


     if digitos[13] > 9 then
        digitos[13] := 0;

     digitos[14] := 11 - ((

          (digitos[1] * 6)+
          (digitos[2] * 5)+
          (digitos[3] * 4)+
          (digitos[4] * 3)+
          (digitos[5] * 2)+
          (digitos[6] * 9)+
          (digitos[7] * 8)+
          (digitos[8] * 7)+
          (digitos[9] * 6)+
          (digitos[10] * 5)+
          (digitos[11] * 4)+
          (digitos[12] * 3)+
          (digitos[13] * 2)

          ) mod 11);


     if digitos[14] > 9 then
        digitos[14] := 0;

     Result := IntToStr(digitos[13])+IntToStr(digitos[14]);
end;   

Respectivamente estes são os códigos para gerar, criar e validar CNPJ:

function TfrmDocumentos.GeraCNPJ: string;
var
   iNum: Int64;
begin
     Randomize;
     iNum:=random(999999999999);
     Result := ZeroEsquerda(IntToStr(iNum), 12);
end;  

procedure TfrmDocumentos.btGeraCNPJClick(Sender: TObject);
var
   sCNPJ: string;
begin
  sCNPJ:=GeraCNPJ;
  if cbMascara.Checked then
     txtGCNPJ.Text := Mascara(sCNPJ+CalculaDVCNPJ(sCNPJ))
  else
     txtGCNPJ.Text := sCNPJ+CalculaDVCNPJ(sCNPJ);
end;  

procedure TfrmDocumentos.btValidaCNPJClick(Sender: TObject);
var
   temp, sCNPJ, sDv: string;
begin
     temp := Limpa(txtVCNPJ.Text);
     sCNPJ:=Copy(temp, 1, 12);
     sDv := Copy(temp, 13, 2);
     if (Length(temp) = 14) and (CalculaDVCNPJ(sCNPJ) =  sDv)  then
     begin
        lbMsg.Caption := 'CNPJ Válido';
        lbMsg.Font.Color:=clGreen;
     end
     else
     begin
        lbMsg.Caption := 'CNPJ Inválido';
        lbMsg.Font.Color:=clRed;
     end;
end;   



Como essas regras e fórmulas são rígidas, bem como os tamanhos e posições dos documentos, optei por usar um vetor de tamanho fixo e armazenar cada valor em sua posição em vez de usar um loop for. Assim eu economizo algumas variáveis temporárias de somatório e módulo, efetuo as somas e divisões uma única vez e a transformação de str para int apenas uma vez, e as transformações de int para str apenas nos dois últimos dígitos, os DV.

Espero ter esclarecido como funciona essa validação.

Você pode baixar o programa aqui.

Nenhum comentário:

Postar um comentário

Postagens populares