From 19ff2489f1f4e5fc57da72c57f13cac024cf96a0 Mon Sep 17 00:00:00 2001 From: Himanshu Warekar Date: Sat, 21 Sep 2019 17:25:56 +0530 Subject: [PATCH 1/5] feat: assignment rule days --- .../assignment_rule/assignment_rule.json | 15 +++++++++- .../assignment_rule/assignment_rule.py | 17 +++++++++++ .../doctype/assignment_rule_day/__init__.py | 0 .../assignment_rule_day.json | 28 +++++++++++++++++++ .../assignment_rule_day.py | 10 +++++++ 5 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 frappe/automation/doctype/assignment_rule_day/__init__.py create mode 100644 frappe/automation/doctype/assignment_rule_day/assignment_rule_day.json create mode 100644 frappe/automation/doctype/assignment_rule_day/assignment_rule_day.py diff --git a/frappe/automation/doctype/assignment_rule/assignment_rule.json b/frappe/automation/doctype/assignment_rule/assignment_rule.json index 799efefd04..e401881976 100644 --- a/frappe/automation/doctype/assignment_rule/assignment_rule.json +++ b/frappe/automation/doctype/assignment_rule/assignment_rule.json @@ -18,6 +18,8 @@ "unassign_condition", "section_break_10", "close_condition", + "sb", + "assignment_day", "assign_to_users_section", "rule", "users", @@ -115,9 +117,20 @@ "fieldname": "close_condition", "fieldtype": "Code", "label": "Close Condition" + }, + { + "fieldname": "sb", + "fieldtype": "Section Break", + "label": "Assignment Days" + }, + { + "fieldname": "assignment_day", + "fieldtype": "Table", + "label": "Assignment Days", + "options": "Assignment Rule Day" } ], - "modified": "2019-09-10 14:45:53.657667", + "modified": "2019-09-21 16:54:10.370154", "modified_by": "Administrator", "module": "Automation", "name": "Assignment Rule", diff --git a/frappe/automation/doctype/assignment_rule/assignment_rule.py b/frappe/automation/doctype/assignment_rule/assignment_rule.py index f4c4a25830..0ba64b6595 100644 --- a/frappe/automation/doctype/assignment_rule/assignment_rule.py +++ b/frappe/automation/doctype/assignment_rule/assignment_rule.py @@ -118,6 +118,9 @@ class AssignmentRule(Document): return False + def get_assignment_days(self): + return [d.day for d in self.assignment_days] + def get_assignments(doc): return frappe.get_all('ToDo', fields = ['name', 'assignment_rule'], filters = dict( reference_type = doc.get('doctype'), @@ -175,12 +178,18 @@ def apply(doc, method=None, doctype=None, name=None): clear = True # are all assignments cleared new_apply = False # are new assignments applied + today = frappe.utils.get_weekday() + if assignments: # first unassign # use case, there are separate groups to be assigned for say L1 and L2, # so when the value switches from L1 to L2, L1 team must be unassigned, then L2 can be assigned. clear = False for assignment_rule in assignment_rule_docs: + assignment_rule_days = assignment_rule.get_assignment_days() + if assignment_rule_days and not today in assignment_rule_days: + continue + clear = assignment_rule.apply_unassign(doc, assignments) if clear: break @@ -188,6 +197,10 @@ def apply(doc, method=None, doctype=None, name=None): # apply rule only if there are no existing assignments if clear: for assignment_rule in assignment_rule_docs: + assignment_rule_days = assignment_rule.get_assignment_days() + if assignment_rule_days and not today in assignment_rule_days: + continue + new_apply = assignment_rule.apply_assign(doc) if new_apply: break @@ -196,6 +209,10 @@ def apply(doc, method=None, doctype=None, name=None): assignments = get_assignments(doc) if assignments: for assignment_rule in assignment_rule_docs: + assignment_rule_days = assignment_rule.get_assignment_days() + if assignment_rule_days and not today in assignment_rule_days: + continue + if not new_apply: reopen = reopen_closed_assignment(doc) if reopen: diff --git a/frappe/automation/doctype/assignment_rule_day/__init__.py b/frappe/automation/doctype/assignment_rule_day/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/frappe/automation/doctype/assignment_rule_day/assignment_rule_day.json b/frappe/automation/doctype/assignment_rule_day/assignment_rule_day.json new file mode 100644 index 0000000000..2a4187965c --- /dev/null +++ b/frappe/automation/doctype/assignment_rule_day/assignment_rule_day.json @@ -0,0 +1,28 @@ +{ + "creation": "2019-09-21 16:52:01.705351", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "day" + ], + "fields": [ + { + "fieldname": "day", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Day", + "options": "Monday\nTuesday\nWednesday\nThursday\nFriday\nSaturday\nSunday" + } + ], + "istable": 1, + "modified": "2019-09-21 16:55:09.376291", + "modified_by": "Administrator", + "module": "Automation", + "name": "Assignment Rule Day", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/frappe/automation/doctype/assignment_rule_day/assignment_rule_day.py b/frappe/automation/doctype/assignment_rule_day/assignment_rule_day.py new file mode 100644 index 0000000000..27f9aa40e1 --- /dev/null +++ b/frappe/automation/doctype/assignment_rule_day/assignment_rule_day.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2019, Frappe Technologies and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +# import frappe +from frappe.model.document import Document + +class AssignmentRuleDay(Document): + pass From 71ec11e3eea80459bd0775e52bca5f18285511d7 Mon Sep 17 00:00:00 2001 From: Himanshu Warekar Date: Sat, 21 Sep 2019 17:42:38 +0530 Subject: [PATCH 2/5] chore: rename fieldname --- .../automation/doctype/assignment_rule/assignment_rule.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frappe/automation/doctype/assignment_rule/assignment_rule.json b/frappe/automation/doctype/assignment_rule/assignment_rule.json index e401881976..c6c426f3be 100644 --- a/frappe/automation/doctype/assignment_rule/assignment_rule.json +++ b/frappe/automation/doctype/assignment_rule/assignment_rule.json @@ -19,7 +19,7 @@ "section_break_10", "close_condition", "sb", - "assignment_day", + "assignment_days", "assign_to_users_section", "rule", "users", @@ -124,13 +124,13 @@ "label": "Assignment Days" }, { - "fieldname": "assignment_day", + "fieldname": "assignment_days", "fieldtype": "Table", "label": "Assignment Days", "options": "Assignment Rule Day" } ], - "modified": "2019-09-21 16:54:10.370154", + "modified": "2019-09-21 17:36:16.787602", "modified_by": "Administrator", "module": "Automation", "name": "Assignment Rule", From 9c432ccb74cea64f1c9da30d653d7569b3223b8b Mon Sep 17 00:00:00 2001 From: Himanshu Warekar Date: Thu, 26 Sep 2019 09:02:29 +0530 Subject: [PATCH 3/5] fix:check if days are repeated --- .../assignment_rule/assignment_rule.json | 5 ++- .../assignment_rule/assignment_rule.py | 42 +++++++++++++++---- .../assignment_rule/test_assignment_rule.py | 18 ++++++++ 3 files changed, 55 insertions(+), 10 deletions(-) diff --git a/frappe/automation/doctype/assignment_rule/assignment_rule.json b/frappe/automation/doctype/assignment_rule/assignment_rule.json index c6c426f3be..eb79b9e3a8 100644 --- a/frappe/automation/doctype/assignment_rule/assignment_rule.json +++ b/frappe/automation/doctype/assignment_rule/assignment_rule.json @@ -127,10 +127,11 @@ "fieldname": "assignment_days", "fieldtype": "Table", "label": "Assignment Days", - "options": "Assignment Rule Day" + "options": "Assignment Rule Day", + "reqd": 1 } ], - "modified": "2019-09-21 17:36:16.787602", + "modified": "2019-09-25 14:52:12.214514", "modified_by": "Administrator", "module": "Automation", "name": "Assignment Rule", diff --git a/frappe/automation/doctype/assignment_rule/assignment_rule.py b/frappe/automation/doctype/assignment_rule/assignment_rule.py index 0ba64b6595..768c1487d6 100644 --- a/frappe/automation/doctype/assignment_rule/assignment_rule.py +++ b/frappe/automation/doctype/assignment_rule/assignment_rule.py @@ -8,8 +8,16 @@ import frappe from frappe.model.document import Document from frappe.desk.form import assign_to import frappe.cache_manager +from frappe import _ class AssignmentRule(Document): + + def validate(self): + assignment_days = self.get_assignment_days() + if not len(set(assignment_days)) == len(assignment_days): + repeated_days = get_repeated(assignment_days) + frappe.throw(_("Assignment Day {0} has been repeated.".format(frappe.bold(repeated_days)))) + def on_update(self): # pylint: disable=no-self-use frappe.cache_manager.clear_doctype_map('Assignment Rule', self.name) @@ -121,6 +129,18 @@ class AssignmentRule(Document): def get_assignment_days(self): return [d.day for d in self.assignment_days] + def is_rule_not_applicable_today(self): + # If today is in assignment_rule_days then return False + # If today is not in assignment_rule_days then return True + today = frappe.utils.get_weekday() + print(today) + assignment_days = self.get_assignment_days() + print(assignment_days) + if assignment_days and not today in assignment_days: + return True + + return False + def get_assignments(doc): return frappe.get_all('ToDo', fields = ['name', 'assignment_rule'], filters = dict( reference_type = doc.get('doctype'), @@ -178,16 +198,13 @@ def apply(doc, method=None, doctype=None, name=None): clear = True # are all assignments cleared new_apply = False # are new assignments applied - today = frappe.utils.get_weekday() - if assignments: # first unassign # use case, there are separate groups to be assigned for say L1 and L2, # so when the value switches from L1 to L2, L1 team must be unassigned, then L2 can be assigned. clear = False for assignment_rule in assignment_rule_docs: - assignment_rule_days = assignment_rule.get_assignment_days() - if assignment_rule_days and not today in assignment_rule_days: + if assignment_rule.is_rule_not_applicable_today(): continue clear = assignment_rule.apply_unassign(doc, assignments) @@ -197,8 +214,7 @@ def apply(doc, method=None, doctype=None, name=None): # apply rule only if there are no existing assignments if clear: for assignment_rule in assignment_rule_docs: - assignment_rule_days = assignment_rule.get_assignment_days() - if assignment_rule_days and not today in assignment_rule_days: + if assignment_rule.is_rule_not_applicable_today(): continue new_apply = assignment_rule.apply_assign(doc) @@ -209,8 +225,7 @@ def apply(doc, method=None, doctype=None, name=None): assignments = get_assignments(doc) if assignments: for assignment_rule in assignment_rule_docs: - assignment_rule_days = assignment_rule.get_assignment_days() - if assignment_rule_days and not today in assignment_rule_days: + if assignment_rule.is_rule_not_applicable_today(): continue if not new_apply: @@ -224,3 +239,14 @@ def apply(doc, method=None, doctype=None, name=None): def get_assignment_rules(): return [d.document_type for d in frappe.db.get_all('Assignment Rule', fields=['document_type'], filters=dict(disabled = 0))] + +def get_repeated(values): + unique_list = [] + diff = [] + for value in values: + if value not in unique_list: + unique_list.append(str(value)) + else: + if value not in diff: + diff.append(str(value)) + return " ".join(diff) \ No newline at end of file diff --git a/frappe/automation/doctype/assignment_rule/test_assignment_rule.py b/frappe/automation/doctype/assignment_rule/test_assignment_rule.py index 3b1e1a76fa..cd1ca6d619 100644 --- a/frappe/automation/doctype/assignment_rule/test_assignment_rule.py +++ b/frappe/automation/doctype/assignment_rule/test_assignment_rule.py @@ -157,6 +157,15 @@ def get_assignment_rule(): unassign_condition = 'public == 0 or notify_on_login == 1', close_condition = '"Closed" in content', rule = 'Round Robin', + assignment_days = [ + dict(day = 'Sunday'), + dict(day = 'Monday'), + dict(day = 'Tueday'), + dict(day = 'Wednesday'), + dict(day = 'Thursday'), + dict(day = 'Friday'), + dict(day = 'Saturday'), + ], users = [ dict(user = 'test@example.com'), dict(user = 'test1@example.com'), @@ -175,6 +184,15 @@ def get_assignment_rule(): assign_condition = 'notify_on_login == 1', unassign_condition = 'notify_on_login == 0', rule = 'Round Robin', + assignment_days = [ + dict(day = 'Sunday'), + dict(day = 'Monday'), + dict(day = 'Tueday'), + dict(day = 'Wednesday'), + dict(day = 'Thursday'), + dict(day = 'Friday'), + dict(day = 'Saturday'), + ], users = [ dict(user = 'test3@example.com') ] From 87b6b71f4440f23880ef16864aaa72ce481e4785 Mon Sep 17 00:00:00 2001 From: Himanshu Warekar Date: Fri, 27 Sep 2019 18:55:00 +0530 Subject: [PATCH 4/5] test: new test cases --- .../assignment_rule/assignment_rule.py | 6 +- .../assignment_rule/test_assignment_rule.py | 70 +++++++++++++------ 2 files changed, 48 insertions(+), 28 deletions(-) diff --git a/frappe/automation/doctype/assignment_rule/assignment_rule.py b/frappe/automation/doctype/assignment_rule/assignment_rule.py index 768c1487d6..f6b986c9be 100644 --- a/frappe/automation/doctype/assignment_rule/assignment_rule.py +++ b/frappe/automation/doctype/assignment_rule/assignment_rule.py @@ -130,12 +130,8 @@ class AssignmentRule(Document): return [d.day for d in self.assignment_days] def is_rule_not_applicable_today(self): - # If today is in assignment_rule_days then return False - # If today is not in assignment_rule_days then return True - today = frappe.utils.get_weekday() - print(today) + today = frappe.flags.assignment_day or frappe.utils.get_weekday() assignment_days = self.get_assignment_days() - print(assignment_days) if assignment_days and not today in assignment_days: return True diff --git a/frappe/automation/doctype/assignment_rule/test_assignment_rule.py b/frappe/automation/doctype/assignment_rule/test_assignment_rule.py index cd1ca6d619..7c68e63d95 100644 --- a/frappe/automation/doctype/assignment_rule/test_assignment_rule.py +++ b/frappe/automation/doctype/assignment_rule/test_assignment_rule.py @@ -6,10 +6,21 @@ from __future__ import unicode_literals import frappe import unittest from frappe.utils import random_string +from frappe.test_runner import make_test_records class TestAutoAssign(unittest.TestCase): def setUp(self): - self.assignment_rule = get_assignment_rule() + make_test_records("User") + days = [ + dict(day = 'Sunday'), + dict(day = 'Monday'), + dict(day = 'Tuesday'), + dict(day = 'Wednesday'), + dict(day = 'Thursday'), + dict(day = 'Friday'), + dict(day = 'Saturday'), + ] + self.assignment_rule = get_assignment_rule([days, days]) clear_assignments() def test_round_robin(self): @@ -142,30 +153,52 @@ class TestAutoAssign(unittest.TestCase): status = 'Open' ), 'owner'), 'test@example.com') + def check_assignment_rule_scheduling(self): + frappe.db.sql("DELETE FROM `tabAssignment Rule`") + + days_1 = [dict(day = 'Sunday'), dict(day = 'Monday'), dict(day = 'Tuesday')] + + days_2 = [dict(day = 'Wednesday'), dict(day = 'Thursday'), dict(day = 'Friday'), dict(day = 'Saturday')] + + get_assignment_rule([days_1, days_2], ['public == 1', 'public == 1']) + + frappe.flags.assignment_day = "Monday" + note = make_note(dict(public=1)) + + self.assertIn(frappe.db.get_value('ToDo', dict( + reference_type = 'Note', + reference_name = note.name, + status = 'Open' + ), 'owner'), ['test@example.com', 'test1@example.com', 'test2@example.com']) + + frappe.flags.assignment_day = "Friday" + note = make_note(dict(public=1)) + + self.assertIn(frappe.db.get_value('ToDo', dict( + reference_type = 'Note', + reference_name = note.name, + status = 'Open' + ), 'owner'), ['test3@example.com']) + def clear_assignments(): frappe.db.sql("delete from tabToDo where reference_type = 'Note'") -def get_assignment_rule(): +def get_assignment_rule(days, assign=None): frappe.delete_doc_if_exists('Assignment Rule', 'For Note 1') + if not assign: + assign = ['public == 1', 'notify_on_login == 1'] + assignment_rule = frappe.get_doc(dict( name = 'For Note 1', doctype = 'Assignment Rule', priority = 0, document_type = 'Note', - assign_condition = 'public == 1', + assign_condition = assign[0], unassign_condition = 'public == 0 or notify_on_login == 1', close_condition = '"Closed" in content', rule = 'Round Robin', - assignment_days = [ - dict(day = 'Sunday'), - dict(day = 'Monday'), - dict(day = 'Tueday'), - dict(day = 'Wednesday'), - dict(day = 'Thursday'), - dict(day = 'Friday'), - dict(day = 'Saturday'), - ], + assignment_days = days[0], users = [ dict(user = 'test@example.com'), dict(user = 'test1@example.com'), @@ -181,24 +214,15 @@ def get_assignment_rule(): doctype = 'Assignment Rule', priority = 1, document_type = 'Note', - assign_condition = 'notify_on_login == 1', + assign_condition = assign[1], unassign_condition = 'notify_on_login == 0', rule = 'Round Robin', - assignment_days = [ - dict(day = 'Sunday'), - dict(day = 'Monday'), - dict(day = 'Tueday'), - dict(day = 'Wednesday'), - dict(day = 'Thursday'), - dict(day = 'Friday'), - dict(day = 'Saturday'), - ], + assignment_days = days[1], users = [ dict(user = 'test3@example.com') ] )).insert() - return assignment_rule def make_note(values=None): From 09b8750d470eb391b4e3f2664ab6fafb9aeb7eca Mon Sep 17 00:00:00 2001 From: Himanshu Date: Wed, 2 Oct 2019 23:53:13 +0530 Subject: [PATCH 5/5] Update frappe/automation/doctype/assignment_rule/assignment_rule.py Co-Authored-By: Shivam Mishra --- frappe/automation/doctype/assignment_rule/assignment_rule.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/automation/doctype/assignment_rule/assignment_rule.py b/frappe/automation/doctype/assignment_rule/assignment_rule.py index f6b986c9be..b431c7c473 100644 --- a/frappe/automation/doctype/assignment_rule/assignment_rule.py +++ b/frappe/automation/doctype/assignment_rule/assignment_rule.py @@ -127,7 +127,7 @@ class AssignmentRule(Document): return False def get_assignment_days(self): - return [d.day for d in self.assignment_days] + return [d.day for d in self.get('assignment_days', [])] def is_rule_not_applicable_today(self): today = frappe.flags.assignment_day or frappe.utils.get_weekday() @@ -245,4 +245,4 @@ def get_repeated(values): else: if value not in diff: diff.append(str(value)) - return " ".join(diff) \ No newline at end of file + return " ".join(diff)