diff --git a/issue8240.diff b/issue8240.diff new file mode 100644 index 0000000..c5cf1a6 --- /dev/null +++ b/issue8240.diff @@ -0,0 +1,509 @@ +diff -r 02a9082b44d1 bower.json +--- a/public_data/sao/bower.json Mon Jun 03 15:45:06 2019 +0200 ++++ b/public_data/sao/bower.json Tue Jun 04 12:04:17 2019 +0200 +@@ -24,7 +24,8 @@ + "papaparse": "^4.1", + "fullcalendar": "^3.0", + "mousetrap": "^1.6", +- "bootstrap-rtl-ondemand": "^3.3.4-ondemand" ++ "bootstrap-rtl-ondemand": "^3.3.4-ondemand", ++ "Sortable": "sortablejs#^1.8.4" + }, + "devDependencies": { + "qunit": "^1.18" +diff -r 02a9082b44d1 index.html +--- a/public_data/sao/index.html Mon Jun 03 15:45:06 2019 +0200 ++++ b/public_data/sao/index.html Tue Jun 04 12:04:17 2019 +0200 +@@ -21,6 +21,7 @@ + + + ++ + + + +diff -r 02a9082b44d1 src/model.js +--- a/public_data/sao/src/model.js Mon Jun 03 15:45:06 2019 +0200 ++++ b/public_data/sao/src/model.js Tue Jun 04 12:04:17 2019 +0200 +@@ -473,6 +473,48 @@ + }; + return jQuery.when().then(browse_child); + }; ++ array.set_sequence = function(field) { ++ var changed = false; ++ var prev = null; ++ var record, index, update, value; ++ for (var i=0; i < this.length; i++) { ++ record = this[i]; ++ if (record.get_loaded([field]) || changed || record.id < 0) { ++ if (prev) { ++ index = prev.field_get(field); ++ } else { ++ index = null; ++ } ++ update = false; ++ value = record.field_get(field); ++ if (value === null) { ++ if (index) { ++ update = true; ++ } else if (prev && (record.id >= 0)) { ++ update = record.id < prev.id; ++ } ++ } else if (value === index) { ++ if (prev && (record.id >= 0)) { ++ update = record.id < prev.id; ++ } ++ } else if (value <= (index || 0)) { ++ update = true; ++ } ++ if (update) { ++ if (index === null) { ++ index = 0; ++ } ++ index += 1; ++ record.field_set_client(field, index); ++ changed = record; ++ } ++ } ++ prev = record; ++ } ++ if (changed) { ++ this.changed(); ++ } ++ }; + return array; + }; + +@@ -1265,6 +1307,33 @@ + path.reverse(); + return path; + }, ++ children_group: function(field_name) { ++ var group_prm = jQuery.Deferred(); ++ if (!field_name) { ++ group_prm.resolve([]); ++ return group_prm; ++ } ++ var load_prm = this._check_load([field_name]); ++ load_prm.done(function() { ++ var group = this._values[field_name]; ++ if (group === undefined) { ++ group_prm.resolve(null); ++ return; ++ } ++ ++ if (group.model.fields !== this.group.model.fields) { ++ jQuery.extend(this.group.model.fields, group.model.fields); ++ group.model.fields = this.group.model.fields; ++ } ++ group.on_write = this.group.on_write; ++ group.readonly = this.group.readonly; ++ jQuery.extend(group._context, this.group._context); ++ ++ group_prm.resolve(group); ++ return; ++ }.bind(this)); ++ return group_prm; ++ }, + get deleted() { + return Boolean(~this.group.record_deleted.indexOf(this)); + }, +diff -r 02a9082b44d1 src/sao.less +--- a/public_data/sao/src/sao.less Mon Jun 03 15:45:06 2019 +0200 ++++ b/public_data/sao/src/sao.less Tue Jun 04 12:04:17 2019 +0200 +@@ -207,10 +207,13 @@ + width: 100%; + table-layout: fixed; + +- col.selection-state, col.favorite { ++ col.selection-state, col.favorite, col.draggable-handle { + width: 30px; + } + ++ td.draggable-handle { ++ cursor: grab; ++ } + th.selection-state, td.selection-state { + > input { + .center-block(); +@@ -239,6 +242,13 @@ + vertical-align: middle; + } + } ++ > tbody > tr.dragged-row { ++ background-color: @state-info-bg; ++ } ++ > tbody > tr.children-container { ++ min-height: @line-height-base; ++ border: 1px dashed @gray-light; ++ } + + td.prefix { + padding-right: @table-cell-padding; +diff -r 02a9082b44d1 src/view/tree.js +--- a/public_data/sao/src/view/tree.js Mon Jun 03 15:45:06 2019 +0200 ++++ b/public_data/sao/src/view/tree.js Tue Jun 04 12:04:17 2019 +0200 +@@ -76,6 +76,7 @@ + Sao.View.Tree = Sao.class_(Sao.View, { + view_type: 'tree', + xml_parser: Sao.View.TreeXMLViewParser, ++ draggable: false, + init: function(view_id, screen, xml, children_field) { + this.children_field = children_field; + this.sum_widgets = {}; +@@ -99,10 +100,10 @@ + this.table.addClass('table-bordered'); + } + this.el.append(this.table); +- var colgroup = jQuery('').appendTo(this.table); ++ this.colgroup = jQuery('').appendTo(this.table); + var col = jQuery('', { + 'class': 'selection-state', +- }).appendTo(colgroup); ++ }).appendTo(this.colgroup); + if (this.selection_mode == Sao.common.SELECTION_NONE) { + col.css('width', 0); + } +@@ -132,7 +133,7 @@ + this.columns.forEach(function(column) { + col = jQuery('', { + 'class': column.attributes.widget, +- }).appendTo(colgroup); ++ }).appendTo(this.colgroup); + th = jQuery('', { + 'class': column.attributes.widget, + }); +@@ -183,6 +184,8 @@ + this.tbody = jQuery(''); + this.table.append(this.tbody); + ++ this.set_drag_and_drop(); ++ + this.display_size = Sao.config.display_size; + }, + get editable() { +@@ -278,6 +281,195 @@ + } + }); + }, ++ _add_drag_n_drop: function() { ++ Sortable.create(this.tbody[0], { ++ animation: 150, ++ handle: '.draggable-handle', ++ ghostClass: 'dragged-row' ++ }); ++ this.tbody.on('dragstart', this.drag_data_get.bind(this)); ++ this.tbody.on('drop', this.drag_data_received.bind(this)); ++ }, ++ set_drag_and_drop: function() { ++ var dnd = false; ++ var children, parent_name; ++ if (this.children_field) { ++ children = this.screen.model.fields[this.children_field]; ++ if (children) { ++ parent_name = children.description.relation_field; ++ dnd = Boolean(this.widgets[parent_name]); ++ } ++ } else if (this.attributes.sequence) { ++ dnd = true; ++ } ++ if (this.screen.readonly) { ++ dnd = false; ++ } ++ ++ this.draggable = dnd; ++ if (dnd) { ++ this.colgroup.prepend(jQuery('', { ++ 'class': 'draggable-handle', ++ })); ++ this.thead.children().prepend(jQuery('', { ++ 'class': 'draggable-handle', ++ })); ++ this._add_drag_n_drop(); ++ } ++ }, ++ drag_data_get: function(evt) { ++ var row_position = 0; ++ var row_leaves = []; ++ var set_dragged_row = function(row) { ++ if (row.el[0] === evt.target) { ++ evt.originalEvent.dataTransfer.setData('path', row.path); ++ evt.originalEvent.dataTransfer.setData( ++ 'position', row_position); ++ } ++ if (row.rows.length === 0) { ++ row_leaves.push(row); ++ } ++ row_position += 1; ++ row.rows.forEach(set_dragged_row.bind(this)); ++ }; ++ this.rows.forEach(set_dragged_row.bind(this)); ++ ++ if (this.children_field) { ++ var child_row; ++ for (var i = 0; i < row_leaves.length; i++) { ++ child_row = jQuery('', { ++ 'class': 'children-container', ++ }).append(jQuery('', { ++ colspan: this.columns.length, ++ })); ++ row_leaves[i].el.after(child_row); ++ } ++ } ++ }, ++ drag_data_received: function(evt) { ++ var dataTransfer = evt.originalEvent.dataTransfer; ++ var origin_path = dataTransfer.getData('path').split('.'); ++ if (origin_path.length === 0) { ++ return ; ++ } ++ ++ var row = this; ++ while (origin_path.length > 0) { ++ row = row.rows[origin_path[0]]; ++ origin_path = origin_path.slice(1); ++ } ++ ++ var parent_row = null; ++ var parent_tr; ++ var next_sibling_row; ++ var next_sibling_tr = row.el.next(); ++ if (next_sibling_tr.length === 0) { ++ next_sibling_row = null; ++ parent_row = null; ++ } else if (next_sibling_tr.hasClass('children-container')) { ++ parent_tr = row.el.prev(); ++ parent_row = this._find_row(parent_tr); ++ next_sibling_row = null; ++ } else { ++ next_sibling_row = this._find_row(next_sibling_tr); ++ parent_row = next_sibling_row.parent_; ++ } ++ this.tbody.find('tr.children-container').remove(); ++ ++ var current_row = parent_row; ++ while (current_row && (current_row != row)) { ++ current_row = current_row.parent_; ++ } ++ if (current_row) { ++ // There is a recursion cancel the drop ++ // by moving the row at its previous place ++ var original_position = dataTransfer.getData('position'); ++ var successor = jQuery( ++ this.tbody.children()[original_position]); ++ successor.before(row.el); ++ return; ++ } ++ ++ var previous_row = row; ++ var move_child = function(child_row) { ++ previous_row.el.after(child_row.el); ++ previous_row = child_row; ++ child_row.rows.forEach(move_child); ++ }; ++ row.rows.forEach(move_child); ++ ++ var dest_position, dest_group_prm; ++ var origin_group, origin_position; ++ origin_group = row.record.group; ++ origin_position = row.group_position; ++ if (parent_row) { ++ if (next_sibling_row) { ++ dest_position = parent_row.rows.indexOf(next_sibling_row); ++ } else { ++ dest_position = parent_row.rows.length; ++ } ++ ++ dest_group_prm = parent_row.record.children_group( ++ this.children_field); ++ } else { ++ dest_group_prm = jQuery.Deferred().resolve(this.group); ++ if (next_sibling_row) { ++ dest_position = next_sibling_row.group_position; ++ } else { ++ dest_position = this.group.length; ++ } ++ } ++ ++ dest_group_prm.then(function(dest_group) { ++ var origin_rows, dest_rows; ++ if (row.parent_) { ++ origin_rows = row.parent_.rows; ++ } else { ++ origin_rows = this.rows; ++ } ++ if (parent_row) { ++ dest_rows = parent_row.rows; ++ } else { ++ dest_rows = this.rows; ++ } ++ ++ if (origin_group === dest_group) { ++ if (origin_position < dest_position) { ++ dest_position -= 1; ++ } ++ origin_group.splice(origin_position, 1); ++ origin_group.splice(dest_position, 0, row.record); ++ origin_group.changed(); ++ } else { ++ origin_group.remove(row.record, true, true, true); ++ dest_group.add(row.record, dest_position); ++ } ++ dest_rows.splice(dest_position, 0, row); ++ origin_rows.splice(origin_position, 1); ++ ++ row.parent_ = parent_row; ++ row.record.group = dest_group; ++ dest_rows.slice(dest_position).forEach(function(r) { ++ r.reset_path(); ++ }); ++ origin_rows.slice(origin_position).forEach(function(r) { ++ r.reset_path(); ++ }); ++ ++ var selected = this.get_selected_paths(); ++ var expanded = this.expanded; ++ row.redraw(selected, expanded); ++ var child_redraw = function(child_row) { ++ child_row.redraw(selected, expanded); ++ child_row.rows.forEach(child_redraw); ++ }; ++ row.rows.forEach(child_redraw); ++ ++ if (this.attributes.sequence) { ++ row.record.group.set_sequence(this.attributes.sequence); ++ } ++ }.bind(this)); ++ }, + get_fields: function() { + return Object.keys(this.widgets); + }, +@@ -396,7 +588,8 @@ + if (column.header.hasClass('invisible')) { + column.col.css('width', 0); + column.col.hide(); +- } else if (!column.col.hasClass('selection-state') && ++ } else if (!column.col.hasClass('draggable-handle') && ++ !column.col.hasClass('selection-state') && + !column.col.hasClass('favorite')) { + var width = { + 'integer': 6, +@@ -442,6 +635,9 @@ + if (!extend) { + this.rows = []; + this.tbody = jQuery(''); ++ if (this.draggable) { ++ this._add_drag_n_drop(); ++ } + this.edited_row = null; + } else { + this.tbody.find('tr.more-row').remove(); +@@ -802,6 +998,18 @@ + row.set_editable(); + } + this.edited_row = row; ++ }, ++ _find_row: function(tr) { ++ var row = null; ++ var find_row = function(r) { ++ if (r.el[0] == tr[0]) { ++ row = r; ++ return; ++ } ++ r.rows.forEach(find_row); ++ }; ++ this.rows.forEach(find_row); ++ return row; + } + }); + +@@ -837,15 +1045,37 @@ + this.parent_ = parent; + this.children_field = tree.children_field; + this.expander = null; +- var path = []; +- if (parent) { +- path = jQuery.extend([], parent.path.split('.')); +- } +- path.push(pos); +- this.path = path.join('.'); ++ this._group_position = null; ++ this._path = null; + this.el = jQuery(''); + this.el.on('click', this.select_row.bind(this)); + }, ++ get group_position() { ++ if (this._group_position === null) { ++ this._group_position = this.record.group.indexOf(this.record); ++ } ++ return this._group_position; ++ }, ++ get path() { ++ if (!this._path) { ++ var path, position; ++ if (this.parent_) { ++ path = jQuery.extend([], this.parent_.path.split('.')); ++ } else { ++ path = []; ++ } ++ path.push(this.group_position); ++ this._path = path.join('.'); ++ } ++ return this._path; ++ }, ++ reset_path: function() { ++ this._group_position = null; ++ this._path = null; ++ for (var i=0; i < this.rows.length; i++) { ++ this.rows[i].reset_path(); ++ } ++ }, + is_expanded: function() { + return (this.path in this.tree.expanded); + }, +@@ -875,8 +1105,15 @@ + el_node.removeChild(el_node.firstChild); + } + +- var td; ++ var td, drag_img; + this.tree.el.uniqueId(); ++ if (this.tree.draggable) { ++ td = jQuery('', { ++ 'class': 'draggable-handle' ++ }); ++ td.append(Sao.common.ICONFACTORY.get_icon_img('tryton-drag')); ++ this.el.append(td); ++ } + td = jQuery('', { + 'class': 'selection-state', + }).click(function(event_) { +@@ -971,7 +1208,11 @@ + }, + _get_column_td: function(column_index, row) { + row = row || this.el; +- return jQuery(row.children()[column_index + 1]); ++ var offset = 1; // take into account the selection column ++ if (this.tree.draggable) { ++ offset += 1; ++ } ++ return jQuery(row.children()[column_index + offset]); + }, + redraw: function(selected, expanded) { + selected = selected || []; +@@ -993,6 +1234,15 @@ + } + + ++ if (this.children_field) { ++ var depth = this.path.split('.').length; ++ var margin = 'margin-left'; ++ if (Sao.i18n.rtl) { ++ margin = 'margin-right'; ++ } ++ this.expander.css(margin, (depth - 1) + 'em'); ++ } ++ + for (var i = 0; i < this.tree.columns.length; i++) { + var column = this.tree.columns[i]; + var td = this._get_column_td(i); diff --git a/series b/series index 552014e..8b402e4 100644 --- a/series +++ b/series @@ -25,3 +25,7 @@ issue10467.diff # [stock_lot] add lot to grouping when assign try if lot it's re party_identifier_migration.diff # [Party] avoid errors on upgrades # TODO: improve_performance_on_try_assign.diff # [stock] change browse of product to get default_uom to pysql + +# Sao + +issue8240.diff # Add drag and drop support to sao