From 63ee6a74a91a630f5b5049f10ae67291df85f760 Mon Sep 17 00:00:00 2001 From: "Divyesh Makwana (Open ERP)" Date: Mon, 9 Jul 2012 17:09:54 +0530 Subject: [PATCH 001/229] [IMP] project : column project_task_history_cumulative.project_id does not exist. lp bug: https://launchpad.net/bugs/1022509 fixed bzr revid: mdi@tinyerp.com-20120709113954-l17wlurxovz6z05i --- addons/project/report/project_cumulative.xml | 3 --- 1 file changed, 3 deletions(-) diff --git a/addons/project/report/project_cumulative.xml b/addons/project/report/project_cumulative.xml index 3f7f3b26d96..535fbaf5f87 100644 --- a/addons/project/report/project_cumulative.xml +++ b/addons/project/report/project_cumulative.xml @@ -9,7 +9,6 @@ - @@ -63,9 +62,7 @@ - - From e2ba0eb95c38a16839836deeaa9a73b58221e9aa Mon Sep 17 00:00:00 2001 From: "Divyesh Makwana (Open ERP)" Date: Mon, 9 Jul 2012 18:42:59 +0530 Subject: [PATCH 002/229] [IMP] project : Revert the unneccessary changes. bzr revid: mdi@tinyerp.com-20120709131259-c9fl7g3hgwhxutgb --- addons/project/report/project_cumulative.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/addons/project/report/project_cumulative.xml b/addons/project/report/project_cumulative.xml index 535fbaf5f87..177e945bc93 100644 --- a/addons/project/report/project_cumulative.xml +++ b/addons/project/report/project_cumulative.xml @@ -9,6 +9,7 @@ + From 7655344ec534467b68e0f9bdd78ebcd46efc29b3 Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Thu, 20 Sep 2012 09:45:14 +0200 Subject: [PATCH 003/229] [FIX] auth_signup: fix reference to template user for signup bzr revid: rco@openerp.com-20120920074514-en23fuwudyn2zxjx --- addons/auth_signup/res_users.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/auth_signup/res_users.py b/addons/auth_signup/res_users.py index 5f6463508b6..ca778d68173 100644 --- a/addons/auth_signup/res_users.py +++ b/addons/auth_signup/res_users.py @@ -14,7 +14,7 @@ class res_users(osv.Model): # groups (optional) # sign (for partner_id and groups) # - user_template_id = self.pool.get('ir.config_parameter').get_param(cr, uid, 'auth.signup_template_user_id', 0) + user_template_id = self.pool.get('ir.config_parameter').get_param(cr, uid, 'auth_signup.template_user_id', 0) if user_template_id: self.pool.get('res.users').copy(cr, SUPERUSER_ID, user_template_id, new_user, context=context) else: From 7873305eb1050444330e77e99344929c7a5a8891 Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Fri, 21 Sep 2012 12:42:37 +0200 Subject: [PATCH 004/229] [IMP] auth_signup: define new model and methods for signup bzr revid: rco@openerp.com-20120921104237-krjff0k8lvhric8c --- addons/auth_signup/res_config.py | 4 +- addons/auth_signup/res_config.xml | 4 +- addons/auth_signup/res_users.py | 121 +++++++++++++++++++++++++++++- 3 files changed, 126 insertions(+), 3 deletions(-) diff --git a/addons/auth_signup/res_config.py b/addons/auth_signup/res_config.py index 8bfe1fc3ddb..36732d439f5 100644 --- a/addons/auth_signup/res_config.py +++ b/addons/auth_signup/res_config.py @@ -32,10 +32,12 @@ class base_config_settings(osv.TransientModel): def get_default_auth_signup_template_user_id(self, cr, uid, fields, context=None): icp = self.pool.get('ir.config_parameter') return { - 'auth_signup_template_user_id': icp.get_param(cr, uid, 'auth_signup.template_user_id', 0) or False + 'auth_signup_uninvited': icp.get_param(cr, uid, 'auth_signup.allow_uninvited', False), + 'auth_signup_template_user_id': icp.get_param(cr, uid, 'auth_signup.template_user_id', False), } def set_auth_signup_template_user_id(self, cr, uid, ids, context=None): config = self.browse(cr, uid, ids[0], context=context) icp = self.pool.get('ir.config_parameter') + icp.set_param(cr, uid, 'auth_signup.allow_uninvited', config.auth_signup_uninvited) icp.set_param(cr, uid, 'auth_signup.template_user_id', config.auth_signup_template_user_id.id) diff --git a/addons/auth_signup/res_config.xml b/addons/auth_signup/res_config.xml index c17634ececb..9d3eb4eaa5a 100644 --- a/addons/auth_signup/res_config.xml +++ b/addons/auth_signup/res_config.xml @@ -14,7 +14,9 @@
diff --git a/addons/auth_signup/res_users.py b/addons/auth_signup/res_users.py index ca778d68173..636763e5ca9 100644 --- a/addons/auth_signup/res_users.py +++ b/addons/auth_signup/res_users.py @@ -1,6 +1,125 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2012-today OpenERP SA () +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see +# +############################################################################## + import openerp -from openerp.osv import osv +from openerp.osv import osv, fields from openerp import SUPERUSER_ID +from openerp.tools.misc import DEFAULT_SERVER_DATETIME_FORMAT + +import time +import random +import urlparse + +def random_token(): + # the token has an entropy of 120 bits (6 bits/char * 20 chars) + chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' + return ''.join(random.choice(chars) for i in xrange(20)) + +def now(): + return time.strftime(DEFAULT_SERVER_DATETIME_FORMAT) + + +class res_partner(osv.Model): + _inherit = 'res.partner' + _columns = { + 'signup_token': fields.char(size=24, string='Signup Ticket'), + 'signup_expiration': fields.datetime(string='Signup Expiration'), + } + + def signup_generate_token(self, cr, uid, partner_id, context=None): + """ generate a new token for a partner, and return it + :param partner_id: the partner id + :param expiration: the expiration datetime of the token (string, optional) + :return: the token (string) + """ + # generate a unique token + token = random_token() + while self.signup_retrieve_partner(cr, uid, token, context): + token = random_token() + self.write(cr, uid, [partner_id], {'signup_token': token, 'signup_expiration': expiration}, context=context) + return token + + def signup_retrieve_partner(self, cr, uid, token, raise_exception=False, context=None): + """ find the partner corresponding to a token, and return its partner id or False """ + partner_ids = self.search(cr, uid, [('signup_token', '=', token)], context=context) + return partner_ids and partner_ids[0] or False + + def signup_get_url(self, cr, uid, partner_id, context): + """ determine a url for the partner_id to sign up """ + base_url = self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url') + token = self.browse(cr, uid, partner_id, context).signup_token + if not token: + token = self.signup_generate_token(cr, uid, partner_id, context=context) + return urlparse.urljoin(base_url, '/login?db=%s#action=signup&token=%s' % (cr.dbname, token)) + + def signup(self, cr, values, token=None, context=None): + """ signup a user, to either: + - create a new user (no token), or + - create a user for a partner (with token, but no user for partner), or + - change the password of a user (with token, and existing user). + :param values: a dictionary with field values + :param token: signup token (optional) + :return: the uid of the signed up user + """ + # signup the user, then log in (for setting properly the login_date) + assert values.get('login') and values.get('password') + uid = self._signup_user(cr, values, token, context) + return self.login(cr.dbname, values['login'], values['password']) + + def _signup_user(self, cr, values, token=None, context=None): + ir_config_parameter = self.pool.get('ir.config_parameter') + values.update({'signup_token': False, 'signup_expiration': False}) + + if token: + # signup with a token: find the corresponding partner id + partner_id = self.signup_retrieve_partner(cr, SUPERUSER_ID, token, context=None) + if not partner_id: + raise Exception('Signup token is not valid') + partner = self.browse(cr, SUPERUSER_ID, partner_id, context) + if partner.signup_expiration and partner.signup_expiration < now(): + raise Exception('Signup token is no longer valid') + assert values['login'] == partner.email + + # if user exists, modify its password + if partner.user_ids: + user = partner.user_ids[0] + user.write(values) + return user.id + + # user does not exist: connect the new user to the partner + values.update({'name': partner.name, 'partner_id': partner.id}) + + else: + # check whether uninvited users may sign up + if not ir_config_parameter.get_param(cr, SUPERUSER_ID, 'auth_signup.allow_uninvited', False): + raise Exception('Signup is not allowed for uninvited users') + + # create a new user + assert values.get('name') + values['email'] = values['login'] + template_user_id = ir_config_parameter.get_param(cr, SUPERUSER_ID, 'auth_signup.template_user_id') + assert template_user_id, 'Signup: missing template user' + return self.pool.get('res.users').copy(cr, SUPERUSER_ID, template_user_id, data, context=context) + + class res_users(osv.Model): _inherit = 'res.users' From d19ea766fa0499f5905c15894cf8e0615c1555d6 Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Mon, 24 Sep 2012 10:26:09 +0200 Subject: [PATCH 005/229] [IMP] auth_signup: improve API and implementation of signup bzr revid: rco@openerp.com-20120924082609-75dnuzwrlhtvafh4 --- addons/auth_signup/res_users.py | 65 ++++++++++++++++++++------------- 1 file changed, 39 insertions(+), 26 deletions(-) diff --git a/addons/auth_signup/res_users.py b/addons/auth_signup/res_users.py index 636763e5ca9..f37c547ad8c 100644 --- a/addons/auth_signup/res_users.py +++ b/addons/auth_signup/res_users.py @@ -77,47 +77,60 @@ class res_partner(osv.Model): - change the password of a user (with token, and existing user). :param values: a dictionary with field values :param token: signup token (optional) - :return: the uid of the signed up user + :return: (dbname, login, password) for the signed up user """ - # signup the user, then log in (for setting properly the login_date) assert values.get('login') and values.get('password') - uid = self._signup_user(cr, values, token, context) - return self.login(cr.dbname, values['login'], values['password']) - - def _signup_user(self, cr, values, token=None, context=None): - ir_config_parameter = self.pool.get('ir.config_parameter') - values.update({'signup_token': False, 'signup_expiration': False}) + result = (cr.dbname, values['login'], values['password']) if token: # signup with a token: find the corresponding partner id - partner_id = self.signup_retrieve_partner(cr, SUPERUSER_ID, token, context=None) + partner_id = self.signup_retrieve_partner(cr, uid, token, context=None) if not partner_id: raise Exception('Signup token is not valid') - partner = self.browse(cr, SUPERUSER_ID, partner_id, context) + partner = self.browse(cr, uid, partner_id, context) if partner.signup_expiration and partner.signup_expiration < now(): raise Exception('Signup token is no longer valid') - assert values['login'] == partner.email - # if user exists, modify its password if partner.user_ids: - user = partner.user_ids[0] - user.write(values) - return user.id + # user exists, modify its password and clear token + partner.user_ids[0].write({ + 'password': values['password'], + 'signup_token': False, + 'signup_expiration': False, + }) + else: + # user does not exist: sign up invited user + self._signup_create_user(cr, uid, { + 'name': partner.name, + 'login': values['login'], + 'password': values['password'], + 'email': values['login'], + 'partner_id': partner.id, + }, token=token, context=context) - # user does not exist: connect the new user to the partner - values.update({'name': partner.name, 'partner_id': partner.id}) + return result - else: - # check whether uninvited users may sign up - if not ir_config_parameter.get_param(cr, SUPERUSER_ID, 'auth_signup.allow_uninvited', False): - raise Exception('Signup is not allowed for uninvited users') + # sign up an external user + assert values.get('name'), 'Signup: no name given for new user' + self._signup_create_user(cr, uid, { + 'name': values['name'], + 'login': values['login'], + 'password': values['password'], + 'email': values['login'], + }, context=context) + return result - # create a new user - assert values.get('name') - values['email'] = values['login'] - template_user_id = ir_config_parameter.get_param(cr, SUPERUSER_ID, 'auth_signup.template_user_id') + def _signup_create_user(self, cr, uid, values, token=None, context=None): + """ create a new user from the template user """ + # check that uninvited users may sign up + ir_config_parameter = self.pool.get('ir.config_parameter') + if token and not ir_config_parameter.get_param(cr, uid, 'auth_signup.allow_uninvited', False): + raise Exception('Signup is not allowed for uninvited users') + + template_user_id = ir_config_parameter.get_param(cr, uid, 'auth_signup.template_user_id') assert template_user_id, 'Signup: missing template user' - return self.pool.get('res.users').copy(cr, SUPERUSER_ID, template_user_id, data, context=context) + values['active'] = True + return self.pool.get('res.users').copy(cr, uid, template_user_id, values, context=context) From aceae3501efeece3366db5a478bf450067e37301 Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Tue, 25 Sep 2012 12:40:13 +0200 Subject: [PATCH 006/229] [IMP] auth_signup: extend the login form to allow signups bzr revid: rco@openerp.com-20120925104013-f2vfqcjknx7zyo26 --- addons/auth_signup/controllers/main.py | 49 +++++-- addons/auth_signup/res_users.py | 121 ++++++++---------- .../auth_signup/static/src/js/auth_signup.js | 104 ++++++++------- .../static/src/xml/auth_signup.xml | 43 +++---- 4 files changed, 172 insertions(+), 145 deletions(-) diff --git a/addons/auth_signup/controllers/main.py b/addons/auth_signup/controllers/main.py index 821ef44f459..343c660ff7d 100644 --- a/addons/auth_signup/controllers/main.py +++ b/addons/auth_signup/controllers/main.py @@ -1,25 +1,58 @@ -import logging - -import werkzeug.urls +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2012-today OpenERP SA () +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see +# +############################################################################## +from openerp import SUPERUSER_ID from openerp.modules.registry import RegistryManager from openerp.addons.web.controllers.main import login_and_redirect import openerp.addons.web.common.http as openerpweb -from openerp import SUPERUSER_ID +import werkzeug + +import logging _logger = logging.getLogger(__name__) -class OpenIDController(openerpweb.Controller): +class Controller(openerpweb.Controller): _cp_path = '/auth_signup' + @openerpweb.jsonrequest + def retrieve(self, req, dbname, token): + """ retrieve the user info (name, login or email) corresponding to a signup token """ + registry = RegistryManager.get(dbname) + user_info = None + with registry.cursor() as cr: + res_partner = registry.get('res.partner') + user_info = res_partner.signup_retrieve_info(cr, SUPERUSER_ID, token) + user_info.update(db=dbname, token=token) + return user_info + @openerpweb.httprequest - def signup(self, req, dbname, name, login, password): + def signup(self, req, dbname, token, name, login, password): + """ sign up a user (new or existing), and log it in """ url = '/' registry = RegistryManager.get(dbname) with registry.cursor() as cr: try: - Users = registry.get('res.users') - credentials = Users.auth_signup(cr, SUPERUSER_ID, name, login, password) + res_users = registry.get('res.users') + values = {'name': name, 'login': login, 'password': password} + credentials = res_users.signup(cr, SUPERUSER_ID, values, token) cr.commit() return login_and_redirect(req, *credentials) except AttributeError: diff --git a/addons/auth_signup/res_users.py b/addons/auth_signup/res_users.py index f37c547ad8c..600ed9c000e 100644 --- a/addons/auth_signup/res_users.py +++ b/addons/auth_signup/res_users.py @@ -29,8 +29,8 @@ import random import urlparse def random_token(): - # the token has an entropy of 120 bits (6 bits/char * 20 chars) - chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' + # the token has an entropy of about 120 bits (6 bits/char * 20 chars) + chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789' return ''.join(random.choice(chars) for i in xrange(20)) def now(): @@ -39,12 +39,25 @@ def now(): class res_partner(osv.Model): _inherit = 'res.partner' + + def signup_get_url(self, cr, uid, partner_ids, name, arg, context=None): + """ determine a url for the partner_id to sign up """ + base_url = self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url') + res = {} + for partner in self.browse(cr, uid, partner_ids, context): + token = partner.signup_token + if not token: + token = self._signup_generate_token(cr, uid, partner.id, context=context) + res[partner.id] = urlparse.urljoin(base_url, '#action=login&db=%s&token=%s' % (cr.dbname, token)) + return res + _columns = { 'signup_token': fields.char(size=24, string='Signup Ticket'), 'signup_expiration': fields.datetime(string='Signup Expiration'), + 'signup_url': fields.function(signup_get_url, type='char', string='Signup URL'), } - def signup_generate_token(self, cr, uid, partner_id, context=None): + def _signup_generate_token(self, cr, uid, partner_id, expiration=False, context=None): """ generate a new token for a partner, and return it :param partner_id: the partner id :param expiration: the expiration datetime of the token (string, optional) @@ -52,25 +65,45 @@ class res_partner(osv.Model): """ # generate a unique token token = random_token() - while self.signup_retrieve_partner(cr, uid, token, context): + while self._signup_retrieve_partner(cr, uid, token, context=context): token = random_token() self.write(cr, uid, [partner_id], {'signup_token': token, 'signup_expiration': expiration}, context=context) return token - def signup_retrieve_partner(self, cr, uid, token, raise_exception=False, context=None): - """ find the partner corresponding to a token, and return its partner id or False """ + def _signup_retrieve_partner(self, cr, uid, token, raise_exception=False, context=None): + """ find the partner corresponding to a token, and check its validity + :return: partner (browse record) or False (if raise_exception is False) + :raise: when token not valid (if raise_exception is True) + """ partner_ids = self.search(cr, uid, [('signup_token', '=', token)], context=context) - return partner_ids and partner_ids[0] or False + if not partner_ids: + if raise_exception: + raise Exception("Signup token '%s' is not valid" % token) + return False + partner = self.browse(cr, uid, partner_ids[0], context) + if partner.signup_expiration and partner.signup_expiration < now(): + if raise_exception: + raise Exception("Signup token '%s' is no longer valid" % token) + return False + return partner - def signup_get_url(self, cr, uid, partner_id, context): - """ determine a url for the partner_id to sign up """ - base_url = self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url') - token = self.browse(cr, uid, partner_id, context).signup_token - if not token: - token = self.signup_generate_token(cr, uid, partner_id, context=context) - return urlparse.urljoin(base_url, '/login?db=%s#action=signup&token=%s' % (cr.dbname, token)) + def signup_retrieve_info(self, cr, uid, token, context=None): + """ retrieve the user info about the token + :return: either {'name': ..., 'login': ...} if a user exists for that token, + or {'name': ..., 'email': ...} otherwise + """ + partner = self._signup_retrieve_partner(cr, uid, token, raise_exception=True, context=None) + if partner.user_ids: + return {'name': partner.name, 'login': partner.user_ids[0].login} + else: + return {'name': partner.name, 'email': partner.email} - def signup(self, cr, values, token=None, context=None): + + +class res_users(osv.Model): + _inherit = 'res.users' + + def signup(self, cr, uid, values, token=None, context=None): """ signup a user, to either: - create a new user (no token), or - create a user for a partner (with token, but no user for partner), or @@ -84,13 +117,8 @@ class res_partner(osv.Model): if token: # signup with a token: find the corresponding partner id - partner_id = self.signup_retrieve_partner(cr, uid, token, context=None) - if not partner_id: - raise Exception('Signup token is not valid') - partner = self.browse(cr, uid, partner_id, context) - if partner.signup_expiration and partner.signup_expiration < now(): - raise Exception('Signup token is no longer valid') - + res_partner = self.pool.get('res.partner') + partner = res_partner._signup_retrieve_partner(cr, uid, token, raise_exception=True, context=None) if partner.user_ids: # user exists, modify its password and clear token partner.user_ids[0].write({ @@ -129,51 +157,6 @@ class res_partner(osv.Model): template_user_id = ir_config_parameter.get_param(cr, uid, 'auth_signup.template_user_id') assert template_user_id, 'Signup: missing template user' - values['active'] = True - return self.pool.get('res.users').copy(cr, uid, template_user_id, values, context=context) + values.update({'active': True, 'signup_token': False, 'signup_expiration': False}) + return self.copy(cr, uid, template_user_id, values, context=context) - - -class res_users(osv.Model): - _inherit = 'res.users' - - def auth_signup_create(self, cr, uid, new_user, context=None): - # new_user: - # login - # email - # name (optional) - # partner_id (optional) - # groups (optional) - # sign (for partner_id and groups) - # - user_template_id = self.pool.get('ir.config_parameter').get_param(cr, uid, 'auth_signup.template_user_id', 0) - if user_template_id: - self.pool.get('res.users').copy(cr, SUPERUSER_ID, user_template_id, new_user, context=context) - else: - self.pool.get('res.users').create(cr, SUPERUSER_ID, new_user, context=context) - - def auth_signup(self, cr, uid, name, login, password, context=None): - r = (cr.dbname, login, password) - res = self.search(cr, uid, [("login", "=", login)]) - if res: - # Existing user - user_id = res[0] - try: - self.check(cr.dbname, user_id, password) - # Same password - except openerp.exceptions.AccessDenied: - # Different password - raise - else: - # New user - new_user = { - 'name': name, - 'login': login, - 'user_email': login, - 'password': password, - 'active': True, - } - self.auth_signup_create(cr, uid, new_user) - return r - -# diff --git a/addons/auth_signup/static/src/js/auth_signup.js b/addons/auth_signup/static/src/js/auth_signup.js index 2ab13aa7598..bec32ce2803 100644 --- a/addons/auth_signup/static/src/js/auth_signup.js +++ b/addons/auth_signup/static/src/js/auth_signup.js @@ -5,56 +5,68 @@ openerp.auth_signup = function(instance) { instance.web.Login.include({ start: function() { var self = this; - this.$('a.oe_signup').click(function() { - var dbname = self.$("form [name=db]").val(); - self.do_action({ - type: 'ir.actions.client', - tag: 'auth_signup.signup', - params: {'dbname': dbname}, - target: 'new', - name: 'Sign up' - }); - return true; - }); - return this._super(); + var d = this._super(); + // hide the signup fields in the case of a regular login + self.on_change_mode() + self.$("form input[name=signup]").click(self.on_change_mode); + // in case of a signup, retrieve the user information from the token + if (self.params.db && self.params.token) { + d = self.rpc("/auth_signup/retrieve", {dbname: self.params.db, token: self.params.token}) + .done(self.on_token_loaded) + .fail(self.on_token_failed); + } + return d; }, - }); - - - instance.auth_signup.Signup = instance.web.Widget.extend({ - template: 'auth_signup.signup', - init: function(parent, params) { - this.params = params; - return this._super(); + on_token_loaded: function(result) { + // set the name and login of user + this.selected_db = result.db; + this.on_db_loaded({db_list: [result.db]}); + this.$("form input[name=signup]").val(result.login ? [] : ["check_signup"]); + this.$("form input[name=name]").val(result.name); + this.$("form input[name=login]").val(result.login || result.email); + this.$("form input[name=password]").val(""); + this.$("form input[name=confirm_password]").val(""); + this.on_change_mode(); }, - start: function() { - var self = this; - this.$('input[name=password_confirmation]').keyup(function() { - var v = $(this).val(); - var $b = self.$('button'); - if (_.isEmpty(v) || self.$('input[name=password]').val() === v) { - $b.removeAttr('disabled'); + on_token_failed: function(result) { + // currently does nothing + }, + on_change_mode: function() { + // 'mode' has changed: regular login, sign up, reset password + var is_signup = this.$("input[name=signup]:checked").val(); + this.$(".oe_signup").toggleClass('oe_form_invisible', false && !is_signup); + return true; + }, + on_submit: function(ev) { + if (ev) { + ev.preventDefault(); + } + if (this.params.token || this.$("input[name=signup]:checked").val()) { + // signup user (or reset password) + var db = this.params.db; + var name = this.$("form input[name=name]").val(); + var login = this.$("form input[name=login]").val(); + var password = this.$("form input[name=password]").val(); + var confirm_password = this.$("form input[name=confirm_password]").val(); + + if (password && password === confirm_password) { + var params = { + dbname : db, + token: this.params.token, + name: name, + login: login, + password: password, + }; + var url = "/auth_signup/signup?" + $.param(params); + window.location = url; } else { - $b.attr('disabled', 'disabled'); + alert('Incorrect password; please retype your password.'); } - }); - - this.$('form').submit(function(ev) { - if(ev) { - ev.preventDefault(); - } - var params = { - dbname : self.params.dbname, - name: self.$('input[name=name]').val(), - login: self.$('input[name=email]').val(), - password: self.$('input[name=password]').val(), - }; - var url = "/auth_signup/signup?" + $.param(params); - window.location = url; - }); - return this._super(); - } + } else { + // regular login + this._super(ev); + } + }, }); - instance.web.client_actions.add("auth_signup.signup", "instance.auth_signup.Signup"); }; diff --git a/addons/auth_signup/static/src/xml/auth_signup.xml b/addons/auth_signup/static/src/xml/auth_signup.xml index c2eec846d15..73e26eb6ba8 100644 --- a/addons/auth_signup/static/src/xml/auth_signup.xml +++ b/addons/auth_signup/static/src/xml/auth_signup.xml @@ -1,28 +1,27 @@ - + - - -
  • - -
  • + + + + + + + +
  • Username
  • +
    + + + +
    -
    - - -
    -
    - Name =
    - Email =
    - Password =
    - Confirmation =
    - -
    -
    -
    - -
    From 597919467d4ecedddb2b3e14798c10bdf020f7ea Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Tue, 25 Sep 2012 14:45:57 +0200 Subject: [PATCH 007/229] [IMP] auth_signup: add css bzr revid: rco@openerp.com-20120925124557-2yka4yu0x7528atl --- addons/auth_signup/__openerp__.py | 1 + addons/auth_signup/static/src/css/Makefile | 3 +++ addons/auth_signup/static/src/css/base.css | 4 ++++ addons/auth_signup/static/src/css/base.sass | 7 +++++++ 4 files changed, 15 insertions(+) create mode 100644 addons/auth_signup/static/src/css/Makefile create mode 100644 addons/auth_signup/static/src/css/base.css create mode 100644 addons/auth_signup/static/src/css/base.sass diff --git a/addons/auth_signup/__openerp__.py b/addons/auth_signup/__openerp__.py index 9cfe89cdc1c..5bb3aab4ec0 100644 --- a/addons/auth_signup/__openerp__.py +++ b/addons/auth_signup/__openerp__.py @@ -36,5 +36,6 @@ Allow users to sign up. 'res_config.xml', ], 'js': ['static/src/js/auth_signup.js'], + 'css' : ['static/src/css/base.css'], 'qweb': ['static/src/xml/auth_signup.xml'], } diff --git a/addons/auth_signup/static/src/css/Makefile b/addons/auth_signup/static/src/css/Makefile new file mode 100644 index 00000000000..d6b4f4b2fc8 --- /dev/null +++ b/addons/auth_signup/static/src/css/Makefile @@ -0,0 +1,3 @@ +base.css: base.sass + sass --trace -t expanded base.sass base.css + diff --git a/addons/auth_signup/static/src/css/base.css b/addons/auth_signup/static/src/css/base.css new file mode 100644 index 00000000000..153f27bd129 --- /dev/null +++ b/addons/auth_signup/static/src/css/base.css @@ -0,0 +1,4 @@ +@charset "utf-8"; +.openerp .oe_login input[type="checkbox"] { + width: 21px !important; +} diff --git a/addons/auth_signup/static/src/css/base.sass b/addons/auth_signup/static/src/css/base.sass new file mode 100644 index 00000000000..87bdb623727 --- /dev/null +++ b/addons/auth_signup/static/src/css/base.sass @@ -0,0 +1,7 @@ +@charset "utf-8" + +.openerp + // Login form + .oe_login + input[type="checkbox"] + width: 21px !important From e184913f346f891fd74c2c6f1e0f048beb283fbb Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Tue, 25 Sep 2012 14:55:22 +0200 Subject: [PATCH 008/229] [FIX] auth_signup: store and retrieve config parameters that may be null bzr revid: rco@openerp.com-20120925125522-2t74fta15yfoesc1 --- addons/auth_signup/res_config.py | 11 +++++++---- addons/auth_signup/res_users.py | 8 +++++--- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/addons/auth_signup/res_config.py b/addons/auth_signup/res_config.py index 36732d439f5..c69c01127f6 100644 --- a/addons/auth_signup/res_config.py +++ b/addons/auth_signup/res_config.py @@ -20,6 +20,7 @@ ############################################################################## from openerp.osv import osv, fields +from openerp.tools.safe_eval import safe_eval class base_config_settings(osv.TransientModel): _inherit = 'base.config.settings' @@ -31,13 +32,15 @@ class base_config_settings(osv.TransientModel): def get_default_auth_signup_template_user_id(self, cr, uid, fields, context=None): icp = self.pool.get('ir.config_parameter') + # we use safe_eval on the result, since the value of the parameter is a nonempty string return { - 'auth_signup_uninvited': icp.get_param(cr, uid, 'auth_signup.allow_uninvited', False), - 'auth_signup_template_user_id': icp.get_param(cr, uid, 'auth_signup.template_user_id', False), + 'auth_signup_uninvited': safe_eval(icp.get_param(cr, uid, 'auth_signup.allow_uninvited', 'False')), + 'auth_signup_template_user_id': safe_eval(icp.get_param(cr, uid, 'auth_signup.template_user_id', 'False')), } def set_auth_signup_template_user_id(self, cr, uid, ids, context=None): config = self.browse(cr, uid, ids[0], context=context) icp = self.pool.get('ir.config_parameter') - icp.set_param(cr, uid, 'auth_signup.allow_uninvited', config.auth_signup_uninvited) - icp.set_param(cr, uid, 'auth_signup.template_user_id', config.auth_signup_template_user_id.id) + # we store the repr of the values, since the value of the parameter is a required string + icp.set_param(cr, uid, 'auth_signup.allow_uninvited', repr(config.auth_signup_uninvited)) + icp.set_param(cr, uid, 'auth_signup.template_user_id', repr(config.auth_signup_template_user_id.id)) diff --git a/addons/auth_signup/res_users.py b/addons/auth_signup/res_users.py index 600ed9c000e..6956c8e2109 100644 --- a/addons/auth_signup/res_users.py +++ b/addons/auth_signup/res_users.py @@ -23,6 +23,7 @@ import openerp from openerp.osv import osv, fields from openerp import SUPERUSER_ID from openerp.tools.misc import DEFAULT_SERVER_DATETIME_FORMAT +from openerp.tools.safe_eval import safe_eval import time import random @@ -152,10 +153,11 @@ class res_users(osv.Model): """ create a new user from the template user """ # check that uninvited users may sign up ir_config_parameter = self.pool.get('ir.config_parameter') - if token and not ir_config_parameter.get_param(cr, uid, 'auth_signup.allow_uninvited', False): - raise Exception('Signup is not allowed for uninvited users') + if not token: + if not safe_eval(ir_config_parameter.get_param(cr, uid, 'auth_signup.allow_uninvited', 'False')): + raise Exception('Signup is not allowed for uninvited users') - template_user_id = ir_config_parameter.get_param(cr, uid, 'auth_signup.template_user_id') + template_user_id = safe_eval(ir_config_parameter.get_param(cr, uid, 'auth_signup.template_user_id', 'False')) assert template_user_id, 'Signup: missing template user' values.update({'active': True, 'signup_token': False, 'signup_expiration': False}) return self.copy(cr, uid, template_user_id, values, context=context) From 9906a2bda10ca089fff55521f3252f4f8b178344 Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Tue, 25 Sep 2012 16:34:33 +0200 Subject: [PATCH 009/229] [IMP] auth_signup: improve error message handling in web client bzr revid: rco@openerp.com-20120925143433-p4r2gl6t37dw0s17 --- addons/auth_signup/controllers/main.py | 8 +-- .../auth_signup/static/src/js/auth_signup.js | 61 ++++++++++++++----- 2 files changed, 47 insertions(+), 22 deletions(-) diff --git a/addons/auth_signup/controllers/main.py b/addons/auth_signup/controllers/main.py index 343c660ff7d..6e053715c09 100644 --- a/addons/auth_signup/controllers/main.py +++ b/addons/auth_signup/controllers/main.py @@ -55,14 +55,10 @@ class Controller(openerpweb.Controller): credentials = res_users.signup(cr, SUPERUSER_ID, values, token) cr.commit() return login_and_redirect(req, *credentials) - except AttributeError: - # auth_signup is not installed - _logger.exception('attribute error when signup') - url = "/#action=auth_signup&error=NA" # Not Available - except Exception: + except Exception as e: # signup error _logger.exception('error when signup') - url = "/#action=auth_signup&error=UE" # Unexcpected Error + url = "/#action=login&error_message=%s" % werkzeug.urls.url_quote(e.message) return werkzeug.utils.redirect(url) # vim:expandtab:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/auth_signup/static/src/js/auth_signup.js b/addons/auth_signup/static/src/js/auth_signup.js index bec32ce2803..9c90358d9e3 100644 --- a/addons/auth_signup/static/src/js/auth_signup.js +++ b/addons/auth_signup/static/src/js/auth_signup.js @@ -6,9 +6,21 @@ openerp.auth_signup = function(instance) { start: function() { var self = this; var d = this._super(); + // hide the signup fields in the case of a regular login self.on_change_mode() self.$("form input[name=signup]").click(self.on_change_mode); + + // if there is an error message, show it then forget it + if (self.params.error_message) { + this.$el.addClass('oe_login_invalid'); + this.$(".oe_login_error_message").text(self.params.error_message); + delete self.params.error_message; + } else { + this.$el.removeClass('oe_login_invalid'); + this.$(".oe_login_error_message").text("Invalid username or password"); + } + // in case of a signup, retrieve the user information from the token if (self.params.db && self.params.token) { d = self.rpc("/auth_signup/retrieve", {dbname: self.params.db, token: self.params.token}) @@ -28,8 +40,14 @@ openerp.auth_signup = function(instance) { this.$("form input[name=confirm_password]").val(""); this.on_change_mode(); }, - on_token_failed: function(result) { - // currently does nothing + on_token_failed: function(result, ev) { + if (ev) { + ev.preventDefault(); + } + this.$el.addClass('oe_login_invalid'); + this.$(".oe_login_error_message").text("Invalid signup token"); + delete this.params.db; + delete this.params.token; }, on_change_mode: function() { // 'mode' has changed: regular login, sign up, reset password @@ -43,25 +61,36 @@ openerp.auth_signup = function(instance) { } if (this.params.token || this.$("input[name=signup]:checked").val()) { // signup user (or reset password) - var db = this.params.db; + var db = this.$("form [name=db]").val(); var name = this.$("form input[name=name]").val(); var login = this.$("form input[name=login]").val(); var password = this.$("form input[name=password]").val(); var confirm_password = this.$("form input[name=confirm_password]").val(); - - if (password && password === confirm_password) { - var params = { - dbname : db, - token: this.params.token, - name: name, - login: login, - password: password, - }; - var url = "/auth_signup/signup?" + $.param(params); - window.location = url; - } else { - alert('Incorrect password; please retype your password.'); + if (!db) { + this.do_warn("Login", "No database selected !"); + return false; + } else if (!name) { + this.do_warn("Login", "Please enter a name.") + return false; + } else if (!login) { + this.do_warn("Login", "Please enter a username.") + return false; + } else if (!password || !confirm_password) { + this.do_warn("Login", "Please enter a password and confirm it.") + return false; + } else if (password !== confirm_password) { + this.do_warn("Login", "Passwords do not match; please retype them.") + return false; } + var params = { + dbname : db, + token: this.params.token || "", + name: name, + login: login, + password: password, + }; + var url = "/auth_signup/signup?" + $.param(params); + window.location = url; } else { // regular login this._super(ev); From a50dfa668d9de6fe8e054deb24366d99b21e3d96 Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Wed, 26 Sep 2012 11:53:53 +0200 Subject: [PATCH 010/229] [IMP] auth_signup: improve error message handling with method show_error() bzr revid: rco@openerp.com-20120926095353-7cgcby7biohfx0i2 --- addons/auth_signup/static/src/js/auth_signup.js | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/addons/auth_signup/static/src/js/auth_signup.js b/addons/auth_signup/static/src/js/auth_signup.js index 9c90358d9e3..282221bc87e 100644 --- a/addons/auth_signup/static/src/js/auth_signup.js +++ b/addons/auth_signup/static/src/js/auth_signup.js @@ -11,14 +11,10 @@ openerp.auth_signup = function(instance) { self.on_change_mode() self.$("form input[name=signup]").click(self.on_change_mode); - // if there is an error message, show it then forget it + // if there is an error message in params, show it then forget it if (self.params.error_message) { - this.$el.addClass('oe_login_invalid'); - this.$(".oe_login_error_message").text(self.params.error_message); + this.show_error(self.params.error_message); delete self.params.error_message; - } else { - this.$el.removeClass('oe_login_invalid'); - this.$(".oe_login_error_message").text("Invalid username or password"); } // in case of a signup, retrieve the user information from the token @@ -44,8 +40,7 @@ openerp.auth_signup = function(instance) { if (ev) { ev.preventDefault(); } - this.$el.addClass('oe_login_invalid'); - this.$(".oe_login_error_message").text("Invalid signup token"); + this.show_error("Invalid signup token"); delete this.params.db; delete this.params.token; }, From 0605295602606a7e25f075f5e6bbfdd446e63dfc Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Wed, 26 Sep 2012 15:02:41 +0200 Subject: [PATCH 011/229] [IMP] auth_signup: improve web look-and-feel bzr revid: rco@openerp.com-20120926130241-wlzeyzrsr1xwjdcm --- addons/auth_signup/static/src/css/base.css | 10 ++++-- addons/auth_signup/static/src/css/base.sass | 12 +++++-- .../auth_signup/static/src/js/auth_signup.js | 32 +++++++++++-------- .../static/src/xml/auth_signup.xml | 31 +++++++++--------- 4 files changed, 51 insertions(+), 34 deletions(-) diff --git a/addons/auth_signup/static/src/css/base.css b/addons/auth_signup/static/src/css/base.css index 153f27bd129..3f97e7a9301 100644 --- a/addons/auth_signup/static/src/css/base.css +++ b/addons/auth_signup/static/src/css/base.css @@ -1,4 +1,10 @@ @charset "utf-8"; -.openerp .oe_login input[type="checkbox"] { - width: 21px !important; +.openerp .oe_login .oe_signup_show { + display: none; +} +.openerp .oe_login_signup .oe_signup_show { + display: block !important; +} +.openerp .oe_login_signup .oe_signup_hide { + display: none; } diff --git a/addons/auth_signup/static/src/css/base.sass b/addons/auth_signup/static/src/css/base.sass index 87bdb623727..f97665d192c 100644 --- a/addons/auth_signup/static/src/css/base.sass +++ b/addons/auth_signup/static/src/css/base.sass @@ -1,7 +1,13 @@ @charset "utf-8" .openerp - // Login form + // Regular login form .oe_login - input[type="checkbox"] - width: 21px !important + .oe_signup_show + display: none + // Signup form + .oe_login_signup + .oe_signup_show + display: block !important + .oe_signup_hide + display: none diff --git a/addons/auth_signup/static/src/js/auth_signup.js b/addons/auth_signup/static/src/js/auth_signup.js index 282221bc87e..927ee8de0c5 100644 --- a/addons/auth_signup/static/src/js/auth_signup.js +++ b/addons/auth_signup/static/src/js/auth_signup.js @@ -7,9 +7,14 @@ openerp.auth_signup = function(instance) { var self = this; var d = this._super(); - // hide the signup fields in the case of a regular login - self.on_change_mode() - self.$("form input[name=signup]").click(self.on_change_mode); + // to switch between the signup and regular login form + this.$('a.oe_signup_signup').click(function() { + self.$el.addClass("oe_login_signup"); + }); + this.$('a.oe_signup_back').click(function() { + self.$el.removeClass("oe_login_signup"); + delete self.params.token; + }); // if there is an error message in params, show it then forget it if (self.params.error_message) { @@ -26,15 +31,20 @@ openerp.auth_signup = function(instance) { return d; }, on_token_loaded: function(result) { - // set the name and login of user + // switch to signup mode + this.$el.addClass("oe_login_signup"); + // select the right the database this.selected_db = result.db; this.on_db_loaded({db_list: [result.db]}); - this.$("form input[name=signup]").val(result.login ? [] : ["check_signup"]); - this.$("form input[name=name]").val(result.name); - this.$("form input[name=login]").val(result.login || result.email); + // set the name and login of user + this.$("form input[name=name]").val(result.name).attr("readonly", "readonly"); + if (result.login) { + this.$("form input[name=login]").val(result.login).attr("readonly", "readonly"); + } else { + this.$("form input[name=login]").val(result.email); + } this.$("form input[name=password]").val(""); this.$("form input[name=confirm_password]").val(""); - this.on_change_mode(); }, on_token_failed: function(result, ev) { if (ev) { @@ -44,12 +54,6 @@ openerp.auth_signup = function(instance) { delete this.params.db; delete this.params.token; }, - on_change_mode: function() { - // 'mode' has changed: regular login, sign up, reset password - var is_signup = this.$("input[name=signup]:checked").val(); - this.$(".oe_signup").toggleClass('oe_form_invisible', false && !is_signup); - return true; - }, on_submit: function(ev) { if (ev) { ev.preventDefault(); diff --git a/addons/auth_signup/static/src/xml/auth_signup.xml b/addons/auth_signup/static/src/xml/auth_signup.xml index 73e26eb6ba8..e63bd5863b0 100644 --- a/addons/auth_signup/static/src/xml/auth_signup.xml +++ b/addons/auth_signup/static/src/xml/auth_signup.xml @@ -3,24 +3,25 @@ - - - - + + + -
  • Username
  • + +
    - - - + + + + + + + + + +
  • +
  • From 479dfb7ea079184c60dc39929c68ab37ada639e1 Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Wed, 26 Sep 2012 17:04:59 +0200 Subject: [PATCH 012/229] [FIX] auth_signup: fix the creation of a user attached to a partner from a user template bzr revid: rco@openerp.com-20120926150459-9degade1ra9sk0ds --- addons/auth_signup/res_users.py | 44 +++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/addons/auth_signup/res_users.py b/addons/auth_signup/res_users.py index 6956c8e2109..4d5bdc28b8f 100644 --- a/addons/auth_signup/res_users.py +++ b/addons/auth_signup/res_users.py @@ -97,7 +97,7 @@ class res_partner(osv.Model): if partner.user_ids: return {'name': partner.name, 'login': partner.user_ids[0].login} else: - return {'name': partner.name, 'email': partner.email} + return {'name': partner.name, 'email': partner.email or ''} @@ -120,23 +120,19 @@ class res_users(osv.Model): # signup with a token: find the corresponding partner id res_partner = self.pool.get('res.partner') partner = res_partner._signup_retrieve_partner(cr, uid, token, raise_exception=True, context=None) + # invalidate signup token + partner.write({'signup_token': False, 'signup_expiration': False}) if partner.user_ids: - # user exists, modify its password and clear token - partner.user_ids[0].write({ - 'password': values['password'], - 'signup_token': False, - 'signup_expiration': False, - }) + # user exists, modify its password + partner.user_ids[0].write({'password': values['password']}) else: # user does not exist: sign up invited user self._signup_create_user(cr, uid, { - 'name': partner.name, 'login': values['login'], 'password': values['password'], 'email': values['login'], 'partner_id': partner.id, - }, token=token, context=context) - + }, context=context) return result # sign up an external user @@ -149,16 +145,28 @@ class res_users(osv.Model): }, context=context) return result - def _signup_create_user(self, cr, uid, values, token=None, context=None): + def _signup_create_user(self, cr, uid, values, context=None): """ create a new user from the template user """ - # check that uninvited users may sign up ir_config_parameter = self.pool.get('ir.config_parameter') - if not token: + template_user_id = safe_eval(ir_config_parameter.get_param(cr, uid, 'auth_signup.template_user_id', 'False')) + assert template_user_id and self.exists(cr, uid, template_user_id, context=context), 'Signup: invalid template user' + + values['active'] = True + if values.get('partner_id'): + # create a copy of the template user attached to values['partner_id'] + # note: we do not include 'partner_id' here, as copy() does not handle it correctly + safe_values = {'login': values['login'], 'password': values['password']} + user_id = self.copy(cr, uid, template_user_id, safe_values, context=context) + # problem: the res.partner part of the template user has been duplicated + # solution: unlink it, and replace it by values['partner_id'] + user = self.browse(cr, uid, user_id, context=context) + partner = user.partner_id + user.write(values) + partner.unlink() + else: + # check that uninvited users may sign up if not safe_eval(ir_config_parameter.get_param(cr, uid, 'auth_signup.allow_uninvited', 'False')): raise Exception('Signup is not allowed for uninvited users') + user_id = self.copy(cr, uid, template_user_id, values, context=context) - template_user_id = safe_eval(ir_config_parameter.get_param(cr, uid, 'auth_signup.template_user_id', 'False')) - assert template_user_id, 'Signup: missing template user' - values.update({'active': True, 'signup_token': False, 'signup_expiration': False}) - return self.copy(cr, uid, template_user_id, values, context=context) - + return user_id From b80d84aafc7f4d543247d22d445e71752272f925 Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Thu, 27 Sep 2012 10:27:04 +0200 Subject: [PATCH 013/229] [IMP] auth_signup: remove temporary function field signup_url, and clean methods bzr revid: rco@openerp.com-20120927082704-wtuvir9tm2pgsvnq --- addons/auth_signup/res_users.py | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/addons/auth_signup/res_users.py b/addons/auth_signup/res_users.py index 4d5bdc28b8f..333686d4ca5 100644 --- a/addons/auth_signup/res_users.py +++ b/addons/auth_signup/res_users.py @@ -41,24 +41,21 @@ def now(): class res_partner(osv.Model): _inherit = 'res.partner' - def signup_get_url(self, cr, uid, partner_ids, name, arg, context=None): - """ determine a url for the partner_id to sign up """ - base_url = self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url') - res = {} - for partner in self.browse(cr, uid, partner_ids, context): - token = partner.signup_token - if not token: - token = self._signup_generate_token(cr, uid, partner.id, context=context) - res[partner.id] = urlparse.urljoin(base_url, '#action=login&db=%s&token=%s' % (cr.dbname, token)) - return res - _columns = { 'signup_token': fields.char(size=24, string='Signup Ticket'), 'signup_expiration': fields.datetime(string='Signup Expiration'), - 'signup_url': fields.function(signup_get_url, type='char', string='Signup URL'), } - def _signup_generate_token(self, cr, uid, partner_id, expiration=False, context=None): + def signup_get_url(self, cr, uid, partner_id, context=None): + """ determine a signup url for the given partner_id """ + base_url = self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url') + partner = self.browse(cr, uid, partner_id, context) + token = partner.signup_token + if not token: + token = self.signup_generate_token(cr, uid, partner.id, context=context) + return urlparse.urljoin(base_url, '#action=login&db=%s&token=%s' % (cr.dbname, token)) + + def signup_generate_token(self, cr, uid, partner_id, expiration=False, context=None): """ generate a new token for a partner, and return it :param partner_id: the partner id :param expiration: the expiration datetime of the token (string, optional) From 0f4529603c667f786f152859ad16e08ce9cc1fea Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Fri, 28 Sep 2012 10:52:59 +0200 Subject: [PATCH 014/229] [FIX] auth_signup: change test to determine when signing in bzr revid: rco@openerp.com-20120928085259-cb51yq7e7mivxsw3 --- addons/auth_signup/static/src/js/auth_signup.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/auth_signup/static/src/js/auth_signup.js b/addons/auth_signup/static/src/js/auth_signup.js index 927ee8de0c5..9bc775c8355 100644 --- a/addons/auth_signup/static/src/js/auth_signup.js +++ b/addons/auth_signup/static/src/js/auth_signup.js @@ -58,7 +58,7 @@ openerp.auth_signup = function(instance) { if (ev) { ev.preventDefault(); } - if (this.params.token || this.$("input[name=signup]:checked").val()) { + if (this.$el.hasClass("oe_login_signup")) { // signup user (or reset password) var db = this.$("form [name=db]").val(); var name = this.$("form input[name=name]").val(); From bf5395ab3dc4dfe1a26faa7f9d490a46c80271f7 Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Fri, 28 Sep 2012 10:58:43 +0200 Subject: [PATCH 015/229] [IMP] auth_signup: add function fields signup_valid, signup_url, and method prepare_signup bzr revid: rco@openerp.com-20120928085843-0erxrzva9lk5tqfh --- addons/auth_signup/res_users.py | 57 +++++++++++++++++++++------------ 1 file changed, 37 insertions(+), 20 deletions(-) diff --git a/addons/auth_signup/res_users.py b/addons/auth_signup/res_users.py index 333686d4ca5..0624b25b357 100644 --- a/addons/auth_signup/res_users.py +++ b/addons/auth_signup/res_users.py @@ -41,32 +41,49 @@ def now(): class res_partner(osv.Model): _inherit = 'res.partner' + def _get_signup_valid(self, cr, uid, ids, name, arg, context=None): + dt = now() + res = {} + for partner in self.browse(cr, uid, ids, context): + res[partner.id] = bool(partner.signup_token) and (partner.signup_expiration or '') <= dt + return res + + def _get_signup_url(self, cr, uid, ids, name, arg, context=None): + """ determine a signup url for a given partner """ + base_url = self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url') + template_url = '#action=login&db=%s&token=%s' + + # if required, make sure that every partner has a valid signup token + if context and context.get('signup_valid'): + self.signup_prepare(cr, uid, ids, context=context) + + res = dict.fromkeys(ids, False) + for partner in self.browse(cr, uid, ids, context): + if partner.signup_token: + res[partner.id] = urlparse.urljoin(base_url, template_url % (cr.dbname, partner.signup_token)) + return res + _columns = { - 'signup_token': fields.char(size=24, string='Signup Ticket'), + 'signup_token': fields.char(size=24, string='Signup Token'), 'signup_expiration': fields.datetime(string='Signup Expiration'), + 'signup_valid': fields.function(_get_signup_valid, type='boolean', string='Signup Token is Valid'), + 'signup_url': fields.function(_get_signup_url, type='char', string='Signup URL'), } - def signup_get_url(self, cr, uid, partner_id, context=None): - """ determine a signup url for the given partner_id """ - base_url = self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url') - partner = self.browse(cr, uid, partner_id, context) - token = partner.signup_token - if not token: - token = self.signup_generate_token(cr, uid, partner.id, context=context) - return urlparse.urljoin(base_url, '#action=login&db=%s&token=%s' % (cr.dbname, token)) + def action_signup_prepare(self, cr, uid, ids, context=None): + return self.signup_prepare(cr, uid, ids, context=context) - def signup_generate_token(self, cr, uid, partner_id, expiration=False, context=None): - """ generate a new token for a partner, and return it - :param partner_id: the partner id + def signup_prepare(self, cr, uid, ids, expiration=False, context=None): + """ generate a new token for the partners with the given validity, if necessary :param expiration: the expiration datetime of the token (string, optional) - :return: the token (string) """ - # generate a unique token - token = random_token() - while self._signup_retrieve_partner(cr, uid, token, context=context): - token = random_token() - self.write(cr, uid, [partner_id], {'signup_token': token, 'signup_expiration': expiration}, context=context) - return token + for partner in self.browse(cr, uid, ids, context): + if expiration or not partner.signup_valid: + token = random_token() + while self._signup_retrieve_partner(cr, uid, token, context=context): + token = random_token() + partner.write({'signup_token': token, 'signup_expiration': expiration}) + return True def _signup_retrieve_partner(self, cr, uid, token, raise_exception=False, context=None): """ find the partner corresponding to a token, and check its validity @@ -79,7 +96,7 @@ class res_partner(osv.Model): raise Exception("Signup token '%s' is not valid" % token) return False partner = self.browse(cr, uid, partner_ids[0], context) - if partner.signup_expiration and partner.signup_expiration < now(): + if not partner.signup_valid: if raise_exception: raise Exception("Signup token '%s' is no longer valid" % token) return False From a6ef1d4a428674b71710c84590dcccc1b89f82b4 Mon Sep 17 00:00:00 2001 From: "Quentin (OpenERP)" Date: Fri, 28 Sep 2012 11:50:35 +0200 Subject: [PATCH 016/229] [IMP] mrp: cleaning of stock move name bzr revid: qdp-launchpad@openerp.com-20120928095035-w1g06od25auo52ev --- addons/mrp/mrp.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/addons/mrp/mrp.py b/addons/mrp/mrp.py index 049c7c712fb..c14b852d08e 100644 --- a/addons/mrp/mrp.py +++ b/addons/mrp/mrp.py @@ -902,12 +902,11 @@ class mrp_production(osv.osv): # Internal shipment is created for Stockable and Consumer Products if production_line.product_id.type not in ('product', 'consu'): return False - move_name = _('PROD: %s') % production.name source_location_id = production.location_src_id.id if not destination_location_id: destination_location_id = source_location_id return stock_move.create(cr, uid, { - 'name': move_name, + 'name': production.name, 'picking_id': shipment_id, 'product_id': production_line.product_id.id, 'product_qty': production_line.product_qty, @@ -957,9 +956,8 @@ class mrp_production(osv.osv): stock_move = self.pool.get('stock.move') source_location_id = production.product_id.product_tmpl_id.property_stock_production.id destination_location_id = production.location_dest_id.id - move_name = _('PROD: %s') + production.name data = { - 'name': move_name, + 'name': production.name, 'date': production.date_planned, 'product_id': production.product_id.id, 'product_qty': production.product_qty, @@ -982,12 +980,11 @@ class mrp_production(osv.osv): # Internal shipment is created for Stockable and Consumer Products if production_line.product_id.type not in ('product', 'consu'): return False - move_name = _('PROD: %s') % production.name destination_location_id = production.product_id.product_tmpl_id.property_stock_production.id if not source_location_id: source_location_id = production.location_src_id.id move_id = stock_move.create(cr, uid, { - 'name': move_name, + 'name': production.name, 'date': production.date_planned, 'product_id': production_line.product_id.id, 'product_qty': production_line.product_qty, From 20bf037ea40d38f600a7166ea948b81840c613a0 Mon Sep 17 00:00:00 2001 From: "Quentin (OpenERP)" Date: Fri, 28 Sep 2012 11:54:58 +0200 Subject: [PATCH 017/229] [IMP] sale: added a test to verify the the product onchange in sale order line was correctly triggered bzr revid: qdp-launchpad@openerp.com-20120928095458-vjwd4xt0nun8lfjx --- addons/sale/test/sale_order_demo.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/addons/sale/test/sale_order_demo.yml b/addons/sale/test/sale_order_demo.yml index 05109efbea2..5b1c08bb5e1 100644 --- a/addons/sale/test/sale_order_demo.yml +++ b/addons/sale/test/sale_order_demo.yml @@ -7,8 +7,12 @@ payment_term: account.account_payment_term - !record {model: sale.order.line, id: line}: - name : 'LCD Monitor' order_id: sale_order_test1 product_id: product.product_product_7 - price_unit: 190.50 product_uom_qty: 8 +- + I verify that the onchange was correctly triggered +- + !assert {model: sale.order.line, id: sale.line, string: The onchange function of product was not correctly triggered}: + - l.name == '17” LCD Monitor' + - l.price_unit == 1350.0 From 6251b57b39ac2a01d907f1f5f6fccf13a0319830 Mon Sep 17 00:00:00 2001 From: "Quentin (OpenERP)" Date: Fri, 28 Sep 2012 12:15:31 +0200 Subject: [PATCH 018/229] [IMP] sale_stock: added a test to verify the the product onchange in sale order line was correctly triggered bzr revid: qdp-launchpad@openerp.com-20120928101531-sw1r2d5tc034ie23 --- addons/sale_stock/__openerp__.py | 1 + addons/sale_stock/sale_stock.py | 2 +- addons/sale_stock/sale_stock_view.xml | 6 ++--- .../sale_stock/test/sale_order_onchange.yml | 24 +++++++++++++++++++ 4 files changed, 29 insertions(+), 4 deletions(-) create mode 100644 addons/sale_stock/test/sale_order_onchange.yml diff --git a/addons/sale_stock/__openerp__.py b/addons/sale_stock/__openerp__.py index 2a670000581..10773b44a3f 100644 --- a/addons/sale_stock/__openerp__.py +++ b/addons/sale_stock/__openerp__.py @@ -61,6 +61,7 @@ You can choose flexible invoicing methods: 'test': ['test/cancel_order_sale_stock.yml', 'test/picking_order_policy.yml', 'test/prepaid_order_policy.yml', + 'test/sale_order_onchange.yml', ], 'installable': True, 'auto_install': True, diff --git a/addons/sale_stock/sale_stock.py b/addons/sale_stock/sale_stock.py index a48b766f3bb..aea39590152 100644 --- a/addons/sale_stock/sale_stock.py +++ b/addons/sale_stock/sale_stock.py @@ -625,7 +625,7 @@ class sale_order_line(osv.osv): uom = False if not uom2: uom2 = product_obj.uom_id - + compare_qty = float_compare(product_obj.virtual_available * uom2.factor, qty * product_obj.uom_id.factor, precision_rounding=product_obj.uom_id.rounding) if (product_obj.type=='product') and int(compare_qty) == -1 \ and (product_obj.procure_method=='make_to_stock'): diff --git a/addons/sale_stock/sale_stock_view.xml b/addons/sale_stock/sale_stock_view.xml index 7efb2849a52..09a8cd51af8 100644 --- a/addons/sale_stock/sale_stock_view.xml +++ b/addons/sale_stock/sale_stock_view.xml @@ -1,7 +1,7 @@ - + sale.shop.inherit.form sale.shop @@ -13,7 +13,7 @@
    - + sale.shop.sale.stock sale.shop @@ -41,7 +41,7 @@ -
  • - +
  • Reset password
  • + From 5763e40506f9f155e09b2fccbc7ffa4247b02b9b Mon Sep 17 00:00:00 2001 From: Christophe Simonis Date: Mon, 1 Oct 2012 17:14:21 +0200 Subject: [PATCH 039/229] [IMP] orm: remove trailing spaces bzr revid: chs@openerp.com-20121001151421-vdfkzuf864rz8q20 --- openerp/osv/orm.py | 60 +++++++++++++++++++++++----------------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/openerp/osv/orm.py b/openerp/osv/orm.py index 47afda39e5e..8b78621db03 100644 --- a/openerp/osv/orm.py +++ b/openerp/osv/orm.py @@ -372,7 +372,7 @@ class browse_record(object): else: return attr else: - error_msg = "Field '%s' does not exist in object '%s'" % (name, self) + error_msg = "Field '%s' does not exist in object '%s'" % (name, self) self.__logger.warning(error_msg) if self.__logger.isEnabledFor(logging.DEBUG): self.__logger.debug(''.join(traceback.format_stack())) @@ -712,7 +712,7 @@ class BaseModel(object): def log(self, cr, uid, id, message, secondary=False, context=None): return _logger.warning("log() is deprecated. Please use OpenChatter notification system instead of the res.log mechanism.") - + def view_init(self, cr, uid, fields_list, context=None): """Override this method to do specific things when a view on the object is opened.""" pass @@ -902,7 +902,7 @@ class BaseModel(object): # If new class defines a constraint with # same function name, we let it override # the old one. - + new[c2] = c exist = True break @@ -1596,7 +1596,7 @@ class BaseModel(object): def user_has_groups(self, cr, uid, groups, context=None): """Return true if the user is at least member of one of the groups in groups_str. Typically used to resolve ``groups`` attribute - in view and model definitions. + in view and model definitions. :param str groups: comma-separated list of fully-qualified group external IDs, e.g.: ``base.group_user,base.group_system`` @@ -1639,7 +1639,7 @@ class BaseModel(object): the field should be completely removed from the view, as it is completely unavailable for non-members - :return: True if field should be included in the result of fields_view_get + :return: True if field should be included in the result of fields_view_get """ if node.tag == 'field' and node.get('name') in self._all_columns: column = self._all_columns[node.get('name')].column @@ -1914,7 +1914,7 @@ class BaseModel(object): def _get_default_calendar_view(self, cr, user, context=None): """ Generates a default calendar view by trying to infer calendar fields from a number of pre-set attribute names - + :param cr: database cursor :param int user: user id :param dict context: connection context @@ -1993,7 +1993,7 @@ class BaseModel(object): def raise_view_error(error_msg, child_view_id): view, child_view = self.pool.get('ir.ui.view').browse(cr, user, [view_id, child_view_id], context) - error_msg = error_msg % {'parent_xml_id': view.xml_id} + error_msg = error_msg % {'parent_xml_id': view.xml_id} raise AttributeError("View definition error for inherited view '%s' on model '%s': %s" % (child_view.xml_id, self._name, error_msg)) @@ -2029,7 +2029,7 @@ class BaseModel(object): if all(node.get(attr) == spec.get(attr) \ for attr in spec.attrib if attr not in ('position','version')): - # Version spec should match parent's root element's version + # Version spec should match parent's root element's version if spec.get('version') and spec.get('version') != source.get('version'): return None return node @@ -2103,7 +2103,7 @@ class BaseModel(object): raise_view_error("Mismatching view API version for element '%s': %r vs %r in parent view '%%(parent_xml_id)s'" % \ (tag, spec.get('version'), source.get('version')), inherit_id) raise_view_error("Element '%s' not found in parent view '%%(parent_xml_id)s'" % tag, inherit_id) - + return source def apply_view_inheritance(cr, user, source, inherit_id): @@ -2318,7 +2318,7 @@ class BaseModel(object): or ``'='``. :param int limit: optional max number of records to return :rtype: list - :return: list of pairs ``(id,text_repr)`` for all matching records. + :return: list of pairs ``(id,text_repr)`` for all matching records. """ return self._name_search(cr, user, name, args, operator, context, limit) @@ -2448,7 +2448,7 @@ class BaseModel(object): # This is useful to implement kanban views for instance, where all columns # should be displayed even if they don't contain any record. - # Grab the list of all groups that should be displayed, including all present groups + # Grab the list of all groups that should be displayed, including all present groups present_group_ids = [x[groupby][0] for x in read_group_result if x[groupby]] all_groups,folded = self._group_by_full[groupby](self, cr, uid, present_group_ids, domain, read_group_order=read_group_order, @@ -2824,28 +2824,28 @@ class BaseModel(object): def _m2o_fix_foreign_key(self, cr, source_table, source_field, dest_model, ondelete): # Find FK constraint(s) currently established for the m2o field, - # and see whether they are stale or not + # and see whether they are stale or not cr.execute("""SELECT confdeltype as ondelete_rule, conname as constraint_name, cl2.relname as foreign_table FROM pg_constraint as con, pg_class as cl1, pg_class as cl2, pg_attribute as att1, pg_attribute as att2 - WHERE con.conrelid = cl1.oid - AND cl1.relname = %s - AND con.confrelid = cl2.oid - AND array_lower(con.conkey, 1) = 1 - AND con.conkey[1] = att1.attnum - AND att1.attrelid = cl1.oid - AND att1.attname = %s - AND array_lower(con.confkey, 1) = 1 - AND con.confkey[1] = att2.attnum - AND att2.attrelid = cl2.oid - AND att2.attname = %s + WHERE con.conrelid = cl1.oid + AND cl1.relname = %s + AND con.confrelid = cl2.oid + AND array_lower(con.conkey, 1) = 1 + AND con.conkey[1] = att1.attnum + AND att1.attrelid = cl1.oid + AND att1.attname = %s + AND array_lower(con.confkey, 1) = 1 + AND con.confkey[1] = att2.attnum + AND att2.attrelid = cl2.oid + AND att2.attname = %s AND con.contype = 'f'""", (source_table, source_field, 'id')) constraints = cr.dictfetchall() if constraints: if len(constraints) == 1: # Is it the right constraint? - cons, = constraints + cons, = constraints if cons['ondelete_rule'] != POSTGRES_CONFDELTYPES.get((ondelete or 'set null').upper(), 'a')\ or cons['foreign_table'] != dest_model._table: _schema.debug("Table '%s': dropping obsolete FK constraint: '%s'", @@ -3218,7 +3218,7 @@ class BaseModel(object): def _o2m_raise_on_missing_reference(self, cr, f): # TODO this check should be a method on fields.one2many. - + other = self.pool.get(f._obj) if other: # TODO the condition could use fields_get_keys(). @@ -3735,7 +3735,7 @@ class BaseModel(object): the length of `ids`, and raise an appropriate exception if it does not. """ if cr.rowcount != len(ids): - # 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 cr.execute('SELECT id FROM ' + self._table + ' WHERE id IN %s', (tuple(ids),)) if cr.rowcount != len(ids): @@ -3790,13 +3790,13 @@ class BaseModel(object): self._check_record_rules_result_count(cr, uid, sub_ids, operation, context=context) def _workflow_trigger(self, cr, uid, ids, trigger, context=None): - """Call given workflow trigger as a result of a CRUD operation""" + """Call given workflow trigger as a result of a CRUD operation""" wf_service = netsvc.LocalService("workflow") for res_id in ids: getattr(wf_service, trigger)(uid, self._name, res_id, cr) def _workflow_signal(self, cr, uid, ids, signal, context=None): - """Send given workflow signal""" + """Send given workflow signal""" wf_service = netsvc.LocalService("workflow") for res_id in ids: wf_service.trg_validate(uid, self._name, res_id, signal, cr) @@ -3827,7 +3827,7 @@ class BaseModel(object): self.check_access_rights(cr, uid, 'unlink') ir_property = self.pool.get('ir.property') - + # Check if the records are used as default properties. domain = [('res_id', '=', False), ('value_reference', 'in', ['%s,%s' % (self._name, i) for i in ids]), @@ -4955,7 +4955,7 @@ class BaseModel(object): :return: map of ids to their fully qualified XML ID, defaulting to an empty string when there's none - (to be usable as a function field), + (to be usable as a function field), e.g.:: { 'id': 'module.ext_id', From 9d8ea41d450f738afe77c47b1f86c212e3132e33 Mon Sep 17 00:00:00 2001 From: niv-openerp Date: Mon, 1 Oct 2012 17:19:17 +0200 Subject: [PATCH 040/229] wip bzr revid: nicolas.vanhoren@openerp.com-20121001151917-o095dj2m8aypm3da --- addons/hr_timesheet_sheet/hr_timesheet_sheet_view.xml | 1 - addons/hr_timesheet_sheet/static/src/js/timesheet.js | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/addons/hr_timesheet_sheet/hr_timesheet_sheet_view.xml b/addons/hr_timesheet_sheet/hr_timesheet_sheet_view.xml index 6ff3e5c43e0..78fc997d15e 100644 --- a/addons/hr_timesheet_sheet/hr_timesheet_sheet_view.xml +++ b/addons/hr_timesheet_sheet/hr_timesheet_sheet_view.xml @@ -73,7 +73,6 @@ - This is a test. diff --git a/addons/hr_timesheet_sheet/static/src/js/timesheet.js b/addons/hr_timesheet_sheet/static/src/js/timesheet.js index 1cde7a80a90..9b186443217 100644 --- a/addons/hr_timesheet_sheet/static/src/js/timesheet.js +++ b/addons/hr_timesheet_sheet/static/src/js/timesheet.js @@ -21,7 +21,7 @@ openerp.hr_timesheet_sheet = function(instance) { this.on("change:sheets", this, this.update_sheets); this.res_o2m_drop = new instance.web.DropMisordered(); this.render_drop = new instance.web.DropMisordered(); - this.description_line = _t("No description"); + this.description_line = _t("/"); }, query_sheets: function() { var self = this; From b7e5519b4c0b4c212ef1268a09dacdfb26b704d8 Mon Sep 17 00:00:00 2001 From: Christophe Simonis Date: Mon, 1 Oct 2012 17:37:53 +0200 Subject: [PATCH 041/229] [FIX] orm: handle fields.char() with not size correctly bzr revid: chs@openerp.com-20121001153753-um68vpz1caojm17r --- openerp/osv/orm.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/openerp/osv/orm.py b/openerp/osv/orm.py index 8b78621db03..ec995c6a8cc 100644 --- a/openerp/osv/orm.py +++ b/openerp/osv/orm.py @@ -2967,14 +2967,14 @@ class BaseModel(object): ('numeric', 'float', get_pg_type(f)[1], '::'+get_pg_type(f)[1]), ('float8', 'float', get_pg_type(f)[1], '::'+get_pg_type(f)[1]), ] - if f_pg_type == 'varchar' and f._type == 'char' and f_pg_size < f.size: + if f_pg_type == 'varchar' and f._type == 'char' and ((f.size is None and f_pg_size) or f_pg_size < f.size): cr.execute('ALTER TABLE "%s" RENAME COLUMN "%s" TO temp_change_size' % (self._table, k)) cr.execute('ALTER TABLE "%s" ADD COLUMN "%s" %s' % (self._table, k, pg_varchar(f.size))) cr.execute('UPDATE "%s" SET "%s"=temp_change_size::%s' % (self._table, k, pg_varchar(f.size))) cr.execute('ALTER TABLE "%s" DROP COLUMN temp_change_size CASCADE' % (self._table,)) cr.commit() _schema.debug("Table '%s': column '%s' (type varchar) changed size from %s to %s", - self._table, k, f_pg_size, f.size) + self._table, k, f_pg_size or 'unlimited', f.size or 'unlimited') for c in casts: if (f_pg_type==c[0]) and (f._type==c[1]): if f_pg_type != f_obj_type: @@ -3207,8 +3207,7 @@ class BaseModel(object): # attlen is the number of bytes necessary to represent the type when # the type has a fixed size. If the type has a varying size attlen is # -1 and atttypmod is the size limit + 4, or -1 if there is no limit. - # Thus the query can return a negative size for a unlimited varchar. - cr.execute("SELECT c.relname,a.attname,a.attlen,a.atttypmod,a.attnotnull,a.atthasdef,t.typname,CASE WHEN a.attlen=-1 THEN a.atttypmod-4 ELSE a.attlen END as size " \ + cr.execute("SELECT c.relname,a.attname,a.attlen,a.atttypmod,a.attnotnull,a.atthasdef,t.typname,CASE WHEN a.attlen=-1 THEN (CASE WHEN a.atttypmod=-1 THEN 0 ELSE a.atttypmod-4 END) ELSE a.attlen END as size " \ "FROM pg_class c,pg_attribute a,pg_type t " \ "WHERE c.relname=%s " \ "AND c.oid=a.attrelid " \ From 7812c73354d9ca924c109b3c6331bfbe1986f388 Mon Sep 17 00:00:00 2001 From: "Quentin (OpenERP)" Date: Mon, 1 Oct 2012 17:38:06 +0200 Subject: [PATCH 042/229] [IMP] yaml processing: improved previous patch by replacing the old parameter view with the result of fields_view_get() directly, to avoid having an extra parameter a bit useless bzr revid: qdp-launchpad@openerp.com-20121001153806-k40ly5vdzh1v3hv3 --- openerp/tools/yaml_import.py | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/openerp/tools/yaml_import.py b/openerp/tools/yaml_import.py index f88f253c0f2..a5de5873908 100644 --- a/openerp/tools/yaml_import.py +++ b/openerp/tools/yaml_import.py @@ -313,14 +313,13 @@ class YamlInterpreter(object): #context = self.get_context(record, self.eval_context) #TOFIX: record.context like {'withoutemployee':True} should pass from self.eval_context. example: test_project.yml in project module context = record.context - fields_view_get_result = False + view_info = False if view_id: varg = view_id if view_id is True: varg = False - fields_view_get_result = model.fields_view_get(self.cr, SUPERUSER_ID, varg, 'form', context) - view_id = etree.fromstring(fields_view_get_result['arch'].encode('utf-8')) + view_info = model.fields_view_get(self.cr, SUPERUSER_ID, varg, 'form', context) - record_dict = self._create_record(model, fields, view_id, default=default, fields_view_get=fields_view_get_result) + record_dict = self._create_record(model, fields, view_info, default=default) _logger.debug("RECORD_DICT %s" % record_dict) id = self.pool.get('ir.model.data')._update(self.cr, SUPERUSER_ID, record.model, \ self.module, record_dict, record.id, noupdate=self.isnoupdate(record), mode=self.mode, context=context) @@ -328,7 +327,8 @@ class YamlInterpreter(object): if config.get('import_partial'): self.cr.commit() - def _create_record(self, model, fields, view=False, parent={}, default=True, fields_view_get=None): + def _create_record(self, model, fields, view_info=False, parent={}, default=True): + view = view_info and etree.fromstring(view_info['arch'].encode('utf-8')) or False if view is not False: defaults = default and model._add_missing_default_values(self.cr, SUPERUSER_ID, {}, context=self.context) or {} fg = model.fields_get(self.cr, SUPERUSER_ID, context=self.context) @@ -357,17 +357,15 @@ class YamlInterpreter(object): field_name = el.attrib['name'] assert field_name in fg, "The field '%s' is defined in the form view but not on the object '%s'!" % (field_name, model._name) if field_name in fields: - view2 = None + one2many_form_view = None # if the form view is not inline, we call fields_view_get if (view is not False) and (fg[field_name]['type']=='one2many'): - tmp = fields_view_get['fields'][field_name]['views'] - if tmp.has_key('form'): - view2 = tmp.get('form') - if not view2: - view2 = self.pool.get(fg[field_name]['relation']).fields_view_get(self.cr, SUPERUSER_ID, False, 'form', self.context) - view2 = etree.fromstring(view2['arch'].encode('utf-8')) + one2many_form_view = view_info['fields'][field_name]['views'].get('form') + if not one2many_form_view: + one2many_form_view = self.pool.get(fg[field_name]['relation']).fields_view_get(self.cr, SUPERUSER_ID, False, 'form', self.context) - field_value = self._eval_field(model, field_name, fields[field_name], view2, parent=record_dict, default=default, fields_view_get=fields_view_get) + view_info = one2many_form_view or view_info + field_value = self._eval_field(model, field_name, fields[field_name], view_info, parent=record_dict, default=default) record_dict[field_name] = field_value #if (field_name in defaults) and defaults[field_name] == field_value: # print '*** You can remove these lines:', field_name, field_value @@ -412,7 +410,7 @@ class YamlInterpreter(object): for field_name, expression in fields.items(): if field_name in record_dict: continue - field_value = self._eval_field(model, field_name, expression, default=False, fields_view_get=fields_view_get) + field_value = self._eval_field(model, field_name, expression, default=False) record_dict[field_name] = field_value return record_dict @@ -443,7 +441,7 @@ class YamlInterpreter(object): def process_eval(self, node): return eval(node.expression, self.eval_context) - def _eval_field(self, model, field_name, expression, view=False, parent={}, default=True, fields_view_get=None): + def _eval_field(self, model, field_name, expression, view_info=False, parent={}, default=True): # TODO this should be refactored as something like model.get_field() in bin/osv if field_name in model._columns: column = model._columns[field_name] @@ -464,7 +462,7 @@ class YamlInterpreter(object): value = self.get_id(expression) elif column._type == "one2many": other_model = self.get_model(column._obj) - value = [(0, 0, self._create_record(other_model, fields, view, parent, default=default, fields_view_get=fields_view_get)) for fields in expression] + value = [(0, 0, self._create_record(other_model, fields, view_info, parent, default=default)) for fields in expression] elif column._type == "many2many": ids = [self.get_id(xml_id) for xml_id in expression] value = [(6, 0, ids)] From 7c1339928b1e69a17e84c66bd366144dec6fed8e Mon Sep 17 00:00:00 2001 From: niv-openerp Date: Mon, 1 Oct 2012 17:46:05 +0200 Subject: [PATCH 043/229] Removed day by day bzr revid: nicolas.vanhoren@openerp.com-20121001154605-nemay3pze49ejjld --- .../hr_timesheet_sheet/hr_timesheet_sheet.py | 157 +----------------- .../hr_timesheet_sheet_view.xml | 17 +- 2 files changed, 8 insertions(+), 166 deletions(-) diff --git a/addons/hr_timesheet_sheet/hr_timesheet_sheet.py b/addons/hr_timesheet_sheet/hr_timesheet_sheet.py index 13c11cf72c7..1c29a4e736f 100644 --- a/addons/hr_timesheet_sheet/hr_timesheet_sheet.py +++ b/addons/hr_timesheet_sheet/hr_timesheet_sheet.py @@ -27,80 +27,6 @@ from osv import fields, osv from tools.translate import _ import netsvc -class one2many_mod2(fields.one2many): - def get(self, cr, obj, ids, name, user=None, offset=0, context=None, values=None): - if context is None: - context = {} - - if values is None: - values = {} - - # res6 = {id: date_current, ...} - res6 = dict([(rec['id'], rec['date_current']) - for rec in obj.read(cr, user, ids, ['date_current'], context=context)]) - - dom = [] - for c, id in enumerate(ids): - if id in res6: - if c: # skip first - dom.insert(0 ,'|') - dom.append('&') - dom.append('&') - dom.append(('name', '>=', res6[id])) - dom.append(('name', '<=', res6[id])) - dom.append(('sheet_id', '=', id)) - - ids2 = obj.pool.get(self._obj).search(cr, user, dom, limit=self._limit) - - res = {} - for i in ids: - res[i] = [] - - for r in obj.pool.get(self._obj)._read_flat(cr, user, ids2, [self._fields_id], context=context, load='_classic_read'): - if r[self._fields_id]: - res[r[self._fields_id][0]].append(r['id']) - return res - - def set(self, cr, obj, id, field, values, user=None, context=None): - if context is None: - context = {} - - context = context.copy() - context['sheet_id'] = id - return super(one2many_mod2, self).set(cr, obj, id, field, values, user=user, context=context) - - -class one2many_mod(fields.one2many): - def get(self, cr, obj, ids, name, user=None, offset=0, context=None, values=None): - if context is None: - context = {} - - if values is None: - values = {} - - - res5 = obj.read(cr, user, ids, ['date_current'], context=context) - res6 = {} - for r in res5: - res6[r['id']] = r['date_current'] - - ids2 = [] - for id in ids: - dom = [] - if id in res6: - dom = [('date', '=', res6[id]), ('sheet_id', '=', id)] - ids2.extend(obj.pool.get(self._obj).search(cr, user, - dom, limit=self._limit)) - res = {} - for i in ids: - res[i] = [] - for r in obj.pool.get(self._obj)._read_flat(cr, user, ids2, - [self._fields_id], context=context, load='_classic_read'): - if r[self._fields_id]: - res[r[self._fields_id][0]].append(r['id']) - - return res - class hr_timesheet_sheet(osv.osv): _name = "hr_timesheet_sheet.sheet" _inherit = "mail.thread" @@ -111,8 +37,7 @@ class hr_timesheet_sheet(osv.osv): def _total_attendances(self, cr, uid, ids, name, args, context=None): """ Get the total attendance for the timesheets Returns a dict like : - {id: {'date_current': '2011-06-17', - 'total_per_day': {day: timedelta, ...}, + {id: {'total_per_day': {day: timedelta, ...}, }, ... } @@ -122,7 +47,6 @@ class hr_timesheet_sheet(osv.osv): res = {} for sheet_id in ids: sheet = self.browse(cr, uid, sheet_id, context=context) - date_current = sheet.date_current # field attendances_ids of hr_timesheet_sheet.sheet only # returns attendances of timesheet's current date attendance_ids = attendance_obj.search(cr, uid, [('sheet_id', '=', sheet_id)], context=context) @@ -143,20 +67,7 @@ class hr_timesheet_sheet(osv.osv): else: total_attendance[day] += attendance_interval - # if the delta is negative, it means that a sign out is missing - # in a such case, we want to have the time to the end of the day - # for a past date, and the time to now for the current date - if total_attendance[day] < timedelta(0): - if day == date_current: - now = datetime.now() - total_attendance[day] += timedelta(hours=now.hour, - minutes=now.minute, - seconds=now.second) - else: - total_attendance[day] += timedelta(days=1) - - res[sheet_id] = {'date_current': date_current, - 'total_per_day': total_attendance} + res[sheet_id] = {'total_per_day': total_attendance} return res def _total_timesheet(self, cr, uid, ids, name, args, context=None): @@ -210,24 +121,16 @@ class hr_timesheet_sheet(osv.osv): all_attendances_sheet = all_timesheet_attendances[id] - date_current = all_attendances_sheet['date_current'] total_attendances_sheet = all_attendances_sheet['total_per_day'] total_attendances_all_days = sum_all_days(total_attendances_sheet) - total_attendances_day = total_attendances_sheet.get(date_current, timedelta(seconds=0)) total_timesheets_sheet = all_timesheet_lines[id] total_timesheets_all_days = sum_all_days(total_timesheets_sheet) - total_timesheets_day = total_timesheets_sheet.get(date_current, timedelta(seconds=0)) total_difference_all_days = total_attendances_all_days - total_timesheets_all_days - total_difference_day = total_attendances_day - total_timesheets_day res[id]['total_attendance'] = timedelta_to_hours(total_attendances_all_days) res[id]['total_timesheet'] = timedelta_to_hours(total_timesheets_all_days) res[id]['total_difference'] = timedelta_to_hours(total_difference_all_days) - - res[id]['total_attendance_day'] = timedelta_to_hours(total_attendances_day) - res[id]['total_timesheet_day'] = timedelta_to_hours(total_timesheets_day) - res[id]['total_difference_day'] = timedelta_to_hours(total_difference_day) return res def check_employee_attendance_state(self, cr, uid, sheet_id, context=None): @@ -277,44 +180,6 @@ class hr_timesheet_sheet(osv.osv): raise osv.except_osv(_('Warning!'), _('Please verify that the total difference of the sheet is lower than %.2f.') %(di,)) return True - def date_today(self, cr, uid, ids, context=None): - for sheet in self.browse(cr, uid, ids, context=context): - if datetime.today() <= datetime.strptime(sheet.date_from, '%Y-%m-%d'): - self.write(cr, uid, [sheet.id], {'date_current': sheet.date_from,}, context=context) - elif datetime.now() >= datetime.strptime(sheet.date_to, '%Y-%m-%d'): - self.write(cr, uid, [sheet.id], {'date_current': sheet.date_to,}, context=context) - else: - self.write(cr, uid, [sheet.id], {'date_current': time.strftime('%Y-%m-%d')}, context=context) - return True - - def date_previous(self, cr, uid, ids, context=None): - for sheet in self.browse(cr, uid, ids, context=context): - if datetime.strptime(sheet.date_current, '%Y-%m-%d') <= datetime.strptime(sheet.date_from, '%Y-%m-%d'): - self.write(cr, uid, [sheet.id], {'date_current': sheet.date_from,}, context=context) - else: - self.write(cr, uid, [sheet.id], { - 'date_current': (datetime.strptime(sheet.date_current, '%Y-%m-%d') + relativedelta(days=-1)).strftime('%Y-%m-%d'), - }, context=context) - return True - - def date_next(self, cr, uid, ids, context=None): - for sheet in self.browse(cr, uid, ids, context=context): - if datetime.strptime(sheet.date_current, '%Y-%m-%d') >= datetime.strptime(sheet.date_to, '%Y-%m-%d'): - self.write(cr, uid, [sheet.id], {'date_current': sheet.date_to,}, context=context) - else: - self.write(cr, uid, [sheet.id], { - 'date_current': (datetime.strptime(sheet.date_current, '%Y-%m-%d') + relativedelta(days=1)).strftime('%Y-%m-%d'), - }, context=context) - return True - - def button_dummy(self, cr, uid, ids, context=None): - for sheet in self.browse(cr, uid, ids, context=context): - if datetime.strptime(sheet.date_current, '%Y-%m-%d') <= datetime.strptime(sheet.date_from, '%Y-%m-%d'): - self.write(cr, uid, [sheet.id], {'date_current': sheet.date_from,}, context=context) - elif datetime.strptime(sheet.date_current, '%Y-%m-%d') >= datetime.strptime(sheet.date_to, '%Y-%m-%d'): - self.write(cr, uid, [sheet.id], {'date_current': sheet.date_to,}, context=context) - return True - def attendance_action_change(self, cr, uid, ids, context=None): hr_employee = self.pool.get('hr.employee') employee_ids = [] @@ -329,14 +194,13 @@ class hr_timesheet_sheet(osv.osv): 'user_id': fields.related('employee_id', 'user_id', type="many2one", relation="res.users", store=True, string="User", required=False, readonly=True),#fields.many2one('res.users', 'User', required=True, select=1, states={'confirm':[('readonly', True)], 'done':[('readonly', True)]}), 'date_from': fields.date('Date from', required=True, select=1, readonly=True, states={'new':[('readonly', False)]}), 'date_to': fields.date('Date to', required=True, select=1, readonly=True, states={'new':[('readonly', False)]}), - 'date_current': fields.date('Current date', required=True, select=1), - 'timesheet_ids' : one2many_mod('hr.analytic.timesheet', 'sheet_id', - 'Timesheet lines', domain=[('date', '=', time.strftime('%Y-%m-%d'))], + 'timesheet_ids' : fields.one2many('hr.analytic.timesheet', 'sheet_id', + 'Timesheet lines', readonly=True, states={ 'draft': [('readonly', False)], 'new': [('readonly', False)]} ), - 'attendances_ids' : one2many_mod2('hr.attendance', 'sheet_id', 'Attendances'), + 'attendances_ids' : fields.one2many('hr.attendance', 'sheet_id', 'Attendances'), 'state' : fields.selection([ ('new', 'New'), ('draft','Open'), @@ -346,9 +210,6 @@ class hr_timesheet_sheet(osv.osv): \n* The \'Confirmed\' state is used for to confirm the timesheet by user. \ \n* The \'Done\' state is used when users timesheet is accepted by his/her senior.'), 'state_attendance' : fields.related('employee_id', 'state', type='selection', selection=[('absent', 'Absent'), ('present', 'Present')], string='Current Status', readonly=True), - 'total_attendance_day': fields.function(_total, method=True, string='Total Attendance', multi="_total"), - 'total_timesheet_day': fields.function(_total, method=True, string='Total Timesheet', multi="_total"), - 'total_difference_day': fields.function(_total, method=True, string='Difference', multi="_total"), 'total_attendance': fields.function(_total, method=True, string='Total Attendance', multi="_total"), 'total_timesheet': fields.function(_total, method=True, string='Total Timesheet', multi="_total"), 'total_difference': fields.function(_total, method=True, string='Difference', multi="_total"), @@ -386,7 +247,6 @@ class hr_timesheet_sheet(osv.osv): _defaults = { 'date_from' : _default_date_from, - 'date_current' : lambda *a: time.strftime('%Y-%m-%d'), 'date_to' : _default_date_to, 'state': 'new', 'employee_id': _default_employee, @@ -406,16 +266,9 @@ class hr_timesheet_sheet(osv.osv): return False return True - def _date_current_check(self, cr, uid, ids, context=None): - for sheet in self.browse(cr, uid, ids, context=context): - if sheet.date_current < sheet.date_from or sheet.date_current > sheet.date_to: - return False - return True - _constraints = [ (_sheet_date, 'You cannot have 2 timesheets that overlaps !\nPlease use the menu \'My Current Timesheet\' to avoid this problem.', ['date_from','date_to']), - (_date_current_check, 'You must select a Current date which is in the timesheet dates !', ['date_current']), ] def action_set_to_draft(self, cr, uid, ids, *args): diff --git a/addons/hr_timesheet_sheet/hr_timesheet_sheet_view.xml b/addons/hr_timesheet_sheet/hr_timesheet_sheet_view.xml index 78fc997d15e..7dd2b9a1b88 100644 --- a/addons/hr_timesheet_sheet/hr_timesheet_sheet_view.xml +++ b/addons/hr_timesheet_sheet/hr_timesheet_sheet_view.xml @@ -76,19 +76,8 @@ - -
    -
    -
    -
    -
    - + @@ -105,9 +94,9 @@ - + - + From 7d46e337633ff4265642d8a7bf9a8a9157a80c18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20van=20der=20Essen?= Date: Mon, 1 Oct 2012 17:49:16 +0200 Subject: [PATCH 044/229] [IMP] css: animations for kanban notes bzr revid: fva@openerp.com-20121001154916-r5mq1z4icc54jmgz --- addons/web_kanban/static/src/css/kanban.css | 9 ++++++--- addons/web_kanban/static/src/css/kanban.sass | 14 ++++++++++++-- addons/web_kanban/static/src/js/kanban.js | 6 ++++++ 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/addons/web_kanban/static/src/css/kanban.css b/addons/web_kanban/static/src/css/kanban.css index 6846171a82a..60e8eefcf37 100644 --- a/addons/web_kanban/static/src/css/kanban.css +++ b/addons/web_kanban/static/src/css/kanban.css @@ -9,7 +9,6 @@ .openerp .oe_kanban_view .ui-sortable-placeholder { border: 1px solid rgba(0, 0, 0, 0.1); visibility: visible !important; - height: 60px !important; } .openerp .oe_kanban_view .ui-sortable-helper { -moz-box-shadow: 0 1px 10px rgba(0, 0, 0, 0.3); @@ -19,6 +18,9 @@ -webkit-transform: rotate(3deg); -o-transform: rotate(3deg); -ms-transform: rotate(3deg); + -webkit-transition: -webkit-transform 100ms linear; + -moz-transition: -moz-transform 100ms linear; + transition: transform 100ms linear; } .openerp .oe_kanban_view .oe_kanban_left { float: left; @@ -364,17 +366,18 @@ border-bottom-color: rgba(0, 0, 0, 0.3); padding: 8px; display: block; + -webkit-transition: -webkit-transform, -webkit-box-shadow, border 200ms linear; -moz-border-radius: 4px; -webkit-border-radius: 4px; border-radius: 4px; } -.openerp .oe_kanban_view .oe_kanban_card:hover, .openerp .oe_kanban_view .oe_kanban_quick_create:hover { +.openerp .oe_kanban_view .oe_kanban_card:not(.ui-sortable-helper):hover, .openerp .oe_kanban_view .oe_kanban_quick_create:not(.ui-sortable-helper):hover { border: 1px solid #7c7bad; -moz-box-shadow: 0 0 4px #7c7bad; -webkit-box-shadow: 0 0 4px #7c7bad; -box-shadow: 0 0 4px #7c7bad; } -.openerp .oe_kanban_view .oe_kanban_card:hover .oe_dropdown_kanban > span, .openerp .oe_kanban_view .oe_kanban_quick_create:hover .oe_dropdown_kanban > span { +.openerp .oe_kanban_view .oe_kanban_card:not(.ui-sortable-helper):hover .oe_dropdown_kanban > span, .openerp .oe_kanban_view .oe_kanban_quick_create:not(.ui-sortable-helper):hover .oe_dropdown_kanban > span { visibility: visible; } .openerp .oe_kanban_view .oe_kanban_card h3, .openerp .oe_kanban_view .oe_kanban_quick_create h3 { diff --git a/addons/web_kanban/static/src/css/kanban.sass b/addons/web_kanban/static/src/css/kanban.sass index 88b60134f24..1210e9bf640 100644 --- a/addons/web_kanban/static/src/css/kanban.sass +++ b/addons/web_kanban/static/src/css/kanban.sass @@ -37,6 +37,13 @@ -webkit-transform: $transform -o-transform: $transform -ms-transform: $transform + +@mixin transition($transition: all 200ms linear) + -webkit-transition: $transition + -moz-transition: $transition + -ms-transition: $transition + -o-transition: $transition + transition: $transition // }}} .openerp .oe_kanban_view @@ -49,10 +56,12 @@ .ui-sortable-placeholder border: 1px solid rgba(0,0,0,0.1) visibility: visible !important - height: 60px !important .ui-sortable-helper @include box-shadow(0 1px 10px rgba(0, 0, 0, 0.3)) @include transform(rotate(3deg)) + -webkit-transition: -webkit-transform 100ms linear + -moz-transition: -moz-transform 100ms linear + transition: transform 100ms linear .oe_kanban_left float: left .oe_kanban_right @@ -328,8 +337,9 @@ border-bottom-color: rgba(0,0,0,0.3) padding: 8px display: block + -webkit-transition: -webkit-transform, -webkit-box-shadow, border 200ms linear @include radius(4px) - &:hover + &:not(.ui-sortable-helper):hover border: 1px solid #7C7BAD @include box-shadow(0 0 4px #7C7BAD) .oe_dropdown_kanban > span diff --git a/addons/web_kanban/static/src/js/kanban.js b/addons/web_kanban/static/src/js/kanban.js index bd36dcbf4ba..d8e93efa580 100644 --- a/addons/web_kanban/static/src/js/kanban.js +++ b/addons/web_kanban/static/src/js/kanban.js @@ -309,8 +309,14 @@ instance.web_kanban.KanbanView = instance.web.View.extend({ ui.item.find('*').on('click.prevent', function(ev) { return false; }); + ui.placeholder.height(ui.item.height()); + }, + revert: 150, + beforeStop: function(event,ui){ + console.log('beforeStop:',ui); }, stop: function(event, ui) { + console.log('stop:',ui); var record = ui.item.data('widget'); var old_index = self.currently_dragging.index; var new_index = ui.item.index(); From 2b07b4e35c5ee619eaf93a7bb4fb14a37e55fb0e Mon Sep 17 00:00:00 2001 From: "Quentin (OpenERP)" Date: Mon, 1 Oct 2012 18:08:54 +0200 Subject: [PATCH 045/229] [FIX] sale, sale_stock: yaml tests on product_id_change() + fix in that same function (type is defined in sale_stock now) bzr revid: qdp-launchpad@openerp.com-20121001160854-7d2xew5pol6u27b2 --- addons/sale/sale.py | 1 - addons/sale/sale_view.xml | 5 ++--- addons/sale/test/sale_order_demo.yml | 14 ++++++-------- addons/sale_stock/test/sale_order_onchange.yml | 14 ++++++-------- 4 files changed, 14 insertions(+), 20 deletions(-) diff --git a/addons/sale/sale.py b/addons/sale/sale.py index 96f44d353d6..f9dc3c62281 100644 --- a/addons/sale/sale.py +++ b/addons/sale/sale.py @@ -899,7 +899,6 @@ class sale_order_line(osv.osv): fpos = fiscal_position and self.pool.get('account.fiscal.position').browse(cr, uid, fiscal_position) or False if update_tax: #The quantity only have changed result['tax_id'] = self.pool.get('account.fiscal.position').map_tax(cr, uid, fpos, product_obj.taxes_id) - result.update({'type': product_obj.procure_method}) if not flag: result['name'] = self.pool.get('product.product').name_get(cr, uid, [product_obj.id], context=context_partner)[0][1] diff --git a/addons/sale/sale_view.xml b/addons/sale/sale_view.xml index 23d2c556f40..620991d8053 100644 --- a/addons/sale/sale_view.xml +++ b/addons/sale/sale_view.xml @@ -200,8 +200,7 @@ - From 5b910c668a6fc5cdc5a856ea2f8fad2df56d3a5c Mon Sep 17 00:00:00 2001 From: niv-openerp Date: Tue, 2 Oct 2012 14:50:55 +0200 Subject: [PATCH 059/229] added readonly mode bzr revid: nicolas.vanhoren@openerp.com-20121002125055-j01ea26yf9t3uwjg --- .../static/src/js/timesheet.js | 41 ++++++++++--------- .../static/src/xml/timesheet.xml | 5 ++- 2 files changed, 26 insertions(+), 20 deletions(-) diff --git a/addons/hr_timesheet_sheet/static/src/js/timesheet.js b/addons/hr_timesheet_sheet/static/src/js/timesheet.js index a21fbfbf540..d80c45162ae 100644 --- a/addons/hr_timesheet_sheet/static/src/js/timesheet.js +++ b/addons/hr_timesheet_sheet/static/src/js/timesheet.js @@ -3,7 +3,7 @@ openerp.hr_timesheet_sheet = function(instance) { var QWeb = instance.web.qweb; var _t = instance.web._t; - instance.hr_timesheet_sheet.WeeklyTimesheet = instance.web.form.FormWidget.extend({ + instance.hr_timesheet_sheet.WeeklyTimesheet = instance.web.form.FormWidget.extend(instance.web.form.ReinitializeWidgetMixin, { init: function() { this._super.apply(this, arguments); this.set({ @@ -50,13 +50,12 @@ openerp.hr_timesheet_sheet = function(instance) { }, start: function() { var self = this; - self.on("change:sheets", self, self.render); - self.on("change:date_to", self, self.render); - self.on("change:date_from", self, self.render); - self.on("change:user_id", self, self.render); - self.render(); + self.on("change:sheets", self, self.initialize_content); + self.on("change:date_to", self, self.initialize_content); + self.on("change:date_from", self, self.initialize_content); + self.on("change:user_id", self, self.initialize_content); }, - render: function() { + initialize_content: function() { var self = this; if (self.setting) return; @@ -139,27 +138,31 @@ openerp.hr_timesheet_sheet = function(instance) { self.$el.html(QWeb.render("hr_timesheet_sheet.WeeklyTimesheet", {widget: self})); _.each(self.accounts, function(account) { _.each(_.range(account.days.length), function(day_count) { - self.get_case(account, day_count).val(self.sum_case(account, day_count)).change(function() { - var num = Number($(this).val()); - if (isNaN(num)) { - $(this).val(self.sum_case(account, day_count)); - } else { - account.days[day_count].lines[0].unit_amount += num - self.sum_case(account, day_count); - self.get_total(account).html(self.sum_total(account)); - self.sync(); - } - }); + if (!self.get('effective_readonly')) { + self.get_box(account, day_count).val(self.sum_box(account, day_count)).change(function() { + var num = Number($(this).val()); + if (isNaN(num)) { + $(this).val(self.sum_box(account, day_count)); + } else { + account.days[day_count].lines[0].unit_amount += num - self.sum_box(account, day_count); + self.get_total(account).html(self.sum_total(account)); + self.sync(); + } + }); + } else { + self.get_box(account, day_count).html(self.sum_box(account, day_count)); + } }); self.get_total(account).html(self.sum_total(account)); }); }, - get_case: function(account, day_count) { + get_box: function(account, day_count) { return this.$('[data-account="' + account.account + '"][data-day-count="' + day_count + '"]'); }, get_total: function(account) { return this.$('[data-account-total="' + account.account + '"]'); }, - sum_case: function(account, day_count) { + sum_box: function(account, day_count) { var line_total = 0; _.each(account.days[day_count].lines, function(line) { line_total += line.unit_amount; diff --git a/addons/hr_timesheet_sheet/static/src/xml/timesheet.xml b/addons/hr_timesheet_sheet/static/src/xml/timesheet.xml index cb633543cec..4da95f59a11 100644 --- a/addons/hr_timesheet_sheet/static/src/xml/timesheet.xml +++ b/addons/hr_timesheet_sheet/static/src/xml/timesheet.xml @@ -16,7 +16,10 @@ - + + From 2d8535e473a99d1905e084aa07a5a21e3e67c697 Mon Sep 17 00:00:00 2001 From: niv-openerp Date: Tue, 2 Oct 2012 15:25:47 +0200 Subject: [PATCH 060/229] Added some styling bzr revid: nicolas.vanhoren@openerp.com-20121002132547-p7fi2ra8a0tgxpbn --- .../static/src/css/timesheet.css | 40 +++++++++++++++++++ .../static/src/js/timesheet.js | 3 +- .../static/src/xml/timesheet.xml | 4 +- 3 files changed, 44 insertions(+), 3 deletions(-) diff --git a/addons/hr_timesheet_sheet/static/src/css/timesheet.css b/addons/hr_timesheet_sheet/static/src/css/timesheet.css index e69de29bb2d..e609bb636b4 100644 --- a/addons/hr_timesheet_sheet/static/src/css/timesheet.css +++ b/addons/hr_timesheet_sheet/static/src/css/timesheet.css @@ -0,0 +1,40 @@ + +.oe_timesheet_weekly table { + width: 100%; +} + +.oe_timesheet_weekly th { + text-align: right; + color: #069; + font-family: 'Helvetica Neue', Arial, Verdana, 'Nimbus Sans L', sans-serif; + font-size: 10px; +} + +.oe_timesheet_weekly td { + text-align: right; + vertical-align: middle; +} + +.oe_timesheet_weekly .oe_timesheet_weekly_account { + text-align: left; +} + +.oe_timesheet_weekly td input { + border: 1px solid #CCC; + padding: 5px 2px !important; + color: #666 !important; + font-size: 14px; + font-weight: bold; + width: 38px; + text-align: right; + min-width: 0 !important; +} + +.oe_timesheet_weekly td .oe_timesheet_weekly_box { + padding: 5px 2px !important; + color: #666 !important; + font-size: 14px; + font-weight: bold; + width: 38px; + display: inline-block; +} \ No newline at end of file diff --git a/addons/hr_timesheet_sheet/static/src/js/timesheet.js b/addons/hr_timesheet_sheet/static/src/js/timesheet.js index d80c45162ae..16d42261585 100644 --- a/addons/hr_timesheet_sheet/static/src/js/timesheet.js +++ b/addons/hr_timesheet_sheet/static/src/js/timesheet.js @@ -48,7 +48,8 @@ openerp.hr_timesheet_sheet = function(instance) { self.updating = false; }); }, - start: function() { + initialize_field: function() { + instance.web.form.ReinitializeWidgetMixin.initialize_field.call(this); var self = this; self.on("change:sheets", self, self.initialize_content); self.on("change:date_to", self, self.initialize_content); diff --git a/addons/hr_timesheet_sheet/static/src/xml/timesheet.xml b/addons/hr_timesheet_sheet/static/src/xml/timesheet.xml index 4da95f59a11..7fb956cc103 100644 --- a/addons/hr_timesheet_sheet/static/src/xml/timesheet.xml +++ b/addons/hr_timesheet_sheet/static/src/xml/timesheet.xml @@ -13,11 +13,11 @@ Total - + + t-att-data-day-count="day_count" type="text"/> From ab97cd990772bf3021ac76940f38719f87921123 Mon Sep 17 00:00:00 2001 From: niv-openerp Date: Tue, 2 Oct 2012 15:27:14 +0200 Subject: [PATCH 061/229] minor bzr revid: nicolas.vanhoren@openerp.com-20121002132714-p7hxzd8meo26mwfj --- addons/hr_timesheet_sheet/static/src/xml/timesheet.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/hr_timesheet_sheet/static/src/xml/timesheet.xml b/addons/hr_timesheet_sheet/static/src/xml/timesheet.xml index 7fb956cc103..e27df42d47d 100644 --- a/addons/hr_timesheet_sheet/static/src/xml/timesheet.xml +++ b/addons/hr_timesheet_sheet/static/src/xml/timesheet.xml @@ -10,7 +10,7 @@
    - Total + TOTAL From a217a94f7fd222f0c8298c3c91a1c75bb9fdd77b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20van=20der=20Essen?= Date: Tue, 2 Oct 2012 16:06:44 +0200 Subject: [PATCH 062/229] [IMP] css: display previously implicit 'or' operator in the advanced search bzr revid: fva@openerp.com-20121002140644-3qbx3w0fsgk3vd0v --- addons/web/static/src/css/base.css | 7 +++++++ addons/web/static/src/css/base.sass | 5 +++++ addons/web/static/src/xml/base.xml | 1 + 3 files changed, 13 insertions(+) diff --git a/addons/web/static/src/css/base.css b/addons/web/static/src/css/base.css index 7cd189f8d4b..1220dee986d 100644 --- a/addons/web/static/src/css/base.css +++ b/addons/web/static/src/css/base.css @@ -1842,6 +1842,13 @@ margin: 0; white-space: nowrap; } +.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_advanced li:first-child .searchview_extended_prop_or { + visibility: hidden; +} +.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_advanced .searchview_extended_prop_or { + opacity: 0.5; + margin-left: -14px; +} .openerp .oe_searchview .oe_searchview_drawer .oe_opened h4:before { content: "▾ "; position: relative; diff --git a/addons/web/static/src/css/base.sass b/addons/web/static/src/css/base.sass index 06890b991dc..680ed75abb8 100644 --- a/addons/web/static/src/css/base.sass +++ b/addons/web/static/src/css/base.sass @@ -1465,6 +1465,11 @@ $sheet-max-width: 860px list-style: none margin: 0 white-space: nowrap + &:first-child .searchview_extended_prop_or + visibility: hidden + .searchview_extended_prop_or + opacity: 0.5 + margin-left: -14px .oe_opened h4:before content: "▾ " diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml index fdf39633186..a2fb99f2195 100644 --- a/addons/web/static/src/xml/base.xml +++ b/addons/web/static/src/xml/base.xml @@ -1485,6 +1485,7 @@
  • + or @@ -24,6 +24,9 @@ + + +
    From 050f8daa8c2c37df585e04f6175c050de6bfed7f Mon Sep 17 00:00:00 2001 From: niv-openerp Date: Fri, 5 Oct 2012 15:55:28 +0200 Subject: [PATCH 137/229] [FIX] problem with calculation of modifiers bzr revid: nicolas.vanhoren@openerp.com-20121005135528-ridisb8ybcng1szi --- addons/web/static/src/js/view_form.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js index 3fbd089e166..3319177d325 100644 --- a/addons/web/static/src/js/view_form.js +++ b/addons/web/static/src/js/view_form.js @@ -1212,7 +1212,7 @@ instance.web.form.FormRenderingEngine = instance.web.form.FormRenderingEngineInt this.$form.appendTo(this.$target); - _.each(this.fields_to_init, function($elem) { + var ws = _.map(this.fields_to_init, function($elem) { var name = $elem.attr("name"); if (!self.fvg.fields[name]) { throw new Error("Field '" + name + "' specified in view could not be found."); @@ -1228,7 +1228,10 @@ instance.web.form.FormRenderingEngine = instance.web.form.FormRenderingEngineInt } self.alter_field(w); self.view.register_field(w, $elem.attr("name")); - w.replace($elem); + return [w, $elem]; + }); + _.each(ws, function(w) { + w[0].replace(w[1]); }); _.each(this.tags_to_init, function($elem) { var tag_name = $elem[0].tagName.toLowerCase(); From a280aa6d81288bed3bf7756c20826153a3a4c69f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20van=20der=20Essen?= Date: Fri, 5 Oct 2012 16:08:31 +0200 Subject: [PATCH 138/229] [FIX] removed blue background for readonly fields, fixed css errors as reported by firefox bzr revid: fva@openerp.com-20121005140831-ssp03115gu7zy1ge --- addons/web/static/src/css/base.css | 2 +- addons/web/static/src/css/base.sass | 2 +- addons/web_calendar/static/src/css/web_calendar.css | 1 - addons/web_diagram/static/src/css/base_diagram.css | 2 +- 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/addons/web/static/src/css/base.css b/addons/web/static/src/css/base.css index 165c893933c..14496fe3522 100644 --- a/addons/web/static/src/css/base.css +++ b/addons/web/static/src/css/base.css @@ -2246,7 +2246,7 @@ .openerp .oe_form .oe_datepicker_root { display: inline-block; } -.openerp .oe_form .oe_form_required input:not([disabled]), .openerp .oe_form .oe_form_required select:not([disabled]), .openerp .oe_form .oe_form_required textarea:not([disabled]) { +.openerp .oe_form .oe_form_required input:not([disabled]):not([readonly]), .openerp .oe_form .oe_form_required select:not([disabled]):not([readonly]), .openerp .oe_form .oe_form_required textarea:not([disabled]):not([readonly]) { background-color: #d2d2ff !important; } .openerp .oe_form .oe_form_invalid input, .openerp .oe_form .oe_form_invalid select, .openerp .oe_form .oe_form_invalid textarea { diff --git a/addons/web/static/src/css/base.sass b/addons/web/static/src/css/base.sass index 68446cf777b..784f93d94cf 100644 --- a/addons/web/static/src/css/base.sass +++ b/addons/web/static/src/css/base.sass @@ -1780,7 +1780,7 @@ $sheet-max-width: 860px .oe_datepicker_root display: inline-block .oe_form_required - input:not([disabled]), select:not([disabled]), textarea:not([disabled]) + input:not([disabled]):not([readonly]), select:not([disabled]):not([readonly]), textarea:not([disabled]):not([readonly]) background-color: #D2D2FF !important .oe_form_invalid input, select, textarea diff --git a/addons/web_calendar/static/src/css/web_calendar.css b/addons/web_calendar/static/src/css/web_calendar.css index 52ea63e787e..9cfc2f777bd 100644 --- a/addons/web_calendar/static/src/css/web_calendar.css +++ b/addons/web_calendar/static/src/css/web_calendar.css @@ -997,7 +997,6 @@ div.openerp .dhx_cal_editor textarea { } .openerp .dhx_cal_event_selected{ background-color: #757575; - color: ffffff; } /* Agenda week end */ .openerp .dhx_scale_bar_header { diff --git a/addons/web_diagram/static/src/css/base_diagram.css b/addons/web_diagram/static/src/css/base_diagram.css index 8d0a418f52e..6de8b5deb8a 100644 --- a/addons/web_diagram/static/src/css/base_diagram.css +++ b/addons/web_diagram/static/src/css/base_diagram.css @@ -49,5 +49,5 @@ -ms-user-select: none; -o-user-select: none; user-select: none; -}; +} From 46c83756dad86dfea2391bd33e5c5c4d09fac800 Mon Sep 17 00:00:00 2001 From: "Quentin (OpenERP)" Date: Fri, 5 Oct 2012 16:36:06 +0200 Subject: [PATCH 139/229] [FIX] analytic: fix crash when trying to set currency_id to False in an analytic account bzr revid: qdp-launchpad@openerp.com-20121005143606-1evrpd8g3drg0v8m --- addons/analytic/analytic.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/addons/analytic/analytic.py b/addons/analytic/analytic.py index d71b480f799..1ce483a26cd 100644 --- a/addons/analytic/analytic.py +++ b/addons/analytic/analytic.py @@ -125,7 +125,8 @@ class account_analytic_account(osv.osv): if account.company_id: if account.company_id.currency_id.id != value: raise osv.except_osv(_('Error!'), _("If you set a company, the currency selected has to be the same as it's currency. \nYou can remove the company belonging, and thus change the currency, only on analytic account of type 'view'. This can be really usefull for consolidation purposes of several companies charts with different currencies, for example.")) - return cr.execute("""update account_analytic_account set currency_id=%s where id=%s""", (value, account.id, )) + if value: + return cr.execute("""update account_analytic_account set currency_id=%s where id=%s""", (value, account.id, )) def _currency(self, cr, uid, ids, field_name, arg, context=None): result = {} From def9d0dabfbcb72c97ceed06265adc86f6ff9c5c Mon Sep 17 00:00:00 2001 From: Antonin Bourguignon Date: Fri, 5 Oct 2012 16:54:13 +0200 Subject: [PATCH 140/229] [IMP] meeting form view: when 'recurrency' is checked, the recurrency options are required bzr revid: abo@openerp.com-20121005145413-7yyc3rr6ecx3ofu5 --- addons/base_calendar/crm_meeting_view.xml | 69 +++++++++++------------ 1 file changed, 34 insertions(+), 35 deletions(-) diff --git a/addons/base_calendar/crm_meeting_view.xml b/addons/base_calendar/crm_meeting_view.xml index 140f49cdcfc..37797a012aa 100644 --- a/addons/base_calendar/crm_meeting_view.xml +++ b/addons/base_calendar/crm_meeting_view.xml @@ -15,9 +15,9 @@ action_crm_meeting_read - + action - + crm.meeting client_action_multi @@ -33,9 +33,9 @@ action_crm_meeting_unread - + action - + crm.meeting client_action_multi @@ -124,38 +124,38 @@ diff --git a/openerp/addons/base/res/res_company_view.xml b/openerp/addons/base/res/res_company_view.xml index cac53b7a76a..b8cb12d3dd0 100644 --- a/openerp/addons/base/res/res_company_view.xml +++ b/openerp/addons/base/res/res_company_view.xml @@ -46,10 +46,10 @@
    - +
    - +