diff --git a/addons/web/static/src/js/fields/basic_fields.js b/addons/web/static/src/js/fields/basic_fields.js index 9df9a49fac2..3e9ea89be31 100644 --- a/addons/web/static/src/js/fields/basic_fields.js +++ b/addons/web/static/src/js/fields/basic_fields.js @@ -93,9 +93,6 @@ var DebouncedField = AbstractField.extend({ // _debouncedStarted is used to detect that the user interacted at least // once with the widget, so that we can prevent it from triggering a // field_changed in commitChanges if the user didn't change anything - // (this is required as sometimes it is hard to detect that an unset - // value is still unset, e.g. if a numerical field contains the value 0, - // is it because it is still unset or because the user set it to 0? this._debouncedStarted = false; if (this.mode === 'edit') { if (this.DEBOUNCE) { diff --git a/addons/web/static/src/js/fields/field_utils.js b/addons/web/static/src/js/fields/field_utils.js index 9a683954e7e..ed3014d9bb6 100644 --- a/addons/web/static/src/js/fields/field_utils.js +++ b/addons/web/static/src/js/fields/field_utils.js @@ -71,7 +71,7 @@ function formatChar(value) { * @returns {string} */ function formatDate(value, field, options) { - if (!value) { + if (value === false) { return ""; } if (!options || !('timezone' in options) || options.timezone) { @@ -96,7 +96,7 @@ function formatDate(value, field, options) { * @returns {string} */ function formatDateTime(value, field, options) { - if (!value) { + if (value === false) { return ""; } if (!options || !('timezone' in options) || options.timezone) { @@ -113,7 +113,7 @@ function formatDateTime(value, field, options) { * Returns a string representing a float. The result takes into account the * user settings (to display the correct decimal separator). * - * @param {float} value the value that should be formatted + * @param {float|false} value the value that should be formatted * @param {Object} [field] a description of the field (returned by fields_get * for example). It may contain a description of the number of digits that * should be used. @@ -124,6 +124,9 @@ function formatDateTime(value, field, options) { * @returns {string} */ function formatFloat(value, field, options) { + if (value === false) { + return ""; + } var l10n = core._t.database.parameters; var precision; if (options && options.digits) { @@ -213,7 +216,7 @@ function formatX2Many(value) { * Returns a string representing a monetary value. The result takes into account * the user settings (to display the correct decimal separator, currency, ...). * - * @param {float} value the value that should be formatted + * @param {float|false} value the value that should be formatted * @param {Object} [field] * a description of the field (returned by fields_get for example). It * may contain a description of the number of digits that should be used. @@ -238,6 +241,9 @@ function formatX2Many(value) { * @returns {string} */ function formatMonetary(value, field, options) { + if (value === false) { + return ""; + } options = options || {}; var currency = options.currency; diff --git a/addons/web/static/tests/fields/basic_fields_tests.js b/addons/web/static/tests/fields/basic_fields_tests.js index f2334b61f4a..6e007949ea7 100644 --- a/addons/web/static/tests/fields/basic_fields_tests.js +++ b/addons/web/static/tests/fields/basic_fields_tests.js @@ -415,9 +415,9 @@ QUnit.module('basic_fields', { '', }); - var zeroValues = list.$('td').filter(function () {return $(this).text() === '0.000';}); - assert.strictEqual(zeroValues.length, 2, - 'Unset float values should be rendered as zeros.'); + var zeroValues = list.$('td.o_data_cell').filter(function () {return $(this).text() === '';}); + assert.strictEqual(zeroValues.length, 1, + 'Unset float values should be rendered as empty strings.'); // switch to edit mode var $cell = list.$('tr.o_data_row td:not(.o_list_record_selector)').first(); @@ -441,8 +441,8 @@ QUnit.module('basic_fields', { QUnit.test('do not trigger a field_changed if they have not changed', function (assert) { assert.expect(2); - this.data.partner.records[1].qux = undefined; - this.data.partner.records[1].int_field = undefined; + this.data.partner.records[1].qux = false; + this.data.partner.records[1].int_field = false; var form = createView({ View: FormView, model: 'partner', @@ -1659,9 +1659,9 @@ QUnit.module('basic_fields', { assert.strictEqual(euroValues.length, 1, 'One one line has euro as a currency.'); - var zeroValues = list.$('td').filter(function () {return _.str.include($(this).text(), '0.00');}); - assert.strictEqual(zeroValues.length, 2, - 'Unset float values should be rendered as zeros.'); + var zeroValues = list.$('td.o_data_cell').filter(function () {return $(this).text() === '';}); + assert.strictEqual(zeroValues.length, 1, + 'Unset float values should be rendered as empty strings.'); // switch to edit mode var $cell = list.$('tr.o_data_row td:not(.o_list_record_selector):contains($)'); diff --git a/addons/web/static/tests/fields/field_utils_tests.js b/addons/web/static/tests/fields/field_utils_tests.js index d56f077b828..bfd60000035 100644 --- a/addons/web/static/tests/fields/field_utils_tests.js +++ b/addons/web/static/tests/fields/field_utils_tests.js @@ -23,13 +23,13 @@ QUnit.test('format integer', function(assert) { assert.strictEqual(fieldUtils.format.integer(106500), '106,50,0'); assert.strictEqual(fieldUtils.format.integer(0), "0"); - assert.strictEqual(fieldUtils.format.integer(undefined), ""); + assert.strictEqual(fieldUtils.format.integer(false), ""); core._t.database.parameters.grouping = originalGrouping; }); QUnit.test('format float', function(assert) { - assert.expect(4); + assert.expect(5); var originalParameters = $.extend(true, {}, core._t.database.parameters); @@ -48,6 +48,7 @@ QUnit.test('format float', function(assert) { thousands_sep: '.' }); assert.strictEqual(fieldUtils.format.float(6000), '6.000,00'); + assert.strictEqual(fieldUtils.format.float(false), ''); core._t.database.parameters = originalParameters; }); @@ -70,6 +71,12 @@ QUnit.test("format_many2one", function (assert) { })); }); +QUnit.test('format monetary', function(assert) { + assert.expect(1); + + assert.strictEqual(fieldUtils.format.monetary(false), ''); +}); + QUnit.test('format char', function(assert) { assert.expect(1); diff --git a/addons/web/static/tests/views/form_tests.js b/addons/web/static/tests/views/form_tests.js index bedcf83b139..d1e45577ca6 100644 --- a/addons/web/static/tests/views/form_tests.js +++ b/addons/web/static/tests/views/form_tests.js @@ -696,6 +696,50 @@ QUnit.module('Views', { form.destroy(); }); + QUnit.test('required float fields works as expected', function (assert) { + assert.expect(8); + + this.data.partner.fields.qux.required = true; + var form = createView({ + View: FormView, + model: 'partner', + data: this.data, + arch: '
' + + '' + + '' + + '' + + '' + + '' + + '
', + mockRPC: function (route, args) { + assert.step(args.method); + return this._super.apply(this, arguments); + }, + }); + + assert.ok(form.$('input[name="qux"]').hasClass('o_required_modifier'), + "qux input is flagged as required"); + assert.strictEqual(form.$('input[name="qux"]').val(), "", + "qux input is empty"); + + form.$buttons.find('.o_form_button_save').click(); + + assert.ok(form.$('input[name="qux"]').hasClass('o_field_invalid'), + "qux input is displayed as invalid"); + + form.$('input[name="qux"]').val("0").trigger('input'); + + form.$buttons.find('.o_form_button_save').click(); + + form.$buttons.find('.o_form_button_edit').click(); + + assert.strictEqual(form.$('input[name="qux"]').val(), "0.0", + "qux input is properly formatted"); + + assert.verifySteps(['default_get', 'create', 'read']); + form.destroy(); + }); + QUnit.test('separators', function (assert) { assert.expect(1);