[ADD]: Add/Remove dynamic digest field, group and misc changes
This commit is contained in:
parent
44e47f76a1
commit
84ed2b2571
@ -19,9 +19,11 @@ Send KPI Digests periodically
|
||||
'data/ir_cron_data.xml',
|
||||
'data/res_config_settings_data.xml',
|
||||
'views/digest_views.xml',
|
||||
'wizard/digest_custom_fields_view.xml',
|
||||
'wizard/digest_custom_remove_view.xml',
|
||||
'views/digest_views_inherit.xml',
|
||||
'views/digest_templates.xml',
|
||||
'views/res_config_settings_views.xml',
|
||||
'wizard/digest_custom_fields_view.xml',
|
||||
],
|
||||
'installable': True,
|
||||
}
|
||||
|
@ -62,43 +62,6 @@
|
||||
<group name="kpi_sales"/>
|
||||
</group>
|
||||
</page>
|
||||
<page name="how_to" string="How to customize your digest?" groups="base.group_no_one">
|
||||
<div>
|
||||
<button type="action" name="%(digest.digest_custom_fields_action)d" string="Customized Digest"/>
|
||||
</div>
|
||||
<div class="alert alert-info" role="alert">
|
||||
In order to build your customized digest, follow these steps:
|
||||
<ol>
|
||||
<li>
|
||||
You may want to add new computed fields:
|
||||
<ul>
|
||||
<li>
|
||||
you must create 2 fields on the
|
||||
<code>digest</code>
|
||||
object:
|
||||
</li>
|
||||
<li>
|
||||
first create a boolean field called
|
||||
<code>x_kpi_field_name</code>
|
||||
and display it in the KPI's tab;
|
||||
</li>
|
||||
<li>
|
||||
then create a computed field called
|
||||
<code>x_kpi_field_name_value</code>
|
||||
that will compute your customized KPI.
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Select your KPIs in the KPI's tab.</li>
|
||||
<li>
|
||||
Create or edit the mail template: you may get computed KPI's value using these fields:
|
||||
<code>
|
||||
<field name="available_fields" class="oe_inline" />
|
||||
</code>
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
</page>
|
||||
</notebook>
|
||||
</sheet>
|
||||
</form>
|
||||
|
50
addons/digest/views/digest_views_inherit.xml
Normal file
50
addons/digest/views/digest_views_inherit.xml
Normal file
@ -0,0 +1,50 @@
|
||||
<?xml version="1.0"?>
|
||||
<flectra>
|
||||
<record id="digest_digest_view_form_inherit" model="ir.ui.view">
|
||||
<field name="name">digest.digest.view.form.inherit</field>
|
||||
<field name="model">digest.digest</field>
|
||||
<field name="inherit_id" ref="digest.digest_digest_view_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//page[@name='kpis']" position="after">
|
||||
<page name="how_to" string="How to customize your digest?" groups="base.group_no_one">
|
||||
<div>
|
||||
<button type="action" class="oe_highlight" name="%(digest.digest_custom_fields_action)d" string="Add Customized Digest"/>
|
||||
<button type="action" name="%(digest.digest_custom_remove_action)d" string="Remove Customized Digest"/>
|
||||
</div>
|
||||
<div class="alert alert-info" role="alert">
|
||||
In order to build your customized digest, follow these steps:
|
||||
<ol>
|
||||
<li>
|
||||
You may want to add new computed fields:
|
||||
<ul>
|
||||
<li>
|
||||
you must create 2 fields on the
|
||||
<code>digest</code>
|
||||
object:
|
||||
</li>
|
||||
<li>
|
||||
first create a boolean field called
|
||||
<code>x_kpi_field_name</code>
|
||||
and display it in the KPI's tab;
|
||||
</li>
|
||||
<li>
|
||||
then create a computed field called
|
||||
<code>x_kpi_field_name_value</code>
|
||||
that will compute your customized KPI.
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Select your KPIs in the KPI's tab.</li>
|
||||
<li>
|
||||
Create or edit the mail template: you may get computed KPI's value using these fields:
|
||||
<code>
|
||||
<field name="available_fields" class="oe_inline" />
|
||||
</code>
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
</page>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
</flectra>
|
@ -1,4 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Flectra. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import digest_custom_fields
|
||||
from . import digest_custom_fields
|
||||
from . import digest_custom_remove
|
@ -5,6 +5,7 @@ from flectra import api, fields, models, _
|
||||
from flectra.exceptions import ValidationError, UserError
|
||||
from lxml import etree
|
||||
from flectra.tools.safe_eval import test_python_expr
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
|
||||
class DigestCustomFields(models.TransientModel):
|
||||
@ -19,29 +20,37 @@ class DigestCustomFields(models.TransientModel):
|
||||
# - time, datetime, dateutil, timezone: useful Python libraries
|
||||
# - log: log(message, level='info'): logging function to record debug information in ir.logging table
|
||||
# - Warning: Warning Exception to use with raise
|
||||
# To return an action, assign: action = {...}\n\n\n\n"""
|
||||
# To return an action, assign: action = {...}
|
||||
for rec in self:
|
||||
rec[''] = self.env[''].search([])\n\n\n\n"""
|
||||
|
||||
|
||||
# field = fields.Many2one('ir.model.fields', domain="[('model_id', '=', 'digest.digest'), ('name', 'ilike', 'x_kpi'), ('depends', '=', False)]")
|
||||
# compute_field = fields.Many2one('ir.model.fields', domain="[('model_id', '=', 'digest.digest'), ('name', 'ilike', 'x_kpi'), ('depends', '!=', False)]")
|
||||
|
||||
field_name = fields.Char('Field Name', default='x_kpi_', required=True)
|
||||
label_name = fields.Char('Label Name', required=True)
|
||||
group_name = fields.Char('Group Name', required=True)
|
||||
ttype = fields.Selection([('integer', 'Integer'), ('monetary', 'Monetary')], string='Field Type', required=True)
|
||||
# compute = fields.Text(help="Code to compute the value of the field.\n"
|
||||
# "Iterate on the recordset 'self' and assign the field's value:\n\n"
|
||||
# " for record in self:\n"
|
||||
# " record['size'] = len(record.name)\n\n"
|
||||
# "Modules time, datetime, dateutil are available.")
|
||||
|
||||
|
||||
# group_type = fields.Selection([('new', 'New Group'), ('existing', 'Existing Group')], string='Group Type', required=True)
|
||||
new_group_name = fields.Char('Group Name')
|
||||
ttype = fields.Selection([('integer', 'Integer'), ('monetary', 'Monetary')], string='Field Type', required=True, default='integer')
|
||||
compute = fields.Text(string='Python Code', groups='base.group_system',
|
||||
default=DEFAULT_PYTHON_CODE,
|
||||
help="Write Python code that the action will execute. Some variables are "
|
||||
"available for use; help about pyhon expression is given in the help tab.")
|
||||
|
||||
compute_field_name = fields.Char(compute='_compute_get_field_name', string='Compute Field Name')
|
||||
available_group_name = fields.Selection('_get_group_name', string='Available Group')
|
||||
position = fields.Selection([('before', 'Before'), ('after', 'After'), ('inside', 'Inside')], string='Position')
|
||||
|
||||
def _get_group_name(self):
|
||||
print("=====self=========", self.env.context)
|
||||
digest_view_id = self.env.ref('digest.digest_digest_view_form').id
|
||||
view_ids = self.env['ir.ui.view'].search([('inherit_id', 'child_of', digest_view_id)])
|
||||
group_value = {}
|
||||
for view_id in view_ids:
|
||||
root = ET.fromstring(view_id.arch_base)
|
||||
for group_name in root.iter('group'):
|
||||
if group_name.attrib.get('name', False) and group_name.attrib.get('string', False):
|
||||
group_key = str(view_id.id) + '_' + str(group_name.attrib['name'])
|
||||
group_value.update({group_key : group_name.attrib['string']})
|
||||
return [(x) for x in group_value.items()]
|
||||
|
||||
@api.constrains('compute')
|
||||
def _check_python_code(self):
|
||||
@ -60,7 +69,9 @@ class DigestCustomFields(models.TransientModel):
|
||||
def _check_name(self):
|
||||
for field in self:
|
||||
if not field.field_name.startswith('x_kpi_'):
|
||||
raise ValidationError(_("Custom fields must have a name that starts with 'x_kpi_' !"))
|
||||
raise ValidationError(_("Custom fields must have a name that starts with 'x_kpi_'!"))
|
||||
# if self.position != 'inside' and not field.new_group_name.startswith('x_kpi_'):
|
||||
# raise ValidationError(_("Group Name must have a name that starts with 'x_kpi_'!"))
|
||||
try:
|
||||
models.check_pg_name(field.field_name)
|
||||
except ValidationError:
|
||||
@ -90,41 +101,44 @@ class DigestCustomFields(models.TransientModel):
|
||||
'depends': first_field_name,
|
||||
'compute': self.compute
|
||||
}
|
||||
print("====values=======", values)
|
||||
ir_model_fields_obj.create(values)
|
||||
|
||||
def field_arch(self):
|
||||
xpath = etree.Element('xpath')
|
||||
xpath_type = "group"
|
||||
name = "kpi_general"
|
||||
position = "after"
|
||||
xpath_field = self.field_name
|
||||
expr = '//' + xpath_type + '[@name="' + name + '"][not(ancestor::field)]'
|
||||
name = self.available_group_name and self.available_group_name.split('_', 1)[1] or "kpis"
|
||||
expr = '//' + 'group' + '[@name="' + name + '"]'
|
||||
xpath.set('expr', expr)
|
||||
xpath.set('position', position)
|
||||
if position == 'after' or position == 'before' or position == 'inside':
|
||||
expr = '//' + xpath_type + '[@name="' + name + '"][not(ancestor::field)]'
|
||||
group = etree.Element('group')
|
||||
group.set('string', self.group_name)
|
||||
xpath.set('position', self.position)
|
||||
|
||||
if self.position == 'inside':
|
||||
field = etree.Element('field')
|
||||
field.set('name', xpath_field)
|
||||
field.set('name', self.field_name)
|
||||
xpath.set('expr', expr)
|
||||
xpath.append(field)
|
||||
else:
|
||||
group = etree.Element('group')
|
||||
group.set('name', 'x_kpi_' + self.new_group_name.replace(" ", "_"))
|
||||
group.set('string', self.new_group_name)
|
||||
field = etree.Element('field')
|
||||
field.set('name', self.field_name)
|
||||
xpath.set('expr', expr)
|
||||
group.append(field)
|
||||
xpath.append(group)
|
||||
|
||||
return etree.tostring(xpath).decode("utf-8")
|
||||
|
||||
@api.multi
|
||||
def action_customize_digest(self):
|
||||
def action_add_customize_digest(self):
|
||||
self.add_new_fields()
|
||||
arch = '<data>' + str(self.field_arch()) + '</data>'
|
||||
print("====arch=======", arch)
|
||||
arch = '<?xml version="1.0"?>' + str(self.field_arch())
|
||||
view_id = self.available_group_name and self.available_group_name.split('_', 1)[0] or False
|
||||
vals = {
|
||||
'type': 'form',
|
||||
'model': 'digest.digest',
|
||||
'inherit_id': self.env.ref('digest.digest_digest_view_form').id,
|
||||
'inherit_id': view_id or self.env.ref('digest.digest_digest_view_form').id,
|
||||
'mode': 'extension',
|
||||
'arch_base': arch,
|
||||
'name': 'x_kpi_' + self.field_name + "_Customization",
|
||||
'name': 'x_kpi_' + self.field_name + "_customization",
|
||||
}
|
||||
ir_model = self.env['ir.model'].search([('model', '=', 'digest.digest')])
|
||||
if hasattr(ir_model, 'module_id'):
|
||||
|
@ -5,10 +5,12 @@
|
||||
<field name="model">digest.custom.fields</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Customized Digest">
|
||||
<group class="oe_title" col="4">
|
||||
<group col="4">
|
||||
<field name="field_name"/>
|
||||
<field name="label_name"/>
|
||||
<field name="group_name"/>
|
||||
<field name="available_group_name" required="1"/>
|
||||
<field name="position" required="1"/>
|
||||
<field name="new_group_name" attrs="{'required': [('position', '!=', 'inside')], 'invisible': [('position', 'not in', ['before', 'after'])]}"/>
|
||||
</group>
|
||||
<group string="Compute Details">
|
||||
<field name="compute_field_name"/>
|
||||
@ -16,7 +18,7 @@
|
||||
<field name="compute" widget="ace" options="{'mode': 'python'}"/>
|
||||
</group>
|
||||
<footer>
|
||||
<button name="action_customize_digest" string="Save" type="object" class="btn btn-sm btn-primary"/>
|
||||
<button name="action_add_customize_digest" string="Save" type="object" class="btn btn-sm btn-primary"/>
|
||||
<button string="Cancel" class="btn btn-sm btn-default" special="cancel"/>
|
||||
</footer>
|
||||
</form>
|
||||
|
76
addons/digest/wizard/digest_custom_remove.py
Normal file
76
addons/digest/wizard/digest_custom_remove.py
Normal file
@ -0,0 +1,76 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Flectra. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from flectra import api, fields, models, _
|
||||
from flectra.exceptions import ValidationError, UserError
|
||||
from lxml import etree
|
||||
from flectra.tools.safe_eval import test_python_expr
|
||||
import xml.etree.ElementTree as ET
|
||||
from flectra.osv import expression
|
||||
|
||||
|
||||
class DigestCustomRemove(models.TransientModel):
|
||||
_name = 'digest.custom.remove'
|
||||
|
||||
remove_type = fields.Selection([('group', 'Group'), ('field', 'Field')], string='Remove Type')
|
||||
field_id = fields.Many2one('ir.model.fields', 'Field', domain=[('model', '=', 'digest.digest'), ('required', '=', False), ('ttype', '=', 'boolean'), ('name', 'ilike', 'x_kpi_')])
|
||||
available_group_name = fields.Selection('_get_group_name', string='Available Group')
|
||||
|
||||
def _get_group_name(self):
|
||||
digest_view_id = self.env.ref('digest.digest_digest_view_form').id
|
||||
view_ids = self.env['ir.ui.view'].search([('inherit_id', 'child_of', digest_view_id)])
|
||||
group_value = {}
|
||||
for view_id in view_ids:
|
||||
root = ET.fromstring(view_id.arch_base)
|
||||
for group_name in root.iter('group'):
|
||||
if group_name.attrib.get('name', False) and group_name.attrib.get('string', False) and group_name.attrib['name'].startswith('x_kpi_'):
|
||||
group_key = str(view_id.id) + '_' + str(group_name.attrib['name'])
|
||||
group_value.update({group_key : group_name.attrib['string']})
|
||||
return [(x) for x in group_value.items()]
|
||||
|
||||
@api.multi
|
||||
def action_customize_digest_remove(self):
|
||||
ir_model_fields_obj = self.env['ir.model.fields']
|
||||
ir_ui_view_obj = self.env['ir.ui.view']
|
||||
if self.remove_type == 'group':
|
||||
find_view_id = self.available_group_name and self.available_group_name.split('_', 1)[0] or False
|
||||
print("===find_view_id==", find_view_id)
|
||||
view_ids = ir_ui_view_obj.search([('inherit_id', 'child_of', int(find_view_id))], order="id desc")
|
||||
field_list = []
|
||||
for view_id in view_ids:
|
||||
print("===view_id========", view_id)
|
||||
root = ET.fromstring(view_id.arch_base)
|
||||
print("==========root=====", root)
|
||||
for child in root.iter('group'):
|
||||
name = child.find('field')
|
||||
if name.attrib and name.attrib.get('name', False):
|
||||
field_list.append(name.attrib.get('name', False))
|
||||
field_ids = ir_model_fields_obj.search([('name', 'in', field_list)])
|
||||
print("====field_ids====", field_ids, view_ids.ids)
|
||||
view_ids.unlink()
|
||||
for field_id in field_ids:
|
||||
ir_model_fields_obj.search([('depends', '=', field_id.name)]).unlink()
|
||||
field_ids.unlink()
|
||||
else:
|
||||
domain = expression.OR([('arch_db', 'like', record.name)] for record in self.field_id)
|
||||
print("===domain======", domain)
|
||||
view_ids = ir_ui_view_obj.search(domain)
|
||||
print("==========>>>>>>>>.", view_ids)
|
||||
for view_id in view_ids:
|
||||
# print("=====view_id.arch_base======before========", view_id.arch_base)
|
||||
root = ET.fromstring(view_id.arch_base)
|
||||
# result = len(root.getchildren())
|
||||
# count = sum(1 for root in root.iter("field"))
|
||||
# print("===============result==============>", result, count)
|
||||
for child in root.iter('field'):
|
||||
# print("===========>>>>",child.text, child.attrib, child.tag)
|
||||
if child.attrib and child.attrib.get('name', False) == self.field_id.name:
|
||||
view_id.unlink()
|
||||
# root.remove(child)
|
||||
# view_id.write({'arch_base': ET.tostring(root)})
|
||||
ir_model_fields_obj.search([('depends', '=', self.field_id.name)]).unlink()
|
||||
self.field_id.unlink()
|
||||
return {
|
||||
'type': 'ir.actions.client',
|
||||
'tag': 'reload',
|
||||
}
|
31
addons/digest/wizard/digest_custom_remove_view.xml
Normal file
31
addons/digest/wizard/digest_custom_remove_view.xml
Normal file
@ -0,0 +1,31 @@
|
||||
<?xml version="1.0"?>
|
||||
<flectra>
|
||||
<record id="digest_custom_remove_view_form" model="ir.ui.view">
|
||||
<field name="name">digest.custom.remove.form</field>
|
||||
<field name="model">digest.custom.remove</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Customized Digest">
|
||||
<group>
|
||||
<field name="remove_type"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="field_id" attrs="{'invisible': [('remove_type', '!=', 'field')]}" options="{'no_create': True}"/>
|
||||
<field name="available_group_name" attrs="{'invisible': [('remove_type', '!=', 'group')]}"/>
|
||||
</group>
|
||||
<footer>
|
||||
<button name="action_customize_digest_remove" string="Remove" type="object" class="btn btn-sm btn-primary"/>
|
||||
<button string="Cancel" class="btn btn-sm btn-default" special="cancel"/>
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="digest_custom_remove_action" model="ir.actions.act_window">
|
||||
<field name="name">Customized Digest Remove</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">digest.custom.remove</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="view_id" ref="digest_custom_remove_view_form"/>
|
||||
<field name="target">new</field>
|
||||
</record>
|
||||
</flectra>
|
@ -202,7 +202,9 @@ class EventEvent(models.Model):
|
||||
|
||||
@api.model
|
||||
def _tz_get(self):
|
||||
return [(x, x) for x in pytz.all_timezones]
|
||||
a = [(x, x) for x in pytz.all_timezones]
|
||||
print("\n\n\n=================>>>>>>.", a)
|
||||
return a
|
||||
|
||||
@api.one
|
||||
@api.depends('date_tz', 'date_begin')
|
||||
|
Loading…
Reference in New Issue
Block a user