diff --git a/CHANGELOG b/CHANGELOG
index bb6aff549b..89c1f10b7c 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,5 @@
+* Use instance method to get next sequence value
+* Store the sequence type directly on sequence
* Allow sending email with record's attachments
* Add which records to use for actions
* Add parent to group to inherit accesses
diff --git a/trytond/ir/message.xml b/trytond/ir/message.xml
index 90d9f935a9..1c4150a370 100644
--- a/trytond/ir/message.xml
+++ b/trytond/ir/message.xml
@@ -264,6 +264,9 @@ this repository contains the full copyright notices and license terms. -->
The "Last Timestamp" cannot be in the future for sequence "%s".
+
+ You cannot change the sequence type of a sequence instead create a new sequence.
+
Invalid grouping "%(grouping)s" for language "%(language)s".
diff --git a/trytond/ir/sequence.py b/trytond/ir/sequence.py
index 1effc28199..d5e814e571 100644
--- a/trytond/ir/sequence.py
+++ b/trytond/ir/sequence.py
@@ -9,7 +9,7 @@ from trytond import backend
from trytond.exceptions import UserError
from trytond.i18n import gettext
from trytond.model import ModelView, ModelSQL, DeactivableMixin, fields, Check
-from trytond.model.exceptions import ValidationError
+from trytond.model.exceptions import ValidationError, AccessError
from trytond.pool import Pool
from trytond.pyson import Eval, And
from trytond.transaction import Transaction
@@ -34,7 +34,15 @@ class SequenceType(ModelSQL, ModelView):
__name__ = 'ir.sequence.type'
name = fields.Char('Sequence Name', required=True, translate=True)
- code = fields.Char('Sequence Code', required=True)
+
+ @classmethod
+ def __register__(cls, module):
+ super().__register__(module)
+ table_h = cls.__table_handler__(module)
+
+ # Migration from 5.8: remove code
+ # We keep the column until ir.sequence has been migrated
+ table_h.not_null_action('code', action='remove')
class Sequence(DeactivableMixin, ModelSQL, ModelView):
@@ -43,10 +51,13 @@ class Sequence(DeactivableMixin, ModelSQL, ModelView):
_strict = False
name = fields.Char('Sequence Name', required=True, translate=True)
- code = fields.Selection('code_get', 'Sequence Code', required=True,
+ sequence_type = fields.Many2One(
+ 'ir.sequence.type', "Sequence Type",
+ required=True, ondelete='RESTRICT',
states={
- 'readonly': Eval('context', {}).contains('code'),
- })
+ 'readonly': Eval('id', -1) >= 0,
+ },
+ depends=['id'])
prefix = fields.Char('Prefix')
suffix = fields.Char('Suffix')
type = fields.Selection([
@@ -100,6 +111,29 @@ class Sequence(DeactivableMixin, ModelSQL, ModelView):
'Timestamp rounding should be greater than 0'),
]
+ @classmethod
+ def __register__(cls, module):
+ pool = Pool()
+ SequenceType = pool.get('ir.sequence.type')
+ cursor = Transaction().connection.cursor()
+ table = cls.__table__()
+ sequence_type = SequenceType.__table__()
+
+ super().__register__(module)
+
+ table_h = cls.__table_handler__(module)
+
+ # Migration from 5.8: replace code by sequence_type
+ if table_h.column_exist('code'):
+ cursor.execute(*table.update(
+ [table.sequence_type],
+ sequence_type.select(
+ sequence_type.id,
+ where=sequence_type.code == table.code)))
+ table_h.drop_column('code')
+ sequence_type_h = SequenceType.__table_handler__(module)
+ sequence_type_h.drop_column('code')
+
@staticmethod
def default_type():
return 'incremental'
@@ -128,10 +162,6 @@ class Sequence(DeactivableMixin, ModelSQL, ModelView):
def default_last_timestamp():
return 0
- @staticmethod
- def default_code():
- return Transaction().context.get('code')
-
def get_number_next(self, name):
if self.type != 'incremental':
return
@@ -171,10 +201,17 @@ class Sequence(DeactivableMixin, ModelSQL, ModelView):
return sequences
@classmethod
- def write(cls, sequences, values, *args):
- super(Sequence, cls).write(sequences, values, *args)
+ def write(cls, *args):
+ transaction = Transaction()
+ if (transaction.user != 0
+ and transaction.context.get('_check_access')):
+ for values in args[1::2]:
+ if 'sequence_type' in values:
+ raise AccessError(gettext(
+ 'ir.msg_sequence_change_sequence_type'))
+ super().write(*args)
if sql_sequence and not cls._strict:
- actions = iter((sequences, values) + args)
+ actions = iter(args)
for sequences, values in zip(actions, actions):
for sequence in sequences:
sequence.update_sql_sequence(values.get('number_next'))
@@ -186,13 +223,6 @@ class Sequence(DeactivableMixin, ModelSQL, ModelView):
sequence.delete_sql_sequence()
return super(Sequence, cls).delete(sequences)
- @classmethod
- def code_get(cls):
- pool = Pool()
- SequenceType = pool.get('ir.sequence.type')
- sequence_types = SequenceType.search([])
- return [(x.code, x.name) for x in sequence_types]
-
@classmethod
def validate(cls, sequences):
super(Sequence, cls).validate(sequences)
@@ -323,21 +353,16 @@ class Sequence(DeactivableMixin, ModelSQL, ModelView):
return hex(timestamp)[2:].upper()
return ''
- @classmethod
- def get_id(cls, domain, _lock=False):
+ def get(self, _lock=False):
'''
- Return sequence value for the domain
+ Return the next sequence value
'''
- if isinstance(domain, cls):
- domain = domain.id
- if isinstance(domain, int):
- domain = [('id', '=', domain)]
-
+ cls = self.__class__
# bypass rules on sequences
with Transaction().set_context(user=False, _check_access=False):
with Transaction().set_user(0):
try:
- sequence, = cls.search(domain, limit=1)
+ sequence = cls(self.id)
except TypeError:
raise MissingError(gettext('ir.msg_sequence_missing'))
if _lock:
@@ -360,10 +385,6 @@ class Sequence(DeactivableMixin, ModelSQL, ModelView):
cls._process(sequence.suffix, date=date),
)
- @classmethod
- def get(cls, code):
- return cls.get_id([('code', '=', code)])
-
class SequenceStrict(Sequence):
"Sequence Strict"
@@ -371,6 +392,5 @@ class SequenceStrict(Sequence):
_table = None # Needed to reset Sequence._table
_strict = True
- @classmethod
- def get_id(cls, clause):
- return super(SequenceStrict, cls).get_id(clause, _lock=True)
+ def get(self, _lock=True):
+ return super().get(_lock=True)
diff --git a/trytond/ir/view/sequence_form.xml b/trytond/ir/view/sequence_form.xml
index 563d28ffb9..4dabddd7fd 100644
--- a/trytond/ir/view/sequence_form.xml
+++ b/trytond/ir/view/sequence_form.xml
@@ -4,8 +4,8 @@ this repository contains the full copyright notices and license terms. -->
diff --git a/trytond/ir/view/sequence_type_list.xml b/trytond/ir/view/sequence_type_list.xml
index 68eab48f87..2e8e05081d 100644
--- a/trytond/ir/view/sequence_type_list.xml
+++ b/trytond/ir/view/sequence_type_list.xml
@@ -3,5 +3,4 @@
this repository contains the full copyright notices and license terms. -->
-
diff --git a/trytond/res/__init__.py b/trytond/res/__init__.py
index d355764459..304378cb9e 100644
--- a/trytond/res/__init__.py
+++ b/trytond/res/__init__.py
@@ -36,8 +36,6 @@ def register():
ir.Lang,
ir.SequenceType,
ir.SequenceTypeGroup,
- ir.Sequence,
- ir.SequenceStrict,
ir.ModuleConfigWizardItem,
ir.Export,
ir.Export_Group,
diff --git a/trytond/res/ir.py b/trytond/res/ir.py
index 93afe9fad0..03a431ef8a 100644
--- a/trytond/res/ir.py
+++ b/trytond/res/ir.py
@@ -224,44 +224,6 @@ class SequenceTypeGroup(ModelSQL):
Rule._domain_get_cache.clear()
-class Sequence(metaclass=PoolMeta):
- __name__ = 'ir.sequence'
- groups = fields.Function(fields.Many2Many('res.group', None, None,
- 'User Groups'), 'get_groups', searcher='search_groups')
-
- @classmethod
- def get_groups(cls, sequences, name):
- SequenceType = Pool().get('ir.sequence.type')
- code2seq = {}
- for sequence in sequences:
- code2seq.setdefault(sequence.code, []).append(sequence.id)
-
- sequence_types = SequenceType.search([
- ('code', 'in', list(code2seq.keys())),
- ])
-
- groups = {}
- for sequence_type in sequence_types:
- seq_ids = code2seq[sequence_type.code]
- for seq_id in seq_ids:
- groups.setdefault(seq_id, []).append(sequence_type.id)
-
- return groups
-
- @staticmethod
- def search_groups(name, clause):
- SequenceType = Pool().get('ir.sequence.type')
- seq_types = SequenceType.search([clause], order=[])
- codes = set(st.code for st in seq_types)
- return [('code', 'in', list(codes))]
-
-
-class SequenceStrict(Sequence):
- # This empty class declaration is needed to inherit the groups
- # field
- __name__ = 'ir.sequence.strict'
-
-
class ModuleConfigWizardItem(metaclass=PoolMeta):
__name__ = 'ir.module.config_wizard.item'
diff --git a/trytond/res/ir.xml b/trytond/res/ir.xml
index c1a1ce6006..3edff59623 100644
--- a/trytond/res/ir.xml
+++ b/trytond/res/ir.xml
@@ -778,7 +778,7 @@ this repository contains the full copyright notices and license terms. -->
@@ -791,7 +791,7 @@ this repository contains the full copyright notices and license terms. -->
diff --git a/trytond/res/view/sequence_type_form.xml b/trytond/res/view/sequence_type_form.xml
index 4a880ec0f1..1f56effbf5 100644
--- a/trytond/res/view/sequence_type_form.xml
+++ b/trytond/res/view/sequence_type_form.xml
@@ -2,9 +2,7 @@
-
+
diff --git a/trytond/tests/sequence.xml b/trytond/tests/sequence.xml
index ef45126087..4222916e75 100644
--- a/trytond/tests/sequence.xml
+++ b/trytond/tests/sequence.xml
@@ -6,7 +6,6 @@ this repository contains the full copyright notices and license terms. -->
Test
- test
diff --git a/trytond/tests/test_ir.py b/trytond/tests/test_ir.py
index 9c51112a75..a5add9d0e2 100644
--- a/trytond/tests/test_ir.py
+++ b/trytond/tests/test_ir.py
@@ -31,20 +31,18 @@ class IrTestCase(ModuleTestCase):
except KeyError:
groups = []
- sequence_type = SequenceType(name='Test', code='test', groups=groups)
+ sequence_type = SequenceType(name='Test', groups=groups)
sequence_type.save()
- sequence = Sequence(name='Test Sequence', code='test')
+ sequence = Sequence(name='Test Sequence', sequence_type=sequence_type)
sequence.save()
- self.assertEqual(Sequence.get_id(sequence.id), '1')
+ self.assertEqual(sequence.get(), '1')
today = Date.today()
sequence.prefix = '${year}'
sequence.save()
- self.assertEqual(Sequence.get_id(sequence.id),
- '%s2' % str(today.year))
+ self.assertEqual(sequence.get(), '%s2' % str(today.year))
next_year = today + relativedelta(years=1)
with Transaction().set_context(date=next_year):
- self.assertEqual(Sequence.get_id(sequence.id),
- '%s3' % str(next_year.year))
+ self.assertEqual(sequence.get(), '%s3' % str(next_year.year))
@with_transaction()
def test_global_search(self):
diff --git a/trytond/tests/test_sequence.py b/trytond/tests/test_sequence.py
index 55f5bd140c..965ed873bc 100644
--- a/trytond/tests/test_sequence.py
+++ b/trytond/tests/test_sequence.py
@@ -24,49 +24,59 @@ class SequenceTestCase(unittest.TestCase):
@with_transaction()
def test_incremental(self):
'Test incremental'
+ pool = Pool()
Sequence = self.get_model()
+ SequenceType = pool.get('ir.sequence.type')
+ sequence_type, = SequenceType.search([
+ ('name', '=', "Test"),
+ ], limit=1)
sequence, = Sequence.create([{
'name': 'Test incremental',
- 'code': 'test',
+ 'sequence_type': sequence_type.id,
'prefix': '',
'suffix': '',
'type': 'incremental',
}])
self.assertEqual(sequence.number_next, 1)
- self.assertEqual(Sequence.get_id(sequence), '1')
+ self.assertEqual(sequence.get(), '1')
Sequence.write([sequence], {
'number_increment': 10,
})
self.assertEqual(sequence.number_next, 2)
- self.assertEqual(Sequence.get_id(sequence), '2')
- self.assertEqual(Sequence.get_id(sequence), '12')
+ self.assertEqual(sequence.get(), '2')
+ self.assertEqual(sequence.get(), '12')
Sequence.write([sequence], {
'padding': 3,
})
self.assertEqual(sequence.number_next, 22)
- self.assertEqual(Sequence.get_id(sequence), '022')
+ self.assertEqual(sequence.get(), '022')
@with_transaction()
def test_decimal_timestamp(self):
'Test Decimal Timestamp'
+ pool = Pool()
Sequence = self.get_model()
+ SequenceType = pool.get('ir.sequence.type')
+ sequence_type, = SequenceType.search([
+ ('name', '=', "Test"),
+ ], limit=1)
sequence, = Sequence.create([{
'name': 'Test decimal timestamp',
- 'code': 'test',
+ 'sequence_type': sequence_type.id,
'prefix': '',
'suffix': '',
'type': 'decimal timestamp',
}])
- timestamp = Sequence.get_id(sequence)
+ timestamp = sequence.get()
self.assertEqual(timestamp, str(sequence.last_timestamp))
self.assertEqual(sequence.number_next, None)
- self.assertNotEqual(Sequence.get_id(sequence), timestamp)
+ self.assertNotEqual(sequence.get(), timestamp)
next_timestamp = Sequence._timestamp(sequence)
self.assertRaises(LastTimestampError, Sequence.write, [sequence], {
@@ -76,22 +86,27 @@ class SequenceTestCase(unittest.TestCase):
@with_transaction()
def test_hexadecimal_timestamp(self):
'Test Hexadecimal Timestamp'
+ pool = Pool()
Sequence = self.get_model()
+ SequenceType = pool.get('ir.sequence.type')
+ sequence_type, = SequenceType.search([
+ ('name', '=', "Test"),
+ ], limit=1)
sequence, = Sequence.create([{
'name': 'Test hexadecimal timestamp',
- 'code': 'test',
+ 'sequence_type': sequence_type.id,
'prefix': '',
'suffix': '',
'type': 'hexadecimal timestamp',
}])
- timestamp = Sequence.get_id(sequence)
+ timestamp = sequence.get()
self.assertEqual(timestamp,
hex(int(sequence.last_timestamp))[2:].upper())
self.assertEqual(sequence.number_next, None)
- self.assertNotEqual(Sequence.get_id(sequence), timestamp)
+ self.assertNotEqual(sequence.get(), timestamp)
next_timestamp = Sequence._timestamp(sequence)
self.assertRaises(LastTimestampError, Sequence.write, [sequence], {
@@ -101,16 +116,21 @@ class SequenceTestCase(unittest.TestCase):
@with_transaction()
def test_prefix_suffix(self):
'Test prefix/suffix'
+ pool = Pool()
Sequence = self.get_model()
+ SequenceType = pool.get('ir.sequence.type')
+ sequence_type, = SequenceType.search([
+ ('name', '=', "Test"),
+ ], limit=1)
sequence, = Sequence.create([{
'name': 'Test incremental',
- 'code': 'test',
+ 'sequence_type': sequence_type.id,
'prefix': 'prefix/',
'suffix': '/suffix',
'type': 'incremental',
}])
- self.assertEqual(Sequence.get_id(sequence),
+ self.assertEqual(sequence.get(),
'prefix/1/suffix')
Sequence.write([sequence], {
@@ -118,8 +138,7 @@ class SequenceTestCase(unittest.TestCase):
'suffix': '/${day}.${month}.${year}',
})
with Transaction().set_context(date=datetime.date(2010, 8, 15)):
- self.assertEqual(Sequence.get_id(sequence),
- '2010-08-15/2/15.08.2010')
+ self.assertEqual(sequence.get(), '2010-08-15/2/15.08.2010')
class SequenceStrictTestCase(SequenceTestCase):