From b6f9d0ec09a32e194134b922e56e2d8cb41c6bf5 Mon Sep 17 00:00:00 2001 From: oihane Date: Thu, 4 Jun 2015 19:24:48 +0200 Subject: [PATCH 01/12] [ADD] New module --- web_decimal_numpad_dot/README.rst | 14 + web_decimal_numpad_dot/__init__.py | 4 + web_decimal_numpad_dot/__openerp__.py | 40 ++ .../static/src/js/formats.js | 376 ++++++++++++++++++ .../static/src/js/numpad_dot.js | 23 ++ .../views/web_decimal_numpad_dot.xml | 10 + 6 files changed, 467 insertions(+) create mode 100644 web_decimal_numpad_dot/README.rst create mode 100644 web_decimal_numpad_dot/__init__.py create mode 100644 web_decimal_numpad_dot/__openerp__.py create mode 100644 web_decimal_numpad_dot/static/src/js/formats.js create mode 100644 web_decimal_numpad_dot/static/src/js/numpad_dot.js create mode 100644 web_decimal_numpad_dot/views/web_decimal_numpad_dot.xml diff --git a/web_decimal_numpad_dot/README.rst b/web_decimal_numpad_dot/README.rst new file mode 100644 index 00000000..f0a463a2 --- /dev/null +++ b/web_decimal_numpad_dot/README.rst @@ -0,0 +1,14 @@ +Web - Numpad Dot as decimal separator +===================================== + + + +Credits +======= + +Contributors +------------ + +* Oihane Crucelaegui +* Pedro M. Baeza +* Ana Juaristi diff --git a/web_decimal_numpad_dot/__init__.py b/web_decimal_numpad_dot/__init__.py new file mode 100644 index 00000000..ee9e0cf6 --- /dev/null +++ b/web_decimal_numpad_dot/__init__.py @@ -0,0 +1,4 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# For copyright and license notices, see __openerp__.py file in root directory +############################################################################## diff --git a/web_decimal_numpad_dot/__openerp__.py b/web_decimal_numpad_dot/__openerp__.py new file mode 100644 index 00000000..9e6051de --- /dev/null +++ b/web_decimal_numpad_dot/__openerp__.py @@ -0,0 +1,40 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see http://www.gnu.org/licenses/. +# +############################################################################## + +{ + "name": "Web - Numpad Dot as decimal separator", + "version": "1.0", + "depends": [ + "web", + ], + "author": "OdooMRP team, " + "AvanzOSC, " + "Serv. Tecnol. Avanzados - Pedro M. Baeza", + "website": "http://www.odoomrp.com", + "contributors": [ + "Oihane Crucelaegui ", + "Pedro M. Baeza ", + "Ana Juaristi ", + ], + "category": "Custom Module", + "summary": "", + "data": [ + "views/web_decimal_numpad_dot.xml", + ], + "installable": True, +} diff --git a/web_decimal_numpad_dot/static/src/js/formats.js b/web_decimal_numpad_dot/static/src/js/formats.js new file mode 100644 index 00000000..22131d4d --- /dev/null +++ b/web_decimal_numpad_dot/static/src/js/formats.js @@ -0,0 +1,376 @@ + +(function() { + +var instance = openerp; +openerp.web.formats = {}; + +var _t = instance.web._t; + +/** + * Intersperses ``separator`` in ``str`` at the positions indicated by + * ``indices``. + * + * ``indices`` is an array of relative offsets (from the previous insertion + * position, starting from the end of the string) at which to insert + * ``separator``. + * + * There are two special values: + * + * ``-1`` + * indicates the insertion should end now + * ``0`` + * indicates that the previous section pattern should be repeated (until all + * of ``str`` is consumed) + * + * @param {String} str + * @param {Array} indices + * @param {String} separator + * @returns {String} + */ +instance.web.intersperse = function (str, indices, separator) { + separator = separator || ''; + var result = [], last = str.length; + + for(var i=0; i stop + break; + } else if (section === 0) { + // repeat previous section forever + //noinspection AssignmentToForLoopParameterJS + section = indices[--i]; + } + result.push(str.substring(last-section, last)); + last -= section; + } + + var s = str.substring(0, last); + if (s) { result.push(s); } + return result.reverse().join(separator); +}; +/** + * Insert "thousands" separators in the provided number (which is actually + * a string) + * + * @param {String} num + * @returns {String} + */ +instance.web.insert_thousand_seps = function (num) { + var negative = num[0] === '-'; + num = (negative ? num.slice(1) : num); + return (negative ? '-' : '') + instance.web.intersperse( + num, _t.database.parameters.grouping, _t.database.parameters.thousands_sep); +}; + +/** + * removes literal (non-format) text from a date or time pattern, as datejs can + * not deal with literal text in format strings (whatever the format), whereas + * strftime allows for literal characters + * + * @param {String} value original format + */ +instance.web.strip_raw_chars = function (value) { + var isletter = /[a-zA-Z]/, output = []; + for(var index=0; index < value.length; ++index) { + var character = value[index]; + if(isletter.test(character) && (index === 0 || value[index-1] !== '%')) { + continue; + } + output.push(character); + } + return output.join(''); +}; +var normalize_format = function (format) { + return Date.normalizeFormat(instance.web.strip_raw_chars(format)); +}; + +/** + * Check with a scary heuristic if the value is a bin_size or not. + * If not, compute an approximate size out of the base64 encoded string. + * + * @param {String} value original format + */ +instance.web.binary_to_binsize = function (value) { + if (!value) { + return instance.web.human_size(0); + } + if (value.substr(0, 10).indexOf(' ') == -1) { + // Computing approximate size out of base64 encoded string + // http://en.wikipedia.org/wiki/Base64#MIME + return instance.web.human_size(value.length / 1.37); + } else { + // already bin_size + return value; + } +}; + +/** + * Returns a human readable size + * + * @param {Number} numner of bytes + */ +instance.web.human_size = function(size) { + var units = _t("Bytes,Kb,Mb,Gb,Tb,Pb,Eb,Zb,Yb").split(','); + var i = 0; + while (size >= 1024) { + size /= 1024; + ++i; + } + return size.toFixed(2) + ' ' + units[i]; +}; + +/** + * Formats a single atomic value based on a field descriptor + * + * @param {Object} value read from OpenERP + * @param {Object} descriptor union of orm field and view field + * @param {Object} [descriptor.widget] widget to use to display the value + * @param {Object} descriptor.type fallback if no widget is provided, or if the provided widget is unknown + * @param {Object} [descriptor.digits] used for the formatting of floats + * @param {String} [value_if_empty=''] returned if the ``value`` argument is considered empty + */ +instance.web.format_value = function (value, descriptor, value_if_empty) { + // If NaN value, display as with a `false` (empty cell) + if (typeof value === 'number' && isNaN(value)) { + value = false; + } + //noinspection FallthroughInSwitchStatementJS + switch (value) { + case '': + if (descriptor.type === 'char' || descriptor.type === 'text') { + return ''; + } + console.warn('Field', descriptor, 'had an empty string as value, treating as false...'); + return value_if_empty === undefined ? '' : value_if_empty; + case false: + case undefined: + case Infinity: + case -Infinity: + return value_if_empty === undefined ? '' : value_if_empty; + } + var l10n = _t.database.parameters; + switch (descriptor.widget || descriptor.type || (descriptor.field && descriptor.field.type)) { + case 'id': + return value.toString(); + case 'integer': + return instance.web.insert_thousand_seps( + _.str.sprintf('%d', value)); + case 'float': + var digits = descriptor.digits ? descriptor.digits : [69,2]; + digits = typeof digits === "string" ? py.eval(digits) : digits; + var precision = digits[1]; + var formatted = _.str.sprintf('%.' + precision + 'f', value).split('.'); + formatted[0] = instance.web.insert_thousand_seps(formatted[0]); + return formatted.join(l10n.decimal_point); + case 'float_time': + var pattern = '%02d:%02d'; + if (value < 0) { + value = Math.abs(value); + pattern = '-' + pattern; + } + var hour = Math.floor(value); + var min = Math.round((value % 1) * 60); + if (min == 60){ + min = 0; + hour = hour + 1; + } + return _.str.sprintf(pattern, hour, min); + case 'many2one': + // name_get value format + return value[1] ? value[1].split("\n")[0] : value[1]; + case 'one2many': + case 'many2many': + if (typeof value === 'string') { + return value; + } + return _.str.sprintf(_t("(%d records)"), value.length); + case 'datetime': + if (typeof(value) == "string") + value = instance.web.auto_str_to_date(value); + + return value.toString(normalize_format(l10n.date_format) + + ' ' + normalize_format(l10n.time_format)); + case 'date': + if (typeof(value) == "string") + value = instance.web.auto_str_to_date(value); + return value.toString(normalize_format(l10n.date_format)); + case 'time': + if (typeof(value) == "string") + value = instance.web.auto_str_to_date(value); + return value.toString(normalize_format(l10n.time_format)); + case 'selection': case 'statusbar': + // Each choice is [value, label] + if(_.isArray(value)) { + return value[1]; + } + var result = _(descriptor.selection).detect(function (choice) { + return choice[0] === value; + }); + if (result) { return result[1]; } + return; + default: + return value; + } +}; + +instance.web.parse_value = function (value, descriptor, value_if_empty) { + var date_pattern = normalize_format(_t.database.parameters.date_format), + time_pattern = normalize_format(_t.database.parameters.time_format); + switch (value) { + case false: + case "": + return value_if_empty === undefined ? false : value_if_empty; + } + var tmp; + switch (descriptor.widget || descriptor.type || (descriptor.field && descriptor.field.type)) { + case 'integer': + do { + tmp = value; + value = value.replace(instance.web._t.database.parameters.thousands_sep, ""); + } while(tmp !== value); + tmp = Number(value); + // do not accept not numbers or float values + if (isNaN(tmp) || tmp % 1) + throw new Error(_.str.sprintf(_t("'%s' is not a correct integer"), value)); + return tmp; + case 'float': + tmp = Number(value); + if (!isNaN(tmp)) + return tmp; + + var tmp2 = value; + do { + tmp = tmp2; + tmp2 = tmp.replace(instance.web._t.database.parameters.thousands_sep, ""); + } while(tmp !== tmp2); + var reformatted_value = tmp.replace(instance.web._t.database.parameters.decimal_point, "."); + var parsed = Number(reformatted_value); + if (isNaN(parsed)) + throw new Error(_.str.sprintf(_t("'%s' is not a correct float"), value)); + return parsed; + case 'float_time': + var factor = 1; + if (value[0] === '-') { + value = value.slice(1); + factor = -1; + } + var float_time_pair = value.split(":"); + if (float_time_pair.length != 2) + return factor * instance.web.parse_value(value, {type: "float"}); + var hours = instance.web.parse_value(float_time_pair[0], {type: "integer"}); + var minutes = instance.web.parse_value(float_time_pair[1], {type: "integer"}); + return factor * (hours + (minutes / 60)); + case 'progressbar': + return instance.web.parse_value(value, {type: "float"}); + case 'datetime': + var datetime = Date.parseExact( + value, (date_pattern + ' ' + time_pattern)); + if (datetime !== null) + return instance.web.datetime_to_str(datetime); + datetime = Date.parseExact(value, (date_pattern)); + if (datetime !== null) + return instance.web.datetime_to_str(datetime); + var leading_zero_value = value.toString().replace(/\d+/g, function(m){ + return m.length === 1 ? "0" + m : m ; + }); + datetime = Date.parseExact(leading_zero_value, (date_pattern + ' ' + time_pattern)); + if (datetime !== null) + return instance.web.datetime_to_str(datetime); + datetime = Date.parseExact(leading_zero_value, (date_pattern)); + if (datetime !== null) + return instance.web.datetime_to_str(datetime); + datetime = Date.parse(value); + if (datetime !== null) + return instance.web.datetime_to_str(datetime); + throw new Error(_.str.sprintf(_t("'%s' is not a correct datetime"), value)); + case 'date': + var date = Date.parseExact(value, date_pattern); + if (date !== null) + return instance.web.date_to_str(date); + date = Date.parseExact(value.toString().replace(/\d+/g, function(m){ + return m.length === 1 ? "0" + m : m ; + }), date_pattern); + if (date !== null) + return instance.web.date_to_str(date); + date = Date.parse(value); + if (date !== null) + return instance.web.date_to_str(date); + throw new Error(_.str.sprintf(_t("'%s' is not a correct date"), value)); + case 'time': + var time = Date.parseExact(value, time_pattern); + if (time !== null) + return instance.web.time_to_str(time); + time = Date.parse(value); + if (time !== null) + return instance.web.time_to_str(time); + throw new Error(_.str.sprintf(_t("'%s' is not a correct time"), value)); + } + return value; +}; + +instance.web.auto_str_to_date = function(value, type) { + try { + return instance.web.str_to_datetime(value); + } catch(e) {} + try { + return instance.web.str_to_date(value); + } catch(e) {} + try { + return instance.web.str_to_time(value); + } catch(e) {} + throw new Error(_.str.sprintf(_t("'%s' is not a correct date, datetime nor time"), value)); +}; + +instance.web.auto_date_to_str = function(value, type) { + switch(type) { + case 'datetime': + return instance.web.datetime_to_str(value); + case 'date': + return instance.web.date_to_str(value); + case 'time': + return instance.web.time_to_str(value); + default: + throw new Error(_.str.sprintf(_t("'%s' is not convertible to date, datetime nor time"), type)); + } +}; + +/** + * performs a half up rounding with arbitrary precision, correcting for float loss of precision + * See the corresponding float_round() in server/tools/float_utils.py for more info + * @param {Number} the value to be rounded + * @param {Number} a precision parameter. eg: 0.01 rounds to two digits. + */ +instance.web.round_precision = function(value, precision){ + if (!value) { + return 0; + } else if (!precision || precision < 0) { + precision = 1; + } + var normalized_value = value / precision; + var epsilon_magnitude = Math.log(Math.abs(normalized_value))/Math.log(2); + var epsilon = Math.pow(2, epsilon_magnitude - 53); + normalized_value += normalized_value >= 0 ? epsilon : -epsilon; + var rounded_value = Math.round(normalized_value); + return rounded_value * precision; +}; + +/** + * performs a half up rounding with a fixed amount of decimals, correcting for float loss of precision + * See the corresponding float_round() in server/tools/float_utils.py for more info + * @param {Number} the value to be rounded + * @param {Number} the number of decimals. eg: round_decimals(3.141592,2) -> 3.14 + */ +instance.web.round_decimals = function(value, decimals){ + return instance.web.round_precision(value, Math.pow(10,-decimals)); +}; + +instance.web.float_is_zero = function(value, decimals){ + epsilon = Math.pow(10, -decimals); + return Math.abs(instance.web.round_precision(value, epsilon)) < epsilon; +}; + +})(); diff --git a/web_decimal_numpad_dot/static/src/js/numpad_dot.js b/web_decimal_numpad_dot/static/src/js/numpad_dot.js new file mode 100644 index 00000000..ec9c34cf --- /dev/null +++ b/web_decimal_numpad_dot/static/src/js/numpad_dot.js @@ -0,0 +1,23 @@ +(function() { + +var instance = openerp; + +instance.web.form.FieldFloat = instance.web.form.FieldChar.extend({ + is_field_number: true, + widget_class: 'oe_form_field_float', + events: { + "keyup": "floatKeyup", + }, + floatKeyup: function(e){ + var code = e.which ? e.which : e.keyCode; + + if (code === 110){ + var input = this.$("input").val(); + input = input.substr(0, input.length -1); + input += instance.web._t.database.parameters.decimal_point; + this.set("value", input); + } + }, +}); + +})(); diff --git a/web_decimal_numpad_dot/views/web_decimal_numpad_dot.xml b/web_decimal_numpad_dot/views/web_decimal_numpad_dot.xml new file mode 100644 index 00000000..3345c5dc --- /dev/null +++ b/web_decimal_numpad_dot/views/web_decimal_numpad_dot.xml @@ -0,0 +1,10 @@ + + + + + + From 8cd453b114bf4f4419cd9d86734d93f58b6262fa Mon Sep 17 00:00:00 2001 From: oihane Date: Fri, 13 Nov 2015 09:18:09 +0100 Subject: [PATCH 02/12] [IMP] web_decimal_numpad_dot: now changes . for , in floating numbers --- .../static/src/js/numpad_dot.js | 35 +++++++++---------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/web_decimal_numpad_dot/static/src/js/numpad_dot.js b/web_decimal_numpad_dot/static/src/js/numpad_dot.js index ec9c34cf..621bf89c 100644 --- a/web_decimal_numpad_dot/static/src/js/numpad_dot.js +++ b/web_decimal_numpad_dot/static/src/js/numpad_dot.js @@ -1,23 +1,20 @@ (function() { -var instance = openerp; - -instance.web.form.FieldFloat = instance.web.form.FieldChar.extend({ - is_field_number: true, - widget_class: 'oe_form_field_float', - events: { - "keyup": "floatKeyup", - }, - floatKeyup: function(e){ - var code = e.which ? e.which : e.keyCode; - - if (code === 110){ - var input = this.$("input").val(); - input = input.substr(0, input.length -1); - input += instance.web._t.database.parameters.decimal_point; - this.set("value", input); - } - }, -}); + var instance = openerp; + instance.web.form.FieldFloat = instance.web.form.FieldChar.extend({ + is_field_number: true, + widget_class: 'oe_form_field_float', + events: { + "keypress": "floatKeypress", + }, + floatKeypress: function(e){ + if(e.keyCode == '46' || e.charCode == '46'){ + //Cancel the keypress + e.preventDefault(); + // Add the comma to the value of the input field + this.$("input").val(this.$("input").val() + ','); + } + }, + }); })(); From 414dc03faecab571a3afdf7ffcf47cf68e5a3d12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Omar=20Casti=C3=B1eira=20Saavedra?= Date: Tue, 15 Dec 2015 11:53:05 +0100 Subject: [PATCH 03/12] [IMP] web_decimal_numpad_dot: fixed float widget not working properly --- web_decimal_numpad_dot/static/src/js/numpad_dot.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/web_decimal_numpad_dot/static/src/js/numpad_dot.js b/web_decimal_numpad_dot/static/src/js/numpad_dot.js index 621bf89c..55f9917c 100644 --- a/web_decimal_numpad_dot/static/src/js/numpad_dot.js +++ b/web_decimal_numpad_dot/static/src/js/numpad_dot.js @@ -2,11 +2,13 @@ var instance = openerp; - instance.web.form.FieldFloat = instance.web.form.FieldChar.extend({ - is_field_number: true, - widget_class: 'oe_form_field_float', - events: { - "keypress": "floatKeypress", + instance.web.form.FieldFloat = instance.web.form.FieldFloat.extend({ + render_value: function() { + var self = this; + this._super(); + if (!this.get('readonly')){ + this.$el.find('input').on('keypress', this.floatKeypress.bind(this)); + } }, floatKeypress: function(e){ if(e.keyCode == '46' || e.charCode == '46'){ @@ -14,7 +16,7 @@ e.preventDefault(); // Add the comma to the value of the input field this.$("input").val(this.$("input").val() + ','); - } + } }, }); })(); From 1225b34723334b5d359715bd32fb0f9b22889c29 Mon Sep 17 00:00:00 2001 From: Roel Adriaans Date: Wed, 16 Dec 2015 10:45:30 +0100 Subject: [PATCH 04/12] [IMP] web_decimal_numpad_dot: use the decimal_point defined in user language --- web_decimal_numpad_dot/static/src/js/numpad_dot.js | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/web_decimal_numpad_dot/static/src/js/numpad_dot.js b/web_decimal_numpad_dot/static/src/js/numpad_dot.js index 55f9917c..560ce048 100644 --- a/web_decimal_numpad_dot/static/src/js/numpad_dot.js +++ b/web_decimal_numpad_dot/static/src/js/numpad_dot.js @@ -11,12 +11,18 @@ } }, floatKeypress: function(e){ - if(e.keyCode == '46' || e.charCode == '46'){ - //Cancel the keypress + if (e.keyCode == '46' || e.charCode == '46') { + // Cancel the keypress e.preventDefault(); // Add the comma to the value of the input field - this.$("input").val(this.$("input").val() + ','); + this.$("input").val(this.$("input").val() + instance.web._t.database.parameters.decimal_point); } + else if (e.keyCode == '44' || e.charCode == '44') { + // Cancel the keypress + e.preventDefault(); + // Add the comma to the value of the input field + this.$("input").val(this.$("input").val() + instance.web._t.database.parameters.thousands_sep); + } }, }); })(); From 28af79ac38ebb25750bc2c3fd92244dc1f374e63 Mon Sep 17 00:00:00 2001 From: David Vidal Date: Mon, 28 Aug 2017 11:22:11 +0200 Subject: [PATCH 05/12] [MIG] web_decimal_numpad_dot: Migration to 10.0 --- web_decimal_numpad_dot/README.rst | 49 ++- web_decimal_numpad_dot/__init__.py | 3 - web_decimal_numpad_dot/__manifest__.py | 27 ++ web_decimal_numpad_dot/__openerp__.py | 40 -- .../static/description/icon.png | Bin 0 -> 38689 bytes .../static/src/js/formats.js | 376 ------------------ .../static/src/js/numpad_dot.js | 56 +-- .../views/web_decimal_numpad_dot.xml | 19 +- 8 files changed, 114 insertions(+), 456 deletions(-) create mode 100644 web_decimal_numpad_dot/__manifest__.py delete mode 100644 web_decimal_numpad_dot/__openerp__.py create mode 100644 web_decimal_numpad_dot/static/description/icon.png delete mode 100644 web_decimal_numpad_dot/static/src/js/formats.js diff --git a/web_decimal_numpad_dot/README.rst b/web_decimal_numpad_dot/README.rst index f0a463a2..71f5e51c 100644 --- a/web_decimal_numpad_dot/README.rst +++ b/web_decimal_numpad_dot/README.rst @@ -1,7 +1,31 @@ -Web - Numpad Dot as decimal separator -===================================== +.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg + :target: http://www.gnu.org/licenses/agpl + :alt: License: AGPL-3 +=============================== +Numpad Dot as decimal separator +=============================== +Allows using numpad dot to enter period decimal separator even in localizations +where comma is used instead of period. + +Usage +===== + +Whenever on a float or monetary input field pressing numpad dot produces the +proper decimal separator for the active localization. + +.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas + :alt: Try me on Runbot + :target: https://runbot.odoo-community.org/runbot/web/162 + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues +`_. In case of trouble, please +check there if your issue has already been reported. If you spotted it first, +help us smash it by providing detailed and welcomed feedback. Credits ======= @@ -10,5 +34,24 @@ Contributors ------------ * Oihane Crucelaegui -* Pedro M. Baeza +* Pedro M. Baeza * Ana Juaristi +* Omar Castiñeira Saavedra +* Oliver Dony <@odony> +* Wim Audenaert +* David Vidal + +Maintainer +---------- + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +This module is maintained by the OCA. + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +To contribute to this module, please visit https://odoo-community.org. diff --git a/web_decimal_numpad_dot/__init__.py b/web_decimal_numpad_dot/__init__.py index ee9e0cf6..dae354a6 100644 --- a/web_decimal_numpad_dot/__init__.py +++ b/web_decimal_numpad_dot/__init__.py @@ -1,4 +1 @@ # -*- encoding: utf-8 -*- -############################################################################## -# For copyright and license notices, see __openerp__.py file in root directory -############################################################################## diff --git a/web_decimal_numpad_dot/__manifest__.py b/web_decimal_numpad_dot/__manifest__.py new file mode 100644 index 00000000..457c130b --- /dev/null +++ b/web_decimal_numpad_dot/__manifest__.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +# Copyright 2015 AvanzOSC - Oihane Crucelaegui +# Copyright 2015 Tecnativa - Pedro M. Baeza +# Copyright 2015 Comunitea - Omar Castiñeira Saavedra +# Copyright 2016 Oliver Dony +# Copyright 2017 Tecnativa - David Vidal +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + "name": "Web - Numpad Dot as decimal separator", + "version": "10.0.1.0.0", + "license": "AGPL-3", + "summary": "Allows using numpad dot to enter period decimal separator", + "depends": [ + "web", + ], + "author": "AvanzOSC, " + "Comunitea, " + "Tecnativa, " + "Odoo Community Association (OCA)", + "website": "https://odoo-community.org/", + "category": "Web", + "data": [ + "views/web_decimal_numpad_dot.xml", + ], + "installable": True, +} diff --git a/web_decimal_numpad_dot/__openerp__.py b/web_decimal_numpad_dot/__openerp__.py deleted file mode 100644 index 9e6051de..00000000 --- a/web_decimal_numpad_dot/__openerp__.py +++ /dev/null @@ -1,40 +0,0 @@ -# -*- encoding: utf-8 -*- -############################################################################## -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published -# by the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see http://www.gnu.org/licenses/. -# -############################################################################## - -{ - "name": "Web - Numpad Dot as decimal separator", - "version": "1.0", - "depends": [ - "web", - ], - "author": "OdooMRP team, " - "AvanzOSC, " - "Serv. Tecnol. Avanzados - Pedro M. Baeza", - "website": "http://www.odoomrp.com", - "contributors": [ - "Oihane Crucelaegui ", - "Pedro M. Baeza ", - "Ana Juaristi ", - ], - "category": "Custom Module", - "summary": "", - "data": [ - "views/web_decimal_numpad_dot.xml", - ], - "installable": True, -} diff --git a/web_decimal_numpad_dot/static/description/icon.png b/web_decimal_numpad_dot/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..9c205cbc407e01b23717ce2f173018c6b443e782 GIT binary patch literal 38689 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

@Y{7C%@TN8aC$(b?ezb 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 indices - * @param {String} separator - * @returns {String} - */ -instance.web.intersperse = function (str, indices, separator) { - separator = separator || ''; - var result = [], last = str.length; - - for(var i=0; i stop - break; - } else if (section === 0) { - // repeat previous section forever - //noinspection AssignmentToForLoopParameterJS - section = indices[--i]; - } - result.push(str.substring(last-section, last)); - last -= section; - } - - var s = str.substring(0, last); - if (s) { result.push(s); } - return result.reverse().join(separator); -}; -/** - * Insert "thousands" separators in the provided number (which is actually - * a string) - * - * @param {String} num - * @returns {String} - */ -instance.web.insert_thousand_seps = function (num) { - var negative = num[0] === '-'; - num = (negative ? num.slice(1) : num); - return (negative ? '-' : '') + instance.web.intersperse( - num, _t.database.parameters.grouping, _t.database.parameters.thousands_sep); -}; - -/** - * removes literal (non-format) text from a date or time pattern, as datejs can - * not deal with literal text in format strings (whatever the format), whereas - * strftime allows for literal characters - * - * @param {String} value original format - */ -instance.web.strip_raw_chars = function (value) { - var isletter = /[a-zA-Z]/, output = []; - for(var index=0; index < value.length; ++index) { - var character = value[index]; - if(isletter.test(character) && (index === 0 || value[index-1] !== '%')) { - continue; - } - output.push(character); - } - return output.join(''); -}; -var normalize_format = function (format) { - return Date.normalizeFormat(instance.web.strip_raw_chars(format)); -}; - -/** - * Check with a scary heuristic if the value is a bin_size or not. - * If not, compute an approximate size out of the base64 encoded string. - * - * @param {String} value original format - */ -instance.web.binary_to_binsize = function (value) { - if (!value) { - return instance.web.human_size(0); - } - if (value.substr(0, 10).indexOf(' ') == -1) { - // Computing approximate size out of base64 encoded string - // http://en.wikipedia.org/wiki/Base64#MIME - return instance.web.human_size(value.length / 1.37); - } else { - // already bin_size - return value; - } -}; - -/** - * Returns a human readable size - * - * @param {Number} numner of bytes - */ -instance.web.human_size = function(size) { - var units = _t("Bytes,Kb,Mb,Gb,Tb,Pb,Eb,Zb,Yb").split(','); - var i = 0; - while (size >= 1024) { - size /= 1024; - ++i; - } - return size.toFixed(2) + ' ' + units[i]; -}; - -/** - * Formats a single atomic value based on a field descriptor - * - * @param {Object} value read from OpenERP - * @param {Object} descriptor union of orm field and view field - * @param {Object} [descriptor.widget] widget to use to display the value - * @param {Object} descriptor.type fallback if no widget is provided, or if the provided widget is unknown - * @param {Object} [descriptor.digits] used for the formatting of floats - * @param {String} [value_if_empty=''] returned if the ``value`` argument is considered empty - */ -instance.web.format_value = function (value, descriptor, value_if_empty) { - // If NaN value, display as with a `false` (empty cell) - if (typeof value === 'number' && isNaN(value)) { - value = false; - } - //noinspection FallthroughInSwitchStatementJS - switch (value) { - case '': - if (descriptor.type === 'char' || descriptor.type === 'text') { - return ''; - } - console.warn('Field', descriptor, 'had an empty string as value, treating as false...'); - return value_if_empty === undefined ? '' : value_if_empty; - case false: - case undefined: - case Infinity: - case -Infinity: - return value_if_empty === undefined ? '' : value_if_empty; - } - var l10n = _t.database.parameters; - switch (descriptor.widget || descriptor.type || (descriptor.field && descriptor.field.type)) { - case 'id': - return value.toString(); - case 'integer': - return instance.web.insert_thousand_seps( - _.str.sprintf('%d', value)); - case 'float': - var digits = descriptor.digits ? descriptor.digits : [69,2]; - digits = typeof digits === "string" ? py.eval(digits) : digits; - var precision = digits[1]; - var formatted = _.str.sprintf('%.' + precision + 'f', value).split('.'); - formatted[0] = instance.web.insert_thousand_seps(formatted[0]); - return formatted.join(l10n.decimal_point); - case 'float_time': - var pattern = '%02d:%02d'; - if (value < 0) { - value = Math.abs(value); - pattern = '-' + pattern; - } - var hour = Math.floor(value); - var min = Math.round((value % 1) * 60); - if (min == 60){ - min = 0; - hour = hour + 1; - } - return _.str.sprintf(pattern, hour, min); - case 'many2one': - // name_get value format - return value[1] ? value[1].split("\n")[0] : value[1]; - case 'one2many': - case 'many2many': - if (typeof value === 'string') { - return value; - } - return _.str.sprintf(_t("(%d records)"), value.length); - case 'datetime': - if (typeof(value) == "string") - value = instance.web.auto_str_to_date(value); - - return value.toString(normalize_format(l10n.date_format) - + ' ' + normalize_format(l10n.time_format)); - case 'date': - if (typeof(value) == "string") - value = instance.web.auto_str_to_date(value); - return value.toString(normalize_format(l10n.date_format)); - case 'time': - if (typeof(value) == "string") - value = instance.web.auto_str_to_date(value); - return value.toString(normalize_format(l10n.time_format)); - case 'selection': case 'statusbar': - // Each choice is [value, label] - if(_.isArray(value)) { - return value[1]; - } - var result = _(descriptor.selection).detect(function (choice) { - return choice[0] === value; - }); - if (result) { return result[1]; } - return; - default: - return value; - } -}; - -instance.web.parse_value = function (value, descriptor, value_if_empty) { - var date_pattern = normalize_format(_t.database.parameters.date_format), - time_pattern = normalize_format(_t.database.parameters.time_format); - switch (value) { - case false: - case "": - return value_if_empty === undefined ? false : value_if_empty; - } - var tmp; - switch (descriptor.widget || descriptor.type || (descriptor.field && descriptor.field.type)) { - case 'integer': - do { - tmp = value; - value = value.replace(instance.web._t.database.parameters.thousands_sep, ""); - } while(tmp !== value); - tmp = Number(value); - // do not accept not numbers or float values - if (isNaN(tmp) || tmp % 1) - throw new Error(_.str.sprintf(_t("'%s' is not a correct integer"), value)); - return tmp; - case 'float': - tmp = Number(value); - if (!isNaN(tmp)) - return tmp; - - var tmp2 = value; - do { - tmp = tmp2; - tmp2 = tmp.replace(instance.web._t.database.parameters.thousands_sep, ""); - } while(tmp !== tmp2); - var reformatted_value = tmp.replace(instance.web._t.database.parameters.decimal_point, "."); - var parsed = Number(reformatted_value); - if (isNaN(parsed)) - throw new Error(_.str.sprintf(_t("'%s' is not a correct float"), value)); - return parsed; - case 'float_time': - var factor = 1; - if (value[0] === '-') { - value = value.slice(1); - factor = -1; - } - var float_time_pair = value.split(":"); - if (float_time_pair.length != 2) - return factor * instance.web.parse_value(value, {type: "float"}); - var hours = instance.web.parse_value(float_time_pair[0], {type: "integer"}); - var minutes = instance.web.parse_value(float_time_pair[1], {type: "integer"}); - return factor * (hours + (minutes / 60)); - case 'progressbar': - return instance.web.parse_value(value, {type: "float"}); - case 'datetime': - var datetime = Date.parseExact( - value, (date_pattern + ' ' + time_pattern)); - if (datetime !== null) - return instance.web.datetime_to_str(datetime); - datetime = Date.parseExact(value, (date_pattern)); - if (datetime !== null) - return instance.web.datetime_to_str(datetime); - var leading_zero_value = value.toString().replace(/\d+/g, function(m){ - return m.length === 1 ? "0" + m : m ; - }); - datetime = Date.parseExact(leading_zero_value, (date_pattern + ' ' + time_pattern)); - if (datetime !== null) - return instance.web.datetime_to_str(datetime); - datetime = Date.parseExact(leading_zero_value, (date_pattern)); - if (datetime !== null) - return instance.web.datetime_to_str(datetime); - datetime = Date.parse(value); - if (datetime !== null) - return instance.web.datetime_to_str(datetime); - throw new Error(_.str.sprintf(_t("'%s' is not a correct datetime"), value)); - case 'date': - var date = Date.parseExact(value, date_pattern); - if (date !== null) - return instance.web.date_to_str(date); - date = Date.parseExact(value.toString().replace(/\d+/g, function(m){ - return m.length === 1 ? "0" + m : m ; - }), date_pattern); - if (date !== null) - return instance.web.date_to_str(date); - date = Date.parse(value); - if (date !== null) - return instance.web.date_to_str(date); - throw new Error(_.str.sprintf(_t("'%s' is not a correct date"), value)); - case 'time': - var time = Date.parseExact(value, time_pattern); - if (time !== null) - return instance.web.time_to_str(time); - time = Date.parse(value); - if (time !== null) - return instance.web.time_to_str(time); - throw new Error(_.str.sprintf(_t("'%s' is not a correct time"), value)); - } - return value; -}; - -instance.web.auto_str_to_date = function(value, type) { - try { - return instance.web.str_to_datetime(value); - } catch(e) {} - try { - return instance.web.str_to_date(value); - } catch(e) {} - try { - return instance.web.str_to_time(value); - } catch(e) {} - throw new Error(_.str.sprintf(_t("'%s' is not a correct date, datetime nor time"), value)); -}; - -instance.web.auto_date_to_str = function(value, type) { - switch(type) { - case 'datetime': - return instance.web.datetime_to_str(value); - case 'date': - return instance.web.date_to_str(value); - case 'time': - return instance.web.time_to_str(value); - default: - throw new Error(_.str.sprintf(_t("'%s' is not convertible to date, datetime nor time"), type)); - } -}; - -/** - * performs a half up rounding with arbitrary precision, correcting for float loss of precision - * See the corresponding float_round() in server/tools/float_utils.py for more info - * @param {Number} the value to be rounded - * @param {Number} a precision parameter. eg: 0.01 rounds to two digits. - */ -instance.web.round_precision = function(value, precision){ - if (!value) { - return 0; - } else if (!precision || precision < 0) { - precision = 1; - } - var normalized_value = value / precision; - var epsilon_magnitude = Math.log(Math.abs(normalized_value))/Math.log(2); - var epsilon = Math.pow(2, epsilon_magnitude - 53); - normalized_value += normalized_value >= 0 ? epsilon : -epsilon; - var rounded_value = Math.round(normalized_value); - return rounded_value * precision; -}; - -/** - * performs a half up rounding with a fixed amount of decimals, correcting for float loss of precision - * See the corresponding float_round() in server/tools/float_utils.py for more info - * @param {Number} the value to be rounded - * @param {Number} the number of decimals. eg: round_decimals(3.141592,2) -> 3.14 - */ -instance.web.round_decimals = function(value, decimals){ - return instance.web.round_precision(value, Math.pow(10,-decimals)); -}; - -instance.web.float_is_zero = function(value, decimals){ - epsilon = Math.pow(10, -decimals); - return Math.abs(instance.web.round_precision(value, epsilon)) < epsilon; -}; - -})(); diff --git a/web_decimal_numpad_dot/static/src/js/numpad_dot.js b/web_decimal_numpad_dot/static/src/js/numpad_dot.js index 560ce048..8d227803 100644 --- a/web_decimal_numpad_dot/static/src/js/numpad_dot.js +++ b/web_decimal_numpad_dot/static/src/js/numpad_dot.js @@ -1,28 +1,34 @@ -(function() { +/* License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). */ - var instance = openerp; +odoo.define("web_decimal_numpad_dot.FieldFloat", function (require) { + "use strict"; - instance.web.form.FieldFloat = instance.web.form.FieldFloat.extend({ - render_value: function() { - var self = this; - this._super(); - if (!this.get('readonly')){ - this.$el.find('input').on('keypress', this.floatKeypress.bind(this)); - } - }, - floatKeypress: function(e){ - if (e.keyCode == '46' || e.charCode == '46') { - // Cancel the keypress - e.preventDefault(); - // Add the comma to the value of the input field - this.$("input").val(this.$("input").val() + instance.web._t.database.parameters.decimal_point); - } - else if (e.keyCode == '44' || e.charCode == '44') { - // Cancel the keypress - e.preventDefault(); - // Add the comma to the value of the input field - this.$("input").val(this.$("input").val() + instance.web._t.database.parameters.thousands_sep); - } - }, + var form_widgets = require("web.form_widgets"); + var translation = require("web.translation"); + + form_widgets.FieldFloat.include({ + init: function () { + this.events.keypress = function (event) { + if (event.which === 46 || event.which === 44) { + event.preventDefault(); + var input = this.$input || this.$("input"); + var l10n = translation._t.database.parameters; + if (!_.str.contains(input.val(), l10n.decimal_point)) { + try { + var caret_pos = input[0].selectionStart; + var selection_end = input[0].selectionEnd; + var cur_val = input.val(); + var newval = cur_val.substring(0, caret_pos) + l10n.decimal_point + cur_val.substring(selection_end); + input.val(newval); + input[0].selectionStart = input[0].selectionEnd = caret_pos + 1; + } catch (error) { + //fallback to appending if no caret position can be determined + input.val(input.val() + l10n.decimal_point); + } + } + } + }; + return this._super.apply(this, arguments); + } }); -})(); +}); diff --git a/web_decimal_numpad_dot/views/web_decimal_numpad_dot.xml b/web_decimal_numpad_dot/views/web_decimal_numpad_dot.xml index 3345c5dc..d4815753 100644 --- a/web_decimal_numpad_dot/views/web_decimal_numpad_dot.xml +++ b/web_decimal_numpad_dot/views/web_decimal_numpad_dot.xml @@ -1,10 +1,11 @@ - - - - - + + + + + From f892e30a6f395162229c8eef54b62b550bc8f0cb Mon Sep 17 00:00:00 2001 From: Jairo Llopis Date: Thu, 7 Sep 2017 15:10:41 +0200 Subject: [PATCH 06/12] [FIX][IMP][web_decimal_numpad_dot] Support float time - All Char widgets were getting affected by this behavior. - Time float widgets now get ":" instead of "," or ".". - Little code refactor. - Only detect numpad dot, not normal dot and comma. --- web_decimal_numpad_dot/README.rst | 1 + web_decimal_numpad_dot/__manifest__.py | 2 +- .../static/src/js/numpad_dot.js | 51 +++++++++++-------- 3 files changed, 32 insertions(+), 22 deletions(-) diff --git a/web_decimal_numpad_dot/README.rst b/web_decimal_numpad_dot/README.rst index 71f5e51c..fd318468 100644 --- a/web_decimal_numpad_dot/README.rst +++ b/web_decimal_numpad_dot/README.rst @@ -40,6 +40,7 @@ Contributors * Oliver Dony <@odony> * Wim Audenaert * David Vidal +* Jairo Llopis Maintainer ---------- diff --git a/web_decimal_numpad_dot/__manifest__.py b/web_decimal_numpad_dot/__manifest__.py index 457c130b..10c957dc 100644 --- a/web_decimal_numpad_dot/__manifest__.py +++ b/web_decimal_numpad_dot/__manifest__.py @@ -8,7 +8,7 @@ { "name": "Web - Numpad Dot as decimal separator", - "version": "10.0.1.0.0", + "version": "10.0.1.1.0", "license": "AGPL-3", "summary": "Allows using numpad dot to enter period decimal separator", "depends": [ diff --git a/web_decimal_numpad_dot/static/src/js/numpad_dot.js b/web_decimal_numpad_dot/static/src/js/numpad_dot.js index 8d227803..4e8b7de4 100644 --- a/web_decimal_numpad_dot/static/src/js/numpad_dot.js +++ b/web_decimal_numpad_dot/static/src/js/numpad_dot.js @@ -8,27 +8,36 @@ odoo.define("web_decimal_numpad_dot.FieldFloat", function (require) { form_widgets.FieldFloat.include({ init: function () { - this.events.keypress = function (event) { - if (event.which === 46 || event.which === 44) { - event.preventDefault(); - var input = this.$input || this.$("input"); - var l10n = translation._t.database.parameters; - if (!_.str.contains(input.val(), l10n.decimal_point)) { - try { - var caret_pos = input[0].selectionStart; - var selection_end = input[0].selectionEnd; - var cur_val = input.val(); - var newval = cur_val.substring(0, caret_pos) + l10n.decimal_point + cur_val.substring(selection_end); - input.val(newval); - input[0].selectionStart = input[0].selectionEnd = caret_pos + 1; - } catch (error) { - //fallback to appending if no caret position can be determined - input.val(input.val() + l10n.decimal_point); - } - } - } - }; + this.events = $.extend({}, this.events, { + "keydown": "numpad_dot_replace", + }); return this._super.apply(this, arguments); - } + }, + + l10n_decimal_point: function () { + return this.widget == "float_time" + ? ":" : translation._t.database.parameters.decimal_point; + }, + + numpad_dot_replace: function (event) { + // Only act on numpad dot key + if (event.keyCode != 110) { + return; + } + event.preventDefault(); + var from = this.$input.prop("selectionStart"), + to = this.$input.prop("selectionEnd"), + cur_val = this.$input.val(), + point = this.l10n_decimal_point(); + // Replace selected text by proper character + this.$input.val( + cur_val.substring(0, from) + + point + + cur_val.substring(to) + ); + // Put user caret in place + to = from + point.length + this.$input.prop("selectionStart", to).prop("selectionEnd", to); + }, }); }); From 11fa9ad5f8bb6eb1a23f5355189f203df6563ec2 Mon Sep 17 00:00:00 2001 From: David Date: Mon, 18 Dec 2017 11:06:01 +0100 Subject: [PATCH 07/12] [MIG] web_decimal_numpad_dot: Migration to 11.0 --- web_decimal_numpad_dot/README.rst | 2 +- web_decimal_numpad_dot/__init__.py | 1 - web_decimal_numpad_dot/__manifest__.py | 4 ++-- web_decimal_numpad_dot/static/src/js/numpad_dot.js | 4 ++-- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/web_decimal_numpad_dot/README.rst b/web_decimal_numpad_dot/README.rst index fd318468..787c7388 100644 --- a/web_decimal_numpad_dot/README.rst +++ b/web_decimal_numpad_dot/README.rst @@ -17,7 +17,7 @@ proper decimal separator for the active localization. .. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas :alt: Try me on Runbot - :target: https://runbot.odoo-community.org/runbot/web/162 + :target: https://runbot.odoo-community.org/runbot/web/162/11.0 Bug Tracker =========== diff --git a/web_decimal_numpad_dot/__init__.py b/web_decimal_numpad_dot/__init__.py index dae354a6..e69de29b 100644 --- a/web_decimal_numpad_dot/__init__.py +++ b/web_decimal_numpad_dot/__init__.py @@ -1 +0,0 @@ -# -*- encoding: utf-8 -*- diff --git a/web_decimal_numpad_dot/__manifest__.py b/web_decimal_numpad_dot/__manifest__.py index 10c957dc..857b38bb 100644 --- a/web_decimal_numpad_dot/__manifest__.py +++ b/web_decimal_numpad_dot/__manifest__.py @@ -8,7 +8,7 @@ { "name": "Web - Numpad Dot as decimal separator", - "version": "10.0.1.1.0", + "version": "11.0.1.0.0", "license": "AGPL-3", "summary": "Allows using numpad dot to enter period decimal separator", "depends": [ @@ -18,7 +18,7 @@ "Comunitea, " "Tecnativa, " "Odoo Community Association (OCA)", - "website": "https://odoo-community.org/", + "website": "https://github.com/OCA/web", "category": "Web", "data": [ "views/web_decimal_numpad_dot.xml", diff --git a/web_decimal_numpad_dot/static/src/js/numpad_dot.js b/web_decimal_numpad_dot/static/src/js/numpad_dot.js index 4e8b7de4..a9860de5 100644 --- a/web_decimal_numpad_dot/static/src/js/numpad_dot.js +++ b/web_decimal_numpad_dot/static/src/js/numpad_dot.js @@ -3,10 +3,10 @@ odoo.define("web_decimal_numpad_dot.FieldFloat", function (require) { "use strict"; - var form_widgets = require("web.form_widgets"); + var basic_fields = require("web.basic_fields"); var translation = require("web.translation"); - form_widgets.FieldFloat.include({ + basic_fields.FieldFloat.include({ init: function () { this.events = $.extend({}, this.events, { "keydown": "numpad_dot_replace", From 797a6fc107404e741103c90f463d8704981dc132 Mon Sep 17 00:00:00 2001 From: David Vidal Date: Fri, 23 Feb 2018 00:54:35 +0100 Subject: [PATCH 08/12] [FIX] web_decimal_numpad: extend FieldMonetary (#867) --- web_decimal_numpad_dot/__manifest__.py | 4 ++-- web_decimal_numpad_dot/static/src/js/numpad_dot.js | 11 +++++++++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/web_decimal_numpad_dot/__manifest__.py b/web_decimal_numpad_dot/__manifest__.py index 857b38bb..4864b9fa 100644 --- a/web_decimal_numpad_dot/__manifest__.py +++ b/web_decimal_numpad_dot/__manifest__.py @@ -3,12 +3,12 @@ # Copyright 2015 Tecnativa - Pedro M. Baeza # Copyright 2015 Comunitea - Omar Castiñeira Saavedra # Copyright 2016 Oliver Dony -# Copyright 2017 Tecnativa - David Vidal +# Copyright 2017-18 Tecnativa - David Vidal # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). { "name": "Web - Numpad Dot as decimal separator", - "version": "11.0.1.0.0", + "version": "11.0.1.0.1", "license": "AGPL-3", "summary": "Allows using numpad dot to enter period decimal separator", "depends": [ diff --git a/web_decimal_numpad_dot/static/src/js/numpad_dot.js b/web_decimal_numpad_dot/static/src/js/numpad_dot.js index a9860de5..78b47a24 100644 --- a/web_decimal_numpad_dot/static/src/js/numpad_dot.js +++ b/web_decimal_numpad_dot/static/src/js/numpad_dot.js @@ -6,7 +6,7 @@ odoo.define("web_decimal_numpad_dot.FieldFloat", function (require) { var basic_fields = require("web.basic_fields"); var translation = require("web.translation"); - basic_fields.FieldFloat.include({ + var NumpadDotReplaceMixin = { init: function () { this.events = $.extend({}, this.events, { "keydown": "numpad_dot_replace", @@ -39,5 +39,12 @@ odoo.define("web_decimal_numpad_dot.FieldFloat", function (require) { to = from + point.length this.$input.prop("selectionStart", to).prop("selectionEnd", to); }, - }); + }; + + basic_fields.FieldFloat.include(NumpadDotReplaceMixin); + basic_fields.FieldMonetary.include(NumpadDotReplaceMixin); + + return { + NumpadDotReplaceMixin: NumpadDotReplaceMixin, + }; }); From 56f7bad1c3e517e148f3baeee89cb4b5e92b543d Mon Sep 17 00:00:00 2001 From: Jordi Ballester Alomar Date: Fri, 25 May 2018 13:36:26 +0200 Subject: [PATCH 09/12] fix tab navigation issue in web_decimal_numpad_dot --- web_decimal_numpad_dot/__manifest__.py | 2 +- web_decimal_numpad_dot/static/src/js/numpad_dot.js | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/web_decimal_numpad_dot/__manifest__.py b/web_decimal_numpad_dot/__manifest__.py index 4864b9fa..7d895998 100644 --- a/web_decimal_numpad_dot/__manifest__.py +++ b/web_decimal_numpad_dot/__manifest__.py @@ -8,7 +8,7 @@ { "name": "Web - Numpad Dot as decimal separator", - "version": "11.0.1.0.1", + "version": "11.0.1.0.2", "license": "AGPL-3", "summary": "Allows using numpad dot to enter period decimal separator", "depends": [ diff --git a/web_decimal_numpad_dot/static/src/js/numpad_dot.js b/web_decimal_numpad_dot/static/src/js/numpad_dot.js index 78b47a24..5d6c2ab4 100644 --- a/web_decimal_numpad_dot/static/src/js/numpad_dot.js +++ b/web_decimal_numpad_dot/static/src/js/numpad_dot.js @@ -9,7 +9,7 @@ odoo.define("web_decimal_numpad_dot.FieldFloat", function (require) { var NumpadDotReplaceMixin = { init: function () { this.events = $.extend({}, this.events, { - "keydown": "numpad_dot_replace", + "keyup": "numpad_dot_replace", }); return this._super.apply(this, arguments); }, @@ -20,7 +20,11 @@ odoo.define("web_decimal_numpad_dot.FieldFloat", function (require) { }, numpad_dot_replace: function (event) { + String.prototype.replaceAt=function(index, replacement) { + return this.substr(0, index) + replacement + this.substr(index + replacement.length); + } // Only act on numpad dot key + event.stopPropagation() if (event.keyCode != 110) { return; } @@ -29,12 +33,8 @@ odoo.define("web_decimal_numpad_dot.FieldFloat", function (require) { to = this.$input.prop("selectionEnd"), cur_val = this.$input.val(), point = this.l10n_decimal_point(); - // Replace selected text by proper character - this.$input.val( - cur_val.substring(0, from) + - point + - cur_val.substring(to) - ); + var new_val = cur_val.replaceAt(from-1, point) + this.$input.val(new_val); // Put user caret in place to = from + point.length this.$input.prop("selectionStart", to).prop("selectionEnd", to); From 9bab7feffd63b6d817b6eae6e62ff00863dd3ac1 Mon Sep 17 00:00:00 2001 From: oca-travis Date: Sun, 24 Jun 2018 10:28:10 +0000 Subject: [PATCH 10/12] [UPD] Update web_decimal_numpad_dot.pot --- .../i18n/web_decimal_numpad_dot.pot | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 web_decimal_numpad_dot/i18n/web_decimal_numpad_dot.pot diff --git a/web_decimal_numpad_dot/i18n/web_decimal_numpad_dot.pot b/web_decimal_numpad_dot/i18n/web_decimal_numpad_dot.pot new file mode 100644 index 00000000..447d3bb3 --- /dev/null +++ b/web_decimal_numpad_dot/i18n/web_decimal_numpad_dot.pot @@ -0,0 +1,14 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 11.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: <>\n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + From eb0530b5e6f4dfdeb0dde3f7df0cea54d9c4269a Mon Sep 17 00:00:00 2001 From: David Vidal Date: Wed, 10 Oct 2018 09:43:25 +0200 Subject: [PATCH 11/12] [FIX] web_decimal_numpad: float_time format (#1068) Recover functionality from https://github.com/OCA/web/pull/720, which was lost in migration to Odoo v11. --- web_decimal_numpad_dot/__manifest__.py | 2 +- .../static/src/js/numpad_dot.js | 18 ++++++++++-------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/web_decimal_numpad_dot/__manifest__.py b/web_decimal_numpad_dot/__manifest__.py index 7d895998..271a45c4 100644 --- a/web_decimal_numpad_dot/__manifest__.py +++ b/web_decimal_numpad_dot/__manifest__.py @@ -8,7 +8,7 @@ { "name": "Web - Numpad Dot as decimal separator", - "version": "11.0.1.0.2", + "version": "11.0.1.0.3", "license": "AGPL-3", "summary": "Allows using numpad dot to enter period decimal separator", "depends": [ diff --git a/web_decimal_numpad_dot/static/src/js/numpad_dot.js b/web_decimal_numpad_dot/static/src/js/numpad_dot.js index 5d6c2ab4..82775b71 100644 --- a/web_decimal_numpad_dot/static/src/js/numpad_dot.js +++ b/web_decimal_numpad_dot/static/src/js/numpad_dot.js @@ -15,17 +15,19 @@ odoo.define("web_decimal_numpad_dot.FieldFloat", function (require) { }, l10n_decimal_point: function () { - return this.widget == "float_time" + return this.formatType === "float_time" ? ":" : translation._t.database.parameters.decimal_point; }, + _replaceAt: function (cur_val, index, replacement) { + return cur_val.substr(0, index) + replacement + + cur_val.substr(index + replacement.length); + }, + numpad_dot_replace: function (event) { - String.prototype.replaceAt=function(index, replacement) { - return this.substr(0, index) + replacement + this.substr(index + replacement.length); - } // Only act on numpad dot key - event.stopPropagation() - if (event.keyCode != 110) { + event.stopPropagation(); + if (event.keyCode !== 110) { return; } event.preventDefault(); @@ -33,10 +35,10 @@ odoo.define("web_decimal_numpad_dot.FieldFloat", function (require) { to = this.$input.prop("selectionEnd"), cur_val = this.$input.val(), point = this.l10n_decimal_point(); - var new_val = cur_val.replaceAt(from-1, point) + var new_val = this._replaceAt(cur_val, from-1, point); this.$input.val(new_val); // Put user caret in place - to = from + point.length + to = from + point.length; this.$input.prop("selectionStart", to).prop("selectionEnd", to); }, }; From 66a2c9631fa93eb76a14438ef8d3a6e18bc9e740 Mon Sep 17 00:00:00 2001 From: ernesto Date: Mon, 5 Nov 2018 10:40:07 -0500 Subject: [PATCH 12/12] [MIG] web_decimal_numpad_dot: Migration to 12.0 --- web_decimal_numpad_dot/README.rst | 80 +++- web_decimal_numpad_dot/__manifest__.py | 3 +- .../readme/CONTRIBUTORS.rst | 11 + web_decimal_numpad_dot/readme/DESCRIPTION.rst | 2 + web_decimal_numpad_dot/readme/USAGE.rst | 2 + .../static/description/index.html | 439 ++++++++++++++++++ 6 files changed, 512 insertions(+), 25 deletions(-) create mode 100644 web_decimal_numpad_dot/readme/CONTRIBUTORS.rst create mode 100644 web_decimal_numpad_dot/readme/DESCRIPTION.rst create mode 100644 web_decimal_numpad_dot/readme/USAGE.rst create mode 100644 web_decimal_numpad_dot/static/description/index.html diff --git a/web_decimal_numpad_dot/README.rst b/web_decimal_numpad_dot/README.rst index 787c7388..82e353e2 100644 --- a/web_decimal_numpad_dot/README.rst +++ b/web_decimal_numpad_dot/README.rst @@ -1,58 +1,92 @@ -.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg - :target: http://www.gnu.org/licenses/agpl - :alt: License: AGPL-3 +===================================== +Web - Numpad Dot as decimal separator +===================================== -=============================== -Numpad Dot as decimal separator -=============================== +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fweb-lightgray.png?logo=github + :target: https://github.com/OCA/web/tree/12.0/web_decimal_numpad_dot + :alt: OCA/web +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/web-12-0/web-12-0-web_decimal_numpad_dot + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png + :target: https://runbot.odoo-community.org/runbot/162/12.0 + :alt: Try me on Runbot + +|badge1| |badge2| |badge3| |badge4| |badge5| Allows using numpad dot to enter period decimal separator even in localizations where comma is used instead of period. +**Table of contents** + +.. contents:: + :local: + Usage ===== Whenever on a float or monetary input field pressing numpad dot produces the proper decimal separator for the active localization. -.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas - :alt: Try me on Runbot - :target: https://runbot.odoo-community.org/runbot/web/162/11.0 - Bug Tracker =========== -Bugs are tracked on `GitHub Issues -`_. In case of trouble, please -check there if your issue has already been reported. If you spotted it first, -help us smash it by providing detailed and welcomed feedback. +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. Credits ======= +Authors +~~~~~~~ + +* AvanzOSC +* Comunitea +* Tecnativa + Contributors ------------- +~~~~~~~~~~~~ * Oihane Crucelaegui -* Pedro M. Baeza * Ana Juaristi * Omar Castiñeira Saavedra * Oliver Dony <@odony> * Wim Audenaert -* David Vidal -* Jairo Llopis +* `Tecnativa `_: + + * Pedro M. Baeza + * David Vidal + * Jairo Llopis + * Ernesto Tejeda -Maintainer ----------- +Maintainers +~~~~~~~~~~~ + +This module is maintained by the OCA. .. image:: https://odoo-community.org/logo.png :alt: Odoo Community Association :target: https://odoo-community.org -This module is maintained by the OCA. - OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use. -To contribute to this module, please visit https://odoo-community.org. +This module is part of the `OCA/web `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/web_decimal_numpad_dot/__manifest__.py b/web_decimal_numpad_dot/__manifest__.py index 271a45c4..1951a3e2 100644 --- a/web_decimal_numpad_dot/__manifest__.py +++ b/web_decimal_numpad_dot/__manifest__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright 2015 AvanzOSC - Oihane Crucelaegui # Copyright 2015 Tecnativa - Pedro M. Baeza # Copyright 2015 Comunitea - Omar Castiñeira Saavedra @@ -8,7 +7,7 @@ { "name": "Web - Numpad Dot as decimal separator", - "version": "11.0.1.0.3", + "version": "12.0.1.0.0", "license": "AGPL-3", "summary": "Allows using numpad dot to enter period decimal separator", "depends": [ diff --git a/web_decimal_numpad_dot/readme/CONTRIBUTORS.rst b/web_decimal_numpad_dot/readme/CONTRIBUTORS.rst new file mode 100644 index 00000000..c8d09bb7 --- /dev/null +++ b/web_decimal_numpad_dot/readme/CONTRIBUTORS.rst @@ -0,0 +1,11 @@ +* Oihane Crucelaegui +* Ana Juaristi +* Omar Castiñeira Saavedra +* Oliver Dony <@odony> +* Wim Audenaert +* `Tecnativa `_: + + * Pedro M. Baeza + * David Vidal + * Jairo Llopis + * Ernesto Tejeda diff --git a/web_decimal_numpad_dot/readme/DESCRIPTION.rst b/web_decimal_numpad_dot/readme/DESCRIPTION.rst new file mode 100644 index 00000000..9737bec1 --- /dev/null +++ b/web_decimal_numpad_dot/readme/DESCRIPTION.rst @@ -0,0 +1,2 @@ +Allows using numpad dot to enter period decimal separator even in localizations +where comma is used instead of period. diff --git a/web_decimal_numpad_dot/readme/USAGE.rst b/web_decimal_numpad_dot/readme/USAGE.rst new file mode 100644 index 00000000..b7eab380 --- /dev/null +++ b/web_decimal_numpad_dot/readme/USAGE.rst @@ -0,0 +1,2 @@ +Whenever on a float or monetary input field pressing numpad dot produces the +proper decimal separator for the active localization. diff --git a/web_decimal_numpad_dot/static/description/index.html b/web_decimal_numpad_dot/static/description/index.html new file mode 100644 index 00000000..7ebdcf75 --- /dev/null +++ b/web_decimal_numpad_dot/static/description/index.html @@ -0,0 +1,439 @@ + + + + + + +Web - Numpad Dot as decimal separator + + + +
+

Web - Numpad Dot as decimal separator

+ + +

Beta License: AGPL-3 OCA/web Translate me on Weblate Try me on Runbot

+

Allows using numpad dot to enter period decimal separator even in localizations +where comma is used instead of period.

+

Table of contents

+ +
+

Usage

+

Whenever on a float or monetary input field pressing numpad dot produces the +proper decimal separator for the active localization.

+
+
+

Bug Tracker

+

Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • AvanzOSC
  • +
  • Comunitea
  • +
  • Tecnativa
  • +
+
+
+

Contributors

+ +
+
+

Maintainers

+

This module is maintained by the OCA.

+Odoo Community Association +

OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use.

+

This module is part of the OCA/web project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+ +