diff --git a/doc/default/id_number.md b/doc/default/id_number.md index 01dd0a62d7..b453fee370 100644 --- a/doc/default/id_number.md +++ b/doc/default/id_number.md @@ -22,9 +22,12 @@ Faker::IDNumber.valid_south_african_id_number #=> "8105128870184" Faker::IDNumber.invalid_south_african_id_number #=> "1642972065088" # Generate a Brazilian citizen number (CPF) +# Keyword arguments: formatted Faker::IDNumber.brazilian_citizen_number #=> "53540542221" +Faker::IDNumber.brazilian_citizen_number(formatted: true) #=> 000.000.000-00 -# Generate a formatted Brazilian CPF +# Generate a Brazilian ID Number (RG) # Keyword arguments: formatted -Faker::IDNumber.brazilian_citizen_number(formatted: true) #=> 000.000.000-00 +Faker::IDNumber.brazilian_id #=> 1212312312 +Faker::IDNumber.brazilian_id(formatted: true) #=> 12.123.123-12 ``` diff --git a/lib/faker/default/id_number.rb b/lib/faker/default/id_number.rb index cf424a8192..4c4bc47781 100644 --- a/lib/faker/default/id_number.rb +++ b/lib/faker/default/id_number.rb @@ -12,6 +12,9 @@ class IDNumber < Base ].freeze ZA_RACE_DIGIT = '8' ZA_CITIZENSHIP_DIGITS = %w[0 1].freeze + BRAZILIAN_ID_FORMAT = /(\d{1,2})(\d{3})(\d{3})([\dX])/ + BRAZILIAN_ID_FROM = 10_000_000 + BRAZILIAN_ID_TO = 99_999_999 class << self def valid @@ -83,6 +86,17 @@ def brazilian_citizen_number(formatted: false) formatted ? format('%s.%s.%s-%s', *number.scan(/\d{2,3}/).flatten) : number end + alias brazilian_cpf brazilian_citizen_number + + def brazilian_id(formatted: false) + digits = Faker::Number.between(to: BRAZILIAN_ID_FROM, from: BRAZILIAN_ID_TO).to_s + check_digit = brazilian_id_checksum_digit(digits) + number = [digits, check_digit].join + formatted ? format('%s.%s.%s-%s', *number.scan(BRAZILIAN_ID_FORMAT).flatten) : number + end + + alias brazilian_rg brazilian_id + private def south_african_id_checksum_digit(id_number) @@ -104,13 +118,36 @@ def south_african_id_checksum_digit(id_number) end def brazilian_citizen_number_checksum_digit(digits) - digit_sum = digits.chars.each_with_index.inject(0) do |acc, (digit, i)| + checksum = brazilian_document_checksum(digits) + brazilian_document_digit(checksum) + end + + def brazilian_id_checksum_digit(digits) + checksum = brazilian_document_checksum(digits) + brazilian_document_digit(checksum, id: true) + end + + def brazilian_document_checksum(digits) + digits.chars.each_with_index.inject(0) do |acc, (digit, i)| acc + digit.to_i * (digits.size + 1 - i) end * 10 - remainder = digit_sum % 11 + end + + def brazilian_document_digit(checksum, id = false) + remainder = checksum % 11 + id ? brazilian_id_digit(remainder) : brazilian_citizen_number_digit(remainder) + end + + def brazilian_citizen_number_digit(remainder) remainder == 10 ? '0' : remainder.to_s end + def brazilian_id_digit(remainder) + subtraction = 11 - remainder.to_i + digits = { 10 => 'X', 11 => '0' } + digits.include?(subtraction) ? digits[subtraction] : subtraction.to_s + end + def _translate(key) parse("id_number.#{key}") end diff --git a/test/faker/default/test_faker_id_number.rb b/test/faker/default/test_faker_id_number.rb index 393d4e0b88..4e67c1fc99 100644 --- a/test/faker/default/test_faker_id_number.rb +++ b/test/faker/default/test_faker_id_number.rb @@ -67,18 +67,6 @@ def test_brazilian_citizen_number sample = @tester.brazilian_citizen_number assert_match(/^\d{11}$/, sample) assert_match(/(\d)((?!\1)\d)+/, sample) - digit_sum = sample[0..8].chars.each_with_index.inject(0) do |acc, (digit, i)| - acc + digit.to_i * (10 - i) - end * 10 - remainder = digit_sum % 11 - first_digit = remainder == 10 ? '0' : remainder.to_s - assert_equal sample[9], first_digit - digit_sum = sample[0..9].chars.each_with_index.inject(0) do |acc, (digit, i)| - acc + digit.to_i * (11 - i) - end * 10 - remainder = digit_sum % 11 - second_digit = remainder == 10 ? '0' : remainder.to_s - assert_equal sample[10], second_digit end def test_brazilian_citizen_number_formatted @@ -86,6 +74,67 @@ def test_brazilian_citizen_number_formatted assert_match(/^\d{3}\.\d{3}\.\d{3}-\d{2}$/, sample) end + def test_brazilian_id + sample = @tester.brazilian_id + assert_match(/^\d{9}$/, sample) + assert_match(/(\d)((?!\1)\d)+/, sample) + end + + def test_brazilian_id_formatted + sample = @tester.brazilian_id(formatted: true) + assert_match(/^\d{1,2}.\d{3}.\d{3}-[\dX]$/, sample) + end + + def test_brazilian_citizen_number_checksum_digit + digits = '128991760' + checksum_digit = Faker::IDNumber.send(:brazilian_citizen_number_checksum_digit, digits) + assert_equal checksum_digit, '4' + digits = '1289917604' + checksum_digit = Faker::IDNumber.send(:brazilian_citizen_number_checksum_digit, digits) + assert_equal checksum_digit, '8' + end + + def test_brazilian_id_checksum_digit + digits = '41987080' + checksum_digit = Faker::IDNumber.send(:brazilian_id_checksum_digit, digits) + assert_equal checksum_digit, '5' + end + + def test_brazilian_document_checksum + digits = '123456789' + checksum = Faker::IDNumber.send(:brazilian_document_checksum, digits) + assert_equal checksum, 2100 + end + + def test_brazilian_document_digit + citizen_number_digit10 = Faker::IDNumber.send(:brazilian_document_digit, 10) + citizen_number_digit_other = Faker::IDNumber.send(:brazilian_document_digit, 9) + id_digit10 = Faker::IDNumber.send(:brazilian_document_digit, 1, true) + id_digit11 = Faker::IDNumber.send(:brazilian_document_digit, 0, true) + id_digit_other = Faker::IDNumber.send(:brazilian_document_digit, 2, true) + assert_equal citizen_number_digit10, '0' + assert_equal citizen_number_digit_other, '9' + assert_equal id_digit10, 'X' + assert_equal id_digit11, '0' + assert_equal id_digit_other, '9' + end + + def test_brazilian_citizen_number_digit + digit10 = Faker::IDNumber.send(:brazilian_citizen_number_digit, 10) + digit_other = Faker::IDNumber.send(:brazilian_citizen_number_digit, 9) + assert_equal digit10, '0' + assert_equal digit_other, '9' + end + + def test_brazilian_id_digit + digit10 = Faker::IDNumber.send(:brazilian_id_digit, 1) + digit11 = Faker::IDNumber.send(:brazilian_id_digit, 0) + digit_other = Faker::IDNumber.send(:brazilian_id_digit, 2) + assert_equal digit10, 'X' + assert_equal digit11, '0' + assert_equal digit_other, '9' + end + private def south_african_id_number_to_date_of_birth_string(sample)