Browse Source
[MIG] web_widget_float_formula: v9 with i18n
[MIG] web_widget_float_formula: v9 with i18n
* Update JS to use v9 module system * Fix non-functioning logic for obtaining the value of an input element * Eliminate redundant calls (e.g. there were multiple calls to eval) * Modify formula cleanup to use localized decimal point and thousands separator characters * Add JS unit testspull/425/head
Oleg Bulkin
8 years ago
10 changed files with 369 additions and 179 deletions
-
73web_widget_float_formula/README.rst
-
4web_widget_float_formula/__init__.py
-
23web_widget_float_formula/__openerp__.py
-
132web_widget_float_formula/static/src/js/models.js
-
103web_widget_float_formula/static/src/js/web_widget_float_formula.js
-
161web_widget_float_formula/static/tests/js/test_web_widget_float_formula.js
-
5web_widget_float_formula/tests/__init__.py
-
15web_widget_float_formula/tests/test_js.py
-
9web_widget_float_formula/views/qweb.xml
-
21web_widget_float_formula/views/web_widget_float_formula.xml
@ -1,4 +0,0 @@ |
|||
# -*- encoding: utf-8 -*- |
|||
############################################################################### |
|||
# See __openerp__.py file for Copyright and Licence Informations. |
|||
############################################################################### |
@ -1,19 +1,22 @@ |
|||
# -*- encoding: utf-8 -*- |
|||
############################################################################### |
|||
# See Copyright and Licence Informations undermentioned. |
|||
############################################################################### |
|||
# -*- 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', |
|||
'version': '8.0.1.0.0', |
|||
'category': 'web', |
|||
'author': 'GRAP,Odoo Community Association (OCA)', |
|||
'name': 'Web Widget - Formulas in Float Fields', |
|||
'summary': 'Allow use of simple formulas in float fields', |
|||
'version': '9.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/qweb.xml', |
|||
'views/web_widget_float_formula.xml', |
|||
], |
|||
'installable': False, |
|||
'installable': True, |
|||
'application': False, |
|||
} |
@ -1,132 +0,0 @@ |
|||
/******************************************************************************* |
|||
See __openerp__.py file for Copyright and Licence Informations. |
|||
*******************************************************************************/ |
|||
|
|||
openerp.web_widget_float_formula = function (instance) { |
|||
|
|||
instance.web.FormView = instance.web.FormView.extend({ |
|||
/*********************************************************************** |
|||
Overload section |
|||
***********************************************************************/ |
|||
|
|||
/** |
|||
* Overload : '_process_save' function |
|||
1: to force computation of formula if the user realize a keydown directly after the formula input in a tree view ; |
|||
2: to clean up the '_formula_text' value in all case to avoid bugs in tree view ; |
|||
*/ |
|||
_process_save: function(save_obj) { |
|||
for (var f in this.fields) { |
|||
if (!this.fields.hasOwnProperty(f)) { continue; } |
|||
f = this.fields[f]; |
|||
if (f.hasOwnProperty('_formula_text')){ |
|||
currentval = f.$('input').attr('value') |
|||
if (typeof currentval != 'undefined'){ |
|||
formula = f._get_valid_expression(currentval); |
|||
if (formula){ |
|||
f._compute_result(); |
|||
} |
|||
} |
|||
f._clean_formula_text(); |
|||
} |
|||
} |
|||
return this._super(save_obj); |
|||
}, |
|||
|
|||
}); |
|||
|
|||
instance.web.form.FieldFloat = instance.web.form.FieldFloat.extend({ |
|||
/*********************************************************************** |
|||
Overload section |
|||
***********************************************************************/ |
|||
|
|||
/** |
|||
* Overload : 'start' function to catch 'blur' and 'focus' events. |
|||
*/ |
|||
start: function() { |
|||
this.on("blurred", this, this._compute_result); |
|||
this.on("focused", this, this._display_formula); |
|||
return this._super(); |
|||
}, |
|||
|
|||
/** |
|||
* Overload : 'initialize_content' function to clean '_formula_text' value. |
|||
*/ |
|||
initialize_content: function() { |
|||
this._clean_formula_text(); |
|||
return this._super(); |
|||
}, |
|||
|
|||
/*********************************************************************** |
|||
Custom section |
|||
***********************************************************************/ |
|||
|
|||
/** |
|||
* keep in memory the formula to allow user to edit it again. |
|||
The formula has to be keeped in memory until a 'save' action. |
|||
*/ |
|||
_formula_text: '', |
|||
|
|||
/** |
|||
* Clean '_formula_text' value. |
|||
*/ |
|||
_clean_formula_text: function() { |
|||
this._formula_text = ''; |
|||
}, |
|||
|
|||
/** |
|||
* Return a valid formula from a val, if possible. |
|||
Otherwise, return false. |
|||
*/ |
|||
_get_valid_expression: function(val) { |
|||
// Trim the value
|
|||
currenttxt = val.toString().replace(/^\s+|\s+$/g, ''); |
|||
// Test if the value is a formula
|
|||
if (currenttxt[0] == '=') { |
|||
// allowed chars : [0-9] .,+-/*() and spaces
|
|||
myreg = RegExp('[0-9]|\\s|\\.|,|\\(|\\)|\\+|\\-|\\*|\\/','g') |
|||
// Test to avoid code injonction in eval function.
|
|||
if (currenttxt.substring(1).replace(myreg, '') == ''){ |
|||
try { |
|||
// Try to compute
|
|||
formula = currenttxt.substring(1).replace(/,/g,'.'); |
|||
var floatval = eval(formula); |
|||
}catch (e) {} |
|||
if (typeof (floatval) != 'undefined'){ |
|||
return formula; |
|||
} |
|||
} |
|||
} |
|||
return false; |
|||
}, |
|||
|
|||
/** |
|||
* test if the content of the field is a valid formula, |
|||
* compute the result, and replace the current value by the final result. |
|||
*/ |
|||
_compute_result: function() { |
|||
var formula |
|||
// Erase old formula
|
|||
this._formula_text = ''; |
|||
|
|||
formula = this._get_valid_expression(this.$el.find('input').attr('value')); |
|||
if (formula){ |
|||
// Store new formula
|
|||
this._formula_text = "=" + formula; |
|||
// put the result in the field
|
|||
this.set_value(eval(formula)); |
|||
// Force rendering anyway to avoid format loss if no change
|
|||
this.render_value(); |
|||
} |
|||
}, |
|||
|
|||
/** |
|||
* Display the stored formula in the field, to allow modification. |
|||
*/ |
|||
_display_formula: function() { |
|||
if (this._formula_text != ''){ |
|||
this.$el.find('input').val(this._formula_text); |
|||
} |
|||
}, |
|||
|
|||
}); |
|||
}; |
@ -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 form_view = require('web.FormView'); |
|||
form_view.include({ |
|||
// Ensure that formula is computed even if user saves right away and
|
|||
// clean up '_formula_text' value to avoid bugs in tree view
|
|||
_process_save: function(save_obj) { |
|||
for (var f in this.fields) { |
|||
if (!this.fields.hasOwnProperty(f)) { continue; } |
|||
f = this.fields[f]; |
|||
if (f.hasOwnProperty('_formula_text')) { |
|||
f._compute_result(); |
|||
f._clean_formula_text(); |
|||
} |
|||
} |
|||
|
|||
return this._super(save_obj); |
|||
}, |
|||
}); |
|||
|
|||
var core = require('web.core'); |
|||
core.bus.on('web_client_ready', null, function () { |
|||
// 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 field_float = require('web.form_widgets').FieldFloat; |
|||
field_float.include({ |
|||
start: function() { |
|||
this.on('blurred', this, this._compute_result); |
|||
this.on('focused', this, this._display_formula); |
|||
return this._super(); |
|||
}, |
|||
|
|||
initialize_content: function() { |
|||
this._clean_formula_text(); |
|||
return this._super(); |
|||
}, |
|||
|
|||
_formula_text: '', |
|||
|
|||
_clean_formula_text: function() { |
|||
this._formula_text = ''; |
|||
}, |
|||
|
|||
_process_formula: function(formula) { |
|||
var clean_formula = formula.toString().replace(/^\s+|\s+$/g, ''); |
|||
if (clean_formula[0] == '=') { |
|||
clean_formula = clean_formula.substring(1); |
|||
var myreg = RegExp('[0-9]|\\s|\\.|,|\\(|\\)|\\+|\\-|\\*|\\/','g'); |
|||
if (clean_formula.replace(myreg, '') === '') { |
|||
return clean_formula; |
|||
} |
|||
} |
|||
return false; |
|||
}, |
|||
|
|||
_eval_formula: function(formula) { |
|||
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 formula = this._process_formula(this.$el.find('input').val()); |
|||
if (formula !== false) { |
|||
var value = this._eval_formula(formula); |
|||
if (value !== false) { |
|||
this._formula_text = "=" + formula; |
|||
this.set_value(value); |
|||
// Force rendering to avoid format loss if there's no change
|
|||
this.render_value(); |
|||
} |
|||
} |
|||
}, |
|||
|
|||
// Display the formula stored in the field to allow modification
|
|||
_display_formula: function() { |
|||
if (this._formula_text !== '') { |
|||
this.$el.find('input').val(this._formula_text); |
|||
} |
|||
}, |
|||
}); |
|||
}); |
|||
}); |
@ -0,0 +1,161 @@ |
|||
/** |
|||
* Copyright 2016 LasLabs Inc. |
|||
* License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
|||
**/ |
|||
|
|||
odoo.define_section('web_widget_float_formula', ['web.form_common', 'web.form_widgets', 'web.core'], function(test) { |
|||
'use strict'; |
|||
|
|||
window.test_setup = function(self, form_common, form_widgets, core) { |
|||
core.bus.trigger('web_client_ready'); |
|||
var field_manager = new form_common.DefaultFieldManager(null, {}); |
|||
var filler = {'attrs': {}}; // Needed to instantiate FieldFloat
|
|||
self.field = new form_widgets.FieldFloat(field_manager, filler); |
|||
self.$element = $('<input>'); |
|||
self.field.$el.append(self.$element); |
|||
}; |
|||
|
|||
test('Float fields should have a _formula_text property that defaults to an empty string', |
|||
function(assert, form_common, form_widgets, core) { |
|||
window.test_setup(this, form_common, form_widgets, core); |
|||
|
|||
assert.strictEqual(this.field._formula_text, ''); |
|||
}); |
|||
|
|||
test('.initialize_content() on float fields should clear the _formula_text property', |
|||
function(assert, form_common, form_widgets, core) { |
|||
window.test_setup(this, form_common, form_widgets, core); |
|||
this.field._formula_text = 'test'; |
|||
this.field.initialize_content(); |
|||
|
|||
assert.strictEqual(this.field._formula_text, ''); |
|||
}); |
|||
|
|||
test('._clean_formula_text() on float fields should clear the _formula_text property', |
|||
function(assert, form_common, form_widgets, core) { |
|||
window.test_setup(this, form_common, form_widgets, core); |
|||
this.field._formula_text = 'test'; |
|||
this.field._clean_formula_text(); |
|||
|
|||
assert.strictEqual(this.field._formula_text, ''); |
|||
}); |
|||
|
|||
test('._process_formula() on float fields should return false when given invalid formulas', |
|||
function(assert, form_common, form_widgets, core) { |
|||
window.test_setup(this, form_common, form_widgets, core); |
|||
|
|||
assert.strictEqual(this.field._process_formula('2*3'), false); |
|||
assert.strictEqual(this.field._process_formula('=2*3a'), false); |
|||
}); |
|||
|
|||
test('._process_formula() on float fields should properly process a valid formula', |
|||
function(assert, form_common, form_widgets, core) { |
|||
window.test_setup(this, form_common, form_widgets, core); |
|||
|
|||
assert.strictEqual(this.field._process_formula(' =2*3\n'), '2*3'); |
|||
}); |
|||
|
|||
test('._eval_formula() on float fields should properly evaluate a valid formula', |
|||
function(assert, form_common, form_widgets, core) { |
|||
window.test_setup(this, form_common, form_widgets, core); |
|||
|
|||
assert.equal(this.field._eval_formula('2*3'), 6); |
|||
}); |
|||
|
|||
test('._eval_formula() on float fields should properly handle alternative decimal points and thousands seps', |
|||
function(assert, form_common, form_widgets, core) { |
|||
var translation_params = core._t.database.parameters; |
|||
translation_params.decimal_point = ','; |
|||
translation_params.thousands_sep = '.'; |
|||
window.test_setup(this, form_common, form_widgets, core); |
|||
|
|||
assert.equal(this.field._eval_formula('2.000*3,5'), 7000); |
|||
}); |
|||
|
|||
test('._eval_formula() on float fields should return false when given an input that evals to undefined', |
|||
function(assert, form_common, form_widgets, core) { |
|||
window.test_setup(this, form_common, form_widgets, core); |
|||
|
|||
assert.equal(this.field._eval_formula(''), false); |
|||
}); |
|||
|
|||
test('._eval_formula() on float fields should return false when given an input that cannot be evaluated', |
|||
function(assert, form_common, form_widgets, core) { |
|||
window.test_setup(this, form_common, form_widgets, core); |
|||
|
|||
assert.equal(this.field._eval_formula('*/'), false); |
|||
}); |
|||
|
|||
test('._compute_result() on float fields should always clean up _formula_text', |
|||
function(assert, form_common, form_widgets, core) { |
|||
window.test_setup(this, form_common, form_widgets, core); |
|||
this.field._formula_text = 'test'; |
|||
this.field._compute_result(); |
|||
|
|||
assert.strictEqual(this.field._formula_text, ''); |
|||
}); |
|||
|
|||
test('._compute_result() should not change the value of the associated input when it is not a valid formula', |
|||
function(assert, form_common, form_widgets, core) { |
|||
window.test_setup(this, form_common, form_widgets, core); |
|||
this.$element.val('=2*3a'); |
|||
this.field._compute_result(); |
|||
|
|||
assert.strictEqual(this.$element.val(), '=2*3a'); |
|||
}); |
|||
|
|||
test('._compute_result() should not change the value of the associated input when it cannot be evaled', |
|||
function(assert, form_common, form_widgets, core) { |
|||
window.test_setup(this, form_common, form_widgets, core); |
|||
this.$element.val('=*/'); |
|||
this.field._compute_result(); |
|||
|
|||
assert.strictEqual(this.$element.val(), '=*/'); |
|||
}); |
|||
|
|||
test('._compute_result() should behave properly when the current value of the input element is a valid formula', |
|||
function(assert, form_common, form_widgets, core) { |
|||
window.test_setup(this, form_common, form_widgets, core); |
|||
this.$element.val('=2*3'); |
|||
this.field._compute_result(); |
|||
|
|||
assert.equal(this.$element.val(), '6'); |
|||
assert.strictEqual(this.field._formula_text, '=2*3'); |
|||
}); |
|||
|
|||
test('._display_formula() should update the value of the input element when there is a stored formula', |
|||
function(assert, form_common, form_widgets, core) { |
|||
window.test_setup(this, form_common, form_widgets, core); |
|||
this.field._formula_text = "test"; |
|||
this.field._display_formula(); |
|||
|
|||
assert.equal(this.$element.val(), 'test'); |
|||
}); |
|||
|
|||
test('.start() on float fields should add a handler that calls ._compute_result() when the field is blurred', |
|||
function(assert, form_common, form_widgets, core) { |
|||
window.test_setup(this, form_common, form_widgets, core); |
|||
this.field.called = false; |
|||
this.field._compute_result = function() { |
|||
this.called = true; |
|||
}; |
|||
this.field.start(); |
|||
this.field.trigger('blurred'); |
|||
|
|||
assert.strictEqual(this.field.called, true); |
|||
}); |
|||
|
|||
test('.start() on float fields should add a handler that calls ._display_formula() when the field is focused', |
|||
function(assert, form_common, form_widgets, core) { |
|||
window.test_setup(this, form_common, form_widgets, core); |
|||
this.field.called = false; |
|||
this.field._display_formula = function() { |
|||
this.called = true; |
|||
}; |
|||
this.field.start(); |
|||
this.field.trigger('focused'); |
|||
|
|||
assert.strictEqual(this.field.called, true); |
|||
}); |
|||
|
|||
}); |
@ -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,15 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Copyright 2016 LasLabs Inc. |
|||
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). |
|||
|
|||
from openerp.tests import HttpCase |
|||
|
|||
|
|||
class TestJS(HttpCase): |
|||
|
|||
def test_js(self): |
|||
self.phantom_js( |
|||
"/web/tests?module=web_widget_float_formula", |
|||
"", |
|||
login="admin", |
|||
) |
@ -1,9 +0,0 @@ |
|||
<openerp> |
|||
<data> |
|||
<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/models.js"></script> |
|||
</xpath> |
|||
</template> |
|||
</data> |
|||
</openerp> |
@ -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="//html/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