[MERGE] sync with 7.0
bzr revid: mat@openerp.com-20140213095713-b1ys0nu8u7o8ybyr
This commit is contained in:
commit
6f8811e7f6
|
@ -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 :
|
||||
|
||||
|
|
|
@ -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``.
|
||||
|
||||
|
|
@ -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>
|
||||
|
|
|
@ -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
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
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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']
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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"/>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 !!!
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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:
|
|
@ -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)
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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']:
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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)),
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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"/>
|
||||
|
|
|
@ -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
Loading…
Reference in New Issue