From fb6c9c24434c941383ffd73ab3056cafd7740c52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexandre=20Ku=CC=88hn?= Date: Thu, 9 Nov 2017 13:12:55 +0100 Subject: [PATCH] [IMP] web: FieldChar trim its value by default With this commit, we change the behaviour of the web client with respect to spaces in char fields. Most of the time, starting and ending spaces have no value, and worse, make the data not so reliable. After this commit, field char will trim by default (so, if the user input a char as ' abc ', the string 'abc' will be sent to the server instead). Note that this only applies when the value of the field is changed. If someone open a form view, then switches to edit mode and save, nothing will change. This is the desired behavior most of the time. However, in some rare cases, this is actually harmful. For example, if we trim the 'decimal_point' field, it will not be possible to enter a whitespace as decimal separator. In those cases, we introduce a new attribute 'trim', which allow the developer to desactivate that feature. --- .../web/static/src/js/fields/basic_fields.js | 14 +++++++ .../static/tests/fields/basic_fields_tests.js | 39 ++++++++++++++++++- odoo/addons/base/ir/ir_sequence.py | 4 +- odoo/addons/base/res/res_lang.py | 4 +- odoo/fields.py | 6 +++ 5 files changed, 62 insertions(+), 5 deletions(-) diff --git a/addons/web/static/src/js/fields/basic_fields.js b/addons/web/static/src/js/fields/basic_fields.js index a4979848adb..2872be972a5 100644 --- a/addons/web/static/src/js/fields/basic_fields.js +++ b/addons/web/static/src/js/fields/basic_fields.js @@ -347,6 +347,20 @@ var FieldChar = InputField.extend(TranslatableFieldMixin, { this.$el = this.$el.add(this._renderTranslateButton()); return def; }, + /** + * Trim the value input by the user. + * + * @override + * @private + * @param {any} value + * @param {Object} [options] + */ + _setValue: function (value, options) { + if (this.field.trim) { + value = value.trim(); + } + return this._super(value, options); + }, }); diff --git a/addons/web/static/tests/fields/basic_fields_tests.js b/addons/web/static/tests/fields/basic_fields_tests.js index 35431904cec..0e1bb096e50 100644 --- a/addons/web/static/tests/fields/basic_fields_tests.js +++ b/addons/web/static/tests/fields/basic_fields_tests.js @@ -24,7 +24,7 @@ QUnit.module('basic_fields', { date: {string: "A date", type: "date", searchable: true}, datetime: {string: "A datetime", type: "datetime", searchable: true}, display_name: {string: "Displayed name", type: "char", searchable: true}, - foo: {string: "Foo", type: "char", default: "My little Foo Value", searchable: true}, + foo: {string: "Foo", type: "char", default: "My little Foo Value", searchable: true, trim: true}, bar: {string: "Bar", type: "boolean", default: true, searchable: true}, txt: {string: "txt", type: "text", default: "My little txt Value\nHo-ho-hoooo Merry Christmas"}, int_field: {string: "int_field", type: "integer", sortable: true, searchable: true}, @@ -876,6 +876,43 @@ QUnit.module('basic_fields', { form.destroy(); }); + QUnit.test('char field trim (or not) characters', function (assert) { + assert.expect(2); + + this.data.partner.fields.foo2 = {string: "Foo2", type: "char", trim: false}; + + var form = createView({ + View: FormView, + model: 'partner', + data: this.data, + arch: '
' + + '' + + '' + + '' + + '' + + '' + + '' + + '
', + res_id: 1, + viewOptions: { + mode: 'edit', + }, + }); + + form.$('input[name="foo"]').val(' abc ').trigger('input'); + form.$('input[name="foo2"]').val(' def ').trigger('input'); + + form.$buttons.find('.o_form_button_save').click(); + + // edit mode + form.$buttons.find('.o_form_button_edit').click(); + + assert.strictEqual(form.$('input[name="foo"]').val(), 'abc', 'Foo value should have been trimmed'); + assert.strictEqual(form.$('input[name="foo2"]').val(), ' def ', 'Foo2 value should not have been trimmed'); + + form.destroy(); + }); + QUnit.test('input field: change value before pending onchange returns', function (assert) { assert.expect(3); diff --git a/odoo/addons/base/ir/ir_sequence.py b/odoo/addons/base/ir/ir_sequence.py index 70d70b536ef..84c8be3af60 100644 --- a/odoo/addons/base/ir/ir_sequence.py +++ b/odoo/addons/base/ir/ir_sequence.py @@ -122,8 +122,8 @@ class IrSequence(models.Model): "and 'No gap'. The later is slower than the former but forbids any" "gap in the sequence (while they are possible in the former).") active = fields.Boolean(default=True) - prefix = fields.Char(help="Prefix value of the record for the sequence") - suffix = fields.Char(help="Suffix value of the record for the sequence") + prefix = fields.Char(help="Prefix value of the record for the sequence", trim=False) + suffix = fields.Char(help="Suffix value of the record for the sequence", trim=False) number_next = fields.Integer(string='Next Number', required=True, default=1, help="Next number of this sequence") number_next_actual = fields.Integer(compute='_get_number_next_actual', inverse='_set_number_next_actual', string='Next Number', diff --git a/odoo/addons/base/res/res_lang.py b/odoo/addons/base/res/res_lang.py index 38737df2cb0..38b4209cc6f 100644 --- a/odoo/addons/base/res/res_lang.py +++ b/odoo/addons/base/res/res_lang.py @@ -38,8 +38,8 @@ class Lang(models.Model): "-1 will end the separation. e.g. [3,2,-1] will represent 106500 to be 1,06,500; " "[1,2,-1] will represent it to be 106,50,0;[3] will represent it as 106,500. " "Provided ',' as the thousand separator in each case.") - decimal_point = fields.Char(string='Decimal Separator', required=True, default='.') - thousands_sep = fields.Char(string='Thousands Separator', default=',') + decimal_point = fields.Char(string='Decimal Separator', required=True, default='.', trim=False) + thousands_sep = fields.Char(string='Thousands Separator', default=',', trim=False) _sql_constraints = [ ('name_uniq', 'unique(name)', 'The name of the language must be unique !'), diff --git a/odoo/fields.py b/odoo/fields.py index 4c41c938637..9818e99e1d3 100644 --- a/odoo/fields.py +++ b/odoo/fields.py @@ -1362,6 +1362,9 @@ class Char(_String): :param int size: the maximum size of values stored for that field + :param bool trim: states whether the value is trimmed or not. Note that + the trim operation is applied only on the web client + :param translate: enable the translation of the field's values; use ``translate=True`` to translate field values as a whole; ``translate`` may also be a callable such that ``translate(callback, value)`` @@ -1372,6 +1375,7 @@ class Char(_String): column_cast_from = ('text',) _slots = { 'size': None, # maximum size of values (deprecated) + 'trim': True, # whether value is trimmed (only by web client) } @property @@ -1388,7 +1392,9 @@ class Char(_String): super(Char, self).update_db_column(model, column) _related_size = property(attrgetter('size')) + _related_trim = property(attrgetter('trim')) _description_size = property(attrgetter('size')) + _description_trim = property(attrgetter('trim')) def _setup_regular_base(self, model): super(Char, self)._setup_regular_base(model)