diff --git a/erpnext/projects/doctype/project/project.py b/erpnext/projects/doctype/project/project.py index 44b0c00e65..1f4bd005c6 100644 --- a/erpnext/projects/doctype/project/project.py +++ b/erpnext/projects/doctype/project/project.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals import frappe -from frappe.utils import flt, getdate, get_url +from frappe.utils import flt, getdate, get_url, now from frappe import _ from frappe.model.document import Document @@ -60,6 +60,7 @@ class Project(Document): self.validate_weights() self.sync_tasks() self.tasks = [] + self.load_tasks() self.send_welcome_email() def validate_project_name(self): @@ -83,36 +84,68 @@ class Project(Document): """sync tasks and remove table""" if self.flags.dont_sync_tasks: return task_names = [] + + existing_task_data = {} + for d in frappe.get_all('Project Task', + fields = ["title", "status", "start_date", "end_date", "description", "task_weight", "task_id"], + filters = {'parent': self.name}): + existing_task_data.setdefault(d.task_id, d) + for t in self.tasks: if t.task_id: task = frappe.get_doc("Task", t.task_id) 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 - }) - self.map_custom_fields(t, task) + if not t.task_id or self.is_row_updated(t, existing_task_data): + 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 - task.flags.from_project = True - task.flags.ignore_feed = True - task.save(ignore_permissions = True) - task_names.append(task.name) + self.map_custom_fields(t, task) + + task.flags.ignore_links = True + task.flags.from_project = True + task.flags.ignore_feed = True + + if t.task_id: + task.update({ + "modified_by": frappe.session.user, + "modified": now() + }) + + task.validate() + task.db_update() + else: + task.save(ignore_permissions = True) + task_names.append(task.name) + else: + task_names.append(task.name) # delete for t in frappe.get_all("Task", ["name"], {"project": self.name, "name": ("not in", task_names)}): frappe.delete_doc("Task", t.name) + def update_costing_and_percentage_complete(self): self.update_percent_complete() self.update_costing() + def is_row_updated(self, row, existing_task_data): + if self.get("__islocal") or not existing_task_data: return True + + d = existing_task_data.get(row.task_id) + + if (d and (row.title != d.title or row.status != d.status + or getdate(row.start_date) != getdate(d.start_date) or getdate(row.end_date) != getdate(d.end_date) + or row.description != d.description or row.task_weight != d.task_weight)): + return True + def map_custom_fields(self, source, target): project_task_custom_fields = frappe.get_all("Custom Field", {"dt": "Project Task"}, "fieldname") @@ -207,6 +240,9 @@ class Project(Document): self.total_billed_amount = total_billed_amount and total_billed_amount[0][0] or 0 + def after_rename(self, old_name, new_name, merge=False): + if old_name == self.copied_from: + frappe.db.set_value('Project', new_name, 'copied_from', new_name) def send_welcome_email(self): url = get_url("/project/?name={0}".format(self.name)) @@ -227,8 +263,7 @@ class Project(Document): user.welcome_email_sent=1 def on_update(self): - self.load_tasks() - self.sync_tasks() + self.update_costing_and_percentage_complete() self.update_dependencies_on_duplicated_project() def update_dependencies_on_duplicated_project(self): @@ -251,9 +286,7 @@ class Project(Document): continue name = _task.name - depends_on_tasks = _task.depends_on_tasks - depends_on_tasks = [x for x in depends_on_tasks.split(',') if x] dependency_map[task.title] = [ x['subject'] for x in frappe.get_list( 'Task Depends On', {"parent": name}, ['subject'])] @@ -264,7 +297,8 @@ class Project(Document): for dt in value: dt_name = frappe.db.get_value('Task', {"subject": dt, "project": self.name }) task_doc.append('depends_on', {"task": dt_name}) - task_doc.save() + + task_doc.update_db() def get_timeline_data(doctype, name): '''Return timeline for attendance''' diff --git a/erpnext/projects/doctype/project_task/project_task.json b/erpnext/projects/doctype/project_task/project_task.json index 79790e57b0..e5f28b76b1 100644 --- a/erpnext/projects/doctype/project_task/project_task.json +++ b/erpnext/projects/doctype/project_task/project_task.json @@ -371,7 +371,7 @@ "remember_last_selected_value": 1, "report_hide": 0, "reqd": 0, - "search_index": 0, + "search_index": 1, "set_only_once": 0, "unique": 0 } @@ -386,7 +386,7 @@ "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2017-12-19 14:49:15.886339", + "modified": "2018-06-26 11:46:50.890251", "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 8e72d0339e..cf524da486 100644 --- a/erpnext/projects/doctype/task/task.json +++ b/erpnext/projects/doctype/task/task.json @@ -38,7 +38,7 @@ "remember_last_selected_value": 0, "report_hide": 0, "reqd": 1, - "search_index": 0, + "search_index": 1, "set_only_once": 0, "unique": 0 }, @@ -70,7 +70,7 @@ "remember_last_selected_value": 1, "report_hide": 0, "reqd": 0, - "search_index": 0, + "search_index": 1, "set_only_once": 0, "unique": 0 }, @@ -1214,7 +1214,7 @@ "istable": 0, "max_attachments": 5, "menu_index": 0, - "modified": "2017-11-10 18:37:19.660293", + "modified": "2018-06-26 11:46:06.678115", "modified_by": "Administrator", "module": "Projects", "name": "Task",