summaryrefslogtreecommitdiff
path: root/addons/web/static/src
diff options
context:
space:
mode:
Diffstat (limited to 'addons/web/static/src')
-rw-r--r--addons/web/static/src/js/fields/relational_fields.js53
-rw-r--r--addons/web/static/src/js/views/basic/basic_controller.js32
-rw-r--r--addons/web/static/src/js/views/basic/basic_renderer.js58
-rw-r--r--addons/web/static/src/js/views/kanban/kanban_renderer.js25
-rw-r--r--addons/web/static/src/js/views/kanban/kanban_view.js2
-rw-r--r--addons/web/static/src/js/views/list/list_controller.js28
-rw-r--r--addons/web/static/src/js/views/list/list_editable_renderer.js66
-rw-r--r--addons/web/static/src/js/views/list/list_renderer.js3
8 files changed, 156 insertions, 111 deletions
diff --git a/addons/web/static/src/js/fields/relational_fields.js b/addons/web/static/src/js/fields/relational_fields.js
index 642fa55b33e..85342269617 100644
--- a/addons/web/static/src/js/fields/relational_fields.js
+++ b/addons/web/static/src/js/fields/relational_fields.js
@@ -812,7 +812,7 @@ var FieldX2Many = AbstractField.extend({
open_record: '_onOpenRecord',
kanban_record_delete: '_onRemoveRecord',
list_record_remove: '_onRemoveRecord',
- resequence: '_onResequence',
+ resequence_records: '_onResequenceRecords',
save_line: '_onSaveLine',
toggle_column_order: '_onToggleColumnOrder',
activate_next_widget: '_onActiveNextWidget',
@@ -1043,6 +1043,7 @@ var FieldX2Many = AbstractField.extend({
};
_.extend(rendererParams, {
record_options: record_options,
+ readOnlyMode: this.isReadonly,
});
}
@@ -1351,38 +1352,52 @@ var FieldX2Many = AbstractField.extend({
* Forces a resequencing of the records.
*
* @private
- * @param {OdooEvent} event
+ * @param {OdooEvent} ev
+ * @param {string[]} ev.data.recordIds
+ * @param {integer} ev.data.offset
+ * @param {string} ev.data.handleField
*/
- _onResequence: function (event) {
- event.stopPropagation();
+ _onResequenceRecords: function (ev) {
+ ev.stopPropagation();
var self = this;
- this.trigger_up('edited_list', { id: this.value.id });
- var rowIDs = event.data.rowIDs.slice();
- var rowID = rowIDs.pop();
- var defs = _.map(rowIDs, function (rowID, index) {
+ if (this.view.arch.tag === 'tree') {
+ this.trigger_up('edited_list', { id: this.value.id });
+ }
+ var handleField = ev.data.handleField;
+ var offset = ev.data.offset;
+ var recordIds = ev.data.recordIds.slice();
+ // trigger an update of all records but the last one with option
+ // 'notifyChanges' set to false, and once all those changes have been
+ // validated by the model, trigger the change on the last record
+ // (without the option, s.t. the potential onchange on parent record
+ // is triggered)
+ var recordId = recordIds.pop();
+ var proms = recordIds.map(function (recordId, index) {
var data = {};
- data[event.data.handleField] = event.data.offset + index;
+ data[handleField] = offset + index;
return self._setValue({
operation: 'UPDATE',
- id: rowID,
+ id: recordId,
data: data,
}, {
notifyChange: false,
});
});
- Promise.all(defs).then(function () {
+ Promise.all(proms).then(function () {
function always() {
- self.trigger_up('toggle_column_order', {
- id: self.value.id,
- name: event.data.handleField,
- });
+ if (self.view.arch.tag === 'tree') {
+ self.trigger_up('toggle_column_order', {
+ id: self.value.id,
+ name: handleField,
+ });
+ }
}
-
- // trigger only once the onchange for parent record
+ var data = {};
+ data[handleField] = offset + recordIds.length;
self._setValue({
operation: 'UPDATE',
- id: rowID,
- data: _.object([event.data.handleField], [event.data.offset + rowIDs.length]),
+ id: recordId,
+ data: data,
}).then(always).guardedCatch(always);
});
},
diff --git a/addons/web/static/src/js/views/basic/basic_controller.js b/addons/web/static/src/js/views/basic/basic_controller.js
index 064f7b84a52..610e7e6dc6b 100644
--- a/addons/web/static/src/js/views/basic/basic_controller.js
+++ b/addons/web/static/src/js/views/basic/basic_controller.js
@@ -19,6 +19,7 @@ var BasicController = AbstractController.extend(FieldManagerMixin, {
custom_events: _.extend({}, AbstractController.prototype.custom_events, FieldManagerMixin.custom_events, {
discard_changes: '_onDiscardChanges',
reload: '_onReload',
+ resequence_records: '_onResequenceRecords',
set_dirty: '_onSetDirty',
sidebar_data_asked: '_onSidebarDataAsked',
translate: '_onTranslate',
@@ -642,6 +643,37 @@ var BasicController = AbstractController.extend(FieldManagerMixin, {
}
},
/**
+ * Resequence records in the given order.
+ *
+ * @private
+ * @param {OdooEvent} ev
+ * @param {string[]} ev.data.recordIds
+ * @param {integer} ev.data.offset
+ * @param {string} ev.data.handleField
+ */
+ _onResequenceRecords: function (ev) {
+ var self = this;
+
+ this.trigger_up('mutexify', {
+ action: function () {
+ var state = self.model.get(self.handle);
+ var resIDs = _.map(ev.data.recordIds, function (recordID) {
+ return _.findWhere(state.data, {id: recordID}).res_id;
+ });
+ var options = {
+ offset: ev.data.offset,
+ field: ev.data.handleField,
+ };
+ return self.model.resequence(self.modelName, resIDs, self.handle, options)
+ .then(function () {
+ self._updateEnv();
+ state = self.model.get(self.handle);
+ return self.renderer.updateState(state, {noRender: true});
+ });
+ },
+ });
+ },
+ /**
* @private
* @param {OdooEvent} ev
*/
diff --git a/addons/web/static/src/js/views/basic/basic_renderer.js b/addons/web/static/src/js/views/basic/basic_renderer.js
index 4c03d9b77a1..4ee2ef8fbb3 100644
--- a/addons/web/static/src/js/views/basic/basic_renderer.js
+++ b/addons/web/static/src/js/views/basic/basic_renderer.js
@@ -31,6 +31,9 @@ var BasicRenderer = AbstractRenderer.extend({
this.viewType = params.viewType;
this.mode = params.mode || 'readonly';
this.widgets = [];
+ // This attribute lets us know if there is a handle widget on a field,
+ // and on which field it is set.
+ this.handleField = null;
},
/**
* This method has two responsabilities: find every invalid fields in the
@@ -389,6 +392,61 @@ var BasicRenderer = AbstractRenderer.extend({
return this.state.count !== 0;
},
/**
+ * Force the resequencing of the records after moving one of them to a given
+ * index.
+ *
+ * @private
+ * @param {string} recordId datapoint id of the moved record
+ * @param {integer} toIndex new index of the moved record
+ */
+ _moveRecord: function (recordId, toIndex) {
+ var self = this;
+ var records = this.state.data;
+ var record = _.findWhere(records, {id: recordId});
+ var fromIndex = records.indexOf(record);
+ var lowerIndex = Math.min(fromIndex, toIndex);
+ var upperIndex = Math.max(fromIndex, toIndex) + 1;
+ var order = _.findWhere(this.state.orderedBy, {name: this.handleField});
+ var asc = !order || order.asc;
+ var reorderAll = false;
+ var sequence = (asc ? -1 : 1) * Infinity;
+
+ // determine if we need to reorder all records
+ _.each(records, function (record, index) {
+ if ((index < lowerIndex || index >= upperIndex) &&
+ ((asc && sequence >= record.data[self.handleField]) ||
+ (!asc && sequence <= record.data[self.handleField]))) {
+ reorderAll = true;
+ }
+ sequence = record.data[self.handleField];
+ });
+
+ if (reorderAll) {
+ records = _.without(records, record);
+ records.splice(toIndex, 0, record);
+ } else {
+ records = records.slice(lowerIndex, upperIndex);
+ records = _.without(records, record);
+ if (fromIndex > toIndex) {
+ records.unshift(record);
+ } else {
+ records.push(record);
+ }
+ }
+
+ var sequences = _.pluck(_.pluck(records, 'data'), this.handleField);
+ var recordIds = _.pluck(records, 'id');
+ if (!asc) {
+ recordIds.reverse();
+ }
+
+ this.trigger_up('resequence_records', {
+ handleField: this.handleField,
+ offset: _.min(sequences),
+ recordIds: recordIds,
+ });
+ },
+ /**
* This function is called each time a field widget is created, when it is
* ready (after its willStart and Start methods are complete). This is the
* place where work having to do with $el should be done.
diff --git a/addons/web/static/src/js/views/kanban/kanban_renderer.js b/addons/web/static/src/js/views/kanban/kanban_renderer.js
index 4c5326f755b..9e86c857085 100644
--- a/addons/web/static/src/js/views/kanban/kanban_renderer.js
+++ b/addons/web/static/src/js/views/kanban/kanban_renderer.js
@@ -121,6 +121,10 @@ var KanbanRenderer = BasicRenderer.extend({
this.columnOptions.progressBarStates = {};
}
this.quickCreateEnabled = params.quickCreateEnabled;
+ if (!params.readOnlyMode) {
+ var handleField = _.findWhere(this.state.fieldsInfo.kanban, {widget: 'handle'});
+ this.handleField = handleField && handleField.name;
+ }
this._setState(state);
},
/**
@@ -384,6 +388,27 @@ var KanbanRenderer = BasicRenderer.extend({
self.defs.push(def);
});
+ // enable record resequencing if there is a field with widget='handle'
+ // and if there is no orderBy (in this case we assume that the widget
+ // has been put on the first default order field of the model), or if
+ // the first orderBy field is the one with widget='handle'
+ var orderedBy = this.state.orderedBy;
+ var hasHandle = this.handleField &&
+ (orderedBy.length === 0 || orderedBy[0].name === this.handleField);
+ if (hasHandle) {
+ this.$el.sortable({
+ items: '.o_kanban_record:not(.o_kanban_ghost)',
+ cursor: 'move',
+ revert: 0,
+ delay: 0,
+ tolerance: 'pointer',
+ forcePlaceholderSize: true,
+ stop: function (event, ui) {
+ self._moveRecord(ui.item.data('record').db_id, ui.item.index());
+ },
+ });
+ }
+
// append ghost divs to ensure that all kanban records are left aligned
var prom = Promise.all(self.defs).then(function () {
return self._renderGhostDivs(fragment, 6);
diff --git a/addons/web/static/src/js/views/kanban/kanban_view.js b/addons/web/static/src/js/views/kanban/kanban_view.js
index 49ba9b74034..3764cf7ca22 100644
--- a/addons/web/static/src/js/views/kanban/kanban_view.js
+++ b/addons/web/static/src/js/views/kanban/kanban_view.js
@@ -77,13 +77,13 @@ var KanbanView = BasicView.extend({
read_only_mode: params.readOnlyMode,
};
this.rendererParams.quickCreateEnabled = this._isQuickCreateEnabled();
+ this.rendererParams.readOnlyMode = params.readOnlyMode;
var examples = archAttrs.examples;
if (examples) {
this.rendererParams.examples = kanbanExamplesRegistry.get(examples);
}
this.controllerParams.on_create = archAttrs.on_create;
- this.controllerParams.readOnlyMode = false;
this.controllerParams.hasButtons = true;
this.controllerParams.quickCreateEnabled = this.rendererParams.quickCreateEnabled;
diff --git a/addons/web/static/src/js/views/list/list_controller.js b/addons/web/static/src/js/views/list/list_controller.js
index 5db0323c87d..10d3c0d54c5 100644
--- a/addons/web/static/src/js/views/list/list_controller.js
+++ b/addons/web/static/src/js/views/list/list_controller.js
@@ -29,7 +29,6 @@ var ListController = BasicController.extend({
group_edit_button_clicked: '_onEditGroupClicked',
edit_line: '_onEditLine',
save_line: '_onSaveLine',
- resequence: '_onResequence',
selection_changed: '_onSelectionChanged',
toggle_column_order: '_onToggleColumnOrder',
toggle_group: '_onToggleGroup',
@@ -630,33 +629,6 @@ var ListController = BasicController.extend({
this._super.apply(this, arguments);
},
/**
- * Force a resequence of the records curently on this page.
- *
- * @private
- * @param {OdooEvent} ev
- */
- _onResequence: function (ev) {
- var self = this;
-
- this.trigger_up('mutexify', {
- action: function () {
- var state = self.model.get(self.handle);
- var resIDs = _.map(ev.data.rowIDs, function(rowID) {
- return _.findWhere(state.data, {id: rowID}).res_id;
- });
- var options = {
- offset: ev.data.offset,
- field: ev.data.handleField,
- };
- return self.model.resequence(self.modelName, resIDs, self.handle, options).then(function () {
- self._updateEnv();
- state = self.model.get(self.handle);
- return self.renderer.updateState(state, {noRender: true});
- });
- },
- });
- },
- /**
* Called when the renderer displays an editable row and the user tries to
* leave it -> Saves the record associated to that line.
*
diff --git a/addons/web/static/src/js/views/list/list_editable_renderer.js b/addons/web/static/src/js/views/list/list_editable_renderer.js
index 818739c6f48..c68f5ab573a 100644
--- a/addons/web/static/src/js/views/list/list_editable_renderer.js
+++ b/addons/web/static/src/js/views/list/list_editable_renderer.js
@@ -775,6 +775,7 @@ ListRenderer.include({
* @override
*/
_renderBody: function () {
+ var self = this;
var $body = this._super.apply(this, arguments);
if (this.hasHandle) {
$body.sortable({
@@ -782,7 +783,11 @@ ListRenderer.include({
items: '> tr.o_data_row',
helper: 'clone',
handle: '.o_row_handle',
- stop: this._resequence.bind(this),
+ stop: function (event, ui) {
+ self.unselectRow().then(function () {
+ self._moveRecord(ui.item.data('id'), ui.item.index());
+ });
+ },
});
}
return $body;
@@ -891,65 +896,6 @@ ListRenderer.include({
});
},
/**
- * Force the resequencing of the items in the list.
- *
- * @private
- * @param {jQuery.Event} event
- * @param {Object} ui jqueryui sortable widget
- */
- _resequence: function (event, ui) {
- var self = this;
- var movedRecordID = ui.item.data('id');
- var rows = this.state.data;
- var row = self._getRecord(movedRecordID);
- var index0 = rows.indexOf(row);
- var index1 = ui.item.index();
- var lower = Math.min(index0, index1);
- var upper = Math.max(index0, index1) + 1;
-
- var order = _.findWhere(self.state.orderedBy, {name: self.handleField});
- var asc = !order || order.asc;
- var reorderAll = false;
- var sequence = (asc ? -1 : 1) * Infinity;
-
- // determine if we need to reorder all lines
- _.each(rows, function (row, index) {
- if ((index < lower || index >= upper) &&
- ((asc && sequence >= row.data[self.handleField]) ||
- (!asc && sequence <= row.data[self.handleField]))) {
- reorderAll = true;
- }
- sequence = row.data[self.handleField];
- });
-
- if (reorderAll) {
- rows = _.without(rows, row);
- rows.splice(index1, 0, row);
- } else {
- rows = rows.slice(lower, upper);
- rows = _.without(rows, row);
- if (index0 > index1) {
- rows.unshift(row);
- } else {
- rows.push(row);
- }
- }
-
- var sequences = _.pluck(_.pluck(rows, 'data'), self.handleField);
- var rowIDs = _.pluck(rows, 'id');
-
- if (!asc) {
- rowIDs.reverse();
- }
- this.unselectRow().then(function () {
- self.trigger_up('resequence', {
- rowIDs: rowIDs,
- offset: _.min(sequences),
- handleField: self.handleField,
- });
- });
- },
- /**
* This is one of the trickiest method in the editable renderer. It has to
* do a lot of stuff: it has to determine which cell should be selected (if
* the target cell is readonly, we need to find another suitable cell), then
diff --git a/addons/web/static/src/js/views/list/list_renderer.js b/addons/web/static/src/js/views/list/list_renderer.js
index 471598ca4e4..4d8be05d252 100644
--- a/addons/web/static/src/js/views/list/list_renderer.js
+++ b/addons/web/static/src/js/views/list/list_renderer.js
@@ -51,9 +51,6 @@ var ListRenderer = BasicRenderer.extend({
*/
init: function (parent, state, params) {
this._super.apply(this, arguments);
- // This attribute lets us know if there is a handle widget on a field,
- // and on which field it is set.
- this.handleField = null;
this.rowDecorations = _.chain(this.arch.attrs)
.pick(function (value, key) {
return DECORATIONS.indexOf(key) >= 0;