[MERGE] forward port of branch 9.0 up to 6c8bbc3
This commit is contained in:
@@ -5,7 +5,7 @@ from lxml import etree
|
||||
from datetime import datetime
|
||||
from dateutil.relativedelta import relativedelta
|
||||
|
||||
from openerp import api, fields, models, _
|
||||
from openerp import api, fields, models, _, SUPERUSER_ID
|
||||
from openerp.tools import float_is_zero
|
||||
from openerp.tools.misc import formatLang
|
||||
|
||||
@@ -1093,6 +1093,8 @@ class AccountInvoiceLine(models.Model):
|
||||
taxes = self.product_id.taxes_id or self.account_id.tax_ids
|
||||
else:
|
||||
taxes = self.product_id.supplier_taxes_id or self.account_id.tax_ids
|
||||
if self.env.uid == SUPERUSER_ID:
|
||||
taxes = taxes.filtered(lambda r: r.company_id == self.company_id)
|
||||
self.invoice_line_tax_ids = fp_taxes = self.invoice_id.fiscal_position_id.map_tax(taxes)
|
||||
|
||||
fix_price = self.env['account.tax']._fix_tax_included_price
|
||||
|
||||
@@ -451,7 +451,6 @@ var ChatterComposer = composer.BasicComposer.extend({
|
||||
record_name: false,
|
||||
is_log: false,
|
||||
internal_subtypes: [],
|
||||
default_body: '',
|
||||
});
|
||||
if (this.options.is_log) {
|
||||
this.options.send_text = _('Log');
|
||||
@@ -468,13 +467,6 @@ var ChatterComposer = composer.BasicComposer.extend({
|
||||
return $.when(this._super.apply(this, arguments), this.message_get_suggested_recipients());
|
||||
},
|
||||
|
||||
start: function () {
|
||||
var self = this;
|
||||
return this._super().then(function () {
|
||||
self.$input.val(self.options.default_body);
|
||||
});
|
||||
},
|
||||
|
||||
should_send: function () {
|
||||
return false;
|
||||
},
|
||||
@@ -726,6 +718,16 @@ var Chatter = form_common.AbstractField.extend({
|
||||
start: function () {
|
||||
var self = this;
|
||||
|
||||
// Hide the chatter in 'create' mode
|
||||
this.view.on("change:actual_mode", this, this.check_visibility);
|
||||
this.check_visibility();
|
||||
var $container = this.$el.parent();
|
||||
if ($container.hasClass('oe_chatter')) {
|
||||
this.$el
|
||||
.addClass($container.attr("class"))
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
// Move the follower's widget (if any) inside the chatter
|
||||
this.followers = this.field_manager.fields.message_follower_ids;
|
||||
if (this.followers) {
|
||||
@@ -759,6 +761,10 @@ var Chatter = form_common.AbstractField.extend({
|
||||
});
|
||||
},
|
||||
|
||||
check_visibility: function () {
|
||||
this.set({"force_invisible": this.view.get("actual_mode") === "create"});
|
||||
},
|
||||
|
||||
fetch_and_render_thread: function (ids, options) {
|
||||
var self = this;
|
||||
options = options || {};
|
||||
@@ -906,6 +912,7 @@ var Chatter = form_common.AbstractField.extend({
|
||||
is_log: options && options.is_log,
|
||||
record_name: this.record_name,
|
||||
default_body: old_composer && old_composer.$input.val(),
|
||||
default_mention_selections: old_composer && old_composer.mention_get_listener_selections(),
|
||||
});
|
||||
this.composer.on('input_focused', this, function () {
|
||||
this.composer.mention_set_prefetched_partners(this.mention_suggestions || []);
|
||||
|
||||
@@ -435,7 +435,7 @@ var ChatAction = Widget.extend(ControlPanelMixin, {
|
||||
display_load_more: !chat_manager.all_history_loaded(this.channel, this.domain),
|
||||
display_needactions: this.channel.display_needactions,
|
||||
messages_separator_position: this.messages_separator_position,
|
||||
squash_close_messages: this.channel.type !== 'static',
|
||||
squash_close_messages: this.channel.type !== 'static' && !this.channel.mass_mailing,
|
||||
display_empty_channel: !messages.length && !this.domain.length,
|
||||
display_no_match: !messages.length && this.domain.length,
|
||||
display_subject: this.channel.mass_mailing || this.channel.id === "channel_inbox",
|
||||
|
||||
@@ -27,6 +27,9 @@ var accented_letters_mapping = {
|
||||
' ': '[()\\[\\]]',
|
||||
};
|
||||
|
||||
var MENTION_PARTNER_DELIMITER = '@';
|
||||
var MENTION_CHANNEL_DELIMITER = '#';
|
||||
|
||||
// The MentionManager allows the Composer to register listeners. For each
|
||||
// listener, it detects if the user is currently typing a mention (starting by a
|
||||
// given delimiter). If so, if fetches mention suggestions and renders them. On
|
||||
@@ -106,7 +109,7 @@ var MentionManager = Widget.extend({
|
||||
* @param {string} [redirect_classname] the classname of the <a> wrapping the mention
|
||||
*/
|
||||
register: function (listener) {
|
||||
this.listeners.push(_.extend(listener, {
|
||||
this.listeners.push(_.defaults(listener, {
|
||||
selection: [],
|
||||
}));
|
||||
},
|
||||
@@ -123,6 +126,14 @@ var MentionManager = Widget.extend({
|
||||
return listener ? listener.selection : [];
|
||||
},
|
||||
|
||||
get_listener_selections: function () {
|
||||
var selections = {};
|
||||
_.each(this.listeners, function (listener) {
|
||||
selections[listener.delimiter] = listener.selection;
|
||||
});
|
||||
return selections;
|
||||
},
|
||||
|
||||
proposition_navigation: function (keycode) {
|
||||
var $active = this.$('.o_mention_proposition.active');
|
||||
if (keycode === $.ui.keyCode.ENTER) { // selecting proposition
|
||||
@@ -333,6 +344,8 @@ var BasicComposer = Widget.extend({
|
||||
mention_fetch_limit: 8,
|
||||
mention_partners_restricted: false, // set to true to only suggest prefetched partners
|
||||
send_text: _('Send'),
|
||||
default_body: '',
|
||||
default_mention_selections: {},
|
||||
});
|
||||
this.context = this.options.context;
|
||||
|
||||
@@ -344,16 +357,18 @@ var BasicComposer = Widget.extend({
|
||||
// Mention
|
||||
this.mention_manager = new MentionManager(this);
|
||||
this.mention_manager.register({
|
||||
delimiter: '@',
|
||||
delimiter: MENTION_PARTNER_DELIMITER,
|
||||
fetch_callback: this.mention_fetch_partners.bind(this),
|
||||
model: 'res.partner',
|
||||
redirect_classname: 'o_mail_redirect',
|
||||
selection: this.options.default_mention_selections[MENTION_PARTNER_DELIMITER],
|
||||
});
|
||||
this.mention_manager.register({
|
||||
delimiter: '#',
|
||||
delimiter: MENTION_CHANNEL_DELIMITER,
|
||||
fetch_callback: this.mention_fetch_channels.bind(this),
|
||||
model: 'mail.channel',
|
||||
redirect_classname: 'o_channel_redirect',
|
||||
selection: this.options.default_mention_selections[MENTION_CHANNEL_DELIMITER],
|
||||
});
|
||||
|
||||
// Emojis
|
||||
@@ -372,6 +387,7 @@ var BasicComposer = Widget.extend({
|
||||
this.$input.focus(function () {
|
||||
self.trigger('input_focused');
|
||||
});
|
||||
this.$input.val(this.options.default_body);
|
||||
dom_utils.autoresize(this.$input, {parent: this, min_height: this.options.input_min_height});
|
||||
|
||||
// Attachments
|
||||
@@ -635,6 +651,9 @@ var BasicComposer = Widget.extend({
|
||||
mention_set_prefetched_partners: function (prefetched_partners) {
|
||||
this.mention_prefetched_partners = prefetched_partners;
|
||||
},
|
||||
mention_get_listener_selections: function () {
|
||||
return this.mention_manager.get_listener_selections();
|
||||
},
|
||||
|
||||
// Others
|
||||
is_empty: function () {
|
||||
|
||||
@@ -58,6 +58,8 @@ var Thread = Widget.extend({
|
||||
},
|
||||
|
||||
render: function (messages, options) {
|
||||
clearTimeout(this.auto_render_timeout);
|
||||
var self = this;
|
||||
var msgs = _.map(messages, this._preprocess_message.bind(this));
|
||||
if (this.options.display_order === ORDER.DESC) {
|
||||
msgs.reverse();
|
||||
@@ -83,6 +85,12 @@ var Thread = Widget.extend({
|
||||
options: options,
|
||||
ORDER: ORDER,
|
||||
}));
|
||||
|
||||
this.auto_render_timeout = setTimeout(function () {
|
||||
if (!self.isDestroyed()) {
|
||||
self.render(messages, options);
|
||||
}
|
||||
}, 1000*60); // re-render the thread every minute to update dates
|
||||
},
|
||||
|
||||
on_click_redirect: function (event) {
|
||||
@@ -110,7 +118,7 @@ var Thread = Widget.extend({
|
||||
_preprocess_message: function (message) {
|
||||
var msg = _.extend({}, message);
|
||||
|
||||
// Set the date in the browser timezone
|
||||
msg.date = moment.min(msg.date, moment());
|
||||
var date = msg.date.format('YYYY-MM-DD');
|
||||
|
||||
if (date === moment().format('YYYY-MM-DD')) {
|
||||
|
||||
@@ -23,6 +23,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
@media print {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.o_chat_header {
|
||||
.o-flex-display();
|
||||
.o-flex(0, 0, auto);
|
||||
|
||||
@@ -131,9 +131,3 @@
|
||||
list-style: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media print {
|
||||
.oe_chatter {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,10 +67,18 @@
|
||||
}
|
||||
}
|
||||
.o_mail_chat_channel_item {
|
||||
.o-flex-display();
|
||||
width: 100%;
|
||||
padding: 2px 8px 2px 1.5*@odoo-horizontal-padding; // larger padding-left than titles
|
||||
> .badge {
|
||||
margin: 0;
|
||||
.o_channel_name {
|
||||
.o-flex(1, 1, auto);
|
||||
.o-text-overflow();
|
||||
}
|
||||
.o_mail_sidebar_needaction, .o_mail_partner_unpin {
|
||||
.o-flex(0, 0, auto);
|
||||
}
|
||||
.o_mail_sidebar_needaction {
|
||||
margin: 0 0 0 5px;
|
||||
}
|
||||
.o_mail_partner_unpin {
|
||||
padding: 2px 6px;
|
||||
@@ -82,10 +90,11 @@
|
||||
}
|
||||
.o_mail_add_channel {
|
||||
width: 100%;
|
||||
padding: 5px 8px 5px 1.5*@odoo-horizontal-padding; // larger padding-left than titles
|
||||
padding: 5px 1.5*@odoo-horizontal-padding; // larger padding-left than titles
|
||||
position: relative;
|
||||
|
||||
> input {
|
||||
width: 100%;
|
||||
padding: 2px 5px;
|
||||
padding-left: 12px;
|
||||
}
|
||||
@@ -172,8 +181,6 @@
|
||||
opacity: 0.9;
|
||||
|
||||
.o_mail_chat_channel_item {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
.o_composer {
|
||||
.o_composer_input > textarea {
|
||||
width: 100%;
|
||||
resize: none;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,13 +19,13 @@
|
||||
<div class="o_mail_chat_sidebar">
|
||||
<div t-attf-class="o_mail_chat_channel_item #{(active_channel_id == 'channel_inbox') ? 'o_active': ''}"
|
||||
data-channel-id="channel_inbox">
|
||||
@ Inbox
|
||||
<span class="o_channel_name"> <i class="fa fa-at"/> Inbox </span>
|
||||
<t t-set="counter" t-value="needaction_counter"/>
|
||||
<t t-call="mail.chat.SidebarNeedaction"/>
|
||||
</div>
|
||||
<div t-attf-class="o_mail_chat_channel_item #{(active_channel_id == 'channel_starred') ? 'o_active': ''}"
|
||||
data-channel-id="channel_starred">
|
||||
<span class="fa fa-star-o"/> Starred
|
||||
<span class="o_channel_name"> <i class="fa fa-star-o"/> Starred </span>
|
||||
</div>
|
||||
|
||||
<t t-set="channel_type" t-value="'public'"/>
|
||||
@@ -57,7 +57,7 @@
|
||||
</div>
|
||||
</t>
|
||||
<t t-name="mail.chat.SidebarNeedaction">
|
||||
<span t-attf-class="o_mail_sidebar_needaction badge pull-right #{(!counter ? 'hide' : '')}">
|
||||
<span t-attf-class="o_mail_sidebar_needaction badge #{(!counter ? 'hide' : '')}">
|
||||
<t t-esc="counter"/>
|
||||
</span>
|
||||
</t>
|
||||
@@ -82,18 +82,20 @@
|
||||
<t t-name="mail.chat.SidebarItems">
|
||||
<t t-foreach="channels" t-as="channel">
|
||||
<t t-set="counter" t-value="channel.is_chat ? channel.unread_counter : channel.needaction_counter"/>
|
||||
<div t-if="channel.type === channel_type" t-att-data-channel-id="channel.id"
|
||||
<div t-if="channel.type === channel_type" t-att-data-channel-id="channel.id" t-att-title="channel.name"
|
||||
t-attf-class="o_mail_chat_channel_item #{channel.unread_counter ? ' o_unread_message' : ''} #{(active_channel_id == channel.id) ? 'o_active': ''}">
|
||||
<span t-if="display_status">
|
||||
<t t-call="mail.chat.UserStatus">
|
||||
<t t-set="status" t-value="channel.status"/>
|
||||
</t>
|
||||
<span class="o_channel_name">
|
||||
<span t-if="display_status">
|
||||
<t t-call="mail.chat.UserStatus">
|
||||
<t t-set="status" t-value="channel.status"/>
|
||||
</t>
|
||||
</span>
|
||||
<span t-if="display_hash" class="o_mail_hash">#</span>
|
||||
<t t-esc="channel.name"/>
|
||||
<i t-if="channel.mass_mailing" class="fa fa-envelope-o" title="Sends messages by email"/>
|
||||
</span>
|
||||
<span t-if="display_hash" class="o_mail_hash">#</span>
|
||||
<t t-esc="channel.name"/>
|
||||
<i t-if="channel.mass_mailing" class="fa fa-envelope-o" title="Sends messages by email"/>
|
||||
<t t-call="mail.chat.SidebarNeedaction"/>
|
||||
<span t-attf-class="fa fa-times pull-right o_mail_partner_unpin #{counter ? 'hide' : ''}"
|
||||
<span t-attf-class="fa fa-times o_mail_partner_unpin #{counter ? 'hide' : ''}"
|
||||
title="Leave this channel" t-att-data-channel-id="channel.id"/>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
@@ -1129,6 +1129,7 @@ class pos_order(osv.osv):
|
||||
'comment': order.note or '',
|
||||
'currency_id': order.pricelist_id.currency_id.id, # considering partner's sale pricelist's currency
|
||||
'company_id': company_id,
|
||||
'user_id': uid,
|
||||
}
|
||||
invoice = inv_ref.new(cr, uid, inv)
|
||||
invoice._onchange_partner_id()
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
import logging
|
||||
|
||||
from openerp import SUPERUSER_ID
|
||||
from openerp.osv import fields, osv
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
@@ -80,17 +81,17 @@ class sale_configuration(osv.TransientModel):
|
||||
|
||||
def set_sale_defaults(self, cr, uid, ids, context=None):
|
||||
sale_price = self.browse(cr, uid, ids, context=context).sale_pricelist_setting
|
||||
res = self.pool.get('ir.values').set_default(cr, uid, 'sale.config.settings', 'sale_pricelist_setting', sale_price)
|
||||
res = self.pool.get('ir.values').set_default(cr, SUPERUSER_ID, 'sale.config.settings', 'sale_pricelist_setting', sale_price)
|
||||
return res
|
||||
|
||||
def set_deposit_product_id_defaults(self, cr, uid, ids, context=None):
|
||||
deposit_product_id = self.browse(cr, uid, ids, context=context).deposit_product_id_setting
|
||||
res = self.pool.get('ir.values').set_default(cr, uid, 'sale.config.settings', 'deposit_product_id_setting', deposit_product_id.id)
|
||||
res = self.pool.get('ir.values').set_default(cr, SUPERUSER_ID, 'sale.config.settings', 'deposit_product_id_setting', deposit_product_id.id)
|
||||
return res
|
||||
|
||||
def set_auto_done_defaults(self, cr, uid, ids, context=None):
|
||||
auto_done = self.browse(cr, uid, ids, context=context).auto_done_setting
|
||||
res = self.pool.get('ir.values').set_default(cr, uid, 'sale.config.settings', 'auto_done_setting', auto_done)
|
||||
res = self.pool.get('ir.values').set_default(cr, SUPERUSER_ID, 'sale.config.settings', 'auto_done_setting', auto_done)
|
||||
return res
|
||||
|
||||
def onchange_sale_price(self, cr, uid, ids, sale_pricelist_setting, context=None):
|
||||
|
||||
@@ -5,7 +5,7 @@ from datetime import date, datetime
|
||||
|
||||
from openerp.exceptions import AccessError
|
||||
from openerp.tests import common
|
||||
from openerp.tools import mute_logger
|
||||
from openerp.exceptions import except_orm
|
||||
|
||||
|
||||
class TestNewFields(common.TransactionCase):
|
||||
@@ -420,49 +420,7 @@ class TestNewFields(common.TransactionCase):
|
||||
self.assertEqual(message.name, "[%s] %s" % (discussion.name, ''))
|
||||
self.assertEqual(message.size, len(BODY))
|
||||
|
||||
@mute_logger('openerp.addons.base.ir.ir_model')
|
||||
def test_41_new_related(self):
|
||||
""" test the behavior of related fields starting on new records. """
|
||||
# make discussions unreadable for demo user
|
||||
access = self.env.ref('test_new_api.access_discussion')
|
||||
access.write({'perm_read': False})
|
||||
|
||||
# create an environment for demo user
|
||||
env = self.env(user=self.env.ref('base.user_demo'))
|
||||
self.assertEqual(env.user.login, "demo")
|
||||
|
||||
# create a new message as demo user
|
||||
discussion = self.env.ref('test_new_api.discussion_0')
|
||||
message = env['test_new_api.message'].new({'discussion': discussion})
|
||||
self.assertEqual(message.discussion, discussion)
|
||||
|
||||
# read the related field discussion_name
|
||||
self.assertEqual(message.discussion.env, env)
|
||||
self.assertEqual(message.discussion_name, discussion.name)
|
||||
with self.assertRaises(AccessError):
|
||||
message.discussion.name
|
||||
|
||||
@mute_logger('openerp.addons.base.ir.ir_model')
|
||||
def test_42_new_related(self):
|
||||
""" test the behavior of related fields traversing new records. """
|
||||
# make discussions unreadable for demo user
|
||||
access = self.env.ref('test_new_api.access_discussion')
|
||||
access.write({'perm_read': False})
|
||||
|
||||
# create an environment for demo user
|
||||
env = self.env(user=self.env.ref('base.user_demo'))
|
||||
self.assertEqual(env.user.login, "demo")
|
||||
|
||||
# create a new discussion and a new message as demo user
|
||||
discussion = env['test_new_api.discussion'].new({'name': 'Stuff'})
|
||||
message = env['test_new_api.message'].new({'discussion': discussion})
|
||||
self.assertEqual(message.discussion, discussion)
|
||||
|
||||
# read the related field discussion_name
|
||||
self.assertNotEqual(message.sudo().env, message.env)
|
||||
self.assertEqual(message.discussion_name, discussion.name)
|
||||
|
||||
def test_50_defaults(self):
|
||||
def test_41_defaults(self):
|
||||
""" test default values. """
|
||||
fields = ['discussion', 'body', 'author', 'size']
|
||||
defaults = self.env['test_new_api.message'].default_get(fields)
|
||||
|
||||
+2
-13
@@ -42,15 +42,6 @@ def _check_value(value):
|
||||
""" Return ``value``, or call its getter if ``value`` is a :class:`SpecialValue`. """
|
||||
return value.get() if isinstance(value, SpecialValue) else value
|
||||
|
||||
def copy_cache(records, env):
|
||||
""" Recursively copy the cache of ``records`` to the environment ``env``. """
|
||||
for record, target in zip(records, records.with_env(env)):
|
||||
if not target._cache:
|
||||
for name, value in record._cache.iteritems():
|
||||
target[name] = value
|
||||
if isinstance(value, BaseModel):
|
||||
copy_cache(value, env)
|
||||
|
||||
|
||||
def resolve_mro(model, name, predicate):
|
||||
""" Return the list of successively overridden values of attribute ``name``
|
||||
@@ -570,10 +561,8 @@ class Field(object):
|
||||
# when related_sudo, bypass access rights checks when reading values
|
||||
others = records.sudo() if self.related_sudo else records
|
||||
for record, other in zip(records, others):
|
||||
if not record.id and record.env != other.env:
|
||||
# draft records: copy record's cache to other's cache first
|
||||
copy_cache(record, other.env)
|
||||
other, field = self.traverse_related(other)
|
||||
# do not switch to another environment if record is a draft one
|
||||
other, field = self.traverse_related(other if record.id else record)
|
||||
record[self.name] = other[field.name]
|
||||
|
||||
def _inverse_related(self, records):
|
||||
|
||||
Reference in New Issue
Block a user