From 53f0049e754e4d8ae006cd63121926c0208141d6 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 22 Jun 2026 18:04:53 +0530 Subject: [PATCH] test(project): cover create_duplicate_project and set_project_status Both are whitelisted, UI-triggered functions that had no server tests. Covers duplicating a project with its tasks (and the same-name guard), and bulk-setting a project plus its tasks to a terminal status (and the invalid-status guard). --- .../projects/doctype/project/test_project.py | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/erpnext/projects/doctype/project/test_project.py b/erpnext/projects/doctype/project/test_project.py index 04d5934b368..6290829904c 100644 --- a/erpnext/projects/doctype/project/test_project.py +++ b/erpnext/projects/doctype/project/test_project.py @@ -348,6 +348,48 @@ class TestProject(ERPNextTestSuite): project.update_percent_complete() self.assertEqual(project.percent_complete, 75) # 100 * 3/4 + 0 * 1/4 + def test_create_duplicate_project_copies_tasks(self): + from erpnext.projects.doctype.project.project import create_duplicate_project + + source, tasks = self._project_with_tasks("Task Completion", 2) + new_name = f"{source.project_name} Copy" + + create_duplicate_project(frappe.as_json(source.as_dict()), new_name) + + # Project is named by series, so look the copy up by its project_name + new_project = frappe.db.get_value("Project", {"project_name": new_name}) + self.assertTrue(new_project) + copied_tasks = frappe.get_all("Task", filters={"project": new_project}) + self.assertEqual(len(copied_tasks), len(tasks)) + + def test_create_duplicate_project_rejects_same_name(self): + from erpnext.projects.doctype.project.project import create_duplicate_project + + source, _ = self._project_with_tasks("Task Completion", 1) + self.assertRaises( + frappe.ValidationError, + create_duplicate_project, + frappe.as_json(source.as_dict()), + source.name, + ) + + def test_set_project_status_updates_project_and_tasks(self): + from erpnext.projects.doctype.project.project import set_project_status + + project, tasks = self._project_with_tasks("Task Completion", 2) + + set_project_status(project.name, "Cancelled") + + self.assertEqual(frappe.db.get_value("Project", project.name, "status"), "Cancelled") + for task in tasks: + self.assertEqual(frappe.db.get_value("Task", task, "status"), "Cancelled") + + def test_set_project_status_rejects_invalid_status(self): + from erpnext.projects.doctype.project.project import set_project_status + + project, _ = self._project_with_tasks("Task Completion", 1) + self.assertRaises(frappe.ValidationError, set_project_status, project.name, "Open") + def get_project(name, template): project = frappe.get_doc(