diff --git a/CHANGELOG b/CHANGELOG
index 533c5d2097..77736ea900 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,4 @@
+* Use party identifier to store SIREN and SIRET
* Add support for Python 3.10
* Remove support for Python 3.6
diff --git a/address.py b/address.py
index bc29fc1e74..9fbcc82442 100644
--- a/address.py
+++ b/address.py
@@ -1,37 +1,87 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
-from trytond.i18n import gettext
-from trytond.model import fields
-from trytond.pool import PoolMeta
+from sql import Literal, Null
+from sql.operators import Concat
-from . import luhn
-from .exceptions import SIRETValidationError
+from trytond.model import fields
+from trytond.pool import Pool, PoolMeta
+from trytond.transaction import Transaction
class Address(metaclass=PoolMeta):
__name__ = 'party.address'
- siret_nic = fields.Char("SIRET NIC", select=True, size=5)
- siret = fields.Function(fields.Char('SIRET'), 'get_siret')
-
- def get_siret(self, name):
- if self.party.siren and self.siret_nic:
- return self.party.siren + self.siret_nic
+ siret = fields.Function(fields.Many2One(
+ 'party.identifier', "SIRET"),
+ 'get_siret', searcher='search_siret')
@classmethod
- def validate(cls, addresses):
- super(Address, cls).validate(addresses)
- for address in addresses:
- address.check_siret()
+ def __register__(cls, module):
+ pool = Pool()
+ Party = pool.get('party.party')
+ Identifier = pool.get('party.identifier')
+ cursor = Transaction().connection.cursor()
+ party = Party.__table__()
+ address = cls.__table__()
+ identifier = Identifier.__table__()
- def check_siret(self):
- '''
- Check validity of SIRET
- '''
- if self.siret:
- if (len(self.siret) != 14
- or not luhn.validate(self.siret)):
- raise SIRETValidationError(
- gettext('party_siret.msg_invalid_siret',
- number=self.siret,
- address=self.rec_name))
+ super().__register__(module)
+
+ table_h = cls.__table_handler__(module)
+ party_h = Party.__table_handler__(module)
+
+ # Migrate from 6.2: replace siren and siret by identifier
+ if party_h.column_exist('siren'):
+ cursor.execute(*identifier.insert(
+ [identifier.party,
+ identifier.type, identifier.code,
+ identifier.active],
+ party.select(
+ party.id, Literal('fr_siren'),
+ party.siren, party.active,
+ where=(party.siren != Null)
+ & (party.siren != ''))))
+ if table_h.column_exist('siret_nic'):
+ cursor.execute(*identifier.insert(
+ [identifier.party, identifier.address,
+ identifier.type, identifier.code,
+ identifier.active],
+ address.join(
+ party, condition=address.party == party.id
+ ).select(
+ address.party, address.id,
+ Literal('fr_siret'),
+ Concat(party.siren, address.siret_nic),
+ address.active,
+ where=(address.siret_nic != Null)
+ & (address.siret_nic != '')
+ & (party.siren != Null)
+ & (party.siren != ''))))
+ table_h.drop_column('siret_nic')
+ party_h.drop_column('siren')
+
+ def get_siret(self, name):
+ for identifier in self.identifiers:
+ if identifier.type == 'fr_siret':
+ return identifier.id
+
+ @classmethod
+ def search_siret(cls, name, clause):
+ _, operator, value = clause
+ domain = [
+ ('identifiers', 'where', [
+ ('code', operator, value),
+ ('type', 'in', 'fr_siren'),
+ ]),
+ ]
+ # Add party without tax identifier
+ if ((operator == '=' and value is None)
+ or (operator == 'in' and None in value)):
+ domain = ['OR',
+ domain, [
+ ('identifiers', 'not where', [
+ ('type', '=', 'fr_siren'),
+ ]),
+ ],
+ ]
+ return domain
diff --git a/address.xml b/address.xml
index 1e8b443cbd..b3b9835b1e 100644
--- a/address.xml
+++ b/address.xml
@@ -8,10 +8,5 @@ this repository contains the full copyright notices and license terms. -->
address_tree
-
- party.address
-
- address_form
-
diff --git a/exceptions.py b/exceptions.py
deleted file mode 100644
index 429ed4d2d8..0000000000
--- a/exceptions.py
+++ /dev/null
@@ -1,12 +0,0 @@
-# This file is part of Tryton. The COPYRIGHT file at the top level of
-# this repository contains the full copyright notices and license terms.
-
-from trytond.model.exceptions import ValidationError
-
-
-class SIRENValidationError(ValidationError):
- pass
-
-
-class SIRETValidationError(SIRENValidationError):
- pass
diff --git a/luhn.py b/luhn.py
deleted file mode 100644
index e48e2da092..0000000000
--- a/luhn.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# This file is part of Tryton. The COPYRIGHT file at the top level of
-# this repository contains the full copyright notices and license terms.
-
-
-def validate(value):
- """
- Validate value with Luhn algorithm
- :param value: the value
- :return: a boolean
- """
- if not isinstance(value, str):
- value = str(value)
- try:
- evens = sum(int(x) for x in value[-1::-2])
- odds = sum(sum(divmod(int(x) * 2, 10)) for x in value[-2::-2])
- return (evens + odds) % 10 == 0
- except ValueError:
- return False
diff --git a/message.xml b/message.xml
deleted file mode 100644
index 623e858850..0000000000
--- a/message.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
-
-
- The SIREN number "%(number)s" for party "%(party)s" is not valid.
-
-
- The SIRET number "%(number)s" for address "%(address)s" is not valid.
-
-
-
diff --git a/party.py b/party.py
index 1b358d75cb..856c9cde03 100644
--- a/party.py
+++ b/party.py
@@ -1,31 +1,38 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
-from trytond.i18n import gettext
from trytond.model import fields
from trytond.pool import PoolMeta
-from . import luhn
-from .exceptions import SIRENValidationError
-
class Party(metaclass=PoolMeta):
__name__ = 'party.party'
- siren = fields.Char("SIREN", select=True, size=9)
+ siren = fields.Function(fields.Many2One(
+ 'party.identifier', "SIREN"),
+ 'get_siren', searcher='search_siren')
+
+ def get_siren(self, name):
+ for identifier in self.identifiers:
+ if identifier.type == 'fr_siren':
+ return identifier.id
@classmethod
- def validate(cls, parties):
- super(Party, cls).validate(parties)
- for party in parties:
- party.check_siren()
-
- def check_siren(self):
- '''
- Check validity of SIREN
- '''
- if self.siren:
- if len(self.siren) != 9 or not luhn.validate(self.siren):
- raise SIRENValidationError(
- gettext('party_siret.msg_invalid_siren',
- number=self.siren,
- party=self.rec_name))
+ def search_siren(cls, name, clause):
+ _, operator, value = clause
+ domain = [
+ ('identifiers', 'where', [
+ ('code', operator, value),
+ ('type', 'in', 'fr_siren'),
+ ]),
+ ]
+ # Add party without tax identifier
+ if ((operator == '=' and value is None)
+ or (operator == 'in' and None in value)):
+ domain = ['OR',
+ domain, [
+ ('identifiers', 'not where', [
+ ('type', '=', 'fr_siren'),
+ ]),
+ ],
+ ]
+ return domain
diff --git a/party.xml b/party.xml
index 85f307213f..7e887ad828 100644
--- a/party.xml
+++ b/party.xml
@@ -8,10 +8,5 @@ this repository contains the full copyright notices and license terms. -->
party_tree
-
- party.party
-
- party_form
-
diff --git a/tests/test_party_siret.py b/tests/test_party_siret.py
index ec83e8fc66..10904868c7 100644
--- a/tests/test_party_siret.py
+++ b/tests/test_party_siret.py
@@ -3,7 +3,6 @@
import unittest
import trytond.tests.test_tryton
-from trytond.modules.party_siret import luhn
from trytond.tests.test_tryton import ModuleTestCase
@@ -11,32 +10,6 @@ class PartySiretTestCase(ModuleTestCase):
'Test PartySiret module'
module = 'party_siret'
- def test_luhn(self):
- 'Test luhn'
- values = (
- (4111111111111111, True),
- ('4111111111111111', True),
- (4222222222222, True),
- (378734493671000, True),
- (5424000000000015, True),
- (5555555555554444, True),
- (1008, True),
- ('0000001008', True),
- ('000000001008', True),
- (4012888888881881, True),
- (1234567890123456789012345678909, True),
- (4111111111211111, False),
- (42222222222224, False),
- (100, False),
- ('100', False),
- ('0000100', False),
- ('abc', False),
- (None, False),
- (object(), False),
- )
- for value, test in values:
- self.assertTrue(luhn.validate(value) == test)
-
def suite():
suite = trytond.tests.test_tryton.suite()
diff --git a/tryton.cfg b/tryton.cfg
index b806971089..9e6cae02bb 100644
--- a/tryton.cfg
+++ b/tryton.cfg
@@ -6,4 +6,3 @@ depends:
xml:
party.xml
address.xml
- message.xml
diff --git a/view/address_form.xml b/view/address_form.xml
deleted file mode 100644
index b07347e583..0000000000
--- a/view/address_form.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/view/party_form.xml b/view/party_form.xml
deleted file mode 100644
index 142bd1fa7b..0000000000
--- a/view/party_form.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
-
-
-
-
-
-