diff --git a/addons/account/models/account_invoice.py b/addons/account/models/account_invoice.py index e2395272e26..418038915f0 100644 --- a/addons/account/models/account_invoice.py +++ b/addons/account/models/account_invoice.py @@ -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 diff --git a/addons/mail/static/src/js/chatter.js b/addons/mail/static/src/js/chatter.js index dc528b2b1aa..fb39b595415 100644 --- a/addons/mail/static/src/js/chatter.js +++ b/addons/mail/static/src/js/chatter.js @@ -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 || []); diff --git a/addons/mail/static/src/js/client_action.js b/addons/mail/static/src/js/client_action.js index 82b36f3425d..e0acd48236b 100644 --- a/addons/mail/static/src/js/client_action.js +++ b/addons/mail/static/src/js/client_action.js @@ -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", diff --git a/addons/mail/static/src/js/composer.js b/addons/mail/static/src/js/composer.js index 5d6de2bad50..163f32dc5fb 100644 --- a/addons/mail/static/src/js/composer.js +++ b/addons/mail/static/src/js/composer.js @@ -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 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 () { diff --git a/addons/mail/static/src/js/thread.js b/addons/mail/static/src/js/thread.js index 439b1a8beb3..d0123c0457e 100644 --- a/addons/mail/static/src/js/thread.js +++ b/addons/mail/static/src/js/thread.js @@ -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')) { diff --git a/addons/mail/static/src/less/chat_window.less b/addons/mail/static/src/less/chat_window.less index a4024c93dc1..d6c69f8d688 100644 --- a/addons/mail/static/src/less/chat_window.less +++ b/addons/mail/static/src/less/chat_window.less @@ -23,6 +23,10 @@ } } + @media print { + display: none; + } + .o_chat_header { .o-flex-display(); .o-flex(0, 0, auto); diff --git a/addons/mail/static/src/less/chatter.less b/addons/mail/static/src/less/chatter.less index 839e419e88e..8ea43b8373e 100644 --- a/addons/mail/static/src/less/chatter.less +++ b/addons/mail/static/src/less/chatter.less @@ -131,9 +131,3 @@ list-style: none; } } - -@media print { - .oe_chatter { - display: none; - } -} diff --git a/addons/mail/static/src/less/client_action.less b/addons/mail/static/src/less/client_action.less index aee37ab5e9f..d7b72cb7453 100644 --- a/addons/mail/static/src/less/client_action.less +++ b/addons/mail/static/src/less/client_action.less @@ -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 { diff --git a/addons/mail/static/src/less/composer.less b/addons/mail/static/src/less/composer.less index 88b44fec311..31ae4725ef0 100644 --- a/addons/mail/static/src/less/composer.less +++ b/addons/mail/static/src/less/composer.less @@ -11,6 +11,7 @@ .o_composer { .o_composer_input > textarea { + width: 100%; resize: none; } } diff --git a/addons/mail/static/src/xml/client_action.xml b/addons/mail/static/src/xml/client_action.xml index 1e38fe3d6dd..78371340e36 100644 --- a/addons/mail/static/src/xml/client_action.xml +++ b/addons/mail/static/src/xml/client_action.xml @@ -19,13 +19,13 @@
- @ Inbox + Inbox
- Starred + Starred
@@ -57,7 +57,7 @@
- + @@ -82,18 +82,20 @@ -
- - - - + + + + + + + # + + - # - - -
diff --git a/addons/point_of_sale/point_of_sale.py b/addons/point_of_sale/point_of_sale.py index a46c1ed09bc..b09fcc8a722 100644 --- a/addons/point_of_sale/point_of_sale.py +++ b/addons/point_of_sale/point_of_sale.py @@ -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() diff --git a/addons/sale/res_config.py b/addons/sale/res_config.py index 7cb448b7a1b..e8f46bc5fdd 100644 --- a/addons/sale/res_config.py +++ b/addons/sale/res_config.py @@ -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): diff --git a/openerp/addons/test_new_api/tests/test_new_fields.py b/openerp/addons/test_new_api/tests/test_new_fields.py index 8a7d9225f3b..e1ceca9ea51 100644 --- a/openerp/addons/test_new_api/tests/test_new_fields.py +++ b/openerp/addons/test_new_api/tests/test_new_fields.py @@ -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) diff --git a/openerp/fields.py b/openerp/fields.py index e86e3582f59..19bf4184574 100644 --- a/openerp/fields.py +++ b/openerp/fields.py @@ -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):