Autocomplete party from European VAT number

This commit is contained in:
Cédric Krier
2023-09-30 16:14:33 +02:00
parent 4bdad83247
commit 6714a94b28
3 changed files with 186 additions and 0 deletions
+1
View File
@@ -1,3 +1,4 @@
* Autocomplete party from European VAT number
* Use vat alias for party identifier
* Add Belgian BIS Number identifier
* Add Belgian National Number identifier
+86
View File
@@ -1,10 +1,14 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
import logging
import stdnum.exceptions
from sql import Column, Literal
from sql.aggregate import Min
from sql.functions import CharLength
from stdnum import get_cc_module
from stdnum.eu.vat import MEMBER_STATES as EU_MEMBER_STATES
from trytond.i18n import gettext
from trytond.model import (
@@ -21,6 +25,8 @@ from .contact_mechanism import _PHONE_TYPES, _ContactMechanismMixin
from .exceptions import (
EraseError, InvalidIdentifierCode, SimilarityWarning, VIESUnavailable)
logger = logging.getLogger(__name__)
class Party(
DeactivableMixin, _ContactMechanismMixin, ModelSQL, ModelView,
@@ -322,6 +328,86 @@ class Party(
return address
return default_address
@classmethod
def autocomplete(cls, text, domain=None, limit=None, order=None):
pool = Pool()
Configuration = pool.get('party.configuration')
configuration = Configuration(1)
result = super().autocomplete(
text, domain=domain, limit=limit, order=order)
identifier_types = dict(configuration.get_identifier_types())
def add(type, code):
result.append({
'id': None,
'name': code,
'defaults': {
'identifiers': [{
'type': type,
'code': code,
}],
},
})
eu_vat = get_cc_module('eu', 'vat')
if 'eu_vat' in identifier_types and eu_vat.is_valid(text):
code = eu_vat.compact(text)
add('eu_vat', code)
else:
for country in eu_vat.guess_country(text):
if 'eu_vat' in identifier_types:
code = eu_vat.compact(country + text)
add('eu_vat', code)
elif f'{country}_vat' in identifier_types:
vat = get_cc_module(country, 'vat')
code = vat.compact(text)
add(f'{country}_vat', code)
return result
@classmethod
def default_get(cls, fields_names, with_rec_name=True):
pool = Pool()
Country = pool.get('country.country')
values = super().default_get(fields_names, with_rec_name=with_rec_name)
eu_vat = get_cc_module('eu', 'vat')
eu_types = {'eu_vat'} | {f'{m}_vat' for m in EU_MEMBER_STATES}
for identifier in values.get('identifiers') or []:
if identifier.get('type') in eu_types and identifier.get('code'):
code = identifier['code']
if identifier['type'] != 'eu_vat':
code = identifier['type'][2:] + code
try:
result = eu_vat.check_vies(code)
except Exception:
print('exception', code)
logger.debug(
f"Fail to check {identifier['code']}", exc_info=True)
continue
if result['valid']:
values['name'] = result['name']
for address in values.get('addresses') or []:
if not address:
break
else:
address = {}
values.setdefault('addresses', []).insert(0, address)
address['street'] = result['address']
country_code = code[:2]
countries = Country.search([
('code', 'ilike', country_code),
], limit=1)
if countries:
country, = countries
address['country'] = country.id
if with_rec_name:
address.setdefault('country.', {})['rec_name'] = (
country.rec_name)
break
return values
class PartyLang(ModelSQL, ValueMixin):
"Party Lang"
+99
View File
@@ -1,6 +1,9 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
import unittest
from unittest.mock import patch
from stdnum import get_cc_module
try:
import phonenumbers
@@ -100,6 +103,102 @@ class PartyTestCase(PartyCheckEraseMixin, ModuleTestCase):
'code': code,
})
@with_transaction()
def test_party_autocomplete_eu_vat(self):
"Test party autocomplete eu_vat"
pool = Pool()
Party = pool.get('party.party')
self.assertEqual(
Party.autocomplete('BE500923836'), [{
'id': None,
'name': 'BE0500923836',
'defaults': {
'identifiers': [{
'type': 'eu_vat',
'code': 'BE0500923836',
}],
},
}])
@with_transaction()
def test_party_autocomplete_eu_vat_without_country(self):
"Test party autocomplete eu_vat without country"
pool = Pool()
Party = pool.get('party.party')
self.assertIn({
'id': None,
'name': 'BE0500923836',
'defaults': {
'identifiers': [{
'type': 'eu_vat',
'code': 'BE0500923836',
}],
},
},
Party.autocomplete('500923836'))
@with_transaction()
def test_party_autocomplete_be_vat(self):
"Test party autocomplete be_vat"
pool = Pool()
Party = pool.get('party.party')
Configuration = pool.get('party.configuration')
configuration = Configuration(1)
configuration.identifier_types = ['be_vat']
configuration.save()
self.assertEqual(
Party.autocomplete('BE500923836'), [{
'id': None,
'name': '0500923836',
'defaults': {
'identifiers': [{
'type': 'be_vat',
'code': '0500923836',
}],
},
}])
@with_transaction()
def test_party_default_get_eu_vat(self):
"Test party default_get eu_vat"
pool = Pool()
Party = pool.get('party.party')
Country = pool.get('country.country')
belgium = Country(code='BE', name="Belgium")
belgium.save()
eu_vat = get_cc_module('eu', 'vat')
with patch.object(eu_vat, 'check_vies') as check_vies:
check_vies.return_value = {
'valid': True,
'name': "Tryton Foundation",
'address': "Street",
}
with Transaction().set_context(default_identifiers=[{
'type': 'eu_vat',
'code': 'BE0500923836',
}]):
self.assertEqual(
Party.default_get(['name', 'addresses', 'identifiers']), {
'name': "Tryton Foundation",
'addresses': [{
'street': "Street",
'country': belgium.id,
'country.': {
'rec_name': "🇧🇪 Belgium",
},
}],
'identifiers': [{
'type': 'eu_vat',
'code': 'BE0500923836',
}],
})
@with_transaction()
def test_address(self):
'Create address'