[MERGE] from trunk
bzr revid: rco@openerp.com-20110718083252-4phjgbo7fbxoeots
This commit is contained in:
@@ -1,20 +1,21 @@
|
||||
# Translation of OpenERP Server.
|
||||
# This file contains the translation of the following modules:
|
||||
# * base
|
||||
# * base
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: OpenERP Server 5.0.4\n"
|
||||
"Project-Id-Version: openobject-server\n"
|
||||
"Report-Msgid-Bugs-To: support@openerp.com\n"
|
||||
"POT-Creation-Date: 2011-01-11 11:14+0000\n"
|
||||
"PO-Revision-Date: 2010-06-06 04:19+0000\n"
|
||||
"Last-Translator: Vladimír Burian <vladimir.burian@email.cz>\n"
|
||||
"PO-Revision-Date: 2011-07-15 18:58+0000\n"
|
||||
"Last-Translator: Chronos <robie@centrum.cz>\n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2011-07-02 04:55+0000\n"
|
||||
"X-Generator: Launchpad (build 13168)\n"
|
||||
"X-Launchpad-Export-Date: 2011-07-16 04:53+0000\n"
|
||||
"X-Generator: Launchpad (build 13405)\n"
|
||||
"X-Poedit-Language: Czech\n"
|
||||
|
||||
#. module: base
|
||||
#: view:ir.filters:0
|
||||
@@ -585,7 +586,7 @@ msgstr "Srbsko"
|
||||
#. module: base
|
||||
#: selection:ir.translation,type:0
|
||||
msgid "Wizard View"
|
||||
msgstr ""
|
||||
msgstr "Zobrazení průvodce"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.kh
|
||||
@@ -1181,7 +1182,7 @@ msgstr "res.request.link"
|
||||
#. module: base
|
||||
#: field:ir.actions.wizard,name:0
|
||||
msgid "Wizard Info"
|
||||
msgstr ""
|
||||
msgstr "Informace průvodce"
|
||||
|
||||
#. module: base
|
||||
#: view:base.language.export:0
|
||||
@@ -1196,6 +1197,8 @@ msgid ""
|
||||
"Do not display this log if it belongs to the same object the user is working "
|
||||
"on"
|
||||
msgstr ""
|
||||
"Nezobrazovat tento záznam, pokud patří ke stejnému objektu, se kterým "
|
||||
"pracuje uživatel"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.tp
|
||||
@@ -1253,7 +1256,7 @@ msgstr "Maledivy"
|
||||
#. module: base
|
||||
#: help:ir.values,res_id:0
|
||||
msgid "Keep 0 if the action must appear on all resources."
|
||||
msgstr ""
|
||||
msgstr "Nechejte 0 pokud se musí akce objevit u všech zdrojů."
|
||||
|
||||
#. module: base
|
||||
#: model:ir.model,name:base.model_ir_rule
|
||||
@@ -2501,7 +2504,7 @@ msgstr "Kontrola Ean"
|
||||
#. module: base
|
||||
#: field:res.partner,vat:0
|
||||
msgid "VAT"
|
||||
msgstr "DPH"
|
||||
msgstr "DIČ"
|
||||
|
||||
#. module: base
|
||||
#: view:res.lang:0
|
||||
@@ -3588,7 +3591,7 @@ msgstr ""
|
||||
#. module: base
|
||||
#: model:ir.ui.menu,name:base.menu_translation_app
|
||||
msgid "Application Terms"
|
||||
msgstr ""
|
||||
msgstr "Podmínky aplikace"
|
||||
|
||||
#. module: base
|
||||
#: help:res.config.users,context_tz:0
|
||||
@@ -3818,7 +3821,7 @@ msgstr "Vlastník bankovního účtu"
|
||||
#. module: base
|
||||
#: model:ir.actions.act_window,name:base.act_values_form
|
||||
msgid "Client Actions Connections"
|
||||
msgstr ""
|
||||
msgstr "Nastavení akcí klienta"
|
||||
|
||||
#. module: base
|
||||
#: field:ir.attachment,res_name:0
|
||||
@@ -3880,7 +3883,7 @@ msgstr "Měsíc"
|
||||
#. module: base
|
||||
#: model:res.country,name:base.my
|
||||
msgid "Malaysia"
|
||||
msgstr ""
|
||||
msgstr "Malaysie"
|
||||
|
||||
#. module: base
|
||||
#: view:base.language.install:0
|
||||
@@ -4282,7 +4285,7 @@ msgstr "Zděděný pohled"
|
||||
#. module: base
|
||||
#: view:ir.translation:0
|
||||
msgid "Source Term"
|
||||
msgstr ""
|
||||
msgstr "Podmínky zdrojového kódu"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.ui.menu,name:base.menu_main_pm
|
||||
@@ -7262,6 +7265,8 @@ msgid ""
|
||||
"Changing the type of a column is not yet supported. Please drop it and "
|
||||
"create it again!"
|
||||
msgstr ""
|
||||
"Změna typu sloupce ještě není podporována. Prosíme smažte jej a opět "
|
||||
"vytvořte!"
|
||||
|
||||
#. module: base
|
||||
#: field:ir.ui.view_sc,user_id:0
|
||||
@@ -8821,7 +8826,7 @@ msgstr "base.module.configuration"
|
||||
#: field:base.language.export,name:0
|
||||
#: field:ir.attachment,datas_fname:0
|
||||
msgid "Filename"
|
||||
msgstr ""
|
||||
msgstr "Jméno souboru"
|
||||
|
||||
#. module: base
|
||||
#: field:ir.model,access_ids:0
|
||||
@@ -8899,7 +8904,7 @@ msgstr "Bulharština / български език"
|
||||
#. module: base
|
||||
#: model:ir.ui.menu,name:base.menu_aftersale
|
||||
msgid "After-Sale Services"
|
||||
msgstr ""
|
||||
msgstr "Poprodejní služby"
|
||||
|
||||
#. module: base
|
||||
#: view:ir.actions.todo:0
|
||||
|
||||
@@ -7,13 +7,13 @@ msgstr ""
|
||||
"Project-Id-Version: OpenERP Server 5.0.4\n"
|
||||
"Report-Msgid-Bugs-To: support@openerp.com\n"
|
||||
"POT-Creation-Date: 2011-01-11 11:14+0000\n"
|
||||
"PO-Revision-Date: 2011-06-06 17:53+0000\n"
|
||||
"Last-Translator: Hector Rojas (doingIT.cl) <Unknown>\n"
|
||||
"PO-Revision-Date: 2011-07-11 21:37+0000\n"
|
||||
"Last-Translator: doingit.cl <Unknown>\n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2011-07-02 05:03+0000\n"
|
||||
"X-Launchpad-Export-Date: 2011-07-12 04:35+0000\n"
|
||||
"X-Generator: Launchpad (build 13168)\n"
|
||||
|
||||
#. module: base
|
||||
@@ -47,6 +47,8 @@ msgid ""
|
||||
"The second argument of the many2many field %s must be a SQL table !You used "
|
||||
"%s, which is not a valid SQL table name."
|
||||
msgstr ""
|
||||
"¡El segundo argumento del campo many2many %s debe ser una tabla SQL! Has "
|
||||
"utilizado %s, que no es un nombre de tabla SQL válido."
|
||||
|
||||
#. module: base
|
||||
#: view:ir.values:0
|
||||
@@ -278,7 +280,7 @@ msgstr "Nombre de asistente"
|
||||
#: code:addons/orm.py:2160
|
||||
#, python-format
|
||||
msgid "Invalid group_by"
|
||||
msgstr ""
|
||||
msgstr "group_by Invalido"
|
||||
|
||||
#. module: base
|
||||
#: field:res.partner,credit_limit:0
|
||||
|
||||
@@ -62,7 +62,7 @@ class ir_config_parameter(osv.osv):
|
||||
if not ids:
|
||||
self.set_param(cr, 1, key, func())
|
||||
|
||||
def get_param(self, cr, uid, key, context=None):
|
||||
def get_param(self, cr, uid, key, default=False, context=None):
|
||||
""" Get the value of a parameter.
|
||||
|
||||
@param key: The key of the parameter.
|
||||
@@ -72,7 +72,7 @@ class ir_config_parameter(osv.osv):
|
||||
"""
|
||||
ids = self.search(cr, uid, [('key','=',key)], context=context)
|
||||
if not ids:
|
||||
return False
|
||||
return default
|
||||
param = self.browse(cr, uid, ids[0], context=context)
|
||||
value = param.value
|
||||
return value
|
||||
|
||||
@@ -131,13 +131,6 @@ class res_currency(osv.osv):
|
||||
else:
|
||||
return (from_amount * rate)
|
||||
|
||||
def name_search(self, cr, uid, name, args=[], operator='ilike', context={}, limit=100):
|
||||
args2 = args[:]
|
||||
if name:
|
||||
args += [('name', operator, name)]
|
||||
ids = self.search(cr, uid, args, limit=limit)
|
||||
res = self.name_get(cr, uid, ids, context)
|
||||
return res
|
||||
res_currency()
|
||||
|
||||
class res_currency_rate(osv.osv):
|
||||
|
||||
@@ -354,6 +354,7 @@
|
||||
<rng:element name="separator">
|
||||
<rng:ref name="overload"/>
|
||||
<rng:ref name="access_rights"/>
|
||||
<rng:optional><rng:attribute name="name"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="colspan"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="rowspan"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="string"/></rng:optional>
|
||||
|
||||
+62
-57
@@ -88,14 +88,14 @@ def load_module_graph(cr, graph, status=None, perform_checks=True, skip_modules=
|
||||
if new_query:
|
||||
cr.execute(new_query)
|
||||
|
||||
def load_init_xml(cr, m, idref, mode):
|
||||
_load_data(cr, m, idref, mode, 'init_xml')
|
||||
def load_init_xml(cr, module_name, idref, mode):
|
||||
_load_data(cr, module_name, idref, mode, 'init_xml')
|
||||
|
||||
def load_update_xml(cr, m, idref, mode):
|
||||
_load_data(cr, m, idref, mode, 'update_xml')
|
||||
def load_update_xml(cr, module_name, idref, mode):
|
||||
_load_data(cr, module_name, idref, mode, 'update_xml')
|
||||
|
||||
def load_demo_xml(cr, m, idref, mode):
|
||||
_load_data(cr, m, idref, mode, 'demo_xml')
|
||||
def load_demo_xml(cr, module_name, idref, mode):
|
||||
_load_data(cr, module_name, idref, mode, 'demo_xml')
|
||||
|
||||
def load_data(cr, module_name, idref, mode):
|
||||
_load_data(cr, module_name, idref, mode, 'data')
|
||||
@@ -152,38 +152,38 @@ def load_module_graph(cr, graph, status=None, perform_checks=True, skip_modules=
|
||||
status = {}
|
||||
|
||||
processed_modules = []
|
||||
loaded_modules = []
|
||||
statusi = 0
|
||||
pool = pooler.get_pool(cr.dbname)
|
||||
migrations = openerp.modules.migration.MigrationManager(cr, graph)
|
||||
logger.notifyChannel('init', netsvc.LOG_DEBUG, 'loading %d packages..' % len(graph))
|
||||
|
||||
# register, instanciate and initialize models for each modules
|
||||
# register, instantiate and initialize models for each modules
|
||||
for package in graph:
|
||||
if skip_modules and package.name in skip_modules:
|
||||
module_name = package.name
|
||||
module_id = package.id
|
||||
|
||||
if skip_modules and module_name in skip_modules:
|
||||
continue
|
||||
|
||||
logger.notifyChannel('init', netsvc.LOG_INFO, 'module %s: loading objects' % package.name)
|
||||
migrations.migrate_module(package, 'pre')
|
||||
register_module_classes(package.name)
|
||||
models = pool.instanciate(package.name, cr)
|
||||
loaded_modules.append(package.name)
|
||||
if hasattr(package, 'init') or hasattr(package, 'update') or package.state in ('to install', 'to upgrade'):
|
||||
init_module_models(cr, package.name, models)
|
||||
cr.commit()
|
||||
|
||||
# load data for each modules
|
||||
modobj = pool.get('ir.module.module')
|
||||
for package in graph:
|
||||
status['progress'] = (float(statusi)+0.1) / len(graph)
|
||||
m = package.name
|
||||
mid = package.id
|
||||
status['progress'] = float(statusi) / len(graph)
|
||||
|
||||
if skip_modules and m in skip_modules:
|
||||
continue
|
||||
# Can't put this line out of the loop: ir.module.module will be
|
||||
# registered by init_module_models() above.
|
||||
modobj = pool.get('ir.module.module')
|
||||
|
||||
if perform_checks:
|
||||
modobj.check(cr, 1, [mid])
|
||||
modobj.check(cr, 1, [module_id])
|
||||
|
||||
idref = {}
|
||||
status['progress'] = (float(statusi)+0.4) / len(graph)
|
||||
|
||||
mode = 'update'
|
||||
if hasattr(package, 'init') or package.state == 'to install':
|
||||
@@ -192,21 +192,21 @@ def load_module_graph(cr, graph, status=None, perform_checks=True, skip_modules=
|
||||
if hasattr(package, 'init') or hasattr(package, 'update') or package.state in ('to install', 'to upgrade'):
|
||||
if package.state=='to upgrade':
|
||||
# upgrading the module information
|
||||
modobj.write(cr, 1, [mid], modobj.get_values_from_terp(package.data))
|
||||
load_init_xml(cr, m, idref, mode)
|
||||
load_update_xml(cr, m, idref, mode)
|
||||
load_data(cr, m, idref, mode)
|
||||
modobj.write(cr, 1, [module_id], modobj.get_values_from_terp(package.data))
|
||||
load_init_xml(cr, module_name, idref, mode)
|
||||
load_update_xml(cr, module_name, idref, mode)
|
||||
load_data(cr, module_name, idref, mode)
|
||||
if hasattr(package, 'demo') or (package.dbdemo and package.state != 'installed'):
|
||||
status['progress'] = (float(statusi)+0.75) / len(graph)
|
||||
load_demo_xml(cr, m, idref, mode)
|
||||
load_demo(cr, m, idref, mode)
|
||||
cr.execute('update ir_module_module set demo=%s where id=%s', (True, mid))
|
||||
load_demo_xml(cr, module_name, idref, mode)
|
||||
load_demo(cr, module_name, idref, mode)
|
||||
cr.execute('update ir_module_module set demo=%s where id=%s', (True, module_id))
|
||||
|
||||
# launch tests only in demo mode, as most tests will depend
|
||||
# on demo data. Other tests can be added into the regular
|
||||
# 'data' section, but should probably not alter the data,
|
||||
# as there is no rollback.
|
||||
load_test(cr, m, idref, mode)
|
||||
load_test(cr, module_name, idref, mode)
|
||||
|
||||
processed_modules.append(package.name)
|
||||
|
||||
@@ -214,22 +214,21 @@ def load_module_graph(cr, graph, status=None, perform_checks=True, skip_modules=
|
||||
|
||||
ver = release.major_version + '.' + package.data['version']
|
||||
# Set new modules and dependencies
|
||||
modobj.write(cr, 1, [mid], {'state': 'installed', 'latest_version': ver})
|
||||
cr.commit()
|
||||
modobj.write(cr, 1, [module_id], {'state': 'installed', 'latest_version': ver})
|
||||
# Update translations for all installed languages
|
||||
modobj.update_translations(cr, 1, [mid], None)
|
||||
cr.commit()
|
||||
modobj.update_translations(cr, 1, [module_id], None)
|
||||
|
||||
package.state = 'installed'
|
||||
for kind in ('init', 'demo', 'update'):
|
||||
if hasattr(package, kind):
|
||||
delattr(package, kind)
|
||||
|
||||
cr.commit()
|
||||
statusi += 1
|
||||
|
||||
cr.commit()
|
||||
|
||||
return processed_modules
|
||||
return loaded_modules, processed_modules
|
||||
|
||||
def _check_module_names(cr, module_names):
|
||||
mod_names = set(module_names)
|
||||
@@ -245,11 +244,26 @@ def _check_module_names(cr, module_names):
|
||||
incorrect_names = mod_names.difference([x['name'] for x in cr.dictfetchall()])
|
||||
logging.getLogger('init').warning('invalid module names, ignored: %s', ", ".join(incorrect_names))
|
||||
|
||||
def load_marked_modules(cr, graph, states, force, progressdict, report, loaded_modules):
|
||||
"""Loads modules marked with ``states``, adding them to ``graph`` and
|
||||
``loaded_modules`` and returns a list of installed/upgraded modules."""
|
||||
processed_modules = []
|
||||
while True:
|
||||
cr.execute("SELECT name from ir_module_module WHERE state IN %s" ,(tuple(states),))
|
||||
module_list = [name for (name,) in cr.fetchall() if name not in graph]
|
||||
new_modules_in_graph = graph.add_modules(cr, module_list, force)
|
||||
logger.notifyChannel('init', netsvc.LOG_DEBUG, 'Updating graph with %d more modules' % (len(module_list)))
|
||||
loaded, processed = load_module_graph(cr, graph, progressdict, report=report, skip_modules=loaded_modules)
|
||||
processed_modules.extend(processed)
|
||||
loaded_modules.extend(loaded)
|
||||
if not processed: break
|
||||
return processed_modules
|
||||
|
||||
|
||||
def load_modules(db, force_demo=False, status=None, update_module=False):
|
||||
# TODO status['progress'] reporting is broken: used twice (and reset each
|
||||
# time to zero) in load_module_graph, not fine-grained enough.
|
||||
# It should be a method exposed by the pool.
|
||||
|
||||
initialize_sys_path()
|
||||
|
||||
open_openerp_namespace()
|
||||
@@ -271,10 +285,9 @@ def load_modules(db, force_demo=False, status=None, update_module=False):
|
||||
# This is a brand new pool, just created in pooler.get_db_and_pool()
|
||||
pool = pooler.get_pool(cr.dbname)
|
||||
|
||||
processed_modules = []
|
||||
processed_modules = [] # for cleanup step after install
|
||||
loaded_modules = [] # to avoid double loading
|
||||
report = tools.assertion_report()
|
||||
# NOTE: Try to also load the modules that have been marked as uninstallable previously...
|
||||
STATES_TO_LOAD = ['installed', 'to upgrade', 'uninstallable']
|
||||
if 'base' in tools.config['update'] or 'all' in tools.config['update']:
|
||||
cr.execute("update ir_module_module set state=%s where name=%s and state=%s", ('to upgrade', 'base', 'installed'))
|
||||
|
||||
@@ -284,7 +297,8 @@ def load_modules(db, force_demo=False, status=None, update_module=False):
|
||||
if not graph:
|
||||
logger.notifyChannel('init', netsvc.LOG_CRITICAL, 'module base cannot be loaded! (hint: verify addons-path)')
|
||||
raise osv.osv.except_osv(_('Could not load base module'), _('module base cannot be loaded! (hint: verify addons-path)'))
|
||||
processed_modules.extend(load_module_graph(cr, graph, status, perform_checks=(not update_module), report=report))
|
||||
loaded, processed = load_module_graph(cr, graph, status, perform_checks=(not update_module), report=report)
|
||||
processed_modules.extend(processed)
|
||||
|
||||
if tools.config['load_language']:
|
||||
for lang in tools.config['load_language'].split(','):
|
||||
@@ -313,28 +327,19 @@ def load_modules(db, force_demo=False, status=None, update_module=False):
|
||||
|
||||
cr.execute("update ir_module_module set state=%s where name=%s", ('installed', 'base'))
|
||||
|
||||
STATES_TO_LOAD += ['to install']
|
||||
|
||||
|
||||
# STEP 3: Load marked modules (skipping base which was done in STEP 1)
|
||||
loop_guardrail = 0
|
||||
while True:
|
||||
loop_guardrail += 1
|
||||
if loop_guardrail > 100:
|
||||
raise ValueError('Possible recursive module tree detected, aborting.')
|
||||
cr.execute("SELECT name from ir_module_module WHERE state IN %s" ,(tuple(STATES_TO_LOAD),))
|
||||
|
||||
module_list = [name for (name,) in cr.fetchall() if name not in graph]
|
||||
if not module_list:
|
||||
break
|
||||
|
||||
new_modules_in_graph = graph.add_modules(cr, module_list, force)
|
||||
if new_modules_in_graph == 0:
|
||||
# nothing to load
|
||||
break
|
||||
|
||||
logger.notifyChannel('init', netsvc.LOG_DEBUG, 'Updating graph with %d more modules' % (len(module_list)))
|
||||
processed_modules.extend(load_module_graph(cr, graph, status, report=report, skip_modules=processed_modules))
|
||||
# IMPORTANT: this is done in two parts, first loading all installed or
|
||||
# partially installed modules (i.e. installed/to upgrade), to
|
||||
# offer a consistent system to the second part: installing
|
||||
# newly selected modules.
|
||||
states_to_load = ['installed', 'to upgrade']
|
||||
processed = load_marked_modules(cr, graph, states_to_load, force, status, report, loaded_modules)
|
||||
processed_modules.extend(processed)
|
||||
if update_module:
|
||||
states_to_load = ['to install']
|
||||
processed = load_marked_modules(cr, graph, states_to_load, force, status, report, loaded_modules)
|
||||
processed_modules.extend(processed)
|
||||
|
||||
# load custom models
|
||||
cr.execute('select model from ir_model where state=%s', ('manual',))
|
||||
|
||||
+47
-51
@@ -1212,34 +1212,30 @@ class serialized(_column):
|
||||
self._symbol_get = self._deserialize_func
|
||||
super(serialized, self).__init__(string=string, **args)
|
||||
|
||||
|
||||
# TODO: review completly this class for speed improvement
|
||||
class property(function):
|
||||
|
||||
def _get_default(self, obj, cr, uid, prop_name, context=None):
|
||||
return self._get_defaults(obj, cr, uid, [prop_name], context=None)[0][prop_name]
|
||||
return self._get_defaults(obj, cr, uid, [prop_name], context=None)[prop_name]
|
||||
|
||||
def _get_defaults(self, obj, cr, uid, prop_name, context=None):
|
||||
def _get_defaults(self, obj, cr, uid, prop_names, context=None):
|
||||
"""Get the default values for ``prop_names´´ property fields (result of ir.property.get() function for res_id = False).
|
||||
|
||||
:param list of string prop_names: list of name of property fields for those we want the default value
|
||||
:return: map of property field names to their default value
|
||||
:rtype: dict
|
||||
"""
|
||||
prop = obj.pool.get('ir.property')
|
||||
domain = [('fields_id.model', '=', obj._name), ('fields_id.name','in',prop_name), ('res_id','=',False)]
|
||||
ids = prop.search(cr, uid, domain, context=context)
|
||||
replaces = {}
|
||||
default_value = {}.fromkeys(prop_name, False)
|
||||
for prop_rec in prop.browse(cr, uid, ids, context=context):
|
||||
if default_value.get(prop_rec.fields_id.name, False):
|
||||
continue
|
||||
value = prop.get_by_record(cr, uid, prop_rec, context=context) or False
|
||||
default_value[prop_rec.fields_id.name] = value
|
||||
if value and (prop_rec.type == 'many2one'):
|
||||
replaces.setdefault(value._name, {})
|
||||
replaces[value._name][value.id] = True
|
||||
return default_value, replaces
|
||||
res = {}
|
||||
for prop_name in prop_names:
|
||||
res[prop_name] = prop.get(cr, uid, prop_name, obj._name, context=context)
|
||||
return res
|
||||
|
||||
def _get_by_id(self, obj, cr, uid, prop_name, ids, context=None):
|
||||
prop = obj.pool.get('ir.property')
|
||||
vids = [obj._name + ',' + str(oid) for oid in ids]
|
||||
|
||||
domain = [('fields_id.model', '=', obj._name), ('fields_id.name','in',prop_name)]
|
||||
domain = [('fields_id.model', '=', obj._name), ('fields_id.name', 'in', prop_name)]
|
||||
#domain = prop._get_domain(cr, uid, prop_name, obj._name, context)
|
||||
if vids:
|
||||
domain = [('res_id', 'in', vids)] + domain
|
||||
@@ -1274,48 +1270,48 @@ class property(function):
|
||||
}, context=context)
|
||||
return False
|
||||
|
||||
def _fnct_read(self, obj, cr, uid, ids, prop_names, obj_dest, context=None):
|
||||
prop = obj.pool.get('ir.property')
|
||||
# get the default values (for res_id = False) for the property fields
|
||||
default_val = self._get_defaults(obj, cr, uid, prop_names, context)
|
||||
|
||||
def _fnct_read(self, obj, cr, uid, ids, prop_name, obj_dest, context=None):
|
||||
properties = obj.pool.get('ir.property')
|
||||
domain = [('fields_id.model', '=', obj._name), ('fields_id.name','in',prop_name)]
|
||||
domain += [('res_id','in', [obj._name + ',' + str(oid) for oid in ids])]
|
||||
nids = properties.search(cr, uid, domain, context=context)
|
||||
default_val,replaces = self._get_defaults(obj, cr, uid, prop_name, context)
|
||||
|
||||
# build the dictionary that will be returned
|
||||
res = {}
|
||||
for id in ids:
|
||||
res[id] = default_val.copy()
|
||||
|
||||
brs = properties.browse(cr, uid, nids, context=context)
|
||||
for prop in brs:
|
||||
value = properties.get_by_record(cr, uid, prop, context=context)
|
||||
res[prop.res_id.id][prop.fields_id.name] = value or False
|
||||
if value and (prop.type == 'many2one'):
|
||||
# check existence 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.
|
||||
record_exists = obj.pool.get(value._name).exists(cr, 1, value.id)
|
||||
if record_exists:
|
||||
replaces.setdefault(value._name, {})
|
||||
replaces[value._name][value.id] = True
|
||||
else:
|
||||
res[prop.res_id.id][prop.fields_id.name] = False
|
||||
|
||||
for rep in replaces:
|
||||
# search+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.
|
||||
nids = obj.pool.get(rep).search(cr, 1, [('id','in',replaces[rep].keys())], context=context)
|
||||
replaces[rep] = dict(obj.pool.get(rep).name_get(cr, 1, nids, context=context))
|
||||
|
||||
for prop in prop_name:
|
||||
for prop_name in prop_names:
|
||||
property_field = obj._all_columns.get(prop_name).column
|
||||
property_destination_obj = property_field._obj if property_field._type == 'many2one' else False
|
||||
# If the property field is a m2o field, we will append the id of the value to name_get_ids
|
||||
# in order to make a name_get in batch for all the ids needed.
|
||||
name_get_ids = {}
|
||||
for id in ids:
|
||||
if res[id][prop] and hasattr(res[id][prop], '_name'):
|
||||
res[id][prop] = (res[id][prop].id , replaces[res[id][prop]._name].get(res[id][prop].id, False))
|
||||
|
||||
# get the result of ir.property.get() for this res_id and save it in res if it's existing
|
||||
obj_reference = obj._name + ',' + str(id)
|
||||
value = prop.get(cr, uid, prop_name, obj._name, res_id=obj_reference, context=context)
|
||||
if value:
|
||||
res[id][prop_name] = value
|
||||
# Check existence 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) in order to avoid
|
||||
# pointing on an unexisting record.
|
||||
if property_destination_obj:
|
||||
if res[id][prop_name] and obj.pool.get(property_destination_obj).exists(cr, 1, res[id][prop_name].id):
|
||||
name_get_ids[id] = res[id][prop_name].id
|
||||
else:
|
||||
res[id][prop_name] = False
|
||||
if property_destination_obj:
|
||||
# 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.)
|
||||
name_get_values = dict(obj.pool.get(property_destination_obj).name_get(cr, 1, name_get_ids.values(), context=context))
|
||||
# the property field is a m2o, we need to return a tuple with (id, name)
|
||||
for k, v in name_get_ids.iteritems():
|
||||
if res[k][prop_name]:
|
||||
res[k][prop_name] = (v , name_get_values.get(v))
|
||||
return res
|
||||
|
||||
|
||||
def _field_get(self, cr, uid, model_name, prop):
|
||||
if not self.field_id.get(cr.dbname):
|
||||
cr.execute('SELECT id \
|
||||
|
||||
+19
-6
@@ -77,9 +77,21 @@ module_class_list = {}
|
||||
ROOT_USER_ID = 1
|
||||
|
||||
def transfer_field_to_modifiers(field, modifiers):
|
||||
for a in ('invisible', 'readonly', 'required'):
|
||||
if field.get(a):
|
||||
modifiers[a] = bool(field.get(a))
|
||||
default_values = {}
|
||||
state_exceptions = {}
|
||||
for attr in ('invisible', 'readonly', 'required'):
|
||||
state_exceptions[attr] = []
|
||||
default_values[attr] = bool(field.get(attr))
|
||||
for state, modifs in (field.get("states",{})).items():
|
||||
for modif in modifs:
|
||||
if default_values[modif[0]] != modif[1]:
|
||||
state_exceptions[modif[0]].append(state)
|
||||
|
||||
for attr, default_value in default_values.items():
|
||||
if state_exceptions[attr]:
|
||||
modifiers[attr] = [("state", "not in" if default_value else "in", state_exceptions[attr])]
|
||||
else:
|
||||
modifiers[attr] = default_value
|
||||
|
||||
|
||||
# Don't deal with groups, it is done by check_group().
|
||||
@@ -2484,9 +2496,10 @@ class orm(orm_template):
|
||||
:param int offset: optional number of records to skip
|
||||
:param int limit: optional max number of records to return
|
||||
:param dict context: context arguments, like lang, time zone
|
||||
:param order: optional ``order by`` specification, for overriding the natural
|
||||
sort ordering of the groups, see also :py:meth:`~osv.osv.osv.search`
|
||||
(supported only for many2one fields currently)
|
||||
:param list orderby: optional ``order by`` specification, for
|
||||
overriding the natural sort ordering of the
|
||||
groups, see also :py:meth:`~osv.osv.osv.search`
|
||||
(supported only for many2one fields currently)
|
||||
:return: list of dictionaries(one dictionary for each record) containing:
|
||||
|
||||
* the values of fields grouped by the fields in ``groupby`` argument
|
||||
|
||||
+85
-16
@@ -37,7 +37,8 @@ __all__ = ['db_connect', 'close_db']
|
||||
|
||||
from threading import currentThread
|
||||
import logging
|
||||
from psycopg2.extensions import ISOLATION_LEVEL_AUTOCOMMIT, ISOLATION_LEVEL_READ_COMMITTED, ISOLATION_LEVEL_SERIALIZABLE
|
||||
from psycopg2.extensions import ISOLATION_LEVEL_AUTOCOMMIT, ISOLATION_LEVEL_READ_COMMITTED, ISOLATION_LEVEL_SERIALIZABLE,\
|
||||
ISOLATION_LEVEL_REPEATABLE_READ
|
||||
from psycopg2.psycopg1 import cursor as psycopg1cursor
|
||||
from psycopg2.pool import PoolError
|
||||
|
||||
@@ -79,12 +80,64 @@ re_into = re.compile('.* into "?([a-zA-Z_0-9]+)"? .*$');
|
||||
sql_counter = 0
|
||||
|
||||
class Cursor(object):
|
||||
""" Cursor is an open transaction to Postgres, utilizing a TCP connection
|
||||
|
||||
A lightweight wrapper around psycopg2's `psycopg1cursor` objects
|
||||
|
||||
This is the object behind the `cr` variable used all over the OpenERP
|
||||
code.
|
||||
"""Represents an open transaction to the PostgreSQL DB backend,
|
||||
acting as a lightweight wrapper around psycopg2's
|
||||
``psycopg1cursor`` objects.
|
||||
|
||||
``Cursor`` is the object behind the ``cr`` variable used all
|
||||
over the OpenERP code.
|
||||
|
||||
.. rubric:: Transaction Isolation
|
||||
|
||||
One very important property of database transactions is the
|
||||
level of isolation between concurrent transactions.
|
||||
The SQL standard defines four levels of transaction isolation,
|
||||
ranging from the most strict *Serializable* level, to the least
|
||||
strict *Read Uncommitted* level. These levels are defined in
|
||||
terms of the phenomena that must not occur between concurrent
|
||||
transactions, such as *dirty read*, etc.
|
||||
In the context of a generic business data management software
|
||||
such as OpenERP, we need the best guarantees that no data
|
||||
corruption can ever be cause by simply running multiple
|
||||
transactions in parallel. Therefore, the preferred level would
|
||||
be the *serializable* level, which ensures that a set of
|
||||
transactions is guaranteed to produce the same effect as
|
||||
running them one at a time in some order.
|
||||
|
||||
However, most database management systems implement a limited
|
||||
serializable isolation in the form of
|
||||
`snapshot isolation <http://en.wikipedia.org/wiki/Snapshot_isolation>`_,
|
||||
providing most of the same advantages as True Serializability,
|
||||
with a fraction of the performance cost.
|
||||
With PostgreSQL up to version 9.0, this snapshot isolation was
|
||||
the implementation of both the ``REPEATABLE READ`` and
|
||||
``SERIALIZABLE`` levels of the SQL standard.
|
||||
As of PostgreSQL 9.1, the previous snapshot isolation implementation
|
||||
was kept for ``REPEATABLE READ``, while a new ``SERIALIZABLE``
|
||||
level was introduced, providing some additional heuristics to
|
||||
detect a concurrent update by parallel transactions, and forcing
|
||||
one of them to rollback.
|
||||
|
||||
OpenERP implements its own level of locking protection
|
||||
for transactions that are highly likely to provoke concurrent
|
||||
updates, such as stock reservations or document sequences updates.
|
||||
Therefore we mostly care about the properties of snapshot isolation,
|
||||
but we don't really need additional heuristics to trigger transaction
|
||||
rollbacks, as we are taking care of triggering instant rollbacks
|
||||
ourselves when it matters (and we can save the additional performance
|
||||
hit of these heuristics).
|
||||
|
||||
As a result of the above, we have selected ``REPEATABLE READ`` as
|
||||
the default transaction isolation level for OpenERP cursors, as
|
||||
it will be mapped to the desired ``snapshot isolation`` level for
|
||||
all supported PostgreSQL version (8.3 - 9.x).
|
||||
|
||||
Note: up to psycopg2 v.2.4.2, psycopg2 itself remapped the repeatable
|
||||
read level to serializable before sending it to the database, so it would
|
||||
actually select the new serializable mode on PostgreSQL 9.1. Make
|
||||
sure you use psycopg2 v2.4.2 or newer if you use PostgreSQL 9.1 and
|
||||
the performance hit is a concern for you.
|
||||
|
||||
"""
|
||||
IN_MAX = 1000 # decent limit on size of IN queries - guideline = Oracle limit
|
||||
__logger = None
|
||||
@@ -100,7 +153,7 @@ class Cursor(object):
|
||||
return f(self, *args, **kwargs)
|
||||
return wrapper
|
||||
|
||||
def __init__(self, pool, dbname, serialized=False):
|
||||
def __init__(self, pool, dbname, serialized=True):
|
||||
if self.__class__.__logger is None:
|
||||
self.__class__.__logger = logging.getLogger('db.cursor')
|
||||
self.sql_from_log = {}
|
||||
@@ -115,7 +168,11 @@ class Cursor(object):
|
||||
# is raised by any of the following initialisations
|
||||
self._pool = pool
|
||||
self.dbname = dbname
|
||||
|
||||
# Whether to enable snapshot isolation level for this cursor.
|
||||
# see also the docstring of Cursor.
|
||||
self._serialized = serialized
|
||||
|
||||
self._cnx = pool.borrow(dsn(dbname))
|
||||
self._obj = self._cnx.cursor(cursor_factory=psycopg1cursor)
|
||||
self.__closed = False # real initialisation value
|
||||
@@ -227,9 +284,6 @@ class Cursor(object):
|
||||
self.__closer = frame_codeinfo(currentframe(),3)
|
||||
self.print_log()
|
||||
|
||||
if not self._serialized:
|
||||
self.rollback() # Ensure we close the current transaction.
|
||||
|
||||
self._obj.close()
|
||||
|
||||
# This force the cursor to be freed, and thus, available again. It is
|
||||
@@ -248,8 +302,23 @@ class Cursor(object):
|
||||
|
||||
@check
|
||||
def autocommit(self, on):
|
||||
offlevel = [ISOLATION_LEVEL_READ_COMMITTED, ISOLATION_LEVEL_SERIALIZABLE][bool(self._serialized)]
|
||||
self._cnx.set_isolation_level([offlevel, ISOLATION_LEVEL_AUTOCOMMIT][bool(on)])
|
||||
if on:
|
||||
isolation_level = ISOLATION_LEVEL_AUTOCOMMIT
|
||||
else:
|
||||
# If a serializable cursor was requested, we
|
||||
# use the appropriate PotsgreSQL isolation level
|
||||
# that maps to snaphsot isolation.
|
||||
# For all supported PostgreSQL versions (8.3-9.x),
|
||||
# this is currently the ISOLATION_REPEATABLE_READ.
|
||||
# See also the docstring of this class.
|
||||
# NOTE: up to psycopg 2.4.2, repeatable read
|
||||
# is remapped to serializable before being
|
||||
# sent to the database, so it is in fact
|
||||
# unavailable for use with pg 9.1.
|
||||
isolation_level = ISOLATION_LEVEL_REPEATABLE_READ \
|
||||
if self._serialized \
|
||||
else ISOLATION_LEVEL_READ_COMMITTED
|
||||
self._cnx.set_isolation_level(isolation_level)
|
||||
|
||||
@check
|
||||
def commit(self):
|
||||
@@ -383,13 +452,13 @@ class Connection(object):
|
||||
self.dbname = dbname
|
||||
self._pool = pool
|
||||
|
||||
def cursor(self, serialized=False):
|
||||
def cursor(self, serialized=True):
|
||||
cursor_type = serialized and 'serialized ' or ''
|
||||
self.__logger.log(logging.DEBUG_SQL, 'create %scursor to %r', cursor_type, self.dbname)
|
||||
return Cursor(self._pool, self.dbname, serialized=serialized)
|
||||
|
||||
def serialized_cursor(self):
|
||||
return self.cursor(True)
|
||||
# serialized_cursor is deprecated - cursors are serialized by default
|
||||
serialized_cursor = cursor
|
||||
|
||||
def __nonzero__(self):
|
||||
"""Check if connection is possible"""
|
||||
|
||||
@@ -1387,6 +1387,21 @@ def upload_data(email, data, type='SURVEY'):
|
||||
a.start()
|
||||
return True
|
||||
|
||||
def get_and_group_by_field(cr, uid, obj, ids, field, context=None):
|
||||
""" Read the values of ``field´´ for the given ``ids´´ and group ids by value.
|
||||
|
||||
:param string field: name of the field we want to read and group by
|
||||
:return: mapping of field values to the list of ids that have it
|
||||
:rtype: dict
|
||||
"""
|
||||
res = {}
|
||||
for record in obj.read(cr, uid, ids, [field], context=context):
|
||||
key = record[field]
|
||||
res.setdefault(key[0] if isinstance(key, tuple) else key, []).append(record['id'])
|
||||
return res
|
||||
|
||||
def get_and_group_by_company(cr, uid, obj, ids, context=None):
|
||||
return get_and_group_by_field(cr, uid, obj, ids, field='company_id', context=context)
|
||||
|
||||
# port of python 2.6's attrgetter with support for dotted notation
|
||||
def resolve_attr(obj, attr):
|
||||
|
||||
Reference in New Issue
Block a user