Hoang Gia Minh
5 years ago
committed by
GitHub
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 351 additions and 0 deletions
-
82web_widget_float_formula/README.rst
-
0web_widget_float_formula/__init__.py
-
22web_widget_float_formula/__manifest__.py
-
BINweb_widget_float_formula/static/description/icon.png
-
103web_widget_float_formula/static/src/js/web_widget_float_formula.js
-
102web_widget_float_formula/static/tests/js/test_web_widget_float_formula.js
-
5web_widget_float_formula/tests/__init__.py
-
16web_widget_float_formula/tests/test_js.py
-
21web_widget_float_formula/views/web_widget_float_formula.xml
@ -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 <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 |
|||
======= |
|||
|
|||
Contributors |
|||
------------ |
|||
|
|||
* Sylvain Le Gal (https://twitter.com/legalsylvain) |
|||
* Oleg Bulkin <o.bulkin@gmail.com> |
|||
|
|||
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. |
@ -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, |
|||
} |
After Width: 128 | Height: 128 | Size: 3.0 KiB |
@ -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); |
|||
} |
|||
}, |
|||
}); |
|||
}); |
@ -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: '<form><field name="bar"/></form>', |
|||
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(); |
|||
}); |
|||
|
|||
}); |
@ -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 |
@ -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" |
|||
) |
@ -0,0 +1,21 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
|
|||
<!-- |
|||
Copyright GRAP |
|||
Copyright 2016 LasLabs Inc. |
|||
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
|||
--> |
|||
|
|||
<odoo> |
|||
<template id="assets_backend" name="web_widget_float_formula Assets" inherit_id="web.assets_backend"> |
|||
<xpath expr="." position="inside"> |
|||
<script type="text/javascript" src="/web_widget_float_formula/static/src/js/web_widget_float_formula.js"/> |
|||
</xpath> |
|||
</template> |
|||
|
|||
<template id="qunit_suite" name="web_widget_float_formula Test Assets" inherit_id="web.qunit_suite"> |
|||
<xpath expr="//t[@t-set='head']" position="inside"> |
|||
<script type="text/javascript" src="/web_widget_float_formula/static/tests/js/test_web_widget_float_formula.js"/> |
|||
</xpath> |
|||
</template> |
|||
</odoo> |
Write
Preview
Loading…
Cancel
Save
Reference in new issue