diff --git a/addons/account/models/account_invoice.py b/addons/account/models/account_invoice.py
index 146212f3e2d..627db50a12d 100644
--- a/addons/account/models/account_invoice.py
+++ b/addons/account/models/account_invoice.py
@@ -978,7 +978,7 @@ class AccountInvoice(models.Model):
values['invoice_line_ids'] = self._refund_cleanup_lines(invoice.invoice_line_ids)
- tax_lines = filter(lambda l: l.manual, invoice.tax_line_ids)
+ tax_lines = invoice.tax_line_ids
values['tax_line_ids'] = self._refund_cleanup_lines(tax_lines)
if journal_id:
diff --git a/addons/account/wizard/account_invoice_refund.py b/addons/account/wizard/account_invoice_refund.py
index 3467b2e74a6..b45e4a5970c 100644
--- a/addons/account/wizard/account_invoice_refund.py
+++ b/addons/account/wizard/account_invoice_refund.py
@@ -58,7 +58,6 @@ class AccountInvoiceRefund(models.TransientModel):
date = form.date or False
description = form.description or inv.name
refund = inv.refund(form.date_invoice, date, description, inv.journal_id.id)
- refund.compute_taxes()
created_inv.append(refund.id)
if mode in ('cancel', 'modify'):
diff --git a/addons/point_of_sale/models/pos_order.py b/addons/point_of_sale/models/pos_order.py
index 6db76a52243..e56e012533a 100644
--- a/addons/point_of_sale/models/pos_order.py
+++ b/addons/point_of_sale/models/pos_order.py
@@ -562,7 +562,7 @@ class PosOrder(models.Model):
'location_id': location_id,
'location_dest_id': destination_id,
}
- pos_qty = any([x.qty >= 0 for x in order.lines])
+ pos_qty = any([x.qty > 0 for x in order.lines])
if pos_qty:
order_picking = Picking.create(picking_vals.copy())
neg_qty = any([x.qty < 0 for x in order.lines])
diff --git a/addons/point_of_sale/views/pos_session_view.xml b/addons/point_of_sale/views/pos_session_view.xml
index d6fb4cc6953..5dfe4acdb1e 100644
--- a/addons/point_of_sale/views/pos_session_view.xml
+++ b/addons/point_of_sale/views/pos_session_view.xml
@@ -154,7 +154,7 @@
-
+
diff --git a/addons/sale/sale_analytic.py b/addons/sale/sale_analytic.py
index 622d2520c63..0f07cff230f 100644
--- a/addons/sale/sale_analytic.py
+++ b/addons/sale/sale_analytic.py
@@ -13,7 +13,19 @@ class SaleOrderLine(models.Model):
lines = {}
if not domain:
# To filter on analyic lines linked to an expense
- domain = [('so_line', 'in', self.ids), ('amount', '<=', 0.0)]
+ expense_type_id = self.env.ref('account.data_account_type_expenses', raise_if_not_found=False)
+ expense_type_id = expense_type_id and expense_type_id.id
+ domain = [
+ ('so_line', 'in', self.ids),
+ '|',
+ ('amount', '<', 0),
+ '&',
+ ('amount', '=', 0),
+ '|',
+ ('move_id', '=', False),
+ ('move_id.account_id.user_type_id', '=', expense_type_id)
+ ]
+
data = self.env['account.analytic.line'].read_group(
domain,
['so_line', 'unit_amount', 'product_uom_id'], ['product_uom_id', 'so_line'], lazy=False
diff --git a/addons/sale_timesheet/models/sale_timesheet.py b/addons/sale_timesheet/models/sale_timesheet.py
index 8d2085aecc9..bd312081201 100644
--- a/addons/sale_timesheet/models/sale_timesheet.py
+++ b/addons/sale_timesheet/models/sale_timesheet.py
@@ -251,7 +251,20 @@ class SaleOrderLine(models.Model):
def _compute_analytic(self, domain=None):
if not domain:
# To filter on analyic lines linked to an expense
- domain = [('so_line', 'in', self.ids), '|', ('amount', '<=', 0.0), ('project_id', '!=', False)]
+ expense_type_id = self.env.ref('account.data_account_type_expenses', raise_if_not_found=False)
+ expense_type_id = expense_type_id and expense_type_id.id
+ domain = [
+ ('so_line', 'in', self.ids),
+ '|',
+ '|',
+ ('amount', '<', 0.0),
+ ('project_id', '!=', False),
+ '&',
+ ('amount', '=', 0),
+ '|',
+ ('move_id', '=', False),
+ ('move_id.account_id.user_type_id', '=', expense_type_id)
+ ]
return super(SaleOrderLine, self)._compute_analytic(domain=domain)
@api.model
diff --git a/doc/cla/individual/ttpphuctran.md b/doc/cla/individual/ttpphuctran.md
new file mode 100644
index 00000000000..2cbbdd58851
--- /dev/null
+++ b/doc/cla/individual/ttpphuctran.md
@@ -0,0 +1,11 @@
+Vietnam, 2017-04-10
+
+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,
+
+Tran Thanh Phuc ttp.phuctran@gmail.com https://github.com/ttpphuctran
diff --git a/openerp/addons/base/ir/ir_rule.py b/openerp/addons/base/ir/ir_rule.py
index 5e571c1347b..99366eebe8e 100644
--- a/openerp/addons/base/ir/ir_rule.py
+++ b/openerp/addons/base/ir/ir_rule.py
@@ -101,9 +101,8 @@ class IrRule(models.Model):
group_domains = defaultdict(list) # {group: list of domains}
for rule in rules.sudo():
dom = expression.normalize_domain(rule_domain[rule.id])
- for group in rule.groups:
- if group in user.groups_id:
- group_domains[group].append(dom)
+ if rule.groups & user.groups_id:
+ group_domains[rule.groups[0]].append(dom)
if not rule.groups:
global_domains.append(dom)
diff --git a/openerp/addons/test_new_api/ir.model.access.csv b/openerp/addons/test_new_api/ir.model.access.csv
index a8d921c30ae..3d078fd4427 100644
--- a/openerp/addons/test_new_api/ir.model.access.csv
+++ b/openerp/addons/test_new_api/ir.model.access.csv
@@ -13,3 +13,4 @@ access_test_function_counter,access_test_function_counter,model_test_old_api_fun
access_domain_bool,access_domain_bool,model_domain_bool,,1,1,1,1
access_test_new_api_foo,access_test_new_api_foo,model_test_new_api_foo,,1,1,1,1
access_test_new_api_bar,access_test_new_api_bar,model_test_new_api_bar,,1,1,1,1
+access_test_new_api_recursive,access_test_new_api_recursive,model_test_new_api_recursive,,1,1,1,1
diff --git a/openerp/addons/test_new_api/models.py b/openerp/addons/test_new_api/models.py
index 620202c0576..284196c3a88 100644
--- a/openerp/addons/test_new_api/models.py
+++ b/openerp/addons/test_new_api/models.py
@@ -321,3 +321,19 @@ class Bar(models.Model):
def _compute_foo(self):
for bar in self:
bar.foo = self.env['test_new_api.foo'].search([('name', '=', bar.name)], limit=1)
+
+
+class ComputeRecursive(models.Model):
+ _name = 'test_new_api.recursive'
+
+ name = fields.Char(required=True)
+ parent = fields.Many2one('test_new_api.recursive')
+ display_name = fields.Char(compute='_compute_display_name', store=True)
+
+ @api.depends('name', 'parent.display_name')
+ def _compute_display_name(self):
+ for rec in self:
+ if rec.parent:
+ rec.display_name = rec.parent.display_name + " / " + rec.name
+ else:
+ rec.display_name = rec.name
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 323c2c2c31d..70cfc50faae 100644
--- a/openerp/addons/test_new_api/tests/test_new_fields.py
+++ b/openerp/addons/test_new_api/tests/test_new_fields.py
@@ -180,6 +180,29 @@ class TestNewFields(common.TransactionCase):
cath.parent = finn
self.assertEqual(ewan.display_name, "Gabriel / Finnley / Catherine / Ewan")
+ def test_12_recursive_recompute(self):
+ """ test recomputation on recursively dependent field """
+ a = self.env['test_new_api.recursive'].create({'name': 'A'})
+ b = self.env['test_new_api.recursive'].create({'name': 'B', 'parent': a.id})
+ c = self.env['test_new_api.recursive'].create({'name': 'C', 'parent': b.id})
+ d = self.env['test_new_api.recursive'].create({'name': 'D', 'parent': c.id})
+ self.assertEqual(a.display_name, 'A')
+ self.assertEqual(b.display_name, 'A / B')
+ self.assertEqual(c.display_name, 'A / B / C')
+ self.assertEqual(d.display_name, 'A / B / C / D')
+
+ b.parent = False
+ self.assertEqual(a.display_name, 'A')
+ self.assertEqual(b.display_name, 'B')
+ self.assertEqual(c.display_name, 'B / C')
+ self.assertEqual(d.display_name, 'B / C / D')
+
+ b.name = 'X'
+ self.assertEqual(a.display_name, 'A')
+ self.assertEqual(b.display_name, 'X')
+ self.assertEqual(c.display_name, 'X / C')
+ self.assertEqual(d.display_name, 'X / C / D')
+
def test_12_cascade(self):
""" test computed field depending on computed field """
message = self.env.ref('test_new_api.message_0_0')
diff --git a/openerp/fields.py b/openerp/fields.py
index 4d01054be0b..dabf83f0923 100644
--- a/openerp/fields.py
+++ b/openerp/fields.py
@@ -639,7 +639,8 @@ class Field(object):
if field in fields:
self.recursive = True
- continue
+ if not i:
+ continue # self directly depends on self, give up
# add trigger on field and its inverses to recompute self
model._field_triggers.add(field, (self, '.'.join(path[:i] or ['id'])))