br-utils 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +120 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +21 -0
- data/README.md +348 -0
- data/Rakefile +6 -0
- data/examples/boleto_usage_example.rb +79 -0
- data/examples/cep_usage_example.rb +148 -0
- data/examples/cnh_usage_example.rb +120 -0
- data/examples/cnpj_usage_example.rb +227 -0
- data/examples/cpf_usage_example.rb +237 -0
- data/examples/currency_usage_example.rb +266 -0
- data/examples/date_usage_example.rb +259 -0
- data/examples/email_usage_example.rb +321 -0
- data/examples/legal_nature_usage_example.rb +437 -0
- data/examples/legal_process_usage_example.rb +444 -0
- data/examples/license_plate_usage_example.rb +440 -0
- data/examples/phone_usage_example.rb +595 -0
- data/examples/pis_usage_example.rb +588 -0
- data/examples/renavam_usage_example.rb +499 -0
- data/examples/voter_id_usage_example.rb +573 -0
- data/lib/brazilian-utils/boleto-utils.rb +176 -0
- data/lib/brazilian-utils/cep-utils.rb +330 -0
- data/lib/brazilian-utils/cnh-utils.rb +88 -0
- data/lib/brazilian-utils/cnpj-utils.rb +202 -0
- data/lib/brazilian-utils/cpf-utils.rb +192 -0
- data/lib/brazilian-utils/currency-utils.rb +226 -0
- data/lib/brazilian-utils/data/legal_process_ids.json +38 -0
- data/lib/brazilian-utils/date-utils.rb +244 -0
- data/lib/brazilian-utils/email-utils.rb +54 -0
- data/lib/brazilian-utils/legal-nature-utils.rb +235 -0
- data/lib/brazilian-utils/legal-process-utils.rb +240 -0
- data/lib/brazilian-utils/license-plate-utils.rb +279 -0
- data/lib/brazilian-utils/phone-utils.rb +272 -0
- data/lib/brazilian-utils/pis-utils.rb +151 -0
- data/lib/brazilian-utils/renavam-utils.rb +113 -0
- data/lib/brazilian-utils/voter-id-utils.rb +165 -0
- metadata +123 -0
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
require 'date'
|
|
2
|
+
|
|
3
|
+
module BrazilianUtils
|
|
4
|
+
# Brazilian months enumeration
|
|
5
|
+
module Months
|
|
6
|
+
NAMES = {
|
|
7
|
+
1 => 'janeiro',
|
|
8
|
+
2 => 'fevereiro',
|
|
9
|
+
3 => 'março',
|
|
10
|
+
4 => 'abril',
|
|
11
|
+
5 => 'maio',
|
|
12
|
+
6 => 'junho',
|
|
13
|
+
7 => 'julho',
|
|
14
|
+
8 => 'agosto',
|
|
15
|
+
9 => 'setembro',
|
|
16
|
+
10 => 'outubro',
|
|
17
|
+
11 => 'novembro',
|
|
18
|
+
12 => 'dezembro'
|
|
19
|
+
}.freeze
|
|
20
|
+
|
|
21
|
+
def self.name(month_number)
|
|
22
|
+
NAMES[month_number]
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
module DateUtils
|
|
27
|
+
DATE_REGEX = /^\d{2}\/\d{2}\/\d{4}$/.freeze
|
|
28
|
+
|
|
29
|
+
# Brazilian national holidays (fixed dates)
|
|
30
|
+
NATIONAL_HOLIDAYS = {
|
|
31
|
+
[1, 1] => 'Ano Novo',
|
|
32
|
+
[4, 21] => 'Tiradentes',
|
|
33
|
+
[5, 1] => 'Dia do Trabalho',
|
|
34
|
+
[9, 7] => 'Independência do Brasil',
|
|
35
|
+
[10, 12] => 'Nossa Senhora Aparecida',
|
|
36
|
+
[11, 2] => 'Finados',
|
|
37
|
+
[11, 15] => 'Proclamação da República',
|
|
38
|
+
[12, 25] => 'Natal'
|
|
39
|
+
}.freeze
|
|
40
|
+
|
|
41
|
+
# State-specific holidays (fixed dates)
|
|
42
|
+
STATE_HOLIDAYS = {
|
|
43
|
+
'AC' => { [1, 23] => 'Dia do Evangélico', [6, 15] => 'Aniversário do Acre', [9, 5] => 'Dia da Amazônia', [11, 17] => 'Assinatura do Tratado de Petrópolis' },
|
|
44
|
+
'AL' => { [6, 24] => 'São João', [6, 29] => 'São Pedro', [9, 16] => 'Emancipação Política', [11, 20] => 'Morte de Zumbi dos Palmares' },
|
|
45
|
+
'AM' => { [9, 5] => 'Elevação do Amazonas à categoria de província' },
|
|
46
|
+
'AP' => { [3, 19] => 'Dia de São José', [9, 13] => 'Criação do Território Federal' },
|
|
47
|
+
'BA' => { [7, 2] => 'Independência da Bahia' },
|
|
48
|
+
'CE' => { [3, 19] => 'São José', [3, 25] => 'Data Magna do Ceará' },
|
|
49
|
+
'DF' => { [4, 21] => 'Fundação de Brasília', [11, 30] => 'Dia do Evangélico' },
|
|
50
|
+
'ES' => { [4, 21] => 'Nossa Senhora da Penha' },
|
|
51
|
+
'GO' => { [10, 24] => 'Pedra fundamental de Goiânia' },
|
|
52
|
+
'MA' => { [7, 28] => 'Adesão do Maranhão à independência do Brasil' },
|
|
53
|
+
'MG' => { [4, 21] => 'Data Magna de Minas Gerais' },
|
|
54
|
+
'MS' => { [10, 11] => 'Criação do estado' },
|
|
55
|
+
'MT' => { [11, 20] => 'Dia da Consciência Negra' },
|
|
56
|
+
'PA' => { [8, 15] => 'Adesão do Grão-Pará à independência do Brasil' },
|
|
57
|
+
'PB' => { [7, 26] => 'Homenagem à memória do ex-presidente João Pessoa', [8, 5] => 'Fundação do Estado em 1585' },
|
|
58
|
+
'PE' => { [3, 6] => 'Revolução Pernambucana de 1817', [6, 24] => 'São João' },
|
|
59
|
+
'PI' => { [10, 19] => 'Dia do Piauí' },
|
|
60
|
+
'PR' => { [12, 19] => 'Emancipação política do Paraná' },
|
|
61
|
+
'RJ' => { [4, 23] => 'Dia de São Jorge', [11, 20] => 'Dia da Consciência Negra' },
|
|
62
|
+
'RN' => { [6, 29] => 'Dia de São Pedro', [10, 3] => 'Mártires de Cunhaú e Uruaçu' },
|
|
63
|
+
'RO' => { [1, 4] => 'Criação do estado', [6, 18] => 'Dia do Evangélico' },
|
|
64
|
+
'RR' => { [10, 5] => 'Criação de Roraima' },
|
|
65
|
+
'RS' => { [9, 20] => 'Revolução Farroupilha' },
|
|
66
|
+
'SC' => { [8, 11] => 'Criação da capitania, separando-se de SP' },
|
|
67
|
+
'SE' => { [7, 8] => 'Autonomia política de Sergipe' },
|
|
68
|
+
'SP' => { [7, 9] => 'Revolução Constitucionalista de 1932' },
|
|
69
|
+
'TO' => { [10, 5] => 'Criação de Tocantins' }
|
|
70
|
+
}.freeze
|
|
71
|
+
|
|
72
|
+
VALID_UFS = %w[
|
|
73
|
+
AC AL AP AM BA CE DF ES GO MA MT MS MG PA PB PR PE PI RJ RN RS RO RR SC SP SE TO
|
|
74
|
+
].freeze
|
|
75
|
+
|
|
76
|
+
# Checks if the given date is a national or state holiday in Brazil.
|
|
77
|
+
#
|
|
78
|
+
# This function takes a date as a Date or DateTime object and an optional UF (Unidade Federativa),
|
|
79
|
+
# returning a boolean value indicating whether the date is a holiday or nil if the date or
|
|
80
|
+
# UF are invalid.
|
|
81
|
+
#
|
|
82
|
+
# The method does not handle municipal holidays.
|
|
83
|
+
#
|
|
84
|
+
# @param target_date [Date, DateTime, Time] The date to be checked.
|
|
85
|
+
# @param uf [String, nil] The state abbreviation (UF) to check for state holidays.
|
|
86
|
+
# If not provided, only national holidays will be considered.
|
|
87
|
+
#
|
|
88
|
+
# @return [Boolean, nil] Returns true if the date is a holiday, false if it is not,
|
|
89
|
+
# or nil if the date or UF are invalid.
|
|
90
|
+
#
|
|
91
|
+
# @note This implementation includes fixed national and state holidays.
|
|
92
|
+
# Movable holidays (like Carnival, Easter) are not included in this basic implementation.
|
|
93
|
+
#
|
|
94
|
+
# @example
|
|
95
|
+
# is_holiday(Date.new(2024, 1, 1)) #=> true (New Year)
|
|
96
|
+
# is_holiday(Date.new(2024, 1, 2)) #=> false
|
|
97
|
+
# is_holiday(Date.new(2024, 7, 9), 'SP') #=> true (SP state holiday)
|
|
98
|
+
# is_holiday(Date.new(2024, 12, 25), 'RJ') #=> true (Christmas)
|
|
99
|
+
def self.is_holiday(target_date, uf = nil)
|
|
100
|
+
return nil unless target_date.is_a?(Date) || target_date.is_a?(DateTime) || target_date.is_a?(Time)
|
|
101
|
+
|
|
102
|
+
# Convert to Date if needed
|
|
103
|
+
date = target_date.is_a?(Date) ? target_date : target_date.to_date
|
|
104
|
+
|
|
105
|
+
# Validate UF if provided
|
|
106
|
+
if uf && !VALID_UFS.include?(uf.to_s.upcase)
|
|
107
|
+
return nil
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
month_day = [date.month, date.day]
|
|
111
|
+
|
|
112
|
+
# Check national holidays
|
|
113
|
+
return true if NATIONAL_HOLIDAYS.key?(month_day)
|
|
114
|
+
|
|
115
|
+
# Check state holidays if UF is provided
|
|
116
|
+
if uf
|
|
117
|
+
state_uf = uf.to_s.upcase
|
|
118
|
+
state_holidays = STATE_HOLIDAYS[state_uf]
|
|
119
|
+
return true if state_holidays && state_holidays.key?(month_day)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
false
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# Converts a given date in Brazilian format (dd/mm/yyyy) to its textual representation.
|
|
126
|
+
#
|
|
127
|
+
# This function takes a date as a string in the format dd/mm/yyyy and converts it
|
|
128
|
+
# to a string with the date written out in Brazilian Portuguese, including the full
|
|
129
|
+
# month name and the year.
|
|
130
|
+
#
|
|
131
|
+
# @param date [String] The date to be converted into text. Expected format: dd/mm/yyyy.
|
|
132
|
+
#
|
|
133
|
+
# @return [String, nil] A string with the date written out in Brazilian Portuguese,
|
|
134
|
+
# or nil if the date is invalid.
|
|
135
|
+
#
|
|
136
|
+
# @example
|
|
137
|
+
# convert_date_to_text("01/01/2024") #=> "Primeiro de janeiro de dois mil e vinte e quatro"
|
|
138
|
+
# convert_date_to_text("15/03/2024") #=> "Quinze de março de dois mil e vinte e quatro"
|
|
139
|
+
# convert_date_to_text("invalid") #=> nil
|
|
140
|
+
def self.convert_date_to_text(date)
|
|
141
|
+
return nil unless DATE_REGEX.match?(date)
|
|
142
|
+
|
|
143
|
+
begin
|
|
144
|
+
dt = Date.strptime(date, '%d/%m/%Y')
|
|
145
|
+
rescue ArgumentError
|
|
146
|
+
return nil
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
day = dt.day
|
|
150
|
+
month = dt.month
|
|
151
|
+
year = dt.year
|
|
152
|
+
|
|
153
|
+
# Convert day to text (special case for 1st)
|
|
154
|
+
day_str = if day == 1
|
|
155
|
+
'Primeiro'
|
|
156
|
+
else
|
|
157
|
+
# Reuse number_to_words from CurrencyUtils or implement inline
|
|
158
|
+
number_to_words(day).capitalize
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
month_name = Months.name(month)
|
|
162
|
+
year_str = number_to_words(year)
|
|
163
|
+
|
|
164
|
+
"#{day_str} de #{month_name} de #{year_str}"
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
# Converts a number to its textual representation in Brazilian Portuguese.
|
|
168
|
+
# This is a simplified version focused on dates (days 1-31, years).
|
|
169
|
+
#
|
|
170
|
+
# @param number [Integer] The number to convert
|
|
171
|
+
# @return [String] The textual representation
|
|
172
|
+
#
|
|
173
|
+
# @private
|
|
174
|
+
def self.number_to_words(number)
|
|
175
|
+
return 'zero' if number.zero?
|
|
176
|
+
|
|
177
|
+
ones = %w[zero um dois três quatro cinco seis sete oito nove]
|
|
178
|
+
tens = %w[dez onze doze treze quatorze quinze dezesseis dezessete dezoito dezenove]
|
|
179
|
+
tens_multiples = %w[_ _ vinte trinta quarenta cinquenta sessenta setenta oitenta noventa]
|
|
180
|
+
hundreds = %w[
|
|
181
|
+
_
|
|
182
|
+
cento
|
|
183
|
+
duzentos
|
|
184
|
+
trezentos
|
|
185
|
+
quatrocentos
|
|
186
|
+
quinhentos
|
|
187
|
+
seiscentos
|
|
188
|
+
setecentos
|
|
189
|
+
oitocentos
|
|
190
|
+
novecentos
|
|
191
|
+
]
|
|
192
|
+
|
|
193
|
+
if number < 10
|
|
194
|
+
return ones[number]
|
|
195
|
+
elsif number < 20
|
|
196
|
+
return tens[number - 10]
|
|
197
|
+
elsif number < 100
|
|
198
|
+
tens_digit = number / 10
|
|
199
|
+
ones_digit = number % 10
|
|
200
|
+
if ones_digit.zero?
|
|
201
|
+
return tens_multiples[tens_digit]
|
|
202
|
+
else
|
|
203
|
+
return "#{tens_multiples[tens_digit]} e #{ones[ones_digit]}"
|
|
204
|
+
end
|
|
205
|
+
elsif number == 100
|
|
206
|
+
return 'cem'
|
|
207
|
+
elsif number < 1000
|
|
208
|
+
hundreds_digit = number / 100
|
|
209
|
+
remainder = number % 100
|
|
210
|
+
if remainder.zero?
|
|
211
|
+
return hundreds[hundreds_digit]
|
|
212
|
+
else
|
|
213
|
+
return "#{hundreds[hundreds_digit]} e #{number_to_words(remainder)}"
|
|
214
|
+
end
|
|
215
|
+
elsif number < 1_000_000
|
|
216
|
+
# For years like 2024
|
|
217
|
+
thousands = number / 1000
|
|
218
|
+
remainder = number % 1000
|
|
219
|
+
|
|
220
|
+
result = []
|
|
221
|
+
|
|
222
|
+
if thousands == 1
|
|
223
|
+
result << 'mil'
|
|
224
|
+
else
|
|
225
|
+
result << "#{number_to_words(thousands)} mil"
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
if remainder > 0
|
|
229
|
+
if remainder < 100
|
|
230
|
+
result << "e #{number_to_words(remainder)}"
|
|
231
|
+
else
|
|
232
|
+
result << number_to_words(remainder)
|
|
233
|
+
end
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
result.join(' ')
|
|
237
|
+
else
|
|
238
|
+
number.to_s
|
|
239
|
+
end
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
private_class_method :number_to_words
|
|
243
|
+
end
|
|
244
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
module BrazilianUtils
|
|
2
|
+
module EmailUtils
|
|
3
|
+
# Email validation pattern based on RFC 5322
|
|
4
|
+
# This pattern validates:
|
|
5
|
+
# - Email must not start with a dot
|
|
6
|
+
# - Local part (before @): alphanumeric, dots, underscores, percent, plus, minus
|
|
7
|
+
# - @ symbol required
|
|
8
|
+
# - Domain part: alphanumeric, dots, hyphens
|
|
9
|
+
# - Dot separator required
|
|
10
|
+
# - TLD: at least 2 alphabetic characters
|
|
11
|
+
EMAIL_PATTERN = /\A(?![.])[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}\z/.freeze
|
|
12
|
+
|
|
13
|
+
# Checks if a string corresponds to a valid email address.
|
|
14
|
+
#
|
|
15
|
+
# This function validates email addresses following the specifications defined
|
|
16
|
+
# by RFC 5322, which is the widely accepted standard for email address formats.
|
|
17
|
+
#
|
|
18
|
+
# @param email [String] The input string to be checked
|
|
19
|
+
#
|
|
20
|
+
# @return [Boolean] Returns true if email is a valid email address, false otherwise
|
|
21
|
+
#
|
|
22
|
+
# @example Valid emails
|
|
23
|
+
# is_valid("brutils@brutils.com") #=> true
|
|
24
|
+
# is_valid("user.name@example.com") #=> true
|
|
25
|
+
# is_valid("user+tag@example.co.uk") #=> true
|
|
26
|
+
# is_valid("user_123@test-domain.com") #=> true
|
|
27
|
+
#
|
|
28
|
+
# @example Invalid emails
|
|
29
|
+
# is_valid("invalid-email@brutils") #=> false (no TLD)
|
|
30
|
+
# is_valid(".user@example.com") #=> false (starts with dot)
|
|
31
|
+
# is_valid("user@") #=> false (no domain)
|
|
32
|
+
# is_valid("@example.com") #=> false (no local part)
|
|
33
|
+
# is_valid("user name@example.com") #=> false (space not allowed)
|
|
34
|
+
# is_valid(nil) #=> false (not a string)
|
|
35
|
+
#
|
|
36
|
+
# @note The validation rules generally follow RFC 5322 specifications:
|
|
37
|
+
# - Local part cannot start with a dot
|
|
38
|
+
# - Local part can contain: letters, numbers, dots, underscores, percent, plus, minus
|
|
39
|
+
# - Must have @ symbol
|
|
40
|
+
# - Domain can contain: letters, numbers, dots, hyphens
|
|
41
|
+
# - Must have at least one dot in domain
|
|
42
|
+
# - TLD must be at least 2 characters and only letters
|
|
43
|
+
def self.is_valid(email)
|
|
44
|
+
return false unless email.is_a?(String)
|
|
45
|
+
|
|
46
|
+
EMAIL_PATTERN.match?(email)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Alias for is_valid to provide alternative naming convention
|
|
50
|
+
class << self
|
|
51
|
+
alias valid? is_valid
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
module BrazilianUtils
|
|
2
|
+
# Utilities for consulting and validating the official *Natureza Jurídica* (Legal Nature)
|
|
3
|
+
# codes defined by the Receita Federal do Brasil (RFB).
|
|
4
|
+
#
|
|
5
|
+
# The codes and descriptions in this module are sourced from the official
|
|
6
|
+
# **Tabela de Natureza Jurídica** (RFB), as provided in the document used
|
|
7
|
+
# by the Cadastro Nacional (e.g., FCN).
|
|
8
|
+
#
|
|
9
|
+
# This module offers simple lookups and validation helpers based on the official table.
|
|
10
|
+
# It does not infer the current legal/registration status of any entity.
|
|
11
|
+
#
|
|
12
|
+
# Source: https://www.gov.br/empresas-e-negocios/pt-br/drei/links-e-downloads/arquivos/TABELADENATUREZAJURDICA.pdf
|
|
13
|
+
module LegalNatureUtils
|
|
14
|
+
# Official Legal Nature codes from Receita Federal do Brasil
|
|
15
|
+
# Format: 4-digit code => Description in Portuguese
|
|
16
|
+
LEGAL_NATURE = {
|
|
17
|
+
# 1. ADMINISTRAÇÃO PÚBLICA
|
|
18
|
+
'1015' => 'Órgão Público do Poder Executivo Federal',
|
|
19
|
+
'1023' => 'Órgão Público do Poder Executivo Estadual ou do Distrito Federal',
|
|
20
|
+
'1031' => 'Órgão Público do Poder Executivo Municipal',
|
|
21
|
+
'1040' => 'Órgão Público do Poder Legislativo Federal',
|
|
22
|
+
'1058' => 'Órgão Público do Poder Legislativo Estadual ou do Distrito Federal',
|
|
23
|
+
'1066' => 'Órgão Público do Poder Legislativo Municipal',
|
|
24
|
+
'1074' => 'Órgão Público do Poder Judiciário Federal',
|
|
25
|
+
'1082' => 'Órgão Público do Poder Judiciário Estadual',
|
|
26
|
+
'1104' => 'Autarquia Federal',
|
|
27
|
+
'1112' => 'Autarquia Estadual ou do Distrito Federal',
|
|
28
|
+
'1120' => 'Autarquia Municipal',
|
|
29
|
+
'1139' => 'Fundação Federal',
|
|
30
|
+
'1147' => 'Fundação Estadual ou do Distrito Federal',
|
|
31
|
+
'1155' => 'Fundação Municipal',
|
|
32
|
+
'1163' => 'Órgão Público Autônomo da União',
|
|
33
|
+
'1171' => 'Órgão Público Autônomo Estadual ou do Distrito Federal',
|
|
34
|
+
'1180' => 'Órgão Público Autônomo Municipal',
|
|
35
|
+
|
|
36
|
+
# 2. ENTIDADES EMPRESARIAIS
|
|
37
|
+
'2011' => 'Empresa Pública',
|
|
38
|
+
'2038' => 'Sociedade de Economia Mista',
|
|
39
|
+
'2046' => 'Sociedade Anônima Aberta',
|
|
40
|
+
'2054' => 'Sociedade Anônima Fechada',
|
|
41
|
+
'2062' => 'Sociedade Empresária Limitada',
|
|
42
|
+
'2070' => 'Sociedade Empresária em Nome Coletivo',
|
|
43
|
+
'2089' => 'Sociedade Empresária em Comandita Simples',
|
|
44
|
+
'2097' => 'Sociedade Empresária em Comandita por Ações',
|
|
45
|
+
'2100' => 'Sociedade Mercantil de Capital e Indústria (extinta pelo NCC/2002)',
|
|
46
|
+
'2127' => 'Sociedade Empresária em Conta de Participação',
|
|
47
|
+
'2135' => 'Empresário (Individual)',
|
|
48
|
+
'2143' => 'Cooperativa',
|
|
49
|
+
'2151' => 'Consórcio de Sociedades',
|
|
50
|
+
'2160' => 'Grupo de Sociedades',
|
|
51
|
+
'2178' => 'Estabelecimento, no Brasil, de Sociedade Estrangeira',
|
|
52
|
+
'2194' => 'Estabelecimento, no Brasil, de Empresa Binacional Argentino-Brasileira',
|
|
53
|
+
'2208' => 'Entidade Binacional Itaipu',
|
|
54
|
+
'2216' => 'Empresa Domiciliada no Exterior',
|
|
55
|
+
'2224' => 'Clube/Fundo de Investimento',
|
|
56
|
+
'2232' => 'Sociedade Simples Pura',
|
|
57
|
+
'2240' => 'Sociedade Simples Limitada',
|
|
58
|
+
'2259' => 'Sociedade em Nome Coletivo',
|
|
59
|
+
'2267' => 'Sociedade em Comandita Simples',
|
|
60
|
+
'2275' => 'Sociedade Simples em Conta de Participação',
|
|
61
|
+
'2305' => 'Empresa Individual de Responsabilidade Limitada',
|
|
62
|
+
|
|
63
|
+
# 3. ENTIDADES SEM FINS LUCRATIVOS
|
|
64
|
+
'3034' => 'Serviço Notarial e Registral (Cartório)',
|
|
65
|
+
'3042' => 'Organização Social',
|
|
66
|
+
'3050' => 'Organização da Sociedade Civil de Interesse Público (Oscip)',
|
|
67
|
+
'3069' => 'Outras Formas de Fundações Mantidas com Recursos Privados',
|
|
68
|
+
'3077' => 'Serviço Social Autônomo',
|
|
69
|
+
'3085' => 'Condomínio Edilícios',
|
|
70
|
+
'3093' => 'Unidade Executora (Programa Dinheiro Direto na Escola)',
|
|
71
|
+
'3107' => 'Comissão de Conciliação Prévia',
|
|
72
|
+
'3115' => 'Entidade de Mediação e Arbitragem',
|
|
73
|
+
'3123' => 'Partido Político',
|
|
74
|
+
'3131' => 'Entidade Sindical',
|
|
75
|
+
'3204' => 'Estabelecimento, no Brasil, de Fundação ou Associação Estrangeiras',
|
|
76
|
+
'3212' => 'Fundação ou Associação Domiciliada no Exterior',
|
|
77
|
+
'3999' => 'Outras Formas de Associação',
|
|
78
|
+
|
|
79
|
+
# 4. PESSOAS FÍSICAS
|
|
80
|
+
'4014' => 'Empresa Individual Imobiliária',
|
|
81
|
+
'4022' => 'Segurado Especial',
|
|
82
|
+
'4081' => 'Contribuinte individual',
|
|
83
|
+
|
|
84
|
+
# 5. ORGANIZAÇÕES INTERNACIONAIS E OUTRAS INSTITUIÇÕES EXTRATERRITORIAIS
|
|
85
|
+
'5002' => 'Organização Internacional e Outras Instituições Extraterritoriais'
|
|
86
|
+
}.freeze
|
|
87
|
+
|
|
88
|
+
# Normalizes a legal nature code to 4-digit format.
|
|
89
|
+
# Accepts formats like "2062", "206-2", or any string with exactly 4 digits.
|
|
90
|
+
#
|
|
91
|
+
# @param code [String] The code to normalize
|
|
92
|
+
# @return [String, nil] The normalized 4-digit code, or nil if invalid
|
|
93
|
+
#
|
|
94
|
+
# @private
|
|
95
|
+
def self.normalize(code)
|
|
96
|
+
return nil unless code.is_a?(String)
|
|
97
|
+
|
|
98
|
+
# Extract only digits from the input
|
|
99
|
+
digits = code.strip.gsub(/\D/, '')
|
|
100
|
+
|
|
101
|
+
# Return the digits only if we have exactly 4
|
|
102
|
+
digits.length == 4 ? digits : nil
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
private_class_method :normalize
|
|
106
|
+
|
|
107
|
+
# Checks if a string corresponds to a valid *Natureza Jurídica* code.
|
|
108
|
+
#
|
|
109
|
+
# Validation is based solely on the presence of the code in the official RFB table.
|
|
110
|
+
# It does not verify the current legal status or registration of the entity.
|
|
111
|
+
#
|
|
112
|
+
# @param code [String] The code to be validated. Accepts either "NNNN" or "NNN-N" format.
|
|
113
|
+
#
|
|
114
|
+
# @return [Boolean] Returns true if the normalized code exists in the official table,
|
|
115
|
+
# false otherwise.
|
|
116
|
+
#
|
|
117
|
+
# @example Valid codes
|
|
118
|
+
# is_valid("2062") #=> true (Sociedade Empresária Limitada)
|
|
119
|
+
# is_valid("206-2") #=> true (same, with hyphen)
|
|
120
|
+
# is_valid("1015") #=> true (Órgão Público Federal)
|
|
121
|
+
# is_valid("101-5") #=> true (same, with hyphen)
|
|
122
|
+
#
|
|
123
|
+
# @example Invalid codes
|
|
124
|
+
# is_valid("9999") #=> false (not in official table)
|
|
125
|
+
# is_valid("0000") #=> false (not in official table)
|
|
126
|
+
# is_valid("123") #=> false (wrong length)
|
|
127
|
+
# is_valid("abcd") #=> false (not digits)
|
|
128
|
+
# is_valid(nil) #=> false (not a string)
|
|
129
|
+
def self.is_valid(code)
|
|
130
|
+
normalized = normalize(code)
|
|
131
|
+
return false unless normalized
|
|
132
|
+
|
|
133
|
+
LEGAL_NATURE.key?(normalized)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Alias for is_valid to provide Ruby-style naming
|
|
137
|
+
class << self
|
|
138
|
+
alias valid? is_valid
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# Retrieves the description of a *Natureza Jurídica* code.
|
|
142
|
+
#
|
|
143
|
+
# @param code [String] The code to look up. Accepts either "NNNN" or "NNN-N" format.
|
|
144
|
+
#
|
|
145
|
+
# @return [String, nil] The full description if the code is valid, otherwise nil.
|
|
146
|
+
#
|
|
147
|
+
# @example Valid lookups
|
|
148
|
+
# get_description("2062")
|
|
149
|
+
# #=> "Sociedade Empresária Limitada"
|
|
150
|
+
#
|
|
151
|
+
# get_description("101-5")
|
|
152
|
+
# #=> "Órgão Público do Poder Executivo Federal"
|
|
153
|
+
#
|
|
154
|
+
# get_description("2305")
|
|
155
|
+
# #=> "Empresa Individual de Responsabilidade Limitada"
|
|
156
|
+
#
|
|
157
|
+
# @example Invalid lookups
|
|
158
|
+
# get_description("0000")
|
|
159
|
+
# #=> nil
|
|
160
|
+
#
|
|
161
|
+
# get_description("invalid")
|
|
162
|
+
# #=> nil
|
|
163
|
+
def self.get_description(code)
|
|
164
|
+
normalized = normalize(code)
|
|
165
|
+
return nil unless normalized
|
|
166
|
+
|
|
167
|
+
LEGAL_NATURE[normalized]
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
# Returns a copy of the full *Natureza Jurídica* table.
|
|
171
|
+
#
|
|
172
|
+
# @return [Hash<String, String>] Mapping from 4-digit codes to descriptions
|
|
173
|
+
#
|
|
174
|
+
# @example
|
|
175
|
+
# all_codes = list_all
|
|
176
|
+
# all_codes["2062"]
|
|
177
|
+
# #=> "Sociedade Empresária Limitada"
|
|
178
|
+
#
|
|
179
|
+
# all_codes.size
|
|
180
|
+
# #=> 64 (total number of codes in the official table)
|
|
181
|
+
def self.list_all
|
|
182
|
+
LEGAL_NATURE.dup
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
# Returns all codes within a specific category.
|
|
186
|
+
#
|
|
187
|
+
# Categories:
|
|
188
|
+
# - 1: Administração Pública (Public Administration)
|
|
189
|
+
# - 2: Entidades Empresariais (Business Entities)
|
|
190
|
+
# - 3: Entidades Sem Fins Lucrativos (Non-Profit Entities)
|
|
191
|
+
# - 4: Pessoas Físicas (Individuals)
|
|
192
|
+
# - 5: Organizações Internacionais (International Organizations)
|
|
193
|
+
#
|
|
194
|
+
# @param category [Integer, String] The category number (1-5)
|
|
195
|
+
#
|
|
196
|
+
# @return [Hash<String, String>] Codes and descriptions for the specified category
|
|
197
|
+
#
|
|
198
|
+
# @example
|
|
199
|
+
# business_entities = list_by_category(2)
|
|
200
|
+
# business_entities.keys
|
|
201
|
+
# #=> ["2011", "2038", "2046", ...]
|
|
202
|
+
#
|
|
203
|
+
# non_profits = list_by_category(3)
|
|
204
|
+
# non_profits["3123"]
|
|
205
|
+
# #=> "Partido Político"
|
|
206
|
+
def self.list_by_category(category)
|
|
207
|
+
category_str = category.to_s
|
|
208
|
+
return {} unless ['1', '2', '3', '4', '5'].include?(category_str)
|
|
209
|
+
|
|
210
|
+
LEGAL_NATURE.select { |code, _| code.start_with?(category_str) }
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
# Returns the category number for a given code.
|
|
214
|
+
#
|
|
215
|
+
# @param code [String] The code to check. Accepts either "NNNN" or "NNN-N" format.
|
|
216
|
+
#
|
|
217
|
+
# @return [Integer, nil] The category number (1-5), or nil if invalid
|
|
218
|
+
#
|
|
219
|
+
# @example
|
|
220
|
+
# get_category("2062")
|
|
221
|
+
# #=> 2 (Entidades Empresariais)
|
|
222
|
+
#
|
|
223
|
+
# get_category("101-5")
|
|
224
|
+
# #=> 1 (Administração Pública)
|
|
225
|
+
#
|
|
226
|
+
# get_category("9999")
|
|
227
|
+
# #=> nil (invalid code)
|
|
228
|
+
def self.get_category(code)
|
|
229
|
+
normalized = normalize(code)
|
|
230
|
+
return nil unless normalized && LEGAL_NATURE.key?(normalized)
|
|
231
|
+
|
|
232
|
+
normalized[0].to_i
|
|
233
|
+
end
|
|
234
|
+
end
|
|
235
|
+
end
|