tryton-sao 8.0.3 → 8.0.5

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.5 - 2026-06-18
3
+ --------------------------
4
+ * Bug fixes (see mercurial logs for details)
5
+
6
+
7
+ Version 8.0.4 - 2026-06-02
8
+ --------------------------
9
+ * Bug fixes (see mercurial logs for details)
10
+
11
+
2
12
  Version 8.0.3 - 2026-05-20
3
13
  --------------------------
4
14
  * Bug fixes (see mercurial logs for details)
@@ -9465,6 +9465,10 @@ html[theme="default"] .carousel-caption h6 {
9465
9465
  margin-bottom: -25px;
9466
9466
  width: 15px;
9467
9467
  }
9468
+ #user-preferences ul.notification-menu {
9469
+ background-color: #fff;
9470
+ color: #333333;
9471
+ }
9468
9472
  @media (min-width: 768px) {
9469
9473
  #user-preferences ul.notification-menu {
9470
9474
  width: 320px;
@@ -9477,6 +9481,7 @@ html[theme="default"] .carousel-caption h6 {
9477
9481
  background-color: #bceaeb;
9478
9482
  }
9479
9483
  #user-preferences ul.notification-menu > li.notification-item > a {
9484
+ color: #333333;
9480
9485
  display: flex;
9481
9486
  padding: 3px 10px;
9482
9487
  }
@@ -9503,6 +9508,9 @@ html[theme="default"] .carousel-caption h6 {
9503
9508
  text-align: center;
9504
9509
  background-color: #f5f5f5;
9505
9510
  }
9511
+ #user-preferences ul.notification-menu > li.divider {
9512
+ background-color: #e5e5e5;
9513
+ }
9506
9514
  .attachment-preview {
9507
9515
  border-bottom: 1px solid #eeeeee;
9508
9516
  display: flex;
@@ -3,7 +3,7 @@
3
3
 
4
4
  /* eslint-disable no-redeclare */
5
5
  var Sao = {
6
- __version__: '8.0.3',
6
+ __version__: '8.0.5',
7
7
  };
8
8
  /* eslint-enable no-redeclare */
9
9
 
@@ -93,7 +93,7 @@ var Sao = {
93
93
  let display = element.style.display;
94
94
  if (show) {
95
95
  if (display === 'none') {
96
- element.style.display = 'revert';
96
+ element.style.display = '';
97
97
  }
98
98
  } else {
99
99
  if (display !== 'none') {
@@ -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(
@@ -4965,7 +4963,9 @@ var Sao = {
4965
4963
  };
4966
4964
 
4967
4965
  var complete_datetime = function() {
4968
- return [Sao.Date(), Sao.DateTime().utc()];
4966
+ return [Sao.Date(), Sao.DateTime(
4967
+ undefined, undefined, undefined,
4968
+ 0, 0, 0, 0, true)];
4969
4969
  };
4970
4970
 
4971
4971
  var complete_date = function() {
@@ -5272,6 +5272,32 @@ var Sao = {
5272
5272
  ]);
5273
5273
  return;
5274
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
+ }
5275
5301
  }
5276
5302
  if (['many2one', 'one2many', 'many2many', 'one2one',
5277
5303
  'many2many', 'one2one'].includes(field.type) && value) {
@@ -13998,6 +14024,9 @@ var Sao = {
13998
14024
  });
13999
14025
  },
14000
14026
  get current_record() {
14027
+ if (this.__current_record && this.__current_record.destroyed) {
14028
+ this.__current_record = null;
14029
+ }
14001
14030
  return this.__current_record;
14002
14031
  },
14003
14032
  set current_record(record) {
@@ -15577,11 +15606,12 @@ function eval_pyson(value){
15577
15606
  });
15578
15607
  for (const e of fields) {
15579
15608
  const name = e[0];
15580
- promesses.push(record.load(name));
15609
+ if (!record.is_loaded(name)) {
15610
+ promesses.push(record.load(name));
15611
+ }
15581
15612
  }
15582
15613
  }
15583
- return jQuery.when.apply(jQuery,promesses)
15584
- .then(() => {
15614
+ let display = function() {
15585
15615
  let promesses = [];
15586
15616
  var record = this.record;
15587
15617
  for (const name in this.widgets) {
@@ -15619,7 +15649,13 @@ function eval_pyson(value){
15619
15649
  container.set_grid_template();
15620
15650
  }
15621
15651
  });
15622
- });
15652
+ }.bind(this);
15653
+ if (promesses.length) {
15654
+ return jQuery.when.apply(jQuery, promesses).then(
15655
+ () => display());
15656
+ } else {
15657
+ return display();
15658
+ }
15623
15659
  },
15624
15660
  set_value: function() {
15625
15661
  var record = this.record;
@@ -18721,7 +18757,11 @@ function eval_pyson(value){
18721
18757
  },
18722
18758
  set_readonly: function(readonly) {
18723
18759
  Sao.View.Form.One2Many._super.set_readonly.call(this, readonly);
18724
- this.prm.done(() => this._set_button_sensitive());
18760
+ if (this.prm.state() == 'pending') {
18761
+ this.prm.done(() => this._set_button_sensitive());
18762
+ } else {
18763
+ this._set_button_sensitive();
18764
+ }
18725
18765
  },
18726
18766
  _set_button_sensitive: function() {
18727
18767
  var size_limit, o2m_size;
@@ -18825,7 +18865,7 @@ function eval_pyson(value){
18825
18865
  display: function() {
18826
18866
  Sao.View.Form.One2Many._super.display.call(this);
18827
18867
 
18828
- return this.prm.then(() => {
18868
+ let display = function() {
18829
18869
  this._set_button_sensitive();
18830
18870
 
18831
18871
  var record = this.record;
@@ -18872,7 +18912,13 @@ function eval_pyson(value){
18872
18912
  .css('max-height', this.attributes.height + 'px');
18873
18913
  }
18874
18914
  return this.screen.display();
18875
- });
18915
+ }.bind(this);
18916
+
18917
+ if (this.prm.state() == 'pending') {
18918
+ return this.prm.then(() => display());
18919
+ } else {
18920
+ return display();
18921
+ }
18876
18922
  },
18877
18923
  focus: function() {
18878
18924
  if (this.attributes.add_remove) {
@@ -19149,7 +19195,11 @@ function eval_pyson(value){
19149
19195
  }
19150
19196
  var message = name + ' / ' + Sao.common.humanize(size);
19151
19197
  this.badge.text(message).attr('title', message);
19152
- this.prm.done(() => this._set_button_sensitive());
19198
+ if (this.prm.state() == 'pending') {
19199
+ this.prm.done(() => this._set_button_sensitive());
19200
+ } else {
19201
+ this._set_button_sensitive();
19202
+ }
19153
19203
  },
19154
19204
  validate: function() {
19155
19205
  var prm = jQuery.Deferred();
@@ -19158,9 +19208,8 @@ function eval_pyson(value){
19158
19208
  if (record) {
19159
19209
  var fields = this.screen.current_view.get_fields();
19160
19210
  if (!record.validate(fields)) {
19161
- this.screen.display(true);
19162
- prm.reject();
19163
- return;
19211
+ this.screen.display(true).always(() => prm.reject());
19212
+ return prm;
19164
19213
  }
19165
19214
  if (this.screen.pre_validate) {
19166
19215
  return record.pre_validate().then(
@@ -19398,7 +19447,7 @@ function eval_pyson(value){
19398
19447
  display: function() {
19399
19448
  Sao.View.Form.Many2Many._super.display.call(this);
19400
19449
 
19401
- return this.prm.then(() => {
19450
+ let display = function() {
19402
19451
  var record = this.record;
19403
19452
  var field = this.field;
19404
19453
 
@@ -19420,7 +19469,13 @@ function eval_pyson(value){
19420
19469
  .css('max-height', this.attributes.height + 'px');
19421
19470
  }
19422
19471
  return this.screen.display();
19423
- });
19472
+ }.bind(this);
19473
+
19474
+ if (this.prm.state() == 'pending') {
19475
+ return this.prm.then(() => display());
19476
+ } else {
19477
+ return display();
19478
+ }
19424
19479
  },
19425
19480
  focus: function() {
19426
19481
  this.entry.focus();
@@ -22670,6 +22725,13 @@ function eval_pyson(value){
22670
22725
  this.display_size = this.group.length;
22671
22726
  this.display();
22672
22727
  }
22728
+ if (reset_view) {
22729
+ let current_path = this.record.get_path(this.group);
22730
+ current_path = current_path.map(function(e) {
22731
+ return e[1];
22732
+ });
22733
+ this.display([current_path]);
22734
+ }
22673
22735
  if (path.length > 1) {
22674
22736
  prm = this.rows[path[0]].expand_to_path(
22675
22737
  path.slice(1),
@@ -23904,7 +23966,7 @@ function eval_pyson(value){
23904
23966
  fields, false, false)) {
23905
23967
  var value = cell.prop('checked');
23906
23968
  this.field.set_client(record, value);
23907
- if (record !== current_record) {
23969
+ if ((!this.group.parent) & (record !== current_record)) {
23908
23970
  // we can not rely on editable tree handler to save the row
23909
23971
  record.save();
23910
23972
  }
@@ -25455,6 +25517,7 @@ function eval_pyson(value){
25455
25517
  },
25456
25518
  button_clicked: function(event) {
25457
25519
  if (Sao.common.compare(this.screen.selected_records, [this.record])) {
25520
+ event.stopPropagation();
25458
25521
  Sao.View.ListGroupViewForm._super.button_clicked.call(this, event);
25459
25522
  }
25460
25523
  }
@@ -25557,6 +25620,12 @@ function eval_pyson(value){
25557
25620
  return this.group.slice();
25558
25621
  },
25559
25622
  set_cursor: function(new_, reset_view) {
25623
+ if (!this.record) {
25624
+ return;
25625
+ }
25626
+ if (reset_view) {
25627
+ this.display([this.record.id]);
25628
+ }
25560
25629
  if (new_) {
25561
25630
  this.el.animate({
25562
25631
  scrollTop: this.el[0].scrollHeight
@@ -28573,15 +28642,16 @@ function eval_pyson(value){
28573
28642
  },
28574
28643
  _fill_with: function(template) {
28575
28644
  var prm;
28645
+ let context = this.record.get_context();
28576
28646
  if (template) {
28577
28647
  prm = Sao.rpc({
28578
28648
  'method': 'model.ir.email.template.get',
28579
- 'params': [template, this.record.id, {}],
28649
+ 'params': [template, this.record.id, context],
28580
28650
  }, this.record.model.session);
28581
28651
  } else {
28582
28652
  prm = Sao.rpc({
28583
28653
  'method': 'model.ir.email.template.get_default',
28584
- 'params': [this.record.model.name, this.record.id, {}],
28654
+ 'params': [this.record.model.name, this.record.id, context],
28585
28655
  }, this.record.model.session);
28586
28656
  }
28587
28657
  prm.then(values => {
@@ -28829,6 +28899,7 @@ function eval_pyson(value){
28829
28899
  if (this.__processing || this.__waiting_response) {
28830
28900
  return jQuery.when();
28831
28901
  }
28902
+ this.__processing = true;
28832
28903
  var process = function() {
28833
28904
  if (this.state == this.end_state) {
28834
28905
  return this.end();
@@ -28887,8 +28958,8 @@ function eval_pyson(value){
28887
28958
  } else {
28888
28959
  prms.push(execute_actions());
28889
28960
  }
28890
- this.__processing = false;
28891
- return jQuery.when.apply(jQuery, prms);
28961
+ return jQuery.when.apply(jQuery, prms).then(
28962
+ () => this.__processing = false);
28892
28963
  }, result => {
28893
28964
  if (!result || !this.screen) {
28894
28965
  this.state = this.end_state;
@@ -29011,11 +29082,14 @@ function eval_pyson(value){
29011
29082
  this.footer.empty();
29012
29083
  },
29013
29084
  _get_button: function(definition) {
29085
+ let state = this.state;
29014
29086
  var button = Sao.Wizard.Form._super._get_button.call(this,
29015
29087
  definition);
29016
29088
  this.footer.append(button.el);
29017
29089
  button.el.click(() => {
29018
- this.response(definition);
29090
+ if (this.state === state) {
29091
+ this.response(definition);
29092
+ }
29019
29093
  });
29020
29094
  return button;
29021
29095
  },
@@ -29063,19 +29137,24 @@ function eval_pyson(value){
29063
29137
  this.footer.empty();
29064
29138
  },
29065
29139
  _get_button: function(definition) {
29140
+ let state = this.state;
29066
29141
  var button = Sao.Wizard.Dialog._super._get_button.call(this,
29067
29142
  definition);
29068
29143
  this.footer.append(button.el);
29069
29144
  if (definition['default']) {
29070
29145
  this.content.unbind('submit');
29071
29146
  this.content.submit(e => {
29072
- this.response(definition);
29073
29147
  e.preventDefault();
29148
+ if (this.state === state) {
29149
+ this.response(definition);
29150
+ }
29074
29151
  });
29075
29152
  button.el.attr('type', 'submit');
29076
29153
  } else {
29077
29154
  button.el.click(() => {
29078
- this.response(definition);
29155
+ if (this.state === state) {
29156
+ this.response(definition);
29157
+ }
29079
29158
  });
29080
29159
  }
29081
29160
  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.3",
5
+ "version": "8.0.5",
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.3',
6
+ __version__: '8.0.5',
7
7
  };
8
8
  /* eslint-enable no-redeclare */
9
9
 
@@ -93,7 +93,7 @@ var Sao = {
93
93
  let display = element.style.display;
94
94
  if (show) {
95
95
  if (display === 'none') {
96
- element.style.display = 'revert';
96
+ element.style.display = '';
97
97
  }
98
98
  } else {
99
99
  if (display !== 'none') {
package/src/sao.less CHANGED
@@ -441,6 +441,9 @@ html[theme="default"] {
441
441
  }
442
442
 
443
443
  ul.notification-menu {
444
+ background-color: @body-bg;
445
+ color: @text-color;
446
+
444
447
  @media (min-width: @grid-float-breakpoint) {
445
448
  width: 320px;
446
449
  }
@@ -453,6 +456,7 @@ html[theme="default"] {
453
456
  }
454
457
 
455
458
  > a {
459
+ color: @text-color;
456
460
  display: flex;
457
461
  padding: 3px 10px;
458
462
 
@@ -489,6 +493,10 @@ html[theme="default"] {
489
493
  }
490
494
  }
491
495
  }
496
+
497
+ > li.divider {
498
+ background-color: @dropdown-divider-bg;
499
+ }
492
500
  }
493
501
  }
494
502
 
package/src/screen.js CHANGED
@@ -1356,6 +1356,9 @@
1356
1356
  });
1357
1357
  },
1358
1358
  get current_record() {
1359
+ if (this.__current_record && this.__current_record.destroyed) {
1360
+ this.__current_record = null;
1361
+ }
1359
1362
  return this.__current_record;
1360
1363
  },
1361
1364
  set current_record(record) {
package/src/view/form.js CHANGED
@@ -378,11 +378,12 @@ function eval_pyson(value){
378
378
  });
379
379
  for (const e of fields) {
380
380
  const name = e[0];
381
- promesses.push(record.load(name));
381
+ if (!record.is_loaded(name)) {
382
+ promesses.push(record.load(name));
383
+ }
382
384
  }
383
385
  }
384
- return jQuery.when.apply(jQuery,promesses)
385
- .then(() => {
386
+ let display = function() {
386
387
  let promesses = [];
387
388
  var record = this.record;
388
389
  for (const name in this.widgets) {
@@ -420,7 +421,13 @@ function eval_pyson(value){
420
421
  container.set_grid_template();
421
422
  }
422
423
  });
423
- });
424
+ }.bind(this);
425
+ if (promesses.length) {
426
+ return jQuery.when.apply(jQuery, promesses).then(
427
+ () => display());
428
+ } else {
429
+ return display();
430
+ }
424
431
  },
425
432
  set_value: function() {
426
433
  var record = this.record;
@@ -3522,7 +3529,11 @@ function eval_pyson(value){
3522
3529
  },
3523
3530
  set_readonly: function(readonly) {
3524
3531
  Sao.View.Form.One2Many._super.set_readonly.call(this, readonly);
3525
- this.prm.done(() => this._set_button_sensitive());
3532
+ if (this.prm.state() == 'pending') {
3533
+ this.prm.done(() => this._set_button_sensitive());
3534
+ } else {
3535
+ this._set_button_sensitive();
3536
+ }
3526
3537
  },
3527
3538
  _set_button_sensitive: function() {
3528
3539
  var size_limit, o2m_size;
@@ -3626,7 +3637,7 @@ function eval_pyson(value){
3626
3637
  display: function() {
3627
3638
  Sao.View.Form.One2Many._super.display.call(this);
3628
3639
 
3629
- return this.prm.then(() => {
3640
+ let display = function() {
3630
3641
  this._set_button_sensitive();
3631
3642
 
3632
3643
  var record = this.record;
@@ -3673,7 +3684,13 @@ function eval_pyson(value){
3673
3684
  .css('max-height', this.attributes.height + 'px');
3674
3685
  }
3675
3686
  return this.screen.display();
3676
- });
3687
+ }.bind(this);
3688
+
3689
+ if (this.prm.state() == 'pending') {
3690
+ return this.prm.then(() => display());
3691
+ } else {
3692
+ return display();
3693
+ }
3677
3694
  },
3678
3695
  focus: function() {
3679
3696
  if (this.attributes.add_remove) {
@@ -3950,7 +3967,11 @@ function eval_pyson(value){
3950
3967
  }
3951
3968
  var message = name + ' / ' + Sao.common.humanize(size);
3952
3969
  this.badge.text(message).attr('title', message);
3953
- this.prm.done(() => this._set_button_sensitive());
3970
+ if (this.prm.state() == 'pending') {
3971
+ this.prm.done(() => this._set_button_sensitive());
3972
+ } else {
3973
+ this._set_button_sensitive();
3974
+ }
3954
3975
  },
3955
3976
  validate: function() {
3956
3977
  var prm = jQuery.Deferred();
@@ -3959,9 +3980,8 @@ function eval_pyson(value){
3959
3980
  if (record) {
3960
3981
  var fields = this.screen.current_view.get_fields();
3961
3982
  if (!record.validate(fields)) {
3962
- this.screen.display(true);
3963
- prm.reject();
3964
- return;
3983
+ this.screen.display(true).always(() => prm.reject());
3984
+ return prm;
3965
3985
  }
3966
3986
  if (this.screen.pre_validate) {
3967
3987
  return record.pre_validate().then(
@@ -4199,7 +4219,7 @@ function eval_pyson(value){
4199
4219
  display: function() {
4200
4220
  Sao.View.Form.Many2Many._super.display.call(this);
4201
4221
 
4202
- return this.prm.then(() => {
4222
+ let display = function() {
4203
4223
  var record = this.record;
4204
4224
  var field = this.field;
4205
4225
 
@@ -4221,7 +4241,13 @@ function eval_pyson(value){
4221
4241
  .css('max-height', this.attributes.height + 'px');
4222
4242
  }
4223
4243
  return this.screen.display();
4224
- });
4244
+ }.bind(this);
4245
+
4246
+ if (this.prm.state() == 'pending') {
4247
+ return this.prm.then(() => display());
4248
+ } else {
4249
+ return display();
4250
+ }
4225
4251
  },
4226
4252
  focus: function() {
4227
4253
  this.entry.focus();
@@ -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
  }
@@ -114,6 +115,12 @@
114
115
  return this.group.slice();
115
116
  },
116
117
  set_cursor: function(new_, reset_view) {
118
+ if (!this.record) {
119
+ return;
120
+ }
121
+ if (reset_view) {
122
+ this.display([this.record.id]);
123
+ }
117
124
  if (new_) {
118
125
  this.el.animate({
119
126
  scrollTop: this.el[0].scrollHeight
package/src/view/tree.js CHANGED
@@ -1557,6 +1557,13 @@
1557
1557
  this.display_size = this.group.length;
1558
1558
  this.display();
1559
1559
  }
1560
+ if (reset_view) {
1561
+ let current_path = this.record.get_path(this.group);
1562
+ current_path = current_path.map(function(e) {
1563
+ return e[1];
1564
+ });
1565
+ this.display([current_path]);
1566
+ }
1560
1567
  if (path.length > 1) {
1561
1568
  prm = this.rows[path[0]].expand_to_path(
1562
1569
  path.slice(1),
@@ -2791,7 +2798,7 @@
2791
2798
  fields, false, false)) {
2792
2799
  var value = cell.prop('checked');
2793
2800
  this.field.set_client(record, value);
2794
- if (record !== current_record) {
2801
+ if ((!this.group.parent) & (record !== current_record)) {
2795
2802
  // we can not rely on editable tree handler to save the row
2796
2803
  record.save();
2797
2804
  }
package/src/window.js CHANGED
@@ -2699,15 +2699,16 @@
2699
2699
  },
2700
2700
  _fill_with: function(template) {
2701
2701
  var prm;
2702
+ let context = this.record.get_context();
2702
2703
  if (template) {
2703
2704
  prm = Sao.rpc({
2704
2705
  'method': 'model.ir.email.template.get',
2705
- 'params': [template, this.record.id, {}],
2706
+ 'params': [template, this.record.id, context],
2706
2707
  }, this.record.model.session);
2707
2708
  } else {
2708
2709
  prm = Sao.rpc({
2709
2710
  'method': 'model.ir.email.template.get_default',
2710
- 'params': [this.record.model.name, this.record.id, {}],
2711
+ 'params': [this.record.model.name, this.record.id, context],
2711
2712
  }, this.record.model.session);
2712
2713
  }
2713
2714
  prm.then(values => {
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() {