Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions bwh_hive/bwh_hive/doctype/hive_task/hive_task.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,9 @@ def _maybe_spawn_recurrence(self):
"""When a recurring task is marked Done, spawn the next instance.

Skips if the frequency is unset, the status didn't just change to Done,
or the computed next due date is past `recurrence_end_date`.
an open instance of the series already exists, or the computed next due
date is past `recurrence_end_date`. An overdue series rolls forward to
the first due date on or after today.
"""
if not self.recurrence_frequency or self.status != "Done":
return
Expand All @@ -124,9 +126,18 @@ def _maybe_spawn_recurrence(self):
if self.flags.get("recurrence_spawned"):
return

parent_name = self.recurring_parent or self.name
if frappe.db.exists(
"Hive Task",
{"recurring_parent": parent_name, "status": ("!=", "Done"), "is_archived": 0},
):
return

next_due = _add_period(self.due_date or today(), self.recurrence_frequency)
if not next_due:
return
while getdate(next_due) < getdate(today()):
next_due = _add_period(next_due, self.recurrence_frequency)
if self.recurrence_end_date and getdate(next_due) > getdate(self.recurrence_end_date):
return

Expand All @@ -135,7 +146,6 @@ def _maybe_spawn_recurrence(self):
interval_days = (getdate(self.due_date) - getdate(self.start_date)).days
new_start = add_days(next_due, -interval_days)

parent_name = self.recurring_parent or self.name
new_task = frappe.new_doc("Hive Task")
new_task.update(
{
Expand Down
44 changes: 44 additions & 0 deletions bwh_hive/bwh_hive/doctype/hive_task/test_hive_task.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import frappe
from frappe.tests import IntegrationTestCase
from frappe.utils import add_days, getdate, today

EXTRA_TEST_RECORD_DEPENDENCIES = []
IGNORE_TEST_RECORD_DEPENDENCIES = []
Expand Down Expand Up @@ -192,3 +193,46 @@ def test_multiple_tasks_only_archived_one_hidden(self):
visible_names = {t.name for t in visible}
self.assertNotIn(self.task.name, visible_names)
self.assertIn(task2.name, visible_names)


class TestHiveTaskRecurrence(IntegrationTestCase):
def setUp(self):
self.project = _make_project("Recurrence Test Project")

def tearDown(self):
frappe.db.rollback()

def _open_instances(self, parent):
return frappe.get_all(
"Hive Task",
filters={"recurring_parent": parent.name, "status": ("!=", "Done")},
pluck="due_date",
)

def test_overdue_task_spawns_next_instance_from_today(self):
task = _make_task(
self.project,
"Daily Standup Notes",
recurrence_frequency="Daily",
due_date=add_days(today(), -3),
)
task.status = "Done"
task.save()

self.assertEqual(self._open_instances(task), [getdate(today())])

def test_reopening_and_completing_does_not_duplicate_next_instance(self):
task = _make_task(
self.project,
"Weekly Report",
recurrence_frequency="Weekly",
due_date=today(),
)
task.status = "Done"
task.save()
task.status = "To Do"
task.save()
task.status = "Done"
task.save()

self.assertEqual(self._open_instances(task), [getdate(add_days(today(), 7))])
Loading