Página 1 de 2

Gerador de relatorios?

Enviado: 26 Nov 2016 20:29
por JoséQuintas
O ajuste automático pra imprimir o quadro de produtos no Danfe tá ficando interessante.
Pode servir até como parte de um gerador de relatórios, não sei ao certo.

Só sei que estou achando o trem muito doido... rs

Mostrar parcial.
Criei um array, os elementos vão ser estes:

Código: Selecionar todos

#define LAYOUT_TITULO          1
#define LAYOUT_LARGURA         2
#define LAYOUT_COLUNAPDF       3
#define LAYOUT_LARGURAPDF      4
#define LAYOUT_DECIMAIS        5
#define LAYOUT_IMPRIME         6
#define LAYOUT_TITULO1         7
#define LAYOUT_TITULO2         8
#define LAYOUT_CONTEUDO        9
Exemplo, parcial da Danfe:

Código: Selecionar todos

   ::aLayout[ LAYOUT_CODIGO,    LAYOUT_TITULO1 ]   := "CÓDIGO"
   ::aLayout[ LAYOUT_CODIGO,    LAYOUT_CONTEUDO ]  := { || ::aItem[ "cProd" ] }
   ::aLayout[ LAYOUT_DESCRICAO, LAYOUT_TITULO1 ]   := "DESCRIÇÃO DO PRODUTO / SERVIÇO"
   ::aLayout[ LAYOUT_DESCRICAO, LAYOUT_CONTEUDO ]  := { || MemoLine( ::aItem[ "xProd" ], ::aLayout[ LAYOUT_DESCRICAO, LAYOUT_LARGURA ], 1 ) }
   ::aLayout[ LAYOUT_NCM,       LAYOUT_TITULO1 ]   := "NCM/SH"
   ::aLayout[ LAYOUT_NCM,       LAYOUT_CONTEUDO ]  := { || ::aItem[ "NCM" ] }
Nada do outro mundo, é como um tBrowse()

Os cálculos começam....

Código: Selecionar todos

   nItem := 1
   DO WHILE .T.
      IF ! ::ProcessaItens( ::cXml, nItem )
         EXIT
      ENDIF
      nItem += 1
      ::aLayout[ LAYOUT_QTD,      LAYOUT_DECIMAIS ] := ::DefineDecimais( ::aItem[ "qCom" ],   ::aLayout[ LAYOUT_QTD,    LAYOUT_DECIMAIS ] )
      ::aLayout[ LAYOUT_UNITARIO, LAYOUT_DECIMAIS ] := ::DefineDecimais( ::aItem[ "vUnCom" ], ::aLayout[ LAYOUT_UNITARIO, LAYOUT_DECIMAIS ] )
      FOR EACH oElement IN ::aLayout
         oElement[ LAYOUT_LARGURA ] := Max( oElement[ LAYOUT_LARGURA ], Len( Eval( oElement[ LAYOUT_CONTEUDO ] ) ) )
         oElement[ LAYOUT_LARGURAPDF ] := Max( oElement[ LAYOUT_LARGURAPDF ], ::LarguraTexto( Eval( oElement[ LAYOUT_CONTEUDO ] ) ) )
      NEXT
      IF Val( ::aItemIPI[ "pIPI" ]  ) > 0 .OR. Val( ::aItemIPI[ "vIPI" ] ) > 0 // Se houver IPI no XML, habilita coluna
         ::aLayout[ LAYOUT_IPIVAL, LAYOUT_IMPRIME ] := .T.
         ::aLayout[ LAYOUT_IPIALI, LAYOUT_IMPRIME ] := .T.
      ENDIF
   ENDDO
Testo cada coluna sobre cada item, pra decidir quantidade de decimais, largura em caracteres e "largura em pdf"
largura em PDF é a largura que será necessária no PDF. A letra não tem tamanho fixo, então não dá pra fixar um fator de conversão, melhor perguntar pra HARUPDF.

As colunas tem titulo, dois títulos, tem que caber o titulo e o conteúdo, então melhor pegar o maior dos três.

Código: Selecionar todos

   FOR EACH oElement IN ::aLayout
      oElement[ LAYOUT_LARGURA ] += 1
      oElement[ LAYOUT_LARGURAPDF ] := Max( oElement[ LAYOUT_LARGURAPDF ], ::LarguraTexto( oElement[ LAYOUT_TITULO1 ] ) ) 
      oElement[ LAYOUT_LARGURAPDF ] := Max( oElement[ LAYOUT_LARGURAPDF ], ::LarguraTexto( oELement[ LAYOUT_TITULO2 ] ) )
      oElement[ LAYOUT_LARGURAPDF ] += 4
   NEXT
Tem coluna que pode ou não ser impressa, então melhor ZERAR o tamanho do que não vai sair

Código: Selecionar todos

   AEval( ::aLayout, { | oElement | oElement[ LAYOUT_LARGURAPDF ] := iif( oElement[ LAYOUT_IMPRIME ], oElement[ LAYOUT_LARGURAPDF ], 0 ) } )
Agora que temos o tamanho de cada coluna, o que sai ou não, já podemos calcular a posição de cada coluna.
A posição é calculada de trás pra frente.

Código: Selecionar todos

   // Calcula posição das colunas
   nColunaFinal := iif( ::lPaisagem, 832, 592 )
   ::aLayout[ LAYOUT_IPIALI,    LAYOUT_COLUNAPDF ]  := nColunaFinal - ::aLayout[ LAYOUT_IPIALI,   LAYOUT_LARGURAPDF ]
   FOR nCont = Len( ::aLayout ) - 1 TO 3 STEP -1
      ::aLayout[ nCont, LAYOUT_COLUNAPDF ] := ::aLayout[ nCont + 1, LAYOUT_COLUNAPDF ] - ::aLayout[ nCont, LAYOUT_LARGURAPDF ]
   NEXT
Lógico, código e descrição são os primeiros, o cálculo é diferente

Código: Selecionar todos

   ::aLayout[ LAYOUT_CODIGO,    LAYOUT_COLUNAPDF ]  := iif( ::lPaisagem, 70, 6 )
   ::aLayout[ LAYOUT_DESCRICAO, LAYOUT_COLUNAPDF ]  := ::aLayout[ LAYOUT_CODIGO, LAYOUT_COLUNAPDF ] + ::aLayout[ LAYOUT_CODIGO, LAYOUT_LARGURAPDF ]
Pronto, o espaço que sobra, é pra descrição dos produtos
Por enquanto apenas usando uma formula pra calcular a largura em caracteres, vou descrever depois a complicação disso.

Código: Selecionar todos

   ::aLayout[ LAYOUT_DESCRICAO, LAYOUT_LARGURAPDF ] := ::aLayout[ LAYOUT_NCM, LAYOUT_COLUNAPDF ] - ::aLayout[ LAYOUT_DESCRICAO, LAYOUT_COLUNAPDF ]
   ::aLayout[ LAYOUT_DESCRICAO, LAYOUT_LARGURA ]    := Int( ::aLayout[ LAYOUT_DESCRICAO, LAYOUT_LARGURAPDF ] / ::nLayoutFonteLargura ) // iif( ::lPaisagem, 45, 37 ) //

Gerador de relatorios?

Enviado: 26 Nov 2016 20:43
por JoséQuintas
A "largura em PDF" pra descrição:

A descrição pode ocupar mais de uma linha, tem também informações adicionais de produtos.
Mas... tem a quantidade total de páginas. No titulo do Danfe sai página 1/2, 2/2, ou 4/4, ou 5/5, etc.
O tamanho do Danfe vai variar conforme o tamanho das descrições, e do texto de informações adicionais: depende de quantas linhas precisar pra isso.

Se usa mais de uma linha, e os caracteres não tem tamanho fixo, surge um novo desafio:

Se eu dividir o texto com largura fixa em caracteres, a largura em PDF vai ser variável, sempre menor que o espaço disponível.
Se eu dividir como largura fixa em PDF... bom... não existe rotina pronta pra isso, a quantidade de caracteres pode ser qualquer uma.

MemoLine() atende a divisão por caracteres, mas não pra dividir conforme o PDF.

Pra dividir diferente durante a impressão, também vou ter que fazer o mesmo ANTES da impressão, pra calcular o total de páginas.

Eu disse... é um trem muito doido.

Já tenho idéia de como fazer.... é que estou alterando uma parte de cada vez. Hoje foi o uso da HARUPDF pra calcular a largura das colunas.

Gerador de relatorios?

Enviado: 26 Nov 2016 20:49
por JoséQuintas
Isso tudo tem a ver com isto.
danfe.png

Nota:
O fonte é parte do PDF do Danfe, então não é necessário repetir tudo aqui, até porque ficaria desatualizado.

Gerador de relatorios?

Enviado: 26 Nov 2016 20:53
por JoséQuintas
A parte de impressão das informações... acho que não compensa reduzir mais que isso.
Graças ao #define inicial, tá bem explicado o que imprime com cada linha de codigo.

Código: Selecionar todos

      ::DrawTextoProduto( LAYOUT_CODIGO,    ::nLinhaPdf, LAYOUT_CONTEUDO, HPDF_TALIGN_CENTER )
      ::DrawTextoProduto( LAYOUT_DESCRICAO, ::nLinhaPdf, LAYOUT_CONTEUDO, HPDF_TALIGN_LEFT )
      ::DrawTextoProduto( LAYOUT_NCM,       ::nLinhaPdf, LAYOUT_CONTEUDO, HPDF_TALIGN_CENTER )
      ::DrawTextoProduto( LAYOUT_CST,       ::nLinhaPdf, LAYOUT_CONTEUDO, HPDF_TALIGN_CENTER )
      ::DrawTextoProduto( LAYOUT_CFOP,      ::nLinhaPdf, LAYOUT_CONTEUDO, HPDF_TALIGN_CENTER )
      ::DrawTextoProduto( LAYOUT_UNIDADE,   ::nLinhaPdf, LAYOUT_CONTEUDO, HPDF_TALIGN_CENTER )
      ::DrawTextoProduto( LAYOUT_QTD,       ::nLinhaPdf, LAYOUT_CONTEUDO, HPDF_TALIGN_RIGHT )
      ::DrawTextoProduto( LAYOUT_UNITARIO,  ::nLinhaPdf, LAYOUT_CONTEUDO, HPDF_TALIGN_RIGHT )
      ::DrawTextoProduto( LAYOUT_TOTAL,     ::nLinhaPdf, LAYOUT_CONTEUDO, HPDF_TALIGN_RIGHT )
      ::DrawTextoProduto( LAYOUT_DESCONTO,  ::nLinhaPdf, LAYOUT_CONTEUDO, HPDF_TALIGN_RIGHT )
      ::DrawTextoProduto( LAYOUT_ICMBAS,    ::nLinhaPdf, LAYOUT_CONTEUDO, HPDF_TALIGN_RIGHT )
      ::DrawTextoProduto( LAYOUT_ICMVAL,    ::nLinhaPdf, LAYOUT_CONTEUDO, HPDF_TALIGN_RIGHT )
      ::DrawTextoProduto( LAYOUT_SUBBAS,    ::nLinhaPdf, LAYOUT_CONTEUDO, HPDF_TALIGN_RIGHT )
      ::DrawTextoProduto( LAYOUT_SUBVAL,    ::nLinhaPdf, LAYOUT_CONTEUDO, HPDF_TALIGN_RIGHT )
      ::DrawTextoProduto( LAYOUT_IPIVAL,    ::nLinhaPdf, LAYOUT_CONTEUDO, HPDF_TALIGN_RIGHT )
      ::DrawTextoProduto( LAYOUT_ICMALI,    ::nLinhaPdf, LAYOUT_CONTEUDO, HPDF_TALIGN_RIGHT )
      ::DrawTextoProduto( LAYOUT_IPIALI,    ::nLinhaPdf, LAYOUT_CONTEUDO, HPDF_TALIGN_RIGHT )
 

Gerador de relatorios?

Enviado: 26 Nov 2016 20:56
por JoséQuintas
A DrawTextoProduto() também não está complicada.

Código: Selecionar todos

METHOD DrawTextoProduto( nCampo, nRow, nConteudo, nAlign ) CLASS hbNFeDanfe

   LOCAL nColunaInicial, nColunaFinal, cTexto

   IF ::aLayout[ nCampo, LAYOUT_IMPRIME ]
      nColunaInicial := ::aLayout[ nCampo, LAYOUT_COLUNAPDF ]
      nColunaFinal   := ::aLayout[ nCampo, LAYOUT_COLUNAPDF ] + ::aLayout[ nCampo, LAYOUT_LARGURAPDF ] - 2
      cTexto         := ::aLayout[ nCampo, nConteudo ]
      IF nConteudo == LAYOUT_CONTEUDO
         cTexto := Eval( cTexto )
      ENDIF
      hbNFe_Texto_hpdf( ::oPdfPage, nColunaInicial, nRow, nColunaFinal, NIL, cTexto, nAlign, ::oPDFFontCabecalho, ::nLayoutFonteAltura )
   ENDIF

   RETURN NIL

Gerador de relatorios?

Enviado: 26 Nov 2016 20:59
por JoséQuintas
Só escolher o título e o conteúdo das colunas.

Código: Selecionar todos

   ::aLayout[ LAYOUT_ICMVAL,    LAYOUT_TITULO1 ]   := "VALOR"
   ::aLayout[ LAYOUT_ICMVAL,    LAYOUT_TITULO2 ]   := "ICMS"
   ::aLayout[ LAYOUT_ICMVAL,    LAYOUT_CONTEUDO ]  := { || AllTrim( FormatNumber( Val( ::aItemICMS[ "vICMS" ] ), 15, 2 ) ) }
   ::aLayout[ LAYOUT_SUBBAS,    LAYOUT_TITULO1 ]   := "B.CÁLC.ICMS"
   ::aLayout[ LAYOUT_SUBBAS,    LAYOUT_TITULO2 ]   := "SUBST.TRIB"
   ::aLayout[ LAYOUT_SUBBAS,    LAYOUT_CONTEUDO ]  := { || AllTrim( FormatNumber( Val( ::aItemICMS[ "vBCST" ] ), 15, 2 ) ) }
   ::aLayout[ LAYOUT_SUBVAL,    LAYOUT_TITULO1 ]   := "VALOR ICMS"
   ::aLayout[ LAYOUT_SUBVAL,    LAYOUT_TITULO2 ]   := "SUBST.TRIB"
   ::aLayout[ LAYOUT_SUBVAL,    LAYOUT_CONTEUDO ]  := { || AllTrim( FormatNumber( Val( ::aItemICMS[ "vICMSST" ] ), 15, 2 ) ) }
Isso não é praticamente um gerador de relatórios?
E com cálculos em modo gráfico, não texto... rs

Gerador de relatorios?

Enviado: 26 Nov 2016 21:06
por JoséQuintas
A mesma nota, agora deixando IPI automático conforme XML, o que causou a remoção das colunas do IPI por não existir no XML.
E também alterando o formato pra Paisagem.
E em modo paisagem, surgem as colunas de substituição tributária... pensando bem, também poderia ser automática... rs
danfe2.png

Gerador de relatorios?

Enviado: 26 Nov 2016 21:17
por JoséQuintas
Agora exagerando nas colunas, até descobri um erro que causei nas alterações anteriores: esqueci da continuação de descrição e informações adicionais... rs
danfe3.png
Abri uma exceção no DrawTextoProduto() permitindo que nConteudo possa trazer um texto a ser impresso, diferente do pré-configurado.

Gerador de relatorios?

Enviado: 26 Nov 2016 21:24
por JoséQuintas
Pelo menos o quadro de produtos, não vai dar mais trabalho.
Tá fácil alterar qualquer coisa nele.

Gerador de relatorios?

Enviado: 26 Nov 2016 22:12
por fladimir
Parabéns

Gerador de relatorios?

Enviado: 27 Nov 2016 00:06
por rochinha
:-Y

Gerador de relatorios?

Enviado: 27 Nov 2016 07:24
por asimoes
:)Pos

Gerador de relatorios?

Enviado: 27 Nov 2016 12:24
por Pablo César
Parabéns Mr. Quintas

Gerador de relatorios?

Enviado: 27 Nov 2016 19:23
por sygecom
:)Pos :)Pos :)Pos

Gerador de relatorios?

Enviado: 28 Nov 2016 17:05
por JoséQuintas
Fiz o que faltava.
Descrição, informação adicional de produtos, e informações adicionais.

Até testei alterando o tamanho do fonte, e descobri um "bug"? na Harupdf.

Talvez não tenha sido feito pra isso.
Ao testar aonde dividir o texto pra caber na área selecionada:

1) Se couber, retorna Len( string )
2) Se precisar dividir a string, pega a posição até a palavra que cabe
3) Se não couber a primeira palavra, retorna ZERO, aí o problema que causou um loop infinito.

Mas já resolvido também.
No caso de retornar ZERO, tento largura área_pdf / tamanho do fonte, ou no mínimo 2 letras.
Isso é mais pra não travar, é uma exceção.
Testei com um fonte muito grande, onde nem caberiam as colunas no PDF..... rs
Mas vai que não tem espaço entre as palavras.... já fica resolvido.

Já fica aí o aviso:
Usar a Harupdf pra dividir uma frase em palavras, precisa precaução quando a palavra for maior que o espaço disponível.
Pra ela, não imprime nada, não divide nada.
Exceto se for a divisão normal, cortando em qualquer lugar.

Putz.... isso me leva a reconsiderar o que fiz, e usar a opção de cortar em qualquer lugar.... rs
Lá vamos nós de novo.... rs