[MERGE] forward port of branch 9.0 up to 3381e7b

This commit is contained in:
Christophe Simonis
2016-05-04 14:42:54 +02:00
16 changed files with 59 additions and 43 deletions
+1 -1
View File
@@ -248,7 +248,7 @@ class account_payment(models.Model):
invoice_defaults = self.resolve_2many_commands('invoice_ids', rec.get('invoice_ids')) invoice_defaults = self.resolve_2many_commands('invoice_ids', rec.get('invoice_ids'))
if invoice_defaults and len(invoice_defaults) == 1: if invoice_defaults and len(invoice_defaults) == 1:
invoice = invoice_defaults[0] 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['currency_id'] = invoice['currency_id'][0]
rec['payment_type'] = invoice['type'] in ('out_invoice', 'in_refund') and 'inbound' or 'outbound' rec['payment_type'] = invoice['type'] in ('out_invoice', 'in_refund') and 'inbound' or 'outbound'
rec['partner_type'] = MAP_INVOICE_TYPE_PARTNER_TYPE[invoice['type']] rec['partner_type'] = MAP_INVOICE_TYPE_PARTNER_TYPE[invoice['type']]
+1 -1
View File
@@ -274,7 +274,7 @@ class base_action_rule(osv.osv):
# check conditions, and execute actions on the records that satisfy them # check conditions, and execute actions on the records that satisfy them
for action in actions: for action in actions:
action._process(action._filter_post(pre[action])) action._process(action._filter_post(records))
# call original method # call original method
return unlink.origin(self, **kwargs) return unlink.origin(self, **kwargs)
@@ -41,7 +41,7 @@ class crm_lead_forward_to_partner(osv.TransientModel):
if wizard.comment: if wizard.comment:
message += '<p>%s</p>' % wizard.comment message += '<p>%s</p>' % wizard.comment
for active_id in context.get('active_ids', []): 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: if values:
lead_obj.write(cr, SUPERUSER_ID, context.get('active_ids', []), 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) lead_obj.set_tag_assign(cr, SUPERUSER_ID, context.get('active_ids', []), False)
+13
View File
@@ -450,6 +450,19 @@ class hr_holidays(osv.osv):
return True return True
def holidays_confirm(self, cr, uid, ids, context=None): 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'}) return self.write(cr, uid, ids, {'state': 'confirm'})
def holidays_refuse(self, cr, uid, ids, context=None): def holidays_refuse(self, cr, uid, ids, context=None):
+9 -12
View File
@@ -283,12 +283,13 @@ var ChatAction = Widget.extend(ControlPanelMixin, {
}, },
unselect_message: function() { unselect_message: function() {
if (this.channel.type !== 'static' && !this.channel.mass_mailing) { this.basic_composer.toggle(this.channel.type !== 'static' && !this.channel.mass_mailing);
this.basic_composer.toggle(true); this.extended_composer.toggle(this.channel.type !== 'static' && this.channel.mass_mailing);
this.basic_composer.focus(); 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.$el.removeClass('o_mail_selection_mode');
this.extended_composer.toggle(this.channel.mass_mailing);
this.thread.unselect(); this.thread.unselect();
this.selected_message = null; this.selected_message = null;
}, },
@@ -424,10 +425,6 @@ var ChatAction = Widget.extend(ControlPanelMixin, {
.find('.o_mail_chat_button_unstar_all') .find('.o_mail_chat_button_unstar_all')
.toggle(channel.id === "channel_starred"); .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') self.$('.o_mail_chat_channel_item')
.removeClass('o_active') .removeClass('o_active')
.filter('[data-channel-id=' + channel.id + ']') .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 // Update control panel before focusing the composer, otherwise focus is on the searchview
self.update_cp(); 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) { if (config.device.size_class === config.device.SIZES.XS) {
self.$('.o_mail_chat_sidebar').hide(); 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({ self.action_manager.do_push_state({
action: self.action.id, action: self.action.id,
active_id: self.channel.id, active_id: self.channel.id,
+1 -3
View File
@@ -111,9 +111,7 @@ var MessagingMenu = Widget.extend({
// Generate last message preview (inline message body and compute date to display) // Generate last message preview (inline message body and compute date to display)
_.each(channels_preview, function (channel) { _.each(channels_preview, function (channel) {
channel.last_message_preview = channel.last_message_preview = chat_manager.get_message_body_preview(channel.last_message.body);
channel.last_message.displayed_author + ': ' +
chat_manager.get_message_body_preview(channel.last_message.body);
if (channel.last_message.date.isSame(new Date(), 'd')) { // today if (channel.last_message.date.isSame(new Date(), 'd')) { // today
channel.last_message_date = channel.last_message.date.format('LT'); channel.last_message_date = channel.last_message.date.format('LT');
} else { } else {
+1
View File
@@ -45,6 +45,7 @@
<span class="o_last_message_date"> <t t-esc="channel.last_message_date"/> </span> <span class="o_last_message_date"> <t t-esc="channel.last_message_date"/> </span>
</div> </div>
<div class="o_last_message_preview"> <div class="o_last_message_preview">
<t t-esc="channel.last_message.displayed_author"/>:
<t t-raw="channel.last_message_preview"/> <t t-raw="channel.last_message_preview"/>
</div> </div>
</div> </div>
+20 -9
View File
@@ -802,13 +802,7 @@ class mrp_production(osv.osv):
return True return True
def _compute_costs_from_production(self, cr, uid, ids, context=None): def _compute_costs_from_production(self, cr, uid, ids, context=None):
""" Generate workcenter costs and rectify the value of the quant """ Generate workcenter costs in analytic accounts"""
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.
"""
for production in self.browse(cr, uid, ids): for production in self.browse(cr, uid, ids):
total_cost = self._costs_generate(cr, uid, production) 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]) total_cost += sum([x.inventory_value for x in consumed_move.quant_ids if x.qty > 0])
return total_cost 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): 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). """ 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. 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) total_consume_moves.append(extra_move_id)
if production_mode == 'consume_produce': 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 price_unit = 0
for produce_product in production.move_created_ids: 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' is_main_product = (produce_product.product_id.id == production.product_id.id) and production.product_id.cost_method=='real'
if is_main_product: if is_main_product:
total_cost = self._calculate_total_cost(cr, uid, total_consume_moves, context=context) 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) subproduct_factor = self._get_subproduct_factor(cr, uid, production.id, produce_product.id, context=context)
lot_id = False lot_id = False
if wiz: if wiz:
lot_id = wiz.lot_id.id lot_id = wiz.lot_id.id
qty = min(subproduct_factor * production_qty_uom, produce_product.product_qty) #Needed when producing more than maximum quantity 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) 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, 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) location_id=produce_product.location_id.id, restrict_lot_id=lot_id, context=context)
@@ -39,6 +39,7 @@
<field name="price_unit" on_change="onchange_qty(parent.pricelist_id,product_id, discount, qty, price_unit, context)" widget="monetary"/> <field name="price_unit" on_change="onchange_qty(parent.pricelist_id,product_id, discount, qty, price_unit, context)" widget="monetary"/>
<field name="discount" on_change="onchange_qty(parent.pricelist_id,product_id, discount, qty, price_unit, context)" widget="monetary"/> <field name="discount" on_change="onchange_qty(parent.pricelist_id,product_id, discount, qty, price_unit, context)" widget="monetary"/>
<field name="tax_ids_after_fiscal_position" widget="many2many_tags"/> <field name="tax_ids_after_fiscal_position" widget="many2many_tags"/>
<field name="tax_ids" invisible="1"/>
<field name="price_subtotal" widget="monetary"/> <field name="price_subtotal" widget="monetary"/>
<field name="price_subtotal_incl" widget="monetary"/> <field name="price_subtotal_incl" widget="monetary"/>
</tree> </tree>
@@ -1023,7 +1023,9 @@ var ClientListScreenWidget = ScreenWidget.extend({
if (this.editing_client) { if (this.editing_client) {
this.$('.detail.barcode').val(code.code); this.$('.detail.barcode').val(code.code);
} else if (this.pos.db.get_partner_by_barcode(code.code)) { } else if (this.pos.db.get_partner_by_barcode(code.code)) {
this.display_client_details('show',this.pos.db.get_partner_by_barcode(code.code)); var partner = this.pos.db.get_partner_by_barcode(code.code);
this.new_client = partner;
this.display_client_details('show', partner);
} }
}, },
perform_search: function(query, associate_result){ perform_search: function(query, associate_result){
+1 -1
View File
@@ -40,7 +40,7 @@ class stock_production_lot(osv.osv):
# Assign dates according to products data # Assign dates according to products data
def create(self, cr, uid, vals, context=None): def create(self, cr, uid, vals, context=None):
context = dict(context or {}) context = dict(context or {})
context['product_id'] = vals.get('product_id', context.get('default_product_id')) context['product_id'] = vals.get('product_id', context.get('default_product_id') or context.get('product_id'))
return super(stock_production_lot, self).create(cr, uid, vals, context=context) return super(stock_production_lot, self).create(cr, uid, vals, context=context)
_defaults = { _defaults = {
+1 -3
View File
@@ -144,6 +144,4 @@ class AccountInvoiceLine(models.Model):
average_price_unit += factor * self._compute_average_price(prod_qty_done, prod_quantity, prod_moves) average_price_unit += factor * self._compute_average_price(prod_qty_done, prod_quantity, prod_moves)
price_unit = average_price_unit or price_unit price_unit = average_price_unit or price_unit
price_unit = uom_obj._compute_qty_obj(self.uom_id, price_unit, self.product_id.uom_id, round=False) price_unit = uom_obj._compute_qty_obj(self.uom_id, price_unit, self.product_id.uom_id, round=False)
return price_unit return price_unit
else:
return price_unit
@@ -10,15 +10,7 @@ class WebsiteAccount(website_account):
def account(self): def account(self):
response = super(WebsiteAccount, self).account() response = super(WebsiteAccount, self).account()
user = request.env.user user = request.env.user
# TDE FIXME: shouldn't that be mnaged by the access rule itself ? project_issues = request.env['project.issue'].search([])
# portal projects where you or someone from your company are a follower
project_issues = request.env['project.issue'].search([
'&',
('project_id.privacy_visibility', '=', 'portal'),
'|',
('message_partner_ids', 'child_of', [user.partner_id.commercial_partner_id.id]),
('message_partner_ids', 'child_of', [user.partner_id.id])
])
response.qcontext.update({'issues': project_issues}) response.qcontext.update({'issues': project_issues})
return response return response
+1
View File
@@ -639,6 +639,7 @@ Model Reference
.. automethod:: write .. automethod:: write
.. automethod:: read .. automethod:: read
.. automethod:: read_group
.. rubric:: Research .. rubric:: Research
+1 -1
View File
@@ -14902,7 +14902,7 @@ msgstr ""
#. module: base #. module: base
#: model:ir.model.fields,help:base.field_res_partner_company_type #: model:ir.model.fields,help:base.field_res_partner_company_type
#: model:ir.model.fields,help:base.field_res_users_company_type #: model:ir.model.fields,help:base.field_res_users_company_type
msgid "Technical field, used only to display a boolean using a radio button. As for Odoo v9 RadioButton cannot be used on boolean fields, this one serves as interface. Due to the old API limitations with interface function field, we implement it by hand instead of a true function field. When migrating to the new API the code should be simplified." msgid "Technical field, used only to display a boolean using a radio button. As for Odoo v9 RadioButton cannot be used on boolean fields, this one serves as interface. Due to the old API limitations with interface function field, we implement it by hand instead of a true function field. When migrating to the new API the code should be simplified. Changing the company_type of a company contact into a company will not display this contact as a company contact but as a standalone company."
msgstr "" msgstr ""
#. module: base #. module: base
+3 -1
View File
@@ -239,7 +239,9 @@ class res_partner(osv.Model, format_address):
'fields, this one serves as interface. Due to the old API ' 'fields, this one serves as interface. Due to the old API '
'limitations with interface function field, we implement it ' 'limitations with interface function field, we implement it '
'by hand instead of a true function field. When migrating to ' 'by hand instead of a true function field. When migrating to '
'the new API the code should be simplified.'), 'the new API the code should be simplified. Changing the'
'company_type of a company contact into a company will not display'
'this contact as a company contact but as a standalone company.'),
'use_parent_address': fields.boolean('Use Company Address', help="Select this if you want to set company's address information for this contact"), 'use_parent_address': fields.boolean('Use Company Address', help="Select this if you want to set company's address information for this contact"),
'company_id': fields.many2one('res.company', 'Company', select=1), 'company_id': fields.many2one('res.company', 'Company', select=1),
'color': fields.integer('Color Index'), 'color': fields.integer('Color Index'),