merge upstream

bzr revid: chs@openerp.com-20130411154519-xvepmjrm4ibqddx7
This commit is contained in:
Christophe Simonis 2013-04-11 17:45:19 +02:00
commit 841cf5aaad
117 changed files with 33134 additions and 16416 deletions

3
debian/control vendored
View File

@ -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

69
debian/openerp.init vendored
View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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):

View File

@ -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>

View File

@ -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):

View File

@ -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')]}"/>

View File

@ -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"

View File

@ -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):

View File

@ -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"/>

View File

@ -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':

View File

@ -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

View File

@ -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)

View File

@ -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"

View File

@ -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)

View File

@ -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>

View File

@ -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>

View File

@ -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")

View File

@ -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) {

View File

@ -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

View File

@ -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"]:

View 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)

View File

@ -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:

View File

@ -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

View File

@ -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:

View File

@ -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)

View File

@ -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:

View File

@ -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