diff --git a/web_widget_float_formula/README.rst b/web_widget_float_formula/README.rst new file mode 100644 index 00000000..4804167e --- /dev/null +++ b/web_widget_float_formula/README.rst @@ -0,0 +1,82 @@ +.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 + +======================== +Formulas in Float Fields +======================== + +This module allows the use of simple math formulas in integer/float fields +(e.g. "=45 + 4/3 - 5 * (2 + 1)"). + +* Only supports parentheses, decimal points, thousands separators, and the + operators "+", "-", "*", and "/" +* Will use the decimal point and thousands separator characters associated + with your language +* If the formula is valid, the result will be computed and displayed, and the + formula will be stored for editing +* If the formula is not valid, it's retained in the field as text + +**Technical Details** + +* Overloads web.form_widgets.FieldFloat (so it works for fields.integer & + fields.float) +* Uses the eval() JS function to evaluate the formula +* Does not do any rounding (this is handled elsewhere) +* Avoids code injection by applying strict regex to formula prior to eval() + (e.g. "=alert('security')" would not get evaluated) + +Installation +============ + +To install this module, simply follow the standard install process. + +Configuration +============= + +No configuration is needed or possible. + +Usage +===== + +Install and enjoy. A short demo video can be found at +http://www.youtube.com/watch?v=jQGdD34WYrA. + +.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas + :alt: Try me on Runbot + :target: https://runbot.odoo-community.org/runbot/162/10.0 + +Known Issues / Roadmap +====================== + +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 +======= + +Contributors +------------ + +* Sylvain Le Gal (https://twitter.com/legalsylvain) +* Oleg Bulkin + +Maintainer +---------- + +.. image:: http://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: http://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 http://odoo-community.org. diff --git a/web_widget_float_formula/__init__.py b/web_widget_float_formula/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/web_widget_float_formula/__manifest__.py b/web_widget_float_formula/__manifest__.py new file mode 100644 index 00000000..d759aab8 --- /dev/null +++ b/web_widget_float_formula/__manifest__.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +# Copyright GRAP +# Copyright 2016 LasLabs Inc. +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +{ + 'name': 'Web Widget - Formulas in Float Fields', + 'summary': 'Allow use of simple formulas in float fields', + 'version': '11.0.1.0.0', + 'category': 'Web', + 'author': 'GRAP, LasLabs, Odoo Community Association (OCA)', + 'website': 'http://www.grap.coop', + 'license': 'AGPL-3', + 'depends': [ + 'web', + ], + 'data': [ + 'views/web_widget_float_formula.xml', + ], + 'installable': True, + 'application': False, +} diff --git a/web_widget_float_formula/static/description/icon.png b/web_widget_float_formula/static/description/icon.png new file mode 100644 index 00000000..7f744399 Binary files /dev/null and b/web_widget_float_formula/static/description/icon.png differ diff --git a/web_widget_float_formula/static/src/js/web_widget_float_formula.js b/web_widget_float_formula/static/src/js/web_widget_float_formula.js new file mode 100644 index 00000000..7fee7c6e --- /dev/null +++ b/web_widget_float_formula/static/src/js/web_widget_float_formula.js @@ -0,0 +1,103 @@ +/** +* Copyright GRAP +* Copyright 2016 LasLabs Inc. +* License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +**/ + +odoo.define('web_widget_float_formula', function(require) { + "use strict"; + + var core = require('web.core'); + var basic_fields = require('web.basic_fields'); + var FieldFloat = basic_fields.FieldFloat; + FieldFloat.include({ + start: function() { + this._super(); + this.on('blurred', this, this._compute_result); + this.on('focused', this, this._display_formula); + + this.setUpFocus(); + }, + + setUpFocus: function() { + var self = this; + this.$el.on({ + focus: function() { self.trigger('focused'); }, + blur: function() { self.trigger('blurred'); } + }); + }, + + commitChanges: function() { + this._compute_result(); + }, + + _formula_text: '', + + _clean_formula_text: function() { + this._formula_text = ''; + }, + + _process_formula: function(formula) { + try{ + formula = formula.toString(); + } catch (ex) { + return false; + } + var clean_formula = formula.toString().replace(/^\s+|\s+$/g, ''); + if (clean_formula[0] == '=') { + clean_formula = clean_formula.substring(1); + var myreg = new RegExp('[0-9]|\\s|\\.|,|\\(|\\)|\\+|\\-|\\*|\\/', 'g'); + if (clean_formula.replace(myreg, '') === '') { + return clean_formula; + } + } + return false; + }, + + _eval_formula: function(formula) { + // Import localization values used to eval formula + var translation_params = core._t.database.parameters; + var decimal_point = translation_params.decimal_point; + var thousands_sep = translation_params.thousands_sep; + + var value; + formula = formula.replace(thousands_sep, '').replace(decimal_point, '.'); + try { + value = eval(formula); + } + catch(e) {} + + if (typeof value != 'undefined') { + return value; + } + return false; + }, + + _compute_result: function() { + this._clean_formula_text(); + + var input = this.$input.val(); + + var formula = this._process_formula(input); + if (formula !== false) { + var value = this._eval_formula(formula); + if (value !== false) { + this._formula_text = "=" + formula; + + var decimal_point = core._t.database.parameters.decimal_point; + + // _setValue + this._setValue(value.toString().replace(decimal_point, '.')) + this._render(); + } + } + }, + + // Display the formula stored in the field to allow modification + _display_formula: function() { + if (this._formula_text !== '') { + this.$input.val(this._formula_text); + } + }, + }); +}); diff --git a/web_widget_float_formula/static/tests/js/test_web_widget_float_formula.js b/web_widget_float_formula/static/tests/js/test_web_widget_float_formula.js new file mode 100644 index 00000000..09f69b95 --- /dev/null +++ b/web_widget_float_formula/static/tests/js/test_web_widget_float_formula.js @@ -0,0 +1,102 @@ +/** +* Copyright 2016 LasLabs Inc. +* License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +**/ + +odoo.define('web_widget_float_formula', function(require) { + "use strict"; + + var core = require('web.core'); + var testUtils = require('web.test_utils'); + var FormView = require('web.FormView'); + + var createView = testUtils.createView; + var triggerKeypressEvent = testUtils.triggerKeypressEvent; + + var assertClose = function(assert, actual, expected, message) { + var pass = Math.abs(actual - expected) < 0.00001; + + assert.pushResult({ + result: pass, + actual: actual, + expected: expected, + message: message + }); + } + + QUnit.module('web_widget_float_formula', { + beforeEach: function() { + this.data = { + foo: { + fields: { + bar: { string: "Bar", type: "float" } + }, + records: [ + { id: 1, bar: 1.2 } + ] + } + }; + } + }); + + QUnit.test('Float fields in form view', function(assert) { + assert.expect(8); + + var form = createView({ + View: FormView, + model: 'foo', + data: this.data, + res_id: 1, + arch: '
', + viewOptions: { + mode: 'edit', + }, + mockRPC: function (route, args) { + if (args.method === 'write') { + assert.step('save'); + } + return this._super.apply(this, arguments); + }, + }); + + assertClose(assert, form.$('input').val(), 1.2); + + form.$('input').val('=(1 + 2) / 3').blur(); + assertClose(assert, form.$('input').val(), 1, + 'blur event should trigger compute event'); + + form.$('input').focus(); + assert.strictEqual(form.$('input').val(), '=(1 + 2) / 3', + 'focus event should display the forumla'); + + form.$('input').val('=(1 * 2x) /').blur(); + assert.strictEqual(form.$('input').val(), '=(1 * 2x) /', + 'invalid formula should not be calculated'); + + _.extend(core._t.database.parameters, { + grouping: [3, 0], + decimal_point: ',', + thousands_sep: '.' + }); + + form.$('input').val('=2.000*3,5').blur(); + assert.strictEqual(form.$('input').val(), "7.000,00", + 'eval should handle decimal point and thousands separator properly'); + + _.extend(core._t.database.parameters, { + grouping: [3, 0], + decimal_point: '.', + thousands_sep: ',' + }); + + form.$('input').val('=3-2'); + + form.$buttons.find('.o_form_button_save').click(); + assert.verifySteps(['save'], 'should have saved'); + assertClose(assert, form.$('.o_field_widget').text(), 1, + 'save should also trigger compute result') + + form.destroy(); + }); + +}); diff --git a/web_widget_float_formula/tests/__init__.py b/web_widget_float_formula/tests/__init__.py new file mode 100644 index 00000000..9cf9e2d2 --- /dev/null +++ b/web_widget_float_formula/tests/__init__.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- +# Copyright 2016 LasLabs Inc. +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from . import test_js diff --git a/web_widget_float_formula/tests/test_js.py b/web_widget_float_formula/tests/test_js.py new file mode 100644 index 00000000..53d85490 --- /dev/null +++ b/web_widget_float_formula/tests/test_js.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- +# Copyright 2016 LasLabs Inc. +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). + +import odoo.tests + + +class FloatFormulaSuite(odoo.tests.HttpCase): + + def test_js(self): + self.phantom_js( + "/web/tests?module=web_widget_float_formula", + "", + "", + login="admin" + ) diff --git a/web_widget_float_formula/views/web_widget_float_formula.xml b/web_widget_float_formula/views/web_widget_float_formula.xml new file mode 100644 index 00000000..64dcfed7 --- /dev/null +++ b/web_widget_float_formula/views/web_widget_float_formula.xml @@ -0,0 +1,21 @@ + + + + + +