diff --git a/erpnext/docs/assets/img/project/percent-complete-calc.png b/erpnext/docs/assets/img/project/percent-complete-calc.png new file mode 100644 index 00000000000..25e170ddece Binary files /dev/null and b/erpnext/docs/assets/img/project/percent-complete-calc.png differ diff --git a/erpnext/docs/assets/img/project/percent-complete-formula.png b/erpnext/docs/assets/img/project/percent-complete-formula.png new file mode 100644 index 00000000000..42ff53a6f59 Binary files /dev/null and b/erpnext/docs/assets/img/project/percent-complete-formula.png differ diff --git a/erpnext/docs/assets/img/project/project-percent-complete.png b/erpnext/docs/assets/img/project/project-percent-complete.png new file mode 100644 index 00000000000..cebfde321e0 Binary files /dev/null and b/erpnext/docs/assets/img/project/project-percent-complete.png differ diff --git a/erpnext/docs/assets/img/project/task-weights.png b/erpnext/docs/assets/img/project/task-weights.png new file mode 100644 index 00000000000..c3cb2564670 Binary files /dev/null and b/erpnext/docs/assets/img/project/task-weights.png differ diff --git a/erpnext/docs/assets/img/project/tasks.png b/erpnext/docs/assets/img/project/tasks.png new file mode 100644 index 00000000000..7ee008bed32 Binary files /dev/null and b/erpnext/docs/assets/img/project/tasks.png differ diff --git a/erpnext/docs/user/manual/en/projects/project.md b/erpnext/docs/user/manual/en/projects/project.md index e8a0e0eb878..75223962c69 100644 --- a/erpnext/docs/user/manual/en/projects/project.md +++ b/erpnext/docs/user/manual/en/projects/project.md @@ -2,6 +2,20 @@ Project management in ERPNext is Task driven. You can create Project and assign Project +You can also track % Completion of a Project using different methods. + + 1. Task Completion + 2. Task Progress + 3. Task Weight + +Project + +Some examples of how the % Completion is calculated based on Tasks. + +Project + +Project + ### Managing tasks Project can be divided into multiple Tasks. Task can be created via Project document itself or can be created via [Task]({{docs_base_url}}/user/manual/en/projects/tasks.html) @@ -18,6 +32,12 @@ Task can be created via Project document itself or can be created via [Task]({{ Project - Task Grid +* To add Weights to Tasks you can follow the below steps + +Project - Task Grid +Project - Task Grid + + ### Managing time ERPNext uses [Time Log]({{docs_base_url}}/user/manual/en/projects/time-log.html) to track the progress of a Project. diff --git a/erpnext/projects/doctype/project/project.json b/erpnext/projects/doctype/project/project.json index f0944285ce9..05e716011c7 100644 --- a/erpnext/projects/doctype/project/project.json +++ b/erpnext/projects/doctype/project/project.json @@ -3,16 +3,19 @@ "allow_import": 1, "allow_rename": 1, "autoname": "field:project_name", + "beta": 0, "creation": "2013-03-07 11:55:07", "custom": 0, "docstatus": 0, "doctype": "DocType", "document_type": "Setup", + "editable_grid": 0, "fields": [ { "allow_on_submit": 0, "bold": 0, "collapsible": 0, + "columns": 0, "description": "", "fieldname": "project_name", "fieldtype": "Data", @@ -39,6 +42,7 @@ "allow_on_submit": 0, "bold": 0, "collapsible": 0, + "columns": 0, "default": "Open", "fieldname": "status", "fieldtype": "Select", @@ -67,6 +71,7 @@ "allow_on_submit": 0, "bold": 0, "collapsible": 0, + "columns": 0, "fieldname": "project_type", "fieldtype": "Select", "hidden": 0, @@ -94,6 +99,7 @@ "allow_on_submit": 0, "bold": 0, "collapsible": 0, + "columns": 0, "fieldname": "is_active", "fieldtype": "Select", "hidden": 0, @@ -121,6 +127,35 @@ "allow_on_submit": 0, "bold": 0, "collapsible": 0, + "columns": 0, + "default": "Task Completion", + "fieldname": "percent_complete_method", + "fieldtype": "Select", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "% Complete Method", + "length": 0, + "no_copy": 0, + "options": "Task Completion\nTask Progress\nTask Weight", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, "fieldname": "column_break_5", "fieldtype": "Column Break", "hidden": 0, @@ -145,6 +180,7 @@ "allow_on_submit": 0, "bold": 0, "collapsible": 0, + "columns": 0, "fieldname": "priority", "fieldtype": "Select", "hidden": 0, @@ -172,6 +208,7 @@ "allow_on_submit": 0, "bold": 0, "collapsible": 0, + "columns": 0, "fieldname": "expected_start_date", "fieldtype": "Date", "hidden": 0, @@ -198,6 +235,7 @@ "allow_on_submit": 0, "bold": 1, "collapsible": 0, + "columns": 0, "fieldname": "expected_end_date", "fieldtype": "Date", "hidden": 0, @@ -220,10 +258,36 @@ "set_only_once": 0, "unique": 0 }, + { + "allow_on_submit": 0, + "bold": 1, + "collapsible": 0, + "columns": 0, + "fieldname": "percent_complete", + "fieldtype": "Percent", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "% Completed", + "length": 0, + "no_copy": 1, + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, { "allow_on_submit": 0, "bold": 0, "collapsible": 1, + "columns": 0, "fieldname": "customer_details", "fieldtype": "Section Break", "hidden": 0, @@ -250,6 +314,7 @@ "allow_on_submit": 0, "bold": 0, "collapsible": 0, + "columns": 0, "fieldname": "customer", "fieldtype": "Link", "hidden": 0, @@ -277,6 +342,7 @@ "allow_on_submit": 0, "bold": 0, "collapsible": 0, + "columns": 0, "fieldname": "column_break_14", "fieldtype": "Column Break", "hidden": 0, @@ -301,6 +367,7 @@ "allow_on_submit": 0, "bold": 0, "collapsible": 0, + "columns": 0, "fieldname": "sales_order", "fieldtype": "Link", "hidden": 0, @@ -327,6 +394,7 @@ "allow_on_submit": 0, "bold": 0, "collapsible": 1, + "columns": 0, "fieldname": "users_section", "fieldtype": "Section Break", "hidden": 0, @@ -352,6 +420,7 @@ "allow_on_submit": 0, "bold": 0, "collapsible": 0, + "columns": 0, "description": "Project will be accessible on the website to these users", "fieldname": "users", "fieldtype": "Table", @@ -379,6 +448,7 @@ "allow_on_submit": 0, "bold": 0, "collapsible": 0, + "columns": 0, "fieldname": "sb_milestones", "fieldtype": "Section Break", "hidden": 0, @@ -405,6 +475,7 @@ "allow_on_submit": 0, "bold": 0, "collapsible": 0, + "columns": 0, "fieldname": "tasks", "fieldtype": "Table", "hidden": 0, @@ -427,34 +498,11 @@ "set_only_once": 0, "unique": 0 }, - { - "allow_on_submit": 0, - "bold": 1, - "collapsible": 0, - "fieldname": "percent_complete", - "fieldtype": "Percent", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "label": "% Tasks Completed", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, { "allow_on_submit": 0, "bold": 0, "collapsible": 1, + "columns": 0, "fieldname": "section_break0", "fieldtype": "Section Break", "hidden": 0, @@ -481,6 +529,7 @@ "allow_on_submit": 0, "bold": 0, "collapsible": 0, + "columns": 0, "fieldname": "notes", "fieldtype": "Text Editor", "hidden": 0, @@ -507,6 +556,7 @@ "allow_on_submit": 0, "bold": 0, "collapsible": 1, + "columns": 0, "fieldname": "section_break_18", "fieldtype": "Section Break", "hidden": 0, @@ -532,6 +582,7 @@ "allow_on_submit": 0, "bold": 0, "collapsible": 0, + "columns": 0, "fieldname": "actual_start_date", "fieldtype": "Data", "hidden": 0, @@ -557,6 +608,7 @@ "allow_on_submit": 0, "bold": 0, "collapsible": 0, + "columns": 0, "fieldname": "actual_time", "fieldtype": "Float", "hidden": 0, @@ -582,6 +634,7 @@ "allow_on_submit": 0, "bold": 0, "collapsible": 0, + "columns": 0, "fieldname": "column_break_20", "fieldtype": "Column Break", "hidden": 0, @@ -606,6 +659,7 @@ "allow_on_submit": 0, "bold": 0, "collapsible": 0, + "columns": 0, "fieldname": "actual_end_date", "fieldtype": "Date", "hidden": 0, @@ -632,6 +686,7 @@ "allow_on_submit": 0, "bold": 0, "collapsible": 1, + "columns": 0, "fieldname": "project_details", "fieldtype": "Section Break", "hidden": 0, @@ -658,6 +713,7 @@ "allow_on_submit": 0, "bold": 0, "collapsible": 0, + "columns": 0, "fieldname": "estimated_costing", "fieldtype": "Currency", "hidden": 0, @@ -685,6 +741,7 @@ "allow_on_submit": 0, "bold": 0, "collapsible": 0, + "columns": 0, "description": "", "fieldname": "total_costing_amount", "fieldtype": "Currency", @@ -711,6 +768,7 @@ "allow_on_submit": 0, "bold": 0, "collapsible": 0, + "columns": 0, "description": "", "fieldname": "total_expense_claim", "fieldtype": "Currency", @@ -737,6 +795,7 @@ "allow_on_submit": 0, "bold": 0, "collapsible": 0, + "columns": 0, "fieldname": "company", "fieldtype": "Link", "hidden": 0, @@ -762,6 +821,7 @@ "allow_on_submit": 0, "bold": 0, "collapsible": 0, + "columns": 0, "fieldname": "cost_center", "fieldtype": "Link", "hidden": 0, @@ -787,6 +847,7 @@ "allow_on_submit": 0, "bold": 0, "collapsible": 0, + "columns": 0, "fieldname": "column_break_28", "fieldtype": "Column Break", "hidden": 0, @@ -811,6 +872,7 @@ "allow_on_submit": 0, "bold": 0, "collapsible": 0, + "columns": 0, "description": "", "fieldname": "total_billing_amount", "fieldtype": "Currency", @@ -837,6 +899,7 @@ "allow_on_submit": 0, "bold": 0, "collapsible": 0, + "columns": 0, "fieldname": "total_purchase_cost", "fieldtype": "Currency", "hidden": 0, @@ -862,6 +925,7 @@ "allow_on_submit": 0, "bold": 0, "collapsible": 1, + "columns": 0, "fieldname": "margin", "fieldtype": "Section Break", "hidden": 0, @@ -888,6 +952,7 @@ "allow_on_submit": 0, "bold": 0, "collapsible": 0, + "columns": 0, "fieldname": "gross_margin", "fieldtype": "Currency", "hidden": 0, @@ -915,6 +980,7 @@ "allow_on_submit": 0, "bold": 0, "collapsible": 0, + "columns": 0, "fieldname": "column_break_37", "fieldtype": "Column Break", "hidden": 0, @@ -939,6 +1005,7 @@ "allow_on_submit": 0, "bold": 0, "collapsible": 0, + "columns": 0, "fieldname": "per_gross_margin", "fieldtype": "Percent", "hidden": 0, @@ -967,13 +1034,15 @@ "hide_toolbar": 0, "icon": "icon-puzzle-piece", "idx": 29, + "image_view": 0, "in_create": 0, "in_dialog": 0, "is_submittable": 0, "issingle": 0, "istable": 0, "max_attachments": 4, - "modified": "2016-04-22 03:15:39.635420", + "modified": "2016-10-21 04:20:14.974653", + "modified": "2016-10-23 01:02:40.632849", "modified_by": "Administrator", "module": "Projects", "name": "Project", @@ -989,6 +1058,7 @@ "export": 0, "if_owner": 0, "import": 0, + "is_custom": 0, "permlevel": 0, "print": 1, "read": 1, @@ -1009,6 +1079,7 @@ "export": 0, "if_owner": 0, "import": 0, + "is_custom": 0, "permlevel": 1, "print": 0, "read": 1, @@ -1027,4 +1098,4 @@ "sort_order": "DESC", "timeline_field": "customer", "track_seen": 1 -} \ No newline at end of file +} diff --git a/erpnext/projects/doctype/project/project.py b/erpnext/projects/doctype/project/project.py index bdf3c3bd1f9..388f43e93dc 100644 --- a/erpnext/projects/doctype/project/project.py +++ b/erpnext/projects/doctype/project/project.py @@ -36,7 +36,8 @@ class Project(Document): "start_date": task.exp_start_date, "end_date": task.exp_end_date, "description": task.description, - "task_id": task.name + "task_id": task.name, + "task_weight": task.task_weight }) def get_tasks(self): @@ -44,6 +45,7 @@ class Project(Document): def validate(self): self.validate_dates() + self.validate_weights() self.sync_tasks() self.tasks = [] self.send_welcome_email() @@ -52,6 +54,14 @@ class Project(Document): if self.expected_start_date and self.expected_end_date: if getdate(self.expected_end_date) < getdate(self.expected_start_date): frappe.throw(_("Expected End Date can not be less than Expected Start Date")) + + def validate_weights(self): + sum = 0 + for task in self.tasks: + if task.task_weight > 0: + sum = sum + task.task_weight + if sum > 0 and sum != 1: + frappe.throw(_("Total of all task weights should be 1. Please adjust weights of all Project tasks accordingly")) def sync_tasks(self): """sync tasks and remove table""" @@ -64,13 +74,13 @@ class Project(Document): else: task = frappe.new_doc("Task") task.project = self.name - task.update({ "subject": t.title, "status": t.status, "exp_start_date": t.start_date, "exp_end_date": t.end_date, "description": t.description, + "task_weight": t.task_weight }) task.flags.ignore_links = True @@ -93,13 +103,28 @@ class Project(Document): self.save(ignore_permissions = True) def update_percent_complete(self): - total = frappe.db.sql("""select count(*) from tabTask where project=%s""", self.name)[0][0] - if total: - completed = frappe.db.sql("""select count(*) from tabTask where + total = frappe.db.sql("""select count(name) from tabTask where project=%s""", self.name)[0][0] + if (self.percent_complete_method == "Task Completion" and total > 0) or (not self.percent_complete_method and total > 0): + completed = frappe.db.sql("""select count(name) from tabTask where project=%s and status in ('Closed', 'Cancelled')""", self.name)[0][0] - self.percent_complete = flt(flt(completed) / total * 100, 2) + if (self.percent_complete_method == "Task Progress" and total > 0): + progress = frappe.db.sql("""select sum(progress) from tabTask where + project=%s""", self.name)[0][0] + self.percent_complete = flt(flt(progress) / total, 2) + + if (self.percent_complete_method == "Task Weight" and total > 0): + weight_sum = frappe.db.sql("""select sum(task_weight) from tabTask where + project=%s""", self.name)[0][0] + if weight_sum == 1: + weighted_progress = frappe.db.sql("""select progress,task_weight from tabTask where + project=%s""", self.name,as_dict=1) + pct_complete=0 + for row in weighted_progress: + pct_complete += row["progress"] * row["task_weight"] + self.percent_complete = flt(flt(pct_complete), 2) + def update_costing(self): from_time_sheet = frappe.db.sql("""select sum(costing_amount) as costing_amount, diff --git a/erpnext/projects/doctype/project_task/project_task.json b/erpnext/projects/doctype/project_task/project_task.json index a0ef69df0c1..9c469f1969a 100644 --- a/erpnext/projects/doctype/project_task/project_task.json +++ b/erpnext/projects/doctype/project_task/project_task.json @@ -9,6 +9,7 @@ "doctype": "DocType", "document_type": "Other", "editable_grid": 1, + "engine": "InnoDB", "fields": [ { "allow_on_submit": 0, @@ -169,6 +170,32 @@ "set_only_once": 0, "unique": 0 }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "task_weight", + "fieldtype": "Float", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 1, + "label": "Weight", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 1, + "print_hide_if_no_value": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, { "allow_on_submit": 0, "bold": 0, @@ -258,7 +285,7 @@ "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2016-08-26 02:44:34.737837", + "modified": "2016-10-19 11:16:16.023965", "modified_by": "Administrator", "module": "Projects", "name": "Project Task", diff --git a/erpnext/projects/doctype/task/task.json b/erpnext/projects/doctype/task/task.json index 46c874677e5..6905e9d2259 100644 --- a/erpnext/projects/doctype/task/task.json +++ b/erpnext/projects/doctype/task/task.json @@ -230,6 +230,32 @@ "set_only_once": 0, "unique": 0 }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "task_weight", + "fieldtype": "Float", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Weight", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, { "allow_on_submit": 0, "bold": 0, @@ -294,7 +320,7 @@ "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 0, - "label": "Progress", + "label": "% Progress", "length": 0, "no_copy": 0, "permlevel": 0, @@ -854,7 +880,7 @@ "istable": 0, "max_attachments": 5, "menu_index": 0, - "modified": "2016-10-03 15:12:56.078675", + "modified": "2016-10-20 02:59:02.228659", "modified_by": "Administrator", "module": "Projects", "name": "Task", diff --git a/erpnext/projects/doctype/task/task.py b/erpnext/projects/doctype/task/task.py index 34b9ed5884c..a9b48fc0707 100644 --- a/erpnext/projects/doctype/task/task.py +++ b/erpnext/projects/doctype/task/task.py @@ -28,6 +28,7 @@ class Task(Document): def validate(self): self.validate_dates() + self.validate_progress() self.validate_status() self.update_depends_on() @@ -46,6 +47,10 @@ class Task(Document): from frappe.desk.form.assign_to import clear clear(self.doctype, self.name) + + def validate_progress(self): + if self.progress > 100: + frappe.throw(_("Progress % for a task cannot be more than 100.")) def update_depends_on(self): depends_on_tasks = ""