Ir para conteúdo
  • Cadastre-se

Gabriel Franciscon

Membros
  • Total de ítens

    100
  • Registro em

  • Última visita

Posts postados por Gabriel Franciscon

  1. 13 horas atrás, Daniel Paixao disse:

    E como vocÊ faz com o banco de dados?

    Opa! 

    Após a atualização no primeiro computador. O sistema rodará os scritps. Esses scripts estão dentro Instalador do InstallShield. Por tanto ao concluir a instalação é criado uma pasta contendo todos os scripts necessários. E após o usuário abrir o programa e informar o login os scripts daquela pasta são executados (através do componente do FireDac - FDScript). 

    Após rodar os scripts no banco os demais computadores mostra uma mensagem informando que é necessário realizar um atualização... Aí o instalador com o update é rodado e quando o usuário abrir o sistema nesses outros PC's, não será executado nenhum script pois dentro do banco de dados tem uma tabela que guarda a versão do banco. e antes de rodar eu verifico se a versão do executável é compatível com a versão do banco

    • Curtir 1
  2. 4 minutos atrás, centuryinf disse:

    resolvi criando criando um numero sequencia em cNfe.

    Não é correto usar um número sequencial e sim um número randômico de 8 dígitos. Pois pode acontecer que o seu número sequencial seja um dos números inválidos. 

    Aconselho utilizar a função mencionada no tópico do Ítalo GerarCodigoDFe e armazenando esse código junto a nota fiscal no seu banco de dados.

  3. Já tentou passar 0 (zero) para o parâmetro NSU? Informando zero retorna todos os NSU's dos últimos 90 dias.

    Exemplo:

    DistribuicaoDFePorUltNSU(41, '12345678000100', '0')

    Outra dica importante é verificar o ambiente nas configurações do ACBr (homologação - produção) para cada ambiente há uma sequencia diferente de NSU.

    • Curtir 1
  4. Boa tarde,

    2 horas atrás, Phelipe Silva disse:

    Sendo bem direto na pergunta,eu gostaria de saber,qual é a regra do SEFAZ para liberar o download de uma nota?

    Quando o fornecedor emite uma nota fiscal, essa nota é enviada para SEFAZ do estado e a mesma se encarrega de enviar para o ambiente nacional*. Feito isso, o ambiente nacional criará um NSU com o resumo dessa nota fiscal e disponibiliza através do Web-Service de DistribuicaoDFe. O seu software irá localizar esse resumo através do método DistribuicaoDFePorUltNSU. Com o resumo em mãos seu software irá realizar a manifestação do destinatário... Ao fazer isso, o ambiente nacional irá criar um novo NSU, agora com a nota completa. E novamente você executará o método DistribuicaoDFePorUltNSU ou DistribuicaoDFePorChaveNFe.

     

    *O tempo entre a SEFAZ do estado enviar a nota e o Ambiente Nacional receber e disponibilizar varia muito... Aqui no Paraná vejo um média de 2 horas. mas já vi relatos aqui no fórum que pode levar até 24 horas.

     

    Vou deixar um tópico abaixo que detalha melhor o funcionamento:

     

    • Curtir 2
    • Obrigado 1
  5. Infelizmente a versão oficial dos schemas disponibilizados não permite a visualização do status atual da manifestação. Porém...

    Se observarmos o software gratuito da SEFAZ, percebemos que ao "baixar" uma nota já vem o status atual da manifestação. Mesmo se a manifestação do destinatário tenha sido realizada em outro software.

    Isso acontece pois o software gratuito utiliza uma versão mais recente dos schemas (v.135). Nessa versão existe uma tag chamada: cSitConf. (se olhar a nota técnica até existe a menção dessa tag)

     

    Mas como essa não é uma versão oficial, digo, disponibilizada pela SEFAZ. Creio que ACBr não irá utilizar. De qualquer forma vou deixar aqui o link para download desses schemas.

    Com uma simples alteração na unit pcnRetDistDFeInt você adiciona uma nova property e na sequencia pode ler o valor desse campo.

    Nessa versão infelizmente o método DistribuicaoDFePorChaveNFe não funciona. (Mas é possível contornar isso alterando algumas linhas)

    --Se for DistribuicaoDFePorChaveNFe utiliza 1.02 caso contrário, utiliza a 1.35

    v1.35.zip

    • Curtir 3
  6. 5 minutos atrás, ALA disse:

    mas como vcs estão procedendo quando perde a chave da NFe/NFC-e e tem a necessidade de criar a mesma chave que ja foi enviada para SEFAZ..?

    Você deve armazenar o cNF no seu banco de dados. Dessa forma se precisar gerar a chave novamente, você utiliza o mesmo cNF da primeira vez. 

  7. 42 minutos atrás, ALA disse:

    "Erro ao Gerar NFC-e Código Numérico inválido, Chave não Gerada" o que devo passar para função para não acontecer esse erro. 

    Isso acontece pois você está passando para o campo cNF o mesmo valor de nNF. 

    De uma olhada para mais detalhes:

     

    • Curtir 1
    • Obrigado 1
  8. 6 minutos atrás, New Standard Software disse:

    Certo, mas fica a mesma dúvida que escrevi acima, como vejo se foi manifestada, deveria ter um evento associado a ela? E outro detalhe, quem deve fazer a manifestação, o gerador da nota, ou o destinatário?

    A manifestação é feita pelo destinatário da nota fiscal.

    E a SEFAZ não envia uma informação se foi realizado a manifestação ou não para o destinatário. Apenas para o emitente. Pois entende-se que como o destinatário realizou a manifestação. O mesmo não precisa saber, pois foi ele mesmo quem fez. Ou seja, quando o seu cliente realizar a manifestação você deverá armazenar essa informação.

    E como citado acima o método DistribuicaoDFePorChaveNFe só retorna informação caso a nota em questão já tiver um registro de manifestação vinculado a ela.

  9. 2 horas atrás, New Standard Software disse:

    Mas o problema já ocorre no primeiro passo, ele não localiza nada e retorna o cStat 137

    O ideal seria utilizar o método DistribuicaoDFePorUltNSU.

    Pois o método DistribuicaoDFePorChaveNFe retorna informações apenas se a nota já estiver com um registro de manifestação pelo destinatário.

    Então na teoria ficaria assim

    1. DistribuicaoDFePorUltNSU (trará: Nota resumida, Nota completa, Eventos resumido e Eventos completo)
    2. Realizar a manifestação
    3. Executar o método DistribuicaoDFePorUltNSU novamente (ou o DistribuicaoDFePorChaveNFe)
    • Curtir 1
  10. Realizar a manifestação do destinatário apenas com a Ciência e não concluir posteriormente pode sim acarretar em algum tipo de multa...

    A manifestação do destinatário está "amarrada" ao DistribuiçãoDFe. Porém os objetivos são diferentes... Então a ideia de "só baixar o XML" não funciona muito já que seu cliente se torna obrigado a realizar a manifestação.

    Aqui eu trato da seguinte maneira:

    1. Aplicativo configurado para realizar a manifestação do destinatário automaticamente com a Ciência da operação ao encontrar um novo documento.
    2. Usuário realiza o recebimento de mercadorias no sistema: Disparo a Confirmação da operação
    3. Desconhecimento ou Operação não realizada fica por conta do usuário.
    4. Caso fique notas sem a "conclusão" da manifestação por parte do destinatário, alerto o mesmo através de notificações.
    • Curtir 3
  11. Como você não mencionou a mensagem exata de erro que está dando, vou dar outra sugestão;

    Limpar o componente antes de criar um evento. (Se tiver uma nota fiscal carregada no ACBr e você tentar criar um evento, terá a mensagem abaixo)

    Screenshot_20.png.745e629e183939775d2bbe452532ce7e.png

    Por tanto, certifique-se de limpar as notas fiscais e eventos que estão previamente carregados no ACBr.

    with ACBrNFe1 do
    begin
      NotasFiscais.Clear;
      EventoNFe.Evento.Clear;
      with EventoNFe.Evento.New do
      begin
        InfEvento.cOrgao          := 91;  //91 - Ambiente Nacional. No caso de evento de manifestação, sempre será 91
        InfEvento.chNFe           := '';  //Chave de acesso da NFe
        InfEvento.CNPJ            := '';  //CNPJ da empresa que está emitindo o evento (o mesmo do certificado digital)
        InfEvento.dhEvento        := now; //Data do evento
        InfEvento.tpEvento        := '';  //teManifDestCiencia, teManifDestConfirmacao, teManifDestOperNaoRealizada, teManifDestDesconhecimento
        InfEvento.detEvento.xJust := '';  //Justificativa, caso seja desconhecimento ou op não realizada
        end;
      EnviarEvento(IdLote);
    end;

     

    • Curtir 3
    • Obrigado 1
  12. 4 horas atrás, nildglan disse:

    erro é que não vincula o evento

    Se a mensagem for: Evento registrado mas não vinculado a NF-e

    Pode significar que:

    1. A chave de acesso pertence a uma nota fiscal emitida no ambiente de homologação e o evento está sendo enviando em produção (ou vise-versa)
    2. A chave de acesso está incorreta.

    Lembrando ainda que o campo CNPJ é o CNPJ da empresa que está enviando o Evento, no caso o mesmo do certificado digital.

     

    E uma observação, dependendo da sua versão do Delphi, considere substituir o método .add para .new. Visto que o add é um método depreciado e que em versões futuras da Embarcadero será descontinuado.

  13. Em 06/06/2019 at 16:25, picuuai disse:

    Chave Correta da NFe:     35190401186408000162550010000399001000285142

    Chave que o ACBr gerou: 35190401186408000162550010000399001577940241

     O valor em negrito contem a informação do campo cNF. Esse campo compõe a chave de acesso.

    O ACbr atribui um valor aleatório automaticamente pra esse campo, caso você não preencha. Ou seja, quando estiver montando o XML, você vai precisar utilizar um Copy da chave de acesso correta, copiando essa informação e informando no campo cNF do ACBr. 

    • Curtir 1
  14. Boa noite, tente assim:

    ACBrNFe1.EventoNFe.Evento.Clear;
    with ACBrNFe1.EventoNFe.Evento.New do
    begin
      InfEvento.cOrgao          := 91;  //91 - Ambiente Nacional. No caso de evento de manifestação, sempre será 91
      InfEvento.chNFe           := '';  //Chave de acesso da NFe
      InfEvento.CNPJ            := '';  //CNPJ da empresa que está emitindo o evento (o mesmo do certificado digital)
      InfEvento.dhEvento        := now; //Data do evento
      InfEvento.tpEvento        := '';  //teManifDestCiencia, teManifDestConfirmacao, teManifDestOperNaoRealizada, teManifDestDesconhecimento
      InfEvento.detEvento.xJust := '';  //Justificativa, caso seja desconhecimento ou op não realizada
      end;
    ACBrNFe1.EnviarEvento(IdLote);

     

    • Curtir 1
  15. 1 hora atrás, AlexBecker disse:

    Testando o exemplo que me passou se eu deixo o array completo como acima ele me diz que não é um objeto válido

    Isso acontece pois você colocou apenas uma parte do JSON de retorno do iFood. Como eu criei uma classe que lê todos os campos de retorno. Você precisa colocar o JSON de retorno completo. (e funciona caso um item tenha subitems e o outro item não tenha)

    Ou seja, eu não preciso ficar desmembrando o JSON. Em apenas uma linha converto para um objeto (no caso a classe que eu criei - Essa classe deve contem todos os campos retornados).

    Depois eu vou acessando esse objeto. Em tese é a mesma coisa que o ACBr para ler um XML. A única diferença é que o ACBr vai montando o objeto com o retorno da SEFAZ e no meu caso eu utilizo uma função do Próprio Delphi (JsonToObject) para montar o meu objeto.

    Acredito que seja melhor você criar um objeto como falei na primeira resposta. Pois assim, ficará bem mais fácil pra dar manutenção depois. Além de que pra acessar os dados fica de forma simples.

  16. 5 horas atrás, AlexBecker disse:

    o problema ali é que a primeira linha dos itens não retorna a estrutura completa do objeto/array itens se o mesmo não tem subitens

    Sim, quando o iFood monta o JSON, eles ignoram arrays, strings, números, objetos em branco. Deixando assim o retorno somente com o que é necessário.

    Como eu citei acima, aqui eu criei um unit com todos os retornos possível do JSON (property). Depois eu só converto o JSON para um ObjectList usando generics.

    Fiz um exemplo bem simples, só pra mostrar que de um lado eu tenho um memo com o JSON do iFOOD e do outro o meu ObjectList.

    giphy.gif

    Vou deixar o código aqui.

    var
    MyPedidoiFood:  TiFoodPedido;
    x, i: Integer;
    begin
      MyPedidoiFood := TJSON.JsonToObject<TiFoodPedido>(MemoJSON.Lines.Text); //Memo com o JSON do iFood
      try
        for x := 0 to Length(MyPedidoiFood.items) - 1 do
        begin
          MemoClasseDelphi.Lines.Add('Nome mercadoria base: '+MyPedidoiFood.Items[x].name);
          for i := 0 to Length(MyPedidoiFood.items[x].subItems) - 1 do
            MemoClasseDelphi.Lines.Add('Nome subitem: '+MyPedidoiFood.Items[x].subItems[i].name);
          MemoClasseDelphi.Lines.Add('');
        end;
      finally
        MyPedidoiFood.Free;
      end;
    end;

    E o link do exemplo compilado caso queira testar com um JSON seu. 

    5 horas atrás, AlexBecker disse:

    jsontodataset cria automaticamente um campo com 255 caracteres e não entra todo o valor

    Se estiver utilizando TCustomJSONDataSetAdapter. Você pode aumentar o tamanho da string por essa propriedade: StringFieldSize (antes de criar os campos).

    iFood.gif

    • Curtir 1
  17. JsonToDataset não leva em consideração o "detail" de um objeto ou array do json...

    A melhor forma de você tratar isso é convertendo um JSON para um Object usando Generics 

    Em resumo, você cria uma classe no Delphi igual ao JSON que recebe do iFood.

    TPayments = class
      private
        Fname:  string;
        Fcode:  string;
        Fvalue: Extended;
      public
        property name:  string   read Fname  write Fname;
        property code:  string   read Fcode  write Fcode;
        property value: Extended read Fvalue write Fvalue;
      end;
      TArrayPayments = array of TPayments;  
    
    TiFoodPedido = class
      private
        Fid:        string;
        Freference: string;
        Fpayments:  TArrayPayments;
      public
        property id:        string           read Fid        write Fid;
        property reference: string           read Freference write Freference;
        property payments:  TArrayPayments   read Fpayments  write Fpayments;
      end;
      TiFoodPedidos = TObjectList<TiFoodPedido>;

    No caso do payments é um array. Mas terá casos onde serão objetos (merchant, address, customer...). Sendo assim não esqueça de criar constructor e destructor para criar e destruir esses objetos.

    constructor TiFoodPedido.Create;
    begin
      FCustomer        := TCustomer.Create;
      FDeliveryAddress := TDeliveryAddress.Create;
    end;
    
    destructor TiFoodPedido.Destrtoy;
    begin
      FCustomer.Free;
      FDeliveryAddress.Free;
    end;

    Depois você pode simplesmente chamar assim:

    var
    PedidoiFood: TiFoodPedido;
    begin
      PedidoiFood := TJSON.JsonToObject<TiFoodPedido>('JSON de retorno do iFood');
      try
        //dessa forma você consegue ler os items dentro de um array
        PedidoiFood.payments[0].name;
        PedidoiFood.payments[0].code;
        PedidoiFood.payments[0].value;    
      finally
        PedidoiFood.Free;
      end;  
    end;

    Caso queira adicionar mais pedidos na sua lista:

    var
    Pedidos:     TiFoodPedidos;
    PedidoiFood: TiFoodPedido;
    x: Integer;
    begin
      Pedidos := TiFoodPedidos.Create;
      try
        for x := 0 to 5 do
        begin
          PedidoiFood := TJSON.JsonToObject<TiFoodPedido>('JSON de retorno do iFood');
          Pedidos.Add(PedidoiFood);
        end;
        
        //No final poderá acessar assim
        Pedidos.Items[0].payments[0].name;
      finally
        Pedidos.Free;
      end;
    end;        

     

    • Curtir 2
    • Obrigado 1
  18. Você pode converter o JSON em DataSet, eu particularmente acho bem mais fácil.

    • client_id e client_secret: Obtido através do cadastro da SoftHouse no site do iFood.
    • username e password: Obtido através do cadastro do seu cliente no site do IFood. Ou seja, devem ser variáveis no seu software.
    var
    IdHTTPConexao: TidHTTP;
    aArg: TStringList;
    begin
      IdHTTPConexao := TidHTTP.Create;
      aArg          := TStringList.Create;
      try
        aArg.Add('client_id='+FClientID);
        aArg.Add('client_secret='+FClientSecret);
        aArg.Add('grant_type=password');
        aArg.Add('username='+FUsername);
        aArg.Add('password='+FPassword);
    
        with IdHTTPConexao do
        begin
          Request.Accept := 'application/json';
          JsonToDataset(FDMemTable1, Post('https://pos-api.ifood.com.br/oauth/token', aArg));
        end;
    
        FDMemTable1.First;
        FToken   := FDMemTable1.FieldByName('access_token').AsString; //<-Guada esse token para as demais requisições
        FExpires := FDMemTable1.FieldByName('expires_in').AsString;   //<-Deve executar esse método novamente quando expirar
      finally
        IdHTTPConexao.Free;
        aArg.Free;
      end;
    • Após obter o Token é necessário dar GET no endpoint /events:polling a cada 30 segundos. É aqui que você recebe um resumo dos pedidos ainda não capturados.
    var
    IdHTTPConexao: TidHTTP;
    JSONRetorno: TStringStream;
    begin
      IdHTTPConexao := TidHTTP.Create;
      JSONRetorno   := TStringStream.Create('', TEncoding.UTF8);
      try
        with IdHTTPConexao do
        begin
          Request.Clear;
          Request.ContentType         := 'application/json';
          Request.Charset             := 'UFT-8';
          Request.BasicAuthentication := False;
          Request.CustomHeaders.Clear;
          Request.CustomHeaders.AddValue('Content-Type', 'application/json');
          Request.CustomHeaders.AddValue('Authorization', 'bearer'+FToken);
          Request.CustomHeaders.AddValue('cache-control', 'no-cache');
          Request.Accept := 'application/json';
          Get('https://pos-api.ifood.com.br/v1.0/events%3Apolling', JSONRetorno);
          JsonToDataset(FDMemTableResumo, JSONRetorno.DataString);
        end;
    
        with FDMemTableResumo do
        begin
          First;
          while not Eof do
          begin
            FieldByName('id').AsString;            //<-Id do evento. Necessário guardar para limpar a fila depois de processar esse evento.
            FieldByName('code').AsString;          //<-Status retornado para o pedido (Confirmado, cancelado...).
            FieldByName('correlationId').AsString; //<-Código do pedido. Com esse código que você obtem o detalhamento do pedido.
            FieldByName('createdAt').AsString;     //<-Data de criação do evento.
            Next;
          end;
        end;
      finally
        IdHTTPConexao.Free;
        JSONRetorno.Free;
      end;
    end;

    Não vou postar um exemplo pois aqui o código fica grande e cada aplicação tratará diferente os resultados... Mas a lógica é a mesma. Não esqueça de alterar o status do pedido quando processar ele. O status integration é obrigatório.

    • Após processar todos os pedidos, chegou a hora de limpar a fila... Alguns limpam a fila no final do dia. Mas particularmente eu prefiro limpar a fila a cada execução do polling.
    IdHTTPConexao: TidHTTP;
    aArg: TStringStream;
    FIds: string;
    begin
      FIds := '';
      with FDMemTableResumo do
      begin
        while not Eof do
        begin
          if FIds <> '' then
            FIds := FIds + ', ';
          FIds := FIds + '{"id":"'+FieldByName('id').AsString+'"}';
          Next;
        end;
      end;
    
      if FIds <> '' then
      begin
        FIds          := '['+FIds+']';
        IdHTTPConexao := TidHTTP.Create;
        aArg          := TStringStream.Create(FIds, TEncoding.UTF8);
        try
          with IdHTTPConexao do
          begin
            Request.Clear;
            Request.ContentType         := 'application/json';
            Request.Charset             := 'UFT-8';
            Request.BasicAuthentication := False;
            Request.CustomHeaders.Clear;
            Request.CustomHeaders.AddValue('Content-Type', 'application/json');
            Request.CustomHeaders.AddValue('Authorization', 'bearer'+FToken);
            Request.CustomHeaders.AddValue('cache-control', 'no-cache');
            Request.Accept := 'application/json';
            Post('https://pos-api.ifood.com.br/v1.0/events/acknowledgment', aArg);
          end;
        finally
          aArg.Free;
          IdHTTPConexao.Free;
        end;
      end;

    Enfim, é mais ou menos isso... Claro, da pra otimizar muito esse código. Deixei da forma mais simples possível. Uma outra solução seria utilizar RestClient ao invés do Indy..

    Vou deixar alguns links bem legais explicando com mais detalhes o funcionamento:

    Documentação iFood

    A função JsonToDataSet pode ser encontrada aqui.

    Palestra do Kleberson Toro para o CodeRage Brasil 2019

    Fórum do iFood

    Ah, uma observação... Na documentação do iFood já podemos notar que existe uma versão 2.0 para a API. A mesma está liberada apenas para /orders. Então caso for integrar, já use o novo endrpoints

    • Curtir 1
  19. Estranho só conseguir o XML completo com a confirmação da operação. Nos testes que acabei de fazer aqui, com a ciência da operação já consegui ter acesso ao XML completo.

     

    Pode existir um delay entre a manifestação do destinatário e o A.N disponibilizar o XML completo. Já presenciei casos onde esse tempo foi de mais de 1 hora. Assim como no mesmo segundo já obtive o XML completo.

    Aqui após o evento de manifestação do destinatário, eu executo o método DistribuicaoDFePorChaveNFe. Caso o retorno seja 137 - nenhum documento localizado, eu sei que o A.N ainda não disponibilizou o XML completo. Dessa forma eu trato isso com tentativas. Ou seja, tento por X vezes (com um tempo de espera de alguns segundos entre cada tentativa). Caso esgote as tentativas, aviso o usuário que o XML ainda não foi disponibilizado e pra tentar novamente...

    • Curtir 3
  20. Boa noite,

    Você precisa incrementar um número na propriedade nItem a cada item adicionado.

    Então ficará mais ou menos assim:

    var
    NotaF: NotaFiscal;
    Produto: TDetCollectionItem;
    iNumItem: Integer;
    begin
      NotaF := ACBrNFe1.NotasFiscais.Add;
      {...}
    
      iNumItem := 0;
      while not Query.Eof do
      begin
        iNumItem := iNumItem + 1;
        Produto := NotaF.NFe.Det.New;
        Produto.Prod.nItem    := iNumItem; //<-Número sequencial, para cada item deve ser incrementado   
        {...}
        Query.Next;
      end;
    end;

    Parte do código acima foi retirado do exemplo do ACBr.

    • Curtir 2
  21. 17 minutos atrás, JHONLENON disse:

    manifesta tudo certinho.......mais na hora do dawlods as vezes dá erro...

    Você está tentando ler o item 0 porém não existe o item zero. Isso acontece porque provavelmente o Ambiente Nacional ainda não disponibilizou o XML completo da nota fiscal. (sim pode existir um delay grande entre a manifestação do destinatário e o A.N disponibilizar o XML)

    O que você deve fazer é primeiro verificar se existe items retornados pelo A.N. Através da propriedade "count".

    if ACBrNFe1.WebServices.DistribuicaoDFe.retDistDFeInt.docZip.Count > 0 then

    E um detalhe importante; é retornado também em uma propriedade o código de retorno. Ou seja, dependendo do código de retorno, você saberá se tem alguma informação que precisa ser lida.

    cStat := ACBrNFe1.WebServices.DistribuicaoDFe.retDistDFeInt.cStat;
    
    cStat = 137 (Nenhum documento localizado)
    cStat = 138 (Documentos encontrados)

    Verificações como essa devem ser feitas antes de ler algum "item" pois pode acontecer exatamente o que mencionou acima.

    • Curtir 1
  22. 4 minutos atrás, Suporte AZ disse:

    Obrigado pela ajuda! Só mais uma questão, por exemplo essa nova tag do Responsável Técnico, se ela aparecer em todos os estados, até os que ainda não estão obrigatórios ocorre algum problema ou não faz mal?

    Terá problemas (rejeição)... Já que o estado ainda não está preparado para receber essa informação.

    • Curtir 2
×
×
  • Criar Novo...

Informação Importante

Colocamos cookies em seu dispositivo para ajudar a tornar este site melhor. Você pode ajustar suas configurações de cookies, caso contrário, assumiremos que você está bem para continuar.