Skip to content

Commit

Permalink
Aceitar e validar cnpjs alfanuméricos (#44)
Browse files Browse the repository at this point in the history
* bump nas deps

* aceitar cnpjs alfanuméricos

* adiciona variavel de imagem no ci

* gerar e formatar cnpjs

* teste pra minúsculas

* atualizando o README
  • Loading branch information
VitorTrin authored Jan 15, 2025
1 parent ac83096 commit 0f2df22
Show file tree
Hide file tree
Showing 9 changed files with 143 additions and 35 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/elixir.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ jobs:
steps:
- uses: actions/checkout@v2
- uses: erlef/setup-beam@v1.16.0
env:
ImageOS: ubuntu22
with:
elixir-version: "1.15.7" # Define the elixir version [required]
otp-version: "26.0.2" # Define the OTP version [required]
Expand Down
4 changes: 2 additions & 2 deletions .tool-versions
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
elixir 1.15.7-otp-26
erlang 26.0.2
elixir 1.18.1-otp-27
erlang 27.2
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,11 @@ validate_cnpj(:cnpj, message: "Cnpj Inválido")

Em caso de duvida [veja mais detalhes](https://github.com/williamgueiros/Brcpfcnpj/issues/3#issuecomment-191368591).

## CNPJ alfanuméricos

A partir de janeiro de 2026 será possivel ter caracteres alfanuméricos no CNPJ. Essa biblioteca tem suporte para esses caracteres a partir da versão 2.0.0.
Por favor atualizem a versão antes do prazo entrar em efeito senão poderá acontecer falsos negativos nas validações.

## Contribuições

* [Diogo Beda](https://github.com/diogobeda)
Expand Down
103 changes: 90 additions & 13 deletions lib/cpfcnpj.ex
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,51 @@ defmodule Cpfcnpj do
@cnpj_length 14
@cnpj_algs_1 [5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2, 0, 0]
@cnpj_algs_2 [6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2, 0]
@cnpj_regex ~r/(\d{2})?(\d{3})?(\d{3})?(\d{4})?(\d{2})/
@cnpj_regex ~r/([0-9A-Z]{2})[.]?([0-9A-Z]{3})[.]?([0-9A-Z]{3})[\/]?([0-9A-Z]{4})[-]?([0-9A-Z]{2})/

@cnpj_characters 48..57
|> Enum.to_list()
|> Kernel.++(Enum.to_list(65..90))
|> Enum.map(&List.wrap(&1))

@cnpj_character_to_value_map %{
"0" => 0,
"1" => 1,
"2" => 2,
"3" => 3,
"4" => 4,
"5" => 5,
"6" => 6,
"7" => 7,
"8" => 8,
"9" => 9,
"A" => 17,
"B" => 18,
"C" => 19,
"D" => 20,
"E" => 21,
"F" => 22,
"G" => 23,
"H" => 24,
"I" => 25,
"J" => 26,
"K" => 27,
"L" => 28,
"M" => 29,
"N" => 30,
"O" => 31,
"P" => 32,
"Q" => 33,
"R" => 34,
"S" => 35,
"T" => 36,
"U" => 37,
"V" => 38,
"W" => 39,
"X" => 40,
"Y" => 41,
"Z" => 42
}

@doc """
Valida cpf/cnpj caracteres especias não são levados em consideração.
Expand Down Expand Up @@ -64,10 +108,10 @@ defmodule Cpfcnpj do
end

# Checks validation digits
defp type_checker(tp_cpfcnpj) do
cpfcnpj = String.replace(elem(tp_cpfcnpj, 1), ~r/[^0-9]/, "")
first_char_valid = character_valid(cpfcnpj, {elem(tp_cpfcnpj, 0), :first})
second_char_valid = character_valid(cpfcnpj, {elem(tp_cpfcnpj, 0), :second})
defp type_checker({type, string}) do
cpfcnpj = replace_invalid_characters(type, string)
first_char_valid = character_valid(cpfcnpj, {type, :first})
second_char_valid = character_valid(cpfcnpj, {type, :second})
verif = first_char_valid <> second_char_valid
verif == String.slice(cpfcnpj, -2, 2)
end
Expand All @@ -90,7 +134,8 @@ defmodule Cpfcnpj do
order == "0000" ->
false

String.to_integer(order) > 300 and first_three_digits == "000" and basic != "00000000" ->
cnpj_alphanumeric_translation(order) > 300 and first_three_digits == "000" and
basic != "00000000" ->
false

true ->
Expand All @@ -103,7 +148,7 @@ defmodule Cpfcnpj do
cpfcnpj
|> String.codepoints()
|> Enum.with_index()
|> Enum.map(fn {k, v} -> String.to_integer(k) * Enum.at(algs, v) end)
|> Enum.map(fn {k, v} -> cnpj_alphanumeric_translation(k) * Enum.at(algs, v) end)

Enum.reduce(mult, 0, &+/2)
end
Expand Down Expand Up @@ -136,9 +181,9 @@ defmodule Cpfcnpj do
"""
@spec format_number({:cpf | :cnpj, String.t()}) :: String.t() | nil
def format_number(number_in) do
if valid?(number_in) do
tp_cpfcnpj = {elem(number_in, 0), String.replace(elem(number_in, 1), ~r/[^0-9]/, "")}
def format_number({type, number}) do
if valid?({type, number}) do
tp_cpfcnpj = {type, String.replace(number, ~r/[^0-9A-Z]/, "")}

case tp_cpfcnpj do
{:cpf, cpf} ->
Expand All @@ -157,7 +202,7 @@ defmodule Cpfcnpj do
"""
@spec generate(:cpf | :cnpj) :: String.t()
def generate(tp_cpfcnpj) do
numbers = random_numbers(tp_cpfcnpj)
numbers = random_characters(tp_cpfcnpj)
first_valid_char = character_valid(numbers, {tp_cpfcnpj, :first})
second_valid_char = character_valid(numbers <> first_valid_char, {tp_cpfcnpj, :second})

Expand All @@ -172,12 +217,44 @@ defmodule Cpfcnpj do
end
end

defp random_numbers(tp_cpfcnpj) do
def random_characters(:cpf) do
random_digit_generator = fn -> Enum.random(0..9) end

random_digit_generator
|> Stream.repeatedly()
|> Enum.take(if(tp_cpfcnpj == :cpf, do: @cpf_length, else: @cnpj_length) - 2)
|> Enum.take(@cpf_length - 2)
|> Enum.join()
end

def random_characters(:cnpj) do
random_digit_generator = fn ->
Enum.random(@cnpj_characters)
end

random_digit_generator
|> Stream.repeatedly()
|> Enum.take(@cnpj_length - 2)
|> Enum.join()
end

# Cnpj pode ter caracteres alfanuméricos, cpf não
defp replace_invalid_characters(:cnpj, cnpj) do
String.replace(cnpj, ~r/[^a-zA-Z0-9]/, "")
end

defp replace_invalid_characters(:cpf, cpf) do
String.replace(cpf, ~r/[^0-9]/, "")
end

defp cnpj_alphanumeric_translation(string) do
case String.length(string) do
1 ->
Map.get(@cnpj_character_to_value_map, String.upcase(string, :ascii))

_other ->
string
|> String.codepoints()
|> Enum.map(&cnpj_alphanumeric_translation/1)
end
end
end
6 changes: 3 additions & 3 deletions mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ defmodule Brcpfcnpj.Mixfile do
def project do
[
app: :brcpfcnpj,
version: "1.0.0",
version: "2.0.0",
elixir: "~> 1.7",
description: description(),
package: package(),
Expand All @@ -19,11 +19,11 @@ defmodule Brcpfcnpj.Mixfile do
defp deps do
[
{:earmark, "~> 1.4", only: [:dev, :docs], runtime: false},
{:ex_doc, "~> 0.30", only: [:dev, :docs], runtime: false},
{:ex_doc, "~> 0.36", only: [:dev, :docs], runtime: false},
{:inch_ex, "~> 2.0", only: :docs, runtime: false},
{:credo, "~> 1.7", only: :dev, runtime: false},
{:dialyxir, "~> 1.4", only: :dev, runtime: false},
{:ecto, "~> 3.11", optional: true}
{:ecto, "~> 3.12", optional: true}
]
end

Expand Down
28 changes: 14 additions & 14 deletions mix.lock
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
%{
"bunt": {:hex, :bunt, "0.2.1", "e2d4792f7bc0ced7583ab54922808919518d0e57ee162901a16a1b6664ef3b14", [:mix], [], "hexpm", "a330bfb4245239787b15005e66ae6845c9cd524a288f0d141c148b02603777a5"},
"credo": {:hex, :credo, "1.7.1", "6e26bbcc9e22eefbff7e43188e69924e78818e2fe6282487d0703652bc20fd62", [:mix], [{:bunt, "~> 0.2.1", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2.8", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "e9871c6095a4c0381c89b6aa98bc6260a8ba6addccf7f6a53da8849c748a58a2"},
"decimal": {:hex, :decimal, "2.1.1", "5611dca5d4b2c3dd497dec8f68751f1f1a54755e8ed2a966c2633cf885973ad6", [:mix], [], "hexpm", "53cfe5f497ed0e7771ae1a475575603d77425099ba5faef9394932b35020ffcc"},
"dialyxir": {:hex, :dialyxir, "1.4.2", "764a6e8e7a354f0ba95d58418178d486065ead1f69ad89782817c296d0d746a5", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "516603d8067b2fd585319e4b13d3674ad4f314a5902ba8130cd97dc902ce6bbd"},
"earmark": {:hex, :earmark, "1.4.46", "8c7287bd3137e99d26ae4643e5b7ef2129a260e3dcf41f251750cb4563c8fb81", [:mix], [], "hexpm", "798d86db3d79964e759ddc0c077d5eb254968ed426399fbf5a62de2b5ff8910a"},
"earmark_parser": {:hex, :earmark_parser, "1.4.39", "424642f8335b05bb9eb611aa1564c148a8ee35c9c8a8bba6e129d51a3e3c6769", [:mix], [], "hexpm", "06553a88d1f1846da9ef066b87b57c6f605552cfbe40d20bd8d59cc6bde41944"},
"ecto": {:hex, :ecto, "3.11.0", "ff8614b4e70a774f9d39af809c426def80852048440e8785d93a6e91f48fec00", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7769dad267ef967310d6e988e92d772659b11b09a0c015f101ce0fff81ce1f81"},
"erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"},
"ex_doc": {:hex, :ex_doc, "0.30.9", "d691453495c47434c0f2052b08dd91cc32bc4e1a218f86884563448ee2502dd2", [:mix], [{:earmark_parser, "~> 1.4.31", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "d7aaaf21e95dc5cddabf89063327e96867d00013963eadf2c6ad135506a8bc10"},
"credo": {:hex, :credo, "1.7.11", "d3e805f7ddf6c9c854fd36f089649d7cf6ba74c42bc3795d587814e3c9847102", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "56826b4306843253a66e47ae45e98e7d284ee1f95d53d1612bb483f88a8cf219"},
"decimal": {:hex, :decimal, "2.3.0", "3ad6255aa77b4a3c4f818171b12d237500e63525c2fd056699967a3e7ea20f62", [:mix], [], "hexpm", "a4d66355cb29cb47c3cf30e71329e58361cfcb37c34235ef3bf1d7bf3773aeac"},
"dialyxir": {:hex, :dialyxir, "1.4.5", "ca1571ac18e0f88d4ab245f0b60fa31ff1b12cbae2b11bd25d207f865e8ae78a", [:mix], [{:erlex, ">= 0.2.7", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "b0fb08bb8107c750db5c0b324fa2df5ceaa0f9307690ee3c1f6ba5b9eb5d35c3"},
"earmark": {:hex, :earmark, "1.4.47", "7e7596b84fe4ebeb8751e14cbaeaf4d7a0237708f2ce43630cfd9065551f94ca", [:mix], [], "hexpm", "3e96bebea2c2d95f3b346a7ff22285bc68a99fbabdad9b655aa9c6be06c698f8"},
"earmark_parser": {:hex, :earmark_parser, "1.4.42", "f23d856f41919f17cd06a493923a722d87a2d684f143a1e663c04a2b93100682", [:mix], [], "hexpm", "6915b6ca369b5f7346636a2f41c6a6d78b5af419d61a611079189233358b8b8b"},
"ecto": {:hex, :ecto, "3.12.5", "4a312960ce612e17337e7cefcf9be45b95a3be6b36b6f94dfb3d8c361d631866", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "6eb18e80bef8bb57e17f5a7f068a1719fbda384d40fc37acb8eb8aeca493b6ea"},
"erlex": {:hex, :erlex, "0.2.7", "810e8725f96ab74d17aac676e748627a07bc87eb950d2b83acd29dc047a30595", [:mix], [], "hexpm", "3ed95f79d1a844c3f6bf0cea61e0d5612a42ce56da9c03f01df538685365efb0"},
"ex_doc": {:hex, :ex_doc, "0.36.1", "4197d034f93e0b89ec79fac56e226107824adcce8d2dd0a26f5ed3a95efc36b1", [:mix], [{:earmark_parser, "~> 1.4.42", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "d7d26a7cf965dacadcd48f9fa7b5953d7d0cfa3b44fa7a65514427da44eafd89"},
"file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"},
"inch_ex": {:hex, :inch_ex, "2.0.0", "24268a9284a1751f2ceda569cd978e1fa394c977c45c331bb52a405de544f4de", [:mix], [{:bunt, "~> 0.2", [hex: :bunt, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "96d0ec5ecac8cf63142d02f16b7ab7152cf0f0f1a185a80161b758383c9399a8"},
"jason": {:hex, :jason, "1.4.1", "af1504e35f629ddcdd6addb3513c3853991f694921b1b9368b0bd32beb9f1b63", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fbb01ecdfd565b56261302f7e1fcc27c4fb8f32d56eab74db621fc154604a7a1"},
"makeup": {:hex, :makeup, "1.1.1", "fa0bc768698053b2b3869fa8a62616501ff9d11a562f3ce39580d60860c3a55e", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "5dc62fbdd0de44de194898b6710692490be74baa02d9d108bc29f007783b0b48"},
"makeup_elixir": {:hex, :makeup_elixir, "0.16.1", "cc9e3ca312f1cfeccc572b37a09980287e243648108384b97ff2b76e505c3555", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "e127a341ad1b209bd80f7bd1620a15693a9908ed780c3b763bccf7d200c767c6"},
"makeup_erlang": {:hex, :makeup_erlang, "0.1.3", "d684f4bac8690e70b06eb52dad65d26de2eefa44cd19d64a8095e1417df7c8fd", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "b78dc853d2e670ff6390b605d807263bf606da3c82be37f9d7f68635bd886fc9"},
"nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"},
"telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"},
"jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"},
"makeup": {:hex, :makeup, "1.2.1", "e90ac1c65589ef354378def3ba19d401e739ee7ee06fb47f94c687016e3713d1", [:mix], [{:nimble_parsec, "~> 1.4", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "d36484867b0bae0fea568d10131197a4c2e47056a6fbe84922bf6ba71c8d17ce"},
"makeup_elixir": {:hex, :makeup_elixir, "1.0.1", "e928a4f984e795e41e3abd27bfc09f51db16ab8ba1aebdba2b3a575437efafc2", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "7284900d412a3e5cfd97fdaed4f5ed389b8f2b4cb49efc0eb3bd10e2febf9507"},
"makeup_erlang": {:hex, :makeup_erlang, "1.0.1", "c7f58c120b2b5aa5fd80d540a89fdf866ed42f1f3994e4fe189abebeab610839", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "8a89a1eeccc2d798d6ea15496a6e4870b75e014d1af514b1b71fa33134f57814"},
"nimble_parsec": {:hex, :nimble_parsec, "1.4.1", "f41275a0354c736db4b1d255b5d2a27c91028e55c21ea3145b938e22649ffa3f", [:mix], [], "hexpm", "605e44204998f138d6e13be366c8e81af860e726c8177caf50067e1b618fe522"},
"telemetry": {:hex, :telemetry, "1.3.0", "fedebbae410d715cf8e7062c96a1ef32ec22e764197f70cda73d82778d61e7a2", [:rebar3], [], "hexpm", "7015fc8919dbe63764f4b4b87a95b7c0996bd539e0d499be6ec9d7f3875b79e6"},
}
15 changes: 12 additions & 3 deletions test/brcpfcnpj_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,19 @@ defmodule BrcpfcnpjTest do
assert Brcpfcnpj.cpf_format("11144477735") == "111.444.777-35"
end

test "should return the formated to wrong cpf" do
test "should return nil to wrong cpf" do
assert Brcpfcnpj.cpf_format("11144477731") == nil
end

test "should return the formated cnpj" do
assert Brcpfcnpj.cnpj_format("69103604000160") == "69.103.604/0001-60"
end

test "should return the formated to wrong cnpj" do
test "should return the formated alphanumeric cnpj" do
assert Brcpfcnpj.cnpj_format("UNZ98FFWCLCV50") == "UN.Z98.FFW/CLCV-50"
end

test "should return nil to the wrong cnpj" do
assert Brcpfcnpj.cnpj_format("69103604000161") == nil
end

Expand All @@ -36,7 +40,12 @@ defmodule BrcpfcnpjTest do

test "should generate a formatted cnpj" do
cnpj = Brcpfcnpj.cnpj_generate(true)
assert Regex.match?(~r/(\d{2})[.]?(\d{3})[.]?(\d{3})[\/]?(\d{4})[-]?(\d{2})/, cnpj)

assert Regex.match?(
~r/([0-9A-Z]{2})[.]?([0-9A-Z]{3})[.]?([0-9A-Z]{3})[\/]?([0-9A-Z]{4})[-]?([0-9A-Z]{2})/,
cnpj
)

assert Brcpfcnpj.cnpj_valid?(cnpj)
end

Expand Down
10 changes: 10 additions & 0 deletions test/cnpj_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,14 @@ defmodule CnpjTest do
assert Brcpfcnpj.cnpj_valid?("00000898246954") == false
assert Brcpfcnpj.cnpj_valid?("00000136747140") == false
end

test "should allow alphanumeric characters in the cnpj" do
assert Brcpfcnpj.cnpj_valid?("12.ABC.345/01DE-35") == true
assert Brcpfcnpj.cnpj_valid?("12ABC34501DE35") == true
assert Brcpfcnpj.cnpj_valid?("12abc34501de35") == true
end

test "should validate alphanumeric characters" do
assert Brcpfcnpj.cnpj_valid?("12.ABC.345/01DE-34") == false
end
end
5 changes: 5 additions & 0 deletions test/cpf_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,9 @@ defmodule CpfTest do
test "should be invalid with nil input" do
assert Brcpfcnpj.cpf_valid?(nil) == false
end

test "should not allow alphanumeric characters" do
assert Brcpfcnpj.cpf_valid?("ABC34501D-84") == false
assert Brcpfcnpj.cpf_valid?("ABC34501D84") == false
end
end

0 comments on commit 0f2df22

Please sign in to comment.