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