diff --git a/addons/account/models/account_payment.py b/addons/account/models/account_payment.py
index 8764d190991..d9e28bff592 100644
--- a/addons/account/models/account_payment.py
+++ b/addons/account/models/account_payment.py
@@ -510,6 +510,7 @@ class account_payment(models.Model):
(transfer_credit_aml + transfer_debit_aml).reconcile()
rec.write({'state': 'posted', 'move_name': move.name})
+ return True
@api.multi
def action_draft(self):
@@ -524,7 +525,7 @@ class account_payment(models.Model):
if any(len(record.invoice_ids) != 1 for record in self):
# For multiple invoices, there is account.register.payments wizard
raise UserError(_("This method should only be called to process a single invoice's payment."))
- self.post();
+ return self.post()
def _create_payment_entry(self, amount):
""" Create a journal entry corresponding to a payment, if the payment references invoice(s) they are reconciled.
diff --git a/addons/crm/views/res_config_settings_views.xml b/addons/crm/views/res_config_settings_views.xml
index e06b7c9f7ec..43c7207d5d1 100644
--- a/addons/crm/views/res_config_settings_views.xml
+++ b/addons/crm/views/res_config_settings_views.xml
@@ -61,7 +61,7 @@
- Validate and normalize phone numbers.
+ Formats phone numbers
diff --git a/addons/crm_phone_validation/__manifest__.py b/addons/crm_phone_validation/__manifest__.py
index 72a21dbfcfa..7c729bfae3e 100644
--- a/addons/crm_phone_validation/__manifest__.py
+++ b/addons/crm_phone_validation/__manifest__.py
@@ -13,7 +13,6 @@ CRM Phone Numbers Validation
This module allows for validate and format phone numbers for leads and contacts.""",
'data': [
'views/res_config_settings_views.xml',
- 'views/crm_lead_views.xml'
],
'depends': [
'phone_validation',
diff --git a/addons/crm_phone_validation/views/crm_lead_views.xml b/addons/crm_phone_validation/views/crm_lead_views.xml
deleted file mode 100644
index bcf1840bd7a..00000000000
--- a/addons/crm_phone_validation/views/crm_lead_views.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
- crm.lead.form.opportunity.inherit.crm.phone.validation
- crm.lead
-
-
-
-
-
-
-
-
diff --git a/addons/l10n_fr_hr_payroll/report/report_l10n_fr_fiche_paye.xml b/addons/l10n_fr_hr_payroll/report/report_l10n_fr_fiche_paye.xml
index 56a7e28210e..c9fcc8ae3fd 100644
--- a/addons/l10n_fr_hr_payroll/report/report_l10n_fr_fiche_paye.xml
+++ b/addons/l10n_fr_hr_payroll/report/report_l10n_fr_fiche_paye.xml
@@ -135,7 +135,7 @@
Net Imposable
Plafond s.s.
-
Coût Global Salarié
+
Total versé par l'employeur
diff --git a/addons/phone_validation/models/phone_validation_mixin.py b/addons/phone_validation/models/phone_validation_mixin.py
index 4cd9d8eb2bb..02e4c6aa82a 100644
--- a/addons/phone_validation/models/phone_validation_mixin.py
+++ b/addons/phone_validation/models/phone_validation_mixin.py
@@ -9,11 +9,12 @@ class PhoneValidationMixin(models.AbstractModel):
_name = 'phone.validation.mixin'
def _phone_get_country(self):
- if 'country_id' in self:
+ if 'country_id' in self and self.country_id:
return self.country_id
+ return self.env.user.company_id.country_id
def _phone_get_always_international(self):
- if 'company_id' in self and self.country_id:
+ if 'company_id' in self and self.company_id:
return self.company_id.phone_international_format == 'prefix'
return self.env.user.company_id.phone_international_format == 'prefix'
diff --git a/addons/point_of_sale/models/pos_order.py b/addons/point_of_sale/models/pos_order.py
index 080d7277657..a97fce8135d 100644
--- a/addons/point_of_sale/models/pos_order.py
+++ b/addons/point_of_sale/models/pos_order.py
@@ -227,6 +227,12 @@ class PosOrder(models.Model):
return InvoiceLine.sudo().create(inv_line)
def _create_account_move_line(self, session=None, move=None):
+ def _flatten_tax_and_children(taxes):
+ children = self.env['account.tax']
+ for tax in taxes:
+ children |= _flatten_tax_and_children(tax.children_tax_ids)
+ return taxes + children
+
# Tricky, via the workflow, we only have one id in the ids variable
"""Create a account move line of order grouped by products or not."""
IrProperty = self.env['ir.property']
@@ -303,6 +309,9 @@ class PosOrder(models.Model):
name = name + ' (' + line.notice + ')'
# Create a move for the line for the order line
+ # Just like for invoices, a group of taxes must be present on this base line
+ # As well as its children
+ base_line_tax_ids = _flatten_tax_and_children(line.tax_ids_after_fiscal_position).filtered(lambda tax: tax.type_tax_use in ['sale', 'none'])
insert_data('product', {
'name': name,
'quantity': line.qty,
@@ -311,7 +320,7 @@ class PosOrder(models.Model):
'analytic_account_id': self._prepare_analytic_account(line),
'credit': ((amount > 0) and amount) or 0.0,
'debit': ((amount < 0) and -amount) or 0.0,
- 'tax_ids': [(6, 0, line.tax_ids_after_fiscal_position.ids)],
+ 'tax_ids': [(6, 0, base_line_tax_ids.ids)],
'partner_id': partner_id
})
diff --git a/addons/stock/tests/test_move2.py b/addons/stock/tests/test_move2.py
index 45e898856b7..5f1524efa70 100644
--- a/addons/stock/tests/test_move2.py
+++ b/addons/stock/tests/test_move2.py
@@ -1646,19 +1646,15 @@ class TestSinglePicking(TestStockCommon):
class TestStockUOM(TestStockCommon):
def setUp(self):
- with registry().cursor() as cr:
- env = api.Environment(cr, 1, {})
- dp = env.ref('product.decimal_product_uom')
- self.old_digits = dp.digits
- dp.digits = 7
super(TestStockUOM, self).setUp()
+ dp = self.env.ref('product.decimal_product_uom')
+ dp.digits = 7
- def tearDown(self):
- super(TestStockUOM, self).tearDown()
- with self.registry.cursor() as cr:
- env = api.Environment(cr, 1, {})
- dp = env.ref('product.decimal_product_uom')
- dp.digits = self.old_digits
+ # Trick: invoke the method 'precision_get' with the current environment.
+ # This fills in the cache of the method with the right value. If we
+ # don't do that, the registry will access the corresponding precision
+ # with a new cursor (LazyCursor), and get a different value!
+ self.assertEqual(dp.precision_get(dp.name), 7)
def test_pickings_transfer_with_different_uom_and_back_orders(self):
""" Picking transfer with diffrent unit of meassure. """
diff --git a/addons/web/static/src/js/views/basic/basic_renderer.js b/addons/web/static/src/js/views/basic/basic_renderer.js
index 58a5cb7a70e..8db747f308d 100644
--- a/addons/web/static/src/js/views/basic/basic_renderer.js
+++ b/addons/web/static/src/js/views/basic/basic_renderer.js
@@ -30,6 +30,7 @@ var BasicRenderer = AbstractRenderer.extend({
this.activeActions = params.activeActions;
this.viewType = params.viewType;
this.mode = params.mode || 'readonly';
+ this.widgets = [];
},
/**
* This method has two responsabilities: find every invalid fields in the
@@ -95,6 +96,9 @@ var BasicRenderer = AbstractRenderer.extend({
return this._render().then(_.constant([]));
}
+ // reset all widgets (from the tag) if any:
+ _.invoke(this.widgets, 'updateState', state);
+
var defs = [];
// Reset all the field widgets that are marked as changed and the ones
@@ -613,6 +617,8 @@ var BasicRenderer = AbstractRenderer.extend({
var Widget = widgetRegistry.get(node.attrs.name);
var widget = new Widget(this, record);
+ this.widgets.push(widget);
+
// Prepare widget rendering and save the related deferred
var def = widget._widgetRenderAndInsert(function () {});
if (def.state() === 'pending') {
diff --git a/addons/web/static/src/js/views/list/list_renderer.js b/addons/web/static/src/js/views/list/list_renderer.js
index f4c5219259a..61871bfc2bd 100644
--- a/addons/web/static/src/js/views/list/list_renderer.js
+++ b/addons/web/static/src/js/views/list/list_renderer.js
@@ -71,6 +71,7 @@ var ListRenderer = BasicRenderer.extend({
*/
updateState: function (state, params) {
this._processColumns(params.columnInvisibleFields || {});
+ this.selection = [];
return this._super.apply(this, arguments);
},
@@ -202,9 +203,11 @@ var ListRenderer = BasicRenderer.extend({
var field = self.state.fields[column.attrs.name];
var value = aggregateValues[column.attrs.name].value;
var help = aggregateValues[column.attrs.name].help;
- var formattedValue = field_utils.format[field.type](value, field, {
- escape: true,
- });
+ var formatFunc = field_utils.format[column.attrs.widget];
+ if (!formatFunc) {
+ formatFunc = field_utils.format[field.type];
+ }
+ var formattedValue = formatFunc(value, field, {escape: true});
$cell.addClass('o_list_number').attr('title', help).html(formattedValue);
}
return $cell;
diff --git a/addons/web/static/tests/views/form_tests.js b/addons/web/static/tests/views/form_tests.js
index d2ec320c6ea..7675e21bb45 100644
--- a/addons/web/static/tests/views/form_tests.js
+++ b/addons/web/static/tests/views/form_tests.js
@@ -6232,6 +6232,41 @@ QUnit.module('Views', {
delete widgetRegistry.map.test;
});
+ QUnit.test('basic support for widgets', function (assert) {
+ assert.expect(1);
+
+ var MyWidget = Widget.extend({
+ init: function (parent, dataPoint) {
+ this.data = dataPoint.data;
+ },
+ start: function () {
+ this.$el.text(this.data.foo + "!");
+ },
+ updateState: function (dataPoint) {
+ this.$el.text(dataPoint.data.foo + "!");
+ },
+ });
+ widgetRegistry.add('test', MyWidget);
+
+ var form = createView({
+ View: FormView,
+ model: 'partner',
+ data: this.data,
+ arch: '',
+ });
+
+ form.$('input[name="foo"]').val("I am alive").trigger('input');
+ assert.strictEqual(form.$('.o_widget').text(), 'I am alive!',
+ "widget should have been updated");
+
+ form.destroy();
+ delete widgetRegistry.map.test;
+ });
+
+
QUnit.test('bounce edit button in readonly mode', function (assert) {
assert.expect(3);
diff --git a/addons/web/static/tests/views/list_tests.js b/addons/web/static/tests/views/list_tests.js
index 2fafef26c6f..92ae4033562 100644
--- a/addons/web/static/tests/views/list_tests.js
+++ b/addons/web/static/tests/views/list_tests.js
@@ -582,6 +582,40 @@ QUnit.module('Views', {
list.destroy();
});
+ QUnit.test('selection is reset on reload', function (assert) {
+ assert.expect(5);
+
+ var list = createView({
+ View: ListView,
+ model: 'foo',
+ data: this.data,
+ arch: '' +
+ '' +
+ '' +
+ '',
+ });
+
+ assert.strictEqual(list.$('tfoot td:nth(2)').text(), '32',
+ "total should be 32 (no record selected)");
+
+ // select first record
+ var $firstRowSelector = list.$('tbody .o_list_record_selector input').first();
+ $firstRowSelector.click();
+ assert.ok($firstRowSelector.is(':checked'), "first row should be selected");
+ assert.strictEqual(list.$('tfoot td:nth(2)').text(), '10',
+ "total should be 10 (first record selected)");
+
+ // reload
+ list.reload();
+ $firstRowSelector = list.$('tbody .o_list_record_selector input').first();
+ assert.notOk($firstRowSelector.is(':checked'),
+ "first row should no longer be selected");
+ assert.strictEqual(list.$('tfoot td:nth(2)').text(), '32',
+ "total should be 32 (no more record selected)");
+
+ list.destroy();
+ });
+
QUnit.test('aggregates are computed correctly', function (assert) {
assert.expect(4);
@@ -660,6 +694,25 @@ QUnit.module('Views', {
list.destroy();
});
+ QUnit.test('aggregates are formatted according to field widget', function (assert) {
+ assert.expect(1);
+
+ var list = createView({
+ View: ListView,
+ model: 'foo',
+ data: this.data,
+ arch: '' +
+ '' +
+ '' +
+ '',
+ });
+
+ assert.strictEqual(list.$('tfoot td:nth(2)').text(), '19:24',
+ "total should be formatted as a float_time");
+
+ list.destroy();
+ });
+
QUnit.test('groups can be sorted on aggregates', function (assert) {
assert.expect(10);
diff --git a/addons/web_editor/static/src/js/widgets/ace.js b/addons/web_editor/static/src/js/widgets/ace.js
index 6c28c1e2332..0a89d0d8ce7 100644
--- a/addons/web_editor/static/src/js/widgets/ace.js
+++ b/addons/web_editor/static/src/js/widgets/ace.js
@@ -742,7 +742,7 @@ var ViewEditor = Widget.extend({
var text = data.text || '';
if (!isSelected) {
- text = Array($elem.data('level') + 1).join('-') + ' ' + text;
+ text = Array(($elem.data('level') || 0) + 1).join('-') + ' ' + text;
}
var $div = $('', {
text: text,
diff --git a/addons/website_crm_phone_validation/controllers/website_form.py b/addons/website_crm_phone_validation/controllers/website_form.py
index 3713fe86f9e..c404bb87724 100644
--- a/addons/website_crm_phone_validation/controllers/website_form.py
+++ b/addons/website_crm_phone_validation/controllers/website_form.py
@@ -4,8 +4,6 @@
from odoo.addons.website_form.controllers.main import WebsiteForm
from odoo.http import request, route
-import json
-
class WebsiteForm(WebsiteForm):
@@ -32,17 +30,13 @@ class WebsiteForm(WebsiteForm):
else:
record = data.get('record', {})
phone_fields = self._get_phone_fields_to_validate()
- country = self._get_country()
- invalid_fields = {}
+ country = request.env['res.country'].browse(record.get('country_id'))
+ contact_country = country.exists() and country or self._get_country()
for phone_field in phone_fields:
if not record.get(phone_field):
continue
number = record[phone_field]
- try:
- request.env[model_name].phone_format(number, country)
- except Exception as error:
- invalid_fields[phone_field] = error.args[0]
- if invalid_fields:
- return json.dumps({'error_fields': invalid_fields})
+ fmt_number = request.env[model_name].phone_format(number, contact_country)
+ request.params.update({phone_field: fmt_number})
return super(WebsiteForm, self).website_form(model_name, **kwargs)