summaryrefslogtreecommitdiff
path: root/addons/web/static/src/js
diff options
context:
space:
mode:
authorChristophe Simonis <chs@odoo.com>2017-08-10 18:07:47 +0200
committerChristophe Simonis <chs@odoo.com>2017-08-10 18:07:47 +0200
commit1fa8680286262cea52d26ff8616b72d2862dab07 (patch)
tree54ef67a0d819120fb5dcbbaa98ad91232cbc2116 /addons/web/static/src/js
parent39ae81141777071329623dbc3ca89172b960f295 (diff)
parent48b2ce60ed12b9b0f3aaa0322465504798bd154e (diff)
[MERGE] forward port branch saas-17 up to 48b2ce60ed1
Diffstat (limited to 'addons/web/static/src/js')
-rw-r--r--addons/web/static/src/js/chrome/view_manager.js33
-rw-r--r--addons/web/static/src/js/fields/relational_fields.js14
-rw-r--r--addons/web/static/src/js/views/abstract_view.js9
-rw-r--r--addons/web/static/src/js/views/basic/basic_controller.js12
-rw-r--r--addons/web/static/src/js/views/basic/basic_model.js35
-rw-r--r--addons/web/static/src/js/views/calendar/calendar_renderer.js7
-rw-r--r--addons/web/static/src/js/views/form/form_controller.js20
-rw-r--r--addons/web/static/src/js/views/kanban/kanban_controller.js8
-rw-r--r--addons/web/static/src/js/views/kanban/kanban_model.js2
-rw-r--r--addons/web/static/src/js/views/list/list_renderer.js20
10 files changed, 103 insertions, 57 deletions
diff --git a/addons/web/static/src/js/chrome/view_manager.js b/addons/web/static/src/js/chrome/view_manager.js
index a4a1d65f7b6..5962bb28acb 100644
--- a/addons/web/static/src/js/chrome/view_manager.js
+++ b/addons/web/static/src/js/chrome/view_manager.js
@@ -20,7 +20,7 @@ var ViewManager = Widget.extend(ControlPanelMixin, {
custom_events: {
execute_action: function(event) {
var data = event.data;
- this.do_execute_action(data.action_data, data.model, data.res_ids, data.on_closed)
+ this.do_execute_action(data.action_data, data.env, data.on_closed)
.then(data.on_success, data.on_fail);
},
search: function(event) {
@@ -554,16 +554,19 @@ var ViewManager = Widget.extend(ControlPanelMixin, {
* @param {string} [action_data.effect] if given, a visual effect (a
* rainbowman by default) will be displayed when the action is complete,
* with the string (evaluated) given as options.
- * @param {DataSet} dataset a dataset object used to communicate with the server
- * @param {integer[]} [res_ids] the res_ids of the objects on which the action is to be applied (fallback on env.ids if not set)
+ * @param {Object} env
+ * @param {string} env.model the model of the record(s) triggering the action
+ * @param {integer[]} [env.resIDs] the current ids in the environment where the action is triggered
+ * @param {integer} [env.currentID] the id of the record triggering the action
+ * @param {Object} [env.context] a context to pass to the action
* @param {Function} on_closed callback to execute when dialog is closed or when the action does not generate any result (no new action)
*/
- do_execute_action: function (action_data, model, res_ids, on_closed) {
+ do_execute_action: function (action_data, env, on_closed) {
var self = this;
var result_handler = on_closed || function () {};
- var context = new Context(this.env.context, action_data.context || {});
+ var context = new Context(env.context, action_data.context || {});
// OR NULL hereunder: pyeval waits specifically for a null value, different from undefined
- var record_id = res_ids && res_ids[0] || null;
+ var recordID = env.currentID || null;
// response handler
var handler = function (action) {
@@ -585,11 +588,11 @@ var ViewManager = Widget.extend(ControlPanelMixin, {
}))
);
ncontext.add(action_data.context || {});
- ncontext.add({active_model: self.env.modelName});
- if (res_ids) {
+ ncontext.add({active_model: env.model});
+ if (recordID) {
ncontext.add({
- active_id: record_id,
- active_ids: res_ids,
+ active_id: recordID,
+ active_ids: [recordID],
});
}
ncontext.add(action.context || {});
@@ -611,7 +614,7 @@ var ViewManager = Widget.extend(ControlPanelMixin, {
if (action_data.special) {
return handler({"type":"ir.actions.act_window_close"});
} else if (action_data.type === "object") {
- var args = res_ids ? [res_ids] : [this.env.ids];
+ var args = recordID ? [[recordID]] : [env.resIDs];
if (action_data.args) {
try {
// Warning: quotes and double quotes problem due to json and xml clash
@@ -623,13 +626,13 @@ var ViewManager = Widget.extend(ControlPanelMixin, {
}
}
args.push(context);
- var dataset = new data.DataSet(this, model, this.env.context);
+ var dataset = new data.DataSet(this, env.model, env.context);
return dataset.call_button(action_data.name, args).then(handler);
} else if (action_data.type === "action") {
return data_manager.load_action(action_data.name, _.extend(pyeval.eval('context', context), {
- active_model: this.env.modelName,
- active_ids: this.env.ids,
- active_id: record_id,
+ active_model: env.model,
+ active_ids: env.resIDs,
+ active_id: recordID,
})).then(handler);
}
},
diff --git a/addons/web/static/src/js/fields/relational_fields.js b/addons/web/static/src/js/fields/relational_fields.js
index 64049d8b1ec..0dce6ec2bb4 100644
--- a/addons/web/static/src/js/fields/relational_fields.js
+++ b/addons/web/static/src/js/fields/relational_fields.js
@@ -219,10 +219,16 @@ var FieldMany2One = AbstractField.extend({
/**
* @private
* @param {string} name
+ * @returns {Deferred} resolved after the name_create or when the slowcreate
+ * modal is closed.
*/
_quickCreate: function (name) {
var self = this;
- var slowCreate = this._searchCreatePopup.bind(this, "form", false, this._createContext(name));
+ var def = $.Deferred();
+ var slowCreate = function () {
+ var dialog = self._searchCreatePopup.bind(self, "form", false, self._createContext(name));
+ dialog.on('closed', self, def.resolve.bind(def));
+ };
if (this.nodeOptions.quick_create) {
this.trigger_up('mutexify', {
action: function () {
@@ -235,12 +241,16 @@ var FieldMany2One = AbstractField.extend({
if (self.mode === "edit") {
self.reinitialize({id: result[0], display_name: result[1]});
}
- }, slowCreate);
+ def.resolve();
+ }).fail(function () {
+ slowCreate();
+ });
},
});
} else {
slowCreate();
}
+ return def;
},
/**
* @private
diff --git a/addons/web/static/src/js/views/abstract_view.js b/addons/web/static/src/js/views/abstract_view.js
index 8de118b29b7..644ba8ac985 100644
--- a/addons/web/static/src/js/views/abstract_view.js
+++ b/addons/web/static/src/js/views/abstract_view.js
@@ -29,6 +29,7 @@ var ajax = require('web.ajax');
var AbstractModel = require('web.AbstractModel');
var AbstractRenderer = require('web.AbstractRenderer');
var AbstractController = require('web.AbstractController');
+var Context = require('web.Context');
var AbstractView = Class.extend({
// name displayed in view switchers
@@ -152,9 +153,9 @@ var AbstractView = Class.extend({
});
},
/**
- * Returns the view model or create an instance of it if there is noney
+ * Returns the view model or create an instance of it if none
*
- * @param {Widget} parent the parent of the model
+ * @param {Widget} parent the parent of the model, if it has to be created
* @return {Object} instance of the view model
*/
getModel: function (parent) {
@@ -167,7 +168,7 @@ var AbstractView = Class.extend({
/**
* Returns the a new view renderer instance
*
- * @param {Widget} parent the parent of the model
+ * @param {Widget} parent the parent of the model, if it has to be created
* @param {Object} state the information related to the rendered view
* @return {Object} instance of the view renderer
*/
@@ -239,7 +240,7 @@ var AbstractView = Class.extend({
* Loads the subviews for x2many fields when they are not inline
*
* @private
- * @param {Widget} parent the parent of the model
+ * @param {Widget} parent the parent of the model, if it has to be created
* @returns {Deferred}
*/
_loadSubviews: function (parent) {
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 3a47f65ab03..6ba9dbea477 100644
--- a/addons/web/static/src/js/views/basic/basic_controller.js
+++ b/addons/web/static/src/js/views/basic/basic_controller.js
@@ -232,13 +232,17 @@ var BasicController = AbstractController.extend(FieldManagerMixin, {
return self.isDestroyed() ? $.when() : self.reload();
};
record = record || this.model.get(this.handle);
- var recordID = record.data.id;
+
this.trigger_up('execute_action', {
action_data: _.extend({}, attrs, {
- context: record.getContext({additionalContext: attrs.context || {}}),
+ context: record.getContext({additionalContext: attrs.context || {}}),
}),
- model: record.model,
- res_ids: [recordID],
+ env: {
+ context: record.getContext(),
+ currentID: record.data.id,
+ model: record.model,
+ resIDs: record.res_ids,
+ },
on_closed: function (reason) {
if (!_.isObject(reason)) {
reload(reason);
diff --git a/addons/web/static/src/js/views/basic/basic_model.js b/addons/web/static/src/js/views/basic/basic_model.js
index 04fe384afdc..8755805f993 100644
--- a/addons/web/static/src/js/views/basic/basic_model.js
+++ b/addons/web/static/src/js/views/basic/basic_model.js
@@ -649,7 +649,7 @@ var BasicModel = AbstractModel.extend({
var element = this.localData[id];
if (element.type === 'record') {
- if ('currentId' in options && !options.currentId) {
+ if (!options.currentId && (('currentId' in options) || this.isNew(id))) {
var params = {
context: element.context,
fieldsInfo: element.fieldsInfo,
@@ -2310,12 +2310,20 @@ var BasicModel = AbstractModel.extend({
});
list = this._applyX2ManyOperations(list);
if (type === 'many2many' || list._forceM2MLink) {
+ var relRecordCreated = _.filter(relRecordAdded, function (rec) {
+ return typeof rec.res_id === 'string';
+ });
+ var realIDs = _.difference(list.res_ids, _.pluck(relRecordCreated, 'res_id'));
// deliberately generate a single 'replace' command instead
// of a 'delete' and a 'link' commands with the exact diff
// because 1) performance-wise it doesn't change anything
// and 2) to guard against concurrent updates (policy: force
// a complete override of the actual value of the m2m)
- commands[fieldName].push(x2ManyCommands.replace_with(list.res_ids));
+ commands[fieldName].push(x2ManyCommands.replace_with(realIDs));
+ _.each(relRecordCreated, function (relRecord) {
+ var changes = self._generateChanges(relRecord, options);
+ commands[fieldName].push(x2ManyCommands.create(changes));
+ });
// generate update commands for records that have been
// updated (it may happen with editable lists)
_.each(relRecordUpdated, function (relRecord) {
@@ -2435,6 +2443,12 @@ var BasicModel = AbstractModel.extend({
*/
_getDomain: function (element, options) {
if (options && options.fieldName) {
+ if (element._domains[options.fieldName]) {
+ return Domain.prototype.stringToArray(
+ element._domains[options.fieldName],
+ this._getEvalContext(element, true)
+ );
+ }
var viewType = options.viewType || element.viewType;
var fieldInfo = element.fieldsInfo[viewType][options.fieldName];
if (fieldInfo && fieldInfo.domain) {
@@ -2697,6 +2711,7 @@ var BasicModel = AbstractModel.extend({
var dataPoint = {
_cache: type === 'list' ? {} : undefined,
_changes: null,
+ _domains: {},
aggregateValues: params.aggregateValues || {},
context: params.context || {},
count: params.count || res_ids.length,
@@ -2870,6 +2885,9 @@ var BasicModel = AbstractModel.extend({
r._changes = _.defaults(value[2], r.data);
for (var name in r._changes) {
+ if (r._changes[name] === null) {
+ continue;
+ }
var isFieldInView = name in r.fields;
if (isFieldInView && r.fields[name].type === 'many2one') {
var rec = self._makeDataPoint({
@@ -3021,12 +3039,7 @@ var BasicModel = AbstractModel.extend({
record._warning = true;
}
if (result.domain) {
- var fieldsInfo = record.fieldsInfo[viewType || record.viewType];
- for (var fieldName in result.domain) {
- if (fieldsInfo[fieldName]) {
- fieldsInfo[fieldName].domain = result.domain[fieldName];
- }
- }
+ record._domains = _.extend(record._domains, result.domain);
}
return self._applyOnChange(result.value, record).then(function () {
return result;
@@ -3083,7 +3096,9 @@ var BasicModel = AbstractModel.extend({
*/
_readGroup: function (list) {
var self = this;
- var fields = _.uniq(list.getFieldNames().concat(list.groupedBy));
+ var groupByField = list.groupedBy[0];
+ var rawGroupBy = groupByField.split(':')[0];
+ var fields = _.uniq(list.getFieldNames().concat(rawGroupBy));
return this._rpc({
model: list.model,
method: 'read_group',
@@ -3094,8 +3109,6 @@ var BasicModel = AbstractModel.extend({
lazy: true,
})
.then(function (groups) {
- var groupByField = list.groupedBy[0];
- var rawGroupBy = groupByField.split(':')[0];
var previousGroups = _.map(list.data, function (groupID) {
return self.localData[groupID];
});
diff --git a/addons/web/static/src/js/views/calendar/calendar_renderer.js b/addons/web/static/src/js/views/calendar/calendar_renderer.js
index 4f5719063ff..5fb79cfa3a0 100644
--- a/addons/web/static/src/js/views/calendar/calendar_renderer.js
+++ b/addons/web/static/src/js/views/calendar/calendar_renderer.js
@@ -322,10 +322,11 @@ return AbstractRenderer.extend({
element.addClass($render.attr('class'));
var display_hour = '';
if (!event.allDay) {
- display_hour = (event.start.format('HH:mm') === '00:00' ? event.r_start.format('HH:mm') : event.start.format('HH:mm')) + ' - ' +
- (event.end && event.end.format('HH:mm') !== '00:00' ? event.end.format('HH:mm') : event.r_end.format('HH:mm'));
+ var start = event.r_start || event.start;
+ var end = event.r_end || event.end;
+ display_hour = start.format('HH:mm') + ' - ' + end.format('HH:mm');
if (display_hour === '00:00 - 00:00') {
- display_hour = _t('All the day');
+ display_hour = _t('All day');
}
}
element.find('.fc-content .fc-time').text(display_hour);
diff --git a/addons/web/static/src/js/views/form/form_controller.js b/addons/web/static/src/js/views/form/form_controller.js
index cdfe2cb8d5d..0718b4e202e 100644
--- a/addons/web/static/src/js/views/form/form_controller.js
+++ b/addons/web/static/src/js/views/form/form_controller.js
@@ -217,7 +217,7 @@ var FormController = BasicController.extend({
*
* @private
* @override method from field manager mixin
- * @param {string} id
+ * @param {string} id - id of the previously changed record
* @returns {Deferred}
*/
_confirmSave: function (id) {
@@ -228,15 +228,21 @@ var FormController = BasicController.extend({
return this._setMode('readonly');
}
} else {
- // a subrecord changed, so update the corresponding relational field
+ // A subrecord has changed, so update the corresponding relational field
// i.e. the one whose value is a record with the given id or a list
// having a record with the given id in its data
var record = this.model.get(this.handle);
- var fieldsChanged = _.findKey(record.data, function (d) {
- return _.isObject(d) &&
- (d.id === id || _.findWhere(d.data, {id: id}));
- });
- return this.renderer.confirmChange(record, record.id, [fieldsChanged]);
+
+ // Callback function which returns true
+ // if a value recursively contains a record with the given id.
+ // This will be used to determine the list of fields to reload.
+ var containsChangedRecord = function (value) {
+ return _.isObject(value) &&
+ (value.id === id || _.find(value.data, containsChangedRecord));
+ };
+
+ var changedFields = _.findKey(record.data, containsChangedRecord);
+ return this.renderer.confirmChange(record, record.id, [changedFields]);
}
},
/**
diff --git a/addons/web/static/src/js/views/kanban/kanban_controller.js b/addons/web/static/src/js/views/kanban/kanban_controller.js
index 2cf6a94285e..6870dbbbe24 100644
--- a/addons/web/static/src/js/views/kanban/kanban_controller.js
+++ b/addons/web/static/src/js/views/kanban/kanban_controller.js
@@ -237,8 +237,12 @@ var KanbanController = BasicController.extend({
}
this.trigger_up('execute_action', {
action_data: attrs,
- model: record.model,
- res_ids: [record.res_id],
+ env: {
+ context: record.getContext(),
+ currentID: record.res_id,
+ model: record.model,
+ resIDs: record.res_ids,
+ },
on_closed: function () {
self.model.reload(record.id).then(function (db_id) {
var data = self.model.get(db_id);
diff --git a/addons/web/static/src/js/views/kanban/kanban_model.js b/addons/web/static/src/js/views/kanban/kanban_model.js
index 3977ac0df87..71402fc92ec 100644
--- a/addons/web/static/src/js/views/kanban/kanban_model.js
+++ b/addons/web/static/src/js/views/kanban/kanban_model.js
@@ -240,7 +240,7 @@ var KanbanModel = BasicModel.extend({
*/
_readTooltipFields: function (list) {
var self = this;
- var groupedByField = list.fields[list.groupedBy[0]];
+ var groupedByField = list.fields[list.groupedBy[0].split(':')[0]];
if (groupedByField.type !== 'many2one') {
return $.when();
}
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 4851870c8f8..3ef48792f06 100644
--- a/addons/web/static/src/js/views/list/list_renderer.js
+++ b/addons/web/static/src/js/views/list/list_renderer.js
@@ -282,15 +282,19 @@ var ListRenderer = BasicRenderer.extend({
this._registerModifiers(node, record, $button);
- // TODO this should be moved to a handler
- var self = this;
- $button.on("click", function (e) {
- e.stopPropagation();
- self.trigger_up('button_clicked', {
- attrs: node.attrs,
- record: record,
+ if (record.res_id) {
+ // TODO this should be moved to a handler
+ var self = this;
+ $button.on("click", function (e) {
+ e.stopPropagation();
+ self.trigger_up('button_clicked', {
+ attrs: node.attrs,
+ record: record,
+ });
});
- });
+ } else {
+ $button.prop('disabled', true);
+ }
return $button;
},