diff options
Diffstat (limited to 'addons/base_vat')
| -rw-r--r-- | addons/base_vat/models/res_partner.py | 29 | ||||
| -rw-r--r-- | addons/base_vat/tests/test_validate_ruc.py | 36 |
2 files changed, 46 insertions, 19 deletions
diff --git a/addons/base_vat/models/res_partner.py b/addons/base_vat/models/res_partner.py index 1bcc7dc5d01..dcb863d68a3 100644 --- a/addons/base_vat/models/res_partner.py +++ b/addons/base_vat/models/res_partner.py @@ -5,11 +5,17 @@ import datetime import string import re import stdnum +from stdnum.eu.vat import check_vies +from stdnum.exceptions import InvalidComponent +import logging from odoo import api, models, tools, _ from odoo.tools.misc import ustr from odoo.exceptions import ValidationError + +_logger = logging.getLogger(__name__) + _eu_country_vat = { 'GR': 'EL' } @@ -99,20 +105,24 @@ class ResPartner(models.Model): def _check_vies(self, vat): # Store the VIES result in the cache. In case an exception is raised during the request # (e.g. service unavailable), the fallback on simple_vat_check is not kept in cache. - return stdnum.eu.vat.check_vies(vat) + return check_vies(vat) @api.model def vies_vat_check(self, country_code, vat_number): try: # Validate against VAT Information Exchange System (VIES) # see also http://ec.europa.eu/taxation_customs/vies/ - return self._check_vies(country_code.upper() + vat_number) + vies_result = self._check_vies(country_code.upper() + vat_number) + return vies_result['valid'] + except InvalidComponent: + return False 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. + _logger.exception("Failed VIES VAT check.") return self.simple_vat_check(country_code, vat_number) @api.model @@ -134,17 +144,18 @@ class ResPartner(models.Model): company = self.env['res.company'].browse(self.env.context['company_id']) else: company = self.env.company - if 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 + eu_countries = self.env.ref('base.europe').country_ids for partner in self: if not partner.vat: continue #check with country code as prefix of the TIN vat_country, vat_number = self._split_vat(partner.vat) + if company.vat_check_vies and partner.commercial_partner_id.country_id in eu_countries: + # 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 if not check_func(vat_country, vat_number): #if fails, check with country code from country country_code = partner.commercial_partner_id.country_id.code @@ -183,7 +194,7 @@ class ResPartner(models.Model): ''' # A new VAT number format in Switzerland has been introduced between 2011 and 2013 # https://www.estv.admin.ch/estv/fr/home/mehrwertsteuer/fachinformationen/steuerpflicht/unternehmens-identifikationsnummer--uid-.html - # The old format "TVA 123456" is not valid since 2014 + # The old format "TVA 123456" is not valid since 2014 # Accepted format are: (spaces are ignored) # CHE#########MWST # CHE#########TVA diff --git a/addons/base_vat/tests/test_validate_ruc.py b/addons/base_vat/tests/test_validate_ruc.py index 9f65d5aab37..c51433da8ed 100644 --- a/addons/base_vat/tests/test_validate_ruc.py +++ b/addons/base_vat/tests/test_validate_ruc.py @@ -1,11 +1,20 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. -from odoo.tests import common +from odoo.tests.common import SavepointCase, tagged from odoo.exceptions import ValidationError from unittest.mock import patch -from stdnum.eu import vat +import stdnum.eu.vat -class TestStructure(common.TransactionCase): + +class TestStructure(SavepointCase): + @classmethod + def setUpClass(cls): + def check_vies(vat_number): + return {'valid': vat_number == 'BE0477472701'} + + super().setUpClass() + cls.env.user.company_id.vat_check_vies = False + cls._vies_check_func = check_vies def test_peru_ruc_format(self): """Only values that has the length of 11 will be checked as RUC, that's what we are proving. The second part @@ -29,7 +38,7 @@ class TestStructure(common.TransactionCase): def test_parent_validation(self): """Test the validation with company and contact""" - # disable the verification to set an invalid vat number + # set an invalid vat number self.env.user.company_id.vat_check_vies = False company = self.env["res.partner"].create({ "name": "World Company", @@ -43,11 +52,18 @@ class TestStructure(common.TransactionCase): "company_type": "person", }) - def mock_check_vies(vat_number): - """ Fake vatnumber method that will only allow one number """ - return vat_number == 'BE0987654321' - # reactivate it and correct the vat number - with patch.object(vat, 'check_vies', mock_check_vies): + with patch('odoo.addons.base_vat.models.res_partner.check_vies', type(self)._vies_check_func): self.env.user.company_id.vat_check_vies = True - company.vat = "BE0987654321" + with self.assertRaises(ValidationError), self.env.cr.savepoint(): + company.vat = "BE0987654321" # VIES refused, don't fallback on other check + company.vat = "BE0477472701" + + +@tagged('-standard', 'external') +class TestStructureVIES(TestStructure): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.env.user.company_id.vat_check_vies = True + cls._vies_check_func = stdnum.eu.vat.check_vies |
