mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-02 13:08:27 +00:00
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:
committed by
Nabin Hait
parent
bef897602d
commit
8309fcfbbc
@@ -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
@@ -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(*)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -1,12 +1,6 @@
|
||||
[
|
||||
{
|
||||
"project_name": "_Test Project",
|
||||
"status": "Open",
|
||||
"tasks":[
|
||||
{
|
||||
"title": "_Test Task",
|
||||
"status": "Open"
|
||||
}
|
||||
]
|
||||
"status": "Open"
|
||||
}
|
||||
]
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
@@ -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):
|
||||
|
||||
Reference in New Issue
Block a user