Skip to content

Commit

Permalink
Merge PR #797 into 14.0
Browse files Browse the repository at this point in the history
Signed-off-by simahawk
  • Loading branch information
OCA-git-bot committed Aug 21, 2023
2 parents 02bbe02 + ee7a7e8 commit e8aefdf
Show file tree
Hide file tree
Showing 17 changed files with 613 additions and 65 deletions.
3 changes: 2 additions & 1 deletion edi_oca/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
Define backends, exchange types, exchange records,
basic automation and views for handling EDI exchanges.
""",
"version": "14.0.1.19.0",
"version": "14.0.1.20.0",
"website": "https://github.com/OCA/edi",
"development_status": "Beta",
"license": "LGPL-3",
Expand All @@ -35,6 +35,7 @@
"views/edi_backend_type_views.xml",
"views/edi_exchange_record_views.xml",
"views/edi_exchange_type_views.xml",
"views/edi_exchange_type_rule_views.xml",
"views/menuitems.xml",
"templates/exchange_chatter_msg.xml",
"templates/exchange_mixin_buttons.xml",
Expand Down
35 changes: 35 additions & 0 deletions edi_oca/migrations/14.0.1.20.0/post-migrate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Copyright 2023 Camptocamp SA (http://www.camptocamp.com)
# @author Simone Orsi <[email protected]>
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).

import logging

from odoo import SUPERUSER_ID, api, tools

_logger = logging.getLogger(__name__)


def migrate(cr, version):
if not version:
return

bkp_table = "exc_type_model_rel_bkp"
if not tools.sql.table_exists(cr, bkp_table):
return

# Use backup table (created by pre-migrate step) to create type rules
env = api.Environment(cr, SUPERUSER_ID, {})
query = """
SELECT * FROM exc_type_model_rel_bkp
"""
cr.execute(query)
res = cr.dictfetchall()
model = env["edi.exchange.type.rule"]
for item in res:
kind = "form_btn" if item.pop("form_btn", False) else "custom"
vals = dict(item, name="Default", kind=kind)
rec = model.create(vals)
rec.type_id.button_wipe_deprecated_rule_fields()

cr.execute("DROP TABLE exc_type_model_rel_bkp")
_logger.info("edi.exchange.type.rule created")
42 changes: 42 additions & 0 deletions edi_oca/migrations/14.0.1.20.0/pre-migrate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Copyright 2023 Camptocamp SA (http://www.camptocamp.com)
# @author Simone Orsi <[email protected]>
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).

import logging

from odoo import tools

_logger = logging.getLogger(__name__)


def migrate(cr, version):
if not version:
return

# Backup old style rules to be used later on post migrate
old_table = "edi_exchange_type_ir_model_rel"
if not tools.sql.table_exists(cr, old_table):
return
bkp_table = "exc_type_model_rel_bkp"
if tools.sql.table_exists(cr, bkp_table):
return

bkp_query = """
CREATE TABLE IF NOT EXISTS
exc_type_model_rel_bkp
AS
SELECT
rel.ir_model_id as model_id,
type.id as type_id,
type.enable_domain as enable_domain,
type.enable_snippet as enable_snippet,
type.model_manual_btn as form_btn
FROM
edi_exchange_type type,
edi_exchange_type_ir_model_rel rel
WHERE
rel.edi_exchange_type_id = type.id;
"""
cr.execute(bkp_query)

_logger.info("edi.exchange.type old style rules backed up")
1 change: 1 addition & 0 deletions edi_oca/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@
from . import edi_exchange_record
from . import edi_exchange_consumer_mixin
from . import edi_exchange_type
from . import edi_exchange_type_rule
from . import edi_id_mixin
35 changes: 23 additions & 12 deletions edi_oca/models/edi_exchange_consumer_mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,34 +58,45 @@ def _compute_edi_config(self):
record.edi_has_form_config = any([x.get("form") for x in config.values()])

def _edi_get_exchange_type_config(self):
exchange_types = (
self.env["edi.exchange.type"]
# TODO: move this machinery to the rule model
rules = (
self.env["edi.exchange.type.rule"]
.sudo()
.search([("model_ids.model", "=", self._name)])
.search([("model_id.model", "=", self._name)])
)
result = {}
for exchange_type in exchange_types:
for rule in rules:
exchange_type = rule.type_id
eval_ctx = dict(
self._get_eval_context(), record=self, exchange_type=exchange_type
)
domain = safe_eval.safe_eval(exchange_type.enable_domain or "[]", eval_ctx)
domain = safe_eval.safe_eval(rule.enable_domain or "[]", eval_ctx)
if not self.filtered_domain(domain):
continue
if exchange_type.enable_snippet:
if rule.enable_snippet:
safe_eval.safe_eval(
exchange_type.enable_snippet, eval_ctx, mode="exec", nocopy=True
rule.enable_snippet, eval_ctx, mode="exec", nocopy=True
)
if not eval_ctx.get("result", False):
continue

result[exchange_type.id] = self._edi_get_exchange_type_conf(exchange_type)
result[rule.id] = self._edi_get_exchange_type_rule_conf(rule)
return result

@api.model
def _edi_get_exchange_type_conf(self, exchange_type):
conf = {"form": {}}
if exchange_type.model_manual_btn:
conf.update({"form": {"btn": {"label": exchange_type.name}}})
def _edi_get_exchange_type_rule_conf(self, rule):
conf = {
"form": {},
"type": {
"id": rule.type_id.id,
"name": rule.type_id.name,
},
}
if rule.kind == "form_btn":
label = rule.form_btn_label or rule.type_id.name
conf.update(
{"form": {"btn": {"label": label, "tooltip": rule.form_btn_tooltip}}}
)
return conf

def _get_eval_context(self):
Expand Down
150 changes: 135 additions & 15 deletions edi_oca/models/edi_exchange_type.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,25 +111,33 @@ class EDIExchangeType(models.Model):
""",
)
advanced_settings = Serialized(default={}, compute="_compute_advanced_settings")
model_ids = fields.Many2many(
"ir.model",
help="""Modules to be checked for manual EDI generation""",
)
enable_domain = fields.Char(
string="Enable on domain", help="""Filter domain to be checked on Models"""
rule_ids = fields.One2many(
comodel_name="edi.exchange.type.rule",
inverse_name="type_id",
help="Rules to handle exchanges and UI automatically",
)
enable_snippet = fields.Char(
string="Enable on snippet",
help="""Snippet of code to be checked on Models,
You can use `record` and `exchange_type` here.
It will be executed if variable result has been defined as True
""",
# Deprecated fields for rules - begin
# These fields have been deprecated in
# https://github.com/OCA/edi/pull/797
# but are kept for backward compat.
# If you can stop using them now.
# Anyway, annoying warning messages will be logged.
# See inverse methods.
# NOTE: old configurations are migrated automatically on upgrade
# Yet, if you have data files they might be broken
# if we delete these fields.
model_ids = fields.Many2many(
"ir.model", inverse="_inverse_deprecated_rules_model_ids"
)
enable_domain = fields.Char(inverse="_inverse_deprecated_rules_enable_domain")
enable_snippet = fields.Char(inverse="_inverse_deprecated_rules_enable_snippet")
model_manual_btn = fields.Boolean(
string="Manual button on form",
help="Automatically display a button on related models' form."
# TODO: "Button settings can be configured via advanced settings."
inverse="_inverse_deprecated_rules_model_manual_btn"
)
deprecated_rule_fields_still_used = fields.Boolean(
compute="_compute_deprecated_rule_fields_still_used"
)
# Deprecated fields for rules - end
quick_exec = fields.Boolean(
string="Quick execution",
help="When active, records of this type will be processed immediately "
Expand Down Expand Up @@ -240,3 +248,115 @@ def is_partner_enabled(self, partner):
if exc_type.partner_ids:
return partner.id in exc_type.partner_ids.ids
return True

# API to support deprecated model rules fields - begin
def _inverse_deprecated_rules_warning(self):
_fields = ", ".join(
["model_ids", "enable_domain", "enable_snippet", "model_manual_btn"]
)
_logger.warning(
"The fields %s are deprecated, "
"please stop using them in favor of edi.exchange.type.rule",
_fields,
)

def _inverse_deprecated_rules_model_ids(self):
if self.env.context.get("deprecated_rule_fields_bypass_inverse"):
return
self._inverse_deprecated_rules_warning()
for rec in self:
for model in rec.model_ids:
rule = rec._get_rule_by_model(model)
if not rule:
_logger.warning(
"New rule for %s created from deprecated `model_ids`",
model.model,
)
rec.rule_ids += rec._inverse_deprecated_rules_create(model)
rules_to_delete = rec.rule_ids.browse()
for rule in rec.rule_ids:
if rule.model_id not in rec.model_ids:
_logger.warning(
"Rule for %s deleted from deprecated `model_ids`",
rule.model_id.model,
)
rules_to_delete |= rule
rules_to_delete.unlink()

def _inverse_deprecated_rules_enable_domain(self):
if self.env.context.get("deprecated_rule_fields_bypass_inverse"):
return
self._inverse_deprecated_rules_warning()
for rec in self:
for model in rec.model_ids:
rule = rec._get_rule_by_model(model)
if rule:
_logger.warning(
"Rule for %s domain updated from deprecated `enable_domain`",
model.model,
)
rule.enable_domain = rec.enable_domain

def _inverse_deprecated_rules_enable_snippet(self):
if self.env.context.get("deprecated_rule_fields_bypass_inverse"):
return
self._inverse_deprecated_rules_warning()
for rec in self:
for model in rec.model_ids:
rule = rec._get_rule_by_model(model)
if rule:
_logger.warning(
"Rule for %s snippet updated from deprecated `enable_snippet`",
model.model,
)
rule.enable_snippet = rec.enable_snippet

def _inverse_deprecated_rules_model_manual_btn(self):
if self.env.context.get("deprecated_rule_fields_bypass_inverse"):
return
self._inverse_deprecated_rules_warning()
for rec in self:
for model in rec.model_ids:
rule = rec._get_rule_by_model(model)
if rule:
_logger.warning(
"Rule for %s btn updated from deprecated `model_manual_btn`",
model.model,
)
rule.kind = "form_btn" if self.model_manual_btn else "custom"

def _get_rule_by_model(self, model):
return self.rule_ids.filtered(lambda x: x.model_id == model)

def _inverse_deprecated_rules_create(self, model):
kind = "form_btn" if self.model_manual_btn else "custom"
vals = {
"type_id": self.id,
"model_id": model.id,
"kind": kind,
"name": "Default",
"enable_snippet": self.enable_snippet,
"enable_domain": self.enable_domain,
}
return self.rule_ids.create(vals)

@api.depends("model_ids", "enable_domain", "enable_snippet", "model_manual_btn")
def _compute_deprecated_rule_fields_still_used(self):
for rec in self:
rec.deprecated_rule_fields_still_used = (
rec._deprecated_rule_fields_still_used()
)

def _deprecated_rule_fields_still_used(self):
for fname in ("model_ids", "enable_snippet", "enable_domain"):
if self[fname]:
return True

def button_wipe_deprecated_rule_fields(self):
_fields = ["model_ids", "enable_domain", "enable_snippet", "model_manual_btn"]
deprecated_vals = {}.fromkeys(_fields, None)
self.with_context(deprecated_rule_fields_bypass_inverse=True).write(
deprecated_vals
)

# API to support deprecated model rules fields - end
62 changes: 62 additions & 0 deletions edi_oca/models/edi_exchange_type_rule.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Copyright 2023 Camptocamp SA
# @author Simone Orsi <[email protected]>
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).

from odoo import fields, models

KIND_HELP = """
* Form button: show a button on the related model form
when conditions from domain and snippet are satisfied
* Custom: let devs handle a custom behavior with specific developments
"""


class EDIExchangeTypeRule(models.Model):
"""
Define rules for exchange types.
"""

_name = "edi.exchange.type.rule"
_description = "EDI Exchange type rule"

active = fields.Boolean(default=True)
name = fields.Char(required=True)
type_id = fields.Many2one(
comodel_name="edi.exchange.type",
required=True,
ondelete="cascade",
)
model_id = fields.Many2one(
comodel_name="ir.model",
help="Apply to this model",
ondelete="cascade",
)
model = fields.Char(related="model_id.model") # Tech field
enable_domain = fields.Char(
string="Enable on domain", help="Filter domain to be checked on Models"
)
enable_snippet = fields.Char(
string="Enable on snippet",
help="""Snippet of code to be checked on Models,
You can use `record` and `exchange_type` here.
It will be executed if variable result has been defined as True
""",
)
kind = fields.Selection(
selection=[
("form_btn", "Form button"),
("custom", "Custom"),
],
required=True,
default="form_btn",
help=KIND_HELP,
)
form_btn_label = fields.Char(
string="Form button label", translate=True, help="Type name used by default"
)
form_btn_tooltip = fields.Text(
string="Form button tooltip",
translate=True,
help="Help message visible as tooltip on button h-over",
)
Loading

0 comments on commit e8aefdf

Please sign in to comment.