Current section

Files

Jump to
brcpfcnpj lib cpfcnpj.ex
Raw

lib/cpfcnpj.ex

defmodule Cpfcnpj do
@moduledoc ~S"""
Modulo responsavel pro realizar todos os calculos de validacao
exemplos:
iex>Cpfcnpj.valid?({:cnpj,"69.103.604/0001-60"})
true
iex>Cpfcnpj.valid?({:cpf,"111.444.777-35"})
true
Com ou sem os caracteres especiais os mesmos serao validados
"""
@division 11
@cpf_length 11
@cpf_algs_1 [10, 9, 8, 7, 6, 5, 4, 3, 2,0,0]
@cpf_algs_2 [11, 10, 9, 8, 7, 6, 5, 4, 3, 2,0]
@cpf_regex ~r/(\d{3})?(\d{3})?(\d{3})?(\d{2})/
@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})/
@doc ~S"""
Valida cpf/cnpj caracteres especias nao sao levados em consideracao
## Exemplos
iex>Cpfcnpj.valid?({:cnpj,"69.103.604/0001-60"})
true
"""
def valid?(number_in) do
if check_number(number_in) != :error, do: type_checker(number_in), else: false
end
defp check_number(tp_cpfcnpj) do
cpfcnpj=String.replace(elem(tp_cpfcnpj,1), ~r/[\.\/-]/, "")
all_equal = String.replace(cpfcnpj, String.at(cpfcnpj, 0), "")
|>String.length
case tp_cpfcnpj do
{:cpf,_} ->
if String.length(cpfcnpj) != @cpf_length or all_equal == 0 do
:error
end
{:cnpj,_} ->
if String.length(cpfcnpj) != @cnpj_length or all_equal == 0 do
:error
end
end
end
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})
verif = first_char_valid <> second_char_valid
verif == String.slice(cpfcnpj,-2,2)
end
defp mult_sum(algs, cpfcnpj) do
mult = cpfcnpj
|> String.codepoints
|> Enum.with_index
|> Enum.map(fn {k, v} -> String.to_integer(k) * Enum.at(algs,v) end)
Enum.reduce(mult, 0, &+/2)
end
defp character_calc(remainder) do
if remainder < 2, do: 0, else: @division - remainder
end
defp character_valid(cpfcnpj,valid_type) do
array = case valid_type do
{:cpf,:first} -> @cpf_algs_1
{:cnpj,:first} -> @cnpj_algs_1
{:cpf,:second} -> @cpf_algs_2
{:cnpj,:second} -> @cnpj_algs_2
end
mult_sum(array, cpfcnpj)
|> rem(@division)
|> character_calc
|> Integer.to_string
end
@doc ~S"""
Valida o Cpf/Cnpj e retorna uma String com o mesmo formatado
Caso seja invalido retorna nil
## Exemplos
iex> Cpfcnpj.format_number({:cnpj,"69.103.604/0001-60"})
"69.103.604/0001-60"
"""
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]/, "")}
case tp_cpfcnpj do
{:cpf,cpf} ->
Regex.replace(@cpf_regex, cpf,"\\1.\\2.\\3-\\4")
{:cnpj,cnpj} ->
Regex.replace(@cnpj_regex, cnpj,"\\1.\\2.\\3/\\4-\\5")
end
else
nil
end
end
@doc ~S"""
Gerador de cpf/cnpj concatenado com o digito verificador
"""
def generate(tp_cpfcnpj) do
numbers = random_numbers(tp_cpfcnpj)
first_valid_char = character_valid(numbers, { tp_cpfcnpj, :first })
second_valid_char = character_valid(numbers <> first_valid_char, { tp_cpfcnpj, :second })
numbers <> first_valid_char <> second_valid_char
end
defp random_numbers (tp_cpfcnpj) do
Stream.repeatedly(fn -> round :random.uniform * 9 end)
|> Enum.take((if tp_cpfcnpj == :cpf, do: @cpf_length, else: @cnpj_length) - 2)
|> Enum.reduce("", &(Integer.to_string(&1)<>&2));
end
end