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