diff --git a/dist/tryton-sao.js b/dist/tryton-sao.js index 4c0b763..02bb1c1 100644 --- a/dist/tryton-sao.js +++ b/dist/tryton-sao.js @@ -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('
', { '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('
', { '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('', { + '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('
', { '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('
', { + 'class': 'ag-grid-tree-layouts-transfer-actions', + }).appendTo(actions); + jQuery('