bzr revid: nch@tinyerp.com-20091009095815-xa0ec5gn1zs0gcig
This commit is contained in:
nch@tinyerp.com 2009-10-09 15:28:15 +05:30
commit 797b36fdb0
15 changed files with 600 additions and 220 deletions

View File

@ -1,6 +1,7 @@
# -*- makefile -*-
addons-path := bin/addons/
extra-addons:= ''
root-path := bin/
port := 8069
net_port := 8070
@ -22,7 +23,7 @@ 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)
python $(interrogation_file) install-module --modules=$(module) --database=$(database) --root-path=$(root-path) --addons-path=$(addons-path) --extra-addons=$(extra-addons) --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)

View File

@ -284,57 +284,57 @@ class ir_model_fields(osv.osv):
'complete_name': fields.char('Complete Name', size=64, select=1),
}
def name_search(self, cr, uid, name, args=None, operator='ilike', context=None, limit=800):
def get_fields(cr, uid, field, rel):
result = []
mobj = self.pool.get('ir.model')
id = mobj.search(cr, uid, [('model','=',rel)])
def name_search(self, cr, uid, name, args=None, operator='ilike', context=None, limit=80):
return super(ir_model_fields, self).name_search(cr, uid, name, args, operator, context, limit)
# def get_fields(cr, uid, field, rel):
# result = []
# mobj = self.pool.get('ir.model')
# id = mobj.search(cr, uid, [('model','=',rel)])
obj = self.pool.get('ir.model.fields')
ids = obj.search(cr, uid, [('model_id','in',id)])
records = obj.read(cr, uid, ids)
for record in records:
id = record['id']
fld = field + '/' + record['name']
# obj = self.pool.get('ir.model.fields')
# ids = obj.search(cr, uid, [('model_id','in',id)])
# records = obj.read(cr, uid, ids)
# for record in records:
# id = record['id']
# fld = field + '/' + record['name']
result.append((id, fld))
return result
# result.append((id, fld))
# return result
if not args:
args=[]
if not context:
context={}
return super(ir_model_fields, self).name_search(cr, uid, name, args, operator, context, limit)
# if not args:
# args=[]
# if not context:
# context={}
# return super(ir_model_fields, self).name_search(cr, uid, name, args, operator, context, limit)
if context.get('key') != 'server_action':
return super(ir_model_fields, self).name_search(cr, uid, name, args, operator, context, limit)
# if context.get('key') != 'server_action':
# return super(ir_model_fields, self).name_search(cr, uid, name, args, operator, context, limit)
# result = []
# obj = self.pool.get('ir.model.fields')
# ids = obj.search(cr, uid, args)
# records = obj.read(cr, uid, ids)
# for record in records:
# id = record['id']
# field = record['name']
result = []
obj = self.pool.get('ir.model.fields')
ids = obj.search(cr, uid, args)
records = obj.read(cr, uid, ids)
for record in records:
id = record['id']
field = record['name']
# if record['ttype'] == 'many2one':
# rel = record['relation']
# res = get_fields(cr, uid, field, record['relation'])
# for rs in res:
# result.append(rs)
if record['ttype'] == 'many2one':
rel = record['relation']
res = get_fields(cr, uid, field, record['relation'])
for rs in res:
result.append(rs)
# result.append((id, field))
result.append((id, field))
# for rs in result:
# obj.write(cr, uid, [rs[0]], {'complete_name':rs[1]})
for rs in result:
obj.write(cr, uid, [rs[0]], {'complete_name':rs[1]})
# iids = []
# for rs in result:
# iids.append(rs[0])
iids = []
for rs in result:
iids.append(rs[0])
# result = super(ir_model_fields, self).name_search(cr, uid, name, [('complete_name','ilike',name), ('id','in',iids)], operator, context, limit)
result = super(ir_model_fields, self).name_search(cr, uid, name, [('complete_name','ilike',name), ('id','in',iids)], operator, context, limit)
return result
# return result
ir_model_fields()

View File

@ -27,7 +27,8 @@ import tools
import pooler
from osv import fields,osv
next_wait = 60
def str2tuple(s):
return eval('tuple(%s)' % s)
_intervalTypes = {
'work_days': lambda interval: DateTime.RelativeDateTime(days=interval),
@ -67,27 +68,39 @@ class ir_cron(osv.osv, netsvc.Agent):
'doall' : lambda *a: 1
}
def _check_args(self, cr, uid, ids, context=None):
try:
for this in self.browse(cr, uid, ids, context):
str2tuple(this.args)
except:
return False
return True
_constraints= [
(_check_args, 'Invalid arguments', ['args']),
]
def _callback(self, cr, uid, model, func, args):
args = (args or []) and eval(args)
m=self.pool.get(model)
args = str2tuple(args)
m = self.pool.get(model)
if m and hasattr(m, func):
f = getattr(m, func)
f(cr, uid, *args)
try:
f(cr, uid, *args)
except Exception, e:
self._logger.notifyChannel('timers', netsvc.LOG_ERROR, "Job call of self.pool.get('%s').%s(cr, uid, *%r) failed" % (model, func, args))
self._logger.notifyChannel('timers', netsvc.LOG_ERROR, tools.exception_to_unicode(e))
def _poolJobs(self, db_name, check=False):
def _poolJobs(self, db_name, check=False):
try:
db, pool = pooler.get_db_and_pool(db_name)
except:
return False
if pool._init:
# retry in a few minutes
next_call = 600
else:
next_call = next_wait
return False
try:
cr = db.cursor()
now = DateTime.now()
try:
if not pool._init:
now = DateTime.now()
cr.execute('select * from ir_cron where numbercall<>0 and active and nextcall<=now() order by priority')
for job in cr.dictfetchall():
nextcall = DateTime.strptime(job['nextcall'], '%Y-%m-%d %H:%M:%S')
@ -107,16 +120,38 @@ class ir_cron(osv.osv, netsvc.Agent):
addsql = ', active=False'
cr.execute("update ir_cron set nextcall=%s, numbercall=%s"+addsql+" where id=%s", (nextcall.strftime('%Y-%m-%d %H:%M:%S'), numbercall, job['id']))
cr.commit()
finally:
cr.commit()
cr.close()
#
# Can be improved to do at the min(min(nextcalls), time()+next_call)
# But is this an improvement ?
#
if not check:
self.setAlarm(self._poolJobs, int(time.time()) + next_call, db_name, db_name)
cr.execute('select min(nextcall) as min_next_call from ir_cron where numbercall<>0 and active and nextcall>=now()')
next_call = cr.dictfetchone()['min_next_call']
if next_call:
next_call = time.mktime(time.strptime(next_call, '%Y-%m-%d %H:%M:%S'))
else:
next_call = int(time.time()) + 3600 # if do not find active cron job from database, it will run again after 1 day
if not check:
self.setAlarm(self._poolJobs, next_call, db_name, db_name)
finally:
cr.commit()
cr.close()
def create(self, cr, uid, vals, context=None):
res = super(ir_cron, self).create(cr, uid, vals, context=context)
cr.commit()
self._poolJobs(cr.dbname)
return res
def write(self, cr, user, ids, vals, context=None):
res = super(ir_cron, self).write(cr, user, ids, vals, context=context)
cr.commit()
self._poolJobs(cr.dbname)
return res
def unlink(self, cr, uid, ids, context=None):
res = super(ir_cron, self).unlink(cr, uid, ids, context=context)
cr.commit()
self._poolJobs(cr.dbname)
return res
ir_cron()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -445,10 +445,11 @@ class ir_model_data(osv.osv):
id = False
return id
def _update(self,cr, uid, model, module, values, xml_id=False, store=True, noupdate=False, mode='init', res_id=False):
def _update(self,cr, uid, model, module, values, xml_id=False, store=True, noupdate=False, mode='init', res_id=False, context=None):
warning = True
model_obj = self.pool.get(model)
context = {}
if not context:
context = {}
if xml_id and ('.' in xml_id):
assert len(xml_id.split('.'))==2, _('"%s" contains too many dots. XML ids should not contain dots ! These are used to refer to other modules data, as in module.reference_id') % (xml_id)
warning = False
@ -471,12 +472,12 @@ class ir_model_data(osv.osv):
res_id,action_id = res_id2,action_id2
if action_id and res_id:
model_obj.write(cr, uid, [res_id], values)
model_obj.write(cr, uid, [res_id], values, context=context)
self.write(cr, uid, [action_id], {
'date_update': time.strftime('%Y-%m-%d %H:%M:%S'),
})
},context=context)
elif res_id:
model_obj.write(cr, uid, [res_id], values)
model_obj.write(cr, uid, [res_id], values, context=context)
if xml_id:
self.create(cr, uid, {
'name': xml_id,
@ -484,21 +485,21 @@ class ir_model_data(osv.osv):
'module':module,
'res_id':res_id,
'noupdate': noupdate,
})
},context=context)
if model_obj._inherits:
for table in model_obj._inherits:
inherit_id = model_obj.browse(cr, uid,
res_id)[model_obj._inherits[table]]
res_id,context=context)[model_obj._inherits[table]]
self.create(cr, uid, {
'name': xml_id + '_' + table.replace('.', '_'),
'model': table,
'module': module,
'res_id': inherit_id,
'noupdate': noupdate,
})
},context=context)
else:
if mode=='init' or (mode=='update' and xml_id):
res_id = model_obj.create(cr, uid, values)
res_id = model_obj.create(cr, uid, values, context=context)
if xml_id:
self.create(cr, uid, {
'name': xml_id,
@ -506,18 +507,18 @@ class ir_model_data(osv.osv):
'module': module,
'res_id': res_id,
'noupdate': noupdate
})
},context=context)
if model_obj._inherits:
for table in model_obj._inherits:
inherit_id = model_obj.browse(cr, uid,
res_id)[model_obj._inherits[table]]
res_id,context=context)[model_obj._inherits[table]]
self.create(cr, uid, {
'name': xml_id + '_' + table.replace('.', '_'),
'model': table,
'module': module,
'res_id': inherit_id,
'noupdate': noupdate,
})
},context=context)
if xml_id:
if res_id:
self.loads[(module, xml_id)] = (model, res_id)

View File

@ -26,6 +26,7 @@ import ir
from tools.misc import currency
from tools.translate import _
from tools import config
import mx.DateTime
from mx.DateTime import RelativeDateTime, now, DateTime, localtime
@ -67,7 +68,7 @@ class res_currency(osv.osv):
if currency.rounding == 0:
return 0.0
else:
return round(amount / currency.rounding) * currency.rounding
return round(amount / currency.rounding, int(config['price_accuracy'])) * currency.rounding
def is_zero(self, cr, uid, currency, amount):
return abs(self.round(cr, uid, currency, amount)) < currency.rounding

View File

@ -378,6 +378,7 @@
<rng:ref name="page" />
<rng:ref name="separator"/>
<rng:ref name="button"/>
<rng:ref name="group"/>
<rng:element name="properties"><rng:empty/></rng:element>
<rng:element name="newline"><rng:empty/></rng:element>
</rng:choice>

View File

@ -1,7 +1,7 @@
# -*- encoding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>). All Rights Reserved
# $Id$
#
@ -37,26 +37,36 @@ waittime = 10
wait_count = 0
wait_limit = 12
def to_decode(s):
try:
return s.encode('utf-8')
except UnicodeError:
try:
return s.encode('latin')
except UnicodeError:
try:
return s.decode('ascii')
except UnicodeError:
return s
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))
os.system('python2.5 %sopenerp-server.py --pidfile=openerp.pid --port=%s --no-netrpc --addons-path=%s' %(root_path, str(port), addons_path))
def clean():
if os.path.isfile('openerp.pid'):
ps = open('openerp.pid')
ps = open('openerp.pid')
if ps:
pid = int(ps.read())
ps.close()
if pid:
ps.close()
if pid:
os.kill(pid,9)
def execute(connector, method, *args):
global wait_count
res = False
try:
try:
res = getattr(connector,method)(*args)
except socket.error,e:
if e.args[0] == 111:
except socket.error,e:
if e.args[0] == 111:
if wait_count > wait_limit:
print "Server is taking too long to start, it has exceeded the maximum limit of %d seconds."%(wait_limit)
clean()
@ -68,24 +78,23 @@ def execute(connector, method, *args):
else:
raise e
wait_count = 0
return res
return res
def login(uri, dbname, user, pwd):
conn = xmlrpclib.ServerProxy(uri + '/xmlrpc/common')
uid = execute(conn,'login',dbname, user, pwd)
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:
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'
lang,ext = os.path.splitext(trans_in.split('/')[-1])
state = 'init'
datas = {'form':{}}
while state!='end':
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',{}) )
@ -101,41 +110,49 @@ def import_translate(uri, user, pwd, dbname, translate_in):
})
trans_obj.close()
elif res['type']=='action':
state = res['state']
def check_quality(uri, user, pwd, dbname, modules):
state = res['state']
def check_quality(uri, user, pwd, dbname, modules, quality_logs):
uid = login(uri, dbname, user, pwd)
quality_logs += 'quality-logs'
if uid:
conn = xmlrpclib.ServerProxy(uri + '/xmlrpc/object')
qualityresult = {}
final = {}
test_detail = {}
for module in modules:
final = {}
for module in modules:
qualityresult = {}
test_detail = {}
quality_result = execute(conn,'execute', dbname, uid, pwd,'module.quality.check','check_quality',module)
detail_html = ''
html = '''<html><html><html><html><body><a name="TOP"></a>'''
html +="<h1> Module : %s </h1>"%(quality_result['name'])
html += "<h2> Final score : %s</h2>"%(quality_result['final_score'])
html = '''<html><body><a name="TOP"></a>'''
html +="<h1> Module: %s </h1>"%(quality_result['name'])
html += "<h2> Final score: %s</h2>"%(quality_result['final_score'])
html += "<div id='tabs'>"
html += "<ul>"
for x,y,detail in quality_result['check_detail_ids']:
for x,y,detail in quality_result['check_detail_ids']:
test = detail.get('name')
msg = detail.get('message','')
score = round(float(detail.get('score',0)),2)
html += "<li><a href=\"#%s\">%s</a></li>"%(test.replace(' ','-'),test)
detail_html +="<div id=\"%s\"><h3>%s (Score : %s)</h3>%s</div>"%(test.replace(' ','-'),test,score,detail.get('detail'))
test_detail[test] = (score,detail.get('detail',''))
html += "</ul>%s</body></html></html></html></html></html>"%(detail_html)
html += "</div>"
final[quality_result['name']] = (quality_result['final_score'],html,test_detail)
detail_html +='''<div id=\"%s\"><h3>%s (Score : %s)</h3><font color=red><h5>%s</h5></font>%s</div>'''%(test.replace(' ', '-'), test, score, msg, detail.get('detail', ''))
test_detail[test] = (score,msg,detail.get('detail',''))
html += "</ul>"
html += "%s"%(detail_html)
html += "</div></body></html>"
if not os.path.isdir(quality_logs):
os.mkdir(quality_logs)
fp = open('%s/%s.html'%(quality_logs,module),'wb')
fp.write(to_decode(html))
fp.close()
#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
#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 True
else:
print 'Login Failed...'
print 'Login Failed...'
clean()
sys.exit(1)
@ -156,76 +173,101 @@ def create_db(uri, dbname, user='admin', pwd='admin', lang='en_US'):
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)
id = execute(conn,'create',admin_passwd, dbname, True, lang)
wait(id,uri)
uid = login_conn.login(dbname, user, pwd)
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':
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():
for field in res['fields'].keys():
datas['form'][field] = datas['form'].get(field,False)
state = res['state'][-1][0]
datas['form'].update({
'profile': -1
'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)
install_module(uri, dbname, ['base_module_quality'],user=user,pwd=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)
execute(conn, 'drop', admin_passwd, dbname)
return True
def install_module(uri, dbname, modules, user='admin', pwd='admin'):
def make_links(uri, uid, dbname, source, destination, module, user, pwd):
if module in ('base','quality_integration_server'):
return True
if not os.path.islink(destination + '/' + module):
if not os.path.isdir(destination + '/' + module):
for path in source:
if os.path.isdir(path + '/' + module):
os.symlink(path + '/' + module, destination + '/' + module)
obj_conn = xmlrpclib.ServerProxy(uri + '/xmlrpc/object')
execute(obj_conn, 'execute', dbname, uid, pwd, 'ir.module.module', 'update_list')
module_ids = execute(obj_conn, 'execute', dbname, uid, pwd, 'ir.module.module', 'search', [('name','=',module)])
if len(module_ids):
data = execute(obj_conn, 'execute', dbname, uid, pwd, 'ir.module.module', 'read', module_ids[0],['name','dependencies_id'])
dep_datas = execute(obj_conn, 'execute', dbname, uid, pwd, 'ir.module.module.dependency', 'read', data['dependencies_id'],['name'])
for dep_data in dep_datas:
make_links(uri, uid, dbname, source, destination, dep_data['name'], user, pwd)
return True
return False
def install_module(uri, dbname, modules, addons='', extra_addons='', user='admin', pwd='admin'):
uid = login(uri, dbname, user, pwd)
if uid:
if extra_addons:
extra_addons = extra_addons.split(',')
if uid:
if addons and extra_addons:
for module in modules:
make_links(uri, uid, dbname, extra_addons, addons, module, user, pwd)
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)
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, {})
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'
state = 'end'
return True
def upgrade_module(uri, dbname, modules, user='admin', pwd='admin'):
uid = login(uri, dbname, user, pwd)
if uid:
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)
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, {})
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'
state = 'end'
return True
@ -238,28 +280,32 @@ Basic Commands:
start-server Start Server
create-db Create new database
drop-db Drop database
install-module Install module
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 = 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("--quality-logs", dest="quality_logs", help="specify the path of quality logs files which has to stores")
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("-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")
parser.add_option("--extra-addons", dest="extra_addons",
help="specify extra_addons and trunkCommunity modules path ")
(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")
parser.error("incorrect command")
def die(cond, msg):
if cond:
@ -274,38 +320,47 @@ die(opt.translate_in and (not opt.db_name),
options = {
'addons-path' : opt.addons_path or 'addons',
'quality-logs' : opt.quality_logs or '',
'root-path' : opt.root_path or '',
'translate-in': opt.translate_in,
'port' : opt.port or 8069,
'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 '',
'extra-addons':opt.extra_addons 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 []
# Hint:i18n-import=purchase:ar_AR.po+sale:fr_FR.po,nl_BE.po
if opt.translate_in:
translate = opt.translate_in
for module_name,po_files in map(lambda x:tuple(x.split(':')),translate.split('+')):
for po_file in po_files.split(','):
po_link = '%s/%s/i18n/%s'%(options['addons-path'], module_name, po_file)
options['translate-in'].append(po_link)
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':
try:
server_thread.start()
if command == 'create-db':
create_db(uri, options['database'], options['login'], options['pwd'])
if command == 'drop-db':
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':
if command == 'install-module':
install_module(uri, options['database'], options['modules'],options['addons-path'],options['extra-addons'],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'])
check_quality(uri, options['login'], options['pwd'], options['database'], options['modules'], options['quality-logs'])
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()
@ -314,7 +369,7 @@ except Exception, e:
print e
clean()
sys.exit(1)

View File

@ -44,14 +44,25 @@ class expression(object):
and (((not internal) and element[1] in OPS) \
or (internal and element[1] in INTERNAL_OPS))
def __execute_recursive_in(self, cr, s, f, w, ids):
def __execute_recursive_in(self, cr, s, f, w, ids, op, type):
res = []
for i in range(0, len(ids), cr.IN_MAX):
subids = ids[i:i+cr.IN_MAX]
cr.execute('SELECT "%s"' \
' FROM "%s"' \
' WHERE "%s" in (%s)' % (s, f, w, ','.join(['%s']*len(subids))),
subids)
if ids:
if op in ['<','>','>=','<=']:
cr.execute('SELECT "%s"' \
' FROM "%s"' \
' WHERE "%s" %s %s' % (s, f, w, op, ids[0]))
res.extend([r[0] for r in cr.fetchall()])
else:
for i in range(0, len(ids), cr.IN_MAX):
subids = ids[i:i+cr.IN_MAX]
cr.execute('SELECT "%s"' \
' FROM "%s"' \
' WHERE "%s" in (%s)' % (s, f, w, ','.join(['%s']*len(subids))),
subids)
res.extend([r[0] for r in cr.fetchall()])
else:
cr.execute('SELECT distinct("%s")' \
' FROM "%s" where "%s" is not null' % (s, f, s)),
res.extend([r[0] for r in cr.fetchall()])
return res
@ -101,7 +112,6 @@ class expression(object):
if self._is_operator(e) or e == self.__DUMMY_LEAF:
continue
left, operator, right = e
working_table = table
main_table = table
fargs = left.split('.', 1)
@ -154,14 +164,32 @@ class expression(object):
elif field._type == 'one2many':
if isinstance(right, basestring):
ids2 = [x[0] for x in field_obj.name_search(cr, uid, right, [], operator, limit=None)]
else:
ids2 = list(right)
if not ids2:
self.__exp[i] = ('id', '=', '0')
else:
self.__exp[i] = ('id', 'in', self.__execute_recursive_in(cr, field._fields_id, field_obj._table, 'id', ids2))
call_null = True
if right:
if isinstance(right, basestring):
ids2 = [x[0] for x in field_obj.name_search(cr, uid, right, [], operator, limit=None)]
operator = 'in'
else:
if not isinstance(right,list):
ids2 = [right]
else:
ids2 = right
if not ids2:
call_null = True
operator = 'in' # operator changed because ids are directly related to main object
else:
call_null = False
o2m_op = 'in'
if operator in ['not like','not ilike','not in','<>','!=']:
o2m_op = 'not in'
self.__exp[i] = ('id', o2m_op, self.__execute_recursive_in(cr, field._fields_id, field_obj._table, 'id', ids2, operator, field._type))
if call_null:
o2m_op = 'not in'
if operator in ['not like','not ilike','not in','<>','!=']:
o2m_op = 'in'
self.__exp[i] = ('id', o2m_op, self.__execute_recursive_in(cr, field._fields_id, field_obj._table, 'id', [], operator, field._type) or [0])
elif field._type == 'many2many':
#FIXME
@ -174,17 +202,38 @@ class expression(object):
def _rec_convert(ids):
if field_obj == table:
return ids
return self.__execute_recursive_in(cr, field._id1, field._rel, field._id2, ids)
return self.__execute_recursive_in(cr, field._id1, field._rel, field._id2, ids, operator, field._type)
dom = _rec_get(ids2, field_obj)
ids2 = field_obj.search(cr, uid, dom, context=context)
self.__exp[i] = ('id', 'in', _rec_convert(ids2))
else:
if isinstance(right, basestring):
res_ids = [x[0] for x in field_obj.name_search(cr, uid, right, [], operator)]
else:
res_ids = list(right)
self.__exp[i] = ('id', 'in', self.__execute_recursive_in(cr, field._id1, field._rel, field._id2, res_ids) or [0])
call_null_m2m = True
if right:
if isinstance(right, basestring):
res_ids = [x[0] for x in field_obj.name_search(cr, uid, right, [], operator)]
operator = 'in'
else:
if not isinstance(right, list):
res_ids = [right]
else:
res_ids = right
if not res_ids:
call_null_m2m = True
operator = 'in' # operator changed because ids are directly related to main object
else:
call_null_m2m = False
m2m_op = 'in'
if operator in ['not like','not ilike','not in','<>','!=']:
m2m_op = 'not in'
self.__exp[i] = ('id', m2m_op, self.__execute_recursive_in(cr, field._id1, field._rel, field._id2, res_ids, operator, field._type) or [0])
if call_null_m2m:
m2m_op = 'not in'
if operator in ['not like','not ilike','not in','<>','!=']:
m2m_op = 'in'
self.__exp[i] = ('id', m2m_op, self.__execute_recursive_in(cr, field._id1, field._rel, field._id2, [], operator, field._type) or [0])
elif field._type == 'many2one':
if operator == 'child_of':
if isinstance(right, basestring):

View File

@ -607,6 +607,12 @@ class function(_column):
self._multi = multi
if 'relation' in args:
self._obj = args['relation']
if 'digits' in args:
self.digits = args['digits']
else:
self.digits = (16,2)
self._fnct_inv_arg = fnct_inv_arg
if not fnct_inv:
self.readonly = 1

View File

@ -299,7 +299,10 @@ def get_pg_type(f):
t = eval('fields.'+(f._type))
f_type = (type_dict[t], type_dict[t])
elif isinstance(f, fields.function) and f._type == 'float':
f_type = ('float8', 'DOUBLE PRECISION')
if f.digits:
f_type = ('numeric', 'NUMERIC(%d,%d)' % (f.digits[0], f.digits[1]))
else:
f_type = ('float8', 'DOUBLE PRECISION')
elif isinstance(f, fields.function) and f._type == 'selection':
f_type = ('text', 'text')
elif isinstance(f, fields.function) and f._type == 'char':
@ -574,7 +577,11 @@ class orm_template(object):
raise Exception(_('Please check that all your lines have %d columns.') % (len(fields),))
if not line[i]:
continue
field = fields[i]
if prefix and not prefix[0] in field:
continue
if (len(field)==len(prefix)+1) and field[len(prefix)].endswith(':db_id'):
# Database ID
res = False
@ -631,7 +638,7 @@ class orm_template(object):
id = ir_model_data_obj._get_id(cr, uid, module, xml_id)
res_id = ir_model_data_obj.read(cr, uid, [id],
['res_id'])[0]['res_id']
row[field[0][:-3]] = res_id or False
row[field[-1][:-3]] = res_id or False
continue
if (len(field) == len(prefix)+1) and \
len(field[len(prefix)].split(':lang=')) == 2:
@ -667,7 +674,7 @@ class orm_template(object):
try:
_check_db_id(self, model_name, line[i])
data_res_id = is_db_id = int(line[i])
except Exception,e:
except Exception,e:
warning += [tools.exception_to_unicode(e)]
logger.notifyChannel("import", netsvc.LOG_ERROR,
tools.exception_to_unicode(e))
@ -808,7 +815,7 @@ class orm_template(object):
try:
id = ir_model_data_obj._update(cr, uid, self._name,
current_module, res, xml_id=data_id, mode=mode,
noupdate=noupdate, res_id=res_id)
noupdate=noupdate, res_id=res_id, context=context)
except Exception, e:
import psycopg2
if isinstance(e,psycopg2.IntegrityError):
@ -997,9 +1004,10 @@ class orm_template(object):
attrs = {'views': views}
if node.hasAttribute('widget') and node.getAttribute('widget')=='selection':
# We can not use the 'string' domain has it is defined according to the record !
dom = None
dom = []
if column._domain and not isinstance(column._domain, (str, unicode)):
dom = column._domain
attrs['selection'] = self.pool.get(relation).name_search(cr, user, '', dom, context=context)
if (node.hasAttribute('required') and not int(node.getAttribute('required'))) or not column.required:
attrs['selection'].append((False,''))
@ -1054,19 +1062,37 @@ class orm_template(object):
buttons = (n for n in node.getElementsByTagName('button') if n.getAttribute('type') != 'object')
for button in buttons:
ok = True
can_click = True
if user != 1: # admin user has all roles
user_roles = usersobj.read(cr, user, [user], ['roles_id'])[0]['roles_id']
cr.execute("select role_id from wkf_transition where signal=%s", (button.getAttribute('name'),))
# TODO handle the case of more than one workflow for a model
cr.execute("""SELECT DISTINCT t.role_id
FROM wkf
INNER JOIN wkf_activity a ON a.wkf_id = wkf.id
INNER JOIN wkf_transition t ON (t.act_to = a.id)
WHERE wkf.osv = %s
AND t.signal = %s
""", (self._name, button.getAttribute('name'),))
roles = cr.fetchall()
for role in roles:
if role[0]:
ok = ok and rolesobj.check(cr, user, user_roles, role[0])
# draft -> valid = signal_next (role X)
# draft -> cancel = signal_cancel (no role)
#
# valid -> running = signal_next (role Y)
# valid -> cancel = signal_cancel (role Z)
#
# running -> done = signal_next (role Z)
# running -> cancel = signal_cancel (role Z)
if not ok:
button.setAttribute('readonly', '1')
else:
button.setAttribute('readonly', '0')
# As we don't know the object state, in this scenario,
# the button "signal_cancel" will be always shown as there is no restriction to cancel in draft
# the button "signal_next" will be show if the user has any of the roles (X Y or Z)
# The verification will be made later in workflow process...
if roles:
can_click = any((not role) or rolesobj.check(cr, user, user_roles, role) for (role,) in roles)
button.setAttribute('readonly', str(int(not can_click)))
arch = node.toxml(encoding="utf-8").replace('\t', '')
fields = self.fields_get(cr, user, fields_def.keys(), context)
@ -1204,6 +1230,15 @@ class orm_template(object):
model = True
sql_res = False
while ok:
view_ref = context.get(view_type + '_view_ref', False)
if view_ref:
if '.' in view_ref:
module, view_ref = view_ref.split('.', 1)
cr.execute("SELECT res_id FROM ir_model_data WHERE model='ir.ui.view' AND module=%s AND name=%s", (module, view_ref))
view_ref_res = cr.fetchone()
if view_ref_res:
view_id = view_ref_res[0]
if view_id:
where = (model and (" and model='%s'" % (self._name,))) or ''
cr.execute('SELECT arch,name,field_parent,id,type,inherit_id FROM ir_ui_view WHERE id=%s'+where, (view_id,))
@ -1328,7 +1363,7 @@ class orm_template(object):
def name_get(self, cr, user, ids, context=None):
raise _('The name_get method is not implemented on this object !')
def name_search(self, cr, user, name='', args=None, operator='ilike', context=None, limit=None):
def name_search(self, cr, user, name='', args=None, operator='ilike', context=None, limit=80):
raise _('The name_search method is not implemented on this object !')
def copy(self, cr, uid, id, default=None, context=None):
@ -1769,7 +1804,7 @@ class orm(orm_template):
f_pg_notnull = f_pg_def['attnotnull']
if isinstance(f, fields.function) and not f.store:
logger.notifyChannel('orm', netsvc.LOG_INFO, 'column %s (%s) in table %s removed: converted to a function !\n' % (k, f.string, self._table))
cr.execute('ALTER TABLE %s DROP COLUMN %s'% (self._table, k))
cr.execute('ALTER TABLE "%s" DROP COLUMN "%s"'% (self._table, k))
cr.commit()
f_obj_type = None
else:
@ -1782,6 +1817,8 @@ class orm(orm_template):
('varchar', 'text', 'TEXT', ''),
('int4', 'float', get_pg_type(f)[1], '::'+get_pg_type(f)[1]),
('date', 'datetime', 'TIMESTAMP', '::TIMESTAMP'),
('numeric', 'float', get_pg_type(f)[1], '::'+get_pg_type(f)[1]),
('float8', 'float', get_pg_type(f)[1], '::'+get_pg_type(f)[1]),
]
# !!! Avoid reduction of varchar field !!!
if f_pg_type == 'varchar' and f._type == 'char' and f_pg_size < f.size:
@ -1794,13 +1831,15 @@ class orm(orm_template):
cr.commit()
for c in casts:
if (f_pg_type==c[0]) and (f._type==c[1]):
logger.notifyChannel('orm', netsvc.LOG_INFO, "column '%s' in table '%s' changed type to %s." % (k, self._table, c[1]))
ok = True
cr.execute('ALTER TABLE "%s" RENAME COLUMN "%s" TO temp_change_size' % (self._table, k))
cr.execute('ALTER TABLE "%s" ADD COLUMN "%s" %s' % (self._table, k, c[2]))
cr.execute(('UPDATE "%s" SET "%s"=temp_change_size'+c[3]) % (self._table, k))
cr.execute('ALTER TABLE "%s" DROP COLUMN temp_change_size CASCADE' % (self._table,))
cr.commit()
if f_pg_type != f_obj_type:
logger.notifyChannel('orm', netsvc.LOG_INFO, "column '%s' in table '%s' changed type to %s." % (k, self._table, c[1]))
ok = True
cr.execute('ALTER TABLE "%s" RENAME COLUMN "%s" TO temp_change_size' % (self._table, k))
cr.execute('ALTER TABLE "%s" ADD COLUMN "%s" %s' % (self._table, k, c[2]))
cr.execute(('UPDATE "%s" SET "%s"=temp_change_size'+c[3]) % (self._table, k))
cr.execute('ALTER TABLE "%s" DROP COLUMN temp_change_size CASCADE' % (self._table,))
cr.commit()
break
if f_pg_type != f_obj_type:
if not ok:
@ -1813,7 +1852,7 @@ class orm(orm_template):
default = self._defaults[k](self, cr, 1, {})
if (default is not None):
ss = self._columns[k]._symbol_set
query = 'UPDATE "%s" SET "%s"=%s WHERE %s is NULL' % (self._table, k, ss[0], k)
query = 'UPDATE "%s" SET "%s"=%s WHERE "%s" is NULL' % (self._table, k, ss[0], k)
cr.execute(query, (ss[1](default),))
# add the NOT NULL constraint
cr.commit()
@ -2469,9 +2508,10 @@ class orm(orm_template):
if c[0].startswith('default_'):
del rel_context[c[0]]
result = []
for field in upd_todo:
for id in ids:
self._columns[field].set(cr, self, id, field, vals[field], user, context=rel_context)
result += self._columns[field].set(cr, self, id, field, vals[field], user, context=rel_context) or []
for table in self._inherits:
col = self._inherits[table]
@ -2533,7 +2573,7 @@ class orm(orm_template):
cr.execute('update '+self._table+' set parent_right=parent_right+%s where parent_right>=%s', (distance, position))
cr.execute('update '+self._table+' set parent_left=parent_left-%s, parent_right=parent_right-%s where parent_left>=%s and parent_left<%s', (pleft-position+distance,pleft-position+distance, pleft+distance, pright+distance))
result = self._store_get_values(cr, user, ids, vals.keys(), context)
result += self._store_get_values(cr, user, ids, vals.keys(), context)
for order, object, ids, fields in result:
self.pool.get(object)._store_set_values(cr, user, ids, fields, context)
@ -2701,6 +2741,8 @@ class orm(orm_template):
for fnct in range(len(fncts)):
if fncts[fnct][3]:
ok = False
if not fields:
ok = True
for f in (fields or []):
if f in fncts[fnct][3]:
ok = True
@ -2869,7 +2911,7 @@ class orm(orm_template):
return [(r['id'], tools.ustr(r[self._rec_name])) for r in self.read(cr, user, ids,
[self._rec_name], context, load='_classic_write')]
def name_search(self, cr, user, name='', args=None, operator='ilike', context=None, limit=None):
def name_search(self, cr, user, name='', args=None, operator='ilike', context=None, limit=80):
if not args:
args = []
if not context:

View File

@ -22,7 +22,7 @@
##############################################################################
name = 'openerp-server'
version = '5.0.5-bzr'
version = '5.0.6'
major_version = '5.0'
description = 'OpenERP Server'
long_desc = '''\

View File

@ -51,7 +51,10 @@ class report_printscreen_list(report_int):
return result
def _parse_string(self, view):
dom = etree.XML(view)
try:
dom = etree.XML(view.encode('utf-8'))
except:
dom = etree.XML(view)
return self._parse_node(dom)
def create(self, cr, uid, ids, datas, context=None):

View File

@ -42,6 +42,7 @@ import reportlab
from lxml import etree
import copy
import tools
import locale
_regex = re.compile('\[\[(.+?)\]\]')
@ -133,6 +134,15 @@ units = [
def unit_get(size):
global units
if size:
if size.find('.') == -1:
decimal_point = '.'
try:
decimal_point = locale.nl_langinfo(locale.RADIXCHAR)
except:
decimal_point = locale.localeconv()['decimal_point']
size = size.replace(decimal_point, '.')
for unit in units:
res = unit[0].search(size, 0)
if res:

View File

@ -424,14 +424,14 @@ def trans_generate(lang, modules, dbname=None):
xml_name = "%s.%s" % (module, encode(xml_name))
if not pool.get(model):
logger.notifyChannel("db", netsvc.LOG_ERROR, "unable to find object %r" % (model,))
logger.notifyChannel("db", netsvc.LOG_ERROR, "Unable to find object %r" % (model,))
continue
try:
obj = pool.get(model).browse(cr, uid, res_id)
except BrowseRecordError:
logger.notifyChannel("db", netsvc.LOG_ERROR, "unable to find object %r with id %d" % (model, res_id))
exists = pool.get(model).exists(cr, uid, res_id)
if not exists:
logger.notifyChannel("db", netsvc.LOG_WARNING, "Unable to find object %r with id %d" % (model, res_id))
continue
obj = pool.get(model).browse(cr, uid, res_id)
if model=='ir.ui.view':
d = xml.dom.minidom.parseString(encode(obj.arch))

View File

@ -1,3 +1,179 @@
2009-09-22: 5.0.6
=================
Bugfixes (server)
-----------------
* https://launchpad.net/bugs/421904
* Fields.function : Store value computation corrected
* https://launchpad.net/bugs/407332
* Price Accuracy : rounding made to be based on --price_accuracy option
Bugfixes (addons)
-----------------
* Not linked to a bug report:
* fixed an "unsupported operand type(s) for +: 'bool' and 'str'" error.
* Sale_delivery_report : Delivery order report should take shipping address from picking itself
* account: bugfixed the fiscalyear closing wizard:
* Purchase_manual : certificate on terp displayed wrong value
* https://launchpad.net/bugs/431864
* hr_timesheet_sheet : casting correction for a view
* https://launchpad.net/bugs/431871
* Account : Move line creation was giving encoding error while automated entries were inserted,corrected
* https://launchpad.net/bugs/433979
* [CRITICAL]document: crash list of directory after rev:hmo@tinyerp.com-20090916110722-4kl5t5q71im6c76i
* https://launchpad.net/bugs/407332
* Account : Rounding problem based on price_accuracy
* https://launchpad.net/bugs/419720
* Sale : Delivery Date delay computation made corrected when days are passed with fractions
* https://launchpad.net/bugs/423726
* Sale : SO Boolean field picked behavior corrected
* https://launchpad.net/bugs/431821
* Account_followup : Sending email while sending follow up corrected.
Improvements (addons)
---------------------
* purchase_manual: do not redefine view: add fields by inheritance...
* purchase: improve views to be directly usable in portals...
2009-09-17: 5.0.5
=================
Bugfixes (server)
-----------------
* Not linked to a bug report:
* quality_integration_server: fixe problem on make link of extra-module in addons
* quality_integration_server : quality log : reduce overwrite last test log in other test logs
* base : ir_cron : take minimun next_call from database for job waiting. if database does not has any job, it will take 1 day for job waiting
* quality_integration_server: get message from base_quality_module if module can not success to reach minimun score
* regression in fields.function computation
* better log error, new line
* Report: context was missing while printing any report with header in a non-English Language
* quality_integration_server: fixe problem on make link of extra-module in addons
* https://launchpad.net/bugs/425131
* Better translation exportation.
* https://launchpad.net/bugs/379645
* Print screen : Correction when text for report is already encoded
* https://launchpad.net/bugs/421162
* Send Mail : Unicode error handled
* Send mail : Non-English characters were throwing error.
* https://launchpad.net/bugs/421137
* Base : 'Supplier Partners' menu will open new record with supplier=1,customer=0
* https://launchpad.net/bugs/425430
* Export : Error solved while Exporting O2M records with None value of any field
* https://launchpad.net/bugs/399208
* Translation issue with cache: it needed to restart server,SOLVED
* https://launchpad.net/bugs/422563
* Security Loophole corrected
Improvements (server)
---------------------
* quality_integration_server: make link of extra-addons module in addons module automatic on install-module command
* quality_integration_server: get message from base_quality_module if module can not success to reach minimun score
* Rng : Accepting Groups under/after/replacing Buttons
* quality_integration_server : quality html log
* quality_integration_server: put max_limit in server connection
* Base : Rate for ARS Added
* cchange version number
Bugfixes (addons)
-----------------
* Not linked to a bug report:
* Purchase_manual : certificate on terp displayed wrong value
* fix the problem of message depending on the partner language
* fix a problem for the overdue payment message
* fix problem to get the detault message
* #416908 computation of invoice lines
* Project : Taks hours computed accurately.
* base_module_record: avoid duplications
* document: do not try to index binary documents
* +1 on the lengthmonth returns was too much, removed it
* Problem when using hr_holidays Summary Report when entering date 1 jan 2009 for example.
* multi currency reconciliations on bank statements weren't properly displayed
* multi currency payments weren't correctly computed on invoice's residual amount
* Project_gtd : Demo data defined for tasks(inboxes)
* Module:product Added report tag for product pricelist reportso that it can be modified using OODesigner
* Document : error on create if attachment not specified
* https://launchpad.net/bugs/422401
* project_gtd : timebox field re-arranged
* https://launchpad.net/bugs/421907
* Account : Aged Partner Balance report for Future analysis corrected
* https://launchpad.net/bugs/427388
* Project : task work behavior towards task hours corrected
* https://launchpad.net/bugs/422408
* Project_gtd : Hierarchical timeboxes allowed to open
* https://launchpad.net/bugs/403972
* Account : Reports from Chart of Accounts were not considering contextual value of MOVE(draft,posted)
* https://launchpad.net/bugs/430577
* Sale : Order Lines switches view from tree,form and view
* https://launchpad.net/bugs/425257
* Project_gtd : improved menuitems names to avoid user confusion,domain improved
* https://launchpad.net/bugs/422078
* Account_tax_include : floatdivision error corrected
* https://launchpad.net/bugs/423585
* https://launchpad.net/bugs/423581
* https://launchpad.net/bugs/416807
* report_document : Dashboard should not display partner with no files
* https://launchpad.net/bugs/430522
* https://launchpad.net/bugs/424214
* Account :Residual amount unclear when used with partial payments.
* https://launchpad.net/bugs/424177
* hr_timesheet_sheet : Rounding value problem in float_time widget
* https://launchpad.net/bugs/415056
* https://launchpad.net/bugs/421636
* added tooltip on tax and payment term
* https://launchpad.net/bugs/407290
* https://launchpad.net/bugs/407293
* Account : Making account.invoice.tax field language-depenant from account.tax
* https://launchpad.net/bugs/430043
* Hr_timesheet_invoice : Query calculation corrected
* https://launchpad.net/bugs/379645
* Base_report_creator : Print screen report is now available for custom reports
* https://launchpad.net/bugs/423260
* Account : Title of of Move Line widnows renamed
* https://launchpad.net/bugs/427982
* hr_timesheet_invoice : Invoicing analytic entries wizard corrected for product
* https://launchpad.net/bugs/426243
* Account : Context was missing in report
* https://launchpad.net/bugs/426831
* l10n_ch : default_value() of res.partner.bank corrected
* https://launchpad.net/bugs/430098
* Hr_holidays : Workflow actions validations corrected
* https://launchpad.net/bugs/430516
* https://launchpad.net/bugs/379191
* its better to aply the patch
* fix the problem for related fields, related fields act as a many2one fields
* https://launchpad.net/bugs/429203
* CRM : Mailgate script used default uid=3 instead of 1
* https://launchpad.net/bugs/399817
* Fixed Partial deliveries problem
Improvements (addons)
---------------------
* Invoice Supplier Control per line/manual
* add missing file for the followup date
* changed interval CRM calls to 4 hours instead of one -> efficient connection pooling with cron
* add inherited view for company
* add new filed to company object which will store the mesages
* add new data file which will add over due payment message to company
* Account : Analytic Account report following parental relationship approach(account with all its children
* hr_timesheet_invoice : Misleading string on invoice analytic entries' wizard
* base_module_quality: remove certificate tag from terp test
* report_dcoument : changed the misleading name of action
2009-08-28: 5.0.4
=================