merge upstream
bzr revid: chs@openerp.com-20121107112830-fi0xu1poqt5cbb8u
This commit is contained in:
commit
9bb5e47baf
26
README
26
README
|
@ -6,17 +6,9 @@ Customer Relationship Management software. More info at:
|
|||
|
||||
http://www.openerp.com
|
||||
|
||||
Installation on Debian Ubuntu
|
||||
Installation on Debian/Ubuntu
|
||||
-----------------------------
|
||||
|
||||
Download the deb file and type:
|
||||
|
||||
$ sudo dpkg -i <openerp-deb-filename>
|
||||
$ sudo apt-get install install -f
|
||||
|
||||
Installation on Debian Ubuntu from nightly build
|
||||
------------------------------------------------
|
||||
|
||||
Add the the apt repository
|
||||
|
||||
deb http://nightly.openerp.com/6.1/deb/ ./
|
||||
|
@ -26,6 +18,11 @@ in your source.list and type:
|
|||
$ sudo apt-get update
|
||||
$ sudo apt-get install openerp
|
||||
|
||||
Or download the deb file and type:
|
||||
|
||||
$ sudo dpkg -i <openerp-deb-filename>
|
||||
$ sudo apt-get install install -f
|
||||
|
||||
Installation on RedHat, Fedora, CentOS
|
||||
--------------------------------------
|
||||
|
||||
|
@ -42,6 +39,8 @@ Install the openerp rpm
|
|||
Installation on Windows
|
||||
-----------------------
|
||||
|
||||
Check the notes in setup.py
|
||||
|
||||
Installation on MacOSX
|
||||
-----------------------
|
||||
|
||||
|
@ -54,14 +53,7 @@ default master password is "admin".
|
|||
Detailed System Requirements
|
||||
----------------------------
|
||||
|
||||
You need the following software installed:
|
||||
|
||||
postgresql-client, python-dateutil, python-feedparser, python-gdata,
|
||||
python-ldap, python-libxslt1, python-lxml, python-mako, python-openid,
|
||||
python-psycopg2, python-pybabel, python-pychart, python-pydot,
|
||||
python-pyparsing, python-reportlab, python-simplejson, python-tz,
|
||||
python-vatnumber, python-vobject, python-webdav, python-werkzeug, python-xlwt,
|
||||
python-yaml, python-zsi
|
||||
The dependencies are listed in setup.py
|
||||
|
||||
For Luxembourg localization, you also need:
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-11-05 04:40+0000\n"
|
||||
"X-Launchpad-Export-Date: 2012-11-06 04:50+0000\n"
|
||||
"X-Generator: Launchpad (build 16232)\n"
|
||||
|
||||
#. module: base
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import collections
|
||||
import datetime
|
||||
import functools
|
||||
import operator
|
||||
|
@ -31,9 +32,70 @@ REPLACE_WITH = lambda ids: (6, False, ids)
|
|||
|
||||
class ConversionNotFound(ValueError): pass
|
||||
|
||||
class ColumnWrapper(object):
|
||||
def __init__(self, column, cr, uid, pool, fromtype, context=None):
|
||||
self._converter = None
|
||||
self._column = column
|
||||
if column._obj:
|
||||
self._pool = pool
|
||||
self._converter_args = {
|
||||
'cr': cr,
|
||||
'uid': uid,
|
||||
'model': pool[column._obj],
|
||||
'fromtype': fromtype,
|
||||
'context': context
|
||||
}
|
||||
@property
|
||||
def converter(self):
|
||||
if not self._converter:
|
||||
self._converter = self._pool['ir.fields.converter'].for_model(
|
||||
**self._converter_args)
|
||||
return self._converter
|
||||
|
||||
def __getattr__(self, item):
|
||||
return getattr(self._column, item)
|
||||
|
||||
class ir_fields_converter(orm.Model):
|
||||
_name = 'ir.fields.converter'
|
||||
|
||||
def for_model(self, cr, uid, model, fromtype=str, context=None):
|
||||
""" Returns a converter object for the model. A converter is a
|
||||
callable taking a record-ish (a dictionary representing an openerp
|
||||
record with values of typetag ``fromtype``) and returning a converted
|
||||
records matching what :meth:`openerp.osv.orm.Model.write` expects.
|
||||
|
||||
:param model: :class:`openerp.osv.orm.Model` for the conversion base
|
||||
:returns: a converter callable
|
||||
:rtype: (record: dict, logger: (field, error) -> None) -> dict
|
||||
"""
|
||||
columns = dict(
|
||||
(k, ColumnWrapper(v.column, cr, uid, self.pool, fromtype, context))
|
||||
for k, v in model._all_columns.iteritems())
|
||||
converters = dict(
|
||||
(k, self.to_field(cr, uid, model, column, fromtype, context))
|
||||
for k, column in columns.iteritems())
|
||||
|
||||
def fn(record, log):
|
||||
converted = {}
|
||||
for field, value in record.iteritems():
|
||||
if field in (None, 'id', '.id'): continue
|
||||
if not value:
|
||||
converted[field] = False
|
||||
continue
|
||||
try:
|
||||
converted[field], ws = converters[field](value)
|
||||
for w in ws:
|
||||
if isinstance(w, basestring):
|
||||
# wrap warning string in an ImportWarning for
|
||||
# uniform handling
|
||||
w = ImportWarning(w)
|
||||
log(field, w)
|
||||
except ValueError, e:
|
||||
log(field, e)
|
||||
|
||||
return converted
|
||||
return fn
|
||||
|
||||
def to_field(self, cr, uid, model, column, fromtype=str, context=None):
|
||||
""" Fetches a converter for the provided column object, from the
|
||||
specified type.
|
||||
|
@ -343,6 +405,10 @@ class ir_fields_converter(orm.Model):
|
|||
# [{subfield:ref1},{subfield:ref2},{subfield:ref3}]
|
||||
records = ({subfield:item} for item in record[subfield].split(','))
|
||||
|
||||
def log(_, e):
|
||||
if not isinstance(e, Warning):
|
||||
raise e
|
||||
warnings.append(e)
|
||||
for record in records:
|
||||
id = None
|
||||
refs = only_ref_fields(record)
|
||||
|
@ -355,7 +421,7 @@ class ir_fields_converter(orm.Model):
|
|||
cr, uid, model, column, subfield, reference, context=context)
|
||||
warnings.extend(w2)
|
||||
|
||||
writable = exclude_ref_fields(record)
|
||||
writable = column.converter(exclude_ref_fields(record), log)
|
||||
if id:
|
||||
commands.append(LINK_TO(id))
|
||||
commands.append(UPDATE(id, writable))
|
||||
|
|
|
@ -24,6 +24,7 @@ import logging
|
|||
|
||||
import openerp.modules
|
||||
from openerp.osv import fields, osv
|
||||
from tools.translate import _
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
@ -336,6 +337,8 @@ class ir_translation(osv.osv):
|
|||
trans_model = self.pool.get(model)
|
||||
domain = ['&', ('res_id', '=', id), ('name', '=like', model + ',%')]
|
||||
langs_ids = self.pool.get('res.lang').search(cr, uid, [('code', '!=', 'en_US')], context=context)
|
||||
if not langs_ids:
|
||||
raise osv.except_osv(_('Error'), _("Translation features are unavailable until you install an extra OpenERP translation."))
|
||||
langs = [lg.code for lg in self.pool.get('res.lang').browse(cr, uid, langs_ids, context=context)]
|
||||
main_lang = 'en_US'
|
||||
translatable_fields = []
|
||||
|
|
|
@ -264,10 +264,15 @@ class res_partner(osv.osv, format_address):
|
|||
return False
|
||||
|
||||
def _get_default_image(self, cr, uid, is_company, context=None, colorize=False):
|
||||
if is_company:
|
||||
image = open(openerp.modules.get_module_resource('base', 'static/src/img', 'company_image.png')).read()
|
||||
else:
|
||||
image = tools.image_colorize(open(openerp.modules.get_module_resource('base', 'static/src/img', 'avatar.png')).read())
|
||||
img_path = openerp.modules.get_module_resource('base', 'static/src/img',
|
||||
('company_image.png' if is_company else 'avatar.png'))
|
||||
with open(img_path, 'rb') as f:
|
||||
image = f.read()
|
||||
|
||||
# colorize user avatars
|
||||
if not is_company:
|
||||
image = tools.image_colorize(image)
|
||||
|
||||
return tools.image_resize_image_big(image.encode('base64'))
|
||||
|
||||
def fields_view_get(self, cr, user, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
|
||||
|
|
|
@ -38,7 +38,6 @@ import openerp.osv as osv
|
|||
import openerp.pooler as pooler
|
||||
import openerp.release as release
|
||||
import openerp.tools as tools
|
||||
import openerp.tools.assertion_report as assertion_report
|
||||
from openerp import SUPERUSER_ID
|
||||
|
||||
from openerp import SUPERUSER_ID
|
||||
|
@ -285,7 +284,7 @@ def load_modules(db, force_demo=False, status=None, update_module=False):
|
|||
|
||||
# processed_modules: for cleanup step after install
|
||||
# loaded_modules: to avoid double loading
|
||||
report = assertion_report.assertion_report()
|
||||
report = pool._assertion_report
|
||||
loaded_modules, processed_modules = load_module_graph(cr, graph, status, perform_checks=(not update_module), report=report)
|
||||
|
||||
if tools.config['load_language']:
|
||||
|
|
|
@ -32,6 +32,7 @@ import openerp.cron
|
|||
import openerp.tools
|
||||
import openerp.modules.db
|
||||
import openerp.tools.config
|
||||
from openerp.tools import assertion_report
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
@ -49,6 +50,7 @@ class Registry(object):
|
|||
self._store_function = {}
|
||||
self._init = True
|
||||
self._init_parent = {}
|
||||
self._assertion_report = assertion_report.assertion_report()
|
||||
|
||||
# modules fully loaded (maintained during init phase by `loading` module)
|
||||
self._init_modules = set()
|
||||
|
|
|
@ -1152,96 +1152,51 @@ class related(function):
|
|||
"""
|
||||
|
||||
def _fnct_search(self, tobj, cr, uid, obj=None, name=None, domain=None, context=None):
|
||||
self._field_get2(cr, uid, obj, context)
|
||||
i = len(self._arg)-1
|
||||
sarg = name
|
||||
while i>0:
|
||||
if type(sarg) in [type([]), type( (1,) )]:
|
||||
where = [(self._arg[i], 'in', sarg)]
|
||||
else:
|
||||
where = [(self._arg[i], '=', sarg)]
|
||||
if domain:
|
||||
where = map(lambda x: (self._arg[i],x[1], x[2]), domain)
|
||||
domain = []
|
||||
sarg = obj.pool.get(self._relations[i]['object']).search(cr, uid, where, context=context)
|
||||
i -= 1
|
||||
return [(self._arg[0], 'in', sarg)]
|
||||
# assume self._arg = ('foo', 'bar', 'baz')
|
||||
# domain = [(name, op, val)] => search [('foo.bar.baz', op, val)]
|
||||
field = '.'.join(self._arg)
|
||||
return map(lambda x: (field, x[1], x[2]), domain)
|
||||
|
||||
def _fnct_write(self,obj,cr, uid, ids, field_name, values, args, context=None):
|
||||
self._field_get2(cr, uid, obj, context=context)
|
||||
if type(ids) != type([]):
|
||||
ids=[ids]
|
||||
objlst = obj.browse(cr, uid, ids)
|
||||
for data in objlst:
|
||||
t_id = data.id
|
||||
t_data = data
|
||||
for i in range(len(self.arg)):
|
||||
if not t_data: break
|
||||
field_detail = self._relations[i]
|
||||
if not t_data[self.arg[i]]:
|
||||
if self._type not in ('one2many', 'many2many'):
|
||||
t_id = t_data['id']
|
||||
t_data = False
|
||||
elif field_detail['type'] in ('one2many', 'many2many'):
|
||||
if self._type != "many2one":
|
||||
t_id = t_data.id
|
||||
t_data = t_data[self.arg[i]][0]
|
||||
else:
|
||||
t_data = False
|
||||
else:
|
||||
t_id = t_data['id']
|
||||
t_data = t_data[self.arg[i]]
|
||||
else:
|
||||
model = obj.pool.get(self._relations[-1]['object'])
|
||||
model.write(cr, uid, [t_id], {args[-1]: values}, context=context)
|
||||
if isinstance(ids, (int, long)):
|
||||
ids = [ids]
|
||||
for record in obj.browse(cr, uid, ids, context=context):
|
||||
# traverse all fields except the last one
|
||||
for field in self.arg[:-1]:
|
||||
record = record[field] or False
|
||||
if not record:
|
||||
break
|
||||
elif isinstance(record, list):
|
||||
# record is the result of a one2many or many2many field
|
||||
record = record[0]
|
||||
if record:
|
||||
# write on the last field
|
||||
record.write({self.arg[-1]: values})
|
||||
|
||||
def _fnct_read(self, obj, cr, uid, ids, field_name, args, context=None):
|
||||
self._field_get2(cr, uid, obj, context)
|
||||
if not ids: return {}
|
||||
relation = obj._name
|
||||
if self._type in ('one2many', 'many2many'):
|
||||
res = dict([(i, []) for i in ids])
|
||||
else:
|
||||
res = {}.fromkeys(ids, False)
|
||||
|
||||
objlst = obj.browse(cr, SUPERUSER_ID, ids, context=context)
|
||||
for data in objlst:
|
||||
if not data:
|
||||
continue
|
||||
t_data = data
|
||||
relation = obj._name
|
||||
for i in range(len(self.arg)):
|
||||
field_detail = self._relations[i]
|
||||
relation = field_detail['object']
|
||||
try:
|
||||
if not t_data[self.arg[i]]:
|
||||
t_data = False
|
||||
break
|
||||
except:
|
||||
t_data = False
|
||||
res = {}
|
||||
for record in obj.browse(cr, SUPERUSER_ID, ids, context=context):
|
||||
value = record
|
||||
for field in self.arg:
|
||||
if isinstance(value, list):
|
||||
value = value[0]
|
||||
value = value[field] or False
|
||||
if not value:
|
||||
break
|
||||
if field_detail['type'] in ('one2many', 'many2many') and i != len(self.arg) - 1:
|
||||
t_data = t_data[self.arg[i]][0]
|
||||
elif t_data:
|
||||
t_data = t_data[self.arg[i]]
|
||||
if type(t_data) == type(objlst[0]):
|
||||
res[data.id] = t_data.id
|
||||
elif t_data:
|
||||
res[data.id] = t_data
|
||||
if self._type=='many2one':
|
||||
ids = filter(None, res.values())
|
||||
if ids:
|
||||
# name_get as root, as seeing the name of a related
|
||||
# object depends on access right of source document,
|
||||
# not target, so user may not have access.
|
||||
ng = dict(obj.pool.get(self._obj).name_get(cr, SUPERUSER_ID, ids, context=context))
|
||||
for r in res:
|
||||
if res[r]:
|
||||
res[r] = (res[r], ng[res[r]])
|
||||
res[record.id] = value
|
||||
|
||||
if self._type == 'many2one':
|
||||
# res[id] is a browse_record or False; convert it to (id, name) or False.
|
||||
# Perform name_get as root, as seeing the name of a related object depends on
|
||||
# access right of source document, not target, so user may not have access.
|
||||
value_ids = list(set(value.id for value in res.itervalues() if value))
|
||||
value_name = dict(obj.pool.get(self._obj).name_get(cr, SUPERUSER_ID, value_ids, context=context))
|
||||
res = dict((id, value and (value.id, value_name[value.id])) for id, value in res.iteritems())
|
||||
|
||||
elif self._type in ('one2many', 'many2many'):
|
||||
for r in res:
|
||||
if res[r]:
|
||||
res[r] = [x.id for x in res[r]]
|
||||
# res[id] is a list of browse_record or False; convert it to a list of ids
|
||||
res = dict((id, value and map(int, value) or []) for id, value in res.iteritems())
|
||||
|
||||
return res
|
||||
|
||||
def __init__(self, *arg, **args):
|
||||
|
@ -1252,22 +1207,6 @@ class related(function):
|
|||
# TODO: improve here to change self.store = {...} according to related objects
|
||||
pass
|
||||
|
||||
def _field_get2(self, cr, uid, obj, context=None):
|
||||
if self._relations:
|
||||
return
|
||||
result = []
|
||||
obj_name = obj._name
|
||||
for i in range(len(self._arg)):
|
||||
f = obj.pool.get(obj_name).fields_get(cr, uid, [self._arg[i]], context=context)[self._arg[i]]
|
||||
result.append({
|
||||
'object': obj_name,
|
||||
'type': f['type']
|
||||
|
||||
})
|
||||
if f.get('relation',False):
|
||||
obj_name = f['relation']
|
||||
result[-1]['relation'] = f['relation']
|
||||
self._relations = result
|
||||
|
||||
class sparse(function):
|
||||
|
||||
|
|
|
@ -1352,9 +1352,11 @@ class BaseModel(object):
|
|||
noupdate=noupdate, res_id=id, context=context))
|
||||
cr.execute('RELEASE SAVEPOINT model_load_save')
|
||||
except psycopg2.Warning, e:
|
||||
_logger.exception('Failed to import record %s', record)
|
||||
cr.execute('ROLLBACK TO SAVEPOINT model_load_save')
|
||||
messages.append(dict(info, type='warning', message=str(e)))
|
||||
except psycopg2.Error, e:
|
||||
_logger.exception('Failed to import record %s', record)
|
||||
# Failed to write, log to messages, rollback savepoint (to
|
||||
# avoid broken transaction) and keep going
|
||||
cr.execute('ROLLBACK TO SAVEPOINT model_load_save')
|
||||
|
@ -1458,15 +1460,16 @@ class BaseModel(object):
|
|||
field_names = dict(
|
||||
(f, (Translation._get_source(cr, uid, self._name + ',' + f, 'field',
|
||||
context.get('lang'))
|
||||
or column.string or f))
|
||||
or column.string))
|
||||
for f, column in columns.iteritems())
|
||||
converters = dict(
|
||||
(k, Converter.to_field(cr, uid, self, column, context=context))
|
||||
for k, column in columns.iteritems())
|
||||
|
||||
convert = Converter.for_model(cr, uid, self, context=context)
|
||||
|
||||
def _log(base, field, exception):
|
||||
type = 'warning' if isinstance(exception, Warning) else 'error'
|
||||
record = dict(base, field=field, type=type,
|
||||
# logs the logical (not human-readable) field name for automated
|
||||
# processing of response, but injects human readable in message
|
||||
record = dict(base, type=type, field=field,
|
||||
message=unicode(exception.args[0]) % base)
|
||||
if len(exception.args) > 1 and exception.args[1]:
|
||||
record.update(exception.args[1])
|
||||
|
@ -1476,7 +1479,6 @@ class BaseModel(object):
|
|||
for record, extras in stream:
|
||||
dbid = False
|
||||
xid = False
|
||||
converted = {}
|
||||
# name_get/name_create
|
||||
if None in record: pass
|
||||
# xid
|
||||
|
@ -1497,27 +1499,8 @@ class BaseModel(object):
|
|||
message=_(u"Unknown database identifier '%s'") % dbid))
|
||||
dbid = False
|
||||
|
||||
for field, strvalue in record.iteritems():
|
||||
if field in (None, 'id', '.id'): continue
|
||||
if not strvalue:
|
||||
converted[field] = False
|
||||
continue
|
||||
|
||||
# In warnings and error messages, use translated string as
|
||||
# field name
|
||||
message_base = dict(
|
||||
extras, record=stream.index, field=field_names[field])
|
||||
try:
|
||||
converted[field], ws = converters[field](strvalue)
|
||||
|
||||
for w in ws:
|
||||
if isinstance(w, basestring):
|
||||
# wrap warning string in an ImportWarning for
|
||||
# uniform handling
|
||||
w = ImportWarning(w)
|
||||
_log(message_base, field, w)
|
||||
except ValueError, e:
|
||||
_log(message_base, field, e)
|
||||
converted = convert(record, lambda field, err:\
|
||||
_log(dict(extras, record=stream.index, field=field_names[field]), field, err))
|
||||
|
||||
yield dbid, xid, converted, dict(extras, record=stream.index)
|
||||
|
||||
|
@ -3878,10 +3861,12 @@ class BaseModel(object):
|
|||
getattr(wf_service, trigger)(uid, self._name, res_id, cr)
|
||||
|
||||
def _workflow_signal(self, cr, uid, ids, signal, context=None):
|
||||
"""Send given workflow signal"""
|
||||
"""Send given workflow signal and return a dict mapping ids to workflow results"""
|
||||
wf_service = netsvc.LocalService("workflow")
|
||||
result = {}
|
||||
for res_id in ids:
|
||||
wf_service.trg_validate(uid, self._name, res_id, signal, cr)
|
||||
result[res_id] = wf_service.trg_validate(uid, self._name, res_id, signal, cr)
|
||||
return result
|
||||
|
||||
def unlink(self, cr, uid, ids, context=None):
|
||||
"""
|
||||
|
|
|
@ -188,7 +188,8 @@ class object_proxy(object):
|
|||
object = pooler.get_pool(cr.dbname).get(obj)
|
||||
if not object:
|
||||
raise except_osv('Object Error', 'Object %s doesn\'t exist' % str(obj))
|
||||
return object._workflow_signal(cr, uid, [args[0]], signal)
|
||||
res_id = args[0]
|
||||
return object._workflow_signal(cr, uid, [res_id], signal)[res_id]
|
||||
|
||||
@check
|
||||
def exec_workflow(self, db, uid, obj, signal, *args):
|
||||
|
|
|
@ -34,7 +34,6 @@ import openerp.netsvc
|
|||
import openerp.osv
|
||||
import openerp.tools
|
||||
import openerp.service.wsgi_server
|
||||
import openerp.service.workers
|
||||
|
||||
#.apidoc title: RPC Services
|
||||
|
||||
|
@ -123,6 +122,7 @@ def stop_services():
|
|||
logging.shutdown()
|
||||
|
||||
def start_services_workers():
|
||||
import openerp.service.workers
|
||||
openerp.multi_process = True
|
||||
openerp.service.workers.Multicorn(openerp.service.wsgi_server.application).run()
|
||||
|
||||
|
|
|
@ -3,20 +3,17 @@
|
|||
# TODO rename class: Multicorn -> Arbiter ?
|
||||
#-----------------------------------------------------------
|
||||
import errno
|
||||
try:
|
||||
import fcntl
|
||||
except ImportError:
|
||||
fcntl = None
|
||||
import fcntl
|
||||
import logging
|
||||
import os
|
||||
import psutil
|
||||
import random
|
||||
import resource
|
||||
import select
|
||||
import socket
|
||||
import time
|
||||
import logging
|
||||
import os
|
||||
import signal
|
||||
import socket
|
||||
import sys
|
||||
import time
|
||||
|
||||
import werkzeug.serving
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ See the :ref:`test-framework` section in the :ref:`features` list.
|
|||
"""
|
||||
|
||||
from . import test_expression, test_html_sanitize, test_ir_sequence, test_orm,\
|
||||
test_basecase, \
|
||||
test_fields, test_basecase, \
|
||||
test_view_validation, test_uninstall, test_misc, test_db_cursor
|
||||
|
||||
fast_suite = [
|
||||
|
@ -21,6 +21,7 @@ checks = [
|
|||
test_html_sanitize,
|
||||
test_db_cursor,
|
||||
test_orm,
|
||||
test_fields,
|
||||
test_basecase,
|
||||
test_view_validation,
|
||||
test_misc,
|
||||
|
|
|
@ -79,6 +79,7 @@ class One2ManyMultiple(orm.Model):
|
|||
_name = 'export.one2many.multiple'
|
||||
|
||||
_columns = {
|
||||
'parent_id': fields.many2one('export.one2many.recursive'),
|
||||
'const': fields.integer(),
|
||||
'child1': fields.one2many('export.one2many.child.1', 'parent_id'),
|
||||
'child2': fields.one2many('export.one2many.child.2', 'parent_id'),
|
||||
|
@ -135,3 +136,11 @@ class SelectionWithDefault(orm.Model):
|
|||
'const': 4,
|
||||
'value': 2,
|
||||
}
|
||||
|
||||
class RecO2M(orm.Model):
|
||||
_name = 'export.one2many.recursive'
|
||||
|
||||
_columns = {
|
||||
'value': fields.integer(),
|
||||
'child': fields.one2many('export.one2many.multiple', 'parent_id')
|
||||
}
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
[["Wood y Wood Pecker", "", "Snow Street, 25", "Kainuu", "Finland", "Supplier", "1", "0", "1", ""], ["Roger Pecker", "Default", "Snow Street, 27", "Kainuu", "Finland", "Supplier", "1", "0", "0", "Wood y Wood Pecker"], ["Sharon Pecker", "Shipping", "Snow Street, 28", "Kainuu", "Finland", "Supplier", "1", "0", "0", "Wood y Wood Pecker"], ["Thomas Pecker", "Contact", "Snow Street, 27", "Kainuu", "Finland", "Supplier", "1", "0", "0", "Wood y Wood Pecker"], ["Norseman Roundabout", "", "Atonium Street, 45a", "Brussels", "Belgium", "Supplier", "1", "0", "1", ""], ["Yvan Holiday", "Invoice", "Atonium Street, 45b", "Brussels", "Belgium", "Supplier", "1", "0", "0", "Norseman Roundabout"], ["Jack Unsworth", "Contact", "Atonium Street, 45a", "Brussels", "Belgium", "Supplier", "1", "0", "0", "Norseman Roundabout"]]
|
|
@ -1,6 +1,7 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import json
|
||||
import pkgutil
|
||||
import unittest2
|
||||
|
||||
import openerp.modules.registry
|
||||
import openerp
|
||||
|
@ -246,7 +247,7 @@ class test_integer_field(ImporterCase):
|
|||
-1, -42, -(2**31 - 1), -(2**31), -12345678
|
||||
], values(self.read()))
|
||||
|
||||
@mute_logger('openerp.sql_db')
|
||||
@mute_logger('openerp.sql_db', 'openerp.osv.orm')
|
||||
def test_out_of_range(self):
|
||||
result = self.import_(['value'], [[str(2**31)]])
|
||||
self.assertIs(result['ids'], False)
|
||||
|
@ -388,14 +389,14 @@ class test_unbound_string_field(ImporterCase):
|
|||
class test_required_string_field(ImporterCase):
|
||||
model_name = 'export.string.required'
|
||||
|
||||
@mute_logger('openerp.sql_db')
|
||||
@mute_logger('openerp.sql_db', 'openerp.osv.orm')
|
||||
def test_empty(self):
|
||||
result = self.import_(['value'], [[]])
|
||||
self.assertEqual(result['messages'], [message(
|
||||
u"Missing required value for the field 'unknown'")])
|
||||
self.assertIs(result['ids'], False)
|
||||
|
||||
@mute_logger('openerp.sql_db')
|
||||
@mute_logger('openerp.sql_db', 'openerp.osv.orm')
|
||||
def test_not_provided(self):
|
||||
result = self.import_(['const'], [['12']])
|
||||
self.assertEqual(result['messages'], [message(
|
||||
|
@ -1007,6 +1008,46 @@ class test_realworld(common.TransactionCase):
|
|||
self.assertFalse(result['messages'])
|
||||
self.assertEqual(len(result['ids']), len(data))
|
||||
|
||||
def test_backlink(self):
|
||||
data = json.loads(pkgutil.get_data(self.__module__, 'contacts.json'))
|
||||
result = self.registry('res.partner').load(
|
||||
self.cr, openerp.SUPERUSER_ID,
|
||||
["name", "type", "street", "city", "country_id", "category_id",
|
||||
"supplier", "customer", "is_company", "parent_id"],
|
||||
data)
|
||||
self.assertFalse(result['messages'])
|
||||
self.assertEqual(len(result['ids']), len(data))
|
||||
|
||||
def test_recursive_o2m(self):
|
||||
""" The content of the o2m field's dict needs to go through conversion
|
||||
as it may be composed of convertables or other relational fields
|
||||
"""
|
||||
self.registry('ir.model.data').clear_caches()
|
||||
Model = self.registry('export.one2many.recursive')
|
||||
result = Model.load(self.cr, openerp.SUPERUSER_ID,
|
||||
['value', 'child/const', 'child/child1/str', 'child/child2/value'],
|
||||
[
|
||||
['4', '42', 'foo', '55'],
|
||||
['', '43', 'bar', '56'],
|
||||
['', '', 'baz', ''],
|
||||
['', '55', 'qux', '57'],
|
||||
['5', '99', 'wheee', ''],
|
||||
['', '98', '', '12'],
|
||||
],
|
||||
context=None)
|
||||
|
||||
self.assertFalse(result['messages'])
|
||||
self.assertEqual(len(result['ids']), 2)
|
||||
|
||||
b = Model.browse(self.cr, openerp.SUPERUSER_ID, result['ids'], context=None)
|
||||
self.assertEqual((b[0].value, b[1].value), (4, 5))
|
||||
|
||||
self.assertEqual([child.str for child in b[0].child[1].child1],
|
||||
['bar', 'baz'])
|
||||
self.assertFalse(len(b[1].child[1].child1))
|
||||
self.assertEqual([child.value for child in b[1].child[1].child2],
|
||||
[12])
|
||||
|
||||
class test_date(ImporterCase):
|
||||
model_name = 'export.date'
|
||||
|
||||
|
|
|
@ -0,0 +1,112 @@
|
|||
#
|
||||
# test cases for fields access, etc.
|
||||
#
|
||||
|
||||
import unittest2
|
||||
import common
|
||||
|
||||
import openerp
|
||||
from openerp.osv import fields
|
||||
|
||||
class TestRelatedField(common.TransactionCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestRelatedField, self).setUp()
|
||||
self.partner = self.registry('res.partner')
|
||||
self.company = self.registry('res.company')
|
||||
|
||||
def test_0_related(self):
|
||||
""" test an usual related field """
|
||||
# add a related field test_related_company_id on res.partner
|
||||
old_columns = self.partner._columns
|
||||
self.partner._columns = dict(old_columns)
|
||||
self.partner._columns.update({
|
||||
'related_company_partner_id': fields.related('company_id', 'partner_id', type='many2one', obj='res.partner'),
|
||||
})
|
||||
|
||||
# find a company with a non-null partner_id
|
||||
ids = self.company.search(self.cr, self.uid, [('partner_id', '!=', False)], limit=1)
|
||||
id = ids[0]
|
||||
|
||||
# find partners that satisfy [('partner_id.company_id', '=', id)]
|
||||
company_ids = self.company.search(self.cr, self.uid, [('partner_id', '=', id)])
|
||||
partner_ids1 = self.partner.search(self.cr, self.uid, [('company_id', 'in', company_ids)])
|
||||
partner_ids2 = self.partner.search(self.cr, self.uid, [('related_company_partner_id', '=', id)])
|
||||
self.assertEqual(partner_ids1, partner_ids2)
|
||||
|
||||
# restore res.partner fields
|
||||
self.partner._columns = old_columns
|
||||
|
||||
def do_test_company_field(self, field):
|
||||
# get a partner with a non-null company_id
|
||||
ids = self.partner.search(self.cr, self.uid, [('company_id', '!=', False)], limit=1)
|
||||
partner = self.partner.browse(self.cr, self.uid, ids[0])
|
||||
|
||||
# check reading related field
|
||||
self.assertEqual(partner[field], partner.company_id)
|
||||
|
||||
# check that search on related field is equivalent to original field
|
||||
ids1 = self.partner.search(self.cr, self.uid, [('company_id', '=', partner.company_id.id)])
|
||||
ids2 = self.partner.search(self.cr, self.uid, [(field, '=', partner.company_id.id)])
|
||||
self.assertEqual(ids1, ids2)
|
||||
|
||||
def test_1_single_related(self):
|
||||
""" test a related field with a single indirection like fields.related('foo') """
|
||||
# add a related field test_related_company_id on res.partner
|
||||
old_columns = self.partner._columns
|
||||
self.partner._columns = dict(old_columns)
|
||||
self.partner._columns.update({
|
||||
'single_related_company_id': fields.related('company_id', type='many2one', obj='res.company'),
|
||||
})
|
||||
|
||||
self.do_test_company_field('single_related_company_id')
|
||||
|
||||
# restore res.partner fields
|
||||
self.partner._columns = old_columns
|
||||
|
||||
def test_2_related_related(self):
|
||||
""" test a related field referring to a related field """
|
||||
# add a related field on a related field on res.partner
|
||||
old_columns = self.partner._columns
|
||||
self.partner._columns = dict(old_columns)
|
||||
self.partner._columns.update({
|
||||
'single_related_company_id': fields.related('company_id', type='many2one', obj='res.company'),
|
||||
'related_related_company_id': fields.related('single_related_company_id', type='many2one', obj='res.company'),
|
||||
})
|
||||
|
||||
self.do_test_company_field('related_related_company_id')
|
||||
|
||||
# restore res.partner fields
|
||||
self.partner._columns = old_columns
|
||||
|
||||
def test_3_read_write(self):
|
||||
""" write on a related field """
|
||||
# add a related field test_related_company_id on res.partner
|
||||
old_columns = self.partner._columns
|
||||
self.partner._columns = dict(old_columns)
|
||||
self.partner._columns.update({
|
||||
'related_company_partner_id': fields.related('company_id', 'partner_id', type='many2one', obj='res.partner'),
|
||||
})
|
||||
|
||||
# find a company with a non-null partner_id
|
||||
company_ids = self.company.search(self.cr, self.uid, [('partner_id', '!=', False)], limit=1)
|
||||
company = self.company.browse(self.cr, self.uid, company_ids[0])
|
||||
|
||||
# find partners that satisfy [('partner_id.company_id', '=', company.id)]
|
||||
partner_ids = self.partner.search(self.cr, self.uid, [('related_company_partner_id', '=', company.id)])
|
||||
partner = self.partner.browse(self.cr, self.uid, partner_ids[0])
|
||||
|
||||
# create a new partner, and assign it to company
|
||||
new_partner_id = self.partner.create(self.cr, self.uid, {'name': 'Foo'})
|
||||
partner.write({'related_company_partner_id': new_partner_id})
|
||||
|
||||
company = self.company.browse(self.cr, self.uid, company_ids[0])
|
||||
self.assertEqual(company.partner_id.id, new_partner_id)
|
||||
|
||||
partner = self.partner.browse(self.cr, self.uid, partner_ids[0])
|
||||
self.assertEqual(partner.related_company_partner_id.id, new_partner_id)
|
||||
|
||||
# restore res.partner fields
|
||||
self.partner._columns = old_columns
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
26
setup.py
26
setup.py
|
@ -74,6 +74,19 @@ def py2exe_options():
|
|||
|
||||
execfile(join(os.path.dirname(__file__), 'openerp', 'release.py'))
|
||||
|
||||
# Notes for OpenERP developer on windows:
|
||||
#
|
||||
# To setup a windows developer evironement install python2.7 then pip and use
|
||||
# "pip install <depencey>" for every dependency listed below.
|
||||
#
|
||||
# Dependecies that requires DLLs are not installable with pip install, for
|
||||
# them we added comments with links where you can find the installers.
|
||||
#
|
||||
# OpenERP on windows also require the pywin32, the binary can be found at
|
||||
# http://pywin32.sf.net
|
||||
#
|
||||
# Both python2.7 32bits and 64bits are known to work.
|
||||
|
||||
setuptools.setup(
|
||||
name = 'openerp',
|
||||
version = version,
|
||||
|
@ -90,29 +103,30 @@ setuptools.setup(
|
|||
dependency_links = ['http://download.gna.org/pychart/'],
|
||||
#include_package_data = True,
|
||||
install_requires = [
|
||||
'pychart',
|
||||
'pychart', # not on pypi, use: pip install http://download.gna.org/pychart/PyChart-1.39.tar.gz
|
||||
'babel',
|
||||
'docutils',
|
||||
'feedparser',
|
||||
'gdata',
|
||||
'lxml < 3',
|
||||
'lxml < 3', # windows binary http://www.lfd.uci.edu/~gohlke/pythonlibs/
|
||||
'mako',
|
||||
'psutil',
|
||||
'PIL', # windows binary http://www.lfd.uci.edu/~gohlke/pythonlibs/
|
||||
'psutil', # windows binary code.google.com/p/psutil/downloads/list
|
||||
'psycopg2',
|
||||
'pydot',
|
||||
'python-dateutil < 2',
|
||||
'python-ldap',
|
||||
'python-ldap', # optional
|
||||
'python-openid',
|
||||
'pytz',
|
||||
'pywebdav',
|
||||
'pyyaml',
|
||||
'reportlab',
|
||||
'reportlab', # windows binary pypi.python.org/pypi/reportlab
|
||||
'simplejson',
|
||||
'vatnumber',
|
||||
'vobject',
|
||||
'werkzeug',
|
||||
'xlwt',
|
||||
'zsi',
|
||||
'zsi', # optional
|
||||
],
|
||||
extras_require = {
|
||||
'SSL' : ['pyopenssl'],
|
||||
|
|
Loading…
Reference in New Issue