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
-
75web_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', |
'website': 'http://www.grap.coop', |
||||
'license': 'AGPL-3', |
'license': 'AGPL-3', |
||||
'depends': [ |
'depends': [ |
||||
'web', |
'web', |
||||
], |
], |
||||
'data': [ |
'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