diff --git a/CHANGELOG.md b/CHANGELOG.md index a8d204d..1074ade 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Utilitário `list_all_legal_nature` [#653](https://github.com/brazilian-utils/python/pull/653) - Utilitário `is_valid_cnh` [#651](https://github.com/brazilian-utils/brutils-python/pull/651) - Utilitário `is_valid_renavam` [#652](https://github.com/brazilian-utils/brutils-python/pull/652) +- Suporte a CNPJ alfanumérico em `is_valid_cnpj`, `format_cnpj` e `remove_symbols_cnpj` ### Fixed diff --git a/README.md b/README.md index a24fffa..bcf204b 100644 --- a/README.md +++ b/README.md @@ -228,6 +228,8 @@ Exemplo: True >>> is_valid_cnpj('00111222000133') False +>>> is_valid_cnpj('12ABC34501DE35') +True ``` ### format_cnpj @@ -276,6 +278,8 @@ Exemplo: >>> from brutils import remove_symbols_cnpj >>> remove_symbols_cnpj('00.111.222/0001-00') '00111222000100' +>>> remove_symbols_cnpj('12.ABC.345/01DE-35') +'12ABC34501DE35' ``` ### generate_cnpj diff --git a/README_EN.md b/README_EN.md index aa784da..4d1a6f7 100644 --- a/README_EN.md +++ b/README_EN.md @@ -229,6 +229,8 @@ Example: True >>> is_valid_cnpj('00111222000133') False +>>> is_valid_cnpj('12ABC34501DE35') +True ``` ### format_cnpj @@ -278,6 +280,8 @@ Example: >>> from brutils import remove_symbols_cnpj >>> remove_symbols_cnpj('00.111.222/0001-00') '00111222000100' +>>> remove_symbols_cnpj('12.ABC.345/01DE-35') +'12ABC34501DE35' ``` ### generate_cnpj diff --git a/brutils/cnpj.py b/brutils/cnpj.py index 84dd7ae..228a9ef 100644 --- a/brutils/cnpj.py +++ b/brutils/cnpj.py @@ -151,7 +151,7 @@ def validate(cnpj: str) -> bool: backward compatibility. """ - if not cnpj.isdigit() or len(cnpj) != 14 or len(set(cnpj)) == 1: + if len(cnpj) != 14 or len(set(cnpj)) == 1: return False return all( _hashdigit(cnpj, i + 13) == int(v) for i, v in enumerate(cnpj[12:]) @@ -230,7 +230,8 @@ def _hashdigit(cnpj: str, position: int) -> int: weightgen = chain(range(position - 8, 1, -1), range(9, 1, -1)) val = ( - sum(int(digit) * weight for digit, weight in zip(cnpj, weightgen)) % 11 + sum((ord(char) - 48) * weight for char, weight in zip(cnpj, weightgen)) + % 11 ) return 0 if val < 2 else 11 - val diff --git a/tests/test_cnpj.py b/tests/test_cnpj.py index 2ec7261..927aa8e 100644 --- a/tests/test_cnpj.py +++ b/tests/test_cnpj.py @@ -35,6 +35,10 @@ def test_validate(self): self.assertIs(validate("34665388000161"), True) self.assertIs(validate("52599927000100"), False) self.assertIs(validate("00000000000"), False) + self.assertIs(validate("12ABC34501DE35"), True) + self.assertIs(validate("12ABC34501DE00"), False) + self.assertIs(validate("AAAAAAAAAAAAAA"), False) + self.assertIs(validate("12ABC34501DE3"), False) def test_is_valid(self): # When CNPJ is not string, returns False @@ -66,6 +70,7 @@ def test_is_valid(self): # When CNPJ is valid self.assertIs(is_valid("34665388000161"), True) self.assertIs(is_valid("01838723000127"), True) + self.assertIs(is_valid("12ABC34501DE35"), True) def test_generate(self): for _ in range(10_000): @@ -77,10 +82,15 @@ def test__hashdigit(self): self.assertEqual(_hashdigit("00000000000000", 14), 0) self.assertEqual(_hashdigit("52513127000292", 13), 9) self.assertEqual(_hashdigit("52513127000292", 14), 9) + self.assertEqual(_hashdigit("12ABC34501DE", 13), 3) + self.assertEqual(_hashdigit("12ABC34501DE3", 14), 5) def test__checksum(self): self.assertEqual(_checksum("00000000000000"), "00") self.assertEqual(_checksum("52513127000299"), "99") + self.assertEqual(_checksum("12ABC34501DE35"), "35") + self.assertEqual(_checksum("1345C3A50001"), "06") + self.assertEqual(_checksum("R55231B30007"), "57") @patch("brutils.cnpj.sieve") @@ -89,6 +99,8 @@ def test_remove_symbols(self, mock_sieve): # When call remove_symbols, it calls sieve remove_symbols("12.345.678/0001-90") mock_sieve.assert_called() + remove_symbols("12.ABC.345/01DE-35") + mock_sieve.assert_called() @patch("brutils.cnpj.is_valid") @@ -102,6 +114,13 @@ def test_when_cnpj_is_valid_returns_true_to_format(self, mock_is_valid): # Checks if function is_valid_cnpj is called mock_is_valid.assert_called_once_with("01838723000127") + mock_is_valid.reset_mock() + # When cnpj is_valid, returns formatted cnpj + self.assertEqual(format_cnpj("12ABC34501DE35"), "12.ABC.345/01DE-35") + + # Checks if function is_valid_cnpj is called + mock_is_valid.assert_called_once_with("12ABC34501DE35") + def test_when_cnpj_is_not_valid_returns_none(self, mock_is_valid): mock_is_valid.return_value = False