mirror of
https://github.com/bringout/oca-ocb-core.git
synced 2026-04-20 02:12:01 +02:00
19.0 vanilla
This commit is contained in:
parent
0a7ae8db93
commit
991d2234ca
416 changed files with 646602 additions and 300844 deletions
|
|
@ -1,90 +1,72 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
""" Modules (also called addons) management.
|
||||
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import datetime
|
||||
import itertools
|
||||
import logging
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
import typing
|
||||
import traceback
|
||||
|
||||
import odoo
|
||||
import odoo.modules.db
|
||||
import odoo.modules.graph
|
||||
import odoo.modules.migration
|
||||
import odoo.modules.registry
|
||||
from .. import SUPERUSER_ID, api, tools
|
||||
import odoo.sql_db
|
||||
import odoo.tools.sql
|
||||
import odoo.tools.translate
|
||||
from odoo import api, tools
|
||||
from odoo.tools.convert import convert_file, IdRef, ConvertMode as LoadMode
|
||||
|
||||
from . import db as modules_db
|
||||
from .migration import MigrationManager
|
||||
from .module import adapt_version, initialize_sys_path, load_openerp_module
|
||||
from .module_graph import ModuleGraph
|
||||
from .registry import Registry
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from collections.abc import Collection, Iterable
|
||||
from odoo.api import Environment
|
||||
from odoo.sql_db import BaseCursor
|
||||
from odoo.tests.result import OdooTestResult
|
||||
from .module_graph import ModuleNode
|
||||
|
||||
LoadKind = typing.Literal['data', 'demo']
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def load_data(env, idref, mode, kind, package):
|
||||
def load_data(env: Environment, idref: IdRef, mode: LoadMode, kind: LoadKind, package: ModuleNode) -> bool:
|
||||
"""
|
||||
|
||||
kind: data, demo, test, init_xml, update_xml, demo_xml.
|
||||
|
||||
noupdate is False, unless it is demo data or it is csv data in
|
||||
init mode.
|
||||
noupdate is False, unless it is demo data
|
||||
|
||||
:returns: Whether a file was loaded
|
||||
:rtype: bool
|
||||
"""
|
||||
keys = ('init_xml', 'data') if kind == 'data' else ('demo',)
|
||||
|
||||
def _get_files_of_kind(kind):
|
||||
if kind == 'demo':
|
||||
keys = ['demo_xml', 'demo']
|
||||
elif kind == 'data':
|
||||
keys = ['init_xml', 'update_xml', 'data']
|
||||
if isinstance(kind, str):
|
||||
keys = [kind]
|
||||
files = []
|
||||
for k in keys:
|
||||
for f in package.data[k]:
|
||||
if f in files:
|
||||
_logger.warning("File %s is imported twice in module %s %s", f, package.name, kind)
|
||||
files.append(f)
|
||||
if k.endswith('_xml') and not (k == 'init_xml' and not f.endswith('.xml')):
|
||||
# init_xml, update_xml and demo_xml are deprecated except
|
||||
# for the case of init_xml with csv and sql files as
|
||||
# we can't specify noupdate for those file.
|
||||
correct_key = 'demo' if k.count('demo') else 'data'
|
||||
_logger.warning(
|
||||
"module %s: key '%s' is deprecated in favor of '%s' for file '%s'.",
|
||||
package.name, k, correct_key, f
|
||||
)
|
||||
return files
|
||||
files: set[str] = set()
|
||||
for k in keys:
|
||||
if k == 'init_xml' and package.manifest[k]:
|
||||
_logger.warning("module %s: key 'init_xml' is deprecated in Odoo 19.", package.name)
|
||||
for filename in package.manifest[k]:
|
||||
if filename in files:
|
||||
_logger.warning("File %s is imported twice in module %s %s", filename, package.name, kind)
|
||||
files.add(filename)
|
||||
|
||||
filename = None
|
||||
try:
|
||||
if kind in ('demo', 'test'):
|
||||
threading.current_thread().testing = True
|
||||
for filename in _get_files_of_kind(kind):
|
||||
_logger.info("loading %s/%s", package.name, filename)
|
||||
noupdate = False
|
||||
if kind in ('demo', 'demo_xml') or (filename.endswith('.csv') and kind in ('init', 'init_xml')):
|
||||
noupdate = True
|
||||
tools.convert_file(env, package.name, filename, idref, mode, noupdate, kind)
|
||||
finally:
|
||||
if kind in ('demo', 'test'):
|
||||
threading.current_thread().testing = False
|
||||
convert_file(env, package.name, filename, idref, mode, noupdate=kind == 'demo')
|
||||
|
||||
return bool(filename)
|
||||
return bool(files)
|
||||
|
||||
def load_demo(env, package, idref, mode):
|
||||
|
||||
def load_demo(env: Environment, package: ModuleNode, idref: IdRef, mode: LoadMode) -> bool:
|
||||
"""
|
||||
Loads demo data for the specified package.
|
||||
"""
|
||||
if not package.should_have_demo():
|
||||
return False
|
||||
|
||||
try:
|
||||
if package.data.get('demo') or package.data.get('demo_xml'):
|
||||
if package.manifest.get('demo') or package.manifest.get('demo_xml'):
|
||||
_logger.info("Module %s: loading demo", package.name)
|
||||
with env.cr.savepoint(flush=False):
|
||||
load_data(env(su=True), idref, mode, kind='demo', package=package)
|
||||
|
|
@ -103,46 +85,47 @@ def load_demo(env, package, idref, mode):
|
|||
return False
|
||||
|
||||
|
||||
def force_demo(env):
|
||||
def force_demo(env: Environment) -> None:
|
||||
"""
|
||||
Forces the `demo` flag on all modules, and installs demo data for all installed modules.
|
||||
"""
|
||||
graph = odoo.modules.graph.Graph()
|
||||
env.cr.execute('UPDATE ir_module_module SET demo=True')
|
||||
env.cr.execute(
|
||||
"SELECT name FROM ir_module_module WHERE state IN ('installed', 'to upgrade', 'to remove')"
|
||||
)
|
||||
module_list = [name for (name,) in env.cr.fetchall()]
|
||||
graph.add_modules(env.cr, module_list, ['demo'])
|
||||
graph = ModuleGraph(env.cr, mode='load')
|
||||
graph.extend(module_list)
|
||||
|
||||
for package in graph:
|
||||
load_demo(env, package, {}, 'init')
|
||||
|
||||
env['ir.module.module'].invalidate_model(['demo'])
|
||||
env['res.groups']._update_user_groups_view()
|
||||
|
||||
|
||||
def load_module_graph(env, graph, status=None, perform_checks=True,
|
||||
skip_modules=None, report=None, models_to_check=None):
|
||||
"""Migrates+Updates or Installs all module nodes from ``graph``
|
||||
def load_module_graph(
|
||||
env: Environment,
|
||||
graph: ModuleGraph,
|
||||
update_module: bool = False,
|
||||
report: OdooTestResult | None = None,
|
||||
models_to_check: set[str] | None = None,
|
||||
install_demo: bool = True,
|
||||
) -> None:
|
||||
""" Load, upgrade and install not loaded module nodes in the ``graph`` for ``env.registry``
|
||||
|
||||
:param env:
|
||||
:param graph: graph of module nodes to load
|
||||
:param status: deprecated parameter, unused, left to avoid changing signature in 8.0
|
||||
:param perform_checks: whether module descriptors should be checked for validity (prints warnings
|
||||
for same cases)
|
||||
:param skip_modules: optional list of module names (packages) which have previously been loaded and can be skipped
|
||||
:param update_module: whether to update modules or not
|
||||
:param report:
|
||||
:param set models_to_check:
|
||||
:return: list of modules that were installed or updated
|
||||
:param install_demo: whether to attempt installing demo data for newly installed modules
|
||||
"""
|
||||
if models_to_check is None:
|
||||
models_to_check = set()
|
||||
|
||||
processed_modules = []
|
||||
loaded_modules = []
|
||||
registry = env.registry
|
||||
migrations = odoo.modules.migration.MigrationManager(env.cr, graph)
|
||||
assert isinstance(env.cr, odoo.sql_db.Cursor), "Need for a real Cursor to load modules"
|
||||
migrations = MigrationManager(env.cr, graph)
|
||||
module_count = len(graph)
|
||||
_logger.info('loading %d modules...', module_count)
|
||||
|
||||
|
|
@ -157,102 +140,98 @@ def load_module_graph(env, graph, status=None, perform_checks=True,
|
|||
module_name = package.name
|
||||
module_id = package.id
|
||||
|
||||
if skip_modules and module_name in skip_modules:
|
||||
if module_name in registry._init_modules:
|
||||
continue
|
||||
|
||||
module_t0 = time.time()
|
||||
module_cursor_query_count = env.cr.sql_log_count
|
||||
module_extra_query_count = odoo.sql_db.sql_counter
|
||||
|
||||
needs_update = (
|
||||
hasattr(package, "init")
|
||||
or hasattr(package, "update")
|
||||
or package.state in ("to install", "to upgrade")
|
||||
)
|
||||
update_operation = (
|
||||
'install' if package.state == 'to install' else
|
||||
'upgrade' if package.state == 'to upgrade' else
|
||||
'reinit' if module_name in registry._reinit_modules else
|
||||
None
|
||||
) if update_module else None
|
||||
module_log_level = logging.DEBUG
|
||||
if needs_update:
|
||||
if update_operation:
|
||||
module_log_level = logging.INFO
|
||||
_logger.log(module_log_level, 'Loading module %s (%d/%d)', module_name, index, module_count)
|
||||
|
||||
new_install = package.state == 'to install'
|
||||
if needs_update:
|
||||
if not new_install:
|
||||
if update_operation:
|
||||
if update_operation == 'upgrade' or module_name in registry._force_upgrade_scripts:
|
||||
if package.name != 'base':
|
||||
registry.setup_models(env.cr)
|
||||
registry._setup_models__(env.cr, []) # incremental setup
|
||||
migrations.migrate_module(package, 'pre')
|
||||
if package.name != 'base':
|
||||
env.flush_all()
|
||||
|
||||
load_openerp_module(package.name)
|
||||
|
||||
if new_install:
|
||||
if update_operation == 'install':
|
||||
py_module = sys.modules['odoo.addons.%s' % (module_name,)]
|
||||
pre_init = package.info.get('pre_init_hook')
|
||||
pre_init = package.manifest.get('pre_init_hook')
|
||||
if pre_init:
|
||||
registry.setup_models(env.cr)
|
||||
registry._setup_models__(env.cr, []) # incremental setup
|
||||
getattr(py_module, pre_init)(env)
|
||||
|
||||
model_names = registry.load(env.cr, package)
|
||||
model_names = registry.load(package)
|
||||
|
||||
mode = 'update'
|
||||
if hasattr(package, 'init') or package.state == 'to install':
|
||||
mode = 'init'
|
||||
|
||||
loaded_modules.append(package.name)
|
||||
if needs_update:
|
||||
if update_operation:
|
||||
model_names = registry.descendants(model_names, '_inherit', '_inherits')
|
||||
models_updated |= set(model_names)
|
||||
models_to_check -= set(model_names)
|
||||
registry.setup_models(env.cr)
|
||||
registry.init_models(env.cr, model_names, {'module': package.name}, new_install)
|
||||
elif package.state != 'to remove':
|
||||
registry._setup_models__(env.cr, []) # incremental setup
|
||||
registry.init_models(env.cr, model_names, {'module': package.name}, update_operation == 'install')
|
||||
elif update_module and package.state != 'to remove':
|
||||
# The current module has simply been loaded. The models extended by this module
|
||||
# and for which we updated the schema, must have their schema checked again.
|
||||
# This is because the extension may have changed the model,
|
||||
# e.g. adding required=True to an existing field, but the schema has not been
|
||||
# updated by this module because it's not marked as 'to upgrade/to install'.
|
||||
model_names = registry.descendants(model_names, '_inherit', '_inherits')
|
||||
models_to_check |= set(model_names) & models_updated
|
||||
|
||||
idref = {}
|
||||
|
||||
if needs_update:
|
||||
if update_operation:
|
||||
# Can't put this line out of the loop: ir.module.module will be
|
||||
# registered by init_models() above.
|
||||
module = env['ir.module.module'].browse(module_id)
|
||||
module._check()
|
||||
|
||||
if perform_checks:
|
||||
module._check()
|
||||
idref: dict = {}
|
||||
|
||||
if package.state == 'to upgrade':
|
||||
if update_operation == 'install':
|
||||
load_data(env, idref, 'init', kind='data', package=package)
|
||||
if install_demo and package.demo_installable:
|
||||
package.demo = load_demo(env, package, idref, 'init')
|
||||
else: # 'upgrade' or 'reinit'
|
||||
# upgrading the module information
|
||||
module.write(module.get_values_from_terp(package.data))
|
||||
load_data(env, idref, mode, kind='data', package=package)
|
||||
demo_loaded = package.dbdemo = load_demo(env, package, idref, mode)
|
||||
env.cr.execute('update ir_module_module set demo=%s where id=%s', (demo_loaded, module_id))
|
||||
module.write(module.get_values_from_terp(package.manifest))
|
||||
mode = 'update' if update_operation == 'upgrade' else 'init'
|
||||
load_data(env, idref, mode, kind='data', package=package)
|
||||
if package.demo:
|
||||
package.demo = load_demo(env, package, idref, mode)
|
||||
env.cr.execute('UPDATE ir_module_module SET demo = %s WHERE id = %s', (package.demo, module_id))
|
||||
module.invalidate_model(['demo'])
|
||||
|
||||
migrations.migrate_module(package, 'post')
|
||||
|
||||
# Update translations for all installed languages
|
||||
overwrite = odoo.tools.config["overwrite_existing_translations"]
|
||||
overwrite = tools.config["overwrite_existing_translations"]
|
||||
module._update_translations(overwrite=overwrite)
|
||||
|
||||
if package.name is not None:
|
||||
registry._init_modules.add(package.name)
|
||||
|
||||
if needs_update:
|
||||
if new_install:
|
||||
post_init = package.info.get('post_init_hook')
|
||||
if update_operation:
|
||||
if update_operation == 'install':
|
||||
post_init = package.manifest.get('post_init_hook')
|
||||
if post_init:
|
||||
getattr(py_module, post_init)(env)
|
||||
|
||||
if mode == 'update':
|
||||
elif update_operation == 'upgrade':
|
||||
# validate the views that have not been checked yet
|
||||
env['ir.ui.view']._validate_module_views(module_name)
|
||||
|
||||
# need to commit any modification the module's installation or
|
||||
# update made to the schema or data so the tests can run
|
||||
# (separately in their own transaction)
|
||||
env.cr.commit()
|
||||
concrete_models = [model for model in model_names if not registry[model]._abstract]
|
||||
if concrete_models:
|
||||
env.cr.execute("""
|
||||
|
|
@ -270,18 +249,32 @@ def load_module_graph(env, graph, status=None, perform_checks=True,
|
|||
lines.append(f"{module_name}.access_{xmlid},access_{xmlid},{module_name}.model_{xmlid},base.group_user,1,0,0,0")
|
||||
_logger.warning('\n'.join(lines))
|
||||
|
||||
updating = tools.config.options['init'] or tools.config.options['update']
|
||||
test_time = test_queries = 0
|
||||
registry.updated_modules.append(package.name)
|
||||
|
||||
ver = adapt_version(package.manifest['version'])
|
||||
# Set new modules and dependencies
|
||||
module.write({'state': 'installed', 'latest_version': ver})
|
||||
|
||||
package.state = 'installed'
|
||||
module.env.flush_all()
|
||||
module.env.cr.commit()
|
||||
|
||||
test_time = 0.0
|
||||
test_queries = 0
|
||||
test_results = None
|
||||
if tools.config.options['test_enable'] and (needs_update or not updating):
|
||||
|
||||
update_from_config = tools.config['update'] or tools.config['init'] or tools.config['reinit']
|
||||
if tools.config['test_enable'] and (update_operation or not update_from_config):
|
||||
from odoo.tests import loader # noqa: PLC0415
|
||||
suite = loader.make_suite([module_name], 'at_install')
|
||||
if suite.countTestCases():
|
||||
if not needs_update:
|
||||
registry.setup_models(env.cr)
|
||||
if not update_operation:
|
||||
registry._setup_models__(env.cr, []) # incremental setup
|
||||
registry.check_null_constraints(env.cr)
|
||||
# Python tests
|
||||
tests_t0, tests_q0 = time.time(), odoo.sql_db.sql_counter
|
||||
test_results = loader.run_suite(suite, global_report=report)
|
||||
assert report is not None, "Missing report during tests"
|
||||
report.update(test_results)
|
||||
test_time = time.time() - tests_t0
|
||||
test_queries = odoo.sql_db.sql_counter - tests_q0
|
||||
|
|
@ -289,20 +282,6 @@ def load_module_graph(env, graph, status=None, perform_checks=True,
|
|||
# tests may have reset the environment
|
||||
module = env['ir.module.module'].browse(module_id)
|
||||
|
||||
if needs_update:
|
||||
processed_modules.append(package.name)
|
||||
|
||||
ver = adapt_version(package.data['version'])
|
||||
# Set new modules and dependencies
|
||||
module.write({'state': 'installed', 'latest_version': ver})
|
||||
|
||||
package.load_state = package.state
|
||||
package.load_version = package.installed_version
|
||||
package.state = 'installed'
|
||||
for kind in ('init', 'demo', 'update'):
|
||||
if hasattr(package, kind):
|
||||
delattr(package, kind)
|
||||
module.env.flush_all()
|
||||
|
||||
extra_queries = odoo.sql_db.sql_counter - module_extra_query_count - test_queries
|
||||
extras = []
|
||||
|
|
@ -330,59 +309,43 @@ def load_module_graph(env, graph, status=None, perform_checks=True,
|
|||
env.cr.sql_log_count - loading_cursor_query_count,
|
||||
odoo.sql_db.sql_counter - loading_extra_query_count) # extra queries: testes, notify, any other closed cursor
|
||||
|
||||
return loaded_modules, processed_modules
|
||||
|
||||
def _check_module_names(cr, module_names):
|
||||
def _check_module_names(cr: BaseCursor, module_names: Iterable[str]) -> None:
|
||||
mod_names = set(module_names)
|
||||
if 'base' in mod_names:
|
||||
# ignore dummy 'all' module
|
||||
if 'all' in mod_names:
|
||||
mod_names.remove('all')
|
||||
mod_names.discard('all')
|
||||
if mod_names:
|
||||
cr.execute("SELECT count(id) AS count FROM ir_module_module WHERE name in %s", (tuple(mod_names),))
|
||||
if cr.dictfetchone()['count'] != len(mod_names):
|
||||
row = cr.fetchone()
|
||||
assert row is not None # for typing
|
||||
if row[0] != len(mod_names):
|
||||
# find out what module name(s) are incorrect:
|
||||
cr.execute("SELECT name FROM ir_module_module")
|
||||
incorrect_names = mod_names.difference([x['name'] for x in cr.dictfetchall()])
|
||||
_logger.warning('invalid module names, ignored: %s', ", ".join(incorrect_names))
|
||||
|
||||
def load_marked_modules(env, graph, states, force, progressdict, report,
|
||||
loaded_modules, perform_checks, models_to_check=None):
|
||||
"""Loads modules marked with ``states``, adding them to ``graph`` and
|
||||
``loaded_modules`` and returns a list of installed/upgraded modules."""
|
||||
|
||||
if models_to_check is None:
|
||||
models_to_check = set()
|
||||
|
||||
processed_modules = []
|
||||
while True:
|
||||
env.cr.execute("SELECT name from ir_module_module WHERE state IN %s", (tuple(states),))
|
||||
module_list = [name for (name,) in env.cr.fetchall() if name not in graph]
|
||||
if not module_list:
|
||||
break
|
||||
graph.add_modules(env.cr, module_list, force)
|
||||
_logger.debug('Updating graph with %d more modules', len(module_list))
|
||||
loaded, processed = load_module_graph(
|
||||
env, graph, progressdict, report=report, skip_modules=loaded_modules,
|
||||
perform_checks=perform_checks, models_to_check=models_to_check
|
||||
)
|
||||
processed_modules.extend(processed)
|
||||
loaded_modules.extend(loaded)
|
||||
if not processed:
|
||||
break
|
||||
return processed_modules
|
||||
|
||||
def load_modules(registry, force_demo=False, status=None, update_module=False):
|
||||
def load_modules(
|
||||
registry: Registry,
|
||||
*,
|
||||
update_module: bool = False,
|
||||
upgrade_modules: Collection[str] = (),
|
||||
install_modules: Collection[str] = (),
|
||||
reinit_modules: Collection[str] = (),
|
||||
new_db_demo: bool = False,
|
||||
) -> None:
|
||||
""" Load the modules for a registry object that has just been created. This
|
||||
function is part of Registry.new() and should not be used anywhere else.
|
||||
|
||||
:param registry: The new inited registry object used to load modules.
|
||||
:param update_module: Whether to update (install, upgrade, or uninstall) modules. Defaults to ``False``
|
||||
:param upgrade_modules: A collection of module names to upgrade.
|
||||
:param install_modules: A collection of module names to install.
|
||||
:param reinit_modules: A collection of module names to reinitialize.
|
||||
:param new_db_demo: Whether to install demo data for new database. Defaults to ``False``
|
||||
"""
|
||||
initialize_sys_path()
|
||||
|
||||
force = []
|
||||
if force_demo:
|
||||
force.append('demo')
|
||||
|
||||
models_to_check = set()
|
||||
models_to_check: set[str] = set()
|
||||
|
||||
with registry.cursor() as cr:
|
||||
# prevent endless wait for locks on schema changes (during online
|
||||
|
|
@ -390,52 +353,53 @@ def load_modules(registry, force_demo=False, status=None, update_module=False):
|
|||
# connection settings are automatically reset when the connection is
|
||||
# borrowed from the pool
|
||||
cr.execute("SET SESSION lock_timeout = '15s'")
|
||||
if not odoo.modules.db.is_initialized(cr):
|
||||
if not modules_db.is_initialized(cr):
|
||||
if not update_module:
|
||||
_logger.error("Database %s not initialized, you can force it with `-i base`", cr.dbname)
|
||||
return
|
||||
_logger.info("init db")
|
||||
odoo.modules.db.initialize(cr)
|
||||
update_module = True # process auto-installed modules
|
||||
tools.config["init"]["all"] = 1
|
||||
if not tools.config['without_demo']:
|
||||
tools.config["demo"]['all'] = 1
|
||||
_logger.info("Initializing database %s", cr.dbname)
|
||||
modules_db.initialize(cr)
|
||||
elif 'base' in reinit_modules:
|
||||
registry._reinit_modules.add('base')
|
||||
|
||||
if 'base' in tools.config['update'] or 'all' in tools.config['update']:
|
||||
if 'base' in upgrade_modules:
|
||||
cr.execute("update ir_module_module set state=%s where name=%s and state=%s", ('to upgrade', 'base', 'installed'))
|
||||
|
||||
# STEP 1: LOAD BASE (must be done before module dependencies can be computed for later steps)
|
||||
graph = odoo.modules.graph.Graph()
|
||||
graph.add_module(cr, 'base', force)
|
||||
graph = ModuleGraph(cr, mode='update' if update_module else 'load')
|
||||
graph.extend(['base'])
|
||||
if not graph:
|
||||
_logger.critical('module base cannot be loaded! (hint: verify addons-path)')
|
||||
raise ImportError('Module `base` cannot be loaded! (hint: verify addons-path)')
|
||||
if update_module and tools.config['update']:
|
||||
for pyfile in tools.config['pre_upgrade_scripts'].split(','):
|
||||
if update_module and upgrade_modules:
|
||||
for pyfile in tools.config['pre_upgrade_scripts']:
|
||||
odoo.modules.migration.exec_script(cr, graph['base'].installed_version, pyfile, 'base', 'pre')
|
||||
|
||||
if update_module and odoo.tools.sql.table_exists(cr, 'ir_model_fields'):
|
||||
if update_module and tools.sql.table_exists(cr, 'ir_model_fields'):
|
||||
# determine the fields which are currently translated in the database
|
||||
cr.execute("SELECT model || '.' || name FROM ir_model_fields WHERE translate IS TRUE")
|
||||
registry._database_translated_fields = {row[0] for row in cr.fetchall()}
|
||||
cr.execute("SELECT model || '.' || name, translate FROM ir_model_fields WHERE translate IS NOT NULL")
|
||||
registry._database_translated_fields = dict(cr.fetchall())
|
||||
|
||||
# determine the fields which are currently company dependent in the database
|
||||
if odoo.tools.sql.column_exists(cr, 'ir_model_fields', 'company_dependent'):
|
||||
cr.execute("SELECT model || '.' || name FROM ir_model_fields WHERE company_dependent IS TRUE")
|
||||
registry._database_company_dependent_fields = {row[0] for row in cr.fetchall()}
|
||||
|
||||
# processed_modules: for cleanup step after install
|
||||
# loaded_modules: to avoid double loading
|
||||
report = registry._assertion_report
|
||||
env = api.Environment(cr, SUPERUSER_ID, {})
|
||||
loaded_modules, processed_modules = load_module_graph(
|
||||
env, graph, status, perform_checks=update_module,
|
||||
report=report, models_to_check=models_to_check)
|
||||
env = api.Environment(cr, api.SUPERUSER_ID, {})
|
||||
load_module_graph(
|
||||
env,
|
||||
graph,
|
||||
update_module=update_module,
|
||||
report=report,
|
||||
models_to_check=models_to_check,
|
||||
install_demo=new_db_demo,
|
||||
)
|
||||
|
||||
load_lang = tools.config.pop('load_language')
|
||||
load_lang = tools.config._cli_options.pop('load_language', None)
|
||||
if load_lang or update_module:
|
||||
# some base models are used below, so make sure they are set up
|
||||
registry.setup_models(cr)
|
||||
registry._setup_models__(cr, []) # incremental setup
|
||||
|
||||
if load_lang:
|
||||
for lang in load_lang.split(','):
|
||||
|
|
@ -447,54 +411,52 @@ def load_modules(registry, force_demo=False, status=None, update_module=False):
|
|||
_logger.info('updating modules list')
|
||||
Module.update_list()
|
||||
|
||||
_check_module_names(cr, itertools.chain(tools.config['init'], tools.config['update']))
|
||||
_check_module_names(cr, itertools.chain(install_modules, upgrade_modules))
|
||||
|
||||
module_names = [k for k, v in tools.config['init'].items() if v]
|
||||
if module_names:
|
||||
modules = Module.search([('state', '=', 'uninstalled'), ('name', 'in', module_names)])
|
||||
if install_modules:
|
||||
modules = Module.search([('state', '=', 'uninstalled'), ('name', 'in', tuple(install_modules))])
|
||||
if modules:
|
||||
modules.button_install()
|
||||
|
||||
module_names = [k for k, v in tools.config['update'].items() if v]
|
||||
if module_names:
|
||||
modules = Module.search([('state', 'in', ('installed', 'to upgrade')), ('name', 'in', module_names)])
|
||||
if upgrade_modules:
|
||||
modules = Module.search([('state', 'in', ('installed', 'to upgrade')), ('name', 'in', tuple(upgrade_modules))])
|
||||
if modules:
|
||||
modules.button_upgrade()
|
||||
|
||||
if reinit_modules:
|
||||
modules = Module.search([('state', 'in', ('installed', 'to upgrade')), ('name', 'in', tuple(reinit_modules))])
|
||||
reinit_modules = modules.downstream_dependencies(exclude_states=('uninstalled', 'uninstallable', 'to remove', 'to install')) + modules
|
||||
registry._reinit_modules.update(m for m in reinit_modules.mapped('name') if m not in graph._imported_modules)
|
||||
|
||||
env.flush_all()
|
||||
cr.execute("update ir_module_module set state=%s where name=%s", ('installed', 'base'))
|
||||
Module.invalidate_model(['state'])
|
||||
|
||||
# STEP 3: Load marked modules (skipping base which was done in STEP 1)
|
||||
# IMPORTANT: this is done in two parts, first loading all installed or
|
||||
# partially installed modules (i.e. installed/to upgrade), to
|
||||
# offer a consistent system to the second part: installing
|
||||
# newly selected modules.
|
||||
# We include the modules 'to remove' in the first step, because
|
||||
# they are part of the "currently installed" modules. They will
|
||||
# be dropped in STEP 6 later, before restarting the loading
|
||||
# process.
|
||||
# IMPORTANT 2: We have to loop here until all relevant modules have been
|
||||
# processed, because in some rare cases the dependencies have
|
||||
# changed, and modules that depend on an uninstalled module
|
||||
# will not be processed on the first pass.
|
||||
# It's especially useful for migrations.
|
||||
previously_processed = -1
|
||||
while previously_processed < len(processed_modules):
|
||||
previously_processed = len(processed_modules)
|
||||
processed_modules += load_marked_modules(env, graph,
|
||||
['installed', 'to upgrade', 'to remove'],
|
||||
force, status, report, loaded_modules, update_module, models_to_check)
|
||||
# loop this step in case extra modules' states are changed to 'to install'/'to update' during loading
|
||||
while True:
|
||||
if update_module:
|
||||
processed_modules += load_marked_modules(env, graph,
|
||||
['to install'], force, status, report,
|
||||
loaded_modules, update_module, models_to_check)
|
||||
states = ('installed', 'to upgrade', 'to remove', 'to install')
|
||||
else:
|
||||
states = ('installed', 'to upgrade', 'to remove')
|
||||
env.cr.execute("SELECT name from ir_module_module WHERE state IN %s", [states])
|
||||
module_list = [name for (name,) in env.cr.fetchall() if name not in graph]
|
||||
if not module_list:
|
||||
break
|
||||
graph.extend(module_list)
|
||||
_logger.debug('Updating graph with %d more modules', len(module_list))
|
||||
updated_modules_count = len(registry.updated_modules)
|
||||
load_module_graph(
|
||||
env, graph, update_module=update_module,
|
||||
report=report, models_to_check=models_to_check)
|
||||
if len(registry.updated_modules) == updated_modules_count:
|
||||
break
|
||||
|
||||
if update_module:
|
||||
# set up the registry without the patch for translated fields
|
||||
database_translated_fields = registry._database_translated_fields
|
||||
registry._database_translated_fields = ()
|
||||
registry.setup_models(cr)
|
||||
registry._database_translated_fields = {}
|
||||
registry._setup_models__(cr, []) # incremental setup
|
||||
# determine which translated fields should no longer be translated,
|
||||
# and make their model fix the database schema
|
||||
models_to_untranslate = set()
|
||||
|
|
@ -508,7 +470,7 @@ def load_modules(registry, force_demo=False, status=None, update_module=False):
|
|||
registry.init_models(cr, list(models_to_untranslate), {'models_to_check': True})
|
||||
|
||||
registry.loaded = True
|
||||
registry.setup_models(cr)
|
||||
registry._setup_models__(cr)
|
||||
|
||||
# check that all installed modules have been loaded by the registry
|
||||
Module = env['ir.module.module']
|
||||
|
|
@ -518,9 +480,10 @@ def load_modules(registry, force_demo=False, status=None, update_module=False):
|
|||
_logger.error("Some modules are not loaded, some dependencies or manifest may be missing: %s", missing)
|
||||
|
||||
# STEP 3.5: execute migration end-scripts
|
||||
migrations = odoo.modules.migration.MigrationManager(cr, graph)
|
||||
for package in graph:
|
||||
migrations.migrate_module(package, 'end')
|
||||
if update_module:
|
||||
migrations = MigrationManager(cr, graph)
|
||||
for package in graph:
|
||||
migrations.migrate_module(package, 'end')
|
||||
|
||||
# check that new module dependencies have been properly installed after a migration/upgrade
|
||||
cr.execute("SELECT name from ir_module_module WHERE state IN ('to install', 'to upgrade')")
|
||||
|
|
@ -529,10 +492,10 @@ def load_modules(registry, force_demo=False, status=None, update_module=False):
|
|||
_logger.error("Some modules have inconsistent states, some dependencies may be missing: %s", sorted(module_list))
|
||||
|
||||
# STEP 3.6: apply remaining constraints in case of an upgrade
|
||||
registry.finalize_constraints()
|
||||
registry.finalize_constraints(cr)
|
||||
|
||||
# STEP 4: Finish and cleanup installations
|
||||
if processed_modules:
|
||||
if registry.updated_modules:
|
||||
|
||||
cr.execute("SELECT model from ir_model")
|
||||
for (model,) in cr.fetchall():
|
||||
|
|
@ -542,7 +505,7 @@ def load_modules(registry, force_demo=False, status=None, update_module=False):
|
|||
_logger.runbot("Model %s is declared but cannot be loaded! (Perhaps a module was partially removed or renamed)", model)
|
||||
|
||||
# Cleanup orphan records
|
||||
env['ir.model.data']._process_end(processed_modules)
|
||||
env['ir.model.data']._process_end(registry.updated_modules)
|
||||
# Cleanup cron
|
||||
vacuum_cron = env.ref('base.autovacuum_job', raise_if_not_found=False)
|
||||
if vacuum_cron:
|
||||
|
|
@ -551,9 +514,6 @@ def load_modules(registry, force_demo=False, status=None, update_module=False):
|
|||
|
||||
env.flush_all()
|
||||
|
||||
for kind in ('init', 'demo', 'update'):
|
||||
tools.config[kind] = {}
|
||||
|
||||
# STEP 5: Uninstall modules to remove
|
||||
if update_module:
|
||||
# Remove records referenced from ir_model_data for modules to be
|
||||
|
|
@ -563,7 +523,7 @@ def load_modules(registry, force_demo=False, status=None, update_module=False):
|
|||
if modules_to_remove:
|
||||
pkgs = reversed([p for p in graph if p.name in modules_to_remove])
|
||||
for pkg in pkgs:
|
||||
uninstall_hook = pkg.info.get('uninstall_hook')
|
||||
uninstall_hook = pkg.manifest.get('uninstall_hook')
|
||||
if uninstall_hook:
|
||||
py_module = sys.modules['odoo.addons.%s' % (pkg.name,)]
|
||||
getattr(py_module, uninstall_hook)(env)
|
||||
|
|
@ -575,13 +535,13 @@ def load_modules(registry, force_demo=False, status=None, update_module=False):
|
|||
# modules to remove next time
|
||||
cr.commit()
|
||||
_logger.info('Reloading registry once more after uninstalling modules')
|
||||
registry = odoo.modules.registry.Registry.new(
|
||||
cr.dbname, force_demo, status, update_module
|
||||
registry = Registry.new(
|
||||
cr.dbname, update_module=update_module
|
||||
)
|
||||
cr.reset()
|
||||
registry.check_tables_exist(cr)
|
||||
cr.commit()
|
||||
return registry
|
||||
return
|
||||
|
||||
# STEP 5.5: Verify extended fields on every model
|
||||
# This will fix the schema of all models in a situation such as:
|
||||
|
|
@ -590,12 +550,15 @@ def load_modules(registry, force_demo=False, status=None, update_module=False):
|
|||
# - module C is loaded and extends model M;
|
||||
# - module B and C depend on A but not on each other;
|
||||
# The changes introduced by module C are not taken into account by the upgrade of B.
|
||||
if update_module:
|
||||
# We need to fix custom fields for which we have dropped the not-null constraint.
|
||||
cr.execute("""SELECT DISTINCT model FROM ir_model_fields WHERE state = 'manual'""")
|
||||
models_to_check.update(model_name for model_name, in cr.fetchall() if model_name in registry)
|
||||
if models_to_check:
|
||||
registry.init_models(cr, list(models_to_check), {'models_to_check': True})
|
||||
registry.init_models(cr, list(models_to_check), {'models_to_check': True, 'update_custom_fields': True})
|
||||
|
||||
# STEP 6: verify custom views on every model
|
||||
if update_module:
|
||||
env['res.groups']._update_user_groups_view()
|
||||
View = env['ir.ui.view']
|
||||
for model in registry:
|
||||
try:
|
||||
|
|
@ -608,21 +571,20 @@ def load_modules(registry, force_demo=False, status=None, update_module=False):
|
|||
else:
|
||||
_logger.error('At least one test failed when loading the modules.')
|
||||
|
||||
|
||||
# STEP 8: save installed/updated modules for post-install tests and _register_hook
|
||||
registry.updated_modules += processed_modules
|
||||
|
||||
# STEP 9: call _register_hook on every model
|
||||
# This is done *exactly once* when the registry is being loaded. See the
|
||||
# management of those hooks in `Registry.setup_models`: all the calls to
|
||||
# setup_models() done here do not mess up with hooks, as registry.ready
|
||||
# management of those hooks in `Registry._setup_models__`: all the calls to
|
||||
# _setup_models__() done here do not mess up with hooks, as registry.ready
|
||||
# is False.
|
||||
for model in env.values():
|
||||
model._register_hook()
|
||||
env.flush_all()
|
||||
|
||||
# STEP 10: check that we can trust nullable columns
|
||||
registry.check_null_constraints(cr)
|
||||
|
||||
def reset_modules_state(db_name):
|
||||
|
||||
def reset_modules_state(db_name: str) -> None:
|
||||
"""
|
||||
Resets modules flagged as "to x" to their original state
|
||||
"""
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue