feat(danfe): ajustes Revenda Mais (CST/CSOSN + Forma de Pagamento + Marca Cancelada)#43
Merged
Merged
Conversation
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>
…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>
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>
…-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>
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>
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>
…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>
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>
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>
…fe-forma-pagamento Combina as alterações do tema 1 (#38 — coluna O/CSOSN: separador / e cabeçalho derivado dos itens) na branch do tema 2 (#39 — bloco "Forma de Pagamento") para visualizar todas as mudanças da sprint da Revenda Mais numa única PR. Esta é uma branch de visualização/integração — quando #41 mergear na main, este merge commit fica obsoleto (os commits da #41 já estarão na main e essa branch só ficará com os commits específicos da #42).
…ESCRIÇÃ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>
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>
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>
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>
…rash) 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>
…diá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>
…+ 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>
) 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>
…/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>
…icata) 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>
…rmark 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>
- 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>
3 tasks
…agamento 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>
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.
Resumo
PR umbrella consolidando os 3 temas da sprint Revenda Mais (umbrella #37) em uma única branch para review e merge.
/+ cabeçalho dinâmico derivado dos itensPRs #41 e #42 foram fechadas como superseded — seus commits estão preservados nesta branch via merge/branching chain.
Tema 1 — Fix coluna O/CST/CSOSN (#38)
Problema: célula da coluna ICMS exibia
400(origem 4 concat com CST 00) em vez de4/00. Cabeçalho usava sempreO/CSOSNmesmo em emitentes Regime Normal quando<emit><CRT>estava ausente no XML.Solução:
DanfeViewModelCreator.cs:497: separarproduto.OCst = icms.orig + icms.CST + icms.CSOSNem propriedadesOrigem/Cst/CsosnnoProdutoViewModel.OCstvirou propriedade calculada (Origem/codigocom/, sem barra solta quando<orig>ausente).TabelaProdutosServicos.cs: cabeçalho derivado dos itens viaProdutoViewModel.CalcularCabecalhoColunaIcms()— robusto a CRT ausente.Base normativa: MOC 7.0 Anexo II §3.1.7 manda coluna
CSTobrigatória — não prescreve formato combinadoO/CSTnem separador/. É convenção universal de mercado (TOTVS/SAP/SmartGo/eMissor).OpenSpec change
fix-danfe-csosn-rendering(arquivada → specdanfe-icms-column).Tema 2 — Bloco FORMA DE PAGAMENTO + ajustes visuais (#39)
Problema: DanfeSharp não renderizava bloco "FORMA DE PAGAMENTO" mesmo com
<pag><detPag>preenchido no XML.Solução:
ProcNFe.cs: adicionada tagxPag(YA03) à classdetPag.PagamentoViewModel: nova propriedadeDescricao(mapeada dexPag).DanfeViewModelCreator: novo bloco emCreateFromProcNFe(faltava — só estava emCreateFromProcNFCe). Bug raiz:model.Pagamentoficava vazio para NF-e modelo 55.FormaPagamentoExtensions.GetDescricao(FormaPagamento): helper via[DescriptionAttribute](Dinheiro / Cartão Crédito / PIX / Outros / etc).BlocoFormaPagamento.cs(BlocoBase, espelha padrão deBlocoCalculoImposto).Danfe.csentreBlocoCalculoImpostoeBlocoTransportador.Polish visual adicional aplicado durante review:
Duplicata.cs: layout horizontal (3 colunas lado a lado) em vez de empilhado vertical.BlocoDuplicataFatura.cs: 6→3 (retrato) e 7→4 (paisagem) duplicatas por linha para acomodar o novo layout.TabelaProdutosServicos.cs: coluna QUANTI.→QUANT. (label mais curto + width reduzida); NCM/SH width reduzida; espaço economizado vai para DESCRIÇÃO (flex).Danfe.Gerar(): quando blocos topo overflow vertical em paisagem com NF-e rica, antes lançavaHeight is invalid. Agora pula página sem espaço e usa próxima página.OpenSpec change
add-danfe-payment-block(não arquivada — fica para post-merge).Tema 3 — Marca d'água DOCUMENTO CANCELADO (#40)
Achado arquitetural crítico: cancelamento em NF-e modelo 55 é registrado via evento separado (
NFeProcEventotpEvento=110111), NÃO via mudança no<cStat>da nota original. A PR #8 do @mateuszanini (2023) detectava viacStat=101— não funciona na prática.Solução:
DanfeViewModel.IsCancelled(bool, default false) como gatilho primário — consumer NFe.io seta com base no histórico de eventos do banco.CodigoStatusReposta == 101(compat com abordagem da PR Marca d'água para documentos cancelados #8 + cenário raro de XML modificado).DanfePagina.DesenharAvisoCancelamento()reaproveitado da PR Marca d'água para documentos cancelados #8 do @mateuszanini (texto "DOCUMENTO CANCELADO" com mesmo padrão visual do "SEM VALOR FISCAL" de homologação).Base normativa: MOC 7.0 Anexo II §3.10.1 autoriza marca d'água — não obriga nem especifica texto/posição. Texto e estilo são convenção universal de mercado.
OpenSpec change
add-cancelled-watermark(não arquivada — fica para post-merge).Stats
origin/main)fix-danfe-csosn-rendering(archived →specs/danfe-icms-column),add-danfe-payment-block,add-cancelled-watermarkPDFs demo para review
C:\Users\rhfra\source\repos\danfe-payment-validation\:exemplos/— 6 PDFs (3 fixtures × Retrato + Paisagem) sem cancelamentoexemplos-cancelada/— 5 PDFs com marca "DOCUMENTO CANCELADO"Test plan
dotnet build0 errospdftotextIsCancelledadd-danfe-payment-blockeadd-cancelled-watermarkapós mergeReconhecimentos
DesenharAvisoCancelamentoreusado da PR Marca d'água para documentos cancelados #8 (2023). Apenas o gatilho foi substituído por flag explícita, refletindo a realidade arquitetural do cancelamento via evento em NF-e modelo 55.Fixes #38, #39, #40
🤖 Generated with Claude Code