Merge branch 'develop' into bank-trans-party-automatch
diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json
index c0eed18..259612e 100644
--- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json
+++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json
@@ -59,7 +59,9 @@
   "frozen_accounts_modifier",
   "report_settings_sb",
   "tab_break_dpet",
-  "show_balance_in_coa"
+  "show_balance_in_coa",
+  "banking_tab",
+  "enable_party_matching"
  ],
  "fields": [
   {
@@ -368,6 +370,18 @@
    "fieldname": "book_tax_discount_loss",
    "fieldtype": "Check",
    "label": "Book Tax Loss on Early Payment Discount"
+  },
+  {
+   "fieldname": "banking_tab",
+   "fieldtype": "Tab Break",
+   "label": "Banking"
+  },
+  {
+   "default": "0",
+   "description": "Auto match and set the Party in Bank Transactions",
+   "fieldname": "enable_party_matching",
+   "fieldtype": "Check",
+   "label": "Enable Automatic Party Matching"
   }
  ],
  "icon": "icon-cog",
@@ -375,7 +389,7 @@
  "index_web_pages_for_search": 1,
  "issingle": 1,
  "links": [],
- "modified": "2023-03-28 09:50:20.375233",
+ "modified": "2023-04-04 16:20:41.330039",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Accounts Settings",
diff --git a/erpnext/accounts/doctype/bank_party_mapper/__init__.py b/erpnext/accounts/doctype/bank_party_mapper/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/accounts/doctype/bank_party_mapper/__init__.py
diff --git a/erpnext/accounts/doctype/bank_party_mapper/bank_party_mapper.js b/erpnext/accounts/doctype/bank_party_mapper/bank_party_mapper.js
new file mode 100644
index 0000000..b13e46a
--- /dev/null
+++ b/erpnext/accounts/doctype/bank_party_mapper/bank_party_mapper.js
@@ -0,0 +1,10 @@
+// Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on("Bank Party Mapper", {
+	refresh(frm) {
+        if (!frm.is_new()) {
+            frm.set_intro(__("Please avoid editing unless you are absolutely certain."));
+        }
+	},
+});
diff --git a/erpnext/accounts/doctype/bank_party_mapper/bank_party_mapper.json b/erpnext/accounts/doctype/bank_party_mapper/bank_party_mapper.json
new file mode 100644
index 0000000..ddc79f2
--- /dev/null
+++ b/erpnext/accounts/doctype/bank_party_mapper/bank_party_mapper.json
@@ -0,0 +1,80 @@
+{
+ "actions": [],
+ "creation": "2023-03-31 10:48:20.249481",
+ "default_view": "List",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "party_type",
+  "party",
+  "column_break_wbna",
+  "bank_party_name",
+  "bank_party_account_number",
+  "bank_party_iban"
+ ],
+ "fields": [
+  {
+   "fieldname": "bank_party_account_number",
+   "fieldtype": "Data",
+   "in_list_view": 1,
+   "label": "Party Account No. (Bank Statement)"
+  },
+  {
+   "fieldname": "bank_party_iban",
+   "fieldtype": "Data",
+   "in_list_view": 1,
+   "label": "Party IBAN (Bank Statement)"
+  },
+  {
+   "fieldname": "column_break_wbna",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "party_type",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "in_standard_filter": 1,
+   "label": "Party Type",
+   "options": "DocType"
+  },
+  {
+   "fieldname": "party",
+   "fieldtype": "Dynamic Link",
+   "in_list_view": 1,
+   "in_standard_filter": 1,
+   "label": "Party",
+   "options": "party_type"
+  },
+  {
+   "fieldname": "bank_party_name",
+   "fieldtype": "Data",
+   "label": "Party Name (Bank Statement)"
+  }
+ ],
+ "index_web_pages_for_search": 1,
+ "links": [],
+ "modified": "2023-04-04 14:27:23.450456",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Bank Party Mapper",
+ "owner": "Administrator",
+ "permissions": [
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "System Manager",
+   "share": 1,
+   "write": 1
+  }
+ ],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "states": [],
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/bank_party_mapper/bank_party_mapper.py b/erpnext/accounts/doctype/bank_party_mapper/bank_party_mapper.py
new file mode 100644
index 0000000..02e36b0
--- /dev/null
+++ b/erpnext/accounts/doctype/bank_party_mapper/bank_party_mapper.py
@@ -0,0 +1,24 @@
+# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+import frappe
+from frappe.model.document import Document
+
+
+class BankPartyMapper(Document):
+	def on_update(self):
+		self.update_party_in_linked_transactions()
+
+	def update_party_in_linked_transactions(self):
+		if self.is_new():
+			return
+
+		# Set updated party values in other linked bank transactions
+		bank_transaction = frappe.qb.DocType("Bank Transaction")
+
+		frappe.qb.update(bank_transaction).set("party_type", self.party_type).set(
+			"party", self.party
+		).where(
+			(bank_transaction.bank_party_mapper == self.name)
+			& ((bank_transaction.party_type != self.party_type) | (bank_transaction.party != self.party))
+		).run()
diff --git a/erpnext/accounts/doctype/bank_party_mapper/test_bank_party_mapper.py b/erpnext/accounts/doctype/bank_party_mapper/test_bank_party_mapper.py
new file mode 100644
index 0000000..c05b23f
--- /dev/null
+++ b/erpnext/accounts/doctype/bank_party_mapper/test_bank_party_mapper.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+
+# import frappe
+from frappe.tests.utils import FrappeTestCase
+
+
+class TestBankPartyMapper(FrappeTestCase):
+	pass
diff --git a/erpnext/accounts/doctype/bank_transaction/auto_match_party.py b/erpnext/accounts/doctype/bank_transaction/auto_match_party.py
new file mode 100644
index 0000000..2a2aff1
--- /dev/null
+++ b/erpnext/accounts/doctype/bank_transaction/auto_match_party.py
@@ -0,0 +1,205 @@
+from typing import Tuple, Union
+
+import frappe
+from frappe.utils import flt
+from rapidfuzz import fuzz, process
+
+
+class AutoMatchParty:
+	"""
+	Matches by Account/IBAN and then by Party Name/Description sequentially.
+	Returns when a result is obtained.
+
+	Result (if present) is of the form: (Party Type, Party, Mapper,)
+
+	Mapper(if present) is one of the forms:
+	        1. {"mapper_name": <docname>}: Indicates that an existing Bank Party Mapper matched against
+	                the transaction and the same must be linked in the Bank Transaction.
+
+	        2. {"bank_party_account_number": <ACC No.>, "bank_party_iban": <IBAN>} : Indicates that a match was
+	                found in Customer/Supplier/Employee by account details. A Bank Party Mapper is now created
+	                mapping the Party to the Account No./IBAN
+
+	        3. {"bank_party_name": <Counter Party Name>}: Indicates that a match was found in
+	                Customer/Supplier/Employee by party name. A Bank Party Mapper is now created mapping the Party
+	                to the Party Name (Bank Statement). If matched by Description, no mapper is created as
+	                description is not a static key.
+
+	Mapper data is used either to create a new Bank Party Mapper or link an existing mapper to a transaction.
+	"""
+
+	def __init__(self, **kwargs) -> None:
+		self.__dict__.update(kwargs)
+
+	def get(self, key):
+		return self.__dict__.get(key, None)
+
+	def match(self) -> Union[Tuple, None]:
+		result = AutoMatchbyAccountIBAN(
+			bank_party_account_number=self.bank_party_account_number,
+			bank_party_iban=self.bank_party_iban,
+			deposit=self.deposit,
+		).match()
+
+		if not result:
+			result = AutoMatchbyPartyDescription(
+				bank_party_name=self.bank_party_name, description=self.description, deposit=self.deposit
+			).match()
+
+		return result
+
+
+class AutoMatchbyAccountIBAN:
+	def __init__(self, **kwargs) -> None:
+		self.__dict__.update(kwargs)
+
+	def get(self, key):
+		return self.__dict__.get(key, None)
+
+	def match(self):
+		if not (self.bank_party_account_number or self.bank_party_iban):
+			return None
+
+		result = self.match_account_in_bank_party_mapper()
+		if not result:
+			result = self.match_account_in_party()
+
+		return result
+
+	def match_account_in_bank_party_mapper(self) -> Union[Tuple, None]:
+		"""Check for a IBAN/Account No. match in Bank Party Mapper"""
+		result = None
+		or_filters = {}
+		if self.bank_party_account_number:
+			or_filters["bank_party_account_number"] = self.bank_party_account_number
+
+		if self.bank_party_iban:
+			or_filters["bank_party_iban"] = self.bank_party_iban
+
+		mapper = frappe.db.get_all(
+			"Bank Party Mapper",
+			or_filters=or_filters,
+			fields=["party_type", "party", "name"],
+			limit_page_length=1,
+		)
+		if mapper:
+			data = mapper[0]
+			return (data["party_type"], data["party"], {"mapper_name": data["name"]})
+
+		return result
+
+	def match_account_in_party(self) -> Union[Tuple, None]:
+		"""Check if there is a IBAN/Account No. match in Customer/Supplier/Employee"""
+		result = None
+
+		parties = ["Supplier", "Employee", "Customer"]  # most -> least likely to receive
+		if flt(self.deposit) > 0:
+			parties = ["Customer", "Supplier", "Employee"]  # most -> least likely to pay
+
+		for party in parties:
+			or_filters = {}
+			if self.bank_party_account_number:
+				acc_no_field = "bank_ac_no" if party == "Employee" else "bank_account_no"
+				or_filters[acc_no_field] = self.bank_party_account_number
+
+			if self.bank_party_iban:
+				or_filters["iban"] = self.bank_party_iban
+
+			party_result = frappe.db.get_all(
+				party, or_filters=or_filters, pluck="name", limit_page_length=1
+			)
+			if party_result:
+				result = (
+					party,
+					party_result[0],
+					{
+						"bank_party_account_number": self.get("bank_party_account_number"),
+						"bank_party_iban": self.get("bank_party_iban"),
+					},
+				)
+				break
+
+		return result
+
+
+class AutoMatchbyPartyDescription:
+	def __init__(self, **kwargs) -> None:
+		self.__dict__.update(kwargs)
+
+	def get(self, key):
+		return self.__dict__.get(key, None)
+
+	def match(self) -> Union[Tuple, None]:
+		# Match  by Customer, Supplier or Employee Name
+		# search bank party mapper by party
+		# fuzzy search by customer/supplier & employee
+		if not (self.bank_party_name or self.description):
+			return None
+
+		result = self.match_party_name_in_bank_party_mapper()
+
+		if not result:
+			result = self.match_party_name_desc_in_party()
+
+		return result
+
+	def match_party_name_in_bank_party_mapper(self) -> Union[Tuple, None]:
+		"""Check if match exists for party name in Bank Party Mapper"""
+		result = None
+		if not self.bank_party_name:
+			return
+
+		mapper_res = frappe.get_all(
+			"Bank Party Mapper",
+			filters={"bank_party_name": self.bank_party_name},
+			fields=["party_type", "party", "name"],
+			limit_page_length=1,
+		)
+		if mapper_res:
+			mapper_res = mapper_res[0]
+			return (
+				mapper_res["party_type"],
+				mapper_res["party"],
+				{"mapper_name": mapper_res["name"]},
+			)
+
+		return result
+
+	def match_party_name_desc_in_party(self) -> Union[Tuple, None]:
+		"""Fuzzy search party name and/or description against parties in the system"""
+		result = None
+		parties = ["Supplier", "Employee", "Customer"]  # most-least likely to receive
+		if flt(self.deposit) > 0.0:
+			parties = ["Customer", "Supplier", "Employee"]  # most-least likely to pay
+
+		for party in parties:
+			name_field = party.lower() + "_name"
+			filters = {"status": "Active"} if party == "Employee" else {"disabled": 0}
+
+			names = frappe.get_all(party, filters=filters, pluck=name_field)
+
+			for field in ["bank_party_name", "description"]:
+				if not result and self.get(field):
+					result = self.fuzzy_search_and_return_result(party, names, field)
+					if result:
+						break
+
+		return result
+
+	def fuzzy_search_and_return_result(self, party, names, field) -> Union[Tuple, None]:
+		result = process.extractOne(query=self.get(field), choices=names, scorer=fuzz.token_set_ratio)
+
+		if result:
+			party_name, score, index = result
+			if score > 75:
+				# Dont set description as a key in Bank Party Mapper due to its volatility
+				mapper = {"bank_party_name": self.get(field)} if field == "bank_party_name" else None
+				return (
+					party,
+					party_name,
+					mapper,
+				)
+			else:
+				return None
+
+		return result
diff --git a/erpnext/accounts/doctype/bank_transaction/bank_transaction.json b/erpnext/accounts/doctype/bank_transaction/bank_transaction.json
index 768d2f0..d3dc5b5 100644
--- a/erpnext/accounts/doctype/bank_transaction/bank_transaction.json
+++ b/erpnext/accounts/doctype/bank_transaction/bank_transaction.json
@@ -33,7 +33,12 @@
   "unallocated_amount",
   "party_section",
   "party_type",
-  "party"
+  "party",
+  "column_break_3czf",
+  "bank_party_name",
+  "bank_party_account_number",
+  "bank_party_iban",
+  "bank_party_mapper"
  ],
  "fields": [
   {
@@ -202,11 +207,38 @@
    "fieldtype": "Data",
    "label": "Transaction Type",
    "length": 50
+  },
+  {
+   "fieldname": "column_break_3czf",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "bank_party_name",
+   "fieldtype": "Data",
+   "label": "Party Name/Account Holder (Bank Statement)"
+  },
+  {
+   "fieldname": "bank_party_iban",
+   "fieldtype": "Data",
+   "label": "Party IBAN (Bank Statement)"
+  },
+  {
+   "fieldname": "bank_party_account_number",
+   "fieldtype": "Data",
+   "label": "Party Account No. (Bank Statement)"
+  },
+  {
+   "fieldname": "bank_party_mapper",
+   "fieldtype": "Link",
+   "hidden": 1,
+   "label": "Bank Party Mapper",
+   "options": "Bank Party Mapper",
+   "read_only": 1
   }
  ],
  "is_submittable": 1,
  "links": [],
- "modified": "2022-05-29 18:36:50.475964",
+ "modified": "2023-04-04 15:47:20.620006",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Bank Transaction",
@@ -260,4 +292,4 @@
  "states": [],
  "title_field": "bank_account",
  "track_changes": 1
-}
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/bank_transaction/bank_transaction.py b/erpnext/accounts/doctype/bank_transaction/bank_transaction.py
index fcbaf32..cf3a6e6 100644
--- a/erpnext/accounts/doctype/bank_transaction/bank_transaction.py
+++ b/erpnext/accounts/doctype/bank_transaction/bank_transaction.py
@@ -15,6 +15,9 @@
 		self.clear_linked_payment_entries()
 		self.set_status()
 
+		if frappe.db.get_single_value("Accounts Settings", "enable_party_matching"):
+			self.auto_set_party()
+
 	_saving_flag = False
 
 	# nosemgrep: frappe-semgrep-rules.rules.frappe-modifying-but-not-comitting
@@ -26,6 +29,9 @@
 			self.update_allocations()
 			self._saving_flag = False
 
+		if frappe.db.get_single_value("Accounts Settings", "enable_party_matching"):
+			self.update_automatch_bank_party_mapper()
+
 	def on_cancel(self):
 		self.clear_linked_payment_entries(for_cancel=True)
 		self.set_status(update=True)
@@ -146,6 +152,49 @@
 			payment_entry.payment_document, payment_entry.payment_entry, clearance_date, self
 		)
 
+	def auto_set_party(self):
+		from erpnext.accounts.doctype.bank_transaction.auto_match_party import AutoMatchParty
+
+		if self.party_type and self.party:
+			return
+
+		result = AutoMatchParty(
+			bank_party_account_number=self.bank_party_account_number,
+			bank_party_iban=self.bank_party_iban,
+			bank_party_name=self.bank_party_name,
+			description=self.description,
+			deposit=self.deposit,
+		).match()
+
+		if result:
+			party_type, party, mapper = result
+			to_update = {"party_type": party_type, "party": party}
+
+			if mapper and mapper.get("mapper_name"):
+				# Transaction matched with an existing Bank party Mapper record
+				to_update["bank_party_mapper"] = mapper.get("mapper_name")
+			elif mapper:
+				# Make new Mapper record to remember match
+				mapper_doc = frappe.get_doc(
+					{"doctype": "Bank Party Mapper", "party_type": party_type, "party": party}
+				)
+				mapper_doc.update(mapper)
+				mapper_doc.insert()
+				to_update["bank_party_mapper"] = mapper_doc.name
+
+			frappe.db.set_value("Bank Transaction", self.name, field=to_update)
+
+	def update_automatch_bank_party_mapper(self):
+		"""Update Bank Party Mapper if Party Type & Party are manually changed after submit."""
+		doc_before_update = self.get_doc_before_save()
+		party_type_changed = self.party_type and (doc_before_update.party_type != self.party_type)
+		party_changed = self.party and (doc_before_update.party != self.party)
+
+		if (party_type_changed or party_changed) and self.bank_party_mapper:
+			mapper_doc = frappe.get_doc("Bank Party Mapper", self.bank_party_mapper)
+			mapper_doc.update({"party_type": self.party_type, "party": self.party})
+			mapper_doc.save()
+
 
 @frappe.whitelist()
 def get_doctypes_for_bank_reconciliation():
diff --git a/erpnext/accounts/doctype/shareholder/shareholder.json b/erpnext/accounts/doctype/shareholder/shareholder.json
index e94aea9..2be2a2f 100644
--- a/erpnext/accounts/doctype/shareholder/shareholder.json
+++ b/erpnext/accounts/doctype/shareholder/shareholder.json
@@ -1,4 +1,5 @@
 {
+ "actions": [],
  "autoname": "naming_series:",
  "creation": "2017-12-25 16:50:53.878430",
  "doctype": "DocType",
@@ -19,7 +20,11 @@
   "contact_html",
   "section_break_3",
   "share_balance",
-  "contact_list"
+  "contact_list",
+  "bank_details_section",
+  "bank_account_no",
+  "column_break_tyo0",
+  "iban"
  ],
  "fields": [
   {
@@ -109,13 +114,33 @@
    "hidden": 1,
    "label": "Contact List",
    "read_only": 1
+  },
+  {
+   "fieldname": "bank_details_section",
+   "fieldtype": "Section Break",
+   "label": "Bank Details"
+  },
+  {
+   "fieldname": "bank_account_no",
+   "fieldtype": "Data",
+   "label": "Bank Account No"
+  },
+  {
+   "fieldname": "column_break_tyo0",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "iban",
+   "fieldtype": "Data",
+   "label": "IBAN"
   }
  ],
- "modified": "2019-11-17 23:24:11.395882",
+ "links": [],
+ "modified": "2023-03-30 16:00:55.087823",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Shareholder",
- "name_case": "Title Case",
+ "naming_rule": "By \"Naming Series\" field",
  "owner": "Administrator",
  "permissions": [
   {
@@ -158,6 +183,7 @@
  "search_fields": "folio_no",
  "sort_field": "modified",
  "sort_order": "DESC",
+ "states": [],
  "title_field": "title",
  "track_changes": 1
 }
\ No newline at end of file
diff --git a/erpnext/buying/doctype/supplier/supplier.json b/erpnext/buying/doctype/supplier/supplier.json
index 1bf7f58..a80dcfe 100644
--- a/erpnext/buying/doctype/supplier/supplier.json
+++ b/erpnext/buying/doctype/supplier/supplier.json
@@ -52,6 +52,11 @@
   "supplier_primary_address",
   "primary_address",
   "accounting_tab",
+  "bank_details_section",
+  "bank_account_no",
+  "column_break_n8mz",
+  "iban",
+  "section_break_ow3k",
   "payment_terms",
   "accounts",
   "settings_tab",
@@ -445,6 +450,29 @@
   {
    "fieldname": "column_break_59",
    "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "bank_details_section",
+   "fieldtype": "Section Break",
+   "label": "Bank Details"
+  },
+  {
+   "fieldname": "bank_account_no",
+   "fieldtype": "Data",
+   "label": "Bank Account No"
+  },
+  {
+   "fieldname": "column_break_n8mz",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "iban",
+   "fieldtype": "Data",
+   "label": "IBAN"
+  },
+  {
+   "fieldname": "section_break_ow3k",
+   "fieldtype": "Section Break"
   }
  ],
  "icon": "fa fa-user",
@@ -457,7 +485,7 @@
    "link_fieldname": "party"
   }
  ],
- "modified": "2023-02-18 11:05:50.592270",
+ "modified": "2023-03-30 15:50:40.241257",
  "modified_by": "Administrator",
  "module": "Buying",
  "name": "Supplier",
diff --git a/erpnext/selling/doctype/customer/customer.json b/erpnext/selling/doctype/customer/customer.json
index c133cd3..5dc6a72 100644
--- a/erpnext/selling/doctype/customer/customer.json
+++ b/erpnext/selling/doctype/customer/customer.json
@@ -61,6 +61,10 @@
   "tax_category",
   "tax_withholding_category",
   "accounting_tab",
+  "bank_details_section",
+  "bank_account_no",
+  "column_break_xtwg",
+  "iban",
   "credit_limit_section",
   "payment_terms",
   "credit_limits",
@@ -555,6 +559,25 @@
   {
    "fieldname": "column_break_54",
    "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "bank_details_section",
+   "fieldtype": "Section Break",
+   "label": "Bank Details"
+  },
+  {
+   "fieldname": "bank_account_no",
+   "fieldtype": "Data",
+   "label": "Bank Account No"
+  },
+  {
+   "fieldname": "column_break_xtwg",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "iban",
+   "fieldtype": "Data",
+   "label": "IBAN"
   }
  ],
  "icon": "fa fa-user",
@@ -568,7 +591,7 @@
    "link_fieldname": "party"
   }
  ],
- "modified": "2023-02-18 11:04:46.343527",
+ "modified": "2023-03-30 15:45:44.387975",
  "modified_by": "Administrator",
  "module": "Selling",
  "name": "Customer",
diff --git a/erpnext/setup/doctype/employee/employee.json b/erpnext/setup/doctype/employee/employee.json
index 99693d9..6cb4292 100644
--- a/erpnext/setup/doctype/employee/employee.json
+++ b/erpnext/setup/doctype/employee/employee.json
@@ -78,7 +78,9 @@
   "salary_mode",
   "bank_details_section",
   "bank_name",
+  "column_break_heye",
   "bank_ac_no",
+  "iban",
   "personal_details",
   "marital_status",
   "family_background",
@@ -804,17 +806,26 @@
   {
    "fieldname": "column_break_104",
    "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "column_break_heye",
+   "fieldtype": "Column Break"
+  },
+  {
+   "depends_on": "eval:doc.salary_mode == 'Bank'",
+   "fieldname": "iban",
+   "fieldtype": "Data",
+   "label": "IBAN"
   }
  ],
  "icon": "fa fa-user",
  "idx": 24,
  "image_field": "image",
  "links": [],
- "modified": "2022-09-13 10:27:14.579197",
+ "modified": "2023-03-30 15:57:05.174592",
  "modified_by": "Administrator",
  "module": "Setup",
  "name": "Employee",
- "name_case": "Title Case",
  "naming_rule": "By \"Naming Series\" field",
  "owner": "Administrator",
  "permissions": [
diff --git a/pyproject.toml b/pyproject.toml
index 0718e5b..e5bc884 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -12,6 +12,7 @@
     "pycountry~=20.7.3",
     "Unidecode~=1.2.0",
     "barcodenumber~=0.5.0",
+    "rapidfuzz~=2.15.0",
 
     # integration dependencies
     "gocardless-pro~=1.22.0",