tryton-sao 7.8.10 → 7.8.12

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 7.8.12 - 2026-06-18
3
+ ---------------------------
4
+ * Bug fixes (see mercurial logs for details)
5
+
6
+
7
+ Version 7.8.11 - 2026-06-02
8
+ ---------------------------
9
+ * Bug fixes (see mercurial logs for details)
10
+
11
+
2
12
  Version 7.8.10 - 2026-05-20
3
13
  ---------------------------
4
14
  * Bug fixes (see mercurial logs for details)
@@ -9534,6 +9534,10 @@ html[theme="default"] .carousel-caption h6 {
9534
9534
  margin-bottom: -25px;
9535
9535
  width: 15px;
9536
9536
  }
9537
+ #user-preferences ul.notification-menu {
9538
+ background-color: #fff;
9539
+ color: #333333;
9540
+ }
9537
9541
  @media (min-width: 768px) {
9538
9542
  #user-preferences ul.notification-menu {
9539
9543
  width: 320px;
@@ -9546,6 +9550,7 @@ html[theme="default"] .carousel-caption h6 {
9546
9550
  background-color: #bceaeb;
9547
9551
  }
9548
9552
  #user-preferences ul.notification-menu > li.notification-item > a {
9553
+ color: #333333;
9549
9554
  display: flex;
9550
9555
  padding: 3px 10px;
9551
9556
  }
@@ -9572,6 +9577,9 @@ html[theme="default"] .carousel-caption h6 {
9572
9577
  text-align: center;
9573
9578
  background-color: #f5f5f5;
9574
9579
  }
9580
+ #user-preferences ul.notification-menu > li.divider {
9581
+ background-color: #e5e5e5;
9582
+ }
9575
9583
  .attachment-preview {
9576
9584
  border-bottom: 1px solid #eeeeee;
9577
9585
  display: flex;
@@ -3,7 +3,7 @@
3
3
 
4
4
  /* eslint-disable no-redeclare */
5
5
  var Sao = {
6
- __version__: '7.8.10',
6
+ __version__: '7.8.12',
7
7
  };
8
8
  /* eslint-enable no-redeclare */
9
9
 
@@ -1450,11 +1450,6 @@ var Sao = {
1450
1450
  };
1451
1451
 
1452
1452
  var ajax_error = function(query, status_, error) {
1453
- if (!process_exception) {
1454
- console.debug(`RPC error calling ${args}: ${status_}: ${error}.`);
1455
- dfd.reject();
1456
- return;
1457
- }
1458
1453
  if (query.status == 503) {
1459
1454
  this.retries++;
1460
1455
  if (this.retries < 5) {
@@ -1480,8 +1475,7 @@ var Sao = {
1480
1475
  }
1481
1476
  return;
1482
1477
  }
1483
- }
1484
- if (query.status == 401) {
1478
+ } else if (query.status == 401) {
1485
1479
  //Try to relog
1486
1480
  Sao.Session.renew(session).then(function() {
1487
1481
  if (async) {
@@ -1491,6 +1485,10 @@ var Sao = {
1491
1485
  dfd.resolve();
1492
1486
  }
1493
1487
  }, dfd.reject);
1488
+ } else if (!process_exception) {
1489
+ console.debug(`RPC error calling ${args}: ${status_}: ${error}.`);
1490
+ dfd.reject();
1491
+ return;
1494
1492
  } else {
1495
1493
  var err_msg = `[${query.status}] ${error}`;
1496
1494
  Sao.common.message.run(
@@ -4942,7 +4940,9 @@ var Sao = {
4942
4940
  };
4943
4941
 
4944
4942
  var complete_datetime = function() {
4945
- return [Sao.Date(), Sao.DateTime().utc()];
4943
+ return [Sao.Date(), Sao.DateTime(
4944
+ undefined, undefined, undefined,
4945
+ 0, 0, 0, 0, true)];
4946
4946
  };
4947
4947
 
4948
4948
  var complete_date = function() {
@@ -5249,6 +5249,32 @@ var Sao = {
5249
5249
  ]);
5250
5250
  return;
5251
5251
  }
5252
+ if ((typeof value == 'string') &&
5253
+ ['datetime', 'timestamp'].includes(field.type) &&
5254
+ (operator == '=')) {
5255
+ let ctx, format_, parsed_date;
5256
+ if (this.context && Object.keys(this.context).length) {
5257
+ ctx = this.context;
5258
+ } else {
5259
+ ctx = {};
5260
+ }
5261
+ format_ = Sao.common.date_format(ctx.date_format);
5262
+ parsed_date = Sao.common.parse_date(format_, value);
5263
+ if (parsed_date &&
5264
+ (Sao.common.format_date(format_, parsed_date) == value)) {
5265
+ let date = Sao.DateTime.combine(parsed_date, Sao.Time());
5266
+ let next_day = Sao.DateTime(
5267
+ date.year(), date.month(), date.date(),
5268
+ date.hour(), date.minute(), date.second(),
5269
+ date.millisecond())
5270
+ next_day.add(1, 'day');
5271
+ result.push(this._clausify([
5272
+ [field_name, '>=', date],
5273
+ [field_name, '<', next_day]
5274
+ ]));
5275
+ return;
5276
+ }
5277
+ }
5252
5278
  }
5253
5279
  if (['many2one', 'one2many', 'many2many', 'one2one',
5254
5280
  'many2many', 'one2one'].includes(field.type) && value) {
@@ -13966,6 +13992,9 @@ var Sao = {
13966
13992
  });
13967
13993
  },
13968
13994
  get current_record() {
13995
+ if (this.__current_record && this.__current_record.destroyed) {
13996
+ this.__current_record = null;
13997
+ }
13969
13998
  return this.__current_record;
13970
13999
  },
13971
14000
  set current_record(record) {
@@ -15539,11 +15568,12 @@ function eval_pyson(value){
15539
15568
  });
15540
15569
  for (const e of fields) {
15541
15570
  const name = e[0];
15542
- promesses.push(record.load(name));
15571
+ if (!record.is_loaded(name)) {
15572
+ promesses.push(record.load(name));
15573
+ }
15543
15574
  }
15544
15575
  }
15545
- return jQuery.when.apply(jQuery,promesses)
15546
- .then(() => {
15576
+ let display = function() {
15547
15577
  let promesses = [];
15548
15578
  var record = this.record;
15549
15579
  for (const name in this.widgets) {
@@ -15581,7 +15611,13 @@ function eval_pyson(value){
15581
15611
  container.set_grid_template();
15582
15612
  }
15583
15613
  });
15584
- });
15614
+ }.bind(this);
15615
+ if (promesses.length) {
15616
+ return jQuery.when.apply(jQuery, promesses).then(
15617
+ () => display());
15618
+ } else {
15619
+ return display();
15620
+ }
15585
15621
  },
15586
15622
  set_value: function() {
15587
15623
  var record = this.record;
@@ -18677,7 +18713,11 @@ function eval_pyson(value){
18677
18713
  },
18678
18714
  set_readonly: function(readonly) {
18679
18715
  Sao.View.Form.One2Many._super.set_readonly.call(this, readonly);
18680
- this.prm.done(() => this._set_button_sensitive());
18716
+ if (this.prm.state() == 'pending') {
18717
+ this.prm.done(() => this._set_button_sensitive());
18718
+ } else {
18719
+ this._set_button_sensitive();
18720
+ }
18681
18721
  this._set_label_state();
18682
18722
  },
18683
18723
  set_required: function(required) {
@@ -18790,7 +18830,7 @@ function eval_pyson(value){
18790
18830
  display: function() {
18791
18831
  Sao.View.Form.One2Many._super.display.call(this);
18792
18832
 
18793
- return this.prm.then(() => {
18833
+ let display = function() {
18794
18834
  this._set_button_sensitive();
18795
18835
 
18796
18836
  var record = this.record;
@@ -18837,7 +18877,13 @@ function eval_pyson(value){
18837
18877
  .css('max-height', this.attributes.height + 'px');
18838
18878
  }
18839
18879
  return this.screen.display();
18840
- });
18880
+ }.bind(this);
18881
+
18882
+ if (this.prm.state() == 'pending') {
18883
+ return this.prm.then(() => display());
18884
+ } else {
18885
+ return display();
18886
+ }
18841
18887
  },
18842
18888
  focus: function() {
18843
18889
  if (this.attributes.add_remove) {
@@ -19114,7 +19160,11 @@ function eval_pyson(value){
19114
19160
  }
19115
19161
  var message = name + ' / ' + Sao.common.humanize(size);
19116
19162
  this.label.text(message).attr('title', message);
19117
- this.prm.done(() => this._set_button_sensitive());
19163
+ if (this.prm.state() == 'pending') {
19164
+ this.prm.done(() => this._set_button_sensitive());
19165
+ } else {
19166
+ this._set_button_sensitive();
19167
+ }
19118
19168
  },
19119
19169
  validate: function() {
19120
19170
  var prm = jQuery.Deferred();
@@ -19123,9 +19173,8 @@ function eval_pyson(value){
19123
19173
  if (record) {
19124
19174
  var fields = this.screen.current_view.get_fields();
19125
19175
  if (!record.validate(fields)) {
19126
- this.screen.display(true);
19127
- prm.reject();
19128
- return;
19176
+ this.screen.display(true).always(() => prm.reject());
19177
+ return prm;
19129
19178
  }
19130
19179
  if (this.screen.pre_validate) {
19131
19180
  return record.pre_validate().then(
@@ -19370,7 +19419,7 @@ function eval_pyson(value){
19370
19419
  display: function() {
19371
19420
  Sao.View.Form.Many2Many._super.display.call(this);
19372
19421
 
19373
- return this.prm.then(() => {
19422
+ let display = function() {
19374
19423
  var record = this.record;
19375
19424
  var field = this.field;
19376
19425
 
@@ -19392,7 +19441,13 @@ function eval_pyson(value){
19392
19441
  .css('max-height', this.attributes.height + 'px');
19393
19442
  }
19394
19443
  return this.screen.display();
19395
- });
19444
+ }.bind(this);
19445
+
19446
+ if (this.prm.state() == 'pending') {
19447
+ return this.prm.then(() => display());
19448
+ } else {
19449
+ return display();
19450
+ }
19396
19451
  },
19397
19452
  focus: function() {
19398
19453
  this.entry.focus();
@@ -22616,6 +22671,13 @@ function eval_pyson(value){
22616
22671
  this.display_size = this.group.length;
22617
22672
  this.display();
22618
22673
  }
22674
+ if (reset_view) {
22675
+ let current_path = this.record.get_path(this.group);
22676
+ current_path = current_path.map(function(e) {
22677
+ return e[1];
22678
+ });
22679
+ this.display([current_path]);
22680
+ }
22619
22681
  if (path.length > 1) {
22620
22682
  prm = this.rows[path[0]].expand_to_path(
22621
22683
  path.slice(1),
@@ -23850,7 +23912,7 @@ function eval_pyson(value){
23850
23912
  fields, false, false)) {
23851
23913
  var value = cell.prop('checked');
23852
23914
  this.field.set_client(record, value);
23853
- if (record !== current_record) {
23915
+ if ((!this.group.parent) & (record !== current_record)) {
23854
23916
  // we can not rely on editable tree handler to save the row
23855
23917
  record.save();
23856
23918
  }
@@ -25401,6 +25463,7 @@ function eval_pyson(value){
25401
25463
  },
25402
25464
  button_clicked: function(event) {
25403
25465
  if (Sao.common.compare(this.screen.selected_records, [this.record])) {
25466
+ event.stopPropagation();
25404
25467
  Sao.View.ListGroupViewForm._super.button_clicked.call(this, event);
25405
25468
  }
25406
25469
  }
@@ -25503,6 +25566,12 @@ function eval_pyson(value){
25503
25566
  return this.group.slice();
25504
25567
  },
25505
25568
  set_cursor: function(new_, reset_view) {
25569
+ if (!this.record) {
25570
+ return;
25571
+ }
25572
+ if (reset_view) {
25573
+ this.display([this.record.id]);
25574
+ }
25506
25575
  if (new_) {
25507
25576
  this.el.animate({
25508
25577
  scrollTop: this.el[0].scrollHeight
@@ -28519,15 +28588,16 @@ function eval_pyson(value){
28519
28588
  },
28520
28589
  _fill_with: function(template) {
28521
28590
  var prm;
28591
+ let context = this.record.get_context();
28522
28592
  if (template) {
28523
28593
  prm = Sao.rpc({
28524
28594
  'method': 'model.ir.email.template.get',
28525
- 'params': [template, this.record.id, {}],
28595
+ 'params': [template, this.record.id, context],
28526
28596
  }, this.record.model.session);
28527
28597
  } else {
28528
28598
  prm = Sao.rpc({
28529
28599
  'method': 'model.ir.email.template.get_default',
28530
- 'params': [this.record.model.name, this.record.id, {}],
28600
+ 'params': [this.record.model.name, this.record.id, context],
28531
28601
  }, this.record.model.session);
28532
28602
  }
28533
28603
  prm.then(values => {
@@ -28775,6 +28845,7 @@ function eval_pyson(value){
28775
28845
  if (this.__processing || this.__waiting_response) {
28776
28846
  return jQuery.when();
28777
28847
  }
28848
+ this.__processing = true;
28778
28849
  var process = function() {
28779
28850
  if (this.state == this.end_state) {
28780
28851
  return this.end();
@@ -28833,8 +28904,8 @@ function eval_pyson(value){
28833
28904
  } else {
28834
28905
  prms.push(execute_actions());
28835
28906
  }
28836
- this.__processing = false;
28837
- return jQuery.when.apply(jQuery, prms);
28907
+ return jQuery.when.apply(jQuery, prms).then(
28908
+ () => this.__processing = false);
28838
28909
  }, result => {
28839
28910
  if (!result || !this.screen) {
28840
28911
  this.state = this.end_state;
@@ -28957,11 +29028,14 @@ function eval_pyson(value){
28957
29028
  this.footer.empty();
28958
29029
  },
28959
29030
  _get_button: function(definition) {
29031
+ let state = this.state;
28960
29032
  var button = Sao.Wizard.Form._super._get_button.call(this,
28961
29033
  definition);
28962
29034
  this.footer.append(button.el);
28963
29035
  button.el.click(() => {
28964
- this.response(definition);
29036
+ if (this.state === state) {
29037
+ this.response(definition);
29038
+ }
28965
29039
  });
28966
29040
  return button;
28967
29041
  },
@@ -29009,19 +29083,24 @@ function eval_pyson(value){
29009
29083
  this.footer.empty();
29010
29084
  },
29011
29085
  _get_button: function(definition) {
29086
+ let state = this.state;
29012
29087
  var button = Sao.Wizard.Dialog._super._get_button.call(this,
29013
29088
  definition);
29014
29089
  this.footer.append(button.el);
29015
29090
  if (definition['default']) {
29016
29091
  this.content.unbind('submit');
29017
29092
  this.content.submit(e => {
29018
- this.response(definition);
29019
29093
  e.preventDefault();
29094
+ if (this.state === state) {
29095
+ this.response(definition);
29096
+ }
29020
29097
  });
29021
29098
  button.el.attr('type', 'submit');
29022
29099
  } else {
29023
29100
  button.el.click(() => {
29024
- this.response(definition);
29101
+ if (this.state === state) {
29102
+ this.response(definition);
29103
+ }
29025
29104
  });
29026
29105
  }
29027
29106
  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": "7.8.10",
5
+ "version": "7.8.12",
6
6
  "homepage": "https://www.tryton.org/",
7
7
  "author": {
8
8
  "name": "Tryton"
package/src/common.js CHANGED
@@ -1635,7 +1635,9 @@
1635
1635
  };
1636
1636
 
1637
1637
  var complete_datetime = function() {
1638
- return [Sao.Date(), Sao.DateTime().utc()];
1638
+ return [Sao.Date(), Sao.DateTime(
1639
+ undefined, undefined, undefined,
1640
+ 0, 0, 0, 0, true)];
1639
1641
  };
1640
1642
 
1641
1643
  var complete_date = function() {
@@ -1942,6 +1944,32 @@
1942
1944
  ]);
1943
1945
  return;
1944
1946
  }
1947
+ if ((typeof value == 'string') &&
1948
+ ['datetime', 'timestamp'].includes(field.type) &&
1949
+ (operator == '=')) {
1950
+ let ctx, format_, parsed_date;
1951
+ if (this.context && Object.keys(this.context).length) {
1952
+ ctx = this.context;
1953
+ } else {
1954
+ ctx = {};
1955
+ }
1956
+ format_ = Sao.common.date_format(ctx.date_format);
1957
+ parsed_date = Sao.common.parse_date(format_, value);
1958
+ if (parsed_date &&
1959
+ (Sao.common.format_date(format_, parsed_date) == value)) {
1960
+ let date = Sao.DateTime.combine(parsed_date, Sao.Time());
1961
+ let next_day = Sao.DateTime(
1962
+ date.year(), date.month(), date.date(),
1963
+ date.hour(), date.minute(), date.second(),
1964
+ date.millisecond())
1965
+ next_day.add(1, 'day');
1966
+ result.push(this._clausify([
1967
+ [field_name, '>=', date],
1968
+ [field_name, '<', next_day]
1969
+ ]));
1970
+ return;
1971
+ }
1972
+ }
1945
1973
  }
1946
1974
  if (['many2one', 'one2many', 'many2many', 'one2one',
1947
1975
  '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__: '7.8.10',
6
+ __version__: '7.8.12',
7
7
  };
8
8
  /* eslint-enable no-redeclare */
9
9
 
package/src/sao.less CHANGED
@@ -460,6 +460,9 @@ html[theme="default"] {
460
460
  }
461
461
 
462
462
  ul.notification-menu {
463
+ background-color: @body-bg;
464
+ color: @text-color;
465
+
463
466
  @media (min-width: @grid-float-breakpoint) {
464
467
  width: 320px;
465
468
  }
@@ -472,6 +475,7 @@ html[theme="default"] {
472
475
  }
473
476
 
474
477
  > a {
478
+ color: @text-color;
475
479
  display: flex;
476
480
  padding: 3px 10px;
477
481
 
@@ -508,6 +512,10 @@ html[theme="default"] {
508
512
  }
509
513
  }
510
514
  }
515
+
516
+ > li.divider {
517
+ background-color: @dropdown-divider-bg;
518
+ }
511
519
  }
512
520
  }
513
521
 
package/src/screen.js CHANGED
@@ -1348,6 +1348,9 @@
1348
1348
  });
1349
1349
  },
1350
1350
  get current_record() {
1351
+ if (this.__current_record && this.__current_record.destroyed) {
1352
+ this.__current_record = null;
1353
+ }
1351
1354
  return this.__current_record;
1352
1355
  },
1353
1356
  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;
@@ -3516,7 +3523,11 @@ function eval_pyson(value){
3516
3523
  },
3517
3524
  set_readonly: function(readonly) {
3518
3525
  Sao.View.Form.One2Many._super.set_readonly.call(this, readonly);
3519
- this.prm.done(() => this._set_button_sensitive());
3526
+ if (this.prm.state() == 'pending') {
3527
+ this.prm.done(() => this._set_button_sensitive());
3528
+ } else {
3529
+ this._set_button_sensitive();
3530
+ }
3520
3531
  this._set_label_state();
3521
3532
  },
3522
3533
  set_required: function(required) {
@@ -3629,7 +3640,7 @@ function eval_pyson(value){
3629
3640
  display: function() {
3630
3641
  Sao.View.Form.One2Many._super.display.call(this);
3631
3642
 
3632
- return this.prm.then(() => {
3643
+ let display = function() {
3633
3644
  this._set_button_sensitive();
3634
3645
 
3635
3646
  var record = this.record;
@@ -3676,7 +3687,13 @@ function eval_pyson(value){
3676
3687
  .css('max-height', this.attributes.height + 'px');
3677
3688
  }
3678
3689
  return this.screen.display();
3679
- });
3690
+ }.bind(this);
3691
+
3692
+ if (this.prm.state() == 'pending') {
3693
+ return this.prm.then(() => display());
3694
+ } else {
3695
+ return display();
3696
+ }
3680
3697
  },
3681
3698
  focus: function() {
3682
3699
  if (this.attributes.add_remove) {
@@ -3953,7 +3970,11 @@ function eval_pyson(value){
3953
3970
  }
3954
3971
  var message = name + ' / ' + Sao.common.humanize(size);
3955
3972
  this.label.text(message).attr('title', message);
3956
- this.prm.done(() => this._set_button_sensitive());
3973
+ if (this.prm.state() == 'pending') {
3974
+ this.prm.done(() => this._set_button_sensitive());
3975
+ } else {
3976
+ this._set_button_sensitive();
3977
+ }
3957
3978
  },
3958
3979
  validate: function() {
3959
3980
  var prm = jQuery.Deferred();
@@ -3962,9 +3983,8 @@ function eval_pyson(value){
3962
3983
  if (record) {
3963
3984
  var fields = this.screen.current_view.get_fields();
3964
3985
  if (!record.validate(fields)) {
3965
- this.screen.display(true);
3966
- prm.reject();
3967
- return;
3986
+ this.screen.display(true).always(() => prm.reject());
3987
+ return prm;
3968
3988
  }
3969
3989
  if (this.screen.pre_validate) {
3970
3990
  return record.pre_validate().then(
@@ -4209,7 +4229,7 @@ function eval_pyson(value){
4209
4229
  display: function() {
4210
4230
  Sao.View.Form.Many2Many._super.display.call(this);
4211
4231
 
4212
- return this.prm.then(() => {
4232
+ let display = function() {
4213
4233
  var record = this.record;
4214
4234
  var field = this.field;
4215
4235
 
@@ -4231,7 +4251,13 @@ function eval_pyson(value){
4231
4251
  .css('max-height', this.attributes.height + 'px');
4232
4252
  }
4233
4253
  return this.screen.display();
4234
- });
4254
+ }.bind(this);
4255
+
4256
+ if (this.prm.state() == 'pending') {
4257
+ return this.prm.then(() => display());
4258
+ } else {
4259
+ return display();
4260
+ }
4235
4261
  },
4236
4262
  focus: function() {
4237
4263
  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
@@ -1528,6 +1528,13 @@
1528
1528
  this.display_size = this.group.length;
1529
1529
  this.display();
1530
1530
  }
1531
+ if (reset_view) {
1532
+ let current_path = this.record.get_path(this.group);
1533
+ current_path = current_path.map(function(e) {
1534
+ return e[1];
1535
+ });
1536
+ this.display([current_path]);
1537
+ }
1531
1538
  if (path.length > 1) {
1532
1539
  prm = this.rows[path[0]].expand_to_path(
1533
1540
  path.slice(1),
@@ -2762,7 +2769,7 @@
2762
2769
  fields, false, false)) {
2763
2770
  var value = cell.prop('checked');
2764
2771
  this.field.set_client(record, value);
2765
- if (record !== current_record) {
2772
+ if ((!this.group.parent) & (record !== current_record)) {
2766
2773
  // we can not rely on editable tree handler to save the row
2767
2774
  record.save();
2768
2775
  }
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() {