Ir para conteúdo
  • Cadastre-se

Gabriel Franciscon

Membros
  • Total de ítens

    100
  • Registro em

  • Última visita

Tudo que Gabriel Franciscon postou

  1. Olá pessoal! tudo bem? Na unidade ACBrPIXPSPMercadoPago (Fontes\ACBrPIXCD) está dando uses em "Dialogs". Porém não está sendo utilizado para nada... Seria possível remover esse uses desnecessário? ACBrPIXPSPMercadoPago.pas
  2. Olá pessoal! tudo bem? Na unidade ACBrNFSeXDANFSeFPDFClass (Fontes\ACBrDFe\ACBrNFSeX\DANFSE\FPDF) está dando uses em "Forms". Porém não está sendo utilizado para nada... Seria possível remover esse uses desnecessário? ACBrNFSeXDANFSeFPDFClass.pas
  3. 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
  4. 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.
  5. 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.
  6. Boa tarde, 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:
  7. 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
  8. Exatamente... Pois aí você terá todas as informações que são necessárias para gerar a mesma chave de acesso novamente
  9. 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.
  10. Isso acontece pois você está passando para o campo cNF o mesmo valor de nNF. De uma olhada para mais detalhes:
  11. 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.
  12. 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 DistribuicaoDFePorUltNSU (trará: Nota resumida, Nota completa, Eventos resumido e Eventos completo) Realizar a manifestação Executar o método DistribuicaoDFePorUltNSU novamente (ou o DistribuicaoDFePorChaveNFe)
  13. 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: Aplicativo configurado para realizar a manifestação do destinatário automaticamente com a Ciência da operação ao encontrar um novo documento. Usuário realiza o recebimento de mercadorias no sistema: Disparo a Confirmação da operação Desconhecimento ou Operação não realizada fica por conta do usuário. Caso fique notas sem a "conclusão" da manifestação por parte do destinatário, alerto o mesmo através de notificações.
  14. 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) 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;
  15. Se a mensagem for: Evento registrado mas não vinculado a NF-e Pode significar que: 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) 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.
  16. 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.
  17. 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);
  18. 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.
  19. 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. 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. Se estiver utilizando TCustomJSONDataSetAdapter. Você pode aumentar o tamanho da string por essa propriedade: StringFieldSize (antes de criar os campos).
  20. 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;
  21. 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; O próximo passo é executar o endpoints /orders/{reference} através do método GET (https://pos-api.ifood.com.br/v1.0/orders/reference). Substituindo o valor "reference" pelo o correlationId. Ou seja do pedido que deseja. 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
  22. 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...
  23. 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.
  24. 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.
  25. Terá problemas (rejeição)... Já que o estado ainda não está preparado para receber essa informação.
×
×
  • 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.