SITE EM DESENVOLVIMENTO

Programação‎ > ‎Delphi‎ > ‎

Criando um validador de CNPJ, CPF e Inscrição Estadual

Aqui vou ensinar como criar um validador para CNPJ, CPF e IE.
Esse validador pode fazer a verificação de um único número (sendo CPF, CNPJ ou IE) e também pode ler uma tabela do Excel, com 3 colunas, cada qual corresponde a um número de documento, mais uma coluna indicando a UF (para validação da IE). A IE é validade por uma DLL fornecida pela Receita Federal, e que vale para qualquer estado brasileiro.
 
Antes de mais nada vamos entender as lógicas para validar um CPF e CNPJ:

Validação de CPF e CNPJ

Temos o seguinte número de CPF: 491122534-30.
Os dígitos depois do hífen são chamados dígitos verificadores, porque há um cálculo para encontrá-lo.
Há várias lógicas, mas eu sempre usei a seguinte:
Concentre-se apenas nos 9 primeiros dígitos, como um número único. Multiplique o algarismo menos significativo (o da direita) por 2, e vá subindo o fator de multiplicação até chegar no algarismo mais significativo, quando o fator de multiplicação será 10. Some cada produto, assim como na conta abaixo, e verifique o resultado:
 
4        9        1        1        2        2        5        3        4                    *
10      9        8        7        6        5        4        3        2

40  + 81   +  8   +   7   +  12   +  10  +  20   +  9   +   8                = 195
 
Agora, encontre o resto da divisão desse resultado por 11, no nosso caso: 195/11=(17*11)+8, o resto é 8. Agora subtraia o resto de 11, encontrando o 1º dígito verificador: 11-8=3. ATENÇÃO: Caso o resto seja menor que 2, ou seja, se for  1 ou 0 ele será 0!
Agora una o 1º dígito verificador aos 9 primeiros dígitos, e faça a mesma conta, agora com um número de 10 algarismos e o fator de multiplicação indo de 2 a 11:
 
4        9        1        1        2        2        5        3        4        3        *
11     10        9        8       7        6        5        4        3        2

44  + 90   +   9   +   8   +  14  +  12  +  25  +  12   +  12  +  6        =   232
 
Agora no mesmo procedimento, encontre o resultado da divisão por 11, no exemplo: 232/11=(21*11)+1, resto 1. Como o resto é menor que 2, o dígito será 0.
Agora encontramos os dois dígitos verficadores, 3 e 0.
Como eles batem com os dígitos passados no número, esse número (491122534-30) é um CPF válido.
 
A lógica do CNPJ é a mesma, porém o CNPJ é composto de 12 dígitos mais os dois verificadores.
Então, na 1ª conta multiplica-se o menos significativo por 2 e o mais significativo por 13. Une-se o 1º dígito verificar e refaz-se a conta com um número de 13 algarismos, onde multiplica-se o menos significativo por 2 e o mais significativo por 14.
 
 
Com as lógicas entendidas, vamos para o programa!
    
 
Vamo primeiro preparar todo o ambiente:
    1-) Crie uma pasta para esse programa;
    2-) Na pasta do programa crie uma pasta com o nome 'Tabelas';
    3-) Dentro da pasta 'Tabelas' coloque um arquivo do excel com o nome 'tabela.xls';
    4-) De volta à pasta do programa, coloque a DLL, para verificar as IE's; (baixe a DLL aqui);
 
Depois iremos modelar nosso programa:
    1-) Coloque um GroupBox, e altere as propriedades:
            Caption:    Tabela Original
            Name:        bxGeral
        Dentro desse GroupBox, adicione um StringGrid e um Button, e altere as propriedades para:
 
        StringGrid:
            ColCount:     4
            FixedCols:    0
            FixedRows:  1
            Name:            tabelaGeral
            RowCount:    2
 
        Button:
            Caption:    Carregar e verificar a tabela
            Name:       btnCarrega
 
    2-) No Form, ao lado do bxGeral, adicione outro GroupBox com 2 Labels, 2 Edits e um Button, com as seguintes propriedades:
 
        Label1:
            Caption:        Número
 
        Label2:
            Caption:        UF 
 
        Edit1:
            Name:        edtNum
            Text:           
 
        Edit2:
            Name:        edtUF
            Text:
 
        Button:
            Caption:  Verificar
            Name:     btnIndep
 
    3-) Abaixo dos GroupBox's, adicione uma progressbar e altere as propriedades:
            Name:        barraGera
 
    4-) Abaixo de barraGera, crie mais um GroupBox, e altere as seguintes propriedades:
            Caption:     Resultados
            Name:        bxResult
        Dentro desse GroupBox adicione mais 3 GroupBox's, com 1 StringGrid em cada um. Coloque também um Button abaixo de cada GroupBox.
        Abaixo, seguem as propriedades de cada um:
        
        GroupBox1:
            Caption:            CPF's
            Name:               bxCpf
 
        GroupBox2:
            Caption:            CNPJ's
            Name:               bxCnpj
 
        GroupBox3:
            Caption:            Incrições Estaduais
            Name:               bxIe
 
        StringGrid (do bxCPF):
            ColCount:     2
            FixedCols:    0
            FixedRows:  1
            Name:            tabCpfs
            RowCount:    2
 
        StringGrid (do bxCNPJ):
            ColCount:     2
            FixedCols:    0
            FixedRows:  1
            Name:            tabCnpj
            RowCount:    2
 
        StringGrid (do bxIe):
            ColCount:     2
            FixedCols:    0
            FixedRows:  1
            Name:            tabIe
            RowCount:    2
 
        Button (do bxCpf):
            Caption:    Salvar
            Name:       btnCpf
 
        Button (do bxCnpj):
            Caption:    Salvar
            Name:       btnCnpj
 
        Button (do bxIe):
            Caption:    Salvar
            Name:       btnIe
 
    5-) Por último adicione um Button no fim do Form com as propriedades:
            Caption:    Salvar em tabela única
            Name:       btnTds
 
Seu Form deve ter ficado como a figura abaixo:
 
 
Agora o visual está pronto, abaixo uma imagem que ilustra como deve se apresentar o documento 'tabela.xls':
 
Como visto, deve-se seguir a seguinte ordem das colunas: CNPJ, CPF, IE e Estado. E na 1ª linha deve-se ter apenas os nomes dos campos. Nas próximas virão os valores de cada campo.
 
Agora que temos tudo pronto, irei explicar o funcionamento do programa:
Ao se abrir o programa, você pode pressionar o btnCarrega para que ele leia a tabela inteira e valide campo a campo. Assim ele separa  CNPJ, IE e CPF em cada StringGrid do bxResult. Esses StringGrids terão duas colunas: Correto e Incorreto. Se quiser salvar apenas uma tabela com CPF, por exemplo, clique no btnCpfs. Para salvar todas as tabelas clique no btnTds.
Você também poderá verificar apenas um número, para isso preencha os Edits do bxIndep e clique no btnIndep. CPF e CNPJ dispensam a UF, mas para IE é necessário colocar uma UF.
 
Agora vamos ao que interessa: a programação!
 
A cláusula uses deve ficar da seguinte forma:

uses

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Grids, ComObj, ComCtrls,
  ExtCtrls, Gauges, Buttons, WinProcs, WinTypes, ExcelXP;
 
Depois vamos criar um tipo para a DLL, abaixo da cláusula uses adicione o código:

Type da DLL

type
  TConsisteInscricaoEstadual  = function (const Insc, UF: String): Integer; stdcall;
  TVersaoDLL                  = function : Integer; stdcall;
 
Depois adicione a varíavel global dirEXE (do tipo String) na cláusula var.
 
Esse programa terá 10 funções, abaixo segue o protótipo delas, que deve estar na cláusula private do Form:

Private

private
    { Private declarations }
    function StrDir(txt : String; tam : Integer) : String;
    function StrEsq(txt : String; tam : Integer) : String;
    function stringSohNum (const val : string) : String;

    procedure geraXls(const tabela : TStringGrid; const nomeXls : String);
    function XlsToStringGrid(AGrid: TStringGrid; AXLSFile: string): Boolean;

    function validaCnpj (numCnpj : String) : String;
    function cnpj (cnpj : String) : Boolean;
    function validaCpf(numCpf : string) : string;
    function cpf (cpf : String) : Boolean;
    function validaIe(const numIe : string;const UF : string ) : Boolean;
 
As 3 primeiras funções são para manipulação de Strings, o código delas fica da seguinte forma:

Funções de Strings

{retornando os 'tam' dígitos à direita de uma String}
function TForm1.StrDir(txt : String; tam : Integer) : String;
begin
  Result := Copy(txt,StrLen(PChar(txt))+1-tam,tam);
end;

{retornando os 'tam' dígitos à esquerda de uma String}
function TForm1.StrEsq(txt : String; tam : Integer) : String;
begin
  Result := Copy(txt,0,tam);
end;

{deixando a String apenas com números}
function TForm1.stringSohNum (const val : string) : String;
var
  ret : string;
  i : Integer;
begin
  Result := '';
  for i:=1 to length(val) do
  begin
    if (val[i] in ['0'..'9']) then
      Result := Result+val[i];
  end;
end;

 
As próximas duas são em ordem: para salvar um StringGrid em um XLS, e para ler um XLS em um StringGrid. O código delas é:

StringGrid para XLS, e vice-versa

procedure TForm1.geraXls(const tabela : TStringGrid; const nomeXls : String);
var
  strLst : TStringList;
  linha : String;
  i,j : Integer;
begin
  {criando um StringList}
  strLst := TStringList.Create;
  {preparando a ProgressBar}
  barraGera.Visible := True;
  barraGera.Position := 0;
  barraGera.Max := tabela.RowCount;
  {varrendo campo a campo do StringGrid}
  for i:=0 to tabela.RowCount do
  begin
    linha := '';
    for j:=0 to tabela.ColCount-1 do
      {adicionando cada linha do StringGrid a uma linha do StringList
       as células são separadas por TAB (#9), assim basta salvar
       o texto como .xls, que o Excel lê normalmente}
      linha := linha+tabela.Cells[j,i]+#9;
    strLst.Append(linha);
    barraGera.Position := i;
  end;
  barraGera.Position := 0;
  barraGera.Visible := False;
  {salvando o StringList num arquivo com extensão XLS}
  strLst.SaveToFile(nomeXls);
  Sleep(200);
  {verificando se a tabela foi criada, se sim é exibido o caminho dela}
  if FileExists(nomeXls) then
    ShowMessage('A tabela foi criada com sucesso na pasta:'+#10+#13+nomeXls)
  {senão exibe-se um erro}
  else
    ShowMessage('Houve um problema durante a criação da tabela!')
end;

function TForm1.XlsToStringGrid(AGrid: TStringGrid; AXLSFile: string): Boolean;
const
    xlCellTypeLastCell = $0000000B;
var
    XLApp, Sheet: OLEVariant;
    RangeMatrix: Variant;
    x, y, k, r: Integer;
begin
  Result:=False;
  //Cria Excel- OLE Object
  XLApp:=CreateOleObject('Excel.Application');
  try
    //Esconde Excel
    XLApp.Visible:=False;
    //Abre o Workbook
    XLApp.Workbooks.Open(AXLSFile);
    Sheet:=XLApp.Workbooks[ExtractFileName(AXLSFile)].WorkSheets[1];
    Sheet.Cells.SpecialCells(xlCellTypeLastCell, EmptyParam).Activate;
    //Pega o número da última linha
    x:=XLApp.ActiveCell.Row;
    //Pega o número da última coluna
    y:=XLApp.ActiveCell.Column;
    //Seta StringGrid linha e coluna
    AGrid.RowCount:=x;
    AGrid.ColCount:=y;
    //Associaca a variant WorkSheet com a Variant do Delphi
    RangeMatrix:=XLApp.Range['A1', XLApp.Cells.Item[X, Y]].Value;
    //Cria o loop para listar os registros no TStringGrid
    k:=1;
    repeat
      for r:=1 to y do
        AGrid.Cells[(r - 1),(k - 1)]:=RangeMatrix[K, R];
      Inc(k,1);
    until k > x;
    RangeMatrix:=Unassigned;
  finally
    //Fecha o Excel
    if not VarIsEmpty(XLApp) then begin
      XLApp.Quit;
      XLAPP:=Unassigned;
      Sheet:=Unassigned;
      Result:=True;
    end;
  end;
end;

 
Agora só restam as funções que validam os números dos documentos, os códigos estão abaixo:

Funções de validação

{essa função não valida o CNPJ de fato
 ela retorna a comparação do resultado da função validaCNPJ()
 com os dois últimos dígitos}
function TForm1.cnpj (cnpj : String) : Boolean;
begin
  if (StrLen(PChar(cnpj))=14) then
    Result := validaCnpj(StrEsq(cnpj,12))=StrDir(cnpj,2)
  else
    Result := False;
end;

{assim como a função cnpj(), esta compara apenas os dígitos
 verificadores com o resultado da função validaCPF()}
function TForm1.cpf (cpf : String) : Boolean;
begin
  if (StrLen(PChar(cpf))=11) then
    Result := validaCpf(StrEsq(cpf,9))=StrDir(cpf,2)
  else
    Result := False;
end;

{aqui é feita a validação do CNPJ}
function TForm1.validaCnpj (numCnpj : String) : String;
var
  resto : Integer;

  {essa função varre a String que contém o CNPJ
   multiplicando dígito a dígito pelo valor adequado, e reorna a
   soma total}
  function loop(str : String) : Integer;
  var
    mul,i : Integer;
  begin
    mul := 2;
    Result := 0;
    for i:=StrLen(PChar(str)) downto 1 do
    begin
      if (mul=10) then
        mul := 2;
      Result := Result+(StrToInt(str[i])*mul);
      Inc(mul);
    end;
  end;

begin
  {o 1º dígito verificado é achado aqui}
  resto := loop(numCnpj) mod 11;
  if resto<2 then
    resto := 0
  else
    resto := 11-resto;
  numCnpj := numCnpj+IntToStr(resto);
  {agora acha-se o 2º dígito verificador}
  resto := loop(numCnpj) mod 11;
  if resto<2 then
    resto := 0
  else
    resto := 11-resto;
  numCnpj := numCnpj+IntToStr(resto);
  {retorna-se apenas os 2 útlimos dígitos, que são os verificadores}
  Result := StrDir(numCnpj,2);
end;

function TForm1.validaCpf(numCpf : string) : string;
var
  resto : Integer;

  {essa função varre a String que contém o CPF
   multiplicando dígito a dígito pelo valor adequado, e reorna a
   soma total}
  function loop (str : String) : Integer;
  var
    mul,i : Integer;
  begin
    mul := 2;
    Result := 0;
    for i:=StrLen(PChar(str)) downto 1 do
    begin
      Result := Result+(StrToInt(str[i])*mul);
      Inc(mul);
    end;
  end;

begin
  {o 1º dígito verificado é achado aqui}
  resto := loop(numCpf) mod 11;
  if resto<2 then
    resto := 0
  else
    resto := 11-resto;
  numCpf := numCpf+IntToStr(resto);
  {agora acha-se o 2º dígito verificador}
  resto := loop(numCpf) mod 11;
  if resto<2 then
    resto := 0
  else
    resto := 11-resto;
  numCpf := numCpf+IntToStr(resto);
  {retorna-se apenas os 2 útlimos dígitos, que são os verificadores}
  Result := StrDir(numCpf,2);
end;

{validando uma IE}
function TForm1.validaIe(const numIe : string;const UF : string ) : Boolean;
var
  IRet, IOk, IErro, IPar    : Integer;
  LibHandle                 : THandle;
  ConsisteInscricaoEstadual : TConsisteInscricaoEstadual;
begin
  try
    {procurando pela DLL}
    LibHandle :=  LoadLibrary (PChar (Trim ('DllInscE32.Dll')));
    if  LibHandle <=  HINSTANCE_ERROR then
      raise Exception.Create ('Dll não carregada');
    @ConsisteInscricaoEstadual  :=  GetProcAddress (LibHandle,
                                                    'ConsisteInscricaoEstadual');
    if  @ConsisteInscricaoEstadual  = nil then
      raise Exception.Create('Entrypoint Download não encontrado na Dll');
    {fazendo a validação da IE através da DLL}
    IRet := ConsisteInscricaoEstadual (numIe,UF);
    Result := False;
    {quando a IE for válida
     a função ConsisteInscricaoEstadual() retorna 0}
    if IRet=0 then
      Result := True;
  finally
    {limpando a memória}
    FreeLibrary (LibHandle);
  end;
end;

 
Está quase tudo pronto, faltam os eventos OnCreate do Form e OnClick dos Buttons. Abaixo estão todos os códigos, pelo nome das procedures você conseguirá identificar a qual objeto elas se encaixam:

Eventos

procedure TForm1.FormCreate(Sender: TObject);
begin
  {extraindo o diretório onde o aplicativo está rodando}
  dirExe := ExtractFileDir(Application.ExeName);
end;

procedure TForm1.btnCarregaClick(Sender: TObject);
 var
  i,j : Integer;
  planilha : Variant;
begin
  {verificando se a tabela existe}
  if FileExists(dirExe+'\Tabelas\tabela.xls') then
  begin
    {carregando a tabela para o StringGrid}
    XlsToStringGrid(tabelaGeral,ExtractFileDir(Application.ExeName)+'\Tabelas\tabela.xls');
    btnCarrega.Enabled := False;
    barraGera.Visible := True;
    barraGera.Position := 0;
    {setando a propriedade Max da ProgressBar}
    barraGera.Max := tabelaGeral.RowCount;
    {adicionando as colunas aos outros StringGrids}
    tabCpfs.Cells[0,0] := 'Correto';
    tabCpfs.Cells[1,0] := 'Incorreto';
    tabCnpj.Cells[0,0] := 'Correto';
    tabCnpj.Cells[1,0] := 'Incorreto';
    tabIe.Cells[0,0] := 'Correto';
    tabIe.Cells[1,0] := 'Incorreto';
    {varrendo toda a tabela}
    for i:=1 to tabelaGeral.RowCount do begin
      {apenas adiciona-se valor para as tabelas de CPF, CNPJ e IE
       se o valor não for vazio}
      if not (tabelaGeral.Cells[2,i]='') then begin
        {se for um CPF válido, adiciona-se à coluna CORRETOS}
        if cpf(tabelaGeral.Cells[2,i]) then
          tabCpfs.Cells[0,i] := tabelaGeral.Cells[2,i]
        {senão, adiciona-se à coluna INCORRETOS}
        else
          tabCpfs.Cells[1,i] := tabelaGeral.Cells[2,i];
      end;
      {o mesmo processo dos CPF's para os CNPJ's...}
      if not (tabelaGeral.Cells[0,i]='') then begin
        if cnpj(stringSohNum(tabelaGeral.Cells[0,i])) then
          tabCnpj.Cells[0,i] := tabelaGeral.Cells[0,i]
        else
          tabCnpj.Cells[1,i] := tabelaGeral.Cells[0,i];
      end;
      {e também para as IE's}
      if not (tabelaGeral.Cells[1,i]='') then begin
        if validaIe(stringSohNum(tabelaGeral.Cells[1,i]),tabelaGeral.Cells[3,i]) then
          tabIe.Cells[0,i] := tabelaGeral.Cells[1,i]
        else
          tabIe.Cells[1,i] := tabelaGeral.Cells[1,i];
      end;
      {adicionando novas linhas às tabelas}
      tabIe.RowCount := tabIe.RowCount+1;
      tabCnpj.RowCount := tabCnpj.RowCount+1;
      tabCpfs.RowCount := tabCpfs.RowCount+1;
      {andando com a ProgressBar}
      barraGera.Position := i;
    end;
    btnCarrega.Enabled := True;
    barraGera.Visible := False;
  end
  {caso a tabela não seja encontrada}
  else
    ShowMessage('Não foi possível ler a tabela!'+#10+#13+
                'Certifique-se de que a tabela '+dirExe+
                '\Tabelas\tabela.xls realmente existe!');
end;

{salvando uma tabela apenas com CNPJ's}
procedure TForm1.btnCnpjClick(Sender: TObject);
begin
  btnCnpj.Enabled := False;
  geraXls(tabCnpj,dirExe+'\Tabelas\CNPJ.xls');
  btnCnpj.Enabled := True;
end;

{salvando uma tabela apenas com CPF's}
procedure TForm1.btnCpfClick(Sender: TObject);
begin
  btnCpf.Enabled := False;
  geraXls(tabCpfs,dirExe+'\Tabelas\CPFS.xls');
  btnCpf.Enabled := True;
end;

{salvando uma tabela apenas com IE's}
procedure TForm1.btnIeClick(Sender: TObject);
begin
  btnIe.Enabled := False;
  geraXls(tabIe,dirExe+'\Tabelas\INSCEST.xls');
  btnIe.Enabled := True;
end;

{validando um número único}
procedure TForm1.btnIndepClick(Sender: TObject);
begin
  {verifica-se se o número é uma IE}
  if validaIe(edtNum.Text,edtUF.Text) then
    ShowMessage('Inscrição válida')
  {senão, verifica-se se é um CPF}
  else if cpf(edtNum.Text) then
    ShowMessage('CPF válido')
  {senão, ainda verifica-se se é um CNPJ}
  else if cnpj(edtNum.Text) then
    ShowMessage('CNPJ válido')
  {se não é IE, nem CPF e nem CNPJ: retorna-se um erro}
  else
    ShowMessage('O número não corresponde a um CPF, CNPJ ou IE válidos');
end;

{salvando todas as tabelas um único arquivo}
procedure TForm1.btnTdsClick(Sender: TObject);
var
  strLst : TStringList;
  linha : String;
  i,j : Integer;
begin
  btnTds.Enabled := False;
  strLst := TStringList.Create;
  barraGera.Visible := True;
  barraGera.Position := 0;
  barraGera.Max := tabelaGeral.RowCount;
  strLst.Append('CNPJ'+#9+'I.E.'+#9+'CPF');
  for i:=1 to tabelaGeral.RowCount do
  begin
    linha := '';
    linha := linha+tabCnpj.Cells[0,i]+#9+tabIe.Cells[0,i]+#9+
             tabCpfs.Cells[0,i];
    strLst.Append(linha);
    barraGera.Position := i;
  end;
  barraGera.Position := 0;
  barraGera.Visible := False;
  strLst.SaveToFile(dirExe+'\Tabelas\CORRETOS.xls');
  Sleep(200);
  if FileExists(dirExe+'\Tabelas\CORRETOS.xls') then
    ShowMessage('A tabela foi criada com sucesso na pasta:'+#10+#13+
                dirExe+'\Tabelas\CORRETOS.xls')
  else
    ShowMessage('Houve um problema durante a criação da tabela!');
  btnTds.Enabled := True;
end;

 
Agora está tudo pronto!   =D
 
Você deve prestar atenção no seguinte: o programa não funciona no ambiente de programação! Acho que DLL retorna algum valor inválido e a execução é parada, mas se você rodar por fora do Delphi funciona perfeitamente.
Para quem quiser, abaixo está um arquivo zipado com os fontes, a DLL, e uma tabela de teste.
 
 
 
Adolfo T. Carvalho
Comments