[MERGE] merge trunk into local branch
bzr revid: ged@openerp.com-20140113142459-pjoiau7pp4knvvda bzr revid: ged@openerp.com-20140116144304-d00alltpt7j1gzvh bzr revid: ged@openerp.com-20140121142924-641cu8trhm4pdf1m
This commit is contained in:
commit
17e3c31992
|
@ -2,12 +2,11 @@ graft debian
|
|||
graft doc
|
||||
graft install
|
||||
graft openerp
|
||||
graft tests
|
||||
graft win32
|
||||
include README
|
||||
include LICENSE
|
||||
include MANIFEST.in
|
||||
include openerp-wsgi.py
|
||||
include openerp-server
|
||||
include openerp-*
|
||||
include oe*
|
||||
include setup*
|
||||
global-exclude *pyc *~ # Exclude possible garbage from previous graft.
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -2,7 +2,7 @@
|
|||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2013 OpenERP S.A. <http://www.openerp.com>
|
||||
# Copyright (C) 2004-2014 OpenERP S.A. <http://www.openerp.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
@ -417,7 +417,7 @@ class ir_actions_server(osv.osv):
|
|||
|
||||
def _select_objects(self, cr, uid, context=None):
|
||||
model_pool = self.pool.get('ir.model')
|
||||
ids = model_pool.search(cr, uid, [('name', 'not ilike', '.')])
|
||||
ids = model_pool.search(cr, uid, [], limit=None)
|
||||
res = model_pool.read(cr, uid, ids, ['model', 'name'])
|
||||
return [(r['model'], r['name']) for r in res] + [('', '')]
|
||||
|
||||
|
|
|
@ -242,7 +242,7 @@ class ir_attachment(osv.osv):
|
|||
# performed in batch as much as possible.
|
||||
ima = self.pool.get('ir.model.access')
|
||||
for model, targets in model_attachments.iteritems():
|
||||
if model not in self.pool:
|
||||
if not self.pool.get(model):
|
||||
continue
|
||||
if not ima.check(cr, uid, model, 'read', False):
|
||||
# remove all corresponding attachment ids
|
||||
|
|
|
@ -411,6 +411,7 @@ class ir_mail_server(osv.osv):
|
|||
email_to = message['To']
|
||||
email_cc = message['Cc']
|
||||
email_bcc = message['Bcc']
|
||||
|
||||
smtp_to_list = filter(None, tools.flatten(map(extract_rfc2822_addresses,[email_to, email_cc, email_bcc])))
|
||||
assert smtp_to_list, "At least one valid recipient address should be specified for outgoing emails (To/Cc/Bcc)"
|
||||
|
||||
|
|
|
@ -77,7 +77,7 @@ addresses belonging to this country.\n\nYou can use the python-style string pate
|
|||
context=context)
|
||||
|
||||
def write(self, cursor, user, ids, vals, context=None):
|
||||
if vals.get('code', False):
|
||||
if vals.get('code'):
|
||||
vals['code'] = vals['code'].upper()
|
||||
return super(Country, self).write(cursor, user, ids, vals,
|
||||
context=context)
|
||||
|
|
|
@ -827,7 +827,7 @@ class users_view(osv.osv):
|
|||
fields.append('groups_id')
|
||||
res = super(users_view, self).read(cr, uid, ids, fields, context=context, load=load)
|
||||
|
||||
if group_fields:
|
||||
if res and group_fields:
|
||||
for values in (res if isinstance(res, list) else [res]):
|
||||
self._get_reified_groups(group_fields, values)
|
||||
if inject_groups_id:
|
||||
|
@ -859,6 +859,7 @@ class users_view(osv.osv):
|
|||
'selection': [(False, '')] + [(g.id, g.name) for g in gs],
|
||||
'help': '\n'.join(tips),
|
||||
'exportable': False,
|
||||
'selectable': False,
|
||||
}
|
||||
else:
|
||||
# boolean group fields
|
||||
|
@ -868,6 +869,7 @@ class users_view(osv.osv):
|
|||
'string': g.name,
|
||||
'help': g.comment,
|
||||
'exportable': False,
|
||||
'selectable': False,
|
||||
}
|
||||
return res
|
||||
|
||||
|
|
|
@ -647,6 +647,15 @@
|
|||
<rng:optional><rng:attribute name="date_stop" /></rng:optional>
|
||||
<rng:optional><rng:attribute name="day_length" /></rng:optional>
|
||||
<rng:optional><rng:attribute name="date_delay" /></rng:optional>
|
||||
<rng:optional><rng:attribute name="all_day" /></rng:optional>
|
||||
<rng:optional><rng:attribute name="event_open_popup" /></rng:optional>
|
||||
<rng:optional><rng:attribute name="attendee" /></rng:optional>
|
||||
<rng:optional><rng:attribute name="color_is_attendee" /></rng:optional>
|
||||
<rng:optional><rng:attribute name="avatar_model" /></rng:optional>
|
||||
<rng:optional><rng:attribute name="avatar_title" /></rng:optional>
|
||||
<rng:optional><rng:attribute name="use_contacts" /></rng:optional>
|
||||
<rng:optional><rng:attribute name="quick_add" /></rng:optional>
|
||||
<rng:optional><rng:attribute name="display" /></rng:optional>
|
||||
<rng:optional><rng:attribute name="type" /></rng:optional>
|
||||
<rng:optional><rng:attribute name="mode" /></rng:optional>
|
||||
<rng:oneOrMore>
|
||||
|
|
|
@ -530,4 +530,34 @@
|
|||
!python {model: res.partner }: |
|
||||
res_ids = self.search(cr, uid, [('child_ids.country_id','=','Belgium'),('active','=',False)])
|
||||
assert len(res_ids) != 0, "Record not Found with country Belgium and active False."
|
||||
-
|
||||
Check that we can exclude translated fields (bug lp:1071710)
|
||||
-
|
||||
!python {model: res.country}: |
|
||||
# first install french language
|
||||
Modules = self.pool.get('ir.module.module')
|
||||
base = Modules.browse(cr, uid, Modules.search(cr, uid, [('name', '=', 'base')])[0])
|
||||
|
||||
# hack: mark module as installed to allow install of the translation
|
||||
base_state = base.state
|
||||
base.write({'state': 'installed'})
|
||||
base.update_translations('fr_FR')
|
||||
base.write({'state': base_state})
|
||||
|
||||
# tests
|
||||
be_id = self.search(cr, uid, [('name', '=', 'Belgium')])[0]
|
||||
|
||||
ctx = {'lang': 'fr_FR'}
|
||||
not_be = self.search(cr, uid, [('name', '!=', 'Belgique')], context=ctx)
|
||||
|
||||
assert be_id not in not_be, "Search match failed"
|
||||
|
||||
# indirect search via m2o
|
||||
Partners = self.pool.get('res.partner')
|
||||
agrolait = Partners.search(cr, uid, [('name', '=', 'Agrolait')])[0]
|
||||
|
||||
not_be = Partners.search(cr, uid, [('country_id', '!=', 'Belgium')])
|
||||
assert agrolait not in not_be, "Search match failed (m2o)"
|
||||
|
||||
not_be = Partners.search(cr, uid, [('country_id', '!=', 'Belgique')], context=ctx)
|
||||
assert agrolait not in not_be, "Search match failed (m2o)"
|
||||
|
|
|
@ -26,7 +26,7 @@ from openerp import report, tools
|
|||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
def graph_get(cr, graph, wkf_ids, nested, workitem, processed_subflows):
|
||||
def graph_get(cr, graph, wkf_ids, nested, workitem, witm_trans, processed_subflows):
|
||||
import pydot
|
||||
cr.execute('select * from wkf_activity where wkf_id in ('+','.join(['%s']*len(wkf_ids))+')', wkf_ids)
|
||||
nodes = cr.dictfetchall()
|
||||
|
@ -40,7 +40,7 @@ def graph_get(cr, graph, wkf_ids, nested, workitem, processed_subflows):
|
|||
cr.execute('select * from wkf where id=%s', (n['subflow_id'],))
|
||||
wkfinfo = cr.dictfetchone()
|
||||
graph2 = pydot.Cluster('subflow'+str(n['subflow_id']), fontsize='12', label = "\"Subflow: %s\\nOSV: %s\"" % ( n['name'], wkfinfo['osv']) )
|
||||
(s1,s2) = graph_get(cr, graph2, [n['subflow_id']], True, workitem, processed_subflows)
|
||||
(s1,s2) = graph_get(cr, graph2, [n['subflow_id']], True, workitem, witm_trans, processed_subflows)
|
||||
graph.add_subgraph(graph2)
|
||||
actfrom[n['id']] = s2
|
||||
actto[n['id']] = s1
|
||||
|
@ -89,7 +89,9 @@ def graph_get(cr, graph, wkf_ids, nested, workitem, processed_subflows):
|
|||
args['arrowtail']='inv'
|
||||
|
||||
if activities[t['act_to']]['join_mode']=='AND':
|
||||
args['arrowhead']='crow'
|
||||
args['arrowhead']='crow'
|
||||
if t['id'] in witm_trans:
|
||||
args['color'] = 'red'
|
||||
|
||||
activity_from = actfrom[t['act_from']][1].get(t['signal'], actfrom[t['act_from']][0])
|
||||
activity_to = actto[t['act_to']][1].get(t['signal'], actto[t['act_to']][0])
|
||||
|
@ -119,8 +121,12 @@ def graph_instance_get(cr, graph, inst_id, nested=False):
|
|||
workitems.update(workitem_get(subflow_id))
|
||||
return workitems
|
||||
|
||||
def witm_get(instance):
|
||||
cr.execute("select trans_id from wkf_witm_trans where inst_id=%s", (instance,))
|
||||
return set(t[0] for t in cr.fetchall())
|
||||
|
||||
processed_subflows = set()
|
||||
graph_get(cr, graph, [x[0] for x in inst], nested, workitem_get(inst_id), processed_subflows)
|
||||
graph_get(cr, graph, [x[0] for x in inst], nested, workitem_get(inst_id), witm_get(inst_id), processed_subflows)
|
||||
|
||||
#
|
||||
# TODO: pas clean: concurrent !!!
|
||||
|
|
|
@ -286,7 +286,7 @@ class JsonRequest(WebRequest):
|
|||
if jsonp and self.httprequest.method == 'POST':
|
||||
# jsonp 2 steps step1 POST: save call
|
||||
def handler():
|
||||
self.session.jsonp_requests[request_id] = self.httprequest.form['r']
|
||||
self.session['jsonp_request_%s' % (request_id,)] = self.httprequest.form['r']
|
||||
self.session.modified = True
|
||||
headers=[('Content-Type', 'text/plain; charset=utf-8')]
|
||||
r = werkzeug.wrappers.Response(request_id, headers=headers)
|
||||
|
@ -298,7 +298,7 @@ class JsonRequest(WebRequest):
|
|||
request = args.get('r')
|
||||
elif jsonp and request_id:
|
||||
# jsonp 2 steps step2 GET: run and return result
|
||||
request = self.session.jsonp_requests.pop(request_id, "")
|
||||
request = self.session.pop('jsonp_request_%s' % (request_id,), '{}')
|
||||
else:
|
||||
# regular jsonrpc2
|
||||
request = self.httprequest.stream.read()
|
||||
|
@ -637,7 +637,6 @@ class OpenERPSession(werkzeug.contrib.sessions.Session):
|
|||
self.setdefault("login", None)
|
||||
self.setdefault("password", None)
|
||||
self.setdefault("context", {'tz': "UTC", "uid": None})
|
||||
self.setdefault("jsonp_requests", {})
|
||||
|
||||
def get_context(self):
|
||||
"""
|
||||
|
|
|
@ -59,7 +59,6 @@ def load_module_graph(cr, graph, status=None, perform_checks=True, skip_modules=
|
|||
def load_test(module_name, idref, mode):
|
||||
cr.commit()
|
||||
try:
|
||||
threading.currentThread().testing = True
|
||||
_load_data(cr, module_name, idref, mode, 'test')
|
||||
return True
|
||||
except Exception:
|
||||
|
@ -67,7 +66,6 @@ def load_module_graph(cr, graph, status=None, perform_checks=True, skip_modules=
|
|||
'module %s: an exception occurred in a test', module_name)
|
||||
return False
|
||||
finally:
|
||||
threading.currentThread().testing = False
|
||||
if tools.config.options['test_commit']:
|
||||
cr.commit()
|
||||
else:
|
||||
|
@ -104,12 +102,18 @@ def load_module_graph(cr, graph, status=None, perform_checks=True, skip_modules=
|
|||
init mode.
|
||||
|
||||
"""
|
||||
for filename in _get_files_of_kind(kind):
|
||||
_logger.info("module %s: loading %s", module_name, filename)
|
||||
noupdate = False
|
||||
if kind in ('demo', 'demo_xml') or (filename.endswith('.csv') and kind in ('init', 'init_xml')):
|
||||
noupdate = True
|
||||
tools.convert_file(cr, module_name, filename, idref, mode, noupdate, kind, report)
|
||||
try:
|
||||
if kind in ('demo', 'test'):
|
||||
threading.currentThread().testing = True
|
||||
for filename in _get_files_of_kind(kind):
|
||||
_logger.info("module %s: loading %s", module_name, filename)
|
||||
noupdate = False
|
||||
if kind in ('demo', 'demo_xml') or (filename.endswith('.csv') and kind in ('init', 'init_xml')):
|
||||
noupdate = True
|
||||
tools.convert_file(cr, module_name, filename, idref, mode, noupdate, kind, report)
|
||||
finally:
|
||||
if kind in ('demo', 'test'):
|
||||
threading.currentThread().testing = False
|
||||
|
||||
if status is None:
|
||||
status = {}
|
||||
|
|
|
@ -30,6 +30,7 @@ from os.path import join as opj
|
|||
import openerp
|
||||
import openerp.release as release
|
||||
import openerp.tools as tools
|
||||
from openerp.tools.parse_version import parse_version
|
||||
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
@ -93,7 +94,7 @@ class MigrationManager(object):
|
|||
return
|
||||
|
||||
def convert_version(version):
|
||||
if version.startswith(release.major_version) and version != release.major_version:
|
||||
if version.count('.') >= 2:
|
||||
return version # the version number already containt the server version
|
||||
return "%s.%s" % (release.major_version, version)
|
||||
|
||||
|
@ -135,8 +136,6 @@ class MigrationManager(object):
|
|||
a.update(b)
|
||||
return a
|
||||
|
||||
from openerp.tools.parse_version import parse_version
|
||||
|
||||
parsed_installed_version = parse_version(pkg.installed_version or '')
|
||||
current_version = parse_version(convert_version(pkg.data['version']))
|
||||
|
||||
|
|
|
@ -152,8 +152,8 @@ DOMAIN_OPERATORS = (NOT_OPERATOR, OR_OPERATOR, AND_OPERATOR)
|
|||
# for consistency. This list doesn't contain '<>' as it is simpified to '!='
|
||||
# by the normalize_operator() function (so later part of the code deals with
|
||||
# only one representation).
|
||||
# An internal (i.e. not available to the user) 'inselect' operator is also
|
||||
# used. In this case its right operand has the form (subselect, params).
|
||||
# Internals (i.e. not available to the user) 'inselect' and 'not inselect'
|
||||
# operators are also used. In this case its right operand has the form (subselect, params).
|
||||
TERM_OPERATORS = ('=', '!=', '<=', '<', '>', '>=', '=?', '=like', '=ilike',
|
||||
'like', 'not like', 'ilike', 'not ilike', 'in', 'not in',
|
||||
'child_of')
|
||||
|
@ -395,7 +395,7 @@ def is_leaf(element, internal=False):
|
|||
"""
|
||||
INTERNAL_OPS = TERM_OPERATORS + ('<>',)
|
||||
if internal:
|
||||
INTERNAL_OPS += ('inselect',)
|
||||
INTERNAL_OPS += ('inselect', 'not inselect')
|
||||
return (isinstance(element, tuple) or isinstance(element, list)) \
|
||||
and len(element) == 3 \
|
||||
and element[1] in INTERNAL_OPS \
|
||||
|
@ -1024,6 +1024,12 @@ class expression(object):
|
|||
if need_wildcard:
|
||||
right = '%%%s%%' % right
|
||||
|
||||
inselect_operator = 'inselect'
|
||||
if sql_operator in NEGATIVE_TERM_OPERATORS:
|
||||
# negate operator (fix lp:1071710)
|
||||
sql_operator = sql_operator[4:] if sql_operator[:3] == 'not' else '='
|
||||
inselect_operator = 'not inselect'
|
||||
|
||||
subselect = '( SELECT res_id' \
|
||||
' FROM ir_translation' \
|
||||
' WHERE name = %s' \
|
||||
|
@ -1031,7 +1037,7 @@ class expression(object):
|
|||
' AND type = %s'
|
||||
instr = ' %s'
|
||||
#Covering in,not in operators with operands (%s,%s) ,etc.
|
||||
if sql_operator in ['in', 'not in']:
|
||||
if sql_operator == 'in':
|
||||
instr = ','.join(['%s'] * len(right))
|
||||
subselect += ' AND value ' + sql_operator + ' ' + " (" + instr + ")" \
|
||||
') UNION (' \
|
||||
|
@ -1051,7 +1057,7 @@ class expression(object):
|
|||
right,
|
||||
right,
|
||||
]
|
||||
push(create_substitution_leaf(leaf, ('id', 'inselect', (subselect, params)), working_model))
|
||||
push(create_substitution_leaf(leaf, ('id', inselect_operator, (subselect, params)), working_model))
|
||||
|
||||
else:
|
||||
push_result(leaf)
|
||||
|
@ -1072,7 +1078,7 @@ class expression(object):
|
|||
left, operator, right = leaf
|
||||
|
||||
# final sanity checks - should never fail
|
||||
assert operator in (TERM_OPERATORS + ('inselect',)), \
|
||||
assert operator in (TERM_OPERATORS + ('inselect', 'not inselect')), \
|
||||
"Invalid operator %r in domain term %r" % (operator, leaf)
|
||||
assert leaf in (TRUE_LEAF, FALSE_LEAF) or left in model._all_columns \
|
||||
or left in MAGIC_COLUMNS, "Invalid field %r in domain term %r" % (left, leaf)
|
||||
|
@ -1091,6 +1097,10 @@ class expression(object):
|
|||
query = '(%s."%s" in (%s))' % (table_alias, left, right[0])
|
||||
params = right[1]
|
||||
|
||||
elif operator == 'not inselect':
|
||||
query = '(%s."%s" not in (%s))' % (table_alias, left, right[0])
|
||||
params = right[1]
|
||||
|
||||
elif operator in ['in', 'not in']:
|
||||
# Two cases: right is a boolean or a list. The boolean case is an
|
||||
# abuse and handled for backward compatibility.
|
||||
|
|
|
@ -203,7 +203,8 @@ class reference(_column):
|
|||
model_name, res_id = value.split(',')
|
||||
if model_name in obj.pool and res_id:
|
||||
model = obj.pool[model_name]
|
||||
return model.name_get(cr, uid, [int(res_id)], context=context)[0][1]
|
||||
names = model.name_get(cr, uid, [int(res_id)], context=context)
|
||||
return names[0][1] if names else False
|
||||
return tools.ustr(value)
|
||||
|
||||
# takes a string (encoded in utf8) and returns a string (encoded in utf8)
|
||||
|
|
|
@ -77,6 +77,8 @@ from openerp.tools import SKIPPED_ELEMENT_TYPES
|
|||
regex_order = re.compile('^(([a-z0-9_]+|"[a-z0-9_]+")( *desc| *asc)?( *, *|))+$', re.I)
|
||||
regex_object_name = re.compile(r'^[a-z0-9_.]+$')
|
||||
|
||||
AUTOINIT_RECALCULATE_STORED_FIELDS = 1000
|
||||
|
||||
def transfer_field_to_modifiers(field, modifiers):
|
||||
default_values = {}
|
||||
state_exceptions = {}
|
||||
|
@ -1009,7 +1011,7 @@ class BaseModel(object):
|
|||
continue
|
||||
sm = f.store
|
||||
if sm is True:
|
||||
sm = {self._name: (lambda self, cr, uid, ids, c={}: ids, None, 10, None)}
|
||||
sm = {self._name: (lambda self, cr, uid, ids, c={}: ids, None, f.priority, None)}
|
||||
for object, aa in sm.items():
|
||||
if len(aa) == 4:
|
||||
(fnct, fields2, order, length) = aa
|
||||
|
@ -1785,7 +1787,7 @@ class BaseModel(object):
|
|||
children = False
|
||||
views = {}
|
||||
for f in node:
|
||||
if f.tag in ('form', 'tree', 'graph', 'kanban'):
|
||||
if f.tag in ('form', 'tree', 'graph', 'kanban', 'calendar'):
|
||||
node.remove(f)
|
||||
ctx = context.copy()
|
||||
ctx['base_model_name'] = self._name
|
||||
|
@ -1829,7 +1831,7 @@ class BaseModel(object):
|
|||
in_tree_view = node.tag == 'tree'
|
||||
|
||||
elif node.tag == 'calendar':
|
||||
for additional_field in ('date_start', 'date_delay', 'date_stop', 'color'):
|
||||
for additional_field in ('date_start', 'date_delay', 'date_stop', 'color', 'all_day','attendee'):
|
||||
if node.get(additional_field):
|
||||
fields[node.get(additional_field)] = {}
|
||||
|
||||
|
@ -2023,7 +2025,7 @@ class BaseModel(object):
|
|||
return False
|
||||
|
||||
view = etree.Element('calendar', string=self._description)
|
||||
etree.SubElement(view, 'field', self._rec_name_fallback(cr, user, context))
|
||||
etree.SubElement(view, 'field', name=self._rec_name_fallback(cr, user, context))
|
||||
|
||||
if self._date_name not in self._columns:
|
||||
date_found = False
|
||||
|
@ -2836,8 +2838,8 @@ class BaseModel(object):
|
|||
cr.execute('select id from '+self._table)
|
||||
ids_lst = map(lambda x: x[0], cr.fetchall())
|
||||
while ids_lst:
|
||||
iids = ids_lst[:40]
|
||||
ids_lst = ids_lst[40:]
|
||||
iids = ids_lst[:AUTOINIT_RECALCULATE_STORED_FIELDS]
|
||||
ids_lst = ids_lst[AUTOINIT_RECALCULATE_STORED_FIELDS:]
|
||||
res = f.get(cr, self, iids, k, SUPERUSER_ID, {})
|
||||
for key, val in res.items():
|
||||
if f._multi:
|
||||
|
@ -4248,7 +4250,8 @@ class BaseModel(object):
|
|||
if not src_trans:
|
||||
src_trans = vals[f]
|
||||
# Inserting value to DB
|
||||
self.write(cr, user, ids, {f: vals[f]})
|
||||
context_wo_lang = dict(context, lang=None)
|
||||
self.write(cr, user, ids, {f: vals[f]}, context=context_wo_lang)
|
||||
self.pool.get('ir.translation')._set_ids(cr, user, self._name+','+f, 'model', context['lang'], ids, vals[f], src_trans)
|
||||
|
||||
|
||||
|
@ -4410,7 +4413,15 @@ class BaseModel(object):
|
|||
tocreate[v] = {}
|
||||
else:
|
||||
tocreate[v] = {'id': vals[self._inherits[v]]}
|
||||
(upd0, upd1, upd2) = ('', '', [])
|
||||
|
||||
columns = [
|
||||
# columns will contain a list of field defined as a tuple
|
||||
# tuple(field_name, format_string, field_value)
|
||||
# the tuple will be used by the string formatting for the INSERT
|
||||
# statement.
|
||||
('id', "nextval('%s')" % self._sequence),
|
||||
]
|
||||
|
||||
upd_todo = []
|
||||
unknown_fields = []
|
||||
for v in vals.keys():
|
||||
|
@ -4427,15 +4438,12 @@ class BaseModel(object):
|
|||
'No such field(s) in model %s: %s.',
|
||||
self._name, ', '.join(unknown_fields))
|
||||
|
||||
# Try-except added to filter the creation of those records whose filds are readonly.
|
||||
# Example : any dashboard which has all the fields readonly.(due to Views(database views))
|
||||
try:
|
||||
cr.execute("SELECT nextval('"+self._sequence+"')")
|
||||
except:
|
||||
raise except_orm(_('UserError'),
|
||||
_('You cannot perform this operation. New Record Creation is not allowed for this object as this object is for reporting purpose.'))
|
||||
if not self._sequence:
|
||||
raise except_orm(
|
||||
_('UserError'),
|
||||
_('You cannot perform this operation. New Record Creation is not allowed for this object as this object is for reporting purpose.')
|
||||
)
|
||||
|
||||
id_new = cr.fetchone()[0]
|
||||
for table in tocreate:
|
||||
if self._inherits[table] in vals:
|
||||
del vals[self._inherits[table]]
|
||||
|
@ -4452,9 +4460,7 @@ class BaseModel(object):
|
|||
else:
|
||||
self.pool[table].write(cr, user, [record_id], tocreate[table], context=parent_context)
|
||||
|
||||
upd0 += ',' + self._inherits[table]
|
||||
upd1 += ',%s'
|
||||
upd2.append(record_id)
|
||||
columns.append((self._inherits[table], '%s', record_id))
|
||||
|
||||
#Start : Set bool fields to be False if they are not touched(to make search more powerful)
|
||||
bool_fields = [x for x in self._columns.keys() if self._columns[x]._type=='boolean']
|
||||
|
@ -4491,13 +4497,13 @@ class BaseModel(object):
|
|||
if not edit:
|
||||
vals.pop(field)
|
||||
for field in vals:
|
||||
if self._columns[field]._classic_write:
|
||||
upd0 = upd0 + ',"' + field + '"'
|
||||
upd1 = upd1 + ',' + self._columns[field]._symbol_set[0]
|
||||
upd2.append(self._columns[field]._symbol_set[1](vals[field]))
|
||||
current_field = self._columns[field]
|
||||
if current_field._classic_write:
|
||||
columns.append((field, '%s', current_field._symbol_set[1](vals[field])))
|
||||
|
||||
#for the function fields that receive a value, we set them directly in the database
|
||||
#(they may be required), but we also need to trigger the _fct_inv()
|
||||
if (hasattr(self._columns[field], '_fnct_inv')) and not isinstance(self._columns[field], fields.related):
|
||||
if (hasattr(current_field, '_fnct_inv')) and not isinstance(current_field, fields.related):
|
||||
#TODO: this way to special case the related fields is really creepy but it shouldn't be changed at
|
||||
#one week of the release candidate. It seems the only good way to handle correctly this is to add an
|
||||
#attribute to make a field `really readonly´ and thus totally ignored by the create()... otherwise
|
||||
|
@ -4509,17 +4515,33 @@ class BaseModel(object):
|
|||
else:
|
||||
#TODO: this `if´ statement should be removed because there is no good reason to special case the fields
|
||||
#related. See the above TODO comment for further explanations.
|
||||
if not isinstance(self._columns[field], fields.related):
|
||||
if not isinstance(current_field, fields.related):
|
||||
upd_todo.append(field)
|
||||
if field in self._columns \
|
||||
and hasattr(self._columns[field], 'selection') \
|
||||
and hasattr(current_field, 'selection') \
|
||||
and vals[field]:
|
||||
self._check_selection_field_value(cr, user, field, vals[field], context=context)
|
||||
if self._log_access:
|
||||
upd0 += ',create_uid,create_date,write_uid,write_date'
|
||||
upd1 += ",%s,(now() at time zone 'UTC'),%s,(now() at time zone 'UTC')"
|
||||
upd2.extend((user, user))
|
||||
cr.execute('insert into "'+self._table+'" (id'+upd0+") values ("+str(id_new)+upd1+')', tuple(upd2))
|
||||
columns.append(('create_uid', '%s', user))
|
||||
columns.append(('write_uid', '%s', user))
|
||||
columns.append(('create_date', "(now() at time zone 'UTC')"))
|
||||
columns.append(('write_date', "(now() at time zone 'UTC')"))
|
||||
|
||||
# the list of tuples used in this formatting corresponds to
|
||||
# tuple(field_name, format, value)
|
||||
# In some case, for example (id, create_date, write_date) we does not
|
||||
# need to read the third value of the tuple, because the real value is
|
||||
# encoded in the second value (the format).
|
||||
cr.execute(
|
||||
"""INSERT INTO "%s" (%s) VALUES(%s) RETURNING id""" % (
|
||||
self._table,
|
||||
', '.join('"%s"' % f[0] for f in columns),
|
||||
', '.join(f[1] for f in columns)
|
||||
),
|
||||
tuple([f[2] for f in columns if len(f) > 2])
|
||||
)
|
||||
|
||||
id_new, = cr.fetchone()
|
||||
upd_todo.sort(lambda x, y: self._columns[x].priority-self._columns[y].priority)
|
||||
|
||||
if self._parent_store and not context.get('defer_parent_store_computation'):
|
||||
|
@ -4558,7 +4580,9 @@ class BaseModel(object):
|
|||
self._validate(cr, user, [id_new], context)
|
||||
|
||||
if not context.get('no_store_function', False):
|
||||
result += self._store_get_values(cr, user, [id_new], vals.keys(), context)
|
||||
result += self._store_get_values(cr, user, [id_new],
|
||||
list(set(vals.keys() + self._inherits.values())),
|
||||
context)
|
||||
result.sort()
|
||||
done = []
|
||||
for order, model_name, ids, fields2 in result:
|
||||
|
@ -4783,6 +4807,9 @@ class BaseModel(object):
|
|||
|
||||
:param query: the current query object
|
||||
"""
|
||||
if uid == SUPERUSER_ID:
|
||||
return
|
||||
|
||||
def apply_rule(added_clause, added_params, added_tables, parent_model=None, child_object=None):
|
||||
""" :param string parent_model: string of the parent model
|
||||
:param model child_object: model object, base of the rule application
|
||||
|
@ -5423,6 +5450,11 @@ class BaseModel(object):
|
|||
record_ids = self.search(cr, uid, domain or [], offset, limit or False, order or False, context or {})
|
||||
if not record_ids:
|
||||
return []
|
||||
|
||||
if fields and fields == ['id']:
|
||||
# shortcut read if we only want the ids
|
||||
return [{'id': id} for id in record_ids]
|
||||
|
||||
result = self.read(cr, uid, record_ids, fields or [], context or {})
|
||||
# reorder read
|
||||
if len(result) >= 1:
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
from lxml import etree
|
||||
import unittest2
|
||||
from . import test_mail_examples
|
||||
from openerp.tools import html_sanitize, html_email_clean, append_content_to_html, plaintext2html
|
||||
from openerp.tools import html_sanitize, html_email_clean, append_content_to_html, plaintext2html, email_split
|
||||
|
||||
class TestSanitizer(unittest2.TestCase):
|
||||
""" Test the html sanitizer that filters html to remove unwanted attributes """
|
||||
|
@ -325,6 +325,19 @@ class TestHtmlTools(unittest2.TestCase):
|
|||
for html, content, plaintext_flag, preserve_flag, container_tag, expected in test_samples:
|
||||
self.assertEqual(append_content_to_html(html, content, plaintext_flag, preserve_flag, container_tag), expected, 'append_content_to_html is broken')
|
||||
|
||||
class TestEmailTools(unittest2.TestCase):
|
||||
""" Test some of our generic utility functions for emails """
|
||||
|
||||
def test_email_split(self):
|
||||
cases = [
|
||||
("John <12345@gmail.com>", ['12345@gmail.com']), # regular form
|
||||
("d@x; 1@2", ['d@x', '1@2']), # semi-colon + extra space
|
||||
("'(ss)' <123@gmail.com>, 'foo' <foo@bar>", ['123@gmail.com','foo@bar']), # comma + single-quoting
|
||||
('"john@gmail.com"<johnny@gmail.com>', ['johnny@gmail.com']), # double-quoting
|
||||
('"<jg>" <johnny@gmail.com>', ['johnny@gmail.com']), # double-quoting with brackets
|
||||
]
|
||||
for text, expected in cases:
|
||||
self.assertEqual(email_split(text), expected, 'email_split is broken')
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest2.main()
|
||||
|
|
|
@ -29,6 +29,7 @@ import re
|
|||
import socket
|
||||
import threading
|
||||
import time
|
||||
from email.utils import getaddresses
|
||||
|
||||
import openerp
|
||||
from openerp.loglevels import ustr
|
||||
|
@ -559,4 +560,9 @@ def email_split(text):
|
|||
""" Return a list of the email addresses found in ``text`` """
|
||||
if not text:
|
||||
return []
|
||||
return re.findall(r'([^ ,<@]+@[^> ,]+)', text)
|
||||
return [addr[1] for addr in getaddresses([text])
|
||||
# getaddresses() returns '' when email parsing fails, and
|
||||
# sometimes returns emails without at least '@'. The '@'
|
||||
# is strictly required in RFC2822's `addr-spec`.
|
||||
if addr[1]
|
||||
if '@' in addr[1]]
|
|
@ -961,6 +961,7 @@ def trans_load_data(cr, fileobj, fileformat, lang, lang_name=None, verbose=True,
|
|||
irt_cursor.push(dic)
|
||||
|
||||
irt_cursor.finish()
|
||||
trans_obj.clear_caches()
|
||||
if verbose:
|
||||
_logger.info("translation file loaded succesfully")
|
||||
except IOError:
|
||||
|
|
5
setup.py
5
setup.py
|
@ -125,17 +125,20 @@ setuptools.setup(
|
|||
'lxml', # windows binary http://www.lfd.uci.edu/~gohlke/pythonlibs/
|
||||
'mako',
|
||||
'mock',
|
||||
'PIL', # windows binary http://www.lfd.uci.edu/~gohlke/pythonlibs/
|
||||
'pillow', # windows binary http://www.lfd.uci.edu/~gohlke/pythonlibs/
|
||||
'psutil', # windows binary code.google.com/p/psutil/downloads/list
|
||||
'psycopg2 >= 2.2',
|
||||
'pydot',
|
||||
'pyparsing < 2',
|
||||
'pyserial',
|
||||
'python-dateutil < 2',
|
||||
'python-ldap', # optional
|
||||
'python-openid',
|
||||
'pytz',
|
||||
'pyusb >= 1.0.0b1',
|
||||
'pywebdav',
|
||||
'pyyaml',
|
||||
'qrcode',
|
||||
'reportlab', # windows binary pypi.python.org/pypi/reportlab
|
||||
'simplejson',
|
||||
'unittest2',
|
||||
|
|
|
@ -13,3 +13,6 @@ python setup.py install --optimize 1 --root=$RPM_BUILD_ROOT --record=INSTALLED_F
|
|||
|
||||
SUFFIX=gz
|
||||
sed "s!\(/share/man/.*\)!\1.$SUFFIX!" -i INSTALLED_FILES
|
||||
|
||||
# quote files with a space
|
||||
sed 's/\(.* .*\)/"\1"/' -i INSTALLED_FILES
|
||||
|
|
Loading…
Reference in New Issue