diff --git a/CTe.AppTeste.NetCore/Program.cs b/CTe.AppTeste.NetCore/Program.cs index 6ed940b80..e348f7241 100644 --- a/CTe.AppTeste.NetCore/Program.cs +++ b/CTe.AppTeste.NetCore/Program.cs @@ -111,6 +111,9 @@ private static async void Menu() case 10: await EventoDesacordoCTe(); break; + case 11: + await EventoCancelaDesacordoCTe(); + break; } if (Convert.ToInt32(option) > 0) @@ -370,6 +373,23 @@ private static async Task EventoDesacordoCTe() OnSucessoSync(new RetornoEEnvio(retorno)); } + private static async Task EventoCancelaDesacordoCTe() + { + var config = new ConfiguracaoDao().BuscarConfiguracao(); + //CarregarConfiguracoes(config); + var configuracaoServico = MontarConfiguracoes(config); + + var cnpj = RequisitarInput("CNPJ Tomador"); + var chave = RequisitarInput("Chave CTe"); + var sequenciaEvento = int.Parse(RequisitarInput("Sequencia Evento")); + var nProtEventoDesacordo = RequisitarInput("Número do Protocolo do Evento de Desacordo"); + + var servico = new EventoCancelamentoDesacordo(sequenciaEvento, chave, cnpj, nProtEventoDesacordo); + var retorno = await servico.CancelarDesacordoAsync(configuracaoServico); + + OnSucessoSync(new RetornoEEnvio(retorno)); + } + private static async Task CartaCorrecao() { var config = new ConfiguracaoDao().BuscarConfiguracao(); diff --git a/CTe.AppTeste/CTeTesteModel.cs b/CTe.AppTeste/CTeTesteModel.cs index 3add5291d..d5772b197 100644 --- a/CTe.AppTeste/CTeTesteModel.cs +++ b/CTe.AppTeste/CTeTesteModel.cs @@ -826,6 +826,22 @@ public void EventoDesacordoCTe() OnSucessoSync(new RetornoEEnvio(retorno)); } + public void EventoCancelaDesacordoCTe() + { + var config = new ConfiguracaoDao().BuscarConfiguracao(); + CarregarConfiguracoes(config); + + var cnpj = (InputBoxTuche("CNPJ Tomador")); + var chave = (InputBoxTuche("Chave CTe")); + var sequenciaEvento = int.Parse(InputBoxTuche("Sequencia Evento")); + var nProtEventoDesacordo = InputBoxTuche("Número do Protocolo do Evento de Desacordo"); + + var servico = new EventoCancelamentoDesacordo(sequenciaEvento, chave, cnpj, nProtEventoDesacordo); + var retorno = servico.CancelarDesacordo(); + + OnSucessoSync(new RetornoEEnvio(retorno)); + } + public void CartaCorrecao() { var config = new ConfiguracaoDao().BuscarConfiguracao(); diff --git a/CTe.AppTeste/MainWindow.xaml b/CTe.AppTeste/MainWindow.xaml index f928d0d3c..d576a74b3 100644 --- a/CTe.AppTeste/MainWindow.xaml +++ b/CTe.AppTeste/MainWindow.xaml @@ -228,6 +228,7 @@ + @@ -243,8 +244,9 @@ + - + diff --git a/CTe.AppTeste/MainWindow.xaml.cs b/CTe.AppTeste/MainWindow.xaml.cs index 6f1511e49..0f847b006 100644 --- a/CTe.AppTeste/MainWindow.xaml.cs +++ b/CTe.AppTeste/MainWindow.xaml.cs @@ -120,6 +120,11 @@ private void EventoDesacordoCTe_Click(object sender, RoutedEventArgs e) _model.EventoDesacordoCTe(); } + private void EventoCancelaDesacordoCTe_Click(object sender, RoutedEventArgs e) + { + _model.EventoCancelaDesacordoCTe(); + } + private void CartaCorrecao_Click(object sender, RoutedEventArgs e) { _model.CartaCorrecao(); diff --git a/CTe.Classes/CTeOutrosServicos/Informacoes/Impostos/impOs.cs b/CTe.Classes/CTeOutrosServicos/Informacoes/Impostos/impOs.cs index 058428168..378cdb677 100644 --- a/CTe.Classes/CTeOutrosServicos/Informacoes/Impostos/impOs.cs +++ b/CTe.Classes/CTeOutrosServicos/Informacoes/Impostos/impOs.cs @@ -24,5 +24,21 @@ public decimal? vTotTrib public infTribFed infTribFed { get; set; } public IBSCBS IBSCBS { get; set; } + + private decimal? _vTotDFe; + /// + /// O total geral do DFe deverá ser a soma do total da prestação + IBS + CBS + /// vTotDFe = vPrest / vTPrest + gIBSCBS / vIBS + gCBS / vCBS + /// + /// Exceção: Em 2026 não somar IBS e CBS + /// Observação: Implementação futura + /// + public decimal? vTotDFe + { + get { return _vTotDFe.Arredondar(2); } + set { _vTotDFe = value.Arredondar(2); } + } + + public bool vTotDFeSpecified { get { return vTotDFe.HasValue; } } } } \ No newline at end of file diff --git a/CTe.Classes/Informacoes/Tipos/tpEmis.cs b/CTe.Classes/Informacoes/Tipos/tpEmis.cs index 154f06900..32554c433 100644 --- a/CTe.Classes/Informacoes/Tipos/tpEmis.cs +++ b/CTe.Classes/Informacoes/Tipos/tpEmis.cs @@ -36,6 +36,7 @@ namespace CTe.Classes.Informacoes.Tipos /// /// Forma de emissão da CT-e /// 1 - Emissão normal (não em contingência) + /// 3 - Regime Especial NFF /// 4 - Contingência EPEC pela SVC /// 5 - Contingência FS-DA, com impressão do DANFE em formulário de segurança /// 7 - Contingência SVC-RS (SEFAZ Virtual de Contingência do RS) @@ -45,6 +46,8 @@ public enum tpEmis { [XmlEnum("1")] teNormal = 1, + [XmlEnum("3")] + teNFF = 3, [XmlEnum("4")] teEPEC = 4, [XmlEnum("5")] @@ -54,4 +57,4 @@ public enum tpEmis [XmlEnum("8")] teSVCSP = 8 } -} \ No newline at end of file +} diff --git a/CTe.Classes/Informacoes/autXML.cs b/CTe.Classes/Informacoes/autXML.cs index e49bd4c29..ed8e8b204 100644 --- a/CTe.Classes/Informacoes/autXML.cs +++ b/CTe.Classes/Informacoes/autXML.cs @@ -49,13 +49,12 @@ public string CNPJ get { return cnpj; } set { - if (string.IsNullOrEmpty(value)) return; - if (string.IsNullOrEmpty(cpf)) - cnpj = value; - else - { + if (string.IsNullOrEmpty(value)) + return; + if (!string.IsNullOrEmpty(cpf)) throw new ArgumentException(ErroCpfCnpjPreenchidos); - } + + cnpj = value; } } @@ -67,13 +66,12 @@ public string CPF get { return cpf; } set { - if (string.IsNullOrEmpty(value)) return; - if (string.IsNullOrEmpty(cnpj)) - cpf = value; - else - { + if (string.IsNullOrEmpty(value)) + return; + if (!string.IsNullOrEmpty(cnpj)) throw new ArgumentException(ErroCpfCnpjPreenchidos); - } + + cpf = value; } } } diff --git a/CTe.Classes/Servicos/DistribuicaoDFe/Schemas/procEventoCTe.cs b/CTe.Classes/Servicos/DistribuicaoDFe/Schemas/procEventoCTe.cs index 72e81ee2a..571ea6796 100644 --- a/CTe.Classes/Servicos/DistribuicaoDFe/Schemas/procEventoCTe.cs +++ b/CTe.Classes/Servicos/DistribuicaoDFe/Schemas/procEventoCTe.cs @@ -64,7 +64,5 @@ public class procEventoCTe /// [XmlElement(Namespace = "http://www.portalfiscal.inf.br/cte")] public retEventoCTe retEventoCTe { get; set; } - - } } diff --git a/CTe.Classes/Servicos/DistribuicaoDFe/Schemas/resCTe.cs b/CTe.Classes/Servicos/DistribuicaoDFe/Schemas/resCTe.cs new file mode 100644 index 000000000..638022f1a --- /dev/null +++ b/CTe.Classes/Servicos/DistribuicaoDFe/Schemas/resCTe.cs @@ -0,0 +1,123 @@ +using DFe.Utils; +using System; +using System.ComponentModel; +using System.Xml.Serialization; + +namespace CTe.Classes.Servicos.DistribuicaoDFe.Schemas +{ + /// + /// Resumo de CT-e retornado no docZip com schema resCTe_v1.03.xsd + /// Ref: NT 2015/002 - CTeDistribuicaoDFe + /// + [Serializable()] + [DesignerCategory("code")] + [XmlType(AnonymousType = true, Namespace = "http://www.portalfiscal.inf.br/cte")] + [XmlRoot(Namespace = "http://www.portalfiscal.inf.br/cte", IsNullable = false)] + public class resCTe + { + /// + /// Chave de acesso do CT-e (44 dígitos) + /// + [XmlElement("chCTe")] + public string chCTe { get; set; } + + /// + /// CNPJ do emitente (14 dígitos) + /// + [XmlElement("CNPJ")] + public string CNPJ { get; set; } + + /// + /// Razão social ou nome do emitente + /// + [XmlElement("xNome")] + public string xNome { get; set; } + + /// + /// Inscrição Estadual do emitente + /// + [XmlElement("IE")] + public string IE { get; set; } + + /// + /// Modal do CT-e + /// + /// 01=Rodoviário + /// 02=Aéreo + /// 03=Aquaviário + /// 04=Ferroviário + /// 05=Dutoviário + /// 06=Multimodal + /// + /// + [XmlElement("modal")] + public string modal { get; set; } + + /// + /// Data e hora de emissão do CT-e + /// + [XmlIgnore] + public DateTimeOffset dhEmi { get; set; } + + [XmlElement(ElementName = "dhEmi")] + public string ProxydhEmi + { + get { return dhEmi.ParaDataHoraStringUtc(); } + set { dhEmi = DateTimeOffset.Parse(value); } + } + + /// + /// Tipo do CT-e + /// + /// 0=CT-e Normal + /// 1=CT-e de Complemento de Valores + /// 2=CT-e de Anulação + /// 3=CT-e de Substituição + /// + /// + [XmlElement("tpCTe")] + public string tpCTe { get; set; } + + /// + /// Digest value da assinatura do CT-e + /// + [XmlElement("digVal")] + public string digVal { get; set; } + + /// + /// Data e hora do recebimento pelo Ambiente Nacional + /// + [XmlIgnore] + public DateTimeOffset dhRecbto { get; set; } + + [XmlElement(ElementName = "dhRecbto")] + public string ProxydhRecbto + { + get { return dhRecbto.ParaDataHoraStringUtc(); } + set { dhRecbto = DateTimeOffset.Parse(value); } + } + + /// + /// Número do protocolo de autorização + /// + [XmlElement("nProt")] + public string nProt { get; set; } + + /// + /// Valor a receber pelo transportador + /// + [XmlElement("vRec")] + public string vRec { get; set; } + + /// + /// Situação do CT-e + /// + /// 100=Autorizado o uso do CT-e + /// 101=Cancelamento de CT-e homologado + /// 110=Uso denegado + /// + /// + [XmlElement("cSitCTe")] + public string cSitCTe { get; set; } + } +} diff --git a/CTe.Classes/Servicos/DistribuicaoDFe/distDFeInt.cs b/CTe.Classes/Servicos/DistribuicaoDFe/distDFeInt.cs index b61e0d2cd..f40503365 100644 --- a/CTe.Classes/Servicos/DistribuicaoDFe/distDFeInt.cs +++ b/CTe.Classes/Servicos/DistribuicaoDFe/distDFeInt.cs @@ -71,13 +71,12 @@ public string CNPJ get { return _cNPJ; } set { - if (string.IsNullOrEmpty(value)) return; - if (string.IsNullOrEmpty(_cPF)) - _cNPJ = value; - else - { + if (string.IsNullOrEmpty(value)) + return; + if (!string.IsNullOrEmpty(_cPF)) throw new ArgumentException(ErroCpfCnpjPreenchidos); - } + + _cNPJ = value; } } @@ -89,13 +88,12 @@ public string CPF get { return _cPF; } set { - if (string.IsNullOrEmpty(value)) return; - if (string.IsNullOrEmpty(_cNPJ)) - _cPF = value; - else - { + if (string.IsNullOrEmpty(value)) + return; + if (!string.IsNullOrEmpty(_cNPJ)) throw new ArgumentException(ErroCpfCnpjPreenchidos); - } + + _cPF = value; } } @@ -107,8 +105,6 @@ public string CPF /// /// A09 - Grupo para consultar um DF-e a partir de um NSU específico /// - public consNSU consNSU { get; set; } - - + public consNSU consNSU { get; set; } } } diff --git a/CTe.Classes/Servicos/DistribuicaoDFe/loteDistDFeInt.cs b/CTe.Classes/Servicos/DistribuicaoDFe/loteDistDFeInt.cs index 5a12f9988..3e0dbf8ab 100644 --- a/CTe.Classes/Servicos/DistribuicaoDFe/loteDistDFeInt.cs +++ b/CTe.Classes/Servicos/DistribuicaoDFe/loteDistDFeInt.cs @@ -31,6 +31,7 @@ /* Rua Comendador Francisco josé da Cunha, 111 - Itabaiana - SE - 49500-000 */ /********************************************************************************/ +using CTe.Classes.Servicos.DistribuicaoDFe.Schemas; using System; using System.ComponentModel; using System.Xml.Serialization; @@ -69,5 +70,21 @@ public class loteDistDFeInt /// [XmlText(DataType = "base64Binary")] public byte[] XmlNfe { get; set; } + + #region Objetos possíveis para descompactar o conteúdo do campo XmlNfe, dependendo do valor do campo schema + [XmlIgnore] + public cteProc cteProc { get; set; } + + [XmlIgnore] + public resCTe resCTe { get; set; } + + [XmlIgnore] + public CTeOSDocumento.CTe.CTeOS.Retorno.cteOSProc cteOSProc { get; set; } + + [XmlIgnore] + public Classes.Servicos.DistribuicaoDFe.Schemas.procEventoCTe procEventoCTe { get; set; } + + //TO DO: Adicionar no futuro o procGTVe adicionando na versão 1.04 do schema de distribuição de DFe + #endregion } } diff --git a/CTe.Classes/Servicos/Evento/dest.cs b/CTe.Classes/Servicos/Evento/dest.cs index 9ac0e13ed..204a57316 100644 --- a/CTe.Classes/Servicos/Evento/dest.cs +++ b/CTe.Classes/Servicos/Evento/dest.cs @@ -60,13 +60,12 @@ public string CNPJ get { return _cnpj; } set { - if (string.IsNullOrEmpty(value)) return; - if (string.IsNullOrEmpty(CPF) & string.IsNullOrEmpty(idEstrangeiro)) - _cnpj = value; - else - { + if (string.IsNullOrEmpty(value)) + return; + if (!string.IsNullOrEmpty(CPF) || !string.IsNullOrEmpty(idEstrangeiro)) throw new ArgumentException(ErroCpfCnpjIdEstrangeiroPreenchidos); - } + + _cnpj = value; } } @@ -78,13 +77,12 @@ public string CPF get { return _cpf; } set { - if (string.IsNullOrEmpty(value)) return; - if (string.IsNullOrEmpty(CNPJ) & string.IsNullOrEmpty(idEstrangeiro)) - _cpf = value; - else - { + if (string.IsNullOrEmpty(value)) + return; + if (!string.IsNullOrEmpty(CNPJ) || !string.IsNullOrEmpty(idEstrangeiro)) throw new ArgumentException(ErroCpfCnpjIdEstrangeiroPreenchidos); - } + + _cpf = value; } } @@ -96,13 +94,12 @@ public string idEstrangeiro get { return _idEstrangeiro; } set { - if (string.IsNullOrEmpty(value)) return; - if (string.IsNullOrEmpty(CNPJ) & string.IsNullOrEmpty(CPF)) - _idEstrangeiro = value; - else - { + if (string.IsNullOrEmpty(value)) + return; + if (!string.IsNullOrEmpty(CNPJ) || !string.IsNullOrEmpty(CPF)) throw new ArgumentException(ErroCpfCnpjIdEstrangeiroPreenchidos); - } + + _idEstrangeiro = value; } } diff --git a/CTe.Classes/Servicos/Evento/detEvento.cs b/CTe.Classes/Servicos/Evento/detEvento.cs index 17c3d2563..e840e81d3 100644 --- a/CTe.Classes/Servicos/Evento/detEvento.cs +++ b/CTe.Classes/Servicos/Evento/detEvento.cs @@ -44,6 +44,7 @@ public class detEvento [XmlElement("evCancCTe", typeof(evCancCTe), Namespace = "http://www.portalfiscal.inf.br/cte")] [XmlElement("evCCeCTe", typeof(evCCeCTe), Namespace = "http://www.portalfiscal.inf.br/cte")] [XmlElement("evPrestDesacordo", typeof(evPrestDesacordo), Namespace = "http://www.portalfiscal.inf.br/cte")] + [XmlElement("evCancPrestDesacordo", typeof(evCancPrestDesacordo), Namespace = "http://www.portalfiscal.inf.br/cte")] public EventoContainer EventoContainer { get; set; } } } \ No newline at end of file diff --git a/CTe.Classes/Servicos/Evento/evCancPrestDesacordo.cs b/CTe.Classes/Servicos/Evento/evCancPrestDesacordo.cs new file mode 100644 index 000000000..f50233dc4 --- /dev/null +++ b/CTe.Classes/Servicos/Evento/evCancPrestDesacordo.cs @@ -0,0 +1,18 @@ +using System.Xml.Serialization; + +namespace CTe.Classes.Servicos.Evento +{ + [XmlRoot(Namespace = "http://www.portalfiscal.inf.br/cte")] + public class evCancPrestDesacordo : EventoContainer + { + public evCancPrestDesacordo() + { + this.descEvento = "Cancelamento Prestação do Serviço em Desacordo"; + } + + public string descEvento { get; set; } + + public string nProtEvPrestDes { get; set; } + + } +} diff --git a/CTe.Servicos/ConsultaStatus/StatusServico.cs b/CTe.Servicos/ConsultaStatus/StatusServico.cs index 296f79b6b..d1c8063d2 100644 --- a/CTe.Servicos/ConsultaStatus/StatusServico.cs +++ b/CTe.Servicos/ConsultaStatus/StatusServico.cs @@ -61,6 +61,9 @@ public retConsStatServCte ConsultaStatus(ConfiguracaoServico configuracaoServico public retConsStatServCTe ConsultaStatusV4(ConfiguracaoServico configuracaoServico = null) { + if (configuracaoServico == null) + configuracaoServico = ConfiguracaoServico.Instancia; + var consStatServCte = ClassesFactory.CriaConsStatServCTe(configuracaoServico); if (configuracaoServico.IsValidaSchemas) diff --git a/CTe.Servicos/DistribuicaoDFe/ServicoCTeDistribuicaoDFe.cs b/CTe.Servicos/DistribuicaoDFe/ServicoCTeDistribuicaoDFe.cs index e068c9f3e..d4b7db967 100644 --- a/CTe.Servicos/DistribuicaoDFe/ServicoCTeDistribuicaoDFe.cs +++ b/CTe.Servicos/DistribuicaoDFe/ServicoCTeDistribuicaoDFe.cs @@ -98,27 +98,33 @@ public RetornoCteDistDFeInt CTeDistDFeInteresse(string ufAutor, string documento { for (int i = 0; i < retConsulta.loteDistDFeInt.Length; i++) { - string conteudo = Compressao.Unzip(retConsulta.loteDistDFeInt[i].XmlNfe).RemoverDeclaracaoXml(); + var loteAtual = retConsulta.loteDistDFeInt[i]; + string conteudo = Compressao.Unzip(loteAtual.XmlNfe).RemoverDeclaracaoXml(); string chCTe = string.Empty; if (conteudo.StartsWith("(conteudo); - chCTe = retConteudo.protCTe.infProt.chCTe; + var cteConteudo = FuncoesXml.XmlStringParaClasse(conteudo); + chCTe = cteConteudo.protCTe.infProt.chCTe; + loteAtual.cteProc = cteConteudo; } else if (conteudo.StartsWith("(conteudo); chCTe = procEventoNFeConteudo.eventoCTe.infEvento.chCTe; + loteAtual.procEventoCTe = procEventoNFeConteudo; } else if (conteudo.StartsWith("(conteudo); - chCTe = retConteudo.protCTe.infProt.chCTe; + var cteOSConteudo = FuncoesXml.XmlStringParaClasse(conteudo); + chCTe = cteOSConteudo.protCTe.infProt.chCTe; + loteAtual.cteOSProc = cteOSConteudo; } - else + else if (conteudo.StartsWith("(conteudo); + chCTe = resCTeConteudo.chCTe; + loteAtual.resCTe = resCTeConteudo; } string[] schema = retConsulta.loteDistDFeInt[i].schema.Split('_'); diff --git a/CTe.Servicos/Enderecos/Helpers/UrlHelper.cs b/CTe.Servicos/Enderecos/Helpers/UrlHelper.cs index 827cd2a1e..6c007186b 100644 --- a/CTe.Servicos/Enderecos/Helpers/UrlHelper.cs +++ b/CTe.Servicos/Enderecos/Helpers/UrlHelper.cs @@ -42,36 +42,51 @@ namespace CTe.Servicos.Enderecos.Helpers { public class UrlHelper { - public static UrlCTe ObterUrlServico(ConfiguracaoServico configuracaoServico = null) + /// + /// Obtem a url de Serviço. Por padrão pega as informações de ConfiguracaoServico, mas pode ser customizada individualmente. Motivo: em alguns casos a url de destino de um evento de CTe pode seguir as informações do xml e não da empresa emissora / com certificado ativo + /// + /// + /// + /// + /// + /// + /// + /// + public static UrlCTe ObterUrlServico(ConfiguracaoServico configuracaoServico = null, tpEmis? tipoEmissao = null , TipoAmbiente? tipoAmbiente = null, Estado? ufRecepcao = null, versao? versaoLayout = null) { var configServico = configuracaoServico ?? ConfiguracaoServico.Instancia; - switch (configServico.tpAmb) + var tipoEmissaoUtilizado = tipoEmissao ?? configServico.TipoEmissao; + var tipoAmbienteUtilizado = tipoAmbiente ?? configServico.tpAmb; + var ufUtilizada = ufRecepcao ?? configServico.cUF; + var versaoUtilizada = versaoLayout ?? configServico.VersaoLayout; + + switch (tipoAmbienteUtilizado) { case TipoAmbiente.Homologacao: - if (configServico.TipoEmissao == tpEmis.teSVCRS) + if (tipoEmissaoUtilizado == tpEmis.teSVCRS) { - return UrlHomologacaoSvrs(configServico); + return UrlHomologacaoSvrs(versaoUtilizada); } - if (configServico.TipoEmissao == tpEmis.teSVCSP) + if (tipoEmissaoUtilizado == tpEmis.teSVCSP) { - return UrlHomologacaoSvcsp(configServico); + return UrlHomologacaoSvcsp(versaoUtilizada); } - return UrlHomologacao(configServico); + return UrlHomologacao(ufUtilizada, versaoUtilizada); case TipoAmbiente.Producao: - if (configServico.TipoEmissao == tpEmis.teSVCRS) + if (tipoEmissaoUtilizado == tpEmis.teSVCRS) { - return UrlProducaoSvrs(configServico); + return UrlProducaoSvrs(versaoUtilizada); } - if (configServico.TipoEmissao == tpEmis.teSVCSP) + if (tipoEmissaoUtilizado == tpEmis.teSVCSP) { - return UrlProducaoSvcsp(configServico); + return UrlProducaoSvcsp(versaoUtilizada); } - return UrlProducao(configServico); + return UrlProducao(ufUtilizada, versaoUtilizada); } throw new InvalidOperationException("Tipo Ambiente inválido"); @@ -84,17 +99,17 @@ public static string ObterUrlQrCode(ConfiguracaoServico configuracaoServico = nu switch (configServico.tpAmb) { case TipoAmbiente.Homologacao: - return UrlHomologacao(configServico).QrCode; + return UrlHomologacao(configServico.cUF, configServico.VersaoLayout).QrCode; case TipoAmbiente.Producao: - return UrlProducao(configServico).QrCode; + return UrlProducao(configServico.cUF, configServico.VersaoLayout).QrCode; } throw new InvalidOperationException("Tipo Ambiente inválido"); } - private static UrlCTe UrlProducaoSvcsp(ConfiguracaoServico configuracaoServico) + private static UrlCTe UrlProducaoSvcsp(versao VersaoLayout) { - if (configuracaoServico.VersaoLayout == versao.ve400) + if (VersaoLayout == versao.ve400) { return new UrlCTe { @@ -119,9 +134,9 @@ private static UrlCTe UrlProducaoSvcsp(ConfiguracaoServico configuracaoServico) }; } - private static UrlCTe UrlHomologacaoSvcsp(ConfiguracaoServico configuracaoServico) + private static UrlCTe UrlHomologacaoSvcsp(versao VersaoLayout) { - if (configuracaoServico.VersaoLayout == versao.ve400) + if (VersaoLayout == versao.ve400) { return new UrlCTe { @@ -146,9 +161,9 @@ private static UrlCTe UrlHomologacaoSvcsp(ConfiguracaoServico configuracaoServic }; } - private static UrlCTe UrlHomologacaoSvrs(ConfiguracaoServico configuracaoServico) + private static UrlCTe UrlHomologacaoSvrs(versao VersaoLayout) { - if (configuracaoServico.VersaoLayout == versao.ve400) + if (VersaoLayout == versao.ve400) { return new UrlCTe { @@ -174,9 +189,9 @@ private static UrlCTe UrlHomologacaoSvrs(ConfiguracaoServico configuracaoServico }; } - private static UrlCTe UrlProducaoSvrs(ConfiguracaoServico configuracaoServico) + private static UrlCTe UrlProducaoSvrs(versao VersaoLayout) { - if (configuracaoServico.VersaoLayout == versao.ve400) + if (VersaoLayout == versao.ve400) { return new UrlCTe { @@ -202,12 +217,12 @@ private static UrlCTe UrlProducaoSvrs(ConfiguracaoServico configuracaoServico) }; } - private static UrlCTe UrlProducao(ConfiguracaoServico configuracaoServico) + private static UrlCTe UrlProducao(Estado UFRecepcao, versao VersaoLayout) { - switch (configuracaoServico.cUF) + switch (UFRecepcao) { case Estado.MT: - if (configuracaoServico.VersaoLayout == versao.ve400) + if (VersaoLayout == versao.ve400) { return new UrlCTe { @@ -233,7 +248,7 @@ private static UrlCTe UrlProducao(ConfiguracaoServico configuracaoServico) CTeDistribuicaoDFe = "https://www1.cte.fazenda.gov.br/CTeDistribuicaoDFe/CTeDistribuicaoDFe.asmx" }; case Estado.MS: - if (configuracaoServico.VersaoLayout == versao.ve400) + if (VersaoLayout == versao.ve400) { return new UrlCTe { @@ -259,7 +274,7 @@ private static UrlCTe UrlProducao(ConfiguracaoServico configuracaoServico) CTeDistribuicaoDFe = "https://www1.cte.fazenda.gov.br/CTeDistribuicaoDFe/CTeDistribuicaoDFe.asmx" }; case Estado.MG: - if (configuracaoServico.VersaoLayout == versao.ve400) + if (VersaoLayout == versao.ve400) { return new UrlCTe { @@ -285,7 +300,7 @@ private static UrlCTe UrlProducao(ConfiguracaoServico configuracaoServico) CTeDistribuicaoDFe = "https://www1.cte.fazenda.gov.br/CTeDistribuicaoDFe/CTeDistribuicaoDFe.asmx" }; case Estado.PR: - if (configuracaoServico.VersaoLayout == versao.ve400) + if (VersaoLayout == versao.ve400) { return new UrlCTe { @@ -311,7 +326,7 @@ private static UrlCTe UrlProducao(ConfiguracaoServico configuracaoServico) CTeDistribuicaoDFe = "https://www1.cte.fazenda.gov.br/CTeDistribuicaoDFe/CTeDistribuicaoDFe.asmx" }; case Estado.SP: - if (configuracaoServico.VersaoLayout == versao.ve400) + if (VersaoLayout == versao.ve400) { return new UrlCTe { @@ -359,7 +374,7 @@ private static UrlCTe UrlProducao(ConfiguracaoServico configuracaoServico) case Estado.SE: case Estado.TO: case Estado.RS: - if (configuracaoServico.VersaoLayout == versao.ve400) + if (VersaoLayout == versao.ve400) { return new UrlCTe { @@ -390,7 +405,7 @@ private static UrlCTe UrlProducao(ConfiguracaoServico configuracaoServico) case Estado.AP: case Estado.PE: case Estado.RR: - if (configuracaoServico.VersaoLayout == versao.ve400) + if (VersaoLayout == versao.ve400) { return new UrlCTe { @@ -422,12 +437,12 @@ private static UrlCTe UrlProducao(ConfiguracaoServico configuracaoServico) } - private static UrlCTe UrlHomologacao(ConfiguracaoServico configuracaoServico) + private static UrlCTe UrlHomologacao(Estado UFRecepcao, versao VersaoLayout) { - switch (configuracaoServico.cUF) + switch (UFRecepcao) { case Estado.MT: - if (configuracaoServico.VersaoLayout == versao.ve400) + if (VersaoLayout == versao.ve400) { return new UrlCTe { @@ -454,7 +469,7 @@ private static UrlCTe UrlHomologacao(ConfiguracaoServico configuracaoServico) CTeDistribuicaoDFe = @"https://hom1.cte.fazenda.gov.br/CTeDistribuicaoDFe/CTeDistribuicaoDFe.asmx" }; case Estado.MS: - if (configuracaoServico.VersaoLayout == versao.ve400) + if (VersaoLayout == versao.ve400) { return new UrlCTe { @@ -478,7 +493,7 @@ private static UrlCTe UrlHomologacao(ConfiguracaoServico configuracaoServico) CTeDistribuicaoDFe = @"https://hom1.cte.fazenda.gov.br/CTeDistribuicaoDFe/CTeDistribuicaoDFe.asmx" }; case Estado.MG: - if (configuracaoServico.VersaoLayout == versao.ve400) + if (VersaoLayout == versao.ve400) { return new UrlCTe { @@ -504,7 +519,7 @@ private static UrlCTe UrlHomologacao(ConfiguracaoServico configuracaoServico) CTeDistribuicaoDFe = @"https://hom1.cte.fazenda.gov.br/CTeDistribuicaoDFe/CTeDistribuicaoDFe.asmx" }; case Estado.PR: - if (configuracaoServico.VersaoLayout == versao.ve400) + if (VersaoLayout == versao.ve400) { return new UrlCTe { @@ -545,7 +560,7 @@ private static UrlCTe UrlHomologacao(ConfiguracaoServico configuracaoServico) CTeDistribuicaoDFe = @"https://hom1.cte.fazenda.gov.br/CTeDistribuicaoDFe/CTeDistribuicaoDFe.asmx" }; case Estado.SP: - if (configuracaoServico.VersaoLayout == versao.ve400) + if (VersaoLayout == versao.ve400) { return new UrlCTe { @@ -588,7 +603,7 @@ private static UrlCTe UrlHomologacao(ConfiguracaoServico configuracaoServico) case Estado.SC: case Estado.SE: case Estado.TO: - if (configuracaoServico.VersaoLayout == versao.ve400) + if (VersaoLayout == versao.ve400) { return new UrlCTe { @@ -619,7 +634,7 @@ private static UrlCTe UrlHomologacao(ConfiguracaoServico configuracaoServico) case Estado.AP: case Estado.PE: case Estado.RR: - if (configuracaoServico.VersaoLayout == versao.ve400) + if (VersaoLayout == versao.ve400) { return new UrlCTe { diff --git a/CTe.Servicos/Eventos/EventoCancelamentoDesacordo.cs b/CTe.Servicos/Eventos/EventoCancelamentoDesacordo.cs new file mode 100644 index 000000000..f4040a0d2 --- /dev/null +++ b/CTe.Servicos/Eventos/EventoCancelamentoDesacordo.cs @@ -0,0 +1,49 @@ +using CTe.Classes; +using CTe.Classes.Servicos.Evento; +using CTe.Classes.Servicos.Evento.Flags; +using CTe.Servicos.Factory; +using System.Threading.Tasks; + +namespace CTe.Servicos.Eventos +{ + public class EventoCancelamentoDesacordo + { + private readonly int _sequenciaEvento; + private readonly string _cnpj; + private readonly string _chave; + private readonly string _nProtEvPrestDes; + + public eventoCTe EventoEnviado { get; private set; } + public retEventoCTe RetornoSefaz { get; private set; } + + public EventoCancelamentoDesacordo(int sequenciaEvento, string chave, string cnpj, string nProtEvPrestDes) + { + _chave = chave; + _cnpj = cnpj; + _sequenciaEvento = sequenciaEvento; + _nProtEvPrestDes = nProtEvPrestDes; + } + + public retEventoCTe CancelarDesacordo(ConfiguracaoServico configuracaoServico = null, DFe.Classes.Entidades.Estado? orgaoEmissor = null) + { + var configServico = configuracaoServico ?? ConfiguracaoServico.Instancia; + var eventoCancelaDiscordar = ClassesFactory.CriaEvCancPrestDesacordo(_nProtEvPrestDes); + + EventoEnviado = FactoryEvento.CriaEvento(CTeTipoEvento.CancelamentoPrestacaodoServicoemDesacordo, _sequenciaEvento, _chave, _cnpj, eventoCancelaDiscordar, configServico, orgaoEmissor); + RetornoSefaz = new ServicoController().Executar(CTeTipoEvento.CancelamentoPrestacaodoServicoemDesacordo, _sequenciaEvento, _chave, _cnpj, eventoCancelaDiscordar, configServico, orgaoEmissor); + + return RetornoSefaz; + } + + public async Task CancelarDesacordoAsync(ConfiguracaoServico configuracaoServico = null, DFe.Classes.Entidades.Estado? orgaoEmissor = null) + { + var configServico = configuracaoServico ?? ConfiguracaoServico.Instancia; + var eventoCancelaDiscordar = ClassesFactory.CriaEvCancPrestDesacordo(_nProtEvPrestDes); + + EventoEnviado = FactoryEvento.CriaEvento(CTeTipoEvento.CancelamentoPrestacaodoServicoemDesacordo, _sequenciaEvento, _chave, _cnpj, eventoCancelaDiscordar, configServico, orgaoEmissor); + RetornoSefaz = await new ServicoController().ExecutarAsync(CTeTipoEvento.CancelamentoPrestacaodoServicoemDesacordo, _sequenciaEvento, _chave, _cnpj, eventoCancelaDiscordar, configServico, orgaoEmissor); + + return RetornoSefaz; + } + } +} diff --git a/CTe.Servicos/Eventos/EventoDesacordo.cs b/CTe.Servicos/Eventos/EventoDesacordo.cs index d2cef837a..a90db0d8d 100644 --- a/CTe.Servicos/Eventos/EventoDesacordo.cs +++ b/CTe.Servicos/Eventos/EventoDesacordo.cs @@ -59,23 +59,35 @@ public EventoDesacordo(int sequenciaEvento, string chave, string cnpj, string in _observacao = observacao; } - public retEventoCTe Discordar(ConfiguracaoServico configuracaoServico = null) + /// + /// Gera o evento de desacordo de CTe + /// + /// + /// Sempre considera a UF que gerou o xml. Então a empresa pode estar configurada para uma UF X e gerar o desacordo de um xml gerado na UF Y, sendo o evento, portanto, enviado para UF Y + /// + public retEventoCTe Discordar(ConfiguracaoServico configuracaoServico = null, DFe.Classes.Entidades.Estado? orgaoEmissor = null) { + var configServico = configuracaoServico ?? ConfiguracaoServico.Instancia; var eventoDiscordar = ClassesFactory.CriaEvPrestDesacordo(_indicadorDesacordo, _observacao); - EventoEnviado = FactoryEvento.CriaEvento(CTeTipoEvento.Desacordo, _sequenciaEvento, _chave, _cnpj, eventoDiscordar, configuracaoServico); - RetornoSefaz = new ServicoController().Executar(CTeTipoEvento.Desacordo, _sequenciaEvento, _chave, _cnpj, eventoDiscordar, configuracaoServico); - + EventoEnviado = FactoryEvento.CriaEvento(CTeTipoEvento.Desacordo, _sequenciaEvento, _chave, _cnpj, eventoDiscordar, configServico, orgaoEmissor); + RetornoSefaz = new ServicoController().Executar(CTeTipoEvento.Desacordo, _sequenciaEvento, _chave, _cnpj, eventoDiscordar, configServico, orgaoEmissor); return RetornoSefaz; } - public async Task DiscordarAsync(ConfiguracaoServico configuracaoServico = null) + /// + /// Gera o evento de desacordo de CTe de forma assíncrona + /// + /// + /// Sempre considera a UF que gerou o xml. Então a empresa pode estar configurada para uma UF X e gerar o desacordo de um xml gerado na UF Y, sendo o evento, portanto, enviado para UF Y + /// + public async Task DiscordarAsync(ConfiguracaoServico configuracaoServico = null, DFe.Classes.Entidades.Estado? orgaoEmissor = null) { + var configServico = configuracaoServico ?? ConfiguracaoServico.Instancia; var eventoDiscordar = ClassesFactory.CriaEvPrestDesacordo(_indicadorDesacordo, _observacao); - EventoEnviado = FactoryEvento.CriaEvento(CTeTipoEvento.Desacordo, _sequenciaEvento, _chave, _cnpj, eventoDiscordar, configuracaoServico); - RetornoSefaz = await new ServicoController().ExecutarAsync(CTeTipoEvento.Desacordo, _sequenciaEvento, _chave, _cnpj, eventoDiscordar, configuracaoServico); - + EventoEnviado = FactoryEvento.CriaEvento(CTeTipoEvento.Desacordo, _sequenciaEvento, _chave, _cnpj, eventoDiscordar, configServico, orgaoEmissor); + RetornoSefaz = await new ServicoController().ExecutarAsync(CTeTipoEvento.Desacordo, _sequenciaEvento, _chave, _cnpj, eventoDiscordar, configServico, orgaoEmissor); return RetornoSefaz; } } diff --git a/CTe.Servicos/Eventos/FactoryEvento.cs b/CTe.Servicos/Eventos/FactoryEvento.cs index f862794e2..465732105 100644 --- a/CTe.Servicos/Eventos/FactoryEvento.cs +++ b/CTe.Servicos/Eventos/FactoryEvento.cs @@ -51,7 +51,7 @@ public static eventoCTe CriaEvento(CTeEletronico cte, CTeTipoEvento cTeTipoEvent return CriaEvento(cTeTipoEvento, sequenciaEvento, cte.Chave(), cte.infCte.emit.CNPJ, container, configServico); } - public static eventoCTe CriaEvento(CTeTipoEvento cTeTipoEvento, int sequenciaEvento, string chave, string cnpj, EventoContainer container, ConfiguracaoServico configuracaoServico = null) + public static eventoCTe CriaEvento(CTeTipoEvento cTeTipoEvento, int sequenciaEvento, string chave, string cnpj, EventoContainer container, ConfiguracaoServico configuracaoServico = null, DFe.Classes.Entidades.Estado? cOrgao = null) { var configServico = configuracaoServico ?? ConfiguracaoServico.Instancia; @@ -72,7 +72,7 @@ public static eventoCTe CriaEvento(CTeTipoEvento cTeTipoEvento, int sequenciaEve { tpAmb = configServico.tpAmb, CNPJ = cnpj, - cOrgao = configServico.cUF, + cOrgao = cOrgao ?? configServico.cUF, chCTe = chave, dhEvento = DateTimeOffset.Now, nSeqEvento = sequenciaEvento, diff --git a/CTe.Servicos/Eventos/ServicoController.cs b/CTe.Servicos/Eventos/ServicoController.cs index 5e319f335..751b9e719 100644 --- a/CTe.Servicos/Eventos/ServicoController.cs +++ b/CTe.Servicos/Eventos/ServicoController.cs @@ -66,10 +66,10 @@ public async Task ExecutarAsync(CteEletronico cte, int sequenciaEv return await ExecutarAsync(cTeTipoEvento, sequenciaEvento, cte.Chave(), cte.infCte.emit.CNPJ, container, configServico); } - public retEventoCTe Executar(CTeTipoEvento cTeTipoEvento, int sequenciaEvento, string chave, string cnpj, EventoContainer container, ConfiguracaoServico configuracaoServico = null) + public retEventoCTe Executar(CTeTipoEvento cTeTipoEvento, int sequenciaEvento, string chave, string cnpj, EventoContainer container, ConfiguracaoServico configuracaoServico = null, DFe.Classes.Entidades.Estado? cOrgao = null) { var configServico = configuracaoServico ?? ConfiguracaoServico.Instancia; - var evento = FactoryEvento.CriaEvento(cTeTipoEvento, sequenciaEvento, chave, cnpj, container, configServico); + var evento = FactoryEvento.CriaEvento(cTeTipoEvento, sequenciaEvento, chave, cnpj, container, configServico, cOrgao); evento.Assina(configServico); if (configServico.IsValidaSchemas) @@ -81,13 +81,13 @@ public retEventoCTe Executar(CTeTipoEvento cTeTipoEvento, int sequenciaEvento, s if (evento.versao == versao.ve200 || evento.versao == versao.ve300) { - var webService = WsdlFactory.CriaWsdlCteEvento(configServico); + var webService = WsdlFactory.CriaWsdlCteEvento(configServico, ufUrl: cOrgao); retornoXml = webService.cteRecepcaoEvento(evento.CriaXmlRequestWs()); } if (evento.versao == versao.ve400) { - var webService = WsdlFactory.CriaWsdlCteEventoV4(configServico); + var webService = WsdlFactory.CriaWsdlCteEventoV4(configServico, ufUrl: cOrgao); retornoXml = webService.cteRecepcaoEvento(evento.CriaXmlRequestWs()); } @@ -97,15 +97,11 @@ public retEventoCTe Executar(CTeTipoEvento cTeTipoEvento, int sequenciaEvento, s return retorno; } - public async Task ExecutarAsync(CTeTipoEvento cTeTipoEvento, - int sequenciaEvento, - string chave, string - cnpj, EventoContainer container, - ConfiguracaoServico configuracaoServico = null) + public async Task ExecutarAsync(CTeTipoEvento cTeTipoEvento, int sequenciaEvento, string chave, string cnpj, EventoContainer container, ConfiguracaoServico configuracaoServico = null, DFe.Classes.Entidades.Estado? cOrgao = null) { var configServico = configuracaoServico ?? ConfiguracaoServico.Instancia; - var evento = FactoryEvento.CriaEvento(cTeTipoEvento, sequenciaEvento, chave, cnpj, container, configServico); + var evento = FactoryEvento.CriaEvento(cTeTipoEvento, sequenciaEvento, chave, cnpj, container, configServico, cOrgao); evento.Assina(configServico); if (configServico.IsValidaSchemas) @@ -117,16 +113,15 @@ public async Task ExecutarAsync(CTeTipoEvento cTeTipoEvento, if (evento.versao == versao.ve200 || evento.versao == versao.ve300) { - var webService = WsdlFactory.CriaWsdlCteEvento(configServico); + var webService = WsdlFactory.CriaWsdlCteEvento(configServico, ufUrl: cOrgao); retornoXml = await webService.cteRecepcaoEventoAsync(evento.CriaXmlRequestWs()); } if (evento.versao == versao.ve400) { - var webService = WsdlFactory.CriaWsdlCteEventoV4(configServico); + var webService = WsdlFactory.CriaWsdlCteEventoV4(configServico, ufUrl: cOrgao); retornoXml = await webService.cteRecepcaoEventoAsync(evento.CriaXmlRequestWs()); } - var retorno = retEventoCTe.LoadXml(retornoXml.OuterXml, evento); retorno.SalvarXmlEmDisco(configServico); diff --git a/CTe.Servicos/Factory/ClassesFactory.cs b/CTe.Servicos/Factory/ClassesFactory.cs index 03492279d..646c6428e 100644 --- a/CTe.Servicos/Factory/ClassesFactory.cs +++ b/CTe.Servicos/Factory/ClassesFactory.cs @@ -164,6 +164,22 @@ public static evPrestDesacordo CriaEvPrestDesacordo(string indicadorDesacordo, s return evPrestDesacordo; } + public static evCancPrestDesacordo CriaEvCancPrestDesacordo(string nProtEvPrestDes, ConfiguracaoServico configuracaoServico = null) + { + var configServico = configuracaoServico ?? ConfiguracaoServico.Instancia; + + var evPrestDesacordo = new evCancPrestDesacordo + { + nProtEvPrestDes = nProtEvPrestDes + }; + + if (configServico.cUF == Estado.MT)//sem acentuação issue #1386 + { + evPrestDesacordo.descEvento = "Cancelamento Prestacao do Servico em Desacordo"; + } + + return evPrestDesacordo; + } public static enviCTe CriaEnviCTe(int lote, List cteEletronicoList, ConfiguracaoServico configuracaoServico = null) { diff --git a/CTe.Servicos/Factory/WsdlFactory.cs b/CTe.Servicos/Factory/WsdlFactory.cs index fb633d0e2..cc0bd4591 100644 --- a/CTe.Servicos/Factory/WsdlFactory.cs +++ b/CTe.Servicos/Factory/WsdlFactory.cs @@ -47,6 +47,7 @@ using CTe.Wsdl.Evento.V4; using CTe.Wsdl.Recepcao.Sincrono; using System.Security.Cryptography.X509Certificates; +using DFe.Classes.Entidades; namespace CTe.Servicos.Factory { @@ -123,22 +124,22 @@ public static CteRecepcaoSincronoOSV4 CriaWsdlCteRecepcaoSincronoOSV4(Configurac return new CteRecepcaoSincronoOSV4(configuracaoWsdl); } - public static CteRecepcaoEvento CriaWsdlCteEvento(ConfiguracaoServico configuracaoServico = null, X509Certificate2 certificado = null) + public static CteRecepcaoEvento CriaWsdlCteEvento(ConfiguracaoServico configuracaoServico = null, X509Certificate2 certificado = null, Estado? ufUrl = null) { - var url = UrlHelper.ObterUrlServico(configuracaoServico).CteRecepcaoEvento; + var url = UrlHelper.ObterUrlServico(configuracaoServico, ufRecepcao: ufUrl).CteRecepcaoEvento; - var configuracaoWsdl = CriaConfiguracao(url, configuracaoServico, certificado); + var configuracaoWsdl = CriaConfiguracao(url, configuracaoServico, certificado, ufUrl); return new CteRecepcaoEvento(configuracaoWsdl); } - public static CteRecepcaoEventoV4 CriaWsdlCteEventoV4(ConfiguracaoServico configuracaoServico = null, X509Certificate2 certificado = null) + public static CteRecepcaoEventoV4 CriaWsdlCteEventoV4(ConfiguracaoServico configuracaoServico = null, X509Certificate2 certificado = null, Estado? ufUrl = null) { var configServico = configuracaoServico ?? ConfiguracaoServico.Instancia; - var url = UrlHelper.ObterUrlServico(configServico).CteRecepcaoEvento; + var url = UrlHelper.ObterUrlServico(configServico, ufRecepcao: ufUrl).CteRecepcaoEvento; - var configuracaoWsdl = CriaConfiguracao(url, configServico, certificado); + var configuracaoWsdl = CriaConfiguracao(url, configServico, certificado, ufUrl); return new CteRecepcaoEventoV4(configuracaoWsdl); } @@ -154,11 +155,11 @@ public static CTeDistDFeInteresse CriaWsdlCTeDistDFeInteresse(ConfiguracaoServic } - private static WsdlConfiguracao CriaConfiguracao(string url, ConfiguracaoServico configuracaoServico, X509Certificate2 certificado) + private static WsdlConfiguracao CriaConfiguracao(string url, ConfiguracaoServico configuracaoServico, X509Certificate2 certificado, Estado? ufUrl = null) { var configServico = configuracaoServico ?? ConfiguracaoServico.Instancia; - var codigoEstado = configServico.cUF.GetCodigoIbgeEmString(); + var codigoEstado = ufUrl.HasValue ? ufUrl.Value.GetCodigoIbgeEmString() : configServico.cUF.GetCodigoIbgeEmString(); var certificadoDigital = certificado ?? configServico.X509Certificate2; var versaoEmString = configServico.VersaoLayout.GetString(); var timeOut = configServico.TimeOut; diff --git a/DFe.Testes/DFe.Testes.csproj b/DFe.Testes/DFe.Testes.csproj index cc90271e4..a7deb0021 100644 --- a/DFe.Testes/DFe.Testes.csproj +++ b/DFe.Testes/DFe.Testes.csproj @@ -18,8 +18,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.Testes/Gerais/ObterIdTestes.cs b/DFe.Testes/Gerais/ObterIdTestes.cs new file mode 100644 index 000000000..2733d6e47 --- /dev/null +++ b/DFe.Testes/Gerais/ObterIdTestes.cs @@ -0,0 +1,61 @@ +using DFe.Classes.Entidades; +using DFe.Classes.Flags; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using NFe.Classes.Servicos.Tipos; +using NFe.Utils.Evento; +using NFe.Utils.Inutilizacao; + +namespace DFe.Testes.Gerais +{ + /// + /// Vetores ouro do Id assinável de evento (infEvento/@Id) e de inutilização (infInut/@Id). + /// + /// Esses Ids também são calculados internamente pelos métodos que assinam (ServicosNFe). Se o formato + /// divergir, o digest da assinatura não bate e o SEFAZ rejeita — por isso os valores esperados aqui são + /// literais do layout, e não o resultado do próprio método. + /// + /// + [TestClass] + public class ObterIdTestes + { + /// Chave de NF-e (modelo 55) com CNPJ alfanumérico, conforme NT Conjunta 2025.001 + private const string ChaveNFe = "522507PC3D315K000193550010000000011000000018"; + + /// Chave de NFC-e (modelo 65), 100% numérica + private const string ChaveNFCe = "23190811820016000167650010000000221100000227"; + + private const string CnpjNumerico = "11820016000167"; + private const string CnpjAlfanumerico = "PC3D315K000193"; + + [TestMethod] + [DataRow(NFeTipoEvento.TeNfeCancelamento, ChaveNFe, 1, "ID110111" + ChaveNFe + "01", + DisplayName = "Cancelamento, chave com CNPJ alfanumérico")] + [DataRow(NFeTipoEvento.TeNfeCartaCorrecao, ChaveNFe, 2, "ID110110" + ChaveNFe + "02", + DisplayName = "Carta de Correção, 2ª sequência")] + [DataRow(NFeTipoEvento.TeMdCienciaDaOperacao, ChaveNFCe, 1, "ID210210" + ChaveNFCe + "01", + DisplayName = "Ciência da Operação, chave numérica")] + [DataRow(NFeTipoEvento.TeMdCienciaDaOperacao, ChaveNFCe, 11, "ID210210" + ChaveNFCe + "11", + DisplayName = "Sequência com 2 dígitos não recebe zero à esquerda")] + public void ObterIdEvento_SegueOLayout(NFeTipoEvento tpEvento, string chNFe, int nSeqEvento, string esperado) + { + Assert.AreEqual(esperado, Extevento.ObterId(tpEvento, chNFe, nSeqEvento)); + } + + [TestMethod] + [DataRow(ModeloDocumento.NFe, CnpjNumerico, 1, 1, 10, + "ID3525" + CnpjNumerico + "55" + "001" + "000000001" + "000000010", + DisplayName = "NF-e, modelo 55")] + [DataRow(ModeloDocumento.NFCe, CnpjNumerico, 1, 1, 10, + "ID3525" + CnpjNumerico + "65" + "001" + "000000001" + "000000010", + DisplayName = "NFC-e, modelo 65")] + [DataRow(ModeloDocumento.NFe, CnpjAlfanumerico, 12, 5, 5, + "ID3525" + CnpjAlfanumerico + "55" + "012" + "000000005" + "000000005", + DisplayName = "CNPJ alfanumérico, faixa de um número só")] + public void ObterIdInutilizacao_SegueOLayout(ModeloDocumento modelo, string cnpj, int serie, + int numeroInicial, int numeroFinal, string esperado) + { + Assert.AreEqual(esperado, + ExtinutNFe.ObterId(Estado.SP, 25, cnpj, modelo, serie, numeroInicial, numeroFinal)); + } + } +} diff --git a/DFe.Utils/Assinatura/CertificadoDigital.cs b/DFe.Utils/Assinatura/CertificadoDigital.cs index 8104d944d..7f29bcbe0 100644 --- a/DFe.Utils/Assinatura/CertificadoDigital.cs +++ b/DFe.Utils/Assinatura/CertificadoDigital.cs @@ -52,9 +52,9 @@ public static class CertificadoDigital /// /// /// - public static X509Store ObterX509Store(OpenFlags openFlags) + public static X509Store ObterX509Store(OpenFlags openFlags, StoreLocation storeLocation = StoreLocation.CurrentUser) { - var store = new X509Store(StoreName.My, StoreLocation.CurrentUser); + X509Store store = new X509Store(StoreName.My, storeLocation); store.Open(openFlags); return store; } @@ -89,7 +89,7 @@ private static X509Certificate2 ObterDoArrayBytes(byte[] arrayBytes, string senh { try { - var certificado = new X509Certificate2(arrayBytes, senha, keyStorageFlag); + X509Certificate2 certificado = new X509Certificate2(arrayBytes, senha, keyStorageFlag); return certificado; } catch (Exception ex) @@ -102,12 +102,12 @@ private static X509Certificate2 ObterDoArrayBytes(byte[] arrayBytes, string senh /// Obtém um objeto pelo serial passado no parÂmetro /// /// - private static X509Certificate2 ObterDoRepositorio(string serial, OpenFlags opcoesDeAbertura) + private static X509Certificate2 ObterDoRepositorio(string serial, OpenFlags opcoesDeAbertura, StoreLocation storeLocation = StoreLocation.CurrentUser) { if (string.IsNullOrEmpty(serial)) throw new ArgumentException("O número de série do certificado digital não foi informado!"); X509Certificate2 certificado = null; - var store = ObterX509Store(opcoesDeAbertura); + var store = ObterX509Store(opcoesDeAbertura, storeLocation); try { foreach (var item in store.Certificates) @@ -133,9 +133,9 @@ private static X509Certificate2 ObterDoRepositorio(string serial, OpenFlags opco /// /// /// - private static X509Certificate2 ObterDoRepositorioPassandoPin(string serial, string senha = null) + private static X509Certificate2 ObterDoRepositorioPassandoPin(string serial, string senha = null, StoreLocation storeLocation = StoreLocation.CurrentUser) { - var certificado = ObterDoRepositorio(serial, OpenFlags.ReadOnly); + var certificado = ObterDoRepositorio(serial, OpenFlags.ReadOnly, storeLocation); if (string.IsNullOrEmpty(senha)) return certificado; certificado.DefinirPinParaChavePrivada(senha); return certificado; @@ -187,13 +187,13 @@ private static X509Certificate2 ObterDadosCertificado(ConfiguracaoCertificado co switch (configuracaoCertificado.TipoCertificado) { case TipoCertificado.A1Repositorio: - return ObterDoRepositorio(configuracaoCertificado.Serial, OpenFlags.MaxAllowed); + return ObterDoRepositorio(configuracaoCertificado.Serial, OpenFlags.MaxAllowed, configuracaoCertificado.StoreLocation); case TipoCertificado.A1ByteArray: return ObterDoArrayBytes(configuracaoCertificado.ArrayBytesArquivo, configuracaoCertificado.Senha, configuracaoCertificado.KeyStorageFlags); case TipoCertificado.A1Arquivo: return ObterDeArquivo(configuracaoCertificado.Arquivo, configuracaoCertificado.Senha, configuracaoCertificado.KeyStorageFlags); case TipoCertificado.A3: - return ObterDoRepositorioPassandoPin(configuracaoCertificado.Serial, configuracaoCertificado.Senha); + return ObterDoRepositorioPassandoPin(configuracaoCertificado.Serial, configuracaoCertificado.Senha, configuracaoCertificado.StoreLocation); default: throw new ArgumentOutOfRangeException(); } diff --git a/DFe.Utils/CertificadoDigitalUtils.cs b/DFe.Utils/CertificadoDigitalUtils.cs index 178f71ea8..9b9067c37 100644 --- a/DFe.Utils/CertificadoDigitalUtils.cs +++ b/DFe.Utils/CertificadoDigitalUtils.cs @@ -16,9 +16,9 @@ public class CertificadoDigitalUtils /// Exibe a lista de certificados instalados no PC e devolve o certificado selecionado /// /// - public static X509Certificate2 ListareObterDoRepositorio() + public static X509Certificate2 ListareObterDoRepositorio(StoreLocation storeLocation = StoreLocation.CurrentUser) { - var store = CertificadoDigital.ObterX509Store(OpenFlags.OpenExistingOnly | OpenFlags.ReadOnly); + var store = CertificadoDigital.ObterX509Store(OpenFlags.OpenExistingOnly | OpenFlags.ReadOnly, storeLocation); var collection = store.Certificates; var fcollection = collection.Find(X509FindType.FindByTimeValid, DateTime.Now, true); var scollection = X509Certificate2UI.SelectFromCollection(fcollection, "Certificados válidos:", "Selecione o certificado que deseja usar", diff --git a/DFe.Utils/ChaveFiscal.cs b/DFe.Utils/ChaveFiscal.cs index 30c7ed088..75cfe3dc6 100644 --- a/DFe.Utils/ChaveFiscal.cs +++ b/DFe.Utils/ChaveFiscal.cs @@ -57,16 +57,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) { - var chave = new StringBuilder(); + if (string.IsNullOrEmpty(cnpjEmitente)) + throw new ArgumentException("O CNPJ/CPF do emitente deve ser informado.", "cnpjEmitente"); - if (cnpjEmitente.Length < 14) - { - cnpjEmitente = cnpjEmitente.PadLeft(14, '0'); - } + // 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")) @@ -116,15 +124,20 @@ private static string ObterDigitoVerificador(string chave) } /// - /// Obtem o valor de um caractere + /// 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) /// /// /// internal static int ObterValorDoCaractere(char caractere) { - const int zeroASCII = 48; - var valor = caractere - zeroASCII; - return valor; + // 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'; } /// 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/DFe.Utils/ConfiguracaoCertificado.cs b/DFe.Utils/ConfiguracaoCertificado.cs index 158e28bf5..28ad75948 100644 --- a/DFe.Utils/ConfiguracaoCertificado.cs +++ b/DFe.Utils/ConfiguracaoCertificado.cs @@ -61,9 +61,11 @@ public class ConfiguracaoCertificado private string _cacheId; private byte[] _arrayBytesArquivo; private X509KeyStorageFlags _keyStorageFlags; + private StoreLocation _storeLocation; public ConfiguracaoCertificado() { + StoreLocation = StoreLocation.CurrentUser; KeyStorageFlags = X509KeyStorageFlags.MachineKeySet; SignatureMethodSignedXml = "http://www.w3.org/2000/09/xmldsig#rsa-sha1"; DigestMethodReference = "http://www.w3.org/2000/09/xmldsig#sha1"; @@ -195,5 +197,19 @@ public X509KeyStorageFlags KeyStorageFlags _keyStorageFlags = value; } } + + /// + /// + /// + public StoreLocation StoreLocation + { + get { return _storeLocation; } + set + { + if (value == _storeLocation) + return; + _storeLocation = value; + } + } } } diff --git a/MDFe.Servicos/EventosMDFe/Contratos/IServicoController.cs b/MDFe.Servicos/EventosMDFe/Contratos/IServicoController.cs index 161ea4685..4f523579b 100644 --- a/MDFe.Servicos/EventosMDFe/Contratos/IServicoController.cs +++ b/MDFe.Servicos/EventosMDFe/Contratos/IServicoController.cs @@ -42,5 +42,7 @@ namespace MDFe.Servicos.EventosMDFe.Contratos public interface IServicoController { MDFeRetEventoMDFe Executar(MDFeEletronico mdfe, byte sequenciaEvento, MDFeEventoContainer eventoContainer, MDFeTipoEvento tipoEvento, MDFeConfiguracao cfgMdfe = null); + + MDFeRetEventoMDFe Executar(MDFeComandoEvento comando, MDFeEventoContainer eventoContainer, MDFeTipoEvento tipoEvento, MDFeConfiguracao cfgMdfe = null); } } \ No newline at end of file diff --git a/MDFe.Servicos/EventosMDFe/EventoCancelar.cs b/MDFe.Servicos/EventosMDFe/EventoCancelar.cs index 4a9f55e5c..b74350df5 100644 --- a/MDFe.Servicos/EventosMDFe/EventoCancelar.cs +++ b/MDFe.Servicos/EventosMDFe/EventoCancelar.cs @@ -51,5 +51,12 @@ public MDFeRetEventoMDFe MDFeEventoCancelar(MDFeEletronico mdfe, byte sequenciaE return retorno; } + + public MDFeRetEventoMDFe MDFeEventoCancelar(MDFeComandoCancelamento comando, MDFeConfiguracao cfgMdfe = null) + { + var config = cfgMdfe ?? MDFeConfiguracao.Instancia; + var evento = ClassesFactory.CriaEvCancMDFe(comando.Protocolo, comando.Justificativa); + return new ServicoController().Executar(comando, evento, MDFeTipoEvento.Cancelamento, config); + } } } \ No newline at end of file diff --git a/MDFe.Servicos/EventosMDFe/EventoEncerramento.cs b/MDFe.Servicos/EventosMDFe/EventoEncerramento.cs index e7de8b41c..1ae049cba 100644 --- a/MDFe.Servicos/EventosMDFe/EventoEncerramento.cs +++ b/MDFe.Servicos/EventosMDFe/EventoEncerramento.cs @@ -63,5 +63,12 @@ public MDFeRetEventoMDFe MDFeEventoEncerramento(MDFeEletronico mdfe, Estado esta return retorno; } + + public MDFeRetEventoMDFe MDFeEventoEncerramento(MDFeComandoEncerramento comando, MDFeConfiguracao cfgMdfe = null) + { + var config = cfgMdfe ?? MDFeConfiguracao.Instancia; + var evento = ClassesFactory.CriaEvEncMDFe(comando.EstadoEncerramento, comando.CodigoMunicipioEncerramento, comando.Protocolo); + return new ServicoController().Executar(comando, evento, MDFeTipoEvento.Encerramento, config); + } } } \ No newline at end of file diff --git a/MDFe.Servicos/EventosMDFe/EventoInclusaoCondutor.cs b/MDFe.Servicos/EventosMDFe/EventoInclusaoCondutor.cs index 32d640724..c3798ff2a 100644 --- a/MDFe.Servicos/EventosMDFe/EventoInclusaoCondutor.cs +++ b/MDFe.Servicos/EventosMDFe/EventoInclusaoCondutor.cs @@ -46,10 +46,14 @@ public MDFeRetEventoMDFe MDFeEventoIncluirCondutor(MDFeEletronico mdfe, byte seq var config = cfgMdfe ?? MDFeConfiguracao.Instancia; var incluirCodutor = ClassesFactory.CriaEvIncCondutorMDFe(nome, cpf); + return new ServicoController().Executar(mdfe, sequenciaEvento, incluirCodutor, MDFeTipoEvento.InclusaoDeCondutor, config); + } - var retorno = new ServicoController().Executar(mdfe, sequenciaEvento, incluirCodutor, MDFeTipoEvento.InclusaoDeCondutor, config); - - return retorno; + public MDFeRetEventoMDFe MDFeEventoIncluirCondutor(MDFeComandoInclusaoCondutor comando, MDFeConfiguracao cfgMdfe = null) + { + var config = cfgMdfe ?? MDFeConfiguracao.Instancia; + var evento = ClassesFactory.CriaEvIncCondutorMDFe(comando.Nome, comando.CpfCondutor); + return new ServicoController().Executar(comando, evento, MDFeTipoEvento.InclusaoDeCondutor, config); } } } \ No newline at end of file diff --git a/MDFe.Servicos/EventosMDFe/EventoInclusaoDFe.cs b/MDFe.Servicos/EventosMDFe/EventoInclusaoDFe.cs index 9dd35c33c..953f86882 100644 --- a/MDFe.Servicos/EventosMDFe/EventoInclusaoDFe.cs +++ b/MDFe.Servicos/EventosMDFe/EventoInclusaoDFe.cs @@ -49,8 +49,14 @@ public MDFeRetEventoMDFe MDFeEventoIncluirDFe(MDFeEletronico mdfe, byte sequenci var config = cfgMdfe ?? MDFeConfiguracao.Instancia; var inclusao = ClassesFactory.CriaEvIncDFeMDFe(protocolo, codigoMunicipioCarregamento, nomeMunicipioCarregamento, informacoesDocumentos); - return new ServicoController().Executar(mdfe, sequenciaEvento, inclusao, MDFeTipoEvento.InclusaoDFe, config); } + + public MDFeRetEventoMDFe MDFeEventoIncluirDFe(MDFeComandoInclusaoDFe comando, MDFeConfiguracao cfgMdfe = null) + { + var config = cfgMdfe ?? MDFeConfiguracao.Instancia; + var evento = ClassesFactory.CriaEvIncDFeMDFe(comando.Protocolo, comando.CodigoMunicipioCarregamento, comando.NomeMunicipioCarregamento, comando.InformacoesDocumentos); + return new ServicoController().Executar(comando, evento, MDFeTipoEvento.InclusaoDFe, config); + } } } \ No newline at end of file diff --git a/MDFe.Servicos/EventosMDFe/EventoPagamentoOperacao.cs b/MDFe.Servicos/EventosMDFe/EventoPagamentoOperacao.cs index 26e27ef71..21ba82430 100644 --- a/MDFe.Servicos/EventosMDFe/EventoPagamentoOperacao.cs +++ b/MDFe.Servicos/EventosMDFe/EventoPagamentoOperacao.cs @@ -47,15 +47,18 @@ public MDFeRetEventoMDFe MDFeEventoPagamentoOperacao(Classes.Informacoes.MDFe md { var config = cfgMdfe ?? MDFeConfiguracao.Instancia; - var eventoPagamento = ClassesFactory.CriaEvPagtoOperMDFe( - protocolo, - infViagens, - infPagamentos - ); + var eventoPagamento = ClassesFactory.CriaEvPagtoOperMDFe(protocolo, infViagens, infPagamentos); var retorno = new ServicoController().Executar(mdfe, sequencia, eventoPagamento, MDFeTipoEvento.PagamentoOperacaoMDFe, config); return retorno; } + + public MDFeRetEventoMDFe MDFeEventoPagamentoOperacao(MDFeComandoPagamentoOperacao comando, MDFeConfiguracao cfgMdfe = null) + { + var config = cfgMdfe ?? MDFeConfiguracao.Instancia; + var evento = ClassesFactory.CriaEvPagtoOperMDFe(comando.Protocolo, comando.InfViagens, comando.Pagamentos); + return new ServicoController().Executar(comando, evento, MDFeTipoEvento.PagamentoOperacaoMDFe, config); + } } } \ No newline at end of file diff --git a/MDFe.Servicos/EventosMDFe/FactoryEvento.cs b/MDFe.Servicos/EventosMDFe/FactoryEvento.cs index f566c4bf0..416ae76a4 100644 --- a/MDFe.Servicos/EventosMDFe/FactoryEvento.cs +++ b/MDFe.Servicos/EventosMDFe/FactoryEvento.cs @@ -78,5 +78,40 @@ public static MDFeEventoMDFe CriaEvento(MDFeEletronico MDFe, MDFeTipoEvento tipo return eventoMDFe; } + + public static MDFeEventoMDFe CriaEvento(MDFeComandoEvento comando, MDFeTipoEvento tipoEvento, MDFeEventoContainer evento, MDFeConfiguracao cfgMdfe = null) + { + var config = cfgMdfe ?? MDFeConfiguracao.Instancia; + var eventoMDFe = new MDFeEventoMDFe + { + Versao = config.VersaoWebService.VersaoLayout, + InfEvento = new MDFeInfEvento(config.VersaoWebService.VersaoLayout) + { + Id = "ID" + (long)tipoEvento + comando.Chave + comando.SequenciaEvento.ToString("D2"), + TpAmb = config.VersaoWebService.TipoAmbiente, + COrgao = comando.UfEmitente, + ChMDFe = comando.Chave, + DetEvento = new MDFeDetEvento + { + VersaoServico = config.VersaoWebService.VersaoLayout, + EventoContainer = evento + }, + DhEvento = DateTime.Now, + NSeqEvento = comando.SequenciaEvento, + TpEvento = tipoEvento + } + }; + + eventoMDFe.InfEvento.CNPJ = comando.CnpjEmitente; + + if (!string.IsNullOrEmpty(comando.CpfEmitente)) + { + eventoMDFe.InfEvento.CPF = comando.CpfEmitente; + } + + eventoMDFe.Assinar(config); + + return eventoMDFe; + } } } \ No newline at end of file diff --git a/MDFe.Servicos/EventosMDFe/MDFeComandoCancelamento.cs b/MDFe.Servicos/EventosMDFe/MDFeComandoCancelamento.cs new file mode 100644 index 000000000..f93c59411 --- /dev/null +++ b/MDFe.Servicos/EventosMDFe/MDFeComandoCancelamento.cs @@ -0,0 +1,11 @@ +namespace MDFe.Servicos.EventosMDFe +{ + /// + /// Dados para emitir o evento de cancelamento de MDFe. + /// + public class MDFeComandoCancelamento : MDFeComandoEvento + { + public string Protocolo { get; set; } + public string Justificativa { get; set; } + } +} \ No newline at end of file diff --git a/MDFe.Servicos/EventosMDFe/MDFeComandoEncerramento.cs b/MDFe.Servicos/EventosMDFe/MDFeComandoEncerramento.cs new file mode 100644 index 000000000..91c165bb9 --- /dev/null +++ b/MDFe.Servicos/EventosMDFe/MDFeComandoEncerramento.cs @@ -0,0 +1,14 @@ +using DFe.Classes.Entidades; + +namespace MDFe.Servicos.EventosMDFe +{ + /// + /// Dados para emitir o evento de encerramento de MDFe. + /// + public class MDFeComandoEncerramento : MDFeComandoEvento + { + public string Protocolo { get; set; } + public Estado EstadoEncerramento { get; set; } + public long CodigoMunicipioEncerramento { get; set; } + } +} \ No newline at end of file diff --git a/MDFe.Servicos/EventosMDFe/MDFeComandoEvento.cs b/MDFe.Servicos/EventosMDFe/MDFeComandoEvento.cs new file mode 100644 index 000000000..5c3d60faa --- /dev/null +++ b/MDFe.Servicos/EventosMDFe/MDFeComandoEvento.cs @@ -0,0 +1,18 @@ +using DFe.Classes.Entidades; + +namespace MDFe.Servicos.EventosMDFe +{ + /// + /// Base abstrata com os dados mínimos necessários + /// para emitir um evento de MDFe sem depender + /// do objeto "" completo. + /// + public abstract class MDFeComandoEvento + { + public string Chave { get; set; } + public Estado UfEmitente { get; set; } + public string CnpjEmitente { get; set; } + public string CpfEmitente { get; set; } + public byte SequenciaEvento { get; set; } + } +} \ No newline at end of file diff --git a/MDFe.Servicos/EventosMDFe/MDFeComandoInclusaoCondutor.cs b/MDFe.Servicos/EventosMDFe/MDFeComandoInclusaoCondutor.cs new file mode 100644 index 000000000..80dd508f1 --- /dev/null +++ b/MDFe.Servicos/EventosMDFe/MDFeComandoInclusaoCondutor.cs @@ -0,0 +1,11 @@ +namespace MDFe.Servicos.EventosMDFe +{ + /// + /// Dados para emitir o evento de inclusão de condutor no MDFe. + /// + public class MDFeComandoInclusaoCondutor : MDFeComandoEvento + { + public string Nome { get; set; } + public string CpfCondutor { get; set; } + } +} \ No newline at end of file diff --git a/MDFe.Servicos/EventosMDFe/MDFeComandoInclusaoDFe.cs b/MDFe.Servicos/EventosMDFe/MDFeComandoInclusaoDFe.cs new file mode 100644 index 000000000..aa4dcda39 --- /dev/null +++ b/MDFe.Servicos/EventosMDFe/MDFeComandoInclusaoDFe.cs @@ -0,0 +1,16 @@ +using MDFe.Classes.Informacoes.Evento.CorpoEvento; +using System.Collections.Generic; + +namespace MDFe.Servicos.EventosMDFe +{ + /// + /// Dados para emitir o evento de inclusão de DF-e no MDFe. + /// + public class MDFeComandoInclusaoDFe : MDFeComandoEvento + { + public string Protocolo { get; set; } + public string CodigoMunicipioCarregamento { get; set; } + public string NomeMunicipioCarregamento { get; set; } + public List InformacoesDocumentos { get; set; } + } +} \ No newline at end of file diff --git a/MDFe.Servicos/EventosMDFe/MDFeComandoPagamentoOperacao.cs b/MDFe.Servicos/EventosMDFe/MDFeComandoPagamentoOperacao.cs new file mode 100644 index 000000000..5e3615fd1 --- /dev/null +++ b/MDFe.Servicos/EventosMDFe/MDFeComandoPagamentoOperacao.cs @@ -0,0 +1,15 @@ +using MDFe.Classes.Informacoes; +using System.Collections.Generic; + +namespace MDFe.Servicos.EventosMDFe +{ + /// + /// Dados para emitir o evento de pagamento de operação de transporte. + /// + public class MDFeComandoPagamentoOperacao : MDFeComandoEvento + { + public string Protocolo { get; set; } + public MDFeInfViagens InfViagens { get; set; } + public List Pagamentos { get; set; } + } +} \ No newline at end of file diff --git a/MDFe.Servicos/EventosMDFe/ServicoController.cs b/MDFe.Servicos/EventosMDFe/ServicoController.cs index 72bf4a0c7..5a32c60f0 100644 --- a/MDFe.Servicos/EventosMDFe/ServicoController.cs +++ b/MDFe.Servicos/EventosMDFe/ServicoController.cs @@ -67,5 +67,22 @@ public MDFeRetEventoMDFe Executar(MDFeEletronico mdfe, byte sequenciaEvento, MDF return retorno; } + + public MDFeRetEventoMDFe Executar(MDFeComandoEvento comando, MDFeEventoContainer eventoContainer, MDFeTipoEvento tipoEvento, MDFeConfiguracao cfgMdfe = null) + { + var config = cfgMdfe ?? MDFeConfiguracao.Instancia; + var evento = FactoryEvento.CriaEvento(comando, tipoEvento, eventoContainer, config); + + evento.ValidarSchema(config); + evento.SalvarXmlEmDisco(comando.Chave, config); + + var webService = WsdlFactory.CriaWsdlMDFeRecepcaoEvento(config); + var retornoXml = webService.mdfeRecepcaoEvento(evento.CriaXmlRequestWs()); + + var retorno = MDFeRetEventoMDFe.LoadXml(retornoXml.OuterXml, evento); + retorno.SalvarXmlEmDisco(comando.Chave, config); + + return retorno; + } } } \ No newline at end of file diff --git a/MDFe.Servicos/EventosMDFe/ServicoMDFeEvento.cs b/MDFe.Servicos/EventosMDFe/ServicoMDFeEvento.cs index 653dae482..78b9a3fa1 100644 --- a/MDFe.Servicos/EventosMDFe/ServicoMDFeEvento.cs +++ b/MDFe.Servicos/EventosMDFe/ServicoMDFeEvento.cs @@ -54,6 +54,11 @@ public MDFeRetEventoMDFe MDFeEventoIncluirCondutor( return eventoIncluirCondutor.MDFeEventoIncluirCondutor(mdfe, sequenciaEvento, nome, cpf, config); } + public MDFeRetEventoMDFe MDFeEventoIncluirCondutor(MDFeComandoInclusaoCondutor cmd, MDFeConfiguracao cfgMdfe = null) + { + return new EventoInclusaoCondutor().MDFeEventoIncluirCondutor(cmd, cfgMdfe); + } + public MDFeRetEventoMDFe MDFeEventoIncluirDFe( MDFeEletronica mdfe, byte sequenciaEvento, string protocolo, string codigoMunicipioCarregamento, string nomeMunicipioCarregamento, List informacoesDocumentos, @@ -66,6 +71,11 @@ public MDFeRetEventoMDFe MDFeEventoIncluirDFe( return eventoIncluirDFe.MDFeEventoIncluirDFe(mdfe, sequenciaEvento, protocolo, codigoMunicipioCarregamento, nomeMunicipioCarregamento, informacoesDocumentos, config); } + public MDFeRetEventoMDFe MDFeEventoIncluirDFe(MDFeComandoInclusaoDFe cmd, MDFeConfiguracao cfgMdfe = null) + { + return new EventoInclusaoDFe().MDFeEventoIncluirDFe(cmd, cfgMdfe); + } + public MDFeRetEventoMDFe MDFeEventoEncerramentoMDFeEventoEncerramento(MDFeEletronica mdfe, byte sequenciaEvento, string protocolo, MDFeConfiguracao cfgMdfe = null) { var config = cfgMdfe ?? MDFeConfiguracao.Instancia; @@ -85,6 +95,11 @@ public MDFeRetEventoMDFe MDFeEventoEncerramentoMDFeEventoEncerramento(MDFeEletro return eventoEncerramento.MDFeEventoEncerramento(mdfe, estadoEncerramento, codigoMunicipioEncerramento, sequenciaEvento, protocolo, config); } + public MDFeRetEventoMDFe MDFeEventoEncerramentoMDFeEventoEncerramento(MDFeComandoEncerramento cmd, MDFeConfiguracao cfgMdfe = null) + { + return new EventoEncerramento().MDFeEventoEncerramento(cmd, cfgMdfe); + } + public MDFeRetEventoMDFe MDFeEventoCancelar(MDFeEletronica mdfe, byte sequenciaEvento, string protocolo, string justificativa, MDFeConfiguracao cfgMdfe = null) { @@ -95,6 +110,11 @@ public MDFeRetEventoMDFe MDFeEventoCancelar(MDFeEletronica mdfe, byte sequenciaE return eventoCancelamento.MDFeEventoCancelar(mdfe, sequenciaEvento, protocolo, justificativa, config); } + public MDFeRetEventoMDFe MDFeEventoCancelar(MDFeComandoCancelamento cmd, MDFeConfiguracao cfgMdfe = null) + { + return new EventoCancelar().MDFeEventoCancelar(cmd, cfgMdfe); + } + public MDFeRetEventoMDFe MDFeEventoPagamentoOperacaoTransporte(MDFeEletronica mdfe, byte sequenciaEvneto, string protocolo, MDFeInfViagens infViagens, List infPagamentos, MDFeConfiguracao cfgMdfe = null) { @@ -105,5 +125,10 @@ public MDFeRetEventoMDFe MDFeEventoPagamentoOperacaoTransporte(MDFeEletronica md return eventoPagamentoOperacao.MDFeEventoPagamentoOperacao(mdfe, sequenciaEvneto, protocolo, infViagens, infPagamentos, config); } + + public MDFeRetEventoMDFe MDFeEventoPagamentoOperacaoTransporte(MDFeComandoPagamentoOperacao cmd, MDFeConfiguracao cfgMdfe = null) + { + return new EventoPagamentoOperacao().MDFeEventoPagamentoOperacao(cmd, cfgMdfe); + } } } \ No newline at end of file diff --git a/NFe.AppTeste/MainWindow.xaml.cs b/NFe.AppTeste/MainWindow.xaml.cs index df1180be2..38b287f21 100644 --- a/NFe.AppTeste/MainWindow.xaml.cs +++ b/NFe.AppTeste/MainWindow.xaml.cs @@ -1870,7 +1870,8 @@ protected virtual dest GetDestinatario(VersaoServico versao, ModeloDocumento mod { var dest = new dest(versao) { - CNPJ = "99999999000191", + CNPJ = "0ZEN3MS8000127", + //CNPJ = "99999999000191", //CPF = "99999999999", }; dest.xNome = "NF-E EMITIDA EM AMBIENTE DE HOMOLOGACAO - SEM VALOR FISCAL"; //Obrigatório para NFe e opcional para NFCe @@ -2106,7 +2107,7 @@ protected virtual det GetDetalhe(int i, CRT crt, ModeloDocumento modelo) { qTrib = 1, uTrib = "PC", - pISEspec = 0, + adRemIS = 0, pIS = 0, vIS = 0, cClassTribIS = "000001", @@ -2593,18 +2594,15 @@ private void BtnAdminCsc_Click(object sender, RoutedEventArgs e) 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):" + "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) + //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 apenas números!" + "A Raiz do CNPJ do contribuinte deve conter 8 caracteres, apenas números e letras maiúsculas!" ); - if (raizCnpj.Length != 8) - throw new Exception("A Raiz do CNPJ do contribuinte deve conter 8 caracteres!"); var idCsc = ""; var codigoCsc = ""; 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/leiauteNFe_v4.00.xsd b/NFe.AppTeste/Schemas/leiauteNFe_v4.00.xsd index 8c6ade56e..9e86de93b 100644 --- a/NFe.AppTeste/Schemas/leiauteNFe_v4.00.xsd +++ b/NFe.AppTeste/Schemas/leiauteNFe_v4.00.xsd @@ -111,13 +111,19 @@ 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). @@ -128,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. @@ -188,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 @@ -216,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. @@ -246,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: @@ -599,6 +617,17 @@ Este campo será obrigatoriamente preenchido com: + + + Inscrição do emitente na Suframa + + + + + + + + @@ -6633,7 +6662,7 @@ tipo de ato concessório: - + @@ -6707,7 +6736,7 @@ tipo de ato concessório: Descrição literal do status do serviço solicitado. - + Código da Mensagem. @@ -6726,7 +6755,7 @@ tipo de ato concessório: - + @@ -7462,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; @@ -7485,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; @@ -7494,6 +7524,7 @@ alterado para tamanho variavel 1-4. (NT2011/004) + diff --git a/NFe.Classes/Informacoes/Destinatario/dest.cs b/NFe.Classes/Informacoes/Destinatario/dest.cs index 3cb1a1f73..8c9cd5fec 100644 --- a/NFe.Classes/Informacoes/Destinatario/dest.cs +++ b/NFe.Classes/Informacoes/Destinatario/dest.cs @@ -30,18 +30,19 @@ /* http://www.zeusautomacao.com.br/ */ /* Rua Comendador Francisco josé da Cunha, 111 - Itabaiana - SE - 49500-000 */ /********************************************************************************/ + +using DFe.Classes.Flags; using System; using System.Xml.Serialization; -using DFe.Classes.Flags; -using NFe.Classes.Servicos.Tipos; namespace NFe.Classes.Informacoes.Destinatario { public class dest { - private const string ErroCpfCnpjPreenchidos = "Somente preencher um dos campos: CNPJ ou CPF, para um objeto do tipo dest!"; + private const string ErroCpfCnpjIdEstrangeiroPreenchidos = "Somente preencher um dos campos: CNPJ, CPF ou idEstrangeiro, para um objeto do tipo dest!"; private string cnpj; private string cpf; + private string _idEstrangeiro; private readonly VersaoServico _versao; /// @@ -66,13 +67,12 @@ public string CNPJ get { return cnpj; } set { - if (string.IsNullOrEmpty(value)) return; - if (string.IsNullOrEmpty(cpf)) - cnpj = value; - else - { - throw new ArgumentException(ErroCpfCnpjPreenchidos); - } + if (string.IsNullOrEmpty(value)) + return; + if (!string.IsNullOrEmpty(cpf) || !string.IsNullOrEmpty(idEstrangeiro)) + throw new ArgumentException(ErroCpfCnpjIdEstrangeiroPreenchidos); + + cnpj = value; } } @@ -84,20 +84,31 @@ public string CPF get { return cpf; } set { - if (string.IsNullOrEmpty(value)) return; - if (string.IsNullOrEmpty(cnpj)) - cpf = value; - else - { - throw new ArgumentException(ErroCpfCnpjPreenchidos); - } + if (string.IsNullOrEmpty(value)) + return; + if (!string.IsNullOrEmpty(cnpj) || !string.IsNullOrEmpty(idEstrangeiro)) + throw new ArgumentException(ErroCpfCnpjIdEstrangeiroPreenchidos); + + cpf = value; } } /// /// E03a - Identificador do destinatário, em caso de comprador estrangeiro /// - public string idEstrangeiro { get; set; } + public string idEstrangeiro + { + get { return _idEstrangeiro; } + set + { + if (string.IsNullOrEmpty(value)) + return; + if (!string.IsNullOrEmpty(CNPJ) || !string.IsNullOrEmpty(CPF)) + throw new ArgumentException(ErroCpfCnpjIdEstrangeiroPreenchidos); + + _idEstrangeiro = value; + } + } /// /// E04 - Razão Social ou nome do destinatário diff --git a/NFe.Classes/Informacoes/Detalhe/Tributacao/Federal/IS.cs b/NFe.Classes/Informacoes/Detalhe/Tributacao/Federal/IS.cs index 951d93b15..06ad15da1 100644 --- a/NFe.Classes/Informacoes/Detalhe/Tributacao/Federal/IS.cs +++ b/NFe.Classes/Informacoes/Detalhe/Tributacao/Federal/IS.cs @@ -80,7 +80,7 @@ public decimal pIS /// UB07 - Alíquota específica por unidade de medida apropriada (em percentual) /// [XmlElement(Order = 5)] - public decimal? pISEspec + public decimal? adRemIS { get => _pIsEspec.Arredondar(4); set => _pIsEspec = value.Arredondar(4); @@ -112,6 +112,6 @@ public decimal vIS set => _vIs = value.Arredondar(2); } - public bool ShouldSerializepISEspec() => pISEspec.HasValue; + public bool ShouldSerializeadRemIS() => adRemIS.HasValue; } } \ No newline at end of file diff --git a/NFe.Classes/Informacoes/Detalhe/Tributacao/Federal/Tipos/IPITipos.cs b/NFe.Classes/Informacoes/Detalhe/Tributacao/Federal/Tipos/IPITipos.cs index b6d87d258..c318df8bb 100644 --- a/NFe.Classes/Informacoes/Detalhe/Tributacao/Federal/Tipos/IPITipos.cs +++ b/NFe.Classes/Informacoes/Detalhe/Tributacao/Federal/Tipos/IPITipos.cs @@ -58,97 +58,97 @@ public enum CSTIPI /// [Description("Entrada com recuperação de crédito")] [XmlEnum("00")] - ipi00, - - /// - /// 49 - Outras entradas - /// - [Description("Outras entradas")] - [XmlEnum("49")] - ipi49, - - /// - /// 50 - Saída tributada - /// - [Description("Saída tributada")] - [XmlEnum("50")] - ipi50, - - /// - /// 99 - Outras saídas - /// - [Description("Outras saídas")] - [XmlEnum("99")] - ipi99, + ipi00 = 0, /// /// 01 - Entrada tributada com alíquota zero /// [Description("Entrada tributada com alíquota zero")] [XmlEnum("01")] - ipi01, + ipi01 = 1, /// /// 02 - Entrada isenta /// [Description("Entrada isenta")] [XmlEnum("02")] - ipi02, + ipi02 = 2, /// /// 03 - Entrada não-tributada /// [Description("Entrada não-tributada")] [XmlEnum("03")] - ipi03, + ipi03 = 3, /// /// 04 - Entrada imune /// [Description("Entrada imune")] [XmlEnum("04")] - ipi04, + ipi04 = 4, /// /// 05 - Entrada com suspensão /// [Description("Entrada com suspensão")] [XmlEnum("05")] - ipi05, + ipi05 = 5, + + /// + /// 49 - Outras entradas + /// + [Description("Outras entradas")] + [XmlEnum("49")] + ipi49 = 49, + + /// + /// 50 - Saída tributada + /// + [Description("Saída tributada")] + [XmlEnum("50")] + ipi50 = 50, /// /// 51 - Saída tributada com alíquota zero /// [Description("Saída tributada com alíquota zero")] [XmlEnum("51")] - ipi51, + ipi51 = 51, /// /// 52 - Saída isenta /// [Description("Saída isenta")] [XmlEnum("52")] - ipi52, + ipi52 = 52, /// /// 53 - Saída não-tributada /// [Description("Saída não-tributada")] [XmlEnum("53")] - ipi53, + ipi53 = 53, /// /// 54 - Saída imune /// [Description("Saída imune")] [XmlEnum("54")] - ipi54, + ipi54 = 54, /// /// 55 - Saída com suspensão /// [Description("Saída com suspensão")] [XmlEnum("55")] - ipi55 + ipi55 = 55, + + /// + /// 99 - Outras saídas + /// + [Description("Outras saídas")] + [XmlEnum("99")] + ipi99 = 99 } } \ No newline at end of file diff --git a/NFe.Classes/Informacoes/Emitente/emit.cs b/NFe.Classes/Informacoes/Emitente/emit.cs index 238d8f5c4..fdd7b8abf 100644 --- a/NFe.Classes/Informacoes/Emitente/emit.cs +++ b/NFe.Classes/Informacoes/Emitente/emit.cs @@ -50,14 +50,12 @@ public string CNPJ get { return _cnpj; } set { - if (string.IsNullOrEmpty(value)) return; - if (string.IsNullOrEmpty(_cpf)) - _cnpj = Regex.Match(value, @"[0-9A-Z]+").Value; - - else - { + if (string.IsNullOrEmpty(value)) + return; + if (!string.IsNullOrEmpty(_cpf)) throw new ArgumentException(ErroCpfCnpjPreenchidos); - } + + _cnpj = Regex.Match(value, @"[0-9A-Z]+").Value; } } @@ -69,13 +67,12 @@ public string CPF get { return _cpf; } set { - if (string.IsNullOrEmpty(value)) return; - if (string.IsNullOrEmpty(_cnpj)) - _cpf = Regex.Match(value, @"\d+").Value; - else - { + if (string.IsNullOrEmpty(value)) + return; + if (!string.IsNullOrEmpty(_cnpj)) throw new ArgumentException(ErroCpfCnpjPreenchidos); - } + + _cpf = Regex.Match(value, @"\d+").Value; } } diff --git a/NFe.Classes/Informacoes/Identificacao/Tipos/ideTipos.cs b/NFe.Classes/Informacoes/Identificacao/Tipos/ideTipos.cs index 065164a7e..73d2160d6 100644 --- a/NFe.Classes/Informacoes/Identificacao/Tipos/ideTipos.cs +++ b/NFe.Classes/Informacoes/Identificacao/Tipos/ideTipos.cs @@ -148,6 +148,7 @@ public enum DestinoOperacao /// 3 - DANFe Simplificado; /// 4 - DANFe NFC-e; /// 5 - DANFe NFC-e em mensagem eletrônica + /// 6 - DANFe Simplificado Tipo 2 /// public enum TipoImpressao { @@ -191,7 +192,14 @@ public enum TipoImpressao /// [Description("DANFe NFC-e em mensagem eletrônica")] [XmlEnum("5")] - tiMsgEletronica = 5 + tiMsgEletronica = 5, + + /// + /// 6 - DANFe Simplificado Tipo 2 + /// + [Description("DANFe Simplificado Tipo 2")] + [XmlEnum("6")] + tiSimplificadoTp2 = 6 } /// @@ -591,7 +599,8 @@ public enum TpNotaDebito /// 02 - Apropriação de crédito presumido de IBS sobre o saldo devedor na ZFM (art. 450, § 1º, LC 214/25) /// 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; + /// 05 - Transferência de crédito na sucessão + /// 06 - Retorno por recusa parcial na entrega /// public enum TpNotaCredito { @@ -611,9 +620,13 @@ public enum TpNotaCredito [XmlEnum("04")] ReducaoDeValores, - [Description("Transferência de crédito na sucessão;")] + [Description("Transferência de crédito na sucessão")] [XmlEnum("05")] - TfCreditoSucessao + TfCreditoSucessao, + + [Description("Retorno por recusa parcial na entrega")] + [XmlEnum("06")] + RetornoPorRecusaParcialNaEntrega } /// diff --git a/NFe.Classes/Informacoes/Transporte/transporta.cs b/NFe.Classes/Informacoes/Transporte/transporta.cs index 288fddaec..fdca1caa0 100644 --- a/NFe.Classes/Informacoes/Transporte/transporta.cs +++ b/NFe.Classes/Informacoes/Transporte/transporta.cs @@ -48,13 +48,12 @@ public string CNPJ get { return cnpj; } set { - if (string.IsNullOrEmpty(value)) return; - if (string.IsNullOrEmpty(cpf)) - cnpj = value; - else - { + if (string.IsNullOrEmpty(value)) + return; + if (!string.IsNullOrEmpty(cpf)) throw new ArgumentException(ErroCpfCnpjPreenchidos); - } + + cnpj = value; } } @@ -66,13 +65,12 @@ public string CPF get { return cpf; } set { - if (string.IsNullOrEmpty(value)) return; - if (string.IsNullOrEmpty(cnpj)) - cpf = value; - else - { + if (string.IsNullOrEmpty(value)) + return; + if (!string.IsNullOrEmpty(cnpj)) throw new ArgumentException(ErroCpfCnpjPreenchidos); - } + + cpf = value; } } diff --git a/NFe.Classes/Informacoes/autXML.cs b/NFe.Classes/Informacoes/autXML.cs index 026bfc4d2..335d01b0d 100644 --- a/NFe.Classes/Informacoes/autXML.cs +++ b/NFe.Classes/Informacoes/autXML.cs @@ -48,13 +48,12 @@ public string CNPJ get { return cnpj; } set { - if (string.IsNullOrEmpty(value)) return; - if (string.IsNullOrEmpty(cpf)) - cnpj = value; - else - { + if (string.IsNullOrEmpty(value)) + return; + if (!string.IsNullOrEmpty(cpf)) throw new ArgumentException(ErroCpfCnpjPreenchidos); - } + + cnpj = value; } } @@ -66,13 +65,12 @@ public string CPF get { return cpf; } set { - if (string.IsNullOrEmpty(value)) return; - if (string.IsNullOrEmpty(cnpj)) - cpf = value; - else - { + if (string.IsNullOrEmpty(value)) + return; + if (!string.IsNullOrEmpty(cnpj)) throw new ArgumentException(ErroCpfCnpjPreenchidos); - } + + cpf = value; } } } diff --git a/NFe.Classes/Informacoes/entrega.cs b/NFe.Classes/Informacoes/entrega.cs index fbe1f8e07..83852d663 100644 --- a/NFe.Classes/Informacoes/entrega.cs +++ b/NFe.Classes/Informacoes/entrega.cs @@ -49,13 +49,12 @@ public string CNPJ get { return cnpj; } set { - if (string.IsNullOrEmpty(value)) return; - if (string.IsNullOrEmpty(cpf)) - cnpj = value; - else - { + if (string.IsNullOrEmpty(value)) + return; + if (!string.IsNullOrEmpty(cpf)) throw new ArgumentException(ErroCpfCnpjPreenchidos); - } + + cnpj = value; } } @@ -67,13 +66,12 @@ public string CPF get { return cpf; } set { - if (string.IsNullOrEmpty(value)) return; - if (string.IsNullOrEmpty(cnpj)) - cpf = value; - else - { + if (string.IsNullOrEmpty(value)) + return; + if (!string.IsNullOrEmpty(cnpj)) throw new ArgumentException(ErroCpfCnpjPreenchidos); - } + + cpf = value; } } diff --git a/NFe.Classes/Informacoes/retirada.cs b/NFe.Classes/Informacoes/retirada.cs index 72d3b0138..f90977335 100644 --- a/NFe.Classes/Informacoes/retirada.cs +++ b/NFe.Classes/Informacoes/retirada.cs @@ -49,13 +49,12 @@ public string CNPJ get { return cnpj; } set { - if (string.IsNullOrEmpty(value)) return; - if (string.IsNullOrEmpty(cpf)) - cnpj = value; - else - { + if (string.IsNullOrEmpty(value)) + return; + if (!string.IsNullOrEmpty(cpf)) throw new ArgumentException(ErroCpfCnpjPreenchidos); - } + + cnpj = value; } } @@ -67,13 +66,12 @@ public string CPF get { return cpf; } set { - if (string.IsNullOrEmpty(value)) return; - if (string.IsNullOrEmpty(cnpj)) - cpf = value; - else - { + if (string.IsNullOrEmpty(value)) + return; + if (!string.IsNullOrEmpty(cnpj)) throw new ArgumentException(ErroCpfCnpjPreenchidos); - } + + cpf = value; } } diff --git a/NFe.Classes/Servicos/ConsultaCadastro/infConsEnv.cs b/NFe.Classes/Servicos/ConsultaCadastro/infConsEnv.cs index fddac9e5e..6cf172235 100644 --- a/NFe.Classes/Servicos/ConsultaCadastro/infConsEnv.cs +++ b/NFe.Classes/Servicos/ConsultaCadastro/infConsEnv.cs @@ -65,13 +65,12 @@ public string IE get { return _ie; } set { - if (string.IsNullOrEmpty(value)) return; - if (string.IsNullOrEmpty(CNPJ) & string.IsNullOrEmpty(CPF)) - _ie = value; - else - { + if (string.IsNullOrEmpty(value)) + return; + if (!string.IsNullOrEmpty(CNPJ) || !string.IsNullOrEmpty(CPF)) throw new ArgumentException(ErroCpfCnpjIePreenchidos); - } + + _ie = value; } } @@ -84,13 +83,12 @@ public string CNPJ get { return _cnpj; } set { - if (string.IsNullOrEmpty(value)) return; - if (string.IsNullOrEmpty(CPF) & string.IsNullOrEmpty(IE)) - _cnpj = value; - else - { + if (string.IsNullOrEmpty(value)) + return; + if (!string.IsNullOrEmpty(CPF) || !string.IsNullOrEmpty(IE)) throw new ArgumentException(ErroCpfCnpjIePreenchidos); - } + + _cnpj = value; } } @@ -103,13 +101,12 @@ public string CPF get { return _cpf; } set { - if (string.IsNullOrEmpty(value)) return; - if (string.IsNullOrEmpty(CNPJ) & string.IsNullOrEmpty(IE)) - _cpf = value; - else - { + if (string.IsNullOrEmpty(value)) + return; + if (!string.IsNullOrEmpty(CNPJ) || !string.IsNullOrEmpty(IE)) throw new ArgumentException(ErroCpfCnpjIePreenchidos); - } + + _cpf = value; } } } diff --git a/NFe.Classes/Servicos/DistribuicaoDFe/distDFeInt.cs b/NFe.Classes/Servicos/DistribuicaoDFe/distDFeInt.cs index 8015a9795..5ad92bd01 100644 --- a/NFe.Classes/Servicos/DistribuicaoDFe/distDFeInt.cs +++ b/NFe.Classes/Servicos/DistribuicaoDFe/distDFeInt.cs @@ -71,13 +71,12 @@ public string CNPJ get { return _cNPJ; } set { - if (string.IsNullOrEmpty(value)) return; - if (string.IsNullOrEmpty(_cPF)) - _cNPJ = value; - else - { + if (string.IsNullOrEmpty(value)) + return; + if (!string.IsNullOrEmpty(_cPF)) throw new ArgumentException(ErroCpfCnpjPreenchidos); - } + + _cNPJ = value; } } @@ -89,13 +88,12 @@ public string CPF get { return _cPF; } set { - if (string.IsNullOrEmpty(value)) return; - if (string.IsNullOrEmpty(_cNPJ)) - _cPF = value; - else - { + if (string.IsNullOrEmpty(value)) + return; + if (!string.IsNullOrEmpty(_cNPJ)) throw new ArgumentException(ErroCpfCnpjPreenchidos); - } + + _cPF = value; } } diff --git a/NFe.Classes/Servicos/Evento/dest.cs b/NFe.Classes/Servicos/Evento/dest.cs index 725018543..7e6c820ae 100644 --- a/NFe.Classes/Servicos/Evento/dest.cs +++ b/NFe.Classes/Servicos/Evento/dest.cs @@ -58,13 +58,12 @@ public string CNPJ get { return _cnpj; } set { - if (string.IsNullOrEmpty(value)) return; - if (string.IsNullOrEmpty(CPF) & string.IsNullOrEmpty(idEstrangeiro)) - _cnpj = value; - else - { + if (string.IsNullOrEmpty(value)) + return; + if (!string.IsNullOrEmpty(CPF) || !string.IsNullOrEmpty(idEstrangeiro)) throw new ArgumentException(ErroCpfCnpjIdEstrangeiroPreenchidos); - } + + _cnpj = value; } } @@ -76,13 +75,12 @@ public string CPF get { return _cpf; } set { - if (string.IsNullOrEmpty(value)) return; - if (string.IsNullOrEmpty(CNPJ) & string.IsNullOrEmpty(idEstrangeiro)) - _cpf = value; - else - { + if (string.IsNullOrEmpty(value)) + return; + if (!string.IsNullOrEmpty(CNPJ) || !string.IsNullOrEmpty(idEstrangeiro)) throw new ArgumentException(ErroCpfCnpjIdEstrangeiroPreenchidos); - } + + _cpf = value; } } @@ -95,12 +93,10 @@ public string idEstrangeiro set { if (string.IsNullOrEmpty(value)) return; - if (string.IsNullOrEmpty(CNPJ) & string.IsNullOrEmpty(CPF)) - _idEstrangeiro = value; - else - { + if (!string.IsNullOrEmpty(CNPJ) || !string.IsNullOrEmpty(CPF)) throw new ArgumentException(ErroCpfCnpjIdEstrangeiroPreenchidos); - } + + _idEstrangeiro = value; } } diff --git a/NFe.Classes/Servicos/Evento/infEventoEnv.cs b/NFe.Classes/Servicos/Evento/infEventoEnv.cs index 1c4592c3f..9c0b79ac1 100644 --- a/NFe.Classes/Servicos/Evento/infEventoEnv.cs +++ b/NFe.Classes/Servicos/Evento/infEventoEnv.cs @@ -69,13 +69,12 @@ public string CNPJ get { return cnpj; } set { - if (string.IsNullOrEmpty(value)) return; - if (string.IsNullOrEmpty(cpf)) - cnpj = value; - else - { + if (string.IsNullOrEmpty(value)) + return; + if (!string.IsNullOrEmpty(cpf)) throw new ArgumentException(ErroCpfCnpjPreenchidos); - } + + cnpj = value; } } @@ -87,13 +86,12 @@ public string CPF get { return cpf; } set { - if (string.IsNullOrEmpty(value)) return; - if (string.IsNullOrEmpty(cnpj)) - cpf = value; - else - { + if (string.IsNullOrEmpty(value)) + return; + if (!string.IsNullOrEmpty(cnpj)) throw new ArgumentException(ErroCpfCnpjPreenchidos); - } + + cpf = value; } } diff --git a/NFe.Classes/Servicos/Evento/infEventoRet.cs b/NFe.Classes/Servicos/Evento/infEventoRet.cs index 85b927e61..f33ee61f9 100644 --- a/NFe.Classes/Servicos/Evento/infEventoRet.cs +++ b/NFe.Classes/Servicos/Evento/infEventoRet.cs @@ -110,13 +110,12 @@ public string CNPJDest get { return cnpj; } set { - if (string.IsNullOrEmpty(value)) return; - if (string.IsNullOrEmpty(cpf)) - cnpj = value; - else - { + if (string.IsNullOrEmpty(value)) + return; + if (!string.IsNullOrEmpty(cpf)) throw new ArgumentException(ErroCpfCnpjPreenchidos); - } + + cnpj = value; } } @@ -128,13 +127,12 @@ public string CPFDest get { return cpf; } set { - if (string.IsNullOrEmpty(value)) return; - if (string.IsNullOrEmpty(cnpj)) - cpf = value; - else - { + if (string.IsNullOrEmpty(value)) + return; + if (!string.IsNullOrEmpty(cnpj)) throw new ArgumentException(ErroCpfCnpjPreenchidos); - } + + cpf = value; } } diff --git a/NFe.Danfe.AppTeste.Fast/MainWindow.xaml b/NFe.Danfe.AppTeste.Fast/MainWindow.xaml index cbd75fdf1..36f02701b 100644 --- a/NFe.Danfe.AppTeste.Fast/MainWindow.xaml +++ b/NFe.Danfe.AppTeste.Fast/MainWindow.xaml @@ -217,6 +217,8 @@ VerticalAlignment="Top" Width="120" Click="BtnNfceDanfe_Click" Margin="5,10,5,0" />