diff --git a/spec_driven_model/__manifest__.py b/spec_driven_model/__manifest__.py index 885597b722a3..4dded68f3697 100644 --- a/spec_driven_model/__manifest__.py +++ b/spec_driven_model/__manifest__.py @@ -3,12 +3,11 @@ { "name": "Spec Driven Model", - "summary": """ - Tools for specifications driven mixins (from xsd for instance)""", + "summary": """XML binding for Odoo: XML to Odoo models and models to XML.""", "version": "15.0.1.3.1", "maintainers": ["rvalyi"], "license": "LGPL-3", - "author": "Akretion,Odoo Community Association (OCA)", + "author": "Akretion, Odoo Community Association (OCA)", "website": "https://github.com/OCA/l10n-brazil", "depends": [], "data": [], diff --git a/spec_driven_model/models/spec_mixin.py b/spec_driven_model/models/spec_mixin.py index ec0e6e9270f1..cba16344c57d 100644 --- a/spec_driven_model/models/spec_mixin.py +++ b/spec_driven_model/models/spec_mixin.py @@ -43,27 +43,50 @@ def _get_concrete_model(self, model_name): else: return self.env.get(model_name) + def _spec_prefix(self): + """ + _spec_prefix should be available for all generated specs mixins + and it should be defined in SpecModel to avoid circular imports. + """ + return SpecModel._ensure_spec_prefix(self._context) + + def _get_spec_property(self, spec_property="", fallback=None): + """ + Used to access schema wise and version wise automatic mappings properties + """ + return getattr(self, f"_{self._spec_prefix()}_{spec_property}", fallback) + + def _get_stacking_points(self): + return self._get_spec_property("stacking_points", {}) + def _register_hook(self): """ Called once all modules are loaded. - Here we take all spec models that are not injected into existing concrete + Here we take all spec models that were not injected into existing concrete Odoo models and we make them concrete automatically with their _auto_init method that will create their SQL DDL structure. """ res = super()._register_hook() - if not hasattr(self, "_spec_module"): + if "spec_schema" not in self._context: return res - - load_key = "_%s_loaded" % (self._spec_module,) + spec_module = self._get_spec_property("odoo_module") + if not spec_module: + return res + odoo_module = spec_module.split("_spec.")[0].split(".")[-1] + load_key = f"_{spec_module}_loaded" if hasattr(self.env.registry, load_key): # already done for registry return res setattr(self.env.registry, load_key, True) access_data = [] access_fields = [] + relation_prefix = ( + f"{self._context['spec_schema']}.{self._context['spec_version']}.%" + ) + field_prefix = f"{self._context['spec_schema']}{self._context['spec_version']}_" self.env.cr.execute( """SELECT DISTINCT relation FROM ir_model_fields WHERE relation LIKE %s;""", - (f"{self._schema_name}.{self._schema_version.replace('.', '')[:2]}.%",), + (relation_prefix,), ) # now we will filter only the spec models not injected into some existing class: remaining_models = { @@ -73,17 +96,14 @@ def _register_hook(self): and not SPEC_MIXIN_MAPPINGS[self.env.cr.dbname].get(i[0]) } for name in remaining_models: - spec_class = StackedModel._odoo_name_to_class(name, self._spec_module) + spec_class = StackedModel._odoo_name_to_class(name, spec_module) if spec_class is None: continue spec_class._module = "fiscal" # TODO use python_module ? fields = self.env[spec_class._name].fields_get_keys() rec_name = next( filter( - lambda x: ( - x.startswith(self.env[spec_class._name]._field_prefix) - and "_choice" not in x - ), + lambda x: (x.startswith(field_prefix) and "_choice" not in x), fields, ) ) @@ -93,16 +113,16 @@ def _register_hook(self): { "_name": name, "_inherit": spec_class._inherit, - "_original_module": "fiscal", - "_odoo_module": self._odoo_module, - "_spec_module": self._spec_module, + "_original_module": odoo_module, "_rec_name": rec_name, - "_module": self._odoo_module, + "_module": odoo_module, }, ) - model_type._schema_name = self._schema_name - model_type._schema_version = self._schema_version - models.MetaModel.module_to_models[self._odoo_module] += [model_type] + # we set _spec_schema and _spec_version because + # _build_model will not have context access: + model_type._spec_schema = self._context["spec_schema"] + model_type._spec_version = self._context["spec_version"] + models.MetaModel.module_to_models[odoo_module] += [model_type] # now we init these models properly # a bit like odoo.modules.loading#load_module_graph would do @@ -123,11 +143,11 @@ def _register_hook(self): "perm_create", "perm_unlink", ] - model._auto_fill_access_data(self.env, self._odoo_module, access_data) + model._auto_fill_access_data(self.env, odoo_module, access_data) self.env["ir.model.access"].load(access_fields, access_data) self.env.registry.init_models( - self.env.cr, remaining_models, {"module": self._odoo_module} + self.env.cr, remaining_models, {"module": odoo_module} ) return res diff --git a/spec_driven_model/models/spec_models.py b/spec_driven_model/models/spec_models.py index c05b5eb9f3b0..4961bde6dcbe 100644 --- a/spec_driven_model/models/spec_models.py +++ b/spec_driven_model/models/spec_models.py @@ -64,7 +64,7 @@ def _compute_display_name(self): res = super()._compute_display_name() for rec in self: if rec.display_name == "False" or not rec.display_name: - rec.display_name = _("Abrir...") + rec.display_name = _("Open...") return res def _get_stacking_points(self): @@ -73,14 +73,6 @@ def _get_stacking_points(self): return getattr(self, key)["stacking_points"] return {} - @classmethod - def _ensure_spec_prefix(cls, context=None, spec_schema=None, spec_version=None): - if context and context.get("spec_schema"): - spec_schema = context.get("spec_schema") - if context and context.get("spec_version"): - spec_version = context.get("spec_version") - return "%s%s" % (spec_schema, spec_version.replace(".", "")[:2]) - @classmethod def _build_model(cls, pool, cr): """ @@ -193,7 +185,6 @@ def _setup_fields(self): @classmethod def _map_concrete(cls, dbname, key, target, quiet=False): - # TODO bookkeep according to a key to allow multiple injection contexts if not quiet: _logger.debug(f"{key} ---> {target}") global SPEC_MIXIN_MAPPINGS @@ -245,15 +236,14 @@ class StackedModel(SpecModel): @classmethod def _build_model(cls, pool, cr): - mod = import_module(".".join(cls.__module__.split(".")[:-1])) - if hasattr(cls, "_spec_schema"): + if hasattr(cls, "_spec_schema"): # when called via _register_hook schema = cls._spec_schema version = cls._spec_version.replace(".", "")[:2] else: mod = import_module(".".join(cls.__module__.split(".")[:-1])) schema = mod.spec_schema version = mod.spec_version.replace(".", "")[:2] - spec_prefix = cls._ensure_spec_prefix(spec_schema=schema, spec_version=version) + spec_prefix = f"{schema}{version}" stacking_settings = getattr(cls, "_%s_spec_settings" % (spec_prefix,)) # inject all stacked m2o as inherited classes _logger.info(f"building StackedModel {cls._name} {cls}")