Ir para conteúdo
  • Cadastre-se

Roberto Henrique Borges Machado

Membros
  • Total de ítens

    21
  • Registro em

  • Última visita

Últimos Visitantes

293 visualizações

Roberto Henrique Borges Machado's Achievements

Apprentice

Apprentice (3/14)

  • Dedicated Rare
  • Reacting Well Rare
  • Collaborator Rare
  • First Post
  • Conversation Starter

Recent Badges

7

Reputação

  1. 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
  2. @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
  3. Não achei uma solução. Eu utilizei outra forma, manipulando um arquivo em tempo de execução. Amanhã mando aqui o que fiz.
  4. 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
  5. 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.
  6. 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;
  7. 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.
  8. 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.
  9. 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.
  10. Que o layout do boleto caracterizava cobrança e não proposta.
  11. Na situação que presenciei com meu cliente a emissão do boleto no layout de Cobrança, por exemplo, constando os dizeres nas informações adicionais, não foi aceito para pagamento. Após verificar que existia esse modelo de layout "padrão" para Proposta fiz a implementação, foi enviado e aceitaram para pagamento.
  12. Em anexo. Art. 2º II; Art. 4º §5º, Art. 5º §4º III. Não existe manual para leiaute, design ou confecção. Introduz alterações e consolida as regras para a li-quidação financeira dos resultados apurados na Cen-tralizadora da Compensa.pdf
  13. Olá! Devido a uma necessidade especial de um cliente, que começou a vender para a rede Carrefour, precisei implementar um layout para Boleto de Proposta, já que a rede em questão EXIGE essa modalidade de boleto. Segue em anexo. Obrigado. ACBrBoletoConversao.pas ACBrBoletoFCFortesFr.pas ACBrBoletoFCFortesFr.dfm
×
×
  • 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.