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