tryton-sao 7.4.11 → 7.4.13

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.4.13 - 2025-07-01
3
+ ---------------------------
4
+ * Bug fixes (see mercurial logs for details)
5
+
6
+
7
+ Version 7.4.12 - 2025-06-04
8
+ ---------------------------
9
+ * Bug fixes (see mercurial logs for details)
10
+
11
+
2
12
  Version 7.4.11 - 2025-05-15
3
13
  ---------------------------
4
14
  * Bug fixes (see mercurial logs for details)
@@ -8780,6 +8780,11 @@ html[theme="default"] .radio input[type="radio"]:focus,
8780
8780
  html[theme="default"] .radio-inline input[type="radio"]:focus {
8781
8781
  outline: none;
8782
8782
  }
8783
+ html[theme="default"] input[type="radio"]:focus:after,
8784
+ html[theme="default"] .radio input[type="radio"]:focus:after,
8785
+ html[theme="default"] .radio-inline input[type="radio"]:focus:after {
8786
+ border-color: #71bdc1 !important;
8787
+ }
8783
8788
  html[theme="default"] input[type="radio"]:before,
8784
8789
  html[theme="default"] .radio input[type="radio"]:before,
8785
8790
  html[theme="default"] .radio-inline input[type="radio"]:before,
@@ -8869,10 +8874,10 @@ html[theme="default"] .checkbox input[type="checkbox"]:focus,
8869
8874
  html[theme="default"] .checkbox-inline input[type="checkbox"]:focus {
8870
8875
  outline: none;
8871
8876
  }
8872
- html[theme="default"] input[type="checkbox"]:focus:before,
8873
- html[theme="default"] .checkbox input[type="checkbox"]:focus:before,
8874
- html[theme="default"] .checkbox-inline input[type="checkbox"]:focus:before {
8875
- border-color: #267f82;
8877
+ html[theme="default"] input[type="checkbox"]:focus:after,
8878
+ html[theme="default"] .checkbox input[type="checkbox"]:focus:after,
8879
+ html[theme="default"] .checkbox-inline input[type="checkbox"]:focus:after {
8880
+ border-color: #71bdc1 !important;
8876
8881
  }
8877
8882
  html[theme="default"] input[type="checkbox"]:before,
8878
8883
  html[theme="default"] .checkbox input[type="checkbox"]:before,
@@ -9214,7 +9219,8 @@ html[theme="default"] .carousel-caption h6 {
9214
9219
  float: none;
9215
9220
  }
9216
9221
  }
9217
- .btn-primary .icon {
9222
+ .btn-primary .icon,
9223
+ .bg-primary .icon {
9218
9224
  filter: brightness(0) invert(1);
9219
9225
  }
9220
9226
  .panel-heading a {
@@ -3,7 +3,7 @@
3
3
 
4
4
  /* eslint-disable no-redeclare */
5
5
  var Sao = {
6
- __version__: '7.4.11',
6
+ __version__: '7.4.13',
7
7
  };
8
8
  /* eslint-enable no-redeclare */
9
9
 
@@ -675,6 +675,7 @@ var Sao = {
675
675
  "Incompatible version of the server."),
676
676
  Sao.i18n.gettext("Version mismatch"));
677
677
  } else {
678
+ let url = window.location.hash.substr(1);
678
679
  Sao.Session.get_credentials()
679
680
  .then(function(session) {
680
681
  Sao.Session.current_session = session;
@@ -684,7 +685,7 @@ var Sao = {
684
685
  .then(function(preferences) {
685
686
  Sao.menu(preferences);
686
687
  Sao.user_menu(preferences);
687
- Sao.open_url();
688
+ Sao.open_url(url);
688
689
  Sao.Bus.listen();
689
690
  });
690
691
  }
@@ -1173,20 +1174,26 @@ var Sao = {
1173
1174
  shortcut: 'alt+shift+tab',
1174
1175
  label: Sao.i18n.gettext('Previous tab'),
1175
1176
  callback: function() {
1176
- Sao.Tab.previous_tab();
1177
+ if (!jQuery('body').children('.modal').length) {
1178
+ Sao.Tab.previous_tab();
1179
+ }
1177
1180
  },
1178
1181
  }, {
1179
1182
  shortcut: 'alt+tab',
1180
1183
  label: Sao.i18n.gettext('Next tab'),
1181
1184
  callback: function() {
1182
- Sao.Tab.next_tab();
1185
+ if (!jQuery('body').children('.modal').length) {
1186
+ Sao.Tab.next_tab();
1187
+ }
1183
1188
  },
1184
1189
  }, {
1185
1190
  shortcut: 'ctrl+k',
1186
1191
  label: Sao.i18n.gettext('Global search'),
1187
1192
  callback: function() {
1188
- jQuery('#main_navbar:hidden').collapse('show');
1189
- jQuery('#global-search-entry').focus();
1193
+ if (!jQuery('body').children('.modal').length) {
1194
+ jQuery('#main_navbar:hidden').collapse('show');
1195
+ jQuery('#global-search-entry').focus();
1196
+ }
1190
1197
  },
1191
1198
  }, {
1192
1199
  shortcut: 'f1',
@@ -5351,8 +5358,13 @@ var Sao = {
5351
5358
  }
5352
5359
  let { format } = new Intl.NumberFormat(
5353
5360
  Sao.i18n.BC47(Sao.i18n.getlang()));
5361
+ // use 10000 because some language (ex: es) add thousand
5362
+ // separator only after 9999
5363
+ let [, thousandSeparator] = /^10(.)?000/.exec(format(10000));
5354
5364
  let [, decimalSign] = /^0(.)1$/.exec(format(0.1));
5355
- return Number(string.replace(decimalSign, '.'));
5365
+ return Number(string
5366
+ .replace(new RegExp(thousandSeparator, 'g'), '')
5367
+ .replace(decimalSign, '.'));
5356
5368
  }
5357
5369
  var convert_selection = function() {
5358
5370
  if (typeof value == 'string') {
@@ -7700,16 +7712,19 @@ var Sao = {
7700
7712
  return null;
7701
7713
  }
7702
7714
  var type = '';
7703
- try {
7704
- var xml = data;
7705
- if (xml instanceof Uint8Array) {
7706
- xml = new TextDecoder().decode(data);
7707
- }
7708
- if (jQuery.parseXML(xml)) {
7715
+ var xml = data;
7716
+ if (xml instanceof Uint8Array) {
7717
+ xml = new TextDecoder().decode(data);
7718
+ }
7719
+ // simple test to avoid logging of parsing error
7720
+ if (/^\s*<[\s\S]+>\s*$/.test(xml.trim())) {
7721
+ let parser = new DOMParser();
7722
+ let doc = parser.parseFromString(xml, 'image/svg+xml');
7723
+ if (!doc.querySelector('parsererror')
7724
+ && (doc.documentElement.tagName.toLowerCase() === 'svg' ||
7725
+ doc.getElementsByTagName('svg').length > 0)) {
7709
7726
  type = 'image/svg+xml';
7710
7727
  }
7711
- } catch (e) {
7712
- // continue
7713
7728
  }
7714
7729
  var blob = new Blob([data], {type: type});
7715
7730
  return window.URL.createObjectURL(blob);
@@ -9478,7 +9493,6 @@ var Sao = {
9478
9493
  // XXX to remove once server domains are fixed
9479
9494
  value = null;
9480
9495
  }
9481
- var setdefault = true;
9482
9496
  var original_domain;
9483
9497
  if (!jQuery.isEmptyObject(record.group.domain)) {
9484
9498
  original_domain = inversion.merge(record.group.domain);
@@ -9486,20 +9500,12 @@ var Sao = {
9486
9500
  original_domain = inversion.merge(domain);
9487
9501
  }
9488
9502
  var domain_readonly = original_domain[0] == 'AND';
9503
+ let setdefault;
9489
9504
  if (leftpart.contains('.')) {
9490
- var recordpart = leftpart.split('.', 1)[0];
9491
- var localpart = leftpart.split('.', 1)[1];
9492
- var constraintfields = [];
9493
- if (domain_readonly) {
9494
- for (const leaf of inversion.localize_domain(
9495
- original_domain.slice(1))) {
9496
- constraintfields.push(leaf);
9497
- }
9498
- }
9499
- if ((localpart != 'id') ||
9500
- !~constraintfields.indexOf(recordpart)) {
9501
- setdefault = false;
9502
- }
9505
+ let localpart = leftpart.split('.').slice(1).join('.');
9506
+ setdefault = localpart == 'id';
9507
+ } else {
9508
+ setdefault = true;
9503
9509
  }
9504
9510
  if (setdefault && jQuery.isEmptyObject(pre_validate)) {
9505
9511
  this.set_client(record, value);
@@ -9763,12 +9769,23 @@ var Sao = {
9763
9769
  },
9764
9770
  apply_factor: function(record, value, factor) {
9765
9771
  if (value !== null) {
9772
+ // The default precision is the one used by value (before
9773
+ // applying the factor), per the ecmascript specification
9774
+ // it's the shortest representation of said value.
9775
+ // Once the factor is applied the number might become even
9776
+ // more inexact thus we should rely on the initial
9777
+ // precision + the effect factor will have
9778
+ // https://tc39.es/ecma262/multipage/ecmascript-data-types-and-values.html#sec-numeric-types-number-tostring
9779
+ let default_precision = (value.toString().split('.')[1] || '').length;
9780
+ default_precision += Math.ceil(Math.log10(factor));
9766
9781
  value /= factor;
9767
9782
  var digits = this.digits(record);
9768
9783
  if (digits) {
9769
9784
  // Round to avoid float precision error
9770
9785
  // after the division by factor
9771
9786
  value = value.toFixed(digits[1]);
9787
+ } else {
9788
+ value = value.toFixed(default_precision);
9772
9789
  }
9773
9790
  value = this.convert(value);
9774
9791
  }
@@ -11542,40 +11559,40 @@ var Sao = {
11542
11559
  });
11543
11560
  },
11544
11561
  modified_save: function() {
11545
- this.screen.save_tree_state();
11546
- this.screen.current_view.set_value();
11547
- if (this.screen.modified()) {
11548
- return Sao.common.sur_3b.run(
11549
- Sao.i18n.gettext('This record has been modified\n' +
11550
- 'do you want to save it?'))
11551
- .then(result => {
11552
- switch(result) {
11553
- case 'ok':
11554
- return this.save();
11555
- case 'ko':
11556
- var record_id = null;
11557
- if (this.screen.current_record) {
11558
- record_id = this.screen.current_record.id;
11559
- }
11560
- return this.reload(false).then(() => {
11561
- if (record_id !== null) {
11562
- if (record_id < 0) {
11563
- return jQuery.Deferred().reject(true);
11564
- }
11565
- else if (this.screen.current_record) {
11566
- if (record_id !=
11567
- this.screen.current_record.id) {
11568
- return jQuery.Deferred().reject();
11562
+ return this.screen.save_tree_state().then(() => {
11563
+ this.screen.current_view.set_value();
11564
+ if (this.screen.modified()) {
11565
+ return Sao.common.sur_3b.run(
11566
+ Sao.i18n.gettext('This record has been modified\n' +
11567
+ 'do you want to save it?'))
11568
+ .then(result => {
11569
+ switch(result) {
11570
+ case 'ok':
11571
+ return this.save();
11572
+ case 'ko':
11573
+ var record_id = null;
11574
+ if (this.screen.current_record) {
11575
+ record_id = this.screen.current_record.id;
11576
+ }
11577
+ return this.reload(false).then(() => {
11578
+ if (record_id !== null) {
11579
+ if (record_id < 0) {
11580
+ return jQuery.Deferred().reject(true);
11581
+ }
11582
+ else if (this.screen.current_record) {
11583
+ if (record_id !=
11584
+ this.screen.current_record.id) {
11585
+ return jQuery.Deferred().reject();
11586
+ }
11569
11587
  }
11570
11588
  }
11571
- }
11572
- });
11573
- default:
11574
- return jQuery.Deferred().reject();
11575
- }
11576
- });
11577
- }
11578
- return jQuery.when();
11589
+ });
11590
+ default:
11591
+ return jQuery.Deferred().reject();
11592
+ }
11593
+ });
11594
+ }
11595
+ });
11579
11596
  },
11580
11597
  new_: function() {
11581
11598
  if (!Sao.common.MODELACCESS.get(this.screen.model_name).create) {
@@ -11589,24 +11606,29 @@ var Sao = {
11589
11606
  });
11590
11607
  },
11591
11608
  save: function(tab) {
11609
+ let prm;
11592
11610
  if (tab) {
11593
11611
  // Called from button so we must save the tree state
11594
- this.screen.save_tree_state();
11595
- }
11596
- var access = Sao.common.MODELACCESS.get(this.screen.model_name);
11597
- if (this.screen.readonly || !(access.write || access.create)) {
11598
- return jQuery.Deferred().reject();
11612
+ prm = this.screen.save_tree_state();
11613
+ } else {
11614
+ prm = jQuery.when();
11599
11615
  }
11600
- return this.screen.save_current().then(
11601
- () => {
11602
- this.info_bar.add(
11603
- Sao.i18n.gettext('Record saved.'), 'info');
11604
- this.screen.count_tab_domain(true);
11605
- }, () => {
11606
- this.info_bar.add(
11607
- this.screen.invalid_message(), 'danger');
11616
+ prm.then(() => {
11617
+ var access = Sao.common.MODELACCESS.get(this.screen.model_name);
11618
+ if (this.screen.readonly || !(access.write || access.create)) {
11608
11619
  return jQuery.Deferred().reject();
11609
- });
11620
+ }
11621
+ return this.screen.save_current().then(
11622
+ () => {
11623
+ this.info_bar.add(
11624
+ Sao.i18n.gettext('Record saved.'), 'info');
11625
+ this.screen.count_tab_domain(true);
11626
+ }, () => {
11627
+ this.info_bar.add(
11628
+ this.screen.invalid_message(), 'danger');
11629
+ return jQuery.Deferred().reject();
11630
+ });
11631
+ });
11610
11632
  },
11611
11633
  switch_: function() {
11612
11634
  return this.modified_save().then(() => this.screen.switch_view());
@@ -11645,8 +11667,7 @@ var Sao = {
11645
11667
  if (test_modified) {
11646
11668
  return this.modified_save().then(reload);
11647
11669
  } else {
11648
- this.screen.save_tree_state(false);
11649
- return reload();
11670
+ return this.screen.save_tree_state(false).then(reload);
11650
11671
  }
11651
11672
  },
11652
11673
  copy: function() {
@@ -18835,7 +18856,6 @@ function eval_pyson(value){
18835
18856
  return prm;
18836
18857
  },
18837
18858
  set_value: function() {
18838
- this.screen.save_tree_state();
18839
18859
  if (this.screen.modified()) { // TODO check if required
18840
18860
  this.view.screen.record_modified(false);
18841
18861
  }
@@ -22589,12 +22609,13 @@ function eval_pyson(value){
22589
22609
  current_record = this.tree.screen.current_record;
22590
22610
  this.tree.select_records(current_record, this.record);
22591
22611
  } else {
22612
+ let selected = this.is_selected();
22592
22613
  if (!(event_.ctrlKey || event_.metaKey) ||
22593
22614
  this.tree.selection_mode ==
22594
22615
  Sao.common.SELECTION_SINGLE) {
22595
22616
  this.tree.select_records(null, null);
22596
22617
  }
22597
- this.set_selection(!this.is_selected());
22618
+ this.set_selection(!selected);
22598
22619
  }
22599
22620
  this.selection_changed();
22600
22621
  if (current_record) {
@@ -22625,6 +22646,9 @@ function eval_pyson(value){
22625
22646
  },
22626
22647
  selection_changed: function() {
22627
22648
  var is_selected = this.is_selected();
22649
+ if (this.tree.selection_mode == Sao.common.SELECTION_SINGLE) {
22650
+ this.tree.select_records(null, null);
22651
+ }
22628
22652
  this.set_selection(is_selected);
22629
22653
  if (is_selected) {
22630
22654
  this.tree.select_changed(this.record);
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.4.11",
5
+ "version": "7.4.13",
6
6
  "homepage": "https://www.tryton.org/",
7
7
  "author": {
8
8
  "name": "Tryton"
package/src/common.js CHANGED
@@ -2019,8 +2019,13 @@
2019
2019
  }
2020
2020
  let { format } = new Intl.NumberFormat(
2021
2021
  Sao.i18n.BC47(Sao.i18n.getlang()));
2022
+ // use 10000 because some language (ex: es) add thousand
2023
+ // separator only after 9999
2024
+ let [, thousandSeparator] = /^10(.)?000/.exec(format(10000));
2022
2025
  let [, decimalSign] = /^0(.)1$/.exec(format(0.1));
2023
- return Number(string.replace(decimalSign, '.'));
2026
+ return Number(string
2027
+ .replace(new RegExp(thousandSeparator, 'g'), '')
2028
+ .replace(decimalSign, '.'));
2024
2029
  }
2025
2030
  var convert_selection = function() {
2026
2031
  if (typeof value == 'string') {
@@ -4368,16 +4373,19 @@
4368
4373
  return null;
4369
4374
  }
4370
4375
  var type = '';
4371
- try {
4372
- var xml = data;
4373
- if (xml instanceof Uint8Array) {
4374
- xml = new TextDecoder().decode(data);
4375
- }
4376
- if (jQuery.parseXML(xml)) {
4376
+ var xml = data;
4377
+ if (xml instanceof Uint8Array) {
4378
+ xml = new TextDecoder().decode(data);
4379
+ }
4380
+ // simple test to avoid logging of parsing error
4381
+ if (/^\s*<[\s\S]+>\s*$/.test(xml.trim())) {
4382
+ let parser = new DOMParser();
4383
+ let doc = parser.parseFromString(xml, 'image/svg+xml');
4384
+ if (!doc.querySelector('parsererror')
4385
+ && (doc.documentElement.tagName.toLowerCase() === 'svg' ||
4386
+ doc.getElementsByTagName('svg').length > 0)) {
4377
4387
  type = 'image/svg+xml';
4378
4388
  }
4379
- } catch (e) {
4380
- // continue
4381
4389
  }
4382
4390
  var blob = new Blob([data], {type: type});
4383
4391
  return window.URL.createObjectURL(blob);
package/src/model.js CHANGED
@@ -1753,7 +1753,6 @@
1753
1753
  // XXX to remove once server domains are fixed
1754
1754
  value = null;
1755
1755
  }
1756
- var setdefault = true;
1757
1756
  var original_domain;
1758
1757
  if (!jQuery.isEmptyObject(record.group.domain)) {
1759
1758
  original_domain = inversion.merge(record.group.domain);
@@ -1761,20 +1760,12 @@
1761
1760
  original_domain = inversion.merge(domain);
1762
1761
  }
1763
1762
  var domain_readonly = original_domain[0] == 'AND';
1763
+ let setdefault;
1764
1764
  if (leftpart.contains('.')) {
1765
- var recordpart = leftpart.split('.', 1)[0];
1766
- var localpart = leftpart.split('.', 1)[1];
1767
- var constraintfields = [];
1768
- if (domain_readonly) {
1769
- for (const leaf of inversion.localize_domain(
1770
- original_domain.slice(1))) {
1771
- constraintfields.push(leaf);
1772
- }
1773
- }
1774
- if ((localpart != 'id') ||
1775
- !~constraintfields.indexOf(recordpart)) {
1776
- setdefault = false;
1777
- }
1765
+ let localpart = leftpart.split('.').slice(1).join('.');
1766
+ setdefault = localpart == 'id';
1767
+ } else {
1768
+ setdefault = true;
1778
1769
  }
1779
1770
  if (setdefault && jQuery.isEmptyObject(pre_validate)) {
1780
1771
  this.set_client(record, value);
@@ -2038,12 +2029,23 @@
2038
2029
  },
2039
2030
  apply_factor: function(record, value, factor) {
2040
2031
  if (value !== null) {
2032
+ // The default precision is the one used by value (before
2033
+ // applying the factor), per the ecmascript specification
2034
+ // it's the shortest representation of said value.
2035
+ // Once the factor is applied the number might become even
2036
+ // more inexact thus we should rely on the initial
2037
+ // precision + the effect factor will have
2038
+ // https://tc39.es/ecma262/multipage/ecmascript-data-types-and-values.html#sec-numeric-types-number-tostring
2039
+ let default_precision = (value.toString().split('.')[1] || '').length;
2040
+ default_precision += Math.ceil(Math.log10(factor));
2041
2041
  value /= factor;
2042
2042
  var digits = this.digits(record);
2043
2043
  if (digits) {
2044
2044
  // Round to avoid float precision error
2045
2045
  // after the division by factor
2046
2046
  value = value.toFixed(digits[1]);
2047
+ } else {
2048
+ value = value.toFixed(default_precision);
2047
2049
  }
2048
2050
  value = this.convert(value);
2049
2051
  }
package/src/sao.js CHANGED
@@ -3,7 +3,7 @@
3
3
 
4
4
  /* eslint-disable no-redeclare */
5
5
  var Sao = {
6
- __version__: '7.4.11',
6
+ __version__: '7.4.13',
7
7
  };
8
8
  /* eslint-enable no-redeclare */
9
9
 
@@ -675,6 +675,7 @@ var Sao = {
675
675
  "Incompatible version of the server."),
676
676
  Sao.i18n.gettext("Version mismatch"));
677
677
  } else {
678
+ let url = window.location.hash.substr(1);
678
679
  Sao.Session.get_credentials()
679
680
  .then(function(session) {
680
681
  Sao.Session.current_session = session;
@@ -684,7 +685,7 @@ var Sao = {
684
685
  .then(function(preferences) {
685
686
  Sao.menu(preferences);
686
687
  Sao.user_menu(preferences);
687
- Sao.open_url();
688
+ Sao.open_url(url);
688
689
  Sao.Bus.listen();
689
690
  });
690
691
  }
@@ -1173,20 +1174,26 @@ var Sao = {
1173
1174
  shortcut: 'alt+shift+tab',
1174
1175
  label: Sao.i18n.gettext('Previous tab'),
1175
1176
  callback: function() {
1176
- Sao.Tab.previous_tab();
1177
+ if (!jQuery('body').children('.modal').length) {
1178
+ Sao.Tab.previous_tab();
1179
+ }
1177
1180
  },
1178
1181
  }, {
1179
1182
  shortcut: 'alt+tab',
1180
1183
  label: Sao.i18n.gettext('Next tab'),
1181
1184
  callback: function() {
1182
- Sao.Tab.next_tab();
1185
+ if (!jQuery('body').children('.modal').length) {
1186
+ Sao.Tab.next_tab();
1187
+ }
1183
1188
  },
1184
1189
  }, {
1185
1190
  shortcut: 'ctrl+k',
1186
1191
  label: Sao.i18n.gettext('Global search'),
1187
1192
  callback: function() {
1188
- jQuery('#main_navbar:hidden').collapse('show');
1189
- jQuery('#global-search-entry').focus();
1193
+ if (!jQuery('body').children('.modal').length) {
1194
+ jQuery('#main_navbar:hidden').collapse('show');
1195
+ jQuery('#global-search-entry').focus();
1196
+ }
1190
1197
  },
1191
1198
  }, {
1192
1199
  shortcut: 'f1',
package/src/sao.less CHANGED
@@ -108,7 +108,7 @@ html[theme="default"] {
108
108
  }
109
109
  }
110
110
 
111
- .btn-primary {
111
+ .btn-primary, .bg-primary {
112
112
  .icon {
113
113
  filter: brightness(0) invert(1);
114
114
  }
package/src/tab.js CHANGED
@@ -830,40 +830,40 @@
830
830
  });
831
831
  },
832
832
  modified_save: function() {
833
- this.screen.save_tree_state();
834
- this.screen.current_view.set_value();
835
- if (this.screen.modified()) {
836
- return Sao.common.sur_3b.run(
837
- Sao.i18n.gettext('This record has been modified\n' +
838
- 'do you want to save it?'))
839
- .then(result => {
840
- switch(result) {
841
- case 'ok':
842
- return this.save();
843
- case 'ko':
844
- var record_id = null;
845
- if (this.screen.current_record) {
846
- record_id = this.screen.current_record.id;
847
- }
848
- return this.reload(false).then(() => {
849
- if (record_id !== null) {
850
- if (record_id < 0) {
851
- return jQuery.Deferred().reject(true);
852
- }
853
- else if (this.screen.current_record) {
854
- if (record_id !=
855
- this.screen.current_record.id) {
856
- return jQuery.Deferred().reject();
833
+ return this.screen.save_tree_state().then(() => {
834
+ this.screen.current_view.set_value();
835
+ if (this.screen.modified()) {
836
+ return Sao.common.sur_3b.run(
837
+ Sao.i18n.gettext('This record has been modified\n' +
838
+ 'do you want to save it?'))
839
+ .then(result => {
840
+ switch(result) {
841
+ case 'ok':
842
+ return this.save();
843
+ case 'ko':
844
+ var record_id = null;
845
+ if (this.screen.current_record) {
846
+ record_id = this.screen.current_record.id;
847
+ }
848
+ return this.reload(false).then(() => {
849
+ if (record_id !== null) {
850
+ if (record_id < 0) {
851
+ return jQuery.Deferred().reject(true);
852
+ }
853
+ else if (this.screen.current_record) {
854
+ if (record_id !=
855
+ this.screen.current_record.id) {
856
+ return jQuery.Deferred().reject();
857
+ }
857
858
  }
858
859
  }
859
- }
860
- });
861
- default:
862
- return jQuery.Deferred().reject();
863
- }
864
- });
865
- }
866
- return jQuery.when();
860
+ });
861
+ default:
862
+ return jQuery.Deferred().reject();
863
+ }
864
+ });
865
+ }
866
+ });
867
867
  },
868
868
  new_: function() {
869
869
  if (!Sao.common.MODELACCESS.get(this.screen.model_name).create) {
@@ -877,24 +877,29 @@
877
877
  });
878
878
  },
879
879
  save: function(tab) {
880
+ let prm;
880
881
  if (tab) {
881
882
  // Called from button so we must save the tree state
882
- this.screen.save_tree_state();
883
- }
884
- var access = Sao.common.MODELACCESS.get(this.screen.model_name);
885
- if (this.screen.readonly || !(access.write || access.create)) {
886
- return jQuery.Deferred().reject();
883
+ prm = this.screen.save_tree_state();
884
+ } else {
885
+ prm = jQuery.when();
887
886
  }
888
- return this.screen.save_current().then(
889
- () => {
890
- this.info_bar.add(
891
- Sao.i18n.gettext('Record saved.'), 'info');
892
- this.screen.count_tab_domain(true);
893
- }, () => {
894
- this.info_bar.add(
895
- this.screen.invalid_message(), 'danger');
887
+ prm.then(() => {
888
+ var access = Sao.common.MODELACCESS.get(this.screen.model_name);
889
+ if (this.screen.readonly || !(access.write || access.create)) {
896
890
  return jQuery.Deferred().reject();
897
- });
891
+ }
892
+ return this.screen.save_current().then(
893
+ () => {
894
+ this.info_bar.add(
895
+ Sao.i18n.gettext('Record saved.'), 'info');
896
+ this.screen.count_tab_domain(true);
897
+ }, () => {
898
+ this.info_bar.add(
899
+ this.screen.invalid_message(), 'danger');
900
+ return jQuery.Deferred().reject();
901
+ });
902
+ });
898
903
  },
899
904
  switch_: function() {
900
905
  return this.modified_save().then(() => this.screen.switch_view());
@@ -933,8 +938,7 @@
933
938
  if (test_modified) {
934
939
  return this.modified_save().then(reload);
935
940
  } else {
936
- this.screen.save_tree_state(false);
937
- return reload();
941
+ return this.screen.save_tree_state(false).then(reload);
938
942
  }
939
943
  },
940
944
  copy: function() {
package/src/theme.less CHANGED
@@ -240,6 +240,10 @@ input[type="radio"],
240
240
  outline: none;
241
241
  }
242
242
 
243
+ &:focus:after {
244
+ border-color: @brand-info !important;
245
+ }
246
+
243
247
  &:before,
244
248
  &:after {
245
249
  content: "";
@@ -303,8 +307,8 @@ input[type="checkbox"],
303
307
  outline: none;
304
308
  }
305
309
 
306
- &:focus:before {
307
- border-color: @brand-primary;
310
+ &:focus:after {
311
+ border-color: @brand-info !important;
308
312
  }
309
313
 
310
314
  &:before {
package/src/view/form.js CHANGED
@@ -3908,7 +3908,6 @@ function eval_pyson(value){
3908
3908
  return prm;
3909
3909
  },
3910
3910
  set_value: function() {
3911
- this.screen.save_tree_state();
3912
3911
  if (this.screen.modified()) { // TODO check if required
3913
3912
  this.view.screen.record_modified(false);
3914
3913
  }
package/src/view/tree.js CHANGED
@@ -1854,12 +1854,13 @@
1854
1854
  current_record = this.tree.screen.current_record;
1855
1855
  this.tree.select_records(current_record, this.record);
1856
1856
  } else {
1857
+ let selected = this.is_selected();
1857
1858
  if (!(event_.ctrlKey || event_.metaKey) ||
1858
1859
  this.tree.selection_mode ==
1859
1860
  Sao.common.SELECTION_SINGLE) {
1860
1861
  this.tree.select_records(null, null);
1861
1862
  }
1862
- this.set_selection(!this.is_selected());
1863
+ this.set_selection(!selected);
1863
1864
  }
1864
1865
  this.selection_changed();
1865
1866
  if (current_record) {
@@ -1890,6 +1891,9 @@
1890
1891
  },
1891
1892
  selection_changed: function() {
1892
1893
  var is_selected = this.is_selected();
1894
+ if (this.tree.selection_mode == Sao.common.SELECTION_SINGLE) {
1895
+ this.tree.select_records(null, null);
1896
+ }
1893
1897
  this.set_selection(is_selected);
1894
1898
  if (is_selected) {
1895
1899
  this.tree.select_changed(this.record);