diff --git a/src/NetDevPack.Brasil/Documentos/TituloEleitor.cs b/src/NetDevPack.Brasil/Documentos/TituloEleitor.cs new file mode 100644 index 0000000..6cb5622 --- /dev/null +++ b/src/NetDevPack.Brasil/Documentos/TituloEleitor.cs @@ -0,0 +1,19 @@ +using NetDevPack.Utilities; +using NetDevPack.Brasil.Documentos.Validacao; +using NetDevPack.Domain; + +namespace NetDevPack.Brasil.Documentos +{ + public class TituloEleitor + { + public string Numero { get; } + + public TituloEleitor(string numero) + { + Numero = numero.OnlyNumbers(); + if (!EstaValido()) throw new DomainException("TituloEleitor Invalido"); + } + + public bool EstaValido() => new TituloEleitorValidator(Numero).EstaValido(); + } +} diff --git a/src/NetDevPack.Brasil/Documentos/Validacao/DigitoVerificador.cs b/src/NetDevPack.Brasil/Documentos/Validacao/DigitoVerificador.cs index 060432c..5f58dab 100644 --- a/src/NetDevPack.Brasil/Documentos/Validacao/DigitoVerificador.cs +++ b/src/NetDevPack.Brasil/Documentos/Validacao/DigitoVerificador.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; namespace NetDevPack.Brasil.Documentos.Validacao { @@ -6,6 +7,9 @@ public class DigitoVerificador { private string _numero; private int _modulo = 11; + private int _valorLimite = 0; + private int _valorSubstituto = 0; + private bool _limite = false; private bool _somarAlgarismos; private bool _complementarDoModulo = true; private readonly List _multiplicadores = new List { 2, 3, 4, 5, 6, 7, 8, 9 }; @@ -74,6 +78,28 @@ public DigitoVerificador SemComplementarDoModulo() return this; } + public DigitoVerificador InvertendoNumero() + { + _numero = new string(_numero.Reverse().ToArray()); + return this; + } + + public DigitoVerificador HabilitaLimiteModulo(int valorLimite, int valorSubstituto = 0) + { + _valorLimite = valorLimite; + _valorSubstituto = valorSubstituto; + _limite = true; + return this; + } + + public DigitoVerificador DesabilitaLimiteModulo() + { + _limite = false; + _valorLimite = 0; + _valorSubstituto = 0; + return this; + } + public void AddDigito(string digito) => _numero = string.Concat(_numero, digito); public string CalculaDigito() => _numero.Length > 0 ? ObterSomaDosDigitos() : string.Empty ; @@ -91,6 +117,7 @@ private string ObterSomaDosDigitos() var mod = (soma % _modulo); var resultado = _complementarDoModulo ? _modulo - mod : mod; + resultado = _limite && mod >= _valorLimite ? _valorSubstituto : resultado; return _substituicoes.ContainsKey(resultado) ? _substituicoes[resultado] : resultado.ToString(); } diff --git a/src/NetDevPack.Brasil/Documentos/Validacao/TituloEleitorValidator.cs b/src/NetDevPack.Brasil/Documentos/Validacao/TituloEleitorValidator.cs new file mode 100644 index 0000000..4d848ea --- /dev/null +++ b/src/NetDevPack.Brasil/Documentos/Validacao/TituloEleitorValidator.cs @@ -0,0 +1,76 @@ +using NetDevPack.Utilities; +using System.Collections.Generic; + +namespace NetDevPack.Brasil.Documentos.Validacao +{ + public class TituloEleitorValidator + { + private const int TamanhoTituloEleitor = 12; + private readonly string _tituloEleitorTratado; + private static readonly HashSet _titulosEleitorInvalidos = new HashSet + { + "000000000000", + "111111111111", + "222222222222", + "333333333333", + "444444444444", + "555555555555", + "666666666666", + "777777777777", + "888888888888", + "999999999999" + }; + + public TituloEleitorValidator(string numero) => _tituloEleitorTratado = numero.OnlyNumbers(); + + public bool EstaValido() + { + if (!PossuiTamanhoValido()) return false; + if (PossuiDigitosRepetidos()) return false; + return PossuiDigitosValidos(); + } + + private bool PossuiTamanhoValido() => _tituloEleitorTratado.Length == TamanhoTituloEleitor; + + private bool PossuiDigitosRepetidos() => _titulosEleitorInvalidos.Contains(_tituloEleitorTratado); + + private bool PossuiDigitosValidos() + { + var primeiroDigito = ObtemPrimeiroDigito(); + var segundoDigito = ObtemSegundoDigito(primeiroDigito); + + return DigitosCalculadosSaoIguaisAosDigitosInformados(primeiroDigito, segundoDigito); + } + + private string ObtemPrimeiroDigito() + { + var primeiraParte = _tituloEleitorTratado.Substring(0, TamanhoTituloEleitor - 4); + + return new DigitoVerificador(primeiraParte) + .Modulo(11) + .SemComplementarDoModulo() + .InvertendoNumero() + .HabilitaLimiteModulo(10) + .CalculaDigito(); + } + + private string ObtemSegundoDigito(string primeiroDigito) + { + var segundaParte = string.Concat(_tituloEleitorTratado.Substring(8, 2), primeiroDigito); + + return new DigitoVerificador(segundaParte) + .Modulo(11) + .SemComplementarDoModulo() + .InvertendoNumero() + .HabilitaLimiteModulo(10) + .ComMultiplicadoresDeAte(7, 9) + .CalculaDigito(); + } + + + private bool DigitosCalculadosSaoIguaisAosDigitosInformados(string primeiroDigito, string segundoDigito) + { + return string.Concat(primeiroDigito, segundoDigito) == _tituloEleitorTratado.Substring(TamanhoTituloEleitor - 2, 2); + } + } +} diff --git a/tests/NetDevPack.Brasil.Tests/Documentos/TituloEleitorValidadorTest.cs b/tests/NetDevPack.Brasil.Tests/Documentos/TituloEleitorValidadorTest.cs new file mode 100644 index 0000000..b045007 --- /dev/null +++ b/tests/NetDevPack.Brasil.Tests/Documentos/TituloEleitorValidadorTest.cs @@ -0,0 +1,62 @@ +using FluentAssertions; +using NetDevPack.Brasil.Documentos; +using NetDevPack.Brasil.Documentos.Validacao; +using NetDevPack.Domain; +using System; +using Xunit; + +namespace NetDevPack.Brasil.Tests +{ + public class TituloEleitorValidadorTest + { + [Theory(DisplayName = "TituloEleitor ReturnDomainException")] + [InlineData("000000000000")] + [InlineData("1111 1111 1111")] + [InlineData("2222 2222 2222")] + [InlineData("3333 3333 3333")] + [InlineData("4444 4444 4444")] + [InlineData("5555 5555 5555")] + [InlineData("6666 6666 6666")] + [InlineData("7777 7777 7777")] + [InlineData("8888 8888 8888")] + [InlineData("9999 9999 9999")] + [InlineData("456123456789")] + [InlineData("456123456789022")] + [InlineData("258365200125")] + [InlineData("002365896322")] + [InlineData("147258369789")] + [Trait("Category", "TituloEleitor Tests")] + public void TituloEleitor_PossuiDigitosRepetidos_ShouldReturnDomainException(string value) + { + // Arrange & Act + Action act = () => new TituloEleitor(value); + + // Assert + act.Should().ThrowExactly().WithMessage("TituloEleitor Invalido"); + } + + [Theory(DisplayName = "TituloEleitor ReturnTrue")] + [InlineData("007378060183")] + [InlineData("304083450132")] + [InlineData("223644070124")] + [InlineData("8018 6038 0159")] + [InlineData("7542 4177 0175")] + [InlineData("8720 0766 0191")] + [InlineData("6311 6330 1414")] + [InlineData("874726180620")] + [InlineData("067106681546")] + [InlineData("313246081040")] + [Trait("Category", "TituloEleitor Tests")] + public void TituloEleitor_NewTituloEleitor_ShouldReturnTrue(string value) + { + // Arrange + var tituloEleitor = new TituloEleitor(value); + + // Act + var result = new TituloEleitorValidator(tituloEleitor.Numero).EstaValido(); + + // Assert + result.Should().BeTrue(); + } + } +}