sábado, 15 de abril de 2017

Função que retorna mais de um valor

Olá..
Hoje vamos trabalhar uma função que retorna mais de um valor, é bem simples, e por várias situações precisamos deste função já que a função padrão retorna somente um valor, na verdade ele retorna só um valor, mas com muito valores embutidos neste valor, vamos utilizar uma TStringList para isso.

E também nós vamos utilizar o Delphi xe5, Firebird 2.5, e FIREDAC, mas se você  não quiser utilizar esses componentes de acesso, pode ser com outros componentes de acesso à dados a sua escolha.

Primeiramente vamos criar uma tabela simples no BD, somente para fins didáticos conforme abaixo:

CREATE TABLE CONDICAOPAGAMENTO (
    CON_ID          INTEGER NOT NULL PRIMARY KEY,
    CON_NOME        VARCHAR(30),
    CON_AVISTA      VARCHAR(3),
    CON_MODALIDADE  VARCHAR(20)
);


Insira alguns dados para os testes conforme abaixo:


Agora vamos ao Delphi.

Criei um novo projeto e salvei a Unit como uPrincipal e o projeto como TesteFuncao.
Mudei a Propriedade Name do Form para fPrincipal.

Para não alongar muito o nosso post, resolvi colocar os componentes de acesso a dados diretamente no Form, normalmente utiliza-se um Data Module para isso.

Adicionei:
Da Paleta FireDac: 1 FDConnection, 1 FDQuery.
Da Paleta FireDacLinks: 1 FDPhysFBDriverLink.
Da Paleta FireDac UI: 1 FDGUIxWaitCursor.

Vamos configurar as propriedades do nosso  FDConnection1 dando um duplo clique no componente surgirá a janela abaixo:


Configure o DataBase conforme a localização do seu BD.
Altere a propriedade  LoginPrompt do componente FDConnection1  para False.

Altera a Propriedade Name do FDQuery1 para qCondicoes e SQL coloque (SELECT * FROM CONDICAOPAGAMENTO) este comando é somente para fins didáticos muito cuidado com o "* from" com tabelas com muitos campos e dados, o ideal seria selecionar somente os campos necessários.


Desenhe o form Principal de acordo com a imagem abaixo:


Vamos a implementação da função, depois do primeiro uses antes do Type coloque:

type
   f_condicoes      = record
   cond_id          : integer;
   cond_nome        : string[30];
   cond_avista      : string[3];
   cond_modalidade  : string[20];
end;

E mais abaixo no var, coloque
var
  fPrincipal: TfPrincipal; //Criado automaticamente na criação do form
  Lista:TStringList;
  ft_condicao  : f_condicoes;

Em public do form coloque:
  public
    { Public declarations }
    Function RetornaCondicao(aCodigo:Integer):f_condicoes;
  end;

Tecle Shif+Ctrl+C para criar a função.

function TfPrincipal.RetornaCondicao(aCodigo: Integer): f_condicoes;
var
  Localizar:Variant;
begin
   Localizar:=aCodigo;
   if Not qCondicoes.Locate('con_id', Localizar, []) then
     Result.cond_id :=0
   else
     Result.cond_id          :=qCondicoes.FieldByName('con_id').AsInteger;
     Result.cond_nome        :=qCondicoes.FieldByName('con_nome').AsString;
     Result.cond_avista      :=qCondicoes.FieldByName('con_avista').AsString;
     Result.cond_modalidade  :=qCondicoes.FieldByName('con_modalidade').AsString;

end;

E no OnKeyDown do edtCodigo coloque:
procedure TfPrincipal.edtCodigoKeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
Var
  retorno:f_condicoes;
begin
   if key=VK_RETURN then begin
     if Length(edtCodigo.Text)>0 then begin
       Retorno:=RetornaCondicao(StrToInt(edtCodigo.Text));
       if retorno.cond_id=0 then begin
         ShowMessage('Código não cadastrado');
         edtCodigo.SetFocus;
         abort;
       end;
       edtNome.Text      :=retorno.cond_nome;
       edtAVista.Text    :=retorno.cond_avista;
       edtModalidade.Text:=retorno.cond_modalidade;
     end;
   end;
end;

Executando a aplicação.

Veja o form em tempo de execução:

Código não encontrado, retorna a mensagem.



Se encontrar retorna os dados.


Muito simples Né?


Comente e até o próximo post.




Cadastro Cliente Servidor Parte II

Olá...
Continuando nosso cadastro cliente servidor, vamos implantar uma pequena rotina de pesquisar no cadastro.

- Abra o projeto que iniciamos no post anterior.
- Adicione um FDQuery ao Datamodule e altere a propriedadade name para qAgendaPesquisa e a propriedade Connection para o FDConnection1.
- No menu principal vamos em File/New/VCL Form Delphi.
- Altere as seguintes propriedades:
Caption para Pesquisar;
Name para fPesquisar;
Salve-o como uPesquisar.
Agora adicione os seguintes Componentes:
3 GroupBox.
1 ComboBox renomeie para cbCampos (onde colocaremos os nomes dos campos a serem pesquisados).
1 RadioGroup  renomeie para rgCriterios (servirá para definir os critérios da procura).
1 Edit renomeie para edtLocalizar (servirá para receber o texto a ser localizado).
1 DataSource DataSource1.
1 Dbgrid Dbgrid1.


Tecle Alt+F11 e adicione u unit do Dm à cláusula Use do Form de pesquisa.

Agora ligue a propriedade DataSet do DataSource1 à qAgendaPesquisa, veja abaixo:

Agora ligue a propriedade DataSet do DataSource1 à qAgendaPesquisa, veja abaixo:

Na propriedade Items do cbCampos Digite:
Nome
Telefone
E-Mail


Veja a Tela Pronta:




Agora no fPesquisar vamos criar uma função que dará ao form chamante qual foi a escolha do usuário.

Antes do Private da unit, vamo fazer a função.
type
  TfPesquisar = class(TForm)
    DBGrid1: TDBGrid;
    DataSource1: TDataSource;
    rgCriterios: TRadioGroup;
    GroupBox1: TGroupBox;
    GroupBox2: TGroupBox;
    cbCampos: TComboBox;
    edtLocalizar: TEdit;
    function show():TModalResult;
  private
    { Private declarations }
  public
    { Public declarations }
  end;

Tecle Shift+Ctrl+C para criar a função:
function TfPesquisar.show: TModalResult;
begin

end;

vamos digitar o seguinte dentro da função: result:=inherited ShowModal;
Ficou Assim:
function TfPesquisar.show: TModalResult;
begin
  result:=inherited ShowModal;
end;

Agora vamos implementar as rotinas da pesquisa:

OnShow do Form.
procedure TfPesquisar.FormShow(Sender: TObject);
begin
   Dm.qAgendaPesquisa.Close;
   cbCampos.ItemIndex   := 0;
   rgCriterios.ItemIndex:=0;
   edtLocalizar.SetFocus;
end;


No Evento OnKeyDown do DbGrid digite:
procedure TfPesquisar.DBGrid1KeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
  if key=VK_RETURN then begin
    if Dm.qAgendaPesquisa.RecordCount>0 then begin
      ModalResult:=MrOk;
    end;
  end;
  if key=VK_ESCAPE then begin
     ModalResult:=mrCancel;
  end;
end;

No Evento OnKeyDown do edtLocalizar digite:
procedure TfPesquisar.edtLocalizarKeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
var
  txtSQL:String;
begin
  if key=VK_RETURN then begin
    if Length(edtLocalizar.Text)>0 then begin
      with Dm.qAgendaPesquisa do Begin
        Close;
        Sql.Clear;
        case cbCampos.ItemIndex of
          0:Sql.Add('SELECT ID, NOME, TELEFONE, EMAIL FROM VI_AGENDA WHERE NOME LIKE :LOCALIZAR');
          1:Sql.Add('SELECT ID, NOME, TELEFONE, EMAIL FROM VI_AGENDA WHERE TELEFONE LIKE :Localizar');
          2:Sql.Add('SELECT ID, NOME, TELEFONE, EMAIL FROM VI_AGENDA WHERE EMAIL LIKE :Localizar');
        end;
        case rgCriterios.ItemIndex of
          0:ParamByName('localizar').AsString :=edtLocalizar.Text+'%'; //Procura pelo inicio da string
          1:ParamByName('localizar').AsString :='%'+edtLocalizar.Text+'%'; //Procura em qualquer parte da string
        end;
        Open;
        if RecordCount>0 then
          DBGrid1.SetFocus;
      End;
    end;
  end;
end;

Ok, agora nosso form de pesquisa está pronto.

Vamos agora implementar o código para chamar o form de pesquisa.

No Form Principal (no meu caso Form1),  adicione a unit do fPesquisa com Alt+F11.

No Form Principal (no meu caso Form1), coloque um Button e no onclick dele coloque:
procedure TForm1.btPesquisarClick(Sender: TObject);
begin
   fPesquisar:=TfPesquisar.Create(Application);
   if fPesquisar.show()=MrOk then
     Banco_Tela(Dm.qAgendaPesquisa, Form1);
   fPesquisar.Free;
end;

Não esqueça de colocar o fPesquisa como Available Forms no menu Preject/option.


Pronto, está feita a nossa pesquisa, você pode melhorar e muuuito estas rotinas e layout.

Duvidas e sugestões, comente.


sexta-feira, 14 de abril de 2017

Cadastro Cliente Servidor Parte I

Neste Exemplo vamos abordar Rotinas de Inserção, Alteração e exclusão num ambiente
Cliente servidor onde os comandos são executados diretamente no servidor.

Utilizei o DelphiXE5 mas pode ser feito a partir da versão do Delphi que trouxe a tecnologia FIREDAC,  Firebird 2.5, e a IDE para Firebird IbExpert personal  edition, é gratuito só precisa fazer um cadastro e de mês em mês ele pede uma nova senha que você recebe no seu e-mail, é muito bom. 

Baixe em ibexpert.com, se não quiser, utilize o da sua preferência.
Neste exemplo não utilizaremos o componente TTable, Tdbedit, e nada que ligue diretamente a campos de tabelas.

Vou mostrar como fazer isso de forma simples e seguro.

Iniciaremos Criando uma tabela no seu BD conforme abaixo:

Tabela: tb_agenda

create table tb_agenda(
age_id       integer not null primary key,
age_nome     varchar(50),
age_telefone varchar(10),
age_email    varchar(50),
age_ativo    integer)


Altere vamos transformar o campo age_id como autoincremento.


Clique com o botão direito do mouse em cima da tabela tb_agenda, em seguida em Edit Table.

Dê um duplo clique no campo AGE_ID e surgirá a janela abaixo:

Em seguida clique na aba AutoIncrement e marque a caixa Create Generator.

Depois disso clique em OK, na próxima janela em Commit.

Agora veja no Database Explorer



Agora nós temos uma tabela com campo inteiro e autoincremento.
Agora Vamos Criar uma VIEW que são os dados que serão apresentados nas pesquisas e no dbgrid, não vamos ligar diretamente a tabela tb_agenda.

Se preferir pode criar diretamente o generator assim:

create generator gen_tb_agenda_id ou outro nome de sua preferência mas cuidado, mais adiante utilizaremos este nome.





View: vi_agenda
create view vi_agenda(
 
id       ,
nome     ,
telefone ,
email    ,
ativo    )
as select
age_id       ,
age_nome     ,
age_telefone ,
age_email    ,
age_ativo
from tb_agenda

Agora vamos criar um procedure para manipular os dados e as rotinas de Inserir, Alterar e Excluir Registros:
CREATE PROCEDURE SP_AGENDA (
    id integer,
    nome varchar(50),
    telefone varchar(10),
    email varchar(50),
    ativo integer,
    oper integer)
as
declare variable reg integer;
begin
  if (oper=1) then
  begin
    select gen_id(gen_tb_agenda_id, 1) from rdb$database into :reg;
    insert into tb_agenda (age_id, age_nome, age_telefone, age_email, age_ativo)
    values (:reg, :nome, :telefone, :email, :ativo);
  end
  if (oper=2) then
  begin
    update tb_agenda set
    age_nome    =:nome,
    age_telefone=:telefone,
    age_email   =:email,
    age_ativo   =:ativo
    where age_id=:id;
  end
  if (oper=3) then
  begin
    delete from tb_agenda
    where age_id=:id;
  end
end

Agora vamos para o Delphi.

Crie um novo projeto e salve de acordo com a sua conveniência. Vamos inserir os componentes necessários:

Componente
Propriedade Name
Propriedade MaxLength
Propriedade Caption
Form
Form1 (pode mudar)

Agenda
Edit
Nome
50

Edit
Telefone
10

Edit
Email
50

Button
btIncluir

Novo
Button
btAlterar

Alterar
Button
btExcluir

Excluir
Button
btSalvar

Salvar
DataSource
dsAgenda


DbGrid
dbgAgenda


DbNavigator
DbNavigator1


CheckBox
Ativo



Observe que os edit´s foram nomeados de acordo com os nomes/apelidos que foram atribuídos a view que criamos.

Veja abaixo como ficou o LayOut do fPrincipal é bem básico, somente para fins didático, determine sua própria forma de layout.



Agora Vamos criar o nosso DataModule utilizando o menu File/New/Other e em Delphi Files clique em Data Module em seguida em OK.
Altere a Proprieda Name para Dm, e salve como uDm.
Vamos utiizar os componentes FireDac para acesso e manipulação de dados.
Adicione:
Da Paleta FireDac: 1 FDConnection,  1 FDTransaction, 1 FDQuery, 1 FDStoredProc.
Da Paleta FireDacLinks: 1 FDPhysFBDriverLink.
Da Paleta FireDac UI: 1 FDGUIxWaitCursor.

Vamos setar as propriedades do nosso  FDConnection1 dando um duplo clique no componente surgirá a janela abaixo:



Altere a propriedade  LoginPrompt do componente FDConnection1  para False.

Altere a propriedade name da FDQuery1 para qAgenda, e verifique a propriedade connection set está setado para o nosso FDConnection1.
Na propriedade SQL digite: SELECT * FROM VI_AGENDA
Altere a Propriedade Name do FDStoredProc1 para spAgenda, este componente será ligado a SP_agenda,   StoredProcName para a Procedure que criamos no FireBird SP_AGENDA, veja abaixo:


Veja agora como ficou nosso datamodule:

No form principal:
-Tecle Alt+F11 para adicionar o datamodlule à cláusula Uses.
-Adicione uma variável global no form conforme abaixo:
var
  Form1: TForm1; //já existente
  oper:Integer;

Ligue a propriedade dataset do dsAgenda para qAgenda:


Ligando o DataSouce à qAgenda

Ligue a propriedade datasource do Dbgrid com dsAgenda.

Agora vamos criar alguma procedures e function que serão utilizados mais adiante

Vamos ao datamodule:

Veja a declaração:
  private
    { Private declarations }
  public
    { Public declarations }
  end;

//declare as procedure antes da cláusula var do Datamodule para que seja utilizado em todo o //sistema.

  //Procedure que mostra os dados nos edit´s
  procedure Banco_Tela( SP:TDataSet; Form:TForm );
  //Procedure que Trava ou Lira os campos para digitação
  procedure Trava_Campo( Form:TForm; Status:Boolean );
  function IIF(Expressao: Boolean; Result_True, Result_False: Variant): Variant;
  //Limpar os campos
  procedure Limpa_Campo( Form:TForm );

As Procedures:

procedure Banco_Tela( SP:TDataSet; Form:TForm );
var
  i: Integer;
  Source: TComponent;
begin
  for i := 0 to SP.FieldDefs.Count-1 do
    begin
      Source := Form.FindComponent(SP.FieldDefs.Items[i].Name);
      if Source <> nil then
         begin
          if Source is TEdit then
            TEdit(Source).Text := SP.FieldByName( SP.FieldDefs.Items[i].Name ).AsString ;
          if Source is TMaskEdit then
            TMaskEdit(Source).Text := SP.FieldByName( SP.FieldDefs.Items[i].Name ).AsString ;
          if Source is TComboBox then begin
            if SP.FieldByName( SP.FieldDefs.Items[i].Name ).DataType=ftInteger then begin
              TComboBox(Source).ItemIndex := SP.FieldByName( SP.FieldDefs.Items[i].Name ).AsInteger;
            end else begin
              TComboBox(Source).Text := SP.FieldByName( SP.FieldDefs.Items[i].Name ).AsString;
            end;
          end;
          if Source is TCheckBox then begin
            if SP.FieldByName( SP.FieldDefs.Items[i].Name ).DataType=ftInteger then begin
              TCheckBox(Source).Checked := SP.FieldByName( SP.FieldDefs.Items[i].Name ).AsInteger=0;
            end else begin
              TCheckBox(Source).Checked := False;
            end;
          end;
        end;
    end;
end;


procedure Trava_Campo( Form:TForm; Status:Boolean );
var
  i : integer;
  Source: TComponent;
begin
  for i := 0 to Form.ComponentCount-1 do
    begin
      Source := Form.Components[i];
      if Source <> nil then
        begin
          if Source is TEdit       then TEdit(Source).Enabled       := Status;
          if Source is TMaskEdit   then TMaskEdit(Source).Enabled   := Status;
          if Source is TComboBox   then TComboBox(Source).Enabled   := Status;
          if Source is TCheckBox   then TCheckBox(Source).Enabled   := Status;
        end;
    end;
end;

function IIF(Expressao: Boolean; Result_True, Result_False: Variant): Variant;
begin
  if Expressao then
    Result := Result_True else Result := Result_False;
end;

procedure Limpa_Campo( Form:TForm );
var
  i : integer;
  Source: TComponent;
begin
  for i := 0 to Form.ComponentCount-1 do
    begin
      Source := Form.Components[i];
      if Source <> nil then
        begin
          if Source is TEdit       then TEdit(Source).Clear;
          if Source is TMaskEdit   then TMaskEdit(Source).Clear;
          if Source is TComboBox   then TComboBox(Source).ItemIndex := 0;
          if Source is TCheckBox   then TCheckBox(Source).Checked:=False;
        end;
    end;
end;


No Evento OnShow do Form digite:
   Dm.qAgenda.Open();
   Oper:=0; //Iniciar sem comando
   if Dm.qAgenda.RecordCount>0 then begin
     Dm.qAgenda.Last;
     Banco_Tela(Dm.qAgenda, Form1);
   end;
   Trava_Campo(Form1, False);


No Evento OnClose do Form Digite:
 Dm.qAgenda.Close;

 
Veja em execução o nosso form.


Vamos implementar o Evento OnClick do btNovo.

procedure TForm1.NovoClick(Sender: TObject);
begin
   Oper:=1; //Inserir
   Trava_Campo(Form1, True); //Libera os campos para digitação
   Limpa_Campo(Form1); //Limpa os campos
   Nome.SetFocus; //Direciona o foco para o edit chamado Nome
end;

Após a digitação, o usuário clicará em Salvar, vamos implementar o evento do btSalvar:

procedure TForm1.btSalvarClick(Sender: TObject);
begin
  try
    Dm.FDTransaction1.StartTransaction;
    with Dm.spAgenda do Begin
      if Not Prepared then prepare;
      //Se for uma inclusão, não vamos passar o id que será gerado automaticamente com o    
      //gen_tb_agenda_id
      if Oper>1 then //Alteração ou exclusão
        ParamByName('id').AsInteger   := Dm.qAgenda.FieldByName('id').AsInteger
      Else //Inclusão, parâmetro com valor zero só pra não dar excessão na execução da procedure
        ParamByName('id').AsInteger   := 0;
      ParamByName('nome').AsString    := nome.Text;
      ParamByName('telefone').AsString:= Telefone.Text;
      ParamByName('email').AsString   := email.Text;
       //Se tiver Marcado esta inativo então ele grava 0=ativo, 1=Inativo;
      ParamByName('ativo').AsInteger  := iif(Ativo.Checked, 0, 1);
      ParamByName('oper').AsInteger   := oper; //Variavel de controle de inserção, etc
      ExecProc;
    End;
    Dm.FDTransaction1.Commit;
    Dm.qAgenda.Refresh;
  except
    Dm.FDTransaction1.Rollback;
    raise;
  end;
  Oper:=2; //Iniciar como alterar
  Dm.qAgenda.Last;
  Banco_Tela(Dm.qAgenda, Form1);
  Trava_Campo(Form1, False);
end;

Observe que acima utilizei “Dm.qAgenda.FieldByName('id').AsInteger”, existe uma discussão nos fóruns de Delphi que: qual seria a forma que menos consome recurso do servidor ou maquina local, se “Dm.qAgenda.FieldByName('id').AsInteger” , “Dm.qAgendaId.AsInteger” ou
Dm.qAgenda.fields[x].AsInteger, mas isso você pode utilizar a forma mais conveniente.


Evento do btAlterar:

procedure TForm1.AlterarClick(Sender: TObject);
begin
   Oper:=2; //Alterar
   Trava_Campo(Form1, True); //Libera os campos para digitação
   Banco_Tela(Dm.qAgenda, Form1);
   Nome.SetFocus; //Direciona o foco para o edit  “Nome”
end;

Evento btExcluir:
procedure TForm1.ExcluirClick(Sender: TObject);
Begin
  //Aqui você pode gerar uma pergunta antes de excluir
   Oper:=3; //Excluir
   Banco_Tela(Dm.qAgenda, Form1);
   btSalvar.OnClick(Sender);
end;

No evento OnDataChange do dsAgenda coloque:
  Banco_Tela(Dm.qAgenda, Form1);



Em execução:



Pra finalizar, esta forma de utilização de controle de dados que chamam cliente/servidor onde o processo é feito no servidor, liberam os recursos da estação.
Desta forma, pode ser feitos os filtros, seleções, junção de tabelas para relatórios tudo vai depender de como você fez a modelagem.

A utilização de View, este recurso permite que se crie visualizações bem complexas utilizado várias tabelas ao mesmo tempo, mas este assunto será abordado em um post específico.

Se gostou comente, dúvidas, sugestões também.

No próximo post faremos as rotinas de Pesquisas.
E a quem interessar faremos um cadastro em SQLServer 2008.

Até o próximo post.