geovanesilveira
Membros-
Total de ítens
19 -
Registro em
-
Última visita
Últimos Visitantes
O bloco dos últimos visitantes está desativado e não está sendo visualizado por outros usuários.
geovanesilveira's Achievements
-
Possível correção para problemas de Out of Memory
geovanesilveira replied to geovanesilveira's tópico in ACBrSPEDContabil
Confesso que não considero uma boa solução mesmo, foi apenas o jeito mais rapido e pratico que achei de dar uma alternativa às pessoas que tiveram esse mesmo problema na rotina, e que ainda queiram usufruir dos fontes comitados, sem precisar ter um controle dessa unit por fora, ou por um repositorio local -
Possível correção para problemas de Out of Memory
geovanesilveira replied to geovanesilveira's tópico in ACBrSPEDContabil
Atenção, a classe helper da que falo abaixo deverá ser apenas utilizada por programadores que também tem problema na rotina TotalizarTermos, executada pelo SaveFileTXT. Se você não tem esse problema, ignore este post. Fiz os testes aqui, e esta funcionando normal a parte dos registros, porem, ele não consegue totalizar os termos. Olhando bem, acho que esse tipo de situação poderia ser resolvida pelo proprio programador, disponibilizando a rotina TotalizarTermos para ser usada em classes helper. Com isso, a rotina SaveFileTXT não poderia ser mais utilizada, ja que é ela que finalizava o processo. Agora seria necessário chamar o TotalizarTermos e FinalizarGeracao (rotina nova) no lugar do SaveFileTXT. Do jeito que eu havia feito nos uploads anteriores, é preciso acessar a variavel FACBrTXT, logo, precisei alterar a TACBrSPEDContabil pra disponibilizar o acesso à esse objeto. Após reinstalar os componentes do ACBr, criei o seguinte helper: unit ExemploHelper; ... TACBrSPEDContabilHelper = class helper for TACBrSPEDContabil public procedure TotalizarTermos; end; Comparação de como era o código antes e de como vai ficar: // ANTIGO SPED.IniciaGeracao(True); ... SPED.SaveFileTXT; // NOVO uses ExemploHelper; ... try SPED.IniciaGeracao(True); ... SPED.TotalizarTermos; finally SPED.FinalizarGeracao; end; ACBrSpedContabil.pas -
Possível correção para problemas de Out of Memory
geovanesilveira replied to geovanesilveira's tópico in ACBrSPEDContabil
Segue uma correção. Faltou limpar alguns objetos que estavam presentes em uns List ACBrSpedContabil.pas -
Possível correção para problemas de Out of Memory
geovanesilveira replied to geovanesilveira's tópico in ACBrSPEDContabil
Opa, percebi só agora que havia feito o upload dos arquivos errados. Segue os corretos agora ACBrECDBloco_9.pas ACBrSpedContabil.pas -
Possível correção para problemas de Out of Memory
geovanesilveira replied to geovanesilveira's tópico in ACBrSPEDContabil
Só uma correção no exemplo de como fica pra utilizar esse novo metodo (os Writes deveriam ser do SPED.WriteRegistro???? não do with SPED.Bloco_0 do) SPED.IniciaGeracao(True); // NOVO with SPED.Bloco_0 do begin with Registro0000 do ... SPED.WriteRegistro0000; // NOVO with Registro0007.New do ... SPED.WriteRegistro0007; // NOVO for i := 0 to 2 do begin with Registro0150.New do begin ... with Registro0180.New do ... end; SPED.WriteRegistro0150; // NOVO end; end; SPED.WriteBloco_0; // NOVO SPED.SaveFileTXT; -
Possível correção para problemas de Out of Memory
um tópico no fórum postou geovanesilveira ACBrSPEDContabil
Citando o problema occorido no post sobre Out of Memory no Bloco I, estava pensando em um forma de modificar o componente com o menor impacto possível. Achei uma solução que me parece ser adequada e gostaria de saber se é aceitavel esse tipo de modificação. Hoje, o componente trabalha com alguns dados sendo alimentados em um TObjectList, o qual são percorridos quando chama a função SaveFileTXT. A minha ideia foi, ao inves de ficar segurando esses itens até o fim, porque não ir alimentado o arquivo conforme os registros de cada bloco iriam sendo finalizados? Com isso em mente, eu precisava informar pro componente que iria querer trabalhar de uma forma diferenciada quanto a alimentação do arquivo, o qual nos leva a primera modificação no ACBrSpedContabil.pas: procedure IniciaGeracao(const pWriteManual: Boolean = False); Esse parametro será salvo em uma variavel (privada) que será usada ao longo dos WriteBlocos presentes no componente. Como o nome indica, os Writes (tanto WriteBloco_? quanto WriteRegistro????, com exceção dos ?990 que são totalizadores) serão feitos manualmente, ou seja, o aplicativo precisa chamar esses comandos, o que nos leva a segunda modificação no ACBrSpedContabil.pas: public procedure WriteRegistro0000; procedure WriteRegistro0001; ... procedure WriteRegistroI001; ... procedure WriteRegistroJ001; ... Com esses comandos em public, a sua aplicação ja consegue acessa-los. Apartir daqui, precisei informar aos WriteBloco_? que quando for em modo WriteManual, ele não deve chamar as rotinas que não sejam de totalizadores. Segue (alguma das, são basicamente iguais em todos os blocos, menos no 9) a terceira modificação no ACBrSpedContabil.pas: procedure TACBrSPEDContabil.WriteBloco_0; begin if Bloco_0.Gravado then exit ; if not FInicializado then raise Exception.Create( 'Métodos "IniciaGeracao" não foi executado' ); if not(FWriteManual) then begin WriteRegistro0000; WriteRegistro0001; WriteRegistro0007; WriteRegistro0035; WriteRegistro0020; WriteRegistro0150; //WriteRegistro0180; end; WriteRegistro0990; Bloco_0.WriteBuffer; Bloco_0.Conteudo.Clear; Bloco_0.Gravado := true; end; Os outros blocos seguem o mesmo padrão, apenas o WriteRegistro?990 esta fora da validação do WriteManual, exceto o WriteBloco_9. Segue o mesmo: procedure TACBrSPEDContabil.WriteBloco_9; begin if Bloco_9.Gravado then Exit; if not Bloco_9.Gravado then WriteBloco_K; if not(FWriteManual) then WriteRegistro9001; WriteRegistro9900; WriteRegistro9990; WriteRegistro9999; Bloco_9.WriteBuffer; Bloco_9.Conteudo.Clear; Bloco_9.Gravado := true; end; Como o proprio bloco ja é um totalizador em si, e só é necessário informar o registro 9001, ele contem outras rotinas fora da validação do WriteManual. Falando no Bloco 9, ele me apresentou casos que me fizeram modificar o ACBrECDBloco_9.pas. Segue ela: function TRegistro9900List.FindByREG_BLC(const pValue: String; out pReg: TRegistro9900): Boolean; var i: Integer; begin Result := False; for i := 0 to (Count - 1) do if (TRegistro9900(Items[i]).REG_BLC = pValue) then begin pReg := TRegistro9900(Items[i]); Result := True; Break; end; end; Em registros como o do I200, K030, existe a relação de mestre-detalhe entre os itens, e como os WriteRegistro???? sempre chamam a rotina que cria o seu respectivo totalizador no bloco 9, foi preciso fazer um jeito de que quando existisse mais de um registro mestre, ele não ficasse criando registros subsequentes no bloco 9, e apartir disso, surgiu a rotina acima. Voltando ao ACBrSpedContabil.pas, como precisava usar a FindByREG_BLC, ja aproveitei e dei ao luxo de centralizar a rotina que gera os totalizadores do bloco 9. Segue ela: procedure TACBrSPEDContabil.AddRegistro9900(const pREG_BLC: String; const pQTD_REG_BLC: Integer); var Reg: TRegistro9900; begin if (pQTD_REG_BLC > 0) then begin if not(Bloco_9.Registro9900.FindByREG_BLC(pREG_BLC, Reg)) then begin Reg := Bloco_9.Registro9900.New; Reg.REG_BLC := pREG_BLC; end; Reg.QTD_REG_BLC := Reg.QTD_REG_BLC + pQTD_REG_BLC; end; end; Com essa rotina, os WriteRegistro???? passaram a utilizar elas ao inves de cada um chamar o bloco 9 individualmente. Segue alguns exemplos: // NOVO procedure TACBrSPEDContabil.WriteRegistro0000; begin Bloco_0.WriteRegistro0000; AddRegistro9900('0000', 1); end; // ANTIGO procedure TACBrSPEDContabil.WriteRegistro0000; begin with Bloco_9.Registro9900.New do begin REG_BLC := '0000'; QTD_REG_BLC := 1; end; Bloco_0.WriteRegistro0000; end; // NOVO procedure TACBrSPEDContabil.WriteRegistro0007; begin Bloco_0.WriteRegistro0007; AddRegistro9900('0007', Bloco_0.Registro0007.Count); if FWriteManual then Bloco_0.Registro0007.Clear; end; // ANTIGO procedure TACBrSPEDContabil.WriteRegistro0007; begin if Bloco_0.Registro0007.Count > 0 then begin with Bloco_9.Registro9900.New do begin REG_BLC := '0007'; QTD_REG_BLC := Bloco_0.Registro0007.Count; end; end; Bloco_0.WriteRegistro0007; end; // NOVO procedure TACBrSPEDContabil.WriteRegistro0150; begin Bloco_0.WriteRegistro0150; AddRegistro9900('0150', Bloco_0.Registro0150.Count); AddRegistro9900('0180', Bloco_0.Registro0180Count); if FWriteManual then begin Bloco_0.Registro0150.Clear; Bloco_0.Registro0180Count := 0; end; end; // ANTIGO procedure TACBrSPEDContabil.WriteRegistro0150; begin Bloco_0.WriteRegistro0150; if Bloco_0.Registro0150.Count > 0 then begin with Bloco_9.Registro9900.New do begin REG_BLC := '0150'; QTD_REG_BLC := Bloco_0.Registro0150.Count; end; end; if Bloco_0.Registro0180Count > 0 then begin with Bloco_9.Registro9900.New do begin REG_BLC := '0180'; QTD_REG_BLC := Bloco_0.Registro0180Count; end; end; end; Como você pode perceber, algumas dessas rotinas começarão a dar Clear no TObjectList, e, zerar as variaveis de Count dos detalhes, quando presente. Isso acontece pois cada Bloco_?.WriteRegistro???? percorre sempre todo o TObjectList, então cada vez que chamarmos manualmente essa rotina, ele não pode mais alimentar o que ja havia sido alimentado antes. O mesmo serve para a variavel de Count, como a rotina AddRegistro9900 é incremental, precisa zera-la, para que na proxima leva, incremente apenas o que foi informado ali. Feito isso, o componente já esta preparado para funcionar dessa nova forma de alimentação. Como implementar isso na minha aplicação? Usando apenas os blocos acimas para exemplificar, segue como é feito hoje: SPED.IniciaGeracao; with SPED.Bloco_0 do begin with Registro0000 do ... with Registro0007.New do ... for i := 0 to 2 do with Registro0150.New do begin ... with Registro0180.New do ... end; end; SPED.SaveFileTXT; E como deverá ser feito, caso queira utilizar o WriteManual: SPED.IniciaGeracao(True); // NOVO with SPED.Bloco_0 do begin with Registro0000 do ... WriteRegistro0000; // NOVO with Registro0007.New do ... WriteRegistro0007; // NOVO for i := 0 to 2 do begin with Registro0150.New do begin ... with Registro0180.New do ... end; WriteRegistro0150; // NOVO end; end; SPED.WriteBloco_0; // NOVO SPED.SaveFileTXT; Seguindo essa lógica para os outros blocos/registros, seus dados finais deverão ser identicos ao mesmo metodo de hoje, e também identicos quando for usado WriteManual False. Também modifiquei a rotina TotalizarTermos usando um método que achei na internet (não consigo mais achar) pois a base de dados que estava usando para fazer os testes resulta em arquivos consideravelmente grandes (4gb+, 40mi+ linhas) e a rotina antiga simplismente não finalizava. ACBrECDBloco_9.pas ACBrSpedContabil.pas -
Sped Contábil - Bloco I - Out of Memory
geovanesilveira replied to Marcos JasKow's tópico in ACBrSPEDContabil
Ja tive esse problema a um ano atras, também com um cliente que tinha uma quantidade massiva de informações (4gb+, 40mi+ de linhas). No meu caso, alem do bloco I dar problema, a rotina TotalizarTermos também não funcionava, e tive que modificar a mesma. Segue em anexo as minhas modificações. Essas modificações requerem que você também mexa na unit que gera essas informações pelo ACBrSPED. Não consegui pensar numa forma de que tudo isso ficasse dentro do componente sem precisar ter alterações externas. - Fazer a classe "hack" da TACBrSPEDContabil Na unit que gera os dados do sped, é necessario ter um type sobre o proprio componente do TACBrSPEDContabil. Serve pra utilizar as rotinas que não estão no public e que eu, propositalmente, não coloquei la, pois não queria que a classe começasse a ficar com muito "lixo" de informação. TACBrSPEDContabilHack = class(TACBrSPEDContabil); - Bloco I precisa ter seus registros chamados manualmente Não utilizaremos mais a rotina TACBrSPEDContabil.WriteBloco_I para escrever nosso Bloco I. Basicamente, para cada chamada que existe na rotina WriteBloco_I, ela sera feita logo depois de adicionar os registros. Exemplo: with ComponenteSPEDContabil.Bloco_I do begin ... DataSet.First; while not(DataSet.Eof) do begin with RegistroI050.New do begin ... with RegistroI051.New do begin ... end; with RegistroI052.New do begin ... end; end; DataSet.Next; end; end; TACBrSPEDContabilHack(ComponenteSPEDContabil).WriteRegistroI050; ComponenteSPEDContabil.Bloco_I.RegistroI050.Clear; Esse metodo de adicionar todos os registros necessarios e escrever depois funcionou em todos os registros, exceto no I200 e I250, que são os mais pesados, no meu caso. - Registros I200 e I250, casos especiais Esses dois registros são os responsaveis pelo qual optei por fazer o arquivo ir salvando conforme vai recebendo dados, pois o computador não aguentava tudo em memória. Na rotina que gera essas linhas, são necessários 3 variaveis pra ela funcionar: i_200_9900, i_250_9900: Integer; Reg200: TRegistroI200; Conforme as modificações na unit do ABCrSpedContabil, a rotina WriteRegistroI200 agora pode receber 2 parametros (integer) de entrada, os quais vão guardar o index do registro 9900 para o registro I200 e I250. Como há muito registro nesse bloco, é necessário fazer com que ele vá salvando os registros I200 conforme for preciso, ao inves de adicionar tudo e fazer no final, como os outros blocos, e essas duas variaveis integer, vão salvar pra nós o index do bloco 9 que correspondem aos registros I200 e I250. A variavel Reg200 serve pra dizer quando é preciso criar um novo registro. No meu caso, os registros I200 só são criados quando há uma troca em uma das seguintes propriedades dele: NUM_LCTO ou DT_LCTO ou IND_LCTO. Quando isso ocorre, eu sei que ele vai trocar de I200, então é nesse momento que eu faço ele salvar no txt os dados que contêm na variavel. Código exemplo de toda essa situação: Reg200 := nil; i_200_990 := -1; i_250_9900 := -1; with ComponenteSPEDContabil.Bloco_I do begin ... DataSet.First; while not(DataSet.Eof) do begin if not(Assigned(Reg200)) or ((Reg200.NUM_LCTO <> DataSet['EMP'] +'-'+ DataSet['LOTE'] +'-'+ DataSet['DATA']) or (Reg200.DT_LCTO <> DataSet['DATA']) or (Reg200.IND_LCTO <> DataSet['TIPO'])) then begin if Assigned(Reg200) then begin TACBrSPEDContabilHack(ComponenteSPEDContabil).WriteRegistroI200(i_200_990, i_250_9900); ComponenteSPEDContabil.Bloco_I.RegistroI200.Clear; ComponenteSPEDContabil.Bloco_I.RegistroI250Count := 0; end; Reg200 := RegistroI200.New; Reg200.NUM_LCTO := DataSet['EMP'] +'-'+ DataSet['LOTE'] +'-'+ DataSet['DATA']; Reg200.DT_LCTO := DataSet['DATA']; Reg200.IND_LCTO := DataSet['TIPO']; .... end; with Reg200.RegistroI250.New do begin ... end; with Reg200.RegistroI250.New do begin ... end; DataSet.Next; end end; TACBrSPEDContabilHack(ComponenteSPEDContabil).WriteRegistroI200(i200, i250); ComponenteSPEDContabil.Bloco_I.RegistroI200.Clear; ComponenteSPEDContabil.Bloco_I.RegistroI250Count := 0; Outro caso especial nesses registro é que é preciso limpar a variavel RegistroI250Count, pois ela é um contador dos I250 que são adicionados num I200, então mesmo que você de um Clear nos I200, essa variavel ja foi incrementada durante o .New do I250, então tem que limpar ela. - Finalizando o Bloco I Apos isso, a unica coisa que resta é chamar as rotinas que vão finalizar o nosso Bloco I. São elas: TACBrSPEDContabilHack(ComponenteSPEDContabli).WriteRegistroI990; ComponenteSPEDContabli.Bloco_I.WriteBuffer; ComponenteSPEDContabli.Bloco_I.Conteudo.Clear; ComponenteSPEDContabli.Bloco_I.Gravado := True; - TotalizarTermos Depois que todos os blocos foram gerados, é necessário chamar a rotina que vai trocar as tags [**] pelo número de registros do componente. A chamada dela é simples, mas ela deve ser a ultima coisa que a sua rotina chama: TACBrSPEDContabilHack(ComponenteSPEDContabil).TotalizarTermos; A mudança que eu fiz nela foi devido ao arquivo ser muito grande, ele também estava dando Out of memory, então após algumas pesquisas na internet, me deparei com uma rotina que funcionou perfeitamente para o meu caso. - Como disse antes, não consegui pensar num jeito bom de implementar isso no componente do ACBr, pois acredito que o jeito mais certo seria ter um propriedade (provavelmente boolean) que indicasse que tu queria fazer o arquivo ser salvo por registro. Como estava com pouco tempo por agora, e eu ja sabia que esse metodo funcionava, optei por so usar ele mesmo e compartilhar com a comunidade pra ver se alguem não tem alguma ideia que possa ser implementa no componente. Tentei resumir um pouco da situação toda e dos codigos, então se alguem ainda tiver duvidas, estou disposto a ajudar. ACBrECDBloco_9.pas ACBrSpedContabil.pas -
Tag <codMaterialCliente>, filha da <infAdProd>
geovanesilveira replied to geovanesilveira's tópico in ACBrNFe
Foi um dos primeiros teste que fiz, e percebi que não poderia ser desse jeito. A não ser que eles fizessem a leitura de nodes de um modo diferente que possibilitasse esse "hack", mas não acredito que isso seria posto em pratica. Realmente, no momento em que estava fazendo isso nem me passou pela cabeça que isso seria bloqueado pelo schemas. (e tinha esquecido sobre esse site validador) -
Seguinte, a Votorantim requer que na tag <infAdProd> seja adicionado uma tag filha, chamada <codMaterialCliente> Conforme manual (começa na página 71) Hoje, a tag <infAdProd> é uma string, não possibilitando fazer a situação citada. Gostaria de saber se é possível adicionar um node em tempo de execução? Ou, se seria mais correto, já criar propriedades e rotinas para atender esse tipo de ocorrência.
-
Sistema não enviou um SAT e o cliente gerou outro e enviou este ANTES de enviar o anterior
um tópico no fórum postou geovanesilveira ACBrSAT
Boa noite, acabou de acontecer com um cliente meu o seguinte: Meu cliente fez a venda nº 500. Por algum motivo, o sistema não conseguiu enviar a mesma e o cliente também não percebeu. Seguindo o ritmo dele, outras 5 vendas (nºs: 501~505) foram efetuadas e todas enviaram. Após um tempo, o cliente notou que havia esta nota (nº 500) pendente e enviou a mesma. Depois que foi feito isso, o SAT parece que voltou no tempo, e todas a notas que eu tento fazer apartir de agora, elas saem como se fossem a próxima venda que tinha sido feito na sequencia ANTES de ele tentar ter enviado essa pendente. Simplificando: Após de ter enviado as notas 501~505 ele voltou para enviar a 500. Depois disso as novas notas saem como se fosse as antigas. 506 é emitida como se fosse a nota 501. 507 igual a 502. E assim por diante. O cliente não consegue mais tirar nenhuma nota, pois todas elas saem como se fosse a nota anterior. Alguém já passou ou sabe como resolver esse problema? -
Desculpe a demora, mas é exatamente como o Fernando falou. Após pesquisar mais não consegui de maneira nenhuma automatizar isso, no fim, abri o site (com o panel da solicitação ja aberto) e com as informações ja preenchida, dai precisa apenas informar o captcha e clicar em preencher. Caso alguem descobrir algo e querer compartilhar mesmo assim, esteja livre para faze-lo.
-
Após dar uma olhada no fonte do ACBRGNRe, notei que não existe a opção de solicitar o uso do webservice. A imagem abaixo mostra o que eu quis dizer. Depois de ler os manuais da GNRe e o código fonte da página (que, dessa vez, não me ajudou em nada), não achei em nenhum lugar algo que indique como automatizar essa opção. Seria esse o motivo de não ter essa opção no GNRe? Ou há uma maneira, porém, não foi implementada? Essa seria uma das coisas que iria implementar no sistema, porém não estou conseguindo montar a URL, caso alguem tenha alguma ideia, favor, compartilhar.
-
Alteração em ACBrNFSeWebServices (provedor Betha)
um tópico no fórum postou geovanesilveira ACBrNFSe
A modificação e o exemplo abaixo servem apenas para quem tem como provedor a Betha. Após atualizar os fontes do ACBr, há um código em TNFSeWebService.InicializarGerarDadosMsg (unit ACBrNFSeWebServices) que cria um exception caso a Inscrição Municipal esteja vazia: if IM = '' then GerarException(ACBrStr('A I.M. não informada em: Configuracoes.Geral.Emitente.InscMun')); Porém quem usa o provedor Betha, em ambiente de HOMOLOGAÇÃO, não se deve informar a IM. Abaixo segue a linha corrigida e o anexo da unit: if (IM = '') and ((Provedor <> proBetha) or (FPConfiguracoes.WebServices.Ambiente = taProducao)) then GerarException(ACBrStr('A I.M. não informada em: Configuracoes.Geral.Emitente.InscMun')); ACBrNFSeWebServices.pas -
Mensagem de retorno do status 539 (duplicidade)
geovanesilveira replied to geovanesilveira's tópico in ACBrCTe
3Soft, sim, mas por questões de facilitação, traze-la na mensagem não deveria ser um problema, não? -
Mensagem de retorno do status 539 (duplicidade)
geovanesilveira replied to geovanesilveira's tópico in ACBrCTe
3Soft, entendi a diferença entre elas, porém no segundo caso, 204, a mensagem não traz a chave, apena o número de recibo do lote, ao qual propus a alteração para fazer trazer, mas ja que a mensagem vem do XML, o único jeito seria fazer um condição só pra ela (o que não é legal): for I := 0 to FConhecimentos.Count - 1 do begin if not FConhecimentos.Items[I].Confirmado then begin FPMsg := FPMsg + IntToStr(FConhecimentos.Items[I].CTe.Ide.nCT) +'->'+ FConhecimentos.Items[I].Msg; if (FcStat = 204) then FPMsg := FPMsg +' [chCTE: '+ FConhecimentos.Items[I].NumID +']'; FPMsg := FPMsg + LineBreak; end; end;