[MERGE] latest trunk-proto61.
bzr revid: vda@tinyerp.com-20110517093550-zvxyvwm62nvqhy5z
This commit is contained in:
@@ -357,6 +357,10 @@ class DataSet(openerpweb.Controller):
|
||||
reads = Model.read(ids, fields or False, request.context)
|
||||
reads.sort(key=lambda obj: ids.index(obj['id']))
|
||||
return reads
|
||||
|
||||
@openerpweb.jsonrequest
|
||||
def read(self, request, model, ids, fields=False):
|
||||
return self.do_search_read(request, model, ids, fields)
|
||||
|
||||
@openerpweb.jsonrequest
|
||||
def get(self, request, model, ids, fields=False):
|
||||
@@ -424,6 +428,19 @@ class DataSet(openerpweb.Controller):
|
||||
m = req.session.model(model)
|
||||
r = m.default_get(fields, context)
|
||||
return {'result': r}
|
||||
|
||||
@openerpweb.jsonrequest
|
||||
def name_search(self, req, model, search_str, domain=[], context={}):
|
||||
m = req.session.model(model)
|
||||
r = m.name_search(search_str+'%', domain, '=ilike', context)
|
||||
return {'result': r}
|
||||
|
||||
class DataGroup(openerpweb.Controller):
|
||||
_cp_path = "/base/group"
|
||||
@openerpweb.jsonrequest
|
||||
def read(self, request, model, group_by_fields, domain=None, context=None):
|
||||
Model = request.session.model(model)
|
||||
return Model.read_group(domain or False, False, group_by_fields, 0, False, context or False)
|
||||
|
||||
class View(openerpweb.Controller):
|
||||
def fields_view_get(self, request, model, view_id, view_type,
|
||||
|
||||
@@ -24,10 +24,11 @@
|
||||
<script type="text/javascript" src="/base/static/src/js/dates.js"></script>
|
||||
<script type="text/javascript" src="/base/static/src/js/chrome.js"></script>
|
||||
<script type="text/javascript" src="/base/static/src/js/data.js"></script>
|
||||
<script type="text/javascript" src="/base/static/src/js/views.js"></script>
|
||||
<script type="text/javascript" src="/base/static/src/js/form.js"></script>
|
||||
<script type="text/javascript" src="/base/static/src/js/list.js"></script>
|
||||
<script type="text/javascript" src="/base/static/src/js/search.js"></script>
|
||||
<script type="text/javascript" src="/base/static/src/js/views.js"></script>
|
||||
<script type="text/javascript" src="/base/static/src/js/m2o.js"></script>
|
||||
|
||||
<link rel="stylesheet" type="text/css" media="screen" href="/base/static/lib/jquery.ui/css/smoothness/jquery-ui-1.8.9.custom.css" />
|
||||
<link rel="stylesheet" type="text/css" media="screen" href="/base/static/lib/jquery.ui.notify/css/ui.notify.css" />
|
||||
|
||||
@@ -221,8 +221,6 @@ body.openerp {
|
||||
height: 63px;
|
||||
width: 200px;
|
||||
margin-right: 10px;
|
||||
line-height: 63px;
|
||||
text-align: center;
|
||||
border: 1px solid white;
|
||||
border-right-color: black;
|
||||
border-bottom-color: black;
|
||||
@@ -232,7 +230,12 @@ body.openerp {
|
||||
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#FFFFFF', endColorstr='#CECECE',GradientType=0 );
|
||||
}
|
||||
.openerp .company_logo {
|
||||
vertical-align: middle
|
||||
margin-top: 7px;
|
||||
margin-left: 10px;
|
||||
display: block;
|
||||
background: url(/base/static/src/img/logo.png);
|
||||
width:180px;
|
||||
height:46px;
|
||||
}
|
||||
.openerp .header_title {
|
||||
float: left;
|
||||
@@ -773,44 +776,61 @@ body.openerp {
|
||||
background-position: 21px 0;
|
||||
}
|
||||
|
||||
.openerp .kitten-mode-activated {
|
||||
.openerp.kitten-mode-activated .main_table {
|
||||
background: url(http://placekitten.com/g/1500/800) repeat;
|
||||
}
|
||||
|
||||
.openerp .kitten-mode-activated .header {
|
||||
.openerp.kitten-mode-activated .header {
|
||||
background: url(http://placekitten.com/g/211/65) repeat;
|
||||
}
|
||||
|
||||
.openerp .kitten-mode-activated .secondary_menu {
|
||||
.openerp.kitten-mode-activated .secondary_menu {
|
||||
background: url(http://placekitten.com/g/212/100) repeat;
|
||||
}
|
||||
|
||||
.openerp .kitten-mode-activated .menu {
|
||||
.openerp.kitten-mode-activated .menu {
|
||||
background: #828282;
|
||||
background: -moz-linear-gradient(top, #828282 0%, #4D4D4D 100%);
|
||||
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#828282), color-stop(100%,#4D4D4D));
|
||||
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#828282', endColorstr='#4D4D4D',GradientType=0 );
|
||||
}
|
||||
.openerp .kitten-mode-activated .menu a {
|
||||
.openerp.kitten-mode-activated .menu a {
|
||||
background: none;
|
||||
}
|
||||
.openerp .kitten-mode-activated .menu span {
|
||||
.openerp.kitten-mode-activated .menu span {
|
||||
background: none;
|
||||
}
|
||||
.openerp .kitten-mode-activated .sidebar-displaying-div li a,
|
||||
.openerp .kitten-mode-activated .view-manager-main-content h2.oe_view_title,
|
||||
.openerp .kitten-mode-activated .view-manager-main-content a.searchview_group_string,
|
||||
.openerp .kitten-mode-activated .view-manager-main-content label {
|
||||
.openerp.kitten-mode-activated .sidebar-displaying-div li a,
|
||||
.openerp.kitten-mode-activated .oe-application .view-manager-main-content h2.oe_view_title,
|
||||
.openerp.kitten-mode-activated .oe-application .view-manager-main-content a.searchview_group_string,
|
||||
.openerp.kitten-mode-activated .oe-application .view-manager-main-content label {
|
||||
color: white;
|
||||
}
|
||||
.openerp .kitten-mode-activated .menu,
|
||||
.openerp .kitten-mode-activated .header_corner,
|
||||
.openerp .kitten-mode-activated .secondary_menu div h3,
|
||||
.openerp .kitten-mode-activated .sidebar-displaying-div h2,
|
||||
.openerp .kitten-mode-activated .view-manager-main-content #oe_app_view_list,
|
||||
.openerp .kitten-mode-activated .view-manager-main-content .oe_form_notebook,
|
||||
.openerp .kitten-mode-activated .view-manager-main-content .oe_form_hpaned {
|
||||
.openerp.kitten-mode-activated .menu,
|
||||
.openerp.kitten-mode-activated .header_corner,
|
||||
.openerp.kitten-mode-activated .header_title,
|
||||
.openerp.kitten-mode-activated .secondary_menu div,
|
||||
.openerp.kitten-mode-activated .oe-application,
|
||||
.openerp.kitten-mode-activated .oe_footer,
|
||||
.openerp.kitten-mode-activated .loading,
|
||||
.openerp.kitten-mode-activated .ui-dialog {
|
||||
opacity:0.8;
|
||||
filter:alpha(opacity=80);
|
||||
}
|
||||
.openerp.kitten-mode-activated .header .company_logo {
|
||||
background: url(http://placekitten.com/g/180/46);
|
||||
}
|
||||
.openerp.kitten-mode-activated .loading {
|
||||
background: #828282;
|
||||
border-color: #828282;
|
||||
}
|
||||
|
||||
/* Many2one Autosearch */
|
||||
.openerp .ui_combo {
|
||||
height: 20px;
|
||||
margin-right: 1px;
|
||||
top: 4px;
|
||||
width: 22px;
|
||||
}
|
||||
/* ------------- End autocomplete ------- */
|
||||
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 473 B |
Binary file not shown.
|
After Width: | Height: | Size: 471 B |
@@ -126,6 +126,7 @@ openerp.base = function(instance) {
|
||||
openerp.base.views(instance);
|
||||
openerp.base.search(instance);
|
||||
openerp.base.list(instance);
|
||||
openerp.base.m2o(instance);
|
||||
openerp.base.form(instance);
|
||||
};
|
||||
|
||||
|
||||
@@ -790,10 +790,6 @@ openerp.base.Header = openerp.base.Controller.extend({
|
||||
this.do_update();
|
||||
},
|
||||
do_update: function() {
|
||||
if(jQuery.param != undefined &&
|
||||
jQuery.deparam(jQuery.param.querystring()).kitten != undefined) {
|
||||
this.kitten = 1;
|
||||
}
|
||||
this.$element.html(QWeb.render("Header", this));
|
||||
}
|
||||
});
|
||||
@@ -897,7 +893,7 @@ openerp.base.WebClient = openerp.base.Controller.extend({
|
||||
var params = {};
|
||||
if(jQuery.param != undefined &&
|
||||
jQuery.deparam(jQuery.param.querystring()).kitten != undefined) {
|
||||
params = {kitten:1};
|
||||
this.$element.addClass("kitten-mode-activated");
|
||||
}
|
||||
this.$element.html(QWeb.render("Interface", params));
|
||||
|
||||
|
||||
@@ -3,15 +3,139 @@ openerp.base.data = function(openerp) {
|
||||
|
||||
openerp.base.DataGroup = openerp.base.Controller.extend( /** @lends openerp.base.DataGroup# */{
|
||||
/**
|
||||
* Management interface between views and the collection of selected OpenERP
|
||||
* records (represents the view's state?)
|
||||
* Management interface between views and grouped collections of OpenERP
|
||||
* records.
|
||||
*
|
||||
* The root DataGroup is instantiated with the relevant information
|
||||
* (a session, a model, a domain, a context and a group_by sequence), the
|
||||
* domain and context may be empty. It is then interacted with via
|
||||
* :js:func:`~openerp.base.DataGroup.list`, which is used to read the
|
||||
* content of the current grouping level, and
|
||||
* :js:func:`~openerp.base.DataGroup.get`, which is used to fetch an item
|
||||
* of the current grouping level.
|
||||
*
|
||||
* @constructs
|
||||
* @extends openerp.base.Controller
|
||||
*
|
||||
* @param {openerp.base.Session} session Current OpenERP session
|
||||
* @param {String} model name of the model managed by this DataGroup
|
||||
* @param {Array} domain search domain for this DataGroup
|
||||
* @param {Object} context context of the DataGroup's searches
|
||||
* @param {Array} group_by sequence of fields by which to group
|
||||
*/
|
||||
init: function(session) {
|
||||
init: function(session, model, domain, context, group_by) {
|
||||
this._super(session, null);
|
||||
this.model = model;
|
||||
this.context = context;
|
||||
this.domain = domain;
|
||||
|
||||
this.group_by = group_by;
|
||||
|
||||
this.groups = null;
|
||||
},
|
||||
fetch: function () {
|
||||
// internal method
|
||||
var d = new $.Deferred();
|
||||
var self = this;
|
||||
|
||||
if (this.groups) {
|
||||
d.resolveWith(this, [this.groups]);
|
||||
} else {
|
||||
this.rpc('/base/group/read', {
|
||||
model: this.model,
|
||||
context: this.context,
|
||||
domain: this.domain,
|
||||
group_by_fields: this.group_by
|
||||
}, function () { }).then(function (response) {
|
||||
self.groups = response.result;
|
||||
// read_group results are annoying: they use the name of the
|
||||
// field grouped on to hold the value and the count, so no
|
||||
// generic access to those values is possible.
|
||||
// Alias them to `value` and `length`.
|
||||
d.resolveWith(self, [_(response.result).map(function (group) {
|
||||
var field_name = self.group_by[0];
|
||||
return _.extend({}, group, {
|
||||
// provide field used for grouping
|
||||
grouped_on: field_name,
|
||||
length: group[field_name + '_count'],
|
||||
value: group[field_name]
|
||||
});
|
||||
})]);
|
||||
}, function () {
|
||||
d.rejectWith.apply(d, self, [arguments]);
|
||||
});
|
||||
}
|
||||
return d.promise();
|
||||
},
|
||||
/**
|
||||
* Retrieves the content of an item in the DataGroup, which results in
|
||||
* either a DataSet or new DataGroup instance.
|
||||
*
|
||||
* Calling :js:func:`~openerp.base.DataGroup.get` without having called
|
||||
* :js:func:`~openerp.base.DataGroup.list` beforehand will likely result
|
||||
* in an error.
|
||||
*
|
||||
* The resulting :js:class:`~openerp.base.DataGroup` or
|
||||
* :js:class:`~openerp.base.DataSet` will be provided through the relevant
|
||||
* callback function. In both functions, the current DataGroup will be
|
||||
* provided as context (``this``)
|
||||
*
|
||||
* @param {Number} index the index of the group to open in the datagroup's collection
|
||||
* @param {Function} ifDataSet executed if the item results in a DataSet, provided with the new dataset as parameter
|
||||
* @param {Function} ifDataGroup executed if the item results in a DataSet, provided with the new datagroup as parameter
|
||||
*/
|
||||
get: function (index, ifDataSet, ifDataGroup) {
|
||||
var group = this.groups[index];
|
||||
if (!group) {
|
||||
throw new Error("No group at index " + index);
|
||||
}
|
||||
|
||||
var child_context = _.extend({}, this.context, group.__context);
|
||||
if (group.__context.group_by.length) {
|
||||
var datagroup = new openerp.base.DataGroup(
|
||||
this.session, this.model, group.__domain, child_context,
|
||||
group.__context.group_by);
|
||||
ifDataGroup.call(this, datagroup);
|
||||
} else {
|
||||
var dataset = new openerp.base.DataSetSearch(this.session, this.model);
|
||||
dataset.domain = group.__domain;
|
||||
dataset.context = child_context;
|
||||
ifDataSet.call(this, dataset);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Gathers the content of the current data group (the current grouping
|
||||
* level), and provides it via a promise object to which callbacks should
|
||||
* be added::
|
||||
*
|
||||
* datagroup.list().then(function (list) {
|
||||
* // manipulate list here
|
||||
* });
|
||||
*
|
||||
* The argument to the callback is the list of elements fetched, the
|
||||
* context (``this``) is the datagroup itself.
|
||||
*
|
||||
* The items of a list are the standard objects returned by ``read_group``
|
||||
* with three properties added:
|
||||
*
|
||||
* ``length``
|
||||
* the number of records contained in the group (and all of its
|
||||
* sub-groups). This does *not* provide the size of the "next level"
|
||||
* of the group, unless the group is terminal (no more groups within
|
||||
* it).
|
||||
* ``grouped_on``
|
||||
* the name of the field this level was grouped on, this is mostly
|
||||
* used for display purposes, in order to know the name of the current
|
||||
* level of grouping. The ``grouped_on`` should be the same for all
|
||||
* objects of the list.
|
||||
* ``value``
|
||||
* the value which led to this group (this is the value all contained
|
||||
* records have for the current ``grouped_on`` field name).
|
||||
*
|
||||
* @returns {$.Deferred}
|
||||
*/
|
||||
list: function () {
|
||||
return this.fetch();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -89,12 +213,12 @@ openerp.base.DataSet = openerp.base.Controller.extend( /** @lends openerp.base.
|
||||
context: this.context
|
||||
}, callback);
|
||||
},
|
||||
create: function(data, callback) {
|
||||
create: function(data, callback, error_callback) {
|
||||
return this.rpc('/base/dataset/create', {
|
||||
model: this.model,
|
||||
data: data,
|
||||
context: this.context
|
||||
}, callback);
|
||||
}, callback, error_callback);
|
||||
},
|
||||
write: function (id, data, callback) {
|
||||
return this.rpc('/base/dataset/save', {
|
||||
@@ -119,6 +243,15 @@ openerp.base.DataSet = openerp.base.Controller.extend( /** @lends openerp.base.
|
||||
args: args
|
||||
}, callback);
|
||||
},
|
||||
name_search: function (search_str, callback) {
|
||||
search_str = search_str || '';
|
||||
return this.rpc('/base/dataset/name_search', {
|
||||
model: this.model,
|
||||
search_str: search_str,
|
||||
domain: this.domain || [],
|
||||
context: this.context
|
||||
}, callback);
|
||||
},
|
||||
exec_workflow: function (id, signal, callback) {
|
||||
return this.rpc('/base/dataset/exec_workflow', {
|
||||
model: this.model,
|
||||
|
||||
@@ -1,11 +1,21 @@
|
||||
|
||||
openerp.base.dates = function(openerp) {
|
||||
|
||||
/**
|
||||
* Converts a string to a Date javascript object using OpenERP's
|
||||
* datetime string format (exemple: '2011-12-01 15:12:35').
|
||||
*
|
||||
* The timezone is assumed to be UTC (standard for OpenERP 6.1)
|
||||
* and will be converted to the browser's timezone.
|
||||
*
|
||||
* @param {String} str A string representing a datetime.
|
||||
* @returns {Date}
|
||||
*/
|
||||
openerp.base.parse_datetime = function(str) {
|
||||
if(!str) {
|
||||
return str;
|
||||
}
|
||||
var regex = /\d\d\d\d-\d\d-\d\) \d\d:\d\d:\d\d/;
|
||||
var regex = /\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d/;
|
||||
var res = regex.exec(str);
|
||||
if ( res[0] != str ) {
|
||||
throw "'" + str + "' is not a valid datetime";
|
||||
@@ -17,6 +27,13 @@ openerp.base.parse_datetime = function(str) {
|
||||
return obj;
|
||||
};
|
||||
|
||||
/**
|
||||
* Converts a string to a Date javascript object using OpenERP's
|
||||
* date string format (exemple: '2011-12-01').
|
||||
*
|
||||
* @param {String} str A string representing a date.
|
||||
* @returns {Date}
|
||||
*/
|
||||
openerp.base.parse_date = function(str) {
|
||||
if(!str) {
|
||||
return str;
|
||||
@@ -33,6 +50,13 @@ openerp.base.parse_date = function(str) {
|
||||
return obj;
|
||||
};
|
||||
|
||||
/**
|
||||
* Converts a string to a Date javascript object using OpenERP's
|
||||
* time string format (exemple: '15:12:35').
|
||||
*
|
||||
* @param {String} str A string representing a time.
|
||||
* @returns {Date}
|
||||
*/
|
||||
openerp.base.parse_time = function(str) {
|
||||
if(!str) {
|
||||
return str;
|
||||
@@ -49,7 +73,7 @@ openerp.base.parse_time = function(str) {
|
||||
return obj;
|
||||
};
|
||||
|
||||
/**
|
||||
/*
|
||||
* Just a simple function to add some '0' if an integer it too small.
|
||||
*/
|
||||
var fts = function(str, size) {
|
||||
@@ -61,8 +85,18 @@ var fts = function(str, size) {
|
||||
return to_add + str;
|
||||
};
|
||||
|
||||
/**
|
||||
* Converts a Date javascript object to a string using OpenERP's
|
||||
* datetime string format (exemple: '2011-12-01 15:12:35').
|
||||
*
|
||||
* The timezone of the Date object is assumed to be the one of the
|
||||
* browser and it will be converted to UTC (standard for OpenERP 6.1).
|
||||
*
|
||||
* @param {Date} obj
|
||||
* @returns {String} A string representing a datetime.
|
||||
*/
|
||||
openerp.base.format_datetime = function(obj) {
|
||||
if(! str) {
|
||||
if (!obj) {
|
||||
return false;
|
||||
}
|
||||
return fts(obj.getUTCFullYear(),4) + "-" + fts(obj.getUTCMonth() + 1,2) + "-"
|
||||
@@ -70,20 +104,34 @@ openerp.base.format_datetime = function(obj) {
|
||||
+ fts(obj.getUTCMinutes(),2) + ":" + fts(obj.getUTCSeconds(),2);
|
||||
};
|
||||
|
||||
/**
|
||||
* Converts a Date javascript object to a string using OpenERP's
|
||||
* date string format (exemple: '2011-12-01').
|
||||
*
|
||||
* @param {Date} obj
|
||||
* @returns {String} A string representing a date.
|
||||
*/
|
||||
openerp.base.format_date = function(obj) {
|
||||
if(! str) {
|
||||
if (!obj) {
|
||||
return false;
|
||||
}
|
||||
return fts(obj.getFullYear(),4) + "-" + fts(obj.getMonth() + 1,2) + "-"
|
||||
+ fts(obj.getDate(),2);
|
||||
};
|
||||
|
||||
/**
|
||||
* Converts a Date javascript object to a string using OpenERP's
|
||||
* time string format (exemple: '15:12:35').
|
||||
*
|
||||
* @param {Date} obj
|
||||
* @returns {String} A string representing a time.
|
||||
*/
|
||||
openerp.base.format_time = function(obj) {
|
||||
if(! str) {
|
||||
if (!obj) {
|
||||
return false;
|
||||
}
|
||||
return fts(obj.getHours(),2) + ":" + fts(obj.getMinutes(),2) + ":"
|
||||
+ fts(obj.getSeconds(),2);
|
||||
};
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
openerp.base.form = function (openerp) {
|
||||
|
||||
openerp.base.views.add('form', 'openerp.base.FormView');
|
||||
openerp.base.FormView = openerp.base.Controller.extend( /** @lends openerp.base.FormView# */{
|
||||
openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormView# */{
|
||||
/**
|
||||
* Indicates that this view is not searchable, and thus that no search
|
||||
* view should be displayed (if there is one active).
|
||||
@@ -143,14 +143,21 @@ openerp.base.FormView = openerp.base.Controller.extend( /** @lends openerp.base
|
||||
var call = onchange.match(/^\s?(.*?)\((.*?)\)\s?$/);
|
||||
if (call) {
|
||||
var method = call[1], args = [];
|
||||
var argument_replacement = {
|
||||
'False' : false,
|
||||
'True' : true,
|
||||
'None' : null
|
||||
}
|
||||
_.each(call[2].split(','), function(a) {
|
||||
var field = _.trim(a);
|
||||
if (self.fields[field]) {
|
||||
if (field in argument_replacement) {
|
||||
args.push(argument_replacement[field]);
|
||||
} else if (self.fields[field]) {
|
||||
var value = self.fields[field].value;
|
||||
args.push(value == null ? false : value);
|
||||
} else {
|
||||
args.push(false);
|
||||
this.log("warning : on_change can't find field " + field, onchange);
|
||||
self.log("warning : on_change can't find field " + field, onchange);
|
||||
}
|
||||
});
|
||||
var ajax = {
|
||||
@@ -365,7 +372,8 @@ openerp.base.form.compute_domain = function(expr, fields) {
|
||||
}
|
||||
}
|
||||
return _.indexOf(stack, false) == -1;
|
||||
},
|
||||
}
|
||||
|
||||
openerp.base.form.Widget = openerp.base.Controller.extend({
|
||||
init: function(view, node) {
|
||||
this.view = view;
|
||||
@@ -532,7 +540,7 @@ openerp.base.form.WidgetButton = openerp.base.form.Widget.extend({
|
||||
on_confirmed: function() {
|
||||
var self = this;
|
||||
|
||||
this.execute_action(
|
||||
this.view.execute_action(
|
||||
this.node.attrs, this.view.dataset, this.session.action_manager,
|
||||
this.view.datarecord.id, function (result) {
|
||||
self.log("Button returned", result);
|
||||
@@ -540,9 +548,6 @@ openerp.base.form.WidgetButton = openerp.base.form.Widget.extend({
|
||||
});
|
||||
}
|
||||
});
|
||||
// let WidgetButton execute actions
|
||||
_.extend(openerp.base.form.WidgetButton.prototype,
|
||||
openerp.base.ActionExecutor);
|
||||
|
||||
openerp.base.form.WidgetLabel = openerp.base.form.Widget.extend({
|
||||
init: function(view, node) {
|
||||
@@ -649,7 +654,24 @@ openerp.base.form.FieldChar = openerp.base.form.Field.extend({
|
||||
openerp.base.form.FieldEmail = openerp.base.form.FieldChar.extend({
|
||||
init: function(view, node) {
|
||||
this._super(view, node);
|
||||
this.template = "FieldEmail";
|
||||
this.validation_regex = /@/;
|
||||
},
|
||||
start: function() {
|
||||
this._super.apply(this, arguments);
|
||||
this.$element.find('button').click(this.on_button_clicked);
|
||||
},
|
||||
on_button_clicked: function() {
|
||||
if (!this.value || this.invalid) {
|
||||
this.notification.warn("E-mail error", "Can't send email to invalid e-mail address");
|
||||
} else {
|
||||
location.href = 'mailto:' + this.value;
|
||||
}
|
||||
},
|
||||
set_value: function(value) {
|
||||
this._super.apply(this, arguments);
|
||||
var show_value = (value != null && value !== false) ? value : '';
|
||||
this.$element.find('a').attr('href', 'mailto:' + show_value);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -681,32 +703,69 @@ openerp.base.form.FieldFloat = openerp.base.form.FieldChar.extend({
|
||||
}
|
||||
});
|
||||
|
||||
openerp.base.form.FieldDate = openerp.base.form.FieldChar.extend({
|
||||
openerp.base.form.FieldDatetime = openerp.base.form.Field.extend({
|
||||
init: function(view, node) {
|
||||
this._super(view, node);
|
||||
this.template = "FieldDate";
|
||||
this.validation_regex = /^\d+-\d+-\d+$/;
|
||||
this.jqueryui_object = 'datetimepicker';
|
||||
},
|
||||
start: function() {
|
||||
this._super.apply(this, arguments);
|
||||
this.$element.find('input').change(this.on_ui_change).datepicker({
|
||||
dateFormat: 'yy-mm-dd'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
openerp.base.form.FieldDatetime = openerp.base.form.FieldChar.extend({
|
||||
init: function(view, node) {
|
||||
this._super(view, node);
|
||||
this.template = "FieldDatetime";
|
||||
this.validation_regex = /^\d+-\d+-\d+( \d+:\d+(:\d+)?)?$/;
|
||||
},
|
||||
start: function() {
|
||||
this._super.apply(this, arguments);
|
||||
this.$element.find('input').change(this.on_ui_change).datetimepicker({
|
||||
this.$element.find('input').change(this.on_ui_change)[this.jqueryui_object]({
|
||||
dateFormat: 'yy-mm-dd',
|
||||
timeFormat: 'hh:mm:ss'
|
||||
});
|
||||
},
|
||||
set_value: function(value) {
|
||||
this._super.apply(this, arguments);
|
||||
if (value == null || value == false) {
|
||||
this.$element.find('input').val('');
|
||||
} else {
|
||||
this.value = this.parse(value);
|
||||
this.$element.find('input')[this.jqueryui_object]('setDate', this.value);
|
||||
}
|
||||
},
|
||||
set_value_from_ui: function() {
|
||||
this.value = this.$element.find('input')[this.jqueryui_object]('getDate') || false;
|
||||
if (this.value) {
|
||||
this.value = this.format(this.value);
|
||||
}
|
||||
},
|
||||
validate: function() {
|
||||
this.invalid = this.required && this.value === false;
|
||||
},
|
||||
parse: openerp.base.parse_datetime,
|
||||
format: openerp.base.format_datetime
|
||||
});
|
||||
|
||||
openerp.base.form.FieldDate = openerp.base.form.FieldDatetime.extend({
|
||||
init: function(view, node) {
|
||||
this._super(view, node);
|
||||
this.jqueryui_object = 'datepicker';
|
||||
},
|
||||
parse: openerp.base.parse_date,
|
||||
format: openerp.base.format_date
|
||||
});
|
||||
|
||||
openerp.base.form.FieldFloatTime = openerp.base.form.FieldChar.extend({
|
||||
init: function(view, node) {
|
||||
this._super(view, node);
|
||||
this.validation_regex = /^\d+:\d+$/;
|
||||
},
|
||||
set_value: function(value) {
|
||||
value = value || 0;
|
||||
this._super.apply(this, [value]);
|
||||
var show_value = _.sprintf("%02d:%02d", Math.floor(value), Math.round((value % 1) * 60));
|
||||
this.$element.find('input').val(show_value);
|
||||
},
|
||||
validate: function() {
|
||||
if (typeof(this.value) == "string") {
|
||||
this._super.apply(this, arguments);
|
||||
if (!this.invalid) {
|
||||
var time = this.value.split(':');
|
||||
this.value = parseInt(time[0], 10) + parseInt(time[1], 10) / 60;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -827,10 +886,33 @@ openerp.base.form.FieldSelection = openerp.base.form.Field.extend({
|
||||
}
|
||||
});
|
||||
|
||||
openerp.base.form.FieldMany2OneDatasSet = openerp.base.DataSetStatic.extend({
|
||||
start: function() {
|
||||
},
|
||||
write: function (id, data, callback) {
|
||||
this._super(id, data, callback);
|
||||
},
|
||||
});
|
||||
|
||||
openerp.base.form.FieldMany2OneViewManager = openerp.base.ViewManager.extend({
|
||||
init: function(session, element_id, dataset, views) {
|
||||
this._super(session, element_id, dataset, views);
|
||||
}
|
||||
});
|
||||
|
||||
openerp.base.form.FieldMany2One = openerp.base.form.Field.extend({
|
||||
init: function(view, node) {
|
||||
this._super(view, node);
|
||||
this.template = "FieldMany2One";
|
||||
this.is_field_m2o = true;
|
||||
},
|
||||
start: function() {
|
||||
this.$element = $('#' + this.element_id);
|
||||
this.dataset = new openerp.base.form.FieldMany2OneDatasSet(this.session, this.field.relation);
|
||||
var views = [ [false,"list"], [false,"form"] ];
|
||||
this.viewmanager = new openerp.base.form.FieldMany2OneViewManager(this.view.session, this.element_id, this.dataset, views);
|
||||
new openerp.base.m2o(this.viewmanager, this.$element, this.field.relation, this.dataset, this.session)
|
||||
this.$element.find('input').change(this.on_ui_change);
|
||||
},
|
||||
set_value: function(value) {
|
||||
this._super.apply(this, arguments);
|
||||
@@ -840,6 +922,16 @@ openerp.base.form.FieldMany2One = openerp.base.form.Field.extend({
|
||||
this.value = value[0];
|
||||
}
|
||||
this.$element.find('input').val(show_value);
|
||||
this.$element.find('input').attr('m2o_id', this.value);
|
||||
},
|
||||
|
||||
get_value: function() {
|
||||
var val = this.$element.find('input').attr('m2o_id') || this.value
|
||||
return val;
|
||||
},
|
||||
|
||||
on_ui_change: function() {
|
||||
this.touched = this.view.touched = true;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -900,6 +992,8 @@ openerp.base.form.FieldMany2Many = openerp.base.form.Field.extend({
|
||||
this._super(view, node);
|
||||
this.template = "FieldMany2Many";
|
||||
this.list_id = _.uniqueId("many2many");
|
||||
this.is_started = false;
|
||||
this.is_setted = false;
|
||||
},
|
||||
start: function() {
|
||||
this._super.apply(this, arguments);
|
||||
@@ -909,23 +1003,35 @@ openerp.base.form.FieldMany2Many = openerp.base.form.Field.extend({
|
||||
var self = this;
|
||||
this.list_view.m2m_field = this;
|
||||
this.list_view.start();
|
||||
var hack = {loaded: false};
|
||||
this.list_view.on_loaded.add_last(function() {
|
||||
if (! hack.loaded) {
|
||||
self.is_started = true;
|
||||
self.check_load();
|
||||
hack.loaded = true;
|
||||
}
|
||||
});
|
||||
},
|
||||
set_value: function(value) {
|
||||
if (value != false) {
|
||||
this.dataset.ids = value;
|
||||
this.dataset.count = value.length;
|
||||
this.list_view.do_reload();
|
||||
this.is_setted = true;
|
||||
this.check_load();
|
||||
}
|
||||
},
|
||||
get_value: function() {
|
||||
return [[6,false,this.dataset.ids]];
|
||||
},
|
||||
check_load: function() {
|
||||
if(this.is_started && this.is_setted) {
|
||||
this.list_view.do_reload();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
openerp.base.form.Many2ManyListView = openerp.base.ListView.extend({
|
||||
do_delete: function (e) {
|
||||
e.stopImmediatePropagation();
|
||||
var ids = [this.rows[$(e.currentTarget).closest('tr').prevAll().length].data.id.value];
|
||||
do_delete: function (ids) {
|
||||
this.dataset.ids = _.without.apply(null, [this.dataset.ids].concat(ids));
|
||||
this.dataset.count = this.dataset.ids.length;
|
||||
// there may be a faster way
|
||||
@@ -947,12 +1053,20 @@ openerp.base.form.Many2ManyListView = openerp.base.ListView.extend({
|
||||
},
|
||||
do_add_record: function (e) {
|
||||
e.stopImmediatePropagation();
|
||||
// TODO: need to open a popup with search view
|
||||
var pop = new openerp.base.form.Many2XSelectPopup(null, this.m2m_field.view.session);
|
||||
pop.select_element(this.model);
|
||||
var self = this;
|
||||
pop.on_select_element.add(function(element_id) {
|
||||
if(! _.detect(self.dataset.ids, function(x) {return x == element_id;})) {
|
||||
self.dataset.ids.push(element_id);
|
||||
self.dataset.count = self.dataset.ids.length;
|
||||
self.do_reload();
|
||||
}
|
||||
pop.stop();
|
||||
});
|
||||
},
|
||||
on_select_row: function(event) {
|
||||
var $target = $(event.currentTarget);
|
||||
var row = this.rows[$target.prevAll().length];
|
||||
var id = row.data.id.value;
|
||||
select_record: function(index) {
|
||||
var id = this.rows[index].data.id.value;
|
||||
if(! id) {
|
||||
return;
|
||||
}
|
||||
@@ -969,6 +1083,97 @@ openerp.base.form.Many2ManyListView = openerp.base.ListView.extend({
|
||||
}
|
||||
});
|
||||
|
||||
openerp.base.form.Many2XSelectPopup = openerp.base.BaseWidget.extend({
|
||||
identifier_prefix: "many2xselectpopup",
|
||||
template: "Many2XSelectPopup",
|
||||
select_element: function(model) {
|
||||
this.model = model;
|
||||
var html = this.render();
|
||||
jQuery(html).dialog({title: '',
|
||||
modal: true,
|
||||
minWidth: 800});
|
||||
this.start();
|
||||
},
|
||||
start: function() {
|
||||
this._super();
|
||||
this.dataset = new openerp.base.DataSetSearch(this.session, this.model);
|
||||
this.setup_search_view();
|
||||
},
|
||||
setup_search_view: function() {
|
||||
var self = this;
|
||||
if (this.searchview) {
|
||||
this.searchview.stop();
|
||||
}
|
||||
this.searchview = new openerp.base.SearchView(this, this.session, this.element_id + "_search",
|
||||
this.dataset, false, {});
|
||||
this.searchview.on_search.add(function(domains, contexts, groupbys) {
|
||||
self.view_list.do_search.call(
|
||||
self, domains, contexts, groupbys);
|
||||
});
|
||||
this.searchview.on_loaded.add_last(function () {
|
||||
var $buttons = self.searchview.$element.find(".oe_search-view-buttons");
|
||||
$buttons.append(QWeb.render("Many2XSelectPopup.search.buttons"));
|
||||
var $nbutton = $buttons.find(".oe_many2xselectpopup-search-new");
|
||||
$nbutton.click(function() {
|
||||
self.new_object();
|
||||
});
|
||||
var $cbutton = $buttons.find(".oe_many2xselectpopup-search-close");
|
||||
$cbutton.click(function() {
|
||||
self.stop();
|
||||
});
|
||||
self.view_list = new openerp.base.form.Many2XPopupListView( null, self.session,
|
||||
self.element_id + "_view_list", self.dataset, false);
|
||||
self.view_list.popup = self;
|
||||
self.view_list.do_show();
|
||||
self.view_list.start();
|
||||
var tmphack = {"loaded": false};
|
||||
self.view_list.on_loaded.add_last(function() {
|
||||
if ( !tmphack.loaded ) {
|
||||
self.view_list.do_reload();
|
||||
tmphack.loaded = true;
|
||||
};
|
||||
});
|
||||
});
|
||||
this.searchview.start();
|
||||
},
|
||||
on_select_element: function(element_id) {
|
||||
},
|
||||
new_object: function() {
|
||||
var self = this;
|
||||
this.searchview.hide();
|
||||
this.view_list.$element.hide();
|
||||
this.dataset.index = null;
|
||||
this.view_form = new openerp.base.FormView({}, this.session,
|
||||
this.element_id + "_view_form", this.dataset, false);
|
||||
this.view_form.start();
|
||||
this.view_form.on_loaded.add_last(function() {
|
||||
var $buttons = self.view_form.$element.find(".oe_form_buttons");
|
||||
$buttons.html(QWeb.render("Many2XSelectPopup.form.buttons"));
|
||||
var $nbutton = $buttons.find(".oe_many2xselectpopup-form-save");
|
||||
$nbutton.click(function() {
|
||||
self.view_form.do_save();
|
||||
});
|
||||
var $cbutton = $buttons.find(".oe_many2xselectpopup-form-close");
|
||||
$cbutton.click(function() {
|
||||
self.stop();
|
||||
});
|
||||
});
|
||||
this.view_form.on_created.add_last(function(r, success) {
|
||||
if (r.result) {
|
||||
var id = arguments[0].result;
|
||||
self.on_select_element(id);
|
||||
}
|
||||
});
|
||||
this.view_form.do_show();
|
||||
}
|
||||
});
|
||||
|
||||
openerp.base.form.Many2XPopupListView = openerp.base.ListView.extend({
|
||||
switch_to_record: function(index) {
|
||||
this.popup.on_select_element(this.dataset.ids[index]);
|
||||
}
|
||||
});
|
||||
|
||||
openerp.base.form.FieldReference = openerp.base.form.Field.extend({
|
||||
init: function(view, node) {
|
||||
this._super(view, node);
|
||||
@@ -976,6 +1181,17 @@ openerp.base.form.FieldReference = openerp.base.form.Field.extend({
|
||||
}
|
||||
});
|
||||
|
||||
openerp.base.form.FieldImage = openerp.base.form.Field.extend({
|
||||
init: function(view, node) {
|
||||
this._super(view, node);
|
||||
this.template = "FieldImage";
|
||||
},
|
||||
set_value: function(value) {
|
||||
this._super.apply(this, arguments);
|
||||
this.$element.find('img').show().attr('src', 'data:image/png;base64,' + this.value);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Registry of form widgets, called by :js:`openerp.base.FormView`
|
||||
*/
|
||||
@@ -1002,7 +1218,8 @@ openerp.base.form.widgets = new openerp.base.Registry({
|
||||
'float' : 'openerp.base.form.FieldFloat',
|
||||
'integer': 'openerp.base.form.FieldFloat',
|
||||
'progressbar': 'openerp.base.form.FieldProgressBar',
|
||||
'float_time': 'openerp.base.form.FieldFloat'
|
||||
'float_time': 'openerp.base.form.FieldFloatTime',
|
||||
'image': 'openerp.base.form.FieldImage'
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
openerp.base.list = function (openerp) {
|
||||
openerp.base.views.add('list', 'openerp.base.ListView');
|
||||
openerp.base.ListView = openerp.base.Controller.extend(
|
||||
/** @lends openerp.base.ListView# */ {
|
||||
openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListView# */ {
|
||||
defaults: {
|
||||
// records can be selected one by one
|
||||
'selectable': true,
|
||||
@@ -61,7 +60,11 @@ openerp.base.ListView = openerp.base.Controller.extend(
|
||||
columns: this.columns,
|
||||
rows: this.rows
|
||||
});
|
||||
$(this.list).bind({
|
||||
this.groups = new openerp.base.ListView.Groups({
|
||||
options: this.options,
|
||||
columns: this.columns
|
||||
});
|
||||
$([this.list, this.groups]).bind({
|
||||
'selected': function (e, selection) {
|
||||
self.$element.find('#oe-list-delete')
|
||||
.toggle(!!selection.length);
|
||||
@@ -80,7 +83,7 @@ openerp.base.ListView = openerp.base.Controller.extend(
|
||||
id, self.do_reload);
|
||||
},
|
||||
'row_link': function (e, index) {
|
||||
self.switch_to_record(index);
|
||||
self.select_record(index);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -233,7 +236,7 @@ openerp.base.ListView = openerp.base.Controller.extend(
|
||||
* @param {Number|null} index the record index (in the current dataset) to switch to
|
||||
* @param {String} [view="form"] the view type to switch to
|
||||
*/
|
||||
switch_to_record:function (index, view) {
|
||||
select_record:function (index, view) {
|
||||
view = view || 'form';
|
||||
this.dataset.index = index;
|
||||
_.delay(_.bind(function () {
|
||||
@@ -290,6 +293,14 @@ openerp.base.ListView = openerp.base.Controller.extend(
|
||||
// TODO: handle non-empty results.group_by with read_group
|
||||
self.dataset.context = results.context;
|
||||
self.dataset.domain = results.domain;
|
||||
if (results.group_by.length) {
|
||||
self.groups.datagroup = new openerp.base.DataGroup(
|
||||
self.session, self.dataset.model,
|
||||
results.domain, results.context,
|
||||
results.group_by);
|
||||
self.$element.html(self.groups.render());
|
||||
return;
|
||||
}
|
||||
return self.do_reload();
|
||||
});
|
||||
},
|
||||
@@ -338,7 +349,7 @@ openerp.base.ListView = openerp.base.Controller.extend(
|
||||
*/
|
||||
do_add_record: function () {
|
||||
this.notification.notify('Add', "New record");
|
||||
this.switch_to_record(null);
|
||||
this.select_record(null);
|
||||
},
|
||||
/**
|
||||
* Handles deletion of all selected lines
|
||||
@@ -349,10 +360,7 @@ openerp.base.ListView = openerp.base.Controller.extend(
|
||||
}
|
||||
// TODO: implement reorder (drag and drop rows)
|
||||
});
|
||||
_.extend(openerp.base.ListView.prototype, openerp.base.ActionExecutor);
|
||||
|
||||
openerp.base.ListView.List = Class.extend(
|
||||
/** @lends openerp.base.ListView.List# */{
|
||||
openerp.base.ListView.List = Class.extend( /** @lends openerp.base.ListView.List# */{
|
||||
/**
|
||||
* List display for the ListView, handles basic DOM events and transforms
|
||||
* them in the relevant higher-level events, to which the list view (or
|
||||
@@ -464,10 +472,54 @@ openerp.base.ListView.List = Class.extend(
|
||||
// drag and drop
|
||||
// editable?
|
||||
});
|
||||
|
||||
openerp.base.TreeView = openerp.base.Controller.extend({
|
||||
openerp.base.ListView.Groups = Class.extend( /** @lends openerp.base.ListView.Groups# */{
|
||||
/**
|
||||
* Grouped display for the ListView. Handles basic DOM events and interacts
|
||||
* with the :js:class:`~openerp.base.DataGroup` bound to it.
|
||||
*
|
||||
* Provides events similar to those of
|
||||
* :js:class:`~openerp.base.ListView.List`
|
||||
*/
|
||||
init: function (opts) {
|
||||
this.options = opts.options;
|
||||
this.columns = opts.columns;
|
||||
this.datagroup = {};
|
||||
},
|
||||
make_level: function (datagroup) {
|
||||
var self = this, $root = $('<dl>');
|
||||
datagroup.list().then(function (list) {
|
||||
_(list).each(function (group, index) {
|
||||
var $title = $('<dt>')
|
||||
.text(group.grouped_on + ': ' + group.value + ' (' + group.length + ')')
|
||||
.appendTo($root);
|
||||
$title.click(function () {
|
||||
datagroup.get(index, function (new_dataset) {
|
||||
var $content = $('<ul>').appendTo(
|
||||
$('<dd>').insertAfter($title));
|
||||
new_dataset.read_slice([], null, null, function (records) {
|
||||
_(records).each(function (record) {
|
||||
$('<li>')
|
||||
.appendTo($content)
|
||||
.text(_(record).map(function (value, key) {
|
||||
return key + ': ' + value;
|
||||
}).join(', '));
|
||||
});
|
||||
});
|
||||
}, function (new_datagroup) {
|
||||
console.log(new_datagroup);
|
||||
$('<dd>')
|
||||
.insertAfter($title)
|
||||
.append(self.make_level(new_datagroup));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
return $root;
|
||||
},
|
||||
render: function () {
|
||||
return this.make_level(this.datagroup);
|
||||
}
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax:
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
openerp.base.m2o = function(openerp){
|
||||
|
||||
openerp.base.m2o = openerp.base.Controller.extend({
|
||||
init: function(view_manager, element_id, model, dataset, session){
|
||||
this._super(element_id, model, dataset, session);
|
||||
|
||||
this.view_manager = view_manager
|
||||
this.session = session;
|
||||
this.element = element_id.find('input');
|
||||
this.dataset = dataset;
|
||||
var cache = {};
|
||||
var lastXhr;
|
||||
this.relation = model;
|
||||
this.result_ids = []
|
||||
var self = this
|
||||
|
||||
var $input = this.element.autocomplete({
|
||||
|
||||
source: function(request, response){
|
||||
var search_val = request.term;
|
||||
if (search_val in cache) {
|
||||
response(cache[search_val]);
|
||||
return;
|
||||
}
|
||||
//pass request to server
|
||||
lastXhr = self.dataset.name_search(search_val, function(obj, status, xhr){
|
||||
var result = obj.result
|
||||
var values = [];
|
||||
if (!result.length) {
|
||||
values.push({'value': 'Create...', id: 'create'})
|
||||
}
|
||||
|
||||
$.each(result, function(i, val){
|
||||
values.push({
|
||||
value: val[1],
|
||||
id: val[0],
|
||||
orig_val: val[1]
|
||||
});
|
||||
self.result_ids.push(result[i][0])
|
||||
});
|
||||
|
||||
if (values.length > 10) {
|
||||
values = values.slice(0, 10);
|
||||
values.push({'value': 'More...', id: 'more'})
|
||||
}
|
||||
//process response
|
||||
cache[search_val] = values;
|
||||
response(values);
|
||||
});
|
||||
return;
|
||||
},
|
||||
|
||||
select: function(event, ui){
|
||||
ui.item.value = ui.item.orig_val? ui.item.orig_val : self.element.data( "autocomplete" ).term
|
||||
if (ui.item.id == 'more') {
|
||||
self.dataset.ids = self.result_ids;
|
||||
self.dataset.count = self.dataset.ids.length;
|
||||
self.dataset.domain = []
|
||||
self.element.val('')
|
||||
var pop = new openerp.base.form.Many2XSelectPopup(null, self.session);
|
||||
pop.select_element(self.relation, self.dataset);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ui.item.id == 'create') {
|
||||
var val = self.element.val()
|
||||
self.dataset.create({'name': ui.item.value},
|
||||
function(r){}, function(r){
|
||||
var element_id = _.uniqueId("act_window_dialog");
|
||||
var dialog = jQuery('<div>',
|
||||
{'id': element_id
|
||||
}).dialog({
|
||||
modal: true,
|
||||
minWidth: 800
|
||||
});
|
||||
self.element.val('')
|
||||
var event_form = new openerp.base.FormView(self.view_manager, self.session, element_id, self.dataset, false);
|
||||
event_form.start();
|
||||
});
|
||||
$input.val(self.element.data( "autocomplete" ).term);
|
||||
return true;
|
||||
}
|
||||
self.element.attr('m2o_id', ui.item.id);
|
||||
},
|
||||
|
||||
minLength: 0,
|
||||
|
||||
focus: function(event, ui) {
|
||||
if (ui.item.id == ('create')) {
|
||||
return true;
|
||||
}
|
||||
ui.item.value = self.element.data("autocomplete").term.length ? self.element.val() + '[' + ui.item.orig_val.substring(self.element.data("autocomplete").term.length) + ']' : this.lastSearch
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
$("<div type='button' class='ui_combo'> </div>")
|
||||
.attr("tabIndex", -1)
|
||||
.attr("title", "Show All Items")
|
||||
.insertAfter($input)
|
||||
.button({
|
||||
icons: {
|
||||
primary: "ui-icon-triangle-1-s"
|
||||
},
|
||||
text: false
|
||||
})
|
||||
.removeClass("ui-corner-all")
|
||||
.addClass("ui-corner-right ui-button-icon")
|
||||
.click(function() {
|
||||
// close if already visible
|
||||
if ($input.autocomplete("widget").is(":visible")) {
|
||||
$input.autocomplete( "close" );
|
||||
return;
|
||||
}
|
||||
$(this).blur();
|
||||
$input.autocomplete("search", "" );
|
||||
$input.focus();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -121,7 +121,10 @@ openerp.base.SearchView = openerp.base.Controller.extend({
|
||||
'lines': lines,
|
||||
'defaults': this.defaults
|
||||
});
|
||||
this.$element.html(render);
|
||||
// We don't understand why the following commented line does not work in Chrome but
|
||||
// the non-commented line does. As far as we investigated, only God knows.
|
||||
//this.$element.html(render);
|
||||
jQuery(render).appendTo(this.$element);
|
||||
|
||||
var f = this.$element.find('form');
|
||||
this.$element.find('form')
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
/*---------------------------------------------------------
|
||||
* OpenERP base library
|
||||
*---------------------------------------------------------*/
|
||||
|
||||
openerp.base.view_tree = function(openerp) {
|
||||
|
||||
openerp.base.views.add('tree', 'openerp.base.TreeView');
|
||||
openerp.base.TreeView = openerp.base.Controller.extend({
|
||||
/**
|
||||
* Genuine tree view (the one displayed as a tree, not the list)
|
||||
*/
|
||||
start: function () {
|
||||
this._super();
|
||||
this.$element.append('Tree view');
|
||||
},
|
||||
do_show: function () {
|
||||
this.$element.show();
|
||||
},
|
||||
do_hide: function () {
|
||||
this.$element.hide();
|
||||
}
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax:
|
||||
@@ -17,17 +17,23 @@ openerp.base.ActionManager = openerp.base.Controller.extend({
|
||||
/**
|
||||
* Process an action
|
||||
* Supported actions: act_window
|
||||
*
|
||||
* If the action contains a 'no_sidebar' key, the resulting view will never contain a sidebar.
|
||||
*/
|
||||
do_action: function(action) {
|
||||
var self = this;
|
||||
action.flags = _.extend({
|
||||
sidebar : true,
|
||||
search_view : true,
|
||||
new_window : false,
|
||||
views_switcher : true,
|
||||
toolbar : true,
|
||||
pager : true
|
||||
}, action.flags || {});
|
||||
// instantiate the right controllers by understanding the action
|
||||
switch (action.type) {
|
||||
case 'ir.actions.act_window':
|
||||
if (action.target == 'new') {
|
||||
var element_id = _.uniqueId("act_window_dialog");
|
||||
var dialog = $('<div id="'+element_id+'"></div>');
|
||||
var dialog = $('<div id="' + element_id + '"></div>');
|
||||
dialog.dialog({
|
||||
title: action.name,
|
||||
modal: true,
|
||||
@@ -37,18 +43,21 @@ 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);
|
||||
viewmanager.start();
|
||||
this.dialog_stack.push(viewmanager);
|
||||
} else if (action.flags.new_window) {
|
||||
action.flags.new_window = false;
|
||||
this.rpc("/base/session/save_session_action", { the_action : action}, function(key) {
|
||||
var url = window.location.protocol + "//" + window.location.host +
|
||||
window.location.pathname + "?" + jQuery.param({ s_action : "" + key });
|
||||
window.open(url);
|
||||
});
|
||||
} else {
|
||||
if (this.viewmanager) {
|
||||
this.viewmanager.stop();
|
||||
}
|
||||
var sidebar = true;
|
||||
if(action.no_sidebar) {
|
||||
sidebar = false;
|
||||
}
|
||||
this.viewmanager = new openerp.base.ViewManagerAction(this.session, this.element_id, action, sidebar);
|
||||
this.viewmanager = new openerp.base.ViewManagerAction(this.session, this.element_id, action);
|
||||
this.viewmanager.start();
|
||||
}
|
||||
break;
|
||||
@@ -63,63 +72,6 @@ openerp.base.ActionManager = openerp.base.Controller.extend({
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Mixin for action-executing objects, provides handling of OpenERP actions to
|
||||
* all clients.
|
||||
*
|
||||
* Mix into existing classes via ``_.extend`` of the class's prototype.
|
||||
*
|
||||
* @class
|
||||
*/
|
||||
openerp.base.ActionExecutor =
|
||||
/**
|
||||
* @lends openerp.base.ActionExecutor#
|
||||
*/ {
|
||||
/**
|
||||
* Fetches and executes the action identified by ``action_data``.
|
||||
*
|
||||
* @param {Object} action_data the action descriptor data
|
||||
* @param {String} action_data.name the action name, used to uniquely identify the action to find and execute it
|
||||
* @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 {openerp.base.DataSet} dataset a dataset object used to communicate with the server
|
||||
* @param {openerp.base.ActionManager} action_manager object able to actually execute the action, if any is fetched
|
||||
* @param {Number} [record_id] the identifier of the object on which the action is to be applied
|
||||
* @param {Function} on_no_action callback to execute if the action does not generate any result (no new action)
|
||||
*/
|
||||
execute_action: function (action_data, dataset, action_manager, record_id, on_no_action) {
|
||||
var handler = function (r) {
|
||||
if (r.result && r.result.constructor == Object) {
|
||||
action_manager.do_action(r.result);
|
||||
} else {
|
||||
on_no_action(r.result);
|
||||
}
|
||||
};
|
||||
|
||||
if (action_data.special) {
|
||||
handler({
|
||||
result : { type: 'ir.actions.act_window_close' }
|
||||
});
|
||||
} else {
|
||||
var context = _.extend({}, dataset.context, action_data.context || {});
|
||||
switch(action_data.type) {
|
||||
case 'object':
|
||||
return dataset.call(action_data.name, [record_id], [context], handler);
|
||||
case 'action':
|
||||
return this.rpc('/base/action/load', { action_id: parseInt(action_data.name, 10) }, handler);
|
||||
default:
|
||||
return dataset.exec_workflow(record_id, action_data.name, handler);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Registry for all the main views
|
||||
*/
|
||||
openerp.base.views = new openerp.base.Registry();
|
||||
|
||||
openerp.base.ViewManager = openerp.base.Controller.extend({
|
||||
init: function(session, element_id, dataset, views) {
|
||||
this._super(session, element_id);
|
||||
@@ -129,6 +81,7 @@ openerp.base.ViewManager = openerp.base.Controller.extend({
|
||||
this.active_view = null;
|
||||
this.views_src = views;
|
||||
this.views = {};
|
||||
this.flags = this.flags || {};
|
||||
},
|
||||
/**
|
||||
* @returns {jQuery.Deferred} initial view loading promise
|
||||
@@ -143,6 +96,9 @@ openerp.base.ViewManager = openerp.base.Controller.extend({
|
||||
_.each(this.views_src, function(view) {
|
||||
self.views[view[1]] = { view_id: view[0], controller: null };
|
||||
});
|
||||
if (this.flags.views_switcher === false) {
|
||||
this.$element.find('.oe_vm_switch').hide();
|
||||
}
|
||||
// switch to the first one in sequence
|
||||
return this.on_mode_switch(this.views_src[0][1]);
|
||||
},
|
||||
@@ -166,7 +122,7 @@ openerp.base.ViewManager = openerp.base.Controller.extend({
|
||||
this.views[view_type].controller = controller;
|
||||
}
|
||||
|
||||
if(this.searchview) {
|
||||
if (this.searchview) {
|
||||
if (view.controller.searchable === false) {
|
||||
this.searchview.hide();
|
||||
} else {
|
||||
@@ -202,6 +158,9 @@ openerp.base.ViewManager = openerp.base.Controller.extend({
|
||||
this.searchview.stop();
|
||||
}
|
||||
this.searchview = new openerp.base.SearchView(this, this.session, this.element_id + "_search", this.dataset, view_id, search_defaults);
|
||||
if (this.flags.search_view === false) {
|
||||
this.searchview.hide();
|
||||
}
|
||||
this.searchview.on_search.add(function(domains, contexts, groupbys) {
|
||||
self.views[self.active_view].controller.do_search.call(
|
||||
self, domains.concat(self.domains()),
|
||||
@@ -240,7 +199,7 @@ openerp.base.ViewManager = openerp.base.Controller.extend({
|
||||
});
|
||||
|
||||
openerp.base.ViewManagerAction = openerp.base.ViewManager.extend({
|
||||
init: function(session, element_id, action, sidebar) {
|
||||
init: function(session, element_id, action) {
|
||||
var dataset;
|
||||
if(!action.res_id) {
|
||||
dataset = new openerp.base.DataSetSearch(session, action.res_model);
|
||||
@@ -251,9 +210,10 @@ openerp.base.ViewManagerAction = openerp.base.ViewManager.extend({
|
||||
}
|
||||
this._super(session, element_id, dataset, action.views);
|
||||
this.action = action;
|
||||
this.sidebar = sidebar;
|
||||
if (sidebar)
|
||||
this.flags = this.action.flags || {};
|
||||
if (this.flags.sidebar) {
|
||||
this.sidebar = new openerp.base.Sidebar(null, this);
|
||||
}
|
||||
},
|
||||
start: function() {
|
||||
var inital_view_loaded = this._super();
|
||||
@@ -346,7 +306,10 @@ openerp.base.Sidebar = openerp.base.BaseWidget.extend({
|
||||
var i = $this.attr("data-i");
|
||||
var j = $this.attr("data-j");
|
||||
var action = self.sections[i].elements[j];
|
||||
(new openerp.base.ExternalActionManager(self.view_manager.session, null)) .handle_action(action);
|
||||
action.flags = {
|
||||
new_window : true
|
||||
}
|
||||
self.session.action_manager.do_action(action);
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
});
|
||||
@@ -357,89 +320,51 @@ openerp.base.Sidebar = openerp.base.BaseWidget.extend({
|
||||
}
|
||||
});
|
||||
|
||||
openerp.base.ExternalActionManager = openerp.base.Controller.extend({
|
||||
handle_action: function(action) {
|
||||
if(action.type=="ir.actions.act_window") {
|
||||
if(action.target=="new") {
|
||||
var element_id = _.uniqueId("act_window_dialog");
|
||||
var dialog = $('<div id="'+element_id+'"></div>');
|
||||
dialog.dialog({
|
||||
title: action.name
|
||||
});
|
||||
var viewmanager = new openerp.base.ViewManagerAction(this.session ,element_id, action, false);
|
||||
viewmanager.start();
|
||||
} else if (action.target == "current") {
|
||||
this.rpc("/base/session/save_session_action", {the_action:action}, function(key) {
|
||||
var url = window.location.protocol + "//" + window.location.host +
|
||||
window.location.pathname + "?" + jQuery.param({s_action:""+key});
|
||||
window.open(url);
|
||||
});
|
||||
openerp.base.View = openerp.base.Controller.extend({
|
||||
/**
|
||||
* Fetches and executes the action identified by ``action_data``.
|
||||
*
|
||||
* @param {Object} action_data the action descriptor data
|
||||
* @param {String} action_data.name the action name, used to uniquely identify the action to find and execute it
|
||||
* @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 {openerp.base.DataSet} dataset a dataset object used to communicate with the server
|
||||
* @param {openerp.base.ActionManager} action_manager object able to actually execute the action, if any is fetched
|
||||
* @param {Number} [record_id] the identifier of the object on which the action is to be applied
|
||||
* @param {Function} on_no_action callback to execute if the action does not generate any result (no new action)
|
||||
*/
|
||||
execute_action: function (action_data, dataset, action_manager, record_id, on_no_action) {
|
||||
var handler = function (r) {
|
||||
if (r.result && r.result.constructor == Object) {
|
||||
action_manager.do_action(r.result);
|
||||
} else {
|
||||
on_no_action(r.result);
|
||||
}
|
||||
};
|
||||
|
||||
if (action_data.special) {
|
||||
handler({
|
||||
result : { type: 'ir.actions.act_window_close' }
|
||||
});
|
||||
} else {
|
||||
var context = _.extend({}, dataset.context, action_data.context || {});
|
||||
switch(action_data.type) {
|
||||
case 'object':
|
||||
return dataset.call(action_data.name, [record_id], [context], handler);
|
||||
case 'action':
|
||||
return this.rpc('/base/action/load', { action_id: parseInt(action_data.name, 10) }, handler);
|
||||
default:
|
||||
return dataset.exec_workflow(record_id, action_data.name, handler);
|
||||
}
|
||||
}
|
||||
// TODO: show an error like "not implemented" here
|
||||
// since we don't currently have any way to handle errors do you have any better idea
|
||||
// than using todos?
|
||||
}
|
||||
});
|
||||
|
||||
openerp.base.views.add('calendar', 'openerp.base.CalendarView');
|
||||
openerp.base.CalendarView = openerp.base.Controller.extend({
|
||||
start: function () {
|
||||
this._super();
|
||||
this.$element.append('Calendar view');
|
||||
},
|
||||
do_show: function () {
|
||||
this.$element.show();
|
||||
},
|
||||
do_hide: function () {
|
||||
this.$element.hide();
|
||||
}
|
||||
});
|
||||
|
||||
openerp.base.views.add('gantt', 'openerp.base.GanttView');
|
||||
openerp.base.GanttView = openerp.base.Controller.extend({
|
||||
start: function () {
|
||||
this._super();
|
||||
this.$element.append('Gantt view');
|
||||
},
|
||||
do_show: function () {
|
||||
this.$element.show();
|
||||
},
|
||||
do_hide: function () {
|
||||
this.$element.hide();
|
||||
}
|
||||
});
|
||||
|
||||
openerp.base.views.add('tree', 'openerp.base.TreeView');
|
||||
openerp.base.TreeView = openerp.base.Controller.extend({
|
||||
/**
|
||||
* Genuine tree view (the one displayed as a tree, not the list)
|
||||
* Registry for all the main views
|
||||
*/
|
||||
start: function () {
|
||||
this._super();
|
||||
this.$element.append('Tree view');
|
||||
},
|
||||
do_show: function () {
|
||||
this.$element.show();
|
||||
},
|
||||
do_hide: function () {
|
||||
this.$element.hide();
|
||||
}
|
||||
});
|
||||
|
||||
openerp.base.views.add('graph', 'openerp.base.GraphView');
|
||||
openerp.base.GraphView = openerp.base.Controller.extend({
|
||||
start: function () {
|
||||
this._super();
|
||||
this.$element.append('Graph view');
|
||||
},
|
||||
do_show: function () {
|
||||
this.$element.show();
|
||||
},
|
||||
do_hide: function () {
|
||||
this.$element.hide();
|
||||
}
|
||||
});
|
||||
openerp.base.views = new openerp.base.Registry();
|
||||
|
||||
openerp.base.ProcessView = openerp.base.Controller.extend({
|
||||
});
|
||||
|
||||
@@ -18,8 +18,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div id="oe_login" class="login"></div>
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" height="100%"
|
||||
t-att-class="'main_table' + (typeof kitten == 'undefined' ? '' : ' kitten-mode-activated')">
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" height="100%" class="main_table">
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<div id="oe_header" class="header"></div>
|
||||
@@ -55,8 +54,7 @@
|
||||
</t>
|
||||
<t t-name="Header">
|
||||
<a href="/" class="company_logo_link">
|
||||
<img t-att-src="typeof kitten == 'undefined' ? '/base/static/src/img/logo.png' :
|
||||
'http://placekitten.com/g/179/46'" border="0" class="company_logo"/>
|
||||
<div class="company_logo" />
|
||||
</a>
|
||||
<h1 class="header_title" t-if="session.session_is_valid()">
|
||||
<span class="company">$company</span> - (<span class="database">$database</span>)<br/>
|
||||
@@ -259,7 +257,7 @@
|
||||
<t t-foreach="row" t-as="td">
|
||||
<td t-att-colspan="td.colspan gt 1 ? td.colspan : undefined"
|
||||
t-att-width="td.width ? td.width : undefined"
|
||||
t-att-nowrap="td.is_field_label ? 'true' : undefined"
|
||||
t-att-nowrap="td.is_field_label or td.is_field_m2o? 'true' : undefined"
|
||||
t-att-valign="td.table ? 'top' : undefined"
|
||||
t-att-id="td.element_id"
|
||||
t-att-class="'oe_form_' + (td.is_field_label ? 'label' : (td.field ? 'field_' + td.type : td.type))"
|
||||
@@ -306,6 +304,20 @@
|
||||
t-att-class="'field_' + widget.type" style="width: 100%"
|
||||
/>
|
||||
</t>
|
||||
<t t-name="FieldEmail">
|
||||
<table cellpadding="0" cellspacing="0" border="0" width="100%">
|
||||
<tr>
|
||||
<td width="100%">
|
||||
<t t-call="FieldChar"/>
|
||||
</td>
|
||||
<td width="16">
|
||||
<button class="button" title="Send an e-mail with your default e-mail client">
|
||||
<img src="/base/static/src/img/icons/terp-mail-message-new.png"/>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</t>
|
||||
<t t-name="FieldText">
|
||||
<textarea rows="6" style="width: 100%;"
|
||||
t-att-name="widget.name"
|
||||
@@ -320,13 +332,6 @@
|
||||
t-att-class="'field_' + widget.type"
|
||||
/>
|
||||
</t>
|
||||
<t t-name="FieldDatetime">
|
||||
<input type="text" style="width: 100%"
|
||||
t-att-name="widget.name"
|
||||
t-att-id="widget.element_id + '_field'"
|
||||
t-att-class="'field_' + widget.type"
|
||||
/>
|
||||
</t>
|
||||
<t t-name="FieldSelection">
|
||||
<select
|
||||
t-att-name="widget.name"
|
||||
@@ -345,7 +350,9 @@
|
||||
t-att-name="widget.name"
|
||||
t-att-id="widget.element_id + '_field'"
|
||||
t-att-class="'field_' + widget.type"
|
||||
t-att-type="widget.type"
|
||||
style="width: 100%;"/>
|
||||
|
||||
</t>
|
||||
<t t-name="FieldOne2Many">
|
||||
<div t-att-id="widget.element_id">
|
||||
@@ -369,6 +376,15 @@
|
||||
<span></span>
|
||||
</div>
|
||||
</t>
|
||||
<t t-name="FieldImage">
|
||||
<img align="left" border="1" style="display: none"
|
||||
t-att-id="widget.element_id + '_field'"
|
||||
t-att-name="widget.name"
|
||||
t-att-class="'field_' + widget.type"
|
||||
t-att-width="widget.node.attrs.img_width || widget.node.attrs.width"
|
||||
t-att-height="widget.node.attrs.img_height || widget.node.attrs.height"
|
||||
/>
|
||||
</t>
|
||||
<t t-name="WidgetButton">
|
||||
<button type="button"
|
||||
t-att-id="widget.element_id + '_button'"
|
||||
@@ -382,7 +398,7 @@
|
||||
<h2 class="oe_view_title"><t t-esc="view.attrs['string']"/></h2>
|
||||
<form>
|
||||
<t t-call="SearchView.render_lines"/>
|
||||
<div style="text-align:right;">
|
||||
<div class="oe_search-view-buttons" style="text-align:right;">
|
||||
<input type="submit" value="Search"/>
|
||||
<input type="reset" value="Clear"/>
|
||||
</div>
|
||||
@@ -418,7 +434,7 @@
|
||||
<span t-if="attrs.help">(?)</span>
|
||||
</label>
|
||||
<div style="white-space: nowrap;">
|
||||
<input type="text" t-att-name="attrs.name"
|
||||
<input type="text" size="15" t-att-name="attrs.name"
|
||||
t-att-autofocus="attrs.default_focus === '1' ? 'autofocus' : undefined"
|
||||
t-att-id="element_id"
|
||||
t-att-value="defaults[attrs.name] || ''"/>
|
||||
@@ -490,7 +506,8 @@
|
||||
<t t-set="expand" t-value="false"/>
|
||||
<t t-set="label" t-value="'Custom Filters'"/>
|
||||
<t t-set="content">
|
||||
<div class="searchview_extended_groups_list"/>
|
||||
<div class="searchview_extended_groups_list">
|
||||
</div>
|
||||
<button class="searchview_extended_add_group"
|
||||
type="button"><span>Add group of conditions</span></button>
|
||||
</t>
|
||||
@@ -570,4 +587,19 @@
|
||||
</p>
|
||||
</div>
|
||||
</t>
|
||||
<t t-name="Many2XSelectPopup">
|
||||
<div t-att-id="element_id">
|
||||
<div t-att-id="element_id + '_search'"></div>
|
||||
<div t-att-id="element_id + '_view_list'"></div>
|
||||
<div t-att-id="element_id + '_view_form'"></div>
|
||||
</div>
|
||||
</t>
|
||||
<t t-name="Many2XSelectPopup.search.buttons">
|
||||
<button type="button" class="oe_many2xselectpopup-search-new">New</button>
|
||||
<button type="button" class="oe_many2xselectpopup-search-close">Close</button>
|
||||
</t>
|
||||
<t t-name="Many2XSelectPopup.form.buttons">
|
||||
<button type="button" class="oe_many2xselectpopup-form-save">Save</button>
|
||||
<button type="button" class="oe_many2xselectpopup-form-close">Close</button>
|
||||
</t>
|
||||
</templates>
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
<script src="/base/static/src/js/data.js"></script>
|
||||
<script src="/base/static/src/js/views.js"></script>
|
||||
<script src="/base/static/src/js/search.js"></script>
|
||||
<script src="/base/static/src/js/m2o.js"></script>
|
||||
<script src="/base/static/src/js/form.js"></script>
|
||||
<script src="/base/static/src/js/list.js"></script>
|
||||
<script type="text/javascript">
|
||||
|
||||
@@ -107,9 +107,11 @@ openerp.base.form.Action = openerp.base.form.Widget.extend({
|
||||
|
||||
on_load_action: function(result) {
|
||||
var action = result.action;
|
||||
action.search_view = action.search_view_id = false;
|
||||
action.no_sidebar = true;
|
||||
|
||||
action.flags = {
|
||||
search_view : false,
|
||||
sidebar : false,
|
||||
views_switcher : false
|
||||
}
|
||||
var node_attrs = this.node.attrs;
|
||||
var content_id = jQuery('#'+node_attrs.name).find('.portlet-content').attr('id')
|
||||
var action_manager = new openerp.base.ActionManager(this.session, content_id);
|
||||
@@ -143,4 +145,4 @@ openerp.base.form.widgets.add('hpaned', 'openerp.base.form.Board');
|
||||
openerp.base.form.widgets.add('child', 'openerp.base.form.Dashbar');
|
||||
openerp.base.form.widgets.add('vpaned', 'openerp.base.form.Vpaned');
|
||||
openerp.base.form.widgets.add('action', 'openerp.base.form.Action');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
"version": "2.0",
|
||||
"depends": ['base'],
|
||||
"js": [
|
||||
'static/lib/dhtmlxGantt/codebase/dhtmlxcommon.js',
|
||||
'static/lib/dhtmlxGantt/codebase/dhtmlxgantt.js',
|
||||
'static/lib/dhtmlxGantt/sources/dhtmlxcommon.js',
|
||||
'static/lib/dhtmlxGantt/sources/dhtmlxgantt.js',
|
||||
'static/src/js/gantt.js'
|
||||
],
|
||||
"css": ['static/lib/dhtmlxGantt/codebase/dhtmlxgantt.css'],
|
||||
|
||||
@@ -1,184 +1,11 @@
|
||||
import glob, os
|
||||
from xml.etree import ElementTree
|
||||
import math
|
||||
import simplejson
|
||||
import openerpweb
|
||||
import time
|
||||
import datetime
|
||||
from base.controllers.main import Xml2Json
|
||||
|
||||
from base.controllers.main import View
|
||||
|
||||
COLOR_PALETTE = ['#f57900', '#cc0000', '#d400a8', '#75507b', '#3465a4', '#73d216', '#c17d11', '#edd400',
|
||||
'#fcaf3e', '#ef2929', '#ff00c9', '#ad7fa8', '#729fcf', '#8ae234', '#e9b96e', '#fce94f',
|
||||
'#ff8e00', '#ff0000', '#b0008c', '#9000ff', '#0078ff', '#00ff00', '#e6ff00', '#ffff00',
|
||||
'#905000', '#9b0000', '#840067', '#510090', '#0000c9', '#009b00', '#9abe00', '#ffc900', ]
|
||||
|
||||
_colorline = ['#%02x%02x%02x' % (25 + ((r + 10) % 11) * 23, 5 + ((g + 1) % 11) * 20, 25 + ((b + 4) % 11) * 23) for r in range(11) for g in range(11) for b in range(11) ]
|
||||
|
||||
def choice_colors(n):
|
||||
if n > len(COLOR_PALETTE):
|
||||
return _colorline[0:-1:len(_colorline) / (n + 1)]
|
||||
elif n:
|
||||
return COLOR_PALETTE[:n]
|
||||
return []
|
||||
|
||||
class GanttView(openerpweb.Controller):
|
||||
class GanttView(View):
|
||||
_cp_path = "/base_gantt/ganttview"
|
||||
|
||||
date_start = None
|
||||
date_delay = None
|
||||
date_stop = None
|
||||
color_field = None
|
||||
|
||||
day_length = 8
|
||||
fields = {}
|
||||
events = []
|
||||
calendar_fields = {}
|
||||
ids = []
|
||||
model = ''
|
||||
domain = []
|
||||
context = {}
|
||||
event_res = []
|
||||
colors = {}
|
||||
color_values = []
|
||||
|
||||
|
||||
@openerpweb.jsonrequest
|
||||
def load(self, req, model, view_id):
|
||||
m = req.session.model(model)
|
||||
r = m.fields_view_get(view_id, 'gantt')
|
||||
r["arch"] = Xml2Json.convert_to_structure(r["arch"])
|
||||
return {'fields_view':r}
|
||||
|
||||
@openerpweb.jsonrequest
|
||||
def get_events(self, req, **kw):
|
||||
fields_view = self.fields_view_get(req, model, view_id, 'gantt')
|
||||
return {'fields_view':fields_view}
|
||||
|
||||
self.model = kw['model']
|
||||
self.fields = kw['fields']
|
||||
self.day_length = kw['day_length']
|
||||
self.calendar_fields = kw['calendar_fields']
|
||||
self.color_field = kw.get('color_field') or self.color_field or None
|
||||
self.fields[self.color_field] = ""
|
||||
self.colors = kw.get('colors') or {}
|
||||
self.text = self.calendar_fields['text']['name']
|
||||
|
||||
self.date_start = self.calendar_fields['date_start']['name']
|
||||
self.fields[self.date_start] = ""
|
||||
|
||||
if self.calendar_fields.get('date_stop'):
|
||||
self.date_stop = self.calendar_fields['date_stop']['name']
|
||||
self.fields[self.date_stop] = ""
|
||||
if self.calendar_fields.get('date_delay'):
|
||||
self.date_delay = self.calendar_fields['date_delay']['name']
|
||||
self.fields[self.date_delay] = ""
|
||||
if self.calendar_fields.get('parent'):
|
||||
self.parent = self.calendar_fields['parent']['name']
|
||||
self.fields[self.parent] = ""
|
||||
|
||||
model = req.session.model(self.model)
|
||||
event_ids = model.search([])
|
||||
|
||||
return self.create_event(event_ids, model)
|
||||
|
||||
def create_event(self, event_ids, model):
|
||||
|
||||
self.events = model.read(event_ids, self.fields.keys())
|
||||
result = []
|
||||
for evt in self.events:
|
||||
|
||||
event_res = {}
|
||||
key = evt[self.color_field]
|
||||
name = key
|
||||
value = key
|
||||
if isinstance(key, list): # M2O, XMLRPC returns List instead of Tuple
|
||||
name = key[0]
|
||||
value = key[-1]
|
||||
evt[self.color_field] = key = key[-1]
|
||||
if isinstance(key, tuple): # M2O
|
||||
value, name = key
|
||||
|
||||
self.colors[key] = (name, value, None)
|
||||
|
||||
st_date = evt.get(self.date_start)
|
||||
if st_date:
|
||||
self.set_format(st_date)
|
||||
if self.date_delay:
|
||||
duration = evt.get(self.date_delay)
|
||||
else:
|
||||
en_date = evt.get(self.date_stop)
|
||||
|
||||
duration = (time.mktime(time.strptime(en_date, self.format))-\
|
||||
time.mktime(time.strptime(st_date, self.format)))/ (60 * 60)
|
||||
|
||||
if duration > self.day_length :
|
||||
d = math.floor(duration / 24)
|
||||
h = duration % 24
|
||||
duration = d * self.day_length + h
|
||||
|
||||
event_res = {}
|
||||
event_res['start_date'] = st_date
|
||||
event_res['duration'] = duration
|
||||
event_res['text'] = evt.get(self.text)
|
||||
event_res['id'] = evt['id']
|
||||
event_res['parent'] = evt.get(self.parent)
|
||||
result.append(event_res)
|
||||
|
||||
colors = choice_colors(len(self.colors))
|
||||
for i, (key, value) in enumerate(self.colors.items()):
|
||||
self.colors[key] = [value[0], value[1], colors[i]]
|
||||
|
||||
return {'result': result,'sidebar': self.colors}
|
||||
|
||||
def set_format(self, st_date):
|
||||
if len(st_date) == 10 :
|
||||
self.format = "%Y-%m-%d"
|
||||
else :
|
||||
self.format = "%Y-%m-%d %H:%M:%S"
|
||||
return
|
||||
|
||||
def check_format(self, date):
|
||||
if self.format == "%Y-%m-%d %H:%M:%S":
|
||||
date = date + " 00:00:00"
|
||||
return date
|
||||
|
||||
@openerpweb.jsonrequest
|
||||
def on_event_resize(self, req, **kw):
|
||||
if self.date_delay:
|
||||
key = self.date_delay
|
||||
value = kw['duration']
|
||||
else:
|
||||
key = self.date_stop
|
||||
value = self.check_format(kw['end_date'])
|
||||
try:
|
||||
model = req.session.model(self.model)
|
||||
res = model.write(kw['id'], {key : value})
|
||||
except Exception, e:
|
||||
print "eeeeeeeeeeeeeeeeeeeeeeeeeee",e
|
||||
return True
|
||||
|
||||
@openerpweb.jsonrequest
|
||||
def on_event_drag(self, req, **kw):
|
||||
start_date = self.check_format(kw['start_date'])
|
||||
if self.date_delay:
|
||||
key = self.date_delay
|
||||
value = kw['duration']
|
||||
else:
|
||||
key = self.date_stop
|
||||
value = self.check_format(kw['end_date'])
|
||||
try:
|
||||
model = req.session.model(self.model)
|
||||
res = model.write(kw['id'], {self.date_start : start_date, key : value})
|
||||
except Exception, e:
|
||||
print "eeeeeeeeeeeeeeeeeeeeeeeeeee",e
|
||||
return True
|
||||
|
||||
@openerpweb.jsonrequest
|
||||
def reload_gantt(self, req, **kw):
|
||||
|
||||
model = req.session.model(kw['model'])
|
||||
if (kw['domain']):
|
||||
domain = (kw['color_field'],'in', kw['domain'])
|
||||
event_ids = model.search([domain])
|
||||
else:
|
||||
event_ids = model.search([])
|
||||
return self.create_event(event_ids, model)
|
||||
|
||||
@@ -173,8 +173,9 @@ GanttProjectInfo.prototype.getTaskByIdInTree = function(parentTask, id)
|
||||
* @type: public
|
||||
* @topic: 0
|
||||
*/
|
||||
function GanttTaskInfo(id, name, est, duration, percentCompleted, predecessorTaskId)
|
||||
function GanttTaskInfo(id, name, est, duration, percentCompleted, predecessorTaskId, color)
|
||||
{
|
||||
this.color = color || "white";
|
||||
this.Id = id;
|
||||
this.Name = name;
|
||||
this.EST = est;
|
||||
@@ -348,7 +349,7 @@ GanttProject.prototype.create = function()
|
||||
* @type: public
|
||||
* @topic: 0
|
||||
*/
|
||||
function GanttChart()
|
||||
function GanttChart(day_length)
|
||||
{
|
||||
this.Error = new GanttError();
|
||||
this.dhtmlXMLSenderObject = new dhtmlXMLSenderObject(this);
|
||||
@@ -356,7 +357,7 @@ function GanttChart()
|
||||
//settings
|
||||
this.heightTaskItem = 12;
|
||||
this.dayInPixels = 24;
|
||||
this.hoursInDay = 8;
|
||||
this.hoursInDay = day_length;
|
||||
this._showTreePanel = true;
|
||||
this._showTooltip = true;
|
||||
this.isShowDescTask = false;
|
||||
@@ -4869,11 +4870,13 @@ GanttTask.prototype.createTaskItem = function()
|
||||
cellTblTask.height = this.Chart.heightTaskItem + "px";
|
||||
cellTblTask.width = this.TaskInfo.PercentCompleted + "%";
|
||||
cellTblTask.style.lineHeight = "1px";
|
||||
cellTblTask.style.backgroundColor = self.TaskInfo.color;
|
||||
|
||||
var imgPr = document.createElement("img");
|
||||
imgPr.style.width = (this.TaskInfo.PercentCompleted * this.TaskInfo.Duration * this.Chart.hourInPixelsWork) / 100 + "px";
|
||||
imgPr.style.height = this.Chart.heightTaskItem + "px";
|
||||
cellTblTask.appendChild(imgPr);
|
||||
imgPr.src = this.Chart.imgs + "progress_filled.png";
|
||||
//imgPr.src = this.Chart.imgs + "progress_filled.png";
|
||||
}
|
||||
|
||||
if (this.TaskInfo.PercentCompleted != 100)
|
||||
@@ -4943,9 +4946,9 @@ GanttTask.prototype.createTaskItem = function()
|
||||
}
|
||||
|
||||
var taskClick = function() {
|
||||
self.Chart.callEvent("onTaskClick", [self]);
|
||||
self.Chart.callEvent("onTaskDblClick", [self]);
|
||||
};
|
||||
this.addEvent(divMove, 'click', taskClick, false);
|
||||
this.addEvent(divMove, 'dblclick', taskClick, false);
|
||||
|
||||
if (this.Chart.isEditable)
|
||||
{
|
||||
|
||||
@@ -7,7 +7,8 @@ QWeb.add_template('/base_gantt/static/src/xml/base_gantt.xml');
|
||||
openerp.base.views.add('gantt', 'openerp.base_gantt.GanttView');
|
||||
openerp.base_gantt.GanttView = openerp.base.Controller.extend({
|
||||
|
||||
init: function(view_manager, session, element_id, dataset, view_id) {
|
||||
init: function(view_manager, session, element_id, dataset, view_id) {
|
||||
|
||||
this._super(session, element_id);
|
||||
this.view_manager = view_manager;
|
||||
this.dataset = dataset;
|
||||
@@ -16,207 +17,326 @@ openerp.base_gantt.GanttView = openerp.base.Controller.extend({
|
||||
this.fields_views = {};
|
||||
this.widgets = {};
|
||||
this.widgets_counter = 0;
|
||||
this.fields = {};
|
||||
this.datarecord = {};
|
||||
this.fields = this.dataset.fields ? this.dataset.fields: {};
|
||||
this.ids = this.dataset.ids;
|
||||
this.name = "";
|
||||
this.date_start = "";
|
||||
this.date_delay = "";
|
||||
this.date_stop = "";
|
||||
this.color_field = "";
|
||||
this.day_lenth = 8;
|
||||
this.colors = [];
|
||||
this.color_values = [];
|
||||
this.calendar_fields = {};
|
||||
this.info_fields = [];
|
||||
this.domain = this.dataset._domain ? this.dataset._domain: [];
|
||||
this.context = {};
|
||||
},
|
||||
do_show: function () {
|
||||
// TODO: re-trigger search
|
||||
this.$element.show();
|
||||
},
|
||||
do_hide: function () {
|
||||
this.$element.hide();
|
||||
},
|
||||
|
||||
start: function() {
|
||||
this.rpc("/base_gantt/ganttview/load", {"model": this.model, "view_id": this.view_id}, this.on_loaded);
|
||||
this.rpc("/base_gantt/ganttview/load",
|
||||
{"model": this.model, "view_id": this.view_id}, this.on_loaded);
|
||||
},
|
||||
|
||||
on_loaded: function(data) {
|
||||
this.fields_view = data.fields_view;
|
||||
|
||||
var self = this;
|
||||
this.fields_view = data.fields_view;
|
||||
|
||||
this.name = this.fields_view.name || this.fields_view.arch.attrs.string;
|
||||
this.view_id = this.fields_view.view_id;
|
||||
|
||||
this.date_start = this.fields_view.arch.attrs.date_start;
|
||||
this.date_delay = this.fields_view.arch.attrs.date_delay;
|
||||
this.date_stop = this.fields_view.arch.attrs.date_stop;
|
||||
this.color_field = this.fields_view.arch.attrs.color;
|
||||
|
||||
this.color_field = this.fields_view.arch.attrs.color;
|
||||
this.day_length = this.fields_view.arch.attrs.day_length || 8;
|
||||
this.colors = this.fields_view.arch.attrs.colors;
|
||||
this.fields = this.fields_view.fields;
|
||||
|
||||
this.text = this.fields_view.arch.children[0].children[0].attrs.name;
|
||||
this.parent = this.fields_view.arch.children[0].attrs.link;
|
||||
|
||||
this.calendar_fields['parent'] = {'name': this.parent};
|
||||
this.calendar_fields['date_start'] = {'name': this.date_start};
|
||||
this.calendar_fields['text'] = {'name': this.text};
|
||||
if(this.date_delay)
|
||||
this.calendar_fields['date_delay'] = {'name': this.date_delay};
|
||||
if(this.date_stop)
|
||||
this.calendar_fields['date_stop'] = {'name': this.date_stop};
|
||||
this.format = "yyyy-MM-dd";
|
||||
|
||||
self.create_gantt();
|
||||
self.get_events();
|
||||
|
||||
this.calendar_fields['day_length'] = this.day_length;
|
||||
this.rpc('/base_gantt/ganttview/get_events',
|
||||
{'model': this.model,
|
||||
'fields': this.fields,
|
||||
'color_field': this.color_field,
|
||||
'day_length': this.day_length,
|
||||
'calendar_fields': this.calendar_fields,
|
||||
'colors': this.colors,
|
||||
'info_fields': this.info_fields
|
||||
},
|
||||
function(res) {
|
||||
self.create_gantt();
|
||||
self.load_event(res);
|
||||
})
|
||||
this.$element.html(QWeb.render("GanttView", {"view": this, "fields_view": this.fields_view}));
|
||||
|
||||
},
|
||||
convert_date_format: function(date) {
|
||||
date=date+"";
|
||||
if(typeof (date)!="string"||date.length===0){
|
||||
return null;
|
||||
}
|
||||
var iso=date.split("-");
|
||||
if(iso.length===0){
|
||||
return null;
|
||||
}
|
||||
var day = iso[2];
|
||||
var iso_hours = day.split(' ');
|
||||
|
||||
if (iso_hours.length > 1) {
|
||||
day = iso_hours[0];
|
||||
var iso_date_hours = iso_hours[1].split(':')
|
||||
var new_date = new Date(iso[0], iso[1] - 1, day);
|
||||
new_date.setHours(iso_date_hours[0]);
|
||||
new_date.setMinutes(iso_date_hours[1]);
|
||||
new_date.setSeconds(iso_date_hours[2]);
|
||||
}
|
||||
else {
|
||||
var new_date = new Date(iso[0], iso[1] - 1, day);
|
||||
}
|
||||
new_date.setFullYear(iso[0]);
|
||||
new_date.setMonth(iso[1]-1);
|
||||
new_date.setDate(day);
|
||||
return new_date;
|
||||
},
|
||||
|
||||
create_gantt: function() {
|
||||
ganttChartControl = new GanttChart();
|
||||
|
||||
ganttChartControl = new GanttChart(this.day_length);
|
||||
ganttChartControl.setImagePath("/base_gantt/static/lib/dhtmlxGantt/codebase/imgs/");
|
||||
ganttChartControl.setEditable(true);
|
||||
ganttChartControl.showTreePanel(true);
|
||||
ganttChartControl.showContextMenu(true);
|
||||
ganttChartControl.showDescTask(true,'d,s-f');
|
||||
ganttChartControl.showDescProject(true,'n,d');
|
||||
},
|
||||
load_event: function(res) {
|
||||
var self = this
|
||||
var result = res.result;
|
||||
var sidebar = res.sidebar;
|
||||
var project_id = new Array();
|
||||
var project = new Array();
|
||||
var j = -1;
|
||||
var self = this;
|
||||
for (i in result) {
|
||||
|
||||
var parent_id = result[i]['parent'][0];
|
||||
var parent_name = result[i]['parent'][1];
|
||||
|
||||
if (jQuery.inArray(parent_id, project_id) == -1){
|
||||
if (parent_id == undefined){
|
||||
parent_name = "";
|
||||
}
|
||||
j = j + 1;
|
||||
project[j] = new GanttProjectInfo(parent_id, parent_name, new Date(2011, 1, 1));
|
||||
project_id[j] = parent_id;
|
||||
}
|
||||
|
||||
var id = result[i]['id'];
|
||||
var text = result[i]['text'];
|
||||
var start_date = this.convert_date_format(result[i]['start_date']);
|
||||
var duration = result[i]['duration'];
|
||||
|
||||
var task = new GanttTaskInfo(id, text, start_date, duration, 100, "");
|
||||
|
||||
k = project_id.indexOf(parent_id);
|
||||
project[k].addTask(task);
|
||||
|
||||
}
|
||||
for (i in project_id){
|
||||
ganttChartControl.addProject(project[i]);
|
||||
}
|
||||
ganttChartControl.create("GanttDiv");
|
||||
ganttChartControl.attachEvent("onTaskEndResize", function(task) {self.on_task_end_resize(task);})
|
||||
ganttChartControl.attachEvent("onTaskEndDrag", function(task) {self.on_task_end_drag(task);})
|
||||
|
||||
//Create Sidebar
|
||||
if (jQuery('#cal-sidebar-option').length == 0){
|
||||
jQuery('#gantt-sidebar').append(
|
||||
jQuery('<table>',{'width':'100%','cellspacing': 0, 'cellpadding': 0, 'id':'cal-sidebar-option'})
|
||||
)
|
||||
for(s in sidebar) {
|
||||
jQuery('#cal-sidebar-option').append(
|
||||
jQuery('<tr>').append(
|
||||
jQuery('<td>').append(
|
||||
jQuery('<div>')
|
||||
.append(
|
||||
jQuery('<input>',
|
||||
{
|
||||
'type': 'checkbox',
|
||||
'id':sidebar[s][0],
|
||||
'value':sidebar[s][0]
|
||||
}).bind('click',function(){
|
||||
self.reload_gantt(self.color_field,self.model)
|
||||
}),
|
||||
sidebar[s][1]
|
||||
)
|
||||
.css('background-color',sidebar[s][sidebar[s].length-1])
|
||||
)
|
||||
)
|
||||
)
|
||||
},
|
||||
|
||||
get_events: function() {
|
||||
|
||||
var self = this;
|
||||
this.dataset.read_ids(this.dataset.ids, {}, function(result) {
|
||||
self.load_event(result);
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
load_event: function(events) {
|
||||
|
||||
var self = this;
|
||||
|
||||
var result = events;
|
||||
var project = {};
|
||||
var project_smalldate = {};
|
||||
var proj_id = [];
|
||||
var proj_id_text = [];
|
||||
|
||||
COLOR_PALETTE = ['#ccccff', '#cc99ff', '#75507b', '#3465a4', '#73d216', '#c17d11', '#edd400',
|
||||
'#fcaf3e', '#ef2929', '#ff00c9', '#ad7fa8', '#729fcf', '#8ae234', '#e9b96e', '#fce94f',
|
||||
'#ff8e00', '#ff0000', '#b0008c', '#9000ff', '#0078ff', '#00ff00', '#e6ff00', '#ffff00',
|
||||
'#905000', '#9b0000', '#840067', '#510090', '#0000c9', '#009b00', '#9abe00', '#ffc900']
|
||||
|
||||
//Smallest date of child is parent start date
|
||||
for (i in result){
|
||||
var res = result[i];
|
||||
|
||||
if (res[this.date_start] != false){
|
||||
|
||||
var parent_id = res[this.parent][0] || res[this.parent];
|
||||
var parent_name = res[this.parent][1];
|
||||
var start_date = this.convert_str_date(res[this.date_start]);
|
||||
|
||||
if (project_smalldate[parent_id] == undefined){
|
||||
project_smalldate[parent_id] = start_date;
|
||||
proj_id.push(parent_id);
|
||||
|
||||
if (parent_name != undefined){
|
||||
proj_id_text.push(res[this.parent]);
|
||||
}
|
||||
}
|
||||
else{
|
||||
if (start_date < project_smalldate[parent_id]){
|
||||
project_smalldate[parent_id] = start_date;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//get parent text
|
||||
if (parent_name == undefined){
|
||||
|
||||
var ajax = {
|
||||
url: '/base/dataset/call',
|
||||
async: false
|
||||
};
|
||||
|
||||
this.rpc(ajax, {
|
||||
model: this.dataset.model,
|
||||
method: "name_get",
|
||||
ids: proj_id,
|
||||
args: ""
|
||||
}, function(response) {
|
||||
proj_id_text = response.result;
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
//create parents
|
||||
for (i in proj_id){
|
||||
|
||||
var id = proj_id_text[i][0];
|
||||
var text = proj_id_text[i][1];
|
||||
|
||||
project[id] = new GanttProjectInfo(id, text, project_smalldate[id]);
|
||||
}
|
||||
|
||||
//create childs
|
||||
var k = 0;
|
||||
var color_box = {};
|
||||
for (i in result) {
|
||||
|
||||
var res = result[i];
|
||||
if (res[this.date_start] != false){
|
||||
|
||||
var parent_id = res[this.parent][0] || res[this.parent];
|
||||
var id = res['id'];
|
||||
var text = res[this.text];
|
||||
|
||||
var start_date = this.convert_str_date(res[this.date_start]);
|
||||
|
||||
var color = res[this.color_field][0] || res[this.color_field];
|
||||
if (color_box[color] == undefined){
|
||||
color_box[color] = COLOR_PALETTE[k];
|
||||
k = k + 1;
|
||||
}
|
||||
|
||||
if (this.date_stop != undefined){
|
||||
if (res[this.date_stop] != false){
|
||||
var stop_date = this.convert_str_date(res[this.date_stop]);
|
||||
var duration= self.hours_between(start_date, stop_date);
|
||||
}
|
||||
else{
|
||||
var duration = 0;
|
||||
}
|
||||
}
|
||||
else{
|
||||
var duration = res[this.date_delay];
|
||||
}
|
||||
if (duration == false)
|
||||
duration = 0
|
||||
task = new GanttTaskInfo(id, text, start_date, duration, 100, "", color_box[color]);
|
||||
project[parent_id].addTask(task);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//Add parent
|
||||
for (i in proj_id){
|
||||
ganttChartControl.addProject(project[proj_id[i]]);
|
||||
}
|
||||
|
||||
ganttChartControl.create("GanttDiv");
|
||||
ganttChartControl.attachEvent("onTaskStartDrag", function(task) {self.on_drag_start(task);});
|
||||
ganttChartControl.attachEvent("onTaskEndResize", function(task) {self.on_resize_drag_end(task, "resize");});
|
||||
ganttChartControl.attachEvent("onTaskEndDrag", function(task) {self.on_resize_drag_end(task, "drag");});
|
||||
ganttChartControl.attachEvent("onTaskDblClick", function(task) {self.open_popup(task);});
|
||||
|
||||
},
|
||||
reload_gantt: function(color_field, model) {
|
||||
var domain = [];
|
||||
|
||||
hours_between: function(date1, date2) {
|
||||
|
||||
var ONE_DAY = 1000 * 60 * 60 * 24;
|
||||
var date1_ms = date1.getTime();
|
||||
var date2_ms = date2.getTime();
|
||||
var difference_ms = Math.abs(date1_ms - date2_ms);
|
||||
|
||||
d = Math.round(difference_ms / ONE_DAY);
|
||||
h = Math.round((difference_ms % ONE_DAY)/(1000 * 60 * 60));
|
||||
|
||||
return (d * this.day_length) + h;
|
||||
|
||||
},
|
||||
|
||||
open_popup : function(task) {
|
||||
var event_id = task.getId();
|
||||
if (event_id) {
|
||||
event_id = parseInt(event_id, 10);
|
||||
var dataset_event_index = jQuery.inArray(event_id, this.ids);
|
||||
} else {
|
||||
var dataset_event_index = null;
|
||||
}
|
||||
this.dataset.index = dataset_event_index;
|
||||
var element_id = _.uniqueId("act_window_dialog");
|
||||
var dialog = jQuery('<div>',
|
||||
{'id': element_id
|
||||
}).dialog({
|
||||
title: 'Gantt Chart',
|
||||
modal: true,
|
||||
minWidth: 800,
|
||||
position: 'top'
|
||||
});
|
||||
var event_form = new openerp.base.FormView(this.view_manager, this.session, element_id, this.dataset, false);
|
||||
event_form.start();
|
||||
},
|
||||
|
||||
on_drag_start : function(task){
|
||||
st_date = task.getEST();
|
||||
if(st_date.getHours()){
|
||||
self.hh = st_date.getHours();
|
||||
self.mm = st_date.getMinutes();
|
||||
}
|
||||
},
|
||||
|
||||
on_resize_drag_end : function(task, evt){
|
||||
|
||||
var event_id = task.getId();
|
||||
var data = {};
|
||||
|
||||
if (evt == "drag"){
|
||||
full_date = task.getEST().set({hour: self.hh, minute : self.mm, second:0});
|
||||
data[this.date_start] = this.convert_date_str(full_date);
|
||||
}
|
||||
if (this.date_stop != undefined){
|
||||
tm = (task.getDuration() % this.day_length);
|
||||
stp = task.getFinishDate().add(tm).hour();
|
||||
data[this.date_stop] = this.convert_date_str(stp);
|
||||
}else{
|
||||
data[this.date_delay] = task.getDuration();
|
||||
}
|
||||
this.dataset.write(event_id, data, function(result) {});
|
||||
|
||||
},
|
||||
|
||||
do_show: function () {
|
||||
this.$element.show();
|
||||
},
|
||||
|
||||
do_hide: function () {
|
||||
this.$element.hide();
|
||||
},
|
||||
|
||||
convert_str_date: function (str){
|
||||
if (str.length == 19){
|
||||
this.format = "yyyy-MM-dd HH:mm:ss";
|
||||
return openerp.base.parse_datetime(str);
|
||||
} else if (str.length == 10){
|
||||
this.format = "yyyy-MM-dd";
|
||||
return openerp.base.parse_date(str);
|
||||
} else if (str.length == 8){
|
||||
this.format = "HH:mm:ss";
|
||||
return openerp.base.parse_time(str);
|
||||
}
|
||||
throw "Unrecognized date/time format";
|
||||
},
|
||||
|
||||
convert_date_str: function(full_date) {
|
||||
if (this.format == "yyyy-MM-dd HH:mm:ss"){
|
||||
return openerp.base.format_datetime(full_date);
|
||||
} else if (this.format == "yyyy-MM-dd"){
|
||||
return openerp.base.format_date(full_date);
|
||||
} else if (this.format == "HH:mm:ss"){
|
||||
return openerp.base.format_time(full_date);
|
||||
}
|
||||
throw "Unrecognized date/time format";
|
||||
},
|
||||
|
||||
reload_gantt: function(domain) {
|
||||
var self = this;
|
||||
jQuery('input[type=checkbox]:checked','#cal-sidebar-option').each(function() {
|
||||
domain.push(parseInt(jQuery(this).attr('id')))
|
||||
var ajax = {
|
||||
url: '/base/dataset/search_read',
|
||||
async: false
|
||||
};
|
||||
this.rpc(ajax, {
|
||||
model: this.dataset.model,
|
||||
domain: self.dataset.domain,
|
||||
context :self.dataset.context
|
||||
}, function(response) {
|
||||
ganttChartControl.clearAll();
|
||||
jQuery("#GanttDiv").children().remove();
|
||||
self.load_event(response);
|
||||
});
|
||||
},
|
||||
|
||||
do_search: function (domains, contexts, groupbys) {
|
||||
|
||||
var self = this;
|
||||
|
||||
return this.rpc('/base/session/eval_domain_and_context', {
|
||||
domains: domains,
|
||||
contexts: contexts,
|
||||
group_by_seq: groupbys
|
||||
}, function (results) {
|
||||
self.dataset.context = results.context;
|
||||
self.dataset.domain = results.domain;
|
||||
return self.reload_gantt(self.dataset.domain);
|
||||
});
|
||||
this.rpc('/base_gantt/ganttview/reload_gantt',{
|
||||
'domain':domain,
|
||||
'color_field':color_field,
|
||||
'model': model
|
||||
},function(res) {
|
||||
ganttChartControl.clearAll();
|
||||
jQuery("#GanttDiv").children().remove();
|
||||
self.load_event(res);
|
||||
});
|
||||
},
|
||||
reverse_convert_date_format: function(date) {
|
||||
return date.getFullYear()+"-"+(date.getMonth()+1)+"-"+date.getDate();
|
||||
},
|
||||
|
||||
on_task_end_resize : function(task) {
|
||||
this.rpc('/base_gantt/ganttview/on_event_resize',
|
||||
{'id' : task.getId(),
|
||||
'end_date' : this.reverse_convert_date_format(task.getFinishDate()),
|
||||
'duration' : task.getDuration()
|
||||
},
|
||||
function(result) {
|
||||
})
|
||||
},
|
||||
on_task_end_drag : function(task) {
|
||||
this.rpc('/base_gantt/ganttview/on_event_drag',
|
||||
{'id' : task.getId(),
|
||||
'start_date' : this.reverse_convert_date_format(task.getEST()),
|
||||
'end_date' : this.reverse_convert_date_format(task.getFinishDate()),
|
||||
'duration' : task.getDuration()
|
||||
},
|
||||
function(result) {
|
||||
})
|
||||
}
|
||||
|
||||
});
|
||||
@@ -224,5 +344,4 @@ openerp.base_gantt.GanttView = openerp.base.Controller.extend({
|
||||
// here you may tweak globals object, if any, and play with on_* or do_* callbacks on them
|
||||
|
||||
};
|
||||
|
||||
// vim:et fdc=0 fdl=0:
|
||||
|
||||
@@ -3,12 +3,9 @@
|
||||
<h3 class="title"><t t-esc="view.fields_view.arch.attrs.string"/></h3>
|
||||
<table class="gantt-view" width="100%" height="100%" cellspacing="0" cellpadding="0">
|
||||
<tr>
|
||||
<td style="width:85%">
|
||||
<td>
|
||||
<div style="width:100%;height:300px;position:relative" id="GanttDiv"/>
|
||||
</td>
|
||||
<td valign = "top">
|
||||
<div id="gantt-sidebar"/>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</t>
|
||||
|
||||
@@ -10,5 +10,6 @@
|
||||
'static/src/js/web_chat.js'
|
||||
],
|
||||
"css": [],
|
||||
# 'active': True,
|
||||
'active': False,
|
||||
}
|
||||
|
||||
@@ -307,8 +307,6 @@ Javascript
|
||||
|
||||
:returns: the dataset's current active id
|
||||
|
||||
.. js:class:: openerp.base.DataRecord(session, model, fields, values)
|
||||
|
||||
Ad-hoc objects and structural types
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
||||
@@ -1,27 +1,3 @@
|
||||
[global]
|
||||
server.environment = "development"
|
||||
# Some server parameters that you may want to tweak
|
||||
server.socket_host = "0.0.0.0"
|
||||
server.socket_port = 8080
|
||||
# Sets the number of threads the server uses
|
||||
server.thread_pool = 10
|
||||
|
||||
tools.sessions.on = True
|
||||
tools.sessions.persistent = False
|
||||
|
||||
# logging
|
||||
#log.access_file = "/var/log/openerp-web/access.log"
|
||||
#log.error_file = "/var/log/openerp-web/error.log"
|
||||
log.access_level = "INFO"
|
||||
log.error_level = "INFO"
|
||||
|
||||
# OpenERP Server
|
||||
openerp.server.host = 'localhost'
|
||||
openerp.server.port = '8070'
|
||||
openerp.server.protocol = 'socket'
|
||||
openerp.server.timeout = 450
|
||||
|
||||
# Regex for dbname %{subdomain}s for the subdomain
|
||||
#openerp.web.database.list = "%{subdomain}s_.*"
|
||||
#openerp.web.database.manager = False
|
||||
|
||||
|
||||
+29
-3
@@ -20,9 +20,6 @@
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
""" We assume that, just like the OpenERP server, the python part of the client
|
||||
doesn't need to handle timezone-aware datetimes, this could be changed in the future. """
|
||||
|
||||
import datetime
|
||||
|
||||
DEFAULT_SERVER_DATE_FORMAT = "%Y-%m-%d"
|
||||
@@ -32,31 +29,60 @@ DEFAULT_SERVER_DATETIME_FORMAT = "%s %s" % (
|
||||
DEFAULT_SERVER_TIME_FORMAT)
|
||||
|
||||
def parse_datetime(str):
|
||||
"""
|
||||
Converts a string to a datetime object using OpenERP's
|
||||
datetime string format (exemple: '2011-12-01 15:12:35').
|
||||
|
||||
No timezone information is added, the datetime is a naive instance, but
|
||||
according to OpenERP 6.1 specification the timezone is always UTC.
|
||||
"""
|
||||
if not str:
|
||||
return str
|
||||
return datetime.datetime.strptime(str, DEFAULT_SERVER_DATETIME_FORMAT)
|
||||
|
||||
def parse_date(str):
|
||||
"""
|
||||
Converts a string to a date object using OpenERP's
|
||||
date string format (exemple: '2011-12-01').
|
||||
"""
|
||||
if not str:
|
||||
return str
|
||||
return datetime.datetime.strptime(str, DEFAULT_SERVER_DATE_FORMAT).date()
|
||||
|
||||
def parse_time(str):
|
||||
"""
|
||||
Converts a string to a time object using OpenERP's
|
||||
time string format (exemple: '15:12:35').
|
||||
"""
|
||||
if not str:
|
||||
return str
|
||||
return datetime.datetime.strptime(str, DEFAULT_SERVER_TIME_FORMAT).time()
|
||||
|
||||
def format_datetime(obj):
|
||||
"""
|
||||
Converts a datetime object to a string using OpenERP's
|
||||
datetime string format (exemple: '2011-12-01 15:12:35').
|
||||
|
||||
The datetime instance should not have an attached timezone and be in UTC.
|
||||
"""
|
||||
if not obj:
|
||||
return False
|
||||
return obj.strftime(DEFAULT_SERVER_DATETIME_FORMAT)
|
||||
|
||||
def format_date(obj):
|
||||
"""
|
||||
Converts a date object to a string using OpenERP's
|
||||
date string format (exemple: '2011-12-01').
|
||||
"""
|
||||
if not obj:
|
||||
return False
|
||||
return obj.strftime(DEFAULT_SERVER_DATE_FORMAT)
|
||||
|
||||
def format_time(obj):
|
||||
"""
|
||||
Converts a time object to a string using OpenERP's
|
||||
time string format (exemple: '15:12:35').
|
||||
"""
|
||||
if not obj:
|
||||
return False
|
||||
return obj.strftime(DEFAULT_SERVER_TIME_FORMAT)
|
||||
|
||||
+28
-16
@@ -500,30 +500,42 @@ def main(argv):
|
||||
# change the timezone of the program to the OpenERP server's assumed timezone
|
||||
os.environ["TZ"] = "UTC"
|
||||
|
||||
# Parse config
|
||||
op = optparse.OptionParser()
|
||||
op.add_option("-p", "--port", dest="socket_port", help="listening port", metavar="NUMBER", default=8002)
|
||||
op.add_option("-s", "--session-path", dest="storage_path",
|
||||
help="directory used for session storage", metavar="DIR",
|
||||
default=os.path.join(tempfile.gettempdir(), "cpsessions"))
|
||||
(o, args) = op.parse_args(argv[1:])
|
||||
|
||||
# Prepare cherrypy config from options
|
||||
if not os.path.exists(o.storage_path):
|
||||
os.mkdir(o.storage_path, 0700)
|
||||
config = {
|
||||
'server.socket_port': int(o.socket_port),
|
||||
DEFAULT_CONFIG = {
|
||||
'server.socket_port': 8002,
|
||||
'server.socket_host': '0.0.0.0',
|
||||
#'server.thread_pool' = 10,
|
||||
'tools.sessions.on': True,
|
||||
'tools.sessions.storage_type': 'file',
|
||||
'tools.sessions.storage_path': o.storage_path,
|
||||
'tools.sessions.storage_path': os.path.join(tempfile.gettempdir(), "cpsessions"),
|
||||
'tools.sessions.timeout': 60
|
||||
}
|
||||
|
||||
# Parse config
|
||||
op = optparse.OptionParser()
|
||||
op.add_option("-p", "--port", dest="server.socket_port", help="listening port",
|
||||
type="int", metavar="NUMBER")
|
||||
op.add_option("-s", "--session-path", dest="tools.sessions.storage_path",
|
||||
help="directory used for session storage", metavar="DIR")
|
||||
(o, args) = op.parse_args(argv[1:])
|
||||
o = vars(o)
|
||||
for k in o.keys():
|
||||
if o[k] == None:
|
||||
del(o[k])
|
||||
|
||||
# Setup and run cherrypy
|
||||
cherrypy.tree.mount(Root())
|
||||
cherrypy.config.update(config)
|
||||
|
||||
cherrypy.config.update(config=DEFAULT_CONFIG)
|
||||
if os.path.exists(os.path.join(os.path.dirname(
|
||||
os.path.dirname(__file__)),'openerp-web.cfg')):
|
||||
cherrypy.config.update(os.path.join(os.path.dirname(
|
||||
os.path.dirname(__file__)),'openerp-web.cfg'))
|
||||
if os.path.exists(os.path.expanduser('~/.openerp_webrc')):
|
||||
cherrypy.config.update(os.path.expanduser('~/.openerp_webrc'))
|
||||
cherrypy.config.update(o)
|
||||
|
||||
if not os.path.exists(cherrypy.config['tools.sessions.storage_path']):
|
||||
os.mkdir(cherrypy.config['tools.sessions.storage_path'], 0700)
|
||||
|
||||
cherrypy.server.subscribe()
|
||||
cherrypy.engine.start()
|
||||
cherrypy.engine.block()
|
||||
|
||||
Reference in New Issue
Block a user