diff --git a/web_decimal_numpad_dot/README.rst b/web_decimal_numpad_dot/README.rst
index f0a463a2d..71f5e51c5 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
+<https://github.com/OCA/web/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 <oihanecrucelaegi@avanzosc.es>
-* Pedro M. Baeza <pedro.baeza@serviciosbaeza.com>
+* Pedro M. Baeza <pedro.baeza@tecnativa.com>
 * Ana Juaristi <anajuaristi@avanzosc.es>
+* Omar CastiƱeira Saavedra <omar@comunitea.com>
+* Oliver Dony <@odony>
+* Wim Audenaert <Wim.Audenaert@ucamco.com>
+* David Vidal <david.vidal@tecnativa.com>
+
+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 ee9e0cf60..dae354a67 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 000000000..457c130b4
--- /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 9e6051ded..000000000
--- 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 <oihanecrucelaegi@avanzosc.es>",
-        "Pedro M. Baeza <pedro.baeza@serviciosbaeza.com>",
-        "Ana Juaristi <anajuaristi@avanzosc.es>",
-    ],
-    "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 000000000..9c205cbc4
Binary files /dev/null and b/web_decimal_numpad_dot/static/description/icon.png differ
diff --git a/web_decimal_numpad_dot/static/src/js/formats.js b/web_decimal_numpad_dot/static/src/js/formats.js
deleted file mode 100644
index 22131d4d5..000000000
--- a/web_decimal_numpad_dot/static/src/js/formats.js
+++ /dev/null
@@ -1,376 +0,0 @@
-
-(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<Number>} 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<indices.length; ++i) {
-        var section = indices[i];
-        if (section === -1 || last <= 0) {
-            // Done with string, or -1 (stops formatting string)
-            break;
-        } else if(section === 0 && i === 0) {
-            // repeats previous section, which there is none => 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 560ce0482..8d2278039 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 3345c5dcc..d4815753b 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 @@
 <?xml version="1.0" encoding="utf-8"?>
-<openerp>
-    <data>
-        <template id="assets_backend" name="numpad_dot assets" inherit_id="web.assets_backend">
-            <xpath expr="." position="inside">
-                <script type="text/javascript" src="/web_decimal_numpad_dot/static/src/js/numpad_dot.js"></script>
-            </xpath>
-        </template>
-    </data>
-</openerp>
+<odoo>
+
+    <template id="assets_backend"
+              name="numpad_dot assets" inherit_id="web.assets_backend">
+        <xpath expr="." position="inside">
+            <script type="text/javascript" src="/web_decimal_numpad_dot/static/src/js/numpad_dot.js"></script>
+        </xpath>
+    </template>
+
+</odoo>