[Enhance] GL Entry - ability to multiselect party (#13769)

* change dynamic link to multiselect field and improv

* print based fix

* refactor changes according to multiselect changes
diff --git a/erpnext/accounts/report/general_ledger/general_ledger.html b/erpnext/accounts/report/general_ledger/general_ledger.html
index 9a2205a..40469ae 100644
--- a/erpnext/accounts/report/general_ledger/general_ledger.html
+++ b/erpnext/accounts/report/general_ledger/general_ledger.html
@@ -2,7 +2,7 @@
 <h4 class="text-center">
 	{% if (filters.party_name) { %}
 		{%= filters.party_name %}
-	{% } else if (filters.party) { %}
+	{% } else if (filters.party && filters.show_name) { %}
 		{%= filters.party %}
 	{% } else if (filters.account) { %}
 		{%= filters.account %}
diff --git a/erpnext/accounts/report/general_ledger/general_ledger.js b/erpnext/accounts/report/general_ledger/general_ledger.js
index 6438263..5e8f9cf 100644
--- a/erpnext/accounts/report/general_ledger/general_ledger.js
+++ b/erpnext/accounts/report/general_ledger/general_ledger.js
@@ -72,31 +72,60 @@
 		{
 			"fieldname":"party",
 			"label": __("Party"),
-			"fieldtype": "Dynamic Link",
-			"get_options": function() {
+			"fieldtype": "MultiSelect",
+			get_data: function() {
+				if(!frappe.query_report_filters_by_name) return;
+
 				var party_type = frappe.query_report_filters_by_name.party_type.get_value();
-				var party = frappe.query_report_filters_by_name.party.get_value();
-				if(party && !party_type) {
+				var parties = frappe.query_report_filters_by_name.party.get_value();
+				if(!party_type) {
 					frappe.throw(__("Please select Party Type first"));
 				}
-				return party_type;
+
+				const values = parties.split(/\s*,\s*/).filter(d => d);
+				const txt = parties.match(/[^,\s*]*$/)[0] || '';
+				let data = [];
+
+				frappe.call({
+					type: "GET",
+					method:'frappe.desk.search.search_link',
+					async: false,
+					no_spinner: true,
+					args: {
+						doctype: frappe.query_report_filters_by_name.party_type.get_value(),
+						txt: txt,
+						filters: {
+							"name": ["not in", values]
+						}
+					},
+					callback: function(r) {
+						data = r.results;
+					}
+				});
+				return data;
 			},
 			on_change: function() {
 				var party_type = frappe.query_report_filters_by_name.party_type.get_value();
-				var party = frappe.query_report_filters_by_name.party.get_value();
-				if(!party_type || !party) {
-					frappe.query_report_filters_by_name.party_name.set_value("");
-					return;
-				}
-				var fieldname = erpnext.utils.get_party_name(party_type) || "name";
-				frappe.db.get_value(party_type, party, fieldname, function(value) {
-					frappe.query_report_filters_by_name.party_name.set_value(value[fieldname]);
-				});
+				var parties = frappe.query_report_filters_by_name.party.get_value();
+				const values = parties.split(/\s*,\s*/).filter(d => d);
 
-				if (party_type === "Customer" || party_type === "Supplier") {
-					frappe.db.get_value(party_type, party, "tax_id", function(value) {
-						frappe.query_report_filters_by_name.tax_id.set_value(value["tax_id"]);
+				if(!party_type || !parties || values.length>1) {
+					frappe.query_report_filters_by_name.party_name.set_value("");
+					frappe.query_report_filters_by_name.tax_id.set_value("");
+					return;
+				} else {
+					var party = values[0];
+					frappe.query_report_filters_by_name.show_name = true;
+					var fieldname = erpnext.utils.get_party_name(party_type) || "name";
+					frappe.db.get_value(party_type, party, fieldname, function(value) {
+						frappe.query_report_filters_by_name.party_name.set_value(value[fieldname]);
 					});
+
+					if (party_type === "Customer" || party_type === "Supplier") {
+						frappe.db.get_value(party_type, party, "tax_id", function(value) {
+							frappe.query_report_filters_by_name.tax_id.set_value(value["tax_id"]);
+						});
+					}
 				}
 			}
 		},
diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py
index d6972b9..7fd653e 100644
--- a/erpnext/accounts/report/general_ledger/general_ledger.py
+++ b/erpnext/accounts/report/general_ledger/general_ledger.py
@@ -20,6 +20,10 @@
 	for acc in frappe.db.sql("""select name, is_group from tabAccount""", as_dict=1):
 		account_details.setdefault(acc.name, acc)
 
+	if filters.get('party'):
+		parties = str(filters.get("party")).strip()
+		filters.party = [d.strip() for d in parties.split(',') if d]
+
 	validate_filters(filters, account_details)
 
 	validate_party(filters)
@@ -57,14 +61,13 @@
 	if party:
 		if not party_type:
 			frappe.throw(_("To filter based on Party, select Party Type first"))
-		elif not frappe.db.exists(party_type, party):
-			frappe.throw(_("Invalid {0}: {1}").format(party_type, party))
-
+		else:
+			for d in party:
+				if not frappe.db.exists(party_type, d):
+					frappe.throw(_("Invalid {0}: {1}").format(party_type, d))
 
 def set_account_currency(filters):
-	if not (filters.get("account") or filters.get("party")):
-		return filters
-	else:
+	if filters.get("account") or (filters.get('party') and len(filters.party) == 1):
 		filters["company_currency"] = frappe.db.get_value("Company", filters.company, "default_currency")
 		account_currency = None
 
@@ -73,7 +76,7 @@
 		elif filters.get("party"):
 			gle_currency = frappe.db.get_value(
 				"GL Entry", {
-					"party_type": filters.party_type, "party": filters.party, "company": filters.company
+					"party_type": filters.party_type, "party": filters.party[0], "company": filters.company
 				},
 				"account_currency"
 			)
@@ -82,14 +85,14 @@
 				account_currency = gle_currency
 			else:
 				account_currency = None if filters.party_type in ["Employee", "Student", "Shareholder"] else \
-					frappe.db.get_value(filters.party_type, filters.party, "default_currency")
+					frappe.db.get_value(filters.party_type, filters.party[0], "default_currency")
 
 		filters["account_currency"] = account_currency or filters.company_currency
 
 		if filters.account_currency != filters.company_currency:
 			filters["show_in_account_currency"] = 1
 
-		return filters
+	return filters
 
 def get_result(filters, account_details):
 	gl_entries = get_gl_entries(filters)
@@ -151,7 +154,7 @@
 		conditions.append("party_type=%(party_type)s")
 
 	if filters.get("party"):
-		conditions.append("party=%(party)s")
+		conditions.append("party in %(party)s")
 
 	if not (filters.get("account") or filters.get("party") or
 		filters.get("group_by") in ["Group by Account", "Group by Party"]):
diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py
index 78f892b..239b532 100644
--- a/erpnext/selling/doctype/customer/customer.py
+++ b/erpnext/selling/doctype/customer/customer.py
@@ -7,7 +7,7 @@
 from frappe import _, msgprint, throw
 import frappe.defaults
 from frappe.utils import flt, cint, cstr
-from frappe.desk.reportview import build_match_conditions
+from frappe.desk.reportview import build_match_conditions, get_filters_cond
 from erpnext.utilities.transaction_base import TransactionBase
 from erpnext.accounts.party import validate_party_accounts, get_dashboard_info, get_timeline_data # keep this
 from frappe.contacts.address_and_contact import load_address_and_contact, delete_contact_and_address
@@ -179,7 +179,7 @@
 			frappe.db.set(self, "customer_name", newdn)
 
 
-def get_customer_list(doctype, txt, searchfield, start, page_len, filters):
+def get_customer_list(doctype, txt, searchfield, start, page_len, filters=None):
 	if frappe.db.get_default("cust_master_name") == "Customer Name":
 		fields = ["name", "customer_group", "territory"]
 	else:
@@ -188,6 +188,10 @@
 	match_conditions = build_match_conditions("Customer")
 	match_conditions = "and {}".format(match_conditions) if match_conditions else ""
 
+	if filters:
+		filter_conditions = get_filters_cond(doctype, filters, [])
+		match_conditions += "{}".format(filter_conditions)
+
 	return frappe.db.sql("""select %s from `tabCustomer` where docstatus < 2
 		and (%s like %s or customer_name like %s)
 		{match_conditions}