CPF/CNPJ
CPF/CNPJ is a Crystal shard for handling Brazilian CPF and CNPJ identifiers.
- What is CPF and CNPJ?
- Installation
- Usage
- CPF
- CNPJ
- CadastroID
- Database
- JSON
- YAML
- Development
- Contributing
- Contributors
What is CPF and CNPJ?
CPF (Cadastro de Pessoa Física) is Brazil’s individual taxpayer registry. It is
an 11-digit identifier in the format 000.000.000-00
, where the last two digits
are check digits calculated from the first nine digits.
CNPJ (Cadastro Nacional de Pessoa Jurídica) is Brazil’s national registry of
legal entities. It is a 14-digit identifier in the format XX.XXX.XXX/XXXX-00
,
where the last two digits are check digits calculated from the first twelve
digits.
Installation
-
Add the dependency to your
shard.yml
:dependencies: cpf_cnpj: codeberg: gunbolt/cpf_cnpj
-
Run
shards install
Usage
Require cpf_cnpj
require "cpf_cnpj"
CPF
You can instantiate CPF
passing formatted or unformatted identifiers. Both
#value
and #to_s
methods returns the identifier in the same form it was
provided:
cpf = CPF.new("640.061.830-97")
cpf.value # => "640.061.830-97"
cpf.to_s # => "640.061.830-97"
cpf = CPF.new("64006183097")
cpf.value # => "64006183097"
cpf.to_s # => "64006183097"
A CPF
object always represents a valid CPF identifier. This ensures you never
deal with invalid, empty, or nil
values. If you attempt to initialize a CPF
with an invalid identifier, a CPF::InvalidValueError
will be raised:
CPF.new("11111111111") # => raises `CPF::InvalidValueError`
CPF.new("111.111.111-11") # => raises `CPF::InvalidValueError`
To create a CPF
without raising an exception, use the .parse?
method. It
returns a CPF?
(a CPF
struct or nil
) depending on whether the value is
valid:
# With invalid value
CPF.parse?("11111111111") # => nil
# With valid value
CPF.parse?("640.061.830-97") # => #<CPF:0x104fe0ae0 @value="640.061.830-97">
To check whether an identifier is valid, use the CPF::Validator
module:
CPF::Validator.valid?("11111111111") # => false
CPF::Validator.valid?("640.061.830-97") # => true
Use the #formatted
and #unformatted
methods to get the identifier with or
without punctuation:
cpf = CPF.new("64006183097")
cpf.formatted # => "640.061.830-97"
cpf.unformatted # => "64006183097"
CNPJ
CNPJ
behaves the same way as CPF. It also supports alphanumeric
identifiers scheduled to take effect in 2026:
# With valid values
cnpj = CNPJ.new("24.485.147/0001-87")
cnpj.value # => "24.485.147/0001-87"
cnpj.to_s # => "24.485.147/0001-87"
cnpj = CNPJ.new("VCZ83T1R000106")
cnpj.value # => "VCZ83T1R000106"
cnpj.to_s # => "VCZ83T1R000106"
# With invalid values
CNPJ.new("11111111111111") # => raises `CNPJ::InvalidValueError`
CNPJ.new("11.111.111/1111-11") # => raises `CNPJ::InvalidValueError`
# Safely instatiating a CNPJ
CNPJ.parse?("11111111111111") # => nil
CNPJ.parse?("24.485.147/0001-87") # => #<CNPJ:0x104fe0ae0 @value="24.485.147/0001-87">
# Validating an identifier
CNPJ::Validator.valid?("11111111111111") # => false
CNPJ::Validator.valid?("24.485.147/0001-87") # => true
# Formatting & unformatting values
cnpj = CNPJ.new("VCZ83T1R000106")
cnpj.value # => "VCZ83T1R000106"
cnpj.formatted # => "VC.Z83.T1R/0001-06"
cnpj.unformatted # => "VCZ83T1R000106"
The only additional feature in CNPJ
is the #root
method ("raiz" in
Portuguese), which returns the first eight characters of the identifier. This is
useful for verifying parent and subsidiary companies:
cnpj = CNPJ.new("VCZ83T1R000106")
cnpj.root # => "VCZ83T1R"
other_cnpj = CNPJ.new("VCZ83T1R000289")
other_cnpj.root # => "VCZ83T1R"
CadastroID
CadastroID
is an abstract struct, so you cannot instantiate it directly. It is
useful when a variable may hold a CPF
or a CNPJ
value. It provides the
.new
and .parse?
methods, just like CPF
and CNPJ
:
id_a = CadastroID.new("640.061.830-97")
id_a.value # => "640.061.830-97"
id_a.class # => CPF
typeof(id_a) # => CadastroID
id_b = CadastroID.new("VCZ83T1R000106")
id_b.value # => "VCZ83T1R000106"
id_b.class # => CNPJ
typeof(id_b) # => CadastroID
# With invalid value
CadastroID.new("1234") # => raises `CadastroID::InvalidValueError`
# Safely instantiating a CPF or CNPJ
CadastroID.parse?("1234") # => nil
CadastroID.parse?("24.485.147/0001-87") # => #<CNPJ:0x104fe0ae0 @value="24.485.147/0001-87">
Database
CPF/CNPJ integrates with crystal-db
shard. Simply require cpf_cnpj/db
and use the @[DB::Field]
annotation to set
up the converter.
require "cpf_cnpj/db"
class Client
include DB::Serializable
@[DB::Field(converter: CadastroID)]
property identifier : CadastroID
end
class Company
include DB::Serializable
@[DB::Field(converter: CNPJ)]
property cnpj : CNPJ
end
class User
include DB::Serializable
@[DB::Field(converter: CPF)]
property cpf : CPF
end
# CPF
users = User.from_rs(database.query("SELECT cpf FROM users"))
users[0].cpf.value # => "640.061.830-97"
# CNPJ
companies = Company.from_rs(database.query("SELECT cnpj FROM companies"))
companies[0].cnpj.value # => "UP.FVU.R5W/0001-07"
# CadastroID
clients = Client.from_rs(database.query("SELECT identifier FROM clients"))
clients[0].identifier.value # => "UP.FVU.R5W/0001-07"
clients[1].identifier.value # => "640.061.830-97"
Database values should be stored as text/string types and must be valid
identifiers (formatted or unformatted). If a table column can contain NULL
,
empty, invalid values, the corresponding property should be declared as nilabe.
For example:
class User
include DB::Serializable
@[DB::Field(converter: CPF)]
property cpf : CPF?
end
# returns invalid values from database
users = User.from_rs(database.query("SELECT cpf FROM users"))
users[0].cpf # => nil
JSON
CPF/CNPJ also integrates with JSON::Serializable
. You need to require
"cpf_cnpj/json"
to enable JSON serialization and deserialization:
require "cpf_cnpj/json"
class Client
include JSON::Serializable
property identifier : CadastroID
end
class Company
include JSON::Serializable
property cnpj : CNPJ
end
class User
include JSON::Serializable
property cpf : CPF
end
client = Client.from_json(%({"identifier": "7B.N1F.Y9N/0001-98"}))
client.identifier.value # => "7B.N1F.Y9N/0001-98"
client.identifier.to_json # => "\"7B.N1F.Y9N/0001-98\""
company = Company.from_json(%({"cnpj": "7B.N1F.Y9N/0001-98"}))
company.cnpj.to_s # => "7B.N1F.Y9N/0001-98"
company.cnpj.to_json # => "\"7B.N1F.Y9N/0001-98\""
user = User.from_json(%({"cpf": "64006183097"}))
user.cpf.to_s # => "64006183097"
user.cpf.to_json # => "\"64006183097\""
YAML
CPF/CNPJ can be serialized and deserialized with YAML::Serializable
. To use
this feature, you need to require "cpf_cnpj/json"
.
require "cpf_cnpj/yaml"
class Client
include YAML::Serializable
property identifier : CadastroID
end
class Company
include YAML::Serializable
property cnpj : CNPJ
end
class User
include YAML::Serializable
property cpf : CPF
end
client = Client.from_yaml("identifier: 7B.N1F.Y9N/0001-98")
client.identifier.value # => "7B.N1F.Y9N/0001-98"
client.identifier.to_yaml # => "--- 7B.N1F.Y9N/0001-98\n"
company = Company.from_yaml("cnpj: 7B.N1F.Y9N/0001-98")
company.cnpj.to_s # => "7B.N1F.Y9N/0001-98"
company.cnpj.to_yaml # => "--- 7B.N1F.Y9N/0001-98\n"
user = User.from_yaml("cpf: 64006183097")
user.cpf.to_s # => "64006183097"
user.cpf.to_yaml # => "--- 64006183097\n"
Development
shards install
to install dependenciescrystal spec
to run testscrystal tool format && bin/ameba
to format/lint code
Contributing
- Fork it (https://codeberg.org/gunbolt/cpf_cnpj/fork)
- Create your feature branch (
git checkout -b my-new-feature
) - Commit your changes (
git commit -am 'Add some feature'
) - Push to the branch (
git push origin my-new-feature
) - Create a new Pull Request
Contributors
- stephann - creator and maintainer