DACTE: agrega componentes do valor além do 9º em vez de descartá-los - #191
Merged
Conversation
O quadro "COMPONENTES DO VALOR DA PRESTAÇÃO DO SERVIÇO" é um retângulo fixo de 18 mm: os dados começam 6 mm abaixo do topo e cada linha ocupa 4 mm, então cabem 3 linhas em 3 colunas. A distribuição era fixa em comp_list[:3], [3:6] e [6:9] — o que passasse disso não era desenhado. O XSD do CT-e (PL_CTe_400, cteTiposBasico_v4.00.xsd) define Comp como maxOccurs="unbounded", então um CT-e com mais de 9 componentes é válido. O efeito no PDF era pior que a omissão: o VALOR TOTAL DO SERVIÇO (vTPrest) é impresso na quarta coluna do mesmo retângulo, então a soma dos componentes exibidos deixava de bater com o total ao lado, sem nenhuma indicação de por quê. O excedente passa a ser agregado numa linha "DEMAIS", preservando a soma. O rótulo não é "OUTROS" porque xNome é texto livre de até 15 caracteres e um componente real pode se chamar assim — o PDF sairia com duas linhas homônimas e valores diferentes. Nem self.comp_list nem self.emit_name são mutados: só o texto renderizado. Vale registrar o que as outras implementações fazem, porque nenhuma resolve: o ACBr (ACBrCTeDACTeRLRetrato.pas) distribui com `case i of 0,3,6,9 / 1,4,7,10 / 2,5,8,11` sem ramo `else`, ou seja, descarta em silêncio a partir do 13º; o sped-da (nfephp-org) desenha todos e deixa vazar para fora do quadro. As fixtures têm no máximo 6 componentes, que é por que os testes golden nunca pegaram isto: os PDFs de referência seguem inalterados.
This was referenced Aug 14, 2026
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #191 +/- ##
=======================================
Coverage 96.43% 96.44%
=======================================
Files 32 32
Lines 4375 4385 +10
Branches 348 349 +1
=======================================
+ Hits 4219 4229 +10
Misses 92 92
Partials 64 64 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
CristianoMafraJunior
approved these changes
Aug 19, 2026
This was referenced Aug 20, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Segunda metade da #186, de @andmit10 — a parte que envolve decisão de layout. Os dois recortes de texto foram separados na #190.
Problema
O quadro "COMPONENTES DO VALOR DA PRESTAÇÃO DO SERVIÇO" é um retângulo fixo de 18 mm: os dados começam 6 mm abaixo do topo e cada linha ocupa 4 mm, então cabem 3 linhas × 3 colunas = 9 componentes. A distribuição era fixa:
comp_list[9:]nunca era referenciado. Do 10º componente em diante o dado sumia — sem aviso.E o efeito é pior que a omissão: o VALOR TOTAL DO SERVIÇO (
vTPrest) é impresso na quarta coluna do mesmo retângulo. Então a conta não fecha na cara do documento:O XSD do CT-e (
PL_CTe_400,cteTiposBasico_v4.00.xsd) defineCompcomomaxOccurs="unbounded"— um CT-e com mais de 9 componentes é perfeitamente válido.O que muda
O excedente é agregado numa linha
DEMAIS, preservando a soma. Nada mutaself.comp_list— só o texto renderizado.O rótulo não é
OUTROS:xNomeé texto livre de até 15 caracteres, então um componente real pode se chamar assim, e o PDF sairia com duas linhas homônimas e valores diferentes.Prior art — nenhuma implementação resolve isto
Vale registrar, porque muda como esta PR deve ser lida:
ACBrCTeDACTeRLRetrato.pas)src/CTe/Dacte.php)DEMAISO ACBr distribui com
case i of 0,3,6,9 / 1,4,7,10 / 2,5,8,11sem ramoelse— em Delphi isso não faz nada, então do 13º em diante o dado é descartado, exatamente o mesmo bug com um teto maior. O sped-da percorre todos sembreake deixa o conteúdo transbordar do retângulo.Ou seja: agregar coloca a lib à frente das duas referências. É uma escolha, não conformidade — e por isso está numa PR separada, fácil de recusar sem segurar o recorte de texto.
A alternativa conservadora seria empatar com o ACBr subindo o teto para 12 (4 linhas de 3 mm em vez de 3 de 4 mm). Custa mexer na geometria, muda todos os PDFs golden, e ainda descarta a partir do 13º — mais trabalho por menos cobertura.
Testes
Renderizam o PDF e leem de volta as posições do texto do content stream.
test_componentes_alem_do_nono_aparecem_agregados— 12 componentes: saemCOMP 1..8+DEMAIS, e a linha agregada vale420.00(= 90+100+110+120)test_nove_componentes_saem_sem_agregacao— exatamente 9 cabem, nada é agregadotest_soma_dos_componentes_exibidos_bate_com_o_total— com 6, 9, 12 e 20 componentes, a soma do que está impresso fecha com o que o XML declaratest_to_float_tolera_valor_ausente_ou_invalidoPDFs de referência inalterados — as fixtures têm no máximo 6 componentes, que é por que os testes golden nunca pegaram isto.