tin_valid 1.1.1 → 1.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0dc6696e709037b0b4366b05c398e951cfb291691eb732412dd2091e7f17f025
4
- data.tar.gz: 0cbf922fa63b254934f39fd978d0037824acc54363ce0fa88fc98d3e6c87525d
3
+ metadata.gz: 3fb98f7f433b3c6a6755c0caa0835c75ed58408fee17ee0e96a083603fd965d5
4
+ data.tar.gz: 2eb697ac67268456c7cd8ff346144b3cbbfcb2b4bbff8052065ad6b7789b0d15
5
5
  SHA512:
6
- metadata.gz: e48fccc3f431949c165d4aaab617bf6ef6a13e58e5e41484ef14a05766e07f424a4b1da5dc9b229839685baf52219d160012541ac46a7517d870cd4b1e95c88a
7
- data.tar.gz: 00b6226d2e85cb1371fc67792bcdc51d491ee7174faf76837b9a931a98af3754a4af079732dd259c6f9321f604d5148e655955496507556a96994a94462df590
6
+ metadata.gz: ea7cd37992f808250a153fa5be6728489438136701318e8397d1822512efaddfd8ed395534cdc0e62f8523f9ec225cb151f37409ea741accdcd8c233e7858e5f
7
+ data.tar.gz: 91eefad78dd3d3219cb7e4add5b5022cd72f62e09a7d6fa5b8b9279e60e165a7620ca2ab25fe4ccb94da5b629f6251e814aeeff0007399830b275be3b99e5ce7
data/.rubocop.yml CHANGED
@@ -12,6 +12,7 @@ Layout/LineLength:
12
12
  Max: 80
13
13
  Exclude:
14
14
  - bin/rspec
15
+ - bin/rake
15
16
 
16
17
  Layout/MultilineMethodCallIndentation:
17
18
  # On multiline calls, indent starting from the receiver.
@@ -33,6 +34,13 @@ Metrics/BlockLength:
33
34
  Exclude:
34
35
  - spec/**/*
35
36
 
37
+ # Security
38
+
39
+ Security/Eval:
40
+ # Allow the use of `eval` in specs.
41
+ Exclude:
42
+ - spec/**/*
43
+
36
44
  # Style
37
45
 
38
46
  Style/Documentation:
data/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [1.2.0] - 2025-05-14
4
+
5
+ Features:
6
+ - Add generic `TinValid::Tin.new(country_code: "…", tin: "…").valid?`
7
+ - Add `#siren?` and `#siret?` on `TinValid::FranceTin`
8
+
9
+ Fixes:
10
+ - Accept SIRET for France
11
+
12
+ ## [1.1.2] - 2025-05-06
13
+
14
+ Fixes:
15
+ - Fix Luxembourg check
16
+ - Accept SIREN for France
17
+
3
18
  ## [1.1.1] - 2025-04-28
4
19
 
5
20
  Fixes:
data/README.md CHANGED
@@ -31,8 +31,11 @@ Validate Tax Identification Numbers (TINs) for the following European countries:
31
31
  - Sweden 🇸🇪
32
32
  - United Kingdom 🇬🇧
33
33
 
34
- See also the [descriptions of the structure provided by the European
35
- Union](https://taxation-customs.ec.europa.eu/online-services/online-services-and-databases-taxation/taxpayer-identification-number-tin_en).
34
+ See also the
35
+ [TIN specifications](https://ec.europa.eu/taxation_customs/tin/#/check-tin)
36
+ and the
37
+ [descriptions of the structure](https://taxation-customs.ec.europa.eu/online-services/online-services-and-databases-taxation/taxpayer-identification-number-tin_en)
38
+ provided by the European Union.
36
39
 
37
40
  ## Installation
38
41
 
@@ -51,117 +54,287 @@ gem install tin_valid
51
54
 
52
55
  ## Usage
53
56
 
57
+ Check a TIN by providing the country code as a lowercase string.
58
+
59
+ Also accepts the following optional arguments:
60
+
61
+ - `birth_date`: Date
62
+ - `kind`: `"individual"` or `"company"`
63
+
54
64
  ```rb
55
- # Austria
56
- TinValid::AustriaTin.new(tin: "…").valid? # => true
65
+ TinValid::Tin.new(country_code: "fr", tin: "3023217600053").valid?
66
+ ```
57
67
 
58
- # Belgium
59
- # Optional birth_date
60
- TinValid::BelgiumTin.new(tin: "…", birth_date: Date.new(…)).valid?
68
+ You can also call countries individually:
61
69
 
62
- # Bulgaria
63
- # Optional birth_date
64
- TinValid::BulgariaTin.new(tin: "…", birth_date: Date.new(…)).valid?
70
+ ### Austria (at)
65
71
 
66
- # Croatia
67
- TinValid::CroatiaTin.new(tin: "…").valid?
72
+ ```rb
73
+ TinValid::AustriaTin.new(
74
+ tin: "931736581",
75
+ ).valid?
76
+ ```
68
77
 
69
- # Cyprus
70
- # Optional kind ("individual" or "company")
71
- TinValid::CyprusTin.new(tin: "…", kind: "individual").valid?
78
+ ### Belgium (be)
72
79
 
73
- # Czechia
74
- # Optional birth_date
75
- TinValid::CzechiaTin.new(tin: "…", birth_date: Date.new(…)).valid?
80
+ `birth_date` is optional
76
81
 
77
- # Denmark
78
- # Optional birth_date
79
- TinValid::DenmarkTin.new(tin: "", birth_date: Date.new(…)).valid?
82
+ ```rb
83
+ TinValid::BelgiumTin.new(
84
+ tin: "00012511119",
85
+ birth_date: Date.new(1900, 1, 25),
86
+ ).valid?
87
+ ```
80
88
 
81
- # Estonia
82
- # Optional birth_date
83
- TinValid::EstoniaTin.new(tin: "…", birth_date: Date.new(…)).valid?
89
+ ### Bulgaria (bg)
84
90
 
85
- # Finland
86
- # Optional birth_date
87
- TinValid::FinlandTin.new(tin: "…", birth_date: Date.new(…)).valid?
91
+ `birth_date` is optional
88
92
 
89
- # France
90
- TinValid::FranceTin.new(tin: "…").valid?
93
+ ```rb
94
+ TinValid::BulgariaTin.new(
95
+ tin: "7501010010",
96
+ birth_date: Date.new(1975, 1, 1),
97
+ ).valid?
98
+ ```
91
99
 
92
- # Germany
93
- TinValid::GermanyTin.new(tin: "…").valid?
100
+ ### Croatia (hr)
94
101
 
95
- # Greece
96
- TinValid::GreeceTin.new(tin: "…").valid?
102
+ ```rb
103
+ TinValid::CroatiaTin.new(
104
+ tin: "94577403194",
105
+ ).valid?
106
+ ```
97
107
 
98
- # Hungary
99
- TinValid::HungaryTin.new(tin: "…").valid?
108
+ ### Cyprus (cy)
100
109
 
101
- # Ireland
102
- TinValid::IrelandTin.new(tin: "…").valid?
110
+ `kind` is optional and can be `"individual"` or `"company"`
103
111
 
104
- # Italy
105
- # Optional birth_date
106
- TinValid::ItalyTin.new(tin: "…", birth_date: Date.new(…)).valid?
112
+ ```rb
113
+ TinValid::CyprusTin.new(tin: "00123123T", kind: "individual").valid?
114
+ ```
107
115
 
108
- # Latvia
109
- # Optional birth_date
110
- TinValid::LatviaTin.new(tin: "…", birth_date: Date.new(…)).valid?
116
+ ### Czechia (cz)
111
117
 
112
- # Lithuania
113
- # Optional birth_date
114
- TinValid::LithuaniaTin.new(tin: "…", birth_date: Date.new(…)).valid?
118
+ `birth_date` is optional
115
119
 
116
- # Luxembourg
117
- # Optional birth_date
118
- TinValid::LuxembourgTin.new(tin: "", birth_date: Date.new(…)).valid?
120
+ ```rb
121
+ TinValid::CzechiaTin.new(
122
+ tin: "420901999",
123
+ birth_date: Date.new(1942, 9, 1),
124
+ ).valid?
125
+ ```
126
+
127
+ ### Denmark (dk)
119
128
 
120
- # Malta
121
- TinValid::MaltaTin.new(tin: "…").valid?
129
+ `birth_date` is optional
122
130
 
123
- # Netherlands
124
- TinValid::NetherlandsTin.new(tin: "…").valid?
131
+ ```rb
132
+ TinValid::DenmarkTin.new(
133
+ tin: "0101111113",
134
+ birth_date: Date.new(1911, 1, 1),
135
+ ).valid?
136
+ ```
125
137
 
126
- # Poland
127
- # Optional birth_date
128
- TinValid::PolandTin.new(tin: "…", birth_date: Date.new(…)).valid?
138
+ ### Estonia (ee)
129
139
 
130
- # Portugal
131
- TinValid::PortugalTin.new(tin: "…").valid?
140
+ `birth_date` is optional
141
+
142
+ ```rb
143
+ TinValid::EstoniaTin.new(
144
+ tin: "37102250382",
145
+ birth_date: Date.new(1971, 2, 25),
146
+ ).valid?
147
+ ```
132
148
 
133
- # Romania
134
- # Optional birth_date
135
- TinValid::RomaniaTin.new(tin: "…", birth_date: Date.new(…)).valid?
149
+ ### Finland (fi)
136
150
 
137
- # Slovakia
138
- # Optional birth_date
139
- TinValid::SlovakiaTin.new(tin: "…", birth_date: Date.new(…)).valid?
151
+ `birth_date` is optional
140
152
 
141
- # Slovenia
142
- TinValid::SloveniaTin.new(tin: "…").valid?
153
+ ```rb
154
+ TinValid::FinlandTin.new(
155
+ tin: "131052-308T",
156
+ birth_date: Date.new(1952, 10, 13),
157
+ ).valid?
158
+ ```
143
159
 
144
- # Spain
145
- TinValid::SpainTin.new(tin: "…").valid?
160
+ ### France (fr)
146
161
 
147
- # Sweden
148
- # Optional birth_date
149
- TinValid::SwedenTin.new(tin: "…", birth_date: Date.new(…)).valid?
162
+ ```rb
163
+ TinValid::FranceTin.new(tin: "3023217600053").valid?
164
+ ```
150
165
 
151
- # United Kingdom
152
- TinValid::UnitedKingdomTin.new(tin: "").valid?
166
+ ```rb
167
+ TinValid::FranceTin.new(tin: "732829320").siren?
168
+ ```
169
+
170
+ ```rb
171
+ TinValid::FranceTin.new(tin: "73282932000074").siret?
172
+ ```
173
+
174
+ ### Germany (de)
175
+
176
+ ```rb
177
+ TinValid::GermanyTin.new(tin: "5133081508159").valid?
178
+ ```
179
+
180
+ ### Greece (gr)
181
+
182
+ ```rb
183
+ TinValid::GreeceTin.new(tin: "999999999").valid?
184
+ ```
185
+
186
+ ### Hungary (hu)
187
+
188
+ ```rb
189
+ TinValid::HungaryTin.new(tin: "8071592153").valid?
190
+ ```
191
+
192
+ ### Ireland (ie)
193
+
194
+ ```rb
195
+ TinValid::IrelandTin.new(tin: "1234567T").valid?
196
+ ```
197
+
198
+ ### Italy (it)
199
+
200
+ `birth_date` is optional
201
+
202
+ ```rb
203
+ TinValid::ItalyTin.new(
204
+ tin: "DMLPRY77D15H501F",
205
+ birth_date: Date.new(1977, 4, 15),
206
+ ).valid?
207
+ ```
208
+
209
+ ### Latvia (lv)
210
+
211
+ `birth_date` is optional
212
+
213
+ ```rb
214
+ TinValid::LatviaTin.new(
215
+ tin: "01011012345",
216
+ birth_date: Date.new(1910, 1, 1),
217
+ ).valid?
218
+ ```
219
+
220
+ ### Lithuania (lt)
221
+
222
+ `birth_date` is optional
223
+
224
+ ```rb
225
+ TinValid::LithuaniaTin.new(
226
+ tin: "10101010005",
227
+ birth_date: Date.new(2001, 1, 1),
228
+ ).valid?
229
+ ```
230
+
231
+ ### Luxembourg (lu)
232
+
233
+ `birth_date` is optional
234
+
235
+ ```rb
236
+ TinValid::LuxembourgTin.new(
237
+ tin: "1893120105732",
238
+ birth_date: Date.new(1893, 12, 1),
239
+ ).valid?
240
+ ```
241
+
242
+ ### Malta (mt)
243
+
244
+ ```rb
245
+ TinValid::MaltaTin.new(tin: "1234567A").valid?
246
+ ```
247
+
248
+ ### Netherlands (nl)
249
+
250
+ ```rb
251
+ TinValid::NetherlandsTin.new(tin: "174559434").valid?
252
+ ```
253
+
254
+ ### Poland (pl)
255
+
256
+ `birth_date` is optional
257
+
258
+ ```rb
259
+ TinValid::PolandTin.new(
260
+ tin: "02070803628",
261
+ birth_date: Date.new(1902, 7, 8),
262
+ ).valid?
263
+ ```
264
+
265
+ ### Portugal (pt)
266
+
267
+ ```rb
268
+ TinValid::PortugalTin.new(tin: "299999998").valid?
269
+ ```
270
+
271
+ ### Romania (ro)
272
+
273
+ `birth_date` is optional
274
+
275
+ ```rb
276
+ TinValid::RomaniaTin.new(
277
+ tin: "8001011234567",
278
+ birth_date: Date.new(2000, 10, 11),
279
+ ).valid?
280
+ ```
281
+
282
+ ### Slovakia (sk)
283
+
284
+ `birth_date` is optional
285
+
286
+ ```rb
287
+ TinValid::SlovakiaTin.new(
288
+ tin: "7711167420",
289
+ birth_date: Date.new(1977, 11, 16),
290
+ ).valid?
291
+ ```
292
+
293
+ ### Slovenia (si)
294
+
295
+ ```rb
296
+ TinValid::SloveniaTin.new(tin: "15012557").valid?
297
+ ```
298
+
299
+ ### Spain (es)
300
+
301
+ ```rb
302
+ TinValid::SpainTin.new(tin: "54237A").valid?
303
+ ```
304
+
305
+ ### Sweden (se)
306
+
307
+ `birth_date` is optional
308
+
309
+ ```rb
310
+ TinValid::SwedenTin.new(
311
+ tin: "640823-3234",
312
+ birth_date: Date.new(1964, 8, 23),
313
+ ).valid?
314
+ ```
315
+
316
+ ### United Kingdom (gb)
317
+
318
+ ```rb
319
+ TinValid::UnitedKingdomTin.new(tin: "9234567890").valid?
153
320
  ```
154
321
 
155
322
  ## Development
156
323
 
157
324
  After checking out the repo, run `bin/setup` to install dependencies. Then, run
158
- `rake spec` to run the tests. You can also run `bin/console` for an interactive
325
+ `bin/rspec` to run the tests. You can also run `bin/console` for an interactive
159
326
  prompt that will allow you to experiment.
160
327
 
161
- To install this gem onto your local machine, run `bundle exec rake install`. To
162
- release a new version, update the version number in `version.rb`, and then run
163
- `bundle exec rake release`, which will create a git tag for the version, push
164
- git commits and the created tag, and push the `.gem` file to
328
+ To install this gem onto your local machine, run `bin/rake install`. To
329
+ release a new version, update the version number in `version.rb`, call `bundle`
330
+ and then create a commit using:
331
+
332
+ ```sh
333
+ git commit -m "v`ruby -r./lib/tin_valid/version <<< 'puts TinValid::VERSION + \" 🎉\"'`"
334
+ ```
335
+
336
+ Finally, call `bin/rake release`, which will create a git tag for the version,
337
+ push git commits and the created tag, and add the `.gem` file to
165
338
  [rubygems.org](https://rubygems.org).
166
339
 
167
340
  ## Contributing
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # https://www.economie.gouv.fr/cedef/fiches-pratiques/quest-ce-que-le-nif
3
4
  module TinValid
4
5
  class FranceTin
5
6
  def initialize(tin:)
@@ -8,21 +9,48 @@ module TinValid
8
9
 
9
10
  attr_reader :tin
10
11
 
11
- def valid?
12
- return false unless /\A[0-3][0-9]{12}\z/.match?(tin)
13
- return false if tin == "0000000000000"
12
+ def valid? = valid_v1? || siren? || siret?
14
13
 
15
- tin[-3..].to_i == check
14
+ # https://fr.wikipedia.org/wiki/Syst%C3%A8me_d%27identification_du_r%C3%A9pertoire_des_entreprises
15
+ def siren?
16
+ return false unless /\A[1-9][0-9]{8}\z/.match?(tin)
17
+ return false if tin == "123456789"
18
+
19
+ luhn_valid?
20
+ end
21
+
22
+ # https://fr.wikipedia.org/wiki/Syst%C3%A8me_d%27identification_du_r%C3%A9pertoire_des_%C3%A9tablissements
23
+ def siret?
24
+ return false unless /\A\d{14}\z/.match?(tin)
25
+ return false if tin == "00000000000000"
26
+
27
+ luhn_valid?
16
28
  end
17
29
 
18
30
  private
19
31
 
20
- def check
32
+ def valid_v1?
33
+ return false unless /\A[0-3][0-9]{12}\z/.match?(tin)
34
+ return false if tin == "0000000000000"
35
+
36
+ tin[-3..].to_i == v1_check
37
+ end
38
+
39
+ def v1_check
21
40
  # 1. Concatenate C1, C2, C3, C4, C5, C6, C7, C8, C9, C10;
22
41
  # 2. Get modulo 511 of the result of the previous result;
23
42
  # 3. Check digit = remainder if remainder < 100, C11 = 0
24
43
  # (if remainder < 10, C11 = 0 and C12 = 0).
25
44
  tin[..-4].to_i % 511
26
45
  end
46
+
47
+ # https://en.wikipedia.org/wiki/Luhn_algorithm
48
+ def luhn_valid?
49
+ check = tin.chars.reverse.each_slice(2).sum do |a, b|
50
+ a.to_i + (b.to_i * 2).to_s.chars.sum(&:to_i)
51
+ end
52
+
53
+ check % 10 == 0
54
+ end
27
55
  end
28
56
  end
@@ -95,7 +95,7 @@ module TinValid
95
95
  [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
96
96
  [1, 5, 7, 6, 2, 8, 3, 0, 9, 4],
97
97
  [5, 8, 0, 3, 7, 9, 6, 1, 4, 2],
98
- [8, 9, 1, 6, 0, 5, 3, 5, 2, 7],
98
+ [8, 9, 1, 6, 0, 4, 3, 5, 2, 7],
99
99
  [9, 4, 5, 3, 1, 2, 6, 8, 7, 0],
100
100
  [4, 2, 8, 6, 5, 7, 3, 9, 0, 1],
101
101
  [2, 7, 9, 3, 8, 0, 6, 4, 1, 5],
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TinValid
4
+ class Tin
5
+ def initialize(country_code:, tin:, birth_date: nil, kind: nil)
6
+ @country_code = country_code
7
+ @tin = tin
8
+ @birth_date = birth_date
9
+ @kind = kind
10
+ end
11
+
12
+ attr_reader :country_code, :tin, :birth_date, :kind
13
+
14
+ # rubocop:disable Metrics/AbcSize
15
+ # rubocop:disable Metrics/CyclomaticComplexity
16
+ # rubocop:disable Metrics/PerceivedComplexity
17
+ # rubocop:disable Metrics/MethodLength
18
+ def valid?
19
+ case country_code
20
+ in "at" then AustriaTin.new(tin:).valid?
21
+ in "be" then BelgiumTin.new(tin:, birth_date:).valid?
22
+ in "bg" then BulgariaTin.new(tin:, birth_date:).valid?
23
+ in "cy" then CyprusTin.new(tin:, kind:).valid?
24
+ in "cz" then CzechiaTin.new(tin:, birth_date:).valid?
25
+ in "de" then GermanyTin.new(tin:).valid?
26
+ in "dk" then DenmarkTin.new(tin:, birth_date:).valid?
27
+ in "ee" then EstoniaTin.new(tin:, birth_date:).valid?
28
+ in "es" then SpainTin.new(tin:).valid?
29
+ in "fi" then FinlandTin.new(tin:).valid?
30
+ in "fr" then FranceTin.new(tin:).valid?
31
+ in "gb" then UnitedKingdomTin.new(tin:).valid?
32
+ in "gr" then GreeceTin.new(tin:).valid?
33
+ in "hr" then CroatiaTin.new(tin:).valid?
34
+ in "hu" then HungaryTin.new(tin:).valid?
35
+ in "ie" then IrelandTin.new(tin:).valid?
36
+ in "it" then ItalyTin.new(tin:, birth_date:).valid?
37
+ in "lt" then LithuaniaTin.new(tin:, birth_date:).valid?
38
+ in "lu" then LuxembourgTin.new(tin:, birth_date:).valid?
39
+ in "lv" then LatviaTin.new(tin:, birth_date:).valid?
40
+ in "mt" then MaltaTin.new(tin:).valid?
41
+ in "nl" then NetherlandsTin.new(tin:).valid?
42
+ in "pl" then PolandTin.new(tin:, birth_date:).valid?
43
+ in "pt" then PortugalTin.new(tin:).valid?
44
+ in "ro" then RomaniaTin.new(tin:, birth_date:).valid?
45
+ in "se" then SwedenTin.new(tin:, birth_date:).valid?
46
+ in "si" then SloveniaTin.new(tin:).valid?
47
+ in "sk" then SlovakiaTin.new(tin:).valid?
48
+ end
49
+ end
50
+ # rubocop:enable Metrics/AbcSize
51
+ # rubocop:enable Metrics/CyclomaticComplexity
52
+ # rubocop:enable Metrics/PerceivedComplexity
53
+ # rubocop:enable Metrics/MethodLength
54
+ end
55
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module TinValid
4
- VERSION = "1.1.1"
4
+ VERSION = "1.2.0"
5
5
  end
data/lib/tin_valid.rb CHANGED
@@ -3,6 +3,7 @@
3
3
  require "date"
4
4
  require_relative "tin_valid/version"
5
5
  require_relative "tin_valid/helpers"
6
+ require_relative "tin_valid/tin"
6
7
  require_relative "tin_valid/austria_tin"
7
8
  require_relative "tin_valid/belgium_tin"
8
9
  require_relative "tin_valid/bulgaria_tin"
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: 1.1.1
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sunny Ripert
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2025-04-28 00:00:00.000000000 Z
10
+ date: 2025-05-14 00:00:00.000000000 Z
11
11
  dependencies: []
12
12
  email:
13
13
  - sunny@sunfox.org
@@ -51,6 +51,7 @@ files:
51
51
  - lib/tin_valid/slovenia_tin.rb
52
52
  - lib/tin_valid/spain_tin.rb
53
53
  - lib/tin_valid/sweden_tin.rb
54
+ - lib/tin_valid/tin.rb
54
55
  - lib/tin_valid/united_kingdom_tin.rb
55
56
  - lib/tin_valid/version.rb
56
57
  - sig/tin_valid.rbs