tryton-sao 7.4.8 → 7.4.10

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.10 - 2025-05-02
3
+ ---------------------------
4
+ * Bug fixes (see mercurial logs for details)
5
+
6
+
7
+ Version 7.4.9 - 2025-04-26
8
+ --------------------------
9
+ * Bug fixes (see mercurial logs for details)
10
+
11
+
2
12
  Version 7.4.8 - 2025-04-02
3
13
  --------------------------
4
14
  * Bug fixes (see mercurial logs for details)
@@ -3,7 +3,7 @@
3
3
 
4
4
  /* eslint-disable no-redeclare */
5
5
  var Sao = {
6
- __version__: '7.4.8',
6
+ __version__: '7.4.10',
7
7
  };
8
8
  /* eslint-enable no-redeclare */
9
9
 
@@ -4165,7 +4165,7 @@ var Sao = {
4165
4165
  });
4166
4166
  }
4167
4167
  };
4168
- this._selection_prm.done(_update_selection);
4168
+ this._selection_prm.always(_update_selection);
4169
4169
  };
4170
4170
  Sao.common.selection_mixin.filter_selection = function(
4171
4171
  domain, record, field) {
@@ -5345,6 +5345,15 @@ var Sao = {
5345
5345
  if (!context) {
5346
5346
  context = {};
5347
5347
  }
5348
+ function atof(string) {
5349
+ if (!string) {
5350
+ throw("empty string");
5351
+ }
5352
+ let { format } = new Intl.NumberFormat(
5353
+ Sao.i18n.BC47(Sao.i18n.getlang()));
5354
+ let [, decimalSign] = /^0(.)1$/.exec(format(0.1));
5355
+ return Number(string.replace(decimalSign, '.'));
5356
+ }
5348
5357
  var convert_selection = function() {
5349
5358
  if (typeof value == 'string') {
5350
5359
  for (var i = 0; i < field.selection.length; i++) {
@@ -5376,8 +5385,12 @@ var Sao = {
5376
5385
  },
5377
5386
  'float': function() {
5378
5387
  var factor = Number(field.factor || 1);
5379
- var result = Number(value);
5380
- if (isNaN(result) || value === '' || value === null) {
5388
+ try {
5389
+ var result = atof(value);
5390
+ } catch (e) {
5391
+ return null;
5392
+ }
5393
+ if (isNaN(result)) {
5381
5394
  return null;
5382
5395
  } else {
5383
5396
  return result / factor;
@@ -5385,18 +5398,25 @@ var Sao = {
5385
5398
  },
5386
5399
  'integer': function() {
5387
5400
  var factor = Number(field.factor || 1, 10);
5388
- var result = parseInt(value, 10);
5401
+ try {
5402
+ var result = atof(value);
5403
+ } catch (e) {
5404
+ return null;
5405
+ }
5389
5406
  if (isNaN(result)) {
5390
5407
  return null;
5391
5408
  } else {
5392
- return result / factor;
5409
+ return parseInt(result / factor, 10);
5393
5410
  }
5394
5411
  },
5395
5412
  'numeric': function() {
5396
5413
  var factor = Number(field.factor || 1);
5397
- var result = Number(value);
5398
- if (isNaN(result.valueOf()) ||
5399
- value === '' || value === null) {
5414
+ try {
5415
+ var result = atof(value);
5416
+ } catch (e) {
5417
+ return null;
5418
+ }
5419
+ if (isNaN(result)) {
5400
5420
  return null;
5401
5421
  } else {
5402
5422
  return new Sao.Decimal(result / factor);
@@ -5451,8 +5471,7 @@ var Sao = {
5451
5471
  return '';
5452
5472
  }
5453
5473
  var digit = 0;
5454
- var factor = Number(field.factor || 1);
5455
- var string = String(value * factor);
5474
+ var string = String(value);
5456
5475
  if (string.contains('e')) {
5457
5476
  var exp = string.split('e')[1];
5458
5477
  string = string.split('e')[0];
@@ -5461,7 +5480,14 @@ var Sao = {
5461
5480
  if (string.contains('.')) {
5462
5481
  digit += string.replace(/0+$/, '').split('.')[1].length;
5463
5482
  }
5464
- return (value * factor).toFixed(digit);
5483
+ var factor = Number(field.factor || 1);
5484
+ digit -= Math.round(Math.log10(factor));
5485
+ return (value * factor).toLocaleString(
5486
+ Sao.i18n.BC47(Sao.i18n.getlang()), {
5487
+ useGrouping: true,
5488
+ minimumFractionDigits: digit,
5489
+ maximumFractionDigits: digit,
5490
+ });
5465
5491
  };
5466
5492
  var format_selection = function() {
5467
5493
  if (field.selection instanceof Array) {
@@ -7703,7 +7729,7 @@ var Sao = {
7703
7729
  'use strict';
7704
7730
 
7705
7731
  function get_x2m_sub_fields(f_attrs, prefix) {
7706
- if (f_attrs.visible && f_attrs.views) {
7732
+ if (f_attrs.visible && !jQuery.isEmptyObject(f_attrs.views)) {
7707
7733
  // There's only one key but we don't know its value
7708
7734
  const [[, view],] = Object.entries(f_attrs.views);
7709
7735
 
@@ -8285,6 +8311,7 @@ var Sao = {
8285
8311
  this.autocompletion = {};
8286
8312
  this.exception = false;
8287
8313
  this.destroyed = false;
8314
+ this._save_prm = jQuery.when();
8288
8315
  },
8289
8316
  get modified() {
8290
8317
  if (!jQuery.isEmptyObject(this.modified_fields)) {
@@ -8298,33 +8325,28 @@ var Sao = {
8298
8325
  },
8299
8326
  save: function(force_reload=false) {
8300
8327
  var context = this.get_context();
8328
+ if (this._save_prm.state() == 'pending') {
8329
+ return this._save_prm.then(() => this.save(force_reload));
8330
+ }
8301
8331
  var prm = jQuery.when();
8302
8332
  if ((this.id < 0) || this.modified) {
8303
8333
  var values = this.get();
8304
- try {
8305
- // synchronous call to avoid multiple creation or save
8306
- if (this.id < 0) {
8307
- this.id = this.model.execute(
8308
- 'create', [[values]], context, false)[0];
8309
-
8310
- } else {
8311
- if (!jQuery.isEmptyObject(values)) {
8312
- context._timestamp = this.get_timestamp();
8313
- this.model.execute(
8314
- 'write', [[this.id], values], context, false);
8315
- }
8316
- }
8317
- } catch (e) {
8318
- if (e.promise) {
8319
- return e.then(() => this.save(force_reload));
8320
- } else {
8321
- return jQuery.Deferred().reject();
8334
+ if (this.id < 0) {
8335
+ prm = this.model.execute('create', [[values]], context)
8336
+ .then(ids => this.id = ids[0]);
8337
+ } else {
8338
+ if (!jQuery.isEmptyObject(values)) {
8339
+ context._timestamp = this.get_timestamp();
8340
+ prm = this.model.execute(
8341
+ 'write', [[this.id], values], context);
8322
8342
  }
8323
8343
  }
8324
- this.cancel();
8325
- if (force_reload) {
8326
- return this.reload();
8327
- }
8344
+ prm = prm.then(() => {
8345
+ this.cancel();
8346
+ if (force_reload) {
8347
+ return this.reload();
8348
+ }
8349
+ });
8328
8350
  if (this.group) {
8329
8351
  prm = prm.then(() => this.group.written(this.id));
8330
8352
  }
@@ -8333,6 +8355,7 @@ var Sao = {
8333
8355
  delete this.group.parent.modified_fields[this.group.child_name];
8334
8356
  prm = prm.then(() => this.group.parent.save(force_reload));
8335
8357
  }
8358
+ this._save_prm = prm;
8336
8359
  return prm;
8337
8360
  },
8338
8361
  reload: function(fields, async=true) {
@@ -10195,6 +10218,7 @@ var Sao = {
10195
10218
  }
10196
10219
 
10197
10220
  if (value && (value.add || value.update)) {
10221
+ let vals_to_set = {};
10198
10222
  // First set already added fields to prevent triggering a
10199
10223
  // second on_change call
10200
10224
  if (value.update) {
@@ -10204,9 +10228,9 @@ var Sao = {
10204
10228
  }
10205
10229
  const record2 = group.get(vals.id);
10206
10230
  if (record2) {
10207
- var vals_to_set = {};
10208
10231
  for (var key in vals) {
10209
- if (!(key in new_field_names)) {
10232
+ if (!Object.prototype.hasOwnProperty.call(
10233
+ new_field_names, key)) {
10210
10234
  vals_to_set[key] = vals[key];
10211
10235
  }
10212
10236
  }
@@ -10240,7 +10264,14 @@ var Sao = {
10240
10264
  }
10241
10265
  const record2 = group.get(vals.id);
10242
10266
  if (record2) {
10243
- record2.set_on_change(vals);
10267
+ let to_update = Object.fromEntries(
10268
+ Object.entries(vals).filter(
10269
+ ([k, v]) => {
10270
+ !Object.prototype.hasOwnProperty.call(
10271
+ vals_to_set, k)
10272
+ }
10273
+ ));
10274
+ record2.set_on_change(to_update);
10244
10275
  }
10245
10276
  }
10246
10277
  }
@@ -13034,7 +13065,7 @@ var Sao = {
13034
13065
  Sao.ScreenContainer.BetweenDates._super.init.call(this, id);
13035
13066
  this.from.change(this._from_changed.bind(this));
13036
13067
  },
13037
- _get_value: function(entry, value) {
13068
+ _get_value: function(entry) {
13038
13069
  return entry.find('input[type=text]').val();
13039
13070
  },
13040
13071
  _set_value: function(entry, value) {
@@ -13147,11 +13178,17 @@ var Sao = {
13147
13178
  'class': 'form-control input-sm',
13148
13179
  'type': 'number',
13149
13180
  'step': 'any',
13181
+ 'lang': Sao.i18n.getlang(),
13150
13182
  }).appendTo(el);
13151
13183
  return entry;
13152
13184
  },
13153
- _get_value: function(entry, value) {
13154
- return entry.val();
13185
+ _get_value: function(entry) {
13186
+ let value = entry.val();
13187
+ if (value) {
13188
+ value = Number(value).toLocaleString(
13189
+ Sao.i18n.BC47(Sao.i18n.getlang()))
13190
+ }
13191
+ return value;
13155
13192
  },
13156
13193
  _set_value: function(entry, value) {
13157
13194
  return entry.val(value);
@@ -13705,9 +13742,6 @@ var Sao = {
13705
13742
  return this.__current_record;
13706
13743
  },
13707
13744
  set current_record(record) {
13708
- if ((this.__current_record === record) && record) {
13709
- return;
13710
- }
13711
13745
  this.__current_record = record;
13712
13746
  var pos = null;
13713
13747
  var record_id = null;
@@ -14015,7 +14049,6 @@ var Sao = {
14015
14049
  },
14016
14050
  save_current: function() {
14017
14051
  var current_record = this.current_record;
14018
- let new_record = current_record.id < 0;
14019
14052
  if (!current_record) {
14020
14053
  if (this.current_view &&
14021
14054
  (this.current_view.view_type == 'tree') &&
@@ -14026,6 +14059,7 @@ var Sao = {
14026
14059
  return jQuery.when();
14027
14060
  }
14028
14061
  }
14062
+ let new_record = current_record.id < 0;
14029
14063
  if (this.current_view) {
14030
14064
  this.current_view.set_value();
14031
14065
  var fields = this.current_view.get_fields();
@@ -14696,7 +14730,8 @@ var Sao = {
14696
14730
  return view.display(selected_nodes);
14697
14731
  } else {
14698
14732
  var record;
14699
- if (!jQuery.isEmptyObject(selected_nodes)) {
14733
+ if (!jQuery.isEmptyObject(selected_nodes) &&
14734
+ !this.current_record) {
14700
14735
  for (const id of selected_nodes[0]) {
14701
14736
  const new_record = this.group.get(id);
14702
14737
  if (!new_record) {
@@ -25227,9 +25262,10 @@ function eval_pyson(value){
25227
25262
  });
25228
25263
  },
25229
25264
  clear: function() {
25230
- var kinds = this.el.children().each(
25231
- (i, el) => jQuery(el).data('kind'));
25232
- new Set(kinds).forEach(kind => {
25265
+ let kinds = new Set();
25266
+ this.el.children().each(
25267
+ (i, el) => kinds.add(jQuery(el).data('kind')));
25268
+ kinds.forEach(kind => {
25233
25269
  this.refresh(kind);
25234
25270
  });
25235
25271
  this.__messages.clear();
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.8",
5
+ "version": "7.4.10",
6
6
  "homepage": "https://www.tryton.org/",
7
7
  "author": {
8
8
  "name": "Tryton"
package/src/common.js CHANGED
@@ -833,7 +833,7 @@
833
833
  });
834
834
  }
835
835
  };
836
- this._selection_prm.done(_update_selection);
836
+ this._selection_prm.always(_update_selection);
837
837
  };
838
838
  Sao.common.selection_mixin.filter_selection = function(
839
839
  domain, record, field) {
@@ -2013,6 +2013,15 @@
2013
2013
  if (!context) {
2014
2014
  context = {};
2015
2015
  }
2016
+ function atof(string) {
2017
+ if (!string) {
2018
+ throw("empty string");
2019
+ }
2020
+ let { format } = new Intl.NumberFormat(
2021
+ Sao.i18n.BC47(Sao.i18n.getlang()));
2022
+ let [, decimalSign] = /^0(.)1$/.exec(format(0.1));
2023
+ return Number(string.replace(decimalSign, '.'));
2024
+ }
2016
2025
  var convert_selection = function() {
2017
2026
  if (typeof value == 'string') {
2018
2027
  for (var i = 0; i < field.selection.length; i++) {
@@ -2044,8 +2053,12 @@
2044
2053
  },
2045
2054
  'float': function() {
2046
2055
  var factor = Number(field.factor || 1);
2047
- var result = Number(value);
2048
- if (isNaN(result) || value === '' || value === null) {
2056
+ try {
2057
+ var result = atof(value);
2058
+ } catch (e) {
2059
+ return null;
2060
+ }
2061
+ if (isNaN(result)) {
2049
2062
  return null;
2050
2063
  } else {
2051
2064
  return result / factor;
@@ -2053,18 +2066,25 @@
2053
2066
  },
2054
2067
  'integer': function() {
2055
2068
  var factor = Number(field.factor || 1, 10);
2056
- var result = parseInt(value, 10);
2069
+ try {
2070
+ var result = atof(value);
2071
+ } catch (e) {
2072
+ return null;
2073
+ }
2057
2074
  if (isNaN(result)) {
2058
2075
  return null;
2059
2076
  } else {
2060
- return result / factor;
2077
+ return parseInt(result / factor, 10);
2061
2078
  }
2062
2079
  },
2063
2080
  'numeric': function() {
2064
2081
  var factor = Number(field.factor || 1);
2065
- var result = Number(value);
2066
- if (isNaN(result.valueOf()) ||
2067
- value === '' || value === null) {
2082
+ try {
2083
+ var result = atof(value);
2084
+ } catch (e) {
2085
+ return null;
2086
+ }
2087
+ if (isNaN(result)) {
2068
2088
  return null;
2069
2089
  } else {
2070
2090
  return new Sao.Decimal(result / factor);
@@ -2119,8 +2139,7 @@
2119
2139
  return '';
2120
2140
  }
2121
2141
  var digit = 0;
2122
- var factor = Number(field.factor || 1);
2123
- var string = String(value * factor);
2142
+ var string = String(value);
2124
2143
  if (string.contains('e')) {
2125
2144
  var exp = string.split('e')[1];
2126
2145
  string = string.split('e')[0];
@@ -2129,7 +2148,14 @@
2129
2148
  if (string.contains('.')) {
2130
2149
  digit += string.replace(/0+$/, '').split('.')[1].length;
2131
2150
  }
2132
- return (value * factor).toFixed(digit);
2151
+ var factor = Number(field.factor || 1);
2152
+ digit -= Math.round(Math.log10(factor));
2153
+ return (value * factor).toLocaleString(
2154
+ Sao.i18n.BC47(Sao.i18n.getlang()), {
2155
+ useGrouping: true,
2156
+ minimumFractionDigits: digit,
2157
+ maximumFractionDigits: digit,
2158
+ });
2133
2159
  };
2134
2160
  var format_selection = function() {
2135
2161
  if (field.selection instanceof Array) {
package/src/model.js CHANGED
@@ -4,7 +4,7 @@
4
4
  'use strict';
5
5
 
6
6
  function get_x2m_sub_fields(f_attrs, prefix) {
7
- if (f_attrs.visible && f_attrs.views) {
7
+ if (f_attrs.visible && !jQuery.isEmptyObject(f_attrs.views)) {
8
8
  // There's only one key but we don't know its value
9
9
  const [[, view],] = Object.entries(f_attrs.views);
10
10
 
@@ -586,6 +586,7 @@
586
586
  this.autocompletion = {};
587
587
  this.exception = false;
588
588
  this.destroyed = false;
589
+ this._save_prm = jQuery.when();
589
590
  },
590
591
  get modified() {
591
592
  if (!jQuery.isEmptyObject(this.modified_fields)) {
@@ -599,33 +600,28 @@
599
600
  },
600
601
  save: function(force_reload=false) {
601
602
  var context = this.get_context();
603
+ if (this._save_prm.state() == 'pending') {
604
+ return this._save_prm.then(() => this.save(force_reload));
605
+ }
602
606
  var prm = jQuery.when();
603
607
  if ((this.id < 0) || this.modified) {
604
608
  var values = this.get();
605
- try {
606
- // synchronous call to avoid multiple creation or save
607
- if (this.id < 0) {
608
- this.id = this.model.execute(
609
- 'create', [[values]], context, false)[0];
610
-
611
- } else {
612
- if (!jQuery.isEmptyObject(values)) {
613
- context._timestamp = this.get_timestamp();
614
- this.model.execute(
615
- 'write', [[this.id], values], context, false);
616
- }
617
- }
618
- } catch (e) {
619
- if (e.promise) {
620
- return e.then(() => this.save(force_reload));
621
- } else {
622
- return jQuery.Deferred().reject();
609
+ if (this.id < 0) {
610
+ prm = this.model.execute('create', [[values]], context)
611
+ .then(ids => this.id = ids[0]);
612
+ } else {
613
+ if (!jQuery.isEmptyObject(values)) {
614
+ context._timestamp = this.get_timestamp();
615
+ prm = this.model.execute(
616
+ 'write', [[this.id], values], context);
623
617
  }
624
618
  }
625
- this.cancel();
626
- if (force_reload) {
627
- return this.reload();
628
- }
619
+ prm = prm.then(() => {
620
+ this.cancel();
621
+ if (force_reload) {
622
+ return this.reload();
623
+ }
624
+ });
629
625
  if (this.group) {
630
626
  prm = prm.then(() => this.group.written(this.id));
631
627
  }
@@ -634,6 +630,7 @@
634
630
  delete this.group.parent.modified_fields[this.group.child_name];
635
631
  prm = prm.then(() => this.group.parent.save(force_reload));
636
632
  }
633
+ this._save_prm = prm;
637
634
  return prm;
638
635
  },
639
636
  reload: function(fields, async=true) {
@@ -2496,6 +2493,7 @@
2496
2493
  }
2497
2494
 
2498
2495
  if (value && (value.add || value.update)) {
2496
+ let vals_to_set = {};
2499
2497
  // First set already added fields to prevent triggering a
2500
2498
  // second on_change call
2501
2499
  if (value.update) {
@@ -2505,9 +2503,9 @@
2505
2503
  }
2506
2504
  const record2 = group.get(vals.id);
2507
2505
  if (record2) {
2508
- var vals_to_set = {};
2509
2506
  for (var key in vals) {
2510
- if (!(key in new_field_names)) {
2507
+ if (!Object.prototype.hasOwnProperty.call(
2508
+ new_field_names, key)) {
2511
2509
  vals_to_set[key] = vals[key];
2512
2510
  }
2513
2511
  }
@@ -2541,7 +2539,14 @@
2541
2539
  }
2542
2540
  const record2 = group.get(vals.id);
2543
2541
  if (record2) {
2544
- record2.set_on_change(vals);
2542
+ let to_update = Object.fromEntries(
2543
+ Object.entries(vals).filter(
2544
+ ([k, v]) => {
2545
+ !Object.prototype.hasOwnProperty.call(
2546
+ vals_to_set, k)
2547
+ }
2548
+ ));
2549
+ record2.set_on_change(to_update);
2545
2550
  }
2546
2551
  }
2547
2552
  }
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.8',
6
+ __version__: '7.4.10',
7
7
  };
8
8
  /* eslint-enable no-redeclare */
9
9
 
package/src/screen.js CHANGED
@@ -625,7 +625,7 @@
625
625
  Sao.ScreenContainer.BetweenDates._super.init.call(this, id);
626
626
  this.from.change(this._from_changed.bind(this));
627
627
  },
628
- _get_value: function(entry, value) {
628
+ _get_value: function(entry) {
629
629
  return entry.find('input[type=text]').val();
630
630
  },
631
631
  _set_value: function(entry, value) {
@@ -738,11 +738,17 @@
738
738
  'class': 'form-control input-sm',
739
739
  'type': 'number',
740
740
  'step': 'any',
741
+ 'lang': Sao.i18n.getlang(),
741
742
  }).appendTo(el);
742
743
  return entry;
743
744
  },
744
- _get_value: function(entry, value) {
745
- return entry.val();
745
+ _get_value: function(entry) {
746
+ let value = entry.val();
747
+ if (value) {
748
+ value = Number(value).toLocaleString(
749
+ Sao.i18n.BC47(Sao.i18n.getlang()))
750
+ }
751
+ return value;
746
752
  },
747
753
  _set_value: function(entry, value) {
748
754
  return entry.val(value);
@@ -1296,9 +1302,6 @@
1296
1302
  return this.__current_record;
1297
1303
  },
1298
1304
  set current_record(record) {
1299
- if ((this.__current_record === record) && record) {
1300
- return;
1301
- }
1302
1305
  this.__current_record = record;
1303
1306
  var pos = null;
1304
1307
  var record_id = null;
@@ -1606,7 +1609,6 @@
1606
1609
  },
1607
1610
  save_current: function() {
1608
1611
  var current_record = this.current_record;
1609
- let new_record = current_record.id < 0;
1610
1612
  if (!current_record) {
1611
1613
  if (this.current_view &&
1612
1614
  (this.current_view.view_type == 'tree') &&
@@ -1617,6 +1619,7 @@
1617
1619
  return jQuery.when();
1618
1620
  }
1619
1621
  }
1622
+ let new_record = current_record.id < 0;
1620
1623
  if (this.current_view) {
1621
1624
  this.current_view.set_value();
1622
1625
  var fields = this.current_view.get_fields();
@@ -2287,7 +2290,8 @@
2287
2290
  return view.display(selected_nodes);
2288
2291
  } else {
2289
2292
  var record;
2290
- if (!jQuery.isEmptyObject(selected_nodes)) {
2293
+ if (!jQuery.isEmptyObject(selected_nodes) &&
2294
+ !this.current_record) {
2291
2295
  for (const id of selected_nodes[0]) {
2292
2296
  const new_record = this.group.get(id);
2293
2297
  if (!new_record) {
package/src/window.js CHANGED
@@ -101,9 +101,10 @@
101
101
  });
102
102
  },
103
103
  clear: function() {
104
- var kinds = this.el.children().each(
105
- (i, el) => jQuery(el).data('kind'));
106
- new Set(kinds).forEach(kind => {
104
+ let kinds = new Set();
105
+ this.el.children().each(
106
+ (i, el) => kinds.add(jQuery(el).data('kind')));
107
+ kinds.forEach(kind => {
107
108
  this.refresh(kind);
108
109
  });
109
110
  this.__messages.clear();