Merge lp:~fabien-morin/unifield-server/fm-us-2160 into lp:unifield-server

Proposed by jftempo
Status: Rejected
Rejected by: jftempo
Proposed branch: lp:~fabien-morin/unifield-server/fm-us-2160
Merge into: lp:unifield-server
Diff against target: 412 lines (+164/-96)
7 files modified
bin/addons/base/base_update.xml (+0/-47)
bin/addons/base/res/res_user.py (+78/-36)
bin/addons/msf_audittrail/__openerp__.py (+1/-0)
bin/addons/msf_audittrail/audittrail.py (+6/-6)
bin/addons/msf_audittrail/data/audittrail_res_users.yml (+69/-0)
bin/addons/unifield_setup/unifield_setup_data.xml (+0/-6)
bin/service/web_services.py (+10/-1)
To merge this branch: bzr merge lp:~fabien-morin/unifield-server/fm-us-2160
Reviewer Review Type Date Requested Status
UniField Reviewer Team Pending
Review via email: mp+315653@code.launchpad.net
To post a comment you must log in.
4166. By Fabien MORIN

US-2160 [FIX] remove password from the list of tracked changes fields.

4167. By Fabien MORIN

US-2160 [FIX] add audittrail line on related user when a group is created with
users.
[FIX] m2m syntax on res.groups. i.e write
 {'users': [(3, 73)]}

4168. By Fabien MORIN

US-2160 [IMP] do not use view=extended/simple anymore. Add the extended group
to admin at database creation to make possible for him to install other
modules.

4169. By Fabien MORIN

US-2160 [FIX] check user_obj have a check_audit method (only if sync_client
installed) before to continue.

4170. By Fabien MORIN

US-2160 [FIX] user_id was missing in the call of _track_change_of_users()
[IMP] make the code a bit more crash proof after reading the complet diff of
the ticket.

4171. By Fabien MORIN

US-2160 [FIX] typo

4172. By Fabien MORIN

US-2160 [MERGE] with latest trunk

4173. By Fabien MORIN

US-2160 [FIX] tuple comprenhension return a generator while we need a list or a
tuple.
[FIX] give the list of ids to name_get to have a list of result (instead of
only one).
[IMP] user translation_obj instead of calling self.pool.get all the time

Revision history for this message
jftempo (jfb-tempo-consulting) :
Revision history for this message
Fabien MORIN (fabien-morin) wrote :
Download full text (4.9 KiB)

Fixed

On Monday 20 February 2017 13:06:51 you wrote:
> Diff comments:
> > === modified file 'bin/addons/base/res/res_user.py'
> > --- bin/addons/base/res/res_user.py 2017-01-03 16:05:44 +0000
> > +++ bin/addons/base/res/res_user.py 2017-02-16 09:43:29 +0000
> > @@ -789,6 +761,76 @@
> >
> > 'users': fields.many2many('res.users', 'res_groups_users_rel',
> > 'gid', 'uid', 'Users'),>
> > }
> >
> > + def _track_change_of_users(self, cr, uid, previous_values, user_ids,
> > + vals, context=None):
> > + '''add audittrail entry to the related users if their groups were
> > changed + @param previous_values: list of dict containing
> > groups_ids of the users + @param user_ids: related user ids
> > + @param vals: vals parameter from the write/create
> > + '''
> > + current_values = {}
> > + audit_obj = self.pool.get('audittrail.rule')
> > + if isinstance(user_ids, (int, long)):
> > + user_ids = [user_ids]
> > + if 'users' in vals and user_ids:
> > + if vals['users'] and len(vals['users'][0]) > 2:
> > + users_deleted =
> > list(set(user_ids).difference(vals['users'][0][2])) +
> > users_added = list(set(vals['users'][0][2]).difference(user_ids)) +
> > user_obj = self.pool.get('res.users')
> > + if not hasattr(user_obj, 'check_audit'):
> > + return
> > + audit_rule_ids = user_obj.check_audit(cr, uid, 'write')
> > + if users_deleted:
> > + previous_values = [x for x in previous_values if
> > x['id'] in users_deleted] + current_values =
> > dict((x['id'], x) for x in user_obj.read(cr, uid, users_deleted,
> > ['groups_id'], context=context)) +
> > audit_obj.audit_log(cr, uid, audit_rule_ids, user_obj, users_deleted,
> > 'write', previous_values, current_values, context=context) +
> > if users_added:
> > + previous_values = [x for x in previous_values if
> > x['id'] in users_added] + current_values =
> > dict((x['id'], x) for x in user_obj.read(cr, uid, users_added,
> > ['groups_id'], context=context)) +
> > audit_obj.audit_log(cr, uid, audit_rule_ids, user_obj, users_added,
> > 'write', previous_values, current_values, context=context) +
> > + def create(self, cr, uid, vals, context=None):
> > + '''
> > + In case user have been added, a new audit line should be created
> > on the related users + '''
> > + change_user_group = False
> > + previous_values = []
> > + if 'users' in vals and vals['users'] and len(vals['users'][0]) >
> > 2: + user_obj = self.pool.get('res.users')
> > + previous_values = user_obj.read(cr, uid, vals['users'][0][2],
> > ['groups_id'], context=context) + change_user_group = True
> > + user_id = super(groups, self).create(cr, uid, vals,
> > context=context)
> > + if change_user_group:
> "Funny" bug here: you a not in groups() class but in groups2, so it should
> be user_id = super(grou...

Read more...

4174. By Fabien MORIN

US-2160 [FIX] the current class is groups2, not groups

Unmerged revisions

4174. By Fabien MORIN

US-2160 [FIX] the current class is groups2, not groups

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'bin/addons/base/base_update.xml'
2--- bin/addons/base/base_update.xml 2016-11-16 07:57:07 +0000
3+++ bin/addons/base/base_update.xml 2017-02-20 13:31:41 +0000
4@@ -139,7 +139,6 @@
5 <field name="context_lang"/>
6 <field name="context_tz"/>
7 <group colspan="2" col="4">
8- <field name="view" readonly="0"/>
9 <field name="menu_tips" colspan="2"/>
10 </group>
11 </group>
12@@ -314,57 +313,11 @@
13 <field name="target">new</field>
14 </record>
15
16- <record id="view_confirm_simple_view_form" model="ir.ui.view">
17- <field name="name">Configure Your Interface</field>
18- <field name="model">res.config.view</field>
19- <field name="type">form</field>
20- <field name="inherit_id" ref="res_config_view_base"/>
21- <field name="arch" type="xml">
22- <data>
23- <form position="attributes">
24- <attribute name="string">Configure Your Interface</attribute>
25- </form>
26- <xpath expr="//label[@string='description']"
27- position="attributes">
28- <attribute name="string">If you use OpenERP for the first time we strongly advise you to select the simplified interface, which has less features but is easier. You can always switch later from the user preferences.</attribute>
29- </xpath>
30- <xpath expr='//separator[@string="title"]' position='attributes'>
31- <attribute name='string'>Configure Your Interface</attribute>
32- </xpath>
33- <xpath expr='//separator[@string="vsep"]' position='attributes'>
34- <attribute name='string'></attribute>
35- <attribute name='rowspan'>12</attribute>
36- </xpath>
37- <group string="res_config_contents" position="replace">
38- <group colspan="4">
39- <field colspan="4" name="view" nolabel="1"/>
40- </group>
41- </group>
42- <xpath expr='//button[@name="action_skip"]' position='replace'/>
43- </data>
44- </field>
45- </record>
46-
47- <record id="action_config_simple_view_form" model="ir.actions.act_window">
48- <field name="name">Configure Your Interface</field>
49- <field name="type">ir.actions.act_window</field>
50- <field name="res_model">res.config.view</field>
51- <field name="view_type">form</field>
52- <field name="view_id" ref="view_confirm_simple_view_form"/>
53- <field name="view_mode">form</field>
54- <field name="target">new</field>
55- </record>
56-
57 <record id="config_wizard_step_user" model="ir.actions.todo">
58 <field name="action_id" ref="action_config_user_form"/>
59 <field name="sequence">10</field>
60 <field name="restart">never</field>
61 <field name="state">done</field>
62 </record>
63- <record id="config_wizard_simple_view" model="ir.actions.todo">
64- <field name="action_id" ref="action_config_simple_view_form"/>
65- <field name="restart">always</field>
66- <field name="sequence">1</field>
67- </record>
68 </data>
69 </openerp>
70
71=== modified file 'bin/addons/base/res/res_user.py'
72--- bin/addons/base/res/res_user.py 2017-01-03 16:05:44 +0000
73+++ bin/addons/base/res/res_user.py 2017-02-20 13:31:41 +0000
74@@ -114,6 +114,7 @@
75 _uid_cache = {}
76 _name = "res.users"
77 _order = 'name'
78+ _trace = True
79
80 WELCOME_MAIL_SUBJECT = u"Welcome to OpenERP"
81 WELCOME_MAIL_BODY = u"An OpenERP account has been created for you, "\
82@@ -178,36 +179,6 @@
83 body=self.get_welcome_mail_body(
84 cr, uid, context=context) % user)
85
86- def _set_interface_type(self, cr, uid, ids, name, value, arg, context=None):
87- """Implementation of 'view' function field setter, sets the type of interface of the users.
88- @param name: Name of the field
89- @param arg: User defined argument
90- @param value: new value returned
91- @return: True/False
92- """
93- if not value or value not in ['simple','extended']:
94- return False
95- group_obj = self.pool.get('res.groups')
96- extended_group_id = group_obj.get_extended_interface_group(cr, uid, context=context)
97- # First always remove the users from the group (avoids duplication if called twice)
98- self.write(cr, uid, ids, {'groups_id': [(3, extended_group_id)]}, context=context)
99- # Then add them back if requested
100- if value == 'extended':
101- self.write(cr, uid, ids, {'groups_id': [(4, extended_group_id)]}, context=context)
102- return True
103-
104-
105- def _get_interface_type(self, cr, uid, ids, name, args, context=None):
106- """Implementation of 'view' function field getter, returns the type of interface of the users.
107- @param field_name: Name of the field
108- @param arg: User defined argument
109- @return: Dictionary of values
110- """
111- group_obj = self.pool.get('res.groups')
112- extended_group_id = group_obj.get_extended_interface_group(cr, uid, context=context)
113- extended_users = group_obj.read(cr, uid, extended_group_id, ['users'], context=context)['users']
114- return dict(zip(ids, ['extended' if user in extended_users else 'simple' for user in ids]))
115-
116 def _email_get(self, cr, uid, ids, name, arg, context=None):
117 # perform this as superuser because the current user is allowed to read users, and that includes
118 # the email, even without any direct read access on the res_partner_address object.
119@@ -294,8 +265,9 @@
120 for user_id in ids:
121 level = False
122 company_id = self._get_company(cr, user_id, context=context)
123- instance_id = self.pool.get('res.company').read(cr, uid, company_id,
124- ['instance_id'], context=context)['instance_id']
125+ company_obj = self.pool.get('res.company')
126+ instance_id = company_obj.read(cr, uid, company_id,
127+ ['instance_id'], context=context).get('instance_id', False)
128 instance_id = instance_id and instance_id[0] or False
129 if instance_id:
130 level = self.pool.get('msf.instance').read(cr, uid, instance_id, ['level'], context=context)['level']
131@@ -341,8 +313,7 @@
132 'context_tz': fields.selection(_tz_get, 'Timezone', size=64,
133 help="The user's timezone, used to perform timezone conversions "
134 "between the server and the client."),
135- 'view': fields.function(_get_interface_type, method=True, type='selection', fnct_inv=_set_interface_type,
136- selection=[('simple','Simplified'),('extended','Extended')],
137+ 'view': fields.selection([('simple','Simplified'),('extended','Extended')],
138 string='Interface', help="Choose between the simplified interface and the extended one"),
139 'user_email': fields.function(_email_get, method=True, fnct_inv=_email_set, string='Email', type="char", size=240),
140 'menu_tips': fields.boolean('Menu Tips', help="Check out this box if you want to always display tips on each menu action"),
141@@ -452,6 +423,7 @@
142 'address_id': False,
143 'menu_tips':True,
144 'force_password_change': False,
145+ 'view': 'simple',
146 }
147
148 @tools.cache()
149@@ -789,6 +761,76 @@
150 'users': fields.many2many('res.users', 'res_groups_users_rel', 'gid', 'uid', 'Users'),
151 }
152
153+ def _track_change_of_users(self, cr, uid, previous_values, user_ids,
154+ vals, context=None):
155+ '''add audittrail entry to the related users if their groups were changed
156+ @param previous_values: list of dict containing groups_ids of the users
157+ @param user_ids: related user ids
158+ @param vals: vals parameter from the write/create
159+ '''
160+ current_values = {}
161+ audit_obj = self.pool.get('audittrail.rule')
162+ if isinstance(user_ids, (int, long)):
163+ user_ids = [user_ids]
164+ if 'users' in vals and user_ids:
165+ if vals['users'] and len(vals['users'][0]) > 2:
166+ users_deleted = list(set(user_ids).difference(vals['users'][0][2]))
167+ users_added = list(set(vals['users'][0][2]).difference(user_ids))
168+ user_obj = self.pool.get('res.users')
169+ if not hasattr(user_obj, 'check_audit'):
170+ return
171+ audit_rule_ids = user_obj.check_audit(cr, uid, 'write')
172+ if users_deleted:
173+ previous_values = [x for x in previous_values if x['id'] in users_deleted]
174+ current_values = dict((x['id'], x) for x in user_obj.read(cr, uid, users_deleted, ['groups_id'], context=context))
175+ audit_obj.audit_log(cr, uid, audit_rule_ids, user_obj, users_deleted, 'write', previous_values, current_values, context=context)
176+ if users_added:
177+ previous_values = [x for x in previous_values if x['id'] in users_added]
178+ current_values = dict((x['id'], x) for x in user_obj.read(cr, uid, users_added, ['groups_id'], context=context))
179+ audit_obj.audit_log(cr, uid, audit_rule_ids, user_obj, users_added, 'write', previous_values, current_values, context=context)
180+
181+ def create(self, cr, uid, vals, context=None):
182+ '''
183+ In case user have been added, a new audit line should be created on the related users
184+ '''
185+ change_user_group = False
186+ previous_values = []
187+ if 'users' in vals and vals['users'] and len(vals['users'][0]) > 2:
188+ user_obj = self.pool.get('res.users')
189+ previous_values = user_obj.read(cr, uid, vals['users'][0][2], ['groups_id'], context=context)
190+ change_user_group = True
191+ user_id = super(groups2, self).create(cr, uid, vals, context=context)
192+ if change_user_group:
193+ self._track_change_of_users(cr, uid, previous_values, [user_id],
194+ vals, context=context)
195+ return user_id
196+
197+ def write(self, cr, uid, ids, vals, context=None):
198+ '''
199+ In case user have been added or deleted, a new audit line should be created on the related users
200+ '''
201+ all_user_ids = [] # previous user ids + current
202+ previous_values = []
203+ user_ids = []
204+ if isinstance(ids, (int, long)):
205+ ids = [ids]
206+ if 'users' in vals:
207+ new_user_ids = []
208+ if vals['users'] and len(vals['users'][0]) > 2:
209+ new_user_ids = vals['users'][0][2]
210+ for record in self.read(cr, uid, ids, ['users'], context=context):
211+ if record['users']:
212+ user_ids.extend(record['users'])
213+ all_user_ids = set(new_user_ids).union(user_ids)
214+ user_obj = self.pool.get('res.users')
215+ previous_values = user_obj.read(cr, uid, all_user_ids, ['groups_id'], context=context)
216+
217+ res = super(groups2, self).write(cr, uid, ids, vals, context=context)
218+ if 'users' in vals:
219+ self._track_change_of_users(cr, uid, previous_values, user_ids,
220+ vals, context=context)
221+ return res
222+
223 def unlink(self, cr, uid, ids, context=None):
224 group_users = []
225 for record in self.read(cr, uid, ids, ['users'], context=context):
226@@ -814,10 +856,10 @@
227 'name':fields.char('Name', size=64),
228 'view': fields.selection([('simple','Simplified'),
229 ('extended','Extended')],
230- 'Interface', required=True ),
231+ 'Interface', required=False ),
232 }
233 _defaults={
234- 'view':lambda self,cr,uid,*args: self.pool.get('res.users').browse(cr, uid, uid).view or 'simple',
235+ 'view': 'simple',
236 }
237
238 def execute(self, cr, uid, ids, context=None):
239
240=== modified file 'bin/addons/msf_audittrail/__openerp__.py'
241--- bin/addons/msf_audittrail/__openerp__.py 2016-06-07 15:10:42 +0000
242+++ bin/addons/msf_audittrail/__openerp__.py 2017-02-20 13:31:41 +0000
243@@ -46,6 +46,7 @@
244 'data/audittrail_data_purchase.yml',
245 'data/audittrail_data_products.yml',
246 'data/audittrail_data_JI.yml',
247+ 'data/audittrail_res_users.yml',
248 'audittrail_report.xml',
249 'audittrail_invoice_data.yml',
250 ],
251
252=== modified file 'bin/addons/msf_audittrail/audittrail.py'
253--- bin/addons/msf_audittrail/audittrail.py 2017-02-06 11:14:07 +0000
254+++ bin/addons/msf_audittrail/audittrail.py 2017-02-20 13:31:41 +0000
255@@ -795,7 +795,7 @@
256 """
257 _name = 'audittrail.log.line'
258 _description = "Log Line"
259- _order = 'timestamp desc, id desc'
260+ _order = 'timestamp desc, log desc'
261
262 def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
263 """
264@@ -1050,9 +1050,8 @@
265 res = []
266 if values and values != '[]':
267 values = values[1:-1].split(',')
268- values = (int(v) for v in values)
269- res = [x[1] for x in relation_model_pool.name_get(cr, uid,
270- [long(values[0])])]
271+ values = [int(v) for v in values]
272+ res = [x[1] for x in relation_model_pool.name_get(cr, uid, values)]
273 return res
274 elif field['ttype'] == 'date':
275 res = False
276@@ -1076,15 +1075,16 @@
277 elif field['ttype'] == 'selection':
278 res = False
279 if values:
280+ translation_obj = self.pool.get('ir.translation')
281 fct_object = model_pool.browse(cr, uid, model.id, context=context).model
282 sel = self.pool.get(fct_object).fields_get(cr, uid, [field['name']])
283 if field['name'] in sel:
284 res = dict(sel[field['name']]['selection']).get(values)
285 name = '%s,%s' % (fct_object, field['name'])
286 # Search translation
287- res_tr_ids = self.pool.get('ir.translation').search(cr, uid, [('type', '=', 'selection'), ('name', '=', name), ('src', 'in', [values])])
288+ res_tr_ids = translation_obj.search(cr, uid, [('type', '=', 'selection'), ('name', '=', name), ('src', 'in', [values])])
289 if res_tr_ids:
290- res = self.pool.get('ir.translation').read(cr, uid, res_tr_ids, ['value'])[0]['value']
291+ res = translation_obj.read(cr, uid, res_tr_ids, ['value'])[0]['value']
292 else:
293 res = values
294 return res
295
296=== added file 'bin/addons/msf_audittrail/data/audittrail_res_users.yml'
297--- bin/addons/msf_audittrail/data/audittrail_res_users.yml 1970-01-01 00:00:00 +0000
298+++ bin/addons/msf_audittrail/data/audittrail_res_users.yml 2017-02-20 13:31:41 +0000
299@@ -0,0 +1,69 @@
300+-
301+ For Users, rule for the CREATE of objects with the list of fields to track
302+-
303+ !python {model: audittrail.rule}: |
304+ name = 'USER CREATE'
305+ object_ids = self.pool.get('ir.model').search(cr, uid, [('model', '=', 'res.users')], context=context)
306+ rule_id = self.search(cr, uid, [('name', '=', name)], context=context)
307+ if object_ids:
308+ fields = ['action_id', 'active', 'address_id', 'email', 'force_password_change', 'groups_id', 'login', 'menu_id', 'menu_tips', 'name', 'signature', 'user_email']
309+ fields_ids = self.pool.get('ir.model.fields').search(cr, uid, [('model', '=' ,'res.users'), ('name', 'in', fields)], context=context)
310+ vals = {'name': name,
311+ 'object_id': object_ids[0],
312+ 'log_create': True,
313+ 'log_write': False,
314+ 'log_unlink': False,
315+ 'field_ids': [(6, 0, fields_ids)],
316+ }
317+ if not rule_id:
318+ rule_id = self.create(cr, uid, vals, context=context)
319+ elif rule_id:
320+ self.write(cr, uid, rule_id, vals, context=context)
321+ # Subscribe to the rule
322+ self.subscribe(cr, uid, rule_id)
323+-
324+ For Users, rule for the WRITE of objects with the list of fields to track
325+-
326+ !python {model: audittrail.rule}: |
327+ name = 'USER WRITE'
328+ object_ids = self.pool.get('ir.model').search(cr, uid, [('model', '=', 'res.users')], context=context)
329+ rule_id = self.search(cr, uid, [('name', '=', name)], context=context)
330+ if object_ids:
331+ fields = ['action_id', 'active', 'address_id', 'email', 'force_password_change', 'groups_id', 'login', 'menu_id', 'menu_tips', 'name', 'signature', 'user_email']
332+ fields_ids = self.pool.get('ir.model.fields').search(cr, uid, [('model', '=' ,'res.users'), ('name', 'in', fields)], context=context)
333+ vals = {'name': name,
334+ 'object_id': object_ids[0],
335+ 'log_create': False,
336+ 'log_write': True,
337+ 'log_unlink': False,
338+ 'field_ids': [(6, 0, fields_ids)],
339+ }
340+ if not rule_id:
341+ rule_id = self.create(cr, uid, vals, context=context)
342+ elif rule_id:
343+ self.write(cr, uid, rule_id, vals, context=context)
344+ # Subscribe to the rule
345+ self.subscribe(cr, uid, rule_id)
346+-
347+ For USERS, rule for the DELETE of objects with the list of fields to track
348+-
349+ !python {model: audittrail.rule}: |
350+ name = 'USER DELETE'
351+ object_ids = self.pool.get('ir.model').search(cr, uid, [('model', '=', 'res.users')], context=context)
352+ rule_id = self.search(cr, uid, [('name', '=', name)], context=context)
353+ if object_ids:
354+ fields = ['name']
355+ fields_ids = self.pool.get('ir.model.fields').search(cr, uid, [('model', '=' ,'res.users'), ('name', 'in', fields)], context=context)
356+ vals = {'name': name,
357+ 'object_id': object_ids[0],
358+ 'log_create': False,
359+ 'log_write': False,
360+ 'log_unlink': True,
361+ 'field_ids': [(6, 0, fields_ids)],
362+ }
363+ if not rule_id:
364+ rule_id = self.create(cr, uid, vals, context=context)
365+ elif rule_id:
366+ self.write(cr, uid, rule_id, vals, context=context)
367+ # Subscribe to the rule
368+ self.subscribe(cr, uid, rule_id)
369
370=== modified file 'bin/addons/unifield_setup/unifield_setup_data.xml'
371--- bin/addons/unifield_setup/unifield_setup_data.xml 2012-11-19 17:26:48 +0000
372+++ bin/addons/unifield_setup/unifield_setup_data.xml 2017-02-20 13:31:41 +0000
373@@ -3,12 +3,6 @@
374 <data noupdate="0">
375
376 <!-- Re-order the configuration wizards sequences -->
377- <record id="base.config_wizard_simple_view" model="ir.actions.todo">
378- <field name="sequence" eval="1" />
379- <field name="restart">never</field>
380- <field name="state">done</field>
381- </record>
382-
383 <record id="base_setup.base_setup_company_todo" model="ir.actions.todo">
384 <field name="sequence" eval="17" />
385 <field name="restart">always</field>
386
387=== modified file 'bin/service/web_services.py'
388--- bin/service/web_services.py 2017-01-31 11:13:35 +0000
389+++ bin/service/web_services.py 2017-02-20 13:31:41 +0000
390@@ -167,12 +167,21 @@
391 mids = modobj.search(cr, 1, [('state', '=', 'installed')])
392 modobj.update_translations(cr, 1, mids, lang)
393
394- cr.execute('UPDATE res_users SET password=%s, context_lang=%s, active=True WHERE login=%s', (
395+ cr.execute('UPDATE res_users SET password=%s, context_lang=%s, active=True WHERE login=%s RETURNING id', (
396 user_password, lang, 'admin'))
397+ uid = cr.fetchone()[0]
398 cr.execute('SELECT login, password, name ' \
399 ' FROM res_users ' \
400 ' ORDER BY login')
401 serv.actions[id]['users'] = cr.dictfetchall()
402+
403+ # add the extended interface group to the admin user
404+ # this is needed to be able to install other modules
405+ res_group_obj = pool.get('res.groups')
406+ group_extended_id = res_group_obj.get_extended_interface_group(cr, 1)
407+ if group_extended_id:
408+ res_users_obj = pool.get('res.users')
409+ res_users_obj.write(cr, uid, uid, {'groups_id': [(4, group_extended_id)]})
410 serv.actions[id]['clean'] = True
411 cr.commit()
412 except Exception, e:

Subscribers

People subscribed via source and target branches