Add option to rule to move date of occurence to working day

This commit is contained in:
Frederik Jaeckel 2024-06-03 23:37:26 +02:00
parent e76ec5792c
commit a84b672250
2 changed files with 93 additions and 4 deletions

View file

@ -217,11 +217,42 @@ class ScheduledBooking(DeactivableMixin, ModelSQL, ModelView):
used instead of the stored values, Defaults to {}, used instead of the stored values, Defaults to {},
allowed: frequ, weekday, start_date, allowed: frequ, weekday, start_date,
end_date (preferred over 'count'), end_date (preferred over 'count'),
monthday, interval, setpos monthday, interval, setpos, move_event
Returns: Returns:
list: date values, result of rrlue list: date values, result of rrlue
""" """
def get_moved_date(xdate, m_mode):
""" re-arrange xdate to a working day
Args:
xdate (date): date to move to a working day
move_mode (str): move mode:
nop - no operation
after/before - move date to after/before input date
Returns:
date: re-arranged date
"""
Config = Pool().get('cashbook.configuration')
config = Config.get_singleton()
assert m_mode in ['nop', 'after', 'before'], 'invalid move_mode'
if (not config) or (m_mode == 'nop'):
return xdate
holidays = config.holiday_dates([xdate.year, xdate.year + 1])
day_cnt = (
1 if m_mode == 'after'
else -1 if m_mode == 'before' else 0)
if day_cnt != 0:
while (xdate in holidays) or (xdate.weekday() in [5, 6]):
# re-arrange
xdate = xdate + timedelta(days=day_cnt)
return xdate
pfrequ = { pfrequ = {
'year': YEARLY, 'month': MONTHLY, 'week': WEEKLY, 'day': DAILY} 'year': YEARLY, 'month': MONTHLY, 'week': WEEKLY, 'day': DAILY}
pweekday = { pweekday = {
@ -235,6 +266,10 @@ class ScheduledBooking(DeactivableMixin, ModelSQL, ModelView):
end_date = params.get('end_date', self.end_date) end_date = params.get('end_date', self.end_date)
frequ = pfrequ[params.get('frequ', self.frequ)] frequ = pfrequ[params.get('frequ', self.frequ)]
move_event = params.get('move_event', self.move_event)
if move_event not in ['nop', 'before', 'after']:
move_event = 'nop'
setpos = params.get('setpos', self.setpos) setpos = params.get('setpos', self.setpos)
if setpos is not None: if setpos is not None:
setpos = 1 if setpos < 1 else 4 if setpos > 4 else setpos setpos = 1 if setpos < 1 else 4 if setpos > 4 else setpos
@ -262,7 +297,12 @@ class ScheduledBooking(DeactivableMixin, ModelSQL, ModelView):
for x in dtrule: for x in dtrule:
if (query_date and (x.date() >= query_date)) or \ if (query_date and (x.date() >= query_date)) or \
(query_date is None): (query_date is None):
result.append(x.date()) x_date = get_moved_date(x.date(), move_event)
# if date was re-arranged backwards and we are before
# query_date - skip it
if x_date >= query_date:
result.append(x_date)
if len(result) >= count: if len(result) >= count:
break break
return result return result
@ -328,7 +368,7 @@ class ScheduledBooking(DeactivableMixin, ModelSQL, ModelView):
@fields.depends( @fields.depends(
'start_date', 'end_date', 'frequ', 'weekday', 'monthday', 'start_date', 'end_date', 'frequ', 'weekday', 'monthday',
'interval', 'setpos') 'interval', 'setpos', 'move_event')
def on_change_with_nextdates(self, name=None): def on_change_with_nextdates(self, name=None):
""" Calculates the next 5 appointments based on the configured rule, """ Calculates the next 5 appointments based on the configured rule,
returns a formatted date list returns a formatted date list

View file

@ -170,7 +170,9 @@ class PlannerTestCase(object):
def test_planner_create_job(self): def test_planner_create_job(self):
""" create job, check rule + constraints """ create job, check rule + constraints
""" """
Planner = Pool().get('cashbook.planner') pool = Pool()
Planner = pool.get('cashbook.planner')
Config = pool.get('cashbook.configuration')
job = self.prep_create_job() job = self.prep_create_job()
self.assertEqual( self.assertEqual(
@ -243,6 +245,53 @@ class PlannerTestCase(object):
[date(2022, 5, 11), date(2022, 6, 8), date(2022, 7, 13), [date(2022, 5, 11), date(2022, 6, 8), date(2022, 7, 13),
date(2022, 8, 10), date(2022, 9, 14), date(2022, 10, 12)]) date(2022, 8, 10), date(2022, 9, 14), date(2022, 10, 12)])
# set up holidays
cfg1 = Config(
holidays='01-01;05-01;easter:+1;easter:-2;ascension;whitsun:+1')
cfg1.save()
# 1st of may, should be moved to 2nd of may
self.assertEqual(
job._compute_dates_by_rrule(
query_date=date(2022, 4, 25), count=3,
params={
'end_date': None, 'start_date': date(2022, 5, 1),
'move_event': 'after', 'weekday': None,
'setpos': None, 'interval': 1, 'frequ': 'year',
'monthday': None}),
[date(2022, 5, 2), date(2023, 5, 2), date(2024, 5, 2)])
# easter of 2022, occurence-date moved to tuesday after easter'22
self.assertEqual(
job._compute_dates_by_rrule(
query_date=date(2022, 4, 10), count=3,
params={
'end_date': None, 'start_date': date(2022, 4, 17),
'move_event': 'after', 'weekday': None,
'setpos': None,
'interval': 1, 'frequ': 'month', 'monthday': None}),
[date(2022, 4, 19), date(2022, 5, 17), date(2022, 6, 17)])
# easter of 2022, monthly, occurence-date moved to
# thursday before easter'22
self.assertEqual(
job._compute_dates_by_rrule(
query_date=date(2022, 4, 10), count=3,
params={
'end_date': None, 'start_date': date(2022, 4, 17),
'move_event': 'before', 'weekday': None,
'setpos': None,
'interval': 1, 'frequ': 'month', 'monthday': None}),
[date(2022, 4, 14), date(2022, 5, 17), date(2022, 6, 17)])
# easter of 2022, monthly, check next occurence after easter
# recompute date at moved occurence-date+1
self.assertEqual(
job._compute_dates_by_rrule(
query_date=date(2022, 4, 15), count=3,
params={
'end_date': None, 'start_date': date(2022, 4, 17),
'move_event': 'before', 'weekday': None,
'setpos': None,
'interval': 1, 'frequ': 'month', 'monthday': None}),
[date(2022, 5, 17), date(2022, 6, 17), date(2022, 7, 15)])
Planner.write(*[[job], { Planner.write(*[[job], {
'frequ': 'year', 'start_date': date(2022, 5, 1), 'frequ': 'year', 'start_date': date(2022, 5, 1),
'setpos': None, 'monthday': None, 'interval': 1, 'setpos': None, 'monthday': None, 'interval': 1,