diff --git a/addons/hr_payroll/models/hr_payroll.py b/addons/hr_payroll/models/hr_payroll.py
index becb958c771..09719395543 100644
--- a/addons/hr_payroll/models/hr_payroll.py
+++ b/addons/hr_payroll/models/hr_payroll.py
@@ -244,6 +244,7 @@ class HrPayslip(models.Model):
def refund_sheet(self):
for payslip in self:
copied_payslip = payslip.copy({'credit_note': True, 'name': _('Refund: ') + payslip.name})
+ copied_payslip.compute_sheet()
copied_payslip.action_payslip_done()
formview_ref = self.env.ref('hr_payroll.view_hr_payslip_form', False)
treeview_ref = self.env.ref('hr_payroll.view_hr_payslip_tree', False)
diff --git a/addons/mail/models/mail_message.py b/addons/mail/models/mail_message.py
index bf71454dfbf..3716cd4e6f3 100644
--- a/addons/mail/models/mail_message.py
+++ b/addons/mail/models/mail_message.py
@@ -147,7 +147,7 @@ class Message(models.Model):
given, restrict to messages written in one of those channels. """
partner_id = self.env.user.partner_id.id
delete_mode = not self.env.user.share # delete employee notifs, keep customer ones
- if domain is None and delete_mode:
+ if not domain and delete_mode:
query = "DELETE FROM mail_message_res_partner_needaction_rel WHERE res_partner_id IN %s"
args = [(partner_id,)]
if channel_ids:
@@ -465,6 +465,7 @@ class Message(models.Model):
- if author_id == pid, uid is the author, OR
- uid belongs to a notified channel, OR
- uid is in the specified recipients, OR
+ - uid has a notification on the message, OR
- uid have read access to the related document is model, res_id
- otherwise: remove the id
"""
@@ -492,17 +493,22 @@ class Message(models.Model):
# check read access rights before checking the actual rules on the given ids
super(Message, self.sudo(access_rights_uid or self._uid)).check_access_rights('read')
- self._cr.execute("""SELECT DISTINCT m.id, m.model, m.res_id, m.author_id, partner_rel.res_partner_id, channel_partner.channel_id as channel_id
+ self._cr.execute("""
+ SELECT DISTINCT m.id, m.model, m.res_id, m.author_id,
+ COALESCE(partner_rel.res_partner_id, needaction_rel.res_partner_id),
+ channel_partner.channel_id as channel_id
FROM "%s" m
LEFT JOIN "mail_message_res_partner_rel" partner_rel
- ON partner_rel.mail_message_id = m.id AND partner_rel.res_partner_id = (%%s)
+ ON partner_rel.mail_message_id = m.id AND partner_rel.res_partner_id = %%(pid)s
+ LEFT JOIN "mail_message_res_partner_needaction_rel" needaction_rel
+ ON needaction_rel.mail_message_id = m.id AND needaction_rel.res_partner_id = %%(pid)s
LEFT JOIN "mail_message_mail_channel_rel" channel_rel
ON channel_rel.mail_message_id = m.id
LEFT JOIN "mail_channel" channel
ON channel.id = channel_rel.mail_channel_id
LEFT JOIN "mail_channel_partner" channel_partner
- ON channel_partner.channel_id = channel.id AND channel_partner.partner_id = (%%s)
- WHERE m.id = ANY (%%s)""" % self._table, (pid, pid, ids,))
+ ON channel_partner.channel_id = channel.id AND channel_partner.partner_id = %%(pid)s
+ WHERE m.id = ANY (%%(ids)s)""" % self._table, dict(pid=pid, ids=ids))
for id, rmod, rid, author_id, partner_id, channel_id in self._cr.fetchall():
if author_id == pid:
author_ids.add(id)
@@ -530,6 +536,7 @@ class Message(models.Model):
- read: if
- author_id == pid, uid is the author OR
- uid is in the recipients (partner_ids) OR
+ - uid has been notified (needaction) OR
- uid is member of a listern channel (channel_ids.partner_ids) OR
- uid have read access to the related document if model, res_id
- otherwise: raise
@@ -580,17 +587,22 @@ class Message(models.Model):
message_values = dict((res_id, {}) for res_id in self.ids)
if operation in ['read', 'write']:
- self._cr.execute("""SELECT DISTINCT m.id, m.model, m.res_id, m.author_id, m.parent_id, partner_rel.res_partner_id, channel_partner.channel_id as channel_id
+ self._cr.execute("""
+ SELECT DISTINCT m.id, m.model, m.res_id, m.author_id, m.parent_id,
+ COALESCE(partner_rel.res_partner_id, needaction_rel.res_partner_id),
+ channel_partner.channel_id as channel_id
FROM "%s" m
LEFT JOIN "mail_message_res_partner_rel" partner_rel
- ON partner_rel.mail_message_id = m.id AND partner_rel.res_partner_id = (%%s)
+ ON partner_rel.mail_message_id = m.id AND partner_rel.res_partner_id = %%(pid)s
+ LEFT JOIN "mail_message_res_partner_needaction_rel" needaction_rel
+ ON needaction_rel.mail_message_id = m.id AND needaction_rel.res_partner_id = %%(pid)s
LEFT JOIN "mail_message_mail_channel_rel" channel_rel
ON channel_rel.mail_message_id = m.id
LEFT JOIN "mail_channel" channel
ON channel.id = channel_rel.mail_channel_id
LEFT JOIN "mail_channel_partner" channel_partner
- ON channel_partner.channel_id = channel.id AND channel_partner.partner_id = (%%s)
- WHERE m.id = ANY (%%s)""" % self._table, (self.env.user.partner_id.id, self.env.user.partner_id.id, self.ids,))
+ ON channel_partner.channel_id = channel.id AND channel_partner.partner_id = %%(pid)s
+ WHERE m.id = ANY (%%(ids)s)""" % self._table, dict(pid=self.env.user.partner_id.id, ids=self.ids))
for mid, rmod, rid, author_id, parent_id, partner_id, channel_id in self._cr.fetchall():
message_values[mid] = {
'model': rmod,
diff --git a/addons/mail/tests/test_mail_features.py b/addons/mail/tests/test_mail_features.py
index 729f2df07b6..c1461eea3cd 100644
--- a/addons/mail/tests/test_mail_features.py
+++ b/addons/mail/tests/test_mail_features.py
@@ -35,6 +35,71 @@ class TestMailFeatures(TestMail):
self.assertEqual(na_emp1_new, na_emp1_base + 1)
self.assertEqual(na_emp2_new, na_emp2_base)
+ @mute_logger('openerp.addons.mail.models.mail_mail')
+ def test_mark_all_as_read(self):
+ emp_partner = self.user_employee.partner_id.sudo(self.user_employee.id)
+
+ group_private = self.env['mail.channel'].with_context({
+ 'mail_create_nolog': True,
+ 'mail_create_nosubscribe': True,
+ 'mail_channel_noautofollow': True,
+ }).create({
+ 'name': 'Private',
+ 'description': 'Private James R.',
+ 'public': 'private',
+ 'alias_name': 'private',
+ 'alias_contact': 'followers'}
+ ).with_context({'mail_create_nosubscribe': False})
+
+ # mark all as read clear needactions
+ group_private.message_post(body='Test', message_type='comment', subtype='mail.mt_comment', partner_ids=[emp_partner.id])
+ emp_partner.env['mail.message'].mark_all_as_read(channel_ids=[], domain=[])
+ na_count = emp_partner.get_needaction_count()
+ self.assertEqual(na_count, 0, "mark all as read should conclude all needactions")
+
+ # mark all as read also clear inaccessible needactions
+ new_msg = group_private.message_post(body='Zest', message_type='comment', subtype='mail.mt_comment', partner_ids=[emp_partner.id])
+ needaction_accessible = len(emp_partner.env['mail.message'].search([['needaction', '=', True]]))
+ self.assertEqual(needaction_accessible, 1, "a new message to a partner is readable to that partner")
+
+ new_msg.sudo().partner_ids = self.env['res.partner']
+ emp_partner.env['mail.message'].search([['needaction', '=', True]])
+ needaction_length = len(emp_partner.env['mail.message'].search([['needaction', '=', True]]))
+ self.assertEqual(needaction_length, 1, "message should still be readable when notified")
+
+ na_count = emp_partner.get_needaction_count()
+ self.assertEqual(na_count, 1, "message not accessible is currently still counted")
+
+ emp_partner.env['mail.message'].mark_all_as_read(channel_ids=[], domain=[])
+ na_count = emp_partner.get_needaction_count()
+ self.assertEqual(na_count, 0, "mark all read should conclude all needactions even inacessible ones")
+
+ @mute_logger('openerp.addons.mail.models.mail_mail')
+ def test_mark_all_as_read_share(self):
+ portal_partner = self.user_portal.partner_id.sudo(self.user_portal.id)
+
+ # mark all as read clear needactions
+ self.group_pigs.message_post(body='Test', message_type='comment', subtype='mail.mt_comment', partner_ids=[portal_partner.id])
+ portal_partner.env['mail.message'].mark_all_as_read(channel_ids=[], domain=[])
+ na_count = portal_partner.get_needaction_count()
+ self.assertEqual(na_count, 0, "mark all as read should conclude all needactions")
+
+ # mark all as read also clear inaccessible needactions
+ new_msg = self.group_pigs.message_post(body='Zest', message_type='comment', subtype='mail.mt_comment', partner_ids=[portal_partner.id])
+ needaction_accessible = len(portal_partner.env['mail.message'].search([['needaction', '=', True]]))
+ self.assertEqual(needaction_accessible, 1, "a new message to a partner is readable to that partner")
+
+ new_msg.sudo().partner_ids = self.env['res.partner']
+ needaction_length = len(portal_partner.env['mail.message'].search([['needaction', '=', True]]))
+ self.assertEqual(needaction_length, 1, "message should still be readable when notified")
+
+ na_count = portal_partner.get_needaction_count()
+ self.assertEqual(na_count, 1, "message not accessible is currently still counted")
+
+ portal_partner.env['mail.message'].mark_all_as_read(channel_ids=[], domain=[])
+ na_count = portal_partner.get_needaction_count()
+ self.assertEqual(na_count, 0, "mark all read should conclude all needactions even inacessible ones")
+
class TestMessagePost(TestMail):
diff --git a/addons/maintenance/models/maintenance.py b/addons/maintenance/models/maintenance.py
index c2322ae4690..fe808cdcf31 100644
--- a/addons/maintenance/models/maintenance.py
+++ b/addons/maintenance/models/maintenance.py
@@ -213,6 +213,15 @@ class MaintenanceEquipment(models.Model):
self.message_subscribe_users(user_ids=[vals['owner_user_id']])
return super(MaintenanceEquipment, self).write(vals)
+ @api.model
+ def _message_get_auto_subscribe_fields(self, updated_fields, auto_follow_fields=None):
+ """ mail.thread override so user_id which has no special access allowance is not
+ automatically subscribed.
+ """
+ if auto_follow_fields is None:
+ auto_follow_fields = []
+ return super(MaintenanceEquipment, self)._message_get_auto_subscribe_fields(updated_fields, auto_follow_fields)
+
@api.model
def _read_group_category_ids(self, categories, domain, order):
""" Read group customization in order to display all the categories in
diff --git a/addons/website_form/static/src/js/website_form.js b/addons/website_form/static/src/js/website_form.js
index ff59b6186b9..7a8b413a04b 100644
--- a/addons/website_form/static/src/js/website_form.js
+++ b/addons/website_form/static/src/js/website_form.js
@@ -15,7 +15,7 @@ odoo.define('website_form.animation', function (require) {
start: function() {
var self = this;
- qweb.add_template('/website_form/static/src/xml/website_form.xml');
+ this.templates_loaded = ajax.loadXML('/website_form/static/src/xml/website_form.xml', qweb);
this.$target.find('.o_website_form_send').on('click',function(e) {self.send(e);});
// Initialize datetimepickers
@@ -218,7 +218,10 @@ odoo.define('website_form.animation', function (require) {
if (status != 'success') { // Restore send button behavior if result is an error
this.$target.find('.o_website_form_send').on('click',function(e) {self.send(e);});
}
- this.$target.find('#o_website_form_result').replaceWith(qweb.render("website_form.status_" + status))
+ var $result = this.$('#o_website_form_result');
+ this.templates_loaded.done(function () {
+ $result.replaceWith(qweb.render("website_form.status_" + status));
+ });
},
});
});
diff --git a/addons/website_sale/views/templates.xml b/addons/website_sale/views/templates.xml
index 7442e64653d..0e14f1a341e 100644
--- a/addons/website_sale/views/templates.xml
+++ b/addons/website_sale/views/templates.xml
@@ -726,6 +726,7 @@
+
diff --git a/doc/cla/individual/nguyenductamlhp.md b/doc/cla/individual/nguyenductamlhp.md
new file mode 100644
index 00000000000..898f465c52b
--- /dev/null
+++ b/doc/cla/individual/nguyenductamlhp.md
@@ -0,0 +1,11 @@
+Vietnam, 2018-01-16,
+
+I hereby agree to the terms of the Odoo Individual Contributor License
+Agreement v1.0.
+
+I declare that I am authorized and able to make this agreement and sign this
+declaration.
+
+Signed,
+
+Nguyen Duc Tam nguyenductamlhp@gmail.com https://github.com/nguyenductamlhp
diff --git a/doc/cla/individual/sswapnesh.md b/doc/cla/individual/sswapnesh.md
new file mode 100644
index 00000000000..97a61b8c83c
--- /dev/null
+++ b/doc/cla/individual/sswapnesh.md
@@ -0,0 +1,12 @@
+India, 2017-11-24
+
+I hereby agree to the terms of the Odoo Individual Contributor License
+Agreement v1.0.
+
+I declare that I am authorized and able to make this agreement and sign this
+declaration.
+
+Signed,
+
+Swapensh Shah swapneshshah111@gmail.com https://github.com/sswapnesh
+