-
Total de ítens
21 -
Registro em
-
Última visita
Tipo de Conteúdo
Blocks
Notes ACBrLibNFe
Fóruns
Downloads
Calendário
Tudo que Roberto Henrique Borges Machado postou
-
Implementação TEF Elgin no ACBr
um tópico no fórum postou Roberto Henrique Borges Machado Dúvidas sobre TEF
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 -
@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
-
Caixa de diálogo - 'Usar Filtro' e 'Nome do arquivo' padrões
um tópico no fórum postou Roberto Henrique Borges Machado ACBrBoleto
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 -
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.
-
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;
-
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.
- 1 reply
-
- 1
-
-
fixar o ultimo dia mês atual Firebird
Roberto Henrique Borges Machado replied to consultoria Soft's tópico in Banco de Dados
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. -
Auditoria de Banco de Dados [FireBird 2.5+]
um tópico no fórum postou Roberto Henrique Borges Machado Banco de Dados
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. -
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.
-
Boleto de Proposta - Fortes Report
um tópico no fórum postou Roberto Henrique Borges Machado ACBrBoleto
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 -
O fonte mais recente referente ao ACBrBancoSantander.pas ainda está com o problema relatado inicialmente, na function TACBrBancoSantander.GerarRegistroTransacao240, na parte que trata do REGISTRO DETALHE - SEGMENTO R - REMESSA. O que o banco espera: posição inicial/final -> 042-042 Código do desconto 3, 043-050 Data do desconto 3, 051-065 Valor/Percentual a ser concedido. O que o fonte faz: posição inicial/final -> 042-065 Reservado (uso Banco), preenchendo com 24 espaços em branco. O banco rejeita pois deve ser preenchido com zeros! Verificar linha 705 do fonte.
-
ACBrBancoSantander - Atualização necessária!
um tópico no fórum postou Roberto Henrique Borges Machado ACBrBoleto
Estou fazendo uma homologação de boleto para um cliente, referente ao banco Santander. O fonte da ACBr precisa de uma atualização no Registro Detalhe - Segmento R Remessa. No fonte atual a posição inicial/final 042 - 065 está sendo preenchida com espaços em branco como uso exclusivo do banco. No manual que o gerente me enviou essa posição trata de um DESCONTO 3, que caso não exista deve ser preenchido com zeros! Segue manual mais atualizado com o layout do Santander e sugestão para alteração do fonte! H7815 Layout Cobrança CNAB 240 posições padrão Santander Multibanco Agosto 2020 V.3.4.pdf ACBrBancoSantander.pas -
TRLRichText do Fortes Report
um tópico no fórum postou Roberto Henrique Borges Machado Dúvidas Gerais sobre o ACBr
Olá! Recentemente com a notícia de que a LGPD entrará em vigor houve a necessidade de elaboração de um termo para que os clientes da minha empresa possam assinar juntamente comigo para que ambos fiquem resguardados. Tal arquivo foi escrito no Word e salvo em .rtf. Em um TRichEdit consigo dar um LoadFromFile e carregar o arquivo no componente, segue abaixo: RichEdit1.Lines.LoadFromFile('C:\Temp\TERMO.rtf'); A questão é que eu gostaria de usar o Fortes Report para tal situação. Dessa forma, tentei fazer o mesmo procedimento só que usando um TRLRichText, mas sem sucesso. Ao tentar carregar o arquivo no componente, segue abaixo: RLRichText1.Lines.LoadFromFile('C:\Temp\TERMO.rtf'); O seguinte erro provêm: Project SysFire.exe raised exception class EListError with message 'List index out of bounds (3)'. Alguém já teve a necessidade de utilizar o componente TRLRichText para receber um arquivo .rtf e fazer a impressão pelo Fortes Report? Ficaria grato se alguém já conseguiu e pudesse compartilhar, já dei uma boa pesquisada e aprofundada mas sem nenhum resultado. Muito obrigado!