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/abstract_field.js9
-rw-r--r--addons/web/static/src/js/fields/basic_fields.js7
-rw-r--r--addons/web/static/src/js/services/data_manager.js14
-rw-r--r--addons/web/static/src/js/views/basic/basic_model.js56
-rw-r--r--addons/web/static/src/js/views/calendar/calendar_controller.js2
-rw-r--r--addons/web/static/src/js/views/calendar/calendar_model.js14
-rw-r--r--addons/web/static/src/js/views/form/form_controller.js27
-rw-r--r--addons/web/static/src/js/views/list/list_renderer.js5
8 files changed, 93 insertions, 41 deletions
diff --git a/addons/web/static/src/js/fields/abstract_field.js b/addons/web/static/src/js/fields/abstract_field.js
index fc29ba77466..2534f8ae90a 100644
--- a/addons/web/static/src/js/fields/abstract_field.js
+++ b/addons/web/static/src/js/fields/abstract_field.js
@@ -43,6 +43,15 @@ var AbstractField = Widget.extend({
custom_events: {
navigation_move: '_onNavigationMove',
},
+
+ /**
+ * An object representing fields to be fetched by the model eventhough not present in the view
+ * This object contains "field name" as key and an object as value.
+ * That value object must contain the key "type"
+ * see FieldBinaryImage for an example.
+ */
+ fieldDependencies: {},
+
/**
* If this flag is set to true, the field widget will be reset on every
* change which is made in the view (if the view supports it). This is
diff --git a/addons/web/static/src/js/fields/basic_fields.js b/addons/web/static/src/js/fields/basic_fields.js
index 549b2e256be..817c76323d5 100644
--- a/addons/web/static/src/js/fields/basic_fields.js
+++ b/addons/web/static/src/js/fields/basic_fields.js
@@ -20,6 +20,7 @@ var framework = require('web.framework');
var session = require('web.session');
var utils = require('web.utils');
var view_dialogs = require('web.view_dialogs');
+var field_utils = require('web.field_utils');
var qweb = core.qweb;
var _t = core._t;
@@ -1169,6 +1170,10 @@ var AbstractFieldBinary = AbstractField.extend({
});
var FieldBinaryImage = AbstractFieldBinary.extend({
+ fieldDependencies: _.extend({}, AbstractFieldBinary.prototype.fieldDependencies, {
+ __last_update: {type: 'datetime'},
+ }),
+
template: 'FieldBinaryImage',
placeholder: "/web/static/src/img/placeholder.png",
events: _.extend({}, AbstractFieldBinary.prototype.events, {
@@ -1191,7 +1196,7 @@ var FieldBinaryImage = AbstractFieldBinary.extend({
id: JSON.stringify(this.res_id),
field: this.nodeOptions.preview_image || this.name,
// unique forces a reload of the image when the record has been updated
- unique: (this.recordData.__last_update || '').replace(/[^0-9]/g, ''),
+ unique: field_utils.format.datetime(this.recordData.__last_update).replace(/[^0-9]/g, ''),
});
}
}
diff --git a/addons/web/static/src/js/services/data_manager.js b/addons/web/static/src/js/services/data_manager.js
index 911e1c5582f..32d526e160d 100644
--- a/addons/web/static/src/js/services/data_manager.js
+++ b/addons/web/static/src/js/services/data_manager.js
@@ -388,6 +388,11 @@ return core.Class.extend({
}
}
}
+
+ if (attrs.Widget.prototype.fieldDependencies) {
+ attrs.fieldDependencies = attrs.Widget.prototype.fieldDependencies;
+ }
+
return attrs;
},
/**
@@ -414,6 +419,15 @@ return core.Class.extend({
if (node.tag === 'field') {
fieldsInfo[node.attrs.name] = self._processField(viewType,
fields[node.attrs.name], node.attrs ? _.clone(node.attrs) : {});
+
+ if (fieldsInfo[node.attrs.name].fieldDependencies) {
+ var deps = fieldsInfo[node.attrs.name].fieldDependencies;
+ for (var dependency_name in deps) {
+ if (!(dependency_name in fieldsInfo)) {
+ fieldsInfo[dependency_name] = {'name': dependency_name, 'type': deps[dependency_name].type};
+ }
+ }
+ }
return false;
}
return node.tag !== 'arch';
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 9f8ea3227f5..4412d29c455 100644
--- a/addons/web/static/src/js/views/basic/basic_model.js
+++ b/addons/web/static/src/js/views/basic/basic_model.js
@@ -1109,6 +1109,10 @@ var BasicModel = AbstractModel.extend({
if (!options.doNotSetDirty) {
record._isDirty = true;
}
+ var initialData = {};
+ this._visitChildren(record, function (elem) {
+ initialData[elem.id] = $.extend(true, {}, _.pick(elem, 'data', '_changes'));
+ });
// apply changes to local data
for (var fieldName in changes) {
@@ -1137,12 +1141,18 @@ var BasicModel = AbstractModel.extend({
}
}
}
- var onchangeDef;
+ var onchangeDef = $.Deferred();
if (onChangeFields.length) {
- onchangeDef = self._performOnChange(record, onChangeFields, options.viewType).then(function (result) {
- delete record._warning;
- return _.keys(changes).concat(Object.keys(result && result.value || {}));
- });
+ self._performOnChange(record, onChangeFields, options.viewType)
+ .then(function (result) {
+ delete record._warning;
+ onchangeDef.resolve(_.keys(changes).concat(Object.keys(result && result.value || {})));
+ }).fail(function () {
+ self._visitChildren(record, function (elem) {
+ _.extend(elem, initialData[elem.id]);
+ });
+ onchangeDef.resolve({});
+ });
} else {
onchangeDef = $.Deferred().resolve(_.keys(changes));
}
@@ -2653,13 +2663,21 @@ var BasicModel = AbstractModel.extend({
}
commands[fieldName].push(command);
} else if (_.contains(addedIds, list.res_ids[i])) {
- // this is a new id
+ // this is a new id (maybe existing in DB, but new in JS)
relRecord = _.findWhere(relRecordAdded, {res_id: list.res_ids[i]});
changes = this._generateChanges(relRecord, options);
if ('id' in changes) {
+ // the subrecord already exists in db
delete changes.id;
- commands[fieldName].push(x2ManyCommands.update(relRecord.res_id, changes));
+ if (this.isNew(record.id)) {
+ // if the main record is new, link the subrecord to it
+ commands[fieldName].push(x2ManyCommands.link_to(relRecord.res_id));
+ }
+ if (!_.isEmpty(changes)) {
+ commands[fieldName].push(x2ManyCommands.update(relRecord.res_id, changes));
+ }
} else {
+ // the subrecord is new, so create it
commands[fieldName].push(x2ManyCommands.create(relRecord.ref, changes));
}
}
@@ -2892,7 +2910,8 @@ var BasicModel = AbstractModel.extend({
* @returns {boolean}
*/
_isFieldProtected: function (record, fieldName, viewType) {
- var fieldInfo = record.fieldsInfo[viewType || record.viewType][fieldName];
+ var fieldInfo = record.fieldsInfo &&
+ (record.fieldsInfo[viewType || record.viewType][fieldName]);
if (fieldInfo) {
var rawModifiers = fieldInfo.modifiers || {};
var modifiers = this._evalModifiers(record, rawModifiers);
@@ -3249,15 +3268,18 @@ var BasicModel = AbstractModel.extend({
});
return $.when.apply($, defs)
.then(function () {
- return self._performOnChange(record, fields_key).then(function () {
+ var def = $.Deferred();
+ self._performOnChange(record, fields_key).always(function () {
if (record._warning) {
if (params.allowWarning) {
delete record._warning;
} else {
- return $.Deferred().reject();
+ def.reject();
}
}
+ def.resolve();
});
+ return def;
})
.then(function () {
return self._fetchRelationalData(record);
@@ -3324,10 +3346,7 @@ var BasicModel = AbstractModel.extend({
* @param {string[]} fields changed fields
* @param {string} [viewType] current viewType. If not set, we will assume
* main viewType from the record
- * @returns {Deferred} Note: this deferred cannot fail. It is either in the
- * 'success' or 'pending' state. If the onchange rpc fails, it will be
- * resolved with an empty dictionary. Note that the initial change was
- * already applied.
+ * @returns {Deferred}
*/
_performOnChange: function (record, fields, viewType) {
var self = this;
@@ -3347,9 +3366,7 @@ var BasicModel = AbstractModel.extend({
var context = this._getContext(record, options);
var currentData = this._generateOnChangeData(record, {changesOnly: false});
- var def = $.Deferred();
-
- self._rpc({
+ return self._rpc({
model: record.model,
method: 'onchange',
args: [idList, currentData, fields, onchangeSpec, context],
@@ -3374,12 +3391,9 @@ var BasicModel = AbstractModel.extend({
record._domains = _.extend(record._domains, result.domain);
}
return self._applyOnChange(result.value, record).then(function () {
- def.resolve(result);
+ return result;
});
- }).fail(function () {
- def.resolve({});
});
- return def;
},
/**
* Once a record is created and some data has been fetched, we need to do
diff --git a/addons/web/static/src/js/views/calendar/calendar_controller.js b/addons/web/static/src/js/views/calendar/calendar_controller.js
index f76f40460a1..2b17710935c 100644
--- a/addons/web/static/src/js/views/calendar/calendar_controller.js
+++ b/addons/web/static/src/js/views/calendar/calendar_controller.js
@@ -115,7 +115,7 @@ var CalendarController = AbstractController.extend({
*/
_onChangeDate: function (event) {
var modelData = this.model.get();
- if (modelData.target_date.isSame(event.data.date)) {
+ if (modelData.target_date.format('YYYY-MM-DD') === event.data.date.format('YYYY-MM-DD')) {
// When clicking on same date, toggle between the two views
switch (modelData.scale) {
case 'month': this.model.setScale('week'); break;
diff --git a/addons/web/static/src/js/views/calendar/calendar_model.js b/addons/web/static/src/js/views/calendar/calendar_model.js
index b32374057bb..a0f6515b98c 100644
--- a/addons/web/static/src/js/views/calendar/calendar_model.js
+++ b/addons/web/static/src/js/views/calendar/calendar_model.js
@@ -255,6 +255,8 @@ return AbstractModel.extend({
*/
setDate: function (start, highlight) {
this.data.start_date = this.data.end_date = this.data.target_date = this.data.highlight_date = start;
+ this.data.start_date.utc().add(this.getSession().getTZOffset(this.data.start_date), 'minutes');
+
switch (this.data.scale) {
case 'month':
this.data.start_date = this.data.start_date.clone().startOf('month').startOf('week');
@@ -391,6 +393,12 @@ return AbstractModel.extend({
firstDay: moment().startOf('week').isoWeekday(),
};
},
+ /**
+ * Return a domain from the date range
+ *
+ * @private
+ * @returns {Array}
+ */
_getRangeDomain: function () {
// Build OpenERP Domain to filter object by this.mapping.date_start field
// between given start, end dates.
@@ -623,8 +631,10 @@ return AbstractModel.extend({
date_stop = date_start.clone().add(date_delay,'hours');
}
- date_start.add(this.getSession().getTZOffset(date_start), 'minutes');
- date_stop.add(this.getSession().getTZOffset(date_stop), 'minutes');
+ if (!all_day) {
+ date_start.add(this.getSession().getTZOffset(date_start), 'minutes');
+ date_stop.add(this.getSession().getTZOffset(date_stop), 'minutes');
+ }
if (this.mapping.all_day && evt[this.mapping.all_day]) {
date_stop.add(1, 'days');
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 a6248c827a2..7b53b60f90c 100644
--- a/addons/web/static/src/js/views/form/form_controller.js
+++ b/addons/web/static/src/js/views/form/form_controller.js
@@ -367,12 +367,23 @@ var FormController = BasicController.extend({
this._disableButtons();
+ function saveAndExecuteAction () {
+ return self.saveRecord(self.handle, {
+ stayInEdit: true,
+ }).then(function () {
+ // we need to reget the record to make sure we have changes made
+ // by the basic model, such as the new res_id, if the record is
+ // new.
+ var record = self.model.get(event.data.record.id);
+ return self._callButtonAction(attrs, record);
+ });
+ }
var attrs = event.data.attrs;
if (attrs.confirm) {
var d = $.Deferred();
- Dialog.confirm(this, attrs.confirm, { confirm_callback: function () {
- self._callButtonAction(attrs, event.data.record);
- }}).on("closed", null, function () {
+ Dialog.confirm(this, attrs.confirm, {
+ confirm_callback: saveAndExecuteAction,
+ }).on("closed", null, function () {
d.resolve();
});
def = d.promise();
@@ -380,15 +391,7 @@ var FormController = BasicController.extend({
def = this._callButtonAction(attrs, event.data.record);
} else if (!attrs.special || attrs.special === 'save') {
// save the record but don't switch to readonly mode
- def = this.saveRecord(this.handle, {
- stayInEdit: true,
- }).then(function () {
- // we need to reget the record to make sure we have changes made
- // by the basic model, such as the new res_id, if the record is
- // new.
- var record = self.model.get(event.data.record.id);
- return self._callButtonAction(attrs, record);
- });
+ def = saveAndExecuteAction();
}
def.always(this._enableButtons.bind(this));
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 8b24d265be8..33c075fc77f 100644
--- a/addons/web/static/src/js/views/list/list_renderer.js
+++ b/addons/web/static/src/js/views/list/list_renderer.js
@@ -101,9 +101,6 @@ var ListRenderer = BasicRenderer.extend({
} else {
data = this.state.data;
}
- if (data.length === 0) {
- return;
- }
_.each(this.columns, this._computeColumnAggregates.bind(this, data));
},
@@ -145,7 +142,7 @@ var ListRenderer = BasicRenderer.extend({
}
});
if (func === 'avg') {
- aggregateValue = aggregateValue / count;
+ aggregateValue = count ? aggregateValue / count : aggregateValue;
}
column.aggregate = {
help: attrs[func],