forked from Techsystech/web
[MIG] web_widget_float_formula: Migration to 12.0
parent
6f772e19be
commit
61691cc090
|
@ -1,82 +0,0 @@
|
||||||
.. 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 @@
|
||||||
|
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
|
|
@ -1,22 +1,22 @@
|
||||||
# -*- coding: utf-8 -*-
|
# Copyright 2014-2015 GRAP
|
||||||
# Copyright GRAP
|
|
||||||
# Copyright 2016 LasLabs Inc.
|
# Copyright 2016 LasLabs Inc.
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
# Copyright 2020 Brainbean Apps (https://brainbeanapps.com)
|
||||||
|
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
|
||||||
|
|
||||||
{
|
{
|
||||||
'name': 'Web Widget - Formulas in Float Fields',
|
'name': 'Web Widget - Formulas in Float Fields',
|
||||||
'summary': 'Allow use of simple formulas in float fields',
|
'summary': 'Allow use of simple formulas in float fields',
|
||||||
'version': '10.0.1.0.0',
|
'version': '12.0.1.0.0',
|
||||||
'category': 'Web',
|
'category': 'Web',
|
||||||
'author': 'GRAP, LasLabs, Odoo Community Association (OCA)',
|
'author':
|
||||||
'website': 'http://www.grap.coop',
|
'GRAP, LasLabs, Brainbean Apps, Odoo Community Association (OCA)',
|
||||||
|
'website': 'https://github.com/OCA/web/',
|
||||||
'license': 'AGPL-3',
|
'license': 'AGPL-3',
|
||||||
'depends': [
|
'depends': [
|
||||||
'web',
|
'web',
|
||||||
],
|
],
|
||||||
'data': [
|
'data': [
|
||||||
'views/web_widget_float_formula.xml',
|
'templates/assets.xml',
|
||||||
],
|
],
|
||||||
'installable': True,
|
'installable': True,
|
||||||
'application': False,
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
* Sylvain Le Gal (https://twitter.com/legalsylvain)
|
||||||
|
* Oleg Bulkin <o.bulkin@gmail.com>
|
||||||
|
* Alexey Pelykh <alexey.pelykh@brainbeanapps.com>
|
|
@ -0,0 +1,11 @@
|
||||||
|
This module allows the use of simple math formulas in corresponding fields:
|
||||||
|
``=45 + 4/3 - 5 * (2 + 1)``
|
||||||
|
|
||||||
|
Features:
|
||||||
|
|
||||||
|
* ``+`` (addition)
|
||||||
|
* ``-`` (subtraction)
|
||||||
|
* ``*`` (multiplication)
|
||||||
|
* ``/`` (division)
|
||||||
|
* ``%`` (modulus)
|
||||||
|
* ``(`` and ``)`` parentheses
|
|
@ -0,0 +1 @@
|
||||||
|
This module is not needed for v13, as this feature is bundled with Odoo v13.
|
|
@ -1,111 +1,200 @@
|
||||||
/**
|
/**
|
||||||
* Copyright GRAP
|
* Copyright 2014-2015 GRAP
|
||||||
* Copyright 2016 LasLabs Inc.
|
* Copyright 2016 LasLabs Inc.
|
||||||
* License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
* Copyright 2020 Brainbean Apps (https://brainbeanapps.com)
|
||||||
**/
|
* License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
|
||||||
|
*/
|
||||||
odoo.define('web_widget_float_formula', function(require) {
|
odoo.define('web_widget_float_formula', function(require) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
var form_view = require('web.FormView');
|
var field_utils = require('web.field_utils');
|
||||||
form_view.include({
|
var pyUtils = require('web.py_utils');
|
||||||
// Ensure that formula is computed even if user saves right away and
|
var NumericField = require('web.basic_fields').NumericField;
|
||||||
// clean up '_formula_text' value to avoid bugs in tree view
|
var FieldMonetary = require('web.basic_fields').FieldMonetary;
|
||||||
_process_save: function(save_obj) {
|
|
||||||
for (var f in this.fields) {
|
var FormulaFieldMixin = {
|
||||||
if (!this.fields.hasOwnProperty(f)) { continue; }
|
//--------------------------------------------------------------------------
|
||||||
f = this.fields[f];
|
// Private
|
||||||
if (f.hasOwnProperty('_formula_text') && f.$el.find('input').length > 0) {
|
//--------------------------------------------------------------------------
|
||||||
f._compute_result();
|
|
||||||
f._clean_formula_text();
|
/**
|
||||||
|
* Unaltered formula that user has entered.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_formula: '',
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Value of the field that was concealed during formula reveal.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_concealedValue: '',
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns formula prefix character
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_getFormulaPrefix: function () {
|
||||||
|
return '=';
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process formula if one is detected.
|
||||||
|
*
|
||||||
|
* @override
|
||||||
|
* @private
|
||||||
|
* @param {any} value
|
||||||
|
* @param {Object} [options]
|
||||||
|
*/
|
||||||
|
_setValue: function (value, options) {
|
||||||
|
this._formula = '';
|
||||||
|
if (!!value && this._isFormula(value)) {
|
||||||
|
try {
|
||||||
|
var evaluated_value = this._evaluateFormula(value);
|
||||||
|
this._formula = value;
|
||||||
|
|
||||||
|
value = this._formatValue(evaluated_value);
|
||||||
|
this.$input.val(value);
|
||||||
|
} catch (err) {
|
||||||
|
this._formula = '';
|
||||||
|
} finally {
|
||||||
|
this._concealedValue = '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return this._super(value, options);
|
||||||
return this._super(save_obj);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if provided value is a formula.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @param {any} value
|
||||||
|
*/
|
||||||
|
_isFormula: function(value) {
|
||||||
|
value = value.toString().replace(/\s+/gm, '');
|
||||||
|
return value.startsWith(this._getFormulaPrefix())
|
||||||
|
|| this._getOperatorsRegExp().test(value);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns regular expression that matches all supported operators
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_getOperatorsRegExp: function () {
|
||||||
|
return /((?:\+)|(?:\-)|(?:\*)|(?:\/)|(?:\()|(?:\))|(?:\%))/;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Evaluate formula.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @param {any} formula
|
||||||
|
*/
|
||||||
|
_evaluateFormula: function(formula) {
|
||||||
|
return pyUtils.py_eval(this._preparseFormula(formula));
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pre-parses and sanitizes formula
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @param {string} formula
|
||||||
|
*/
|
||||||
|
_preparseFormula: function(formula) {
|
||||||
|
formula = formula.toString().replace(/\s+/gm, '');
|
||||||
|
var prefix = this._getFormulaPrefix();
|
||||||
|
if (formula.startsWith(prefix)) {
|
||||||
|
formula = formula.substring(prefix.length);
|
||||||
|
}
|
||||||
|
var operatorsRegExp = this._getOperatorsRegExp();
|
||||||
|
return formula.split(operatorsRegExp).reduce((tokens, token) => {
|
||||||
|
if (token === '') {
|
||||||
|
return tokens;
|
||||||
|
}
|
||||||
|
if (!operatorsRegExp.test(token)) {
|
||||||
|
token = field_utils.parse.float(token);
|
||||||
|
}
|
||||||
|
tokens.push(token);
|
||||||
|
return tokens;
|
||||||
|
}, []).join('');
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reveals formula
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_revealFormula: function () {
|
||||||
|
if (!!this._formula) {
|
||||||
|
this._concealedValue = this.$input.val();
|
||||||
|
this.$input.val(this._formula);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Conceals formula
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_concealFormula: function () {
|
||||||
|
var value = this.$input.val();
|
||||||
|
if (!!value && this._isFormula(value)) {
|
||||||
|
if (value !== this._formula) {
|
||||||
|
this.commitChanges();
|
||||||
|
} else if (!!this._concealedValue) {
|
||||||
|
this.$input.val(this._concealedValue);
|
||||||
|
this._concealedValue = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles 'focus' event
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @param {FocusEvent} event
|
||||||
|
*/
|
||||||
|
_onFocusFormulaField: function(event) {
|
||||||
|
if (this.$input === undefined || this.mode !== 'edit') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._revealFormula();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles 'blur' event
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @param {FocusEvent} event
|
||||||
|
*/
|
||||||
|
_onBlurFormulaField: function(event) {
|
||||||
|
if (this.$input === undefined || this.mode !== 'edit') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._concealFormula();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
NumericField.include({
|
||||||
|
...FormulaFieldMixin,
|
||||||
|
events: _.extend({}, NumericField.prototype.events, {
|
||||||
|
'focus': '_onFocusFormulaField',
|
||||||
|
'blur': '_onBlurFormulaField',
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
var core = require('web.core');
|
FieldMonetary.include({
|
||||||
core.bus.on('web_client_ready', null, function () {
|
...FormulaFieldMixin,
|
||||||
// Import localization values used to eval formula
|
events: _.extend({}, FieldMonetary.prototype.events, {
|
||||||
var translation_params = core._t.database.parameters;
|
'focusin': '_onFocusFormulaField',
|
||||||
var decimal_point = translation_params.decimal_point;
|
'focusout': '_onBlurFormulaField',
|
||||||
var thousands_sep = translation_params.thousands_sep;
|
}),
|
||||||
|
|
||||||
var field_float = require('web.form_widgets').FieldFloat;
|
|
||||||
field_float.include({
|
|
||||||
start: function() {
|
|
||||||
this._super();
|
|
||||||
this.on('blurred', this, this._compute_result);
|
|
||||||
this.on('focused', this, this._display_formula);
|
|
||||||
return this;
|
|
||||||
},
|
|
||||||
|
|
||||||
initialize_content: function() {
|
|
||||||
this._clean_formula_text();
|
|
||||||
return this._super();
|
|
||||||
},
|
|
||||||
|
|
||||||
_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) {
|
|
||||||
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;
|
|
||||||
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.$input.val(this._formula_text);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
FormulaFieldMixin: FormulaFieldMixin,
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,161 +1,169 @@
|
||||||
/**
|
/**
|
||||||
* Copyright 2016 LasLabs Inc.
|
* Copyright 2016 LasLabs Inc.
|
||||||
* License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
* Copyright 2020 Brainbean Apps (https://brainbeanapps.com)
|
||||||
**/
|
* License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
|
||||||
|
*/
|
||||||
|
odoo.define('web_widget_float_formula.test_web_widget_float_formula', function (require) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
odoo.define_section('web_widget_float_formula', ['web.form_common', 'web.form_widgets', 'web.core'], function(test) {
|
var FormView = require('web.FormView');
|
||||||
'use strict';
|
var testUtils = require('web.test_utils');
|
||||||
|
|
||||||
window.test_setup = function(self, form_common, form_widgets, core) {
|
QUnit.module('web_widget_float_formula', {}, function () {
|
||||||
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.field.$input = $('<input>');
|
|
||||||
self.field.$label = $('<label>');
|
|
||||||
};
|
|
||||||
|
|
||||||
test('Float fields should have a _formula_text property that defaults to an empty string',
|
QUnit.test('float field', async function (assert) {
|
||||||
function(assert, form_common, form_widgets, core) {
|
assert.expect(5);
|
||||||
window.test_setup(this, form_common, form_widgets, core);
|
|
||||||
|
const form = await testUtils.createAsyncView({
|
||||||
|
View: FormView,
|
||||||
|
model: 'demo_entry',
|
||||||
|
data: {
|
||||||
|
demo_entry: {
|
||||||
|
fields: {
|
||||||
|
test_field: {string: 'Test Field', type: 'float'},
|
||||||
|
},
|
||||||
|
records: [{id: 1, test_field: 0.0}],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res_id: 1,
|
||||||
|
arch:
|
||||||
|
'<form>' +
|
||||||
|
'<field name="test_field"/>' +
|
||||||
|
'</form>',
|
||||||
|
viewOptions: {
|
||||||
|
mode: 'edit',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
var test_field = form.$('.o_field_widget[name="test_field"]');
|
||||||
|
|
||||||
|
testUtils.fields.editInput(test_field, '0.0 + 40.0 + 2.0');
|
||||||
|
assert.strictEqual(test_field.val(), '42.00');
|
||||||
|
|
||||||
|
test_field.triggerHandler('focus');
|
||||||
|
assert.strictEqual(test_field.val(), '0.0 + 40.0 + 2.0');
|
||||||
|
test_field.triggerHandler('blur');
|
||||||
|
assert.strictEqual(test_field.val(), '42.00');
|
||||||
|
|
||||||
|
testUtils.fields.editInput(test_field, '=(1.5+8.0/2.0-(15+5)*0.1)');
|
||||||
|
assert.strictEqual(test_field.val(), '3.50');
|
||||||
|
|
||||||
|
testUtils.fields.editInput(test_field, 'bubblegum');
|
||||||
|
assert.strictEqual(test_field.val(), 'bubblegum');
|
||||||
|
|
||||||
|
form.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
QUnit.test('integer field', async function (assert) {
|
||||||
|
assert.expect(5);
|
||||||
|
|
||||||
|
const form = await testUtils.createAsyncView({
|
||||||
|
View: FormView,
|
||||||
|
model: 'demo_entry',
|
||||||
|
data: {
|
||||||
|
demo_entry: {
|
||||||
|
fields: {
|
||||||
|
test_field: {string: 'Test Field', type: 'integer'},
|
||||||
|
},
|
||||||
|
records: [{id: 1, test_field: 0}],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res_id: 1,
|
||||||
|
arch:
|
||||||
|
'<form>' +
|
||||||
|
'<field name="test_field"/>' +
|
||||||
|
'</form>',
|
||||||
|
viewOptions: {
|
||||||
|
mode: 'edit',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
var test_field = form.$('.o_field_widget[name="test_field"]');
|
||||||
|
|
||||||
|
testUtils.fields.editInput(test_field, '0 + 40 + 2');
|
||||||
|
assert.strictEqual(test_field.val(), '42');
|
||||||
|
|
||||||
|
test_field.triggerHandler('focus');
|
||||||
|
assert.strictEqual(test_field.val(), '0 + 40 + 2');
|
||||||
|
test_field.triggerHandler('blur');
|
||||||
|
assert.strictEqual(test_field.val(), '42');
|
||||||
|
|
||||||
|
testUtils.fields.editInput(test_field, '=(1+8/2-(15+5)*0.1)');
|
||||||
|
assert.strictEqual(test_field.val(), '3');
|
||||||
|
|
||||||
|
testUtils.fields.editInput(test_field, 'bubblegum');
|
||||||
|
assert.strictEqual(test_field.val(), 'bubblegum');
|
||||||
|
|
||||||
|
form.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
QUnit.test('monetary field', async function (assert) {
|
||||||
|
assert.expect(5);
|
||||||
|
|
||||||
|
const form = await testUtils.createAsyncView({
|
||||||
|
View: FormView,
|
||||||
|
model: 'demo_entry',
|
||||||
|
data: {
|
||||||
|
demo_entry: {
|
||||||
|
fields: {
|
||||||
|
test_field: {string: 'Test Field', type: 'monetary'},
|
||||||
|
currency_id: {string: 'Currency', type: 'many2one', relation: 'currency', searchable: true},
|
||||||
|
},
|
||||||
|
records: [{id: 1, test_field: 0.0, currency_id: 1}],
|
||||||
|
},
|
||||||
|
currency: {
|
||||||
|
fields: {
|
||||||
|
symbol: {string: 'Currency Sumbol', type: 'char', searchable: true},
|
||||||
|
position: {string: 'Currency Position', type: 'char', searchable: true},
|
||||||
|
},
|
||||||
|
records: [{
|
||||||
|
id: 1,
|
||||||
|
display_name: '$',
|
||||||
|
symbol: '$',
|
||||||
|
position: 'before',
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
},
|
||||||
|
res_id: 1,
|
||||||
|
arch:
|
||||||
|
'<form>' +
|
||||||
|
'<field name="test_field" widget="monetary"/>' +
|
||||||
|
'<field name="currency_id" invisible="1"/>' +
|
||||||
|
'</form>',
|
||||||
|
viewOptions: {
|
||||||
|
mode: 'edit',
|
||||||
|
},
|
||||||
|
session: {
|
||||||
|
currencies: {
|
||||||
|
1: {
|
||||||
|
id: 1,
|
||||||
|
display_name: '$',
|
||||||
|
symbol: '$',
|
||||||
|
position: 'before',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
var test_field = form.$('.o_field_widget[name="test_field"]');
|
||||||
|
var test_field_input = form.$('.o_field_widget[name="test_field"] input');
|
||||||
|
|
||||||
|
testUtils.fields.editInput(test_field_input, '0.0 + 40.0 + 2.0');
|
||||||
|
assert.strictEqual(test_field_input.val(), '42.00');
|
||||||
|
|
||||||
|
test_field.triggerHandler('focusin');
|
||||||
|
assert.strictEqual(test_field_input.val(), '0.0 + 40.0 + 2.0');
|
||||||
|
test_field.triggerHandler('focusout');
|
||||||
|
assert.strictEqual(test_field_input.val(), '42.00');
|
||||||
|
|
||||||
|
testUtils.fields.editInput(test_field_input, '=(1.5+8.0/2.0-(15+5)*0.1)');
|
||||||
|
assert.strictEqual(test_field_input.val(), '3.50');
|
||||||
|
|
||||||
|
testUtils.fields.editInput(test_field_input, 'bubblegum');
|
||||||
|
assert.strictEqual(test_field_input.val(), 'bubblegum');
|
||||||
|
|
||||||
|
form.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
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.field.$input.val('=2*3a');
|
|
||||||
this.field._compute_result();
|
|
||||||
|
|
||||||
assert.strictEqual(this.field.$input.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.field.$input.val('=*/');
|
|
||||||
this.field._compute_result();
|
|
||||||
|
|
||||||
assert.strictEqual(this.field.$input.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.field.$input.val('=2*3');
|
|
||||||
this.field._compute_result();
|
|
||||||
|
|
||||||
assert.equal(this.field.$input.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.field.$input.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);
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Copyright GRAP
|
Copyright 2014-2015 GRAP
|
||||||
Copyright 2016 LasLabs Inc.
|
Copyright 2016 LasLabs Inc.
|
||||||
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
Copyright 2020 Brainbean Apps (https://brainbeanapps.com)
|
||||||
|
License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<odoo>
|
<odoo>
|
||||||
<template id="assets_backend" name="web_widget_float_formula Assets" inherit_id="web.assets_backend">
|
<template id="assets_backend" name="web_widget_float_formula Assets" inherit_id="web.assets_backend">
|
||||||
<xpath expr="." position="inside">
|
<xpath expr="." position="inside">
|
|
@ -1,5 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Copyright 2016 LasLabs Inc.
|
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
|
||||||
|
|
||||||
from . import test_js
|
|
|
@ -1,16 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Copyright 2016 LasLabs Inc.
|
|
||||||
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
|
|
||||||
|
|
||||||
import odoo.tests
|
|
||||||
|
|
||||||
|
|
||||||
class TestJS(odoo.tests.HttpCase):
|
|
||||||
|
|
||||||
def test_js(self):
|
|
||||||
self.phantom_js(
|
|
||||||
"/web/tests?module=web_widget_float_formula",
|
|
||||||
"console.log('ok')",
|
|
||||||
"",
|
|
||||||
login="admin"
|
|
||||||
)
|
|
Loading…
Reference in New Issue