forked from Techsystech/web
commit
df2e48c20c
|
@ -0,0 +1 @@
|
|||
../../../../web_domain_field
|
|
@ -0,0 +1,6 @@
|
|||
import setuptools
|
||||
|
||||
setuptools.setup(
|
||||
setup_requires=['setuptools-odoo'],
|
||||
odoo_addon=True,
|
||||
)
|
|
@ -0,0 +1,107 @@
|
|||
.. 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
|
||||
|
||||
<field name="product_id" domain="[('type','=','product')]"/>
|
||||
|
||||
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.
|
||||
* The right side of domain in case of x2many can involve huge amount of ids
|
||||
(performance problem).
|
||||
* 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
|
||||
* etc...
|
||||
|
||||
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
|
||||
|
||||
<field name="product_id_domain" invisible="1"/>
|
||||
<field name="product_id" domain="product_id_domain"/>
|
||||
|
||||
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/10.0
|
||||
|
||||
|
||||
|
||||
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 smashing it by providing a detailed and welcomed feedback.
|
||||
|
||||
Credits
|
||||
=======
|
||||
|
||||
Images
|
||||
------
|
||||
|
||||
* Odoo Community Association: `Icon <https://github.com/OCA/maintainer-tools/blob/master/template/module/static/description/icon.svg>`_.
|
||||
|
||||
Contributors
|
||||
------------
|
||||
|
||||
* Laurent Mignon <laurent.mignon@acsone.eu>
|
||||
* Denis Roussel <denis.roussel@acsone.eu>
|
||||
|
||||
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.
|
|
@ -0,0 +1,15 @@
|
|||
# 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": "13.0.1.0.0",
|
||||
"license": "AGPL-3",
|
||||
"author": "ACSONE SA/NV,Odoo Community Association (OCA)",
|
||||
"website": "https://github.com/OCA/web",
|
||||
"depends": ["web"],
|
||||
"data": ["views/web_domain_field.xml"],
|
||||
"installable": True,
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
# Translation of Odoo Server.
|
||||
# This file contains the translation of the following modules:
|
||||
# * web_domain_field
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Odoo Server 10.0\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"Last-Translator: <>\n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: \n"
|
||||
"Plural-Forms: \n"
|
||||
|
||||
#. module: web_domain_field
|
||||
#. openerp-web
|
||||
#: code:addons/web_domain_field/static/src/js/pyeval.js:160
|
||||
#, python-format
|
||||
msgid "Unknown nonliteral type "
|
||||
msgstr ""
|
||||
|
Binary file not shown.
After Width: | Height: | Size: 9.2 KiB |
|
@ -0,0 +1,212 @@
|
|||
odoo.define('web.domain_field', function (require) {
|
||||
"use strict";
|
||||
|
||||
var py_utils = require('web.py_utils');
|
||||
var session = require('web.session');
|
||||
|
||||
|
||||
var original_pyeval = py_utils.eval;
|
||||
var py = window.py;
|
||||
|
||||
/** Copied from py_utils and not modified but required since not publicly
|
||||
exposed by web.py_utils**/
|
||||
|
||||
// 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];
|
||||
// Don't add a test case like ``val === undefined``
|
||||
// this is intended to prevent letting crap pass
|
||||
// on the context without even knowing it.
|
||||
// If you face an issue from here, try to sanitize
|
||||
// the context upstream instead
|
||||
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<args.length; ++i) {
|
||||
args[i] = eval_arg(args[i]);
|
||||
}
|
||||
for (var k in kwargs) {
|
||||
if (!kwargs.hasOwnProperty(k)) { continue; }
|
||||
kwargs[k] = eval_arg(kwargs[k]);
|
||||
}
|
||||
}
|
||||
/** End of unmodified methods copied from pyeval **/
|
||||
|
||||
// We need to override the original method to be able to call our
|
||||
// Specialized version of pyeval for domain fields
|
||||
function eval_arg (arg) {
|
||||
if (typeof arg !== 'object' || !arg.__ref) {
|
||||
return arg;
|
||||
}
|
||||
switch (arg.__ref) {
|
||||
case 'domain': case 'compound_domain':
|
||||
return domain_field_pyeval('domains', [arg]);
|
||||
case 'context': case 'compound_context':
|
||||
return original_pyeval('contexts', [arg]);
|
||||
default:
|
||||
throw new Error(_t("Unknown nonliteral type ") + ' ' + arg.__ref);
|
||||
}
|
||||
}
|
||||
|
||||
// Override eval_domains to add 3 lines in order to be able to use a field
|
||||
// value as domain
|
||||
function eval_domains(domains, evaluation_context) {
|
||||
evaluation_context = _.extend(py_utils.context(), evaluation_context || {});
|
||||
var result_domain = [];
|
||||
// Normalize only if the first domain is the array ["|"] or ["!"]
|
||||
var need_normalization = (
|
||||
domains &&
|
||||
domains.length > 0 &&
|
||||
domains[0].length === 1 &&
|
||||
(domains[0][0] === "|" || domains[0][0] === "!")
|
||||
);
|
||||
_(domains).each(function (domain) {
|
||||
if (_.isString(domain)) {
|
||||
// Modified part or the original method
|
||||
if (domain in evaluation_context) {
|
||||
result_domain.push.apply(
|
||||
result_domain, $.parseJSON(evaluation_context[domain]));
|
||||
return;
|
||||
}
|
||||
// End of modifications
|
||||
|
||||
// wrap raw strings in domain
|
||||
domain = { __ref: 'domain', __debug: domain };
|
||||
}
|
||||
var domain_array_to_combine;
|
||||
switch(domain.__ref) {
|
||||
case 'domain':
|
||||
evaluation_context.context = evaluation_context;
|
||||
domain_array_to_combine = py.eval(domain.__debug, wrap_context(evaluation_context));
|
||||
break;
|
||||
default:
|
||||
domain_array_to_combine = domain;
|
||||
}
|
||||
if (need_normalization) {
|
||||
domain_array_to_combine = get_normalized_domain(domain_array_to_combine);
|
||||
}
|
||||
result_domain.push.apply(result_domain, domain_array_to_combine);
|
||||
});
|
||||
return result_domain;
|
||||
}
|
||||
|
||||
|
||||
// Override pyeval in order to call our specialized implementation of
|
||||
// eval_domains
|
||||
function domain_field_pyeval (type, object, context, options) {
|
||||
switch (type) {
|
||||
case 'domain':
|
||||
case 'domains':
|
||||
if (type === 'domain') {
|
||||
object = [object];
|
||||
}
|
||||
return eval_domains(object, context);
|
||||
default:
|
||||
return original_pyeval(type, object, context, options);
|
||||
}
|
||||
}
|
||||
|
||||
function eval_domains_and_contexts(source) {
|
||||
// see Session.eval_context in Python
|
||||
return {
|
||||
context: domain_field_pyeval('contexts', source.contexts || [], source.eval_context),
|
||||
domain: domain_field_pyeval('domains', source.domains, source.eval_context),
|
||||
group_by: domain_field_pyeval('groupbys', source.group_by_seq || [], source.eval_context),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
py_utils.eval = domain_field_pyeval;
|
||||
py_utils.ensure_evaluated = ensure_evaluated;
|
||||
py_utils.eval_domains_and_contexts = eval_domains_and_contexts;
|
||||
|
||||
});
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<template id="assets_backend" name="web_domain_field assets" inherit_id="web.assets_backend">
|
||||
<xpath expr="script[last()]" position="after">
|
||||
<script type="text/javascript" src="/web_domain_field/static/src/js/pyeval.js" />
|
||||
</xpath>
|
||||
</template>
|
||||
</odoo>
|
Loading…
Reference in New Issue