From a9a4a2e6b974f91bd50486cbfc6ed88a32bc4880 Mon Sep 17 00:00:00 2001 From: Nicolas Martinelli Date: Fri, 29 Jan 2016 10:37:58 +0100 Subject: [PATCH 1/6] [FIX] point_of_sale: invoice Salesman Commit a29e291 uses the SUPERUSER in order to create the invoice. However, we should still link the Salesman of the invoice to the PoS user. Fixes #10647 --- addons/point_of_sale/point_of_sale.py | 1 + 1 file changed, 1 insertion(+) diff --git a/addons/point_of_sale/point_of_sale.py b/addons/point_of_sale/point_of_sale.py index 241fe5f6fee..edcdc4992f1 100644 --- a/addons/point_of_sale/point_of_sale.py +++ b/addons/point_of_sale/point_of_sale.py @@ -1132,6 +1132,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() From 628a6146d2da1a66e44acb10969c3a9555b88b0c Mon Sep 17 00:00:00 2001 From: Nicolas Martinelli Date: Fri, 29 Jan 2016 10:40:18 +0100 Subject: [PATCH 2/6] [FIX] account: keep only taxes of the company If the SUPERUSER creates an invoice, we should make sure to keep only the taxes related to the company of the invoice. This is for example the case in the PoS, where the invoice is created with the SUPERUSER. Fixes #10648 --- addons/account/models/account_invoice.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/addons/account/models/account_invoice.py b/addons/account/models/account_invoice.py index e354c44f2ca..1ff9b2498d3 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 From c04103f4da7f24df4010fdd68ac815e24c9c949a Mon Sep 17 00:00:00 2001 From: Aaron Bohy Date: Wed, 27 Jan 2016 10:37:24 +0100 Subject: [PATCH 3/6] [FIX] mail: various UI fixes/improvements - don't print chat windows - add missing 'width: 100%' on composer and Discuss sidebar inputs (required for community edition) - center the Discuss sidebar inputs - Discuss sidebar items layout: one line, text ellipsis if name is too long - hide chatter in 'create' mode (messages wrote in the chatter need a res_model and a res_id, the latter is not yet existing if we are in 'create' mode) - revert 2d2dacc: print chatter by default (classname 'hidden-print' can be used for specific models for which we don't want the chatter to be printed) - thread: ensure the displayed message's date is in the past - re-render threads every minutes to update dates - don't squash messages in mass mailing channels --- addons/mail/static/src/js/chatter.js | 14 ++++++++++ addons/mail/static/src/js/client_action.js | 2 +- addons/mail/static/src/js/thread.js | 10 ++++++- addons/mail/static/src/less/chat_window.less | 4 +++ addons/mail/static/src/less/chatter.less | 6 ----- .../mail/static/src/less/client_action.less | 17 ++++++++---- addons/mail/static/src/less/composer.less | 1 + addons/mail/static/src/xml/client_action.xml | 26 ++++++++++--------- 8 files changed, 55 insertions(+), 25 deletions(-) diff --git a/addons/mail/static/src/js/chatter.js b/addons/mail/static/src/js/chatter.js index 6617ccb8666..7a653f1738c 100644 --- a/addons/mail/static/src/js/chatter.js +++ b/addons/mail/static/src/js/chatter.js @@ -726,6 +726,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 +769,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 || {}; 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/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 @@ -
- - - - + + + + + + + # + + - # - - -
From 5e79439dee6376930f76b0e47163de8b57668bcb Mon Sep 17 00:00:00 2001 From: Aaron Bohy Date: Thu, 28 Jan 2016 14:18:42 +0100 Subject: [PATCH 4/6] [FIX] mail: chatter: keep mentions Message's body is kept when switching from 'New message' to 'Log a note' since rev. f35b48aa. However, mentionned partners or channels were lost. This rev. makes sure to preserve both message's body and mentions when switching between composers. --- addons/mail/static/src/js/chatter.js | 9 +-------- addons/mail/static/src/js/composer.js | 25 ++++++++++++++++++++++--- 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/addons/mail/static/src/js/chatter.js b/addons/mail/static/src/js/chatter.js index 7a653f1738c..a42fac70c80 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; }, @@ -920,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/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 () { From a157fcd8cd5108586d1f59685393ce172dbd67ac Mon Sep 17 00:00:00 2001 From: Denis Ledoux Date: Fri, 29 Jan 2016 16:15:53 +0100 Subject: [PATCH 5/6] [FIX] sale: Sales configuration, ir.values for all users are restricted. The global record rule `Defaults: alter personal values only` (`base.ir_values_default_rule`) ensures that all users can only create personal `ir.values`. `ir.values` for all users must therefore be created as sudo. opw-667140 --- addons/sale/res_config.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/addons/sale/res_config.py b/addons/sale/res_config.py index b2f2c51e552..34ab78b599e 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): From 6c8bbc3d466b9d5d83d3b7f46f3a904bd433cdaf Mon Sep 17 00:00:00 2001 From: Denis Ledoux Date: Fri, 29 Jan 2016 16:55:23 +0100 Subject: [PATCH 6/6] Revert "[FIX] fields: make `related_sudo` work for draft records (used by onchange)" This reverts commits - 995b257a6c09b9dfd89eedac3a84ff36948b095b - 2ebdb5f36e2f83dac4ec67c2e54e55a4d30bf986 - 748a719fd939cf1028cc217d8b5a8ef8dc39ee08. The commit 748a719fd939cf1028cc217d8b5a8ef8dc39ee08 leads to an issue bigger than what it aims to solve: - as demo user - create a new invoice - add a new line - select a product -> traceback opw-668141 --- .../test_new_api/tests/test_new_fields.py | 46 +------------------ openerp/fields.py | 15 +----- 2 files changed, 4 insertions(+), 57 deletions(-) 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):