USANDO GENERATORS

Delphi

02/07/2011

Ola a todos estou fazendo um cadastro de vendas uso os componentes SQLDataSet, DataSetProvider, ClientDataSet, DataSource, quando gera o número do pedido gera automantico usando no banco de dados o Generators até ai tudo bem ele gera o número do pedido e grava no banco de dados tudo normal, quando vou digitar os produtos para esse cadastro não estou sabendo trazer o número do pedido gerado para jogarno campo que tem CODIGO_PEDIDO.TXT esse componente é um edit, atraves dele pego o codigo e mando para a tabela dos produtos no qual eu uso u dbgrid para colocar os itens, ele da esse erro abaixo.   esso erro só esta acontecendo por que não sei como mandar o número que é gerado para esse edit automatico quando salvo o cabeçalho do pedido, agora se faço assim salvo o cabeçalho do pedido fecho a tela e entro novamente no pedido ai da tudo certo porque já informei o número do pedido no edit. tem algum componente que não estou sabendo usar p/ assim que gerar o número pelo generators apareça o número no edit.
Helio Souza

Helio Souza

Curtidas 0

Respostas

Marco Salles

Marco Salles

02/07/2011

geralmente se usa alguma coisa como esta função que eu peguei aqu na net

Function TDm.RetornaChaveFirebird(Indice: String): Integer;


var


  {Declarando a query que sera utilizada para pegar o valor gerado.}


  QryId: TSQLQuery;


begin


 {Instanciado o objeto.}


 QryId := TSQLQuery.Create(nil);


 try


   {Preparando a query para a execução}


   QryId.SQLConnection := Conexao;


   {SQL que irá pegar o registro gerado, dado onome do generator da tabela.}


   QryId.SQL.Add('SELECT GEN_ID(' + Indice +',1) AS ID_RET FROM RDB$DATABASE');


   QryId.Open;


   if QryId.IsEmpty then


     {Não ocorreu geração, por isso o cadastro não pode continuar.}


     raise Exception.Create('Não foi criado nenhum valor.')


   else


     {Retornando o valor gerado.}


     result := QryId.FieldByName('ID_RET').AsInteger;


 finally


   {Apos o uso da query, liberando a memoria.}


   QryId.Free;


 end;


end;



para pegar o valor do genator vc faz assim

{Pegando o valor incrementado no banco}


  CdsPadrao.FieldByName('PRO_CODIGO').AsInteger := Dm.RetornaChaveFirebird('gen_produto_id');



como voce vai pegar , quando vc vai pegar vai depender de sua lógica ,

Agora eu particularmente pego esse valores pelo DataSetProvider no evento BeforeUpdateREcord...
Tem um post do diego macário
https://www.devmedia.com.br/forum/delphi/395811-Master-Detail-com-incremento-negativo.html
é um pouco complicado para quem esta inicializando .. Mas no meu ponto de vista é a maneira mais limpa de gerar
esses codigos

Mas vc vai tentando com esta funão padrão acima e a medida que for dando erro , vai postando para chegar num mesmo MDC

GOSTEI 0
Emerson Nascimento

Emerson Nascimento

02/07/2011

e aí, Maco Salles, blz?

eu estava dando uma olhada no tópico que você sugeriu para esclarecer a dúvida do colega e observer um detelhe curioso: você se preocupou em guardar o valor do ID ta tabela principal numa variável pra depois atribuí-lo à tabela filha. isso não é necessário. quando o DatasetProvider executa o BeforeUpdateRecord, ele o faz para cada Dataset do Provider, partindo sempre do Pai.

Então, quando o BeforeUpdateRecord estiver atualizando uma tabela filha, ele já passou pela tabela pai e atribuiui o valor ao campo ID da tabela pai. Com isso basta obter esse valor diretamente do Dataset pai, sem qualquer variável.

Usando como exemplo do tópico que você sugeriu, fica assim:

  if (UpdateKind = ukInsert) then
  begin
    if (SourceDS = sqlfornecedor)  then
//    begin
      // eu não faria essa verificação, pois SE ESTÁ INSERINDO
      // é porque é necessário atribuir o ID, então eu tiraria
      // o if e atribuiria direto.
//      if (DeltaDS.FieldByName('CHAVE_FORNECEDOR').Value < 0)  then
//      begin
//        Id := GenID('FORNECEDOR');
        DeltaDS.FieldByName('CHAVE_FORNECEDOR').NewValue := GenID('FORNECEDOR')
//      end;
//    end
    else
    if (SourceDS = sqlcontato)  then
    begin
      DeltaDS.FieldByName('CHAVE_CONTATO').NewValue := GenID('CONTATO');
      // aqui atribui o ID da tabela pai, sem necessidade de qualquer análise
      DeltaDS.FieldByName('CHAVE_FORNECEDOR').NewValue := //AQUI É NEWVALUE
         DeltaDS.DatasetField.Dataset.FieldByName('CHAVE_FORNECEDOR').Value; //AQUI É VALUE
    end;
  end;


note que na atribuição da tabela filha, é usado DeltaDS.DatasetField.Dataset, que é para alcançar o Dataset princial.

eu estava escrevendo um artigo - faz alguns anos - sobre como trabalhar com NestedDatasets, usando vários recursos, como atribuição mestre/detalhe, geração de logs (auditoria), etc, mas ainda não consegui terminar...

GOSTEI 0
Marco Salles

Marco Salles

02/07/2011

Beleza Emerson


eu estava dando uma olhada no tópico que você sugeriu para esclarecer a dúvida do colega e observer um detelhe curioso: você se preocupou em guardar o valor do ID ta tabela principal numa variável pra depois atribuí-lo à tabela filha



Não .. O Valor atribuido não vem de uma tabela , mas sim de uma constante Tipada .São Valores negativos , decrementados e não repetidos para evitar duplicidade . Mas isto não teria importância . O importante é entender que são os PRIMARY_KEYS da tabela Mastre e DEtalhe que são substituidas antes do provider resolver o Sql

Veja eu não costumo a cada post em um Cds dar ApplayUpdate . Logo o  BeforeUpdateRevcor não sera executado
Porém a cadasPost eu ( O Diego_Macário) necessitava de valores para Chaves (Porque ele Reclama se esses valores não forem atribuidos ).. Não ha chave do Relaciomanento que automaticamente é atribuida para a filha, mas sim os Primary_Key .. A idéia era trabalhar desconectado , inserir editar alterar e no fim dar o ApplayUpdates .. Ai sim seria Chamado o evento BeforeUpdateRecord que teria a função de Substituir os Valores Atribuidos ao Primary_Key , porque esses valores eram negativos e decrementados (-1,-2,-3...-10....etc) Para evitar a mensagem de Duplicidade . Por isto ha necessidade dos Ifs que vc vé no código

Poderia a cada post , dar um ApplayUpdates , Poderia a cada Post atribuir um Valor para os Primary_Key utilizando a função GENID('') ETC.. Mas na época optou por trabalhar desconectado

Confesso que eu gostei do Modelo , e eu não sei se soube explicar ou se eu lhe entendi. mas a idéia não era
dar o ApplayUpdates chamando assim o BeforeUpdateRecord ,, E sim desconectadamente
GOSTEI 0
Helio Souza

Helio Souza

02/07/2011

Ola MARCO SALLES e Emerson e não sei onde estou errando coloquei o codigo abaixo no sistema mais ainda continua dando o mesmo erro, tem como dar uma olhada onde estou errando. foi feito tudo dentro do DataModule no AfterUpdateRecord da tabela filho.
procedure Tdmvendas.dspitemprecoclienteAfterUpdateRecord(Sender: TObject;
  SourceDS: TDataSet; DeltaDS: TCustomClientDataSet;
  UpdateKind: TUpdateKind);
begin
  if (UpdateKind = ukInsert) then
  begin
    if (SourceDS = sqlClientes)  then //TABELA CLIENTE TABELA PAI
//    begin
      // eu não faria essa verificação, pois SE ESTÁ INSERINDO
      // é porque é necessário atribuir o ID, então eu tiraria
      // o if e atribuiria direto.
//      if (DeltaDS.FieldByName('CHAVE_FORNECEDOR').Value < 0)  then
//      begin
//        Id := GenID('FORNECEDOR');
        DeltaDS.FieldByName('CLI_CODIGO').NewValue := ('GEN_CADCLIENTECODIGO_ID') //O CAMPO CLI_CODIGO É ONDE É GERADO O CODIGO DO CLIENTE PELO GENERATOR
//      end;
//    end
    else
    if (SourceDS = sqlitemprecocliente)  then //TABELA ONDE VAR O PREÇO DO PRODUTO POR CIENTE TABELA FILHO
    begin
      DeltaDS.FieldByName('ITEMPRE_CODCLIENTE').NewValue := ('GEN_CADITEMPRECOCLIENTE_ID'); //O CAMPO ITEMPRE_CODCLIENTE É ONDE VAI RECEBER O CODIGO DO CLIENTE GERADO PELO GENERATOR
      // aqui atribui o ID da tabela pai, sem necessidade de qualquer análise               //O GEN_CADITEMPRECOCLIENTE_ID ELE É UM GENERATOR SÓ PARA RECEBER O CAMPO GEN_CADCLIENTECODIGO_ID
      DeltaDS.FieldByName('CLI_CODIGO').NewValue := //AQUI É NEWVALUE                       //FOI CRIADO NORMALMENTE IGUAL AOS DEMAIS ESTA CERTO COMO FOI CRIADO
         DeltaDS.DatasetField.Dataset.FieldByName('CLI_CODIGO').Value; //AQUI É VALUE
    end;
   end;
end;
e aí, Maco Salles, blz?

eu estava dando uma olhada no tópico que você sugeriu para esclarecer a dúvida do colega e observer um detelhe curioso: você se preocupou em guardar o valor do ID ta tabela principal numa variável pra depois atribuí-lo à tabela filha. isso não é necessário. quando o DatasetProvider executa o BeforeUpdateRecord, ele o faz para cada Dataset do Provider, partindo sempre do Pai.

Então, quando o BeforeUpdateRecord estiver atualizando uma tabela filha, ele já passou pela tabela pai e atribuiui o valor ao campo ID da tabela pai. Com isso basta obter esse valor diretamente do Dataset pai, sem qualquer variável.

Usando como exemplo do tópico que você sugeriu, fica assim:

  if (UpdateKind = ukInsert) then
  begin
    if (SourceDS = sqlfornecedor)  then
//    begin
      // eu não faria essa verificação, pois SE ESTÁ INSERINDO
      // é porque é necessário atribuir o ID, então eu tiraria
      // o if e atribuiria direto.
//      if (DeltaDS.FieldByName('CHAVE_FORNECEDOR').Value < 0)  then
//      begin
//        Id := GenID('FORNECEDOR');
        DeltaDS.FieldByName('CHAVE_FORNECEDOR').NewValue := GenID('FORNECEDOR')
//      end;
//    end
    else
    if (SourceDS = sqlcontato)  then
    begin
      DeltaDS.FieldByName('CHAVE_CONTATO').NewValue := GenID('CONTATO');
      // aqui atribui o ID da tabela pai, sem necessidade de qualquer análise
      DeltaDS.FieldByName('CHAVE_FORNECEDOR').NewValue := //AQUI É NEWVALUE
         DeltaDS.DatasetField.Dataset.FieldByName('CHAVE_FORNECEDOR').Value; //AQUI É VALUE
    end;
  end;


note que na atribuição da tabela filha, é usado DeltaDS.DatasetField.Dataset, que é para alcançar o Dataset princial.

eu estava escrevendo um artigo - faz alguns anos - sobre como trabalhar com NestedDatasets, usando vários recursos, como atribuição mestre/detalhe, geração de logs (auditoria), etc, mas ainda não consegui terminar...

GOSTEI 0
Marco Salles

Marco Salles

02/07/2011

Helio vc não esta usando o NestedDataSet ????
GOSTEI 0
Helio Souza

Helio Souza

02/07/2011

Ola Marco eu estou usando ClientDataSet Em todos tanto pai = cdsclientes e filho = cdsitemprecocliente estou usando o exemplo que o Emerson postou, tenho que usar o nesteddataset onde?
GOSTEI 0
Emerson Nascimento

Emerson Nascimento

02/07/2011

Beleza Emerson


eu estava dando uma olhada no tópico que você sugeriu para esclarecer a dúvida do colega e observer um detelhe curioso: você se preocupou em guardar o valor do ID ta tabela principal numa variável pra depois atribuí-lo à tabela filha


Não .. O Valor atribuido não vem de uma tabela , mas sim de uma constante Tipada .São Valores negativos , decrementados e não repetidos para evitar duplicidade . Mas isto não teria importância . O importante é entender que são os PRIMARY_KEYS da tabela Mastre e DEtalhe que são substituidas antes do provider resolver o Sql

Veja eu não costumo a cada post em um Cds dar ApplayUpdate . Logo o  BeforeUpdateRevcor não sera executado
Porém a cadasPost eu ( O Diego_Macário) necessitava de valores para Chaves (Porque ele Reclama se esses valores não forem atribuidos ).. Não ha chave do Relaciomanento que automaticamente é atribuida para a filha, mas sim os Primary_Key .. A idéia era trabalhar desconectado , inserir editar alterar e no fim dar o ApplayUpdates .. Ai sim seria Chamado o evento BeforeUpdateRecord que teria a função de Substituir os Valores Atribuidos ao Primary_Key , porque esses valores eram negativos e decrementados (-1,-2,-3...-10....etc) Para evitar a mensagem de Duplicidade . Por isto ha necessidade dos Ifs que vc vé no código

Poderia a cada post , dar um ApplayUpdates , Poderia a cada Post atribuir um Valor para os Primary_Key utilizando a função GENID('') ETC.. Mas na época optou por trabalhar desconectado

Confesso que eu gostei do Modelo , e eu não sei se soube explicar ou se eu lhe entendi. mas a idéia não era
dar o ApplayUpdates chamando assim o BeforeUpdateRecord ,, E sim desconectadamente


Marcos, a forma que eu sugeri funciona da forma que você exemplificou:
Preciso digitar um pedido com 30 itens. Dou o Post a cada digitação de item de o ApplyUpdates somente no final de toda a digitação (posso até trabalhar desconectado nesse período).
Quando o ApplyUpdates é executado, o DatasetProvider tentar gravar efetivamente o registro da tabela principal, depois cada registro de cada tabela filha individualmente, e com isso basta pegar o ID já gravado na tabela pai, da forma que eu exemplifiquei, porque com certeza ele já está atualizado.
Por isso não há necessidade de obter nenhum ID enquanto digita-se o documento e nem gravar o ID em qualquer variável (pelo menos foi assim que eu entendi o seu código: você grava o ID do pai numa variável e depois replica para os filhos).

agora tentando ajudar nosso colega: isso tudo que está sendo discutido aqui, bem como o exemplo que eu passei, só funcionam em NestedDatasets e no evento BEFOREUpdateRecord (ANTES DE GRAVAR). No AFTER não vai resolver.

GOSTEI 0
Emerson Nascimento

Emerson Nascimento

02/07/2011

procedure Tdmvendas.dspitemprecoclienteAfterUpdateRecord(Sender: TObject;
  SourceDS: TDataSet; DeltaDS: TCustomClientDataSet;
  UpdateKind: TUpdateKind);
begin
  if (UpdateKind = ukInsert) then
  begin
    if (SourceDS = sqlClientes)  then //TABELA CLIENTE TABELA PAI
        DeltaDS.FieldByName('CLI_CODIGO').NewValue := ('GEN_CADCLIENTECODIGO_ID') //O CAMPO CLI_CODIGO É ONDE É GERADO O CODIGO DO CLIENTE PELO GENERATOR
    else
    if (SourceDS = sqlitemprecocliente)  then //TABELA ONDE VAR O PREÇO DO PRODUTO POR CIENTE TABELA FILHO
    begin
      DeltaDS.FieldByName('ITEMPRE_CODCLIENTE').NewValue := ('GEN_CADITEMPRECOCLIENTE_ID'); //O CAMPO ITEMPRE_CODCLIENTE É ONDE VAI RECEBER O CODIGO DO CLIENTE GERADO PELO GENERATOR
      // aqui atribui o ID da tabela pai, sem necessidade de qualquer análise               //O GEN_CADITEMPRECOCLIENTE_ID ELE É UM GENERATOR SÓ PARA RECEBER O CAMPO GEN_CADCLIENTECODIGO_ID
      DeltaDS.FieldByName('CLI_CODIGO').NewValue := //AQUI É NEWVALUE                       //FOI CRIADO NORMALMENTE IGUAL AOS DEMAIS ESTA CERTO COMO FOI CRIADO
         DeltaDS.DatasetField.Dataset.FieldByName('CLI_CODIGO').Value; //AQUI É VALUE
    end;
   end;
end;


não entendi:

qual é o campo ID da tabela filha?
qual é o campo que relaciona a tabela filha com a tabela pai ?





GOSTEI 0
Marco Salles

Marco Salles

02/07/2011

Marcos, a forma que eu sugeri funciona da forma que você exemplificou:
Preciso digitar um pedido com 30 itens. Dou o Post a cada digitação de item de o ApplyUpdates somente no final de toda a digitação (posso até trabalhar desconectado nesse período).
Quando o ApplyUpdates é executado, o DatasetProvider tentar gravar efetivamente o registro da tabela principal, depois cada registro de cada tabela filha individualmente, e com isso basta pegar o ID já gravado na tabela pai, da forma que eu exemplifiquei, porque com certeza ele já está atualizado.
Por isso não há necessidade de obter nenhum ID enquanto digita-se o documento e nem gravar o ID em qualquer variável (pelo menos foi assim que eu entendi o seu código: você grava o ID do pai numa variável e depois replica para os filhos).


Vc ainda não entendeu o Modelo emerson

Vaje:

Preciso digitar um pedido com 30 itens. Dou o Post a cada digitação de item

Neste momento na tabela Filha a cada Post é necessário  GERAR UM ID .( Primary Key ) ...

Como Fazer ??? Qual O ID atribuir ????

Não vou fazer um Select para Retornar O ID porque estou trabalhando desconectado ...

No Modelolo é atribuido o valor negativo para este item

Mestre
Item_Venda :=-1  // ******** Eu preciso deste Valor para o Relacionamento ***********

Detalhe .............. Relacionamento ... Item_Venda:=-1  
Item_Pedidos:=-1;
Item_Pedidos=:-2;
Item_Pedidos=:-30;

Mestre
Item_Venda:=-2; // ******** Eu preciso deste Valor para o Relacionamento ***********


Detalhe  .............. Relacionamento ... Item_Venda:=-2
Item_Pedidos:=-31;
Item_Pedidos=:-32;
...
Item_Pedidos=:-60;

No fim de tudo no no BeforeUpdate Modificamos e atribuimos resolvendo tudo no Provider

Item_Venda :=1

Item_Pedidos:=1;
Item_Pedidos=:2;
Item_Pedidos=:30;

Item_Venda:=2;

Item_Pedidos:=31;
Item_Pedidos=:32;
...
Item_Pedidos=:60;


Continuando Desconectado ... Proxima Venda

Mestre
tem_Venda :=-1

Item_Pedidos:=-1;
Item_Pedidos=:-2;
Item_Pedidos=:-30;

e assim segue O Modelo  ....  Qualquer coisa se ainda tiver algum modelo simples que simplifique a situação apresentada acima , favor me envie a tabela e o projeto para analisar e ficarei grato se tiver alguma novidade a cerca do apresentado acima . Caso confirme esta posição eu publicamente retrocederei minha opinião a cerca do assunto.
Você sabe que isto não é de forma nenhuma pessoal a vc , talves não saiba o quanto lhe admiro e lhe prezo em seus comentários sempre certeiros a cerca do problema que de longos anos presta suporte nesta comunidade . Voce de fato é muito bom

Porém , posso não esta lhe entendendo e vice versa , por isto comentei a cerca do projeto ( simples )


Sobre a ajuda ao amigo Helio

agora tentando ajudar nosso colega: isso tudo que está sendo discutido aqui, bem como o exemplo que eu passei, só funcionam em NestedDatasets e no evento BEFOREUpdateRecord (ANTES DE GRAVAR). No AFTER não vai resolver.

Alem disso o mais importante que eu não comentei antes porque fiz a pergunta Primeiro... No NestedDatasets é so um Provider do Mestre que é responsável por tudo .. ele disse que estava utilizando o provider do Filho









GOSTEI 0
Emerson Nascimento

Emerson Nascimento

02/07/2011

você está corretíssimo Marco.

eu estava falando em relação ao [url=https://www.devmedia.com.br/forum/delphi/395811-Master-Detail-com-incremento-negativo.html]tópico que você passou[/url] para o colega. transcrevo o trecho que eu estou comentando:

procedure TDm_Dados.dtsfornecedorBeforeUpdateRecord(Sender: TObject;
  SourceDS: TDataSet; DeltaDS: TCustomClientDataSet; UpdateKind: TUpdateKind;
  var Applied: Boolean);
{$J+}
const
  Id: Integer = -1;
{$J-}
begin   if (UpdateKind = ukInsert) then
  begin
    if (SourceDS = sqlfornecedor)  then
      begin
        if (DeltaDS.FieldByName('CHAVE_FORNECEDOR').Value < 0)  then
         begin
           Id := GenID('FORNECEDOR');
           DeltaDS.FieldByName('CHAVE_FORNECEDOR').NewValue := Id;
        end;
      end
    else
        if (SourceDS = sqlcontato)  then
          if (DeltaDS.FieldByName('CHAVE_CONTATO').Value < 0)  then
            begin
              if DeltaDS.FieldByName('CHAVE_FORNECEDOR').Value < 0 then
                  DeltaDS.FieldByName('CHAVE_FORNECEDOR').NewValue := Id;
              DeltaDS.FieldByName('CHAVE_CONTATO').NewValue := GenID('CONTATO');
           end;
  end;
end;  no meu entender, é obtido o ID da tabela pai (no caso, fornecedor) e guardado na variável Id, correto?
depois essa variável é usada para relacionar os registros filhos, como se vê no trecho:

              if DeltaDS.FieldByName('CHAVE_FORNECEDOR').Value < 0 then
                  DeltaDS.FieldByName('CHAVE_FORNECEDOR').NewValue := Id;

foi isso que eu disse que não é necessário pois, se estou incluindo um registro na tabela filho, basta atribuir o valor do ID da tabela pai, sem qualquer análise.
por isso sugeri, na parte que manipula o Dataset filho:

              DeltaDS.FieldByName('CHAVE_FORNECEDOR').NewValue :=
                  DeltaDS.DatasetField.Dataset.FieldByName('CHAVE_FORNECEDOR').Value;

ou seja: independe do valor que já estiver gravado ali, eu coloco o valor que está na tabela pai.

eu estava falando daquele tópico e não deste aberto aqui pelo nosso colega Hélio.

entendido?

Alem disso o mais importante que eu não comentei antes porque fiz a pergunta Primeiro... No NestedDatasets é so um Provider do Mestre que é responsável por tudo .. ele disse que estava utilizando o provider do Filho


exatamente. o código DEVE ficar no Provider da tabela PAI, pois num NestedDataset só há o Provider do pai, correto?


P.S.: que 'editor de textos' FDP que a devmidia foi implantar no fórum, hein?
GOSTEI 0
Helio Souza

Helio Souza

02/07/2011

Ola Emerson estou fazendo tudo isso dentro do dspitemprecoclienteAfterUpdateRecord filho, o ID da tabela filho ('ITEMPRE_CODCLIENTE'), o campo que relaciona tabela pai e filha é ('CLI_CODIGO') tabela pai e ('ITEMPRE_CODCLIENTE') tabela filho, o campo ('CLI_CODIGO') é onde gera o codigo pelo generation e o ('ITEMPRE_CODCLIENTE') é onde vai receber o.codigo gerado de cada item
procedure Tdmvendas.dspitemprecoclienteAfterUpdateRecord(Sender: TObject;
  SourceDS: TDataSet; DeltaDS: TCustomClientDataSet;
  UpdateKind: TUpdateKind);
begin
  if (UpdateKind = ukInsert) then
  begin
    if (SourceDS = sqlClientes)  then //TABELA CLIENTE TABELA PAI
        DeltaDS.FieldByName('CLI_CODIGO').NewValue := ('GEN_CADCLIENTECODIGO_ID') //O CAMPO CLI_CODIGO É ONDE É GERADO O CODIGO DO CLIENTE PELO GENERATOR
    else
    if (SourceDS = sqlitemprecocliente)  then //TABELA ONDE VAR O PREÇO DO PRODUTO POR CIENTE TABELA FILHO
    begin
      DeltaDS.FieldByName('ITEMPRE_CODCLIENTE').NewValue := ('GEN_CADITEMPRECOCLIENTE_ID'); //O CAMPO ITEMPRE_CODCLIENTE É ONDE VAI RECEBER O CODIGO DO CLIENTE GERADO PELO GENERATOR
      // aqui atribui o ID da tabela pai, sem necessidade de qualquer análise               //O GEN_CADITEMPRECOCLIENTE_ID ELE É UM GENERATOR SÓ PARA RECEBER O CAMPO GEN_CADCLIENTECODIGO_ID
      DeltaDS.FieldByName('CLI_CODIGO').NewValue := //AQUI É NEWVALUE                       //FOI CRIADO NORMALMENTE IGUAL AOS DEMAIS ESTA CERTO COMO FOI CRIADO
         DeltaDS.DatasetField.Dataset.FieldByName('CLI_CODIGO').Value; //AQUI É VALUE
    end;
   end;
end;


não entendi:

qual é o campo ID da tabela filha?
qual é o campo que relaciona a tabela filha com a tabela pai ?





GOSTEI 0
Emerson Nascimento

Emerson Nascimento

02/07/2011

peraí, Hélio.

então deixe-me entender e estrutura das suas tabelas.

uma modelagem correta deveria ser mais ou menos assim:

CLIENTE
--------------
ID_CLIENTE (PK) (o generator deve ser aplicado aqui)
COD_CLI (UK) (qualquer numerador que você quiser)
NOME
etc


PRODUTOCLIENTE
--------------
ID_PRODUTOCLIENTE (PK) (o generator deve ser aplicado aqui)
ID_CLIENTE (FK) (este campo relaciona a tabela PRODUTOCLIENTE com a tabela CLIENTE)
ID_PRODUTO (FK) (este campo relaciona a tabela PRODUTOCLIENTE com a tabela PRODUTO)
PRECO
etc


PRODUTO
--------------
ID_PRODUTO (PK) (o generator deve ser aplicado aqui)
CODIGO (UK) (qualquer numerador que você quiser)
DESCRICAO
ALIQICMS
ALIQIPI
PRECO
etc


então você tem um generator em cada tabela, com um campo em cada uma delas para identificação do REGISTRO e, na tabela PRODUTOCLIENTE, você tem um campos que fazem o relacionamento entre as tabelas.

entendido?

depois você deve fazer a implementação proposta no DatasetProvider da tabela PAI, desde que use NestedDatasets (se não sabe o que é isso, pergunte). E deve ser feito no BEFOREUpdate.

então, seguindo a estrutura acima, o evento seria (note que é BeforeUpdateRecord, e na tabela PAI):

procedure Tdmvendas.dspClienteBeforeUpdateRecord(Sender: TObject;
  SourceDS: TDataSet; DeltaDS: TCustomClientDataSet;
  UpdateKind: TUpdateKind);
begin
  if (UpdateKind = ukInsert) then
  begin
    if (SourceDS = sqlClientes)  then // TABELA CLIENTE (PAI)
        DeltaDS.FieldByName('ID_CLIENTE').NewValue := GEN_ID('GEN_CLIENTE_ID'); // GERA O IDENTIFICADOR UNICO DO REGISTRO
    else
    if (SourceDS = sqlitemprecocliente) then // TABELA PRODUTO x CLIENTE (FILHO)
    begin
      DeltaDS.FieldByName('ID_PRODUTOCLIENTE').NewValue := GEN_ID('GEN_PRODUTOCLIENTE_ID'); // GERA O IDENTIFICADOR UNICO DO REGISTRO
      // aqui atribui o ID da tabela pai para que seja feito o relacionamento entre as tabelas
      DeltaDS.FieldByName('ID_CLIENTE').NewValue := // id_cliente da tabela filho
             DeltaDS.DatasetField.Dataset.FieldByName('ID_CLIENTE').Value; // igual ao id_cliente da tabela pai
    end;
 end;
end;


GOSTEI 0
Marco Salles

Marco Salles

02/07/2011

P.S.: que 'editor de textos' FDP que a devmidia foi implantar no fórum, hein?


Rapaz eu estou passando muita raica com isto.. Não da para CITAR , não da Para IDENTAR , não da para COMENTAR

As mensagens ficam fora de ORDEM .. Enfim , ningume se entende e uma coisa TRIVIAl , como é o caso da dúvida do amigo
Helio , fica arrastando para a eternidade

Enfim , vamos ver se chegumemos a um acordo

Este Modelo que ele não sei porque foi levado a praticar ( utilizando o AfterUpdateRecord ) não vai dar certo

Primeiro que o evento seria o BeforeUpdateRecord e não o AfterUpdateRecord


segundo.... Isto so funciona se existir um ID previamente cadastrado , como eu cansei exaustivamente
de comentar nos THREADS anteriores . Ou se dar um applayUpdates a cada post (neste caso pode ser que sim)
mas perder um encanto da tecnologia que é trabalhar desconectado

COmo ele obtem o ID quando esta simplesmente cadastradando um Item da Tabela PAI ??????

Como se ao dar um Insert na tabela Filha ele ira pedir o Id da Tabela Pai ????

Pelo que estou entendendo esta deixando para o Evento BeforeUpdateRecord , Mas ele precisa deste valores inicialmente em Memoria  (nos CDS...) A Cada Insert eu preciso ter este VAlor....

Eu desconheço este Modelo , e queria também entender , fazendo um NestedDataSet utilizando o BeforeUpdateREcord sem os Ids previamentes cadastradados ????

Enfim , acho que ele esta ficando maluco ( nos e o Editor do Delphi ) o estamos Deixando maluco. Ate eu estou ficando maluco



GOSTEI 0
Helio Souza

Helio Souza

02/07/2011

CARAMBA, RSS. TO DOIDÃO. pessoal desculpa os transtornos ai, vou começar de novo, teve tanto post que to confuso todo mundo ja entendeu o meu caso, deixa eu explicr a minha logica, tenho uma tabela de clientes é criei uma tabela de preço do produto por cliente, e tenho uma tabela de produtos, minha intenção é o seguinte o usuario negocio alguns produtos com preço diferenciados para determinado cliente, então criei uma tabela onde o usuario pode colocar esses itens para o cliente quando ele puxar a lista de preço primeiramente ele vai buscar os cadastro dentro dessa tabela "PREÇO POR CLIENTE" se não tiver nada ele vai na tabela produto: respondendo a ultima pergunta do amigo Emerson: tenho a tabela CLIENTES 'ONDE ESTA O CADASTRO DO CLIENTE   CADCLIENTE: --------------------------------- CLI_CODIGO (o generator é gerado aqui: Atraves desse campo que identifico o cliente na tabela filho) CLI_CNPJCPF CLI_RAZAO etc.   CADITEMPRECOCLIENTE: --------------------------------- ITEMPRE_CODCLIENTE (esse campo não é para ser um generator eu só criei pq. vi no exem. do Emerson que tinha que ser generator, acho que entendi errado)                                      (esse campo é que vai receber o 'CLI_CODIGO' ta babela pai) ITEMPRE_CODPRODUTO (esse campo recebe o codigo do produto: ESTA FUNCIONANDO NORMAL) ITEMPRE_CODFORNECEDOR ITEMPRE_VALOR ITEMPRE_COMISSAO (só tenho esses campos nessa tabela   CADPRODUTO: --------------------------------- PROD_CODPROD (esse campo vai para tabela CADITEMPRECOCLIENTE no campo ITEMPRE_CODPRODUTO para identificar o produto na tabela filho, esta funcionando blz. não é um generator eu informo o codigo de fabrica do produto, não uso generator nessa tabela) PROD_DESCRICAO PROD_UNIDADE etc.   meu problema é só quando estou inserindo um novo cliente é apos dar um dmvendas.cdsclientes.Post; dmvendas.cdsclientes.ApplyUpdates(0); para salvar na tabela CLIENTES, e vou cadastrar na tabela filho CADITEMPRECOCLIENTE, da esse erro, mas se salvo o cliente saio do do form e retorno e vou editar, que para editar tenho que informar o codigo gerado não acontece nada porque eu estou informando o codigo que foi gerado manualmente, se vou fazer isso tudo de uma vez cadastrar cliente e em seguida cadastrar o produto para esse cliente estou apanhando. eu não sei oque é NestedDatasets.
GOSTEI 0
Helio Souza

Helio Souza

02/07/2011

eu coloquei da dessa frma agora no BeforeUpdateRecord esta dando esse erro.     procedure Tdmvendas.dspClientesBeforeUpdateRecord(Sender: TObject;
  SourceDS: TDataSet; DeltaDS: TCustomClientDataSet;
  UpdateKind: TUpdateKind; var Applied: Boolean);
begin
  if (UpdateKind = ukInsert) then
  begin
    if (SourceDS = sqlClientes)  then // TABELA CLIENTE (PAI)
        DeltaDS.FieldByName('CLI_CODIGO').NewValue := ('GEN_CADCLIENTECODIGO_ID') // GERA O IDENTIFICADOR UNICO DO REGISTRO
    else
    if (SourceDS = sqlitemprecocliente)  then // TABELA PRODUTO x CLIENTE (FILHO)
    begin
      DeltaDS.FieldByName('ITEMPRE_CODCLIENTE').NewValue := ('GEN_CADITEMPRECOCLIENTE_ID');  // GERA O IDENTIFICADOR UNICO DO REGISTRO
      // aqui atribui o ID da tabela pai para que seja feito o relacionamento entre as tabelas
      DeltaDS.FieldByName('CLI_CODIGO').NewValue := // id_cliente da tabela filho
       DeltaDS.DatasetField.Dataset.FieldByName('CLI_CODIGO').Value; // igual ao id_cliente da tabela pai
    end;
   end;
end;   eu uso edits para buscar o produto é quando saio do ultimo campo ele manda o produto para um dbgrid.   procedure Tfrmcadcliente.comissao_itempreExit(Sender: TObject);
begin
//esse codigo verifica se tem item repetido, no dbgrid
  if  dmvendas.dsitemprecocliente.DataSet.Locate('ITEMPRE_CODPRODUTO',codproduto_itempre.Text,[]) then
      begin
        Application.MessageBox('Produto não pode ser inserido, já Digitado anteriormente!','Cadastro de Cliente',MB_OK+MB_ICONEXCLAMATION);
        codproduto_itempre.SetFocus;
        codproduto_itempre.Clear;
        descricao_itempre.Clear;
        valor_itempre.Clear;
        comissao_itempre.Clear;
        Abort;
      end;
//fim do codigo

        dmVendas.cdsitemprecocliente.Insert;
        DBGrid1.Fields[0].AsString := codproduto_itempre.Text;
        DBGrid1.Fields[1].AsString := valor_itempre.Text;
        DBGrid1.Fields[2].AsString := comissao_itempre.Text;
        DBGrid1.Fields[3].AsString := codrep_cli.Text;          codproduto_itempre.SetFocus;
end;  
GOSTEI 0
Emerson Nascimento

Emerson Nascimento

02/07/2011

com algumas questões respondidas, podemos obter o seguinte:

procedure Tdmvendas.dspClientesBeforeUpdateRecord(Sender: TObject;
  SourceDS: TDataSet; DeltaDS: TCustomClientDataSet;
  UpdateKind: TUpdateKind; var Applied: Boolean);
begin
  if (UpdateKind = ukInsert) then
  begin
    if (SourceDS = sqlClientes)  then // TABELA CLIENTE (PAI)
        DeltaDS.FieldByName('CLI_CODIGO').NewValue := GEN_ID('GEN_CADCLIENTECODIGO_ID') // GERA O IDENTIFICADOR UNICO DO REGISTRO
    else
    if (SourceDS = sqlitemprecocliente) then // TABELA PRODUTO x CLIENTE (FILHO)
      // aqui atribui o ID da tabela pai para fazer o relacionamento entre as tabelas
      DeltaDS.FieldByName('ITEMPRE_CODCLIENTE').NewValue :=
       DeltaDS.DatasetField.Dataset.FieldByName('CLI_CODIGO').Value; // igual ao id_cliente da tabela pai
      // lembro que essa forma não é usual. toda tabela deveria ter um campo identificador
   end;
end;

agora só precisamos saber se você está aninhando os datasets (NestedDatasets).
o que é isso??? é quando você faz o relacionamento a partir do dataset e não do clientdataset. com isso as tabelas filhas são campos especiais da tabela pai.

assim:

SQLDatasetCliente (pai)
DatasourceCliente
DatasetProviderCliente
SQLDatasetProdCli (filho)

o detalhe é que a instrução do SQLDataset filho precisa ter um parâmetro que irá receber o relacionamento da tabela pai.

algo assim:

SELECT * FROM CADITEMPRECOCLIENTE WHERE ITEMPRE_CODCLIENTE = :CLI_CODIGO

ATENÇÃO! o parâmetro precisa ter o mesmo nome do campo da tabela pai. é preferível até que os campos tenham o mesmo nome em ambas tabelas.

aí você faz a ligação entre esses datasets assim:

DatasourceCliente.Dataset = SQLDatasetCliente
SQLDatasetProdCli.Datasource = DatasourceCliente -> é aqui que a mágica acontece
DatasetProviderCliente.Dataset = SQLDatasetCliente

pronto. foi feito o relacionamento entre as tabelas.

agora você precisa preparar os ClientDatasets para receberem esse aninhamento de datasets.

instancie dois ClientDatasetse dois Datasources. vou chamá-los de cdsCliente e cdsProdCli.
agora configurando os clientdatasets:

cdsCliente.ProviderName = DatasetProviderCliente

dependendo da sua forma de acesso, será necessário preencher também a propriedade RemoteServer ou a propriedade ConnectionBroker.

dê um duplo-clique no cdsClientes e traga todos os campos.
entre eles deverá existir um campo chamado cdsClienteSQLDatasetProdCli. esse é o campo especial que eu citei acima. ele é um campo do tipo TDatasetField e na verdade é a tabela filho.

agora, no cdsProdCli, faça a ligação da propriedade DatasetField à esse campo citado. pronto. seu NestedDataset está pronto.

agora, implemente a propriedade BeforeUpdateRecord do DatasetProviderCliente com aquele código do topo deste mensagem. assim os valores dos GENERATORs serão atribuídos ao executar o método ApplyUpdates.

LEMBRANDO QUE O APPLYUPDATES DEVE SER EXECUTADO SOMENTE NO CLIENTDATASET PAI.

pra finalizar você precisa atribuir valores fictícios ao campos de identificação no momento da inserção dos registros, para poder trabalhar nos registros em memória, até que eles recebam a atribuição definitiva.

então, no cdsCliente, implemente o AfterInsert de modo a obter um valor qualquer (somente para poder relacionar com a tabela filho).

algo assim:

procedure TForm14.cdsClienteAfterInsert(Dataset: TDataset);
begin
  cdsClienteCLI_CODIGO.AsInteger := GetTickCount; // função na unit Windows.
end;

dessa forma, ao incliur um registro no cdsCliente, será atribuído automaticamente um valor ao compo ID.

trabalhando dessa forma você só precisa executar o ApplyUpdates ao confirmar toda a digitação.

bom, espero que agora tudo se resolva.


GOSTEI 0
Helio Souza

Helio Souza

02/07/2011

nesse comentario aq.

SELECT * FROM CADITEMPRECOCLIENTE WHERE ITEMPRE_CODCLIENTE = :CLI_CODIGO
o ITEMPRE_CODCLIENTE // é da tabela filho, ok e esse outro :CLI_CODIGO é da tabela pai, se for esta dando esse erro quando mando trazer todos os campos, tudo ligado como vc disse. eu criei um campo na tabela filho onde vai receber o codigo do cliente gerado coloqui o nome iguai a tabela pai ficou assim.  
 SELECT * FROM CADITEMPRECOCLIENTE WHERE CLI_CODIGO = :CLI_CODIGO 
GOSTEI 0
Marco Salles

Marco Salles

02/07/2011

Mas vc croiu o parâmetro ?? no SqlDataSet e o adicionou ao Cds Detalhes com o Fetchparams ????
GOSTEI 0
Helio Souza

Helio Souza

02/07/2011

Marco no SqlDataSet foi criado no campo Params, CLI_CODIGO, como vc esta falando para adicionar no CDS, quando mando buscar os campos ele fala que não existe. como deo adicionar?  
Mas vc croiu o parâmetro ?? no SqlDataSet e o adicionou ao Cds Detalhes com o Fetchparams ????
 
GOSTEI 0
Marco Salles

Marco Salles

02/07/2011

Maneira rápida , de um click no CDS e escolha aopção fechParams
GOSTEI 0
Helio Souza

Helio Souza

02/07/2011

Marco fiz conforme vc mandou. e agora devo fazer oq? continua dando o erro.  
Maneira rápida , de um click no CDS e escolha aopção fechParams
GOSTEI 0
Helio Souza

Helio Souza

02/07/2011

Marco mil desculpas cara minha tabela estava fecha e por isso deu erro, desculpas ai cara, to doido com esse negocio.  
Mas vc croiu o parâmetro ?? no SqlDataSet e o adicionou ao Cds Detalhes com o Fetchparams ????
GOSTEI 0
Helio Souza

Helio Souza

02/07/2011

Marcos agora rodou, quando dou o comando dmvendas.cdsclientes.Post;  dmvendas.cdsclientes.ApplyUpdates(0); ele ta dando esse erro. é quando vou editar ou inserir ele não esta aceitando eu digitar nada. e quando salvo da esse erro.    
GOSTEI 0
Marco Salles

Marco Salles

02/07/2011

Parece que tem algum campo boolean que vc esta atribuindo um valor string.. Confira ai
GOSTEI 0
Helio Souza

Helio Souza

02/07/2011

quando dou um post; e applyupdates, ele passa ele vai dar o erro nessa linha.
        DeltaDS.FieldByName('CLI_CODIGO').NewValue := ('GEN_CADCLIENTECODIGO_ID')
procedure Tdmvendas.dspClientesBeforeUpdateRecord(Sender: TObject;
  SourceDS: TDataSet; DeltaDS: TCustomClientDataSet;
  UpdateKind: TUpdateKind; var Applied: Boolean);
begin
  if (UpdateKind = ukInsert) then
  begin
    if (SourceDS = sqlClientes)  then // TABELA CLIENTE (PAI)
        DeltaDS.FieldByName('CLI_CODIGO').NewValue := ('GEN_CADCLIENTECODIGO_ID') // GERA O IDENTIFICADOR UNICO DO REGISTRO
    else
    if (SourceDS = sqlitemprecocliente)  then // TABELA PRODUTO x CLIENTE (FILHO)
    begin
      DeltaDS.FieldByName('ITEMPRE_CODCLIENTE').NewValue := ('GEN_CADITEMPRECOCLIENTE_ID');  // GERA O IDENTIFICADOR UNICO DO REGISTRO
      // aqui atribui o ID da tabela pai para que seja feito o relacionamento entre as tabelas
      DeltaDS.FieldByName('CLI_CODIGO').NewValue := // id_cliente da tabela filho
       DeltaDS.DatasetField.Dataset.FieldByName('CLI_CODIGO').Value; // igual ao id_cliente da tabela pai
    end;
   end;
end;
GOSTEI 0
Helio Souza

Helio Souza

02/07/2011

na tabela CLIENTES o campo CLI_CODIGO ele é INTEGER!
GOSTEI 0
Helio Souza

Helio Souza

02/07/2011

o motivo de não deixar eu digitar nada tem relação com alguma ligação feita, pois tudo que foi postado acima deu certo e rodou blz. mais por algum motivo não esta deixando eu digitar nada no dbedit que esta cetado aos campos da tabela pai. não esta trazendo nenhum campo, na busca na tabela todos os campos estão vindo vazio.
GOSTEI 0
Emerson Nascimento

Emerson Nascimento

02/07/2011

se o erro aparece depois do ApplyUpdates, o problema é no evento BeforeUpdateRecord.

preste atenção no trecho abaixo e veja se você está gerando corretamente o id.


repare bem na linha que você publicou e na linha que eu sugeri. a diferença é sutil, mas vou deixar em negrito para ficar mais fácil de você identificar:

seu código:
    if (SourceDS = sqlClientes)  then // TABELA CLIENTE (PAI)
        DeltaDS.FieldByName('CLI_CODIGO').NewValue := ('GEN_CADCLIENTECODIGO_ID') // GERA O IDENTIFICADOR UNICO DO REGISTRO


o correto:
    if (SourceDS = sqlClientes)  then // TABELA CLIENTE (PAI)
        DeltaDS.FieldByName('CLI_CODIGO').NewValue := GEN_ID('GEN_CADCLIENTECODIGO_ID') // GERA O IDENTIFICADOR UNICO DO REGISTRO


seu código:
    if (SourceDS = sqlitemprecocliente)  then // TABELA PRODUTO x CLIENTE (FILHO)
    begin
      DeltaDS.FieldByName('ITEMPRE_CODCLIENTE').NewValue := ('GEN_CADITEMPRECOCLIENTE_ID');  // GERA O IDENTIFICADOR UNICO DO REGISTRO

o correto:
    if (SourceDS = sqlitemprecocliente)  then // TABELA PRODUTO x CLIENTE (FILHO)
    begin
      DeltaDS.FieldByName('ITEMPRE_CODCLIENTE').NewValue := GEN_ID('GEN_CADITEMPRECOCLIENTE_ID');  // GERA O IDENTIFICADOR UNICO DO REGISTRO


outra coisa: não esqueça de acrescentar poPropagateChanges nas opções do DatasetProvider. com isso o ClientDataset receberá o valor atribuído ao ID e você não precisará dar Refresh no nele.
GOSTEI 0
Helio Souza

Helio Souza

02/07/2011

para usar o GEN_ID oque eu tenho que declarar na uses.
GOSTEI 0
Marco Salles

Marco Salles

02/07/2011

Tem que usar aquela função que eu te indiquei la no primeiro post
GOSTEI 0
Marco Salles

Marco Salles

02/07/2011

Tem que usar aquela função que eu te indiquei la no primeiro post
GOSTEI 0
Helio Souza

Helio Souza

02/07/2011

MARCO estou usando a função que vc me passou para usar o GEN_ID, eu não entendi esse indice  
QryId.SQL.Add('SELECT GEN_ID(' + Indice +',1) AS ID_RET FROM RDB$DATABASE'); 
Function TDm.RetornaChaveFirebird(Indice: String): Integer; 
var 
{Declarando a query que sera utilizada para pegar o valor gerado.} 
QryId: TSQLQuery; 
begin 
{Instanciado o objeto.} 
QryId := TSQLQuery.Create(nil); 
try 
{Preparando a query para a execução} 
QryId.SQLConnection := Conexao; 
{SQL que irá pegar o registro gerado, dado onome do generator da tabela.} 
QryId.SQL.Add('SELECT GEN_ID(' + Indice +',1) AS ID_RET FROM RDB$DATABASE'); 
QryId.Open; 
if QryId.IsEmpty then 
{Não ocorreu geração, por isso o cadastro não pode continuar.} 
raise Exception.Create('Não foi criado nenhum valor.') 
else 
{Retornando o valor gerado.} 
result := QryId.FieldByName('ID_RET').AsInteger; 
finally 
{Apos o uso da query, liberando a memoria.} 
QryId.Free; 
end; 
end; 

GOSTEI 0
Emerson Nascimento

Emerson Nascimento

02/07/2011


    if (SourceDS = sqlClientes)  then // TABELA CLIENTE (PAI)
        DeltaDS.FieldByName('CLI_CODIGO').NewValue := RetornaChaveFirebird('GEN_CADCLIENTECODIGO_ID');


GOSTEI 0
Helio Souza

Helio Souza

02/07/2011

Pessoa deu certo ele esta mandando bls. só que esta assim, na tabela pai ele gera o número correto, ex. 27, o proximo numero gerado só que quando manda p/ tabela filho ele manda um número muito mairo ex. 17461718. foi oque gerou no meu aq.   onde estou errando novamente?
GOSTEI 0
Marco Salles

Marco Salles

02/07/2011

[QUOTE]Pessoa deu certo ele esta mandando bls. só que esta assim, na tabela pai ele gera o número correto, ex. 27, o proximo numero gerado só que quando manda p/ tabela filho ele manda um número muito mairo ex. 17461718. foi oque gerou no meu aq.   onde estou errando novamente?[/QUOTE]
Por acaso vc esta usando isto ???

procedure TForm14.cdsClienteAfterInsert(Dataset: TDataset);
begin
  cdsClienteCLI_CODIGO.AsInteger := GetTickCount; // função na unit Windows.
end;
GOSTEI 0
Helio Souza

Helio Souza

02/07/2011

Por um acaso não estou usando. não deve ser usado, quando eu tiro esse comando ele não fica com o numero gerado pela tabela pai ele gera seu proprio número?
GOSTEI 0
Helio Souza

Helio Souza

02/07/2011

Por um acaso não estou usando. não deve ser usado, quando eu tiro esse comando ele não fica com o número gerado pela tabela pai ele gera seu proprio número.
GOSTEI 0
POSTAR