[IMP] openerp, web(_*): batch views RPCs
Load the fields_view of all views of the action together. If a searchview is required, also load its fields_view and its filters. Use lazy-loading for fields as they are required for the Pivot and Graph views only, and may be used by Filters and GroupBy menus of the Search view if the users clicks on 'Add a custom Filter/Group'. The fields are thus also loaded directly if the first view of the action is Pivot or Graph, and otherwise, they are lazy-loaded when/if the user opens a Pivot or Graph view, or if he wants to add a custom Filter/Group. Some refactoring of views API as well: - Views must now be instantiated with their fields_view - Move code requiring fields_view from start() to init(), or willStart() for heavy processing. - Remove view_type attribute from views as it was used to call fields_view_get. - Remove view_id parameter to views' init as it is no longer used. - Move duplicate code from various views' init to the View widget. - Remove view_loading() function from views as they now follow the classical lifecycle of widgets (init -> willStart -> start -> destroy). - Searchview now extends View like all other views. - Remove useless function guard_active() as it was used for the form_view's action buttons to wrap handlers to ensure that they are executed only if the form_view is the current active view. This is not useful anymore as from v9, views and their control panel are appended in the DOM simultaneously, so one can't click on a button of a view which is not the current active view.
This commit is contained in:
@@ -186,8 +186,7 @@ function start_modules (modules) {
|
||||
// Monkey-patching of the ListView for backward compatibiliy of the colors and
|
||||
// fonts row's attributes, as they are deprecated in 9.0.
|
||||
ListView.include({
|
||||
load_list: function(data) {
|
||||
this._super(data);
|
||||
willStart: function() {
|
||||
if (this.fields_view.arch.attrs.colors) {
|
||||
this.colors = _(this.fields_view.arch.attrs.colors.split(';')).chain()
|
||||
.compact()
|
||||
@@ -208,6 +207,8 @@ ListView.include({
|
||||
return [font, py.parse(py.tokenize(expr)), expr];
|
||||
}).value();
|
||||
}
|
||||
|
||||
return this._super(data);
|
||||
},
|
||||
/**
|
||||
* Returns the style for the provided record in the current view (from the
|
||||
|
||||
@@ -82,29 +82,31 @@ var Model = Class.extend({
|
||||
* @param {Boolean} [args.toolbar=false] get the toolbar definition
|
||||
*/
|
||||
fields_view_get: function (options) {
|
||||
|
||||
return this.call('fields_view_get', {
|
||||
view_id: options.view_id,
|
||||
view_type: options.view_type,
|
||||
context: options.context,
|
||||
toolbar: options.toolbar || false
|
||||
}).then(function postprocess(fvg) {
|
||||
var doc = $.parseXML(fvg.arch).documentElement;
|
||||
fvg.arch = utils.xml_to_json(doc, (doc.nodeName.toLowerCase() !== 'kanban'));
|
||||
if ('id' in fvg.fields) {
|
||||
// Special case for id's
|
||||
var id_field = fvg.fields.id;
|
||||
id_field.original_type = id_field.type;
|
||||
id_field.type = 'id';
|
||||
}
|
||||
_.each(fvg.fields, function(field) {
|
||||
_.each(field.views || {}, function(view) {
|
||||
postprocess(view);
|
||||
});
|
||||
}).then(this.postprocess_fvg.bind(this));
|
||||
},
|
||||
|
||||
postprocess_fvg: function (fvg) {
|
||||
var self = this;
|
||||
var doc = $.parseXML(fvg.arch).documentElement;
|
||||
fvg.arch = utils.xml_to_json(doc, (doc.nodeName.toLowerCase() !== 'kanban'));
|
||||
if ('id' in fvg.fields) {
|
||||
// Special case for id's
|
||||
var id_field = fvg.fields.id;
|
||||
id_field.original_type = id_field.type;
|
||||
id_field.type = 'id';
|
||||
}
|
||||
_.each(fvg.fields, function(field) {
|
||||
_.each(field.views || {}, function(view) {
|
||||
self.postprocess_fvg(view);
|
||||
});
|
||||
return fvg;
|
||||
});
|
||||
}
|
||||
return fvg;
|
||||
},
|
||||
});
|
||||
|
||||
return Model;
|
||||
|
||||
@@ -14,10 +14,11 @@ var View = Widget.extend({
|
||||
this.do_action(action_data);
|
||||
}
|
||||
},
|
||||
defaults: {
|
||||
action: {},
|
||||
},
|
||||
// name displayed in view switchers
|
||||
display_name: '',
|
||||
// define a view type for each view to allow automatic call to fields_view_get.
|
||||
view_type: undefined,
|
||||
// used by views that need a searchview.
|
||||
searchable: true,
|
||||
// used by views that need a searchview but don't want it to be displayed.
|
||||
@@ -27,57 +28,28 @@ var View = Widget.extend({
|
||||
multi_record: true,
|
||||
// icon is the font-awesome icon to display in the view switcher
|
||||
icon: 'fa-question',
|
||||
init: function(parent, dataset, view_id, options) {
|
||||
// whether or not the view requires the model's fields to render itself
|
||||
require_fields: false,
|
||||
init: function(parent, dataset, fields_view, options) {
|
||||
this._super(parent);
|
||||
this.ViewManager = parent;
|
||||
this.dataset = dataset;
|
||||
this.view_id = view_id;
|
||||
this.set_default_options(options);
|
||||
this.model = dataset.model;
|
||||
this.fields_view = fields_view;
|
||||
this.options = _.defaults({}, options, this.defaults);
|
||||
},
|
||||
start: function () {
|
||||
return this.load_view();
|
||||
},
|
||||
load_view: function() {
|
||||
var self = this;
|
||||
var view_loaded_def;
|
||||
if (this.embedded_view) {
|
||||
view_loaded_def = $.Deferred();
|
||||
$.async_when().done(function() {
|
||||
view_loaded_def.resolve(self.embedded_view);
|
||||
});
|
||||
} else {
|
||||
if (! this.view_type)
|
||||
console.warn("view_type is not defined", this);
|
||||
view_loaded_def = this.dataset._model.fields_view_get({
|
||||
"view_id": this.view_id,
|
||||
"view_type": this.view_type,
|
||||
"toolbar": !!this.options.sidebar,
|
||||
"context": this.dataset.get_context(),
|
||||
});
|
||||
}
|
||||
return this.alive(view_loaded_def).then(function(r) {
|
||||
self.fields_view = r;
|
||||
// add css classes that reflect the (absence of) access rights
|
||||
self.$el.addClass('oe_view')
|
||||
.toggleClass('oe_cannot_create', !self.is_action_enabled('create'))
|
||||
.toggleClass('oe_cannot_edit', !self.is_action_enabled('edit'))
|
||||
.toggleClass('oe_cannot_delete', !self.is_action_enabled('delete'));
|
||||
return $.when(self.view_loading(r)).then(function() {
|
||||
self.trigger('view_loaded', r);
|
||||
});
|
||||
});
|
||||
},
|
||||
view_loading: function(r) {
|
||||
},
|
||||
set_default_options: function(options) {
|
||||
this.options = options || {};
|
||||
_.defaults(this.options, {
|
||||
// All possible views options should be defaulted here
|
||||
$sidebar: null,
|
||||
sidebar_id: null,
|
||||
action: null,
|
||||
action_views_ids: {}
|
||||
});
|
||||
/**
|
||||
* Triggers event 'view_loaded'.
|
||||
* Views extending start() must call this.super() once they are ready.
|
||||
* @return {Deferred}
|
||||
*/
|
||||
start: function() {
|
||||
// add css classes that reflect the (absence of) access rights
|
||||
this.$el.addClass('oe_view')
|
||||
.toggleClass('oe_cannot_create', !this.is_action_enabled('create'))
|
||||
.toggleClass('oe_cannot_edit', !this.is_action_enabled('edit'))
|
||||
.toggleClass('oe_cannot_delete', !this.is_action_enabled('delete'));
|
||||
return this._super().then(this.trigger.bind(this, 'view_loaded'));
|
||||
},
|
||||
/**
|
||||
* Fetches and executes the action identified by ``action_data``.
|
||||
@@ -87,7 +59,7 @@ var View = Widget.extend({
|
||||
* @param {String} [action_data.special=null] special action handlers (currently: only ``'cancel'``)
|
||||
* @param {String} [action_data.type='workflow'] the action type, if present, one of ``'object'``, ``'action'`` or ``'workflow'``
|
||||
* @param {Object} [action_data.context=null] additional action context, to add to the current context
|
||||
* @param {instance.web.DataSet} dataset a dataset object used to communicate with the server
|
||||
* @param {DataSet} dataset a dataset object used to communicate with the server
|
||||
* @param {Object} [record_id] the identifier of the object on which the action is to be applied
|
||||
* @param {Function} on_closed callback to execute when dialog is closed or when the action does not generate any result (no new action)
|
||||
*/
|
||||
@@ -158,47 +130,16 @@ var View = Widget.extend({
|
||||
return dataset.exec_workflow(record_id, action_data.name).then(handler);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Directly set a view to use instead of calling fields_view_get. This method must
|
||||
* be called before start(). When an embedded view is set, underlying implementations
|
||||
* of instance.web.View must use the provided view instead of any other one.
|
||||
*
|
||||
* @param embedded_view A view.
|
||||
*/
|
||||
set_embedded_view: function(embedded_view) {
|
||||
this.embedded_view = embedded_view;
|
||||
},
|
||||
do_show: function () {
|
||||
this._super();
|
||||
core.bus.trigger('view_shown', this);
|
||||
},
|
||||
is_active: function () {
|
||||
return this.ViewManager.active_view.controller === this;
|
||||
},
|
||||
/**
|
||||
* Wraps fn to only call it if the current view is the active one. If the
|
||||
* current view is not active, doesn't call fn.
|
||||
*
|
||||
* fn can not return anything, as a non-call to fn can't return anything
|
||||
* either
|
||||
*
|
||||
* @param {Function} fn function to wrap in the active guard
|
||||
*/
|
||||
guard_active: function (fn) {
|
||||
var self = this;
|
||||
return function () {
|
||||
if (self.is_active()) {
|
||||
fn.apply(self, arguments);
|
||||
}
|
||||
};
|
||||
},
|
||||
do_push_state: function(state) {
|
||||
if (this.getParent() && this.getParent().do_push_state) {
|
||||
this.getParent().do_push_state(state);
|
||||
}
|
||||
},
|
||||
do_load_state: function (state, warm) {
|
||||
|
||||
},
|
||||
/**
|
||||
* This function should render the buttons of the view, set this.$buttons to
|
||||
|
||||
@@ -3,7 +3,6 @@ odoo.define('web.ViewManager', function (require) {
|
||||
|
||||
var ControlPanelMixin = require('web.ControlPanelMixin');
|
||||
var core = require('web.core');
|
||||
var data = require('web.data');
|
||||
var framework = require('web.framework');
|
||||
var Model = require('web.DataModel');
|
||||
var pyeval = require('web.pyeval');
|
||||
@@ -17,14 +16,14 @@ var ViewManager = Widget.extend(ControlPanelMixin, {
|
||||
template: "ViewManager",
|
||||
/**
|
||||
* @param {Object} [dataset]
|
||||
* @param {Array} [views] List of [view_id, view_type]
|
||||
* @param {Array} [views] List of [view_id, view_type[, fields_view]]
|
||||
* @param {Object} [flags] various boolean describing UI state
|
||||
*/
|
||||
init: function(parent, dataset, views, flags, options) {
|
||||
var self = this;
|
||||
this._super(parent);
|
||||
|
||||
this.action = options && options.action;
|
||||
this.action = options && options.action || {};
|
||||
this.action_manager = options && options.action_manager;
|
||||
this.flags = flags || {};
|
||||
this.dataset = dataset;
|
||||
@@ -33,7 +32,7 @@ var ViewManager = Widget.extend(ControlPanelMixin, {
|
||||
this.view_stack = []; // used for breadcrumbs
|
||||
this.active_view = null;
|
||||
this.registry = core.view_registry;
|
||||
this.title = this.action && this.action.name;
|
||||
this.title = this.action.name;
|
||||
this.is_in_DOM = false; // used to know if the view manager is attached in the DOM
|
||||
_.each(views, function (view) {
|
||||
var view_type = view[1] || view.view_type;
|
||||
@@ -44,45 +43,42 @@ var ViewManager = Widget.extend(ControlPanelMixin, {
|
||||
}
|
||||
var view_label = View.prototype.display_name;
|
||||
var view_descr = {
|
||||
controller: null,
|
||||
options: view.options || {},
|
||||
view_id: view[0] || view.view_id,
|
||||
type: view_type,
|
||||
label: view_label,
|
||||
embedded_view: view.embedded_view,
|
||||
title: self.title,
|
||||
button_label: _.str.sprintf(_t('%(view_type)s view'), {'view_type': (view_label || view_type)}),
|
||||
multi_record: View.prototype.multi_record,
|
||||
accesskey: View.prototype.accesskey,
|
||||
icon: View.prototype.icon,
|
||||
};
|
||||
accesskey: View.prototype.accesskey,
|
||||
button_label: _.str.sprintf(_t('%(view_type)s view'), {'view_type': (view_label || view_type)}),
|
||||
controller: null,
|
||||
fields_view: view[2] || view.fields_view,
|
||||
embedded_view: view.embedded_view,
|
||||
icon: View.prototype.icon,
|
||||
label: view_label,
|
||||
multi_record: View.prototype.multi_record,
|
||||
options: view.options || {},
|
||||
require_fields: View.prototype.require_fields,
|
||||
title: self.title,
|
||||
type: view_type,
|
||||
view_id: view[0] || view.view_id,
|
||||
};
|
||||
self.view_order.push(view_descr);
|
||||
self.views[view_type] = view_descr;
|
||||
});
|
||||
this.first_view = this.views[options && options.view_type]; // view to open first
|
||||
|
||||
// Listen to event 'switch_view' indicating that the VM must now display view wiew_type
|
||||
this.on('switch_view', this, function(view_type) {
|
||||
if (view_type === 'form' && this.active_view && this.active_view.type === 'form') {
|
||||
this._display_view(view_type);
|
||||
} else {
|
||||
this.switch_mode(view_type);
|
||||
}
|
||||
});
|
||||
this.default_view = this.get_default_view();
|
||||
},
|
||||
willStart: function () {
|
||||
var views_def;
|
||||
var first_view_to_display = this.first_view || this.default_view;
|
||||
if (!first_view_to_display.fields_view || (this.flags.search_view && !this.search_fields_view)) {
|
||||
views_def = this.load_views(first_view_to_display.require_fields);
|
||||
}
|
||||
return $.when(this._super(), views_def);
|
||||
},
|
||||
/**
|
||||
* @returns {jQuery.Deferred} initial view loading promise
|
||||
* @return {Deferred} initial view and search view (if any) loading promise
|
||||
*/
|
||||
start: function() {
|
||||
var self = this;
|
||||
this._super();
|
||||
|
||||
var views_ids = {};
|
||||
_.each(this.views, function (view) {
|
||||
views_ids[view.type] = view.view_id;
|
||||
view.options = _.extend({
|
||||
action : self.action,
|
||||
action_views_ids : views_ids,
|
||||
action: self.action,
|
||||
}, self.flags, self.flags[view.type], view.options);
|
||||
view.$container = self.$(".oe-view-manager-view-" + view.type);
|
||||
});
|
||||
@@ -99,16 +95,58 @@ var ViewManager = Widget.extend(ControlPanelMixin, {
|
||||
|
||||
// If a non multi-record first_view is given, switch to it but first push the default_view
|
||||
// to the view_stack to complete the breadcrumbs
|
||||
var default_view = this.get_default_view();
|
||||
if (this.first_view && !this.first_view.multi_record && default_view.multi_record) {
|
||||
default_view.controller = this.create_view(default_view);
|
||||
this.view_stack.push(default_view);
|
||||
if (this.first_view && !this.first_view.multi_record && this.default_view.multi_record) {
|
||||
this.default_view.controller = this.create_view(this.default_view);
|
||||
this.view_stack.push(this.default_view);
|
||||
}
|
||||
var view_to_load = this.first_view || default_view;
|
||||
var view_to_load = this.first_view || this.default_view;
|
||||
var options = this.flags[view_to_load] && this.flags[view_to_load].options;
|
||||
var main_view_loaded = this.switch_mode(view_to_load.type, options);
|
||||
|
||||
return $.when(main_view_loaded, this.search_view_loaded);
|
||||
return $.when(this._super(), main_view_loaded, this.search_view_loaded);
|
||||
},
|
||||
/**
|
||||
* Loads all missing field_views of views in this.views and the search view.
|
||||
*
|
||||
* @param {Boolean} [load_fields] whether or not to load the fields as well
|
||||
* @return {Deferred}
|
||||
*/
|
||||
load_views: function (load_fields) {
|
||||
var self = this;
|
||||
var views = [];
|
||||
_.each(this.views, function (view) {
|
||||
if (!view.fields_view) {
|
||||
views.push([view.view_id, view.type]);
|
||||
}
|
||||
});
|
||||
if (this.flags.search_view && !this.search_fields_view) {
|
||||
var searchview_id = this.action.search_view_id && this.action.search_view_id[0];
|
||||
views.push([searchview_id || false, 'search']);
|
||||
}
|
||||
var options = {
|
||||
action_id: this.action.id,
|
||||
load_fields: load_fields && !this.fields_get_def,
|
||||
toolbar: this.flags.sidebar,
|
||||
};
|
||||
return this.dataset._model.call('load_views', {
|
||||
views: views,
|
||||
options: options,
|
||||
context: this.dataset.get_context(),
|
||||
}).then(function (result) {
|
||||
_.each(result.fields_views, function (fields_view, view_type) {
|
||||
if (view_type === 'search') {
|
||||
self.search_fields_view = self.dataset._model.postprocess_fvg(fields_view);
|
||||
} else {
|
||||
self.views[view_type].fields_view = self.dataset._model.postprocess_fvg(fields_view);
|
||||
}
|
||||
});
|
||||
if (result.filters) {
|
||||
self.search_filters = result.filters;
|
||||
}
|
||||
if (result.fields) {
|
||||
self.fields_get_def = $.when(result.fields);
|
||||
}
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Returns the default view with the following fallbacks:
|
||||
@@ -120,6 +158,14 @@ var ViewManager = Widget.extend(ControlPanelMixin, {
|
||||
get_default_view: function() {
|
||||
return this.views[this.flags.default_view || this.view_order[0].type];
|
||||
},
|
||||
get_fields: function() {
|
||||
if (!this.fields_get_def) {
|
||||
this.fields_get_def = this.dataset._model.call('fields_get', {
|
||||
context: this.dataset.get_context()
|
||||
});
|
||||
}
|
||||
return this.fields_get_def;
|
||||
},
|
||||
switch_mode: function(view_type, view_options) {
|
||||
var self = this;
|
||||
var view = this.views[view_type];
|
||||
@@ -131,51 +177,60 @@ var ViewManager = Widget.extend(ControlPanelMixin, {
|
||||
this.currently_switching = true; // prevent overlapping switches
|
||||
}
|
||||
|
||||
if (view.multi_record) {
|
||||
this.view_stack = [];
|
||||
} else if (this.view_stack.length > 0 && !(_.last(this.view_stack).multi_record)) {
|
||||
// Replace the last view by the new one if both are mono_record
|
||||
this.view_stack.pop();
|
||||
}
|
||||
this.view_stack.push(view);
|
||||
this.active_view = view;
|
||||
|
||||
if (!view.loaded) {
|
||||
if (!view.controller) {
|
||||
view.controller = this.create_view(view, view_options);
|
||||
}
|
||||
view.$fragment = $('<div>');
|
||||
view.loaded = view.controller.appendTo(view.$fragment).done(function () {
|
||||
// Remove the unnecessary outer div
|
||||
view.$fragment = view.$fragment.contents();
|
||||
self.trigger("controller_inited", view.type, view.controller);
|
||||
});
|
||||
// Ensure that the fields_view has been loaded
|
||||
var views_def;
|
||||
if (!view.fields_view) {
|
||||
views_def = this.load_views(view.require_fields);
|
||||
}
|
||||
|
||||
// Call do_search on the searchview to compute domains, contexts and groupbys
|
||||
if (this.search_view_loaded &&
|
||||
this.flags.auto_search &&
|
||||
view.controller.searchable !== false) {
|
||||
this.active_search = $.Deferred();
|
||||
$.when(this.search_view_loaded, view.loaded).done(function() {
|
||||
self.searchview.do_search();
|
||||
});
|
||||
}
|
||||
|
||||
return $.when(view.loaded, this.active_search)
|
||||
.then(function() {
|
||||
return self._display_view(view_options, old_view).then(function() {
|
||||
self.trigger('switch_mode', view_type, view_options);
|
||||
});
|
||||
}).fail(function(e) {
|
||||
if (!(e && e.code === 200 && e.data.exception_type)) {
|
||||
self.do_warn(_t("Error"), view.controller.display_name + _t(" view couldn't be loaded"));
|
||||
}
|
||||
// Restore internal state
|
||||
self.active_view = old_view;
|
||||
return $.when(views_def).then(function () {
|
||||
if (view.multi_record) {
|
||||
self.view_stack = [];
|
||||
} else if (self.view_stack.length > 0 && !(_.last(self.view_stack).multi_record)) {
|
||||
// Replace the last view by the new one if both are mono_record
|
||||
self.view_stack.pop();
|
||||
}).always(function () {
|
||||
self.currently_switching = false;
|
||||
}
|
||||
self.view_stack.push(view);
|
||||
|
||||
self.active_view = view;
|
||||
|
||||
if (!view.loaded) {
|
||||
if (!view.controller) {
|
||||
view.controller = self.create_view(view, view_options);
|
||||
}
|
||||
view.$fragment = $('<div>');
|
||||
view.loaded = view.controller.appendTo(view.$fragment).done(function () {
|
||||
// Remove the unnecessary outer div
|
||||
view.$fragment = view.$fragment.contents();
|
||||
self.trigger("controller_inited", view.type, view.controller);
|
||||
});
|
||||
}
|
||||
|
||||
// Call do_search on the searchview to compute domains, contexts and groupbys
|
||||
if (self.search_view_loaded &&
|
||||
self.flags.auto_search &&
|
||||
view.controller.searchable !== false) {
|
||||
self.active_search = $.Deferred();
|
||||
$.when(self.search_view_loaded, view.loaded).done(function() {
|
||||
self.searchview.do_search();
|
||||
});
|
||||
}
|
||||
|
||||
return $.when(view.loaded, self.active_search)
|
||||
.then(function() {
|
||||
return self._display_view(view_options, old_view).then(function() {
|
||||
self.trigger('switch_mode', view_type, view_options);
|
||||
});
|
||||
}).fail(function(e) {
|
||||
if (!(e && e.code === 200 && e.data.exception_type)) {
|
||||
self.do_warn(_t("Error"), view.controller.display_name + _t(" view couldn't be loaded"));
|
||||
}
|
||||
// Restore internal state
|
||||
self.active_view = old_view;
|
||||
self.view_stack.pop();
|
||||
});
|
||||
}).always(function () {
|
||||
self.currently_switching = false;
|
||||
});
|
||||
},
|
||||
_display_view: function (view_options, old_view) {
|
||||
@@ -218,17 +273,12 @@ var ViewManager = Widget.extend(ControlPanelMixin, {
|
||||
var self = this;
|
||||
var View = this.registry.get(view.type);
|
||||
var options = _.clone(view.options);
|
||||
|
||||
if (view.type === "form" && ((this.action && (this.action.target === 'new' || this.action.target === 'inline')) ||
|
||||
if (view.type === "form" && ((this.action.target === 'new' || this.action.target === 'inline') ||
|
||||
(view_options && view_options.mode === 'edit'))) {
|
||||
options.initial_mode = options.initial_mode || 'edit';
|
||||
}
|
||||
|
||||
var controller = new View(this, this.dataset, view.view_id, options);
|
||||
|
||||
if (view.embedded_view) {
|
||||
controller.set_embedded_view(view.embedded_view);
|
||||
}
|
||||
var controller = new View(this, this.dataset, view.fields_view, options);
|
||||
|
||||
controller.on('switch_mode', this, this.switch_mode.bind(this));
|
||||
controller.on('history_back', this, function () {
|
||||
@@ -306,12 +356,6 @@ var ViewManager = Widget.extend(ControlPanelMixin, {
|
||||
}
|
||||
return this.active_view.control_elements;
|
||||
},
|
||||
/**
|
||||
* @returns {Number|Boolean} the view id of the given type, false if not found
|
||||
*/
|
||||
get_view_id: function(view_type) {
|
||||
return this.views[view_type] && this.views[view_type].view_id || false;
|
||||
},
|
||||
/**
|
||||
* Sets up the current viewmanager's search view.
|
||||
* Sets $searchview and $searchview_buttons in control_elements to send to the ControlPanel
|
||||
@@ -325,28 +369,25 @@ var ViewManager = Widget.extend(ControlPanelMixin, {
|
||||
this.searchview.destroy();
|
||||
}
|
||||
|
||||
var view_id = (this.action && this.action.search_view_id && this.action.search_view_id[0]) || false;
|
||||
|
||||
var search_defaults = {};
|
||||
|
||||
var context = this.action ? this.action.context : [];
|
||||
var context = this.action.context || [];
|
||||
_.each(context, function (value, key) {
|
||||
var match = /^search_default_(.*)$/.exec(key);
|
||||
if (match) {
|
||||
search_defaults[match[1]] = value;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
var options = {
|
||||
hidden: this.flags.search_view === false,
|
||||
disable_custom_filters: this.flags.search_disable_custom_filters,
|
||||
$buttons: $("<div>"),
|
||||
action: this.action,
|
||||
search_defaults: search_defaults,
|
||||
filters: this.search_filters,
|
||||
};
|
||||
// Instantiate the SearchView, but do not append it nor its buttons to the DOM as this will
|
||||
// be done later, simultaneously to all other ControlPanel elements
|
||||
this.searchview = new SearchView(this, this.dataset, view_id, search_defaults, options);
|
||||
this.searchview = new SearchView(this, this.dataset, this.search_fields_view, options);
|
||||
|
||||
this.searchview.on('search_data', this, this.search.bind(this));
|
||||
return $.when(this.searchview.appendTo($("<div>"))).done(function() {
|
||||
|
||||
@@ -769,7 +769,7 @@ var AbstractField = FormWidget.extend(FieldInterface, {
|
||||
on_translate: function() {
|
||||
var self = this;
|
||||
var trans = new data.DataSet(this, 'ir.translation');
|
||||
return trans.call_button('translate_fields', [this.view.dataset.model, this.view.datarecord.id, this.name, this.view.dataset.get_context()]).done(function(r) {
|
||||
return trans.call_button('translate_fields', [this.view.model, this.view.datarecord.id, this.name, this.view.dataset.get_context()]).done(function(r) {
|
||||
self.do_action(r);
|
||||
});
|
||||
},
|
||||
@@ -793,7 +793,6 @@ var ViewDialog = Dialog.extend({ // FIXME should use ViewManager
|
||||
* options:
|
||||
* -readonly: only applicable when not in creation mode, default to false
|
||||
* - alternative_form_view
|
||||
* - view_id
|
||||
* - write_function
|
||||
* - read_function
|
||||
* - create_function
|
||||
@@ -814,7 +813,6 @@ var ViewDialog = Dialog.extend({ // FIXME should use ViewManager
|
||||
this.context = options.context || {};
|
||||
this.options = _.extend(this.options || {}, options || {});
|
||||
|
||||
this.on('closed', this, this.select);
|
||||
this.on_selected = options.on_selected || (function() {});
|
||||
},
|
||||
|
||||
@@ -838,6 +836,8 @@ var ViewDialog = Dialog.extend({ // FIXME should use ViewManager
|
||||
};
|
||||
this.dataset.parent_view = this.options.parent_view;
|
||||
this.dataset.child_name = this.options.child_name;
|
||||
|
||||
this.on('closed', this, this.select);
|
||||
},
|
||||
|
||||
select: function() {
|
||||
@@ -898,33 +898,41 @@ var FormViewDialog = ViewDialog.extend({
|
||||
|
||||
open: function() {
|
||||
var self = this;
|
||||
self._super();
|
||||
self.init_dataset();
|
||||
var _super = this._super.bind(this);
|
||||
this.init_dataset();
|
||||
|
||||
if (self.res_id) {
|
||||
self.dataset.ids = [self.res_id];
|
||||
self.dataset.index = 0;
|
||||
if (this.res_id) {
|
||||
this.dataset.ids = [this.res_id];
|
||||
this.dataset.index = 0;
|
||||
} else {
|
||||
self.dataset.index = null;
|
||||
this.dataset.index = null;
|
||||
}
|
||||
var options = _.clone(self.options.form_view_options) || {};
|
||||
if (self.res_id !== null) {
|
||||
options.initial_mode = self.options.readonly ? "view" : "edit";
|
||||
var options = _.clone(this.options.form_view_options) || {};
|
||||
if (this.res_id !== null) {
|
||||
options.initial_mode = this.options.readonly ? "view" : "edit";
|
||||
}
|
||||
_.extend(options, {
|
||||
$buttons: self.$buttons,
|
||||
$buttons: this.$buttons,
|
||||
});
|
||||
var FormView = core.view_registry.get('form');
|
||||
self.view_form = new FormView(self, self.dataset, self.options.view_id || false, options);
|
||||
if (self.options.alternative_form_view) {
|
||||
self.view_form.set_embedded_view(self.options.alternative_form_view);
|
||||
var fields_view_def;
|
||||
if (this.options.alternative_form_view) {
|
||||
fields_view_def = $.when(this.options.alternative_form_view);
|
||||
} else {
|
||||
fields_view_def = this.dataset._model.fields_view_get({
|
||||
view_id: this.options.view_id || false,
|
||||
view_type: 'form',
|
||||
context: this.dataset.get_context(),
|
||||
toolbar: false,
|
||||
});
|
||||
}
|
||||
|
||||
self.do_hide();
|
||||
self.view_form.appendTo(self.$el);
|
||||
self.view_form.on("form_view_loaded", self, function() {
|
||||
self.view_form.do_show().then(function() {
|
||||
self.do_show();
|
||||
fields_view_def.then(function (fields_view) {
|
||||
self.view_form = new FormView(self, self.dataset, fields_view, options);
|
||||
var fragment = document.createDocumentFragment();
|
||||
self.view_form.appendTo(fragment).then(function () {
|
||||
self.view_form.do_show().then(function() {
|
||||
_super().$el.append(fragment);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -965,49 +973,64 @@ var SelectCreateDialog = ViewDialog.extend({
|
||||
},
|
||||
|
||||
open: function() {
|
||||
this._super();
|
||||
this.init_dataset();
|
||||
if (this.options.initial_view == "search") {
|
||||
var context = pyeval.sync_eval_domains_and_contexts({
|
||||
domains: [],
|
||||
contexts: [this.context]
|
||||
}).context;
|
||||
var search_defaults = {};
|
||||
_.each(context, function (value_, key) {
|
||||
var match = /^search_default_(.*)$/.exec(key);
|
||||
if (match) {
|
||||
search_defaults[match[1]] = value_;
|
||||
}
|
||||
});
|
||||
this.setup(search_defaults);
|
||||
} else { // "form"
|
||||
this.create_edit_record();
|
||||
if(this.options.initial_view !== "search") {
|
||||
return this.create_edit_record();
|
||||
}
|
||||
|
||||
var self = this;
|
||||
var _super = this._super.bind(this);
|
||||
this.init_dataset();
|
||||
var context = pyeval.sync_eval_domains_and_contexts({
|
||||
domains: [],
|
||||
contexts: [this.context]
|
||||
}).context;
|
||||
var search_defaults = {};
|
||||
_.each(context, function (value_, key) {
|
||||
var match = /^search_default_(.*)$/.exec(key);
|
||||
if (match) {
|
||||
search_defaults[match[1]] = value_;
|
||||
}
|
||||
});
|
||||
this.dataset._model.call('load_views', {
|
||||
views: [[false, 'list'], [false, 'search']],
|
||||
context: this.dataset.get_context(),
|
||||
}).then(function (result) {
|
||||
var fields_views = _.mapObject(result.fields_views, self.dataset._model.postprocess_fvg);
|
||||
self.setup(search_defaults, fields_views).then(function (fragment) {
|
||||
_super().$el.append(fragment);
|
||||
});
|
||||
});
|
||||
return this;
|
||||
},
|
||||
|
||||
setup: function(search_defaults) {
|
||||
setup: function(search_defaults, fields_views) {
|
||||
var self = this;
|
||||
if (this.searchview) {
|
||||
this.searchview.destroy();
|
||||
}
|
||||
var $header = $('<div/>').addClass('o_modal_header').appendTo(this.$el);
|
||||
var fragment = document.createDocumentFragment();
|
||||
var $header = $('<div/>').addClass('o_modal_header').appendTo(fragment);
|
||||
var $pager = $('<div/>').addClass('o_pager').appendTo($header);
|
||||
var $buttons = $('<div/>').addClass('o_search_options').appendTo($header);
|
||||
this.searchview = new SearchView(this, this.dataset, false, search_defaults, {$buttons: $buttons});
|
||||
this.searchview.on('search_data', self, function(domains, contexts, groupbys) {
|
||||
if (self.initial_ids) {
|
||||
self.do_search(domains.concat([[["id", "in", self.initial_ids]], self.domain]),
|
||||
contexts.concat(self.context), groupbys);
|
||||
self.initial_ids = undefined;
|
||||
var options = {
|
||||
$buttons: $('<div/>').addClass('o_search_options').appendTo($header),
|
||||
search_defaults: search_defaults,
|
||||
};
|
||||
|
||||
this.searchview = new SearchView(this, this.dataset, fields_views.search, options);
|
||||
this.searchview.on('search_data', this, function(domains, contexts, groupbys) {
|
||||
if (this.initial_ids) {
|
||||
this.do_search(domains.concat([[["id", "in", this.initial_ids]], this.domain]),
|
||||
contexts.concat(this.context), groupbys);
|
||||
this.initial_ids = undefined;
|
||||
} else {
|
||||
self.do_search(domains.concat([self.domain]), contexts.concat(self.context), groupbys);
|
||||
this.do_search(domains.concat([this.domain]), contexts.concat(this.context), groupbys);
|
||||
}
|
||||
});
|
||||
this.searchview.prependTo($header).done(function() {
|
||||
return this.searchview.prependTo($header).then(function() {
|
||||
self.searchview.toggle_visibility(true);
|
||||
|
||||
self.view_list = new SelectCreateListView(self,
|
||||
self.dataset, false,
|
||||
self.dataset, fields_views.list,
|
||||
_.extend({'deletable': false,
|
||||
'selectable': !self.options.disable_multiple_selection,
|
||||
'import_enabled': false,
|
||||
@@ -1019,17 +1042,6 @@ var SelectCreateDialog = ViewDialog.extend({
|
||||
e.cancel = true;
|
||||
});
|
||||
self.view_list.popup = self;
|
||||
self.view_list.appendTo(self.$el).then(function() {
|
||||
self.view_list.do_show();
|
||||
self.view_list.render_pager($pager);
|
||||
}).then(function() {
|
||||
if (self.options.initial_facet) {
|
||||
self.searchview.query.reset([self.options.initial_facet], {
|
||||
preventSearch: true,
|
||||
});
|
||||
}
|
||||
self.searchview.do_search();
|
||||
});
|
||||
|
||||
var buttons = [
|
||||
{text: _t("Cancel"), classes: "btn-default o_form_button_cancel", close: true}
|
||||
@@ -1045,6 +1057,19 @@ var SelectCreateDialog = ViewDialog.extend({
|
||||
}});
|
||||
}
|
||||
self.set_buttons(buttons);
|
||||
|
||||
return self.view_list.appendTo(fragment).then(function() {
|
||||
self.view_list.do_show();
|
||||
self.view_list.render_pager($pager);
|
||||
if (self.options.initial_facet) {
|
||||
self.searchview.query.reset([self.options.initial_facet], {
|
||||
preventSearch: true,
|
||||
});
|
||||
}
|
||||
self.searchview.do_search();
|
||||
|
||||
return fragment;
|
||||
});
|
||||
});
|
||||
},
|
||||
do_search: function(domains, contexts, groupbys) {
|
||||
@@ -1060,8 +1085,8 @@ var SelectCreateDialog = ViewDialog.extend({
|
||||
this.$footer.find(".o_selectcreatepopup_search_select").prop('disabled', this.selected_ids.length <= 0);
|
||||
},
|
||||
create_edit_record: function() {
|
||||
new FormViewDialog(this.getParent(), this.options).open();
|
||||
this.close();
|
||||
return new FormViewDialog(this.__parentedParent, this.options).open();
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@ var Model = require('web.DataModel');
|
||||
var session = require('web.session');
|
||||
var utils = require('web.utils');
|
||||
var ViewManager = require('web.ViewManager');
|
||||
var formats = require('web.formats');
|
||||
|
||||
var _t = core._t;
|
||||
var QWeb = core.qweb;
|
||||
@@ -752,11 +751,9 @@ var FieldX2Many = AbstractManyField.extend({
|
||||
var view = {
|
||||
view_id: false,
|
||||
view_type: view_type === "tree" ? "list" : view_type,
|
||||
options: {}
|
||||
fields_view: self.field.views && self.field.views[view_type],
|
||||
options: {},
|
||||
};
|
||||
if (self.field.views && self.field.views[view_type]) {
|
||||
view.embedded_view = self.field.views[view_type];
|
||||
}
|
||||
if(view.view_type === "list") {
|
||||
_.extend(view.options, {
|
||||
action_buttons: false, // to avoid 'Save' and 'Discard' buttons to appear in X2M fields
|
||||
@@ -825,9 +822,7 @@ var FieldX2Many = AbstractManyField.extend({
|
||||
});
|
||||
});
|
||||
$.async_when().done(function () {
|
||||
if (!self.isDestroyed()) {
|
||||
self.viewmanager.appendTo(self.$el);
|
||||
}
|
||||
self.alive( self.viewmanager.appendTo(self.$el));
|
||||
});
|
||||
return def;
|
||||
},
|
||||
@@ -900,7 +895,7 @@ var X2ManyDataSet = data.BufferedDataSet.extend({
|
||||
var X2ManyViewManager = ViewManager.extend({
|
||||
init: function(parent, dataset, views, flags, x2many_views) {
|
||||
// By default, render buttons and pager in X2M fields, but no sidebar
|
||||
var flags = _.extend({}, flags, {
|
||||
flags = _.extend({}, flags, {
|
||||
headless: false,
|
||||
search_view: false,
|
||||
action_buttons: true,
|
||||
@@ -926,7 +921,6 @@ var X2ManyViewManager = ViewManager.extend({
|
||||
res_model: self.x2m.field.relation,
|
||||
res_id: id,
|
||||
context: self.x2m.build_context(),
|
||||
|
||||
title: _t("Open: ") + self.x2m.string,
|
||||
create_function: function(data, options) {
|
||||
return self.x2m.data_create(data, options);
|
||||
@@ -953,7 +947,6 @@ var X2ManyViewManager = ViewManager.extend({
|
||||
|
||||
var X2ManyListView = ListView.extend({
|
||||
is_valid: function () {
|
||||
var self = this;
|
||||
if (!this.fields_view || !this.editable()){
|
||||
return true;
|
||||
}
|
||||
@@ -967,7 +960,7 @@ var X2ManyListView = ListView.extend({
|
||||
field.no_rerender = true;
|
||||
current_values[field.name] = field.get('value');
|
||||
});
|
||||
var cached_records = _.filter(this.dataset.cache, function(item){return !_.isEmpty(item.values)});
|
||||
var cached_records = _.filter(this.dataset.cache, function(item){return !_.isEmpty(item.values);});
|
||||
var valid = _.every(cached_records, function(record){
|
||||
_.each(fields, function(field){
|
||||
var value = record.values[field.name];
|
||||
@@ -1019,14 +1012,16 @@ var X2ManyList = ListView.List.extend({
|
||||
.click(function (e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
// FIXME: there should also be an API for that one
|
||||
if (self.view.editor.form.__blur_timeout) {
|
||||
clearTimeout(self.view.editor.form.__blur_timeout);
|
||||
self.view.editor.form.__blur_timeout = false;
|
||||
var def;
|
||||
if (self.view.editable()) {
|
||||
// FIXME: there should also be an API for that one
|
||||
if (self.view.editor.form.__blur_timeout) {
|
||||
clearTimeout(self.view.editor.form.__blur_timeout);
|
||||
self.view.editor.form.__blur_timeout = false;
|
||||
}
|
||||
def = self.view.save_edition();
|
||||
}
|
||||
self.view.save_edition().done(function () {
|
||||
self.view.do_add_record();
|
||||
});
|
||||
$.when(def).done(self.view.do_add_record.bind(self));
|
||||
}));
|
||||
|
||||
var $padding = this.$current.find('tr:not([data-id]):first');
|
||||
@@ -1041,12 +1036,12 @@ var X2ManyList = ListView.List.extend({
|
||||
|
||||
var One2ManyListView = X2ManyListView.extend({
|
||||
_template: 'One2Many.listview',
|
||||
init: function (parent, dataset, view_id, options) {
|
||||
var self = this;
|
||||
this._super(parent, dataset, view_id, _.extend(options || {}, {
|
||||
init: function () {
|
||||
this._super.apply(this, arguments);
|
||||
this.options = _.extend(this.options, {
|
||||
GroupsType: One2ManyGroups,
|
||||
ListType: X2ManyList
|
||||
}));
|
||||
});
|
||||
this.on('edit:after', this, this.proxy('_after_edit'));
|
||||
this.on('save:before cancel:before', this, this.proxy('_before_unedit'));
|
||||
|
||||
@@ -1293,15 +1288,12 @@ var FieldOne2Many = FieldX2Many.extend({
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* @class
|
||||
* @extends instance.web.ListView
|
||||
*/
|
||||
var Many2ManyListView = X2ManyListView.extend(/** @lends instance.web.form.Many2ManyListView# */{
|
||||
init: function (parent, dataset, view_id, options) {
|
||||
this._super(parent, dataset, view_id, _.extend(options || {}, {
|
||||
var Many2ManyListView = X2ManyListView.extend({
|
||||
init: function () {
|
||||
this._super.apply(this, arguments);
|
||||
this.options = _.extend(this.options, {
|
||||
ListType: X2ManyList,
|
||||
}));
|
||||
});
|
||||
},
|
||||
do_add_record: function () {
|
||||
var self = this;
|
||||
@@ -1323,7 +1315,7 @@ var Many2ManyListView = X2ManyListView.extend(/** @lends instance.web.form.Many2
|
||||
do_activate_record: function(index, id) {
|
||||
var self = this;
|
||||
var pop = new common.FormViewDialog(this, {
|
||||
res_model: this.dataset.model,
|
||||
res_model: this.model,
|
||||
res_id: id,
|
||||
context: this.x2m.build_context(),
|
||||
title: _t("Open: ") + this.x2m.string,
|
||||
|
||||
@@ -16,45 +16,29 @@ var _lt = core._lt;
|
||||
var QWeb = core.qweb;
|
||||
var Class = core.Class;
|
||||
|
||||
/** @namespace */
|
||||
// instance.web.form = instance.web.form || {};
|
||||
|
||||
/**
|
||||
* Properties:
|
||||
* - actual_mode: always "view", "edit" or "create". Read-only property. Determines
|
||||
* the mode used by the view.
|
||||
*/
|
||||
var FormView = View.extend(common.FieldManagerMixin, {
|
||||
/**
|
||||
* Indicates that this view is not searchable, and thus that no search
|
||||
* view should be displayed (if there is one active).
|
||||
*/
|
||||
accesskey: "F",
|
||||
defaults: _.extend({}, View.prototype.defaults, {
|
||||
not_interactible_on_create: false,
|
||||
initial_mode: "view",
|
||||
disable_autofocus: false,
|
||||
footer_to_buttons: false,
|
||||
}),
|
||||
display_name: _lt('Form'),
|
||||
icon: 'fa-edit',
|
||||
multi_record: false,
|
||||
// Indicates that this view is not searchable, and thus that no search view should be displayed.
|
||||
searchable: false,
|
||||
template: "FormView",
|
||||
display_name: _lt('Form'),
|
||||
view_type: "form",
|
||||
multi_record: false,
|
||||
accesskey: "F",
|
||||
icon: 'fa-edit',
|
||||
/**
|
||||
* @constructs instance.web.FormView
|
||||
* @extends instance.web.View
|
||||
*
|
||||
* @param {instance.web.Session} session the current openerp session
|
||||
* @param {instance.web.DataSet} dataset the dataset this view will work with
|
||||
* @param {String} view_id the identifier of the OpenERP view object
|
||||
*
|
||||
* @property {instance.web.Registry} registry=instance.web.form.widgets widgets registry for this form view instance
|
||||
*/
|
||||
init: function(parent, dataset, view_id, options) {
|
||||
|
||||
init: function() {
|
||||
var self = this;
|
||||
this._super(parent);
|
||||
this.ViewManager = parent;
|
||||
this.set_default_options(options);
|
||||
this.dataset = dataset;
|
||||
this.model = dataset.model;
|
||||
this.view_id = view_id || false;
|
||||
this.fields_view = {};
|
||||
this._super.apply(this, arguments);
|
||||
this.fields = {};
|
||||
this.fields_order = [];
|
||||
this.datarecord = {};
|
||||
@@ -67,12 +51,6 @@ var FormView = View.extend(common.FieldManagerMixin, {
|
||||
this.widgets_registry = core.form_custom_registry;
|
||||
this.has_been_loaded = $.Deferred();
|
||||
this.translatable_fields = [];
|
||||
_.defaults(this.options, {
|
||||
"not_interactible_on_create": false,
|
||||
"initial_mode": "view",
|
||||
"disable_autofocus": false,
|
||||
"footer_to_buttons": false,
|
||||
});
|
||||
this.is_initialized = $.Deferred();
|
||||
this.mutating_mutex = new utils.Mutex();
|
||||
this.save_list = [];
|
||||
@@ -104,34 +82,13 @@ var FormView = View.extend(common.FieldManagerMixin, {
|
||||
});
|
||||
});
|
||||
},
|
||||
view_loading: function(r) {
|
||||
return this.load_form(r);
|
||||
},
|
||||
destroy: function() {
|
||||
_.each(this.get_widgets(), function(w) {
|
||||
w.off('focused blurred');
|
||||
w.destroy();
|
||||
});
|
||||
if (this.$el) {
|
||||
this.$el.off('.formBlur');
|
||||
}
|
||||
this._super();
|
||||
},
|
||||
load_form: function(data) {
|
||||
start: function() {
|
||||
var self = this;
|
||||
if (!data) {
|
||||
throw new Error(_t("No data provided."));
|
||||
}
|
||||
if (this.arch) {
|
||||
throw "Form view does not support multiple calls to load_form";
|
||||
}
|
||||
this.fields_order = [];
|
||||
this.fields_view = data;
|
||||
|
||||
this.rendering_engine.set_fields_registry(this.fields_registry);
|
||||
this.rendering_engine.set_tags_registry(this.tags_registry);
|
||||
this.rendering_engine.set_widgets_registry(this.widgets_registry);
|
||||
this.rendering_engine.set_fields_view(data);
|
||||
this.rendering_engine.set_fields_view(this.fields_view);
|
||||
var $dest = this.$el.hasClass("oe_form_container") ? this.$el : this.$el.find('.oe_form_container');
|
||||
this.rendering_engine.render_to($dest);
|
||||
|
||||
@@ -143,7 +100,7 @@ var FormView = View.extend(common.FieldManagerMixin, {
|
||||
|
||||
// Add bounce effect on button 'Edit' when click on readonly page view.
|
||||
this.$el.find(".oe_form_group_row,.oe_form_field,label,h1,.oe_title,.oe_notebook_page, .oe_list_content").on('click', function (e) {
|
||||
if(self.get("actual_mode") == "view" && self.$buttons && !$(e.target).is('[data-toggle]')) {
|
||||
if(self.get("actual_mode") === "view" && self.$buttons && !$(e.target).is('[data-toggle]')) {
|
||||
var $button = self.$buttons.find(".oe_form_button_edit");
|
||||
$button.openerpBounce();
|
||||
e.stopPropagation();
|
||||
@@ -152,14 +109,23 @@ var FormView = View.extend(common.FieldManagerMixin, {
|
||||
});
|
||||
//bounce effect on red button when click on statusbar.
|
||||
this.$el.find(".oe_form_field_status:not(.oe_form_status_clickable)").on('click', function (e) {
|
||||
if((self.get("actual_mode") == "view")) {
|
||||
if((self.get("actual_mode") === "view")) {
|
||||
var $button = self.$el.find(".oe_highlight:not(.o_form_invisible)").css({'float':'left','clear':'none'});
|
||||
$button.openerpBounce();
|
||||
e.stopPropagation();
|
||||
}
|
||||
});
|
||||
this.trigger('form_view_loaded', data);
|
||||
return $.when();
|
||||
return this._super();
|
||||
},
|
||||
destroy: function() {
|
||||
_.each(this.get_widgets(), function(w) {
|
||||
w.off('focused blurred');
|
||||
w.destroy();
|
||||
});
|
||||
if (this.$el) {
|
||||
this.$el.off('.formBlur');
|
||||
}
|
||||
this._super();
|
||||
},
|
||||
/**
|
||||
* Render the buttons according to the FormView.buttons template and add listeners on it.
|
||||
@@ -181,14 +147,10 @@ var FormView = View.extend(common.FieldManagerMixin, {
|
||||
|
||||
// Show or hide the buttons according to the view mode
|
||||
this.toggle_buttons();
|
||||
this.$buttons.on('click', '.oe_form_button_create',
|
||||
this.guard_active(this.on_button_create));
|
||||
this.$buttons.on('click', '.oe_form_button_edit',
|
||||
this.guard_active(this.on_button_edit));
|
||||
this.$buttons.on('click', '.oe_form_button_save',
|
||||
this.guard_active(this.on_button_save));
|
||||
this.$buttons.on('click', '.oe_form_button_cancel',
|
||||
this.guard_active(this.on_button_cancel));
|
||||
this.$buttons.on('click', '.oe_form_button_create', this.on_button_create);
|
||||
this.$buttons.on('click', '.oe_form_button_edit', this.on_button_edit);
|
||||
this.$buttons.on('click', '.oe_form_button_save', this.on_button_save);
|
||||
this.$buttons.on('click', '.oe_form_button_cancel', this.on_button_cancel);
|
||||
|
||||
if ($node) {
|
||||
this.$buttons.appendTo($node);
|
||||
@@ -311,10 +273,8 @@ var FormView = View.extend(common.FieldManagerMixin, {
|
||||
}
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @param {Object} [options]
|
||||
* @param {Boolean} [mode=undefined] If specified, switch the form to specified mode. Can be "edit" or "view".
|
||||
* @param {Boolean} [reload=true] whether the form should reload its content on show, or use the currently loaded record
|
||||
* @param {Boolean} [options.mode=undefined] If specified, switch the form to specified mode. Can be "edit" or "view".
|
||||
* @param {Boolean} [options.reload=true] whether the form should reload its content on show, or use the currently loaded record
|
||||
* @return {$.Deferred}
|
||||
*/
|
||||
do_show: function (options) {
|
||||
@@ -521,7 +481,7 @@ var FormView = View.extend(common.FieldManagerMixin, {
|
||||
// In case of a o2m virtual id, we should pass an empty ids list
|
||||
ids.push(self.datarecord.id);
|
||||
}
|
||||
def = self.alive(new Model(self.dataset.model).call(
|
||||
def = self.alive(self.dataset.call(
|
||||
"onchange", [ids, values, trigger_field_name, onchange_specs, context]));
|
||||
}
|
||||
this.onchanges_mutex.exec(function(){
|
||||
@@ -1199,7 +1159,7 @@ var FormView = View.extend(common.FieldManagerMixin, {
|
||||
all_users = d.$el.find('#formview_default_all').is(':checked');
|
||||
new data.DataSet(self, 'ir.values').call(
|
||||
'set_default', [
|
||||
self.dataset.model,
|
||||
self.model,
|
||||
field_to_set,
|
||||
self.fields[field_to_set].get_value(),
|
||||
all_users,
|
||||
|
||||
@@ -6,7 +6,6 @@ odoo.define('web.GraphView', function (require) {
|
||||
|
||||
var core = require('web.core');
|
||||
var GraphWidget = require('web.GraphWidget');
|
||||
var Model = require('web.DataModel');
|
||||
var View = require('web.View');
|
||||
|
||||
var _lt = core._lt;
|
||||
@@ -17,21 +16,30 @@ var GraphView = View.extend({
|
||||
className: 'oe_graph',
|
||||
display_name: _lt('Graph'),
|
||||
icon: 'fa-bar-chart',
|
||||
view_type: 'graph',
|
||||
require_fields: true,
|
||||
|
||||
init: function(parent, dataset, view_id, options) {
|
||||
this._super(parent, dataset, view_id, options);
|
||||
init: function () {
|
||||
this._super.apply(this, arguments);
|
||||
|
||||
this.model = new Model(dataset.model, {group_by_no_leaf: true});
|
||||
this.measures = [];
|
||||
this.active_measure = '__count__';
|
||||
this.initial_groupbys = [];
|
||||
this.widget = undefined;
|
||||
},
|
||||
start: function () {
|
||||
var load_fields = this.model.call('fields_get', [], {context: this.dataset.get_context()})
|
||||
.then(this.prepare_fields.bind(this));
|
||||
|
||||
willStart: function () {
|
||||
var self = this;
|
||||
var load_fields = this.ViewManager.get_fields().then(this.prepare_fields.bind(this));
|
||||
this.fields_view.arch.children.forEach(function (field) {
|
||||
var name = field.attrs.name;
|
||||
if (field.attrs.interval) {
|
||||
name += ':' + field.attrs.interval;
|
||||
}
|
||||
if (field.attrs.type === 'measure') {
|
||||
self.active_measure = name;
|
||||
} else {
|
||||
self.initial_groupbys.push(name);
|
||||
}
|
||||
});
|
||||
return $.when(this._super(), load_fields);
|
||||
},
|
||||
/**
|
||||
@@ -59,20 +67,6 @@ var GraphView = View.extend({
|
||||
$(li).toggleClass('selected', $(li).data('field') === self.active_measure);
|
||||
});
|
||||
},
|
||||
view_loading: function (fvg) {
|
||||
var self = this;
|
||||
fvg.arch.children.forEach(function (field) {
|
||||
var name = field.attrs.name;
|
||||
if (field.attrs.interval) {
|
||||
name += ':' + field.attrs.interval;
|
||||
}
|
||||
if (field.attrs.type === 'measure') {
|
||||
self.active_measure = name;
|
||||
} else {
|
||||
self.initial_groupbys.push(name);
|
||||
}
|
||||
});
|
||||
},
|
||||
do_show: function () {
|
||||
this.do_push_state({});
|
||||
return this._super();
|
||||
@@ -92,7 +86,7 @@ var GraphView = View.extend({
|
||||
do_search: function (domain, context, group_by) {
|
||||
if (!this.widget) {
|
||||
this.initial_groupbys = context.graph_groupbys || this.initial_groupbys;
|
||||
this.widget = new GraphWidget(this, this.dataset.model, {
|
||||
this.widget = new GraphWidget(this, this.model, {
|
||||
measure: context.graph_measure || this.active_measure,
|
||||
mode: context.graph_mode || this.active_mode,
|
||||
domain: domain,
|
||||
|
||||
@@ -32,33 +32,33 @@ var row_decoration = [
|
||||
'decoration-warning'
|
||||
];
|
||||
|
||||
var ListView = View.extend( /** @lends instance.web.ListView# */ {
|
||||
var ListView = View.extend({
|
||||
_template: 'ListView',
|
||||
accesskey: 'L',
|
||||
icon: 'fa-list-ul',
|
||||
display_name: _lt('List'),
|
||||
defaults: {
|
||||
defaults: _.extend({}, View.prototype.defaults, {
|
||||
// records can be selected one by one
|
||||
'selectable': true,
|
||||
selectable: true,
|
||||
// list rows can be deleted
|
||||
'deletable': false,
|
||||
deletable: false,
|
||||
// whether the column headers should be displayed
|
||||
'header': true,
|
||||
header: true,
|
||||
// display addition button, with that label
|
||||
'addable': _lt("Create"),
|
||||
addable: _lt("Create"),
|
||||
// whether the list view can be sorted, note that once a view has been
|
||||
// sorted it can not be reordered anymore
|
||||
'sortable': true,
|
||||
sortable: true,
|
||||
// whether the view rows can be reordered (via vertical drag & drop)
|
||||
'reorderable': true,
|
||||
'action_buttons': true,
|
||||
reorderable: true,
|
||||
action_buttons: true,
|
||||
//whether the editable property of the view has to be disabled
|
||||
'disable_editable_mode': false,
|
||||
},
|
||||
view_type: 'tree',
|
||||
disable_editable_mode: false,
|
||||
}),
|
||||
events: {
|
||||
'click thead th.oe_sortable[data-id]': 'sort_by_column'
|
||||
},
|
||||
icon: 'fa-list-ul',
|
||||
|
||||
/**
|
||||
* Core class for list-type displays.
|
||||
*
|
||||
@@ -69,11 +69,11 @@ var ListView = View.extend( /** @lends instance.web.ListView# */ {
|
||||
* See constructor parameters and method documentations for information on
|
||||
* the default behaviors and possible options for the list view.
|
||||
*
|
||||
* @constructs instance.web.ListView
|
||||
* @extends instance.web.View
|
||||
* @constructs ListView
|
||||
* @extends View
|
||||
*
|
||||
* @param parent parent object
|
||||
* @param {instance.web.DataSet} dataset the dataset the view should work with
|
||||
* @param {DataSet} dataset the dataset the view should work with
|
||||
* @param {String} view_id the listview's identifier, if any
|
||||
* @param {Object} options A set of options used to configure the view
|
||||
* @param {Boolean} [options.selectable=true] determines whether view rows are selectable (e.g. via a checkbox)
|
||||
@@ -83,13 +83,14 @@ var ListView = View.extend( /** @lends instance.web.ListView# */ {
|
||||
* @param {Boolean} [options.sortable=true] is it possible to sort the table by clicking on column headers
|
||||
* @param {Boolean} [options.reorderable=true] is it possible to reorder list rows
|
||||
*/
|
||||
init: function(parent, dataset, view_id, options) {
|
||||
init: function() {
|
||||
var self = this;
|
||||
this._super(parent);
|
||||
this.set_default_options(_.extend({}, this.defaults, options || {}));
|
||||
this.dataset = dataset;
|
||||
this.model = dataset.model;
|
||||
this.view_id = view_id;
|
||||
this._super.apply(this, arguments);
|
||||
this.options = _.defaults(this.options, {
|
||||
GroupsType: ListView.Groups,
|
||||
ListType: ListView.List,
|
||||
});
|
||||
|
||||
this.previous_colspan = null;
|
||||
this.colors = null;
|
||||
this.fonts = null;
|
||||
@@ -105,8 +106,8 @@ var ListView = View.extend( /** @lends instance.web.ListView# */ {
|
||||
} else {
|
||||
this.groups.datagroup = new DataGroup(
|
||||
this, this.model,
|
||||
dataset.get_domain(),
|
||||
dataset.get_context());
|
||||
this.dataset.get_domain(),
|
||||
this.dataset.get_context());
|
||||
this.groups.datagroup.sort = this.dataset._sort;
|
||||
}
|
||||
|
||||
@@ -125,21 +126,22 @@ var ListView = View.extend( /** @lends instance.web.ListView# */ {
|
||||
if (!this.options.$pager || !this.options.$pager.length) {
|
||||
this.options.$pager = false;
|
||||
}
|
||||
|
||||
this.name = "" + this.fields_view.arch.attrs.string;
|
||||
|
||||
// the view's number of records per page (|| section)
|
||||
this._limit = (this.options.limit ||
|
||||
this.defaults.limit ||
|
||||
(this.getParent().action || {}).limit ||
|
||||
parseInt(this.fields_view.arch.attrs.limit, 10) ||
|
||||
80);
|
||||
},
|
||||
view_loading: function(r) {
|
||||
return this.load_list(r);
|
||||
},
|
||||
set_default_options: function (options) {
|
||||
this._super(options);
|
||||
_.defaults(this.options, {
|
||||
GroupsType: ListView.Groups,
|
||||
ListType: ListView.List
|
||||
});
|
||||
|
||||
//Sort
|
||||
var default_order = this.fields_view.arch.attrs.default_order,
|
||||
unsorted = !this.dataset._sort.length;
|
||||
if (unsorted && default_order && !this.grouped) {
|
||||
this.dataset.set_sort(default_order.split(','));
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Set a custom Group construct as the root of the List View.
|
||||
@@ -169,14 +171,41 @@ var ListView = View.extend( /** @lends instance.web.ListView# */ {
|
||||
}
|
||||
});
|
||||
},
|
||||
/**
|
||||
* View startup method, the default behavior is to set the ``oe_list``
|
||||
* class on its root element and to perform an RPC load call.
|
||||
*
|
||||
* @returns {$.Deferred} loading promise
|
||||
*/
|
||||
willStart: function() {
|
||||
// Retrieve the decoration defined on the model's list view
|
||||
this.decoration = _.pick(this.fields_view.arch.attrs, function(value, key) {
|
||||
return row_decoration.indexOf(key) >= 0;
|
||||
});
|
||||
this.decoration = _.mapObject(this.decoration, function(value) {
|
||||
return py.parse(py.tokenize(value));
|
||||
});
|
||||
|
||||
this.setup_columns(this.fields_view.fields, this.grouped);
|
||||
|
||||
return this._super();
|
||||
},
|
||||
start: function() {
|
||||
var self = this;
|
||||
this.$el.html(QWeb.render(this._template, this));
|
||||
this.$el.addClass(this.fields_view.arch.attrs['class']);
|
||||
this.$el.addClass('oe_list o_list_view');
|
||||
|
||||
// Selecting records
|
||||
this.$('.oe_list_record_selector').click(function() {
|
||||
var checked = self.$('.oe_list_record_selector').prop('checked');
|
||||
self.$('.oe_list_record_selector input').prop('checked', checked || false);
|
||||
var selection = self.groups.get_selection();
|
||||
$(self.groups).trigger('selected', [selection.ids, selection.records]);
|
||||
});
|
||||
|
||||
if(this.dataset._sort.length){
|
||||
if(this.dataset._sort[0].indexOf('-') === -1){
|
||||
this.$el.find('th[data-id=' + this.dataset._sort[0] + ']').addClass("sortdown");
|
||||
}else {
|
||||
this.$el.find('th[data-id=' + this.dataset._sort[0].split('-')[1] + ']').addClass("sortup");
|
||||
}
|
||||
}
|
||||
this.trigger('list_view_loaded', data, this.grouped);
|
||||
return this._super();
|
||||
},
|
||||
/**
|
||||
@@ -201,73 +230,6 @@ var ListView = View.extend( /** @lends instance.web.ListView# */ {
|
||||
});
|
||||
return classnames;
|
||||
},
|
||||
/**
|
||||
* Called after loading the list view's description, sets up such things
|
||||
* as the view table's columns, renders the table itself and hooks up the
|
||||
* various table-level and row-level DOM events (action buttons, deletion
|
||||
* buttons, selection of records, [New] button, selection of a given
|
||||
* record, ...)
|
||||
*
|
||||
* Sets up the following:
|
||||
*
|
||||
* * Processes arch and fields to generate a complete field descriptor for each field
|
||||
* * Create the table itself and allocate visible columns
|
||||
* * Hook in the top-level (header) [New|Add] and [Delete] button
|
||||
* * Sets up showing/hiding the top-level [Delete] button based on records being selected or not
|
||||
* * Sets up event handlers for action buttons and per-row deletion button
|
||||
* * Hooks global callback for clicking on a row
|
||||
*
|
||||
* @param {Object} data wrapped fields_view_get result
|
||||
* @param {Object} data.fields_view fields_view_get result (processed)
|
||||
* @param {Object} data.fields_view.fields mapping of fields for the current model
|
||||
* @param {Object} data.fields_view.arch current list view descriptor
|
||||
* @param {Boolean} grouped Is the list view grouped
|
||||
*/
|
||||
load_list: function(data) {
|
||||
var self = this;
|
||||
this.fields_view = data;
|
||||
this.name = "" + this.fields_view.arch.attrs.string;
|
||||
this._limit = parseInt(this.fields_view.arch.attrs.limit, 10) || this._limit;
|
||||
|
||||
// Retrieve the decoration defined on the model's list view
|
||||
this.decoration = _.pick(this.fields_view.arch.attrs, function(value, key) {
|
||||
return row_decoration.indexOf(key) >= 0;
|
||||
});
|
||||
this.decoration = _.mapObject(this.decoration, function(value) {
|
||||
return py.parse(py.tokenize(value));
|
||||
});
|
||||
|
||||
this.setup_columns(this.fields_view.fields, this.grouped);
|
||||
|
||||
this.$el.html(QWeb.render(this._template, this));
|
||||
this.$el.addClass(this.fields_view.arch.attrs['class']);
|
||||
|
||||
// Head hook
|
||||
// Selecting records
|
||||
this.$el.find('.oe_list_record_selector').click(function(){
|
||||
self.$el.find('.oe_list_record_selector input').prop('checked',
|
||||
self.$el.find('.oe_list_record_selector').prop('checked') || false);
|
||||
var selection = self.groups.get_selection();
|
||||
$(self.groups).trigger(
|
||||
'selected', [selection.ids, selection.records]);
|
||||
});
|
||||
|
||||
//Sort
|
||||
var default_order = this.fields_view.arch.attrs.default_order,
|
||||
unsorted = !this.dataset._sort.length;
|
||||
if (unsorted && default_order && !this.grouped) {
|
||||
this.dataset.set_sort(default_order.split(','));
|
||||
}
|
||||
|
||||
if(this.dataset._sort.length){
|
||||
if(this.dataset._sort[0].indexOf('-') == -1){
|
||||
this.$el.find('th[data-id=' + this.dataset._sort[0] + ']').addClass("sortdown");
|
||||
}else {
|
||||
this.$el.find('th[data-id=' + this.dataset._sort[0].split('-')[1] + ']').addClass("sortup");
|
||||
}
|
||||
}
|
||||
this.trigger('list_view_loaded', data, this.grouped);
|
||||
},
|
||||
/**
|
||||
* Render the buttons according to the ListView.buttons template and
|
||||
* add listeners on it.
|
||||
@@ -499,16 +461,6 @@ var ListView = View.extend( /** @lends instance.web.ListView# */ {
|
||||
this.sidebar.do_hide();
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Reloads the list view based on the current settings (dataset & al)
|
||||
*
|
||||
* @deprecated
|
||||
* @param {Boolean} [grouped] Should the list be displayed grouped
|
||||
* @param {Object} [context] context to send the server while loading the view
|
||||
*/
|
||||
reload_view: function (grouped, context) {
|
||||
return this.load_view(context);
|
||||
},
|
||||
/**
|
||||
* re-renders the content of the list view
|
||||
*
|
||||
@@ -617,8 +569,7 @@ var ListView = View.extend( /** @lends instance.web.ListView# */ {
|
||||
this.no_leaf = !!context['group_by_no_leaf'];
|
||||
this.grouped = !!group_by;
|
||||
|
||||
return this.alive(this.load_view(context)).then(
|
||||
this.proxy('reload_content'));
|
||||
return this.reload_content();
|
||||
},
|
||||
/**
|
||||
* Handles the signal to delete lines from the records list
|
||||
@@ -720,7 +671,7 @@ var ListView = View.extend( /** @lends instance.web.ListView# */ {
|
||||
c.set_eval_context(_.extend({
|
||||
active_id: id,
|
||||
active_ids: [id],
|
||||
active_model: this.dataset.model
|
||||
active_model: this.model
|
||||
}, this.records.get(id).toContext()));
|
||||
if (action.context) {
|
||||
c.add(action.context);
|
||||
@@ -960,7 +911,7 @@ var ListView = View.extend( /** @lends instance.web.ListView# */ {
|
||||
});
|
||||
core.view_registry.add('list', ListView);
|
||||
|
||||
ListView.List = Class.extend( /** @lends instance.web.ListView.List# */{
|
||||
ListView.List = Class.extend({
|
||||
/**
|
||||
* List display for the ListView, handles basic DOM events and transforms
|
||||
* them in the relevant higher-level events, to which the list view (or
|
||||
@@ -1187,7 +1138,7 @@ ListView.List = Class.extend( /** @lends instance.web.ListView.List# */{
|
||||
}
|
||||
}
|
||||
return column.format(record.toForm().data, {
|
||||
model: this.dataset.model,
|
||||
model: this.model,
|
||||
id: record.get('id')
|
||||
});
|
||||
},
|
||||
@@ -1299,7 +1250,7 @@ ListView.List = Class.extend( /** @lends instance.web.ListView.List# */{
|
||||
}
|
||||
});
|
||||
|
||||
ListView.Groups = Class.extend( /** @lends instance.web.ListView.Groups# */{
|
||||
ListView.Groups = Class.extend({
|
||||
passthrough_events: 'action deleted row_link',
|
||||
/**
|
||||
* Grouped display for the ListView. Handles basic DOM events and interacts
|
||||
|
||||
@@ -38,19 +38,18 @@ var Editor = Widget.extend({
|
||||
delegate: this.getParent(),
|
||||
});
|
||||
this.delegate = this.options.delegate;
|
||||
|
||||
this.record = null;
|
||||
this.form = new (this.options.formView)(this, this.delegate.dataset, false, {
|
||||
},
|
||||
start: function () {
|
||||
var self = this;
|
||||
var form_fields_view = this._validate_view(this.delegate.edition_view(this));
|
||||
this.form = new (this.options.formView)(this, this.delegate.dataset, form_fields_view, {
|
||||
initial_mode: 'edit',
|
||||
is_list_editable: true,
|
||||
disable_autofocus: true,
|
||||
$buttons: $(),
|
||||
$pager: $(),
|
||||
});
|
||||
},
|
||||
start: function () {
|
||||
var self = this;
|
||||
this.form.embedded_view = this._validate_view(this.delegate.edition_view(this));
|
||||
return $.when(this._super(), this.form.appendTo($('<div/>')).then(function() {
|
||||
self.form.$el.addClass(self.$el.attr('class'));
|
||||
self.replaceElement(self.form.$el);
|
||||
@@ -258,7 +257,7 @@ ListView.include(/** @lends instance.web.ListView# */{
|
||||
this._super.apply(this, arguments);
|
||||
}
|
||||
},
|
||||
load_list: function (data, grouped) {
|
||||
start: function () {
|
||||
// tree/@editable takes priority on everything else if present.
|
||||
var result = this._super.apply(this, arguments);
|
||||
|
||||
|
||||
@@ -19,10 +19,6 @@ var _t = core._t;
|
||||
var QWeb = core.qweb;
|
||||
|
||||
var PivotView = View.extend({
|
||||
template: 'PivotView',
|
||||
icon: 'fa-table',
|
||||
display_name: _lt('Pivot'),
|
||||
view_type: 'pivot',
|
||||
events: {
|
||||
'click .oe-opened': 'on_open_header_click',
|
||||
'click .oe-closed': 'on_closed_header_click',
|
||||
@@ -30,17 +26,20 @@ var PivotView = View.extend({
|
||||
'click td': 'on_cell_click',
|
||||
'click .measure-row': 'on_measure_row_click',
|
||||
},
|
||||
display_name: _lt('Pivot'),
|
||||
icon: 'fa-table',
|
||||
require_fields: true,
|
||||
template: 'PivotView',
|
||||
|
||||
init: function(parent, dataset, view_id, options) {
|
||||
this._super(parent, dataset, view_id, options);
|
||||
this.model = new Model(dataset.model, {group_by_no_leaf: true});
|
||||
init: function() {
|
||||
this._super.apply(this, arguments);
|
||||
this._model = new Model(this.model, {group_by_no_leaf: true});
|
||||
|
||||
this.fields = {};
|
||||
this.measures = {};
|
||||
this.groupable_fields = {};
|
||||
this.ready = false; // will be ready after the first do_search
|
||||
this.data_loaded = $.Deferred();
|
||||
this.title = options.title;
|
||||
this.title = this.options.title || this.fields_view.arch.attrs.string;
|
||||
|
||||
this.main_row = {
|
||||
root: undefined,
|
||||
@@ -63,30 +62,72 @@ var PivotView = View.extend({
|
||||
|
||||
this.numbering = {};
|
||||
this.widgets = [];
|
||||
|
||||
this.enable_linking = !this.fields_view.arch.attrs.disable_linking;
|
||||
},
|
||||
willStart: function () {
|
||||
var self = this;
|
||||
return session.rpc('/web/pivot/check_xlwt').then(function(result) {
|
||||
|
||||
var load_fields = this.ViewManager.get_fields();
|
||||
var load_xlwt = session.rpc('/web/pivot/check_xlwt').then(function(result) {
|
||||
self.xlwt_installed = result;
|
||||
});
|
||||
|
||||
this.fields_view.arch.children.forEach(function (field) {
|
||||
var name = field.attrs.name;
|
||||
if (field.attrs.interval) {
|
||||
name += ':' + field.attrs.interval;
|
||||
}
|
||||
//noinspection FallThroughInSwitchStatementJS
|
||||
switch (field.attrs.type) {
|
||||
case 'measure':
|
||||
self.widgets.push(field.attrs.widget || "");
|
||||
self.active_measures.push(name);
|
||||
break;
|
||||
case 'col':
|
||||
self.initial_col_groupby.push(name);
|
||||
break;
|
||||
default:
|
||||
if ('operator' in field.attrs) {
|
||||
self.active_measures.push(name);
|
||||
break;
|
||||
}
|
||||
case 'row':
|
||||
self.initial_row_groupby.push(name);
|
||||
}
|
||||
});
|
||||
if ((!this.active_measures.length) || this.fields_view.arch.attrs.display_quantity) {
|
||||
this.active_measures.push('__count__');
|
||||
}
|
||||
return $.when(load_fields, load_xlwt, this._super()).then(function (fields) {
|
||||
self.prepare_fields(fields);
|
||||
// add active measures to the measure list. This is very rarely necessary, but it
|
||||
// can be useful if one is working with a functional field non stored, but in a
|
||||
// model with an overrided read_group method. In this case, the pivot view could
|
||||
// work, and the measure should be allowed. However, be careful if you define a
|
||||
// measure in your pivot view: non stored functional fields will probably not work
|
||||
// (their aggregate will always be 0).
|
||||
_.each(self.active_measures, function (m) {
|
||||
if (!(m in self.measures)) {
|
||||
self.measures[m] = self.fields[m];
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
start: function () {
|
||||
this.$table_container = this.$('.o-pivot-table');
|
||||
|
||||
var load_fields = this.model.call('fields_get', [], {context: this.dataset.get_context()})
|
||||
.then(this.prepare_fields.bind(this));
|
||||
|
||||
return $.when(this._super(), load_fields).then(this.render_field_selection.bind(this));
|
||||
},
|
||||
render_field_selection: function () {
|
||||
var self = this;
|
||||
|
||||
var context = {fields: _.chain(this.groupable_fields).pairs().sortBy(function(f){return f[1].string;}).value()};
|
||||
this.$el.toggleClass('oe_enable_linking', this.enable_linking);
|
||||
var context = {
|
||||
fields: _.chain(this.groupable_fields).pairs().sortBy(function(f) {
|
||||
return f[1].string;
|
||||
}).value(),
|
||||
};
|
||||
this.$field_selection = this.$('.o-field-selection');
|
||||
this.$field_selection.html(QWeb.render('PivotView.FieldSelection', context));
|
||||
core.bus.on('click', self, function () {
|
||||
self.$field_selection.find('ul').first().hide();
|
||||
core.bus.on('click', this, function () {
|
||||
this.$field_selection.find('ul').first().hide();
|
||||
});
|
||||
return this._super();
|
||||
},
|
||||
/**
|
||||
* Render the buttons according to the PivotView.buttons template and
|
||||
@@ -127,49 +168,6 @@ var PivotView = View.extend({
|
||||
this.sidebar.appendTo($node);
|
||||
}
|
||||
},
|
||||
view_loading: function (fvg) {
|
||||
var self = this;
|
||||
this.title = this.title || fvg.arch.attrs.string;
|
||||
this.enable_linking = !fvg.arch.attrs.disable_linking;
|
||||
this.$el.toggleClass('oe-enable-linking', this.enable_linking);
|
||||
fvg.arch.children.forEach(function (field) {
|
||||
var name = field.attrs.name;
|
||||
if (field.attrs.interval) {
|
||||
name += ':' + field.attrs.interval;
|
||||
}
|
||||
//noinspection FallThroughInSwitchStatementJS
|
||||
switch (field.attrs.type) {
|
||||
case 'measure':
|
||||
self.widgets.push(field.attrs.widget || "");
|
||||
self.active_measures.push(name);
|
||||
break;
|
||||
case 'col':
|
||||
self.initial_col_groupby.push(name);
|
||||
break;
|
||||
default:
|
||||
if ('operator' in field.attrs) {
|
||||
self.active_measures.push(name);
|
||||
break;
|
||||
}
|
||||
case 'row':
|
||||
self.initial_row_groupby.push(name);
|
||||
}
|
||||
});
|
||||
if ((!self.active_measures.length) || fvg.arch.attrs.display_quantity){
|
||||
self.active_measures.push('__count__');
|
||||
}
|
||||
// add active measures to the measure list. This is very rarely necessary, but it
|
||||
// can be useful if one is working with a functional field non stored, but in a
|
||||
// model with an overrided read_group method. In this case, the pivot view could
|
||||
// work, and the measure should be allowed. However, be careful if you define a
|
||||
// measure in your pivot view: non stored functional fields will probably not work
|
||||
// (their aggregate will always be 0).
|
||||
_.each(this.active_measures, function (m) {
|
||||
if (!(m in self.measures)) {
|
||||
self.measures[m] = self.fields[m];
|
||||
}
|
||||
});
|
||||
},
|
||||
prepare_fields: function (fields) {
|
||||
var self = this,
|
||||
groupable_types = ['many2one', 'char', 'boolean',
|
||||
@@ -277,15 +275,14 @@ var PivotView = View.extend({
|
||||
col_domain = this.headers[col_id].domain,
|
||||
context = _.omit(_.clone(this.context), 'group_by');
|
||||
|
||||
var views = [
|
||||
[this.options.action_views_ids.list || false, 'list'],
|
||||
[this.options.action_views_ids.form || false, 'form']
|
||||
];
|
||||
var views = _.filter(this.options.action.views, function (view) {
|
||||
return view[1] === 'form' || view[1] === 'list';
|
||||
});
|
||||
|
||||
return this.do_action({
|
||||
type: 'ir.actions.act_window',
|
||||
name: this.title,
|
||||
res_model: this.model.name,
|
||||
res_model: this.model,
|
||||
views: views,
|
||||
view_type : "list",
|
||||
view_mode : "list",
|
||||
@@ -345,7 +342,7 @@ var PivotView = View.extend({
|
||||
groupbys.push([field].concat(other_groupbys.slice(0,i)));
|
||||
}
|
||||
return $.when.apply(null, groupbys.map(function (groupby) {
|
||||
return self.model.query(fields)
|
||||
return self._model.query(fields)
|
||||
.filter(header.domain.length ? header.domain : self.domain)
|
||||
.context(self.context)
|
||||
.lazy(false)
|
||||
@@ -398,7 +395,7 @@ var PivotView = View.extend({
|
||||
}
|
||||
}
|
||||
return $.when.apply(null, groupbys.map(function (groupby) {
|
||||
return self.model.query(fields)
|
||||
return self._model.query(fields)
|
||||
.filter(self.domain)
|
||||
.context(self.context)
|
||||
.lazy(false)
|
||||
|
||||
@@ -26,18 +26,34 @@ return Widget.extend({
|
||||
}
|
||||
},
|
||||
},
|
||||
init: function (parent, query, target_model, action_id) {
|
||||
init: function (parent, query, target_model, action_id, filters) {
|
||||
this._super.apply(this,arguments);
|
||||
this.searchview = parent;
|
||||
this.query = query;
|
||||
this.target_model = target_model;
|
||||
this.model = new Model('ir.filters');
|
||||
this.filters = {};
|
||||
this.$filters = {};
|
||||
this.action_id = action_id;
|
||||
if (filters) {
|
||||
this.filters = {};
|
||||
_.each(filters, this.add_filter.bind(this));
|
||||
}
|
||||
},
|
||||
willStart: function () {
|
||||
var self = this;
|
||||
var def;
|
||||
if (!this.filters) {
|
||||
def = this.model.call('get_filters', [this.target_model, this.action_id], {
|
||||
context: this.searchview.dataset.context,
|
||||
}).then(function (filters) {
|
||||
self.filters = {};
|
||||
_.each(filters, self.add_filter.bind(self));
|
||||
});
|
||||
}
|
||||
return $.when(def, this._super());
|
||||
},
|
||||
start: function () {
|
||||
var self = this;
|
||||
this.$filters = {};
|
||||
this.$save_search = this.$('.oe-save-search');
|
||||
this.$save_name = this.$('.oe-save-name');
|
||||
this.$inputs = this.$save_name.find('input');
|
||||
@@ -55,12 +71,10 @@ return Widget.extend({
|
||||
}
|
||||
})
|
||||
.on('reset', this.proxy('clear_selection'));
|
||||
return this.model.call('get_filters', [this.target_model, this.action_id],
|
||||
{context: this.searchview.dataset.context})
|
||||
.done(this.proxy('prepare_dropdown_menu'));
|
||||
},
|
||||
prepare_dropdown_menu: function (filters) {
|
||||
filters.map(this.append_filter.bind(this));
|
||||
|
||||
_.each(this.filters, this.append_filter.bind(this));
|
||||
|
||||
return this._super();
|
||||
},
|
||||
toggle_save_menu: function (is_open) {
|
||||
this.$save_search
|
||||
@@ -115,7 +129,7 @@ return Widget.extend({
|
||||
var filter = {
|
||||
name: filter_name,
|
||||
user_id: shared_filter ? false : session.uid,
|
||||
model_id: this.searchview.dataset.model,
|
||||
model_id: this.target_model,
|
||||
context: results.context,
|
||||
domain: results.domain,
|
||||
sort: JSON.stringify(this.searchview.dataset._sort),
|
||||
@@ -126,6 +140,7 @@ return Widget.extend({
|
||||
filter.id = id;
|
||||
self.toggle_save_menu(false);
|
||||
self.$save_name.find('input').val('').prop('checked', false);
|
||||
self.add_filter(filter);
|
||||
self.append_filter(filter);
|
||||
self.toggle_filter(filter, true);
|
||||
});
|
||||
@@ -187,31 +202,39 @@ return Widget.extend({
|
||||
clear_selection: function () {
|
||||
this.$('li.selected').removeClass('selected');
|
||||
},
|
||||
/**
|
||||
* Adds a filter description to the filters dict
|
||||
* @param {Object} [filter] the filter description
|
||||
*/
|
||||
add_filter: function (filter) {
|
||||
this.filters[this.key_for(filter)] = filter;
|
||||
},
|
||||
/**
|
||||
* Creates a $filter JQuery node, adds it to the $filters dict and appends it to the filter menu
|
||||
* @param {Object} [filter] the filter description
|
||||
*/
|
||||
append_filter: function (filter) {
|
||||
var self = this,
|
||||
key = this.key_for(filter),
|
||||
$filter;
|
||||
var self = this;
|
||||
var key = this.key_for(filter);
|
||||
|
||||
this.$divider.show();
|
||||
if (key in this.$filters) {
|
||||
$filter = this.$filters[key];
|
||||
} else {
|
||||
this.filters[key] = filter;
|
||||
$filter = $('<li></li>')
|
||||
.insertBefore(this.$divider)
|
||||
if (!(key in this.$filters)) {
|
||||
var $filter = $('<li>')
|
||||
.addClass(filter.user_id ? 'oe_searchview_custom_private'
|
||||
: 'oe_searchview_custom_public')
|
||||
.toggleClass('oe_searchview_custom_default', filter.is_default)
|
||||
.append($('<a>').text(filter.name));
|
||||
|
||||
.append($('<a>').text(filter.name))
|
||||
.append($('<span>', {
|
||||
class: 'fa fa-trash-o remove-filter',
|
||||
on: {
|
||||
click: function (event) {
|
||||
event.stopImmediatePropagation();
|
||||
self.remove_filter(filter, $filter, key);
|
||||
},
|
||||
},
|
||||
}))
|
||||
.insertBefore(this.$divider);
|
||||
this.$filters[key] = $filter;
|
||||
this.$filters[key].addClass(filter.user_id ? 'oe_searchview_custom_private'
|
||||
: 'oe_searchview_custom_public');
|
||||
$('<span>')
|
||||
.addClass('fa fa-trash-o remove-filter')
|
||||
.click(function (event) {
|
||||
event.stopImmediatePropagation();
|
||||
self.remove_filter(filter, $filter, key);
|
||||
})
|
||||
.appendTo($filter);
|
||||
}
|
||||
this.$filters[key].unbind('click').click(function () {
|
||||
self.toggle_filter(filter);
|
||||
@@ -282,22 +305,12 @@ return Widget.extend({
|
||||
}
|
||||
},
|
||||
},
|
||||
init: function (parent, filters, fields_def) {
|
||||
init: function (parent, filters) {
|
||||
this._super(parent);
|
||||
this.filters = filters || [];
|
||||
this.searchview = parent;
|
||||
this.propositions = [];
|
||||
this.fields_def = fields_def.then(function (data) {
|
||||
var fields = {
|
||||
id: { string: 'ID', type: 'id', searchable: true }
|
||||
};
|
||||
_.each(data, function(field_def, field_name) {
|
||||
if (field_def.selectable !== false && field_name !== 'id') {
|
||||
fields[field_name] = field_def;
|
||||
}
|
||||
});
|
||||
return fields;
|
||||
});
|
||||
this.custom_filters_open = false;
|
||||
},
|
||||
start: function () {
|
||||
var self = this;
|
||||
@@ -311,23 +324,41 @@ return Widget.extend({
|
||||
$('<li class="divider">').insertBefore(self.$add_filter);
|
||||
}
|
||||
});
|
||||
this.append_proposition().then(function (prop) {
|
||||
prop.$el.hide();
|
||||
});
|
||||
},
|
||||
get_fields: function () {
|
||||
if (!this._fields_def) {
|
||||
this._fields_def = this.searchview.ViewManager.get_fields().then(function (data) {
|
||||
var fields = {
|
||||
id: { string: 'ID', type: 'id', searchable: true }
|
||||
};
|
||||
_.each(data, function(field_def, field_name) {
|
||||
if (field_def.selectable !== false && field_name !== 'id') {
|
||||
fields[field_name] = field_def;
|
||||
}
|
||||
});
|
||||
return fields;
|
||||
});
|
||||
}
|
||||
return this._fields_def;
|
||||
},
|
||||
toggle_custom_filter_menu: function (is_open) {
|
||||
this.$add_filter
|
||||
.toggleClass('o_closed_menu', !(_.isUndefined(is_open)) ? !is_open : undefined)
|
||||
.toggleClass('o_open_menu', is_open);
|
||||
this.$add_filter_menu.toggle(is_open);
|
||||
if (this.$add_filter.hasClass('o_closed_menu') && (!this.propositions.length)) {
|
||||
this.append_proposition();
|
||||
var self = this;
|
||||
this.custom_filters_open = !_.isUndefined(is_open) ? is_open : !this.custom_filters_open;
|
||||
var def;
|
||||
if (this.custom_filters_open && !this.propositions.length) {
|
||||
def = this.append_proposition();
|
||||
}
|
||||
this.$('.oe-filter-condition').toggle(is_open);
|
||||
$.when(def).then(function () {
|
||||
self.$add_filter
|
||||
.toggleClass('o_closed_menu', !self.custom_filters_open)
|
||||
.toggleClass('o_open_menu', self.custom_filters_open);
|
||||
self.$add_filter_menu.toggle(self.custom_filters_open);
|
||||
self.$('.oe-filter-condition').toggle(self.custom_filters_open);
|
||||
});
|
||||
},
|
||||
append_proposition: function () {
|
||||
var self = this;
|
||||
return this.fields_def.then(function (fields) {
|
||||
return this.get_fields().then(function (fields) {
|
||||
var prop = new search_filters.ExtendedSearchProposition(self, fields);
|
||||
self.propositions.push(prop);
|
||||
prop.insertBefore(self.$add_filter_menu);
|
||||
@@ -387,15 +418,13 @@ return Widget.extend({
|
||||
this.toggle_add_menu();
|
||||
},
|
||||
},
|
||||
init: function (parent, groups, fields_def) {
|
||||
init: function (parent, groups) {
|
||||
this._super(parent);
|
||||
this.groups = groups || [];
|
||||
this.groupable_fields = [];
|
||||
this.searchview = parent;
|
||||
this.fields_def = fields_def.then(this.proxy('get_groupable_fields'));
|
||||
},
|
||||
start: function () {
|
||||
var self = this;
|
||||
this.$menu = this.$('.group-by-menu');
|
||||
var divider = this.$menu.find('.divider');
|
||||
_.invoke(this.groups, 'insertBefore', divider);
|
||||
@@ -403,35 +432,43 @@ return Widget.extend({
|
||||
divider.show();
|
||||
}
|
||||
this.$add_group = this.$menu.find('.add-custom-group');
|
||||
this.fields_def.then(function () {
|
||||
self.$menu.append(QWeb.render('GroupByMenuSelector', self));
|
||||
self.$add_group_menu = self.$('.oe-add-group');
|
||||
self.$group_selector = self.$('.oe-group-selector');
|
||||
self.$('.oe-select-group').click(function () {
|
||||
self.toggle_add_menu(false);
|
||||
var field = self.$group_selector.find(':selected').data('name');
|
||||
self.add_groupby_to_menu(field);
|
||||
});
|
||||
});
|
||||
},
|
||||
get_groupable_fields: function (fields) {
|
||||
var groupable_types = ['many2one', 'char', 'boolean', 'selection', 'date', 'datetime'];
|
||||
var filter_group_field = _.filter(fields, function(field, name) {
|
||||
if (field.store && _.contains(groupable_types, field.type)) {
|
||||
field.name = name;
|
||||
return field;
|
||||
}
|
||||
});
|
||||
this.groupable_fields = _.sortBy(filter_group_field, 'string');
|
||||
get_fields: function () {
|
||||
var self = this;
|
||||
if (!this._fields_def) {
|
||||
this._fields_def = this.searchview.ViewManager.get_fields().then(function (fields) {
|
||||
var groupable_types = ['many2one', 'char', 'boolean', 'selection', 'date', 'datetime'];
|
||||
var filter_group_field = _.filter(fields, function(field, name) {
|
||||
if (field.store && _.contains(groupable_types, field.type)) {
|
||||
field.name = name;
|
||||
return field;
|
||||
}
|
||||
});
|
||||
self.groupable_fields = _.sortBy(filter_group_field, 'string');
|
||||
|
||||
self.$menu.append(QWeb.render('GroupByMenuSelector', self));
|
||||
self.$add_group_menu = self.$('.oe-add-group');
|
||||
self.$group_selector = self.$('.oe-group-selector');
|
||||
self.$('.oe-select-group').click(function () {
|
||||
self.toggle_add_menu(false);
|
||||
var field = self.$group_selector.find(':selected').data('name');
|
||||
self.add_groupby_to_menu(field);
|
||||
});
|
||||
});
|
||||
}
|
||||
return this._fields_def;
|
||||
},
|
||||
toggle_add_menu: function (is_open) {
|
||||
this.$add_group
|
||||
.toggleClass('o_closed_menu', !(_.isUndefined(is_open)) ? !is_open : undefined)
|
||||
.toggleClass('o_open_menu', is_open);
|
||||
this.$add_group_menu.toggle(is_open);
|
||||
if (this.$add_group.hasClass('o_open_menu')) {
|
||||
this.$group_selector.focus();
|
||||
}
|
||||
var self = this;
|
||||
this.get_fields().then(function () {
|
||||
self.$add_group
|
||||
.toggleClass('o_closed_menu', !(_.isUndefined(is_open)) ? !is_open : undefined)
|
||||
.toggleClass('o_open_menu', is_open);
|
||||
self.$add_group_menu.toggle(is_open);
|
||||
if (self.$add_group.hasClass('o_open_menu')) {
|
||||
self.$group_selector.focus();
|
||||
}
|
||||
});
|
||||
},
|
||||
add_groupby_to_menu: function (field_name) {
|
||||
var filter = new search_inputs.Filter({attrs:{
|
||||
|
||||
@@ -6,10 +6,9 @@ var core = require('web.core');
|
||||
var FavoriteMenu = require('web.FavoriteMenu');
|
||||
var FilterMenu = require('web.FilterMenu');
|
||||
var GroupByMenu = require('web.GroupByMenu');
|
||||
var Model = require('web.DataModel');
|
||||
var pyeval = require('web.pyeval');
|
||||
var search_inputs = require('web.search_inputs');
|
||||
var utils = require('web.utils');
|
||||
var View = require('web.View');
|
||||
var Widget = require('web.Widget');
|
||||
|
||||
var Backbone = window.Backbone;
|
||||
@@ -317,8 +316,14 @@ var FacetValueView = Widget.extend({
|
||||
}
|
||||
});
|
||||
|
||||
var SearchView = Widget.extend(/** @lends instance.web.SearchView# */{
|
||||
template: "SearchView",
|
||||
var SearchView = View.extend({
|
||||
defaults: _.extend({}, View.prototype.defaults, {
|
||||
hidden: false,
|
||||
disable_filters: false,
|
||||
disable_groupby: false,
|
||||
disable_favorites: false,
|
||||
disable_custom_filters: false,
|
||||
}),
|
||||
events: {
|
||||
// focus last input if view itself is clicked
|
||||
'click': function (e) {
|
||||
@@ -355,44 +360,36 @@ var SearchView = Widget.extend(/** @lends instance.web.SearchView# */{
|
||||
this.$el.autocomplete('widget').css('z-index', 9999);
|
||||
},
|
||||
},
|
||||
template: "SearchView",
|
||||
|
||||
/**
|
||||
* @constructs instance.web.SearchView
|
||||
* @extends instance.web.Widget
|
||||
* @constructs SearchView
|
||||
* @extends View
|
||||
*
|
||||
* @param parent
|
||||
* @param dataset
|
||||
* @param view_id
|
||||
* @param defaults
|
||||
* @param fields_view
|
||||
* @param {Object} [options]
|
||||
* @param {Boolean} [options.hidden=false] hide the search view
|
||||
* @param {Boolean} [options.disable_custom_filters=false] do not load custom filters from ir.filters
|
||||
*/
|
||||
init: function(parent, dataset, view_id, defaults, options) {
|
||||
this.options = _.defaults(options || {}, {
|
||||
hidden: false,
|
||||
disable_filters: false,
|
||||
disable_groupby: false,
|
||||
disable_favorites: false,
|
||||
disable_custom_filters: false,
|
||||
});
|
||||
this._super(parent);
|
||||
init: function() {
|
||||
this._super.apply(this, arguments);
|
||||
this.title = this.options.action && this.options.action.name;
|
||||
this.action_id = this.options.action && this.options.action.id;
|
||||
this.query = undefined;
|
||||
this.dataset = dataset;
|
||||
this.view_id = view_id;
|
||||
this.title = options.action && options.action.name;
|
||||
this.search_fields = [];
|
||||
this.filters = [];
|
||||
this.groupbys = [];
|
||||
this.visible_filters = (localStorage.visible_search_menu === 'true');
|
||||
this.input_subviews = []; // for user input in searchbar
|
||||
this.defaults = defaults || {};
|
||||
this.headless = this.options.hidden && _.isEmpty(this.defaults);
|
||||
this.search_defaults = this.options.search_defaults || {};
|
||||
this.headless = this.options.hidden && _.isEmpty(this.search_defaults);
|
||||
this.$buttons = this.options.$buttons;
|
||||
|
||||
this.filter_menu = undefined;
|
||||
this.groupby_menu = undefined;
|
||||
this.favorite_menu = undefined;
|
||||
this.action_id = this.options && this.options.action && this.options.action.id;
|
||||
},
|
||||
start: function() {
|
||||
if (this.headless) {
|
||||
@@ -405,43 +402,29 @@ var SearchView = Widget.extend(/** @lends instance.web.SearchView# */{
|
||||
.on('add change reset remove', this.proxy('do_search'))
|
||||
.on('change', this.proxy('renderChangedFacets'))
|
||||
.on('add reset remove', this.proxy('renderFacets'));
|
||||
var load_view = this.dataset._model.fields_view_get({
|
||||
view_id: this.view_id,
|
||||
view_type: 'search',
|
||||
context: this.dataset.get_context(),
|
||||
});
|
||||
this.$('.oe_searchview_unfold_drawer')
|
||||
.toggleClass('fa-caret-down', !this.visible_filters)
|
||||
.toggleClass('fa-caret-up', this.visible_filters);
|
||||
return this.alive($.when(this._super(), this.alive(load_view).then(this.view_loaded.bind(this))));
|
||||
},
|
||||
get_title: function() {
|
||||
return this.title;
|
||||
},
|
||||
view_loaded: function (r) {
|
||||
var menu_defs = [];
|
||||
this.fields_view_get = r;
|
||||
this.view_id = this.view_id || r.view_id;
|
||||
this.prepare_search_inputs();
|
||||
if (this.$buttons) {
|
||||
var fields_def = new Model(this.dataset.model).call('fields_get', {
|
||||
context: this.dataset.get_context()
|
||||
});
|
||||
|
||||
if (!this.options.disable_filters) {
|
||||
this.filter_menu = new FilterMenu(this, this.filters, fields_def);
|
||||
this.filter_menu = new FilterMenu(this, this.filters);
|
||||
menu_defs.push(this.filter_menu.appendTo(this.$buttons));
|
||||
}
|
||||
if (!this.options.disable_groupby) {
|
||||
this.groupby_menu = new GroupByMenu(this, this.groupbys, fields_def);
|
||||
this.groupby_menu = new GroupByMenu(this, this.groupbys);
|
||||
menu_defs.push(this.groupby_menu.appendTo(this.$buttons));
|
||||
}
|
||||
if (!this.options.disable_favorites) {
|
||||
this.favorite_menu = new FavoriteMenu(this, this.query, this.dataset.model, this.action_id);
|
||||
this.favorite_menu = new FavoriteMenu(this, this.query, this.dataset.model, this.action_id, this.options.filters);
|
||||
menu_defs.push(this.favorite_menu.appendTo(this.$buttons));
|
||||
}
|
||||
}
|
||||
return $.when.apply($, menu_defs).then(this.proxy('set_default_filters'));
|
||||
return $.when.apply($, menu_defs).then(this.set_default_filters.bind(this));
|
||||
},
|
||||
get_title: function() {
|
||||
return this.title;
|
||||
},
|
||||
set_default_filters: function () {
|
||||
var self = this,
|
||||
@@ -449,10 +432,10 @@ var SearchView = Widget.extend(/** @lends instance.web.SearchView# */{
|
||||
if (!self.options.disable_custom_filters && default_custom_filter) {
|
||||
return this.favorite_menu.toggle_filter(default_custom_filter, true);
|
||||
}
|
||||
if (!_.isEmpty(this.defaults)) {
|
||||
if (!_.isEmpty(this.search_defaults)) {
|
||||
var inputs = this.search_fields.concat(this.filters, this.groupbys),
|
||||
defaults = _.invoke(inputs, 'facet_for_defaults', this.defaults);
|
||||
return $.when.apply(null, defaults).then(function () {
|
||||
search_defaults = _.invoke(inputs, 'facet_for_defaults', this.search_defaults);
|
||||
return $.when.apply(null, search_defaults).then(function () {
|
||||
self.query.reset(_(arguments).compact(), {preventSearch: true});
|
||||
});
|
||||
}
|
||||
@@ -667,7 +650,7 @@ var SearchView = Widget.extend(/** @lends instance.web.SearchView# */{
|
||||
// * this.group_by: group_bys
|
||||
prepare_search_inputs: function () {
|
||||
var self = this,
|
||||
arch = this.fields_view_get.arch;
|
||||
arch = this.fields_view.arch;
|
||||
|
||||
var filters = [].concat.apply([], _.map(arch.children, function (item) {
|
||||
return item.tag !== 'group' ? eval_item(item) : item.children.map(eval_item);
|
||||
@@ -702,7 +685,7 @@ var SearchView = Widget.extend(/** @lends instance.web.SearchView# */{
|
||||
}
|
||||
if (filter.item.tag === 'field') {
|
||||
var attrs = filter.item.attrs;
|
||||
var field = self.fields_view_get.fields[attrs.name];
|
||||
var field = self.fields_view.fields[attrs.name];
|
||||
|
||||
// M2O combined with selection widget is pointless and broken in search views,
|
||||
// but has been used in the past for unsupported hacks -> ignore it
|
||||
|
||||
@@ -14,41 +14,21 @@ var View = require('web.View');
|
||||
var _lt = core._lt;
|
||||
var QWeb = core.qweb;
|
||||
|
||||
var TreeView = View.extend(/** @lends instance.web.TreeView# */{
|
||||
var TreeView = View.extend({
|
||||
display_name: _lt('Tree'),
|
||||
icon: 'fa-align-left',
|
||||
view_type: 'tree',
|
||||
/**
|
||||
* Indicates that this view is not searchable, and thus that no search
|
||||
* view should be displayed (if there is one active).
|
||||
*/
|
||||
searchable : false,
|
||||
// Indicates that this view is not searchable, and thus that no search view should be displayed.
|
||||
searchable: false,
|
||||
|
||||
/**
|
||||
* Genuine tree view (the one displayed as a tree, not the list)
|
||||
*
|
||||
* @constructs instance.web.TreeView
|
||||
* @extends instance.web.View
|
||||
*
|
||||
* @param parent
|
||||
* @param dataset
|
||||
* @param view_id
|
||||
* @param options
|
||||
*/
|
||||
init: function(parent, dataset, view_id, options) {
|
||||
this._super(parent);
|
||||
this.dataset = dataset;
|
||||
this.model = dataset.model;
|
||||
this.view_id = view_id;
|
||||
|
||||
init: function() {
|
||||
this._super.apply(this, arguments);
|
||||
this.records = {};
|
||||
|
||||
this.options = _.extend({}, this.defaults, options || {});
|
||||
|
||||
_.bindAll(this, 'color_for');
|
||||
},
|
||||
|
||||
view_loading: function(r) {
|
||||
return this.load_tree(r);
|
||||
this.fields = this.fields_view.fields;
|
||||
this.children_field = this.fields_view.field_parent;
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -67,20 +47,29 @@ var TreeView = View.extend(/** @lends instance.web.TreeView# */{
|
||||
}
|
||||
return fields;
|
||||
},
|
||||
load_tree: function (fields_view) {
|
||||
var self = this;
|
||||
var has_toolbar = !!fields_view.arch.attrs.toolbar;
|
||||
// field name in OpenERP is kinda stupid: this is the name of the field
|
||||
// holding the ids to the children of the current node, why call it
|
||||
// field_parent?
|
||||
this.children_field = fields_view.field_parent;
|
||||
this.fields_view = fields_view;
|
||||
willStart: function () {
|
||||
_(this.fields_view.arch.children).each(function (field) {
|
||||
if (field.attrs.modifiers) {
|
||||
field.attrs.modifiers = JSON.parse(field.attrs.modifiers);
|
||||
}
|
||||
});
|
||||
this.fields = fields_view.fields;
|
||||
|
||||
if (this.fields_view.arch.attrs.colors) {
|
||||
this.colors = _(this.fields_view.arch.attrs.colors.split(';')).chain()
|
||||
.compact()
|
||||
.map(function(color_pair) {
|
||||
var pair = color_pair.split(':'),
|
||||
color = pair[0],
|
||||
expr = pair[1];
|
||||
return [color, py.parse(py.tokenize(expr)), expr];
|
||||
}).value();
|
||||
}
|
||||
|
||||
return this._super();
|
||||
},
|
||||
start: function () {
|
||||
var self = this;
|
||||
var has_toolbar = !!this.fields_view.arch.attrs.toolbar;
|
||||
this.hook_row_click();
|
||||
this.$el.html(QWeb.render('TreeView', {
|
||||
'title': this.fields_view.arch.attrs.string,
|
||||
@@ -120,17 +109,7 @@ var TreeView = View.extend(/** @lends instance.web.TreeView# */{
|
||||
// TODO store open nodes in url ?...
|
||||
this.do_push_state({});
|
||||
|
||||
if (!this.fields_view.arch.attrs.colors) {
|
||||
return;
|
||||
}
|
||||
this.colors = _(this.fields_view.arch.attrs.colors.split(';')).chain()
|
||||
.compact()
|
||||
.map(function(color_pair) {
|
||||
var pair = color_pair.split(':'),
|
||||
color = pair[0],
|
||||
expr = pair[1];
|
||||
return [color, py.parse(py.tokenize(expr)), expr];
|
||||
}).value();
|
||||
return this._super();
|
||||
},
|
||||
/**
|
||||
* Returns the color for the provided record in the current view (from the
|
||||
@@ -225,7 +204,7 @@ var TreeView = View.extend(/** @lends instance.web.TreeView# */{
|
||||
activate: function(id) {
|
||||
var self = this;
|
||||
var local_context = {
|
||||
active_model: self.dataset.model,
|
||||
active_model: self.model,
|
||||
active_id: id,
|
||||
active_ids: [id]
|
||||
};
|
||||
@@ -234,7 +213,7 @@ var TreeView = View.extend(/** @lends instance.web.TreeView# */{
|
||||
this.dataset.get_context(), local_context));
|
||||
return this.rpc('/web/treeview/action', {
|
||||
id: id,
|
||||
model: this.dataset.model,
|
||||
model: this.model,
|
||||
context: ctx
|
||||
}).then(function (actions) {
|
||||
if (!actions.length) { return; }
|
||||
@@ -260,11 +239,6 @@ var TreeView = View.extend(/** @lends instance.web.TreeView# */{
|
||||
$child_row.toggle(show);
|
||||
}, this);
|
||||
},
|
||||
|
||||
do_hide: function () {
|
||||
this.hidden = true;
|
||||
this._super();
|
||||
}
|
||||
});
|
||||
|
||||
core.view_registry.add('tree', TreeView);
|
||||
|
||||
@@ -193,17 +193,6 @@ odoo.define_section('list.edition', ['web.data', 'web.ListView'], function (test
|
||||
}
|
||||
return [];
|
||||
});
|
||||
mock.add('demo:fields_view_get', function () {
|
||||
return {
|
||||
type: 'tree',
|
||||
fields: {
|
||||
a: {type: 'char', string: "A"},
|
||||
b: {type: 'char', string: "B"},
|
||||
c: {type: 'char', string: "C"}
|
||||
},
|
||||
arch: '<tree><field name="a"/><field name="b"/><field name="c"/></tree>',
|
||||
};
|
||||
});
|
||||
mock.add('demo:onchange', function () {
|
||||
return {};
|
||||
});
|
||||
@@ -224,7 +213,16 @@ odoo.define_section('list.edition', ['web.data', 'web.ListView'], function (test
|
||||
});
|
||||
|
||||
var ds = new data.DataSetStatic(null, 'demo', null, [1]);
|
||||
var l = new ListView({}, ds, false, {editable: 'top'});
|
||||
var fields_view = ds._model.postprocess_fvg({
|
||||
type: 'tree',
|
||||
fields: {
|
||||
a: {type: 'char', string: "A"},
|
||||
b: {type: 'char', string: "B"},
|
||||
c: {type: 'char', string: "C"}
|
||||
},
|
||||
arch: '<tree><field name="a"/><field name="b"/><field name="c"/></tree>',
|
||||
});
|
||||
var l = new ListView({}, ds, fields_view, {editable: 'top'});
|
||||
|
||||
var $fix = $( "#qunit-fixture");
|
||||
return l.appendTo($fix)
|
||||
@@ -250,22 +248,21 @@ odoo.define_section('list.edition', ['web.data', 'web.ListView'], function (test
|
||||
});
|
||||
|
||||
odoo.define_section('list.edition.events', ['web.data', 'web.ListView'], function (test, mock) {
|
||||
|
||||
function fields_view_get () {
|
||||
return {
|
||||
type: 'tree',
|
||||
fields: {
|
||||
a: {type: 'char', string: "A"},
|
||||
b: {type: 'char', string: "B"},
|
||||
c: {type: 'char', string: "C"}
|
||||
},
|
||||
arch: '<tree><field name="a"/><field name="b"/><field name="c"/></tree>',
|
||||
};
|
||||
}
|
||||
function setup () {
|
||||
mock.add('demo:read', function () {
|
||||
return [{ id: 1, a: 'foo', b: 'bar', c: 'baz' }];
|
||||
});
|
||||
mock.add('demo:fields_view_get', function () {
|
||||
return {
|
||||
type: 'tree',
|
||||
fields: {
|
||||
a: {type: 'char', string: "A"},
|
||||
b: {type: 'char', string: "B"},
|
||||
c: {type: 'char', string: "C"}
|
||||
},
|
||||
arch: '<tree><field name="a"/><field name="b"/><field name="c"/></tree>',
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
test('edition events',function (assert, data, ListView) {
|
||||
@@ -276,7 +273,8 @@ odoo.define_section('list.edition.events', ['web.data', 'web.ListView'], functio
|
||||
counter: 0,
|
||||
onEvent: function (e) { this.counter++; }
|
||||
};
|
||||
var l = new ListView({}, ds, false, {editable: 'top'});
|
||||
var fields_view = ds._model.postprocess_fvg(fields_view_get());
|
||||
var l = new ListView({}, ds, fields_view, {editable: 'top'});
|
||||
l.on('edit:before edit:after', o, o.onEvent);
|
||||
|
||||
var $fix = $( "#qunit-fixture");
|
||||
@@ -297,7 +295,8 @@ odoo.define_section('list.edition.events', ['web.data', 'web.ListView'], functio
|
||||
setup();
|
||||
var edit_after = false;
|
||||
var ds = new data.DataSetStatic(null, 'demo', null, [1]);
|
||||
var l = new ListView({}, ds, false, {editable: 'top'});
|
||||
var fields_view = ds._model.postprocess_fvg(fields_view_get());
|
||||
var l = new ListView({}, ds, fields_view, {editable: 'top'});
|
||||
l.on('edit:before', {}, function (e) {
|
||||
e.cancel = true;
|
||||
});
|
||||
@@ -329,16 +328,6 @@ odoo.define_section('list.edition.onwrite', ['web.data', 'web.ListView'], functi
|
||||
mock.add('demo:onchange', function () {
|
||||
return {};
|
||||
});
|
||||
|
||||
mock.add('demo:fields_view_get', function () {
|
||||
return {
|
||||
type: 'tree',
|
||||
fields: {
|
||||
a: {type: 'char', string: "A"}
|
||||
},
|
||||
arch: '<tree on_write="on_write" colors="red:a == \'foo\'"><field name="a"/></tree>',
|
||||
};
|
||||
});
|
||||
mock.add('demo:read', function (args, kwargs) {
|
||||
if (_.isEmpty(args[0])) {
|
||||
return [];
|
||||
@@ -358,7 +347,14 @@ odoo.define_section('list.edition.onwrite', ['web.data', 'web.ListView'], functi
|
||||
mock.add('demo:on_write', function () { return [42]; });
|
||||
|
||||
var ds = new data.DataSetStatic(null, 'demo', null, []);
|
||||
var l = new ListView({}, ds, false, {editable: 'top'});
|
||||
var fields_view = ds._model.postprocess_fvg({
|
||||
type: 'tree',
|
||||
fields: {
|
||||
a: {type: 'char', string: "A"}
|
||||
},
|
||||
arch: '<tree on_write="on_write" colors="red:a == \'foo\'"><field name="a"/></tree>',
|
||||
});
|
||||
var l = new ListView({}, ds, fields_view, {editable: 'top'});
|
||||
|
||||
var $fix = $( "#qunit-fixture");
|
||||
return l.appendTo($fix)
|
||||
|
||||
@@ -3,15 +3,6 @@ odoo.define_section('list.buttons', ['web.ListView', 'web.data'], function (test
|
||||
test('record-deletion', function (assert, ListView, data) {
|
||||
assert.expect(2);
|
||||
|
||||
mock.add('demo:fields_view_get', function () {
|
||||
return {
|
||||
type: 'tree',
|
||||
fields: {
|
||||
a: {type: 'char', string: "A"}
|
||||
},
|
||||
arch: '<tree><field name="a"/><button type="object" name="foo"/></tree>',
|
||||
};
|
||||
});
|
||||
mock.add('demo:read', function (args, kwargs) {
|
||||
if (_.isEqual(args[0], [1, 2, 3])) {
|
||||
return [
|
||||
@@ -29,10 +20,18 @@ odoo.define_section('list.buttons', ['web.ListView', 'web.data'], function (test
|
||||
});
|
||||
mock.add('/web/dataset/call_button', function () { return false; });
|
||||
|
||||
|
||||
var ds = new data.DataSetStatic(null, 'demo', null, [1, 2, 3]);
|
||||
var fields_view = ds._model.postprocess_fvg({
|
||||
type: 'tree',
|
||||
fields: {
|
||||
a: {type: 'char', string: "A"}
|
||||
},
|
||||
arch: '<tree><field name="a"/><button type="object" name="foo"/></tree>',
|
||||
});
|
||||
var list = new ListView({
|
||||
do_action: odoo.testing.noop
|
||||
}, ds, false, {editable: 'top'});
|
||||
}, ds, fields_view, {editable: 'top'});
|
||||
|
||||
var $fix = $( "#qunit-fixture");
|
||||
|
||||
@@ -52,6 +51,5 @@ odoo.define_section('list.buttons', ['web.ListView', 'web.data'], function (test
|
||||
assert.strictEqual($fix.find('table tbody tr[data-id]').length, 2,
|
||||
"should have 2 rows left");
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
@@ -156,28 +156,18 @@ odoo.define_section('search.query', ['web.SearchView'], function (test) {
|
||||
* @return {instance.web.SearchView}
|
||||
*/
|
||||
|
||||
function makeSearchView (test, dummy_widget_attributes, defaults, options) {
|
||||
function makeSearchView (test, dummy_widget_attributes, defaults, options, raw_fields_view) {
|
||||
var core = test.deps['web.core'];
|
||||
var search_inputs = test.deps['web.search_inputs'];
|
||||
var data = test.deps['web.data'];
|
||||
var SearchView = test.deps['web.SearchView'];
|
||||
|
||||
var mock = test.mock;
|
||||
var assert = test.assert
|
||||
var assert = test.assert;
|
||||
|
||||
var DummyWidget = search_inputs.Field.extend(dummy_widget_attributes || {});
|
||||
core.search_widgets_registry.add('dummy', DummyWidget);
|
||||
|
||||
mock.add('dummy.model:fields_view_get', function () {
|
||||
return {
|
||||
type: 'search',
|
||||
fields: {
|
||||
dummy: {type: 'char', string: "Dummy", searchable: true}
|
||||
},
|
||||
arch: '<search><field name="dummy" widget="dummy"/></search>'
|
||||
};
|
||||
}, true);
|
||||
|
||||
mock.add('ir.filters:get_filters', function () {
|
||||
return [];
|
||||
});
|
||||
@@ -188,16 +178,25 @@ function makeSearchView (test, dummy_widget_attributes, defaults, options) {
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
// instance.client = { action_manager: { inner_action: undefined } };
|
||||
|
||||
var dataset = new data.DataSet(null, 'dummy.model');
|
||||
|
||||
var mock_parent = {getParent: function () {return null;}};
|
||||
var mock_parent = {
|
||||
getParent: function () { return null; },
|
||||
get_fields: function () {
|
||||
return dataset._model.call('fields_get', { context: dataset.get_context() });
|
||||
},
|
||||
};
|
||||
|
||||
options = _.defaults(options || {}, {$buttons: $('<div>')});
|
||||
|
||||
var view = new SearchView(mock_parent, dataset, false, defaults, options);
|
||||
options.search_defaults = defaults;
|
||||
var fields_view = dataset._model.postprocess_fvg(raw_fields_view || {
|
||||
type: 'search',
|
||||
fields: {
|
||||
dummy: {type: 'char', string: "Dummy", searchable: true}
|
||||
},
|
||||
arch: '<search><field name="dummy" widget="dummy"/></search>'
|
||||
});
|
||||
var view = new SearchView(mock_parent, dataset, fields_view, options);
|
||||
|
||||
view.on('invalid_search', this, function () {
|
||||
assert.ok(false, JSON.stringify([].slice(arguments)));
|
||||
@@ -1211,25 +1210,22 @@ odoo.define_section('search.serialization', ['web.search_inputs', 'web.SearchVie
|
||||
});
|
||||
|
||||
odoo.define_section('search.filters', ['web.search_inputs', 'web.SearchView', 'web.core', 'web.data'], function (test, mock) {
|
||||
function setup () {
|
||||
mock.add('dummy.model:fields_view_get', function () {
|
||||
// view with a single group of filters
|
||||
return {
|
||||
type: 'search',
|
||||
fields: {},
|
||||
arch: '<search>' +
|
||||
'<filter string="Foo1" domain="[ [\'foo\', \'=\', \'1\'] ]"/>' +
|
||||
'<filter name="foo2" string="Foo2" domain="[ [\'foo\', \'=\', \'2\'] ]"/>' +
|
||||
'<filter string="Foo3" domain="[ [\'foo\', \'=\', \'3\'] ]"/>' +
|
||||
'</search>',
|
||||
};
|
||||
});
|
||||
function fields_view_get () {
|
||||
// view with a single group of filters
|
||||
return {
|
||||
type: 'search',
|
||||
fields: {},
|
||||
arch: '<search>' +
|
||||
'<filter string="Foo1" domain="[ [\'foo\', \'=\', \'1\'] ]"/>' +
|
||||
'<filter name="foo2" string="Foo2" domain="[ [\'foo\', \'=\', \'2\'] ]"/>' +
|
||||
'<filter string="Foo3" domain="[ [\'foo\', \'=\', \'3\'] ]"/>' +
|
||||
'</search>',
|
||||
};
|
||||
}
|
||||
|
||||
test('drawn', function (assert) {
|
||||
setup();
|
||||
assert.expect(3);
|
||||
var view = makeSearchView(this);
|
||||
var view = makeSearchView(this, {}, {}, {}, fields_view_get());
|
||||
var $fix = $('#qunit-fixture');
|
||||
|
||||
return view.appendTo($fix)
|
||||
@@ -1237,10 +1233,9 @@ odoo.define_section('search.filters', ['web.search_inputs', 'web.SearchView', 'w
|
||||
var $filters = view.$buttons.find('.filters-menu li'),
|
||||
$favorites = view.$buttons.find('.o_favorites_menu li'),
|
||||
$groupby = view.$buttons.find('.group-by-menu li');
|
||||
// 3 filters, 1 separator, 1 button add filter,
|
||||
// 1 filter condition menu, 1 apply button
|
||||
assert.equal($filters.length, 7,
|
||||
'filter menu should have 7 elements total');
|
||||
// 3 filters, 1 separator, 1 button add filter, 1 apply button
|
||||
assert.equal($filters.length, 6,
|
||||
'filter menu should have 6 elements total');
|
||||
|
||||
// 1 divider, 1 save search button, 1 text input, 2 checkboxes,
|
||||
// 1 save button, 3 add to dashboard things (a, input, button)
|
||||
@@ -1249,9 +1244,9 @@ odoo.define_section('search.filters', ['web.search_inputs', 'web.SearchView', 'w
|
||||
// assert.equal($favorites.length, 9,
|
||||
// "favorites menu should have 9 elements");
|
||||
|
||||
// 1 divider, 1 add custom group button, 1 select groupby, 1 apply button,
|
||||
assert.equal($groupby.length, 4,
|
||||
"groupby menu should have 4 element");
|
||||
// 1 divider, 1 add custom group button
|
||||
assert.equal($groupby.length, 2,
|
||||
"groupby menu should have 2 element");
|
||||
assert.equal(_.str.strip($filters.find('a')[0].textContent), "Foo1",
|
||||
"Text content of first filter option should match filter string");
|
||||
});
|
||||
@@ -1259,10 +1254,9 @@ odoo.define_section('search.filters', ['web.search_inputs', 'web.SearchView', 'w
|
||||
|
||||
test('click adding from empty query', function (assert, search_inputs) {
|
||||
assert.expect(4);
|
||||
setup();
|
||||
var Filter = search_inputs.Filter;
|
||||
|
||||
var view = makeSearchView(this);
|
||||
var view = makeSearchView(this, {}, {}, {}, fields_view_get());
|
||||
var $fix = $('#qunit-fixture');
|
||||
return view.appendTo($fix)
|
||||
.done(function () {
|
||||
@@ -1281,8 +1275,7 @@ odoo.define_section('search.filters', ['web.search_inputs', 'web.SearchView', 'w
|
||||
|
||||
test('click adding from existing query', function (assert) {
|
||||
assert.expect(4);
|
||||
setup();
|
||||
var view = makeSearchView(this, {}, {foo2: true});
|
||||
var view = makeSearchView(this, {}, {foo2: true}, {}, fields_view_get());
|
||||
|
||||
var $fix = $('#qunit-fixture');
|
||||
return view.appendTo($fix)
|
||||
@@ -1304,10 +1297,9 @@ odoo.define_section('search.filters', ['web.search_inputs', 'web.SearchView', 'w
|
||||
|
||||
test('click removing from query', function (assert) {
|
||||
assert.expect(4);
|
||||
setup();
|
||||
|
||||
var calls = 0;
|
||||
var view = makeSearchView(this, {}, {foo2: true});
|
||||
var view = makeSearchView(this, {}, {foo2: true}, {}, fields_view_get());
|
||||
view.on('search_data', null, function () {
|
||||
++calls;
|
||||
});
|
||||
@@ -1329,20 +1321,17 @@ odoo.define_section('search.groupby', ['web.search_inputs', 'web.SearchView', 'w
|
||||
|
||||
test('basic', ['web.FavoriteMenu', 'web.FilterMenu', 'web.GroupByMenu'], function (assert, search_inputs, SearchView, core, data, FavoriteMenu, FilterMenu, GroupByMenu) {
|
||||
assert.expect(12);
|
||||
mock.add('dummy.model:fields_view_get', function () {
|
||||
return {
|
||||
type: 'search',
|
||||
fields: {},
|
||||
arch: [
|
||||
'<search>',
|
||||
'<filter string="Foo" context="{\'group_by\': \'foo\'}"/>',
|
||||
'<filter string="Bar" context="{\'group_by\': \'bar\'}"/>',
|
||||
'<filter string="Baz" context="{\'group_by\': \'baz\'}"/>',
|
||||
'</search>'
|
||||
].join(''),
|
||||
};
|
||||
var view = makeSearchView(this, {}, {}, {}, {
|
||||
type: 'search',
|
||||
fields: {},
|
||||
arch: [
|
||||
'<search>',
|
||||
'<filter string="Foo" context="{\'group_by\': \'foo\'}"/>',
|
||||
'<filter string="Bar" context="{\'group_by\': \'bar\'}"/>',
|
||||
'<filter string="Baz" context="{\'group_by\': \'baz\'}"/>',
|
||||
'</search>'
|
||||
].join(''),
|
||||
});
|
||||
var view = makeSearchView(this);
|
||||
var $fix = $('#qunit-fixture');
|
||||
return view.appendTo($fix)
|
||||
.done(function () {
|
||||
@@ -1378,22 +1367,19 @@ odoo.define_section('search.groupby', ['web.search_inputs', 'web.SearchView', 'w
|
||||
|
||||
test('unified multiple groupby groups', function (assert, search_inputs, SearchView, core, data) {
|
||||
assert.expect(3);
|
||||
mock.add('dummy.model:fields_view_get', function () {
|
||||
return {
|
||||
type: 'search',
|
||||
fields: {},
|
||||
arch: [
|
||||
'<search>',
|
||||
'<filter string="Foo" context="{\'group_by\': \'foo\'}"/>',
|
||||
'<separator/>',
|
||||
'<filter string="Bar" context="{\'group_by\': \'bar\'}"/>',
|
||||
'<separator/>',
|
||||
'<filter string="Baz" context="{\'group_by\': \'baz\'}"/>',
|
||||
'</search>'
|
||||
].join(''),
|
||||
};
|
||||
var view = makeSearchView(this, {}, {}, {}, {
|
||||
type: 'search',
|
||||
fields: {},
|
||||
arch: [
|
||||
'<search>',
|
||||
'<filter string="Foo" context="{\'group_by\': \'foo\'}"/>',
|
||||
'<separator/>',
|
||||
'<filter string="Bar" context="{\'group_by\': \'bar\'}"/>',
|
||||
'<separator/>',
|
||||
'<filter string="Baz" context="{\'group_by\': \'baz\'}"/>',
|
||||
'</search>'
|
||||
].join(''),
|
||||
});
|
||||
var view = makeSearchView(this);
|
||||
var $fix = $('#qunit-fixture');
|
||||
|
||||
return view.appendTo($fix)
|
||||
|
||||
@@ -61,65 +61,32 @@ function isNullOrUndef(value) {
|
||||
}
|
||||
|
||||
var CalendarView = View.extend({
|
||||
template: "CalendarView",
|
||||
defaults: _.extend({}, View.prototype.defaults, {
|
||||
confirm_on_delete: true,
|
||||
}),
|
||||
display_name: _lt('Calendar'),
|
||||
icon: 'fa-calendar',
|
||||
quick_create_instance: widgets.QuickCreate,
|
||||
template: "CalendarView",
|
||||
|
||||
init: function (parent, dataset, view_id, options) {
|
||||
this._super(parent);
|
||||
this.ready = $.Deferred();
|
||||
this.set_default_options(options);
|
||||
this.dataset = dataset;
|
||||
this.model = dataset.model;
|
||||
this.fields_view = {};
|
||||
this.view_id = view_id;
|
||||
this.view_type = 'calendar';
|
||||
init: function () {
|
||||
this._super.apply(this, arguments);
|
||||
this.color_map = {};
|
||||
this.range_start = null;
|
||||
this.range_stop = null;
|
||||
this.selected_filters = [];
|
||||
this.info_fields = [];
|
||||
|
||||
this.title = (this.options.action)? this.options.action.name : '';
|
||||
|
||||
this.shown = $.Deferred();
|
||||
},
|
||||
|
||||
set_default_options: function(options) {
|
||||
this._super(options);
|
||||
_.defaults(this.options, {
|
||||
confirm_on_delete: true
|
||||
});
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
if (this.$calendar) {
|
||||
this.$calendar.fullCalendar('destroy');
|
||||
}
|
||||
if (this.$small_calendar) {
|
||||
this.$small_calendar.datepicker('destroy');
|
||||
}
|
||||
this._super.apply(this, arguments);
|
||||
},
|
||||
|
||||
view_loading: function (fv) {
|
||||
/* xml view calendar options */
|
||||
var attrs = fv.arch.attrs,
|
||||
self = this;
|
||||
this.fields_view = fv;
|
||||
this.$calendar = this.$(".o_calendar_widget");
|
||||
|
||||
this.info_fields = [];
|
||||
|
||||
var attrs = this.fields_view.arch.attrs;
|
||||
if (!attrs.date_start) {
|
||||
throw new Error(_t("Calendar view has not defined 'date_start' attribute."));
|
||||
}
|
||||
|
||||
this.$el.addClass(attrs['class']);
|
||||
|
||||
this.name = fv.name || attrs.string;
|
||||
this.view_id = fv.view_id;
|
||||
|
||||
this.fields = this.fields_view.fields;
|
||||
this.name = this.fields_view.name || attrs.string;
|
||||
this.mode = attrs.mode; // one of month, week or day
|
||||
this.date_start = attrs.date_start; // Field name of starting date field
|
||||
this.date_delay = attrs.date_delay; // duration
|
||||
@@ -129,7 +96,7 @@ var CalendarView = View.extend({
|
||||
this.attendee_people = attrs.attendee;
|
||||
|
||||
// Check whether the date field is editable (i.e. if the events can be dragged and dropped)
|
||||
this.editable = !this.options.read_only_mode && !this.fields_view.fields[this.date_start].readonly;
|
||||
this.editable = !this.options.read_only_mode && !this.fields[this.date_start].readonly;
|
||||
|
||||
//if quick_add = False, we don't allow quick_add
|
||||
//if quick_add = not specified in view, we use the default quick_create_instance
|
||||
@@ -155,7 +122,7 @@ var CalendarView = View.extend({
|
||||
this.colorIsAttendee = !(isNullOrUndef(attrs.color_is_attendee) || !_.str.toBoolElse(attrs.color_is_attendee, true));
|
||||
|
||||
// if we have not sidebar, (eg: Dashboard), we don't use the filter "coworkers"
|
||||
if (isNullOrUndef(self.options.sidebar)) {
|
||||
if (isNullOrUndef(this.options.sidebar)) {
|
||||
this.useContacts = false;
|
||||
this.colorIsAttendee = false;
|
||||
this.attendee_people = undefined;
|
||||
@@ -203,32 +170,38 @@ var CalendarView = View.extend({
|
||||
}
|
||||
}
|
||||
|
||||
this.fields = fv.fields;
|
||||
|
||||
for (var fld = 0; fld < fv.arch.children.length; fld++) {
|
||||
this.info_fields.push(fv.arch.children[fld].attrs.name);
|
||||
for (var fld = 0; fld < this.fields_view.arch.children.length; fld++) {
|
||||
this.info_fields.push(this.fields_view.arch.children[fld].attrs.name);
|
||||
}
|
||||
|
||||
self.shown.done(this._do_show_init.bind(this));
|
||||
var edit_check = new Model(this.dataset.model)
|
||||
.call("check_access_rights", ["write", false])
|
||||
.then(function (write_right) {
|
||||
self.write_right = write_right;
|
||||
});
|
||||
var init = new Model(this.dataset.model)
|
||||
.call("check_access_rights", ["create", false])
|
||||
.then(function (create_right) {
|
||||
self.create_right = create_right;
|
||||
self.ready.resolve();
|
||||
self.trigger('calendar_view_loaded', fv);
|
||||
});
|
||||
return $.when(edit_check, init);
|
||||
},
|
||||
_do_show_init: function () {
|
||||
willStart: function () {
|
||||
var self = this;
|
||||
var write_def = this.dataset.call("check_access_rights", ["write", false]);
|
||||
var create_def = this.dataset.call("check_access_rights", ["create", false]);
|
||||
return $.when(write_def, create_def, this._super()).then(function (write, create) {
|
||||
self.write_right = write;
|
||||
self.create_right = create;
|
||||
});
|
||||
},
|
||||
start: function () {
|
||||
this.$calendar = this.$(".o_calendar_widget");
|
||||
this.$el.addClass(this.fields_view.arch.attrs.class);
|
||||
this.shown.done(this._do_show_init.bind(this));
|
||||
return this._super();
|
||||
},
|
||||
destroy: function() {
|
||||
if (this.$calendar) {
|
||||
this.$calendar.fullCalendar('destroy');
|
||||
}
|
||||
if (this.$small_calendar) {
|
||||
this.$small_calendar.datepicker('destroy');
|
||||
}
|
||||
this._super.apply(this, arguments);
|
||||
},
|
||||
|
||||
_do_show_init: function () {
|
||||
this.init_calendar().then(function() {
|
||||
$(window).trigger('resize');
|
||||
self.trigger('calendar_view_loaded', self.fields_view);
|
||||
});
|
||||
},
|
||||
/**
|
||||
@@ -876,9 +849,9 @@ var CalendarView = View.extend({
|
||||
}
|
||||
}
|
||||
else {
|
||||
var dialog = new form_common.FormViewDialog(this, {
|
||||
res_model: this.dataset.model,
|
||||
res_id: parseInt(id).toString() == id ? parseInt(id) : id,
|
||||
new form_common.FormViewDialog(this, {
|
||||
res_model: this.model,
|
||||
res_id: parseInt(id).toString() === id ? parseInt(id) : id,
|
||||
context: this.dataset.get_context(),
|
||||
title: title,
|
||||
view_id: +this.open_popup_action,
|
||||
|
||||
@@ -2,7 +2,6 @@ odoo.define('web_calendar.widgets', function(require) {
|
||||
"use strict";
|
||||
|
||||
var core = require('web.core');
|
||||
var data = require('web.data');
|
||||
var Dialog = require('web.Dialog');
|
||||
var form_common = require('web.form_common');
|
||||
var Widget = require('web.Widget');
|
||||
@@ -69,12 +68,12 @@ var QuickCreate = Dialog.extend({
|
||||
});
|
||||
|
||||
this.$input = this.$('input').keyup(function enterHandler (e) {
|
||||
if(e.keyCode == $.ui.keyCode.ENTER) {
|
||||
if(e.keyCode === $.ui.keyCode.ENTER) {
|
||||
self.$input.off('keyup', enterHandler);
|
||||
if (!self.quick_add()){
|
||||
self.$input.on('keyup', enterHandler);
|
||||
}
|
||||
} else if (e.keyCode == $.ui.keyCode.ESCAPE && self._buttons) {
|
||||
} else if (e.keyCode === $.ui.keyCode.ESCAPE && self._buttons) {
|
||||
self.close();
|
||||
}
|
||||
});
|
||||
@@ -114,20 +113,6 @@ var QuickCreate = Dialog.extend({
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Show full form popup
|
||||
*/
|
||||
get_form_popup_infos: function() {
|
||||
var parent = this.getParent();
|
||||
var infos = {
|
||||
view_id: false,
|
||||
title: this.name,
|
||||
};
|
||||
if (!_.isUndefined(parent) && !(_.isUndefined(parent.ViewManager))) {
|
||||
infos.view_id = parent.ViewManager.get_view_id('form');
|
||||
}
|
||||
return infos;
|
||||
},
|
||||
slow_create: function(_data) {
|
||||
//if all day, we could reset time to display 00:00:00
|
||||
|
||||
@@ -140,26 +125,21 @@ var QuickCreate = Dialog.extend({
|
||||
defaults['default_' + field_name] = val;
|
||||
});
|
||||
|
||||
var pop_infos = self.get_form_popup_infos();
|
||||
var context = new data.CompoundContext(this.dataset.context, defaults);
|
||||
var pop = new form_common.FormViewDialog(this, {
|
||||
res_model: this.dataset.model,
|
||||
context: this.dataset.get_context(defaults),
|
||||
title: this.get_title(),
|
||||
disable_multiple_selection: true,
|
||||
view_id: pop_infos.view_id,
|
||||
// Ensuring we use ``self.dataset`` and DO NOT create a new one.
|
||||
create_function: function(data, options) {
|
||||
return self.dataset.create(data, options).done(function(r) {
|
||||
}).fail(function (r, event) {
|
||||
return self.dataset.create(data, options).fail(function (r) {
|
||||
if (!r.data.message) { //else manage by openerp
|
||||
throw new Error(r);
|
||||
}
|
||||
});
|
||||
},
|
||||
read_function: function(id, fields, options) {
|
||||
return self.dataset.read_ids.apply(self.dataset, arguments).done(function() {
|
||||
}).fail(function (r, event) {
|
||||
read_function: function() {
|
||||
return self.dataset.read_ids.apply(self.dataset, arguments).fail(function (r) {
|
||||
if (!r.data.message) { //else manage by openerp
|
||||
throw new Error(r);
|
||||
}
|
||||
@@ -171,7 +151,7 @@ var QuickCreate = Dialog.extend({
|
||||
def.resolve();
|
||||
}
|
||||
});
|
||||
pop.on('create_completed', self, function(id) {
|
||||
pop.on('create_completed', self, function() {
|
||||
created = true;
|
||||
self.trigger('slowadded');
|
||||
});
|
||||
@@ -231,13 +211,12 @@ var SidebarFilter = Widget.extend({
|
||||
this.$el.html(QWeb.render('CalendarView.sidebar.responsible', { filters: filters }));
|
||||
},
|
||||
filter_click: function(e) {
|
||||
var self = this;
|
||||
if (self.view.all_filters[0] && e.target.value == self.view.all_filters[0].value) {
|
||||
self.view.all_filters[0].is_checked = e.target.checked;
|
||||
if (this.view.all_filters[0] && e.target.value === this.view.all_filters[0].value) {
|
||||
this.view.all_filters[0].is_checked = e.target.checked;
|
||||
} else {
|
||||
self.view.all_filters[e.target.value].is_checked = e.target.checked;
|
||||
this.view.all_filters[e.target.value].is_checked = e.target.checked;
|
||||
}
|
||||
self.view.$calendar.fullCalendar('refetchEvents');
|
||||
this.view.$calendar.fullCalendar('refetchEvents');
|
||||
},
|
||||
select_previous: function(e) {
|
||||
$(e.target).siblings('input').trigger('click');
|
||||
|
||||
@@ -18,45 +18,17 @@ var QWeb = core.qweb;
|
||||
var DiagramView = View.extend({
|
||||
display_name: _lt('Diagram'),
|
||||
icon: 'fa-code-fork',
|
||||
view_type: 'diagram',
|
||||
searchable: false,
|
||||
multi_record: false,
|
||||
init: function(parent, dataset, view_id, options) {
|
||||
this._super(parent);
|
||||
this.set_default_options(options);
|
||||
this.view_manager = parent;
|
||||
this.dataset = dataset;
|
||||
this.model = this.dataset.model;
|
||||
this.view_id = view_id;
|
||||
searchable: false,
|
||||
|
||||
init: function() {
|
||||
this._super.apply(this, arguments);
|
||||
this.domain = this.dataset._domain || [];
|
||||
this.context = {};
|
||||
this.ids = this.dataset.ids;
|
||||
},
|
||||
|
||||
view_loading: function(r) {
|
||||
if (window.Raphael) {
|
||||
return this.load_diagram(r);
|
||||
if (this.ids && this.ids.length) {
|
||||
this.id = this.ids[this.dataset.index || 0];
|
||||
}
|
||||
var self = this;
|
||||
return $.when(
|
||||
ajax.loadJS('/web_diagram/static/lib/js/jquery.mousewheel.js'),
|
||||
ajax.loadJS('/web_diagram/static/lib/js/raphael.js')).then(function () {
|
||||
return self.load_diagram(r);
|
||||
});
|
||||
},
|
||||
|
||||
toTitleCase: function(str) {
|
||||
return str.replace(/\w\S*/g, function(txt){return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();});
|
||||
},
|
||||
|
||||
load_diagram: function(result) {
|
||||
var self = this;
|
||||
if(this.ids && this.ids.length) {
|
||||
this.id = this.ids[self.dataset.index || 0];
|
||||
}
|
||||
|
||||
this.fields_view = result;
|
||||
this.view_id = this.fields_view.view_id;
|
||||
this.fields = this.fields_view.fields;
|
||||
this.nodes = this.fields_view.arch.children[0];
|
||||
this.connectors = this.fields_view.arch.children[1];
|
||||
@@ -65,19 +37,35 @@ var DiagramView = View.extend({
|
||||
this.labels = _.filter(this.fields_view.arch.children, function(label) {
|
||||
return label.tag === "label";
|
||||
});
|
||||
},
|
||||
|
||||
this.$el.html(QWeb.render("DiagramView", {'widget': self}));
|
||||
this.$el.addClass('o_diagram_view').addClass(this.fields_view.arch.attrs['class']);
|
||||
willStart: function() {
|
||||
if (window.Raphael) {
|
||||
return $.when();
|
||||
}
|
||||
return $.when(
|
||||
ajax.loadJS('/web_diagram/static/lib/js/jquery.mousewheel.js'),
|
||||
ajax.loadJS('/web_diagram/static/lib/js/raphael.js')
|
||||
);
|
||||
},
|
||||
start: function() {
|
||||
var self = this;
|
||||
this.$el.html(QWeb.render("DiagramView", {'widget': this}));
|
||||
this.$el.addClass('o_diagram_view').addClass(this.fields_view.arch.attrs.class);
|
||||
|
||||
_.each(self.labels,function(label){
|
||||
_.each(this.labels,function(label){
|
||||
self.$('.o_diagram_header').append($('<span>').html(label.attrs.string));
|
||||
});
|
||||
|
||||
|
||||
var diagram_info;
|
||||
if(this.id) {
|
||||
self.get_diagram_info();
|
||||
diagram_info = this.get_diagram_info();
|
||||
}
|
||||
this.trigger('diagram_view_loaded', result);
|
||||
return $.when(this._super(), diagram_info);
|
||||
},
|
||||
|
||||
toTitleCase: function(str) {
|
||||
return str.replace(/\w\S*/g, function(txt){return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();});
|
||||
},
|
||||
|
||||
get_diagram_info: function() {
|
||||
@@ -112,7 +100,7 @@ var DiagramView = View.extend({
|
||||
params.connectors_fields.push(self.fields[conn.attrs.name]['string']|| this.toTitleCase(conn.attrs.name));
|
||||
params.connectors.push(conn.attrs.name);
|
||||
});
|
||||
this.rpc(
|
||||
return this.rpc(
|
||||
'/web_diagram/diagram/get_diagram_info',params).done(function(result) {
|
||||
self.draw_diagram(result);
|
||||
}
|
||||
@@ -277,7 +265,7 @@ var DiagramView = View.extend({
|
||||
var title = _t('Activity');
|
||||
var pop = new form_common.FormViewDialog(self, {
|
||||
res_model: self.node,
|
||||
domain: self.dataset.domain,
|
||||
domain: self.domain,
|
||||
context: self.context || self.dataset.context,
|
||||
title: _t("Create:") + title,
|
||||
disable_multiple_selection: true,
|
||||
@@ -321,7 +309,7 @@ var DiagramView = View.extend({
|
||||
var title = _t('Transition');
|
||||
var pop = new form_common.FormViewDialog(self, {
|
||||
res_model: self.connector,
|
||||
domain: this.dataset.domain,
|
||||
domain: this.domain,
|
||||
context: this.context || this.dataset.context,
|
||||
title: _t("Create:") + title,
|
||||
disable_multiple_selection: true,
|
||||
|
||||
@@ -26,11 +26,6 @@ var fields_registry = kanban_widgets.registry;
|
||||
var KanbanView = View.extend({
|
||||
accesskey: "K",
|
||||
className: "o_kanban_view",
|
||||
display_name: _lt("Kanban"),
|
||||
icon: 'fa-th-large',
|
||||
mobile_friendly: true,
|
||||
view_type: "kanban",
|
||||
|
||||
custom_events: {
|
||||
'kanban_record_open': 'open_record',
|
||||
'kanban_record_edit': 'edit_record',
|
||||
@@ -49,27 +44,30 @@ var KanbanView = View.extend({
|
||||
'kanban_load_more': 'load_more',
|
||||
'kanban_call_method': 'call_method',
|
||||
},
|
||||
defaults: _.extend(View.prototype.defaults, {
|
||||
quick_creatable: true,
|
||||
creatable: true,
|
||||
create_text: undefined,
|
||||
read_only_mode: false,
|
||||
confirm_on_delete: true,
|
||||
}),
|
||||
display_name: _lt("Kanban"),
|
||||
icon: 'fa-th-large',
|
||||
mobile_friendly: true,
|
||||
|
||||
init: function (parent, dataset, view_id, options) {
|
||||
this._super(parent, dataset, view_id, options);
|
||||
_.defaults(this.options, {
|
||||
"quick_creatable": true,
|
||||
"creatable": true,
|
||||
"create_text": undefined,
|
||||
"read_only_mode": false,
|
||||
"confirm_on_delete": true,
|
||||
});
|
||||
init: function () {
|
||||
this._super.apply(this, arguments);
|
||||
|
||||
// qweb setup
|
||||
this.qweb = new QWeb2.Engine();
|
||||
this.qweb.debug = session.debug;
|
||||
this.qweb.default_dict = _.clone(QWeb.default_dict);
|
||||
|
||||
this.model = this.dataset.model;
|
||||
this.limit = options.limit || 40;
|
||||
this.limit = this.options.limit || 40;
|
||||
this.fields_keys = _.keys(this.fields_view.fields);
|
||||
this.grouped = undefined;
|
||||
this.group_by_field = undefined;
|
||||
this.default_group_by = undefined;
|
||||
this.default_group_by = this.fields_view.arch.attrs.default_group_by;
|
||||
this.grouped_by_m2o = undefined;
|
||||
this.relation = undefined;
|
||||
this.is_empty = undefined;
|
||||
@@ -77,39 +75,39 @@ var KanbanView = View.extend({
|
||||
this.m2m_context = {};
|
||||
this.widgets = [];
|
||||
this.data = undefined;
|
||||
this.model = dataset.model;
|
||||
this.quick_creatable = options.quick_creatable;
|
||||
this.no_content_msg = options.action && (options.action.get_empty_list_help || options.action.help);
|
||||
this.quick_creatable = this.options.quick_creatable;
|
||||
this.no_content_msg = this.options.action &&
|
||||
(this.options.action.get_empty_list_help || this.options.action.help);
|
||||
this.search_orderer = new utils.DropMisordered();
|
||||
},
|
||||
|
||||
view_loading: function(fvg) {
|
||||
this.$el.addClass(fvg.arch.attrs.class);
|
||||
this.fields_view = fvg;
|
||||
this.default_group_by = fvg.arch.attrs.default_group_by;
|
||||
|
||||
this.fields_keys = _.keys(this.fields_view.fields);
|
||||
|
||||
// use default order if defined in xml description
|
||||
var default_order = this.fields_view.arch.attrs.default_order;
|
||||
if (!this.dataset._sort.length && default_order) {
|
||||
this.dataset.set_sort(default_order.split(','));
|
||||
}
|
||||
},
|
||||
|
||||
willStart: function() {
|
||||
// add qweb templates
|
||||
for (var i=0, ii=this.fields_view.arch.children.length; i < ii; i++) {
|
||||
var child = this.fields_view.arch.children[i];
|
||||
if (child.tag === "templates") {
|
||||
transform_qweb_template(child, fvg, this.many2manys);
|
||||
transform_qweb_template(child, this.fields_view, this.many2manys);
|
||||
this.qweb.add_template(utils.json_node_to_xml(child));
|
||||
break;
|
||||
} else if (child.tag === 'field') {
|
||||
var ftype = child.attrs.widget || this.fields_view.fields[child.attrs.name].type;
|
||||
if(ftype == "many2many" && "context" in child.attrs) {
|
||||
if(ftype === "many2many" && "context" in child.attrs) {
|
||||
this.m2m_context[child.attrs.name] = child.attrs.context;
|
||||
}
|
||||
}
|
||||
}
|
||||
this.trigger('kanban_view_loaded');
|
||||
return this._super();
|
||||
},
|
||||
|
||||
start: function() {
|
||||
this.$el.addClass(this.fields_view.arch.attrs.class);
|
||||
return this._super();
|
||||
},
|
||||
|
||||
do_search: function(domain, context, group_by) {
|
||||
@@ -254,7 +252,7 @@ var KanbanView = View.extend({
|
||||
var is_empty = true;
|
||||
return $.when.apply(null, _.map(groups, function (group) {
|
||||
var def = $.when([]);
|
||||
var dataset = new data.DataSetSearch(self, self.dataset.model,
|
||||
var dataset = new data.DataSetSearch(self, self.model,
|
||||
new data.CompoundContext(self.dataset.get_context(), group.model.context()), group.model.domain());
|
||||
if (self.dataset._sort) {
|
||||
dataset.set_sort(self.dataset._sort);
|
||||
@@ -494,7 +492,7 @@ var KanbanView = View.extend({
|
||||
context.set_eval_context({
|
||||
active_id: event.target.id,
|
||||
active_ids: [event.target.id],
|
||||
active_model: this.dataset.model,
|
||||
active_model: this.model,
|
||||
});
|
||||
this.do_execute_action(event.data, this.dataset, event.target.id).then(function () {
|
||||
self.reload_record(event.target);
|
||||
@@ -655,7 +653,7 @@ var KanbanView = View.extend({
|
||||
event.preventDefault();
|
||||
var popup = new form_common.SelectCreatePopup(this);
|
||||
popup.select_element(
|
||||
self.dataset.model,
|
||||
self.model,
|
||||
{
|
||||
title: _t("Create: "),
|
||||
initial_view: "form",
|
||||
@@ -683,7 +681,7 @@ var KanbanView = View.extend({
|
||||
model.call('create', [{name: event.data.value}], {
|
||||
context: this.search_context,
|
||||
}).then(function (id) {
|
||||
var dataset = new data.DataSetSearch(self, self.dataset.model, self.dataset.get_context(), []);
|
||||
var dataset = new data.DataSetSearch(self, self.model, self.dataset.get_context(), []);
|
||||
var group_data = {
|
||||
records: [],
|
||||
title: event.data.value,
|
||||
|
||||
@@ -1532,6 +1532,37 @@ class BaseModel(object):
|
||||
|
||||
return view
|
||||
|
||||
def load_views(self, cr, uid, views, options=None, context=None):
|
||||
""" Returns the fields_views of given views, and optionally filters and fields.
|
||||
|
||||
:param views: list of [view_id, view_type]
|
||||
:param options.toolbar: true to include contextual actions when loading fields_views
|
||||
:param options.action_id: id of the action to get the filters
|
||||
:param options.load_fields: true to load the model's fields
|
||||
:return: dictionary with fields_views, filters and fields
|
||||
"""
|
||||
if options is None:
|
||||
options = {}
|
||||
if context is None:
|
||||
context = {}
|
||||
result = {}
|
||||
|
||||
toolbar = options.get('toolbar')
|
||||
result['fields_views'] = {
|
||||
v_type: self.fields_view_get(cr, uid, v_id, v_type if v_type != 'list' else 'tree',
|
||||
toolbar=toolbar if v_type != 'search' else False,
|
||||
context=context)
|
||||
for [v_id, v_type] in views
|
||||
}
|
||||
|
||||
if 'search' in result['fields_views']:
|
||||
result['filters'] = self.pool['ir.filters'].get_filters(cr, uid, self._name, options.get('action_id'), context=context)
|
||||
|
||||
if options.get('load_fields'):
|
||||
result['fields'] = self.fields_get(cr, uid, context=context)
|
||||
|
||||
return result
|
||||
|
||||
def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
|
||||
""" fields_view_get([view_id | view_type='form'])
|
||||
|
||||
|
||||
Reference in New Issue
Block a user