tryton-sao 8.0.2 → 8.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG CHANGED
@@ -1,4 +1,14 @@
1
1
 
2
+ Version 8.0.4 - 2026-06-02
3
+ --------------------------
4
+ * Bug fixes (see mercurial logs for details)
5
+
6
+
7
+ Version 8.0.3 - 2026-05-20
8
+ --------------------------
9
+ * Bug fixes (see mercurial logs for details)
10
+
11
+
2
12
  Version 8.0.2 - 2026-05-02
3
13
  --------------------------
4
14
  * Bug fixes (see mercurial logs for details)
@@ -10077,6 +10077,10 @@ img.icon {
10077
10077
  height: 1.42857143em;
10078
10078
  display: block;
10079
10079
  }
10080
+ [dir="rtl"] .treeview > table.tree img.column-affix,
10081
+ [dir="rtl"] .treeview > table.tree a.column-affix > img {
10082
+ transform: scaleX(-1);
10083
+ }
10080
10084
  .treeview > table.tree tr.more-row {
10081
10085
  text-align: center;
10082
10086
  }
@@ -10196,6 +10200,7 @@ img.icon {
10196
10200
  }
10197
10201
  [dir="rtl"] .treeview .expander > img {
10198
10202
  float: right;
10203
+ transform: scaleX(-1);
10199
10204
  }
10200
10205
  .treeview label {
10201
10206
  font-weight: normal;
@@ -3,7 +3,7 @@
3
3
 
4
4
  /* eslint-disable no-redeclare */
5
5
  var Sao = {
6
- __version__: '8.0.2',
6
+ __version__: '8.0.4',
7
7
  };
8
8
  /* eslint-enable no-redeclare */
9
9
 
@@ -1490,11 +1490,6 @@ var Sao = {
1490
1490
  };
1491
1491
 
1492
1492
  var ajax_error = function(query, status_, error) {
1493
- if (!process_exception) {
1494
- console.debug(`RPC error calling ${args}: ${status_}: ${error}.`);
1495
- dfd.reject();
1496
- return;
1497
- }
1498
1493
  if (query.status == 503) {
1499
1494
  this.retries++;
1500
1495
  if (this.retries < 5) {
@@ -1520,8 +1515,7 @@ var Sao = {
1520
1515
  }
1521
1516
  return;
1522
1517
  }
1523
- }
1524
- if (query.status == 401) {
1518
+ } else if (query.status == 401) {
1525
1519
  //Try to relog
1526
1520
  Sao.Session.renew(session).then(function() {
1527
1521
  if (async) {
@@ -1531,6 +1525,10 @@ var Sao = {
1531
1525
  dfd.resolve();
1532
1526
  }
1533
1527
  }, dfd.reject);
1528
+ } else if (!process_exception) {
1529
+ console.debug(`RPC error calling ${args}: ${status_}: ${error}.`);
1530
+ dfd.reject();
1531
+ return;
1534
1532
  } else {
1535
1533
  var err_msg = `[${query.status}] ${error}`;
1536
1534
  Sao.common.message.run(
@@ -2761,7 +2759,7 @@ var Sao = {
2761
2759
  'contentType': 'application/json',
2762
2760
  'data': JSON.stringify(args),
2763
2761
  'dataType': 'json',
2764
- 'url': '/' + this.database + '/',
2762
+ 'url': (this.database || '.') + '/rpc/#' + args.method,
2765
2763
  'type': 'post',
2766
2764
  });
2767
2765
  },
@@ -2859,15 +2857,16 @@ var Sao = {
2859
2857
 
2860
2858
  Sao.Session.server_version = function() {
2861
2859
  var timeoutID = Sao.common.processing.show();
2862
- return jQuery.ajax({
2863
- 'contentType': 'application/json',
2864
- 'data': JSON.stringify({
2860
+ let args = {
2865
2861
  'id': 0,
2866
2862
  'method': 'common.server.version',
2867
2863
  'params': []
2868
- }),
2864
+ };
2865
+ return jQuery.ajax({
2866
+ 'contentType': 'application/json',
2867
+ 'data': JSON.stringify(args),
2869
2868
  'dataType': 'json',
2870
- 'url': 'rpc/',
2869
+ 'url': 'rpc/#' + args.method,
2871
2870
  'type': 'post',
2872
2871
  'complete': [function() {
2873
2872
  Sao.common.processing.hide(timeoutID);
@@ -3272,15 +3271,16 @@ var Sao = {
3272
3271
 
3273
3272
  Sao.DB.list = function() {
3274
3273
  var timeoutID = Sao.common.processing.show();
3275
- return jQuery.ajax({
3276
- 'contentType': 'application/json',
3277
- 'data': JSON.stringify({
3274
+ let args = {
3278
3275
  'id': 0,
3279
3276
  'method': 'common.db.list',
3280
3277
  'params': []
3281
- }),
3278
+ };
3279
+ return jQuery.ajax({
3280
+ 'contentType': 'application/json',
3281
+ 'data': JSON.stringify(args),
3282
3282
  'dataType': 'json',
3283
- 'url': 'rpc/',
3283
+ 'url': 'rpc/#' + args.method,
3284
3284
  'type': 'post',
3285
3285
  'complete': [function() {
3286
3286
  Sao.common.processing.hide(timeoutID);
@@ -3294,15 +3294,16 @@ var Sao = {
3294
3294
 
3295
3295
  Sao.Authentication.services = function() {
3296
3296
  var timeoutID = Sao.common.processing.show();
3297
- return jQuery.ajax({
3298
- 'contentType': 'application/json',
3299
- 'data': JSON.stringify({
3297
+ let args = {
3300
3298
  'id': 0,
3301
3299
  'method': 'common.authentication.services',
3302
3300
  'params': []
3303
- }),
3301
+ }
3302
+ return jQuery.ajax({
3303
+ 'contentType': 'application/json',
3304
+ 'data': JSON.stringify(args),
3304
3305
  'dataType': 'json',
3305
- 'url': 'rpc/',
3306
+ 'url': 'rpc/#' + args.method,
3306
3307
  'type': 'post',
3307
3308
  'complete': [function() {
3308
3309
  Sao.common.processing.hide(timeoutID);
@@ -4962,7 +4963,9 @@ var Sao = {
4962
4963
  };
4963
4964
 
4964
4965
  var complete_datetime = function() {
4965
- return [Sao.Date(), Sao.DateTime().utc()];
4966
+ return [Sao.Date(), Sao.DateTime(
4967
+ undefined, undefined, undefined,
4968
+ 0, 0, 0, 0, true)];
4966
4969
  };
4967
4970
 
4968
4971
  var complete_date = function() {
@@ -5269,6 +5272,32 @@ var Sao = {
5269
5272
  ]);
5270
5273
  return;
5271
5274
  }
5275
+ if ((typeof value == 'string') &&
5276
+ ['datetime', 'timestamp'].includes(field.type) &&
5277
+ (operator == '=')) {
5278
+ let ctx, format_, parsed_date;
5279
+ if (this.context && Object.keys(this.context).length) {
5280
+ ctx = this.context;
5281
+ } else {
5282
+ ctx = {};
5283
+ }
5284
+ format_ = Sao.common.date_format(ctx.date_format);
5285
+ parsed_date = Sao.common.parse_date(format_, value);
5286
+ if (parsed_date &&
5287
+ (Sao.common.format_date(format_, parsed_date) == value)) {
5288
+ let date = Sao.DateTime.combine(parsed_date, Sao.Time());
5289
+ let next_day = Sao.DateTime(
5290
+ date.year(), date.month(), date.date(),
5291
+ date.hour(), date.minute(), date.second(),
5292
+ date.millisecond())
5293
+ next_day.add(1, 'day');
5294
+ result.push(this._clausify([
5295
+ [field_name, '>=', date],
5296
+ [field_name, '<', next_day]
5297
+ ]));
5298
+ return;
5299
+ }
5300
+ }
5272
5301
  }
5273
5302
  if (['many2one', 'one2many', 'many2many', 'one2one',
5274
5303
  'many2many', 'one2one'].includes(field.type) && value) {
@@ -19155,9 +19184,8 @@ function eval_pyson(value){
19155
19184
  if (record) {
19156
19185
  var fields = this.screen.current_view.get_fields();
19157
19186
  if (!record.validate(fields)) {
19158
- this.screen.display(true);
19159
- prm.reject();
19160
- return;
19187
+ this.screen.display(true).always(() => prm.reject());
19188
+ return prm;
19161
19189
  }
19162
19190
  if (this.screen.pre_validate) {
19163
19191
  return record.pre_validate().then(
@@ -23901,7 +23929,7 @@ function eval_pyson(value){
23901
23929
  fields, false, false)) {
23902
23930
  var value = cell.prop('checked');
23903
23931
  this.field.set_client(record, value);
23904
- if (record !== current_record) {
23932
+ if ((!this.group.parent) & (record !== current_record)) {
23905
23933
  // we can not rely on editable tree handler to save the row
23906
23934
  record.save();
23907
23935
  }
@@ -25452,6 +25480,7 @@ function eval_pyson(value){
25452
25480
  },
25453
25481
  button_clicked: function(event) {
25454
25482
  if (Sao.common.compare(this.screen.selected_records, [this.record])) {
25483
+ event.stopPropagation();
25455
25484
  Sao.View.ListGroupViewForm._super.button_clicked.call(this, event);
25456
25485
  }
25457
25486
  }
@@ -28826,6 +28855,7 @@ function eval_pyson(value){
28826
28855
  if (this.__processing || this.__waiting_response) {
28827
28856
  return jQuery.when();
28828
28857
  }
28858
+ this.__processing = true;
28829
28859
  var process = function() {
28830
28860
  if (this.state == this.end_state) {
28831
28861
  return this.end();
@@ -28884,8 +28914,8 @@ function eval_pyson(value){
28884
28914
  } else {
28885
28915
  prms.push(execute_actions());
28886
28916
  }
28887
- this.__processing = false;
28888
- return jQuery.when.apply(jQuery, prms);
28917
+ return jQuery.when.apply(jQuery, prms).then(
28918
+ () => this.__processing = false);
28889
28919
  }, result => {
28890
28920
  if (!result || !this.screen) {
28891
28921
  this.state = this.end_state;
@@ -29008,11 +29038,14 @@ function eval_pyson(value){
29008
29038
  this.footer.empty();
29009
29039
  },
29010
29040
  _get_button: function(definition) {
29041
+ let state = this.state;
29011
29042
  var button = Sao.Wizard.Form._super._get_button.call(this,
29012
29043
  definition);
29013
29044
  this.footer.append(button.el);
29014
29045
  button.el.click(() => {
29015
- this.response(definition);
29046
+ if (this.state === state) {
29047
+ this.response(definition);
29048
+ }
29016
29049
  });
29017
29050
  return button;
29018
29051
  },
@@ -29060,19 +29093,24 @@ function eval_pyson(value){
29060
29093
  this.footer.empty();
29061
29094
  },
29062
29095
  _get_button: function(definition) {
29096
+ let state = this.state;
29063
29097
  var button = Sao.Wizard.Dialog._super._get_button.call(this,
29064
29098
  definition);
29065
29099
  this.footer.append(button.el);
29066
29100
  if (definition['default']) {
29067
29101
  this.content.unbind('submit');
29068
29102
  this.content.submit(e => {
29069
- this.response(definition);
29070
29103
  e.preventDefault();
29104
+ if (this.state === state) {
29105
+ this.response(definition);
29106
+ }
29071
29107
  });
29072
29108
  button.el.attr('type', 'submit');
29073
29109
  } else {
29074
29110
  button.el.click(() => {
29075
- this.response(definition);
29111
+ if (this.state === state) {
29112
+ this.response(definition);
29113
+ }
29076
29114
  });
29077
29115
  }
29078
29116
  return button;
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "tryton-sao",
3
3
  "title": "sao",
4
4
  "description": "Tryton webclient",
5
- "version": "8.0.2",
5
+ "version": "8.0.4",
6
6
  "homepage": "https://www.tryton.org/",
7
7
  "author": {
8
8
  "name": "Tryton"
package/src/common.js CHANGED
@@ -1644,7 +1644,9 @@
1644
1644
  };
1645
1645
 
1646
1646
  var complete_datetime = function() {
1647
- return [Sao.Date(), Sao.DateTime().utc()];
1647
+ return [Sao.Date(), Sao.DateTime(
1648
+ undefined, undefined, undefined,
1649
+ 0, 0, 0, 0, true)];
1648
1650
  };
1649
1651
 
1650
1652
  var complete_date = function() {
@@ -1951,6 +1953,32 @@
1951
1953
  ]);
1952
1954
  return;
1953
1955
  }
1956
+ if ((typeof value == 'string') &&
1957
+ ['datetime', 'timestamp'].includes(field.type) &&
1958
+ (operator == '=')) {
1959
+ let ctx, format_, parsed_date;
1960
+ if (this.context && Object.keys(this.context).length) {
1961
+ ctx = this.context;
1962
+ } else {
1963
+ ctx = {};
1964
+ }
1965
+ format_ = Sao.common.date_format(ctx.date_format);
1966
+ parsed_date = Sao.common.parse_date(format_, value);
1967
+ if (parsed_date &&
1968
+ (Sao.common.format_date(format_, parsed_date) == value)) {
1969
+ let date = Sao.DateTime.combine(parsed_date, Sao.Time());
1970
+ let next_day = Sao.DateTime(
1971
+ date.year(), date.month(), date.date(),
1972
+ date.hour(), date.minute(), date.second(),
1973
+ date.millisecond())
1974
+ next_day.add(1, 'day');
1975
+ result.push(this._clausify([
1976
+ [field_name, '>=', date],
1977
+ [field_name, '<', next_day]
1978
+ ]));
1979
+ return;
1980
+ }
1981
+ }
1954
1982
  }
1955
1983
  if (['many2one', 'one2many', 'many2many', 'one2one',
1956
1984
  'many2many', 'one2one'].includes(field.type) && value) {
package/src/rpc.js CHANGED
@@ -119,11 +119,6 @@
119
119
  };
120
120
 
121
121
  var ajax_error = function(query, status_, error) {
122
- if (!process_exception) {
123
- console.debug(`RPC error calling ${args}: ${status_}: ${error}.`);
124
- dfd.reject();
125
- return;
126
- }
127
122
  if (query.status == 503) {
128
123
  this.retries++;
129
124
  if (this.retries < 5) {
@@ -149,8 +144,7 @@
149
144
  }
150
145
  return;
151
146
  }
152
- }
153
- if (query.status == 401) {
147
+ } else if (query.status == 401) {
154
148
  //Try to relog
155
149
  Sao.Session.renew(session).then(function() {
156
150
  if (async) {
@@ -160,6 +154,10 @@
160
154
  dfd.resolve();
161
155
  }
162
156
  }, dfd.reject);
157
+ } else if (!process_exception) {
158
+ console.debug(`RPC error calling ${args}: ${status_}: ${error}.`);
159
+ dfd.reject();
160
+ return;
163
161
  } else {
164
162
  var err_msg = `[${query.status}] ${error}`;
165
163
  Sao.common.message.run(
package/src/sao.js CHANGED
@@ -3,7 +3,7 @@
3
3
 
4
4
  /* eslint-disable no-redeclare */
5
5
  var Sao = {
6
- __version__: '8.0.2',
6
+ __version__: '8.0.4',
7
7
  };
8
8
  /* eslint-enable no-redeclare */
9
9
 
package/src/sao.less CHANGED
@@ -1025,6 +1025,10 @@ img.icon {
1025
1025
  width: unit(@line-height-base, em);
1026
1026
  height: unit(@line-height-base, em);
1027
1027
  display: block;
1028
+
1029
+ [dir="rtl"] & {
1030
+ transform: scaleX(-1);
1031
+ }
1028
1032
  }
1029
1033
 
1030
1034
  tr.more-row {
@@ -1118,6 +1122,7 @@ img.icon {
1118
1122
  float: left;
1119
1123
  [dir="rtl"] & {
1120
1124
  float: right;
1125
+ transform: scaleX(-1);
1121
1126
  }
1122
1127
  width: unit(@line-height-base, em);
1123
1128
  height: unit(@line-height-base, em);
package/src/session.js CHANGED
@@ -83,7 +83,7 @@
83
83
  'contentType': 'application/json',
84
84
  'data': JSON.stringify(args),
85
85
  'dataType': 'json',
86
- 'url': '/' + this.database + '/',
86
+ 'url': (this.database || '.') + '/rpc/#' + args.method,
87
87
  'type': 'post',
88
88
  });
89
89
  },
@@ -181,15 +181,16 @@
181
181
 
182
182
  Sao.Session.server_version = function() {
183
183
  var timeoutID = Sao.common.processing.show();
184
- return jQuery.ajax({
185
- 'contentType': 'application/json',
186
- 'data': JSON.stringify({
184
+ let args = {
187
185
  'id': 0,
188
186
  'method': 'common.server.version',
189
187
  'params': []
190
- }),
188
+ };
189
+ return jQuery.ajax({
190
+ 'contentType': 'application/json',
191
+ 'data': JSON.stringify(args),
191
192
  'dataType': 'json',
192
- 'url': 'rpc/',
193
+ 'url': 'rpc/#' + args.method,
193
194
  'type': 'post',
194
195
  'complete': [function() {
195
196
  Sao.common.processing.hide(timeoutID);
@@ -594,15 +595,16 @@
594
595
 
595
596
  Sao.DB.list = function() {
596
597
  var timeoutID = Sao.common.processing.show();
597
- return jQuery.ajax({
598
- 'contentType': 'application/json',
599
- 'data': JSON.stringify({
598
+ let args = {
600
599
  'id': 0,
601
600
  'method': 'common.db.list',
602
601
  'params': []
603
- }),
602
+ };
603
+ return jQuery.ajax({
604
+ 'contentType': 'application/json',
605
+ 'data': JSON.stringify(args),
604
606
  'dataType': 'json',
605
- 'url': 'rpc/',
607
+ 'url': 'rpc/#' + args.method,
606
608
  'type': 'post',
607
609
  'complete': [function() {
608
610
  Sao.common.processing.hide(timeoutID);
@@ -616,15 +618,16 @@
616
618
 
617
619
  Sao.Authentication.services = function() {
618
620
  var timeoutID = Sao.common.processing.show();
619
- return jQuery.ajax({
620
- 'contentType': 'application/json',
621
- 'data': JSON.stringify({
621
+ let args = {
622
622
  'id': 0,
623
623
  'method': 'common.authentication.services',
624
624
  'params': []
625
- }),
625
+ }
626
+ return jQuery.ajax({
627
+ 'contentType': 'application/json',
628
+ 'data': JSON.stringify(args),
626
629
  'dataType': 'json',
627
- 'url': 'rpc/',
630
+ 'url': 'rpc/#' + args.method,
628
631
  'type': 'post',
629
632
  'complete': [function() {
630
633
  Sao.common.processing.hide(timeoutID);
package/src/view/form.js CHANGED
@@ -3959,9 +3959,8 @@ function eval_pyson(value){
3959
3959
  if (record) {
3960
3960
  var fields = this.screen.current_view.get_fields();
3961
3961
  if (!record.validate(fields)) {
3962
- this.screen.display(true);
3963
- prm.reject();
3964
- return;
3962
+ this.screen.display(true).always(() => prm.reject());
3963
+ return prm;
3965
3964
  }
3966
3965
  if (this.screen.pre_validate) {
3967
3966
  return record.pre_validate().then(
@@ -12,6 +12,7 @@
12
12
  },
13
13
  button_clicked: function(event) {
14
14
  if (Sao.common.compare(this.screen.selected_records, [this.record])) {
15
+ event.stopPropagation();
15
16
  Sao.View.ListGroupViewForm._super.button_clicked.call(this, event);
16
17
  }
17
18
  }
package/src/view/tree.js CHANGED
@@ -2791,7 +2791,7 @@
2791
2791
  fields, false, false)) {
2792
2792
  var value = cell.prop('checked');
2793
2793
  this.field.set_client(record, value);
2794
- if (record !== current_record) {
2794
+ if ((!this.group.parent) & (record !== current_record)) {
2795
2795
  // we can not rely on editable tree handler to save the row
2796
2796
  record.save();
2797
2797
  }
package/src/wizard.js CHANGED
@@ -55,6 +55,7 @@
55
55
  if (this.__processing || this.__waiting_response) {
56
56
  return jQuery.when();
57
57
  }
58
+ this.__processing = true;
58
59
  var process = function() {
59
60
  if (this.state == this.end_state) {
60
61
  return this.end();
@@ -113,8 +114,8 @@
113
114
  } else {
114
115
  prms.push(execute_actions());
115
116
  }
116
- this.__processing = false;
117
- return jQuery.when.apply(jQuery, prms);
117
+ return jQuery.when.apply(jQuery, prms).then(
118
+ () => this.__processing = false);
118
119
  }, result => {
119
120
  if (!result || !this.screen) {
120
121
  this.state = this.end_state;
@@ -237,11 +238,14 @@
237
238
  this.footer.empty();
238
239
  },
239
240
  _get_button: function(definition) {
241
+ let state = this.state;
240
242
  var button = Sao.Wizard.Form._super._get_button.call(this,
241
243
  definition);
242
244
  this.footer.append(button.el);
243
245
  button.el.click(() => {
244
- this.response(definition);
246
+ if (this.state === state) {
247
+ this.response(definition);
248
+ }
245
249
  });
246
250
  return button;
247
251
  },
@@ -289,19 +293,24 @@
289
293
  this.footer.empty();
290
294
  },
291
295
  _get_button: function(definition) {
296
+ let state = this.state;
292
297
  var button = Sao.Wizard.Dialog._super._get_button.call(this,
293
298
  definition);
294
299
  this.footer.append(button.el);
295
300
  if (definition['default']) {
296
301
  this.content.unbind('submit');
297
302
  this.content.submit(e => {
298
- this.response(definition);
299
303
  e.preventDefault();
304
+ if (this.state === state) {
305
+ this.response(definition);
306
+ }
300
307
  });
301
308
  button.el.attr('type', 'submit');
302
309
  } else {
303
310
  button.el.click(() => {
304
- this.response(definition);
311
+ if (this.state === state) {
312
+ this.response(definition);
313
+ }
305
314
  });
306
315
  }
307
316
  return button;
package/tests/sao.js CHANGED
@@ -1933,6 +1933,12 @@
1933
1933
  'name': 'integer',
1934
1934
  'type': 'integer'
1935
1935
  },
1936
+ 'timestamp': {
1937
+ 'name': 'timestamp',
1938
+ 'string': 'Timestamp',
1939
+ 'type': 'timestamp',
1940
+ 'format': '"%H:%M:%S"',
1941
+ },
1936
1942
  'selection': {
1937
1943
  'string': 'Selection',
1938
1944
  'name': 'selection',
@@ -2033,6 +2039,7 @@
2033
2039
  c(['integer', '>=', 3]),
2034
2040
  c(['integer', '<=', 5])
2035
2041
  ]]],
2042
+ [[c(['Timestamp', null, null])], [c(['timestamp', '=', null])]],
2036
2043
  [[c(['Reference', null, 'foo'])],
2037
2044
  [c(['reference', 'ilike', '%foo%'])]],
2038
2045
  [[c(['Reference', null, 'Spam'])],
@@ -2073,6 +2080,33 @@
2073
2080
  QUnit.assert.deepEqual(parser.parse_clause(value), result,
2074
2081
  'parse_clause(' + JSON.stringify(value) + ')');
2075
2082
  });
2083
+
2084
+ let clause = parser.parse_clause([c(
2085
+ ['Timestamp', '=', Sao.common.format_date('%x', Sao.Date(2002, 12, 4))])]);
2086
+ QUnit.assert.strictEqual(clause[0].length, 2);
2087
+ let [ , operator, value] = clause[0][0];
2088
+ QUnit.assert.strictEqual(operator, '>=');
2089
+ QUnit.assert.ok(value.isSame(Sao.Date(2002, 12, 4)));
2090
+ [ , operator, value] = clause[0][1];
2091
+ QUnit.assert.strictEqual(operator, '<');
2092
+ QUnit.assert.ok(value.isSame(Sao.Date(2002, 12, 5)));
2093
+
2094
+ clause = parser.parse_clause([c(
2095
+ ['Timestamp', '=',
2096
+ Sao.common.format_datetime('%x %X', Sao.DateTime(2002, 12, 4, 12, 30))])]);
2097
+ [ , operator, value] = clause[0];
2098
+ QUnit.assert.strictEqual(operator, '=');
2099
+ QUnit.assert.ok(value.isSame(Sao.DateTime(2002, 12, 4, 12, 30)));
2100
+
2101
+ clause = parser.parse_clause([c(
2102
+ ['Timestamp', null, [
2103
+ `${Sao.common.format_date('%x', Sao.Date(2002, 12, 4))}`,
2104
+ `${Sao.common.format_date('%x', Sao.Date(2002, 12, 5))}`,
2105
+ ]])]);
2106
+ [ , operator, value] = clause[0];
2107
+ QUnit.assert.strictEqual(operator, 'in');
2108
+ QUnit.assert.ok(value[0].isSame(Sao.DateTime(2002, 12, 4)));
2109
+ QUnit.assert.ok(value[1].isSame(Sao.DateTime(2002, 12, 5)));
2076
2110
  });
2077
2111
 
2078
2112
  QUnit.test('DomainParser.format_value', function() {