22# Part of Odoo. See LICENSE file for full copyright and licensing details.
33
44import datetime
5- import logging
65import string
76import 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
249from odoo import api , models , tools , _
2510from odoo .tools .misc import ustr
2611from 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'
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-
0 commit comments