Skip to content

fix pdf convert on product invoice#1

Merged
gblmarquez merged 2 commits into
devfrom
feature/implement-fix-pdf-convert-on-product-invoice
Dec 11, 2017
Merged

fix pdf convert on product invoice#1
gblmarquez merged 2 commits into
devfrom
feature/implement-fix-pdf-convert-on-product-invoice

Conversation

@lessilva
Copy link
Copy Markdown

@lessilva lessilva commented Dec 6, 2017

Fix error on Gfx class
Implement contingency SVCAN, SVCRS generate on pdf

Comment thread DanfeSharp/Modelo/DanfeViewModel.cs Outdated
@@ -345,4 +363,4 @@ public virtual String TextoAdicional()
public Boolean IsPaisagem => Orientacao == Orientacao.Paisagem;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Excluir as linhas em branco no arquivo.

@gblmarquez
Copy link
Copy Markdown

Tudo OK!

@gblmarquez gblmarquez closed this Dec 11, 2017
@gblmarquez gblmarquez reopened this Dec 11, 2017
@gblmarquez gblmarquez merged commit c6f51c6 into dev Dec 11, 2017
rhfranzoni added a commit that referenced this pull request May 28, 2026
Para visualizar como a tag <infAdProd> (informações adicionais do
produto, YA02 do MOC) é renderizada na DANFE:

- Item #1 (SOFTWARE A): <infAdProd>Licença digital · Não-reembolsável
  · S/N: ABC123-VERSAO-3.0</infAdProd>
- Item #2 (SOFTWARE B): <infAdProd>Garantia: 12 meses contra defeitos
  de fabricação · Suporte: 0800-XXX-YYYY</infAdProd>

A informação aparece logo abaixo do nome do produto, dentro da mesma
célula da coluna "DESCRIÇÃO DO PRODUTO / SERVIÇO" (concatenada via
ProdutoViewModel.DescricaoCompleta com separador \r\n). Conformidade
com MOC 7.0 Anexo II §3.1.7 (pg 10): "As informações adicionais de
produto deverão constar impressas no DANFE logo abaixo do item ao qual
se referirem."

Validação: dotnet vstest v4_RevendaMaisDemo → aprovado; texto
extraído por pdftotext confirma render correto com line wrap
automático em strings longas.

Refs #39
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
rhfranzoni added a commit that referenced this pull request May 29, 2026
…arca Cancelada) (#43)

* chore(openspec): bootstrap + change fix-danfe-csosn-rendering (#38)

Initialize OpenSpec (--tools=claude) and propose change covering
the ICMS column rendering bug reported in #38:

- Concatenation without separator (e.g. "120" instead of "1/20")
  in DanfeViewModelCreator.cs:497
- Static header label "O/CSOSN" derived from Emitente.CRT only,
  fragile when CRT is missing in the XML

Artifacts in openspec/changes/fix-danfe-csosn-rendering/:
- proposal.md: motivation, capabilities, impact
- design.md: 4 decisions anchored on real code (item-derived
  header, separate Origem/Cst/Csosn properties, OCst as
  computed, no feature flag)
- specs/danfe-icms-column/spec.md: 3 requirements + scenarios
- tasks.md: 9 task groups (investigation, model, producer,
  renderer, tests, visual validation, docs, PR, archive)

No code changes yet — implementation lands via /opsx:apply on a
follow-up commit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(danfe): coluna ICMS — separador / e cabeçalho derivado dos itens (#38)

Corrige dois sintomas reportados pelo cliente Revenda Mais na coluna
"O/CSOSN" do bloco produtos da DANFE:

1. Origem e código (CST/CSOSN) concatenados sem separador ("120" em vez
   de "1/20"). Causa: `produto.OCst = icms.orig + icms.CST + icms.CSOSN`
   em `DanfeViewModelCreator.cs:497`.

2. Cabeçalho "O/CSOSN" mesmo para emitentes do Regime Normal quando
   `<emit><CRT>` está ausente/inconsistente no XML. Causa: lógica
   dependia só de `Emitente.CRT == "3"`.

Mudanças

- `ProdutoViewModel` ganha `Origem`/`Cst`/`Csosn` separados. `OCst`
  vira propriedade calculada que aplica "<origem>/<código>" e omite
  componentes vazios (XMLs sem <orig> exibem só o código).
- `ProdutoViewModel.CalcularCabecalhoColunaIcms(produtos)` — helper
  estático que decide o cabeçalho inspecionando os itens da nota
  (se algum tem CST → "O/CST"; senão CSOSN → "O/CSOSN"; fallback
  "O/CST"). Mais robusto que confiar em `Emitente.CRT`.
- `DanfeViewModelCreator` popula `Origem`/`Cst`/`Csosn` normalizando
  strings vazias do XmlSerializer para null.
- `TabelaProdutosServicos` consome o helper.

Testes

`DanfeSharp.Test/ProdutoIcmsColumnTests.cs`: 15 testes (8 de célula,
7 de cabeçalho), incluindo regressão do caso Revenda Mais (CRT
ausente + CST nos itens → "O/CST"). 15/15 verdes via `dotnet vstest`.

Hotfix tangencial (pre-existente em `origin/main`, bloqueava build do
projeto de teste)

- `FabricaFake.cs:51`: cast `(decimal?)v` para `vFCPUFDest`.
- `DanfeTest.cs:63`: `DanfeCCC` renomeado para `DanfeCartaCorrecao`
  (rename antigo não propagado).

Pendente (manual)

- §1 pre-flight: confirmar com Carolina que separador é `/` e
  grep cross-repo por `ProdutoViewModel.OCst`.
- §6 validação visual: gerar DANFE da invoice de referência do
  Revenda Mais e comparar PDF antes/depois.

OpenSpec change: openspec/changes/fix-danfe-csosn-rendering/
Spec validated: `openspec validate` OK.

Fixes #38

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs(openspec): calibrar base normativa do change #38 (MOC 7.0 §3.1.7)

Após leitura literal do MOC 7.0 Anexo II (PDF oficial do CONFAZ),
constatei que:

- §3.1.7 (página 11) lista a coluna "CST" como obrigatória no
  quadro "Dados dos Produtos/Serviços", mas NÃO prescreve
  literalmente o formato combinado "O/CST" nem o separador "/".
- O campo <orig> faz parte do schema do XML (Anexo I) mas o Anexo II
  não menciona apresentação visual conjunta com CST/CSOSN.
- O formato "O/CST" com separador "/" é convenção universal de
  mercado (TOTVS, SAP, SmartGo, eMissor, etc.) — não mandato literal.

Calibragem aplicada em:
- proposal.md (Why): substituída "conformidade visual com MOC v7.0+"
  por "convenção universal de mercado", com referência §3.1.7
  para a parte que é mandato (coluna CST).
- design.md (Decision 3): nova seção "Base normativa" com URLs
  do CONFAZ para Anexo I + Anexo II. Open Question sobre separador
  marcada como resolvida.
- specs/danfe-icms-column/spec.md: nota normativa no topo do bloco
  ADDED Requirements explicando o que é mandato literal vs convenção.
- tasks.md §1.2: contexto expandido — separador "/" é convenção
  universal, não MOC literal.

Sem mudança de código nem de comportamento; só de citação/justificativa.
Implementation alinhada com convenção do mercado, que é o que
receptores fiscais esperam ver (raiz do problema com cliente
Revenda Mais).

Fixes #38 (continued)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* chore(openspec): archive fix-danfe-csosn-rendering → specs/danfe-icms-column

- Move openspec/changes/fix-danfe-csosn-rendering/ →
  openspec/changes/archive/2026-05-28-fix-danfe-csosn-rendering/
- Promote ADDED requirements to openspec/specs/danfe-icms-column/spec.md
- Preencher Purpose com base normativa (MOC 7.0 §3.1.7) — substituindo
  o boilerplate "TBD" gerado pelo CLI

A spec danfe-icms-column passa a ser fonte de verdade para qualquer
mudança futura na coluna ICMS da DANFE. Mudanças subsequentes devem
ser propostas como MODIFIED/ADDED via /opsx:new ou /opsx:propose.

Note: 11 tasks ficaram incompletas no archive (manuais/externas —
§1.1 grep cross-repo, §1.2 confirmar com Carolina, §4.3 validar
largura visual, §6 validação visual com XML real do Revenda Mais,
§8 review + merge, §9 archive já feita aqui). O archive forçou via
--yes flag por se tratar de tarefas extra-código.

Fixes #38 (continued)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* chore(openspec): plan add-danfe-payment-block (#39)

Initialize OpenSpec na branch (igual a #38) e propor o change cobrindo
a implementação do bloco "Forma de Pagamento" no DANFE de NF-e modelo 55:

PROBLEMA RAIZ
- DanfeSharp não renderiza bloco visual de pagamento mesmo quando o
  XML traz <pag><detPag> preenchido (NT 2016.002 v1.50 tornou
  obrigatório desde 2018).
- Cliente Revenda Mais reportou via Carolina Fagundes / Teams 2026-05-27;
  invoice 9175b02ac0cf4ed898025c4bad09e2fe.

ESTADO DO CÓDIGO (auditado)
- Schema XML: pag/detPag/FormaPagamento enum já existem (ProcNFe.cs)
  com Description em cada tPag (01..19, 90, 99); FALTA xPag em detPag.
- ViewModel: PagamentoViewModel + DetalheViewModel + CartaoViewModel
  já existem; FALTA Descricao em DetalheViewModel.
- Producer (DanfeViewModelCreator) já popula model.Pagamento; falta
  apenas mapear xPag → Descricao.
- FALTA renderer visual (BlocoFormaPagamento.cs).
- FALTA inserção no fluxo do construtor de Danfe.cs.

BASE NORMATIVA CALIBRADA (lição aprendida do #38)
- MOC 7.0 Anexo II não tem seção dedicada a forma de pagamento para
  DANFE NF-e modelo 55 (verifiquei literalmente o PDF do CONFAZ;
  zero ocorrências de "pagamento"/"detPag"/"tPag" no texto).
- NT 2016.002 obrigou o grupo <pag> no XML; o manual do DANFE NFCe
  (Anexo III) mandou exibir o bloco no cupom da NFCe.
- Para NF-e modelo 55, o bloco visual é CONVENÇÃO UNIVERSAL DE MERCADO
  (TOTVS, SAP, SmartGo, eMissor), não mandato literal. Spec deixa
  isso explícito.

ARTEFATOS
- proposal.md (5.9 KB): motivação + capability danfe-payment-block + impacto
- design.md (14 KB): 6 decisões ancoradas no código + open questions
- specs/danfe-payment-block/spec.md (8.2 KB): 8 requirements + 18 cenários BDD
- tasks.md (8.3 KB): 13 grupos (32 itens) cobrindo schema, viewmodel,
  producer, helper, renderer, fluxo, testes, fixtures, validação
  visual, docs, PR, archive

`openspec validate` OK.

Nenhuma mudança de código ainda — implementação virá via /opsx:apply
no próximo commit.

Fixes #39

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(danfe): adiciona bloco FORMA DE PAGAMENTO (#39)

Adiciona o bloco "Forma de Pagamento" na DANFE de NF-e modelo 55,
lendo o grupo <pag><detPag> do XML. Demanda do cliente Revenda Mais
(invoice 9175b02ac0cf4ed898025c4bad09e2fe), umbrella #37.

MUDANÇAS

Schema (DanfeSharp/Esquemas/ProcNFe.cs)
- detPag ganha campo xPag (YA03 — descrição do pagamento).

ViewModel (DanfeSharp/Modelo/PagamentoViewModel.cs)
- DetalheViewModel ganha propriedade Descricao (mapeada de xPag).

Producer (DanfeSharp/Modelo/DanfeViewModelCreator.cs)
- Producer existente para <pag> em CreateFromProcNFCe ganha população
  de Descricao (xPag → Descricao com normalização "" → null).
- BUG RAIZ ADICIONAL: a população do grupo <pag> estava SÓ em
  CreateFromProcNFCe (NFC-e modelo 65) — faltava em CreateFromProcNFe
  (NF-e modelo 55, escopo desta change). Adicionado o foreach no
  CreateFromProcNFe antes do return — sem essa correção o renderer
  nunca dispararia para NF-e.

Helper (DanfeSharp/Modelo/FormaPagamentoExtensions.cs — novo)
- GetDescricao(this FormaPagamento) lê [DescriptionAttribute] via
  reflexão. Retorna string.Empty para enum value inválido (defensivo).

Renderer (DanfeSharp/Blocos/BlocoFormaPagamento.cs — novo)
- Herda BlocoBase. Cabecalho "Forma de Pagamento". Itera sobre
  Pagamento[].DetalhePagamento via SelectMany e cria uma LinhaCampos
  por detalhe com FORMA PAGAMENTO (Descricao ?? GetDescricao) + VALOR
  (ComCampoNumerico, sem prefixo R$ — convenção do BlocoCalculoImposto).
- Auto-omissão: early return quando detalhes == null/empty.

Sequência (DanfeSharp/Danfe.cs)
- AdicionarBloco<BlocoFormaPagamento>() inserido entre
  BlocoCalculoImposto e BlocoTransportador.
- Condicionado a Pagamento ser não-vazio (defense in depth).

TESTES (DanfeSharp.Test/FormaPagamentoTests.cs — novo)
- 14 testes em FormaPagamentoTests: helper para Dinheiro/CartaoCredito/
  CartaoDebito/Outro/PIX/inválido, schema parsing com/sem <xPag>,
  ViewModel propertes, regra Descricao-prevalece-sobre-enum.
- 2 testes de integração em IntegracaoPagamentoNFe: NF-e v4 popula
  model.Pagamento (regressão do bug raiz §4.2); NF-e v3.10 sem <pag>
  não quebra e mantém Pagamento vazio.
- Aliases FormaPagamentoVm/FormaPagamentoSchema para desambiguar os 2
  enums coexistentes (Modelo/Enums.cs e Esquemas/ProcNFe.cs).
- DanfeSharp.Test.csproj: <Reference Include="System.Xml" /> para
  acessar XmlSerializer nos testes de schema.

dotnet vstest:
- FormaPagamentoTests + IntegracaoPagamentoNFe: 16/16 verde (748 ms)
- DanfeXmlTests: 5/5 verde (zero regressão)
- PDF re-renderizado do v4_ComLocalEntrega.xml exibe o bloco no local
  esperado (texto extraído: "FORMA DE PAGAMENTO ... VALOR ... Outros
  ... 3.977,00").

HOTFIX TANGENCIAL (pré-existente em origin/main)
- FabricaFake.cs:51 cast (decimal?)v para vFCPUFDest
- DanfeTest.cs:63 DanfeCCC → DanfeCartaCorrecao
- Mesmas correções aplicadas na PR #41 (#38); necessárias para o
  projeto de teste compilar enquanto a #41 não merge na main.

OpenSpec change: openspec/changes/add-danfe-payment-block/
Spec validated: `openspec validate` OK.
38 tasks; tasks externas/manuais (§1, §10, §12, §13) seguem pendentes
do lado humano (cliente + reviewer + post-merge archive).

Fixes #39

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* refactor(danfe): layout horizontal dos campos da Duplicata (FATURA / DUPLICATA)

Reportado pelo cliente Revenda Mais durante revisão do PR #42: o quadro
"FATURA / DUPLICATA" exibia Número / Vencimento / Valor empilhados
verticalmente dentro de cada célula, ocupando 3× mais altura que o
necessário. Refatora para layout horizontal, alinhando com o padrão
visual do BlocoCalculoImposto e demais blocos com campos labelados.

Mudanças

- DanfeSharp/Elementos/Duplicata.cs: o Draw passa a dividir o
  bounding rect em 3 colunas iguais e desenhar cada par (label
  no topo + valor abaixo) lado a lado em vez de empilhar com CutTop.
  Height reduzida de 3*linha → 2*linha (label + valor).

- DanfeSharp/Blocos/BlocoDuplicataFatura.cs: numeroElementosLinha
  reduzido de 6→3 (retrato) e 7→4 (paisagem) para acomodar a largura
  necessária por célula no novo layout. Sem essa redução, os valores
  ("11/10/2018", "R$ 3.977,00") seriam cortados.

Validação visual

- v4_ComLocalEntrega.pdf re-renderizado. Texto extraído por pdftotext:
    Número                Vencimento:          Valor:
    001                   11/10/2018           R$ 3.977,00
  (em uma única linha, 3 colunas legíveis)

- dotnet vstest DanfeXmlTests: 5/5 aprovados, zero regressão.

Fora do escopo original do PR #42 (que cobre o bloco FORMA DE PAGAMENTO).
Aplicado como hotfix de polish a pedido do criador da PR, conforme
feedback durante revisão visual da DANFE rendererizada.

Base normativa: o MOC 7.0 Anexo II §3.1.6 ("Quadro Fatura/Duplicatas")
não prescreve layout específico para os campos internos da duplicata —
"Poderá conter linhas divisórias internas separando as informações."
Layout escolhido alinha com a convenção dos demais blocos do
DanfeSharp.

Refs #39
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* test(danfe): fixture v4_RevendaMaisDemo para visualizar ambos os ajustes

XML sintético derivado de v4_ComLocalEntrega.xml mas com cobr/pag
expandidos para exercitar todos os caminhos novos:

FATURA / DUPLICATA (validação do refactor 371ed39)
- 6 duplicatas mensais R$ 662,83 cada (sum 3.977,00 igual ao vLiq da
  fat). Demonstra o novo layout horizontal de campos + paginação em
  2 linhas de 3 duplicatas (numeroElementosLinha=3 em retrato).

FORMA DE PAGAMENTO (validação da feature aaf6e9f)
- 3 <detPag> variados:
  • tPag=17 (PIX, NT 2020.001) — R$ 1.500,00 → renderiza
    "Pagamento Instantâneo (PIX)" via fallback do enum
  • tPag=03 (Cartão de Crédito) + grupo <card> — R$ 1.000,00 →
    renderiza "Cartão de Crédito" via fallback do enum
  • tPag=99 (Outros) + <xPag>VALE TROCO</xPag> — R$ 1.477,00 →
    renderiza "VALE TROCO" (xPag prevalecendo sobre "Outros",
    regra da spec danfe-payment-block)

Soma dos detPag = 3.977,00 = soma das duplicatas = vNF (consistente).

Wiring
- v4_RevendaMaisDemo.xml em DanfeSharp.Test/Xml/NFe/v4.00/
- <Content Include> + CopyToOutputDirectory em DanfeSharp.Test.csproj
- DanfeXmlTests.v4_RevendaMaisDemo() chama TestXml(...) que gera
  Output/DeXml/v4_RevendaMaisDemo.pdf

Validação
- dotnet vstest da suite completa (DanfeXmlTests + FormaPagamentoTests
  + IntegracaoPagamentoNFe) → 22/22 verde.
- Texto extraído do PDF confirma:
    FATURA / DUPLICATA: 6 duplicatas em 2 linhas, layout horizontal.
    FORMA DE PAGAMENTO: 3 linhas com FormaPagamento + Valor distintos.

Refs #39
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* test(fixture): adiciona <infAdProd> em 2 produtos do demo

Para visualizar como a tag <infAdProd> (informações adicionais do
produto, YA02 do MOC) é renderizada na DANFE:

- Item #1 (SOFTWARE A): <infAdProd>Licença digital · Não-reembolsável
  · S/N: ABC123-VERSAO-3.0</infAdProd>
- Item #2 (SOFTWARE B): <infAdProd>Garantia: 12 meses contra defeitos
  de fabricação · Suporte: 0800-XXX-YYYY</infAdProd>

A informação aparece logo abaixo do nome do produto, dentro da mesma
célula da coluna "DESCRIÇÃO DO PRODUTO / SERVIÇO" (concatenada via
ProdutoViewModel.DescricaoCompleta com separador \r\n). Conformidade
com MOC 7.0 Anexo II §3.1.7 (pg 10): "As informações adicionais de
produto deverão constar impressas no DANFE logo abaixo do item ao qual
se referirem."

Validação: dotnet vstest v4_RevendaMaisDemo → aprovado; texto
extraído por pdftotext confirma render correto com line wrap
automático em strings longas.

Refs #39
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* style(danfe): rename QUANTI.→QUANT. e diminui coluna para favorecer DESCRIÇÃO

Pedido durante revisão visual da PR #42:
- Renomear o cabeçalho da coluna Quantidade de "QUANTI." para "QUANT."
- Diminuir a largura da coluna (sobrava espaço pra abreviação mais curta)
- Devolver o espaço ganho à coluna DESCRIÇÃO DO PRODUTO / SERVIÇO
  (que é flex, width=0 — absorve automaticamente)

Mudanças em DanfeSharp/Blocos/TabelaProdutosServicos.cs:
- Retrato:   6F   → 4F     (-2F devolvidos à descrição)
- Paisagem:  5.25F → 3.75F (-1.5F devolvidos à descrição)
- Label em ambos os modos: "QUANTI." → "QUANT."

Validação: dotnet vstest DanfeXmlTests → 6/6 verde, zero regressão.
Texto extraído do PDF confirma cabeçalho "QUANT." e descrição mais larga.

Refs #39
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* style(danfe): diminui coluna NCM/SH em favor da DESCRIÇÃO

Continuação do ajuste visual da PR #42:
- NCM/SH: o conteúdo é sempre 8 dígitos (49111090) — não precisa do
  espaço atual. Diminuir e devolver à coluna DESCRIÇÃO (flex).

Retrato:   5.6F  → 4.5F  (-1.1F devolvidos à descrição)
Paisagem:  5.5F  → 4.5F  (-1.0F devolvido à descrição)

Validação: dotnet vstest DanfeXmlTests → 6/6 verde.

Refs #39
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(danfe): ajusta largura da coluna NCM/SH (4.5F → 5F)

O commit anterior (c72ebab) reduziu NCM/SH para 4.5F, mas a coluna
ficou apertada demais — o último dígito do NCM de 8 dígitos era
truncado (49111090 virava 4911109). Aumenta para 5F (ainda ganha
0.6F vs original em retrato, 0.5F em paisagem) garantindo que NCMs
de 8 dígitos rendereizem completos.

Refs #39
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* style(danfe): ajuste fino paisagem QUANT. 3.75F → 4.00F

Pedido durante review da PR #42: dar +0.25F à coluna QUANT. em modo
paisagem (que estava apertada visualmente). DESCRIÇÃO (flex, width=0)
absorve o ajuste automaticamente cedendo 0.25F.

Refs #39
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* test(danfe): adiciona test methods para paisagem (demo + light fixture)

Pedido durante review da PR #42: gerar exemplos completos em retrato
e paisagem. Adiciona 2 test methods em DanfeXmlTests:

- v4_RevendaMaisDemo_Paisagem: força Orientacao=Paisagem na fixture
  rica. Atualmente FALHA com "Height is invalid" — overflow vertical
  porque a fixture tem 6 duplicatas + 3 detPag que não cabem em A4
  paisagem (mais baixa que retrato). Documentado como bug pre-existente
  do layout engine (mesmo problema dos testes Paisagem*/FabricaFake
  que já falhavam em main).

- v4_ComLocalEntrega_Paisagem: força paisagem na fixture leve (1
  duplicata + 1 detPag) — funciona. Demonstra que a renderização
  paisagem em si está OK; falha do demo é por excesso de conteúdo.

DanfeViewModelCreator.cs:411 ainda hardcoda Orientacao=Retrato
(comentário sobre netcore2.0 bug). Esses tests forçam override
após o load.

Refs #39
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(danfe): paginar quando blocos topo overflow vertical (em vez de crash)

Bug: Danfe.Gerar() lançava "InvalidOperationException: Height is
invalid" quando os blocos topo (Emitente, Destinatário, Local
Entrega/Retirada, Fatura, Cálculo, Forma de Pagamento, Transportador,
Dados Adicionais) consumiam toda a altura disponível da página —
situação comum em paisagem com NF-e ricas (caso típico do
cliente Revenda Mais com 6 duplicatas + 3 detPag + bloco FORMA).

Root cause: o while loop em Gerar() já suportava paginação via
TabelaProdutosServicos.CompletamenteDesenhada, mas faltava um guard
para casos onde RetanguloCorpo.Height ≤ 0 — ele tentava desenhar
mesmo assim e o DrawableBase.Draw lançava.

Fix: adiciona guard em Gerar() — quando RetanguloCorpo.Height ≤ 0,
pula a página sem desenhar a tabela. A próxima iteração cria uma
nova página onde apenas BlocoIdentificacaoEmitente é desenhado
(único com VisivelSomentePrimeiraPagina=false), liberando espaço
para a tabela.

Proteção contra loop infinito: se 2 páginas consecutivas têm Height
≤ 0, lança erro descritivo orientando reduzir blocos opcionais ou
usar Retrato.

Validação:
- v4_RevendaMaisDemo_Paisagem (heavy: 6 duplicatas + 3 detPag) — antes
  lançava NPE, agora gera PDF com 2 páginas: pág 1 = blocos topo,
  pág 2 = identificação + tabela produtos.
- v4_ComLocalEntrega_Paisagem (light) — continua passando.
- Retrato continua passando para todas as fixtures.
- Falhas pre-existentes em FabricaFake-based Paisagem tests (PaisagemSemIcmsInterestadual etc.)
  não são afetadas — são causadas por bug independente (TextoReservadoFisco():355
  NotImplementedException por TipoEmissao=0 default no fake).

Refs #39
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* test(fixtures): adiciona 3 demos cobrindo cenários extremos + intermediário

Para visualizar a renderização da DANFE em variações de complexidade
(pedido durante review da PR #42):

- v4_DanfeMinimo.xml — 1 item sem infAdProd, sem <cobr>, sem <pag>.
  Caso de NF-e mínima (ex.: doação, brinde) sem fatura/pagamento.
  PDF gerado: 5.8 KB, 1 página.

- v4_DanfeIntermediario.xml — 5 itens (3 com infAdProd: notebook,
  teclado, monitor; 2 sem: mouse, cabo), 2 duplicatas R$ 500 cada,
  2 detPag (Cartão Crédito R$ 600 + PIX R$ 400). Total R$ 1000.
  PDF: 6.7 KB.

- v4_DanfeCompleto.xml — 20 itens (todos com infAdProd
  "Lote LT-N-2026 · Validade 12 meses · Origem: Brasil"), 6
  duplicatas mensais R$ 333.34 cada, 3 detPag (PIX 800 + Cartão
  Crédito 700 + Outros R$ 500 com xPag="PROMOCIONAL BLACK FRIDAY").
  Total R$ 2000. PDF: 11 KB, múltiplas páginas (paginação automática
  graças ao fix em Danfe.Gerar()).

Geração via script reproduzível (generate-fixtures.sh) com helpers
para gen_item, gen_dup, gen_detpag, gen_header, gen_footer.
Reexecutável: bash DanfeSharp.Test/Xml/NFe/v4.00/generate-fixtures.sh

Wiring
- Content + CopyToOutputDirectory em DanfeSharp.Test.csproj
- 3 test methods em DanfeXmlTests (v4_DanfeMinimo, v4_DanfeIntermediario,
  v4_DanfeCompleto) que chamam TestXml(...) para gerar PDFs em
  bin/Debug/Output/DeXml/

Validação
- dotnet vstest dos 3 → todos aprovados.
- Sanity checks via pdftotext confirmam contagens esperadas:
  EX1: 0 FATURA, 0 FORMA, 1 PROD-, 0 infAdProd
  EX2: 1 FATURA, 1 FORMA, 5 PROD-
  EX3: 1 FATURA, 1 FORMA, 20 PROD-, 20 "Lote" (infAdProd)

Refs #39
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* test(fixtures): adiciona 3 demos em paisagem (Mínimo + Intermediário + Completo)

Mesmas fixtures do commit anterior, agora renderizadas em paisagem
para visualização completa do layout em ambos os modos.

Adicionado helper RenderPaisagem(xmlPath, outName) que carrega o XML
e força Orientacao = Paisagem antes de gerar (necessário porque
DanfeViewModelCreator hardcoda Retrato — comentário sobre netcore2.0
em DanfeViewModelCreator.cs:411).

3 novos test methods:
- v4_DanfeMinimo_Paisagem
- v4_DanfeIntermediario_Paisagem
- v4_DanfeCompleto_Paisagem (20 itens em paisagem — paginação automática
  graças ao fix do bug "Height is invalid" aplicado anteriormente)

Todos os 3 passam. PDFs gerados em bin/Debug/Output/DeXml/ com
sufixo _Paisagem.

Refs #39
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(danfe): marca d'água "DOCUMENTO CANCELADO" para NF-e cancelada (#40)

Tema 3 do umbrella Revenda Mais (issue #37). Renderiza marca d'água
"DOCUMENTO CANCELADO" centralizada em todas as páginas da DANFE
quando a NF-e está cancelada.

ARQUITETURA — primeiro detectar via flag, depois cStat=101 como fallback

O cancelamento em NF-e modelo 55 é registrado via EVENTO SEPARADO
(NFeProcEvento com tpEvento=110111), NÃO via mudança no <cStat> da
NF-e original — o XML protocolo da nota mantém cStat=100 (Autorizado)
mesmo após cancelamento autorizado. Quem sabe que a nota foi
cancelada é o sistema do emissor consultando o histórico de eventos.

A PR #8 do @mateuszanini (2023, branch feature/aviso-cancelamento)
tinha a renderização visualmente correta mas detectava via cStat=101
do XML protocolo — que na prática raramente é 101 para uma nota
cancelada. Esta change reaproveita o método dele (DesenharAvisoCancelamento)
mas usa flag explícita IsCancelled como mecanismo primário + cStat=101
como fallback OR (zero custo).

CÓDIGO

- DanfeSharp/Modelo/DanfeViewModel.cs:223 — nova propriedade
  IsCancelled (bool, default false) com XML doc explicando o motivo
  arquitetural (cancelamento via evento, não via cStat do XML).

- DanfeSharp/DanfePagina.cs:106 — novo método DesenharAvisoCancelamento
  espelhando DesenharAvisoHomologacao: TextStack centralizado no
  RetanguloCorpo, fonte 48pt regular, cor RGB(0.35, 0.35, 0.35),
  texto "DOCUMENTO CANCELADO". Padrão visual idêntico ao "SEM VALOR
  FISCAL" já existente — vamos da PR #8.

- DanfeSharp/Danfe.cs:CriarPagina — após DesenharAvisoHomologacao,
  adiciona "if (ViewModel.IsCancelled || ViewModel.CodigoStatusReposta == 101)
  p.DesenharAvisoCancelamento();". Sobreposição com aviso de
  homologação aceita (NF-e cancelada em homologação → ambos os
  avisos aparecem, comportamento correto).

TESTES (DanfeCanceladaTests.cs, 11 tests)

- Unit-level: IsCancelled default false, settable.
- Integration: render com IsCancelled=true, com cStat=101, com ambos,
  e regressão sem nenhum trigger.
- Demos: render dos 3 fixtures (Mínimo/Intermediário/Completo) +
  versões paisagem do Intermediário e Completo, todos com IsCancelled=true.
- pdftotext -raw confirma "DOCUMENTO CANCELADO" presente em
  todos os 5 PDFs cancelados (1x em página única, 2x em multi-página
  do Completo), e 0 ocorrências em DANFEs não-canceladas.
- DanfeXmlTests existentes: 14/14 verde (zero regressão).

BASE NORMATIVA

MOC 7.0 Anexo II §3.10.1 (Marca d'Água) autoriza literalmente:
"O formulário poderá conter marca d'água desde que não prejudique a
legibilidade dos dados impressos." NÃO obriga marca para nota
cancelada, NÃO especifica texto/posição/opacidade. Texto e estilo
são convenção universal de mercado (TOTVS/SAP/SmartGo/eMissor)
alinhada ao "SEM VALOR FISCAL" do ambiente de homologação.

OpenSpec change: openspec/changes/add-cancelled-watermark/
- proposal.md, design.md (5 decisões), specs/danfe-cancelled-watermark/spec.md
  (5 requirements + 11 cenários), tasks.md (10 grupos).
- openspec validate ✓

Fixes #40
Refs PR #8 (@mateuszanini, 2023 — render mantida, gatilho substituído)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* refactor(danfe): bloco FORMA DE PAGAMENTO usa padrão visual de FATURA/DUPLICATA

Ajustes cosméticos pedidos durante review da PR #43:

1) Mover bloco FORMA DE PAGAMENTO para logo abaixo de FATURA/DUPLICATA
   (em Danfe.cs:CriarPagina). Ordem antiga:
     FATURA → CÁLCULO IMPOSTO → FORMA PAGAMENTO → TRANSPORTADOR
   Nova:
     FATURA → FORMA PAGAMENTO → CÁLCULO IMPOSTO → TRANSPORTADOR
   Agrupa os blocos de informação comercial (fatura + pagamento)
   adjacentes, separados dos fiscais (imposto + transportador).

2) Refatorar BlocoFormaPagamento para mesmo padrão visual do
   BlocoDuplicataFatura — cards lado a lado em FlexibleLine, cada
   um com 2 sub-colunas (FORMA PAGAMENTO + VALOR), label em cima
   e valor abaixo. Antes era 1 linha por detalhe via
   AdicionarLinhaCampos (estilo do BlocoCalculoImposto).

   Novo elemento DanfeSharp/Elementos/PagamentoDetalhe.cs espelhando
   Duplicata.cs:
   - 2 colunas iguais (FORMA PAGAMENTO + VALOR) com label em FonteA
     e valor em FonteB (mesmas fontes da Duplicata).
   - Descricao prevalece sobre [Description] do enum (regra da spec
     danfe-payment-block já existente).
   - Valor formatado com R$ via Formatador.FormatarMoeda (cast
     decimal→double).

   BlocoFormaPagamento usa FlexibleLine com N cards por linha:
   - Retrato: 2 (descrições podem ser longas, ex. "Pagamento
     Instantâneo (PIX)" = 28 chars; 4 cards/linha colidia)
   - Paisagem: 3

Validação visual em demo Completo (retrato, 3 detPag):
  Linha 1: PIX R$ 800,00 | Cartão de Crédito R$ 700,00
  Linha 2: VALE TROCO R$ 500,00

Em paisagem, os 3 detPag cabem na mesma linha.

Testes: 56/56 verde (zero regressão em ProdutoIcmsColumnTests,
FormaPagamentoTests, IntegracaoPagamentoNFe, DanfeXmlTests,
DanfeCanceladaTests). PDFs demo atualizados em
../danfe-payment-validation/{exemplos,exemplos-cancelada}/.

Refs #39
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* style(danfe): Title Case nas labels de PagamentoDetalhe (espelha Duplicata)

Pedido durante review da PR #43: as labels internas do PagamentoDetalhe
estavam em ALL UPPERCASE ("FORMA PAGAMENTO", "VALOR") enquanto as do
Duplicata.cs estão em Title Case ("Número", "Vencimento:", "Valor:").
Alinhar para padrão único.

Mudança:
- "FORMA PAGAMENTO" → "Forma de Pagamento" (sem ':', como "Número")
- "VALOR" → "Valor:" (com ':', como "Vencimento:" e "Valor:" em Duplicata)

Refs #39
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* chore(openspec): archive add-danfe-payment-block + add-cancelled-watermark

Promove 2 specs para openspec/specs/ ao final da sprint Revenda Mais:

- danfe-payment-block (#39) — bloco "Forma de Pagamento" da DANFE NF-e
  modelo 55 (schema <pag>/<detPag>/<xPag>, mapping tPag→descrição via
  [Description], prevalência de xPag, omissão graciosa, layout em cards
  estilo Duplicata).

- danfe-cancelled-watermark (#40) — marca d'água "DOCUMENTO CANCELADO"
  para NF-e cancelada (flag IsCancelled primária + cStat=101 fallback,
  fonte 48pt cinza centralizada em todas as páginas).

Purpose preenchido para os 2 specs (substituindo o placeholder TBD
gerado pelo openspec archive), citando base normativa MOC 7.0
Anexo II + NT 2016.002 e reconhecendo que os formatos visuais são
convenção universal de mercado (não mandato literal do MOC).

Changes movidas para openspec/changes/archive/:
- 2026-05-28-add-danfe-payment-block
- 2026-05-28-add-cancelled-watermark

(Já estavam arquivadas: 2026-05-28-fix-danfe-csosn-rendering, do
tema 1 da sprint.)

Refs #37 (umbrella), #38, #39, #40
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix: address PR #43 Copilot review (OCst setter + watermark assertions)

- ProdutoViewModel.OCst: restaura setter público como [Obsolete] com
  backing field _ocstLegacy, usado apenas quando Origem/Cst/Csosn estão
  todos vazios. Preserva ABI/source-compat para consumers que ainda
  atribuem OCst diretamente (uso anterior a Origem/Cst/Csosn).
- DanfeCanceladaTests: testes IsCancelledTrue, cStat101 e NaoCancelada
  agora extraem texto do PDF via TextExtractor do PDFClown (sem dependencia
  externa) e asseguram presenca/ausencia real da marca "DOCUMENTO CANCELADO"
  - antes eram smoke tests que passariam mesmo se a marca nunca fosse desenhada.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(danfe): celulas vazias com borda em Fatura/Duplicata e Forma de Pagamento

Quando o numero de duplicatas/formas de pagamento nao fecha a linha de
colunas (1 duplicata num bloco que cabe 3, p.ex.), as posicoes restantes
ficavam como espaco em branco sem borda, truncando visualmente o quadro
antes da margem direita. Substituido o ElementoVazio (Draw vazio, herdava
DrawableBase) por CelulaVazia (herda ElementoBase, recebe Estilo e
desenha apenas a borda via base.Draw -> StrokeRectangle), preenchendo o
restante da linha com celulas com borda em retrato e paisagem.

Aplicado em BlocoDuplicataFatura e BlocoFormaPagamento; ElementoVazio
mantido intacto (sem outros usuarios).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants