Merge branch 'official-trunk' into deb-scripts
bzr revid: p_christ@hol.gr-20110117094021-rbjm55dip17tnuy4
This commit is contained in:
commit
3aed57ec23
|
@ -18,3 +18,6 @@ bin/python2.6
|
|||
build/
|
||||
bin/yolk
|
||||
bin/pil*.py
|
||||
.project
|
||||
.pydevproject
|
||||
.settings
|
||||
|
|
|
@ -1,3 +1,11 @@
|
|||
OpenERP v6.0 is published under the GNU AFFERO GENERAL PUBLIC LICENSE,
|
||||
Version 3 (AGPLv3), as included below. Some external libraries and
|
||||
contributions bundled with OpenERP may be published under other
|
||||
AGPLv3-compatible licenses. For these, please refer to the relevant
|
||||
source files and/or license files, in the source code tree.
|
||||
|
||||
**************************************************************************
|
||||
|
||||
GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
Version 3, 19 November 2007
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
|
||||
# Copyright (C) 2010 OpenERP s.a. (<http://openerp.com>).
|
||||
# Copyright (C) 2010-2011 OpenERP s.a. (<http://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
|
||||
|
@ -30,7 +30,7 @@ import tools
|
|||
import tools.osutil
|
||||
from tools.safe_eval import safe_eval as eval
|
||||
import pooler
|
||||
|
||||
from tools.translate import _
|
||||
|
||||
import netsvc
|
||||
|
||||
|
@ -47,8 +47,8 @@ import logging
|
|||
|
||||
logger = netsvc.Logger()
|
||||
|
||||
_ad = os.path.abspath(opj(tools.config['root_path'], 'addons')) # default addons path (base)
|
||||
ad_paths= map(lambda m: os.path.abspath(m.strip()),tools.config['addons_path'].split(','))
|
||||
_ad = os.path.abspath(opj(tools.ustr(tools.config['root_path']), u'addons')) # default addons path (base)
|
||||
ad_paths= map(lambda m: os.path.abspath(tools.ustr(m.strip())), tools.config['addons_path'].split(','))
|
||||
|
||||
sys.path.insert(1, _ad)
|
||||
|
||||
|
@ -225,6 +225,10 @@ def zip_directory(directory, b64enc=True, src=True):
|
|||
|
||||
archname = StringIO()
|
||||
archive = PyZipFile(archname, "w", ZIP_DEFLATED)
|
||||
|
||||
# for Python 2.5, ZipFile.write() still expects 8-bit strings (2.6 converts to utf-8)
|
||||
directory = tools.ustr(directory).encode('utf-8')
|
||||
|
||||
archive.writepy(directory)
|
||||
_zippy(archive, directory, src=src)
|
||||
archive.close()
|
||||
|
@ -270,19 +274,18 @@ def get_module_resource(module, *args):
|
|||
@return: absolute path to the resource
|
||||
"""
|
||||
a = get_module_path(module)
|
||||
res = a and opj(a, *args) or False
|
||||
if not a: return False
|
||||
resource_path = opj(a, *args)
|
||||
if zipfile.is_zipfile( a +'.zip') :
|
||||
zip = zipfile.ZipFile( a + ".zip")
|
||||
files = ['/'.join(f.split('/')[1:]) for f in zip.namelist()]
|
||||
res = '/'.join(args)
|
||||
if res in files:
|
||||
return opj(a, res)
|
||||
elif os.path.isfile(res):
|
||||
return res
|
||||
resource_path = '/'.join(args)
|
||||
if resource_path in files:
|
||||
return opj(a, resource_path)
|
||||
elif os.path.exists(resource_path):
|
||||
return resource_path
|
||||
return False
|
||||
|
||||
|
||||
|
||||
def get_modules():
|
||||
"""Returns the list of module names
|
||||
"""
|
||||
|
@ -311,7 +314,11 @@ def load_information_from_description_file(module):
|
|||
for filename in ['__openerp__.py', '__terp__.py']:
|
||||
description_file = get_module_resource(module, filename)
|
||||
if description_file :
|
||||
return eval(tools.file_open(description_file).read())
|
||||
desc_f = tools.file_open(description_file)
|
||||
try:
|
||||
return eval(desc_f.read())
|
||||
finally:
|
||||
desc_f.close()
|
||||
|
||||
#TODO: refactor the logger in this file to follow the logging guidelines
|
||||
# for 6.0
|
||||
|
@ -350,11 +357,14 @@ def upgrade_graph(graph, cr, module_list, force=None):
|
|||
continue
|
||||
|
||||
if os.path.isfile(terp_file) or zipfile.is_zipfile(mod_path+'.zip'):
|
||||
terp_f = tools.file_open(terp_file)
|
||||
try:
|
||||
info = eval(tools.file_open(terp_file).read())
|
||||
except:
|
||||
info = eval(terp_f.read())
|
||||
except Exception:
|
||||
logger.notifyChannel('init', netsvc.LOG_ERROR, 'module %s: eval file %s' % (module, terp_file))
|
||||
raise
|
||||
finally:
|
||||
terp_f.close()
|
||||
if info.get('installable', True):
|
||||
packages.append((module, info.get('depends', []), info))
|
||||
else:
|
||||
|
@ -598,8 +608,15 @@ class MigrationManager(object):
|
|||
|
||||
log = logging.getLogger('init')
|
||||
|
||||
def load_module_graph(cr, graph, status=None, perform_checks=True, **kwargs):
|
||||
|
||||
def load_module_graph(cr, graph, status=None, perform_checks=True, skip_modules=None, **kwargs):
|
||||
"""Migrates+Updates or Installs all module nodes from ``graph``
|
||||
:param graph: graph of module nodes to load
|
||||
:param status: status dictionary for keeping track of progress
|
||||
:param perform_checks: whether module descriptors should be checked for validity (prints warnings
|
||||
for same cases, and even raise osv_except if certificate is invalid)
|
||||
:param skip_modules: optional list of module names (packages) which have previously been loaded and can be skipped
|
||||
:return: list of modules that were installed or updated
|
||||
"""
|
||||
def process_sql_file(cr, fp):
|
||||
queries = fp.read().split(';')
|
||||
for query in queries:
|
||||
|
@ -612,29 +629,33 @@ def load_module_graph(cr, graph, status=None, perform_checks=True, **kwargs):
|
|||
logger.notifyChannel('init', netsvc.LOG_INFO, 'module %s: loading %s' % (m, filename))
|
||||
_, ext = os.path.splitext(filename)
|
||||
fp = tools.file_open(opj(m, filename))
|
||||
if ext == '.csv':
|
||||
noupdate = (kind == 'init')
|
||||
tools.convert_csv_import(cr, m, os.path.basename(filename), fp.read(), idref, mode=mode, noupdate=noupdate)
|
||||
elif ext == '.sql':
|
||||
process_sql_file(cr, fp)
|
||||
elif ext == '.yml':
|
||||
tools.convert_yaml_import(cr, m, fp, idref, mode=mode, **kwargs)
|
||||
else:
|
||||
tools.convert_xml_import(cr, m, fp, idref, mode=mode, **kwargs)
|
||||
fp.close()
|
||||
try:
|
||||
if ext == '.csv':
|
||||
noupdate = (kind == 'init')
|
||||
tools.convert_csv_import(cr, m, os.path.basename(filename), fp.read(), idref, mode=mode, noupdate=noupdate)
|
||||
elif ext == '.sql':
|
||||
process_sql_file(cr, fp)
|
||||
elif ext == '.yml':
|
||||
tools.convert_yaml_import(cr, m, fp, idref, mode=mode, **kwargs)
|
||||
else:
|
||||
tools.convert_xml_import(cr, m, fp, idref, mode=mode, **kwargs)
|
||||
finally:
|
||||
fp.close()
|
||||
|
||||
def load_demo_xml(cr, m, idref, mode):
|
||||
for xml in package.data.get('demo_xml', []):
|
||||
name, ext = os.path.splitext(xml)
|
||||
logger.notifyChannel('init', netsvc.LOG_INFO, 'module %s: loading %s' % (m, xml))
|
||||
fp = tools.file_open(opj(m, xml))
|
||||
if ext == '.csv':
|
||||
tools.convert_csv_import(cr, m, os.path.basename(xml), fp.read(), idref, mode=mode, noupdate=True)
|
||||
elif ext == '.yml':
|
||||
tools.convert_yaml_import(cr, m, fp, idref, mode=mode, noupdate=True, **kwargs)
|
||||
else:
|
||||
tools.convert_xml_import(cr, m, fp, idref, mode=mode, noupdate=True, **kwargs)
|
||||
fp.close()
|
||||
try:
|
||||
if ext == '.csv':
|
||||
tools.convert_csv_import(cr, m, os.path.basename(xml), fp.read(), idref, mode=mode, noupdate=True)
|
||||
elif ext == '.yml':
|
||||
tools.convert_yaml_import(cr, m, fp, idref, mode=mode, noupdate=True, **kwargs)
|
||||
else:
|
||||
tools.convert_xml_import(cr, m, fp, idref, mode=mode, noupdate=True, **kwargs)
|
||||
finally:
|
||||
fp.close()
|
||||
|
||||
def load_data(cr, module_name, id_map, mode):
|
||||
_load_data(cr, module_name, id_map, mode, 'data')
|
||||
|
@ -648,8 +669,7 @@ def load_module_graph(cr, graph, status=None, perform_checks=True, **kwargs):
|
|||
try:
|
||||
_load_data(cr, module_name, id_map, mode, 'test')
|
||||
except Exception, e:
|
||||
logger.notifyChannel('ERROR', netsvc.LOG_TEST, e)
|
||||
pass
|
||||
logging.getLogger('test').exception('Tests failed to execute in module %s', module_name)
|
||||
finally:
|
||||
if tools.config.options['test_commit']:
|
||||
cr.commit()
|
||||
|
@ -657,41 +677,40 @@ def load_module_graph(cr, graph, status=None, perform_checks=True, **kwargs):
|
|||
cr.rollback()
|
||||
|
||||
def _load_data(cr, module_name, id_map, mode, kind):
|
||||
noupdate = (kind == 'demo')
|
||||
for filename in package.data.get(kind, []):
|
||||
noupdate = (kind == 'demo')
|
||||
_, ext = os.path.splitext(filename)
|
||||
log.info("module %s: loading %s", module_name, filename)
|
||||
pathname = os.path.join(module_name, filename)
|
||||
file = tools.file_open(pathname)
|
||||
# TODO manage .csv file with noupdate == (kind == 'init')
|
||||
if ext == '.sql':
|
||||
process_sql_file(cr, file)
|
||||
elif ext == '.csv':
|
||||
noupdate = (kind == 'init')
|
||||
tools.convert_csv_import(cr, module_name, pathname, file.read(), id_map, mode, noupdate)
|
||||
elif ext == '.yml':
|
||||
tools.convert_yaml_import(cr, module_name, file, id_map, mode, noupdate)
|
||||
else:
|
||||
tools.convert_xml_import(cr, module_name, file, id_map, mode, noupdate)
|
||||
file.close()
|
||||
try:
|
||||
if ext == '.sql':
|
||||
process_sql_file(cr, file)
|
||||
elif ext == '.csv':
|
||||
noupdate = (kind == 'init')
|
||||
tools.convert_csv_import(cr, module_name, pathname, file.read(), id_map, mode, noupdate)
|
||||
elif ext == '.yml':
|
||||
tools.convert_yaml_import(cr, module_name, file, id_map, mode, noupdate)
|
||||
else:
|
||||
tools.convert_xml_import(cr, module_name, file, id_map, mode, noupdate)
|
||||
finally:
|
||||
file.close()
|
||||
|
||||
# **kwargs is passed directly to convert_xml_import
|
||||
if not status:
|
||||
status = {}
|
||||
|
||||
status = status.copy()
|
||||
package_todo = []
|
||||
processed_modules = []
|
||||
statusi = 0
|
||||
pool = pooler.get_pool(cr.dbname)
|
||||
|
||||
migrations = MigrationManager(cr, graph)
|
||||
|
||||
has_updates = False
|
||||
modobj = None
|
||||
|
||||
logger.notifyChannel('init', netsvc.LOG_DEBUG, 'loading %d packages..' % len(graph))
|
||||
|
||||
for package in graph:
|
||||
if skip_modules and package.name in skip_modules:
|
||||
continue
|
||||
logger.notifyChannel('init', netsvc.LOG_INFO, 'module %s: loading objects' % package.name)
|
||||
migrations.migrate_module(package, 'pre')
|
||||
register_class(package.name)
|
||||
|
@ -705,6 +724,9 @@ def load_module_graph(cr, graph, status=None, perform_checks=True, **kwargs):
|
|||
m = package.name
|
||||
mid = package.id
|
||||
|
||||
if skip_modules and m in skip_modules:
|
||||
continue
|
||||
|
||||
if modobj is None:
|
||||
modobj = pool.get('ir.module.module')
|
||||
|
||||
|
@ -719,7 +741,6 @@ def load_module_graph(cr, graph, status=None, perform_checks=True, **kwargs):
|
|||
mode = 'init'
|
||||
|
||||
if hasattr(package, 'init') or hasattr(package, 'update') or package.state in ('to install', 'to upgrade'):
|
||||
has_updates = True
|
||||
for kind in ('init', 'update'):
|
||||
if package.state=='to upgrade':
|
||||
# upgrading the module information
|
||||
|
@ -738,7 +759,7 @@ def load_module_graph(cr, graph, status=None, perform_checks=True, **kwargs):
|
|||
# as there is no rollback.
|
||||
load_test(cr, m, idref, mode)
|
||||
|
||||
package_todo.append(package.name)
|
||||
processed_modules.append(package.name)
|
||||
|
||||
migrations.migrate_module(package, 'post')
|
||||
|
||||
|
@ -758,14 +779,9 @@ def load_module_graph(cr, graph, status=None, perform_checks=True, **kwargs):
|
|||
|
||||
statusi += 1
|
||||
|
||||
cr.execute('select model from ir_model where state=%s', ('manual',))
|
||||
for model in cr.dictfetchall():
|
||||
pool.get('ir.model').instanciate(cr, 1, model['model'], {})
|
||||
|
||||
pool.get('ir.model.data')._process_end(cr, 1, package_todo)
|
||||
cr.commit()
|
||||
|
||||
return has_updates
|
||||
return processed_modules
|
||||
|
||||
def _check_module_names(cr, module_names):
|
||||
mod_names = set(module_names)
|
||||
|
@ -797,17 +813,30 @@ def load_modules(db, force_demo=False, status=None, update_module=False):
|
|||
force = []
|
||||
if force_demo:
|
||||
force.append('demo')
|
||||
|
||||
# This is a brand new pool, just created in pooler.get_db_and_pool()
|
||||
pool = pooler.get_pool(cr.dbname)
|
||||
|
||||
try:
|
||||
processed_modules = []
|
||||
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'))
|
||||
|
||||
# STEP 1: LOAD BASE (must be done before module dependencies can be computed for later steps)
|
||||
graph = create_graph(cr, ['base'], force)
|
||||
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)')
|
||||
has_updates = load_module_graph(cr, graph, status, perform_checks=(not update_module), report=report)
|
||||
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))
|
||||
|
||||
if tools.config['load_language']:
|
||||
for lang in tools.config['load_language'].split(','):
|
||||
tools.load_language(cr, lang)
|
||||
|
||||
# STEP 2: Mark other modules to be loaded/updated
|
||||
if update_module:
|
||||
modobj = pool.get('ir.module.module')
|
||||
logger.notifyChannel('init', netsvc.LOG_INFO, 'updating modules list')
|
||||
|
@ -832,6 +861,8 @@ def load_modules(db, force_demo=False, status=None, update_module=False):
|
|||
|
||||
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
|
||||
|
@ -849,14 +880,19 @@ def load_modules(db, force_demo=False, status=None, update_module=False):
|
|||
break
|
||||
|
||||
logger.notifyChannel('init', netsvc.LOG_DEBUG, 'Updating graph with %d more modules' % (len(module_list)))
|
||||
r = load_module_graph(cr, graph, status, report=report)
|
||||
has_updates = has_updates or r
|
||||
processed_modules.extend(load_module_graph(cr, graph, status, report=report, skip_modules=processed_modules))
|
||||
|
||||
# STEP 4: Finish and cleanup
|
||||
if processed_modules:
|
||||
# load custom models
|
||||
cr.execute('select model from ir_model where state=%s', ('manual',))
|
||||
for model in cr.dictfetchall():
|
||||
pool.get('ir.model').instanciate(cr, 1, model['model'], {})
|
||||
|
||||
if has_updates:
|
||||
cr.execute("""select model,name from ir_model where id NOT IN (select distinct model_id from ir_model_access)""")
|
||||
for (model, name) in cr.fetchall():
|
||||
model_obj = pool.get(model)
|
||||
if not isinstance(model_obj, osv.osv.osv_memory):
|
||||
if model_obj and not isinstance(model_obj, osv.osv.osv_memory):
|
||||
logger.notifyChannel('init', netsvc.LOG_WARNING, 'object %s (%s) has no access rules!' % (model, name))
|
||||
|
||||
# Temporary warning while we remove access rights on osv_memory objects, as they have
|
||||
|
@ -872,6 +908,11 @@ def load_modules(db, force_demo=False, status=None, update_module=False):
|
|||
obj = pool.get(model)
|
||||
if obj:
|
||||
obj._check_removed_columns(cr, log=True)
|
||||
else:
|
||||
logger.notifyChannel('init', netsvc.LOG_WARNING, "Model %s is referenced but not present in the orm pool!" % model)
|
||||
|
||||
# Cleanup orphan records
|
||||
pool.get('ir.model.data')._process_end(cr, 1, processed_modules)
|
||||
|
||||
if report.get_report():
|
||||
logger.notifyChannel('init', netsvc.LOG_INFO, report)
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
import ir
|
||||
import module
|
||||
import res
|
||||
import maintenance
|
||||
import publisher_warranty
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
|
||||
{
|
||||
'name': 'Base',
|
||||
'version': '1.2',
|
||||
'version': '1.3',
|
||||
'category': 'Generic Modules/Base',
|
||||
'description': """The kernel of OpenERP, needed for all installation.""",
|
||||
'author': 'OpenERP SA',
|
||||
|
@ -71,22 +71,25 @@
|
|||
|
||||
'res/ir_property_view.xml',
|
||||
'security/base_security.xml',
|
||||
'maintenance/maintenance_view.xml',
|
||||
'publisher_warranty/publisher_warranty_view.xml',
|
||||
|
||||
'security/ir.model.access.csv',
|
||||
'res/res_widget_view.xml',
|
||||
'res/res_widget_data.xml',
|
||||
'publisher_warranty/publisher_warranty_data.xml',
|
||||
],
|
||||
'demo_xml': [
|
||||
'base_demo.xml',
|
||||
'res/partner/partner_demo.xml',
|
||||
'res/partner/crm_demo.xml',
|
||||
'res/res_widget_demo.xml',
|
||||
],
|
||||
'test': [
|
||||
'test/base_test.xml',
|
||||
#'test/base_test.yml'
|
||||
'test/base_test.yml',
|
||||
'test/test_context.xml',
|
||||
'test/bug_lp541545.xml',
|
||||
'test/test_osv_expression.yml',
|
||||
],
|
||||
'installable': True,
|
||||
'active': True,
|
||||
|
|
|
@ -161,7 +161,8 @@ CREATE TABLE res_groups (
|
|||
|
||||
CREATE TABLE res_groups_users_rel (
|
||||
uid integer NOT NULL references res_users on delete cascade,
|
||||
gid integer NOT NULL references res_groups on delete cascade
|
||||
gid integer NOT NULL references res_groups on delete cascade,
|
||||
UNIQUE("uid","gid")
|
||||
);
|
||||
|
||||
create index res_groups_users_rel_uid_idx on res_groups_users_rel (uid);
|
||||
|
@ -288,6 +289,7 @@ CREATE TABLE ir_module_module (
|
|||
description text,
|
||||
demo boolean default False,
|
||||
web boolean DEFAULT FALSE,
|
||||
license character varying(32),
|
||||
primary key(id)
|
||||
);
|
||||
ALTER TABLE ir_module_module add constraint name_uniq unique (name);
|
||||
|
|
|
@ -28,6 +28,8 @@
|
|||
<field name="translatable">True</field>
|
||||
</record>
|
||||
|
||||
<function name="install_lang" model="res.lang"/>
|
||||
|
||||
<record id="ad" model="res.country">
|
||||
<field name="name">Andorra, Principality of</field>
|
||||
<field name="code">ad</field>
|
||||
|
@ -915,7 +917,7 @@
|
|||
</record>
|
||||
<record id="uk" model="res.country">
|
||||
<field name="name">United Kingdom</field>
|
||||
<field name="code">uk</field>
|
||||
<field name="code">gb</field>
|
||||
</record>
|
||||
<record id="um" model="res.country">
|
||||
<field name="name">USA Minor Outlying Islands</field>
|
||||
|
@ -989,7 +991,7 @@
|
|||
<field name="name">Zambia</field>
|
||||
<field name="code">zm</field>
|
||||
</record>
|
||||
<!-- DEPRECATED, News name of Zaire is Democratic Republic of the Congo ! -->
|
||||
<!-- DEPRECATED, New name of Zaire is Democratic Republic of the Congo ! -->
|
||||
<record id="zr" model="res.country">
|
||||
<field name="name">Zaire</field>
|
||||
<field name="code">zr</field>
|
||||
|
@ -1020,7 +1022,6 @@
|
|||
<!-- Currencies -->
|
||||
<record id="EUR" model="res.currency">
|
||||
<field name="name">EUR</field>
|
||||
<field name="code">EUR</field>
|
||||
<field name="symbol">€</field>
|
||||
<field name="rounding">0.01</field>
|
||||
<field name="accuracy">4</field>
|
||||
|
@ -1044,7 +1045,7 @@
|
|||
</record>
|
||||
|
||||
<assert id="main_company" model="res.company">
|
||||
<test expr="currency_id.code == 'eur'.upper()"/>
|
||||
<test expr="currency_id.name == 'eur'.upper()"/>
|
||||
<test expr="name">OpenERP S.A.</test>
|
||||
</assert>
|
||||
|
||||
|
@ -1073,7 +1074,6 @@
|
|||
</record>
|
||||
<record id="USD" model="res.currency">
|
||||
<field name="name">USD</field>
|
||||
<field name="code">USD</field>
|
||||
<field name="symbol">$</field>
|
||||
<field name="rounding">0.01</field>
|
||||
<field name="accuracy">4</field>
|
||||
|
@ -1087,7 +1087,6 @@
|
|||
|
||||
<record id="VEB" model="res.currency">
|
||||
<field name="name">Bs</field>
|
||||
<field name="code">VEB</field>
|
||||
<field name="symbol">Bs</field>
|
||||
<field name="rounding">2.95</field>
|
||||
<field name="accuracy">4</field>
|
||||
|
@ -1101,7 +1100,6 @@
|
|||
|
||||
<record id="CAD" model="res.currency">
|
||||
<field name="name">CAD</field>
|
||||
<field name="code">CAD</field>
|
||||
<field name="symbol">$</field>
|
||||
<field name="rounding">0.01</field>
|
||||
<field name="accuracy">4</field>
|
||||
|
@ -1116,7 +1114,6 @@
|
|||
|
||||
<record id="CHF" model="res.currency">
|
||||
<field name="name">CHF</field>
|
||||
<field name="code">CHF</field>
|
||||
<field name="symbol">CHF</field>
|
||||
<field name="rounding">0.01</field>
|
||||
<field name="accuracy">4</field>
|
||||
|
@ -1130,7 +1127,6 @@
|
|||
|
||||
<record id="BRL" model="res.currency">
|
||||
<field name="name">BRL</field>
|
||||
<field name="code">BRL</field>
|
||||
<field name="symbol">R$</field>
|
||||
<field name="rounding">0.01</field>
|
||||
<field name="accuracy">4</field>
|
||||
|
@ -1144,7 +1140,6 @@
|
|||
|
||||
<record id="CNY" model="res.currency">
|
||||
<field name="name">CNY</field>
|
||||
<field name="code">CNY</field>
|
||||
<field name="symbol">¥</field>
|
||||
<field name="rounding">0.01</field>
|
||||
<field name="accuracy">4</field>
|
||||
|
@ -1159,7 +1154,6 @@
|
|||
|
||||
<record id="COP" model="res.currency">
|
||||
<field name="name">COP</field>
|
||||
<field name="code">COP</field>
|
||||
<field name="symbol">$</field>
|
||||
<field name="rounding">0.01</field>
|
||||
<field name="accuracy">4</field>
|
||||
|
@ -1170,10 +1164,9 @@
|
|||
<field name="currency_id" ref="COP"/>
|
||||
<field eval="time.strftime('%Y-01-01')" name="name"/>
|
||||
</record>
|
||||
|
||||
|
||||
<record id="CZK" model="res.currency">
|
||||
<field name="name">Kč</field>
|
||||
<field name="code">CZK</field>
|
||||
<field name="symbol">Kč</field>
|
||||
<field name="rounding">0.01</field>
|
||||
<field name="accuracy">4</field>
|
||||
|
@ -1186,8 +1179,7 @@
|
|||
</record>
|
||||
|
||||
<record id="DKK" model="res.currency">
|
||||
<field name="name">kr</field>
|
||||
<field name="code">DKK</field>
|
||||
<field name="name">DKK</field>
|
||||
<field name="symbol">kr</field>
|
||||
<field name="rounding">0.01</field>
|
||||
<field name="accuracy">4</field>
|
||||
|
@ -1202,7 +1194,6 @@
|
|||
|
||||
<record id="HUF" model="res.currency">
|
||||
<field name="name">Ft</field>
|
||||
<field name="code">HUF</field>
|
||||
<field name="symbol">Ft</field>
|
||||
<field name="rounding">0.01</field>
|
||||
<field name="accuracy">4</field>
|
||||
|
@ -1213,29 +1204,27 @@
|
|||
<field name="currency_id" ref="HUF"/>
|
||||
<field eval="time.strftime('%Y-01-01')" name="name"/>
|
||||
</record>
|
||||
|
||||
|
||||
<record id="IDR" model="res.currency">
|
||||
<field name="name">Rs</field>
|
||||
<field name="code">IDR</field>
|
||||
<field name="symbol">Rs</field>
|
||||
<field name="name">Rp</field>
|
||||
<field name="symbol">Rp</field>
|
||||
<field name="rounding">0.01</field>
|
||||
<field name="accuracy">4</field>
|
||||
<field name="company_id" ref="main_company"/>
|
||||
</record>
|
||||
<record id="rateIDR1" model="res.currency.rate">
|
||||
<field name="rate">65.8287</field>
|
||||
<field name="rate">14352.00</field>
|
||||
<field name="currency_id" ref="IDR"/>
|
||||
<field eval="time.strftime('2009-01-01')" name="name"/>
|
||||
</record>
|
||||
<record id="rateIDR" model="res.currency.rate">
|
||||
<field name="rate">58.8287</field>
|
||||
<field name="rate">11796.39</field>
|
||||
<field name="currency_id" ref="IDR"/>
|
||||
<field eval="time.strftime('%Y-01-01')" name="name"/>
|
||||
</record>
|
||||
|
||||
<record id="LVL" model="res.currency">
|
||||
<field name="name">Ls</field>
|
||||
<field name="code">LVL</field>
|
||||
<field name="symbol">Ls</field>
|
||||
<field name="rounding">0.01</field>
|
||||
<field name="accuracy">4</field>
|
||||
|
@ -1249,8 +1238,7 @@
|
|||
|
||||
|
||||
<record id="NOK" model="res.currency">
|
||||
<field name="name">kr</field>
|
||||
<field name="code">NOK</field>
|
||||
<field name="name">NOK</field>
|
||||
<field name="symbol">kr</field>
|
||||
<field name="rounding">0.01</field>
|
||||
<field name="accuracy">4</field>
|
||||
|
@ -1262,10 +1250,21 @@
|
|||
<field eval="time.strftime('%Y-01-01')" name="name"/>
|
||||
</record>
|
||||
|
||||
<record id="XPF" model="res.currency">
|
||||
<field name="name">XPF</field>
|
||||
<field name="symbol">XPF</field>
|
||||
<field name="rounding">1.00</field>
|
||||
<field name="accuracy">4</field>
|
||||
<field name="company_id" ref="main_company"/>
|
||||
</record>
|
||||
<record id="rateXPF" model="res.currency.rate">
|
||||
<field name="rate">119.331742</field>
|
||||
<field name="currency_id" ref="XPF"/>
|
||||
<field eval="time.strftime('%Y-01-01')" name="name"/>
|
||||
</record>
|
||||
|
||||
<record id="PAB" model="res.currency">
|
||||
<field name="name">PAB</field>
|
||||
<field name="code">PAB</field>
|
||||
<field name="symbol">B/.</field>
|
||||
<field name="rounding">0.01</field>
|
||||
<field name="accuracy">4</field>
|
||||
|
@ -1279,7 +1278,6 @@
|
|||
|
||||
<record id="PLN" model="res.currency">
|
||||
<field name="name">zł</field>
|
||||
<field name="code">PLN</field>
|
||||
<field name="symbol">zł</field>
|
||||
<field name="rounding">0.01</field>
|
||||
<field name="accuracy">4</field>
|
||||
|
@ -1290,10 +1288,9 @@
|
|||
<field name="currency_id" ref="PLN"/>
|
||||
<field eval="time.strftime('%Y-01-01')" name="name"/>
|
||||
</record>
|
||||
|
||||
|
||||
<record id="SEK" model="res.currency">
|
||||
<field name="name">kr</field>
|
||||
<field name="code">SEK</field>
|
||||
<field name="name">SEK</field>
|
||||
<field name="symbol">kr</field>
|
||||
<field name="rounding">0.01</field>
|
||||
<field name="accuracy">4</field>
|
||||
|
@ -1304,10 +1301,9 @@
|
|||
<field name="currency_id" ref="SEK"/>
|
||||
<field eval="time.strftime('%Y-01-01')" name="name"/>
|
||||
</record>
|
||||
|
||||
|
||||
<record id="GBP" model="res.currency">
|
||||
<field name="name">GBP</field>
|
||||
<field name="code">GBP</field>
|
||||
<field name="symbol">₤</field>
|
||||
<field name="rounding">0.01</field>
|
||||
<field name="accuracy">4</field>
|
||||
|
@ -1321,12 +1317,11 @@
|
|||
|
||||
<record id="ARS" model="res.currency">
|
||||
<field name="name">ARS</field>
|
||||
<field name="code">ARS</field>
|
||||
<field name="symbol">$</field>
|
||||
<field name="rounding">0.01</field>
|
||||
<field name="accuracy">4</field>
|
||||
<field name="company_id" ref="main_company"/>
|
||||
</record>
|
||||
</record>
|
||||
<record id="rateARS" model="res.currency.rate">
|
||||
<field name="rate">5.0881</field>
|
||||
<field name="currency_id" ref="ARS"/>
|
||||
|
@ -1335,7 +1330,6 @@
|
|||
|
||||
<record id="INR" model="res.currency">
|
||||
<field name="name">Rs</field>
|
||||
<field name="code">INR</field>
|
||||
<field name="symbol">Rs</field>
|
||||
<field name="rounding">0.01</field>
|
||||
<field name="accuracy">4</field>
|
||||
|
@ -1349,7 +1343,6 @@
|
|||
|
||||
<record id="AUD" model="res.currency">
|
||||
<field name="name">AUD</field>
|
||||
<field name="code">AUD</field>
|
||||
<field name="symbol">$</field>
|
||||
<field name="rounding">0.01</field>
|
||||
<field name="accuracy">4</field>
|
||||
|
@ -1363,7 +1356,6 @@
|
|||
|
||||
<record id="UAH" model="res.currency">
|
||||
<field name="name">UAH</field>
|
||||
<field name="code">UAH</field>
|
||||
<field name="symbol">₴</field>
|
||||
<field name="rounding">0.01</field>
|
||||
<field name="accuracy">4</field>
|
||||
|
@ -1374,9 +1366,244 @@
|
|||
<field name="currency_id" ref="UAH"/>
|
||||
<field eval="time.strftime('%Y-01-01')" name="name"/>
|
||||
</record>
|
||||
|
||||
<record id="VND" model="res.currency">
|
||||
<field name="name">VND</field>
|
||||
<field name="symbol">₫</field>
|
||||
<field name="rounding">0.01</field>
|
||||
<field name="accuracy">4</field>
|
||||
<field name="company_id" ref="main_company"/>
|
||||
</record>
|
||||
<record id="rateVND" model="res.currency.rate">
|
||||
<field name="rate">26330.01</field>
|
||||
<field name="currency_id" ref="VND"/>
|
||||
<field eval="time.strftime('%Y-01-01')" name="name"/>
|
||||
</record>
|
||||
|
||||
<record id="HKD" model="res.currency">
|
||||
<field name="name">HKD</field>
|
||||
<field name="symbol">$</field>
|
||||
<field name="rounding">0.01</field>
|
||||
<field name="accuracy">4</field>
|
||||
<field name="company_id" ref="main_company"/>
|
||||
</record>
|
||||
<record id="rateHKD" model="res.currency.rate">
|
||||
<field name="rate">11.1608</field>
|
||||
<field name="currency_id" ref="HKD"/>
|
||||
<field eval="time.strftime('%Y-01-01')" name="name"/>
|
||||
</record>
|
||||
|
||||
<record id="JPY" model="res.currency">
|
||||
<field name="name">JPY</field>
|
||||
<field name="symbol">¥</field>
|
||||
<field name="rounding">0.01</field>
|
||||
<field name="accuracy">4</field>
|
||||
<field name="company_id" ref="main_company"/>
|
||||
</record>
|
||||
<record id="rateJPY" model="res.currency.rate">
|
||||
<field name="rate">133.62</field>
|
||||
<field name="currency_id" ref="JPY"/>
|
||||
<field eval="time.strftime('%Y-01-01')" name="name"/>
|
||||
</record>
|
||||
|
||||
<record id="BGN" model="res.currency">
|
||||
<field name="name">BGN</field>
|
||||
<field name="symbol">лв</field>
|
||||
<field name="rounding">0.01</field>
|
||||
<field name="accuracy">4</field>
|
||||
<field name="company_id" ref="main_company"/>
|
||||
</record>
|
||||
<record id="rateBGN" model="res.currency.rate">
|
||||
<field name="rate">1.9558</field>
|
||||
<field name="currency_id" ref="BGN"/>
|
||||
<field eval="time.strftime('%Y-01-01')" name="name"/>
|
||||
</record>
|
||||
|
||||
<record id="LTL" model="res.currency">
|
||||
<field name="name">LTL</field>
|
||||
<field name="symbol">Lt</field>
|
||||
<field name="rounding">0.01</field>
|
||||
<field name="accuracy">4</field>
|
||||
<field name="company_id" ref="main_company"/>
|
||||
</record>
|
||||
<record id="rateLTL" model="res.currency.rate">
|
||||
<field name="rate">3.4528</field>
|
||||
<field name="currency_id" ref="LTL"/>
|
||||
<field eval="time.strftime('%Y-01-01')" name="name"/>
|
||||
</record>
|
||||
|
||||
<record id="RON" model="res.currency">
|
||||
<field name="name">RON</field>
|
||||
<field name="symbol">lei</field>
|
||||
<field name="rounding">0.01</field>
|
||||
<field name="accuracy">4</field>
|
||||
<field name="company_id" ref="main_company"/>
|
||||
</record>
|
||||
<record id="rateRON" model="res.currency.rate">
|
||||
<field name="rate">4.2253</field>
|
||||
<field name="currency_id" ref="RON"/>
|
||||
<field eval="time.strftime('%Y-01-01')" name="name"/>
|
||||
</record>
|
||||
|
||||
<record id="HRK" model="res.currency">
|
||||
<field name="name">HRK</field>
|
||||
<field name="symbol">kn</field>
|
||||
<field name="rounding">0.01</field>
|
||||
<field name="accuracy">4</field>
|
||||
<field name="company_id" ref="main_company"/>
|
||||
</record>
|
||||
<record id="rateHRK" model="res.currency.rate">
|
||||
<field name="rate">7.2936</field>
|
||||
<field name="currency_id" ref="HRK"/>
|
||||
<field eval="time.strftime('%Y-01-01')" name="name"/>
|
||||
</record>
|
||||
|
||||
<record id="RUB" model="res.currency">
|
||||
<field name="name">RUB</field>
|
||||
<field name="symbol">руб</field>
|
||||
<field name="rounding">0.01</field>
|
||||
<field name="accuracy">4</field>
|
||||
<field name="company_id" ref="main_company"/>
|
||||
</record>
|
||||
<record id="rateRUB" model="res.currency.rate">
|
||||
<field name="rate">43.16</field>
|
||||
<field name="currency_id" ref="RUB"/>
|
||||
<field eval="time.strftime('%Y-01-01')" name="name"/>
|
||||
</record>
|
||||
|
||||
<record id="TRY" model="res.currency">
|
||||
<field name="name">TRY</field>
|
||||
<field name="symbol">TL</field>
|
||||
<field name="rounding">0.01</field>
|
||||
<field name="accuracy">4</field>
|
||||
<field name="company_id" ref="main_company"/>
|
||||
</record>
|
||||
<record id="rateTRY" model="res.currency.rate">
|
||||
<field name="rate">2.1411</field>
|
||||
<field name="currency_id" ref="TRY"/>
|
||||
<field eval="time.strftime('%Y-01-01')" name="name"/>
|
||||
</record>
|
||||
|
||||
<record id="KRW" model="res.currency">
|
||||
<field name="name">KRW</field>
|
||||
<field name="symbol">₩</field>
|
||||
<field name="rounding">0.01</field>
|
||||
<field name="accuracy">4</field>
|
||||
<field name="company_id" ref="main_company"/>
|
||||
</record>
|
||||
<record id="rateKRW" model="res.currency.rate">
|
||||
<field name="rate">1662.37</field>
|
||||
<field name="currency_id" ref="KRW"/>
|
||||
<field eval="time.strftime('%Y-01-01')" name="name"/>
|
||||
</record>
|
||||
|
||||
<record id="MXN" model="res.currency">
|
||||
<field name="name">MXN</field>
|
||||
<field name="symbol">$</field>
|
||||
<field name="rounding">0.01</field>
|
||||
<field name="accuracy">4</field>
|
||||
<field name="company_id" ref="main_company"/>
|
||||
</record>
|
||||
<record id="rateMXN" model="res.currency.rate">
|
||||
<field name="rate">18.6664</field>
|
||||
<field name="currency_id" ref="MXN"/>
|
||||
<field eval="time.strftime('%Y-01-01')" name="name"/>
|
||||
</record>
|
||||
|
||||
<record id="MYR" model="res.currency">
|
||||
<field name="name">MYR</field>
|
||||
<field name="symbol">RM</field>
|
||||
<field name="rounding">0.01</field>
|
||||
<field name="accuracy">4</field>
|
||||
<field name="company_id" ref="main_company"/>
|
||||
</record>
|
||||
<record id="rateMYR" model="res.currency.rate">
|
||||
<field name="rate">4.8887</field>
|
||||
<field name="currency_id" ref="MYR"/>
|
||||
<field eval="time.strftime('%Y-01-01')" name="name"/>
|
||||
</record>
|
||||
|
||||
<record id="NZD" model="res.currency">
|
||||
<field name="name">NZD</field>
|
||||
<field name="symbol">$</field>
|
||||
<field name="rounding">0.01</field>
|
||||
<field name="accuracy">4</field>
|
||||
<field name="company_id" ref="main_company"/>
|
||||
</record>
|
||||
<record id="rateNZD" model="res.currency.rate">
|
||||
<field name="rate">1.9764</field>
|
||||
<field name="currency_id" ref="NZD"/>
|
||||
<field eval="time.strftime('%Y-01-01')" name="name"/>
|
||||
</record>
|
||||
|
||||
<record id="PHP" model="res.currency">
|
||||
<field name="name">PHP</field>
|
||||
<field name="symbol">Php</field>
|
||||
<field name="rounding">0.01</field>
|
||||
<field name="accuracy">4</field>
|
||||
<field name="company_id" ref="main_company"/>
|
||||
</record>
|
||||
<record id="ratePHP" model="res.currency.rate">
|
||||
<field name="rate">66.1</field>
|
||||
<field name="currency_id" ref="PHP"/>
|
||||
<field eval="time.strftime('%Y-01-01')" name="name"/>
|
||||
</record>
|
||||
|
||||
<record id="SGD" model="res.currency">
|
||||
<field name="name">SGD</field>
|
||||
<field name="symbol">$</field>
|
||||
<field name="rounding">0.01</field>
|
||||
<field name="accuracy">4</field>
|
||||
<field name="company_id" ref="main_company"/>
|
||||
</record>
|
||||
<record id="rateSGD" model="res.currency.rate">
|
||||
<field name="rate">2.0126</field>
|
||||
<field name="currency_id" ref="SGD"/>
|
||||
<field eval="time.strftime('%Y-01-01')" name="name"/>
|
||||
</record>
|
||||
|
||||
<record id="THB" model="res.currency">
|
||||
<field name="name">THB</field>
|
||||
<field name="symbol">฿</field>
|
||||
<field name="rounding">0.01</field>
|
||||
<field name="accuracy">4</field>
|
||||
<field name="company_id" ref="main_company"/>
|
||||
</record>
|
||||
<record id="rateTHB" model="res.currency.rate">
|
||||
<field name="rate">47.779</field>
|
||||
<field name="currency_id" ref="THB"/>
|
||||
<field eval="time.strftime('%Y-01-01')" name="name"/>
|
||||
</record>
|
||||
|
||||
<record id="ZAR" model="res.currency">
|
||||
<field name="name">ZAR</field>
|
||||
<field name="symbol">R</field>
|
||||
<field name="rounding">0.01</field>
|
||||
<field name="accuracy">4</field>
|
||||
<field name="company_id" ref="main_company"/>
|
||||
</record>
|
||||
<record id="rateZAR" model="res.currency.rate">
|
||||
<field name="rate">10.5618</field>
|
||||
<field name="currency_id" ref="ZAR"/>
|
||||
<field eval="time.strftime('%Y-01-01')" name="name"/>
|
||||
</record>
|
||||
|
||||
<record id="res_bank_1" model="res.bank">
|
||||
<field name="name">Reserve</field>
|
||||
<field name="code">RSV</field>
|
||||
</record>
|
||||
|
||||
<record id="CRC" model="res.currency">
|
||||
<field name="name">CRC</field>
|
||||
<field name="rounding">0.01</field>
|
||||
<field name="accuracy">4</field>
|
||||
<field name="symbol">¢</field>
|
||||
</record>
|
||||
<record id="rateCRC" model="res.currency.rate">
|
||||
<field name="rate">691.3153</field>
|
||||
<field name="currency_id" ref="CRC"/>
|
||||
<field eval="time.strftime('%Y-01-01')" name="name"/>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
|
|
|
@ -1,14 +1,27 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data noupdate="1">
|
||||
|
||||
<record id="demo_address" model="res.partner.address">
|
||||
<field name="name">Fabien Dupont</field>
|
||||
<field name="street">Chaussee de Namur</field>
|
||||
<field name="zip">1367</field>
|
||||
<field name="city">Gerompont</field>
|
||||
<field name="phone">(+32).81.81.37.00</field>
|
||||
<field name="type">default</field>
|
||||
<field model="res.country" name="country_id" ref="be"/>
|
||||
<!-- Company ID will be set later -->
|
||||
<field name="company_id" eval="None"/>
|
||||
</record>
|
||||
|
||||
<record id="user_demo" model="res.users">
|
||||
<field name="login">demo</field>
|
||||
<field name="password">demo</field>
|
||||
<field name="name">Demo User</field>
|
||||
<field name="signature">Mr Demo</field>
|
||||
<field name="address_id" ref="main_address"/>
|
||||
<field name="address_id" ref="demo_address"/>
|
||||
<field name="company_id" ref="main_company"/>
|
||||
<field name="groups_id" eval="[(6,0,[ref('base.group_user')])]"/>
|
||||
<field name="groups_id" eval="[(6,0,[ref('base.group_user')])]"/>
|
||||
</record>
|
||||
</data>
|
||||
</openerp>
|
||||
|
|
|
@ -1,16 +1,18 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data>
|
||||
<menuitem icon="terp-administration" id="menu_administration" name="Administration" sequence="50"/>
|
||||
<menuitem icon="terp-administration" id="menu_administration"
|
||||
name="Administration" sequence="50"
|
||||
web_icon="data/administration.png"
|
||||
web_icon_hover="data/administration-hover.png"/>
|
||||
|
||||
<menuitem icon="terp-administration" id="menu_administration_shortcut" parent="menu_administration" name="Custom Shortcuts" sequence="50"/>
|
||||
<menuitem id="next_id_4" name="Low Level Objects"
|
||||
parent="base.menu_administration" sequence="3"
|
||||
groups="base.group_no_one"/>
|
||||
<menuitem id="menu_low_workflow" name="Workflows" parent="base.next_id_4"/>
|
||||
<menuitem id="menu_custom" name="Customization"
|
||||
parent="base.menu_administration" sequence="2"
|
||||
groups="base.group_extended"/>
|
||||
<menuitem id="next_id_4" name="Low Level Objects"
|
||||
parent="base.menu_custom" sequence="30"/>
|
||||
<menuitem id="menu_low_workflow" name="Workflows" parent="base.next_id_4"/>
|
||||
<menuitem id="menu_custom_action" name="Actions" parent="base.menu_custom" groups="base.group_extended" sequence="20"/>
|
||||
<menuitem id="menu_config" name="Configuration" parent="base.menu_administration" sequence="1"/>
|
||||
<menuitem id="menu_translation" name="Translations" parent="base.menu_administration" sequence="4"/>
|
||||
|
@ -28,6 +30,6 @@
|
|||
|
||||
<menuitem id="base.menu_reporting" name="Reporting" parent="base.menu_administration" sequence="11"
|
||||
groups="base.group_extended"/>
|
||||
<menuitem id="menu_audit" name="Audit" parent="base.menu_reporting" groups="base.group_extended" sequence="50"/>
|
||||
<menuitem id="menu_audit" name="Audit" parent="base.menu_reporting" sequence="50"/>
|
||||
</data>
|
||||
</openerp>
|
||||
|
|
|
@ -69,6 +69,7 @@
|
|||
Users
|
||||
======================
|
||||
-->
|
||||
|
||||
<record id="view_users_form_simple_modif" model="ir.ui.view">
|
||||
<field name="name">res.users.form.modif</field>
|
||||
<field name="model">res.users</field>
|
||||
|
@ -79,21 +80,19 @@
|
|||
<notebook colspan="4">
|
||||
<page string="Current Activity">
|
||||
<field name="company_id" widget="selection" readonly="0"
|
||||
context="{'user_id': self, 'user_preference': 1}" groups="base.group_multi_company"/>
|
||||
groups="base.group_multi_company"
|
||||
on_change="on_change_company_id(company_id)" />
|
||||
<field name="view" readonly="0"/>
|
||||
<label string="" colspan="2"/>
|
||||
<separator string="Default Filters" colspan="4"/>
|
||||
<newline/>
|
||||
</page>
|
||||
<page string="Preferences">
|
||||
<field name="password" password="True" readonly="0" />
|
||||
<field name="context_lang" completion="1" readonly="0"/>
|
||||
<label string="" colspan="1"/>
|
||||
<label colspan="3" string="You must logout and login again after changing your password."/>
|
||||
<field name="context_tz" completion="1" readonly="0"/>
|
||||
<field name="menu_tips" colspan="2" readonly="0"/>
|
||||
<separator string="Email & Signature" colspan="4"/>
|
||||
<group colspan="4"><field name="user_email" widget="email"/></group>
|
||||
<group colspan="4"><field name="user_email" widget="email" readonly="0"/></group>
|
||||
<field colspan="4" name="signature" readonly="0" nolabel="1"/>
|
||||
</page>
|
||||
</notebook>
|
||||
|
@ -110,15 +109,14 @@
|
|||
<field name="name" select="1"/>
|
||||
<field name="active"/>
|
||||
<field name="login" select="1"/>
|
||||
<field name="password" password="True"/>
|
||||
<field name="new_password" password="True"/>
|
||||
<newline/>
|
||||
<notebook colspan="4">
|
||||
<page string="User">
|
||||
<group colspan="1" col="2">
|
||||
<separator string="Contact" colspan="2"/>
|
||||
<field name="company_id" required="1"
|
||||
context="{'user_id': self, 'user_preference': 1}"
|
||||
on_change="on_change_company_id(company_id)"
|
||||
context="{'user_preference': 0}"
|
||||
groups="base.group_multi_company"
|
||||
/>
|
||||
<field name="address_id"/>
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 2.1 KiB |
Binary file not shown.
After Width: | Height: | Size: 1.2 KiB |
Binary file not shown.
After Width: | Height: | Size: 2.3 KiB |
Binary file not shown.
After Width: | Height: | Size: 1.2 KiB |
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -34,6 +34,8 @@ import ir_exports
|
|||
import workflow
|
||||
import ir_rule
|
||||
import wizard
|
||||
import ir_config_parameter
|
||||
import osv_memory_autovacuum
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
||||
|
|
|
@ -705,6 +705,7 @@
|
|||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">res.company</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="help">Create and manage the companies that will be managed by OpenERP from here. Shops or subsidiaries can be created and maintained from here.</field>
|
||||
</record>
|
||||
<menuitem action="action_res_company_form" id="menu_action_res_company_form" parent="base.menu_res_company_global"/>
|
||||
|
||||
|
@ -716,6 +717,7 @@
|
|||
<field name="view_type">form</field>
|
||||
<field name="view_id" ref="view_users_tree"/>
|
||||
<field name="search_view_id" ref="view_users_search"/>
|
||||
<field name="help">Create and manage users that will connect to the system. Users can be deactivated should there be a period of time during which they will/should not connect to the system. You can assign them groups in order to give them specific access to the applications they need to use in the system.</field>
|
||||
</record>
|
||||
<record id="action_res_users_view1" model="ir.actions.act_window.view">
|
||||
<field eval="10" name="sequence"/>
|
||||
|
@ -757,6 +759,7 @@
|
|||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">res.groups</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="help">A group is a set of functional areas that will be assigned to the user in order to give them access and rights to specific applications and tasks in the system. You can create custom groups or edit the ones existing by default in order to customize the view of the menu that users will be able to see. Whether they can have a read, write, create and delete access right can be managed from here.</field>
|
||||
</record>
|
||||
<menuitem action="action_res_groups" id="menu_action_res_groups" parent="base.menu_users"
|
||||
groups="base.group_extended"/>
|
||||
|
@ -816,20 +819,17 @@
|
|||
<filter icon="terp-stock_zoom"
|
||||
string="Search"
|
||||
domain="[('type', '=', 'search')]"/>
|
||||
<separator orientation="vertical"/>
|
||||
<filter icon="gtk-indent"
|
||||
string="Tree"
|
||||
domain="[('type', '=', 'tree')]"/>
|
||||
<separator orientation="vertical"/>
|
||||
<filter icon="gtk-new"
|
||||
string="Form"
|
||||
domain="[('type', '=','form')]"/>
|
||||
<newline/>
|
||||
<separator orientation="vertical"/>
|
||||
<field name="name"/>
|
||||
<field name="type"/>
|
||||
<field name="model"/>
|
||||
<field name="inherit_id"/>
|
||||
<field name="xml_id"/>
|
||||
<newline/>
|
||||
<group expand="0" string="Group By...">
|
||||
<filter string="Object" icon="terp-stock_align_left_24" domain="[]" context="{'group_by':'model'}"/>
|
||||
|
@ -844,9 +844,56 @@
|
|||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">ir.ui.view</field>
|
||||
<field name="view_id" ref="view_view_tree"/>
|
||||
<field name="help">Views allows you to personalize each view of OpenERP. You can add new fields, move fields, rename them or delete the ones that you do not need.</field>
|
||||
</record>
|
||||
<menuitem action="action_ui_view" id="menu_action_ui_view" parent="base.next_id_2"/>
|
||||
|
||||
|
||||
<!-- View customizations -->
|
||||
<record id="view_view_custom_search" model="ir.ui.view">
|
||||
<field name="name">ir.ui.view.custom.search</field>
|
||||
<field name="model">ir.ui.view.custom</field>
|
||||
<field name="type">search</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Customized Views">
|
||||
<field name="user_id"/>
|
||||
<field name="ref_id"/>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
<record id="view_view_custom_form" model="ir.ui.view">
|
||||
<field name="name">ir.ui.view.custom.form</field>
|
||||
<field name="model">ir.ui.view.custom</field>
|
||||
<field name="type">form</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Customized Views">
|
||||
<field name="user_id"/>
|
||||
<field name="ref_id"/>
|
||||
<separator colspan="4" string="Customized Architecture"/>
|
||||
<field name="arch" colspan="4" nolabel="1"/>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
<record id="view_view_custom_tree" model="ir.ui.view">
|
||||
<field name="name">ir.ui.view.custom.tree</field>
|
||||
<field name="model">ir.ui.view.custom</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Customized Views">
|
||||
<field name="user_id"/>
|
||||
<field name="ref_id"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
<record id="action_ui_view_custom" model="ir.actions.act_window">
|
||||
<field name="name">Customized Views</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">ir.ui.view.custom</field>
|
||||
<field name="help">Customized views are used when users reorganize the content of their dashboard views (via web client)</field>
|
||||
</record>
|
||||
<menuitem action="action_ui_view_custom" id="menu_action_ui_view_custom" parent="base.next_id_4"/>
|
||||
|
||||
|
||||
<!-- Attachment -->
|
||||
<record id="view_attachment_form" model="ir.ui.view">
|
||||
<field name="name">ir.attachment.view</field>
|
||||
|
@ -936,7 +983,7 @@
|
|||
<group expand="0" string="Group By...">
|
||||
<filter string="Owner" icon="terp-personal" domain="[]" context="{'group_by':'create_uid'}"/>
|
||||
<filter string="Type" icon="terp-stock_symbol-selection" domain="[]" context="{'group_by':'type'}" groups="base.group_extended"/>
|
||||
<filter string="Company" icon="terp-personal" domain="[]" context="{'group_by':'company_id'}" groups="base.group_multi_company"/>
|
||||
<filter string="Company" icon="terp-gtk-home" domain="[]" context="{'group_by':'company_id'}" groups="base.group_multi_company"/>
|
||||
<separator orientation="vertical"/>
|
||||
<filter string="Month" help="Creation Month" icon="terp-go-month" domain="[]" context="{'group_by':'create_date'}"/>
|
||||
</group>
|
||||
|
@ -1043,8 +1090,8 @@
|
|||
<field name="type">tree</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Model Description">
|
||||
<field name="name"/>
|
||||
<field name="model"/>
|
||||
<field name="name"/>
|
||||
<field name="state"/>
|
||||
<field name="osv_memory"/>
|
||||
</tree>
|
||||
|
@ -1143,11 +1190,11 @@
|
|||
<field name="type">search</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Fields">
|
||||
<filter icon="terp-camera_test"
|
||||
<filter icon="terp-gnome-cpu-frequency-applet+"
|
||||
string="Required"
|
||||
domain="[('required', '=', True)]"/>
|
||||
<separator orientation="vertical"/>
|
||||
<filter icon="terp-stock_align_left_24"
|
||||
<filter icon="terp-dialog-close"
|
||||
string="Readonly"
|
||||
domain="[('readonly', '=', True)]"/>
|
||||
<separator orientation="vertical"/>
|
||||
|
@ -1263,7 +1310,7 @@
|
|||
<field name="type">search</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Translations">
|
||||
<filter icon="terp-document-new"
|
||||
<filter icon="terp-gdu-smart-failing"
|
||||
string="Untranslated"
|
||||
domain="['|',('value', '=', False),('value','=','')]"/>
|
||||
<separator orientation="vertical"/>
|
||||
|
@ -1375,6 +1422,12 @@
|
|||
<field name="action" colspan="4" />
|
||||
<field name="icon" on_change="onchange_icon(icon)" colspan="2"/>
|
||||
<field name="icon_pict" widget="picture" nolabel="1" colspan="2"/>
|
||||
<group col="4" colspan="8" groups="base.group_extended">
|
||||
<field name="web_icon" groups="base.group_extended" />
|
||||
<field name="web_icon_hover" groups="base.group_extended" />
|
||||
<field name="web_icon_data" widget="image" groups="base.group_extended"/>
|
||||
<field name="web_icon_hover_data" widget="image" groups="base.group_extended"/>
|
||||
</group>
|
||||
</group>
|
||||
<notebook colspan="4">
|
||||
<page string="Groups">
|
||||
|
@ -1402,7 +1455,9 @@
|
|||
<field name="res_model">ir.ui.menu</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_id" ref="edit_menu"/>
|
||||
<field name="context">{'ir.ui.menu.full_list':True}</field>
|
||||
<field name="search_view_id" ref="edit_menu_access_search"/>
|
||||
<field name="help">Manage and customize the items available and displayed in your OpenERP system menu. You can delete an item by clicking on the box at the beginning of each line and then delete it through the button that appeared. Items can be assigned to specific groups in order to make them accessible to some users within the system.</field>
|
||||
</record>
|
||||
<menuitem action="grant_menu_access" id="menu_grant_menu_access" parent="base.next_id_2" sequence="1"/>
|
||||
|
||||
|
@ -1673,7 +1728,7 @@
|
|||
<field name="name">Property multi-company</field>
|
||||
<field model="ir.model" name="model_id" ref="model_ir_property"/>
|
||||
<field eval="True" name="global"/>
|
||||
<field name="domain_force">['|',('company_id','child_of',user.company_id.id),('company_id','=',False)]</field>
|
||||
<field name="domain_force">['|',('company_id','=',user.company_id.id),('company_id','=',False)]</field>
|
||||
</record>
|
||||
|
||||
<!--server action view-->
|
||||
|
@ -1863,10 +1918,26 @@
|
|||
<field name="res_model">ir.actions.todo</field>
|
||||
<field name="view_id" ref="ir_actions_todo_tree"/>
|
||||
<field name="view_type">form</field>
|
||||
<field name="help">The configuration wizards are used to help you configure a new instance of OpenERP. They are launched during the installation of new modules, but you can choose to restart some wizards manually from this menu.</field>
|
||||
</record>
|
||||
<menuitem id="next_id_11" name="Configuration Wizards" parent="base.menu_config" sequence="1"/>
|
||||
|
||||
<menuitem action="act_ir_actions_todo_form" id="menu_ir_actions_todo_form"
|
||||
parent="next_id_11" groups="base.group_extended" sequence="20"/>
|
||||
|
||||
|
||||
|
||||
<record model="ir.cron" id="cronjob_osv_memory_autovacuum">
|
||||
<field name='name'>AutoVacuum osv_memory objects</field>
|
||||
<field name='interval_number'>30</field>
|
||||
<field name='interval_type'>minutes</field>
|
||||
<field name="numbercall">-1</field>
|
||||
<field name="active">True</field>
|
||||
<field name="doall" eval="False" />
|
||||
<field name="model">osv_memory.autovacuum</field>
|
||||
<field name="function">power_on</field>
|
||||
<field name="args">()</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
|
|
|
@ -26,6 +26,7 @@ import time
|
|||
from tools.config import config
|
||||
from tools.translate import _
|
||||
import netsvc
|
||||
import logging
|
||||
import re
|
||||
import copy
|
||||
import os
|
||||
|
@ -35,6 +36,7 @@ from report.report_sxw import report_sxw, report_rml
|
|||
class actions(osv.osv):
|
||||
_name = 'ir.actions.actions'
|
||||
_table = 'ir_actions'
|
||||
_order = 'name'
|
||||
_columns = {
|
||||
'name': fields.char('Action Name', required=True, size=64),
|
||||
'type': fields.char('Action Type', required=True, size=32,readonly=True),
|
||||
|
@ -53,11 +55,15 @@ class report_xml(osv.osv):
|
|||
for report in self.browse(cursor, user, ids, context=context):
|
||||
data = report[name + '_data']
|
||||
if not data and report[name[:-8]]:
|
||||
fp = None
|
||||
try:
|
||||
fp = tools.file_open(report[name[:-8]], mode='rb')
|
||||
data = fp.read()
|
||||
except:
|
||||
data = False
|
||||
finally:
|
||||
if fp:
|
||||
fp.close()
|
||||
res[report.id] = data
|
||||
return res
|
||||
|
||||
|
@ -97,6 +103,7 @@ class report_xml(osv.osv):
|
|||
_name = 'ir.actions.report.xml'
|
||||
_table = 'ir_act_report_xml'
|
||||
_sequence = 'ir_actions_id_seq'
|
||||
_order = 'name'
|
||||
_columns = {
|
||||
'name': fields.char('Name', size=64, required=True, translate=True),
|
||||
'model': fields.char('Object', size=64, required=True),
|
||||
|
@ -143,19 +150,24 @@ class act_window(osv.osv):
|
|||
_name = 'ir.actions.act_window'
|
||||
_table = 'ir_act_window'
|
||||
_sequence = 'ir_actions_id_seq'
|
||||
_order = 'name'
|
||||
|
||||
def _check_model(self, cr, uid, ids, context={}):
|
||||
def _check_model(self, cr, uid, ids, context=None):
|
||||
for action in self.browse(cr, uid, ids, context):
|
||||
if not self.pool.get(action.res_model):
|
||||
return False
|
||||
if action.src_model and not self.pool.get(action.src_model):
|
||||
return False
|
||||
return True
|
||||
|
||||
def _invalid_model_msg(self, cr, uid, ids, context=None):
|
||||
return _('Invalid model name in the action definition.')
|
||||
|
||||
_constraints = [
|
||||
(_check_model, 'Invalid model name in the action definition.', ['res_model','src_model'])
|
||||
(_check_model, _invalid_model_msg, ['res_model','src_model'])
|
||||
]
|
||||
|
||||
def _views_get_fnc(self, cr, uid, ids, name, arg, context={}):
|
||||
def _views_get_fnc(self, cr, uid, ids, name, arg, context=None):
|
||||
res={}
|
||||
for act in self.browse(cr, uid, ids):
|
||||
res[act.id]=[(view.view_id.id, view.view_mode) for view in act.view_ids]
|
||||
|
@ -171,30 +183,34 @@ class act_window(osv.osv):
|
|||
res[act.id].append((False, t))
|
||||
return res
|
||||
|
||||
def _search_view(self, cr, uid, ids, name, arg, context={}):
|
||||
def _search_view(self, cr, uid, ids, name, arg, context=None):
|
||||
res = {}
|
||||
def encode(s):
|
||||
if isinstance(s, unicode):
|
||||
return s.encode('utf8')
|
||||
return s
|
||||
for act in self.browse(cr, uid, ids):
|
||||
for act in self.browse(cr, uid, ids, context=context):
|
||||
fields_from_fields_get = self.pool.get(act.res_model).fields_get(cr, uid, context=context)
|
||||
search_view_id = False
|
||||
if act.search_view_id:
|
||||
search_view_id = act.search_view_id.id
|
||||
else:
|
||||
res_view = self.pool.get('ir.ui.view').search(cr, uid, [('model','=',act.res_model),('type','=','search'),('inherit_id','=',False)])
|
||||
res_view = self.pool.get('ir.ui.view').search(cr, uid,
|
||||
[('model','=',act.res_model),('type','=','search'),
|
||||
('inherit_id','=',False)], context=context)
|
||||
if res_view:
|
||||
search_view_id = res_view[0]
|
||||
if search_view_id:
|
||||
field_get = self.pool.get(act.res_model).fields_view_get(cr, uid, search_view_id, 'search', context)
|
||||
field_get = self.pool.get(act.res_model).fields_view_get(cr, uid, search_view_id,
|
||||
'search', context)
|
||||
fields_from_fields_get.update(field_get['fields'])
|
||||
field_get['fields'] = fields_from_fields_get
|
||||
res[act.id] = str(field_get)
|
||||
else:
|
||||
def process_child(node, new_node, doc):
|
||||
for child in node.childNodes:
|
||||
if child.localName=='field' and child.hasAttribute('select') and child.getAttribute('select')=='1':
|
||||
if child.localName=='field' and child.hasAttribute('select') \
|
||||
and child.getAttribute('select')=='1':
|
||||
if child.childNodes:
|
||||
fld = doc.createElement('field')
|
||||
for attr in child.attributes.keys():
|
||||
|
@ -218,7 +234,7 @@ class act_window(osv.osv):
|
|||
res[act.id] = str(form_arch)
|
||||
return res
|
||||
|
||||
def _get_help_status(self, cr, uid, ids, name, arg, context={}):
|
||||
def _get_help_status(self, cr, uid, ids, name, arg, context=None):
|
||||
activate_tips = self.pool.get('res.users').browse(cr, uid, uid).menu_tips
|
||||
return dict([(id, activate_tips) for id in ids])
|
||||
|
||||
|
@ -253,7 +269,8 @@ class act_window(osv.osv):
|
|||
'search_view' : fields.function(_search_view, type='text', method=True, string='Search View'),
|
||||
'menus': fields.char('Menus', size=4096),
|
||||
'help': fields.text('Action description',
|
||||
help='Optional help text for the users with a description of the target view, such as its usage and purpose.'),
|
||||
help='Optional help text for the users with a description of the target view, such as its usage and purpose.',
|
||||
translate=True),
|
||||
'display_menu_tip':fields.function(_get_help_status, type='boolean', method=True, string='Display Menu Tips',
|
||||
help='It gives the status if the tip has to be displayed or not when a user executes an action'),
|
||||
'multi': fields.boolean('Action on Multiple Doc.', help="If set to true, the action will not be displayed on the right toolbar of a form view"),
|
||||
|
@ -271,6 +288,19 @@ class act_window(osv.osv):
|
|||
'multi': False,
|
||||
}
|
||||
|
||||
def for_xml_id(self, cr, uid, module, xml_id, context=None):
|
||||
""" Returns the act_window object created for the provided xml_id
|
||||
|
||||
:param module: the module the act_window originates in
|
||||
:param xml_id: the namespace-less id of the action (the @id
|
||||
attribute from the XML file)
|
||||
:return: A read() view of the ir.actions.act_window
|
||||
"""
|
||||
dataobj = self.pool.get('ir.model.data')
|
||||
data_id = dataobj._get_id (cr, 1, module, xml_id)
|
||||
res_id = dataobj.browse(cr, uid, data_id, context).res_id
|
||||
return self.read(cr, uid, res_id, [], context)
|
||||
|
||||
act_window()
|
||||
|
||||
class act_window_view(osv.osv):
|
||||
|
@ -301,6 +331,7 @@ class act_wizard(osv.osv):
|
|||
_inherit = 'ir.actions.actions'
|
||||
_table = 'ir_act_wizard'
|
||||
_sequence = 'ir_actions_id_seq'
|
||||
_order = 'name'
|
||||
_columns = {
|
||||
'name': fields.char('Wizard Info', size=64, required=True, translate=True),
|
||||
'type': fields.char('Action Type', size=32, required=True),
|
||||
|
@ -319,6 +350,7 @@ class act_url(osv.osv):
|
|||
_name = 'ir.actions.url'
|
||||
_table = 'ir_act_url'
|
||||
_sequence = 'ir_actions_id_seq'
|
||||
_order = 'name'
|
||||
_columns = {
|
||||
'name': fields.char('Action Name', size=64, translate=True),
|
||||
'type': fields.char('Action Type', size=32, required=True),
|
||||
|
@ -335,7 +367,7 @@ class act_url(osv.osv):
|
|||
}
|
||||
act_url()
|
||||
|
||||
def model_get(self, cr, uid, context={}):
|
||||
def model_get(self, cr, uid, context=None):
|
||||
wkf_pool = self.pool.get('workflow')
|
||||
ids = wkf_pool.search(cr, uid, [])
|
||||
osvs = wkf_pool.read(cr, uid, ids, ['osv'])
|
||||
|
@ -380,7 +412,7 @@ server_object_lines()
|
|||
#
|
||||
class actions_server(osv.osv):
|
||||
|
||||
def _select_signals(self, cr, uid, context={}):
|
||||
def _select_signals(self, cr, uid, context=None):
|
||||
cr.execute("SELECT distinct w.osv, t.signal FROM wkf w, wkf_activity a, wkf_transition t \
|
||||
WHERE w.id = a.wkf_id AND t.act_from = a.id OR t.act_to = a.id AND t.signal!='' \
|
||||
AND t.signal NOT IN (null, NULL)")
|
||||
|
@ -392,13 +424,13 @@ class actions_server(osv.osv):
|
|||
res.append(line)
|
||||
return res
|
||||
|
||||
def _select_objects(self, cr, uid, context={}):
|
||||
def _select_objects(self, cr, uid, context=None):
|
||||
model_pool = self.pool.get('ir.model')
|
||||
ids = model_pool.search(cr, uid, [('name','not ilike','.')])
|
||||
res = model_pool.read(cr, uid, ids, ['model', 'name'])
|
||||
return [(r['model'], r['name']) for r in res] + [('','')]
|
||||
|
||||
def change_object(self, cr, uid, ids, copy_object, state, context={}):
|
||||
def change_object(self, cr, uid, ids, copy_object, state, context=None):
|
||||
if state == 'object_copy':
|
||||
model_pool = self.pool.get('ir.model')
|
||||
model = copy_object.split(',')[0]
|
||||
|
@ -413,7 +445,7 @@ class actions_server(osv.osv):
|
|||
_name = 'ir.actions.server'
|
||||
_table = 'ir_act_server'
|
||||
_sequence = 'ir_actions_id_seq'
|
||||
_order = 'sequence'
|
||||
_order = 'sequence,name'
|
||||
_columns = {
|
||||
'name': fields.char('Action Name', required=True, size=64, help="Easy to Refer action by name e.g. One Sales Order -> Many Invoices", translate=True),
|
||||
'condition' : fields.char('Condition', size=256, required=True, help="Condition that is to be tested before action is executed, e.g. object.list_price > object.cost_price"),
|
||||
|
@ -469,7 +501,7 @@ class actions_server(osv.osv):
|
|||
}
|
||||
|
||||
def get_email(self, cr, uid, action, context):
|
||||
logger = netsvc.Logger()
|
||||
logger = logging.getLogger('Workflow')
|
||||
obj_pool = self.pool.get(action.model_id.model)
|
||||
id = context.get('active_id')
|
||||
obj = obj_pool.browse(cr, uid, id)
|
||||
|
@ -484,13 +516,13 @@ class actions_server(osv.osv):
|
|||
for field in fields:
|
||||
try:
|
||||
obj = getattr(obj, field)
|
||||
except Exception,e :
|
||||
logger.notifyChannel('Workflow', netsvc.LOG_ERROR, 'Failed to parse : %s' % (field))
|
||||
except Exception:
|
||||
logger.exception('Failed to parse: %s', field)
|
||||
|
||||
return obj
|
||||
|
||||
def get_mobile(self, cr, uid, action, context):
|
||||
logger = netsvc.Logger()
|
||||
logger = logging.getLogger('Workflow')
|
||||
obj_pool = self.pool.get(action.model_id.model)
|
||||
id = context.get('active_id')
|
||||
obj = obj_pool.browse(cr, uid, id)
|
||||
|
@ -505,15 +537,14 @@ class actions_server(osv.osv):
|
|||
for field in fields:
|
||||
try:
|
||||
obj = getattr(obj, field)
|
||||
except Exception,e :
|
||||
logger.notifyChannel('Workflow', netsvc.LOG_ERROR, 'Failed to parse : %s' % (field))
|
||||
except Exception:
|
||||
logger.exception('Failed to parse: %s', field)
|
||||
|
||||
return obj
|
||||
|
||||
def merge_message(self, cr, uid, keystr, action, context=None):
|
||||
if context is None:
|
||||
context = {}
|
||||
logger = netsvc.Logger()
|
||||
def merge(match):
|
||||
obj_pool = self.pool.get(action.model_id.model)
|
||||
id = context.get('active_id')
|
||||
|
@ -543,7 +574,7 @@ class actions_server(osv.osv):
|
|||
|
||||
# FIXME: refactor all the eval() calls in run()!
|
||||
def run(self, cr, uid, ids, context=None):
|
||||
logger = netsvc.Logger()
|
||||
logger = logging.getLogger(self._name)
|
||||
if context is None:
|
||||
context = {}
|
||||
for action in self.browse(cr, uid, ids, context):
|
||||
|
@ -591,18 +622,19 @@ class actions_server(osv.osv):
|
|||
pass
|
||||
|
||||
if not address:
|
||||
logger.notifyChannel('email', netsvc.LOG_INFO, 'Partner Email address not Specified!')
|
||||
logger.info('Partner Email address not Specified!')
|
||||
continue
|
||||
if not user:
|
||||
logger.info('Email-From address not Specified at server!')
|
||||
raise osv.except_osv(_('Error'), _("Please specify server option --email-from !"))
|
||||
|
||||
subject = self.merge_message(cr, uid, action.subject, action, context)
|
||||
body = self.merge_message(cr, uid, action.message, action, context)
|
||||
|
||||
if tools.email_send(user, [address], subject, body, debug=False, subtype='html') == True:
|
||||
logger.notifyChannel('email', netsvc.LOG_INFO, 'Email successfully send to : %s' % (address))
|
||||
logger.info('Email successfully sent to: %s', address)
|
||||
else:
|
||||
logger.notifyChannel('email', netsvc.LOG_ERROR, 'Failed to send email to : %s' % (address))
|
||||
logger.warning('Failed to send email to: %s', address)
|
||||
|
||||
if action.state == 'trigger':
|
||||
wf_service = netsvc.LocalService("workflow")
|
||||
|
@ -616,7 +648,7 @@ class actions_server(osv.osv):
|
|||
#TODO: set the user and password from the system
|
||||
# for the sms gateway user / password
|
||||
# USE smsclient module from extra-addons
|
||||
logger.notifyChannel('sms', netsvc.LOG_ERROR, 'SMS Facility has not been implemented yet. Use smsclient module!')
|
||||
logger.warning('SMS Facility has not been implemented yet. Use smsclient module!')
|
||||
|
||||
if action.state == 'other':
|
||||
res = []
|
||||
|
@ -771,7 +803,7 @@ class ir_actions_todo(osv.osv):
|
|||
'sequence': 10,
|
||||
'restart': 'onskip',
|
||||
}
|
||||
_order="sequence,id"
|
||||
_order="sequence,name,id"
|
||||
|
||||
def action_launch(self, cr, uid, ids, context=None):
|
||||
""" Launch Action of Wizard"""
|
||||
|
|
|
@ -24,16 +24,29 @@ from osv.orm import except_orm
|
|||
import tools
|
||||
|
||||
class ir_attachment(osv.osv):
|
||||
def check(self, cr, uid, ids, mode, context=None):
|
||||
def check(self, cr, uid, ids, mode, context=None, values=None):
|
||||
"""Restricts the access to an ir.attachment, according to referred model
|
||||
In the 'document' module, it is overriden to relax this hard rule, since
|
||||
more complex ones apply there.
|
||||
"""
|
||||
if not ids:
|
||||
return
|
||||
ima = self.pool.get('ir.model.access')
|
||||
if isinstance(ids, (int, long)):
|
||||
ids = [ids]
|
||||
cr.execute('select distinct res_model from ir_attachment where id IN %s', (tuple(ids),))
|
||||
for obj in cr.fetchall():
|
||||
if obj[0]:
|
||||
ima.check(cr, uid, obj[0], mode, context=context)
|
||||
res_ids = {}
|
||||
if ids:
|
||||
if isinstance(ids, (int, long)):
|
||||
ids = [ids]
|
||||
cr.execute('SELECT DISTINCT res_model, res_id FROM ir_attachment WHERE id = ANY (%s)', (ids,))
|
||||
for rmod, rid in cr.fetchall():
|
||||
if not (rmod and rid):
|
||||
continue
|
||||
res_ids.setdefault(rmod,[]).append(rid)
|
||||
if values:
|
||||
if 'res_model' in values and 'res_id' in values:
|
||||
res_ids.setdefault(values['res_model'],[]).append(values['res_id'])
|
||||
|
||||
for model, mids in res_ids.items():
|
||||
self.pool.get(model).check_access_rule(cr, uid, mids, mode, context=context)
|
||||
|
||||
def search(self, cr, uid, args, offset=0, limit=None, order=None,
|
||||
context=None, count=False):
|
||||
|
@ -64,7 +77,7 @@ class ir_attachment(osv.osv):
|
|||
return super(ir_attachment, self).read(cr, uid, ids, fields_to_read, context, load)
|
||||
|
||||
def write(self, cr, uid, ids, vals, context=None):
|
||||
self.check(cr, uid, ids, 'write', context=context)
|
||||
self.check(cr, uid, ids, 'write', context=context, values=vals)
|
||||
return super(ir_attachment, self).write(cr, uid, ids, vals, context)
|
||||
|
||||
def copy(self, cr, uid, id, default=None, context=None):
|
||||
|
@ -76,15 +89,12 @@ class ir_attachment(osv.osv):
|
|||
return super(ir_attachment, self).unlink(cr, uid, ids, context)
|
||||
|
||||
def create(self, cr, uid, values, context=None):
|
||||
if 'res_model' in values and values['res_model'] != '':
|
||||
self.pool.get('ir.model.access').check(cr, uid, values['res_model'], 'create', context=context)
|
||||
self.check(cr, uid, [], mode='create', context=context, values=values)
|
||||
return super(ir_attachment, self).create(cr, uid, values, context)
|
||||
|
||||
def action_get(self, cr, uid, context=None):
|
||||
dataobj = self.pool.get('ir.model.data')
|
||||
data_id = dataobj._get_id(cr, 1, 'base', 'action_attachment')
|
||||
res_id = dataobj.browse(cr, uid, data_id, context).res_id
|
||||
return self.pool.get('ir.actions.act_window').read(cr, uid, res_id, [], context)
|
||||
return self.pool.get('ir.actions.act_window').for_xml_id(
|
||||
cr, uid, 'base', 'action_attachment', context=context)
|
||||
|
||||
def _name_get_resname(self, cr, uid, ids, object,method, context):
|
||||
data = {}
|
||||
|
@ -114,11 +124,11 @@ class ir_attachment(osv.osv):
|
|||
'url': fields.char('Url', size=512, oldname="link"),
|
||||
'type': fields.selection(
|
||||
[ ('url','URL'), ('binary','Binary'), ],
|
||||
'Type', help="Binary File or external URL", required=True),
|
||||
'Type', help="Binary File or external URL", required=True, change_default=True),
|
||||
|
||||
'create_date': fields.datetime('Date Created', readonly=True),
|
||||
'create_uid': fields.many2one('res.users', 'Owner', readonly=True),
|
||||
'company_id': fields.many2one('res.company', 'Company'),
|
||||
'company_id': fields.many2one('res.company', 'Company', change_default=True),
|
||||
}
|
||||
|
||||
_defaults = {
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
"""
|
||||
A module to store some configuration parameters relative to a whole database.
|
||||
"""
|
||||
|
||||
from osv import osv,fields
|
||||
import uuid
|
||||
import datetime
|
||||
from tools import misc
|
||||
|
||||
"""
|
||||
A dictionary holding some configuration parameters to be initialized when the database is created.
|
||||
"""
|
||||
_default_parameters = {
|
||||
"database.uuid": lambda: str(uuid.uuid1()),
|
||||
"database.create_date": lambda: datetime.datetime.now().strftime(misc.DEFAULT_SERVER_DATETIME_FORMAT),
|
||||
}
|
||||
|
||||
class ir_config_parameter(osv.osv):
|
||||
""" An osv to old configuration parameters for a given database.
|
||||
|
||||
To be short, it's just a global dictionary of strings stored in a table. """
|
||||
|
||||
_name = 'ir.config_parameter'
|
||||
|
||||
_columns = {
|
||||
# The key of the configuration parameter.
|
||||
'key': fields.char('Key', size=256, required=True, select=1),
|
||||
# The value of the configuration parameter.
|
||||
'value': fields.text('Value', required=True),
|
||||
}
|
||||
|
||||
_sql_constraints = [
|
||||
('key_uniq', 'unique (key)', 'Key must be unique.')
|
||||
]
|
||||
|
||||
def init(self, cr):
|
||||
"""
|
||||
Initializes the parameters listed in _default_parameters.
|
||||
"""
|
||||
for key, func in _default_parameters.iteritems():
|
||||
ids = self.search(cr, 1, [('key','=',key)])
|
||||
if not ids:
|
||||
self.set_param(cr, 1, key, func())
|
||||
|
||||
def get_param(self, cr, uid, key, context=None):
|
||||
""" Get the value of a parameter.
|
||||
|
||||
@param key: The key of the parameter.
|
||||
@type key: string
|
||||
@return: The value of the parameter, False if it does not exist.
|
||||
@rtype: string
|
||||
"""
|
||||
ids = self.search(cr, uid, [('key','=',key)], context=context)
|
||||
if not ids:
|
||||
return False
|
||||
param = self.browse(cr, uid, ids[0], context=context)
|
||||
value = param.value
|
||||
return value
|
||||
|
||||
def set_param(self, cr, uid, key, value, context=None):
|
||||
""" Set the value of a parameter.
|
||||
|
||||
@param key: The key of the parameter.
|
||||
@type key: string
|
||||
@param value: The value of the parameter.
|
||||
@type value: string
|
||||
@return: Return the previous value of the parameter of False if it did
|
||||
not existed.
|
||||
@rtype: string
|
||||
"""
|
||||
ids = self.search(cr, uid, [('key','=',key)], context=context)
|
||||
if ids:
|
||||
param = self.browse(cr, uid, ids[0], context=context)
|
||||
old = param.value
|
||||
self.write(cr, uid, ids, {'value': value}, context=context)
|
||||
return old
|
||||
else:
|
||||
self.create(cr, uid, {'key': key, 'value': value}, context=context)
|
||||
return False
|
||||
|
||||
ir_config_parameter()
|
|
@ -2,20 +2,19 @@
|
|||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>). All Rights Reserved
|
||||
# $Id$
|
||||
# Copyright (C) 2004-TODAY 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 General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
@ -43,6 +42,7 @@ _intervalTypes = {
|
|||
|
||||
class ir_cron(osv.osv, netsvc.Agent):
|
||||
_name = "ir.cron"
|
||||
_order = 'name'
|
||||
_columns = {
|
||||
'name': fields.char('Name', size=60, required=True),
|
||||
'user_id': fields.many2one('res.users', 'User', required=True),
|
||||
|
@ -124,7 +124,7 @@ class ir_cron(osv.osv, netsvc.Agent):
|
|||
cr.commit()
|
||||
|
||||
|
||||
cr.execute('select min(nextcall) as min_next_call from ir_cron where numbercall<>0 and active and nextcall>=now()')
|
||||
cr.execute('select min(nextcall) as min_next_call from ir_cron where numbercall<>0 and active')
|
||||
next_call = cr.dictfetchone()['min_next_call']
|
||||
if next_call:
|
||||
next_call = time.mktime(time.strptime(next_call, '%Y-%m-%d %H:%M:%S'))
|
||||
|
@ -145,7 +145,8 @@ class ir_cron(osv.osv, netsvc.Agent):
|
|||
|
||||
def restart(self, dbname):
|
||||
self.cancel(dbname)
|
||||
self._poolJobs(dbname)
|
||||
# Reschedule cron processing job asap, but not in the current thread
|
||||
self.setAlarm(self._poolJobs, time.time(), dbname, dbname)
|
||||
|
||||
def create(self, cr, uid, vals, context=None):
|
||||
res = super(ir_cron, self).create(cr, uid, vals, context=context)
|
||||
|
|
|
@ -24,9 +24,10 @@ from osv import fields,osv
|
|||
|
||||
class ir_exports(osv.osv):
|
||||
_name = "ir.exports"
|
||||
_order = 'name'
|
||||
_columns = {
|
||||
'name': fields.char('Export Name', size=128),
|
||||
'resource': fields.char('Resource', size=128),
|
||||
'resource': fields.char('Resource', size=128, select=True),
|
||||
'export_fields': fields.one2many('ir.exports.line', 'export_id',
|
||||
'Export ID'),
|
||||
}
|
||||
|
@ -35,6 +36,7 @@ ir_exports()
|
|||
|
||||
class ir_exports_line(osv.osv):
|
||||
_name = 'ir.exports.line'
|
||||
_order = 'id'
|
||||
_columns = {
|
||||
'name': fields.char('Field Name', size=64),
|
||||
'export_id': fields.many2one('ir.exports', 'Export', select=True, ondelete='cascade'),
|
||||
|
|
|
@ -60,7 +60,7 @@ class ir_filters(osv.osv):
|
|||
'user_id':fields.many2one('res.users', 'User', help='False means for every user'),
|
||||
'domain': fields.text('Domain Value', required=True),
|
||||
'context': fields.text('Context Value', required=True),
|
||||
'model_id': fields.selection(_list_all_models, 'Object', required=True),
|
||||
'model_id': fields.selection(_list_all_models, 'Object', size=64, required=True),
|
||||
}
|
||||
|
||||
ir_filters()
|
||||
|
|
|
@ -19,14 +19,16 @@
|
|||
#
|
||||
##############################################################################
|
||||
import logging
|
||||
import re
|
||||
import time
|
||||
from operator import itemgetter
|
||||
|
||||
from osv import fields,osv
|
||||
import ir, re
|
||||
import ir
|
||||
import netsvc
|
||||
from osv.orm import except_orm, browse_record
|
||||
|
||||
import time
|
||||
import tools
|
||||
from tools.safe_eval import safe_eval as eval
|
||||
from tools import config
|
||||
from tools.translate import _
|
||||
import pooler
|
||||
|
@ -43,7 +45,7 @@ def _get_fields_type(self, cr, uid, context=None):
|
|||
class ir_model(osv.osv):
|
||||
_name = 'ir.model'
|
||||
_description = "Objects"
|
||||
_rec_name = 'name'
|
||||
_order = 'model'
|
||||
|
||||
def _is_osv_memory(self, cr, uid, ids, field_name, arg, context=None):
|
||||
models = self.browse(cr, uid, ids, context=context)
|
||||
|
@ -57,7 +59,7 @@ class ir_model(osv.osv):
|
|||
return []
|
||||
field, operator, value = domain[0]
|
||||
if operator not in ['=', '!=']:
|
||||
raise osv.except_osv('Invalid search criterions','The osv_memory field can only be compared with = and != operator.')
|
||||
raise osv.except_osv(_('Invalid search criterions'), _('The osv_memory field can only be compared with = and != operator.'))
|
||||
value = bool(value) if operator == '=' else not bool(value)
|
||||
all_model_ids = self.search(cr, uid, [], context=context)
|
||||
is_osv_mem = self._is_osv_memory(cr, uid, all_model_ids, 'osv_memory', arg=None, context=context)
|
||||
|
@ -75,12 +77,14 @@ class ir_model(osv.osv):
|
|||
fnct_search=_search_osv_memory,
|
||||
help="Indicates whether this object model lives in memory only, i.e. is not persisted (osv.osv_memory)")
|
||||
}
|
||||
|
||||
_defaults = {
|
||||
'model': lambda *a: 'x_',
|
||||
'state': lambda self,cr,uid,ctx={}: (ctx and ctx.get('manual',False)) and 'manual' or 'base',
|
||||
'state': lambda self,cr,uid,ctx=None: (ctx and ctx.get('manual',False)) and 'manual' or 'base',
|
||||
}
|
||||
def _check_model_name(self, cr, uid, ids):
|
||||
for model in self.browse(cr, uid, ids):
|
||||
|
||||
def _check_model_name(self, cr, uid, ids, context=None):
|
||||
for model in self.browse(cr, uid, ids, context=context):
|
||||
if model.state=='manual':
|
||||
if not model.model.startswith('x_'):
|
||||
return False
|
||||
|
@ -88,8 +92,10 @@ class ir_model(osv.osv):
|
|||
return False
|
||||
return True
|
||||
|
||||
def _model_name_msg(self, cr, uid, ids, context=None):
|
||||
return _('The Object name must start with x_ and not contain any special character !')
|
||||
_constraints = [
|
||||
(_check_model_name, 'The Object name must start with x_ and not contain any special character !', ['model']),
|
||||
(_check_model_name, _model_name_msg, ['model']),
|
||||
]
|
||||
|
||||
# overridden to allow searching both on model name (model field)
|
||||
|
@ -148,42 +154,78 @@ class ir_model_fields(osv.osv):
|
|||
_description = "Fields"
|
||||
_columns = {
|
||||
'name': fields.char('Name', required=True, size=64, select=1),
|
||||
'model': fields.char('Object Name', size=64, required=True, select=1),
|
||||
'relation': fields.char('Object Relation', size=64),
|
||||
'relation_field': fields.char('Relation Field', size=64),
|
||||
'model_id': fields.many2one('ir.model', 'Object ID', required=True, select=True, ondelete='cascade'),
|
||||
'model': fields.char('Object Name', size=64, required=True, select=1,
|
||||
help="The technical name of the model this field belongs to"),
|
||||
'relation': fields.char('Object Relation', size=64,
|
||||
help="For relationship fields, the technical name of the target model"),
|
||||
'relation_field': fields.char('Relation Field', size=64,
|
||||
help="For one2many fields, the field on the target model that implement the opposite many2one relationship"),
|
||||
'model_id': fields.many2one('ir.model', 'Model', required=True, select=True, ondelete='cascade',
|
||||
help="The model this field belongs to"),
|
||||
'field_description': fields.char('Field Label', required=True, size=256),
|
||||
'ttype': fields.selection(_get_fields_type, 'Field Type',size=64, required=True),
|
||||
'selection': fields.char('Field Selection',size=128),
|
||||
'selection': fields.char('Selection Options',size=128, help="List of options for a selection field, "
|
||||
"specified as a Python expression defining a list of (key, label) pairs. "
|
||||
"For example: [('blue','Blue'),('yellow','Yellow')]"),
|
||||
'required': fields.boolean('Required'),
|
||||
'readonly': fields.boolean('Readonly'),
|
||||
'select_level': fields.selection([('0','Not Searchable'),('1','Always Searchable'),('2','Advanced Search')],'Searchable', required=True),
|
||||
'translate': fields.boolean('Translate'),
|
||||
'select_level': fields.selection([('0','Not Searchable'),('1','Always Searchable'),('2','Advanced Search (deprecated)')],'Searchable', required=True),
|
||||
'translate': fields.boolean('Translate', help="Whether values for this field can be translated (enables the translation mechanism for that field)"),
|
||||
'size': fields.integer('Size'),
|
||||
'state': fields.selection([('manual','Custom Field'),('base','Base Field')],'Type', required=True, readonly=True, select=1),
|
||||
'on_delete': fields.selection([('cascade','Cascade'),('set null','Set NULL')], 'On delete', help='On delete property for many2one fields'),
|
||||
'domain': fields.char('Domain', size=256),
|
||||
'domain': fields.char('Domain', size=256, help="The optional domain to restrict possible values for relationship fields, "
|
||||
"specified as a Python expression defining a list of triplets. "
|
||||
"For example: [('color','=','red')]"),
|
||||
'groups': fields.many2many('res.groups', 'ir_model_fields_group_rel', 'field_id', 'group_id', 'Groups'),
|
||||
'view_load': fields.boolean('View Auto-Load'),
|
||||
'selectable': fields.boolean('Selectable'),
|
||||
}
|
||||
_rec_name='field_description'
|
||||
_defaults = {
|
||||
'view_load': lambda *a: 0,
|
||||
'selection': lambda *a: "[]",
|
||||
'domain': lambda *a: "[]",
|
||||
'name': lambda *a: 'x_',
|
||||
'view_load': 0,
|
||||
'selection': "",
|
||||
'domain': "[]",
|
||||
'name': 'x_',
|
||||
'state': lambda self,cr,uid,ctx={}: (ctx and ctx.get('manual',False)) and 'manual' or 'base',
|
||||
'on_delete': lambda *a: 'set null',
|
||||
'select_level': lambda *a: '0',
|
||||
'size': lambda *a: 64,
|
||||
'field_description': lambda *a: '',
|
||||
'selectable': lambda *a: 1,
|
||||
'on_delete': 'set null',
|
||||
'select_level': '0',
|
||||
'size': 64,
|
||||
'field_description': '',
|
||||
'selectable': 1,
|
||||
}
|
||||
_order = "id"
|
||||
_order = "name"
|
||||
|
||||
def _check_selection(self, cr, uid, selection, context=None):
|
||||
try:
|
||||
selection_list = eval(selection)
|
||||
except Exception:
|
||||
logging.getLogger('ir.model').warning('Invalid selection list definition for fields.selection', exc_info=True)
|
||||
raise except_orm(_('Error'),
|
||||
_("The Selection Options expression is not a valid Pythonic expression." \
|
||||
"Please provide an expression in the [('key','Label'), ...] format."))
|
||||
|
||||
check = True
|
||||
if not (isinstance(selection_list, list) and selection_list):
|
||||
check = False
|
||||
else:
|
||||
for item in selection_list:
|
||||
if not (isinstance(item, (tuple,list)) and len(item) == 2):
|
||||
check = False
|
||||
break
|
||||
|
||||
if not check:
|
||||
raise except_orm(_('Error'),
|
||||
_("The Selection Options expression is must be in the [('key','Label'), ...] format!"))
|
||||
return True
|
||||
|
||||
def _size_gt_zero_msg(self, cr, user, ids, context=None):
|
||||
return _('Size of the field can never be less than 1 !')
|
||||
|
||||
_sql_constraints = [
|
||||
('size_gt_zero', 'CHECK (size>0)', 'Size of the field can never be less than 1 !'),
|
||||
('size_gt_zero', 'CHECK (size>0)',_size_gt_zero_msg ),
|
||||
]
|
||||
|
||||
def unlink(self, cr, user, ids, context=None):
|
||||
for field in self.browse(cr, user, ids, context):
|
||||
if field.state <> 'manual':
|
||||
|
@ -203,13 +245,17 @@ class ir_model_fields(osv.osv):
|
|||
context = {}
|
||||
if context and context.get('manual',False):
|
||||
vals['state'] = 'manual'
|
||||
if vals.get('ttype', False) == 'selection':
|
||||
if not vals.get('selection',False):
|
||||
raise except_orm(_('Error'), _('For selection fields, the Selection Options must be given!'))
|
||||
self._check_selection(cr, user, vals['selection'], context=context)
|
||||
res = super(ir_model_fields,self).create(cr, user, vals, context)
|
||||
if vals.get('state','base') == 'manual':
|
||||
if not vals['name'].startswith('x_'):
|
||||
raise except_orm(_('Error'), _("Custom fields must have a name that starts with 'x_' !"))
|
||||
|
||||
if vals.get('relation',False) and not self.pool.get('ir.model').search(cr, user, [('model','=',vals['relation'])]):
|
||||
raise except_orm(_('Error'), _("Model %s Does not Exist !" % vals['relation']))
|
||||
raise except_orm(_('Error'), _("Model %s does not exist!") % vals['relation'])
|
||||
|
||||
if self.pool.get(vals['model']):
|
||||
self.pool.get(vals['model']).__init__(self.pool, cr)
|
||||
|
@ -220,13 +266,120 @@ class ir_model_fields(osv.osv):
|
|||
|
||||
return res
|
||||
|
||||
def write(self, cr, user, ids, vals, context=None):
|
||||
if context is None:
|
||||
context = {}
|
||||
if context and context.get('manual',False):
|
||||
vals['state'] = 'manual'
|
||||
|
||||
column_rename = None # if set, *one* column can be renamed here
|
||||
obj = None
|
||||
models_patch = {} # structs of (obj, [(field, prop, change_to),..])
|
||||
# data to be updated on the orm model
|
||||
|
||||
# static table of properties
|
||||
model_props = [ # (our-name, fields.prop, set_fn)
|
||||
('field_description', 'string', lambda a: a),
|
||||
('required', 'required', bool),
|
||||
('readonly', 'readonly', bool),
|
||||
('domain', '_domain', lambda a: a),
|
||||
('size', 'size', int),
|
||||
('on_delete', 'ondelete', str),
|
||||
('translate', 'translate', bool),
|
||||
('view_load', 'view_load', bool),
|
||||
('selectable', 'selectable', bool),
|
||||
('select_level', 'select', int),
|
||||
('selection', 'selection', eval),
|
||||
]
|
||||
|
||||
if vals and ids:
|
||||
checked_selection = False # need only check it once, so defer
|
||||
|
||||
for item in self.browse(cr, user, ids, context=context):
|
||||
if not (obj and obj._name == item.model):
|
||||
obj = self.pool.get(item.model)
|
||||
|
||||
if item.state != 'manual':
|
||||
raise except_orm(_('Error!'),
|
||||
_('Properties of base fields cannot be altered in this manner! '
|
||||
'Please modify them through Python code, '
|
||||
'preferably through a custom addon!'))
|
||||
|
||||
if item.ttype == 'selection' and 'selection' in vals \
|
||||
and not checked_selection:
|
||||
self._check_selection(cr, user, vals['selection'], context=context)
|
||||
checked_selection = True
|
||||
|
||||
final_name = item.name
|
||||
if 'name' in vals and vals['name'] != item.name:
|
||||
# We need to rename the column
|
||||
if column_rename:
|
||||
raise except_orm(_('Error!'), _('Can only rename one column at a time!'))
|
||||
if vals['name'] in obj._columns:
|
||||
raise except_orm(_('Error!'), _('Cannot rename column to %s, because that column already exists!') % vals['name'])
|
||||
if vals.get('state', 'base') == 'manual' and not vals['name'].startswith('x_'):
|
||||
raise except_orm(_('Error!'), _('New column name must still start with x_ , because it is a custom field!'))
|
||||
if '\'' in vals['name'] or '"' in vals['name'] or ';' in vals['name']:
|
||||
raise ValueError('Invalid character in column name')
|
||||
column_rename = (obj, (obj._table, item.name, vals['name']))
|
||||
final_name = vals['name']
|
||||
|
||||
if 'model_id' in vals and vals['model_id'] != item.model_id:
|
||||
raise except_orm(_("Error!"), _("Changing the model of a field is forbidden!"))
|
||||
|
||||
if 'ttype' in vals and vals['ttype'] != item.ttype:
|
||||
raise except_orm(_("Error!"), _("Changing the type of a column is not yet supported. "
|
||||
"Please drop it and create it again!"))
|
||||
|
||||
# We don't check the 'state', because it might come from the context
|
||||
# (thus be set for multiple fields) and will be ignored anyway.
|
||||
if obj:
|
||||
models_patch.setdefault(obj._name, (obj,[]))
|
||||
# find out which properties (per model) we need to update
|
||||
for field_name, field_property, set_fn in model_props:
|
||||
if field_name in vals:
|
||||
property_value = set_fn(vals[field_name])
|
||||
if getattr(obj._columns[item.name], field_property) != property_value:
|
||||
models_patch[obj._name][1].append((final_name, field_property, property_value))
|
||||
# our dict is ready here, but no properties are changed so far
|
||||
|
||||
# These shall never be written (modified)
|
||||
for column_name in ('model_id', 'model', 'state'):
|
||||
if column_name in vals:
|
||||
del vals[column_name]
|
||||
|
||||
res = super(ir_model_fields,self).write(cr, user, ids, vals, context=context)
|
||||
|
||||
if column_rename:
|
||||
cr.execute('ALTER TABLE "%s" RENAME COLUMN "%s" TO "%s"' % column_rename[1])
|
||||
# This is VERY risky, but let us have this feature:
|
||||
# we want to change the key of column in obj._columns dict
|
||||
col = column_rename[0]._columns.pop(column_rename[1][1]) # take object out, w/o copy
|
||||
column_rename[0]._columns[column_rename[1][2]] = col
|
||||
|
||||
if models_patch:
|
||||
# We have to update _columns of the model(s) and then call their
|
||||
# _auto_init to sync the db with the model. Hopefully, since write()
|
||||
# was called earlier, they will be in-sync before the _auto_init.
|
||||
# Anything we don't update in _columns now will be reset from
|
||||
# the model into ir.model.fields (db).
|
||||
ctx = context.copy()
|
||||
ctx.update({'select': vals.get('select_level','0'),'update_custom_fields':True})
|
||||
|
||||
for model_key, patch_struct in models_patch.items():
|
||||
obj = patch_struct[0]
|
||||
for col_name, col_prop, val in patch_struct[1]:
|
||||
setattr(obj._columns[col_name], col_prop, val)
|
||||
obj._auto_init(cr, ctx)
|
||||
return res
|
||||
|
||||
ir_model_fields()
|
||||
|
||||
class ir_model_access(osv.osv):
|
||||
_name = 'ir.model.access'
|
||||
_columns = {
|
||||
'name': fields.char('Name', size=64, required=True, select=True),
|
||||
'model_id': fields.many2one('ir.model', 'Object', required=True, domain=[('osv_memory','=', False)], select=True),
|
||||
'model_id': fields.many2one('ir.model', 'Object', required=True, domain=[('osv_memory','=', False)], select=True, ondelete='cascade'),
|
||||
'group_id': fields.many2one('res.groups', 'Group', ondelete='cascade', select=True),
|
||||
'perm_read': fields.boolean('Read Access'),
|
||||
'perm_write': fields.boolean('Write Access'),
|
||||
|
@ -317,14 +470,24 @@ class ir_model_access(osv.osv):
|
|||
r = cr.fetchone()[0]
|
||||
|
||||
if not r and raise_exception:
|
||||
cr.execute('''select
|
||||
g.name
|
||||
from
|
||||
ir_model_access a
|
||||
left join ir_model m on (a.model_id=m.id)
|
||||
left join res_groups g on (a.group_id=g.id)
|
||||
where
|
||||
m.model=%s and
|
||||
a.group_id is not null and perm_''' + mode, (model_name, ))
|
||||
groups = ', '.join(map(lambda x: x[0], cr.fetchall())) or '/'
|
||||
msgs = {
|
||||
'read': _('You can not read this document! (%s)'),
|
||||
'write': _('You can not write in this document! (%s)'),
|
||||
'create': _('You can not create this kind of document! (%s)'),
|
||||
'unlink': _('You can not delete this document! (%s)'),
|
||||
'read': _("You can not read this document (%s) ! Be sure your user belongs to one of these groups: %s."),
|
||||
'write': _("You can not write in this document (%s) ! Be sure your user belongs to one of these groups: %s."),
|
||||
'create': _("You can not create this document (%s) ! Be sure your user belongs to one of these groups: %s."),
|
||||
'unlink': _("You can not delete this document (%s) ! Be sure your user belongs to one of these groups: %s."),
|
||||
}
|
||||
|
||||
raise except_orm(_('AccessError'), msgs[mode] % model_name )
|
||||
raise except_orm(_('AccessError'), msgs[mode] % (model_name, groups) )
|
||||
return r
|
||||
|
||||
check = tools.cache()(check)
|
||||
|
@ -369,6 +532,7 @@ ir_model_access()
|
|||
class ir_model_data(osv.osv):
|
||||
_name = 'ir.model.data'
|
||||
__logger = logging.getLogger('addons.base.'+_name)
|
||||
_order = 'module,model,name'
|
||||
_columns = {
|
||||
'name': fields.char('XML Identifier', required=True, size=128, select=1),
|
||||
'model': fields.char('Object', required=True, size=64, select=1),
|
||||
|
@ -381,8 +545,8 @@ class ir_model_data(osv.osv):
|
|||
_defaults = {
|
||||
'date_init': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'),
|
||||
'date_update': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'),
|
||||
'noupdate': lambda *a: False,
|
||||
'module': lambda *a: ''
|
||||
'noupdate': False,
|
||||
'module': ''
|
||||
}
|
||||
_sql_constraints = [
|
||||
('module_name_uniq', 'unique(name, module)', 'You cannot have multiple records with the same id for the same module !'),
|
||||
|
@ -394,6 +558,12 @@ class ir_model_data(osv.osv):
|
|||
self.doinit = True
|
||||
self.unlink_mark = {}
|
||||
|
||||
def _auto_init(self, cr, context=None):
|
||||
super(ir_model_data, self)._auto_init(cr, context)
|
||||
cr.execute('SELECT indexname FROM pg_indexes WHERE indexname = \'ir_model_data_module_name_index\'')
|
||||
if not cr.fetchone():
|
||||
cr.execute('CREATE INDEX ir_model_data_module_name_index ON ir_model_data (module, name)')
|
||||
|
||||
@tools.cache()
|
||||
def _get_id(self, cr, uid, module, xml_id):
|
||||
"""Returns the id of the ir.model.data record corresponding to a given module and xml_id (cached) or raise a ValueError if not found"""
|
||||
|
@ -429,6 +599,10 @@ class ir_model_data(osv.osv):
|
|||
model_obj = self.pool.get(model)
|
||||
if not context:
|
||||
context = {}
|
||||
|
||||
# records created during module install should result in res.log entries that are already read!
|
||||
context = dict(context, res_log_read=True)
|
||||
|
||||
if xml_id and ('.' in xml_id):
|
||||
assert len(xml_id.split('.'))==2, _("'%s' contains too many dots. XML ids should not contain dots ! These are used to refer to other modules data, as in module.reference_id") % (xml_id)
|
||||
module, xml_id = xml_id.split('.')
|
||||
|
@ -437,18 +611,19 @@ class ir_model_data(osv.osv):
|
|||
action_id = False
|
||||
|
||||
if xml_id:
|
||||
cr.execute('select id,res_id from ir_model_data where module=%s and name=%s', (module,xml_id))
|
||||
cr.execute('''SELECT imd.id, imd.res_id, md.id
|
||||
FROM ir_model_data imd LEFT JOIN %s md ON (imd.res_id = md.id)
|
||||
WHERE imd.module=%%s AND imd.name=%%s''' % model_obj._table,
|
||||
(module, xml_id))
|
||||
results = cr.fetchall()
|
||||
for action_id2,res_id2 in results:
|
||||
cr.execute('select id from '+model_obj._table+' where id=%s', (res_id2,))
|
||||
result3 = cr.fetchone()
|
||||
if not result3:
|
||||
for imd_id2,res_id2,real_id2 in results:
|
||||
if not real_id2:
|
||||
self._get_id.clear_cache(cr.dbname, uid, module, xml_id)
|
||||
self.get_object_reference.clear_cache(cr.dbname, uid, module, xml_id)
|
||||
cr.execute('delete from ir_model_data where id=%s', (action_id2,))
|
||||
cr.execute('delete from ir_model_data where id=%s', (imd_id2,))
|
||||
res_id = False
|
||||
else:
|
||||
res_id,action_id = res_id2,action_id2
|
||||
res_id,action_id = res_id2,imd_id2
|
||||
|
||||
if action_id and res_id:
|
||||
model_obj.write(cr, uid, [res_id], values, context=context)
|
||||
|
@ -588,7 +763,7 @@ class ir_model_data(osv.osv):
|
|||
cr.commit()
|
||||
except Exception:
|
||||
cr.rollback()
|
||||
self.__logger.exception(
|
||||
self.__logger.warn(
|
||||
'Could not delete id: %d of model %s\nThere '
|
||||
'should be some relation that points to this '
|
||||
'resource\nYou should manually fix this and '
|
||||
|
|
|
@ -28,6 +28,7 @@ from tools.safe_eval import safe_eval as eval
|
|||
|
||||
class ir_rule(osv.osv):
|
||||
_name = 'ir.rule'
|
||||
_order = 'name'
|
||||
_MODES = ['read', 'write', 'create', 'unlink']
|
||||
|
||||
def _domain_force_get(self, cr, uid, ids, field_name, arg, context={}):
|
||||
|
@ -47,7 +48,7 @@ class ir_rule(osv.osv):
|
|||
res[rule.id] = False
|
||||
return res
|
||||
|
||||
def _check_model_obj(self, cr, uid, ids, context={}):
|
||||
def _check_model_obj(self, cr, uid, ids, context=None):
|
||||
return not any(isinstance(self.pool.get(rule.model_id.model), osv.osv_memory) for rule in self.browse(cr, uid, ids, context))
|
||||
|
||||
_columns = {
|
||||
|
|
|
@ -2,20 +2,19 @@
|
|||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>). All Rights Reserved
|
||||
# $Id$
|
||||
# Copyright (C) 2004-TODAY 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 General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
@ -26,6 +25,7 @@ import pooler
|
|||
|
||||
class ir_sequence_type(osv.osv):
|
||||
_name = 'ir.sequence.type'
|
||||
_order = 'name'
|
||||
_columns = {
|
||||
'name': fields.char('Name',size=64, required=True),
|
||||
'code': fields.char('Code',size=32, required=True),
|
||||
|
@ -38,6 +38,7 @@ def _code_get(self, cr, uid, context={}):
|
|||
|
||||
class ir_sequence(osv.osv):
|
||||
_name = 'ir.sequence'
|
||||
_order = 'name'
|
||||
_columns = {
|
||||
'name': fields.char('Name',size=64, required=True),
|
||||
'code': fields.selection(_code_get, 'Code',size=64, required=True),
|
||||
|
@ -74,10 +75,18 @@ class ir_sequence(osv.osv):
|
|||
|
||||
def get_id(self, cr, uid, sequence_id, test='id', context=None):
|
||||
assert test in ('code','id')
|
||||
cr.execute('SELECT id, number_next, prefix, suffix, padding FROM ir_sequence WHERE '+test+'=%s AND active=%s FOR UPDATE NOWAIT', (sequence_id, True))
|
||||
company_id = self.pool.get('res.users').read(cr, uid, uid, ['company_id'], context=context)['company_id'][0] or None
|
||||
cr.execute('''SELECT id, number_next, prefix, suffix, padding
|
||||
FROM ir_sequence
|
||||
WHERE %s=%%s
|
||||
AND active=true
|
||||
AND (company_id = %%s or company_id is NULL)
|
||||
ORDER BY company_id, id
|
||||
FOR UPDATE NOWAIT''' % test,
|
||||
(sequence_id, company_id))
|
||||
res = cr.dictfetchone()
|
||||
if res:
|
||||
cr.execute('UPDATE ir_sequence SET number_next=number_next+number_increment WHERE id=%s AND active=%s', (res['id'], True))
|
||||
cr.execute('UPDATE ir_sequence SET number_next=number_next+number_increment WHERE id=%s AND active=true', (res['id'],))
|
||||
if res['number_next']:
|
||||
return self._process(res['prefix']) + '%%0%sd' % res['padding'] % res['number_next'] + self._process(res['suffix'])
|
||||
else:
|
||||
|
|
|
@ -57,10 +57,14 @@ class ir_translation(osv.osv):
|
|||
_columns = {
|
||||
'name': fields.char('Field Name', size=128, required=True),
|
||||
'res_id': fields.integer('Resource ID', select=True),
|
||||
'lang': fields.selection(_get_language, string='Language', size=5),
|
||||
'lang': fields.selection(_get_language, string='Language', size=16),
|
||||
'type': fields.selection(TRANSLATION_TYPE, string='Type', size=16, select=True),
|
||||
'src': fields.text('Source'),
|
||||
'value': fields.text('Translation Value'),
|
||||
# These two columns map to ir_model_data.module and ir_model_data.name.
|
||||
# They are used to resolve the res_id above after loading is done.
|
||||
'module': fields.char('Module', size=64, help='Maps to the ir_model_data for which this translation is provided.'),
|
||||
'xml_id': fields.char('XML Id', size=128, help='Maps to the ir_model_data for which this translation is provided.'),
|
||||
}
|
||||
|
||||
def _auto_init(self, cr, context={}):
|
||||
|
@ -88,8 +92,6 @@ class ir_translation(osv.osv):
|
|||
cr.execute('CREATE INDEX ir_translation_ltn ON ir_translation (name, lang, type)')
|
||||
cr.commit()
|
||||
|
||||
|
||||
|
||||
@tools.cache(skiparg=3, multi='ids')
|
||||
def _get_ids(self, cr, uid, name, tt, lang, ids):
|
||||
translations = dict.fromkeys(ids, False)
|
||||
|
@ -138,7 +140,7 @@ class ir_translation(osv.osv):
|
|||
and source. All values passed to this method should be unicode (not byte strings),
|
||||
especially ``source``.
|
||||
|
||||
:param name: identification of the term to translate, such as field name
|
||||
:param name: identification of the term to translate, such as field name (optional if source is passed)
|
||||
:param types: single string defining type of term to translate (see ``type`` field on ir.translation), or sequence of allowed types (strings)
|
||||
:param lang: language code of the desired translation
|
||||
:param source: optional source term to translate (should be unicode)
|
||||
|
@ -153,19 +155,22 @@ class ir_translation(osv.osv):
|
|||
if isinstance(types, basestring):
|
||||
types = (types,)
|
||||
if source:
|
||||
cr.execute('select value ' \
|
||||
'from ir_translation ' \
|
||||
'where lang=%s ' \
|
||||
'and type in %s ' \
|
||||
'and name=%s ' \
|
||||
'and src=%s',
|
||||
(lang or '', types, tools.ustr(name), source))
|
||||
query = """SELECT value
|
||||
FROM ir_translation
|
||||
WHERE lang=%s
|
||||
AND type in %s
|
||||
AND src=%s"""
|
||||
params = (lang or '', types, tools.ustr(source))
|
||||
if name:
|
||||
query += " AND name=%s"
|
||||
params += (tools.ustr(name),)
|
||||
cr.execute(query, params)
|
||||
else:
|
||||
cr.execute('select value ' \
|
||||
'from ir_translation ' \
|
||||
'where lang=%s ' \
|
||||
'and type in %s ' \
|
||||
'and name=%s',
|
||||
cr.execute("""SELECT value
|
||||
FROM ir_translation
|
||||
WHERE lang=%s
|
||||
AND type in %s
|
||||
AND name=%s""",
|
||||
(lang or '', types, tools.ustr(name)))
|
||||
res = cr.fetchone()
|
||||
trad = res and res[0] or u''
|
||||
|
|
|
@ -19,9 +19,13 @@
|
|||
#
|
||||
##############################################################################
|
||||
|
||||
from osv import fields, osv
|
||||
import base64
|
||||
import re
|
||||
|
||||
import tools
|
||||
import addons
|
||||
from osv import fields, osv
|
||||
from tools.translate import _
|
||||
|
||||
def one_in(setA, setB):
|
||||
"""Check the presence of an element of setA in setB
|
||||
|
@ -31,25 +35,6 @@ def one_in(setA, setB):
|
|||
return True
|
||||
return False
|
||||
|
||||
def cond(C, X, Y):
|
||||
if C: return X
|
||||
return Y
|
||||
|
||||
class many2many_unique(fields.many2many):
|
||||
def set(self, cr, obj, id, name, values, user=None, context=None):
|
||||
if not values:
|
||||
return
|
||||
val = values[:]
|
||||
for act in values:
|
||||
if act[0]==4:
|
||||
cr.execute('SELECT * FROM '+self._rel+' \
|
||||
WHERE '+self._id1+'=%s AND '+self._id2+'=%s', (id, act[1]))
|
||||
if cr.fetchall():
|
||||
val.remove(act)
|
||||
return super(many2many_unique, self).set(cr, obj, id, name, val, user=user,
|
||||
context=context)
|
||||
|
||||
|
||||
class ir_ui_menu(osv.osv):
|
||||
_name = 'ir.ui.menu'
|
||||
|
||||
|
@ -67,46 +52,15 @@ class ir_ui_menu(osv.osv):
|
|||
# radical but this doesn't frequently happen
|
||||
self._cache = {}
|
||||
|
||||
def create_shortcut(self, cr, uid, values, context={}):
|
||||
dataobj = self.pool.get('ir.model.data')
|
||||
new_context = context.copy()
|
||||
for key in context:
|
||||
if key.startswith('default_'):
|
||||
del new_context[key]
|
||||
|
||||
menu_id = dataobj._get_id(cr, uid, 'base', 'menu_administration_shortcut', new_context)
|
||||
shortcut_menu_id = int(dataobj.read(cr, uid, menu_id, ['res_id'], new_context)['res_id'])
|
||||
action_id = self.pool.get('ir.actions.act_window').create(cr, uid, values, new_context)
|
||||
menu_data = {'name':values['name'],
|
||||
'sequence':10,
|
||||
'action':'ir.actions.act_window,'+str(action_id),
|
||||
'parent_id':shortcut_menu_id,
|
||||
'icon':'STOCK_JUSTIFY_FILL'}
|
||||
menu_id = self.pool.get('ir.ui.menu').create(cr, 1, menu_data)
|
||||
sc_data= {'name':values['name'], 'sequence': 1,'res_id': menu_id }
|
||||
sc_menu_id = self.pool.get('ir.ui.view_sc').create(cr, uid, sc_data, new_context)
|
||||
|
||||
user_groups = set(self.pool.get('res.users').read(cr, 1, uid, ['groups_id'])['groups_id'])
|
||||
key = (cr.dbname, shortcut_menu_id, tuple(user_groups))
|
||||
self._cache[key] = True
|
||||
return action_id
|
||||
|
||||
def search(self, cr, uid, args, offset=0, limit=None, order=None,
|
||||
context=None, count=False):
|
||||
|
||||
ids = super(ir_ui_menu, self).search(cr, uid, args, offset=0,
|
||||
limit=None, order=order, context=context, count=False)
|
||||
|
||||
if not ids:
|
||||
if count:
|
||||
return 0
|
||||
return []
|
||||
|
||||
|
||||
def _filter_visible_menus(self, cr, uid, ids, context=None):
|
||||
"""Filters the give menu ids to only keep the menu items that should be
|
||||
visible in the menu hierarchy of the current user.
|
||||
Uses a cache for speeding up the computation.
|
||||
"""
|
||||
modelaccess = self.pool.get('ir.model.access')
|
||||
user_groups = set(self.pool.get('res.users').read(cr, 1, uid, ['groups_id'])['groups_id'])
|
||||
result = []
|
||||
for menu in self.browse(cr, uid, ids):
|
||||
for menu in self.browse(cr, uid, ids, context=context):
|
||||
# this key works because user access rights are all based on user's groups (cfr ir_model_access.check)
|
||||
key = (cr.dbname, menu.id, tuple(user_groups))
|
||||
if key in self._cache:
|
||||
|
@ -147,6 +101,25 @@ class ir_ui_menu(osv.osv):
|
|||
|
||||
result.append(menu.id)
|
||||
self._cache[key] = True
|
||||
return result
|
||||
|
||||
def search(self, cr, uid, args, offset=0, limit=None, order=None, context=None, count=False):
|
||||
if context is None:
|
||||
context = {}
|
||||
|
||||
ids = super(ir_ui_menu, self).search(cr, uid, args, offset=0,
|
||||
limit=None, order=order, context=context, count=False)
|
||||
|
||||
if not ids:
|
||||
if count:
|
||||
return 0
|
||||
return []
|
||||
|
||||
# menu filtering is done only on main menu tree, not other menu lists
|
||||
if context.get('ir.ui.menu.full_list'):
|
||||
result = ids
|
||||
else:
|
||||
result = self._filter_visible_menus(cr, uid, ids, context=context)
|
||||
|
||||
if offset:
|
||||
result = result[long(offset):]
|
||||
|
@ -172,6 +145,10 @@ class ir_ui_menu(osv.osv):
|
|||
parent_path = ''
|
||||
return parent_path + menu.name
|
||||
|
||||
def create(self, *args, **kwargs):
|
||||
self.clear_cache()
|
||||
return super(ir_ui_menu, self).create(*args, **kwargs)
|
||||
|
||||
def write(self, *args, **kwargs):
|
||||
self.clear_cache()
|
||||
return super(ir_ui_menu, self).write(*args, **kwargs)
|
||||
|
@ -251,30 +228,48 @@ class ir_ui_menu(osv.osv):
|
|||
return {}
|
||||
return {'type': {'icon_pict': 'picture'}, 'value': {'icon_pict': ('stock', (icon,'ICON_SIZE_MENU'))}}
|
||||
|
||||
def _check_recursion(self, cr, uid, ids):
|
||||
level = 100
|
||||
while len(ids):
|
||||
cr.execute('select distinct parent_id from ir_ui_menu where id IN %s',(tuple(ids),))
|
||||
ids = filter(None, map(lambda x:x[0], cr.fetchall()))
|
||||
if not level:
|
||||
return False
|
||||
level -= 1
|
||||
return True
|
||||
|
||||
|
||||
def read_image(self, path):
|
||||
path_info = path.split(',')
|
||||
icon_path = addons.get_module_resource(path_info[0],path_info[1])
|
||||
icon_image = False
|
||||
if icon_path:
|
||||
try:
|
||||
icon_file = tools.file_open(icon_path,'rb')
|
||||
icon_image = base64.encodestring(icon_file.read())
|
||||
finally:
|
||||
icon_file.close()
|
||||
return icon_image
|
||||
|
||||
def _get_image_icon(self, cr, uid, ids, name, args, context=None):
|
||||
res = {}
|
||||
for menu in self.browse(cr, uid, ids, context=context):
|
||||
res[menu.id] = {
|
||||
'web_icon_data': False,
|
||||
'web_icon_hover_data': False,
|
||||
}
|
||||
if not menu.parent_id:
|
||||
if menu.web_icon_hover:
|
||||
res[menu.id]['web_icon_hover_data'] = self.read_image(menu.web_icon_hover)
|
||||
if menu.web_icon:
|
||||
res[menu.id]['web_icon_data'] = self.read_image(menu.web_icon)
|
||||
return res
|
||||
|
||||
_columns = {
|
||||
'name': fields.char('Menu', size=64, required=True, translate=True),
|
||||
'sequence': fields.integer('Sequence'),
|
||||
'child_id' : fields.one2many('ir.ui.menu', 'parent_id','Child IDs'),
|
||||
'parent_id': fields.many2one('ir.ui.menu', 'Parent Menu', select=True),
|
||||
'groups_id': many2many_unique('res.groups', 'ir_ui_menu_group_rel',
|
||||
'groups_id': fields.many2many('res.groups', 'ir_ui_menu_group_rel',
|
||||
'menu_id', 'gid', 'Groups', help="If you have groups, the visibility of this menu will be based on these groups. "\
|
||||
"If this field is empty, OpenERP will compute visibility based on the related object's read access."),
|
||||
'complete_name': fields.function(_get_full_name, method=True,
|
||||
string='Complete Name', type='char', size=128),
|
||||
'icon': fields.selection(tools.icons, 'Icon', size=64),
|
||||
'icon_pict': fields.function(_get_icon_pict, method=True, type='char', size=32),
|
||||
'web_icon': fields.char('Web Icon File', size=128),
|
||||
'web_icon_hover':fields.char('Web Icon File (hover)', size=128),
|
||||
'web_icon_data': fields.function(_get_image_icon, string='Web Icon Image', type='binary', method=True, readonly=True, store=True, multi='icon'),
|
||||
'web_icon_hover_data':fields.function(_get_image_icon, string='Web Icon Image (hover)', type='binary', method=True, readonly=True, store=True, multi='icon'),
|
||||
'action': fields.function(_action, fnct_inv=_action_inv,
|
||||
method=True, type='reference', string='Action',
|
||||
selection=[
|
||||
|
@ -285,13 +280,17 @@ class ir_ui_menu(osv.osv):
|
|||
('ir.actions.server', 'ir.actions.server'),
|
||||
]),
|
||||
}
|
||||
|
||||
def _rec_message(self, cr, uid, ids, context=None):
|
||||
return _('Error ! You can not create recursive Menu.')
|
||||
|
||||
_constraints = [
|
||||
(_check_recursion, 'Error ! You can not create recursive Menu.', ['parent_id'])
|
||||
(osv.osv._check_recursion, _rec_message , ['parent_id'])
|
||||
]
|
||||
_defaults = {
|
||||
'icon' : lambda *a: 'STOCK_OPEN',
|
||||
'icon_pict': lambda *a: ('stock', ('STOCK_OPEN','ICON_SIZE_MENU')),
|
||||
'sequence' : lambda *a: 10,
|
||||
'icon' : 'STOCK_OPEN',
|
||||
'icon_pict': ('stock', ('STOCK_OPEN','ICON_SIZE_MENU')),
|
||||
'sequence' : 10,
|
||||
}
|
||||
_order = "sequence,id"
|
||||
ir_ui_menu()
|
||||
|
|
|
@ -28,33 +28,42 @@ import netsvc
|
|||
import os
|
||||
import logging
|
||||
|
||||
def _check_xml(self, cr, uid, ids, context={}):
|
||||
def _check_xml(self, cr, uid, ids, context=None):
|
||||
logger = logging.getLogger('init')
|
||||
for view in self.browse(cr, uid, ids, context):
|
||||
eview = etree.fromstring(view.arch.encode('utf8'))
|
||||
frng = tools.file_open(os.path.join('base','rng','view.rng'))
|
||||
relaxng_doc = etree.parse(frng)
|
||||
relaxng = etree.RelaxNG(relaxng_doc)
|
||||
if not relaxng.validate(eview):
|
||||
for error in relaxng.error_log:
|
||||
logger.error(tools.ustr(error))
|
||||
return False
|
||||
try:
|
||||
relaxng_doc = etree.parse(frng)
|
||||
relaxng = etree.RelaxNG(relaxng_doc)
|
||||
if not relaxng.validate(eview):
|
||||
for error in relaxng.error_log:
|
||||
logger.error(tools.ustr(error))
|
||||
return False
|
||||
finally:
|
||||
frng.close()
|
||||
return True
|
||||
|
||||
class view_custom(osv.osv):
|
||||
_name = 'ir.ui.view.custom'
|
||||
_columns = {
|
||||
'ref_id': fields.many2one('ir.ui.view', 'Original View'),
|
||||
'user_id': fields.many2one('res.users', 'User'),
|
||||
'ref_id': fields.many2one('ir.ui.view', 'Original View', select=True),
|
||||
'user_id': fields.many2one('res.users', 'User', select=True),
|
||||
'arch': fields.text('View Architecture', required=True),
|
||||
}
|
||||
|
||||
def _auto_init(self, cr, context=None):
|
||||
super(view_custom, self)._auto_init(cr, context)
|
||||
cr.execute('SELECT indexname FROM pg_indexes WHERE indexname = \'ir_ui_view_custom_user_id_ref_id\'')
|
||||
if not cr.fetchone():
|
||||
cr.execute('CREATE INDEX ir_ui_view_custom_user_id_ref_id ON ir_ui_view_custom (user_id, ref_id)')
|
||||
view_custom()
|
||||
|
||||
class view(osv.osv):
|
||||
_name = 'ir.ui.view'
|
||||
_columns = {
|
||||
'name': fields.char('View Name',size=64, required=True),
|
||||
'model': fields.char('Object', size=64, required=True),
|
||||
'model': fields.char('Object', size=64, required=True, select=True),
|
||||
'priority': fields.integer('Sequence', required=True),
|
||||
'type': fields.selection((
|
||||
('tree','Tree'),
|
||||
|
@ -64,9 +73,9 @@ class view(osv.osv):
|
|||
('calendar', 'Calendar'),
|
||||
('diagram','Diagram'),
|
||||
('gantt', 'Gantt'),
|
||||
('search','Search')), 'View Type', required=True),
|
||||
('search','Search')), 'View Type', required=True, select=True),
|
||||
'arch': fields.text('View Architecture', required=True),
|
||||
'inherit_id': fields.many2one('ir.ui.view', 'Inherited View', ondelete='cascade'),
|
||||
'inherit_id': fields.many2one('ir.ui.view', 'Inherited View', ondelete='cascade', select=True),
|
||||
'field_parent': fields.char('Child Field',size=64),
|
||||
'xml_id': fields.function(osv.osv.get_xml_id, type='char', size=128, string="XML ID",
|
||||
method=True, help="ID of the view defined in xml file"),
|
||||
|
@ -75,49 +84,29 @@ class view(osv.osv):
|
|||
'arch': '<?xml version="1.0"?>\n<tree string="My view">\n\t<field name="name"/>\n</tree>',
|
||||
'priority': 16
|
||||
}
|
||||
_order = "priority"
|
||||
_order = "priority,name"
|
||||
_constraints = [
|
||||
(_check_xml, 'Invalid XML for View Architecture!', ['arch'])
|
||||
]
|
||||
|
||||
def read(self, cr, uid, ids, fields=None, context={}, load='_classic_read'):
|
||||
|
||||
if not isinstance(ids, (list, tuple)):
|
||||
ids = [ids]
|
||||
|
||||
result = super(view, self).read(cr, uid, ids, fields, context, load)
|
||||
|
||||
for rs in result:
|
||||
if rs.get('model') == 'board.board':
|
||||
cr.execute("select id,arch,ref_id from ir_ui_view_custom where user_id=%s and ref_id=%s", (uid, rs['id']))
|
||||
oview = cr.dictfetchall()
|
||||
if oview:
|
||||
rs['arch'] = oview[0]['arch']
|
||||
|
||||
|
||||
return result
|
||||
def _auto_init(self, cr, context=None):
|
||||
super(view, self)._auto_init(cr, context)
|
||||
cr.execute('SELECT indexname FROM pg_indexes WHERE indexname = \'ir_ui_view_model_type_inherit_id\'')
|
||||
if not cr.fetchone():
|
||||
cr.execute('CREATE INDEX ir_ui_view_model_type_inherit_id ON ir_ui_view (model, type, inherit_id)')
|
||||
|
||||
def write(self, cr, uid, ids, vals, context={}):
|
||||
|
||||
if not isinstance(ids, (list, tuple)):
|
||||
ids = [ids]
|
||||
result = super(view, self).write(cr, uid, ids, vals, context)
|
||||
|
||||
exist = self.pool.get('ir.ui.view').browse(cr, uid, ids[0])
|
||||
if exist.model == 'board.board' and 'arch' in vals:
|
||||
vids = self.pool.get('ir.ui.view.custom').search(cr, uid, [('user_id','=',uid), ('ref_id','=',ids[0])])
|
||||
vals2 = {'user_id': uid, 'ref_id': ids[0], 'arch': vals.pop('arch')}
|
||||
# drop the corresponding view customizations (used for dashboards for example), otherwise
|
||||
# not all users would see the updated views
|
||||
custom_view_ids = self.pool.get('ir.ui.view.custom').search(cr, uid, [('ref_id','in',ids)])
|
||||
if custom_view_ids:
|
||||
self.pool.get('ir.ui.view.custom').unlink(cr, uid, custom_view_ids)
|
||||
|
||||
# write fields except arch to the `ir.ui.view`
|
||||
result = super(view, self).write(cr, uid, ids, vals, context)
|
||||
|
||||
if not vids:
|
||||
self.pool.get('ir.ui.view.custom').create(cr, uid, vals2)
|
||||
else:
|
||||
self.pool.get('ir.ui.view.custom').write(cr, uid, vids, vals2)
|
||||
|
||||
return result
|
||||
|
||||
return super(view, self).write(cr, uid, ids, vals, context)
|
||||
return result
|
||||
|
||||
def graph_get(self, cr, uid, id, model, node_obj, conn_obj, src_node, des_node,label,scale,context={}):
|
||||
if not label:
|
||||
|
@ -192,18 +181,28 @@ view()
|
|||
class view_sc(osv.osv):
|
||||
_name = 'ir.ui.view_sc'
|
||||
_columns = {
|
||||
'name': fields.char('Shortcut Name', size=64, required=True),
|
||||
'res_id': fields.many2one('ir.ui.menu','Resource Ref.', ondelete='cascade'),
|
||||
'name': fields.char('Shortcut Name', size=64), # Kept for backwards compatibility only - resource name used instead (translatable)
|
||||
'res_id': fields.integer('Resource Ref.', help="Reference of the target resource, whose model/table depends on the 'Resource Name' field."),
|
||||
'sequence': fields.integer('Sequence'),
|
||||
'user_id': fields.many2one('res.users', 'User Ref.', required=True, ondelete='cascade'),
|
||||
'resource': fields.char('Resource Name', size=64, required=True)
|
||||
'user_id': fields.many2one('res.users', 'User Ref.', required=True, ondelete='cascade', select=True),
|
||||
'resource': fields.char('Resource Name', size=64, required=True, select=True)
|
||||
}
|
||||
|
||||
def get_sc(self, cr, uid, user_id, model='ir.ui.menu', context={}):
|
||||
ids = self.search(cr, uid, [('user_id','=',user_id),('resource','=',model)], context=context)
|
||||
return self.read(cr, uid, ids, ['res_id','name'], context=context)
|
||||
def _auto_init(self, cr, context=None):
|
||||
super(view_sc, self)._auto_init(cr, context)
|
||||
cr.execute('SELECT indexname FROM pg_indexes WHERE indexname = \'ir_ui_view_sc_user_id_resource\'')
|
||||
if not cr.fetchone():
|
||||
cr.execute('CREATE INDEX ir_ui_view_sc_user_id_resource ON ir_ui_view_sc (user_id, resource)')
|
||||
|
||||
_order = 'sequence'
|
||||
def get_sc(self, cr, uid, user_id, model='ir.ui.menu', context=None):
|
||||
ids = self.search(cr, uid, [('user_id','=',user_id),('resource','=',model)], context=context)
|
||||
results = self.read(cr, uid, ids, ['res_id'], context=context)
|
||||
name_map = dict(self.pool.get(model).name_get(cr, uid, [x['res_id'] for x in results], context=context))
|
||||
for result in results:
|
||||
result.update(name=name_map[result['res_id']])
|
||||
return results
|
||||
|
||||
_order = 'sequence,name'
|
||||
_defaults = {
|
||||
'resource': lambda *a: 'ir.ui.menu',
|
||||
'user_id': lambda obj, cr, uid, context: uid,
|
||||
|
|
|
@ -24,6 +24,10 @@ from osv.orm import except_orm
|
|||
import pickle
|
||||
from tools.translate import _
|
||||
|
||||
EXCLUDED_FIELDS = set((
|
||||
'report_sxw_content', 'report_rml_content', 'report_sxw', 'report_rml',
|
||||
'report_sxw_content_data', 'report_rml_content_data', 'search_view', ))
|
||||
|
||||
class ir_values(osv.osv):
|
||||
_name = 'ir.values'
|
||||
|
||||
|
@ -67,21 +71,21 @@ class ir_values(osv.osv):
|
|||
'name': fields.char('Name', size=128),
|
||||
'model_id': fields.many2one('ir.model', 'Object', size=128,
|
||||
help="This field is not used, it only helps you to select a good model."),
|
||||
'model': fields.char('Object Name', size=128),
|
||||
'model': fields.char('Object Name', size=128, select=True),
|
||||
'action_id': fields.many2one('ir.actions.actions', 'Action',
|
||||
help="This field is not used, it only helps you to select the right action."),
|
||||
'value': fields.text('Value'),
|
||||
'value_unpickle': fields.function(_value_unpickle, fnct_inv=_value_pickle,
|
||||
method=True, type='text', string='Value'),
|
||||
'object': fields.boolean('Is Object'),
|
||||
'key': fields.selection([('action','Action'),('default','Default')], 'Type', size=128),
|
||||
'key2' : fields.char('Event Type',help="The kind of action or button in the client side that will trigger the action.", size=128),
|
||||
'key': fields.selection([('action','Action'),('default','Default')], 'Type', size=128, select=True),
|
||||
'key2' : fields.char('Event Type',help="The kind of action or button in the client side that will trigger the action.", size=128, select=True),
|
||||
'meta': fields.text('Meta Datas'),
|
||||
'meta_unpickle': fields.function(_value_unpickle, fnct_inv=_value_pickle,
|
||||
method=True, type='text', string='Metadata'),
|
||||
'res_id': fields.integer('Object ID', help="Keep 0 if the action must appear on all resources."),
|
||||
'user_id': fields.many2one('res.users', 'User', ondelete='cascade'),
|
||||
'company_id': fields.many2one('res.company', 'Company')
|
||||
'res_id': fields.integer('Object ID', help="Keep 0 if the action must appear on all resources.", select=True),
|
||||
'user_id': fields.many2one('res.users', 'User', ondelete='cascade', select=True),
|
||||
'company_id': fields.many2one('res.company', 'Company', select=True)
|
||||
}
|
||||
_defaults = {
|
||||
'key': lambda *a: 'action',
|
||||
|
@ -89,14 +93,14 @@ class ir_values(osv.osv):
|
|||
'company_id': lambda *a: False
|
||||
}
|
||||
|
||||
def _auto_init(self, cr, context={}):
|
||||
def _auto_init(self, cr, context=None):
|
||||
super(ir_values, self)._auto_init(cr, context)
|
||||
cr.execute('SELECT indexname FROM pg_indexes WHERE indexname = \'ir_values_key_model_key2_index\'')
|
||||
cr.execute('SELECT indexname FROM pg_indexes WHERE indexname = \'ir_values_key_model_key2_res_id_user_id_idx\'')
|
||||
if not cr.fetchone():
|
||||
cr.execute('CREATE INDEX ir_values_key_model_key2_index ON ir_values (key, model, key2)')
|
||||
cr.execute('CREATE INDEX ir_values_key_model_key2_res_id_user_id_idx ON ir_values (key, model, key2, res_id, user_id)')
|
||||
|
||||
def set(self, cr, uid, key, key2, name, models, value, replace=True, isobject=False, meta=False, preserve_user=False, company=False):
|
||||
if type(value)==type(u''):
|
||||
if isinstance(value, unicode):
|
||||
value = value.encode('utf8')
|
||||
if not isobject:
|
||||
value = pickle.dumps(value)
|
||||
|
@ -104,30 +108,24 @@ class ir_values(osv.osv):
|
|||
meta = pickle.dumps(meta)
|
||||
ids_res = []
|
||||
for model in models:
|
||||
if type(model)==type([]) or type(model)==type(()):
|
||||
if isinstance(model, (list, tuple)):
|
||||
model,res_id = model
|
||||
else:
|
||||
res_id=False
|
||||
if replace:
|
||||
search_criteria = [
|
||||
('key', '=', key),
|
||||
('key2', '=', key2),
|
||||
('model', '=', model),
|
||||
('res_id', '=', res_id),
|
||||
('user_id', '=', preserve_user and uid)
|
||||
]
|
||||
if key in ('meta', 'default'):
|
||||
ids = self.search(cr, uid, [
|
||||
('key', '=', key),
|
||||
('key2', '=', key2),
|
||||
('name', '=', name),
|
||||
('model', '=', model),
|
||||
('res_id', '=', res_id),
|
||||
('user_id', '=', preserve_user and uid)
|
||||
])
|
||||
search_criteria.append(('name', '=', name))
|
||||
else:
|
||||
ids = self.search(cr, uid, [
|
||||
('key', '=', key),
|
||||
('key2', '=', key2),
|
||||
('value', '=', value),
|
||||
('model', '=', model),
|
||||
('res_id', '=', res_id),
|
||||
('user_id', '=', preserve_user and uid)
|
||||
])
|
||||
self.unlink(cr, uid, ids)
|
||||
search_criteria.append(('value', '=', value))
|
||||
|
||||
self.unlink(cr, uid, self.search(cr, uid, search_criteria))
|
||||
vals = {
|
||||
'name': name,
|
||||
'value': value,
|
||||
|
@ -149,8 +147,8 @@ class ir_values(osv.osv):
|
|||
def get(self, cr, uid, key, key2, models, meta=False, context={}, res_id_req=False, without_user=True, key2_req=True):
|
||||
result = []
|
||||
for m in models:
|
||||
if type(m)==type([]) or type(m)==type(()):
|
||||
m,res_id = m
|
||||
if isinstance(m, (list, tuple)):
|
||||
m, res_id = m
|
||||
else:
|
||||
res_id=False
|
||||
|
||||
|
@ -159,9 +157,8 @@ class ir_values(osv.osv):
|
|||
if key2:
|
||||
where.append('key2=%s')
|
||||
params.append(key2[:200])
|
||||
else:
|
||||
if key2_req and not meta:
|
||||
where.append('key2 is null')
|
||||
elif key2_req and not meta:
|
||||
where.append('key2 is null')
|
||||
if res_id_req and (models[-1][0]==m):
|
||||
if res_id:
|
||||
where.append('res_id=%s')
|
||||
|
@ -193,48 +190,37 @@ class ir_values(osv.osv):
|
|||
keys.append(x[1])
|
||||
if x[3]:
|
||||
model,id = x[2].split(',')
|
||||
id = int(id)
|
||||
fields = self.pool.get(model).fields_get_keys(cr, uid)
|
||||
pos = 0
|
||||
while pos<len(fields):
|
||||
if fields[pos] in ('report_sxw_content', 'report_rml_content',
|
||||
'report_sxw', 'report_rml', 'report_sxw_content_data',
|
||||
'report_rml_content_data'):
|
||||
del fields[pos]
|
||||
else:
|
||||
pos+=1
|
||||
# FIXME: It might be a good idea to opt-in that kind of stuff
|
||||
# FIXME: instead of arbitrarily removing random fields
|
||||
fields = [
|
||||
field
|
||||
for field in self.pool.get(model).fields_get_keys(cr, uid)
|
||||
if field not in EXCLUDED_FIELDS]
|
||||
|
||||
try:
|
||||
datas = self.pool.get(model).read(cr, uid, [id], fields, context)
|
||||
datas = self.pool.get(model).read(cr, uid, [int(id)], fields, context)
|
||||
except except_orm, e:
|
||||
return False
|
||||
datas= datas and datas[0] or None
|
||||
datas = datas and datas[0]
|
||||
if not datas:
|
||||
#ir_del(cr, uid, x[0])
|
||||
return False
|
||||
else:
|
||||
datas = pickle.loads(str(x[2].encode('utf-8')))
|
||||
datas = pickle.loads(x[2].encode('utf-8'))
|
||||
if meta:
|
||||
meta2 = pickle.loads(x[4])
|
||||
return (x[0],x[1],datas,meta2)
|
||||
return (x[0],x[1],datas)
|
||||
return (x[0], x[1], datas, pickle.loads(x[4]))
|
||||
return (x[0], x[1], datas)
|
||||
keys = []
|
||||
res = filter(bool, map(lambda x: _result_get(x, keys), list(result)))
|
||||
res = filter(None, map(lambda x: _result_get(x, keys), result))
|
||||
res2 = res[:]
|
||||
for r in res:
|
||||
if type(r[2])==type({}) and 'type' in r[2]:
|
||||
if r[2]['type'] in ('ir.actions.report.xml','ir.actions.act_window','ir.actions.wizard'):
|
||||
groups = r[2].get('groups_id')
|
||||
if groups:
|
||||
cr.execute('SELECT COUNT(1) FROM res_groups_users_rel WHERE gid IN %s AND uid=%s',(tuple(groups), uid))
|
||||
cnt = cr.fetchone()[0]
|
||||
if not cnt:
|
||||
res2.remove(r)
|
||||
if r[1] == 'Menuitem' and not res2:
|
||||
raise osv.except_osv('Error !','You do not have the permission to perform this operation !!!')
|
||||
if isinstance(r[2], dict) and r[2].get('type') in ('ir.actions.report.xml','ir.actions.act_window','ir.actions.wizard'):
|
||||
groups = r[2].get('groups_id')
|
||||
if groups:
|
||||
cr.execute('SELECT COUNT(1) FROM res_groups_users_rel WHERE gid IN %s AND uid=%s',(tuple(groups), uid))
|
||||
cnt = cr.fetchone()[0]
|
||||
if not cnt:
|
||||
res2.remove(r)
|
||||
if r[1] == 'Menuitem' and not res2:
|
||||
raise osv.except_osv('Error !','You do not have the permission to perform this operation !!!')
|
||||
return res2
|
||||
ir_values()
|
||||
|
||||
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2010 OpenERP s.a. (<http://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
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
from osv import osv
|
||||
from osv.orm import orm_memory
|
||||
|
||||
class osv_memory_autovacuum(osv.osv_memory):
|
||||
_name = 'osv_memory.autovacuum'
|
||||
|
||||
def power_on(self, cr, uid, context=None):
|
||||
for model in self.pool.obj_list():
|
||||
obj = self.pool.get(model)
|
||||
if isinstance(obj, orm_memory):
|
||||
obj.vaccum(cr, uid)
|
||||
return True
|
||||
|
||||
osv_memory_autovacuum()
|
||||
|
|
@ -31,8 +31,12 @@ class wizard_screen(osv.osv_memory):
|
|||
|
||||
def _get_image(self, cr, uid, context=None):
|
||||
path = os.path.join('base','res','config_pixmaps','%d.png'%random.randrange(1,4))
|
||||
file_data = tools.file_open(path,'rb').read()
|
||||
return base64.encodestring(file_data)
|
||||
image_file = file_data = tools.file_open(path,'rb')
|
||||
try:
|
||||
file_data = image_file.read()
|
||||
return base64.encodestring(file_data)
|
||||
finally:
|
||||
image_file.close()
|
||||
|
||||
def _get_image_fn(self, cr, uid, ids, name, args, context=None):
|
||||
image = self._get_image(cr, uid, context)
|
||||
|
|
|
@ -26,6 +26,7 @@ import netsvc
|
|||
class workflow(osv.osv):
|
||||
_name = "workflow"
|
||||
_table = "wkf"
|
||||
_order = "name"
|
||||
# _log_access = False
|
||||
_columns = {
|
||||
'name': fields.char('Name', size=64, required=True),
|
||||
|
@ -115,6 +116,7 @@ workflow()
|
|||
class wkf_activity(osv.osv):
|
||||
_name = "workflow.activity"
|
||||
_table = "wkf_activity"
|
||||
_order = "name"
|
||||
# _log_access = False
|
||||
_columns = {
|
||||
'name': fields.char('Name', size=64, required=True),
|
||||
|
@ -171,15 +173,15 @@ class wkf_instance(osv.osv):
|
|||
_log_access = False
|
||||
_columns = {
|
||||
'wkf_id': fields.many2one('workflow', 'Workflow', ondelete='cascade', select=True),
|
||||
'res_id': fields.integer('Resource ID', select=True),
|
||||
'res_type': fields.char('Resource Object', size=64, select=True),
|
||||
'state': fields.char('State', size=32, select=True),
|
||||
'res_id': fields.integer('Resource ID'),
|
||||
'res_type': fields.char('Resource Object', size=64),
|
||||
'state': fields.char('State', size=32),
|
||||
}
|
||||
def _auto_init(self, cr, context={}):
|
||||
def _auto_init(self, cr, context=None):
|
||||
super(wkf_instance, self)._auto_init(cr, context)
|
||||
cr.execute('SELECT indexname FROM pg_indexes WHERE indexname = \'wkf_instance_res_id_res_type_state_index\'')
|
||||
cr.execute('SELECT indexname FROM pg_indexes WHERE indexname = \'wkf_instance_res_type_res_id_state_index\'')
|
||||
if not cr.fetchone():
|
||||
cr.execute('CREATE INDEX wkf_instance_res_id_res_type_state_index ON wkf_instance (res_id, res_type, state)')
|
||||
cr.execute('CREATE INDEX wkf_instance_res_type_res_id_state_index ON wkf_instance (res_type, res_id, state)')
|
||||
cr.execute('SELECT indexname FROM pg_indexes WHERE indexname = \'wkf_instance_res_id_wkf_id_index\'')
|
||||
if not cr.fetchone():
|
||||
cr.execute('CREATE INDEX wkf_instance_res_id_wkf_id_index ON wkf_instance (res_id, wkf_id)')
|
||||
|
|
|
@ -182,7 +182,7 @@
|
|||
<field name="action"/>
|
||||
<newline/>
|
||||
<group expand="0" string="Group By...">
|
||||
<filter string="Workflow" icon="terp-stock_align_left_24" domain="[]" context="{'group_by':'wkf_id'}"/>
|
||||
<filter string="Workflow" icon="terp-stage" domain="[]" context="{'group_by':'wkf_id'}"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
|
|
|
@ -1,206 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
from osv import osv, fields
|
||||
import pooler
|
||||
import time
|
||||
import netsvc
|
||||
|
||||
from tools.misc import debug
|
||||
from tools.misc import ustr
|
||||
from tools.translate import _
|
||||
import tools.maintenance as tm
|
||||
|
||||
class maintenance_contract_module(osv.osv):
|
||||
_name ="maintenance.contract.module"
|
||||
_description = "maintenance contract modules"
|
||||
_columns = {
|
||||
'name' : fields.char('Name', size=128, required=True),
|
||||
'version': fields.char('Version', size=64,),
|
||||
}
|
||||
|
||||
maintenance_contract_module()
|
||||
|
||||
class maintenance_contract(osv.osv):
|
||||
_name = "maintenance.contract"
|
||||
_description = "Maintenance Contract"
|
||||
|
||||
def _get_valid_contracts(self, cr, uid):
|
||||
return [contract for contract in self.browse(cr, uid, self.search(cr, uid, [])) if contract.state == 'valid']
|
||||
|
||||
def status(self, cr, uid):
|
||||
covered_modules, uncovered_modules = set(), set()
|
||||
|
||||
status = 'none'
|
||||
for contract in self._get_valid_contracts(cr, uid):
|
||||
covered_modules.update([m.name for m in contract.module_ids])
|
||||
|
||||
if covered_modules:
|
||||
modobj = self.pool.get('ir.module.module')
|
||||
modids = modobj.search(cr, uid, [('state', '=', 'installed')])
|
||||
uncovered_modules = set(m.name for m in modobj.browse(cr, uid, modids)) - covered_modules
|
||||
status = ['full', 'partial'][len(uncovered_modules) > 0]
|
||||
|
||||
return {
|
||||
'status': status,
|
||||
'uncovered_modules': list(uncovered_modules),
|
||||
}
|
||||
|
||||
def send(self, cr, uid, tb, explanations, remarks=None):
|
||||
status = self.status(cr, uid)
|
||||
if status['status'] != 'full':
|
||||
raise osv.except_osv(_('Error'), _("Your can't submit bug reports due to uncovered modules: %s") % (', '.join(status['uncovered_modules']),))
|
||||
|
||||
dbmsg = _('This error occurs on database %s') % (cr.dbname,)
|
||||
if not remarks:
|
||||
remarks = dbmsg
|
||||
else:
|
||||
remarks += '\n\n-----\n' + dbmsg
|
||||
|
||||
valid_contracts = self._get_valid_contracts(cr, uid)
|
||||
|
||||
crm_case_id = None
|
||||
rc = None
|
||||
try:
|
||||
for contract in valid_contracts:
|
||||
rc = tm.remote_contract(contract.name, contract.password)
|
||||
if rc.id:
|
||||
break
|
||||
rc = None
|
||||
|
||||
if not rc:
|
||||
raise osv.except_osv(_('Error'), _('Unable to find a valid contract'))
|
||||
|
||||
origin = 'client'
|
||||
crm_case_id = rc.submit(rc.id, tb, explanations, remarks or '', origin)
|
||||
|
||||
except tm.RemoteContractException, rce:
|
||||
netsvc.Logger().notifyChannel('maintenance', netsvc.LOG_INFO, rce)
|
||||
except osv.except_osv:
|
||||
raise
|
||||
except:
|
||||
pass
|
||||
|
||||
cid = rc and rc.name or valid_contracts[0].name
|
||||
try:
|
||||
# as backup, put it also in another database...
|
||||
import urllib
|
||||
args = urllib.urlencode({
|
||||
'contract_id': cid,
|
||||
'crm_case_id': crm_case_id or 0,
|
||||
'explanation': explanations,
|
||||
'remark': remarks or '',
|
||||
'tb': tb,
|
||||
})
|
||||
uo = urllib.urlopen('http://www.openerp.com/scripts/maintenance.php', args)
|
||||
submit_result = uo.read()
|
||||
debug(submit_result)
|
||||
uo.close()
|
||||
except:
|
||||
if not crm_case_id:
|
||||
# TODO schedule a retry (ir.cron)
|
||||
return False
|
||||
return True
|
||||
|
||||
def _valid_get(self, cr, uid, ids, field_name, arg, context=None):
|
||||
res = {}
|
||||
for contract in self.browse(cr, uid, ids, context=context):
|
||||
res[contract.id] = ("unvalid", "valid")[contract.date_stop >= time.strftime('%Y-%m-%d')]
|
||||
return res
|
||||
|
||||
_columns = {
|
||||
'name' : fields.char('Contract ID', size=256, required=True, readonly=True),
|
||||
'password' : fields.char('Password', size=64, invisible=True, required=True, readonly=True),
|
||||
'date_start' : fields.date('Starting Date', readonly=True),
|
||||
'date_stop' : fields.date('Ending Date', readonly=True),
|
||||
'module_ids' : fields.many2many('maintenance.contract.module', 'maintenance_contract_module_rel', 'contract_id', 'module_id', 'Covered Modules', readonly=True),
|
||||
'state' : fields.function(_valid_get, method=True, string="State", type="selection", selection=[('valid', 'Valid'),('unvalid', 'Unvalid')], readonly=True),
|
||||
'kind' : fields.selection([('full', 'Full'),('partial', 'Partial')], 'Kind', required=True, readonly=True),
|
||||
}
|
||||
_defaults = {
|
||||
'password' : lambda obj,cr,uid,context={} : '',
|
||||
}
|
||||
_sql_constraints = [
|
||||
('uniq_name', 'unique(name)', "Your maintenance contract is already subscribed in the system !")
|
||||
]
|
||||
|
||||
maintenance_contract()
|
||||
|
||||
|
||||
class maintenance_contract_wizard(osv.osv_memory):
|
||||
_name = 'maintenance.contract.wizard'
|
||||
|
||||
_columns = {
|
||||
'name' : fields.char('Contract ID', size=256, required=True ),
|
||||
'password' : fields.char('Password', size=64, required=True),
|
||||
'state' : fields.selection([('draft', 'Draft'),('validated', 'Validated'),('unvalidated', 'Unvalidated')], 'States'),
|
||||
}
|
||||
|
||||
_defaults = {
|
||||
'state' : lambda *a: 'draft',
|
||||
}
|
||||
|
||||
def action_validate(self, cr, uid, ids, context=None):
|
||||
if not ids:
|
||||
return False
|
||||
|
||||
module_proxy = self.pool.get('ir.module.module')
|
||||
module_ids = module_proxy.search(cr, uid, [('state', '=', 'installed')])
|
||||
modules = module_proxy.read(cr, uid, module_ids, ['name', 'installed_version'])
|
||||
|
||||
contract = self.read(cr, uid, ids, ['name', 'password'])[0]
|
||||
|
||||
try:
|
||||
contract_info = tm.remote_contract(contract['name'], contract['password'], modules)
|
||||
except tm.RemoteContractException, rce:
|
||||
raise osv.except_osv(_('Error'), ustr(rce))
|
||||
|
||||
is_ok = contract_info['status'] in ('partial', 'full')
|
||||
if is_ok:
|
||||
module_ids = []
|
||||
if contract_info['modules_with_contract']:
|
||||
for name, version in contract_info['modules_with_contract']:
|
||||
contract_module = self.pool.get('maintenance.contract.module')
|
||||
res = contract_module.search(cr, uid, [('name', '=', name),('version', '=', version)])
|
||||
if not res:
|
||||
id = contract_module.create(cr, uid, { 'name' : name, 'version' : version } )
|
||||
else:
|
||||
id = res[0]
|
||||
module_ids.append(id)
|
||||
|
||||
self.pool.get('maintenance.contract').create(
|
||||
cr,
|
||||
uid, {
|
||||
'name' : contract['name'],
|
||||
'password' : contract['password'],
|
||||
'date_start' : contract_info['date_from'],
|
||||
'date_stop' : contract_info['date_to'],
|
||||
'kind' : contract_info['status'],
|
||||
'module_ids' : [(6,0,module_ids)],
|
||||
}
|
||||
)
|
||||
|
||||
return self.write(cr, uid, ids, {'state' : ('unvalidated', 'validated')[is_ok] }, context=context)
|
||||
|
||||
maintenance_contract_wizard()
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
|
@ -1,131 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data>
|
||||
|
||||
<record id="maintenance_contract_tree_view" model="ir.ui.view">
|
||||
<field name="name">maintenance.contract.tree</field>
|
||||
<field name="model">maintenance.contract</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Maintenance Contract">
|
||||
<field name="name"/>
|
||||
<field name="date_start"/>
|
||||
<field name="date_stop"/>
|
||||
<field name="state" />
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="maintenance_contract_form_view" model="ir.ui.view">
|
||||
<field name="name">maintenance.contract.form</field>
|
||||
<field name="model">maintenance.contract</field>
|
||||
<field name="type">form</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Maintenance Contract">
|
||||
<group col="6" colspan="4">
|
||||
<field name="name"/>
|
||||
<field name="date_start"/>
|
||||
<field name="date_stop"/>
|
||||
</group>
|
||||
<separator string="Covered Modules" colspan="4"/>
|
||||
<field name="module_ids" nolabel="1" colspan="4">
|
||||
<tree string="Covered Modules">
|
||||
<field name="name" />
|
||||
<field name="version" />
|
||||
</tree>
|
||||
</field>
|
||||
<field name="state" colspan="4"/>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="maintenance_contract_search_view" model="ir.ui.view">
|
||||
<field name="name">maintenance.contract.search</field>
|
||||
<field name="model">maintenance.contract</field>
|
||||
<field name="type">search</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Maintenance Contract">
|
||||
<field name="name"/>
|
||||
<field name="date_start"/>
|
||||
<field name="date_stop"/>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="maintenance_contract_view_calendar" model="ir.ui.view">
|
||||
<field name="name">maintenance.contract.calendar</field>
|
||||
<field name="model">maintenance.contract</field>
|
||||
<field name="type">calendar</field>
|
||||
<field name="arch" type="xml">
|
||||
<calendar string="Maintenance Contract" date_start="date_start" color="state">
|
||||
<field name="name"/>
|
||||
<field name="state"/>
|
||||
</calendar>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_maintenance_contract_form" model="ir.actions.act_window">
|
||||
<field name="name">Maintenance Contracts</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">maintenance.contract</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form,calendar</field>
|
||||
<field name="search_view_id" ref="maintenance_contract_search_view"/>
|
||||
</record>
|
||||
|
||||
<menuitem
|
||||
name="Maintenance"
|
||||
id="maintenance"
|
||||
parent="base.menu_administration" groups="base.group_extended"/>
|
||||
|
||||
<menuitem
|
||||
action="action_maintenance_contract_form"
|
||||
id="menu_maintenance_contract"
|
||||
parent="maintenance"/>
|
||||
|
||||
<record id="maintenance_contract_add_wizard" model="ir.ui.view">
|
||||
<field name="name">maintenance.contract.add.wizard</field>
|
||||
<field name="model">maintenance.contract.wizard</field>
|
||||
<field name="type">form</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Add Maintenance Contract" col="2">
|
||||
<group col="1">
|
||||
<separator string="Add Maintenance Contract" />
|
||||
<group states="draft">
|
||||
<field name="name" width="250" />
|
||||
<newline />
|
||||
<field name="password" password="True" />
|
||||
<field name="state" invisible="1" />
|
||||
</group>
|
||||
<group states="validated">
|
||||
<label string="Maintenance contract added !"/>
|
||||
</group>
|
||||
<group states="unvalidated">
|
||||
<label string="Could you check your contract information ?" />
|
||||
</group>
|
||||
</group>
|
||||
<group colspan="4">
|
||||
<button type="object" string="_Cancel" icon="gtk-cancel" special="cancel" states="draft"/>
|
||||
<button type="object" string="_Validate" icon="gtk-apply" name="action_validate" states="draft"/>
|
||||
<button type="object" string="_Close" icon="gtk-close" special="cancel" states="validated,unvalidated"/>
|
||||
</group>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_maintenance_contract_add_wizard" model="ir.actions.act_window">
|
||||
<field name="name">Add Maintenance Contract</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">maintenance.contract.wizard</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="target">new</field>
|
||||
</record>
|
||||
|
||||
<menuitem
|
||||
action="action_maintenance_contract_add_wizard"
|
||||
id="menu_maintenance_contract_add"
|
||||
parent="maintenance" />
|
||||
|
||||
</data>
|
||||
</openerp>
|
|
@ -19,15 +19,18 @@
|
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
import base64
|
||||
import cStringIO
|
||||
import imp
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import StringIO
|
||||
import urllib
|
||||
import zipfile
|
||||
import zipimport
|
||||
|
||||
import addons
|
||||
import netsvc
|
||||
import pooler
|
||||
import release
|
||||
import tools
|
||||
|
@ -41,7 +44,7 @@ class module_category(osv.osv):
|
|||
_name = "ir.module.category"
|
||||
_description = "Module Category"
|
||||
|
||||
def _module_nbr(self,cr,uid, ids, prop, unknow_none,context):
|
||||
def _module_nbr(self,cr,uid, ids, prop, unknow_none, context):
|
||||
cr.execute('SELECT category_id, COUNT(*) \
|
||||
FROM ir_module_module \
|
||||
WHERE category_id IN %(ids)s \
|
||||
|
@ -53,12 +56,12 @@ class module_category(osv.osv):
|
|||
result = dict(cr.fetchall())
|
||||
for id in ids:
|
||||
cr.execute('select id from ir_module_category where parent_id=%s', (id,))
|
||||
childs = [c for c, in cr.fetchall()]
|
||||
result[id] = reduce(lambda x,y:x+y, [result.get(c, 0) for c in childs], result.get(id, 0))
|
||||
result[id] = sum([result.get(c, 0) for (c,) in cr.fetchall()],
|
||||
result.get(id, 0))
|
||||
return result
|
||||
|
||||
_columns = {
|
||||
'name': fields.char("Name", size=128, required=True),
|
||||
'name': fields.char("Name", size=128, required=True, select=True),
|
||||
'parent_id': fields.many2one('ir.module.category', 'Parent Category', select=True),
|
||||
'child_ids': fields.one2many('ir.module.category', 'parent_id', 'Child Categories'),
|
||||
'module_nr': fields.function(_module_nbr, method=True, string='Number of Modules', type='integer')
|
||||
|
@ -71,24 +74,25 @@ class module(osv.osv):
|
|||
_description = "Module"
|
||||
__logger = logging.getLogger('base.' + _name)
|
||||
|
||||
def get_module_info(self, name):
|
||||
@classmethod
|
||||
def get_module_info(cls, name):
|
||||
info = {}
|
||||
try:
|
||||
info = addons.load_information_from_description_file(name)
|
||||
if 'version' in info:
|
||||
info['version'] = release.major_version + '.' + info['version']
|
||||
except Exception:
|
||||
self.__logger.debug('Error when trying to fetch informations for '
|
||||
cls.__logger.debug('Error when trying to fetch informations for '
|
||||
'module %s', name, exc_info=True)
|
||||
return info
|
||||
|
||||
def _get_latest_version(self, cr, uid, ids, field_name=None, arg=None, context={}):
|
||||
def _get_latest_version(self, cr, uid, ids, field_name=None, arg=None, context=None):
|
||||
res = dict.fromkeys(ids, '')
|
||||
for m in self.browse(cr, uid, ids):
|
||||
res[m.id] = self.get_module_info(m.name).get('version', '')
|
||||
return res
|
||||
|
||||
def _get_views(self, cr, uid, ids, field_name=None, arg=None, context={}):
|
||||
def _get_views(self, cr, uid, ids, field_name=None, arg=None, context=None):
|
||||
res = {}
|
||||
model_data_obj = self.pool.get('ir.model.data')
|
||||
view_obj = self.pool.get('ir.ui.view')
|
||||
|
@ -97,39 +101,46 @@ class module(osv.osv):
|
|||
mlist = self.browse(cr, uid, ids, context=context)
|
||||
mnames = {}
|
||||
for m in mlist:
|
||||
mnames[m.name] = m.id
|
||||
# skip uninstalled modules below,
|
||||
# no data to find anyway
|
||||
if m.state in ('installed', 'to upgrade', 'to remove'):
|
||||
mnames[m.name] = m.id
|
||||
res[m.id] = {
|
||||
'menus_by_module':'',
|
||||
'reports_by_module':'',
|
||||
'views_by_module': ''
|
||||
'menus_by_module':[],
|
||||
'reports_by_module':[],
|
||||
'views_by_module': []
|
||||
}
|
||||
|
||||
if not mnames:
|
||||
return res
|
||||
|
||||
view_id = model_data_obj.search(cr,uid,[('module','in', mnames.keys()),
|
||||
('model','in',('ir.ui.view','ir.actions.report.xml','ir.ui.menu'))])
|
||||
for data_id in model_data_obj.browse(cr,uid,view_id,context):
|
||||
# We use try except, because views or menus may not exist
|
||||
try:
|
||||
key = data_id['model']
|
||||
key = data_id.model
|
||||
res_mod_dic = res[mnames[data_id.module]]
|
||||
if key=='ir.ui.view':
|
||||
try:
|
||||
v = view_obj.browse(cr,uid,data_id.res_id)
|
||||
aa = v.inherit_id and '* INHERIT ' or ''
|
||||
res[mnames[data_id.module]]['views_by_module'] += aa + v.name + ' ('+v.type+')\n'
|
||||
except Exception:
|
||||
self.__logger.debug(
|
||||
'Unknown error while browsing ir.ui.view[%s]',
|
||||
data_id.res_id, exc_info=True)
|
||||
v = view_obj.browse(cr,uid,data_id.res_id)
|
||||
aa = v.inherit_id and '* INHERIT ' or ''
|
||||
res_mod_dic['views_by_module'].append(aa + v.name + '('+v.type+')')
|
||||
elif key=='ir.actions.report.xml':
|
||||
res[mnames[data_id.module]]['reports_by_module'] += report_obj.browse(cr,uid,data_id.res_id).name + '\n'
|
||||
res_mod_dic['reports_by_module'].append(report_obj.browse(cr,uid,data_id.res_id).name)
|
||||
elif key=='ir.ui.menu':
|
||||
try:
|
||||
m = menu_obj.browse(cr,uid,data_id.res_id)
|
||||
res[mnames[data_id.module]]['menus_by_module'] += m.complete_name + '\n'
|
||||
except Exception:
|
||||
self.__logger.debug(
|
||||
'Unknown error while browsing ir.ui.menu[%s]',
|
||||
data_id.res_id, exc_info=True)
|
||||
except KeyError:
|
||||
res_mod_dic['menus_by_module'].append(menu_obj.browse(cr,uid,data_id.res_id).complete_name)
|
||||
except KeyError, e:
|
||||
self.__logger.warning(
|
||||
'Data not found for reference %s[%s:%s.%s]', data_id.model,
|
||||
data_id.res_id, data_id.model, data_id.name, exc_info=True)
|
||||
pass
|
||||
except Exception, e:
|
||||
self.__logger.warning('Unknown error while browsing %s[%s]',
|
||||
data_id.model, data_id.res_id, exc_info=True)
|
||||
pass
|
||||
for key, value in res.iteritems():
|
||||
for k, v in res[key].iteritems() :
|
||||
res[key][k] = "\n".join(sorted(v))
|
||||
return res
|
||||
|
||||
_columns = {
|
||||
|
@ -180,16 +191,21 @@ class module(osv.osv):
|
|||
}
|
||||
|
||||
_defaults = {
|
||||
'state': lambda *a: 'uninstalled',
|
||||
'demo': lambda *a: False,
|
||||
'license': lambda *a: 'AGPL-3',
|
||||
'state': 'uninstalled',
|
||||
'demo': False,
|
||||
'license': 'AGPL-3',
|
||||
'web': False,
|
||||
}
|
||||
_order = 'name'
|
||||
|
||||
def _name_uniq_msg(self, cr, uid, ids, context=None):
|
||||
return _('The name of the module must be unique !')
|
||||
def _certificate_uniq_msg(self, cr, uid, ids, context=None):
|
||||
return _('The certificate ID of the module must be unique !')
|
||||
|
||||
_sql_constraints = [
|
||||
('name_uniq', 'unique (name)', 'The name of the module must be unique !'),
|
||||
('certificate_uniq', 'unique (certificate)', 'The certificate ID of the module must be unique !')
|
||||
('name_uniq', 'UNIQUE (name)',_name_uniq_msg ),
|
||||
('certificate_uniq', 'UNIQUE (certificate)',_certificate_uniq_msg )
|
||||
]
|
||||
|
||||
def unlink(self, cr, uid, ids, context=None):
|
||||
|
@ -231,8 +247,21 @@ class module(osv.osv):
|
|||
if tools.find_in_path(binary) is None:
|
||||
raise Exception('Unable to find %r in path' % (binary,))
|
||||
|
||||
@classmethod
|
||||
def check_external_dependencies(cls, module_name, newstate='to install'):
|
||||
terp = cls.get_module_info(module_name)
|
||||
try:
|
||||
cls._check_external_dependencies(terp)
|
||||
except Exception, e:
|
||||
if newstate == 'to install':
|
||||
msg = _('Unable to install module "%s" because an external dependency is not met: %s')
|
||||
elif newstate == 'to upgrade':
|
||||
msg = _('Unable to upgrade module "%s" because an external dependency is not met: %s')
|
||||
else:
|
||||
msg = _('Unable to process module "%s" because an external dependency is not met: %s')
|
||||
raise orm.except_orm(_('Error'), msg % (module_name, e.args[0]))
|
||||
|
||||
def state_update(self, cr, uid, ids, newstate, states_to_update, context={}, level=100):
|
||||
def state_update(self, cr, uid, ids, newstate, states_to_update, context=None, level=100):
|
||||
if level<1:
|
||||
raise orm.except_orm(_('Error'), _('Recursion error in modules dependencies !'))
|
||||
demo = False
|
||||
|
@ -240,7 +269,7 @@ class module(osv.osv):
|
|||
mdemo = False
|
||||
for dep in module.dependencies_id:
|
||||
if dep.state == 'unknown':
|
||||
raise orm.except_orm(_('Error'), _("You try to install the module '%s' that depends on the module:'%s'.\nBut this module is not available in your system.") % (module.name, dep.name,))
|
||||
raise orm.except_orm(_('Error'), _("You try to install module '%s' that depends on module '%s'.\nBut the latter module is not available in your system.") % (module.name, dep.name,))
|
||||
ids2 = self.search(cr, uid, [('name','=',dep.name)])
|
||||
if dep.state != newstate:
|
||||
mdemo = self.state_update(cr, uid, ids2, newstate, states_to_update, context, level-1,) or mdemo
|
||||
|
@ -248,11 +277,7 @@ class module(osv.osv):
|
|||
od = self.browse(cr, uid, ids2)[0]
|
||||
mdemo = od.demo or mdemo
|
||||
|
||||
terp = self.get_module_info(module.name)
|
||||
try:
|
||||
self._check_external_dependencies(terp)
|
||||
except Exception, e:
|
||||
raise orm.except_orm(_('Error'), _('Unable %s the module "%s" because an external dependencie is not met: %s' % (newstate, module.name, e.args[0])))
|
||||
self.check_external_dependencies(module.name, newstate)
|
||||
if not module.dependencies_id:
|
||||
mdemo = module.demo
|
||||
if module.state in states_to_update:
|
||||
|
@ -260,14 +285,14 @@ class module(osv.osv):
|
|||
demo = demo or mdemo
|
||||
return demo
|
||||
|
||||
def button_install(self, cr, uid, ids, context={}):
|
||||
def button_install(self, cr, uid, ids, context=None):
|
||||
return self.state_update(cr, uid, ids, 'to install', ['uninstalled'], context)
|
||||
|
||||
def button_install_cancel(self, cr, uid, ids, context={}):
|
||||
def button_install_cancel(self, cr, uid, ids, context=None):
|
||||
self.write(cr, uid, ids, {'state': 'uninstalled', 'demo':False})
|
||||
return True
|
||||
|
||||
def button_uninstall(self, cr, uid, ids, context={}):
|
||||
def button_uninstall(self, cr, uid, ids, context=None):
|
||||
for module in self.browse(cr, uid, ids):
|
||||
cr.execute('''select m.state,m.name
|
||||
from
|
||||
|
@ -283,7 +308,7 @@ class module(osv.osv):
|
|||
self.write(cr, uid, ids, {'state': 'to remove'})
|
||||
return True
|
||||
|
||||
def button_uninstall_cancel(self, cr, uid, ids, context={}):
|
||||
def button_uninstall_cancel(self, cr, uid, ids, context=None):
|
||||
self.write(cr, uid, ids, {'state': 'installed'})
|
||||
return True
|
||||
|
||||
|
@ -299,6 +324,7 @@ class module(osv.osv):
|
|||
if mod.state not in ('installed','to upgrade'):
|
||||
raise orm.except_orm(_('Error'),
|
||||
_("Can not upgrade module '%s'. It is not installed.") % (mod.name,))
|
||||
self.check_external_dependencies(mod.name, 'to upgrade')
|
||||
iids = depobj.search(cr, uid, [('name', '=', mod.name)], context=context)
|
||||
for dep in depobj.browse(cr, uid, iids, context=context):
|
||||
if dep.module_id.state=='installed' and dep.module_id not in todo:
|
||||
|
@ -319,7 +345,7 @@ class module(osv.osv):
|
|||
self.button_install(cr, uid, to_install, context=context)
|
||||
return True
|
||||
|
||||
def button_upgrade_cancel(self, cr, uid, ids, context={}):
|
||||
def button_upgrade_cancel(self, cr, uid, ids, context=None):
|
||||
self.write(cr, uid, ids, {'state': 'installed'})
|
||||
return True
|
||||
def button_update_translations(self, cr, uid, ids, context=None):
|
||||
|
@ -335,8 +361,8 @@ class module(osv.osv):
|
|||
'maintainer': terp.get('maintainer', False),
|
||||
'contributors': ', '.join(terp.get('contributors', [])) or False,
|
||||
'website': terp.get('website', ''),
|
||||
'license': terp.get('license', 'GPL-2'),
|
||||
'certificate': terp.get('certificate') or None,
|
||||
'license': terp.get('license', 'AGPL-3'),
|
||||
'certificate': terp.get('certificate') or False,
|
||||
'web': terp.get('web') or False,
|
||||
}
|
||||
|
||||
|
@ -344,34 +370,40 @@ class module(osv.osv):
|
|||
def update_list(self, cr, uid, context={}):
|
||||
res = [0, 0] # [update, add]
|
||||
|
||||
# iterate through installed modules and mark them as being so
|
||||
known_mods = self.browse(cr, uid, self.search(cr, uid, []))
|
||||
known_mods_names = dict([(m.name, m) for m in known_mods])
|
||||
|
||||
# iterate through detected modules and update/create them in db
|
||||
for mod_name in addons.get_modules():
|
||||
ids = self.search(cr, uid, [('name','=',mod_name)])
|
||||
mod = known_mods_names.get(mod_name)
|
||||
terp = self.get_module_info(mod_name)
|
||||
values = self.get_values_from_terp(terp)
|
||||
|
||||
if ids:
|
||||
id = ids[0]
|
||||
mod = self.browse(cr, uid, id)
|
||||
if mod:
|
||||
updated_values = {}
|
||||
for key in values:
|
||||
old = getattr(mod, key)
|
||||
updated = isinstance(values[key], basestring) and tools.ustr(values[key]) or values[key]
|
||||
if not old == updated:
|
||||
updated_values[key] = values[key]
|
||||
if terp.get('installable', True) and mod.state == 'uninstallable':
|
||||
self.write(cr, uid, id, {'state': 'uninstalled'})
|
||||
updated_values['state'] = 'uninstalled'
|
||||
if parse_version(terp.get('version', '')) > parse_version(mod.latest_version or ''):
|
||||
self.write(cr, uid, id, {'url': ''})
|
||||
res[0] += 1
|
||||
self.write(cr, uid, id, values)
|
||||
cr.execute('DELETE FROM ir_module_module_dependency WHERE module_id = %s', (id,))
|
||||
if updated_values:
|
||||
self.write(cr, uid, mod.id, updated_values)
|
||||
else:
|
||||
mod_path = addons.get_module_path(mod_name)
|
||||
if not mod_path:
|
||||
continue
|
||||
if not terp or not terp.get('installable', True):
|
||||
continue
|
||||
|
||||
ids = self.search(cr, uid, [('name','=',mod_name)])
|
||||
id = self.create(cr, uid, dict(name=mod_name, state='uninstalled', **values))
|
||||
mod = self.browse(cr, uid, id)
|
||||
res[1] += 1
|
||||
self._update_dependencies(cr, uid, id, terp.get('depends', []))
|
||||
self._update_category(cr, uid, id, terp.get('category', 'Uncategorized'))
|
||||
|
||||
self._update_dependencies(cr, uid, mod, terp.get('depends', []))
|
||||
self._update_category(cr, uid, mod, terp.get('category', 'Uncategorized'))
|
||||
|
||||
return res
|
||||
|
||||
|
@ -403,40 +435,52 @@ class module(osv.osv):
|
|||
self.write(cr, uid, mod.id, self.get_values_from_terp(terp))
|
||||
cr.execute('DELETE FROM ir_module_module_dependency ' \
|
||||
'WHERE module_id = %s', (mod.id,))
|
||||
self._update_dependencies(cr, uid, mod.id, terp.get('depends',
|
||||
self._update_dependencies(cr, uid, mod, terp.get('depends',
|
||||
[]))
|
||||
self._update_category(cr, uid, mod.id, terp.get('category',
|
||||
self._update_category(cr, uid, mod, terp.get('category',
|
||||
'Uncategorized'))
|
||||
# Import module
|
||||
zimp = zipimport.zipimporter(fname)
|
||||
zimp.load_module(mod.name)
|
||||
return res
|
||||
|
||||
def _update_dependencies(self, cr, uid, id, depends=[]):
|
||||
for d in depends:
|
||||
cr.execute('INSERT INTO ir_module_module_dependency (module_id, name) values (%s, %s)', (id, d))
|
||||
def _update_dependencies(self, cr, uid, mod_browse, depends=None):
|
||||
if depends is None:
|
||||
depends = []
|
||||
existing = set(x.name for x in mod_browse.dependencies_id)
|
||||
needed = set(depends)
|
||||
for dep in (needed - existing):
|
||||
cr.execute('INSERT INTO ir_module_module_dependency (module_id, name) values (%s, %s)', (mod_browse.id, dep))
|
||||
for dep in (existing - needed):
|
||||
cr.execute('DELETE FROM ir_module_module_dependency WHERE module_id = %s and name = %s', (mod_browse.id, dep))
|
||||
|
||||
def _update_category(self, cr, uid, mod_browse, category='Uncategorized'):
|
||||
current_category = mod_browse.category_id
|
||||
current_category_path = []
|
||||
while current_category:
|
||||
current_category_path.insert(0, current_category.name)
|
||||
current_category = current_category.parent_id
|
||||
|
||||
def _update_category(self, cr, uid, id, category='Uncategorized'):
|
||||
categs = category.split('/')
|
||||
p_id = None
|
||||
while categs:
|
||||
if p_id is not None:
|
||||
cr.execute('select id from ir_module_category where name=%s and parent_id=%s', (categs[0], p_id))
|
||||
else:
|
||||
cr.execute('select id from ir_module_category where name=%s and parent_id is NULL', (categs[0],))
|
||||
c_id = cr.fetchone()
|
||||
if not c_id:
|
||||
cr.execute('select nextval(\'ir_module_category_id_seq\')')
|
||||
c_id = cr.fetchone()[0]
|
||||
cr.execute('insert into ir_module_category (id, name, parent_id) values (%s, %s, %s)', (c_id, categs[0], p_id))
|
||||
else:
|
||||
c_id = c_id[0]
|
||||
p_id = c_id
|
||||
categs = categs[1:]
|
||||
self.write(cr, uid, [id], {'category_id': p_id})
|
||||
if categs != current_category_path:
|
||||
p_id = None
|
||||
while categs:
|
||||
if p_id is not None:
|
||||
cr.execute('SELECT id FROM ir_module_category WHERE name=%s AND parent_id=%s', (categs[0], p_id))
|
||||
else:
|
||||
cr.execute('SELECT id FROM ir_module_category WHERE name=%s AND parent_id is NULL', (categs[0],))
|
||||
c_id = cr.fetchone()
|
||||
if not c_id:
|
||||
cr.execute('INSERT INTO ir_module_category (name, parent_id) VALUES (%s, %s) RETURNING id', (categs[0], p_id))
|
||||
c_id = cr.fetchone()[0]
|
||||
else:
|
||||
c_id = c_id[0]
|
||||
p_id = c_id
|
||||
categs = categs[1:]
|
||||
self.write(cr, uid, [mod_browse.id], {'category_id': p_id})
|
||||
|
||||
def update_translations(self, cr, uid, ids, filter_lang=None, context={}):
|
||||
logger = netsvc.Logger()
|
||||
logger = logging.getLogger('i18n')
|
||||
if not filter_lang:
|
||||
pool = pooler.get_pool(cr.dbname)
|
||||
lang_obj = pool.get('res.lang')
|
||||
|
@ -453,16 +497,28 @@ class module(osv.osv):
|
|||
# unable to find the module. we skip
|
||||
continue
|
||||
for lang in filter_lang:
|
||||
if len(lang) > 5:
|
||||
raise osv.except_osv(_('Error'), _('You Can Not Load Translation For language Due To Invalid Language/Country Code'))
|
||||
iso_lang = tools.get_iso_codes(lang)
|
||||
f = os.path.join(modpath, 'i18n', iso_lang + '.po')
|
||||
if not os.path.exists(f) and iso_lang.find('_') != -1:
|
||||
f = os.path.join(modpath, 'i18n', iso_lang.split('_')[0] + '.po')
|
||||
f = addons.get_module_resource(mod.name, 'i18n', iso_lang + '.po')
|
||||
context2 = context and context.copy() or {}
|
||||
if f and '_' in iso_lang:
|
||||
iso_lang2 = iso_lang.split('_')[0]
|
||||
f2 = addons.get_module_resource(mod.name, 'i18n', iso_lang2 + '.po')
|
||||
if f2:
|
||||
logger.info('module %s: loading base translation file %s for language %s', mod.name, iso_lang2, lang)
|
||||
tools.trans_load(cr, f2, lang, verbose=False, context=context)
|
||||
context2['overwrite'] = True
|
||||
# Implementation notice: we must first search for the full name of
|
||||
# the language derivative, like "en_UK", and then the generic,
|
||||
# like "en".
|
||||
if (not f) and '_' in iso_lang:
|
||||
iso_lang = iso_lang.split('_')[0]
|
||||
if os.path.exists(f):
|
||||
logger.notifyChannel("i18n", netsvc.LOG_INFO, 'module %s: loading translation file for language %s' % (mod.name, iso_lang))
|
||||
tools.trans_load(cr.dbname, f, lang, verbose=False, context=context)
|
||||
f = addons.get_module_resource(mod.name, 'i18n', iso_lang + '.po')
|
||||
if f:
|
||||
logger.info('module %s: loading translation file (%s) for language %s', mod.name, iso_lang, lang)
|
||||
tools.trans_load(cr, f, lang, verbose=False, context=context2)
|
||||
elif iso_lang != 'en':
|
||||
logger.warning('module %s: no translation for language %s', mod.name, iso_lang)
|
||||
tools.trans_update_res_ids(cr)
|
||||
|
||||
def check(self, cr, uid, ids, context=None):
|
||||
logger = logging.getLogger('init')
|
||||
|
@ -479,13 +535,13 @@ class module(osv.osv):
|
|||
raise osv.except_osv(_('Error'), _('Module %s: Invalid Quality Certificate') % (mod.name,))
|
||||
|
||||
def list_web(self, cr, uid, context=None):
|
||||
""" list_web(cr, uid, context) -> [module_name]
|
||||
""" list_web(cr, uid, context) -> [(module_name, module_version)]
|
||||
Lists all the currently installed modules with a web component.
|
||||
|
||||
Returns a list of addon names.
|
||||
Returns a list of a tuple of addon names and addon versions.
|
||||
"""
|
||||
return [
|
||||
module['name']
|
||||
(module['name'], module['installed_version'])
|
||||
for module in self.browse(cr, uid,
|
||||
self.search(cr, uid,
|
||||
[('web', '=', True),
|
||||
|
@ -502,6 +558,58 @@ class module(osv.osv):
|
|||
else:
|
||||
self._web_dependencies(
|
||||
cr, uid, parent, context=context)
|
||||
|
||||
def _translations_subdir(self, module):
|
||||
""" Returns the path to the subdirectory holding translations for the
|
||||
module files, or None if it can't find one
|
||||
|
||||
:param module: a module object
|
||||
:type module: browse(ir.module.module)
|
||||
"""
|
||||
subdir = addons.get_module_resource(module.name, 'po')
|
||||
if subdir: return subdir
|
||||
# old naming convention
|
||||
subdir = addons.get_module_resource(module.name, 'i18n')
|
||||
if subdir: return subdir
|
||||
return None
|
||||
|
||||
def _add_translations(self, module, web_data):
|
||||
""" Adds translation data to a zipped web module
|
||||
|
||||
:param module: a module descriptor
|
||||
:type module: browse(ir.module.module)
|
||||
:param web_data: zipped data of a web module
|
||||
:type web_data: bytes
|
||||
"""
|
||||
# cStringIO.StringIO is either read or write, not r/w
|
||||
web_zip = StringIO.StringIO(web_data)
|
||||
web_archive = zipfile.ZipFile(web_zip, 'a')
|
||||
|
||||
# get the contents of the i18n or po folder and move them to the
|
||||
# po/messages subdirectory of the web module.
|
||||
# The POT file will be incorrectly named, but that should not
|
||||
# matter since the web client is not going to use it, only the PO
|
||||
# files.
|
||||
translations_file = cStringIO.StringIO(
|
||||
addons.zip_directory(self._translations_subdir(module), False))
|
||||
translations_archive = zipfile.ZipFile(translations_file)
|
||||
|
||||
for path in translations_archive.namelist():
|
||||
web_path = os.path.join(
|
||||
'web', 'po', 'messages', os.path.basename(path))
|
||||
web_archive.writestr(
|
||||
web_path,
|
||||
translations_archive.read(path))
|
||||
|
||||
translations_archive.close()
|
||||
translations_file.close()
|
||||
|
||||
web_archive.close()
|
||||
try:
|
||||
return web_zip.getvalue()
|
||||
finally:
|
||||
web_zip.close()
|
||||
|
||||
def get_web(self, cr, uid, names, context=None):
|
||||
""" get_web(cr, uid, [module_name], context) -> [{name, depends, content}]
|
||||
|
||||
|
@ -516,14 +624,21 @@ class module(osv.osv):
|
|||
if not modules: return []
|
||||
self.__logger.info('Sending web content of modules %s '
|
||||
'to web client', names)
|
||||
return [
|
||||
{'name': module.name,
|
||||
'depends': list(self._web_dependencies(
|
||||
cr, uid, module, context=context)),
|
||||
'content': addons.zip_directory(
|
||||
addons.get_module_resource(module.name, 'web'))}
|
||||
for module in modules
|
||||
]
|
||||
|
||||
modules_data = []
|
||||
for module in modules:
|
||||
web_data = addons.zip_directory(
|
||||
addons.get_module_resource(module.name, 'web'), False)
|
||||
if self._translations_subdir(module):
|
||||
web_data = self._add_translations(module, web_data)
|
||||
modules_data.append({
|
||||
'name': module.name,
|
||||
'version': module.installed_version,
|
||||
'depends': list(self._web_dependencies(
|
||||
cr, uid, module, context=context)),
|
||||
'content': base64.encodestring(web_data)
|
||||
})
|
||||
return modules_data
|
||||
|
||||
module()
|
||||
|
||||
|
@ -531,7 +646,7 @@ class module_dependency(osv.osv):
|
|||
_name = "ir.module.module.dependency"
|
||||
_description = "Module dependency"
|
||||
|
||||
def _state(self, cr, uid, ids, name, args, context={}):
|
||||
def _state(self, cr, uid, ids, name, args, context=None):
|
||||
result = {}
|
||||
mod_obj = self.pool.get('ir.module.module')
|
||||
for md in self.browse(cr, uid, ids):
|
||||
|
|
|
@ -68,7 +68,6 @@
|
|||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="domain">[('category_id','=',active_id)]</field>
|
||||
|
||||
</record>
|
||||
<record id="ir_action_module_category" model="ir.values">
|
||||
<field eval="'tree_but_open'" name="key2"/>
|
||||
|
@ -169,7 +168,8 @@
|
|||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="domain"/>
|
||||
<field name="search_view_id" ref="view_module_filter"/>
|
||||
<field name="search_view_id" ref="view_module_filter"/>
|
||||
<field name="help">You can install new modules in order to activate new features, menu, reports or data in your OpenERP instance. To install some modules, click on the button "Schedule for Installation" from the form view, then click on "Apply Scheduled Upgrades" to migrate your system.</field>
|
||||
</record>
|
||||
<menuitem action="open_module_tree" id="menu_module_tree" parent="base.menu_management"/>
|
||||
|
||||
|
|
|
@ -73,6 +73,7 @@ class ir_module_reference_print(report_sxw.rml_parse):
|
|||
def _fields_find(self, obj):
|
||||
modobj = self.pool.get(obj)
|
||||
res = modobj.fields_get(self.cr, self.uid).items()
|
||||
res.sort()
|
||||
return res
|
||||
|
||||
report_sxw.report_sxw('report.ir.module.reference', 'ir.module.module',
|
||||
|
|
|
@ -47,7 +47,7 @@ class base_language_export(osv.osv_memory):
|
|||
mods = map(lambda m: m.name, this.modules) or ['all']
|
||||
mods.sort()
|
||||
buf=cStringIO.StringIO()
|
||||
tools.trans_export(this.lang, mods, buf, this.format, dbname=cr.dbname)
|
||||
tools.trans_export(this.lang, mods, buf, this.format, cr)
|
||||
if this.format == 'csv':
|
||||
this.advice = _("Save this document to a .CSV file and open it with your favourite spreadsheet software. The file encoding is UTF-8. You have to translate the latest column before reimporting it.")
|
||||
elif this.format == 'po':
|
||||
|
@ -86,4 +86,4 @@ class base_language_export(osv.osv_memory):
|
|||
}
|
||||
base_language_export()
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
|
|
@ -56,8 +56,9 @@ class base_language_import(osv.osv_memory):
|
|||
fileformat = first_line.endswith("type,name,res_id,src,value") and 'csv' or 'po'
|
||||
fileobj.seek(0)
|
||||
|
||||
tools.trans_load_data(cr.dbname, fileobj, fileformat, import_data.code, lang_name=import_data.name)
|
||||
tools.trans_load_data(cr, fileobj, fileformat, import_data.code, lang_name=import_data.name)
|
||||
tools.trans_update_res_ids(cr)
|
||||
fileobj.close()
|
||||
return {}
|
||||
|
||||
base_language_import()
|
||||
base_language_import()
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
<separator orientation="vertical" rowspan="15"/>
|
||||
<group colspan="4" col="4">
|
||||
<separator string="Import Translation" colspan="4"/>
|
||||
<label colspan="4" nolabel="1" string="If you need another language than the official ones available, you can import a language pack from here. Other OpenERP languages than the official ones can be found on launchpad."/>
|
||||
<newline/>
|
||||
<field name="name" width="200"/>
|
||||
<field name="code"/>
|
||||
<field name="data" colspan="4"/>
|
||||
|
|
|
@ -38,7 +38,9 @@ class base_language_install(osv.osv_memory):
|
|||
'state': 'init',
|
||||
'overwrite': False
|
||||
}
|
||||
def lang_install(self, cr, uid, ids, context):
|
||||
def lang_install(self, cr, uid, ids, context=None):
|
||||
if context is None:
|
||||
context = {}
|
||||
language_obj = self.browse(cr, uid, ids)[0]
|
||||
lang = language_obj.lang
|
||||
if lang:
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
<field name="name">Start Configuration</field>
|
||||
<field name="model_id" ref="model_base_module_configuration"/>
|
||||
<field name="state">code</field>
|
||||
<field name="code">action = obj.start([])</field>
|
||||
<field name="code">action = obj.start()</field>
|
||||
</record>
|
||||
|
||||
<menuitem name="Start Configuration"
|
||||
|
|
|
@ -56,7 +56,7 @@ class base_module_import(osv.osv_memory):
|
|||
fname = fdata.namelist()[0]
|
||||
module_name = os.path.split(fname)[0]
|
||||
|
||||
ad = tools.config['addons_path']
|
||||
ad = tools.config['addons_path'].split(",")[-1]
|
||||
|
||||
fname = os.path.join(ad, module_name+'.zip')
|
||||
try:
|
||||
|
|
|
@ -9,22 +9,22 @@
|
|||
<field name="arch" type="xml">
|
||||
<form string="Import module">
|
||||
<group col="8">
|
||||
<group colspan="3">
|
||||
<group colspan="3" col="1">
|
||||
<field name="config_logo" widget="image" width="220" height="130" nolabel="1" colspan="1"/>
|
||||
<newline/>
|
||||
<label colspan="4" width="220" string="This wizard helps you add a new language to you OpenERP system. After loading a new language it becomes available as default interface language for users and partners."/>
|
||||
<label colspan="4" width="220"/>
|
||||
<label colspan="4" width="220" string="Please be patient, this operation may take a few minutes (depending on the number of modules currently installed)..."/>
|
||||
<label width="220" string="This wizard helps you add a new language to you OpenERP system. After loading a new language it becomes available as default interface language for users and partners."/>
|
||||
<label width="220"/>
|
||||
<label width="220" string="Please be patient, this operation may take a few minutes (depending on the number of modules currently installed)..."/>
|
||||
<field name="state" invisible="1"/>
|
||||
</group>
|
||||
<separator orientation="vertical" rowspan="15"/>
|
||||
<separator orientation="vertical" rowspan="5"/>
|
||||
<group colspan="4">
|
||||
<separator string="Module Import" colspan="4"/>
|
||||
<group states="init" colspan="4" col="4">
|
||||
<group states="init" col="4">
|
||||
<label string="Select module package to import (.zip file):" colspan="4"/>
|
||||
<field name="module_file" colspan="4"/>
|
||||
</group>
|
||||
<group states="done" colspan="4" col="4">
|
||||
<group states="done" col="4">
|
||||
<label string="Module file successfully imported!" colspan="4"/>
|
||||
</group>
|
||||
</group>
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
|
||||
import pooler
|
||||
from osv import osv, fields
|
||||
from tools.translate import _
|
||||
|
||||
class base_module_upgrade(osv.osv_memory):
|
||||
""" Module Upgrade """
|
||||
|
@ -80,24 +81,23 @@ class base_module_upgrade(osv.osv_memory):
|
|||
res = mod_obj.read(cr, uid, ids, ['name','state'], context)
|
||||
return {'module_info': '\n'.join(map(lambda x: x['name']+' : '+x['state'], res))}
|
||||
|
||||
def upgrade_module(self, cr, uid, ids, context):
|
||||
pool = pooler.get_pool(cr.dbname)
|
||||
def upgrade_module(self, cr, uid, ids, context=None):
|
||||
mod_obj = self.pool.get('ir.module.module')
|
||||
data_obj = self.pool.get('ir.model.data')
|
||||
ids = mod_obj.search(cr, uid, [('state', 'in', ['to upgrade', 'to remove', 'to install'])])
|
||||
unmet_packages = []
|
||||
mod_dep_obj = pool.get('ir.module.module.dependency')
|
||||
mod_dep_obj = self.pool.get('ir.module.module.dependency')
|
||||
for mod in mod_obj.browse(cr, uid, ids):
|
||||
depends_mod_ids = mod_dep_obj.search(cr, uid, [('module_id', '=', mod.id)])
|
||||
for dep_mod in mod_dep_obj.browse(cr, uid, depends_mod_ids):
|
||||
if dep_mod.state in ('unknown','uninstalled'):
|
||||
unmet_packages.append(dep_mod.name)
|
||||
if len(unmet_packages):
|
||||
raise osv.except_osv('Unmet dependency !', 'Following modules are uninstalled or unknown. \n\n'+'\n'.join(unmet_packages))
|
||||
raise osv.except_osv(_('Unmet dependency !'), _('Following modules are not installed or unknown: %s') % ('\n\n' + '\n'.join(unmet_packages)))
|
||||
mod_obj.download(cr, uid, ids, context=context)
|
||||
cr.commit()
|
||||
_, pool = pooler.restart_pool(cr.dbname, update_module=True)
|
||||
_db, pool = pooler.restart_pool(cr.dbname, update_module=True)
|
||||
|
||||
data_obj = pool.get('ir.model.data')
|
||||
id2 = data_obj._get_id(cr, uid, 'base', 'view_base_module_upgrade_install')
|
||||
if id2:
|
||||
id2 = data_obj.browse(cr, uid, id2, context=context).res_id
|
||||
|
|
|
@ -46,6 +46,7 @@
|
|||
<field name="name">Module Upgrade Install</field>
|
||||
<field name="model">base.module.upgrade</field>
|
||||
<field name="type">form</field>
|
||||
<field name="priority" eval="20"/>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Apply Scheduled Upgrades">
|
||||
<separator string="System update completed" colspan="4"/>
|
||||
|
|
|
@ -35,7 +35,7 @@ class base_update_translations(osv.osv_memory):
|
|||
lang_obj=pooler.get_pool(cr.dbname).get('res.lang')
|
||||
ids=lang_obj.search(cr, uid, [('code', '=', lang_code)])
|
||||
if not ids:
|
||||
raise osv.except_osv(_('No language with code "%s" exists') % lang_code)
|
||||
raise osv.except_osv(_('Error!'), _('No language with code "%s" exists') % lang_code)
|
||||
lang = lang_obj.browse(cr, uid, ids[0])
|
||||
return lang.name
|
||||
def act_cancel(self, cr, uid, ids, context=None):
|
||||
|
@ -45,14 +45,21 @@ class base_update_translations(osv.osv_memory):
|
|||
this = self.browse(cr, uid, ids)[0]
|
||||
lang_name = self._get_lang_name(cr, uid, this.lang)
|
||||
buf=cStringIO.StringIO()
|
||||
tools.trans_export(this.lang, ['all'], buf, 'csv', dbname=cr.dbname)
|
||||
tools.trans_load_data(cr.dbname, buf, 'csv', this.lang, lang_name=lang_name)
|
||||
tools.trans_export(this.lang, ['all'], buf, 'csv', cr)
|
||||
tools.trans_load_data(cr, buf, 'csv', this.lang, lang_name=lang_name)
|
||||
tools.trans_update_res_ids(cr)
|
||||
buf.close()
|
||||
return {'type': 'ir.actions.act_window_close'}
|
||||
|
||||
def default_get(self, cr, uid, fields, context=None):
|
||||
if context is None:
|
||||
context = {}
|
||||
res = super(base_update_translations, self).default_get(cr, uid, fields, context=context)
|
||||
record_id = context and context.get('active_id', False) or False
|
||||
|
||||
if context.get('active_model') != "res.lang":
|
||||
return res
|
||||
|
||||
record_id = context.get('active_id', False) or False
|
||||
if record_id:
|
||||
lang = self.pool.get('res.lang').browse(cr, uid, record_id).code
|
||||
res.update(lang=lang)
|
||||
|
@ -64,4 +71,4 @@ class base_update_translations(osv.osv_memory):
|
|||
'lang': fields.selection(_get_languages, 'Language', required=True),
|
||||
}
|
||||
|
||||
base_update_translations()
|
||||
base_update_translations()
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue