Add timestamp sequence

This commit is contained in:
Cédric Krier committed 2010-09-02 17:31:51 +02:00
1 parent e147a1cc2e
commit 56e8ebe5d0
7 files changed
+261 -27

No files matched your search

+1
View File
@@ -1,3 +1,4 @@
* Add timestamp sequence
* New transaction management
* Make _timestamp numeric instead of datetime to work with XML-RPC and JSON-RPC
* Add ir.trigger to trigger ModelStorage change
+92 -20
View File
@@ -3,9 +3,10 @@
from __future__ import with_statement
from string import Template
import datetime
import time
from trytond.model import ModelView, ModelSQL, fields
from trytond.tools import datetime_strftime
from trytond.pyson import In, Eval
from trytond.pyson import In, Eval, Not
from trytond.transaction import Transaction
@@ -24,30 +25,64 @@ class Sequence(ModelSQL, ModelView):
_name = 'ir.sequence'
_description = __doc__
name = fields.Char('Sequence Name', required=True, translate=True)
code = fields.Selection('code_get', 'Sequence Type', required=True,
code = fields.Selection('code_get', 'Sequence Code', required=True,
states={
'readonly': In('code', Eval('context', {})),
})
active = fields.Boolean('Active')
prefix = fields.Char('Prefix')
suffix = fields.Char('Suffix')
number_next = fields.Integer('Next Number')
number_increment = fields.Integer('Increment Number')
padding = fields.Integer('Number padding')
type = fields.Selection([
('incremental', 'Incremental'),
('decimal timestamp', 'Decimal Timestamp'),
('hexadecimal timestamp', 'Hexadecimal Timestamp'),
], 'Type')
number_next = fields.Integer('Next Number',
states={
'invisible': Not(In(Eval('type'), ['incremental'])),
}, depends=['type'])
number_increment = fields.Integer('Increment Number',
states={
'invisible': Not(In(Eval('type'), ['incremental'])),
}, depends=['type'])
padding = fields.Integer('Number padding',
states={
'invisible': Not(In(Eval('type'), ['incremental'])),
}, depends=['type'])
timestamp_rounding = fields.Integer('Timestamp Rounding', required=True,
states={
'invisible': Not(In(Eval('type'),
['decimal timestamp', 'hexadecimal timestamp'])),
}, depends=['type'])
timestamp_offset = fields.Float('Timestamp Offset',
states={
'invisible': Not(In(Eval('type'),
['decimal timestamp', 'hexadecimal timestamp'])),
}, depends=['type'])
last_timestamp = fields.Integer('Last Timestamp',
states={
'invisible': Not(In(Eval('type'),
['decimal timestamp', 'hexadecimal timestamp'])),
}, depends=['type'])
def __init__(self):
super(Sequence, self).__init__()
self._constraints += [
('check_prefix_suffix', 'invalid_prefix_suffix'),
('check_last_timestamp', 'future_last_timestamp'),
]
self._error_messages.update({
'missing': 'Missing sequence!',
'invalid_prefix_suffix': 'Invalid prefix/suffix!',
'future_last_timestamp': 'Last Timestamp could not be in future!',
})
def default_active(self):
return True
def default_type(self):
return 'incremental'
def default_number_increment(self):
return 1
@@ -57,6 +92,15 @@ class Sequence(ModelSQL, ModelView):
def default_padding(self):
return 0
def default_timestamp_rounding(self):
return 1.0
def default_timestamp_offset(self):
return 946681200.0 # Offset for 2000-01-01
def default_last_timestamp(self):
return 0.0
def default_code(self):
return Transaction().context.get('code', False)
@@ -77,6 +121,15 @@ class Sequence(ModelSQL, ModelView):
return False
return True
def check_last_timestamp(self, ids):
"Check last_timestamp"
for sequence in self.browse(ids):
next_timestamp = self._timestamp(sequence)
if sequence.last_timestamp > next_timestamp:
return False
return True
def _process(self, string, date=None):
date_obj = self.pool.get('ir.date')
if not date:
@@ -90,6 +143,35 @@ class Sequence(ModelSQL, ModelView):
day=day,
)
def _timestamp(self, sequence):
return int((time.time() - sequence.timestamp_offset)
/ sequence.timestamp_rounding)
def _get_sequence(self, sequence):
if sequence.type == 'incremental':
#Pre-fetch number_next
number_next = sequence.number_next or 0
with Transaction().set_user(0):
self.write(sequence.id, {
'number_next': number_next + sequence.number_increment,
})
return '%%0%sd' % sequence.padding % number_next
elif sequence.type in ('decimal timestamp', 'hexadecimal timestamp'):
timestamp = sequence.last_timestamp
while timestamp == sequence.last_timestamp:
timestamp = self._timestamp(sequence)
with Transaction().set_user(0):
self.write(sequence.id, {
'last_timestamp': timestamp,
})
if sequence.type == 'decimal timestamp':
return '%d' % timestamp
else:
return hex(timestamp)[2:].upper()
return ''
def get_id(self, domain):
'''
Return sequence value for the domain
@@ -108,21 +190,11 @@ class Sequence(ModelSQL, ModelView):
if sequence_ids:
with Transaction().set_user(0):
sequence = self.browse(sequence_ids[0])
#Pre-fetch number_next
number_next = sequence.number_next
self.write(sequence.id, {
'number_next': (number_next +
sequence.number_increment),
})
if number_next:
return (self._process(sequence.prefix, date=date) +
'%%0%sd' % sequence.padding % number_next +
self._process(sequence.suffix, date=date))
else:
return (self._process(sequence.prefix, date=date) +
self._process(sequence.suffix, date=date))
return '%s%s%s' % (
self._process(sequence.prefix, date=date),
self._get_sequence(sequence),
self._process(sequence.suffix, date=date),
)
self.raise_user_error('missing')
def get(self, code):
+21 -7
View File
@@ -22,12 +22,25 @@ this repository contains the full copyright notices and license terms. -->
<field name="prefix"/>
<label name="suffix"/>
<field name="suffix"/>
<label name="padding"/>
<field name="padding" />
<label name="number_increment"/>
<field name="number_increment"/>
<label name="number_next"/>
<field name="number_next"/>
<label name="type"/>
<field name="type"/>
<group string="Incremental" id="incremental" colspan="4"
states="{'invisible': Not(In(Eval('type'), ['incremental']))}">
<label name="padding"/>
<field name="padding" />
<label name="number_increment"/>
<field name="number_increment"/>
<label name="number_next"/>
<field name="number_next"/>
</group>
<group string="Timestamp" id="timestamp" colspan="4"
states="{'invisible': Not(In(Eval('type'), ['decimal timestamp', 'hexadecimal timestamp']))}">
<label name="timestamp_rounding"/>
<field name="timestamp_rounding"/>
<label name="timestamp_offset"/>
<field name="timestamp_offset"/>
<field name="last_timestamp"/>
</group>
<separator string="Legend (Placeholders for prefix, suffix)" colspan="4"
id="legend"/>
<label string="Year:" id="label_year"/>
@@ -48,7 +61,8 @@ this repository contains the full copyright notices and license terms. -->
<tree string="Sequences">
<field name="name" select="1"/>
<field name="code" select="1"/>
<field name="active" select="1"/>
<field name="type" select="1"/>
<field name="active" select="2"/>
</tree>
]]>
</field>
+1
View File
@@ -8,6 +8,7 @@
'depends': ['ir', 'res'],
'xml': [
'import_data.xml',
'sequence.xml',
],
'translation': [
],
+14
View File
@@ -0,0 +1,14 @@
<?xml version="1.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. -->
<tryton>
<data>
<record model="ir.sequence.type" id="sequence_type_test">
<field name="name">Test</field>
<field name="code">test</field>
<field name="groups" eval="[('add', ref('res.group_admin'))]"/>
</record>
</data>
</tryton>
+130
View File
@@ -0,0 +1,130 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#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 __future__ import with_statement
import unittest
import datetime
from trytond.tests.test_tryton import POOL, DB_NAME, USER, CONTEXT, \
install_module
from trytond.transaction import Transaction
class SequenceTestCase(unittest.TestCase):
'''
Test Sequence
'''
def setUp(self):
install_module('test')
self.sequence = POOL.get('ir.sequence')
def test0010incremental(self):
'''
Test incremental
'''
with Transaction().start(DB_NAME, USER, CONTEXT) as transaction:
sequence_id = self.sequence.create({
'name': 'Test incremental',
'code': 'test',
'prefix': '',
'suffix': '',
'type': 'incremental',
})
self.assertEqual(self.sequence.get_id(sequence_id), '1')
self.sequence.write(sequence_id, {
'number_increment': 10,
})
self.assertEqual(self.sequence.get_id(sequence_id), '2')
self.assertEqual(self.sequence.get_id(sequence_id), '12')
self.sequence.write(sequence_id, {
'padding': 3,
})
self.assertEqual(self.sequence.get_id(sequence_id), '022')
transaction.cursor.rollback()
def test0020decimal_timestamp(self):
'''
Test Decimal Timestamp
'''
with Transaction().start(DB_NAME, USER, CONTEXT) as transaction:
sequence_id = self.sequence.create({
'name': 'Test decimal timestamp',
'code': 'test',
'prefix': '',
'suffix': '',
'type': 'decimal timestamp',
})
timestamp = self.sequence.get_id(sequence_id)
sequence = self.sequence.read(sequence_id, ['last_timestamp'])
self.assertEqual(timestamp, str(sequence['last_timestamp']))
self.assertNotEqual(self.sequence.get_id(sequence_id), timestamp)
sequence = self.sequence.browse(sequence_id)
next_timestamp = self.sequence._timestamp(sequence)
self.assertRaises(Exception, self.sequence.write, sequence_id, {
'last_timestamp': next_timestamp + 100,
})
transaction.cursor.rollback()
def test0030hexadecimal_timestamp(self):
'''
Test Hexadecimal Timestamp
'''
with Transaction().start(DB_NAME, USER, CONTEXT) as transaction:
sequence_id = self.sequence.create({
'name': 'Test hexadecimal timestamp',
'code': 'test',
'prefix': '',
'suffix': '',
'type': 'hexadecimal timestamp',
})
timestamp = self.sequence.get_id(sequence_id)
sequence = self.sequence.read(sequence_id, ['last_timestamp'])
self.assertEqual(timestamp, hex(sequence['last_timestamp'])[2:].upper())
self.assertNotEqual(self.sequence.get_id(sequence_id), timestamp)
sequence = self.sequence.browse(sequence_id)
next_timestamp = self.sequence._timestamp(sequence)
self.assertRaises(Exception, self.sequence.write, sequence_id, {
'last_timestamp': next_timestamp + 100,
})
transaction.cursor.rollback()
def test0040prefix_suffix(self):
'''
Test prefix/suffix
'''
with Transaction().start(DB_NAME, USER, CONTEXT) as transaction:
sequence_id = self.sequence.create({
'name': 'Test incremental',
'code': 'test',
'prefix': 'prefix/',
'suffix': '/suffix',
'type': 'incremental',
})
self.assertEqual(self.sequence.get_id(sequence_id),
'prefix/1/suffix')
self.sequence.write(sequence_id, {
'prefix': '${year}-${month}-${day}/',
'suffix': '/${day}.${month}.${year}',
})
context = CONTEXT.copy()
with Transaction().set_context(date=datetime.date(2010, 8, 15)):
self.assertEqual(self.sequence.get_id(sequence_id),
'2010-08-15/2/15.08.2010')
def suite():
return unittest.TestLoader().loadTestsFromTestCase(SequenceTestCase)
if __name__ == '__main__':
suite = suite()
unittest.TextTestRunner(verbosity=2).run(suite)
+2
View File
@@ -184,6 +184,8 @@ def all_suite():
suite_.addTests(test_trigger.suite())
import trytond.tests.test_protocols_datatype as test_protocols_datatype
suite_.addTests(test_protocols_datatype.suite())
import trytond.tests.test_sequence as test_sequence
suite_.addTests(test_sequence.suite())
return suite_
def modules_suite():