[FIX] web: Call 'toggle_active' instead of 'write'

Purpose
=======

Currently when clicking on 'Archive' or 'Unarchive' on the list view,
we write directly on the record {'active': False}, instead of calling
toggle_active. Missing that way implementation of numerous business cases.

Example
- Archive a record --> archive its next activities
- (Un)Archive a user --> (Un)Archive its partner
- (Un)Archive a product variant --> (Un)Archive its template if
  there is only one variant
- Archive an employee --> Open a wizard to choose an exit plan + describe
  the departure reason
- Archive a route --> Archive its procurement rules
- ...

Specification
=============

- Call toggle_active instead of write when calling toggleActive in the web
  client.
- On the other hand, use actionArchive and actionUnarchive to apply the
  toggle action on the related records only.
This commit is contained in:
Yannick Tivisse
2019-07-12 13:01:36 +02:00
parent ba03089230
commit f896afcc8a
8 changed files with 154 additions and 13 deletions
@@ -1176,7 +1176,7 @@ var BasicModel = AbstractModel.extend({
* @param {string} parentID id of the parent resource to reload
* @returns {Promise<string>} resolves to the parent id
*/
toggleActive: function (recordIDs, value, parentID) {
toggleActive: function (recordIDs, parentID) {
var self = this;
var parent = this.localData[parentID];
var resIDs = _.map(recordIDs, function (recordID) {
@@ -1184,8 +1184,56 @@ var BasicModel = AbstractModel.extend({
});
return this._rpc({
model: parent.model,
method: 'write',
args: [resIDs, { active: value }],
method: 'toggle_active',
args: [resIDs],
})
.then(function () {
// optionally clear the DataManager's cache
self._invalidateCache(parent);
return self.reload(parentID);
});
},
/**
* Archive the given records
*
* @param {Array} recordIDs local ids of the records to (un)archive
* @param {string} parentID id of the parent resource to reload
* @returns {Promise<string>} resolves to the parent id
*/
actionArchive: function (recordIDs, parentID) {
var self = this;
var parent = this.localData[parentID];
var resIDs = _.map(recordIDs, function (recordID) {
return self.localData[recordID].res_id;
});
return this._rpc({
model: parent.model,
method: 'action_archive',
args: [resIDs],
})
.then(function () {
// optionally clear the DataManager's cache
self._invalidateCache(parent);
return self.reload(parentID);
});
},
/**
* Unarchive the given records
*
* @param {Array} recordIDs local ids of the records to (un)archive
* @param {string} parentID id of the parent resource to reload
* @returns {Promise<string>} resolves to the parent id
*/
actionUnarchive: function (recordIDs, parentID) {
var self = this;
var parent = this.localData[parentID];
var resIDs = _.map(recordIDs, function (recordID) {
return self.localData[recordID].res_id;
});
return this._rpc({
model: parent.model,
method: 'action_unarchive',
args: [resIDs],
})
.then(function () {
// optionally clear the DataManager's cache
@@ -293,9 +293,15 @@ var FormController = BasicController.extend({
if (ids.length === 0) {
return Promise.resolve();
}
return this.model
.toggleActive(ids, !archive, this.handle)
.then(this.update.bind(this, {}, {reload: false}));
if (archive) {
return this.model
.actionArchive(ids, this.handle)
.then(this.update.bind(this, {}, {reload: false}));
} else {
return this.model
.actionUnarchive(ids, this.handle)
.then(this.update.bind(this, {}, {reload: false}));
}
},
/**
@@ -569,12 +569,11 @@ var KanbanController = BasicController.extend({
*/
_onToggleActiveRecords: function (ev) {
var self = this;
var active = !ev.data.archive;
var column = ev.target;
var recordIds = _.pluck(column.records, 'db_id');
if (recordIds.length) {
this.model
.toggleActive(recordIds, active, column.db_id)
.toggleActive(recordIds, column.db_id)
.then(function (dbID) {
var data = self.model.get(dbID);
self.renderer.updateColumn(dbID, data);
@@ -265,9 +265,15 @@ var ListController = BasicController.extend({
if (ids.length === 0) {
return Promise.resolve();
}
return this.model
.toggleActive(ids, !archive, this.handle)
.then(this.update.bind(this, {}, {reload: false}));
if (archive) {
return this.model
.actionArchive(ids, this.handle)
.then(this.update.bind(this, {}, {reload: false}));
} else {
return this.model
.actionUnarchive(ids, this.handle)
.then(this.update.bind(this, {}, {reload: false}));
}
},
/**
* Assign on the buttons create additionnal behavior to facilitate the work of the users doing input only using the keyboard
@@ -135,6 +135,13 @@ QUnit.module('Views', {
var model = createModel({
Model: KanbanModel,
data: this.data,
mockRPC: function (route, args) {
if (route === '/web/dataset/call_kw/partner/action_archive') {
this.data.partner.records[0].active = false;
return Promise.resolve();
}
return this._super.apply(this, arguments);
},
});
var params = _.extend(this.params, {
@@ -151,7 +158,7 @@ QUnit.module('Views', {
// archive the column 'xphone'
var recordIDs = _.pluck(xphoneGroup.data, 'id');
await model.toggleActive(recordIDs, false, xphoneGroup.id);
await model.actionArchive(recordIDs, xphoneGroup.id);
state = model.get(resultID);
xphoneGroup = _.findWhere(state.data, {res_id: 37});
assert.strictEqual(xphoneGroup.count, 0, 'xphone group has no record anymore');
@@ -176,6 +176,20 @@ QUnit.module('Views', {
'<div><field name="foo"/></div>' +
'</t></templates></kanban>',
groupBy: ['bar'],
mockRPC: function (route, args) {
if (route === '/web/dataset/call_kw/partner/toggle_active') {
var partnerIDS = args.args[0];
var records = this.data.partner.records
_.each(partnerIDS, function(partnerID) {
_.find(records, function (record) {
return record.id === partnerID;
}).active = false;
})
this.data.partner.records[0].active;
return Promise.resolve();
}
return this._super.apply(this, arguments);
},
});
// check archive/restore all actions in kanban header's config dropdown
@@ -219,6 +233,20 @@ QUnit.module('Views', {
'<div><field name="foo"/></div>' +
'</t></templates></kanban>',
groupBy: ['bar'],
mockRPC: function (route, args) {
if (route === '/web/dataset/call_kw/partner/toggle_active') {
var partnerIDS = args.args[0];
var records = this.data.partner.records
_.each(partnerIDS, function(partnerID) {
_.find(records, function (record) {
return record.id === partnerID;
}).active = false;
})
this.data.partner.records[0].active;
return Promise.resolve();
}
return this._super.apply(this, arguments);
},
});
// check archive/restore all actions in kanban header's config dropdown
@@ -4747,6 +4775,20 @@ QUnit.module('Views', {
'</t></templates>' +
'</kanban>',
groupBy: ['bar'],
mockRPC: function (route, args) {
if (route === '/web/dataset/call_kw/partner/toggle_active') {
var partnerIDS = args.args[0];
var records = this.data.partner.records
_.each(partnerIDS, function(partnerID) {
_.find(records, function (record) {
return record.id === partnerID;
}).active = false;
})
this.data.partner.records[0].active;
return Promise.resolve();
}
return this._super.apply(this, arguments);
},
});
assert.strictEqual(kanban.$('.o_kanban_group:eq(1) .o_kanban_counter_side').text(), "36",
@@ -4790,6 +4832,20 @@ QUnit.module('Views', {
'</t></templates>' +
'</kanban>',
groupBy: ['bar'],
mockRPC: function (route, args) {
if (route === '/web/dataset/call_kw/partner/toggle_active') {
var partnerIDS = args.args[0];
var records = this.data.partner.records
_.each(partnerIDS, function(partnerID) {
_.find(records, function (record) {
return record.id === partnerID;
}).active = false;
})
this.data.partner.records[0].active;
return Promise.resolve();
}
return this._super.apply(this, arguments);
},
});
assert.deepEqual(kanban.exportState().resIds, [1, 2, 3, 4]);
+5 -1
View File
@@ -1473,6 +1473,10 @@ QUnit.module('Views', {
arch: '<tree><field name="foo"/></tree>',
mockRPC: function (route) {
assert.step(route);
if (route === '/web/dataset/call_kw/foo/action_archive') {
this.data.foo.records[0].active = false;
return Promise.resolve();
}
return this._super.apply(this, arguments);
},
});
@@ -1496,7 +1500,7 @@ QUnit.module('Views', {
assert.strictEqual($('.modal').length, 1, 'a confirm modal should be displayed');
await testUtils.dom.click($('.modal-footer .btn-primary'));
assert.containsN(list, 'tbody td.o_list_record_selector', 3, "should have 3 records");
assert.verifySteps(['/web/dataset/call_kw/foo/write', '/web/dataset/search_read']);
assert.verifySteps(['/web/dataset/call_kw/foo/action_archive', '/web/dataset/search_read']);
list.destroy();
});
+15
View File
@@ -4671,6 +4671,21 @@ Fields:
for record in self:
record.active = not record.active
def action_archive(self):
"""
Set active=False on a recordset, by calling toggle_active to take the
corresponding actions according to the model
"""
return self.filtered(lambda record: record.active).toggle_active()
@api.multi
def action_unarchive(self):
"""
Set active=True on a recordset, by calling toggle_active to take the
corresponding actions according to the model
"""
return self.filtered(lambda record: not record.active).toggle_active()
def _register_hook(self):
""" stuff to do right after the registry is built """
pass