tryton-sao 7.0.30 → 7.0.32

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.0.32 - 2025-07-01
3
+ ---------------------------
4
+ * Bug fixes (see mercurial logs for details)
5
+
6
+
7
+ Version 7.0.31 - 2025-06-04
8
+ ---------------------------
9
+ * Bug fixes (see mercurial logs for details)
10
+
11
+
2
12
  Version 7.0.30 - 2025-05-15
3
13
  ---------------------------
4
14
  * Bug fixes (see mercurial logs for details)
@@ -8777,6 +8777,11 @@ html[theme="default"] .radio input[type="radio"]:focus,
8777
8777
  html[theme="default"] .radio-inline input[type="radio"]:focus {
8778
8778
  outline: none;
8779
8779
  }
8780
+ html[theme="default"] input[type="radio"]:focus:after,
8781
+ html[theme="default"] .radio input[type="radio"]:focus:after,
8782
+ html[theme="default"] .radio-inline input[type="radio"]:focus:after {
8783
+ border-color: #71bdc1 !important;
8784
+ }
8780
8785
  html[theme="default"] input[type="radio"]:before,
8781
8786
  html[theme="default"] .radio input[type="radio"]:before,
8782
8787
  html[theme="default"] .radio-inline input[type="radio"]:before,
@@ -8866,10 +8871,10 @@ html[theme="default"] .checkbox input[type="checkbox"]:focus,
8866
8871
  html[theme="default"] .checkbox-inline input[type="checkbox"]:focus {
8867
8872
  outline: none;
8868
8873
  }
8869
- html[theme="default"] input[type="checkbox"]:focus:before,
8870
- html[theme="default"] .checkbox input[type="checkbox"]:focus:before,
8871
- html[theme="default"] .checkbox-inline input[type="checkbox"]:focus:before {
8872
- border-color: #267f82;
8874
+ html[theme="default"] input[type="checkbox"]:focus:after,
8875
+ html[theme="default"] .checkbox input[type="checkbox"]:focus:after,
8876
+ html[theme="default"] .checkbox-inline input[type="checkbox"]:focus:after {
8877
+ border-color: #71bdc1 !important;
8873
8878
  }
8874
8879
  html[theme="default"] input[type="checkbox"]:before,
8875
8880
  html[theme="default"] .checkbox input[type="checkbox"]:before,
@@ -9201,7 +9206,8 @@ html[theme="default"] .carousel-caption h6 {
9201
9206
  float: none;
9202
9207
  }
9203
9208
  }
9204
- .btn-primary .icon {
9209
+ .btn-primary .icon,
9210
+ .bg-primary .icon {
9205
9211
  filter: brightness(0) invert(1);
9206
9212
  }
9207
9213
  .panel-heading a {
@@ -3,7 +3,7 @@
3
3
 
4
4
  /* eslint-disable no-redeclare */
5
5
  var Sao = {
6
- __version__: '7.0.30',
6
+ __version__: '7.0.32',
7
7
  };
8
8
  /* eslint-enable no-redeclare */
9
9
 
@@ -630,6 +630,7 @@ var Sao = {
630
630
  "Incompatible version of the server."),
631
631
  Sao.i18n.gettext("Version mismatch"));
632
632
  } else {
633
+ let url = window.location.hash.substr(1);
633
634
  Sao.Session.get_credentials()
634
635
  .then(function(session) {
635
636
  Sao.Session.current_session = session;
@@ -639,7 +640,7 @@ var Sao = {
639
640
  .then(function(preferences) {
640
641
  Sao.menu(preferences);
641
642
  Sao.user_menu(preferences);
642
- Sao.open_url();
643
+ Sao.open_url(url);
643
644
  Sao.Bus.listen();
644
645
  });
645
646
  }
@@ -1135,20 +1136,26 @@ var Sao = {
1135
1136
  shortcut: 'alt+shift+tab',
1136
1137
  label: Sao.i18n.gettext('Previous tab'),
1137
1138
  callback: function() {
1138
- Sao.Tab.previous_tab();
1139
+ if (!jQuery('body').children('.modal').length) {
1140
+ Sao.Tab.previous_tab();
1141
+ }
1139
1142
  },
1140
1143
  }, {
1141
1144
  shortcut: 'alt+tab',
1142
1145
  label: Sao.i18n.gettext('Next tab'),
1143
1146
  callback: function() {
1144
- Sao.Tab.next_tab();
1147
+ if (!jQuery('body').children('.modal').length) {
1148
+ Sao.Tab.next_tab();
1149
+ }
1145
1150
  },
1146
1151
  }, {
1147
1152
  shortcut: 'ctrl+k',
1148
1153
  label: Sao.i18n.gettext('Global search'),
1149
1154
  callback: function() {
1150
- jQuery('#main_navbar:hidden').collapse('show');
1151
- jQuery('#global-search-entry').focus();
1155
+ if (!jQuery('body').children('.modal').length) {
1156
+ jQuery('#main_navbar:hidden').collapse('show');
1157
+ jQuery('#global-search-entry').focus();
1158
+ }
1152
1159
  },
1153
1160
  }, {
1154
1161
  shortcut: 'f1',
@@ -5205,8 +5212,13 @@ var Sao = {
5205
5212
  }
5206
5213
  let { format } = new Intl.NumberFormat(
5207
5214
  Sao.i18n.BC47(Sao.i18n.getlang()));
5215
+ // use 10000 because some language (ex: es) add thousand
5216
+ // separator only after 9999
5217
+ let [, thousandSeparator] = /^10(.)?000/.exec(format(10000));
5208
5218
  let [, decimalSign] = /^0(.)1$/.exec(format(0.1));
5209
- return Number(string.replace(decimalSign, '.'));
5219
+ return Number(string
5220
+ .replace(new RegExp(thousandSeparator, 'g'), '')
5221
+ .replace(decimalSign, '.'));
5210
5222
  }
5211
5223
  var convert_selection = function() {
5212
5224
  if (typeof value == 'string') {
@@ -7576,15 +7588,19 @@ var Sao = {
7576
7588
  return null;
7577
7589
  }
7578
7590
  var type = '';
7579
- try {
7580
- var xml = data;
7581
- if (xml instanceof Uint8Array) {
7582
- xml = new TextDecoder().decode(data);
7583
- }
7584
- if (jQuery.parseXML(xml)) {
7591
+ var xml = data;
7592
+ if (xml instanceof Uint8Array) {
7593
+ xml = new TextDecoder().decode(data);
7594
+ }
7595
+ // simple test to avoid logging of parsing error
7596
+ if (/^\s*<[\s\S]+>\s*$/.test(xml.trim())) {
7597
+ let parser = new DOMParser();
7598
+ let doc = parser.parseFromString(xml, 'image/svg+xml');
7599
+ if (!doc.querySelector('parsererror')
7600
+ && (doc.documentElement.tagName.toLowerCase() === 'svg' ||
7601
+ doc.getElementsByTagName('svg').length > 0)) {
7585
7602
  type = 'image/svg+xml';
7586
7603
  }
7587
- } catch (e) {
7588
7604
  }
7589
7605
  var blob = new Blob([data], {type: type});
7590
7606
  return window.URL.createObjectURL(blob);
@@ -9359,7 +9375,6 @@ var Sao = {
9359
9375
  // XXX to remove once server domains are fixed
9360
9376
  value = null;
9361
9377
  }
9362
- var setdefault = true;
9363
9378
  var original_domain;
9364
9379
  if (!jQuery.isEmptyObject(record.group.domain)) {
9365
9380
  original_domain = inversion.merge(record.group.domain);
@@ -9367,20 +9382,12 @@ var Sao = {
9367
9382
  original_domain = inversion.merge(domain);
9368
9383
  }
9369
9384
  var domain_readonly = original_domain[0] == 'AND';
9385
+ let setdefault;
9370
9386
  if (leftpart.contains('.')) {
9371
- var recordpart = leftpart.split('.', 1)[0];
9372
- var localpart = leftpart.split('.', 1)[1];
9373
- var constraintfields = [];
9374
- if (domain_readonly) {
9375
- for (const leaf of inversion.localize_domain(
9376
- original_domain.slice(1))) {
9377
- constraintfields.push(leaf);
9378
- }
9379
- }
9380
- if ((localpart != 'id') ||
9381
- !~constraintfields.indexOf(recordpart)) {
9382
- setdefault = false;
9383
- }
9387
+ let localpart = leftpart.split('.').slice(1).join('.');
9388
+ setdefault = localpart == 'id';
9389
+ } else {
9390
+ setdefault = true;
9384
9391
  }
9385
9392
  if (setdefault && jQuery.isEmptyObject(pre_validate)) {
9386
9393
  this.set_client(record, value);
@@ -9644,12 +9651,23 @@ var Sao = {
9644
9651
  },
9645
9652
  apply_factor: function(record, value, factor) {
9646
9653
  if (value !== null) {
9654
+ // The default precision is the one used by value (before
9655
+ // applying the factor), per the ecmascript specification
9656
+ // it's the shortest representation of said value.
9657
+ // Once the factor is applied the number might become even
9658
+ // more inexact thus we should rely on the initial
9659
+ // precision + the effect factor will have
9660
+ // https://tc39.es/ecma262/multipage/ecmascript-data-types-and-values.html#sec-numeric-types-number-tostring
9661
+ let default_precision = (value.toString().split('.')[1] || '').length;
9662
+ default_precision += Math.ceil(Math.log10(factor));
9647
9663
  value /= factor;
9648
9664
  var digits = this.digits(record);
9649
9665
  if (digits) {
9650
9666
  // Round to avoid float precision error
9651
9667
  // after the division by factor
9652
9668
  value = value.toFixed(digits[1]);
9669
+ } else {
9670
+ value = value.toFixed(default_precision);
9653
9671
  }
9654
9672
  value = this.convert(value);
9655
9673
  }
@@ -11340,40 +11358,40 @@ var Sao = {
11340
11358
  });
11341
11359
  },
11342
11360
  modified_save: function() {
11343
- this.screen.save_tree_state();
11344
- this.screen.current_view.set_value();
11345
- if (this.screen.modified()) {
11346
- return Sao.common.sur_3b.run(
11347
- Sao.i18n.gettext('This record has been modified\n' +
11348
- 'do you want to save it?'))
11349
- .then(result => {
11350
- switch(result) {
11351
- case 'ok':
11352
- return this.save();
11353
- case 'ko':
11354
- var record_id = null;
11355
- if (this.screen.current_record) {
11356
- record_id = this.screen.current_record.id;
11357
- }
11358
- return this.reload(false).then(() => {
11359
- if (record_id !== null) {
11360
- if (record_id < 0) {
11361
- return jQuery.Deferred().reject(true);
11362
- }
11363
- else if (this.screen.current_record) {
11364
- if (record_id !=
11365
- this.screen.current_record.id) {
11366
- return jQuery.Deferred().reject();
11361
+ return this.screen.save_tree_state().then(() => {
11362
+ this.screen.current_view.set_value();
11363
+ if (this.screen.modified()) {
11364
+ return Sao.common.sur_3b.run(
11365
+ Sao.i18n.gettext('This record has been modified\n' +
11366
+ 'do you want to save it?'))
11367
+ .then(result => {
11368
+ switch(result) {
11369
+ case 'ok':
11370
+ return this.save();
11371
+ case 'ko':
11372
+ var record_id = null;
11373
+ if (this.screen.current_record) {
11374
+ record_id = this.screen.current_record.id;
11375
+ }
11376
+ return this.reload(false).then(() => {
11377
+ if (record_id !== null) {
11378
+ if (record_id < 0) {
11379
+ return jQuery.Deferred().reject(true);
11380
+ }
11381
+ else if (this.screen.current_record) {
11382
+ if (record_id !=
11383
+ this.screen.current_record.id) {
11384
+ return jQuery.Deferred().reject();
11385
+ }
11367
11386
  }
11368
11387
  }
11369
- }
11370
- });
11371
- default:
11372
- return jQuery.Deferred().reject();
11373
- }
11374
- });
11375
- }
11376
- return jQuery.when();
11388
+ });
11389
+ default:
11390
+ return jQuery.Deferred().reject();
11391
+ }
11392
+ });
11393
+ }
11394
+ });
11377
11395
  },
11378
11396
  new_: function() {
11379
11397
  if (!Sao.common.MODELACCESS.get(this.screen.model_name).create) {
@@ -11387,24 +11405,29 @@ var Sao = {
11387
11405
  });
11388
11406
  },
11389
11407
  save: function(tab) {
11408
+ let prm;
11390
11409
  if (tab) {
11391
11410
  // Called from button so we must save the tree state
11392
- this.screen.save_tree_state();
11393
- }
11394
- var access = Sao.common.MODELACCESS.get(this.screen.model_name);
11395
- if (this.screen.readonly || !(access.write || access.create)) {
11396
- return jQuery.Deferred().reject();
11411
+ prm = this.screen.save_tree_state();
11412
+ } else {
11413
+ prm = jQuery.when();
11397
11414
  }
11398
- return this.screen.save_current().then(
11399
- () => {
11400
- this.info_bar.add(
11401
- Sao.i18n.gettext('Record saved.'), 'info');
11402
- this.screen.count_tab_domain(true);
11403
- }, () => {
11404
- this.info_bar.add(
11405
- this.screen.invalid_message(), 'danger');
11415
+ prm.then(() => {
11416
+ var access = Sao.common.MODELACCESS.get(this.screen.model_name);
11417
+ if (this.screen.readonly || !(access.write || access.create)) {
11406
11418
  return jQuery.Deferred().reject();
11407
- });
11419
+ }
11420
+ return this.screen.save_current().then(
11421
+ () => {
11422
+ this.info_bar.add(
11423
+ Sao.i18n.gettext('Record saved.'), 'info');
11424
+ this.screen.count_tab_domain(true);
11425
+ }, () => {
11426
+ this.info_bar.add(
11427
+ this.screen.invalid_message(), 'danger');
11428
+ return jQuery.Deferred().reject();
11429
+ });
11430
+ });
11408
11431
  },
11409
11432
  switch_: function() {
11410
11433
  return this.modified_save().then(() => this.screen.switch_view());
@@ -11443,8 +11466,7 @@ var Sao = {
11443
11466
  if (test_modified) {
11444
11467
  return this.modified_save().then(reload);
11445
11468
  } else {
11446
- this.screen.save_tree_state(false);
11447
- return reload();
11469
+ return this.screen.save_tree_state(false).then(reload);
11448
11470
  }
11449
11471
  },
11450
11472
  copy: function() {
@@ -18478,7 +18500,6 @@ function eval_pyson(value){
18478
18500
  return prm;
18479
18501
  },
18480
18502
  set_value: function() {
18481
- this.screen.save_tree_state();
18482
18503
  if (this.screen.modified()) { // TODO check if required
18483
18504
  this.view.screen.record_modified(false);
18484
18505
  }
@@ -22115,12 +22136,13 @@ function eval_pyson(value){
22115
22136
  current_record = this.tree.screen.current_record;
22116
22137
  this.tree.select_records(current_record, this.record);
22117
22138
  } else {
22139
+ let selected = this.is_selected();
22118
22140
  if (!(event_.ctrlKey || event_.metaKey) ||
22119
22141
  this.tree.selection_mode ==
22120
22142
  Sao.common.SELECTION_SINGLE) {
22121
22143
  this.tree.select_records(null, null);
22122
22144
  }
22123
- this.set_selection(!this.is_selected());
22145
+ this.set_selection(!selected);
22124
22146
  }
22125
22147
  this.selection_changed();
22126
22148
  if (current_record) {
@@ -22151,6 +22173,9 @@ function eval_pyson(value){
22151
22173
  },
22152
22174
  selection_changed: function() {
22153
22175
  var is_selected = this.is_selected();
22176
+ if (this.tree.selection_mode == Sao.common.SELECTION_SINGLE) {
22177
+ this.tree.select_records(null, null);
22178
+ }
22154
22179
  this.set_selection(is_selected);
22155
22180
  if (is_selected) {
22156
22181
  this.tree.select_changed(this.record);
@@ -8777,6 +8777,11 @@ html[theme="default"] .radio input[type="radio"]:focus,
8777
8777
  html[theme="default"] .radio-inline input[type="radio"]:focus {
8778
8778
  outline: none;
8779
8779
  }
8780
+ html[theme="default"] input[type="radio"]:focus:after,
8781
+ html[theme="default"] .radio input[type="radio"]:focus:after,
8782
+ html[theme="default"] .radio-inline input[type="radio"]:focus:after {
8783
+ border-color: #71bdc1 !important;
8784
+ }
8780
8785
  html[theme="default"] input[type="radio"]:before,
8781
8786
  html[theme="default"] .radio input[type="radio"]:before,
8782
8787
  html[theme="default"] .radio-inline input[type="radio"]:before,
@@ -8866,10 +8871,10 @@ html[theme="default"] .checkbox input[type="checkbox"]:focus,
8866
8871
  html[theme="default"] .checkbox-inline input[type="checkbox"]:focus {
8867
8872
  outline: none;
8868
8873
  }
8869
- html[theme="default"] input[type="checkbox"]:focus:before,
8870
- html[theme="default"] .checkbox input[type="checkbox"]:focus:before,
8871
- html[theme="default"] .checkbox-inline input[type="checkbox"]:focus:before {
8872
- border-color: #267f82;
8874
+ html[theme="default"] input[type="checkbox"]:focus:after,
8875
+ html[theme="default"] .checkbox input[type="checkbox"]:focus:after,
8876
+ html[theme="default"] .checkbox-inline input[type="checkbox"]:focus:after {
8877
+ border-color: #71bdc1 !important;
8873
8878
  }
8874
8879
  html[theme="default"] input[type="checkbox"]:before,
8875
8880
  html[theme="default"] .checkbox input[type="checkbox"]:before,
@@ -9201,7 +9206,8 @@ html[theme="default"] .carousel-caption h6 {
9201
9206
  float: none;
9202
9207
  }
9203
9208
  }
9204
- .btn-primary .icon {
9209
+ .btn-primary .icon,
9210
+ .bg-primary .icon {
9205
9211
  filter: brightness(0) invert(1);
9206
9212
  }
9207
9213
  .panel-heading a {