Merge PR #2417 into 16.0

Signed-off-by pedrobaeza
pull/2468/head
OCA-git-bot 2022-10-19 14:25:39 +00:00
commit c5de75f867
53 changed files with 4825 additions and 0 deletions

5
requirements.txt 100644
View File

@ -0,0 +1,5 @@
# generated from manifests external_dependencies
dataclasses
mako
odoorpc
openupgradelib

View File

@ -0,0 +1 @@
../../../../upgrade_analysis

View File

@ -0,0 +1,6 @@
import setuptools
setuptools.setup(
setup_requires=['setuptools-odoo'],
odoo_addon=True,
)

View File

@ -0,0 +1,106 @@
================
Upgrade Analysis
================
.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
:target: https://odoo-community.org/page/development-status
:alt: Beta
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fserver--tools-lightgray.png?logo=github
:target: https://github.com/OCA/server-tools/tree/16.0/upgrade_analysis
:alt: OCA/server-tools
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/server-tools-16-0/server-tools-16-0-upgrade_analysis
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png
:target: https://runbot.odoo-community.org/runbot/149/16.0
:alt: Try me on Runbot
|badge1| |badge2| |badge3| |badge4| |badge5|
This module provides the tool to generate the database analysis files that indicate how the Odoo data model and module data have changed between two versions of Odoo. Database analysis files for the core modules are included in the OpenUpgrade distribution so as a migration script developer you will not usually need to use this tool yourself. If you do need to run your analysis of a custom set of modules, please refer to the documentation here: https://doc.therp.nl/openupgrade/analysis.html
This module is just a tool, a continuation of the old openupgrade_records in OpenUpgrade in previous versions. It's not recommended to have this module in a production database.
**Table of contents**
.. contents::
:local:
Usage
=====
`Usage instructions <https://oca.github.io/OpenUpgrade/analyse.html>`_
Known issues / Roadmap
======================
* Log removed modules in the module that owned them (#468)
* Detect renamed many2many tables (#213)
* Make sure that the ``migration_analysis.txt`` file is always generated in all cases. (See: https://github.com/OCA/OpenUpgrade/pull/3209#issuecomment-1157449981)
Bug Tracker
===========
Bugs are tracked on `GitHub Issues <https://github.com/OCA/server-tools/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us smashing it by providing a detailed and welcomed
`feedback <https://github.com/OCA/server-tools/issues/new?body=module:%20upgrade_analysis%0Aversion:%2016.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
Do not contact contributors directly about support or help with technical issues.
Credits
=======
Authors
~~~~~~~
* Therp BV
* Opener B.V.
* GRAP
Contributors
~~~~~~~~~~~~
* Stefan Rijnhart <stefan@opener.amsterdam>
* Holger Brunn <hbrunn@therp.nl>
* Pedro M. Baeza <pedro.baeza@gmail.com>
* Ferdinand Gassauer <gass@cc-l-12.chircar.at>
* Florent Xicluna <florent.xicluna@gmail.com>
* Miquel Raïch <miquel.raich@forgeflow.com>
* Sylvain LE GAL <https://twitter.com/legalsylvain>
Maintainers
~~~~~~~~~~~
This module is maintained by the OCA.
.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org
OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.
.. |maintainer-StefanRijnhart| image:: https://github.com/StefanRijnhart.png?size=40px
:target: https://github.com/StefanRijnhart
:alt: StefanRijnhart
.. |maintainer-legalsylvain| image:: https://github.com/legalsylvain.png?size=40px
:target: https://github.com/legalsylvain
:alt: legalsylvain
Current `maintainers <https://odoo-community.org/page/maintainer-role>`__:
|maintainer-StefanRijnhart| |maintainer-legalsylvain|
This module is part of the `OCA/server-tools <https://github.com/OCA/server-tools/tree/16.0/upgrade_analysis>`_ project on GitHub.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

View File

@ -0,0 +1,6 @@
from . import odoo_patch
from . import models
from . import wizards
from . import blacklist
from . import compare
from . import upgrade_log

View File

@ -0,0 +1,28 @@
# Copyright 2011-2015 Therp BV <https://therp.nl>
# Copyright 2016 Opener B.V. <https://opener.am>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
{
"name": "Upgrade Analysis",
"summary": "Performs a difference analysis between modules"
" installed on two different Odoo instances",
"version": "16.0.1.0.0",
"category": "Migration",
"author": "Therp BV, Opener B.V., GRAP, Odoo Community Association (OCA)",
"maintainers": ["StefanRijnhart", "legalsylvain"],
"website": "https://github.com/OCA/server-tools",
"data": [
"security/ir.model.access.csv",
"views/menu.xml",
"views/view_upgrade_comparison_config.xml",
"views/view_upgrade_analysis.xml",
"views/view_upgrade_record.xml",
"wizards/view_upgrade_generate_record_wizard.xml",
"wizards/view_upgrade_install_wizard.xml",
],
"installable": True,
"depends": ["base"],
"external_dependencies": {
"python": ["mako", "dataclasses", "odoorpc", "openupgradelib"],
},
"license": "AGPL-3",
}

View File

@ -0,0 +1,10 @@
BLACKLIST_MODULES = []
# the hw_* modules are not affected by a migration as they don't
# contain any ORM functionality, but they do start up threads that
# delay the process and spit out annoying log messages continuously.
# We also don't want to analyze tests modules
BLACKLIST_MODULES_STARTS_WITH = ["hw_", "test_"]
BLACKLIST_MODULES_ENDS_WITH = ["_test"]

View File

@ -0,0 +1,547 @@
# Copyright 2011-2015 Therp BV <https://therp.nl>
# Copyright 2015-2016 Opener B.V. <https://opener.am>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
# flake8: noqa: C901
#####################################################################
# library providing a function to analyse two progressive database
# layouts from the OpenUpgrade server.
#####################################################################
import collections
import copy
try:
from odoo.addons.openupgrade_scripts import apriori
except ImportError:
from dataclasses import dataclass, field as dc_field
@dataclass
class NullApriori:
renamed_modules: dict = dc_field(default_factory=dict)
merged_modules: dict = dc_field(default_factory=dict)
renamed_models: dict = dc_field(default_factory=dict)
merged_models: dict = dc_field(default_factory=dict)
apriori = NullApriori()
def module_map(module):
return apriori.renamed_modules.get(
module, apriori.merged_modules.get(module, module)
)
def model_rename_map(model):
return apriori.renamed_models.get(model, model)
def model_map(model):
return apriori.renamed_models.get(model, apriori.merged_models.get(model, model))
def inv_model_map(model):
inv_model_map_dict = {v: k for k, v in apriori.renamed_models.items()}
return inv_model_map_dict.get(model, model)
IGNORE_FIELDS = [
"create_date",
"create_uid",
"id",
"write_date",
"write_uid",
]
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["module"]) != dict_new["module"]:
return False
elif field == "model":
if model_rename_map(dict_old["model"]) != dict_new["model"]:
return False
elif field == "other_prefix":
if (
dict_old["module"] != dict_old["prefix"]
or dict_new["module"] != dict_new["prefix"]
):
return False
if dict_old["model"] == "ir.ui.view":
# basically, to avoid the assets_backend case
return False
elif dict_old[field] != dict_new[field]:
return False
return True
def search(item, item_list, fields, get_all=None):
"""
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.
"""
all_found = []
for other in item_list:
if not compare_records(item, other, fields):
continue
if not get_all:
return other
if other["module"] != other["prefix"]:
all_found.append(other)
if get_all:
return all_found
# search for renamed fields
if "field" in fields:
for other in item_list:
if not item["field"] or item["field"] is not None or item["isproperty"]:
continue
if compare_records(dict(item, field=other["field"]), other, fields):
return other
return None
def fieldprint(old, new, field, text, reprs):
fieldrepr = "{}".format(old["field"])
if old["field"] not in ("_inherits", "_order"):
fieldrepr += " ({})".format(old["type"])
fullrepr = "{:<12} / {:<24} / {:<30}".format(old["module"], old["model"], fieldrepr)
if not text:
text = "{} is now '{}' ('{}')".format(field, new[field], old[field])
if field in ("column1", "column2"):
text += " [%s]" % old["table"]
if field == "relation":
text += " [nothing to do]"
reprs[module_map(old["module"])].append("{}: {}".format(fullrepr, text))
if field == "module":
text = "previously in module %s" % old[field]
fullrepr = "{:<12} / {:<24} / {:<30}".format(
new["module"], old["model"], fieldrepr
)
reprs[module_map(new["module"])].append("{}: {}".format(fullrepr, text))
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"
fieldprint(old, new, "", text, reprs)
elif attr == "stored":
if old[attr] != new[attr]:
if new["stored"]:
text = "is now stored"
else:
text = "not stored anymore"
fieldprint(old, new, "", text, reprs)
elif attr == "isfunction":
if old[attr] != new[attr]:
if new["isfunction"]:
text = "now a function"
else:
text = "not a function anymore"
fieldprint(old, new, "", text, reprs)
elif attr == "isproperty":
if old[attr] != new[attr]:
if new[attr]:
text = "now a property"
else:
text = "not a property anymore"
fieldprint(old, new, "", text, reprs)
elif attr == "isrelated":
if old[attr] != new[attr]:
if new[attr]:
text = "now related"
else:
text = "not related anymore"
fieldprint(old, new, "", text, reprs)
elif attr == "table":
if old[attr] != new[attr]:
fieldprint(old, new, attr, "", reprs)
if old[attr] and new[attr]:
if old["column1"] != new["column1"]:
fieldprint(old, new, "column1", "", reprs)
if old["column2"] != new["column2"]:
fieldprint(old, new, "column2", "", reprs)
elif old[attr] != new[attr]:
fieldprint(old, new, attr, "", 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 = collections.defaultdict(list)
def clean_records(records):
result = []
for record in records:
if record["field"] not in IGNORE_FIELDS:
result.append(record)
return result
old_records = clean_records(old_records)
new_records = clean_records(new_records)
origlen = len(old_records)
new_models = {column["model"] for column in new_records}
old_models = {column["model"] for column in old_records}
matched_direct = 0
matched_other_module = 0
matched_other_type = 0
in_obsolete_models = 0
obsolete_models = []
for model in old_models:
if model not in new_models:
if model_map(model) not in new_models:
obsolete_models.append(model)
non_obsolete_old_records = []
for column in copy.copy(old_records):
if column["model"] in obsolete_models:
in_obsolete_models += 1
else:
non_obsolete_old_records.append(column)
def match(match_fields, report_fields, warn=False):
count = 0
for column in copy.copy(non_obsolete_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)
non_obsolete_old_records.remove(column)
new_records.remove(found)
count += 1
return count
matched_direct = match(
["module", "mode", "model", "field"],
[
"relation",
"type",
"selection_keys",
"_inherits",
"stored",
"isfunction",
"isrelated",
"required",
"table",
"_order",
],
)
# other module, same type and operation
matched_other_module = match(
["mode", "model", "field", "type"],
[
"module",
"relation",
"selection_keys",
"_inherits",
"stored",
"isfunction",
"isrelated",
"required",
"table",
"_order",
],
)
# other module, same operation, other type
matched_other_type = match(
["module", "mode", "model", "field"],
[
"relation",
"type",
"selection_keys",
"_inherits",
"stored",
"isfunction",
"isrelated",
"required",
"table",
"_order",
],
)
# Info that is displayed for deleted fields
printkeys_old = [
"relation",
"required",
"selection_keys",
"_inherits",
"mode",
"attachment",
]
# Info that is displayed for new fields
printkeys_new = printkeys_old + [
"hasdefault",
]
for column in old_records:
if column["field"] == "_order":
continue
# we do not care about removed non stored function fields
if not column["stored"] and (column["isfunction"] or column["isrelated"]):
continue
if column["mode"] == "create":
column["mode"] = ""
extra_message = ", ".join(
[
k + ": " + str(column[k]) if k != str(column[k]) else k
for k in printkeys_old
if column[k]
]
)
if extra_message:
extra_message = " " + extra_message
fieldprint(column, "", "", "DEL" + extra_message, reprs)
for column in new_records:
if column["field"] == "_order":
continue
# we do not care about newly added non stored function fields
if not column["stored"] and (column["isfunction"] or column["isrelated"]):
continue
if column["mode"] == "create":
column["mode"] = ""
printkeys = printkeys_new.copy()
if column["isfunction"] or column["isrelated"]:
printkeys.extend(["isfunction", "isrelated", "stored"])
extra_message = ", ".join(
[
k + ": " + str(column[k]) if k != str(column[k]) else k
for k in printkeys
if column[k]
]
)
if extra_message:
extra_message = " " + extra_message
fieldprint(column, "", "", "NEW" + extra_message, 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,
"# 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 = collections.defaultdict(list)
def match_updates(match_fields):
old_updated, new_updated = {}, {}
for column in copy.copy(old_records):
found_all = search(column, old_records, match_fields, True)
for found in found_all:
old_records.remove(found)
for column in copy.copy(new_records):
found_all = search(column, new_records, match_fields, True)
for found in found_all:
new_records.remove(found)
matched_records = list(old_updated.values()) + list(new_updated.values())
matched_records = [y for x in matched_records for y in x]
return matched_records
def match(match_fields, match_type="direct"):
matched_records = []
for column in copy.copy(old_records):
found = search(column, new_records, match_fields)
if found:
old_records.remove(column)
new_records.remove(found)
if match_type != "direct":
column["old"] = True
found["new"] = True
column[match_type] = found["module"]
found[match_type] = column["module"]
found["domain"] = (
column["domain"] != found["domain"]
and column["domain"] != "[]"
and found["domain"] is False
)
column["domain"] = False
found["definition"] = (
column["definition"]
and column["definition"] != found["definition"]
and "is now '{}' ('{}')".format(
found["definition"], column["definition"]
)
)
column["definition"] = False
column["noupdate_switched"] = False
found["noupdate_switched"] = column["noupdate"] != found["noupdate"]
if match_type != "direct":
matched_records.append(column)
matched_records.append(found)
elif (
match_type == "direct" and (found["domain"] or found["definition"])
) or found["noupdate_switched"]:
matched_records.append(found)
return matched_records
# direct match
modified_records = match(["module", "model", "name"])
# updated records (will be excluded)
match_updates(["model", "name"])
# other module, same full xmlid
moved_records = match(["model", "name"], "moved")
# other module, same suffix, other prefix
renamed_records = match(["model", "suffix", "other_prefix"], "renamed")
for record in old_records:
record["old"] = True
record["domain"] = False
record["definition"] = False
record["noupdate_switched"] = False
for record in new_records:
record["new"] = True
record["domain"] = False
record["definition"] = False
record["noupdate_switched"] = False
sorted_records = sorted(
old_records + new_records + moved_records + renamed_records + modified_records,
key=lambda k: (k["model"], "old" in k, k["name"]),
)
for entry in sorted_records:
content = ""
if "old" in entry:
content = "DEL %(model)s: %(name)s" % entry
if "moved" in entry:
content += " [moved to %(moved)s module]" % entry
elif "renamed" in entry:
content += " [renamed to %(renamed)s module]" % entry
elif "new" in entry:
content = "NEW %(model)s: %(name)s" % entry
if "moved" in entry:
content += " [moved from %(moved)s module]" % entry
elif "renamed" in entry:
content += " [renamed from %(renamed)s module]" % entry
if "old" not in entry and "new" not in entry:
content = "%(model)s: %(name)s" % entry
if entry["domain"]:
content += " (deleted domain)"
if entry["definition"]:
content += " (changed definition: %(definition)s)" % entry
if entry["noupdate"]:
content += " (noupdate)"
if entry["noupdate_switched"]:
content += " (noupdate switched)"
reprs[module_map(entry["module"])].append(content)
return reprs
def compare_model_sets(old_records, new_records):
"""
Compare a set of OpenUpgrade model representations.
"""
reprs = collections.defaultdict(list)
new_models = {column["model"]: column["module"] for column in new_records}
old_models = {column["model"]: column["module"] for column in old_records}
obsolete_models = []
for column in copy.copy(old_records):
model = column["model"]
if model in old_models:
if model not in new_models:
if model_map(model) not in new_models:
obsolete_models.append(model)
text = "obsolete model %s" % model
if column["model_type"]:
text += " [%s]" % column["model_type"]
reprs[module_map(column["module"])].append(text)
reprs["general"].append(
"obsolete model %s [module %s]"
% (model, module_map(column["module"]))
)
else:
moved_module = ""
if module_map(column["module"]) != new_models[model_map(model)]:
moved_module = " in module %s" % new_models[model_map(model)]
text = "obsolete model {} (renamed to {}{})".format(
model,
model_map(model),
moved_module,
)
if column["model_type"]:
text += " [%s]" % column["model_type"]
reprs[module_map(column["module"])].append(text)
reprs["general"].append(
"obsolete model %s (renamed to %s) [module %s]"
% (model, model_map(model), module_map(column["module"]))
)
else:
if module_map(column["module"]) != new_models[model]:
text = "model {} (moved to {})".format(model, new_models[model])
if column["model_type"]:
text += " [%s]" % column["model_type"]
reprs[module_map(column["module"])].append(text)
text = "model {} (moved from {})".format(model, old_models[model])
if column["model_type"]:
text += " [%s]" % column["model_type"]
for column in copy.copy(new_records):
model = column["model"]
if model in new_models:
if model not in old_models:
if inv_model_map(model) not in old_models:
text = "new model %s" % model
if column["model_type"]:
text += " [%s]" % column["model_type"]
reprs[column["module"]].append(text)
reprs["general"].append(
"new model {} [module {}]".format(model, column["module"])
)
else:
moved_module = ""
if column["module"] != module_map(old_models[inv_model_map(model)]):
moved_module = (
" in module %s" % old_models[inv_model_map(model)]
)
text = "new model {} (renamed from {}{})".format(
model,
inv_model_map(model),
moved_module,
)
if column["model_type"]:
text += " [%s]" % column["model_type"]
reprs[column["module"]].append(text)
reprs["general"].append(
"new model %s (renamed from %s) [module %s]"
% (model, inv_model_map(model), column["module"])
)
else:
if column["module"] != module_map(old_models[model]):
text = "model {} (moved from {})".format(model, old_models[model])
if column["model_type"]:
text += " [%s]" % column["model_type"]
reprs[column["module"]].append(text)
return reprs

View File

@ -0,0 +1,535 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * upgrade_analysis
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 15.0\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2022-09-04 06:07+0000\n"
"Last-Translator: Ignacio Buioli <ibuioli@gmail.com>\n"
"Language-Team: none\n"
"Language: es_AR\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 4.3.2\n"
#. module: upgrade_analysis
#: model_terms:ir.ui.view,arch_db:upgrade_analysis.view_upgrade_install_wizard_form
msgid "All Modules"
msgstr "Todos los Módulos"
#. module: upgrade_analysis
#: model_terms:ir.ui.view,arch_db:upgrade_analysis.view_upgrade_install_wizard_form
msgid "All OCA Modules"
msgstr "Todos los Módulos de OCA"
#. module: upgrade_analysis
#: model_terms:ir.ui.view,arch_db:upgrade_analysis.view_upgrade_install_wizard_form
msgid "All Odoo SA Modules"
msgstr "Todos los Módulos de Odoo SA"
#. module: upgrade_analysis
#: model_terms:ir.ui.view,arch_db:upgrade_analysis.view_upgrade_install_wizard_form
msgid "All Other Modules"
msgstr "Todos los Otros Módulos"
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_comparison_config__analysis_ids
#: model_terms:ir.ui.view,arch_db:upgrade_analysis.view_upgrade_comparison_config_form
msgid "Analyses"
msgstr "Análisis"
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_analysis__analysis_date
msgid "Analysis Date"
msgstr "Fecha de Análisis"
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_comparison_config__analysis_qty
msgid "Analysis Qty"
msgstr "Análisis de Cantidad"
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_record__attribute_ids
msgid "Attribute"
msgstr "Atributo"
#. module: upgrade_analysis
#: model_terms:ir.ui.view,arch_db:upgrade_analysis.view_upgrade_record_form
msgid "Attributes"
msgstr "Atributos"
#. module: upgrade_analysis
#: code:addons/upgrade_analysis/wizards/upgrade_generate_record_wizard.py:0
#, python-format
msgid "Cannot seem to install or upgrade modules %s"
msgstr "Parece que no se pueden instalar o actualizar los módulos %s"
#. module: upgrade_analysis
#: model_terms:ir.ui.view,arch_db:upgrade_analysis.view_upgrade_install_wizard_form
msgid "Clear the list"
msgstr "Limpiar la lista"
#. module: upgrade_analysis
#: model_terms:ir.ui.view,arch_db:upgrade_analysis.view_upgrade_generate_record_wizard_form
#: model_terms:ir.ui.view,arch_db:upgrade_analysis.view_upgrade_install_wizard_form
msgid "Close"
msgstr "Cerrar"
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_analysis__config_id
msgid "Comparison Config"
msgstr "Configuración de Comparación"
#. module: upgrade_analysis
#: model:ir.ui.menu,name:upgrade_analysis.menu_upgrade_comparison_config
msgid "Comparison Configurations"
msgstr "Configuraciones de Comparación"
#. module: upgrade_analysis
#: code:addons/upgrade_analysis/models/upgrade_comparison_config.py:0
#, python-format
msgid ""
"Connection failed.\n"
"\n"
"DETAIL: %s"
msgstr ""
"Conexión fallida.\n"
"\n"
"DETALLES: %s"
#. module: upgrade_analysis
#: model_terms:ir.ui.view,arch_db:upgrade_analysis.view_upgrade_generate_record_wizard_form
msgid "Continue"
msgstr "Continuar"
#. module: upgrade_analysis
#: code:addons/upgrade_analysis/models/upgrade_comparison_config.py:0
#, python-format
msgid "Could not connect the Odoo server at %(server)s:%(port)s"
msgstr "No es posible conectar al servidor de Odoo en %(server)s:%(port)s"
#. module: upgrade_analysis
#: model:ir.model.fields.selection,name:upgrade_analysis.selection__upgrade_record__mode__create
msgid "Create"
msgstr "Crear"
#. module: upgrade_analysis
#: model_terms:ir.ui.view,arch_db:upgrade_analysis.view_upgrade_record_search
msgid "Create Mode"
msgstr "Modo de Creación"
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_analysis__create_uid
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_attribute__create_uid
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_comparison_config__create_uid
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_generate_record_wizard__create_uid
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_install_wizard__create_uid
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_record__create_uid
msgid "Created by"
msgstr "Creado por"
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_analysis__create_date
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_attribute__create_date
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_comparison_config__create_date
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_generate_record_wizard__create_date
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_install_wizard__create_date
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_record__create_date
msgid "Created on"
msgstr "Creado en"
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_comparison_config__database
msgid "Database"
msgstr "Base de Datos"
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_record__definition
msgid "Definition"
msgstr "Definición"
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_analysis__display_name
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_attribute__display_name
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_comparison_config__display_name
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_generate_record_wizard__display_name
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_install_wizard__display_name
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_record__display_name
msgid "Display Name"
msgstr "Mostrar Nombre"
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_record__domain
msgid "Domain"
msgstr "Dominio"
#. module: upgrade_analysis
#: model:ir.model.fields.selection,name:upgrade_analysis.selection__upgrade_analysis__state__done
#: model:ir.model.fields.selection,name:upgrade_analysis.selection__upgrade_generate_record_wizard__state__done
#: model:ir.model.fields.selection,name:upgrade_analysis.selection__upgrade_install_wizard__state__done
msgid "Done"
msgstr "Hecho"
#. module: upgrade_analysis
#: model:ir.model.fields.selection,name:upgrade_analysis.selection__upgrade_generate_record_wizard__state__draft
#: model:ir.model.fields.selection,name:upgrade_analysis.selection__upgrade_install_wizard__state__draft
msgid "Draft"
msgstr "Borrador"
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_record__field
#: model:ir.model.fields.selection,name:upgrade_analysis.selection__upgrade_record__type__field
msgid "Field"
msgstr "Campo"
#. module: upgrade_analysis
#: model:ir.actions.act_window,name:upgrade_analysis.action_upgrade_generate_record_wizard
#: model:ir.ui.menu,name:upgrade_analysis.menu_upgrade_generate_record
msgid "Generate Records Wizard"
msgstr "Asistente de Generación de Registros"
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_analysis__id
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_attribute__id
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_comparison_config__id
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_generate_record_wizard__id
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_install_wizard__id
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_record__id
msgid "ID"
msgstr "ID"
#. module: upgrade_analysis
#: model_terms:ir.ui.view,arch_db:upgrade_analysis.view_upgrade_install_wizard_form
msgid "Install Modules"
msgstr "Instalar Módulos"
#. module: upgrade_analysis
#: model:ir.actions.act_window,name:upgrade_analysis.action_upgrade_install_wizard
#: model:ir.ui.menu,name:upgrade_analysis.menu_upgrade_install
msgid "Install Modules Wizard"
msgstr "Asistente de Instalación de Módulos"
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_ir_module_module__is_oca_module
msgid "Is Oca Module"
msgstr "Es un Módulo de OCA"
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_ir_module_module__is_odoo_module
msgid "Is Odoo Module"
msgstr "Es un Módulo de Odoo"
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_analysis____last_update
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_attribute____last_update
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_comparison_config____last_update
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_generate_record_wizard____last_update
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_install_wizard____last_update
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_record____last_update
msgid "Last Modified on"
msgstr "Última modificación en"
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_analysis__write_uid
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_attribute__write_uid
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_comparison_config__write_uid
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_generate_record_wizard__write_uid
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_install_wizard__write_uid
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_record__write_uid
msgid "Last Updated by"
msgstr "Última actualización realizada por"
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_analysis__write_date
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_attribute__write_date
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_comparison_config__write_date
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_generate_record_wizard__write_date
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_install_wizard__write_date
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_record__write_date
msgid "Last Updated on"
msgstr "Última actualización el"
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_analysis__log
#: model_terms:ir.ui.view,arch_db:upgrade_analysis.view_upgrade_analysis_form
msgid "Log"
msgstr "Registro"
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_record__mode
msgid "Mode"
msgstr "Modo"
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_record__model
#: model:ir.model.fields.selection,name:upgrade_analysis.selection__upgrade_record__type__model
msgid "Model"
msgstr "Modelo"
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_record__model_original_module
msgid "Model Original Module"
msgstr "Modelo de Módulo Original"
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_record__model_type
msgid "Model Type"
msgstr "Tipo de Modelo"
#. module: upgrade_analysis
#: model:ir.model.fields.selection,name:upgrade_analysis.selection__upgrade_record__mode__modify
msgid "Modify"
msgstr "Modificar"
#. module: upgrade_analysis
#: model_terms:ir.ui.view,arch_db:upgrade_analysis.view_upgrade_record_search
msgid "Modify Mode"
msgstr "Modo de Modificación"
#. module: upgrade_analysis
#: model:ir.model,name:upgrade_analysis.model_ir_module_module
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_install_wizard__module_ids
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_record__module
msgid "Module"
msgstr "Módulo"
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_install_wizard__module_qty
msgid "Modules Quantity"
msgstr "Cantidad de Módulos"
#. module: upgrade_analysis
#: model_terms:ir.ui.view,arch_db:upgrade_analysis.view_upgrade_generate_record_wizard_form
msgid "Modules initialized and record created"
msgstr "Módulos inicializados y registros creados"
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_attribute__name
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_comparison_config__name
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_record__name
msgid "Name"
msgstr "Nombre"
#. module: upgrade_analysis
#: model_terms:ir.ui.view,arch_db:upgrade_analysis.view_upgrade_comparison_config_form
msgid "New Analysis"
msgstr "Nuevo Análisis"
#. module: upgrade_analysis
#: code:addons/upgrade_analysis/models/upgrade_record.py:0
#, python-format
msgid "No manifest found in %(addon_dir)s"
msgstr "No se encontró manifiesta en %(addon_dir)s"
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_record__noupdate
msgid "Noupdate"
msgstr "Noupdate"
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_comparison_config__password
msgid "Password"
msgstr "Contraseña"
#. module: upgrade_analysis
#: model_terms:ir.ui.view,arch_db:upgrade_analysis.view_upgrade_analysis_form
msgid "Perform Analysis"
msgstr "Realizar Análisis"
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_comparison_config__port
msgid "Port"
msgstr "Puerto"
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_record__prefix
msgid "Prefix"
msgstr "Prefijo"
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_attribute__record_id
msgid "Record"
msgstr "Registro"
#. module: upgrade_analysis
#: model:ir.ui.menu,name:upgrade_analysis.menu_upgrade_records
msgid "Records"
msgstr "Registros"
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_comparison_config__server
msgid "Server"
msgstr "Servidor"
#. module: upgrade_analysis
#: model:ir.model.fields,help:upgrade_analysis.field_upgrade_record__mode
msgid ""
"Set to Create if a field is newly created in this module. If this module "
"modifies an attribute of an existing field, set to Modify."
msgstr ""
"Establézcalo en Crear si se crea un campo recientemente en este módulo. Si "
"este módulo modifica un atributo de un campo existente, configúrelo en "
"Modificar."
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_analysis__state
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_generate_record_wizard__state
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_install_wizard__state
msgid "State"
msgstr "Estado"
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_record__suffix
msgid "Suffix"
msgstr "Sufijo"
#. module: upgrade_analysis
#: model_terms:ir.ui.view,arch_db:upgrade_analysis.view_upgrade_comparison_config_form
msgid "Test Connection"
msgstr "Prueba de Conexión"
#. module: upgrade_analysis
#: model:ir.model.fields,help:upgrade_analysis.field_upgrade_analysis__upgrade_path
msgid ""
"The base file path to save the analyse files of Odoo modules. Taken from "
"Odoo's --upgrade-path command line option or the 'scripts' subdirectory in "
"the openupgrade_scripts addon."
msgstr ""
"La ruta del archivo base para guardar los archivos de análisis de los "
"módulos de Odoo. Tomado de la opción de línea de comando --upgrade-path de "
"Odoo o del subdirectorio 'scripts' en el complemento openupgrade_scripts."
#. module: upgrade_analysis
#: model_terms:ir.ui.view,arch_db:upgrade_analysis.view_upgrade_install_wizard_form
msgid "The modules have been installed successfuly"
msgstr "Los módulos han sido instalados correctamente"
#. module: upgrade_analysis
#: model_terms:ir.ui.view,arch_db:upgrade_analysis.view_upgrade_install_wizard_form
msgid ""
"This will install the selected modules on the database. Do not continue if "
"you use this database in production."
msgstr ""
"Esto instalará los módulos seleccionados en la base de datos. No continúe si "
"utiliza esta base de datos en producción."
#. module: upgrade_analysis
#: model_terms:ir.ui.view,arch_db:upgrade_analysis.view_upgrade_generate_record_wizard_form
msgid ""
"This will reinitialize all the modules installed on this database. Do not "
"continue if you use this database in production."
msgstr ""
"Esto reiniciará todos los módulos instalados en esta base de datos. No "
"continúe si utiliza esta base de datos en producción."
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_record__type
msgid "Type"
msgstr "Tipo"
#. module: upgrade_analysis
#: code:addons/upgrade_analysis/models/upgrade_analysis.py:0
#, python-format
msgid "Unexpected root Element: %(root)s in file: %(file)s"
msgstr "Elemento root Inesperado: %(root)s en el archivo: %(file)s"
#. module: upgrade_analysis
#: model:ir.actions.act_window,name:upgrade_analysis.action_upgrade_analysis_tree
#: model:ir.model,name:upgrade_analysis.model_upgrade_analysis
#: model:ir.ui.menu,name:upgrade_analysis.menu_upgrade_analysis
msgid "Upgrade Analyses"
msgstr "Análisis de Actualización"
#. module: upgrade_analysis
#: model:ir.ui.menu,name:upgrade_analysis.menu_upgrade
msgid "Upgrade Analysis"
msgstr "Análisis de Actualización"
#. module: upgrade_analysis
#: model:ir.model,name:upgrade_analysis.model_upgrade_attribute
msgid "Upgrade Attribute"
msgstr "Atributo de Actualización"
#. module: upgrade_analysis
#: model:ir.model,name:upgrade_analysis.model_upgrade_comparison_config
msgid "Upgrade Comparison Configuration"
msgstr "Configuración de Comparación de Actualizaciones"
#. module: upgrade_analysis
#: model:ir.model,name:upgrade_analysis.model_upgrade_generate_record_wizard
msgid "Upgrade Generate Record Wizard"
msgstr "Actualización del Asistente de Generación de Registros"
#. module: upgrade_analysis
#: model:ir.model,name:upgrade_analysis.model_upgrade_install_wizard
msgid "Upgrade Install Wizard"
msgstr "Asistente de Instalación de Actualización"
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_analysis__upgrade_path
msgid "Upgrade Path"
msgstr "Ruta de Actualización"
#. module: upgrade_analysis
#: model:ir.model,name:upgrade_analysis.model_upgrade_record
msgid "Upgrade Record"
msgstr "Registro de Actualización"
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_comparison_config__username
msgid "Username"
msgstr "Nombre de usuario"
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_attribute__value
msgid "Value"
msgstr "Valor"
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_comparison_config__version
msgid "Version"
msgstr "Versión"
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_analysis__write_files
msgid "Write Files"
msgstr "Escribir Archivos"
#. module: upgrade_analysis
#: model:ir.model.fields,help:upgrade_analysis.field_upgrade_analysis__write_files
msgid "Write analysis files to the module directories"
msgstr "Escribir archivos de análisis en los directorios del módulo"
#. module: upgrade_analysis
#: model:ir.model.fields.selection,name:upgrade_analysis.selection__upgrade_record__type__xmlid
msgid "XML ID"
msgstr "XML ID"
#. module: upgrade_analysis
#: code:addons/upgrade_analysis/models/upgrade_comparison_config.py:0
#, python-format
msgid ""
"You are correctly connected to the server %(server)s (version %(version)s) "
"with the user %(user_name)s"
msgstr ""
"Está correctamente conectado al servidor %(server)s (versión %(version)s) "
"con el usuario %(user_name)s"
#. module: upgrade_analysis
#: model:ir.model.fields.selection,name:upgrade_analysis.selection__upgrade_analysis__state__draft
msgid "draft"
msgstr "borrador"
#. module: upgrade_analysis
#: model:ir.actions.act_window,name:upgrade_analysis.action_upgrade_comparison_config_tree
msgid "upgrade Comparison Configs"
msgstr "actualizar configuraciones de comparación"
#. module: upgrade_analysis
#: model:ir.actions.act_window,name:upgrade_analysis.action_upgrade_record_tree
msgid "upgrade Records"
msgstr "actualizar Registros"

View File

@ -0,0 +1,520 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * upgrade_analysis
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 14.0\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2021-05-14 19:47+0000\n"
"Last-Translator: Yves Le Doeuff <yld@alliasys.fr>\n"
"Language-Team: none\n"
"Language: fr\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: nplurals=2; plural=n > 1;\n"
"X-Generator: Weblate 4.3.2\n"
#. module: upgrade_analysis
#: model_terms:ir.ui.view,arch_db:upgrade_analysis.view_upgrade_install_wizard_form
msgid "All Modules"
msgstr "Tous les modules"
#. module: upgrade_analysis
#: model_terms:ir.ui.view,arch_db:upgrade_analysis.view_upgrade_install_wizard_form
msgid "All OCA Modules"
msgstr "Tous les modules OCA"
#. module: upgrade_analysis
#: model_terms:ir.ui.view,arch_db:upgrade_analysis.view_upgrade_install_wizard_form
msgid "All Odoo SA Modules"
msgstr "Tous les modules Odoo SA"
#. module: upgrade_analysis
#: model_terms:ir.ui.view,arch_db:upgrade_analysis.view_upgrade_install_wizard_form
msgid "All Other Modules"
msgstr "Tous les autres modules"
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_comparison_config__analysis_ids
#: model_terms:ir.ui.view,arch_db:upgrade_analysis.view_upgrade_comparison_config_form
msgid "Analyses"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_analysis__analysis_date
msgid "Analysis Date"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_comparison_config__analysis_qty
msgid "Analysis Qty"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_record__attribute_ids
msgid "Attribute"
msgstr ""
#. module: upgrade_analysis
#: model_terms:ir.ui.view,arch_db:upgrade_analysis.view_upgrade_record_form
msgid "Attributes"
msgstr ""
#. module: upgrade_analysis
#: code:addons/upgrade_analysis/wizards/upgrade_generate_record_wizard.py:0
#, python-format
msgid "Cannot seem to install or upgrade modules %s"
msgstr ""
#. module: upgrade_analysis
#: model_terms:ir.ui.view,arch_db:upgrade_analysis.view_upgrade_install_wizard_form
msgid "Clear the list"
msgstr ""
#. module: upgrade_analysis
#: model_terms:ir.ui.view,arch_db:upgrade_analysis.view_upgrade_generate_record_wizard_form
#: model_terms:ir.ui.view,arch_db:upgrade_analysis.view_upgrade_install_wizard_form
msgid "Close"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_analysis__config_id
msgid "Comparison Config"
msgstr ""
#. module: upgrade_analysis
#: model:ir.ui.menu,name:upgrade_analysis.menu_upgrade_comparison_config
msgid "Comparison Configurations"
msgstr ""
#. module: upgrade_analysis
#: code:addons/upgrade_analysis/models/upgrade_comparison_config.py:0
#, python-format
msgid ""
"Connection failed.\n"
"\n"
"DETAIL: %s"
msgstr ""
#. module: upgrade_analysis
#: model_terms:ir.ui.view,arch_db:upgrade_analysis.view_upgrade_generate_record_wizard_form
msgid "Continue"
msgstr ""
#. module: upgrade_analysis
#: code:addons/upgrade_analysis/models/upgrade_comparison_config.py:0
#, python-format
msgid "Could not connect the Odoo server at %(server)s:%(port)s"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields.selection,name:upgrade_analysis.selection__upgrade_record__mode__create
msgid "Create"
msgstr ""
#. module: upgrade_analysis
#: model_terms:ir.ui.view,arch_db:upgrade_analysis.view_upgrade_record_search
msgid "Create Mode"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_analysis__create_uid
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_attribute__create_uid
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_comparison_config__create_uid
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_generate_record_wizard__create_uid
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_install_wizard__create_uid
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_record__create_uid
msgid "Created by"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_analysis__create_date
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_attribute__create_date
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_comparison_config__create_date
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_generate_record_wizard__create_date
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_install_wizard__create_date
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_record__create_date
msgid "Created on"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_comparison_config__database
msgid "Database"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_record__definition
msgid "Definition"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_analysis__display_name
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_attribute__display_name
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_comparison_config__display_name
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_generate_record_wizard__display_name
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_install_wizard__display_name
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_record__display_name
msgid "Display Name"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_record__domain
msgid "Domain"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields.selection,name:upgrade_analysis.selection__upgrade_analysis__state__done
#: model:ir.model.fields.selection,name:upgrade_analysis.selection__upgrade_generate_record_wizard__state__done
#: model:ir.model.fields.selection,name:upgrade_analysis.selection__upgrade_install_wizard__state__done
msgid "Done"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields.selection,name:upgrade_analysis.selection__upgrade_generate_record_wizard__state__draft
#: model:ir.model.fields.selection,name:upgrade_analysis.selection__upgrade_install_wizard__state__draft
msgid "Draft"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_record__field
#: model:ir.model.fields.selection,name:upgrade_analysis.selection__upgrade_record__type__field
msgid "Field"
msgstr ""
#. module: upgrade_analysis
#: model:ir.actions.act_window,name:upgrade_analysis.action_upgrade_generate_record_wizard
#: model:ir.ui.menu,name:upgrade_analysis.menu_upgrade_generate_record
msgid "Generate Records Wizard"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_analysis__id
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_attribute__id
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_comparison_config__id
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_generate_record_wizard__id
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_install_wizard__id
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_record__id
msgid "ID"
msgstr ""
#. module: upgrade_analysis
#: model_terms:ir.ui.view,arch_db:upgrade_analysis.view_upgrade_install_wizard_form
msgid "Install Modules"
msgstr ""
#. module: upgrade_analysis
#: model:ir.actions.act_window,name:upgrade_analysis.action_upgrade_install_wizard
#: model:ir.ui.menu,name:upgrade_analysis.menu_upgrade_install
msgid "Install Modules Wizard"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_ir_module_module__is_oca_module
msgid "Is Oca Module"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_ir_module_module__is_odoo_module
msgid "Is Odoo Module"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_analysis____last_update
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_attribute____last_update
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_comparison_config____last_update
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_generate_record_wizard____last_update
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_install_wizard____last_update
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_record____last_update
msgid "Last Modified on"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_analysis__write_uid
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_attribute__write_uid
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_comparison_config__write_uid
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_generate_record_wizard__write_uid
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_install_wizard__write_uid
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_record__write_uid
msgid "Last Updated by"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_analysis__write_date
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_attribute__write_date
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_comparison_config__write_date
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_generate_record_wizard__write_date
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_install_wizard__write_date
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_record__write_date
msgid "Last Updated on"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_analysis__log
#: model_terms:ir.ui.view,arch_db:upgrade_analysis.view_upgrade_analysis_form
msgid "Log"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_record__mode
msgid "Mode"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_record__model
#: model:ir.model.fields.selection,name:upgrade_analysis.selection__upgrade_record__type__model
msgid "Model"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_record__model_original_module
msgid "Model Original Module"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_record__model_type
msgid "Model Type"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields.selection,name:upgrade_analysis.selection__upgrade_record__mode__modify
msgid "Modify"
msgstr ""
#. module: upgrade_analysis
#: model_terms:ir.ui.view,arch_db:upgrade_analysis.view_upgrade_record_search
msgid "Modify Mode"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model,name:upgrade_analysis.model_ir_module_module
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_install_wizard__module_ids
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_record__module
msgid "Module"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_install_wizard__module_qty
msgid "Modules Quantity"
msgstr ""
#. module: upgrade_analysis
#: model_terms:ir.ui.view,arch_db:upgrade_analysis.view_upgrade_generate_record_wizard_form
msgid "Modules initialized and record created"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_attribute__name
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_comparison_config__name
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_record__name
msgid "Name"
msgstr ""
#. module: upgrade_analysis
#: model_terms:ir.ui.view,arch_db:upgrade_analysis.view_upgrade_comparison_config_form
msgid "New Analysis"
msgstr ""
#. module: upgrade_analysis
#: code:addons/upgrade_analysis/models/upgrade_record.py:0
#, python-format
msgid "No manifest found in %(addon_dir)s"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_record__noupdate
msgid "Noupdate"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_comparison_config__password
msgid "Password"
msgstr ""
#. module: upgrade_analysis
#: model_terms:ir.ui.view,arch_db:upgrade_analysis.view_upgrade_analysis_form
msgid "Perform Analysis"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_comparison_config__port
msgid "Port"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_record__prefix
msgid "Prefix"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_attribute__record_id
msgid "Record"
msgstr ""
#. module: upgrade_analysis
#: model:ir.ui.menu,name:upgrade_analysis.menu_upgrade_records
msgid "Records"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_comparison_config__server
msgid "Server"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,help:upgrade_analysis.field_upgrade_record__mode
msgid ""
"Set to Create if a field is newly created in this module. If this module "
"modifies an attribute of an existing field, set to Modify."
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_analysis__state
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_generate_record_wizard__state
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_install_wizard__state
msgid "State"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_record__suffix
msgid "Suffix"
msgstr ""
#. module: upgrade_analysis
#: model_terms:ir.ui.view,arch_db:upgrade_analysis.view_upgrade_comparison_config_form
msgid "Test Connection"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,help:upgrade_analysis.field_upgrade_analysis__upgrade_path
msgid ""
"The base file path to save the analyse files of Odoo modules. Taken from "
"Odoo's --upgrade-path command line option or the 'scripts' subdirectory in "
"the openupgrade_scripts addon."
msgstr ""
#. module: upgrade_analysis
#: model_terms:ir.ui.view,arch_db:upgrade_analysis.view_upgrade_install_wizard_form
msgid "The modules have been installed successfuly"
msgstr ""
#. module: upgrade_analysis
#: model_terms:ir.ui.view,arch_db:upgrade_analysis.view_upgrade_install_wizard_form
msgid ""
"This will install the selected modules on the database. Do not continue if "
"you use this database in production."
msgstr ""
#. module: upgrade_analysis
#: model_terms:ir.ui.view,arch_db:upgrade_analysis.view_upgrade_generate_record_wizard_form
msgid ""
"This will reinitialize all the modules installed on this database. Do not "
"continue if you use this database in production."
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_record__type
msgid "Type"
msgstr ""
#. module: upgrade_analysis
#: code:addons/upgrade_analysis/models/upgrade_analysis.py:0
#, python-format
msgid "Unexpected root Element: %(root)s in file: %(file)s"
msgstr ""
#. module: upgrade_analysis
#: model:ir.actions.act_window,name:upgrade_analysis.action_upgrade_analysis_tree
#: model:ir.model,name:upgrade_analysis.model_upgrade_analysis
#: model:ir.ui.menu,name:upgrade_analysis.menu_upgrade_analysis
msgid "Upgrade Analyses"
msgstr ""
#. module: upgrade_analysis
#: model:ir.ui.menu,name:upgrade_analysis.menu_upgrade
msgid "Upgrade Analysis"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model,name:upgrade_analysis.model_upgrade_attribute
msgid "Upgrade Attribute"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model,name:upgrade_analysis.model_upgrade_comparison_config
msgid "Upgrade Comparison Configuration"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model,name:upgrade_analysis.model_upgrade_generate_record_wizard
msgid "Upgrade Generate Record Wizard"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model,name:upgrade_analysis.model_upgrade_install_wizard
msgid "Upgrade Install Wizard"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_analysis__upgrade_path
msgid "Upgrade Path"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model,name:upgrade_analysis.model_upgrade_record
msgid "Upgrade Record"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_comparison_config__username
msgid "Username"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_attribute__value
msgid "Value"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_comparison_config__version
msgid "Version"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_analysis__write_files
msgid "Write Files"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,help:upgrade_analysis.field_upgrade_analysis__write_files
msgid "Write analysis files to the module directories"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields.selection,name:upgrade_analysis.selection__upgrade_record__type__xmlid
msgid "XML ID"
msgstr ""
#. module: upgrade_analysis
#: code:addons/upgrade_analysis/models/upgrade_comparison_config.py:0
#, python-format
msgid ""
"You are correctly connected to the server %(server)s (version %(version)s) "
"with the user %(user_name)s"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields.selection,name:upgrade_analysis.selection__upgrade_analysis__state__draft
msgid "draft"
msgstr ""
#. module: upgrade_analysis
#: model:ir.actions.act_window,name:upgrade_analysis.action_upgrade_comparison_config_tree
msgid "upgrade Comparison Configs"
msgstr ""
#. module: upgrade_analysis
#: model:ir.actions.act_window,name:upgrade_analysis.action_upgrade_record_tree
msgid "upgrade Records"
msgstr ""

View File

@ -0,0 +1,517 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * upgrade_analysis
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 15.0\n"
"Report-Msgid-Bugs-To: \n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: upgrade_analysis
#: model_terms:ir.ui.view,arch_db:upgrade_analysis.view_upgrade_install_wizard_form
msgid "All Modules"
msgstr ""
#. module: upgrade_analysis
#: model_terms:ir.ui.view,arch_db:upgrade_analysis.view_upgrade_install_wizard_form
msgid "All OCA Modules"
msgstr ""
#. module: upgrade_analysis
#: model_terms:ir.ui.view,arch_db:upgrade_analysis.view_upgrade_install_wizard_form
msgid "All Odoo SA Modules"
msgstr ""
#. module: upgrade_analysis
#: model_terms:ir.ui.view,arch_db:upgrade_analysis.view_upgrade_install_wizard_form
msgid "All Other Modules"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_comparison_config__analysis_ids
#: model_terms:ir.ui.view,arch_db:upgrade_analysis.view_upgrade_comparison_config_form
msgid "Analyses"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_analysis__analysis_date
msgid "Analysis Date"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_comparison_config__analysis_qty
msgid "Analysis Qty"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_record__attribute_ids
msgid "Attribute"
msgstr ""
#. module: upgrade_analysis
#: model_terms:ir.ui.view,arch_db:upgrade_analysis.view_upgrade_record_form
msgid "Attributes"
msgstr ""
#. module: upgrade_analysis
#: code:addons/upgrade_analysis/wizards/upgrade_generate_record_wizard.py:0
#, python-format
msgid "Cannot seem to install or upgrade modules %s"
msgstr ""
#. module: upgrade_analysis
#: model_terms:ir.ui.view,arch_db:upgrade_analysis.view_upgrade_install_wizard_form
msgid "Clear the list"
msgstr ""
#. module: upgrade_analysis
#: model_terms:ir.ui.view,arch_db:upgrade_analysis.view_upgrade_generate_record_wizard_form
#: model_terms:ir.ui.view,arch_db:upgrade_analysis.view_upgrade_install_wizard_form
msgid "Close"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_analysis__config_id
msgid "Comparison Config"
msgstr ""
#. module: upgrade_analysis
#: model:ir.ui.menu,name:upgrade_analysis.menu_upgrade_comparison_config
msgid "Comparison Configurations"
msgstr ""
#. module: upgrade_analysis
#: code:addons/upgrade_analysis/models/upgrade_comparison_config.py:0
#, python-format
msgid ""
"Connection failed.\n"
"\n"
"DETAIL: %s"
msgstr ""
#. module: upgrade_analysis
#: model_terms:ir.ui.view,arch_db:upgrade_analysis.view_upgrade_generate_record_wizard_form
msgid "Continue"
msgstr ""
#. module: upgrade_analysis
#: code:addons/upgrade_analysis/models/upgrade_comparison_config.py:0
#, python-format
msgid "Could not connect the Odoo server at %(server)s:%(port)s"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields.selection,name:upgrade_analysis.selection__upgrade_record__mode__create
msgid "Create"
msgstr ""
#. module: upgrade_analysis
#: model_terms:ir.ui.view,arch_db:upgrade_analysis.view_upgrade_record_search
msgid "Create Mode"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_analysis__create_uid
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_attribute__create_uid
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_comparison_config__create_uid
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_generate_record_wizard__create_uid
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_install_wizard__create_uid
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_record__create_uid
msgid "Created by"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_analysis__create_date
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_attribute__create_date
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_comparison_config__create_date
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_generate_record_wizard__create_date
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_install_wizard__create_date
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_record__create_date
msgid "Created on"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_comparison_config__database
msgid "Database"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_record__definition
msgid "Definition"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_analysis__display_name
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_attribute__display_name
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_comparison_config__display_name
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_generate_record_wizard__display_name
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_install_wizard__display_name
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_record__display_name
msgid "Display Name"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_record__domain
msgid "Domain"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields.selection,name:upgrade_analysis.selection__upgrade_analysis__state__done
#: model:ir.model.fields.selection,name:upgrade_analysis.selection__upgrade_generate_record_wizard__state__done
#: model:ir.model.fields.selection,name:upgrade_analysis.selection__upgrade_install_wizard__state__done
msgid "Done"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields.selection,name:upgrade_analysis.selection__upgrade_generate_record_wizard__state__draft
#: model:ir.model.fields.selection,name:upgrade_analysis.selection__upgrade_install_wizard__state__draft
msgid "Draft"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_record__field
#: model:ir.model.fields.selection,name:upgrade_analysis.selection__upgrade_record__type__field
msgid "Field"
msgstr ""
#. module: upgrade_analysis
#: model:ir.actions.act_window,name:upgrade_analysis.action_upgrade_generate_record_wizard
#: model:ir.ui.menu,name:upgrade_analysis.menu_upgrade_generate_record
msgid "Generate Records Wizard"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_analysis__id
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_attribute__id
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_comparison_config__id
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_generate_record_wizard__id
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_install_wizard__id
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_record__id
msgid "ID"
msgstr ""
#. module: upgrade_analysis
#: model_terms:ir.ui.view,arch_db:upgrade_analysis.view_upgrade_install_wizard_form
msgid "Install Modules"
msgstr ""
#. module: upgrade_analysis
#: model:ir.actions.act_window,name:upgrade_analysis.action_upgrade_install_wizard
#: model:ir.ui.menu,name:upgrade_analysis.menu_upgrade_install
msgid "Install Modules Wizard"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_ir_module_module__is_oca_module
msgid "Is Oca Module"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_ir_module_module__is_odoo_module
msgid "Is Odoo Module"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_analysis____last_update
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_attribute____last_update
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_comparison_config____last_update
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_generate_record_wizard____last_update
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_install_wizard____last_update
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_record____last_update
msgid "Last Modified on"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_analysis__write_uid
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_attribute__write_uid
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_comparison_config__write_uid
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_generate_record_wizard__write_uid
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_install_wizard__write_uid
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_record__write_uid
msgid "Last Updated by"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_analysis__write_date
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_attribute__write_date
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_comparison_config__write_date
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_generate_record_wizard__write_date
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_install_wizard__write_date
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_record__write_date
msgid "Last Updated on"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_analysis__log
#: model_terms:ir.ui.view,arch_db:upgrade_analysis.view_upgrade_analysis_form
msgid "Log"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_record__mode
msgid "Mode"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_record__model
#: model:ir.model.fields.selection,name:upgrade_analysis.selection__upgrade_record__type__model
msgid "Model"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_record__model_original_module
msgid "Model Original Module"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_record__model_type
msgid "Model Type"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields.selection,name:upgrade_analysis.selection__upgrade_record__mode__modify
msgid "Modify"
msgstr ""
#. module: upgrade_analysis
#: model_terms:ir.ui.view,arch_db:upgrade_analysis.view_upgrade_record_search
msgid "Modify Mode"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model,name:upgrade_analysis.model_ir_module_module
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_install_wizard__module_ids
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_record__module
msgid "Module"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_install_wizard__module_qty
msgid "Modules Quantity"
msgstr ""
#. module: upgrade_analysis
#: model_terms:ir.ui.view,arch_db:upgrade_analysis.view_upgrade_generate_record_wizard_form
msgid "Modules initialized and record created"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_attribute__name
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_comparison_config__name
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_record__name
msgid "Name"
msgstr ""
#. module: upgrade_analysis
#: model_terms:ir.ui.view,arch_db:upgrade_analysis.view_upgrade_comparison_config_form
msgid "New Analysis"
msgstr ""
#. module: upgrade_analysis
#: code:addons/upgrade_analysis/models/upgrade_record.py:0
#, python-format
msgid "No manifest found in %(addon_dir)s"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_record__noupdate
msgid "Noupdate"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_comparison_config__password
msgid "Password"
msgstr ""
#. module: upgrade_analysis
#: model_terms:ir.ui.view,arch_db:upgrade_analysis.view_upgrade_analysis_form
msgid "Perform Analysis"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_comparison_config__port
msgid "Port"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_record__prefix
msgid "Prefix"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_attribute__record_id
msgid "Record"
msgstr ""
#. module: upgrade_analysis
#: model:ir.ui.menu,name:upgrade_analysis.menu_upgrade_records
msgid "Records"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_comparison_config__server
msgid "Server"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,help:upgrade_analysis.field_upgrade_record__mode
msgid ""
"Set to Create if a field is newly created in this module. If this module "
"modifies an attribute of an existing field, set to Modify."
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_analysis__state
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_generate_record_wizard__state
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_install_wizard__state
msgid "State"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_record__suffix
msgid "Suffix"
msgstr ""
#. module: upgrade_analysis
#: model_terms:ir.ui.view,arch_db:upgrade_analysis.view_upgrade_comparison_config_form
msgid "Test Connection"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,help:upgrade_analysis.field_upgrade_analysis__upgrade_path
msgid ""
"The base file path to save the analyse files of Odoo modules. Taken from "
"Odoo's --upgrade-path command line option or the 'scripts' subdirectory in "
"the openupgrade_scripts addon."
msgstr ""
#. module: upgrade_analysis
#: model_terms:ir.ui.view,arch_db:upgrade_analysis.view_upgrade_install_wizard_form
msgid "The modules have been installed successfuly"
msgstr ""
#. module: upgrade_analysis
#: model_terms:ir.ui.view,arch_db:upgrade_analysis.view_upgrade_install_wizard_form
msgid ""
"This will install the selected modules on the database. Do not continue if "
"you use this database in production."
msgstr ""
#. module: upgrade_analysis
#: model_terms:ir.ui.view,arch_db:upgrade_analysis.view_upgrade_generate_record_wizard_form
msgid ""
"This will reinitialize all the modules installed on this database. Do not "
"continue if you use this database in production."
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_record__type
msgid "Type"
msgstr ""
#. module: upgrade_analysis
#: code:addons/upgrade_analysis/models/upgrade_analysis.py:0
#, python-format
msgid "Unexpected root Element: %(root)s in file: %(file)s"
msgstr ""
#. module: upgrade_analysis
#: model:ir.actions.act_window,name:upgrade_analysis.action_upgrade_analysis_tree
#: model:ir.model,name:upgrade_analysis.model_upgrade_analysis
#: model:ir.ui.menu,name:upgrade_analysis.menu_upgrade_analysis
msgid "Upgrade Analyses"
msgstr ""
#. module: upgrade_analysis
#: model:ir.ui.menu,name:upgrade_analysis.menu_upgrade
msgid "Upgrade Analysis"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model,name:upgrade_analysis.model_upgrade_attribute
msgid "Upgrade Attribute"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model,name:upgrade_analysis.model_upgrade_comparison_config
msgid "Upgrade Comparison Configuration"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model,name:upgrade_analysis.model_upgrade_generate_record_wizard
msgid "Upgrade Generate Record Wizard"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model,name:upgrade_analysis.model_upgrade_install_wizard
msgid "Upgrade Install Wizard"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_analysis__upgrade_path
msgid "Upgrade Path"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model,name:upgrade_analysis.model_upgrade_record
msgid "Upgrade Record"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_comparison_config__username
msgid "Username"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_attribute__value
msgid "Value"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_comparison_config__version
msgid "Version"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,field_description:upgrade_analysis.field_upgrade_analysis__write_files
msgid "Write Files"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields,help:upgrade_analysis.field_upgrade_analysis__write_files
msgid "Write analysis files to the module directories"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields.selection,name:upgrade_analysis.selection__upgrade_record__type__xmlid
msgid "XML ID"
msgstr ""
#. module: upgrade_analysis
#: code:addons/upgrade_analysis/models/upgrade_comparison_config.py:0
#, python-format
msgid ""
"You are correctly connected to the server %(server)s (version %(version)s) "
"with the user %(user_name)s"
msgstr ""
#. module: upgrade_analysis
#: model:ir.model.fields.selection,name:upgrade_analysis.selection__upgrade_analysis__state__draft
msgid "draft"
msgstr ""
#. module: upgrade_analysis
#: model:ir.actions.act_window,name:upgrade_analysis.action_upgrade_comparison_config_tree
msgid "upgrade Comparison Configs"
msgstr ""
#. module: upgrade_analysis
#: model:ir.actions.act_window,name:upgrade_analysis.action_upgrade_record_tree
msgid "upgrade Records"
msgstr ""

View File

@ -0,0 +1,5 @@
from . import ir_module_module
from . import upgrade_comparison_config
from . import upgrade_analysis
from . import upgrade_attribute
from . import upgrade_record

View File

@ -0,0 +1,32 @@
# Copyright 2011-2015 Therp BV <https://therp.nl>
# Copyright 2016 Opener B.V. <https://opener.am>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
import os
from odoo import fields, models
from odoo.modules import get_module_path
class IrModuleModule(models.Model):
_inherit = "ir.module.module"
is_odoo_module = fields.Boolean(
compute="_compute_is_odoo_module",
)
is_oca_module = fields.Boolean(compute="_compute_is_oca_module")
def _compute_is_oca_module(self):
for module in self:
module.is_oca_module = "/OCA/" in module.website
def _compute_is_odoo_module(self):
for module in self:
module_path = get_module_path(module.name)
if not module_path:
module.is_odoo_module = False
continue
absolute_repo_path = os.path.split(module_path)[0]
x, relative_repo_path = os.path.split(absolute_repo_path)
module.is_odoo_module = relative_repo_path == "addons"

View File

@ -0,0 +1,596 @@
# Copyright 2011-2015 Therp BV <https://therp.nl>
# Copyright 2016-2020 Opener B.V. <https://opener.am>
# Copyright 2019 ForgeFlow <https://forgeflow.com>
# Copyright 2020 GRAP <https://grap.coop>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
# flake8: noqa: C901
import logging
import os
from copy import deepcopy
from lxml import etree
from mako.template import Template
from odoo import fields, models, release
from odoo.exceptions import ValidationError
from odoo.modules import get_module_path
from odoo.tools import config
from odoo.tools.convert import nodeattr2bool
from odoo.tools.translate import _
try:
from odoo.addons.openupgrade_scripts.apriori import merged_modules, renamed_modules
except ImportError:
renamed_modules = {}
merged_modules = {}
from .. import compare
_logger = logging.getLogger(__name__)
_IGNORE_MODULES = ["openupgrade_records", "upgrade_analysis"]
class UpgradeAnalysis(models.Model):
_name = "upgrade.analysis"
_description = "Upgrade Analyses"
analysis_date = fields.Datetime(readonly=True)
state = fields.Selection(
[("draft", "draft"), ("done", "Done")], readonly=True, default="draft"
)
config_id = fields.Many2one(
string="Comparison Config",
comodel_name="upgrade.comparison.config",
readonly=True,
required=True,
)
log = fields.Text(readonly=True)
upgrade_path = fields.Char(
compute="_compute_upgrade_path",
readonly=True,
help=(
"The base file path to save the analyse files of Odoo modules. "
"Taken from Odoo's --upgrade-path command line option or the "
"'scripts' subdirectory in the openupgrade_scripts addon."
),
)
write_files = fields.Boolean(
help="Write analysis files to the module directories", default=True
)
def _compute_upgrade_path(self):
"""Return the --upgrade-path configuration option or the `scripts`
directory in `openupgrade_scripts` if available
"""
res = config.get("upgrade_path", False)
if not res:
module_path = get_module_path("openupgrade_scripts", display_warning=False)
if module_path:
res = os.path.join(module_path, "scripts")
self.upgrade_path = res
def _get_remote_model(self, connection, model):
self.ensure_one()
if model == "record":
if float(self.config_id.version) < 14.0:
return connection.env["openupgrade.record"]
else:
return connection.env["upgrade.record"]
return False
def _write_file(
self, module_name, version, content, filename="upgrade_analysis.txt"
):
module = self.env["ir.module.module"].search([("name", "=", module_name)])[0]
if module.is_odoo_module:
if not self.upgrade_path:
return (
"ERROR: no upgrade_path set when writing analysis of %s\n"
% module_name
)
full_path = os.path.join(self.upgrade_path, module_name, version)
else:
full_path = os.path.join(
get_module_path(module_name), "migrations", version
)
if not os.path.exists(full_path):
try:
os.makedirs(full_path)
except os.error:
return "ERROR: could not create migrations directory %s:\n" % (
full_path
)
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
_logger.debug("Writing analysis to %s", logfile)
f.write(content)
f.close()
return None
def analyze(self):
"""
Retrieve both sets of database representations,
perform the comparison and register the resulting
change set
"""
self.ensure_one()
self.write(
{
"analysis_date": fields.Datetime.now(),
}
)
connection = self.config_id.get_connection()
RemoteRecord = self._get_remote_model(connection, "record")
LocalRecord = self.env["upgrade.record"]
# Retrieve field representations and compare
remote_records = RemoteRecord.field_dump()
local_records = LocalRecord.field_dump()
res = compare.compare_sets(remote_records, local_records)
# Retrieve xml id representations and compare
flds = [
"module",
"model",
"name",
"noupdate",
"prefix",
"suffix",
"domain",
"definition",
]
local_xml_records = [
{field: record[field] for field in flds}
for record in LocalRecord.search([("type", "=", "xmlid")])
]
remote_xml_record_ids = RemoteRecord.search([("type", "=", "xmlid")])
remote_xml_records = [
{field: record[field] for field in flds}
for record in RemoteRecord.read(remote_xml_record_ids, flds)
]
res_xml = compare.compare_xml_sets(remote_xml_records, local_xml_records)
# Retrieve model representations and compare
flds = [
"module",
"model",
"name",
"model_original_module",
"model_type",
]
local_model_records = [
{field: record[field] for field in flds}
for record in LocalRecord.search([("type", "=", "model")])
]
remote_model_record_ids = RemoteRecord.search([("type", "=", "model")])
remote_model_records = [
{field: record[field] for field in flds}
for record in RemoteRecord.read(remote_model_record_ids, flds)
]
res_model = compare.compare_model_sets(
remote_model_records, local_model_records
)
affected_modules = sorted(
{
record["module"]
for record in remote_records
+ local_records
+ remote_xml_records
+ local_xml_records
+ remote_model_records
+ local_model_records
}
)
if "base" in affected_modules:
try:
pass
except ImportError:
_logger.error(
"You are using upgrade_analysis on core modules without "
" having openupgrade_scripts module available."
" The analysis process will not work properly,"
" if you are generating analysis for the odoo modules"
" in an openupgrade context."
)
# reorder and output the result
keys = ["general"] + affected_modules
modules = {
module["name"]: module
for module in self.env["ir.module.module"].search(
[("state", "=", "installed")]
)
}
general_log = ""
no_changes_modules = []
for ignore_module in _IGNORE_MODULES:
if ignore_module in keys:
keys.remove(ignore_module)
for key in keys:
contents = "---Models in module '%s'---\n" % key
if key in res_model:
contents += "\n".join([str(line) for line in res_model[key]])
if res_model[key]:
contents += "\n"
contents += "---Fields in module '%s'---\n" % key
if key in res:
contents += "\n".join([str(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([str(line) for line in res_xml[key]])
if res_xml[key]:
contents += "\n"
if key not in res and key not in res_xml and key not in res_model:
contents += "---nothing has changed in this module--\n"
no_changes_modules.append(key)
if key == "general":
general_log += contents
continue
if compare.module_map(key) not in modules:
general_log += (
"ERROR: module not in list of installed modules:\n" + contents
)
continue
if key not in modules:
# no need to log in full log the merged/renamed modules
continue
if self.write_files:
error = self._write_file(key, modules[key].installed_version, contents)
if error:
general_log += error
general_log += contents
else:
general_log += contents
# Store the full log
if self.write_files and "base" in modules:
self._write_file(
"base",
modules["base"].installed_version,
general_log,
"upgrade_general_log.txt",
)
try:
self.generate_noupdate_changes()
except Exception as e:
_logger.exception("Error generating noupdate changes: %s" % e)
general_log += "ERROR: error when generating noupdate changes: %s\n" % e
try:
self.generate_module_coverage_file(no_changes_modules)
except Exception as e:
_logger.exception("Error generating module coverage file: %s" % e)
general_log += "ERROR: error when generating module coverage file: %s\n" % e
self.write(
{
"state": "done",
"log": general_log,
}
)
return True
@staticmethod
def _get_node_dict(element):
res = {}
if element is None:
return res
for child in element:
if "name" in child.attrib:
key = "./{}[@name='{}']".format(child.tag, child.attrib["name"])
res[key] = child
return res
@staticmethod
def _get_node_value(element):
if "eval" in element.attrib.keys():
return element.attrib["eval"]
if "ref" in element.attrib.keys():
return element.attrib["ref"]
if not len(element):
return element.text
return etree.tostring(element)
def _get_xml_diff(
self, remote_update, remote_noupdate, local_update, local_noupdate
):
odoo = etree.Element("odoo")
for xml_id in sorted(local_noupdate.keys()):
local_record = local_noupdate[xml_id]
remote_record = None
if xml_id in remote_update and xml_id not in remote_noupdate:
remote_record = remote_update[xml_id]
elif xml_id in remote_noupdate:
remote_record = remote_noupdate[xml_id]
if "." in xml_id:
module_xmlid = xml_id.split(".", 1)[0]
else:
module_xmlid = ""
if remote_record is None and not module_xmlid:
continue
if local_record.tag == "template":
old_tmpl = etree.tostring(remote_record, encoding="utf-8")
new_tmpl = etree.tostring(local_record, encoding="utf-8")
if old_tmpl != new_tmpl:
odoo.append(local_record)
continue
element = etree.Element(
"record", id=xml_id, model=local_record.attrib["model"]
)
# Add forcecreate attribute if exists
if local_record.attrib.get("forcecreate"):
element.attrib["forcecreate"] = local_record.attrib["forcecreate"]
record_remote_dict = self._get_node_dict(remote_record)
record_local_dict = self._get_node_dict(local_record)
for key in sorted(record_remote_dict.keys()):
if not local_record.xpath(key):
# The element is no longer present.
# Does the field still exist?
if record_remote_dict[key].tag == "field":
field_name = remote_record.xpath(key)[0].attrib.get("name")
if (
field_name
not in self.env[local_record.attrib["model"]]._fields.keys()
):
continue
# Overwrite an existing value with an empty one.
attribs = deepcopy(record_remote_dict[key]).attrib
for attr in ["eval", "ref"]:
if attr in attribs:
del attribs[attr]
element.append(etree.Element(record_remote_dict[key].tag, attribs))
else:
oldrepr = self._get_node_value(record_remote_dict[key])
newrepr = self._get_node_value(record_local_dict[key])
if oldrepr != newrepr:
element.append(deepcopy(record_local_dict[key]))
for key in sorted(record_local_dict.keys()):
if remote_record is None or not remote_record.xpath(key):
element.append(deepcopy(record_local_dict[key]))
if len(element):
odoo.append(element)
if not len(odoo):
return ""
return etree.tostring(
etree.ElementTree(odoo),
pretty_print=True,
xml_declaration=True,
encoding="utf-8",
).decode("utf-8")
@staticmethod
def _update_node(target, source):
for element in source:
if "name" in element.attrib:
query = "./{}[@name='{}']".format(element.tag, element.attrib["name"])
else:
# query = "./{}".format(element.tag)
continue
for existing in target.xpath(query):
target.remove(existing)
target.append(element)
@classmethod
def _process_data_node(
self, data_node, records_update, records_noupdate, module_name
):
noupdate = nodeattr2bool(data_node, "noupdate", False)
for record in data_node.xpath("./record") + data_node.xpath("./template"):
self._process_record_node(
record, noupdate, records_update, records_noupdate, module_name
)
@classmethod
def _process_record_node(
self, record, noupdate, records_update, records_noupdate, module_name
):
xml_id = record.get("id")
if not xml_id:
return
if "." in xml_id and xml_id.startswith(module_name + "."):
xml_id = xml_id[len(module_name) + 1 :]
for records in records_noupdate, records_update:
# records can occur multiple times in the same module
# with different noupdate settings
if xml_id in records:
# merge records (overwriting an existing element
# with the same tag). The order processing the
# various directives from the manifest is
# important here
self._update_node(records[xml_id], record)
break
else:
target_dict = records_noupdate if noupdate else records_update
target_dict[xml_id] = record
@classmethod
def _parse_files(self, xml_files, module_name):
records_update = {}
records_noupdate = {}
parser = etree.XMLParser(
remove_blank_text=True,
strip_cdata=False,
)
for xml_file in xml_files:
try:
# This is for a final correct pretty print
# Ref.: https://stackoverflow.com/a/7904066
# Also don't strip CDATA tags as needed for HTML content
root_node = etree.fromstring(xml_file.encode("utf-8"), parser=parser)
except etree.XMLSyntaxError:
continue
# Support xml files with root Element either odoo or openerp
# Condition: each xml file should have only one root element
# {<odoo>, <openerp> or —rarely— <data>};
root_node_noupdate = nodeattr2bool(root_node, "noupdate", False)
if root_node.tag not in ("openerp", "odoo", "data"):
raise ValidationError(
_("Unexpected root Element: %(root)s in file: %(file)s")
% {"root": root_node.getroot(), "file": xml_file}
)
for node in root_node:
if node.tag == "data":
self._process_data_node(
node, records_update, records_noupdate, module_name
)
elif node.tag == "record":
self._process_record_node(
node,
root_node_noupdate,
records_update,
records_noupdate,
module_name,
)
return records_update, records_noupdate
def generate_noupdate_changes(self):
"""Communicate with the remote server to fetch all xml data records
per module, and generate a diff in XML format that can be imported
from the module's migration script using openupgrade.load_data()
"""
self.ensure_one()
connection = self.config_id.get_connection()
remote_record_obj = self._get_remote_model(connection, "record")
local_record_obj = self.env["upgrade.record"]
local_modules = local_record_obj.list_modules()
all_remote_modules = remote_record_obj.list_modules()
for local_module in local_modules:
remote_files = []
remote_modules = []
remote_update, remote_noupdate = {}, {}
for remote_module in all_remote_modules:
if local_module == renamed_modules.get(
remote_module, merged_modules.get(remote_module, remote_module)
):
remote_files.extend(
remote_record_obj.get_xml_records(remote_module)
)
remote_modules.append(remote_module)
add_remote_update, add_remote_noupdate = self._parse_files(
remote_files, remote_module
)
remote_update.update(add_remote_update)
remote_noupdate.update(add_remote_noupdate)
if not remote_modules:
continue
local_files = local_record_obj.get_xml_records(local_module)
local_update, local_noupdate = self._parse_files(local_files, local_module)
diff = self._get_xml_diff(
remote_update, remote_noupdate, local_update, local_noupdate
)
if diff:
module = self.env["ir.module.module"].search(
[("name", "=", local_module)]
)
self._write_file(
local_module,
module.installed_version,
diff,
filename="noupdate_changes.xml",
)
return True
def generate_module_coverage_file(self, no_changes_modules):
self.ensure_one()
module_coverage_file_folder = config.get("module_coverage_file_folder", False)
if not module_coverage_file_folder:
return
file_template = Template(
filename=os.path.join(
get_module_path("upgrade_analysis"),
"static",
"src",
"module_coverage_template.rst.mako",
)
)
module_domain = [
("state", "=", "installed"),
("name", "not in", ["upgrade_analysis", "openupgrade_records"]),
]
connection = self.config_id.get_connection()
all_local_modules = (
self.env["ir.module.module"].search(module_domain).mapped("name")
)
all_remote_modules = (
connection.env["ir.module.module"]
.browse(connection.env["ir.module.module"].search(module_domain))
.mapped("name")
)
start_version = connection.version
end_version = release.major_version
all_modules = sorted(list(set(all_remote_modules + all_local_modules)))
module_descriptions = {}
for module in all_modules:
status = ""
if module in all_local_modules and module in all_remote_modules:
module_description = " %s" % module
elif module in all_local_modules:
module_description = " |new| %s" % module
else:
module_description = " |del| %s" % module
if module in compare.apriori.merged_modules:
status = "Merged into %s. " % compare.apriori.merged_modules[module]
elif module in compare.apriori.renamed_modules:
status = "Renamed to %s. " % compare.apriori.renamed_modules[module]
elif module in compare.apriori.renamed_modules.values():
status = (
"Renamed from %s. "
% [
x
for x in compare.apriori.renamed_modules
if compare.apriori.renamed_modules[x] == module
][0]
)
elif module in no_changes_modules:
status += "No DB layout changes. "
module_descriptions[module_description.ljust(49, " ")] = status.ljust(
49, " "
)
rendered_text = file_template.render(
start_version=start_version,
end_version=end_version,
module_descriptions=module_descriptions,
)
file_name = "modules{}-{}.rst".format(
start_version.replace(".", ""),
end_version.replace(".", ""),
)
file_path = os.path.join(module_coverage_file_folder, file_name)
f = open(file_path, "w+")
f.write(rendered_text)
f.close()
return True

View File

@ -0,0 +1,21 @@
# Copyright 2011-2015 Therp BV <https://therp.nl>
# Copyright 2016 Opener B.V. <https://opener.am>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import fields, models
class UpgradeAttribute(models.Model):
_name = "upgrade.attribute"
_description = "Upgrade Attribute"
name = fields.Char(readonly=True)
value = fields.Char(readonly=True)
record_id = fields.Many2one(
comodel_name="upgrade.record",
index=True,
ondelete="CASCADE",
readonly=True,
)

View File

@ -0,0 +1,96 @@
# Copyright 2011-2015 Therp BV <https://therp.nl>
# Copyright 2016 Opener B.V. <https://opener.am>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from urllib.error import URLError
import odoorpc
from odoo import api, fields, models
from odoo.exceptions import UserError
from odoo.tools.translate import _
class UpgradeComparisonConfig(models.Model):
_name = "upgrade.comparison.config"
_description = "Upgrade Comparison Configuration"
name = fields.Char()
server = fields.Char(required=True, default="localhost")
port = fields.Integer(required=True, default=8069)
database = fields.Char(required=True)
username = fields.Char(required=True, default="admin")
password = fields.Char(required=True, default="admin")
version = fields.Char()
analysis_ids = fields.One2many(
string="Analyses", comodel_name="upgrade.analysis", inverse_name="config_id"
)
analysis_qty = fields.Integer(compute="_compute_analysis_qty")
@api.depends("analysis_ids")
def _compute_analysis_qty(self):
for config in self:
config.analysis_qty = len(config.analysis_ids)
def get_connection(self):
self.ensure_one()
try:
remote = odoorpc.ODOO(self.server, port=self.port)
except URLError as exc:
raise UserError(
_("Could not connect the Odoo server at %(server)s:%(port)s")
% {"server": self.server, "port": self.port}
) from exc
remote.login(self.database, self.username, self.password)
self.version = remote.version
return remote
def test_connection(self):
self.ensure_one()
try:
connection = self.get_connection()
user_model = connection.env["res.users"]
ids = user_model.search([("login", "=", "admin")])
user_info = user_model.read([ids[0]], ["name"])[0]
except Exception as e:
raise UserError(_("Connection failed.\n\nDETAIL: %s") % e) from e
return {
"type": "ir.actions.client",
"tag": "display_notification",
"params": {
"type": "info",
"message": _(
"You are correctly connected to the server %(server)s"
" (version %(version)s) with the user %(user_name)s"
)
% dict(
server=self.server,
version=self.version,
user_name=user_info["name"],
),
},
}
def new_analysis(self):
self.ensure_one()
analysis = self.env["upgrade.analysis"].create({"config_id": self.id})
return {
"name": analysis._description,
"view_mode": "form",
"res_model": analysis._name,
"type": "ir.actions.act_window",
# "target": "new",
"res_id": analysis.id,
# "nodestroy": True,
}
def action_show_analysis(self):
self.ensure_one()
return {}

View File

@ -0,0 +1,185 @@
# Copyright 2011-2015 Therp BV <https://therp.nl>
# Copyright 2016-2020 Opener B.V. <https://opener.am>
# Copyright 2019 ForgeFlow <https://forgeflow.com>
# Copyright 2020 GRAP <https://grap.coop>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
import ast
import logging
import os
from odoo import api, fields, models
from odoo.exceptions import ValidationError
from odoo.modules.module import MANIFEST_NAMES, get_module_path
from odoo.tools.translate import _
_logger = logging.getLogger(__name__)
class UpgradeRecord(models.Model):
_name = "upgrade.record"
_description = "Upgrade Record"
name = fields.Char(readonly=True)
module = fields.Char(readonly=True)
model = fields.Char(readonly=True)
field = fields.Char(readonly=True)
mode = fields.Selection(
[("create", "Create"), ("modify", "Modify")],
help="Set to Create if a field is newly created "
"in this module. If this module modifies an attribute of an "
"existing field, set to Modify.",
readonly=True,
)
type = fields.Selection(
[("field", "Field"), ("xmlid", "XML ID"), ("model", "Model")],
readonly=True,
)
attribute_ids = fields.One2many(
comodel_name="upgrade.attribute", inverse_name="record_id", readonly=True
)
noupdate = fields.Boolean(readonly=True)
domain = fields.Char(readonly=True)
definition = fields.Char(readonly=True)
prefix = fields.Char(compute="_compute_prefix_and_suffix")
suffix = fields.Char(compute="_compute_prefix_and_suffix")
model_original_module = fields.Char(compute="_compute_model_original_module")
model_type = fields.Char(compute="_compute_model_type")
@api.depends("name")
def _compute_prefix_and_suffix(self):
for rec in self:
rec.prefix, rec.suffix = rec.name.split(".", 1)
@api.depends("model", "type")
def _compute_model_original_module(self):
for rec in self:
if rec.type == "model":
rec.model_original_module = self.env[rec.model]._original_module
else:
rec.model_original_module = ""
@api.depends("model", "type")
def _compute_model_type(self):
for rec in self:
if rec.type == "model":
model = self.env[rec.model]
if model._auto and model._transient:
rec.model_type = "transient"
elif model._auto:
rec.model_type = ""
elif not model._auto and model._abstract:
rec.model_type = "abstract"
else:
rec.model_type = "sql_view"
else:
rec.model_type = ""
@api.model
def field_dump(self):
keys = [
"attachment",
"module",
"mode",
"model",
"field",
"type",
"isfunction",
"isproperty",
"isrelated",
"relation",
"required",
"stored",
"selection_keys",
"hasdefault",
"table",
"_inherits",
"_order",
]
template = {x: False for x in keys}
data = []
for record in self.search([("type", "=", "field")]):
repre = template.copy()
repre.update(
{
"module": record.module,
"model": record.model,
"field": record.field,
"mode": record.mode,
}
)
repre.update({x.name: x.value for x in record.attribute_ids})
if repre["table"]:
repre.update(
{
"column1": self.env[repre["model"]]
._fields[repre["field"]]
.column1,
"column2": self.env[repre["model"]]
._fields[repre["field"]]
.column2,
}
)
data.append(repre)
return data
@api.model
def list_modules(self):
"""Return the set of covered modules"""
self.env.cr.execute(
"""SELECT DISTINCT(module) FROM upgrade_record
ORDER BY module"""
)
return [module for module, in self.env.cr.fetchall()]
@staticmethod
def _read_manifest(addon_dir):
for manifest_name in MANIFEST_NAMES:
if os.access(os.path.join(addon_dir, manifest_name), os.R_OK):
with open(os.path.join(addon_dir, manifest_name), "r") as f:
manifest_string = f.read()
return ast.literal_eval(manifest_string)
raise ValidationError(
_("No manifest found in %(addon_dir)s") % {"addon_dir": addon_dir}
)
@api.model
def get_xml_records(self, module):
"""Return all XML records from the given module"""
addon_dir = get_module_path(module)
manifest = self._read_manifest(addon_dir)
# The order of the keys are important.
# Load files in the same order as in
# module/loading.py:load_module_graph
files = []
for key in ["init_xml", "update_xml", "data"]:
if not manifest.get(key):
continue
for xml_file in manifest[key]:
if not xml_file.lower().endswith(".xml"):
continue
parts = xml_file.split("/")
try:
with open(os.path.join(addon_dir, *parts), "r") as xml_handle:
files.append(xml_handle.read())
except UnicodeDecodeError:
_logger.warning(
"Encoding error: Unable to read %s",
os.path.join(addon_dir, *parts),
)
continue
return files

View File

@ -0,0 +1,3 @@
from . import addons
from . import odoo
from . import odoo_patch

View File

@ -0,0 +1,3 @@
from . import mrp
from . import point_of_sale
from . import stock

View File

@ -0,0 +1,11 @@
# flake8: noqa: B902
from odoo.addons import mrp
from ...odoo_patch import OdooPatch
class PreInitHookPatch(OdooPatch):
target = mrp
method_names = ["_pre_init_mrp"]
def _pre_init_mrp(cr):
"""Don't try to create an existing column on reinstall"""

View File

@ -0,0 +1,13 @@
# flake8: noqa: B902
from odoo import api
from odoo.addons.point_of_sale.models import pos_config
from ...odoo_patch import OdooPatch
class PreInitHookPatch(OdooPatch):
target = pos_config.PosConfig
method_names = ["post_install_pos_localisation"]
@api.model
def post_install_pos_localisation(cr):
"""Do not configure twice pos_localisation"""

View File

@ -0,0 +1,11 @@
# flake8: noqa: B902
from odoo.addons import stock
from ...odoo_patch import OdooPatch
class PreInitHookPatch(OdooPatch):
target = stock
method_names = ["pre_init_hook"]
def pre_init_hook(cr):
"""Don't unlink stock data on reinstall"""

View File

@ -0,0 +1,4 @@
from . import addons
from . import models
from . import modules
from . import tools

View File

@ -0,0 +1 @@
from . import base

View File

@ -0,0 +1 @@
from . import models

View File

@ -0,0 +1 @@
from . import ir_model

View File

@ -0,0 +1,40 @@
from odoo.addons.base.models import ir_model
from ...... import upgrade_log
from .....odoo_patch import OdooPatch
class IrModelConstraintPatch(OdooPatch):
target = ir_model.IrModelConstraint
method_names = ["_reflect_model"]
def _reflect_model(self, model):
"""Reflect the _sql_constraints of the given model."""
def cons_text(txt):
return txt.lower().replace(", ", ",").replace(" (", "(")
# map each constraint on the name of the module where it is defined
constraint_module = {
constraint[0]: cls._module
for cls in reversed(type(model).mro())
if not getattr(cls, "pool", None)
for constraint in getattr(cls, "_local_sql_constraints", ())
}
data_list = []
for (key, definition, message) in model._sql_constraints:
conname = "%s_%s" % (model._table, key)
module = constraint_module.get(key)
record = self._reflect_constraint(
model, conname, "u", cons_text(definition), module, message
)
if record:
xml_id = "%s.constraint_%s" % (module, conname)
data_list.append(dict(xml_id=xml_id, record=record))
self.env["ir.model.data"]._update_xmlids(data_list)
for data in data_list:
xml_id = data.get("xml_id")
module = xml_id.split(".")[0]
upgrade_log.log_xml_id(self.env.cr, module, xml_id)

View File

@ -0,0 +1,23 @@
from odoo import api, models
from ... import upgrade_log
from ..odoo_patch import OdooPatch
class BaseModelPatch(OdooPatch):
target = models.BaseModel
method_names = ["_convert_records"]
@api.model
def _convert_records(self, records, log=lambda a: None):
"""Log data ids that are imported with `load`"""
current_module = self.env.context["module"]
for res in BaseModelPatch._convert_records._original_method(
self, records, log=log
):
_id, xid, _record, _info = res
if xid:
xid = xid if "." in xid else "{}.{}".format(current_module, xid)
upgrade_log.log_xml_id(self.env.cr, current_module, xid)
yield res

View File

@ -0,0 +1 @@
from . import registry

View File

@ -0,0 +1,33 @@
import logging
from threading import current_thread
from odoo import SUPERUSER_ID, api
from odoo.modules.registry import Registry
from .... import upgrade_log
from ...odoo_patch import OdooPatch
_logger = logging.getLogger(__name__)
class RegistryPatch(OdooPatch):
target = Registry
method_names = ["init_models"]
def init_models(self, cr, model_names, context, install=True):
module_name = context["module"]
_logger.debug("Logging models of module %s", module_name)
upg_registry = current_thread()._upgrade_registry
local_registry = {}
env = api.Environment(cr, SUPERUSER_ID, {})
for model in env.values():
if not model._auto:
continue
upgrade_log.log_model(model, local_registry)
upgrade_log.compare_registries(
cr, context["module"], upg_registry, local_registry
)
return RegistryPatch.init_models._original_method(
self, cr, model_names, context, install=install
)

View File

@ -0,0 +1 @@
from . import convert

View File

@ -0,0 +1,14 @@
from odoo.tools.convert import xml_import
from .... import upgrade_log
from ...odoo_patch import OdooPatch
class XMLImportPatch(OdooPatch):
target = xml_import
method_names = ["_test_xml_id"]
def _test_xml_id(self, xml_id):
res = XMLImportPatch._test_xml_id._original_method(self, xml_id)
upgrade_log.log_xml_id(self.env.cr, self.module, xml_id)
return res

View File

@ -0,0 +1,61 @@
import logging
_logger = logging.getLogger(__name__)
class OdooPatch(object):
"""Simple mechanism to apply a collection of monkeypatches using a
context manager.
Classes can register their monkeypatches by inheriting from this class.
They need to define a `target` member, referring to the object or module
that needs to be patched, and a list `method_names`. They also need to
redefine those methods under the same name.
The original method is made available on the new method as
`_original_method`.
Example:
```
from odoo import api
from odoo.addons.some_module.models.my_model import MyModel
class MyModelPatch(OdooPatch):
target = MyModel
method_names = ['do_something']
@api.model
def do_something(self):
res = MyModelPatch.do_something._original_method()
...
return res
```
Usage:
```
with OdooPatch():
do_something()
```
"""
def __enter__(self):
for cls in OdooPatch.__subclasses__():
for method_name in cls.method_names:
method = getattr(cls, method_name)
method._original_method = getattr(cls.target, method_name)
setattr(cls.target, method_name, method)
def __exit__(self, exc_type, exc_value, tb):
for cls in OdooPatch.__subclasses__():
for method_name in cls.method_names:
method = getattr(cls.target, method_name)
if hasattr(method, "_original_method"):
setattr(cls.target, method_name, method._original_method)
else:
_logger.warning(
"_original_method not found on method %s of class %s",
method_name,
cls.target,
)

View File

@ -0,0 +1,7 @@
* Stefan Rijnhart <stefan@opener.amsterdam>
* Holger Brunn <hbrunn@therp.nl>
* Pedro M. Baeza <pedro.baeza@gmail.com>
* Ferdinand Gassauer <gass@cc-l-12.chircar.at>
* Florent Xicluna <florent.xicluna@gmail.com>
* Miquel Raïch <miquel.raich@forgeflow.com>
* Sylvain LE GAL <https://twitter.com/legalsylvain>

View File

@ -0,0 +1,3 @@
This module provides the tool to generate the database analysis files that indicate how the Odoo data model and module data have changed between two versions of Odoo. Database analysis files for the core modules are included in the OpenUpgrade distribution so as a migration script developer you will not usually need to use this tool yourself. If you do need to run your analysis of a custom set of modules, please refer to the documentation here: https://doc.therp.nl/openupgrade/analysis.html
This module is just a tool, a continuation of the old openupgrade_records in OpenUpgrade in previous versions. It's not recommended to have this module in a production database.

View File

@ -0,0 +1,3 @@
* Log removed modules in the module that owned them (#468)
* Detect renamed many2many tables (#213)
* Make sure that the ``migration_analysis.txt`` file is always generated in all cases. (See: https://github.com/OCA/OpenUpgrade/pull/3209#issuecomment-1157449981)

View File

@ -0,0 +1 @@
`Usage instructions <https://oca.github.io/OpenUpgrade/analyse.html>`_

View File

@ -0,0 +1,7 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_upgrade_record,upgrade.record all,model_upgrade_record,,1,0,0,0
access_upgrade_attribute,upgrade.attribute all,model_upgrade_attribute,,1,0,0,0
access_upgrade_comparison_config,upgrade.comparison.config,model_upgrade_comparison_config,base.group_system,1,1,1,1
access_upgrade_analysis,access_upgrade_analysis,model_upgrade_analysis,base.group_system,1,1,1,1
access_upgrade_generate_record_wizard,access_upgrade_generate_record_wizard,model_upgrade_generate_record_wizard,base.group_system,1,1,1,1
access_upgrade_install_wizard,access_upgrade_install_wizard,model_upgrade_install_wizard,base.group_system,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_upgrade_record upgrade.record all model_upgrade_record 1 0 0 0
3 access_upgrade_attribute upgrade.attribute all model_upgrade_attribute 1 0 0 0
4 access_upgrade_comparison_config upgrade.comparison.config model_upgrade_comparison_config base.group_system 1 1 1 1
5 access_upgrade_analysis access_upgrade_analysis model_upgrade_analysis base.group_system 1 1 1 1
6 access_upgrade_generate_record_wizard access_upgrade_generate_record_wizard model_upgrade_generate_record_wizard base.group_system 1 1 1 1
7 access_upgrade_install_wizard access_upgrade_install_wizard model_upgrade_install_wizard base.group_system 1 1 1 1

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

View File

@ -0,0 +1,444 @@
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils: http://docutils.sourceforge.net/" />
<title>Upgrade Analysis</title>
<style type="text/css">
/*
:Author: David Goodger (goodger@python.org)
:Id: $Id: html4css1.css 7952 2016-07-26 18:15:59Z milde $
:Copyright: This stylesheet has been placed in the public domain.
Default cascading style sheet for the HTML output of Docutils.
See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to
customize this style sheet.
*/
/* used to remove borders from tables and images */
.borderless, table.borderless td, table.borderless th {
border: 0 }
table.borderless td, table.borderless th {
/* Override padding for "table.docutils td" with "! important".
The right padding separates the table cells. */
padding: 0 0.5em 0 0 ! important }
.first {
/* Override more specific margin styles with "! important". */
margin-top: 0 ! important }
.last, .with-subtitle {
margin-bottom: 0 ! important }
.hidden {
display: none }
.subscript {
vertical-align: sub;
font-size: smaller }
.superscript {
vertical-align: super;
font-size: smaller }
a.toc-backref {
text-decoration: none ;
color: black }
blockquote.epigraph {
margin: 2em 5em ; }
dl.docutils dd {
margin-bottom: 0.5em }
object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] {
overflow: hidden;
}
/* Uncomment (and remove this text!) to get bold-faced definition list terms
dl.docutils dt {
font-weight: bold }
*/
div.abstract {
margin: 2em 5em }
div.abstract p.topic-title {
font-weight: bold ;
text-align: center }
div.admonition, div.attention, div.caution, div.danger, div.error,
div.hint, div.important, div.note, div.tip, div.warning {
margin: 2em ;
border: medium outset ;
padding: 1em }
div.admonition p.admonition-title, div.hint p.admonition-title,
div.important p.admonition-title, div.note p.admonition-title,
div.tip p.admonition-title {
font-weight: bold ;
font-family: sans-serif }
div.attention p.admonition-title, div.caution p.admonition-title,
div.danger p.admonition-title, div.error p.admonition-title,
div.warning p.admonition-title, .code .error {
color: red ;
font-weight: bold ;
font-family: sans-serif }
/* Uncomment (and remove this text!) to get reduced vertical space in
compound paragraphs.
div.compound .compound-first, div.compound .compound-middle {
margin-bottom: 0.5em }
div.compound .compound-last, div.compound .compound-middle {
margin-top: 0.5em }
*/
div.dedication {
margin: 2em 5em ;
text-align: center ;
font-style: italic }
div.dedication p.topic-title {
font-weight: bold ;
font-style: normal }
div.figure {
margin-left: 2em ;
margin-right: 2em }
div.footer, div.header {
clear: both;
font-size: smaller }
div.line-block {
display: block ;
margin-top: 1em ;
margin-bottom: 1em }
div.line-block div.line-block {
margin-top: 0 ;
margin-bottom: 0 ;
margin-left: 1.5em }
div.sidebar {
margin: 0 0 0.5em 1em ;
border: medium outset ;
padding: 1em ;
background-color: #ffffee ;
width: 40% ;
float: right ;
clear: right }
div.sidebar p.rubric {
font-family: sans-serif ;
font-size: medium }
div.system-messages {
margin: 5em }
div.system-messages h1 {
color: red }
div.system-message {
border: medium outset ;
padding: 1em }
div.system-message p.system-message-title {
color: red ;
font-weight: bold }
div.topic {
margin: 2em }
h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
margin-top: 0.4em }
h1.title {
text-align: center }
h2.subtitle {
text-align: center }
hr.docutils {
width: 75% }
img.align-left, .figure.align-left, object.align-left, table.align-left {
clear: left ;
float: left ;
margin-right: 1em }
img.align-right, .figure.align-right, object.align-right, table.align-right {
clear: right ;
float: right ;
margin-left: 1em }
img.align-center, .figure.align-center, object.align-center {
display: block;
margin-left: auto;
margin-right: auto;
}
table.align-center {
margin-left: auto;
margin-right: auto;
}
.align-left {
text-align: left }
.align-center {
clear: both ;
text-align: center }
.align-right {
text-align: right }
/* reset inner alignment in figures */
div.align-right {
text-align: inherit }
/* div.align-center * { */
/* text-align: left } */
.align-top {
vertical-align: top }
.align-middle {
vertical-align: middle }
.align-bottom {
vertical-align: bottom }
ol.simple, ul.simple {
margin-bottom: 1em }
ol.arabic {
list-style: decimal }
ol.loweralpha {
list-style: lower-alpha }
ol.upperalpha {
list-style: upper-alpha }
ol.lowerroman {
list-style: lower-roman }
ol.upperroman {
list-style: upper-roman }
p.attribution {
text-align: right ;
margin-left: 50% }
p.caption {
font-style: italic }
p.credits {
font-style: italic ;
font-size: smaller }
p.label {
white-space: nowrap }
p.rubric {
font-weight: bold ;
font-size: larger ;
color: maroon ;
text-align: center }
p.sidebar-title {
font-family: sans-serif ;
font-weight: bold ;
font-size: larger }
p.sidebar-subtitle {
font-family: sans-serif ;
font-weight: bold }
p.topic-title {
font-weight: bold }
pre.address {
margin-bottom: 0 ;
margin-top: 0 ;
font: inherit }
pre.literal-block, pre.doctest-block, pre.math, pre.code {
margin-left: 2em ;
margin-right: 2em }
pre.code .ln { color: grey; } /* line numbers */
pre.code, code { background-color: #eeeeee }
pre.code .comment, code .comment { color: #5C6576 }
pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
pre.code .literal.string, code .literal.string { color: #0C5404 }
pre.code .name.builtin, code .name.builtin { color: #352B84 }
pre.code .deleted, code .deleted { background-color: #DEB0A1}
pre.code .inserted, code .inserted { background-color: #A3D289}
span.classifier {
font-family: sans-serif ;
font-style: oblique }
span.classifier-delimiter {
font-family: sans-serif ;
font-weight: bold }
span.interpreted {
font-family: sans-serif }
span.option {
white-space: nowrap }
span.pre {
white-space: pre }
span.problematic {
color: red }
span.section-subtitle {
/* font-size relative to parent (h1..h6 element) */
font-size: 80% }
table.citation {
border-left: solid 1px gray;
margin-left: 1px }
table.docinfo {
margin: 2em 4em }
table.docutils {
margin-top: 0.5em ;
margin-bottom: 0.5em }
table.footnote {
border-left: solid 1px black;
margin-left: 1px }
table.docutils td, table.docutils th,
table.docinfo td, table.docinfo th {
padding-left: 0.5em ;
padding-right: 0.5em ;
vertical-align: top }
table.docutils th.field-name, table.docinfo th.docinfo-name {
font-weight: bold ;
text-align: left ;
white-space: nowrap ;
padding-left: 0 }
/* "booktabs" style (no vertical lines) */
table.docutils.booktabs {
border: 0px;
border-top: 2px solid;
border-bottom: 2px solid;
border-collapse: collapse;
}
table.docutils.booktabs * {
border: 0px;
}
table.docutils.booktabs th {
border-bottom: thin solid;
text-align: left;
}
h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
font-size: 100% }
ul.auto-toc {
list-style-type: none }
</style>
</head>
<body>
<div class="document" id="upgrade-analysis">
<h1 class="title">Upgrade Analysis</h1>
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<p><a class="reference external" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external" href="https://github.com/OCA/server-tools/tree/16.0/upgrade_analysis"><img alt="OCA/server-tools" src="https://img.shields.io/badge/github-OCA%2Fserver--tools-lightgray.png?logo=github" /></a> <a class="reference external" href="https://translation.odoo-community.org/projects/server-tools-16-0/server-tools-16-0-upgrade_analysis"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external" href="https://runbot.odoo-community.org/runbot/149/16.0"><img alt="Try me on Runbot" src="https://img.shields.io/badge/runbot-Try%20me-875A7B.png" /></a></p>
<p>This module provides the tool to generate the database analysis files that indicate how the Odoo data model and module data have changed between two versions of Odoo. Database analysis files for the core modules are included in the OpenUpgrade distribution so as a migration script developer you will not usually need to use this tool yourself. If you do need to run your analysis of a custom set of modules, please refer to the documentation here: <a class="reference external" href="https://doc.therp.nl/openupgrade/analysis.html">https://doc.therp.nl/openupgrade/analysis.html</a></p>
<p>This module is just a tool, a continuation of the old openupgrade_records in OpenUpgrade in previous versions. Its not recommended to have this module in a production database.</p>
<p><strong>Table of contents</strong></p>
<div class="contents local topic" id="contents">
<ul class="simple">
<li><a class="reference internal" href="#usage" id="id1">Usage</a></li>
<li><a class="reference internal" href="#known-issues-roadmap" id="id2">Known issues / Roadmap</a></li>
<li><a class="reference internal" href="#bug-tracker" id="id3">Bug Tracker</a></li>
<li><a class="reference internal" href="#credits" id="id4">Credits</a><ul>
<li><a class="reference internal" href="#authors" id="id5">Authors</a></li>
<li><a class="reference internal" href="#contributors" id="id6">Contributors</a></li>
<li><a class="reference internal" href="#maintainers" id="id7">Maintainers</a></li>
</ul>
</li>
</ul>
</div>
<div class="section" id="usage">
<h1><a class="toc-backref" href="#id1">Usage</a></h1>
<p><a class="reference external" href="https://oca.github.io/OpenUpgrade/analyse.html">Usage instructions</a></p>
</div>
<div class="section" id="known-issues-roadmap">
<h1><a class="toc-backref" href="#id2">Known issues / Roadmap</a></h1>
<ul class="simple">
<li>Log removed modules in the module that owned them (#468)</li>
<li>Detect renamed many2many tables (#213)</li>
<li>Make sure that the <tt class="docutils literal">migration_analysis.txt</tt> file is always generated in all cases. (See: <a class="reference external" href="https://github.com/OCA/OpenUpgrade/pull/3209#issuecomment-1157449981">https://github.com/OCA/OpenUpgrade/pull/3209#issuecomment-1157449981</a>)</li>
</ul>
</div>
<div class="section" id="bug-tracker">
<h1><a class="toc-backref" href="#id3">Bug Tracker</a></h1>
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/server-tools/issues">GitHub Issues</a>.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us smashing it by providing a detailed and welcomed
<a class="reference external" href="https://github.com/OCA/server-tools/issues/new?body=module:%20upgrade_analysis%0Aversion:%2016.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
<p>Do not contact contributors directly about support or help with technical issues.</p>
</div>
<div class="section" id="credits">
<h1><a class="toc-backref" href="#id4">Credits</a></h1>
<div class="section" id="authors">
<h2><a class="toc-backref" href="#id5">Authors</a></h2>
<ul class="simple">
<li>Therp BV</li>
<li>Opener B.V.</li>
<li>GRAP</li>
</ul>
</div>
<div class="section" id="contributors">
<h2><a class="toc-backref" href="#id6">Contributors</a></h2>
<ul class="simple">
<li>Stefan Rijnhart &lt;<a class="reference external" href="mailto:stefan&#64;opener.amsterdam">stefan&#64;opener.amsterdam</a>&gt;</li>
<li>Holger Brunn &lt;<a class="reference external" href="mailto:hbrunn&#64;therp.nl">hbrunn&#64;therp.nl</a>&gt;</li>
<li>Pedro M. Baeza &lt;<a class="reference external" href="mailto:pedro.baeza&#64;gmail.com">pedro.baeza&#64;gmail.com</a>&gt;</li>
<li>Ferdinand Gassauer &lt;<a class="reference external" href="mailto:gass&#64;cc-l-12.chircar.at">gass&#64;cc-l-12.chircar.at</a>&gt;</li>
<li>Florent Xicluna &lt;<a class="reference external" href="mailto:florent.xicluna&#64;gmail.com">florent.xicluna&#64;gmail.com</a>&gt;</li>
<li>Miquel Raïch &lt;<a class="reference external" href="mailto:miquel.raich&#64;forgeflow.com">miquel.raich&#64;forgeflow.com</a>&gt;</li>
<li>Sylvain LE GAL &lt;<a class="reference external" href="https://twitter.com/legalsylvain">https://twitter.com/legalsylvain</a>&gt;</li>
</ul>
</div>
<div class="section" id="maintainers">
<h2><a class="toc-backref" href="#id7">Maintainers</a></h2>
<p>This module is maintained by the OCA.</p>
<a class="reference external image-reference" href="https://odoo-community.org"><img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" /></a>
<p>OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.</p>
<p>Current <a class="reference external" href="https://odoo-community.org/page/maintainer-role">maintainers</a>:</p>
<p><a class="reference external" href="https://github.com/StefanRijnhart"><img alt="StefanRijnhart" src="https://github.com/StefanRijnhart.png?size=40px" /></a> <a class="reference external" href="https://github.com/legalsylvain"><img alt="legalsylvain" src="https://github.com/legalsylvain.png?size=40px" /></a></p>
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/server-tools/tree/16.0/upgrade_analysis">OCA/server-tools</a> project on GitHub.</p>
<p>You are welcome to contribute. To learn how please visit <a class="reference external" href="https://odoo-community.org/page/Contribute">https://odoo-community.org/page/Contribute</a>.</p>
</div>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,12 @@
Module coverage ${start_version} -> ${end_version}
============================
.. include:: coverage_legend.rst
+-------------------------------------------------+----------------------+-------------------------------------------------+
| Module | Status + Extra Information |
+=================================================+======================+=================================================+
% for module, extra_information in module_descriptions.items():
|${module}| |${extra_information}|
+-------------------------------------------------+----------------------+-------------------------------------------------+
% endfor

View File

@ -0,0 +1 @@
from . import test_module

View File

@ -0,0 +1,46 @@
from odoo.tests import common, tagged
@tagged("post_install", "-at_install")
class TestUpgradeAnalysis(common.TransactionCase):
def setUp(self):
super().setUp()
self.IrModuleModule = self.env["ir.module.module"]
self.product_module = self.IrModuleModule.search([("name", "=", "product")])
self.sale_module = self.IrModuleModule.search([("name", "=", "sale")])
self.upgrade_analysis = self.IrModuleModule.search(
[("name", "=", "upgrade_analysis")]
)
def test_upgrade_install_wizard(self):
InstallWizard = self.env["upgrade.install.wizard"]
wizard = InstallWizard.create({})
wizard.select_odoo_modules()
self.assertTrue(
self.product_module.id in wizard.module_ids.ids,
"Select Odoo module should select 'product' module",
)
wizard.select_oca_modules()
self.assertTrue(
self.upgrade_analysis.id in wizard.module_ids.ids,
"Select OCA module should select 'upgrade_analysis' module",
)
wizard.select_other_modules()
self.assertFalse(
self.product_module.id in wizard.module_ids.ids,
"Select Other module should not select 'product' module",
)
wizard.unselect_modules()
self.assertEqual(
wizard.module_ids.ids, [], "Unselect module should clear the selection"
)
# For the time being, tests doens't call install_modules() function
# because installing module in a test context will execute the test
# of the installed modules, raising finally an error:
# TypeError: Many2many fields ir.actions.server.partner_ids and
# ir.actions.server.partner_ids use the same table and columns

View File

@ -0,0 +1,235 @@
# Copyright 2011-2015 Therp BV <https://therp.nl>
# Copyright 2016 Opener B.V. <https://opener.am>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
import logging
from openupgradelib.openupgrade_tools import table_exists
from odoo import models
_logger = logging.getLogger(__name__)
def get_record_id(cr, module, model, field, mode):
"""
OpenUpgrade: get or create the id from the record table matching
the key parameter values
"""
cr.execute(
"SELECT id FROM upgrade_record "
"WHERE module = %s AND model = %s AND "
"field = %s AND mode = %s AND type = %s",
(module, model, field, mode, "field"),
)
record = cr.fetchone()
if record:
return record[0]
cr.execute(
"INSERT INTO upgrade_record "
"(create_date, module, model, field, mode, type) "
"VALUES (NOW() AT TIME ZONE 'UTC', %s, %s, %s, %s, %s)",
(module, model, field, mode, "field"),
)
cr.execute(
"SELECT id FROM upgrade_record "
"WHERE module = %s AND model = %s AND "
"field = %s AND mode = %s AND type = %s",
(module, model, field, mode, "field"),
)
return cr.fetchone()[0]
def compare_registries(cr, module, registry, local_registry):
"""
OpenUpgrade: Compare the local registry with the global registry,
log any differences and merge the local registry with
the global one.
"""
if not table_exists(cr, "upgrade_record"):
return
for model, flds in local_registry.items():
registry.setdefault(model, {})
for field, attributes in flds.items():
old_field = registry[model].setdefault(field, {})
mode = old_field and "modify" or "create"
record_id = False
for key, value in attributes.items():
if key not in old_field or old_field[key] != value:
if not record_id:
record_id = get_record_id(cr, module, model, field, mode)
cr.execute(
"SELECT id FROM upgrade_attribute "
"WHERE name = %s AND value = %s AND "
"record_id = %s",
(key, value, record_id),
)
if not cr.fetchone():
cr.execute(
"INSERT INTO upgrade_attribute "
"(create_date, name, value, record_id) "
"VALUES (NOW() AT TIME ZONE 'UTC', %s, %s, %s)",
(key, value, record_id),
)
old_field[key] = value
def hasdefault(field):
"""Return a representation of the field's default method.
The default method is only meaningful if the field is a regular read/write
field with a `default` method or a `compute` method.
Note that Odoo fields accept a literal value as a `default` attribute
this value is wrapped in a lambda expression in odoo/fields.py:
https://github.com/odoo/odoo/blob/7eeba9d/odoo/fields.py#L484-L487
"""
if (
not field.readonly # It's not a proper computed field
and not field.inverse # It's not a field that delegates their data
and not isrelated(field) # It's not an (unstored) related field.
):
if field.default:
return "default"
if field.compute:
return "compute"
return ""
def isfunction(field):
if (
field.compute
and (field.readonly or field.inverse)
and not field.related
and not field.company_dependent
):
return "function"
return ""
def isproperty(field):
if field.company_dependent:
return "property"
return ""
def isrelated(field):
if field.related:
return "related"
return ""
def _get_relation(field):
if field.type in ("many2many", "many2one", "one2many"):
return field.comodel_name
elif field.type == "many2one_reference":
return field.model_field
else:
return ""
def log_model(model, local_registry):
"""
OpenUpgrade: Store the characteristics of the BaseModel and its fields
in the local registry, so that we can compare changes with the
main registry
"""
if not model._name:
return
typemap = {"monetary": "float"}
# persistent models only
if isinstance(model, models.TransientModel):
return
model_registry = local_registry.setdefault(model._name, {})
if model._inherits:
model_registry["_inherits"] = {"_inherits": str(model._inherits)}
model_registry["_order"] = {"_order": model._order}
for fieldname, field in model._fields.items():
properties = {
"type": typemap.get(field.type, field.type),
"isfunction": isfunction(field),
"isproperty": isproperty(field),
"isrelated": isrelated(field),
"relation": _get_relation(field),
"table": field.relation if field.type == "many2many" else "",
"required": field.required and "required" or "",
"stored": field.store and "stored" or "",
"selection_keys": "",
"hasdefault": hasdefault(field),
}
if field.type == "selection":
if isinstance(field.selection, (tuple, list)):
properties["selection_keys"] = str(
sorted(x[0] for x in field.selection)
)
else:
properties["selection_keys"] = "function"
elif field.type == "binary":
properties["attachment"] = str(getattr(field, "attachment", False))
for key, value in properties.items():
if value:
model_registry.setdefault(fieldname, {})[key] = value
def log_xml_id(cr, module, xml_id):
"""
Log xml_ids at load time in the records table.
Called from:
- tools/convert.py:xml_import._test_xml_id()
- odoo/models.py:BaseModel._convert_records()
- odoo/addons/base/models/ir_model.py:IrModelConstraint._reflect_model()
# Catcha's
- The module needs to be loaded with 'init', or the calling method
won't be called. This can be brought about by installing the
module or updating the 'state' field of the module to 'to install'
or call the server with '--init <module>' and the database argument.
- Do you get the right results immediately when installing the module?
No, sorry. This method retrieves the model from the ir_model_table, but
when the xml id is encountered for the first time, this method is called
before the item is present in this table. Therefore, you will not
get any meaningful results until the *second* time that you 'init'
the module.
- The good news is that the upgrade_analysis module that comes
with this distribution allows you to deal with all of this with
one click on the menu item Settings -> Customizations ->
Database Structure -> OpenUpgrade -> Generate Records
- You cannot reinitialize the modules in your production database
and expect to keep working on it happily ever after. Do not perform
this routine on your production database.
:param module: The module that contains the xml_id
:param xml_id: the xml_id, with or without 'module.' prefix
"""
if not table_exists(cr, "upgrade_record"):
return
if "." not in xml_id:
xml_id = "{}.{}".format(module, xml_id)
cr.execute(
"SELECT model FROM ir_model_data " "WHERE module = %s AND name = %s",
xml_id.split("."),
)
record = cr.fetchone()
if not record:
_logger.warning("Cannot find xml_id %s", xml_id)
return
else:
cr.execute(
"SELECT id FROM upgrade_record "
"WHERE module=%s AND model=%s AND name=%s AND type=%s",
(module, record[0], xml_id, "xmlid"),
)
if not cr.fetchone():
cr.execute(
"INSERT INTO upgrade_record "
"(create_date, module, model, name, type) "
"values(NOW() AT TIME ZONE 'UTC', %s, %s, %s, %s)",
(module, record[0], xml_id, "xmlid"),
)

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<menuitem
id="menu_upgrade"
name="Upgrade Analysis"
parent="base.menu_administration"
sequence="99"
/>
</odoo>

View File

@ -0,0 +1,75 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record id="view_upgrade_analysis_tree" model="ir.ui.view">
<field name="model">upgrade.analysis</field>
<field name="arch" type="xml">
<tree decoration-info="state == 'draft'">
<field name="config_id" />
<field name="analysis_date" />
<field name="state" />
</tree>
</field>
</record>
<record id="view_upgrade_analysis_form" model="ir.ui.view">
<field name="model">upgrade.analysis</field>
<field name="arch" type="xml">
<form create="false">
<header>
<field name="state" widget="statusbar" />
<button
name="analyze"
string="Perform Analysis"
type="object"
icon="fa-cogs"
colspan="2"
/>
</header>
<sheet>
<group col="4" colspan="4">
<field
name="config_id"
attrs="{'readonly': [('state', '=', 'done')]}"
/>
<field
name="write_files"
attrs="{'readonly': [('state', '=', 'done')]}"
/>
<field
name="upgrade_path"
attrs="{'invisible': [('write_files', '=', False)]}"
/>
<field
name="analysis_date"
attrs="{'invisible': [('analysis_date', '=', False)]}"
/>
</group>
<group string="Log">
<field
name="log"
nolabel="1"
widget="ace"
options="{'mode': 'txt'}"
/>
</group>
</sheet>
</form>
</field>
</record>
<record id="action_upgrade_analysis_tree" model="ir.actions.act_window">
<field name="name">Upgrade Analyses</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">upgrade.analysis</field>
</record>
<menuitem
action="action_upgrade_analysis_tree"
id="menu_upgrade_analysis"
name="Upgrade Analyses"
parent="menu_upgrade"
/>
</odoo>

View File

@ -0,0 +1,79 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record id="view_upgrade_comparison_config_tree" model="ir.ui.view">
<field name="model">upgrade.comparison.config</field>
<field name="arch" type="xml">
<tree>
<field name="name" />
<field name="server" />
<field name="port" />
<field name="database" />
</tree>
</field>
</record>
<record id="view_upgrade_comparison_config_form" model="ir.ui.view">
<field name="model">upgrade.comparison.config</field>
<field name="arch" type="xml">
<form>
<header>
</header>
<sheet>
<div class="oe_button_box" name="button_box">
<button
name="action_show_analysis"
type="object"
class="oe_stat_button"
icon="fa-users"
>
<field
string="Analyses"
name="analysis_qty"
widget="statinfo"
/>
</button>
</div>
<group>
<field name="name" />
<field name="server" />
<field name="port" />
<field name="database" />
<field name="username" />
<field name="password" />
<field name="version" />
</group>
<button
name="test_connection"
string="Test Connection"
type="object"
icon="fa-television"
colspan="2"
/>
<newline />
<button
name="new_analysis"
string="New Analysis"
type="object"
icon="fa-cogs"
colspan="2"
/>
</sheet>
</form>
</field>
</record>
<record id="action_upgrade_comparison_config_tree" model="ir.actions.act_window">
<field name="name">upgrade Comparison Configs</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">upgrade.comparison.config</field>
</record>
<menuitem
action="action_upgrade_comparison_config_tree"
id="menu_upgrade_comparison_config"
name="Comparison Configurations"
parent="menu_upgrade"
/>
</odoo>

View File

@ -0,0 +1,81 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record id="view_upgrade_record_search" model="ir.ui.view">
<field name="model">upgrade.record</field>
<field name="arch" type="xml">
<search>
<field name="module" />
<field name="model" />
<field name="field" />
<field name="type" />
<filter
name="filter_create"
string="Create Mode"
domain="[('mode', '=', 'create')]"
/>
<filter
name="filter_modify"
string="Modify Mode"
domain="[('mode', '=', 'modify')]"
/>
<filter name="group_by_module" context="{'group_by': 'module'}" />
<filter name="group_by_model" context="{'group_by': 'model'}" />
</search>
</field>
</record>
<record id="view_upgrade_record_tree" model="ir.ui.view">
<field name="model">upgrade.record</field>
<field name="arch" type="xml">
<tree>
<field name="module" />
<field name="model" />
<field name="field" />
<field name="name" />
<field name="type" />
<field name="mode" />
</tree>
</field>
</record>
<record id="view_upgrade_record_form" model="ir.ui.view">
<field name="model">upgrade.record</field>
<field name="arch" type="xml">
<form>
<sheet>
<group col="4" colspan="4">
<field name="name" />
<field name="module" />
<field name="model" />
<field name="field" />
<field name="type" />
<field name="mode" />
</group>
<group string="Attributes">
<field name="attribute_ids" nolabel="1" colspan="4">
<tree>
<field name="name" />
<field name="value" />
</tree>
</field>
</group>
</sheet>
</form>
</field>
</record>
<record id="action_upgrade_record_tree" model="ir.actions.act_window">
<field name="name">upgrade Records</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">upgrade.record</field>
</record>
<menuitem
action="action_upgrade_record_tree"
id="menu_upgrade_records"
name="Records"
parent="menu_upgrade"
/>
</odoo>

View File

@ -0,0 +1,2 @@
from . import upgrade_generate_record_wizard
from . import upgrade_install_wizard

View File

@ -0,0 +1,127 @@
# Copyright 2011-2015 Therp BV <https://therp.nl>
# Copyright 2016 Opener B.V. <https://opener.am>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from threading import current_thread
from odoo import _, fields, models
from odoo.exceptions import UserError
from odoo.modules.registry import Registry
from ..odoo_patch.odoo_patch import OdooPatch
class GenerateWizard(models.TransientModel):
_name = "upgrade.generate.record.wizard"
_description = "Upgrade Generate Record Wizard"
state = fields.Selection([("draft", "Draft"), ("done", "Done")], default="draft")
def generate(self):
"""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
self.env.cr.execute("TRUNCATE upgrade_attribute, upgrade_record;")
# Check of all the modules are correctly installed
modules = self.env["ir.module.module"].search(
[("state", "in", ["to install", "to upgrade"])]
)
if modules:
raise UserError(
_("Cannot seem to install or upgrade modules %s")
% (", ".join([module.name for module in modules]))
)
# Now reinitialize all installed modules
self.env["ir.module.module"].search([("state", "=", "installed")]).write(
{"state": "to install"}
)
self.env.cr.commit() # pylint: disable=invalid-commit
# Patch the registry on the thread
thread = current_thread()
thread._upgrade_registry = {}
# Regenerate the registry with monkeypatches that log the records
with OdooPatch():
Registry.new(self.env.cr.dbname, update_module=True)
# Free the registry
delattr(thread, "_upgrade_registry")
# Set domain property
self.env.cr.execute(
""" UPDATE upgrade_record our
SET domain = iaw.domain
FROM ir_model_data imd
JOIN ir_act_window iaw ON imd.res_id = iaw.id
WHERE our.type = 'xmlid'
AND imd.model = 'ir.actions.act_window'
AND our.model = imd.model
AND our.name = imd.module || '.' || imd.name
"""
)
self.env.cache.invalidate(
[
(self.env["upgrade.record"]._fields["domain"], None),
]
)
# Set constraint definition
self.env.cr.execute(
""" UPDATE upgrade_record our
SET definition = btrim(replace(replace(replace(replace(
imc.definition, chr(9), ' '), chr(10), ' '), ' ', ' '), ' ', ' '))
FROM ir_model_data imd
JOIN ir_model_constraint imc ON imd.res_id = imc.id
WHERE our.type = 'xmlid'
AND imd.model = 'ir.model.constraint'
AND our.model = imd.model
AND our.name = imd.module || '.' || imd.name"""
)
self.env.cache.invalidate(
[
(self.env["upgrade.record"]._fields["definition"], None),
]
)
# Set noupdate property from ir_model_data
self.env.cr.execute(
""" UPDATE upgrade_record our
SET noupdate = imd.noupdate
FROM ir_model_data imd
WHERE our.type = 'xmlid'
AND our.model = imd.model
AND our.name = imd.module || '.' || imd.name
"""
)
self.env.cache.invalidate(
[
(self.env["upgrade.record"]._fields["noupdate"], None),
]
)
# Log model records
self.env.cr.execute(
"""INSERT INTO upgrade_record
(create_date, module, name, model, type)
SELECT NOW() AT TIME ZONE 'UTC',
imd2.module, imd2.module || '.' || imd.name AS name,
im.model, 'model' AS type
FROM (
SELECT min(id) as id, name, res_id
FROM ir_model_data
WHERE name LIKE 'model_%' AND model = 'ir.model'
GROUP BY name, res_id
) imd
JOIN ir_model_data imd2 ON imd2.id = imd.id
JOIN ir_model im ON imd.res_id = im.id
ORDER BY imd.name, imd.id""",
)
return self.write({"state": "done"})

View File

@ -0,0 +1,117 @@
# Copyright 2011-2015 Therp BV <https://therp.nl>
# Copyright 2016 Opener B.V. <https://opener.am>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import api, fields, models
from odoo.modules.registry import Registry
from odoo.osv.expression import AND
from ..blacklist import (
BLACKLIST_MODULES,
BLACKLIST_MODULES_ENDS_WITH,
BLACKLIST_MODULES_STARTS_WITH,
)
class UpgradeInstallWizard(models.TransientModel):
_name = "upgrade.install.wizard"
_description = "Upgrade Install Wizard"
state = fields.Selection(
[("draft", "Draft"), ("done", "Done")], readonly=True, default="draft"
)
module_ids = fields.Many2many(
comodel_name="ir.module.module",
domain=lambda x: x._module_ids_domain(),
)
module_qty = fields.Integer(
string="Modules Quantity", compute="_compute_module_qty"
)
@api.model
def _module_ids_domain(self, extra_domain=None):
domain = [
"&",
("state", "not in", ["uninstallable", "unknown"]),
("name", "not in", BLACKLIST_MODULES),
]
if extra_domain:
domain = AND([domain, extra_domain])
modules = self.env["ir.module.module"].search(domain)
for start_pattern in BLACKLIST_MODULES_STARTS_WITH:
modules = modules.filtered(
lambda x, start_pattern=start_pattern: not x.name.startswith(
start_pattern
)
)
for end_pattern in BLACKLIST_MODULES_ENDS_WITH:
modules = modules.filtered(
lambda x, end_pattern=end_pattern: not x.name.endswith(end_pattern)
)
return [("id", "in", modules.ids)]
@api.depends("module_ids")
def _compute_module_qty(self):
for wizard in self:
wizard.module_qty = len(wizard.module_ids)
def select_odoo_modules(self, extra_domain=None):
self.ensure_one()
modules = self.env["ir.module.module"].search(
self._module_ids_domain(extra_domain=extra_domain)
)
modules = modules.filtered(lambda x: x.is_odoo_module)
self.module_ids = modules
return self.return_same_form_view()
def select_oca_modules(self, extra_domain=None):
self.ensure_one()
modules = self.env["ir.module.module"].search(
self._module_ids_domain(extra_domain=extra_domain)
)
modules = modules.filtered(lambda x: x.is_oca_module)
self.module_ids = modules
return self.return_same_form_view()
def select_other_modules(self, extra_domain=None):
self.ensure_one()
modules = self.env["ir.module.module"].search(
self._module_ids_domain(extra_domain=extra_domain)
)
modules = modules.filtered(lambda x: not (x.is_oca_module or x.is_odoo_module))
self.module_ids = modules
return self.return_same_form_view()
def select_installable_modules(self, extra_domain=None):
self.ensure_one()
self.module_ids = self.env["ir.module.module"].search(
self._module_ids_domain(extra_domain=extra_domain)
)
return self.return_same_form_view()
def unselect_modules(self):
self.ensure_one()
self.module_ids = False
return self.return_same_form_view()
def install_modules(self):
"""Set all selected modules and actually install them."""
self.ensure_one()
self.module_ids.write({"state": "to install"})
self.env.cr.commit() # pylint: disable=invalid-commit
Registry.new(self.env.cr.dbname, update_module=True)
self.write({"state": "done"})
return self.return_same_form_view()
def return_same_form_view(self):
return {
"type": "ir.actions.act_window",
"res_model": "upgrade.install.wizard",
"view_mode": "form",
"res_id": self.id,
"views": [(False, "form")],
"target": "new",
}

View File

@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record id="view_upgrade_generate_record_wizard_form" model="ir.ui.view">
<field name="model">upgrade.generate.record.wizard</field>
<field name="arch" type="xml">
<form>
<header>
<field name="state" widget="statusbar" />
</header>
<group states="draft" colspan="4">
<p
>This will reinitialize all the modules installed on this database. Do not continue if you use this database in production.</p>
</group>
<group states="done" colspan="4">
<p>Modules initialized and record created</p>
</group>
<footer>
<button
string="Continue"
name="generate"
type="object"
states="draft"
class="btn-primary"
/>
<button special="cancel" string="Close" class="btn-default" />
</footer>
</form>
</field>
</record>
<record id="action_upgrade_generate_record_wizard" model="ir.actions.act_window">
<field name="name">Generate Records Wizard</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">upgrade.generate.record.wizard</field>
<field name="view_mode">form,tree</field>
<field name="target">new</field>
</record>
<menuitem
name="Generate Records Wizard"
id="menu_upgrade_generate_record"
parent="menu_upgrade"
action="action_upgrade_generate_record_wizard"
sequence="15"
/>
</odoo>

View File

@ -0,0 +1,89 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record id="view_upgrade_install_wizard_form" model="ir.ui.view">
<field name="model">upgrade.install.wizard</field>
<field name="arch" type="xml">
<form>
<header>
<field name="state" widget="statusbar" />
</header>
<group states="draft">
<p
class="alert alert-warning"
role="alert"
>This will install the selected modules on the database. Do not continue if you use this database in production.</p>
</group>
<group states="done">
<p
class="alert alert-info"
role="alert"
>The modules have been installed successfuly</p>
</group>
<group col="4" states="draft">
<button
name="select_installable_modules"
type="object"
string="All Modules"
/>
<button
name="select_odoo_modules"
type="object"
string="All Odoo SA Modules"
class="btn-primary"
/>
<button
name="select_oca_modules"
type="object"
string="All OCA Modules"
/>
<button
name="select_other_modules"
type="object"
string="All Other Modules"
/>
</group>
<group states="draft">
<field name="module_qty" />
<field
name="module_ids"
widget="many2many_tags"
options="{'no_create': True}"
/>
<button
name="unselect_modules"
type="object"
string="Clear the list"
/>
</group>
<footer>
<button
name="install_modules"
type="object"
class="btn-primary"
string="Install Modules"
states="draft"
/>
<button special="cancel" string="Close" class="btn-default" />
</footer>
</form>
</field>
</record>
<record id="action_upgrade_install_wizard" model="ir.actions.act_window">
<field name="name">Install Modules Wizard</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">upgrade.install.wizard</field>
<field name="view_mode">form,tree</field>
<field name="target">new</field>
</record>
<menuitem
name="Install Modules Wizard"
id="menu_upgrade_install"
parent="menu_upgrade"
action="action_upgrade_install_wizard"
sequence="14"
/>
</odoo>