diff --git a/web_domain_field/README.rst b/web_domain_field/README.rst new file mode 100644 index 00000000..e70f68f2 --- /dev/null +++ b/web_domain_field/README.rst @@ -0,0 +1,104 @@ +.. 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 + +================ +Web Domain Field +================ + +When you define a view you can specify on the relational fields a domain +attribute. This attribute is evaluated as filter to apply when displaying +existing records for selection. + +.. code-block:: xml + + + +The value provided for the domain attribute must be a string representing a +valid Odoo domain. This string is evaluated on the client side in a +restricted context where we can reference as right operand the values of +fields present into the form and a limited set of functions. + +In this context it's hard to build complex domain and we are facing to some +limitations as: + + * The syntax to include in your domain a criteria involving values from a + x2many field is complex. + * Domains computed by an onchange on an other field are not recomputed when + you modify the form and don't modify the field triggering the onchange. + * It's not possible to extend an existing domain. You must completely redefine + the domain in your specialized addon + * ... + +In order to mitigate these limitations this new addon allows you to use the +value of a field as domain of an other field in the xml definition of your +view. + +.. code-block:: xml + + + + +The field used as domain must provide the domain as a JSON encoded string. + +.. code-block:: python + + product_id_domain = fields.Char( + compute="_compute_product_id_domain", + readonly=True, + store=False, + ) + + @api.multi + @api.depends('name') + def _compute_product_id_domain(self): + for rec in self: + rec.product_id_domain = json.dumps( + [('type', '=', 'product'), ('name', 'like', rec.name)] + ) + + +Usage +===== + +.. 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/9.0 + + + +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 smashing it by providing a detailed and welcomed feedback. + +Credits +======= + +Images +------ + +* Odoo Community Association: `Icon `_. + +Contributors +------------ + +* Laurent Mignon + +Maintainer +---------- + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://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 https://odoo-community.org. diff --git a/web_domain_field/__init__.py b/web_domain_field/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/web_domain_field/__openerp__.py b/web_domain_field/__openerp__.py new file mode 100644 index 00000000..4ff66a1c --- /dev/null +++ b/web_domain_field/__openerp__.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 ACSONE SA/NV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + 'name': 'Web Domain Field', + 'summary': """ + Use computed field as domain""", + 'version': '9.0.1.0.0', + 'license': 'AGPL-3', + 'author': 'ACSONE SA/NV,Odoo Community Association (OCA)', + 'website': 'https://acsone.eu/', + 'depends': [ + 'web' + ], + 'data': [ + 'views/web_domain_field.xml', + ], + 'demo': [ + ], +} diff --git a/web_domain_field/static/description/icon.png b/web_domain_field/static/description/icon.png new file mode 100644 index 00000000..3a0328b5 Binary files /dev/null and b/web_domain_field/static/description/icon.png differ diff --git a/web_domain_field/static/src/js/pyeval.js b/web_domain_field/static/src/js/pyeval.js new file mode 100644 index 00000000..f4b75e57 --- /dev/null +++ b/web_domain_field/static/src/js/pyeval.js @@ -0,0 +1,233 @@ +odoo.define('web.domain_field', function (require) { +"use strict"; + +var pyeval = require('web.pyeval'); +var session = require('web.session'); + + +var original_pyeval = pyeval.eval; +var original_ensure_evaluated = pyeval.ensure_evaluated; +var py = window.py; + +/** copied from pyeval and not modified but required since not publicly +exposed by web.pyeval**/ + +// recursively wraps JS objects passed into the context to attributedicts +// which jsonify back to JS objects +function wrap(value) { + if (value === null) { return py.None; } + + switch (typeof value) { + case 'undefined': throw new Error("No conversion for undefined"); + case 'boolean': return py.bool.fromJSON(value); + case 'number': return py.float.fromJSON(value); + case 'string': return py.str.fromJSON(value); + } + + switch(value.constructor) { + case Object: return wrapping_dict.fromJSON(value); + case Array: return wrapping_list.fromJSON(value); + } + + throw new Error("ValueError: unable to wrap " + value); +} + +var wrapping_dict = py.type('wrapping_dict', null, { + __init__: function () { + this._store = {}; + }, + __getitem__: function (key) { + var k = key.toJSON(); + if (!(k in this._store)) { + throw new Error("KeyError: '" + k + "'"); + } + return wrap(this._store[k]); + }, + __getattr__: function (key) { + return this.__getitem__(py.str.fromJSON(key)); + }, + __len__: function () { + return Object.keys(this._store).length + }, + __nonzero__: function () { + return py.PY_size(this) > 0 ? py.True : py.False; + }, + get: function () { + var args = py.PY_parseArgs(arguments, ['k', ['d', py.None]]); + + if (!(args.k.toJSON() in this._store)) { return args.d; } + return this.__getitem__(args.k); + }, + fromJSON: function (d) { + var instance = py.PY_call(wrapping_dict); + instance._store = d; + return instance; + }, + toJSON: function () { + return this._store; + }, +}); + +var wrapping_list = py.type('wrapping_list', null, { + __init__: function () { + this._store = []; + }, + __getitem__: function (index) { + return wrap(this._store[index.toJSON()]); + }, + __len__: function () { + return this._store.length; + }, + __nonzero__: function () { + return py.PY_size(this) > 0 ? py.True : py.False; + }, + fromJSON: function (ar) { + var instance = py.PY_call(wrapping_list); + instance._store = ar; + return instance; + }, + toJSON: function () { + return this._store; + }, +}); + +function wrap_context (context) { + for (var k in context) { + if (!context.hasOwnProperty(k)) { continue; } + var val = context[k]; + + if (val === null) { continue; } + if (val.constructor === Array) { + context[k] = wrapping_list.fromJSON(val); + } else if (val.constructor === Object + && !py.PY_isInstance(val, py.object)) { + context[k] = wrapping_dict.fromJSON(val); + } + } + return context; +} + +function ensure_evaluated (args, kwargs) { + for (var i=0; i + +