Skip to content

Commit c377e46

Browse files
hpr-odood-fence
authored andcommitted
[IMP] base_vat: replace vatnumver by stdnum library.
Python module vatnumber doesn't seem maintained anymore. Therefore, we should: - call directly stdnum (which is maintained and mostly used everywhere in vatnumber) Also improve stdnum import, vat fix method and vat expected formats task-1915371 cherry-pick of 4b2abaf Part-of: odoo#138754
1 parent 5fc477b commit c377e46

File tree

8 files changed

+98
-55
lines changed

8 files changed

+98
-55
lines changed

addons/base_vat/models/res_partner.py

+91-48
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,9 @@
22
# Part of Odoo. See LICENSE file for full copyright and licensing details.
33

44
import datetime
5-
import logging
65
import string
76
import re
8-
9-
_logger = logging.getLogger(__name__)
10-
try:
11-
import vatnumber
12-
except ImportError:
13-
_logger.warning("VAT validation partially unavailable because the `vatnumber` Python library cannot be found. "
14-
"Install it to support more countries, for example with `easy_install vatnumber`.")
15-
vatnumber = None
16-
17-
# Although stdnum is a dependency of vatnumber, the import of the latter is surrounded by a try/except
18-
# if it is not installed. Therefore, we cannot be sure stdnum is installed in all cases.
19-
try:
20-
import stdnum
21-
except ImportError:
22-
stdnum = None
7+
import stdnum
238

249
from odoo import api, models, tools, _
2510
from odoo.tools.misc import ustr
@@ -32,38 +17,46 @@
3217
_eu_country_vat_inverse = {v: k for k, v in _eu_country_vat.items()}
3318

3419
_ref_vat = {
20+
'al': 'ALJ91402501L',
21+
'ar': 'AR200-5536168-2 or 20055361682',
3522
'at': 'ATU12345675',
3623
'be': 'BE0477472701',
3724
'bg': 'BG1234567892',
3825
'ch': 'CHE-123.456.788 TVA or CHE-123.456.788 MWST or CHE-123.456.788 IVA', # Swiss by Yannick Vaucher @ Camptocamp
3926
'cl': 'CL76086428-5',
4027
'co': 'CO213123432-1 or CO213.123.432-1',
41-
'cy': 'CY12345678F',
28+
'cy': 'CY10259033P',
4229
'cz': 'CZ12345679',
4330
'de': 'DE123456788',
4431
'dk': 'DK12345674',
32+
'do': 'DO1-01-85004-3 or 101850043',
33+
'ec': 'EC1792060346-001',
4534
'ee': 'EE123456780',
4635
'el': 'EL12345670',
4736
'es': 'ESA12345674',
4837
'fi': 'FI12345671',
49-
'fr': 'FR32123456789',
38+
'fr': 'FR23334175221',
5039
'gb': 'GB123456782 or XI123456782',
5140
'gr': 'GR12345670',
5241
'hu': 'HU12345676',
5342
'hr': 'HR01234567896', # Croatia, contributed by Milan Tribuson
5443
'ie': 'IE1234567FA',
44+
'is': 'IS062199',
5545
'it': 'IT12345670017',
5646
'lt': 'LT123456715',
5747
'lu': 'LU12345613',
5848
'lv': 'LV41234567891',
49+
'mc': 'FR53000004605',
5950
'mt': 'MT12345634',
60-
'mx': 'ABC123456T1B',
51+
'mx': 'MXGODE561231GR8 or GODE561231GR8',
6152
'nl': 'NL123456782B90',
6253
'no': 'NO123456785',
6354
'pe': '10XXXXXXXXY or 20XXXXXXXXY or 15XXXXXXXXY or 16XXXXXXXXY or 17XXXXXXXXY',
6455
'pl': 'PL1234567883',
6556
'pt': 'PT123456789',
6657
'ro': 'RO1234567897',
58+
'rs': 'RS101134702',
59+
'ru': 'RU123456789047',
6760
'se': 'SE123456789701',
6861
'si': 'SI12345679',
6962
'sk': 'SK0012345675',
@@ -92,7 +85,7 @@ def simple_vat_check(self, country_code, vat_number):
9285
if not ustr(country_code).encode('utf-8').isalpha():
9386
return False
9487
check_func_name = 'check_vat_' + country_code
95-
check_func = getattr(self, check_func_name, None) or getattr(vatnumber, check_func_name, None)
88+
check_func = getattr(self, check_func_name, None) or getattr(stdnum.util.get_cc_module(country_code, 'vat'), 'is_valid', None)
9689
if not check_func:
9790
# No VAT validation available, default to check that the country code exists
9891
if country_code.upper() == 'EU':
@@ -108,7 +101,7 @@ def simple_vat_check(self, country_code, vat_number):
108101
def _check_vies(self, vat):
109102
# Store the VIES result in the cache. In case an exception is raised during the request
110103
# (e.g. service unavailable), the fallback on simple_vat_check is not kept in cache.
111-
return vatnumber.check_vies(vat)
104+
return stdnum.eu.vat.check_vies(vat)
112105

113106
@api.model
114107
def vies_vat_check(self, country_code, vat_number):
@@ -230,6 +223,26 @@ def _ie_check_char(self, vat):
230223
checksum = extra + sum((8-i) * int(x) for i, x in enumerate(vat[:7]))
231224
return 'WABCDEFGHIJKLMNOPQRSTUV'[checksum % 23]
232225

226+
def check_vat_co(self, rut):
227+
'''
228+
Check Colombian RUT number.
229+
Method copied from vatnumber 1.2 lib https://code.google.com/archive/p/vatnumber/
230+
'''
231+
if len(rut) != 10:
232+
return False
233+
try:
234+
int(rut)
235+
except ValueError:
236+
return False
237+
nums = [3, 7, 13, 17, 19, 23, 29, 37, 41, 43, 47, 53, 59, 67, 71]
238+
sum = 0
239+
for i in range(len(rut) - 2, -1, -1):
240+
sum += int(rut[i]) * nums[len(rut) - 2 - i]
241+
if sum % 11 > 1:
242+
return int(rut[-1]) == 11 - (sum % 11)
243+
else:
244+
return int(rut[-1]) == sum % 11
245+
233246
def check_vat_ie(self, vat):
234247
""" Temporary Ireland VAT validation to support the new format
235248
introduced in January 2013 in Ireland, until upstream is fixed.
@@ -322,6 +335,43 @@ def check_vat_pe(self, vat):
322335
dig_check = 1
323336
return int(vat[10]) == dig_check
324337

338+
def check_vat_ru(self, vat):
339+
'''
340+
Check Russia VAT number.
341+
Method copied from vatnumber 1.2 lib https://code.google.com/archive/p/vatnumber/
342+
'''
343+
if len(vat) != 10 and len(vat) != 12:
344+
return False
345+
try:
346+
int(vat)
347+
except ValueError:
348+
return False
349+
350+
if len(vat) == 10:
351+
check_sum = 2 * int(vat[0]) + 4 * int(vat[1]) + 10 * int(vat[2]) + \
352+
3 * int(vat[3]) + 5 * int(vat[4]) + 9 * int(vat[5]) + \
353+
4 * int(vat[6]) + 6 * int(vat[7]) + 8 * int(vat[8])
354+
check = check_sum % 11
355+
if check % 10 != int(vat[9]):
356+
return False
357+
else:
358+
check_sum1 = 7 * int(vat[0]) + 2 * int(vat[1]) + 4 * int(vat[2]) + \
359+
10 * int(vat[3]) + 3 * int(vat[4]) + 5 * int(vat[5]) + \
360+
9 * int(vat[6]) + 4 * int(vat[7]) + 6 * int(vat[8]) + \
361+
8 * int(vat[9])
362+
check = check_sum1 % 11
363+
364+
if check != int(vat[10]):
365+
return False
366+
check_sum2 = 3 * int(vat[0]) + 7 * int(vat[1]) + 2 * int(vat[2]) + \
367+
4 * int(vat[3]) + 10 * int(vat[4]) + 3 * int(vat[5]) + \
368+
5 * int(vat[6]) + 9 * int(vat[7]) + 4 * int(vat[8]) + \
369+
6 * int(vat[9]) + 8 * int(vat[10])
370+
check = check_sum2 % 11
371+
if check != int(vat[11]):
372+
return False
373+
return True
374+
325375
# VAT validation in Turkey, contributed by # Levent Karakas @ Eska Yazilim A.S.
326376
def check_vat_tr(self, vat):
327377

@@ -365,19 +415,22 @@ def check_vat_tr(self, vat):
365415

366416
return False
367417

368-
def check_vat_al(self, vat):
418+
def check_vat_ua(self, vat):
419+
'''
420+
Check Ukraine VAT number.
421+
Method copied from vatnumber 1.2 lib https://code.google.com/archive/p/vatnumber/
422+
'''
423+
if len(vat) != 8:
424+
return False
369425
try:
370-
import stdnum.al
371-
return stdnum.al.vat.is_valid(vat)
372-
except ImportError:
373-
return True
426+
int(vat)
427+
except ValueError:
428+
return False
429+
return True
374430

375431
def check_vat_cl(self, vat):
376432
return stdnum.util.get_cc_module('cl', 'vat').is_valid(vat) if stdnum else True
377433

378-
def check_vat_co(self, vat):
379-
return stdnum.util.get_cc_module('co', 'vat').is_valid(vat) if stdnum else True
380-
381434
def check_vat_nl(self, vat):
382435
"""
383436
Temporary Netherlands VAT validation to support the new format introduced in January 2020,
@@ -431,25 +484,15 @@ def check_vat_nl(self, vat):
431484

432485
return False
433486

434-
def check_vat_ua(self, vat):
435-
res = []
436-
for partner in self:
437-
if partner.commercial_partner_id.country_id.code == 'MX':
438-
if len(vat) == 10:
439-
res.append(True)
440-
else:
441-
res.append(False)
442-
elif partner.commercial_partner_id.is_company:
443-
if len(vat) == 12:
444-
res.append(True)
445-
else:
446-
res.append(False)
447-
else:
448-
if len(vat) == 10 or len(vat) == 9:
449-
res.append(True)
450-
else:
451-
res.append(False)
452-
return all(res)
487+
def _fix_vat_number(self, vat):
488+
vat_country, vat_number = self._split_vat(vat)
489+
stdnum_vat_fix_func = getattr(stdnum.util.get_cc_module(vat_country, 'vat'), 'compact', None)
490+
#If any localization module need to define vat fix method for it's country then we give first priority to it.
491+
format_func_name = 'format_vat_' + vat_country
492+
format_func = getattr(self, format_func_name, None) or stdnum_vat_fix_func
493+
if format_func:
494+
vat_number = format_func(vat_number)
495+
return vat_country.upper() + vat_number
453496

454497
def check_vat_xi(self, vat):
455498
""" Temporary Nothern Ireland VAT validation following Brexit

addons/point_of_sale/tools/posbox/overwrite_before_init/etc/init_posbox_image.sh

+1-1
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,8 @@ PKGS_TO_INSTALL="
8484
python3-reportlab \
8585
python3-requests \
8686
python3-simplejson \
87+
python3-stdnum \
8788
python3-tz \
88-
python3-vatnumber \
8989
python3-werkzeug \
9090
python3-serial \
9191
python3-pip \

debian/control

+2-1
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,10 @@ Depends:
3636
python3-pypdf2,
3737
python3-reportlab,
3838
python3-requests,
39+
python3-stdnum,
3940
python3-suds,
4041
python3-tz,
41-
python3-vatnumber,
42+
python3-vobject,
4243
python3-werkzeug,
4344
python3-xlsxwriter,
4445
python3-xlrd,

requirements.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,11 @@ qrcode==5.3
3737
reportlab==3.3.0
3838
requests==2.20.0
3939
suds-jurko==0.6
40-
vatnumber==1.2
4140
vobject==0.9.3
4241
Werkzeug==0.11.15 ; sys_platform != 'win32'
4342
Werkzeug==0.16.0 ; sys_platform == 'win32'
4443
XlsxWriter==0.9.3
44+
python-stdnum==1.8
4545
xlwt==1.3.*
4646
xlrd==1.0.0
4747
pypiwin32 ; sys_platform == 'win32'

setup.cfg

-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@ requires =
4444
python3-six
4545
python3-stdnum
4646
python3-suds
47-
python3-vatnumber
4847
python3-vobject
4948
python3-werkzeug
5049
python3-xlwt

setup.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,13 @@
4444
'pypdf2',
4545
'pyserial',
4646
'python-dateutil',
47+
'python-stdnum',
4748
'pytz',
4849
'pyusb >= 1.0.0b1',
4950
'qrcode',
5051
'reportlab', # windows binary pypi.python.org/pypi/reportlab
5152
'requests',
5253
'suds-jurko',
53-
'vatnumber',
5454
'vobject',
5555
'werkzeug',
5656
'xlsxwriter',

setup/package.dfdebian

+1-1
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,10 @@ RUN apt-get update -qq && \
4545
python3-reportlab \
4646
python3-requests \
4747
python3-serial \
48+
python3-stdnum \
4849
python3-suds \
4950
python3-tz \
5051
python3-usb \
51-
python3-vatnumber \
5252
python3-vobject \
5353
python3-werkzeug \
5454
python3-xlsxwriter \

setup/package.dffedora

+1-1
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@ RUN dnf update -d 0 -e 0 -y && \
3838
python3-reportlab \
3939
python3-requests \
4040
python3-six \
41+
python3-stdnum \
4142
python3-suds \
42-
python3-vatnumber \
4343
python3-vobject \
4444
python3-werkzeug \
4545
python3-xlwt \

0 commit comments

Comments
 (0)