From b4a298ddd4f99d69a460801c180bb108123329f1 Mon Sep 17 00:00:00 2001 From: Neil Trini Lasrado Date: Thu, 30 Apr 2015 20:32:42 +0530 Subject: [PATCH] multiple dependant tasks added --- .../doctype/dependent_task/__init__.py | 0 .../dependent_task/dependent_task.json | 50 ++++++++ .../doctype/dependent_task/dependent_task.py | 10 ++ erpnext/projects/doctype/task/task.js | 1 + erpnext/projects/doctype/task/task.json | 28 +++-- erpnext/projects/doctype/task/task.py | 42 ++++--- erpnext/projects/doctype/task/test_task.py | 116 ++++++++++++------ .../doctype/task_depends_on/__init__.py | 0 .../task_depends_on/task_depends_on.json | 66 ++++++++++ .../task_depends_on/task_depends_on.py | 10 ++ 10 files changed, 257 insertions(+), 66 deletions(-) create mode 100644 erpnext/projects/doctype/dependent_task/__init__.py create mode 100644 erpnext/projects/doctype/dependent_task/dependent_task.json create mode 100644 erpnext/projects/doctype/dependent_task/dependent_task.py create mode 100644 erpnext/projects/doctype/task_depends_on/__init__.py create mode 100644 erpnext/projects/doctype/task_depends_on/task_depends_on.json create mode 100644 erpnext/projects/doctype/task_depends_on/task_depends_on.py diff --git a/erpnext/projects/doctype/dependent_task/__init__.py b/erpnext/projects/doctype/dependent_task/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/projects/doctype/dependent_task/dependent_task.json b/erpnext/projects/doctype/dependent_task/dependent_task.json new file mode 100644 index 0000000000..c649b53b17 --- /dev/null +++ b/erpnext/projects/doctype/dependent_task/dependent_task.json @@ -0,0 +1,50 @@ +{ + "allow_copy": 0, + "allow_import": 0, + "allow_rename": 0, + "creation": "2015-04-29 04:52:48.868079", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "fields": [ + { + "allow_on_submit": 0, + "fieldname": "task", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 1, + "label": "Task", + "no_copy": 0, + "options": "Task", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0 + } + ], + "hide_heading": 0, + "hide_toolbar": 0, + "in_create": 0, + "in_dialog": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 1, + "modified": "2015-04-29 04:54:36.024844", + "modified_by": "Administrator", + "module": "Projects", + "name": "Dependent Task", + "name_case": "", + "owner": "Administrator", + "permissions": [], + "read_only": 0, + "read_only_onload": 0, + "sort_field": "modified", + "sort_order": "DESC" +} \ No newline at end of file diff --git a/erpnext/projects/doctype/dependent_task/dependent_task.py b/erpnext/projects/doctype/dependent_task/dependent_task.py new file mode 100644 index 0000000000..90a96ac1b7 --- /dev/null +++ b/erpnext/projects/doctype/dependent_task/dependent_task.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe.model.document import Document + +class DependentTask(Document): + pass diff --git a/erpnext/projects/doctype/task/task.js b/erpnext/projects/doctype/task/task.js index 975633186b..d9a611eed6 100644 --- a/erpnext/projects/doctype/task/task.js +++ b/erpnext/projects/doctype/task/task.js @@ -40,6 +40,7 @@ erpnext.projects.Task = frappe.ui.form.Controller.extend({ } }); +cur_frm.add_fetch('task', 'subject', 'subject'); cur_frm.cscript = new erpnext.projects.Task({frm: cur_frm}); diff --git a/erpnext/projects/doctype/task/task.json b/erpnext/projects/doctype/task/task.json index ad1ad01c4e..ddcc48b2d3 100644 --- a/erpnext/projects/doctype/task/task.json +++ b/erpnext/projects/doctype/task/task.json @@ -27,14 +27,6 @@ "options": "Project", "permlevel": 0 }, - { - "fieldname": "depends_on", - "fieldtype": "Link", - "label": "Depends on (Task)", - "options": "Task", - "permlevel": 0, - "precision": "" - }, { "fieldname": "column_break0", "fieldtype": "Column Break", @@ -86,12 +78,26 @@ "width": "300px" }, { - "fieldname": "time_and_budget", + "fieldname": "section_break", "fieldtype": "Section Break", - "label": "", + "label": "Depends On", "oldfieldtype": "Section Break", "permlevel": 0 }, + { + "fieldname": "depends_on", + "fieldtype": "Table", + "label": "depends_on", + "options": "Task Depends On", + "permlevel": 0, + "precision": "" + }, + { + "fieldname": "section_break_10", + "fieldtype": "Section Break", + "permlevel": 0, + "precision": "" + }, { "fieldname": "exp_start_date", "fieldtype": "Date", @@ -257,7 +263,7 @@ "idx": 1, "istable": 0, "max_attachments": 5, - "modified": "2015-04-22 04:58:30.865304", + "modified": "2015-04-30 05:48:55.176993", "modified_by": "Administrator", "module": "Projects", "name": "Task", diff --git a/erpnext/projects/doctype/task/task.py b/erpnext/projects/doctype/task/task.py index cde23e7db6..a232be1604 100644 --- a/erpnext/projects/doctype/task/task.py +++ b/erpnext/projects/doctype/task/task.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals import frappe, json -from frappe.utils import getdate, date_diff, add_days +from frappe.utils import getdate, date_diff, add_days, cstr from frappe import _ from frappe.model.document import Document @@ -28,8 +28,6 @@ class Task(Document): def validate(self): self.validate_dates() - self.validate_depends_on() - self.reschedule_dependent_task() def validate_dates(self): if self.exp_start_date and self.exp_end_date and getdate(self.exp_start_date) > getdate(self.exp_end_date): @@ -39,6 +37,8 @@ class Task(Document): frappe.throw(_("'Actual Start Date' can not be greater than 'Actual End Date'")) def on_update(self): + self.check_recursion() + self.reschedule_dependent_tasks() self.update_percentage() self.update_project() @@ -72,30 +72,34 @@ class Task(Document): project.update_costing() project.save() - def validate_depends_on(self): - if not self.depends_on: - return - task_list = [self.name] - task = self.depends_on - while task: - task = self.check_recursion(task, task_list) + def check_recursion(self): + if self.flags.recursion_check: return + check_list = [['task', 'parent'], ['parent', 'task']] + for d in check_list: + task_list, count = [self.name], 0 + while (len(task_list) > count ): + tasks = frappe.db.sql(" select %s from `tabTask Depends On` where %s = %s " % + (d[0], d[1], '%s'), cstr(task_list[count])) + count = count + 1 + for b in tasks: + if b[0] == self.name: + frappe.throw(_("Circular Reference Error"), CircularReferenceError) + if b[0]: + task_list.append(b[0]) + if count == 15: + break - def check_recursion(self, task, task_list): - if task in task_list: - frappe.throw("Circular Reference Error", CircularReferenceError) - else : - task_list.append(task) - return frappe.db.get_value("Task", task, "depends_on") - - def reschedule_dependent_task(self): + def reschedule_dependent_tasks(self): end_date = self.exp_end_date or self.act_end_date if end_date: - for task_name in frappe.db.sql("select name from `tabTask` where depends_on = %s", self.name, as_dict=1): + for task_name in frappe.db.sql("select name from `tabTask` as parent where %s in \ + (select task from `tabTask Depends On` as child where parent.name = child.parent )", self.name, as_dict=1): task = frappe.get_doc("Task", task_name.name) if task.exp_start_date and task.exp_end_date and task.exp_start_date < getdate(end_date) and task.status == "Open" : task_duration = date_diff(task.exp_end_date, task.exp_start_date) task.exp_start_date = add_days(end_date, 1) task.exp_end_date = add_days(task.exp_start_date, task_duration) + task.flags.recursion_check = True task.save() @frappe.whitelist() diff --git a/erpnext/projects/doctype/task/test_task.py b/erpnext/projects/doctype/task/test_task.py index aea6a2515e..8880763fa2 100644 --- a/erpnext/projects/doctype/task/test_task.py +++ b/erpnext/projects/doctype/task/test_task.py @@ -5,45 +5,17 @@ import frappe import unittest from frappe.utils import getdate -test_records = frappe.get_test_records('Task') +# test_records = frappe.get_test_records('Task') from erpnext.projects.doctype.task.task import CircularReferenceError class TestTask(unittest.TestCase): - def test_circular_refereence(self): + def test_circular_reference(self): + task1 = frappe.new_doc('Task') task1.update({ "status": "Open", - "subject": "_Test Task 3" - }) - task1.save() - - task2 = frappe.new_doc('Task') - task2.update({ - "status": "Open", - "subject": "_Test Task 4", - "depends_on": task1.name - }) - task2.save() - - task3 = frappe.new_doc('Task') - task3.update({ - "status": "Open", - "subject": "_Test Task 5", - "depends_on": task2.name - }) - task3.save() - - task1.update({ - "depends_on": task3.name - }) - self.assertRaises(CircularReferenceError, task1.save) - - def test_reschedule_dependent_task(self): - task1 = frappe.new_doc('Task') - task1.update({ - "status": "Open", - "subject": "_Test Task 6", + "subject": "_Test Task 1", "exp_start_date": "2015-1-1", "exp_end_date": "2015-1-10" }) @@ -52,20 +24,92 @@ class TestTask(unittest.TestCase): task2 = frappe.new_doc('Task') task2.update({ "status": "Open", - "subject": "_Test Task 7", + "subject": "_Test Task 2", "exp_start_date": "2015-1-11", "exp_end_date": "2015-1-15", - "depends_on": task1.name + "depends_on":[ + { + "task": task1.name + } + ] }) task2.save() task3 = frappe.new_doc('Task') task3.update({ "status": "Open", - "subject": "_Test Task 5", + "subject": "_Test Task 2", + "exp_start_date": "2015-1-11", + "exp_end_date": "2015-1-15", + "depends_on":[ + { + "task": task2.name + } + ] + }) + task3.save() + + task1.append("depends_on", { + "task": task3.name + }) + self.assertRaises(CircularReferenceError, task1.save) + + task1.set("depends_on", []) + task1.save() + + task4 = frappe.new_doc('Task') + task4.update({ + "status": "Open", + "subject": "_Test Task 1", + "exp_start_date": "2015-1-1", + "exp_end_date": "2015-1-15", + "depends_on":[ + { + "task": task1.name + } + ] + }) + task4.save() + + task3.append("depends_on", { + "task": task4.name + }) + + def test_reschedule_dependent_task(self): + task1 = frappe.new_doc('Task') + task1.update({ + "status": "Open", + "subject": "_Test Task 1", + "exp_start_date": "2015-1-1", + "exp_end_date": "2015-1-10" + }) + task1.save() + + task2 = frappe.new_doc('Task') + task2.update({ + "status": "Open", + "subject": "_Test Task 2", + "exp_start_date": "2015-1-11", + "exp_end_date": "2015-1-15", + "depends_on":[ + { + "task": task1.name + } + ] + }) + task2.save() + + task3 = frappe.new_doc('Task') + task3.update({ + "status": "Open", + "subject": "_Test Task 3", "exp_start_date": "2015-1-16", "exp_end_date": "2015-1-18", - "depends_on": task2.name + "depends_on":[ + { + "task": task2.name + } + ] }) task3.save() diff --git a/erpnext/projects/doctype/task_depends_on/__init__.py b/erpnext/projects/doctype/task_depends_on/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/projects/doctype/task_depends_on/task_depends_on.json b/erpnext/projects/doctype/task_depends_on/task_depends_on.json new file mode 100644 index 0000000000..7a960c1c41 --- /dev/null +++ b/erpnext/projects/doctype/task_depends_on/task_depends_on.json @@ -0,0 +1,66 @@ +{ + "allow_copy": 0, + "allow_import": 0, + "allow_rename": 0, + "creation": "2015-04-29 04:52:48.868079", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "fields": [ + { + "allow_on_submit": 0, + "fieldname": "task", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "in_filter": 0, + "in_list_view": 1, + "label": "Task", + "no_copy": 0, + "options": "Task", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0 + }, + { + "fieldname": "column_break_2", + "fieldtype": "Column Break", + "permlevel": 0, + "precision": "" + }, + { + "fieldname": "subject", + "fieldtype": "Text", + "in_list_view": 1, + "label": "Subject", + "options": "", + "permlevel": 0, + "precision": "", + "read_only": 1 + } + ], + "hide_heading": 0, + "hide_toolbar": 0, + "in_create": 0, + "in_dialog": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 1, + "modified": "2015-04-30 05:52:16.250948", + "modified_by": "Administrator", + "module": "Projects", + "name": "Task Depends On", + "name_case": "", + "owner": "Administrator", + "permissions": [], + "read_only": 0, + "read_only_onload": 0, + "sort_field": "modified", + "sort_order": "DESC" +} \ No newline at end of file diff --git a/erpnext/projects/doctype/task_depends_on/task_depends_on.py b/erpnext/projects/doctype/task_depends_on/task_depends_on.py new file mode 100644 index 0000000000..723a0fc339 --- /dev/null +++ b/erpnext/projects/doctype/task_depends_on/task_depends_on.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe.model.document import Document + +class TaskDependsOn(Document): + pass