[FIX]: Remove workflow activity and drop constraint during uninstalling module.

bzr revid: atp@tinyerp.com-20120229135343-g0v04jb5nc723w9q
This commit is contained in:
Atul Patel (OpenERP) 2012-02-29 19:23:43 +05:30
parent 679d307ead
commit 3465869df7
8 changed files with 133 additions and 68 deletions

View File

@ -510,7 +510,7 @@ class actions_server(osv.osv):
'code':fields.text('Python Code', help="Python code to be executed if condition is met.\n"
"It is a Python block that can use the same values as for the condition field"),
'sequence': fields.integer('Sequence', help="Important when you deal with multiple actions, the execution order will be decided based on this, low number is higher priority."),
'model_id': fields.many2one('ir.model', 'Object', required=True, help="Select the object on which the action will work (read, write, create)."),
'model_id': fields.many2one('ir.model', 'Object', required=True, help="Select the object on which the action will work (read, write, create).", ondelete='cascade'),
'action_id': fields.many2one('ir.actions.actions', 'Client Action', help="Select the Action Window, Report, Wizard to be executed."),
'trigger_name': fields.selection(_select_signals, string='Trigger Signal', size=128, help="The workflow signal to trigger"),
'wkf_model_id': fields.many2one('ir.model', 'Target Object', help="The object that should receive the workflow signal (must have an associated workflow)"),

View File

@ -1,4 +1,5 @@
# -*- coding: utf-8 -*-
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
@ -134,11 +135,18 @@ class ir_model(osv.osv):
super(ir_model, self).search(cr, uid, domain, limit=limit, context=context),
context=context)
def _drop_table(self, cr, uid, ids, context=None):
for model in self.browse(cr, uid, ids, context):
model_pool = self.pool.get(model.model)
if getattr(model_pool, '_auto', True) and not model.osv_memory:
cr.execute("DROP table %s cascade" % model_pool._table)
return True
def unlink(self, cr, user, ids, context=None):
for model in self.browse(cr, user, ids, context):
if model.state != 'manual':
raise except_orm(_('Error'), _("You can not remove the model '%s' !") %(model.name,))
# for model in self.browse(cr, user, ids, context):
# if model.state != 'manual':
# raise except_orm(_('Error'), _("You can not remove the model '%s' !") %(model.name,))
# self._drop_table(cr, user, ids, context)
res = super(ir_model, self).unlink(cr, user, ids, context)
pooler.restart_pool(cr.dbname)
return res
@ -263,17 +271,19 @@ class ir_model_fields(osv.osv):
_sql_constraints = [
('size_gt_zero', 'CHECK (size>0)',_size_gt_zero_msg ),
]
def _drop_column(self, cr, uid, ids, context=None):
for field in self.browse(cr, uid, ids, context):
model = self.pool.get(field.model)
if not field.model.osv_memory and getattr(model, '_auto', True):
cr.execute("ALTER table %s DROP column %s" % (model._table, field.name))
model._columns.pop(field.name, None)
return True
def unlink(self, cr, user, ids, context=None):
for field in self.browse(cr, user, ids, context):
if field.state <> 'manual':
raise except_orm(_('Error'), _("You cannot remove the field '%s' !") %(field.name,))
#
# MAY BE ADD A ALTER TABLE DROP ?
#
#Removing _columns entry for that table
self.pool.get(field.model)._columns.pop(field.name,None)
return super(ir_model_fields, self).unlink(cr, user, ids, context)
self._drop_column(cr, user, ids, context)
res = super(ir_model_fields, self).unlink(cr, user, ids, context)
return res
def create(self, cr, user, vals, context=None):
if 'model_id' in vals:
@ -624,6 +634,7 @@ class ir_model_data(osv.osv):
# also stored in pool to avoid being discarded along with this osv instance
if getattr(pool, 'model_data_reference_ids', None) is None:
self.pool.model_data_reference_ids = {}
self.loads = self.pool.model_data_reference_ids
def _auto_init(self, cr, context=None):
@ -667,9 +678,11 @@ class ir_model_data(osv.osv):
except:
id = False
return id
def unlink(self, cr, uid, ids, context=None):
""" Regular unlink method, but make sure to clear the caches. """
self._pre_process_unlink(cr, uid, ids, context)
self._get_id.clear_cache(self)
self.get_object_reference.clear_cache(self)
return super(ir_model_data,self).unlink(cr, uid, ids, context=context)
@ -688,7 +701,6 @@ class ir_model_data(osv.osv):
if (not xml_id) and (not self.doinit):
return False
action_id = False
if xml_id:
cr.execute('''SELECT imd.id, imd.res_id, md.id, imd.model
FROM ir_model_data imd LEFT JOIN %s md ON (imd.res_id = md.id)
@ -791,53 +803,78 @@ class ir_model_data(osv.osv):
elif xml_id:
cr.execute('UPDATE ir_values set value=%s WHERE model=%s and key=%s and name=%s'+where,(value, model, key, name))
return True
def _pre_process_unlink(self, cr, uid, ids, context=None):
wkf_todo = []
to_unlink = []
for data in self.browse(cr, uid, ids, context):
model = data.model
res_id = data.res_id
model_obj = self.pool.get(model)
if str(data.name).startswith('constraint_'):
cr.execute('ALTER TABLE "%s" DROP CONSTRAINT "%s"' % (model_obj._table,data.name[11:]),)
_logger.info('Drop CONSTRAINT %s@%s', data.name[11:], model)
continue
to_unlink.append((model,res_id))
if model=='workflow.activity':
cr.execute('select res_type,res_id from wkf_instance where id IN (select inst_id from wkf_workitem where act_id=%s)', (res_id,))
wkf_todo.extend(cr.fetchall())
cr.execute("update wkf_transition set condition='True', group_id=NULL, signal=NULL,act_to=act_from,act_from=%s where act_to=%s", (res_id,res_id))
cr.execute("delete from wkf_transition where act_to=%s", (res_id,))
for model,res_id in wkf_todo:
wf_service = netsvc.LocalService("workflow")
wf_service.trg_write(uid, model, res_id, cr)
#cr.commit()
if not config.get('import_partial'):
for (model, res_id) in to_unlink:
if self.pool.get(model):
_logger.info('Deleting %s@%s', res_id, model)
res_ids = self.pool.get(model).search(cr, uid, [('id', '=', res_id)])
if res_ids:
self.pool.get(model).unlink(cr, uid, [res_id])
cr.commit()
# except Exception:
# cr.rollback()
# _logger.warning(
# 'Could not delete obsolete record with id: %d of model %s\n'
## 'There should be some relation that points to this resource\n'
# 'You should manually fix this and restart with --update=module',
# res_id, model)
def _process_end(self, cr, uid, modules):
""" Clear records removed from updated module data.
This method is called at the end of the module loading process.
It is meant to removed records that are no longer present in the
updated data. Such records are recognised as the one with an xml id
and a module in ir_model_data and noupdate set to false, but not
present in self.loads.
"""
if not modules:
return True
modules = list(modules)
data_ids = self.search(cr, uid, [('module','in',modules)])
module_in = ",".join(["%s"] * len(modules))
cr.execute('select id,name,model,res_id,module from ir_model_data where module IN (' + module_in + ') and noupdate=%s', modules + [False])
wkf_todo = []
process_query = 'select id,name,model,res_id,module from ir_model_data where module IN (' + module_in + ')'
process_query+= ' and noupdate=%s'
to_unlink = []
cr.execute( process_query, modules + [False])
for (id, name, model, res_id,module) in cr.fetchall():
if (module,name) not in self.loads:
to_unlink.append((model,res_id))
if model=='workflow.activity':
cr.execute('select res_type,res_id from wkf_instance where id IN (select inst_id from wkf_workitem where act_id=%s)', (res_id,))
wkf_todo.extend(cr.fetchall())
cr.execute("update wkf_transition set condition='True', group_id=NULL, signal=NULL,act_to=act_from,act_from=%s where act_to=%s", (res_id,res_id))
cr.execute("delete from wkf_transition where act_to=%s", (res_id,))
for model,id in wkf_todo:
wf_service = netsvc.LocalService("workflow")
wf_service.trg_write(uid, model, id, cr)
cr.commit()
self.pool.get(model).unlink(cr, uid, [res_id])
if not config.get('import_partial'):
for (model, res_id) in to_unlink:
if self.pool.get(model):
_logger.info('Deleting %s@%s', res_id, model)
try:
self.pool.get(model).unlink(cr, uid, [res_id])
cr.commit()
except Exception:
cr.rollback()
_logger.warning(
'Could not delete obsolete record with id: %d of model %s\n'
'There should be some relation that points to this resource\n'
'You should manually fix this and restart with --update=module',
res_id, model)
return True
self.pool.get(model).unlink(cr, uid, [res_id])
# cr.commit()
ir_model_data()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -75,7 +75,7 @@ class ir_rule(osv.osv):
_columns = {
'name': fields.char('Name', size=128, select=1),
'model_id': fields.many2one('ir.model', 'Object',select=1, required=True),
'model_id': fields.many2one('ir.model', 'Object',select=1, required=True, ondelete='cascade'),
'global': fields.function(_get_value, string='Global', type='boolean', store=True, help="If no group is specified the rule is global and applied to everyone"),
'groups': fields.many2many('res.groups', 'rule_group_rel', 'rule_group_id', 'group_id', 'Groups'),
'domain_force': fields.text('Domain'),

View File

@ -375,10 +375,18 @@ class module(osv.osv):
def button_install_cancel(self, cr, uid, ids, context=None):
self.write(cr, uid, ids, {'state': 'uninstalled', 'demo':False})
return True
def module_uninstall(self, cr, uid, ids, context=None):
model_data = self.pool.get('ir.model.data')
remove_modules = map(lambda x: x.name, self.browse(cr, uid, ids, context))
data_ids = model_data.search(cr, uid, [('module', 'in', remove_modules)])
model_data.unlink(cr, uid, data_ids, context)
self.write(cr, uid, ids, {'state': 'uninstalled'})
return True
def button_uninstall(self, cr, uid, ids, context=None):
for module in self.browse(cr, uid, ids):
cr.execute('''select m.state,m.name
cr.execute('''select m.id
from
ir_module_module_dependency d
join
@ -387,8 +395,11 @@ class module(osv.osv):
d.name=%s and
m.state not in ('uninstalled','uninstallable','to remove')''', (module.name,))
res = cr.fetchall()
if res:
raise orm.except_orm(_('Error'), _('Some installed modules depend on the module you plan to Uninstall :\n %s') % '\n'.join(map(lambda x: '\t%s: %s' % (x[0], x[1]), res)))
for i in range(0,len(res)):
ids.append(res[i][0])
# if res:
# self.write(cr, uid, ids, {'state': 'to remove'})
## raise orm.except_orm(_('Error'), _('Some installed modules depend on the module you plan to Uninstall :\n %s') % '\n'.join(map(lambda x: '\t%s: %s' % (x[0], x[1]), res)))
self.write(cr, uid, ids, {'state': 'to remove'})
return dict(ACTION_DICT, name=_('Uninstall'))

View File

@ -83,7 +83,9 @@ class base_module_upgrade(osv.osv_memory):
def upgrade_module(self, cr, uid, ids, context=None):
mod_obj = self.pool.get('ir.module.module')
ids = mod_obj.search(cr, uid, [('state', 'in', ['to upgrade', 'to remove', 'to install'])])
data_obj = self.pool.get('ir.model.data')
# process to install and upgrade modules
ids = mod_obj.search(cr, uid, [('state', 'in', ['to upgrade', 'to install'])])
unmet_packages = []
mod_dep_obj = self.pool.get('ir.module.module.dependency')
for mod in mod_obj.browse(cr, uid, ids):
@ -95,9 +97,14 @@ class base_module_upgrade(osv.osv_memory):
raise osv.except_osv(_('Unmet dependency !'), _('Following modules are not installed or unknown: %s') % ('\n\n' + '\n'.join(unmet_packages)))
mod_obj.download(cr, uid, ids, context=context)
cr.commit()
# process to remove modules
remove_module_ids = mod_obj.search(cr, uid, [('state', 'in', ['to remove'])])
mod_obj.module_uninstall(cr, uid, remove_module_ids, context)
_db, pool = pooler.restart_pool(cr.dbname, update_module=True)
data_obj = pool.get('ir.model.data')
id2 = data_obj._get_id(cr, uid, 'base', 'view_base_module_upgrade_install')
if id2:
id2 = data_obj.browse(cr, uid, id2, context=context).res_id

View File

@ -53,7 +53,7 @@ class groups(osv.osv):
_columns = {
'name': fields.char('Name', size=64, required=True, translate=True),
'users': fields.many2many('res.users', 'res_groups_users_rel', 'gid', 'uid', 'Users'),
'users': fields.many2many('res.users', 'res_groups_users_rel', 'gid', 'uid', 'Users', ondelete='CASCADE'),
'model_access': fields.one2many('ir.model.access', 'group_id', 'Access Controls'),
'rule_groups': fields.many2many('ir.rule', 'rule_group_rel',
'group_id', 'rule_group_id', 'Rules', domain=[('global', '=', False)]),

View File

@ -377,19 +377,22 @@ def load_modules(db, force_demo=False, status=None, update_module=False):
if update_module:
# Remove records referenced from ir_model_data for modules to be
# removed (and removed the references from ir_model_data).
cr.execute("select id,name from ir_module_module where state=%s", ('to remove',))
for mod_id, mod_name in cr.fetchall():
cr.execute('select model,res_id from ir_model_data where noupdate=%s and module=%s order by id desc', (False, mod_name,))
for rmod, rid in cr.fetchall():
uid = 1
rmod_module= pool.get(rmod)
if rmod_module:
# TODO group by module so that we can delete multiple ids in a call
rmod_module.unlink(cr, uid, [rid])
else:
_logger.error('Could not locate %s to remove res=%d' % (rmod,rid))
cr.execute('delete from ir_model_data where noupdate=%s and module=%s', (False, mod_name,))
cr.commit()
#cr.execute("select id,name from ir_module_module where state=%s", ('to remove',))
#remove_modules = map(lambda x: x['name'], cr.dictfetchall())
# Cleanup orphan records
#print "pooler", pool.get('mrp.bom')
#pool.get('ir.model.data')._process_end(cr, 1, remove_modules, noupdate=None)
# for mod_id, mod_name in cr.fetchall():
# cr.execute('select model,res_id from ir_model_data where noupdate=%s and module=%s order by id desc', (False, mod_name,))
# for rmod, rid in cr.fetchall():
# uid = 1
# rmod_module= pool.get(rmod)
# if rmod_module:
# rmod_module.unlink(cr, uid, [rid])
# else:
# _logger.error('Could not locate %s to remove res=%d' % (rmod,rid))
# cr.execute('delete from ir_model_data where module=%s', (mod_name,))
# cr.commit()
# Remove menu items that are not referenced by any of other
# (child) menu item, ir_values, or ir_model_data.
@ -411,8 +414,8 @@ def load_modules(db, force_demo=False, status=None, update_module=False):
_logger.info('removed %d unused menus', cr.rowcount)
# Pretend that modules to be removed are actually uninstalled.
cr.execute("update ir_module_module set state=%s where state=%s", ('uninstalled', 'to remove',))
cr.commit()
#cr.execute("update ir_module_module set state=%s where state=%s", ('uninstalled', 'to remove',))
#cr.commit()
_logger.info('Modules loaded.')
finally:

View File

@ -3018,7 +3018,7 @@ class BaseModel(object):
cr.commit() # start a new transaction
self._add_sql_constraints(cr)
self._add_sql_constraints(cr, context["module"])
if create:
self._execute_sql(cr)
@ -3145,7 +3145,7 @@ class BaseModel(object):
_schema.debug("Create table '%s': m2m relation between '%s' and '%s'", m2m_tbl, self._table, ref)
def _add_sql_constraints(self, cr):
def _add_sql_constraints(self, cr, module):
"""
Modify this model's database table constraints so they match the one in
@ -3196,6 +3196,13 @@ class BaseModel(object):
cr.execute(sql_action['query'])
cr.commit()
_schema.debug(sql_action['msg_ok'])
name_id = 'constraint_'+ conname
cr.execute('select * from ir_model_data where name=%s and module=%s', (name_id, module))
if not cr.rowcount:
cr.execute("INSERT INTO ir_model_data (name,date_init,date_update,module, model) VALUES (%s, now(), now(), %s, %s)", \
(name_id, module, self._name)
)
#
except:
_schema.warning(sql_action['msg_err'])
cr.rollback()
@ -3710,7 +3717,7 @@ class BaseModel(object):
wf_service = netsvc.LocalService("workflow")
for oid in ids:
wf_service.trg_delete(uid, self._name, oid, cr)
self.check_access_rule(cr, uid, ids, 'unlink', context=context)
pool_model_data = self.pool.get('ir.model.data')