diff --git a/addons/account/account_invoice_view.xml b/addons/account/account_invoice_view.xml index 266112d7cb8..4eca707b863 100644 --- a/addons/account/account_invoice_view.xml +++ b/addons/account/account_invoice_view.xml @@ -399,7 +399,7 @@ - + diff --git a/addons/account/account_view.xml b/addons/account/account_view.xml index 51e005a45fb..e03285dacd3 100644 --- a/addons/account/account_view.xml +++ b/addons/account/account_view.xml @@ -885,6 +885,7 @@ 100 + @@ -904,6 +905,7 @@ + @@ -2154,6 +2156,7 @@ child_ids + @@ -2183,6 +2186,7 @@ + diff --git a/addons/account_analytic_analysis/account_analytic_analysis.py b/addons/account_analytic_analysis/account_analytic_analysis.py index 8d8be98e38f..313dd54f4db 100644 --- a/addons/account_analytic_analysis/account_analytic_analysis.py +++ b/addons/account_analytic_analysis/account_analytic_analysis.py @@ -375,7 +375,7 @@ class account_analytic_account(osv.osv): inv_ids = [] for account in self.browse(cr, uid, ids, context=context): res[account.id] = 0.0 - line_ids = lines_obj.search(cr, uid, [('account_id','=', account.id), ('invoice_id','!=',False), ('to_invoice','!=', False), ('journal_id.type', '=', 'general'), ('invoice_id.type', 'in', ['out_invoice', 'out_refund'])], context=context) + line_ids = lines_obj.search(cr, uid, [('account_id','=', account.id), ('invoice_id','!=',False), ('invoice_id.state', 'not in', ['draft', 'cancel']), ('to_invoice','!=', False), ('journal_id.type', '=', 'general'), ('invoice_id.type', 'in', ['out_invoice', 'out_refund'])], context=context) for line in lines_obj.browse(cr, uid, line_ids, context=context): if line.invoice_id not in inv_ids: inv_ids.append(line.invoice_id) diff --git a/addons/auth_oauth/auth_oauth_data.xml b/addons/auth_oauth/auth_oauth_data.xml index fe64f42b2f6..b389386f1c1 100644 --- a/addons/auth_oauth/auth_oauth_data.xml +++ b/addons/auth_oauth/auth_oauth_data.xml @@ -14,8 +14,8 @@ Facebook Graph https://www.facebook.com/dialog/oauth - - https://graph.facebook.com/me/permissions + public_profile,email + https://graph.facebook.com/me fa fa-facebook-square Log in with facebook diff --git a/addons/auth_oauth/res_users.py b/addons/auth_oauth/res_users.py index f2c6be389f1..cada7d51a26 100644 --- a/addons/auth_oauth/res_users.py +++ b/addons/auth_oauth/res_users.py @@ -101,7 +101,12 @@ class res_users(osv.Model): validation = self._auth_oauth_validate(cr, uid, provider, access_token) # required check if not validation.get('user_id'): - raise openerp.exceptions.AccessDenied() + # Workaround: facebook does not send 'user_id' in Open Graph Api + if validation.get('id'): + validation['user_id'] = validation['id'] + else: + raise openerp.exceptions.AccessDenied() + # retrieve and sign in user login = self._auth_oauth_signin(cr, uid, provider, validation, params, context=context) if not login: diff --git a/addons/auth_openid/res_users.py b/addons/auth_openid/res_users.py index 35f70438d80..f8367073f62 100644 --- a/addons/auth_openid/res_users.py +++ b/addons/auth_openid/res_users.py @@ -63,19 +63,15 @@ class res_users(osv.osv): cr.commit() return res[0] if res else False - def check(self, db, uid, passwd): + def check_credentials(self, cr, uid, password): try: - return super(res_users, self).check(db, uid, passwd) + return super(res_users, self).check_credentials(cr, uid, password) except openerp.exceptions.AccessDenied: - if not passwd: + cr.execute('''SELECT COUNT(1) + FROM res_users + WHERE id=%s + AND openid_key=%s + AND active=%s''', + (int(uid), password, True)) + if not cr.fetchone()[0]: raise - with RegistryManager.get(db).cursor() as cr: - cr.execute('''SELECT COUNT(1) - FROM res_users - WHERE id=%s - AND openid_key=%s - AND active=%s''', - (int(uid), passwd, True)) - if not cr.fetchone()[0]: - raise - self._uid_cache.setdefault(db, {})[uid] = passwd diff --git a/addons/hr_holidays/hr_holidays_view.xml b/addons/hr_holidays/hr_holidays_view.xml index fdbce2b0310..6ee9c2cd931 100644 --- a/addons/hr_holidays/hr_holidays_view.xml +++ b/addons/hr_holidays/hr_holidays_view.xml @@ -1,3 +1,4 @@ + @@ -25,17 +26,27 @@ + + - - + + + + + diff --git a/addons/hr_timesheet_invoice/hr_timesheet_invoice.py b/addons/hr_timesheet_invoice/hr_timesheet_invoice.py index d475a10f6a6..a1dffb15a00 100644 --- a/addons/hr_timesheet_invoice/hr_timesheet_invoice.py +++ b/addons/hr_timesheet_invoice/hr_timesheet_invoice.py @@ -255,6 +255,7 @@ class account_analytic_line(osv.osv): def invoice_cost_create(self, cr, uid, ids, data=None, context=None): invoice_obj = self.pool.get('account.invoice') invoice_line_obj = self.pool.get('account.invoice.line') + analytic_line_obj = self.pool.get('account.analytic.line') invoices = [] if context is None: context = {} @@ -303,13 +304,15 @@ class account_analytic_line(osv.osv): analytic_line.to_invoice.id, analytic_line.account_id, analytic_line.journal_id.type) + # We want to retrieve the data in the partner language for the invoice creation + analytic_line = analytic_line_obj.browse(cr, uid , [line.id for line in analytic_line], context=invoice_context) invoice_lines_grouping.setdefault(key, []).append(analytic_line) # finally creates the invoice line for (product_id, uom, user_id, factor_id, account, journal_type), lines_to_invoice in invoice_lines_grouping.items(): curr_invoice_line = self._prepare_cost_invoice_line(cr, uid, last_invoice, product_id, uom, user_id, factor_id, account, lines_to_invoice, - journal_type, data, context=context) + journal_type, data, context=invoice_context) invoice_line_obj.create(cr, uid, curr_invoice_line, context=context) self.write(cr, uid, [l.id for l in analytic_lines], {'invoice_id': last_invoice}, context=context) diff --git a/addons/hw_escpos/controllers/main.py b/addons/hw_escpos/controllers/main.py index c57238bd659..083e5cd5557 100644 --- a/addons/hw_escpos/controllers/main.py +++ b/addons/hw_escpos/controllers/main.py @@ -40,6 +40,10 @@ from openerp.tools.translate import _ _logger = logging.getLogger(__name__) +# workaround https://bugs.launchpad.net/openobject-server/+bug/947231 +# related to http://bugs.python.org/issue7980 +from datetime import datetime +datetime.strptime('2012-01-01', '%Y-%m-%d') class EscposDriver(Thread): def __init__(self): @@ -146,7 +150,7 @@ class EscposDriver(Thread): _logger.warning('ESC/POS Device Disconnected: '+message) def run(self): - + printer = None if not escpos: _logger.error('ESC/POS cannot initialize, please verify system dependencies.') return @@ -192,7 +196,7 @@ class EscposDriver(Thread): errmsg = str(e) + '\n' + '-'*60+'\n' + traceback.format_exc() + '-'*60 + '\n' _logger.error(errmsg); finally: - if error: + if error: self.queue.put((timestamp, task, data)) if printer: printer.close() diff --git a/addons/hw_posbox_homepage/controllers/main.py b/addons/hw_posbox_homepage/controllers/main.py index 25e33c409fa..a021986bee7 100644 --- a/addons/hw_posbox_homepage/controllers/main.py +++ b/addons/hw_posbox_homepage/controllers/main.py @@ -36,16 +36,16 @@ index_template = """

For more information on how to setup the Point of Sale with - the PosBox, please refer to the manual + the PosBox, please refer to the manual.

To see the status of the connected hardware, please refer - to the hardware status page + to the hardware status page.

- The PosBox software installed on this posbox is version 6, + The PosBox software installed on this posbox is version 13, the posbox version number is independent from Odoo. You can upgrade - the software on the upgrade page + the software on the upgrade page.

For any other question, please contact the Odoo support at support@odoo.com

diff --git a/addons/hw_posbox_upgrade/controllers/main.py b/addons/hw_posbox_upgrade/controllers/main.py index 0947fbc8564..5ed958d020e 100644 --- a/addons/hw_posbox_upgrade/controllers/main.py +++ b/addons/hw_posbox_upgrade/controllers/main.py @@ -29,17 +29,8 @@ upgrade_template = """ $.ajax({ url:'/hw_proxy/perform_upgrade/' }).then(function(status){ - $('#upgrade').html('Upgrade Successful
Click to Restart the PosBox'); + $('#upgrade').html('Upgrade successful, restarting the posbox...'); $('#upgrade').off('click'); - $('#upgrade').click(function(){ - $.ajax({ url:'/hw_proxy/perform_restart' }) - $('#upgrade').text('Restarting'); - $('#upgrade').off('click'); - setTimeout(function(){ - window.location = '/' - },30*1000); - }); - },function(){ $('#upgrade').text('Upgrade Failed'); }); @@ -73,9 +64,11 @@ upgrade_template = """

PosBox Software Upgrade

- This tool will help you perform an upgrade of the PosBox's software. + This tool will help you perform an upgrade of the PosBox's software over the + internet. +

However the preferred method to upgrade the posbox is to flash the sd-card with - the latest image. The upgrade + the latest image. The upgrade procedure is explained into to the PosBox manual

@@ -93,7 +86,6 @@ class PosboxUpgrader(hw_proxy.Proxy): def __init__(self): super(PosboxUpgrader,self).__init__() self.upgrading = threading.Lock() - self.last_upgrade = 0 @http.route('/hw_proxy/upgrade', type='http', auth='none', ) def upgrade(self): @@ -102,25 +94,8 @@ class PosboxUpgrader(hw_proxy.Proxy): @http.route('/hw_proxy/perform_upgrade', type='http', auth='none') def perform_upgrade(self): self.upgrading.acquire() - if time.time() - self.last_upgrade < 30: - self.upgrading.release() - return 'UPTODATE' - else: - os.system('/bin/bash /home/pi/openerp/update.sh') - self.last_upgrade = time.time() - self.upgrading.release() - return 'SUCCESS' - - @http.route('/hw_proxy/perform_restart', type='http', auth='none') - def perform_restart(self): - self.upgrading.acquire() - if time.time() - self.last_upgrade < 30: - self.upgrading.release() - return 'RESTARTED' - else: - os.system('/bin/bash /home/pi/openerp/restart.sh') - self.last_upgrade = time.time() - self.upgrading.release() - return 'SUCCESS' + os.system('/home/pi/posbox_update.sh') + self.upgrading.release() + return 'SUCCESS' diff --git a/addons/hw_proxy/controllers/main.py b/addons/hw_proxy/controllers/main.py index 337874cf67a..eb95527efcd 100644 --- a/addons/hw_proxy/controllers/main.py +++ b/addons/hw_proxy/controllers/main.py @@ -17,6 +17,14 @@ _logger = logging.getLogger(__name__) from openerp import http from openerp.http import request +# Those are the builtin raspberry pi USB modules, they should +# not appear in the list of connected devices. +BANNED_DEVICES = set([ + "0424:9514", # Standard Microsystem Corp. Builtin Ethernet module + "1d6b:0002", # Linux Foundation 2.0 root hub + "0424:ec00", # Standard Microsystem Corp. Other Builtin Ethernet module +]) + # drivers modules must add to drivers an object with a get_status() method # so that 'status' can return the status of all active drivers @@ -88,10 +96,18 @@ class Proxy(http.Controller):

The list of connected USB devices as seen by the posbox

""" devices = commands.getoutput("lsusb").split('\n') + count = 0 resp += "
\n" for device in devices: device_name = device[device.find('ID')+2:] - resp+= "
"+device_name+"
\n" + device_id = device_name.split()[0] + if not (device_id in BANNED_DEVICES): + resp+= "
"+device_name+"
\n" + count += 1 + + if count == 0: + resp += "
No USB Device Found
" + resp += "
\n" resp += """

Add New Printer

diff --git a/addons/hw_scale/controllers/main.py b/addons/hw_scale/controllers/main.py index 44a031f5566..2f9e0271dd5 100644 --- a/addons/hw_scale/controllers/main.py +++ b/addons/hw_scale/controllers/main.py @@ -44,6 +44,11 @@ class Scale(Thread): if status == self.status['status']: if message != None and message != self.status['messages'][-1]: self.status['messages'].append(message) + + if status == 'error' and message: + _logger.error('Scale Error: '+message) + elif status == 'disconnected' and message: + _logger.warning('Disconnected Scale: '+message) else: self.status['status'] = status if message: @@ -51,13 +56,16 @@ class Scale(Thread): else: self.status['messages'] = [] - if status == 'error' and message: - _logger.error('Scale Error: '+message) - elif status == 'disconnected' and message: - _logger.info('Disconnected Scale: %s', message) + if status == 'error' and message: + _logger.error('Scale Error: '+message) + elif status == 'disconnected' and message: + _logger.info('Disconnected Scale: %s', message) def get_device(self): try: + if not os.path.exists(self.input_dir): + self.set_status('disconnected','Scale Not Found') + return None devices = [ device for device in listdir(self.input_dir)] scales = [ device for device in devices if ('mettler' in device.lower()) or ('toledo' in device.lower()) ] if len(scales) > 0: @@ -69,8 +77,8 @@ class Scale(Thread): stopbits = serial.STOPBITS_ONE, parity = serial.PARITY_EVEN, #xonxoff = serial.XON, - timeout = 0.01, - writeTimeout= 0.01) + timeout = 0.02, + writeTimeout= 0.02) else: self.set_status('disconnected','Scale Not Found') return None @@ -95,7 +103,7 @@ class Scale(Thread): if self.device: try: self.device.write('W') - time.sleep(0.1) + time.sleep(0.2) answer = [] while True: @@ -171,7 +179,7 @@ class Scale(Thread): while True: if self.device: self.read_weight() - time.sleep(0.05) + time.sleep(0.15) else: with self.scalelock: self.device = self.get_device() diff --git a/addons/hw_scanner/controllers/main.py b/addons/hw_scanner/controllers/main.py index 6e53c37006f..8e5e8f6af18 100644 --- a/addons/hw_scanner/controllers/main.py +++ b/addons/hw_scanner/controllers/main.py @@ -111,6 +111,8 @@ class Scanner(Thread): def get_device(self): try: + if not evdev: + return None devices = [ device for device in listdir(self.input_dir)] keyboards = [ device for device in devices if ('kbd' in device) and ('keyboard' not in device.lower())] scanners = [ device for device in devices if ('barcode' in device.lower()) or ('scanner' in device.lower())] @@ -133,6 +135,7 @@ class Scanner(Thread): been returned before. This is necessary to catch barcodes scanned while the POS is busy reading another barcode """ + self.lockedstart() while True: @@ -164,10 +167,13 @@ class Scanner(Thread): try: device.ungrab() except Exception as e: + device = None self.set_status('error',str(e)) else: time.sleep(5) # wait until a suitable device is plugged device = self.get_device() + if not device: + continue try: device.grab() diff --git a/addons/mass_mailing/models/mail_mail.py b/addons/mass_mailing/models/mail_mail.py index 7a9128189e6..94cb8b0794b 100644 --- a/addons/mass_mailing/models/mail_mail.py +++ b/addons/mass_mailing/models/mail_mail.py @@ -19,8 +19,8 @@ # ############################################################################## -import urllib import urlparse +import werkzeug.urls from openerp import tools from openerp import SUPERUSER_ID @@ -55,7 +55,7 @@ class MailMail(osv.Model): track_url = urlparse.urljoin( base_url, 'mail/track/%(mail_id)s/blank.gif?%(params)s' % { 'mail_id': mail.id, - 'params': urllib.urlencode({'db': cr.dbname}) + 'params': werkzeug.url_encode({'db': cr.dbname}) } ) return '' % track_url @@ -65,7 +65,7 @@ class MailMail(osv.Model): url = urlparse.urljoin( base_url, 'mail/mailing/%(mailing_id)s/unsubscribe?%(params)s' % { 'mailing_id': mail.mailing_id.id, - 'params': urllib.urlencode({'db': cr.dbname, 'res_id': mail.res_id, 'email': email_to}) + 'params': werkzeug.url_encode({'db': cr.dbname, 'res_id': mail.res_id, 'email': email_to}) } ) return url diff --git a/addons/point_of_sale/tools/posbox/.gitignore b/addons/point_of_sale/tools/posbox/.gitignore new file mode 100644 index 00000000000..b26eafa34d1 --- /dev/null +++ b/addons/point_of_sale/tools/posbox/.gitignore @@ -0,0 +1,3 @@ +kernel-qemu +posbox.img +raspbian.img \ No newline at end of file diff --git a/addons/point_of_sale/tools/posbox/overwrite_after_init/etc/fstab b/addons/point_of_sale/tools/posbox/overwrite_after_init/etc/fstab new file mode 100644 index 00000000000..28898fc8ea4 --- /dev/null +++ b/addons/point_of_sale/tools/posbox/overwrite_after_init/etc/fstab @@ -0,0 +1,4 @@ +proc /proc proc defaults 0 0 +/dev/mmcblk0p1 /boot vfat defaults 0 2 +/dev/mmcblk0p2 / ext4 defaults,noatime,ro 0 1 +# a swapfile is not a swap partition, so no using swapon|off from here on, use dphys-swapfile swap[on|off] for that diff --git a/addons/point_of_sale/tools/posbox/overwrite_after_init/etc/init.d/rcS b/addons/point_of_sale/tools/posbox/overwrite_after_init/etc/init.d/rcS new file mode 100755 index 00000000000..6380e949834 --- /dev/null +++ b/addons/point_of_sale/tools/posbox/overwrite_after_init/etc/init.d/rcS @@ -0,0 +1,10 @@ +#! /bin/sh +# +# rcS +# +# Call all S??* scripts in /etc/rcS.d/ in numerical/alphabetical order +# + +/etc/setup_ramdisks.sh + +exec /etc/init.d/rc S diff --git a/addons/point_of_sale/tools/posbox/overwrite_after_init/etc/network/interfaces b/addons/point_of_sale/tools/posbox/overwrite_after_init/etc/network/interfaces new file mode 100644 index 00000000000..95b6f07524a --- /dev/null +++ b/addons/point_of_sale/tools/posbox/overwrite_after_init/etc/network/interfaces @@ -0,0 +1,3 @@ +# needed for postgresql +auto lo +iface lo inet loopback \ No newline at end of file diff --git a/addons/point_of_sale/tools/posbox/overwrite_after_init/etc/rc.local b/addons/point_of_sale/tools/posbox/overwrite_after_init/etc/rc.local new file mode 100755 index 00000000000..065b6a05f77 --- /dev/null +++ b/addons/point_of_sale/tools/posbox/overwrite_after_init/etc/rc.local @@ -0,0 +1,25 @@ +#!/bin/sh -e +# +# rc.local +# +# This script is executed at the end of each multiuser runlevel. +# Make sure that the script will "exit 0" on success or any other +# value on error. +# +# In order to enable or disable this script just change the execution +# bits. +# +# By default this script does nothing. + +# Print the IP address +_IP=$(hostname -I) || true +if [ "$_IP" ]; then + printf "My IP address is %s\n" "$_IP" +fi + +mkdir -p /var/run/odoo +chown pi:pi /var/run/odoo + +/home/pi/led_status.sh & + +exit 0 diff --git a/addons/point_of_sale/tools/posbox/overwrite_after_init/etc/setup_ramdisks.sh b/addons/point_of_sale/tools/posbox/overwrite_after_init/etc/setup_ramdisks.sh new file mode 100755 index 00000000000..47e11d1bdd3 --- /dev/null +++ b/addons/point_of_sale/tools/posbox/overwrite_after_init/etc/setup_ramdisks.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +set -o errexit +set -o nounset +set -o pipefail +# set -o xtrace + +create_ramdisk () { + ORIGINAL="${1}" + RAMDISK="${ORIGINAL}_ram" + SIZE="${2}" + echo "Creating ramdisk for ${1} of size ${SIZE}..." + + mount -t tmpfs -o size="${SIZE}" tmpfs "${RAMDISK}" + rsync -a --exclude="swap" --exclude="apt" --exclude="dpkg" "${ORIGINAL}/" "${RAMDISK}/" + mount --bind "${RAMDISK}" "${ORIGINAL}" +} + +# check /proc/cmdline + +# bind mount / so that we can get to the real /var and /etc +mount --bind / /root_bypass_ramdisks + +echo "Creating ramdisks..." +create_ramdisk "/var" "128M" +create_ramdisk "/etc" "16M" diff --git a/addons/point_of_sale/tools/posbox/overwrite_before_init/etc/init.d/odoo b/addons/point_of_sale/tools/posbox/overwrite_before_init/etc/init.d/odoo new file mode 100755 index 00000000000..9bdaa86c584 --- /dev/null +++ b/addons/point_of_sale/tools/posbox/overwrite_before_init/etc/init.d/odoo @@ -0,0 +1,72 @@ +#!/bin/bash +### BEGIN INIT INFO +# Provides: odoo.py +# Required-Start: $remote_fs $syslog postgresql +# Required-Stop: $remote_fs $syslog +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: Start odoo daemon at boot time +# Description: Enable service provided by daemon. +# X-Interactive: true +### END INIT INFO +## more info: http://wiki.debian.org/LSBInitScripts + +. /lib/lsb/init-functions + +PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/bin +DAEMON=/home/pi/odoo/odoo.py +NAME=odoo +DESC=odoo +CONFIG=/home/pi/odoo.conf +LOGFILE=/var/log/odoo/odoo-server.log +PIDFILE=/var/run/${NAME}.pid +USER=pi + +test -x $DAEMON || exit 0 +set -e + +function _start() { + start-stop-daemon --start --quiet --pidfile $PIDFILE --chuid $USER:$USER --background --make-pidfile --exec $DAEMON -- --config $CONFIG --logfile $LOGFILE --load=web,hw_proxy,hw_posbox_homepage,hw_posbox_upgrade,hw_scale,hw_scanner,hw_escpos +} + +function _stop() { + start-stop-daemon --stop --quiet --pidfile $PIDFILE --oknodo --retry 3 + rm -f $PIDFILE +} + +function _status() { + start-stop-daemon --status --quiet --pidfile $PIDFILE + return $? +} + + +case "$1" in + start) + echo -n "Starting $DESC: " + _start + echo "ok" + ;; + stop) + echo -n "Stopping $DESC: " + _stop + echo "ok" + ;; + restart|force-reload) + echo -n "Restarting $DESC: " + _stop + sleep 1 + _start + echo "ok" + ;; + status) + echo -n "Status of $DESC: " + _status && echo "running" || echo "stopped" + ;; + *) + N=/etc/init.d/$NAME + echo "Usage: $N {start|stop|restart|force-reload|status}" >&2 + exit 1 + ;; +esac + +exit 0 diff --git a/addons/point_of_sale/tools/posbox/overwrite_before_init/etc/init_posbox_image.sh b/addons/point_of_sale/tools/posbox/overwrite_before_init/etc/init_posbox_image.sh new file mode 100755 index 00000000000..68b64fb94ca --- /dev/null +++ b/addons/point_of_sale/tools/posbox/overwrite_before_init/etc/init_posbox_image.sh @@ -0,0 +1,68 @@ +#!/usr/bin/env bash +set -o errexit +set -o nounset +set -o pipefail +# set -o xtrace + +__dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +__file="${__dir}/$(basename "${BASH_SOURCE[0]}")" +__base="$(basename ${__file} .sh)" + +# Recommends: antiword, graphviz, ghostscript, postgresql, python-gevent, poppler-utils +export DEBIAN_FRONTEND=noninteractive + +# GUI-related packages +PKGS_TO_DELETE="xserver-xorg-video-fbdev xserver-xorg xinit gstreamer1.0-x gstreamer1.0-omx gstreamer1.0-plugins-base gstreamer1.0-plugins-good gstreamer1.0-plugins-bad gstreamer1.0-alsa gstreamer1.0-libav epiphany-browser lxde lxtask menu-xdg gksu xserver-xorg-video-fbturbo xpdf gtk2-engines alsa-utils netsurf-gtk zenity desktop-base lxpolkit weston omxplayer raspberrypi-artwork lightdm gnome-themes-standard-data gnome-icon-theme qt50-snapshot qt50-quick-particle-examples idle python-pygame python-tk idle3 python-serial python-picamera debian-reference-en dillo x2x scratch nuscratch raspberrypi-ui-mods timidity smartsim penguinspuzzle pistore sonic-pi python-pifacecommon python-pifacedigitalio oracle-java8-jdk minecraft-pi python-minecraftpi wolfram-engine raspi-config libgl1-mesa-dri libicu48 pypy-upstream lxde-icon-theme python3" +INSTALLED_PKGS_TO_DELETE="" +set +o errexit +for CURRENT_PKG in $(echo $PKGS_TO_DELETE); do + $(dpkg --status $CURRENT_PKG &> /dev/null) + if [[ $? -eq 0 ]]; then + INSTALLED_PKGS_TO_DELETE="$INSTALLED_PKGS_TO_DELETE $CURRENT_PKG" + fi +done +set -o errexit + +apt-get -y remove --purge ${INSTALLED_PKGS_TO_DELETE} + +# Remove automatically installed dependency packages +apt-get -y autoremove + +apt-get update +apt-get -y dist-upgrade + +PKGS_TO_INSTALL="adduser postgresql-client python python-dateutil python-decorator python-docutils python-feedparser python-imaging python-jinja2 python-ldap python-libxslt1 python-lxml python-mako python-mock python-openid python-passlib python-psutil python-psycopg2 python-pybabel python-pychart python-pydot python-pyparsing python-pypdf python-reportlab python-requests python-simplejson python-tz python-unittest2 python-vatnumber python-vobject python-werkzeug python-xlwt python-yaml postgresql python-gevent python-serial python-pip python-dev localepurge vim mc mg" + +apt-get -y install ${PKGS_TO_INSTALL} + +apt-get clean +localepurge +rm -rf /usr/share/doc || true +rm -rf /home/pi/python_games || true + +# python-usb in wheezy is too old +# the latest pyusb from pip does not work either, usb.core.find() never returns +# this may be fixed with libusb>2:1.0.11-1, but that's the most recent one in raspbian +# so we install the latest pyusb that works with this libusb +pip install pyusb==1.0.0b1 +pip install qrcode +pip install evdev + +usermod -a -G lp pi + +sudo -u postgres createuser -s pi +mkdir /var/log/odoo +chown pi:pi /var/log/odoo + +update-rc.d odoo defaults + +# create dirs for ramdisks +create_ramdisk_dir () { + mkdir "${1}_ram" +} + +create_ramdisk_dir "/var" +create_ramdisk_dir "/etc" +mkdir /root_bypass_ramdisks + +reboot diff --git a/addons/point_of_sale/tools/posbox/overwrite_before_init/etc/ld.so.preload b/addons/point_of_sale/tools/posbox/overwrite_before_init/etc/ld.so.preload new file mode 100644 index 00000000000..5e9332843b5 --- /dev/null +++ b/addons/point_of_sale/tools/posbox/overwrite_before_init/etc/ld.so.preload @@ -0,0 +1,2 @@ +# the used qemu-kernel doesn't work well with this library, so get rid of it +#/usr/lib/arm-linux-gnueabihf/libcofi_rpi.so diff --git a/addons/point_of_sale/tools/posbox/overwrite_before_init/etc/logrotate.conf b/addons/point_of_sale/tools/posbox/overwrite_before_init/etc/logrotate.conf new file mode 100644 index 00000000000..1ac409b252e --- /dev/null +++ b/addons/point_of_sale/tools/posbox/overwrite_before_init/etc/logrotate.conf @@ -0,0 +1,32 @@ +# see "man logrotate" for details +# rotate log files daily +daily + +# keep 2 days worth of backlogs +rotate 2 + +# create new (empty) log files after rotating old ones +create + +# uncomment this if you want your log files compressed +#compress + +# packages drop log rotation information into this directory +include /etc/logrotate.d + +# no packages own wtmp, or btmp -- we'll rotate them here +/var/log/wtmp { + missingok + monthly + create 0664 root utmp + rotate 1 +} + +/var/log/btmp { + missingok + monthly + create 0660 root utmp + rotate 1 +} + +# system-specific logs may be configured here diff --git a/addons/point_of_sale/tools/posbox/overwrite_before_init/etc/logrotate.d/odoo b/addons/point_of_sale/tools/posbox/overwrite_before_init/etc/logrotate.d/odoo new file mode 100644 index 00000000000..060ec54d012 --- /dev/null +++ b/addons/point_of_sale/tools/posbox/overwrite_before_init/etc/logrotate.d/odoo @@ -0,0 +1,5 @@ +/var/log/odoo/*.log { + copytruncate + missingok + notifempty +} diff --git a/addons/point_of_sale/tools/posbox/overwrite_before_init/etc/rc.local b/addons/point_of_sale/tools/posbox/overwrite_before_init/etc/rc.local new file mode 100755 index 00000000000..c55290c3978 --- /dev/null +++ b/addons/point_of_sale/tools/posbox/overwrite_before_init/etc/rc.local @@ -0,0 +1,22 @@ +#!/bin/sh -e +# +# rc.local +# +# This script is executed at the end of each multiuser runlevel. +# Make sure that the script will "exit 0" on success or any other +# value on error. +# +# In order to enable or disable this script just change the execution +# bits. +# +# By default this script does nothing. + +# Print the IP address +_IP=$(hostname -I) || true +if [ "$_IP" ]; then + printf "My IP address is %s\n" "$_IP" +fi + +/etc/init_posbox_image.sh + +exit 0 diff --git a/addons/point_of_sale/tools/posbox/overwrite_before_init/etc/udev/rules.d/90-qemu.rules b/addons/point_of_sale/tools/posbox/overwrite_before_init/etc/udev/rules.d/90-qemu.rules new file mode 100644 index 00000000000..fadedd206fe --- /dev/null +++ b/addons/point_of_sale/tools/posbox/overwrite_before_init/etc/udev/rules.d/90-qemu.rules @@ -0,0 +1,3 @@ +KERNEL=="sda", SYMLINK+="mmcblk0" +KERNEL=="sda?", SYMLINK+="mmcblk0p%n" +KERNEL=="sda2", SYMLINK+="root" \ No newline at end of file diff --git a/addons/point_of_sale/tools/posbox/overwrite_before_init/home/pi/led_status.sh b/addons/point_of_sale/tools/posbox/overwrite_before_init/home/pi/led_status.sh new file mode 100755 index 00000000000..8d272d7de2b --- /dev/null +++ b/addons/point_of_sale/tools/posbox/overwrite_before_init/home/pi/led_status.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash + +set_brightness() { + echo "${1}" > /sys/class/leds/led0/brightness +} + +check_status_loop() { + while true ; do + if wget --quiet localhost:8069/hw_proxy/hello -O /dev/null ; then + set_brightness 255 + else + set_brightness 0 + fi + sleep 5 + done +} + +echo none > /sys/class/leds/led0/trigger +check_status_loop diff --git a/addons/point_of_sale/tools/posbox/overwrite_before_init/home/pi/odoo.conf b/addons/point_of_sale/tools/posbox/overwrite_before_init/home/pi/odoo.conf new file mode 100644 index 00000000000..1bc0a0d3ba5 --- /dev/null +++ b/addons/point_of_sale/tools/posbox/overwrite_before_init/home/pi/odoo.conf @@ -0,0 +1,6 @@ +[options] +data_dir = /var/run/odoo +log_level = error +logfile = /var/log/odoo/odoo-server.log +pidfile = /var/run/odoo/odoo.pid +server_wide_modules = web,hw_proxy,hw_posbox_homepage,hw_posbox_upgrade,hw_scale,hw_scanner,hw_escpos \ No newline at end of file diff --git a/addons/point_of_sale/tools/posbox/overwrite_before_init/home/pi/posbox_update.sh b/addons/point_of_sale/tools/posbox/overwrite_before_init/home/pi/posbox_update.sh new file mode 100755 index 00000000000..e994a60461f --- /dev/null +++ b/addons/point_of_sale/tools/posbox/overwrite_before_init/home/pi/posbox_update.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +sudo mount -o remount,rw / +sudo git --work-tree=/home/pi/odoo/ --git-dir=/home/pi/odoo/.git pull +sudo mount -o remount,ro / +(sleep 5 && sudo reboot) & diff --git a/addons/point_of_sale/tools/posbox/posbox_create_image.sh b/addons/point_of_sale/tools/posbox/posbox_create_image.sh new file mode 100755 index 00000000000..2a2a0dd9425 --- /dev/null +++ b/addons/point_of_sale/tools/posbox/posbox_create_image.sh @@ -0,0 +1,75 @@ +#!/usr/bin/env bash +set -o errexit +set -o nounset +set -o pipefail +# set -o xtrace + +if [[ $EUID -ne 0 ]]; then + echo "This script must be run as root" + exit 1 +fi + +file_exists() { + [[ -f $1 ]]; +} + +__dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +__file="${__dir}/$(basename "${BASH_SOURCE[0]}")" +__base="$(basename ${__file} .sh)" + +MOUNT_POINT="${__dir}/root_mount" +OVERWRITE_FILES_BEFORE_INIT_DIR="${__dir}/overwrite_before_init" +OVERWRITE_FILES_AFTER_INIT_DIR="${__dir}/overwrite_after_init" + +if [ ! -f kernel-qemu ] || ! file_exists *raspbian*.img ; then + ./posbox_download_images.sh +fi + +cp -a *raspbian*.img posbox.img + +CLONE_DIR="${OVERWRITE_FILES_BEFORE_INIT_DIR}/home/pi/odoo" +mkdir "${CLONE_DIR}" +git clone -b 8.0 --no-checkout --depth 1 https://github.com/odoo/odoo.git "${CLONE_DIR}" +cd "${CLONE_DIR}" +git config core.sparsecheckout true +echo "addons/web +addons/web_kanban +addons/hw_* +openerp/ +odoo.py" | tee --append .git/info/sparse-checkout > /dev/null +git read-tree -mu HEAD +cd "${__dir}" + +# rc.local +LOOP_MAPPER_PATH=$(kpartx -av posbox.img | tail -n 1 | cut -d ' ' -f 3) +LOOP_MAPPER_PATH="/dev/mapper/${LOOP_MAPPER_PATH}" +mkdir "${MOUNT_POINT}" +mount "${LOOP_MAPPER_PATH}" "${MOUNT_POINT}" + +# 'overlay' the overwrite directory onto the mounted image filesystem +cp -a "${OVERWRITE_FILES_BEFORE_INIT_DIR}"/* "${MOUNT_POINT}" + +# get rid of the git clone +rm -rf "${CLONE_DIR}" + +# get rid of the mount, we have to remount it anyway because we have +# to "refresh" the filesystem after qemu modified it +sleep 2 +umount "${MOUNT_POINT}" + +# from http://paulscott.co.za/blog/full-raspberry-pi-raspbian-emulation-with-qemu/ +# ssh pi@localhost -p10022 +qemu-system-arm -kernel kernel-qemu -cpu arm1176 -m 256 -M versatilepb -no-reboot -serial stdio -append "root=/dev/sda2 panic=1 rootfstype=ext4 rw" -hda posbox.img -net user,hostfwd=tcp::10022-:22,hostfwd=tcp::18069-:8069 -net nic + +mount "${LOOP_MAPPER_PATH}" "${MOUNT_POINT}" +cp -av "${OVERWRITE_FILES_AFTER_INIT_DIR}"/* "${MOUNT_POINT}" + +# cleanup +sleep 2 +umount "${MOUNT_POINT}" +rm -r "${MOUNT_POINT}" + +echo "Running zerofree..." +zerofree -v "${LOOP_MAPPER_PATH}" || true + +kpartx -d posbox.img diff --git a/addons/point_of_sale/tools/posbox/posbox_download_images.sh b/addons/point_of_sale/tools/posbox/posbox_download_images.sh new file mode 100755 index 00000000000..a138e44bfab --- /dev/null +++ b/addons/point_of_sale/tools/posbox/posbox_download_images.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +wget 'http://downloads.raspberrypi.org/raspbian_latest' -O raspbian.img.zip +unzip raspbian.img.zip +wget 'https://github.com/dhruvvyas90/qemu-rpi-kernel/raw/master/kernel-qemu' diff --git a/addons/sale/wizard/sale_make_invoice_advance.py b/addons/sale/wizard/sale_make_invoice_advance.py index 28c48bc8a67..0be952c936b 100644 --- a/addons/sale/wizard/sale_make_invoice_advance.py +++ b/addons/sale/wizard/sale_make_invoice_advance.py @@ -79,7 +79,8 @@ class sale_advance_payment_inv(osv.osv_memory): result = [] for sale in sale_obj.browse(cr, uid, sale_ids, context=context): val = inv_line_obj.product_id_change(cr, uid, [], wizard.product_id.id, - False, partner_id=sale.partner_id.id, fposition_id=sale.fiscal_position.id) + False, partner_id=sale.partner_id.id, fposition_id=sale.fiscal_position.id, + company_id=sale.company_id.id) res = val['value'] # determine and check income account @@ -143,7 +144,7 @@ class sale_advance_payment_inv(osv.osv_memory): 'partner_id': sale.partner_invoice_id.id, 'invoice_line': [(0, 0, inv_line_values)], 'currency_id': sale.pricelist_id.currency_id.id, - 'comment': '', + 'comment': sale.note, 'payment_term': sale.payment_term.id, 'fiscal_position': sale.fiscal_position.id or sale.partner_id.property_account_position.id, 'team_id': sale.team_id.id, diff --git a/addons/stock/stock.py b/addons/stock/stock.py index 11d5e8442eb..5791b050f23 100644 --- a/addons/stock/stock.py +++ b/addons/stock/stock.py @@ -993,7 +993,8 @@ class stock_picking(osv.osv): move_obj = self.pool.get("stock.move") move_obj.write(cr, uid, backorder_move_ids, {'picking_id': backorder_id}, context=context) - self.write(cr, uid, [picking.id], {'date_done': time.strftime(DEFAULT_SERVER_DATETIME_FORMAT)}, context=context) + if not picking.date_done: + self.write(cr, uid, [picking.id], {'date_done': time.strftime(DEFAULT_SERVER_DATETIME_FORMAT)}, context=context) self.action_confirm(cr, uid, [backorder_id], context=context) return backorder_id return False @@ -1744,12 +1745,14 @@ class stock_move(osv.osv): settings_obj = self.pool.get('stock.config.settings') uom_obj = self.pool.get('product.uom') res = dict.fromkeys(ids, '') + precision = self.pool['decimal.precision'].precision_get(cr, uid, 'Product Unit of Measure') for move in self.browse(cr, uid, ids, context=context): if move.state in ('draft', 'done', 'cancel') or move.location_id.usage != 'internal': res[move.id] = '' # 'not applicable' or 'n/a' could work too continue total_available = min(move.product_qty, move.reserved_availability + move.availability) - total_available = uom_obj._compute_qty_obj(cr, uid, move.product_id.uom_id, total_available, move.product_uom, context=context) + total_available = uom_obj._compute_qty_obj(cr, uid, move.product_id.uom_id, total_available, move.product_uom, round=False, context=context) + total_available = float_round(total_available, precision_digits=precision) info = str(total_available) #look in the settings if we need to display the UoM name or not config_ids = settings_obj.search(cr, uid, [], limit=1, order='id DESC', context=context) @@ -1760,7 +1763,8 @@ class stock_move(osv.osv): if move.reserved_availability: if move.reserved_availability != total_available: #some of the available quantity is assigned and some are available but not reserved - reserved_available = uom_obj._compute_qty_obj(cr, uid, move.product_id.uom_id, move.reserved_availability, move.product_uom, context=context) + reserved_available = uom_obj._compute_qty_obj(cr, uid, move.product_id.uom_id, move.reserved_availability, move.product_uom, round=False, context=context) + reserved_available = float_round(reserved_available, precision_digits=precision) info += _(' (%s reserved)') % str(reserved_available) else: #all available quantity is assigned diff --git a/addons/stock_account/stock.py b/addons/stock_account/stock.py index 1b12d4d95b4..f40e7b3c561 100644 --- a/addons/stock_account/stock.py +++ b/addons/stock_account/stock.py @@ -315,9 +315,15 @@ class stock_picking(osv.osv): invoices[key] = invoice_id else: invoice = invoice_obj.browse(cr, uid, invoices[key], context=context) + merge_vals = {} if not invoice.origin or invoice_vals['origin'] not in invoice.origin.split(', '): invoice_origin = filter(None, [invoice.origin, invoice_vals['origin']]) - invoice.write({'origin': ', '.join(invoice_origin)}) + merge_vals['origin'] = ', '.join(invoice_origin) + if not invoice.name or invoice_vals['name'] not in invoice.name.split(', '): + invoice_name = filter(None, [invoice.name, invoice_vals['name']]) + merge_vals['name'] = ', '.join(invoice_name) + if merge_vals: + invoice.write(merge_vals) invoice_line_vals = move_obj._get_invoice_line_vals(cr, uid, move, partner, inv_type, context=context) invoice_line_vals['invoice_id'] = invoices[key] diff --git a/addons/survey/security/survey_security.xml b/addons/survey/security/survey_security.xml index 7375cf227ab..97bcb8cc65e 100644 --- a/addons/survey/security/survey_security.xml +++ b/addons/survey/security/survey_security.xml @@ -50,7 +50,7 @@ Access to user_input for regular users - [('create_uid', '=', user.id)] + ['|', ('create_uid', '=', user.id), ('partner_id', '=', user.partner_id.id)] diff --git a/addons/web/static/src/css/ie.css b/addons/web/static/src/css/ie.css index f628ed1213d..a547120033e 100644 --- a/addons/web/static/src/css/ie.css +++ b/addons/web/static/src/css/ie.css @@ -24,9 +24,6 @@ } /** web_kanban */ -:root .openerp .oe_kanban_view .oe_kanban_group_title_vertical { - writing-mode: lr-tb; -} .openerp .oe_kanban_column_cards { min-height: 80px; } diff --git a/addons/web_kanban/static/src/css/kanban.css b/addons/web_kanban/static/src/css/kanban.css index d7762f76398..8cde68b17d8 100644 --- a/addons/web_kanban/static/src/css/kanban.css +++ b/addons/web_kanban/static/src/css/kanban.css @@ -145,7 +145,6 @@ .openerp .oe_kanban_view .oe_kanban_group_title_undefined { color: #666666; } .openerp .oe_kanban_view .oe_kanban_group_title_vertical { - writing-mode: vertical-rl; -webkit-transform: rotate(90deg); -moz-transform: rotate(90deg); -o-transform: rotate(90deg); @@ -483,7 +482,6 @@ .openerp_ie .oe_kanban_view .oe_kanban_group_header.oe_kanban_group_folded .oe_kanban_group_title_vertical { display: inline-block !important; } .openerp_ie .oe_kanban_view .oe_kanban_group_title_vertical { - -ms-writing-mode: lr-tb !important; background: #f0eeee; } .openerp_ie .oe_kanban_view.oe_kanban_grouped .oe_kanban_group_header { height: 1%; } diff --git a/addons/web_kanban/static/src/css/kanban.sass b/addons/web_kanban/static/src/css/kanban.sass index 463cc916bc6..0933e3aa836 100644 --- a/addons/web_kanban/static/src/css/kanban.sass +++ b/addons/web_kanban/static/src/css/kanban.sass @@ -203,7 +203,6 @@ .oe_kanban_group_title_undefined color: #666666 .oe_kanban_group_title_vertical - writing-mode: vertical-rl -webkit-transform: rotate(90deg) -moz-transform: rotate(90deg) -o-transform: rotate(90deg) @@ -515,7 +514,6 @@ .oe_kanban_group_header.oe_kanban_group_folded .oe_kanban_group_title_vertical display: inline-block !important .oe_kanban_group_title_vertical - -ms-writing-mode: lr-tb !important background: rgb(240, 238, 238) &.oe_kanban_grouped .oe_kanban_group_header diff --git a/addons/website/static/src/js/website.js b/addons/website/static/src/js/website.js index 3869986e48a..a11961e8e5c 100644 --- a/addons/website/static/src/js/website.js +++ b/addons/website/static/src/js/website.js @@ -369,8 +369,8 @@ // retrieve the hash before the redirect var redirect = { lang: self.data('lang'), - url: self.attr('href'), - hash: location.hash + url: encodeURIComponent(self.attr('href')), + hash: encodeURIComponent(location.hash) }; location.href = _.str.sprintf("/website/lang/%(lang)s?r=%(url)s%(hash)s", redirect); }); diff --git a/addons/website_sale/static/src/js/website_sale.js b/addons/website_sale/static/src/js/website_sale.js index 15965a2d8da..086473fec5c 100644 --- a/addons/website_sale/static/src/js/website_sale.js +++ b/addons/website_sale/static/src/js/website_sale.js @@ -84,7 +84,7 @@ $('.oe_website_sale').each(function () { return; } if (!data.quantity) { - location.reload(); + location.reload(true); return; } var $q = $(".my_cart_quantity"); diff --git a/doc/cla/corporate/hbee.md b/doc/cla/corporate/hbee.md new file mode 100644 index 00000000000..aaebe686d00 --- /dev/null +++ b/doc/cla/corporate/hbee.md @@ -0,0 +1,21 @@ +Lithuania, Macedonia, 2015-07-31 + +HBEE agrees to the terms of the Odoo Corporate Contributor License +Agreement v1.0. + +I declare that I am authorized and able to make this agreement and sign this +declaration. + +Signed, + +Paulius Sladkevičius paulius@hbee.eu https://github.com/komsas + +List of contributors: + +Darko Nikolovski darko@hbee.eu https://github.com/DarkoNikolovski +Ignas Karpiejus ignas@hbee.eu https://github.com/ignask +Karolis Kalantojus karolis@hbee.eu https://github.com/kkarolis +Kiril Vangelovski kiril@hbee.eu https://github.com/kirca +Naglis Jonaitis naglis@hbee.eu https://github.com/naglis +Paulius Sladkevičius paulius@hbee.eu https://github.com/komsas +Tomas Račys tomas@hbee.eu https://github.com/taezaz diff --git a/doc/cla/corporate/rossa.md b/doc/cla/corporate/rossa.md new file mode 100644 index 00000000000..5f274745bd5 --- /dev/null +++ b/doc/cla/corporate/rossa.md @@ -0,0 +1,14 @@ +Paraguay, 2015-07-09 + +Rossa S.A. agrees to the terms of the Odoo Corporate Contributor License Agreement v1.0. + +I declare that I am authorized and able to make this agreement and sign this declaration. + +Signed, + +Rossa S.A. emaildarossa@gmail.com https://github.com/rossasa + +List of contributors: + +Marcelo Pickler marcelo@rossa.com.py https://github.com/loxamir +Alan R. S. Pieske alan@rossa.com.py https://github.com/alanpy diff --git a/doc/cla/corporate/willow-it.md b/doc/cla/corporate/willow-it.md new file mode 100644 index 00000000000..9e3480e6cb4 --- /dev/null +++ b/doc/cla/corporate/willow-it.md @@ -0,0 +1,18 @@ +Australia, 2015-07-20 + +Willow IT agrees to the terms of the Odoo Corporate Contributor License +Agreement v1.0. + +I declare that I am authorized and able to make this agreement and sign this +declaration. + +Signed, + +Richard deMeester richard@willowit.com.au https://github.com/richard-willowit + +List of contributors: + +Richard deMeester richard@willowit.com.au https://github.com/richard-willowit +Douglas Parker doug@willowit.com.au https://github.com/doug-willowit +Bhavya Malik bhavya@willowit.com.au https://github.com/bhavya-willowit +Jon Wilson jon@willowit.com.au https://github.com/jon-willowit diff --git a/openerp/addons/base/ir/ir_ui_view.py b/openerp/addons/base/ir/ir_ui_view.py index c2e2840a58e..7acbf86a8cc 100644 --- a/openerp/addons/base/ir/ir_ui_view.py +++ b/openerp/addons/base/ir/ir_ui_view.py @@ -730,7 +730,7 @@ class view(osv.osv): orm.transfer_field_to_modifiers(field, modifiers) elif node.tag in ('form', 'tree'): - result = Model.view_header_get(cr, user, False, node.tag, context) + result = Model.view_header_get(cr, user, False, node.tag, context=context) if result: node.set('string', result) in_tree_view = node.tag == 'tree' @@ -869,17 +869,17 @@ class view(osv.osv): if node.tag == 'diagram': if node.getchildren()[0].tag == 'node': node_model = self.pool[node.getchildren()[0].get('object')] - node_fields = node_model.fields_get(cr, user, None, context) + node_fields = node_model.fields_get(cr, user, None, context=context) fields.update(node_fields) if not node.get("create") and \ not node_model.check_access_rights(cr, user, 'create', raise_exception=False) or \ not context.get("create", True): node.set("create", 'false') if node.getchildren()[1].tag == 'arrow': - arrow_fields = self.pool[node.getchildren()[1].get('object')].fields_get(cr, user, None, context) + arrow_fields = self.pool[node.getchildren()[1].get('object')].fields_get(cr, user, None, context=context) fields.update(arrow_fields) else: - fields = Model.fields_get(cr, user, None, context) + fields = Model.fields_get(cr, user, None, context=context) node = self.add_on_change(cr, user, model, node) fields_def = self.postprocess(cr, user, model, node, view_id, False, fields, context=context) diff --git a/openerp/addons/base/res/res_users.py b/openerp/addons/base/res/res_users.py index 05c29c435f4..ed27af14b1d 100644 --- a/openerp/addons/base/res/res_users.py +++ b/openerp/addons/base/res/res_users.py @@ -142,7 +142,7 @@ class res_users(osv.osv): avatar, ... The user model is now dedicated to technical data. """ __admin_ids = {} - _uid_cache = {} + __uid_cache = {} _inherits = { 'res.partner': 'partner_id', } @@ -307,6 +307,24 @@ class res_users(osv.osv): return result + def read_group(self, cr, uid, domain, fields, groupby, offset=0, limit=None, context=None, orderby=False, lazy=True): + if uid != SUPERUSER_ID: + groupby_fields = set([groupby] if isinstance(groupby, basestring) else groupby) + if groupby_fields.intersection(USER_PRIVATE_FIELDS): + raise openerp.exceptions.AccessError('Invalid groupby') + return super(res_users, self).read_group( + cr, uid, domain, fields, groupby, offset=offset, limit=limit, context=context, orderby=orderby, lazy=lazy) + + def _search(self, cr, user, args, offset=0, limit=None, order=None, context=None, count=False, access_rights_uid=None): + if user != SUPERUSER_ID and args: + domain_terms = [term for term in args if isinstance(term, (tuple, list))] + domain_fields = set(left for (left, op, right) in domain_terms) + if domain_fields.intersection(USER_PRIVATE_FIELDS): + raise openerp.exceptions.AccessError('Invalid search criterion') + return super(res_users, self)._search( + cr, user, args, offset=offset, limit=limit, order=order, context=context, count=count, + access_rights_uid=access_rights_uid) + def create(self, cr, uid, vals, context=None): user_id = super(res_users, self).create(cr, uid, vals, context=context) user = self.browse(cr, uid, user_id, context=context) @@ -341,10 +359,10 @@ class res_users(osv.osv): clear = partial(self.pool['ir.rule'].clear_cache, cr) map(clear, ids) db = cr.dbname - if db in self._uid_cache: + if db in self.__uid_cache: for id in ids: - if id in self._uid_cache[db]: - del self._uid_cache[db][id] + if id in self.__uid_cache[db]: + del self.__uid_cache[db][id] self._context_get.clear_cache(self) self.has_group.clear_cache(self) return res @@ -353,10 +371,10 @@ class res_users(osv.osv): if 1 in ids: raise UserError(_('You can not remove the admin user as it is used internally for resources created by Odoo (updates, module installation, ...)')) db = cr.dbname - if db in self._uid_cache: + if db in self.__uid_cache: for id in ids: - if id in self._uid_cache[db]: - del self._uid_cache[db][id] + if id in self.__uid_cache[db]: + del self.__uid_cache[db][id] return super(res_users, self).unlink(cr, uid, ids, context=context) def name_search(self, cr, user, name='', args=None, operator='ilike', context=None, limit=100): @@ -495,15 +513,12 @@ class res_users(osv.osv): if not passwd: # empty passwords disallowed for obvious security reasons raise openerp.exceptions.AccessDenied() - if self._uid_cache.get(db, {}).get(uid) == passwd: + if self.__uid_cache.setdefault(db, {}).get(uid) == passwd: return cr = self.pool.cursor() try: self.check_credentials(cr, uid, passwd) - if self._uid_cache.has_key(db): - self._uid_cache[db][uid] = passwd - else: - self._uid_cache[db] = {uid:passwd} + self.__uid_cache[db][uid] = passwd finally: cr.close() diff --git a/openerp/tests/common.py b/openerp/tests/common.py index 24d49c88617..1413250b126 100644 --- a/openerp/tests/common.py +++ b/openerp/tests/common.py @@ -334,6 +334,7 @@ class HttpCase(TransactionCase): phantom.terminate() phantom.wait() self._wait_remaining_requests() + # we ignore phantomjs return code as we kill it as soon as we have ok _logger.info("phantom_run execution finished") def _wait_remaining_requests(self):