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. -->
@@ -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):