Skip to content
GitLab
Menu
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
Tryton
Tryton
Commits
e3aa1151f4e8
Commit
e3aa1151
authored
Dec 27, 2022
by
Cédric Krier
Browse files
Use `&` and `|` as boolean operator in search widget
Closes
#11962
,
#8714
parent
802cca4584ee
Pipeline
#61577
passed with stages
in 20 minutes and 22 seconds
Changes
7
Pipelines
2
Hide whitespace changes
Inline
Side-by-side
sao/CHANGELOG
View file @
e3aa1151
* Use ``&`` and ``|`` as boolean operator in search widget
* Use toggle button and label on translate dialog
* Support border type of images
* Manage domain on id in single value list as unique
...
...
sao/src/common.js
View file @
e3aa1151
...
...
@@ -1306,7 +1306,7 @@
var
nary
=
'
'
;
if
((
domain
[
0
]
==
'
AND
'
)
||
(
domain
[
0
]
==
'
OR
'
))
{
if
(
domain
[
0
]
==
'
OR
'
)
{
nary
=
'
or
'
;
nary
=
'
|
'
;
}
domain
=
domain
.
slice
(
1
);
}
...
...
@@ -1729,5 +1729,6 @@
var
result
=
[];
operator
=
operator
||
'
or
'
;
tokens
=
jQuery
.
extend
([],
tokens
);
var
notation
=
{
'
or
'
:
'
|
'
,
'
and
'
:
'
&
'
}[
operator
];
var
test
=
function
(
value
)
{
if
(
value
instanceof
Array
)
{
...
...
@@ -1732,4 +1733,4 @@
var
test
=
function
(
value
)
{
if
(
value
instanceof
Array
)
{
return
Sao
.
common
.
compare
(
value
,
[
operator
]);
return
Sao
.
common
.
compare
(
value
,
[
notation
]);
}
else
{
...
...
@@ -1735,5 +1736,5 @@
}
else
{
return
value
==
operator
;
return
value
==
notation
;
}
};
var
cur
=
tokens
.
shift
();
...
...
sao/tests/sao.js
View file @
e3aa1151
...
...
@@ -1578,9 +1578,9 @@
[
[[
'
a
'
],
[
'
a
'
]],
[[
'
a
'
,
'
or
'
,
'
b
'
],
[[
'
OR
'
,
'
a
'
,
'
b
'
]]],
[[
'
a
'
,
'
or
'
,
'
b
'
,
'
or
'
,
'
c
'
],
[[
'
OR
'
,
[
'
OR
'
,
'
a
'
,
'
b
'
],
'
c
'
]]],
[[
'
a
'
,
'
b
'
,
'
or
'
,
'
c
'
],
[
'
a
'
,
[
'
OR
'
,
'
b
'
,
'
c
'
]]],
[[
'
a
'
,
'
or
'
,
'
b
'
,
'
c
'
],
[[
'
OR
'
,
'
a
'
,
'
b
'
],
'
c
'
]],
[[
'
a
'
,
'
|
'
,
'
b
'
],
[[
'
OR
'
,
'
a
'
,
'
b
'
]]],
[[
'
a
'
,
'
|
'
,
'
b
'
,
'
|
'
,
'
c
'
],
[[
'
OR
'
,
[
'
OR
'
,
'
a
'
,
'
b
'
],
'
c
'
]]],
[[
'
a
'
,
'
b
'
,
'
|
'
,
'
c
'
],
[
'
a
'
,
[
'
OR
'
,
'
b
'
,
'
c
'
]]],
[[
'
a
'
,
'
|
'
,
'
b
'
,
'
c
'
],
[[
'
OR
'
,
'
a
'
,
'
b
'
],
'
c
'
]],
[[
'
a
'
,
[
'
b
'
,
'
c
'
]],
[
'
a
'
,
[
'
b
'
,
'
c
'
]]],
[[
'
a
'
,
[
'
b
'
,
'
c
'
],
'
d
'
],
[
'
a
'
,
[
'
b
'
,
'
c
'
],
'
d
'
]],
...
...
@@ -1585,5 +1585,5 @@
[[
'
a
'
,
[
'
b
'
,
'
c
'
]],
[
'
a
'
,
[
'
b
'
,
'
c
'
]]],
[[
'
a
'
,
[
'
b
'
,
'
c
'
],
'
d
'
],
[
'
a
'
,
[
'
b
'
,
'
c
'
],
'
d
'
]],
[[
'
a
'
,
'
or
'
,
[
'
b
'
,
'
c
'
]],
[[
'
OR
'
,
'
a
'
,
[
'
b
'
,
'
c
'
]]]],
[[
'
a
'
,
'
or
'
,
[
'
b
'
,
'
c
'
],
'
d
'
],
[[
'
a
'
,
'
|
'
,
[
'
b
'
,
'
c
'
]],
[[
'
OR
'
,
'
a
'
,
[
'
b
'
,
'
c
'
]]]],
[[
'
a
'
,
'
|
'
,
[
'
b
'
,
'
c
'
],
'
d
'
],
[[
'
OR
'
,
'
a
'
,
[
'
b
'
,
'
c
'
]],
'
d
'
]],
...
...
@@ -1589,3 +1589,3 @@
[[
'
OR
'
,
'
a
'
,
[
'
b
'
,
'
c
'
]],
'
d
'
]],
[[
'
a
'
,
[
'
b
'
,
'
c
'
],
'
or
'
,
'
d
'
],
[[
'
a
'
,
[
'
b
'
,
'
c
'
],
'
|
'
,
'
d
'
],
[
'
a
'
,
[
'
OR
'
,
[
'
b
'
,
'
c
'
],
'
d
'
]]],
...
...
@@ -1591,3 +1591,3 @@
[
'
a
'
,
[
'
OR
'
,
[
'
b
'
,
'
c
'
],
'
d
'
]]],
[[
'
a
'
,
'
or
'
,
[
'
b
'
,
'
or
'
,
'
c
'
]],
[[
'
a
'
,
'
|
'
,
[
'
b
'
,
'
|
'
,
'
c
'
]],
[[
'
OR
'
,
'
a
'
,
[[
'
OR
'
,
'
b
'
,
'
c
'
]]]]],
...
...
@@ -1593,12 +1593,12 @@
[[
'
OR
'
,
'
a
'
,
[[
'
OR
'
,
'
b
'
,
'
c
'
]]]]],
[[
'
or
'
],
[]],
[[
'
or
'
,
'
a
'
],
[
'
a
'
]],
[[
'
a
'
,
[
'
or
'
,
'
b
'
]],
[
'
a
'
,
[
'
b
'
]]],
[[
'
a
'
,
'
or
'
,
'
or
'
,
'
b
'
],
[[
'
OR
'
,
'
a
'
,
'
b
'
]]],
[[
'
or
'
,
'
or
'
,
'
a
'
],
[
'
a
'
]],
[[
'
or
'
,
'
or
'
,
'
a
'
,
'
b
'
],
[
'
a
'
,
'
b
'
]],
[[
'
or
'
,
'
or
'
,
'
a
'
,
'
or
'
,
'
b
'
],
[[
'
OR
'
,
'
a
'
,
'
b
'
]]],
[[
'
a
'
,
[
'
b
'
,
'
or
'
,
'
c
'
]],
[
'
a
'
,
[[
'
OR
'
,
'
b
'
,
'
c
'
]]]],
[[
a
,
[
b
,
[
'
or
'
],
c
]],
[
a
,
[[
'
OR
'
,
b
,
c
]]]],
[[
'
a
'
,
[
'
b
'
,
'
or
'
]],
[
'
a
'
,
[[
'
OR
'
,
'
b
'
]]]],
[[
'
|
'
],
[]],
[[
'
|
'
,
'
a
'
],
[
'
a
'
]],
[[
'
a
'
,
[
'
|
'
,
'
b
'
]],
[
'
a
'
,
[
'
b
'
]]],
[[
'
a
'
,
'
|
'
,
'
|
'
,
'
b
'
],
[[
'
OR
'
,
'
a
'
,
'
b
'
]]],
[[
'
|
'
,
'
|
'
,
'
a
'
],
[
'
a
'
]],
[[
'
|
'
,
'
|
'
,
'
a
'
,
'
b
'
],
[
'
a
'
,
'
b
'
]],
[[
'
|
'
,
'
|
'
,
'
a
'
,
'
|
'
,
'
b
'
],
[[
'
OR
'
,
'
a
'
,
'
b
'
]]],
[[
'
a
'
,
[
'
b
'
,
'
|
'
,
'
c
'
]],
[
'
a
'
,
[[
'
OR
'
,
'
b
'
,
'
c
'
]]]],
[[
a
,
[
b
,
[
'
|
'
],
c
]],
[
a
,
[[
'
OR
'
,
b
,
c
]]]],
[[
'
a
'
,
[
'
b
'
,
'
|
'
]],
[
'
a
'
,
[[
'
OR
'
,
'
b
'
]]]],
[[
null_
],
[
null_
]],
...
...
@@ -1604,5 +1604,5 @@
[[
null_
],
[
null_
]],
[[
null_
,
'
or
'
,
double_null_
],
[[
'
OR
'
,
null_
,
double_null_
]]]
[[
null_
,
'
|
'
,
double_null_
],
[[
'
OR
'
,
null_
,
double_null_
]]]
].
forEach
(
function
(
test
)
{
var
value
=
test
[
0
];
var
result
=
test
[
1
];
...
...
@@ -2233,10 +2233,10 @@
[[
'
OR
'
,
[
'
name
'
,
'
ilike
'
,
'
%Doe%
'
],
[
'
name
'
,
'
ilike
'
,
'
%Jane%
'
]
],
'
Name: Doe
or
Name: Jane
'
],
],
'
Name: Doe
|
Name: Jane
'
],
[[
[
'
name
'
,
'
ilike
'
,
'
%Doe%
'
],
[
'
OR
'
,
[
'
name
'
,
'
ilike
'
,
'
%John%
'
],
[
'
name
'
,
'
ilike
'
,
'
%Jane%
'
]
]
...
...
@@ -2237,10 +2237,10 @@
[[
[
'
name
'
,
'
ilike
'
,
'
%Doe%
'
],
[
'
OR
'
,
[
'
name
'
,
'
ilike
'
,
'
%John%
'
],
[
'
name
'
,
'
ilike
'
,
'
%Jane%
'
]
]
],
'
Name: Doe (Name: John
or
Name: Jane)
'
],
],
'
Name: Doe (Name: John
|
Name: Jane)
'
],
[[],
''
],
[[[
'
surname
'
,
'
ilike
'
,
'
%Doe%
'
]],
'
"(Sur)Name": Doe
'
],
//[[['date', '>=', new Date(2012, 10, 24)]], 'Date: >=10/24/2012'],
...
...
@@ -3112,8 +3112,8 @@
[
'
Name: !=foo
'
,
[]],
[
''
,
[
"
Name:
"
]],
[
'
'
,
[
""
,
"
Name:
"
]],
[
"
Name: foo
or
"
,
[
"
Name: foo
"
]],
[
'
Name: foo (Name: foo
or
N
'
,
[
"
Name: foo (Name: foo
or
Name:
"
]],
[
"
Name: foo
|
"
,
[
"
Name: foo
"
]],
[
'
Name: foo (Name: foo
|
N
'
,
[
"
Name: foo (Name: foo
|
Name:
"
]],
].
forEach
(
function
(
test
)
{
var
value
=
test
[
0
];
var
expected
=
test
[
1
];
...
...
tryton/CHANGELOG
View file @
e3aa1151
* Use ``&`` and ``|`` as boolean operator in search widget
* Use toggle button and label on translate dialog
* Manage domain on id in single value list as unique
* Do not validate domain and enforce uniqueness for empty fields unless they're required
...
...
tryton/doc/usage.rst
View file @
e3aa1151
...
...
@@ -512,9 +512,10 @@
Clause composition
++++++++++++++++++
The clauses can be composed using the two boolean operators ``and`` and ``or``.
By default, there is an implicit ``and`` between each clause if no operator is
The clauses can be composed using the two boolean operators ``&`` (for `logical
conjunction`_) and ``|`` (for `logical disjunction`_).
By default, there is an implicit ``&`` between each clause if no operator is
specified.
For example: ``Name: Michael Amount: 100``
...
...
@@ -517,6 +518,6 @@
specified.
For example: ``Name: Michael Amount: 100``
is the same as ``Name: Michael
and
Amount: 100``
is the same as ``Name: Michael
&
Amount: 100``
...
...
@@ -522,4 +523,6 @@
The ``and`` operator has a highest precedence than ``or`` but you can change it
by using parenthesis.
The ``&`` operator has a highest precedence than ``|`` but you can change it by
using parenthesis.
For example: ``(Name: Michael | Name: Pam) & Amount: 100``
...
...
@@ -525,3 +528,3 @@
For example:
``
(
Name: Michael
or
Name: Pam
) and
Amount: 100``
is different than
``Name: Michael
|
Name: Pam
&
Amount: 100``
...
...
@@ -527,3 +530,3 @@
is different than
``Name: Michael
or
Name: Pam
and
Amount: 100``
which is evaluated as
``Name: Michael
| (
Name: Pam
&
Amount: 100
)
``
...
...
@@ -529,5 +532,6 @@
which is evaluated as ``Name: Michael or (Name: Pam and Amount: 100)``
.. _logical conjunction: https://en.wikipedia.org/wiki/Logical_conjunction
.. _logical disjunction: https://en.wikipedia.org/wiki/Logical_disjunction
RichText Editor
^^^^^^^^^^^^^^^
...
...
tryton/tryton/common/domain_parser.py
View file @
e3aa1151
...
...
@@ -484,7 +484,10 @@
def
operatorize
(
tokens
,
operator
=
'or'
):
"Convert operators"
test
=
(
operator
,
(
operator
,))
test
=
{
'or'
:
(
'|'
,
(
'|'
,)),
'and'
:
(
'&'
,
(
'&'
,)),
}[
operator
]
try
:
cur
=
next
(
tokens
)
while
cur
in
test
:
...
...
@@ -656,7 +659,7 @@
if
not
domain
:
return
''
if
domain
[
0
]
in
(
'AND'
,
'OR'
):
nary
=
' '
if
domain
[
0
]
==
'AND'
else
'
or
'
nary
=
' '
if
domain
[
0
]
==
'AND'
else
'
|
'
domain
=
domain
[
1
:]
else
:
nary
=
' '
...
...
tryton/tryton/tests/test_common_domain_parser.py
View file @
e3aa1151
...
...
@@ -529,9 +529,9 @@
double_null_
=
(
'e'
,
None
,
None
)
for
value
,
result
in
(
([
'a'
],
[
'a'
]),
([
'a'
,
'
or
'
,
'b'
],
[[
'OR'
,
'a'
,
'b'
]]),
([
'a'
,
'
or
'
,
'b'
,
'
or
'
,
'c'
],
[[
'OR'
,
[
'OR'
,
'a'
,
'b'
],
'c'
]]),
([
'a'
,
'b'
,
'
or
'
,
'c'
],
[
'a'
,
[
'OR'
,
'b'
,
'c'
]]),
([
'a'
,
'
or
'
,
'b'
,
'c'
],
[[
'OR'
,
'a'
,
'b'
],
'c'
]),
([
'a'
,
'
|
'
,
'b'
],
[[
'OR'
,
'a'
,
'b'
]]),
([
'a'
,
'
|
'
,
'b'
,
'
|
'
,
'c'
],
[[
'OR'
,
[
'OR'
,
'a'
,
'b'
],
'c'
]]),
([
'a'
,
'b'
,
'
|
'
,
'c'
],
[
'a'
,
[
'OR'
,
'b'
,
'c'
]]),
([
'a'
,
'
|
'
,
'b'
,
'c'
],
[[
'OR'
,
'a'
,
'b'
],
'c'
]),
([
'a'
,
iter
([
'b'
,
'c'
])],
[
'a'
,
[
'b'
,
'c'
]]),
([
'a'
,
iter
([
'b'
,
'c'
]),
'd'
],
[
'a'
,
[
'b'
,
'c'
],
'd'
]),
...
...
@@ -536,5 +536,5 @@
([
'a'
,
iter
([
'b'
,
'c'
])],
[
'a'
,
[
'b'
,
'c'
]]),
([
'a'
,
iter
([
'b'
,
'c'
]),
'd'
],
[
'a'
,
[
'b'
,
'c'
],
'd'
]),
([
'a'
,
'
or
'
,
iter
([
'b'
,
'c'
])],
[[
'OR'
,
'a'
,
[
'b'
,
'c'
]]]),
([
'a'
,
'
or
'
,
iter
([
'b'
,
'c'
]),
'd'
],
([
'a'
,
'
|
'
,
iter
([
'b'
,
'c'
])],
[[
'OR'
,
'a'
,
[
'b'
,
'c'
]]]),
([
'a'
,
'
|
'
,
iter
([
'b'
,
'c'
]),
'd'
],
[[
'OR'
,
'a'
,
[
'b'
,
'c'
]],
'd'
]),
...
...
@@ -540,3 +540,3 @@
[[
'OR'
,
'a'
,
[
'b'
,
'c'
]],
'd'
]),
([
'a'
,
iter
([
'b'
,
'c'
]),
'
or
'
,
'd'
],
([
'a'
,
iter
([
'b'
,
'c'
]),
'
|
'
,
'd'
],
[
'a'
,
[
'OR'
,
[
'b'
,
'c'
],
'd'
]]),
...
...
@@ -542,3 +542,3 @@
[
'a'
,
[
'OR'
,
[
'b'
,
'c'
],
'd'
]]),
([
'a'
,
'
or
'
,
iter
([
'b'
,
'
or
'
,
'c'
])],
([
'a'
,
'
|
'
,
iter
([
'b'
,
'
|
'
,
'c'
])],
[[
'OR'
,
'a'
,
[[
'OR'
,
'b'
,
'c'
]]]]),
...
...
@@ -544,12 +544,12 @@
[[
'OR'
,
'a'
,
[[
'OR'
,
'b'
,
'c'
]]]]),
([
'
or
'
],
[]),
([
'
or
'
,
'a'
],
[
'a'
]),
([
'a'
,
iter
([
'
or
'
,
'b'
])],
[
'a'
,
[
'b'
]]),
([
'a'
,
'
or
'
,
'
or
'
,
'b'
],
[[
'OR'
,
'a'
,
'b'
]]),
([
'
or
'
,
'
or
'
,
'a'
],
[
'a'
]),
([
'
or
'
,
'
or
'
,
'a'
,
'b'
],
[
'a'
,
'b'
]),
([
'
or
'
,
'
or
'
,
'a'
,
'
or
'
,
'b'
],
[[
'OR'
,
'a'
,
'b'
]]),
([
'a'
,
iter
([
'b'
,
'
or
'
,
'c'
])],
[
'a'
,
[[
'OR'
,
'b'
,
'c'
]]]),
([
a
,
iter
([
b
,
(
'
or
'
,),
c
])],
[
a
,
[[
'OR'
,
b
,
c
]]]),
([
'a'
,
iter
([
'b'
,
'
or
'
])],
[
'a'
,
[[
'OR'
,
'b'
]]]),
([
'
|
'
],
[]),
([
'
|
'
,
'a'
],
[
'a'
]),
([
'a'
,
iter
([
'
|
'
,
'b'
])],
[
'a'
,
[
'b'
]]),
([
'a'
,
'
|
'
,
'
|
'
,
'b'
],
[[
'OR'
,
'a'
,
'b'
]]),
([
'
|
'
,
'
|
'
,
'a'
],
[
'a'
]),
([
'
|
'
,
'
|
'
,
'a'
,
'b'
],
[
'a'
,
'b'
]),
([
'
|
'
,
'
|
'
,
'a'
,
'
|
'
,
'b'
],
[[
'OR'
,
'a'
,
'b'
]]),
([
'a'
,
iter
([
'b'
,
'
|
'
,
'c'
])],
[
'a'
,
[[
'OR'
,
'b'
,
'c'
]]]),
([
a
,
iter
([
b
,
(
'
|
'
,),
c
])],
[
a
,
[[
'OR'
,
b
,
c
]]]),
([
'a'
,
iter
([
'b'
,
'
|
'
])],
[
'a'
,
[[
'OR'
,
'b'
]]]),
([
null_
],
[
null_
]),
...
...
@@ -555,5 +555,5 @@
([
null_
],
[
null_
]),
([
null_
,
'
or
'
,
double_null_
],
[[
'OR'
,
null_
,
double_null_
]]),
([
null_
,
'
|
'
,
double_null_
],
[[
'OR'
,
null_
,
double_null_
]]),
):
self
.
assertEqual
(
rlist
(
operatorize
(
iter
(
value
))),
result
,
...
...
@@ -696,10 +696,10 @@
dom
.
string
([
'OR'
,
(
'name'
,
'ilike'
,
'%Doe%'
),
(
'name'
,
'ilike'
,
'%Jane%'
)]),
'Name: Doe
or
Name: Jane'
)
'Name: Doe
|
Name: Jane'
)
self
.
assertEqual
(
dom
.
string
([
(
'name'
,
'ilike'
,
'%Doe%'
),
[
'OR'
,
(
'name'
,
'ilike'
,
'%John%'
),
(
'name'
,
'ilike'
,
'%Jane%'
)]]),
...
...
@@ -700,10 +700,10 @@
self
.
assertEqual
(
dom
.
string
([
(
'name'
,
'ilike'
,
'%Doe%'
),
[
'OR'
,
(
'name'
,
'ilike'
,
'%John%'
),
(
'name'
,
'ilike'
,
'%Jane%'
)]]),
'Name: Doe (Name: John
or
Name: Jane)'
)
'Name: Doe (Name: John
|
Name: Jane)'
)
self
.
assertEqual
(
dom
.
string
([]),
''
)
self
.
assertEqual
(
dom
.
string
([(
'surname'
,
'ilike'
,
'%Doe%'
)]),
'"(Sur)Name": Doe'
)
...
...
@@ -1121,5 +1121,5 @@
self
.
assertEqual
(
list
(
dom
.
completion
(
'Name: !=foo'
)),
[])
self
.
assertEqual
(
list
(
dom
.
completion
(
''
)),
[
'Name: '
])
self
.
assertEqual
(
list
(
dom
.
completion
(
' '
)),
[
''
,
'Name: '
])
self
.
assertEqual
(
list
(
dom
.
completion
(
'Name: foo
or
'
)),
[
'Name: foo'
])
self
.
assertEqual
(
list
(
dom
.
completion
(
'Name: foo
|
'
)),
[
'Name: foo'
])
self
.
assertEqual
(
...
...
@@ -1125,6 +1125,6 @@
self
.
assertEqual
(
list
(
dom
.
completion
(
'Name: foo (Name: foo
or
N'
)),
[
'Name: foo (Name: foo
or
Name: '
])
list
(
dom
.
completion
(
'Name: foo (Name: foo
|
N'
)),
[
'Name: foo (Name: foo
|
Name: '
])
def
test_completion_many2one
(
self
):
"Test completion many2one"
...
...
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment