summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristophe Simonis <chs@odoo.com>2015-08-31 19:57:20 +0200
committerChristophe Simonis <chs@odoo.com>2015-08-31 19:57:20 +0200
commit044c0625ad4644ae7abb197433309822eccdbcdc (patch)
tree82b63128bb463a893037e5d4ddd3581d839090e0
parentbd30f262ebfc1d586d4b5d96a89a56527ef6c375 (diff)
parent7faf72c0242353bf3049e7ded9074437ae1d7e82 (diff)
[MERGE] forward port of branch saas-6 up to 7faf72c
-rw-r--r--addons/calendar/calendar.py8
-rw-r--r--addons/membership/membership.py6
-rw-r--r--addons/point_of_sale/static/src/js/models.js4
-rw-r--r--addons/resource/resource.py2
-rw-r--r--addons/resource/tests/test_resource.py2
-rw-r--r--addons/web_editor/static/src/css/snippets.css7
-rw-r--r--addons/web_editor/static/src/css/snippets.sass3
-rw-r--r--addons/web_editor/static/src/js/snippets.editor.js2
-rw-r--r--addons/web_editor/static/src/js/summernote.js4
-rw-r--r--addons/web_editor/static/src/xml/snippets.xml4
-rw-r--r--addons/website_hr_recruitment/data/hr_job_demo.xml6
-rw-r--r--addons/website_sale_delivery/views/website_sale_delivery.xml5
-rw-r--r--openerp/addons/base/ir/ir_ui_view.py9
-rw-r--r--openerp/addons/test_new_api/__openerp__.py4
-rw-r--r--openerp/addons/test_new_api/demo_data.xml2
-rw-r--r--openerp/addons/test_new_api/ir.model.access.csv2
-rw-r--r--openerp/addons/test_new_api/models.py50
-rw-r--r--openerp/addons/test_new_api/tests/__init__.py1
-rw-r--r--openerp/addons/test_new_api/tests/test_no_infinite_recursion.py31
-rw-r--r--openerp/models.py42
-rw-r--r--openerp/service/server.py2
21 files changed, 160 insertions, 36 deletions
diff --git a/addons/calendar/calendar.py b/addons/calendar/calendar.py
index aa89eb6cc90..9b251ee80fd 100644
--- a/addons/calendar/calendar.py
+++ b/addons/calendar/calendar.py
@@ -1636,6 +1636,14 @@ class calendar_event(osv.Model):
self.create_attendees(cr, uid, [res], context=context)
return res
+ def export_data(self, cr, uid, ids, *args, **kwargs):
+ """ Override to convert virtual ids to ids """
+ real_ids = []
+ for real_id in get_real_ids(ids):
+ if real_id not in real_ids:
+ real_ids.append(real_id)
+ return super(calendar_event, self).export_data(cr, uid, real_ids, *args, **kwargs)
+
def read_group(self, cr, uid, domain, fields, groupby, offset=0, limit=None, context=None, orderby=False, lazy=True):
context = dict(context or {})
diff --git a/addons/membership/membership.py b/addons/membership/membership.py
index a38a7d9cdd6..41244648a59 100644
--- a/addons/membership/membership.py
+++ b/addons/membership/membership.py
@@ -293,7 +293,7 @@ class Partner(osv.osv):
store = {
'account.invoice': (_get_invoice_partner, ['state', 'invoice_line_ids', 'payment_ids'], 10),
'membership.membership_line': (_get_partner_id, ['state'], 10, ),
- 'res.partner': (lambda self, cr, uid, ids, c={}: ids, ['free_member'], 10)
+ 'res.partner': (_get_partners, ['free_member', 'membership_state', 'associate_member'], 10)
}, help="Date from which membership becomes active."),
'membership_stop': fields.function(
_membership_date,
@@ -301,7 +301,7 @@ class Partner(osv.osv):
store = {
'account.invoice': (_get_invoice_partner, ['state', 'invoice_line_ids', 'payment_ids'], 10),
'membership.membership_line': (_get_partner_id, ['state'], 10),
- 'res.partner': (lambda self, cr, uid, ids, c={}: ids, ['free_member'], 10)
+ 'res.partner': (_get_partners, ['free_member', 'membership_state', 'associate_member'], 10)
}, help="Date until which membership remains active."),
'membership_cancel': fields.function(
_membership_date,
@@ -309,7 +309,7 @@ class Partner(osv.osv):
store = {
'account.invoice': (_get_invoice_partner, ['state', 'invoice_line_ids', 'payment_ids'], 11),
'membership.membership_line': (_get_partner_id, ['state'], 10),
- 'res.partner': (lambda self, cr, uid, ids, c={}: ids, ['free_member'], 10)
+ 'res.partner': (_get_partners, ['free_member', 'membership_state', 'associate_member'], 10)
}, help="Date on which membership has been cancelled"),
}
diff --git a/addons/point_of_sale/static/src/js/models.js b/addons/point_of_sale/static/src/js/models.js
index 543e47c8128..84fc1f42634 100644
--- a/addons/point_of_sale/static/src/js/models.js
+++ b/addons/point_of_sale/static/src/js/models.js
@@ -1721,6 +1721,10 @@ exports.Order = Backbone.Model.extend({
this.select_orderline(this.get_last_orderline());
},
add_product: function(product, options){
+ if(this._printed){
+ this.destroy();
+ return this.pos.get_order().add_product(product, options);
+ }
this.assert_editable();
options = options || {};
var attr = JSON.parse(JSON.stringify(product));
diff --git a/addons/resource/resource.py b/addons/resource/resource.py
index 2c62cfe5fd3..6f558c04392 100644
--- a/addons/resource/resource.py
+++ b/addons/resource/resource.py
@@ -319,6 +319,7 @@ class resource_calendar(osv.osv):
# no calendar: try to use the default_interval, then return directly
if id is None:
+ working_interval = []
if default_interval:
working_interval = (start_dt.replace(hour=default_interval[0], minute=0, second=0),
start_dt.replace(hour=default_interval[1], minute=0, second=0))
@@ -532,7 +533,6 @@ class resource_calendar(osv.osv):
intervals = []
planned_days = 0
iterations = 0
-
current_datetime = day_date.replace(hour=0, minute=0, second=0)
while planned_days < days and iterations < 100:
diff --git a/addons/resource/tests/test_resource.py b/addons/resource/tests/test_resource.py
index 24866011b94..14afecf6b44 100644
--- a/addons/resource/tests/test_resource.py
+++ b/addons/resource/tests/test_resource.py
@@ -413,6 +413,8 @@ class TestResource(TestResourceCommon):
res = self.resource_calendar.schedule_days_get_date(cr, uid, self.calendar_id, 5, day_date=self.date1)
self.assertEqual(res.date(), datetime.strptime('2013-02-26 00:0:00', _format).date(), 'resource_calendar: wrong days scheduling')
+ res = self.resource_calendar.schedule_days_get_date(cr, uid, self.calendar_id, -2, day_date=self.date1)
+ self.assertEqual(res.date(), datetime.strptime('2013-02-08 00:00:00', _format).date(), 'resource_calendar: wrong days scheduling')
res = self.resource_calendar.schedule_days_get_date(
cr, uid, self.calendar_id, 5, day_date=self.date1,
diff --git a/addons/web_editor/static/src/css/snippets.css b/addons/web_editor/static/src/css/snippets.css
index 950e5eb4e70..b4d74eecac6 100644
--- a/addons/web_editor/static/src/css/snippets.css
+++ b/addons/web_editor/static/src/css/snippets.css
@@ -1,3 +1,4 @@
+@charset "UTF-8";
/* ---- SNIPPET EDITOR ---- {{{ */
#oe_snippets #o_left_bar, #oe_snippets #o_arrow {
position: fixed;
@@ -8,6 +9,10 @@
-moz-box-shadow: 0px 10px 10px -10px black inset;
-webkit-box-shadow: 0px 10px 10px -10px black inset;
box-shadow: 0px 10px 10px -10px black inset;
+ font-size: 14px;
+}
+#oe_snippets #o_left_bar .snippets_loading, #oe_snippets #o_arrow .snippets_loading {
+ color: white;
}
#oe_snippets #o_arrow {
z-index: 1009;
@@ -487,4 +492,4 @@
.move-important, .move-important * {
cursor: move !important;
-} \ No newline at end of file
+}
diff --git a/addons/web_editor/static/src/css/snippets.sass b/addons/web_editor/static/src/css/snippets.sass
index 2f042640b70..ed5bd46a525 100644
--- a/addons/web_editor/static/src/css/snippets.sass
+++ b/addons/web_editor/static/src/css/snippets.sass
@@ -13,6 +13,9 @@
bottom: 0
background: rgb(40,40,40)
+box-shadow(0px 10px 10px -10px black inset)
+ font-size: 14px
+ .snippets_loading
+ color: white
#o_arrow
z-index: 1009
width: 24px
diff --git a/addons/web_editor/static/src/js/snippets.editor.js b/addons/web_editor/static/src/js/snippets.editor.js
index 20fefdb4aef..fef552c0216 100644
--- a/addons/web_editor/static/src/js/snippets.editor.js
+++ b/addons/web_editor/static/src/js/snippets.editor.js
@@ -343,7 +343,7 @@ var BuildingBlock = Widget.extend({
$html.find('.o_not_editable').attr("contentEditable", false);
- $left_bar.append($html);
+ $left_bar.html($html);
// animate for list of snippet blocks
$left_bar.on('click', '.scroll-link', function (event) {
diff --git a/addons/web_editor/static/src/js/summernote.js b/addons/web_editor/static/src/js/summernote.js
index 3ce90c14496..d300f8b28ca 100644
--- a/addons/web_editor/static/src/js/summernote.js
+++ b/addons/web_editor/static/src/js/summernote.js
@@ -946,8 +946,7 @@ options.keyMap.pc['ESCAPE'] = 'cancel';
options.keyMap.mac['SHIFT+TAB'] = 'untab';
options.keyMap.mac['BACKSPACE'] = 'backspace';
-options.keyMap.mac['CMD+BACKSPACE'] = 'delete';
-options.keyMap.mac['SHIFT+BACKSPACE'] = 'delete';
+options.keyMap.mac['DELETE'] = 'delete';
options.keyMap.mac['ENTER'] = 'enter';
options.keyMap.mac['ESCAPE'] = 'cancel';
@@ -1816,7 +1815,6 @@ eventHandler.toolbar.button.updateRecentColor = function (elBtn, sEvent, sValue)
font.style.backgroundColor = sValue !== 'inherit' ? sValue : "";
}
}
- event.preventDefault();
return false;
};
diff --git a/addons/web_editor/static/src/xml/snippets.xml b/addons/web_editor/static/src/xml/snippets.xml
index ccced98af2c..0b194de6af9 100644
--- a/addons/web_editor/static/src/xml/snippets.xml
+++ b/addons/web_editor/static/src/xml/snippets.xml
@@ -20,7 +20,9 @@
<div>Insert Blocks</div>
<span class="fa fa-angle-double-right fa-1x" style="bottom: 10px;"></span>
</div>
- <div id="o_left_bar"></div>
+ <div id="o_left_bar">
+ <span class='snippets_loading'>Snippets are loading...</span>
+ </div>
</div>
</t>
diff --git a/addons/website_hr_recruitment/data/hr_job_demo.xml b/addons/website_hr_recruitment/data/hr_job_demo.xml
index 962f468b2f9..45a0fed846a 100644
--- a/addons/website_hr_recruitment/data/hr_job_demo.xml
+++ b/addons/website_hr_recruitment/data/hr_job_demo.xml
@@ -209,7 +209,7 @@
<ul class="list-group">
<li class="list-group-item">Good knowledge of the programming language</li>
<li class="list-group-item">Good knowledge of the latest web technologies</li>
- <li class="list-group-item">Good language skills in an other langages</li>
+ <li class="list-group-item">Good language skills in another language</li>
<li class="list-group-item">Contributions to open source projects</li>
<li class="list-group-item">Passion for the Internet and its culture</li>
<li class="list-group-item">Quick and autonomous learner</li>
@@ -603,7 +603,7 @@
<ul class="list-group">
<li class="list-group-item">Good knowledge of the programming language</li>
<li class="list-group-item">Good knowledge of the latest web technologies</li>
- <li class="list-group-item">Good language skills in an other langages</li>
+ <li class="list-group-item">Good language skills in another language</li>
<li class="list-group-item">Contributions to open source projects</li>
</ul>
<div class="panel-footer text-center">
@@ -731,7 +731,7 @@
<ul class="list-group">
<li class="list-group-item">Good knowledge of the programming language</li>
<li class="list-group-item">Good knowledge of the latest web technologies</li>
- <li class="list-group-item">Good language skills in an other langages</li>
+ <li class="list-group-item">Good language skills in another language</li>
<li class="list-group-item">Contributions to open source projects</li>
</ul>
<div class="panel-footer text-center">
diff --git a/addons/website_sale_delivery/views/website_sale_delivery.xml b/addons/website_sale_delivery/views/website_sale_delivery.xml
index 5a1cf73f85d..0c33577cea3 100644
--- a/addons/website_sale_delivery/views/website_sale_delivery.xml
+++ b/addons/website_sale_delivery/views/website_sale_delivery.xml
@@ -9,7 +9,7 @@
<span class="col-xs-6 text-left text-muted">
<span t-field="website_sale_order.amount_delivery" style="white-space: nowrap;" t-field-options='{
"widget": "monetary",
- "from_currency": "website_sale_order.pricelist_id.currency_id",
+ "from_currency": "website_sale_order.currency_id",
"display_currency": "user_id.partner_id.property_product_pricelist.currency_id"
}'/>
</span>
@@ -38,7 +38,8 @@
<span class="badge" t-field="delivery.price"
t-field-options='{
"widget": "monetary",
- "display_currency": "website.pricelist_id.currency_id"
+ "from_currency": "website.currency_id",
+ "display_currency": "website_sale_order.currency_id"
}'/>
<div t-field="delivery.website_description" class="text-muted"/>
</label>
diff --git a/openerp/addons/base/ir/ir_ui_view.py b/openerp/addons/base/ir/ir_ui_view.py
index c780e70a088..d23d75c7850 100644
--- a/openerp/addons/base/ir/ir_ui_view.py
+++ b/openerp/addons/base/ir/ir_ui_view.py
@@ -94,10 +94,17 @@ def get_view_arch_from_file(filename, xmlid):
doc = etree.parse(filename)
node = None
- for n in doc.xpath('//*[@id="%s"] | //*[@id="%s"]' % (xmlid, xmlid.split('.')[1])):
+ for n in doc.xpath('//*[@id="%s"]' % (xmlid)):
if n.tag in ('template', 'record'):
node = n
break
+ if node is None:
+ # fallback search on template with implicit module name
+ for n in doc.xpath('//*[@id="%s"]' % (xmlid.split('.')[1])):
+ if n.tag in ('template', 'record'):
+ node = n
+ break
+
if node is not None:
if node.tag == 'record':
field = node.find('field[@name="arch"]')
diff --git a/openerp/addons/test_new_api/__openerp__.py b/openerp/addons/test_new_api/__openerp__.py
index 3b959888314..c49f3a19309 100644
--- a/openerp/addons/test_new_api/__openerp__.py
+++ b/openerp/addons/test_new_api/__openerp__.py
@@ -1,9 +1,9 @@
# -*- coding: utf-8 -*-
{
- 'name': 'Test New API',
+ 'name': 'Test API',
'version': '1.0',
'category': 'Tests',
- 'description': """A module to test the new API.""",
+ 'description': """A module to test the API.""",
'author': 'OpenERP SA',
'maintainer': 'OpenERP SA',
'website': 'http://www.openerp.com',
diff --git a/openerp/addons/test_new_api/demo_data.xml b/openerp/addons/test_new_api/demo_data.xml
index acb53a7b07f..1715b024cd7 100644
--- a/openerp/addons/test_new_api/demo_data.xml
+++ b/openerp/addons/test_new_api/demo_data.xml
@@ -26,5 +26,7 @@
<field name="discussion" ref="discussion_0"/>
<field name="body">This is a much longer message</field>
</record>
+ <record id="c1" model="test_old_api.function_counter">
+ </record>
</data>
</openerp>
diff --git a/openerp/addons/test_new_api/ir.model.access.csv b/openerp/addons/test_new_api/ir.model.access.csv
index 5b62045ae08..2bbd1adf12d 100644
--- a/openerp/addons/test_new_api/ir.model.access.csv
+++ b/openerp/addons/test_new_api/ir.model.access.csv
@@ -3,3 +3,5 @@ access_category,test_new_api_category,test_new_api.model_test_new_api_category,,
access_discussion,test_new_api_discussion,test_new_api.model_test_new_api_discussion,,1,1,1,1
access_message,test_new_api_message,test_new_api.model_test_new_api_message,,1,1,1,1
access_mixed,test_new_api_mixed,test_new_api.model_test_new_api_mixed,,1,1,1,1
+access_test_function_noinfiniterecursion,access_test_function_noinfiniterecursion,model_test_old_api_function_noinfiniterecursion,,1,1,1,1
+access_test_function_counter,access_test_function_counter,model_test_old_api_function_counter,,1,1,1,1
diff --git a/openerp/addons/test_new_api/models.py b/openerp/addons/test_new_api/models.py
index 9da034a9b0a..bc07f360e9f 100644
--- a/openerp/addons/test_new_api/models.py
+++ b/openerp/addons/test_new_api/models.py
@@ -1,9 +1,17 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
+import datetime
from openerp.exceptions import AccessError
+
+##############################################################################
+#
+# OLD API
+#
+##############################################################################
from openerp.osv import osv, fields
+
class res_partner(osv.Model):
_inherit = 'res.partner'
@@ -23,6 +31,48 @@ class res_partner(osv.Model):
}
+class TestFunctionCounter(osv.Model):
+ _name = 'test_old_api.function_counter'
+
+ def _compute_cnt(self, cr, uid, ids, fname, arg, context=None):
+ res = {}
+ for cnt in self.browse(cr, uid, ids, context=context):
+ res[cnt.id] = cnt.access and cnt.cnt + 1 or 0
+ return res
+
+ _columns = {
+ 'access': fields.datetime('Datetime Field'),
+ 'cnt': fields.function(
+ _compute_cnt, type='integer', string='Function Field', store=True),
+ }
+
+
+class TestFunctionNoInfiniteRecursion(osv.Model):
+ _name = 'test_old_api.function_noinfiniterecursion'
+
+ def _compute_f1(self, cr, uid, ids, fname, arg, context=None):
+ res = {}
+ for tf in self.browse(cr, uid, ids, context=context):
+ res[tf.id] = 'create' in tf.f0 and 'create' or 'write'
+ cntobj = self.pool['test_old_api.function_counter']
+ cnt_id = self.pool['ir.model.data'].xmlid_to_res_id(
+ cr, uid, 'test_new_api.c1')
+ cntobj.write(
+ cr, uid, cnt_id, {'access': datetime.datetime.now()},
+ context=context)
+ return res
+
+ _columns = {
+ 'f0': fields.char('Char Field'),
+ 'f1': fields.function(
+ _compute_f1, type='char', string='Function Field', store=True),
+ }
+
+##############################################################################
+#
+# NEW API
+#
+##############################################################################
from openerp import models, fields, api, _
diff --git a/openerp/addons/test_new_api/tests/__init__.py b/openerp/addons/test_new_api/tests/__init__.py
index e2540926d14..a8961af9f74 100644
--- a/openerp/addons/test_new_api/tests/__init__.py
+++ b/openerp/addons/test_new_api/tests/__init__.py
@@ -6,3 +6,4 @@ from . import test_onchange
from . import test_field_conversions
from . import test_attributes
from . import test_ui
+from . import test_no_infinite_recursion
diff --git a/openerp/addons/test_new_api/tests/test_no_infinite_recursion.py b/openerp/addons/test_new_api/tests/test_no_infinite_recursion.py
new file mode 100644
index 00000000000..68ef3fcf3b9
--- /dev/null
+++ b/openerp/addons/test_new_api/tests/test_no_infinite_recursion.py
@@ -0,0 +1,31 @@
+# -*- coding: utf-8 -*-
+from openerp.tests import common
+
+
+class test_no_infinite_recursion(common.TransactionCase):
+
+ def setUp(self):
+ super(test_no_infinite_recursion, self).setUp()
+ self.tstfct = self.registry['test_old_api.function_noinfiniterecursion']
+
+ def test_00_create_and_update(self):
+ """
+ Check that computing old api function field does not cycle infinitely
+ See https://github.com/odoo/odoo/pull/7558
+ """
+ cr, uid, context, tstfct = self.cr, self.uid, {}, self.tstfct
+
+ vals = {
+ 'f0': 'Test create',
+ }
+ idnew = tstfct.create(cr, uid, vals, context=context)
+ tst = tstfct.browse(cr, uid, idnew, context=context)
+
+ self.assertEqual(tst.f1, 'create')
+
+ vals = {
+ 'f0': 'Test write',
+ }
+ tstfct.write(cr, uid, idnew, vals, context=context)
+
+ self.assertEqual(tst.f1, 'write')
diff --git a/openerp/models.py b/openerp/models.py
index 4e3d6cf348f..69d37d88185 100644
--- a/openerp/models.py
+++ b/openerp/models.py
@@ -4019,18 +4019,22 @@ class BaseModel(object):
done = {}
recs.env.recompute_old.extend(result)
- for order, model_name, ids_to_update, fields_to_recompute in sorted(recs.env.recompute_old):
- key = (model_name, tuple(fields_to_recompute))
- done.setdefault(key, {})
- # avoid to do several times the same computation
- todo = []
- for id in ids_to_update:
- if id not in done[key]:
- done[key][id] = True
- if id not in deleted_related[model_name]:
- todo.append(id)
- self.pool[model_name]._store_set_values(cr, user, todo, fields_to_recompute, context)
- recs.env.clear_recompute_old()
+ while recs.env.recompute_old:
+ sorted_recompute_old = sorted(recs.env.recompute_old)
+ recs.env.clear_recompute_old()
+ for __, model_name, ids_to_update, fields_to_recompute in \
+ sorted_recompute_old:
+ key = (model_name, tuple(fields_to_recompute))
+ done.setdefault(key, {})
+ # avoid to do several times the same computation
+ todo = []
+ for id in ids_to_update:
+ if id not in done[key]:
+ done[key][id] = True
+ if id not in deleted_related[model_name]:
+ todo.append(id)
+ self.pool[model_name]._store_set_values(
+ cr, user, todo, fields_to_recompute, context)
# recompute new-style fields
if recs.env.recompute and context.get('recompute', True):
@@ -4287,11 +4291,15 @@ class BaseModel(object):
if recs.env.recompute and context.get('recompute', True):
done = []
- for order, model_name, ids, fields2 in sorted(recs.env.recompute_old):
- if not (model_name, ids, fields2) in done:
- self.pool[model_name]._store_set_values(cr, user, ids, fields2, context)
- done.append((model_name, ids, fields2))
- recs.env.clear_recompute_old()
+ while recs.env.recompute_old:
+ sorted_recompute_old = sorted(recs.env.recompute_old)
+ recs.env.clear_recompute_old()
+ for __, model_name, ids, fields2 in sorted_recompute_old:
+ if not (model_name, ids, fields2) in done:
+ self.pool[model_name]._store_set_values(
+ cr, user, ids, fields2, context)
+ done.append((model_name, ids, fields2))
+
# recompute new-style fields
recs.recompute()
diff --git a/openerp/service/server.py b/openerp/service/server.py
index 608b8478295..b3ae669d169 100644
--- a/openerp/service/server.py
+++ b/openerp/service/server.py
@@ -673,7 +673,7 @@ class Worker(object):
r = resource.getrusage(resource.RUSAGE_SELF)
cpu_time = r.ru_utime + r.ru_stime
def time_expired(n, stack):
- _logger.info('Worker (%d) CPU time limit (%s) reached.', config['limit_time_cpu'])
+ _logger.info('Worker (%d) CPU time limit (%s) reached.', self.pid, config['limit_time_cpu'])
# We dont suicide in such case
raise Exception('CPU time limit exceeded.')
signal.signal(signal.SIGXCPU, time_expired)