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 @@ - - + +
Total
@@ -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) - + &nbsp; @@ -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 @@ - +
Total
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 @@
- +
@@ -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 @@ -1065,9 +1065,9 @@ - on + on
@@ -1214,7 +1214,7 @@
- + @@ -1355,10 +1355,10 @@ member since - + last connection - + bio @@ -1374,7 +1374,7 @@ , - + @@ -1443,7 +1443,7 @@