2009-10-13 05:58:37 +00:00
# -*- coding: utf-8 -*-
2008-08-22 13:33:44 +00:00
##############################################################################
#
2010-05-11 06:59:53 +00:00
# OpenERP, Open Source Management Solution
2011-09-20 12:10:54 +00:00
# Copyright (C) 2004-2011 OpenERP SA (<http://openerp.com>)
2008-08-22 13:33:44 +00:00
#
2008-11-03 19:18:56 +00:00
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
2008-08-22 13:33:44 +00:00
#
2008-11-03 19:18:56 +00:00
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
2008-08-22 13:33:44 +00:00
#
2008-11-03 19:18:56 +00:00
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
2008-08-22 13:33:44 +00:00
#
2008-11-03 19:18:56 +00:00
##############################################################################
2011-07-06 07:00:52 +00:00
2011-09-20 12:10:54 +00:00
import logging
2009-04-15 08:57:38 +00:00
import string
2011-07-06 09:14:48 +00:00
import datetime
2011-07-06 07:00:52 +00:00
import re
2012-06-22 06:48:54 +00:00
_logger = logging . getLogger ( __name__ )
2011-07-06 07:00:52 +00:00
2011-09-20 12:10:54 +00:00
try :
import vatnumber
except ImportError :
2012-06-22 06:48:54 +00:00
_logger . warning ( " VAT validation partially unavailable because the `vatnumber` Python library cannot be found. "
2011-09-20 12:10:54 +00:00
" Install it to support more countries, for example with `easy_install vatnumber`. " )
vatnumber = None
2010-05-12 05:59:51 +00:00
from osv import osv , fields
2011-04-29 08:36:25 +00:00
from tools . misc import ustr
2010-05-11 12:50:02 +00:00
from tools . translate import _
_ref_vat = {
2012-01-18 15:30:05 +00:00
' at ' : ' ATU12345675 ' ,
2012-01-30 15:18:36 +00:00
' be ' : ' BE0477472701 ' ,
2012-01-18 15:30:05 +00:00
' bg ' : ' BG1234567892 ' ,
' ch ' : ' CHE-123.456.788 TVA or CH TVA 123456 ' , #Swiss by Yannick Vaucher @ Camptocamp
' cy ' : ' CY12345678F ' ,
' cz ' : ' CZ12345679 ' ,
' de ' : ' DE123456788 ' ,
' dk ' : ' DK12345674 ' ,
' ee ' : ' EE123456780 ' ,
' el ' : ' EL12345670 ' ,
' es ' : ' ESA12345674 ' ,
' fi ' : ' FI12345671 ' ,
' fr ' : ' FR32123456789 ' ,
' gb ' : ' GB123456782 ' ,
' gr ' : ' GR12345670 ' ,
' hu ' : ' HU12345676 ' ,
2011-09-20 13:02:27 +00:00
' hr ' : ' HR01234567896 ' , # Croatia, contributed by Milan Tribuson
2012-01-18 15:30:05 +00:00
' ie ' : ' IE1234567T ' ,
' it ' : ' IT12345670017 ' ,
' lt ' : ' LT123456715 ' ,
' lu ' : ' LU12345613 ' ,
' lv ' : ' LV41234567891 ' ,
' mt ' : ' MT12345634 ' ,
' mx ' : ' MXABC123456T1B ' ,
' nl ' : ' NL123456782B90 ' ,
' no ' : ' NO123456785 ' ,
' pl ' : ' PL1234567883 ' ,
' pt ' : ' PT123456789 ' ,
' ro ' : ' RO1234567897 ' ,
' se ' : ' SE123456789701 ' ,
' si ' : ' SI12345679 ' ,
' sk ' : ' SK0012345675 ' ,
2011-07-06 07:00:52 +00:00
}
2008-08-22 13:33:44 +00:00
class res_partner ( osv . osv ) :
_inherit = ' res.partner '
2008-09-02 13:26:51 +00:00
2010-05-11 12:50:02 +00:00
def _split_vat ( self , vat ) :
vat_country , vat_number = vat [ : 2 ] . lower ( ) , vat [ 2 : ] . replace ( ' ' , ' ' )
return vat_country , vat_number
2011-11-28 16:21:27 +00:00
def simple_vat_check ( self , cr , uid , country_code , vat_number , context = None ) :
2008-08-22 13:33:44 +00:00
'''
Check the VAT number depending of the country .
http : / / sima - pc . com / nif . php
'''
2011-11-28 16:21:27 +00:00
check_func_name = ' check_vat_ ' + country_code
check_func = getattr ( self , check_func_name , None ) or \
getattr ( vatnumber , check_func_name , None )
if not check_func :
# No VAT validation available, default to check that the country code exists
res_country = self . pool . get ( ' res.country ' )
return bool ( res_country . search ( cr , uid , [ ( ' code ' , ' =ilike ' , country_code ) ] , context = context ) )
return check_func ( vat_number )
def vies_vat_check ( self , cr , uid , country_code , vat_number , context = None ) :
try :
# Validate against VAT Information Exchange System (VIES)
# see also http://ec.europa.eu/taxation_customs/vies/
return vatnumber . check_vies ( country_code . upper ( ) + vat_number )
except Exception :
# see http://ec.europa.eu/taxation_customs/vies/checkVatService.wsdl
# Fault code may contain INVALID_INPUT, SERVICE_UNAVAILABLE, MS_UNAVAILABLE,
# TIMEOUT or SERVER_BUSY. There is no way we can validate the input
# with VIES if any of these arise, including the first one (it means invalid
# country code or empty VAT number), so we fall back to the simple check.
return self . simple_vat_check ( cr , uid , country_code , vat_number , context = context )
2012-02-08 15:26:47 +00:00
def button_check_vat ( self , cr , uid , ids , context = None ) :
if not self . check_vat ( cr , uid , ids , context = context ) :
msg = self . _construct_constraint_msg ( cr , uid , ids , context = context )
2012-08-07 11:31:37 +00:00
raise osv . except_osv ( _ ( ' Error! ' ) , msg )
2012-08-24 05:53:12 +00:00
return True
2012-02-08 15:26:47 +00:00
2011-11-28 16:21:27 +00:00
def check_vat ( self , cr , uid , ids , context = None ) :
user_company = self . pool . get ( ' res.users ' ) . browse ( cr , uid , uid ) . company_id
if user_company . vat_check_vies :
# force full VIES online check
check_func = self . vies_vat_check
else :
# quick and partial off-line checksum validation
check_func = self . simple_vat_check
2010-11-19 13:48:01 +00:00
for partner in self . browse ( cr , uid , ids , context = context ) :
2008-08-22 13:33:44 +00:00
if not partner . vat :
2010-05-11 12:57:51 +00:00
continue
2010-05-11 12:50:02 +00:00
vat_country , vat_number = self . _split_vat ( partner . vat )
2011-11-28 16:21:27 +00:00
if not check_func ( cr , uid , vat_country , vat_number , context = context ) :
2008-08-22 13:33:44 +00:00
return False
return True
2010-05-12 05:59:51 +00:00
def vat_change ( self , cr , uid , ids , value , context = None ) :
2008-09-02 13:26:51 +00:00
return { ' value ' : { ' vat_subjected ' : bool ( value ) } }
_columns = {
2009-01-22 12:04:01 +00:00
' vat_subjected ' : fields . boolean ( ' VAT Legal Statement ' , help = " Check this box if the partner is subjected to the VAT. It will be used for the VAT legal statement. " )
2008-09-02 13:26:51 +00:00
}
2010-05-11 12:50:02 +00:00
2010-12-07 07:18:00 +00:00
def _construct_constraint_msg ( self , cr , uid , ids , context = None ) :
2010-05-11 12:50:02 +00:00
def default_vat_check ( cn , vn ) :
# by default, a VAT number is valid if:
# it starts with 2 letters
# has more than 3 characters
return cn [ 0 ] in string . ascii_lowercase and cn [ 1 ] in string . ascii_lowercase
vat_country , vat_number = self . _split_vat ( self . browse ( cr , uid , ids ) [ 0 ] . vat )
2011-09-20 12:10:54 +00:00
vat_no = " ' CC## ' (CC=Country Code, ##=VAT Number) "
2010-05-11 12:50:02 +00:00
if default_vat_check ( vat_country , vat_number ) :
2011-09-20 12:10:54 +00:00
vat_no = _ref_vat [ vat_country ] if vat_country in _ref_vat else vat_no
return ' \n ' + _ ( ' This VAT number does not seem to be valid. \n Note: the expected format is %s ' ) % vat_no
2010-05-11 12:50:02 +00:00
_constraints = [ ( check_vat , _construct_constraint_msg , [ " vat " ] ) ]
2008-09-02 13:26:51 +00:00
2008-08-22 13:33:44 +00:00
2012-01-26 15:59:34 +00:00
__check_vat_ch_re1 = re . compile ( r ' (MWST|TVA|IVA)[0-9] {6} $ ' )
__check_vat_ch_re2 = re . compile ( r ' E([0-9] {9} |-[0-9] {3} \ .[0-9] {3} \ .[0-9] {3} )(MWST|TVA|IVA)$ ' )
2012-01-18 15:30:05 +00:00
def check_vat_ch ( self , vat ) :
'''
Check Switzerland VAT number .
'''
# VAT number in Switzerland will change between 2011 and 2013
# http://www.estv.admin.ch/mwst/themen/00154/00589/01107/index.html?lang=fr
# Old format is "TVA 123456" we will admit the user has to enter ch before the number
# Format will becomes such as "CHE-999.999.99C TVA"
# Both old and new format will be accepted till end of 2013
2012-01-26 15:59:34 +00:00
# Accepted format are: (spaces are ignored)
2012-01-18 15:30:05 +00:00
# CH TVA ######
2012-01-26 15:59:34 +00:00
# CH IVA ######
# CH MWST #######
2012-01-18 15:30:05 +00:00
#
# CHE#########MWST
# CHE#########TVA
# CHE#########IVA
# CHE-###.###.### MWST
# CHE-###.###.### TVA
# CHE-###.###.### IVA
#
2012-01-26 15:59:34 +00:00
if self . __check_vat_ch_re1 . match ( vat ) :
return True
match = self . __check_vat_ch_re2 . match ( vat )
if match :
# For new TVA numbers, do a mod11 check
num = filter ( lambda s : s . isdigit ( ) , match . group ( 1 ) ) # get the digits only
factor = ( 5 , 4 , 3 , 2 , 7 , 6 , 5 , 4 )
csum = sum ( [ int ( num [ i ] ) * factor [ i ] for i in range ( 8 ) ] )
check = 11 - ( csum % 11 )
return check == int ( num [ 8 ] )
return False
2012-01-18 15:30:05 +00:00
2011-09-20 12:10:54 +00:00
# Mexican VAT verification, contributed by <moylop260@hotmail.com>
# and Panos Christeas <p_christ@hol.gr>
2011-04-29 08:36:25 +00:00
__check_vat_mx_re = re . compile ( r " (?P<primeras>[A-Za-z \ xd1 \ xf1&] { 3,4}) " \
2011-04-20 12:06:58 +00:00
r " [ \ -_]? " \
2011-04-29 08:36:25 +00:00
r " (?P<ano>[0-9] {2} )(?P<mes>[01][0-9])(?P<dia>[0-3][0-9]) " \
2011-04-20 12:06:58 +00:00
r " [ \ -_]? " \
2011-04-29 08:36:25 +00:00
r " (?P<code>[A-Za-z0-9& \ xd1 \ xf1] {3} )$ " )
2011-02-11 11:29:07 +00:00
def check_vat_mx ( self , vat ) :
2011-04-20 12:06:58 +00:00
''' Mexican VAT verification
2011-07-06 07:00:52 +00:00
2011-04-20 12:05:54 +00:00
Verificar RFC México
2011-02-11 11:29:07 +00:00
'''
2011-04-29 08:36:25 +00:00
# we convert to 8-bit encoding, to help the regex parse only bytes
vat = ustr ( vat ) . encode ( ' iso8859-1 ' )
2011-04-20 12:06:58 +00:00
m = self . __check_vat_mx_re . match ( vat )
if not m :
2011-04-20 12:05:54 +00:00
#No valid format
2011-02-11 11:29:07 +00:00
return False
2011-04-20 12:05:54 +00:00
try :
2011-04-29 08:36:25 +00:00
ano = int ( m . group ( ' ano ' ) )
if ano > 30 :
ano = 1900 + ano
else :
ano = 2000 + ano
datetime . date ( ano , int ( m . group ( ' mes ' ) ) , int ( m . group ( ' dia ' ) ) )
2011-04-20 12:06:58 +00:00
except ValueError :
2011-02-11 11:29:07 +00:00
return False
2011-07-06 07:00:52 +00:00
2011-04-20 12:05:54 +00:00
#Valid format and valid date
2011-02-11 11:29:07 +00:00
return True
2011-08-09 14:17:01 +00:00
2011-08-09 14:20:29 +00:00
2011-09-20 12:10:54 +00:00
# Norway VAT validation, contributed by Rolv Råen (adEgo) <rora@adego.no>
2011-04-19 10:17:34 +00:00
def check_vat_no ( self , vat ) :
'''
2011-08-07 11:25:56 +00:00
Check Norway VAT number . See http : / / www . brreg . no / english / coordination / number . html
2011-04-19 10:17:34 +00:00
'''
if len ( vat ) != 9 :
return False
try :
int ( vat )
2011-08-07 11:25:56 +00:00
except ValueError :
2011-04-19 10:17:34 +00:00
return False
sum = ( 3 * int ( vat [ 0 ] ) ) + ( 2 * int ( vat [ 1 ] ) ) + \
( 7 * int ( vat [ 2 ] ) ) + ( 6 * int ( vat [ 3 ] ) ) + \
( 5 * int ( vat [ 4 ] ) ) + ( 4 * int ( vat [ 5 ] ) ) + \
2011-09-15 10:11:30 +00:00
( 3 * int ( vat [ 6 ] ) ) + ( 2 * int ( vat [ 7 ] ) )
2011-08-09 14:17:01 +00:00
2011-04-19 10:17:34 +00:00
check = 11 - ( sum % 11 )
if check == 11 :
check = 0
if check == 10 :
2011-08-09 13:49:48 +00:00
# 10 is not a valid check digit for an organization number
return False
2011-08-07 11:25:56 +00:00
return check == int ( vat [ 8 ] )
2011-09-15 10:11:30 +00:00
2008-08-22 13:33:44 +00:00
res_partner ( )
2011-01-17 20:44:55 +00:00
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: