Refactor party type (#13831)

diff --git a/erpnext/__init__.py b/erpnext/__init__.py
index 3eaf994..e388601 100644
--- a/erpnext/__init__.py
+++ b/erpnext/__init__.py
@@ -79,6 +79,16 @@
 
 	return frappe.local.enable_perpetual_inventory[company]
 
+def get_party_account_type(party_type):
+	if not hasattr(frappe.local, 'party_account_types'):
+		frappe.local.party_account_types = {}
+
+	if not party_type in frappe.local.party_account_types:
+		frappe.local.party_account_types[party_type] = frappe.db.get_value("Party Type",
+			party_type, "account_type") or ''
+
+	return frappe.local.party_account_types[party_type]
+
 def get_region(company=None):
 	'''Return the default country based on flag, company or global settings
 
diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.js b/erpnext/accounts/doctype/journal_entry/journal_entry.js
index 9aa8de1..afa1ffe 100644
--- a/erpnext/accounts/doctype/journal_entry/journal_entry.js
+++ b/erpnext/accounts/doctype/journal_entry/journal_entry.js
@@ -158,9 +158,14 @@
 			};
 		});
 
-		me.frm.set_query("party_type", "accounts", function() {
-			return{
-				query: "erpnext.setup.doctype.party_type.party_type.get_party_type"
+		me.frm.set_query("party_type", "accounts", function(doc, cdt, cdn) {
+			const row = locals[cdt][cdn];
+
+			return {
+				query: "erpnext.setup.doctype.party_type.party_type.get_party_type",
+				filters: {
+					'account': row.account
+				}
 			}
 		});
 
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js
index f79efed..ca50e2d 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.js
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js
@@ -12,10 +12,8 @@
 
 	setup: function(frm) {
 		frm.set_query("paid_from", function() {
-			var party_account_type = in_list(["Customer", "Student"], frm.doc.party_type) ?
-				"Receivable" : "Payable";
 			var account_types = in_list(["Pay", "Internal Transfer"], frm.doc.payment_type) ?
-				["Bank", "Cash"] : party_account_type;
+				["Bank", "Cash"] : [frappe.boot.party_account_types[frm.doc.party_type]];
 
 			return {
 				filters: {
@@ -29,16 +27,14 @@
 		frm.set_query("party_type", function() {
 			return{
 				"filters": {
-					"name": ["in",["Customer","Supplier", "Employee", "Student"]],
+					"name": ["in", Object.keys(frappe.boot.party_account_types)],
 				}
 			}
 		});
 
 		frm.set_query("paid_to", function() {
-			var party_account_type = in_list(["Customer", "Student"], frm.doc.party_type) ?
-				"Receivable" : "Payable";
 			var account_types = in_list(["Receive", "Internal Transfer"], frm.doc.payment_type) ?
-				["Bank", "Cash"] : party_account_type;
+				["Bank", "Cash"] : [frappe.boot.party_account_types[frm.doc.party_type]];
 
 			return {
 				filters: {
@@ -86,9 +82,9 @@
 		});
 
 		frm.set_query("reference_name", "references", function(doc, cdt, cdn) {
-			child = locals[cdt][cdn];
-			filters = {"docstatus": 1, "company": doc.company};
-			party_type_doctypes = ['Sales Invoice', 'Sales Order', 'Purchase Invoice', 
+			const child = locals[cdt][cdn];
+			const filters = {"docstatus": 1, "company": doc.company};
+			const party_type_doctypes = ['Sales Invoice', 'Sales Order', 'Purchase Invoice',
 				'Purchase Order', 'Expense Claim', 'Fees'];
 
 			if (in_list(party_type_doctypes, child.reference_doctype)) {
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py
index 9b50252..96b997f 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.py
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py
@@ -160,9 +160,9 @@
 			if not frappe.db.exists(self.party_type, self.party):
 				frappe.throw(_("Invalid {0}: {1}").format(self.party_type, self.party))
 
-			if self.party_account and self.party_type != "Employee":
-				party_account_type = "Receivable" if self.party_type in ("Customer", "Student") else "Payable"
-				self.validate_account_type(self.party_account, [party_account_type])
+			if self.party_account:
+				self.validate_account_type(self.party_account,
+					[erpnext.get_party_account_type(self.party_type)])
 
 	def validate_bank_accounts(self):
 		if self.payment_type in ("Pay", "Internal Transfer"):
@@ -413,7 +413,6 @@
 			else:
 				against_account = self.paid_from
 
-
 			party_gl_dict = self.get_gl_dict({
 				"account": self.party_account,
 				"party_type": self.party_type,
@@ -422,7 +421,7 @@
 				"account_currency": self.party_account_currency
 			})
 
-			dr_or_cr = "credit" if self.party_type in ["Customer", "Student"] else "debit"
+			dr_or_cr = "credit" if erpnext.get_party_account_type(self.party_type) == 'Receivable' else "debit"
 
 			for d in self.get("references"):
 				gle = party_gl_dict.copy()
diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js
index 0901046..4c24a9f 100644
--- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js
+++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js
@@ -24,8 +24,10 @@
 	onload: function() {
 		var me = this;
 		this.frm.set_query("party_type", function() {
-			return{
-				query: "erpnext.setup.doctype.party_type.party_type.get_party_type"
+			return {
+				"filters": {
+					"name": ["in", Object.keys(frappe.boot.party_account_types)],
+				}
 			}
 		});
 
@@ -37,7 +39,7 @@
 					filters: {
 						"company": me.frm.doc.company,
 						"is_group": 0,
-						"account_type": (me.frm.doc.party_type == "Customer" ? "Receivable" : "Payable")
+						"account_type": frappe.boot.party_account_types[me.frm.doc.party_type]
 					}
 				};
 			}
diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
index 3c87fe5..7cd951a 100644
--- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
+++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
@@ -2,7 +2,7 @@
 # For license information, please see license.txt
 
 from __future__ import unicode_literals
-import frappe
+import frappe, erpnext
 from frappe.utils import flt
 from frappe import msgprint, _
 from frappe.model.document import Document
@@ -30,8 +30,8 @@
 		return payment_entries
 
 	def get_jv_entries(self):
-		dr_or_cr = "credit_in_account_currency" if self.party_type == "Customer" \
-			else "debit_in_account_currency"
+		dr_or_cr = ("credit_in_account_currency" if erpnext.get_party_account_type(self.party_type) == 'Receivable'
+			else "debit_in_account_currency")
 
 		bank_account_condition = "t2.against_account like %(bank_cash_account)s" \
 				if self.bank_cash_account else "1=1"
@@ -104,8 +104,8 @@
 
 		self.get_invoice_entries()
 		self.validate_invoice()
-		dr_or_cr = "credit_in_account_currency" \
-			if self.party_type == "Customer" else "debit_in_account_currency"
+		dr_or_cr = ("credit_in_account_currency"
+			if erpnext.get_party_account_type(self.party_type) == 'Receivable' else "debit_in_account_currency")
 			
 		lst = []
 		for e in self.get('payments'):
@@ -173,11 +173,8 @@
 	def check_condition(self):
 		cond = " and posting_date >= '{0}'".format(frappe.db.escape(self.from_date)) if self.from_date else ""
 		cond += " and posting_date <= '{0}'".format(frappe.db.escape(self.to_date)) if self.to_date else ""
-
-		if self.party_type == "Customer":
-			dr_or_cr = "debit_in_account_currency"
-		else:
-			dr_or_cr = "credit_in_account_currency"
+		dr_or_cr = ("debit_in_account_currency" if erpnext.get_party_account_type(self.party_type) == 'Receivable'
+			else "credit_in_account_currency")
 
 		if self.minimum_amount:
 			cond += " and `{0}` >= {1}".format(dr_or_cr, flt(self.minimum_amount))
diff --git a/erpnext/accounts/report/trial_balance_for_party/trial_balance_for_party.js b/erpnext/accounts/report/trial_balance_for_party/trial_balance_for_party.js
index 7ccec30..29f798e 100644
--- a/erpnext/accounts/report/trial_balance_for_party/trial_balance_for_party.js
+++ b/erpnext/accounts/report/trial_balance_for_party/trial_balance_for_party.js
@@ -46,9 +46,10 @@
 		{
 			"fieldname":"party_type",
 			"label": __("Party Type"),
-			"fieldtype": "Select",
-			"options": ["Customer", "Supplier"],
-			"default": "Customer"
+			"fieldtype": "Link",
+			"options": "Party Type",
+			"default": "Customer",
+			"reqd": 1
 		},
 		{
 			"fieldname":"party",
diff --git a/erpnext/accounts/report/trial_balance_for_party/trial_balance_for_party.py b/erpnext/accounts/report/trial_balance_for_party/trial_balance_for_party.py
index 6480623..8be63a0 100644
--- a/erpnext/accounts/report/trial_balance_for_party/trial_balance_for_party.py
+++ b/erpnext/accounts/report/trial_balance_for_party/trial_balance_for_party.py
@@ -7,26 +7,28 @@
 from frappe.utils import flt, cint
 from erpnext.accounts.report.trial_balance.trial_balance import validate_filters
 
-
 def execute(filters=None):
 	validate_filters(filters)
-	
+
 	show_party_name = is_party_name_visible(filters)
-	
+
 	columns = get_columns(filters, show_party_name)
 	data = get_data(filters, show_party_name)
 
 	return columns, data
-	
+
 def get_data(filters, show_party_name):
-	party_name_field = "customer_name" if filters.get("party_type")=="Customer" else "supplier_name"
+	party_name_field = "{0}_name".format(frappe.scrub(filters.get('party_type')))
+	if filters.get('party_type') == 'Student':
+		party_name_field = 'first_name'
+
 	party_filters = {"name": filters.get("party")} if filters.get("party") else {}
 	parties = frappe.get_all(filters.get("party_type"), fields = ["name", party_name_field], 
 		filters = party_filters, order_by="name")
 	company_currency = frappe.db.get_value("Company", filters.company, "default_currency")
 	opening_balances = get_opening_balances(filters)
 	balances_within_period = get_balances_within_period(filters)
-	
+
 	data = []
 	# total_debit, total_credit = 0, 0
 	total_row = frappe._dict({
@@ -41,28 +43,28 @@
 		row = { "party": party.name }
 		if show_party_name:
 			row["party_name"] = party.get(party_name_field)
-		
+
 		# opening
 		opening_debit, opening_credit = opening_balances.get(party.name, [0, 0])
 		row.update({
 			"opening_debit": opening_debit,
 			"opening_credit": opening_credit
 		})
-		
+
 		# within period
 		debit, credit = balances_within_period.get(party.name, [0, 0])
 		row.update({
 			"debit": debit,
 			"credit": credit
 		})
-				
+
 		# closing
 		closing_debit, closing_credit = toggle_debit_credit(opening_debit + debit, opening_credit + credit)
 		row.update({
 			"closing_debit": closing_debit,
 			"closing_credit": closing_credit
 		})
-		
+
 		# totals
 		for col in total_row:
 			total_row[col] += row.get(col)
@@ -70,24 +72,24 @@
 		row.update({
 			"currency": company_currency
 		})
-		
+
 		has_value = False
 		if (opening_debit or opening_credit or debit or credit or closing_debit or closing_credit):
 			has_value  =True
 		
 		if cint(filters.show_zero_values) or has_value:
 			data.append(row)
-		
+
 	# Add total row
-	
+
 	total_row.update({
 		"party": "'" + _("Totals") + "'",
 		"currency": company_currency
 	})
 	data.append(total_row)
-	
+
 	return data
-	
+
 def get_opening_balances(filters):
 	gle = frappe.db.sql("""
 		select party, sum(debit) as opening_debit, sum(credit) as opening_credit 
@@ -100,14 +102,14 @@
 			"from_date": filters.from_date,
 			"party_type": filters.party_type
 		}, as_dict=True)
-		
+
 	opening = frappe._dict()
 	for d in gle:
 		opening_debit, opening_credit = toggle_debit_credit(d.opening_debit, d.opening_credit)
 		opening.setdefault(d.party, [opening_debit, opening_credit])
-		
+
 	return opening
-	
+
 def get_balances_within_period(filters):
 	gle = frappe.db.sql("""
 		select party, sum(debit) as debit, sum(credit) as credit 
@@ -122,13 +124,13 @@
 			"to_date": filters.to_date,
 			"party_type": filters.party_type
 		}, as_dict=True)
-		
+
 	balances_within_period = frappe._dict()
 	for d in gle:
 		balances_within_period.setdefault(d.party, [d.debit, d.credit])
-		
+
 	return balances_within_period
-	
+
 def toggle_debit_credit(debit, credit):
 	if flt(debit) > flt(credit):
 		debit = flt(debit) - flt(credit)
@@ -136,9 +138,9 @@
 	else:
 		credit = flt(credit) - flt(debit)
 		debit = 0.0
-		
+
 	return debit, credit
-	
+
 def get_columns(filters, show_party_name):
 	columns = [
 		{
@@ -198,7 +200,7 @@
 			"hidden": 1
 		}
 	]
-	
+
 	if show_party_name:
 		columns.insert(1, {
 			"fieldname": "party_name",
@@ -206,17 +208,21 @@
 			"fieldtype": "Data",
 			"width": 200
 		})
-		
+
 	return columns
-		
+
 def is_party_name_visible(filters):
 	show_party_name = False
-	if filters.get("party_type") == "Customer":
-		party_naming_by = frappe.db.get_single_value("Selling Settings", "cust_master_name")
+
+	if filters.get('party_type') in ['Customer', 'Supplier']:
+		if filters.get("party_type") == "Customer":
+			party_naming_by = frappe.db.get_single_value("Selling Settings", "cust_master_name")
+		else:
+			party_naming_by = frappe.db.get_single_value("Buying Settings", "supp_master_name")
+
+		if party_naming_by == "Naming Series":
+			show_party_name = True
 	else:
-		party_naming_by = frappe.db.get_single_value("Buying Settings", "supp_master_name")
-		
-	if party_naming_by == "Naming Series":
 		show_party_name = True
-		
+
 	return show_party_name
\ No newline at end of file
diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py
index b5d9cd8..9c7310f 100644
--- a/erpnext/accounts/utils.py
+++ b/erpnext/accounts/utils.py
@@ -3,7 +3,7 @@
 
 from __future__ import unicode_literals
 
-import frappe
+import frappe, erpnext
 import frappe.defaults
 from frappe.utils import nowdate, cstr, flt, cint, now, getdate
 from frappe import throw, _
@@ -322,7 +322,9 @@
 			and t1.name = %(voucher_no)s and t2.name = %(voucher_detail_no)s
 			and t1.docstatus=1 """.format(dr_or_cr = args.get("dr_or_cr")), args)
 	else:
-		party_account_field = "paid_from" if args.party_type == "Customer" else "paid_to"
+		party_account_field = ("paid_from"
+			if erpnext.get_party_account_type(args.party_type) == 'Receivable' else "paid_to")
+
 		if args.voucher_detail_no:
 			ret = frappe.db.sql("""select t1.name
 				from `tabPayment Entry` t1, `tabPayment Entry Reference` t2
@@ -574,14 +576,14 @@
 	outstanding_invoices = []
 	precision = frappe.get_precision("Sales Invoice", "outstanding_amount")
 
-	if party_type in ("Customer", "Student"):
+	if erpnext.get_party_account_type(party_type) == 'Receivable':
 		dr_or_cr = "debit_in_account_currency - credit_in_account_currency"
 		payment_dr_or_cr = "payment_gl_entry.credit_in_account_currency - payment_gl_entry.debit_in_account_currency"
 	else:
 		dr_or_cr = "credit_in_account_currency - debit_in_account_currency"
 		payment_dr_or_cr = "payment_gl_entry.debit_in_account_currency - payment_gl_entry.credit_in_account_currency"
 
-	invoice = 'Sales Invoice' if party_type == 'Customer' else 'Purchase Invoice'
+	invoice = 'Sales Invoice' if erpnext.get_party_account_type(party_type) == 'Receivable' else 'Purchase Invoice'
 	invoice_list = frappe.db.sql("""
 		select
 			voucher_no, voucher_type, posting_date, ifnull(sum({dr_or_cr}), 0) as invoice_amount,
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index abee28f..a8c6cea 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -523,6 +523,7 @@
 erpnext.patches.v11_0.add_default_email_template_for_leave
 erpnext.patches.v11_0.set_default_email_template_in_hr
 erpnext.patches.v10_0.taxes_issue_with_pos
+erpnext.patches.v11_0.update_account_type_in_party_type
 erpnext.patches.v10_1.transfer_subscription_to_auto_repeat
 erpnext.patches.v10_1.drop_old_subscription_records
 erpnext.patches.v11_0.update_brand_in_item_price
diff --git a/erpnext/patches/v11_0/update_account_type_in_party_type.py b/erpnext/patches/v11_0/update_account_type_in_party_type.py
new file mode 100644
index 0000000..efa04fd
--- /dev/null
+++ b/erpnext/patches/v11_0/update_account_type_in_party_type.py
@@ -0,0 +1,13 @@
+# Copyright (c) 2017, Frappe and Contributors
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+import frappe
+
+def execute():
+	frappe.reload_doc('setup', 'doctype', 'party_type')
+	party_types = {'Customer': 'Receivable', 'Supplier': 'Payable',
+		'Employee': 'Payable', 'Member': 'Receivable', 'Shareholder': 'Payable', 'Student': 'Receivable'}
+
+	for party_type, account_type in party_types.items():
+		frappe.db.set_value('Party Type', party_type, 'account_type', account_type)
\ No newline at end of file
diff --git a/erpnext/setup/doctype/party_type/party_type.json b/erpnext/setup/doctype/party_type/party_type.json
index e1814ae..00f06a0 100644
--- a/erpnext/setup/doctype/party_type/party_type.json
+++ b/erpnext/setup/doctype/party_type/party_type.json
@@ -42,6 +42,39 @@
    "reqd": 1, 
    "search_index": 0, 
    "set_only_once": 0, 
+   "translatable": 0, 
+   "unique": 0
+  }, 
+  {
+   "allow_bulk_edit": 0, 
+   "allow_on_submit": 0, 
+   "bold": 0, 
+   "collapsible": 0, 
+   "columns": 0, 
+   "fieldname": "account_type", 
+   "fieldtype": "Select", 
+   "hidden": 0, 
+   "ignore_user_permissions": 0, 
+   "ignore_xss_filter": 0, 
+   "in_filter": 0, 
+   "in_global_search": 0, 
+   "in_list_view": 1, 
+   "in_standard_filter": 0, 
+   "label": "Account Type", 
+   "length": 0, 
+   "no_copy": 0, 
+   "options": "Payable\nReceivable", 
+   "permlevel": 0, 
+   "precision": "", 
+   "print_hide": 0, 
+   "print_hide_if_no_value": 0, 
+   "read_only": 0, 
+   "remember_last_selected_value": 0, 
+   "report_hide": 0, 
+   "reqd": 1, 
+   "search_index": 0, 
+   "set_only_once": 0, 
+   "translatable": 0, 
    "unique": 0
   }
  ], 
@@ -55,7 +88,7 @@
  "issingle": 0, 
  "istable": 0, 
  "max_attachments": 0, 
- "modified": "2017-11-23 17:46:27.075001", 
+ "modified": "2018-04-26 13:00:49.457439", 
  "modified_by": "Administrator", 
  "module": "Setup", 
  "name": "Party Type", 
@@ -64,7 +97,6 @@
  "permissions": [
   {
    "amend": 0, 
-   "apply_user_permissions": 0, 
    "cancel": 0, 
    "create": 0, 
    "delete": 0, 
@@ -84,7 +116,6 @@
   }, 
   {
    "amend": 0, 
-   "apply_user_permissions": 0, 
    "cancel": 0, 
    "create": 0, 
    "delete": 0, 
@@ -104,7 +135,6 @@
   }, 
   {
    "amend": 0, 
-   "apply_user_permissions": 0, 
    "cancel": 0, 
    "create": 0, 
    "delete": 0, 
diff --git a/erpnext/setup/doctype/party_type/party_type.py b/erpnext/setup/doctype/party_type/party_type.py
index 74db037..8baddf4 100644
--- a/erpnext/setup/doctype/party_type/party_type.py
+++ b/erpnext/setup/doctype/party_type/party_type.py
@@ -11,10 +11,15 @@
 
 @frappe.whitelist()
 def get_party_type(doctype, txt, searchfield, start, page_len, filters):
+	cond = ''
+	if filters and filters.get('account'):
+		account_type = frappe.db.get_value('Account', filters.get('account'), 'account_type')
+		cond = "and account_type = '%s'" % account_type
+
 	return frappe.db.sql("""select name from `tabParty Type`
-			where `{key}` LIKE %(txt)s
+			where `{key}` LIKE %(txt)s {cond}
 			order by name limit %(start)s, %(page_len)s"""
-			.format(key=searchfield), {
+			.format(key=searchfield, cond=cond), {
 				'txt': "%%%s%%" % frappe.db.escape(txt),
 				'start': start, 'page_len': page_len
 			})
diff --git a/erpnext/setup/setup_wizard/operations/install_fixtures.py b/erpnext/setup/setup_wizard/operations/install_fixtures.py
index f2eaece..3309cbc 100644
--- a/erpnext/setup/setup_wizard/operations/install_fixtures.py
+++ b/erpnext/setup/setup_wizard/operations/install_fixtures.py
@@ -198,12 +198,12 @@
 		{'doctype': "Email Account", "email_id": "support@example.com", "append_to": "Issue"},
 		{'doctype': "Email Account", "email_id": "jobs@example.com", "append_to": "Job Applicant"},
 
-		{'doctype': "Party Type", "party_type": "Customer"},
-		{'doctype': "Party Type", "party_type": "Supplier"},
-		{'doctype': "Party Type", "party_type": "Employee"},
-		{'doctype': "Party Type", "party_type": "Member"},
-		{'doctype': "Party Type", "party_type": "Shareholder"},
-		{'doctype': "Party Type", "party_type": "Student"},
+		{'doctype': "Party Type", "party_type": "Customer", "account_type": "Receivable"},
+		{'doctype': "Party Type", "party_type": "Supplier", "account_type": "Payable"},
+		{'doctype': "Party Type", "party_type": "Employee", "account_type": "Payable"},
+		{'doctype': "Party Type", "party_type": "Member", "account_type": "Receivable"},
+		{'doctype': "Party Type", "party_type": "Shareholder", "account_type": "Payable"},
+		{'doctype': "Party Type", "party_type": "Student", "account_type": "Receivable"},
 
 		{'doctype': "Opportunity Type", "name": "Hub"},
 		{'doctype': "Opportunity Type", "name": _("Sales")},
diff --git a/erpnext/startup/boot.py b/erpnext/startup/boot.py
index 8c43306..62c9e7b 100644
--- a/erpnext/startup/boot.py
+++ b/erpnext/startup/boot.py
@@ -36,6 +36,9 @@
 			default_letter_head, default_bank_account, enable_perpetual_inventory from `tabCompany`""",
 			as_dict=1, update={"doctype":":Company"})
 
+		party_account_types = frappe.db.sql(""" select name, ifnull(account_type, '') from `tabParty Type`""")
+		bootinfo.party_account_types = frappe._dict(party_account_types)
+
 def load_country_and_currency(bootinfo):
 	country = frappe.db.get_default("country")
 	if country and frappe.db.exists("Country", country):