[MERGE] forward port of branch 8.0 up to 22bd3bd

This commit is contained in:
Christophe Simonis
2015-07-01 18:11:32 +02:00
15 changed files with 140 additions and 75 deletions
@@ -203,7 +203,7 @@ class account_analytic_line(osv.osv):
unit_price = total_price*-1.0 / total_qty
factor = self.pool['hr_timesheet_invoice.factor'].browse(cr, uid, factor_id, context=uom_context)
factor_name = factor.customer_name
factor_name = factor.customer_name or ''
curr_invoice_line = {
'price_unit': unit_price,
'quantity': total_qty,
@@ -73,7 +73,7 @@ class hr_timesheet_report(osv.osv):
htss.state"""
def _from(self):
return super(hr_timesheet_report, self)._from() + "left join hr_timesheet_sheet_sheet as htss ON (hat.sheet_id=htss.id) join totals as t on (t.sheet_id = hat.sheet_id and t.date = aal.date)"
return super(hr_timesheet_report, self)._from() + "left join hr_timesheet_sheet_sheet as htss ON (hat.sheet_id=htss.id) left join totals as t on (t.sheet_id = hat.sheet_id and t.date = aal.date)"
def _group_by(self):
return super(hr_timesheet_report, self)._group_by() + """,
+3 -3
View File
@@ -33,14 +33,14 @@ class procurement_order(osv.osv):
self.check(cr, uid, [procurement_id], context=context)
return procurement_id
def run(self, cr, uid, ids, context=None):
def run(self, cr, uid, ids, autocommit=False, context=None):
context = dict(context or {}, procurement_autorun_defer=True)
res = super(procurement_order, self).run(cr, uid, ids, context=context)
res = super(procurement_order, self).run(cr, uid, ids, autocommit=autocommit, context=context)
procurement_ids = self.search(cr, uid, [('move_dest_id.procurement_id', 'in', ids)], order='id', context=context)
if procurement_ids:
return self.run(cr, uid, procurement_ids, context=context)
return self.run(cr, uid, procurement_ids, autocommit=autocommit, context=context)
return res
class stock_move(osv.osv):
@@ -56,8 +56,10 @@ class stock_history(osv.osv):
for product_id in ids:
line_ids.add(product_id)
line_ids = list(line_ids)
cr.execute('SELECT id, product_id, price_unit_on_quant, company_id, quantity FROM stock_history WHERE id in %s', (tuple(line_ids),))
lines_rec = cr.dictfetchall()
lines_rec = {}
if line_ids:
cr.execute('SELECT id, product_id, price_unit_on_quant, company_id, quantity FROM stock_history WHERE id in %s', (tuple(line_ids),))
lines_rec = cr.dictfetchall()
lines_dict = dict((line['id'], line) for line in lines_rec)
product_ids = list(set(line_rec['product_id'] for line_rec in lines_rec))
products_rec = self.pool['product.product'].read(cr, uid, product_ids, ['cost_method', 'product_tmpl_id'], context=context)
@@ -565,7 +565,7 @@ openerp.web_calendar = function(instance) {
attendee_showed += 1;
if (attendee_showed<= MAX_ATTENDEES) {
if (self.avatar_model !== null) {
the_title_avatar += '<img title="' + self.all_attendees[the_attendee_people] + '" class="attendee_head" \
the_title_avatar += '<img title="' + _.escape(self.all_attendees[the_attendee_people]) + '" class="attendee_head" \
src="/web/binary/image?model=' + self.avatar_model + '&field=image_small&id=' + the_attendee_people + '"></img>';
}
else {
@@ -573,12 +573,12 @@ openerp.web_calendar = function(instance) {
tempColor = (self.all_filters[the_attendee_people] !== undefined)
? self.all_filters[the_attendee_people].color
: (self.all_filters[-1] ? self.all_filters[-1].color : 1);
the_title_avatar += '<i class="fa fa-user attendee_head color_'+tempColor+'" title="' + self.all_attendees[the_attendee_people] + '" ></i>';
the_title_avatar += '<i class="fa fa-user attendee_head color_'+tempColor+'" title="' + _.escape(self.all_attendees[the_attendee_people]) + '" ></i>';
}//else don't add myself
}
}
else {
attendee_other += self.all_attendees[the_attendee_people] +", ";
attendee_other += _.escape(self.all_attendees[the_attendee_people]) +", ";
}
}
);
@@ -183,7 +183,7 @@
if (!this.$target.has('.media_iframe_video_size')) {
var editor = '<div class="css_editable_mode_display">&nbsp;</div>';
var size = '<div class="media_iframe_video_size">&nbsp;</div>';
this.$target.html(editor+size+'<iframe src="'+this.$target.data("src")+'" frameborder="0" allowfullscreen="allowfullscreen"></iframe>');
this.$target.html(editor+size+'<iframe src="'+_.escape(this.$target.data("src"))+'" frameborder="0" allowfullscreen="allowfullscreen"></iframe>');
}
},
});
@@ -4,6 +4,7 @@ import json
from openerp import SUPERUSER_ID
from openerp.addons.web import http
from openerp.addons.web.http import request
from openerp.tools import html_escape as escape
class google_map(http.Controller):
@@ -48,12 +49,13 @@ class google_map(http.Controller):
}
request.context.update({'show_address': True})
for partner in partner_obj.browse(cr, SUPERUSER_ID, partners_ids, context=context):
# TODO in master, do not use `escape` but `t-esc` in the qweb template.
partner_data["partners"].append({
'id': partner.id,
'name': partner.name,
'address': '\n'.join(partner.name_get()[0][1].split('\n')[1:]),
'latitude': partner.partner_latitude,
'longitude': partner.partner_longitude,
'name': escape(partner.name),
'address': escape('\n'.join(partner.name_get()[0][1].split('\n')[1:])),
'latitude': escape(str(partner.partner_latitude)),
'longitude': escape(str(partner.partner_longitude)),
})
# generate the map
+17
View File
@@ -0,0 +1,17 @@
Slovenia, 2015-06-29
Mentis d.o.o. agrees to the terms of the Odoo Corporate Contributor License
Agreement v1.0.
I declare that I am authorized and able to make this agreement and sign this
declaration.
Signed,
Dušan Laznik dusan.laznik@mentis.si https://github.com/laznikd
List of contributors:
Dušan Laznik dusan.laznik@mentis.si https://github.com/laznikd
Aleksander Dirntiš sandi.dirntis@mentis.si https://github.com/adirntis
Matjaž Kalič matjaz.kalic@mentis.si https://github.com/kalicm
+60 -31
View File
@@ -37,6 +37,19 @@ class ConversionNotFound(ValueError): pass
class ir_fields_converter(models.Model):
_name = 'ir.fields.converter'
@api.model
def _format_import_error(self, error_type, error_msg, error_params=(), error_args=None):
# sanitize error params for later formatting by the import system
sanitize = lambda p: p.replace('%', '%%') if isinstance(p, basestring) else p
if error_params:
if isinstance(error_params, basestring):
error_params = sanitize(error_params)
elif isinstance(error_params, dict):
error_params = dict((k, sanitize(v)) for k, v in error_params.iteritems())
elif isinstance(error_params, tuple):
error_params = tuple(map(sanitize, error_params))
return error_type(error_msg % error_params, error_args)
@api.model
def for_model(self, model, fromtype=str):
""" Returns a converter object for the model. A converter is a
@@ -148,29 +161,34 @@ class ir_fields_converter(models.Model):
if value.lower() in falses:
return False, []
return True, [ImportWarning(
_(u"Unknown value '%s' for boolean field '%%(field)s', assuming '%s'")
% (value, yes), {
'moreinfo': _(u"Use '1' for yes and '0' for no")
})]
return True, [self._format_import_error(
ImportWarning,
_(u"Unknown value '%s' for boolean field '%%(field)s', assuming '%s'"),
(value, yes),
{'moreinfo': _(u"Use '1' for yes and '0' for no")}
)]
@api.model
def _str_to_integer(self, model, field, value):
try:
return int(value), []
except ValueError:
raise ValueError(
_(u"'%s' does not seem to be an integer for field '%%(field)s'")
% value)
raise self._format_import_error(
ValueError,
_(u"'%s' does not seem to be an integer for field '%%(field)s'"),
value
)
@api.model
def _str_to_float(self, model, field, value):
try:
return float(value), []
except ValueError:
raise ValueError(
_(u"'%s' does not seem to be a number for field '%%(field)s'")
% value)
raise self._format_import_error(
ValueError,
_(u"'%s' does not seem to be a number for field '%%(field)s'"),
value
)
@api.model
def _str_id(self, model, field, value):
@@ -184,10 +202,12 @@ class ir_fields_converter(models.Model):
time.strptime(value, DEFAULT_SERVER_DATE_FORMAT)
return value, []
except ValueError:
raise ValueError(
_(u"'%s' does not seem to be a valid date for field '%%(field)s'") % value, {
'moreinfo': _(u"Use the format '%s'") % u"2012-12-31"
})
raise self._format_import_error(
ValueError,
_(u"'%s' does not seem to be a valid date for field '%%(field)s'"),
value,
{'moreinfo': _(u"Use the format '%s'") % u"2012-12-31"}
)
@api.model
def _input_tz(self):
@@ -215,10 +235,12 @@ class ir_fields_converter(models.Model):
parsed_value = datetime.datetime.strptime(
value, DEFAULT_SERVER_DATETIME_FORMAT)
except ValueError:
raise ValueError(
_(u"'%s' does not seem to be a valid datetime for field '%%(field)s'") % value, {
'moreinfo': _(u"Use the format '%s'") % u"2012-12-31 23:59:59"
})
raise self._format_import_error(
ValueError,
_(u"'%s' does not seem to be a valid datetime for field '%%(field)s'"),
value,
{'moreinfo': _(u"Use the format '%s'") % u"2012-12-31 23:59:59"}
)
input_tz = self._input_tz()# Apply input tz to the parsed naive datetime
dt = input_tz.localize(parsed_value, is_dst=False)
@@ -251,12 +273,12 @@ class ir_fields_converter(models.Model):
if value == unicode(item) or value in labels:
return item, []
raise ValueError(
_(u"Value '%s' not found in selection field '%%(field)s'") % (
value), {
'moreinfo': [_label or unicode(item) for item, _label in selection
if _label or item]
})
raise self._format_import_error(
ValueError,
_(u"Value '%s' not found in selection field '%%(field)s'"),
value,
{'moreinfo': [_label or unicode(item) for item, _label in selection if _label or item]}
)
@api.model
def db_id_for(self, model, field, subfield, value):
@@ -298,8 +320,10 @@ class ir_fields_converter(models.Model):
id = tentative_id
except psycopg2.DataError:
# type error
raise ValueError(
_(u"Invalid database id '%s' for the field '%%(field)s'") % value,
raise self._format_import_error(
ValueError,
_(u"Invalid database id '%s' for the field '%%(field)s'"),
value,
{'moreinfo': action})
elif subfield == 'id':
field_type = _(u"external id")
@@ -321,12 +345,17 @@ class ir_fields_converter(models.Model):
% (len(ids))))
id, _name = ids[0]
else:
raise Exception(_(u"Unknown sub-field '%s'") % subfield)
raise self._format_import_error(
Exception,
_(u"Unknown sub-field '%s'"),
subfield
)
if id is None:
raise ValueError(
_(u"No matching record found for %(field_type)s '%(value)s' in field '%%(field)s'")
% {'field_type': field_type, 'value': value},
raise self._format_import_error(
ValueError,
_(u"No matching record found for %(field_type)s '%(value)s' in field '%%(field)s'"),
{'field_type': field_type, 'value': value},
{'moreinfo': action})
return id, field_type, warnings
+11 -2
View File
@@ -111,6 +111,12 @@ class test_ids_stuff(ImporterCase):
self.import_(['id', 'value'], [['somexmlid', '1234567']])
self.assertEqual([1234567], values(self.read()))
def test_wrong_format(self):
self.assertEqual(
self.import_(['value'], [['50%']]),
error(1, u"'50%' does not seem to be an integer for field 'unknown'"))
class test_boolean_field(ImporterCase):
model_name = 'export.boolean'
@@ -163,10 +169,13 @@ class test_boolean_field(ImporterCase):
# Problem: OpenOffice (and probably excel) output localized booleans
['VRAI'],
[u'OFF'],
[u'是的'],
['!&%#${}'],
['%(field)s'],
]),
ok(8))
ok(11))
self.assertEqual(
[True] * 8,
[True] * 11,
values(self.read()))
class test_integer_field(ImporterCase):
+16 -17
View File
@@ -30,7 +30,7 @@ import logging
import pytz
import xmlrpclib
from openerp.tools import float_round, frozendict, html_sanitize, ustr
from openerp.tools import float_round, frozendict, html_sanitize, ustr, OrderedSet
from openerp.tools import DEFAULT_SERVER_DATE_FORMAT as DATE_FORMAT
from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT as DATETIME_FORMAT
@@ -1606,34 +1606,33 @@ class _RelationalMulti(_Relational):
return value.with_env(record.env)
elif isinstance(value, list):
# value is a list of record ids or commands
if not record.id:
record = record.browse() # new record has no value
result = record[self.name]
# modify result with the commands;
# beware to not introduce duplicates in result
comodel = record.env[self.comodel_name]
ids = OrderedSet(record[self.name].ids)
# modify ids with the commands
for command in value:
if isinstance(command, (tuple, list)):
if command[0] == 0:
result += result.new(command[2])
ids.add(comodel.new(command[2]).id)
elif command[0] == 1:
result.browse(command[1]).update(command[2])
result += result.browse(command[1]) - result
comodel.browse(command[1]).update(command[2])
ids.add(command[1])
elif command[0] == 2:
# note: the record will be deleted by write()
result -= result.browse(command[1])
ids.discard(command[1])
elif command[0] == 3:
result -= result.browse(command[1])
ids.discard(command[1])
elif command[0] == 4:
result += result.browse(command[1]) - result
ids.add(command[1])
elif command[0] == 5:
result = result.browse()
ids.clear()
elif command[0] == 6:
result = result.browse(command[2])
ids = OrderedSet(command[2])
elif isinstance(command, dict):
result += result.new(command)
ids.add(comodel.new(command).id)
else:
result += result.browse(command) - result
return result
ids.add(command)
# return result as a recordset
return comodel.browse(list(ids))
elif not value:
return self.null(record.env)
raise ValueError("Wrong value for %s: %s" % (self, value))
+2 -5
View File
@@ -1194,11 +1194,8 @@ class BaseModel(object):
type = 'warning' if isinstance(exception, Warning) else 'error'
# logs the logical (not human-readable) field name for automated
# processing of response, but injects human readable in message
# Before applying string substitution, we need to escape all '%'
# characters that are not part of substitutable reference.
regex = '%%(?!\((%s)\)s)' % '|'.join(base.keys())
message = unicode(re.sub(regex, '%%', exception.args[0])) % base
record = dict(base, type=type, field=field, message=message)
record = dict(base, type=type, field=field,
message=unicode(exception.args[0]) % base)
if len(exception.args) > 1 and exception.args[1]:
record.update(exception.args[1])
log(record)
+12 -1
View File
@@ -37,7 +37,7 @@ import threading
import time
import werkzeug.utils
import zipfile
from collections import defaultdict, Mapping
from collections import defaultdict, Mapping, OrderedDict
from datetime import datetime
from itertools import islice, izip, groupby
from lxml import etree
@@ -1136,6 +1136,17 @@ class frozendict(dict):
def update(self, *args, **kwargs):
raise NotImplementedError("'update' not supported on frozendict")
class OrderedSet(OrderedDict):
""" A simple collection that remembers the elements insertion order. """
def __init__(self, seq=()):
super(OrderedSet, self).__init__([(x, None) for x in seq])
def add(self, elem):
self[elem] = None
def discard(self, elem):
self.pop(elem, None)
@contextmanager
def ignore(*exc):
try:
+1 -1
View File
@@ -3,7 +3,7 @@ Jinja2==2.7.3
Mako==1.0.1
MarkupSafe==0.23
Pillow==2.7.0
http://download.gna.org/pychart/PyChart-1.39.tar.gz#egg=PyChart
Python-Chart==1.39
PyYAML==3.11
Werkzeug==0.9.6
argparse==1.2.1
+2 -3
View File
@@ -87,7 +87,7 @@ def py2exe_options():
'PIL',
'poplib',
'psutil',
'pychart',
'python-chart',
'pydot',
'pyparsing',
'pyPdf',
@@ -128,7 +128,6 @@ setup(
packages=find_packages(),
package_dir={'%s' % lib_name: 'openerp'},
include_package_data=True,
dependency_links=['http://download.gna.org/pychart/'],
install_requires=[
'babel >= 1.0',
'decorator',
@@ -144,7 +143,7 @@ setup(
'psutil', # windows binary code.google.com/p/psutil/downloads/list
'psycogreen',
'psycopg2 >= 2.2',
'pychart', # not on pypi, use: pip install http://download.gna.org/pychart/PyChart-1.39.tar.gz
'python-chart',
'pydot',
'pyparsing < 2',
'pypdf',