[MERGE] Merged latest trunk branch.Improved dashboard using addwidget api.
bzr revid: vda@tinyerp.com-20110427102456-icjhuk24ke8x8cbg
This commit is contained in:
@@ -409,6 +409,11 @@ class DataSet(openerpweb.Controller):
|
||||
r = getattr(m, method)(ids, *args)
|
||||
return {'result': r}
|
||||
|
||||
@openerpweb.jsonrequest
|
||||
def exec_workflow(self, req, model, id, signal):
|
||||
r = req.session.exec_workflow(model, id, signal)
|
||||
return {'result': r}
|
||||
|
||||
@openerpweb.jsonrequest
|
||||
def default_get(self, req, model, fields, context={}):
|
||||
m = req.session.model(model)
|
||||
@@ -573,19 +578,22 @@ class ListView(View):
|
||||
:param int limit: search limit, for pagination
|
||||
:returns: hell if I have any idea yet
|
||||
"""
|
||||
view = self.fields_view_get(request, model, id)
|
||||
view = self.fields_view_get(request, model, id, toolbar=True)
|
||||
|
||||
rows = DataSet().do_search_read(request, model,
|
||||
offset=offset, limit=limit,
|
||||
domain=domain)
|
||||
eval_context = request.session.evaluation_context(
|
||||
request.context)
|
||||
return [
|
||||
{'data': dict((key, {'value': value})
|
||||
for key, value in row.iteritems()),
|
||||
'color': self.process_colors(view, row, eval_context)}
|
||||
for row in rows
|
||||
]
|
||||
return {
|
||||
'view': view,
|
||||
'records': [
|
||||
{'data': dict((key, {'value': value})
|
||||
for key, value in row.iteritems()),
|
||||
'color': self.process_colors(view, row, eval_context)}
|
||||
for row in rows
|
||||
]
|
||||
}
|
||||
|
||||
def process_colors(self, view, row, context):
|
||||
colors = view['arch']['attrs'].get('colors')
|
||||
@@ -618,5 +626,12 @@ class Action(openerpweb.Controller):
|
||||
_cp_path = "/base/action"
|
||||
|
||||
@openerpweb.jsonrequest
|
||||
def load(self, req, action_id):
|
||||
return {}
|
||||
def load(self, req, action_id, context={}):
|
||||
Actions = req.session.model('ir.actions.actions')
|
||||
value = False
|
||||
action_type = Actions.read([action_id], ['type'], context)
|
||||
if action_type:
|
||||
action = req.session.model(action_type[0]['type']).read([action_id], False, context)
|
||||
if action:
|
||||
value = action[0]
|
||||
return {'result': value}
|
||||
|
||||
@@ -336,8 +336,9 @@ body.openerp {
|
||||
}
|
||||
|
||||
/* Main Application */
|
||||
.openerp .application {
|
||||
.openerp .oe-application {
|
||||
padding: 0px;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.openerp h2.oe_view_title {
|
||||
@@ -486,6 +487,9 @@ body.openerp {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.openerp .oe-listview .oe-field-cell {
|
||||
cursor: pointer;
|
||||
}
|
||||
.openerp .oe-listview .oe-field-cell button {
|
||||
padding: 0;
|
||||
border: none;
|
||||
@@ -636,7 +640,7 @@ body.openerp {
|
||||
.openerp td.required input, .openerp td.required select {
|
||||
background-color: #D2D2FF;
|
||||
}
|
||||
.openerp td.invalid input, .openerp td.invalid select {
|
||||
.openerp td.invalid input, .openerp td.invalid select, .openerp td.invalid textarea {
|
||||
background-color: #F66;
|
||||
border: 1px solid #D00;
|
||||
}
|
||||
@@ -673,13 +677,14 @@ body.openerp {
|
||||
}
|
||||
|
||||
.openerp .view-manager-main-content {
|
||||
width: 100%;
|
||||
padding: 6px;
|
||||
}
|
||||
|
||||
.openerp .view-manager-main-sidebar {
|
||||
width:0;
|
||||
padding:0;
|
||||
margin:0;
|
||||
width: 180px;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.openerp .sidebar-main-div {
|
||||
@@ -746,6 +751,7 @@ body.openerp {
|
||||
}
|
||||
|
||||
.openerp .toggle-sidebar {
|
||||
cursor: pointer;
|
||||
border-color: #D2CFCF;
|
||||
border-style: solid;
|
||||
border-width: 1px;
|
||||
|
||||
@@ -118,6 +118,13 @@ openerp.base.DataSet = openerp.base.Controller.extend( /** @lends openerp.base.
|
||||
ids: ids,
|
||||
args: args
|
||||
}, callback);
|
||||
},
|
||||
exec_workflow: function (id, signal, callback) {
|
||||
return this.rpc('/base/dataset/exec_workflow', {
|
||||
model: this.model,
|
||||
id: id,
|
||||
signal: signal
|
||||
}, callback);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -457,16 +457,14 @@ openerp.base.form.WidgetFrame = openerp.base.form.Widget.extend({
|
||||
}
|
||||
this.add_widget(widget);
|
||||
},
|
||||
add_widget: function(w) {
|
||||
if (!w.invisible) {
|
||||
var current_row = this.table[this.table.length - 1];
|
||||
if (current_row.length && (this.x + w.colspan) > this.columns) {
|
||||
current_row = this.add_row();
|
||||
}
|
||||
current_row.push(w);
|
||||
this.x += w.colspan;
|
||||
add_widget: function(widget) {
|
||||
var current_row = this.table[this.table.length - 1];
|
||||
if (current_row.length && (this.x + widget.colspan) > this.columns) {
|
||||
current_row = this.add_row();
|
||||
}
|
||||
return w;
|
||||
current_row.push(widget);
|
||||
this.x += widget.colspan;
|
||||
return widget;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -534,7 +532,7 @@ openerp.base.form.WidgetButton = openerp.base.form.Widget.extend({
|
||||
on_confirmed: function() {
|
||||
var attrs = this.node.attrs;
|
||||
if (attrs.special) {
|
||||
this.on_button_object({
|
||||
this.on_button_action({
|
||||
result : { type: 'ir.actions.act_window_close' }
|
||||
});
|
||||
} else {
|
||||
@@ -542,19 +540,20 @@ openerp.base.form.WidgetButton = openerp.base.form.Widget.extend({
|
||||
var context = _.extend({}, this.view.dataset.context, attrs.context || {});
|
||||
switch(type) {
|
||||
case 'object':
|
||||
return this.view.dataset.call(attrs.name, [this.view.datarecord.id], [context], this.on_button_object);
|
||||
break;
|
||||
return this.view.dataset.call(attrs.name, [this.view.datarecord.id], [context], this.on_button_action);
|
||||
case 'action':
|
||||
return this.rpc('/base/action/load', { action_id: parseInt(attrs.name) }, this.on_button_action);
|
||||
default:
|
||||
this.log(_.sprintf("Unsupported button type : %s", type));
|
||||
return this.view.dataset.exec_workflow(this.view.datarecord.id, attrs.name, this.on_button_action);
|
||||
}
|
||||
}
|
||||
},
|
||||
on_button_object: function(r) {
|
||||
if (r.result === false) {
|
||||
this.log("Button object returns false");
|
||||
} else if (r.result.constructor == Object) {
|
||||
on_button_action: function(r) {
|
||||
console.log("Got reesonse button", r)
|
||||
if (r.result && r.result.constructor == Object) {
|
||||
this.session.action_manager.do_action(r.result);
|
||||
} else {
|
||||
this.log("Button returned", r.result);
|
||||
this.view.reload();
|
||||
}
|
||||
}
|
||||
@@ -911,7 +910,6 @@ openerp.base.form.FieldReference = openerp.base.form.Field.extend({
|
||||
* Registry of form widgets, called by :js:`openerp.base.FormView`
|
||||
*/
|
||||
openerp.base.form.widgets = new openerp.base.Registry({
|
||||
'hpaned': 'openerp.base.form.Hpaned',
|
||||
'group' : 'openerp.base.form.WidgetFrame',
|
||||
'notebook' : 'openerp.base.form.WidgetNotebook',
|
||||
'separator' : 'openerp.base.form.WidgetSeparator',
|
||||
|
||||
@@ -10,9 +10,23 @@ openerp.base.ListView = openerp.base.Controller.extend(
|
||||
// whether the column headers should be displayed
|
||||
'header': true,
|
||||
// display addition button, with that label
|
||||
'addable': "New"
|
||||
'addable': "New",
|
||||
// whether the list view can be sorted, note that once a view has been
|
||||
// sorted it can not be reordered anymore
|
||||
'sortable': true,
|
||||
// whether the view rows can be reordered (via vertical drag & drop)
|
||||
'reorderable': true
|
||||
},
|
||||
/**
|
||||
* Core class for list-type displays.
|
||||
*
|
||||
* As a view, needs a number of view-related parameters to be correctly
|
||||
* instantiated, provides options and overridable methods for behavioral
|
||||
* customization.
|
||||
*
|
||||
* See constructor parameters and method documentations for information on
|
||||
* the default behaviors and possible options for the list view.
|
||||
*
|
||||
* @constructs
|
||||
* @param view_manager
|
||||
* @param session An OpenERP session object
|
||||
@@ -24,6 +38,8 @@ openerp.base.ListView = openerp.base.Controller.extend(
|
||||
* @param {Boolean} [options.header=true] should the list's header be displayed
|
||||
* @param {Boolean} [options.deletable=true] are the list rows deletable
|
||||
* @param {null|String} [options.addable="New"] should the new-record button be displayed, and what should its label be. Use ``null`` to hide the button.
|
||||
* @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(view_manager, session, element_id, dataset, view_id, options) {
|
||||
this._super(session, element_id);
|
||||
@@ -37,11 +53,42 @@ openerp.base.ListView = openerp.base.Controller.extend(
|
||||
|
||||
this.options = _.extend({}, this.defaults, options || {});
|
||||
},
|
||||
/**
|
||||
* View startup method, the default behavior is to set the ``oe-listview``
|
||||
* class on its root element and to perform an RPC load call.
|
||||
*
|
||||
* @returns {$.Deferred} loading promise
|
||||
*/
|
||||
start: function() {
|
||||
this.$element.addClass('oe-listview');
|
||||
return this.rpc("/base/listview/load", {"model": this.model, "view_id":this.view_id,
|
||||
toolbar:!!this.view_manager.sidebar}, this.on_loaded);
|
||||
return this.rpc("/base/listview/load", {
|
||||
model: this.model,
|
||||
view_id: this.view_id,
|
||||
toolbar: !!this.view_manager.sidebar
|
||||
}, this.on_loaded);
|
||||
},
|
||||
/**
|
||||
* 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
|
||||
* * Sets up its sidebar, if any
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
on_loaded: function(data) {
|
||||
var self = this;
|
||||
this.fields_view = data.fields_view;
|
||||
@@ -108,8 +155,9 @@ openerp.base.ListView = openerp.base.Controller.extend(
|
||||
|
||||
var context = _.extend(
|
||||
{}, self.dataset.context, field.context || {});
|
||||
self.dataset.call(action, [row.data.id.value], [context],
|
||||
self.do_reload);
|
||||
self.notification.notify(
|
||||
'[Dummy] Execute action', self.dataset.model + '#' + action + "([" + row.data.id.value + '])');
|
||||
// TODO: implement actions correctly once the corresponding manager has been extracted (and filled)
|
||||
e.stopImmediatePropagation();
|
||||
});
|
||||
$table.delegate(
|
||||
@@ -127,10 +175,18 @@ openerp.base.ListView = openerp.base.Controller.extend(
|
||||
/**
|
||||
* Fills the table with the provided records after emptying it
|
||||
*
|
||||
* @param {Array} records the records to fill the list view with
|
||||
* TODO: should also re-load the table itself, as e.g. columns may have changed
|
||||
*
|
||||
* @param {Object} result filling result
|
||||
* @param {Array} [result.view] the new view (wrapped fields_view_get result)
|
||||
* @param {Array} result.records records the records to fill the list view with
|
||||
* @returns {Promise} promise to the end of view rendering (list views are asynchronously filled for improved responsiveness)
|
||||
*/
|
||||
do_fill_table: function(records) {
|
||||
do_fill_table: function(result) {
|
||||
if (result.view) {
|
||||
this.on_loaded({fields_view: result.view});
|
||||
}
|
||||
var records = result.records;
|
||||
var $table = this.$element.find('table');
|
||||
this.rows = records;
|
||||
|
||||
@@ -188,14 +244,19 @@ openerp.base.ListView = openerp.base.Controller.extend(
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Asks the view manager to switch to a different view, using the provided
|
||||
* record index (within the current dataset).
|
||||
* Used to handle a click on a table row, if no other handler caught the
|
||||
* event.
|
||||
*
|
||||
* The default implementation asks the list view's view manager to switch
|
||||
* to a different view (by calling
|
||||
* :js:func:`~openerp.base.ViewManager.on_mode_switch`), using the
|
||||
* provided record index (within the current list view's dataset).
|
||||
*
|
||||
* If the index is null, ``switch_to_record`` asks for the creation of a
|
||||
* new record.
|
||||
*
|
||||
* @param {Number|null} index the record index (in the current dataset) to switch to
|
||||
* @param {String} [view="form"] the view to switch to
|
||||
* @param {String} [view="form"] the view type to switch to
|
||||
*/
|
||||
switch_to_record:function (index, view) {
|
||||
view = view || 'form';
|
||||
@@ -204,6 +265,16 @@ openerp.base.ListView = openerp.base.Controller.extend(
|
||||
this.view_manager.on_mode_switch(view);
|
||||
}, this));
|
||||
},
|
||||
/**
|
||||
* Base handler for clicking on a row, discovers the index of the record
|
||||
* corresponding to the clicked row in the list view's dataset.
|
||||
*
|
||||
* Should not be overridden, use
|
||||
* :js:func:`~openerp.base.ListView.switch_to_record` to customize the
|
||||
* behavior of the list view when clicking on a row instead.
|
||||
*
|
||||
* @param {Object} event jQuery DOM event object
|
||||
*/
|
||||
on_select_row: function (event) {
|
||||
var $target = $(event.currentTarget);
|
||||
if (!$target.parent().is('tbody')) {
|
||||
@@ -216,8 +287,8 @@ openerp.base.ListView = openerp.base.Controller.extend(
|
||||
if (index == undefined || index === -1) {
|
||||
return;
|
||||
}
|
||||
this.switch_to_record(index);
|
||||
},
|
||||
this.switch_to_record(index);
|
||||
},
|
||||
do_show: function () {
|
||||
this.$element.show();
|
||||
if (this.hidden) {
|
||||
@@ -246,6 +317,15 @@ openerp.base.ListView = openerp.base.Controller.extend(
|
||||
'domain': this.dataset.domain
|
||||
}, this.do_fill_table);
|
||||
},
|
||||
/**
|
||||
* Event handler for a search, asks for the computation/folding of domains
|
||||
* and contexts (and group-by), then reloads the view's content.
|
||||
*
|
||||
* @param {Array} domains a sequence of literal and non-literal domains
|
||||
* @param {Array} contexts a sequence of literal and non-literal contexts
|
||||
* @param {Array} groupbys a sequence of literal and non-literal group-by contexts
|
||||
* @returns {$.Deferred} fold request evaluation promise
|
||||
*/
|
||||
do_search: function (domains, contexts, groupbys) {
|
||||
var self = this;
|
||||
return this.rpc('/base/session/eval_domain_and_context', {
|
||||
@@ -266,7 +346,7 @@ openerp.base.ListView = openerp.base.Controller.extend(
|
||||
/**
|
||||
* Handles the signal to delete a line from the DOM
|
||||
*
|
||||
* @param e
|
||||
* @param e jQuery event object
|
||||
*/
|
||||
do_delete: function (e) {
|
||||
// don't link to forms
|
||||
@@ -295,7 +375,7 @@ openerp.base.ListView = openerp.base.Controller.extend(
|
||||
},
|
||||
/**
|
||||
* Gets the ids of all currently selected records, if any
|
||||
* @returns a list of ids, empty if no record is selected (or the list view is not selectable
|
||||
* @returns {Array} empty if no record is selected (or the list view is not selectable)
|
||||
*/
|
||||
get_selection: function () {
|
||||
if (!this.options.selectable) {
|
||||
@@ -307,6 +387,8 @@ openerp.base.ListView = openerp.base.Controller.extend(
|
||||
return rows[$(this).prevAll().length].data.id.value;
|
||||
}).get();
|
||||
}
|
||||
// TODO: implement sort (click on column headers), if sorted, the list can not be reordered anymore
|
||||
// TODO: implement reorder (drag and drop rows)
|
||||
});
|
||||
|
||||
openerp.base.TreeView = openerp.base.Controller.extend({
|
||||
|
||||
@@ -19,7 +19,6 @@ openerp.base.ActionManager = openerp.base.Controller.extend({
|
||||
* Supported actions: act_window
|
||||
*/
|
||||
do_action: function(action) {
|
||||
this.log(action);
|
||||
var self = this;
|
||||
// instantiate the right controllers by understanding the action
|
||||
switch (action.type) {
|
||||
@@ -36,14 +35,14 @@ openerp.base.ActionManager = openerp.base.Controller.extend({
|
||||
// When dialog is closed with ESC key or close manually, branch to act_window_close logic
|
||||
self.do_action({ type: 'ir.actions.act_window_close' });
|
||||
});
|
||||
var viewmanager = new openerp.base.ViewManagerAction(this.session ,element_id, action, false);
|
||||
var viewmanager = new openerp.base.ViewManagerAction(this.session, element_id, action, false);
|
||||
viewmanager.start();
|
||||
this.dialog_stack.push(viewmanager);
|
||||
} else if (action.target == "current") {
|
||||
} else {
|
||||
if (this.viewmanager) {
|
||||
this.viewmanager.stop();
|
||||
}
|
||||
this.viewmanager = new openerp.base.ViewManagerAction(this.session,this.element_id, action, true);
|
||||
this.viewmanager = new openerp.base.ViewManagerAction(this.session, this.element_id, action, true);
|
||||
this.viewmanager.start();
|
||||
}
|
||||
break;
|
||||
@@ -211,14 +210,17 @@ openerp.base.ViewManagerAction = openerp.base.ViewManager.extend({
|
||||
// init search view
|
||||
var searchview_id = this.action.search_view_id && this.action.search_view_id[0];
|
||||
|
||||
var searchview_loaded = this.setup_search_view(
|
||||
searchview_id || false, search_defaults);
|
||||
if (searchview_id) {
|
||||
var searchview_loaded = this.setup_search_view(
|
||||
searchview_id, search_defaults);
|
||||
|
||||
// schedule auto_search
|
||||
if (searchview_loaded != null && this.action['auto_search']) {
|
||||
$.when(searchview_loaded, inital_view_loaded)
|
||||
.then(this.searchview.do_search);
|
||||
// schedule auto_search
|
||||
if (this.action['auto_search']) {
|
||||
$.when(searchview_loaded, inital_view_loaded)
|
||||
.then(this.searchview.do_search);
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
stop: function() {
|
||||
// should be replaced by automatic destruction implemented in BaseWidget
|
||||
|
||||
@@ -18,9 +18,6 @@
|
||||
</div>
|
||||
</div>
|
||||
<div id="oe_login" class="login"></div>
|
||||
<div style="position: absolute; right: 2px; top: 38px;">
|
||||
<button onclick="QWeb.add_template('base.xml'); $('body').css('background-color', '#FFFF9C'); setTimeout(function() { $('body').css('background-color', '#FFF'); }, 500);">Reload QWEB</button>
|
||||
</div>
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" height="100%"
|
||||
t-att-class="'main_table' + (typeof kitten == 'undefined' ? '' : ' kitten-mode-activated')">
|
||||
<tr>
|
||||
@@ -33,10 +30,7 @@
|
||||
<td valign="top" id="oe_secondary_menu" class="secondary_menu">
|
||||
</td>
|
||||
<td valign="top" width="100%" height="100%">
|
||||
<div id="oe_main" class="main">
|
||||
<div id="oe_app" class="application"></div>
|
||||
<div id="oe_sidebar"></div>
|
||||
</div>
|
||||
<div id="oe_app" class="oe-application"></div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -135,23 +129,23 @@
|
||||
<t t-name="ViewManager">
|
||||
<table class="view-manager-main-table">
|
||||
<tr>
|
||||
<td class="view-manager-main-content">
|
||||
<!-- TODO prefix id with the element_id of the controller t-attf-id="#{prefix}_localid" -->
|
||||
<div class="oe_vm_switch">
|
||||
<t t-foreach="views" t-as="view">
|
||||
<button type="button" t-att-data-view-type="view[1]">
|
||||
<t t-esc="view[1]"/>
|
||||
</button>
|
||||
</t>
|
||||
</div>
|
||||
<div t-attf-id="#{prefix}_search"/>
|
||||
<t t-foreach="views" t-as="view">
|
||||
<div t-attf-id="#{prefix}_view_#{view[1]}"/>
|
||||
</t>
|
||||
</td>
|
||||
<td class="view-manager-main-sidebar">
|
||||
</td>
|
||||
</tr>
|
||||
<td class="view-manager-main-content">
|
||||
<!-- TODO prefix id with the element_id of the controller t-attf-id="#{prefix}_localid" -->
|
||||
<div class="oe_vm_switch">
|
||||
<t t-foreach="views" t-as="view">
|
||||
<button type="button" t-att-data-view-type="view[1]">
|
||||
<t t-esc="view[1]"/>
|
||||
</button>
|
||||
</t>
|
||||
</div>
|
||||
<div t-attf-id="#{prefix}_search" t-opentag="true"/>
|
||||
<t t-foreach="views" t-as="view">
|
||||
<div t-attf-id="#{prefix}_view_#{view[1]}"/>
|
||||
</t>
|
||||
</td>
|
||||
<td class="view-manager-main-sidebar">
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</t>
|
||||
<table t-name="ListView">
|
||||
@@ -217,7 +211,8 @@
|
||||
<t t-set="is_button" t-value="column.tag === 'button'"/>
|
||||
<!-- TODO: get correct widget from form -->
|
||||
<t t-if="!is_button and row['data'][column.id].value">
|
||||
<t t-esc="row['data'][column.id].value"/>
|
||||
<t t-set="value" t-value="row['data'][column.id].value"/>
|
||||
<t t-esc="value instanceof Array ? value[1] : value"/>
|
||||
</t>
|
||||
<button type="button" t-att-title="column.help"
|
||||
t-if="is_button">
|
||||
@@ -299,7 +294,7 @@
|
||||
t-att-ondblclick="'console.log(\'' + widget.element_id + '\', openerp.screen.' + widget.element_id + ')'">
|
||||
<t t-esc="widget.string"/>
|
||||
<span t-if="widget.help">?</span>
|
||||
<t t-if="widget.string">:</t>
|
||||
<t t-if="widget.string and widget.node.tag != 'label'">:</t>
|
||||
</label>
|
||||
</t>
|
||||
<t t-name="FieldChar">
|
||||
|
||||
@@ -34,15 +34,15 @@ $(document).ready(function () {
|
||||
asyncTest('render selection checkboxes', 2, function () {
|
||||
var listview = new openerp.base.ListView(
|
||||
{}, null,
|
||||
'qunit-fixture', {model: null});
|
||||
'qunit-fixture', {model: null, ids: [null, null, null], index: 0});
|
||||
|
||||
listview.on_loaded(fvg);
|
||||
|
||||
listview.do_fill_table([
|
||||
listview.do_fill_table({records: [
|
||||
{data: {id: {value: null}}},
|
||||
{data: {id: {value: null}}},
|
||||
{data: {id: {value: null}}}
|
||||
]).then(function () {
|
||||
]}).then(function () {
|
||||
ok(are(listview.$element.find('tbody th'),
|
||||
'.oe-record-selector'));
|
||||
ok(are(listview.$element.find('tbody th input'),
|
||||
@@ -53,30 +53,30 @@ $(document).ready(function () {
|
||||
asyncTest('render no checkbox if selectable=false', 1, function () {
|
||||
var listview = new openerp.base.ListView(
|
||||
{}, null,
|
||||
'qunit-fixture', {model: null}, false,
|
||||
'qunit-fixture', {model: null, ids: [null, null, null], index: 0}, false,
|
||||
{selectable: false});
|
||||
|
||||
listview.on_loaded(fvg);
|
||||
|
||||
listview.do_fill_table([
|
||||
listview.do_fill_table({records: [
|
||||
{data: {id: {value: null}}},
|
||||
{data: {id: {value: null}}},
|
||||
{data: {id: {value: null}}}
|
||||
]).then(function () {
|
||||
]}).then(function () {
|
||||
equal(listview.$element.find('tbody th').length, 0);
|
||||
start();
|
||||
});
|
||||
});
|
||||
asyncTest('select a bunch of records', 2, function () {
|
||||
var listview = new openerp.base.ListView(
|
||||
{}, null, 'qunit-fixture', {model: null});
|
||||
{}, null, 'qunit-fixture', {model: null, ids: [1, 2, 3], index: 0});
|
||||
listview.on_loaded(fvg);
|
||||
|
||||
listview.do_fill_table([
|
||||
listview.do_fill_table({records: [
|
||||
{data: {id: {value: 1}}},
|
||||
{data: {id: {value: 2}}},
|
||||
{data: {id: {value: 3}}}
|
||||
]).then(function () {
|
||||
]}).then(function () {
|
||||
listview.$element.find('tbody th input:eq(2)')
|
||||
.attr('checked', true);
|
||||
deepEqual(listview.get_selection(), [3]);
|
||||
@@ -88,15 +88,15 @@ $(document).ready(function () {
|
||||
});
|
||||
asyncTest('render deletion button if list is deletable', 1, function () {
|
||||
var listview = new openerp.base.ListView(
|
||||
{}, null, 'qunit-fixture', {model: null});
|
||||
{}, null, 'qunit-fixture', {model: null, ids: [null, null, null], index: 0});
|
||||
|
||||
listview.on_loaded(fvg);
|
||||
|
||||
listview.do_fill_table([
|
||||
listview.do_fill_table({records: [
|
||||
{data: {id: {value: null}}},
|
||||
{data: {id: {value: null}}},
|
||||
{data: {id: {value: null}}}
|
||||
]).then(function () {
|
||||
]}).then(function () {
|
||||
equal(
|
||||
listview.$element.find('tbody tr td.oe-record-delete button').length,
|
||||
3);
|
||||
@@ -109,15 +109,15 @@ $(document).ready(function () {
|
||||
var listview = new openerp.base.ListView(
|
||||
{}, null, 'qunit-fixture', {model: null, unlink: function (ids) {
|
||||
deleted = ids;
|
||||
}});
|
||||
}, ids: [1, 2, 3], index: 0});
|
||||
|
||||
listview.on_loaded(fvg);
|
||||
|
||||
listview.do_fill_table([
|
||||
listview.do_fill_table({records: [
|
||||
{data: {id: {value: 1}}},
|
||||
{data: {id: {value: 2}}},
|
||||
{data: {id: {value: 3}}}
|
||||
]).then(function () {
|
||||
]}).then(function () {
|
||||
listview.$element.find('tbody td.oe-record-delete:eq(2) button').click();
|
||||
deepEqual(deleted, [3]);
|
||||
listview.$element.find('tbody td.oe-record-delete:eq(0) button').click();
|
||||
@@ -130,15 +130,15 @@ $(document).ready(function () {
|
||||
var listview = new openerp.base.ListView(
|
||||
{}, null, 'qunit-fixture', {model: null, unlink: function (ids) {
|
||||
deleted = ids;
|
||||
}});
|
||||
}, ids: [1, 2, 3], index: 0});
|
||||
|
||||
listview.on_loaded(fvg);
|
||||
|
||||
listview.do_fill_table([
|
||||
listview.do_fill_table({records: [
|
||||
{data: {id: {value: 1}}},
|
||||
{data: {id: {value: 2}}},
|
||||
{data: {id: {value: 3}}}
|
||||
]).then(function () {
|
||||
]}).then(function () {
|
||||
listview.$element.find('tbody th input:eq(2)')
|
||||
.attr('checked', true);
|
||||
listview.$element.find('tbody th input:eq(1)')
|
||||
|
||||
@@ -5,5 +5,13 @@ class Dashboard(View):
|
||||
_cp_path = "/base_dashboard/dashboard"
|
||||
|
||||
@openerpweb.jsonrequest
|
||||
def load(self, req, node):
|
||||
return
|
||||
def load(self, req, node_attrs):
|
||||
|
||||
self.action_id = int(node_attrs['name'])
|
||||
actions = req.session.model('ir.actions.actions')
|
||||
result = actions.read([self.action_id],['type'], req.session.context)
|
||||
if not result:
|
||||
raise _('Action not found!')
|
||||
self.action = req.session.model(result[0]['type']).read([self.action_id], False, req.session.context)[0]
|
||||
|
||||
return {'action': self.action}
|
||||
@@ -1,89 +1,70 @@
|
||||
openerp.base_dashboard = function(openerp) {
|
||||
openerp.base_dashboard = function(openerp){
|
||||
|
||||
QWeb.add_template('/base_dashboard/static/src/xml/base_dashboard.xml');
|
||||
openerp.base.form.Hpaned = openerp.base.form.Widget.extend({
|
||||
init: function(view, node) {
|
||||
|
||||
openerp.base.form.Board = openerp.base.form.Widget.extend({
|
||||
init: function(view, node) {
|
||||
|
||||
this._super(view, node);
|
||||
this.template = "Hpaned";
|
||||
this.template = "Board";
|
||||
},
|
||||
start: function() {
|
||||
this._super.apply(this, arguments);
|
||||
this.$element.html(QWeb.render(this.template))
|
||||
this.$element.html(QWeb.render(this.template));
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var self = this;
|
||||
jQuery('body').append(
|
||||
jQuery('<div>', {'id': 'dashboard_templates'}).hide()
|
||||
);
|
||||
jQuery('<div>', {'id': 'dashboard_template'}).load('/base_dashboard/static/src/dashboard_template.html',self.on_loaded).hide()
|
||||
)
|
||||
|
||||
$("#dashboard_templates").load("/base_dashboard/static/src/dashboard_template.html", function(result){
|
||||
self.render_dashboard()
|
||||
});
|
||||
},
|
||||
|
||||
render_dashboard: function() {
|
||||
var self = this;
|
||||
var data = {"layout": "layout2","data": []}
|
||||
var get_column = ['first', 'second', 'third'];
|
||||
on_loaded: function() {
|
||||
var children = this.node.children;
|
||||
|
||||
var action = []
|
||||
for (child in children) {
|
||||
action.push({})
|
||||
var ch_widget = children[child]['children'];
|
||||
for (ch in ch_widget) {
|
||||
if (ch_widget[ch].tag == 'action') {
|
||||
// var _act = new openerp.base.form.Action(this.view, ch_widget[ch]);
|
||||
// action[child][ch] = _act.render();
|
||||
data['data'].push({
|
||||
"title": ch_widget[ch].attrs.string,
|
||||
"id": ch_widget[ch].attrs.name,
|
||||
"column": get_column[child],
|
||||
"url": "/base_dashboard/static/data.html",
|
||||
"open": true
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var get_column = ['first', 'second', 'third'];
|
||||
var board = jQuery('#dashboard').dashboard({
|
||||
layoutClass:'layout',
|
||||
json_data: data
|
||||
layoutClass:'layout'
|
||||
});
|
||||
board.init();
|
||||
for(var ch = 0; ch < children.length; ch++) {
|
||||
var ch_widgets = children[ch].children;
|
||||
for(var chld = 0; chld < ch_widgets.length; chld++) {
|
||||
var widget_type = ch_widgets[chld].tag;
|
||||
var board_element = board.element.find('[id=column-'+get_column[chld]+']');
|
||||
var widget = new (openerp.base.form.widgets.get_object(widget_type)) (this.view, ch_widgets[chld], board_element);
|
||||
board.addWidget({
|
||||
'id': ch_widgets[chld].attrs.name,
|
||||
'title': ch_widgets[chld].attrs.string,
|
||||
'url': widget.start()
|
||||
}, board_element)
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
openerp.base.form.Action = openerp.base.form.Widget.extend({
|
||||
init: function(view, node) {
|
||||
this._super(view, node);
|
||||
init: function(view, node, column) {
|
||||
this._super(view, node, column);
|
||||
this.template = "Action";
|
||||
},
|
||||
start: function() {
|
||||
this._super.apply(this, arguments);
|
||||
this.rpc('/base_dashboard/dashboard/load',{
|
||||
node_attrs: this.node.attrs
|
||||
},
|
||||
this.on_load_action);
|
||||
},
|
||||
|
||||
render: function(){
|
||||
return QWeb.render(this.template, {'node': this.node});
|
||||
on_load_action: function(result) {
|
||||
var action = result.action;
|
||||
action_manager = new openerp.base.ActionManager(this.session, this.$element.attr('id'));
|
||||
action_manager.start();
|
||||
action_manager.do_action(action);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
openerp.base.form.Vpaned = openerp.base.form.Widget.extend({
|
||||
init: function(view, node) {
|
||||
this._super(view, node);
|
||||
this.template = "Vpaned";
|
||||
},
|
||||
start: function() {
|
||||
this._super.apply(this, arguments);
|
||||
},
|
||||
|
||||
render: function(){
|
||||
return QWeb.render(this.template);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
openerp.base.form.widgets.add('hpaned', 'openerp.base.form.Board');
|
||||
openerp.base.form.widgets.add('action', 'openerp.base.form.Action');
|
||||
}
|
||||
@@ -1,17 +1,12 @@
|
||||
<template>
|
||||
<t t-name="Hpaned">
|
||||
<div>HPANED>>>>>>>>>>>>>>>>>>>>>>>>></div>
|
||||
<t t-name="Board">
|
||||
<div id="dashboard" class="dashboard">
|
||||
<!-- this HTML covers all layouts. The 5 different layouts are handled by setting another layout classname -->
|
||||
<div class="layout">
|
||||
<div class="column first column-first"></div>
|
||||
<div class="column second column-second"></div>
|
||||
<div class="column third column-third"></div>
|
||||
<div id="column-first" class="column first column-first"></div>
|
||||
<div id="column-second" class="column second column-second"></div>
|
||||
<div id="column-third" class="column third column-third"></div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
<t t-name="Vpaned">
|
||||
</t>
|
||||
<t t-name="Action">
|
||||
<t t-esc="node.attrs.string"/>
|
||||
</t>
|
||||
</template>
|
||||
+14
-13
@@ -55,7 +55,7 @@ Events
|
||||
Event triggered after a user asked for a search. The search view
|
||||
fires this event after collecting all input data (contexts, domains
|
||||
and group_by contexts). Note that the search view does *not* merge
|
||||
those (or therwise evaluate them), they are returned as provided by
|
||||
those (or otherwise evaluate them), they are returned as provided by
|
||||
the various inputs within the view.
|
||||
|
||||
``on_clear``
|
||||
@@ -71,7 +71,7 @@ An important concept in the search view is that of input. It is both
|
||||
an informal protocol and an abstract type that can be inherited from.
|
||||
|
||||
Inputs are widgets which can contain user data (a char widget for
|
||||
instance, or a selection box). They are able of action and of
|
||||
instance, or a selection box). They are capable of action and of
|
||||
reaction:
|
||||
|
||||
.. _views-search-registration:
|
||||
@@ -93,9 +93,9 @@ reaction:
|
||||
* Return a context (an object), this is the "normal" response if the
|
||||
input holds a value.
|
||||
|
||||
* Return a falsy value (generally ``null``). This value indicates
|
||||
the input does not contain any value and will not take part in the
|
||||
research.
|
||||
* Return a value that evaluates as false (generally ``null``). This
|
||||
value indicates the input does not contain any value and will not
|
||||
affect the results of the search.
|
||||
|
||||
* Raise :js:class:`openerp.base.search.Invalid` to indicate that it
|
||||
holds a value but this value can not be used in the search
|
||||
@@ -104,7 +104,7 @@ reaction:
|
||||
the search process.
|
||||
|
||||
:js:class:`~openerp.base.search.Invalid` takes three mandatory
|
||||
arguments: an indentifier (a name for instance), the invalid value
|
||||
arguments: an identifier (a name for instance), the invalid value,
|
||||
and a validation message indicating the issue.
|
||||
|
||||
``get_domain``
|
||||
@@ -114,11 +114,11 @@ reaction:
|
||||
|
||||
The :js:class:`openerp.base.search.Input` type implements registration
|
||||
on its own, but its implementations of ``get_context`` and
|
||||
``get_domain`` simply raise errors and *have* to be overridden.
|
||||
``get_domain`` simply raise errors and *must* be overridden.
|
||||
|
||||
One last action is for filters, as an activation order has to be kept
|
||||
on them for some controls (establish the correct grouping sequence for
|
||||
instance).
|
||||
on them for some controls (to establish the correct grouping sequence,
|
||||
for instance).
|
||||
|
||||
To that end, filters can call
|
||||
:js:func:`openerp.base.Search.do_toggle_filter`, providing themselves
|
||||
@@ -137,7 +137,7 @@ Life cycle
|
||||
|
||||
The search view has a pretty simple and linear life cycle, in three main steps:
|
||||
|
||||
:js:class:`init <openerp.base.SearchView>`
|
||||
:js:class:`~openerp.base.SearchView.init`
|
||||
|
||||
Nothing interesting happens here
|
||||
|
||||
@@ -211,7 +211,7 @@ called in this order:
|
||||
Gives the widget the opportunity to unbind its events, remove itself
|
||||
from the DOM and perform any other cleanup task it may have.
|
||||
|
||||
Event if the widget does not do anything itself, it is responsible
|
||||
Even if the widget does not do anything itself, it is responsible
|
||||
for shutting down its children.
|
||||
|
||||
An abstract type is available and can be inherited from, to simplify
|
||||
@@ -232,7 +232,8 @@ abstract types, used to implement input widgets:
|
||||
<views-search-registration>`.
|
||||
|
||||
If inherited from, descendant classes should not call its
|
||||
implementations of ``get_context`` and ``get_domain``.
|
||||
implementations of :js:func:`~openerp.base.search.Input.get_context`
|
||||
and :js:func:`~openerp.base.search.Input.get_domain`.
|
||||
|
||||
* :js:class:`openerp.base.search.Field` is used to implement more
|
||||
"field" widgets (which allow the user to input potentially complex
|
||||
@@ -251,7 +252,7 @@ abstract types, used to implement input widgets:
|
||||
:js:func:`~openerp.base.search.Widget.make_id`.
|
||||
|
||||
* It sets up a basic (overridable)
|
||||
:js:attr:`~opererp.base.search.Field.template` attribute, combined
|
||||
:js:attr:`~openerp.base.search.Field.template` attribute, combined
|
||||
with the previous tasks, this makes subclasses of
|
||||
:js:class:`~openerp.base.search.Field` render themselves "for
|
||||
free".
|
||||
|
||||
@@ -31,7 +31,7 @@ Guides and main documentation
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The meat and most important part of all documentation. Should be
|
||||
written in plain english, using reStructuredText_ and taking advantage
|
||||
written in plain English, using reStructuredText_ and taking advantage
|
||||
of `Sphinx's extensions`_, especially `cross-references`_.
|
||||
|
||||
Python API Documentation
|
||||
@@ -65,7 +65,7 @@ RST, using Sphinx's `Python domain`_ [#]_:
|
||||
and ``__new__``), using the `info fields`_ as well.
|
||||
|
||||
* Attributes (class and instance) should be documented in their
|
||||
class's docstrin via the ``.. attribute::`` directiveg, following
|
||||
class's docstring via the ``.. attribute::`` directive, following
|
||||
the class's own documentation.
|
||||
|
||||
* The relation between modules and module-level attributes is similar:
|
||||
@@ -220,7 +220,7 @@ Roadmap
|
||||
Release notes
|
||||
+++++++++++++
|
||||
|
||||
.. [#] because Python is the default domain, the ``py:`` markup prefix
|
||||
.. [#] Because Python is the default domain, the ``py:`` markup prefix
|
||||
is optional and should be left out.
|
||||
|
||||
.. [#] Resig's Class still uses prototypes under the hood, it doesn't
|
||||
|
||||
@@ -116,6 +116,12 @@ class OpenERPSession(object):
|
||||
r = self.proxy('object').execute(self._db, self._uid, self._password, model, func, *l, **d)
|
||||
return r
|
||||
|
||||
def exec_workflow(self, model, id, signal):
|
||||
if not (self._db and self._uid and self._password):
|
||||
raise OpenERPUnboundException()
|
||||
r = self.proxy('object').exec_workflow(self._db, self._uid, self._password, model, signal, id)
|
||||
return r
|
||||
|
||||
def model(self, model):
|
||||
""" Get an RPC proxy for the object ``model``, bound to this session.
|
||||
|
||||
@@ -289,13 +295,34 @@ class JsonRequest(object):
|
||||
* the json string is passed as a form parameter named "request"
|
||||
* method is currently ignored
|
||||
|
||||
Sucessful request:
|
||||
--> {"jsonrpc": "2.0", "method": "call", "params": {"session_id": "SID", "context": {}, "arg1": "val1" }, "id": null}
|
||||
<-- {"jsonrpc": "2.0", "result": { "res1": "val1" }, "id": null}
|
||||
Sucessful request::
|
||||
|
||||
Request producing a error:
|
||||
--> {"jsonrpc": "2.0", "method": "call", "params": {"session_id": "SID", "context": {}, "arg1": "val1" }, "id": null}
|
||||
<-- {"jsonrpc": "2.0", "error": {"code": 1, "message": "End user error message.", "data": {"code": "codestring", "debug": "traceback" } }, "id": null}
|
||||
--> {"jsonrpc": "2.0",
|
||||
"method": "call",
|
||||
"params": {"session_id": "SID",
|
||||
"context": {},
|
||||
"arg1": "val1" },
|
||||
"id": null}
|
||||
|
||||
<-- {"jsonrpc": "2.0",
|
||||
"result": { "res1": "val1" },
|
||||
"id": null}
|
||||
|
||||
Request producing a error::
|
||||
|
||||
--> {"jsonrpc": "2.0",
|
||||
"method": "call",
|
||||
"params": {"session_id": "SID",
|
||||
"context": {},
|
||||
"arg1": "val1" },
|
||||
"id": null}
|
||||
|
||||
<-- {"jsonrpc": "2.0",
|
||||
"error": {"code": 1,
|
||||
"message": "End user error message.",
|
||||
"data": {"code": "codestring",
|
||||
"debug": "traceback" } },
|
||||
"id": null}
|
||||
|
||||
"""
|
||||
|
||||
|
||||
Reference in New Issue
Block a user