Merge pull request #24742 from deepeshgarg007/project_filter_search_fields

fix: Add searchfields in project query
This commit is contained in:
Deepesh Garg 2021-02-27 18:20:56 +05:30 committed by GitHub
commit d7a81be56d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -17,319 +17,326 @@ 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_expected_end_date() self.validate_parent_expected_end_date()
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() 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_expected_end_date(self): def validate_parent_expected_end_date(self):
if self.parent_task: if self.parent_task:
parent_exp_end_date = frappe.db.get_value("Task", self.parent_task, "exp_end_date") parent_exp_end_date = frappe.db.get_value("Task", self.parent_task, "exp_end_date")
if parent_exp_end_date and getdate(self.get("exp_end_date")) > getdate(parent_exp_end_date): if parent_exp_end_date and getdate(self.get("exp_end_date")) > getdate(parent_exp_end_date):
frappe.throw(_("Expected End Date should be less than or equal to parent task's Expected End Date {0}.").format(getdate(parent_exp_end_date))) frappe.throw(_("Expected End Date should be less than or equal to parent task's Expected End Date {0}.").format(getdate(parent_exp_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.is_template and self.status != "Template": if self.is_template and self.status != "Template":
self.status = "Template" self.status = "Template"
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 validate_dependencies_for_template_task(self): def validate_dependencies_for_template_task(self):
if self.is_template: if self.is_template:
self.validate_parent_template_task() self.validate_parent_template_task()
self.validate_depends_on_tasks() self.validate_depends_on_tasks()
def validate_parent_template_task(self): def validate_parent_template_task(self):
if self.parent_task: if self.parent_task:
if not frappe.db.get_value("Task", self.parent_task, "is_template"): 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) 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)) frappe.throw(_("Parent Task {0} is not a Template Task").format(parent_task_format))
def validate_depends_on_tasks(self): def validate_depends_on_tasks(self):
if self.depends_on: if self.depends_on:
for task in self.depends_on: for task in self.depends_on:
if not frappe.db.get_value("Task", task.task, "is_template"): if not frappe.db.get_value("Task", task.task, "is_template"):
dependent_task_format = """<a href="#Form/Task/{0}">{0}</a>""".format(task.task) 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)) frappe.throw(_("Dependent Task {0} is not a Template Task").format(dependent_task_format))
def update_depends_on(self): def update_depends_on(self):
depends_on_tasks = self.depends_on_tasks or "" depends_on_tasks = self.depends_on_tasks or ""
for d in self.depends_on: for d in self.depends_on:
if d.task and d.task not in depends_on_tasks: if d.task and d.task not in depends_on_tasks:
depends_on_tasks += d.task + "," depends_on_tasks += d.task + ","
self.depends_on_tasks = depends_on_tasks self.depends_on_tasks = depends_on_tasks
def update_nsm_model(self): def update_nsm_model(self):
frappe.utils.nestedset.update_nsm(self) frappe.utils.nestedset.update_nsm(self)
def on_update(self): def on_update(self):
self.update_nsm_model() self.update_nsm_model()
self.check_recursion() self.check_recursion()
self.reschedule_dependent_tasks() self.reschedule_dependent_tasks()
self.update_project() self.update_project()
self.unassign_todo() self.unassign_todo()
self.populate_depends_on() self.populate_depends_on()
def unassign_todo(self): def unassign_todo(self):
if self.status == "Completed": if self.status == "Completed":
close_all_assignments(self.doctype, self.name) close_all_assignments(self.doctype, self.name)
if self.status == "Cancelled": if self.status == "Cancelled":
clear(self.doctype, self.name) clear(self.doctype, self.name)
def update_total_expense_claim(self): def update_total_expense_claim(self):
self.total_expense_claim = frappe.db.sql("""select sum(total_sanctioned_amount) from `tabExpense Claim` self.total_expense_claim = frappe.db.sql("""select sum(total_sanctioned_amount) from `tabExpense Claim`
where project = %s and task = %s and docstatus=1""",(self.project, self.name))[0][0] where project = %s and task = %s and docstatus=1""",(self.project, self.name))[0][0]
def update_time_and_costing(self): def update_time_and_costing(self):
tl = frappe.db.sql("""select min(from_time) as start_date, max(to_time) as end_date, tl = frappe.db.sql("""select min(from_time) as start_date, max(to_time) as end_date,
sum(billing_amount) as total_billing_amount, sum(costing_amount) as total_costing_amount, 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""" sum(hours) as time from `tabTimesheet Detail` where task = %s and docstatus=1"""
,self.name, as_dict=1)[0] ,self.name, as_dict=1)[0]
if self.status == "Open": if self.status == "Open":
self.status = "Working" self.status = "Working"
self.total_costing_amount= tl.total_costing_amount self.total_costing_amount= tl.total_costing_amount
self.total_billing_amount= tl.total_billing_amount self.total_billing_amount= tl.total_billing_amount
self.actual_time= tl.time self.actual_time= tl.time
self.act_start_date= tl.start_date self.act_start_date= tl.start_date
self.act_end_date= tl.end_date self.act_end_date= tl.end_date
def update_project(self): def update_project(self):
if self.project and not self.flags.from_project: if self.project and not self.flags.from_project:
frappe.get_cached_doc("Project", self.project).update_project() frappe.get_cached_doc("Project", self.project).update_project()
def check_recursion(self): def check_recursion(self):
if self.flags.ignore_recursion_check: return if self.flags.ignore_recursion_check: return
check_list = [['task', 'parent'], ['parent', 'task']] check_list = [['task', 'parent'], ['parent', 'task']]
for d in check_list: for d in check_list:
task_list, count = [self.name], 0 task_list, count = [self.name], 0
while (len(task_list) > count ): while (len(task_list) > count ):
tasks = frappe.db.sql(" select %s from `tabTask Depends On` where %s = %s " % tasks = frappe.db.sql(" select %s from `tabTask Depends On` where %s = %s " %
(d[0], d[1], '%s'), cstr(task_list[count])) (d[0], d[1], '%s'), cstr(task_list[count]))
count = count + 1 count = count + 1
for b in tasks: for b in tasks:
if b[0] == self.name: if b[0] == self.name:
frappe.throw(_("Circular Reference Error"), CircularReferenceError) frappe.throw(_("Circular Reference Error"), CircularReferenceError)
if b[0]: if b[0]:
task_list.append(b[0]) task_list.append(b[0])
if count == 15: if count == 15:
break break
def reschedule_dependent_tasks(self): def reschedule_dependent_tasks(self):
end_date = self.exp_end_date or self.act_end_date end_date = self.exp_end_date or self.act_end_date
if end_date: if end_date:
for task_name in frappe.db.sql(""" for task_name in frappe.db.sql("""
select name from `tabTask` as parent select name from `tabTask` as parent
where parent.project = %(project)s where parent.project = %(project)s
and parent.name in ( and parent.name in (
select parent from `tabTask Depends On` as child select parent from `tabTask Depends On` as child
where child.task = %(task)s and child.project = %(project)s) where child.task = %(task)s and child.project = %(project)s)
""", {'project': self.project, 'task':self.name }, as_dict=1): """, {'project': self.project, 'task':self.name }, as_dict=1):
task = frappe.get_doc("Task", task_name.name) 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": 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_duration = date_diff(task.exp_end_date, task.exp_start_date)
task.exp_start_date = add_days(end_date, 1) task.exp_start_date = add_days(end_date, 1)
task.exp_end_date = add_days(task.exp_start_date, task_duration) task.exp_end_date = add_days(task.exp_start_date, task_duration)
task.flags.ignore_recursion_check = True task.flags.ignore_recursion_check = True
task.save() task.save()
def has_webform_permission(self): def has_webform_permission(self):
project_user = frappe.db.get_value("Project User", {"parent": self.project, "user":frappe.session.user} , "user") project_user = frappe.db.get_value("Project User", {"parent": self.project, "user":frappe.session.user} , "user")
if project_user: if project_user:
return True return True
def populate_depends_on(self): def populate_depends_on(self):
if self.parent_task: if self.parent_task:
parent = frappe.get_doc('Task', self.parent_task) parent = frappe.get_doc('Task', self.parent_task)
if self.name not in [row.task for row in parent.depends_on]: if self.name not in [row.task for row in parent.depends_on]:
parent.append("depends_on", { parent.append("depends_on", {
"doctype": "Task Depends On", "doctype": "Task Depends On",
"task": self.name, "task": self.name,
"subject": self.subject "subject": self.subject
}) })
parent.save() parent.save()
def on_trash(self): def on_trash(self):
if check_if_child_exists(self.name): if check_if_child_exists(self.name):
throw(_("Child Task exists for this Task. You can not delete this Task.")) throw(_("Child Task exists for this Task. You can not delete this Task."))
self.update_nsm_model() self.update_nsm_model()
def after_delete(self): def after_delete(self):
self.update_project() self.update_project()
def update_status(self): def update_status(self):
if self.status not in ('Cancelled', 'Completed') and self.exp_end_date: if self.status not in ('Cancelled', 'Completed') and self.exp_end_date:
from datetime import datetime from datetime import datetime
if self.exp_end_date < datetime.now().date(): if self.exp_end_date < datetime.now().date():
self.db_set('status', 'Overdue', update_modified=False) self.db_set('status', 'Overdue', update_modified=False)
self.update_project() 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` meta = frappe.get_meta(doctype)
where %(key)s like %(txt)s searchfields = meta.get_search_fields()
%(mcond)s search_columns = ", " + ", ".join(searchfields) if searchfields else ''
order by name search_cond = " or " + " or ".join([field + " like %(txt)s" for field in searchfields])
limit %(start)s, %(page_len)s""" % {
'key': searchfield, return frappe.db.sql(""" select name {search_columns} from `tabProject`
'txt': frappe.db.escape('%' + txt + '%'), where %(key)s like %(txt)s
'mcond':get_match_cond(doctype), %(mcond)s
'start': start, {search_condition}
'page_len': page_len order by name
}) limit %(start)s, %(page_len)s""".format(search_columns = search_columns,
search_condition=search_cond), {
'key': searchfield,
'txt': '%' + txt + '%',
'mcond':get_match_cond(doctype),
'start': start,
'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))