summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLucas Perais (lpe) <lpe@odoo.com>2017-11-15 09:02:26 +0100
committerLucas Perais (lpe) <lpe@odoo.com>2017-11-16 10:39:32 +0100
commitb4ec51021ba2a4f6cb9be29e4c3dae9bf2c4a56f (patch)
treeb54f938d0405ea0628120f5693be4996bedc1964
parent9785f8bbed1036147b4ee1054c40cdeb91b0d1b3 (diff)
[FIX][BACKPORT] calendar: correct search in recurrent event
backporting 76b724294aa21c83edd3409d4664e041dd70cd00 OPW 779208 closes #20996 original commit message: Add a 3 use cases with tests that were failing before. datetime filters were not working correctly on stop filters The `_get_recurrent_date_by_event` was only computing recurrency on the start field of an event which can make a difference. A recurring event start=2017-01-01 09:00:00; stop=2017-01-01 10:00:0 was not matched in a search ('stop', '>', '2017-01-01 09:30:00') The filter != was not handled in the recurring use cases. e.g. making the search 'start is set' in the web client is equal to ('start', '!=', False) Searching with only a start filter matching recurring events but not the first ("real") occurence, was returning no result if no stop filter was present. start=2017-01-01 09:00:00; count=5 had no result in a search ('start', '>', '2017-01-02')
-rw-r--r--addons/calendar/calendar.py63
-rw-r--r--addons/calendar/test/test_calendar_recurrent_event_case2.yml47
2 files changed, 97 insertions, 13 deletions
diff --git a/addons/calendar/calendar.py b/addons/calendar/calendar.py
index ae0cbe60349..43064b369d6 100644
--- a/addons/calendar/calendar.py
+++ b/addons/calendar/calendar.py
@@ -16,6 +16,7 @@ from dateutil.relativedelta import relativedelta
from openerp import api
from openerp import tools, SUPERUSER_ID
from openerp.osv import fields, osv
+from openerp.fields import Datetime as FieldDatetime
from openerp.tools import DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT
from openerp.tools.translate import _
from openerp.http import request
@@ -666,9 +667,23 @@ class calendar_event(osv.Model):
def do_run_scheduler(self, cr, uid, id, context=None):
self.pool['calendar.alarm_manager'].get_next_mail(cr, uid, context=context)
+ def _get_recurrent_dates_by_event(self, cr, uid, ids, context=None):
+ """ Get recurrent start and stop dates based on Rule string"""
+ start_dates = self._get_recurrent_date_by_event(cr, uid, ids, date_field='start')
+ stop_dates = self._get_recurrent_date_by_event(cr, uid, ids, date_field='stop')
+ return zip(start_dates, stop_dates)
+
def get_recurrent_date_by_event(self, cr, uid, event, context=None):
+ return self._get_recurrent_date_by_event(cr, uid, event, context=None)
+
+ def _get_recurrent_date_by_event(self, cr, uid, event, context=None, date_field='start'):
"""Get recurrent dates based on Rule string and all event where recurrent_id is child
"""
+ if date_field in event._fields.keys() and event._fields[date_field].type in ('date', 'datetime'):
+ reference_date = event[date_field]
+ else:
+ reference_date = event.start
+
def todate(date):
val = parser.parse(''.join((re.compile('\d')).findall(date)))
## Dates are localized to saved timezone if any, else current timezone.
@@ -680,14 +695,14 @@ class calendar_event(osv.Model):
context = {}
timezone = pytz.timezone(context.get('tz') or 'UTC')
- startdate = pytz.UTC.localize(datetime.strptime(event.start, DEFAULT_SERVER_DATETIME_FORMAT)) # Add "+hh:mm" timezone
- if not startdate:
- startdate = datetime.now()
+ event_date = pytz.UTC.localize(FieldDatetime.from_string(reference_date)) # Add "+hh:mm" timezone
+ if not event_date:
+ event_date = datetime.now()
- ## Convert the start date to saved timezone (or context tz) as it'll
+ ## Convert the event date to saved timezone (or context tz) as it'll
## define the correct hour/day asked by the user to repeat for recurrence.
- startdate = startdate.astimezone(timezone) # transform "+hh:mm" timezone
- rset1 = rrule.rrulestr(str(event.rrule), dtstart=startdate, forceset=True)
+ event_date = event_date.astimezone(timezone) # transform "+hh:mm" timezone
+ rset1 = rrule.rrulestr(str(event.rrule), dtstart=event_date, forceset=True)
ids_depending = self.search(cr, uid, [('recurrent_id', '=', event.id), '|', ('active', '=', False), ('active', '=', True)], context=context)
all_events = self.browse(cr, uid, ids_depending, context=context)
for ev in all_events:
@@ -1173,9 +1188,9 @@ class calendar_event(osv.Model):
result.append(ev.id)
result_data.append(self.get_search_fields(ev, order_fields))
continue
- rdates = self.get_recurrent_date_by_event(cr, uid, ev, context=context)
+ rdates = self._get_recurrent_dates_by_event(cr, uid, ev, context=context)
- for r_date in rdates:
+ for r_start_date, r_stop_date in rdates:
# fix domain evaluation
# step 1: check date and replace expression by True or False, replace other expressions by True
# step 2: evaluation of & and |
@@ -1184,16 +1199,26 @@ class calendar_event(osv.Model):
ok = True
for arg in domain:
if str(arg[0]) in ('start', 'stop', 'final_date'):
+ if str(arg[0]) == 'start':
+ r_date = r_start_date
+ else:
+ r_date = r_stop_date
+ if arg[2] and len(arg[2]) > len(r_date.strftime(DEFAULT_SERVER_DATE_FORMAT)):
+ dformat = DEFAULT_SERVER_DATETIME_FORMAT
+ else:
+ dformat = DEFAULT_SERVER_DATE_FORMAT
if (arg[1] == '='):
- ok = r_date.strftime('%Y-%m-%d') == arg[2]
+ ok = r_date.strftime(dformat) == arg[2]
if (arg[1] == '>'):
- ok = r_date.strftime('%Y-%m-%d') > arg[2]
+ ok = r_date.strftime(dformat) > arg[2]
if (arg[1] == '<'):
- ok = r_date.strftime('%Y-%m-%d') < arg[2]
+ ok = r_date.strftime(dformat) < arg[2]
if (arg[1] == '>='):
- ok = r_date.strftime('%Y-%m-%d') >= arg[2]
+ ok = r_date.strftime(dformat) >= arg[2]
if (arg[1] == '<='):
- ok = r_date.strftime('%Y-%m-%d') <= arg[2]
+ ok = r_date.strftime(dformat) <= arg[2]
+ if (arg[1] == '!='):
+ ok = r_date.strftime(dformat) != arg[2]
pile.append(ok)
elif str(arg) == str('&') or str(arg) == str('|'):
pile.append(arg)
@@ -1529,6 +1554,18 @@ class calendar_event(osv.Model):
if not context.get('virtual_id', True):
return super(calendar_event, self).search(cr, uid, new_args, offset=offset, limit=limit, order=order, count=count, context=context)
+ if any(arg[0] == 'start' for arg in args) and \
+ not any(arg[0] in ('stop', 'final_date') for arg in args):
+ # domain with a start filter but with no stop clause should be extended
+ # e.g. start=2017-01-01, count=5 => virtual occurences must be included in ('start', '>', '2017-01-02')
+ start_args = new_args
+ new_args = []
+ for arg in start_args:
+ new_arg = arg
+ if arg[0] in ('start_date', 'start_datetime', 'start',):
+ new_args += ['|', '&', ('recurrency', '=', 1), ('final_date', arg[1], arg[2])]
+ new_args.append(new_arg)
+
# offset, limit, order and count must be treated separately as we may need to deal with virtual ids
res = super(calendar_event, self).search(cr, uid, new_args, offset=0, limit=0, order=None, context=context, count=False)
res = self.get_recurrent_ids(cr, uid, res, args, order=order, context=context)
diff --git a/addons/calendar/test/test_calendar_recurrent_event_case2.yml b/addons/calendar/test/test_calendar_recurrent_event_case2.yml
index 0908edfb2a8..99ff3855c88 100644
--- a/addons/calendar/test/test_calendar_recurrent_event_case2.yml
+++ b/addons/calendar/test/test_calendar_recurrent_event_case2.yml
@@ -100,3 +100,50 @@
self.write(cr, uid, ids, {'description': DESCRIPTION}, context=context)
for i, event in enumerate(self.browse(cr, uid, ids, context)):
assert event.description == DESCRIPTION, "Event %d has not been updated" % i
+-
+ !record {model: calendar.event, id: calendar_event_reviewcode0}:
+ count: 12
+ start: '2011-01-22 11:47:00'
+ stop: '2011-01-22 12:47:00'
+ day: 0.0
+ duration: 1.0
+ final_date: 2017-06-30
+ end_type: 'end_date'
+ name: Review code with programmer
+ fr: true
+ mo: true
+ th: true
+ tu: true
+ we: true
+ recurrency: true
+ rrule_type: weekly
+-
+ !python {model: calendar.event}: |
+ context = {'virtual_id': True}
+
+ meetings_count = self.search_count(cr, uid, [
+ ('start', '<=', '2017-01-24'), ('stop', '>=', '2017-01-24'), ('name', '=', 'Review code with programmer')
+ ], context=context)
+
+ assert meetings_count == 1, 'Recurrent weekly meetings are not found using date filter !'
+
+ meetings_count = self.search_count(cr, uid, [
+ ('start', '<=', '2017-01-24 11:55:00'), ('stop', '>=', '2017-01-24 11:55:00'), ('name', '=', 'Review code with programmer')
+ ], context=context)
+ assert meetings_count == 1, 'Recurrent weekly meetings are not found using time filter !'
+
+ meetings_count = self.search_count(cr, uid, [
+ ('start', '!=', False), ('stop', '>=', '2017-06-30 11:55:00'), ('name', '=', 'Review code with programmer')
+ ], context=context)
+ assert meetings_count == 1, "Last recurrent weekly meetings are not found using 'is set' filter !"
+
+ meetings_count = self.search_count(cr, uid, [
+ ('start', '<=', '2017-01-24 11:55:00'), ('stop', '>=', '2017-01-24 15:55:00')
+ ], context=context)
+ assert meetings_count == 0, 'Too late recurrent meetings are found using time filter !'
+
+ # I search using a start filter but no stop
+ meetings_count = self.search_count(cr, uid, [
+ ('start', '>=', '2017-06-30 08:00:00'), ('name', '=', 'Review code with programmer')
+ ], context=context)
+ assert meetings_count == 1, "Last recurrent weekly meetings are not found without stop filter !"