Module: BrazilianUtils::CNHUtils

Defined in:
lib/brazilian-utils/cnh-utils.rb

Class Method Summary collapse

Class Method Details

.format(value, options = {}) ⇒ String

Formats a CNH number as 000000000-00 (9 digits, hyphen, 2 check digits). The mask is applied as far as the digits go.

Examples:

format("00000000119")  #=> "000000001-19"

Parameters:

  • value (String, Integer) —

    The value to format.

  • options (Hash) (defaults to: {}) —

    :pad left-pads the value with zeros to 11 digits first.

Returns:

  • (String)


98
99
100
101
102
103
104
# File 'lib/brazilian-utils/cnh-utils.rb', line 98

def self.format(value, options = {})
  digits = value.to_s.gsub(/\D/, '')
  digits = digits.rjust(11, '0') if options[:pad] || options['pad']
  return digits if digits.length <= 9

  "#{digits[0, 9]}-#{digits[9..-1]}"
end

.generate ⇒ String

Generates a valid random CNH number (11 digits, unformatted).

Returns:

  • (String)


122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/brazilian-utils/cnh-utils.rb', line 122

def self.generate
  loop do
    base9 = 9.times.map { rand(0..9) }
    next if base9.uniq.length == 1

    first_rest = calculate_first_rest(base9)
    first_verificator = first_rest > 9 ? 0 : first_rest

    sum2 = 0
    9.times { |i| sum2 += base9[i] * (i + 1) }
    result = sum2 % 11
    result = (result - 2).negative? ? result + 9 : result - 2 if first_rest > 9
    second_verificator = result > 9 ? 0 : result

    cnh = "#{base9.join}#{first_verificator}#{second_verificator}"
    return cnh if valid?(cnh)
  end
end

.parse(value) ⇒ String

Removes CNH formatting and keeps only digits, capped to 11 digits.

Examples:

parse("000000001-19")  #=> "00000000119"

Parameters:

  • value (String, Integer)

Returns:

  • (String)


113
114
115
116
117
# File 'lib/brazilian-utils/cnh-utils.rb', line 113

def self.parse(value)
  return '' unless value.is_a?(String) || value.is_a?(Integer)

  value.to_s.gsub(/\D/, '')[0, 11]
end

.valid?(cnh) ⇒ Boolean

Validates the registration number for the Brazilian CNH (Carteira Nacional de Habilitação) that was created in 2022.

Previous versions of the CNH are not supported in this version. This function checks if the given CNH is valid based on the format and allowed characters, verifying the verification digits.

Examples:

valid?("12345678901")      #=> false
valid?("A2C45678901")      #=> false
valid?("98765432100")      #=> true
valid?("987654321-00")     #=> true

Parameters:

  • cnh (String) —

    CNH string (symbols will be ignored).

Returns:

  • (Boolean) —

    true if CNH has a valid format, false otherwise.



18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# File 'lib/brazilian-utils/cnh-utils.rb', line 18

def self.valid?(cnh)
  return false unless cnh

  # Clean the input and check for numbers only
  clean_cnh = cnh.to_s.gsub(/\D/, '')

  return false if clean_cnh.empty?
  return false if clean_cnh.length != 11

  # Reject sequences as "00000000000", "11111111111", etc.
  return false if clean_cnh == clean_cnh[0] * 11

  # Cast digits to array of integers
  digits = clean_cnh.chars.map(&:to_i)
  first_verificator = digits[9]
  second_verificator = digits[10]

  # Check the 10th digit; keep the raw remainder (0-10), since the
  # decrement applied to the 11th digit depends on whether the *pre-cap*
  # remainder overflowed (>9), not on the printed digit (always 0-9).
  first_rest = calculate_first_rest(digits)
  expected_first = first_rest > 9 ? 0 : first_rest
  return false unless expected_first == first_verificator

  # Check the 11th digit
  check_second_verificator(digits, second_verificator, first_rest)
end