Merge lp:~therp-nl/therp-backports/openerp-server-6.1-lp960201_fix_sequence into lp:therp-backports/server-6.1

Proposed by Stefan Rijnhart (Opener)
Status: Merged
Merged at revision: 4300
Proposed branch: lp:~therp-nl/therp-backports/openerp-server-6.1-lp960201_fix_sequence
Merge into: lp:therp-backports/server-6.1
Diff against target: 205 lines (+105/-10)
2 files modified
openerp/addons/base/ir/ir.xml (+11/-1)
openerp/addons/base/ir/ir_sequence.py (+94/-9)
To merge this branch: bzr merge lp:~therp-nl/therp-backports/openerp-server-6.1-lp960201_fix_sequence
Reviewer Review Type Date Requested Status
Stefan Rijnhart (Opener) (community) Approve
Holger Brunn (Therp) Approve
Cristian Salamea (community) Needs Information
Review via email: mp+136993@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Cristian Salamea (ovnicraft) wrote :

Hello, there is any news about the status of this merge ?
I am testing and works ok by now, no errors and gives the correct feedback.
I want to know our opinion.

Regards,

review: Needs Information
Revision history for this message
Stefan Rijnhart (Opener) (stefan-opener) wrote :

Yes, review from me and Ronald's other colleagues is long overdue. Note that this proposal is on the therp-backports of OpenERP, not on the official branches. That proposal is here: https://code.launchpad.net/~therp-nl/openobject-server/ronald@therp.nl_fix_sequence_lp960201/+merge/136913. It has been neglected there as well, by the OpenERP developers. If you have tested the code succesfully, I would appreciate it if you submitted your approval there, using 'test' as a review type.

I'll take the opportunity to review now as well. Ronald, is there a technical reason for the whole 'prohibit' scheme, or do you really want to save junior administrators who strayed into this dark corner of the configuration from accidentally changing the next number? I think it clutters up this otherwise really good patch and I would prefer it if you removed it.

review: Needs Fixing
Revision history for this message
Ronald Portier (Therp) (rportier1962) wrote :

Stefan,

The prohibit "scheme" is not there for a technical reason, but to prevent users from shooting themselves in their foot.

There are already to many places in OpenERP were users are led to dangerous mistakes and errors, no need to add to them.

Ronald

Revision history for this message
Holger Brunn (Therp) (hbrunn) wrote :

Making it more complicated for users to screw up is a good thing in my opinion. And I like your solution.

review: Approve
Revision history for this message
Stefan Rijnhart (Opener) (stefan-opener) wrote :

I gladly bow to the majority. We need to remember this one if Therp branches get promoted to OCB/6.1 with stable/unstable split.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'openerp/addons/base/ir/ir.xml'
2--- openerp/addons/base/ir/ir.xml 2012-05-16 12:38:46 +0000
3+++ openerp/addons/base/ir/ir.xml 2012-11-29 17:02:22 +0000
4@@ -151,7 +151,17 @@
5 <field name="suffix"/>
6 <field name="padding"/>
7 <field name="number_increment"/>
8- <field name="number_next"/>
9+ <!-- If number_next_actual is 0, we have a brand new record -->
10+ <field
11+ name="prohibit_number_next_change"
12+ attrs="{'invisible':[('number_next_actual','=',0)]}" />
13+ <field name="prohibit_number_next_lower"
14+ attrs="{'invisible':[('number_next_actual','=',0)]}" />
15+ <field name="number_next_actual"
16+ attrs="{'invisible':[('number_next_actual','=',0)]}" />
17+ <field
18+ name="number_next"
19+ attrs="{'readonly':[('prohibit_number_next_change','=',True),('number_next_actual','&gt;',0)]}" />
20 <field name="implementation"/>
21 <separator colspan="4" string="Legend (for prefix, suffix)"/>
22 <group col="8" colspan="4">
23
24=== modified file 'openerp/addons/base/ir/ir_sequence.py'
25--- openerp/addons/base/ir/ir_sequence.py 2012-01-24 11:47:30 +0000
26+++ openerp/addons/base/ir/ir_sequence.py 2012-11-29 17:02:22 +0000
27@@ -22,6 +22,7 @@
28 import logging
29 import time
30
31+from openerp.tools.translate import _
32 import openerp
33
34 _logger = logging.getLogger(__name__)
35@@ -37,7 +38,7 @@
36 _sql_constraints = [
37 ('code_unique', 'unique(code)', '`code` must be unique.'),
38 ]
39-
40+
41 def _code_get(self, cr, uid, context=None):
42 cr.execute('select code, name from ir_sequence_type')
43 return cr.fetchall()
44@@ -52,6 +53,32 @@
45 """
46 _name = 'ir.sequence'
47 _order = 'name'
48+
49+ def _get_number_next_actual(
50+ self, cr, user, ids, field_name, arg, context=None):
51+ '''Return number from ir_sequence row when no_gap implementation,
52+ and number from postgres sequence when standard implementation.'''
53+ res = dict.fromkeys(ids)
54+ for seq_id in ids:
55+ this_obj = self.browse(cr, user, seq_id, context=context)
56+ if this_obj.implementation != 'standard':
57+ res[seq_id] = this_obj.number_next
58+ else:
59+ # get number from postgres sequence. Cannot use
60+ # currval, because that might give an error when
61+ # not having used nextval before.
62+ statement = (
63+ "SELECT last_value, increment_by, is_called"
64+ " FROM ir_sequence_%03d"
65+ % seq_id)
66+ cr.execute(statement)
67+ (last_value, increment_by, is_called) = cr.fetchone()
68+ if is_called:
69+ res[seq_id] = last_value + increment_by
70+ else:
71+ res[seq_id] = last_value
72+ return res
73+
74 _columns = {
75 'name': openerp.osv.fields.char('Name', size=64, required=True),
76 'code': openerp.osv.fields.selection(_code_get, 'Code', size=64),
77@@ -64,11 +91,31 @@
78 'active': openerp.osv.fields.boolean('Active'),
79 'prefix': openerp.osv.fields.char('Prefix', size=64, help="Prefix value of the record for the sequence"),
80 'suffix': openerp.osv.fields.char('Suffix', size=64, help="Suffix value of the record for the sequence"),
81- 'number_next': openerp.osv.fields.integer('Next Number', required=True, help="Next number of this sequence"),
82+ 'number_next': openerp.osv.fields.integer(
83+ 'Next Number', required=True,
84+ help='Next number originally set or reset for this sequence'),
85+ 'number_next_actual': openerp.osv.fields.function(
86+ _get_number_next_actual, type='integer', required=True,
87+ string='Actual Next Number',
88+ help='Next number that will actually be used.'
89+ ' Will be zero for new sequence.'),
90+ 'prohibit_number_next_change': openerp.osv.fields.boolean(
91+ 'Prohibit change of Next Number',
92+ help='Changing next number can wreak havoc on sequences.'
93+ ' Checking this option will prevent accidental changes.'),
94+ 'prohibit_number_next_lower': openerp.osv.fields.boolean(
95+ 'Prohibit lower value for Next Number',
96+ help='When manually setting Next Number to a value lower than the'
97+ ' the current value, there is a big risk for duplicate key errors'
98+ ' when trying to add new sequences generated to the database.'
99+ ' This is especially the case when your prefix or suffix is not'
100+ ' changed at the same time, or has no dynamic part (formula) that'
101+ ' will also change.'),
102 'number_increment': openerp.osv.fields.integer('Increment Number', required=True, help="The next number of the sequence will be incremented by this number"),
103 'padding' : openerp.osv.fields.integer('Number Padding', required=True, help="OpenERP will automatically adds some '0' on the left of the 'Next Number' to get the required padding size."),
104 'company_id': openerp.osv.fields.many2one('res.company', 'Company'),
105 }
106+
107 _defaults = {
108 'implementation': 'standard',
109 'active': True,
110@@ -76,6 +123,8 @@
111 'number_increment': 1,
112 'number_next': 1,
113 'padding' : 0,
114+ 'prohibit_number_next_change': False,
115+ 'prohibit_number_next_lower': False,
116 }
117
118 def init(self, cr):
119@@ -117,16 +166,18 @@
120 # object depends on it.
121 cr.execute("DROP SEQUENCE IF EXISTS %s RESTRICT " % names)
122
123- def _alter_sequence(self, cr, id, number_increment, number_next):
124+ def _alter_sequence(self, cr, id, number_increment, number_next=0):
125 """ Alter a PostreSQL sequence.
126
127 There is no access rights check.
128 """
129 assert isinstance(id, (int, long))
130- cr.execute("""
131- ALTER SEQUENCE ir_sequence_%03d INCREMENT BY %%s RESTART WITH %%s
132- """ % id, (number_increment, number_next))
133-
134+ statement = ("ALTER SEQUENCE ir_sequence_%03d INCREMENT BY %d" %
135+ (id, number_increment))
136+ if number_next:
137+ statement += " RESTART WITH %d" % (number_next, )
138+ cr.execute(statement)
139+
140 def create(self, cr, uid, values, context=None):
141 """ Create a sequence, in implementation == standard a fast gaps-allowed PostgreSQL sequence is used.
142 """
143@@ -140,10 +191,37 @@
144 super(ir_sequence, self).unlink(cr, uid, ids, context)
145 self._drop_sequence(cr, ids)
146 return True
147+
148+ def _pre_validate(self, cr, uid, ids, values, context=None):
149+ '''If number_next changed, check wether change allowed.
150+If number is lower than actual next number, check wether this is allowed.
151+Raise error when invalid change requested.'''
152+ if not 'number_next' in values:
153+ return
154+ nn_new = values['number_next']
155+ for this_obj in self.browse(cr, uid, ids, context=context):
156+ # Only validate when changed.
157+ if this_obj.number_next != nn_new:
158+ prohibit_change = values.get(
159+ 'prohibit_number_next_change',
160+ this_obj.prohibit_number_next_change)
161+ if prohibit_change:
162+ raise openerp.osv.orm.except_orm(
163+ _('ValidationError'),
164+ _('Chance of Next Number has been prohibited'))
165+ prohibit_lower = values.get(
166+ 'prohibit_number_next_lower',
167+ this_obj.prohibit_number_next_lower)
168+ if prohibit_lower and (this_obj.number_next_actual > nn_new):
169+ raise openerp.osv.orm.except_orm(
170+ _('ValidationError'),
171+ _('Setting Next Number to a value lower than the'
172+ ' actual value has been prohibited'))
173
174 def write(self, cr, uid, ids, values, context=None):
175 if not isinstance(ids, (list, tuple)):
176 ids = [ids]
177+ self._pre_validate(cr, uid, ids, values, context=context)
178 new_implementation = values.get('implementation')
179 rows = self.read(cr, uid, ids, ['implementation', 'number_increment', 'number_next'], context)
180 super(ir_sequence, self).write(cr, uid, ids, values, context)
181@@ -154,7 +232,15 @@
182 n = values.get('number_next', row['number_next'])
183 if row['implementation'] == 'standard':
184 if new_implementation in ('standard', None):
185- self._alter_sequence(cr, row['id'], i, n)
186+ # Implementation has NOT changed.
187+ # Only change sequence if really requested.
188+ if (('number_next' in values)
189+ and (values['number_next'] != row['number_next'])):
190+ self._alter_sequence(cr, row['id'], i, n)
191+ else:
192+ # Just in case only increment changed
193+ if 'number_increment' in values:
194+ self._alter_sequence(cr, row['id'], i)
195 else:
196 self._drop_sequence(cr, row['id'])
197 else:
198@@ -162,7 +248,6 @@
199 pass
200 else:
201 self._create_sequence(cr, row['id'], i, n)
202-
203 return True
204
205 def _interpolate(self, s, d):

Subscribers

People subscribed via source and target branches

to all changes: