tin_valid 0.1.1 → 1.0.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 +4 -4
- data/.rspec +0 -1
- data/.rubocop.yml +5 -0
- data/CHANGELOG.md +17 -1
- data/README.md +72 -2
- data/lib/tin_valid/germany_tin.rb +6 -3
- data/lib/tin_valid/hungary_tin.rb +0 -2
- data/lib/tin_valid/italy_tin.rb +170 -0
- data/lib/tin_valid/latvia_tin.rb +59 -0
- data/lib/tin_valid/lithuania_tin.rb +72 -0
- data/lib/tin_valid/luxembourg_tin.rb +107 -0
- data/lib/tin_valid/malta_tin.rb +45 -0
- data/lib/tin_valid/netherlands_tin.rb +29 -0
- data/lib/tin_valid/poland_tin.rb +78 -0
- data/lib/tin_valid/portugal_tin.rb +38 -0
- data/lib/tin_valid/romania_tin.rb +84 -0
- data/lib/tin_valid/slovakia_tin.rb +64 -0
- data/lib/tin_valid/slovenia_tin.rb +32 -0
- data/lib/tin_valid/spain_tin.rb +45 -0
- data/lib/tin_valid/united_kingdom_tin.rb +22 -0
- data/lib/tin_valid/version.rb +1 -1
- data/lib/tin_valid.rb +13 -0
- metadata +15 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c64b5917083db71c567a2b80c3edf35d58d32b07da94b9e0ced0e189a3bb4f13
|
4
|
+
data.tar.gz: 9785af41b29fc7c335d1f7f062ed8ae217a421e145005a783bd56eeb43d75c5a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '097ef7fcbdeb7f95b0456fdcd930501f9da4eb85ddb9bc0a5a8e9996d58aa03939fe409d2a0ed7f0c25ae1bee786bcce19a3c079e91969b62839337d581f5d3a'
|
7
|
+
data.tar.gz: e4bb19d3238b04228390f78e505375af8e695f2cb3060c518f6aa6e41b2b61db2f065fc483ade6a84f45213e05c90f55f76b4a18f36f54b1a0615f505bf08e05
|
data/.rspec
CHANGED
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,22 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
+
## [1.0.0] - 2025-04-17
|
4
|
+
|
5
|
+
Features:
|
6
|
+
- Add Italy 🇮🇹
|
7
|
+
- Add Latvia 🇱🇻
|
8
|
+
- Add Luxembourg 🇱🇺
|
9
|
+
- Add Lithuania 🇱🇹
|
10
|
+
- Add Malta 🇲🇹
|
11
|
+
- Add Netherlands 🇳🇱
|
12
|
+
- Add Poland 🇵🇱
|
13
|
+
- Add Portugal 🇵🇹
|
14
|
+
- Add Romania 🇷🇴
|
15
|
+
- Add Slovakia 🇸🇰
|
16
|
+
- Add Slovenia 🇸🇮
|
17
|
+
- Add Spain 🇪🇸
|
18
|
+
- Add United Kingdom 🇬🇧
|
19
|
+
|
3
20
|
## [0.1.1] - 2025-04-15
|
4
21
|
|
5
22
|
Features:
|
@@ -7,7 +24,6 @@ Features:
|
|
7
24
|
- Add Greece 🇬🇷
|
8
25
|
- Add Hungary 🇭🇺
|
9
26
|
- Add Ireland 🇮🇪
|
10
|
-
- Add #normalized to Austria 🇦🇹
|
11
27
|
|
12
28
|
## [0.1.0] - 2025-04-14
|
13
29
|
|
data/README.md
CHANGED
@@ -14,7 +14,20 @@ Validate Tax Identification Numbers (TINs) for the following European countries:
|
|
14
14
|
- Greece 🇬🇷
|
15
15
|
- Hungary 🇭🇺
|
16
16
|
- Ireland 🇮🇪
|
17
|
+
- Italy 🇮🇹
|
18
|
+
- Latvia 🇱🇻
|
19
|
+
- Lithuania 🇱🇹
|
20
|
+
- Luxembourg 🇱🇺
|
21
|
+
- Malta 🇲🇹
|
22
|
+
- Netherlands 🇳🇱
|
23
|
+
- Poland 🇵🇱
|
24
|
+
- Portugal 🇵🇹
|
25
|
+
- Romania 🇷🇴
|
26
|
+
- Slovakia 🇸🇰
|
27
|
+
- Slovenia 🇸🇮
|
28
|
+
- Spain 🇪🇸
|
17
29
|
- Sweden 🇸🇪
|
30
|
+
- United Kingdom 🇬🇧
|
18
31
|
|
19
32
|
See also the [descriptions of the structure provided by the European
|
20
33
|
Union](https://taxation-customs.ec.europa.eu/online-services/online-services-and-databases-taxation/taxpayer-identification-number-tin_en).
|
@@ -38,8 +51,7 @@ gem install tin_valid
|
|
38
51
|
|
39
52
|
```rb
|
40
53
|
# Austria
|
41
|
-
TinValid::AustriaTin.new(tin: "
|
42
|
-
TinValid::AustriaTin.new(tin: "93-173/6581").normalized # => "931736581"
|
54
|
+
TinValid::AustriaTin.new(tin: "…").valid? # => true
|
43
55
|
|
44
56
|
# Belgium
|
45
57
|
# Optional birth_date
|
@@ -68,9 +80,67 @@ TinValid::DenmarkTin.new(tin: "…", birth_date: Date.new(…)).valid?
|
|
68
80
|
# Optional birth_date
|
69
81
|
TinValid::EstoniaTin.new(tin: "…", birth_date: Date.new(…)).valid?
|
70
82
|
|
83
|
+
# Germany
|
84
|
+
TinValid::GermanyTin.new(tin: "…").valid?
|
85
|
+
|
86
|
+
# Greece
|
87
|
+
TinValid::GreeceTin.new(tin: "…").valid?
|
88
|
+
|
89
|
+
# Hungary
|
90
|
+
TinValid::HungaryTin.new(tin: "…").valid?
|
91
|
+
|
92
|
+
# Ireland
|
93
|
+
TinValid::IrelandTin.new(tin: "…").valid?
|
94
|
+
|
95
|
+
# Italy
|
96
|
+
# Optional birth_date
|
97
|
+
TinValid::ItalyTin.new(tin: "…", birth_date: Date.new(…)).valid?
|
98
|
+
|
99
|
+
# Latvia
|
100
|
+
# Optional birth_date
|
101
|
+
TinValid::LatviaTin.new(tin: "…", birth_date: Date.new(…)).valid?
|
102
|
+
|
103
|
+
# Lithuania
|
104
|
+
# Optional birth_date
|
105
|
+
TinValid::LithuaniaTin.new(tin: "…", birth_date: Date.new(…)).valid?
|
106
|
+
|
107
|
+
# Luxembourg
|
108
|
+
# Optional birth_date
|
109
|
+
TinValid::LuxembourgTin.new(tin: "…", birth_date: Date.new(…)).valid?
|
110
|
+
|
111
|
+
# Malta
|
112
|
+
TinValid::MaltaTin.new(tin: "…").valid?
|
113
|
+
|
114
|
+
# Netherlands
|
115
|
+
TinValid::NetherlandsTin.new(tin: "…").valid?
|
116
|
+
|
117
|
+
# Poland
|
118
|
+
# Optional birth_date
|
119
|
+
TinValid::PolandTin.new(tin: "…", birth_date: Date.new(…)).valid?
|
120
|
+
|
121
|
+
# Portugal
|
122
|
+
TinValid::PortugalTin.new(tin: "…").valid?
|
123
|
+
|
124
|
+
# Romania
|
125
|
+
# Optional birth_date
|
126
|
+
TinValid::RomaniaTin.new(tin: "…", birth_date: Date.new(…)).valid?
|
127
|
+
|
128
|
+
# Slovakia
|
129
|
+
# Optional birth_date
|
130
|
+
TinValid::SlovakiaTin.new(tin: "…", birth_date: Date.new(…)).valid?
|
131
|
+
|
132
|
+
# Slovenia
|
133
|
+
TinValid::SloveniaTin.new(tin: "…").valid?
|
134
|
+
|
135
|
+
# Spain
|
136
|
+
TinValid::SpainTin.new(tin: "…").valid?
|
137
|
+
|
71
138
|
# Sweden
|
72
139
|
# Optional birth_date
|
73
140
|
TinValid::SwedenTin.new(tin: "…", birth_date: Date.new(…)).valid?
|
141
|
+
|
142
|
+
# United Kingdom
|
143
|
+
TinValid::UnitedKingdomTin.new(tin: "…").valid?
|
74
144
|
```
|
75
145
|
|
76
146
|
## Development
|
@@ -48,13 +48,16 @@ module TinValid
|
|
48
48
|
# 3. Multiply the result by 2;
|
49
49
|
result *= 2
|
50
50
|
|
51
|
-
# 4. Take modulo 11 of the result. Update the value of variable X with
|
51
|
+
# 4. Take modulo 11 of the result. Update the value of variable X with
|
52
|
+
# the result of this operation;
|
52
53
|
x = result % 11
|
53
54
|
|
54
55
|
# 5. Take C2 + X modulo 10. If result is 0, result is 10;
|
55
56
|
# 6. Multiply the result by 2;
|
56
|
-
# 7. Take modulo 11 of the result. Update the value of variable X with
|
57
|
-
#
|
57
|
+
# 7. Take modulo 11 of the result. Update the value of variable X with
|
58
|
+
# the result of this operation;
|
59
|
+
# 8. Apply steps 5, 6 and 7 in an analogue way for digits C3 to C10.
|
60
|
+
# Consider that last value called Y;
|
58
61
|
x
|
59
62
|
end
|
60
63
|
|
@@ -11,8 +11,6 @@ module TinValid
|
|
11
11
|
private
|
12
12
|
|
13
13
|
def check
|
14
|
-
tin[..-2].chars
|
15
|
-
|
16
14
|
# 1. Multiply the values of each position by the corresponding weight:
|
17
15
|
# 2. Add up the results of the above multiplications;
|
18
16
|
result = (1..9).each_with_index.sum { |num, i| num * tin[i].to_i }
|
@@ -0,0 +1,170 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TinValid
|
4
|
+
# rubocop:disable Metrics/ClassLength
|
5
|
+
class ItalyTin
|
6
|
+
def initialize(tin:, birth_date: nil)
|
7
|
+
@tin = tin
|
8
|
+
@birth_date = birth_date
|
9
|
+
end
|
10
|
+
|
11
|
+
attr_reader :tin, :birth_date
|
12
|
+
|
13
|
+
def valid?
|
14
|
+
match = MATCHER.match(tin)
|
15
|
+
return false unless match
|
16
|
+
return false unless valid_date?(match)
|
17
|
+
|
18
|
+
tin[-1] == check
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def valid_date?(match)
|
24
|
+
# C7,C8: Two last digits of a year.
|
25
|
+
year = replacement_to_i(match[:year])
|
26
|
+
|
27
|
+
# C9: A letter representing a month;
|
28
|
+
month = MONTH_LETTERS.fetch(match[:month])
|
29
|
+
|
30
|
+
# C10,C11: Day of month (in the range 1...31 for men)
|
31
|
+
# or day of month + 40 (in the range 41...71 for women).
|
32
|
+
day = replacement_to_i(match[:day])
|
33
|
+
day -= 40 if day > 40
|
34
|
+
|
35
|
+
if birth_date
|
36
|
+
birth_date == date("#{birth_century}#{year}", month, day)
|
37
|
+
else
|
38
|
+
date?("19#{year}", month, day) || date?("20#{year}", month, day)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def check
|
43
|
+
# 1. Each of the first fifteen characters,depending on its relevant
|
44
|
+
# position (even or odd), is converted into a numeric value,according to
|
45
|
+
# correspondence shown in the tables below:
|
46
|
+
sum = tin[..14].chars.each.with_index(1).sum do |char, index|
|
47
|
+
index.even? ? even_number(char) : odd_number(char)
|
48
|
+
end
|
49
|
+
|
50
|
+
# 2. The numerical values thus determined are added together and their sum
|
51
|
+
# is divided by 26. The check character (C16) is obtained by converting
|
52
|
+
# the remainder of the division in the corresponding alphabetic character
|
53
|
+
# according to the table below:
|
54
|
+
((sum % 26) + 65).chr
|
55
|
+
end
|
56
|
+
|
57
|
+
def birth_century = birth_date.strftime("%Y")[..1]
|
58
|
+
|
59
|
+
def replacement_to_i(string)
|
60
|
+
string.chars.map { NUMERICAL_REPLACEMENTS.fetch(_1, _1) }.join.to_i
|
61
|
+
end
|
62
|
+
|
63
|
+
def date?(year, month, day)
|
64
|
+
found_date = date(year, month, day)
|
65
|
+
found_date && found_date < Date.today
|
66
|
+
end
|
67
|
+
|
68
|
+
def date(year, month, day)
|
69
|
+
Date.new(year.to_i, month.to_i, day.to_i)
|
70
|
+
rescue Date::Error
|
71
|
+
nil
|
72
|
+
end
|
73
|
+
|
74
|
+
MATCHER = %r{
|
75
|
+
\A
|
76
|
+
[A-Z]{6}
|
77
|
+
(?<year>[0-9LMNPQRSTUV]{2})
|
78
|
+
(?<month>[ABCDEHLMPRST])
|
79
|
+
(?<day>[0-7LMNPQRST][0-9LMNPQRSTUV])
|
80
|
+
[A-Z]
|
81
|
+
[0-9LMNPQRSTUV]{3}
|
82
|
+
[A-Z]
|
83
|
+
\z
|
84
|
+
}x
|
85
|
+
private_constant :MATCHER
|
86
|
+
|
87
|
+
# C9: A letter representing a month; the letter can only take the values:
|
88
|
+
MONTH_LETTERS = {
|
89
|
+
"A" => 1,
|
90
|
+
"B" => 2,
|
91
|
+
"C" => 3,
|
92
|
+
"D" => 4,
|
93
|
+
"E" => 5,
|
94
|
+
"H" => 6,
|
95
|
+
"L" => 7,
|
96
|
+
"M" => 8,
|
97
|
+
"P" => 9,
|
98
|
+
"R" => 10,
|
99
|
+
"S" => 11,
|
100
|
+
"T" => 12
|
101
|
+
}.freeze
|
102
|
+
private_constant :MONTH_LETTERS
|
103
|
+
|
104
|
+
NUMERICAL_REPLACEMENTS = {
|
105
|
+
"L" => 0,
|
106
|
+
"M" => 1,
|
107
|
+
"N" => 2,
|
108
|
+
"P" => 3,
|
109
|
+
"Q" => 4,
|
110
|
+
"R" => 5,
|
111
|
+
"S" => 6,
|
112
|
+
"T" => 7,
|
113
|
+
"U" => 8,
|
114
|
+
"V" => 9
|
115
|
+
}.freeze
|
116
|
+
private_constant :NUMERICAL_REPLACEMENTS
|
117
|
+
|
118
|
+
def even_number(character)
|
119
|
+
case character
|
120
|
+
in "0".."9" then character.to_i
|
121
|
+
in "A".."Z" then character.ord - 65
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def odd_number(character)
|
126
|
+
ODD_NUMBER_TABLE.fetch(character)
|
127
|
+
end
|
128
|
+
|
129
|
+
ODD_NUMBER_TABLE = {
|
130
|
+
"A" => 1,
|
131
|
+
"0" => 1,
|
132
|
+
"B" => 0,
|
133
|
+
"1" => 0,
|
134
|
+
"C" => 5,
|
135
|
+
"2" => 5,
|
136
|
+
"D" => 7,
|
137
|
+
"3" => 7,
|
138
|
+
"E" => 9,
|
139
|
+
"4" => 9,
|
140
|
+
"F" => 13,
|
141
|
+
"5" => 13,
|
142
|
+
"G" => 15,
|
143
|
+
"6" => 15,
|
144
|
+
"H" => 17,
|
145
|
+
"7" => 17,
|
146
|
+
"I" => 19,
|
147
|
+
"8" => 19,
|
148
|
+
"J" => 21,
|
149
|
+
"9" => 21,
|
150
|
+
"K" => 2,
|
151
|
+
"L" => 4,
|
152
|
+
"M" => 18,
|
153
|
+
"N" => 20,
|
154
|
+
"O" => 11,
|
155
|
+
"P" => 3,
|
156
|
+
"Q" => 6,
|
157
|
+
"R" => 8,
|
158
|
+
"S" => 12,
|
159
|
+
"T" => 14,
|
160
|
+
"U" => 16,
|
161
|
+
"V" => 10,
|
162
|
+
"W" => 22,
|
163
|
+
"X" => 25,
|
164
|
+
"Y" => 24,
|
165
|
+
"Z" => 23
|
166
|
+
}.freeze
|
167
|
+
private_constant :ODD_NUMBER_TABLE
|
168
|
+
end
|
169
|
+
# rubocop:enable Metrics/ClassLength
|
170
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TinValid
|
4
|
+
class LatviaTin < Data.define(:tin, :birth_date)
|
5
|
+
def initialize(tin:, birth_date: nil)
|
6
|
+
super
|
7
|
+
end
|
8
|
+
|
9
|
+
def valid? = valid_v1? || valid_v2?
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
MATCHER_V1 = %r{
|
14
|
+
\A
|
15
|
+
(?<day>[0-3][0-9])
|
16
|
+
(?<month>[0-1][0-9])
|
17
|
+
(?<year>[0-9]{2})
|
18
|
+
(?<century>[0-2])
|
19
|
+
-?
|
20
|
+
[0-9]{4}
|
21
|
+
\z
|
22
|
+
}x
|
23
|
+
private_constant :MATCHER_V1
|
24
|
+
|
25
|
+
MATCHER_V2 = %r{
|
26
|
+
\A
|
27
|
+
32
|
28
|
+
[0-9]{4}
|
29
|
+
-?
|
30
|
+
[0-9]{5}
|
31
|
+
\z
|
32
|
+
}x
|
33
|
+
private_constant :MATCHER_V2
|
34
|
+
|
35
|
+
def valid_v1?
|
36
|
+
match = MATCHER_V1.match(tin)
|
37
|
+
return false unless match
|
38
|
+
|
39
|
+
if birth_date
|
40
|
+
tin[..5] == birth_date.strftime("%d%m%y") &&
|
41
|
+
tin[6] == birth_century_digit
|
42
|
+
else
|
43
|
+
true
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def valid_v2?
|
48
|
+
MATCHER_V2.match?(tin)
|
49
|
+
end
|
50
|
+
|
51
|
+
def birth_century_digit
|
52
|
+
case birth_date.year
|
53
|
+
when 1800..1899 then "0"
|
54
|
+
when 1900..1999 then "1"
|
55
|
+
when 2000.. then "2"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TinValid
|
4
|
+
class LithuaniaTin < Data.define(:tin, :birth_date)
|
5
|
+
def initialize(tin:, birth_date: nil)
|
6
|
+
super
|
7
|
+
end
|
8
|
+
|
9
|
+
def valid?
|
10
|
+
matcher = MATCHER.match(tin)
|
11
|
+
return false unless matcher
|
12
|
+
|
13
|
+
if birth_date && (matcher[:birth_date] != birth_date.strftime("%y%m%d"))
|
14
|
+
return false
|
15
|
+
end
|
16
|
+
|
17
|
+
tin[-1].to_i == check
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
MATCHER = %r{
|
23
|
+
[1-6]
|
24
|
+
(?<birth_date>
|
25
|
+
[0-9]{2}
|
26
|
+
[0-1][0-9]
|
27
|
+
[0-3][0-9]
|
28
|
+
)
|
29
|
+
[0-9]{4}
|
30
|
+
}x
|
31
|
+
private_constant :MATCHER
|
32
|
+
|
33
|
+
# rubocop:disable Metrics/AbcSize
|
34
|
+
# rubocop:disable Metrics/MethodLength
|
35
|
+
def check
|
36
|
+
# 1. Multiply the values of each position by the corresponding weight:
|
37
|
+
weights =
|
38
|
+
[1, 2, 3, 4, 5, 6, 7, 8, 9, 1]
|
39
|
+
.each_with_index
|
40
|
+
.map { |weight, index| weight * tin[index].to_i }
|
41
|
+
|
42
|
+
# 2. Add up the results of the above multiplications;
|
43
|
+
sum = weights.sum
|
44
|
+
|
45
|
+
# 3. Get modulo 11 of the result of the previous addition;
|
46
|
+
remainder = sum % 11
|
47
|
+
|
48
|
+
# 4. C11 = remainder if remainder is not 10;
|
49
|
+
return remainder if remainder != 10
|
50
|
+
|
51
|
+
# 5. If remainder is 10, calculate a new check digit with over
|
52
|
+
# corresponding weight:
|
53
|
+
weights =
|
54
|
+
[3, 4, 5, 6, 7, 8, 9, 1, 2, 3]
|
55
|
+
.each_with_index
|
56
|
+
.map { |weight, index| weight * tin[index].to_i }
|
57
|
+
|
58
|
+
# 6. Add up the results of the above multiplications;
|
59
|
+
sum = weights.sum
|
60
|
+
|
61
|
+
# 7. Get modulo 11 of the result of the previous addition;
|
62
|
+
remainder = sum % 11
|
63
|
+
|
64
|
+
# 8. C11 = remainder if remainder is not 10; if remainder is 10, C11 = 0.
|
65
|
+
return remainder if remainder != 10
|
66
|
+
|
67
|
+
0
|
68
|
+
end
|
69
|
+
# rubocop:enable Metrics/AbcSize
|
70
|
+
# rubocop:enable Metrics/MethodLength
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TinValid
|
4
|
+
class LuxembourgTin < Data.define(:tin, :birth_date)
|
5
|
+
def initialize(tin:, birth_date: nil)
|
6
|
+
super
|
7
|
+
end
|
8
|
+
|
9
|
+
def valid?
|
10
|
+
match = MATCHER.match(tin)
|
11
|
+
return false unless match
|
12
|
+
|
13
|
+
tin_date = date(match[:year], match[:month], match[:day])
|
14
|
+
return false unless tin_date
|
15
|
+
|
16
|
+
return false if birth_date && birth_date != tin_date
|
17
|
+
|
18
|
+
check1? && check2?
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
MATCHER = %r{
|
24
|
+
\A
|
25
|
+
(?<year>[0-9]{4})
|
26
|
+
(?<month>[0-1][0-9])
|
27
|
+
(?<day>[0-3][0-9])
|
28
|
+
[0-9]{5}
|
29
|
+
\z
|
30
|
+
}x
|
31
|
+
private_constant :MATCHER
|
32
|
+
|
33
|
+
def check1?
|
34
|
+
# 1. Multiply the values of each position by the corresponding weight:
|
35
|
+
weights =
|
36
|
+
[2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1]
|
37
|
+
.each_with_index
|
38
|
+
.map { |weight, index| weight * tin[index].to_i }
|
39
|
+
|
40
|
+
# 2. If the product of a doubling operation is > 9, sum the digits of the
|
41
|
+
# product;
|
42
|
+
weights = weights.map { _1 > 9 ? _1.to_s.chars.sum(&:to_i) : _1 }
|
43
|
+
|
44
|
+
# 3. Add up the results of the above multiplications;
|
45
|
+
sum = weights.sum
|
46
|
+
|
47
|
+
# 4. Get modulo 10 of the result of the previous addition;
|
48
|
+
remainder = sum % 10
|
49
|
+
|
50
|
+
# 5. If remainder = 0, C12 is valid. Otherwise the TIN is not valid.
|
51
|
+
remainder == 0
|
52
|
+
end
|
53
|
+
|
54
|
+
def check2?
|
55
|
+
# 1. Create an array n containing the individual C1 to C11 and C13 of the
|
56
|
+
# TIN (where ni = the value of the corresponding C), taken from right to
|
57
|
+
# left:
|
58
|
+
array = [*tin[..10].chars, tin[12]].map(&:to_i).reverse
|
59
|
+
|
60
|
+
# 2. Initialize the checksum c to 0;
|
61
|
+
# 3. For each index i of the array n, starting at 0, replace c by
|
62
|
+
# d(c,p(i mod 8, ni)), according to the following tables:
|
63
|
+
checksum = array.each_with_index.inject(0) do |c, (ni, index)|
|
64
|
+
table_d(c, table_p(index % 8, ni))
|
65
|
+
end
|
66
|
+
|
67
|
+
# 4. Check digit c if c = 0, C13 is valid. Otherwise, the TIN is not
|
68
|
+
# valid.
|
69
|
+
checksum == 0
|
70
|
+
end
|
71
|
+
|
72
|
+
def date(year, month, day)
|
73
|
+
Date.new(year.to_i, month.to_i, day.to_i)
|
74
|
+
rescue Date::Error
|
75
|
+
nil
|
76
|
+
end
|
77
|
+
|
78
|
+
def table_d(number_i, number_j) = TABLE_D.dig(number_i, number_j)
|
79
|
+
def table_p(number_m, number_n) = TABLE_P.dig(number_m, number_n)
|
80
|
+
|
81
|
+
TABLE_D = [
|
82
|
+
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
|
83
|
+
[1, 2, 3, 4, 0, 6, 7, 8, 9, 5],
|
84
|
+
[2, 3, 4, 0, 1, 7, 8, 9, 5, 6],
|
85
|
+
[3, 4, 0, 1, 2, 8, 9, 5, 6, 7],
|
86
|
+
[4, 0, 1, 2, 3, 9, 5, 6, 7, 8],
|
87
|
+
[5, 9, 8, 7, 6, 0, 4, 3, 2, 1],
|
88
|
+
[6, 5, 9, 8, 7, 1, 0, 4, 3, 2],
|
89
|
+
[7, 6, 5, 9, 8, 2, 1, 0, 4, 3],
|
90
|
+
[8, 7, 6, 5, 9, 3, 2, 1, 0, 4],
|
91
|
+
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0],
|
92
|
+
].freeze
|
93
|
+
private_constant :TABLE_D
|
94
|
+
|
95
|
+
TABLE_P = [
|
96
|
+
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
|
97
|
+
[1, 5, 7, 6, 2, 8, 3, 0, 9, 4],
|
98
|
+
[5, 8, 0, 3, 7, 9, 6, 1, 4, 2],
|
99
|
+
[8, 9, 1, 6, 0, 5, 3, 5, 2, 7],
|
100
|
+
[9, 4, 5, 3, 1, 2, 6, 8, 7, 0],
|
101
|
+
[4, 2, 8, 6, 5, 7, 3, 9, 0, 1],
|
102
|
+
[2, 7, 9, 3, 8, 0, 6, 4, 1, 5],
|
103
|
+
[7, 0, 4, 6, 9, 1, 3, 2, 5, 8],
|
104
|
+
].freeze
|
105
|
+
private_constant :TABLE_P
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TinValid
|
4
|
+
class MaltaTin < Data.define(:tin)
|
5
|
+
def valid? = valid_format_1? || valid_format_2?
|
6
|
+
|
7
|
+
private
|
8
|
+
|
9
|
+
MATCHER_V1 = %r{
|
10
|
+
\A
|
11
|
+
(?<number>
|
12
|
+
(?<part1>[0-9]{0,5})
|
13
|
+
[0-9]{1,2}
|
14
|
+
)
|
15
|
+
(?<letter>[MGAPLHBZ])
|
16
|
+
\z
|
17
|
+
}x
|
18
|
+
private_constant :MATCHER_V1
|
19
|
+
|
20
|
+
MATCHER_V2 = %r{
|
21
|
+
\A
|
22
|
+
(11|22|33|44|55|66|77|88)
|
23
|
+
[0-9]{7}
|
24
|
+
\z
|
25
|
+
}x
|
26
|
+
private_constant :MATCHER_V2
|
27
|
+
|
28
|
+
def valid_format_1?
|
29
|
+
match = MATCHER_V1.match(tin)
|
30
|
+
return false unless match
|
31
|
+
|
32
|
+
case match[:letter]
|
33
|
+
when "A", "P"
|
34
|
+
true
|
35
|
+
when "M", "G", "L", "H", "B", "Z"
|
36
|
+
(0..32_000).cover?(match[:part1].to_i) &&
|
37
|
+
match[:number].to_i != 0
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def valid_format_2?
|
42
|
+
MATCHER_V2.match?(tin)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TinValid
|
4
|
+
class NetherlandsTin < Data.define(:tin)
|
5
|
+
def valid?
|
6
|
+
return false unless /\A[0-9]{9}\z/.match?(tin)
|
7
|
+
|
8
|
+
tin[-1].to_i == check
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def check
|
14
|
+
# 1. Multiply the values of each position by the corresponding weight:
|
15
|
+
multipliers =
|
16
|
+
9
|
17
|
+
.downto(2)
|
18
|
+
.each_with_index
|
19
|
+
.map { |multiplier, position| multiplier * tin[position].to_i }
|
20
|
+
|
21
|
+
# 2. Add up the results of the above multiplications;
|
22
|
+
sum = multipliers.sum
|
23
|
+
|
24
|
+
# 3. Get modulo 11 of the result of the previous addition;
|
25
|
+
# 4. Check digit = remainder (if remainder = 10, the TIN is not valid).
|
26
|
+
sum % 11
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TinValid
|
4
|
+
class PolandTin < Data.define(:tin, :birth_date)
|
5
|
+
def initialize(tin:, birth_date: nil)
|
6
|
+
super
|
7
|
+
end
|
8
|
+
|
9
|
+
def valid? = valid_v1? || valid_v2?
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
MATCHER_V1 = /\A[0-9]{10}\z/
|
14
|
+
private_constant :MATCHER_V1
|
15
|
+
|
16
|
+
MATCHER_V2 = %r{
|
17
|
+
\A
|
18
|
+
(?<year>[0-9]{2})
|
19
|
+
(?<month>[0-9]{2})
|
20
|
+
(?<day>[0-3][0-9])
|
21
|
+
[0-9]{5}
|
22
|
+
\z
|
23
|
+
}x
|
24
|
+
private_constant :MATCHER_V2
|
25
|
+
|
26
|
+
def valid_v1?
|
27
|
+
return false unless MATCHER_V1.match?(tin)
|
28
|
+
|
29
|
+
tin[-1].to_i == check
|
30
|
+
end
|
31
|
+
|
32
|
+
def valid_v2?
|
33
|
+
match = MATCHER_V2.match(tin)
|
34
|
+
return false unless match
|
35
|
+
return true unless birth_date
|
36
|
+
|
37
|
+
tin_date = date(
|
38
|
+
"#{birth_century}#{match[:year]}",
|
39
|
+
match[:month].to_i - month_increase,
|
40
|
+
match[:day],
|
41
|
+
)
|
42
|
+
tin_date == birth_date
|
43
|
+
end
|
44
|
+
|
45
|
+
def check
|
46
|
+
# 1. Multiply the values of each position by the corresponding weight:
|
47
|
+
weights =
|
48
|
+
[6, 5, 7, 2, 3, 4, 5, 6, 7]
|
49
|
+
.each_with_index
|
50
|
+
.map { |weight, index| weight * tin[index].to_i }
|
51
|
+
|
52
|
+
# 2. Add up the results of the above multiplications;
|
53
|
+
sum = weights.sum
|
54
|
+
|
55
|
+
# 3. Get modulo 11 of the result of the previous addition;
|
56
|
+
# 4. Check digit = remainder (if remainder = 10, the TIN is not valid).
|
57
|
+
sum % 11
|
58
|
+
end
|
59
|
+
|
60
|
+
def birth_century = birth_date.year.to_s[..1].to_i
|
61
|
+
|
62
|
+
def month_increase
|
63
|
+
case birth_century
|
64
|
+
when 18 then 80
|
65
|
+
when 20 then 20
|
66
|
+
when 21 then 40
|
67
|
+
when 22 then 60
|
68
|
+
else 0
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def date(year, month, day)
|
73
|
+
Date.new(year.to_i, month.to_i, day.to_i)
|
74
|
+
rescue Date::Error
|
75
|
+
nil
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TinValid
|
4
|
+
class PortugalTin < Data.define(:tin)
|
5
|
+
def valid?
|
6
|
+
return false unless /\A[0-9]{9}\z/.match?(tin)
|
7
|
+
|
8
|
+
tin[-1].to_i == check
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def check
|
14
|
+
# 1. Multiply the values of each position by the corresponding weight:
|
15
|
+
multipliers =
|
16
|
+
9
|
17
|
+
.downto(2)
|
18
|
+
.with_index
|
19
|
+
.map { |multiplier, index| multiplier * tin[index].to_i }
|
20
|
+
|
21
|
+
# 2. Add up the results of the above multiplications;
|
22
|
+
sum = multipliers.sum
|
23
|
+
|
24
|
+
# 3. Get modulo 11 of the result of the previous addition;
|
25
|
+
remainder = sum % 11
|
26
|
+
|
27
|
+
# 4. Check digit = 11 - remainder:
|
28
|
+
digit = 11 - remainder
|
29
|
+
|
30
|
+
# If check digit < = 9 then check digit is OK (11 – remainder);
|
31
|
+
return digit if digit <= 9
|
32
|
+
|
33
|
+
# If check digit = 10 then check digit is 0 (zero);
|
34
|
+
# If check digit = 11 then check digit is 0 (zero).
|
35
|
+
0
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TinValid
|
4
|
+
class RomaniaTin < Data.define(:tin, :birth_date)
|
5
|
+
def initialize(tin:, birth_date: nil)
|
6
|
+
super
|
7
|
+
end
|
8
|
+
|
9
|
+
def valid? = valid_v1? || valid_v2?
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
MATCHER_V1 = %r{
|
14
|
+
\A
|
15
|
+
(?<century_code>[1-9])
|
16
|
+
(?<year>[0-9]{2})
|
17
|
+
(?<month>[0-1][0-9])
|
18
|
+
(?<day>[0-3][0-9])
|
19
|
+
(?<district>[0-5][0-9])
|
20
|
+
[0-9]{4}
|
21
|
+
\z
|
22
|
+
}x
|
23
|
+
private_constant :MATCHER_V1
|
24
|
+
|
25
|
+
MATCHER_V2 = %r{
|
26
|
+
\A
|
27
|
+
9
|
28
|
+
000
|
29
|
+
[0-9]{8,9}
|
30
|
+
\z
|
31
|
+
}x
|
32
|
+
private_constant :MATCHER_V2
|
33
|
+
|
34
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
35
|
+
# rubocop:disable Metrics/MethodLength
|
36
|
+
def valid_v1?
|
37
|
+
match = MATCHER_V1.match(tin)
|
38
|
+
return false unless match
|
39
|
+
return false unless valid_district?(match[:district])
|
40
|
+
|
41
|
+
century = tin_century_from_code(match[:century_code]) || birth_century
|
42
|
+
if century
|
43
|
+
tin_date = date(
|
44
|
+
"#{century}#{match[:year]}",
|
45
|
+
match[:month],
|
46
|
+
match[:day],
|
47
|
+
)
|
48
|
+
return false if tin_date.nil?
|
49
|
+
return false if birth_date && tin_date != birth_date
|
50
|
+
end
|
51
|
+
|
52
|
+
true
|
53
|
+
end
|
54
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
55
|
+
# rubocop:enable Metrics/MethodLength
|
56
|
+
|
57
|
+
def valid_v2? = MATCHER_V2.match?(tin)
|
58
|
+
|
59
|
+
def valid_district?(district)
|
60
|
+
case district.to_i
|
61
|
+
when 1..47, 51..52 then true
|
62
|
+
else false
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def birth_century
|
67
|
+
birth_date.year.to_s[..1] if birth_date
|
68
|
+
end
|
69
|
+
|
70
|
+
def tin_century_from_code(code)
|
71
|
+
case code.to_i
|
72
|
+
when 1..2 then 19
|
73
|
+
when 3..4 then 18
|
74
|
+
when 5..6 then 20
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def date(year, month, day)
|
79
|
+
Date.new(year.to_i, month.to_i, day.to_i)
|
80
|
+
rescue Date::Error
|
81
|
+
nil
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TinValid
|
4
|
+
class SlovakiaTin < Data.define(:tin, :birth_date)
|
5
|
+
def initialize(tin:, birth_date: nil)
|
6
|
+
super
|
7
|
+
end
|
8
|
+
|
9
|
+
def valid? = valid_v2? || valid_v1?
|
10
|
+
|
11
|
+
def normalized = tin.tr("/", "")
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
MATCHER_V1 = %r{
|
16
|
+
\A
|
17
|
+
(?<year>[0-9]{2})
|
18
|
+
(?<month>[0-6][0-9])
|
19
|
+
(?<day>[0-3][0-9])
|
20
|
+
/?
|
21
|
+
[0-9]{3}
|
22
|
+
(?<check>[0-9])?
|
23
|
+
\z
|
24
|
+
}x
|
25
|
+
private_constant :MATCHER_V1
|
26
|
+
|
27
|
+
MATCHER_V2 = %r{
|
28
|
+
\A
|
29
|
+
[0-9]{10}
|
30
|
+
\z
|
31
|
+
}x
|
32
|
+
private_constant :MATCHER_V2
|
33
|
+
|
34
|
+
# rubocop:disable Metrics/MethodLength
|
35
|
+
def valid_v1?
|
36
|
+
match = MATCHER_V1.match(tin)
|
37
|
+
return false unless match
|
38
|
+
|
39
|
+
year = match[:year].to_i
|
40
|
+
return false if year >= 54 && match[:check].nil?
|
41
|
+
|
42
|
+
if birth_date
|
43
|
+
month = match[:month].to_i
|
44
|
+
month -= 50 if month > 50
|
45
|
+
|
46
|
+
tin_date = date("#{birth_century}#{year}", month, match[:day])
|
47
|
+
return false if tin_date != birth_date
|
48
|
+
end
|
49
|
+
|
50
|
+
true
|
51
|
+
end
|
52
|
+
# rubocop:enable Metrics/MethodLength
|
53
|
+
|
54
|
+
def valid_v2? = MATCHER_V2.match?(tin)
|
55
|
+
|
56
|
+
def birth_century = birth_date.year.to_s[..1]
|
57
|
+
|
58
|
+
def date(year, month, day)
|
59
|
+
Date.new(year.to_i, month.to_i, day.to_i)
|
60
|
+
rescue Date::Error
|
61
|
+
nil
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TinValid
|
4
|
+
class SloveniaTin < Data.define(:tin)
|
5
|
+
def valid?
|
6
|
+
return false unless /\A[1-9][0-9]{7}\z/.match?(tin)
|
7
|
+
|
8
|
+
tin[-1].to_i == check
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def check
|
14
|
+
# Multiply the values of each position by the corresponding weight:
|
15
|
+
weights =
|
16
|
+
8
|
17
|
+
.downto(2)
|
18
|
+
.each_with_index
|
19
|
+
.map { |weight, index| weight * tin[index].to_i }
|
20
|
+
|
21
|
+
# 2. Add up the results of the above multiplications;
|
22
|
+
sum = weights.sum
|
23
|
+
|
24
|
+
# 3. Get modulo 11 of the result of the previous addition;
|
25
|
+
remainder = sum % 11
|
26
|
+
|
27
|
+
# 4. Check digit = 11 - remainder. If result = 10, Check digit = 0.
|
28
|
+
digit = 11 - remainder
|
29
|
+
digit == 10 ? 0 : digit
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TinValid
|
4
|
+
class SpainTin < Data.define(:tin)
|
5
|
+
def valid?
|
6
|
+
valid_v1? || valid_v2?
|
7
|
+
end
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def valid_v1?
|
12
|
+
return false unless /\A[0-9]{1,8}[A-Z]\z/.match?(tin)
|
13
|
+
|
14
|
+
tin[-1] == check_letter(tin)
|
15
|
+
end
|
16
|
+
|
17
|
+
def valid_v2?
|
18
|
+
return false unless /\A[XYZKLM][0-9]{7}[A-Z]\z/.match?(tin)
|
19
|
+
|
20
|
+
tin[-1] == check_v2
|
21
|
+
end
|
22
|
+
|
23
|
+
def check_v2
|
24
|
+
# 0. Replace the leading letter by the corresponding digit and concatenate
|
25
|
+
# the result with the other characters:
|
26
|
+
digit =
|
27
|
+
case tin[0]
|
28
|
+
when "X", "K", "L", "M" then 0
|
29
|
+
when "Y" then 1
|
30
|
+
when "Z" then 2
|
31
|
+
end
|
32
|
+
|
33
|
+
check_letter("#{digit}#{tin[1..]}")
|
34
|
+
end
|
35
|
+
|
36
|
+
def check_letter(code)
|
37
|
+
# 1. Take the remainder of modulo 23 of the 8 first characters;
|
38
|
+
remainder = code[..-2].to_i % 23
|
39
|
+
|
40
|
+
# 2. Add 1 to the remainder of operation 1;
|
41
|
+
# 3. The check letter corresponds to this figure in the table below:
|
42
|
+
"TRWAGMYFPDXBNJZSQVHLCKE"[remainder]
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TinValid
|
4
|
+
class UnitedKingdomTin < Data.define(:tin)
|
5
|
+
def valid?
|
6
|
+
valid_v1? || valid_v2?
|
7
|
+
end
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def valid_v1? = /\A[0-9]{10}\z/.match?(tin)
|
12
|
+
|
13
|
+
def valid_v2?
|
14
|
+
return false unless /\A[A-Z]{2}[0-9]{6}[A-D]?\z/.match?(tin)
|
15
|
+
return false if %w[D F I Q U V].include?(tin[0])
|
16
|
+
return false if %w[D F I Q O U V].include?(tin[1])
|
17
|
+
return false if %w[GB NK TN ZZ].include?(tin[0..1])
|
18
|
+
|
19
|
+
true
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/lib/tin_valid/version.rb
CHANGED
data/lib/tin_valid.rb
CHANGED
@@ -14,6 +14,19 @@ require_relative "tin_valid/germany_tin"
|
|
14
14
|
require_relative "tin_valid/greece_tin"
|
15
15
|
require_relative "tin_valid/hungary_tin"
|
16
16
|
require_relative "tin_valid/ireland_tin"
|
17
|
+
require_relative "tin_valid/italy_tin"
|
18
|
+
require_relative "tin_valid/latvia_tin"
|
19
|
+
require_relative "tin_valid/lithuania_tin"
|
20
|
+
require_relative "tin_valid/luxembourg_tin"
|
21
|
+
require_relative "tin_valid/malta_tin"
|
22
|
+
require_relative "tin_valid/netherlands_tin"
|
23
|
+
require_relative "tin_valid/poland_tin"
|
24
|
+
require_relative "tin_valid/portugal_tin"
|
25
|
+
require_relative "tin_valid/romania_tin"
|
26
|
+
require_relative "tin_valid/slovakia_tin"
|
27
|
+
require_relative "tin_valid/slovenia_tin"
|
28
|
+
require_relative "tin_valid/spain_tin"
|
29
|
+
require_relative "tin_valid/united_kingdom_tin"
|
17
30
|
require_relative "tin_valid/sweden_tin"
|
18
31
|
|
19
32
|
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.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sunny Ripert
|
8
8
|
bindir: exe
|
9
9
|
cert_chain: []
|
10
|
-
date: 2025-04-
|
10
|
+
date: 2025-04-17 00:00:00.000000000 Z
|
11
11
|
dependencies: []
|
12
12
|
email:
|
13
13
|
- sunny@sunfox.org
|
@@ -35,7 +35,20 @@ files:
|
|
35
35
|
- lib/tin_valid/greece_tin.rb
|
36
36
|
- lib/tin_valid/hungary_tin.rb
|
37
37
|
- lib/tin_valid/ireland_tin.rb
|
38
|
+
- lib/tin_valid/italy_tin.rb
|
39
|
+
- lib/tin_valid/latvia_tin.rb
|
40
|
+
- lib/tin_valid/lithuania_tin.rb
|
41
|
+
- lib/tin_valid/luxembourg_tin.rb
|
42
|
+
- lib/tin_valid/malta_tin.rb
|
43
|
+
- lib/tin_valid/netherlands_tin.rb
|
44
|
+
- lib/tin_valid/poland_tin.rb
|
45
|
+
- lib/tin_valid/portugal_tin.rb
|
46
|
+
- lib/tin_valid/romania_tin.rb
|
47
|
+
- lib/tin_valid/slovakia_tin.rb
|
48
|
+
- lib/tin_valid/slovenia_tin.rb
|
49
|
+
- lib/tin_valid/spain_tin.rb
|
38
50
|
- lib/tin_valid/sweden_tin.rb
|
51
|
+
- lib/tin_valid/united_kingdom_tin.rb
|
39
52
|
- lib/tin_valid/version.rb
|
40
53
|
- sig/tin_valid.rbs
|
41
54
|
homepage: https://github.com/cults/tin_valid
|