merge upstream
bzr revid: chs@openerp.com-20130411154519-xvepmjrm4ibqddx7
This commit is contained in:
commit
841cf5aaad
|
@ -19,6 +19,7 @@ Depends:
|
|||
python-docutils,
|
||||
python-feedparser,
|
||||
python-gdata,
|
||||
python-imaging,
|
||||
python-jinja2,
|
||||
python-ldap,
|
||||
python-libxslt1,
|
||||
|
@ -46,7 +47,7 @@ Depends:
|
|||
Conflicts: tinyerp-server, openerp-server, openerp-web
|
||||
Replaces: tinyerp-server, openerp-server, openerp-web
|
||||
Recommends:
|
||||
graphviz, ghostscript, postgresql, python-imaging, python-matplotlib
|
||||
graphviz, ghostscript, postgresql, python-matplotlib, poppler-utils
|
||||
Description: OpenERP Enterprise Resource Management
|
||||
OpenERP, previously known as TinyERP, is a complete ERP and CRM. The main
|
||||
features are accounting (analytic and financial), stock management, sales and
|
||||
|
|
|
@ -17,55 +17,46 @@ DAEMON=/usr/bin/openerp-server
|
|||
NAME=openerp-server
|
||||
DESC=openerp-server
|
||||
CONFIG=/etc/openerp/openerp-server.conf
|
||||
LOGFILE=/var/log/openerp-server.log
|
||||
LOGFILE=/var/log/openerp/openerp-server.log
|
||||
USER=openerp
|
||||
|
||||
test -x ${DAEMON} || exit 0
|
||||
|
||||
set -e
|
||||
|
||||
do_start () {
|
||||
echo -n "Starting ${DESC}: "
|
||||
start-stop-daemon --start --quiet --pidfile /var/run/${NAME}.pid --chuid ${USER} --background --make-pidfile --exec ${DAEMON} -- --config=${CONFIG} --logfile=${LOGFILE}
|
||||
echo "${NAME}."
|
||||
}
|
||||
|
||||
do_stop () {
|
||||
echo -n "Stopping ${DESC}: "
|
||||
start-stop-daemon --stop --quiet --pidfile /var/run/${NAME}.pid --oknodo
|
||||
echo "${NAME}."
|
||||
}
|
||||
|
||||
case "${1}" in
|
||||
start)
|
||||
echo -n "Starting ${DESC}: "
|
||||
start)
|
||||
do_start
|
||||
;;
|
||||
|
||||
start-stop-daemon --start --quiet --pidfile /var/run/${NAME}.pid \
|
||||
--chuid ${USER} --background --make-pidfile \
|
||||
--exec ${DAEMON} -- --config=${CONFIG} \
|
||||
--logfile=${LOGFILE}
|
||||
stop)
|
||||
do_stop
|
||||
;;
|
||||
|
||||
echo "${NAME}."
|
||||
;;
|
||||
restart|force-reload)
|
||||
echo -n "Restarting ${DESC}: "
|
||||
do_stop
|
||||
sleep 1
|
||||
do_start
|
||||
;;
|
||||
|
||||
stop)
|
||||
echo -n "Stopping ${DESC}: "
|
||||
|
||||
start-stop-daemon --stop --quiet --pidfile /var/run/${NAME}.pid \
|
||||
--oknodo
|
||||
|
||||
echo "${NAME}."
|
||||
;;
|
||||
|
||||
restart|force-reload)
|
||||
echo -n "Restarting ${DESC}: "
|
||||
|
||||
start-stop-daemon --stop --quiet --pidfile /var/run/${NAME}.pid \
|
||||
--oknodo
|
||||
|
||||
sleep 1
|
||||
|
||||
start-stop-daemon --start --quiet --pidfile /var/run/${NAME}.pid \
|
||||
--chuid ${USER} --background --make-pidfile \
|
||||
--exec ${DAEMON} -- --config=${CONFIG} \
|
||||
--logfile=${LOGFILE}
|
||||
|
||||
echo "${NAME}."
|
||||
;;
|
||||
|
||||
*)
|
||||
N=/etc/init.d/${NAME}
|
||||
echo "Usage: ${NAME} {start|stop|restart|force-reload}" >&2
|
||||
exit 1
|
||||
;;
|
||||
*)
|
||||
N=/etc/init.d/${NAME}
|
||||
echo "Usage: ${NAME} {start|stop|restart|force-reload}" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
||||
|
|
|
@ -12,9 +12,9 @@ case "${1}" in
|
|||
chown openerp:openerp /etc/openerp/openerp-server.conf
|
||||
chmod 0640 /etc/openerp/openerp-server.conf
|
||||
# Creating log file
|
||||
touch /var/log/openerp-server.log
|
||||
chown openerp:openerp /var/log/openerp-server.log
|
||||
chmod 0640 /var/log/openerp-server.log
|
||||
mkdir -p /var/log/openerp/
|
||||
chown openerp:openerp /var/log/openerp
|
||||
chmod 0750 /var/log/openerp
|
||||
# Creating local storage directory
|
||||
mkdir -p /var/lib/openerp/filestore
|
||||
chown openerp:openerp -R /var/lib/openerp
|
||||
|
|
|
@ -22,6 +22,17 @@
|
|||
""" OpenERP core library.
|
||||
|
||||
"""
|
||||
|
||||
# Make sure the OpenERP server runs in UTC. This is especially necessary
|
||||
# under Windows as under Linux it seems the real import of time is
|
||||
# sufficiently deferred so that setting the TZ environment variable
|
||||
# in openerp.cli.server was working.
|
||||
import os
|
||||
os.environ['TZ'] = 'UTC' # Set the timezone...
|
||||
import time # ... *then* import time.
|
||||
del os
|
||||
del time
|
||||
|
||||
# The hard-coded super-user id (a.k.a. administrator, or root user).
|
||||
SUPERUSER_ID = 1
|
||||
|
||||
|
@ -35,10 +46,8 @@ import osv
|
|||
import pooler
|
||||
import release
|
||||
import report
|
||||
import run_tests
|
||||
import service
|
||||
import sql_db
|
||||
import test
|
||||
import tools
|
||||
import workflow
|
||||
# backward compatilbility
|
||||
|
|
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
|
@ -206,7 +206,7 @@ class act_window(osv.osv):
|
|||
def _search_view(self, cr, uid, ids, name, arg, context=None):
|
||||
res = {}
|
||||
for act in self.browse(cr, uid, ids, context=context):
|
||||
field_get = self.pool.get(act.res_model).fields_view_get(cr, uid,
|
||||
field_get = self.pool[act.res_model].fields_view_get(cr, uid,
|
||||
act.search_view_id and act.search_view_id.id or False,
|
||||
'search', context=context)
|
||||
res[act.id] = str(field_get)
|
||||
|
@ -608,7 +608,7 @@ class actions_server(osv.osv):
|
|||
.read(cr, uid, action.action_id.id, context=context)
|
||||
|
||||
if action.state=='code':
|
||||
eval(action.code, cxt, mode="exec", nocopy=True) # nocopy allows to return 'action'
|
||||
eval(action.code.strip(), cxt, mode="exec", nocopy=True) # nocopy allows to return 'action'
|
||||
if 'action' in cxt:
|
||||
return cxt['action']
|
||||
|
||||
|
@ -791,7 +791,7 @@ Launch Manually Once: after having been launched manually, it sets automatically
|
|||
act_type = self.pool.get('ir.actions.actions').read(cr, uid, wizard.action_id.id, ['type'], context=context)
|
||||
|
||||
res = self.pool.get(act_type['type']).read(cr, uid, wizard.action_id.id, [], context=context)
|
||||
if act_type<>'ir.actions.act_window':
|
||||
if act_type['type'] != 'ir.actions.act_window':
|
||||
return res
|
||||
res.setdefault('context','{}')
|
||||
res['nodestroy'] = True
|
||||
|
|
|
@ -21,12 +21,15 @@
|
|||
|
||||
import hashlib
|
||||
import itertools
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
|
||||
from openerp import tools
|
||||
from openerp.osv import fields,osv
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
class ir_attachment(osv.osv):
|
||||
"""Attachments are used to link binary files or url to any openerp document.
|
||||
|
||||
|
@ -80,7 +83,7 @@ class ir_attachment(osv.osv):
|
|||
if bin_size:
|
||||
r = os.path.getsize(full_path)
|
||||
else:
|
||||
r = open(full_path).read().encode('base64')
|
||||
r = open(full_path,'rb').read().encode('base64')
|
||||
except IOError:
|
||||
_logger.error("_read_file reading %s",full_path)
|
||||
return r
|
||||
|
|
|
@ -18,8 +18,9 @@
|
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
import time
|
||||
import logging
|
||||
import threading
|
||||
import time
|
||||
import psycopg2
|
||||
from datetime import datetime
|
||||
from dateutil.relativedelta import relativedelta
|
||||
|
@ -181,6 +182,7 @@ class ir_cron(osv.osv):
|
|||
If a job was processed, returns True, otherwise returns False.
|
||||
"""
|
||||
db = openerp.sql_db.db_connect(db_name)
|
||||
threading.current_thread().dbname = db_name
|
||||
cr = db.cursor()
|
||||
jobs = []
|
||||
try:
|
||||
|
@ -237,6 +239,9 @@ class ir_cron(osv.osv):
|
|||
# we're exiting due to an exception while acquiring the lock
|
||||
lock_cr.close()
|
||||
|
||||
if hasattr(threading.current_thread(), 'dbname'): # cron job could have removed it as side-effect
|
||||
del threading.current_thread().dbname
|
||||
|
||||
def _try_lock(self, cr, uid, ids, context=None):
|
||||
"""Try to grab a dummy exclusive write-lock to the rows with the given ids,
|
||||
to make sure a following write() or unlink() will not block due
|
||||
|
|
|
@ -28,7 +28,7 @@ class ir_filters(osv.osv):
|
|||
_description = 'Filters'
|
||||
|
||||
def _list_all_models(self, cr, uid, context=None):
|
||||
cr.execute("SELECT model, name from ir_model")
|
||||
cr.execute("SELECT model, name FROM ir_model ORDER BY name")
|
||||
return cr.fetchall()
|
||||
|
||||
def copy(self, cr, uid, id, default=None, context=None):
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
<field name="name"/>
|
||||
<field name="user_id"/>
|
||||
<field name="model_id"/>
|
||||
<field name="is_default"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="domain"/>
|
||||
|
@ -35,6 +36,7 @@
|
|||
<field name="name"/>
|
||||
<field name="model_id"/>
|
||||
<field name="user_id"/>
|
||||
<field name="is_default"/>
|
||||
<field name="domain" groups="base.group_no_one"/>
|
||||
<field name="context" groups="base.group_no_one"/>
|
||||
</tree>
|
||||
|
@ -45,12 +47,14 @@
|
|||
<field name="arch" type="xml">
|
||||
<search string="Filters">
|
||||
<field name="name" string="Filter Name"/>
|
||||
<filter string="Personal" domain="[('user_id','!=',False)]" help="Filters visible only for one user"/>
|
||||
<filter string="Shared" domain="[('user_id','=',False)]" help="Filters shared with all users"/>
|
||||
<filter string="User" domain="[('user_id','!=',False)]" name="user" help="Filters visible only for one user"/>
|
||||
<filter string="Shared" domain="[('user_id','=',False)]" name="shared" help="Filters shared with all users"/>
|
||||
<filter string="My filters" domain="[('user_id','=',uid)]" name="my_filters" help="Filters created by myself"/>
|
||||
<separator/>
|
||||
<filter icon="terp-personal" domain="[('user_id','in',(uid, False))]"
|
||||
name="my_filters"
|
||||
string="My Filters"/>
|
||||
<group expand="0" string="Group By...">
|
||||
<filter string="User" domain="[]" context="{'group_by':'user_id'}"/>
|
||||
<filter string="Model" domain="[]" context="{'group_by':'model_id'}"/>
|
||||
</group>
|
||||
<field name="model_id"/>
|
||||
<field name="user_id"/>
|
||||
</search>
|
||||
|
|
|
@ -25,6 +25,7 @@ import time
|
|||
import types
|
||||
|
||||
import openerp
|
||||
import openerp.modules.registry
|
||||
from openerp import SUPERUSER_ID
|
||||
from openerp import netsvc, pooler, tools
|
||||
from openerp.osv import fields,osv
|
||||
|
@ -168,7 +169,9 @@ class ir_model(osv.osv):
|
|||
if not context.get(MODULE_UNINSTALL_FLAG):
|
||||
# only reload pool for normal unlink. For module uninstall the
|
||||
# reload is done independently in openerp.modules.loading
|
||||
cr.commit() # must be committed before reloading registry in new cursor
|
||||
pooler.restart_pool(cr.dbname)
|
||||
openerp.modules.registry.RegistryManager.signal_registry_change(cr.dbname)
|
||||
|
||||
return res
|
||||
|
||||
|
@ -194,7 +197,7 @@ class ir_model(osv.osv):
|
|||
field_state='manual',
|
||||
select=vals.get('select_level', '0'))
|
||||
self.pool.get(vals['model'])._auto_init(cr, ctx)
|
||||
#pooler.restart_pool(cr.dbname)
|
||||
openerp.modules.registry.RegistryManager.signal_registry_change(cr.dbname)
|
||||
return res
|
||||
|
||||
def instanciate(self, cr, user, model, context=None):
|
||||
|
@ -260,7 +263,6 @@ class ir_model_fields(osv.osv):
|
|||
'state': lambda self,cr,uid,ctx=None: (ctx and ctx.get('manual',False)) and 'manual' or 'base',
|
||||
'on_delete': 'set null',
|
||||
'select_level': '0',
|
||||
'size': 64,
|
||||
'field_description': '',
|
||||
'selectable': 1,
|
||||
}
|
||||
|
@ -290,10 +292,10 @@ class ir_model_fields(osv.osv):
|
|||
return True
|
||||
|
||||
def _size_gt_zero_msg(self, cr, user, ids, context=None):
|
||||
return _('Size of the field can never be less than 1 !')
|
||||
return _('Size of the field can never be less than 0 !')
|
||||
|
||||
_sql_constraints = [
|
||||
('size_gt_zero', 'CHECK (size>0)',_size_gt_zero_msg ),
|
||||
('size_gt_zero', 'CHECK (size>=0)',_size_gt_zero_msg ),
|
||||
]
|
||||
|
||||
def _drop_column(self, cr, uid, ids, context=None):
|
||||
|
@ -319,6 +321,9 @@ class ir_model_fields(osv.osv):
|
|||
|
||||
self._drop_column(cr, user, ids, context)
|
||||
res = super(ir_model_fields, self).unlink(cr, user, ids, context)
|
||||
if not context.get(MODULE_UNINSTALL_FLAG):
|
||||
cr.commit()
|
||||
openerp.modules.registry.RegistryManager.signal_registry_change(cr.dbname)
|
||||
return res
|
||||
|
||||
def create(self, cr, user, vals, context=None):
|
||||
|
@ -350,6 +355,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)
|
||||
openerp.modules.registry.RegistryManager.signal_registry_change(cr.dbname)
|
||||
|
||||
return res
|
||||
|
||||
|
@ -466,6 +472,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)
|
||||
openerp.modules.registry.RegistryManager.signal_registry_change(cr.dbname)
|
||||
return res
|
||||
|
||||
class ir_model_constraint(Model):
|
||||
|
|
|
@ -151,7 +151,7 @@
|
|||
'readonly': [('ttype','not in', ['many2one','one2many','many2many'])]}"/>
|
||||
<field name="relation_field" attrs="{'required': [('ttype','=','one2many')], 'readonly': [('ttype','!=','one2many')]}"/>
|
||||
<field name="selection" attrs="{'required': [('ttype','in',['selection','reference'])], 'readonly': [('ttype','not in',['selection','reference'])]}"/>
|
||||
<field name="size" attrs="{'required': [('ttype','in',['char','reference'])], 'readonly': [('ttype','not in',['char','reference'])]}"/>
|
||||
<field name="size" attrs="{'invisible': [('ttype','not in',['char','text','reference'])]}"/>
|
||||
<field name="domain" attrs="{'readonly': [('relation','=','')]}"/>
|
||||
<field name="serialization_field_id" attrs="{'readonly': [('state','=','base')]}" domain="[('ttype','=','serialized'), ('model_id', '=', model_id)]"/>
|
||||
<field name="on_delete" attrs="{'readonly': [('ttype','!=','many2one')]}"/>
|
||||
|
|
|
@ -83,7 +83,8 @@ class view(osv.osv):
|
|||
}
|
||||
_defaults = {
|
||||
'arch': '<?xml version="1.0"?>\n<tree string="My view">\n\t<field name="name"/>\n</tree>',
|
||||
'priority': 16
|
||||
'priority': 16,
|
||||
'type': 'tree',
|
||||
}
|
||||
_order = "priority,name"
|
||||
|
||||
|
|
|
@ -411,7 +411,6 @@ class module(osv.osv):
|
|||
if to_install_ids:
|
||||
self.button_install(cr, uid, to_install_ids, context=context)
|
||||
|
||||
openerp.modules.registry.RegistryManager.signal_registry_change(cr.dbname)
|
||||
return dict(ACTION_DICT, name=_('Install'))
|
||||
|
||||
def button_immediate_install(self, cr, uid, ids, context=None):
|
||||
|
@ -500,7 +499,6 @@ class module(osv.osv):
|
|||
raise orm.except_orm(_('Error'), _("The `base` module cannot be uninstalled"))
|
||||
dep_ids = self.downstream_dependencies(cr, uid, ids, context=context)
|
||||
self.write(cr, uid, ids + dep_ids, {'state': 'to remove'})
|
||||
openerp.modules.registry.RegistryManager.signal_registry_change(cr.dbname)
|
||||
return dict(ACTION_DICT, name=_('Uninstall'))
|
||||
|
||||
def button_uninstall_cancel(self, cr, uid, ids, context=None):
|
||||
|
|
|
@ -125,6 +125,7 @@
|
|||
<field name="model">res.partner.bank</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Bank Accounts">
|
||||
<field name="state" invisible="1"/>
|
||||
<field name="sequence" invisible="1"/>
|
||||
<field name="acc_number"/>
|
||||
<field name="bank_name"/>
|
||||
|
|
|
@ -305,7 +305,7 @@ class res_company(osv.osv):
|
|||
<frame id="first" x1="1.3cm" y1="3.0cm" height="%s" width="19.0cm"/>
|
||||
<stylesheet>
|
||||
<paraStyle name="main_footer" fontName="DejaVu Sans" fontSize="8.0" alignment="CENTER"/>
|
||||
<paraStyle name="main_header" fontName="DejaVu Sans" fontSize="8.0" leading="10" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/>
|
||||
<paraStyle name="main_header" fontName="DejaVu Sans" fontSize="8.0" leading="10" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/>
|
||||
</stylesheet>
|
||||
<pageGraphics>
|
||||
<!-- You Logo - Change X,Y,Width and Height -->
|
||||
|
@ -344,8 +344,8 @@ class res_company(osv.osv):
|
|||
</pageTemplate>
|
||||
</header>"""
|
||||
|
||||
_header_a4 = _header_main % ('23.0cm', '27.6cm', '27.7cm', '27.7cm', '27.8cm', '27.3cm', '25.3cm', '25.0cm', '25.0cm', '24.6cm', '24.6cm', '24.5cm', '24.5cm')
|
||||
_header_letter = _header_main % ('21.3cm', '25.9cm', '26.0cm', '26.0cm', '26.1cm', '25.6cm', '23.6cm', '23.3cm', '23.3cm', '22.9cm', '22.9cm', '22.8cm', '22.8cm')
|
||||
_header_a4 = _header_main % ('21.7cm', '27.7cm', '27.7cm', '27.7cm', '27.8cm', '27.3cm', '25.3cm', '25.0cm', '25.0cm', '24.6cm', '24.6cm', '24.5cm', '24.5cm')
|
||||
_header_letter = _header_main % ('20cm', '26.0cm', '26.0cm', '26.0cm', '26.1cm', '25.6cm', '23.6cm', '23.3cm', '23.3cm', '22.9cm', '22.9cm', '22.8cm', '22.8cm')
|
||||
|
||||
def onchange_paper_format(self, cr, uid, ids, paper_format, context=None):
|
||||
if paper_format == 'us_letter':
|
||||
|
|
|
@ -573,7 +573,10 @@ class res_config_settings(osv.osv_memory, res_config_module_installation_mixin):
|
|||
if action:
|
||||
return action
|
||||
|
||||
config = self.pool.get('res.config').next(cr, uid, [], context=context) or {}
|
||||
# After the uninstall/install calls, the self.pool is no longer valid.
|
||||
# So we reach into the RegistryManager directly.
|
||||
res_config = openerp.modules.registry.RegistryManager.get(cr.dbname)['res.config']
|
||||
config = res_config.next(cr, uid, [], context=context) or {}
|
||||
if config.get('type') not in ('ir.actions.act_window_close',):
|
||||
return config
|
||||
|
||||
|
|
|
@ -71,7 +71,7 @@ addresses belonging to this country.\n\nYou can use the python-style string pate
|
|||
name_search = location_name_search
|
||||
|
||||
def create(self, cursor, user, vals, context=None):
|
||||
if 'code' in vals:
|
||||
if vals.get('code'):
|
||||
vals['code'] = vals['code'].upper()
|
||||
return super(Country, self).create(cursor, user, vals,
|
||||
context=context)
|
||||
|
|
|
@ -49,7 +49,7 @@ class res_currency(osv.osv):
|
|||
id, rate = cr.fetchall()[0]
|
||||
res[id] = rate
|
||||
else:
|
||||
res[id] = 0
|
||||
raise osv.except_osv(_('Error!'),_("No currency rate associated for currency %d for the given period" % (id)))
|
||||
return res
|
||||
_name = "res.currency"
|
||||
_description = "Currency"
|
||||
|
|
|
@ -381,16 +381,30 @@ class res_partner(osv.osv, format_address):
|
|||
|
||||
def create(self, cr, uid, vals, context=None):
|
||||
if context is None:
|
||||
context={}
|
||||
context = {}
|
||||
# Update parent and siblings records
|
||||
if vals.get('parent_id') and vals.get('use_parent_address'):
|
||||
domain_siblings = [('parent_id', '=', vals['parent_id']), ('use_parent_address', '=', True)]
|
||||
update_ids = [vals['parent_id']] + self.search(cr, uid, domain_siblings, context=context)
|
||||
self.update_address(cr, uid, update_ids, vals, context)
|
||||
return super(res_partner,self).create(cr, uid, vals, context=context)
|
||||
if vals.get('parent_id'):
|
||||
if 'use_parent_address' in vals:
|
||||
use_parent_address = vals['use_parent_address']
|
||||
else:
|
||||
use_parent_address = self.default_get(cr, uid, ['use_parent_address'], context=context)['use_parent_address']
|
||||
|
||||
if use_parent_address:
|
||||
domain_siblings = [('parent_id', '=', vals['parent_id']), ('use_parent_address', '=', True)]
|
||||
update_ids = [vals['parent_id']] + self.search(cr, uid, domain_siblings, context=context)
|
||||
self.update_address(cr, uid, update_ids, vals, context)
|
||||
|
||||
# add missing address keys
|
||||
onchange_values = self.onchange_address(cr, uid, [], use_parent_address,
|
||||
vals['parent_id'], context=context).get('value') or {}
|
||||
vals.update(dict((key, value)
|
||||
for key, value in onchange_values.iteritems()
|
||||
if key in ADDRESS_FIELDS and key not in vals))
|
||||
|
||||
return super(res_partner, self).create(cr, uid, vals, context=context)
|
||||
|
||||
def update_address(self, cr, uid, ids, vals, context=None):
|
||||
addr_vals = dict((key, vals[key]) for key in POSTAL_ADDRESS_FIELDS if vals.get(key))
|
||||
addr_vals = dict((key, vals[key]) for key in POSTAL_ADDRESS_FIELDS if key in vals)
|
||||
if addr_vals:
|
||||
return super(res_partner, self).write(cr, uid, ids, addr_vals, context)
|
||||
|
||||
|
@ -417,10 +431,10 @@ class res_partner(osv.osv, format_address):
|
|||
""" Supported syntax:
|
||||
- 'Raoul <raoul@grosbedon.fr>': will find name and email address
|
||||
- otherwise: default, everything is set as the name """
|
||||
match = re.search(r'([^\s,<@]+@[^>\s,]+)', text)
|
||||
if match:
|
||||
email = match.group(1)
|
||||
name = text[:text.index(email)].replace('"','').replace('<','').strip()
|
||||
emails = tools.email_split(text)
|
||||
if emails:
|
||||
email = emails[0]
|
||||
name = text[:text.index(email)].replace('"', '').replace('<', '').strip()
|
||||
else:
|
||||
name, email = text, ''
|
||||
return name, email
|
||||
|
@ -463,8 +477,7 @@ class res_partner(osv.osv, format_address):
|
|||
OR partner.name || ' (' || COALESCE(company.name,'') || ')'
|
||||
''' + operator + ' %(name)s ' + limit_str, query_args)
|
||||
ids = map(lambda x: x[0], cr.fetchall())
|
||||
if args:
|
||||
ids = self.search(cr, uid, [('id', 'in', ids)] + args, limit=limit, context=context)
|
||||
ids = self.search(cr, uid, [('id', 'in', ids)] + args, limit=limit, context=context)
|
||||
if ids:
|
||||
return self.name_get(cr, uid, ids, context)
|
||||
return super(res_partner,self).name_search(cr, uid, name, args, operator=operator, context=context, limit=limit)
|
||||
|
|
|
@ -87,7 +87,7 @@
|
|||
<field name="city">Wavre</field>
|
||||
<field name="zip">1300</field>
|
||||
<field name="country_id" ref="base.be"/>
|
||||
<field name="street">69 rue de Chimay</field>
|
||||
<field name="street">69 rue de Namur</field>
|
||||
<field name="email">info@agrolait.com</field>
|
||||
<field name="phone">+32 10 588 558</field>
|
||||
<field name="website">www.agrolait.com</field>
|
||||
|
|
|
@ -182,7 +182,7 @@
|
|||
</group>
|
||||
|
||||
<notebook colspan="4">
|
||||
<page string="Contacts" attrs="{'invisible': [('is_company','=',False)]}">
|
||||
<page string="Contacts" attrs="{'invisible': [('is_company','=',False)]}" autofocus="autofocus">
|
||||
<field name="child_ids" context="{'default_parent_id': active_id}" mode="kanban">
|
||||
<kanban>
|
||||
<field name="color"/>
|
||||
|
@ -304,18 +304,19 @@
|
|||
filter_domain="['|','|',('name','ilike',self),('parent_id','ilike',self),('ref','=',self)]"/>
|
||||
<filter help="My Partners" icon="terp-personal+" domain="[('user_id','=',uid)]"/>
|
||||
<separator/>
|
||||
<filter string="Persons" name="type_person" icon="terp-personal" domain="[('is_company','=',0)]"/>
|
||||
<filter string="Companies" name="type_company" icon="terp-partner" domain="[('is_company','=',1)]"/>
|
||||
<filter string="Persons" name="type_person" domain="[('is_company','=',0)]"/>
|
||||
<filter string="Companies" name="type_company" domain="[('is_company','=',1)]"/>
|
||||
<separator/>
|
||||
<filter string="Customers" name="customer" icon="terp-personal" domain="[('customer','=',1)]" help="Customer Partners"/>
|
||||
<filter string="Customers" name="customer" domain="[('customer','=',1)]" help="Customer Partners"/>
|
||||
<filter string="Suppliers" name="supplier" domain="[('supplier','=',1)]" help="Supplier Partners"/>
|
||||
<separator/>
|
||||
<filter string="Suppliers" name="supplier" icon="terp-personal" domain="[('supplier','=',1)]" help="Supplier Partners"/>
|
||||
<field name="category_id" string="Tag" filter_domain="[('category_id','ilike', self)]"/>
|
||||
<field name="user_id"/>
|
||||
<field name="parent_id" filter_domain="[('parent_id','child_of',[self])]"/>
|
||||
<field name="parent_id" domain="[('is_company','=',1)]" filter_domain="[('parent_id','child_of',[self])]"/>
|
||||
<group expand="0" string="Group By...">
|
||||
<filter string="Salesperson" icon="terp-personal" domain="[]" context="{'group_by' : 'user_id'}" />
|
||||
<filter string="Company" context="{'group_by': 'parent_id'}"/>
|
||||
<filter string="Country" context="{'group_by': 'country_id'}"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
|
||||
# Copyright (C) 2010-2012 OpenERP s.a. (<http://openerp.com>).
|
||||
# Copyright (C) 2010-2013 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
|
||||
|
@ -172,6 +172,10 @@ class res_users(osv.osv):
|
|||
}
|
||||
}
|
||||
|
||||
def onchange_state(self, cr, uid, ids, state_id, context=None):
|
||||
partner_ids = [user.partner_id.id for user in self.browse(cr, uid, ids, context=context)]
|
||||
return self.pool.get('res.partner').onchange_state(cr, uid, partner_ids, state_id, context=context)
|
||||
|
||||
def onchange_type(self, cr, uid, ids, is_company, context=None):
|
||||
""" Wrapper on the user.partner onchange_type, because some calls to the
|
||||
partner form view applied to the user may trigger the
|
||||
|
@ -426,7 +430,9 @@ class res_users(osv.osv):
|
|||
cr = pooler.get_db(db).cursor()
|
||||
try:
|
||||
base = user_agent_env['base_location']
|
||||
self.pool.get('ir.config_parameter').set_param(cr, uid, 'web.base.url', base)
|
||||
ICP = self.pool.get('ir.config_parameter')
|
||||
if not ICP.get_param(cr, uid, 'web.base.url.freeze'):
|
||||
ICP.set_param(cr, uid, 'web.base.url', base)
|
||||
cr.commit()
|
||||
except Exception:
|
||||
_logger.exception("Failed to update web.base.url configuration parameter")
|
||||
|
|
|
@ -56,7 +56,8 @@ openerp.base = function(instance) {
|
|||
});
|
||||
|
||||
};
|
||||
i.src = _.str.sprintf('%s/web/static/src/img/sep-a.gif', client.origin);
|
||||
var ts = new Date().getTime();
|
||||
i.src = _.str.sprintf('%s/web/static/src/img/sep-a.gif?%s', client.origin, ts);
|
||||
return d.promise();
|
||||
};
|
||||
if (instance.base.apps_client) {
|
||||
|
@ -96,7 +97,7 @@ openerp.base = function(instance) {
|
|||
client.replace(self.$el).
|
||||
done(function() {
|
||||
client.$el.removeClass('openerp');
|
||||
client.do_action(self.remote_action_id);
|
||||
client.do_action(self.remote_action_id, {hide_breadcrumb: true});
|
||||
});
|
||||
}).
|
||||
fail(function(client) {
|
||||
|
|
|
@ -114,6 +114,12 @@ class test_expression(common.TransactionCase):
|
|||
# Test2: inheritance + relational fields
|
||||
user_ids = users_obj.search(cr, uid, [('child_ids.name', 'like', 'test_B')])
|
||||
self.assertEqual(set(user_ids), set([b1]), 'searching through inheritance failed')
|
||||
|
||||
# Special =? operator mean "is equal if right is set, otherwise always True"
|
||||
user_ids = users_obj.search(cr, uid, [('name', 'like', 'test'), ('parent_id', '=?', False)])
|
||||
self.assertEqual(set(user_ids), set([a, b1, b2]), '(x =? False) failed')
|
||||
user_ids = users_obj.search(cr, uid, [('name', 'like', 'test'), ('parent_id', '=?', b1_user.partner_id.id)])
|
||||
self.assertEqual(set(user_ids), set([b2]), '(x =? id) failed')
|
||||
|
||||
def test_20_auto_join(self):
|
||||
registry, cr, uid = self.registry, self.cr, self.uid
|
||||
|
|
|
@ -218,15 +218,7 @@ def quit_on_signals():
|
|||
os.unlink(config['pidfile'])
|
||||
sys.exit(0)
|
||||
|
||||
def configure_babel_localedata_path():
|
||||
# Workaround: py2exe and babel.
|
||||
if hasattr(sys, 'frozen'):
|
||||
import babel
|
||||
babel.localedata._dirname = os.path.join(os.path.dirname(sys.executable), 'localedata')
|
||||
|
||||
def main(args):
|
||||
os.environ["TZ"] = "UTC"
|
||||
|
||||
check_root_user()
|
||||
openerp.tools.config.parse_config(args)
|
||||
|
||||
|
@ -236,8 +228,6 @@ def main(args):
|
|||
|
||||
config = openerp.tools.config
|
||||
|
||||
configure_babel_localedata_path()
|
||||
|
||||
setup_signal_handlers()
|
||||
|
||||
if config["test_file"]:
|
||||
|
|
|
@ -34,6 +34,7 @@ import openerp
|
|||
import openerp.modules.db
|
||||
import openerp.modules.graph
|
||||
import openerp.modules.migration
|
||||
import openerp.modules.registry
|
||||
import openerp.osv as osv
|
||||
import openerp.pooler as pooler
|
||||
import openerp.tools as tools
|
||||
|
@ -138,7 +139,7 @@ def load_module_graph(cr, graph, status=None, perform_checks=True, skip_modules=
|
|||
loaded_modules = []
|
||||
pool = pooler.get_pool(cr.dbname)
|
||||
migrations = openerp.modules.migration.MigrationManager(cr, graph)
|
||||
_logger.debug('loading %d packages...', len(graph))
|
||||
_logger.info('loading %d modules...', len(graph))
|
||||
|
||||
# Query manual fields for all models at once and save them on the registry
|
||||
# so the initialization code for each model does not have to do it
|
||||
|
@ -156,7 +157,7 @@ def load_module_graph(cr, graph, status=None, perform_checks=True, skip_modules=
|
|||
if skip_modules and module_name in skip_modules:
|
||||
continue
|
||||
|
||||
_logger.info('module %s: loading objects', package.name)
|
||||
_logger.debug('module %s: loading objects', package.name)
|
||||
migrations.migrate_module(package, 'pre')
|
||||
load_openerp_module(package.name)
|
||||
|
||||
|
|
|
@ -400,7 +400,7 @@ def load_openerp_module(module_name):
|
|||
initialize_sys_path()
|
||||
try:
|
||||
mod_path = get_module_path(module_name)
|
||||
zip_mod_path = mod_path + '.zip'
|
||||
zip_mod_path = '' if not mod_path else mod_path + '.zip'
|
||||
if not os.path.isfile(zip_mod_path):
|
||||
__import__('openerp.addons.' + module_name)
|
||||
else:
|
||||
|
|
|
@ -190,6 +190,10 @@ class RegistryManager(object):
|
|||
except KeyError:
|
||||
return cls.new(db_name, force_demo, status,
|
||||
update_module)
|
||||
finally:
|
||||
# set db tracker - cleaned up at the WSGI
|
||||
# dispatching phase in openerp.service.wsgi_server.application
|
||||
threading.current_thread().dbname = db_name
|
||||
|
||||
@classmethod
|
||||
def new(cls, db_name, force_demo=False, status=None,
|
||||
|
@ -216,6 +220,11 @@ class RegistryManager(object):
|
|||
del cls.registries[db_name]
|
||||
raise
|
||||
|
||||
# load_modules() above can replace the registry by calling
|
||||
# indirectly new() again (when modules have to be uninstalled).
|
||||
# Yeah, crazy.
|
||||
registry = cls.registries[db_name]
|
||||
|
||||
cr = registry.db.cursor()
|
||||
try:
|
||||
Registry.setup_multi_process_signaling(cr)
|
||||
|
@ -227,6 +236,9 @@ class RegistryManager(object):
|
|||
|
||||
registry.ready = True
|
||||
|
||||
if update_module:
|
||||
# only in case of update, otherwise we'll have an infinite reload loop!
|
||||
cls.signal_registry_change(db_name)
|
||||
return registry
|
||||
|
||||
@classmethod
|
||||
|
|
|
@ -186,7 +186,13 @@ def init_logger():
|
|||
# Normal Handler on standard output
|
||||
handler = logging.StreamHandler(sys.stdout)
|
||||
|
||||
if isinstance(handler, logging.StreamHandler) and os.isatty(handler.stream.fileno()):
|
||||
# Check that handler.stream has a fileno() method: when running OpenERP
|
||||
# behind Apache with mod_wsgi, handler.stream will have type mod_wsgi.Log,
|
||||
# which has no fileno() method. (mod_wsgi.Log is what is being bound to
|
||||
# sys.stderr when the logging.StreamHandler is being constructed above.)
|
||||
if isinstance(handler, logging.StreamHandler) \
|
||||
and hasattr(handler.stream, 'fileno') \
|
||||
and os.isatty(handler.stream.fileno()):
|
||||
formatter = ColoredFormatter(format)
|
||||
else:
|
||||
formatter = DBFormatter(format)
|
||||
|
@ -284,8 +290,6 @@ def dispatch_rpc(service_name, method, params):
|
|||
if rpc_request and rpc_response_flag:
|
||||
log(rpc_request,logging.DEBUG,'%s.%s'%(service_name,method), replace_request_password(params))
|
||||
|
||||
threading.current_thread().uid = None
|
||||
threading.current_thread().dbname = None
|
||||
result = ExportService.getService(service_name).dispatch(method, params)
|
||||
|
||||
if rpc_request_flag or rpc_response_flag:
|
||||
|
|
|
@ -199,7 +199,7 @@ def normalize_domain(domain):
|
|||
expected -= 1
|
||||
else:
|
||||
expected += op_arity.get(token, 0) - 1
|
||||
assert expected == 0
|
||||
assert expected == 0, 'This domain is syntactically not correct: %s' % (domain)
|
||||
return result
|
||||
|
||||
|
||||
|
@ -598,6 +598,15 @@ class ExtendedLeaf(object):
|
|||
self.leaf = normalize_leaf(self.leaf)
|
||||
return True
|
||||
|
||||
def create_substitution_leaf(leaf, new_elements, new_model=None):
|
||||
""" From a leaf, create a new leaf (based on the new_elements tuple
|
||||
and new_model), that will have the same join context. Used to
|
||||
insert equivalent leafs in the processing stack. """
|
||||
if new_model is None:
|
||||
new_model = leaf.model
|
||||
new_join_context = [tuple(context) for context in leaf.join_context]
|
||||
new_leaf = ExtendedLeaf(new_elements, new_model, join_context=new_join_context)
|
||||
return new_leaf
|
||||
|
||||
class expression(object):
|
||||
""" Parse a domain expression
|
||||
|
@ -715,16 +724,6 @@ class expression(object):
|
|||
return ids + recursive_children(ids2, model, parent_field)
|
||||
return [(left, 'in', recursive_children(ids, left_model, parent or left_model._parent_name))]
|
||||
|
||||
def create_substitution_leaf(leaf, new_elements, new_model=None):
|
||||
""" From a leaf, create a new leaf (based on the new_elements tuple
|
||||
and new_model), that will have the same join context. Used to
|
||||
insert equivalent leafs in the processing stack. """
|
||||
if new_model is None:
|
||||
new_model = leaf.model
|
||||
new_join_context = [tuple(context) for context in leaf.join_context]
|
||||
new_leaf = ExtendedLeaf(new_elements, new_model, join_context=new_join_context)
|
||||
return new_leaf
|
||||
|
||||
def pop():
|
||||
""" Pop a leaf to process. """
|
||||
return self.stack.pop()
|
||||
|
@ -1153,7 +1152,8 @@ class expression(object):
|
|||
params = []
|
||||
else:
|
||||
# '=?' behaves like '=' in other cases
|
||||
query, params = self.__leaf_to_sql((left, '=', right), model)
|
||||
query, params = self.__leaf_to_sql(
|
||||
create_substitution_leaf(eleaf, (left, '=', right), model))
|
||||
|
||||
elif left == 'id':
|
||||
query = '%s.id %s %%s' % (table_alias, operator)
|
||||
|
|
|
@ -1029,7 +1029,7 @@ class BaseModel(object):
|
|||
'required': bool(field['required']),
|
||||
'readonly': bool(field['readonly']),
|
||||
'domain': eval(field['domain']) if field['domain'] else None,
|
||||
'size': field['size'],
|
||||
'size': field['size'] or None,
|
||||
'ondelete': field['on_delete'],
|
||||
'translate': (field['translate']),
|
||||
'manual': True,
|
||||
|
@ -1077,7 +1077,7 @@ class BaseModel(object):
|
|||
|
||||
# Validate rec_name
|
||||
if self._rec_name is not None:
|
||||
assert self._rec_name in self._columns.keys() + ['id'], "Invalid rec_name %s for model %s" % (self._rec_name, self._name)
|
||||
assert self._rec_name in self._all_columns.keys() + ['id'], "Invalid rec_name %s for model %s" % (self._rec_name, self._name)
|
||||
else:
|
||||
self._rec_name = 'name'
|
||||
|
||||
|
@ -1362,11 +1362,9 @@ class BaseModel(object):
|
|||
noupdate=noupdate, res_id=id, context=context))
|
||||
cr.execute('RELEASE SAVEPOINT model_load_save')
|
||||
except psycopg2.Warning, e:
|
||||
_logger.exception('Failed to import record %s', record)
|
||||
messages.append(dict(info, type='warning', message=str(e)))
|
||||
cr.execute('ROLLBACK TO SAVEPOINT model_load_save')
|
||||
except psycopg2.Error, e:
|
||||
_logger.exception('Failed to import record %s', record)
|
||||
messages.append(dict(
|
||||
info, type='error',
|
||||
**PGERROR_TO_OE[e.pgcode](self, fg, info, e)))
|
||||
|
@ -4434,7 +4432,6 @@ class BaseModel(object):
|
|||
upd1 += ",%s,(now() at time zone 'UTC'),%s,(now() at time zone 'UTC')"
|
||||
upd2.extend((user, user))
|
||||
cr.execute('insert into "'+self._table+'" (id'+upd0+") values ("+str(id_new)+upd1+')', tuple(upd2))
|
||||
self.check_access_rule(cr, user, [id_new], 'create', context=context)
|
||||
upd_todo.sort(lambda x, y: self._columns[x].priority-self._columns[y].priority)
|
||||
|
||||
if self._parent_store and not context.get('defer_parent_store_computation'):
|
||||
|
@ -4487,6 +4484,7 @@ class BaseModel(object):
|
|||
self.name_get(cr, user, [id_new], context=context)[0][1] + \
|
||||
"' " + _("created.")
|
||||
self.log(cr, user, id_new, message, True, context=context)
|
||||
self.check_access_rule(cr, user, [id_new], 'create', context=context)
|
||||
self._workflow_trigger(cr, user, [id_new], 'trg_create', context=context)
|
||||
return id_new
|
||||
|
||||
|
@ -5327,11 +5325,28 @@ def convert_pgerror_23502(model, fields, info, e):
|
|||
'message': message,
|
||||
'field': field_name,
|
||||
}
|
||||
def convert_pgerror_23505(model, fields, info, e):
|
||||
m = re.match(r'^duplicate key (?P<field>\w+) violates unique constraint',
|
||||
str(e))
|
||||
field_name = m.group('field')
|
||||
if not m or field_name not in fields:
|
||||
return {'message': unicode(e)}
|
||||
message = _(u"The value for the field '%s' already exists.") % field_name
|
||||
field = fields.get(field_name)
|
||||
if field:
|
||||
message = _(u"%s This might be '%s' in the current model, or a field "
|
||||
u"of the same name in an o2m.") % (message, field['string'])
|
||||
return {
|
||||
'message': message,
|
||||
'field': field_name,
|
||||
}
|
||||
|
||||
PGERROR_TO_OE = collections.defaultdict(
|
||||
# shape of mapped converters
|
||||
lambda: (lambda model, fvg, info, pgerror: {'message': unicode(pgerror)}), {
|
||||
# not_null_violation
|
||||
'23502': convert_pgerror_23502,
|
||||
# unique constraint error
|
||||
'23505': convert_pgerror_23505,
|
||||
})
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
|
|
@ -25,7 +25,7 @@ from functools import wraps
|
|||
import logging
|
||||
import threading
|
||||
|
||||
from psycopg2 import IntegrityError, errorcodes
|
||||
from psycopg2 import IntegrityError, OperationalError, errorcodes
|
||||
|
||||
import orm
|
||||
import openerp
|
||||
|
@ -36,8 +36,14 @@ from openerp.tools.translate import translate
|
|||
from openerp.osv.orm import MetaModel, Model, TransientModel, AbstractModel
|
||||
import openerp.exceptions
|
||||
|
||||
import time
|
||||
import random
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
PG_CONCURRENCY_ERRORS_TO_RETRY = (errorcodes.LOCK_NOT_AVAILABLE, errorcodes.SERIALIZATION_FAILURE, errorcodes.DEADLOCK_DETECTED)
|
||||
MAX_TRIES_ON_CONCURRENCY_FAILURE = 5
|
||||
|
||||
# Deprecated.
|
||||
class except_osv(Exception):
|
||||
def __init__(self, name, value):
|
||||
|
@ -117,45 +123,58 @@ class object_proxy(object):
|
|||
def _(src):
|
||||
return tr(src, 'code')
|
||||
|
||||
try:
|
||||
if pooler.get_pool(dbname)._init:
|
||||
raise except_osv('Database not ready', 'Currently, this database is not fully loaded and can not be used.')
|
||||
return f(self, dbname, *args, **kwargs)
|
||||
except orm.except_orm, inst:
|
||||
raise except_osv(inst.name, inst.value)
|
||||
except except_osv:
|
||||
raise
|
||||
except IntegrityError, inst:
|
||||
osv_pool = pooler.get_pool(dbname)
|
||||
for key in osv_pool._sql_error.keys():
|
||||
if key in inst[0]:
|
||||
netsvc.abort_response(1, _('Constraint Error'), 'warning',
|
||||
tr(osv_pool._sql_error[key], 'sql_constraint') or inst[0])
|
||||
if inst.pgcode in (errorcodes.NOT_NULL_VIOLATION, errorcodes.FOREIGN_KEY_VIOLATION, errorcodes.RESTRICT_VIOLATION):
|
||||
msg = _('The operation cannot be completed, probably due to the following:\n- deletion: you may be trying to delete a record while other records still reference it\n- creation/update: a mandatory field is not correctly set')
|
||||
_logger.debug("IntegrityError", exc_info=True)
|
||||
try:
|
||||
errortxt = inst.pgerror.replace('«','"').replace('»','"')
|
||||
if '"public".' in errortxt:
|
||||
context = errortxt.split('"public".')[1]
|
||||
model_name = table = context.split('"')[1]
|
||||
else:
|
||||
last_quote_end = errortxt.rfind('"')
|
||||
last_quote_begin = errortxt.rfind('"', 0, last_quote_end)
|
||||
model_name = table = errortxt[last_quote_begin+1:last_quote_end].strip()
|
||||
model = table.replace("_",".")
|
||||
model_obj = osv_pool.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
|
||||
netsvc.abort_response(1, _('Integrity Error'), 'warning', msg)
|
||||
else:
|
||||
netsvc.abort_response(1, _('Integrity Error'), 'warning', inst[0])
|
||||
except Exception:
|
||||
_logger.exception("Uncaught exception")
|
||||
raise
|
||||
tries = 0
|
||||
while True:
|
||||
try:
|
||||
if pooler.get_pool(dbname)._init:
|
||||
raise except_osv('Database not ready', 'Currently, this database is not fully loaded and can not be used.')
|
||||
return f(self, dbname, *args, **kwargs)
|
||||
except OperationalError, e:
|
||||
# Automatically retry the typical transaction serialization errors
|
||||
if e.pgcode not in PG_CONCURRENCY_ERRORS_TO_RETRY:
|
||||
raise
|
||||
if tries >= MAX_TRIES_ON_CONCURRENCY_FAILURE:
|
||||
_logger.warning("%s, maximum number of tries reached" % errorcodes.lookup(e.pgcode))
|
||||
raise
|
||||
wait_time = random.uniform(0.0, 2 ** tries)
|
||||
tries += 1
|
||||
_logger.info("%s, retry %d/%d in %.04f sec..." % (errorcodes.lookup(e.pgcode), tries, MAX_TRIES_ON_CONCURRENCY_FAILURE, wait_time))
|
||||
time.sleep(wait_time)
|
||||
except orm.except_orm, inst:
|
||||
raise except_osv(inst.name, inst.value)
|
||||
except except_osv:
|
||||
raise
|
||||
except IntegrityError, inst:
|
||||
osv_pool = pooler.get_pool(dbname)
|
||||
for key in osv_pool._sql_error.keys():
|
||||
if key in inst[0]:
|
||||
netsvc.abort_response(1, _('Constraint Error'), 'warning',
|
||||
tr(osv_pool._sql_error[key], 'sql_constraint') or inst[0])
|
||||
if inst.pgcode in (errorcodes.NOT_NULL_VIOLATION, errorcodes.FOREIGN_KEY_VIOLATION, errorcodes.RESTRICT_VIOLATION):
|
||||
msg = _('The operation cannot be completed, probably due to the following:\n- deletion: you may be trying to delete a record while other records still reference it\n- creation/update: a mandatory field is not correctly set')
|
||||
_logger.debug("IntegrityError", exc_info=True)
|
||||
try:
|
||||
errortxt = inst.pgerror.replace('«','"').replace('»','"')
|
||||
if '"public".' in errortxt:
|
||||
context = errortxt.split('"public".')[1]
|
||||
model_name = table = context.split('"')[1]
|
||||
else:
|
||||
last_quote_end = errortxt.rfind('"')
|
||||
last_quote_begin = errortxt.rfind('"', 0, last_quote_end)
|
||||
model_name = table = errortxt[last_quote_begin+1:last_quote_end].strip()
|
||||
model = table.replace("_",".")
|
||||
model_obj = osv_pool.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
|
||||
netsvc.abort_response(1, _('Integrity Error'), 'warning', msg)
|
||||
else:
|
||||
netsvc.abort_response(1, _('Integrity Error'), 'warning', inst[0])
|
||||
except Exception:
|
||||
_logger.exception("Uncaught exception")
|
||||
raise
|
||||
|
||||
return wrapper
|
||||
|
||||
|
@ -170,7 +189,6 @@ class object_proxy(object):
|
|||
|
||||
@check
|
||||
def execute(self, db, uid, obj, method, *args, **kw):
|
||||
threading.currentThread().dbname = db
|
||||
cr = pooler.get_db(db).cursor()
|
||||
try:
|
||||
try:
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue