[MERGE] forward port of branch 8.0 up to d744923
This commit is contained in:
@@ -94,7 +94,7 @@ class report_aged_receivable(osv.osv):
|
||||
self._init(cr, user)
|
||||
self.called = True # To make sure that init doesn't get called multiple times
|
||||
|
||||
res = super(report_aged_receivable, self).fields_view_get(cr, user, view_id, view_type, context, toolbar=toolbar, submenu=submenu)
|
||||
res = super(report_aged_receivable, self).fields_view_get(cr, user, view_id, view_type, context=context, toolbar=toolbar, submenu=submenu)
|
||||
return res
|
||||
|
||||
def _calc_bal(self, cr, uid, ids, name, args, context=None):
|
||||
|
||||
@@ -1214,9 +1214,11 @@ openerp.account = function (instance) {
|
||||
|
||||
changePartnerClickHandler: function() {
|
||||
var self = this;
|
||||
var partner_name = self.st_line.partner_name;
|
||||
$.when(self.changePartner(false)).then(function(){
|
||||
self.$(".change_partner_container").show();
|
||||
self.$(".partner_name").hide();
|
||||
self.$(".change_partner_container").find("input").attr("placeholder", partner_name);
|
||||
self.change_partner_field.$drop_down.trigger("click");
|
||||
})
|
||||
},
|
||||
@@ -1658,10 +1660,9 @@ openerp.account = function (instance) {
|
||||
// Update model
|
||||
.call("write", [[self.st_line_id], {'partner_id': partner_id}])
|
||||
.then(function () {
|
||||
self.do_load_reconciliation_proposition = false; // of the server might set the statement line's partner
|
||||
self.do_load_reconciliation_proposition = true; // the server will set the statement line with the new partner
|
||||
self.animation_speed = 0;
|
||||
return $.when(self.restart(self.get("mode"))).then(function(){
|
||||
self.do_load_reconciliation_proposition = true;
|
||||
self.is_consistent = true;
|
||||
self.set("mode", "match");
|
||||
});
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
<p t-if="getLines(o)">
|
||||
<t t-foreach="message(o, res_company)" t-as="messageline">
|
||||
<span t-esc="messageline"/>
|
||||
<p t-esc="messageline"/>
|
||||
</t>
|
||||
</p>
|
||||
|
||||
|
||||
@@ -169,7 +169,7 @@ class account_analytic_plan_instance(osv.osv):
|
||||
if context is None:
|
||||
context = {}
|
||||
wiz_id = self.pool.get('ir.actions.act_window').search(cr, uid, [("name","=","analytic.plan.create.model.action")], context=context)
|
||||
res = super(account_analytic_plan_instance,self).fields_view_get(cr, uid, view_id, view_type, context, toolbar=toolbar, submenu=submenu)
|
||||
res = super(account_analytic_plan_instance,self).fields_view_get(cr, uid, view_id, view_type, context=context, toolbar=toolbar, submenu=submenu)
|
||||
journal_obj = self.pool.get('account.journal')
|
||||
analytic_plan_obj = self.pool.get('account.analytic.plan')
|
||||
if (res['type']=='form'):
|
||||
@@ -361,7 +361,7 @@ class account_move_line(osv.osv):
|
||||
def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
|
||||
if context is None:
|
||||
context = {}
|
||||
result = super(account_move_line, self).fields_view_get(cr, uid, view_id, view_type, context, toolbar=toolbar, submenu=submenu)
|
||||
result = super(account_move_line, self).fields_view_get(cr, uid, view_id, view_type, context=context, toolbar=toolbar, submenu=submenu)
|
||||
return result
|
||||
|
||||
|
||||
|
||||
@@ -311,7 +311,7 @@ class ir_model_fields_anonymize_wizard(osv.osv_memory):
|
||||
|
||||
step = context.get('step', 'new_window')
|
||||
|
||||
res = super(ir_model_fields_anonymize_wizard, self).fields_view_get(cr, uid, view_id, view_type, context, *args, **kwargs)
|
||||
res = super(ir_model_fields_anonymize_wizard, self).fields_view_get(cr, uid, view_id, view_type, context=context, *args, **kwargs)
|
||||
|
||||
eview = etree.fromstring(res['arch'])
|
||||
placeholder = eview.xpath("group[@name='placeholder1']")
|
||||
|
||||
@@ -40,7 +40,9 @@
|
||||
<t t-call="web.login_layout">
|
||||
<form class="oe_signup_form" role="form" method="post" t-if="not message">
|
||||
|
||||
<t t-call="auth_signup.fields"/>
|
||||
<t t-call="auth_signup.fields">
|
||||
<t t-set="only_passwords" t-value="bool(token)"/>
|
||||
</t>
|
||||
|
||||
<p class="alert alert-danger" t-if="error">
|
||||
<t t-esc="error"/>
|
||||
|
||||
@@ -143,7 +143,7 @@ class crm_lead(format_address, osv.osv):
|
||||
action = self.get_formview_action(cr, user, context['opportunity_id'], context=context)
|
||||
if action.get('views') and any(view_id for view_id in action['views'] if view_id[1] == view_type):
|
||||
view_id = next(view_id[0] for view_id in action['views'] if view_id[1] == view_type)
|
||||
res = super(crm_lead, self).fields_view_get(cr, user, view_id, view_type, context, toolbar=toolbar, submenu=submenu)
|
||||
res = super(crm_lead, self).fields_view_get(cr, user, view_id, view_type, context=context, toolbar=toolbar, submenu=submenu)
|
||||
if view_type == 'form':
|
||||
res['arch'] = self.fields_view_get_address(cr, user, res['arch'], context=context)
|
||||
return res
|
||||
|
||||
@@ -381,6 +381,9 @@ class gamification_challenge(osv.Model):
|
||||
|
||||
Create goals that haven't been created yet (eg: if added users)
|
||||
Recompute the current value for each goal related"""
|
||||
goal_obj = self.pool['gamification.goal']
|
||||
goal_ids = goal_obj.search(cr, uid, [('challenge_id', 'in', ids), ('state', '=', 'inprogress')], context=context)
|
||||
goal_obj.unlink(cr, uid, goal_ids, context=context)
|
||||
return self._update_all(cr, uid, ids=ids, context=context)
|
||||
|
||||
def action_report_progress(self, cr, uid, ids, context=None):
|
||||
|
||||
@@ -81,7 +81,7 @@ class config(osv.Model):
|
||||
client_secret=google_drive_client_secret,
|
||||
grant_type="refresh_token",
|
||||
scope=scope or 'https://www.googleapis.com/auth/drive'))
|
||||
headers = {"Content-type": "application/x-www-form-urlencoded", "Accept-Encoding": "gzip, deflate"}
|
||||
headers = {"Content-type": "application/x-www-form-urlencoded"}
|
||||
try:
|
||||
req = urllib2.Request('https://accounts.google.com/o/oauth2/token', data, headers)
|
||||
content = urllib2.urlopen(req, timeout=TIMEOUT).read()
|
||||
@@ -101,7 +101,7 @@ class config(osv.Model):
|
||||
access_token = self.get_access_token(cr, uid, context=context)
|
||||
# Copy template in to drive with help of new access token
|
||||
request_url = "https://www.googleapis.com/drive/v2/files/%s?fields=parents/id&access_token=%s" % (template_id, access_token)
|
||||
headers = {"Content-type": "application/x-www-form-urlencoded", "Accept-Encoding": "gzip, deflate"}
|
||||
headers = {"Content-type": "application/x-www-form-urlencoded"}
|
||||
try:
|
||||
req = urllib2.Request(request_url, None, headers)
|
||||
parents = urllib2.urlopen(req, timeout=TIMEOUT).read()
|
||||
|
||||
@@ -52,60 +52,37 @@ class EscposDriver(Thread):
|
||||
self.lock = Lock()
|
||||
self.status = {'status':'connecting', 'messages':[]}
|
||||
|
||||
def supported_devices(self):
|
||||
if not os.path.isfile('escpos_devices.pickle'):
|
||||
return supported_devices.device_list
|
||||
else:
|
||||
try:
|
||||
f = open('escpos_devices.pickle','r')
|
||||
return pickle.load(f)
|
||||
f.close()
|
||||
except Exception as e:
|
||||
self.set_status('error',str(e))
|
||||
return supported_devices.device_list
|
||||
|
||||
def add_supported_device(self,device_string):
|
||||
r = re.compile('[0-9A-Fa-f]{4}:[0-9A-Fa-f]{4}');
|
||||
match = r.search(device_string)
|
||||
if match:
|
||||
match = match.group().split(':')
|
||||
vendor = int(match[0],16)
|
||||
product = int(match[1],16)
|
||||
name = device_string.split('ID')
|
||||
if len(name) >= 2:
|
||||
name = name[1]
|
||||
else:
|
||||
name = name[0]
|
||||
_logger.info('ESC/POS: adding support for device: '+match[0]+':'+match[1]+' '+name)
|
||||
|
||||
device_list = supported_devices.device_list[:]
|
||||
if os.path.isfile('escpos_devices.pickle'):
|
||||
try:
|
||||
f = open('escpos_devices.pickle','r')
|
||||
device_list = pickle.load(f)
|
||||
f.close()
|
||||
except Exception as e:
|
||||
self.set_status('error',str(e))
|
||||
device_list.append({
|
||||
'vendor': vendor,
|
||||
'product': product,
|
||||
'name': name,
|
||||
})
|
||||
|
||||
try:
|
||||
f = open('escpos_devices.pickle','w+')
|
||||
f.seek(0)
|
||||
pickle.dump(device_list,f)
|
||||
f.close()
|
||||
except Exception as e:
|
||||
self.set_status('error',str(e))
|
||||
|
||||
def connected_usb_devices(self):
|
||||
connected = []
|
||||
|
||||
for device in self.supported_devices():
|
||||
if usb.core.find(idVendor=device['vendor'], idProduct=device['product']) != None:
|
||||
connected.append(device)
|
||||
|
||||
# printers can either define bDeviceClass=7, or they can define one of
|
||||
# their interfaces with bInterfaceClass=7. This class checks for both.
|
||||
class FindUsbClass(object):
|
||||
def __init__(self, usb_class):
|
||||
self._class = usb_class
|
||||
def __call__(self, device):
|
||||
# first, let's check the device
|
||||
if device.bDeviceClass == self._class:
|
||||
return True
|
||||
# transverse all devices and look through their interfaces to
|
||||
# find a matching class
|
||||
for cfg in device:
|
||||
intf = usb.util.find_descriptor(cfg, bInterfaceClass=self._class)
|
||||
|
||||
if intf is not None:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
printers = usb.core.find(find_all=True, custom_match=FindUsbClass(7))
|
||||
|
||||
for printer in printers:
|
||||
connected.append({
|
||||
'vendor': printer.idVendor,
|
||||
'product': printer.idProduct,
|
||||
'name': usb.util.get_string(printer, 256, printer.iManufacturer) + " " + usb.util.get_string(printer, 256, printer.iProduct)
|
||||
})
|
||||
|
||||
return connected
|
||||
|
||||
def lockedstart(self):
|
||||
@@ -377,18 +354,3 @@ class EscposProxy(hw_proxy.Proxy):
|
||||
_logger.info('ESC/POS: PRINT XML RECEIPT')
|
||||
driver.push_task('xml_receipt',receipt)
|
||||
|
||||
@http.route('/hw_proxy/escpos/add_supported_device', type='http', auth='none', cors='*')
|
||||
def add_supported_device(self, device_string):
|
||||
_logger.info('ESC/POS: ADDED NEW DEVICE:'+device_string)
|
||||
driver.add_supported_device(device_string)
|
||||
return "The device:\n"+device_string+"\n has been added to the list of supported devices.<br/><a href='/hw_proxy/status'>Ok</a>"
|
||||
|
||||
@http.route('/hw_proxy/escpos/reset_supported_devices', type='http', auth='none', cors='*')
|
||||
def reset_supported_devices(self):
|
||||
try:
|
||||
os.remove('escpos_devices.pickle')
|
||||
except Exception as e:
|
||||
pass
|
||||
return 'The list of supported devices has been reset to factory defaults.<br/><a href="/hw_proxy/status">Ok</a>'
|
||||
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
__all__ = ["constants","escpos","exceptions","printer","supported_devices"]
|
||||
__all__ = ["constants","escpos","exceptions","printer"]
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
# This is a list of esc/pos compatible usb printers. The vendor and product ids can be found by
|
||||
# typing lsusb in a linux terminal, this will give you the ids in the form ID VENDOR:PRODUCT
|
||||
|
||||
device_list = [
|
||||
{ 'vendor' : 0x04b8, 'product' : 0x0e03, 'name' : 'Epson TM-T20' },
|
||||
{ 'vendor' : 0x04b8, 'product' : 0x0202, 'name' : 'Epson TM-T70' },
|
||||
{ 'vendor' : 0x04b8, 'product' : 0x0e15, 'name' : 'Epson TM-T20II' },
|
||||
]
|
||||
@@ -6,6 +6,7 @@ import time
|
||||
import openerp
|
||||
import openerp.addons.hw_proxy.controllers.main as hw_proxy
|
||||
import threading
|
||||
import subprocess
|
||||
from openerp import http
|
||||
from openerp.http import request
|
||||
from openerp.tools.translate import _
|
||||
@@ -74,6 +75,14 @@ upgrade_template = """
|
||||
<p>
|
||||
To upgrade the posbox, click on the upgrade button. The upgrade will take a few minutes. <b>Do not reboot</b> the PosBox during the upgrade.
|
||||
</p>
|
||||
<p>
|
||||
Latest patch:
|
||||
</p>
|
||||
<pre>
|
||||
"""
|
||||
upgrade_template += subprocess.check_output("git --work-tree=/home/pi/odoo/ --git-dir=/home/pi/odoo/.git log -1", shell=True).replace("\n", "<br/>")
|
||||
upgrade_template += """
|
||||
</pre>
|
||||
<div class='centering'>
|
||||
<a href='#' id='upgrade'>Upgrade</a>
|
||||
</div>
|
||||
@@ -95,7 +104,7 @@ class PosboxUpgrader(hw_proxy.Proxy):
|
||||
def perform_upgrade(self):
|
||||
self.upgrading.acquire()
|
||||
|
||||
os.system('/home/pi/posbox_update.sh')
|
||||
os.system('/home/pi/odoo/addons/point_of_sale/tools/posbox/configuration/posbox_update.sh')
|
||||
|
||||
self.upgrading.release()
|
||||
return 'SUCCESS'
|
||||
|
||||
@@ -427,8 +427,7 @@ class Partner(osv.osv):
|
||||
'fiscal_position': fpos_id or False
|
||||
}, context=context)
|
||||
line_value['invoice_id'] = invoice_id
|
||||
invoice_line_id = invoice_line_obj.create(cr, uid, line_value, context=context)
|
||||
invoice_obj.write(cr, uid, invoice_id, {'invoice_line': [(6, 0, [invoice_line_id])]}, context=context)
|
||||
invoice_line_obj.create(cr, uid, line_value, context=context)
|
||||
invoice_list.append(invoice_id)
|
||||
if line_value['invoice_line_tax_id']:
|
||||
tax_value = invoice_tax_obj.compute(cr, uid, invoice_id).values()
|
||||
@@ -456,7 +455,7 @@ class Product(osv.osv):
|
||||
view_id = dict_model['membership_products_form']
|
||||
else:
|
||||
view_id = dict_model['membership_products_tree']
|
||||
return super(Product,self).fields_view_get(cr, user, view_id, view_type, context, toolbar, submenu)
|
||||
return super(Product,self).fields_view_get(cr, user, view_id, view_type, context=context, toolbar=toolbar, submenu=submenu)
|
||||
|
||||
'''Product'''
|
||||
_inherit = 'product.template'
|
||||
|
||||
@@ -5,6 +5,6 @@
|
||||
# Call all S??* scripts in /etc/rcS.d/ in numerical/alphabetical order
|
||||
#
|
||||
|
||||
/etc/setup_ramdisks.sh
|
||||
/home/pi/odoo/addons/point_of_sale/tools/posbox/configuration/setup_ramdisks.sh
|
||||
|
||||
exec /etc/init.d/rc S
|
||||
|
||||
@@ -20,6 +20,6 @@ fi
|
||||
mkdir -p /var/run/odoo
|
||||
chown pi:pi /var/run/odoo
|
||||
|
||||
/home/pi/led_status.sh &
|
||||
/home/pi/odoo/addons/point_of_sale/tools/posbox/configuration/led_status.sh &
|
||||
|
||||
exit 0
|
||||
|
||||
@@ -17,7 +17,7 @@ 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
|
||||
CONFIG=/home/pi/odoo/addons/point_of_sale/tools/posbox/configuration/odoo.conf
|
||||
LOGFILE=/var/log/odoo/odoo-server.log
|
||||
PIDFILE=/var/run/${NAME}.pid
|
||||
USER=pi
|
||||
|
||||
@@ -12,7 +12,7 @@ __base="$(basename ${__file} .sh)"
|
||||
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"
|
||||
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 avahi-daemon"
|
||||
INSTALLED_PKGS_TO_DELETE=""
|
||||
set +o errexit
|
||||
for CURRENT_PKG in $(echo $PKGS_TO_DELETE); do
|
||||
@@ -56,6 +56,12 @@ sudo -u postgres createuser -s pi
|
||||
mkdir /var/log/odoo
|
||||
chown pi:pi /var/log/odoo
|
||||
|
||||
# logrotate is very picky when it comes to file permissions
|
||||
chown -R root:root /etc/logrotate.d/
|
||||
chmod -R 644 /etc/logrotate.d/
|
||||
chown root:root /etc/logrotate.conf
|
||||
chmod 644 /etc/logrotate.conf
|
||||
|
||||
echo "* * * * * rm /var/run/odoo/sessions/*" | crontab -
|
||||
|
||||
update-rc.d odoo defaults
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
/var/log/syslog
|
||||
{
|
||||
rotate 3
|
||||
daily
|
||||
missingok
|
||||
notifempty
|
||||
delaycompress
|
||||
compress
|
||||
postrotate
|
||||
invoke-rc.d rsyslog rotate > /dev/null
|
||||
endscript
|
||||
}
|
||||
|
||||
/var/log/mail.info
|
||||
/var/log/mail.warn
|
||||
/var/log/mail.err
|
||||
/var/log/mail.log
|
||||
/var/log/daemon.log
|
||||
/var/log/kern.log
|
||||
/var/log/auth.log
|
||||
/var/log/user.log
|
||||
/var/log/lpr.log
|
||||
/var/log/cron.log
|
||||
/var/log/debug
|
||||
/var/log/messages
|
||||
{
|
||||
rotate 3
|
||||
daily
|
||||
missingok
|
||||
notifempty
|
||||
compress
|
||||
delaycompress
|
||||
sharedscripts
|
||||
postrotate
|
||||
invoke-rc.d rsyslog rotate > /dev/null
|
||||
endscript
|
||||
}
|
||||
@@ -35,6 +35,7 @@ git config core.sparsecheckout true
|
||||
echo "addons/web
|
||||
addons/web_kanban
|
||||
addons/hw_*
|
||||
addons/point_of_sale/tools/posbox/configuration
|
||||
openerp/
|
||||
odoo.py" | tee --append .git/info/sparse-checkout > /dev/null
|
||||
git read-tree -mu HEAD
|
||||
|
||||
@@ -812,18 +812,24 @@ class product_template(osv.osv):
|
||||
if not name or any(term[0] == 'id' for term in (args or [])):
|
||||
return super(product_template, self).name_search(
|
||||
cr, user, name=name, args=args, operator=operator, context=context, limit=limit)
|
||||
|
||||
template_ids = set()
|
||||
product_product = self.pool['product.product']
|
||||
results = product_product.name_search(
|
||||
cr, user, name, args, operator=operator, context=context, limit=limit)
|
||||
results = product_product.name_search(cr, user, name, args, operator=operator, context=context, limit=limit)
|
||||
product_ids = [p[0] for p in results]
|
||||
template_ids = [p.product_tmpl_id.id
|
||||
for p in product_product.browse(
|
||||
cr, user, product_ids, context=context)]
|
||||
for p in product_product.browse(cr, user, product_ids, context=context):
|
||||
template_ids.add(p.product_tmpl_id.id)
|
||||
while (results and len(template_ids) < limit):
|
||||
domain = [('product_tmpl_id', 'not in', list(template_ids))]
|
||||
results = product_product.name_search(
|
||||
cr, user, name, args+domain, operator=operator, context=context, limit=limit)
|
||||
product_ids = [p[0] for p in results]
|
||||
for p in product_product.browse(cr, user, product_ids, context=context):
|
||||
template_ids.add(p.product_tmpl_id.id)
|
||||
|
||||
|
||||
# re-apply product.template order + name_get
|
||||
return super(product_template, self).name_search(
|
||||
cr, user, '', args=[('id', 'in', template_ids)],
|
||||
cr, user, '', args=[('id', 'in', list(template_ids))],
|
||||
operator='ilike', context=context, limit=limit)
|
||||
|
||||
class product_product(osv.osv):
|
||||
|
||||
@@ -905,7 +905,7 @@ class task(osv.osv):
|
||||
users_obj = self.pool.get('res.users')
|
||||
if context is None: context = {}
|
||||
|
||||
res = super(task, self).fields_view_get(cr, uid, view_id, view_type, context, toolbar, submenu=submenu)
|
||||
res = super(task, self).fields_view_get(cr, uid, view_id, view_type, context=context, toolbar=toolbar, submenu=submenu)
|
||||
|
||||
# read uom as admin to avoid access rights issues, e.g. for portal/share users,
|
||||
# this should be safe (no context passed to avoid side-effects)
|
||||
|
||||
@@ -91,7 +91,7 @@ class project_task_delegate(osv.osv_memory):
|
||||
}
|
||||
|
||||
def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
|
||||
res = super(project_task_delegate, self).fields_view_get(cr, uid, view_id, view_type, context, toolbar, submenu=submenu)
|
||||
res = super(project_task_delegate, self).fields_view_get(cr, uid, view_id, view_type, context=context, toolbar=toolbar, submenu=submenu)
|
||||
users_pool = self.pool.get('res.users')
|
||||
obj_tm = users_pool.browse(cr, uid, uid, context=context).company_id.project_time_mode_id
|
||||
tm = obj_tm and obj_tm.name or 'Hours'
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
<p style="border-left: 1px solid #8e0000; margin-left: 30px;">
|
||||
<strong>OUR REFERENCES</strong><br />
|
||||
RFQ number: <strong>${object.name}</strong><br />
|
||||
RFQ date: ${object.date_order}<br />
|
||||
RFQ date: ${format_tz(object.date_order, tz=user.tz, context={'lang':object.partner_id.lang})}<br />
|
||||
% if object.origin:
|
||||
RFQ reference: ${object.origin}<br />
|
||||
% endif
|
||||
@@ -112,7 +112,7 @@
|
||||
<strong>OUR REFERENCES</strong><br />
|
||||
Order number: <strong>${object.name}</strong><br />
|
||||
Order total: <strong>${object.amount_total} ${object.currency_id.name}</strong><br />
|
||||
Order date: ${object.date_order}<br />
|
||||
Order date: ${format_tz(object.date_order, tz=user.tz, context={'lang':object.partner_id.lang})}<br />
|
||||
% if object.origin:
|
||||
Order reference: ${object.origin}<br />
|
||||
% endif
|
||||
|
||||
@@ -38,6 +38,7 @@ class purchase_order(osv.osv):
|
||||
def _amount_all(self, cr, uid, ids, field_name, arg, context=None):
|
||||
res = {}
|
||||
cur_obj=self.pool.get('res.currency')
|
||||
line_obj = self.pool['purchase.order.line']
|
||||
for order in self.browse(cr, uid, ids, context=context):
|
||||
res[order.id] = {
|
||||
'amount_untaxed': 0.0,
|
||||
@@ -47,10 +48,15 @@ class purchase_order(osv.osv):
|
||||
val = val1 = 0.0
|
||||
cur = order.pricelist_id.currency_id
|
||||
for line in order.order_line:
|
||||
taxes = self.pool.get('account.tax').compute_all(cr, uid, line.taxes_id, line.price_unit, line.product_qty, line.product_id, order.partner_id)
|
||||
val1 += cur_obj.round(cr, uid, cur, taxes['total']) #Decimal precision?
|
||||
for c in taxes['taxes']:
|
||||
val += c.get('amount', 0.0)
|
||||
line_price = line_obj._calc_line_base_price(cr, uid, line,
|
||||
context=context)
|
||||
line_qty = line_obj._calc_line_quantity(cr, uid, line,
|
||||
context=context)
|
||||
taxes = self.pool['account.tax'].compute_all(cr, uid, line.taxes_id, line_price, line_qty,
|
||||
line.product_id, order.partner_id)
|
||||
val1 += cur_obj.round(cr, uid, cur, taxes['total']) #Decimal precision?
|
||||
for c in taxes['taxes']:
|
||||
val += c.get('amount', 0.0)
|
||||
|
||||
res[order.id]['amount_tax']=cur_obj.round(cr, uid, cur, val)
|
||||
res[order.id]['amount_untaxed']=cur_obj.round(cr, uid, cur, val1)
|
||||
@@ -1014,12 +1020,34 @@ class purchase_order(osv.osv):
|
||||
|
||||
|
||||
class purchase_order_line(osv.osv):
|
||||
def _calc_line_base_price(self, cr, uid, line, context=None):
|
||||
"""Return the base price of the line to be used for tax calculation.
|
||||
|
||||
This function can be extended by other modules to modify this base
|
||||
price (adding a discount, for example).
|
||||
"""
|
||||
return line.price_unit
|
||||
|
||||
def _calc_line_quantity(self, cr, uid, line, context=None):
|
||||
"""Return the base quantity of the line to be used for the subtotal.
|
||||
|
||||
This function can be extended by other modules to modify this base
|
||||
quantity (adding for example offers 3x2 and so on).
|
||||
"""
|
||||
return line.product_qty
|
||||
|
||||
def _amount_line(self, cr, uid, ids, prop, arg, context=None):
|
||||
res = {}
|
||||
cur_obj=self.pool.get('res.currency')
|
||||
tax_obj = self.pool.get('account.tax')
|
||||
for line in self.browse(cr, uid, ids, context=context):
|
||||
taxes = tax_obj.compute_all(cr, uid, line.taxes_id, line.price_unit, line.product_qty, line.product_id, line.order_id.partner_id)
|
||||
line_price = self._calc_line_base_price(cr, uid, line,
|
||||
context=context)
|
||||
line_qty = self._calc_line_quantity(cr, uid, line,
|
||||
context=context)
|
||||
taxes = tax_obj.compute_all(cr, uid, line.taxes_id, line_price,
|
||||
line_qty, line.product_id,
|
||||
line.order_id.partner_id)
|
||||
cur = line.order_id.pricelist_id.currency_id
|
||||
res[line.id] = cur_obj.round(cr, uid, cur, taxes['total'])
|
||||
return res
|
||||
|
||||
+17
-3
@@ -48,7 +48,12 @@ class sale_order(osv.osv):
|
||||
|
||||
def _amount_line_tax(self, cr, uid, line, context=None):
|
||||
val = 0.0
|
||||
for c in self.pool.get('account.tax').compute_all(cr, uid, line.tax_id, line.price_unit * (1-(line.discount or 0.0)/100.0), line.product_uom_qty, line.product_id, line.order_id.partner_id)['taxes']:
|
||||
line_obj = self.pool['sale.order.line']
|
||||
price = line_obj._calc_line_base_price(cr, uid, line, context=context)
|
||||
qty = line_obj._calc_line_quantity(cr, uid, line, context=context)
|
||||
for c in self.pool['account.tax'].compute_all(
|
||||
cr, uid, line.tax_id, price, qty, line.product_id,
|
||||
line.order_id.partner_id)['taxes']:
|
||||
val += c.get('amount', 0.0)
|
||||
return val
|
||||
|
||||
@@ -827,6 +832,12 @@ class sale_order_line(osv.osv):
|
||||
return True
|
||||
return False
|
||||
|
||||
def _calc_line_base_price(self, cr, uid, line, context=None):
|
||||
return line.price_unit * (1 - (line.discount or 0.0) / 100.0)
|
||||
|
||||
def _calc_line_quantity(self, cr, uid, line, context=None):
|
||||
return line.product_uom_qty
|
||||
|
||||
def _amount_line(self, cr, uid, ids, field_name, arg, context=None):
|
||||
tax_obj = self.pool.get('account.tax')
|
||||
cur_obj = self.pool.get('res.currency')
|
||||
@@ -834,8 +845,11 @@ class sale_order_line(osv.osv):
|
||||
if context is None:
|
||||
context = {}
|
||||
for line in self.browse(cr, uid, ids, context=context):
|
||||
price = line.price_unit * (1 - (line.discount or 0.0) / 100.0)
|
||||
taxes = tax_obj.compute_all(cr, uid, line.tax_id, price, line.product_uom_qty, line.product_id, line.order_id.partner_id)
|
||||
price = self._calc_line_base_price(cr, uid, line, context=context)
|
||||
qty = self._calc_line_quantity(cr, uid, line, context=context)
|
||||
taxes = tax_obj.compute_all(cr, uid, line.tax_id, price, qty,
|
||||
line.product_id,
|
||||
line.order_id.partner_id)
|
||||
cur = line.order_id.pricelist_id.currency_id
|
||||
res[line.id] = cur_obj.round(cr, uid, cur, taxes['total'])
|
||||
return res
|
||||
|
||||
@@ -2967,6 +2967,10 @@ instance.web.form.FieldTextHtml = instance.web.form.AbstractField.extend(instanc
|
||||
}
|
||||
}
|
||||
},
|
||||
focus: function() {
|
||||
var input = !this.get("effective_readonly") && this.$cleditor
|
||||
return input ? input.focus() : false;
|
||||
},
|
||||
render_value: function() {
|
||||
if (! this.get("effective_readonly")) {
|
||||
this.$textarea.val(this.get('value') || '');
|
||||
|
||||
@@ -301,7 +301,7 @@
|
||||
var $cell = $(cell);
|
||||
|
||||
field.set_dimensions($cell.outerHeight(), $cell.outerWidth());
|
||||
field.$el.position({
|
||||
field.$el.css({top: 0, left: 0}).position({
|
||||
my: 'left top',
|
||||
at: 'left top',
|
||||
of: $cell
|
||||
|
||||
@@ -1312,6 +1312,17 @@ instance.web_kanban.AbstractField = instance.web.Widget.extend(instance.web_kanb
|
||||
},
|
||||
});
|
||||
|
||||
instance.web_kanban.FormatChar = instance.web_kanban.AbstractField.extend({
|
||||
tagName: 'span',
|
||||
init: function(parent, field, $node) {
|
||||
this._super.apply(this, arguments);
|
||||
this.format_descriptor = _.extend({}, this.field, {'widget': this.$node.attr('widget')});
|
||||
},
|
||||
renderElement: function() {
|
||||
this.$el.text(instance.web.format_value(this.field.raw_value, this.format_descriptor));
|
||||
}
|
||||
});
|
||||
|
||||
instance.web_kanban.Priority = instance.web_kanban.AbstractField.extend({
|
||||
init: function(parent, field, $node) {
|
||||
this._super.apply(this, arguments);
|
||||
@@ -1413,4 +1424,5 @@ instance.web_kanban.KanbanSelection = instance.web_kanban.AbstractField.extend({
|
||||
instance.web_kanban.fields_registry = new instance.web.Registry({});
|
||||
instance.web_kanban.fields_registry.add('priority','instance.web_kanban.Priority');
|
||||
instance.web_kanban.fields_registry.add('kanban_state_selection','instance.web_kanban.KanbanSelection');
|
||||
instance.web_kanban.fields_registry.add('float_time','instance.web_kanban.FormatChar');
|
||||
};
|
||||
|
||||
@@ -344,7 +344,12 @@ class Image(orm.AbstractModel):
|
||||
if options.get('resize'):
|
||||
src = "%s/%s" % (src, options.get('resize'))
|
||||
|
||||
img = '<img class="%s" src="%s" style="%s"/>' % (classes, src, options.get('style', ''))
|
||||
alt = None
|
||||
if options.get('alt-field') and getattr(record, options['alt-field'], None):
|
||||
alt = record[options['alt-field']]
|
||||
elif options.get('alt'):
|
||||
alt = options['alt']
|
||||
img = '<img class="%s" src="%s" style="%s"%s/>' % (classes, src, options.get('style', ''), ' alt="%s"' % alt if alt else '')
|
||||
return ir_qweb.HTMLSafe(img)
|
||||
|
||||
local_url_re = re.compile(r'^/(?P<module>[^]]+)/static/(?P<rest>.+)$')
|
||||
|
||||
@@ -8,7 +8,6 @@ from openerp import SUPERUSER_ID, api, tools
|
||||
from openerp.addons.website.models import website
|
||||
from openerp.http import request
|
||||
from openerp.osv import osv, fields
|
||||
from openerp.tools import html_escape
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -144,14 +143,6 @@ class view(osv.osv):
|
||||
# ensure there's only one match
|
||||
[root] = arch.xpath(section_xpath)
|
||||
|
||||
# html text need to be escaped for xml storage
|
||||
def escape_node(node):
|
||||
node.text = node.text and html_escape(node.text)
|
||||
node.tail = node.tail and html_escape(node.tail)
|
||||
escape_node(replacement)
|
||||
for descendant in replacement.iterdescendants():
|
||||
escape_node(descendant)
|
||||
|
||||
root.text = replacement.text
|
||||
root.tail = replacement.tail
|
||||
# replace all children
|
||||
|
||||
@@ -1119,7 +1119,14 @@ define(['summernote/summernote'], function () {
|
||||
return this._save();
|
||||
},
|
||||
saveElement: function ($el) {
|
||||
var markup = $el.prop('outerHTML');
|
||||
// escape text nodes for xml saving
|
||||
var escaped_el = $el.clone();
|
||||
escaped_el.find('*').addBack().not('script,style').contents().each(function(){
|
||||
if(this.nodeType == 3) {
|
||||
this.nodeValue = _.escape(this.nodeValue);
|
||||
}
|
||||
});
|
||||
var markup = escaped_el.prop('outerHTML');
|
||||
return openerp.jsonRpc('/web/dataset/call', 'call', {
|
||||
model: 'ir.ui.view',
|
||||
method: 'save',
|
||||
|
||||
@@ -175,14 +175,34 @@ class TestViewSaving(common.TransactionCase):
|
||||
)
|
||||
|
||||
def test_save_escaped_text(self):
|
||||
""" Test saving html special chars in text nodes """
|
||||
view_id = self.registry('ir.ui.view').create(self.cr, self.uid, {
|
||||
'arch':'<t>hello world</t>',
|
||||
'arch':'<t><p><h1>hello world</h1></p></t>',
|
||||
'type':'qweb'
|
||||
})
|
||||
view = self.registry('ir.ui.view').browse(self.cr, self.uid, view_id)
|
||||
replacement = 'hello world & <angle brackets>!'
|
||||
view.save(replacement, xpath='/t')
|
||||
self.assertEqual(view.render(), replacement, 'html special characters wrongly escaped')
|
||||
# script and style text nodes should not escaped client side
|
||||
replacement = '<script>1 && "hello & world"</script>'
|
||||
view.save(replacement, xpath='/t/p/h1')
|
||||
self.assertIn(
|
||||
replacement.replace('&', '&'),
|
||||
view.arch,
|
||||
'inline script should be escaped server side'
|
||||
)
|
||||
self.assertIn(
|
||||
replacement,
|
||||
view.render(),
|
||||
'inline script should not be escaped when rendering'
|
||||
)
|
||||
# common text nodes should be be escaped client side
|
||||
replacement = 'world &amp; &lt;b&gt;cie'
|
||||
view.save(replacement, xpath='/t/p')
|
||||
self.assertIn(replacement, view.arch, 'common text node should not be escaped server side')
|
||||
self.assertIn(
|
||||
replacement,
|
||||
view.render().replace('&', '&'),
|
||||
'text node characters wrongly unescaped when rendering'
|
||||
)
|
||||
|
||||
def test_save_only_embedded(self):
|
||||
Company = self.registry('res.company')
|
||||
|
||||
@@ -77,7 +77,7 @@
|
||||
</div>
|
||||
<div class="oe_product_image">
|
||||
<a itemprop="url" t-att-href="keep('/shop/product/%s' % slug(product), page=(pager['page']['num'] if pager['page']['num']>1 else None))">
|
||||
<span itemprop="image" t-field="product.image" t-field-options='{"widget": "image" #{None if product_image_big else ", \"resize\": \"300x300\""}}'/>
|
||||
<span itemprop="image" t-field="product.image" t-field-options='{"widget": "image" #{None if product_image_big else ", \"resize\": \"300x300\""}}' t-att-alt="product.name"/>
|
||||
</a>
|
||||
</div>
|
||||
<section>
|
||||
@@ -309,7 +309,7 @@
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-7 col-md-7 col-lg-7">
|
||||
<span itemprop="image" t-field="product.image" t-field-options='{"widget": "image", "class": "product_detail_img"}'/>
|
||||
<span itemprop="image" t-field="product.image" t-field-options='{"widget": "image", "class": "product_detail_img", "alt-field": "name"}'/>
|
||||
</div><div class="col-sm-5 col-md-5 col-lg-4 col-lg-offset-1">
|
||||
<h1 itemprop="name" t-field="product.name">Product Name</h1>
|
||||
<span itemprop="url" style="display:none;" t-esc="'/shop/product/%s' % slug(product)"/>
|
||||
|
||||
@@ -29,6 +29,7 @@ from openerp.osv import fields, osv
|
||||
from openerp.tools import float_round, float_is_zero, float_compare
|
||||
from openerp.tools.translate import _
|
||||
from openerp.exceptions import UserError
|
||||
import simplejson as json
|
||||
|
||||
CURRENCY_DISPLAY_PATTERN = re.compile(r'(\w+)\s*(?:\((.*)\))?')
|
||||
|
||||
@@ -292,9 +293,9 @@ class res_currency(osv.osv):
|
||||
|
||||
format_number_str = "openerp.web.format_value(arguments[0], {type: 'float', digits: [69," + str(digits) + "]}, 0.00)"
|
||||
if row['position'] == 'after':
|
||||
return_str = "return " + format_number_str + " + '\\xA0" + symbol + "';"
|
||||
return_str = "return " + format_number_str + " + '\\xA0' + " + json.dumps(symbol) + ";"
|
||||
else:
|
||||
return_str = "return '" + symbol + "\\xA0' + " + format_number_str + ";"
|
||||
return_str = "return " + json.dumps(symbol) + " + '\\xA0' + " + format_number_str + ";"
|
||||
function += "if (arguments[1] === " + str(row['id']) + ") { " + return_str + " }"
|
||||
return function
|
||||
|
||||
|
||||
Reference in New Issue
Block a user