mirror of
https://github.com/frappe/erpnext.git
synced 2026-08-15 23:48:38 +00:00
fix(selling): don't require cancel and delete perms to remove items via Update Items (backport #57419) (#57601)
Row removal called cancel() and delete() on the child row, and both check
permissions against the parent doctype. Dropping a row therefore needed Cancel
and Delete on the order, while the rest of the dialog only needs Write: the
button is gated on has_perm("write"), update_child_qty_rate checks parent
Write, and edits save with ignore_permissions=True.
Set ignore_permissions on the row before cancel/delete so removal sits behind
the same parent Write check as add and edit. validate_child_on_delete is
unchanged, so rows with ordered, received, delivered or billed qty are still
refused.
On version-16-hotfix validate_and_delete_children still lives in
erpnext/controllers/accounts_controller.py, not the extracted
erpnext/accounts/services/child_item_update.py module it was moved to on
develop.
Co-authored-by: Kaushal Shriwas <64089478+kaulith@users.noreply.github.com>
This commit is contained in:
@@ -3873,6 +3873,7 @@ def validate_and_delete_children(parent, data, ordered_item=None) -> bool:
|
||||
|
||||
for d in deleted_children:
|
||||
validate_child_on_delete(d, parent, ordered_item)
|
||||
d.flags.ignore_permissions = True
|
||||
d.cancel()
|
||||
d.delete()
|
||||
|
||||
|
||||
@@ -688,6 +688,51 @@ class TestSalesOrder(ERPNextTestSuite):
|
||||
frappe.ValidationError, update_child_qty_rate, "Sales Order", trans_item, so.name
|
||||
)
|
||||
|
||||
def test_update_child_removing_item_without_cancel_and_delete_perms(self):
|
||||
for workflow_name in frappe.get_all(
|
||||
"Workflow", filters={"document_type": "Sales Order", "is_active": 1}, pluck="name"
|
||||
):
|
||||
workflow = frappe.get_doc("Workflow", workflow_name)
|
||||
workflow.is_active = 0
|
||||
workflow.save()
|
||||
|
||||
role = "_Test Sales Order Item Editor"
|
||||
if not frappe.db.exists("Role", role):
|
||||
frappe.get_doc({"doctype": "Role", "role_name": role, "desk_access": 1}).insert()
|
||||
|
||||
frappe.permissions.add_permission("Sales Order", role, 0)
|
||||
for right, value in {
|
||||
"read": 1,
|
||||
"write": 1,
|
||||
"create": 1,
|
||||
"submit": 1,
|
||||
"cancel": 0,
|
||||
"delete": 0,
|
||||
}.items():
|
||||
frappe.permissions.update_permission_property("Sales Order", role, 0, right, value)
|
||||
frappe.clear_cache()
|
||||
|
||||
so = make_sales_order(**{"item_list": [{"item_code": "_Test Item", "qty": 5, "rate": 1000}]})
|
||||
trans_item = json.dumps(
|
||||
[
|
||||
{"item_code": "_Test Item", "qty": 5, "rate": 1000, "docname": so.items[0].name},
|
||||
{"item_code": "_Test Item 2", "qty": 2, "rate": 500},
|
||||
]
|
||||
)
|
||||
update_child_qty_rate("Sales Order", trans_item, so.name)
|
||||
so.reload()
|
||||
self.assertEqual(len(so.items), 2)
|
||||
|
||||
test_user = create_user("test_so_item_editor@example.com", role, "Accounts User", "Stock User")
|
||||
trans_item = json.dumps(
|
||||
[{"item_code": "_Test Item", "qty": 5, "rate": 1000, "docname": so.items[0].name}]
|
||||
)
|
||||
with self.set_user(test_user.name):
|
||||
update_child_qty_rate("Sales Order", trans_item, so.name)
|
||||
|
||||
so.reload()
|
||||
self.assertEqual(len(so.items), 1)
|
||||
|
||||
def test_update_child_qty_rate_with_workflow(self):
|
||||
from frappe.model.workflow import apply_workflow
|
||||
|
||||
|
||||
Reference in New Issue
Block a user