mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-13 10:11:20 +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
@@ -12,7 +12,7 @@ from erpnext.hr.doctype.employee_onboarding.employee_onboarding import Incomplet
|
|||||||
class TestEmployeeOnboarding(unittest.TestCase):
|
class TestEmployeeOnboarding(unittest.TestCase):
|
||||||
def test_employee_onboarding_incomplete_task(self):
|
def test_employee_onboarding_incomplete_task(self):
|
||||||
if frappe.db.exists('Employee Onboarding', {'employee_name': 'Test Researcher'}):
|
if frappe.db.exists('Employee Onboarding', {'employee_name': 'Test Researcher'}):
|
||||||
return frappe.get_doc('Employee Onboarding', {'employee_name': 'Test Researcher'})
|
frappe.delete_doc('Employee Onboarding', {'employee_name': 'Test Researcher'})
|
||||||
_set_up()
|
_set_up()
|
||||||
applicant = get_job_applicant()
|
applicant = get_job_applicant()
|
||||||
onboarding = frappe.new_doc('Employee Onboarding')
|
onboarding = frappe.new_doc('Employee Onboarding')
|
||||||
@@ -39,9 +39,10 @@ class TestEmployeeOnboarding(unittest.TestCase):
|
|||||||
|
|
||||||
# complete the task
|
# complete the task
|
||||||
project = frappe.get_doc('Project', onboarding.project)
|
project = frappe.get_doc('Project', onboarding.project)
|
||||||
project.load_tasks()
|
for task in frappe.get_all('Task', dict(project=project.name)):
|
||||||
project.tasks[0].status = 'Completed'
|
task = frappe.get_doc('Task', task.name)
|
||||||
project.save()
|
task.status = 'Completed'
|
||||||
|
task.save()
|
||||||
|
|
||||||
# make employee
|
# make employee
|
||||||
onboarding.reload()
|
onboarding.reload()
|
||||||
@@ -71,4 +72,3 @@ def _set_up():
|
|||||||
project = "Employee Onboarding : Test Researcher - test@researcher.com"
|
project = "Employee Onboarding : Test Researcher - test@researcher.com"
|
||||||
frappe.db.sql("delete from tabProject where name=%s", project)
|
frappe.db.sql("delete from tabProject where name=%s", project)
|
||||||
frappe.db.sql("delete from tabTask where project=%s", project)
|
frappe.db.sql("delete from tabTask where project=%s", project)
|
||||||
frappe.db.sql("delete from `tabProject Task` where parent=%s", project)
|
|
||||||
|
|||||||
@@ -10,33 +10,36 @@ from erpnext.accounts.doctype.account.test_account import create_account
|
|||||||
|
|
||||||
test_records = frappe.get_test_records('Expense Claim')
|
test_records = frappe.get_test_records('Expense Claim')
|
||||||
test_dependencies = ['Employee']
|
test_dependencies = ['Employee']
|
||||||
|
company_name = '_Test Company 4'
|
||||||
|
|
||||||
|
|
||||||
class TestExpenseClaim(unittest.TestCase):
|
class TestExpenseClaim(unittest.TestCase):
|
||||||
def test_total_expense_claim_for_project(self):
|
def test_total_expense_claim_for_project(self):
|
||||||
frappe.db.sql("""delete from `tabTask` where project = "_Test Project 1" """)
|
frappe.db.sql("""delete from `tabTask` where project = "_Test Project 1" """)
|
||||||
frappe.db.sql("""delete from `tabProject Task` where parent = "_Test Project 1" """)
|
|
||||||
frappe.db.sql("""delete from `tabProject` where name = "_Test Project 1" """)
|
frappe.db.sql("""delete from `tabProject` where name = "_Test Project 1" """)
|
||||||
frappe.db.sql("delete from `tabExpense Claim` where project='_Test Project 1'")
|
frappe.db.sql("update `tabExpense Claim` set project = '', task = ''")
|
||||||
|
|
||||||
frappe.get_doc({
|
frappe.get_doc({
|
||||||
"project_name": "_Test Project 1",
|
"project_name": "_Test Project 1",
|
||||||
"doctype": "Project",
|
"doctype": "Project"
|
||||||
}).save()
|
}).save()
|
||||||
|
|
||||||
task = frappe.get_doc({
|
task = frappe.get_doc(dict(
|
||||||
"doctype": "Task",
|
doctype = 'Task',
|
||||||
"subject": "_Test Project Task 1",
|
subject = '_Test Project Task 1',
|
||||||
"project": "_Test Project 1"
|
status = 'Open',
|
||||||
}).save()
|
project = '_Test Project 1'
|
||||||
|
)).insert()
|
||||||
|
|
||||||
task_name = frappe.db.get_value("Task", {"project": "_Test Project 1"})
|
task_name = task.name
|
||||||
payable_account = get_payable_account("Wind Power LLC")
|
payable_account = get_payable_account(company_name)
|
||||||
make_expense_claim(payable_account, 300, 200, "Wind Power LLC","Travel Expenses - WP", "_Test Project 1", task_name)
|
|
||||||
|
make_expense_claim(payable_account, 300, 200, company_name, "Travel Expenses - _TC4", "_Test Project 1", task_name)
|
||||||
|
|
||||||
self.assertEqual(frappe.db.get_value("Task", task_name, "total_expense_claim"), 200)
|
self.assertEqual(frappe.db.get_value("Task", task_name, "total_expense_claim"), 200)
|
||||||
self.assertEqual(frappe.db.get_value("Project", "_Test Project 1", "total_expense_claim"), 200)
|
self.assertEqual(frappe.db.get_value("Project", "_Test Project 1", "total_expense_claim"), 200)
|
||||||
|
|
||||||
expense_claim2 = make_expense_claim(payable_account, 600, 500, "Wind Power LLC", "Travel Expenses - WP","_Test Project 1", task_name)
|
expense_claim2 = make_expense_claim(payable_account, 600, 500, company_name, "Travel Expenses - _TC4","_Test Project 1", task_name)
|
||||||
|
|
||||||
self.assertEqual(frappe.db.get_value("Task", task_name, "total_expense_claim"), 700)
|
self.assertEqual(frappe.db.get_value("Task", task_name, "total_expense_claim"), 700)
|
||||||
self.assertEqual(frappe.db.get_value("Project", "_Test Project 1", "total_expense_claim"), 700)
|
self.assertEqual(frappe.db.get_value("Project", "_Test Project 1", "total_expense_claim"), 700)
|
||||||
@@ -48,8 +51,8 @@ class TestExpenseClaim(unittest.TestCase):
|
|||||||
self.assertEqual(frappe.db.get_value("Project", "_Test Project 1", "total_expense_claim"), 200)
|
self.assertEqual(frappe.db.get_value("Project", "_Test Project 1", "total_expense_claim"), 200)
|
||||||
|
|
||||||
def test_expense_claim_status(self):
|
def test_expense_claim_status(self):
|
||||||
payable_account = get_payable_account("Wind Power LLC")
|
payable_account = get_payable_account(company_name)
|
||||||
expense_claim = make_expense_claim(payable_account, 300, 200, "Wind Power LLC", "Travel Expenses - WP")
|
expense_claim = make_expense_claim(payable_account, 300, 200, company_name, "Travel Expenses - _TC4")
|
||||||
|
|
||||||
je_dict = make_bank_entry("Expense Claim", expense_claim.name)
|
je_dict = make_bank_entry("Expense Claim", expense_claim.name)
|
||||||
je = frappe.get_doc(je_dict)
|
je = frappe.get_doc(je_dict)
|
||||||
@@ -66,9 +69,9 @@ class TestExpenseClaim(unittest.TestCase):
|
|||||||
self.assertEqual(expense_claim.status, "Unpaid")
|
self.assertEqual(expense_claim.status, "Unpaid")
|
||||||
|
|
||||||
def test_expense_claim_gl_entry(self):
|
def test_expense_claim_gl_entry(self):
|
||||||
payable_account = get_payable_account("Wind Power LLC")
|
payable_account = get_payable_account(company_name)
|
||||||
taxes = generate_taxes()
|
taxes = generate_taxes()
|
||||||
expense_claim = make_expense_claim(payable_account, 300, 200, "Wind Power LLC", "Travel Expenses - WP", do_not_submit=True, taxes=taxes)
|
expense_claim = make_expense_claim(payable_account, 300, 200, company_name, "Travel Expenses - _TC4", do_not_submit=True, taxes=taxes)
|
||||||
expense_claim.submit()
|
expense_claim.submit()
|
||||||
|
|
||||||
gl_entries = frappe.db.sql("""select account, debit, credit
|
gl_entries = frappe.db.sql("""select account, debit, credit
|
||||||
@@ -78,9 +81,9 @@ class TestExpenseClaim(unittest.TestCase):
|
|||||||
self.assertTrue(gl_entries)
|
self.assertTrue(gl_entries)
|
||||||
|
|
||||||
expected_values = dict((d[0], d) for d in [
|
expected_values = dict((d[0], d) for d in [
|
||||||
['CGST - WP',10.0, 0.0],
|
['CGST - _TC4',18.0, 0.0],
|
||||||
[payable_account, 0.0, 210.0],
|
[payable_account, 0.0, 218.0],
|
||||||
["Travel Expenses - WP", 200.0, 0.0]
|
["Travel Expenses - _TC4", 200.0, 0.0]
|
||||||
])
|
])
|
||||||
|
|
||||||
for gle in gl_entries:
|
for gle in gl_entries:
|
||||||
@@ -89,14 +92,14 @@ class TestExpenseClaim(unittest.TestCase):
|
|||||||
self.assertEquals(expected_values[gle.account][2], gle.credit)
|
self.assertEquals(expected_values[gle.account][2], gle.credit)
|
||||||
|
|
||||||
def test_rejected_expense_claim(self):
|
def test_rejected_expense_claim(self):
|
||||||
payable_account = get_payable_account("Wind Power LLC")
|
payable_account = get_payable_account(company_name)
|
||||||
expense_claim = frappe.get_doc({
|
expense_claim = frappe.get_doc({
|
||||||
"doctype": "Expense Claim",
|
"doctype": "Expense Claim",
|
||||||
"employee": "_T-Employee-00001",
|
"employee": "_T-Employee-00001",
|
||||||
"payable_account": payable_account,
|
"payable_account": payable_account,
|
||||||
"approval_status": "Rejected",
|
"approval_status": "Rejected",
|
||||||
"expenses":
|
"expenses":
|
||||||
[{ "expense_type": "Travel", "default_account": "Travel Expenses - WP", "amount": 300, "sanctioned_amount": 200 }]
|
[{ "expense_type": "Travel", "default_account": "Travel Expenses - _TC4", "amount": 300, "sanctioned_amount": 200 }]
|
||||||
})
|
})
|
||||||
expense_claim.submit()
|
expense_claim.submit()
|
||||||
|
|
||||||
@@ -111,9 +114,9 @@ def get_payable_account(company):
|
|||||||
|
|
||||||
def generate_taxes():
|
def generate_taxes():
|
||||||
parent_account = frappe.db.get_value('Account',
|
parent_account = frappe.db.get_value('Account',
|
||||||
{'company': "Wind Power LLC", 'is_group':1, 'account_type': 'Tax'},
|
{'company': company_name, 'is_group':1, 'account_type': 'Tax'},
|
||||||
'name')
|
'name')
|
||||||
account = create_account(company="Wind Power LLC", account_name="CGST", account_type="Tax", parent_account=parent_account)
|
account = create_account(company=company_name, account_name="CGST", account_type="Tax", parent_account=parent_account)
|
||||||
return {'taxes':[{
|
return {'taxes':[{
|
||||||
"account_head": account,
|
"account_head": account,
|
||||||
"rate": 0,
|
"rate": 0,
|
||||||
@@ -124,15 +127,18 @@ def generate_taxes():
|
|||||||
|
|
||||||
def make_expense_claim(payable_account, amount, sanctioned_amount, company, account, project=None, task_name=None, do_not_submit=False, taxes=None):
|
def make_expense_claim(payable_account, amount, sanctioned_amount, company, account, project=None, task_name=None, do_not_submit=False, taxes=None):
|
||||||
employee = frappe.db.get_value("Employee", {"status": "Active"})
|
employee = frappe.db.get_value("Employee", {"status": "Active"})
|
||||||
|
currency = frappe.db.get_value('Company', company, 'default_currency')
|
||||||
expense_claim = {
|
expense_claim = {
|
||||||
"doctype": "Expense Claim",
|
"doctype": "Expense Claim",
|
||||||
"employee": employee,
|
"employee": employee,
|
||||||
"payable_account": payable_account,
|
"payable_account": payable_account,
|
||||||
"approval_status": "Approved",
|
"approval_status": "Approved",
|
||||||
"company": company,
|
"company": company,
|
||||||
|
'currency': currency,
|
||||||
"expenses":
|
"expenses":
|
||||||
[{"expense_type": "Travel",
|
[{"expense_type": "Travel",
|
||||||
"default_account": account,
|
"default_account": account,
|
||||||
|
'currency': currency,
|
||||||
"amount": amount,
|
"amount": amount,
|
||||||
"sanctioned_amount": sanctioned_amount}]}
|
"sanctioned_amount": sanctioned_amount}]}
|
||||||
if taxes:
|
if taxes:
|
||||||
|
|||||||
@@ -615,6 +615,7 @@ erpnext.patches.v11_1.set_missing_opportunity_from
|
|||||||
erpnext.patches.v12_0.set_quotation_status
|
erpnext.patches.v12_0.set_quotation_status
|
||||||
erpnext.patches.v12_0.set_priority_for_support
|
erpnext.patches.v12_0.set_priority_for_support
|
||||||
erpnext.patches.v12_0.delete_priority_property_setter
|
erpnext.patches.v12_0.delete_priority_property_setter
|
||||||
|
execute:frappe.delete_doc("DocType", "Project Task")
|
||||||
erpnext.patches.v11_1.update_default_supplier_in_item_defaults
|
erpnext.patches.v11_1.update_default_supplier_in_item_defaults
|
||||||
erpnext.patches.v12_0.update_due_date_in_gle
|
erpnext.patches.v12_0.update_due_date_in_gle
|
||||||
erpnext.patches.v12_0.add_default_buying_selling_terms_in_company
|
erpnext.patches.v12_0.add_default_buying_selling_terms_in_company
|
||||||
|
|||||||
@@ -2,10 +2,9 @@ import frappe
|
|||||||
|
|
||||||
def execute():
|
def execute():
|
||||||
frappe.reload_doctype('Task')
|
frappe.reload_doctype('Task')
|
||||||
frappe.reload_doctype('Project Task')
|
|
||||||
|
|
||||||
# add "Completed" if customized
|
# add "Completed" if customized
|
||||||
for doctype in ('Task', 'Project Task'):
|
for doctype in ('Task'):
|
||||||
property_setter_name = frappe.db.exists('Property Setter', dict(doc_type = doctype, field_name = 'status', property = 'options'))
|
property_setter_name = frappe.db.exists('Property Setter', dict(doc_type = doctype, field_name = 'status', property = 'options'))
|
||||||
if property_setter_name:
|
if property_setter_name:
|
||||||
property_setter = frappe.get_doc('Property Setter', property_setter_name)
|
property_setter = frappe.get_doc('Property Setter', property_setter_name)
|
||||||
|
|||||||
@@ -1,23 +1,6 @@
|
|||||||
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||||
// License: GNU General Public License v3. See license.txt
|
// License: GNU General Public License v3. See license.txt
|
||||||
frappe.ui.form.on("Project", {
|
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) {
|
onload: function (frm) {
|
||||||
var so = frappe.meta.get_docfield("Project", "sales_order");
|
var so = frappe.meta.get_docfield("Project", "sales_order");
|
||||||
so.get_route_options_for_new_doc = function (field) {
|
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))
|
return '{0}: {1}'.format(_(self.status), frappe.safe_decode(self.project_name))
|
||||||
|
|
||||||
def onload(self):
|
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,
|
self.set_onload('activity_summary', frappe.db.sql('''select activity_type,
|
||||||
sum(hours) as total_hours
|
sum(hours) as total_hours
|
||||||
from `tabTimesheet Detail` where project=%s and docstatus < 2 group by activity_type
|
from `tabTimesheet Detail` where project=%s and docstatus < 2 group by activity_type
|
||||||
@@ -33,57 +29,19 @@ class Project(Document):
|
|||||||
def before_print(self):
|
def before_print(self):
|
||||||
self.onload()
|
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):
|
def validate(self):
|
||||||
self.validate_weights()
|
|
||||||
self.sync_tasks()
|
|
||||||
self.tasks = []
|
|
||||||
self.load_tasks()
|
|
||||||
if not self.is_new():
|
if not self.is_new():
|
||||||
self.copy_from_template()
|
self.copy_from_template()
|
||||||
self.validate_dates()
|
|
||||||
self.send_welcome_email()
|
self.send_welcome_email()
|
||||||
self.update_percent_complete(from_validate=True)
|
self.update_costing()
|
||||||
|
self.update_percent_complete()
|
||||||
|
|
||||||
def copy_from_template(self):
|
def copy_from_template(self):
|
||||||
'''
|
'''
|
||||||
Copy tasks from template
|
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
|
# has a template, and no loaded tasks, so lets create
|
||||||
if not self.expected_start_date:
|
if not self.expected_start_date:
|
||||||
@@ -108,104 +66,6 @@ class Project(Document):
|
|||||||
task_weight = task.task_weight
|
task_weight = task.task_weight
|
||||||
)).insert()
|
)).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):
|
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
|
||||||
|
|
||||||
@@ -215,48 +75,43 @@ class Project(Document):
|
|||||||
if row.get(field) != d.get(field):
|
if row.get(field) != d.get(field):
|
||||||
return True
|
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):
|
def update_project(self):
|
||||||
|
'''Called externally by Task'''
|
||||||
self.update_percent_complete()
|
self.update_percent_complete()
|
||||||
self.update_costing()
|
self.update_costing()
|
||||||
|
self.db_update()
|
||||||
|
|
||||||
def after_insert(self):
|
def after_insert(self):
|
||||||
self.copy_from_template()
|
self.copy_from_template()
|
||||||
if self.sales_order:
|
if self.sales_order:
|
||||||
frappe.db.set_value("Sales Order", self.sales_order, "project", self.name)
|
frappe.db.set_value("Sales Order", self.sales_order, "project", self.name)
|
||||||
|
|
||||||
def update_percent_complete(self, from_validate=False):
|
def update_percent_complete(self):
|
||||||
if not self.tasks: return
|
total = frappe.db.count('Task', dict(project=self.name))
|
||||||
total = frappe.db.sql("""select count(name) from tabTask where project=%s""", self.name)[0][0]
|
|
||||||
|
|
||||||
if not total and self.percent_complete:
|
if not total:
|
||||||
self.percent_complete = 0
|
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 (
|
if (self.percent_complete_method == "Task Progress" and total > 0):
|
||||||
not self.percent_complete_method and total > 0):
|
progress = frappe.db.sql("""select sum(progress) from tabTask where
|
||||||
completed = frappe.db.sql("""select count(name) from tabTask where
|
project=%s""", self.name)[0][0]
|
||||||
project=%s and status in ('Cancelled', 'Completed')""", self.name)[0][0]
|
self.percent_complete = flt(flt(progress) / total, 2)
|
||||||
self.percent_complete = flt(flt(completed) / total * 100, 2)
|
|
||||||
|
|
||||||
if (self.percent_complete_method == "Task Progress" and total > 0):
|
if (self.percent_complete_method == "Task Weight" and total > 0):
|
||||||
progress = frappe.db.sql("""select sum(progress) from tabTask where
|
weight_sum = frappe.db.sql("""select sum(task_weight) from tabTask where
|
||||||
project=%s""", self.name)[0][0]
|
project=%s""", self.name)[0][0]
|
||||||
self.percent_complete = flt(flt(progress) / total, 2)
|
weighted_progress = frappe.db.sql("""select progress, task_weight from tabTask where
|
||||||
|
project=%s""", self.name, as_dict=1)
|
||||||
if (self.percent_complete_method == "Task Weight" and total > 0):
|
pct_complete = 0
|
||||||
weight_sum = frappe.db.sql("""select sum(task_weight) from tabTask where
|
for row in weighted_progress:
|
||||||
project=%s""", self.name)[0][0]
|
pct_complete += row["progress"] * frappe.utils.safe_div(row["task_weight"], weight_sum)
|
||||||
weighted_progress = frappe.db.sql("""select progress, task_weight from tabTask where
|
self.percent_complete = flt(flt(pct_complete), 2)
|
||||||
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
|
# don't update status if it is cancelled
|
||||||
if self.status == 'Cancelled':
|
if self.status == 'Cancelled':
|
||||||
@@ -268,9 +123,6 @@ class Project(Document):
|
|||||||
else:
|
else:
|
||||||
self.status = "Open"
|
self.status = "Open"
|
||||||
|
|
||||||
if not from_validate:
|
|
||||||
self.db_update()
|
|
||||||
|
|
||||||
def update_costing(self):
|
def update_costing(self):
|
||||||
from_time_sheet = frappe.db.sql("""select
|
from_time_sheet = frappe.db.sql("""select
|
||||||
sum(costing_amount) as costing_amount,
|
sum(costing_amount) as costing_amount,
|
||||||
@@ -297,7 +149,6 @@ class Project(Document):
|
|||||||
self.update_sales_amount()
|
self.update_sales_amount()
|
||||||
self.update_billed_amount()
|
self.update_billed_amount()
|
||||||
self.calculate_gross_margin()
|
self.calculate_gross_margin()
|
||||||
self.db_update()
|
|
||||||
|
|
||||||
def calculate_gross_margin(self):
|
def calculate_gross_margin(self):
|
||||||
expense_amount = (flt(self.total_costing_amount) + flt(self.total_expense_claim)
|
expense_amount = (flt(self.total_costing_amount) + flt(self.total_expense_claim)
|
||||||
@@ -348,57 +199,6 @@ class Project(Document):
|
|||||||
content=content.format(*messages))
|
content=content.format(*messages))
|
||||||
user.welcome_email_sent = 1
|
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):
|
def get_timeline_data(doctype, name):
|
||||||
'''Return timeline for attendance'''
|
'''Return timeline for attendance'''
|
||||||
return dict(frappe.db.sql('''select unix_timestamp(from_time), count(*)
|
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 = 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]
|
task1 = tasks[0]
|
||||||
self.assertEqual(task1.title, 'Task 1')
|
self.assertEqual(task1.subject, 'Task 1')
|
||||||
self.assertEqual(task1.description, 'Task 1 description')
|
self.assertEqual(task1.description, 'Task 1 description')
|
||||||
self.assertEqual(getdate(task1.start_date), getdate('2019-01-01'))
|
self.assertEqual(getdate(task1.exp_start_date), getdate('2019-01-01'))
|
||||||
self.assertEqual(getdate(task1.end_date), getdate('2019-01-04'))
|
self.assertEqual(getdate(task1.exp_end_date), getdate('2019-01-04'))
|
||||||
|
|
||||||
self.assertEqual(len(project.tasks), 4)
|
self.assertEqual(len(tasks), 4)
|
||||||
task4 = project.tasks[3]
|
task4 = tasks[3]
|
||||||
self.assertEqual(task4.title, 'Task 4')
|
self.assertEqual(task4.subject, 'Task 4')
|
||||||
self.assertEqual(getdate(task4.end_date), getdate('2019-01-06'))
|
self.assertEqual(getdate(task4.exp_end_date), getdate('2019-01-06'))
|
||||||
|
|
||||||
def get_project(name):
|
def get_project(name):
|
||||||
template = get_project_template()
|
template = get_project_template()
|
||||||
|
|||||||
@@ -1,12 +1,6 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"project_name": "_Test Project",
|
"project_name": "_Test Project",
|
||||||
"status": "Open",
|
"status": "Open"
|
||||||
"tasks":[
|
|
||||||
{
|
|
||||||
"title": "_Test Task",
|
|
||||||
"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):
|
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."))
|
||||||
|
|
||||||
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()
|
self.update_nsm_model()
|
||||||
|
|
||||||
def update_status(self):
|
def update_status(self):
|
||||||
|
|||||||
@@ -547,12 +547,6 @@ def make_project(source_name, target_doc=None):
|
|||||||
"base_grand_total" : "estimated_costing",
|
"base_grand_total" : "estimated_costing",
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Sales Order Item": {
|
|
||||||
"doctype": "Project Task",
|
|
||||||
"field_map": {
|
|
||||||
"item_code": "title",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}, target_doc, postprocess)
|
}, target_doc, postprocess)
|
||||||
|
|
||||||
return doc
|
return doc
|
||||||
|
|||||||
Reference in New Issue
Block a user