Add optional PYSON recipients to email template

issue9617
review306401002
This commit is contained in:
Cédric Krier committed 2020-10-11 13:32:18 +02:00
1 parent 1984f83bbf
commit b8e2124a45
4 files changed
+148 -21

No files matched your search

+88 -15
View File
@@ -19,10 +19,10 @@ from genshi.template import TextTemplate
from trytond.i18n import gettext
from trytond.config import config
from trytond.model import ModelSQL, ModelView, fields
from trytond.model import ModelSQL, ModelView, fields, EvalEnvironment
from trytond.model.exceptions import ValidationError, AccessError
from trytond.pool import Pool
from trytond.pyson import Eval
from trytond.pyson import Eval, Bool, PYSONDecoder
from trytond.rpc import RPC
from trytond.sendmail import sendmail_transactional, SMTPDataManager
from trytond.tools import escape_wildcard
@@ -255,22 +255,55 @@ class EmailTemplate(ModelSQL, ModelView):
domain=[
('model', '=', Eval('model')),
],
depends=['model'],
states={
'invisible': Bool(Eval('recipients_pyson')),
},
depends=['model', 'recipients_pyson'],
help="The field that contains the recipient(s).")
recipients_pyson = fields.Char(
"Recipients",
states={
'invisible': Bool(Eval('recipients')),
},
depends=['recipients'],
help="A PYSON expression that generates a list of recipients"
'with the record represented by "self".')
recipients_secondary = fields.Many2One(
'ir.model.field', "Secondary Recipients",
domain=[
('model', '=', Eval('model')),
],
depends=['model'],
states={
'invisible': Bool(Eval('recipients_secondary_pyson')),
},
depends=['model', 'recipients_secondary_pyson'],
help="The field that contains the secondary recipient(s).")
recipients_secondary_pyson = fields.Char(
"Secondary Recipients",
states={
'invisible': Bool(Eval('recipients_secondary')),
},
depends=['recipients_secondary'],
help="A PYSON expression that generates a list of secondary recipients"
'with the record represented by "self".')
recipients_hidden = fields.Many2One(
'ir.model.field', "Hidden Recipients",
domain=[
('model', '=', Eval('model')),
],
depends=['model'],
states={
'invisible': Bool(Eval('recipients_hidden_pyson')),
},
depends=['model', 'recipients_hidden_pyson'],
help="The field that contains the secondary recipient(s).")
recipients_hidden_pyson = fields.Char(
"Hidden Recipients",
states={
'invisible': Bool(Eval('recipients_hidden')),
},
depends=['recipients_hidden'],
help="A PYSON expression that generates a list of hidden recipients"
'with the record represented by "self".')
subject = fields.Char("Subject", translate=True)
body = fields.Text("Body", translate=True)
reports = fields.Many2Many(
@@ -316,6 +349,7 @@ class EmailTemplate(ModelSQL, ModelView):
for template in templates:
template.check_subject()
template.check_body()
template.check_fields_pyson()
def check_subject(self):
if not self.subject:
@@ -339,27 +373,57 @@ class EmailTemplate(ModelSQL, ModelView):
template=self.rec_name,
exception=exception)) from exception
def check_fields_pyson(self):
encoder = PYSONDecoder(noeval=True)
for field in [
'recipients_pyson',
'recipients_secondary_pyson',
'recipients_hidden_pyson',
]:
value = getattr(self, field)
if not value:
continue
try:
pyson = encoder.decode(value)
except Exception as exception:
raise EmailTemplateError(
gettext('ir.msg_email_template_invalid_field_pyson',
template=self.rec_name,
field=self.__names__(field)['field'],
exception=exception)) from exception
if not isinstance(pyson, list) and pyson.types() != {list}:
raise EmailTemplateError(
gettext('ir.msg_email_template_invalid_field_pyson_type',
template=self.rec_name,
field=self.__names__(field)['field'],
))
def get(self, record):
pool = Pool()
Model = pool.get(self.model.model)
record = Model(int(record))
values = {}
for field, key in [
for attr, key in [
('recipients', 'to'),
('recipients_secondary', 'cc'),
('recipients_hidden', 'bcc'),
]:
field = getattr(self, field)
if not field:
continue
field = getattr(self, attr)
try:
if field.name == 'id':
value = record
if field:
if field.name == 'id':
value = record
else:
value = getattr(record, field.name, None)
if value:
values[key] = self.get_addresses(value)
else:
value = getattr(record, field.name, None)
if value:
values[key] = self.get_addresses(value)
value = getattr(self, attr + '_pyson')
if value:
value = self.eval(record, value)
if value:
values[key] = self.get_addresses(value)
except AccessError:
continue
@@ -381,6 +445,13 @@ class EmailTemplate(ModelSQL, ModelView):
values['reports'] = [r.id for r in self.reports]
return values
def eval(self, record, pyson):
'Evaluate the pyson with the record'
env = {}
env['context'] = Transaction().context
env['self'] = EvalEnvironment(record, record.__class__)
return PYSONDecoder(env).decode(pyson)
@classmethod
def _get_default_exclude(cls, record):
return ['create_uid', 'write_uid']
@@ -441,7 +512,9 @@ class EmailTemplate(ModelSQL, ModelView):
def _get_address(cls, record):
pool = Pool()
User = pool.get('res.user')
if isinstance(record, User) and record.email:
if isinstance(record, str):
return (None, record)
elif isinstance(record, User) and record.email:
return (record.name, record.email)
@classmethod
+6
View File
@@ -323,5 +323,11 @@ this repository contains the full copyright notices and license terms. -->
<record model="ir.message" id="msg_email_template_invalid_body">
<field name="text">Invalid body in e-mail template "%(template)s" with exception "%(exception)s"</field>
</record>
<record model="ir.message" id="msg_email_template_invalid_field_pyson">
<field name="text">Invalid PYSON %(field)s in e-mail template "%(template)s" with exception "%(exception)s".</field>
</record>
<record model="ir.message" id="msg_email_template_invalid_field_pyson_type">
<field name="text">The PYSON %(field)s in e-mail template "%(template)s" must generate a list.</field>
</record>
</data>
</tryton>
+15 -6
View File
@@ -6,12 +6,21 @@ this repository contains the full copyright notices and license terms. -->
<field name="name"/>
<label name="model"/>
<field name="model"/>
<label name="recipients"/>
<field name="recipients" colspan="3"/>
<label name="recipients_secondary"/>
<field name="recipients_secondary" colspan="3"/>
<label name="recipients_hidden"/>
<field name="recipients_hidden" colspan="3"/>
<label id="recipients_label" string="Recipients:"/>
<group id="recipients" col="-1" colspan="3">
<field name="recipients"/>
<field name="recipients_pyson" widget="pyson"/>
</group>
<label id="recipients_secondary_label" string="Secondary Recipients:"/>
<group id="recipients_secondary" col="-1" colspan="3">
<field name="recipients_secondary"/>
<field name="recipients_secondary_pyson"/>
</group>
<label id="recipients_hidden_label" string="Hidden Recipients:"/>
<group id="recipients_hidden" col="-1" colspan="3">
<field name="recipients_hidden"/>
<field name="recipients_hidden_pyson"/>
</group>
<label name="subject"/>
<field name="subject" colspan="3"/>
<separator name="body" colspan="4"/>
+39
View File
@@ -9,6 +9,7 @@ from dateutil.relativedelta import relativedelta
from trytond.config import config
from trytond.pool import Pool
from trytond.pyson import Eval, If, PYSONEncoder
from trytond.transaction import Transaction
from .test_tryton import ModuleTestCase, with_transaction
@@ -246,6 +247,44 @@ class IrTestCase(ModuleTestCase):
'subject': "User: Administrator",
})
@with_transaction()
def test_email_template_get_pyson(self):
"Test email template get with pyson"
pool = Pool()
Template = pool.get('ir.email.template')
IrModel = pool.get('ir.model')
IrModelField = pool.get('ir.model.field')
User = pool.get('res.user')
admin = User(1)
admin.email = 'admin@example.com'
admin.save()
model, = IrModel.search([('model', '=', 'res.user')])
field, = IrModelField.search([
('model', '=', model.id),
('name', '=', 'id'),
])
template = Template(
model=model,
name="Test",
recipients_pyson=PYSONEncoder().encode(
[Eval('self.email')]),
recipients_secondary_pyson=PYSONEncoder().encode(
If(Eval('self.email'),
['fallback@example.com'],
[])),
)
template.save()
values = template.get(admin)
self.assertEqual(
values, {
'to': ['admin@example.com'],
'cc': ['fallback@example.com'],
})
def suite():
return unittest.TestLoader().loadTestsFromTestCase(IrTestCase)