[MERGE] forward port of branch 8.0 up to 0c1e978
This commit is contained in:
@@ -1655,6 +1655,14 @@ class calendar_event(osv.Model):
|
|||||||
self.create_attendees(cr, uid, [res], context=context)
|
self.create_attendees(cr, uid, [res], context=context)
|
||||||
return res
|
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):
|
def read_group(self, cr, uid, domain, fields, groupby, offset=0, limit=None, context=None, orderby=False, lazy=True):
|
||||||
context = dict(context or {})
|
context = dict(context or {})
|
||||||
|
|
||||||
|
|||||||
@@ -324,7 +324,8 @@ class account_analytic_line(osv.osv):
|
|||||||
class hr_analytic_timesheet(osv.osv):
|
class hr_analytic_timesheet(osv.osv):
|
||||||
_inherit = "hr.analytic.timesheet"
|
_inherit = "hr.analytic.timesheet"
|
||||||
def on_change_account_id(self, cr, uid, ids, account_id, user_id=False):
|
def on_change_account_id(self, cr, uid, ids, account_id, user_id=False):
|
||||||
res = {}
|
res = super(hr_analytic_timesheet, self).on_change_account_id(
|
||||||
|
cr, uid, ids, account_id, context=user_id)
|
||||||
if not account_id:
|
if not account_id:
|
||||||
return res
|
return res
|
||||||
res.setdefault('value',{})
|
res.setdefault('value',{})
|
||||||
|
|||||||
@@ -347,7 +347,7 @@ class Partner(osv.osv):
|
|||||||
store = {
|
store = {
|
||||||
'account.invoice': (_get_invoice_partner, ['state'], 10),
|
'account.invoice': (_get_invoice_partner, ['state'], 10),
|
||||||
'membership.membership_line': (_get_partner_id, ['state'], 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."),
|
}, help="Date from which membership becomes active."),
|
||||||
'membership_stop': fields.function(
|
'membership_stop': fields.function(
|
||||||
_membership_date,
|
_membership_date,
|
||||||
@@ -355,7 +355,7 @@ class Partner(osv.osv):
|
|||||||
store = {
|
store = {
|
||||||
'account.invoice': (_get_invoice_partner, ['state'], 10),
|
'account.invoice': (_get_invoice_partner, ['state'], 10),
|
||||||
'membership.membership_line': (_get_partner_id, ['state'], 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."),
|
}, help="Date until which membership remains active."),
|
||||||
'membership_cancel': fields.function(
|
'membership_cancel': fields.function(
|
||||||
_membership_date,
|
_membership_date,
|
||||||
@@ -363,7 +363,7 @@ class Partner(osv.osv):
|
|||||||
store = {
|
store = {
|
||||||
'account.invoice': (_get_invoice_partner, ['state'], 11),
|
'account.invoice': (_get_invoice_partner, ['state'], 11),
|
||||||
'membership.membership_line': (_get_partner_id, ['state'], 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 on which membership has been cancelled"),
|
}, help="Date on which membership has been cancelled"),
|
||||||
}
|
}
|
||||||
_defaults = {
|
_defaults = {
|
||||||
|
|||||||
@@ -1548,6 +1548,10 @@ openerp.point_of_sale.load_models = function load_models(instance, module){ //mo
|
|||||||
this.select_orderline(this.get_last_orderline());
|
this.select_orderline(this.get_last_orderline());
|
||||||
},
|
},
|
||||||
add_product: function(product, options){
|
add_product: function(product, options){
|
||||||
|
if(this._printed){
|
||||||
|
this.destroy();
|
||||||
|
return this.pos.get_order().add_product(product, options);
|
||||||
|
}
|
||||||
this.assert_editable();
|
this.assert_editable();
|
||||||
options = options || {};
|
options = options || {};
|
||||||
var attr = JSON.parse(JSON.stringify(product));
|
var attr = JSON.parse(JSON.stringify(product));
|
||||||
|
|||||||
@@ -337,6 +337,7 @@ class resource_calendar(osv.osv):
|
|||||||
|
|
||||||
# no calendar: try to use the default_interval, then return directly
|
# no calendar: try to use the default_interval, then return directly
|
||||||
if id is None:
|
if id is None:
|
||||||
|
working_interval = []
|
||||||
if default_interval:
|
if default_interval:
|
||||||
working_interval = (start_dt.replace(hour=default_interval[0], minute=0, second=0),
|
working_interval = (start_dt.replace(hour=default_interval[0], minute=0, second=0),
|
||||||
start_dt.replace(hour=default_interval[1], minute=0, second=0))
|
start_dt.replace(hour=default_interval[1], minute=0, second=0))
|
||||||
@@ -550,7 +551,6 @@ class resource_calendar(osv.osv):
|
|||||||
intervals = []
|
intervals = []
|
||||||
planned_days = 0
|
planned_days = 0
|
||||||
iterations = 0
|
iterations = 0
|
||||||
|
|
||||||
current_datetime = day_date.replace(hour=0, minute=0, second=0)
|
current_datetime = day_date.replace(hour=0, minute=0, second=0)
|
||||||
|
|
||||||
while planned_days < days and iterations < 100:
|
while planned_days < days and iterations < 100:
|
||||||
|
|||||||
@@ -431,6 +431,8 @@ class TestResource(TestResourceCommon):
|
|||||||
|
|
||||||
res = self.resource_calendar.schedule_days_get_date(cr, uid, self.calendar_id, 5, day_date=self.date1)
|
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')
|
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(
|
res = self.resource_calendar.schedule_days_get_date(
|
||||||
cr, uid, self.calendar_id, 5, day_date=self.date1,
|
cr, uid, self.calendar_id, 5, day_date=self.date1,
|
||||||
|
|||||||
@@ -209,7 +209,7 @@
|
|||||||
<ul class="list-group">
|
<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 programming language</li>
|
||||||
<li class="list-group-item">Good knowledge of the latest web technologies</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">Contributions to open source projects</li>
|
||||||
<li class="list-group-item">Passion for the Internet and its culture</li>
|
<li class="list-group-item">Passion for the Internet and its culture</li>
|
||||||
<li class="list-group-item">Quick and autonomous learner</li>
|
<li class="list-group-item">Quick and autonomous learner</li>
|
||||||
@@ -603,7 +603,7 @@
|
|||||||
<ul class="list-group">
|
<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 programming language</li>
|
||||||
<li class="list-group-item">Good knowledge of the latest web technologies</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">Contributions to open source projects</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="panel-footer text-center">
|
<div class="panel-footer text-center">
|
||||||
@@ -731,7 +731,7 @@
|
|||||||
<ul class="list-group">
|
<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 programming language</li>
|
||||||
<li class="list-group-item">Good knowledge of the latest web technologies</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">Contributions to open source projects</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="panel-footer text-center">
|
<div class="panel-footer text-center">
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
<td class="text-right">
|
<td class="text-right">
|
||||||
<span t-field="website_sale_order.amount_delivery" t-field-options='{
|
<span t-field="website_sale_order.amount_delivery" t-field-options='{
|
||||||
"widget": "monetary",
|
"widget": "monetary",
|
||||||
"display_currency": "website.pricelist_id.currency_id"
|
"display_currency": "website_sale_order.currency_id"
|
||||||
}'/>
|
}'/>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -37,7 +37,8 @@
|
|||||||
<span class="badge" t-field="delivery.price"
|
<span class="badge" t-field="delivery.price"
|
||||||
t-field-options='{
|
t-field-options='{
|
||||||
"widget": "monetary",
|
"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"/>
|
<div t-field="delivery.website_description" class="text-muted"/>
|
||||||
</label>
|
</label>
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
{
|
{
|
||||||
'name': 'Test New API',
|
'name': 'Test API',
|
||||||
'version': '1.0',
|
'version': '1.0',
|
||||||
'category': 'Tests',
|
'category': 'Tests',
|
||||||
'description': """A module to test the new API.""",
|
'description': """A module to test the API.""",
|
||||||
'author': 'OpenERP SA',
|
'author': 'OpenERP SA',
|
||||||
'maintainer': 'OpenERP SA',
|
'maintainer': 'OpenERP SA',
|
||||||
'website': 'http://www.openerp.com',
|
'website': 'http://www.openerp.com',
|
||||||
|
|||||||
@@ -26,5 +26,7 @@
|
|||||||
<field name="discussion" ref="discussion_0"/>
|
<field name="discussion" ref="discussion_0"/>
|
||||||
<field name="body">This is a much longer message</field>
|
<field name="body">This is a much longer message</field>
|
||||||
</record>
|
</record>
|
||||||
|
<record id="c1" model="test_old_api.function_counter">
|
||||||
|
</record>
|
||||||
</data>
|
</data>
|
||||||
</openerp>
|
</openerp>
|
||||||
|
|||||||
@@ -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_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_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_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
|
||||||
|
|||||||
|
@@ -19,9 +19,17 @@
|
|||||||
#
|
#
|
||||||
##############################################################################
|
##############################################################################
|
||||||
|
|
||||||
|
import datetime
|
||||||
from openerp.exceptions import AccessError
|
from openerp.exceptions import AccessError
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
#
|
||||||
|
# OLD API
|
||||||
|
#
|
||||||
|
##############################################################################
|
||||||
from openerp.osv import osv, fields
|
from openerp.osv import osv, fields
|
||||||
|
|
||||||
|
|
||||||
class res_partner(osv.Model):
|
class res_partner(osv.Model):
|
||||||
_inherit = 'res.partner'
|
_inherit = 'res.partner'
|
||||||
|
|
||||||
@@ -41,6 +49,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, _
|
from openerp import models, fields, api, _
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -5,3 +5,4 @@ from . import test_new_fields
|
|||||||
from . import test_onchange
|
from . import test_onchange
|
||||||
from . import test_field_conversions
|
from . import test_field_conversions
|
||||||
from . import test_attributes
|
from . import test_attributes
|
||||||
|
from . import test_no_infinite_recursion
|
||||||
|
|||||||
@@ -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')
|
||||||
+25
-17
@@ -4017,18 +4017,22 @@ class BaseModel(object):
|
|||||||
|
|
||||||
done = {}
|
done = {}
|
||||||
recs.env.recompute_old.extend(result)
|
recs.env.recompute_old.extend(result)
|
||||||
for order, model_name, ids_to_update, fields_to_recompute in sorted(recs.env.recompute_old):
|
while recs.env.recompute_old:
|
||||||
key = (model_name, tuple(fields_to_recompute))
|
sorted_recompute_old = sorted(recs.env.recompute_old)
|
||||||
done.setdefault(key, {})
|
recs.env.clear_recompute_old()
|
||||||
# avoid to do several times the same computation
|
for __, model_name, ids_to_update, fields_to_recompute in \
|
||||||
todo = []
|
sorted_recompute_old:
|
||||||
for id in ids_to_update:
|
key = (model_name, tuple(fields_to_recompute))
|
||||||
if id not in done[key]:
|
done.setdefault(key, {})
|
||||||
done[key][id] = True
|
# avoid to do several times the same computation
|
||||||
if id not in deleted_related[model_name]:
|
todo = []
|
||||||
todo.append(id)
|
for id in ids_to_update:
|
||||||
self.pool[model_name]._store_set_values(cr, user, todo, fields_to_recompute, context)
|
if id not in done[key]:
|
||||||
recs.env.clear_recompute_old()
|
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
|
# recompute new-style fields
|
||||||
if recs.env.recompute and context.get('recompute', True):
|
if recs.env.recompute and context.get('recompute', True):
|
||||||
@@ -4285,11 +4289,15 @@ class BaseModel(object):
|
|||||||
|
|
||||||
if recs.env.recompute and context.get('recompute', True):
|
if recs.env.recompute and context.get('recompute', True):
|
||||||
done = []
|
done = []
|
||||||
for order, model_name, ids, fields2 in sorted(recs.env.recompute_old):
|
while recs.env.recompute_old:
|
||||||
if not (model_name, ids, fields2) in done:
|
sorted_recompute_old = sorted(recs.env.recompute_old)
|
||||||
self.pool[model_name]._store_set_values(cr, user, ids, fields2, context)
|
recs.env.clear_recompute_old()
|
||||||
done.append((model_name, ids, fields2))
|
for __, model_name, ids, fields2 in sorted_recompute_old:
|
||||||
recs.env.clear_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
|
# recompute new-style fields
|
||||||
recs.recompute()
|
recs.recompute()
|
||||||
|
|
||||||
|
|||||||
@@ -673,7 +673,7 @@ class Worker(object):
|
|||||||
r = resource.getrusage(resource.RUSAGE_SELF)
|
r = resource.getrusage(resource.RUSAGE_SELF)
|
||||||
cpu_time = r.ru_utime + r.ru_stime
|
cpu_time = r.ru_utime + r.ru_stime
|
||||||
def time_expired(n, stack):
|
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
|
# We dont suicide in such case
|
||||||
raise Exception('CPU time limit exceeded.')
|
raise Exception('CPU time limit exceeded.')
|
||||||
signal.signal(signal.SIGXCPU, time_expired)
|
signal.signal(signal.SIGXCPU, time_expired)
|
||||||
|
|||||||
Reference in New Issue
Block a user