[FIX] Merge fix in saas1 for the problem that made the server crash when install a db
bzr revid: nicolas.vanhoren@openerp.com-20130610144915-0nj8vpk7n84ocj3f bzr revid: nicolas.vanhoren@openerp.com-20130610150657-3xddn91gm0iq0ubp bzr revid: nicolas.vanhoren@openerp.com-20130610154514-oee6s4f9g17bx63z
This commit is contained in:
commit
a94bbf8e5e
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
'name': 'Web',
|
'name': 'Web',
|
||||||
'category': 'Hidden',
|
'category': 'Hidden',
|
||||||
'version': '7.0.1.0',
|
'version': '1.0',
|
||||||
'description':
|
'description':
|
||||||
"""
|
"""
|
||||||
OpenERP Web core module.
|
OpenERP Web core module.
|
||||||
|
@ -13,6 +13,7 @@ This module provides the core of the OpenERP Web Client.
|
||||||
'auto_install': True,
|
'auto_install': True,
|
||||||
'post_load': 'wsgi_postload',
|
'post_load': 'wsgi_postload',
|
||||||
'js' : [
|
'js' : [
|
||||||
|
"static/src/fixbind.js",
|
||||||
"static/lib/datejs/globalization/en-US.js",
|
"static/lib/datejs/globalization/en-US.js",
|
||||||
"static/lib/datejs/core.js",
|
"static/lib/datejs/core.js",
|
||||||
"static/lib/datejs/parser.js",
|
"static/lib/datejs/parser.js",
|
||||||
|
@ -76,6 +77,7 @@ This module provides the core of the OpenERP Web Client.
|
||||||
"static/test/class.js",
|
"static/test/class.js",
|
||||||
"static/test/registry.js",
|
"static/test/registry.js",
|
||||||
"static/test/form.js",
|
"static/test/form.js",
|
||||||
|
"static/test/data.js",
|
||||||
"static/test/list-utils.js",
|
"static/test/list-utils.js",
|
||||||
"static/test/formats.js",
|
"static/test/formats.js",
|
||||||
"static/test/rpc.js",
|
"static/test/rpc.js",
|
||||||
|
|
|
@ -310,7 +310,7 @@ def fs2web(path):
|
||||||
"""convert FS path into web path"""
|
"""convert FS path into web path"""
|
||||||
return '/'.join(path.split(os.path.sep))
|
return '/'.join(path.split(os.path.sep))
|
||||||
|
|
||||||
def manifest_glob(extension, addons=None, db=None):
|
def manifest_glob(extension, addons=None, db=None, include_remotes=False):
|
||||||
if addons is None:
|
if addons is None:
|
||||||
addons = module_boot(db=db)
|
addons = module_boot(db=db)
|
||||||
else:
|
else:
|
||||||
|
@ -324,6 +324,10 @@ def manifest_glob(extension, addons=None, db=None):
|
||||||
addons_path = os.path.join(manifest['addons_path'], '')[:-1]
|
addons_path = os.path.join(manifest['addons_path'], '')[:-1]
|
||||||
globlist = manifest.get(extension, [])
|
globlist = manifest.get(extension, [])
|
||||||
for pattern in globlist:
|
for pattern in globlist:
|
||||||
|
if pattern.startswith(('http://', 'https://', '//')):
|
||||||
|
if include_remotes:
|
||||||
|
r.append((None, pattern))
|
||||||
|
else:
|
||||||
for path in glob.glob(os.path.normpath(os.path.join(addons_path, addon, pattern))):
|
for path in glob.glob(os.path.normpath(os.path.join(addons_path, addon, pattern))):
|
||||||
r.append((path, fs2web(path[len(addons_path):])))
|
r.append((path, fs2web(path[len(addons_path):])))
|
||||||
return r
|
return r
|
||||||
|
@ -333,14 +337,16 @@ def manifest_list(extension, mods=None, db=None):
|
||||||
mods: a comma separated string listing modules
|
mods: a comma separated string listing modules
|
||||||
db: a database name (return all installed modules in that database)
|
db: a database name (return all installed modules in that database)
|
||||||
"""
|
"""
|
||||||
|
files = manifest_glob(extension, addons=mods, db=db, include_remotes=True)
|
||||||
if not req.debug:
|
if not req.debug:
|
||||||
path = '/web/webclient/' + extension
|
path = '/web/webclient/' + extension
|
||||||
if mods is not None:
|
if mods is not None:
|
||||||
path += '?' + urllib.urlencode({'mods': mods})
|
path += '?' + urllib.urlencode({'mods': mods})
|
||||||
elif db:
|
elif db:
|
||||||
path += '?' + urllib.urlencode({'db': db})
|
path += '?' + urllib.urlencode({'db': db})
|
||||||
return [path]
|
|
||||||
files = manifest_glob(extension, addons=mods, db=db)
|
remotes = [wp for fp, wp in files if fp is None]
|
||||||
|
return [path] + remotes
|
||||||
return [wp for _fp, wp in files]
|
return [wp for _fp, wp in files]
|
||||||
|
|
||||||
def get_last_modified(files):
|
def get_last_modified(files):
|
||||||
|
@ -1204,9 +1210,11 @@ class Binary(openerpweb.Controller):
|
||||||
headers = [('Content-Type', 'image/png')]
|
headers = [('Content-Type', 'image/png')]
|
||||||
etag = req.httprequest.headers.get('If-None-Match')
|
etag = req.httprequest.headers.get('If-None-Match')
|
||||||
hashed_session = hashlib.md5(req.session_id).hexdigest()
|
hashed_session = hashlib.md5(req.session_id).hexdigest()
|
||||||
|
retag = hashed_session
|
||||||
id = None if not id else simplejson.loads(id)
|
id = None if not id else simplejson.loads(id)
|
||||||
if type(id) is list:
|
if type(id) is list:
|
||||||
id = id[0] # m2o
|
id = id[0] # m2o
|
||||||
|
try:
|
||||||
if etag:
|
if etag:
|
||||||
if not id and hashed_session == etag:
|
if not id and hashed_session == etag:
|
||||||
return werkzeug.wrappers.Response(status=304)
|
return werkzeug.wrappers.Response(status=304)
|
||||||
|
@ -1215,8 +1223,6 @@ class Binary(openerpweb.Controller):
|
||||||
if hashlib.md5(date).hexdigest() == etag:
|
if hashlib.md5(date).hexdigest() == etag:
|
||||||
return werkzeug.wrappers.Response(status=304)
|
return werkzeug.wrappers.Response(status=304)
|
||||||
|
|
||||||
retag = hashed_session
|
|
||||||
try:
|
|
||||||
if not id:
|
if not id:
|
||||||
res = Model.default_get([field], req.context).get(field)
|
res = Model.default_get([field], req.context).get(field)
|
||||||
image_base64 = res
|
image_base64 = res
|
||||||
|
|
|
@ -121,6 +121,13 @@ class WebRequest(object):
|
||||||
# we use _ as seprator where RFC2616 uses '-'
|
# we use _ as seprator where RFC2616 uses '-'
|
||||||
self.lang = lang.replace('-', '_')
|
self.lang = lang.replace('-', '_')
|
||||||
|
|
||||||
|
@contextlib.contextmanager
|
||||||
|
def registry_cr(self):
|
||||||
|
dbname = self.session._db or openerp.addons.web.controllers.main.db_monodb(self)
|
||||||
|
registry = openerp.modules.registry.RegistryManager.get(dbname.lower())
|
||||||
|
with registry.cursor() as cr:
|
||||||
|
yield (registry, cr)
|
||||||
|
|
||||||
def reject_nonliteral(dct):
|
def reject_nonliteral(dct):
|
||||||
if '__ref' in dct:
|
if '__ref' in dct:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
|
@ -298,6 +305,8 @@ class HttpRequest(WebRequest):
|
||||||
#_logger.debug("%s --> %s.%s %r", self.httprequest.method, method.im_class.__name__, method.__name__, akw)
|
#_logger.debug("%s --> %s.%s %r", self.httprequest.method, method.im_class.__name__, method.__name__, akw)
|
||||||
try:
|
try:
|
||||||
r = method(**self.params)
|
r = method(**self.params)
|
||||||
|
except werkzeug.exceptions.HTTPException, e:
|
||||||
|
r = e
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
_logger.exception("An exception occured during an http request")
|
_logger.exception("An exception occured during an http request")
|
||||||
se = serialize_exception(e)
|
se = serialize_exception(e)
|
||||||
|
@ -307,7 +316,9 @@ class HttpRequest(WebRequest):
|
||||||
'data': se
|
'data': se
|
||||||
}
|
}
|
||||||
r = werkzeug.exceptions.InternalServerError(cgi.escape(simplejson.dumps(error)))
|
r = werkzeug.exceptions.InternalServerError(cgi.escape(simplejson.dumps(error)))
|
||||||
if self.debug or 1:
|
else:
|
||||||
|
if not r:
|
||||||
|
r = werkzeug.wrappers.Response(status=204) # no content
|
||||||
if isinstance(r, (werkzeug.wrappers.BaseResponse, werkzeug.exceptions.HTTPException)):
|
if isinstance(r, (werkzeug.wrappers.BaseResponse, werkzeug.exceptions.HTTPException)):
|
||||||
_logger.debug('<-- %s', r)
|
_logger.debug('<-- %s', r)
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -913,9 +913,7 @@ instance.web.Menu = instance.web.Widget.extend({
|
||||||
self.reflow();
|
self.reflow();
|
||||||
// launch the fetch of needaction counters, asynchronous
|
// launch the fetch of needaction counters, asynchronous
|
||||||
if (!_.isEmpty(menu_data.all_menu_ids)) {
|
if (!_.isEmpty(menu_data.all_menu_ids)) {
|
||||||
this.rpc("/web/menu/load_needaction", {menu_ids: menu_data.all_menu_ids}).done(function(r) {
|
this.do_load_needaction(menu_data.all_menu_ids);
|
||||||
self.on_needaction_loaded(r);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
var lazyreflow = _.debounce(this.reflow.bind(this), 200);
|
var lazyreflow = _.debounce(this.reflow.bind(this), 200);
|
||||||
|
@ -941,7 +939,7 @@ instance.web.Menu = instance.web.Widget.extend({
|
||||||
this.data = {data: data};
|
this.data = {data: data};
|
||||||
this.renderElement();
|
this.renderElement();
|
||||||
this.$secondary_menus.html(QWeb.render("Menu.secondary", { widget : this }));
|
this.$secondary_menus.html(QWeb.render("Menu.secondary", { widget : this }));
|
||||||
this.$el.on('click', 'a[data-menu]', this.on_menu_click);
|
this.$el.on('click', 'a[data-menu]', this.on_top_menu_click);
|
||||||
// Hide second level submenus
|
// Hide second level submenus
|
||||||
this.$secondary_menus.find('.oe_menu_toggler').siblings('.oe_secondary_submenu').hide();
|
this.$secondary_menus.find('.oe_menu_toggler').siblings('.oe_secondary_submenu').hide();
|
||||||
if (self.current_menu) {
|
if (self.current_menu) {
|
||||||
|
@ -950,6 +948,16 @@ instance.web.Menu = instance.web.Widget.extend({
|
||||||
this.trigger('menu_loaded', data);
|
this.trigger('menu_loaded', data);
|
||||||
this.has_been_loaded.resolve();
|
this.has_been_loaded.resolve();
|
||||||
},
|
},
|
||||||
|
do_load_needaction: function (menu_ids) {
|
||||||
|
var self = this;
|
||||||
|
menu_ids = _.reject(menu_ids, _.isEmpty);
|
||||||
|
if (_.isEmpty(menu_ids)) {
|
||||||
|
return $.when();
|
||||||
|
}
|
||||||
|
return this.rpc("/web/menu/load_needaction", {'menu_ids': menu_ids}).done(function(r) {
|
||||||
|
self.on_needaction_loaded(r);
|
||||||
|
});
|
||||||
|
},
|
||||||
on_needaction_loaded: function(data) {
|
on_needaction_loaded: function(data) {
|
||||||
var self = this;
|
var self = this;
|
||||||
this.needaction_data = data;
|
this.needaction_data = data;
|
||||||
|
@ -1081,11 +1089,38 @@ instance.web.Menu = instance.web.Widget.extend({
|
||||||
}
|
}
|
||||||
this.open_menu(id);
|
this.open_menu(id);
|
||||||
},
|
},
|
||||||
|
do_reload_needaction: function () {
|
||||||
|
var self = this;
|
||||||
|
if (self.current_menu) {
|
||||||
|
self.do_load_needaction([self.current_menu]).then(function () {
|
||||||
|
self.trigger("need_action_reloaded");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
/**
|
/**
|
||||||
* Jquery event handler for menu click
|
* Jquery event handler for menu click
|
||||||
*
|
*
|
||||||
* @param {Event} ev the jquery event
|
* @param {Event} ev the jquery event
|
||||||
*/
|
*/
|
||||||
|
on_top_menu_click: function(ev) {
|
||||||
|
var self = this;
|
||||||
|
var id = $(ev.currentTarget).data('menu');
|
||||||
|
var menu_ids = [id];
|
||||||
|
var menu = _.filter(this.data.data.children, function (menu) {return menu.id == id;})[0];
|
||||||
|
function add_menu_ids (menu) {
|
||||||
|
if (menu.children) {
|
||||||
|
_.each(menu.children, function (menu) {
|
||||||
|
menu_ids.push(menu.id);
|
||||||
|
add_menu_ids(menu);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
add_menu_ids(menu);
|
||||||
|
self.do_load_needaction(menu_ids).then(function () {
|
||||||
|
self.trigger("need_action_reloaded");
|
||||||
|
});
|
||||||
|
this.on_menu_click(ev);
|
||||||
|
},
|
||||||
on_menu_click: function(ev) {
|
on_menu_click: function(ev) {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
var needaction = $(ev.target).is('div.oe_menu_counter');
|
var needaction = $(ev.target).is('div.oe_menu_counter');
|
||||||
|
|
|
@ -1144,7 +1144,8 @@ instance.web.JsonRPC = instance.web.Class.extend(instance.web.PropertiesMixin, {
|
||||||
}
|
}
|
||||||
qs = '?' + $.param(params);
|
qs = '?' + $.param(params);
|
||||||
}
|
}
|
||||||
return this.prefix + path + qs;
|
var prefix = _.any(['http://', 'https://', '//'], _.bind(_.str.startsWith, null, path)) ? '' : this.prefix;
|
||||||
|
return prefix + path + qs;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -91,6 +91,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
|
||||||
init: function(parent, dataset, view_id, options) {
|
init: function(parent, dataset, view_id, options) {
|
||||||
var self = this;
|
var self = this;
|
||||||
this._super(parent);
|
this._super(parent);
|
||||||
|
this.ViewManager = parent;
|
||||||
this.set_default_options(options);
|
this.set_default_options(options);
|
||||||
this.dataset = dataset;
|
this.dataset = dataset;
|
||||||
this.model = dataset.model;
|
this.model = dataset.model;
|
||||||
|
@ -720,6 +721,8 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
|
||||||
return this.save().done(function(result) {
|
return this.save().done(function(result) {
|
||||||
self.trigger("save", result);
|
self.trigger("save", result);
|
||||||
self.to_view_mode();
|
self.to_view_mode();
|
||||||
|
}).then(function(result) {
|
||||||
|
self.ViewManager.ActionManager.__parentedParent.menu.do_reload_needaction();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
on_button_cancel: function(event) {
|
on_button_cancel: function(event) {
|
||||||
|
@ -2901,7 +2904,7 @@ instance.web.form.FieldRadio = instance.web.form.AbstractField.extend(instance.w
|
||||||
var domain = instance.web.pyeval.eval('domain', this.build_domain()) || [];
|
var domain = instance.web.pyeval.eval('domain', this.build_domain()) || [];
|
||||||
if (! _.isEqual(self.domain, domain)) {
|
if (! _.isEqual(self.domain, domain)) {
|
||||||
self.domain = domain;
|
self.domain = domain;
|
||||||
var ds = new instance.web.DataSet(self, self.field.relation);
|
var ds = new instance.web.DataSetStatic(self, self.field.relation, self.build_context());
|
||||||
ds.call('search', [self.domain])
|
ds.call('search', [self.domain])
|
||||||
.then(function (records) {
|
.then(function (records) {
|
||||||
ds.name_get(records).then(function (records) {
|
ds.name_get(records).then(function (records) {
|
||||||
|
@ -3151,6 +3154,7 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc
|
||||||
instance.web.form.CompletionFieldMixin.init.call(this);
|
instance.web.form.CompletionFieldMixin.init.call(this);
|
||||||
this.set({'value': false});
|
this.set({'value': false});
|
||||||
this.display_value = {};
|
this.display_value = {};
|
||||||
|
this.display_value_backup = {};
|
||||||
this.last_search = [];
|
this.last_search = [];
|
||||||
this.floating = false;
|
this.floating = false;
|
||||||
this.current_display = null;
|
this.current_display = null;
|
||||||
|
@ -3234,6 +3238,7 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc
|
||||||
);
|
);
|
||||||
pop.on('write_completed', self, function(){
|
pop.on('write_completed', self, function(){
|
||||||
self.display_value = {};
|
self.display_value = {};
|
||||||
|
self.display_value_backup = {};
|
||||||
self.render_value();
|
self.render_value();
|
||||||
self.focus();
|
self.focus();
|
||||||
});
|
});
|
||||||
|
@ -3284,6 +3289,7 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc
|
||||||
if (self.last_search.length > 0) {
|
if (self.last_search.length > 0) {
|
||||||
if (self.last_search[0][0] != self.get("value")) {
|
if (self.last_search[0][0] != self.get("value")) {
|
||||||
self.display_value = {};
|
self.display_value = {};
|
||||||
|
self.display_value_backup = {};
|
||||||
self.display_value["" + self.last_search[0][0]] = self.last_search[0][1];
|
self.display_value["" + self.last_search[0][0]] = self.last_search[0][1];
|
||||||
self.reinit_value(self.last_search[0][0]);
|
self.reinit_value(self.last_search[0][0]);
|
||||||
} else {
|
} else {
|
||||||
|
@ -3349,6 +3355,7 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc
|
||||||
var item = ui.item;
|
var item = ui.item;
|
||||||
if (item.id) {
|
if (item.id) {
|
||||||
self.display_value = {};
|
self.display_value = {};
|
||||||
|
self.display_value_backup = {};
|
||||||
self.display_value["" + item.id] = item.name;
|
self.display_value["" + item.id] = item.name;
|
||||||
self.reinit_value(item.id);
|
self.reinit_value(item.id);
|
||||||
} else if (item.action) {
|
} else if (item.action) {
|
||||||
|
@ -3394,6 +3401,11 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc
|
||||||
this.alive(dataset.name_get([self.get("value")])).done(function(data) {
|
this.alive(dataset.name_get([self.get("value")])).done(function(data) {
|
||||||
self.display_value["" + self.get("value")] = data[0][1];
|
self.display_value["" + self.get("value")] = data[0][1];
|
||||||
self.render_value(true);
|
self.render_value(true);
|
||||||
|
}).fail( function (data, event) {
|
||||||
|
// avoid displaying crash errors as many2One should be name_get compliant
|
||||||
|
event.preventDefault();
|
||||||
|
self.display_value["" + self.get("value")] = self.display_value_backup["" + self.get("value")];
|
||||||
|
self.render_value(true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -3437,9 +3449,13 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc
|
||||||
var self = this;
|
var self = this;
|
||||||
if (value_ instanceof Array) {
|
if (value_ instanceof Array) {
|
||||||
this.display_value = {};
|
this.display_value = {};
|
||||||
|
this.display_value_backup = {}
|
||||||
if (! this.options.always_reload) {
|
if (! this.options.always_reload) {
|
||||||
this.display_value["" + value_[0]] = value_[1];
|
this.display_value["" + value_[0]] = value_[1];
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
this.display_value_backup["" + value_[0]] = value_[1];
|
||||||
|
}
|
||||||
value_ = value_[0];
|
value_ = value_[0];
|
||||||
}
|
}
|
||||||
value_ = value_ || false;
|
value_ = value_ || false;
|
||||||
|
@ -3450,6 +3466,7 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc
|
||||||
},
|
},
|
||||||
add_id: function(id) {
|
add_id: function(id) {
|
||||||
this.display_value = {};
|
this.display_value = {};
|
||||||
|
this.display_value_backup = {};
|
||||||
this.reinit_value(id);
|
this.reinit_value(id);
|
||||||
},
|
},
|
||||||
is_false: function() {
|
is_false: function() {
|
||||||
|
|
|
@ -522,6 +522,7 @@ instance.web.ViewManager = instance.web.Widget.extend({
|
||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
this.ActionManager = parent;
|
||||||
this.views = {};
|
this.views = {};
|
||||||
this.flags = flags || {};
|
this.flags = flags || {};
|
||||||
this.registry = instance.web.views;
|
this.registry = instance.web.views;
|
||||||
|
@ -1253,6 +1254,7 @@ instance.web.View = instance.web.Widget.extend({
|
||||||
view_type: undefined,
|
view_type: undefined,
|
||||||
init: function(parent, dataset, view_id, options) {
|
init: function(parent, dataset, view_id, options) {
|
||||||
this._super(parent);
|
this._super(parent);
|
||||||
|
this.ViewManager = parent;
|
||||||
this.dataset = dataset;
|
this.dataset = dataset;
|
||||||
this.view_id = view_id;
|
this.view_id = view_id;
|
||||||
this.set_default_options(options);
|
this.set_default_options(options);
|
||||||
|
@ -1324,7 +1326,6 @@ instance.web.View = instance.web.Widget.extend({
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
var context = new instance.web.CompoundContext(dataset.get_context(), action_data.context || {});
|
var context = new instance.web.CompoundContext(dataset.get_context(), action_data.context || {});
|
||||||
|
|
||||||
var handler = function (action) {
|
var handler = function (action) {
|
||||||
if (action && action.constructor == Object) {
|
if (action && action.constructor == Object) {
|
||||||
var ncontext = new instance.web.CompoundContext(context);
|
var ncontext = new instance.web.CompoundContext(context);
|
||||||
|
@ -1361,7 +1362,11 @@ instance.web.View = instance.web.Widget.extend({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
args.push(context);
|
args.push(context);
|
||||||
return dataset.call_button(action_data.name, args).then(handler);
|
return dataset.call_button(action_data.name, args).then(handler).then(function () {
|
||||||
|
if (self.ViewManager.ActionManager) {
|
||||||
|
self.ViewManager.ActionManager.__parentedParent.menu.do_reload_needaction();
|
||||||
|
}
|
||||||
|
});
|
||||||
} else if (action_data.type=="action") {
|
} else if (action_data.type=="action") {
|
||||||
return this.rpc('/web/action/load', {
|
return this.rpc('/web/action/load', {
|
||||||
action_id: action_data.name,
|
action_id: action_data.name,
|
||||||
|
|
|
@ -265,10 +265,13 @@ instance.web_kanban.KanbanView = instance.web.View.extend({
|
||||||
var remaining = groups.length - 1,
|
var remaining = groups.length - 1,
|
||||||
groups_array = [];
|
groups_array = [];
|
||||||
return $.when.apply(null, _.map(groups, function (group, index) {
|
return $.when.apply(null, _.map(groups, function (group, index) {
|
||||||
|
var def = $.when([]);
|
||||||
var dataset = new instance.web.DataSetSearch(self, self.dataset.model,
|
var dataset = new instance.web.DataSetSearch(self, self.dataset.model,
|
||||||
new instance.web.CompoundContext(self.dataset.get_context(), group.model.context()), group.model.domain());
|
new instance.web.CompoundContext(self.dataset.get_context(), group.model.context()), group.model.domain());
|
||||||
return dataset.read_slice(self.fields_keys.concat(['__last_update']), { 'limit': self.limit })
|
if (group.attributes.length >= 1) {
|
||||||
.then(function (records) {
|
def = dataset.read_slice(self.fields_keys.concat(['__last_update']), { 'limit': self.limit });
|
||||||
|
}
|
||||||
|
return def.then(function(records) {
|
||||||
self.nb_records += records.length;
|
self.nb_records += records.length;
|
||||||
self.dataset.ids.push.apply(self.dataset.ids, dataset.ids);
|
self.dataset.ids.push.apply(self.dataset.ids, dataset.ids);
|
||||||
groups_array[index] = new instance.web_kanban.KanbanGroup(self, records, group, dataset);
|
groups_array[index] = new instance.web_kanban.KanbanGroup(self, records, group, dataset);
|
||||||
|
|
|
@ -40,7 +40,7 @@ License: GPL-2+
|
||||||
.
|
.
|
||||||
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU General Public License
|
||||||
along with this program; if not, write to the Free Software
|
along with this program; if not, write to the Free Software
|
||||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
.
|
.
|
||||||
On Debian systems, the complete text of the GNU General Public License
|
On Debian systems, the complete text of the GNU General Public License
|
||||||
can be found in /usr/share/common-licenses/GPL-2 file.
|
can be found in /usr/share/common-licenses/GPL-2 file.
|
||||||
|
@ -137,7 +137,7 @@ License: GPL-2+
|
||||||
.
|
.
|
||||||
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU General Public License
|
||||||
along with this program; if not, write to the Free Software
|
along with this program; if not, write to the Free Software
|
||||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
.
|
.
|
||||||
On Debian systems, the complete text of the GNU General Public License
|
On Debian systems, the complete text of the GNU General Public License
|
||||||
can be found in /usr/share/common-licenses/GPL-2 file.
|
can be found in /usr/share/common-licenses/GPL-2 file.
|
||||||
|
@ -159,7 +159,7 @@ License: GPL-2+
|
||||||
.
|
.
|
||||||
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU General Public License
|
||||||
along with this program; if not, write to the Free Software
|
along with this program; if not, write to the Free Software
|
||||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
.
|
.
|
||||||
On Debian systems, the complete text of the GNU General Public License
|
On Debian systems, the complete text of the GNU General Public License
|
||||||
can be found in /usr/share/common-licenses/GPL-2 file.
|
can be found in /usr/share/common-licenses/GPL-2 file.
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
##############################################################################
|
##############################################################################
|
||||||
#
|
#
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program; if not, write to the Free Software
|
# along with this program; if not, write to the Free Software
|
||||||
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
#
|
#
|
||||||
##############################################################################
|
##############################################################################
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
#!/usr/bin/python
|
|
||||||
# WSGI Handler sample configuration file.
|
# WSGI Handler sample configuration file.
|
||||||
#
|
#
|
||||||
# Change the appropriate settings below, in order to provide the parameters
|
# Change the appropriate settings below, in order to provide the parameters
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -198,7 +198,7 @@ class ir_attachment(osv.osv):
|
||||||
continue
|
continue
|
||||||
res_ids.setdefault(rmod,set()).add(rid)
|
res_ids.setdefault(rmod,set()).add(rid)
|
||||||
if values:
|
if values:
|
||||||
if 'res_model' in values and 'res_id' in values:
|
if values.get('res_model') and 'res_id' in values:
|
||||||
res_ids.setdefault(values['res_model'],set()).add(values['res_id'])
|
res_ids.setdefault(values['res_model'],set()).add(values['res_id'])
|
||||||
|
|
||||||
ima = self.pool.get('ir.model.access')
|
ima = self.pool.get('ir.model.access')
|
||||||
|
|
|
@ -31,6 +31,7 @@ import re
|
||||||
import smtplib
|
import smtplib
|
||||||
import threading
|
import threading
|
||||||
|
|
||||||
|
from openerp import SUPERUSER_ID
|
||||||
from openerp.osv import osv, fields
|
from openerp.osv import osv, fields
|
||||||
from openerp.tools.translate import _
|
from openerp.tools.translate import _
|
||||||
from openerp.tools import html2text
|
from openerp.tools import html2text
|
||||||
|
@ -419,11 +420,11 @@ class ir_mail_server(osv.osv):
|
||||||
# Get SMTP Server Details from Mail Server
|
# Get SMTP Server Details from Mail Server
|
||||||
mail_server = None
|
mail_server = None
|
||||||
if mail_server_id:
|
if mail_server_id:
|
||||||
mail_server = self.browse(cr, uid, mail_server_id)
|
mail_server = self.browse(cr, SUPERUSER_ID, mail_server_id)
|
||||||
elif not smtp_server:
|
elif not smtp_server:
|
||||||
mail_server_ids = self.search(cr, uid, [], order='sequence', limit=1)
|
mail_server_ids = self.search(cr, SUPERUSER_ID, [], order='sequence', limit=1)
|
||||||
if mail_server_ids:
|
if mail_server_ids:
|
||||||
mail_server = self.browse(cr, uid, mail_server_ids[0])
|
mail_server = self.browse(cr, SUPERUSER_ID, mail_server_ids[0])
|
||||||
|
|
||||||
if mail_server:
|
if mail_server:
|
||||||
smtp_server = mail_server.smtp_host
|
smtp_server = mail_server.smtp_host
|
||||||
|
|
|
@ -54,6 +54,34 @@ class ir_sequence(openerp.osv.osv.osv):
|
||||||
"""
|
"""
|
||||||
_name = 'ir.sequence'
|
_name = 'ir.sequence'
|
||||||
_order = 'name'
|
_order = 'name'
|
||||||
|
|
||||||
|
def _get_number_next_actual(self, cr, user, ids, field_name, arg, context=None):
|
||||||
|
'''Return number from ir_sequence row when no_gap implementation,
|
||||||
|
and number from postgres sequence when standard implementation.'''
|
||||||
|
res = dict.fromkeys(ids)
|
||||||
|
for element in self.browse(cr, user, ids, context=context):
|
||||||
|
if element.implementation != 'standard':
|
||||||
|
res[element.id] = element.number_next
|
||||||
|
else:
|
||||||
|
# get number from postgres sequence. Cannot use
|
||||||
|
# currval, because that might give an error when
|
||||||
|
# not having used nextval before.
|
||||||
|
statement = (
|
||||||
|
"SELECT last_value, increment_by, is_called"
|
||||||
|
" FROM ir_sequence_%03d"
|
||||||
|
% element.id)
|
||||||
|
cr.execute(statement)
|
||||||
|
(last_value, increment_by, is_called) = cr.fetchone()
|
||||||
|
if is_called:
|
||||||
|
res[element.id] = last_value + increment_by
|
||||||
|
else:
|
||||||
|
res[element.id] = last_value
|
||||||
|
return res
|
||||||
|
|
||||||
|
def _set_number_next_actual(self, cr, uid, id, name, value, args=None, context=None):
|
||||||
|
return self.write(cr, uid, id, {'number_next': value or 0}, context=context)
|
||||||
|
|
||||||
|
|
||||||
_columns = {
|
_columns = {
|
||||||
'name': openerp.osv.fields.char('Name', size=64, required=True),
|
'name': openerp.osv.fields.char('Name', size=64, required=True),
|
||||||
'code': openerp.osv.fields.selection(_code_get, 'Code', size=64),
|
'code': openerp.osv.fields.selection(_code_get, 'Code', size=64),
|
||||||
|
@ -67,6 +95,7 @@ class ir_sequence(openerp.osv.osv.osv):
|
||||||
'prefix': openerp.osv.fields.char('Prefix', size=64, help="Prefix value of the record for the sequence"),
|
'prefix': openerp.osv.fields.char('Prefix', size=64, help="Prefix value of the record for the sequence"),
|
||||||
'suffix': openerp.osv.fields.char('Suffix', size=64, help="Suffix value of the record for the sequence"),
|
'suffix': openerp.osv.fields.char('Suffix', size=64, help="Suffix value of the record for the sequence"),
|
||||||
'number_next': openerp.osv.fields.integer('Next Number', required=True, help="Next number of this sequence"),
|
'number_next': openerp.osv.fields.integer('Next Number', required=True, help="Next number of this sequence"),
|
||||||
|
'number_next_actual': openerp.osv.fields.function(_get_number_next_actual, fnct_inv=_set_number_next_actual, type='integer', required=True, string='Next Number', help='Next number that will be used. This number can be incremented frequently so the displayed value might already be obsolete'),
|
||||||
'number_increment': openerp.osv.fields.integer('Increment Number', required=True, help="The next number of the sequence will be incremented by this number"),
|
'number_increment': openerp.osv.fields.integer('Increment Number', required=True, help="The next number of the sequence will be incremented by this number"),
|
||||||
'padding' : openerp.osv.fields.integer('Number Padding', required=True, help="OpenERP will automatically adds some '0' on the left of the 'Next Number' to get the required padding size."),
|
'padding' : openerp.osv.fields.integer('Number Padding', required=True, help="OpenERP will automatically adds some '0' on the left of the 'Next Number' to get the required padding size."),
|
||||||
'company_id': openerp.osv.fields.many2one('res.company', 'Company'),
|
'company_id': openerp.osv.fields.many2one('res.company', 'Company'),
|
||||||
|
@ -77,6 +106,7 @@ class ir_sequence(openerp.osv.osv.osv):
|
||||||
'company_id': lambda s,cr,uid,c: s.pool.get('res.company')._company_default_get(cr, uid, 'ir.sequence', context=c),
|
'company_id': lambda s,cr,uid,c: s.pool.get('res.company')._company_default_get(cr, uid, 'ir.sequence', context=c),
|
||||||
'number_increment': 1,
|
'number_increment': 1,
|
||||||
'number_next': 1,
|
'number_next': 1,
|
||||||
|
'number_next_actual': 1,
|
||||||
'padding' : 0,
|
'padding' : 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,7 +151,7 @@ class ir_sequence(openerp.osv.osv.osv):
|
||||||
# object depends on it.
|
# object depends on it.
|
||||||
cr.execute("DROP SEQUENCE IF EXISTS %s RESTRICT " % names)
|
cr.execute("DROP SEQUENCE IF EXISTS %s RESTRICT " % names)
|
||||||
|
|
||||||
def _alter_sequence(self, cr, id, number_increment, number_next):
|
def _alter_sequence(self, cr, id, number_increment, number_next=None):
|
||||||
""" Alter a PostreSQL sequence.
|
""" Alter a PostreSQL sequence.
|
||||||
|
|
||||||
There is no access rights check.
|
There is no access rights check.
|
||||||
|
@ -129,9 +159,15 @@ class ir_sequence(openerp.osv.osv.osv):
|
||||||
if number_increment == 0:
|
if number_increment == 0:
|
||||||
raise osv.except_osv(_('Warning!'),_("Increment number must not be zero."))
|
raise osv.except_osv(_('Warning!'),_("Increment number must not be zero."))
|
||||||
assert isinstance(id, (int, long))
|
assert isinstance(id, (int, long))
|
||||||
cr.execute("""
|
seq_name = 'ir_sequence_%03d' % (id,)
|
||||||
ALTER SEQUENCE ir_sequence_%03d INCREMENT BY %%s RESTART WITH %%s
|
cr.execute("SELECT relname FROM pg_class WHERE relkind = %s AND relname=%s", ('S', seq_name))
|
||||||
""" % id, (number_increment, number_next))
|
if not cr.fetchone():
|
||||||
|
# sequence is not created yet, we're inside create() so ignore it, will be set later
|
||||||
|
return
|
||||||
|
statement = "ALTER SEQUENCE %s INCREMENT BY %d" % (seq_name, number_increment)
|
||||||
|
if number_next is not None:
|
||||||
|
statement += " RESTART WITH %d" % (number_next, )
|
||||||
|
cr.execute(statement)
|
||||||
|
|
||||||
def create(self, cr, uid, values, context=None):
|
def create(self, cr, uid, values, context=None):
|
||||||
""" Create a sequence, in implementation == standard a fast gaps-allowed PostgreSQL sequence is used.
|
""" Create a sequence, in implementation == standard a fast gaps-allowed PostgreSQL sequence is used.
|
||||||
|
@ -160,7 +196,13 @@ class ir_sequence(openerp.osv.osv.osv):
|
||||||
n = values.get('number_next', row['number_next'])
|
n = values.get('number_next', row['number_next'])
|
||||||
if row['implementation'] == 'standard':
|
if row['implementation'] == 'standard':
|
||||||
if new_implementation in ('standard', None):
|
if new_implementation in ('standard', None):
|
||||||
|
# Implementation has NOT changed.
|
||||||
|
# Only change sequence if really requested.
|
||||||
|
if row['number_next'] != n:
|
||||||
self._alter_sequence(cr, row['id'], i, n)
|
self._alter_sequence(cr, row['id'], i, n)
|
||||||
|
else:
|
||||||
|
# Just in case only increment changed
|
||||||
|
self._alter_sequence(cr, row['id'], i)
|
||||||
else:
|
else:
|
||||||
self._drop_sequence(cr, row['id'])
|
self._drop_sequence(cr, row['id'])
|
||||||
else:
|
else:
|
||||||
|
@ -200,7 +242,7 @@ class ir_sequence(openerp.osv.osv.osv):
|
||||||
force_company = context.get('force_company')
|
force_company = context.get('force_company')
|
||||||
if not force_company:
|
if not force_company:
|
||||||
force_company = self.pool.get('res.users').browse(cr, uid, uid).company_id.id
|
force_company = self.pool.get('res.users').browse(cr, uid, uid).company_id.id
|
||||||
sequences = self.read(cr, uid, seq_ids, ['company_id','implementation','number_next','prefix','suffix','padding'])
|
sequences = self.read(cr, uid, seq_ids, ['name','company_id','implementation','number_next','prefix','suffix','padding'])
|
||||||
preferred_sequences = [s for s in sequences if s['company_id'] and s['company_id'][0] == force_company ]
|
preferred_sequences = [s for s in sequences if s['company_id'] and s['company_id'][0] == force_company ]
|
||||||
seq = preferred_sequences[0] if preferred_sequences else sequences[0]
|
seq = preferred_sequences[0] if preferred_sequences else sequences[0]
|
||||||
if seq['implementation'] == 'standard':
|
if seq['implementation'] == 'standard':
|
||||||
|
@ -210,8 +252,11 @@ class ir_sequence(openerp.osv.osv.osv):
|
||||||
cr.execute("SELECT number_next FROM ir_sequence WHERE id=%s FOR UPDATE NOWAIT", (seq['id'],))
|
cr.execute("SELECT number_next FROM ir_sequence WHERE id=%s FOR UPDATE NOWAIT", (seq['id'],))
|
||||||
cr.execute("UPDATE ir_sequence SET number_next=number_next+number_increment WHERE id=%s ", (seq['id'],))
|
cr.execute("UPDATE ir_sequence SET number_next=number_next+number_increment WHERE id=%s ", (seq['id'],))
|
||||||
d = self._interpolation_dict()
|
d = self._interpolation_dict()
|
||||||
|
try:
|
||||||
interpolated_prefix = self._interpolate(seq['prefix'], d)
|
interpolated_prefix = self._interpolate(seq['prefix'], d)
|
||||||
interpolated_suffix = self._interpolate(seq['suffix'], d)
|
interpolated_suffix = self._interpolate(seq['suffix'], d)
|
||||||
|
except ValueError:
|
||||||
|
raise osv.except_osv(_('Warning'), _('Invalid prefix or suffix for sequence \'%s\'') % (seq.get('name')))
|
||||||
return interpolated_prefix + '%%0%sd' % seq['padding'] % seq['number_next'] + interpolated_suffix
|
return interpolated_prefix + '%%0%sd' % seq['padding'] % seq['number_next'] + interpolated_suffix
|
||||||
|
|
||||||
def next_by_id(self, cr, uid, sequence_id, context=None):
|
def next_by_id(self, cr, uid, sequence_id, context=None):
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
<field name="suffix"/>
|
<field name="suffix"/>
|
||||||
<field name="padding"/>
|
<field name="padding"/>
|
||||||
<field name="number_increment"/>
|
<field name="number_increment"/>
|
||||||
<field name="number_next"/>
|
<field name="number_next_actual"/>
|
||||||
<field name="implementation"/>
|
<field name="implementation"/>
|
||||||
</group>
|
</group>
|
||||||
<group col="3" string="Legend (for prefix, suffix)">
|
<group col="3" string="Legend (for prefix, suffix)">
|
||||||
|
@ -57,7 +57,7 @@
|
||||||
<field name="prefix"/>
|
<field name="prefix"/>
|
||||||
<field name="padding"/>
|
<field name="padding"/>
|
||||||
<field name="company_id" groups="base.group_multi_company"/>
|
<field name="company_id" groups="base.group_multi_company"/>
|
||||||
<field name="number_next"/>
|
<field name="number_next_actual"/>
|
||||||
<field name="number_increment"/>
|
<field name="number_increment"/>
|
||||||
<field name="implementation"/>
|
<field name="implementation"/>
|
||||||
</tree>
|
</tree>
|
||||||
|
|
|
@ -278,6 +278,10 @@ class res_company(osv.osv):
|
||||||
<header>
|
<header>
|
||||||
<pageTemplate>
|
<pageTemplate>
|
||||||
<frame id="first" x1="28.0" y1="28.0" width="%s" height="%s"/>
|
<frame id="first" x1="28.0" y1="28.0" width="%s" height="%s"/>
|
||||||
|
<stylesheet>
|
||||||
|
<!-- Set here the default font to use for all <para> tags -->
|
||||||
|
<parastyle name='Normal' fontName="DejaVu Sans"/>
|
||||||
|
</stylesheet>
|
||||||
<pageGraphics>
|
<pageGraphics>
|
||||||
<fill color="black"/>
|
<fill color="black"/>
|
||||||
<stroke color="black"/>
|
<stroke color="black"/>
|
||||||
|
@ -287,6 +291,9 @@ class res_company(osv.osv):
|
||||||
<drawCentredString x="%s" y="%s">[[ company.partner_id.name ]]</drawCentredString>
|
<drawCentredString x="%s" y="%s">[[ company.partner_id.name ]]</drawCentredString>
|
||||||
<stroke color="#000000"/>
|
<stroke color="#000000"/>
|
||||||
<lines>%s</lines>
|
<lines>%s</lines>
|
||||||
|
<!-- Set here the default font to use for all <drawString> tags -->
|
||||||
|
<!-- don't forget to change the 2 other occurence of <setFont> above if needed -->
|
||||||
|
<setFont name="DejaVu Sans" size="8"/>
|
||||||
</pageGraphics>
|
</pageGraphics>
|
||||||
</pageTemplate>
|
</pageTemplate>
|
||||||
</header>"""
|
</header>"""
|
||||||
|
@ -310,13 +317,16 @@ class res_company(osv.osv):
|
||||||
<pageTemplate>
|
<pageTemplate>
|
||||||
<frame id="first" x1="1.3cm" y1="3.0cm" height="%s" width="19.0cm"/>
|
<frame id="first" x1="1.3cm" y1="3.0cm" height="%s" width="19.0cm"/>
|
||||||
<stylesheet>
|
<stylesheet>
|
||||||
<paraStyle name="main_footer" fontName="DejaVu Sans" fontSize="8.0" alignment="CENTER"/>
|
<!-- Set here the default font to use for all <para> tags -->
|
||||||
<paraStyle name="main_header" fontName="DejaVu Sans" fontSize="8.0" leading="10" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/>
|
<parastyle name='Normal' fontName="DejaVu Sans"/>
|
||||||
|
<paraStyle name="main_footer" fontSize="8.0" alignment="CENTER"/>
|
||||||
|
<paraStyle name="main_header" fontSize="8.0" leading="10" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/>
|
||||||
</stylesheet>
|
</stylesheet>
|
||||||
<pageGraphics>
|
<pageGraphics>
|
||||||
|
<!-- Set here the default font to use for all <drawString> tags -->
|
||||||
|
<setFont name="DejaVu Sans" size="8"/>
|
||||||
<!-- You Logo - Change X,Y,Width and Height -->
|
<!-- You Logo - Change X,Y,Width and Height -->
|
||||||
<image x="1.3cm" y="%s" height="40.0" >[[ company.logo or removeParentNode('image') ]]</image>
|
<image x="1.3cm" y="%s" height="40.0" >[[ company.logo or removeParentNode('image') ]]</image>
|
||||||
<setFont name="DejaVu Sans" size="8"/>
|
|
||||||
<fill color="black"/>
|
<fill color="black"/>
|
||||||
<stroke color="black"/>
|
<stroke color="black"/>
|
||||||
|
|
||||||
|
|
|
@ -77,6 +77,7 @@ class res_currency(osv.osv):
|
||||||
'position' : 'after',
|
'position' : 'after',
|
||||||
'rounding': 0.01,
|
'rounding': 0.01,
|
||||||
'accuracy': 4,
|
'accuracy': 4,
|
||||||
|
'company_id': False,
|
||||||
}
|
}
|
||||||
_sql_constraints = [
|
_sql_constraints = [
|
||||||
# this constraint does not cover all cases due to SQL NULL handling for company_id,
|
# this constraint does not cover all cases due to SQL NULL handling for company_id,
|
||||||
|
|
|
@ -74,7 +74,8 @@ class format_address(object):
|
||||||
|
|
||||||
|
|
||||||
def _tz_get(self,cr,uid, context=None):
|
def _tz_get(self,cr,uid, context=None):
|
||||||
return [(x, x) for x in pytz.all_timezones]
|
# put POSIX 'Etc/*' entries at the end to avoid confusing users - see bug 1086728
|
||||||
|
return [(tz,tz) for tz in sorted(pytz.all_timezones, key=lambda tz: tz if not tz.startswith('Etc/') else '_')]
|
||||||
|
|
||||||
class res_partner_category(osv.osv):
|
class res_partner_category(osv.osv):
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,7 @@ class groups(osv.osv):
|
||||||
_name = "res.groups"
|
_name = "res.groups"
|
||||||
_description = "Access Groups"
|
_description = "Access Groups"
|
||||||
_rec_name = 'full_name'
|
_rec_name = 'full_name'
|
||||||
|
_order = 'name'
|
||||||
|
|
||||||
def _get_full_name(self, cr, uid, ids, field, arg, context=None):
|
def _get_full_name(self, cr, uid, ids, field, arg, context=None):
|
||||||
res = {}
|
res = {}
|
||||||
|
|
|
@ -87,6 +87,12 @@
|
||||||
<field name="domain_force">['|','|',('company_id.child_ids','child_of',[user.company_id.id]),('company_id','child_of',[user.company_id.id]),('company_id','=',False)]</field>
|
<field name="domain_force">['|','|',('company_id.child_ids','child_of',[user.company_id.id]),('company_id','child_of',[user.company_id.id]),('company_id','=',False)]</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
<record id="res_currency_rule" model="ir.rule">
|
||||||
|
<field name="name">multi-company currency rule</field>
|
||||||
|
<field name="model_id" ref="model_res_currency"/>
|
||||||
|
<field name="domain_force">['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])]</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
</data>
|
</data>
|
||||||
</openerp>
|
</openerp>
|
||||||
|
|
||||||
|
|
|
@ -114,7 +114,7 @@
|
||||||
"access_ir_filter employee","ir_filters employee","model_ir_filters","group_user",1,1,1,1
|
"access_ir_filter employee","ir_filters employee","model_ir_filters","group_user",1,1,1,1
|
||||||
"access_ir_filters","ir_filters_all","model_ir_filters",,1,1,1,1
|
"access_ir_filters","ir_filters_all","model_ir_filters",,1,1,1,1
|
||||||
"access_ir_config_parameter","ir_config_parameter","model_ir_config_parameter",,1,0,0,0
|
"access_ir_config_parameter","ir_config_parameter","model_ir_config_parameter",,1,0,0,0
|
||||||
"access_ir_mail_server_all","ir_mail_server","model_ir_mail_server",,1,0,0,0
|
"access_ir_mail_server","ir_mail_server","model_ir_mail_server","group_system",1,1,1,1
|
||||||
"access_ir_actions_client","ir_actions_client all","model_ir_actions_client",,1,0,0,0
|
"access_ir_actions_client","ir_actions_client all","model_ir_actions_client",,1,0,0,0
|
||||||
"access_ir_needaction_mixin","ir_needaction_mixin","model_ir_needaction_mixin",,1,1,1,1
|
"access_ir_needaction_mixin","ir_needaction_mixin","model_ir_needaction_mixin",,1,1,1,1
|
||||||
|
|
||||||
|
|
|
|
@ -291,22 +291,22 @@
|
||||||
assert res_101 == [], 'res_101: expected %r, got %r' % ([], res_101)
|
assert res_101 == [], 'res_101: expected %r, got %r' % ([], res_101)
|
||||||
assert res_102 == company_ids, 'res_102: expected %r, got %r' % (company_ids, res_102)
|
assert res_102 == company_ids, 'res_102: expected %r, got %r' % (company_ids, res_102)
|
||||||
-
|
-
|
||||||
Property of the query (one2many != False).
|
Verify domain evaluation for `one2many != False`
|
||||||
-
|
-
|
||||||
!python {model: res.currency }: |
|
!python {model: res.partner.category }: |
|
||||||
ids = self.search(cr, uid, [])
|
all_ids = self.search(cr, uid, [])
|
||||||
referenced_companies = set([x.company_id.id for x in self.browse(cr, uid, ids)])
|
parent_categs = set([c.parent_id.id for c in self.browse(cr, uid, all_ids) if c.parent_id])
|
||||||
companies = set(self.pool.get('res.company').search(cr, uid, [('currency_ids', '!=', False)]))
|
result = set(self.search(cr, uid, [('child_ids', '!=', False)]))
|
||||||
assert referenced_companies == companies
|
assert result and result == parent_categs, "Got %r, expected %r" % (result, parent_categs)
|
||||||
-
|
-
|
||||||
Property of the query (one2many = False).
|
Verify domain evaluation for `one2many == False`
|
||||||
-
|
-
|
||||||
!python {model: res.currency }: |
|
!python {model: res.partner.category }: |
|
||||||
ids = self.search(cr, uid, [])
|
all_ids = self.search(cr, uid, [])
|
||||||
referenced_companies = set([x.company_id.id for x in self.browse(cr, uid, ids)])
|
parent_categs = set([c.parent_id.id for c in self.browse(cr, uid, all_ids) if c.parent_id])
|
||||||
unreferenced_companies = set(self.pool.get('res.company').search(cr, uid, [])).difference(referenced_companies)
|
leaf_categs = set(all_ids) - parent_categs
|
||||||
companies = set(self.pool.get('res.company').search(cr, uid, [('currency_ids', '=', False)]))
|
result = set(self.search(cr, uid, [('child_ids', '=', False)]))
|
||||||
assert unreferenced_companies == companies
|
assert result and result == leaf_categs, "Got %r, expected %r" % (result, leaf_categs)
|
||||||
-
|
-
|
||||||
Equivalent queries.
|
Equivalent queries.
|
||||||
-
|
-
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
##############################################################################
|
##############################################################################
|
||||||
#
|
#
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
##############################################################################
|
##############################################################################
|
||||||
#
|
#
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
##############################################################################
|
##############################################################################
|
||||||
#
|
#
|
||||||
|
|
|
@ -3857,11 +3857,12 @@ class BaseModel(object):
|
||||||
# Attempt to distinguish record rule restriction vs deleted records,
|
# Attempt to distinguish record rule restriction vs deleted records,
|
||||||
# to provide a more specific error message - check if the missinf
|
# to provide a more specific error message - check if the missinf
|
||||||
cr.execute('SELECT id FROM ' + self._table + ' WHERE id IN %s', (tuple(missing_ids),))
|
cr.execute('SELECT id FROM ' + self._table + ' WHERE id IN %s', (tuple(missing_ids),))
|
||||||
if cr.rowcount:
|
forbidden_ids = [x[0] for x in cr.fetchall()]
|
||||||
|
if forbidden_ids:
|
||||||
# the missing ids are (at least partially) hidden by access rules
|
# the missing ids are (at least partially) hidden by access rules
|
||||||
if uid == SUPERUSER_ID:
|
if uid == SUPERUSER_ID:
|
||||||
return
|
return
|
||||||
_logger.warning('Access Denied by record rules for operation: %s, uid: %s, model: %s', operation, uid, self._name)
|
_logger.warning('Access Denied by record rules for operation: %s on record ids: %r, uid: %s, model: %s', operation, forbidden_ids, uid, self._name)
|
||||||
raise except_orm(_('Access Denied'),
|
raise except_orm(_('Access Denied'),
|
||||||
_('The requested operation cannot be completed due to security restrictions. Please contact your system administrator.\n\n(Document type: %s, Operation: %s)') % \
|
_('The requested operation cannot be completed due to security restrictions. Please contact your system administrator.\n\n(Document type: %s, Operation: %s)') % \
|
||||||
(self._description, operation))
|
(self._description, operation))
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU Lesser General Public
|
# You should have received a copy of the GNU Lesser General Public
|
||||||
# License along with this library; if not, write to the Free Software
|
# License along with this library; if not, write to the Free Software
|
||||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
#
|
#
|
||||||
##############################################################################
|
##############################################################################
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU Lesser General Public
|
# You should have received a copy of the GNU Lesser General Public
|
||||||
# License along with this library; if not, write to the Free Software
|
# License along with this library; if not, write to the Free Software
|
||||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
#
|
#
|
||||||
##############################################################################
|
##############################################################################
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
#
|
#
|
||||||
# OpenERP, Open Source Management Solution
|
# OpenERP, Open Source Management Solution
|
||||||
# Copyright (C) 2004-2009 P. Christeas, Tiny SPRL (<http://tiny.be>).
|
# Copyright (C) 2004-2009 P. Christeas, Tiny SPRL (<http://tiny.be>).
|
||||||
# Copyright (C) 2010 OpenERP SA. (http://www.openerp.com)
|
# Copyright (C) 2010-2013 OpenERP SA. (http://www.openerp.com)
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU Affero General Public License as
|
# it under the terms of the GNU Affero General Public License as
|
||||||
|
@ -56,7 +56,11 @@ CustomTTFonts = [ ('Helvetica',"DejaVu Sans", "DejaVuSans.ttf", 'normal'),
|
||||||
('Courier',"FreeMono", "FreeMono.ttf", 'normal'),
|
('Courier',"FreeMono", "FreeMono.ttf", 'normal'),
|
||||||
('Courier',"FreeMono Bold", "FreeMonoBold.ttf", 'bold'),
|
('Courier',"FreeMono Bold", "FreeMonoBold.ttf", 'bold'),
|
||||||
('Courier',"FreeMono Oblique", "FreeMonoOblique.ttf", 'italic'),
|
('Courier',"FreeMono Oblique", "FreeMonoOblique.ttf", 'italic'),
|
||||||
('Courier',"FreeMono BoldOblique", "FreeMonoBoldOblique.ttf", 'bolditalic'),]
|
('Courier',"FreeMono BoldOblique", "FreeMonoBoldOblique.ttf", 'bolditalic'),
|
||||||
|
|
||||||
|
# Sun-ExtA can be downloaded from http://okuc.net/SunWb/
|
||||||
|
('Sun-ExtA',"Sun-ExtA", "Sun-ExtA.ttf", 'normal'),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
TTFSearchPath_Linux = [
|
TTFSearchPath_Linux = [
|
||||||
|
|
|
@ -160,9 +160,11 @@ class _rml_styles(object,):
|
||||||
for style in node.findall('paraStyle'):
|
for style in node.findall('paraStyle'):
|
||||||
sname = style.get('name')
|
sname = style.get('name')
|
||||||
self.styles[sname] = self._para_style_update(style)
|
self.styles[sname] = self._para_style_update(style)
|
||||||
|
if sname in self.default_style:
|
||||||
|
for key, value in self.styles[sname].items():
|
||||||
|
setattr(self.default_style[sname], key, value)
|
||||||
|
else:
|
||||||
self.styles_obj[sname] = reportlab.lib.styles.ParagraphStyle(sname, self.default_style["Normal"], **self.styles[sname])
|
self.styles_obj[sname] = reportlab.lib.styles.ParagraphStyle(sname, self.default_style["Normal"], **self.styles[sname])
|
||||||
|
|
||||||
for variable in node.findall('initialize'):
|
for variable in node.findall('initialize'):
|
||||||
for name in variable.findall('name'):
|
for name in variable.findall('name'):
|
||||||
self.names[ name.get('id')] = name.get('value')
|
self.names[ name.get('id')] = name.get('value')
|
||||||
|
@ -269,6 +271,7 @@ class _rml_doc(object):
|
||||||
from reportlab.pdfbase.ttfonts import TTFont
|
from reportlab.pdfbase.ttfonts import TTFont
|
||||||
|
|
||||||
for node in els:
|
for node in els:
|
||||||
|
|
||||||
for font in node.findall('registerFont'):
|
for font in node.findall('registerFont'):
|
||||||
name = font.get('fontName').encode('ascii')
|
name = font.get('fontName').encode('ascii')
|
||||||
fname = font.get('fontFile').encode('ascii')
|
fname = font.get('fontFile').encode('ascii')
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU Lesser General Public
|
# You should have received a copy of the GNU Lesser General Public
|
||||||
# License along with this library; if not, write to the Free Software
|
# License along with this library; if not, write to the Free Software
|
||||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
#
|
#
|
||||||
##############################################################################
|
##############################################################################
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
##############################################################################
|
##############################################################################
|
||||||
#
|
#
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program; if not, write to the Free Software
|
# along with this program; if not, write to the Free Software
|
||||||
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program; if not, write to the Free Software
|
# along with this program; if not, write to the Free Software
|
||||||
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -353,6 +353,7 @@ class WorkerHTTP(Worker):
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
Worker.start(self)
|
Worker.start(self)
|
||||||
|
self.multi.long_polling_socket.close()
|
||||||
self.server = WorkerBaseWSGIServer(self.multi.app)
|
self.server = WorkerBaseWSGIServer(self.multi.app)
|
||||||
|
|
||||||
class WorkerLongPolling(Worker):
|
class WorkerLongPolling(Worker):
|
||||||
|
@ -362,6 +363,15 @@ class WorkerLongPolling(Worker):
|
||||||
# Disable the watchdog feature for this kind of worker.
|
# Disable the watchdog feature for this kind of worker.
|
||||||
self.watchdog_timeout = None
|
self.watchdog_timeout = None
|
||||||
|
|
||||||
|
def watch_parent(self):
|
||||||
|
import gevent
|
||||||
|
while True:
|
||||||
|
if self.ppid != os.getppid():
|
||||||
|
_logger.info("WorkerLongPolling (%s) Parent changed", self.pid)
|
||||||
|
os.kill(os.getpid(), signal.SIGTERM)
|
||||||
|
return
|
||||||
|
gevent.sleep(self.multi.beat)
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
openerp.evented = True
|
openerp.evented = True
|
||||||
_logger.info('Using gevent mode')
|
_logger.info('Using gevent mode')
|
||||||
|
@ -369,20 +379,22 @@ class WorkerLongPolling(Worker):
|
||||||
gevent.monkey.patch_all()
|
gevent.monkey.patch_all()
|
||||||
import gevent_psycopg2
|
import gevent_psycopg2
|
||||||
gevent_psycopg2.monkey_patch()
|
gevent_psycopg2.monkey_patch()
|
||||||
|
from openerp.modules.registry import RegistryManager
|
||||||
|
from gevent.coros import RLock
|
||||||
|
RegistryManager.registries_lock = RLock()
|
||||||
|
|
||||||
Worker.start(self)
|
Worker.start(self)
|
||||||
|
self.multi.socket.close()
|
||||||
|
|
||||||
|
import gevent
|
||||||
|
watcher = gevent.spawn(self.watch_parent)
|
||||||
|
|
||||||
|
log = _logger.getChild(self.__class__.__name__)
|
||||||
|
log.write = lambda msg: log.info(msg.strip())
|
||||||
|
|
||||||
from gevent.wsgi import WSGIServer
|
from gevent.wsgi import WSGIServer
|
||||||
self.server = WSGIServer(self.multi.long_polling_socket, self.multi.app)
|
self.server = WSGIServer(self.multi.long_polling_socket, self.multi.app, log=log)
|
||||||
self.server.start()
|
self.server.serve_forever()
|
||||||
|
|
||||||
def stop(self):
|
|
||||||
self.server.stop()
|
|
||||||
|
|
||||||
def sleep(self):
|
|
||||||
time.sleep(1)
|
|
||||||
|
|
||||||
def process_work(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
class WorkerBaseWSGIServer(werkzeug.serving.BaseWSGIServer):
|
class WorkerBaseWSGIServer(werkzeug.serving.BaseWSGIServer):
|
||||||
""" werkzeug WSGI Server patched to allow using an external listen socket
|
""" werkzeug WSGI Server patched to allow using an external listen socket
|
||||||
|
@ -456,6 +468,8 @@ class WorkerCron(Worker):
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
Worker.start(self)
|
Worker.start(self)
|
||||||
|
self.multi.socket.close()
|
||||||
|
self.multi.long_polling_socket.close()
|
||||||
openerp.service.start_internal()
|
openerp.service.start_internal()
|
||||||
|
|
||||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program; if not, write to the Free Software
|
# along with this program; if not, write to the Free Software
|
||||||
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
#####################################################################################
|
#####################################################################################
|
||||||
|
|
||||||
!include 'MUI2.nsh'
|
!include 'MUI2.nsh'
|
||||||
|
|
Loading…
Reference in New Issue