credativ team mailing list archive
-
credativ team
-
Mailing list archive
-
Message #05910
[Merge] lp:~0k.io/openupgrade-server/8.0-openupgrade-records-2 into lp:openupgrade-server/8.0
Valentin Lab has proposed merging lp:~0k.io/openupgrade-server/8.0-openupgrade-records-2 into lp:openupgrade-server/8.0.
Requested reviews:
Stefan Rijnhart (Therp) (stefan-therp)
For more details, see:
https://code.launchpad.net/~0k.io/openupgrade-server/8.0-openupgrade-records-2/+merge/214768
This commit contains all the file of the ``openupgrade_records`` module.
You won't find the core or the doc code here, this was split up in:
core: https://code.launchpad.net/~0k.io/openupgrade-server/8.0-openupgrade-base-2
docs: https://code.launchpad.net/~0k.io/openupgrade-server/8.0-openupgrade-doc-2
--
https://code.launchpad.net/~0k.io/openupgrade-server/8.0-openupgrade-records-2/+merge/214768
Your team OpenUpgrade Committers is subscribed to branch lp:openupgrade-server/8.0.
=== added directory 'openerp/addons/openupgrade_records'
=== added file 'openerp/addons/openupgrade_records/__init__.py'
--- openerp/addons/openupgrade_records/__init__.py 1970-01-01 00:00:00 +0000
+++ openerp/addons/openupgrade_records/__init__.py 2014-04-08 14:03:57 +0000
@@ -0,0 +1,2 @@
+import model
+import lib
=== added file 'openerp/addons/openupgrade_records/__openerp__.py'
--- openerp/addons/openupgrade_records/__openerp__.py 1970-01-01 00:00:00 +0000
+++ openerp/addons/openupgrade_records/__openerp__.py 2014-04-08 14:03:57 +0000
@@ -0,0 +1,63 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Management Solution
+# This module Copyright (C) 2012 OpenUpgrade community
+# https://launchpad.net/~openupgrade-committers
+#
+# Contributors:
+# Therp BV <http://therp.nl>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+
+{
+ 'name': 'OpenUpgrade Records',
+ 'version': '0.2',
+ 'category': 'Normal',
+ 'description': """Allow OpenUpgrade records to be
+stored in the database and compare with other servers.
+
+This module depends on OpenERP client lib:
+
+ easy_install openerp-client-lib
+
+""",
+ 'author': 'OpenUpgrade Community',
+ 'maintainer': 'OpenUpgrade Community',
+ 'contributors': ['Therp BV'],
+ 'website': 'https://launchpad.net/~openupgrade-committers',
+ 'depends': [],
+ 'init_xml': [],
+ 'update_xml': [
+ 'view/openupgrade_record.xml',
+ 'view/comparison_config.xml',
+ 'view/analysis_wizard.xml',
+ 'view/generate_records_wizard.xml',
+ 'view/install_all_wizard.xml',
+ 'security/ir.model.access.csv',
+ ],
+ 'demo_xml': [
+ ],
+ 'test': [
+ ],
+ 'installable': True,
+ 'auto_install': False,
+ 'external_dependencies': {
+ 'python' : ['openerplib'],
+ },
+}
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
=== added file 'openerp/addons/openupgrade_records/__terp__.py'
--- openerp/addons/openupgrade_records/__terp__.py 1970-01-01 00:00:00 +0000
+++ openerp/addons/openupgrade_records/__terp__.py 2014-04-08 14:03:57 +0000
@@ -0,0 +1,63 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Management Solution
+# This module Copyright (C) 2012 OpenUpgrade community
+# https://launchpad.net/~openupgrade-committers
+#
+# Contributors:
+# Therp BV <http://therp.nl>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+
+{
+ 'name': 'OpenUpgrade Records',
+ 'version': '0.2',
+ 'category': 'Normal',
+ 'description': """Allow OpenUpgrade records to be
+stored in the database and compare with other servers.
+
+This module depends on OpenERP client lib:
+
+ easy_install openerp-client-lib
+
+""",
+ 'author': 'OpenUpgrade Community',
+ 'maintainer': 'OpenUpgrade Community',
+ 'contributors': ['Therp BV'],
+ 'website': 'https://launchpad.net/~openupgrade-committers',
+ 'depends': [],
+ 'init_xml': [],
+ 'update_xml': [
+ 'view/openupgrade_record.xml',
+ 'view/comparison_config.xml',
+ 'view/analysis_wizard.xml',
+ 'view/generate_records_wizard.xml',
+ 'view/install_all_wizard.xml',
+ 'security/ir.model.access.csv',
+ ],
+ 'demo_xml': [
+ ],
+ 'test': [
+ ],
+ 'installable': True,
+ 'auto_install': False,
+ 'external_dependencies': {
+ 'python' : ['openerplib'],
+ },
+}
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
=== added directory 'openerp/addons/openupgrade_records/lib'
=== added file 'openerp/addons/openupgrade_records/lib/__init__.py'
--- openerp/addons/openupgrade_records/lib/__init__.py 1970-01-01 00:00:00 +0000
+++ openerp/addons/openupgrade_records/lib/__init__.py 2014-04-08 14:03:57 +0000
@@ -0,0 +1,2 @@
+import apriori
+import compare
=== added file 'openerp/addons/openupgrade_records/lib/apriori.py'
--- openerp/addons/openupgrade_records/lib/apriori.py 1970-01-01 00:00:00 +0000
+++ openerp/addons/openupgrade_records/lib/apriori.py 2014-04-08 14:03:57 +0000
@@ -0,0 +1,14 @@
+""" Encode any known changes to the database here
+to help the matching process
+"""
+
+renamed_modules = {
+ 'account_coda': 'l10n_be_coda',
+ 'base_crypt': 'auth_crypt',
+ 'mrp_subproduct': 'mrp_byproduct',
+ 'users_ldap': 'auth_ldap',
+ 'wiki': 'document_page',
+ }
+
+renamed_models = {
+ }
=== added file 'openerp/addons/openupgrade_records/lib/compare.py'
--- openerp/addons/openupgrade_records/lib/compare.py 1970-01-01 00:00:00 +0000
+++ openerp/addons/openupgrade_records/lib/compare.py 2014-04-08 14:03:57 +0000
@@ -0,0 +1,250 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Management Solution
+# This module copyright (C) 2011 Therp BV (<http://therp.nl>).
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+#####################################################################
+# library providing a function to analyse two progressive database
+# layouts from the OpenUpgrade server.
+#####################################################################
+
+import copy
+
+try:
+ from openerp.addons.openupgrade_records.lib import apriori
+except ImportError:
+ from openupgrade_records.lib import apriori
+
+keys = [
+ 'module',
+ 'mode',
+ 'model',
+ 'field',
+ 'type',
+ 'isfunction',
+ 'relation',
+ 'required',
+ 'selection_keys',
+ 'req_default',
+ 'inherits',
+ ]
+
+def module_map(module):
+ return apriori.renamed_modules.get(
+ module, module)
+
+def compare_records(dict_old, dict_new, fields):
+ """
+ Check equivalence of two OpenUpgrade field representations
+ with respect to the keys in the 'fields' arguments.
+ Take apriori knowledge into account for mapped modules or
+ model names.
+ Return True of False.
+ """
+ for field in fields:
+ if field == 'module':
+ if (module_map(dict_old[field]) != dict_new[field]):
+ return False
+ elif field == 'model':
+ if (apriori.renamed_models.get(
+ dict_old[field], dict_old[field]) != dict_new[field]):
+ return False
+ else:
+ if dict_old[field] != dict_new[field]:
+ return False
+ return True
+
+def search(item, item_list, fields):
+ """
+ Find a match of a dictionary in a list of similar dictionaries
+ with respect to the keys in the 'fields' arguments.
+ Return the item if found or None.
+ """
+ for i in item_list:
+ if not compare_records(item, i, fields):
+ continue
+ return i
+ return None
+
+def fieldprint(old, new, field, text, reprs):
+ fieldrepr = "%s (%s)" % (old['field'], old['type'])
+ repr = '%s / %s / %s' % (
+ old['module'].ljust(12), old['model'].ljust(24), fieldrepr.ljust(30))
+ if text:
+ reprs.setdefault(module_map(old['module']), []).append(
+ "%s: %s" % (repr, text))
+ else:
+ reprs.setdefault(module_map(old['module']), []).append(
+ "%s: %s is now \'%s\' ('%s')" % (
+ repr, field, new[field], old[field]))
+
+def report_generic(new, old, attrs, reprs):
+ for attr in attrs:
+ if attr == 'required':
+ if old[attr] != new['required'] and new['required']:
+ text = "now required"
+ if new['req_default']:
+ text += ', default = %s' % new['req_default']
+ fieldprint(old, new, None, text, reprs)
+ elif attr == 'isfunction':
+ if old['isfunction'] != new['isfunction']:
+ if new['isfunction']:
+ text = "now a function"
+ else:
+ text = "not a function anymore"
+ fieldprint(old, new, None, text, reprs)
+ else:
+ if old[attr] != new[attr]:
+ fieldprint(old, new, attr, None, reprs)
+
+def compare_sets(old_records, new_records):
+ """
+ Compare a set of OpenUpgrade field representations.
+ Try to match the equivalent fields in both sets.
+ Return a textual representation of changes in a dictionary with
+ module names as keys. Special case is the 'general' key
+ which contains overall remarks and matching statistics.
+ """
+ reprs = {'general': []}
+
+ for record in old_records + new_records:
+ record['matched'] = False
+ origlen = len(old_records)
+ new_models = set([ column['model'] for column in new_records ])
+ old_models = set([ column['model'] for column in old_records ])
+
+ matched_direct = 0
+ matched_other_module = 0
+ matched_other_type = 0
+ matched_other_name = 0
+ in_obsolete_models = 0
+
+ obsolete_models = []
+ for model in old_models:
+ if model not in new_models:
+ obsolete_models.append(model)
+ reprs['general'].append('obsolete model %s' % model)
+
+ for column in copy.copy(old_records):
+ if column['model'] in obsolete_models:
+ old_records.remove(column)
+ in_obsolete_models += 1
+
+ for model in new_models:
+ if model not in old_models:
+ reprs['general'].append('new model %s' % model)
+
+ def match(match_fields, report_fields, warn=False):
+ count = 0
+ for column in copy.copy(old_records):
+ found = search(column, new_records, match_fields)
+ if found:
+ if warn:
+ pass
+ #print "Tentatively"
+ report_generic(found, column, report_fields, reprs)
+ old_records.remove(column)
+ new_records.remove(found)
+ count += 1
+ return count
+
+ matched_direct = match(
+ ['module', 'mode', 'model', 'field'],
+ ['relation', 'type', 'selection_keys', 'inherits', 'isfunction', 'required'])
+
+ # other module, same type and operation
+ matched_other_module = match(
+ ['mode', 'model', 'field', 'type'],
+ ['module', 'relation', 'selection_keys', 'inherits', 'isfunction', 'required'])
+
+ # other module, same operation, other type
+ matched_other_type = match(
+ ['mode', 'model', 'field'],
+ ['relation', 'type', 'selection_keys', 'inherits', 'isfunction', 'required'])
+
+ # fields with other names
+ #matched_other_name = match(
+ # ['module', 'type', 'relation'],
+ # ['field', 'relation', 'type', 'selection_keys',
+ # 'inherits', 'isfunction', 'required'], warn=True)
+
+ printkeys = [
+ 'relation', 'required', 'selection_keys',
+ 'req_default', 'inherits', 'mode'
+ ]
+ for column in old_records:
+ # we do not care about removed function fields
+ if not column['isfunction']:
+ if column['mode'] == 'create':
+ column['mode'] = ''
+ fieldprint(
+ column, None, None, "DEL " + ", ".join(
+ [k + ': ' + str(column[k]) for k in printkeys if column[k]]
+ ), reprs)
+
+ for column in new_records:
+ # we do not care about newly added function fields
+ if not column['isfunction']:
+ if column['mode'] == 'create':
+ column['mode'] = ''
+ fieldprint(
+ column, None, None, "NEW " + ", ".join(
+ [k + ': ' + str(column[k]) for k in printkeys if column[k]]
+ ), reprs)
+
+ for line in [
+ "# %d fields matched," % (origlen - len(old_records)),
+ "# Direct match: %d" % matched_direct,
+ "# Found in other module: %d" % matched_other_module,
+ "# Found with different type: %d" % matched_other_type,
+ "# Found with different name: %d" % matched_other_name,
+ "# In obsolete models: %d" % in_obsolete_models,
+ "# Not matched: %d" % len(old_records),
+ "# New columns: %d" % len(new_records),
+ ]:
+ reprs['general'].append(line)
+ return reprs
+
+def compare_xml_sets(old_records, new_records):
+ reprs = {}
+ match_fields = ['module', 'model', 'name']
+ for column in copy.copy(old_records):
+ found = search(column, new_records, match_fields)
+ if found:
+ old_records.remove(column)
+ new_records.remove(found)
+
+ for record in old_records:
+ record['old'] = True
+ for record in new_records:
+ record['new'] = True
+
+ sorted_records = sorted(
+ old_records + new_records,
+ key=lambda k: '%s%s%s' % (k['model'].ljust(128), 'old' in k, k['name'])
+ )
+ for entry in sorted_records:
+ if 'old' in entry:
+ content = 'DEL %s: %s' % (entry['model'],
+ entry['name'])
+ elif 'new' in entry:
+ content = 'NEW %s: %s' % (entry['model'],
+ entry['name'])
+ reprs.setdefault(module_map(entry['module']), []).append(content)
+ return reprs
=== added directory 'openerp/addons/openupgrade_records/model'
=== added file 'openerp/addons/openupgrade_records/model/__init__.py'
--- openerp/addons/openupgrade_records/model/__init__.py 1970-01-01 00:00:00 +0000
+++ openerp/addons/openupgrade_records/model/__init__.py 2014-04-08 14:03:57 +0000
@@ -0,0 +1,6 @@
+import openupgrade_record
+import comparison_config
+import analysis_wizard
+import generate_records_wizard
+import install_all_wizard
+
=== added file 'openerp/addons/openupgrade_records/model/analysis_wizard.py'
--- openerp/addons/openupgrade_records/model/analysis_wizard.py 1970-01-01 00:00:00 +0000
+++ openerp/addons/openupgrade_records/model/analysis_wizard.py 2014-04-08 14:03:57 +0000
@@ -0,0 +1,181 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Management Solution
+# This module Copyright (C) 2012 OpenUpgrade community
+# https://launchpad.net/~openupgrade-committers
+#
+# Contributors:
+# Therp BV <http://therp.nl>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+import os
+from openerp.osv import osv, fields
+
+try:
+ from openerp.addons.openupgrade_records.lib import compare
+ from openerp.addons.openupgrade_records.lib import apriori
+ from openerp.addons import get_module_path
+except ImportError:
+ from openupgrade_records.lib import compare
+ from openupgrade_records.lib import apriori
+ from addons import get_module_path
+
+class openupgrade_analysis_wizard(osv.osv_memory):
+ _name = 'openupgrade.analysis.wizard'
+ _description = 'OpenUpgrade Analysis Wizard'
+ _columns = {
+ 'server_config': fields.many2one(
+ 'openupgrade.comparison.config',
+ 'Configuration', required=True),
+ 'state': fields.selection(
+ [('init', 'Init'), ('ready', 'Ready')], 'State',
+ readonly=True),
+ 'log': fields.text('Log'),
+ 'write': fields.boolean(
+ 'Write files',
+ help='Write analysis files to the module directories'
+ ),
+ }
+ _defaults = {
+ 'state': lambda *a: 'init',
+ 'write': lambda *a: True,
+ }
+
+ def get_communication(self, cr, uid, ids, context=None):
+ """
+ Retrieve both sets of database representations,
+ perform the comparison and register the resulting
+ change set
+ """
+ def write_file(
+ module, version, contents, filename='openupgrade_analysis.txt'):
+ module_path = get_module_path(module)
+ if not module_path:
+ return "ERROR: could not find module path:\n"
+ full_path = os.path.join(
+ module_path, 'migrations', version)
+ if not os.path.exists(full_path):
+ try:
+ os.makedirs(full_path)
+ except os.error:
+ return "ERROR: could not create migrations directory:\n"
+ logfile = os.path.join(full_path, filename)
+ try:
+ f = open(logfile, 'w')
+ except Exception:
+ return "ERROR: could not open file %s for writing:\n" % logfile
+ f.write(contents)
+ f.close()
+ return None
+
+ wizard = self.browse(cr, uid, ids[0], context=context)
+ # Retrieve connection and access methods
+ conf_obj = self.pool.get('openupgrade.comparison.config')
+ connection = conf_obj.get_connection(
+ cr, uid, [wizard.server_config.id], context=context)
+ remote_record_obj = connection.get_model('openupgrade.record')
+ local_record_obj = self.pool.get('openupgrade.record')
+
+ # Retrieve field representations and compare
+ remote_records = remote_record_obj.field_dump(context)
+ local_records = local_record_obj.field_dump(cr, uid, context)
+ modules_record = set([record['module'] for record in remote_records + local_records])
+ res = compare.compare_sets(remote_records, local_records)
+
+ # Retrieve xml id representations and compare
+ fields = ['module', 'model', 'name']
+ local_xml_record_ids = local_record_obj.search(
+ cr, uid, [('type', '=', 'xmlid')])
+ remote_xml_record_ids = remote_record_obj.search(
+ [('type', '=', 'xmlid')])
+ local_xml_records = [
+ dict([(field, x[field]) for field in fields])
+ for x in local_record_obj.read(
+ cr, uid, local_xml_record_ids, fields)
+ ]
+ remote_xml_records = [
+ dict([(field, x[field]) for field in fields])
+ for x in remote_record_obj.read(
+ remote_xml_record_ids, fields)
+ ]
+ modules_xml_records = set([record['module'] for record in remote_xml_records + local_xml_records])
+ res_xml = compare.compare_xml_sets(
+ remote_xml_records, local_xml_records)
+
+ # reorder and output the result
+ keys = ['general'] + list(modules_record & modules_xml_records)
+ module_obj = self.pool.get('ir.module.module')
+ module_ids = module_obj.search(
+ cr, uid, [('state', '=', 'installed')])
+ modules = dict([(x['name'], x) for x in module_obj.read(cr, uid, module_ids)])
+ general = ''
+ for key in keys:
+ contents = "---Fields in module '%s'---\n" % key
+ if key in res:
+ contents += '\n'.join([unicode(line) for line in sorted(res[key])])
+ if res[key]:
+ contents += '\n'
+ contents += "---XML records in module '%s'---\n" % key
+ if key in res_xml:
+ contents += '\n'.join([unicode(line) for line in res_xml[key]])
+ if res_xml[key]:
+ contents += '\n'
+ if key not in res and key not in res_xml:
+ contents += '-- nothing has changed in this module'
+ if key == 'general':
+ general += contents
+ continue
+ if key not in modules:
+ general += (
+ "ERROR: module not in list of installed modules:\n"
+ + contents)
+ continue
+ if wizard.write:
+ error = write_file(
+ key, modules[key]['installed_version'], contents)
+ if error:
+ general += error
+ general += contents
+ else:
+ general += contents
+
+ # Store the general log in as many places as possible ;-)
+ if wizard.write and 'base' in modules:
+ write_file(
+ 'base', modules['base']['installed_version'], general,
+ 'openupgrade_general_log.txt')
+ self.pool.get('openupgrade.comparison.config').write(
+ cr, uid, wizard.server_config.id,
+ {'last_log': general})
+ self.write(cr, uid, ids, {'state': 'ready', 'log': general})
+
+ result = {
+ 'name': self._description,
+ 'view_type': 'form',
+ 'view_mode': 'form',
+ 'res_model': 'openupgrade.analysis.wizard',
+ 'domain': [],
+ 'context': context,
+ 'type': 'ir.actions.act_window',
+ #'target': 'new',
+ 'res_id': ids[0],
+ }
+ return result
+
+openupgrade_analysis_wizard()
+
=== added file 'openerp/addons/openupgrade_records/model/comparison_config.py'
--- openerp/addons/openupgrade_records/model/comparison_config.py 1970-01-01 00:00:00 +0000
+++ openerp/addons/openupgrade_records/model/comparison_config.py 2014-04-08 14:03:57 +0000
@@ -0,0 +1,98 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Management Solution
+# This module Copyright (C) 2012 OpenUpgrade community
+# https://launchpad.net/~openupgrade-committers
+#
+# Contributors:
+# Therp BV <http://therp.nl>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+from openerp.osv import osv, fields
+import openerplib
+from openerp.tools.translate import _
+
+class openupgrade_comparison_config(osv.osv):
+ _name = 'openupgrade.comparison.config'
+ _columns = {
+ 'name': fields.char('Name', size=64),
+ 'server': fields.char('Server', size=64, required=True),
+ 'port': fields.integer('Port', required=True),
+ 'protocol': fields.selection(
+ [('http://', 'XML-RPC')],
+ # ('https://', 'XML-RPC Secure')], not supported by libopenerp
+ 'Protocol', required=True),
+ 'database': fields.char('Database', size=64, required=True),
+ 'username': fields.char('Username', size=24, required=True),
+ 'password': fields.char('Password', size=24, required=True, password=True),
+ 'last_log': fields.text('Last log'),
+ }
+ _defaults = {
+ 'port': lambda *a: 8069,
+ 'protocol': lambda *a: 'http://',
+ }
+
+ def get_connection(self, cr, uid, ids, context=None):
+ if not ids:
+ raise osv.except_osv(
+ _("Cannot connect"), _("Invalid id passed."))
+ conf = self.read(cr, uid, ids[0], context=None)
+ return openerplib.get_connection(
+ hostname=conf['server'],
+ database=conf['database'],
+ login=conf['username'],
+ password=conf['password'],
+ port=conf['port'],
+ )
+
+ def test_connection(self, cr, uid, ids, context=None):
+ try:
+ connection = self.get_connection(cr, uid, [ids[0]], context)
+ user_model = connection.get_model("res.users")
+ ids = user_model.search([("login", "=", "admin")])
+ user_info = user_model.read(ids[0], ["name"])
+ except Exception, e:
+ raise osv.except_osv(
+ _("Connection failed."), unicode(e))
+ raise osv.except_osv(
+ _("Connection succesful."),
+ _("%s is connected.") % user_info["name"]
+ )
+
+ def analyze(self, cr, uid, ids, context=None):
+ """
+ Run the analysis wizard
+ """
+ wizard_obj = self.pool.get('openupgrade.analysis.wizard')
+ wizard_id = wizard_obj.create(
+ cr, uid, {'server_config': ids[0]}, context)
+ result = {
+ 'name': wizard_obj._description,
+ 'view_type': 'form',
+ 'view_mode': 'form',
+ 'res_model': 'openupgrade.analysis.wizard',
+ 'domain': [],
+ 'context': context,
+ 'type': 'ir.actions.act_window',
+ 'target': 'new',
+ 'res_id': wizard_id,
+ 'nodestroy': True,
+ }
+ return result
+
+openupgrade_comparison_config()
=== added file 'openerp/addons/openupgrade_records/model/generate_records_wizard.py'
--- openerp/addons/openupgrade_records/model/generate_records_wizard.py 1970-01-01 00:00:00 +0000
+++ openerp/addons/openupgrade_records/model/generate_records_wizard.py 2014-04-08 14:03:57 +0000
@@ -0,0 +1,90 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Management Solution
+# This module Copyright (C) 2012 OpenUpgrade community
+# https://launchpad.net/~openupgrade-committers
+#
+# Contributors:
+# Therp BV <http://therp.nl>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+import os
+from openerp.osv import osv, fields
+from openerp import pooler
+try:
+ from openerp.openupgrade import openupgrade_tools
+except ImportError:
+ from openupgrade import openupgrade_tools
+
+class generate_records_wizard(osv.osv_memory):
+ _name = 'openupgrade.generate.records.wizard'
+ _description = 'OpenUpgrade Generate Records Wizard'
+ _columns = {
+ 'state': fields.selection([('init', 'init'), ('ready', 'ready')], 'State'),
+ }
+ _defaults = {
+ 'state': lambda *a: 'init',
+ }
+
+ def generate(self, cr, uid, ids, context=None):
+ """
+ Main wizard step. Make sure that all modules are up-to-date,
+ then reinitialize all installed modules.
+ Equivalent of running the server with '-d <database> --init all'
+
+ The goal of this is to fill the records table.
+
+ TODO: update module list and versions, then update all modules?
+ """
+ # Truncate the records table
+ if (openupgrade_tools.table_exists(cr, 'openupgrade_attribute') and
+ openupgrade_tools.table_exists(cr, 'openupgrade_record')):
+ cr.execute(
+ 'TRUNCATE openupgrade_attribute, openupgrade_record;'
+ )
+
+ # Need to get all modules in state 'installed'
+ module_obj = self.pool.get('ir.module.module')
+ module_ids = module_obj.search(
+ cr, uid, [('state', 'in', ['to install', 'to upgrade'])])
+ if module_ids:
+ cr.commit()
+ _db, pool = pooler.restart_pool(cr.dbname, update_module=True)
+ # Did we succeed above?
+ module_ids = module_obj.search(
+ cr, uid, [('state', 'in', ['to install', 'to upgrade'])])
+ if module_ids:
+ modules = module_obj.read(
+ cr, uid, module_ids, ['name'], context=context)
+ raise except_osv(
+ "Cannot reliably generate records",
+ ("Cannot seem to install or upgrade modules " +
+ ', '.join([x['name'] for x in modules])))
+ # Now reinitialize all installed modules
+ module_ids = module_obj.search(
+ cr, uid, [('state', '=', 'installed')])
+ module_obj.write(
+ cr, uid, module_ids, {'state': 'to install'})
+ cr.commit()
+ _db, pool = pooler.restart_pool(cr.dbname, update_module=True)
+ self.write(cr, uid, ids, {'state': 'ready'})
+ # and we are done
+ return True
+
+generate_records_wizard()
+
=== added file 'openerp/addons/openupgrade_records/model/install_all_wizard.py'
--- openerp/addons/openupgrade_records/model/install_all_wizard.py 1970-01-01 00:00:00 +0000
+++ openerp/addons/openupgrade_records/model/install_all_wizard.py 2014-04-08 14:03:57 +0000
@@ -0,0 +1,113 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Management Solution
+# This module Copyright (C) 2012 OpenUpgrade community
+# https://launchpad.net/~openupgrade-committers
+#
+# Contributors:
+# Therp BV <http://therp.nl>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+import time
+import os
+from openerp.osv import osv, fields
+from openerp import pooler
+
+class install_all_wizard(osv.osv_memory):
+ _name = 'openupgrade.install.all.wizard'
+ _description = 'OpenUpgrade Install All Wizard'
+ _columns = {
+ 'state': fields.selection([('init', 'init'), ('ready', 'ready')], 'State', readonly=True),
+ 'to_install': fields.integer('Number of modules to install', readonly=True),
+ }
+ _defaults = {
+ 'state': lambda *a: 'init',
+ }
+
+
+ def default_get(self, cr, uid, fields, context=None):
+ """
+ Update module list and retrieve the number
+ of installable modules
+ """
+ res = super(install_all_wizard, self).default_get(
+ cr, uid, fields, context=None)
+ module_obj = self.pool.get('ir.module.module')
+ update, add = module_obj.update_list(cr, uid,)
+ print "%s modules added" % add
+ module_ids = module_obj.search(
+ cr, uid, [('state', 'not in', ['installed', 'uninstallable', 'unknown'])])
+ res.update(
+ {'to_install': module_ids and len(module_ids) or False}
+ )
+ return res
+
+ def quirk_fiscalyear(self, cr, uid, ids, context=None):
+ """
+ Install account module first and create a fiscal year,
+ in order to prevent "No fiscal year defined" exception
+ during an upgrade or reinstallation of the account module.
+
+ Refer to account_fiscalyear.find(), which is called as
+ a default function by the orm upon module upgrade.
+ """
+ module_obj = self.pool.get('ir.module.module')
+ pool = self.pool
+ # Retrieve status of the account module
+ account_module_id = module_obj.search(
+ cr, uid, [('name', '=', 'account')], context=context)[0]
+ state = module_obj.read(
+ cr, uid, account_module_id, ['state'], context=context)['state']
+ if state != 'installed':
+ # Cancel installation of other modules
+ module_ids = module_obj.search(
+ cr, uid, [('state', '=', 'to install')])
+ module_obj.write(cr, uid, module_ids, {'state': 'uninstalled'})
+ # Mark the module and its dependencies
+ module_obj.button_install(cr, uid, [account_module_id])
+ # Install account module
+ cr.commit()
+ _db, pool = pooler.restart_pool(cr.dbname, update_module=True)
+ # get or create today's fiscal year
+ fy_obj = pool.get('account.fiscalyear')
+ if not fy_obj.find(cr, uid, False, exception=False, context=context):
+ fy_obj.create(cr, uid, {
+ 'name': time.strftime('%Y'),
+ 'code': time.strftime('%Y'),
+ 'date_start': "%s-01-01" % time.strftime('%Y'),
+ 'date_stop': "%s-12-31" % time.strftime('%Y'),
+ })
+
+ def install_all(self, cr, uid, ids, context=None):
+ """
+ Main wizard step. Set all installable modules to install
+ and actually install them.
+ """
+ module_obj = self.pool.get('ir.module.module')
+ module_ids = module_obj.search(
+ cr, uid, [('state', 'not in', ['installed', 'uninstallable', 'unknown'])])
+ if module_ids:
+ module_obj.write(
+ cr, uid, module_ids, {'state': 'to install'})
+ cr.commit()
+ _db, pool = pooler.restart_pool(cr.dbname, update_module=True)
+ self.write(cr, uid, ids, {'state': 'ready'})
+ return True
+
+install_all_wizard()
+
=== added file 'openerp/addons/openupgrade_records/model/openupgrade_record.py'
--- openerp/addons/openupgrade_records/model/openupgrade_record.py 1970-01-01 00:00:00 +0000
+++ openerp/addons/openupgrade_records/model/openupgrade_record.py 2014-04-08 14:03:57 +0000
@@ -0,0 +1,111 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Management Solution
+# This module Copyright (C) 2012 OpenUpgrade community
+# https://launchpad.net/~openupgrade-committers
+#
+# Contributors:
+# Therp BV <http://therp.nl>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+from openerp.osv import osv, fields
+
+# Cannot use forward references in 6.0
+class openupgrade_record(osv.osv):
+ _name = 'openupgrade.record'
+openupgrade_record()
+
+class openupgrade_attribute(osv.osv):
+ _name = 'openupgrade.attribute'
+ _rec_name = 'name'
+ _columns = {
+ 'name': fields.char(
+ 'Name', size=24,
+ readonly=True,
+ ),
+ 'value': fields.char(
+ 'Value',
+ size=4096,
+ readonly=True,
+ ),
+ 'record_id': fields.many2one(
+ 'openupgrade.record', ondelete='CASCADE',
+ readonly=True,
+ ),
+ }
+openupgrade_attribute()
+
+class openupgrade_record(osv.osv):
+ _inherit = 'openupgrade.record'
+
+ _columns = {
+ 'name': fields.char('Name', size=256, readonly=True),
+ 'module': fields.char('Module', size=128, readonly=True),
+ 'model': fields.char('Model', size=128, readonly=True),
+ 'field': fields.char('Field', size=128, readonly=True),
+ 'mode': fields.selection(
+ [('create', 'Create'), ('modify', 'Modify')],
+ 'Mode',
+ help='Set to Create if a field is newly created '
+ 'in this module. If this module modifies an attribute of an '
+ 'exting field, set to Modify.',
+ readonly=True,
+ ),
+ 'type': fields.selection(
+ [('field', 'Field'), ('xmlid', 'XML ID')],
+ 'Type',
+ readonly=True,
+ ),
+ 'attribute_ids': fields.one2many(
+ 'openupgrade.attribute', 'record_id', 'Attributes',
+ readonly=True,
+ ),
+ }
+ def field_dump(self, cr, uid, context=None):
+ keys = [
+ 'module',
+ 'mode',
+ 'model',
+ 'field',
+ 'type',
+ 'isfunction',
+ 'relation',
+ 'required',
+ 'selection_keys',
+ 'req_default',
+ 'inherits',
+ ]
+
+ template = dict([(x, False) for x in keys])
+ ids = self.search(cr, uid, [('type', '=', 'field')], context=context)
+ records = self.browse(cr, uid, ids, context=context)
+ data = []
+ for record in records:
+ repr = template.copy()
+ repr.update({
+ 'module': record.module,
+ 'model': record.model,
+ 'field': record.field,
+ 'mode': record.mode,
+ })
+ repr.update(
+ dict([(x.name, x.value) for x in record.attribute_ids]))
+ data.append(repr)
+ return data
+
+openupgrade_record()
=== added directory 'openerp/addons/openupgrade_records/security'
=== added file 'openerp/addons/openupgrade_records/security/ir.model.access.csv'
--- openerp/addons/openupgrade_records/security/ir.model.access.csv 1970-01-01 00:00:00 +0000
+++ openerp/addons/openupgrade_records/security/ir.model.access.csv 2014-04-08 14:03:57 +0000
@@ -0,0 +1,3 @@
+"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
+"access_openupgrade_record","openupgrade.record all","model_openupgrade_record",,1,0,0,0
+"access_openupgrade_attribute","openupgrade.attribute all","model_openupgrade_attribute",,1,0,0,0
=== added directory 'openerp/addons/openupgrade_records/view'
=== added file 'openerp/addons/openupgrade_records/view/analysis_wizard.xml'
--- openerp/addons/openupgrade_records/view/analysis_wizard.xml 1970-01-01 00:00:00 +0000
+++ openerp/addons/openupgrade_records/view/analysis_wizard.xml 2014-04-08 14:03:57 +0000
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+ <data>
+ <record id="view_openupgrade_analysis_wizard_form" model="ir.ui.view">
+ <field name="name">view.openupgrade.analysis_wizard.form</field>
+ <field name="model">openupgrade.analysis.wizard</field>
+ <field name="type">form</field>
+ <field name="arch" type="xml">
+ <form string="OpenUpgrade Analysis Wizard">
+ <field name="server_config" readonly="1"/>
+ <field name="state"/>
+ <field name="log" colspan="4"
+ attrs="{'invisible': [('state', '!=', 'ready')]}"/>
+ <field name="write"
+ attrs="{'readonly': [('state', '!=', 'init')]}"/>
+ <button icon="gtk-close"
+ special="cancel"
+ string="Close"
+ />
+ <button icon="gtk-ok"
+ string="Create"
+ name="get_communication"
+ type="object"
+ states="init"
+ />
+ </form>
+ </field>
+ </record>
+ </data>
+</openerp>
=== added file 'openerp/addons/openupgrade_records/view/comparison_config.xml'
--- openerp/addons/openupgrade_records/view/comparison_config.xml 1970-01-01 00:00:00 +0000
+++ openerp/addons/openupgrade_records/view/comparison_config.xml 2014-04-08 14:03:57 +0000
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+ <data>
+ <record id="view_openupgrade_comparison_config_tree" model="ir.ui.view">
+ <field name="name">view.openupgrade.comparison_config.tree</field>
+ <field name="model">openupgrade.comparison.config</field>
+ <field name="type">tree</field>
+ <field name="arch" type="xml">
+ <tree string="OpenUpgrade Comparison Config">
+ <field name="name" select="1"/>
+ <field name="protocol"/>
+ <field name="server" select="1"/>
+ <field name="port" select="1"/>
+ <field name="database" select="1"/>
+ </tree>
+ </field>
+ </record>
+ <record id="view_openupgrade_comparison_config_form" model="ir.ui.view">
+ <field name="name">view.openupgrade.comparison_config.form</field>
+ <field name="model">openupgrade.comparison.config</field>
+ <field name="type">form</field>
+ <field name="arch" type="xml">
+ <form string="OpenUpgrade Comparison Config">
+ <field name="name"/>
+ <field name="protocol"/>
+ <field name="server"/>
+ <field name="port"/>
+ <field name="database"/>
+ <field name="username"/>
+ <field name="password" password="1"/>
+ <button
+ name="test_connection"
+ string="Test Connection"
+ type="object" icon="gtk-network"
+ colspan="2"
+ />
+ <newline/>
+ <button
+ name="analyze"
+ string="Perform Analysis"
+ type="object" icon="gtk-execute"
+ colspan="2"
+ />
+ <separator string="Last log" colspan="4"/>
+ <field name="last_log" nolabel="1" colspan="4"/>
+ </form>
+ </field>
+ </record>
+ <record id="action_openupgrade_comparison_config_tree" model="ir.actions.act_window">
+ <field name="name">OpenUpgrade Comparison Configs</field>
+ <field name="type">ir.actions.act_window</field>
+ <field name="res_model">openupgrade.comparison.config</field>
+ <field name="view_type">form</field>
+ </record>
+ <menuitem
+ action="action_openupgrade_comparison_config_tree"
+ id="menu_openupgrade_comparison_config"
+ name="Comparison Configurations"
+ parent="menu_openupgrade"
+ />
+ </data>
+</openerp>
=== added file 'openerp/addons/openupgrade_records/view/generate_records_wizard.xml'
--- openerp/addons/openupgrade_records/view/generate_records_wizard.xml 1970-01-01 00:00:00 +0000
+++ openerp/addons/openupgrade_records/view/generate_records_wizard.xml 2014-04-08 14:03:57 +0000
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+ <data>
+ <record id="view_openupgrade_generate_records_wizard_form" model="ir.ui.view">
+ <field name="name">view.openupgrade.generate_records_wizard.form</field>
+ <field name="model">openupgrade.generate.records.wizard</field>
+ <field name="type">form</field>
+ <field name="arch" type="xml">
+ <form string="OpenUpgrade Generate Records Wizard">
+ <group states="init" colspan="4">
+ <label string="This will reinitialize all the modules installed on this database. Do not continue if you use this database in production."
+ />
+ <button icon="gtk-close"
+ special="cancel"
+ string="Cancel"
+ />
+ <button icon="gtk-ok"
+ string="Continue"
+ name="generate"
+ type="object"
+ />
+ </group>
+ <group states="ready" colspan="4">
+ <label string="Modules initialized and records created"
+ />
+ <field name="state" invisible="1"/>
+ <button icon="gtk-close"
+ special="cancel"
+ string="Close"
+ />
+ </group>
+ </form>
+ </field>
+ </record>
+
+ <record id="action_generate_records" model="ir.actions.act_window">
+ <field name="name">Generate Records</field>
+ <field name="type">ir.actions.act_window</field>
+ <field name="res_model">openupgrade.generate.records.wizard</field>
+ <field name="view_type">form</field>
+ <field name="view_mode">form,tree</field>
+ <field name="target">new</field>
+ </record>
+
+ <menuitem name="Generate Records"
+ id="menu_openupgrade_generate_records"
+ parent="menu_openupgrade"
+ action="action_generate_records"
+ sequence="15"/>
+
+ </data>
+</openerp>
=== added file 'openerp/addons/openupgrade_records/view/install_all_wizard.xml'
--- openerp/addons/openupgrade_records/view/install_all_wizard.xml 1970-01-01 00:00:00 +0000
+++ openerp/addons/openupgrade_records/view/install_all_wizard.xml 2014-04-08 14:03:57 +0000
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+ <data>
+ <record id="view_openupgrade_install_all_wizard_form" model="ir.ui.view">
+ <field name="name">view.openupgrade.install_all_wizard.form</field>
+ <field name="model">openupgrade.install.all.wizard</field>
+ <field name="type">form</field>
+ <field name="arch" type="xml">
+ <form string="OpenUpgrade Install All Modules Wizard">
+ <group states="init" colspan="4">
+ <label string="This will install all modules on the database. Do not continue if you use this database in production." colspan="4"
+ />
+ <field name="to_install"/>
+ <newline/>
+ <button icon="gtk-close"
+ special="cancel"
+ string="Cancel"
+ />
+ <button icon="gtk-ok"
+ string="Continue"
+ name="install_all"
+ type="object"
+ />
+ </group>
+ <group states="ready" colspan="4">
+ <label string="Modules installed"
+ />
+ <field name="state" invisible="1"/>
+ <button icon="gtk-close"
+ special="cancel"
+ string="Close"
+ />
+ </group>
+ </form>
+ </field>
+ </record>
+
+ <record id="action_install_all" model="ir.actions.act_window">
+ <field name="name">Install All Modules</field>
+ <field name="type">ir.actions.act_window</field>
+ <field name="res_model">openupgrade.install.all.wizard</field>
+ <field name="view_type">form</field>
+ <field name="view_mode">form,tree</field>
+ <field name="target">new</field>
+ </record>
+
+ <menuitem name="Install All Modules"
+ id="menu_openupgrade_install_all"
+ parent="menu_openupgrade"
+ action="action_install_all"
+ sequence="14"/>
+
+ </data>
+</openerp>
=== added file 'openerp/addons/openupgrade_records/view/openupgrade_record.xml'
--- openerp/addons/openupgrade_records/view/openupgrade_record.xml 1970-01-01 00:00:00 +0000
+++ openerp/addons/openupgrade_records/view/openupgrade_record.xml 2014-04-08 14:03:57 +0000
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+ <data>
+ <!-- Top level menu under 'Database structure' -->
+ <menuitem
+ id="menu_openupgrade"
+ name="OpenUpgrade Development"
+ parent="base.menu_administration"
+ sequence="99"
+ />
+ <record id="view_openupgrade_record_tree" model="ir.ui.view">
+ <field name="name">view.openupgrade.record.tree</field>
+ <field name="model">openupgrade.record</field>
+ <field name="type">tree</field>
+ <field name="arch" type="xml">
+ <tree string="OpenUpgrade Records">
+ <field name="module" select="1"/>
+ <field name="model" select="1"/>
+ <field name="field" select="1"/>
+ <field name="name" select="1"/>
+ <field name="type" select="1"/>
+ <field name="mode" select="1"/>
+ </tree>
+ </field>
+ </record>
+ <record id="view_openupgrade_record_form" model="ir.ui.view">
+ <field name="name">view.openupgrade.record.form</field>
+ <field name="model">openupgrade.record</field>
+ <field name="type">form</field>
+ <field name="arch" type="xml">
+ <form string="OpenUpgrade Record">
+ <field name="module" select="1"/>
+ <field name="model" select="1"/>
+ <field name="field" select="1"/>
+ <field name="name" select="1"/>
+ <field name="type" select="1"/>
+ <field name="mode" select="1"/>
+ <separator string="Attributes" colspan="4"/>
+ <field name="attribute_ids" mode="tree,form" nolabel="1" colspan="4">
+ <tree string="Attributes">
+ <field name="name"/>
+ <field name="value"/>
+ </tree>
+ <form string="Attribute">
+ <field name="name"/>
+ <field name="value"/>
+ </form>
+ </field>
+ </form>
+ </field>
+ </record>
+ <record id="action_openupgrade_record_tree" model="ir.actions.act_window">
+ <field name="name">OpenUpgrade Records</field>
+ <field name="type">ir.actions.act_window</field>
+ <field name="res_model">openupgrade.record</field>
+ <field name="view_type">form</field>
+ </record>
+ <menuitem
+ action="action_openupgrade_record_tree"
+ id="menu_openupgrade_records"
+ name="Records"
+ parent="menu_openupgrade"
+ />
+ </data>
+</openerp>
Follow ups