diff --git a/openerp/addons/base/ir/ir_actions.py b/openerp/addons/base/ir/ir_actions.py
index b36fc27d9d3..cb8440ddb49 100644
--- a/openerp/addons/base/ir/ir_actions.py
+++ b/openerp/addons/base/ir/ir_actions.py
@@ -24,6 +24,8 @@ import logging
import operator
import os
import time
+import datetime
+import dateutil
import openerp
from openerp import SUPERUSER_ID
@@ -268,7 +270,7 @@ class ir_actions_act_window(osv.osv):
'filter': fields.boolean('Filter'),
'auto_search':fields.boolean('Auto Search'),
'search_view' : fields.function(_search_view, type='text', string='Search View'),
- 'multi': fields.boolean('Action on Multiple Doc.', help="If set to true, the action will not be displayed on the right toolbar of a form view"),
+ 'multi': fields.boolean('Restrict to lists', help="If checked and the action is bound to a model, it will only appear in the More menu on list views"),
}
_defaults = {
@@ -928,11 +930,18 @@ class ir_actions_server(osv.osv):
'obj': obj,
'pool': self.pool,
'time': time,
+ 'datetime': datetime,
+ 'dateutil': dateutil,
'cr': cr,
'uid': uid,
'user': user,
+ 'context': context,
}
+ }
+ return eval_context
+
+
def run(self, cr, uid, ids, context=None):
""" Runs the server action. For each server action, the condition is
checked. Note that a void (``False``) condition is considered as always
@@ -955,7 +964,6 @@ class ir_actions_server(osv.osv):
if context is None:
context = {}
res = False
- active_ids = context.get('active_ids', [context.get('active_id')])
for action in self.browse(cr, uid, ids, context):
eval_context = self._get_eval_context(cr, uid, action, context=context)
condition = action.condition
@@ -963,9 +971,7 @@ class ir_actions_server(osv.osv):
# Void (aka False) conditions are considered as True
condition = True
if hasattr(self, 'run_action_%s_multi' % action.state):
- # set active_ids in context only needed if one active_id
- run_context = dict(context, active_ids=active_ids)
- eval_context["context"] = run_context
+ run_context = eval_context['context']
expr = eval(str(condition), eval_context)
if not expr:
continue
@@ -975,6 +981,8 @@ class ir_actions_server(osv.osv):
elif hasattr(self, 'run_action_%s' % action.state):
func = getattr(self, 'run_action_%s' % action.state)
+ active_id = context.get('active_id')
+ active_ids = context.get('active_ids', [active_id] if active_id else [])
for active_id in active_ids:
# run context dedicated to a particular active_id
run_context = dict(context, active_ids=[active_id], active_id=active_id)
diff --git a/openerp/addons/base/ir/ir_actions.xml b/openerp/addons/base/ir/ir_actions.xml
index 7e6c1f9be4b..7ac0252da80 100644
--- a/openerp/addons/base/ir/ir_actions.xml
+++ b/openerp/addons/base/ir/ir_actions.xml
@@ -180,6 +180,7 @@
+
diff --git a/openerp/addons/base/ir/ir_ui_view.py b/openerp/addons/base/ir/ir_ui_view.py
index fe62d9e80cc..aafce29b7eb 100644
--- a/openerp/addons/base/ir/ir_ui_view.py
+++ b/openerp/addons/base/ir/ir_ui_view.py
@@ -755,6 +755,10 @@ class view(osv.osv):
# running index by tag type, for XPath query generation
indexes = collections.defaultdict(lambda: 0)
for child in e.iterchildren(tag=etree.Element):
+ if child.get('data-oe-xpath'):
+ # injected by view inheritance, skip otherwise
+ # generated xpath is incorrect
+ continue
indexes[child.tag] += 1
self.distribute_branding(child, distributed_branding,
parent_xpath=node_path,
diff --git a/openerp/addons/base/ir/ir_values.py b/openerp/addons/base/ir/ir_values.py
index a21b034d1b0..6af09b7ecbb 100644
--- a/openerp/addons/base/ir/ir_values.py
+++ b/openerp/addons/base/ir/ir_values.py
@@ -394,17 +394,16 @@ class ir_values(osv.osv):
for action in cr.dictfetchall():
if not action['value']:
continue # skip if undefined
- action_model,id = action['value'].split(',')
- fields = [
- field
- for field in self.pool[action_model]._all_columns
- if field not in EXCLUDED_FIELDS]
+ action_model_name, action_id = action['value'].split(',')
+ action_model = self.pool.get(action_model_name)
+ if not action_model:
+ continue # unknow model? skip it
+ fields = [field for field in action_model._all_columns if field not in EXCLUDED_FIELDS]
# FIXME: needs cleanup
try:
- action_def = self.pool[action_model].read(cr, uid, int(id), fields, context)
+ action_def = action_model.read(cr, uid, int(action_id), fields, context)
if action_def:
- if action_model in ('ir.actions.report.xml','ir.actions.act_window',
- 'ir.actions.wizard'):
+ if action_model_name in ('ir.actions.report.xml', 'ir.actions.act_window'):
groups = action_def.get('groups_id')
if groups:
cr.execute('SELECT 1 FROM res_groups_users_rel WHERE gid IN %s AND uid=%s',
diff --git a/openerp/addons/base/res/res_company.py b/openerp/addons/base/res/res_company.py
index dfe049bf2b3..ae83aa4f2d1 100644
--- a/openerp/addons/base/res/res_company.py
+++ b/openerp/addons/base/res/res_company.py
@@ -124,7 +124,8 @@ class res_company(osv.osv):
'rml_footer': fields.text('Report Footer', help="Footer text displayed at the bottom of all reports."),
'rml_footer_readonly': fields.related('rml_footer', type='text', string='Report Footer', readonly=True),
'custom_footer': fields.boolean('Custom Footer', help="Check this to define the report footer manually. Otherwise it will be filled in automatically."),
- 'font': fields.many2one('res.font', string="Font",help="Set the font into the report header, it will be used as default font in the RML reports of the user company"),
+ 'font': fields.many2one('res.font', string="Font", domain=[('mode', 'in', ('Normal', 'Regular', 'all', 'Book'))],
+ help="Set the font into the report header, it will be used as default font in the RML reports of the user company"),
'logo': fields.related('partner_id', 'image', string="Logo", type="binary"),
'logo_web': fields.function(_get_logo_web, string="Logo Web", type="binary", store={
'res.company': (lambda s, c, u, i, x: i, ['partner_id'], 10),
diff --git a/openerp/addons/base/res/res_company_view.xml b/openerp/addons/base/res/res_company_view.xml
index 7a146f6bc5e..5139623031f 100644
--- a/openerp/addons/base/res/res_company_view.xml
+++ b/openerp/addons/base/res/res_company_view.xml
@@ -93,7 +93,7 @@
-
+
diff --git a/openerp/addons/base/res/res_partner.py b/openerp/addons/base/res/res_partner.py
index 9115ccae481..e9573088680 100644
--- a/openerp/addons/base/res/res_partner.py
+++ b/openerp/addons/base/res/res_partner.py
@@ -209,6 +209,7 @@ class res_partner(osv.osv, format_address):
context = dict(context or {})
context.pop('show_address', None)
context.pop('show_address_only', None)
+ context.pop('show_email', None)
return dict(self.name_get(cr, uid, ids, context=context))
# indirections to avoid passing a copy of the overridable method when declaring the function field
diff --git a/openerp/addons/base/res/res_users.py b/openerp/addons/base/res/res_users.py
index ad017381409..b2aba694078 100644
--- a/openerp/addons/base/res/res_users.py
+++ b/openerp/addons/base/res/res_users.py
@@ -906,6 +906,8 @@ class change_password_wizard(osv.TransientModel):
for user in wizard.user_ids:
user_ids.append(user.id)
self.pool.get('change.password.user').change_password_button(cr, uid, user_ids, context=context)
+ # don't keep temporary password copies in the database longer than necessary
+ self.pool.get('change.password.user').unlink(cr, uid, user_ids)
return {
'type': 'ir.actions.act_window_close',
}
diff --git a/openerp/addons/base/res/res_users_view.xml b/openerp/addons/base/res/res_users_view.xml
index 9c84d4326f4..ba603c7a0f0 100644
--- a/openerp/addons/base/res/res_users_view.xml
+++ b/openerp/addons/base/res/res_users_view.xml
@@ -231,7 +231,7 @@
res.users
-
+
diff --git a/openerp/addons/base/tests/test_base.py b/openerp/addons/base/tests/test_base.py
index fe8ddc411d7..263cb0adf23 100644
--- a/openerp/addons/base/tests/test_base.py
+++ b/openerp/addons/base/tests/test_base.py
@@ -289,6 +289,7 @@ class test_base(common.TransactionCase):
{'name': 'Alice', 'login': 'alice', 'color': 1, 'function': 'Friend'},
{'name': 'Bob', 'login': 'bob', 'color': 2, 'function': 'Friend'},
{'name': 'Eve', 'login': 'eve', 'color': 3, 'function': 'Eavesdropper'},
+ {'name': 'Nab', 'login': 'nab', 'color': 2, 'function': '5$ Wrench'},
]:
self.res_users.create(cr, uid, user_data)
@@ -298,6 +299,14 @@ class test_base(common.TransactionCase):
self.assertIn('color', group_data, "Aggregated data for the column 'color' is not present in read_group return values")
self.assertEqual(group_data['color'], 3, "Incorrect sum for aggregated data for the column 'color'")
+ groups_data = self.res_users.read_group(cr, uid, domain=[('login', 'in', ('alice', 'bob', 'eve'))], fields=['name', 'color'], groupby='name', orderby='name DESC, color asc')
+ self.assertEqual(len(groups_data), 3, "Incorrect number of results when grouping on a field")
+ self.assertEqual([user['name'] for user in groups_data], ['Eve', 'Bob', 'Alice'], 'Incorrect ordering of the list')
+
+ groups_data = self.res_users.read_group(cr, uid, domain=[('login', 'in', ('alice', 'bob', 'eve', 'nab'))], fields=['function', 'color'], groupby='function', orderby='color ASC')
+ self.assertEqual(len(groups_data), 3, "Incorrect number of results when grouping on a field")
+ self.assertEqual(groups_data, sorted(groups_data, key=lambda x: x['color']), 'Incorrect ordering of the list')
+
class test_partner_recursion(common.TransactionCase):
def setUp(self):
diff --git a/openerp/addons/base/tests/test_fields.py b/openerp/addons/base/tests/test_fields.py
index 6dd016ccaf9..47ffcd2f5b1 100644
--- a/openerp/addons/base/tests/test_fields.py
+++ b/openerp/addons/base/tests/test_fields.py
@@ -120,4 +120,62 @@ class TestRelatedField(common.TransactionCase):
# restore res.partner fields
self.partner._columns = old_columns
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+
+class TestPropertyField(common.TransactionCase):
+
+ def setUp(self):
+ super(TestPropertyField, self).setUp()
+ self.user = self.registry('res.users')
+ self.partner = self.registry('res.partner')
+ self.company = self.registry('res.company')
+ self.country = self.registry('res.country')
+ self.property = self.registry('ir.property')
+ self.imd = self.registry('ir.model.data')
+
+ def test_1_property_multicompany(self):
+ cr, uid = self.cr, self.uid
+
+ parent_company_id = self.imd.get_object_reference(cr, uid, 'base', 'main_company')[1]
+ country_be = self.imd.get_object_reference(cr, uid, 'base', 'be')[1]
+ country_fr = self.imd.get_object_reference(cr, uid, 'base', 'fr')[1]
+ group_partner_manager = self.imd.get_object_reference(cr, uid, 'base', 'group_partner_manager')[1]
+ group_multi_company = self.imd.get_object_reference(cr, uid, 'base', 'group_multi_company')[1]
+
+ sub_company = self.company.create(cr, uid, {'name': 'MegaCorp', 'parent_id': parent_company_id})
+ alice = self.user.create(cr, uid, {'name': 'Alice',
+ 'login':'alice',
+ 'email':'alice@youcompany.com',
+ 'company_id':parent_company_id,
+ 'company_ids':[(6, 0, [parent_company_id, sub_company])],
+ 'country_id':country_be,
+ 'groups_id': [(6, 0, [group_partner_manager, group_multi_company])]
+ })
+ bob = self.user.create(cr, uid, {'name': 'Bob',
+ 'login':'bob',
+ 'email':'bob@megacorp.com',
+ 'company_id':sub_company,
+ 'company_ids':[(6, 0, [parent_company_id, sub_company])],
+ 'country_id':country_fr,
+ 'groups_id': [(6, 0, [group_partner_manager, group_multi_company])]
+ })
+
+ self.partner._columns = dict(self.partner._columns)
+ self.partner._columns.update({
+ 'property_country': fields.property(type='many2one', relation="res.country", string="Country by company"),
+ })
+ self.partner._all_columns.update({
+ 'property_country': fields.column_info('property_country', self.partner._columns['property_country'], None, None, None),
+ })
+ self.partner._field_create(cr)
+
+ partner_id = self.partner.create(cr, alice, {
+ 'name': 'An International Partner',
+ 'email': 'partner@example.com',
+ 'company_id': parent_company_id,
+ })
+ self.partner.write(cr, bob, [partner_id], {'property_country': country_fr})
+ self.assertEqual(self.partner.browse(cr, bob, partner_id).property_country.id, country_fr, "Bob does not see the value he has set on the property field")
+
+ self.partner.write(cr, alice, [partner_id], {'property_country': country_be})
+ self.assertEqual(self.partner.browse(cr, alice, partner_id).property_country.id, country_be, "Alice does not see the value he has set on the property field")
+ self.assertEqual(self.partner.browse(cr, bob, partner_id).property_country.id, country_fr, "Changes made by Alice have overwritten Bob's value")
diff --git a/openerp/addons/base/tests/test_views.py b/openerp/addons/base/tests/test_views.py
index d93734f48cb..6780996ed6e 100644
--- a/openerp/addons/base/tests/test_views.py
+++ b/openerp/addons/base/tests/test_views.py
@@ -417,6 +417,61 @@ class TestNoModel(common.TransactionCase):
ET.tostring(sarch, encoding='utf-8'),
ET.tostring(self.arch, encoding='utf-8'))
+class TestTemplating(common.TransactionCase):
+ def setUp(self):
+ import openerp.modules
+ super(TestTemplating, self).setUp()
+ self._pool = openerp.modules.registry.RegistryManager.get(common.DB)
+ self._init = self._pool._init
+ # fuck off
+ self._pool._init = False
+
+ def tearDown(self):
+ self._pool._init = self._init
+ super(TestTemplating, self).tearDown()
+
+ def test_branding_inherit(self):
+ Views = self.registry('ir.ui.view')
+ id = Views.create(self.cr, self.uid, {
+ 'name': "Base view",
+ 'type': 'qweb',
+ 'arch': """
+
+
+ """
+ })
+ id2 = Views.create(self.cr, self.uid, {
+ 'name': "Extension",
+ 'type': 'qweb',
+ 'inherit_id': id,
+ 'arch': """
+
+
+ """
+ })
+
+ arch_string = Views.read_combined(
+ self.cr, self.uid, id, fields=['arch'],
+ context={'inherit_branding': True})['arch']
+
+ arch = ET.fromstring(arch_string)
+ Views.distribute_branding(arch)
+
+ [initial] = arch.xpath('//item[@order=1]')
+ self.assertEqual(
+ str(id),
+ initial.get('data-oe-id'),
+ "initial should come from the root view")
+ self.assertEqual(
+ '/root[1]/item[1]',
+ initial.get('data-oe-xpath'),
+ "initial's xpath should be within the root view only")
+
+ [second] = arch.xpath('//item[@order=2]')
+ self.assertEqual(
+ str(id2),
+ second.get('data-oe-id'),
+ "second should come from the extension view")
class test_views(common.TransactionCase):
diff --git a/openerp/osv/fields.py b/openerp/osv/fields.py
index 190453d477a..478f982cd69 100644
--- a/openerp/osv/fields.py
+++ b/openerp/osv/fields.py
@@ -1461,11 +1461,9 @@ class property(function):
def _get_by_id(self, obj, cr, uid, prop_name, ids, context=None):
prop = obj.pool.get('ir.property')
vids = [obj._name + ',' + str(oid) for oid in ids]
- def_id = self._field_get(cr, uid, obj._name, prop_name[0])
- company = obj.pool.get('res.company')
- cid = company._company_default_get(cr, uid, obj._name, def_id, context=context)
- domain = [('fields_id.model', '=', obj._name), ('fields_id.name', 'in', prop_name), ('company_id', '=', cid)]
- #domain = prop._get_domain(cr, uid, prop_name, obj._name, context)
+ domain = [('fields_id.model', '=', obj._name), ('fields_id.name', 'in', prop_name)]
+ if context and context.get('company_id'):
+ domain += [('company_id', '=', context.get('company_id'))]
if vids:
domain = [('res_id', 'in', vids)] + domain
return prop.search(cr, uid, domain, context=context)
@@ -1475,7 +1473,12 @@ class property(function):
if context is None:
context = {}
- nids = self._get_by_id(obj, cr, uid, [prop_name], [id], context)
+ def_id = self._field_get(cr, uid, obj._name, prop_name)
+ company = obj.pool.get('res.company')
+ cid = company._company_default_get(cr, uid, obj._name, def_id, context=context)
+ # TODO for trunk: add new parameter company_id to _get_by_id method
+ context_company = dict(context, company_id=cid)
+ nids = self._get_by_id(obj, cr, uid, [prop_name], [id], context_company)
if nids:
cr.execute('DELETE FROM ir_property WHERE id IN %s', (tuple(nids),))
@@ -1489,10 +1492,6 @@ class property(function):
property_create = True
if property_create:
- def_id = self._field_get(cr, uid, obj._name, prop_name)
- company = obj.pool.get('res.company')
- cid = company._company_default_get(cr, uid, obj._name, def_id,
- context=context)
propdef = obj.pool.get('ir.model.fields').browse(cr, uid, def_id,
context=context)
prop = obj.pool.get('ir.property')
diff --git a/openerp/osv/orm.py b/openerp/osv/orm.py
index b24fd0cb6ae..3126507608f 100644
--- a/openerp/osv/orm.py
+++ b/openerp/osv/orm.py
@@ -770,8 +770,6 @@ class BaseModel(object):
(name_id, context['module'], 'ir.model', model_id)
)
- cr.commit()
-
cr.execute("SELECT * FROM ir_model_fields WHERE model=%s", (self._name,))
cols = {}
for rec in cr.dictfetchall():
@@ -838,7 +836,6 @@ class BaseModel(object):
for key, val in vals.items():
if cols[k][key] != vals[key]:
cr.execute('update ir_model_fields set field_description=%s where model=%s and name=%s', (vals['field_description'], vals['model'], vals['name']))
- cr.commit()
cr.execute("""UPDATE ir_model_fields SET
model_id=%s, field_description=%s, ttype=%s, relation=%s,
select_level=%s, readonly=%s ,required=%s, selectable=%s, relation_field=%s, translate=%s, serialization_field_id=%s
@@ -849,7 +846,6 @@ class BaseModel(object):
vals['select_level'], bool(vals['readonly']), bool(vals['required']), bool(vals['selectable']), vals['relation_field'], bool(vals['translate']), vals['serialization_field_id'], vals['model'], vals['name']
))
break
- cr.commit()
#
# Goal: try to apply inheritance at the instanciation level and
@@ -2189,6 +2185,37 @@ class BaseModel(object):
r['__fold'] = folded.get(r[groupby] and r[groupby][0], False)
return result
+ def _read_group_generate_order_by(self, orderby, aggregated_fields, groupby, query):
+ """
+ Generates the ORDER BY sql clause for the read group method. Adds the missing JOIN clause
+ to the query if order should be computed against m2o field.
+ :param orderby: the orderby definition in the form "%(field)s %(order)s"
+ :param aggregated_fields: list of aggregated fields in the query
+ :param groupby: the current groupby field name
+ :param query: the query object used to construct the query afterwards
+ """
+ orderby_list = []
+ ob = []
+ for order_splits in orderby.split(','):
+ order_split = order_splits.split()
+ orderby_field = order_split[0]
+ fields = openerp.osv.fields
+ if isinstance(self._all_columns[orderby_field].column, (fields.date, fields.datetime)):
+ continue
+ orderby_dir = len(order_split) == 2 and order_split[1].upper() == 'ASC' and 'ASC' or 'DESC'
+ if orderby_field == groupby:
+ orderby_item = self._generate_order_by(order_splits, query).replace('ORDER BY ', '')
+ if orderby_item:
+ orderby_list.append(orderby_item)
+ ob += [obi.split()[0] for obi in orderby_item.split(',')]
+ elif orderby_field in aggregated_fields:
+ orderby_list.append('%s %s' % (orderby_field,orderby_dir))
+
+ if orderby_list:
+ return ' ORDER BY %s' % (','.join(orderby_list)), ob and ','.join(ob) or ''
+ else:
+ return '', ''
+
def read_group(self, cr, uid, domain, fields, groupby, offset=0, limit=None, context=None, orderby=False):
"""
Get the list of records in list view grouped by the given ``groupby`` fields
@@ -2301,6 +2328,12 @@ class BaseModel(object):
qualified_field = self._inherits_join_calc(f, query)
flist += "%s(%s) AS %s" % (group_operator, qualified_field, f)
+ order = orderby or groupby
+ orderby_clause = ''
+ ob = ''
+ if order:
+ orderby_clause, ob = self._read_group_generate_order_by(order, aggregated_fields, groupby, query)
+
gb = groupby and (' GROUP BY ' + qualified_groupby_field) or ''
from_clause, where_clause, where_clause_params = query.get_sql()
@@ -2309,20 +2342,21 @@ class BaseModel(object):
offset_str = offset and ' offset %d' % offset or ''
if len(groupby_list) < 2 and context.get('group_by_no_leaf'):
group_count = '_'
- cr.execute('SELECT min(%s.id) AS id, count(%s.id) AS %s_count' % (self._table, self._table, group_count) + (flist and ',') + flist + ' FROM ' + from_clause + where_clause + gb + limit_str + offset_str, where_clause_params)
+ cr.execute('SELECT min(%s.id) AS id, count(%s.id) AS %s_count' % (self._table, self._table, group_count) + (flist and ',') + flist + ' FROM ' + from_clause + where_clause + gb + (ob and ',') + ob + orderby_clause + limit_str + offset_str, where_clause_params)
alldata = {}
groupby = group_by
- for r in cr.dictfetchall():
+
+ fetched_data = cr.dictfetchall()
+
+ data_ids = []
+ for r in fetched_data:
for fld, val in r.items():
if val is None: r[fld] = False
alldata[r['id']] = r
+ data_ids.append(r['id'])
del r['id']
- order = orderby or groupby
- data_ids = self.search(cr, uid, [('id', 'in', alldata.keys())], order=order, context=context)
- # the IDs of records that have groupby field value = False or '' should be included too
- data_ids += set(alldata.keys()).difference(data_ids)
if groupby:
data = self.read(cr, uid, data_ids, [groupby], context=context)
@@ -2716,10 +2750,16 @@ class BaseModel(object):
('float8', 'float', get_pg_type(f)[1], '::'+get_pg_type(f)[1]),
]
if f_pg_type == 'varchar' and f._type == 'char' and ((f.size is None and f_pg_size) or f_pg_size < f.size):
- cr.execute('ALTER TABLE "%s" RENAME COLUMN "%s" TO temp_change_size' % (self._table, k))
- cr.execute('ALTER TABLE "%s" ADD COLUMN "%s" %s' % (self._table, k, pg_varchar(f.size)))
- cr.execute('UPDATE "%s" SET "%s"=temp_change_size::%s' % (self._table, k, pg_varchar(f.size)))
- cr.execute('ALTER TABLE "%s" DROP COLUMN temp_change_size CASCADE' % (self._table,))
+ try:
+ with cr.savepoint():
+ cr.execute('ALTER TABLE "%s" ALTER COLUMN "%s" TYPE %s' % (self._table, k, pg_varchar(f.size)))
+ except psycopg2.NotSupportedError:
+ # In place alter table cannot be done because a view is depending of this field.
+ # Do a manual copy. This will drop the view (that will be recreated later)
+ cr.execute('ALTER TABLE "%s" RENAME COLUMN "%s" TO temp_change_size' % (self._table, k))
+ cr.execute('ALTER TABLE "%s" ADD COLUMN "%s" %s' % (self._table, k, pg_varchar(f.size)))
+ cr.execute('UPDATE "%s" SET "%s"=temp_change_size::%s' % (self._table, k, pg_varchar(f.size)))
+ cr.execute('ALTER TABLE "%s" DROP COLUMN temp_change_size CASCADE' % (self._table,))
cr.commit()
_schema.debug("Table '%s': column '%s' (type varchar) changed size from %s to %s",
self._table, k, f_pg_size or 'unlimited', f.size or 'unlimited')
@@ -2727,10 +2767,10 @@ class BaseModel(object):
if (f_pg_type==c[0]) and (f._type==c[1]):
if f_pg_type != f_obj_type:
ok = True
- cr.execute('ALTER TABLE "%s" RENAME COLUMN "%s" TO temp_change_size' % (self._table, k))
+ cr.execute('ALTER TABLE "%s" RENAME COLUMN "%s" TO __temp_type_cast' % (self._table, k))
cr.execute('ALTER TABLE "%s" ADD COLUMN "%s" %s' % (self._table, k, c[2]))
- cr.execute(('UPDATE "%s" SET "%s"=temp_change_size'+c[3]) % (self._table, k))
- cr.execute('ALTER TABLE "%s" DROP COLUMN temp_change_size CASCADE' % (self._table,))
+ cr.execute(('UPDATE "%s" SET "%s"= __temp_type_cast'+c[3]) % (self._table, k))
+ cr.execute('ALTER TABLE "%s" DROP COLUMN __temp_type_cast CASCADE' % (self._table,))
cr.commit()
_schema.debug("Table '%s': column '%s' changed type from %s to %s",
self._table, k, c[0], c[1])
@@ -4622,24 +4662,22 @@ class BaseModel(object):
blacklist_given_fields(self)
- fields_to_read = [f for f in self.check_field_access_rights(cr, uid, 'read', None)
- if f not in blacklist]
- data = self.read(cr, uid, [id], fields_to_read, context=context)
+
+ fields_to_copy = dict((f,fi) for f, fi in self._all_columns.iteritems()
+ if f not in default
+ if f not in blacklist
+ if not isinstance(fi.column, fields.function))
+
+ data = self.read(cr, uid, [id], fields_to_copy.keys(), context=context)
if data:
data = data[0]
else:
- raise IndexError(_("Record #%d of %s not found, cannot copy!") % (id, self._name))
+ raise IndexError( _("Record #%d of %s not found, cannot copy!") %( id, self._name))
res = dict(default)
- for f, colinfo in self._all_columns.items():
+ for f, colinfo in fields_to_copy.iteritems():
field = colinfo.column
- if f in default:
- pass
- elif f in blacklist:
- pass
- elif isinstance(field, fields.function):
- pass
- elif field._type == 'many2one':
+ if field._type == 'many2one':
res[f] = data[f] and data[f][0]
elif field._type == 'one2many':
other = self.pool[field._obj]
diff --git a/openerp/service/db.py b/openerp/service/db.py
index fa1bc1336fc..adbd058755d 100644
--- a/openerp/service/db.py
+++ b/openerp/service/db.py
@@ -311,7 +311,7 @@ def exp_list(document=False):
cr.execute("select datname from pg_database where datdba=(select usesysid from pg_user where usename=%s) and datname not in %s order by datname", (db_user, templates_list))
else:
cr.execute("select datname from pg_database where datname not in %s order by datname", (templates_list,))
- res = [str(name) for (name,) in cr.fetchall()]
+ res = [openerp.tools.ustr(name) for (name,) in cr.fetchall()]
except Exception:
res = []
res.sort()
diff --git a/openerp/sql_db.py b/openerp/sql_db.py
index de9d21dcd82..694ae02b3d9 100644
--- a/openerp/sql_db.py
+++ b/openerp/sql_db.py
@@ -3,7 +3,7 @@
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2009 Tiny SPRL ().
-# Copyright (C) 2010-2013 OpenERP s.a. ().
+# Copyright (C) 2010-2014 OpenERP s.a. ().
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
@@ -27,8 +27,11 @@ the database, *not* a database abstraction toolkit. Database abstraction is what
the ORM does, in fact.
"""
+from contextlib import contextmanager
from functools import wraps
import logging
+import time
+import uuid
import psycopg2.extensions
from psycopg2.extensions import ISOLATION_LEVEL_AUTOCOMMIT, ISOLATION_LEVEL_READ_COMMITTED, ISOLATION_LEVEL_REPEATABLE_READ
from psycopg2.pool import PoolError
@@ -344,6 +347,19 @@ class Cursor(object):
"""
return self._cnx.rollback()
+ @contextmanager
+ @check
+ def savepoint(self):
+ """context manager entering in a new savepoint"""
+ name = uuid.uuid1().hex
+ self.execute('SAVEPOINT "%s"' % name)
+ try:
+ yield
+ self.execute('RELEASE SAVEPOINT "%s"' % name)
+ except:
+ self.execute('ROLLBACK TO SAVEPOINT "%s"' % name)
+ raise
+
@check
def __getattr__(self, name):
return getattr(self._obj, name)
diff --git a/openerp/tools/image.py b/openerp/tools/image.py
index 5e5055f252d..196ace2da60 100644
--- a/openerp/tools/image.py
+++ b/openerp/tools/image.py
@@ -25,7 +25,7 @@ except ImportError:
import StringIO
from PIL import Image
-from PIL import ImageOps
+from PIL import ImageEnhance
from random import randint
# ----------------------------------------
@@ -81,8 +81,13 @@ def image_resize_image(base64_source, size=(1024, 1024), encoding='base64', file
return base64_source
if image.size != size:
- # If you need faster thumbnails you may use use Image.NEAREST
- image = ImageOps.fit(image, size, Image.ANTIALIAS)
+ # create a thumbnail: will resize and keep ratios, then sharpen for better looking result
+ image.thumbnail(size, Image.ANTIALIAS)
+ sharpener = ImageEnhance.Sharpness(image.convert('RGBA'))
+ resized_image = sharpener.enhance(2.0)
+ # create a transparent image for background and paste the image on it
+ image = Image.new('RGBA', size, (255, 255, 255, 0))
+ image.paste(resized_image, ((size[0] - resized_image.size[0]) / 2, (size[1] - resized_image.size[1]) / 2))
if image.mode not in ["1", "L", "P", "RGB", "RGBA"]:
image = image.convert("RGB")