feat: Project Template
This commit is contained in:
parent
a77a8a42e5
commit
e9984672ce
@ -30,15 +30,20 @@ def get_data():
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "Project Update",
|
||||
"description": _("Project Update."),
|
||||
"dependencies": ["Project"],
|
||||
"name": "Project Template",
|
||||
"description": _("Make project from a template."),
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "Project Type",
|
||||
"description": _("Define Project type."),
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "Project Update",
|
||||
"description": _("Project Update."),
|
||||
"dependencies": ["Project"],
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -65,18 +65,47 @@ class Project(Document):
|
||||
return frappe.get_all("Task", "*", filters, order_by="exp_start_date asc")
|
||||
|
||||
def validate(self):
|
||||
self.validate_project_name()
|
||||
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()
|
||||
|
||||
def validate_project_name(self):
|
||||
if self.get("__islocal") and frappe.db.exists("Project", self.project_name):
|
||||
frappe.throw(_("Project {0} already exists").format(frappe.safe_decode(self.project_name)))
|
||||
def copy_from_template(self):
|
||||
'''
|
||||
Copy tasks from template
|
||||
'''
|
||||
if self.project_template and not len(self.tasks or []):
|
||||
|
||||
# has a template, and no loaded tasks, so lets create
|
||||
if not self.expected_start_date:
|
||||
# project starts today
|
||||
self.expected_start_date = today()
|
||||
|
||||
template = frappe.get_doc('Project Template', self.project_template)
|
||||
|
||||
if not self.project_type:
|
||||
self.project_type = template.project_type
|
||||
|
||||
# create tasks from template
|
||||
for task in template.tasks:
|
||||
frappe.get_doc(dict(
|
||||
doctype = 'Task',
|
||||
subject = task.subject,
|
||||
project = self.name,
|
||||
status = 'Open',
|
||||
exp_start_date = add_days(self.expected_start_date, task.start),
|
||||
exp_end_date = add_days(self.expected_start_date, task.start + task.duration),
|
||||
description = task.description,
|
||||
task_weight = task.task_weight
|
||||
)).insert()
|
||||
|
||||
# reload tasks after project
|
||||
self.load_tasks()
|
||||
|
||||
def validate_dates(self):
|
||||
if self.tasks:
|
||||
@ -201,6 +230,7 @@ class Project(Document):
|
||||
self.save(ignore_permissions=True)
|
||||
|
||||
def after_insert(self):
|
||||
self.copy_from_template()
|
||||
if self.sales_order:
|
||||
frappe.db.set_value("Sales Order", self.sales_order, "project", self.name)
|
||||
|
||||
|
@ -3,6 +3,37 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
||||
import frappe
|
||||
import frappe, unittest
|
||||
test_records = frappe.get_test_records('Project')
|
||||
test_ignore = ["Sales Order"]
|
||||
|
||||
from erpnext.projects.doctype.project_template.test_project_template import get_project_template
|
||||
from frappe.utils import getdate
|
||||
|
||||
class TestProject(unittest.TestCase):
|
||||
def test_project_with_template(self):
|
||||
template = get_project_template()
|
||||
|
||||
frappe.db.sql('delete from tabTask where project = "Test Project with Template"')
|
||||
frappe.delete_doc('Project', 'Test Project with Template')
|
||||
|
||||
project = frappe.get_doc(dict(
|
||||
doctype = 'Project',
|
||||
project_name = 'Test Project with Template',
|
||||
status = 'Open',
|
||||
project_template = template.name,
|
||||
expected_start_date = '2019-01-01'
|
||||
)).insert()
|
||||
|
||||
project.load_tasks()
|
||||
|
||||
task1 = project.tasks[0]
|
||||
self.assertEqual(task1.title, '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(len(project.tasks), 4)
|
||||
task4 = project.tasks[3]
|
||||
self.assertEqual(task4.title, 'Task 4')
|
||||
self.assertEqual(getdate(task4.end_date), getdate('2019-01-06'))
|
||||
|
@ -0,0 +1,8 @@
|
||||
// Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on('Project Template', {
|
||||
refresh: function(frm) {
|
||||
|
||||
}
|
||||
});
|
130
erpnext/projects/doctype/project_template/project_template.json
Normal file
130
erpnext/projects/doctype/project_template/project_template.json
Normal file
@ -0,0 +1,130 @@
|
||||
{
|
||||
"allow_copy": 0,
|
||||
"allow_events_in_timeline": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"autoname": "Prompt",
|
||||
"beta": 0,
|
||||
"creation": "2019-02-18 17:23:11.708371",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"fields": [
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "project_type",
|
||||
"fieldtype": "Link",
|
||||
"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": "Project Type",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Project Type",
|
||||
"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": "tasks",
|
||||
"fieldtype": "Table",
|
||||
"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": "Tasks",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Project Template Task",
|
||||
"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
|
||||
}
|
||||
],
|
||||
"has_web_view": 0,
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"idx": 0,
|
||||
"image_view": 0,
|
||||
"in_create": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2019-02-18 18:01:26.519832",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Projects",
|
||||
"name": "Project Template",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"amend": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "System Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"quick_entry": 1,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"show_name_in_global_search": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1,
|
||||
"track_seen": 0,
|
||||
"track_views": 0
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
class ProjectTemplate(Document):
|
||||
pass
|
@ -0,0 +1,12 @@
|
||||
from __future__ import unicode_literals
|
||||
from frappe import _
|
||||
|
||||
def get_data():
|
||||
return {
|
||||
'fieldname': 'project_template',
|
||||
'transactions': [
|
||||
{
|
||||
'items': ['Project']
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import frappe
|
||||
import unittest
|
||||
|
||||
class TestProjectTemplate(unittest.TestCase):
|
||||
pass
|
||||
|
||||
def get_project_template():
|
||||
if not frappe.db.exists('Project Template', 'Test Project Template'):
|
||||
frappe.get_doc(dict(
|
||||
doctype = 'Project Template',
|
||||
name = 'Test Project Template',
|
||||
tasks = [
|
||||
dict(subject='Task 1', description='Task 1 description',
|
||||
start=0, duration=3),
|
||||
dict(subject='Task 2', description='Task 2 description',
|
||||
start=0, duration=2),
|
||||
dict(subject='Task 3', description='Task 3 description',
|
||||
start=2, duration=4),
|
||||
dict(subject='Task 4', description='Task 4 description',
|
||||
start=3, duration=2),
|
||||
]
|
||||
)).insert()
|
||||
|
||||
return frappe.get_doc('Project Template', 'Test Project Template')
|
@ -0,0 +1,203 @@
|
||||
{
|
||||
"allow_copy": 0,
|
||||
"allow_events_in_timeline": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"beta": 0,
|
||||
"creation": "2019-02-18 17:24:41.830096",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"fields": [
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "subject",
|
||||
"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": "Subject",
|
||||
"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": 0,
|
||||
"fieldname": "start",
|
||||
"fieldtype": "Int",
|
||||
"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": "Begin On (Days)",
|
||||
"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": 0,
|
||||
"fieldname": "duration",
|
||||
"fieldtype": "Int",
|
||||
"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": "Duration (Days)",
|
||||
"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": 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": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Task Weight",
|
||||
"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": 1,
|
||||
"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
|
||||
}
|
||||
],
|
||||
"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-18 18:30:22.688966",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Projects",
|
||||
"name": "Project Template Task",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"quick_entry": 1,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"show_name_in_global_search": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1,
|
||||
"track_seen": 0,
|
||||
"track_views": 0
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
class ProjectTemplateTask(Document):
|
||||
pass
|
Loading…
x
Reference in New Issue
Block a user