fix: project % complete field allowing modification when manual method (backport #57274) (#57276)

Co-authored-by: nishkagosalia <nishka.gosalia@gmail.com>
This commit is contained in:
mergify[bot]
2026-07-20 17:28:39 +05:30
committed by GitHub
parent fcee4f128e
commit 0d53f1adb7
3 changed files with 59 additions and 2 deletions

View File

@@ -121,7 +121,7 @@
"in_list_view": 1,
"label": "% Completed",
"no_copy": 1,
"read_only": 1
"read_only_depends_on": "eval:doc.percent_complete_method != 'Manual'"
},
{
"fieldname": "column_break_5",
@@ -484,7 +484,7 @@
"index_web_pages_for_search": 1,
"links": [],
"max_attachments": 4,
"modified": "2026-07-14 14:32:11.328347",
"modified": "2026-07-21 11:23:22.000000",
"modified_by": "Administrator",
"module": "Projects",
"name": "Project",

View File

@@ -222,6 +222,8 @@ class Project(Document):
if self.percent_complete_method == "Manual":
if self.status == "Completed":
self.percent_complete = 100
elif flt(self.percent_complete) < 0 or flt(self.percent_complete) > 100:
frappe.throw(_("% Complete must be between 0 and 100"))
return
total = frappe.db.count("Task", dict(project=self.name))

View File

@@ -244,6 +244,61 @@ class TestProject(ERPNextTestSuite):
project.save()
self.assertEqual(project.status, "Completed")
def _project_with_tasks(self, method, count):
name = f"_Test PercentComplete {frappe.generate_hash(length=8)}"
project = frappe.get_doc(
{
"doctype": "Project",
"project_name": name,
"status": "Open",
"percent_complete_method": method,
"company": "_Test Company",
"expected_start_date": nowdate(),
}
).insert()
task_names = []
for i in range(count):
task = frappe.get_doc(
{
"doctype": "Task",
"subject": f"{name} Task {i}",
"project": project.name,
"status": "Open",
"exp_start_date": nowdate(),
"exp_end_date": nowdate(),
}
).insert()
task_names.append(task.name)
return project, task_names
def test_percent_complete_manual(self):
project, tasks = self._project_with_tasks("Manual", 2)
# manual value is preserved on save, even with linked tasks
project.percent_complete = 42
project.save()
self.assertEqual(project.percent_complete, 42)
# task updates do not overwrite the manual value
frappe.db.set_value("Task", tasks[0], "status", "Completed")
project.update_percent_complete()
self.assertEqual(project.percent_complete, 42)
# out-of-range values are rejected
project.percent_complete = 150
self.assertRaises(frappe.ValidationError, project.save)
project.reload()
project.percent_complete = -10
self.assertRaises(frappe.ValidationError, project.save)
project.reload()
# Completed status forces 100 regardless of the manual value
project.percent_complete = 42
project.status = "Completed"
project.save()
self.assertEqual(project.percent_complete, 100)
def _create_portal_user(self, email):
"""A user with no Project-related role, so read access can only come from
control_access_for_project_users() sharing the doc with them."""