This commit is contained in:
2026-06-05 20:25:28 +02:00
parent 2835f49182
commit 28efdfef36
3 changed files with 1393 additions and 253 deletions

727
dist/tryton-sao.js vendored
View File

@@ -3610,7 +3610,14 @@ var Sao = {
};
Sao.common.parse_datetime = function(datetime_format, value) {
var date = moment(value, Sao.common.moment_format(datetime_format));
var formats = [
Sao.common.moment_format(datetime_format),
'YYYY-MM-DDTHH:mm',
'YYYY-MM-DDTHH:mm:ss',
'YYYY-MM-DD HH:mm',
'YYYY-MM-DD HH:mm:ss',
];
var date = moment(value, formats, true);
if (date.isValid()) {
date = Sao.DateTime(date.year(), date.month(), date.date(),
date.hour(), date.minute(), date.second(),
@@ -16797,6 +16804,21 @@ function eval_pyson(value){
this.input.appendTo(this.icon);
Sao.common.ICONFACTORY.get_icon_img('tryton-date')
.appendTo(this.icon);
this.icon.on('click', (event_) => {
if (this.date.prop('readonly')) {
return;
}
if (event_.target == this.input[0]) {
return;
}
event_.preventDefault();
event_.stopPropagation();
if (this.input[0].showPicker) {
this.input[0].showPicker();
} else {
this.input.trigger('click');
}
});
}
this.date.change(this.focus_out.bind(this));
var mousetrap = new Mousetrap(this.date[0]);
@@ -16885,7 +16907,7 @@ function eval_pyson(value){
Sao.View.Form.DateTime = Sao.class_(Sao.View.Form.Date, {
class_: 'form-datetime',
_input: 'datetime-local',
_input_format: '%Y-%m-%dT%H:%M:%S',
_input_format: '%Y-%m-%dT%H:%M',
_format: Sao.common.format_datetime,
_parse: Sao.common.parse_datetime,
get_format: function() {
@@ -16910,9 +16932,10 @@ function eval_pyson(value){
_parse: Sao.common.parse_time,
init: function(view, attributes) {
Sao.View.Form.Time._super.init.call(this, view, attributes);
if (~navigator.userAgent.indexOf("Firefox")) {
// time input on Firefox does not have a pop-up
this.input.parent().hide();
if (this.icon) {
this.input.remove();
this.icon.remove();
delete this.icon;
}
},
get_format: function() {
@@ -18351,6 +18374,7 @@ function eval_pyson(value){
view_ids: (attributes.view_ids || '').split(','),
views_preload: attributes.views || {},
order: attributes.order,
one2many: true,
row_activate: this.activate.bind(this),
exclude_field: attributes.relation_field || null,
limit: null,
@@ -18577,12 +18601,22 @@ function eval_pyson(value){
this.screen.domain = domain;
}
this.screen.size_limit = size_limit;
if (this.attributes.height !== undefined &&
this.screen.current_view &&
this.screen.current_view.set_embedded_height) {
this.screen.current_view.set_embedded_height(
this.attributes.height);
}
this.screen.display();
if (this.attributes.height !== undefined) {
this.screen.current_view.el
.find('.treeview,.list-form').first()
.css('min-height', this.attributes.height + 'px')
.css('max-height', this.attributes.height + 'px');
if (this.screen.current_view.set_embedded_height) {
this.screen.current_view.set_embedded_height(
this.attributes.height);
}
}
});
},
@@ -21981,8 +22015,26 @@ function eval_pyson(value){
},
_parse_field: function(node, attributes) {
var name = attributes.name;
var ColumnFactory = Sao.View.TreeXMLViewParser.WIDGETS[
attributes.widget];
var field = this.view.screen.model.fields[name];
var widget = attributes.widget || (field && field.description.type);
var column_widget = widget == 'variation' ?
(field && field.description.type || 'char') : widget;
attributes.widget = widget;
var ColumnFactory = Sao.View.TreeXMLViewParser.WIDGETS[column_widget];
if (!ColumnFactory) {
console.error('[TREE][UNKNOWN_WIDGET]', {
viewId: this.view.view_id,
model: this.view.screen && this.view.screen.model_name,
field: name,
widget: column_widget,
attributes: attributes,
node: node.outerHTML,
availableWidgets: Object.keys(
Sao.View.TreeXMLViewParser.WIDGETS).sort(),
});
attributes.widget = 'char';
ColumnFactory = Sao.View.Tree.CharColumn;
}
var column = new ColumnFactory(this.view.screen.model, attributes);
if (!this.view.widgets[name]) {
this.view.widgets[name] = [];
@@ -22022,9 +22074,21 @@ function eval_pyson(value){
column.prefixes.push(
new Sao.View.Tree.Symbol(attributes, 0));
}
var field_attrs = this.field_attrs[name] ||
(field && field.description) || {};
if (!this.field_attrs[name]) {
console.warn('[TREE][MISSING_FIELD_ATTRS]', {
viewId: this.view.view_id,
model: this.view.screen && this.view.screen.model_name,
field: name,
widget: attributes.widget,
attributes: attributes,
fallbackFieldAttributes: field_attrs,
});
}
if (!this.view.attributes.sequence &&
!this.view.children_field &&
this.field_attrs[name].sortable !== false){
field_attrs.sortable !== false){
column.sortable = true;
}
this.view.columns.push(column);
@@ -22041,6 +22105,18 @@ function eval_pyson(value){
'class': 'value',
});
this.view.sum_widgets.set(column, [sum, aggregate]);
if (window.console && this.view instanceof Sao.View.AGGridTree) {
console.info('[AGGRID][SUMMARY_SUM_COLUMN_JSON]',
JSON.stringify({
viewId: this.view.view_id,
model: this.view.screen &&
this.view.screen.model_name,
name: attributes.name || null,
label: attributes.string || attributes.name || '',
widget: attributes.widget || '',
sum: attributes.sum || '',
}));
}
}
},
_parse_button: function(node, attributes) {
@@ -22462,6 +22538,8 @@ function eval_pyson(value){
(Sao.View.AGGridTree._next_debug_id || 0) + 1;
this._debug_id = Sao.View.AGGridTree._next_debug_id;
this.children_field = children_field;
this.is_embedded_one2many = Boolean(
screen.attributes && screen.attributes.one2many);
this.optionals = [];
this.sum_widgets = new Map();
this.columns = [];
@@ -22524,6 +22602,9 @@ function eval_pyson(value){
this.toolbar = jQuery('<div/>', {
'class': 'ag-grid-tree-toolbar',
}).appendTo(this.el);
if (this.is_embedded_one2many) {
this.el.addClass('ag-grid-tree-embedded-one2many');
}
this.column_menu = jQuery('<div/>', {
'class': 'ag-grid-tree-columns',
}).appendTo(this.toolbar);
@@ -22610,6 +22691,14 @@ function eval_pyson(value){
this._toggle_filter_logic_menu();
})
.appendTo(this.toolbar);
this.import_layout_input = jQuery('<input/>', {
'type': 'file',
'accept': 'application/json,.json',
'class': 'ag-grid-view-import-input',
}).hide().appendTo(this.el);
this.import_layout_input.on('change', evt => {
this._import_layout_from_input(evt.currentTarget);
});
this.filter_logic_panel = jQuery('<div/>', {
'class': 'ag-grid-filter-logic-panel',
}).hide().appendTo(jQuery(document.body));
@@ -22685,8 +22774,18 @@ function eval_pyson(value){
});
Sao.View.AGGridTree._super.init.call(this, view_id, screen, xml);
this._persisted_state = this._load_persisted_state();
if (this._persisted_state && this._persisted_state.valueFilters) {
if (this.is_embedded_one2many) {
this._layout_store = this._normalize_layout_store(null);
this._persisted_state = null;
this._sao_value_filters = {};
this._restored_value_filter_state = null;
this._sao_value_filter_restore_guard = false;
} else {
this._persisted_state = this._load_persisted_state();
}
if (!this.is_embedded_one2many &&
this._persisted_state &&
this._persisted_state.valueFilters) {
this._apply_sao_value_filter_state(
this._persisted_state.valueFilters);
}
@@ -22967,43 +23066,6 @@ function eval_pyson(value){
});
},
_trace_aggrid_column_resize: function(reason, params, extra) {
if (!window.console) {
return;
}
var column_api = this.gridColumnApi || this.gridApi;
var column_state = column_api && column_api.getColumnState ?
column_api.getColumnState() : [];
var target_col_id = null;
if (params && params.column) {
if (params.column.getColId) {
target_col_id = params.column.getColId();
} else if (params.column.colId) {
target_col_id = params.column.colId;
}
}
console.info('[AGGRID][COLUMN_RESIZE_JSON]',
JSON.stringify(jQuery.extend({
reason: reason,
instanceId: this._debug_id,
viewId: this.view_id,
model: this.screen && this.screen.model_name,
targetColId: target_col_id,
finished: params && Object.prototype.hasOwnProperty.call(
params, 'finished') ? params.finished : null,
source: params && params.source || null,
resizeActive: Boolean(this._column_resize_active),
activeLayoutId: this._active_layout_id || null,
columnWidths: (column_state || [])
.filter(state => state && state.colId != '__selection__')
.map(state => ({
colId: state.colId,
width: state.width,
minWidth: state.minWidth || null,
flex: state.flex || null,
})),
pendingTimers: this._layout_refresh_timers ?
this._layout_refresh_timers.length : 0,
}, extra || {})));
},
_refresh_grid_layout_now: function() {
if (!this.gridApi || !this.grid_host || !this.grid_host.length) {
@@ -23080,6 +23142,9 @@ function eval_pyson(value){
window.addEventListener('resize', this._window_resize_handler);
},
_persist_grid_state: function(options) {
if (this.is_embedded_one2many) {
return;
}
options = options || {};
var state = this._capture_grid_state();
state.valueFilters = this._merge_restored_value_filter_state(
@@ -24050,6 +24115,110 @@ function eval_pyson(value){
this._layout_store.defaultPresetId = null;
this._apply_layout_state(this._get_standard_layout_state(), null);
},
_sanitize_layout_file_name: function(name) {
name = String(name || Sao.i18n.gettext('AG Grid view')).trim();
name = name.replace(/[\\/:*?"<>|]+/g, '-')
.replace(/\s+/g, ' ')
.trim();
return name || 'ag-grid-view';
},
_get_layout_export_payload: function() {
var preset = this._find_layout_preset(this._active_layout_id);
var name = preset && !this._layout_dirty ? preset.name :
this._get_active_layout_name().replace(/\s+\*$/, '');
return {
type: 'sao-ag-grid-view',
version: 1,
model: this.screen && this.screen.model_name || null,
viewId: this.view_id || null,
name: name || Sao.i18n.gettext('AG Grid view'),
exportedAt: new Date().toISOString(),
state: this._capture_grid_state(),
};
},
_export_current_layout: function() {
var payload = this._get_layout_export_payload();
var file_name = this._sanitize_layout_file_name(
payload.model + '-' + payload.name) + '.json';
Sao.common.download_file(
JSON.stringify(payload, null, 2),
file_name,
{type: 'application/json;charset=utf-8'});
},
_decode_imported_layout_text: function(data) {
if (typeof data == 'string') {
return data;
}
if (data instanceof Uint8Array) {
return new TextDecoder('utf-8').decode(data);
}
return String(data || '');
},
_import_layout_from_input: function(input) {
var file = input && input.files && input.files[0];
if (!file) {
return;
}
Sao.common.get_file_data(file, data => {
var payload;
try {
payload = JSON.parse(this._decode_imported_layout_text(data));
} catch (error) {
Sao.common.warning.run(
Sao.i18n.gettext('The selected file is not a valid AG Grid view.'),
Sao.i18n.gettext('Import view'));
return;
}
this._import_layout_payload(payload);
});
},
_import_layout_payload: function(payload) {
if (!payload || payload.type != 'sao-ag-grid-view' ||
!payload.state) {
Sao.common.warning.run(
Sao.i18n.gettext('The selected file is not a valid AG Grid view.'),
Sao.i18n.gettext('Import view'));
return;
}
if (payload.model && payload.model != this.screen.model_name) {
Sao.common.warning.run(
Sao.i18n.gettext('This view is for another model.') +
' ' + payload.model + ' / ' + this.screen.model_name,
Sao.i18n.gettext('Import view'));
return;
}
var name = String(payload.name ||
Sao.i18n.gettext('Imported view')).trim();
if (!name) {
name = Sao.i18n.gettext('Imported view');
}
this._layout_store = this._normalize_layout_store(
this._layout_store || {});
var existing_names = new Set((this._layout_store.presets || [])
.map(preset => preset.name));
var base_name = name;
var suffix = 2;
while (existing_names.has(name)) {
name = base_name + ' ' + suffix;
suffix += 1;
}
var now = new Date().toISOString();
var preset = {
id: this._new_layout_id(),
name: name,
importedAt: now,
updatedAt: now,
state: jQuery.extend(true, {}, payload.state),
};
this._layout_store.presets.push(preset);
this._active_layout_id = preset.id;
this._layout_dirty = false;
this._layout_store.activePresetId = preset.id;
this._layout_store.currentState = jQuery.extend(true, {}, preset.state);
this._persisted_state = jQuery.extend(true, {}, preset.state);
this._save_layout_store();
this._apply_layout_state(preset.state, preset.id);
},
_save_layout_as: function() {
var name = window.prompt(Sao.i18n.gettext('View name'));
if (!name) {
@@ -24271,6 +24440,30 @@ function eval_pyson(value){
evt.preventDefault();
this._apply_standard_layout_state();
}).appendTo(actions);
var transfer_actions = jQuery('<div/>', {
'class': 'ag-grid-tree-layouts-transfer-actions',
}).appendTo(actions);
jQuery('<button/>', {
'class': 'btn btn-default btn-sm ag-grid-view-transfer-button',
'type': 'button',
'title': Sao.i18n.gettext('Export view'),
'aria-label': Sao.i18n.gettext('Export view'),
'text': Sao.i18n.gettext('Export'),
}).click(evt => {
evt.preventDefault();
this._export_current_layout();
}).appendTo(transfer_actions);
jQuery('<button/>', {
'class': 'btn btn-default btn-sm ag-grid-view-transfer-button',
'type': 'button',
'title': Sao.i18n.gettext('Import view'),
'aria-label': Sao.i18n.gettext('Import view'),
'text': Sao.i18n.gettext('Import'),
}).click(evt => {
evt.preventDefault();
this.import_layout_input.val(null);
this.import_layout_input[0].click();
}).appendTo(transfer_actions);
},
_render_grouping_panel: function() {
if (!this.grouping_panel) {
@@ -24635,6 +24828,12 @@ function eval_pyson(value){
this._sync_selection_header_checkbox();
this._update_summary_bar();
},
onFirstDataRendered: () => {
window.requestAnimationFrame(() => this._update_summary_bar());
},
onRowDataUpdated: () => {
window.requestAnimationFrame(() => this._update_summary_bar());
},
onColumnMoved: this._on_grid_column_layout_changed.bind(this),
onColumnPinned: this._on_grid_column_layout_changed.bind(this),
onColumnResized: this._on_grid_column_resized.bind(this),
@@ -24844,16 +25043,6 @@ function eval_pyson(value){
},
};
console.info('[AGGRID][ALIGN_COLUMN]', {
colId: name,
label: column.attributes.string || '',
widget: column.attributes.widget || '',
className: column.class_ || '',
rightAligned: right_aligned,
cellClass: col_def.cellClass,
headerClass: col_def.headerClass,
});
column_defs.push(col_def);
});
return column_defs;
@@ -26077,7 +26266,7 @@ function eval_pyson(value){
var pair = item.split(':');
return pair.length < 2 ? '' :
this._normalize_badge_text_key(pair.shift());
}).filter(Boolean);
}).filter(value => value && value != '*');
},
_get_record_field_value: function(record, name) {
if (!record || !name) {
@@ -26169,6 +26358,8 @@ function eval_pyson(value){
} else if (Object.prototype.hasOwnProperty.call(
mapped, String(normalized_text).toLowerCase())) {
color = mapped[String(normalized_text).toLowerCase()];
} else if (Object.prototype.hasOwnProperty.call(mapped, '*')) {
color = mapped['*'];
}
if (!color && attributes.badge_color_field) {
color = this._get_record_field_value(
@@ -26714,7 +26905,7 @@ function eval_pyson(value){
}
return rows;
},
_get_summary_badge_columns: function() {
_get_displayed_column_map: function() {
var visible_columns = {};
var column_api = this.gridColumnApi || this.gridApi;
if (column_api && column_api.getAllDisplayedColumns) {
@@ -26724,37 +26915,336 @@ function eval_pyson(value){
}
});
}
return visible_columns;
},
_get_viewport_column_map: function() {
var visible_columns = {};
var column_api = this.gridColumnApi || this.gridApi;
var columns = null;
if (column_api && column_api.getAllDisplayedVirtualColumns) {
columns = column_api.getAllDisplayedVirtualColumns();
} else if (column_api && column_api.getAllDisplayedColumns) {
columns = column_api.getAllDisplayedColumns();
}
(columns || []).forEach(column => {
if (column && column.getColId) {
visible_columns[column.getColId()] = true;
}
});
return visible_columns;
},
_is_summary_column_visible: function(column, visible_columns) {
var name = column && column.attributes && column.attributes.name;
return name && (!Object.keys(visible_columns).length ||
visible_columns[name]);
},
_get_summary_badge_columns: function() {
return this.columns.filter(column => {
var name = column.attributes && column.attributes.name;
var label = column.attributes &&
(column.attributes.string || column.attributes.name) || '';
var is_status_column = /(^|[_\s-])(status|state|type)([_\s-]|$)/i
.test(String(name || '') + ' ' + String(label || ''));
if (!name || (!this._is_badge_column(column) &&
!is_status_column)) {
return false;
}
return !Object.keys(visible_columns).length ||
visible_columns[name];
return name && column.attributes.widget == 'badge';
});
},
_render_summary_item: function(label, value, color) {
_get_summary_sum_columns: function() {
var columns = [];
this.sum_widgets.forEach((sum_widget, column) => {
if (column && column.attributes && column.attributes.name) {
columns.push(column);
}
});
return columns;
},
_get_summary_variation_columns: function() {
return this.columns.filter(column => {
var attributes = column && column.attributes || {};
return attributes.name &&
attributes.widget == 'variation' &&
attributes.variation_summary == '1' &&
attributes.variation_previous &&
attributes.variation_group_by;
});
},
_render_summary_item: function(
label, value, color, class_name, variation_text,
variation_class) {
var item = jQuery('<div/>', {
'class': 'ag-grid-summary-item',
'class': 'ag-grid-summary-item' +
(class_name ? ' ' + class_name : ''),
});
jQuery('<span/>', {
'class': 'ag-grid-summary-label',
'text': label,
}).appendTo(item);
if (variation_text) {
jQuery('<span/>', {
'class': 'ag-grid-summary-variation-badge ' +
(variation_class || ''),
'text': variation_text,
}).appendTo(item);
}
var value_wrap = jQuery('<span/>', {
'class': 'ag-grid-summary-value-wrap',
}).appendTo(item);
var value_el = jQuery('<span/>', {
'class': 'ag-grid-summary-value',
'text': value,
}).appendTo(item);
}).appendTo(value_wrap);
if (color) {
value_el.css('color', color);
}
return item;
},
_trace_summary_bar: function(
rows, badge_columns, sum_columns, variation_columns, summary_items) {
if (!window.console) {
return;
}
var describe = column => ({
name: column && column.attributes && column.attributes.name || null,
label: column && column.attributes &&
(column.attributes.string || column.attributes.name) || '',
widget: column && column.attributes && column.attributes.widget || '',
sum: column && column.attributes && column.attributes.sum || '',
badge: Boolean(column && column.attributes &&
column.attributes.widget == 'badge'),
});
console.info('[AGGRID][SUMMARY_BAR_JSON]', JSON.stringify({
instanceId: this._debug_id,
viewId: this.view_id,
model: this.screen && this.screen.model_name,
rowCount: rows.length,
viewportColumns: Object.keys(this._get_viewport_column_map()),
displayedColumns: Object.keys(this._get_displayed_column_map()),
badgeColumns: badge_columns.map(describe),
sumColumns: sum_columns.map(describe),
variationColumns: variation_columns.map(describe),
sumWidgetColumns: Array.from(this.sum_widgets.keys()).map(describe),
badgeLikeColumns: this.columns.filter(column =>
this._is_badge_column(column)).map(describe),
summaryItems: summary_items.map(item => ({
label: item.label,
count: item.count,
className: item.className || '',
})),
}));
},
_get_summary_column_load_fields: function(column) {
var attributes = column && column.attributes || {};
var fields = [];
if (attributes.name) {
fields.push(attributes.name);
}
if (attributes.widget == 'variation') {
if (attributes.variation_previous) {
fields.push(attributes.variation_previous);
}
if (attributes.variation_group_by) {
fields.push(attributes.variation_group_by);
}
}
if (attributes.sum_variation == '1' && attributes.sum_previous) {
fields.push(attributes.sum_previous);
}
return fields.filter((field, index) =>
field && fields.indexOf(field) == index);
},
_load_summary_column_values: function(rows, columns, reason) {
if (this._summary_sum_loading) {
return null;
}
var promises = [];
var field_names = [];
columns.forEach(column => {
var names = this._get_summary_column_load_fields(column);
field_names = field_names.concat(names);
rows.forEach(row => {
var record = row && row.__record;
names.forEach(name => {
if (record && record.id >= 0 &&
!record.get_loaded([name])) {
promises.push(record.load(name, true, false));
}
});
});
});
if (!promises.length) {
return null;
}
this._summary_sum_loading = true;
if (window.console) {
console.info('[AGGRID][SUMMARY_SUM_LOAD_JSON]',
JSON.stringify({
instanceId: this._debug_id,
viewId: this.view_id,
model: this.screen && this.screen.model_name,
reason: reason || 'summary',
fields: field_names.filter((field, index) =>
field && field_names.indexOf(field) == index),
promiseCount: promises.length,
}));
}
return jQuery.when.apply(jQuery, promises).always(() => {
this._summary_sum_loading = false;
});
},
_load_summary_sum_values: function(rows, columns) {
return this._load_summary_column_values(rows, columns, 'sum');
},
_load_summary_badge_values: function(rows, columns) {
return this._load_summary_column_values(rows, columns, 'badge');
},
_append_sum_summary_item: function(summary_items, rows, column) {
var sum = null;
var name = column && column.attributes && column.attributes.name;
var field = name ? this.screen.model.fields[name] : null;
var previous_sum = null;
var previous_name = column && column.attributes &&
column.attributes.sum_previous;
var with_variation = column && column.attributes &&
column.attributes.sum_variation == '1' && previous_name;
rows.forEach(row => {
var record = row && row.__record;
if (!field || !record || record.id >= 0 &&
!record.get_loaded([name])) {
return;
}
var value = field.get(record);
if (value && value.isTimeDelta) {
value = value.asSeconds();
}
if (value !== null && value !== undefined && value !== '') {
sum = sum === null ? value : sum + value;
}
if (with_variation) {
var previous = this._get_record_raw_field_value(
record, previous_name);
if (previous !== null && previous !== undefined &&
previous !== '') {
previous = Number(previous);
if (isFinite(previous)) {
previous_sum = previous_sum === null ?
previous : previous_sum + previous;
}
}
}
});
var variation = null;
if (with_variation && sum !== null && previous_sum !== null &&
previous_sum !== 0) {
variation = ((sum - previous_sum) /
Math.abs(previous_sum)) * 100;
}
summary_items.push({
label: column.attributes.string || column.attributes.name,
count: sum === null ? '-' :
this._format_aggregate_value(column, sum),
color: '#111111',
className: 'ag-grid-summary-item-sum',
variationText: variation === null ? '' :
this._format_sum_variation_percent(variation),
variationClass: variation === null ? '' :
(variation < 0 ? 'ag-grid-summary-variation-negative' :
'ag-grid-summary-variation-positive'),
});
},
_get_record_raw_field_value: function(record, name) {
if (!record || !name) {
return null;
}
var field = this.screen.model.fields[name];
if (!field || record.id >= 0 && !record.get_loaded([name])) {
return null;
}
var value = field.get(record);
if (value && value.isTimeDelta) {
value = value.asSeconds();
}
if (value === null || value === undefined || value === '') {
return null;
}
return value;
},
_get_record_text_field_value: function(record, name) {
if (!record || !name) {
return '';
}
var field = this.screen.model.fields[name];
if (!field || record.id >= 0 && !record.get_loaded([name])) {
return '';
}
try {
return field.get_client(record);
} catch (error) {
var value = this._get_record_raw_field_value(record, name);
return value === null || value === undefined ? '' : String(value);
}
},
_format_variation_percent: function(value) {
var locale = Sao.i18n.BC47(Sao.i18n.getlang());
var sign = value > 0 ? '+' : '';
return sign + value.toLocaleString(locale, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
}) + '%';
},
_format_sum_variation_percent: function(value) {
var locale = Sao.i18n.BC47(Sao.i18n.getlang());
var sign = value >= 0 ? '+' : '';
return sign + value.toLocaleString(locale, {
minimumFractionDigits: 0,
maximumFractionDigits: 2,
}) + '%';
},
_append_variation_summary_items: function(summary_items, rows, column) {
var attributes = column && column.attributes || {};
var current_name = attributes.name;
var previous_name = attributes.variation_previous;
var group_name = attributes.variation_group_by;
var groups = {};
rows.forEach(row => {
var record = row && row.__record;
if (!record) {
return;
}
var current = Number(this._get_record_raw_field_value(
record, current_name));
var previous = Number(this._get_record_raw_field_value(
record, previous_name));
var group = this._get_record_text_field_value(
record, group_name) || Sao.i18n.gettext('(Empty)');
if (!isFinite(current) || !isFinite(previous) || previous === 0) {
return;
}
if (!groups[group]) {
groups[group] = {
current: 0,
previous: 0,
};
}
groups[group].current += current;
groups[group].previous += previous;
});
Object.keys(groups).sort((a, b) => String(a).localeCompare(
String(b), undefined, {numeric: true, sensitivity: 'base'}))
.forEach(group => {
var values = groups[group];
if (!values.previous) {
return;
}
var variation = ((values.current - values.previous) /
Math.abs(values.previous)) * 100;
summary_items.push({
label: group,
count: this._format_variation_percent(variation),
color: variation > 0 ? '#16a34a' :
variation < 0 ? '#dc2626' : '#64748b',
className: 'ag-grid-summary-item-variation' +
(variation > 0 ? ' ag-grid-summary-variation-up' :
variation < 0 ?
' ag-grid-summary-variation-down' :
' ag-grid-summary-variation-flat'),
});
});
},
_get_default_badge_summary_color: function(value) {
var key = String(value || '').toLowerCase();
var palette = {
@@ -26785,8 +27275,16 @@ function eval_pyson(value){
var order = [];
var empty = Sao.i18n.gettext('(Empty)');
rows.forEach(row => {
var value = row[name] ?
this._normalize_badge_text_key(row[name]) : empty;
var raw_value = '';
if (column && row && row.__record) {
raw_value = this._get_column_text_value(
column, row.__record);
}
if (!raw_value && row) {
raw_value = row[name];
}
var value = raw_value ?
this._normalize_badge_text_key(raw_value) : empty;
if (value == empty) {
return;
}
@@ -26836,27 +27334,6 @@ function eval_pyson(value){
});
return Boolean(order.length);
},
_get_summary_status_sources_from_grid: function() {
var sources = [];
var seen = {};
(this._grid_columns || []).forEach(col_def => {
var name = col_def && col_def.field || col_def && col_def.colId;
if (!name || name == '__selection__' || seen[name]) {
return;
}
var label = col_def.headerName || name;
if (!/(status|state|type|shipping|shipment|open\/physic)/i
.test(String(name) + ' ' + String(label))) {
return;
}
seen[name] = true;
sources.push({
name: name,
label: label,
});
});
return sources;
},
_update_summary_bar: function() {
if (!this.summary_bar || !this.summary_bar.length) {
return;
@@ -26869,25 +27346,38 @@ function eval_pyson(value){
rows.length.toLocaleString(locale),
'#145369'));
var summary_items = [];
var summarized_names = {};
this._get_summary_badge_columns().forEach(column => {
if (this._append_badge_summary_items(
summary_items, rows, column)) {
summarized_names[column.attributes.name] = true;
}
});
this._get_summary_status_sources_from_grid().forEach(source => {
if (summarized_names[source.name]) {
return;
}
var sum_columns = this._get_summary_sum_columns();
var badge_columns = this._get_summary_badge_columns();
var variation_columns = this._get_summary_variation_columns();
var summary_load = this._load_summary_column_values(
rows, badge_columns.concat(sum_columns, variation_columns),
'summary');
if (summary_load) {
summary_load.then(() => this._update_summary_bar());
}
badge_columns.forEach(column => {
this._append_badge_summary_items(
summary_items, rows, null, source);
summary_items, rows, column);
});
sum_columns.forEach(column => {
this._append_sum_summary_item(summary_items, rows, column);
});
variation_columns.forEach(column => {
this._append_variation_summary_items(
summary_items, rows, column);
});
this._trace_summary_bar(
rows, badge_columns, sum_columns, variation_columns,
summary_items);
summary_items.forEach(item => {
this.summary_bar.append(this._render_summary_item(
item.label,
item.count.toLocaleString(locale),
item.color));
typeof item.count == 'number' ?
item.count.toLocaleString(locale) : item.count,
item.color,
item.className,
item.variationText,
item.variationClass));
});
},
on_copy: function() {
@@ -27052,7 +27542,10 @@ function eval_pyson(value){
var readable_min_height = 420;
var maximum_height = 720;
var height;
if (available >= compact_min_height) {
if (this.is_embedded_one2many && this._embedded_height) {
height = Math.max(80, this._embedded_height - toolbar_height -
summary_height - external_scroll_height);
} else if (available >= compact_min_height) {
height = Math.max(
compact_min_height,
Math.min(maximum_height, available));
@@ -27104,6 +27597,17 @@ function eval_pyson(value){
});
}
},
set_embedded_height: function(height) {
height = parseInt(height, 10);
if (!this.is_embedded_one2many || isNaN(height) || height <= 0) {
return;
}
this._embedded_height = height;
this._resize_grid_host();
if (this.gridApi && this.gridApi.doLayout) {
this.gridApi.doLayout();
}
},
_refresh_grid_layout_after_row_data: function(row_data) {
if (!this.gridApi) {
return;
@@ -27319,7 +27823,7 @@ function eval_pyson(value){
} else if (diagnostics.relationWidget) {
diagnostics.fallbackReason = diagnostics.relationWidget +
'_relation_widget';
} else if (diagnostics.embeddedContext) {
} else if (diagnostics.embeddedContext && diagnostics.editable) {
diagnostics.fallbackReason = 'embedded_relation_tree';
} else if (diagnostics.editable) {
diagnostics.fallbackReason = 'editable_view';
@@ -30481,6 +30985,7 @@ function eval_pyson(value){
'timedelta': Sao.View.Tree.TimeDeltaColumn,
'tolerance_gauge': Sao.View.Tree.ToleranceGauge,
'url': Sao.View.Tree.URLColumn,
'variation': Sao.View.Tree.CharColumn,
};
Sao.View.EditableTree = {};

View File

@@ -9691,20 +9691,20 @@ img.icon {
margin: 0 0 5px;
}
.ag-grid-tree-toolbar > .ag-grid-screen-filter-toggle-bar {
margin: 0 10px 0 auto;
margin: 0 4px 0 auto;
}
.ag-grid-screen-filter-toggle {
display: inline-flex;
align-items: center;
gap: 7px;
min-height: 30px;
padding: 4px 10px;
border: 1px solid #d6e4df;
border-radius: 8px;
background: linear-gradient(180deg, #f6fbf9 0%, #edf5f1 100%);
gap: 5px;
min-height: 26px;
padding: 2px 7px;
border: 1px solid #d8e2df;
border-radius: 6px;
background: #ffffff;
color: #355154;
box-shadow: 0 2px 7px rgba(53, 81, 84, 0.1);
font-size: 12px;
box-shadow: none;
font-size: 11.5px;
font-weight: 600;
line-height: 1.2;
text-transform: none !important;
@@ -9713,18 +9713,18 @@ img.icon {
}
.ag-grid-screen-filter-toggle:hover,
.ag-grid-screen-filter-toggle:focus {
background: linear-gradient(180deg, #f2faf7 0%, #e7f2ed 100%);
background: #f6faf8;
border-color: #bfd5cd;
color: #28484b;
box-shadow: 0 3px 9px rgba(53, 81, 84, 0.13);
box-shadow: 0 1px 1px rgba(53, 81, 84, 0.05);
}
.ag-grid-screen-filter-toggle:active {
transform: translateY(1px);
box-shadow: 0 1px 5px rgba(53, 81, 84, 0.1);
box-shadow: none;
}
.ag-grid-screen-filter-toggle .icon {
width: 14px;
height: 14px;
width: 13px;
height: 13px;
opacity: 0.78;
}
@media screen and (max-width: 991px) {
@@ -11122,24 +11122,26 @@ html[theme="default"] .screen-container .nav-tabs > li.active > a:hover {
display: flex;
align-items: center;
justify-content: flex-start;
gap: 12px;
padding: 6px 0;
gap: 5px;
min-height: 32px;
padding: 2px 0 4px;
}
.ag-grid-tree-columns > .btn,
.ag-grid-tree-grouping > .btn,
.ag-grid-tree-layouts > .btn,
.ag-grid-value-filter-mode {
.ag-grid-value-filter-mode,
.ag-grid-view-transfer-button {
display: inline-flex;
align-items: center;
gap: 8px;
min-height: 34px;
padding: 6px 12px;
border: 1px solid #d6e4df;
border-radius: 8px;
background: linear-gradient(180deg, #f6fbf9 0%, #edf5f1 100%);
gap: 5px;
min-height: 27px;
padding: 3px 8px;
border: 1px solid #d8e2df;
border-radius: 6px;
background: #ffffff;
color: #355154;
box-shadow: 0 3px 10px rgba(53, 81, 84, 0.12);
box-shadow: none;
text-transform: none !important;
transition: background-color 0.18s ease, border-color 0.18s ease,
box-shadow 0.18s ease, transform 0.18s ease;
@@ -11149,20 +11151,23 @@ html[theme="default"] .screen-container .nav-tabs > li.active > a:hover {
.ag-grid-tree-grouping > .btn:hover,
.ag-grid-tree-layouts > .btn:hover,
.ag-grid-value-filter-mode:hover,
.ag-grid-view-transfer-button:hover,
.ag-grid-tree-columns > .btn:focus,
.ag-grid-tree-grouping > .btn:focus,
.ag-grid-tree-layouts > .btn:focus,
.ag-grid-value-filter-mode:focus {
.ag-grid-value-filter-mode:focus,
.ag-grid-view-transfer-button:focus {
background: linear-gradient(180deg, #f2faf7 0%, #e7f2ed 100%);
border-color: #bfd5cd;
color: #28484b;
box-shadow: 0 5px 14px rgba(53, 81, 84, 0.16);
box-shadow: 0 1px 1px rgba(53, 81, 84, 0.05);
}
.ag-grid-tree-columns > .btn:active,
.ag-grid-tree-grouping > .btn:active,
.ag-grid-tree-layouts > .btn:active,
.ag-grid-value-filter-mode:active {
.ag-grid-value-filter-mode:active,
.ag-grid-view-transfer-button:active {
transform: translateY(1px);
box-shadow: 0 2px 8px rgba(53, 81, 84, 0.12);
}
@@ -11171,18 +11176,30 @@ html[theme="default"] .screen-container .nav-tabs > li.active > a:hover {
.ag-grid-tree-grouping-label,
.ag-grid-tree-layouts-label,
.ag-grid-value-filter-mode-label {
font-size: 12px;
font-size: 11.5px;
font-weight: 600;
letter-spacing: 0.01em;
letter-spacing: 0;
text-transform: none;
}
.ag-grid-value-filter-mode.active {
background: linear-gradient(180deg, #e0f4f1 0%, #cbe9e3 100%);
background: #f2faf8;
border-color: #8fc3bd;
color: #0f6f6d;
}
.ag-grid-view-transfer-button {
font-size: 11.5px;
font-weight: 600;
}
.ag-grid-tree-layouts-transfer-actions {
display: inline-flex;
align-items: center;
gap: 6px;
margin-left: auto;
}
.ag-grid-tree-grouping-panel .btn,
.ag-grid-tree-layouts-panel .btn,
.ag-grid-filter-logic-panel .btn {
@@ -11289,7 +11306,7 @@ html[theme="default"] .screen-container .nav-tabs > li.active > a:hover {
justify-content: center;
width: 16px;
height: 16px;
color: #2b8c87;
color: #6f8f8b;
vertical-align: middle;
}
@@ -11308,6 +11325,14 @@ html[theme="default"] .screen-container .nav-tabs > li.active > a:hover {
fill: none;
}
.ag-grid-tree-columns > .btn:hover .ag-grid-toolbar-icon,
.ag-grid-tree-grouping > .btn:hover .ag-grid-toolbar-icon,
.ag-grid-tree-layouts > .btn:hover .ag-grid-toolbar-icon,
.ag-grid-value-filter-mode:hover .ag-grid-toolbar-icon,
.ag-grid-value-filter-mode.active .ag-grid-toolbar-icon {
color: #2b8c87;
}
.ag-grid-tree-grouping-panel {
width: 420px;
max-width: calc(100vw - 24px);
@@ -11637,6 +11662,7 @@ html[theme="default"] .ag-grid-tree-grouping-panel input[type="checkbox"]:after
gap: 3px;
padding: 8px 18px;
border-right: 1px solid #e7efeb;
position: relative;
}
.ag-grid-summary-item:last-child {
@@ -11661,6 +11687,78 @@ html[theme="default"] .ag-grid-tree-grouping-panel input[type="checkbox"]:after
line-height: 1.15;
}
.ag-grid-summary-value-wrap {
display: inline-flex;
align-items: center;
gap: 8px;
min-width: 0;
}
.ag-grid-summary-variation-badge {
display: flex;
align-items: center;
justify-content: center;
position: absolute;
top: 7px;
right: 10px;
min-height: 16px;
padding: 0 5px;
border-radius: 4px;
font-size: 10.5px;
font-weight: 800;
line-height: 1.1;
white-space: nowrap;
}
.ag-grid-summary-variation-positive {
border: none;
background: transparent;
color: #16a34a;
}
.ag-grid-summary-variation-negative {
border: none;
background: transparent;
color: #dc2626;
}
.ag-grid-summary-item-sum .ag-grid-summary-value {
color: #111111;
font-weight: 800;
}
.ag-grid-summary-item-sum {
flex-basis: 180px;
min-width: 165px;
max-width: 230px;
padding-left: 22px;
padding-right: 28px;
}
.ag-grid-summary-item-variation {
flex-basis: 165px;
min-width: 145px;
max-width: 220px;
background: #fbfdfc;
}
.ag-grid-summary-item-variation .ag-grid-summary-value {
font-size: 17px;
font-weight: 800;
}
.ag-grid-summary-variation-up .ag-grid-summary-value {
color: #16a34a;
}
.ag-grid-summary-variation-down .ag-grid-summary-value {
color: #dc2626;
}
.ag-grid-summary-variation-flat .ag-grid-summary-value {
color: #64748b;
}
.ag-grid-tree-host {
flex: 0 0 auto;
min-height: 520px;
@@ -11790,6 +11888,12 @@ html[theme="default"] .ag-grid-tree-grouping-panel input[type="checkbox"]:after
border-right: 1px solid #e3ebe7;
}
.ag-grid-tree-host .ag-header-cell:not([col-id="__selection__"]),
.ag-grid-tree-host .ag-cell:not([col-id="__selection__"]) {
padding-left: 10px;
padding-right: 14px;
}
.ag-grid-tree-host .ag-header-cell:last-child,
.ag-grid-tree-host .ag-cell:last-child {
border-right: none;
@@ -11819,6 +11923,10 @@ html[theme="default"] .ag-grid-tree-grouping-panel input[type="checkbox"]:after
justify-content: flex-end;
}
.ag-grid-tree-host .ag-grid-right-aligned-cell {
padding-right: 18px;
}
.ag-grid-tree-host .ag-grid-badge-cell {
display: flex;
align-items: center;
@@ -12772,6 +12880,7 @@ html[data-theme-mode="dark"] .screen-container .filter-box .btn,
html[data-theme-mode="dark"] .ag-grid-tree-columns > .btn,
html[data-theme-mode="dark"] .ag-grid-tree-grouping > .btn,
html[data-theme-mode="dark"] .ag-grid-tree-layouts > .btn,
html[data-theme-mode="dark"] .ag-grid-view-transfer-button,
html[data-theme-mode="dark"] .ag-grid-tree-grouping-panel .btn,
html[data-theme-mode="dark"] .ag-grid-tree-layouts-panel .btn,
html[data-theme-mode="dark"] .ag-grid-filter-logic-panel .btn {
@@ -12787,6 +12896,7 @@ html[data-theme-mode="dark"] .screen-container .filter-box .btn:hover,
html[data-theme-mode="dark"] .ag-grid-tree-columns > .btn:hover,
html[data-theme-mode="dark"] .ag-grid-tree-grouping > .btn:hover,
html[data-theme-mode="dark"] .ag-grid-tree-layouts > .btn:hover,
html[data-theme-mode="dark"] .ag-grid-view-transfer-button:hover,
html[data-theme-mode="dark"] .ag-grid-tree-grouping-panel .btn:hover,
html[data-theme-mode="dark"] .ag-grid-tree-layouts-panel .btn:hover,
html[data-theme-mode="dark"] .ag-grid-filter-logic-panel .btn:hover {
@@ -12835,6 +12945,26 @@ html[data-theme-mode="dark"] .ag-grid-summary-value {
color: #8fe1d8;
}
html[data-theme-mode="dark"] .ag-grid-summary-item-sum .ag-grid-summary-value {
color: #111111;
}
html[data-theme-mode="dark"] .ag-grid-summary-item-variation {
background: #172321;
}
html[data-theme-mode="dark"] .ag-grid-summary-variation-positive {
border-color: transparent;
background: transparent;
color: #86efac;
}
html[data-theme-mode="dark"] .ag-grid-summary-variation-negative {
border-color: transparent;
background: transparent;
color: #fca5a5;
}
html[data-theme-mode="dark"] .form-tolerance-gauge {
color: #dce7e2;
}

View File

@@ -3610,7 +3610,14 @@ var Sao = {
};
Sao.common.parse_datetime = function(datetime_format, value) {
var date = moment(value, Sao.common.moment_format(datetime_format));
var formats = [
Sao.common.moment_format(datetime_format),
'YYYY-MM-DDTHH:mm',
'YYYY-MM-DDTHH:mm:ss',
'YYYY-MM-DD HH:mm',
'YYYY-MM-DD HH:mm:ss',
];
var date = moment(value, formats, true);
if (date.isValid()) {
date = Sao.DateTime(date.year(), date.month(), date.date(),
date.hour(), date.minute(), date.second(),
@@ -16797,6 +16804,21 @@ function eval_pyson(value){
this.input.appendTo(this.icon);
Sao.common.ICONFACTORY.get_icon_img('tryton-date')
.appendTo(this.icon);
this.icon.on('click', (event_) => {
if (this.date.prop('readonly')) {
return;
}
if (event_.target == this.input[0]) {
return;
}
event_.preventDefault();
event_.stopPropagation();
if (this.input[0].showPicker) {
this.input[0].showPicker();
} else {
this.input.trigger('click');
}
});
}
this.date.change(this.focus_out.bind(this));
var mousetrap = new Mousetrap(this.date[0]);
@@ -16885,7 +16907,7 @@ function eval_pyson(value){
Sao.View.Form.DateTime = Sao.class_(Sao.View.Form.Date, {
class_: 'form-datetime',
_input: 'datetime-local',
_input_format: '%Y-%m-%dT%H:%M:%S',
_input_format: '%Y-%m-%dT%H:%M',
_format: Sao.common.format_datetime,
_parse: Sao.common.parse_datetime,
get_format: function() {
@@ -16910,9 +16932,10 @@ function eval_pyson(value){
_parse: Sao.common.parse_time,
init: function(view, attributes) {
Sao.View.Form.Time._super.init.call(this, view, attributes);
if (~navigator.userAgent.indexOf("Firefox")) {
// time input on Firefox does not have a pop-up
this.input.parent().hide();
if (this.icon) {
this.input.remove();
this.icon.remove();
delete this.icon;
}
},
get_format: function() {
@@ -18351,6 +18374,7 @@ function eval_pyson(value){
view_ids: (attributes.view_ids || '').split(','),
views_preload: attributes.views || {},
order: attributes.order,
one2many: true,
row_activate: this.activate.bind(this),
exclude_field: attributes.relation_field || null,
limit: null,
@@ -18577,12 +18601,22 @@ function eval_pyson(value){
this.screen.domain = domain;
}
this.screen.size_limit = size_limit;
if (this.attributes.height !== undefined &&
this.screen.current_view &&
this.screen.current_view.set_embedded_height) {
this.screen.current_view.set_embedded_height(
this.attributes.height);
}
this.screen.display();
if (this.attributes.height !== undefined) {
this.screen.current_view.el
.find('.treeview,.list-form').first()
.css('min-height', this.attributes.height + 'px')
.css('max-height', this.attributes.height + 'px');
if (this.screen.current_view.set_embedded_height) {
this.screen.current_view.set_embedded_height(
this.attributes.height);
}
}
});
},
@@ -21981,8 +22015,26 @@ function eval_pyson(value){
},
_parse_field: function(node, attributes) {
var name = attributes.name;
var ColumnFactory = Sao.View.TreeXMLViewParser.WIDGETS[
attributes.widget];
var field = this.view.screen.model.fields[name];
var widget = attributes.widget || (field && field.description.type);
var column_widget = widget == 'variation' ?
(field && field.description.type || 'char') : widget;
attributes.widget = widget;
var ColumnFactory = Sao.View.TreeXMLViewParser.WIDGETS[column_widget];
if (!ColumnFactory) {
console.error('[TREE][UNKNOWN_WIDGET]', {
viewId: this.view.view_id,
model: this.view.screen && this.view.screen.model_name,
field: name,
widget: column_widget,
attributes: attributes,
node: node.outerHTML,
availableWidgets: Object.keys(
Sao.View.TreeXMLViewParser.WIDGETS).sort(),
});
attributes.widget = 'char';
ColumnFactory = Sao.View.Tree.CharColumn;
}
var column = new ColumnFactory(this.view.screen.model, attributes);
if (!this.view.widgets[name]) {
this.view.widgets[name] = [];
@@ -22022,9 +22074,21 @@ function eval_pyson(value){
column.prefixes.push(
new Sao.View.Tree.Symbol(attributes, 0));
}
var field_attrs = this.field_attrs[name] ||
(field && field.description) || {};
if (!this.field_attrs[name]) {
console.warn('[TREE][MISSING_FIELD_ATTRS]', {
viewId: this.view.view_id,
model: this.view.screen && this.view.screen.model_name,
field: name,
widget: attributes.widget,
attributes: attributes,
fallbackFieldAttributes: field_attrs,
});
}
if (!this.view.attributes.sequence &&
!this.view.children_field &&
this.field_attrs[name].sortable !== false){
field_attrs.sortable !== false){
column.sortable = true;
}
this.view.columns.push(column);
@@ -22041,6 +22105,18 @@ function eval_pyson(value){
'class': 'value',
});
this.view.sum_widgets.set(column, [sum, aggregate]);
if (window.console && this.view instanceof Sao.View.AGGridTree) {
console.info('[AGGRID][SUMMARY_SUM_COLUMN_JSON]',
JSON.stringify({
viewId: this.view.view_id,
model: this.view.screen &&
this.view.screen.model_name,
name: attributes.name || null,
label: attributes.string || attributes.name || '',
widget: attributes.widget || '',
sum: attributes.sum || '',
}));
}
}
},
_parse_button: function(node, attributes) {
@@ -22462,6 +22538,8 @@ function eval_pyson(value){
(Sao.View.AGGridTree._next_debug_id || 0) + 1;
this._debug_id = Sao.View.AGGridTree._next_debug_id;
this.children_field = children_field;
this.is_embedded_one2many = Boolean(
screen.attributes && screen.attributes.one2many);
this.optionals = [];
this.sum_widgets = new Map();
this.columns = [];
@@ -22524,6 +22602,9 @@ function eval_pyson(value){
this.toolbar = jQuery('<div/>', {
'class': 'ag-grid-tree-toolbar',
}).appendTo(this.el);
if (this.is_embedded_one2many) {
this.el.addClass('ag-grid-tree-embedded-one2many');
}
this.column_menu = jQuery('<div/>', {
'class': 'ag-grid-tree-columns',
}).appendTo(this.toolbar);
@@ -22610,6 +22691,14 @@ function eval_pyson(value){
this._toggle_filter_logic_menu();
})
.appendTo(this.toolbar);
this.import_layout_input = jQuery('<input/>', {
'type': 'file',
'accept': 'application/json,.json',
'class': 'ag-grid-view-import-input',
}).hide().appendTo(this.el);
this.import_layout_input.on('change', evt => {
this._import_layout_from_input(evt.currentTarget);
});
this.filter_logic_panel = jQuery('<div/>', {
'class': 'ag-grid-filter-logic-panel',
}).hide().appendTo(jQuery(document.body));
@@ -22685,8 +22774,18 @@ function eval_pyson(value){
});
Sao.View.AGGridTree._super.init.call(this, view_id, screen, xml);
this._persisted_state = this._load_persisted_state();
if (this._persisted_state && this._persisted_state.valueFilters) {
if (this.is_embedded_one2many) {
this._layout_store = this._normalize_layout_store(null);
this._persisted_state = null;
this._sao_value_filters = {};
this._restored_value_filter_state = null;
this._sao_value_filter_restore_guard = false;
} else {
this._persisted_state = this._load_persisted_state();
}
if (!this.is_embedded_one2many &&
this._persisted_state &&
this._persisted_state.valueFilters) {
this._apply_sao_value_filter_state(
this._persisted_state.valueFilters);
}
@@ -22967,43 +23066,6 @@ function eval_pyson(value){
});
},
_trace_aggrid_column_resize: function(reason, params, extra) {
if (!window.console) {
return;
}
var column_api = this.gridColumnApi || this.gridApi;
var column_state = column_api && column_api.getColumnState ?
column_api.getColumnState() : [];
var target_col_id = null;
if (params && params.column) {
if (params.column.getColId) {
target_col_id = params.column.getColId();
} else if (params.column.colId) {
target_col_id = params.column.colId;
}
}
console.info('[AGGRID][COLUMN_RESIZE_JSON]',
JSON.stringify(jQuery.extend({
reason: reason,
instanceId: this._debug_id,
viewId: this.view_id,
model: this.screen && this.screen.model_name,
targetColId: target_col_id,
finished: params && Object.prototype.hasOwnProperty.call(
params, 'finished') ? params.finished : null,
source: params && params.source || null,
resizeActive: Boolean(this._column_resize_active),
activeLayoutId: this._active_layout_id || null,
columnWidths: (column_state || [])
.filter(state => state && state.colId != '__selection__')
.map(state => ({
colId: state.colId,
width: state.width,
minWidth: state.minWidth || null,
flex: state.flex || null,
})),
pendingTimers: this._layout_refresh_timers ?
this._layout_refresh_timers.length : 0,
}, extra || {})));
},
_refresh_grid_layout_now: function() {
if (!this.gridApi || !this.grid_host || !this.grid_host.length) {
@@ -23080,6 +23142,9 @@ function eval_pyson(value){
window.addEventListener('resize', this._window_resize_handler);
},
_persist_grid_state: function(options) {
if (this.is_embedded_one2many) {
return;
}
options = options || {};
var state = this._capture_grid_state();
state.valueFilters = this._merge_restored_value_filter_state(
@@ -24050,6 +24115,110 @@ function eval_pyson(value){
this._layout_store.defaultPresetId = null;
this._apply_layout_state(this._get_standard_layout_state(), null);
},
_sanitize_layout_file_name: function(name) {
name = String(name || Sao.i18n.gettext('AG Grid view')).trim();
name = name.replace(/[\\/:*?"<>|]+/g, '-')
.replace(/\s+/g, ' ')
.trim();
return name || 'ag-grid-view';
},
_get_layout_export_payload: function() {
var preset = this._find_layout_preset(this._active_layout_id);
var name = preset && !this._layout_dirty ? preset.name :
this._get_active_layout_name().replace(/\s+\*$/, '');
return {
type: 'sao-ag-grid-view',
version: 1,
model: this.screen && this.screen.model_name || null,
viewId: this.view_id || null,
name: name || Sao.i18n.gettext('AG Grid view'),
exportedAt: new Date().toISOString(),
state: this._capture_grid_state(),
};
},
_export_current_layout: function() {
var payload = this._get_layout_export_payload();
var file_name = this._sanitize_layout_file_name(
payload.model + '-' + payload.name) + '.json';
Sao.common.download_file(
JSON.stringify(payload, null, 2),
file_name,
{type: 'application/json;charset=utf-8'});
},
_decode_imported_layout_text: function(data) {
if (typeof data == 'string') {
return data;
}
if (data instanceof Uint8Array) {
return new TextDecoder('utf-8').decode(data);
}
return String(data || '');
},
_import_layout_from_input: function(input) {
var file = input && input.files && input.files[0];
if (!file) {
return;
}
Sao.common.get_file_data(file, data => {
var payload;
try {
payload = JSON.parse(this._decode_imported_layout_text(data));
} catch (error) {
Sao.common.warning.run(
Sao.i18n.gettext('The selected file is not a valid AG Grid view.'),
Sao.i18n.gettext('Import view'));
return;
}
this._import_layout_payload(payload);
});
},
_import_layout_payload: function(payload) {
if (!payload || payload.type != 'sao-ag-grid-view' ||
!payload.state) {
Sao.common.warning.run(
Sao.i18n.gettext('The selected file is not a valid AG Grid view.'),
Sao.i18n.gettext('Import view'));
return;
}
if (payload.model && payload.model != this.screen.model_name) {
Sao.common.warning.run(
Sao.i18n.gettext('This view is for another model.') +
' ' + payload.model + ' / ' + this.screen.model_name,
Sao.i18n.gettext('Import view'));
return;
}
var name = String(payload.name ||
Sao.i18n.gettext('Imported view')).trim();
if (!name) {
name = Sao.i18n.gettext('Imported view');
}
this._layout_store = this._normalize_layout_store(
this._layout_store || {});
var existing_names = new Set((this._layout_store.presets || [])
.map(preset => preset.name));
var base_name = name;
var suffix = 2;
while (existing_names.has(name)) {
name = base_name + ' ' + suffix;
suffix += 1;
}
var now = new Date().toISOString();
var preset = {
id: this._new_layout_id(),
name: name,
importedAt: now,
updatedAt: now,
state: jQuery.extend(true, {}, payload.state),
};
this._layout_store.presets.push(preset);
this._active_layout_id = preset.id;
this._layout_dirty = false;
this._layout_store.activePresetId = preset.id;
this._layout_store.currentState = jQuery.extend(true, {}, preset.state);
this._persisted_state = jQuery.extend(true, {}, preset.state);
this._save_layout_store();
this._apply_layout_state(preset.state, preset.id);
},
_save_layout_as: function() {
var name = window.prompt(Sao.i18n.gettext('View name'));
if (!name) {
@@ -24271,6 +24440,30 @@ function eval_pyson(value){
evt.preventDefault();
this._apply_standard_layout_state();
}).appendTo(actions);
var transfer_actions = jQuery('<div/>', {
'class': 'ag-grid-tree-layouts-transfer-actions',
}).appendTo(actions);
jQuery('<button/>', {
'class': 'btn btn-default btn-sm ag-grid-view-transfer-button',
'type': 'button',
'title': Sao.i18n.gettext('Export view'),
'aria-label': Sao.i18n.gettext('Export view'),
'text': Sao.i18n.gettext('Export'),
}).click(evt => {
evt.preventDefault();
this._export_current_layout();
}).appendTo(transfer_actions);
jQuery('<button/>', {
'class': 'btn btn-default btn-sm ag-grid-view-transfer-button',
'type': 'button',
'title': Sao.i18n.gettext('Import view'),
'aria-label': Sao.i18n.gettext('Import view'),
'text': Sao.i18n.gettext('Import'),
}).click(evt => {
evt.preventDefault();
this.import_layout_input.val(null);
this.import_layout_input[0].click();
}).appendTo(transfer_actions);
},
_render_grouping_panel: function() {
if (!this.grouping_panel) {
@@ -24635,6 +24828,12 @@ function eval_pyson(value){
this._sync_selection_header_checkbox();
this._update_summary_bar();
},
onFirstDataRendered: () => {
window.requestAnimationFrame(() => this._update_summary_bar());
},
onRowDataUpdated: () => {
window.requestAnimationFrame(() => this._update_summary_bar());
},
onColumnMoved: this._on_grid_column_layout_changed.bind(this),
onColumnPinned: this._on_grid_column_layout_changed.bind(this),
onColumnResized: this._on_grid_column_resized.bind(this),
@@ -24844,16 +25043,6 @@ function eval_pyson(value){
},
};
console.info('[AGGRID][ALIGN_COLUMN]', {
colId: name,
label: column.attributes.string || '',
widget: column.attributes.widget || '',
className: column.class_ || '',
rightAligned: right_aligned,
cellClass: col_def.cellClass,
headerClass: col_def.headerClass,
});
column_defs.push(col_def);
});
return column_defs;
@@ -26077,7 +26266,7 @@ function eval_pyson(value){
var pair = item.split(':');
return pair.length < 2 ? '' :
this._normalize_badge_text_key(pair.shift());
}).filter(Boolean);
}).filter(value => value && value != '*');
},
_get_record_field_value: function(record, name) {
if (!record || !name) {
@@ -26169,6 +26358,8 @@ function eval_pyson(value){
} else if (Object.prototype.hasOwnProperty.call(
mapped, String(normalized_text).toLowerCase())) {
color = mapped[String(normalized_text).toLowerCase()];
} else if (Object.prototype.hasOwnProperty.call(mapped, '*')) {
color = mapped['*'];
}
if (!color && attributes.badge_color_field) {
color = this._get_record_field_value(
@@ -26714,7 +26905,7 @@ function eval_pyson(value){
}
return rows;
},
_get_summary_badge_columns: function() {
_get_displayed_column_map: function() {
var visible_columns = {};
var column_api = this.gridColumnApi || this.gridApi;
if (column_api && column_api.getAllDisplayedColumns) {
@@ -26724,37 +26915,336 @@ function eval_pyson(value){
}
});
}
return visible_columns;
},
_get_viewport_column_map: function() {
var visible_columns = {};
var column_api = this.gridColumnApi || this.gridApi;
var columns = null;
if (column_api && column_api.getAllDisplayedVirtualColumns) {
columns = column_api.getAllDisplayedVirtualColumns();
} else if (column_api && column_api.getAllDisplayedColumns) {
columns = column_api.getAllDisplayedColumns();
}
(columns || []).forEach(column => {
if (column && column.getColId) {
visible_columns[column.getColId()] = true;
}
});
return visible_columns;
},
_is_summary_column_visible: function(column, visible_columns) {
var name = column && column.attributes && column.attributes.name;
return name && (!Object.keys(visible_columns).length ||
visible_columns[name]);
},
_get_summary_badge_columns: function() {
return this.columns.filter(column => {
var name = column.attributes && column.attributes.name;
var label = column.attributes &&
(column.attributes.string || column.attributes.name) || '';
var is_status_column = /(^|[_\s-])(status|state|type)([_\s-]|$)/i
.test(String(name || '') + ' ' + String(label || ''));
if (!name || (!this._is_badge_column(column) &&
!is_status_column)) {
return false;
}
return !Object.keys(visible_columns).length ||
visible_columns[name];
return name && column.attributes.widget == 'badge';
});
},
_render_summary_item: function(label, value, color) {
_get_summary_sum_columns: function() {
var columns = [];
this.sum_widgets.forEach((sum_widget, column) => {
if (column && column.attributes && column.attributes.name) {
columns.push(column);
}
});
return columns;
},
_get_summary_variation_columns: function() {
return this.columns.filter(column => {
var attributes = column && column.attributes || {};
return attributes.name &&
attributes.widget == 'variation' &&
attributes.variation_summary == '1' &&
attributes.variation_previous &&
attributes.variation_group_by;
});
},
_render_summary_item: function(
label, value, color, class_name, variation_text,
variation_class) {
var item = jQuery('<div/>', {
'class': 'ag-grid-summary-item',
'class': 'ag-grid-summary-item' +
(class_name ? ' ' + class_name : ''),
});
jQuery('<span/>', {
'class': 'ag-grid-summary-label',
'text': label,
}).appendTo(item);
if (variation_text) {
jQuery('<span/>', {
'class': 'ag-grid-summary-variation-badge ' +
(variation_class || ''),
'text': variation_text,
}).appendTo(item);
}
var value_wrap = jQuery('<span/>', {
'class': 'ag-grid-summary-value-wrap',
}).appendTo(item);
var value_el = jQuery('<span/>', {
'class': 'ag-grid-summary-value',
'text': value,
}).appendTo(item);
}).appendTo(value_wrap);
if (color) {
value_el.css('color', color);
}
return item;
},
_trace_summary_bar: function(
rows, badge_columns, sum_columns, variation_columns, summary_items) {
if (!window.console) {
return;
}
var describe = column => ({
name: column && column.attributes && column.attributes.name || null,
label: column && column.attributes &&
(column.attributes.string || column.attributes.name) || '',
widget: column && column.attributes && column.attributes.widget || '',
sum: column && column.attributes && column.attributes.sum || '',
badge: Boolean(column && column.attributes &&
column.attributes.widget == 'badge'),
});
console.info('[AGGRID][SUMMARY_BAR_JSON]', JSON.stringify({
instanceId: this._debug_id,
viewId: this.view_id,
model: this.screen && this.screen.model_name,
rowCount: rows.length,
viewportColumns: Object.keys(this._get_viewport_column_map()),
displayedColumns: Object.keys(this._get_displayed_column_map()),
badgeColumns: badge_columns.map(describe),
sumColumns: sum_columns.map(describe),
variationColumns: variation_columns.map(describe),
sumWidgetColumns: Array.from(this.sum_widgets.keys()).map(describe),
badgeLikeColumns: this.columns.filter(column =>
this._is_badge_column(column)).map(describe),
summaryItems: summary_items.map(item => ({
label: item.label,
count: item.count,
className: item.className || '',
})),
}));
},
_get_summary_column_load_fields: function(column) {
var attributes = column && column.attributes || {};
var fields = [];
if (attributes.name) {
fields.push(attributes.name);
}
if (attributes.widget == 'variation') {
if (attributes.variation_previous) {
fields.push(attributes.variation_previous);
}
if (attributes.variation_group_by) {
fields.push(attributes.variation_group_by);
}
}
if (attributes.sum_variation == '1' && attributes.sum_previous) {
fields.push(attributes.sum_previous);
}
return fields.filter((field, index) =>
field && fields.indexOf(field) == index);
},
_load_summary_column_values: function(rows, columns, reason) {
if (this._summary_sum_loading) {
return null;
}
var promises = [];
var field_names = [];
columns.forEach(column => {
var names = this._get_summary_column_load_fields(column);
field_names = field_names.concat(names);
rows.forEach(row => {
var record = row && row.__record;
names.forEach(name => {
if (record && record.id >= 0 &&
!record.get_loaded([name])) {
promises.push(record.load(name, true, false));
}
});
});
});
if (!promises.length) {
return null;
}
this._summary_sum_loading = true;
if (window.console) {
console.info('[AGGRID][SUMMARY_SUM_LOAD_JSON]',
JSON.stringify({
instanceId: this._debug_id,
viewId: this.view_id,
model: this.screen && this.screen.model_name,
reason: reason || 'summary',
fields: field_names.filter((field, index) =>
field && field_names.indexOf(field) == index),
promiseCount: promises.length,
}));
}
return jQuery.when.apply(jQuery, promises).always(() => {
this._summary_sum_loading = false;
});
},
_load_summary_sum_values: function(rows, columns) {
return this._load_summary_column_values(rows, columns, 'sum');
},
_load_summary_badge_values: function(rows, columns) {
return this._load_summary_column_values(rows, columns, 'badge');
},
_append_sum_summary_item: function(summary_items, rows, column) {
var sum = null;
var name = column && column.attributes && column.attributes.name;
var field = name ? this.screen.model.fields[name] : null;
var previous_sum = null;
var previous_name = column && column.attributes &&
column.attributes.sum_previous;
var with_variation = column && column.attributes &&
column.attributes.sum_variation == '1' && previous_name;
rows.forEach(row => {
var record = row && row.__record;
if (!field || !record || record.id >= 0 &&
!record.get_loaded([name])) {
return;
}
var value = field.get(record);
if (value && value.isTimeDelta) {
value = value.asSeconds();
}
if (value !== null && value !== undefined && value !== '') {
sum = sum === null ? value : sum + value;
}
if (with_variation) {
var previous = this._get_record_raw_field_value(
record, previous_name);
if (previous !== null && previous !== undefined &&
previous !== '') {
previous = Number(previous);
if (isFinite(previous)) {
previous_sum = previous_sum === null ?
previous : previous_sum + previous;
}
}
}
});
var variation = null;
if (with_variation && sum !== null && previous_sum !== null &&
previous_sum !== 0) {
variation = ((sum - previous_sum) /
Math.abs(previous_sum)) * 100;
}
summary_items.push({
label: column.attributes.string || column.attributes.name,
count: sum === null ? '-' :
this._format_aggregate_value(column, sum),
color: '#111111',
className: 'ag-grid-summary-item-sum',
variationText: variation === null ? '' :
this._format_sum_variation_percent(variation),
variationClass: variation === null ? '' :
(variation < 0 ? 'ag-grid-summary-variation-negative' :
'ag-grid-summary-variation-positive'),
});
},
_get_record_raw_field_value: function(record, name) {
if (!record || !name) {
return null;
}
var field = this.screen.model.fields[name];
if (!field || record.id >= 0 && !record.get_loaded([name])) {
return null;
}
var value = field.get(record);
if (value && value.isTimeDelta) {
value = value.asSeconds();
}
if (value === null || value === undefined || value === '') {
return null;
}
return value;
},
_get_record_text_field_value: function(record, name) {
if (!record || !name) {
return '';
}
var field = this.screen.model.fields[name];
if (!field || record.id >= 0 && !record.get_loaded([name])) {
return '';
}
try {
return field.get_client(record);
} catch (error) {
var value = this._get_record_raw_field_value(record, name);
return value === null || value === undefined ? '' : String(value);
}
},
_format_variation_percent: function(value) {
var locale = Sao.i18n.BC47(Sao.i18n.getlang());
var sign = value > 0 ? '+' : '';
return sign + value.toLocaleString(locale, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
}) + '%';
},
_format_sum_variation_percent: function(value) {
var locale = Sao.i18n.BC47(Sao.i18n.getlang());
var sign = value >= 0 ? '+' : '';
return sign + value.toLocaleString(locale, {
minimumFractionDigits: 0,
maximumFractionDigits: 2,
}) + '%';
},
_append_variation_summary_items: function(summary_items, rows, column) {
var attributes = column && column.attributes || {};
var current_name = attributes.name;
var previous_name = attributes.variation_previous;
var group_name = attributes.variation_group_by;
var groups = {};
rows.forEach(row => {
var record = row && row.__record;
if (!record) {
return;
}
var current = Number(this._get_record_raw_field_value(
record, current_name));
var previous = Number(this._get_record_raw_field_value(
record, previous_name));
var group = this._get_record_text_field_value(
record, group_name) || Sao.i18n.gettext('(Empty)');
if (!isFinite(current) || !isFinite(previous) || previous === 0) {
return;
}
if (!groups[group]) {
groups[group] = {
current: 0,
previous: 0,
};
}
groups[group].current += current;
groups[group].previous += previous;
});
Object.keys(groups).sort((a, b) => String(a).localeCompare(
String(b), undefined, {numeric: true, sensitivity: 'base'}))
.forEach(group => {
var values = groups[group];
if (!values.previous) {
return;
}
var variation = ((values.current - values.previous) /
Math.abs(values.previous)) * 100;
summary_items.push({
label: group,
count: this._format_variation_percent(variation),
color: variation > 0 ? '#16a34a' :
variation < 0 ? '#dc2626' : '#64748b',
className: 'ag-grid-summary-item-variation' +
(variation > 0 ? ' ag-grid-summary-variation-up' :
variation < 0 ?
' ag-grid-summary-variation-down' :
' ag-grid-summary-variation-flat'),
});
});
},
_get_default_badge_summary_color: function(value) {
var key = String(value || '').toLowerCase();
var palette = {
@@ -26785,8 +27275,16 @@ function eval_pyson(value){
var order = [];
var empty = Sao.i18n.gettext('(Empty)');
rows.forEach(row => {
var value = row[name] ?
this._normalize_badge_text_key(row[name]) : empty;
var raw_value = '';
if (column && row && row.__record) {
raw_value = this._get_column_text_value(
column, row.__record);
}
if (!raw_value && row) {
raw_value = row[name];
}
var value = raw_value ?
this._normalize_badge_text_key(raw_value) : empty;
if (value == empty) {
return;
}
@@ -26836,27 +27334,6 @@ function eval_pyson(value){
});
return Boolean(order.length);
},
_get_summary_status_sources_from_grid: function() {
var sources = [];
var seen = {};
(this._grid_columns || []).forEach(col_def => {
var name = col_def && col_def.field || col_def && col_def.colId;
if (!name || name == '__selection__' || seen[name]) {
return;
}
var label = col_def.headerName || name;
if (!/(status|state|type|shipping|shipment|open\/physic)/i
.test(String(name) + ' ' + String(label))) {
return;
}
seen[name] = true;
sources.push({
name: name,
label: label,
});
});
return sources;
},
_update_summary_bar: function() {
if (!this.summary_bar || !this.summary_bar.length) {
return;
@@ -26869,25 +27346,38 @@ function eval_pyson(value){
rows.length.toLocaleString(locale),
'#145369'));
var summary_items = [];
var summarized_names = {};
this._get_summary_badge_columns().forEach(column => {
if (this._append_badge_summary_items(
summary_items, rows, column)) {
summarized_names[column.attributes.name] = true;
}
});
this._get_summary_status_sources_from_grid().forEach(source => {
if (summarized_names[source.name]) {
return;
}
var sum_columns = this._get_summary_sum_columns();
var badge_columns = this._get_summary_badge_columns();
var variation_columns = this._get_summary_variation_columns();
var summary_load = this._load_summary_column_values(
rows, badge_columns.concat(sum_columns, variation_columns),
'summary');
if (summary_load) {
summary_load.then(() => this._update_summary_bar());
}
badge_columns.forEach(column => {
this._append_badge_summary_items(
summary_items, rows, null, source);
summary_items, rows, column);
});
sum_columns.forEach(column => {
this._append_sum_summary_item(summary_items, rows, column);
});
variation_columns.forEach(column => {
this._append_variation_summary_items(
summary_items, rows, column);
});
this._trace_summary_bar(
rows, badge_columns, sum_columns, variation_columns,
summary_items);
summary_items.forEach(item => {
this.summary_bar.append(this._render_summary_item(
item.label,
item.count.toLocaleString(locale),
item.color));
typeof item.count == 'number' ?
item.count.toLocaleString(locale) : item.count,
item.color,
item.className,
item.variationText,
item.variationClass));
});
},
on_copy: function() {
@@ -27052,7 +27542,10 @@ function eval_pyson(value){
var readable_min_height = 420;
var maximum_height = 720;
var height;
if (available >= compact_min_height) {
if (this.is_embedded_one2many && this._embedded_height) {
height = Math.max(80, this._embedded_height - toolbar_height -
summary_height - external_scroll_height);
} else if (available >= compact_min_height) {
height = Math.max(
compact_min_height,
Math.min(maximum_height, available));
@@ -27104,6 +27597,17 @@ function eval_pyson(value){
});
}
},
set_embedded_height: function(height) {
height = parseInt(height, 10);
if (!this.is_embedded_one2many || isNaN(height) || height <= 0) {
return;
}
this._embedded_height = height;
this._resize_grid_host();
if (this.gridApi && this.gridApi.doLayout) {
this.gridApi.doLayout();
}
},
_refresh_grid_layout_after_row_data: function(row_data) {
if (!this.gridApi) {
return;
@@ -27319,7 +27823,7 @@ function eval_pyson(value){
} else if (diagnostics.relationWidget) {
diagnostics.fallbackReason = diagnostics.relationWidget +
'_relation_widget';
} else if (diagnostics.embeddedContext) {
} else if (diagnostics.embeddedContext && diagnostics.editable) {
diagnostics.fallbackReason = 'embedded_relation_tree';
} else if (diagnostics.editable) {
diagnostics.fallbackReason = 'editable_view';
@@ -30481,6 +30985,7 @@ function eval_pyson(value){
'timedelta': Sao.View.Tree.TimeDeltaColumn,
'tolerance_gauge': Sao.View.Tree.ToleranceGauge,
'url': Sao.View.Tree.URLColumn,
'variation': Sao.View.Tree.CharColumn,
};
Sao.View.EditableTree = {};