diff --git a/frappe/automation/doctype/assignment_rule/assignment_rule.json b/frappe/automation/doctype/assignment_rule/assignment_rule.json index 799efefd04..eb79b9e3a8 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_days", "assign_to_users_section", "rule", "users", @@ -115,9 +117,21 @@ "fieldname": "close_condition", "fieldtype": "Code", "label": "Close Condition" + }, + { + "fieldname": "sb", + "fieldtype": "Section Break", + "label": "Assignment Days" + }, + { + "fieldname": "assignment_days", + "fieldtype": "Table", + "label": "Assignment Days", + "options": "Assignment Rule Day", + "reqd": 1 } ], - "modified": "2019-09-10 14:45:53.657667", + "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 f4c4a25830..b431c7c473 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) @@ -118,6 +126,17 @@ class AssignmentRule(Document): return False + def get_assignment_days(self): + 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() + assignment_days = self.get_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'), @@ -181,6 +200,9 @@ def apply(doc, method=None, doctype=None, name=None): # 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: + if assignment_rule.is_rule_not_applicable_today(): + continue + clear = assignment_rule.apply_unassign(doc, assignments) if clear: break @@ -188,6 +210,9 @@ 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: + if assignment_rule.is_rule_not_applicable_today(): + continue + new_apply = assignment_rule.apply_assign(doc) if new_apply: break @@ -196,6 +221,9 @@ def apply(doc, method=None, doctype=None, name=None): assignments = get_assignments(doc) if assignments: for assignment_rule in assignment_rule_docs: + if assignment_rule.is_rule_not_applicable_today(): + continue + if not new_apply: reopen = reopen_closed_assignment(doc) if reopen: @@ -207,3 +235,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) diff --git a/frappe/automation/doctype/assignment_rule/test_assignment_rule.py b/frappe/automation/doctype/assignment_rule/test_assignment_rule.py index 3b1e1a76fa..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,21 +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 = days[0], users = [ dict(user = 'test@example.com'), dict(user = 'test1@example.com'), @@ -172,15 +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 = days[1], users = [ dict(user = 'test3@example.com') ] )).insert() - return assignment_rule def make_note(values=None): 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