[MERGE] sync with 7.0

bzr revid: mat@openerp.com-20140213095713-b1ys0nu8u7o8ybyr
This commit is contained in:
Martin Trigaux 2014-02-13 10:57:13 +01:00
commit 6f8811e7f6
125 changed files with 106811 additions and 43810 deletions

View File

@ -37,7 +37,6 @@ There are two types of views:
.. note:: Since OpenERP 4.1, form views can also contain graphs.
Form views
----------
@ -388,6 +387,33 @@ The easiest method to compute real statistics on objects is:
You can get en example in all modules of the form: report\_.... Example: report_crm.
Controlling view actions
------------------------
When defining a view, the following attributes can be added on the
opening element of the view (i.e. ``<form>``, ``<tree>``...)
``create``
set to ``false`` to hide the link / button which allows to create a new
record.
``delete``
set to ``false`` to hide the link / button which allows to remove a
record.
``edit``
set to ``false`` to hide the link / button which allows to
edit a record.
These attributes are available on form, tree, kanban and gantt
views. They are normally automatically set from the access rights of
the users, but can be forced globally in the view definition. A
possible use case for these attributes is to define an inner tree view
for a one2many relation inside a form view, in which the user cannot
add or remove related records, but only edit the existing ones (which
are presumably created through another way, such as a wizard).
Calendar Views
--------------
@ -680,6 +706,7 @@ toolbar
its descendants will be displayed in the main tree. The value is ignored
for flat lists.
Grouping Elements
+++++++++++++++++
@ -1351,12 +1378,22 @@ When you add a one2many field in a form view, you do something like this :
If you want to specify the views to use, you can add a *context* attribute, and
specify a view id for each type of view supported, exactly like the action's
*view_id* attribute:
*view_id* attribute, except that the provided view id must always be
fully-qualified with the module name, even if it belongs to the same module:
.. code-block:: xml
<field name="order_line" colspan="4" nolabel="1"
context="{'form_view_ref' : 'module.view_id', 'tree_view_ref' : 'model.view_id'}"/>
context="{'form_view_ref': 'module.view_id',
'tree_view_ref': 'module.view_id'}"/>
.. note::
You *have to* put the module name in the view_id, because this
is evaluated when the view is displayed, and not when the XML file
is parsed, so the module name information is not available. Failing
to do so will result in the default view being selected (see
below).
If you don't specify the views, OpenERP will choose one in this order :

14
doc/changelog.rst Normal file
View File

@ -0,0 +1,14 @@
.. _changelog:
Changelog
=========
`7.0`
-----
- Modules may now include an ``i18n_extra`` directory that will be treated like the
default ``i18n`` directory. This is typically useful for manual translation files
that are not managed by Launchpad's translation system. An example is l10n modules
that depend on ``l10n_multilang``.

View File

@ -49,8 +49,6 @@
<field name="symbol"></field>
<field name="rounding">0.01</field>
<field name="accuracy">4</field>
<!-- Company ID will be set later -->
<field name="company_id" eval="None"/>
</record>
<record id="rateEUR" model="res.currency.rate">
<field name="rate">1.0</field>
@ -86,10 +84,6 @@ Administrator</field>
<field name="company_id" ref="main_company"/>
</record>
<record id="EUR" model="res.currency">
<field name="company_id" ref="main_company"/>
</record>
<record id="ir_mail_server_localhost0" model="ir.mail_server">
<field name="name">localhost</field>
<field name="smtp_host">localhost</field>

View File

@ -466,7 +466,6 @@
<field name="rounding">0.01</field>
<field name="accuracy">4</field>
<field name="symbol">¢</field>
<field name="company_id" ref="main_company"/>
</record>
<record id="rateCRC" model="res.currency.rate">
<field name="rate">691.3153</field>
@ -1553,7 +1552,7 @@
</record>
<record id="VUB" model="res.currency">
<field name="name">VUB</field>
<field name="name">VEB</field>
<field name="symbol">Bs</field>
<field name="rounding">0.01</field>
<field name="accuracy">4</field>

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

15550
openerp/addons/base/i18n/bn.po Normal file

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

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

View File

@ -2,7 +2,7 @@
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2011 OpenERP S.A. <http://www.openerp.com>
# Copyright (C) 2004-2014 OpenERP S.A. <http://www.openerp.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
@ -100,7 +100,7 @@ class report_xml(osv.osv):
if r['report_rml'] or r['report_rml_content_data']:
report_sxw('report.'+r['report_name'], r['model'],
opj('addons',r['report_rml'] or '/'), header=r['header'])
if r['report_xsl']:
elif r['report_xsl'] and r['report_xml']:
report_rml('report.'+r['report_name'], r['model'],
opj('addons',r['report_xml']),
r['report_xsl'] and opj('addons',r['report_xsl']))
@ -411,7 +411,7 @@ class actions_server(osv.osv):
def _select_objects(self, cr, uid, context=None):
model_pool = self.pool.get('ir.model')
ids = model_pool.search(cr, uid, [('name','not ilike','.')])
ids = model_pool.search(cr, uid, [], limit=None)
res = model_pool.read(cr, uid, ids, ['model', 'name'])
return [(r['model'], r['name']) for r in res] + [('','')]
@ -603,7 +603,7 @@ class actions_server(osv.osv):
if action.state=='client_action':
if not action.action_id:
raise osv.except_osv(_('Error'), _("Please specify an action to launch !"))
raise osv.except_osv(_('Error'), _("Please specify an action to launch!"))
return self.pool.get(action.action_id.type)\
.read(cr, uid, action.action_id.id, context=context)

View File

@ -27,6 +27,7 @@ import re
from openerp import tools
from openerp.osv import fields,osv
from openerp import SUPERUSER_ID
_logger = logging.getLogger(__name__)
@ -142,9 +143,10 @@ class ir_attachment(osv.osv):
if attach.store_fname:
self._file_delete(cr, uid, location, attach.store_fname)
fname = self._file_write(cr, uid, location, value)
super(ir_attachment, self).write(cr, uid, [id], {'store_fname': fname, 'file_size': file_size}, context=context)
# SUPERUSER_ID as probably don't have write access, trigger during create
super(ir_attachment, self).write(cr, SUPERUSER_ID, [id], {'store_fname': fname, 'file_size': file_size}, context=context)
else:
super(ir_attachment, self).write(cr, uid, [id], {'db_datas': value, 'file_size': file_size}, context=context)
super(ir_attachment, self).write(cr, SUPERUSER_ID, [id], {'db_datas': value, 'file_size': file_size}, context=context)
return True
_name = 'ir.attachment'
@ -186,8 +188,6 @@ class ir_attachment(osv.osv):
In the 'document' module, it is overriden to relax this hard rule, since
more complex ones apply there.
"""
if not ids:
return
res_ids = {}
if ids:
if isinstance(ids, (int, long)):
@ -198,7 +198,7 @@ class ir_attachment(osv.osv):
continue
res_ids.setdefault(rmod,set()).add(rid)
if values:
if values.get('res_model') and 'res_id' in values:
if values.get('res_model') and values.get('res_id'):
res_ids.setdefault(values['res_model'],set()).add(values['res_id'])
ima = self.pool.get('ir.model.access')
@ -242,6 +242,8 @@ class ir_attachment(osv.osv):
# performed in batch as much as possible.
ima = self.pool.get('ir.model.access')
for model, targets in model_attachments.iteritems():
if not self.pool.get(model):
continue
if not ima.check(cr, uid, model, 'read', False):
# remove all corresponding attachment ids
for attach_id in itertools.chain(*targets.values()):
@ -261,10 +263,14 @@ class ir_attachment(osv.osv):
return len(result) if count else list(result)
def read(self, cr, uid, ids, fields_to_read=None, context=None, load='_classic_read'):
if isinstance(ids, (int, long)):
ids = [ids]
self.check(cr, uid, ids, 'read', context=context)
return super(ir_attachment, self).read(cr, uid, ids, fields_to_read, context, load)
def write(self, cr, uid, ids, vals, context=None):
if isinstance(ids, (int, long)):
ids = [ids]
self.check(cr, uid, ids, 'write', context=context, values=vals)
if 'file_size' in vals:
del vals['file_size']
@ -275,6 +281,8 @@ class ir_attachment(osv.osv):
return super(ir_attachment, self).copy(cr, uid, id, default, context)
def unlink(self, cr, uid, ids, context=None):
if isinstance(ids, (int, long)):
ids = [ids]
self.check(cr, uid, ids, 'unlink', context=context)
location = self.pool.get('ir.config_parameter').get_param(cr, uid, 'ir_attachment.location')
if location:
@ -284,7 +292,7 @@ class ir_attachment(osv.osv):
return super(ir_attachment, self).unlink(cr, uid, ids, context)
def create(self, cr, uid, values, context=None):
self.check(cr, uid, [], mode='create', context=context, values=values)
self.check(cr, uid, [], mode='write', context=context, values=values)
if 'file_size' in values:
del values['file_size']
return super(ir_attachment, self).create(cr, uid, values, context)

View File

@ -12,6 +12,7 @@ from openerp.osv import orm
from openerp.tools.translate import _
from openerp.tools.misc import DEFAULT_SERVER_DATE_FORMAT,\
DEFAULT_SERVER_DATETIME_FORMAT
from openerp.tools import html_sanitize
REFERENCING_FIELDS = set([None, 'id', '.id'])
def only_ref_fields(record):
@ -184,7 +185,7 @@ class ir_fields_converter(orm.Model):
def _str_id(self, cr, uid, model, column, value, context=None):
return value, []
_str_to_char = _str_to_text = _str_to_binary = _str_id
_str_to_reference = _str_to_char = _str_to_text = _str_to_binary = _str_to_html = _str_id
def _str_to_date(self, cr, uid, model, column, value, context=None):
try:
@ -253,7 +254,7 @@ class ir_fields_converter(orm.Model):
if not isinstance(selection, (tuple, list)):
# FIXME: Don't pass context to avoid translations?
# Or just copy context & remove lang?
selection = selection(model, cr, uid)
selection = selection(model, cr, uid, context=None)
for item, label in selection:
labels = self._get_translations(
cr, uid, ('selection', 'model', 'code'), label, context=context)

View File

@ -212,14 +212,14 @@ class ir_mail_server(osv.osv):
password=smtp_server.smtp_pass, encryption=smtp_server.smtp_encryption,
smtp_debug=smtp_server.smtp_debug)
except Exception, e:
raise osv.except_osv(_("Connection test failed!"), _("Here is what we got instead:\n %s") % tools.ustr(e))
raise osv.except_osv(_("Connection Test Failed!"), _("Here is what we got instead:\n %s") % tools.ustr(e))
finally:
try:
if smtp: smtp.quit()
except Exception:
# ignored, just a consequence of the previous exception
pass
raise osv.except_osv(_("Connection test succeeded!"), _("Everything seems properly set up!"))
raise osv.except_osv(_("Connection Test Succeeded!"), _("Everything seems properly set up!"))
def connect(self, host, port, user=None, password=None, encryption=False, smtp_debug=False):
"""Returns a new SMTP connection to the give SMTP server, authenticated
@ -402,8 +402,10 @@ class ir_mail_server(osv.osv):
# The email's "Envelope From" (Return-Path), and all recipient addresses must only contain ASCII characters.
from_rfc2822 = extract_rfc2822_addresses(smtp_from)
assert len(from_rfc2822) == 1, "Malformed 'Return-Path' or 'From' address - it may only contain plain ASCII characters"
smtp_from = from_rfc2822[0]
assert from_rfc2822, ("Malformed 'Return-Path' or 'From' address: %r - "
"It should contain one valid plain ASCII email") % smtp_from
# use last extracted email, to support rarities like 'Support@MyComp <support@mycompany.com>'
smtp_from = from_rfc2822[-1]
email_to = message['To']
email_cc = message['Cc']
email_bcc = message['Bcc']

View File

@ -82,7 +82,7 @@ class ir_model(osv.osv):
return []
__, 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 Criteria"), _('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)
@ -195,8 +195,10 @@ class ir_model(osv.osv):
ctx = dict(context,
field_name=vals['name'],
field_state='manual',
select=vals.get('select_level', '0'))
select=vals.get('select_level', '0'),
update_custom_fields=True)
self.pool.get(vals['model'])._auto_init(cr, ctx)
self.pool.get(vals['model'])._auto_end(cr, ctx) # actually create FKs!
openerp.modules.registry.RegistryManager.signal_registry_change(cr.dbname)
return res
@ -355,6 +357,7 @@ class ir_model_fields(osv.osv):
select=vals.get('select_level', '0'),
update_custom_fields=True)
self.pool.get(vals['model'])._auto_init(cr, ctx)
self.pool.get(vals['model'])._auto_end(cr, ctx) # actually create FKs!
openerp.modules.registry.RegistryManager.signal_registry_change(cr.dbname)
return res
@ -472,6 +475,7 @@ class ir_model_fields(osv.osv):
for col_name, col_prop, val in patch_struct[1]:
setattr(obj._columns[col_name], col_prop, val)
obj._auto_init(cr, ctx)
obj._auto_end(cr, ctx) # actually create FKs!
openerp.modules.registry.RegistryManager.signal_registry_change(cr.dbname)
return res
@ -1108,7 +1112,8 @@ class ir_model_data(osv.osv):
return True
to_unlink = []
cr.execute("""SELECT id,name,model,res_id,module FROM ir_model_data
WHERE module IN %s AND res_id IS NOT NULL AND noupdate=%s""",
WHERE module IN %s AND res_id IS NOT NULL AND noupdate=%s
ORDER BY id DESC""",
(tuple(modules), False))
for (id, name, model, res_id, module) in cr.fetchall():
if (module,name) not in self.loads:

View File

@ -155,12 +155,53 @@ class ir_translation(osv.osv):
lang_data = lang_model.read(cr, uid, lang_ids, ['code', 'name'], context=context)
return [(d['code'], d['name']) for d in lang_data]
def _get_src(self, cr, uid, ids, name, arg, context=None):
''' Get source name for the translation. If object type is model then
return the value store in db. Otherwise return value store in src field
'''
if context is None:
context = {}
res = dict.fromkeys(ids, False)
for record in self.browse(cr, uid, ids, context=context):
if record.type != 'model':
res[record.id] = record.src
else:
model_name, field = record.name.split(',')
model = self.pool.get(model_name)
if model and model.exists(cr, uid, record.res_id, context=context):
#We need to take the context without the language information, because we want to read the
#value store in db and not on the one associate with current language.
context_wo_lang = context.copy()
context_wo_lang.pop('lang', None)
result = model.read(cr, uid, record.res_id, [field], context=context_wo_lang)
res[record.id] = result and result[field] or False
return res
def _set_src(self, cr, uid, id, name, value, args, context=None):
''' When changing source term of a translation, change its value in db for
the associated object, and the src field
'''
if context is None:
context = {}
record = self.browse(cr, uid, id, context=context)
if record.type == 'model':
model_name, field = record.name.split(',')
model = self.pool.get(model_name)
#We need to take the context without the language information, because we want to write on the
#value store in db and not on the one associate with current language.
#Also not removing lang from context trigger an error when lang is different
context_wo_lang = context.copy()
context_wo_lang.pop('lang', None)
model.write(cr, uid, record.res_id, {field: value}, context=context_wo_lang)
return self.write(cr, uid, id, {'src': value}, context=context)
_columns = {
'name': fields.char('Translated field', required=True),
'res_id': fields.integer('Record ID', select=True),
'lang': fields.selection(_get_language, string='Language'),
'type': fields.selection(TRANSLATION_TYPE, string='Type', select=True),
'src': fields.text('Source'),
'src': fields.text('Old source'),
'source': fields.function(_get_src, fnct_inv=_set_src, type='text', string='Source'),
'value': fields.text('Translation Value'),
'module': fields.char('Module', help="Module this term belongs to", select=True),
@ -300,7 +341,7 @@ class ir_translation(osv.osv):
return trad
def create(self, cr, uid, vals, context=None):
if not context:
if context is None:
context = {}
ids = super(ir_translation, self).create(cr, uid, vals, context=context)
self._get_source.clear_cache(self, uid, vals.get('name',0), vals.get('type',0), vals.get('lang',0), vals.get('src',0))
@ -308,7 +349,7 @@ class ir_translation(osv.osv):
return ids
def write(self, cursor, user, ids, vals, context=None):
if not context:
if context is None:
context = {}
if isinstance(ids, (int, long)):
ids = [ids]
@ -323,7 +364,7 @@ class ir_translation(osv.osv):
return result
def unlink(self, cursor, user, ids, context=None):
if not context:
if context is None:
context = {}
if isinstance(ids, (int, long)):
ids = [ids]
@ -406,6 +447,13 @@ class ir_translation(osv.osv):
tools.trans_load(cr, base_trans_file, lang, verbose=False, module_name=module_name, context=context)
context['overwrite'] = True # make sure the requested translation will override the base terms later
# i18n_extra folder is for additional translations handle manually (eg: for l10n_be)
base_trans_extra_file = openerp.modules.get_module_resource(module_name, 'i18n_extra', base_lang_code + '.po')
if base_trans_extra_file:
_logger.info('module %s: loading extra base translation file %s for language %s', module_name, base_lang_code, lang)
tools.trans_load(cr, base_trans_extra_file, lang, verbose=False, module_name=module_name, context=context)
context['overwrite'] = True # make sure the requested translation will override the base terms later
# Step 2: then load the main translation file, possibly overriding the terms coming from the base language
trans_file = openerp.modules.get_module_resource(module_name, 'i18n', lang_code + '.po')
if trans_file:
@ -413,6 +461,11 @@ class ir_translation(osv.osv):
tools.trans_load(cr, trans_file, lang, verbose=False, module_name=module_name, context=context)
elif lang_code != 'en_US':
_logger.warning('module %s: no translation for language %s', module_name, lang_code)
trans_extra_file = openerp.modules.get_module_resource(module_name, 'i18n_extra', lang_code + '.po')
if trans_extra_file:
_logger.info('module %s: loading extra translation file (%s) for language %s', module_name, lang_code, lang)
tools.trans_load(cr, trans_extra_file, lang, verbose=False, module_name=module_name, context=context)
return True

View File

@ -38,7 +38,7 @@
<field name="res_id"/>
</group>
<group string="Source Term">
<field name="src" nolabel="1" height="400"/>
<field name="source" nolabel="1" height="400"/>
</group>
<group string="Translation">
<field name="value" nolabel="1" height="400"/>
@ -55,7 +55,7 @@
<field name="model">ir.translation</field>
<field name="arch" type="xml">
<tree string="Translations" editable="top">
<field name="src"/>
<field name="source"/>
<field name="value"/>
<field name="name"/>
<field name="lang"/>

View File

@ -125,11 +125,15 @@ class view(osv.osv):
try:
fvg = self.pool.get(view.model).fields_view_get(cr, uid, view_id=view.id, view_type=view.type, context=context)
return fvg['arch']
except:
except Exception:
_logger.exception("Can't render view %s for model: %s", view.xml_id, view.model)
return False
def _check_xml(self, cr, uid, ids, context=None):
if context is None:
context = {}
context['check_view_ids'] = ids
for view in self.browse(cr, uid, ids, context):
# Sanity check: the view should not break anything upon rendering!
view_arch_utf8 = self._check_render_view(cr, uid, view, context=context)
@ -175,13 +179,15 @@ class view(osv.osv):
:rtype: list of tuples
:return: [(view_arch,view_id), ...]
"""
user_groups = frozenset(self.pool.get('res.users').browse(cr, 1, uid, context).groups_id)
if self.pool._init:
# Module init currently in progress, only consider views from modules whose code was already loaded
check_view_ids = context and context.get('check_view_ids') or (0,)
query = """SELECT v.id FROM ir_ui_view v LEFT JOIN ir_model_data md ON (md.model = 'ir.ui.view' AND md.res_id = v.id)
WHERE v.inherit_id=%s AND v.model=%s AND md.module in %s
WHERE v.inherit_id=%s AND v.model=%s AND (md.module in %s OR v.id in %s)
ORDER BY priority"""
query_params = (view_id, model, tuple(self.pool._init_modules))
query_params = (view_id, model, tuple(self.pool._init_modules), tuple(check_view_ids))
else:
# Modules fully loaded, consider all views
query = """SELECT v.id FROM ir_ui_view v

View File

@ -411,8 +411,8 @@ class ir_values(osv.osv):
(tuple(groups), uid))
if not cr.fetchone():
if action['name'] == 'Menuitem':
raise osv.except_osv('Error !',
'You do not have the permission to perform this operation !!!')
raise osv.except_osv('Error!',
'You do not have the permission to perform this operation!!!')
continue
# keep only the first action registered for each action name
results[action['name']] = (action['id'], action['name'], action_def)

View File

@ -26,7 +26,7 @@ from openerp import report, tools
_logger = logging.getLogger(__name__)
def graph_get(cr, graph, wkf_ids, nested, workitem, processed_subflows):
def graph_get(cr, graph, wkf_ids, nested, workitem, witm_trans, processed_subflows):
import pydot
cr.execute('select * from wkf_activity where wkf_id in ('+','.join(['%s']*len(wkf_ids))+')', wkf_ids)
nodes = cr.dictfetchall()
@ -40,7 +40,7 @@ def graph_get(cr, graph, wkf_ids, nested, workitem, processed_subflows):
cr.execute('select * from wkf where id=%s', (n['subflow_id'],))
wkfinfo = cr.dictfetchone()
graph2 = pydot.Cluster('subflow'+str(n['subflow_id']), fontsize='12', label = "\"Subflow: %s\\nOSV: %s\"" % ( n['name'], wkfinfo['osv']) )
(s1,s2) = graph_get(cr, graph2, [n['subflow_id']], True, workitem, processed_subflows)
(s1,s2) = graph_get(cr, graph2, [n['subflow_id']], True, workitem, witm_trans, processed_subflows)
graph.add_subgraph(graph2)
actfrom[n['id']] = s2
actto[n['id']] = s1
@ -89,7 +89,9 @@ def graph_get(cr, graph, wkf_ids, nested, workitem, processed_subflows):
args['arrowtail']='inv'
if activities[t['act_to']]['join_mode']=='AND':
args['arrowhead']='crow'
args['arrowhead']='crow'
if t['id'] in witm_trans:
args['color'] = 'red'
activity_from = actfrom[t['act_from']][1].get(t['signal'], actfrom[t['act_from']][0])
activity_to = actto[t['act_to']][1].get(t['signal'], actto[t['act_to']][0])
@ -119,8 +121,12 @@ def graph_instance_get(cr, graph, inst_id, nested=False):
workitems.update(workitem_get(subflow_id))
return workitems
def witm_get(instance):
cr.execute("select trans_id from wkf_witm_trans where inst_id=%s", (instance,))
return set(t[0] for t in cr.fetchall())
processed_subflows = set()
graph_get(cr, graph, [x[0] for x in inst], nested, workitem_get(inst_id), processed_subflows)
graph_get(cr, graph, [x[0] for x in inst], nested, workitem_get(inst_id), witm_get(inst_id), processed_subflows)
#
# TODO: pas clean: concurrent !!!

View File

@ -96,7 +96,7 @@ class wkf_activity(osv.osv):
def unlink(self, cr, uid, ids, context=None):
if context is None: context = {}
if not context.get('_force_unlink') and self.pool.get('workflow.workitem').search(cr, uid, [('act_id', 'in', ids)]):
raise osv.except_osv(_('Operation forbidden'),
raise osv.except_osv(_('Operation Forbidden'),
_('Please make sure no workitems refer to an activity before deleting it!'))
super(wkf_activity, self).unlink(cr, uid, ids, context=context)

View File

@ -55,7 +55,7 @@ class base_module_import(osv.osv_memory):
try:
file_data = zipfile.ZipFile(fp, 'r')
except zipfile.BadZipfile:
raise osv.except_osv(_('Error !'), _('File is not a zip file!'))
raise osv.except_osv(_('Error!'), _('File is not a zip file!'))
init_file_name = sorted(file_data.namelist())[0]
module_name = os.path.split(init_file_name)[0]
@ -63,8 +63,8 @@ class base_module_import(osv.osv_memory):
try:
zip_file = open(file_path, 'wb')
except IOError:
raise osv.except_osv(_('Error !'),
_('Can not create the module file: %s !') % \
raise osv.except_osv(_('Error!'),
_('Can not create the module file: %s!') % \
(file_path,) )
zip_file.write(zip_data)
zip_file.close()

View File

@ -1,74 +0,0 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 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/>.
#
##############################################################################
import os
import glob
import imp
import zipfile
from openerp import tools
from openerp.osv import osv
class base_module_scan(osv.osv_memory):
""" scan module """
_name = "base.module.scan"
_description = "scan module"
def watch_dir(self, cr, uid, ids, context):
mod_obj = self.pool.get('ir.module.module')
all_mods = mod_obj.read(cr, uid, mod_obj.search(cr, uid, []), ['name', 'state'])
known_modules = [x['name'] for x in all_mods]
ls_ad = glob.glob(os.path.join(tools.config['addons_path'], '*', '__terp__.py'))
modules = [module_name_re.match(name).group(1) for name in ls_ad]
for fname in os.listdir(tools.config['addons_path']):
if zipfile.is_zipfile(fname):
modules.append( fname.split('.')[0])
for module in modules:
if module in known_modules:
continue
terp = mod_obj.get_module_info(module)
if not terp.get('installable', True):
continue
# XXX check if this code is correct...
fm = imp.find_module(module)
try:
imp.load_module(module, *fm)
finally:
if fm[0]:
fm[0].close()
values = mod_obj.get_values_from_terp(terp)
mod_id = mod_obj.create(cr, uid, dict(name=module, state='uninstalled', **values))
dependencies = terp.get('depends', [])
for d in dependencies:
cr.execute('insert into ir_module_module_dependency (module_id,name) values (%s, %s)', (mod_id, d))
for module in known_modules:
terp = mod_obj.get_module_info(module)
if terp.get('installable', True):
for mod in all_mods:
if mod['name'] == module and mod['state'] == 'uninstallable':
mod_obj.write(cr, uid, [mod['id']], {'state': 'uninstalled'})
return {}
base_module_scan()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -81,7 +81,7 @@ class base_module_upgrade(osv.osv_memory):
(tuple(ids), ('uninstalled',)))
unmet_packages = [x[0] for x in cr.fetchall()]
if unmet_packages:
raise osv.except_osv(_('Unmet dependency !'),
raise osv.except_osv(_('Unmet Dependency!'),
_('Following modules are not installed or unknown: %s') % ('\n\n' + '\n'.join(unmet_packages)))
ir_module.download(cr, uid, ids, context=context)

View File

@ -53,7 +53,13 @@
<field name="res_model">res.bank</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="help">Manage bank records you want to be used in the system.</field>
<field name="help">
<p class="oe_view_nocontent_create">
Click to create a new bank.
</p><p>
Manage bank records you want to be used in the system.
</p>
</field>
</record>
<menuitem action="action_res_bank_form" id="menu_action_res_bank_form" parent="base.menu_config_address_book" sequence="11" groups="base.group_no_one"/>
@ -152,7 +158,17 @@
<field name="res_model">res.partner.bank</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="help">Configure your company's bank accounts and select those that must appear on the report footer. You can reorder bank accounts from the list view. If you use the accounting application of OpenERP, journals and accounts will be created automatically based on these data.</field>
<field name="help" type="html">
<p class="oe_view_nocontent_create">
Click to create a bank account.
</p><p>
Configure your company's bank accounts and select those that must appear on the report footer.
You can reorder bank accounts from the list view.
</p>
<p>
If you use the accounting application of OpenERP, journals and accounts will be created automatically based on these data.
</p>
</field>
</record>
<menuitem action="action_res_partner_bank_account_form"
id="menu_action_res_partner_bank_form"

View File

@ -22,6 +22,7 @@ import logging
from operator import attrgetter
import openerp
from openerp import SUPERUSER_ID
from openerp.osv import osv, fields
from openerp.tools import ustr
from openerp.tools.translate import _
@ -46,10 +47,10 @@ class res_config_module_installation_mixin(object):
to_install_missing_names.append(name)
elif module.state == 'uninstalled':
to_install_ids.append(module.id)
result = None
if to_install_ids:
ir_module.button_immediate_install(cr, uid, to_install_ids, context=context)
result = ir_module.button_immediate_install(cr, uid, to_install_ids, context=context)
#FIXME: if result is not none, the corresponding todo will be skipped because it was just marked done
if to_install_missing_names:
return {
'type': 'ir.actions.client',
@ -57,7 +58,7 @@ class res_config_module_installation_mixin(object):
'params': {'modules': to_install_missing_names},
}
return None
return result
class res_config_configurable(osv.osv_memory):
''' Base classes for new-style configuration items
@ -99,13 +100,10 @@ class res_config_configurable(osv.osv_memory):
res = next.action_launch(context=context)
res['nodestroy'] = False
return res
# reload the client; open the first available root menu
menu_obj = self.pool.get('ir.ui.menu')
menu_ids = menu_obj.search(cr, uid, [('parent_id', '=', False)], context=context)
return {
'type': 'ir.actions.client',
'tag': 'reload',
'params': {'menu_id': menu_ids and menu_ids[0] or False},
}
def start(self, cr, uid, ids, context=None):
@ -533,6 +531,9 @@ class res_config_settings(osv.osv_memory, res_config_module_installation_mixin):
return res
def execute(self, cr, uid, ids, context=None):
if uid != SUPERUSER_ID and not self.pool['res.users'].has_group(cr, uid, 'base.group_erp_manager'):
raise openerp.exceptions.AccessError(_("Only administrators can change the settings"))
ir_values = self.pool.get('ir.values')
ir_module = self.pool.get('ir.module.module')
classified = self._get_classified_fields(cr, uid, context)
@ -541,7 +542,7 @@ class res_config_settings(osv.osv_memory, res_config_module_installation_mixin):
# default values fields
for name, model, field in classified['default']:
ir_values.set_default(cr, uid, model, field, config[name])
ir_values.set_default(cr, SUPERUSER_ID, model, field, config[name])
# group fields: modify group / implied groups
for name, group, implied_group in classified['group']:

View File

@ -77,7 +77,7 @@ addresses belonging to this country.\n\nYou can use the python-style string pate
context=context)
def write(self, cursor, user, ids, vals, context=None):
if 'code' in vals:
if vals.get('code'):
vals['code'] = vals['code'].upper()
return super(Country, self).write(cursor, user, ids, vals,
context=context)

View File

@ -1212,7 +1212,7 @@
<record id="ve" model="res.country">
<field name="name">Venezuela</field>
<field name="code">ve</field>
<field name="currency_id" ref="VUB"/>
<field name="currency_id" ref="VEF"/>
</record>
<record id="vg" model="res.country">
<field name="name">Virgin Islands (British)</field>

View File

@ -31,6 +31,12 @@ CURRENCY_DISPLAY_PATTERN = re.compile(r'(\w+)\s*(?:\((.*)\))?')
class res_currency(osv.osv):
def _current_rate(self, cr, uid, ids, name, arg, context=None):
return self._current_rate_computation(cr, uid, ids, name, arg, True, context=context)
def _current_rate_silent(self, cr, uid, ids, name, arg, context=None):
return self._current_rate_computation(cr, uid, ids, name, arg, False, context=context)
def _current_rate_computation(self, cr, uid, ids, name, arg, raise_on_no_rate, context=None):
if context is None:
context = {}
res = {}
@ -48,9 +54,12 @@ class res_currency(osv.osv):
if cr.rowcount:
id, rate = cr.fetchall()[0]
res[id] = rate
elif not raise_on_no_rate:
res[id] = 0
else:
raise osv.except_osv(_('Error!'),_("No currency rate associated for currency %d for the given period" % (id)))
return res
_name = "res.currency"
_description = "Currency"
_columns = {
@ -59,6 +68,10 @@ class res_currency(osv.osv):
'symbol': fields.char('Symbol', size=4, help="Currency sign, to be used when printing amounts."),
'rate': fields.function(_current_rate, string='Current Rate', digits=(12,6),
help='The rate of the currency to the currency of rate 1.'),
# Do not use for computation ! Same as rate field with silent failing
'rate_silent': fields.function(_current_rate_silent, string='Current Rate', digits=(12,6),
help='The rate of the currency to the currency of rate 1 (0 if no rate defined).'),
'rate_ids': fields.one2many('res.currency.rate', 'currency_id', 'Rates'),
'accuracy': fields.integer('Computational Accuracy'),
'rounding': fields.float('Rounding Factor', digits=(12,6)),

View File

@ -22,7 +22,7 @@
<field name="company_id" groups="base.group_multi_company"/>
<field name="rate_ids" invisible="1"/>
<field name="date"/>
<field name="rate"/>
<field name="rate_silent"/>
<field name="rounding"/>
<field name="accuracy"/>
<field name="position"/>
@ -37,7 +37,7 @@
<form string="Currency" version="7.0">
<group col="4">
<field name="name"/>
<field name="rate"/>
<field name="rate_silent"/>
<field name="company_id" groups="base.group_multi_company"/>
</group>

View File

@ -182,11 +182,11 @@ class lang(osv.osv):
for language in languages:
ctx_lang = context.get('lang')
if language['code']=='en_US':
raise osv.except_osv(_('User Error'), _("Base Language 'en_US' can not be deleted !"))
raise osv.except_osv(_('User Error'), _("Base Language 'en_US' can not be deleted!"))
if ctx_lang and (language['code']==ctx_lang):
raise osv.except_osv(_('User Error'), _("You cannot delete the language which is User's Preferred Language !"))
raise osv.except_osv(_('User Error'), _("You cannot delete the language which is User's Preferred Language!"))
if language['active']:
raise osv.except_osv(_('User Error'), _("You cannot delete the language which is Active !\nPlease de-activate the language first."))
raise osv.except_osv(_('User Error'), _("You cannot delete the language which is Active!\nPlease de-activate the language first."))
trans_obj = self.pool.get('ir.translation')
trans_ids = trans_obj.search(cr, uid, [('lang','=',language['code'])], context=context)
trans_obj.unlink(cr, uid, trans_ids, context=context)

View File

@ -466,7 +466,11 @@ class res_partner(osv.osv, format_address):
if partner.child_ids:
# 2a. Commercial Fields: sync if commercial entity
if partner.commercial_partner_id == partner:
self._commercial_sync_to_children(cr, uid, partner, context=context)
commercial_fields = self._commercial_fields(cr, uid,
context=context)
if any(field in update_values for field in commercial_fields):
self._commercial_sync_to_children(cr, uid, partner,
context=context)
# 2b. Address fields: sync if address changed
address_fields = self._address_fields(cr, uid, context=context)
if any(field in update_values for field in address_fields):
@ -489,6 +493,16 @@ class res_partner(osv.osv, format_address):
def write(self, cr, uid, ids, vals, context=None):
if isinstance(ids, (int, long)):
ids = [ids]
#res.partner must only allow to set the company_id of a partner if it
#is the same as the company of all users that inherit from this partner
#(this is to allow the code from res_users to write to the partner!) or
#if setting the company_id to False (this is compatible with any user company)
if vals.get('company_id'):
for partner in self.browse(cr, uid, ids, context=context):
if partner.user_ids:
user_companies = set([user.company_id.id for user in partner.user_ids])
if len(user_companies) > 1 or vals['company_id'] not in user_companies:
raise osv.except_osv(_("Warning"),_("You can not change the company as the partner/user has multiple user linked with different companies."))
result = super(res_partner,self).write(cr, uid, ids, vals, context=context)
for partner in self.browse(cr, uid, ids, context=context):
self._fields_sync(cr, uid, partner, vals, context)
@ -563,7 +577,7 @@ class res_partner(osv.osv, format_address):
context = {}
name, email = self._parse_partner_name(name, context=context)
if context.get('force_email') and not email:
raise osv.except_osv(_('Warning'), _("Couldn't create contact without email address !"))
raise osv.except_osv(_('Warning'), _("Couldn't create contact without email address!"))
if not name and email:
name = email
rec_id = self.create(cr, uid, {self._rec_name: name or email, 'email': email or False}, context=context)
@ -582,26 +596,53 @@ class res_partner(osv.osv, format_address):
if not args:
args = []
if name and operator in ('=', 'ilike', '=ilike', 'like', '=like'):
self.check_access_rights(cr, uid, 'read')
where_query = self._where_calc(cr, uid, args, context=context)
self._apply_ir_rules(cr, uid, where_query, 'read', context=context)
from_clause, where_clause, where_clause_params = where_query.get_sql()
where_str = where_clause and (" WHERE %s AND " % where_clause) or ' WHERE '
# search on the name of the contacts and of its company
search_name = name
if operator in ('ilike', 'like'):
search_name = '%%%s%%' % name
if operator in ('=ilike', '=like'):
operator = operator[1:]
query_args = {'name': search_name}
limit_str = ''
# TODO: simplify this in trunk with `display_name`, once it is stored
# Perf note: a CTE expression (WITH ...) seems to have an even higher cost
# than this query with duplicated CASE expressions. The bulk of
# the cost is the ORDER BY, and it is inevitable if we want
# relevant results for the next step, otherwise we'd return
# a random selection of `limit` results.
query = ('''SELECT res_partner.id FROM res_partner
LEFT JOIN res_partner company
ON res_partner.parent_id = company.id'''
+ where_str + ''' (res_partner.email ''' + operator + ''' %s OR
CASE
WHEN company.id IS NULL OR res_partner.is_company
THEN res_partner.name
ELSE company.name || ', ' || res_partner.name
END ''' + operator + ''' %s)
ORDER BY
CASE
WHEN company.id IS NULL OR res_partner.is_company
THEN res_partner.name
ELSE company.name || ', ' || res_partner.name
END''')
where_clause_params += [search_name, search_name]
if limit:
limit_str = ' limit %(limit)s'
query_args['limit'] = limit
cr.execute('''SELECT partner.id FROM res_partner partner
LEFT JOIN res_partner company ON partner.parent_id = company.id
WHERE partner.email ''' + operator +''' %(name)s
OR partner.name || ' (' || COALESCE(company.name,'') || ')'
''' + operator + ' %(name)s ' + limit_str, query_args)
query += ' limit %s'
where_clause_params.append(limit)
cr.execute(query, where_clause_params)
ids = map(lambda x: x[0], cr.fetchall())
ids = self.search(cr, uid, [('id', 'in', ids)] + args, limit=limit, context=context)
if ids:
return self.name_get(cr, uid, ids, context)
else:
return []
return super(res_partner,self).name_search(cr, uid, name, args, operator=operator, context=context, limit=limit)
def find_or_create(self, cr, uid, email, context=None):

View File

@ -576,7 +576,14 @@
<field name="type">ir.actions.act_window</field>
<field name="res_model">res.partner.category</field>
<field name="view_type">form</field>
<field name="help">Manage the partner categories in order to better classify them for tracking and analysis purposes. A partner may belong to several categories and categories have a hierarchy structure: a partner belonging to a category also belong to his parent category.</field>
<field name="help" type="html">
<p class="oe_view_nocontent_create">
Click to create a new partner category.
</p><p>
Manage the partner categories in order to better classify them for tracking and analysis purposes.
A partner may belong to several categories and categories have a hierarchy structure: a partner belonging to a category also belong to his parent category.
</p>
</field>
</record>
<menuitem action="action_partner_category_form" id="menu_partner_category_form" name="Partner Tags" sequence="4" parent="menu_config_address_book" groups="base.group_no_one"/>

View File

@ -279,6 +279,13 @@ class res_users(osv.osv):
return result
def create(self, cr, uid, vals, context=None):
user_id = super(res_users, self).create(cr, uid, vals, context=context)
user = self.browse(cr, uid, user_id, context=context)
if user.partner_id.company_id:
user.partner_id.write({'company_id': user.company_id.id})
return user_id
def write(self, cr, uid, ids, values, context=None):
if not hasattr(ids, '__iter__'):
ids = [ids]
@ -293,7 +300,11 @@ class res_users(osv.osv):
uid = 1 # safe fields only, so we write as super-user to bypass access rights
res = super(res_users, self).write(cr, uid, ids, values, context=context)
if 'company_id' in values:
for user in self.browse(cr, uid, ids, context=context):
# if partner is global we keep it that way
if user.partner_id.company_id and user.partner_id.company_id.id != values['company_id']:
user.partner_id.write({'company_id': user.company_id.id})
# clear caches linked to the users
self.pool.get('ir.model.access').call_cache_clearing_methods(cr)
clear = partial(self.pool.get('ir.rule').clear_cache, cr)
@ -797,8 +808,9 @@ class users_view(osv.osv):
if not 'groups_id' in fields:
fields.append('groups_id')
res = super(users_view, self).read(cr, uid, ids, fields, context=context, load=load)
for values in (res if isinstance(res, list) else [res]):
self._get_reified_groups(group_fields, values)
if res:
for values in (res if isinstance(res, list) else [res]):
self._get_reified_groups(group_fields, values)
return res
def _get_reified_groups(self, fields, values):
@ -825,6 +837,8 @@ class users_view(osv.osv):
'string': app and app.name or _('Other'),
'selection': [(False, '')] + [(g.id, g.name) for g in gs],
'help': '\n'.join(tips),
'exportable': False,
'selectable': False,
}
else:
# boolean group fields
@ -833,6 +847,8 @@ class users_view(osv.osv):
'type': 'boolean',
'string': g.name,
'help': g.comment,
'exportable': False,
'selectable': False,
}
return res

Some files were not shown because too many files have changed in this diff Show More