diff --git a/addons/account/static/src/js/reconciliation/reconciliation_renderer.js b/addons/account/static/src/js/reconciliation/reconciliation_renderer.js
index 45ff78b7caf..f7ffbecc967 100644
--- a/addons/account/static/src/js/reconciliation/reconciliation_renderer.js
+++ b/addons/account/static/src/js/reconciliation/reconciliation_renderer.js
@@ -399,9 +399,9 @@ var LineRenderer = Widget.extend(FieldManagerMixin, {
.attr("data-content", qweb.render('reconciliation.line.mv_line.details', {'line': line}));
}
if (line.already_paid === false &&
- ((state.balance.amount_currency < 0 || line.partial_reconcile)
+ (((state.balance.amount_currency < 0 || line.partial_reconcile)
&& line.amount > 0 && state.st_line.amount > 0 && targetLineAmount < line.amount && partialDebitProps <= 1) ||
- ((state.balance.amount_currency > 0 || line.partial_reconcile)
+ ((state.balance.amount_currency > 0 || line.partial_reconcile))
&& line.amount < 0 && state.st_line.amount < 0 && targetLineAmount > line.amount && partialCreditProps <= 1)) {
var $cell = $line.find(line.amount > 0 ? '.cell_right' : '.cell_left');
var text;
diff --git a/addons/account/static/tests/reconciliation_tests.js b/addons/account/static/tests/reconciliation_tests.js
index 38a72820223..db8fa1e0104 100644
--- a/addons/account/static/tests/reconciliation_tests.js
+++ b/addons/account/static/tests/reconciliation_tests.js
@@ -395,6 +395,7 @@ var mv_lines = {
'[6,"",0,6]': [
{'account_type': "liquidity", 'amount_currency_str': "", 'currency_id': false, 'date_maturity': "2017-01-23", 'date': "2017-01-23", 'total_amount_str': "$ 376.00", 'partner_id': 7, 'account_name': "Bank", 'name': "BNK1/2017/0002: SUPP.OUT/2017/0002", 'partner_name': "ASUSTeK", 'total_amount_currency_str': "", 'id': 392, 'credit': 376.0, 'journal_id': "Bank", 'amount_str': "$ 376.00", 'debit': 0.0, 'account_code': "101401", 'ref': "BILL/2017/0003", 'already_paid': true},
{'account_type': "liquidity", 'amount_currency_str': "", 'currency_id': false, 'date_maturity': "2017-01-23", 'date': "2017-01-23", 'total_amount_str': "$ 100.00", 'partner_id': 8, 'account_name': "Bank", 'name': "BNK1/2017/0003: CUST.IN/2017/0001", 'partner_name': "Agrolait", 'total_amount_currency_str': "", 'id': 394, 'credit': 0.0, 'journal_id': "Bank", 'amount_str': "$ 100.00", 'debit': 100.0, 'account_code': "101401", 'ref': "", 'already_paid': true},
+ {'account_type': "payable", 'amount_currency_str': "", 'currency_id': false, 'date_maturity': "2017-02-28", 'date': "2017-01-01", 'total_amount_str': "$ 10,000.00", 'partner_id': 12, 'account_name': "Account Payable", 'name': "BILL/2017/0001", 'partner_name': "Camptocamp", 'total_amount_currency_str': "", 'id': 114, 'credit': 10000.0, 'journal_id': [2, "Vendor Bills"], 'amount_str': "$ 10,000.00", 'debit': 0.0, 'account_code': "111100", 'ref': "", 'already_paid': false},
{'account_type': "liquidity", 'amount_currency_str': "", 'currency_id': false, 'date_maturity': "2017-01-23", 'date': "2017-01-23", 'total_amount_str': "$ 525.50", 'partner_id': 8, 'account_name': "Bank", 'name': "BNK1/2017/0004: CUST.IN/2017/0002", 'partner_name': "Agrolait", 'total_amount_currency_str': "", 'id': 396, 'credit': 0.0, 'journal_id': "Bank", 'amount_str': "$ 525.50", 'debit': 525.5, 'account_code': "101401", 'ref': "INV/2017/0003", 'already_paid': true},
{'account_type': "receivable", 'amount_currency_str': "", 'currency_id': false, 'date_maturity': "2017-02-07", 'date': "2017-01-08", 'total_amount_str': "$ 650.00", 'partner_id': 8, 'account_name': "101200 Account Receivable", 'name': "INV/2017/0002", 'partner_name': "Agrolait", 'total_amount_currency_str': "", 'id': 109, 'credit': 0.0, 'journal_id': [1, "Customer Invoices"], 'amount_str': "$ 650.00", 'debit': 650.0, 'account_code': "101200", 'ref': "", 'already_paid': false},
{'account_type': "receivable", 'amount_currency_str': "", 'currency_id': false, 'date_maturity': "2017-02-22", 'date': "2017-01-23", 'total_amount_str': "$ 525.00", 'partner_id': 8, 'account_name': "101200 Account Receivable", 'name': "INV/2017/0004", 'partner_name': "Agrolait", 'total_amount_currency_str': "", 'id': 399, 'credit': 0.0, 'journal_id': [1, "Customer Invoices"], 'amount_str': "$ 525.00", 'debit': 525.0, 'account_code': "101200", 'ref': "", 'already_paid': false},
@@ -650,12 +651,12 @@ QUnit.module('account', {
clientAction.widgets[1].$('.accounting_view thead td:first').trigger('click');
assert.strictEqual(clientAction.widgets[1].$('.mv_line').length, 5, "should display 5 account move lines");
- assert.strictEqual(clientAction.widgets[1].$('.mv_line .cell_right:not(:empty)').length, 4, "should display only the credit account move lines (hide the debit)");
+ assert.strictEqual(clientAction.widgets[1].$('.mv_line .cell_right:not(:empty)').length, 3, "should display only the credit account move lines (hide the debit)");
assert.strictEqual(clientAction.widgets[1].$('.mv_line.already_reconciled').length, 3, "should display 3 already reconciled account move lines");
assert.strictEqual(clientAction.widgets[1].$('.mv_line').text().replace(/[\n\r\s]+/g, ' '),
- " 101401 2017-01-23 ASUSTeK: BNK1/2017/0002: SUPP.OUT/2017/0002 : BILL/2017/0003 $ 376.00 101401 2017-01-23 Agrolait: BNK1/2017/0003: CUST.IN/2017/0001 $ 100.00 101401 2017-01-23 Agrolait: BNK1/2017/0004: CUST.IN/2017/0002 : INV/2017/0003 $ 525.50 101200 2017-02-07 Agrolait: INV/2017/0002 $ 650.00 101200 2017-02-22 Agrolait: INV/2017/0004 $ 525.00 ",
+ " 101401 2017-01-23 ASUSTeK: BNK1/2017/0002: SUPP.OUT/2017/0002 : BILL/2017/0003 $ 376.00 101401 2017-01-23 Agrolait: BNK1/2017/0003: CUST.IN/2017/0001 $ 100.00 111100 2017-02-28 Camptocamp: BILL/2017/0001 $ 10,000.00 101401 2017-01-23 Agrolait: BNK1/2017/0004: CUST.IN/2017/0002 : INV/2017/0003 $ 525.50 101200 2017-02-07 Agrolait: INV/2017/0002 $ 650.00 ",
"should display 4 account move lines who contains the account_code, due_date, label and the credit");
- assert.strictEqual(clientAction.widgets[1].$('.mv_line .cell_left:not(:empty)').length, 1, "should display only 1 debit account move lines");
+ assert.strictEqual(clientAction.widgets[1].$('.mv_line .cell_left:not(:empty)').length, 2, "should display only 2 debit account move lines");
clientAction.widgets[1].$('.match_controls .fa-chevron-right').trigger('click');
assert.strictEqual(clientAction.widgets[1].$('.mv_line').text().replace(/[\n\r\s]+/g, ' '),
" 101200 2017-02-28 Camptocamp: INV/2017/0001 $ 4,610.00 111100 2017-02-28 Camptocamp: BILL/2017/0001 $ 10,000.00 111100 2017-02-28 ASUSTeK: BILL/2017/0002 $ 5,749.99 ",
@@ -807,8 +808,13 @@ QUnit.module('account', {
[6],
[{
partner_id: false,
- counterpart_aml_dicts:[],
- payment_aml_ids: [392],
+ counterpart_aml_dicts:[{
+ "counterpart_aml_id": 114,
+ "credit": 0,
+ "debit": 32.57999999999993,
+ "name": "BILL/2017/0001"
+ }],
+ payment_aml_ids: [],
new_aml_dicts: [],
}]
], "should call process_bank_statement_line with partial reconcile values");
@@ -837,7 +843,7 @@ QUnit.module('account', {
assert.notOk( widget.$('.cell_left .line_info_button').length, "should not display the partial reconciliation alert");
widget.$('.accounting_view thead td:first').trigger('click');
- widget.$('.match .cell_account_code:first').trigger('click');
+ widget.$('.match .mv_line[data-line-id=114] .cell_account_code').trigger('click');
assert.equal( widget.$('.accounting_view tbody .cell_left .line_info_button').length, 1, "should display the partial reconciliation alert");
assert.ok( widget.$('button.btn-primary:not(hidden)').length, "should not display the reconcile button");
assert.ok( widget.$('.text-danger:not(hidden)').length, "should display counterpart alert");
diff --git a/addons/hr_payroll/models/hr_payslip.py b/addons/hr_payroll/models/hr_payslip.py
index 93ee4d42393..c7d7cb6e407 100644
--- a/addons/hr_payroll/models/hr_payslip.py
+++ b/addons/hr_payroll/models/hr_payslip.py
@@ -180,9 +180,9 @@ class HrPayslip(models.Model):
for interval in day_intervals:
holiday = interval[2]['leaves'].holiday_id
current_leave_struct = leaves.setdefault(holiday.holiday_status_id, {
- 'name': holiday.holiday_status_id.name,
+ 'name': holiday.holiday_status_id.name or _('Global Leaves'),
'sequence': 5,
- 'code': holiday.holiday_status_id.name,
+ 'code': holiday.holiday_status_id.name or 'GLOBAL',
'number_of_days': 0.0,
'number_of_hours': 0.0,
'contract_id': contract.id,
diff --git a/addons/iap/models/iap.py b/addons/iap/models/iap.py
index 2f7053b7c74..13431caca7b 100644
--- a/addons/iap/models/iap.py
+++ b/addons/iap/models/iap.py
@@ -66,7 +66,7 @@ def jsonrpc(url, method='call', params=None):
e.data = response['error']['data']
raise e
return response.get('result')
- except (ValueError, requests.exceptions.ConnectionError, requests.exceptions.MissingSchema) as e:
+ except (ValueError, requests.exceptions.ConnectionError, requests.exceptions.MissingSchema, requests.exceptions.Timeout, requests.exceptions.HTTPError) as e:
raise exceptions.AccessError('The url that this service requested returned an error. Please contact the author the app. The url it tried to contact was ' + url)
#----------------------------------------------------------
@@ -78,7 +78,7 @@ class IapTransaction(object):
self.credit = None
@contextlib.contextmanager
-def charge(env, key, account_token, credit, description=None, credit_template=None):
+def charge(env, key, account_token, credit, description=None, credit_template=None, dbuuid=False):
"""
Account charge context manager: takes a hold for ``credit``
amount before executing the body, then captures it if there
@@ -102,6 +102,7 @@ def charge(env, key, account_token, credit, description=None, credit_template=No
'credit': credit,
'key': key,
'description': description,
+ 'dbuuid': dbuuid,
}
try:
transaction_token = jsonrpc(endpoint + '/iap/1/authorize', params=params)
diff --git a/addons/l10n_nl/data/account_data.xml b/addons/l10n_nl/data/account_data.xml
index 55cccae643d..78531cb730b 100644
--- a/addons/l10n_nl/data/account_data.xml
+++ b/addons/l10n_nl/data/account_data.xml
@@ -9,6 +9,9 @@
BTW 6%
+
+ BTW 9%
+
BTW 21%
@@ -18,6 +21,9 @@
BTW 6% EU
+
+ BTW 9% EU
+
BTW 21% EU
@@ -27,6 +33,9 @@
BTW 6% Niet EU
+
+ BTW 9% Niet EU
+
BTW 21% Niet EU
diff --git a/addons/l10n_nl/data/account_fiscal_position_tax_template.xml b/addons/l10n_nl/data/account_fiscal_position_tax_template.xml
index bb3153bab97..366ec73618f 100644
--- a/addons/l10n_nl/data/account_fiscal_position_tax_template.xml
+++ b/addons/l10n_nl/data/account_fiscal_position_tax_template.xml
@@ -16,6 +16,11 @@
+
+
+
+
+
+
+
+
+
@@ -109,6 +134,11 @@
+
+
+
+
+
@@ -124,6 +154,11 @@
+
+
+
+
+
@@ -139,6 +174,11 @@
+
+
+
+
+
diff --git a/addons/l10n_nl/data/account_tax_template.xml b/addons/l10n_nl/data/account_tax_template.xml
index e9b937b98f5..0dfd73d125e 100644
--- a/addons/l10n_nl/data/account_tax_template.xml
+++ b/addons/l10n_nl/data/account_tax_template.xml
@@ -20,7 +20,7 @@
10
- Verkopen/omzet laag
+ Verkopen/omzet laag 6%
6% BTW
percent
@@ -30,6 +30,19 @@
+
+ 10
+
+ Verkopen/omzet laag 9%
+ 9% BTW
+
+ percent
+
+
+ sale
+
+
+
5
@@ -73,7 +86,7 @@
10
- Verkopen/omzet laag diensten
+ Verkopen/omzet laag diensten 6%
6% BTW diensten
percent
@@ -83,6 +96,19 @@
+
+ 10
+
+ Verkopen/omzet laag diensten 9%
+ 9% BTW diensten
+
+ percent
+
+
+ sale
+
+
+
6
@@ -113,7 +139,7 @@
10
- BTW te vorderen laag (inkopen)
+ BTW te vorderen laag (inkopen) 6%
6% BTW
percent
@@ -123,10 +149,24 @@
+
+ 10
+
+ BTW te vorderen laag (inkopen) 9%
+ 9% BTW
+
+ percent
+
+
+ purchase
+
+
+
+
10
- BTW te vorderen laag (inkopen incl. BTW)
+ BTW te vorderen laag (inkopen incl. BTW) 6%
6% BTW Incl.
percent
@@ -137,6 +177,20 @@
+
+ 10
+
+ BTW te vorderen laag (inkopen incl. BTW) 9%
+ 9% BTW Incl.
+
+ percent
+ True
+
+
+ purchase
+
+
+
5
@@ -181,7 +235,7 @@
10
- BTW te vorderen laag (inkopen) diensten
+ BTW te vorderen laag (inkopen) diensten 6%
6% BTW diensten
percent
@@ -191,6 +245,19 @@
+
+ 10
+
+ BTW te vorderen laag (inkopen) diensten 9%
+ 9% BTW diensten
+
+ percent
+
+
+ purchase
+
+
+
6
@@ -231,7 +298,7 @@
-
+
98
BTW te vorderen verlegd (inkopen1)
@@ -276,7 +343,7 @@
21
- Inkopen import binnen EU laag(1)
+ Inkopen import binnen EU laag(1) 6%
Inkopen import binnen EU laag(1)
percent
@@ -286,10 +353,23 @@
+
+
+ 21
+ Inkopen import binnen EU laag(1) 9%
+ Inkopen import binnen EU laag(1)
+
+ percent
+
+
+ none
+
+
+
22
- Inkopen import binnen EU laag(2)
+ Inkopen import binnen EU laag(2) 6%
Inkopen import binnen EU laag(2)
percent
@@ -299,10 +379,23 @@
+
+
+ 22
+ Inkopen import binnen EU laag(2) 9%
+ Inkopen import binnen EU laag(2)
+
+ percent
+
+
+ none
+
+
+
20
- Inkopen import binnen EU laag
+ Inkopen import binnen EU laag 6%
6% BTW import binnen EU
group
@@ -311,6 +404,18 @@
+
+ 20
+
+ Inkopen import binnen EU laag 9%
+ 9% BTW import binnen EU
+
+ group
+ purchase
+
+
+
+
21
@@ -418,7 +523,7 @@
21
- Inkopen import binnen EU laag(1) diensten
+ Inkopen import binnen EU laag(1) diensten 6%
Inkopen import binnen EU laag(1) diensten
percent
@@ -428,10 +533,23 @@
+
+
+ 21
+ Inkopen import binnen EU laag(1) diensten 9%
+ Inkopen import binnen EU laag(1) diensten
+
+ percent
+
+
+ none
+
+
+
22
- Inkopen import binnen EU laag(2) diensten
+ Inkopen import binnen EU laag(2) diensten 6%
Inkopen import binnen EU laag(2) diensten
percent
@@ -441,10 +559,23 @@
+
+
+ 22
+ Inkopen import binnen EU laag(2) diensten 9%
+ Inkopen import binnen EU laag(2) diensten
+
+ percent
+
+
+ none
+
+
+
20
- Inkopen import binnen EU laag diensten
+ Inkopen import binnen EU laag diensten 6%
6% BTW import binnen EU diensten
group
@@ -453,6 +584,18 @@
+
+ 20
+
+ Inkopen import binnen EU laag diensten 9%
+ 9% BTW import binnen EU diensten
+
+ group
+ purchase
+
+
+
+
21
@@ -534,7 +677,7 @@
- Inkopen import buiten EU laag(1)
+ Inkopen import buiten EU laag(1) 6%
Inkopen import buiten EU laag(1)
percent
@@ -545,9 +688,22 @@
+
+
+ Inkopen import buiten EU laag(1) 9%
+ Inkopen import buiten EU laag(1)
+
+ percent
+
+
+ 21
+ none
+
+
+
- Inkopen import buiten EU laag(2)
+ Inkopen import buiten EU laag(2) 6%
Inkopen import buiten EU laag(2)
percent
@@ -558,10 +714,23 @@
+
+
+ Inkopen import buiten EU laag(2) 9%
+ Inkopen import buiten EU laag(2)
+
+ percent
+
+
+ 22
+ none
+
+
+
20
- Inkopen import buiten EU laag
+ Inkopen import buiten EU laag 6%
BTW import buiten EU laag inkopen
group
@@ -570,6 +739,18 @@
+
+ 20
+
+ Inkopen import buiten EU laag 9%
+ BTW import buiten EU laag inkopen
+
+ group
+ purchase
+
+
+
+
Inkopen import buiten EU hoog(1)
@@ -676,7 +857,7 @@
- Inkopen import buiten EU laag(1) diensten
+ Inkopen import buiten EU laag(1) diensten 6%
Inkopen import buiten EU laag(1) diensten
percent
@@ -687,9 +868,22 @@
+
+
+ Inkopen import buiten EU laag(1) diensten 9%
+ Inkopen import buiten EU laag(1) diensten
+
+ percent
+
+
+ 21
+ none
+
+
+
- Inkopen import buiten EU laag(2) diensten
+ Inkopen import buiten EU laag(2) diensten 6%
Inkopen import buiten EU laag(2) diensten
percent
@@ -700,10 +894,23 @@
+
+
+ Inkopen import buiten EU laag(2) diensten 9%
+ Inkopen import buiten EU laag(2) diensten
+
+ percent
+
+
+ 22
+ none
+
+
+
20
- Inkopen import buiten EU laag diensten
+ Inkopen import buiten EU laag diensten 6%
BTW import buiten EU laag inkopen diensten
group
@@ -712,6 +919,18 @@
+
+ 20
+
+ Inkopen import buiten EU laag diensten 9%
+ BTW import buiten EU laag inkopen diensten
+
+ group
+ purchase
+
+
+
+
Inkopen import buiten EU hoog(1) diensten
diff --git a/addons/l10n_no/data/account_data.xml b/addons/l10n_no/data/account_data.xml
index 72f40f19ae7..b0c9d690ce6 100644
--- a/addons/l10n_no/data/account_data.xml
+++ b/addons/l10n_no/data/account_data.xml
@@ -9,6 +9,9 @@
MVA 10%
+
+ MVA 12%
+
MVA 15%
diff --git a/addons/l10n_no/data/account_tax_data.xml b/addons/l10n_no/data/account_tax_data.xml
index 2609ee6f0bc..814f585514e 100644
--- a/addons/l10n_no/data/account_tax_data.xml
+++ b/addons/l10n_no/data/account_tax_data.xml
@@ -103,7 +103,19 @@
-
+
+
+
+
+ Utgående 12% mva
+ Utgående 12% mva
+ 12
+ percent
+ sale
+
+
+
+
@@ -139,9 +151,20 @@
-
+
+
+
+
+ Inngående 12% mva
+ Inngående 12% mva
+ 12
+ percent
+ purchase
+
+
+
+
-
Inngående 0% mva
diff --git a/addons/product/models/product.py b/addons/product/models/product.py
index 98d1e3392c5..dbd39ceafce 100644
--- a/addons/product/models/product.py
+++ b/addons/product/models/product.py
@@ -382,17 +382,36 @@ class ProductProduct(models.Model):
self.check_access_rule("read")
result = []
+
+ # Prefetch the fields used by the `name_get`, so `browse` doesn't fetch other fields
+ # Use `load=False` to not call `name_get` for the `product_tmpl_id`
+ self.sudo().read(['name', 'default_code', 'product_tmpl_id', 'attribute_value_ids'], load=False)
+
+ product_template_ids = self.sudo().mapped('product_tmpl_id').ids
+
+ if partner_ids:
+ supplier_info = self.env['product.supplierinfo'].sudo().search([
+ ('product_tmpl_id', 'in', product_template_ids),
+ ('name', 'in', partner_ids),
+ ])
+ # Prefetch the fields used by the `name_get`, so `browse` doesn't fetch other fields
+ # Use `load=False` to not call `name_get` for the `product_tmpl_id` and `product_id`
+ supplier_info.sudo().read(['product_tmpl_id', 'product_id', 'product_name', 'product_code'], load=False)
+ supplier_info_by_template = {}
+ for r in supplier_info:
+ supplier_info_by_template.setdefault(r.product_tmpl_id, []).append(r)
for product in self.sudo():
# display only the attributes with multiple possible values on the template
- variable_attributes = product.attribute_line_ids.filtered(lambda l: len(l.value_ids) > 1).mapped('attribute_id')
+ variable_attributes = product.attribute_value_ids.filtered(lambda v: len(v.attribute_id.value_ids) > 1).mapped('attribute_id')
variant = product.attribute_value_ids._variant_name(variable_attributes)
name = variant and "%s (%s)" % (product.name, variant) or product.name
sellers = []
if partner_ids:
- sellers = [x for x in product.seller_ids if (x.name.id in partner_ids) and (x.product_id == product)]
+ product_supplier_info = supplier_info_by_template.get(product.product_tmpl_id, [])
+ sellers = [x for x in product_supplier_info if x.product_id and x.product_id == product]
if not sellers:
- sellers = [x for x in product.seller_ids if (x.name.id in partner_ids) and not x.product_id]
+ sellers = [x for x in product_supplier_info if not x.product_id]
if sellers:
for s in sellers:
seller_variant = s.product_name and (
diff --git a/addons/product/models/product_template.py b/addons/product/models/product_template.py
index c4566a7d981..766aadefb37 100644
--- a/addons/product/models/product_template.py
+++ b/addons/product/models/product_template.py
@@ -354,6 +354,8 @@ class ProductTemplate(models.Model):
@api.multi
def name_get(self):
+ # Prefetch the fields used by the `name_get`, so `browse` doesn't fetch other fields
+ self.read(['name', 'default_code'])
return [(template.id, '%s%s' % (template.default_code and '[%s] ' % template.default_code or '', template.name))
for template in self]
diff --git a/addons/purchase/report/purchase_report.py b/addons/purchase/report/purchase_report.py
index 46df20ce9de..851f865384b 100644
--- a/addons/purchase/report/purchase_report.py
+++ b/addons/purchase/report/purchase_report.py
@@ -74,10 +74,10 @@ class PurchaseReport(models.Model):
extract(epoch from age(s.date_approve,s.date_order))/(24*60*60)::decimal(16,2) as delay,
extract(epoch from age(l.date_planned,s.date_order))/(24*60*60)::decimal(16,2) as delay_pass,
count(*) as nbr_lines,
- sum(l.price_unit / COALESCE(cr.rate, 1.0) * l.product_qty)::decimal(16,2) as price_total,
- avg(100.0 * (l.price_unit / COALESCE(cr.rate,1.0) * l.product_qty) / NULLIF(ip.value_float*l.product_qty/u.factor*u2.factor, 0.0))::decimal(16,2) as negociation,
+ sum(l.price_unit / COALESCE(NULLIF(cr.rate, 0), 1.0) * l.product_qty)::decimal(16,2) as price_total,
+ avg(100.0 * (l.price_unit / COALESCE(NULLIF(cr.rate, 0),1.0) * l.product_qty) / NULLIF(ip.value_float*l.product_qty/u.factor*u2.factor, 0.0))::decimal(16,2) as negociation,
sum(ip.value_float*l.product_qty/u.factor*u2.factor)::decimal(16,2) as price_standard,
- (sum(l.product_qty * l.price_unit / COALESCE(cr.rate, 1.0))/NULLIF(sum(l.product_qty/u.factor*u2.factor),0.0))::decimal(16,2) as price_average,
+ (sum(l.product_qty * l.price_unit / COALESCE(NULLIF(cr.rate, 0), 1.0))/NULLIF(sum(l.product_qty/u.factor*u2.factor),0.0))::decimal(16,2) as price_average,
partner.country_id as country_id,
partner.commercial_partner_id as commercial_partner_id,
analytic_account.id as account_analytic_id,
diff --git a/addons/sale_stock/models/sale_order.py b/addons/sale_stock/models/sale_order.py
index 7c9d0cba786..f501553e8a7 100644
--- a/addons/sale_stock/models/sale_order.py
+++ b/addons/sale_stock/models/sale_order.py
@@ -263,7 +263,14 @@ class SaleOrderLine(models.Model):
@api.onchange('product_uom_qty')
def _onchange_product_uom_qty(self):
- if self.state == 'sale' and self.product_id.type in ['product', 'consu'] and self.product_uom_qty < self._origin.product_uom_qty:
+ # When modifying a one2many, _origin doesn't guarantee that its values will be the ones
+ # in database. Hence, we need to explicitly read them from there.
+ if self._origin:
+ product_uom_qty_origin = self._origin.read(["product_uom_qty"])[0]["product_uom_qty"]
+ else:
+ product_uom_qty_origin = 0
+
+ if self.state == 'sale' and self.product_id.type in ['product', 'consu'] and self.product_uom_qty < product_uom_qty_origin:
# Do not display this warning if the new quantity is below the delivered
# one; the `write` will raise an `UserError` anyway.
if self.product_uom_qty < self.qty_delivered:
diff --git a/addons/stock/models/stock_inventory.py b/addons/stock/models/stock_inventory.py
index bd600c14452..15118b17096 100644
--- a/addons/stock/models/stock_inventory.py
+++ b/addons/stock/models/stock_inventory.py
@@ -387,7 +387,7 @@ class InventoryLine(models.Model):
"""
for line in self:
if line.product_id.type != 'product':
- raise UserError(_("You can only adjust stockable products."))
+ raise UserError(_("You can only adjust stockable products.") + '\n\n%s -> %s' % (line.product_id.display_name, line.product_id.type))
def _get_quants(self):
return self.env['stock.quant'].search([
diff --git a/addons/stock/report/report_location_barcode.xml b/addons/stock/report/report_location_barcode.xml
index 426f7037e28..5e001abda2e 100644
--- a/addons/stock/report/report_location_barcode.xml
+++ b/addons/stock/report/report_location_barcode.xml
@@ -10,7 +10,7 @@
-
+
diff --git a/addons/stock_picking_batch/wizard/stock_picking_to_batch.py b/addons/stock_picking_batch/wizard/stock_picking_to_batch.py
index c25b97bcad9..b64beb0e65a 100644
--- a/addons/stock_picking_batch/wizard/stock_picking_to_batch.py
+++ b/addons/stock_picking_batch/wizard/stock_picking_to_batch.py
@@ -8,7 +8,7 @@ class StockPickingToBatch(models.TransientModel):
_name = 'stock.picking.to.batch'
_description = 'Add pickings to a batch picking'
- batch_id = fields.Many2one('stock.picking.batch', string='Batch Picking', required=True, oldname="wave_id")
+ batch_id = fields.Many2one('stock.picking.batch', string='Batch Picking', oldname="wave_id")
@api.multi
def attach_pickings(self):
diff --git a/addons/stock_picking_batch/wizard/stock_picking_to_batch_views.xml b/addons/stock_picking_batch/wizard/stock_picking_to_batch_views.xml
index dc45cf58269..a1db359283c 100644
--- a/addons/stock_picking_batch/wizard/stock_picking_to_batch_views.xml
+++ b/addons/stock_picking_batch/wizard/stock_picking_to_batch_views.xml
@@ -8,7 +8,7 @@
',
res_id: 1,
mockRPC: function (route, args) {
- assert.step(args.method + ' ' + args.model)
+ assert.step(args.method + ' ' + args.model);
return this._super(route, args);
},
viewOptions: {
@@ -11992,6 +12036,53 @@ QUnit.module('relational_fields', {
form.destroy();
});
+ QUnit.test('widget many2many_tags in one2many with display_name', function (assert) {
+ assert.expect(4);
+ this.data.turtle.records[0].partner_ids = [2];
+
+ var form = createView({
+ View: FormView,
+ model: 'partner',
+ data: this.data,
+ arch: '',
+ archs: {
+ 'partner,false,list': '',
+ },
+ res_id: 1,
+ });
+
+ assert.strictEqual(form.$('.o_field_one2many[name="turtles"] .o_list_view .o_field_many2manytags[name="partner_ids"]').text().replace(/\s/g, ''),
+ "secondrecordaaa", "the tags should be correctly rendered");
+
+ // open the x2m form view
+ form.$('.o_field_one2many[name="turtles"] .o_list_view td.o_data_cell:first').click();
+ assert.strictEqual($('.modal .o_form_view .o_field_many2many[name="partner_ids"] .o_list_view .o_data_cell').text(),
+ "blipMy little Foo Value", "the list view should be correctly rendered with foo");
+
+ $('.modal button.o_form_button_cancel').click();
+ assert.strictEqual(form.$('.o_field_one2many[name="turtles"] .o_list_view .o_field_many2manytags[name="partner_ids"]').text().replace(/\s/g, ''),
+ "secondrecordaaa", "the tags should still be correctly rendered");
+
+ form.$buttons.find('.o_form_button_edit').click();
+ assert.strictEqual(form.$('.o_field_one2many[name="turtles"] .o_list_view .o_field_many2manytags[name="partner_ids"]').text().replace(/\s/g, ''),
+ "secondrecordaaa", "the tags should still be correctly rendered");
+
+ form.destroy();
+ });
+
QUnit.module('FieldRadio');
QUnit.test('fieldradio widget on a many2one in a new record', function (assert) {
diff --git a/doc/cla/corporate/martronic-sa.md b/doc/cla/corporate/martronic-sa.md
new file mode 100644
index 00000000000..1925eb67b7f
--- /dev/null
+++ b/doc/cla/corporate/martronic-sa.md
@@ -0,0 +1,15 @@
+Switzerland, 20.12.2018
+
+Martronic SA 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,
+
+Fabien Moret info@martronic.ch https://github.com/Martronic-SA
+
+List of contributors:
+
+Fabien Moret info@martronic.ch https://github.com/Martronic-SA
diff --git a/doc/cla/corporate/opensourceintegrators.md b/doc/cla/corporate/opensourceintegrators.md
new file mode 100644
index 00000000000..fb4b98e0e73
--- /dev/null
+++ b/doc/cla/corporate/opensourceintegrators.md
@@ -0,0 +1,25 @@
+The United States of America, 2018-12-04
+
+Open Source Integrators 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,
+
+Greg Mader gmader@opensourceintegrators.com https://github.com/gmader
+
+List of contributors:
+
+Greg Mader gmader@opensourceintegrators.com https://github.com/gmader
+Antonio Yamuta ayamuta@opensourceintegrators.com https://github.com/agyamuta
+Balaji Kannan bkannan@opensourceintegrators.com https://github.com/b-kannan
+Bhavesh Odedra bodedra@opensourceintegrators.com https://github.com/bodedra
+Mayank Gosai mgosai@opensourceintegrators.com https://github.com/mgosai
+Maxime Chambreuil mchambreuil@opensourceintegrators.com https://github.com/max3903
+Michael Allen mallen@opensourceintegrators.com https://github.com/osimallen
+Sandeep Magukiya smangukiya@opensourceintegrators.com https://github.com/smangukiya
+Steven Campbell scampbell@opensourceintegrators.com https://github.com/osi-scampbell
+Sudarshan Kadalazhi skadalazhi@opensourceintegrators.com https://github.com/sudarshan1607
+Wolfgang Hall shall@opensourceintegrators.com https://github.com/wolfhall
diff --git a/doc/cla/individual/anandkansagra.md b/doc/cla/individual/anandkansagra.md
new file mode 100644
index 00000000000..514874860b8
--- /dev/null
+++ b/doc/cla/individual/anandkansagra.md
@@ -0,0 +1,11 @@
+India, 2018-11-28
+
+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,
+
+Anand Kansagra kansagraanand@hotmail.com https://github.com/anandkansagra
diff --git a/doc/cla/individual/darshan-odoo.md b/doc/cla/individual/darshan-odoo.md
new file mode 100644
index 00000000000..e038416121a
--- /dev/null
+++ b/doc/cla/individual/darshan-odoo.md
@@ -0,0 +1,9 @@
+India, 2018-12-20
+
+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,
+
+Darshan Patel darshan.barcelona@gmail.com https://github.com/darshan-odoo
diff --git a/doc/cla/individual/doun.md b/doc/cla/individual/doun.md
new file mode 100644
index 00000000000..9e318b22a03
--- /dev/null
+++ b/doc/cla/individual/doun.md
@@ -0,0 +1,11 @@
+China, 2018-11-20
+
+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,
+
+doun doun@163.com https://github.com/doun
diff --git a/doc/cla/individual/etobella.md b/doc/cla/individual/etobella.md
new file mode 100644
index 00000000000..d2d7c711a36
--- /dev/null
+++ b/doc/cla/individual/etobella.md
@@ -0,0 +1,11 @@
+Spain, 2018-09-19
+
+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,
+
+Enric Tobella etobella@creublanca.es https://github.com/etobella
diff --git a/doc/howtos/profilecode.rst b/doc/howtos/profilecode.rst
index 840181ae43b..ed6d2640648 100644
--- a/doc/howtos/profilecode.rst
+++ b/doc/howtos/profilecode.rst
@@ -12,8 +12,8 @@ Graph a method
Odoo embeds a profiler of code. This embeded profiler output can be used to
generate a graph of calls triggered by the method, number of queries, percentage
-of time taken in the method itself as well as time taken in method and it's
-sub-called methods.
+of time taken in the method itself as well as the time that the method took and
+its sub-called methods.
.. code:: python
@@ -23,7 +23,7 @@ sub-called methods.
@api.multi
def mymethod(...)
-This produce a file called /temp/prof.profile
+This produces a file called /temp/prof.profile
A tool called *gprof2dot* will produce a graph with this result:
@@ -37,7 +37,7 @@ A tool called *xdot* will display the resulting graph:
xdot /temp/prof.xdot
-The profiler can be also used without saving data in a file.
+The profiler can be also used without saving data in a file:
.. code:: python
@@ -78,7 +78,7 @@ completely reviewed.
Dump stack
==========
-Sending the SIGQUIT signal to an odoo process (only available on POSIX) makes
+Sending the SIGQUIT signal to an Odoo process (only available on POSIX) makes
this process output the current stack trace to log, with info level. When an
odoo process seems stucked, sending this signal to the process permit to know
what the process is doing, and letting the process continue his job.
@@ -86,8 +86,8 @@ what the process is doing, and letting the process continue his job.
Tracing code execution
======================
-Instead of sending the SIGQUIT signal to an odoo process often enough, to check
-where processes is performing worse than expected, we can use pyflame tool to
+Instead of sending the SIGQUIT signal to an Odoo process often enough, to check
+where the processes are performing worse than expected, we can use the pyflame tool to
do it for us.
Install pyflame and flamegraph
@@ -118,7 +118,7 @@ Once done, we'll display them as an execution graph.
where is the process ID of the odoo process you want to graph. This will
wait until the dead of the process, with a maximum of one hour, and and get 5
-traces a second. With the output of pyflame, we can produce an svg graph with
+traces a second. With the output of pyflame, we can produce an SVG graph with
the flamegraph tool:
.. code:: bash
diff --git a/odoo/addons/base/data/res_currency_data.xml b/odoo/addons/base/data/res_currency_data.xml
index f4028c0b698..1c67808c8a2 100644
--- a/odoo/addons/base/data/res_currency_data.xml
+++ b/odoo/addons/base/data/res_currency_data.xml
@@ -201,7 +201,7 @@
VND
₫
- 0.01
+ 1.00
Dong
Xu
diff --git a/odoo/addons/base/models/res_partner.py b/odoo/addons/base/models/res_partner.py
index 360aeded8ff..c36b34603cc 100644
--- a/odoo/addons/base/models/res_partner.py
+++ b/odoo/addons/base/models/res_partner.py
@@ -649,6 +649,7 @@ class Partner(models.Model):
where_query = self._where_calc(args)
self._apply_ir_rules(where_query, 'read')
from_clause, where_clause, where_clause_params = where_query.get_sql()
+ from_str = from_clause if from_clause else 'res_partner'
where_str = where_clause and (" WHERE %s AND " % where_clause) or ' WHERE '
# search on the name of the contacts and of its company
@@ -660,8 +661,8 @@ class Partner(models.Model):
unaccent = get_unaccent_wrapper(self.env.cr)
- query = """SELECT id
- FROM res_partner
+ query = """SELECT res_partner.id
+ FROM {from_str}
{where} ({email} {operator} {percent}
OR {display_name} {operator} {percent}
OR {reference} {operator} {percent}
@@ -669,13 +670,14 @@ class Partner(models.Model):
-- don't panic, trust postgres bitmap
ORDER BY {display_name} {operator} {percent} desc,
{display_name}
- """.format(where=where_str,
+ """.format(from_str=from_str,
+ where=where_str,
operator=operator,
- email=unaccent('email'),
- display_name=unaccent('display_name'),
- reference=unaccent('ref'),
+ email=unaccent('res_partner.email'),
+ display_name=unaccent('res_partner.display_name'),
+ reference=unaccent('res_partner.ref'),
percent=unaccent('%s'),
- vat=unaccent('vat'),)
+ vat=unaccent('res_partner.vat'),)
where_clause_params += [search_name]*5
if limit:
diff --git a/odoo/addons/base/tests/__init__.py b/odoo/addons/base/tests/__init__.py
index 36916cc53b7..069afe0cec1 100644
--- a/odoo/addons/base/tests/__init__.py
+++ b/odoo/addons/base/tests/__init__.py
@@ -30,6 +30,7 @@ from . import test_user_has_group
from . import test_view_validation
from . import test_views
from . import test_xmlrpc
+from . import test_res_partner
from . import test_res_partner_bank
from . import test_reports
from . import test_tests_tags
diff --git a/odoo/addons/base/tests/test_res_partner.py b/odoo/addons/base/tests/test_res_partner.py
new file mode 100644
index 00000000000..2a6722553af
--- /dev/null
+++ b/odoo/addons/base/tests/test_res_partner.py
@@ -0,0 +1,19 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo.tests.common import TransactionCase
+
+
+class TestPartner(TransactionCase):
+
+ def test_name_search(self):
+ """ Check name_search on partner, especially with domain based on auto_join
+ user_ids field. Check specific SQL of name_search correctly handle joined tables. """
+ test_partner = self.env['res.partner'].create({'name': 'Vlad the Impaler'})
+ test_user = self.env['res.users'].create({'name': 'Vlad the Impaler', 'login': 'vlad', 'email': 'vlad.the.impaler@example.com'})
+
+ ns_res = self.env['res.partner'].name_search('Vlad', operator='ilike')
+ self.assertEqual(set(i[0] for i in ns_res), set((test_partner | test_user.partner_id).ids))
+
+ ns_res = self.env['res.partner'].name_search('Vlad', args=[('user_ids.email', 'ilike', 'vlad')])
+ self.assertEqual(set(i[0] for i in ns_res), set(test_user.partner_id.ids))
diff --git a/odoo/service/model.py b/odoo/service/model.py
index 25f6dee3854..61f63c68155 100644
--- a/odoo/service/model.py
+++ b/odoo/service/model.py
@@ -138,7 +138,7 @@ def check(f):
pass
raise ValidationError(msg)
else:
- raise ValidationError(inst[0])
+ raise ValidationError(inst.args[0])
return wrapper