[MERGE] osv: improved user-friendly error message for violation of NOT_NULL constraints
bzr revid: odo@openerp.com-20100521105042-0fp6cdmb3mc6khzb bzr revid: odo@openerp.com-20100521135626-679j9of9pn9w5bdp bzr revid: odo@openerp.com-20100521162142-p2j60p1ro0jhsrkd
This commit is contained in:
commit
4699eabe75
|
@ -109,11 +109,13 @@
|
|||
<notebook colspan="4">
|
||||
<page string="User">
|
||||
<field name="address_id" select="1"/>
|
||||
<field name="user_email" widget="email"/>
|
||||
<field name="company_id" required="1"/>
|
||||
<field name="action_id" required="True"/>
|
||||
<field domain="[('usage','=','menu')]" name="menu_id" required="True"/>
|
||||
<field name="context_lang"/>
|
||||
<field name="context_tz"/>
|
||||
<field name="view" readonly="0" />
|
||||
<group colspan="2" col="2">
|
||||
<separator string="Signature" colspan="2"/>
|
||||
<field colspan="2" name="signature" nolabel="1"/>
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
# -*- coding: utf8 -*-
|
||||
|
||||
__name__ = "res.partner.address: change type of 'function' field many2one to char"
|
||||
|
||||
def migrate(cr, version):
|
||||
change_column_type(cr,'res_partner_address')
|
||||
|
||||
def change_column_type(cr,table):
|
||||
cr.execute('SELECT id, name FROM res_partner_function')
|
||||
all_function = cr.fetchall()
|
||||
cr.execute('ALTER TABLE %s ADD COLUMN temp_function VARCHAR(64)' % table)
|
||||
for fn in all_function:
|
||||
cr.execute("UPDATE %s SET temp_function = '%s' WHERE function = %s" % (table,fn[1],fn[0]))
|
||||
cr.execute("ALTER TABLE %s DROP COLUMN function CASCADE" % table)
|
||||
cr.execute("ALTER TABLE %s RENAME COLUMN temp_function TO function" % table)
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
|
||||
#
|
||||
|
@ -15,7 +15,7 @@
|
|||
# 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/>.
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
|
@ -27,21 +27,6 @@ import ir
|
|||
import pooler
|
||||
from tools.translate import _
|
||||
|
||||
class res_partner_function(osv.osv):
|
||||
_name = 'res.partner.function'
|
||||
_description = 'Function of the contact'
|
||||
_columns = {
|
||||
'name': fields.char('Function Name', size=64, required=True),
|
||||
'code': fields.char('Code', size=8, required=True),
|
||||
'ref':fields.char('Notes', size=32,),
|
||||
}
|
||||
_order = 'name'
|
||||
_sql_constraints = [
|
||||
('code_uniq', 'unique (code)', 'The Code of the Partner Function must be unique !')
|
||||
]
|
||||
res_partner_function()
|
||||
|
||||
|
||||
class res_payterm(osv.osv):
|
||||
_description = 'Payment term'
|
||||
_name = 'res.payterm'
|
||||
|
@ -149,12 +134,12 @@ class res_partner(osv.osv):
|
|||
'active': fields.boolean('Active'),
|
||||
'customer': fields.boolean('Customer', help="Check this box if the partner is a customer."),
|
||||
'supplier': fields.boolean('Supplier', help="Check this box if the partner is a supplier. If it's not checked, purchase people will not see it when encoding a purchase order."),
|
||||
'city': fields.related('address', 'city', type='char', string='City'),
|
||||
'phone': fields.related('address', 'phone', type='char', string='Phone'),
|
||||
'city': fields.related('address', 'city', type='char', string='City'),
|
||||
'phone': fields.related('address', 'phone', type='char', string='Phone'),
|
||||
'country': fields.related('address', 'country_id', type='many2one', relation='res.country', string='Country'),
|
||||
'employee': fields.boolean('Employee', help="Check this box if the partner is an Employee."),
|
||||
'email': fields.related('address', 'email', type='char', size=240, string='E-mail'),
|
||||
'company_id': fields.many2one('res.company', 'Company', select=1),
|
||||
'company_id': fields.many2one('res.company', 'Company', select=1),
|
||||
}
|
||||
|
||||
def _default_category(self, cr, uid, context={}):
|
||||
|
@ -291,7 +276,7 @@ class res_partner_address(osv.osv):
|
|||
_columns = {
|
||||
'partner_id': fields.many2one('res.partner', 'Partner', ondelete='set null', select=True, help="Keep empty for a private address, not related to partner."),
|
||||
'type': fields.selection( [ ('default','Default'),('invoice','Invoice'), ('delivery','Delivery'), ('contact','Contact'), ('other','Other') ],'Address Type', help="Used to select automatically the right address according to the context in sales and purchases documents."),
|
||||
'function': fields.many2one('res.partner.function', 'Function'),
|
||||
'function': fields.char('Function', size=64),
|
||||
'title': fields.selection(_contact_title_get, 'Title', size=32),
|
||||
'name': fields.char('Contact Name', size=64, select=1),
|
||||
'street': fields.char('Street', size=128),
|
||||
|
@ -469,7 +454,7 @@ class res_partner_category(osv.osv):
|
|||
_columns = {
|
||||
'partner_ids': fields.many2many('res.partner', 'res_partner_category_rel', 'category_id', 'partner_id', 'Partners'),
|
||||
}
|
||||
|
||||
|
||||
res_partner_category()
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
|
|
@ -33,19 +33,6 @@
|
|||
<field name="shortcut">M.</field>
|
||||
</record>
|
||||
|
||||
<record id="function_director" model="res.partner.function">
|
||||
<field name="name">Director</field>
|
||||
<field name="code">CEO</field>
|
||||
</record>
|
||||
<record id="function_it" model="res.partner.function">
|
||||
<field name="name">Chief Technical Officer</field>
|
||||
<field name="code">CTO</field>
|
||||
</record>
|
||||
<record id="function_sale" model="res.partner.function">
|
||||
<field name="name">Salesman</field>
|
||||
<field name="code">SAL</field>
|
||||
</record>
|
||||
|
||||
<!-- Default bank account description -->
|
||||
<record id="bank_normal" model="res.partner.bank.type">
|
||||
<field name="name">Bank account</field>
|
||||
|
|
|
@ -9,41 +9,6 @@
|
|||
<menuitem id="menu_base_config_partner" name="Partners" parent="menu_base_config" sequence="10" groups="base.group_extended"/>
|
||||
<menuitem id="menu_base_config_contact" name="Contacts" parent="menu_base_config" sequence="20" />
|
||||
|
||||
<!--
|
||||
================================
|
||||
Function
|
||||
================================
|
||||
-->
|
||||
<record id="view_partner_function_form" model="ir.ui.view">
|
||||
<field name="name">res.partner.function.form</field>
|
||||
<field name="model">res.partner.function</field>
|
||||
<field name="type">form</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Contact Functions">
|
||||
<field name="name" select="1"/>
|
||||
<field name="code" select="1"/>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
<record id="view_partner_function_tree" model="ir.ui.view">
|
||||
<field name="name">res.partner.function.tree</field>
|
||||
<field name="model">res.partner.function</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Contact Functions">
|
||||
<field name="code"/>
|
||||
<field name="name"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
<record id="action_partner_function_form" model="ir.actions.act_window">
|
||||
<field name="name">Contact Functions</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">res.partner.function</field>
|
||||
<field name="view_type">form</field>
|
||||
</record>
|
||||
<menuitem action="action_partner_function_form" id="menu_partner_function_form" parent="base.menu_base_config_contact" sequence="20"/>
|
||||
|
||||
<!--
|
||||
=====================
|
||||
Partner Address
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
from osv import fields,osv
|
||||
from osv.orm import except_orm, browse_record
|
||||
import tools
|
||||
import operator
|
||||
import pytz
|
||||
import pooler
|
||||
from tools.translate import _
|
||||
|
@ -82,6 +83,11 @@ class groups(osv.osv):
|
|||
default['name'] = default['name'] + _(' (copy)')
|
||||
return super(groups, self).copy(cr, uid, id, default, context=context)
|
||||
|
||||
def get_extended_interface_group(self, cr, uid, context=None):
|
||||
data_obj = self.pool.get('ir.model.data')
|
||||
extended_group_data_id = data_obj._get_id(cr, uid, 'base', 'group_extended')
|
||||
return data_obj.browse(cr, uid, extended_group_data_id, context=context).res_id
|
||||
|
||||
groups()
|
||||
|
||||
class roles(osv.osv):
|
||||
|
@ -167,38 +173,46 @@ class users(osv.osv):
|
|||
body=self.get_welcome_mail_body(
|
||||
cr, uid, context=context) % user)
|
||||
|
||||
def update_user_group(self, cr, uid, ids, name, value, arg, context):
|
||||
""" update User groups according to the interface.
|
||||
def _set_interface_type(self, cr, uid, ids, name, value, arg, context=None):
|
||||
"""Implementation of 'view' function field setter, sets the type of interface of the users.
|
||||
@param name: Name of the field
|
||||
@param arg: User defined argument
|
||||
@param value: new value returned
|
||||
@return: True/False
|
||||
"""
|
||||
if not value: return False
|
||||
if not isinstance(ids, list):
|
||||
ids = [ids]
|
||||
if not value or value not in ['simple','extended']:
|
||||
return False
|
||||
group_obj = self.pool.get('res.groups')
|
||||
extended_group = group_obj.search(cr,uid,[('name','ilike','Extended')])[0]
|
||||
user_groups = self.read(cr, uid, ids, ['groups_id'])[0]['groups_id']
|
||||
if value == 'simple':
|
||||
cr.execute(""" delete from res_groups_users_rel where gid=%s and uid=ANY(%s)""",(extended_group, ids))
|
||||
else:
|
||||
self.write(cr, uid, ids,{'groups_id':[(6, 0, list(set([extended_group] + user_groups)))]})
|
||||
extended_group_id = group_obj.get_extended_interface_group(cr, uid, context=context)
|
||||
self.write(cr, uid, ids, { 'groups_id': [((3 if value == 'simple' else 4), extended_group_id)]}, context=context)
|
||||
return True
|
||||
|
||||
def _set_interface(self, cr, uid, ids, name, args, context=None):
|
||||
""" Sets interface for the User.
|
||||
|
||||
def _get_interface_type(self, cr, uid, ids, name, args, context=None):
|
||||
"""Implementation of 'view' function field getter, returns the type of interface of the users.
|
||||
@param field_name: Name of the field
|
||||
@param arg: User defined argument
|
||||
@return: Dictionary of values
|
||||
"""
|
||||
group_obj = self.pool.get('res.groups')
|
||||
extended_group = group_obj.search(cr,uid,[('name','ilike','Extended')])[0]
|
||||
user_groups = self.read(cr, uid, ids, ['groups_id'])[0]['groups_id']
|
||||
if extended_group not in user_groups:
|
||||
return {ids[0]:'simple'}
|
||||
else:
|
||||
return {ids[0]:'extended'}
|
||||
extended_group_id = group_obj.get_extended_interface_group(cr, uid, context=context)
|
||||
extended_users = group_obj.read(cr, uid, extended_group_id, ['users'], context=context)['users']
|
||||
return dict(zip(ids, ['extended' if user in extended_users else 'simple' for user in ids]))
|
||||
|
||||
def _email_get(self, cr, uid, ids, name, arg, context=None):
|
||||
return dict(map(operator.attrgetter('id', 'address_id.email'), self.browse(cr, uid, ids, context=context)))
|
||||
|
||||
def _email_set(self, cr, uid, ids, name, value, arg, context=None):
|
||||
if not isinstance(ids,list):
|
||||
ids = [ids]
|
||||
address_obj = self.pool.get('res.partner.address')
|
||||
for user in self.browse(cr, uid, ids, context=context):
|
||||
if user.address_id:
|
||||
address_obj.write(cr, uid, user.address_id.id, {'email': value or None}, context=context)
|
||||
else:
|
||||
address_id = address_obj.create(cr, uid, {'name': user.name, 'email': value or None}, context=context)
|
||||
self.write(cr, uid, ids, {'address_id': address_id}, context)
|
||||
return True
|
||||
|
||||
_columns = {
|
||||
'name': fields.char('Name', size=64, required=True, select=True,
|
||||
|
@ -228,9 +242,10 @@ class users(osv.osv):
|
|||
'context_tz': fields.selection(_tz_get, 'Timezone', size=64,
|
||||
help="The user's timezone, used to perform timezone conversions "
|
||||
"between the server and the client."),
|
||||
'view': fields.function(_set_interface, method=True, type='selection', fnct_inv=update_user_group,
|
||||
'view': fields.function(_get_interface_type, method=True, type='selection', fnct_inv=_set_interface_type,
|
||||
selection=[('simple','Simplified'),('extended','Extended')],
|
||||
string='Interface', help="Choose between the simplified interface and the extended one"),
|
||||
'user_email': fields.function(_email_get, method=True, fnct_inv=_email_set, string='Email', type="char"),
|
||||
}
|
||||
|
||||
def read(self,cr, uid, ids, fields=None, context=None, load='_classic_read'):
|
||||
|
|
|
@ -73,8 +73,6 @@
|
|||
"access_res_partner_event_group_partner_manager","res_partner_event group_partner_manager","model_res_partner_event","group_partner_manager",1,1,1,1
|
||||
"access_res_partner_event_type_group_partner_manager","res_partner_event_type group_partner_manager","model_res_partner_event_type","group_partner_manager",1,1,1,1
|
||||
"access_res_partner_event_type_group_user","res_partner_event_type group_user","model_res_partner_event_type","group_user",1,0,0,0
|
||||
"access_res_partner_function_group_user","res_partner_function group_user","model_res_partner_function","group_partner_manager",1,1,1,1
|
||||
"access_res_partner_function_group_partner_manager","res_partner_function group_partner_manager","model_res_partner_function","group_user",1,0,0,0
|
||||
"access_res_partner_som_group_user","res_partner_som group_user","model_res_partner_som","group_partner_manager",1,1,1,1
|
||||
"access_res_partner_som_group_partner_manager","res_partner_som group_partner_manager","model_res_partner_som","group_user",1,0,0,0
|
||||
"access_res_partner_title_group_user","res_partner_title group_user","model_res_partner_title","group_partner_manager",1,1,1,1
|
||||
|
|
|
|
@ -30,7 +30,7 @@ import copy
|
|||
import sys
|
||||
import traceback
|
||||
import logging
|
||||
from psycopg2 import IntegrityError
|
||||
from psycopg2 import IntegrityError, errorcodes
|
||||
from tools.func import wraps
|
||||
|
||||
|
||||
|
@ -65,7 +65,22 @@ class osv_pool(netsvc.Service):
|
|||
for key in self._sql_error.keys():
|
||||
if key in inst[0]:
|
||||
self.abortResponse(1, 'Constraint Error', 'warning', self._sql_error[key])
|
||||
self.abortResponse(1, 'Integrity Error', 'warning', inst[0])
|
||||
if inst.pgcode == errorcodes.NOT_NULL_VIOLATION:
|
||||
msg = 'Sorry, this record cannot be deleted at the moment because other records still reference it.'
|
||||
self.logger.debug("IntegrityError", exc_info=True)
|
||||
try:
|
||||
context = inst.pgerror.split('"public".')[1]
|
||||
model_name = table = context.split('"')[1]
|
||||
model = table.replace("_",".")
|
||||
model_obj = self.get(model)
|
||||
if model_obj:
|
||||
model_name = model_obj._description or model_obj._name
|
||||
msg += '\n\n[object with reference: %s - %s]' % (model_name, model)
|
||||
except Exception:
|
||||
pass
|
||||
self.abortResponse(1, 'Integrity Error', 'warning', msg)
|
||||
else:
|
||||
self.abortResponse(1, 'Integrity Error', 'warning', inst[0])
|
||||
except Exception, e:
|
||||
self.logger.exception("Uncaught exception")
|
||||
raise
|
||||
|
|
Loading…
Reference in New Issue