[MERGE] forward port of branch saas-2 up to revid 5014 mat@openerp.com-20140220145701-6y7s1roj88q7ys13
bzr revid: chs@openerp.com-20140220160548-0rhulha4cml6t08v
This commit is contained in:
commit
65db3c635e
|
@ -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)
|
||||
|
|
|
@ -180,6 +180,7 @@
|
|||
<field name="auto_refresh"/>
|
||||
<field name="auto_search"/>
|
||||
<field name="filter"/>
|
||||
<field name="multi"/>
|
||||
</group>
|
||||
</group>
|
||||
<group string="Help">
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -93,7 +93,7 @@
|
|||
<label for="font" />
|
||||
<div>
|
||||
<div>
|
||||
<field name="font" class="oe_inline" colspan="2" on_change="onchange_font_name(font, rml_header, rml_header2, rml_header3)" domain="[('mode', 'in', ('normal', 'regular', 'all', 'book'))]" />
|
||||
<field name="font" class="oe_inline" colspan="2" on_change="onchange_font_name(font, rml_header, rml_header2, rml_header3)" />
|
||||
<button string="(reload fonts)" name="act_discover_fonts" type="object" class="oe_link" colspan="1"/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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',
|
||||
}
|
||||
|
|
|
@ -231,7 +231,7 @@
|
|||
<field name="model">res.users</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Users">
|
||||
<field name="name" filter_domain="['|', '|', ('name','ilike',self), ('login','ilike',self), ('email,'ilike',self)]" string="User"/>
|
||||
<field name="name" filter_domain="['|', '|', ('name','ilike',self), ('login','ilike',self), ('email','ilike',self)]" string="User"/>
|
||||
<field name="company_ids" string="Company" groups="base.group_multi_company"/>
|
||||
</search>
|
||||
</field>
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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': """<root>
|
||||
<item order="1"/>
|
||||
</root>
|
||||
"""
|
||||
})
|
||||
id2 = Views.create(self.cr, self.uid, {
|
||||
'name': "Extension",
|
||||
'type': 'qweb',
|
||||
'inherit_id': id,
|
||||
'arch': """<xpath expr="//item" position="before">
|
||||
<item order="2"/>
|
||||
</xpath>
|
||||
"""
|
||||
})
|
||||
|
||||
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):
|
||||
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
|
||||
# Copyright (C) 2010-2013 OpenERP s.a. (<http://openerp.com>).
|
||||
# Copyright (C) 2010-2014 OpenERP s.a. (<http://openerp.com>).
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
@ -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)
|
||||
|
|
|
@ -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")
|
||||
|
||||
|
|
Loading…
Reference in New Issue