summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--addons/account/models/account_move.py2
-rw-r--r--addons/account/static/src/js/account_payment_widget.js2
-rw-r--r--addons/bus/models/res_partner.py48
-rw-r--r--addons/calendar/calendar.py14
-rw-r--r--addons/delivery/models/stock_picking.py7
-rw-r--r--addons/hr/models/res_partner.py17
-rw-r--r--addons/mail/models/mail_followers.py18
-rw-r--r--addons/mail/models/mail_message.py17
-rw-r--r--addons/mail/tests/test_mail_features.py11
-rw-r--r--addons/mail/tests/test_mail_message.py5
-rw-r--r--addons/project/project.py7
-rw-r--r--addons/purchase/purchase.py7
-rw-r--r--addons/sale/sale.py8
-rw-r--r--addons/web/views/webclient_templates.xml2
-rw-r--r--addons/web_editor/static/src/js/summernote.js2
-rw-r--r--addons/website/views/website_templates.xml2
-rw-r--r--addons/website_sale/models/sale_order.py42
-rw-r--r--addons/website_sale/views/templates.xml3
-rw-r--r--addons/website_sale_stock/__init__.py1
-rw-r--r--addons/website_sale_stock/models/__init__.py1
-rw-r--r--addons/website_sale_stock/models/website.py15
-rw-r--r--addons/website_slides/models/slides.py4
-rw-r--r--doc/cla/corporate/bacgroup.md21
-rw-r--r--doc/cla/individual/boogerlad.md11
-rw-r--r--openerp/addons/base/res/ir_property.py4
-rw-r--r--openerp/addons/base/res/res_lang.py3
-rw-r--r--openerp/http.py2
27 files changed, 196 insertions, 80 deletions
diff --git a/addons/account/models/account_move.py b/addons/account/models/account_move.py
index fa85b6a47f2..e039f31353a 100644
--- a/addons/account/models/account_move.py
+++ b/addons/account/models/account_move.py
@@ -1014,7 +1014,7 @@ class AccountMoveLine(models.Model):
rec_move_ids = self.env['account.partial.reconcile']
for account_move_line in self:
for invoice in account_move_line.payment_id.invoice_ids:
- if account_move_line in invoice.payment_move_line_ids:
+ if invoice.id == self.env.context.get('invoice_id') and account_move_line in invoice.payment_move_line_ids:
account_move_line.payment_id.write({'invoice_ids': [(3, invoice.id, None)]})
rec_move_ids += account_move_line.matched_debit_ids
rec_move_ids += account_move_line.matched_credit_ids
diff --git a/addons/account/static/src/js/account_payment_widget.js b/addons/account/static/src/js/account_payment_widget.js
index 932de22eef5..a7f2b64c413 100644
--- a/addons/account/static/src/js/account_payment_widget.js
+++ b/addons/account/static/src/js/account_payment_widget.js
@@ -59,7 +59,7 @@ var ShowPaymentLineWidget = form_common.AbstractField.extend({
var payment_id = parseInt($(this).attr('payment-id'))
if (payment_id !== undefined && payment_id !== NaN){
new Model("account.move.line")
- .call("remove_move_reconcile", [payment_id])
+ .call("remove_move_reconcile", [payment_id, {'invoice_id': self.view.datarecord.id}])
.then(function (result) {
self.view.reload();
});
diff --git a/addons/bus/models/res_partner.py b/addons/bus/models/res_partner.py
index c0ba268fc7d..fc6b38f4afd 100644
--- a/addons/bus/models/res_partner.py
+++ b/addons/bus/models/res_partner.py
@@ -34,24 +34,30 @@ class ResPartner(models.Model):
:param name : the partner name to search
:param limit : the limit of result to return
"""
- name = '%' + name + '%'
- excluded_partner_ids = [self.env.user.partner_id.id]
- self.env.cr.execute("""
- SELECT
- U.id as user_id,
- P.id as id,
- P.name as name,
- CASE WHEN B.last_poll IS NULL THEN 'offline'
- WHEN age(now() AT TIME ZONE 'UTC', B.last_poll) > interval %s THEN 'offline'
- WHEN age(now() AT TIME ZONE 'UTC', B.last_presence) > interval %s THEN 'away'
- ELSE 'online'
- END as im_status
- FROM res_users U
- JOIN res_partner P ON P.id = U.partner_id
- LEFT JOIN bus_presence B ON B.user_id = U.id
- WHERE P.name ILIKE %s
- AND P.id NOT IN %s
- AND U.active = 't'
- LIMIT %s
- """, ("%s seconds" % DISCONNECTION_TIMER, "%s seconds" % AWAY_TIMER, name, tuple(excluded_partner_ids), limit))
- return self.env.cr.dictfetchall()
+ # This method is supposed to be used only in the context of channel creation or
+ # extension via an invite. As both of these actions require the 'create' access
+ # right, we check this specific ACL.
+ if self.env['mail.channel'].check_access_rights('create', raise_exception=False):
+ name = '%' + name + '%'
+ excluded_partner_ids = [self.env.user.partner_id.id]
+ self.env.cr.execute("""
+ SELECT
+ U.id as user_id,
+ P.id as id,
+ P.name as name,
+ CASE WHEN B.last_poll IS NULL THEN 'offline'
+ WHEN age(now() AT TIME ZONE 'UTC', B.last_poll) > interval %s THEN 'offline'
+ WHEN age(now() AT TIME ZONE 'UTC', B.last_presence) > interval %s THEN 'away'
+ ELSE 'online'
+ END as im_status
+ FROM res_users U
+ JOIN res_partner P ON P.id = U.partner_id
+ LEFT JOIN bus_presence B ON B.user_id = U.id
+ WHERE P.name ILIKE %s
+ AND P.id NOT IN %s
+ AND U.active = 't'
+ LIMIT %s
+ """, ("%s seconds" % DISCONNECTION_TIMER, "%s seconds" % AWAY_TIMER, name, tuple(excluded_partner_ids), limit))
+ return self.env.cr.dictfetchall()
+ else:
+ return {}
diff --git a/addons/calendar/calendar.py b/addons/calendar/calendar.py
index 741899daae7..6d575ba3425 100644
--- a/addons/calendar/calendar.py
+++ b/addons/calendar/calendar.py
@@ -221,6 +221,7 @@ class calendar_attendee(osv.Model):
'datas_fname': 'invitation.ics',
'datas': str(ics_file).encode('base64')})]
vals['model'] = None # We don't want to have the mail in the tchatter while in queue!
+ vals['res_id'] = False
the_mailmess = mail_pool.browse(cr, uid, mail_id, context=context).mail_message_id
mailmess_pool.write(cr, uid, [the_mailmess.id], vals, context=context)
mail_ids.append(mail_id)
@@ -1596,11 +1597,11 @@ class calendar_event(osv.Model):
super(calendar_event, self).write(cr, uid, real_ids, values, context=context)
# set end_date for calendar searching
- if values.get('recurrency') and values.get('end_type', 'count') in ('count', unicode('count')) and \
- (values.get('rrule_type') or values.get('count') or values.get('start') or values.get('stop')):
- for id in real_ids:
- final_date = self._get_recurrency_end_date(cr, uid, id, context=context)
- super(calendar_event, self).write(cr, uid, [id], {'final_date': final_date}, context=context)
+ if any(field in values for field in ['recurrency', 'end_type', 'count', 'rrule_type', 'start', 'stop']):
+ for event in self.browse(cr, uid, real_ids, context=context):
+ if event.recurrency and event.end_type in ('count', unicode('count')):
+ final_date = self._get_recurrency_end_date(cr, uid, event.id, context=context)
+ super(calendar_event, self).write(cr, uid, [event.id], {'final_date': final_date}, context=context)
attendees_create = False
if values.get('partner_ids', False):
@@ -1694,7 +1695,8 @@ class calendar_event(osv.Model):
for r in result:
if r['user_id']:
user_id = type(r['user_id']) in (tuple, list) and r['user_id'][0] or r['user_id']
- if user_id == uid:
+ partner_id = self.pool['res.users'].browse(cr, uid, uid, context).partner_id.id
+ if user_id == uid or partner_id in r.get("partner_ids", []):
continue
if r['class'] == 'private':
for f in r.keys():
diff --git a/addons/delivery/models/stock_picking.py b/addons/delivery/models/stock_picking.py
index 6da3849839f..7287145bc0f 100644
--- a/addons/delivery/models/stock_picking.py
+++ b/addons/delivery/models/stock_picking.py
@@ -49,8 +49,11 @@ class StockPicking(models.Model):
_inherit = 'stock.picking'
def _default_uom(self):
- uom_categ_id = self.env.ref('product.product_uom_categ_kgm').id
- return self.env['product.uom'].search([('category_id', '=', uom_categ_id), ('factor', '=', 1)], limit=1)
+ weight_uom_id = self.env.ref('product.product_uom_kgm', raise_if_not_found=False)
+ if not weight_uom_id:
+ uom_categ_id = self.env.ref('product.product_uom_categ_kgm').id
+ weight_uom_id = self.env['product.uom'].search([('category_id', '=', uom_categ_id), ('factor', '=', 1)], limit=1)
+ return weight_uom_id
@api.one
@api.depends('pack_operation_ids')
diff --git a/addons/hr/models/res_partner.py b/addons/hr/models/res_partner.py
index 8c306f778da..e746e20c140 100644
--- a/addons/hr/models/res_partner.py
+++ b/addons/hr/models/res_partner.py
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
from odoo import api, models
+from openerp.exceptions import AccessError
class Partner(models.Model):
@@ -11,12 +12,10 @@ class Partner(models.Model):
""" Extend the mail's static mention suggestions by adding the employees. """
suggestions = super(Partner, self).get_static_mention_suggestions()
- employee_group_id = self.env['ir.model.data'].xmlid_to_res_id('base.group_user')
- self._cr.execute("""
- SELECT P.id, P.name, P.email
- FROM res_users U
- INNER JOIN res_groups_users_rel R ON U.id = R.uid
- INNER JOIN res_partner P ON P.id = U.partner_id
- WHERE R.gid = %s AND U.active = 't'""", (employee_group_id,))
- suggestions.append(self._cr.dictfetchall())
- return suggestions
+ try:
+ employee_group = self.env.ref('base.group_user')
+ for user in employee_group.users:
+ suggestions.append((user.partner_id.id, user.name, user.email))
+ return suggestions
+ except AccessError:
+ return suggestions
diff --git a/addons/mail/models/mail_followers.py b/addons/mail/models/mail_followers.py
index 6c066b123dd..14c1cbddbda 100644
--- a/addons/mail/models/mail_followers.py
+++ b/addons/mail/models/mail_followers.py
@@ -112,23 +112,31 @@ class Followers(models.Model):
# Modifying followers change access rights to individual documents. As the
# cache may contain accessible/inaccessible data, one has to refresh it.
#
+ @api.multi
+ def _invalidate_documents(self):
+ """ Invalidate the cache of the documents followed by ``self``. """
+ for record in self:
+ if record.res_id:
+ self.env[record.res_model].invalidate_cache(ids=[record.res_id])
+
@api.model
def create(self, vals):
res = super(Followers, self).create(vals)
- self.invalidate_cache()
+ res._invalidate_documents()
return res
@api.multi
def write(self, vals):
+ if 'res_model' in vals or 'res_id' in vals:
+ self._invalidate_documents()
res = super(Followers, self).write(vals)
- self.invalidate_cache()
+ self._invalidate_documents()
return res
@api.multi
def unlink(self):
- res = super(Followers, self).unlink()
- self.invalidate_cache()
- return res
+ self._invalidate_documents()
+ return super(Followers, self).unlink()
_sql_constraints = [
('mail_followers_res_partner_res_model_id_uniq', 'unique(res_model,res_id,partner_id)', 'Error, a partner cannot follow twice the same object.'),
diff --git a/addons/mail/models/mail_message.py b/addons/mail/models/mail_message.py
index e927d84ca0c..c6d306c65a9 100644
--- a/addons/mail/models/mail_message.py
+++ b/addons/mail/models/mail_message.py
@@ -854,6 +854,13 @@ class Message(models.Model):
message_id = tools.generate_tracking_message_id('private')
return message_id
+ @api.multi
+ def _invalidate_documents(self):
+ """ Invalidate the cache of the documents followed by ``self``. """
+ for record in self:
+ if record.res_id:
+ self.env[record.model].invalidate_cache(ids=[record.res_id])
+
@api.model
def create(self, values):
# coming from mail.js that does not have pid in its values
@@ -870,6 +877,7 @@ class Message(models.Model):
values['record_name'] = self._get_record_name(values)
message = super(Message, self).create(values)
+ message._invalidate_documents()
message._notify(force_send=self.env.context.get('mail_notify_force_send', True),
user_signature=self.env.context.get('mail_notify_user_signature', True))
@@ -883,6 +891,14 @@ class Message(models.Model):
return super(Message, self).read(fields=fields, load=load)
@api.multi
+ def write(self, vals):
+ if 'model' in vals or 'res_id' in vals:
+ self._invalidate_documents()
+ res = super(Message, self).write(vals)
+ self._invalidate_documents()
+ return res
+
+ @api.multi
def unlink(self):
# cascade-delete attachments that are directly attached to the message (should only happen
# for mail.messages that act as parent for a standalone mail.mail record).
@@ -890,6 +906,7 @@ class Message(models.Model):
self.mapped('attachment_ids').filtered(
lambda attach: attach.res_model == self._name and (attach.res_id in self.ids or attach.res_id == 0)
).unlink()
+ self._invalidate_documents()
return super(Message, self).unlink()
#------------------------------------------------------
diff --git a/addons/mail/tests/test_mail_features.py b/addons/mail/tests/test_mail_features.py
index 2766d0d7ced..82dc2e05363 100644
--- a/addons/mail/tests/test_mail_features.py
+++ b/addons/mail/tests/test_mail_features.py
@@ -25,6 +25,17 @@ class TestMailFeatures(TestMail):
'login': 'b4r+_#_R3wl$$', 'alias_name': 'b4r+_#_R3wl$$'})
self.assertEqual(user_barty.alias_name, 'b4r+_-_r3wl-', 'Disallowed chars should be replaced by hyphens')
+ def test_10_cache_invalidation(self):
+ """ Test that creating a mail-thread record does not invalidate the whole cache. """
+ # make a new record in cache
+ record = self.env['res.partner'].new({'name': 'Brave New Partner'})
+ self.assertTrue(record.name)
+
+ # creating a mail-thread record should not invalidate the whole cache
+ self.env['res.partner'].create({'name': 'Actual Partner'})
+ self.assertTrue(record.name)
+
+
@mute_logger('openerp.addons.mail.models.mail_mail')
def test_needaction(self):
na_emp1_base = self.env['mail.message'].sudo(self.user_employee)._needaction_count(domain=[])
diff --git a/addons/mail/tests/test_mail_message.py b/addons/mail/tests/test_mail_message.py
index 6407d97c9cf..2be7537ce75 100644
--- a/addons/mail/tests/test_mail_message.py
+++ b/addons/mail/tests/test_mail_message.py
@@ -251,3 +251,8 @@ class TestMailMessage(TestMail):
msg.toggle_message_starred()
self.assertFalse(msg.starred)
self.assertTrue(msg_emp.starred)
+
+ def test_60_cache_invalidation(self):
+ msg_cnt = len(self.group_pigs.message_ids)
+ self.group_pigs.message_post(body='Hi!', subject='test')
+ self.assertEqual(len(self.group_pigs.message_ids), msg_cnt + 1)
diff --git a/addons/project/project.py b/addons/project/project.py
index 35c0a1b6340..a3a10c5f5bb 100644
--- a/addons/project/project.py
+++ b/addons/project/project.py
@@ -415,12 +415,11 @@ class task(osv.osv):
values = {}
if project_id:
project = self.pool.get('project.project').browse(cr, uid, project_id, context=context)
- if project and project.partner_id:
- values['partner_id'] = project.partner_id.id
- values['stage_id'] = self.stage_find(cr, uid, [], project_id, [('fold', '=', False)], context=context)
+ if project.exists():
+ values['partner_id'] = project.partner_id.id
+ values['stage_id'] = self.stage_find(cr, uid, [], project_id, [('fold', '=', False)], context=context)
else:
values['stage_id'] = False
- values['partner_id'] = False
return {'value': values}
def onchange_user_id(self, cr, uid, ids, user_id, context=None):
diff --git a/addons/purchase/purchase.py b/addons/purchase/purchase.py
index 8919d0bba6f..f3cafdadec0 100644
--- a/addons/purchase/purchase.py
+++ b/addons/purchase/purchase.py
@@ -23,7 +23,12 @@ class PurchaseOrder(models.Model):
amount_untaxed = amount_tax = 0.0
for line in order.order_line:
amount_untaxed += line.price_subtotal
- amount_tax += line.price_tax
+ # FORWARDPORT UP TO 10.0
+ if order.company_id.tax_calculation_rounding_method == 'round_globally':
+ taxes = line.taxes_id.compute_all(line.price_unit, line.order_id.currency_id, line.product_qty, product=line.product_id, partner=line.order_id.partner_id)
+ amount_tax += sum(t.get('amount', 0.0) for t in taxes.get('taxes', []))
+ else:
+ amount_tax += line.price_tax
order.update({
'amount_untaxed': order.currency_id.round(amount_untaxed),
'amount_tax': order.currency_id.round(amount_tax),
diff --git a/addons/sale/sale.py b/addons/sale/sale.py
index b05ef447b42..bbbfd29b658 100644
--- a/addons/sale/sale.py
+++ b/addons/sale/sale.py
@@ -30,7 +30,13 @@ class SaleOrder(models.Model):
amount_untaxed = amount_tax = 0.0
for line in order.order_line:
amount_untaxed += line.price_subtotal
- amount_tax += line.price_tax
+ # FORWARDPORT UP TO 10.0
+ if order.company_id.tax_calculation_rounding_method == 'round_globally':
+ price = line.price_unit * (1 - (line.discount or 0.0) / 100.0)
+ taxes = line.tax_id.compute_all(price, line.order_id.currency_id, line.product_uom_qty, product=line.product_id, partner=line.order_id.partner_id)
+ amount_tax += sum(t.get('amount', 0.0) for t in taxes.get('taxes', []))
+ else:
+ amount_tax += line.price_tax
order.update({
'amount_untaxed': order.pricelist_id.currency_id.round(amount_untaxed),
'amount_tax': order.pricelist_id.currency_id.round(amount_tax),
diff --git a/addons/web/views/webclient_templates.xml b/addons/web/views/webclient_templates.xml
index 0b5acc4af4c..482b17c5f9f 100644
--- a/addons/web/views/webclient_templates.xml
+++ b/addons/web/views/webclient_templates.xml
@@ -246,7 +246,7 @@
<div class="form-group field-password">
<label for="password" class="control-label">Password</label>
- <input type="password" name="password" id="password" class="form-control" required="required" t-att-autofocus="'autofocus' if login else None"/>
+ <input type="password" name="password" id="password" class="form-control" required="required" t-att-autofocus="'autofocus' if login else None" maxlength="4096"/>
</div>
<p class="alert alert-danger" t-if="error">
diff --git a/addons/web_editor/static/src/js/summernote.js b/addons/web_editor/static/src/js/summernote.js
index 1abcd97245c..397d903be21 100644
--- a/addons/web_editor/static/src/js/summernote.js
+++ b/addons/web_editor/static/src/js/summernote.js
@@ -134,7 +134,7 @@ dom.hasProgrammaticStyle = function (node) {
};
dom.mergeFilter = function (prev, cur, parent) {
// merge text nodes
- if (prev && (dom.isText(prev) || ("H1 H2 H3 H4 H5 H6 LI P".indexOf(prev.tagName) !== -1 && prev !== cur.parentNode)) && dom.isText(cur)) {
+ if (prev && (dom.isText(prev) || (['H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'LI', 'P'].indexOf(prev.tagName) !== -1 && prev !== cur.parentNode)) && dom.isText(cur)) {
return true;
}
if (prev && prev.tagName === "P" && dom.isText(cur)) {
diff --git a/addons/website/views/website_templates.xml b/addons/website/views/website_templates.xml
index 343fcf1e648..808604c5885 100644
--- a/addons/website/views/website_templates.xml
+++ b/addons/website/views/website_templates.xml
@@ -78,7 +78,7 @@
<template id="layout" name="Main layout">&lt;!DOCTYPE html&gt;
<html t-att-lang="lang and lang.replace('_', '-')"
- t-att-data-website-id="website.id if (editable or translatable) and website else None"
+ t-att-data-website-id="website.id if website else None"
t-att-data-editable="'1' if editable else None"
t-att-data-translatable="'1' if translatable else None"
t-att-data-edit_translations="'1' if edit_translations else None"
diff --git a/addons/website_sale/models/sale_order.py b/addons/website_sale/models/sale_order.py
index f793c833b38..a423e55b6af 100644
--- a/addons/website_sale/models/sale_order.py
+++ b/addons/website_sale/models/sale_order.py
@@ -259,6 +259,27 @@ class Website(models.Model):
return [("sale_ok", "=", True)]
@api.multi
+ def _prepare_sale_order_values(self, partner, pricelist):
+ self.ensure_one()
+ affiliate_id = request.session.get('affiliate_id')
+ salesperson_id = affiliate_id if self.env['res.users'].sudo().browse(affiliate_id).exists() else request.website.salesperson_id.id
+ addr = partner.address_get(['delivery', 'invoice'])
+ values = {
+ 'partner_id': partner.id,
+ 'pricelist_id': pricelist.id,
+ 'payment_term_id': partner.property_payment_term_id.id,
+ 'team_id': self.salesteam_id.id,
+ 'partner_invoice_id': addr['invoice'],
+ 'partner_shipping_id': addr['delivery'],
+ 'user_id': salesperson_id or self.salesperson_id.id,
+ }
+ company = self.company_id or pricelist.company_id
+ if company:
+ values['company_id'] = company.id
+
+ return values
+
+ @api.multi
def sale_get_order(self, force_create=False, code=None, update_pricelist=False, force_pricelist=False):
""" Return the current sale order after mofications specified by params.
:param bool force_create: Create sale order if not already existing
@@ -289,25 +310,8 @@ class Website(models.Model):
# create so if needed
if not sale_order and (force_create or code):
# TODO cache partner_id session
- affiliate_id = request.session.get('affiliate_id')
- if self.env['res.users'].sudo().browse(affiliate_id).exists():
- salesperson_id = affiliate_id
- else:
- salesperson_id = request.website.salesperson_id.id
- addr = partner.address_get(['delivery', 'invoice'])
- so_data = {
- 'partner_id': partner.id,
- 'pricelist_id': pricelist_id,
- 'payment_term_id': partner.property_payment_term_id.id,
- 'team_id': self.salesteam_id.id,
- 'partner_invoice_id': addr['invoice'],
- 'partner_shipping_id': addr['delivery'],
- 'user_id': salesperson_id or self.salesperson_id.id,
- }
- company = self.company_id or self.env['product.pricelist'].browse(pricelist_id).sudo().company_id
- if company:
- so_data['company_id'] = company.id
-
+ pricelist = self.env['product.pricelist'].browse(pricelist_id).sudo()
+ so_data = self._prepare_sale_order_values(partner, pricelist)
sale_order = self.env['sale.order'].sudo().create(so_data)
# set fiscal position
diff --git a/addons/website_sale/views/templates.xml b/addons/website_sale/views/templates.xml
index ac73d9d8b4b..d61c514bb89 100644
--- a/addons/website_sale/views/templates.xml
+++ b/addons/website_sale/views/templates.xml
@@ -422,7 +422,8 @@
<template id="product_price">
<div itemprop="offers" itemscope="itemscope" itemtype="http://schema.org/Offer" class="product_price mt16">
- <h4 class="oe_price_h4 css_editable_mode_hidden">
+ <h4 class="oe_price_h4 css_editable_mode_hidden decimal_precision"
+ t-att-data-precision="user_id.partner_id.property_product_pricelist.currency_id.rounding">
<span class="text-danger oe_default_price" style="text-decoration: line-through; white-space: nowrap;"
t-field="product.lst_price"
t-field-options='{
diff --git a/addons/website_sale_stock/__init__.py b/addons/website_sale_stock/__init__.py
index 67dee8c60db..5f9ef702708 100644
--- a/addons/website_sale_stock/__init__.py
+++ b/addons/website_sale_stock/__init__.py
@@ -1,2 +1,3 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
+import models \ No newline at end of file
diff --git a/addons/website_sale_stock/models/__init__.py b/addons/website_sale_stock/models/__init__.py
new file mode 100644
index 00000000000..b8b239729b6
--- /dev/null
+++ b/addons/website_sale_stock/models/__init__.py
@@ -0,0 +1 @@
+import website \ No newline at end of file
diff --git a/addons/website_sale_stock/models/website.py b/addons/website_sale_stock/models/website.py
new file mode 100644
index 00000000000..5671fc518d7
--- /dev/null
+++ b/addons/website_sale_stock/models/website.py
@@ -0,0 +1,15 @@
+# -*- coding: utf-8 -*-
+from odoo import api, models
+
+class Website(models.Model):
+ _inherit = 'website'
+
+ @api.multi
+ def _prepare_sale_order_values(self, partner, pricelist):
+ self.ensure_one()
+ values = super(Website, self)._prepare_sale_order_values(partner, pricelist)
+ if values['company_id']:
+ warehouses = self.env['stock.warehouse'].search([('company_id', '=', values['company_id'])], limit=1)
+ if warehouses:
+ values['warehouse_id'] = warehouses.id
+ return values
diff --git a/addons/website_slides/models/slides.py b/addons/website_slides/models/slides.py
index 1b7ce21ddff..b8a28b0332e 100644
--- a/addons/website_slides/models/slides.py
+++ b/addons/website_slides/models/slides.py
@@ -461,7 +461,7 @@ class Slide(models.Model):
base_url = self.env['ir.config_parameter'].get_param('web.base.url')
for slide in self.filtered(lambda slide: slide.website_published and slide.channel_id.publish_template_id):
publish_template = slide.channel_id.publish_template_id
- html_body = publish_template.with_context({'base_url': base_url}).render_template(publish_template.body_html, 'slide.slide', slide.id)
+ html_body = publish_template.with_context(base_url=base_url).render_template(publish_template.body_html, 'slide.slide', slide.id)
subject = publish_template.render_template(publish_template.subject, 'slide.slide', slide.id)
slide.channel_id.message_post(
subject=subject,
@@ -472,7 +472,7 @@ class Slide(models.Model):
@api.one
def send_share_email(self, email):
base_url = self.env['ir.config_parameter'].get_param('web.base.url')
- return self.channel_id.share_template_id.with_context({'email': email, 'base_url': base_url}).send_mail(self.id)
+ return self.channel_id.share_template_id.with_context(email=email, base_url=base_url).send_mail(self.id)
# --------------------------------------------------
# Parsing methods
diff --git a/doc/cla/corporate/bacgroup.md b/doc/cla/corporate/bacgroup.md
new file mode 100644
index 00000000000..140a12e2e31
--- /dev/null
+++ b/doc/cla/corporate/bacgroup.md
@@ -0,0 +1,21 @@
+Honduras, 2017-01-12
+
+Business Analytics Consulting Group S.A. de C.V. agrees to the terms of the Odoo Corporate Contributor License
+Agreement v1.0.
+
+I declare that I am authorized and able to make this agreement and sign this
+declaration.
+
+Signed,
+
+Leandro Gabriel Di Pasquale dipasquale@bacgroup.net https://github.com/leodipasquale
+
+List of contributors:
+
+Allan Daniel Aguilar Archaga aguilar@bacgroup.net https://github.com/allanaguilar
+Cesar Alejandro Rodriguez Castillo rodriguez@bacgroup.net https://github.com/hondurasopen
+Dewin Javier Garcia Armijo garcia@bacgroup.net https://github.com/dewingarcia
+Jose Reynaldo Cabrera Ayala cabrera@bacgroup.net https://github.com/jcabrera1990
+Leandro Gabriel Di Pasquale dipasquale@bacgroup.net https://github.com/leodipasquale
+Edwin Omar McClellan Salinas mcclellan@bacgroup.net https://github.com/prometeomcclellan
+Salvatore Josue Trimarchi Pinto trimarchi@bacgroup.net https://github.com/salvatoretrimarchi
diff --git a/doc/cla/individual/boogerlad.md b/doc/cla/individual/boogerlad.md
new file mode 100644
index 00000000000..af450cfd20a
--- /dev/null
+++ b/doc/cla/individual/boogerlad.md
@@ -0,0 +1,11 @@
+canada, 2017/1/3
+
+I hereby agree to the terms of the Odoo Individual Contributor License
+Agreement v1.0.
+
+I declare that I am authorized and able to make this agreement and sign this
+declaration.
+
+Signed,
+
+kevin hua boogerlad@gmail.com https://github.com/boogerlad
diff --git a/openerp/addons/base/res/ir_property.py b/openerp/addons/base/res/ir_property.py
index 60cc18698f7..1af1a047315 100644
--- a/openerp/addons/base/res/ir_property.py
+++ b/openerp/addons/base/res/ir_property.py
@@ -263,13 +263,13 @@ class Property(models.Model):
elif value <= 0 and operator == '>=':
operator = '<'
include_zero = True
- elif value <= 0 and operator == '>':
+ elif value < 0 and operator == '>':
operator = '<='
include_zero = True
elif value >= 0 and operator == '<=':
operator = '>'
include_zero = True
- elif value >= 0 and operator == '<':
+ elif value > 0 and operator == '<':
operator = '>='
include_zero = True
diff --git a/openerp/addons/base/res/res_lang.py b/openerp/addons/base/res/res_lang.py
index 0b7d1c4623e..fc45d18e420 100644
--- a/openerp/addons/base/res/res_lang.py
+++ b/openerp/addons/base/res/res_lang.py
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
+import json
import locale
import logging
import re
@@ -69,7 +70,7 @@ class Lang(models.Model):
'Provided as the thousand separator in each case.')
for lang in self:
try:
- if not all(isinstance(x, int) for x in eval(lang.grouping)):
+ if not all(isinstance(x, int) for x in json.loads(lang.grouping)):
raise ValidationError(warning)
except Exception:
raise ValidationError(warning)
diff --git a/openerp/http.py b/openerp/http.py
index c54dea5a17a..0c4ea968f06 100644
--- a/openerp/http.py
+++ b/openerp/http.py
@@ -842,7 +842,7 @@ more details.
passing the `csrf=False` parameter to the `route` decorator.
""", request.httprequest.path)
- raise werkzeug.exceptions.BadRequest('Invalid CSRF Token')
+ raise werkzeug.exceptions.BadRequest('Session expired (invalid CSRF token)')
r = self._call_function(**self.params)
if not r: