[MERGE] forward port branch saas-15 up to 0c54862cbf
This commit is contained in:
@@ -316,7 +316,7 @@ class AccountInvoice(models.Model):
|
||||
payment_move_line_ids = fields.Many2many('account.move.line', string='Payment Move Lines', compute='_compute_payments', store=True)
|
||||
user_id = fields.Many2one('res.users', string='Salesperson', track_visibility='onchange',
|
||||
readonly=True, states={'draft': [('readonly', False)]},
|
||||
default=lambda self: self.env.user)
|
||||
default=lambda self: self.env.user, copy=False)
|
||||
fiscal_position_id = fields.Many2one('account.fiscal.position', string='Fiscal Position', oldname='fiscal_position',
|
||||
readonly=True, states={'draft': [('readonly', False)]})
|
||||
commercial_partner_id = fields.Many2one('res.partner', string='Commercial Entity', compute_sudo=True,
|
||||
|
||||
@@ -155,6 +155,10 @@
|
||||
<label for="currency_exchange_journal_id" class="col-md-3 o_light_label"/>
|
||||
<field name="currency_exchange_journal_id"/>
|
||||
</div>
|
||||
<div class="row mt16">
|
||||
<label for="currency_exchange_journal_id" class="col-md-3 o_light_label"/>
|
||||
<field name="currency_exchange_journal_id"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -119,10 +119,10 @@
|
||||
<field name="stop" attrs="{'invisible': True}"/>
|
||||
<field name="id" attrs="{'invisible': True}"/>
|
||||
|
||||
<field name="start_date" string="Starting at" attrs="{'invisible': [('allday','=',False)], 'readonly': [('id', '!=', False), ('recurrency','=',True)]}"/>
|
||||
<field name="stop_date" string="Ending at" attrs="{'invisible': [('allday','=',False)], 'readonly': [('id', '!=', False), ('recurrency','=',True)]}"/>
|
||||
<field name="start_date" string="Starting at" attrs="{'required': [('allday','=',True)], 'invisible': [('allday','=',False)], 'readonly': [('id', '!=', False), ('recurrency','=',True)]}"/>
|
||||
<field name="stop_date" string="Ending at" attrs="{'required': [('allday','=',True)],'invisible': [('allday','=',False)], 'readonly': [('id', '!=', False), ('recurrency','=',True)]}"/>
|
||||
|
||||
<field name="start_datetime" string="Starting at" attrs="{'invisible': [('allday','=',True)], 'readonly': [('id', '!=', False), ('recurrency','=',True)]}"/>
|
||||
<field name="start_datetime" string="Starting at" attrs="{'required': [('allday','=',False)], 'invisible': [('allday','=',True)], 'readonly': [('id', '!=', False), ('recurrency','=',True)]}"/>
|
||||
<field name="stop_datetime" invisible="1"/>
|
||||
<label for="duration" attrs="{'invisible': [('allday','=',True)]}"/>
|
||||
<div attrs="{'invisible': [('allday','=',True)]}">
|
||||
|
||||
@@ -103,6 +103,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)
|
||||
|
||||
@@ -149,7 +149,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:
|
||||
@@ -467,6 +467,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
|
||||
"""
|
||||
@@ -494,17 +495,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)
|
||||
@@ -532,6 +538,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
|
||||
@@ -582,17 +589,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,
|
||||
|
||||
@@ -257,7 +257,7 @@ var Chatter = Widget.extend(chat_mixin, {
|
||||
var suggested_partners = [];
|
||||
var thread_recipients = result[self.context.default_res_id];
|
||||
_.each(thread_recipients, function (recipient) {
|
||||
var parsed_email = utils.parse_email(recipient[1]);
|
||||
var parsed_email = recipient[1] && utils.parse_email(recipient[1]);
|
||||
suggested_partners.push({
|
||||
checked: true,
|
||||
partner_id: recipient[0],
|
||||
|
||||
@@ -100,15 +100,17 @@ function inline (node, transform_children) {
|
||||
|
||||
// Parses text to find email: Tagada <address@mail.fr> -> [Tagada, address@mail.fr] or False
|
||||
function parse_email (text) {
|
||||
var result = text.match(/(.*)<(.*@.*)>/);
|
||||
if (result) {
|
||||
return [_.str.trim(result[1]), _.str.trim(result[2])];
|
||||
if (text){
|
||||
var result = text.match(/(.*)<(.*@.*)>/);
|
||||
if (result) {
|
||||
return [_.str.trim(result[1]), _.str.trim(result[2])];
|
||||
}
|
||||
result = text.match(/(.*@.*)/);
|
||||
if (result) {
|
||||
return [_.str.trim(result[1]), _.str.trim(result[1])];
|
||||
}
|
||||
return [text, false];
|
||||
}
|
||||
result = text.match(/(.*@.*)/);
|
||||
if (result) {
|
||||
return [_.str.trim(result[1]), _.str.trim(result[1])];
|
||||
}
|
||||
return [text, false];
|
||||
}
|
||||
|
||||
// Replaces textarea text into html text (add <p>, <a>)
|
||||
|
||||
@@ -23,7 +23,6 @@ class TestMailFeatures(TestMail):
|
||||
self.env['res.partner'].create({'name': 'Actual Partner'})
|
||||
self.assertTrue(record.name)
|
||||
|
||||
|
||||
@mute_logger('odoo.addons.mail.models.mail_mail')
|
||||
def test_needaction(self):
|
||||
# needaction use Inbox notification
|
||||
|
||||
@@ -288,3 +288,70 @@ class TestMailMessageAccess(TestMail):
|
||||
msg_cnt = len(self.test_pigs.message_ids)
|
||||
self.test_pigs.message_post(body='Hi!', subject='test')
|
||||
self.assertEqual(len(self.test_pigs.message_ids), msg_cnt + 1)
|
||||
|
||||
@mute_logger('openerp.addons.mail.models.mail_mail')
|
||||
def test_mark_all_as_read(self):
|
||||
self.user_employee.notification_type = 'inbox'
|
||||
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):
|
||||
self.user_portal.notification_type = 'inbox'
|
||||
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")
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -23,7 +23,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
|
||||
@@ -233,7 +233,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));
|
||||
});
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
@@ -725,6 +725,7 @@
|
||||
</template>
|
||||
|
||||
<template id="wizard_checkout" name="Wizard Checkout">
|
||||
<t t-set="website_sale_order" t-value="website.sale_get_order()"/>
|
||||
<ul class="wizard pull-right hidden-xs">
|
||||
<li t-att-class="step==10 and 'text-primary' or 'text-muted'" id="step10">
|
||||
<t t-if="step>10 and step<50">
|
||||
|
||||
@@ -112,11 +112,6 @@
|
||||
<i class="fa fa-cloud-upload"></i> Upload
|
||||
</a>
|
||||
</t>
|
||||
<t t-if="channel.can_upload and is_public_user">
|
||||
<a class="btn btn-primary" title="Upload Presentation" t-attf-href="/web/login?redirect=/slides/#{slug(channel)}">
|
||||
<i class="fa fa-cloud-upload"></i> Upload
|
||||
</a>
|
||||
</t>
|
||||
</h3>
|
||||
</div>
|
||||
<!-- Search box -->
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user