diff --git a/addons/account/models/account_payment.py b/addons/account/models/account_payment.py index b08a17092a9..38ad5ecffc1 100644 --- a/addons/account/models/account_payment.py +++ b/addons/account/models/account_payment.py @@ -248,7 +248,7 @@ class account_payment(models.Model): invoice_defaults = self.resolve_2many_commands('invoice_ids', rec.get('invoice_ids')) if invoice_defaults and len(invoice_defaults) == 1: invoice = invoice_defaults[0] - rec['communication'] = invoice['reference'] + rec['communication'] = invoice['reference'] or invoice['name'] or invoice['number'] rec['currency_id'] = invoice['currency_id'][0] rec['payment_type'] = invoice['type'] in ('out_invoice', 'in_refund') and 'inbound' or 'outbound' rec['partner_type'] = MAP_INVOICE_TYPE_PARTNER_TYPE[invoice['type']] diff --git a/addons/base_action_rule/base_action_rule.py b/addons/base_action_rule/base_action_rule.py index 39dd7b1f3e3..76c70e5db11 100644 --- a/addons/base_action_rule/base_action_rule.py +++ b/addons/base_action_rule/base_action_rule.py @@ -274,7 +274,7 @@ class base_action_rule(osv.osv): # check conditions, and execute actions on the records that satisfy them for action in actions: - action._process(action._filter_post(pre[action])) + action._process(action._filter_post(records)) # call original method return unlink.origin(self, **kwargs) diff --git a/addons/crm_partner_assign/wizard/crm_channel_interested.py b/addons/crm_partner_assign/wizard/crm_channel_interested.py index ac9bd931464..ae80f44e375 100644 --- a/addons/crm_partner_assign/wizard/crm_channel_interested.py +++ b/addons/crm_partner_assign/wizard/crm_channel_interested.py @@ -41,7 +41,7 @@ class crm_lead_forward_to_partner(osv.TransientModel): if wizard.comment: message += '
%s
' % wizard.comment for active_id in context.get('active_ids', []): - lead_obj.message_post(cr, uid, active_id, body=message, subtype="mail.mt_comment", context=context) + lead_obj.message_post(cr, uid, active_id, body=message, subtype="mail.mt_note", context=context) if values: lead_obj.write(cr, SUPERUSER_ID, context.get('active_ids', []), values) lead_obj.set_tag_assign(cr, SUPERUSER_ID, context.get('active_ids', []), False) diff --git a/addons/hr_holidays/hr_holidays.py b/addons/hr_holidays/hr_holidays.py index bdce2f9de44..b694b882ca9 100644 --- a/addons/hr_holidays/hr_holidays.py +++ b/addons/hr_holidays/hr_holidays.py @@ -450,6 +450,19 @@ class hr_holidays(osv.osv): return True def holidays_confirm(self, cr, uid, ids, context=None): + for record in self.browse(cr, uid, ids, context=context): + if record.department_id: + # Subscribe the followers of the department following the `confirmed` subtype + # It's done manually because `message_auto_subscribe` only works on fields for which + # the value is passed to the `create` or `write` methods, + # it doesn't work for related/computed fields + # This can be removed as soon as `department_id` on `hr.holidays` becomes a regular + # fields or as soon as `message_auto_subscribe` works with related/computed fields. + confirmed_subtype = self.pool['ir.model.data'].xmlid_to_object(cr, uid, 'hr_holidays.mt_department_holidays_confirmed', context=context) + self.message_subscribe(cr, uid, [record.id], [ + follower.partner_id.id for follower in record.department_id.message_follower_ids + if confirmed_subtype in follower.subtype_ids + ], context=context) return self.write(cr, uid, ids, {'state': 'confirm'}) def holidays_refuse(self, cr, uid, ids, context=None): diff --git a/addons/mail/static/src/js/client_action.js b/addons/mail/static/src/js/client_action.js index 704c3d4064c..04e1ed13ba8 100644 --- a/addons/mail/static/src/js/client_action.js +++ b/addons/mail/static/src/js/client_action.js @@ -283,12 +283,13 @@ var ChatAction = Widget.extend(ControlPanelMixin, { }, unselect_message: function() { - if (this.channel.type !== 'static' && !this.channel.mass_mailing) { - this.basic_composer.toggle(true); - this.basic_composer.focus(); + this.basic_composer.toggle(this.channel.type !== 'static' && !this.channel.mass_mailing); + this.extended_composer.toggle(this.channel.type !== 'static' && this.channel.mass_mailing); + if (!config.device.touch) { + var composer = this.channel.mass_mailing ? this.extended_composer : this.basic_composer; + composer.focus(); } this.$el.removeClass('o_mail_selection_mode'); - this.extended_composer.toggle(this.channel.mass_mailing); this.thread.unselect(); this.selected_message = null; }, @@ -424,10 +425,6 @@ var ChatAction = Widget.extend(ControlPanelMixin, { .find('.o_mail_chat_button_unstar_all') .toggle(channel.id === "channel_starred"); - self.basic_composer.toggle(channel.type !== 'static' && !channel.mass_mailing); - self.extended_composer.toggle(channel.type !== 'static' && channel.mass_mailing); - self.$el.removeClass('o_mail_selection_mode'); - self.$('.o_mail_chat_channel_item') .removeClass('o_active') .filter('[data-channel-id=' + channel.id + ']') @@ -443,14 +440,14 @@ var ChatAction = Widget.extend(ControlPanelMixin, { // Update control panel before focusing the composer, otherwise focus is on the searchview self.update_cp(); - if (!config.device.touch) { - var composer = channel.mass_mailing ? self.extended_composer : self.basic_composer; - composer.focus(); - } if (config.device.size_class === config.device.SIZES.XS) { self.$('.o_mail_chat_sidebar').hide(); } + // Display and focus the adequate composer, and unselect possibly selected message + // to prevent sending messages as reply to that message + self.unselect_message(); + self.action_manager.do_push_state({ action: self.action.id, active_id: self.channel.id, diff --git a/addons/mail/static/src/js/systray.js b/addons/mail/static/src/js/systray.js index af442f5606e..5e73042a7aa 100644 --- a/addons/mail/static/src/js/systray.js +++ b/addons/mail/static/src/js/systray.js @@ -111,9 +111,7 @@ var MessagingMenu = Widget.extend({ // Generate last message preview (inline message body and compute date to display) _.each(channels_preview, function (channel) { - channel.last_message_preview = - channel.last_message.displayed_author + ': ' + - chat_manager.get_message_body_preview(channel.last_message.body); + channel.last_message_preview = chat_manager.get_message_body_preview(channel.last_message.body); if (channel.last_message.date.isSame(new Date(), 'd')) { // today channel.last_message_date = channel.last_message.date.format('LT'); } else { diff --git a/addons/mail/static/src/xml/systray.xml b/addons/mail/static/src/xml/systray.xml index 4a987fdf992..2ce26f4d485 100644 --- a/addons/mail/static/src/xml/systray.xml +++ b/addons/mail/static/src/xml/systray.xml @@ -45,6 +45,7 @@ diff --git a/addons/mrp/mrp.py b/addons/mrp/mrp.py index b94428f91fe..871fe2a6a10 100644 --- a/addons/mrp/mrp.py +++ b/addons/mrp/mrp.py @@ -802,13 +802,7 @@ class mrp_production(osv.osv): return True def _compute_costs_from_production(self, cr, uid, ids, context=None): - """ Generate workcenter costs and rectify the value of the quant - - Must be called at the end of the production. - The value of the quant was not possible to compute before the end of - the manufacturing order due to the cost of raw manterial and production - costs. The price of the quant was not set in get_price_unit. - """ + """ Generate workcenter costs in analytic accounts""" for production in self.browse(cr, uid, ids): total_cost = self._costs_generate(cr, uid, production) @@ -956,6 +950,16 @@ class mrp_production(osv.osv): total_cost += sum([x.inventory_value for x in consumed_move.quant_ids if x.qty > 0]) return total_cost + def _calculate_workcenter_cost(self, cr, uid, production_id, context=None): + """ Compute the planned production cost from the workcenters """ + production = self.browse(cr, uid, production_id, context=context) + total_cost = 0.0 + for wc_line in production.workcenter_lines: + wc = wc_line.workcenter_id + total_cost += wc_line.hour*wc.costs_hour + wc_line.cycle*wc.costs_cycle + + return total_cost + def action_produce(self, cr, uid, production_id, production_qty, production_mode, wiz=False, context=None): """ To produce final product based on production mode (consume/consume&produce). If Production mode is consume, all stock move lines of raw materials will be done/consumed. @@ -1011,18 +1015,25 @@ class mrp_production(osv.osv): total_consume_moves.append(extra_move_id) if production_mode == 'consume_produce': + # add production lines that have already been consumed since the last 'consume & produce' + last_production_date = production.move_created_ids2 and max(production.move_created_ids2.mapped('date')) or False + already_consumed_lines = production.move_lines2.filtered(lambda l: l.date > last_production_date) + total_consume_moves += already_consumed_lines.ids + price_unit = 0 for produce_product in production.move_created_ids: is_main_product = (produce_product.product_id.id == production.product_id.id) and production.product_id.cost_method=='real' if is_main_product: total_cost = self._calculate_total_cost(cr, uid, total_consume_moves, context=context) - price_unit = total_cost / production_qty_uom + production_cost = self._calculate_workcenter_cost(cr, uid, production_id, context=context) + price_unit = (total_cost + production_cost) / production_qty_uom + subproduct_factor = self._get_subproduct_factor(cr, uid, production.id, produce_product.id, context=context) lot_id = False if wiz: lot_id = wiz.lot_id.id qty = min(subproduct_factor * production_qty_uom, produce_product.product_qty) #Needed when producing more than maximum quantity - if is_main_product: + if is_main_product and price_unit: stock_mov_obj.write(cr, uid, [produce_product.id], {'price_unit': price_unit}, context=context) new_moves = stock_mov_obj.action_consume(cr, uid, [produce_product.id], qty, location_id=produce_product.location_id.id, restrict_lot_id=lot_id, context=context) diff --git a/addons/point_of_sale/point_of_sale_view.xml b/addons/point_of_sale/point_of_sale_view.xml index 7db53e35f8e..35a0a02e1e8 100644 --- a/addons/point_of_sale/point_of_sale_view.xml +++ b/addons/point_of_sale/point_of_sale_view.xml @@ -39,6 +39,7 @@