diff --git a/Makefile b/Makefile new file mode 100644 index 00000000000..39ab146c13a --- /dev/null +++ b/Makefile @@ -0,0 +1,43 @@ +# -*- makefile -*- + +addons-path := bin/addons/ +root-path := bin/ +port := 8069 +net_port := 8070 +module := base +database := terp +language := fr_FR +i18n-import := bin/addons/base/i18n/fr_FR.po +interrogation_file := bin/addons/quality_integration_server/base_quality_interrogation.py +login := admin +password := admin + +start: + python $(interrogation_file) start-server --root-path=$(root-path) --addons-path=$(addons-path) --port=$(port) + +create-db: + python $(interrogation_file) create-db --database=$(database) --root-path=$(root-path) --addons-path=$(addons-path) --port=$(port) --login=$(login) --password=$(password) + +drop-db: + python $(interrogation_file) drop-db --database=$(database) --root-path=$(root-path) --addons-path=$(addons-path) --port=$(port) + +install-module: + python $(interrogation_file) install-module --modules=$(module) --database=$(database) --root-path=$(root-path) --addons-path=$(addons-path) --port=$(port) --login=$(login) --password=$(password) + +upgrade-module: + python $(interrogation_file) upgrade-module --modules=$(module) --database=$(database) --root-path=$(root-path) --addons-path=$(addons-path) --port=$(port) --login=$(login) --password=$(password) + + +install-translation: + python $(interrogation_file) install-translation --database=$(database) --translate-in=$(i18n-import) --port=$(port) --login=$(login) --password=$(password) --root-path=$(root-path) --addons-path=$(addons-path) + + +version: + python bin/openerp-server.py --version + +check-quality: + python $(interrogation_file) check-quality --database=$(database) --modules=$(module) --port=$(port) --login=$(login) --password=$(password) --addons-path=$(addons-path) --root-path=$(root-path) + + + + diff --git a/bin/PKG-INFO b/bin/PKG-INFO index 9f04cc77abe..9e9d5d4bcb1 100644 --- a/bin/PKG-INFO +++ b/bin/PKG-INFO @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: OpenERP -Version: 5.0.1 +Version: 5.0.3 Author: Tiny.be Author-email: fp at tiny be Maintainer: Tiny.be diff --git a/bin/addons/base/ir/ir_sequence.py b/bin/addons/base/ir/ir_sequence.py index 202aac754d2..f48ea0f25c4 100644 --- a/bin/addons/base/ir/ir_sequence.py +++ b/bin/addons/base/ir/ir_sequence.py @@ -22,6 +22,7 @@ import time from osv import fields,osv +import pooler class ir_sequence_type(osv.osv): _name = 'ir.sequence.type' @@ -69,13 +70,12 @@ class ir_sequence(osv.osv): 'sec': time.strftime('%S'), } - def get_id(self, cr, uid, sequence_id, test='id=%s', context={}): + def get_id(self, cr, uid, sequence_id, test='id=%s', context=None): try: - cr.execute('lock table ir_sequence') - cr.execute('select id,number_next,number_increment,prefix,suffix,padding from ir_sequence where '+test+' and active=True', (sequence_id,)) + cr.execute('SELECT id, number_next, prefix, suffix, padding FROM ir_sequence WHERE '+test+' AND active=%s FOR UPDATE', (sequence_id, True)) res = cr.dictfetchone() if res: - cr.execute('update ir_sequence set number_next=number_next+number_increment where id=%s and active=True', (res['id'],)) + cr.execute('UPDATE ir_sequence SET number_next=number_next+number_increment WHERE id=%s AND active=%s', (res['id'], True)) if res['number_next']: return self._process(res['prefix']) + '%%0%sd' % res['padding'] % res['number_next'] + self._process(res['suffix']) else: diff --git a/bin/addons/base/res/partner/partner.py b/bin/addons/base/res/partner/partner.py index 6f2b5135fb8..2e5c23c7b6c 100644 --- a/bin/addons/base/res/partner/partner.py +++ b/bin/addons/base/res/partner/partner.py @@ -325,7 +325,10 @@ class res_partner_address(osv.osv): if context.get('contact_display', 'contact')=='partner': ids = self.search(cr, user, [('partner_id',operator,name)], limit=limit, context=context) else: - ids = self.search(cr, user, [('zip','=',name)] + args, limit=limit, context=context) + if not name: + ids = self.search(cr, user, args, limit=limit, context=context) + else: + ids = self.search(cr, user, [('zip','=',name)] + args, limit=limit, context=context) if not ids: ids = self.search(cr, user, [('city',operator,name)] + args, limit=limit, context=context) if name: diff --git a/bin/addons/quality_integration_server/base_quality_interrogation.py b/bin/addons/quality_integration_server/base_quality_interrogation.py new file mode 100755 index 00000000000..2df0af247c7 --- /dev/null +++ b/bin/addons/quality_integration_server/base_quality_interrogation.py @@ -0,0 +1,311 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2009 Tiny SPRL (). All Rights Reserved +# $Id$ +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +############################################################################## + +import xmlrpclib +import ConfigParser +import optparse +import sys +import thread +import threading +import os +import time +import pickle +import base64 +import socket + +admin_passwd = 'admin' +waittime = 10 + +def start_server(root_path, port, addons_path): + if root_path: + root_path += '/' + os.system('python2.5 '+root_path+'openerp-server.py --pidfile=openerp.pid --port=%s --no-netrpc --addons-path=%s' %(str(port),addons_path)) +def clean(): + if os.path.isfile('openerp.pid'): + ps = open('openerp.pid') + if ps: + pid = int(ps.read()) + ps.close() + if pid: + os.kill(pid,9) + +def execute(connector, method, *args): + res = False + try: + res = getattr(connector,method)(*args) + except socket.error,e: + if e.args[0] == 111: + print 'Please wait %d sec to start server....'%(waittime) + time.sleep(waittime) + res = execute(connector, method, *args) + else: + raise e + return res + +def login(uri, dbname, user, pwd): + conn = xmlrpclib.ServerProxy(uri + '/xmlrpc/common') + uid = execute(conn,'login',dbname, user, pwd) + return uid + +def import_translate(uri, user, pwd, dbname, translate_in): + uid = login(uri, dbname, user, pwd) + if uid: + conn = xmlrpclib.ServerProxy(uri + '/xmlrpc/wizard') + wiz_id = execute(conn,'create',dbname, uid, pwd, 'module.lang.import') + for trans_in in translate_in: + lang,ext = os.path.splitext(trans_in.split('/')[-1]) + state = 'init' + datas = {'form':{}} + while state!='end': + res = execute(conn,'execute',dbname, uid, pwd, wiz_id, datas, state, {}) + if 'datas' in res: + datas['form'].update( res['datas'].get('form',{}) ) + if res['type']=='form': + for field in res['fields'].keys(): + datas['form'][field] = res['fields'][field].get('value', False) + state = res['state'][-1][0] + trans_obj = open(trans_in) + datas['form'].update({ + 'name': lang, + 'code': lang, + 'data' : base64.encodestring(trans_obj.read()) + }) + trans_obj.close() + elif res['type']=='action': + state = res['state'] + + +def check_quality(uri, user, pwd, dbname, modules): + uid = login(uri, dbname, user, pwd) + if uid: + conn = xmlrpclib.ServerProxy(uri + '/xmlrpc/object') + qualityresult = {} + final = {} + test_detail = {} + for module in modules: + quality_result = execute(conn,'execute', dbname, uid, pwd,'module.quality.check','check_quality',module) + detail_html = '' + html = '''''' + html +="

Module : %s

"%(quality_result['name']) + html += "

Final score : %s

"%(quality_result['final_score']) + html += "" + for x,y,detail in quality_result['check_detail_ids']: + if detail.get('detail') != '': + test = detail.get('name') + score = round(float(detail.get('score',0)),2) + html += "
  • %s (%.2f)
  • "%(test,test,score) + detail_html +="

    %s (Score : %s)

    %s
    "%(test,test,score,detail.get('detail')) + detail_html +='''Go to Top''' + test_detail[test] = (score,detail.get('detail','')) + html += "
    %s"%(detail_html) + final[quality_result['name']] = (quality_result['final_score'],html,test_detail) + + fp = open('quality_log.pck','wb') + pck_obj = pickle.dump(final,fp) + fp.close() + print "LOG PATH%s"%(os.path.realpath('quality_log.pck')) + return final + else: + print 'Login Failed...' + clean() + sys.exit(1) + + + +def wait(id,url=''): + progress=0.0 + sock2 = xmlrpclib.ServerProxy(url+'/db') + while not progress==1.0: + progress,users = execute(sock2,'get_progress',admin_passwd, id) + return True + + +def create_db(uri, dbname, user='admin', pwd='admin', lang='en_US'): + conn = xmlrpclib.ServerProxy(uri + '/xmlrpc/db') + obj_conn = xmlrpclib.ServerProxy(uri + '/xmlrpc/object') + wiz_conn = xmlrpclib.ServerProxy(uri + '/xmlrpc/wizard') + login_conn = xmlrpclib.ServerProxy(uri + '/xmlrpc/common') + db_list = execute(conn, 'list') + if dbname not in db_list: + id = execute(conn,'create',admin_passwd, dbname, True, lang) + wait(id,uri) + uid = login_conn.login(dbname, user, pwd) + + wiz_id = execute(wiz_conn,'create', dbname, uid, user, 'base_setup.base_setup') + + state = 'init' + datas = {'form':{}} + + while state!='config': + res = execute(wiz_conn, 'execute', dbname, uid, pwd, wiz_id, datas, state, {}) + if state=='init': + datas['form'].update( res['datas'] ) + if res['type']=='form': + for field in res['fields'].keys(): + datas['form'][field] = datas['form'].get(field,False) + state = res['state'][-1][0] + datas['form'].update({ + 'profile': -1 + }) + elif res['type']=='state': + state = res['state'] + res = execute(wiz_conn, 'execute', dbname, uid, pwd, wiz_id, datas, state, {}) + install_module(uri, dbname, ['base_module_quality'],user,pwd) + return True + +def drop_db(uri, dbname): + conn = xmlrpclib.ServerProxy(uri + '/xmlrpc/db') + db_list = execute(conn,'list') + if dbname in db_list: + execute(conn, 'drop', admin_passwd, dbname) + return True + +def install_module(uri, dbname, modules, user='admin', pwd='admin'): + uid = login(uri, dbname, user, pwd) + if uid: + obj_conn = xmlrpclib.ServerProxy(uri + '/xmlrpc/object') + wizard_conn = xmlrpclib.ServerProxy(uri + '/xmlrpc/wizard') + module_ids = execute(obj_conn, 'execute', dbname, uid, pwd, 'ir.module.module', 'search', [('name','in',modules)]) + execute(obj_conn, 'execute', dbname, uid, pwd, 'ir.module.module', 'button_install', module_ids) + wiz_id = execute(wizard_conn, 'create', dbname, uid, pwd, 'module.upgrade.simple') + state = 'init' + datas = {} + #while state!='menu': + while state!='end': + res = execute(wizard_conn, 'execute', dbname, uid, pwd, wiz_id, datas, state, {}) + if state == 'init': + state = 'start' + elif state == 'start': + state = 'end' + return True + +def upgrade_module(uri, dbname, modules, user='admin', pwd='admin'): + uid = login(uri, dbname, user, pwd) + if uid: + obj_conn = xmlrpclib.ServerProxy(uri + '/xmlrpc/object') + wizard_conn = xmlrpclib.ServerProxy(uri + '/xmlrpc/wizard') + module_ids = execute(obj_conn, 'execute', dbname, uid, pwd, 'ir.module.module', 'search', [('name','in',modules)]) + execute(obj_conn, 'execute', dbname, uid, pwd, 'ir.module.module', 'button_upgrade', module_ids) + wiz_id = execute(wizard_conn, 'create', dbname, uid, pwd, 'module.upgrade.simple') + state = 'init' + datas = {} + #while state!='menu': + while state!='end': + res = execute(wizard_conn, 'execute', dbname, uid, pwd, wiz_id, datas, state, {}) + if state == 'init': + state = 'start' + elif state == 'start': + state = 'end' + + return True + + + + + +usage = """%prog command [options] + +Basic Commands: + start-server Start Server + create-db Create new database + drop-db Drop database + install-module Install module + upgrade-module Upgrade module + install-translation Install translation file + check-quality Calculate quality and dump quality result into quality_log.pck using pickle +""" +parser = optparse.OptionParser(usage) +parser.add_option("--modules", dest="modules", + help="specify modules to install or check quality") +parser.add_option("--addons-path", dest="addons_path", help="specify the addons path") +parser.add_option("--root-path", dest="root_path", help="specify the root path") +parser.add_option("-p", "--port", dest="port", help="specify the TCP port", type="int") +parser.add_option("-d", "--database", dest="db_name", help="specify the database name") +parser.add_option("--login", dest="login", help="specify the User Login") +parser.add_option("--password", dest="pwd", help="specify the User Password") +parser.add_option("--translate-in", dest="translate_in", + help="specify .po files to import translation terms") +(opt, args) = parser.parse_args() +if len(args) != 1: + parser.error("incorrect number of arguments") +command = args[0] +if command not in ('start-server','create-db','drop-db','install-module','upgrade-module','check-quality','install-translation'): + parser.error("incorrect command") + +def die(cond, msg): + if cond: + print msg + sys.exit(1) + +die(opt.modules and (not opt.db_name), + "the modules option cannot be used without the database (-d) option") + +die(opt.translate_in and (not opt.db_name), + "the translate-in option cannot be used without the database (-d) option") + +options = { + 'addons-path' : opt.addons_path or 'addons', + 'root-path' : opt.root_path or '', + 'translate-in': opt.translate_in, + 'port' : opt.port or 8069, + 'database': opt.db_name or 'terp', + 'modules' : opt.modules or [], + 'login' : opt.login or 'admin', + 'pwd' : opt.pwd or '', +} + +options['modules'] = opt.modules and map(lambda m: m.strip(), opt.modules.split(',')) or [] +options['translate_in'] = opt.translate_in and map(lambda m: m.strip(), opt.translate_in.split(',')) or [] +uri = 'http://localhost:' + str(options['port']) + +server_thread = threading.Thread(target=start_server, + args=(options['root-path'], options['port'], options['addons-path'])) +try: + server_thread.start() + if command == 'create-db': + create_db(uri, options['database'], options['login'], options['pwd']) + if command == 'drop-db': + drop_db(uri, options['database']) + if command == 'install-module': + install_module(uri, options['database'], options['modules'], options['login'], options['pwd']) + if command == 'upgrade-module': + upgrade_module(uri, options['database'], options['modules'], options['login'], options['pwd']) + if command == 'check-quality': + check_quality(uri, options['login'], options['pwd'], options['database'], options['modules']) + if command == 'install-translation': + import_translate(uri, options['login'], options['pwd'], options['database'], options['translate_in']) + clean() + sys.exit(0) + +except xmlrpclib.Fault, e: + print e.faultString + clean() + sys.exit(1) +except Exception, e: + print e + clean() + sys.exit(1) + + + + + diff --git a/bin/netsvc.py b/bin/netsvc.py index ab8fcec4bc8..dfaabe4d362 100644 --- a/bin/netsvc.py +++ b/bin/netsvc.py @@ -180,10 +180,11 @@ class Logger(object): msg = tools.exception_to_unicode(msg) try: + msg = tools.ustr(msg).strip() if level in (LOG_ERROR,LOG_CRITICAL) and tools.config.get_misc('debug','env_info',True): msg = common().get_server_environment() + msg - result = tools.ustr(msg).strip().split('\n') + result = msg.split('\n') except UnicodeDecodeError: result = msg.strip().split('\n') try: diff --git a/bin/osv/expression.py b/bin/osv/expression.py index 3b36d4f95d3..8acc7d3a218 100644 --- a/bin/osv/expression.py +++ b/bin/osv/expression.py @@ -103,14 +103,23 @@ class expression(object): left, operator, right = e working_table = table - if left in table._inherit_fields: - working_table = table.pool.get(table._inherit_fields[left][0]) - if working_table not in self.__tables.values(): - self.__joins.append(('%s.%s=%s.%s' % (working_table._table, 'id', table._table, table._inherits[working_table._name]), working_table._table)) - - self.__tables[i] = working_table - + main_table = table fargs = left.split('.', 1) + index = i + if left in table._inherit_fields: + while True: + field = main_table._columns.get(fargs[0], False) + if field: + working_table = main_table + self.__tables[i] = working_table + break + working_table = main_table.pool.get(main_table._inherit_fields[left][0]) + if working_table not in self.__tables.values(): + self.__joins.append(('%s.%s=%s.%s' % (working_table._table, 'id', main_table._table, main_table._inherits[working_table._name]), working_table._table)) + self.__tables[index] = working_table + index += 1 + main_table = working_table + field = working_table._columns.get(fargs[0], False) if not field: if left == 'id' and operator == 'child_of': diff --git a/bin/osv/fields.py b/bin/osv/fields.py index 51b5dd1c51e..ee235e95eca 100644 --- a/bin/osv/fields.py +++ b/bin/osv/fields.py @@ -53,6 +53,7 @@ def _symbol_set(symb): class _column(object): _classic_read = True _classic_write = True + _prefetch = True _properties = False _type = 'unknown' _obj = None @@ -196,6 +197,7 @@ class binary(_column): _symbol_get = lambda self, x: x and str(x) _classic_read = False + _prefetch = False def __init__(self, string='unknown', filters=None, **args): _column.__init__(self, string=string, **args) @@ -350,6 +352,7 @@ class many2one(_column): class one2many(_column): _classic_read = False _classic_write = False + _prefetch = False _type = 'one2many' def __init__(self, obj, fields_id, string='unknown', limit=None, **args): @@ -426,11 +429,13 @@ class one2many(_column): return res def set(self, cr, obj, id, field, values, user=None, context=None): + result = [] if not context: context = {} if self._context: context = context.copy() context.update(self._context) + context['no_store_function'] = True if not values: return _table = obj.pool.get(self._obj)._table @@ -438,7 +443,8 @@ class one2many(_column): for act in values: if act[0] == 0: act[2][self._fields_id] = id - obj.create(cr, user, act[2], context=context) + id_new = obj.create(cr, user, act[2], context=context) + result += obj._store_get_values(cr, user, [id_new], act[2].keys(), context) elif act[0] == 1: obj.write(cr, user, [act[1]], act[2], context=context) elif act[0] == 2: @@ -455,6 +461,7 @@ class one2many(_column): cr.execute('select id from '+_table+' where '+self._fields_id+'=%s and id not in ('+','.join(map(str, ids2))+')', (id,)) ids3 = map(lambda x:x[0], cr.fetchall()) obj.write(cr, user, ids3, {self._fields_id:False}, context=context or {}) + return result def search(self, cr, obj, args, name, value, offset=0, limit=None, uid=None, operator='like'): return obj.pool.get(self._obj).name_search(cr, uid, value, self._domain, offset, limit) @@ -472,6 +479,7 @@ class one2many(_column): class many2many(_column): _classic_read = False _classic_write = False + _prefetch = False _type = 'many2many' def __init__(self, obj, rel, id1, id2, string='unknown', limit=None, **args): @@ -582,6 +590,7 @@ class many2many(_column): class function(_column): _classic_read = False _classic_write = False + _prefetch = False _type = 'function' _properties = True diff --git a/bin/osv/orm.py b/bin/osv/orm.py index 880fed339d5..403806a8ccd 100644 --- a/bin/osv/orm.py +++ b/bin/osv/orm.py @@ -174,7 +174,7 @@ class browse_record(object): return None # if the field is a classic one or a many2one, we'll fetch all classic and many2one fields - if col._classic_write: + if col._prefetch: # gen the list of "local" (ie not inherited) fields which are classic or many2one ffields = filter(lambda x: x[1]._classic_write, self._table._columns.items()) # gen the list of inherited fields @@ -673,7 +673,7 @@ class orm_template(object): if fields_def[field[len(prefix)]]['type'] == 'integer': res = line[i] and int(line[i]) elif fields_def[field[len(prefix)]]['type'] == 'boolean': - res = line[i] and bool(line[i]) + res = line[i].lower() not in ('0', 'false', 'off') elif fields_def[field[len(prefix)]]['type'] == 'float': res = line[i] and float(line[i]) elif fields_def[field[len(prefix)]]['type'] == 'selection': @@ -1444,10 +1444,10 @@ class orm_memory(orm_template): self._validate(cr, user, [id_new], context) wf_service = netsvc.LocalService("workflow") wf_service.trg_write(user, self._name, id_new, cr) - self.vaccum(cr, user) return id_new def create(self, cr, user, vals, context=None): + self.vaccum(cr, user) self.next_id += 1 id_new = self.next_id default = [] @@ -1471,7 +1471,6 @@ class orm_memory(orm_template): self._validate(cr, user, [id_new], context) wf_service = netsvc.LocalService("workflow") wf_service.trg_create(user, self._name, id_new, cr) - self.vaccum(cr, user) return id_new def default_get(self, cr, uid, fields_list, context=None): @@ -2056,7 +2055,9 @@ class orm(orm_template): self._inherits_reload_src() def fields_get(self, cr, user, fields=None, context=None): - read_access = self.pool.get('ir.model.access').check(cr, user, self._name, 'write', raise_exception=False, context=context) + ira = self.pool.get('ir.model.access') + read_access = ira.check(cr, user, self._name, 'write', raise_exception=False, context=context) or \ + ira.check(cr, user, self._name, 'create', raise_exception=False, context=context) return super(orm, self).fields_get(cr, user, fields, context, read_access) def read(self, cr, user, ids, fields=None, context=None, load='_classic_read'): @@ -2361,6 +2362,7 @@ class orm(orm_template): if not edit: vals.pop(field) + if not context: context = {} if not ids: @@ -2372,6 +2374,7 @@ class orm(orm_template): self.pool.get('ir.model.access').check(cr, user, self._name, 'write', context=context) + upd0 = [] upd1 = [] upd_todo = [] @@ -2448,6 +2451,7 @@ class orm(orm_template): src_trans = self.pool.get(self._name).read(cr,user,ids,[f]) self.pool.get('ir.translation')._set_ids(cr, user, self._name+','+f, 'model', context['lang'], ids, vals[f], src_trans[0][f]) + # call the 'set' method of fields which are not classic_write upd_todo.sort(lambda x, y: self._columns[x].priority-self._columns[y].priority) @@ -2549,12 +2553,12 @@ class orm(orm_template): for (t, c) in self._inherits.items(): if c in vals: avoid_table.append(t) - for f in self._columns.keys(): # + self._inherit_fields.keys(): - if not f in vals: + for f in self._columns.keys(): + if (not f in vals) and (not isinstance(self._columns[f], fields.property)): default.append(f) for f in self._inherit_fields.keys(): - if (not f in vals) and (self._inherit_fields[f][0] not in avoid_table): + if (not f in vals) and (self._inherit_fields[f][0] not in avoid_table) and (not isinstance(self._inherit_fields[f][2], fields.property)): default.append(f) if len(default): @@ -2563,7 +2567,6 @@ class orm(orm_template): if dv in self._columns and self._columns[dv]._type == 'many2many': if default_values[dv] and isinstance(default_values[dv][0], (int, long)): default_values[dv] = [(6, 0, default_values[dv])] - vals.update(default_values) tocreate = {} @@ -2666,13 +2669,19 @@ class orm(orm_template): if c[0].startswith('default_'): del rel_context[c[0]] + result = [] for field in upd_todo: - self._columns[field].set(cr, self, id_new, field, vals[field], user, rel_context) + result += self._columns[field].set(cr, self, id_new, field, vals[field], user, rel_context) or [] self._validate(cr, user, [id_new], context) - result = self._store_get_values(cr, user, [id_new], vals.keys(), context) - for order, object, ids, fields in result: - self.pool.get(object)._store_set_values(cr, user, ids, fields, context) + if not context.get('no_store_function', False): + result += self._store_get_values(cr, user, [id_new], vals.keys(), context) + result.sort() + done = [] + for order, object, ids, fields2 in result: + if not (object, ids, fields2) in done: + self.pool.get(object)._store_set_values(cr, user, ids, fields2, context) + done.append((object, ids, fields2)) wf_service = netsvc.LocalService("workflow") wf_service.trg_create(user, self._name, id_new, cr) @@ -2682,6 +2691,15 @@ class orm(orm_template): result = {} fncts = self.pool._store_function.get(self._name, []) for fnct in range(len(fncts)): + if fncts[fnct][3]: + ok = False + for f in (fields or []): + if f in fncts[fnct][3]: + ok = True + break + if not ok: + continue + result.setdefault(fncts[fnct][0], {}) ids2 = fncts[fnct][2](self,cr, uid, ids, context) for id in filter(None, ids2): diff --git a/bin/release.py b/bin/release.py index 687eb12b009..27fb87a0fe1 100644 --- a/bin/release.py +++ b/bin/release.py @@ -22,7 +22,7 @@ ############################################################################## name = 'openerp-server' -version = '5.0.3-bzr' +version = '5.0.3' major_version = '5.0' description = 'OpenERP Server' long_desc = '''\ diff --git a/bin/report/print_xml.py b/bin/report/print_xml.py index e67108824b0..63b342457bd 100644 --- a/bin/report/print_xml.py +++ b/bin/report/print_xml.py @@ -240,7 +240,7 @@ class document(object): txt = str(datas[atr['value']]) else: txt = datas[atr['value']] - el.append(txt) + el.text = txt else: for el_cld in node: parse_result_tree(el_cld, el, datas) diff --git a/bin/report/printscreen/ps_list.py b/bin/report/printscreen/ps_list.py index 7ec98c1a0ab..8534fd5c887 100644 --- a/bin/report/printscreen/ps_list.py +++ b/bin/report/printscreen/ps_list.py @@ -51,7 +51,7 @@ class report_printscreen_list(report_int): return result def _parse_string(self, view): - dom = etree.XML(unicode(view, 'utf-8').encode('utf-8')) + dom = etree.XML(view) return self._parse_node(dom) def create(self, cr, uid, ids, datas, context=None): @@ -139,7 +139,7 @@ class report_printscreen_list(report_int): for f in fields_order: field = etree.Element("field") - field.text = fields[f]['string'] or '' + field.text = tools.ustr(fields[f]['string'] or '') header.append(field) new_doc.append(header) @@ -148,12 +148,12 @@ class report_printscreen_list(report_int): count = len(fields_order) for i in range(0,count): tsum.append(0) - + for line in results: node_line = etree.Element("row") count = -1 for f in fields_order: - + float_flag = 0 count += 1 if fields[f]['type']=='many2one' and line[f]: @@ -170,7 +170,9 @@ class report_printscreen_list(report_int): if fields[f]['type'] == 'float' and line[f]: precision=(('digits' in fields[f]) and fields[f]['digits'][1]) or 2 - line[f]='%.2f'%(line[f]) + prec ='%.' + str(precision) +'f' + line[f]=prec%(line[f]) + float_flag = 1 if fields[f]['type'] == 'date' and line[f]: format = str(locale.nl_langinfo(locale.D_FMT).replace('%y', '%Y')) @@ -195,8 +197,12 @@ class report_printscreen_list(report_int): col.set('tree','no') if line[f] != None: col.text = tools.ustr(line[f] or '') + if float_flag: + col.set('tree','float') if temp[count] == 1: tsum[count] = float(tsum[count]) + float(line[f]); + + else: col.text = '/' node_line.append(col) @@ -210,16 +216,18 @@ class report_printscreen_list(report_int): col.set('tree','no') if tsum[f] != None: if tsum[f] >= 0.01 : - total = '%.2f'%(tsum[f]) - txt = str(total or '') - else : - txt = str(tsum[f] or '') + prec = '%.' + str(tools.config['price_accuracy']) + 'f' + total = prec%(tsum[f]) + txt = str(total or '') + col.set('tree','float') + else: + txt = str(tsum[f] or '') else: txt = '/' if f == 0: txt ='Total' - col.text = txt + col.text = tools.ustr(txt or '') node_line.append(col) lines.append(node_line) diff --git a/bin/report/render/rml2pdf/trml2pdf.py b/bin/report/render/rml2pdf/trml2pdf.py index 016b0f09e45..5e7ba57e4f9 100644 --- a/bin/report/render/rml2pdf/trml2pdf.py +++ b/bin/report/render/rml2pdf/trml2pdf.py @@ -236,7 +236,8 @@ class _rml_canvas(object): self.canvas.setTitle(self.title) def _textual(self, node, x=0, y=0): - rc = utils._process_text(self, node.text.encode('utf-8') or '') + text = node.text and node.text.encode('utf-8') or '' + rc = utils._process_text(self, text) for n in node: if n.tag == 'seq': from reportlab.lib.sequencer import getSequencer diff --git a/bin/report/report_sxw.py b/bin/report/report_sxw.py index 616b10beeb7..d8e3570454b 100644 --- a/bin/report/report_sxw.py +++ b/bin/report/report_sxw.py @@ -189,7 +189,7 @@ class rml_parse(object): return newtag, attrs def format(self, text, oldtag=None): - return text + return text.strip() def removeParentNode(self, tag=None): raise Exception('Skip') @@ -420,12 +420,14 @@ class report_sxw(report_rml, preprocess.report): return s.getvalue(), results[0][1] return self.create_single_pdf(cr, uid, ids, data, report_xml, context) - def create_single_pdf(self, cr, uid, ids, data, report_xml, context={}): + def create_single_pdf(self, cr, uid, ids, data, report_xml, context=None): + if not context: + context={} logo = None context = context.copy() title = report_xml.name rml = report_xml.report_rml_content - rml_parser = self.parser(cr, uid, self.name2, context) + rml_parser = self.parser(cr, uid, self.name2, context=context) objs = self.getObjects(cr, uid, ids, context) rml_parser.set_context(objs, data, ids, report_xml.report_type) processed_rml = self.preprocess_rml(etree.XML(rml),report_xml.report_type) @@ -437,7 +439,9 @@ class report_sxw(report_rml, preprocess.report): pdf = create_doc(etree.tostring(processed_rml),rml_parser.localcontext,logo,title.encode('utf8')) return (pdf, report_xml.report_type) - def create_single_odt(self, cr, uid, ids, data, report_xml, context={}): + def create_single_odt(self, cr, uid, ids, data, report_xml, context=None): + if not context: + context={} context = context.copy() report_type = report_xml.report_type context['parents'] = sxw_parents @@ -447,7 +451,7 @@ class report_sxw(report_rml, preprocess.report): meta = sxw_z.read('meta.xml') sxw_z.close() - rml_parser = self.parser(cr, uid, self.name2, context) + rml_parser = self.parser(cr, uid, self.name2, context=context) rml_parser.parents = sxw_parents rml_parser.tag = sxw_tag objs = self.getObjects(cr, uid, ids, context) @@ -514,7 +518,7 @@ class report_sxw(report_rml, preprocess.report): if report_xml.header: #Add corporate header/footer rml = tools.file_open(os.path.join('base', 'report', 'corporate_%s_header.xml' % report_type)).read() - rml_parser = self.parser(cr, uid, self.name2, context) + rml_parser = self.parser(cr, uid, self.name2, context=context) rml_parser.parents = sxw_parents rml_parser.tag = sxw_tag objs = self.getObjects(cr, uid, ids, context) @@ -532,13 +536,15 @@ class report_sxw(report_rml, preprocess.report): sxw_io.close() return (final_op, report_type) - def create_single_html2html(self, cr, uid, ids, data, report_xml, context={}): + def create_single_html2html(self, cr, uid, ids, data, report_xml, context=None): + if not context: + context = {} context = context.copy() report_type = 'html' context['parents'] = html_parents html = report_xml.report_rml_content - html_parser = self.parser(cr, uid, self.name2, context) + html_parser = self.parser(cr, uid, self.name2, context=context) html_parser.parents = html_parents html_parser.tag = sxw_tag objs = self.getObjects(cr, uid, ids, context) diff --git a/bin/tools/misc.py b/bin/tools/misc.py index 2be922edb5d..c70f254d5c0 100644 --- a/bin/tools/misc.py +++ b/bin/tools/misc.py @@ -612,9 +612,16 @@ class cache(object): """ def to_tuple(d): - i = d.items() - i.sort(key=lambda (x,y): x) - return tuple(i) + pairs = d.items() + pairs.sort(key=lambda (k,v): k) + for i, (k, v) in enumerate(pairs): + if isinstance(v, dict): + pairs[i] = (k, to_tuple(v)) + if isinstance(v, (list, set)): + pairs[i] = (k, tuple(v)) + elif not is_hashable(v): + pairs[i] = (k, repr(v)) + return tuple(pairs) if not self.multi: key = (('dbname', dbname),) + to_tuple(kwargs2) @@ -631,12 +638,6 @@ class cache(object): kwargs2 = self.fun_default_values.copy() kwargs2.update(kwargs) kwargs2.update(dict(zip(self.fun_arg_names, args[self.skiparg-2:]))) - for k in kwargs2: - if isinstance(kwargs2[k], (list, dict, set)): - kwargs2[k] = tuple(kwargs2[k]) - elif not is_hashable(kwargs2[k]): - kwargs2[k] = repr(kwargs2[k]) - return kwargs2 def clear(self, dbname, *args, **kwargs): diff --git a/bin/tools/translate.py b/bin/tools/translate.py index ff8d2bcf791..01789857a94 100644 --- a/bin/tools/translate.py +++ b/bin/tools/translate.py @@ -344,8 +344,9 @@ def trans_export(lang, modules, buffer, format, dbname=None): tmpmoddir = join(tmpdir, mod, 'i18n') os.makedirs(tmpmoddir) pofilename = (newlang and mod or lang) + ".po" + (newlang and 't' or '') - buf = open(join(tmpmoddir, pofilename), 'w') + buf = file(join(tmpmoddir, pofilename), 'w') _process('po', [mod], modrows, buf, lang, newlang) + buf.close() tar = tarfile.open(fileobj=buffer, mode='w|gz') tar.add(tmpdir, '') diff --git a/bin/workflow/instance.py b/bin/workflow/instance.py index 8f1c8455598..ce78807d959 100644 --- a/bin/workflow/instance.py +++ b/bin/workflow/instance.py @@ -43,7 +43,7 @@ def delete(cr, ident): def validate(cr, inst_id, ident, signal, force_running=False): cr.execute("select * from wkf_workitem where inst_id=%s", (inst_id,)) - stack = None + stack = [] for witem in cr.dictfetchall(): stack = [] workitem.process(cr, witem, ident, signal, force_running, stack=stack) diff --git a/doc/Changelog b/doc/Changelog index 064ed637e9e..dea6b6ce58e 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,8 +1,85 @@ +2009-08-21: 5.0.2 +================= + +Bugfixes (server) +----------------- + + * Not linked to a bug report: + * set_alarm: removed unwanted comment + * report engine: context argument was passed at a wrong position. Now it's passed as a named argument. + * fields_get check access aginst write or create access + * Not allowing only white spaces text to be printed + * sequences: avoid an sql error when locking table... + * report engine: fixed a bug when the xml tag is empty + * bin/workflow: 'stack' variable was not initialized when sql query returned an empty set. + * security issue: avoid access to inactive users + * security issue: avoid access with 'None' password (Thanks to P. Christeas for the bug report) + * avoid a bug when look in stack when translate code strings + + * https://launchpad.net/bugs/415257 + * import of boolean fields in csv files. + * https://launchpad.net/bugs/415014 + * correct translation tgz archive creation + * https://launchpad.net/bugs/362280 + * https://launchpad.net/bugs/416883 + * Print screen unicode-decode error solved + * https://launchpad.net/bugs/400378 + * partner address search + * https://launchpad.net/bugs/415972 + * do not get next sequence in its own cursor + * https://launchpad.net/bugs/413484 + * Print Screen : was not considering field names with superscripted,subscripted characters(ex.m²) + * https://launchpad.net/bugs/406945 + * import of boolean fields in csv files. + * https://launchpad.net/bugs/413586 + * bug in cache system that altered some arguments passed to cached function + * https://launchpad.net/bugs/413594 + * logger: convert the message in unicode before concatenate it with server environment + * https://launchpad.net/bugs/348217 + * Secure option on config file + + +Improvements (server) +--------------------- + + * Makefile and quality_integration_server module for Integration server + * Speed impprovement: 2x faster for flow: sale -> invoice -> payment + * Speed improvement in creation of records (4 time faster on partners) + + +Bugfixes (addons) +----------------- + + * Not linked to a bug report: + * Purchase :copy method was not setting stock moves to be null for PO lines + * Module:account Fix keyerror in general_ledger report + * Access rules stock / worker + * mrp: fr_FR.po file had a duplicate message definition + + * https://launchpad.net/bugs/413699 + * https://launchpad.net/bugs/415014 + * report_task: regenerate translations files + * https://launchpad.net/bugs/397100 + * improving commit on report + * https://launchpad.net/bugs/416296 + * Base_setup : Country passed in values only if filled + * https://launchpad.net/bugs/415218 + * report_task : report_task_user_pipeline_open was having no uniqueness + + +Improvements (addons) +--------------------- + + * POS: improved the point_of_sale receipt + + + 2009-08-12: 5.0.2 ================= Bugfixes (server) ----------------- + * Not Linked to a bug report: * ensure sys is imported * pass the context to check method of 'ir.model.access' @@ -130,6 +207,7 @@ Bugfixes (server) Improvements (server) --------------------- + * Logger notification improved * update po(t) files * new method that allow the server to return a message that will be display on login page @@ -146,6 +224,7 @@ Improvements (server) Bugfixes (addons) ----------------- + * Not linked to a bug report: * DMS directory not translatable + translations improvement * Project : Searching active=no was failing, corrected @@ -339,6 +418,7 @@ Bugfixes (addons) Improvements (addons) --------------------- + * update po(t) files * Module:account Api changes def _refund_cleanup_lines(self, lines): added cr,uid arguments * pyflakes test display result improve on base_module_quality @@ -381,37 +461,46 @@ Improvements (addons) 2009-05-26: 5.0.1 ================= + TODO 2009-02-12: 5.0.0-3 =================== + TODO 2009-02-08: 5.0.0-2 =================== + TODO 2009-02-06: 5.0.0 ================= + TODO 2009-01-03: 5.0.0-rc3 ===================== + TODO 2008-12-22: 5.0.0-rc2 ===================== + TODO 2008-12-01: 5.0.0-rc1.1 ======================= + TODO 2008-11-28: 5.0.0-rc1 ===================== + TODO 2008-11-03: 5.0.0-alpha ======================= + TODO