sexta-feira, 7 de janeiro de 2011

Conexão com Balanças - Entendo os Códigos Emitidos pela Balança


Como nesse artigo o objetivo é explicar como funcionam os códigos emitidos pela balanças eletrônicas, vou postar uma dúvida que eu peguei em um fórum de automação comercial que participo.

Pergunta:
"Gostaria de entender os Códigos Emitidos pelas Balanças Computadoras (Platina, Prix, etc)
Tipo todas elas emitem o Código com inicio 2 então no meu sistema já sei se o recebido tiver 2 no inicio isso indicado que é algo na balança. até ai tudo bem...
Porem não consegui entender os outros dígitos que estão junto...
Se não estou enganado o ultimo seria digito verificador outros seriam o Código do Produto e outros seriam o total do produto.
o Meu problema esta ai abstrair o restante...
Também sei que tem tamanhos de códigos diferentes alguns com 4 dígitos outros com 6 dígitos mas não consegui ligar muito as coisas não"
Resposta:
Pode haver algumas variações na etiqueta impressa. Essas variações podem ser:
A - 2 C C C C 0 T T T T T T DV
B - 2 C C C C 0 0 P P P P P DV
C - 2 C C C C C T T T T T T DV
D - 2 C C C C C 0 P P P P P DV
E - 2 C C C C C C P P P P P DV
Onde:
2 - Digito 2 sempre
C - Código do Produto
0 - Espaço, não utilizado
T - Valor Total a Pagar
P - Peso ou Quantidade
DV- Digito Verificador EAN-13
Obs: Estes modelos podem ser configurados no programa que acompanha as balanças.
Para que o seu sistema saiba qual o tipo do código de barras que será impresso na balança, ou seja, para que você saiba como tratar no seu sistema, temos que criar uma tela de configuração para armazenar as posições da etiqueta.
Veja na figura abaixo um exemplo que serve para configurarmos corretamente as variações que podem acontecer nas etiquetas.
Com isso tudo configurado, quando o caixa (funcionário), por exemplo, passar um código de barras de um produto no PDV, o mesmo vai saber em qual posição está o peso do produto, código, preço e etc.
O que poderíamos fazer é verificar se o código de barras começa com o digito 2. Se for, já sabemos que trata-se de um código vindo da balança. Agora só nos resta pegarmos as informações que a etiqueta traz.

Ex:
Codigo de balança -> 2 0 1 0 1 0 0 0 0 0   9  0  7
Posições          -> 1 2 3 4 5 6 7 8 9 10 11 12 13
1 - prefixo balança;
2 a 5 ou 7 - código produto;
8 a 12 - preço na etiqueta


Conclusão

Bom, esse artigo foi para explicar como funcionam os códigos de barras impressos pelas balanças eletrônicas. Nos próximos artigos vou apresentar o componente acbrBal, que é para Leitura de informações de Balanças eletrônicas (Filizola, Toledo, Urano, etc) e que faz parte do projeto open source Acbr.
Para realizar a comunicação serial o projeto acbr usa a classe SynaSer
Projeto: Ararat Synapse (http://www.ararat.cz/synapse/)

Sérgio de Castro Guedes 
Contato: sergio_rj45@yahoo.com.br 
Programador com experiência em banco de dados MSSQL, Firebird e 
Oracle. Moderador de Conteúdo do Fórum, Editor Técnico e Colunista do Portal Active Delphi.
Administrador do Projeto Open Source Sia Consult (Sistema Comercial ERP).
Membro do projeto Acbr (Automação Comercial Brasil) e Desenvolvedor da VAP Informática, umas das maiores empresas de software de Manaus AM.

Referencias
- André Ferreira de Moraes - Membro do Projeto Acbr (http://acbr.sourceforge.net)
- Fórum do Projeto ACbr (http://www.forumweb.com.br/foruns/index.php?showforum=465)

Protegendo seu sistema contra cópias ilegais


Apresentarei aqui uma técnica que utiliza o serial lógico de uma unidade de disco do computador, que apesar de poder ser modificada após uma formatação, por exemplo, já é um bom começo!
Bom, vamos lá. Tendo em vista a preocupação dos colegas com pirataria e cópias ilegais, desenvolvi um código que verifica o número de série de um determinado dispositivo conectado ao computador e faz uma comparação.
Abaixo há uma função que pega o número de serie do CD que esta na respectiva unidade. O sistema só irá rodar se aquele determinado CD estiver no drive. Tanto faz se vc colocar o CD na unidade D, E ou F, a função irá verificar cada unidade a procura do número de serie correto e caso não o ache retorna false. Apesar da idéia ser criada com base em um CD, pode ser adaptada para o uso com HD's.
Para testar o código, você precisará acrescenta rum label e um button em seu form. Em seguida, implementar a funçãotbVolSerial, a procedure VerificalSerialH e o evento onClick do botão, conforme abaixo:
// função que pega o numero serial do dispositivo 
function Tform2.tbVolSerial(const Drive: Char; Path: PChar): Cardinal; 
var 
  MaxCompLen, FileSysFlag, PrevErrorMode: Cardinal;
begin
  if Path = nil then 
    Path := PChar(Drive + ':');
  PrevErrorMode := SetErrorMode(SEM_FAILCRITICALERRORS);
  try
    if not GetVolumeInformation(PChar(Path), nil, 0, @Result, MaxCompLen,
      FileSysFlag, nil, 0) then 
      Result := 0;
  finally 
    SetErrorMode(PrevErrorMode);
  end; 
end;
 
//Esta função faz a varredura em todos os dispositivos a
//procura do serial valido. retorna true ou false. 
procedure Tform2.VerificaSerialH(Driver: char); 
const 
  drive: array[0..25] of char = (
    'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
    'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
    'r', 's', 't', 'u', 'w', 'v', 'x', 'y', 'z'); 
var 
  j: integer;
  ini: TIniFile;
  SerialHardware, AdquireSerial: string;
  erro: Boolean;
begin
  if not FileExists('.serial.ini') then
  begin 
    AdquireSerial := IntToStr(tbVolSerial(Driver, nil));
    ini := TIniFile.Create('.serial.ini');
    ini.WriteString('serial', 'Hardware', AdquireSerial);
    ini.Free;
  end
  else
  begin 
    ini := TIniFile.Create('.serial.ini');
    SerialHardware := ini.ReadString('serial', 'Hardware', '');
    ini.free;
  end;
 
  for j := 0 to 25 do
  begin
    if (IntToStr(tbVolSerial(Drive[j], nil))) = SerialHardware then
    begin 
      Erro := false;
      Label1.Caption := 'Original';
      break;
    end
    else 
      Erro := true;
  end;
 
  if Erro then    Label1.Caption := 'Pirata' 
end;
 
procedure Tform2.Button1Click(Sender: TObject); 
begin 
  Label1.Caption := 'Este sistema é...';
  Label1.Repaint;
  VerificaSerialH('G'); 
end;

quinta-feira, 6 de janeiro de 2011

Convertendo a 1ª letra de um edit para maiúsculas

Esta dica é útil para ser utilizada no cadastramanto de nomes.

Se o operador do sistema esquecer de colocar a primeira letra de um nome em minúscula, automaticamente a letra ficará maiúscula.

Para que isto ocorra, coloque um objeto edit no formulário e no seu evento OnChange insira o seguinte código:

var
Variavel : Integer;

begin
With Edit1 do
if Text <> '' then
begin
OnChange := NIL;
Variavel := SelStart;
Text := UpperCase(Copy(Text,1,1))+LowerCase(Copy(Text,2,Length(Text)));
SelStart := Variavel;
OnChange := Edit1Change;
end;
end;

Como gerar senhas aleatórias

Segue abaixo rotina para gerar senhas aleatórias.

procedure TForm1.Button1Click(Sender: TObject);
var
i:integer;
const
str='1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ';
max=6;
begin
Edit1.Text:='';
for i:=1 to max do
begin
   Edit1.Text:=Edit1.Text+str[random(length(str))+1];
end;
end.

Checagem de CEP por Estado

Verifica se um CEP é valido ou não, passando o Estado e o Cep como parâmetro.



function ChecaCEP(cCep:String ; cEstado:String): Boolean;
var
cCEP1 : Integer;
begin
cCep := copy(cCep,1,5) + copy(cCep,7,3);
cCEP1 := StrToInt(copy(cCep,1,3));
if Length(trim(cCep)) > 0 then
   begin
   if (StrToInt(cCep) <= 1000000.0) then
       begin
       MessageDlg('CEP tem que ser maior que [01000-000]',mtError,[mbOk],0);
       Result := False
       end
   else
   begin
     if Length(trim(copy(cCep,6,3))) < 3 then Result := False else
     if (cEstado = 'SP') and (cCEP1 >= 10 ) and (cCEP1 <= 199) then Result := True else
     if (cEstado = 'RJ') and (cCEP1 >= 200) and (cCEP1 <= 289) then Result := True else
     if (cEstado = 'ES') and (cCEP1 >= 290) and (cCEP1 <= 299) then Result := True else
     if (cEstado = 'MG') and (cCEP1 >= 300) and (cCEP1 <= 399) then Result := True else
     if (cEstado = 'BA') and (cCEP1 >= 400) and (cCEP1 <= 489) then Result := True else
     if (cEstado = 'SE') and (cCEP1 >= 490) and (cCEP1 <= 499) then Result := True else
     if (cEstado = 'PE') and (cCEP1 >= 500) and (cCEP1 <= 569) then Result := True else
     if (cEstado = 'AL') and (cCEP1 >= 570) and (cCEP1 <= 579) then Result := True else
     if (cEstado = 'PB') and (cCEP1 >= 580) and (cCEP1 <= 589) then Result := True else
     if (cEstado = 'RN') and (cCEP1 >= 590) and (cCEP1 <= 599) then Result := True else
     if (cEstado = 'CE') and (cCEP1 >= 600) and (cCEP1 <= 639) then Result := True else
     if (cEstado = 'PI') and (cCEP1 >= 640) and (cCEP1 <= 649) then Result := True else
     if (cEstado = 'MA') and (cCEP1 >= 650) and (cCEP1 <= 659) then Result := True else
     if (cEstado = 'PA') and (cCEP1 >= 660) and (cCEP1 <= 688) then Result := True else
     if (cEstado = 'AM') and ((cCEP1 >= 690) and (cCEP1 <= 692) or (cCEP1 >= 694) and 
(cCEP1 <= 698)) then Result := True else
     if (cEstado = 'AP') and (cCEP1 = 689) then Result := True else
     if (cEstado = 'RR') and (cCEP1 = 693) then Result := True else
     if (cEstado = 'AC') and (cCEP1 = 699) then Result := True else
     if ((cEstado = 'DF') or (cEstado = 'GO')) and (cCEP1 >= 700)and(cCEP1 <= 769)then 
Result := True else
     if (cEstado = 'TO') and (cCEP1 >= 770) and (cCEP1 <= 779) then Result := True else
     if (cEstado = 'MT') and (cCEP1 >= 780) and (cCEP1 <= 788) then Result := True else
     if (cEstado = 'MS') and (cCEP1 >= 790) and (cCEP1 <= 799) then Result := True else
     if (cEstado = 'RO') and (cCEP1 = 789) then Result := True else
     if (cEstado = 'PR') and (cCEP1 >= 800) and (cCEP1 <= 879) then Result := True else
     if (cEstado = 'SC') and (cCEP1 >= 880) and (cCEP1 <= 899) then Result := True else
     if (cEstado = 'RS') and (cCEP1 >= 900) and (cCEP1 <= 999) then Result := True else
Result := False
   end;
 end
else
   begin
   Result := True;
   end
end;

Controle sobre a digitação

Quando alguém esta  digitando  algum valor que posteriormente será utilizado para calculo alguns cuidados são necessários, esse procedimento  ValidaKey  deve ser ligado no OnChange do TDBEdit para checar qual foi a tecla digitada.

procedure ValidaKey(Const Sender:TObject; var key: char);
begin
       if not(key in ['0'..'9','.',',',#8,#13]) then key := #0;
       if key in [',','.'] then key := DecimalSeparator;
       if key = DecimalSeparator then
                  if pos(key,TEdit(Sender).Text) <> 0 then key := #0;
end;


       if not(key in ['0'..'9','.',',',#8,#13]) then key := #0;

Se algum numero, ponto, virgula, BackSpace ou Enter for digitado então pode passar normalmente, caso contrario a tecla pressionada é ignorada.

       if key in [',','.'] then key := DecimalSeparator;

Se ponto ou virgula, assume como separador decimal.

        if key = DecimalSeparator then
          if pos(key,TEdit(Sender).Text) <> 0 then key := #0;

O separador decimal so pode ser digitado uma unica vez, na tentativa de uma segunda digitação ignora-se o símbolo.

Observem que o mais importante aqui é o conceito utilizado, o fato de se interceptar os caracteres digitados pelo usuário e poder filtrar esses caracteres para evitar uma entrada de  dados inconsistente. O exemplo de numero e símbolos não é conclusivo, uma vez que o mesmo efeito poderia ter sido obtido com a aplicação de uma mascara.

Obtendo as informações de versão dos arquivos

Um dos recursos disponibilizados pelo Delphi é a customização das informações de versão a serem "anexadas" na linkagem.
Pouco utilizado, este recurso é muito interessante, pois possibilita o cadastro de diversas informações sobre o arquivo gerado, como: número de versão, nome do produto, nome interno do arquivo, nome da empresa, etc.

Podemos alterar as informações na página "Version Info", da página "Project Options":

Atenção com o item "Auto-increment build number": ele só será incrementado automaticamente quando for executada a opção "Build All" para compilar o projeto.
Porém, não existem rotinas "prontas" para obtermos estas informações. É necessário fazermos chamadas diretamente a API Win32, mais espeficamente, para as funções como a "GetFileVersionInfo" e a "VerQueryValue".
Abaixo encontramos uma função, a "FileVerInfo", que exemplifica o processo de obtenção das informações. Ela irá retornar "True" caso o arquivo informado no parâmetro "FileName" possuir as informações de versão, e devolverá por referência um "TStringList" contendo as informações.


//Código

function FileVerInfo(const FileName: string; var FileInfo: TStringList): Boolean;
const
    Key: array[1..9] of string =('CompanyName', 'FileDescription', 'FileVersion', 'InternalName', 'LegalCopyright', 'OriginalFilename', 'ProductName', 'ProductVersion', 'Comments');
KeyBr: array [1..9] of string = ('Empresa', 'Descricao', 'Versao do Arquivo', 'Nome Interno', 'Copyright', 'Nome Original do Arquivo', 'Produto', 'Versao do Produto', 'Comentarios');
var
Dummy : THandle;
BufferSize, Len : Integer;
Buffer : PChar;
LoCharSet, HiCharSet : Word;
Translate, Return : Pointer;
StrFileInfo, Flags : string;
TargetOS, TypeArq : string;
FixedFileInfo : Pointer;
i : Byte;

begin
Result := False;
{ Obtemos o tamanho em bytes do "version  information" }
BufferSize := GetFileVersionInfoSize(PChar(FileName), Dummy);
if BufferSize <> 0 then
begin
   GetMem(Buffer, Succ(BufferSize));
   try
     if GetFileVersionInfo(PChar(FileName), 0, BufferSize,
       Buffer) then
     { Executamos a função "VerQueryValue" e conseguimos informações sobre o idioma /character-set }
     if VerQueryValue(Buffer, '\VarFileInfo\Translation', Translate, UINT(Len)) then
     begin
       LoCharSet := LoWord(Longint(Translate^));
       HiCharSet := HiWord(Longint(Translate^));
       for i := 1 to 9 do
       begin
         { Montamos a string de pesquisa }
         StrFileInfo := Format('\StringFileInfo\0%x0%x\%s', [LoCharSet, HiCharSet, Key[i]]);
         { Adicionamos cada key pré-definido }
         if VerQueryValue(Buffer,PChar(StrFileInfo), Return, UINT(Len)) then
         FileInfo.Add(KeyBr[i] + ': ' + PChar(Return));
       end;
       if VerQueryValue(Buffer,'\',FixedFileInfo, UINT(Len))
         then
         with TVSFixedFileInfo(FixedFileInfo^) do
       begin
         Flags := '';
         {Efetuamos um bitmask e obtemos os "flags" do arquivo}
       if (dwFileFlags and VS_FF_DEBUG) = VS_FF_DEBUG then
           Flags := Concat(Flags,'*Debug* ');
       if (dwFileFlags and VS_FF_SPECIALBUILD) = VS_FF_SPECIALBUILD then
                Flags := Concat(Flags, '*Special Build* ');
       if (dwFileFlags and VS_FF_PRIVATEBUILD) = VS_FF_PRIVATEBUILD then
                Flags := Concat(Flags, '*Private Build* ');
       if (dwFileFlags and VS_FF_PRERELEASE) = VS_FF_PRERELEASE then
                Flags := Concat(Flags, '*Pre-Release Build* ');
       if (dwFileFlags and VS_FF_PATCHED) = VS_FF_PATCHED then
                 Flags := Concat(Flags, '*Patched* ');
       if Flags <> '' then FileInfo.Add('Atributos: ' + Flags);
       TargetOS := 'Plataforma (OS): ';
       { Plataforma }
       case dwFileOS of
         VOS_UNKNOWN :
           TargetOS := Concat(TargetOS, 'Desconhecido');
         VOS_DOS :
           TargetOS := Concat(TargetOS, 'MS-DOS');
         VOS_OS216 :
           TargetOS := Concat(TargetOS, '16-bit OS/2');
         VOS_OS232 :
           TargetOS := Concat(TargetOS, '32-bit OS/2');
         VOS_NT    :
           TargetOS := Concat(TargetOS, 'Windows NT');
         VOS_NT_WINDOWS32, 4:
           TargetOS := Concat(TargetOS, 'Win32 API');
         VOS_DOS_WINDOWS16:
           TargetOS := Concat(TargetOS, '16-bit Windows ',
           'sob MS-DOS');
         else
            TargetOS := Concat(TargetOS, 'Fora do Padrão. Código: ', IntToStr(dwFileOS));
      end;

       FileInfo.Add(TargetOS);
       TypeArq := 'Tipo de Arquivo: ';
       { Tipo de Arquivo }
       case dwFileType of
         VFT_UNKNOWN :
           TypeArq := Concat(TypeArq,'Desconhecido');
         VFT_APP : TypeArq := Concat(TypeArq,'Aplicacao');
         VFT_DLL : TypeArq := Concat(TypeArq,'Dynamic-Link Lib.');
         VFT_DRV : begin
           TypeArq := Concat(TypeArq,'Device driver - Driver ');
           case dwFileSubtype of VFT2_UNKNOWN : TypeArq := Concat (TypeArq, 'Desconhecido');
             VFT2_DRV_PRINTER : TypeArq := Concat(TypeArq,'de Impressao');
             VFT2_DRV_KEYBOARD : TypeArq := Concat(TypeArq,'de Teclado');
             VFT2_DRV_LANGUAGE : TypeArq := Concat(TypeArq,'de Idioma');
             VFT2_DRV_DISPLAY : TypeArq := Concat(TypeArq,'de Vídeo');
             VFT2_DRV_MOUSE : TypeArq := Concat(TypeArq,'de Mouse');
             VFT2_DRV_NETWORK : TypeArq := Concat(TypeArq,'de Rede');
             VFT2_DRV_SYSTEM : TypeArq := Concat(TypeArq,'de Sistema');
             VFT2_DRV_INSTALLABLE : TypeArq := Concat(TypeArq,'Instalavel');
             VFT2_DRV_SOUND : TypeArq := Concat(TypeArq,'Multimida');
           end;
         end;
         VFT_FONT : begin
           TypeArq := Concat(TypeArq,'Fonte - Fonte ');
           case dwFileSubtype of VFT2_UNKNOWN : TypeArq := Concat(TypeArq,  'Desconhecida');
             VFT2_FONT_RASTER : TypeArq := Concat(TypeArq,'Raster');
             VFT2_FONT_VECTOR : TypeArq := Concat(TypeArq,'Vetorial');
             VFT2_FONT_TRUETYPE : TypeArq := Concat(TypeArq,'TrueType');
           end;
         end;
         VFT_VXD : TypeArq := Concat(TypeArq,'Virtual Device');
         VFT_STATIC_LIB
           : TypeArq := Concat(TypeArq,'Static-Link Lib.');
       end;
       FileInfo.Add(TypeArq);
     end;
   end;
   finally
     FreeMem(Buffer, Succ(BufferSize));
     Result := FileInfo.Text <> '';
   end;
end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  StrLst: TStringList;
begin
StrLst := TStringList.Create;
try
   FileVerInfo('C:\WINDOWS\SYSTEM\TAPI.DLL', StrLst);
   Memo1.Lines.Assign(StrLst);
finally
   StrLst.Free;
end;
end;

quarta-feira, 5 de janeiro de 2011

O que são componentes?

A livraria visual de componentes (VCL).
Um componente, em sua definição mais simples não é mais que um objeto descendente do tipo TComponent. Todos os componentes descendem na sua forma mais primitiva de TComponent, já que TComponent proporciona as características básicas que todo componente deve ter: capacidade de ser mostrado na paleta de componentes assim como de operar a nível de desenho de formulário.
Os componentes tornam fácil a programação em Delphi. Em vez de ter que operar a nível de unidades, o usuário de um componente simplesmente tem que colocar na posição desejada no seu form. Isso é tudo: o Delphi se encarrega do resto.
Todos os componentes formam parte da Hierarquia de objetos denominada Visual Component Library (VCL). Quando se cria um novo componente, este se deriva a partir de um componente existente (bem como TComponent ou algum outro mais especializado) e se junta a VCL.
Anatomia de um componente.
Propriedades, métodos e eventos.
Como já mencionado, um componente é um objeto, e como tal, consta de código e dados. Mas ao referirmos a estes não utilizaremos estos termos, só que falaremos de propriedades e métodos, assim como de eventos. A longo do curso iremos estudando em profundidade todos estes aspectos, mas teremos agora uma primeira aproximação:
Propriedades
As propriedades proporcionam ao usuário do componente um fácil acesso ao mesmo. Ao mesmo tempo, permite ao programador do componente "esconder" a estrutura de dados subjacente. Entre as vantagens de utilizar propriedades para usar o componente se podem citar:
As propriedades estão disponíveis em tempo de desenho. Deste modo o usuário do componente pode iniciar os valores das propriedades sem necessidade de escrever uma só linha de código.
As propriedades permitem a validação dos dados ao mesmo tempo de ser introduzidas. Assim se podem prevenir erros causados por valores inválidos.
Nos asseguram que desde o primeiro momento nossas propriedades tenham um valor válido, evitando o erro comum de fazer referência a uma variável que não tenha sido convenientemente iniciada.
Eventos
Os eventos são as conexões existentes entre um determinado evento e ao código escrito pelo programador de componentes. Assim por exemplo, quando ao evento clique do Mouse se poderia mostrar uma mensagem. Ao código que se executa quando se produz um determinado evento se denomina controlador de eventos (event handler) e normalmente é o próprio usuário do componente quem o escreve. Os eventos mais comuns já formam parte dos próprios componentes do Delphi (ações do Mouse, pressionamento do teclado...), mas é também possível definir novos eventos.
Métodos
Os métodos são os procedimentos e/ou funções que formam parte do componente. O usuário do componente os utiliza para realizar uma determinada ação ou para obter um valor determinado que não se pode acessar por meio de uma propriedade. Já que requerem execução de código, os métodos só estão disponíveis em tempo de execução.
Controle de acesso a um componente.
Declarações privadas, protegidas, públicas e publicadas.
Object Pacal dispõe de quatro níveis de controle de acesso para os campos, propriedades e métodos de um componente. Este controle de acesso permite especificar ao programador do componentes que parte do código pode acessar a que partes do objeto. Deste modo se define a interface do componente. É importante planejar bem esta interface, já que assim nossos componentes serão facilmente programáveis e reutilizáveis.
Ao menos que se especifique o contrário, os campos, propriedades e métodos que se unem a um objeto são do tipo publicados (published). Todos os níveis de controle de acesso operam a nível de unidades, é decidir se uma parte dum objeto é acessível ou não, em uma parte da unidade, é também acessível (ou não) em qualquer outra parte da unidade.
Privado: ocultando os detalhes de implementação
Declarando uma parte de um componente (bem como um campo, propriedade ou método) privado (private) provoca que essa parte do objeto seja invisível ao código externo a unidade na qual se declara o objeto. Dentro da unidade que contem a declaração, o código pode acessar a essa parte do objeto como se fosse público.
A principal utilidade das declarações privadas é que permitem ocultar os detalhes de implementação do componente ao usuário final do mesmo, já que estes não podem acessar a parte privada de um objeto. Deste modo se pode trocar a implementação interna do objeto sem afetar o código que havia escrito ao usuário.
Protegido: definindo a interface do programador.
Declarar uma parte de um componente como protegido (protected) provoca, o mesmo se declarado privado, que ao código externo a unidade não pode acessar a dita parte (se faz oculta ao código externo a a unidade). A diferença principal entre declarar uma parte de um objeto protegido ou privado é que os descendentes do componente podem fazer referência a essa parte.
Este comportamento é especialmente útil para a criação de componentes que vão se descender daquilo que criamos.
Público: definindo a interface em tempo de execução.
Todas partes de um objeto que declaremos públicas (public) poderiam ser referidas por qualquer código seja interno ou externo a própria unidade. Neste sentido, a parte pública identifica a interface em tempo de execução de nosso componente. Os métodos que o usuário do componente deve chamar devem ser declarados públicos, assim como também as propriedades de somente leitura, são somente válidas em tempo de execução.
As propriedades declaradas públicas não aparecerão no inspetor de objetos.
Esta seção é talvez a mais importante a considerar ao desenhar um componente. Quando se desenham componentes, se deve considerar cuidadosamente que métodos e propriedades devem ser públicas. Se o desenho é correto, este permitira retocar as estruturas de dados e métodos internos do componente sem ter que tocar a interface pública, que seguirá sendo a mesma para o usuário do componente.
Publicado: definindo a interface em tempo de desenho.
Ao declarar parte de um objeto publicado (published) ocorre que a parte pública e ademais gera informações em tempo de execução para dita parte. As propriedades declaradas publicadas aparecem no inspetor de objetos em tempo de desenho. E já que somente as partes publicadas aparecem no inspetor de objetos, estas partes definem a interface em tempo de desenho do nosso componente. Em geral somente se devem declarar publicadas propriedades e não funções ou procedimentos.
Passos necessários para criar um componente.
O criador de componentes.
Os passos necessários para criar um novo componente são os seguintes:
1. Criar uma unidade para o novo componente.
2. Derivar o novo componente a partir de outro existente, o qual servirá de base para as novas características desejadas.
3. Estabelecer as propriedades, eventos e métodos necessários ao novo componente.
4. Registrar o componente, incluindo os bitmaps adicionais, arquivos de ajuda, etc.
5. Instalar o novo componente na paleta de componentes.
De todos os passos citados, há um que é especialmente importante: a eleição do ascendente a partir da qual derivar o novo componente. Este passo é crucial, já que uma boa eleição do ascendente pode fazer a tarefa de criar novas propriedades e métodos realmente fácil. Como base para a eleição do ascendente, convém fazer notar as seguintes normas:
TComponent - O ponto de partida para os componentes não visuais.
TWinControl - O ponto de partida se é necessário que o componente disponha de handles.
TGraphicControl - Um bom ponto de partida para componentes visuais que não seja necessário que disponham de handles, posso dizer, que não recebam o foco. Esta classe dispõe do método Paint e de Canvas.
TCustomControl - O ponto de partida mais comum. Esta classe dispõe de window handle, eventos e propriedades comuns e, principalmente, canvas com o método Paint.
Bom, já sabemos como determinar o ponto de partida. Vejamos agora como criar a unidade que abrigará o componente. Há duas opções, criar a unidade manualmente ou deixar que o Delphi faça o trabalho "sozinho" utilizando o experto de componentes. Se optarmos pela primeira solução, basta dar um click em New Unit e poremos mãos a obra, mas deste modo teremos que fazer tudo manualmente: derivar o novo componente, registrá-lo, etc. Por isso é mais recomendável a segunda opção: utilizar o experto de componentes.
Para abrir o experto de componentes basta escolher File|New Component.
Aparecerá um quadro de diálogo que devemos complementar os seguintes campos:
Class Name: Aqui devemos especificar o nome do novo componente.
Ancestor type: Introduziremos aqui o ascendente a partir do qual derivaremos nosso componente.
Palette Page: Indicaremos aqui a página da paleta na qual queremos que apareça o novo componente.
Uma vez introduzidos estes campos, clique em Ok para que seja criado o código de nossa unidade. Se por exemplo termos introduzido os seguintes dados no experto de componentes:
Class Name: TMiComponente
Ancestor Type: TComponent
Palette Page: Curso
Ao clicar em Aceitar, Delphi geraria a seguinte unidade, pronta para introduzir as propriedades e métodos de nosso componente:
unit Unit1;
interface
uses
SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
Forms, Dialogs;
type
TMiComponente = class(TComponent)
private
{ Private declarations }
protected
{ Protected declarations }
public
{ Public declarations }
published
{ Published declarations }
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents( 'Curso', [TMiComponente]);
end;
end.
A partir daqui tudo consiste em introduzir as propriedades e métodos necessários para o funcionamento do nosso componente. Mas antes de nada convém fazer notar alguns aspectos:
Na clausula uses, Delphi originalmente as unidades standard. Se nosso componente não usa alguma delas podemos eliminá-la de dita clausula. Do mesmo modo, se utilizarmos algum procedimento ou função situado em outra unidade, devemos citar a dita unidade na clausula uses.
As declarações das propriedades, campos e métodos que vamos definindo, as citaremos na seção apropriada que corresponda, é decidir se as declararemos privadas, protegidas, pública o publicadas.
Delphi declara e define automaticamente o procedimento Register para registrar o componente na paleta.

terça-feira, 4 de janeiro de 2011

TEF - Realizando uma venda com mais de uma transação

Chegamos ao final de nosso pequeno treinamento de TEF! Conseguimos visualizar as principais rotinas e métodos, utilizadas durante o desenvolvimento das operações com o TEF.
Nesta última parte, estaremos conhecendo como uma transação com mais de um cartão é realizada.
Iremos passar por alguns procedimentos importantes e estaremos visualizando um fluxo básico de uma transação.
Então, vamos lá! E, uma boa leitura!
 
1) Procedimentos de uma operação TEF com múltiplos cartões
1.1) A aplicação imprimirá o cupom fiscal normalmente, até a sua totalização;
1.2) Enviará uma solicitação de transação TEF com os dados respectivos. Você deverá salvar uma cópia de cada arquivo de resposta "Intpos.001", pois você precisará dos dados para cancelar a transação, se necessário (quando houver uma queda de energia ou falta de comunicação com a impressora);
Observação: Caso exista uma nova transação TEF, a primeira deverá ser confirmada (CNF).
1.3) Abrirá o arquivo de resposta "Intpos.001" para verificar se o campo "001-000" é igual ao enviado na solicitação, caso contrário, deverá excluir esse arquivo e aguardar o próximo;
1.4) Analisará o campo "009-000". Se a transação for aprovada deverá efetuar a forma de pagamento. Se a transação não for aprovada, deverá exibir a mensagem do campo "030-000" ao operador e aguardar o seu "OK";
1.5) Antes de enviar a solicitação para a segunda transação, a aplicação deverá confirmar (CNF) a transação anterior, sem imprimi-la. O cupom fiscal deverá permanecer aberto (totalizado) após esta confirmação, aguardando o próximo pagamento;
1.6) Enviará a solicitação da segunda transação TEF com os dados respectivos. Você deverá salvar uma cópia de cada arquivo de resposta "Intpos.001", pois você precisará dos dados para cancelar a transação, se necessário (quando houver uma queda de energia ou falta de comunicação com a impressora);
1.7) Repetir o passo 1.4;
1.8) Repetir o 1.5;
Observação: Repetir os passos de 1.5 a 1.8 para cada nova transação.
1.9) Quando a aplicação chegar à última transação TEF, esta não deverá ser confirmada;
1.10) A aplicação encerrará o cupom fiscal com as formas de pagamento, respectivas a cada transação, e iniciará as impressões dos comprovantes do TEF (2 vias para cada);
1.11) Após a impressão da última transação TEF, esta deverá então ser confirmada (CNF), finalizando todo processo;
Observação: Tanto o teclado quanto o mouse deverão permanecer bloqueados durante as impressões.
1.12) Os arquivos temporários deverão ser excluídos.
Dica: As formas de pagamento usadas para realizar transação com TEF, deverão ser as últimas no fechamento do cupom fiscal. Por exemplo, se o cupom fiscal for pago com 3 formas de pagamento diferentes "Dinheiro", "Cheque" e "Cartão", a forma "Cartão" deverá ser a última.
2) Procedimentos com falta de comunicação com o ECF
2.1) Se durante a impressão dos comprovantes do TEF, houver uma falta de comunicação com o ECF, a aplicação deverá detectar esta falta e exibir a seguinte mensagem ao operador: "A impressora não responde. Deseja imprimir novamente?"
2.2) Se o operador optar por "Sim", a aplicação deverá fechar o comprovante, abrir o relatório gerencial e imprimir os comprovantes de todas as transações;
2.3) Se o operador optar por "Não", a aplicação deverá enviar um NCN (Não Confirmação) referente a última transação realizada e um CNC (Cancelamento) referente as demais transações, pois estas haviam sido confirmadas anteriormente.
2.4) O CNC deverá ser enviado para cada transação que foi confirmada respeitando a seguinte lógica:
2.4.1) A aplicação enviará um CNC para a primeira transação confirmada e imprimirá o texto do "Intpos.001" de resposta (referente a este cancelamento), em um relatório gerencial.
2.4.2) A aplicação enviará um CNF (Confirmação) confirmando este cancelamento e enviará o novo CNC para a outra transação confirmada, conforme citado acima. Este processo se aplicará para todas as transações que existirem.
Observação: A aplicação deverá exibir uma mensagem ao operador com as seguintes informações: "Nome da Rede" (bandeira), "NSU" e "Valor da Transação", para cada transação cancelada, incluindo a não confirmada.
2.5) Se durante a impressão dos comprovantes de cancelamento houver novamente uma falta de comunicação com o ECF, o procedimento será o mesmo do ítem 2.1.
2.6) Se o operador optar por "Sim", a aplicação deverá fechar o relatório gerencial e abrir o novo, imprimindo todo o texto novamente.
2.7) Se o operador optar por "Não", a aplicação deverá enviar um NCN (Não Confirmação) e novamente o CNC (Cancelamento) referente à transação, imprimindo o texto do INTPOS.001 de resposta em um novo relatório gerencial.
3) Procedimentos com queda de energia
3.1) Se durante a impressão dos comprovantes do TEF, houver uma queda de energia, a aplicação deverá detectar esta queda, enviar um NCN (Não Confirmação) para a última transação e um CNC (Cancelamento) para cada transação que havia sido confirmada anteriormente.
3.2) Se durante a impressão dos comprovantes do cancelamento houver uma queda de energia, a aplicação deverá detectar esta queda, fechar o relatório gerencial pendente, enviar o NCN (Não Confirma) e novamente o CNC (Cancelamento) referente à transação, imprimindo o texto do "Intpos.001" de resposta em um novo relatório gerencial.
3.3) A aplicação deverá enviar um CNF (Confirmação) após cada impressão do comprovante de cancelamento.
4) Fluxo básico de uma transação: