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 +10 -0
- package/dist/tryton-sao.css +11 -5
- package/dist/tryton-sao.js +103 -79
- package/package.json +1 -1
- package/src/common.js +17 -9
- package/src/model.js +16 -14
- package/src/sao.js +13 -6
- package/src/sao.less +1 -1
- package/src/tab.js +52 -48
- package/src/theme.less +6 -2
- package/src/view/form.js +0 -1
- package/src/view/tree.js +5 -1
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)
|
package/dist/tryton-sao.css
CHANGED
|
@@ -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:
|
|
8873
|
-
html[theme="default"] .checkbox input[type="checkbox"]:focus:
|
|
8874
|
-
html[theme="default"] .checkbox-inline input[type="checkbox"]:focus:
|
|
8875
|
-
border-color: #
|
|
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 {
|
package/dist/tryton-sao.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
/* eslint-disable no-redeclare */
|
|
5
5
|
var Sao = {
|
|
6
|
-
__version__: '7.4.
|
|
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
|
-
|
|
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
|
-
|
|
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('
|
|
1189
|
-
|
|
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
|
|
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
|
-
|
|
7704
|
-
|
|
7705
|
-
|
|
7706
|
-
|
|
7707
|
-
|
|
7708
|
-
|
|
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
|
-
|
|
9491
|
-
|
|
9492
|
-
|
|
9493
|
-
|
|
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
|
-
|
|
11547
|
-
|
|
11548
|
-
|
|
11549
|
-
|
|
11550
|
-
|
|
11551
|
-
|
|
11552
|
-
|
|
11553
|
-
|
|
11554
|
-
|
|
11555
|
-
|
|
11556
|
-
|
|
11557
|
-
|
|
11558
|
-
|
|
11559
|
-
|
|
11560
|
-
|
|
11561
|
-
|
|
11562
|
-
|
|
11563
|
-
|
|
11564
|
-
|
|
11565
|
-
|
|
11566
|
-
|
|
11567
|
-
|
|
11568
|
-
|
|
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
|
-
|
|
11574
|
-
|
|
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
|
-
|
|
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
|
-
|
|
11601
|
-
()
|
|
11602
|
-
|
|
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(!
|
|
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
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
|
|
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
|
-
|
|
4372
|
-
|
|
4373
|
-
|
|
4374
|
-
|
|
4375
|
-
|
|
4376
|
-
|
|
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
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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('
|
|
1189
|
-
|
|
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
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
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
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
|
-
|
|
862
|
-
|
|
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
|
-
|
|
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
|
-
|
|
889
|
-
()
|
|
890
|
-
|
|
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:
|
|
307
|
-
border-color: @brand-
|
|
310
|
+
&:focus:after {
|
|
311
|
+
border-color: @brand-info !important;
|
|
308
312
|
}
|
|
309
313
|
|
|
310
314
|
&:before {
|
package/src/view/form.js
CHANGED
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(!
|
|
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);
|