Ir para conteúdo
  • Cadastre-se

Roberto Henrique Borges Machado

Membros
  • Total de ítens

    35
  • Registro em

  • Última visita

Tudo que Roberto Henrique Borges Machado postou

  1. Segue Schema da SEFAZ. <xs:element name="ICMS51"> <xs:annotation> <xs:documentation>Tributção pelo ICMS 51 - Diferimento. A exigência do preenchimento das informações do ICMS diferido fica à critério de cada UF.</xs:documentation> </xs:annotation> <xs:complexType> <xs:sequence> <xs:element name="orig" type="Torig"> <xs:annotation> <xs:documentation>origem da mercadoria: 0 - Nacional 1 - Estrangeira - Importação direta 2 - Estrangeira - Adquirida no mercado interno</xs:documentation> </xs:annotation> </xs:element> <xs:element name="CST"> <xs:annotation> <xs:documentation>Tributação pelo ICMS 51 - Tributação com Diferimento</xs:documentation> </xs:annotation> <xs:simpleType> <xs:restriction base="xs:string"> <xs:whiteSpace value="preserve"/> <xs:enumeration value="51"/> </xs:restriction> </xs:simpleType> </xs:element> <xs:element name="modBC" minOccurs="0"> <xs:annotation> <xs:documentation>Modalidade de determinação da BC do ICMS: 0 - Margem Valor Agregado (%); 1 - Pauta (valor); 2 - Preço Tabelado Máximo (valor); 3 - Valor da Operação.</xs:documentation> </xs:annotation> <xs:simpleType> <xs:restriction base="xs:string"> <xs:whiteSpace value="preserve"/> <xs:enumeration value="0"/> <xs:enumeration value="1"/> <xs:enumeration value="2"/> <xs:enumeration value="3"/> </xs:restriction> </xs:simpleType> </xs:element> <xs:element name="pRedBC" type="TDec_0302a04" minOccurs="0"> <xs:annotation> <xs:documentation>Percentual de redução da BC</xs:documentation> </xs:annotation> </xs:element> <xs:element name="cBenefRBC" minOccurs="0"> <xs:annotation> <xs:documentation>Código de Benefício Fiscal na UF aplicado ao item quando houver RBC.</xs:documentation> </xs:annotation> <xs:simpleType> <xs:restriction base="xs:string"> <xs:whiteSpace value="preserve"/> <xs:pattern value="[!-ÿ]{8}|[!-ÿ]{10}"/> </xs:restriction> </xs:simpleType> </xs:element> <xs:element name="vBC" type="TDec_1302" minOccurs="0"> <xs:annotation> <xs:documentation>Valor da BC do ICMS</xs:documentation> </xs:annotation> </xs:element> <xs:element name="pICMS" type="TDec_0302a04" minOccurs="0"> <xs:annotation> <xs:documentation>Alíquota do imposto</xs:documentation> </xs:annotation> </xs:element> <xs:element name="vICMSOp" type="TDec_1302" minOccurs="0"> <xs:annotation> <xs:documentation>Valor do ICMS da Operação</xs:documentation> </xs:annotation> </xs:element> <xs:element name="pDif" type="TDec_0302a04Max100" minOccurs="0"> <xs:annotation> <xs:documentation>Percentual do diferemento</xs:documentation> </xs:annotation> </xs:element> <xs:element name="vICMSDif" type="TDec_1302" minOccurs="0"> <xs:annotation> <xs:documentation>Valor do ICMS da diferido</xs:documentation> </xs:annotation> </xs:element> <xs:element name="vICMS" type="TDec_1302" minOccurs="0"> <xs:annotation> <xs:documentation>Valor do ICMS</xs:documentation> </xs:annotation> </xs:element> <xs:sequence minOccurs="0"> <xs:element name="vBCFCP" type="TDec_1302"> <xs:annotation> <xs:documentation>Valor da Base de cálculo do FCP.</xs:documentation> </xs:annotation> </xs:element> <xs:element name="pFCP" type="TDec_0302a04Opc"> <xs:annotation> <xs:documentation>Percentual de ICMS relativo ao Fundo de Combate à Pobreza (FCP).</xs:documentation> </xs:annotation> </xs:element> <xs:element name="vFCP" type="TDec_1302"> <xs:annotation> <xs:documentation>Valor do ICMS relativo ao Fundo de Combate à Pobreza (FCP).</xs:documentation> </xs:annotation> </xs:element> </xs:sequence> <xs:sequence minOccurs="0"> <xs:element name="pFCPDif" type="TDec_0302a04Opc"> <xs:annotation> <xs:documentation>Percentual do diferimento do ICMS relativo ao Fundo de Combate à Pobreza (FCP).</xs:documentation> </xs:annotation> </xs:element> <xs:element name="vFCPDif" type="TDec_1302"> <xs:annotation> <xs:documentation>Valor do ICMS relativo ao Fundo de Combate à Pobreza (FCP) diferido.</xs:documentation> </xs:annotation> </xs:element> <xs:element name="vFCPEfet" type="TDec_1302" minOccurs="0"> <xs:annotation> <xs:documentation>Valor efetivo do ICMS relativo ao Fundo de Combate à Pobreza (FCP).</xs:documentation> </xs:annotation> </xs:element> </xs:sequence> </xs:sequence> </xs:complexType> </xs:element> ACBr: 986 cst20: 987 begin 988: xmlNode.AppendChild(AddNode(tcDe2, '#133', 'pRedBC', 1, 5, 1, 989: BPe.Imp.ICMS.pRedBC, DSC_PREDBC)); 990 991 xmlNode.AppendChild(AddNode(tcDe2, '#134', 'vBC', 1, 15, 1, ... 1024 cst90: 1025 begin 1026: xmlNode.AppendChild(AddNode(tcDe2, '#143', 'pRedBC', 1, 5, 0, 1027: BPe.Imp.ICMS.pRedBC, DSC_PREDBC)); 1028 1029 xmlNode.AppendChild(AddNode(tcDe2, '#144', 'vBC', 1, 15, 1, .... 1301 cst20: 1302 begin 1303: xmlNode.AppendChild(AddNode(tcDe2, '#133', 'pRedBC', 1, 5, 1, 1304: Imposto.ICMS.pRedBC, DSC_PREDBC)); 1305 1306 xmlNode.AppendChild(AddNode(tcDe2, '#134', 'vBC', 1, 15, 1, .... 1339 cst90: 1340 begin 1341: xmlNode.AppendChild(AddNode(tcDe2, '#143', 'pRedBC', 1, 5, 0, 1342: Imposto.ICMS.pRedBC, DSC_PREDBC)); Aparentemente não está implementado no ACBr para inserir no CST 51 o pRedBC. Por isso você informa e não está indo para o XML.
  2. Falta o percentual da redução de base de cálculo pRedBC.
  3. Parece simples, mas tenho o problema de ser funcionário, e não dono kkkk' A solução que encontrei sana o problema. Devo subir para análise de junção ao SVN?
  4. function TACBrTEFDClass.CNC(Rede, NSU: String; DataHoraTransacao: TDateTime; Valor: Double; CodigoAutorizacaoTransacao: String): Boolean; begin IniciarRequisicao('CNC'); Req.ValorTotal := Valor; Req.Rede := Rede; Req.NSU := NSU; Req.DataHoraTransacaoComprovante := DataHoraTransacao; if (CodigoAutorizacaoTransacao <> '') then Req.CodigoAutorizacaoTransacao := CodigoAutorizacaoTransacao; AdicionarIdentificacao; FinalizarRequisicao; LerRespostaRequisicao; Result := Resp.TransacaoAprovada and (Resp.QtdLinhasComprovante > 0); // solução try ProcessarResposta ; { Faz a Impressão e / ou exibe Mensagem ao Operador } finally FinalizarResposta( True ) ; { True = Apaga Arquivo de Resposta } end; end;
  5. Pude identificar o problema. ProcessarResposta limpa Frm_Main.ACBrTEFD.RespostasPendentes após impressão do cancelamento, não permitindo mais capturar a Objects[0].Sucesso. FinalizarResposta limpa Frm_Main.ACBrTEFD.Resp, voltando valores padrões onde ao acessar Resp.Sucesso já está como False. Poderia solucionar o processo retirando de FinalizarResposta o Resp.Clear, mas como há a chamada em outras partes do código, não sei qual a consequência dessa ação. function TACBrTEFDClass.CNC(Rede, NSU: String; DataHoraTransacao: TDateTime; Valor: Double; CodigoAutorizacaoTransacao: String): Boolean; begin IniciarRequisicao('CNC'); Req.ValorTotal := Valor; Req.Rede := Rede; Req.NSU := NSU; Req.DataHoraTransacaoComprovante := DataHoraTransacao; if (CodigoAutorizacaoTransacao <> '') then Req.CodigoAutorizacaoTransacao := CodigoAutorizacaoTransacao; AdicionarIdentificacao; FinalizarRequisicao; LerRespostaRequisicao; Result := Resp.TransacaoAprovada; try ProcessarResposta ; { Faz a Impressão e / ou exibe Mensagem ao Operador } finally FinalizarResposta( True ) ; { True = Apaga Arquivo de Resposta } end; end; procedure TACBrTEFDClass.ProcessarResposta ; var RespostaPendente: TACBrTEFDResp; begin VerificarIniciouRequisicao; GravaLog( Name +' ProcessarResposta: '+Req.Header ); TACBrTEFD(Owner).EstadoResp := respProcessando; if Resp.QtdLinhasComprovante > 0 then begin { Cria cópia do Objeto Resp, e salva no ObjectList "RespostasPendentes" } RespostaPendente := CriarResposta(fpTipo); try RespostaPendente.Assign( Resp ); TACBrTEFD(Owner).RespostasPendentes.Add( RespostaPendente ); ImprimirRelatorio ; with TACBrTEFD(Owner) do begin if Assigned( OnDepoisConfirmarTransacoes ) then OnDepoisConfirmarTransacoes( RespostasPendentes ); end ; finally TACBrTEFD(Owner).RespostasPendentes.Clear; // limpa as respostas pendente e não se pode mais acessar a variável Sucesso após sair da Function CNC end; end else if Resp.TextoEspecialOperador <> '' then TACBrTEFD(Owner).DoExibeMsg( opmOK, Resp.TextoEspecialOperador ) end; procedure TACBrTEFDClass.FinalizarResposta( ApagarArqResp : Boolean ); begin TACBrTEFD(Owner).EstadoResp := respConcluida; GravaLog( Name +' FinalizarResposta: '+Req.Header ); if ApagarArqResp then ApagaEVerifica( ArqResp ); Req.Clear; Resp.Clear; // limpa o Resp, retornando a variável Sucesso para False, é possível acessar o Resp.Sucesso após sair da function CNC, mas Resp não tem mais os valores da resposta end;
  6. Perdão, Juliomar. Eu não entendi, poderia explicar, por favor? Edit 1: Estou chamando a function errada?
  7. Utilizo o ACBrTEF. Bem complicado realizar uma troca nesse momento. Vou fazer uma alteração no componente para utilizar internamente apenas. Obrigado.
  8. Tudo bem? Estou utilizando o CNC para cancelar uma transação. function TACBrTEFD.CNC(const Rede, NSU: String; const DataHoraTransacao: TDateTime; const Valor: Double; CodigoAutorizacaoTransacao: String): Boolean; begin Result := fTefClass.CNC( Rede, NSU, DataHoraTransacao, Valor, CodigoAutorizacaoTransacao); end; O problema é que a função retorna True mesmo quando a transação é negada. Não está sendo possível identificar se o cancelamento foi realmente concluído.
  9. feat: adiciona novos campos ao retorno do Banco do Brasil na integração com o ACBrBoleto Alterações em ACBrBoletoRetorno.pas: - Adicionados novos campos privados e propriedades públicas: - PercentualMultaTitulo: Double - ValorJuroMoraTitulo: Currency - Atualizados métodos de cópia de propriedades (`Assign`) para incluir os novos campos Alterações em ACBrBoletoRet_BancoBrasil_API.pas: - Mapeamento dos novos campos do JSON de resposta do Banco do Brasil: - DataLimitePagto ← dataLimiteRecebimentoTitulo - DiasDeProtesto ← quantidadeDiaProtesto - ValorJuroMoraTitulo ← valorJuroMoraTitulo - PercentualMultaTitulo ← percentualMultaTitulo - Inclusão do mapeamento do sacador/avalista: - NomeAvalista ← nomeSacadorAvalistaTitulo - Pessoa ← códigoTipoInscricaoSacador (mapeado para pFisica/pJuridica) - CNPJCPF ← numeroInscricaoSacadorAvalista Essas mudanças permitem capturar e armazenar mais informações relevantes dos títulos retornados pela API do Banco do Brasil. ACBrBoletoRetorno.pas ACBrBoletoRet_BancoBrasil_API.pas
  10. Bem, nada que uma boa noite de sono não ajude. Eu fiz uma nova tentativa, consegui identificar onde o JSON é lido. Encontrei o problema. Não está implementado os campos que quero acessar: valorJuroMoraTitulo percentualMultaTitulo function TRetornoEnvio_BancoBrasil_API.LerRetorno(const ARetornoWS: TACBrBoletoRetornoWS): Boolean; ... ARetornoWS.DadosRet.TituloRet.ValorMoraJuros := LJsonObject.AsFloat['valorJuroMoraRecebido']; ARetornoWS.DadosRet.TituloRet.PercentualMulta := LJsonObject.AsFloat['valorMultaRecebido']; ... O retorno pega os valores: valorJuroMoraRecebido valorMultaRecebido Acredito que para solucionar teriam que ser criados dois novos campos em TACBrBoletoTituloRet = class, para receber também os valores de valorJuroMoraTitulo e percentualMultaTitulo de criação do título.
  11. Claro, eu tentei debuggar para localizar onde é feito tal procedimento. Mas não obtive êxito. Em TRetornoEnvioREST.RetornoEnvio, o RetornoWeb não contem nenhuma informação. Os valores são atribuídos na chamada de LerRetorno. Ao tentar debugar LerRetorno, entra em TListadeBoletos.GetObject, e não vamos para nenhum outro local. Na saída da GetObject o RetornoWeb já teve seus valores atribuídos, sendo que alguns deles, como no meu exemplo, estão com valores diferentes do JSON.
  12. Sim, apenas um boleto. Está sendo feita a consulta pelo Nosso Número, a Operação deve ser do tipo tpConsultaDetalhe. Só teria a possibilidade de retornar 2 ou mais boletos na Operação tpConsulta. Para tpConsulta deve se usar ListaConsultaRetornoWeb e para tpConsultaDetalhe deve se usar ListaRetornoWeb. O JSON de retorno, que consta ACBrBoleto1.ListaRetornoWeb[0].JSON está correto. O problema são os valores do retorno no ACBrBoleto1.ListaRetornoWeb[0].DadosRet.TituloRet, que não batem com os valores do JSON. Edit1: Eu consegui contornar o problema fazendo a leitura do JSON de retorno. Mas, acredito que possa ter alguma falha pois o componente não está sendo carregado com os valores do JSON. ACBrBoleto1.Configuracoes.WebService.Operacao := tpConsultaDetalhe; ACBrBoleto1.Enviar; JSONRetorno := TJSONObject.ParseJSONValue(TEncoding.ASCII.GetBytes(ACBrBoleto1.ListaRetornoWeb[0].JSON), 0) as TJSONObject; vBoleto.ValorMoraJuros := StrToFloat(JSONRetorno.Get('valorJuroMoraTitulo').JsonValue.Value); ACBrBoleto1.ListaRetornoWeb[0].DadosRet.TituloRet.ValorMoraJuros; vBoleto.PercentualMulta := StrToFloat(JSONRetorno.Get('percentualMultaTitulo').JsonValue.Value); //ACBrBoleto1.ListaRetornoWeb[0].DadosRet.TituloRet.PercentualMulta; vBoleto.DiasDeProtesto := StrToInt(JSONRetorno.Get('quantidadeDiaProtesto').JsonValue.Value); // ACBrBoleto1.ListaRetornoWeb[0].DadosRet.TituloRet.DiasDeProtesto; if vBoleto.DiasDeProtesto > 0 then begin vBoleto.CodigoNegativacao := cnProtestarCorrido; vBoleto.TipoDiasProtesto := diCorridos; vBoleto.DataProtesto := StrToDate(StringReplace(JSONRetorno.Get('dataProtestoTituloCobranca').JsonValue.Value, '.', '/', [rfReplaceAll])); // ACBrBoleto1.ListaRetornoWeb[0].DadosRet.TituloRet.DataProtesto; end; vBoleto.DataLimitePagto := StrToDate(StringReplace(JSONRetorno.Get('dataLimiteRecebimentoTitulo').JsonValue.Value, '.', '/', [rfReplaceAll])); // ACBrBoleto1.ListaRetornoWeb[0].DadosRet.TituloRet.DataLimitePagto; vBoleto.Sacado.SacadoAvalista.NomeAvalista := JSONRetorno.Get('nomeSacadorAvalistaTitulo').JsonValue.Value; // ACBrBoleto1.ListaRetornoWeb[0].DadosRet.TituloRet.SacadoAvalista.NomeAvalista; vBoleto.Sacado.SacadoAvalista.Pessoa := pJuridica; vBoleto.Sacado.SacadoAvalista.CNPJCPF := FormataCNPJ(AdicionaCharEsquerda(JSONRetorno.Get('numeroInscricaoSacadorAvalista').JsonValue.Value, 14, '0')); // FormataCNPJ(AdicionaCharEsquerda(ACBrBoleto1.ListaRetornoWeb[0].DadosRet.TituloRet.SacadoAvalista.CNPJCPF, 14, '0'));
  13. Estou consultando uma cobrança via API do BB. No retorno consta JSON com valores corretos. Mas ao acessa os campos do componente os valores estão zerados.
  14. Olá. Fizemos uma integração com o TEF Elgin. Segue arquivos em anexo. Se não for a área correta, por favor, mover. Obrigado. ACBrTEFDElgin.pas ACBrTEFD.pas ACBrTEFDClass.pas
  15. @Rodrigo Hlatki, segue solução encontrada. Fiz um arquivo padrão em Word, que servirá de modelo, no meu caso, em todas ocasiões. (está anexado) Na aplicação, defino um TRichEdit em tempo de execução. É necessário definir as margens, para simular um A4. Você consegue isso com a procedure abaixo: procedure SetRichEditMargins(const mLeft, mRight, mTop, mBottom: Extended; const re : TRichEdit); var ppiX, ppiY : integer; spaceLeft, spaceTop : integer; r : TRect; begin // pixels por polegadas ppiX := GetDeviceCaps(Printer.Handle, LOGPIXELSX); ppiY := GetDeviceCaps(Printer.Handle, LOGPIXELSY); // não imprimir margens spaceLeft := GetDeviceCaps(Printer.Handle, PHYSICALOFFSETX); spaceTop := GetDeviceCaps(Printer.Handle, PHYSICALOFFSETY); //calcular as margens R.Left := Round(ppiX * mLeft) - spaceLeft; R.Right := Printer.PageWidth - Round(ppiX * mRight) - spaceLeft; R.Top := Round(ppiY * mTop) - spaceTop; R.Bottom := Printer.PageHeight - Round(ppiY * mBottom) - spaceTop; // setar as margens re.PageRect := r; end; Executando procedure: MEsq := (PageSetupDialog1.MarginLeft/2.54)/1000; MDir := (PageSetupDialog1.MarginRight/2.54)/1000; MSup := (PageSetupDialog1.MarginTop/2.54)/1000; MInf := (PageSetupDialog1.MarginBottom/2.54)/1000; SetRichEditMargins(MEsq, MDir, MSup, MInf, richEdit1); Nesse arquivo existem 'variáveis' que vão ser substituídas. Por exemplo: NmCliente, CGCCPFCliente, etc. Para isso, utilizo a procedure: procedure TFrm_TermoConsentimentoCliente.FindReplace(const Enc, Subs: String; var Texto: TRichEdit); var I, Posicao : Integer; Linha : String; begin For I:= 0 To Texto.Lines.Count - 1 Do Begin Linha := Texto.Lines[I]; Repeat Posicao := Pos(Enc, Linha); If Posicao > 0 Then Begin Delete(Linha, Posicao, Length(Enc)); Insert(Subs, Linha, Posicao); Texto.Lines[I] := Linha; End; Until Posicao = 0; End; end; Executando procedure: FindReplace('NmCliente', Nm_ClienteFornecedor, RichEdit1); I := Pos(Nm_ClienteFornecedor, RichEdit1.Text) - 3; RichEdit1.SelStart := I; RichEdit1.SelLength := Length(Nm_ClienteFornecedor); RichEdit1.SelAttributes.Style := [fsBold]; // Identifica posição do Nome para ser colocado em negrito If Fg_PessoaClienteFornecedor = 'F' Then Begin FindReplace('CGCCPFCliente', FormataCPF(FormatFloat('00000000000', Nr_CGCCPF)), RichEdit1); I := Pos(FormataCPF(FormatFloat('00000000000', Nr_CGCCPF)), RichEdit1.Text) - 3; RichEdit1.SelStart := I; RichEdit1.SelLength := Length(FormataCPF(FormatFloat('00000000000', Nr_CGCCPF))); RichEdit1.SelAttributes.Style := [fsBold]; End Else Begin FindReplace('CGCCPFCliente', FormataCNPJ(FormatFloat('00000000000000', Nr_CGCCPF)), RichEdit1); I := Pos(FormataCNPJ(FormatFloat('00000000000000', Nr_CGCCPF)), RichEdit1.Text) - 3; RichEdit1.SelStart := I; RichEdit1.SelLength := Length(FormataCNPJ(FormatFloat('00000000000000', Nr_CGCCPF))); RichEdit1.SelAttributes.Style := [fsBold]; End; Finalmente, peço ao usuário para selecionar uma impressora para imprimir o conteúdo do RichEdit, ou então salvar em PDF. If Printer.Printers.IndexOf('Microsoft Print to PDF') <> -1 Then Printer.PrinterIndex := Printer.Printers.IndexOf('Microsoft Print to PDF') // para salvar em PDF Else If Printer.Printers.IndexOf('Microsoft XPS Document Writer') <> -1 Then Printer.PrinterIndex := Printer.Printers.IndexOf('Microsoft XPS Document Writer') // para salvar em XPS Else Begin Application.MessageBox('O computador não possui os recursos Microsoft Print to PDF e Microsoft XPS Document Writers! Ative-os para poder salvar o termo em PDF ou XPS!', 'Atenção!', MB_ICONWARNING + MB_OK); If Not PrintDialog1.Execute() Then Abort; End; Printer.Orientation := poPortrait; // vertical Agora é só chamar: RichEdit1.Print(Text); Termo.rtf
  16. Não achei uma solução. Eu utilizei outra forma, manipulando um arquivo em tempo de execução. Amanhã mando aqui o que fiz.
  17. Olá! Recentemente vi um usuário no Discord que precisava passar na caixa de diálogo, que é aberta quando se clica em 'Salvar' no relatório do Fortes ao fazer a impressão de boletos, um filtro e um nome de arquivo padrão. No meu sistema tive que fazer uma adaptação no código fonte da ACBr para que pudesse alcançar tal objetivo. Acredito que possa ser útil para futuros usuários. Segue arquivo em anexo com alteração. Obs.: foi fixado para que o padrão do filtro seja 'Filtro PDF' e o 'Nome do arquivo' receberá a variável 'NomeArquivo'. ACBrBoletoFCFortesFr.pas
  18. Faça o seguinte procedimento: No componente TACBrNFe na propriedade DANFE vincule o componente TACBrNFeDANFeRL. No componente TACBrNFeDANFeRL confirma se a propriedade NumCopias está setado como 1. Edit: talvez você não vinculou TACBrNFeDANFeRL a TACBrNFe ou então está trocando a propriedade NumCopias dinamicamente no código. Aqui está funcionando normalmente.
  19. Tente o seguinte código: Declare a função: function IEValida(vsIE, vsUF : String) : Boolean; function IEValida(vsIE, vsUF : String) : Boolean; var vValidador : TACBrValidador; begin Try vValidador := TACBrValidador.Create(Nil); vValidador.TipoDocto := docInscEst; vValidador.Documento := vsIE; vValidador.Complemento := vsUF; Result := vValidador.Validar; Finally vValidador.Free; End; end; Faça a chamada: If ('0800888300299', 'DF') Then ShowMessage('VÁLIDA') Else ShowMessage('INVÁLIDA'); Fiz o teste e retorna True, você provavelmente está passando algum parâmetro errado na validação que está fazendo atualmente. Edit: se em sua aplicação o usuário pode informar a IE com formatação, utilize a função abaixo para retirar caracteres indesejáveis. Declare a função: function RetornaNumeroAlfa(vsString : String) : String; function RetornaNumeroAlfa(vsString : String) : String; var I : Integer; vsRetorno : String; vsLetra : String; begin vsRetorno := ''; For I := 1 To Length(vsString) Do Begin vsLetra := UpperCase(vsString[I]); If (vsString[I] In ['A'..'Z']) Or (vsString[I] In ['0'..'9']) Then vsRetorno := vsRetorno + vsString[I]; End; Result := vsRetorno; end;
  20. Olá, @Dfox. Tudo bem? Existem duas tags para GTIN: cEAN (GTIN do produto, antigo código EAN ou código de barras); cEANTrib (GTIN da unidade tributável, antigo código EAN ou código de barras). O primeiro diz respeito ao identificador do pacote e o segundo ao identificador dos itens dentro do pacote. Por exemplo: Está sendo feita a venda de um palete com 12 caixas contendo 10 latas: cEAN vale para a caixa; cEANTrib vale para a lata. No caso onde o produto da nota for igual a sua unidade tributável (cEANTrib) o código enviado nas duas tags será idêntico. Vamos as observações da SEFAZ: cEAN: Preencher com o código GTIN-8, GTIN-12, GTIN-13 ou GTIN-14 (antigos códigos EAN, UPC e DUN-14); Para produtos que não possuem código de barras com GTIN, deve ser informado o literal “SEM GTIN”. cEANTrib: Preencher com o código GTIN-8, GTIN-12, GTIN-13 ou GTIN-14 (antigos códigos EAN, UPC e DUN-14) da unidade tributável do produto; O GTIN da unidade tributável deve corresponder àquele da menor unidade comercializável identificada por código GTIN; Para produtos que não possuem código de barras com GTIN, deve ser informado o literal "SEM GTIN”. Em meu sistema caso o produto possua um GTIN válido eu preencho as tags com esse código, caso não eu informo "SEM GTIN". Para tal eu utilizo o ACBrValidador que possui a função GtinValido, ele valida tanto o GTIN como o seu prefixo. Para uma análise mais crítica você também pode consultar na SEFAZ os documentos: NT2021.003_v1_10; NT2022.001_v1.00.
  21. Faça o seguinte: D.REFERENCIA <= ( select dateadd( -1 day to dateadd( 1 month to cast(extract(year from current_date) || '-' || extract(month from current_date) || '-01' as date) ) ) from RDB$DATABASE ) Vai solucionar pois: select dateadd( -1 day to dateadd( 1 month to cast(extract(year from current_date) || '-' || extract(month from current_date) || '-01' as date) ) ) from RDB$DATABASE sempre vai retornar o último dia do mês vigente no formato date. Foi testado na versão 2.5 do FireBird.
  22. Corrigido, só não consegui editar no post. Obrigado.
  23. Olá! Recentemente houve a necessidade por parte de meus clientes de uma forma mais detalhada de auditoria de inserções, atualizações e deleções de informações. Depois de muita dedicação consegui uma estrutura de fácil manuseio para aqueles que utilizam o FireBird da versão 2.5 em diante. A auditoria é feita em uma database externa, dessa forma o banco de dados continua independente e não sofre uma expansão referente a esses dados. Fato importante é que o banco da empresa não é afetado com possíveis exceções provenientes do banco de auditoria, ou seja, caso algo dê errado externamente o fluxo de dados na empresa não é afetado. Segue procedimento: 1) Criar banco de dados para auditoria (LOGGERAL.FDB). 2) Criação das tabelas de auditoria, etc: CREATE GENERATOR GEN_LOGOPERACAO_ID; CREATE TABLE LOGOPERACAO ( CD_LOGOPERACAO BIGINT NOT NULL, DS_TABELA VARCHAR(31) NOT NULL, FG_OPERACAO CHAR(1) NOT NULL, CD_USUARIO VARCHAR(10) NOT NULL, DT_OPERACAO TIMESTAMP NOT NULL, DS_CHAVE VARCHAR(255) NOT NULL ); ALTER TABLE LOGOPERACAO ADD CONSTRAINT PK_LOGOPERACAO PRIMARY KEY (CD_LOGOPERACAO); SET TERM ^ ; /* Trigger: LOGOPERACAO_BI */ CREATE OR ALTER TRIGGER LOGOPERACAO_BI FOR LOGOPERACAO ACTIVE BEFORE INSERT POSITION 0 as begin if (new.cd_logoperacao is null) then new.cd_logoperacao = gen_id(gen_logoperacao_id,1); end ^ SET TERM ; ^ CREATE GENERATOR GEN_LOGDATA_ID; CREATE TABLE LOGDATA ( CD_LOGDATA BIGINT NOT NULL, CD_LOGOPERACAO BIGINT NOT NULL, DS_CAMPO VARCHAR(31) NOT NULL, DS_OLD VARCHAR(4000), DS_NEW VARCHAR(4000) ); ALTER TABLE LOGDATA ADD CONSTRAINT PK_LOGDATA PRIMARY KEY (CD_LOGDATA); ALTER TABLE LOGDATA ADD CONSTRAINT FK_LOGDATA_LOGOPERACAO FOREIGN KEY (CD_LOGOPERACAO) REFERENCES LOGOPERACAO (CD_LOGOPERACAO); SET TERM ^ ; /* Trigger: LOGDATA_BI */ CREATE OR ALTER TRIGGER LOGDATA_BI FOR LOGDATA ACTIVE BEFORE INSERT POSITION 0 as begin if (new.cd_logdata is null) then new.cd_logdata = gen_id(gen_logdata_id,1); end ^ SET TERM ; ^ Explicações rápidas: 1) A tabela LOGOPERACAO registra: qual tabela (DS_TABELA) passou por uma inserção (FG_OPERACAO = 'I'), atualização (FG_OPERACAO = 'U') ou deleção (FG_OPERACAO = 'D'), qual foi o usuário responsável (CD_USUARIO), quando ocorreu a operação (DT_OPERACAO), e qual foi a PK (DS_CHAVE) afetada. 2) Vocês vão perceber que nesse processo não importa quantas chaves primárias sua tabela tem, pois o código foi pensado para concatenar as chaves separadas por pipe ('|'). 3) A tabela LOGDATA armazena informações mais detalhadas sobre a LOGOPERACAO. Nesse caso seus registros contemplam qual foi o campo afetado na tabela (DS_CAMPO) apresentando seu valor antigo (DS_OLD) e o novo (DS_NEW). 3) Criação das procedures para auditoria no banco da empresa: SET TERM ^ ; create or alter procedure PROC_LOG ( DS_TABELA varchar(31) not null) as declare variable "TYPE" smallint; declare variable DS_CAMPO varchar(31); declare variable SQL varchar(32765); declare variable AUXOPERACAO varchar(255); declare variable DS_CHAVEAUX varchar(31); declare variable DS_CHAVENEW varchar(255); declare variable DLL blob sub_type 1 segment size 256; declare variable DS_CHAVEOLD varchar(255); begin if (user = 'BACKUP') then exit; sql = ''; ds_chavenew = ''; ds_chaveold = ''; auxoperacao = ''; dll = ''; for select upper(trim(F.RDB$FIELD_NAME)), FS.RDB$FIELD_TYPE from RDB$RELATION_FIELDS F left join RDB$FIELDS FS on FS.RDB$FIELD_NAME = F.RDB$FIELD_SOURCE where F.RDB$RELATION_NAME = :ds_tabela order by F.RDB$FIELD_POSITION into :ds_campo, :type do begin if ((:type <> 261) and (:ds_campo not in ('CD_USRINCALT', 'DT_INCALT'))) then /* IGNORAR 261 = BLOB */ begin sql = sql || ' if (new.' || :ds_campo || ' is distinct from old.' || :ds_campo || ') then' || ascii_char(13) || ' execute procedure proc_logdata(' || ascii_char(13) || ' :cd_logoperacao, ' || ascii_char(13) || ' ''' || :ds_campo || ''',' || ascii_char(13) || ' old.' || :ds_campo || ',' || ascii_char(13) || ' new.' || :ds_campo || ');' || ascii_char(13); end end auxoperacao = ' if (inserting) then fg_operacao = ''I'';' || ascii_char(13) || ' else if (updating) then fg_operacao = ''U'';' || ascii_char(13) || ' else if (deleting) then fg_operacao = ''D'';' || ascii_char(13); for select trim(I.RDB$FIELD_NAME) from RDB$RELATION_CONSTRAINTS RC join RDB$INDEX_SEGMENTS I on (I.RDB$INDEX_NAME = RC.RDB$INDEX_NAME) join RDB$INDICES IDX on (IDX.RDB$INDEX_NAME = RC.RDB$INDEX_NAME) where (RC.RDB$CONSTRAINT_TYPE = 'PRIMARY KEY') and (RC.RDB$RELATION_NAME = :ds_tabela) order by I.RDB$FIELD_POSITION into :ds_chaveaux do begin ds_chavenew = ds_chavenew || 'new.' || :ds_chaveaux || '||''|''||'; ds_chaveold = ds_chaveold || 'old.' || :ds_chaveaux || '||''|''||'; end ds_chavenew = substring(ds_chavenew from 1 for char_length(ds_chavenew) - 7); ds_chaveold = substring(ds_chaveold from 1 for char_length(ds_chaveold) - 7); dll = 'CREATE OR ALTER TRIGGER LOG_' || :ds_tabela || ' for ' || :ds_tabela || ascii_char(13) || 'ACTIVE AFTER INSERT OR UPDATE OR DELETE POSITION 32767' || ascii_char(13) || 'as' || ascii_char(13) || ' declare variable fg_operacao char(1) = null;' || ascii_char(13) || ' declare variable cd_logoperacao bigint;' || ascii_char(13) || 'begin' || ascii_char(13) || auxoperacao || ascii_char(13) || ' if (:fg_operacao is null) then exit;' || ascii_char(13) || ascii_char(13) || ' if (:fg_operacao = ''I'') then' || ascii_char(13) || ' execute procedure proc_logoperacao(' || ascii_char(13) || ' ''' || :ds_tabela || ''',' || ascii_char(13) || ' ''I'',' || ascii_char(13) || ' ' || :ds_chavenew || ')' || ascii_char(13) || ' returning_values :cd_logoperacao;' || ascii_char(13) || ' else' || ascii_char(13) || ' execute procedure proc_logoperacao(' || ascii_char(13) || ' ''' || :ds_tabela || ''',' || ascii_char(13) || ' :fg_operacao,' || ascii_char(13) || ' ' || :ds_chaveold || ')' || ascii_char(13) || ' returning_values :cd_logoperacao;' || ascii_char(13) || ascii_char(13) || sql || ascii_char(13) || ' when any do' || ascii_char(13) || ' begin' || ascii_char(13) || ascii_char(13) || ' end' || ascii_char(13) || 'end'; if (sql <> '') then execute statement :dll; end^ SET TERM ; ^ SET TERM ^ ; create or alter procedure PROC_LOGOPERACAO ( DS_TABELA varchar(31) not null, FG_OPERACAO char(1) not null, DS_CHAVE varchar(255) not null) returns ( CD_LOGOPERACAO bigint) as declare variable SQL varchar(16384); begin sql = 'insert into logoperacao(' || 'ds_tabela, ' || 'fg_operacao, ' || 'cd_usuario, ' || 'dt_operacao, ' || 'ds_chave) ' || 'values(' || ':ds_tabela, ' || ':fg_operacao, ' || ':cd_usuario, ' || ':dt_operacao, ' || ':ds_chave) ' || 'returning ' || 'cd_logoperacao'; execute statement (:sql) ( ds_tabela := :ds_tabela, fg_operacao := :fg_operacao, cd_usuario := user, dt_operacao := current_timestamp, ds_chave := :ds_chave) on external 'c:\sysfire\loggeral.fdb' as user 'SYSDBA' password 'sys@#$' with common transaction into :cd_logoperacao; end^ SET TERM ; ^ SET TERM ^ ; create or alter procedure PROC_LOGDATA ( CD_LOGOPERACAO bigint not null, DS_CAMPO varchar(31) not null, DS_OLD varchar(4000), DS_NEW varchar(4000)) as declare variable SQL varchar(16384); begin sql = 'insert into logdata(' || 'cd_logoperacao, ' || 'ds_campo, ' || 'ds_old, ' || 'ds_new) ' || 'values(' || ':cd_logoperacao, ' || ':ds_campo, ' || ':ds_old, ' || ':ds_new)'; execute statement (:sql) ( cd_logoperacao := :cd_logoperacao, ds_campo := :ds_campo, ds_old := :ds_old, ds_new := :ds_new) on external 'c:\sysfire\loggeral.fdb' as user 'SYSDBA' password 'sys@#$' with common transaction; end^ SET TERM ; ^ Explicações rápidas: 1) A PROC_LOGOPERACAO insere os registros no banco externo na tabela LOGOPERACAO. 2) A PROC_LOGDATA insere os registros no banco externo na tabela LOGDATA. 3) A PROC_LOG gera automaticamente uma trigger para after insert, update e delete (na última posição possível 32767) em uma tabela que seja necessário a auditoria. Aqui não importa quais são os campos da tabela, a geração é autônoma e independe do tipo, tamanho, etc. 3.1) Caso a tabela possua campos tipo BLOB (261) eles serão ignorados. 3.2) Se for necessário auditar campos VARCHAR com tamanho superior a 4000 será necessário rever a criação da tabela LOGDATA e da procedure PROC_LOGDATA pois na forma atual esse é o limite (o maior campo da minha estrutura de dados possui 4000 posições, esse é o motivo da minha escolha). 3.3) Os campos CD_USRINCALT e DT_INCALT estão sendo ignorados pois fazem parte das minhas tabelas e não precisam ser auditados, retirar ou deixá-los no código não afetará em nada no funcionamento. Caso existam campos que você quer ignorar, faça a inserção manual no código, a mesma coisa para tipos de campos, atualmente apenas BLOB é ignorado (você pode pesquisar no fonte para ver os códigos referentes a outros tipos). 4) Na PROC_LOGOPERACAO e PROC_LOGDATA verificar o caminho do banco de auditoria, como teste está fixo c:\sysfire\loggera.fdb. Verificar também a senha do user master SYSDBA, pois a conexão com o banco externo será feita por ele. Após realizar os passos acima só será necessário escolher as tabelas que precisam ser auditadas e chamar a PROC_LOG para geração das triggers (o nome da tabela deve ser exato, com letras maiúsculas e minúsculas, caso seja informada uma tabela que não existe em sua database uma exceção é lançada). execute procedure PROC_LOG('nome_da_tabela'); A partir daqui qualquer tipo de insert, update e delete nas tabelas executas com a PROC_LOG vão gerar registros no banco de auditoria. Bom proveito, obrigado! Edit1: caso uma tabela que já está sendo auditada sofra alterações de campos (novos campos ou drop de campos antigos) é só fazer a chamadada mesma com a PROC_LOG que as triggers serão atualizadas automaticamente. Processo fácil e rápido para manutenção.
  24. Que o layout do boleto caracterizava cobrança e não proposta.
×
×
  • 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.