feat: project template having dependent tasks
This commit is contained in:
parent
6574404536
commit
f9751f1f95
@ -55,11 +55,13 @@ class Project(Document):
|
|||||||
|
|
||||||
# create tasks from template
|
# create tasks from template
|
||||||
project_tasks = []
|
project_tasks = []
|
||||||
|
tmp_task_details = []
|
||||||
for task in template.tasks:
|
for task in template.tasks:
|
||||||
template_task_details = frappe.get_doc("Task", task.task)
|
template_task_details = frappe.get_doc("Task", task.task)
|
||||||
|
tmp_task_details.append(template_task_details)
|
||||||
project_tasks.append(self.create_task_from_template(template_task_details))
|
project_tasks.append(self.create_task_from_template(template_task_details))
|
||||||
|
|
||||||
#self.dependency_mapping(template.tasks, project_tasks)
|
self.dependency_mapping(tmp_task_details, project_tasks)
|
||||||
|
|
||||||
def create_task_from_template(self, task_details):
|
def create_task_from_template(self, task_details):
|
||||||
return frappe.get_doc(dict(
|
return frappe.get_doc(dict(
|
||||||
@ -78,16 +80,33 @@ class Project(Document):
|
|||||||
duration = task_details.duration
|
duration = task_details.duration
|
||||||
)).insert()
|
)).insert()
|
||||||
|
|
||||||
""" def dependency_mapping(self, template_tasks, project_tasks):
|
def dependency_mapping(self, template_tasks, project_tasks):
|
||||||
for tmp_task in template_tasks:
|
for tmp_task in template_tasks:
|
||||||
for prj_task in project_tasks:
|
for prj_task in project_tasks:
|
||||||
if tmp_task.subject == prj_task.subject:
|
if tmp_task.subject == prj_task.subject:
|
||||||
if tmp_task.depends_on and not prj_task.depends_on:
|
self.check_depends_on_value(tmp_task, prj_task, project_tasks)
|
||||||
for child_task in tmp_task.depends_on:
|
self.check_for_parent_tasks(tmp_task, prj_task, project_tasks)
|
||||||
child_task_detai
|
|
||||||
prj_task.depends_on = tmp_task.depends_on
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
def check_depends_on_value(self, tmp_task, prj_task, project_tasks):
|
||||||
|
if tmp_task.depends_on and not prj_task.depends_on:
|
||||||
|
for child_task in tmp_task.depends_on:
|
||||||
|
child_task_subject = frappe.db.get_value("Task", child_task.task, "subject")
|
||||||
|
corresponding_prj_task = list(filter(lambda x: x.subject == child_task_subject, project_tasks))
|
||||||
|
if len(corresponding_prj_task):
|
||||||
|
prj_task.append("depends_on",{
|
||||||
|
"task": corresponding_prj_task[0].name
|
||||||
|
})
|
||||||
|
prj_task.save()
|
||||||
|
|
||||||
|
def check_for_parent_tasks(self, tmp_task, prj_task, project_tasks):
|
||||||
|
if tmp_task.parent_task and not prj_task.parent_task:
|
||||||
|
parent_task_subject = frappe.db.get_value("Task", tmp_task.parent_task, "subject")
|
||||||
|
corresponding_prj_task = list(filter(lambda x: x.subject == parent_task_subject, project_tasks))
|
||||||
|
if len(corresponding_prj_task):
|
||||||
|
prj_task.parent_task = corresponding_prj_task[0].name
|
||||||
|
print(prj_task.name, prj_task.parent_task, corresponding_prj_task[0].name)
|
||||||
|
prj_task.save()
|
||||||
|
print(prj_task.name, corresponding_prj_task[0].name)
|
||||||
|
|
||||||
def is_row_updated(self, row, existing_task_data, fields):
|
def is_row_updated(self, row, existing_task_data, fields):
|
||||||
if self.get("__islocal") or not existing_task_data: return True
|
if self.get("__islocal") or not existing_task_data: return True
|
||||||
|
@ -3,8 +3,27 @@
|
|||||||
# For license information, please see license.txt
|
# For license information, please see license.txt
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
# import frappe
|
import frappe
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
from frappe import _
|
||||||
|
|
||||||
class ProjectTemplate(Document):
|
class ProjectTemplate(Document):
|
||||||
pass
|
|
||||||
|
def validate(self):
|
||||||
|
self.validate_dependencies()
|
||||||
|
|
||||||
|
def validate_dependencies(self):
|
||||||
|
for task in self.tasks:
|
||||||
|
task_details = frappe.get_doc("Task", task.task)
|
||||||
|
if task_details.depends_on:
|
||||||
|
for dependency_task in task_details.depends_on:
|
||||||
|
if not self.check_dependent_task_presence(dependency_task.task):
|
||||||
|
task_details_format = """<a href="#Form/Task/{0}">{0}</a>""".format(task_details.name)
|
||||||
|
dependency_task_format = """<a href="#Form/Task/{0}">{0}</a>""".format(dependency_task.task)
|
||||||
|
frappe.throw(_("Task {0} depends on Task {1}. Please add Task {1} to the Tasks list.").format(frappe.bold(task_details_format), frappe.bold(dependency_task_format)))
|
||||||
|
|
||||||
|
def check_dependent_task_presence(self, task):
|
||||||
|
for task_details in self.tasks:
|
||||||
|
if task_details.task == task:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
@ -17,291 +17,310 @@ class CircularReferenceError(frappe.ValidationError): pass
|
|||||||
class EndDateCannotBeGreaterThanProjectEndDateError(frappe.ValidationError): pass
|
class EndDateCannotBeGreaterThanProjectEndDateError(frappe.ValidationError): pass
|
||||||
|
|
||||||
class Task(NestedSet):
|
class Task(NestedSet):
|
||||||
nsm_parent_field = 'parent_task'
|
nsm_parent_field = 'parent_task'
|
||||||
|
|
||||||
def get_feed(self):
|
def get_feed(self):
|
||||||
return '{0}: {1}'.format(_(self.status), self.subject)
|
return '{0}: {1}'.format(_(self.status), self.subject)
|
||||||
|
|
||||||
def get_customer_details(self):
|
def get_customer_details(self):
|
||||||
cust = frappe.db.sql("select customer_name from `tabCustomer` where name=%s", self.customer)
|
cust = frappe.db.sql("select customer_name from `tabCustomer` where name=%s", self.customer)
|
||||||
if cust:
|
if cust:
|
||||||
ret = {'customer_name': cust and cust[0][0] or ''}
|
ret = {'customer_name': cust and cust[0][0] or ''}
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
self.validate_dates()
|
self.validate_dates()
|
||||||
self.validate_parent_project_dates()
|
self.validate_parent_project_dates()
|
||||||
self.validate_progress()
|
self.validate_progress()
|
||||||
self.validate_status()
|
self.validate_status()
|
||||||
self.update_depends_on()
|
self.update_depends_on()
|
||||||
|
self.validate_dependencies_for_template_task()
|
||||||
|
|
||||||
def validate_dates(self):
|
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):
|
if self.exp_start_date and self.exp_end_date and getdate(self.exp_start_date) > getdate(self.exp_end_date):
|
||||||
frappe.throw(_("{0} can not be greater than {1}").format(frappe.bold("Expected Start Date"), \
|
frappe.throw(_("{0} can not be greater than {1}").format(frappe.bold("Expected Start Date"), \
|
||||||
frappe.bold("Expected End Date")))
|
frappe.bold("Expected End Date")))
|
||||||
|
|
||||||
if self.act_start_date and self.act_end_date and getdate(self.act_start_date) > getdate(self.act_end_date):
|
if self.act_start_date and self.act_end_date and getdate(self.act_start_date) > getdate(self.act_end_date):
|
||||||
frappe.throw(_("{0} can not be greater than {1}").format(frappe.bold("Actual Start Date"), \
|
frappe.throw(_("{0} can not be greater than {1}").format(frappe.bold("Actual Start Date"), \
|
||||||
frappe.bold("Actual End Date")))
|
frappe.bold("Actual End Date")))
|
||||||
|
|
||||||
def validate_parent_project_dates(self):
|
def validate_parent_project_dates(self):
|
||||||
if not self.project or frappe.flags.in_test:
|
if not self.project or frappe.flags.in_test:
|
||||||
return
|
return
|
||||||
|
|
||||||
expected_end_date = frappe.db.get_value("Project", self.project, "expected_end_date")
|
expected_end_date = frappe.db.get_value("Project", self.project, "expected_end_date")
|
||||||
|
|
||||||
if expected_end_date:
|
if expected_end_date:
|
||||||
validate_project_dates(getdate(expected_end_date), self, "exp_start_date", "exp_end_date", "Expected")
|
validate_project_dates(getdate(expected_end_date), self, "exp_start_date", "exp_end_date", "Expected")
|
||||||
validate_project_dates(getdate(expected_end_date), self, "act_start_date", "act_end_date", "Actual")
|
validate_project_dates(getdate(expected_end_date), self, "act_start_date", "act_end_date", "Actual")
|
||||||
|
|
||||||
def validate_status(self):
|
def validate_status(self):
|
||||||
if self.status!=self.get_db_value("status") and self.status == "Completed":
|
if self.status!=self.get_db_value("status") and self.status == "Completed":
|
||||||
for d in self.depends_on:
|
for d in self.depends_on:
|
||||||
if frappe.db.get_value("Task", d.task, "status") not in ("Completed", "Cancelled"):
|
if frappe.db.get_value("Task", d.task, "status") not in ("Completed", "Cancelled"):
|
||||||
frappe.throw(_("Cannot complete task {0} as its dependant task {1} are not ccompleted / cancelled.").format(frappe.bold(self.name), frappe.bold(d.task)))
|
frappe.throw(_("Cannot complete task {0} as its dependant task {1} are not ccompleted / cancelled.").format(frappe.bold(self.name), frappe.bold(d.task)))
|
||||||
|
|
||||||
close_all_assignments(self.doctype, self.name)
|
close_all_assignments(self.doctype, self.name)
|
||||||
|
|
||||||
def validate_progress(self):
|
def validate_progress(self):
|
||||||
if flt(self.progress or 0) > 100:
|
if flt(self.progress or 0) > 100:
|
||||||
frappe.throw(_("Progress % for a task cannot be more than 100."))
|
frappe.throw(_("Progress % for a task cannot be more than 100."))
|
||||||
|
|
||||||
if flt(self.progress) == 100:
|
if flt(self.progress) == 100:
|
||||||
self.status = 'Completed'
|
self.status = 'Completed'
|
||||||
|
|
||||||
if self.status == 'Completed':
|
if self.status == 'Completed':
|
||||||
self.progress = 100
|
self.progress = 100
|
||||||
|
|
||||||
def update_depends_on(self):
|
def validate_dependencies_for_template_task(self):
|
||||||
depends_on_tasks = self.depends_on_tasks or ""
|
if self.is_template:
|
||||||
for d in self.depends_on:
|
self.validate_parent_template_task()
|
||||||
if d.task and not d.task in depends_on_tasks:
|
self.validate_depends_on_tasks()
|
||||||
depends_on_tasks += d.task + ","
|
|
||||||
self.depends_on_tasks = depends_on_tasks
|
def validate_parent_template_task(self):
|
||||||
|
if self.parent_task:
|
||||||
|
if not frappe.db.get_value("Task", self.parent_task, "is_template"):
|
||||||
|
parent_task_format = """<a href="#Form/Task/{0}">{0}</a>""".format(self.parent_task)
|
||||||
|
frappe.throw(_("Parent Task {0} is not a Template Task").format(parent_task_format))
|
||||||
|
|
||||||
|
def validate_depends_on_tasks(self):
|
||||||
|
if self.depends_on:
|
||||||
|
for task in self.depends_on:
|
||||||
|
if not frappe.db.get_value("Task", task.task, "is_template"):
|
||||||
|
dependent_task_format = """<a href="#Form/Task/{0}">{0}</a>""".format(task.task)
|
||||||
|
frappe.throw(_("Dependent Task {0} is not a Template Task").format(dependent_task_format))
|
||||||
|
|
||||||
def update_nsm_model(self):
|
def update_depends_on(self):
|
||||||
frappe.utils.nestedset.update_nsm(self)
|
depends_on_tasks = self.depends_on_tasks or ""
|
||||||
|
for d in self.depends_on:
|
||||||
|
if d.task and not d.task in depends_on_tasks:
|
||||||
|
depends_on_tasks += d.task + ","
|
||||||
|
self.depends_on_tasks = depends_on_tasks
|
||||||
|
|
||||||
def on_update(self):
|
def update_nsm_model(self):
|
||||||
self.update_nsm_model()
|
frappe.utils.nestedset.update_nsm(self)
|
||||||
self.check_recursion()
|
|
||||||
self.reschedule_dependent_tasks()
|
|
||||||
self.update_project()
|
|
||||||
self.unassign_todo()
|
|
||||||
self.populate_depends_on()
|
|
||||||
|
|
||||||
def unassign_todo(self):
|
def on_update(self):
|
||||||
if self.status == "Completed":
|
self.update_nsm_model()
|
||||||
close_all_assignments(self.doctype, self.name)
|
self.check_recursion()
|
||||||
if self.status == "Cancelled":
|
self.reschedule_dependent_tasks()
|
||||||
clear(self.doctype, self.name)
|
self.update_project()
|
||||||
|
self.unassign_todo()
|
||||||
|
self.populate_depends_on()
|
||||||
|
|
||||||
def update_total_expense_claim(self):
|
def unassign_todo(self):
|
||||||
self.total_expense_claim = frappe.db.sql("""select sum(total_sanctioned_amount) from `tabExpense Claim`
|
if self.status == "Completed":
|
||||||
where project = %s and task = %s and docstatus=1""",(self.project, self.name))[0][0]
|
close_all_assignments(self.doctype, self.name)
|
||||||
|
if self.status == "Cancelled":
|
||||||
|
clear(self.doctype, self.name)
|
||||||
|
|
||||||
def update_time_and_costing(self):
|
def update_total_expense_claim(self):
|
||||||
tl = frappe.db.sql("""select min(from_time) as start_date, max(to_time) as end_date,
|
self.total_expense_claim = frappe.db.sql("""select sum(total_sanctioned_amount) from `tabExpense Claim`
|
||||||
sum(billing_amount) as total_billing_amount, sum(costing_amount) as total_costing_amount,
|
where project = %s and task = %s and docstatus=1""",(self.project, self.name))[0][0]
|
||||||
sum(hours) as time from `tabTimesheet Detail` where task = %s and docstatus=1"""
|
|
||||||
,self.name, as_dict=1)[0]
|
|
||||||
if self.status == "Open":
|
|
||||||
self.status = "Working"
|
|
||||||
self.total_costing_amount= tl.total_costing_amount
|
|
||||||
self.total_billing_amount= tl.total_billing_amount
|
|
||||||
self.actual_time= tl.time
|
|
||||||
self.act_start_date= tl.start_date
|
|
||||||
self.act_end_date= tl.end_date
|
|
||||||
|
|
||||||
def update_project(self):
|
def update_time_and_costing(self):
|
||||||
if self.project and not self.flags.from_project:
|
tl = frappe.db.sql("""select min(from_time) as start_date, max(to_time) as end_date,
|
||||||
frappe.get_cached_doc("Project", self.project).update_project()
|
sum(billing_amount) as total_billing_amount, sum(costing_amount) as total_costing_amount,
|
||||||
|
sum(hours) as time from `tabTimesheet Detail` where task = %s and docstatus=1"""
|
||||||
|
,self.name, as_dict=1)[0]
|
||||||
|
if self.status == "Open":
|
||||||
|
self.status = "Working"
|
||||||
|
self.total_costing_amount= tl.total_costing_amount
|
||||||
|
self.total_billing_amount= tl.total_billing_amount
|
||||||
|
self.actual_time= tl.time
|
||||||
|
self.act_start_date= tl.start_date
|
||||||
|
self.act_end_date= tl.end_date
|
||||||
|
|
||||||
def check_recursion(self):
|
def update_project(self):
|
||||||
if self.flags.ignore_recursion_check: return
|
if self.project and not self.flags.from_project:
|
||||||
check_list = [['task', 'parent'], ['parent', 'task']]
|
frappe.get_cached_doc("Project", self.project).update_project()
|
||||||
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:
|
def check_recursion(self):
|
||||||
break
|
if self.flags.ignore_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])
|
||||||
|
|
||||||
def reschedule_dependent_tasks(self):
|
if count == 15:
|
||||||
end_date = self.exp_end_date or self.act_end_date
|
break
|
||||||
if end_date:
|
|
||||||
for task_name in frappe.db.sql("""
|
|
||||||
select name from `tabTask` as parent
|
|
||||||
where parent.project = %(project)s
|
|
||||||
and parent.name in (
|
|
||||||
select parent from `tabTask Depends On` as child
|
|
||||||
where child.task = %(task)s and child.project = %(project)s)
|
|
||||||
""", {'project': self.project, 'task':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.ignore_recursion_check = True
|
|
||||||
task.save()
|
|
||||||
|
|
||||||
def has_webform_permission(self):
|
def reschedule_dependent_tasks(self):
|
||||||
project_user = frappe.db.get_value("Project User", {"parent": self.project, "user":frappe.session.user} , "user")
|
end_date = self.exp_end_date or self.act_end_date
|
||||||
if project_user:
|
if end_date:
|
||||||
return True
|
for task_name in frappe.db.sql("""
|
||||||
|
select name from `tabTask` as parent
|
||||||
|
where parent.project = %(project)s
|
||||||
|
and parent.name in (
|
||||||
|
select parent from `tabTask Depends On` as child
|
||||||
|
where child.task = %(task)s and child.project = %(project)s)
|
||||||
|
""", {'project': self.project, 'task':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.ignore_recursion_check = True
|
||||||
|
task.save()
|
||||||
|
|
||||||
def populate_depends_on(self):
|
def has_webform_permission(self):
|
||||||
if self.parent_task:
|
project_user = frappe.db.get_value("Project User", {"parent": self.project, "user":frappe.session.user} , "user")
|
||||||
parent = frappe.get_doc('Task', self.parent_task)
|
if project_user:
|
||||||
if not self.name in [row.task for row in parent.depends_on]:
|
return True
|
||||||
parent.append("depends_on", {
|
|
||||||
"doctype": "Task Depends On",
|
|
||||||
"task": self.name,
|
|
||||||
"subject": self.subject
|
|
||||||
})
|
|
||||||
parent.save()
|
|
||||||
|
|
||||||
def on_trash(self):
|
def populate_depends_on(self):
|
||||||
if check_if_child_exists(self.name):
|
if self.parent_task:
|
||||||
throw(_("Child Task exists for this Task. You can not delete this Task."))
|
parent = frappe.get_doc('Task', self.parent_task)
|
||||||
|
if not self.name in [row.task for row in parent.depends_on]:
|
||||||
|
parent.append("depends_on", {
|
||||||
|
"doctype": "Task Depends On",
|
||||||
|
"task": self.name,
|
||||||
|
"subject": self.subject
|
||||||
|
})
|
||||||
|
parent.save()
|
||||||
|
|
||||||
self.update_nsm_model()
|
def on_trash(self):
|
||||||
|
if check_if_child_exists(self.name):
|
||||||
|
throw(_("Child Task exists for this Task. You can not delete this Task."))
|
||||||
|
|
||||||
def after_delete(self):
|
self.update_nsm_model()
|
||||||
self.update_project()
|
|
||||||
|
|
||||||
def update_status(self):
|
def after_delete(self):
|
||||||
if self.status not in ('Cancelled', 'Completed') and self.exp_end_date:
|
self.update_project()
|
||||||
from datetime import datetime
|
|
||||||
if self.exp_end_date < datetime.now().date():
|
def update_status(self):
|
||||||
self.db_set('status', 'Overdue', update_modified=False)
|
if self.status not in ('Cancelled', 'Completed') and self.exp_end_date:
|
||||||
self.update_project()
|
from datetime import datetime
|
||||||
|
if self.exp_end_date < datetime.now().date():
|
||||||
|
self.db_set('status', 'Overdue', update_modified=False)
|
||||||
|
self.update_project()
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def check_if_child_exists(name):
|
def check_if_child_exists(name):
|
||||||
child_tasks = frappe.get_all("Task", filters={"parent_task": name})
|
child_tasks = frappe.get_all("Task", filters={"parent_task": name})
|
||||||
child_tasks = [get_link_to_form("Task", task.name) for task in child_tasks]
|
child_tasks = [get_link_to_form("Task", task.name) for task in child_tasks]
|
||||||
return child_tasks
|
return child_tasks
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
@frappe.validate_and_sanitize_search_inputs
|
@frappe.validate_and_sanitize_search_inputs
|
||||||
def get_project(doctype, txt, searchfield, start, page_len, filters):
|
def get_project(doctype, txt, searchfield, start, page_len, filters):
|
||||||
from erpnext.controllers.queries import get_match_cond
|
from erpnext.controllers.queries import get_match_cond
|
||||||
return frappe.db.sql(""" select name from `tabProject`
|
return frappe.db.sql(""" select name from `tabProject`
|
||||||
where %(key)s like %(txt)s
|
where %(key)s like %(txt)s
|
||||||
%(mcond)s
|
%(mcond)s
|
||||||
order by name
|
order by name
|
||||||
limit %(start)s, %(page_len)s""" % {
|
limit %(start)s, %(page_len)s""" % {
|
||||||
'key': searchfield,
|
'key': searchfield,
|
||||||
'txt': frappe.db.escape('%' + txt + '%'),
|
'txt': frappe.db.escape('%' + txt + '%'),
|
||||||
'mcond':get_match_cond(doctype),
|
'mcond':get_match_cond(doctype),
|
||||||
'start': start,
|
'start': start,
|
||||||
'page_len': page_len
|
'page_len': page_len
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def set_multiple_status(names, status):
|
def set_multiple_status(names, status):
|
||||||
names = json.loads(names)
|
names = json.loads(names)
|
||||||
for name in names:
|
for name in names:
|
||||||
task = frappe.get_doc("Task", name)
|
task = frappe.get_doc("Task", name)
|
||||||
task.status = status
|
task.status = status
|
||||||
task.save()
|
task.save()
|
||||||
|
|
||||||
def set_tasks_as_overdue():
|
def set_tasks_as_overdue():
|
||||||
tasks = frappe.get_all("Task", filters={"status": ["not in", ["Cancelled", "Completed"]]}, fields=["name", "status", "review_date"])
|
tasks = frappe.get_all("Task", filters={"status": ["not in", ["Cancelled", "Completed"]]}, fields=["name", "status", "review_date"])
|
||||||
for task in tasks:
|
for task in tasks:
|
||||||
if task.status == "Pending Review":
|
if task.status == "Pending Review":
|
||||||
if getdate(task.review_date) > getdate(today()):
|
if getdate(task.review_date) > getdate(today()):
|
||||||
continue
|
continue
|
||||||
frappe.get_doc("Task", task.name).update_status()
|
frappe.get_doc("Task", task.name).update_status()
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def make_timesheet(source_name, target_doc=None, ignore_permissions=False):
|
def make_timesheet(source_name, target_doc=None, ignore_permissions=False):
|
||||||
def set_missing_values(source, target):
|
def set_missing_values(source, target):
|
||||||
target.append("time_logs", {
|
target.append("time_logs", {
|
||||||
"hours": source.actual_time,
|
"hours": source.actual_time,
|
||||||
"completed": source.status == "Completed",
|
"completed": source.status == "Completed",
|
||||||
"project": source.project,
|
"project": source.project,
|
||||||
"task": source.name
|
"task": source.name
|
||||||
})
|
})
|
||||||
|
|
||||||
doclist = get_mapped_doc("Task", source_name, {
|
doclist = get_mapped_doc("Task", source_name, {
|
||||||
"Task": {
|
"Task": {
|
||||||
"doctype": "Timesheet"
|
"doctype": "Timesheet"
|
||||||
}
|
}
|
||||||
}, target_doc, postprocess=set_missing_values, ignore_permissions=ignore_permissions)
|
}, target_doc, postprocess=set_missing_values, ignore_permissions=ignore_permissions)
|
||||||
|
|
||||||
return doclist
|
return doclist
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_children(doctype, parent, task=None, project=None, is_root=False):
|
def get_children(doctype, parent, task=None, project=None, is_root=False):
|
||||||
|
|
||||||
filters = [['docstatus', '<', '2']]
|
filters = [['docstatus', '<', '2']]
|
||||||
|
|
||||||
if task:
|
if task:
|
||||||
filters.append(['parent_task', '=', task])
|
filters.append(['parent_task', '=', task])
|
||||||
elif parent and not is_root:
|
elif parent and not is_root:
|
||||||
# via expand child
|
# via expand child
|
||||||
filters.append(['parent_task', '=', parent])
|
filters.append(['parent_task', '=', parent])
|
||||||
else:
|
else:
|
||||||
filters.append(['ifnull(`parent_task`, "")', '=', ''])
|
filters.append(['ifnull(`parent_task`, "")', '=', ''])
|
||||||
|
|
||||||
if project:
|
if project:
|
||||||
filters.append(['project', '=', project])
|
filters.append(['project', '=', project])
|
||||||
|
|
||||||
tasks = frappe.get_list(doctype, fields=[
|
tasks = frappe.get_list(doctype, fields=[
|
||||||
'name as value',
|
'name as value',
|
||||||
'subject as title',
|
'subject as title',
|
||||||
'is_group as expandable'
|
'is_group as expandable'
|
||||||
], filters=filters, order_by='name')
|
], filters=filters, order_by='name')
|
||||||
|
|
||||||
# return tasks
|
# return tasks
|
||||||
return tasks
|
return tasks
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def add_node():
|
def add_node():
|
||||||
from frappe.desk.treeview import make_tree_args
|
from frappe.desk.treeview import make_tree_args
|
||||||
args = frappe.form_dict
|
args = frappe.form_dict
|
||||||
args.update({
|
args.update({
|
||||||
"name_field": "subject"
|
"name_field": "subject"
|
||||||
})
|
})
|
||||||
args = make_tree_args(**args)
|
args = make_tree_args(**args)
|
||||||
|
|
||||||
if args.parent_task == 'All Tasks' or args.parent_task == args.project:
|
if args.parent_task == 'All Tasks' or args.parent_task == args.project:
|
||||||
args.parent_task = None
|
args.parent_task = None
|
||||||
|
|
||||||
frappe.get_doc(args).insert()
|
frappe.get_doc(args).insert()
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def add_multiple_tasks(data, parent):
|
def add_multiple_tasks(data, parent):
|
||||||
data = json.loads(data)
|
data = json.loads(data)
|
||||||
new_doc = {'doctype': 'Task', 'parent_task': parent if parent!="All Tasks" else ""}
|
new_doc = {'doctype': 'Task', 'parent_task': parent if parent!="All Tasks" else ""}
|
||||||
new_doc['project'] = frappe.db.get_value('Task', {"name": parent}, 'project') or ""
|
new_doc['project'] = frappe.db.get_value('Task', {"name": parent}, 'project') or ""
|
||||||
|
|
||||||
for d in data:
|
for d in data:
|
||||||
if not d.get("subject"): continue
|
if not d.get("subject"): continue
|
||||||
new_doc['subject'] = d.get("subject")
|
new_doc['subject'] = d.get("subject")
|
||||||
new_task = frappe.get_doc(new_doc)
|
new_task = frappe.get_doc(new_doc)
|
||||||
new_task.insert()
|
new_task.insert()
|
||||||
|
|
||||||
def on_doctype_update():
|
def on_doctype_update():
|
||||||
frappe.db.add_index("Task", ["lft", "rgt"])
|
frappe.db.add_index("Task", ["lft", "rgt"])
|
||||||
|
|
||||||
def validate_project_dates(project_end_date, task, task_start, task_end, actual_or_expected_date):
|
def validate_project_dates(project_end_date, task, task_start, task_end, actual_or_expected_date):
|
||||||
if task.get(task_start) and date_diff(project_end_date, getdate(task.get(task_start))) < 0:
|
if task.get(task_start) and date_diff(project_end_date, getdate(task.get(task_start))) < 0:
|
||||||
frappe.throw(_("Task's {0} Start Date cannot be after Project's End Date.").format(actual_or_expected_date))
|
frappe.throw(_("Task's {0} Start Date cannot be after Project's End Date.").format(actual_or_expected_date))
|
||||||
|
|
||||||
if task.get(task_end) and date_diff(project_end_date, getdate(task.get(task_end))) < 0:
|
if task.get(task_end) and date_diff(project_end_date, getdate(task.get(task_end))) < 0:
|
||||||
frappe.throw(_("Task's {0} End Date cannot be after Project's End Date.").format(actual_or_expected_date))
|
frappe.throw(_("Task's {0} End Date cannot be after Project's End Date.").format(actual_or_expected_date))
|
||||||
|
Loading…
Reference in New Issue
Block a user