Skip to content

Commit

Permalink
[ADD] base_dynamic_message_type
Browse files Browse the repository at this point in the history
  • Loading branch information
jjscarafia committed Aug 19, 2023
1 parent ee3fc45 commit f568827
Show file tree
Hide file tree
Showing 9 changed files with 352 additions and 0 deletions.
52 changes: 52 additions & 0 deletions 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
<https://github.com/ingadhoc/miscellaneous/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.
1 change: 1 addition & 0 deletions base_dynamic_message/__init__.py
@@ -0,0 +1 @@
from . import models
44 changes: 44 additions & 0 deletions 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 <http://www.gnu.org/licenses/>.
#
##############################################################################
{
'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,
}
2 changes: 2 additions & 0 deletions base_dynamic_message/models/__init__.py
@@ -0,0 +1,2 @@
from . import ir_model_dynamic_message
from . import ir_model_dynamic_message_line
106 changes: 106 additions & 0 deletions 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'] = "<ul>%%s</ul>" %% "".join(["<li>%%s</li>" %% 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': """
<sheet position="before">
<div class="alert alert-%s mb-0" role="alert" attrs="{'invisible': [('%s', '=', False)]}">
<field name="%s" nolabel="1" readonly="1"/>
</div>
</sheet>
""" % (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')
]
40 changes: 40 additions & 0 deletions 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('<p>', '').replace('</p>', ''))
else:
rec.code = False
# code = self.filtered_domain(self.env['account.payment.method']._get_payment_method_domain(payment_method_code))
5 changes: 5 additions & 0 deletions 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
13 changes: 13 additions & 0 deletions base_dynamic_message/security/res_groups.xml
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- <record model="ir.module.category" id="dynamic_messages_manager">
<field name="name">Manage Dynamic Messages</field>
</record> -->

<record id="group_dynamic_messages_manager" model="res.groups">
<field name="name">Manage Dynamic Messages</field>
<!-- <field name="name">Portal Holiday</field>
<field name="category_id" ref="category_portal_holiday"/> -->
<field name="users" eval="[(4, ref('base.user_root')), (4, ref('base.user_admin'))]"/>
</record>
</odoo>
89 changes: 89 additions & 0 deletions base_dynamic_message/views/ir_model_dynamic_message_views.xml
@@ -0,0 +1,89 @@
<odoo>
<record id="view_ir_model_dynamic_message_tree" model="ir.ui.view">
<field name="name">ir.model.dynamic_message.tree</field>
<field name="model">ir.model.dynamic_message</field>
<field name="arch" type="xml">
<tree>
<field name="name"/>
<field name="description"/>
<field name="model_id"/>
</tree>
</field>
</record>

<record id="view_ir_model_dynamic_message_form" model="ir.ui.view">
<field name="name">ir.model.dynamic_message.form</field>
<field name="model">ir.model.dynamic_message</field>
<field name="arch" type="xml">
<form>
<header>
<button string="Create / Update" type="object" name="confirm" class="oe_highlight"/>
</header>
<sheet>
<field name="id" invisible="1"/>
<field name="model_name" invisible="1"/>
<group>
<group>
<field name="name"/>
<field name="description"/>
<field name="model_id" force_save="True" options="{'no_create': True, 'no_open': True}" attrs="{'readonly': ['|', ('line_ids', '!=', []), ('id', '!=', False)]}"/>
</group>
<group>
<field name="alert_type"/>
<field name="view_to_inherit_id" domain="[('arch_db', 'like', '&lt;sheet&gt;'), ('type', '=', 'form'), ('model', '=', model_name), ('mode', '=', 'primary')]"/>
<field name="view_id" groups="base.group_no_one" readonly="True"/>
<field name="field_id" groups="base.group_no_one" readonly="True"/>
<field name="depends"/>
</group>
</group>
<notebook>
<page string="Lines">
<field name="line_ids">
<tree>
<field name="description" optional="hide"/>
<field name="domain" optional="show"/>
<field name="message" optional="show"/>
<field name="code" optional="show"/>
<field name="model_name" invisible="True"/>
</tree>
<form>
<p>En las expresiones, del lado derecho, se puede usar el string "EXP:[expresion]" para poner codigo dinamico. A la hora de pasar el dominio al calculo solo se
dejará la expresion. Es importante que "EXP" se ponga con comillas dobles y no comillas simples tal como se muestra en los ejemplos. Las variables disponibles son:
<ul>
<li>rec: registro que se está evaluando</li>
<li>las mismas variables disponibles en un campo calculado, por ej "datetime.datetime.now()" y "dateutil.relativedelta.relativedelta":</li>
</ul>
Por ej:
<ul>
<li>[("create_date", "=", "EXP: datetime.datetime.now() - dateutil.relativedelta.relativedelta(days=-10)")]</li>
<li>[("partner_id", "in", ["EXP: rec.child_ids.ids"])]</li>
</ul>
</p>
<group>
<field name="description"/>
<field name="domain" widget="domain" options="{'model': 'model_name', 'in_dialog': True}" />
<field name="message"/>
<field name="code"/>
<field name="model_name" invisible="True"/>
</group>
</form>
</field>
</page>
<page string="Code">
<field name="code"/>
</page>
</notebook>
</sheet>
</form>
</field>
</record>

<record id="action_ir_model_dynamic_message" model="ir.actions.act_window">
<field name="name">Dynamic Messages</field>
<field name="res_model">ir.model.dynamic_message</field>
<field name="target">current</field>
<field name='view_mode'>tree,form</field>
</record>

<menuitem id="menu_ir_model_dynamic_message" groups="group_dynamic_messages_manager" sequence="100" action="action_ir_model_dynamic_message" web_icon="fa fa-info,#f1c40f,#34495e"/>
</odoo>

0 comments on commit f568827

Please sign in to comment.