[MERGE] forward port of branch 8.0 up to 02954ba

This commit is contained in:
Christophe Simonis
2015-08-11 16:56:47 +02:00
48 changed files with 591 additions and 105 deletions
+1 -1
View File
@@ -399,7 +399,7 @@
<field name="reconciled" invisible="1"/>
</group>
<group>
<field name="payment_term" class="oe_inline" on_change="onchange_payment_term_date_invoice(payment_term,date_invoice)"/>
<field name="payment_term" class="oe_inline" on_change="onchange_payment_term_date_invoice(payment_term, date_invoice)"/>
</group>
<field name="comment" class="oe_inline" placeholder="Additional notes..."/>
</page>
+4
View File
@@ -885,6 +885,7 @@
<field name="priority">100</field>
<field name="arch" type="xml">
<tree string="Account Tax Code" toolbar="1">
<field name="sequence" widget="handle"/>
<field name="name"/>
<field name="code"/>
<field name="sum_period"/>
@@ -904,6 +905,7 @@
<field name="code"/>
<field name="parent_id"/>
<field name="company_id" groups="base.group_multi_company"/>
<field name="sequence"/>
</group>
<group string="Reporting Configuration">
<field name="notprintable"/>
@@ -2154,6 +2156,7 @@
<field name="field_parent">child_ids</field>
<field name="arch" type="xml">
<tree string="Account Tax Code Template" toolbar="1">
<field name="sequence" widget="handle"/>
<field name="name"/>
<field name="code"/>
<field name="parent_id" invisible="1"/>
@@ -2183,6 +2186,7 @@
<field name="code"/>
<field name="parent_id"/>
<field name="sign"/>
<field name="sequence"/>
</group>
<separator string="Description"/>
<field name="info"/>
@@ -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)
+2 -2
View File
@@ -14,8 +14,8 @@
<record id="provider_facebook" model="auth.oauth.provider">
<field name="name">Facebook Graph</field>
<field name="auth_endpoint">https://www.facebook.com/dialog/oauth</field>
<field name="scope"></field>
<field name="validation_endpoint">https://graph.facebook.com/me/permissions</field>
<field name="scope">public_profile,email</field>
<field name="validation_endpoint">https://graph.facebook.com/me</field>
<field name="data_endpoint"></field>
<field name="css_class">fa fa-facebook-square</field>
<field name="body">Log in with facebook</field>
+6 -1
View File
@@ -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:
+9 -13
View File
@@ -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
+13 -2
View File
@@ -1,3 +1,4 @@
<?xml version='1.0' encoding='UTF-8' ?>
<openerp>
<data>
@@ -25,17 +26,27 @@
<filter domain="[('state','in',('confirm','validate1'))]" string="To Approve" name="approve"/>
<filter domain="[('state','=','validate')]" string="Validated" name="validated"/>
<separator/>
<filter name="active_types" string="Active Types" domain="[('holiday_status_id.active', '=', True)]" help="Filters only on allocations and requests that belong to an holiday type that is 'active' (active field is True)"/>
<separator/>
<filter string="New Mail" name="message_unread" domain="[('message_unread','=',True)]"/>
<separator/>
<filter string="Approved Leaves" name="validated" domain="[('state','=','validate')]"/>
<separator/>
<filter string="To Report in Payslip" name="gray" domain="[('payslip_status', '=', False)]" groups="base.group_hr_manager"/>
<separator/>
<filter name="year" string="Year" domain="[('holiday_status_id.active','=',True)]" help="Filters only on allocations and requests that belong to an holiday type that is 'active' (active field is True)"/>
<separator/>
<filter string="To Do" name="todo" domain="[('payslip_status', '=', False), ('holiday_status_id.active', '=', True)]"/>
<separator/>
<filter string="My Department Leaves" help="My Department Leaves" domain="[('department_id.manager_id','=',uid)]"/>
<filter name="year" string="Year"
domain="['|','&amp;',('date_to', '&lt;=', time.strftime('%%Y-12-31')),
('date_to', '&gt;=', time.strftime('%%Y-01-01')),
'&amp;',('date_from', '&lt;=', time.strftime('%%Y-12-31')),
('date_from', '&gt;=', time.strftime('%%Y-01-01'))]"
help="Current Year Leaves"/>
<separator/>
<filter string="My Requests" name="my_leaves" domain="[('user_id', '=', uid)]" help="My Leave Requests"/>
<separator/>
<filter string="My Department Leaves" name="my_department_leaves" domain="[('department_id.manager_id.user_id', '=', uid)]"/>
<field name="employee_id"/>
<field name="department_id"/>
<field name="holiday_status_id"/>
@@ -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)
+6 -2
View File
@@ -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()
@@ -36,16 +36,16 @@ index_template = """
</p>
<p>
For more information on how to setup the Point of Sale with
the PosBox, please refer to <a href='/hw_proxy/static/doc/manual.pdf'>the manual</a>
the PosBox, please refer to <a href='/hw_proxy/static/doc/manual.pdf'>the manual</a>.
</p>
<p>
To see the status of the connected hardware, please refer
to the <a href='/hw_proxy/status'>hardware status page</a>
to the <a href='/hw_proxy/status'>hardware status page</a>.
</p>
<p>
The PosBox software installed on this posbox is <b>version 6</b>,
The PosBox software installed on this posbox is <b>version 13</b>,
the posbox version number is independent from Odoo. You can upgrade
the software on the <a href='/hw_proxy/upgrade/'>upgrade page</a>
the software on the <a href='/hw_proxy/upgrade/'>upgrade page</a>.
</p>
<p>For any other question, please contact the Odoo support at <a href='mailto:support@odoo.com'>support@odoo.com</a>
</p>
+8 -33
View File
@@ -29,17 +29,8 @@ upgrade_template = """
$.ajax({
url:'/hw_proxy/perform_upgrade/'
}).then(function(status){
$('#upgrade').html('Upgrade Successful<br \\>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 = """
<body>
<h1>PosBox Software Upgrade</h1>
<p>
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.
<p></p>
However the preferred method to upgrade the posbox is to flash the sd-card with
the <a href='http://nightly.openerp.com/trunk/posbox/'>latest image</a>. The upgrade
the <a href='http://nightly.odoo.com/trunk/posbox/'>latest image</a>. The upgrade
procedure is explained into to the <a href='/hw_proxy/static/doc/manual.pdf'>PosBox manual</a>
</p>
<p>
@@ -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'
+17 -1
View File
@@ -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):
<p>The list of connected USB devices as seen by the posbox</p>
"""
devices = commands.getoutput("lsusb").split('\n')
count = 0
resp += "<div class='devices'>\n"
for device in devices:
device_name = device[device.find('ID')+2:]
resp+= "<div class='device' data-device='"+device+"'>"+device_name+"</div>\n"
device_id = device_name.split()[0]
if not (device_id in BANNED_DEVICES):
resp+= "<div class='device' data-device='"+device+"'>"+device_name+"</div>\n"
count += 1
if count == 0:
resp += "<div class='device'>No USB Device Found</div>"
resp += "</div>\n"
resp += """
<h2>Add New Printer</h2>
+16 -8
View File
@@ -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()
+6
View File
@@ -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()
+3 -3
View File
@@ -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 '<img src="%s" alt=""/>' % 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
@@ -0,0 +1,3 @@
kernel-qemu
posbox.img
raspbian.img
@@ -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
@@ -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
@@ -0,0 +1,3 @@
# needed for postgresql
auto lo
iface lo inet loopback
@@ -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
@@ -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"
@@ -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
@@ -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
@@ -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
@@ -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
@@ -0,0 +1,5 @@
/var/log/odoo/*.log {
copytruncate
missingok
notifempty
}
@@ -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
@@ -0,0 +1,3 @@
KERNEL=="sda", SYMLINK+="mmcblk0"
KERNEL=="sda?", SYMLINK+="mmcblk0p%n"
KERNEL=="sda2", SYMLINK+="root"
@@ -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
@@ -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
@@ -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) &
+75
View File
@@ -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
@@ -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'
@@ -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,
+7 -3
View File
@@ -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
+7 -1
View File
@@ -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]
+1 -1
View File
@@ -50,7 +50,7 @@
<record id="survey_input_users_access" model="ir.rule">
<field name="name">Access to user_input for regular users</field>
<field name="model_id" ref="survey.model_survey_user_input"/>
<field name="domain_force">[('create_uid', '=', user.id)]</field>
<field name="domain_force">['|', ('create_uid', '=', user.id), ('partner_id', '=', user.partner_id.id)]</field>
<field name="groups" eval="[(4, ref('base.group_survey_user'))]"/>
<field eval="0" name="perm_unlink"/>
<field eval="0" name="perm_write"/>
-3
View File
@@ -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;
}
@@ -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%; }
@@ -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
+2 -2
View File
@@ -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);
});
@@ -84,7 +84,7 @@ $('.oe_website_sale').each(function () {
return;
}
if (!data.quantity) {
location.reload();
location.reload(true);
return;
}
var $q = $(".my_cart_quantity");
+21
View File
@@ -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
+14
View File
@@ -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
+18
View File
@@ -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
+4 -4
View File
@@ -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)
+27 -12
View File
@@ -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()
+1
View File
@@ -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):