mirror of
https://github.com/frappe/erpnext.git
synced 2026-08-15 23:48:38 +00:00
fix(projects): don't overwrite existing Sales Order project link
Creating a Project with a `sales_order` set used `frappe.db.set_value` to unconditionally write the Sales Order's `project` field. When several projects were created for the same Sales Order, each new project silently overwrote the previous link, leaving earlier projects orphaned. Only back-link the Sales Order when it is not already tied to another project, and write the value through the document's `db_set` so the modified timestamp and realtime update are handled. A warning is shown when an existing link is left untouched. Fixes #52179
This commit is contained in:
@@ -240,8 +240,29 @@ class Project(Document):
|
||||
|
||||
def after_insert(self):
|
||||
self.copy_from_template("after_insert")
|
||||
if self.sales_order:
|
||||
frappe.db.set_value("Sales Order", self.sales_order, "project", self.name)
|
||||
self.link_with_sales_order()
|
||||
|
||||
def link_with_sales_order(self) -> None:
|
||||
"""Back-link the source Sales Order to this project.
|
||||
|
||||
The link is set only when the Sales Order is not already tied to another
|
||||
project, so projects created concurrently for the same Sales Order cannot
|
||||
overwrite each other's reference.
|
||||
"""
|
||||
if not self.sales_order:
|
||||
return
|
||||
|
||||
sales_order = frappe.get_doc("Sales Order", self.sales_order)
|
||||
if sales_order.project and sales_order.project != self.name:
|
||||
frappe.msgprint(
|
||||
_("Sales Order {0} is already linked to Project {1}, skipping the link.").format(
|
||||
self.sales_order, sales_order.project
|
||||
),
|
||||
alert=True,
|
||||
)
|
||||
return
|
||||
|
||||
sales_order.db_set("project", self.name)
|
||||
|
||||
def on_trash(self):
|
||||
frappe.db.set_value("Sales Order", {"project": self.name}, "project", "")
|
||||
|
||||
@@ -174,6 +174,24 @@ class TestProject(ERPNextTestSuite):
|
||||
so.reload()
|
||||
self.assertFalse(so.project)
|
||||
|
||||
def test_sales_order_link_is_not_overwritten_by_second_project(self):
|
||||
so = make_sales_order()
|
||||
|
||||
first_project = make_project_from_so(so.name).save()
|
||||
so.reload()
|
||||
self.assertEqual(so.project, first_project.name)
|
||||
|
||||
# A second project for the same sales order must not steal the link.
|
||||
second_project = frappe.get_doc(
|
||||
doctype="Project",
|
||||
project_name="Second project for same sales order",
|
||||
sales_order=so.name,
|
||||
).insert()
|
||||
self.assertEqual(second_project.sales_order, so.name)
|
||||
|
||||
so.reload()
|
||||
self.assertEqual(so.project, first_project.name)
|
||||
|
||||
def test_project_with_template_tasks_having_common_name(self):
|
||||
# Step - 1: Create Template Parent Tasks
|
||||
template_parent_task1 = create_task(subject="Parent Task - 1", is_template=1, is_group=1)
|
||||
|
||||
Reference in New Issue
Block a user