From 7655344ec534467b68e0f9bdd78ebcd46efc29b3 Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Thu, 20 Sep 2012 09:45:14 +0200 Subject: [PATCH 001/128] [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 002/128] [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 003/128] [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 004/128] [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 005/128] [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 006/128] [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 007/128] [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 008/128] [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 009/128] [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 010/128] [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 011/128] [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 012/128] [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 013/128] [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 014/128] [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 015/128] [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 016/128] [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 9d8ea41d450f738afe77c47b1f86c212e3132e33 Mon Sep 17 00:00:00 2001 From: niv-openerp Date: Mon, 1 Oct 2012 17:19:17 +0200 Subject: [PATCH 034/128] 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 7c1339928b1e69a17e84c66bd366144dec6fed8e Mon Sep 17 00:00:00 2001 From: niv-openerp Date: Mon, 1 Oct 2012 17:46:05 +0200 Subject: [PATCH 035/128] 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 2b07b4e35c5ee619eaf93a7bb4fb14a37e55fb0e Mon Sep 17 00:00:00 2001 From: "Quentin (OpenERP)" Date: Mon, 1 Oct 2012 18:08:54 +0200 Subject: [PATCH 036/128] [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 047/128] 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 048/128] 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 049/128] 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 43322be88525a16b4e5f0266acb4bcc402ee1d6c Mon Sep 17 00:00:00 2001 From: niv-openerp Date: Tue, 2 Oct 2012 16:38:48 +0200 Subject: [PATCH 050/128] Added total stuff bzr revid: nicolas.vanhoren@openerp.com-20121002143848-1bgyz62vxn01dsvi --- .../static/src/css/timesheet.css | 35 +++++++++++++++---- .../static/src/js/timesheet.js | 30 ++++++++++++++++ .../static/src/xml/timesheet.xml | 19 ++++++++-- 3 files changed, 76 insertions(+), 8 deletions(-) diff --git a/addons/hr_timesheet_sheet/static/src/css/timesheet.css b/addons/hr_timesheet_sheet/static/src/css/timesheet.css index e609bb636b4..40ef259e1c5 100644 --- a/addons/hr_timesheet_sheet/static/src/css/timesheet.css +++ b/addons/hr_timesheet_sheet/static/src/css/timesheet.css @@ -1,25 +1,34 @@ -.oe_timesheet_weekly table { +.openerp .oe_timesheet_weekly { + overflow-x: auto; +} + +.openerp .oe_timesheet_weekly table { width: 100%; } -.oe_timesheet_weekly th { +.openerp .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 { +.openerp .oe_timesheet_weekly th.oe_timesheet_weekly_date_head { + width: 60px; +} + +.openerp .oe_timesheet_weekly td { text-align: right; vertical-align: middle; } -.oe_timesheet_weekly .oe_timesheet_weekly_account { +.openerp .oe_timesheet_weekly .oe_timesheet_weekly_account { text-align: left; + padding-right: 30px; } -.oe_timesheet_weekly td input { +.openerp .oe_timesheet_weekly td input { border: 1px solid #CCC; padding: 5px 2px !important; color: #666 !important; @@ -30,11 +39,25 @@ min-width: 0 !important; } -.oe_timesheet_weekly td .oe_timesheet_weekly_box { +.openerp .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; +} + +.openerp .oe_timesheet_weekly .oe_timesheet_weekly_adding_tot { + display: table; + width: 100%; +} + +.openerp .oe_timesheet_weekly .oe_timesheet_weekly_adding { + display: table-cell; + text-align: left; +} + +.openerp .oe_timesheet_weekly .oe_timesheet_weekly_tottot { + display: table-cell; } \ 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 16d42261585..e836278fef0 100644 --- a/addons/hr_timesheet_sheet/static/src/js/timesheet.js +++ b/addons/hr_timesheet_sheet/static/src/js/timesheet.js @@ -147,6 +147,8 @@ openerp.hr_timesheet_sheet = function(instance) { } 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.get_day_total(day_count).html(self.sum_day_total(day_count)); + self.get_super_total().html(self.sum_super_total()); self.sync(); } }); @@ -156,6 +158,10 @@ openerp.hr_timesheet_sheet = function(instance) { }); self.get_total(account).html(self.sum_total(account)); }); + _.each(_.range(self.dates.length), function(day_count) { + self.get_day_total(day_count).html(self.sum_day_total(day_count)); + }); + self.get_super_total().html(self.sum_super_total()); }, get_box: function(account, day_count) { return this.$('[data-account="' + account.account + '"][data-day-count="' + day_count + '"]'); @@ -163,6 +169,12 @@ openerp.hr_timesheet_sheet = function(instance) { get_total: function(account) { return this.$('[data-account-total="' + account.account + '"]'); }, + get_day_total: function(day_count) { + return this.$('[data-day-total="' + day_count + '"]'); + }, + get_super_total: function() { + return this.$('.oe_timesheet_weekly_supertotal'); + }, sum_box: function(account, day_count) { var line_total = 0; _.each(account.days[day_count].lines, function(line) { @@ -179,6 +191,23 @@ openerp.hr_timesheet_sheet = function(instance) { }); return total; }, + sum_day_total: function(day_count) { + var total = 0; + _.each(this.accounts, function(account) { + _.each(account.days[day_count].lines, function(line) { + total += line.unit_amount; + }); + }); + return total; + }, + sum_super_total: function(account) { + var self = this; + var total = 0; + _.each(self.accounts, function(account) { + total += self.sum_total(account); + }); + return total; + }, sync: function() { var self = this; var ops = []; @@ -197,6 +226,7 @@ openerp.hr_timesheet_sheet = function(instance) { tmp[k] = v[0]; } }); + // we have to remove some keys, because analytic lines are shitty _.each(_.keys(tmp), function(key) { if (auth_keys[key] === undefined) { tmp[key] = undefined; diff --git a/addons/hr_timesheet_sheet/static/src/xml/timesheet.xml b/addons/hr_timesheet_sheet/static/src/xml/timesheet.xml index e27df42d47d..73b95eb4840 100644 --- a/addons/hr_timesheet_sheet/static/src/xml/timesheet.xml +++ b/addons/hr_timesheet_sheet/static/src/xml/timesheet.xml @@ -6,11 +6,11 @@ - - + @@ -24,6 +24,21 @@ + + + + + + + + +
    +
    TOTALTOTAL
    +
    +
    +
    TOTAL
    +
    +
    +
    From c64ec6591c113732893f7280f5494168d40c927f Mon Sep 17 00:00:00 2001 From: niv-openerp Date: Tue, 2 Oct 2012 16:41:05 +0200 Subject: [PATCH 051/128] wip bzr revid: nicolas.vanhoren@openerp.com-20121002144105-13tzymf7fj00bml6 --- addons/hr_timesheet_sheet/static/src/xml/timesheet.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/addons/hr_timesheet_sheet/static/src/xml/timesheet.xml b/addons/hr_timesheet_sheet/static/src/xml/timesheet.xml index 73b95eb4840..59583415bb7 100644 --- a/addons/hr_timesheet_sheet/static/src/xml/timesheet.xml +++ b/addons/hr_timesheet_sheet/static/src/xml/timesheet.xml @@ -32,11 +32,11 @@ - - - + + + - + From e644adeca12b7ead75159f7ed76850364c9a6bae Mon Sep 17 00:00:00 2001 From: niv-openerp Date: Tue, 2 Oct 2012 16:47:08 +0200 Subject: [PATCH 052/128] wip bzr revid: nicolas.vanhoren@openerp.com-20121002144708-p88zs6a3hjkq0w8h --- addons/hr_timesheet_sheet/static/src/css/timesheet.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/addons/hr_timesheet_sheet/static/src/css/timesheet.css b/addons/hr_timesheet_sheet/static/src/css/timesheet.css index 40ef259e1c5..3ac628ab491 100644 --- a/addons/hr_timesheet_sheet/static/src/css/timesheet.css +++ b/addons/hr_timesheet_sheet/static/src/css/timesheet.css @@ -7,6 +7,10 @@ width: 100%; } +.openerp .oe_timesheet_weekly td { + padding-top: 15px; +} + .openerp .oe_timesheet_weekly th { text-align: right; color: #069; From 295e96d5283b5271295c4604fb03d69b776c1e3a Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Tue, 2 Oct 2012 16:57:57 +0200 Subject: [PATCH 053/128] [IMP] auth_signup: add state (function field) on res.users: new, active, resetting password bzr revid: rco@openerp.com-20121002145757-2m4dj7t5zq2sztfc --- addons/auth_signup/__openerp__.py | 1 + addons/auth_signup/res_users.py | 9 +++++++++ addons/auth_signup/res_users_view.xml | 19 +++++++++++++++++++ 3 files changed, 29 insertions(+) create mode 100644 addons/auth_signup/res_users_view.xml diff --git a/addons/auth_signup/__openerp__.py b/addons/auth_signup/__openerp__.py index 5bb3aab4ec0..9cbeb60638f 100644 --- a/addons/auth_signup/__openerp__.py +++ b/addons/auth_signup/__openerp__.py @@ -34,6 +34,7 @@ Allow users to sign up. 'data': [ 'auth_signup_data.xml', 'res_config.xml', + 'res_users_view.xml', ], 'js': ['static/src/js/auth_signup.js'], 'css' : ['static/src/css/base.css'], diff --git a/addons/auth_signup/res_users.py b/addons/auth_signup/res_users.py index 0d7fd3744df..7b663bd0e33 100644 --- a/addons/auth_signup/res_users.py +++ b/addons/auth_signup/res_users.py @@ -137,6 +137,15 @@ class res_partner(osv.Model): class res_users(osv.Model): _inherit = 'res.users' + def _get_state(self, cr, uid, ids, name, arg, context=None): + return dict((user.id, 'new' if not user.login_date else 'reset' if user.signup_token else 'active') + for user in self.browse(cr, uid, ids, context)) + + _columns = { + 'state': fields.function(_get_state, string='State', type='selection', + selection=[('new', 'New'), ('active', 'Active'), ('reset', 'Resetting Password')]), + } + def signup(self, cr, uid, values, token=None, context=None): """ signup a user, to either: - create a new user (no token), or diff --git a/addons/auth_signup/res_users_view.xml b/addons/auth_signup/res_users_view.xml new file mode 100644 index 00000000000..c42e1ab5f41 --- /dev/null +++ b/addons/auth_signup/res_users_view.xml @@ -0,0 +1,19 @@ + + + + + + user.form.state + res.users + + + +
    + +
    +
    +
    +
    + +
    +
    From add8348f4ba222e046682d31e8f322bbb13b6aea Mon Sep 17 00:00:00 2001 From: niv-openerp Date: Tue, 2 Oct 2012 17:02:50 +0200 Subject: [PATCH 054/128] optimized a little total calculus bzr revid: nicolas.vanhoren@openerp.com-20121002150250-6rx77po7v40f6nqn --- .../static/src/js/timesheet.js | 47 +++++++------------ 1 file changed, 17 insertions(+), 30 deletions(-) diff --git a/addons/hr_timesheet_sheet/static/src/js/timesheet.js b/addons/hr_timesheet_sheet/static/src/js/timesheet.js index e836278fef0..fbaeae2740c 100644 --- a/addons/hr_timesheet_sheet/static/src/js/timesheet.js +++ b/addons/hr_timesheet_sheet/static/src/js/timesheet.js @@ -146,9 +146,7 @@ openerp.hr_timesheet_sheet = function(instance) { $(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.get_day_total(day_count).html(self.sum_day_total(day_count)); - self.get_super_total().html(self.sum_super_total()); + self.display_totals(); self.sync(); } }); @@ -156,12 +154,8 @@ openerp.hr_timesheet_sheet = function(instance) { self.get_box(account, day_count).html(self.sum_box(account, day_count)); } }); - self.get_total(account).html(self.sum_total(account)); }); - _.each(_.range(self.dates.length), function(day_count) { - self.get_day_total(day_count).html(self.sum_day_total(day_count)); - }); - self.get_super_total().html(self.sum_super_total()); + self.display_totals(); }, get_box: function(account, day_count) { return this.$('[data-account="' + account.account + '"][data-day-count="' + day_count + '"]'); @@ -182,31 +176,24 @@ openerp.hr_timesheet_sheet = function(instance) { }); return line_total; }, - sum_total: function(account) { - var total = 0; - _.each(account.days, function(day) { - _.each(day.lines, function(line) { - total += line.unit_amount; - }); - }); - return total; - }, - sum_day_total: function(day_count) { - var total = 0; - _.each(this.accounts, function(account) { - _.each(account.days[day_count].lines, function(line) { - total += line.unit_amount; - }); - }); - return total; - }, - sum_super_total: function(account) { + display_totals: function() { var self = this; - var total = 0; + var day_tots = _.map(_.range(self.dates.length), function() { return 0 }); + var super_tot = 0; _.each(self.accounts, function(account) { - total += self.sum_total(account); + var acc_tot = 0; + _.each(_.range(self.dates.length), function(day_count) { + var sum = self.sum_box(account, day_count); + acc_tot += sum; + day_tots[day_count] += sum; + super_tot += sum; + }); + self.get_total(account).html(acc_tot); }); - return total; + _.each(_.range(self.dates.length), function(day_count) { + self.get_day_total(day_count).html(day_tots[day_count]); + }); + self.get_super_total().html(super_tot); }, sync: function() { var self = this; From 66b62190c7128b4a13c7d9698b09ecd19d619239 Mon Sep 17 00:00:00 2001 From: niv-openerp Date: Tue, 2 Oct 2012 17:15:20 +0200 Subject: [PATCH 055/128] Removed date_current more bzr revid: nicolas.vanhoren@openerp.com-20121002151520-q1m2tpgbfmcvxzux --- addons/hr_timesheet_sheet/hr_timesheet_sheet_demo.xml | 1 - addons/hr_timesheet_sheet/test/test_hr_timesheet_sheet.yml | 1 - addons/hr_timesheet_sheet/wizard/hr_timesheet_current.py | 1 - .../test/worktask_entry_to_timesheetline_entry.yml | 1 - 4 files changed, 4 deletions(-) diff --git a/addons/hr_timesheet_sheet/hr_timesheet_sheet_demo.xml b/addons/hr_timesheet_sheet/hr_timesheet_sheet_demo.xml index 42497eb537e..4e0e1becbe9 100644 --- a/addons/hr_timesheet_sheet/hr_timesheet_sheet_demo.xml +++ b/addons/hr_timesheet_sheet/hr_timesheet_sheet_demo.xml @@ -6,7 +6,6 @@ Sheet 1 - --> diff --git a/addons/hr_timesheet_sheet/test/test_hr_timesheet_sheet.yml b/addons/hr_timesheet_sheet/test/test_hr_timesheet_sheet.yml index 070079a4b80..5ecb3ccba11 100644 --- a/addons/hr_timesheet_sheet/test/test_hr_timesheet_sheet.yml +++ b/addons/hr_timesheet_sheet/test/test_hr_timesheet_sheet.yml @@ -16,7 +16,6 @@ I create a timesheet for employee "Quentin Paolinon". - !record {model: hr_timesheet_sheet.sheet, id: hr_timesheet_sheet_sheet_deddk0}: - date_current: !eval time.strftime('%Y-%m-%d') date_from: !eval time.strftime('%Y-%m-01') name: Quentin Paolinon state: new diff --git a/addons/hr_timesheet_sheet/wizard/hr_timesheet_current.py b/addons/hr_timesheet_sheet/wizard/hr_timesheet_current.py index a6974fc2ccf..3bd495036d5 100644 --- a/addons/hr_timesheet_sheet/wizard/hr_timesheet_current.py +++ b/addons/hr_timesheet_sheet/wizard/hr_timesheet_current.py @@ -42,7 +42,6 @@ class hr_timesheet_current_open(osv.osv_memory): view_type = 'tree,form' domain = "[('id','in',["+','.join(map(str, ids))+"]),('user_id', '=', uid)]" elif len(ids)==1: - ts.write(cr, uid, ids, {'date_current': time.strftime('%Y-%m-%d')}, context=context) domain = "[('user_id', '=', uid)]" else: domain = "[('user_id', '=', uid)]" diff --git a/addons/project_timesheet/test/worktask_entry_to_timesheetline_entry.yml b/addons/project_timesheet/test/worktask_entry_to_timesheetline_entry.yml index 497ec9e3597..3efd133ffb6 100644 --- a/addons/project_timesheet/test/worktask_entry_to_timesheetline_entry.yml +++ b/addons/project_timesheet/test/worktask_entry_to_timesheetline_entry.yml @@ -45,7 +45,6 @@ Create a timesheet sheet for HR manager - !record {model: hr_timesheet_sheet.sheet, id: hr_timesheet_sheet_sheet_sheetforhrmanager0}: - date_current: !eval time.strftime('%Y-05-%d') date_from: !eval "'%s-05-01' %(datetime.now().year)" date_to: !eval "'%s-05-31' %(datetime.now().year)" name: Sheet for hr manager From 224520c2cab5880362702608e56ae03fc84ac1ce Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Wed, 3 Oct 2012 10:38:31 +0200 Subject: [PATCH 056/128] [IMP] auth_signup: improve label in settings bzr revid: rco@openerp.com-20121003083831-1tcla6w18cyazb1j --- addons/auth_signup/res_config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/auth_signup/res_config.py b/addons/auth_signup/res_config.py index c69c01127f6..21fce19745c 100644 --- a/addons/auth_signup/res_config.py +++ b/addons/auth_signup/res_config.py @@ -26,7 +26,7 @@ class base_config_settings(osv.TransientModel): _inherit = 'base.config.settings' _columns = { - 'auth_signup_uninvited': fields.boolean('Allow public users to sign up', help="If unchecked only invited users may sign up"), + 'auth_signup_uninvited': fields.boolean('Allow external users to sign up', help="If unchecked, only invited users may sign up"), 'auth_signup_template_user_id': fields.many2one('res.users', 'Template user for new users created through signup'), } From 558ad0616a4fef54ec290ef1967ab0d0ac083260 Mon Sep 17 00:00:00 2001 From: "Vidhin Mehta (OpenERP)" Date: Wed, 3 Oct 2012 16:04:28 +0530 Subject: [PATCH 057/128] [IMP]remove callbacks from call method and improve related code. bzr revid: vme@tinyerp.com-20121003103428-fnr942tzl065rcei --- addons/web/static/src/js/data.js | 4 ++-- addons/web/static/test/fulltest/dataset.js | 4 ++-- addons/web_view_editor/static/src/js/view_editor.js | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/addons/web/static/src/js/data.js b/addons/web/static/src/js/data.js index 2b82e618203..cf4b29ab56b 100644 --- a/addons/web/static/src/js/data.js +++ b/addons/web/static/src/js/data.js @@ -586,8 +586,8 @@ instance.web.DataSet = instance.web.CallbackEnabled.extend({ * @param {Function} error_callback * @returns {$.Deferred} */ - call: function (method, args, callback, error_callback) { - return this._model.call(method, args).then(callback, error_callback); + call: function (method, args) { + return this._model.call(method, args); }, /** * Calls an arbitrary method, with more crazy diff --git a/addons/web/static/test/fulltest/dataset.js b/addons/web/static/test/fulltest/dataset.js index d276f4e2494..f7e64fba53e 100644 --- a/addons/web/static/test/fulltest/dataset.js +++ b/addons/web/static/test/fulltest/dataset.js @@ -82,14 +82,14 @@ $(document).ready(function () { }); t.test('call', function (openerp) { var ds = new openerp.web.DataSet({session: openerp.session}, 'mod'); - t.expect(ds.call('frob', ['a', 'b', 42]), function (r) { + t.expect(ds.call('frob', ['a', 'b', 42]).then(function (r) { strictEqual(r.method, 'frob'); strictEqual(r.args.length, 3); deepEqual(r.args, ['a', 'b', 42]); ok(_.isEmpty(r.kwargs)); - }); + })); }); t.test('name_get').then(function (openerp) { var ds = new openerp.web.DataSet({session: openerp.session}, 'mod'); diff --git a/addons/web_view_editor/static/src/js/view_editor.js b/addons/web_view_editor/static/src/js/view_editor.js index 73d07efd5c0..e65f0ab5d38 100644 --- a/addons/web_view_editor/static/src/js/view_editor.js +++ b/addons/web_view_editor/static/src/js/view_editor.js @@ -136,7 +136,7 @@ instance.web_view_editor.ViewEditor = instance.web.Widget.extend({ var field_dataset = new instance.web.DataSetSearch(this, this.model, null, null); var model_dataset = new instance.web.DataSetSearch(this, 'ir.model', null, null); var view_string = "", field_name = false, self = this; - field_dataset.call( 'fields_get', [], function(fields) { + field_dataset.call( 'fields_get', []).then(function(fields) { _.each(['name', 'x_name'], function(value) { if (_.include(_.keys(fields), value)) { field_name = value; @@ -539,7 +539,7 @@ instance.web_view_editor.ViewEditor = instance.web.Widget.extend({ var value = _.has(_CHILDREN, element) ? element : _.str.include(html_tag, element)?"html_tag":false; property_to_check.push(value); }); - field_dataset.call( 'fields_get', [], function(result) { + field_dataset.call( 'fields_get', []).then(function(result) { var fields = _.keys(result); fields.push(" "),fields.sort(); self.on_add_node(property_to_check, fields); From 30069a02398c4f9de698c6fcc43bc4958aa1d960 Mon Sep 17 00:00:00 2001 From: ggh-openerp Date: Wed, 3 Oct 2012 17:58:36 +0530 Subject: [PATCH 058/128] [IMP]In rpc method remove callback and modify relate code bzr revid: ggh@tinyerp.com-20121003122836-36g8ijxdcjwgntjl --- addons/web/static/src/js/chrome.js | 10 +++++----- addons/web/static/src/js/corelib.js | 6 +++--- addons/web/static/src/js/coresetup.js | 4 ++-- addons/web/static/src/js/data_export.js | 8 ++++---- addons/web/static/src/js/search.js | 2 +- addons/web/static/src/js/view_form.js | 4 ++-- addons/web/static/src/js/view_list.js | 2 +- addons/web/static/src/js/view_tree.js | 2 +- addons/web/static/src/js/views.js | 6 +++--- addons/web_calendar/static/src/js/calendar.js | 2 +- addons/web_diagram/static/src/js/diagram.js | 3 +-- 11 files changed, 24 insertions(+), 25 deletions(-) diff --git a/addons/web/static/src/js/chrome.js b/addons/web/static/src/js/chrome.js index bed4a159535..985912d57bf 100644 --- a/addons/web/static/src/js/chrome.js +++ b/addons/web/static/src/js/chrome.js @@ -392,7 +392,7 @@ instance.web.DatabaseManager = instance.web.Widget.extend({ do_create: function(form) { var self = this; var fields = $(form).serializeArray(); - self.rpc("/web/database/create", {'fields': fields}, function(result) { + self.rpc("/web/database/create", {'fields': fields}).then(function(result) { var form_obj = self.to_object(fields); var client_action = { type: 'ir.actions.client', @@ -418,7 +418,7 @@ instance.web.DatabaseManager = instance.web.Widget.extend({ if (!db || !confirm("Do you really want to delete the database: " + db + " ?")) { return; } - self.rpc("/web/database/drop", {'fields': fields}, function(result) { + self.rpc("/web/database/drop", {'fields': fields}).then(function(result) { if (result.error) { self.display_error(result); return; @@ -481,7 +481,7 @@ instance.web.DatabaseManager = instance.web.Widget.extend({ var self = this; self.rpc("/web/database/change_password", { 'fields': $(form).serializeArray() - }, function(result) { + }).then(function(result) { if (result.error) { self.display_error(result); return; @@ -660,7 +660,7 @@ instance.web.ChangePassword = instance.web.Widget.extend({ submitHandler: function (form) { self.rpc("/web/session/change_password",{ 'fields': $(form).serializeArray() - }, function(result) { + }).then(function(result) { if (result.error) { self.display_error(result); return; @@ -1165,7 +1165,7 @@ instance.web.EmbeddedClient = instance.web.Client.extend({ var self = this; return $.when(this._super()).pipe(function() { return instance.session.session_authenticate(self.dbname, self.login, self.key, true).pipe(function() { - return self.rpc("/web/action/load", { action_id: self.action_id }, function(result) { + return self.rpc("/web/action/load", { action_id: self.action_id }).then(function(result) { var action = result.result; action.flags = _.extend({ //views_switcher : false, diff --git a/addons/web/static/src/js/corelib.js b/addons/web/static/src/js/corelib.js index 62ac48f0c61..d6dfac963e0 100644 --- a/addons/web/static/src/js/corelib.js +++ b/addons/web/static/src/js/corelib.js @@ -841,7 +841,7 @@ instance.web.Widget = instance.web.Class.extend(instance.web.WidgetMixin, { rpc: function(url, data, success, error) { var def = $.Deferred().then(success, error); var self = this; - instance.session.rpc(url, data). then(function() { + instance.session.rpc(url, data).then(function() { if (!self.isDestroyed()) def.resolve.apply(def, arguments); }, function() { @@ -1299,7 +1299,7 @@ instance.web.JsonRPC = instance.web.CallbackEnabled.extend({ * @param {Function} error_callback function to execute on RPC call failure * @returns {jQuery.Deferred} jquery-provided ajax deferred */ - rpc: function(url, params, success_callback, error_callback) { + rpc: function(url, params) { var self = this; // url can be an $.ajax option object if (_.isString(url)) { @@ -1358,7 +1358,7 @@ instance.web.JsonRPC = instance.web.CallbackEnabled.extend({ self.on_rpc_error(error, event); } }); - }).then(success_callback, error_callback).promise(); + }); return deferred; }, /** diff --git a/addons/web/static/src/js/coresetup.js b/addons/web/static/src/js/coresetup.js index ea160d6faee..7134c068ec3 100644 --- a/addons/web/static/src/js/coresetup.js +++ b/addons/web/static/src/js/coresetup.js @@ -19,9 +19,9 @@ instance.web.Session = instance.web.JsonRPC.extend( /** @lends instance.web.Sess this.name = instance._session_id; this.qweb_mutex = new $.Mutex(); }, - rpc: function(url, params, success_callback, error_callback) { + rpc: function(url, params) { params.session_id = this.session_id; - return this._super(url, params, success_callback, error_callback); + return this._super(url, params); }, /** * Setup a sessionm diff --git a/addons/web/static/src/js/data_export.js b/addons/web/static/src/js/data_export.js index 29aea6362f7..a06e9ccb155 100644 --- a/addons/web/static/src/js/data_export.js +++ b/addons/web/static/src/js/data_export.js @@ -49,7 +49,7 @@ instance.web.DataExport = instance.web.Dialog.extend({ self.rpc("/web/export/get_fields", { model: self.dataset.model, import_compat: Boolean(import_comp) - }, function (records) { + }).then(function (records) { got_fields.resolve(); self.on_show_data(records); }); @@ -57,7 +57,7 @@ instance.web.DataExport = instance.web.Dialog.extend({ return $.when( got_fields, - this.rpc('/web/export/formats', {}, this.do_setup_export_formats), + this.rpc('/web/export/formats', {}).then(this.do_setup_export_formats), this.show_exports_list()); }, do_setup_export_formats: function (formats) { @@ -91,7 +91,7 @@ instance.web.DataExport = instance.web.Dialog.extend({ self.$el.find('#fields_list option').remove(); var export_id = self.$el.find('#saved_export_list option:selected').val(); if (export_id) { - self.rpc('/web/export/namelist', {'model': self.dataset.model, export_id: parseInt(export_id)}, self.do_load_export_field); + self.rpc('/web/export/namelist', {'model': self.dataset.model, export_id: parseInt(export_id)}).then(self.do_load_export_field); } }); self.$el.find('#delete_export_list').click(function() { @@ -181,7 +181,7 @@ instance.web.DataExport = instance.web.Dialog.extend({ import_compat: Boolean(import_comp), parent_field_type : record['field_type'], exclude: exclude_fields - }, function(results) { + }).then(function(results) { record.loaded = true; self.on_show_data(results, record.id); }); diff --git a/addons/web/static/src/js/search.js b/addons/web/static/src/js/search.js index 01bc0c76c87..63d6574dd82 100644 --- a/addons/web/static/src/js/search.js +++ b/addons/web/static/src/js/search.js @@ -1693,7 +1693,7 @@ instance.web.search.Advanced = instance.web.search.Input.extend({ }); return $.when( this._super(), - this.rpc("/web/searchview/fields_get", {model: this.view.model}, function(data) { + this.rpc("/web/searchview/fields_get", {model: this.view.model}).then(function(data) { self.fields = _.extend({ id: { string: 'ID', type: 'id' } }, data.fields); diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js index bf6b869482b..af11ce27bda 100644 --- a/addons/web/static/src/js/view_form.js +++ b/addons/web/static/src/js/view_form.js @@ -4417,7 +4417,7 @@ instance.web.form.SelectCreatePopup = instance.web.form.AbstractFormPopup.extend self.rpc('/web/session/eval_domain_and_context', { domains: [], contexts: [this.context] - }, function (results) { + }).then(function (results) { var search_defaults = {}; _.each(results.context, function (value_, key) { var match = /^search_default_(.*)$/.exec(key); @@ -4485,7 +4485,7 @@ instance.web.form.SelectCreatePopup = instance.web.form.AbstractFormPopup.extend domains: domains || [], contexts: contexts || [], group_by_seq: groupbys || [] - }, function (results) { + }).then(function (results) { self.view_list.do_search(results.domain, results.context, results.group_by); }); }, diff --git a/addons/web/static/src/js/view_list.js b/addons/web/static/src/js/view_list.js index fd34ffddb7b..7457ca5ef67 100644 --- a/addons/web/static/src/js/view_list.js +++ b/addons/web/static/src/js/view_list.js @@ -482,7 +482,7 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi view_type: "tree", context: this.dataset.get_context(context), toolbar: !!this.options.$sidebar - }, callback); + }).then(callback); } }, /** diff --git a/addons/web/static/src/js/view_tree.js b/addons/web/static/src/js/view_tree.js index e0493a446e2..05760420ba8 100644 --- a/addons/web/static/src/js/view_tree.js +++ b/addons/web/static/src/js/view_tree.js @@ -45,7 +45,7 @@ instance.web.TreeView = instance.web.View.extend(/** @lends instance.web.TreeVie view_type: "tree", toolbar: this.view_manager ? !!this.view_manager.sidebar : false, context: this.dataset.get_context() - }, this.on_loaded); + }).then(this.on_loaded); }, /** * Returns the list of fields needed to correctly read objects. diff --git a/addons/web/static/src/js/views.js b/addons/web/static/src/js/views.js index 2fff0e7b683..c80b111fdc8 100644 --- a/addons/web/static/src/js/views.js +++ b/addons/web/static/src/js/views.js @@ -648,7 +648,7 @@ instance.web.ViewManager = instance.web.Widget.extend({ domains: [this.action.domain || []].concat(domains || []), contexts: [action_context].concat(contexts || []), group_by_seq: groupbys || [] - }, function (results) { + }).then(function (results) { self.dataset._model = new instance.web.Model( self.dataset.model, results.context, results.domain); var groupby = results.group_by.length @@ -1054,7 +1054,7 @@ instance.web.Sidebar = instance.web.Widget.extend({ self.rpc("/web/action/load", { action_id: item.action.id, context: additional_context - }, function(result) { + }).then(function(result) { result.result.context = _.extend(result.result.context || {}, additional_context); result.result.flags = result.result.flags || {}; @@ -1233,7 +1233,7 @@ instance.web.View = instance.web.Widget.extend({ args.push(context); return dataset.call_button(action_data.name, args).then(handler); } else if (action_data.type=="action") { - return this.rpc('/web/action/load', { action_id: action_data.name, context: context, do_not_eval: true}, handler); + return this.rpc('/web/action/load', { action_id: action_data.name, context: context, do_not_eval: true}).then(handler); } else { return dataset.exec_workflow(record_id, action_data.name).then(handler); } diff --git a/addons/web_calendar/static/src/js/calendar.js b/addons/web_calendar/static/src/js/calendar.js index 644a35f7fad..81533413312 100644 --- a/addons/web_calendar/static/src/js/calendar.js +++ b/addons/web_calendar/static/src/js/calendar.js @@ -41,7 +41,7 @@ instance.web_calendar.CalendarView = instance.web.View.extend({ }, start: function() { this._super(); - return this.rpc("/web/view/load", {"model": this.model, "view_id": this.view_id, "view_type":"calendar", 'toolbar': false}, this.on_loaded); + return this.rpc("/web/view/load", {"model": this.model, "view_id": this.view_id, "view_type":"calendar", 'toolbar': false}).then(this.on_loaded); }, destroy: function() { scheduler.clearAll(); diff --git a/addons/web_diagram/static/src/js/diagram.js b/addons/web_diagram/static/src/js/diagram.js index 543cc6a9c12..508aeb30563 100644 --- a/addons/web_diagram/static/src/js/diagram.js +++ b/addons/web_diagram/static/src/js/diagram.js @@ -105,8 +105,7 @@ instance.web.DiagramView = instance.web.View.extend({ }); this.rpc( - '/web_diagram/diagram/get_diagram_info',params, - function(result) { + '/web_diagram/diagram/get_diagram_info',params).then(function(result) { self.draw_diagram(result); } ); From 8fbdc2a8253c7b32ad65473ab3ec02683033ba10 Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Wed, 3 Oct 2012 15:21:52 +0200 Subject: [PATCH 059/128] [IMP] auth_reset_password: add button on user form view to reset the user's password bzr revid: rco@openerp.com-20121003132152-1vbzao6n34na2uw6 --- addons/auth_reset_password/__init__.py | 2 +- addons/auth_reset_password/__openerp__.py | 5 ++++- .../{auth_reset_password.py => res_users.py} | 16 ++++++++++------ addons/auth_reset_password/res_users_view.xml | 19 +++++++++++++++++++ 4 files changed, 34 insertions(+), 8 deletions(-) rename addons/auth_reset_password/{auth_reset_password.py => res_users.py} (73%) create mode 100644 addons/auth_reset_password/res_users_view.xml diff --git a/addons/auth_reset_password/__init__.py b/addons/auth_reset_password/__init__.py index 665887c6ee7..8341ccd60c7 100644 --- a/addons/auth_reset_password/__init__.py +++ b/addons/auth_reset_password/__init__.py @@ -20,4 +20,4 @@ ############################################################################## import controllers -import auth_reset_password +import res_users diff --git a/addons/auth_reset_password/__openerp__.py b/addons/auth_reset_password/__openerp__.py index d3e5e6d4bcc..e620d515fa1 100644 --- a/addons/auth_reset_password/__openerp__.py +++ b/addons/auth_reset_password/__openerp__.py @@ -31,7 +31,10 @@ Allow users to reset their password from the login page. 'website': 'http://www.openerp.com', 'installable': True, 'depends': ['auth_signup', 'email_template'], - 'data': ['auth_reset_password.xml'], + 'data': [ + 'auth_reset_password.xml', + 'res_users_view.xml', + ], 'js': ['static/src/js/reset_password.js'], 'qweb': ['static/src/xml/reset_password.xml'], } diff --git a/addons/auth_reset_password/auth_reset_password.py b/addons/auth_reset_password/res_users.py similarity index 73% rename from addons/auth_reset_password/auth_reset_password.py rename to addons/auth_reset_password/res_users.py index fe7d47e13d7..b1039031812 100644 --- a/addons/auth_reset_password/auth_reset_password.py +++ b/addons/auth_reset_password/res_users.py @@ -34,22 +34,26 @@ class res_users(osv.osv): def reset_password(self, cr, uid, login, context=None): """ retrieve the user corresponding to login (login or email), - create a specific signup token, and send it to the user + and reset their password """ user_ids = self.search(cr, uid, [('login', '=', login)], context=context) if not user_ids: user_ids = self.search(cr, uid, [('email', '=', login)], context=context) if len(user_ids) != 1: raise Exception('Reset password: invalid username or email') + return self.action_reset_password(cr, uid, user_ids, context=context) + def action_reset_password(self, cr, uid, ids, context=None): + """ create signup token for each user, and send their signup url by email """ # prepare reset password signup - user = self.browse(cr, uid, user_ids[0], context) - user.partner_id.signup_prepare(expiration=now(days=+1)) + res_partner = self.pool.get('res.partner') + partner_ids = [user.partner_id.id for user in self.browse(cr, uid, ids, context)] + res_partner.signup_prepare(cr, uid, partner_ids, expiration=now(days=+1), context=context) - # send email to user with their signup url - user = self.browse(cr, uid, user.id, context) + # send email to users with their signup url template = self.pool.get('ir.model.data').get_object(cr, uid, 'auth_reset_password', 'reset_password_email') assert template._name == 'email.template' - self.pool.get('email.template').send_mail(cr, uid, template.id, user.id, force_send=True, context=context) + for user in self.browse(cr, uid, ids, context): + self.pool.get('email.template').send_mail(cr, uid, template.id, user.id, context=context) return True diff --git a/addons/auth_reset_password/res_users_view.xml b/addons/auth_reset_password/res_users_view.xml new file mode 100644 index 00000000000..9952162ae65 --- /dev/null +++ b/addons/auth_reset_password/res_users_view.xml @@ -0,0 +1,19 @@ + + + + + + user.form.reset_password + res.users + + + +
    +
    +
    +
    +
    + +
    +
    From a3d648f319d29f244f2b8026c031a6b4e70a0398 Mon Sep 17 00:00:00 2001 From: "Quentin (OpenERP)" Date: Wed, 3 Oct 2012 17:58:20 +0200 Subject: [PATCH 060/128] [FIX] account_voucher: fix amount_currency on writeoff line + fixed/improved yaml tests bzr revid: qdp-launchpad@openerp.com-20121003155820-7ozaoklt042b7i6i --- addons/account_voucher/account_voucher.py | 4 +++- addons/account_voucher/test/case1_usd_usd.yml | 14 +++++++++++++- addons/account_voucher/test/case4_cad_chf.yml | 4 ++-- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/addons/account_voucher/account_voucher.py b/addons/account_voucher/account_voucher.py index 36aa8660bc4..8bb8fe4d9b3 100644 --- a/addons/account_voucher/account_voucher.py +++ b/addons/account_voucher/account_voucher.py @@ -667,6 +667,7 @@ class account_voucher(osv.osv): #voucher line creation for line in account_move_lines: + if _remove_noise_in_o2m(): continue @@ -1183,6 +1184,7 @@ class account_voucher(osv.osv): account_id = voucher_brw.partner_id.property_account_receivable.id else: account_id = voucher_brw.partner_id.property_account_payable.id + sign = voucher_brw.type == 'payment' and -1 or 1 move_line = { 'name': write_off_name or name, 'account_id': account_id, @@ -1191,7 +1193,7 @@ class account_voucher(osv.osv): 'date': voucher_brw.date, 'credit': diff > 0 and diff or 0.0, 'debit': diff < 0 and -diff or 0.0, - 'amount_currency': company_currency <> current_currency and voucher_brw.writeoff_amount or False, + 'amount_currency': company_currency <> current_currency and (sign * -1 * voucher_brw.writeoff_amount) or False, 'currency_id': company_currency <> current_currency and current_currency or False, 'analytic_account_id': voucher_brw.analytic_id and voucher_brw.analytic_id.id or False, } diff --git a/addons/account_voucher/test/case1_usd_usd.yml b/addons/account_voucher/test/case1_usd_usd.yml index b706e6f0644..300d565f229 100644 --- a/addons/account_voucher/test/case1_usd_usd.yml +++ b/addons/account_voucher/test/case1_usd_usd.yml @@ -216,6 +216,10 @@ assert move_line.credit == 63.00, "Debtor account has wrong entry." elif move_line.amount_currency == 10.00: assert move_line.debit == 9.00, "Writeoff amount is wrong." + elif move_line.amount_currency == 240.00: + assert move_line.debit == 216.00, "Bank entry is wrong." + else: + assert False, "Unrecognized journal entry" - I check the residual amount of Invoice1, should be 20 in amount_currency - @@ -237,7 +241,7 @@ - On the first April, I create the second voucher of payment with values 45 USD, journal USD, - - !record {model: account.voucher, id: account_voucher_2_case1}: + !record {model: account.voucher, id: account_voucher_2_case1, view: view_vendor_receipt_form}: account_id: account.cash amount: 45.0 company_id: base.main_company @@ -323,8 +327,16 @@ assert move_line.debit == 4.75, "Writeoff amount is wrong." elif move_line.debit == 11.5 and move_line.account_id.reconcile: assert move_line.amount_currency == 0.0 and move_line.reconcile_id.id == reconcile_a, "Exchange difference entry for the invoice of 100$ is wrong" + elif move_line.credit == 11.5: + assert move_line.amount_currency == 0.0 elif move_line.debit == 31.0 and move_line.account_id.reconcile: assert move_line.amount_currency == 0.0 and move_line.reconcile_id.id == reconcile_b, "Exchange difference entry for the invoice of 200$ is wrong" + elif move_line.credit == 31.0: + assert move_line.amount_currency == 0.0 + elif move_line.amount_currency == 45.00: + assert move_line.debit == 42.75, "Bank entry is wrong." + else: + assert False, "Unrecognized journal entry" - I check the residual amount of Invoice1, should be 0 in residual currency and 0 in amount_residual and paid - diff --git a/addons/account_voucher/test/case4_cad_chf.yml b/addons/account_voucher/test/case4_cad_chf.yml index a78669c95ef..a2b252497cd 100644 --- a/addons/account_voucher/test/case4_cad_chf.yml +++ b/addons/account_voucher/test/case4_cad_chf.yml @@ -164,7 +164,7 @@ - I check that my currency rate difference is correct. 0 in debit with no amount_currency - - I check that my writeoff is correct. 11.05 credit and 13.26 amount_currency + I check that my writeoff is correct. 11.05 credit and -13.26 amount_currency - !python {model: account.voucher}: | voucher = self.search(cr, uid, [('name', '=', 'First payment: Case 4'), ('partner_id', '=', ref('base.res_partner_19'))]) @@ -179,7 +179,7 @@ elif move_line.debit == 0.00 and move_line.credit == 0.00: assert move_line.amount_currency == 0.00, "Incorrect Currency Difference." elif move_line.credit == 10.61: - assert move_line.amount_currency == 13.26, "Writeoff amount is wrong." + assert move_line.amount_currency == -13.26, "Writeoff amount is wrong." else: assert False, "Wrong entry" - From 3c26ec305a6aa003a6d764048a82bd39a16e5792 Mon Sep 17 00:00:00 2001 From: Antony Lesuisse <> Date: Thu, 4 Oct 2012 06:46:24 +0200 Subject: [PATCH 061/128] [IMP] mail: move the code from publisher_warranty to mail bzr revid: stw@openerp.com-20121004044624-hwa3kztl58zrjguh --- addons/mail/__init__.py | 1 + addons/mail/data/mail_data.xml | 14 +++++ addons/mail/mail_message.py | 2 +- addons/mail/update.py | 112 +++++++++++++++++++++++++++++++++ 4 files changed, 128 insertions(+), 1 deletion(-) create mode 100644 addons/mail/update.py diff --git a/addons/mail/__init__.py b/addons/mail/__init__.py index 92e882ae6d8..abf714ad534 100644 --- a/addons/mail/__init__.py +++ b/addons/mail/__init__.py @@ -33,6 +33,7 @@ import report import wizard import res_config import mail_group_menu +import update # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/mail/data/mail_data.xml b/addons/mail/data/mail_data.xml index 5fe1ee45b1d..522cbe8c7a7 100644 --- a/addons/mail/data/mail_data.xml +++ b/addons/mail/data/mail_data.xml @@ -13,6 +13,20 @@ + + Update Notification + + + 1 + weeks + -1 + + + + + 1000 + + comment diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py index bbf68509277..9c873a31097 100644 --- a/addons/mail/mail_message.py +++ b/addons/mail/mail_message.py @@ -504,7 +504,7 @@ class mail_message(osv.Model): if not values.get('message_id') and values.get('res_id') and values.get('model'): values['message_id'] = tools.generate_tracking_message_id('%(model)s-%(res_id)s' % values) newid = super(mail_message, self).create(cr, uid, values, context) - self._notify(cr, 1, newid, context=context) + self._notify(cr, SUPERUSER_ID, newid, context=context) return newid def read(self, cr, uid, ids, fields=None, context=None, load='_classic_read'): diff --git a/addons/mail/update.py b/addons/mail/update.py new file mode 100644 index 00000000000..0e10ee0a7f1 --- /dev/null +++ b/addons/mail/update.py @@ -0,0 +1,112 @@ +# -*- coding: utf-8 -*- +import datetime +import logging +import sys +import urllib +import urllib2 + +import pooler +import release +from osv import osv, fields +from tools.translate import _ +from tools.safe_eval import safe_eval +from tools.config import config +from tools import misc + +_logger = logging.getLogger(__name__) + +""" +Time interval that will be used to determine up to which date we will +check the logs to see if a message we just received was already logged. +@type: datetime.timedelta +""" +_PREVIOUS_LOG_CHECK = datetime.timedelta(days=365) + +def get_sys_logs(cr, uid): + """ + Utility method to send a publisher warranty get logs messages. + """ + pool = pooler.get_pool(cr.dbname) + + dbuuid = pool.get('ir.config_parameter').get_param(cr, uid, 'database.uuid') + db_create_date = pool.get('ir.config_parameter').get_param(cr, uid, 'database.create_date') + limit_date = datetime.datetime.now() + limit_date = limit_date - datetime.timedelta(15) + limit_date_str = limit_date.strftime(misc.DEFAULT_SERVER_DATETIME_FORMAT) + nbr_users = pool.get("res.users").search(cr, uid, [], count=True) + nbr_active_users = pool.get("res.users").search(cr, uid, [("date", ">=", limit_date_str)], count=True) + nbr_share_users = False + nbr_active_share_users = False + if "share" in pool.get("res.users")._all_columns: + nbr_share_users = pool.get("res.users").search(cr, uid, [("share", "=", True)], count=True) + nbr_active_share_users = pool.get("res.users").search(cr, uid, [("share", "=", True), ("date", ">=", limit_date_str)], count=True) + user = pool.get("res.users").browse(cr, uid, uid) + msg = { + "dbuuid": dbuuid, + "nbr_users": nbr_users, + "nbr_active_users": nbr_active_users, + "nbr_share_users": nbr_share_users, + "nbr_active_share_users": nbr_active_share_users, + "dbname": cr.dbname, + "db_create_date": db_create_date, + "version": release.version, + "language": user.lang, + } + msg.update(pool.get("res.company").read(cr,uid,[1],["name","email","phone"])[0]) + + add_arg = {"timeout":30} if sys.version_info >= (2,6) else {} + arguments = {'arg0': msg, "action": "update",} + arguments_raw = urllib.urlencode(arguments) + + url = config.get("publisher_warranty_url") + uo = urllib2.urlopen(url, arguments_raw, **add_arg) + result = {} + try: + submit_result = uo.read() + result = safe_eval(submit_result) + finally: + uo.close() + return result + +class publisher_warranty_contract(osv.osv): + _name = "publisher_warranty.contract" + + def update_notification(self, cr, uid, ids, cron_mode=True, context=None): + """ + Send a message to OpenERP's publisher warranty server to check the + validity of the contracts, get notifications, etc... + + @param cron_mode: If true, catch all exceptions (appropriate for usage in a cron). + @type cron_mode: boolean + """ + try: + try: + result = get_sys_logs(cr, uid) + except Exception: + if cron_mode: # we don't want to see any stack trace in cron + return False + _logger.debug("Exception while sending a get logs messages", exc_info=1) + raise osv.except_osv(_("Error"), _("Error during communication with the publisher warranty server.")) + limit_date = (datetime.datetime.now() - _PREVIOUS_LOG_CHECK).strftime(misc.DEFAULT_SERVER_DATETIME_FORMAT) + # old behavior based on res.log; now on mail.message, that is not necessarily installed + proxy = self.pool.get('mail.message') + + model, res_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'mail', 'group_all_employees') + + for message in result["messages"]: + values = { + 'body' : message, + 'model' : 'mail.group', + 'res_id' : res_id, + 'user_id' : False, + } + proxy.create(cr, uid, values, context=context) + except Exception: + if cron_mode: + return False # we don't want to see any stack trace in cron + else: + raise + return True + +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: + From e020a58b5fcd7f58cd8b83eba1f970670ef5105f Mon Sep 17 00:00:00 2001 From: niv-openerp Date: Thu, 4 Oct 2012 10:34:33 +0200 Subject: [PATCH 062/128] Removed some tests that are now useless bzr revid: nicolas.vanhoren@openerp.com-20121004083433-86d2y81qdfio086c --- .../test/test_hr_timesheet_sheet.yml | 24 ------------------- 1 file changed, 24 deletions(-) diff --git a/addons/hr_timesheet_sheet/test/test_hr_timesheet_sheet.yml b/addons/hr_timesheet_sheet/test/test_hr_timesheet_sheet.yml index 5ecb3ccba11..71776af61ba 100644 --- a/addons/hr_timesheet_sheet/test/test_hr_timesheet_sheet.yml +++ b/addons/hr_timesheet_sheet/test/test_hr_timesheet_sheet.yml @@ -33,30 +33,6 @@ - !assert {model: hr.employee, id: hr.employee_qdp}: - state == 'present' -- - I want to check attendance and work of yesterday. I click on <- button. -- - !python {model: hr_timesheet_sheet.sheet}: | - date_prev = self.date_previous(cr, uid, [ref('hr_timesheet_sheet_sheet_deddk0')], None) - assert date_prev == True, "I See Previous Date Timesheet" -- - Then I click on "Today" button to fill today's timesheet. -- - !python {model: hr_timesheet_sheet.sheet}: | - date_to = self.date_today(cr, uid, [ref('hr_timesheet_sheet_sheet_deddk0')], None) - assert date_to == True, "I See Today Date Timesheet" -- - I can also move to next day by clicking on -> button. -- - !python {model: hr_timesheet_sheet.sheet}: | - date_next = self.date_next(cr, uid, [ref('hr_timesheet_sheet_sheet_deddk0')], None) - assert date_next == True, "I See Next Date Timesheet" -- - I want to go to a particular date and see attendance then I select the date and click on "Go to:" button. -- - !python {model: hr_timesheet_sheet.sheet}: | - button_dumy = self.button_dummy(cr, uid, [ref('hr_timesheet_sheet_sheet_deddk0')], None) - assert button_dumy == True, "I See Particular Date Attendance" - At the time of logout, I create attendance and perform "Sign Out". - From 147da38fa446ee959a4162a94d8a9fa25a500be1 Mon Sep 17 00:00:00 2001 From: "Quentin (OpenERP)" Date: Thu, 4 Oct 2012 11:02:40 +0200 Subject: [PATCH 063/128] [REV] put back the label for product_uom_qty in sale order line, wrongly removed in a previous commit bzr revid: qdp-launchpad@openerp.com-20121004090240-gckfbpgpymtcxixz --- addons/sale/sale_view.xml | 1 + addons/sale_stock/sale_stock_view.xml | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/addons/sale/sale_view.xml b/addons/sale/sale_view.xml index 2291c015ad3..59b06cd22c0 100644 --- a/addons/sale/sale_view.xml +++ b/addons/sale/sale_view.xml @@ -201,6 +201,7 @@ context="{'partner_id':parent.partner_id, 'quantity':product_uom_qty, 'pricelist':parent.pricelist_id, 'shop':parent.shop_id, 'uom':product_uom}" groups="base.group_user" on_change="product_id_change(parent.pricelist_id, product_id, product_uom_qty, product_uom, product_uos_qty, product_uos, name, parent.partner_id, False, True, parent.date_order, False, parent.fiscal_position, False, context)"/> +