diff --git a/addons/account/models/account_bank_statement.py b/addons/account/models/account_bank_statement.py
index c90ac295e02..2e479d9009a 100644
--- a/addons/account/models/account_bank_statement.py
+++ b/addons/account/models/account_bank_statement.py
@@ -275,12 +275,12 @@ class AccountBankStatement(models.Model):
""" Changes statement state to Running."""
for statement in self:
if not statement.name:
- context = {'ir_sequence_date', statement.date}
+ context = {'ir_sequence_date': statement.date}
if statement.journal_id.sequence_id:
- st_number = statement.journal_id.sequence_id.with_context(context).next_by_id()
+ st_number = statement.journal_id.sequence_id.with_context(**context).next_by_id()
else:
SequenceObj = self.env['ir.sequence']
- st_number = SequenceObj.with_context(context).next_by_code('account.bank.statement')
+ st_number = SequenceObj.with_context(**context).next_by_code('account.bank.statement')
statement.name = st_number
statement.state = 'open'
diff --git a/addons/account/models/account_invoice.py b/addons/account/models/account_invoice.py
index 7e43efa745f..3983f2315e4 100644
--- a/addons/account/models/account_invoice.py
+++ b/addons/account/models/account_invoice.py
@@ -474,7 +474,8 @@ class AccountInvoice(models.Model):
else:
account_id = pay_account.id
payment_term_id = p.property_supplier_payment_term_id.id
- fiscal_position = p.property_account_position_id.id
+ addr = p.address_get(['delivery'])
+ fiscal_position = self.env['account.fiscal.position'].get_fiscal_position(p.id, delivery_id=addr['delivery'])
# If partner has no warning, check its company
diff --git a/addons/board/static/src/js/dashboard.js b/addons/board/static/src/js/dashboard.js
index 3cd376893c4..1a8a7c0e52c 100644
--- a/addons/board/static/src/js/dashboard.js
+++ b/addons/board/static/src/js/dashboard.js
@@ -181,6 +181,8 @@ var DashBoard = form_common.FormWidget.extend({
action = result,
view_mode = action_attrs.view_mode;
+ if (!action) { return; }
+
// evaluate action_attrs context and domain
action_attrs.context_string = action_attrs.context;
action_attrs.context = pyeval.eval(
diff --git a/addons/fleet/fleet.py b/addons/fleet/fleet.py
index 5d72c86a966..3a20219197e 100644
--- a/addons/fleet/fleet.py
+++ b/addons/fleet/fleet.py
@@ -177,6 +177,7 @@ class fleet_vehicle(osv.Model):
if context is None:
context = {}
if context.get('xml_id'):
+ context.pop('group_by', False)
res = self.pool.get('ir.actions.act_window').for_xml_id(cr, uid ,'fleet', context['xml_id'], context=context)
res['context'] = context
res['context'].update({'default_vehicle_id': ids[0]})
diff --git a/addons/mail/static/src/js/utils.js b/addons/mail/static/src/js/utils.js
index 7c64ba48df0..9ee90a52a8c 100644
--- a/addons/mail/static/src/js/utils.js
+++ b/addons/mail/static/src/js/utils.js
@@ -59,8 +59,9 @@ function _parse_and_transform(nodes, transform_function) {
}).join("");
}
-// suggested regexp (gruber url matching regexp, adapted to js, see https://gist.github.com/gruber/8891611)
-var url_regexp = /\b((?:https?:\/\/|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'".,<>?«»“”‘’]))/gi;
+// Suggested URL Javascript regex of http://stackoverflow.com/questions/3809401/what-is-a-good-regular-expression-to-match-a-url
+// Adapted to make http(s):// not required if (and only if) www. is given. So `should.notmatch` does not match.
+var url_regexp = /\b(?:https?:\/\/|(www\.))[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,13}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)/gi;
function add_link (node, transform_children) {
if (node.nodeType === 3) { // text node
return node.data.replace(url_regexp, function (url) {
diff --git a/addons/pos_mercury/static/src/xml/pos_mercury.xml b/addons/pos_mercury/static/src/xml/pos_mercury.xml
index 02f25588a3c..acf830e2543 100644
--- a/addons/pos_mercury/static/src/xml/pos_mercury.xml
+++ b/addons/pos_mercury/static/src/xml/pos_mercury.xml
@@ -25,16 +25,16 @@
- this.removeAttr("class");
- this.attr('t-attf-class', 'paymentline selected #{line.mercury_swipe_pending ? "o_pos_mercury_swipe_pending" : ""}');
+ this.removeAttr('class');
+ this.attr('t-attf-class', 'paymentline selected #{line.mercury_swipe_pending ? \'o_pos_mercury_swipe_pending\' : \'\'}');
- this.removeAttr("class");
- this.attr('t-attf-class', 'paymentline #{line.mercury_swipe_pending ? "o_pos_mercury_swipe_pending" : ""}');
+ this.removeAttr('class');
+ this.attr('t-attf-class', 'paymentline #{line.mercury_swipe_pending ? \'o_pos_mercury_swipe_pending\' : \'\'}');
- this.removeAttr("class");
- this.attr('t-attf-class', 'col-tendered edit #{line.mercury_swipe_pending ? "o_pos_mercury_swipe_pending" : ""}');
+ this.removeAttr('class');
+ this.attr('t-attf-class', 'col-tendered edit #{line.mercury_swipe_pending ? \'o_pos_mercury_swipe_pending\' : \'\'}');
diff --git a/addons/sale/sale.py b/addons/sale/sale.py
index 9300ad41f9f..7dedad3a23b 100644
--- a/addons/sale/sale.py
+++ b/addons/sale/sale.py
@@ -652,7 +652,7 @@ class SaleOrderLine(models.Model):
vals = line._prepare_order_line_procurement(group_id=line.order_id.procurement_group_id.id)
vals['product_qty'] = line.product_uom_qty - qty
- new_proc = self.env["procurement.order"].create(vals)
+ new_proc = self.env["procurement.order"].with_context(procurement_autorun_defer=True).create(vals)
new_procs += new_proc
new_procs.run()
return new_procs
diff --git a/addons/sale_stock/sale_stock.py b/addons/sale_stock/sale_stock.py
index 88c2a227489..6859cc035ba 100644
--- a/addons/sale_stock/sale_stock.py
+++ b/addons/sale_stock/sale_stock.py
@@ -121,8 +121,9 @@ class SaleOrderLine(models.Model):
def _compute_qty_delivered_updateable(self):
for line in self:
if line.product_id.type not in ('consu', 'product'):
- return super(SaleOrderLine, self)._compute_qty_delivered_updateable()
- line.qty_delivered_updateable = False
+ super(SaleOrderLine, line)._compute_qty_delivered_updateable()
+ else:
+ line.qty_delivered_updateable = False
@api.onchange('product_id')
def _onchange_product_id_set_customer_lead(self):
diff --git a/addons/web/static/src/js/views/form_view.js b/addons/web/static/src/js/views/form_view.js
index 8515eae3107..33ac59728d2 100644
--- a/addons/web/static/src/js/views/form_view.js
+++ b/addons/web/static/src/js/views/form_view.js
@@ -645,11 +645,11 @@ var FormView = View.extend(common.FieldManagerMixin, {
}
},
disable_button: function () {
- this.$('.oe_form_buttons').add(this.$buttons).find('button').addClass('o_disabled').prop('disabled', true);
+ this.$('.oe_form_buttons,.o_statusbar_buttons').add(this.$buttons).find('button').addClass('o_disabled').prop('disabled', true);
this.is_disabled = true;
},
enable_button: function () {
- this.$('.oe_form_buttons').add(this.$buttons).find('button.o_disabled').removeClass('o_disabled').prop('disabled', false);
+ this.$('.oe_form_buttons,.o_statusbar_buttons').add(this.$buttons).find('button.o_disabled').removeClass('o_disabled').prop('disabled', false);
this.is_disabled = false;
},
on_button_save: function() {
@@ -664,8 +664,10 @@ var FormView = View.extend(common.FieldManagerMixin, {
self.to_view_mode();
core.bus.trigger('do_reload_needaction');
core.bus.trigger('form_view_saved', self);
+ }).always(function() {
+ self.enable_button();
});
- }).always(function(){
+ }).fail(function(){
self.enable_button();
});
},
diff --git a/addons/website_event_track/controllers/main.py b/addons/website_event_track/controllers/main.py
index f9d9dec5c1c..373228a6908 100644
--- a/addons/website_event_track/controllers/main.py
+++ b/addons/website_event_track/controllers/main.py
@@ -38,7 +38,7 @@ class WebsiteEventTrackController(http.Controller):
for loc in locations.keys():
if locations[loc] and (locations[loc][-1][2] > start_date):
locations[loc][-1][3] += 1
- elif not locations[loc] or locations[loc][-1][2] < start_date:
+ elif not locations[loc] or locations[loc][-1][2] <= start_date:
locations[loc].append([False, locations[loc] and locations[loc][-1][2] or dates[0][0], start_date, 1])
dates[-1][1][loc] = locations[loc][-1]
forcetr = not bool(location)
diff --git a/openerp/addons/base/ir/ir_ui_view.py b/openerp/addons/base/ir/ir_ui_view.py
index ddac0bff423..fca64bd140f 100644
--- a/openerp/addons/base/ir/ir_ui_view.py
+++ b/openerp/addons/base/ir/ir_ui_view.py
@@ -300,6 +300,13 @@ actual arch.
raise ValidationError(_('Invalid view definition'))
return True
+ @api.constrains('inherit_id')
+ def _check_000_inheritance(self):
+ # NOTE: constraints methods are check alphabetically. Always ensure this method will be
+ # called before other constraint metheods to avoid infinite loop in `read_combined`.
+ if not self._check_recursion(parent='inherit_id'):
+ raise ValidationError(_('You cannot create recursive inherited views.'))
+
_sql_constraints = [
('inheritance_mode',
"CHECK (mode != 'extension' OR inherit_id IS NOT NULL)",
diff --git a/openerp/addons/base/tests/test_views.py b/openerp/addons/base/tests/test_views.py
index 7fbec8004e4..02cd3a98742 100644
--- a/openerp/addons/base/tests/test_views.py
+++ b/openerp/addons/base/tests/test_views.py
@@ -9,6 +9,7 @@ from lxml.builder import E
from psycopg2 import IntegrityError
+from openerp.exceptions import ValidationError
from openerp.tests import common
import openerp.tools
@@ -251,6 +252,25 @@ class TestViewInheritance(ViewCase):
self.View.default_view(
self.cr, self.uid, model=self.model, view_type='graph'))
+ def test_no_recursion(self):
+ r1 = self.makeView('R1')
+ with self.assertRaises(ValidationError), self.cr.savepoint():
+ self.View.write(self.cr, self.uid, r1, {'inherit_id': r1})
+
+ r2 = self.makeView('R2', r1)
+ r3 = self.makeView('R3', r2)
+ with self.assertRaises(ValidationError), self.cr.savepoint():
+ self.View.write(self.cr, self.uid, r2, {'inherit_id': r3})
+
+ with self.assertRaises(ValidationError), self.cr.savepoint():
+ self.View.write(self.cr, self.uid, r1, {'inherit_id': r3})
+
+ with self.assertRaises(ValidationError), self.cr.savepoint():
+ self.View.write(self.cr, self.uid, r1, {
+ 'inherit_id': r1,
+ 'arch': self.arch_for('itself', parent=True),
+ })
+
class TestApplyInheritanceSpecs(ViewCase):
""" Applies a sequence of inheritance specification nodes to a base
architecture. IO state parameters (cr, uid, model, context) are used for