eu_vat_rates_data 2026.3.30 → 2026.3.32
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/README.md +34 -0
- data/data/eu-vat-rates-data.json +133 -45
- data/lib/eu_vat_rates_data/version.rb +1 -1
- data/lib/eu_vat_rates_data.rb +33 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 728aa4f4706467b4059bf9939ca891ea746aa8fd9433501dbdb93f8882c2cfc4
|
|
4
|
+
data.tar.gz: 7be6d294a5283282b5c9e1669fd4a19780306838da8dc23f53ac5320860213d5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f8fc1b591cb19ee8a61fc6e4cbab4ae2fc8380a973bd2f6a6754ae68eb21ba5915b77c2d0d0666e61f8c2fe0380fc4aa0a6c1d50070d6deab19507d87c0607be
|
|
7
|
+
data.tar.gz: 500b4fc53b9622e65e11ec588a4fecb111a9e29042c905bca8cd5558d1b253265d0d5b6e12342fcd11559daedf061c9e27fd78a80c80c3dc24e24c93473db424
|
data/README.md
CHANGED
|
@@ -10,6 +10,8 @@ VAT rates for **44 European countries** — EU-27 plus Norway, Switzerland, UK,
|
|
|
10
10
|
- `eu_member` flag on every country — `true` for EU-27, `false` for non-EU
|
|
11
11
|
- `vat_name` — official name of the VAT tax in the country's primary official language
|
|
12
12
|
- `vat_abbr` — short abbreviation used locally (e.g. "ALV", "MwSt", "TVA")
|
|
13
|
+
- **`format` — human-readable VAT number format (e.g. `"ATU + 8 digits"`)** — unique to this package
|
|
14
|
+
- **`pattern` — regex for VAT number validation + built-in `valid_format?()` — free, no API key needed** — unique to this package
|
|
13
15
|
- No dependencies — pure Ruby 3.0+
|
|
14
16
|
- Data bundled in the gem — works offline, no network calls
|
|
15
17
|
- EU rates checked daily via GitHub Actions, new version published only when rates change
|
|
@@ -67,6 +69,21 @@ end
|
|
|
67
69
|
|
|
68
70
|
# When were EU rates last fetched?
|
|
69
71
|
puts EuVatRatesData.data_version # e.g. "2026-03-27"
|
|
72
|
+
|
|
73
|
+
# VAT number format validation — no API key, no network call
|
|
74
|
+
EuVatRatesData.valid_format?("ATU12345678") # => true
|
|
75
|
+
EuVatRatesData.valid_format?("DE123456789") # => true
|
|
76
|
+
EuVatRatesData.valid_format?("INVALID") # => false
|
|
77
|
+
|
|
78
|
+
# Access format metadata directly
|
|
79
|
+
at = EuVatRatesData.get_rate("AT")
|
|
80
|
+
puts at["format"] # "ATU + 8 digits"
|
|
81
|
+
puts at["pattern"] # "^ATU\\d{8}$"
|
|
82
|
+
|
|
83
|
+
# Flag emoji from a 2-letter country code — no lookup table, computed from regional indicator symbols
|
|
84
|
+
EuVatRatesData.flag("FI") # => "🇫🇮"
|
|
85
|
+
EuVatRatesData.flag("DE") # => "🇩🇪"
|
|
86
|
+
EuVatRatesData.flag("XX") # => "" (empty string for unknown/invalid codes)
|
|
70
87
|
```
|
|
71
88
|
|
|
72
89
|
---
|
|
@@ -79,6 +96,23 @@ puts EuVatRatesData.data_version # e.g. "2026-03-27"
|
|
|
79
96
|
|
|
80
97
|
---
|
|
81
98
|
|
|
99
|
+
|
|
100
|
+
## Keeping rates current
|
|
101
|
+
|
|
102
|
+
Rates are bundled at install time. A new package version is published automatically whenever rates change — but your installed version will not update itself.
|
|
103
|
+
|
|
104
|
+
**Recommended:** add [Renovate](https://renovatebot.com) or [Dependabot](https://docs.github.com/en/code-security/dependabot) to your repo. They detect new versions and open a PR automatically whenever rates change — no manual update commands needed.
|
|
105
|
+
|
|
106
|
+
**Need real-time accuracy?** Fetch the always-current JSON directly:
|
|
107
|
+
|
|
108
|
+
```
|
|
109
|
+
https://cdn.jsdelivr.net/gh/vatnode/eu-vat-rates-data@main/data/eu-vat-rates-data.json
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
No package needed — parse it with a single `fetch()` / `http.get()` / `file_get_contents()` call and cache locally.
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
82
116
|
## Covered countries
|
|
83
117
|
|
|
84
118
|
**EU-27** (daily auto-updates via EC TEDB):
|
data/data/eu-vat-rates-data.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "2026-03-
|
|
2
|
+
"version": "2026-03-31",
|
|
3
3
|
"source": "European Commission TEDB",
|
|
4
4
|
"rates": {
|
|
5
5
|
"AD": {
|
|
@@ -14,7 +14,9 @@
|
|
|
14
14
|
2.5
|
|
15
15
|
],
|
|
16
16
|
"super_reduced": null,
|
|
17
|
-
"parking": null
|
|
17
|
+
"parking": null,
|
|
18
|
+
"format": "1 letter + 7 digits + 1 letter",
|
|
19
|
+
"pattern": "^[A-Z]\\d{7}[A-Z]$"
|
|
18
20
|
},
|
|
19
21
|
"AL": {
|
|
20
22
|
"country": "Albania",
|
|
@@ -28,7 +30,9 @@
|
|
|
28
30
|
10.0
|
|
29
31
|
],
|
|
30
32
|
"super_reduced": null,
|
|
31
|
-
"parking": null
|
|
33
|
+
"parking": null,
|
|
34
|
+
"format": "letter (J/K/L) + 8 digits + 1 letter",
|
|
35
|
+
"pattern": "^[JKL]\\d{8}[A-Z]$"
|
|
32
36
|
},
|
|
33
37
|
"AT": {
|
|
34
38
|
"country": "Austria",
|
|
@@ -43,7 +47,9 @@
|
|
|
43
47
|
19.0
|
|
44
48
|
],
|
|
45
49
|
"super_reduced": null,
|
|
46
|
-
"parking": null
|
|
50
|
+
"parking": null,
|
|
51
|
+
"format": "ATU + 8 digits",
|
|
52
|
+
"pattern": "^ATU\\d{8}$"
|
|
47
53
|
},
|
|
48
54
|
"BA": {
|
|
49
55
|
"country": "Bosnia and Herzegovina",
|
|
@@ -54,7 +60,9 @@
|
|
|
54
60
|
"standard": 17.0,
|
|
55
61
|
"reduced": [],
|
|
56
62
|
"super_reduced": null,
|
|
57
|
-
"parking": null
|
|
63
|
+
"parking": null,
|
|
64
|
+
"format": "12 digits",
|
|
65
|
+
"pattern": "^\\d{12}$"
|
|
58
66
|
},
|
|
59
67
|
"BE": {
|
|
60
68
|
"country": "Belgium",
|
|
@@ -68,7 +76,9 @@
|
|
|
68
76
|
12.0
|
|
69
77
|
],
|
|
70
78
|
"super_reduced": null,
|
|
71
|
-
"parking": 12.0
|
|
79
|
+
"parking": 12.0,
|
|
80
|
+
"format": "BE + 0/1 + 9 digits",
|
|
81
|
+
"pattern": "^BE[01]\\d{9}$"
|
|
72
82
|
},
|
|
73
83
|
"BG": {
|
|
74
84
|
"country": "Bulgaria",
|
|
@@ -81,7 +91,9 @@
|
|
|
81
91
|
9.0
|
|
82
92
|
],
|
|
83
93
|
"super_reduced": null,
|
|
84
|
-
"parking": null
|
|
94
|
+
"parking": null,
|
|
95
|
+
"format": "BG + 9–10 digits",
|
|
96
|
+
"pattern": "^BG\\d{9,10}$"
|
|
85
97
|
},
|
|
86
98
|
"CH": {
|
|
87
99
|
"country": "Switzerland",
|
|
@@ -95,7 +107,9 @@
|
|
|
95
107
|
3.8
|
|
96
108
|
],
|
|
97
109
|
"super_reduced": null,
|
|
98
|
-
"parking": null
|
|
110
|
+
"parking": null,
|
|
111
|
+
"format": "CHE + 9 digits (+ MWST/TVA/IVA)",
|
|
112
|
+
"pattern": "^CHE\\d{9}(MWST|TVA|IVA)?$"
|
|
99
113
|
},
|
|
100
114
|
"CY": {
|
|
101
115
|
"country": "Cyprus",
|
|
@@ -109,7 +123,9 @@
|
|
|
109
123
|
9.0
|
|
110
124
|
],
|
|
111
125
|
"super_reduced": 3.0,
|
|
112
|
-
"parking": null
|
|
126
|
+
"parking": null,
|
|
127
|
+
"format": "CY + 8 digits + 1 letter",
|
|
128
|
+
"pattern": "^CY\\d{8}[A-Z]$"
|
|
113
129
|
},
|
|
114
130
|
"CZ": {
|
|
115
131
|
"country": "Czech Republic",
|
|
@@ -122,7 +138,9 @@
|
|
|
122
138
|
12.0
|
|
123
139
|
],
|
|
124
140
|
"super_reduced": null,
|
|
125
|
-
"parking": null
|
|
141
|
+
"parking": null,
|
|
142
|
+
"format": "CZ + 8–10 digits",
|
|
143
|
+
"pattern": "^CZ\\d{8,10}$"
|
|
126
144
|
},
|
|
127
145
|
"DE": {
|
|
128
146
|
"country": "Germany",
|
|
@@ -135,7 +153,9 @@
|
|
|
135
153
|
7.0
|
|
136
154
|
],
|
|
137
155
|
"super_reduced": null,
|
|
138
|
-
"parking": null
|
|
156
|
+
"parking": null,
|
|
157
|
+
"format": "DE + 9 digits",
|
|
158
|
+
"pattern": "^DE\\d{9}$"
|
|
139
159
|
},
|
|
140
160
|
"DK": {
|
|
141
161
|
"country": "Denmark",
|
|
@@ -146,7 +166,9 @@
|
|
|
146
166
|
"standard": 25.0,
|
|
147
167
|
"reduced": [],
|
|
148
168
|
"super_reduced": null,
|
|
149
|
-
"parking": null
|
|
169
|
+
"parking": null,
|
|
170
|
+
"format": "DK + 8 digits",
|
|
171
|
+
"pattern": "^DK\\d{8}$"
|
|
150
172
|
},
|
|
151
173
|
"EE": {
|
|
152
174
|
"country": "Estonia",
|
|
@@ -160,7 +182,9 @@
|
|
|
160
182
|
13.0
|
|
161
183
|
],
|
|
162
184
|
"super_reduced": null,
|
|
163
|
-
"parking": null
|
|
185
|
+
"parking": null,
|
|
186
|
+
"format": "EE + 9 digits",
|
|
187
|
+
"pattern": "^EE\\d{9}$"
|
|
164
188
|
},
|
|
165
189
|
"ES": {
|
|
166
190
|
"country": "Spain",
|
|
@@ -173,7 +197,9 @@
|
|
|
173
197
|
10.0
|
|
174
198
|
],
|
|
175
199
|
"super_reduced": 4.0,
|
|
176
|
-
"parking": null
|
|
200
|
+
"parking": null,
|
|
201
|
+
"format": "ES + letter/digit + 7 digits + letter/digit",
|
|
202
|
+
"pattern": "^ES[A-Z0-9]\\d{7}[A-Z0-9]$"
|
|
177
203
|
},
|
|
178
204
|
"FI": {
|
|
179
205
|
"country": "Finland",
|
|
@@ -187,7 +213,9 @@
|
|
|
187
213
|
13.5
|
|
188
214
|
],
|
|
189
215
|
"super_reduced": null,
|
|
190
|
-
"parking": null
|
|
216
|
+
"parking": null,
|
|
217
|
+
"format": "FI + 8 digits",
|
|
218
|
+
"pattern": "^FI\\d{8}$"
|
|
191
219
|
},
|
|
192
220
|
"FR": {
|
|
193
221
|
"country": "France",
|
|
@@ -205,7 +233,9 @@
|
|
|
205
233
|
13.0
|
|
206
234
|
],
|
|
207
235
|
"super_reduced": 2.1,
|
|
208
|
-
"parking": null
|
|
236
|
+
"parking": null,
|
|
237
|
+
"format": "FR + 2 alphanumeric + 9 digits",
|
|
238
|
+
"pattern": "^FR[A-HJ-NP-Z0-9]{2}\\d{9}$"
|
|
209
239
|
},
|
|
210
240
|
"GB": {
|
|
211
241
|
"country": "United Kingdom",
|
|
@@ -218,7 +248,9 @@
|
|
|
218
248
|
5.0
|
|
219
249
|
],
|
|
220
250
|
"super_reduced": null,
|
|
221
|
-
"parking": null
|
|
251
|
+
"parking": null,
|
|
252
|
+
"format": "GB + 9 or 12 digits (or GD/HA + 3 digits)",
|
|
253
|
+
"pattern": "^GB(\\d{9}|\\d{12}|GD\\d{3}|HA\\d{3})$"
|
|
222
254
|
},
|
|
223
255
|
"GE": {
|
|
224
256
|
"country": "Georgia",
|
|
@@ -229,7 +261,9 @@
|
|
|
229
261
|
"standard": 18.0,
|
|
230
262
|
"reduced": [],
|
|
231
263
|
"super_reduced": null,
|
|
232
|
-
"parking": null
|
|
264
|
+
"parking": null,
|
|
265
|
+
"format": "9 digits",
|
|
266
|
+
"pattern": "^\\d{9}$"
|
|
233
267
|
},
|
|
234
268
|
"GR": {
|
|
235
269
|
"country": "Greece",
|
|
@@ -244,7 +278,9 @@
|
|
|
244
278
|
17.0
|
|
245
279
|
],
|
|
246
280
|
"super_reduced": 4.0,
|
|
247
|
-
"parking": 13.0
|
|
281
|
+
"parking": 13.0,
|
|
282
|
+
"format": "EL + 9 digits",
|
|
283
|
+
"pattern": "^EL\\d{9}$"
|
|
248
284
|
},
|
|
249
285
|
"HR": {
|
|
250
286
|
"country": "Croatia",
|
|
@@ -258,7 +294,9 @@
|
|
|
258
294
|
13.0
|
|
259
295
|
],
|
|
260
296
|
"super_reduced": null,
|
|
261
|
-
"parking": null
|
|
297
|
+
"parking": null,
|
|
298
|
+
"format": "HR + 11 digits",
|
|
299
|
+
"pattern": "^HR\\d{11}$"
|
|
262
300
|
},
|
|
263
301
|
"HU": {
|
|
264
302
|
"country": "Hungary",
|
|
@@ -272,7 +310,9 @@
|
|
|
272
310
|
18.0
|
|
273
311
|
],
|
|
274
312
|
"super_reduced": null,
|
|
275
|
-
"parking": null
|
|
313
|
+
"parking": null,
|
|
314
|
+
"format": "HU + 8 digits",
|
|
315
|
+
"pattern": "^HU\\d{8}$"
|
|
276
316
|
},
|
|
277
317
|
"IE": {
|
|
278
318
|
"country": "Ireland",
|
|
@@ -286,7 +326,9 @@
|
|
|
286
326
|
13.5
|
|
287
327
|
],
|
|
288
328
|
"super_reduced": null,
|
|
289
|
-
"parking": null
|
|
329
|
+
"parking": null,
|
|
330
|
+
"format": "IE + 7 digits + 1–2 letters",
|
|
331
|
+
"pattern": "^IE\\d{7}[A-W][A-IW]?$|^IE\\d[A-Z+*]\\d{5}[A-W]$"
|
|
290
332
|
},
|
|
291
333
|
"IS": {
|
|
292
334
|
"country": "Iceland",
|
|
@@ -299,7 +341,9 @@
|
|
|
299
341
|
11.0
|
|
300
342
|
],
|
|
301
343
|
"super_reduced": null,
|
|
302
|
-
"parking": null
|
|
344
|
+
"parking": null,
|
|
345
|
+
"format": "6 digits",
|
|
346
|
+
"pattern": "^\\d{6}$"
|
|
303
347
|
},
|
|
304
348
|
"IT": {
|
|
305
349
|
"country": "Italy",
|
|
@@ -313,7 +357,9 @@
|
|
|
313
357
|
10.0
|
|
314
358
|
],
|
|
315
359
|
"super_reduced": 4.0,
|
|
316
|
-
"parking": null
|
|
360
|
+
"parking": null,
|
|
361
|
+
"format": "IT + 11 digits",
|
|
362
|
+
"pattern": "^IT\\d{11}$"
|
|
317
363
|
},
|
|
318
364
|
"LI": {
|
|
319
365
|
"country": "Liechtenstein",
|
|
@@ -327,7 +373,9 @@
|
|
|
327
373
|
3.8
|
|
328
374
|
],
|
|
329
375
|
"super_reduced": null,
|
|
330
|
-
"parking": null
|
|
376
|
+
"parking": null,
|
|
377
|
+
"format": "CHE + 9 digits",
|
|
378
|
+
"pattern": "^CHE\\d{9}$"
|
|
331
379
|
},
|
|
332
380
|
"LT": {
|
|
333
381
|
"country": "Lithuania",
|
|
@@ -341,7 +389,9 @@
|
|
|
341
389
|
12.0
|
|
342
390
|
],
|
|
343
391
|
"super_reduced": null,
|
|
344
|
-
"parking": null
|
|
392
|
+
"parking": null,
|
|
393
|
+
"format": "LT + 9 or 12 digits",
|
|
394
|
+
"pattern": "^LT(\\d{9}|\\d{12})$"
|
|
345
395
|
},
|
|
346
396
|
"LU": {
|
|
347
397
|
"country": "Luxembourg",
|
|
@@ -355,7 +405,9 @@
|
|
|
355
405
|
14.0
|
|
356
406
|
],
|
|
357
407
|
"super_reduced": 3.0,
|
|
358
|
-
"parking": 14.0
|
|
408
|
+
"parking": 14.0,
|
|
409
|
+
"format": "LU + 8 digits",
|
|
410
|
+
"pattern": "^LU\\d{8}$"
|
|
359
411
|
},
|
|
360
412
|
"LV": {
|
|
361
413
|
"country": "Latvia",
|
|
@@ -369,7 +421,9 @@
|
|
|
369
421
|
12.0
|
|
370
422
|
],
|
|
371
423
|
"super_reduced": null,
|
|
372
|
-
"parking": null
|
|
424
|
+
"parking": null,
|
|
425
|
+
"format": "LV + 11 digits",
|
|
426
|
+
"pattern": "^LV\\d{11}$"
|
|
373
427
|
},
|
|
374
428
|
"MC": {
|
|
375
429
|
"country": "Monaco",
|
|
@@ -383,7 +437,9 @@
|
|
|
383
437
|
10.0
|
|
384
438
|
],
|
|
385
439
|
"super_reduced": 2.1,
|
|
386
|
-
"parking": null
|
|
440
|
+
"parking": null,
|
|
441
|
+
"format": "FR + 2 alphanumeric + 9 digits",
|
|
442
|
+
"pattern": "^FR[A-HJ-NP-Z0-9]{2}\\d{9}$"
|
|
387
443
|
},
|
|
388
444
|
"MD": {
|
|
389
445
|
"country": "Moldova",
|
|
@@ -396,7 +452,9 @@
|
|
|
396
452
|
8.0
|
|
397
453
|
],
|
|
398
454
|
"super_reduced": null,
|
|
399
|
-
"parking": null
|
|
455
|
+
"parking": null,
|
|
456
|
+
"format": "7 digits",
|
|
457
|
+
"pattern": "^\\d{7}$"
|
|
400
458
|
},
|
|
401
459
|
"ME": {
|
|
402
460
|
"country": "Montenegro",
|
|
@@ -410,7 +468,9 @@
|
|
|
410
468
|
15.0
|
|
411
469
|
],
|
|
412
470
|
"super_reduced": null,
|
|
413
|
-
"parking": null
|
|
471
|
+
"parking": null,
|
|
472
|
+
"format": "8 digits",
|
|
473
|
+
"pattern": "^\\d{8}$"
|
|
414
474
|
},
|
|
415
475
|
"MK": {
|
|
416
476
|
"country": "North Macedonia",
|
|
@@ -424,7 +484,9 @@
|
|
|
424
484
|
10.0
|
|
425
485
|
],
|
|
426
486
|
"super_reduced": null,
|
|
427
|
-
"parking": null
|
|
487
|
+
"parking": null,
|
|
488
|
+
"format": "13 digits",
|
|
489
|
+
"pattern": "^\\d{13}$"
|
|
428
490
|
},
|
|
429
491
|
"MT": {
|
|
430
492
|
"country": "Malta",
|
|
@@ -438,7 +500,9 @@
|
|
|
438
500
|
7.0
|
|
439
501
|
],
|
|
440
502
|
"super_reduced": null,
|
|
441
|
-
"parking": 12.0
|
|
503
|
+
"parking": 12.0,
|
|
504
|
+
"format": "MT + 8 digits",
|
|
505
|
+
"pattern": "^MT\\d{8}$"
|
|
442
506
|
},
|
|
443
507
|
"NL": {
|
|
444
508
|
"country": "Netherlands",
|
|
@@ -451,7 +515,9 @@
|
|
|
451
515
|
9.0
|
|
452
516
|
],
|
|
453
517
|
"super_reduced": null,
|
|
454
|
-
"parking": null
|
|
518
|
+
"parking": null,
|
|
519
|
+
"format": "NL + 9 digits + B + 2 digits",
|
|
520
|
+
"pattern": "^NL\\d{9}B\\d{2}$"
|
|
455
521
|
},
|
|
456
522
|
"NO": {
|
|
457
523
|
"country": "Norway",
|
|
@@ -465,7 +531,9 @@
|
|
|
465
531
|
15.0
|
|
466
532
|
],
|
|
467
533
|
"super_reduced": null,
|
|
468
|
-
"parking": null
|
|
534
|
+
"parking": null,
|
|
535
|
+
"format": "NO + 9 digits + MVA",
|
|
536
|
+
"pattern": "^NO\\d{9}MVA$"
|
|
469
537
|
},
|
|
470
538
|
"PL": {
|
|
471
539
|
"country": "Poland",
|
|
@@ -479,7 +547,9 @@
|
|
|
479
547
|
8.0
|
|
480
548
|
],
|
|
481
549
|
"super_reduced": 8.0,
|
|
482
|
-
"parking": null
|
|
550
|
+
"parking": null,
|
|
551
|
+
"format": "PL + 10 digits",
|
|
552
|
+
"pattern": "^PL\\d{10}$"
|
|
483
553
|
},
|
|
484
554
|
"PT": {
|
|
485
555
|
"country": "Portugal",
|
|
@@ -495,7 +565,9 @@
|
|
|
495
565
|
22.0
|
|
496
566
|
],
|
|
497
567
|
"super_reduced": 6.0,
|
|
498
|
-
"parking": 13.0
|
|
568
|
+
"parking": 13.0,
|
|
569
|
+
"format": "PT + 9 digits",
|
|
570
|
+
"pattern": "^PT\\d{9}$"
|
|
499
571
|
},
|
|
500
572
|
"RO": {
|
|
501
573
|
"country": "Romania",
|
|
@@ -508,7 +580,9 @@
|
|
|
508
580
|
11.0
|
|
509
581
|
],
|
|
510
582
|
"super_reduced": null,
|
|
511
|
-
"parking": null
|
|
583
|
+
"parking": null,
|
|
584
|
+
"format": "RO + 2–10 digits",
|
|
585
|
+
"pattern": "^RO\\d{2,10}$"
|
|
512
586
|
},
|
|
513
587
|
"RS": {
|
|
514
588
|
"country": "Serbia",
|
|
@@ -521,7 +595,9 @@
|
|
|
521
595
|
10.0
|
|
522
596
|
],
|
|
523
597
|
"super_reduced": null,
|
|
524
|
-
"parking": null
|
|
598
|
+
"parking": null,
|
|
599
|
+
"format": "9 digits",
|
|
600
|
+
"pattern": "^\\d{9}$"
|
|
525
601
|
},
|
|
526
602
|
"SE": {
|
|
527
603
|
"country": "Sweden",
|
|
@@ -535,7 +611,9 @@
|
|
|
535
611
|
12.0
|
|
536
612
|
],
|
|
537
613
|
"super_reduced": null,
|
|
538
|
-
"parking": null
|
|
614
|
+
"parking": null,
|
|
615
|
+
"format": "SE + 12 digits",
|
|
616
|
+
"pattern": "^SE\\d{12}$"
|
|
539
617
|
},
|
|
540
618
|
"SI": {
|
|
541
619
|
"country": "Slovenia",
|
|
@@ -549,7 +627,9 @@
|
|
|
549
627
|
9.5
|
|
550
628
|
],
|
|
551
629
|
"super_reduced": null,
|
|
552
|
-
"parking": null
|
|
630
|
+
"parking": null,
|
|
631
|
+
"format": "SI + 8 digits",
|
|
632
|
+
"pattern": "^SI\\d{8}$"
|
|
553
633
|
},
|
|
554
634
|
"SK": {
|
|
555
635
|
"country": "Slovakia",
|
|
@@ -563,7 +643,9 @@
|
|
|
563
643
|
19.0
|
|
564
644
|
],
|
|
565
645
|
"super_reduced": null,
|
|
566
|
-
"parking": null
|
|
646
|
+
"parking": null,
|
|
647
|
+
"format": "SK + 10 digits",
|
|
648
|
+
"pattern": "^SK\\d{10}$"
|
|
567
649
|
},
|
|
568
650
|
"TR": {
|
|
569
651
|
"country": "Turkey",
|
|
@@ -577,7 +659,9 @@
|
|
|
577
659
|
10.0
|
|
578
660
|
],
|
|
579
661
|
"super_reduced": null,
|
|
580
|
-
"parking": null
|
|
662
|
+
"parking": null,
|
|
663
|
+
"format": "10 digits",
|
|
664
|
+
"pattern": "^\\d{10}$"
|
|
581
665
|
},
|
|
582
666
|
"UA": {
|
|
583
667
|
"country": "Ukraine",
|
|
@@ -591,7 +675,9 @@
|
|
|
591
675
|
14.0
|
|
592
676
|
],
|
|
593
677
|
"super_reduced": null,
|
|
594
|
-
"parking": null
|
|
678
|
+
"parking": null,
|
|
679
|
+
"format": "12 digits",
|
|
680
|
+
"pattern": "^\\d{12}$"
|
|
595
681
|
},
|
|
596
682
|
"XK": {
|
|
597
683
|
"country": "Kosovo",
|
|
@@ -604,7 +690,9 @@
|
|
|
604
690
|
8.0
|
|
605
691
|
],
|
|
606
692
|
"super_reduced": null,
|
|
607
|
-
"parking": null
|
|
693
|
+
"parking": null,
|
|
694
|
+
"format": "9 digits",
|
|
695
|
+
"pattern": "^\\d{9}$"
|
|
608
696
|
}
|
|
609
697
|
}
|
|
610
698
|
}
|
data/lib/eu_vat_rates_data.rb
CHANGED
|
@@ -17,6 +17,7 @@ require_relative "eu_vat_rates_data/version"
|
|
|
17
17
|
# EuVatRatesData.eu_member?("NO") # => false
|
|
18
18
|
# EuVatRatesData.eu_member?("FR") # => true
|
|
19
19
|
# EuVatRatesData.data_version # => "2026-03-18"
|
|
20
|
+
# EuVatRatesData.flag("FI") # => "🇫🇮"
|
|
20
21
|
module EuVatRatesData
|
|
21
22
|
DATA_FILE = File.expand_path("../data/eu-vat-rates-data.json", __dir__)
|
|
22
23
|
|
|
@@ -68,9 +69,40 @@ module EuVatRatesData
|
|
|
68
69
|
rates.key?(country_code.upcase)
|
|
69
70
|
end
|
|
70
71
|
|
|
71
|
-
|
|
72
|
+
# Return true if vat_id matches the expected format for its country.
|
|
73
|
+
# Input must include the country code prefix (e.g. "ATU12345678").
|
|
74
|
+
# Returns false when the country has no standardised format or the ID does not match.
|
|
75
|
+
# Note: Greece uses the "EL" prefix, not "GR".
|
|
76
|
+
# @param vat_id [String] VAT number string including country code prefix
|
|
77
|
+
# @return [Boolean]
|
|
78
|
+
def self.valid_format?(vat_id)
|
|
79
|
+
code = vat_id[0, 2].upcase
|
|
80
|
+
rate = get_rate(code)
|
|
81
|
+
return false unless rate && rate["pattern"]
|
|
82
|
+
!!(vat_id.upcase =~ /\A#{rate["pattern"]}\z/)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Return the ISO 8601 date when EU data was last fetched from EC TEDB.
|
|
72
86
|
# @return [String] e.g. "2026-03-18"
|
|
73
87
|
def self.data_version
|
|
74
88
|
dataset["version"]
|
|
75
89
|
end
|
|
90
|
+
|
|
91
|
+
# Return the flag emoji for a 2-letter ISO 3166-1 alpha-2 country code.
|
|
92
|
+
# Computed from regional indicator symbols — no lookup table needed.
|
|
93
|
+
# Returns an empty string if the input is not exactly 2 ASCII letters.
|
|
94
|
+
#
|
|
95
|
+
# @param country_code [String] ISO 3166-1 alpha-2 code (e.g. "FI", "DE")
|
|
96
|
+
# @return [String] flag emoji (e.g. "🇫🇮"), or "" if invalid
|
|
97
|
+
#
|
|
98
|
+
# @example
|
|
99
|
+
# EuVatRatesData.flag("FI") # => "🇫🇮"
|
|
100
|
+
# EuVatRatesData.flag("DE") # => "🇩🇪"
|
|
101
|
+
# EuVatRatesData.flag("GB") # => "🇬🇧"
|
|
102
|
+
def self.flag(country_code)
|
|
103
|
+
code = country_code.upcase
|
|
104
|
+
return "" unless code.length == 2 && code.match?(/\A[A-Z]{2}\z/)
|
|
105
|
+
base = 0x1F1E6
|
|
106
|
+
[base + code.ord - 65, base + code[-1].ord - 65].pack("U*")
|
|
107
|
+
end
|
|
76
108
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: eu_vat_rates_data
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2026.3.
|
|
4
|
+
version: 2026.3.32
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Iurii Rogulia
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-03-
|
|
11
|
+
date: 2026-03-31 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: VAT rates (standard, reduced, super-reduced, parking) for 44 European
|
|
14
14
|
countries — EU-27 plus Norway, Switzerland, UK, and more. Includes eu_member flag,
|