tin_valid 0.1.0 → 0.1.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f5b010dba783fef5da5341515542f830f73ac1baeca9fd8334fee16867d07a5a
4
- data.tar.gz: 42d87f203dd942dc281636c0f3ff6b21e4104d9ae22904974cc81d7f503ead3a
3
+ metadata.gz: b248c0be6699ce56b48b082c320833ff8cb5594a62762d298ea66315d8d7555b
4
+ data.tar.gz: a04549a7599fbadd881f1f905e2c13235d83a3e1d4b7bd8d79dc16393cd18465
5
5
  SHA512:
6
- metadata.gz: 886d2bfe8eb377af5fed28166a3a97f2a56f0c5dddea52c06f5e844d0a6d7194e73750a601422ce9e2a90ed93fe5f7ee99f323c6af56c375d53b5b4a83bb1b30
7
- data.tar.gz: '09cd02962705707d397f82dba292263a5414ecf1993bb67c5d87645f6f782a50a74cfe75c97e3cddba494767a8442de2ad6515872b0b5a60d16e4ad22d6f5875'
6
+ metadata.gz: 608e79cfe16d11cd236c02a4b5dffa9062a4b26b0f8816c2e9a9da350471917a5cc7ac6802202223ec6936f82cc0f91f9b820118a9c56868b2c6c4a9eaa8f67f
7
+ data.tar.gz: a17c2624d59049524fbeb5b8adec5263dd6c4009d7bb90c594ad01c499c990a6784515a195bbd3a16148278c32f524b71067b6f0095020dccd24ae5a65987da4
data/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.1.1] - 2025-04-15
4
+
5
+ Features:
6
+ - Add Germany 🇩🇪
7
+ - Add Greece 🇬🇷
8
+ - Add Hungary 🇭🇺
9
+ - Add Ireland 🇮🇪
10
+ - Add #normalized to Austria 🇦🇹
11
+
3
12
  ## [0.1.0] - 2025-04-14
4
13
 
5
- - Initial release
14
+ - Initial release with:
15
+ - Austria 🇦🇹
16
+ - Belgium 🇧🇪
17
+ - Bulgaria 🇧🇬
18
+ - Croatia 🇭🇷
19
+ - Cyprus 🇨🇾
20
+ - Czechia 🇨🇿
21
+ - Denmark 🇩🇰
22
+ - Estonia 🇪🇪
23
+ - Germany 🇩🇪
24
+ - Greece 🇬🇷
25
+ - Sweden 🇸🇪
data/README.md CHANGED
@@ -1,20 +1,27 @@
1
- # TinValid
1
+ # TinValid 🇪🇺
2
2
 
3
3
  Validate Tax Identification Numbers (TINs) for the following European countries:
4
4
 
5
- - Austria
6
- - Belgium
7
- - Bulgaria
8
- - Croatia
9
- - Cyprus
10
- - Czechia
11
- - Denmark
12
- - Estonia
13
- - Sweden
5
+ - Austria 🇦🇹
6
+ - Belgium 🇧🇪
7
+ - Bulgaria 🇧🇬
8
+ - Croatia 🇭🇷
9
+ - Cyprus 🇨🇾
10
+ - Czechia 🇨🇿
11
+ - Denmark 🇩🇰
12
+ - Estonia 🇪🇪
13
+ - Germany 🇩🇪
14
+ - Greece 🇬🇷
15
+ - Hungary 🇭🇺
16
+ - Ireland 🇮🇪
17
+ - Sweden 🇸🇪
18
+
19
+ See also the [descriptions of the structure provided by the European
20
+ Union](https://taxation-customs.ec.europa.eu/online-services/online-services-and-databases-taxation/taxpayer-identification-number-tin_en).
14
21
 
15
22
  ## Installation
16
23
 
17
- Install the gem and add to the application's Gemfile by executing:
24
+ Add the gem to your applications Gemfile by executing:
18
25
 
19
26
  ```bash
20
27
  bundle add tin_valid
@@ -31,7 +38,8 @@ gem install tin_valid
31
38
 
32
39
  ```rb
33
40
  # Austria
34
- TinValid::AustriaTin.new(tin: "").valid?
41
+ TinValid::AustriaTin.new(tin: "93-173/6581").valid? # => true
42
+ TinValid::AustriaTin.new(tin: "93-173/6581").normalized # => "931736581"
35
43
 
36
44
  # Belgium
37
45
  # Optional birth_date
@@ -80,10 +88,10 @@ git commits and the created tag, and push the `.gem` file to
80
88
  ## Contributing
81
89
 
82
90
  Bug reports and pull requests are welcome on GitHub at
83
- https://github.com/sunny/tin_valid. This project is intended to be a safe,
91
+ https://github.com/cults/tin_valid. This project is intended to be a safe,
84
92
  welcoming space for collaboration, and contributors are expected to adhere to
85
93
  the
86
- [code of conduct](https://github.com/sunny/tin_valid/blob/main/CODE_OF_CONDUCT.md).
94
+ [code of conduct](https://github.com/cults/tin_valid/blob/main/CODE_OF_CONDUCT.md).
87
95
 
88
96
  ## License
89
97
 
@@ -94,4 +102,4 @@ The gem is available as open source under the terms of the
94
102
 
95
103
  Everyone interacting in the TinValid project's codebases, issue trackers, chat
96
104
  rooms and mailing lists is expected to follow the
97
- [code of conduct](https://github.com/sunny/tin_valid/blob/main/CODE_OF_CONDUCT.md).
105
+ [code of conduct](https://github.com/cults/tin_valid/blob/main/CODE_OF_CONDUCT.md).
@@ -1,15 +1,28 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module TinValid
4
- class AustriaTin < Data.define(:tin)
4
+ class AustriaTin
5
+ def initialize(tin:)
6
+ @tin = tin
7
+ end
8
+
9
+ attr_reader :tin
10
+
5
11
  def valid?
6
- return false unless /\A[0-9]{9}\z/.match?(tin)
12
+ return false unless MATCHER.match?(normalized)
7
13
 
8
- tin[-1] == check
14
+ normalized[-1] == check
15
+ end
16
+
17
+ def normalized
18
+ @normalized ||= tin.to_s.strip.tr("-/", "")
9
19
  end
10
20
 
11
21
  private
12
22
 
23
+ MATCHER = %r{\A[0-9]{2}-?[0-9]{3}/?[0-9]{4}\z}
24
+ private_constant :MATCHER
25
+
13
26
  # rubocop:disable Metrics/AbcSize
14
27
  def check
15
28
  # 1. Multiply the values of each position by the corresponding weight:
@@ -22,10 +35,10 @@ module TinValid
22
35
  # - C7: 1
23
36
  # - C8: 2
24
37
  values_by_weight =
25
- tin
26
- .chars
38
+ normalized
39
+ .chars[..-2]
27
40
  .each_with_index
28
- .map { |n, i| n.to_i * (i.even? ? 2 : 1) }
41
+ .map { |n, i| n.to_i * (i.even? ? 1 : 2) }
29
42
 
30
43
  # 2. If the product of a doubling operation is > 9, sum the digits of the
31
44
  # product;
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TinValid
4
+ class GermanyTin < Data.define(:tin)
5
+ def valid?
6
+ valid_v1? || valid_v2?
7
+ end
8
+
9
+ private
10
+
11
+ def valid_v1?
12
+ # 1. 13 characters
13
+ # 2. Only digits
14
+ # 3. C5 is a 0
15
+ /\A[0-9]{4}0[0-9]{8}\z/.match?(tin)
16
+ end
17
+
18
+ # rubocop:disable Metrics/AbcSize
19
+ def valid_v2?
20
+ # C1: Must never be 0.
21
+ return false unless /\A[1-9][0-9]{10}\z/.match?(tin)
22
+
23
+ # C1-C10: One and only one mandatory duplicate or triple value:
24
+ # - One of the first ten digits is used twice (the recurrent digits do
25
+ # not have to be located at subsequent positions but they can be);
26
+ # - One of the first ten digits is used tree times (only two recurrent
27
+ # digits are allowed to be one after another).
28
+ consecutive_chars =
29
+ tin[..-2]
30
+ .chars
31
+ .group_by(&:itself)
32
+ .select { |_, chars| chars.size > 1 }
33
+
34
+ return false if consecutive_chars.size != 1
35
+ return false if consecutive_chars.first.last.size > 3
36
+
37
+ tin[-1].to_i == check_v2
38
+ end
39
+ # rubocop:enable Metrics/AbcSize
40
+
41
+ def check_v2
42
+ # 1. Initialize the variable X to 10.
43
+ y = tin[..-2].chars.inject(10) do |x, char|
44
+ # 2. Take C1 + X modulo 10. If result is 0, result is 10;
45
+ result = (char.to_i + x) % 10
46
+ result = 10 if result == 0
47
+
48
+ # 3. Multiply the result by 2;
49
+ result *= 2
50
+
51
+ # 4. Take modulo 11 of the result. Update the value of variable X with the result of this operation;
52
+ x = result % 11
53
+
54
+ # 5. Take C2 + X modulo 10. If result is 0, result is 10;
55
+ # 6. Multiply the result by 2;
56
+ # 7. Take modulo 11 of the result. Update the value of variable X with the result of this operation;
57
+ # 8. Apply steps 5, 6 and 7 in an analogue way for digits C3 to C10. Consider that last value called Y;
58
+ x
59
+ end
60
+
61
+ # 9. 11 - Y = check digit. If check digit = 10, replace it by 0.
62
+ check = 11 - y
63
+ check == 10 ? 0 : check
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TinValid
4
+ class GreeceTin < Data.define(:tin)
5
+ def valid?
6
+ /\A[0-9]{9}\z/.match?(tin)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TinValid
4
+ class HungaryTin < Data.define(:tin)
5
+ def valid?
6
+ return false unless /\A8[0-9]{9}\z/.match?(tin)
7
+
8
+ tin[-1].to_i == check
9
+ end
10
+
11
+ private
12
+
13
+ def check
14
+ tin[..-2].chars
15
+
16
+ # 1. Multiply the values of each position by the corresponding weight:
17
+ # 2. Add up the results of the above multiplications;
18
+ result = (1..9).each_with_index.sum { |num, i| num * tin[i].to_i }
19
+
20
+ # 3. Get modulo 11 of the result of the previous addition;
21
+ # 4. Check digit = remainder.
22
+ result % 11
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TinValid
4
+ class IrelandTin < Data.define(:tin)
5
+ def valid?
6
+ /\A[0-9]{7}[A-W][A-IW]?\z/.match?(tin) &&
7
+ tin.chars[7] == check
8
+ end
9
+
10
+ private
11
+
12
+ # rubocop:disable Metrics/AbcSize
13
+ def check
14
+ # 1. In reverse order, each digit is multiplied by a weight started at 2:
15
+ weights = 8.downto(2)
16
+ digits = weights.with_index.map { |weight, i| weight * tin.chars[i].to_i }
17
+
18
+ # 2. LetterToNumber(C9) is based on the following mapping:
19
+ # “A”=1, “B”=2, “C”=3, “D”=4, “E”=5, “F”=6, “G”=7, “H”=8, “I”=9
20
+ # A “W” or absence of character in position 9 is allocated a numeric
21
+ # value of 0.
22
+ digits.push letter_to_number(tin.chars[9 - 1]) * 9
23
+
24
+ # 3. Add up each result;
25
+ sum = digits.sum
26
+
27
+ # 4. The remainder of the modulo 23 indicates the character position on
28
+ # the alphabet according to the following mapping:
29
+ # 0=”W”, 1=”A”, 2=”B”, 3=”C”… 22=”V”
30
+ number_to_letter(sum % 23)
31
+ end
32
+ # rubocop:enable Metrics/AbcSize
33
+
34
+ def letter_to_number(letter)
35
+ return 0 if letter.nil?
36
+
37
+ case letter
38
+ in "W" then 0
39
+ in "A".."I" then letter.ord - 64
40
+ end
41
+ end
42
+
43
+ def number_to_letter(number)
44
+ case number
45
+ in 0 then "W"
46
+ in 1..22 then (number + 64).chr
47
+ end
48
+ end
49
+ end
50
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module TinValid
4
- VERSION = "0.1.0"
4
+ VERSION = "0.1.1"
5
5
  end
data/lib/tin_valid.rb CHANGED
@@ -10,9 +10,10 @@ require_relative "tin_valid/cyprus_tin"
10
10
  require_relative "tin_valid/czechia_tin"
11
11
  require_relative "tin_valid/denmark_tin"
12
12
  require_relative "tin_valid/estonia_tin"
13
+ require_relative "tin_valid/germany_tin"
14
+ require_relative "tin_valid/greece_tin"
15
+ require_relative "tin_valid/hungary_tin"
16
+ require_relative "tin_valid/ireland_tin"
13
17
  require_relative "tin_valid/sweden_tin"
14
18
 
15
- module TinValid
16
- class Error < StandardError; end
17
- # Your code goes here...
18
- end
19
+ module TinValid; end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tin_valid
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sunny Ripert
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2025-04-14 00:00:00.000000000 Z
10
+ date: 2025-04-15 00:00:00.000000000 Z
11
11
  dependencies: []
12
12
  email:
13
13
  - sunny@sunfox.org
@@ -31,6 +31,10 @@ files:
31
31
  - lib/tin_valid/czechia_tin.rb
32
32
  - lib/tin_valid/denmark_tin.rb
33
33
  - lib/tin_valid/estonia_tin.rb
34
+ - lib/tin_valid/germany_tin.rb
35
+ - lib/tin_valid/greece_tin.rb
36
+ - lib/tin_valid/hungary_tin.rb
37
+ - lib/tin_valid/ireland_tin.rb
34
38
  - lib/tin_valid/sweden_tin.rb
35
39
  - lib/tin_valid/version.rb
36
40
  - sig/tin_valid.rbs