[IMP]: dynamic set domain, field and model for new digest field
This commit is contained in:
parent
84ed2b2571
commit
c2fd95a87b
@ -15,6 +15,7 @@ Send KPI Digests periodically
|
||||
'data': [
|
||||
'security/ir.model.access.csv',
|
||||
'data/digest_template_data.xml',
|
||||
'views/digest_templates.xml',
|
||||
'data/digest_data.xml',
|
||||
'data/ir_cron_data.xml',
|
||||
'data/res_config_settings_data.xml',
|
||||
@ -22,7 +23,6 @@ Send KPI Digests periodically
|
||||
'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',
|
||||
],
|
||||
'installable': True,
|
||||
|
@ -10,8 +10,8 @@
|
||||
<table style="width: 100%; border-spacing: 0; font-family: Helvetica,Arial,Verdana,sans-serif;">
|
||||
<tr>
|
||||
<td align="center" valign="top" style="border-collapse: collapse; padding: 0">
|
||||
% set company, user = ctx['company'], ctx['user']
|
||||
% set data = object.compute_kpis(company, user)
|
||||
% set company, user = user.company_id, user
|
||||
% set data = object.compute_kpis(user.company, user)
|
||||
% set tips = object.compute_tips(company, user)
|
||||
% set kpi_actions = object.compute_kpis_actions(company, user)
|
||||
% set kpis = data.yesterday.keys()
|
||||
@ -120,32 +120,6 @@
|
||||
</tr>
|
||||
</table>
|
||||
% endif
|
||||
<table style="width: 100%; max-width: 600px; margin-top: 5px; border: 1px solid #e7e7e7;">
|
||||
<tr>
|
||||
<td style="border-collapse: collapse; background-color: #ffffff; line-height: 21px; padding: 0 20px 10px 20px; text-align: center;"><br/>
|
||||
<div style="color: #3d466e; font-size: 16px; font-weight: 600; line-height: 23px;">Run your bussiness from anywhere with Flectra Mobile.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<div style="text-align: center;"><a href="https://play.google.com/store/apps/details?id=com.flectrahq.mobile" target="_blank"><img src="/digest/static/src/img/google_play.png" style="display: inline-block; height: 30px; margin-left: auto; margin-right: 12px;"/></a><a href="https://itunes.apple.com/us/app/flectrahq/id1272543640" target="_blank"><img src="/digest/static/src/img/app_store.png" style="display: inline-block; height: 30px; margin-left: 12px; margin-right: auto;"/></a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<table style="margin-top: 5px; border: 1px solid #e7e7e7; font-size: 15px; width: 100%; max-width: 600px;">
|
||||
<tr>
|
||||
<td style="border-collapse: collapse; margin: 0; padding: 10px 20px;">
|
||||
% if ctx['user'].has_group('base.group_system'):
|
||||
<div style="margin-top: 20px;">
|
||||
Want to customize the email?
|
||||
<a href="/web#view_type=form&model=digest.digest&id=${object.id}" target="_blank" style="color: #875A7B;">Choose the metrics you care about</a>
|
||||
</div>
|
||||
<br />
|
||||
% endif
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
@ -44,7 +44,6 @@ class Digest(models.Model):
|
||||
kpi_mail_message_total = fields.Boolean('Messages')
|
||||
kpi_mail_message_total_value = fields.Integer(compute='_compute_kpi_mail_message_total_value')
|
||||
|
||||
|
||||
def _compute_is_subscribed(self):
|
||||
for digest in self:
|
||||
digest.is_subscribed = self.env.user in digest.user_ids
|
||||
@ -108,6 +107,10 @@ class Digest(models.Model):
|
||||
|
||||
def compute_kpis(self, company, user):
|
||||
self.ensure_one()
|
||||
if not company:
|
||||
company = self.env.user.company_id
|
||||
if not user:
|
||||
user = self.env.user
|
||||
res = {}
|
||||
for tf_name, tf in self._compute_timeframes(company).items():
|
||||
digest = self.with_context(start_date=tf[0][0], end_date=tf[0][1], company=company).sudo(user.id)
|
||||
@ -115,7 +118,6 @@ class Digest(models.Model):
|
||||
kpis = {}
|
||||
for field_name, field in self._fields.items():
|
||||
if field.type == 'boolean' and (field_name.startswith('kpi_') or field_name.startswith('x_kpi_')) and self[field_name]:
|
||||
|
||||
try:
|
||||
compute_value = digest[field_name + '_value']
|
||||
previous_value = previous_digest[field_name + '_value']
|
||||
|
@ -10,8 +10,9 @@ class ResUsers(models.Model):
|
||||
def create(self, vals):
|
||||
""" Automatically subscribe employee users to default digest if activated """
|
||||
user = super(ResUsers, self).create(vals)
|
||||
default_digest_emails = self.env['ir.config_parameter'].sudo().get_param('digest.default_digest_emails')
|
||||
default_digest_id = self.env['ir.config_parameter'].sudo().get_param('digest.default_digest_id')
|
||||
config_obj = self.env['ir.config_parameter'].sudo()
|
||||
default_digest_emails = config_obj.get_param('digest.default_digest_emails')
|
||||
default_digest_id = config_obj.get_param('digest.default_digest_id')
|
||||
if user.has_group('base.group_user') and default_digest_emails and default_digest_id:
|
||||
digest = self.env['digest.digest'].sudo().browse(int(default_digest_id))
|
||||
digest.user_ids |= user
|
||||
|
@ -13,4 +13,33 @@
|
||||
</div>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<record model="ir.ui.view" id="email_template_preview_digest_form">
|
||||
<field name="name">email_template.preview.digest.form</field>
|
||||
<field name="model">email_template.preview</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Preview Of Digest">
|
||||
Choose an example <field name="model_id" class="oe_inline" readonly="1"/> record:
|
||||
<field name="res_id" class="oe_inline" style="margin-left: 8px;"/>
|
||||
<field name="body_html" widget="html" readonly="1"
|
||||
nolabel="1" options='{"safe": True}'/>
|
||||
<footer>
|
||||
<button string="Discard" class="btn-default" special="cancel"/>
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="wizard_email_template_preview_digest" model="ir.actions.act_window">
|
||||
<field name="name">Preview of Digest</field>
|
||||
<field name="res_model">email_template.preview</field>
|
||||
<field name="src_model">digest.digest</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="view_id" ref="email_template_preview_digest_form"/>
|
||||
<field name="target">new</field>
|
||||
<field name="context">{'template_id':template_id, 'default_res_id': active_id}</field>
|
||||
</record>
|
||||
|
||||
</flectra>
|
@ -1,5 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<flectra>
|
||||
|
||||
<record id="digest_digest_view_tree" model="ir.ui.view">
|
||||
<field name="name">digest.digest.view.tree</field>
|
||||
<field name="model">digest.digest</field>
|
||||
@ -36,6 +37,9 @@
|
||||
<field name="state" widget="statusbar"/>
|
||||
</header>
|
||||
<sheet>
|
||||
<div class="oe_button_box" name="button_box">
|
||||
<button class="oe_stat_button" name="%(digest.wizard_email_template_preview_digest)d" icon="fa-search-plus" string="Preview" type="action" target="new" context="{'template_id': template_id}" attrs="{'invisible': [('template_id', '=', False)]}"/>
|
||||
</div>
|
||||
<div class="oe_title">
|
||||
<label for="name" class="oe_edit_only"/>
|
||||
<h1>
|
||||
@ -92,4 +96,5 @@
|
||||
parent="base.menu_email"
|
||||
groups="base.group_erp_manager"
|
||||
sequence="93"/>
|
||||
|
||||
</flectra>
|
||||
|
@ -11,36 +11,45 @@ import xml.etree.ElementTree as ET
|
||||
class DigestCustomFields(models.TransientModel):
|
||||
_name = 'digest.custom.fields'
|
||||
|
||||
|
||||
DEFAULT_PYTHON_CODE = """# Available variables:
|
||||
# - env: Flectra Environment on which the action is triggered
|
||||
# - model: Flectra Model of the record on which the action is triggered; is a void recordset
|
||||
# - record: record on which the action is triggered; may be be void
|
||||
# - records: recordset of all records on which the action is triggered in multi-mode; may be void
|
||||
# - 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 = {...}
|
||||
for rec in self:
|
||||
rec[''] = self.env[''].search([])\n\n\n\n"""
|
||||
|
||||
|
||||
field_name = fields.Char('Field Name', default='x_kpi_', required=True)
|
||||
label_name = fields.Char('Label Name', required=True)
|
||||
# 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 = fields.Text(string='Compute', groups='base.group_system')
|
||||
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')
|
||||
model_id = fields.Many2one(
|
||||
'ir.model', string='Model')
|
||||
model_name = fields.Char(related='model_id.model', string='Model Name')
|
||||
model_domain = fields.Char(string='Domain', oldname='domain', default=[])
|
||||
model_real = fields.Char(compute='_compute_model', string='Real Model')
|
||||
related_date_field_id = fields.Many2one('ir.model.fields', 'Date')
|
||||
|
||||
@api.onchange('compute_field_name', 'model_id', 'model_domain', 'date_domain', 'related_date_field_id')
|
||||
def onchange_compute_field_name(self):
|
||||
data = """for record in self:
|
||||
start, end, company = record._get_kpi_compute_parameters()
|
||||
"""
|
||||
domain = self.model_domain
|
||||
date = self.related_date_field_id
|
||||
domain_values = """[["%s", ">=", start], ["%s", "<", end]]""" % (date.name or '', date.name or '')
|
||||
if domain != '[]':
|
||||
domain += domain_values
|
||||
domain = domain.replace("][", ",")
|
||||
else:
|
||||
domain = domain_values
|
||||
|
||||
data += "record['%s'] = self.env['%s'].search_count(%s)" % (self.compute_field_name, self.model_real or '', domain) + "\n\n"
|
||||
self.compute = data
|
||||
|
||||
@api.depends('model_id')
|
||||
def _compute_model(self):
|
||||
for record in self:
|
||||
if record.model_id:
|
||||
record.model_real = record.model_name or 'res.users'
|
||||
|
||||
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 = {}
|
||||
@ -70,8 +79,6 @@ for rec in 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_'!"))
|
||||
# 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,9 +97,8 @@ for rec in self:
|
||||
'field_description': self.label_name,
|
||||
'model': 'digest.digest'
|
||||
}
|
||||
ir_model_fields_obj.create(values)
|
||||
|
||||
values = {
|
||||
compute_values = {
|
||||
'model_id': model_id.id,
|
||||
'ttype': self.ttype,
|
||||
'name': self.field_name + '_value',
|
||||
@ -101,7 +107,12 @@ for rec in self:
|
||||
'depends': first_field_name,
|
||||
'compute': self.compute
|
||||
}
|
||||
|
||||
try:
|
||||
ir_model_fields_obj.create(values)
|
||||
ir_model_fields_obj.create(compute_values)
|
||||
except Exception as e:
|
||||
raise ValidationError(e)
|
||||
|
||||
def field_arch(self):
|
||||
xpath = etree.Element('xpath')
|
||||
@ -109,19 +120,16 @@ for rec in self:
|
||||
expr = '//' + 'group' + '[@name="' + name + '"]'
|
||||
xpath.set('expr', expr)
|
||||
xpath.set('position', self.position)
|
||||
|
||||
if self.position == 'inside':
|
||||
field = etree.Element('field')
|
||||
field.set('name', self.field_name)
|
||||
xpath.set('expr', expr)
|
||||
|
||||
if self.position == 'inside':
|
||||
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)
|
||||
|
||||
|
@ -12,11 +12,38 @@
|
||||
<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">
|
||||
<group>
|
||||
<label for="model_id"/>
|
||||
<div>
|
||||
<field name="model_id" options="{'no_create': True}" required="1"/>
|
||||
<field name="model_name" invisible="1"/>
|
||||
<field name="model_real" invisible="1"/>
|
||||
<field name="model_domain" widget="domain"
|
||||
options="{'model': 'model_real'}"/>
|
||||
</div>
|
||||
</group>
|
||||
<group>
|
||||
<field name="related_date_field_id" options="{'no_create': True}" domain="[('model', '=', model_real), ('ttype', 'in', ['date', 'datetime'])]" required="1"/>
|
||||
</group>
|
||||
<notebook>
|
||||
<page string="Compute Details">
|
||||
<group>
|
||||
<field name="compute_field_name"/>
|
||||
<field name="ttype"/>
|
||||
<field name="compute" widget="ace" options="{'mode': 'python'}"/>
|
||||
</group>
|
||||
</page>
|
||||
<page string="Help">
|
||||
<h3>How to define a computed field</h3>
|
||||
<div class="alert alert-info" role="alert">
|
||||
The field <code>Compute</code> is the Python code to compute the value of the field on a set of records.
|
||||
The value of the field must be assigned to each record with a dictionary-like assignment.
|
||||
<pre>
|
||||
for record in self:
|
||||
record['size'] = len(record.name)</pre>
|
||||
</div>
|
||||
</page>
|
||||
</notebook>
|
||||
<footer>
|
||||
<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"/>
|
||||
|
@ -34,40 +34,27 @@ class DigestCustomRemove(models.TransientModel):
|
||||
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 {
|
||||
|
@ -6,14 +6,15 @@
|
||||
<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')]}"/>
|
||||
<field name="remove_type"/>
|
||||
<field name="field_id" attrs="{'invisible': [('remove_type', '!=', 'field')], 'required': [('remove_type', '=', 'field')]}" options="{'no_create': True}"/>
|
||||
<field name="available_group_name" attrs="{'invisible': [('remove_type', '!=', 'group')], 'required': [('remove_type', '=', 'group')]}"/>
|
||||
</group>
|
||||
</group>
|
||||
<footer>
|
||||
<button name="action_customize_digest_remove" string="Remove" type="object" class="btn btn-sm btn-primary"/>
|
||||
<button name="action_customize_digest_remove" string="Remove Field" attrs="{'invisible': [('remove_type', '!=', 'field')]}" type="object" class="btn btn-sm btn-primary"/>
|
||||
<button name="action_customize_digest_remove" string="Remove Group" attrs="{'invisible': [('remove_type', '!=', 'group')]}" type="object" class="btn btn-sm btn-primary" confirm="Do you want remove all child related to this group?"/>
|
||||
<button string="Cancel" class="btn btn-sm btn-default" special="cancel"/>
|
||||
</footer>
|
||||
</form>
|
||||
|
Loading…
Reference in New Issue
Block a user