diff --git a/doc/default/company.md b/doc/default/company.md index 303c100da0..5e0774c65d 100644 --- a/doc/default/company.md +++ b/doc/default/company.md @@ -84,4 +84,8 @@ Faker::Company.russian_tax_number #=> "0965855857" Faker::Company.russian_tax_number(region: '77') #=> "7717152803" Faker::Company.russian_tax_number(type: :individual) #=> "488935903348" Faker::Company.russian_tax_number(region: '77', type: :individual) #=> "779124694601" + +# Get a random formatted Indian tax number (GST) +Faker::Company.indian_gst_number #=> "15VQPNZ2126J2ZU" +Faker::Company.indian_gst_number(state_code: "22") #=> "22ZVWEY6632K0ZN" ``` diff --git a/lib/faker/default/company.rb b/lib/faker/default/company.rb index ec3feb6fcb..590cf3086d 100644 --- a/lib/faker/default/company.rb +++ b/lib/faker/default/company.rb @@ -464,6 +464,61 @@ def sic_code fetch('company.sic_code') end + ## + # Get a random Indian Goods and Services Tax (GST) number. + # For more on Indian tax number here: + # https://simple.wikipedia.org/wiki/GSTIN + # @params state code [String] Any state code. + # + # @return [String] + # @example + # Faker::Company.indian_gst_number #=> "15VQPNZ2126J2ZU" + # Faker::Company.indian_gst_number(state_code: "22") #=> "22ZVWEY6632K0ZN" + # + # @faker.version 3.2.1 + def indian_gst_number(state_code: nil) + # Check if state code is valid + state_code_ranges = [('02'..'38'), ['98']] + if state_code && !(state_code_ranges[0].include?(state_code) || state_code == '98') + raise ArgumentError, 'state code must be in a range of 02 to 38 or 98' + end + + PositionalGenerator.new(:string) do |gen| + # Generate a state code if not given + if state_code + gen.lit(state_code, name: :state_code_param) + else + gen.letter(name: :state_code_param, length: 1, ranges: state_code_ranges) + end + + # Construct taxpayer number + gen.group(name: :taxpayer_number) do |g_| + g_.letter(length: 3, ranges: ['A'..'Z']) + g_.letter(length: 1, ranges: [%w[A B C F G H L J P T K]].to_a) + g_.letter(length: 1, ranges: ['A'..'Z']) + g_.int(length: 4, ranges: [0..9999]) + g_.letter(length: 1, ranges: ['A'..'Z']) + end + + gen.int(name: :registration_number, length: 1, ranges: [0..9]) + + gen.letter(name: :z_char, length: 1, ranges: [['Z']]) + + gen.computed(deps: %i[state_code_param taxpayer_number registration_number]) do |state_code_param, taxpayer_number, registration_number| + gst_base = "#{state_code_param}#{taxpayer_number}#{registration_number}" + chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'.chars + values = gst_base.chars + sum = values.map.with_index do |char, index| + product = chars.index(char) * (index.odd? ? 2 : 1) + (product / chars.length).floor + (product % chars.length) + end.reduce(:+) + + checksum = (chars.length - (sum % chars.length)) % chars.length + chars[checksum] + end + end.generate + end + private # Mod11 functionality from https://github.com/badmanski/mod11/blob/master/lib/mod11.rb @@ -605,6 +660,19 @@ def spanish_b_algorithm(value) result.to_s[0].to_i + result.to_s[1].to_i end + + def calculate_gst_checksum(state_code, taxpayer_number, registration_number) + gst_base = "#{state_code}#{taxpayer_number}#{registration_number}" + chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'.chars + values = gst_base.upcase.chars + sum = values.map.with_index do |char, index| + product = chars.index(char) * (index.odd? ? 2 : 1) + (product / chars.length).floor + (product % chars.length) + end.reduce(:+) + + checksum = (chars.length - (sum % chars.length)) % chars.length + chars[checksum] + end end end end diff --git a/test/faker/default/test_faker_company.rb b/test/faker/default/test_faker_company.rb index 4360fb4a1a..2a62220c24 100644 --- a/test/faker/default/test_faker_company.rb +++ b/test/faker/default/test_faker_company.rb @@ -250,6 +250,23 @@ def test_spanish_b_algorithm assert_equal(3, @tester.send(:spanish_b_algorithm, 6)) end + def text_indian_gst_number + assert_match(/^([0-2][0-9]|[3][0-7])[A-Z]{3}[ABCFGHLJPTK][A-Z]\d{4}[A-Z][A-Z0-9][Z][A-Z0-9]$/i, @tester.indian_gst_number) + end + + def test_state_code_in_indian_gst_number + assert_raise ArgumentError do + @tester.indian_gst_number(state_code: '01') + end + assert_raise ArgumentError do + @tester.indian_gst_number(state_code: '100') + end + end + + def test_indian_gst_number_with_state_code + assert_match(/^(22)[A-Z]{3}[ABCFGHLJPTK][A-Z]\d{4}[A-Z][A-Z0-9][Z][A-Z0-9]$/i, @tester.indian_gst_number(state_code: '22')) + end + private def czech_o_n_checksum(org_no)