BREAKING CHANGE: Remove anti-pattern "Project Task" (#18059)

* BREAKING CHANGE: Remove anti-pattern "Project Task"

* fix(tests): remove `tasks` from project/test_records.json

* fix(tests)

* fix(test): test_employee_onboarding.py

* fix(tests): test_expense_claim.py

* fix(refactor): cleanup project.py validate/update

* fix(refactor): cleanup project.py validate/update

* fix(test): test_expense_claim

* fix(test): test_expense_claim

* fix(test): test_expense_claim, try Test Company 4

* Update project.py
This commit is contained in:
Rushabh Mehta
2019-07-08 10:39:30 +05:30
committed by Nabin Hait
parent bef897602d
commit 8309fcfbbc
14 changed files with 187 additions and 2396 deletions

View File

@@ -1,23 +1,6 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
frappe.ui.form.on("Project", {
setup: function (frm) {
frm.set_indicator_formatter('title',
function (doc) {
let indicator = 'orange';
if (doc.status == 'Overdue') {
indicator = 'red';
} else if (doc.status == 'Cancelled') {
indicator = 'dark grey';
} else if (doc.status == 'Completed') {
indicator = 'green';
}
return indicator;
}
);
},
onload: function (frm) {
var so = frappe.meta.get_docfield("Project", "sales_order");
so.get_route_options_for_new_doc = function (field) {
@@ -99,58 +82,4 @@ frappe.ui.form.on("Project", {
});
},
tasks_refresh: function (frm) {
var grid = frm.get_field('tasks').grid;
grid.wrapper.find('select[data-fieldname="status"]').each(function () {
if ($(this).val() === 'Open') {
$(this).addClass('input-indicator-open');
} else {
$(this).removeClass('input-indicator-open');
}
});
},
status: function(frm) {
if (frm.doc.status === 'Cancelled') {
frappe.confirm(__('Set tasks in this project as cancelled?'), () => {
frm.doc.tasks = frm.doc.tasks.map(task => {
task.status = 'Cancelled';
return task;
});
frm.refresh_field('tasks');
});
}
}
});
frappe.ui.form.on("Project Task", {
edit_task: function(frm, doctype, name) {
var doc = frappe.get_doc(doctype, name);
if(doc.task_id) {
frappe.set_route("Form", "Task", doc.task_id);
} else {
frappe.msgprint(__("Save the document first."));
}
},
edit_timesheet: function(frm, cdt, cdn) {
var child = locals[cdt][cdn];
frappe.route_options = {"project": frm.doc.project_name, "task": child.task_id};
frappe.set_route("List", "Timesheet");
},
make_timesheet: function(frm, cdt, cdn) {
var child = locals[cdt][cdn];
frappe.model.with_doctype('Timesheet', function() {
var doc = frappe.model.get_new_doc('Timesheet');
var row = frappe.model.add_child(doc, 'time_logs');
row.project = frm.doc.project_name;
row.task = child.task_id;
frappe.set_route('Form', doc.doctype, doc.name);
})
},
status: function(frm, doctype, name) {
frm.trigger('tasks_refresh');
},
});

File diff suppressed because it is too large Load Diff

View File

@@ -19,10 +19,6 @@ class Project(Document):
return '{0}: {1}'.format(_(self.status), frappe.safe_decode(self.project_name))
def onload(self):
"""Load project tasks for quick view"""
if not self.get('__unsaved') and not self.get("tasks"):
self.load_tasks()
self.set_onload('activity_summary', frappe.db.sql('''select activity_type,
sum(hours) as total_hours
from `tabTimesheet Detail` where project=%s and docstatus < 2 group by activity_type
@@ -33,57 +29,19 @@ class Project(Document):
def before_print(self):
self.onload()
def load_tasks(self):
"""Load `tasks` from the database"""
if frappe.flags.in_import:
return
project_task_custom_fields = frappe.get_all("Custom Field", {"dt": "Project Task"}, "fieldname")
self.tasks = []
for task in self.get_tasks():
task_map = {
"title": task.subject,
"status": task.status,
"start_date": task.exp_start_date,
"end_date": task.exp_end_date,
"description": task.description,
"task_id": task.name,
"task_weight": task.task_weight
}
self.map_custom_fields(task, task_map, project_task_custom_fields)
self.append("tasks", task_map)
def get_tasks(self):
if self.name is None:
return {}
else:
filters = {"project": self.name}
if self.get("deleted_task_list"):
filters.update({
'name': ("not in", self.deleted_task_list)
})
return frappe.get_all("Task", "*", filters, order_by="exp_start_date asc, status asc")
def validate(self):
self.validate_weights()
self.sync_tasks()
self.tasks = []
self.load_tasks()
if not self.is_new():
self.copy_from_template()
self.validate_dates()
self.send_welcome_email()
self.update_percent_complete(from_validate=True)
self.update_costing()
self.update_percent_complete()
def copy_from_template(self):
'''
Copy tasks from template
'''
if self.project_template and not len(self.tasks or []):
if self.project_template and not frappe.db.get_all('Task', dict(project = self.name), limit=1):
# has a template, and no loaded tasks, so lets create
if not self.expected_start_date:
@@ -108,104 +66,6 @@ class Project(Document):
task_weight = task.task_weight
)).insert()
# reload tasks after project
self.load_tasks()
def validate_dates(self):
if self.tasks:
for d in self.tasks:
if self.expected_start_date:
if d.start_date and getdate(d.start_date) < getdate(self.expected_start_date):
frappe.throw(_("Start date of task <b>{0}</b> cannot be less than <b>{1}</b> expected start date <b>{2}</b>")
.format(d.title, self.name, self.expected_start_date))
if d.end_date and getdate(d.end_date) < getdate(self.expected_start_date):
frappe.throw(_("End date of task <b>{0}</b> cannot be less than <b>{1}</b> expected start date <b>{2}</b>")
.format(d.title, self.name, self.expected_start_date))
if self.expected_end_date:
if d.start_date and getdate(d.start_date) > getdate(self.expected_end_date):
frappe.throw(_("Start date of task <b>{0}</b> cannot be greater than <b>{1}</b> expected end date <b>{2}</b>")
.format(d.title, self.name, self.expected_end_date))
if d.end_date and getdate(d.end_date) > getdate(self.expected_end_date):
frappe.throw(_("End date of task <b>{0}</b> cannot be greater than <b>{1}</b> expected end date <b>{2}</b>")
.format(d.title, self.name, self.expected_end_date))
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):
for task in self.tasks:
if task.task_weight is not None:
if task.task_weight < 0:
frappe.throw(_("Task weight cannot be negative"))
def sync_tasks(self):
"""sync tasks and remove table"""
if not hasattr(self, "deleted_task_list"):
self.set("deleted_task_list", [])
if self.flags.dont_sync_tasks: return
task_names = []
existing_task_data = {}
fields = ["title", "status", "start_date", "end_date", "description", "task_weight", "task_id"]
exclude_fieldtype = ["Button", "Column Break",
"Section Break", "Table", "Read Only", "Attach", "Attach Image", "Color", "Geolocation", "HTML", "Image"]
custom_fields = frappe.get_all("Custom Field", {"dt": "Project Task",
"fieldtype": ("not in", exclude_fieldtype)}, "fieldname")
for d in custom_fields:
fields.append(d.fieldname)
for d in frappe.get_all('Project Task',
fields = fields,
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
if not t.task_id or self.is_row_updated(t, existing_task_data, fields):
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, custom_fields)
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.run_method("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)}):
self.deleted_task_list.append(t.name)
def is_row_updated(self, row, existing_task_data, fields):
if self.get("__islocal") or not existing_task_data: return True
@@ -215,48 +75,43 @@ class Project(Document):
if row.get(field) != d.get(field):
return True
def map_custom_fields(self, source, target, custom_fields):
for field in custom_fields:
target.update({
field.fieldname: source.get(field.fieldname)
})
def update_project(self):
'''Called externally by Task'''
self.update_percent_complete()
self.update_costing()
self.db_update()
def after_insert(self):
self.copy_from_template()
if self.sales_order:
frappe.db.set_value("Sales Order", self.sales_order, "project", self.name)
def update_percent_complete(self, from_validate=False):
if not self.tasks: return
total = frappe.db.sql("""select count(name) from tabTask where project=%s""", self.name)[0][0]
def update_percent_complete(self):
total = frappe.db.count('Task', dict(project=self.name))
if not total and self.percent_complete:
if not total:
self.percent_complete = 0
else:
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 ('Cancelled', 'Completed')""", self.name)[0][0]
self.percent_complete = flt(flt(completed) / total * 100, 2)
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 ('Cancelled', 'Completed')""", 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 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]
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"] * frappe.utils.safe_div(row["task_weight"], weight_sum)
self.percent_complete = flt(flt(pct_complete), 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]
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"] * frappe.utils.safe_div(row["task_weight"], weight_sum)
self.percent_complete = flt(flt(pct_complete), 2)
# don't update status if it is cancelled
if self.status == 'Cancelled':
@@ -268,9 +123,6 @@ class Project(Document):
else:
self.status = "Open"
if not from_validate:
self.db_update()
def update_costing(self):
from_time_sheet = frappe.db.sql("""select
sum(costing_amount) as costing_amount,
@@ -297,7 +149,6 @@ class Project(Document):
self.update_sales_amount()
self.update_billed_amount()
self.calculate_gross_margin()
self.db_update()
def calculate_gross_margin(self):
expense_amount = (flt(self.total_costing_amount) + flt(self.total_expense_claim)
@@ -348,57 +199,6 @@ class Project(Document):
content=content.format(*messages))
user.welcome_email_sent = 1
def on_update(self):
self.delete_task()
self.load_tasks()
self.update_project()
self.update_dependencies_on_duplicated_project()
def delete_task(self):
if not self.get('deleted_task_list'): return
for d in self.get('deleted_task_list'):
# unlink project
frappe.db.set_value('Task', d, 'project', '')
self.deleted_task_list = []
def update_dependencies_on_duplicated_project(self):
if self.flags.dont_sync_tasks: return
if not self.copied_from:
self.copied_from = self.name
if self.name != self.copied_from and self.get('__unsaved'):
# duplicated project
dependency_map = {}
for task in self.tasks:
_task = frappe.db.get_value(
'Task',
{"subject": task.title, "project": self.copied_from},
['name', 'depends_on_tasks'],
as_dict=True
)
if _task is None:
continue
name = _task.name
dependency_map[task.title] = [x['subject'] for x in frappe.get_list(
'Task Depends On', {"parent": name}, ['subject'])]
for key, value in iteritems(dependency_map):
task_name = frappe.db.get_value('Task', {"subject": key, "project": self.name })
task_doc = frappe.get_doc('Task', task_name)
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.db_update()
def get_timeline_data(doctype, name):
'''Return timeline for attendance'''
return dict(frappe.db.sql('''select unix_timestamp(from_time), count(*)

View File

@@ -19,18 +19,18 @@ class TestProject(unittest.TestCase):
project = get_project('Test Project with Template')
project.load_tasks()
tasks = frappe.get_all('Task', '*', dict(project=project.name), order_by='creation asc')
task1 = project.tasks[0]
self.assertEqual(task1.title, 'Task 1')
task1 = tasks[0]
self.assertEqual(task1.subject, 'Task 1')
self.assertEqual(task1.description, 'Task 1 description')
self.assertEqual(getdate(task1.start_date), getdate('2019-01-01'))
self.assertEqual(getdate(task1.end_date), getdate('2019-01-04'))
self.assertEqual(getdate(task1.exp_start_date), getdate('2019-01-01'))
self.assertEqual(getdate(task1.exp_end_date), getdate('2019-01-04'))
self.assertEqual(len(project.tasks), 4)
task4 = project.tasks[3]
self.assertEqual(task4.title, 'Task 4')
self.assertEqual(getdate(task4.end_date), getdate('2019-01-06'))
self.assertEqual(len(tasks), 4)
task4 = tasks[3]
self.assertEqual(task4.subject, 'Task 4')
self.assertEqual(getdate(task4.exp_end_date), getdate('2019-01-06'))
def get_project(name):
template = get_project_template()

View File

@@ -1,12 +1,6 @@
[
{
"project_name": "_Test Project",
"status": "Open",
"tasks":[
{
"title": "_Test Task",
"status": "Open"
}
]
"status": "Open"
}
]

View File

@@ -1,430 +0,0 @@
{
"allow_copy": 0,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
"creation": "2015-02-22 11:15:28.201059",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "Other",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 3,
"fieldname": "title",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Title",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 3,
"default": "Open",
"fieldname": "status",
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Status",
"length": 0,
"no_copy": 1,
"options": "Open\nWorking\nPending Review\nOverdue\nCompleted\nCancelled",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "task_id",
"fieldname": "edit_task",
"fieldtype": "Button",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "View Task",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "edit_timesheet",
"fieldtype": "Button",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "View Timesheet",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "make_timesheet",
"fieldtype": "Button",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Make Timesheet",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_6",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 2,
"fieldname": "start_date",
"fieldtype": "Date",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Start Date",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 2,
"default": "",
"fieldname": "end_date",
"fieldtype": "Date",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "End Date",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 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_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Weight",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "section_break_6",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "description",
"fieldtype": "Text Editor",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Description",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "task_id",
"fieldtype": "Link",
"hidden": 1,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Task ID",
"length": 0,
"no_copy": 1,
"options": "Task",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 1,
"report_hide": 0,
"reqd": 0,
"search_index": 1,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2019-02-19 12:30:52.648868",
"modified_by": "Administrator",
"module": "Projects",
"name": "Project Task",
"name_case": "",
"owner": "Administrator",
"permissions": [],
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 0,
"track_seen": 0,
"track_views": 0
}

View File

@@ -1,9 +0,0 @@
# 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 ProjectTask(Document):
pass

View File

@@ -158,12 +158,6 @@ class Task(NestedSet):
if check_if_child_exists(self.name):
throw(_("Child Task exists for this Task. You can not delete this Task."))
if self.project:
tasks = frappe.get_doc('Project', self.project).tasks
for task in tasks:
if task.get('task_id') == self.name:
frappe.delete_doc('Project Task', task.name)
self.update_nsm_model()
def update_status(self):