From b16935efdc608361db0ad50f3ddffaabbf86d1e3 Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Tue, 4 Jun 2013 13:13:54 +0200 Subject: [PATCH 01/16] [IMP] orm: include forbidden IDs when logging ir.rules violations, to make troubleshooting easier bzr revid: odo@openerp.com-20130604111354-1x5yc305okhx5ujk --- openerp/osv/orm.py | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/openerp/osv/orm.py b/openerp/osv/orm.py index a451fbf6582..765ab434f9b 100644 --- a/openerp/osv/orm.py +++ b/openerp/osv/orm.py @@ -3853,11 +3853,12 @@ class BaseModel(object): # Attempt to distinguish record rule restriction vs deleted records, # to provide a more specific error message - check if the missinf cr.execute('SELECT id FROM ' + self._table + ' WHERE id IN %s', (tuple(missing_ids),)) - if cr.rowcount: + forbidden_ids = [x[0] for x in cr.fetchall()] + if forbidden_ids: # the missing ids are (at least partially) hidden by access rules if uid == SUPERUSER_ID: return - _logger.warning('Access Denied by record rules for operation: %s, uid: %s, model: %s', operation, uid, self._name) + _logger.warning('Access Denied by record rules for operation: %s on record ids: %r, uid: %s, model: %s', operation, forbidden_ids, uid, self._name) raise except_orm(_('Access Denied'), _('The requested operation cannot be completed due to security restrictions. Please contact your system administrator.\n\n(Document type: %s, Operation: %s)') % \ (self._description, operation)) @@ -4817,8 +4818,8 @@ class BaseModel(object): order_field = order_split[0].strip() order_direction = order_split[1].strip() if len(order_split) == 2 else '' inner_clause = None - if order_field == 'id': - order_by_elements.append('"%s"."id" %s' % (self._table, order_direction)) + if order_field == 'id' or (self._log_access and order_field in LOG_ACCESS_COLUMNS.keys()): + order_by_elements.append('"%s"."%s" %s' % (self._table, order_field, order_direction)) elif order_field in self._columns: order_column = self._columns[order_field] if order_column._classic_read: @@ -4836,6 +4837,8 @@ class BaseModel(object): inner_clause = self._generate_m2o_order_by(order_field, query) else: continue # ignore non-readable or "non-joinable" fields + else: + raise ValueError( _("Sorting field %s not found on model %s") %( order_field, self._name)) if inner_clause: if isinstance(inner_clause, list): for clause in inner_clause: @@ -5092,20 +5095,18 @@ class BaseModel(object): :param parent: optional parent field name (default: ``self._parent_name = parent_id``) :return: **True** if the operation can proceed safely, or **False** if an infinite loop is detected. """ - if not parent: parent = self._parent_name - ids_parent = ids[:] - query = 'SELECT distinct "%s" FROM "%s" WHERE id IN %%s' % (parent, self._table) - while ids_parent: - ids_parent2 = [] - for i in range(0, len(ids), cr.IN_MAX): - sub_ids_parent = ids_parent[i:i+cr.IN_MAX] - cr.execute(query, (tuple(sub_ids_parent),)) - ids_parent2.extend(filter(None, map(lambda x: x[0], cr.fetchall()))) - ids_parent = ids_parent2 - for i in ids_parent: - if i in ids: + + # must ignore 'active' flag, ir.rules, etc. => direct SQL query + query = 'SELECT "%s" FROM "%s" WHERE id = %%s' % (parent, self._table) + for id in ids: + current_id = id + while current_id is not None: + cr.execute(query, (current_id,)) + result = cr.fetchone() + current_id = result[0] if result else None + if current_id == id: return False return True From 9fd2e7297ffb0fe9cb96767bcb8ad686a66d064b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Wed, 5 Jun 2013 12:13:00 +0200 Subject: [PATCH 02/16] [IMP] Chatter: better display of date bzr revid: tde@openerp.com-20130605101300-t1lwykg3d6lp7au2 --- addons/mail/static/src/js/mail.js | 1 + addons/mail/static/src/xml/mail.xml | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/addons/mail/static/src/js/mail.js b/addons/mail/static/src/js/mail.js index 2cd9c7c077d..70e93e2957c 100644 --- a/addons/mail/static/src/js/mail.js +++ b/addons/mail/static/src/js/mail.js @@ -259,6 +259,7 @@ openerp.mail = function (session) { format_data: function () { //formating and add some fields for render this.date = this.date ? session.web.str_to_datetime(this.date) : false; + this.display_date = this.date.toString('ddd MMM dd yyyy HH:mm'); if (this.date && new Date().getTime()-this.date.getTime() < 7*24*60*60*1000) { this.timerelative = $.timeago(this.date); } diff --git a/addons/mail/static/src/xml/mail.xml b/addons/mail/static/src/xml/mail.xml index f5004941a8d..b3b79bcfc43 100644 --- a/addons/mail/static/src/xml/mail.xml +++ b/addons/mail/static/src/xml/mail.xml @@ -295,7 +295,10 @@ notified - + + + + From 9c57f6e1cdaf8a80a403486b51cd9e661c11e3dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Thu, 6 Jun 2013 12:24:37 +0200 Subject: [PATCH 03/16] [FIX] mail: various fixes Mass mailing: emails due to mass mailing are not queued instead of sent immediately. As most of cases go through mail_followers._notify(), a new argument is added that allows to control the force_send (like what is done in email_template). Chatter: better display of recipients when having a message_post due to mass mailing (not always displaying logged a note). Notified people: when having no subtype, just avoid notifying the followers but take into account specified recipients (partner_ids). bzr revid: tde@openerp.com-20130606102437-43s4xp96b5wa3r1q --- addons/mail/mail_followers.py | 17 +++++---- addons/mail/mail_mail.py | 16 ++++----- addons/mail/mail_message.py | 42 ++++++++++++++-------- addons/mail/static/src/xml/mail.xml | 6 ++-- addons/mail/wizard/mail_compose_message.py | 12 +++---- 5 files changed, 49 insertions(+), 44 deletions(-) diff --git a/addons/mail/mail_followers.py b/addons/mail/mail_followers.py index b85410a7a1c..93196976e90 100644 --- a/addons/mail/mail_followers.py +++ b/addons/mail/mail_followers.py @@ -150,11 +150,14 @@ class mail_notification(osv.Model): return footer - def _notify(self, cr, uid, msg_id, partners_to_notify=None, context=None): + def _notify(self, cr, uid, msg_id, partners_to_notify=None, context=None, force_send=False): """ Send by email the notification depending on the user preferences :param list partners_to_notify: optional list of partner ids restricting the notifications to process + :param bool force_send: if True, the generated mail.mail is + immediately sent after being created, as if the scheduler + was executed for this message only. """ if context is None: context = {} @@ -203,13 +206,9 @@ class mail_notification(osv.Model): 'recipient_ids': [(4, id) for id in notify_partner_ids], 'references': references, } - if msg.email_from: - mail_values['email_from'] = msg.email_from - if msg.reply_to: - mail_values['reply_to'] = msg.reply_to mail_mail = self.pool.get('mail.mail') email_notif_id = mail_mail.create(cr, uid, mail_values, context=context) - try: - return mail_mail.send(cr, uid, [email_notif_id], context=context) - except Exception: - return False + + if force_send: + mail_mail.send(cr, uid, [email_notif_id], context=context) + return True diff --git a/addons/mail/mail_mail.py b/addons/mail/mail_mail.py index a36ec85b27b..4da3b27bd7d 100644 --- a/addons/mail/mail_mail.py +++ b/addons/mail/mail_mail.py @@ -55,7 +55,6 @@ class mail_mail(osv.Model): 'auto_delete': fields.boolean('Auto Delete', help="Permanently delete this email after sending it, to save space"), 'references': fields.text('References', help='Message references, such as identifiers of previous messages', readonly=1), - 'email_from': fields.char('From', help='Message sender, taken from user preferences.'), 'email_to': fields.text('To', help='Message recipients (emails)'), 'recipient_ids': fields.many2many('res.partner', string='To (Partners)'), 'email_cc': fields.char('Cc', help='Carbon copy message recipients'), @@ -67,16 +66,10 @@ class mail_mail(osv.Model): } def _get_default_from(self, cr, uid, context=None): - this = self.pool.get('res.users').browse(cr, SUPERUSER_ID, uid, context=context) - if this.alias_domain: - return '%s <%s@%s>' % (this.name, this.alias_name, this.alias_domain) - elif this.email: - return '%s <%s>' % (this.name, this.email) - raise osv.except_osv(_('Invalid Action!'), _("Unable to send email, please configure the sender's email address or alias.")) + return self.pool['mail.message']._get_default_from(cr, uid, context=context) _defaults = { 'state': 'outgoing', - 'email_from': lambda self, cr, uid, ctx=None: self._get_default_from(cr, uid, ctx), } def default_get(self, cr, uid, fields, context=None): @@ -97,19 +90,22 @@ class mail_mail(osv.Model): # model, res_id: comes from values OR related message model = values.get('model') res_id = values.get('res_id') + email_from = values.get('email_from') if values.get('mail_message_id') and (not model or not res_id): message = self.pool.get('mail.message').browse(cr, uid, values.get('mail_message_id'), context=context) if not model: model = message.model if not res_id: res_id = message.res_id + if not email_from: + email_from = message.email_from # if model and res_id: try to use ``message_get_reply_to`` that returns the document alias if model and res_id and hasattr(self.pool[model], 'message_get_reply_to'): email_reply_to = self.pool[model].message_get_reply_to(cr, uid, [res_id], context=context)[0] # no alias reply_to -> reply_to will be the email_from, only the email part - if not email_reply_to and values.get('email_from'): - emails = tools.email_split(values.get('email_from')) + if not email_reply_to and email_from: + emails = tools.email_split(email_from) if emails: email_reply_to = emails[0] diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py index b8a00a790a8..884d59ba9be 100644 --- a/addons/mail/mail_message.py +++ b/addons/mail/mail_message.py @@ -182,6 +182,14 @@ class mail_message(osv.Model): def _needaction_domain_get(self, cr, uid, context=None): return [('to_read', '=', True)] + def _get_default_from(self, cr, uid, context=None): + this = self.pool.get('res.users').browse(cr, SUPERUSER_ID, uid, context=context) + if this.alias_domain: + return '%s <%s@%s>' % (this.name, this.alias_name, this.alias_domain) + elif this.email: + return '%s <%s>' % (this.name, this.email) + raise osv.except_osv(_('Invalid Action!'), _("Unable to send email, please configure the sender's email address or alias.")) + def _get_default_author(self, cr, uid, context=None): return self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0] @@ -190,7 +198,7 @@ class mail_message(osv.Model): 'date': lambda *a: fields.datetime.now(), 'author_id': lambda self, cr, uid, ctx=None: self._get_default_author(cr, uid, ctx), 'body': '', - 'email_from': lambda self, cr, uid, ctx=None: self.pool.get('mail.mail')._get_default_from(cr, uid, ctx), + 'email_from': lambda self, cr, uid, ctx=None: self._get_default_from(cr, uid, ctx), } #------------------------------------------------------ @@ -315,8 +323,10 @@ class mail_message(osv.Model): for key, message in message_tree.iteritems(): if message.author_id: partner_ids |= set([message.author_id.id]) - if message.notified_partner_ids: + if message.subtype_id and message.notified_partner_ids: # take notified people of message with a subtype partner_ids |= set([partner.id for partner in message.notified_partner_ids]) + elif not message.subtype_id and message.partner_ids: # take specified people of message without a subtype (log) + partner_ids |= set([partner.id for partner in message.partner_ids]) if message.attachment_ids: attachment_ids |= set([attachment.id for attachment in message.attachment_ids]) # Read partners as SUPERUSER -> display the names like classic m2o even if no access @@ -336,9 +346,15 @@ class mail_message(osv.Model): else: author = (0, message.email_from) partner_ids = [] - for partner in message.notified_partner_ids: - if partner.id in partner_tree: - partner_ids.append(partner_tree[partner.id]) + if message.subtype_id: + partner_ids = [partner_tree[partner.id] for partner in message.notified_partner_ids + if partner.id in partner_tree] + else: + partner_ids = [partner_tree[partner.id] for partner in message.partner_ids + if partner.id in partner_tree] + # for partner in message.notified_partner_ids: + # if partner.id in partner_tree: + # partner_ids.append(partner_tree[partner.id]) attachment_ids = [] for attachment in message.attachment_ids: if attachment.id in attachments_tree: @@ -752,7 +768,7 @@ class mail_message(osv.Model): elif not values.get('message_id'): values['message_id'] = tools.generate_tracking_message_id('private') newid = super(mail_message, self).create(cr, uid, values, context) - self._notify(cr, uid, newid, context=context) + self._notify(cr, uid, newid, context=context, force_send=context.get('mail_notify_force_send', True)) # TDE FIXME: handle default_starred. Why not setting an inv on starred ? # Because starred will call set_message_starred, that looks for notifications. # When creating a new mail_message, it will create a notification to a message @@ -866,20 +882,16 @@ class mail_message(osv.Model): return '' return result - def _notify(self, cr, uid, newid, context=None): + def _notify(self, cr, uid, newid, context=None, force_send=False): """ Add the related record followers to the destination partner_ids if is not a private message. Call mail_notification.notify to manage the email sending """ notification_obj = self.pool.get('mail.notification') message = self.browse(cr, uid, newid, context=context) - partners_to_notify = set([]) - # message has no subtype_id: pure log message -> no partners, no one notified - if not message.subtype_id: - return True - # all followers of the mail.message document have to be added as partners and notified - if message.model and message.res_id: + # all followers of the mail.message document have to be added as partners and notified if a subtype is defined (otherwise: log message) + if message.subtype_id and message.model and message.res_id: fol_obj = self.pool.get("mail.followers") # browse as SUPERUSER because rules could restrict the search results fol_ids = fol_obj.search(cr, SUPERUSER_ID, [ @@ -889,7 +901,7 @@ class mail_message(osv.Model): ], context=context) partners_to_notify |= set(fo.partner_id for fo in fol_obj.browse(cr, SUPERUSER_ID, fol_ids, context=context)) # remove me from notified partners, unless the message is written on my own wall - if message.author_id and message.model == "res.partner" and message.res_id == message.author_id.id: + if message.subtype_id and message.author_id and message.model == "res.partner" and message.res_id == message.author_id.id: partners_to_notify |= set([message.author_id]) elif message.author_id: partners_to_notify -= set([message.author_id]) @@ -900,7 +912,7 @@ class mail_message(osv.Model): # notify if partners_to_notify: - notification_obj._notify(cr, uid, newid, partners_to_notify=[p.id for p in partners_to_notify], context=context) + notification_obj._notify(cr, uid, newid, partners_to_notify=[p.id for p in partners_to_notify], context=context, force_send=force_send) message.refresh() # An error appear when a user receive a notification without notifying diff --git a/addons/mail/static/src/xml/mail.xml b/addons/mail/static/src/xml/mail.xml index b3b79bcfc43..8c507925359 100644 --- a/addons/mail/static/src/xml/mail.xml +++ b/addons/mail/static/src/xml/mail.xml @@ -271,16 +271,16 @@ - + logged a note - + to nobody - diff --git a/addons/mail/wizard/mail_compose_message.py b/addons/mail/wizard/mail_compose_message.py index 7bf4d47d1ff..383929a6e07 100644 --- a/addons/mail/wizard/mail_compose_message.py +++ b/addons/mail/wizard/mail_compose_message.py @@ -126,7 +126,6 @@ class mail_compose_message(osv.TransientModel): _defaults = { 'composition_mode': 'comment', - 'email_from': lambda self, cr, uid, ctx={}: self.pool.get('mail.mail')._get_default_from(cr, uid, context=ctx), 'body': lambda self, cr, uid, ctx={}: '', 'subject': lambda self, cr, uid, ctx={}: False, 'partner_ids': lambda self, cr, uid, ctx={}: [], @@ -158,7 +157,7 @@ class mail_compose_message(osv.TransientModel): return super(mail_compose_message, self).check_access_rule(cr, uid, ids, operation, context=context) - def _notify(self, cr, uid, newid, context=None): + def _notify(self, cr, uid, newid, context=None, force_send=False): """ Override specific notify method of mail.message, because we do not want that feature in the wizard. """ return @@ -280,11 +279,10 @@ class mail_compose_message(osv.TransientModel): elif mass_mail_mode: # mass mail: is a log pushed to recipients unless specified, author not added if not wizard.notify: subtype = False - context = dict(context, mail_create_nosubscribe=True) # add context key to avoid subscribing the author - msg_id = active_model_pool.message_post(cr, uid, [res_id], type='comment', subtype=subtype, context=context, **post_values) - # mass_mailing, post without notify: notify specific partners - if mass_mail_mode and not wizard.notify and post_values['partner_ids']: - self.pool.get('mail.notification')._notify(cr, uid, msg_id, post_values['partner_ids'], context=context) + context = dict(context, + mail_notify_force_send=False, # do not send emails directly but use the queue instead + mail_create_nosubscribe=True) # add context key to avoid subscribing the author + active_model_pool.message_post(cr, uid, [res_id], type='comment', subtype=subtype, context=context, **post_values) return {'type': 'ir.actions.act_window_close'} From a216e70e8513d1b9cad807b738023d8a92d8c8aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Thu, 6 Jun 2013 14:32:30 +0200 Subject: [PATCH 04/16] [FIX] [IMP] mail: allow to use message_post on models not having message_post method defined. mail.compose.message wizard and messaage_process now check that the active model has message_post defined. Otherwise using thread_model context key that is better managed in message_post, it is possible to use mail_thread.message_post() with a thread_model and thread_id that should not allow message_post otherwise. Hint: look for backport into 7.0 bzr revid: tde@openerp.com-20130606123230-d7ql14yydzfrlc7h --- addons/mail/mail_thread.py | 5 ++++- addons/mail/wizard/mail_compose_message.py | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index 6a32ed3bb52..e676e69552d 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -730,6 +730,9 @@ class mail_thread(osv.AbstractModel): else: assert thread_id == 0, "Posting a message without model should be with a null res_id, to create a private message." model_pool = self.pool.get('mail.thread') + if not hasattr(hasattr, 'message_post'): + context['thread_model'] = model + model_pool = self.pool['mail.thread'] new_msg_id = model_pool.message_post(cr, uid, [thread_id], context=context, subtype='mail.mt_comment', **msg) if partner_ids: @@ -1064,7 +1067,7 @@ class mail_thread(osv.AbstractModel): model = False if thread_id: model = context.get('thread_model', self._name) if self._name == 'mail.thread' else self._name - if model != self._name: + if model != self._name and hasattr(self.pool[model], 'message_post'): del context['thread_model'] return self.pool[model].message_post(cr, uid, thread_id, body=body, subject=subject, type=type, subtype=subtype, parent_id=parent_id, attachments=attachments, context=context, content_subtype=content_subtype, **kwargs) diff --git a/addons/mail/wizard/mail_compose_message.py b/addons/mail/wizard/mail_compose_message.py index 383929a6e07..e3383270548 100644 --- a/addons/mail/wizard/mail_compose_message.py +++ b/addons/mail/wizard/mail_compose_message.py @@ -234,6 +234,9 @@ class mail_compose_message(osv.TransientModel): for wizard in self.browse(cr, uid, ids, context=context): mass_mail_mode = wizard.composition_mode == 'mass_mail' active_model_pool = self.pool[wizard.model if wizard.model else 'mail.thread'] + if not hasattr(active_model_pool, 'message_post'): + context['thread_model'] = wizard.model + active_model_pool = self.pool['mail.thread'] # wizard works in batch mode: [res_id] or active_ids res_ids = active_ids if mass_mail_mode and wizard.model and active_ids else [wizard.res_id] From b3cde8da4a0345ba9f1314681339bcba76bc33d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Thu, 6 Jun 2013 14:34:15 +0200 Subject: [PATCH 05/16] [FIX] email_template: allow to define context action even without having create access on ir.values and ir.action Hint: look for backport into 7.0 bzr revid: tde@openerp.com-20130606123415-vuelai2eh1yy3c87 --- addons/email_template/email_template.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/addons/email_template/email_template.py b/addons/email_template/email_template.py index 4a767457321..c7efb9582fe 100644 --- a/addons/email_template/email_template.py +++ b/addons/email_template/email_template.py @@ -24,6 +24,7 @@ import base64 import logging import openerp +from openerp import SUPERUSER_ID from openerp.osv import osv, fields from openerp.osv import fields from openerp import tools @@ -199,7 +200,7 @@ class email_template(osv.osv): model_data_id = data_obj._get_id(cr, uid, 'mail', 'email_compose_message_wizard_form') res_id = data_obj.browse(cr, uid, model_data_id, context=context).res_id button_name = _('Send Mail (%s)') % template.name - vals['ref_ir_act_window'] = action_obj.create(cr, uid, { + vals['ref_ir_act_window'] = action_obj.create(cr, SUPERUSER_ID, { 'name': button_name, 'type': 'ir.actions.act_window', 'res_model': 'mail.compose.message', @@ -211,7 +212,7 @@ class email_template(osv.osv): 'target': 'new', 'auto_refresh':1 }, context) - vals['ref_ir_value'] = self.pool.get('ir.values').create(cr, uid, { + vals['ref_ir_value'] = self.pool.get('ir.values').create(cr, SUPERUSER_ID, { 'name': button_name, 'model': src_obj, 'key2': 'client_action_multi', From 72f6a8c646179d433de11c2f26e5f589b5ad8cda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Thu, 6 Jun 2013 14:37:24 +0200 Subject: [PATCH 06/16] [FIX] mail.compose.message: supports user_signature of template. Note that in case of multiple wizards (send on ids that is not a singleton list), user_signature on one template is valid for all wizard. bzr revid: tde@openerp.com-20130606123724-f7q3mwm81ek7mr5c --- addons/email_template/wizard/mail_compose_message.py | 10 +++++++--- addons/mail/mail_followers.py | 10 +++++++--- addons/mail/mail_message.py | 9 ++++++--- addons/mail/wizard/mail_compose_message.py | 2 +- 4 files changed, 21 insertions(+), 10 deletions(-) diff --git a/addons/email_template/wizard/mail_compose_message.py b/addons/email_template/wizard/mail_compose_message.py index 7f44828ce15..6fbc541341b 100644 --- a/addons/email_template/wizard/mail_compose_message.py +++ b/addons/email_template/wizard/mail_compose_message.py @@ -54,18 +54,22 @@ class mail_compose_message(osv.TransientModel): Indeed, basic mail.compose.message wizard duplicates attachments in mass mailing mode. But in 'single post' mode, attachments of an email template also have to be duplicated to avoid changing their ownership. """ + if context is None: + context = {} + wizard_context = dict(context) for wizard in self.browse(cr, uid, ids, context=context): + if wizard.template_id and not wizard.template_id.user_signature: + wizard_context['mail_notify_user_signature'] = False # template user_signature is added when generating body_html if not wizard.attachment_ids or wizard.composition_mode == 'mass_mail' or not wizard.template_id: continue - template = self.pool.get('email.template').browse(cr, uid, wizard.template_id.id, context=context) new_attachment_ids = [] for attachment in wizard.attachment_ids: - if attachment in template.attachment_ids: + if attachment in wizard.template.attachment_ids: new_attachment_ids.append(self.pool.get('ir.attachment').copy(cr, uid, attachment.id, {'res_model': 'mail.compose.message', 'res_id': wizard.id}, context=context)) else: new_attachment_ids.append(attachment.id) self.write(cr, uid, wizard.id, {'attachment_ids': [(6, 0, new_attachment_ids)]}, context=context) - return super(mail_compose_message, self).send_mail(cr, uid, ids, context=context) + return super(mail_compose_message, self).send_mail(cr, uid, ids, context=wizard_context) def onchange_template_id(self, cr, uid, ids, template_id, composition_mode, model, res_id, context=None): """ - mass_mailing: we cannot render, so return the template values diff --git a/addons/mail/mail_followers.py b/addons/mail/mail_followers.py index 93196976e90..bc78c8c61d7 100644 --- a/addons/mail/mail_followers.py +++ b/addons/mail/mail_followers.py @@ -150,7 +150,8 @@ class mail_notification(osv.Model): return footer - def _notify(self, cr, uid, msg_id, partners_to_notify=None, context=None, force_send=False): + def _notify(self, cr, uid, msg_id, partners_to_notify=None, context=None, + force_send=False, user_signature=True): """ Send by email the notification depending on the user preferences :param list partners_to_notify: optional list of partner ids restricting @@ -158,6 +159,8 @@ class mail_notification(osv.Model): :param bool force_send: if True, the generated mail.mail is immediately sent after being created, as if the scheduler was executed for this message only. + :param bool user_signature: if True, the generated mail.mail body is + the body of the related mail.message with the author's signature """ if context is None: context = {} @@ -192,8 +195,9 @@ class mail_notification(osv.Model): # add signature body_html = msg.body user_id = msg.author_id and msg.author_id.user_ids and msg.author_id.user_ids[0] and msg.author_id.user_ids[0].id or None - signature_company = self.get_signature_footer(cr, uid, user_id, res_model=msg.model, res_id=msg.res_id, context=context) - body_html = tools.append_content_to_html(body_html, signature_company, plaintext=False, container_tag='div') + if user_signature: + signature_company = self.get_signature_footer(cr, uid, user_id, res_model=msg.model, res_id=msg.res_id, context=context) + body_html = tools.append_content_to_html(body_html, signature_company, plaintext=False, container_tag='div') references = False if msg.parent_id: diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py index 884d59ba9be..4a22684c36e 100644 --- a/addons/mail/mail_message.py +++ b/addons/mail/mail_message.py @@ -768,7 +768,9 @@ class mail_message(osv.Model): elif not values.get('message_id'): values['message_id'] = tools.generate_tracking_message_id('private') newid = super(mail_message, self).create(cr, uid, values, context) - self._notify(cr, uid, newid, context=context, force_send=context.get('mail_notify_force_send', True)) + self._notify(cr, uid, newid, context=context, + force_send=context.get('mail_notify_force_send', True), + user_signature=context.get('mail_notify_user_signature', True)) # TDE FIXME: handle default_starred. Why not setting an inv on starred ? # Because starred will call set_message_starred, that looks for notifications. # When creating a new mail_message, it will create a notification to a message @@ -882,7 +884,7 @@ class mail_message(osv.Model): return '' return result - def _notify(self, cr, uid, newid, context=None, force_send=False): + def _notify(self, cr, uid, newid, context=None, force_send=False, user_signature=True): """ Add the related record followers to the destination partner_ids if is not a private message. Call mail_notification.notify to manage the email sending """ @@ -912,7 +914,8 @@ class mail_message(osv.Model): # notify if partners_to_notify: - notification_obj._notify(cr, uid, newid, partners_to_notify=[p.id for p in partners_to_notify], context=context, force_send=force_send) + notification_obj._notify(cr, uid, newid, partners_to_notify=[p.id for p in partners_to_notify], context=context, + force_send=force_send, user_signature=user_signature) message.refresh() # An error appear when a user receive a notification without notifying diff --git a/addons/mail/wizard/mail_compose_message.py b/addons/mail/wizard/mail_compose_message.py index e3383270548..64886628478 100644 --- a/addons/mail/wizard/mail_compose_message.py +++ b/addons/mail/wizard/mail_compose_message.py @@ -157,7 +157,7 @@ class mail_compose_message(osv.TransientModel): return super(mail_compose_message, self).check_access_rule(cr, uid, ids, operation, context=context) - def _notify(self, cr, uid, newid, context=None, force_send=False): + def _notify(self, cr, uid, newid, context=None, force_send=False, user_signature=True): """ Override specific notify method of mail.message, because we do not want that feature in the wizard. """ return From 62a03693a39a59bf1833dbed0e4aff32ee5ca8c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Thu, 6 Jun 2013 15:08:51 +0200 Subject: [PATCH 07/16] [FIX] mail.compose.message in email_template: var typo bzr revid: tde@openerp.com-20130606130851-h5jswklvvz98enue --- addons/email_template/wizard/mail_compose_message.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/email_template/wizard/mail_compose_message.py b/addons/email_template/wizard/mail_compose_message.py index 6fbc541341b..746c2cc55f6 100644 --- a/addons/email_template/wizard/mail_compose_message.py +++ b/addons/email_template/wizard/mail_compose_message.py @@ -64,7 +64,7 @@ class mail_compose_message(osv.TransientModel): continue new_attachment_ids = [] for attachment in wizard.attachment_ids: - if attachment in wizard.template.attachment_ids: + if attachment in wizard.template_id.attachment_ids: new_attachment_ids.append(self.pool.get('ir.attachment').copy(cr, uid, attachment.id, {'res_model': 'mail.compose.message', 'res_id': wizard.id}, context=context)) else: new_attachment_ids.append(attachment.id) From 05d463a0e2172095589b6601548fcaaf4e5999be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Thu, 6 Jun 2013 16:27:20 +0200 Subject: [PATCH 08/16] [FIX] mail_mail: sdearch view: no need to have default_type_email bzr revid: tde@openerp.com-20130606142720-ghs664yqhgemd132 --- addons/mail/mail_mail_view.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/mail/mail_mail_view.xml b/addons/mail/mail_mail_view.xml index 1ff9a2a292e..e8e544791c8 100644 --- a/addons/mail/mail_mail_view.xml +++ b/addons/mail/mail_mail_view.xml @@ -110,7 +110,7 @@ mail.mail form tree,form - {'search_default_outgoing': 1, 'search_default_type_email': 1} + {'search_default_outgoing': 1} From a1e942c0dec4d016655f12031c9a78336b2d83d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Thu, 6 Jun 2013 16:28:14 +0200 Subject: [PATCH 09/16] [FIX] mail.compose.message: fixed reply_to from template in mass mailing. bzr revid: tde@openerp.com-20130606142814-jyw76nhaeivotu6k --- addons/mail/mail_mail.py | 10 +++++++--- addons/mail/mail_message.py | 3 --- addons/mail/wizard/mail_compose_message.py | 4 +++- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/addons/mail/mail_mail.py b/addons/mail/mail_mail.py index 4da3b27bd7d..4cf319a0cc4 100644 --- a/addons/mail/mail_mail.py +++ b/addons/mail/mail_mail.py @@ -66,6 +66,9 @@ class mail_mail(osv.Model): } def _get_default_from(self, cr, uid, context=None): + """ Kept for compatibility + TDE TODO: remove me in 8.0 + """ return self.pool['mail.message']._get_default_from(cr, uid, context=context) _defaults = { @@ -85,14 +88,15 @@ class mail_mail(osv.Model): """ if values.get('reply_to'): return values.get('reply_to') - email_reply_to = False - # model, res_id: comes from values OR related message + # email_reply_to, model, res_id: comes from values OR related message + email_reply_to = False model = values.get('model') res_id = values.get('res_id') email_from = values.get('email_from') if values.get('mail_message_id') and (not model or not res_id): message = self.pool.get('mail.message').browse(cr, uid, values.get('mail_message_id'), context=context) + email_reply_to = message.reply_to if not model: model = message.model if not res_id: @@ -101,7 +105,7 @@ class mail_mail(osv.Model): email_from = message.email_from # if model and res_id: try to use ``message_get_reply_to`` that returns the document alias - if model and res_id and hasattr(self.pool[model], 'message_get_reply_to'): + if not email_reply_to and model and res_id and hasattr(self.pool[model], 'message_get_reply_to'): email_reply_to = self.pool[model].message_get_reply_to(cr, uid, [res_id], context=context)[0] # no alias reply_to -> reply_to will be the email_from, only the email part if not email_reply_to and email_from: diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py index 4a22684c36e..a76807bb5e8 100644 --- a/addons/mail/mail_message.py +++ b/addons/mail/mail_message.py @@ -352,9 +352,6 @@ class mail_message(osv.Model): else: partner_ids = [partner_tree[partner.id] for partner in message.partner_ids if partner.id in partner_tree] - # for partner in message.notified_partner_ids: - # if partner.id in partner_tree: - # partner_ids.append(partner_tree[partner.id]) attachment_ids = [] for attachment in message.attachment_ids: if attachment.id in attachments_tree: diff --git a/addons/mail/wizard/mail_compose_message.py b/addons/mail/wizard/mail_compose_message.py index 64886628478..7685a27f1ed 100644 --- a/addons/mail/wizard/mail_compose_message.py +++ b/addons/mail/wizard/mail_compose_message.py @@ -259,13 +259,15 @@ class mail_compose_message(osv.TransientModel): new_attach_id = ir_attachment_obj.copy(cr, uid, attach_id, {'res_model': self._name, 'res_id': wizard.id}, context=context) attachment_ids.append(new_attach_id) post_values['attachment_ids'] = attachment_ids - post_values.update(email_dict) # email_from: mass mailing only can specify another email_from if email_dict.get('email_from'): post_values['email_from'] = email_dict.pop('email_from') # replies redirection: mass mailing only if not wizard.same_thread: post_values['reply_to'] = email_dict.pop('reply_to') + else: + email_dict.pop('reply_to') + post_values.update(email_dict) # clean the context (hint: mass mailing sets some default values that # could be wrongly interpreted by mail_mail) context.pop('default_email_to', None) From 1ef65f15772bda4a82bd069b87bd1c782f09278e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Thu, 6 Jun 2013 17:14:40 +0200 Subject: [PATCH 10/16] [FIX] mail_mail: reply_to: use tools.email_split to avoid issues when handling email addresses. bzr revid: tde@openerp.com-20130606151440-atdhk834ny248xqq --- addons/mail/mail_mail.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/mail/mail_mail.py b/addons/mail/mail_mail.py index 4cf319a0cc4..34278079fb5 100644 --- a/addons/mail/mail_mail.py +++ b/addons/mail/mail_mail.py @@ -96,7 +96,7 @@ class mail_mail(osv.Model): email_from = values.get('email_from') if values.get('mail_message_id') and (not model or not res_id): message = self.pool.get('mail.message').browse(cr, uid, values.get('mail_message_id'), context=context) - email_reply_to = message.reply_to + email_reply_to = tools.email_split(message.reply_to) if not model: model = message.model if not res_id: From 30a77204524c964f28626207772552115861bbb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Thu, 6 Jun 2013 17:25:38 +0200 Subject: [PATCH 11/16] [IMP] mail_message: added reply_to to the form view of mail.message bzr revid: tde@openerp.com-20130606152538-4u3s1k4vvkotxz6c --- addons/mail/mail_mail.py | 3 ++- addons/mail/mail_message_view.xml | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/addons/mail/mail_mail.py b/addons/mail/mail_mail.py index 34278079fb5..2b0bb6a6959 100644 --- a/addons/mail/mail_mail.py +++ b/addons/mail/mail_mail.py @@ -96,7 +96,8 @@ class mail_mail(osv.Model): email_from = values.get('email_from') if values.get('mail_message_id') and (not model or not res_id): message = self.pool.get('mail.message').browse(cr, uid, values.get('mail_message_id'), context=context) - email_reply_to = tools.email_split(message.reply_to) + if message.reply_to: + email_reply_to = tools.email_split(message.reply_to)[0] if not model: model = message.model if not res_id: diff --git a/addons/mail/mail_message_view.xml b/addons/mail/mail_message_view.xml index 8f2fb3f1020..abe381c9ceb 100644 --- a/addons/mail/mail_message_view.xml +++ b/addons/mail/mail_message_view.xml @@ -30,9 +30,9 @@ + - @@ -40,6 +40,7 @@ + From 216cb2122e8e3b5784e666ad2b06d60c3fc72751 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Thu, 6 Jun 2013 17:28:14 +0200 Subject: [PATCH 12/16] [IMP] mail: mass_mailing: avoid having 'Followers of' when having a specified reply_to coming from the mail_message bzr revid: tde@openerp.com-20130606152814-iqftppk06s7g8weg --- addons/mail/mail_mail.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/addons/mail/mail_mail.py b/addons/mail/mail_mail.py index 2b0bb6a6959..62c1a9fff19 100644 --- a/addons/mail/mail_mail.py +++ b/addons/mail/mail_mail.py @@ -88,6 +88,7 @@ class mail_mail(osv.Model): """ if values.get('reply_to'): return values.get('reply_to') + redirect_gateway = True # whether replies will be redirected inside the document # email_reply_to, model, res_id: comes from values OR related message email_reply_to = False @@ -97,7 +98,8 @@ class mail_mail(osv.Model): if values.get('mail_message_id') and (not model or not res_id): message = self.pool.get('mail.message').browse(cr, uid, values.get('mail_message_id'), context=context) if message.reply_to: - email_reply_to = tools.email_split(message.reply_to)[0] + email_reply_to = message.reply_to + redirect_gateway = False if not model: model = message.model if not res_id: @@ -115,7 +117,7 @@ class mail_mail(osv.Model): email_reply_to = emails[0] # format 'Document name ' - if email_reply_to and model and res_id: + if email_reply_to and model and res_id and redirect_gateway: document_name = self.pool[model].name_get(cr, SUPERUSER_ID, [res_id], context=context)[0] if document_name: # sanitize document name From a1a66985584a5d8db49d5bdd2c5b7199ee0a0b28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Fri, 7 Jun 2013 11:16:11 +0200 Subject: [PATCH 13/16] [CLEAN] mail: cleaned reply_to code, finally. bzr revid: tde@openerp.com-20130607091611-7cpzlxzngkrs86a0 --- addons/mail/mail_mail.py | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/addons/mail/mail_mail.py b/addons/mail/mail_mail.py index 62c1a9fff19..1398e00255b 100644 --- a/addons/mail/mail_mail.py +++ b/addons/mail/mail_mail.py @@ -88,18 +88,16 @@ class mail_mail(osv.Model): """ if values.get('reply_to'): return values.get('reply_to') - redirect_gateway = True # whether replies will be redirected inside the document - - # email_reply_to, model, res_id: comes from values OR related message + format_name = True # whether to use a 'Followers of Pigs reply_to will be the email_from, only the email part + # no alias reply_to -> reply_to will be the email_from if not email_reply_to and email_from: - emails = tools.email_split(email_from) - if emails: - email_reply_to = emails[0] + email_reply_to = email_from # format 'Document name ' - if email_reply_to and model and res_id and redirect_gateway: + if email_reply_to and model and res_id and format_name: + emails = tools.email_split(email_reply_to) + if emails: + email_reply_to = emails[0] document_name = self.pool[model].name_get(cr, SUPERUSER_ID, [res_id], context=context)[0] if document_name: # sanitize document name From 636c793c70c6504fd6305a7d054ce6554bd613dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Fri, 7 Jun 2013 11:16:19 +0200 Subject: [PATCH 14/16] [TESTS] mail: added tests bzr revid: tde@openerp.com-20130607091619-tfh78a1agqdiqc23 --- addons/mail/tests/test_mail_base.py | 1 + addons/mail/tests/test_mail_gateway.py | 97 ++++++++++++++++++++++++++ 2 files changed, 98 insertions(+) diff --git a/addons/mail/tests/test_mail_base.py b/addons/mail/tests/test_mail_base.py index a3b90288f0b..7ca8b93135c 100644 --- a/addons/mail/tests/test_mail_base.py +++ b/addons/mail/tests/test_mail_base.py @@ -69,6 +69,7 @@ class TestMailBase(common.TransactionCase): self.group_employee_id = group_employee_ref and group_employee_ref[1] or False # Test users to use through the various tests + self.res_users.write(cr, uid, uid, {'name': 'Administrator'}) self.user_raoul_id = self.res_users.create(cr, uid, {'name': 'Raoul Grosbedon', 'signature': 'SignRaoul', 'email': 'raoul@raoul.fr', 'login': 'raoul', 'groups_id': [(6, 0, [self.group_employee_id])]}) self.user_bert_id = self.res_users.create(cr, uid, diff --git a/addons/mail/tests/test_mail_gateway.py b/addons/mail/tests/test_mail_gateway.py index e4269d59d8b..51d6d8add30 100644 --- a/addons/mail/tests/test_mail_gateway.py +++ b/addons/mail/tests/test_mail_gateway.py @@ -20,6 +20,7 @@ ############################################################################## from openerp.addons.mail.tests.test_mail_base import TestMailBase +from openerp.tools import mute_logger MAIL_TEMPLATE = """Return-Path: To: {to} @@ -123,6 +124,102 @@ class TestMailgateway(TestMailBase): self.assertEqual(partner_info['partner_id'], p_b_id, 'mail_thread: message_find_partner_from_emails wrong partner found') + def test_05_mail_message_mail_mail(self): + """ Tests designed for testing email values based on mail.message, aliases, ... """ + cr, uid, user_raoul_id = self.cr, self.uid, self.user_raoul_id + + # Data: update + generic variables + reply_to1 = '_reply_to1@example.com' + reply_to2 = '_reply_to2@example.com' + email_from1 = 'from@example.com' + alias_domain = 'schlouby.fr' + raoul_from = 'Raoul Grosbedon ' + raoul_from_alias = 'Raoul Grosbedon ' + raoul_reply = '"Followers of Pigs" ' + raoul_reply_alias = '"Followers of Pigs" ' + # Data: remove alias_domain to see emails with alias + param_ids = self.registry('ir.config_parameter').search(cr, uid, [('key', '=', 'mail.catchall.domain')]) + self.registry('ir.config_parameter').unlink(cr, uid, param_ids) + + # Do: free message; specified values > default values + msg_id = self.mail_message.create(cr, user_raoul_id, {'reply_to': reply_to1, 'email_from': email_from1}) + msg = self.mail_message.browse(cr, user_raoul_id, msg_id) + # Test: message content + self.assertIn('reply_to', msg.message_id, + 'mail_message: message_id should be specific to a mail_message with a given reply_to') + self.assertEqual(msg.reply_to, reply_to1, + 'mail_message: incorrect reply_to: should come from values') + self.assertEqual(msg.email_from, email_from1, + 'mail_message: incorrect email_from: should come from values') + # Do: create a mail_mail with the previous mail_message + mail_id = self.mail_mail.create(cr, user_raoul_id, {'mail_message_id': msg_id, 'state': 'cancel'}) + mail = self.mail_mail.browse(cr, user_raoul_id, mail_id) + # Test: mail_mail content + self.assertEqual(mail.reply_to, reply_to1, + 'mail_mail: incorrect reply_to: should come from mail.message') + self.assertEqual(mail.email_from, email_from1, + 'mail_mail: incorrect email_from: should come from mail.message') + # Do: create a mail_mail with the previous mail_message + specified reply_to + mail_id = self.mail_mail.create(cr, user_raoul_id, {'mail_message_id': msg_id, 'state': 'cancel', 'reply_to': reply_to2}) + mail = self.mail_mail.browse(cr, user_raoul_id, mail_id) + # Test: mail_mail content + self.assertEqual(mail.reply_to, reply_to2, + 'mail_mail: incorrect reply_to: should come from values') + self.assertEqual(mail.email_from, email_from1, + 'mail_mail: incorrect email_from: should come from mail.message') + + # Do: mail_message attached to a document + msg_id = self.mail_message.create(cr, user_raoul_id, {'model': 'mail.group', 'res_id': self.group_pigs_id}) + msg = self.mail_message.browse(cr, user_raoul_id, msg_id) + # Test: message content + self.assertIn('mail.group', msg.message_id, + 'mail_message: message_id should contain model') + self.assertIn('%s' % self.group_pigs_id, msg.message_id, + 'mail_message: message_id should contain res_id') + self.assertFalse(msg.reply_to, + 'mail_message: incorrect reply_to: should not be generated if not specified') + self.assertEqual(msg.email_from, raoul_from, + 'mail_message: incorrect email_from: should be Raoul') + # Do: create a mail_mail based on the previous mail_message + mail_id = self.mail_mail.create(cr, user_raoul_id, {'mail_message_id': msg_id, 'state': 'cancel'}) + mail = self.mail_mail.browse(cr, user_raoul_id, mail_id) + # Test: mail_mail content + self.assertEqual(mail.reply_to, raoul_reply, + 'mail_mail: incorrect reply_to: should be Raoul') + + # Data: set catchall domain + self.registry('ir.config_parameter').set_param(cr, uid, 'mail.catchall.domain', alias_domain) + + # Update message + self.mail_message.write(cr, user_raoul_id, [msg_id], {'email_from': False, 'reply_to': False}) + msg.refresh() + # Do: create a mail_mail based on the previous mail_message + mail_id = self.mail_mail.create(cr, user_raoul_id, {'mail_message_id': msg_id, 'state': 'cancel'}) + mail = self.mail_mail.browse(cr, user_raoul_id, mail_id) + # Test: mail_mail content + self.assertEqual(mail.reply_to, raoul_reply_alias, + 'mail_mail: incorrect reply_to: should be Pigs alias') + + # Update message: test alias on email_from + msg_id = self.mail_message.create(cr, user_raoul_id, {}) + msg = self.mail_message.browse(cr, user_raoul_id, msg_id) + # Do: create a mail_mail based on the previous mail_message + mail_id = self.mail_mail.create(cr, user_raoul_id, {'mail_message_id': msg_id, 'state': 'cancel'}) + mail = self.mail_mail.browse(cr, user_raoul_id, mail_id) + # Test: mail_mail content + self.assertEqual(mail.reply_to, raoul_from_alias, + 'mail_mail: incorrect reply_to: should be message email_from using Raoul alias') + + # Update message + self.mail_message.write(cr, user_raoul_id, [msg_id], {'res_id': False, 'email_from': 'someone@schlouby.fr', 'reply_to': False}) + msg.refresh() + # Do: create a mail_mail based on the previous mail_message + mail_id = self.mail_mail.create(cr, user_raoul_id, {'mail_message_id': msg_id, 'state': 'cancel'}) + mail = self.mail_mail.browse(cr, user_raoul_id, mail_id) + # Test: mail_mail content + self.assertEqual(mail.reply_to, msg.email_from, + 'mail_mail: incorrect reply_to: should be message email_from') + def test_10_message_process(self): """ Testing incoming emails processing. """ cr, uid, user_raoul = self.cr, self.uid, self.user_raoul From c59fe603efc331567a9f1ea0b33842379bfb276e Mon Sep 17 00:00:00 2001 From: "dle@openerp.com" <> Date: Mon, 10 Jun 2013 14:45:49 +0200 Subject: [PATCH 15/16] [FIX]l10n_be: partner vat intra report rml bzr revid: dle@openerp.com-20130610124549-kfk5wnl34ufd90q2 --- addons/l10n_be/wizard/l10n_be_vat_intra_print.rml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/l10n_be/wizard/l10n_be_vat_intra_print.rml b/addons/l10n_be/wizard/l10n_be_vat_intra_print.rml index ff1a2c2064d..a25f01ff348 100644 --- a/addons/l10n_be/wizard/l10n_be_vat_intra_print.rml +++ b/addons/l10n_be/wizard/l10n_be_vat_intra_print.rml @@ -105,7 +105,7 @@ [[ l['vat'] ]] - [[ l['code'] (l['intra_code']) ]] + [[ l['code'] ]]([[ l['intra_code'] ]]) [[ formatLang(l['amount'], currency_obj=company.currency_id) ]] From 02aa3f211471a55fd956027a84b6baca1272ffa1 Mon Sep 17 00:00:00 2001 From: niv-openerp Date: Mon, 10 Jun 2013 17:41:39 +0200 Subject: [PATCH 16/16] [FIX] regression that made the server crash when we install a db bzr revid: nicolas.vanhoren@openerp.com-20130610154139-jylxm135apnu3app --- openerp/addons/base/security/base_security.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openerp/addons/base/security/base_security.xml b/openerp/addons/base/security/base_security.xml index 8895268814f..4bd78e353cf 100644 --- a/openerp/addons/base/security/base_security.xml +++ b/openerp/addons/base/security/base_security.xml @@ -89,7 +89,7 @@ multi-company currency rule - + ['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])]