diff options
| author | Christophe Simonis <chs@odoo.com> | 2015-08-31 19:57:20 +0200 |
|---|---|---|
| committer | Christophe Simonis <chs@odoo.com> | 2015-08-31 19:57:20 +0200 |
| commit | 044c0625ad4644ae7abb197433309822eccdbcdc (patch) | |
| tree | 82b63128bb463a893037e5d4ddd3581d839090e0 | |
| parent | bd30f262ebfc1d586d4b5d96a89a56527ef6c375 (diff) | |
| parent | 7faf72c0242353bf3049e7ded9074437ae1d7e82 (diff) | |
[MERGE] forward port of branch saas-6 up to 7faf72c
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) |
