From 7e1f2eb91e2ed829fe466ade7fda877de551bad0 Mon Sep 17 00:00:00 2001
From: Christophe Matthieu
Date: Mon, 27 Jun 2016 09:01:50 +0200
Subject: [PATCH 1/9] [IMP] ir.qweb: format monetary use field monetary method
(facilitate code maintenance)
---
openerp/addons/base/ir/ir_qweb/fields.py | 36 ++++++++++-------------
openerp/addons/base/ir/ir_qweb/ir_qweb.py | 18 +++---------
2 files changed, 20 insertions(+), 34 deletions(-)
diff --git a/openerp/addons/base/ir/ir_qweb/fields.py b/openerp/addons/base/ir/ir_qweb/fields.py
index c0feaa27aa8..b82945ca155 100644
--- a/openerp/addons/base/ir/ir_qweb/fields.py
+++ b/openerp/addons/base/ir/ir_qweb/fields.py
@@ -298,8 +298,6 @@ class MonetaryConverter(models.AbstractModel):
@api.model
def record_to_html(self, record, field_name, options, values=None):
- field = record._fields[field_name]
-
# for backward compatibility to remove after v10
if isinstance(options.get('display_currency'), str):
_logger.warning("Use new syntax for '%s' monetary widget 'display_currency' option (instead of deprecated JSON syntax) to improve speed." % options['display_currency'])
@@ -309,14 +307,17 @@ class MonetaryConverter(models.AbstractModel):
options['from_currency'] = safe_eval.safe_eval(options['from_currency'], mode='eval', globals_dict=values)
# end backward
- display_currency = False
- #currency should be specified by monetary field
- if field.type == 'monetary' and field.currency_field:
- display_currency = record[field.currency_field]
- #otherwise fall back to old method
- if not display_currency:
- display_currency = options['display_currency']
+ field = record._fields[field_name]
+ from_amount = record[field_name]
+ #currency should be specified by monetary field
+ if not options.get('display_currency') and field.type == 'monetary' and field.currency_field:
+ options['display_currency'] = record[field.currency_field]
+
+ pre, value, post = self._format_func_monetary(from_amount, options['display_currency'], options.get('from_currency'))
+ return u'{pre}{0} {post}'.format(value, pre=pre, post=post)
+
+ def _format_func_monetary(self, from_amount, display_currency, from_currency=None):
# lang.format mandates a sprintf-style format. These formats are non-
# minimal (they have a default fixed precision instead), and
# lang.format will not set one by default. currency.round will not
@@ -324,10 +325,8 @@ class MonetaryConverter(models.AbstractModel):
# (integer > 0) from the currency's rounding (a float generally < 1.0).
fmt = "%.{0}f".format(display_currency.decimal_places)
- from_amount = record[field_name]
-
- if options.get('from_currency'):
- from_amount = options['from_currency'].compute(from_amount, display_currency)
+ if from_currency:
+ from_amount = from_currency.compute(from_amount, display_currency)
lang = self.user_lang()
formatted_amount = lang.format(fmt, display_currency.round(from_amount),
@@ -335,15 +334,12 @@ class MonetaryConverter(models.AbstractModel):
pre = post = u''
if display_currency.position == 'before':
- pre = u'{symbol}\N{NO-BREAK SPACE}'
+ pre = u'{symbol}\N{NO-BREAK SPACE}'.format(symbol=display_currency.symbol)
else:
- post = u'\N{NO-BREAK SPACE}{symbol}'
+ post = u'\N{NO-BREAK SPACE}{symbol}'.format(symbol=display_currency.symbol)
+
+ return pre, formatted_amount, post
- return u'{pre}{0} {post}'.format(
- formatted_amount, pre=pre, post=post,
- ).format(
- symbol=display_currency.symbol,
- )
TIMEDELTA_UNITS = (
('year', 3600 * 24 * 365),
diff --git a/openerp/addons/base/ir/ir_qweb/ir_qweb.py b/openerp/addons/base/ir/ir_qweb/ir_qweb.py
index 166858edd2c..f57b6fbd437 100644
--- a/openerp/addons/base/ir/ir_qweb/ir_qweb.py
+++ b/openerp/addons/base/ir/ir_qweb/ir_qweb.py
@@ -237,20 +237,10 @@ class IrQWeb(models.AbstractModel, QWeb):
return (attributes, content, inherit_branding or translate)
# formating methods
- def _format_func_monetary(self, value, display_currency=None, from_currency=None):
- env = display_currency.env
- precision = int(round(math.log10(display_currency.rounding)))
- fmt = "%.{0}f".format(-precision if precision < 0 else 0)
- lang = env['res.lang']._lang_get(env.context.get('lang', 'en_US'))
- formatted_amount = lang.format(fmt, value, grouping=True, monetary=True).replace(r' ', u'\N{NO-BREAK SPACE}')
- pre = post = u''
- if display_currency.position == 'before':
- pre = u'{symbol}\N{NO-BREAK SPACE}'
- else:
- post = u'\N{NO-BREAK SPACE}{symbol}'
- return u'{pre}{0}{post}'.format(
- formatted_amount, pre=pre, post=post
- ).format(symbol=display_currency.symbol,)
+
+ def _format_func_monetary(self, value, display_currency, from_currency=None):
+ pre, value, post = self.env['ir.qweb.field.monetary']._format_func_monetary(value, display_currency, from_currency)
+ return u'{pre}{0}{post}'.format(value, pre=pre, post=post)
def _format_func_htmlcontact(self, value, options, values=None):
return self.pool['ir.qweb.field.contact'].record_to_html(
From 3407fb70e4a5b16174234d10d97710572b6f808c Mon Sep 17 00:00:00 2001
From: Christophe Matthieu
Date: Thu, 30 Jun 2016 16:16:40 +0200
Subject: [PATCH 2/9] [IMP] qweb: add t-options on t-esc and t-raw who use
'ir.qweb.field' models
Remove 'field' from 'value_to_html' method attributes
't-esc' escape the result of the widget.
(Added 'remove_span' as option for monetary widget)
E.g.:
result: $ 365.00
---
.../tests/test_qweb_float.py | 19 +-
addons/report/models/ir_qweb.py | 2 +-
openerp/addons/base/ir/ir_qweb/fields.py | 148 +++++++-------
openerp/addons/base/ir/ir_qweb/ir_qweb.py | 55 ++++-
openerp/addons/base/ir/ir_qweb/qweb.py | 192 ++++++++++++------
.../addons/test_converter/tests/test_html.py | 47 ++---
6 files changed, 284 insertions(+), 179 deletions(-)
diff --git a/addons/decimal_precision/tests/test_qweb_float.py b/addons/decimal_precision/tests/test_qweb_float.py
index c481dba2fd9..e598f00d8ec 100644
--- a/addons/decimal_precision/tests/test_qweb_float.py
+++ b/addons/decimal_precision/tests/test_qweb_float.py
@@ -7,11 +7,14 @@ class TestFloatExport(common.TransactionCase):
self.Model = self.registry('decimal.precision.test')
def get_converter(self, name):
- converter = self.registry('ir.qweb.field.float')
- field = self.Model._fields[name]
+ float_obj = self.registry('ir.qweb.field.float')
+ _, precision = self.Model._fields[name].digits or (None, None)
- return lambda value, options=None: converter.value_to_html(
- self.cr, self.uid, value, field, options=options, context=None)
+ def converter(value, options=None):
+ record = self.Model.new(self.cr, self.uid, {name: value}, context=None)
+ return float_obj.record_to_html(
+ self.cr, self.uid, record, name, options or {}, context=None)
+ return converter
def test_basic_float(self):
converter = self.get_converter('float')
@@ -30,12 +33,12 @@ class TestFloatExport(common.TransactionCase):
converter(42.12345),
"42.12")
- converter = self.get_converter('float_4')
+ converter = self.get_converter('float') # don't use float_4 because the field value 42.12345 is orm converted to 42.1235
self.assertEqual(
- converter(42.0),
+ converter(42.0, {'precision': 4}),
'42.0000')
self.assertEqual(
- converter(42.12345),
+ converter(42.12345, {'precision': 4}),
'42.1234')
def test_precision_domain(self):
@@ -57,7 +60,7 @@ class TestFloatExport(common.TransactionCase):
converter(42.0, {'decimal_precision': 'B'}),
'42.000000')
- converter = self.get_converter('float_4')
+ converter = self.get_converter('float') # don't use float_4 because the field value 42.12345 is orm converted to 42.1235
self.assertEqual(
converter(42.12345, {'decimal_precision': 'A'}),
'42.12')
diff --git a/addons/report/models/ir_qweb.py b/addons/report/models/ir_qweb.py
index 68e9673d902..3e8dc558c08 100644
--- a/addons/report/models/ir_qweb.py
+++ b/addons/report/models/ir_qweb.py
@@ -12,7 +12,7 @@ class BarcodeConverter(osv.AbstractModel):
_name = 'ir.qweb.field.barcode'
_inherit = 'ir.qweb.field'
- def value_to_html(self, cr, uid, value, field, options=None, context=None):
+ def value_to_html(self, cr, uid, value, options, context=None):
options = options or {}
barcode_type = options.get('type', 'Code128')
barcode = self.pool['report'].barcode(
diff --git a/openerp/addons/base/ir/ir_qweb/fields.py b/openerp/addons/base/ir/ir_qweb/fields.py
index b82945ca155..f520bd25c9a 100644
--- a/openerp/addons/base/ir/ir_qweb/fields.py
+++ b/openerp/addons/base/ir/ir_qweb/fields.py
@@ -82,7 +82,7 @@ class FieldConverter(models.AbstractModel):
return data
@api.model
- def value_to_html(self, value, field, options=None):
+ def value_to_html(self, value, options):
""" value_to_html(value, field, options=None)
Converts a single value to its HTML version/output
@@ -97,15 +97,8 @@ class FieldConverter(models.AbstractModel):
Converts the specified field of the browse_record ``record`` to HTML
:rtype: unicode
"""
- field = record._fields[field_name]
-
- try:
- value = record[field_name]
- return False if value is False else record.env[self._name].value_to_html(value, field, options=options)
- except Exception:
- _logger.warning("Could not get field %s for model %s",
- field_name, record._name, exc_info=True)
- return None
+ value = record[field_name]
+ return False if value is False else record.env[self._name].value_to_html(value, options=options)
@api.model
def user_lang(self):
@@ -126,7 +119,7 @@ class IntegerConverter(models.AbstractModel):
_inherit = 'ir.qweb.field'
@api.model
- def value_to_html(self, value, field, options=None):
+ def value_to_html(self, value, options):
return unicodifier(self.user_lang().format('%d', value, grouping=True))
@@ -135,15 +128,15 @@ class FloatConverter(models.AbstractModel):
_inherit = 'ir.qweb.field'
@api.model
- def precision(self, field, options=None):
- _, precision = field.digits or (None, None)
- return precision
+ def value_to_html(self, value, options):
+ if 'decimal_precision' in options:
+ precision = self.env['decimal.precision'].search([('name', '=', options['decimal_precision'])]).digits
+ else:
+ precision = options['precision']
- @api.model
- def value_to_html(self, value, field, options=None):
- precision = self.precision(field, options=options)
fmt = '%f' if precision is None else '%.{precision}f'
lang = self.user_lang()
+
formatted = lang.format(fmt.format(precision=precision), value, grouping=True)
# %f does not strip trailing zeroes. %g does but its precision causes
@@ -152,15 +145,23 @@ class FloatConverter(models.AbstractModel):
# strip trailing 0.
if precision is None:
formatted = re.sub(r'(?:(0|\d+?)0+)$', r'\1', formatted)
+
return unicodifier(formatted)
+ @api.model
+ def record_to_html(self, record, field_name, options, values=None):
+ if 'precision' not in options and 'decimal_precision' not in options:
+ _, precision = record._fields[field_name].digits or (None, None)
+ options = dict(options, precision=precision)
+ return super(FloatConverter, self).record_to_html(record, field_name, options, values=values)
+
class DateConverter(models.AbstractModel):
_name = 'ir.qweb.field.date'
_inherit = 'ir.qweb.field'
@api.model
- def value_to_html(self, value, field, options=None):
+ def value_to_html(self, value, options):
if not value or len(value) < 10:
return ''
lang = self.user_lang()
@@ -183,7 +184,7 @@ class DateTimeConverter(models.AbstractModel):
_inherit = 'ir.qweb.field'
@api.model
- def value_to_html(self, value, field, options=None):
+ def value_to_html(self, value, options):
if not value:
return ''
lang = self.user_lang()
@@ -211,7 +212,7 @@ class TextConverter(models.AbstractModel):
_inherit = 'ir.qweb.field'
@api.model
- def value_to_html(self, value, field, options=None):
+ def value_to_html(self, value, options):
"""
Escapes the value and converts newlines to br. This is bullshit.
"""
@@ -223,11 +224,16 @@ class SelectionConverter(models.AbstractModel):
_inherit = 'ir.qweb.field'
@api.model
- def value_to_html(self, value, field, options=None):
+ def value_to_html(self, value, options):
if not value:
return ''
- selection = dict(field.get_description(self.env)['selection'])
- return html_escape(unicodifier(selection[value]) or u'', options)
+ return html_escape(unicodifier(options['selection'][value]) or u'', options)
+
+ @api.model
+ def record_to_html(self, record, field_name, options, values=None):
+ if 'selection' not in options:
+ options = dict(options, selection=dict(record._fields[field_name].get_description(self.env)['selection']))
+ return super(SelectionConverter, self).record_to_html(record, field_name, options, values=values)
class ManyToOneConverter(models.AbstractModel):
@@ -235,8 +241,7 @@ class ManyToOneConverter(models.AbstractModel):
_inherit = 'ir.qweb.field'
@api.model
- def record_to_html(self, record, field_name, options, values=None):
- value = record[field_name]
+ def value_to_html(self, value, options):
if not value:
return False
value = value.sudo().display_name
@@ -250,7 +255,7 @@ class HTMLConverter(models.AbstractModel):
_inherit = 'ir.qweb.field'
@api.model
- def value_to_html(self, value, field, options=None):
+ def value_to_html(self, value, options):
return unicodifier(value) or u''
@@ -267,7 +272,7 @@ class ImageConverter(models.AbstractModel):
_inherit = 'ir.qweb.field'
@api.model
- def value_to_html(self, value, field, options=None):
+ def value_to_html(self, value, options):
try:
image = Image.open(StringIO(value.decode('base64')))
image.verify()
@@ -297,27 +302,9 @@ class MonetaryConverter(models.AbstractModel):
_inherit = 'ir.qweb.field'
@api.model
- def record_to_html(self, record, field_name, options, values=None):
- # for backward compatibility to remove after v10
- if isinstance(options.get('display_currency'), str):
- _logger.warning("Use new syntax for '%s' monetary widget 'display_currency' option (instead of deprecated JSON syntax) to improve speed." % options['display_currency'])
- options['display_currency'] = safe_eval.safe_eval(options['display_currency'], mode='eval', globals_dict=values)
- if isinstance(options.get('from_currency'), str):
- _logger.warning("Use new syntax for '%s' monetary widget 'from_currency' option (instead of deprecated JSON syntax) to improve speed." % options['from_currency'])
- options['from_currency'] = safe_eval.safe_eval(options['from_currency'], mode='eval', globals_dict=values)
- # end backward
+ def value_to_html(self, value, options):
+ display_currency = options['display_currency']
- field = record._fields[field_name]
- from_amount = record[field_name]
-
- #currency should be specified by monetary field
- if not options.get('display_currency') and field.type == 'monetary' and field.currency_field:
- options['display_currency'] = record[field.currency_field]
-
- pre, value, post = self._format_func_monetary(from_amount, options['display_currency'], options.get('from_currency'))
- return u'{pre}{0} {post}'.format(value, pre=pre, post=post)
-
- def _format_func_monetary(self, from_amount, display_currency, from_currency=None):
# lang.format mandates a sprintf-style format. These formats are non-
# minimal (they have a default fixed precision instead), and
# lang.format will not set one by default. currency.round will not
@@ -325,11 +312,11 @@ class MonetaryConverter(models.AbstractModel):
# (integer > 0) from the currency's rounding (a float generally < 1.0).
fmt = "%.{0}f".format(display_currency.decimal_places)
- if from_currency:
- from_amount = from_currency.compute(from_amount, display_currency)
+ if options.get('from_currency'):
+ value = options['from_currency'].compute(value, display_currency)
lang = self.user_lang()
- formatted_amount = lang.format(fmt, display_currency.round(from_amount),
+ formatted_amount = lang.format(fmt, display_currency.round(value),
grouping=True, monetary=True).replace(r' ', u'\N{NO-BREAK SPACE}')
pre = post = u''
@@ -338,8 +325,17 @@ class MonetaryConverter(models.AbstractModel):
else:
post = u'\N{NO-BREAK SPACE}{symbol}'.format(symbol=display_currency.symbol)
- return pre, formatted_amount, post
+ return u'{pre}{0} {post}'.format(formatted_amount, pre=pre, post=post)
+ @api.model
+ def record_to_html(self, record, field_name, options, values=None):
+ options = dict(options)
+ #currency should be specified by monetary field
+ field = record._fields[field_name]
+ if not options.get('display_currency') and field.type == 'monetary' and field.currency_field:
+ options['display_currency'] = record[field.currency_field]
+
+ return self.value_to_html(record[field_name], options)
TIMEDELTA_UNITS = (
('year', 3600 * 24 * 365),
@@ -368,10 +364,12 @@ class DurationConverter(models.AbstractModel):
_inherit = 'ir.qweb.field'
@api.model
- def value_to_html(self, value, field, options=None):
+ def value_to_html(self, value, options):
units = dict(TIMEDELTA_UNITS)
+
if value < 0:
raise ValueError(_("Durations can't be negative"))
+
if not options or options.get('unit') not in units:
raise ValueError(_("A unit must be provided to duration widgets"))
@@ -396,48 +394,52 @@ class RelativeDatetimeConverter(models.AbstractModel):
_inherit = 'ir.qweb.field'
@api.model
- def value_to_html(self, value, field, options=None):
+ def value_to_html(self, value, options):
locale = babel.Locale.parse(self.user_lang().code)
if isinstance(value, basestring):
value = fields.Datetime.from_string(value)
# value should be a naive datetime in UTC. So is fields.Datetime.now()
- reference = fields.Datetime.from_string(field.now())
+ reference = fields.Datetime.from_string(options['now'])
return unicodifier(babel.dates.format_timedelta(value - reference, add_direction=True, locale=locale))
+ @api.model
+ def record_to_html(self, record, field_name, options, values=None):
+ if 'now' not in options:
+ options = dict(options, now=record._fields[field_name].now())
+ return super(RelativeDatetimeConverter, self).record_to_html(record, field_name, options, values=values)
+
class Contact(models.AbstractModel):
_name = 'ir.qweb.field.contact'
_inherit = 'ir.qweb.field.many2one'
@api.model
- def record_to_html(self, record, field_name, options, values=None):
- opf = options and options.get('fields') or ["name", "address", "phone", "mobile", "fax", "email"]
+ def value_to_html(self, value, options):
+ if not value.exists():
+ return False
- value_rec = record[field_name]
- if not value_rec:
- return None
- value_rec = value_rec.sudo().with_context(show_address=True)
- value = value_rec.name_get()[0][1]
+ opf = options and options.get('fields') or ["name", "address", "phone", "mobile", "fax", "email"]
+ value = value.sudo().with_context(show_address=True)
+ name_get = value.name_get()[0][1]
val = {
- 'name': value.split("\n")[0],
- 'address': escape("\n".join(value.split("\n")[1:])).strip(),
- 'phone': value_rec.phone,
- 'mobile': value_rec.mobile,
- 'fax': value_rec.fax,
- 'city': value_rec.city,
- 'country_id': value_rec.country_id.display_name,
- 'website': value_rec.website,
- 'email': value_rec.email,
+ 'name': name_get.split("\n")[0],
+ 'address': escape("\n".join(name_get.split("\n")[1:])).strip(),
+ 'phone': value.phone,
+ 'mobile': value.mobile,
+ 'fax': value.fax,
+ 'city': value.city,
+ 'country_id': value.country_id.display_name,
+ 'website': value.website,
+ 'email': value.email,
'fields': opf,
- 'object': value_rec,
+ 'object': value,
'options': options
}
-
- return unicodifier(self.env.ref('base.contact').render(val, engine='ir.qweb'))
+ return self.env['ir.qweb'].render('base.contact', val)
class QwebView(models.AbstractModel):
diff --git a/openerp/addons/base/ir/ir_qweb/ir_qweb.py b/openerp/addons/base/ir/ir_qweb/ir_qweb.py
index f57b6fbd437..a077a54b614 100644
--- a/openerp/addons/base/ir/ir_qweb/ir_qweb.py
+++ b/openerp/addons/base/ir/ir_qweb/ir_qweb.py
@@ -2,11 +2,11 @@
import ast
from urlparse import urlparse
from lxml import html
-import math
from .qweb import QWeb, Contextifier
from .assetsbundle import AssetsBundle
from lxml import etree
+from collections import OrderedDict
from openerp import api, models, tools
from openerp.tools import safe_eval
@@ -144,6 +144,34 @@ class IrQWeb(models.AbstractModel, QWeb):
))
]
+ # for backward compatibility to remove after v10
+ def _compile_widget_options(self, el, directive_type):
+ field_options = super(IrQWeb, self)._compile_widget_options(el, directive_type)
+
+ if ('t-%s-options' % directive_type) in el.attrib:
+ if tools.config['dev_mode']:
+ _logger.warning("Use new syntax t-options instead of t-%s-options" % directive_type)
+ if not field_options:
+ field_options = el.attrib.pop('t-%s-options' % directive_type)
+
+ if field_options and 'monetary' in field_options:
+ try:
+ options = "{'widget': 'monetary'"
+ for k, v in json.loads(field_options).iteritems():
+ print k, v
+ if k in ('display_currency', 'from_currency'):
+ options = "%s, '%s': %s" % (options, k, v)
+ else:
+ options = "%s, '%s': '%s'" % (options, k, v)
+ options = "%s}" % options
+ field_options = options
+ _logger.warning("Use new syntax for '%s' monetary widget t-options (python dict instead of deprecated JSON syntax)." % etree.tostring(el))
+ except ValueError:
+ pass
+
+ return field_options
+ # end backward
+
# method called by computing code
@tools.conditional(
@@ -231,16 +259,35 @@ class IrQWeb(models.AbstractModel, QWeb):
converter = self.env[model] if model in self.env else self.env['ir.qweb.field']
# get content
- content = converter.record_to_html(record, field_name, field_options, values)
+ content = converter.record_to_html(record, field_name, field_options, values=values)
attributes = converter.attributes(record, field_name, field_options, values)
return (attributes, content, inherit_branding or translate)
+ def _get_widget(self, value, expression, tagName, field_options, options, values):
+ field_options['type'] = field_options['widget']
+ field_options['tagName'] = tagName
+ field_options['expression'] = expression
+
+ # field converter
+ model = 'ir.qweb.field.' + field_options['type']
+ converter = self.env[model] if model in self.env else self.env['ir.qweb.field']
+
+ # get content
+ content = converter.value_to_html(value, field_options)
+ attributes = OrderedDict()
+ attributes['data-oe-type'] = field_options['type']
+ attributes['data-oe-expression'] = field_options['expression']
+
+ return (attributes, content, None)
+
# formating methods
def _format_func_monetary(self, value, display_currency, from_currency=None):
- pre, value, post = self.env['ir.qweb.field.monetary']._format_func_monetary(value, display_currency, from_currency)
- return u'{pre}{0}{post}'.format(value, pre=pre, post=post)
+ options = {}
+ options['display_currency'] = display_currency
+ options['from_currency'] = from_currency
+ return self.env['ir.qweb.field.monetary'].value_to_html(value, options)
def _format_func_htmlcontact(self, value, options, values=None):
return self.pool['ir.qweb.field.contact'].record_to_html(
diff --git a/openerp/addons/base/ir/ir_qweb/qweb.py b/openerp/addons/base/ir/ir_qweb/qweb.py
index dda4e1546c2..2d4f7c90a3a 100644
--- a/openerp/addons/base/ir/ir_qweb/qweb.py
+++ b/openerp/addons/base/ir/ir_qweb/qweb.py
@@ -6,7 +6,8 @@ import re
import traceback
from itertools import count
from textwrap import dedent
-from werkzeug.utils import escape
+import werkzeug
+from werkzeug.utils import escape as _escape
from itertools import izip, tee
import __builtin__
builtin_defaults = {name: getattr(__builtin__, name) for name in dir(__builtin__)}
@@ -141,16 +142,8 @@ class QWebException(Exception):
def __repr__(self):
return str(self)
-
-import werkzeug
-from werkzeug.utils import escape
# Avoid DeprecationWarning while still remaining compatible with werkzeug pre-0.9
-if getattr(werkzeug, '__version__', '0.0') < '0.9.0':
- def qweb_escape(text):
- return escape(unicodifier(text), quote=True)
-else:
- def qweb_escape(text):
- return escape(unicodifier(text))
+escape = (lambda text: _escape(text, quote=True)) if getattr(werkzeug, '__version__', '0.0') < '0.9.0' else _escape
def unicodifier(val):
if val is None or val is False:
@@ -493,14 +486,14 @@ class QWeb(object):
* collections
* itertools
Define:
- * qweb_escape
+ * escape
* unicodifier (empty string for a None or False, otherwise unicode string)
"""
return ast.parse(dedent("""
from collections import OrderedDict
import itertools
from itertools import repeat, imap
- from openerp.addons.base.ir.ir_qweb.qweb import qweb_escape, unicodifier, foreach_iterator
+ from openerp.addons.base.ir.ir_qweb.qweb import escape, unicodifier, foreach_iterator
"""))
def _create_def(self, options, body, prefix='fn', lineno=None):
@@ -684,11 +677,11 @@ class QWeb(object):
return [
'debug',
'groups', 'foreach', 'if', 'else',
- 'field',
+ 'field', 'esc', 'raw',
'tag',
'call',
'set',
- 'esc', 'raw', 'content',
+ 'content',
]
def _is_static_node(self, el):
@@ -704,7 +697,7 @@ class QWeb(object):
content = self._compile_directive_content(el, options)
if el.tag == 't':
return content
- tag = u'<%s%s' % (el.tag, u''.join([u' %s="%s"' % (name, qweb_escape(value)) for name, value in el.attrib.iteritems()]))
+ tag = u'<%s%s' % (el.tag, u''.join([u' %s="%s"' % (name, escape(unicodifier(value))) for name, value in el.attrib.iteritems()]))
if el.tag in self._void_elements:
return [self._append(ast.Str(tag + '/>'))] + content
else:
@@ -795,7 +788,7 @@ class QWeb(object):
# append(u' ')
# append(name)
# append(u'="')
- # append(qweb_escape(value))
+ # append(escape(unicodifier((value)))
# append(u'"')
body.append(ast.For(
target=ast.Tuple(elts=[ast.Name(id='name', ctx=ast.Store()), ast.Name(id='value', ctx=ast.Store())], ctx=ast.Store()),
@@ -829,8 +822,12 @@ class QWeb(object):
self._append(ast.Name(id='name', ctx=ast.Load())),
self._append(ast.Str(u'="')),
self._append(ast.Call(
- func=ast.Name(id='qweb_escape', ctx=ast.Load()),
- args=[ast.Name(id='value', ctx=ast.Load())], keywords=[],
+ func=ast.Name(id='escape', ctx=ast.Load()),
+ args=[ast.Call(
+ func=ast.Name(id='unicodifier', ctx=ast.Load()),
+ args=[ast.Name(id='value', ctx=ast.Load())], keywords=[],
+ starargs=None, kwargs=None
+ )], keywords=[],
starargs=None, kwargs=None
)),
self._append(ast.Str(u'"')),
@@ -844,6 +841,8 @@ class QWeb(object):
def _compile_tag(self, el, content, options, attr_already_created=False):
""" Compile the tag of the given element into a list of AST nodes. """
+ if el.tag == 't':
+ return content
body = [self._append(ast.Str(u'<%s' % el.tag))]
body.extend(self._compile_all_attributes(el, options, attr_already_created))
if el.tag in self._void_elements:
@@ -869,8 +868,6 @@ class QWeb(object):
def _compile_directive_tag(self, el, options):
el.attrib.pop('t-tag', None)
content = self._compile_directives(el, options)
- if el.tag == 't':
- return content
return self._compile_tag(el, content, options, False)
def _compile_directive_set(self, el, options):
@@ -925,44 +922,102 @@ class QWeb(object):
)]
def _compile_directive_esc(self, el, options):
- return [
- # content = t-raw value
- ast.Assign(
- targets=[ast.Name(id='content', ctx=ast.Store())],
- value=self._compile_expr0(el.attrib.pop('t-esc')),
- ),
- # if content is not None: display
- self._if_content_is_not_Falsy(
- # append(escape($content))
- body=[self._append(ast.Call(
- func=ast.Name(id='qweb_escape', ctx=ast.Load()),
- args=[ast.Name(id='content', ctx=ast.Load())], keywords=[],
- starargs=None, kwargs=None
- ))],
- # append(body default value)
- orelse=self._compile_directive_content(el, options),
- )
- ]
+ field_options = self._compile_widget_options(el, 'esc')
+ content = self._compile_widget(el, el.attrib.pop('t-esc'), field_options, 'escape')
+ return content + self._compile_widget_value(el, options)
def _compile_directive_raw(self, el, options):
- return [
- # content = t-raw value
- ast.Assign(
- targets=[ast.Name(id='content', ctx=ast.Store())],
- value=self._compile_expr0(el.attrib.pop('t-raw')),
+ field_options = self._compile_widget_options(el, 'raw')
+ content = self._compile_widget(el, el.attrib.pop('t-raw'), field_options)
+ return content + self._compile_widget_value(el, options)
+
+ def _compile_widget(self, el, expression, field_options, escape=None):
+ # if isinstance(value, basestring):
+ # value = escape(unicodifier(value))
+ escaped = ast.If(
+ test=ast.Call(
+ func=ast.Name(id='isinstance', ctx=ast.Load()),
+ args=[
+ ast.Name(id='content', ctx=ast.Load()),
+ ast.Name(id='basestring', ctx=ast.Load())
+ ], keywords=[],
+ starargs=None, kwargs=None
),
- self._if_content_is_not_Falsy(
- # append($content)
- body=[self._append(ast.Call(
- func=ast.Name(id='unicodifier', ctx=ast.Load()),
- args=[ast.Name(id='content', ctx=ast.Load())], keywords=[],
+ body=[ast.Assign(
+ targets=[ast.Name(id='content', ctx=ast.Store())],
+ value=ast.Call(
+ func=ast.Name(id=escape, ctx=ast.Load()),
+ args=[ast.Call(
+ func=ast.Name(id='unicodifier', ctx=ast.Load()),
+ args=[ast.Name(id='content', ctx=ast.Load())], keywords=[],
+ starargs=None, kwargs=None
+ )], keywords=[],
starargs=None, kwargs=None
- ))],
- # append(body default value)
- orelse=self._compile_directive_content(el, options),
- )
+ ) if escape else ast.Name(id='content', ctx=ast.Load())
+ )],
+ orelse=[]
+ )
+
+ if field_options:
+ return [
+ # value = t-(esc|raw)
+ ast.Assign(
+ targets=[ast.Name(id='content', ctx=ast.Store())],
+ value=self._compile_expr0(expression)
+ ),
+ escaped,
+ # t_attrs, content, force_display = self._get_widget(value, expression, tagName, field options, template options, values)
+ ast.Assign(
+ targets=[ast.Tuple(elts=[
+ ast.Name(id='t_attrs', ctx=ast.Store()),
+ ast.Name(id='content', ctx=ast.Store()),
+ ast.Name(id='force_display', ctx=ast.Store())
+ ], ctx=ast.Store())],
+ value=ast.Call(
+ func=ast.Attribute(
+ value=ast.Name(id='self', ctx=ast.Load()),
+ attr='_get_widget',
+ ctx=ast.Load()
+ ),
+ args=[
+ ast.Name(id='content', ctx=ast.Load()),
+ ast.Str(expression),
+ ast.Str(el.tag),
+ field_options and self._compile_expr(field_options) or ast.Dict(keys=[], values=[]),
+ ast.Name(id='options', ctx=ast.Load()),
+ ast.Name(id='values', ctx=ast.Load()),
+ ],
+ keywords=[], starargs=None, kwargs=None
+ )
+ )
+ ]
+
+ return [
+ # t_attrs, content, force_display = OrderedDict(), t-(esc|raw), None
+ ast.Assign(
+ targets=[ast.Tuple(elts=[
+ ast.Name(id='t_attrs', ctx=ast.Store()),
+ ast.Name(id='content', ctx=ast.Store()),
+ ast.Name(id='force_display', ctx=ast.Store()),
+ ], ctx=ast.Store())],
+ value=ast.Tuple(elts=[
+ ast.Call(
+ func=ast.Name(id='OrderedDict', ctx=ast.Load()),
+ args=[],
+ keywords=[], starargs=None, kwargs=None
+ ),
+ self._compile_expr0(expression),
+ ast.Name(id='None', ctx=ast.Load()),
+ ], ctx=ast.Load())
+ ),
+ escaped
]
+ # for backward compatibility to remove after v10
+ def _compile_widget_options(self, el, directive_type):
+ return el.attrib.pop('t-options', None)
+ # end backward
+
def _compile_directive_content(self, el, options):
body = []
if el.text is not None:
@@ -1060,10 +1115,10 @@ class QWeb(object):
"t-field must have at least a dot like 'record.field_name'"
expression = el.attrib.pop('t-field')
- field_options = el.attrib.pop('t-field-options', None)
+ field_options = self._compile_widget_options(el, 'field')
record, field_name = expression.rsplit('.', 1)
- content = [
+ return [
# t_attrs, content, force_display = self._get_field(record, field_name, expression, tagName, field options, template options, values)
ast.Assign(
targets=[ast.Tuple(elts=[
@@ -1089,8 +1144,9 @@ class QWeb(object):
keywords=[], starargs=None, kwargs=None
)
)
- ]
+ ] + self._compile_widget_value(el, options)
+ def _compile_widget_value(self, el, options):
# if force_display:
# display the tag without content
orelse = [ast.If(
@@ -1145,17 +1201,22 @@ class QWeb(object):
]
# if content is not None:
- # display the tag
+ # display the tag (unicodifier(content))
# else
# if default_content:
# display the tag with default content
# elif force_display:
# display the tag without content
- content.append(self._if_content_is_not_Falsy(
- body=self._compile_tag(el, [self._append(ast.Name(id='content', ctx=ast.Load()))], options, True),
+ return [self._if_content_is_not_Falsy(
+ body=self._compile_tag(el, [self._append(
+ ast.Call(
+ func=ast.Name(id='unicodifier', ctx=ast.Load()),
+ args=[ast.Name(id='content', ctx=ast.Load())], keywords=[],
+ starargs=None, kwargs=None
+ )
+ )], options, True),
orelse=orelse,
- ))
- return content
+ )]
def _compile_directive_call(self, el, options):
tmpl = el.attrib.pop('t-call')
@@ -1299,7 +1360,16 @@ class QWeb(object):
* string or None: content
* boolean: force_display display the tag if the content and default_content are None
"""
- return (OrderedDict(), escape(unicodifier(getattr(record, field_name, None))), False)
+ return self._get_widget(getattr(record, field_name, None), expression, tagName, field_options, options, values)
+
+ def _get_widget(self, value, expression, tagName, field_options, options, values):
+ """
+ :returns: tuple:
+ * OrderedDict: attributes
+ * string or None: content
+ * boolean: force_display display the tag if the content and default_content are None
+ """
+ return (OrderedDict(), value, False)
# compile expression
diff --git a/openerp/addons/test_converter/tests/test_html.py b/openerp/addons/test_converter/tests/test_html.py
index 58128176eac..c42f9c36311 100644
--- a/openerp/addons/test_converter/tests/test_html.py
+++ b/openerp/addons/test_converter/tests/test_html.py
@@ -30,8 +30,11 @@ class TestExport(common.TransactionCase):
break
except KeyError: pass
- return lambda value, options=None, context=None: model.value_to_html(
- self.cr, self.uid, value, field, options=options, context=context)
+ def converter(value, options=None, context=None):
+ record = self.Model.new(self.cr, self.uid, {name: value}, context=context)
+ return model.record_to_html(
+ self.cr, self.uid, record, name, options or {}, context=context)
+ return converter
class TestBasicExport(TestExport):
_model = 'test_converter.test_model'
@@ -188,36 +191,23 @@ class TestTextExport(TestBasicExport):
class TestMany2OneExport(TestBasicExport):
def test_many2one(self):
Sub = self.registry('test_converter.test_model.sub')
+ converter = self.get_converter('many2one')
-
- id0 = self.Model.create(self.cr, self.uid, {
- 'many2one': Sub.create(self.cr, self.uid, {'name': "Foo"})
- })
- id1 = self.Model.create(self.cr, self.uid, {
- 'many2one': Sub.create(self.cr, self.uid, {'name': "Foo "})
- })
-
- def converter(record):
- converter = self.registry('ir.qweb.field.many2one')
- field_name = 'many2one'
- return converter.record_to_html(self.cr, self.uid, record, field_name, {})
-
- value = converter(self.Model.browse(self.cr, self.uid, id0))
+ value = converter(Sub.create(self.cr, self.uid, {'name': "Foo"}))
self.assertEqual(value, "Foo")
- value = converter(self.Model.browse(self.cr, self.uid, id1))
+ value = converter(Sub.create(self.cr, self.uid, {'name': "Foo "}))
self.assertEqual(value, "Fo<b>o</b>")
class TestBinaryExport(TestBasicExport):
def test_image(self):
- field = self.get_field('binary')
converter = self.registry('ir.qweb.field.image')
with open(os.path.join(directory, 'test_vectors', 'image'), 'rb') as f:
content = f.read()
encoded_content = content.encode('base64')
- value = converter.value_to_html(self.cr, self.uid, encoded_content, field)
+ value = converter.value_to_html(self.cr, self.uid, encoded_content, {})
self.assertEqual(
value, ' ' % (
@@ -229,30 +219,23 @@ class TestBinaryExport(TestBasicExport):
with self.assertRaises(ValueError):
converter.value_to_html(
- self.cr, self.uid, 'binary', content.encode('base64'), field)
+ self.cr, self.uid, 'binary', content.encode('base64'), {})
with open(os.path.join(directory, 'test_vectors', 'pptx'), 'rb') as f:
content = f.read()
with self.assertRaises(ValueError):
converter.value_to_html(
- self.cr, self.uid, 'binary', content.encode('base64'), field)
+ self.cr, self.uid, 'binary', content.encode('base64'), {})
class TestSelectionExport(TestBasicExport):
def test_selection(self):
- [record] = self.Model.browse(self.cr, self.uid, [self.Model.create(self.cr, self.uid, {
- 'selection': 4,
- 'selection_str': 'C',
- })])
-
- converter = self.registry('ir.qweb.field.selection')
-
- field_name = 'selection'
- value = converter.record_to_html(self.cr, self.uid, record, field_name, {})
+ converter = self.get_converter('selection')
+ value = converter(4)
self.assertEqual(value, e(u"réponse "))
- field_name = 'selection_str'
- value = converter.record_to_html(self.cr, self.uid, record, field_name, {})
+ converter = self.get_converter('selection_str')
+ value = converter('C')
self.assertEqual(value, u"Qu'est-ce qu'il fout ce maudit pancake, tabernacle ?")
class TestHTMLExport(TestBasicExport):
From ae5f853819bb95c76f47f6b37d43348b87db539d Mon Sep 17 00:00:00 2001
From: Christophe Matthieu
Date: Tue, 5 Jul 2016 17:36:34 +0200
Subject: [PATCH 3/9] [IMP] qweb: remove values from record_to_html to avoid
crappy code
---
addons/web_editor/models/ir_qweb.py | 3 +--
openerp/addons/base/ir/ir_qweb/fields.py | 20 +++++++++----------
openerp/addons/base/ir/ir_qweb/ir_qweb.py | 2 +-
.../addons/test_converter/tests/test_html.py | 2 +-
4 files changed, 13 insertions(+), 14 deletions(-)
diff --git a/addons/web_editor/models/ir_qweb.py b/addons/web_editor/models/ir_qweb.py
index 362cdcd8a0b..b1c0a165e44 100644
--- a/addons/web_editor/models/ir_qweb.py
+++ b/addons/web_editor/models/ir_qweb.py
@@ -31,7 +31,6 @@ from openerp.osv import orm, fields
from openerp.tools import ustr, DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT
from openerp.tools import html_escape as escape
from openerp.addons.base.ir import ir_qweb
-from openerp.tools.translate import translate
REMOTE_CONNECTION_TIMEOUT = 2.5
@@ -296,7 +295,7 @@ class Image(orm.AbstractModel):
_inherit = 'ir.qweb.field.image'
@api.model
- def record_to_html(self, record, field_name, options, values=None):
+ def record_to_html(self, record, field_name, options):
assert options['tagName'] != 'img',\
"Oddly enough, the root tag of an image field can not be img. " \
"That is because the image goes into the tag, or it gets the " \
diff --git a/openerp/addons/base/ir/ir_qweb/fields.py b/openerp/addons/base/ir/ir_qweb/fields.py
index f520bd25c9a..f761b72918f 100644
--- a/openerp/addons/base/ir/ir_qweb/fields.py
+++ b/openerp/addons/base/ir/ir_qweb/fields.py
@@ -91,8 +91,8 @@ class FieldConverter(models.AbstractModel):
return html_escape(unicodifier(value) or u'', options)
@api.model
- def record_to_html(self, record, field_name, options, values=None):
- """ record_to_html(record, field_name, options, values)
+ def record_to_html(self, record, field_name, options):
+ """ record_to_html(record, field_name, options)
Converts the specified field of the browse_record ``record`` to HTML
:rtype: unicode
@@ -149,11 +149,11 @@ class FloatConverter(models.AbstractModel):
return unicodifier(formatted)
@api.model
- def record_to_html(self, record, field_name, options, values=None):
+ def record_to_html(self, record, field_name, options):
if 'precision' not in options and 'decimal_precision' not in options:
_, precision = record._fields[field_name].digits or (None, None)
options = dict(options, precision=precision)
- return super(FloatConverter, self).record_to_html(record, field_name, options, values=values)
+ return super(FloatConverter, self).record_to_html(record, field_name, options)
class DateConverter(models.AbstractModel):
@@ -230,10 +230,10 @@ class SelectionConverter(models.AbstractModel):
return html_escape(unicodifier(options['selection'][value]) or u'', options)
@api.model
- def record_to_html(self, record, field_name, options, values=None):
+ def record_to_html(self, record, field_name, options):
if 'selection' not in options:
options = dict(options, selection=dict(record._fields[field_name].get_description(self.env)['selection']))
- return super(SelectionConverter, self).record_to_html(record, field_name, options, values=values)
+ return super(SelectionConverter, self).record_to_html(record, field_name, options)
class ManyToOneConverter(models.AbstractModel):
@@ -328,7 +328,7 @@ class MonetaryConverter(models.AbstractModel):
return u'{pre}{0} {post}'.format(formatted_amount, pre=pre, post=post)
@api.model
- def record_to_html(self, record, field_name, options, values=None):
+ def record_to_html(self, record, field_name, options):
options = dict(options)
#currency should be specified by monetary field
field = record._fields[field_name]
@@ -406,10 +406,10 @@ class RelativeDatetimeConverter(models.AbstractModel):
return unicodifier(babel.dates.format_timedelta(value - reference, add_direction=True, locale=locale))
@api.model
- def record_to_html(self, record, field_name, options, values=None):
+ def record_to_html(self, record, field_name, options):
if 'now' not in options:
options = dict(options, now=record._fields[field_name].now())
- return super(RelativeDatetimeConverter, self).record_to_html(record, field_name, options, values=values)
+ return super(RelativeDatetimeConverter, self).record_to_html(record, field_name, options)
class Contact(models.AbstractModel):
@@ -447,7 +447,7 @@ class QwebView(models.AbstractModel):
_inherit = 'ir.qweb.field.many2one'
@api.model
- def record_to_html(self, record, field_name, options, values=None):
+ def record_to_html(self, record, field_name, options):
if not getattr(record, field_name):
return None
diff --git a/openerp/addons/base/ir/ir_qweb/ir_qweb.py b/openerp/addons/base/ir/ir_qweb/ir_qweb.py
index a077a54b614..3200c7cd3f4 100644
--- a/openerp/addons/base/ir/ir_qweb/ir_qweb.py
+++ b/openerp/addons/base/ir/ir_qweb/ir_qweb.py
@@ -259,7 +259,7 @@ class IrQWeb(models.AbstractModel, QWeb):
converter = self.env[model] if model in self.env else self.env['ir.qweb.field']
# get content
- content = converter.record_to_html(record, field_name, field_options, values=values)
+ content = converter.record_to_html(record, field_name, field_options)
attributes = converter.attributes(record, field_name, field_options, values)
return (attributes, content, inherit_branding or translate)
diff --git a/openerp/addons/test_converter/tests/test_html.py b/openerp/addons/test_converter/tests/test_html.py
index c42f9c36311..f1d7dfc4ef7 100644
--- a/openerp/addons/test_converter/tests/test_html.py
+++ b/openerp/addons/test_converter/tests/test_html.py
@@ -107,7 +107,7 @@ class TestCurrencyExport(TestExport):
'widget': 'monetary',
'display_currency': dest,
}
- return converter.record_to_html(self.cr, self.uid, obj, 'value', options, {'obj': obj})
+ return converter.record_to_html(self.cr, self.uid, obj, 'value', options)
def test_currency_post(self):
currency = self.create(self.Currency, name="Test", symbol=u"test")
From 229ab3e1cae9cc8a15b0312f32a363df4898b7fd Mon Sep 17 00:00:00 2001
From: Christophe Matthieu
Date: Tue, 5 Jul 2016 18:07:07 +0200
Subject: [PATCH 4/9] [IMP] qweb: move code to gather similar methods and to
facilitate the reading of the code
---
openerp/addons/base/ir/ir_qweb/qweb.py | 170 ++++++++++++-------------
1 file changed, 85 insertions(+), 85 deletions(-)
diff --git a/openerp/addons/base/ir/ir_qweb/qweb.py b/openerp/addons/base/ir/ir_qweb/qweb.py
index 2d4f7c90a3a..759559e1be3 100644
--- a/openerp/addons/base/ir/ir_qweb/qweb.py
+++ b/openerp/addons/base/ir/ir_qweb/qweb.py
@@ -921,6 +921,91 @@ class QWeb(object):
value=value
)]
+ def _compile_directive_content(self, el, options):
+ body = []
+ if el.text is not None:
+ body.append(self._append(ast.Str(unicodifier(el.text))))
+ if el.getchildren():
+ for item in el:
+ # ignore comments & processing instructions
+ if isinstance(item, etree._Comment):
+ continue
+ body.extend(self._compile_node(item, options))
+ body.extend(self._compile_tail(item))
+ return body
+
+ def _compile_directive_else(self, el, options):
+ if el.attrib.pop('t-else') == '_t_skip_else_':
+ return []
+ if not options.pop('t_if', None):
+ raise ValueError("t-else directive must be preceded by t-if directive")
+ compiled = self._compile_directives(el, options)
+ el.attrib['t-else'] = '_t_skip_else_'
+ return compiled
+
+ def _compile_directive_if(self, el, options):
+ orelse = []
+ next_el = el.getnext()
+ if next_el is not None and 't-else' in next_el.attrib:
+ if el.tail and not el.tail.isspace():
+ raise ValueError("Unexpected non-whitespace characters between t-if and t-else directives")
+ el.tail = None
+ orelse = self._compile_node(next_el, dict(options, t_if=True))
+ return [
+ # if $t-if:
+ # next tag directive
+ # else:
+ # $t-else
+ ast.If(
+ test=self._compile_expr(el.attrib.pop('t-if')),
+ body=self._compile_directives(el, options),
+ orelse=orelse
+ )
+ ]
+
+ def _compile_directive_groups(self, el, options):
+ return [
+ # if self.user_has_groups($groups):
+ # next tag directive
+ ast.If(
+ test=ast.Call(
+ func=ast.Attribute(
+ value=ast.Name(id='self', ctx=ast.Load()),
+ attr='user_has_groups',
+ ctx=ast.Load()
+ ),
+ args=[ast.Str(el.attrib.pop('t-groups'))], keywords=[],
+ starargs=None, kwargs=None
+ ),
+ body=self._compile_directives(el, options),
+ orelse=[]
+ )
+ ]
+
+ def _compile_directive_foreach(self, el, options):
+ expr = self._compile_expr(el.attrib.pop('t-foreach'))
+ varname = el.attrib.pop('t-as').replace('.', '_')
+ values = self._make_name('values')
+
+ # create function $foreach
+ def_name = self._create_def(options, self._compile_directives(el, options), prefix='foreach', lineno=el.sourceline)
+
+ # for x in foreach_iterator(values, $expr, $varname):
+ # $foreach(self, append, values, options)
+ return [ast.For(
+ target=ast.Name(id=values, ctx=ast.Store()),
+ iter=ast.Call(
+ func=ast.Name(id='foreach_iterator', ctx=ast.Load()),
+ args=[ast.Name(id='values', ctx=ast.Load()), expr, ast.Str(varname)],
+ keywords=[], starargs=None, kwargs=None
+ ),
+ body=[ast.Expr(self._call_def(def_name, values=values))],
+ orelse=[]
+ )]
+
+ def _compile_tail(self, el):
+ return el.tail is not None and [self._append(ast.Str(unicodifier(el.tail)))] or []
+
def _compile_directive_esc(self, el, options):
field_options = self._compile_widget_options(el, 'esc')
content = self._compile_widget(el, el.attrib.pop('t-esc'), field_options, 'escape')
@@ -1018,91 +1103,6 @@ class QWeb(object):
return el.attrib.pop('t-options', None)
# end backward
- def _compile_directive_content(self, el, options):
- body = []
- if el.text is not None:
- body.append(self._append(ast.Str(unicodifier(el.text))))
- if el.getchildren():
- for item in el:
- # ignore comments & processing instructions
- if isinstance(item, etree._Comment):
- continue
- body.extend(self._compile_node(item, options))
- body.extend(self._compile_tail(item))
- return body
-
- def _compile_directive_else(self, el, options):
- if el.attrib.pop('t-else') == '_t_skip_else_':
- return []
- if not options.pop('t_if', None):
- raise ValueError("t-else directive must be preceded by t-if directive")
- compiled = self._compile_directives(el, options)
- el.attrib['t-else'] = '_t_skip_else_'
- return compiled
-
- def _compile_directive_if(self, el, options):
- orelse = []
- next_el = el.getnext()
- if next_el is not None and 't-else' in next_el.attrib:
- if el.tail and not el.tail.isspace():
- raise ValueError("Unexpected non-whitespace characters between t-if and t-else directives")
- el.tail = None
- orelse = self._compile_node(next_el, dict(options, t_if=True))
- return [
- # if $t-if:
- # next tag directive
- # else:
- # $t-else
- ast.If(
- test=self._compile_expr(el.attrib.pop('t-if')),
- body=self._compile_directives(el, options),
- orelse=orelse
- )
- ]
-
- def _compile_directive_groups(self, el, options):
- return [
- # if self.user_has_groups($groups):
- # next tag directive
- ast.If(
- test=ast.Call(
- func=ast.Attribute(
- value=ast.Name(id='self', ctx=ast.Load()),
- attr='user_has_groups',
- ctx=ast.Load()
- ),
- args=[ast.Str(el.attrib.pop('t-groups'))], keywords=[],
- starargs=None, kwargs=None
- ),
- body=self._compile_directives(el, options),
- orelse=[]
- )
- ]
-
- def _compile_directive_foreach(self, el, options):
- expr = self._compile_expr(el.attrib.pop('t-foreach'))
- varname = el.attrib.pop('t-as').replace('.', '_')
- values = self._make_name('values')
-
- # create function $foreach
- def_name = self._create_def(options, self._compile_directives(el, options), prefix='foreach', lineno=el.sourceline)
-
- # for x in foreach_iterator(values, $expr, $varname):
- # $foreach(self, append, values, options)
- return [ast.For(
- target=ast.Name(id=values, ctx=ast.Store()),
- iter=ast.Call(
- func=ast.Name(id='foreach_iterator', ctx=ast.Load()),
- args=[ast.Name(id='values', ctx=ast.Load()), expr, ast.Str(varname)],
- keywords=[], starargs=None, kwargs=None
- ),
- body=[ast.Expr(self._call_def(def_name, values=values))],
- orelse=[]
- )]
-
- def _compile_tail(self, el):
- return el.tail is not None and [self._append(ast.Str(unicodifier(el.tail)))] or []
-
def _compile_directive_field(self, el, options):
""" Compile something like ``+1 555 555 8069 `` """
node_name = el.tag
From 339d5b85059a191702e479b29471903042144139 Mon Sep 17 00:00:00 2001
From: Christophe Matthieu
Date: Mon, 4 Jul 2016 12:59:41 +0200
Subject: [PATCH 5/9] [FIX] qweb: float widget: use same rounding as orm float
field
42,12345 with precision=4 should be 42,1235 and not 42,1234
---
addons/decimal_precision/tests/test_qweb_float.py | 4 ++--
openerp/addons/base/ir/ir_qweb/fields.py | 11 +++++++----
2 files changed, 9 insertions(+), 6 deletions(-)
diff --git a/addons/decimal_precision/tests/test_qweb_float.py b/addons/decimal_precision/tests/test_qweb_float.py
index e598f00d8ec..7011b0f9e75 100644
--- a/addons/decimal_precision/tests/test_qweb_float.py
+++ b/addons/decimal_precision/tests/test_qweb_float.py
@@ -33,13 +33,13 @@ class TestFloatExport(common.TransactionCase):
converter(42.12345),
"42.12")
- converter = self.get_converter('float') # don't use float_4 because the field value 42.12345 is orm converted to 42.1235
+ converter = self.get_converter('float') # don't use float_4 because the field value 42.12345 is already orm converted to 42.1235
self.assertEqual(
converter(42.0, {'precision': 4}),
'42.0000')
self.assertEqual(
converter(42.12345, {'precision': 4}),
- '42.1234')
+ '42.1235')
def test_precision_domain(self):
DP = self.registry('decimal.precision')
diff --git a/openerp/addons/base/ir/ir_qweb/fields.py b/openerp/addons/base/ir/ir_qweb/fields.py
index f761b72918f..c8d93037c26 100644
--- a/openerp/addons/base/ir/ir_qweb/fields.py
+++ b/openerp/addons/base/ir/ir_qweb/fields.py
@@ -5,7 +5,7 @@ from odoo import api, fields, models, _
from PIL import Image
from cStringIO import StringIO
import babel
-from openerp.tools import html_escape as escape, posix_to_ldml, safe_eval
+from openerp.tools import html_escape as escape, posix_to_ldml, safe_eval, float_utils
from .qweb import unicodifier
import logging
@@ -134,10 +134,13 @@ class FloatConverter(models.AbstractModel):
else:
precision = options['precision']
- fmt = '%f' if precision is None else '%.{precision}f'
- lang = self.user_lang()
+ if precision is None:
+ fmt = '%f'
+ else:
+ value = float_utils.float_round(value, precision_digits=precision)
+ fmt = '%.{precision}f'.format(precision=precision)
- formatted = lang.format(fmt.format(precision=precision), value, grouping=True)
+ formatted = self.user_lang().format(fmt, value, grouping=True)
# %f does not strip trailing zeroes. %g does but its precision causes
# it to switch to scientific notation starting at a million *and* to
From 940e23a7dc0b386d28afe3b4515f5dab49a1a994 Mon Sep 17 00:00:00 2001
From: Christophe Matthieu
Date: Mon, 4 Jul 2016 16:25:29 +0200
Subject: [PATCH 6/9] [IMP] qweb: t-esc/t-raw use t-options instead of format
method
t-field, t-esc and t-raw use the same widgets and options
---
.../views/report_agedpartnerbalance.xml | 28 +-
addons/account/views/report_financial.xml | 10 +-
addons/account/views/report_generalledger.xml | 12 +-
addons/account/views/report_journal.xml | 14 +-
addons/account/views/report_overdue.xml | 12 +-
addons/account/views/report_partnerledger.xml | 14 +-
addons/account/views/report_trialbalance.xml | 6 +-
addons/l10n_fr/views/report_l10nfrbilan.xml | 274 +++++++++---------
.../l10n_fr/views/report_l10nfrresultat.xml | 106 +++----
.../point_of_sale/views/report_statement.xml | 2 +-
addons/sale/views/report_invoice_layouted.xml | 2 +-
addons/sale/views/report_saleorder.xml | 2 +-
.../website_crm_partner_assign_templates.xml | 4 +-
.../views/website_payment_templates.xml | 4 +-
.../website_quote/views/website_quotation.xml | 2 +-
addons/website_sale/views/templates.xml | 12 +-
openerp/addons/base/ir/ir_qweb/ir_qweb.py | 14 +-
17 files changed, 253 insertions(+), 265 deletions(-)
diff --git a/addons/account/views/report_agedpartnerbalance.xml b/addons/account/views/report_agedpartnerbalance.xml
index 560c711be9b..8681931febb 100644
--- a/addons/account/views/report_agedpartnerbalance.xml
+++ b/addons/account/views/report_agedpartnerbalance.xml
@@ -50,13 +50,13 @@
Account Total
-
-
-
-
-
-
-
+
+
+
+
+
+
+
@@ -65,25 +65,25 @@
-
+
-
+
-
+
-
+
-
+
-
+
-
+
diff --git a/addons/account/views/report_financial.xml b/addons/account/views/report_financial.xml
index 692e0bc2676..8b968fda675 100644
--- a/addons/account/views/report_financial.xml
+++ b/addons/account/views/report_financial.xml
@@ -43,13 +43,13 @@
-
+
-
+
-
+
@@ -73,7 +73,7 @@
-
+
@@ -96,7 +96,7 @@
-
+
diff --git a/addons/account/views/report_generalledger.xml b/addons/account/views/report_generalledger.xml
index 7ef9fef28dc..e06e8ddda59 100644
--- a/addons/account/views/report_generalledger.xml
+++ b/addons/account/views/report_generalledger.xml
@@ -66,13 +66,13 @@
-
+
-
+
-
+
@@ -84,13 +84,13 @@
-
+
-
+
-
+
diff --git a/addons/account/views/report_journal.xml b/addons/account/views/report_journal.xml
index 396c43b8474..f94e20fa3b0 100644
--- a/addons/account/views/report_journal.xml
+++ b/addons/account/views/report_journal.xml
@@ -51,10 +51,10 @@
-
-
+
+
-
+
@@ -65,8 +65,8 @@
@@ -87,8 +87,8 @@
-
-
+
+
diff --git a/addons/account/views/report_overdue.xml b/addons/account/views/report_overdue.xml
index 79e0e3cf025..3b7d3808100 100644
--- a/addons/account/views/report_overdue.xml
+++ b/addons/account/views/report_overdue.xml
@@ -44,10 +44,10 @@
-
+
-
+
x
@@ -56,16 +56,16 @@
Subtotal :
-
-
-
+
+
+
Balance :
-
+
diff --git a/addons/account/views/report_partnerledger.xml b/addons/account/views/report_partnerledger.xml
index 94ac9b0d4b6..0259e5167e8 100644
--- a/addons/account/views/report_partnerledger.xml
+++ b/addons/account/views/report_partnerledger.xml
@@ -46,13 +46,13 @@
-
-
+
-
+
-
+
@@ -69,16 +69,16 @@
-
+
-
+
-
+
-
+
diff --git a/addons/account/views/report_trialbalance.xml b/addons/account/views/report_trialbalance.xml
index 7ada5bc393b..750c9f93698 100644
--- a/addons/account/views/report_trialbalance.xml
+++ b/addons/account/views/report_trialbalance.xml
@@ -50,13 +50,13 @@
-
+
-
+
-
+
diff --git a/addons/l10n_fr/views/report_l10nfrbilan.xml b/addons/l10n_fr/views/report_l10nfrbilan.xml
index 5923e6a763f..e35a1ecbf50 100644
--- a/addons/l10n_fr/views/report_l10nfrbilan.xml
+++ b/addons/l10n_fr/views/report_l10nfrbilan.xml
@@ -33,11 +33,11 @@
Capital souscrit - non appelé
-
+
-
+
@@ -59,83 +59,83 @@
Frais d'établissement
-
+
-
+
-
+
Frais de recherche et de développement
-
+
-
+
-
+
Concessions, brevets, licences,..., droits et valeurs similaires
-
+
-
+
-
+
Fonds commercial
-
+
-
+
-
+
Autres
-
+
-
+
-
+
Immobilisations incorporelles en cours
-
+
-
+
-
+
Avances et acomptes
-
+
-
+
@@ -149,71 +149,71 @@
Terrains
-
+
-
+
-
+
Constructions
-
+
-
+
-
+
Installations techniques,matériel et outillage
-
+
-
+
-
+
Autres
-
+
-
+
-
+
Immobilisations corporelles en cours
-
+
-
+
-
+
Avances et acomptes
-
+
-
+
@@ -227,73 +227,73 @@
Participations
-
+
-
+
-
+
Créances rattachées à des participations
-
+
-
+
-
+
Titres immobilisés de l'activité de portefeuille
-
+
-
+
-
+
Autres titres immobilisés
-
+
-
+
-
+
Prêts
-
+
-
+
-
+
Autres
-
+
-
+
-
+
@@ -301,13 +301,13 @@
TOTAL I
-
+
-
+
-
+
@@ -325,59 +325,59 @@
Matières premières et autres approvisionnements
-
+
-
+
-
+
En-cours de production [biens et services]
-
+
-
+
-
+
Produits intermédiaires et finis
-
+
-
+
-
+
Marchandises
-
+
-
+
-
+
Avances et acomptes versés sur commandes
-
+
-
+
@@ -389,35 +389,35 @@
Créances clients et comptes rattachés
-
+
-
+
-
+
Autres
-
+
-
+
-
+
Capital souscrit - appelé , non versé
-
+
-
+
@@ -429,55 +429,55 @@
Actions propres
-
+
-
+
-
+
Autres titres
-
+
-
+
-
+
Instruments de trésorerie
-
+
-
+
Disponibilités
-
+
-
+
Charges constatés d'avance
-
+
-
+
@@ -485,43 +485,43 @@
TOTAL II
-
+
-
+
-
+
Charges à répartir sur plusieurs exercices ( III )
-
+
-
+
Primes de remboursement des emprunts ( IV )
-
+
-
+
Écarts de conversion actif ( V )
-
+
-
+
@@ -529,13 +529,13 @@
TOTAL ACTIF ( I + II + III + IV + V )
-
+
-
+
-
+
@@ -549,19 +549,19 @@
Capital [dont versé...]
-
+
Primes d'émission, de fusion, d'apport
-
+
Écarts de réévaluation
-
+
Écart d'équivalence
-
+
RÉSERVES
@@ -569,37 +569,37 @@
Réserve légale
-
+
Réserves statutaires ou contractuelles
-
+
- Réserves réglementées
+ Réserves réglementées
Autres réserves
-
+
Report à nouveau
-
+
RÉSULTAT DE L'EXERCICE [bénéfice ou perte]
-
+
Subventions d'investissement
-
+
Provisions réglementées
-
+
TOTAL I
-
+
PROVISIONS
@@ -607,15 +607,15 @@
Provisions pour risques
-
+
Provisions pour charges
-
+
TOTAL II
-
+
DETTES
@@ -623,59 +623,59 @@
Emprunts obligataires convertibles
-
+
Autres emprunts obligataires
-
+
Emprunts et dettes auprès des établissements de crédit
-
+
Emprunts et dettes financières diverses
-
+
Avances et acomptes reçus sur commandes en cours
-
+
Dettes fournisseurs et comptes rattachés
-
+
Dettes fiscales et sociales
-
+
Dettes sur immobilisations et comptes rattachés
-
+
Autres dettes
-
+
Instruments de trésorerie
-
+
Produits constatés d'avance
-
+
TOTAL III
-
+
Écarts de conversion passif ( IV )
-
+
TOTAL GÉNÉRAL (I + II + III + IV)
-
+
@@ -683,7 +683,7 @@
ACTIF - PASSIF
-
+
diff --git a/addons/l10n_fr/views/report_l10nfrresultat.xml b/addons/l10n_fr/views/report_l10nfrresultat.xml
index 0516dd58199..16dbfb81bdf 100644
--- a/addons/l10n_fr/views/report_l10nfrresultat.xml
+++ b/addons/l10n_fr/views/report_l10nfrresultat.xml
@@ -35,61 +35,61 @@
Achat de marchandises
-
+
Variation des stocks
-
+
Achats de matières premières et autres approvisionnements
-
+
Variation des stocks
-
+
Autres achats et charges externes
-
+
Redevances de crédit-bail mobilier
-
+
Redevances de crédit-bail immobilier
-
+
Impôts, taxes et versements assimilés
-
+
Salaires et traitements
-
+
Charges sociales
-
+
@@ -99,43 +99,43 @@
Sur immobilisations : dotations aux amortissements
-
+
Sur immobilisations : dotations aux dépréciations
-
+
Sur actif circulant : dotations aux dépréciations
-
+
Dotations aux provisions
-
+
Autres charges
-
+
TOTAL I
-
+
Quotes-parts de résultat sur opérations faites en commun ( II )
-
+
@@ -145,31 +145,31 @@
Dotations aux amortissements, aux dépréciations et aux provisions
-
+
Intérêts et charges assimilées
-
+
Différences négatives de change
-
+
Charges nettes sur cessions de valeurs mobilières de placement
-
+
TOTAL III
-
+
@@ -179,43 +179,43 @@
Sur opérations de gestion
-
+
Sur opérations en capital
-
+
Dotations aux amortissements, aux dépréciations et aux provisions
-
+
TOTAL IV
-
+
Participation des salariés aux résultats ( V )
-
+
Impôts sur les bénéfices ( VI )
-
+
TOTAL CHARGES ( I + II + III + IV+ V+ VI )
-
+
@@ -236,67 +236,67 @@
Vente de marchandises
-
+
Production vendue [biens et services]
-
+
Sous-total A - Montant net du chiffre d'affaires
-
+
Production stockée
-
+
Production immobilisée
-
+
Subventions d'exploitation
-
+
Reprises sur provisions, dépréciations (et amortissements) et transferts de charges
-
+
Autres produits
-
+
Sous-total B
-
+
TOTAL I ( A + B )
-
+
Quotes-parts de résultat sur opérations faites en commun (II)
-
+
@@ -306,43 +306,43 @@
De participation
-
+
D'autres valeurs mobilières et créances de l'actif immobilisé
-
+
Autres intérêts et produits assimilés
-
+
Reprises sur provisions, dépréciations et transferts de charges
-
+
Différences positives de change
-
+
Produits nets sur cessions de valeurs mobilières de placement
-
+
TOTAL III
-
+
@@ -352,37 +352,37 @@
Sur opérations de gestion
-
+
Sur opérations en capital
-
+
Reprises sur provisions, dépréciations et transferts de charges
-
+
TOTAL IV
-
+
TOTAL DES PRODUITS ( I + II + III + IV )
-
+
PRODUITS - CHARGES
-
+
diff --git a/addons/point_of_sale/views/report_statement.xml b/addons/point_of_sale/views/report_statement.xml
index cedd7f55309..bff915b4bf4 100644
--- a/addons/point_of_sale/views/report_statement.xml
+++ b/addons/point_of_sale/views/report_statement.xml
@@ -67,7 +67,7 @@
diff --git a/addons/sale/views/report_invoice_layouted.xml b/addons/sale/views/report_invoice_layouted.xml
index 3f492f8de8e..2762c1e9532 100644
--- a/addons/sale/views/report_invoice_layouted.xml
+++ b/addons/sale/views/report_invoice_layouted.xml
@@ -63,7 +63,7 @@
Subtotal:
-
+
diff --git a/addons/sale/views/report_saleorder.xml b/addons/sale/views/report_saleorder.xml
index 5ee5f52117c..f62a2d9b72d 100644
--- a/addons/sale/views/report_saleorder.xml
+++ b/addons/sale/views/report_saleorder.xml
@@ -112,7 +112,7 @@
Subtotal:
-
+
diff --git a/addons/website_crm_partner_assign/views/website_crm_partner_assign_templates.xml b/addons/website_crm_partner_assign/views/website_crm_partner_assign_templates.xml
index 30bb61b5fb0..6187e147fc1 100644
--- a/addons/website_crm_partner_assign/views/website_crm_partner_assign_templates.xml
+++ b/addons/website_crm_partner_assign/views/website_crm_partner_assign_templates.xml
@@ -329,7 +329,7 @@
- at %
+ at %
@@ -559,7 +559,7 @@
- at %
+ at %
Expected Closing:
diff --git a/addons/website_payment/views/website_payment_templates.xml b/addons/website_payment/views/website_payment_templates.xml
index ec22377e7e1..282ac70c3f8 100644
--- a/addons/website_payment/views/website_payment_templates.xml
+++ b/addons/website_payment/views/website_payment_templates.xml
@@ -104,7 +104,7 @@
@@ -148,7 +148,7 @@
diff --git a/addons/website_quote/views/website_quotation.xml b/addons/website_quote/views/website_quotation.xml
index 1e7cb4d1ec1..15865b78ad7 100644
--- a/addons/website_quote/views/website_quotation.xml
+++ b/addons/website_quote/views/website_quotation.xml
@@ -92,7 +92,7 @@
Subtotal:
-
+
diff --git a/addons/website_sale/views/templates.xml b/addons/website_sale/views/templates.xml
index 7234b30e927..04256896248 100644
--- a/addons/website_sale/views/templates.xml
+++ b/addons/website_sale/views/templates.xml
@@ -97,9 +97,9 @@
-
+
-
+
@@ -495,9 +495,9 @@
-
+
@@ -786,7 +786,7 @@
-
+
@@ -1028,7 +1028,7 @@
+ t-options='{"widget": "monetary", "display_currency": o.currency_id}'/>
@@ -97,7 +97,7 @@
Subtotal
-
+
@@ -111,7 +111,7 @@
Total
-
+
diff --git a/addons/account/views/web_planner_data.xml b/addons/account/views/web_planner_data.xml
index 8a7fec1d501..7f0af5daa35 100644
--- a/addons/account/views/web_planner_data.xml
+++ b/addons/account/views/web_planner_data.xml
@@ -59,7 +59,7 @@
Address
+ t-options='{"widget": "contact", "fields": ["address"], "no_marker": True}'/>
diff --git a/addons/event/report/event_event_templates.xml b/addons/event/report/event_event_templates.xml
index efb46e5ce6f..3720744ce48 100644
--- a/addons/event/report/event_event_templates.xml
+++ b/addons/event/report/event_event_templates.xml
@@ -18,7 +18,7 @@
- ( to )
+ ( to )
@@ -69,7 +69,7 @@
-
( to )
+
( to )
diff --git a/addons/hr_expense/report/report_expense_sheet.xml b/addons/hr_expense/report/report_expense_sheet.xml
index c00f70c23fc..da7e81ad537 100644
--- a/addons/hr_expense/report/report_expense_sheet.xml
+++ b/addons/hr_expense/report/report_expense_sheet.xml
@@ -64,7 +64,7 @@
+ t-options='{"widget": "monetary", "display_currency": o.currency_id}'/>
@@ -77,7 +77,7 @@
Total
+ t-options='{"widget": "monetary", "display_currency": o.currency_id}'/>
diff --git a/addons/hr_expense/views/report_expense.xml b/addons/hr_expense/views/report_expense.xml
index 90c92c05792..f64e5b23714 100644
--- a/addons/hr_expense/views/report_expense.xml
+++ b/addons/hr_expense/views/report_expense.xml
@@ -55,7 +55,7 @@
+ t-options='{"widget": "monetary", "display_currency": line.currency_id}'/>
@@ -75,7 +75,7 @@
+ t-options='{"widget": "monetary", "display_currency": line.currency_id}'/>
diff --git a/addons/hr_payroll/views/report_payslip.xml b/addons/hr_payroll/views/report_payslip.xml
index 43b329f4107..b0809a6716f 100644
--- a/addons/hr_payroll/views/report_payslip.xml
+++ b/addons/hr_payroll/views/report_payslip.xml
@@ -20,7 +20,7 @@
Address
+ t-options='{"widget": "contact", "fields": ["address", "name", "phone", "fax"], "no_marker": True, "phone_icons": True}'/>
diff --git a/addons/hr_payroll/views/report_payslipdetails.xml b/addons/hr_payroll/views/report_payslipdetails.xml
index e72e2bfa620..356b43df714 100644
--- a/addons/hr_payroll/views/report_payslipdetails.xml
+++ b/addons/hr_payroll/views/report_payslipdetails.xml
@@ -20,7 +20,7 @@
Address
+ t-options='{"widget": "contact", "fields": ["address", "name", "phone", "fax"], "no_marker": True, "phone_icons": True}'/>
diff --git a/addons/l10n_fr_hr_payroll/views/report_l10nfrfichepaye.xml b/addons/l10n_fr_hr_payroll/views/report_l10nfrfichepaye.xml
index 3970f0cc290..e4c0b06ba87 100644
--- a/addons/l10n_fr_hr_payroll/views/report_l10nfrfichepaye.xml
+++ b/addons/l10n_fr_hr_payroll/views/report_l10nfrfichepaye.xml
@@ -17,7 +17,7 @@
SIRET:
+ t-options='{"widget": "contact", "fields": ["address", "name"], "no_marker": True}' />SIRET:
APE:
@@ -25,7 +25,7 @@
+ t-options='{"widget": "contact", "fields": ["address", "name"], "no_marker": True}'/>
diff --git a/addons/l10n_in_hr_payroll/views/report_payslipdetails.xml b/addons/l10n_in_hr_payroll/views/report_payslipdetails.xml
index b35cd67b67b..4356519c564 100644
--- a/addons/l10n_in_hr_payroll/views/report_payslipdetails.xml
+++ b/addons/l10n_in_hr_payroll/views/report_payslipdetails.xml
@@ -20,7 +20,7 @@
Address
+ t-options='{"widget": "contact", "fields": ["address", "name", "phone", "fax"], "no_marker": True, "phone_icons": True}'/>
diff --git a/addons/lunch/report/lunch_order_templates.xml b/addons/lunch/report/lunch_order_templates.xml
index 1b22bb3dd99..8ff36c6920f 100644
--- a/addons/lunch/report/lunch_order_templates.xml
+++ b/addons/lunch/report/lunch_order_templates.xml
@@ -9,7 +9,7 @@
+ t-options='{"widget": "contact", "fields": ["address", "name"], "no_marker": True}' />
@@ -51,7 +51,7 @@
+ t-options='{"widget": "monetary", "display_currency": user.company_id.currency_id}'/>
diff --git a/addons/mrp_repair/report/mrp_repair_templates_repair_order.xml b/addons/mrp_repair/report/mrp_repair_templates_repair_order.xml
index 3a12c225066..00e32ef9345 100644
--- a/addons/mrp_repair/report/mrp_repair_templates_repair_order.xml
+++ b/addons/mrp_repair/report/mrp_repair_templates_repair_order.xml
@@ -14,19 +14,19 @@
Invoice address:
+ t-options='{"widget": "contact", "fields": ["address", "name", "phone", "fax"], "no_marker": True, "phone_icons": True}'/>
VAT:
Shipping address :
+ t-options='{"widget": "contact", "fields": ["address", "name", "phone", "fax"], "no_marker": True, "phone_icons": True}'/>
VAT:
+ t-options='{"widget": "contact", "fields": ["address", "name"], "no_marker": True}' />
@@ -88,7 +88,7 @@
+ t-options='{"widget": "monetary", "display_currency": o.pricelist_id.currency_id}'/>
@@ -112,7 +112,7 @@
+ t-options='{"widget": "monetary", "display_currency": o.pricelist_id.currency_id}'/>
@@ -127,21 +127,21 @@
Total Without Taxes
+ t-options='{"widget": "monetary", "display_currency": o.pricelist_id.currency_id}'/>
Taxes
+ t-options='{"widget": "monetary", "display_currency": o.pricelist_id.currency_id}'/>
Total
+ t-options='{"widget": "monetary", "display_currency": o.pricelist_id.currency_id}'/>
diff --git a/addons/point_of_sale/views/report_statement.xml b/addons/point_of_sale/views/report_statement.xml
index bff915b4bf4..82fe782d189 100644
--- a/addons/point_of_sale/views/report_statement.xml
+++ b/addons/point_of_sale/views/report_statement.xml
@@ -34,11 +34,11 @@
Starting Balance :
-
+
Ending Balance :
-
+
@@ -56,7 +56,7 @@
+ t-options='{"widget": "monetary", "display_currency": res_company.currency_id}'/>
diff --git a/addons/purchase/views/report_purchaseorder.xml b/addons/purchase/views/report_purchaseorder.xml
index 158c411de1e..ad3896dd19d 100644
--- a/addons/purchase/views/report_purchaseorder.xml
+++ b/addons/purchase/views/report_purchaseorder.xml
@@ -11,18 +11,18 @@
Shipping address:
+ t-options='{"widget": "contact", "fields": ["address", "name", "phone", "fax"], "no_marker": True, "phone_icons": True}'/>
+ t-options='{"widget": "contact", "fields": ["address", "phone", "fax"], "no_marker": True, "phone_icons": True}'/>
+ t-options='{"widget": "contact", "fields": ["address", "name", "phone", "fax"], "no_marker": True, "phone_icons": True}'/>
VAT:
@@ -76,7 +76,7 @@
+ t-options='{"widget": "monetary", "display_currency": o.currency_id}'/>
@@ -89,21 +89,21 @@
Total Without Taxes
+ t-options='{"widget": "monetary", "display_currency": o.currency_id}'/>
Taxes
+ t-options='{"widget": "monetary", "display_currency": o.currency_id}'/>
Total
+ t-options='{"widget": "monetary", "display_currency": o.currency_id}'/>
diff --git a/addons/purchase/views/report_purchasequotation.xml b/addons/purchase/views/report_purchasequotation.xml
index 104f34527e4..765a108f306 100644
--- a/addons/purchase/views/report_purchasequotation.xml
+++ b/addons/purchase/views/report_purchasequotation.xml
@@ -12,17 +12,17 @@
Shipping address:
+ t-options='{"widget": "contact", "fields": ["address", "name", "phone", "fax"], "no_marker": True, "phone_icons": True}'/>
+ t-options='{"widget": "contact", "fields": ["address", "phone", "fax"], "no_marker": True, "phone_icons": True}'/>
+ t-options='{"widget": "contact", "fields": ["address", "name", "phone", "fax"], "no_marker": True, "phone_icons": True}'/>
VAT:
diff --git a/addons/report/views/layouts.xml b/addons/report/views/layouts.xml
index 39494368b46..f4bebe09cd2 100644
--- a/addons/report/views/layouts.xml
+++ b/addons/report/views/layouts.xml
@@ -108,7 +108,7 @@
diff --git a/addons/report_intrastat/report/report_intrastat_invoice_template.xml b/addons/report_intrastat/report/report_intrastat_invoice_template.xml
index c40c3b8661f..5660ad8f816 100644
--- a/addons/report_intrastat/report/report_intrastat_invoice_template.xml
+++ b/addons/report_intrastat/report/report_intrastat_invoice_template.xml
@@ -7,7 +7,7 @@
+ t-options='{"widget": "contact", "fields": ["address", "name"], "no_marker": True}' />
@@ -67,7 +67,7 @@
+ t-options='{"widget": "monetary", "display_currency": o.currency_id}'/>
@@ -79,19 +79,19 @@
Total Without Taxes
-
+
Taxes
-
+
Total
-
+
@@ -112,7 +112,7 @@
+ t-options='{"widget": "monetary", "display_currency": o.currency_id}'/>
diff --git a/addons/sale/views/report_invoice_layouted.xml b/addons/sale/views/report_invoice_layouted.xml
index 2762c1e9532..48094594501 100644
--- a/addons/sale/views/report_invoice_layouted.xml
+++ b/addons/sale/views/report_invoice_layouted.xml
@@ -49,11 +49,11 @@
+ t-options='{"widget": "monetary", "display_currency": o.currency_id}'/>
+ t-options='{"widget": "monetary", "display_currency": o.currency_id}'/>
@@ -87,12 +87,12 @@
Invoicing and shipping address:
Invoicing address:
+ t-options='{"widget": "contact", "fields": ["address", "name"], "no_marker": True}'/>
TIN:
Shipping address:
+ t-options='{"widget": "contact", "fields": ["address", "name"], "no_marker": True}'/>
TIN:
diff --git a/addons/sale/views/report_saleorder.xml b/addons/sale/views/report_saleorder.xml
index f62a2d9b72d..1e45bb32385 100644
--- a/addons/sale/views/report_saleorder.xml
+++ b/addons/sale/views/report_saleorder.xml
@@ -11,18 +11,18 @@
Invoicing and shipping address:
Invoicing address:
+ t-options='{"widget": "contact", "fields": ["address", "name", "phone", "fax"], "no_marker": True, "phone_icons": True}'/>
VAT:
Shipping address:
+ t-options='{"widget": "contact", "fields": ["address", "name", "phone", "fax"], "no_marker": True, "phone_icons": True}'/>
VAT:
+ t-options='{"widget": "contact", "fields": ["address", "name"], "no_marker": True}' />
@@ -98,11 +98,11 @@
+ t-options='{"widget": "monetary", "display_currency": doc.pricelist_id.currency_id}'/>
+ t-options='{"widget": "monetary", "display_currency": doc.pricelist_id.currency_id}'/>
@@ -133,7 +133,7 @@
Total Without Taxes
+ t-options='{"widget": "monetary", "display_currency": doc.pricelist_id.currency_id}'/>
@@ -148,7 +148,7 @@
Total
+ t-options='{"widget": "monetary", "display_currency": doc.pricelist_id.currency_id}'/>
diff --git a/addons/stock/report/report_deliveryslip.xml b/addons/stock/report/report_deliveryslip.xml
index fec4e8973af..fe91111ed0d 100644
--- a/addons/stock/report/report_deliveryslip.xml
+++ b/addons/stock/report/report_deliveryslip.xml
@@ -13,7 +13,7 @@
+ t-options='{"widget": "contact", "fields": ["address", "name", "phone", "fax"], "no_marker": True}'/>
diff --git a/addons/stock/report/report_stockpicking_operations.xml b/addons/stock/report/report_stockpicking_operations.xml
index bcec910e5f4..bd77df3eaf4 100644
--- a/addons/stock/report/report_stockpicking_operations.xml
+++ b/addons/stock/report/report_stockpicking_operations.xml
@@ -24,12 +24,12 @@
Delivery Address:
+ t-options='{"widget": "contact", "fields": ["address", "name", "phone", "fax"], "no_marker": True, "phone_icons": True}'/>
Warehouse Address:
+ t-options='{"widget": "contact", "fields": ["address", "name", "phone", "fax"], "no_marker": True, "phone_icons": True}'/>
@@ -44,7 +44,7 @@
+ t-options='{"widget": "contact", "fields": ["name", "phone"], "no_marker": True, "phone_icons": True}'/>
VAT:
diff --git a/addons/website/views/website_templates.xml b/addons/website/views/website_templates.xml
index 4091d4cc4d1..fbcbe77aa67 100644
--- a/addons/website/views/website_templates.xml
+++ b/addons/website/views/website_templates.xml
@@ -904,7 +904,7 @@ Sitemap: sitemap.xml
-
diff --git a/addons/website_blog/views/website_blog_templates.xml b/addons/website_blog/views/website_blog_templates.xml
index 2fa39cef5da..2b93c8280af 100644
--- a/addons/website_blog/views/website_blog_templates.xml
+++ b/addons/website_blog/views/website_blog_templates.xml
@@ -136,7 +136,7 @@
-
+
Untitled Post
@@ -144,12 +144,12 @@
-
-
-
+
+
comment
@@ -211,9 +211,9 @@
diff --git a/addons/website_project_issue/views/project_issue_templates.xml b/addons/website_project_issue/views/project_issue_templates.xml
index e7e88b05930..a0bdc8338a1 100644
--- a/addons/website_project_issue/views/project_issue_templates.xml
+++ b/addons/website_project_issue/views/project_issue_templates.xml
@@ -88,19 +88,19 @@
- Date:
+ Date:
diff --git a/addons/website_project_issue_sheet/views/project_issue_templates.xml b/addons/website_project_issue_sheet/views/project_issue_templates.xml
index 4c27cd4b171..b3ff8ec7c4b 100644
--- a/addons/website_project_issue_sheet/views/project_issue_templates.xml
+++ b/addons/website_project_issue_sheet/views/project_issue_templates.xml
@@ -17,7 +17,7 @@
-
+
diff --git a/addons/website_quote/views/report_saleorder.xml b/addons/website_quote/views/report_saleorder.xml
index 1a2ad2606b5..b0bfcbdb5a1 100644
--- a/addons/website_quote/views/report_saleorder.xml
+++ b/addons/website_quote/views/report_saleorder.xml
@@ -38,7 +38,7 @@
diff --git a/addons/website_quote/views/website_quotation.xml b/addons/website_quote/views/website_quotation.xml
index 15865b78ad7..343e737a734 100644
--- a/addons/website_quote/views/website_quotation.xml
+++ b/addons/website_quote/views/website_quotation.xml
@@ -62,7 +62,7 @@
@@ -72,11 +72,11 @@
+ t-options='{"widget": "monetary", "display_currency": quotation.pricelist_id.currency_id}'/>
+ t-options='{"widget": "monetary", "display_currency": quotation.pricelist_id.currency_id}'/>
@@ -103,7 +103,7 @@
Subtotal:
-
+
@@ -112,7 +112,7 @@
Taxes:
-
+
@@ -120,7 +120,7 @@
Total:
-
+
@@ -224,22 +224,22 @@
Your advantage:
+ t-options='{"widget": "monetary", "display_currency": quotation.pricelist_id.currency_id}'/>
+ t-options='{"widget": "monetary", "display_currency": quotation.pricelist_id.currency_id}'/>
( Incl. tax)
+ t-options='{"widget": "monetary", "display_currency": quotation.pricelist_id.currency_id}'/> Incl. tax)
@@ -296,7 +296,7 @@
I agree that by signing this proposal, I
accept it on the behalf of , for an amount of
+ t-options='{"widget": "monetary", "display_currency": quotation.pricelist_id.currency_id}'/>
with payment terms: .
@@ -323,7 +323,7 @@
I agree that by paying this proposal, I
accept it on the behalf of , for an amount of
+ t-options='{"widget": "monetary", "display_currency": quotation.pricelist_id.currency_id}'/>
with payment terms: .
@@ -474,7 +474,7 @@
Bill To:
-
@@ -483,7 +483,7 @@
Ship To:
-
@@ -494,7 +494,7 @@
Your Contact:
-
@@ -591,7 +591,7 @@
@@ -703,7 +703,7 @@
Expired
-
+
diff --git a/addons/website_sale/views/report_shop_saleorder.xml b/addons/website_sale/views/report_shop_saleorder.xml
index 6c0954f6c20..e6ea9306f0d 100644
--- a/addons/website_sale/views/report_shop_saleorder.xml
+++ b/addons/website_sale/views/report_shop_saleorder.xml
@@ -35,7 +35,7 @@
+ t-options='{"widget": "monetary", "display_currency": doc.pricelist_id.currency_id}'/>
diff --git a/addons/website_sale/views/templates.xml b/addons/website_sale/views/templates.xml
index 04256896248..ee64dadd3aa 100644
--- a/addons/website_sale/views/templates.xml
+++ b/addons/website_sale/views/templates.xml
@@ -76,7 +76,7 @@
@@ -366,7 +366,7 @@
-
+
Product Name
@@ -420,7 +420,7 @@
-
+
@@ -504,7 +504,7 @@
@@ -524,7 +524,7 @@
-
-
+
@@ -788,8 +788,8 @@
-
-
+
+
@@ -846,7 +846,7 @@
-
+
@@ -886,7 +886,7 @@
-
+
@@ -899,9 +899,9 @@
-
+
-
+
@@ -1199,7 +1199,7 @@
-
+
@@ -1209,7 +1209,7 @@
-
+
@@ -1224,14 +1224,14 @@
@@ -1341,13 +1341,13 @@
+ t-options='{"widget": "monetary", "display_currency": order.pricelist_id.currency_id}'/>
+ t-options='{"widget": "monetary", "display_currency": order.pricelist_id.currency_id}'/>
@@ -1356,14 +1356,14 @@
Subtotal:
-
+
Taxes:
-
+
@@ -1371,7 +1371,7 @@
Total:
+ t-options='{"widget": "monetary", "display_currency": order.pricelist_id.currency_id}'/>
@@ -1392,7 +1392,7 @@
Total:
-
+
@@ -1404,14 +1404,14 @@
@@ -1459,19 +1459,19 @@
Subtotal:
-
+
Taxes:
-
+
Total:
-
+
diff --git a/addons/website_sale_delivery/views/website_sale_delivery.xml b/addons/website_sale_delivery/views/website_sale_delivery.xml
index 78443297289..ec89155d21d 100644
--- a/addons/website_sale_delivery/views/website_sale_delivery.xml
+++ b/addons/website_sale_delivery/views/website_sale_delivery.xml
@@ -7,7 +7,7 @@
Delivery:
-
-
+
@@ -57,7 +57,7 @@
@@ -88,7 +88,7 @@
-
+
@@ -104,14 +104,14 @@
diff --git a/addons/website_sale_stock/views/templates.xml b/addons/website_sale_stock/views/templates.xml
index fb8965e9c32..a8b72aaeb3e 100644
--- a/addons/website_sale_stock/views/templates.xml
+++ b/addons/website_sale_stock/views/templates.xml
@@ -12,7 +12,7 @@
-
+
Shipped
diff --git a/addons/website_slides/view/website_slides.xml b/addons/website_slides/view/website_slides.xml
index 47e572c0eaa..5f924f59a06 100644
--- a/addons/website_slides/view/website_slides.xml
+++ b/addons/website_slides/view/website_slides.xml
@@ -580,7 +580,7 @@
-
+
on
diff --git a/doc/howtos/website/field-format b/doc/howtos/website/field-format
index 2fbaa541f8c..930ccfa0fa6 100644
--- a/doc/howtos/website/field-format
+++ b/doc/howtos/website/field-format
@@ -10,7 +10,7 @@ diff --git a/academy/templates.xml b/academy/templates.xml
-
Last modified:
-+
Last modified:
++
Last modified:
diff --git a/doc/howtos/website/field-widget b/doc/howtos/website/field-widget
index 667cb8a65f1..ea3ddce562a 100644
--- a/doc/howtos/website/field-widget
+++ b/doc/howtos/website/field-widget
@@ -9,8 +9,8 @@ diff --git a/academy/templates.xml b/academy/templates.xml
--
Last modified:
-+
Last modified:
+-
Last modified:
++
Last modified:
diff --git a/doc/reference/qweb.rst b/doc/reference/qweb.rst
index fc4ecd43aca..1fa29212998 100644
--- a/doc/reference/qweb.rst
+++ b/doc/reference/qweb.rst
@@ -334,7 +334,7 @@ The ``t-field`` directive can only be used when performing field access
to automatically format based on field type, and is integrated in the
website's rich text edition.
-``t-field-options`` can be used to customize fields, the most common option
+``t-options`` can be used to customize fields, the most common option
is ``widget``, other options are field- or widget-dependent.
debugging
diff --git a/doc/reference/reports.rst b/doc/reference/reports.rst
index f1b4acfd479..64545fd5729 100644
--- a/doc/reference/reports.rst
+++ b/doc/reference/reports.rst
@@ -161,7 +161,7 @@ For example, let's look at the Sale Order report from the Sale module::
Invoice and shipping address:
Invoice address:
-
+
<...>
diff --git a/openerp/addons/base/ir/ir_qweb.xml b/openerp/addons/base/ir/ir_qweb.xml
index e4d135119c0..b882a847d30 100644
--- a/openerp/addons/base/ir/ir_qweb.xml
+++ b/openerp/addons/base/ir/ir_qweb.xml
@@ -12,7 +12,7 @@
-
+
From 70a69bf9bc7e35200f0892e421e81ad643ab915a Mon Sep 17 00:00:00 2001
From: Christophe Matthieu
Date: Tue, 5 Jul 2016 15:29:31 +0200
Subject: [PATCH 8/9] [FIX] website/im_livechat: use new api to get asset
Issue: can't get asset content, self don't have env attribute, wrong attributes send.
---
addons/im_livechat/controllers/main.py | 2 +-
addons/website/controllers/main.py | 3 +--
2 files changed, 2 insertions(+), 3 deletions(-)
diff --git a/addons/im_livechat/controllers/main.py b/addons/im_livechat/controllers/main.py
index d2183ef2afa..c80630deff3 100644
--- a/addons/im_livechat/controllers/main.py
+++ b/addons/im_livechat/controllers/main.py
@@ -12,7 +12,7 @@ class LivechatController(http.Controller):
def livechat_lib(self, ext, **kwargs):
# _get_asset return the bundle html code (script and link list) but we want to use the attachment content
xmlid = 'im_livechat.external_lib'
- files, remains = request.registry["ir.qweb"]._get_asset_content(request.cr, request.uid, xmlid, request.context)
+ files, remains = request.env["ir.qweb"]._get_asset_content(xmlid, options=request.context)
asset = AssetsBundle(xmlid, files, remains)
mock_attachment = getattr(asset, ext)()
diff --git a/addons/website/controllers/main.py b/addons/website/controllers/main.py
index fe1439f7dfa..0b1d9bd78af 100644
--- a/addons/website/controllers/main.py
+++ b/addons/website/controllers/main.py
@@ -344,7 +344,6 @@ class Website(openerp.addons.web.controllers.main.Home):
"""
cr, uid, context, pool = request.cr, request.uid, request.context, request.registry
view = pool["ir.ui.view"]
- qweb = pool["ir.qweb"]
context = dict(request.context or {}, active_test=True)
@@ -356,7 +355,7 @@ class Website(openerp.addons.web.controllers.main.Home):
set_active(enable, True)
if get_bundle:
- return qweb._get_asset(cr, uid, 'web.assets_frontend', context)
+ return request.env["ir.qweb"]._get_asset('web.assets_frontend', options=context)
return True
From 253a0f63110e91f7b690dc3b0111309001ffa051 Mon Sep 17 00:00:00 2001
From: Christophe Matthieu
Date: Tue, 5 Jul 2016 15:27:41 +0200
Subject: [PATCH 9/9] [FIX] qweb: no automatic asset invalidation except in
dev_mode
In non-xml-debug mode we want assets to be cached forever, and the admin can force a cache clear
by restarting the server after updating the source code
---
openerp/addons/base/ir/ir_qweb/ir_qweb.py | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/openerp/addons/base/ir/ir_qweb/ir_qweb.py b/openerp/addons/base/ir/ir_qweb/ir_qweb.py
index e452593e7d0..88e6ee95307 100644
--- a/openerp/addons/base/ir/ir_qweb/ir_qweb.py
+++ b/openerp/addons/base/ir/ir_qweb/ir_qweb.py
@@ -175,8 +175,10 @@ class IrQWeb(models.AbstractModel, QWeb):
# method called by computing code
@tools.conditional(
+ # in non-xml-debug mode we want assets to be cached forever, and the admin can force a cache clear
+ # by restarting the server after updating the source code (or using the "Clear server cache" in debug tools)
'xml' not in tools.config['dev_mode'],
- tools.ormcache('xmlid', 'options.get("lang", "en_US")', 'css', 'js', 'debug', 'async', 'values.get("time_cache_assets")'),
+ tools.ormcache('xmlid', 'options.get("lang", "en_US")', 'css', 'js', 'debug', 'async'),
)
def _get_asset(self, xmlid, options, css=True, js=True, debug=False, async=False, values=None):
files, remains = self._get_asset_content(xmlid, options)