Skip to content

Commit a43f857

Browse files
hpr-odoojbw-odoo
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 closes odoo#36978 Signed-off-by: Quentin De Paoli (qdp) <qdp@openerp.com>
1 parent 825b227 commit a43f857

File tree

9 files changed

+98
-92
lines changed

9 files changed

+98
-92
lines changed

addons/base_vat/models/res_partner.py

Lines changed: 91 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -2,63 +2,13 @@
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
2611
from odoo.exceptions import ValidationError
27-
from stdnum.at.uid import compact as compact_at
28-
from stdnum.be.vat import compact as compact_be
29-
from stdnum.bg.vat import compact as compact_bg
30-
from stdnum.ch.vat import compact as compact_ch
31-
from stdnum.cy.vat import compact as compact_cy
32-
from stdnum.cz.dic import compact as compact_cz
33-
from stdnum.de.vat import compact as compact_de
34-
from stdnum.ee.kmkr import compact as compact_ee
35-
# el not in stdnum
36-
from stdnum.es.nif import compact as compact_es
37-
from stdnum.fi.alv import compact as compact_fi
38-
from stdnum.fr.tva import compact as compact_fr
39-
from stdnum.gb.vat import compact as compact_gb
40-
from stdnum.gr.vat import compact as compact_gr
41-
from stdnum.hu.anum import compact as compact_hu
42-
from stdnum.hr.oib import compact as compact_hr
43-
from stdnum.ie.vat import compact as compact_ie
44-
from stdnum.it.iva import compact as compact_it
45-
from stdnum.lt.pvm import compact as compact_lt
46-
from stdnum.lu.tva import compact as compact_lu
47-
from stdnum.lv.pvn import compact as compact_lv
48-
from stdnum.mt.vat import compact as compact_mt
49-
from stdnum.mx.rfc import compact as compact_mx
50-
from stdnum.nl.btw import compact as compact_nl
51-
from stdnum.no.mva import compact as compact_no
52-
# pe is not in stdnum
53-
from stdnum.pl.nip import compact as compact_pl
54-
from stdnum.pt.nif import compact as compact_pt
55-
from stdnum.ro.cf import compact as compact_ro
56-
from stdnum.se.vat import compact as compact_se
57-
from stdnum.si.ddv import compact as compact_si
58-
from stdnum.sk.dph import compact as compact_sk
59-
from stdnum.ar.cuit import compact as compact_ar
60-
# tr compact vat is not in stdnum
61-
6212

6313
_eu_country_vat = {
6414
'GR': 'EL'
@@ -67,42 +17,51 @@
6717
_eu_country_vat_inverse = {v: k for k, v in _eu_country_vat.items()}
6818

6919
_ref_vat = {
20+
'al': 'ALJ91402501L',
21+
'ar': 'AR200-5536168-2 or 20055361682',
7022
'at': 'ATU12345675',
7123
'be': 'BE0477472701',
7224
'bg': 'BG1234567892',
7325
'ch': 'CHE-123.456.788 TVA or CH TVA 123456', # Swiss by Yannick Vaucher @ Camptocamp
7426
'cl': 'CL76086428-5',
7527
'co': 'CO213123432-1 or CO213.123.432-1',
76-
'cy': 'CY12345678F',
28+
'cy': 'CY10259033P',
7729
'cz': 'CZ12345679',
7830
'de': 'DE123456788',
7931
'dk': 'DK12345674',
32+
'do': 'DO1-01-85004-3 or 101850043',
33+
'ec': 'EC1792060346-001',
8034
'ee': 'EE123456780',
8135
'el': 'EL12345670',
8236
'es': 'ESA12345674',
8337
'fi': 'FI12345671',
84-
'fr': 'FR32123456789',
38+
'fr': 'FR23334175221',
8539
'gb': 'GB123456782',
8640
'gr': 'GR12345670',
8741
'hu': 'HU12345676',
8842
'hr': 'HR01234567896', # Croatia, contributed by Milan Tribuson
8943
'ie': 'IE1234567FA',
44+
'is': 'IS062199',
9045
'it': 'IT12345670017',
9146
'lt': 'LT123456715',
9247
'lu': 'LU12345613',
9348
'lv': 'LV41234567891',
49+
'mc': 'FR53000004605',
9450
'mt': 'MT12345634',
95-
'mx': 'ABC123456T1B',
51+
'mx': 'MXGODE561231GR8 or GODE561231GR8',
9652
'nl': 'NL123456782B90',
9753
'no': 'NO123456785',
9854
'pe': '10XXXXXXXXY or 20XXXXXXXXY or 15XXXXXXXXY or 16XXXXXXXXY or 17XXXXXXXXY',
9955
'pl': 'PL1234567883',
10056
'pt': 'PT123456789',
10157
'ro': 'RO1234567897',
58+
'rs': 'RS101134702',
59+
'ru': 'RU123456789047',
10260
'se': 'SE123456789701',
10361
'si': 'SI12345679',
104-
'sk': 'SK0012345675',
105-
'tr': 'TR1234567890 (VERGINO) veya TR12345678901 (TCKIMLIKNO)' # Levent Karakas @ Eska Yazilim A.S.
62+
'sk': 'SK2022749619',
63+
'sm': 'SM24165',
64+
'tr': 'TR1234567890 (VERGINO) or TR17291716060 (TCKIMLIKNO)' # Levent Karakas @ Eska Yazilim A.S.
10665
}
10766

10867

@@ -122,7 +81,7 @@ def simple_vat_check(self, country_code, vat_number):
12281
if not ustr(country_code).encode('utf-8').isalpha():
12382
return False
12483
check_func_name = 'check_vat_' + country_code
125-
check_func = getattr(self, check_func_name, None) or getattr(vatnumber, check_func_name, None)
84+
check_func = getattr(self, check_func_name, None) or getattr(stdnum.util.get_cc_module(country_code, 'vat'), 'is_valid', None)
12685
if not check_func:
12786
# No VAT validation available, default to check that the country code exists
12887
if country_code.upper() == 'EU':
@@ -138,7 +97,7 @@ def simple_vat_check(self, country_code, vat_number):
13897
def _check_vies(self, vat):
13998
# Store the VIES result in the cache. In case an exception is raised during the request
14099
# (e.g. service unavailable), the fallback on simple_vat_check is not kept in cache.
141-
return vatnumber.check_vies(vat)
100+
return stdnum.eu.vat.check_vies(vat)
142101

143102
@api.model
144103
def vies_vat_check(self, country_code, vat_number):
@@ -252,6 +211,26 @@ def _ie_check_char(self, vat):
252211
checksum = extra + sum((8-i) * int(x) for i, x in enumerate(vat[:7]))
253212
return 'WABCDEFGHIJKLMNOPQRSTUV'[checksum % 23]
254213

214+
def check_vat_co(self, rut):
215+
'''
216+
Check Colombian RUT number.
217+
Method copied from vatnumber 1.2 lib https://code.google.com/archive/p/vatnumber/
218+
'''
219+
if len(rut) != 10:
220+
return False
221+
try:
222+
int(rut)
223+
except ValueError:
224+
return False
225+
nums = [3, 7, 13, 17, 19, 23, 29, 37, 41, 43, 47, 53, 59, 67, 71]
226+
sum = 0
227+
for i in range(len(rut) - 2, -1, -1):
228+
sum += int(rut[i]) * nums[len(rut) - 2 - i]
229+
if sum % 11 > 1:
230+
return int(rut[-1]) == 11 - (sum % 11)
231+
else:
232+
return int(rut[-1]) == sum % 11
233+
255234
def check_vat_ie(self, vat):
256235
""" Temporary Ireland VAT validation to support the new format
257236
introduced in January 2013 in Ireland, until upstream is fixed.
@@ -396,6 +375,43 @@ def check_vat_pe(self, vat):
396375
dig_check = 1
397376
return int(vat[10]) == dig_check
398377

378+
def check_vat_ru(self, vat):
379+
'''
380+
Check Russia VAT number.
381+
Method copied from vatnumber 1.2 lib https://code.google.com/archive/p/vatnumber/
382+
'''
383+
if len(vat) != 10 and len(vat) != 12:
384+
return False
385+
try:
386+
int(vat)
387+
except ValueError:
388+
return False
389+
390+
if len(vat) == 10:
391+
check_sum = 2 * int(vat[0]) + 4 * int(vat[1]) + 10 * int(vat[2]) + \
392+
3 * int(vat[3]) + 5 * int(vat[4]) + 9 * int(vat[5]) + \
393+
4 * int(vat[6]) + 6 * int(vat[7]) + 8 * int(vat[8])
394+
check = check_sum % 11
395+
if check % 10 != int(vat[9]):
396+
return False
397+
else:
398+
check_sum1 = 7 * int(vat[0]) + 2 * int(vat[1]) + 4 * int(vat[2]) + \
399+
10 * int(vat[3]) + 3 * int(vat[4]) + 5 * int(vat[5]) + \
400+
9 * int(vat[6]) + 4 * int(vat[7]) + 6 * int(vat[8]) + \
401+
8 * int(vat[9])
402+
check = check_sum1 % 11
403+
404+
if check != int(vat[10]):
405+
return False
406+
check_sum2 = 3 * int(vat[0]) + 7 * int(vat[1]) + 2 * int(vat[2]) + \
407+
4 * int(vat[3]) + 10 * int(vat[4]) + 3 * int(vat[5]) + \
408+
5 * int(vat[6]) + 9 * int(vat[7]) + 4 * int(vat[8]) + \
409+
6 * int(vat[9]) + 8 * int(vat[10])
410+
check = check_sum2 % 11
411+
if check != int(vat[11]):
412+
return False
413+
return True
414+
399415
# VAT validation in Turkey, contributed by # Levent Karakas @ Eska Yazilim A.S.
400416
def check_vat_tr(self, vat):
401417

@@ -439,35 +455,27 @@ def check_vat_tr(self, vat):
439455

440456
return False
441457

442-
def check_vat_al(self, vat):
443-
try:
444-
import stdnum.al
445-
return stdnum.al.vat.is_valid(vat)
446-
except ImportError:
447-
return True
448-
449-
def check_vat_cl(self, vat):
450-
return stdnum.util.get_cc_module('cl', 'vat').is_valid(vat) if stdnum else True
451-
452-
def check_vat_co(self, vat):
453-
return stdnum.util.get_cc_module('co', 'vat').is_valid(vat) if stdnum else True
454-
455-
# Argentinian VAT validation, contributed by ADHOC
456-
def check_vat_ar(self, vat):
458+
def check_vat_ua(self, vat):
459+
'''
460+
Check Ukraine VAT number.
461+
Method copied from vatnumber 1.2 lib https://code.google.com/archive/p/vatnumber/
462+
'''
463+
if len(vat) != 8:
464+
return False
457465
try:
458-
import stdnum.ar
459-
return stdnum.ar.cuit.is_valid(vat)
460-
except ImportError:
461-
return True
462-
463-
def default_compact(self, vat):
464-
return vat
466+
int(vat)
467+
except ValueError:
468+
return False
469+
return True
465470

466471
def _fix_vat_number(self, vat):
467472
vat_country, vat_number = self._split_vat(vat)
468-
check_func_name = 'compact_' + vat_country
469-
check_func = globals().get(check_func_name) or getattr(self, 'default_compact')
470-
vat_number = check_func(vat_number)
473+
stdnum_vat_fix_func = getattr(stdnum.util.get_cc_module(vat_country, 'vat'), 'compact', None)
474+
#If any localization module need to define vat fix method for it's country then we give first priority to it.
475+
format_func_name = 'format_vat_' + vat_country
476+
format_func = getattr(self, format_func_name, None) or stdnum_vat_fix_func
477+
if format_func:
478+
vat_number = format_func(vat_number)
471479
return vat_country.upper() + vat_number
472480

473481
@api.model
@@ -480,4 +488,3 @@ def write(self, values):
480488
if values.get('vat'):
481489
values['vat'] = self._fix_vat_number(values['vat'])
482490
return super(ResPartner, self).write(values)
483-

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,8 @@ PKGS_TO_INSTALL="
8383
python3-reportlab \
8484
python3-requests \
8585
python3-simplejson \
86+
python3-stdnum \
8687
python3-tz \
87-
python3-vatnumber \
8888
python3-werkzeug \
8989
python3-serial \
9090
python3-pip \

debian/control

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,9 @@ Depends:
3939
python3-qrcode,
4040
python3-reportlab,
4141
python3-requests,
42+
python3-stdnum,
4243
python3-suds,
4344
python3-tz,
44-
python3-vatnumber,
4545
python3-vobject,
4646
python3-werkzeug,
4747
python3-xlsxwriter,

requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ qrcode==6.1
3939
reportlab==3.5.13
4040
requests==2.21.0
4141
zeep==3.2.0
42-
vatnumber==1.2
42+
python-stdnum==1.8
4343
vobject==0.9.6.1
4444
Werkzeug==0.14.1
4545
XlsxWriter==1.1.2

setup.cfg

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ requires =
4545
python3-six
4646
python3-stdnum
4747
python3-suds
48-
python3-vatnumber
4948
python3-vobject
5049
python3-werkzeug
5150
python3-xlwt

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,13 @@
4545
'pypdf2',
4646
'pyserial',
4747
'python-dateutil',
48+
'python-stdnum',
4849
'pytz',
4950
'pyusb >= 1.0.0b1',
5051
'qrcode',
5152
'reportlab', # windows binary pypi.python.org/pypi/reportlab
5253
'requests',
5354
'zeep',
54-
'vatnumber',
5555
'vobject',
5656
'werkzeug',
5757
'xlsxwriter',

setup/package.dfdebian

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,10 @@ RUN apt-get update -qq && \
4848
python3-reportlab \
4949
python3-requests \
5050
python3-serial \
51+
python3-stdnum \
5152
python3-suds \
5253
python3-tz \
5354
python3-usb \
54-
python3-vatnumber \
5555
python3-vobject \
5656
python3-werkzeug \
5757
python3-xlsxwriter \

setup/package.dffedora

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@ RUN dnf update -d 0 -e 0 -y && \
3939
python3-reportlab \
4040
python3-requests \
4141
python3-six \
42+
python3-stdnum \
4243
python3-suds \
43-
python3-vatnumber \
4444
python3-vobject \
4545
python3-werkzeug \
4646
python3-xlwt \

setup/win32/winpy_requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ qrcode==5.3
2929
reportlab>=3.3.0
3030
requests==2.20.0
3131
six==1.10.0
32+
stdnum==1.8
3233
zeep==3.1.0
33-
vatnumber==1.2
3434
vobject==0.9.3
3535
Werkzeug>=0.11.11
3636
XlsxWriter==0.9.3

0 commit comments

Comments
 (0)