545 lines
23 KiB
Diff
545 lines
23 KiB
Diff
diff --git a/proteus/proteus/__init__.py b/proteus/proteus/__init__.py
|
|
index bfad1dc7382cb9fd2c17bef77da699ed36869d3e_cHJvdGV1cy9wcm90ZXVzL19faW5pdF9fLnB5..bf7072adc76db4e62ec3fbde7efde17e7bbd630d_cHJvdGV1cy9wcm90ZXVzL19faW5pdF9fLnB5 100644
|
|
--- a/proteus/proteus/__init__.py
|
|
+++ b/proteus/proteus/__init__.py
|
|
@@ -1152,9 +1152,8 @@
|
|
values.update(self._on_change_args(on_change))
|
|
if values:
|
|
context = self._context
|
|
- changes = getattr(self._proxy, 'on_change')(values, names, context)
|
|
- for change in changes:
|
|
- self._set_on_change(change)
|
|
+ change = getattr(self._proxy, 'on_change')(values, names, context)
|
|
+ self._set_on_change(change)
|
|
|
|
values = {}
|
|
fieldnames = set(names)
|
|
diff --git a/sao/src/model.js b/sao/src/model.js
|
|
index bfad1dc7382cb9fd2c17bef77da699ed36869d3e_c2FvL3NyYy9tb2RlbC5qcw==..bf7072adc76db4e62ec3fbde7efde17e7bbd630d_c2FvL3NyYy9tb2RlbC5qcw== 100644
|
|
--- a/sao/src/model.js
|
|
+++ b/sao/src/model.js
|
|
@@ -1071,5 +1071,5 @@
|
|
[values], this.get_context(), false));
|
|
}
|
|
} else {
|
|
- changes = this.model.execute(
|
|
+ changes = [this.model.execute(
|
|
'on_change',
|
|
@@ -1075,5 +1075,5 @@
|
|
'on_change',
|
|
- [values, fieldnames], this.get_context(), false);
|
|
+ [values, fieldnames], this.get_context(), false)];
|
|
}
|
|
} catch (e) {
|
|
return;
|
|
@@ -1129,9 +1129,9 @@
|
|
delete this._values[fieldname + '.'];
|
|
}
|
|
}
|
|
- var result;
|
|
+ var changed;
|
|
fieldnames = Object.keys(fieldnames);
|
|
if (fieldnames.length) {
|
|
try {
|
|
if ((fieldnames.length == 1) ||
|
|
(values.id === undefined)) {
|
|
@@ -1133,7 +1133,7 @@
|
|
fieldnames = Object.keys(fieldnames);
|
|
if (fieldnames.length) {
|
|
try {
|
|
if ((fieldnames.length == 1) ||
|
|
(values.id === undefined)) {
|
|
- result = {};
|
|
+ changed = {};
|
|
for (const fieldname of fieldnames) {
|
|
@@ -1139,6 +1139,8 @@
|
|
for (const fieldname of fieldnames) {
|
|
- result[fieldname] = this.model.execute(
|
|
- 'on_change_with_' + fieldname,
|
|
- [values], this.get_context(), false);
|
|
+ changed = jQuery.extend(
|
|
+ changed,
|
|
+ this.model.execute(
|
|
+ 'on_change_with_' + fieldname,
|
|
+ [values], this.get_context(), false));
|
|
}
|
|
} else {
|
|
@@ -1143,9 +1145,9 @@
|
|
}
|
|
} else {
|
|
- result = this.model.execute(
|
|
+ changed = this.model.execute(
|
|
'on_change_with',
|
|
[values, fieldnames], this.get_context(), false);
|
|
}
|
|
} catch (e) {
|
|
return;
|
|
}
|
|
@@ -1146,10 +1148,10 @@
|
|
'on_change_with',
|
|
[values, fieldnames], this.get_context(), false);
|
|
}
|
|
} catch (e) {
|
|
return;
|
|
}
|
|
- this.set_on_change(result);
|
|
+ this.set_on_change(changed);
|
|
}
|
|
if (!jQuery.isEmptyObject(later)) {
|
|
values = {};
|
|
@@ -1164,5 +1166,5 @@
|
|
try {
|
|
if ((fieldnames.length == 1) ||
|
|
(values.id === undefined)) {
|
|
- result = {};
|
|
+ changed = {};
|
|
for (const fieldname of fieldnames) {
|
|
@@ -1168,6 +1170,8 @@
|
|
for (const fieldname of fieldnames) {
|
|
- result[fieldname] = this.model.execute(
|
|
- 'on_change_with_' + fieldname,
|
|
- [values], this.get_context(), false);
|
|
+ changed = jQuery.extend(
|
|
+ changed,
|
|
+ this.model.execute(
|
|
+ 'on_change_with_' + fieldname,
|
|
+ [values], this.get_context(), false));
|
|
}
|
|
} else {
|
|
@@ -1172,9 +1176,9 @@
|
|
}
|
|
} else {
|
|
- result = this.model.execute(
|
|
+ changed = this.model.execute(
|
|
'on_change_with',
|
|
[values, fieldnames], this.get_context(), false);
|
|
}
|
|
} catch (e) {
|
|
return;
|
|
}
|
|
@@ -1175,10 +1179,10 @@
|
|
'on_change_with',
|
|
[values, fieldnames], this.get_context(), false);
|
|
}
|
|
} catch (e) {
|
|
return;
|
|
}
|
|
- this.set_on_change(result);
|
|
+ this.set_on_change(changed);
|
|
}
|
|
},
|
|
set_on_change: function(values) {
|
|
diff --git a/tryton/tryton/gui/window/view_form/model/record.py b/tryton/tryton/gui/window/view_form/model/record.py
|
|
index bfad1dc7382cb9fd2c17bef77da699ed36869d3e_dHJ5dG9uL3RyeXRvbi9ndWkvd2luZG93L3ZpZXdfZm9ybS9tb2RlbC9yZWNvcmQucHk=..bf7072adc76db4e62ec3fbde7efde17e7bbd630d_dHJ5dG9uL3RyeXRvbi9ndWkvd2luZG93L3ZpZXdfZm9ybS9tb2RlbC9yZWNvcmQucHk= 100644
|
|
--- a/tryton/tryton/gui/window/view_form/model/record.py
|
|
+++ b/tryton/tryton/gui/window/view_form/model/record.py
|
|
@@ -577,9 +577,9 @@
|
|
'on_change_' + fieldname,
|
|
values, context=self.get_context()))
|
|
else:
|
|
- changes = RPCExecute(
|
|
- 'model', self.model_name, 'on_change',
|
|
- values, fieldnames, context=self.get_context())
|
|
+ changes = [RPCExecute(
|
|
+ 'model', self.model_name, 'on_change',
|
|
+ values, fieldnames, context=self.get_context())]
|
|
except RPCException:
|
|
pass
|
|
else:
|
|
@@ -621,5 +621,5 @@
|
|
if fieldnames:
|
|
try:
|
|
if len(fieldnames) == 1 or 'id' not in values:
|
|
- result = {}
|
|
+ changed = {}
|
|
for fieldname in fieldnames:
|
|
@@ -625,6 +625,6 @@
|
|
for fieldname in fieldnames:
|
|
- result[fieldname] = RPCExecute(
|
|
- 'model', self.model_name,
|
|
- 'on_change_with_' + fieldname,
|
|
- values, context=self.get_context())
|
|
+ changed.update(RPCExecute(
|
|
+ 'model', self.model_name,
|
|
+ 'on_change_with_' + fieldname,
|
|
+ values, context=self.get_context()))
|
|
else:
|
|
@@ -630,6 +630,6 @@
|
|
else:
|
|
- result = RPCExecute(
|
|
+ changed = RPCExecute(
|
|
'model', self.model_name, 'on_change_with',
|
|
values, list(fieldnames), context=self.get_context())
|
|
except RPCException:
|
|
return
|
|
@@ -632,8 +632,8 @@
|
|
'model', self.model_name, 'on_change_with',
|
|
values, list(fieldnames), context=self.get_context())
|
|
except RPCException:
|
|
return
|
|
- self.set_on_change(result)
|
|
+ self.set_on_change(changed)
|
|
if later:
|
|
values = {}
|
|
for fieldname in later:
|
|
@@ -642,5 +642,5 @@
|
|
values.update(self._get_on_change_args(on_change_with))
|
|
try:
|
|
if len(later) == 1 or 'id' not in values:
|
|
- result = {}
|
|
+ changed = {}
|
|
for fieldname in fieldnames:
|
|
@@ -646,6 +646,6 @@
|
|
for fieldname in fieldnames:
|
|
- result[fieldname] = RPCExecute(
|
|
- 'model', self.model_name,
|
|
- 'on_change_with_' + fieldname,
|
|
- values, context=self.get_context())
|
|
+ changed.update(RPCExecute(
|
|
+ 'model', self.model_name,
|
|
+ 'on_change_with_' + fieldname,
|
|
+ values, context=self.get_context()))
|
|
else:
|
|
@@ -651,6 +651,6 @@
|
|
else:
|
|
- result = RPCExecute(
|
|
+ changed = RPCExecute(
|
|
'model', self.model_name, 'on_change_with',
|
|
values, list(later), context=self.get_context())
|
|
except RPCException:
|
|
return
|
|
@@ -653,8 +653,8 @@
|
|
'model', self.model_name, 'on_change_with',
|
|
values, list(later), context=self.get_context())
|
|
except RPCException:
|
|
return
|
|
- self.set_on_change(result)
|
|
+ self.set_on_change(changed)
|
|
|
|
def autocomplete_with(self, field_name):
|
|
for fieldname, fieldinfo in self.group.fields.items():
|
|
diff --git a/trytond/CHANGELOG b/trytond/CHANGELOG
|
|
index bfad1dc7382cb9fd2c17bef77da699ed36869d3e_dHJ5dG9uZC9DSEFOR0VMT0c=..bf7072adc76db4e62ec3fbde7efde17e7bbd630d_dHJ5dG9uZC9DSEFOR0VMT0c= 100644
|
|
--- a/trytond/CHANGELOG
|
|
+++ b/trytond/CHANGELOG
|
|
@@ -1,3 +1,5 @@
|
|
+* Support add/update/remove/delete for on_change_with of xxx2Many
|
|
+* Add decorator on RPC
|
|
* Add a canonicalize function for domains
|
|
* Enforce record rules when reading only non SQL fields (#12428)
|
|
* Support PYSON comparison of timedelta
|
|
diff --git a/trytond/doc/ref/rpc.rst b/trytond/doc/ref/rpc.rst
|
|
index bfad1dc7382cb9fd2c17bef77da699ed36869d3e_dHJ5dG9uZC9kb2MvcmVmL3JwYy5yc3Q=..bf7072adc76db4e62ec3fbde7efde17e7bbd630d_dHJ5dG9uZC9kb2MvcmVmL3JwYy5yc3Q= 100644
|
|
--- a/trytond/doc/ref/rpc.rst
|
|
+++ b/trytond/doc/ref/rpc.rst
|
|
@@ -4,7 +4,7 @@
|
|
RPC
|
|
===
|
|
|
|
-.. class:: RPC([readonly[, instantiate[, result[, check_access[, unique[, fresh_session[, cache]]]]]]])
|
|
+.. class:: RPC([readonly[, instantiate[, [decorator, result[, check_access[, unique[, fresh_session[, cache]]]]]]]])
|
|
|
|
Define the behavior of Remote Procedure Call.
|
|
|
|
@@ -18,6 +18,10 @@
|
|
|
|
The position or the slice of the argument to be instanciated
|
|
|
|
+.. attribute:: RPC.decorator
|
|
+
|
|
+ The function to decorate the called procedure with
|
|
+
|
|
.. attribute:: RPC.result
|
|
|
|
The function to transform the result
|
|
diff --git a/trytond/trytond/model/fields/field.py b/trytond/trytond/model/fields/field.py
|
|
index bfad1dc7382cb9fd2c17bef77da699ed36869d3e_dHJ5dG9uZC90cnl0b25kL21vZGVsL2ZpZWxkcy9maWVsZC5weQ==..bf7072adc76db4e62ec3fbde7efde17e7bbd630d_dHJ5dG9uZC90cnl0b25kL21vZGVsL2ZpZWxkcy9maWVsZC5weQ== 100644
|
|
--- a/trytond/trytond/model/fields/field.py
|
|
+++ b/trytond/trytond/model/fields/field.py
|
|
@@ -1,7 +1,7 @@
|
|
# This file is part of Tryton. The COPYRIGHT file at the top level of
|
|
# this repository contains the full copyright notices and license terms.
|
|
import warnings
|
|
-from functools import partial, wraps
|
|
+from functools import wraps
|
|
|
|
import sql
|
|
from sql import (
|
|
@@ -195,18 +195,15 @@
|
|
return record._changed_values
|
|
|
|
|
|
-def on_change_with_result(field, value):
|
|
- from ..modelstorage import ModelStorage
|
|
- if field._type in {'many2one', 'one2one', 'reference'}:
|
|
- if isinstance(value, ModelStorage):
|
|
- if field._type == 'reference':
|
|
- value = str(value)
|
|
- else:
|
|
- value = value.id
|
|
- elif field._type in {'one2many', 'many2many'}:
|
|
- if isinstance(value, (list, tuple)):
|
|
- value = [int(r) for r in value]
|
|
- return value
|
|
+def on_change_with_result(fieldname):
|
|
+ def decorator(func):
|
|
+ @wraps(func)
|
|
+ def wrapper(self, *args, **kwargs):
|
|
+ value = func(self, *args, **kwargs)
|
|
+ setattr(self, fieldname, value)
|
|
+ return self._changed_values
|
|
+ return wrapper
|
|
+ return decorator
|
|
|
|
|
|
def domain_method(func):
|
|
@@ -476,9 +473,9 @@
|
|
return [self.sql_column(table)]
|
|
|
|
def set_rpc(self, model):
|
|
- for attribute, result in (
|
|
- ('on_change', on_change_result),
|
|
- ('on_change_with', partial(on_change_with_result, self)),
|
|
+ for attribute, decorator, result in (
|
|
+ ('on_change', None, on_change_result),
|
|
+ ('on_change_with', on_change_with_result(self.name), None),
|
|
):
|
|
if not getattr(self, attribute):
|
|
continue
|
|
@@ -486,7 +483,8 @@
|
|
assert hasattr(model, func_name), \
|
|
'Missing %s on model %s' % (func_name, model.__name__)
|
|
model.__rpc__.setdefault(
|
|
- func_name, RPC(instantiate=0, result=result))
|
|
+ func_name,
|
|
+ RPC(instantiate=0, decorator=decorator, result=result))
|
|
|
|
def definition(self, model, language):
|
|
pool = Pool()
|
|
diff --git a/trytond/trytond/model/fields/function.py b/trytond/trytond/model/fields/function.py
|
|
index bfad1dc7382cb9fd2c17bef77da699ed36869d3e_dHJ5dG9uZC90cnl0b25kL21vZGVsL2ZpZWxkcy9mdW5jdGlvbi5weQ==..bf7072adc76db4e62ec3fbde7efde17e7bbd630d_dHJ5dG9uZC90cnl0b25kL21vZGVsL2ZpZWxkcy9mdW5jdGlvbi5weQ== 100644
|
|
--- a/trytond/trytond/model/fields/function.py
|
|
+++ b/trytond/trytond/model/fields/function.py
|
|
@@ -9,7 +9,7 @@
|
|
from trytond.tools import is_instance_method
|
|
from trytond.transaction import Transaction, without_check_access
|
|
|
|
-from .field import Field, domain_method, on_change_with_result
|
|
+from .field import Field, domain_method
|
|
|
|
|
|
def getter_context(func):
|
|
@@ -124,5 +124,16 @@
|
|
|
|
def call(name):
|
|
if not instance_method:
|
|
- return on_change_with_result(self, method(records, name))
|
|
+ values = method(records, name)
|
|
+ if isinstance(name, str):
|
|
+ return convert_dict(values)
|
|
+ else:
|
|
+ return {k: convert_dict(v, k) for k, v in values.items()}
|
|
+ else:
|
|
+ return {r.id: convert(method(r, name)) for r in records}
|
|
+
|
|
+ def convert(value, name=None):
|
|
+ from ..model import Model as BaseModel
|
|
+ if name:
|
|
+ field = Model._fields[name]._field
|
|
else:
|
|
@@ -128,7 +139,22 @@
|
|
else:
|
|
- return {
|
|
- r.id: on_change_with_result(self, method(r, name))
|
|
- for r in records}
|
|
+ field = self._field
|
|
+ if field._type in {'many2one', 'one2one', 'reference'}:
|
|
+ if isinstance(value, BaseModel):
|
|
+ if field._type == 'reference':
|
|
+ value = str(value)
|
|
+ else:
|
|
+ value = int(value)
|
|
+ elif field._type in {'one2many', 'many2many'}:
|
|
+ if value:
|
|
+ value = [int(r) for r in value]
|
|
+ return value
|
|
+
|
|
+ def convert_dict(values, name=None):
|
|
+ # Keep the same class
|
|
+ values = values.copy()
|
|
+ values.update((k, convert(v, name)) for k, v in values.items())
|
|
+ return values
|
|
+
|
|
if isinstance(name, list):
|
|
names = name
|
|
if multiple:
|
|
diff --git a/trytond/trytond/model/modelview.py b/trytond/trytond/model/modelview.py
|
|
index bfad1dc7382cb9fd2c17bef77da699ed36869d3e_dHJ5dG9uZC90cnl0b25kL21vZGVsL21vZGVsdmlldy5weQ==..bf7072adc76db4e62ec3fbde7efde17e7bbd630d_dHJ5dG9uZC90cnl0b25kL21vZGVsL21vZGVsdmlldy5weQ== 100644
|
|
--- a/trytond/trytond/model/modelview.py
|
|
+++ b/trytond/trytond/model/modelview.py
|
|
@@ -68,8 +68,9 @@
|
|
super(ModelView, cls).__setup__()
|
|
cls.__rpc__['fields_view_get'] = RPC(cache=dict(days=1))
|
|
cls.__rpc__['view_toolbar_get'] = RPC(cache=dict(days=1))
|
|
- cls.__rpc__['on_change'] = RPC(instantiate=0)
|
|
- cls.__rpc__['on_change_with'] = RPC(instantiate=0)
|
|
+ cls.__rpc__['on_change'] = RPC(instantiate=0, result=on_change_result)
|
|
+ cls.__rpc__['on_change_with'] = RPC(
|
|
+ instantiate=0, result=on_change_result)
|
|
cls.__rpc__['on_change_notify'] = RPC(instantiate=0)
|
|
cls._buttons = {}
|
|
|
|
@@ -758,8 +759,16 @@
|
|
return func
|
|
return decorator
|
|
|
|
+ @on_change
|
|
+ def on_change_with(self, fieldnames):
|
|
+ for fieldname in fieldnames:
|
|
+ method_name = 'on_change_with_%s' % fieldname
|
|
+ value = getattr(self, method_name)()
|
|
+ setattr(self, fieldname, value)
|
|
+
|
|
+ @on_change
|
|
def on_change(self, fieldnames):
|
|
for fieldname in sorted(fieldnames):
|
|
method = getattr(self, 'on_change_%s' % fieldname, None)
|
|
if method:
|
|
method()
|
|
@@ -761,34 +770,8 @@
|
|
def on_change(self, fieldnames):
|
|
for fieldname in sorted(fieldnames):
|
|
method = getattr(self, 'on_change_%s' % fieldname, None)
|
|
if method:
|
|
method()
|
|
- # XXX remove backward compatibility
|
|
- return [self._changed_values]
|
|
-
|
|
- def on_change_with(self, fieldnames):
|
|
- from .modelstorage import ModelStorage
|
|
- changes = {}
|
|
- for fieldname in fieldnames:
|
|
- field = self._fields[fieldname]
|
|
- method_name = 'on_change_with_%s' % fieldname
|
|
- value = getattr(self, method_name)()
|
|
- setattr(self, fieldname, value)
|
|
- if field._type in {'many2one', 'one2one', 'reference'}:
|
|
- if isinstance(value, ModelStorage):
|
|
- if value.id and value.id >= 0:
|
|
- changes[f'%{fieldname}.'] = {
|
|
- 'rec_name': value.rec_name,
|
|
- }
|
|
- if field._type == 'reference':
|
|
- value = str(value)
|
|
- else:
|
|
- value = value.id
|
|
- elif field._type in {'one2many', 'many2many'}:
|
|
- if isinstance(value, (list, tuple)):
|
|
- value = [int(r) for r in value]
|
|
- changes[fieldname] = value
|
|
- return changes
|
|
|
|
def on_change_notify(self):
|
|
"""Return a list of type and message couples.
|
|
@@ -802,8 +785,7 @@
|
|
By default, the value of a field is its internal representation except:
|
|
- for Many2One and One2One field: the id.
|
|
- for Reference field: the string model,id
|
|
- - for Many2Many: the list of ids
|
|
- - for One2Many: a dictionary composed of three keys:
|
|
+ - for One2Many and Many2Many: a dictionary composed of three keys:
|
|
- add: a list of tuple, the first element is the index where
|
|
the new line is added, the second element is
|
|
`_default_values`
|
|
@@ -822,7 +804,7 @@
|
|
# Always test key presence in case value is None
|
|
if (fname in init_values
|
|
and value == init_values[fname]
|
|
- and field._type != 'one2many'):
|
|
+ and field._type not in {'one2many', 'many2many'}):
|
|
continue
|
|
if field._type in ('many2one', 'one2one', 'reference'):
|
|
if value:
|
|
diff --git a/trytond/trytond/protocols/dispatcher.py b/trytond/trytond/protocols/dispatcher.py
|
|
index bfad1dc7382cb9fd2c17bef77da699ed36869d3e_dHJ5dG9uZC90cnl0b25kL3Byb3RvY29scy9kaXNwYXRjaGVyLnB5..bf7072adc76db4e62ec3fbde7efde17e7bbd630d_dHJ5dG9uZC90cnl0b25kL3Byb3RvY29scy9kaXNwYXRjaGVyLnB5 100644
|
|
--- a/trytond/trytond/protocols/dispatcher.py
|
|
+++ b/trytond/trytond/protocols/dispatcher.py
|
|
@@ -190,7 +190,7 @@
|
|
c_args, c_kwargs, transaction.context, transaction.timestamp \
|
|
= rpc.convert(obj, *args, **kwargs)
|
|
transaction.context['_request'] = request.context
|
|
- meth = getattr(obj, method)
|
|
+ meth = rpc.decorate(getattr(obj, method))
|
|
if (rpc.instantiate is None
|
|
or not is_instance_method(obj, method)):
|
|
result = rpc.result(meth(*c_args, **c_kwargs))
|
|
diff --git a/trytond/trytond/rpc.py b/trytond/trytond/rpc.py
|
|
index bfad1dc7382cb9fd2c17bef77da699ed36869d3e_dHJ5dG9uZC90cnl0b25kL3JwYy5weQ==..bf7072adc76db4e62ec3fbde7efde17e7bbd630d_dHJ5dG9uZC90cnl0b25kL3JwYy5weQ== 100644
|
|
--- a/trytond/trytond/rpc.py
|
|
+++ b/trytond/trytond/rpc.py
|
|
@@ -12,9 +12,10 @@
|
|
|
|
readonly: The transaction mode
|
|
instantiate: The position or the slice of the arguments to be instanciated
|
|
+ decorator: A function to decorate the procedure with
|
|
result: The function to transform the result
|
|
check_access: If access right must be checked
|
|
fresh_session: If a fresh session is required
|
|
unique: Check instances are unique
|
|
'''
|
|
|
|
@@ -15,9 +16,10 @@
|
|
result: The function to transform the result
|
|
check_access: If access right must be checked
|
|
fresh_session: If a fresh session is required
|
|
unique: Check instances are unique
|
|
'''
|
|
|
|
- __slots__ = ('readonly', 'instantiate', 'result', 'check_access',
|
|
- 'fresh_session', 'unique', 'cache')
|
|
+ __slots__ = (
|
|
+ 'readonly', 'instantiate', 'decorator', 'result',
|
|
+ 'check_access', 'fresh_session', 'unique', 'cache')
|
|
|
|
@@ -23,5 +25,6 @@
|
|
|
|
- def __init__(self, readonly=True, instantiate=None, result=None,
|
|
+ def __init__(
|
|
+ self, readonly=True, instantiate=None, decorator=None, result=None,
|
|
check_access=True, fresh_session=False, unique=True, cache=None):
|
|
self.readonly = readonly
|
|
self.instantiate = instantiate
|
|
@@ -25,6 +28,7 @@
|
|
check_access=True, fresh_session=False, unique=True, cache=None):
|
|
self.readonly = readonly
|
|
self.instantiate = instantiate
|
|
+ self.decorator = decorator
|
|
if result is None:
|
|
def result(r):
|
|
return r
|
|
@@ -83,6 +87,11 @@
|
|
context['_check_access'] = True
|
|
return args, kwargs, context, timestamp
|
|
|
|
+ def decorate(self, func):
|
|
+ if self.decorator:
|
|
+ func = self.decorator(func)
|
|
+ return func
|
|
+
|
|
|
|
class RPCCache:
|
|
__slots__ = ('duration',)
|
|
diff --git a/trytond/trytond/tests/test_field_function.py b/trytond/trytond/tests/test_field_function.py
|
|
index bfad1dc7382cb9fd2c17bef77da699ed36869d3e_dHJ5dG9uZC90cnl0b25kL3Rlc3RzL3Rlc3RfZmllbGRfZnVuY3Rpb24ucHk=..bf7072adc76db4e62ec3fbde7efde17e7bbd630d_dHJ5dG9uZC90cnl0b25kL3Rlc3RzL3Rlc3RfZmllbGRfZnVuY3Rpb24ucHk= 100644
|
|
--- a/trytond/trytond/tests/test_field_function.py
|
|
+++ b/trytond/trytond/tests/test_field_function.py
|
|
@@ -103,7 +103,7 @@
|
|
|
|
record = Model()
|
|
record.save()
|
|
- with patch.object(Model, 'get_function1') as getter:
|
|
+ with patch.object(Model, 'get_function1', autospec=True) as getter:
|
|
getter.return_value = 'test'
|
|
|
|
Model.read([record.id], ['function1', 'function2'])
|