diff --git a/DFe.Testes/DFe.Testes.csproj b/DFe.Testes/DFe.Testes.csproj index e1974af04..225cd4bb7 100644 --- a/DFe.Testes/DFe.Testes.csproj +++ b/DFe.Testes/DFe.Testes.csproj @@ -17,8 +17,10 @@ + + diff --git a/DFe.Testes/Gerais/ChaveFiscalTestes.cs b/DFe.Testes/Gerais/ChaveFiscalTestes.cs new file mode 100644 index 000000000..4d761043b --- /dev/null +++ b/DFe.Testes/Gerais/ChaveFiscalTestes.cs @@ -0,0 +1,261 @@ +using System; +using DFe.Classes.Entidades; +using DFe.Classes.Flags; +using DFe.Utils; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace DFe.Testes.Gerais +{ + /// + /// Testes da chave de acesso do DF-e cobrindo o fluxo antigo (CNPJ 100% numérico) + /// e o novo fluxo com CNPJ alfanumérico da NT Conjunta 2025.001 + /// + [TestClass] + public class ChaveFiscalTestes + { + //Chave real de NFC-e homologação (CNPJ numérico) usada como âncora de regressão do fluxo antigo + private const string ChaveNumericaReal = "23190811820016000167650010000000221100000227"; + + //Vetor ouro da NT: NFe de GO, emitida em 07/2025 pelo CNPJ alfanumérico de teste da Receita PC3D315K000193 + private const string ChaveAlfanumericaOuro = "522507PC3D315K000193550010000000011000000018"; + + #region Fluxo novo - CNPJ alfanumérico + + [TestMethod] + public void ObterChave_ComCnpjAlfanumerico_GeraAChaveDoVetorOuro() + { + // Arrange + var dataEmissao = new DateTimeOffset(2025, 7, 15, 10, 0, 0, TimeSpan.FromHours(-3)); + + // Act + var dados = ChaveFiscal.ObterChave(Estado.GO, dataEmissao, "PC3D315K000193", ModeloDocumento.NFe, 1, 1, 1, 1); + + // Assert + Assert.AreEqual(ChaveAlfanumericaOuro, dados.Chave); + Assert.AreEqual((byte)8, dados.DigitoVerificador); + } + + [TestMethod] + public void ObterChave_ComCnpjAlfanumerico_ChaveTemLayoutCorreto() + { + var dados = ChaveFiscal.ObterChave(Estado.GO, new DateTime(2025, 7, 15), "PC3D315K000193", ModeloDocumento.NFe, 1, 1, 1, 1); + + Assert.AreEqual(44, dados.Chave.Length); + Assert.AreEqual("52", dados.Chave.Substring(0, 2), "cUF"); + Assert.AreEqual("2507", dados.Chave.Substring(2, 4), "AAMM"); + Assert.AreEqual("PC3D315K000193", dados.Chave.Substring(6, 14), "CNPJ"); + Assert.AreEqual("55", dados.Chave.Substring(20, 2), "modelo"); + } + + [TestMethod] + public void ChaveValida_ComChaveAlfanumericaDoVetorOuro_RetornaVerdadeiro() + { + Assert.IsTrue(ChaveFiscal.ChaveValida(ChaveAlfanumericaOuro)); + } + + [TestMethod] + [DataRow("0")] + [DataRow("1")] + [DataRow("5")] + [DataRow("9")] + public void ChaveValida_ComChaveAlfanumericaComDvErrado_RetornaFalso(string dvErrado) + { + var chaveComDvErrado = ChaveAlfanumericaOuro.Substring(0, 43) + dvErrado; + + Assert.IsFalse(ChaveFiscal.ChaveValida(chaveComDvErrado)); + } + + [TestMethod] + public void ObterChave_ComVariosCnpjsAlfanumericos_DvIgualAoDaImplementacaoDeReferenciaDaNT() + { + // Arrange - bases alfanuméricas com DVs calculados pela regra oficial de CNPJ + var basesCnpj = new[] { "12ABC34501DE", "PC3D315K0001", "A1B2C3D4E5F6", "ZZZZZZZZZZZZ", "0000000000AB", "H9J8K7L6M5N4" }; + var ufs = new[] { Estado.GO, Estado.SP, Estado.CE, Estado.RS, Estado.AM, Estado.DF }; + var modelos = new[] { ModeloDocumento.NFe, ModeloDocumento.NFCe }; + + for (var i = 0; i < basesCnpj.Length; i++) + { + var cnpj = basesCnpj[i] + CnpjFiscal.ObterDigitosVerificadores(basesCnpj[i]); + + foreach (var modelo in modelos) + { + // Act + var dados = ChaveFiscal.ObterChave(ufs[i], new DateTime(2026, 7, 1), cnpj, modelo, i + 1, 1000 + i, 1, 77000 + i); + + // Assert + var dvReferencia = CalcularDvReferenciaNt(dados.Chave.Substring(0, 43)); + Assert.AreEqual(dvReferencia, (int)dados.DigitoVerificador, "CNPJ {0}, modelo {1}", cnpj, modelo); + Assert.IsTrue(ChaveFiscal.ChaveValida(dados.Chave), "ChaveValida deveria aceitar {0}", dados.Chave); + } + } + } + + [TestMethod] + public void ObterChave_ComCnpjAlfanumericoMinusculo_LancaArgumentException() + { + Assert.ThrowsException(() => + ChaveFiscal.ObterChave(Estado.GO, new DateTime(2025, 7, 15), "pc3d315k000193", ModeloDocumento.NFe, 1, 1, 1, 1)); + } + + [TestMethod] + [DataRow(':')] + [DataRow(';')] + [DataRow('<')] + [DataRow('=')] + [DataRow('>')] + [DataRow('?')] + [DataRow('@')] + public void ObterChave_ComCaractereEntreDigitosELetras_LancaArgumentException(char caractereInvalido) + { + //os caracteres ASCII 58 a 64 ficam entre '9' e 'A' e produziriam DV errado sem erro se aceitos + var cnpjInvalido = "PC3D315K0001" + caractereInvalido + "3"; + + Assert.ThrowsException(() => + ChaveFiscal.ObterChave(Estado.GO, new DateTime(2025, 7, 15), cnpjInvalido, ModeloDocumento.NFe, 1, 1, 1, 1)); + } + + [TestMethod] + public void ChaveValida_ComLetraMinusculaNaChave_LancaArgumentException() + { + var chaveComMinuscula = "522507pC3D315K000193550010000000011000000018"; + + Assert.ThrowsException(() => ChaveFiscal.ChaveValida(chaveComMinuscula)); + } + + #endregion + + #region Fluxo antigo - CNPJ numérico (regressão) + + [TestMethod] + public void ObterChave_ComCnpjNumerico_GeraChaveRealConhecida() + { + // Arrange - componentes da chave real de homologação (NFC-e do CE, 08/2019) + var dataEmissao = new DateTimeOffset(2019, 8, 10, 12, 0, 0, TimeSpan.FromHours(-3)); + + // Act + var dados = ChaveFiscal.ObterChave(Estado.CE, dataEmissao, "11820016000167", ModeloDocumento.NFCe, 1, 22, 1, 10000022); + + // Assert + Assert.AreEqual(ChaveNumericaReal, dados.Chave); + Assert.AreEqual((byte)7, dados.DigitoVerificador); + } + + [TestMethod] + public void ChaveValida_ComChaveNumericaRealConhecida_RetornaVerdadeiro() + { + Assert.IsTrue(ChaveFiscal.ChaveValida(ChaveNumericaReal)); + } + + [TestMethod] + public void ChaveValida_ComChaveNumericaComDvErrado_RetornaFalso() + { + var chaveComDvErrado = ChaveNumericaReal.Substring(0, 43) + "9"; + + Assert.IsFalse(ChaveFiscal.ChaveValida(chaveComDvErrado)); + } + + [TestMethod] + public void ObterChave_ComVariosCnpjsNumericos_DvIdenticoAoAlgoritmoLegado() + { + // Arrange - regressão: para chave 100% numérica o DV novo deve ser idêntico ao da implementação antiga + var cnpjs = new[] { "11222333000181", "00000000000191", "11444777000161", "99999999999999", "00000000000000", "12345678000195" }; + var ufs = new[] { Estado.SP, Estado.GO, Estado.CE, Estado.RS, Estado.MG, Estado.BA }; + + for (var i = 0; i < cnpjs.Length; i++) + { + foreach (var modelo in new[] { ModeloDocumento.NFe, ModeloDocumento.NFCe }) + { + // Act + var dados = ChaveFiscal.ObterChave(ufs[i], new DateTime(2024, 12, 31), cnpjs[i], modelo, 883 + i, 999999990 + i, 2, 10000000 - i); + + // Assert + var dvLegado = CalcularDvAlgoritmoLegado(dados.Chave.Substring(0, 43)); + Assert.AreEqual(dvLegado, dados.DigitoVerificador.ToString(), "CNPJ {0}, modelo {1}", cnpjs[i], modelo); + Assert.IsTrue(ChaveFiscal.ChaveValida(dados.Chave)); + } + } + } + + [TestMethod] + public void ObterChave_ComCpfDe11Posicoes_PreencheComZerosAEsquerda() + { + // Act + var dados = ChaveFiscal.ObterChave(Estado.SP, new DateTime(2026, 7, 1), "12345678901", ModeloDocumento.NFe, 1, 123, 1, 55555); + + // Assert + Assert.AreEqual(44, dados.Chave.Length); + Assert.AreEqual("00012345678901", dados.Chave.Substring(6, 14)); + Assert.IsTrue(ChaveFiscal.ChaveValida(dados.Chave)); + } + + #endregion + + #region Validação do documento do emitente + + [TestMethod] + [DataRow("PC3D315K0001", DisplayName = "12 posições - base de CNPJ truncada")] + [DataRow("1122233300018", DisplayName = "13 posições")] + [DataRow("112223330001811", DisplayName = "15 posições")] + [DataRow("1234567890", DisplayName = "10 posições")] + public void ObterChave_ComDocumentoDeComprimentoInvalido_LancaArgumentException(string documento) + { + //um CNPJ truncado não pode virar silenciosamente uma chave bem-formada porém errada + Assert.ThrowsException(() => + ChaveFiscal.ObterChave(Estado.GO, new DateTime(2026, 7, 1), documento, ModeloDocumento.NFe, 1, 1, 1, 1)); + } + + [TestMethod] + public void ObterChave_ComDocumentoVazio_LancaArgumentException() + { + Assert.ThrowsException(() => + ChaveFiscal.ObterChave(Estado.GO, new DateTime(2026, 7, 1), "", ModeloDocumento.NFe, 1, 1, 1, 1)); + } + + [TestMethod] + public void ObterChave_ComDocumentoNulo_LancaArgumentException() + { + Assert.ThrowsException(() => + ChaveFiscal.ObterChave(Estado.GO, new DateTime(2026, 7, 1), null, ModeloDocumento.NFe, 1, 1, 1, 1)); + } + + #endregion + + /// + /// Implementação de referência do Anexo II da NT Conjunta 2025.001, portada para C# + /// + private static int CalcularDvReferenciaNt(string chave43) + { + var soma = 0; + var peso = 2; + for (var i = chave43.Length - 1; i >= 0; i--) + { + soma += (chave43[i] - '0') * peso; + peso = peso == 9 ? 2 : peso + 1; + } + var dv = 11 - soma % 11; + return dv >= 10 ? 0 : dv; + } + + /// + /// Algoritmo do DV como era antes da correção (só funciona com dígitos), + /// usado para garantir que chaves numéricas não sofreram regressão + /// + private static string CalcularDvAlgoritmoLegado(string chave43) + { + var soma = 0; + var peso = 2; + for (var i = chave43.Length - 1; i != -1; i--) + { + var ch = Convert.ToInt32(chave43[i].ToString()); + soma += ch * peso; + if (peso < 9) + peso += 1; + else + peso = 2; + } + var mod = soma % 11; + var dv = mod == 0 || mod == 1 ? 0 : 11 - mod; + return dv.ToString(); + } + } +} diff --git a/DFe.Testes/Gerais/CnpjFiscalTestes.cs b/DFe.Testes/Gerais/CnpjFiscalTestes.cs new file mode 100644 index 000000000..a91b7240c --- /dev/null +++ b/DFe.Testes/Gerais/CnpjFiscalTestes.cs @@ -0,0 +1,128 @@ +using System; +using DFe.Utils; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace DFe.Testes.Gerais +{ + /// + /// Testes do cálculo de dígitos verificadores e validação de CNPJ, + /// cobrindo CNPJs numéricos (fluxo antigo) e alfanuméricos (NT Conjunta 2025.001) + /// + [TestClass] + public class CnpjFiscalTestes + { + #region ObterDigitosVerificadores - vetores ouro + + [TestMethod] + [DataRow("112223330001", "81", DisplayName = "Base numérica")] + [DataRow("12ABC34501DE", "35", DisplayName = "Exemplo da NT Conjunta 2025.001")] + [DataRow("PC3D315K0001", "93", DisplayName = "CNPJ de teste da Receita Federal")] + [DataRow("000000000000", "00", DisplayName = "Base zerada - o DV confere, o bloqueio é do Valido")] + public void ObterDigitosVerificadores_ComBaseDe12Posicoes_RetornaDvsEsperados(string baseCnpj, string dvsEsperados) + { + Assert.AreEqual(dvsEsperados, CnpjFiscal.ObterDigitosVerificadores(baseCnpj)); + } + + [TestMethod] + [DataRow("000000000001", "91", DisplayName = "Banco do Brasil")] + [DataRow("114447770001", "61", DisplayName = "CNPJ numérico clássico de testes")] + [DataRow("123456780001", "95", DisplayName = "CNPJ numérico sequencial")] + public void ObterDigitosVerificadores_ComBasesNumericasHistoricas_MantemComportamentoAntigo(string baseCnpj, string dvsEsperados) + { + Assert.AreEqual(dvsEsperados, CnpjFiscal.ObterDigitosVerificadores(baseCnpj)); + } + + #endregion + + #region ObterDigitosVerificadores - entradas inválidas + + [TestMethod] + [DataRow("pc3d315k0001", DisplayName = "Minúsculas")] + [DataRow("12ABC34501D", DisplayName = "11 posições")] + [DataRow("12ABC34501DEF", DisplayName = "13 posições")] + [DataRow("12ABC34501D@", DisplayName = "Caractere @ (ASCII 64)")] + [DataRow("12ABC34501D:", DisplayName = "Caractere : (ASCII 58)")] + [DataRow("12.ABC.345/01", DisplayName = "Com máscara")] + [DataRow("", DisplayName = "Vazia")] + public void ObterDigitosVerificadores_ComBaseInvalida_LancaArgumentException(string baseCnpj) + { + Assert.ThrowsException(() => CnpjFiscal.ObterDigitosVerificadores(baseCnpj)); + } + + [TestMethod] + public void ObterDigitosVerificadores_ComBaseNula_LancaArgumentNullException() + { + Assert.ThrowsException(() => CnpjFiscal.ObterDigitosVerificadores(null)); + } + + #endregion + + #region Valido - CNPJs aceitos + + [TestMethod] + [DataRow("11222333000181", DisplayName = "Numérico do vetor ouro")] + [DataRow("12ABC34501DE35", DisplayName = "Alfanumérico do exemplo da NT")] + [DataRow("PC3D315K000193", DisplayName = "Alfanumérico de teste da Receita")] + [DataRow("00000000000191", DisplayName = "Numérico clássico - Banco do Brasil")] + [DataRow("11444777000161", DisplayName = "Numérico clássico de testes")] + public void Valido_ComCnpjCorreto_RetornaVerdadeiro(string cnpj) + { + Assert.IsTrue(CnpjFiscal.Valido(cnpj)); + } + + #endregion + + #region Valido - CNPJs rejeitados + + [TestMethod] + public void Valido_ComCnpjZerado_RetornaFalso() + { + //o DV do CNPJ zerado confere, mas a NT veda o seu uso + Assert.IsFalse(CnpjFiscal.Valido("00000000000000")); + } + + [TestMethod] + [DataRow("11222333000182", DisplayName = "Numérico com DV errado")] + [DataRow("12ABC34501DE36", DisplayName = "Alfanumérico com segundo DV errado")] + [DataRow("12ABC34501DE45", DisplayName = "Alfanumérico com primeiro DV errado")] + [DataRow("PC3D315K000139", DisplayName = "Alfanumérico com DVs trocados")] + public void Valido_ComDvIncorreto_RetornaFalso(string cnpj) + { + Assert.IsFalse(CnpjFiscal.Valido(cnpj)); + } + + [TestMethod] + [DataRow("pc3d315k000193", DisplayName = "Minúsculas")] + [DataRow("PC3D315K00019", DisplayName = "13 posições")] + [DataRow("PC3D315K0001935", DisplayName = "15 posições")] + [DataRow("12ABC34501DEA5", DisplayName = "Letra na posição dos DVs")] + [DataRow("12ABC34501D:93", DisplayName = "Caractere : (ASCII 58)")] + [DataRow("12ABC34501D@93", DisplayName = "Caractere @ (ASCII 64)")] + [DataRow("11.222.333/0001-81", DisplayName = "Com máscara")] + [DataRow("", DisplayName = "Vazio")] + [DataRow(null, DisplayName = "Nulo")] + public void Valido_ComFormatoInvalido_RetornaFalso(string cnpj) + { + Assert.IsFalse(CnpjFiscal.Valido(cnpj)); + } + + #endregion + + #region Coerência entre ObterDigitosVerificadores e Valido + + [TestMethod] + public void Valido_ComDvsCalculadosPelaPropriaClasse_RetornaVerdadeiro() + { + var bases = new[] { "112223330001", "12ABC34501DE", "PC3D315K0001", "A1B2C3D4E5F6", "ZZZZZZZZZZZZ", "0000000000AB", "999999999999" }; + + foreach (var baseCnpj in bases) + { + var cnpj = baseCnpj + CnpjFiscal.ObterDigitosVerificadores(baseCnpj); + + Assert.IsTrue(CnpjFiscal.Valido(cnpj), "O CNPJ {0} deveria ser válido", cnpj); + } + } + + #endregion + } +} diff --git a/DFe.Testes/Gerais/Code128HibridoTestes.cs b/DFe.Testes/Gerais/Code128HibridoTestes.cs new file mode 100644 index 000000000..9696207ba --- /dev/null +++ b/DFe.Testes/Gerais/Code128HibridoTestes.cs @@ -0,0 +1,252 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using NFe.Danfe.PdfClown.Tools; + +namespace DFe.Testes.Gerais +{ + /// + /// Testes do codificador CODE-128 híbrido (subconjuntos C e A) da seção 6 da NT Conjunta 2025.001, + /// usado no código de barras da chave de acesso do DANFE + /// + [TestClass] + public class Code128HibridoTestes + { + private const byte StartC = 105; + private const byte TrocaParaCodeA = 101; + private const byte TrocaParaCodeC = 99; + private const byte Stop = 106; + + #region Exemplos literais da NT Conjunta 2025.001 - seção 6 + + [TestMethod] + public void ObterSimbolos_ExemploLiteralDaNT_5225AB83() + { + // Arrange - sequência completa dada pela NT: start, dados, DV 30 e stop + var esperado = new byte[] { 105, 52, 25, 101, 33, 34, 99, 83, 30, 106 }; + + // Act + var simbolos = Code128Hibrido.ObterSimbolos("5225AB83"); + + // Assert + CollectionAssert.AreEqual(esperado, simbolos); + } + + [TestMethod] + public void ObterSimbolos_ExemploLiteralDaNT_123A() + { + // Arrange - "12" em Code C, troca para o Code A (101), '3' (19) e 'A' (33) no Code A + var dadosEsperados = new byte[] { 105, 12, 101, 19, 33 }; + + // Act + var simbolos = Code128Hibrido.ObterSimbolos("123A"); + + // Assert + CollectionAssert.AreEqual(dadosEsperados, simbolos.Take(5).ToArray()); + AssertDigitoVerificadorEStop(simbolos); + } + + #endregion + + #region Chave de acesso com CNPJ alfanumérico + + [TestMethod] + public void ObterSimbolos_ChaveDeAcessoAlfanumericaDoVetorOuro_CodificaConformeAsRegrasDaNT() + { + // Arrange + //6 dígitos iniciais em Code C (3 pares); letras e corridas curtas do CNPJ no Code A; + //corrida final de 30 dígitos de volta ao Code C (15 pares) + var dadosEsperados = new byte[] + { + 105, //Start C + 52, 25, 7, //"522507" + 101, //troca para Code A + 48, 35, //'P' 'C' + 19, //'3' (corrida de 1 dígito fica no Code A) + 36, //'D' + 19, 17, 21, //'3' '1' '5' (corrida de 3 dígitos fica no Code A) + 43, //'K' + 99, //volta ao Code C (corrida final par) + 0, 1, 93, 55, 0, 10, 0, 0, 0, 1, 10, 0, 0, 0, 18 + }; + + // Act + var simbolos = Code128Hibrido.ObterSimbolos("522507PC3D315K000193550010000000011000000018"); + + // Assert + CollectionAssert.AreEqual(dadosEsperados, simbolos.Take(dadosEsperados.Length).ToArray()); + AssertDigitoVerificadorEStop(simbolos); + } + + #endregion + + #region Regressão - conteúdo 100% numérico deve gerar exatamente o CODE-128 C puro + + [TestMethod] + public void ObterSimbolos_ComChavesNumericas_IdenticoAoCode128CPuroDaImplementacaoAntiga() + { + var chavesNumericas = new[] + { + "23190811820016000167650010000000221100000227", + "11222333000181112223330001811122233300018111", + "00000000000000000000000000000000000000000000", + "99999999999999999999999999999999999999999999", + "35150300822602000124550010009923461099234656" + }; + + foreach (var chave in chavesNumericas) + { + // Act + var simbolos = Code128Hibrido.ObterSimbolos(chave); + + // Assert + CollectionAssert.AreEqual(CodificarCode128CPuroLegado(chave), simbolos, "Divergência na chave {0}", chave); + } + } + + #endregion + + #region Regras de troca de subconjunto + + [TestMethod] + public void ObterSimbolos_ComLetraInicial_TrocaParaCodeAAposOStartC() + { + var simbolos = Code128Hibrido.ObterSimbolos("A"); + + CollectionAssert.AreEqual(new byte[] { StartC, TrocaParaCodeA, 33 }, simbolos.Take(3).ToArray()); + AssertDigitoVerificadorEStop(simbolos); + } + + [TestMethod] + public void ObterSimbolos_CorridaDe4DigitosEntreLetras_VoltaAoCodeC() + { + //"AB" no Code A, "1234" volta ao Code C (2 pares), "CD" troca novamente para o Code A + var simbolos = Code128Hibrido.ObterSimbolos("AB1234CD"); + + CollectionAssert.AreEqual(new byte[] { StartC, TrocaParaCodeA, 33, 34, TrocaParaCodeC, 12, 34, TrocaParaCodeA, 35, 36 }, simbolos.Take(10).ToArray()); + AssertDigitoVerificadorEStop(simbolos); + } + + [TestMethod] + public void ObterSimbolos_CorridaDe3DigitosEntreLetras_PermaneceNoCodeA() + { + //corrida curta (menos de 4 dígitos) não justifica a troca + var simbolos = Code128Hibrido.ObterSimbolos("AB123C"); + + CollectionAssert.AreEqual(new byte[] { StartC, TrocaParaCodeA, 33, 34, 17, 18, 19, 35 }, simbolos.Take(8).ToArray()); + AssertDigitoVerificadorEStop(simbolos); + } + + [TestMethod] + public void ObterSimbolos_CorridaImparDe5DigitosAntesDeLetra_UltimoDigitoFicaNoCodeA() + { + //"12345" antes de letra: pares "12" "34" no Code C e o dígito ímpar '5' fica no Code A + var simbolos = Code128Hibrido.ObterSimbolos("AB12345C"); + + CollectionAssert.AreEqual(new byte[] { StartC, TrocaParaCodeA, 33, 34, TrocaParaCodeC, 12, 34, TrocaParaCodeA, 21, 35 }, simbolos.Take(10).ToArray()); + AssertDigitoVerificadorEStop(simbolos); + } + + [TestMethod] + public void ObterSimbolos_CorridaFinalParDe2Digitos_VoltaAoCodeC() + { + //final com quantidade par de dígitos volta ao Code C, como no exemplo 5225AB83 da NT + var simbolos = Code128Hibrido.ObterSimbolos("AB12"); + + CollectionAssert.AreEqual(new byte[] { StartC, TrocaParaCodeA, 33, 34, TrocaParaCodeC, 12 }, simbolos.Take(6).ToArray()); + AssertDigitoVerificadorEStop(simbolos); + } + + [TestMethod] + public void ObterSimbolos_CorridaFinalImparDe3Digitos_PermaneceNoCodeA() + { + var simbolos = Code128Hibrido.ObterSimbolos("AB123"); + + CollectionAssert.AreEqual(new byte[] { StartC, TrocaParaCodeA, 33, 34, 17, 18, 19 }, simbolos.Take(7).ToArray()); + AssertDigitoVerificadorEStop(simbolos); + } + + [TestMethod] + public void ObterSimbolos_CorridaFinalImparDe5Digitos_PrimeiroDigitoNoCodeAEDemaisNoCodeC() + { + //corrida final ímpar de 4+ dígitos: um dígito fica no Code A para a parte restante ser par + var simbolos = Code128Hibrido.ObterSimbolos("AB12345"); + + CollectionAssert.AreEqual(new byte[] { StartC, TrocaParaCodeA, 33, 34, 17, TrocaParaCodeC, 23, 45 }, simbolos.Take(8).ToArray()); + AssertDigitoVerificadorEStop(simbolos); + } + + [TestMethod] + public void ObterSimbolos_NumericoImpar_ParesNoCodeCEUltimoDigitoNoCodeA() + { + var simbolos = Code128Hibrido.ObterSimbolos("123"); + + CollectionAssert.AreEqual(new byte[] { StartC, 12, TrocaParaCodeA, 19 }, simbolos.Take(4).ToArray()); + AssertDigitoVerificadorEStop(simbolos); + } + + #endregion + + #region Entradas inválidas + + [TestMethod] + [DataRow("")] + [DataRow(null)] + public void ObterSimbolos_ComCodigoVazioOuNulo_LancaArgumentException(string codigo) + { + Assert.ThrowsException(() => Code128Hibrido.ObterSimbolos(codigo)); + } + + [TestMethod] + [DataRow("12a4", DisplayName = "Letra minúscula")] + [DataRow("12-4", DisplayName = "Hífen")] + [DataRow("12 34", DisplayName = "Espaço")] + [DataRow("12@34", DisplayName = "Caractere @")] + public void ObterSimbolos_ComCaractereForaDeDigitosELetrasMaiusculas_LancaArgumentException(string codigo) + { + Assert.ThrowsException(() => Code128Hibrido.ObterSimbolos(codigo)); + } + + #endregion + + /// + /// Confere as duas últimas posições da sequência: o DV módulo 103 + /// (soma ponderada com o start valendo peso 1) e o símbolo de Stop (106) + /// + private static void AssertDigitoVerificadorEStop(byte[] simbolos) + { + Assert.AreEqual(Stop, simbolos[simbolos.Length - 1], "O último símbolo deve ser o Stop (106)"); + + var soma = (int)simbolos[0]; + for (var i = 1; i < simbolos.Length - 2; i++) + soma += i * simbolos[i]; + + Assert.AreEqual((byte)(soma % 103), simbolos[simbolos.Length - 2], "Dígito verificador módulo 103 incorreto"); + } + + /// + /// Comportamento da implementação antiga (Barcode128C do DANFE PdfClown): Start C, + /// dados aos pares, DV módulo 103 e Stop - referência de regressão para conteúdo numérico + /// + private static byte[] CodificarCode128CPuroLegado(string codigo) + { + var codeBytes = new List { 105 }; + + for (var i = 0; i < codigo.Length; i += 2) + codeBytes.Add(byte.Parse(codigo.Substring(i, 2))); + + var cd = 105; + for (var i = 1; i < codeBytes.Count; i++) + { + cd += i * codeBytes[i]; + cd %= 103; + } + + codeBytes.Add((byte)cd); + codeBytes.Add(106); + + return codeBytes.ToArray(); + } + } +} diff --git a/DFe.Testes/Gerais/DanfePdfClownChaveAlfanumericaTestes.cs b/DFe.Testes/Gerais/DanfePdfClownChaveAlfanumericaTestes.cs new file mode 100644 index 000000000..1599753fa --- /dev/null +++ b/DFe.Testes/Gerais/DanfePdfClownChaveAlfanumericaTestes.cs @@ -0,0 +1,76 @@ +using System; +using System.IO; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using NFe.Danfe.PdfClown; +using NFe.Danfe.PdfClown.Modelo; + +namespace DFe.Testes.Gerais +{ + /// + /// Smoke test do DANFE PdfClown: o PDF deve ser gerado com o código de barras CODE-128 híbrido + /// tanto para chave 100% numérica (fluxo antigo) quanto para chave com CNPJ alfanumérico + /// + [TestClass] + public class DanfePdfClownChaveAlfanumericaTestes + { + private static DanfeViewModel CriarViewModelMinimo(string chaveAcesso) + { + var model = new DanfeViewModel + { + ChaveAcesso = chaveAcesso, + NfNumero = 1, + NfSerie = 1, + TipoNF = 1, + TipoAmbiente = 2, + DataHoraEmissao = new DateTime(2025, 7, 15), + NaturezaOperacao = "VENDA", + ProtocoloAutorizacao = "352250000000001 15/07/2025 10:00:00" + }; + + model.Emitente.RazaoSocial = "EMITENTE DE TESTE LTDA"; + model.Emitente.CnpjCpf = chaveAcesso.Substring(6, 14); + model.Emitente.EnderecoLogadrouro = "RUA DE TESTE"; + model.Emitente.EnderecoNumero = "1"; + model.Emitente.EnderecoBairro = "CENTRO"; + model.Emitente.Municipio = "GOIANIA"; + model.Emitente.EnderecoUf = "GO"; + model.Emitente.EnderecoCep = "74000000"; + + model.Destinatario.RazaoSocial = "DESTINATARIO DE TESTE"; + model.Destinatario.CnpjCpf = "11222333000181"; + model.Destinatario.EnderecoLogadrouro = "AVENIDA DE TESTE"; + model.Destinatario.EnderecoNumero = "2"; + model.Destinatario.EnderecoBairro = "CENTRO"; + model.Destinatario.Municipio = "GOIANIA"; + model.Destinatario.EnderecoUf = "GO"; + model.Destinatario.EnderecoCep = "74000001"; + + return model; + } + + [TestMethod] + [DataRow("522507PC3D315K000193550010000000011000000018", DisplayName = "Chave com CNPJ alfanumérico")] + [DataRow("23190811820016000167650010000000221100000227", DisplayName = "Chave numérica (regressão)")] + public void Gerar_DanfeComChave_ProduzPdf(string chaveAcesso) + { + // Arrange + var model = CriarViewModelMinimo(chaveAcesso); + + using (var danfe = new DanfeDoc(model)) + { + // Act + danfe.Gerar(); + + using (var ms = new MemoryStream()) + { + danfe.Salvar(ms); + + // Assert - PDF gerado, não vazio e com o cabeçalho %PDF + var bytes = ms.ToArray(); + Assert.IsTrue(bytes.Length > 1000, "O PDF gerado está vazio ou pequeno demais ({0} bytes)", bytes.Length); + Assert.AreEqual("%PDF", System.Text.Encoding.ASCII.GetString(bytes, 0, 4)); + } + } + } + } +} diff --git a/DFe.Testes/Gerais/FormatadorDanfeTestes.cs b/DFe.Testes/Gerais/FormatadorDanfeTestes.cs new file mode 100644 index 000000000..f98d60239 --- /dev/null +++ b/DFe.Testes/Gerais/FormatadorDanfeTestes.cs @@ -0,0 +1,77 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using NFe.Danfe.PdfClown.Tools; + +namespace DFe.Testes.Gerais +{ + /// + /// Testes da formatação de CNPJ, CPF e chave de acesso do DANFE (PdfClown), + /// cobrindo documentos numéricos (fluxo antigo) e o CNPJ alfanumérico da NT Conjunta 2025.001 + /// + [TestClass] + public class FormatadorDanfeTestes + { + #region FormatarCnpj + + [TestMethod] + [DataRow("11222333000181", "11.222.333/0001-81", DisplayName = "CNPJ numérico")] + [DataRow("00000000000191", "00.000.000/0001-91", DisplayName = "CNPJ numérico com zeros")] + [DataRow("12ABC34501DE35", "12.ABC.345/01DE-35", DisplayName = "CNPJ alfanumérico da NT")] + [DataRow("PC3D315K000193", "PC.3D3.15K/0001-93", DisplayName = "CNPJ alfanumérico de teste da Receita")] + public void FormatarCnpj_ComCnpjSemMascara_AplicaAMascaraPosicional(string cnpj, string esperado) + { + Assert.AreEqual(esperado, Formatador.FormatarCnpj(cnpj)); + } + + [TestMethod] + [DataRow("11.222.333/0001-81", "11.222.333/0001-81", DisplayName = "Numérico já com máscara")] + [DataRow("12.ABC.345/01DE-35", "12.ABC.345/01DE-35", DisplayName = "Alfanumérico já com máscara")] + public void FormatarCnpj_ComCnpjJaFormatado_MantemAMascara(string cnpj, string esperado) + { + Assert.AreEqual(esperado, Formatador.FormatarCnpj(cnpj)); + } + + [TestMethod] + [DataRow("12abc34501de35", DisplayName = "Minúsculas não formatam")] + [DataRow("12ABC34501DEA5", DisplayName = "Letra nos DVs não formata")] + [DataRow("123", DisplayName = "Curto demais não formata")] + public void FormatarCnpj_ComConteudoForaDoPadrao_DevolveOTextoOriginal(string cnpj) + { + Assert.AreEqual(cnpj, Formatador.FormatarCnpj(cnpj)); + } + + #endregion + + #region FormatarCpfCnpj + + [TestMethod] + [DataRow("12345678901", "123.456.789-01", DisplayName = "CPF continua com máscara de CPF")] + [DataRow("11222333000181", "11.222.333/0001-81", DisplayName = "CNPJ numérico")] + [DataRow("12ABC34501DE35", "12.ABC.345/01DE-35", DisplayName = "CNPJ alfanumérico")] + public void FormatarCpfCnpj_EscolheAMascaraCorreta(string documento, string esperado) + { + Assert.AreEqual(esperado, Formatador.FormatarCpfCnpj(documento)); + } + + #endregion + + #region FormatarChaveAcesso + + [TestMethod] + public void FormatarChaveAcesso_ComChaveAlfanumerica_AgrupaDeQuatroEmQuatro() + { + var formatada = Formatador.FormatarChaveAcesso("522507PC3D315K000193550010000000011000000018"); + + Assert.AreEqual("5225 07PC 3D31 5K00 0193 5500 1000 0000 0110 0000 0018", formatada); + } + + [TestMethod] + public void FormatarChaveAcesso_ComChaveNumerica_AgrupaDeQuatroEmQuatro() + { + var formatada = Formatador.FormatarChaveAcesso("23190811820016000167650010000000221100000227"); + + Assert.AreEqual("2319 0811 8200 1600 0167 6500 1000 0000 2211 0000 0227", formatada); + } + + #endregion + } +} diff --git a/DFe.Utils/ChaveFiscal.cs b/DFe.Utils/ChaveFiscal.cs index f8a200231..e5ef555fa 100644 --- a/DFe.Utils/ChaveFiscal.cs +++ b/DFe.Utils/ChaveFiscal.cs @@ -24,11 +24,24 @@ public class ChaveFiscal /// Retorna um objeto com os dados da chave de acesso public static DadosChaveFiscal ObterChave(Estado ufEmitente, DateTimeOffset dataEmissao, string cnpjEmitente, ModeloDocumento modelo, int serie, long numero, int tipoEmissao, int cNf) { + if (string.IsNullOrEmpty(cnpjEmitente)) + throw new ArgumentException("O CNPJ/CPF do emitente deve ser informado.", "cnpjEmitente"); + + // NT Conjunta 2025.001: o CNPJ pode ser alfanumérico ([A-Z0-9]{12}[0-9]{2}) e ocupa 14 posições na chave. + // Somente CPF (11 posições) é completado com zeros à esquerda; qualquer outro comprimento seria um + // documento truncado e geraria uma chave bem-formada porém errada. + var documentoEmitente = cnpjEmitente.Length == 11 ? cnpjEmitente.PadLeft(14, '0') : cnpjEmitente; + + if (documentoEmitente.Length != 14) + throw new ArgumentException( + string.Format("O documento do emitente deve ter 14 posições (CNPJ) ou 11 posições (CPF); o valor informado \"{0}\" tem {1}.", cnpjEmitente, cnpjEmitente.Length), + "cnpjEmitente"); + var chave = new StringBuilder(); chave.Append(((int)ufEmitente).ToString("D2")) .Append(dataEmissao.ToString("yyMM")) - .Append(cnpjEmitente) + .Append(documentoEmitente) .Append(((int)modelo).ToString("D2")) .Append(serie.ToString("D3")) .Append(numero.ToString("D9")) @@ -57,7 +70,7 @@ private static string ObterDigitoVerificador(string chave) //percorrendo cada caractere da chave da direita para esquerda para fazer os cálculos com o peso for (var i = chave.Length - 1; i != -1; i--) { - var ch = Convert.ToInt32(chave[i].ToString()); + var ch = ObterValorDoCaractere(chave[i]); soma += ch*peso; //sempre que for 9 voltamos o peso a 2 if (peso < 9) @@ -77,6 +90,21 @@ private static string ObterDigitoVerificador(string chave) return dv.ToString(); } + /// + /// Obtém o valor numérico de um caractere da chave para o cálculo do dígito verificador, + /// conforme a NT Conjunta 2025.001: valor = código ASCII - 48 ('0'-'9' => 0 a 9; 'A'-'Z' => 17 a 42) + /// + private static int ObterValorDoCaractere(char caractere) + { + // A chave admite somente dígitos e letras maiúsculas; qualquer outro caractere + // (minúsculas, símbolos, ASCII 58-64) produziria um DV errado sem nenhum erro. + if ((caractere < '0' || caractere > '9') && (caractere < 'A' || caractere > 'Z')) + throw new ArgumentException( + string.Format("Caractere inválido na chave do DF-e: '{0}'. São aceitos somente dígitos (0-9) e letras maiúsculas (A-Z).", caractere)); + + return caractere - '0'; + } + /// /// Informa se a chave de um DF-e é válida /// diff --git a/DFe.Utils/CnpjFiscal.cs b/DFe.Utils/CnpjFiscal.cs new file mode 100644 index 000000000..27bbd6b5a --- /dev/null +++ b/DFe.Utils/CnpjFiscal.cs @@ -0,0 +1,73 @@ +using System; +using System.Linq; +using System.Text.RegularExpressions; + +namespace DFe.Utils +{ + /// + /// Classe com métodos para tratamento do CNPJ, incluindo o CNPJ alfanumérico da NT Conjunta 2025.001 + /// (12 posições [A-Z0-9] + 2 dígitos verificadores numéricos) + /// + public static class CnpjFiscal + { + private static readonly Regex FormatoCnpj = new Regex("^[A-Z0-9]{12}[0-9]{2}$", RegexOptions.Compiled); + private static readonly Regex FormatoBaseCnpj = new Regex("^[A-Z0-9]{12}$", RegexOptions.Compiled); + + /// + /// Calcula os dois dígitos verificadores para as 12 primeiras posições de um CNPJ, + /// conforme a NT Conjunta 2025.001: módulo 11 sobre o valor de cada caractere (código ASCII - 48), + /// com pesos de 2 a 9 aplicados da direita para a esquerda + /// + /// 12 primeiras posições do CNPJ ([A-Z0-9]{12}) + /// Os dois dígitos verificadores, ex.: "93" + public static string ObterDigitosVerificadores(string cnpjBase) + { + if (cnpjBase == null) + throw new ArgumentNullException("cnpjBase"); + + if (!FormatoBaseCnpj.IsMatch(cnpjBase)) + throw new ArgumentException( + string.Format("A base do CNPJ deve ter 12 posições contendo somente dígitos e letras maiúsculas; valor informado: \"{0}\".", cnpjBase), + "cnpjBase"); + + var dv1 = CalcularDigito(cnpjBase); + var dv2 = CalcularDigito(cnpjBase + dv1); + + return string.Concat(dv1, dv2); + } + + /// + /// Informa se um CNPJ de 14 posições é válido: formato [A-Z0-9]{12}[0-9]{2}, + /// dígitos verificadores corretos e diferente do CNPJ zerado (vedado pela NT Conjunta 2025.001) + /// + /// CNPJ com 14 posições, sem máscara + public static bool Valido(string cnpj) + { + if (string.IsNullOrEmpty(cnpj) || !FormatoCnpj.IsMatch(cnpj)) + return false; + + //CNPJ zerado tem dígitos verificadores que conferem, mas é vedado + if (cnpj.All(c => c == '0')) + return false; + + return cnpj.Substring(12, 2) == ObterDigitosVerificadores(cnpj.Substring(0, 12)); + } + + private static int CalcularDigito(string valor) + { + var soma = 0; + var peso = 2; + + //pesos de 2 a 9, aplicados da direita para a esquerda + for (var i = valor.Length - 1; i != -1; i--) + { + //NT Conjunta 2025.001: valor do caractere = código ASCII - 48 ('0'-'9' => 0 a 9; 'A'-'Z' => 17 a 42) + soma += (valor[i] - '0') * peso; + peso = peso == 9 ? 2 : peso + 1; + } + + var resto = soma % 11; + return resto < 2 ? 0 : 11 - resto; + } + } +} diff --git a/NFe.AppTeste/MainWindow.xaml.cs b/NFe.AppTeste/MainWindow.xaml.cs index 8c8204798..00dcd25e4 100644 --- a/NFe.AppTeste/MainWindow.xaml.cs +++ b/NFe.AppTeste/MainWindow.xaml.cs @@ -1973,14 +1973,11 @@ private void BtnAdminCsc_Click(object sender, RoutedEventArgs e) throw new Exception("O Identificador do tipo de operação deve ser um número inteiro entre 1 e 3!"); //raizCnpj - var raizCnpj = Funcoes.InpuBox(this, "Administração do CSC", "Raiz do CNPJ do contribuinte que está efetuando a consulta (oito primeiros dígitos do CNPJ):"); + var raizCnpj = Funcoes.InpuBox(this, "Administração do CSC", "Raiz do CNPJ do contribuinte que está efetuando a consulta (oito primeiras posições do CNPJ):"); if (string.IsNullOrEmpty(raizCnpj)) throw new Exception("A Raiz do CNPJ do contribuinte deve ser informada!"); - long l; - var longo = long.TryParse(raizCnpj, out l); - if (!longo) - throw new Exception("A Raiz do CNPJ do contribuinte deve conter apenas números!"); - if (raizCnpj.Length != 8) - throw new Exception("A Raiz do CNPJ do contribuinte deve conter 8 caracteres!"); + //NT Conjunta 2025.001: o CNPJ pode ser alfanumérico nas 12 primeiras posições + if (!System.Text.RegularExpressions.Regex.IsMatch(raizCnpj, "^[0-9A-Z]{8}$")) + throw new Exception("A Raiz do CNPJ do contribuinte deve conter 8 caracteres, apenas números e letras maiúsculas!"); var idCsc = ""; var codigoCsc = ""; diff --git a/NFe.AppTeste/Schemas-sem-vIBS/eventoEPEC_v1.00.xsd b/NFe.AppTeste/Schemas-sem-vIBS/eventoEPEC_v1.00.xsd index 3562cc9eb..5524009ff 100644 --- a/NFe.AppTeste/Schemas-sem-vIBS/eventoEPEC_v1.00.xsd +++ b/NFe.AppTeste/Schemas-sem-vIBS/eventoEPEC_v1.00.xsd @@ -213,7 +213,7 @@ - + diff --git a/NFe.AppTeste/Schemas-sem-vIBS/leiauteCCe_v1.00.xsd b/NFe.AppTeste/Schemas-sem-vIBS/leiauteCCe_v1.00.xsd index 4cd45988d..beff945c0 100644 --- a/NFe.AppTeste/Schemas-sem-vIBS/leiauteCCe_v1.00.xsd +++ b/NFe.AppTeste/Schemas-sem-vIBS/leiauteCCe_v1.00.xsd @@ -151,7 +151,7 @@ - + diff --git a/NFe.AppTeste/Schemas-sem-vIBS/leiauteConfRecebto_v1.00.xsd b/NFe.AppTeste/Schemas-sem-vIBS/leiauteConfRecebto_v1.00.xsd index 44581e874..3a610a676 100644 --- a/NFe.AppTeste/Schemas-sem-vIBS/leiauteConfRecebto_v1.00.xsd +++ b/NFe.AppTeste/Schemas-sem-vIBS/leiauteConfRecebto_v1.00.xsd @@ -138,7 +138,7 @@ - + diff --git a/NFe.AppTeste/Schemas-sem-vIBS/leiauteConsSitNFe_v4.00.xsd b/NFe.AppTeste/Schemas-sem-vIBS/leiauteConsSitNFe_v4.00.xsd index a56621542..8672a6761 100644 --- a/NFe.AppTeste/Schemas-sem-vIBS/leiauteConsSitNFe_v4.00.xsd +++ b/NFe.AppTeste/Schemas-sem-vIBS/leiauteConsSitNFe_v4.00.xsd @@ -316,7 +316,7 @@ - + diff --git a/NFe.AppTeste/Schemas-sem-vIBS/leiauteEPEC_v1.00.xsd b/NFe.AppTeste/Schemas-sem-vIBS/leiauteEPEC_v1.00.xsd index 132be27b9..746689377 100644 --- a/NFe.AppTeste/Schemas-sem-vIBS/leiauteEPEC_v1.00.xsd +++ b/NFe.AppTeste/Schemas-sem-vIBS/leiauteEPEC_v1.00.xsd @@ -139,7 +139,7 @@ - + diff --git a/NFe.AppTeste/Schemas-sem-vIBS/leiauteEventoAtorInteressado_v1.00.xsd b/NFe.AppTeste/Schemas-sem-vIBS/leiauteEventoAtorInteressado_v1.00.xsd index 6eeb536ed..8b2ccfa2d 100644 --- a/NFe.AppTeste/Schemas-sem-vIBS/leiauteEventoAtorInteressado_v1.00.xsd +++ b/NFe.AppTeste/Schemas-sem-vIBS/leiauteEventoAtorInteressado_v1.00.xsd @@ -173,7 +173,7 @@ Outros valores: 1=Empresa Emitente, 2=Empresa destinatária; 3=Empresa; 5=Fisco; - + diff --git a/NFe.AppTeste/Schemas-sem-vIBS/leiauteEventoCancEConf_v1.00.xsd b/NFe.AppTeste/Schemas-sem-vIBS/leiauteEventoCancEConf_v1.00.xsd index 710d25def..47e963bca 100644 --- a/NFe.AppTeste/Schemas-sem-vIBS/leiauteEventoCancEConf_v1.00.xsd +++ b/NFe.AppTeste/Schemas-sem-vIBS/leiauteEventoCancEConf_v1.00.xsd @@ -91,7 +91,7 @@ - + diff --git a/NFe.AppTeste/Schemas-sem-vIBS/leiauteEventoCancEntregaNFe_v1.00.xsd b/NFe.AppTeste/Schemas-sem-vIBS/leiauteEventoCancEntregaNFe_v1.00.xsd index 17a5396c5..c3b05d07a 100644 --- a/NFe.AppTeste/Schemas-sem-vIBS/leiauteEventoCancEntregaNFe_v1.00.xsd +++ b/NFe.AppTeste/Schemas-sem-vIBS/leiauteEventoCancEntregaNFe_v1.00.xsd @@ -91,7 +91,7 @@ - + diff --git a/NFe.AppTeste/Schemas-sem-vIBS/leiauteEventoCancInsucessoNFe_v1.00.xsd b/NFe.AppTeste/Schemas-sem-vIBS/leiauteEventoCancInsucessoNFe_v1.00.xsd index 0bde4345e..cfbf265f3 100644 --- a/NFe.AppTeste/Schemas-sem-vIBS/leiauteEventoCancInsucessoNFe_v1.00.xsd +++ b/NFe.AppTeste/Schemas-sem-vIBS/leiauteEventoCancInsucessoNFe_v1.00.xsd @@ -91,7 +91,7 @@ - + diff --git a/NFe.AppTeste/Schemas-sem-vIBS/leiauteEventoCancNFe_v1.00.xsd b/NFe.AppTeste/Schemas-sem-vIBS/leiauteEventoCancNFe_v1.00.xsd index bf0d09a8b..719fdeaab 100644 --- a/NFe.AppTeste/Schemas-sem-vIBS/leiauteEventoCancNFe_v1.00.xsd +++ b/NFe.AppTeste/Schemas-sem-vIBS/leiauteEventoCancNFe_v1.00.xsd @@ -127,7 +127,7 @@ - + diff --git a/NFe.AppTeste/Schemas-sem-vIBS/leiauteEventoCancSubst_v1.00.xsd b/NFe.AppTeste/Schemas-sem-vIBS/leiauteEventoCancSubst_v1.00.xsd index ce7b2c3db..b41416032 100644 --- a/NFe.AppTeste/Schemas-sem-vIBS/leiauteEventoCancSubst_v1.00.xsd +++ b/NFe.AppTeste/Schemas-sem-vIBS/leiauteEventoCancSubst_v1.00.xsd @@ -155,7 +155,7 @@ - + diff --git a/NFe.AppTeste/Schemas-sem-vIBS/leiauteEventoEConf_v1.00.xsd b/NFe.AppTeste/Schemas-sem-vIBS/leiauteEventoEConf_v1.00.xsd index fe9d449d5..530897c85 100644 --- a/NFe.AppTeste/Schemas-sem-vIBS/leiauteEventoEConf_v1.00.xsd +++ b/NFe.AppTeste/Schemas-sem-vIBS/leiauteEventoEConf_v1.00.xsd @@ -91,7 +91,7 @@ - + diff --git a/NFe.AppTeste/Schemas-sem-vIBS/leiauteEventoEntregaNFe_v1.00.xsd b/NFe.AppTeste/Schemas-sem-vIBS/leiauteEventoEntregaNFe_v1.00.xsd index a7d70d798..be38d6aa2 100644 --- a/NFe.AppTeste/Schemas-sem-vIBS/leiauteEventoEntregaNFe_v1.00.xsd +++ b/NFe.AppTeste/Schemas-sem-vIBS/leiauteEventoEntregaNFe_v1.00.xsd @@ -90,7 +90,7 @@ - + diff --git a/NFe.AppTeste/Schemas-sem-vIBS/leiauteEventoInsucessoNFe_v1.00.xsd b/NFe.AppTeste/Schemas-sem-vIBS/leiauteEventoInsucessoNFe_v1.00.xsd index 9ab61fe47..3b7915676 100644 --- a/NFe.AppTeste/Schemas-sem-vIBS/leiauteEventoInsucessoNFe_v1.00.xsd +++ b/NFe.AppTeste/Schemas-sem-vIBS/leiauteEventoInsucessoNFe_v1.00.xsd @@ -91,7 +91,7 @@ - + diff --git a/NFe.AppTeste/Schemas-sem-vIBS/leiauteEvento_v1.00.xsd b/NFe.AppTeste/Schemas-sem-vIBS/leiauteEvento_v1.00.xsd index f1e023fed..e02813f96 100644 --- a/NFe.AppTeste/Schemas-sem-vIBS/leiauteEvento_v1.00.xsd +++ b/NFe.AppTeste/Schemas-sem-vIBS/leiauteEvento_v1.00.xsd @@ -99,7 +99,7 @@ - + diff --git a/NFe.AppTeste/Schemas-sem-vIBS/leiauteInutNFe_v4.00.xsd b/NFe.AppTeste/Schemas-sem-vIBS/leiauteInutNFe_v4.00.xsd index 819597c1a..886e05d0f 100644 --- a/NFe.AppTeste/Schemas-sem-vIBS/leiauteInutNFe_v4.00.xsd +++ b/NFe.AppTeste/Schemas-sem-vIBS/leiauteInutNFe_v4.00.xsd @@ -76,7 +76,7 @@ - + diff --git a/NFe.AppTeste/Schemas-sem-vIBS/tiposBasico_v1.03.xsd b/NFe.AppTeste/Schemas-sem-vIBS/tiposBasico_v1.03.xsd index c967c195b..06f4c6147 100644 --- a/NFe.AppTeste/Schemas-sem-vIBS/tiposBasico_v1.03.xsd +++ b/NFe.AppTeste/Schemas-sem-vIBS/tiposBasico_v1.03.xsd @@ -58,7 +58,7 @@ - + @@ -94,7 +94,7 @@ - + @@ -103,7 +103,7 @@ - + @@ -113,7 +113,7 @@ - + diff --git a/NFe.AppTeste/Schemas-sem-vIBS/tiposBasico_v1.03_OPENSSL.xsd b/NFe.AppTeste/Schemas-sem-vIBS/tiposBasico_v1.03_OPENSSL.xsd index 851a822ba..142dd55b2 100644 --- a/NFe.AppTeste/Schemas-sem-vIBS/tiposBasico_v1.03_OPENSSL.xsd +++ b/NFe.AppTeste/Schemas-sem-vIBS/tiposBasico_v1.03_OPENSSL.xsd @@ -59,7 +59,7 @@ - + @@ -95,7 +95,7 @@ - + @@ -104,7 +104,7 @@ - + @@ -114,7 +114,7 @@ - + diff --git a/NFe.AppTeste/Schemas-sem-vIBS/tiposBasico_v4.00.xsd b/NFe.AppTeste/Schemas-sem-vIBS/tiposBasico_v4.00.xsd index dfabc3366..4ead3c532 100644 --- a/NFe.AppTeste/Schemas-sem-vIBS/tiposBasico_v4.00.xsd +++ b/NFe.AppTeste/Schemas-sem-vIBS/tiposBasico_v4.00.xsd @@ -52,7 +52,7 @@ - + @@ -92,7 +92,7 @@ - + @@ -102,7 +102,7 @@ - + @@ -112,7 +112,7 @@ - + diff --git a/NFe.AppTeste/Schemas-sem-vIBS/tiposDistDFe_v1.01.xsd b/NFe.AppTeste/Schemas-sem-vIBS/tiposDistDFe_v1.01.xsd index 8b0d8a135..1aa75ec7d 100644 --- a/NFe.AppTeste/Schemas-sem-vIBS/tiposDistDFe_v1.01.xsd +++ b/NFe.AppTeste/Schemas-sem-vIBS/tiposDistDFe_v1.01.xsd @@ -116,7 +116,7 @@ - + @@ -173,7 +173,7 @@ - + diff --git a/NFe.AppTeste/Schemas/DFeTiposBasicos_v1.00.xsd b/NFe.AppTeste/Schemas/DFeTiposBasicos_v1.00.xsd index a5051dd02..d204e4061 100644 --- a/NFe.AppTeste/Schemas/DFeTiposBasicos_v1.00.xsd +++ b/NFe.AppTeste/Schemas/DFeTiposBasicos_v1.00.xsd @@ -1,6 +1,16 @@ + + + Tipo Chave de Documento Fiscal Eletrônico + + + + + + + Tipo string genérico @@ -55,6 +65,24 @@ + + + Tipo CNPJ Base + + + + + + + + + Tipo CNPJ + + + + + + Tipo Decimal com 15 dígitos, sendo 13 de corpo e 2 decimais @@ -81,6 +109,23 @@ + + + + + + + Tipo de Receita Bruta do SN + + + + + + + + + + @@ -93,6 +138,8 @@ + + @@ -115,6 +162,14 @@ + + + Ano e mês referência do período de apuração (AAAA-MM) + + + + + Grupo de informações da Tributação da NFCom @@ -163,6 +218,26 @@ + + + Grupo de informações da Tributação da NFGas + + + + + Código Situação Tributária do IBS/CBS + + + + + + + + Informado conforme indicador no cClassTrib + + + + Grupo de informações da Tributação da NFAg @@ -240,7 +315,7 @@ - + @@ -262,7 +337,7 @@ - + Informar essa opção da Choice para Monofasia (CST 620) @@ -298,33 +373,6 @@ - - - Grupo de informações da Tributação da NFGas - - - - - Código Situação Tributária do IBS/CBS - - - - - - - - - Informar essa opção da Choice para Monofasia - - - - - - Informado conforme indicador no cClassTrib - - - - Grupo de informações do Imposto Seletivo @@ -347,7 +395,7 @@ Alíquota do Imposto Seletivo (percentual) - + Alíquota do Imposto Seletivo (por valor) @@ -949,6 +997,152 @@ Grupo de campos da redução de aliquota + + + Grupo de operações em áreas incentivadas (ALC/ZFM) - CBS (alíquota zero) + Grupo de informações para identificação de operações em áreas incentivadas (ALC/ZFM) com alíquota zero da CBS, conforme arts. 451 e 466 da LC 214/2025, quando fornecedor e destinatário estiverem nessas áreas, distinguindo a existência de processo aprovado na Suframa. + + + + + Valor da CBS + + + + + + + + Grupo de informações da Tributação Regular. Informar como seria a tributação caso não cumprida a condição resolutória/suspensiva. Exemplo 1: Art. 442, §4. Operações com ZFM e ALC. Exemplo 2: Operações com suspensão do tributo. + + + + + Grupo de informações da composição do valor do IBS e da CBS em compras governamental + + + + + + + Tipo CBS IBS Completo NFe + + + + IBS / CBS + + + + Valor do BC + + + + + + Grupo de informações do IBS na UF + + + + + + Aliquota do IBS de competência das UF (em percentual) + + + + + Grupo de campos do Diferimento + + + + + Grupo de Informações da devolução de tributos + + + + + Grupo de campos da redução de aliquota + + + + + Valor do IBS de competência das UF + + + + + + + + Grupo de Informações do IBS no Município + + + + + + Aliquota do IBS Municipal (em percentual) + + + + + Grupo de campos do Diferimento + + + + + Grupo de Informações da devolução de tributos + + + + + Grupo de campos da redução de aliquota + + + + + Valor do IBS Municipal + + + + + + + + Valor do IBS + + + + + + Grupo de Tributação da CBS + + + + + + Aliquota da CBS (em percentual) + + + + + Grupo de campos do Diferimento + + + + + Grupo de Informações da devolução de tributos + + + + + Grupo de campos da redução de aliquota + + + + + Grupo de operações em áreas incentivadas (ALC/ZFM) - CBS (alíquota zero) + Grupo de informações para identificação de operações em áreas incentivadas (ALC/ZFM) com alíquota zero da CBS, conforme arts. 451 e 466 da LC 214/2025, quando fornecedor e destinatário estiverem nessas áreas, distinguindo a existência de processo aprovado na Suframa. + + Valor da CBS @@ -1032,10 +1226,14 @@ Tipo Devolução Tributo + + + Percentual de devolução do tributo, conforme LC 214/25 art. 118. + + - Valor do tributo devolvido. No fornecimento de energia elétrica, água, esgoto e -gás natural e em outras hipóteses definidas no regulamento + Valor do tributo devolvido ("cashback" de desconto na própria Nota Fiscal / Fatura) @@ -1131,7 +1329,9 @@ gás natural e em outras hipóteses definidas no regulamento 1=União 2=Estados 3=Distrito Federal -4=Municípios +4=Municípios +5=Consórcio Público +6=Comitê Gestor do IBS @@ -1139,6 +1339,29 @@ gás natural e em outras hipóteses definidas no regulamento Percentual de redução de aliquota em compra governamental + + + Tipo da operação com ente governamental: +1 – Fornecimento com pagamento posterior; + +2 - Recebimento do pagamento com fornecimento já realizado; + +3 – Fornecimento com pagamento já realizado; + +4 – Recebimento do pagamento com fornecimento posterior; + + + + + Chave de acesso do documento fiscal anterior. + +Deverá ser informado para tpOperGov 2 e 3 e vedado para os tipos 1 e 4. + +No caso do tpOperGov 2 aceitará apenas uma chave referenciada, no tipo 3 poderá aceitar múltiplas chaves + +Obs: a chave de acesso deverá ser de um emitente com o mesmo CNPJ base + + @@ -1153,19 +1376,49 @@ gás natural e em outras hipóteses definidas no regulamento 1=União 2=Estados 3=Distrito Federal -4=Municípios +4=Municípios +5=Consórcio Público +6=Comitê Gestor do IBS - Percentual de redução de aliquota em compra governamental + Percentual de redução de alíquota em compra governamental Tipo da operação com ente governamental: -1 - Fornecimento -2 - Recebimento do Pagamento +1 – Fornecimento com pagamento posterior; +2 - Recebimento do pagamento com fornecimento já realizado; +3 – Fornecimento com pagamento já realizado; +4 – Recebimento do pagamento com fornecimento posterior; + + + + + Chave de acesso do documento fiscal anterior. + +Deverá ser informado para tpOperGov 2 e 3 e vedado para os tipos 1 e 4. + +No caso do tpOperGov 2 aceitará apenas uma chave referenciada, no tipo 3 poderá aceitar múltiplas chaves + +Obs: a chave de acesso deverá ser de um emitente com o mesmo CNPJ base + + + + + + + Tipo Pagamento que ocorre em DFe emitdo anteriormente + Informado para abater as parcelas de antecipação de pagamento, conforme art. 10 §4 + + + + + Chave de acesso do documento fiscal de antecipação de pagamento + +Obs: esse DFe deverá ter o indAntecipacaoPgto marcado no grupo ide @@ -1187,6 +1440,68 @@ gás natural e em outras hipóteses definidas no regulamento + + + Tipo Operações em areas incentivadas com CBS Zero + + + + + Percentual efetivo sem a redução + Alíquota efetiva de referência da CBS aplicável à operação fora de áreas ou regimes incentivados. + + + + + Valor efetivo sem a redução + Valor da CBS calculado para a operação fora de áreas ou regimes incentivado + + + + + + + Tipo Operações em áreas incentivadas (ALC/ZFM) - CBS (alíquota zero) + + + + + Tipo de aplicação da alíquota zero da CBS. + + + + + + + + + + + + Número do processo na Suframa para o item +comercializado. + + + + + + + + + + + Percentual efetivo sem a redução + Alíquota efetiva de referência da CBS aplicável à operação fora de áreas ou regimes incentivados. + + + + + Valor efetivo sem a redução + Valor da CBS calculado para a operação fora de áreas ou regimes incentivado + + + + Tipo Estorno de Crédito @@ -1204,14 +1519,6 @@ gás natural e em outras hipóteses definidas no regulamento - - - Ano e mês referência do período de apuração (AAAA-MM) - - - - - Tipo Ajuste de Competência diff --git a/NFe.AppTeste/Schemas/distDFeInt_v1.01.xsd b/NFe.AppTeste/Schemas/distDFeInt_v1.01.xsd index d66807e9c..89a68ae75 100644 --- a/NFe.AppTeste/Schemas/distDFeInt_v1.01.xsd +++ b/NFe.AppTeste/Schemas/distDFeInt_v1.01.xsd @@ -1,84 +1,83 @@ - - - - - - Schema de pedido de distribuição de DF-e de interesse - - - - - - + + + + + Schema de pedido de distribuição de DF-e de interesse + + + + + + Identificação do Ambiente: 1 - Produção 2 - Homologação - - - - - - Código da UF do Autor - - - - - - CNPJ do interessado no DF-e - - - - - CPF do interessado no DF-e - - - - - - - Grupo para distribuir DF-e de interesse - - - - - - Último NSU recebido pelo ator. Caso seja informado com zero, ou com um NSU muito antigo, a consulta retornará unicamente as informações resumidas e documentos fiscais eletrônicos que tenham sido recepcionados pelo Ambiente Nacional nos últimos 3 meses. - - - - - - - - Grupo para consultar um DF-e a partir de um NSU específico - - - - - - Número Sequencial Único. Geralmente esta consulta será utilizada quando identificado pelo interessado um NSU faltante. O Web Service retornará o documento ou informará que o NSU não existe no Ambiente Nacional. Assim, esta consulta fechará a lacuna do NSU identificado como faltante. - - - - - - - - Grupo para consultar uma NF-e a partir da chave de acesso - - - - - - Chave de acesso da NF-e a ser consultada - - - - - - - - - - - \ No newline at end of file + + + + + + Código da UF do Autor + + + + + + CNPJ do interessado no DF-e + + + + + CPF do interessado no DF-e + + + + + + + Grupo para distribuir DF-e de interesse + + + + + + Último NSU recebido pelo ator. Caso seja informado com zero, ou com um NSU muito antigo, a consulta retornará unicamente as informações resumidas e documentos fiscais eletrônicos que tenham sido recepcionados pelo Ambiente Nacional nos últimos 3 meses. + + + + + + + + Grupo para consultar um DF-e a partir de um NSU específico + + + + + + Número Sequencial Único. Geralmente esta consulta será utilizada quando identificado pelo interessado um NSU faltante. O Web Service retornará o documento ou informará que o NSU não existe no Ambiente Nacional. Assim, esta consulta fechará a lacuna do NSU identificado como faltante. + + + + + + + + Grupo para consultar uma NF-e a partir da chave de acesso + + + + + + Chave de acesso da NF-e a ser consultada + + + + + + + + + + + diff --git a/NFe.AppTeste/Schemas/eventoEPEC_v1.00.xsd b/NFe.AppTeste/Schemas/eventoEPEC_v1.00.xsd index 3562cc9eb..5524009ff 100644 --- a/NFe.AppTeste/Schemas/eventoEPEC_v1.00.xsd +++ b/NFe.AppTeste/Schemas/eventoEPEC_v1.00.xsd @@ -213,7 +213,7 @@ - + diff --git a/NFe.AppTeste/Schemas/leiauteCCe_v1.00.xsd b/NFe.AppTeste/Schemas/leiauteCCe_v1.00.xsd index 4cd45988d..beff945c0 100644 --- a/NFe.AppTeste/Schemas/leiauteCCe_v1.00.xsd +++ b/NFe.AppTeste/Schemas/leiauteCCe_v1.00.xsd @@ -151,7 +151,7 @@ - + diff --git a/NFe.AppTeste/Schemas/leiauteConfRecebto_v1.00.xsd b/NFe.AppTeste/Schemas/leiauteConfRecebto_v1.00.xsd index 44581e874..3a610a676 100644 --- a/NFe.AppTeste/Schemas/leiauteConfRecebto_v1.00.xsd +++ b/NFe.AppTeste/Schemas/leiauteConfRecebto_v1.00.xsd @@ -138,7 +138,7 @@ - + diff --git a/NFe.AppTeste/Schemas/leiauteEPEC_v1.00.xsd b/NFe.AppTeste/Schemas/leiauteEPEC_v1.00.xsd index 132be27b9..746689377 100644 --- a/NFe.AppTeste/Schemas/leiauteEPEC_v1.00.xsd +++ b/NFe.AppTeste/Schemas/leiauteEPEC_v1.00.xsd @@ -139,7 +139,7 @@ - + diff --git a/NFe.AppTeste/Schemas/leiauteEventoAtorInteressado_v1.00.xsd b/NFe.AppTeste/Schemas/leiauteEventoAtorInteressado_v1.00.xsd index 6eeb536ed..8b2ccfa2d 100644 --- a/NFe.AppTeste/Schemas/leiauteEventoAtorInteressado_v1.00.xsd +++ b/NFe.AppTeste/Schemas/leiauteEventoAtorInteressado_v1.00.xsd @@ -173,7 +173,7 @@ Outros valores: 1=Empresa Emitente, 2=Empresa destinatária; 3=Empresa; 5=Fisco; - + diff --git a/NFe.AppTeste/Schemas/leiauteEventoCancEConf_v1.00.xsd b/NFe.AppTeste/Schemas/leiauteEventoCancEConf_v1.00.xsd index 710d25def..47e963bca 100644 --- a/NFe.AppTeste/Schemas/leiauteEventoCancEConf_v1.00.xsd +++ b/NFe.AppTeste/Schemas/leiauteEventoCancEConf_v1.00.xsd @@ -91,7 +91,7 @@ - + diff --git a/NFe.AppTeste/Schemas/leiauteEventoCancEntregaNFe_v1.00.xsd b/NFe.AppTeste/Schemas/leiauteEventoCancEntregaNFe_v1.00.xsd index 17a5396c5..c3b05d07a 100644 --- a/NFe.AppTeste/Schemas/leiauteEventoCancEntregaNFe_v1.00.xsd +++ b/NFe.AppTeste/Schemas/leiauteEventoCancEntregaNFe_v1.00.xsd @@ -91,7 +91,7 @@ - + diff --git a/NFe.AppTeste/Schemas/leiauteEventoCancInsucessoNFe_v1.00.xsd b/NFe.AppTeste/Schemas/leiauteEventoCancInsucessoNFe_v1.00.xsd index 0bde4345e..cfbf265f3 100644 --- a/NFe.AppTeste/Schemas/leiauteEventoCancInsucessoNFe_v1.00.xsd +++ b/NFe.AppTeste/Schemas/leiauteEventoCancInsucessoNFe_v1.00.xsd @@ -91,7 +91,7 @@ - + diff --git a/NFe.AppTeste/Schemas/leiauteEventoCancNFe_v1.00.xsd b/NFe.AppTeste/Schemas/leiauteEventoCancNFe_v1.00.xsd index bf0d09a8b..719fdeaab 100644 --- a/NFe.AppTeste/Schemas/leiauteEventoCancNFe_v1.00.xsd +++ b/NFe.AppTeste/Schemas/leiauteEventoCancNFe_v1.00.xsd @@ -127,7 +127,7 @@ - + diff --git a/NFe.AppTeste/Schemas/leiauteEventoCancSubst_v1.00.xsd b/NFe.AppTeste/Schemas/leiauteEventoCancSubst_v1.00.xsd index ce7b2c3db..b41416032 100644 --- a/NFe.AppTeste/Schemas/leiauteEventoCancSubst_v1.00.xsd +++ b/NFe.AppTeste/Schemas/leiauteEventoCancSubst_v1.00.xsd @@ -155,7 +155,7 @@ - + diff --git a/NFe.AppTeste/Schemas/leiauteEventoEConf_v1.00.xsd b/NFe.AppTeste/Schemas/leiauteEventoEConf_v1.00.xsd index fe9d449d5..530897c85 100644 --- a/NFe.AppTeste/Schemas/leiauteEventoEConf_v1.00.xsd +++ b/NFe.AppTeste/Schemas/leiauteEventoEConf_v1.00.xsd @@ -91,7 +91,7 @@ - + diff --git a/NFe.AppTeste/Schemas/leiauteEventoEntregaNFe_v1.00.xsd b/NFe.AppTeste/Schemas/leiauteEventoEntregaNFe_v1.00.xsd index a7d70d798..be38d6aa2 100644 --- a/NFe.AppTeste/Schemas/leiauteEventoEntregaNFe_v1.00.xsd +++ b/NFe.AppTeste/Schemas/leiauteEventoEntregaNFe_v1.00.xsd @@ -90,7 +90,7 @@ - + diff --git a/NFe.AppTeste/Schemas/leiauteEventoInsucessoNFe_v1.00.xsd b/NFe.AppTeste/Schemas/leiauteEventoInsucessoNFe_v1.00.xsd index 9ab61fe47..3b7915676 100644 --- a/NFe.AppTeste/Schemas/leiauteEventoInsucessoNFe_v1.00.xsd +++ b/NFe.AppTeste/Schemas/leiauteEventoInsucessoNFe_v1.00.xsd @@ -91,7 +91,7 @@ - + diff --git a/NFe.AppTeste/Schemas/leiauteEvento_v1.00.xsd b/NFe.AppTeste/Schemas/leiauteEvento_v1.00.xsd index f1e023fed..1f23ffac0 100644 --- a/NFe.AppTeste/Schemas/leiauteEvento_v1.00.xsd +++ b/NFe.AppTeste/Schemas/leiauteEvento_v1.00.xsd @@ -1,4 +1,4 @@ - + @@ -91,6 +91,40 @@ + + + Grupo de Informação do Provedor de Assinatura e Autorização + + + + + + CNPJ do Provedor de Assinatura e Autorização + + + + + Assinatura RSA do Emitente para DFe gerados por PAA + + + + + + Assinatura digital padrão RSA + Converter o atributo Id do DFe para array de bytes e assinar com a chave privada do RSA com algoritmo SHA1 gerando um valor no formato base64. + + + + + Chave Publica no padrão XML RSA Key + + + + + + + + @@ -99,7 +133,7 @@ - + diff --git a/NFe.AppTeste/Schemas/leiauteNFe_v4.00.xsd b/NFe.AppTeste/Schemas/leiauteNFe_v4.00.xsd index 9409ba120..e27ed6a87 100644 --- a/NFe.AppTeste/Schemas/leiauteNFe_v4.00.xsd +++ b/NFe.AppTeste/Schemas/leiauteNFe_v4.00.xsd @@ -1,23 +1,5 @@ - - - - - - - - - - - - - - - - - - @@ -73,10 +55,7 @@ - Série do Documento Fiscal -série normal 0-889 -Avulsa Fisco 890-899 -SCAN 900-999 + Série do Documento Fiscal @@ -132,13 +111,19 @@ SCAN 900-999 Informar o município de ocorrência do fato gerador do fato gerador do IBS / CBS. -Campo preenchido somente quando “indPres = 5 (Operação presencial, fora do estabelecimento) ”, e não tiver endereço do destinatário (Grupo: E05) ou local de entrega (Grupo: G01). +Campo preenchido somente quando "indPres = 5 (Operação presencial, fora do estabelecimento)", e não estiver preenchido o endereço do destinatário (grupo: E05) nem o local de entrega (grupo: G01). - Formato de impressão do DANFE (0-sem DANFE;1-DANFe Retrato; 2-DANFe Paisagem;3-DANFe Simplificado; - 4-DANFe NFC-e;5-DANFe NFC-e em mensagem eletrônica) + Formato de impressão do DANFE: +0 - Sem DANFE; +1 - DANFE Retrato; +2 - DANFE Paisagem; +3 - DANFE Simplificado; +4 - DANFE NFC-e; +5 - DANFE NFC-e em mensagem eletrônica; +6 - DANFE Simplificado Tipo 2 (nas condições do Ajuste SINIEF 13/26). @@ -149,20 +134,21 @@ Campo preenchido somente quando “indPres = 5 (Operação presencial, fora do e + - Forma de emissão da NF-e + Forma de emissão da NF-e: 1 - Normal; -2 - Contingência FS -3 - Regime Especial NFF (NT 2021.002) -4 - Contingência DPEC -5 - Contingência FSDA -6 - Contingência SVC - AN -7 - Contingência SVC - RS -9 - Contingência off-line NFC-e +2 - Contingência FS; +3 - Regime Especial NFF (NT 2021.002); +4 - Contingência DPEC; +5 - Contingência FSDA; +6 - Contingência SVC - AN; +7 - Contingência SVC - RS; +9 - Contingência off-line da NFC-e e da NF-e com DANFE Simplificado Tipo 2. @@ -209,13 +195,7 @@ Campo preenchido somente quando “indPres = 5 (Operação presencial, fora do e - Tipo de Nota de Débito: -01=Transferência de créditos para Cooperativas; -02=Anulação de Crédito por Saídas Imunes/Isentas; -03=Débitos de notas fiscais não processadas na apuração; -04=Multa e juros; -05=Transferência de crédito de sucessão. - + Tipo de Nota de Débito @@ -237,8 +217,14 @@ Campo preenchido somente quando “indPres = 5 (Operação presencial, fora do e - Indicador de presença do comprador no estabelecimento comercial no momento da oepração - (0-Não se aplica (ex.: Nota Fiscal complementar ou de ajuste;1-Operação presencial;2-Não presencial, internet;3-Não presencial, teleatendimento;4-NFC-e entrega em domicílio;5-Operação presencial, fora do estabelecimento;9-Não presencial, outros) + Indicador de presença do comprador no estabelecimento comercial no momento da oepração: +0 - Não se aplica (ex.: Nota Fiscal complementar ou de ajuste); +1 - Operação presencial; +2 - Operação não presencial, internet; +3 - Operação não presencial, teleatendimento; +4 - Operação não presencial com NFC-e e NFe com DANFE Simplificado Tipo 2 (com entrega); +5 - Operação presencial, fora do estabelecimento; +9 - Operação não presencial, outros. @@ -267,6 +253,17 @@ Campo preenchido somente quando “indPres = 5 (Operação presencial, fora do e + + + Código indicador do local da operação de fornecimento + + + + + + + + Processo de emissão utilizado com a seguinte codificação: @@ -620,6 +617,17 @@ Este campo será obrigatoriamente preenchido com: + + + Inscrição do emitente na Suframa + + + + + + + + @@ -6654,7 +6662,7 @@ tipo de ato concessório: - + @@ -6728,7 +6736,7 @@ tipo de ato concessório: Descrição literal do status do serviço solicitado. - + Código da Mensagem. @@ -6747,7 +6755,7 @@ tipo de ato concessório: - + @@ -7483,7 +7491,7 @@ alterado para tamanho variavel 1-4. (NT2011/004) 04=Multa e juros; 05=Transferência de crédito na sucessão; 06=Pagamento antecipado; - 07=Perda em estoque; + 07=Perda em estoque (Perecimento, Perda, Furto, Roubo); 08=Desenquadramento do SN; @@ -7506,6 +7514,7 @@ alterado para tamanho variavel 1-4. (NT2011/004) 03=Retorno por recusa na entrega ou por não localização do destinatário na tentativa de entrega; 04=Redução de valores; 05=Transferência de crédito na sucessão; + 06=Retorno por recusa parcial na entrega; @@ -7515,6 +7524,7 @@ alterado para tamanho variavel 1-4. (NT2011/004) + diff --git a/NFe.AppTeste/Schemas/resEvento_v1.01.xsd b/NFe.AppTeste/Schemas/resEvento_v1.01.xsd index 3ac45a4d8..40113c758 100644 --- a/NFe.AppTeste/Schemas/resEvento_v1.01.xsd +++ b/NFe.AppTeste/Schemas/resEvento_v1.01.xsd @@ -1,94 +1,94 @@ - - - - - - - Schema da estrutura XML gerada pelo Ambiente Nacional com o conjunto de informações resumidas de um evento de NF-e - - - - - - Código do órgão de recepção do Evento. Utilizar a Tabela do IBGE extendida, utilizar 91 para identificar o Ambiente Nacional - - - - - - CNPJ do Emitente - - - - - CPF do Emitente - - - - - - Chave de acesso da NF-e - - - - - Data e Hora do Evento, formato UTC (AAAA-MM-DDThh:mm:ssTZD, onde TZD = +hh:mm ou -hh:mm) - - - - - Tipo do Evento - - - - - - - - - - - Seqüencial do evento para o mesmo tipo de evento. Para maioria dos eventos será 1, nos casos em que possa existir mais de um evento, como é o caso da carta de correção, o autor do evento deve numerar de forma seqüencial - - - - - - - - - - - Descrição do Evento - - - - - - - - - - - Data e hora de autorização do evento no formato AAAA-MM-DDTHH:MM:SSTZD - - - - - Número do Protocolo do evento. 1 posição (1 – Secretaria de Fazenda Estadual 2 – Receita Federal); 2 - códiga da UF - 2 posições ano; 10 seqüencial no ano - - - - - - - - - Tipo Versão do leiate resNFe - - - - - - - \ No newline at end of file + + + + + + Schema da estrutura XML gerada pelo Ambiente Nacional com o conjunto de informações resumidas de um evento de NF-e + + + + + + Código do órgão de recepção do Evento. Utilizar a Tabela do IBGE extendida, utilizar 91 para identificar o Ambiente Nacional + + + + + + +CNPJ do Emitente + + + + + CPF do Emitente + + + + + + Chave de acesso da NF-e + + + + + Data e Hora do Evento, formato UTC (AAAA-MM-DDThh:mm:ssTZD, onde TZD = +hh:mm ou -hh:mm) + + + + + Tipo do Evento + + + + + + + + + + + Seqüencial do evento para o mesmo tipo de evento. Para maioria dos eventos será 1, nos casos em que possa existir mais de um evento, como é o caso da carta de correção, o autor do evento deve numerar de forma seqüencial + + + + + + + + + + + Descrição do Evento + + + + + + + + + + + Data e hora de autorização do evento no formato AAAA-MM-DDTHH:MM:SSTZD + + + + + Número do Protocolo do evento. 1 posição (1 – Secretaria de Fazenda Estadual 2 – Receita Federal); 2 - códiga da UF - 2 posições ano; 10 seqüencial no ano + + + + + + + + + Tipo Versão do leiate resNFe + + + + + + + diff --git a/NFe.AppTeste/Schemas/resNFe_v1.01.xsd b/NFe.AppTeste/Schemas/resNFe_v1.01.xsd index 91d83b0dd..91a9e9e46 100644 --- a/NFe.AppTeste/Schemas/resNFe_v1.01.xsd +++ b/NFe.AppTeste/Schemas/resNFe_v1.01.xsd @@ -1,111 +1,110 @@ - - - - - - - Schema da estrutura XML gerada pelo Ambiente Nacional com o conjunto de informações resumidas de uma NF-e - - - - - - Chave de acesso da NF-e - - - - - - CNPJ do Emitente - - - - - CPF do Emitente - - - - - - Razão Social ou Nome do emitente - - - - - - - - - - - Inscrição Estadual do Emitente - - - - - Data e Hora de emissão do Documento Fiscal (AAAA-MM-DDThh:mm:ssTZD) ex.: 2012-09-01T13:00:00-03:00 - - - - - Tipo do Documento Fiscal (0 - entrada; 1 - saída) - - - - - - - - - - - - Valor Total da NF-e - - - - - Digest Value da NF-e processada. Utilizado para conferir a integridade da NF-e original - - - - - Data e hora de autorização da NF-e, no formato AAAA-MM-DDTHH:MM:SSTZD - - - - - Número do Protocolo de Status da NF-e. 1 posição (1 – Secretaria de Fazenda Estadual 2 – Receita Federal); 2 - códiga da UF - 2 posições ano; 10 seqüencial no ano - - - - - + + + + + + Schema da estrutura XML gerada pelo Ambiente Nacional com o conjunto de informações resumidas de uma NF-e + + + + + + Chave de acesso da NF-e + + + + + + CNPJ do Emitente + + + + + CPF do Emitente + + + + + + Razão Social ou Nome do emitente + + + + + + + + + + + Inscrição Estadual do Emitente + + + + + Data e Hora de emissão do Documento Fiscal (AAAA-MM-DDThh:mm:ssTZD) ex.: 2012-09-01T13:00:00-03:00 + + + + + Tipo do Documento Fiscal (0 - entrada; 1 - saída) + + + + + + + + + + + + Valor Total da NF-e + + + + + Digest Value da NF-e processada. Utilizado para conferir a integridade da NF-e original + + + + + Data e hora de autorização da NF-e, no formato AAAA-MM-DDTHH:MM:SSTZD + + + + + Número do Protocolo de Status da NF-e. 1 posição (1 – Secretaria de Fazenda Estadual 2 – Receita Federal); 2 - códiga da UF - 2 posições ano; 10 seqüencial no ano + + + + + Situação da NF-e 1-Uso autorizado no momento da consulta; 2-Uso denegado; - - - - - - - - - - - - - - - - - - Tipo Versão do leiate resNFe - - - - - - - \ No newline at end of file + + + + + + + + + + + + + + + + + + Tipo Versão do leiate resNFe + + + + + + + diff --git a/NFe.AppTeste/Schemas/retDistDFeInt_v1.01.xsd b/NFe.AppTeste/Schemas/retDistDFeInt_v1.01.xsd index 3c4b8fce6..638e08c18 100644 --- a/NFe.AppTeste/Schemas/retDistDFeInt_v1.01.xsd +++ b/NFe.AppTeste/Schemas/retDistDFeInt_v1.01.xsd @@ -1,83 +1,82 @@ - - - - - - Schema do resultado do pedido de distribuição de DF-e de interesse - - - - - - + + + + + Schema do resultado do pedido de distribuição de DF-e de interesse + + + + + + Identificação do Ambiente: 1 - Produção 2 - Homologação - - - - - - Versão do Web Service NFeDistribuicaoDFe - - - - - Código do status de processamento da requisição - - - - - Descrição literal do status do processamento da requisição - - - - - Data e Hora de processamento da requisição no formato AAAA-MM-DDTHH:MM:SSTZD - - - - - Último NSU pesquisado no Ambiente Nacional. Se for o caso, o solicitante pode continuar a consulta a partir deste NSU para obter novos resultados. - - - - - Maior NSU existente no Ambiente Nacional para o CNPJ/CPF informado - - - - - Conjunto de informações resumidas e documentos fiscais eletrônicos de interesse da pessoa ou empresa. - - - - - - Informação resumida ou documento fiscal eletrônico de interesse da pessoa ou empresa. O conteúdo desta tag estará compactado no padrão gZip. O tipo do campo é base64Binary. - - - - - - - NSU do documento fiscal - - - - - Identificação do Schema XML que será utilizado para validar o XML existente no conteúdo da tag docZip. Vai identificar o tipo do documento e sua versão. Exemplos: resNFe_v1.00.xsd, procNFe_v3.10.xsd, resEvento_1.00.xsd, procEventoNFe_v1.00.xsd - - - - - - - - - - - - - - \ No newline at end of file + + + + + + Versão do Web Service NFeDistribuicaoDFe + + + + + Código do status de processamento da requisição + + + + + Descrição literal do status do processamento da requisição + + + + + Data e Hora de processamento da requisição no formato AAAA-MM-DDTHH:MM:SSTZD + + + + + Último NSU pesquisado no Ambiente Nacional. Se for o caso, o solicitante pode continuar a consulta a partir deste NSU para obter novos resultados. + + + + + Maior NSU existente no Ambiente Nacional para o CNPJ/CPF informado + + + + + Conjunto de informações resumidas e documentos fiscais eletrônicos de interesse da pessoa ou empresa. + + + + + + Informação resumida ou documento fiscal eletrônico de interesse da pessoa ou empresa. O conteúdo desta tag estará compactado no padrão gZip. O tipo do campo é base64Binary. + + + + + + + NSU do documento fiscal + + + + + Identificação do Schema XML que será utilizado para validar o XML existente no conteúdo da tag docZip. Vai identificar o tipo do documento e sua versão. Exemplos: resNFe_v1.00.xsd, procNFe_v3.10.xsd, resEvento_1.00.xsd, procEventoNFe_v1.00.xsd + + + + + + + + + + + + + + diff --git a/NFe.AppTeste/Schemas/tiposBasico_v1.03.xsd b/NFe.AppTeste/Schemas/tiposBasico_v1.03.xsd index 1f9c9904b..592056fcc 100644 --- a/NFe.AppTeste/Schemas/tiposBasico_v1.03.xsd +++ b/NFe.AppTeste/Schemas/tiposBasico_v1.03.xsd @@ -1,13 +1,4 @@ - - - - - - - - - @@ -68,7 +59,7 @@ - + @@ -95,7 +86,8 @@ - + + @@ -104,16 +96,16 @@ - + - Tipo Número do CNPJ tmanho varíavel (3-14) + Tipo Número do CNPJ - + @@ -123,7 +115,7 @@ - + @@ -903,4 +895,13 @@ acrescentado: + + + Tipo que representa uma chave publica padrão RSA + + + + + + diff --git a/NFe.AppTeste/Schemas/tiposBasico_v1.03_OPENSSL.xsd b/NFe.AppTeste/Schemas/tiposBasico_v1.03_OPENSSL.xsd index 851a822ba..dd9796056 100644 --- a/NFe.AppTeste/Schemas/tiposBasico_v1.03_OPENSSL.xsd +++ b/NFe.AppTeste/Schemas/tiposBasico_v1.03_OPENSSL.xsd @@ -59,7 +59,7 @@ - + @@ -95,7 +95,7 @@ - + @@ -104,7 +104,7 @@ - + @@ -114,7 +114,7 @@ - + @@ -813,4 +813,13 @@ acrescentado: + + + Tipo que representa uma chave publica padrão RSA + + + + + + diff --git a/NFe.AppTeste/Schemas/tiposBasico_v4.00.xsd b/NFe.AppTeste/Schemas/tiposBasico_v4.00.xsd index 4445319c9..5fe029e97 100644 --- a/NFe.AppTeste/Schemas/tiposBasico_v4.00.xsd +++ b/NFe.AppTeste/Schemas/tiposBasico_v4.00.xsd @@ -98,7 +98,7 @@ - Tipo Número do CNPJ tmanho varíavel (3-14) + Tipo Número do CNPJ diff --git a/NFe.AppTeste/Schemas/tiposDistDFe_v1.01.xsd b/NFe.AppTeste/Schemas/tiposDistDFe_v1.01.xsd index 8b0d8a135..12c0015f1 100644 --- a/NFe.AppTeste/Schemas/tiposDistDFe_v1.01.xsd +++ b/NFe.AppTeste/Schemas/tiposDistDFe_v1.01.xsd @@ -1,217 +1,216 @@ - - - - - Tipo número sequencial único - - - - - - - - Tipo quantidade de NSU - - - - - - - - - Tipo Versão dos leiautes do Web Service NFeDistribuicaoDFe - - - - - - - - - Tipo Ambiente - - - - - - - - - - Tipo Código da UF da tabela do IBGE - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Tipo Código de orgão (UF da tabela do IBGE + 90 RFB) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Tipo Número do CNPJ - - - - - - - - - - Tipo Número do CPF - - - - - - - - - - Tipo Versão do Aplicativo - - - - - - - - - Tipo Código da Mensagem enviada - - - - - - - - - - Tipo Motivo - - - - - - - - - Tipo string genérico - - - - - - - - - Tipo Chave da Nota Fiscal Eletrônica - - - - - - - - - - Tipo Número do Protocolo de Status - - - - - - - - - - Data e Hora, formato UTC (AAAA-MM-DDThh:mm:ssTZD, onde TZD = +hh:mm ou -hh:mm) - - - - - - - - - Tipo Inscrição Estadual do Emitente // alterado EM 24/10/08 para aceitar ISENTO - - - - - - - - - - Tipo Decimal com 15 dígitos, sendo 13 de corpo e 2 decimais - - - - - - - \ No newline at end of file + + + + Tipo número sequencial único + + + + + + + + Tipo quantidade de NSU + + + + + + + + + Tipo Versão dos leiautes do Web Service NFeDistribuicaoDFe + + + + + + + + + Tipo Ambiente + + + + + + + + + + Tipo Código da UF da tabela do IBGE + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Tipo Código de orgão (UF da tabela do IBGE + 90 RFB) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Tipo Número do CNPJ + + + + + + + + + + Tipo Número do CPF + + + + + + + + + + Tipo Versão do Aplicativo + + + + + + + + + Tipo Código da Mensagem enviada + + + + + + + + + + Tipo Motivo + + + + + + + + + Tipo string genérico + + + + + + + + + Tipo Chave da Nota Fiscal Eletrônica + + + + + + + + + + Tipo Número do Protocolo de Status + + + + + + + + + + Data e Hora, formato UTC (AAAA-MM-DDThh:mm:ssTZD, onde TZD = +hh:mm ou -hh:mm) + + + + + + + + + Tipo Inscrição Estadual do Emitente // alterado EM 24/10/08 para aceitar ISENTO + + + + + + + + + + Tipo Decimal com 15 dígitos, sendo 13 de corpo e 2 decimais + + + + + + + diff --git a/NFe.Danfe.PdfClown/Elementos/Barcode128C.cs b/NFe.Danfe.PdfClown/Elementos/Barcode128C.cs index 524688bad..6ab3d5132 100644 --- a/NFe.Danfe.PdfClown/Elementos/Barcode128C.cs +++ b/NFe.Danfe.PdfClown/Elementos/Barcode128C.cs @@ -1,6 +1,7 @@ using System.Drawing; using System.Text.RegularExpressions; using NFe.Danfe.PdfClown.Graphics; +using NFe.Danfe.PdfClown.Tools; namespace NFe.Danfe.PdfClown.Elementos { @@ -144,12 +145,13 @@ public Barcode128C(string code, Estilo estilo, float largura = 75F) : base(estil throw new ArgumentException("O código não pode ser vazio.", "code"); } - if (!Regex.IsMatch(code, @"^\d+$")) + // NT Conjunta 2025.001: a chave de acesso pode conter letras maiúsculas nas posições do CNPJ + if (!Regex.IsMatch(code, "^[0-9A-Z]+$")) { - throw new ArgumentException("O código deve apenas conter digítos numéricos.", "code"); + throw new ArgumentException("O código deve apenas conter digítos numéricos e letras maiúsculas.", "code"); } - if (code.Length % 2 != 0) + if (Regex.IsMatch(code, @"^\d+$") && code.Length % 2 != 0) { Code = "0" + code; } @@ -163,35 +165,14 @@ public Barcode128C(string code, Estilo estilo, float largura = 75F) : base(estil private void DrawBarcode(RectangleF rect, Gfx gfx) { + byte[] codeBytes = Code128Hibrido.ObterSimbolos(this.Code); - List codeBytes = new List(); - - codeBytes.Add(105); - - for (int i = 0; i < this.Code.Length; i += 2) - { - byte b = byte.Parse(this.Code.Substring(i, 2)); - codeBytes.Add(b); - } - - // Calcular dígito verificador - int cd = 105; - - for (int i = 1; i < codeBytes.Count; i++) - { - cd += i * codeBytes[i]; - cd %= 103; - } - - codeBytes.Add((byte)cd); - codeBytes.Add(106); - - float n = codeBytes.Count * 11 + 2; + float n = codeBytes.Length * 11 + 2; float w = rect.Width / n; float x = 0; - for (int i = 0; i < codeBytes.Count; i++) + for (int i = 0; i < codeBytes.Length; i++) { byte[] pt = Barcode128C.Dic[codeBytes[i]]; diff --git a/NFe.Danfe.PdfClown/Tools/Code128Hibrido.cs b/NFe.Danfe.PdfClown/Tools/Code128Hibrido.cs new file mode 100644 index 000000000..e48f23d6f --- /dev/null +++ b/NFe.Danfe.PdfClown/Tools/Code128Hibrido.cs @@ -0,0 +1,149 @@ +using System.Text.RegularExpressions; + +namespace NFe.Danfe.PdfClown.Tools +{ + /// + /// Codificador CODE-128 híbrido (subconjuntos C e A) conforme a seção 6 da NT Conjunta 2025.001, + /// para o código de barras da chave de acesso com CNPJ alfanumérico. + /// Regras: inicia com Start C (105); alterna para o Code A (código 101) nos caracteres não numéricos; + /// retorna ao Code C (código 99) em corridas de 4 ou mais dígitos ou em corrida final com quantidade + /// par de dígitos; dígito ímpar remanescente antes de uma letra fica no Code A. + /// Conteúdo 100% numérico produz exatamente a sequência do CODE-128 C puro. + /// + public static class Code128Hibrido + { + private const byte StartC = 105; + private const byte TrocaParaCodeA = 101; + private const byte TrocaParaCodeC = 99; + private const byte Stop = 106; + + /// + /// Obtém a sequência completa de símbolos CODE-128 (Start C, dados com as trocas de subconjunto, + /// dígito verificador módulo 103 e Stop) para um conteúdo composto por dígitos e letras maiúsculas. + /// + /// Conteúdo a codificar ([0-9A-Z]+), ex.: chave de acesso de 44 posições + /// Símbolos CODE-128, um valor (0-106) por posição + public static byte[] ObterSimbolos(string codigo) + { + if (string.IsNullOrEmpty(codigo)) + throw new ArgumentException("O código não pode ser vazio.", nameof(codigo)); + + if (!Regex.IsMatch(codigo, "^[0-9A-Z]+$")) + throw new ArgumentException("O código deve conter somente dígitos (0-9) e letras maiúsculas (A-Z).", nameof(codigo)); + + var simbolos = new List { StartC }; + var emCodeC = true; + var i = 0; + + while (i < codigo.Length) + { + var corrida = TamanhoCorridaDeDigitos(codigo, i); + + if (corrida == 0) + { + //caractere não numérico: garante o Code A e codifica a letra + if (emCodeC) + { + simbolos.Add(TrocaParaCodeA); + emCodeC = false; + } + + simbolos.Add(ValorCodeA(codigo[i])); + i++; + continue; + } + + var corridaFinal = i + corrida == codigo.Length; + + if (emCodeC) + { + //em Code C codifica os dígitos aos pares; o dígito ímpar remanescente fica no Code A + for (var p = 0; p < corrida / 2; p++) + { + simbolos.Add(ValorCodeC(codigo, i)); + i += 2; + } + + if (corrida % 2 == 1) + { + simbolos.Add(TrocaParaCodeA); + emCodeC = false; + simbolos.Add(ValorCodeA(codigo[i])); + i++; + } + } + else if (corrida >= 4 || (corridaFinal && corrida % 2 == 0)) + { + //retorna ao Code C para a parte com quantidade par de dígitos + if (corrida % 2 == 1 && corridaFinal) + { + //corrida final ímpar de 4+ dígitos: o primeiro dígito fica no Code A para a parte restante ser par + simbolos.Add(ValorCodeA(codigo[i])); + i++; + corrida--; + } + + simbolos.Add(TrocaParaCodeC); + emCodeC = true; + + for (var p = 0; p < corrida / 2; p++) + { + simbolos.Add(ValorCodeC(codigo, i)); + i += 2; + } + + //corrida ímpar antes de letra: o dígito remanescente é codificado no Code A na próxima iteração + } + else + { + //corrida curta (1 a 3 dígitos) que não justifica a troca: codifica os dígitos no próprio Code A + for (var d = 0; d < corrida; d++) + { + simbolos.Add(ValorCodeA(codigo[i])); + i++; + } + } + } + + simbolos.Add(ObterDigitoVerificador(simbolos)); + simbolos.Add(Stop); + + return simbolos.ToArray(); + } + + /// + /// Dígito verificador módulo 103: soma ponderada dos símbolos, com o start valendo peso 1 + /// e cada símbolo de dados o peso da sua posição + /// + private static byte ObterDigitoVerificador(List simbolos) + { + var soma = (int)simbolos[0]; + + for (var i = 1; i < simbolos.Count; i++) + soma += i * simbolos[i]; + + return (byte)(soma % 103); + } + + private static int TamanhoCorridaDeDigitos(string codigo, int inicio) + { + var fim = inicio; + + while (fim < codigo.Length && codigo[fim] >= '0' && codigo[fim] <= '9') + fim++; + + return fim - inicio; + } + + private static byte ValorCodeC(string codigo, int posicao) + { + return (byte)((codigo[posicao] - '0') * 10 + (codigo[posicao + 1] - '0')); + } + + private static byte ValorCodeA(char caractere) + { + //no Code A os caracteres ASCII 32-95 valem ASCII - 32 ('0'-'9' => 16-25; 'A'-'Z' => 33-58) + return (byte)(caractere - 32); + } + } +} diff --git a/NFe.Danfe.PdfClown/Tools/Formatador.cs b/NFe.Danfe.PdfClown/Tools/Formatador.cs index 20b01c532..4b1069b61 100644 --- a/NFe.Danfe.PdfClown/Tools/Formatador.cs +++ b/NFe.Danfe.PdfClown/Tools/Formatador.cs @@ -22,7 +22,8 @@ static Formatador() public const string FormatoNumeroNF = @"000\.000\.000"; public const string CEP = @"^(\d{5})\-?(\d{3})$"; - public const string CNPJ = @"^(\d{2})\.?(\d{3})\.?(\d{3})\/?(\d{4})\-?(\d{2})$"; + //NT Conjunta 2025.001: CNPJ alfanumérico - 12 posições [A-Z0-9] e 2 dígitos verificadores numéricos, com a mesma máscara posicional + public const string CNPJ = @"^([A-Z\d]{2})\.?([A-Z\d]{3})\.?([A-Z\d]{3})\/?([A-Z\d]{4})\-?(\d{2})$"; public const string CPF = @"^(\d{3})\.?(\d{3})\.?(\d{3})\-?(\d{2})$"; public const string Telefone = @"^\(?(\d{2})\)?\s*(\d{4,5})\s*\-?\s*(\d{4})$"; diff --git a/NFe.Utils.Testes/ExtinfNFeSuplChaveAlfanumericaTestes.cs b/NFe.Utils.Testes/ExtinfNFeSuplChaveAlfanumericaTestes.cs new file mode 100644 index 000000000..f4b3fda9e --- /dev/null +++ b/NFe.Utils.Testes/ExtinfNFeSuplChaveAlfanumericaTestes.cs @@ -0,0 +1,82 @@ +using System; +using DFe.Classes.Entidades; +using DFe.Classes.Flags; +using NFe.Classes; +using NFe.Classes.Informacoes; +using NFe.Classes.Informacoes.Identificacao; +using NFe.Classes.Informacoes.Identificacao.Tipos; +using NFe.Classes.Informacoes.Total; +using NFe.Utils.InformacoesSuplementares; +using Xunit; + +namespace NFe.Utils.Testes +{ + /// + /// Testes do QR-Code da NFC-e com chave de acesso contendo CNPJ alfanumérico (NT Conjunta 2025.001) + /// + public class ExtinfNFeSuplChaveAlfanumericaTestes + { + //NFC-e de GO com o CNPJ alfanumérico de teste da Receita (PC3D315K000193); DV da chave = 0 + private const string ChaveAlfanumerica = "522507PC3D315K000193650010000000011000000010"; + + private static Classes.NFe CriarNfce(string chave) + { + return new Classes.NFe + { + infNFe = new infNFe + { + Id = "NFe" + chave, + versao = "4.00", + ide = new ide + { + tpAmb = TipoAmbiente.Homologacao, + cUF = Estado.GO, + tpEmis = TipoEmissao.teNormal, + dhEmi = new DateTimeOffset(2025, 7, 15, 10, 0, 0, TimeSpan.FromHours(-3)) + }, + total = new total + { + ICMSTot = new ICMSTot { vNF = 10.50m } + } + } + }; + } + + [Fact] + public void ObterUrlQrCode2_ComChaveAlfanumerica_MontaAUrlComAChaveSemConversaoNumerica() + { + // Arrange + var nfce = CriarNfce(ChaveAlfanumerica); + + // Act + var url = new infNFeSupl().ObterUrlQrCode(nfce, VersaoQrCode.QrCodeVersao2, "000001", "CSC-DE-TESTE"); + + // Assert + Assert.Contains(ChaveAlfanumerica, url); + + //formato dos parâmetros: chave|versão do QR-Code|ambiente|idCsc|hash SHA-1 + var parametros = url.Substring(url.IndexOf("p=", StringComparison.Ordinal) + 2).Split('|'); + Assert.Equal(5, parametros.Length); + Assert.Equal(ChaveAlfanumerica, parametros[0]); + Assert.Equal("2", parametros[1]); + Assert.Equal("2", parametros[2]); + Assert.Equal("1", parametros[3]); + Assert.Equal(40, parametros[4].Trim().Length); + } + + [Fact] + public void ObterUrlQrCode2_ComChaveNumerica_MantemComportamentoAntigo() + { + // Arrange + var nfce = CriarNfce("23190811820016000167650010000000221100000227"); + + // Act + var url = new infNFeSupl().ObterUrlQrCode(nfce, VersaoQrCode.QrCodeVersao2, "000001", "CSC-DE-TESTE"); + + // Assert + var parametros = url.Substring(url.IndexOf("p=", StringComparison.Ordinal) + 2).Split('|'); + Assert.Equal("23190811820016000167650010000000221100000227", parametros[0]); + Assert.Equal(40, parametros[4].Trim().Length); + } + } +}