From f56882723d656977776f2394d646542ec059ce4f Mon Sep 17 00:00:00 2001
From: Juan Jose Scarafia
Date: Thu, 10 Aug 2023 23:42:04 -0300
Subject: [PATCH] [ADD] base_dynamic_message_type
---
base_dynamic_message/README.rst | 52 +++++++++
base_dynamic_message/__init__.py | 1 +
base_dynamic_message/__manifest__.py | 44 ++++++++
base_dynamic_message/models/__init__.py | 2 +
.../models/ir_model_dynamic_message.py | 106 ++++++++++++++++++
.../models/ir_model_dynamic_message_line.py | 40 +++++++
.../security/ir.model.access.csv | 5 +
base_dynamic_message/security/res_groups.xml | 13 +++
.../views/ir_model_dynamic_message_views.xml | 89 +++++++++++++++
9 files changed, 352 insertions(+)
create mode 100644 base_dynamic_message/README.rst
create mode 100644 base_dynamic_message/__init__.py
create mode 100644 base_dynamic_message/__manifest__.py
create mode 100644 base_dynamic_message/models/__init__.py
create mode 100644 base_dynamic_message/models/ir_model_dynamic_message.py
create mode 100644 base_dynamic_message/models/ir_model_dynamic_message_line.py
create mode 100644 base_dynamic_message/security/ir.model.access.csv
create mode 100644 base_dynamic_message/security/res_groups.xml
create mode 100644 base_dynamic_message/views/ir_model_dynamic_message_views.xml
diff --git a/base_dynamic_message/README.rst b/base_dynamic_message/README.rst
new file mode 100644
index 00000000..b7379da9
--- /dev/null
+++ b/base_dynamic_message/README.rst
@@ -0,0 +1,52 @@
+.. |company| replace:: ADHOC SA
+
+.. |company_logo| image:: https://raw.githubusercontent.com/ingadhoc/maintainer-tools/master/resources/adhoc-logo.png
+ :alt: ADHOC SA
+ :target: https://www.adhoc.com.ar
+
+.. |icon| image:: https://raw.githubusercontent.com/ingadhoc/maintainer-tools/master/resources/adhoc-icon.png
+
+.. image:: https://img.shields.io/badge/license-AGPL--3-blue.png
+ :target: https://www.gnu.org/licenses/agpl
+ :alt: License: AGPL-3
+
+==============================
+Dynamic Messages on form views
+==============================
+
+Este modulo sirve para generar mensajes dinamicos en vistas form de cualquier modelo de Odoo.
+Algunos ejemplos:
+
+#. Dar un determinado warning cuando un partner es de un determinado país
+#. Advertir cuando un ticket es de un cliente de una determinada categoría
+
+Roadmap:
+#. Mejorar readme
+
+Bug Tracker
+===========
+
+Bugs are tracked on `GitHub 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.
+
+Credits
+=======
+
+Images
+------
+
+* |company| |icon|
+
+Contributors
+------------
+
+Maintainer
+----------
+
+|company_logo|
+
+This module is maintained by the |company|.
+
+To contribute to this module, please visit https://www.adhoc.com.ar.
diff --git a/base_dynamic_message/__init__.py b/base_dynamic_message/__init__.py
new file mode 100644
index 00000000..0650744f
--- /dev/null
+++ b/base_dynamic_message/__init__.py
@@ -0,0 +1 @@
+from . import models
diff --git a/base_dynamic_message/__manifest__.py b/base_dynamic_message/__manifest__.py
new file mode 100644
index 00000000..0ce8a5e3
--- /dev/null
+++ b/base_dynamic_message/__manifest__.py
@@ -0,0 +1,44 @@
+##############################################################################
+#
+# Copyright (C) 2019 ADHOC SA (http://www.adhoc.com.ar)
+# All Rights Reserved.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+#
+##############################################################################
+{
+ 'name': 'Dynamic Message',
+ 'version': '16.0.1.1.0',
+ 'category': 'Base',
+ 'sequence': 14,
+ 'summary': '',
+ 'author': 'ADHOC SA',
+ 'website': 'www.adhoc.com.ar',
+ 'license': 'AGPL-3',
+ 'images': [
+ ],
+ 'depends': [
+ 'base',
+ ],
+ 'data': [
+ 'security/res_groups.xml',
+ 'security/ir.model.access.csv',
+ 'views/ir_model_dynamic_message_views.xml',
+ ],
+ 'demo': [
+ ],
+ 'installable': True,
+ 'auto_install': False,
+ 'application': False,
+}
diff --git a/base_dynamic_message/models/__init__.py b/base_dynamic_message/models/__init__.py
new file mode 100644
index 00000000..f39c10aa
--- /dev/null
+++ b/base_dynamic_message/models/__init__.py
@@ -0,0 +1,2 @@
+from . import ir_model_dynamic_message
+from . import ir_model_dynamic_message_line
diff --git a/base_dynamic_message/models/ir_model_dynamic_message.py b/base_dynamic_message/models/ir_model_dynamic_message.py
new file mode 100644
index 00000000..9f868892
--- /dev/null
+++ b/base_dynamic_message/models/ir_model_dynamic_message.py
@@ -0,0 +1,106 @@
+from odoo import models, fields, api
+import ast
+import textwrap
+
+
+class IrModelDynamicMessage(models.Model):
+ _name = 'ir.model.dynamic_message'
+ _description = 'ir.model.dynamic_message'
+
+ name = fields.Char(required=True)
+ description = fields.Text()
+ model_id = fields.Many2one('ir.model', required=True, ondelete='cascade')
+ model_name = fields.Char(related='model_id.model', string='Model Name')
+ line_ids = fields.One2many('ir.model.dynamic_message.line', 'dynamic_message_id', copy=True)
+ code = fields.Text(compute='_compute_code')
+ depends = fields.Char(compute='_compute_depends', store=True, readonly=False, required=True)
+ field_id = fields.Many2one('ir.model.fields', readonly=True, copy=False)
+ view_to_inherit_id = fields.Many2one(
+ 'ir.ui.view', compute='_compute_view_to_inherit', readonly=False, store=True, required=True)
+ view_id = fields.Many2one('ir.ui.view', readonly=True, copy=False)
+ alert_type = fields.Selection([('info', 'info'), ('warning', 'warning'), ('danger', 'danger')], required=True, default='info')
+
+ @api.depends('model_id')
+ def _compute_view_to_inherit(self):
+ for rec in self:
+ rec.view_to_inherit_id = rec.env['ir.ui.view'].search(
+ [('type', '=', 'form'), ('model', '=', rec.model_id.model), ('mode', '=', 'primary')], limit=1)
+
+ @api.depends('line_ids.code')
+ def _compute_code(self):
+ for rec in self:
+ sub_string = ''.join(rec.line_ids.filtered('code').mapped('code'))
+ sub_string = textwrap.indent(sub_string, prefix=' ')
+ field_name = 'x_dynamic_message_%i' % rec._origin.id or 0
+ rec.code = """
+for rec in self:
+ messages = []
+%s
+ if not messages:
+ rec['%s'] = False
+ else:
+ rec['%s'] = "" %% "".join(["%%s" %% message for message in messages])
+""" % (sub_string, field_name, field_name)
+
+ @api.depends('line_ids.domain')
+ def _compute_depends(self):
+ for rec in self:
+ dep_fields = []
+ for line in rec.line_ids.filtered('domain'):
+ domain = ast.literal_eval(line.domain)
+ for element in domain:
+ if type(element) is not tuple:
+ continue
+ # TODO deberiamos chequear que el campo sea sercheable (en smart search teniamos algo de esto)
+ if '.' in element[0]:
+ dep_fields.append(element[0].split('.')[0])
+ elif element[0] != 'id':
+ dep_fields.append(element[0])
+ rec.depends = ','.join(list(set(dep_fields))) if dep_fields else False
+
+ def confirm(self):
+ for rec in self:
+ field_name = 'x_dynamic_message_%i' % rec.id
+ field_vals = {
+ 'name': field_name,
+ 'field_description': 'Dynamic Message',
+ 'state': 'manual',
+ 'store': False,
+ 'ttype': 'html',
+ 'model_id': rec.model_id.id,
+ 'compute': rec.code,
+ 'depends': rec.depends,
+ }
+ if rec.field_id:
+ rec.field_id.sudo().write(field_vals)
+ else:
+ rec.field_id = rec.field_id.sudo().create(field_vals)
+
+ view_vals = {
+ 'name': 'Dynamic Message for %s' % rec.model_id.name,
+ 'inherit_id': rec.view_to_inherit_id.id,
+ 'model': rec.model_id.model,
+ 'priority': 999,
+ 'arch_db': """
+
+
+
+
+
+""" % (rec.alert_type, field_name, field_name),
+ }
+ if rec.view_id:
+ rec.view_id.sudo().write(view_vals)
+ else:
+ rec.view_id = rec.view_id.sudo().create(view_vals)
+
+ @api.ondelete(at_uninstall=True)
+ def _delete_field_and_view(self):
+ self.mapped('field_id').sudo().unlink()
+ self.mapped('view_id').sudo().unlink()
+
+ _sql_constraints = [
+ (
+ 'unique_level_per_model', 'UNIQUE(model_id, alert_type, view_to_inherit_id)',
+ 'Debe haber un unico registro por modelo, vista heredada y tipo de alerta')
+ ]
diff --git a/base_dynamic_message/models/ir_model_dynamic_message_line.py b/base_dynamic_message/models/ir_model_dynamic_message_line.py
new file mode 100644
index 00000000..3b7fb837
--- /dev/null
+++ b/base_dynamic_message/models/ir_model_dynamic_message_line.py
@@ -0,0 +1,40 @@
+from odoo import models, fields, api
+import re
+
+
+class IrModelDynamicMessageLine(models.Model):
+ _name = 'ir.model.dynamic_message.line'
+ _description = 'ir.model.dynamic_message.line'
+
+ dynamic_message_id = fields.Many2one('ir.model.dynamic_message', required=True, ondelete='cascade')
+ description = fields.Text()
+ domain = fields.Char()
+ message = fields.Html(required=True,)
+ model_name = fields.Char(related='dynamic_message_id.model_id.model')
+ code = fields.Text(compute='_compute_code', store=True, readonly=False)
+
+ @api.depends('message', 'domain')
+ def _compute_code(self):
+ for rec in self:
+ if rec.domain:
+ # si el doinio tiene expresiones "EXP:" recorremos las tuplas y lo dejamos sin string y limpiamos el "EXP:""
+ domain = rec.domain
+ if "EXP:" in rec.domain:
+ # construido con chatgpt
+ # Original string
+ # Regular expression pattern to extract the expression parts
+ pattern = r'"EXP: (.+?)"'
+
+ matches = re.finditer(pattern, domain)
+
+ for match in matches:
+ expression = match.group(1)
+ domain = domain.replace(f'"EXP: {expression}"', expression)
+
+ rec.code = """
+if rec in rec.filtered_domain(%s):
+ messages.append('%s')
+""" % (domain, str(rec.message).replace('', '').replace('
', ''))
+ else:
+ rec.code = False
+ # code = self.filtered_domain(self.env['account.payment.method']._get_payment_method_domain(payment_method_code))
diff --git a/base_dynamic_message/security/ir.model.access.csv b/base_dynamic_message/security/ir.model.access.csv
new file mode 100644
index 00000000..56246d89
--- /dev/null
+++ b/base_dynamic_message/security/ir.model.access.csv
@@ -0,0 +1,5 @@
+id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
+access_ir_model_dynamic_message,access_ir_model_dynamic_message,model_ir_model_dynamic_message,base.group_user,1,0,0,0
+access_ir_model_dynamic_message_line,access_ir_model_dynamic_message_line,model_ir_model_dynamic_message_line,base.group_user,1,0,0,0
+access_ir_model_dynamic_message_admin,access_ir_model_dynamic_message,model_ir_model_dynamic_message,group_dynamic_messages_manager,1,1,1,1
+access_ir_model_dynamic_message_line_admin,access_ir_model_dynamic_message_line,model_ir_model_dynamic_message_line,group_dynamic_messages_manager,1,1,1,1
diff --git a/base_dynamic_message/security/res_groups.xml b/base_dynamic_message/security/res_groups.xml
new file mode 100644
index 00000000..735a3e23
--- /dev/null
+++ b/base_dynamic_message/security/res_groups.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+ Manage Dynamic Messages
+
+
+
+
diff --git a/base_dynamic_message/views/ir_model_dynamic_message_views.xml b/base_dynamic_message/views/ir_model_dynamic_message_views.xml
new file mode 100644
index 00000000..c251d598
--- /dev/null
+++ b/base_dynamic_message/views/ir_model_dynamic_message_views.xml
@@ -0,0 +1,89 @@
+
+
+ ir.model.dynamic_message.tree
+ ir.model.dynamic_message
+
+
+
+
+
+
+
+
+
+
+ ir.model.dynamic_message.form
+ ir.model.dynamic_message
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Dynamic Messages
+ ir.model.dynamic_message
+ current
+ tree,form
+
+
+
+