fix: Webform Permission for custom doctype (#26232)

* fix: Webform Permission for custom doctype

* fix: sider fix

* fix: app check condition for getting correct list_context

* chore: Better naming convention

* test: Added test case to check permission for webform of custom doctype

* chore: linting issue

* chore: linting issue

* fix: sider fix

* test: minor changes

* chore: linter and better naming method

* chore: linter fix

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
diff --git a/erpnext/controllers/website_list_for_contact.py b/erpnext/controllers/website_list_for_contact.py
index ff2ed45..8e5952c 100644
--- a/erpnext/controllers/website_list_for_contact.py
+++ b/erpnext/controllers/website_list_for_contact.py
@@ -7,6 +7,7 @@
 
 import frappe
 from frappe import _
+from frappe.modules.utils import get_module_app
 from frappe.utils import flt, has_common
 from frappe.utils.user import is_website_user
 
@@ -21,8 +22,32 @@
 		"get_list": get_transaction_list
 	}
 
+def get_webform_list_context(module):
+	if get_module_app(module) != 'erpnext':
+		return
+	return {
+		"get_list": get_webform_transaction_list
+	}
 
-def get_transaction_list(doctype, txt=None, filters=None, limit_start=0, limit_page_length=20, order_by="modified"):
+def get_webform_transaction_list(doctype, txt=None, filters=None, limit_start=0, limit_page_length=20, order_by="modified"):
+	""" Get List of transactions for custom doctypes """
+	from frappe.www.list import get_list
+
+	if not filters:
+		filters = []
+
+	meta = frappe.get_meta(doctype)
+
+	for d in meta.fields:
+		if d.fieldtype == 'Link' and d.fieldname != 'amended_from':
+			allowed_docs = [d.name for d in get_transaction_list(doctype=d.options, custom=True)]
+			allowed_docs.append('')
+			filters.append((d.fieldname, 'in', allowed_docs))
+
+	return get_list(doctype, txt, filters, limit_start, limit_page_length, ignore_permissions=False,
+		fields=None, order_by="modified")
+
+def get_transaction_list(doctype, txt=None, filters=None, limit_start=0, limit_page_length=20, order_by="modified", custom=False):
 	user = frappe.session.user
 	ignore_permissions = False
 
@@ -46,7 +71,7 @@
 				filters.append(('customer', 'in', customers))
 		elif suppliers:
 			filters.append(('supplier', 'in', suppliers))
-		else:
+		elif not custom:
 			return []
 
 		if doctype == 'Request for Quotation':
@@ -56,9 +81,16 @@
 		# Since customers and supplier do not have direct access to internal doctypes
 		ignore_permissions = True
 
+		if not customers and not suppliers and custom:
+			ignore_permissions = False
+			filters = []
+
 	transactions = get_list_for_transactions(doctype, txt, filters, limit_start, limit_page_length,
 		fields='name', ignore_permissions=ignore_permissions, order_by='modified desc')
 
+	if custom:
+		return transactions
+
 	return post_process(doctype, transactions)
 
 def get_list_for_transactions(doctype, txt, filters, limit_start, limit_page_length=20,
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index 5b6e1ee..be05e35 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -62,6 +62,7 @@
 # website
 update_website_context = ["erpnext.shopping_cart.utils.update_website_context", "erpnext.education.doctype.education_settings.education_settings.update_website_context"]
 my_account_context = "erpnext.shopping_cart.utils.update_my_account_context"
+webform_list_context = "erpnext.controllers.website_list_for_contact.get_webform_list_context"
 
 calendars = ["Task", "Work Order", "Leave Application", "Sales Order", "Holiday List", "Course Schedule"]
 
diff --git a/erpnext/tests/test_webform.py b/erpnext/tests/test_webform.py
new file mode 100644
index 0000000..19255db
--- /dev/null
+++ b/erpnext/tests/test_webform.py
@@ -0,0 +1,138 @@
+import unittest
+
+import frappe
+
+from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order
+
+
+class TestWebsite(unittest.TestCase):
+	def test_permission_for_custom_doctype(self):
+		create_user('Supplier 1', 'supplier1@gmail.com')
+		create_user('Supplier 2', 'supplier2@gmail.com')
+		create_supplier_with_contact('Supplier1', 'All Supplier Groups', 'Supplier 1', 'supplier1@gmail.com')
+		create_supplier_with_contact('Supplier2', 'All Supplier Groups', 'Supplier 2', 'supplier2@gmail.com')
+		po1 = create_purchase_order(supplier='Supplier1')
+		po2 = create_purchase_order(supplier='Supplier2')
+
+		create_custom_doctype()
+		create_webform()
+		create_order_assignment(supplier='Supplier1', po = po1.name)
+		create_order_assignment(supplier='Supplier2', po = po2.name)
+
+		frappe.set_user("Administrator")
+		# checking if data consist of all order assignment of Supplier1 and Supplier2
+		self.assertTrue('Supplier1' and 'Supplier2' in [data.supplier for data in get_data()])
+
+		frappe.set_user("supplier1@gmail.com")
+		# checking if data only consist of order assignment of Supplier1
+		self.assertTrue('Supplier1' in [data.supplier for data in get_data()])
+		self.assertFalse([data.supplier for data in get_data() if data.supplier != 'Supplier1'])
+
+		frappe.set_user("supplier2@gmail.com")
+		# checking if data only consist of order assignment of Supplier2
+		self.assertTrue('Supplier2' in [data.supplier for data in get_data()])
+		self.assertFalse([data.supplier for data in get_data() if data.supplier != 'Supplier2'])
+
+		frappe.set_user("Administrator")
+
+def get_data():
+	webform_list_contexts = frappe.get_hooks('webform_list_context')
+	if webform_list_contexts:
+		context = frappe._dict(frappe.get_attr(webform_list_contexts[0])('Buying') or {})
+	kwargs = dict(doctype='Order Assignment', order_by = 'modified desc')
+	return context.get_list(**kwargs)
+
+def create_user(name, email):
+	frappe.get_doc({
+		'doctype': 'User',
+		'send_welcome_email': 0,
+		'user_type': 'Website User',
+		'first_name': name,
+		'email': email,
+		'roles': [{"doctype": "Has Role", "role": "Supplier"}]
+	}).insert(ignore_if_duplicate = True)
+
+def create_supplier_with_contact(name, group, contact_name, contact_email):
+	supplier = frappe.get_doc({
+		'doctype': 'Supplier',
+		'supplier_name': name,
+		'supplier_group': group
+	}).insert(ignore_if_duplicate = True)
+
+	if not frappe.db.exists('Contact', contact_name+'-1-'+name):
+		new_contact = frappe.new_doc("Contact")
+		new_contact.first_name = contact_name
+		new_contact.is_primary_contact = True,
+		new_contact.append('links', {
+			"link_doctype": "Supplier",
+			"link_name": supplier.name
+		})
+		new_contact.append('email_ids', {
+			"email_id": contact_email,
+			"is_primary": 1
+		})
+
+		new_contact.insert(ignore_mandatory=True)
+
+def create_custom_doctype():
+	frappe.get_doc({
+		'doctype': 'DocType',
+		'name': 'Order Assignment',
+		'module': 'Buying',
+		'custom': 1,
+		'autoname': 'field:po',
+		'fields': [
+			{'label': 'PO', 'fieldname': 'po', 'fieldtype': 'Link', 'options': 'Purchase Order'},
+			{'label': 'Supplier', 'fieldname': 'supplier', 'fieldtype': 'Data', "fetch_from": "po.supplier"}
+		],
+		'permissions': [
+			{
+				"create": 1,
+				"delete": 1,
+				"email": 1,
+				"export": 1,
+				"print": 1,
+				"read": 1,
+				"report": 1,
+				"role": "System Manager",
+				"share": 1,
+				"write": 1
+			},
+			{
+				"read": 1,
+				"role": "Supplier"
+			}
+		]
+	}).insert(ignore_if_duplicate = True)
+
+def create_webform():
+	frappe.get_doc({
+		'doctype': 'Web Form',
+		'module': 'Buying',
+		'title': 'SO Schedule',
+		'route': 'so-schedule',
+		'doc_type': 'Order Assignment',
+		'web_form_fields': [
+			{
+				'doctype': 'Web Form Field',
+				'fieldname': 'po',
+				'fieldtype': 'Link',
+				'options': 'Purchase Order',
+				'label': 'PO'
+			},
+			{
+				'doctype': 'Web Form Field',
+				'fieldname': 'supplier',
+				'fieldtype': 'Data',
+				'label': 'Supplier'
+			}
+		]
+
+	}).insert(ignore_if_duplicate = True)
+
+def create_order_assignment(supplier, po):
+	frappe.get_doc({
+		'doctype': 'Order Assignment',
+		'po': po,
+		'supplier': supplier,
+	}).insert(ignore_if_duplicate = True)
\ No newline at end of file