refactor: unlink individual vouchers from payments
diff --git a/erpnext/accounts/doctype/unreconcile_payments/unreconcile_payments.py b/erpnext/accounts/doctype/unreconcile_payments/unreconcile_payments.py
index 304cccc..5161a92 100644
--- a/erpnext/accounts/doctype/unreconcile_payments/unreconcile_payments.py
+++ b/erpnext/accounts/doctype/unreconcile_payments/unreconcile_payments.py
@@ -55,7 +55,7 @@
 
 		for alloc in self.allocations:
 			doc = frappe.get_doc(alloc.reference_doctype, alloc.reference_name)
-			unlink_ref_doc_from_payment_entries(doc)
+			unlink_ref_doc_from_payment_entries(doc, self.voucher_no)
 			update_voucher_outstanding(
 				alloc.reference_doctype, alloc.reference_name, alloc.account, alloc.party_type, alloc.party
 			)
diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py
index 5c9b0dd..0a5c5b9 100644
--- a/erpnext/accounts/utils.py
+++ b/erpnext/accounts/utils.py
@@ -705,38 +705,62 @@
 				frappe.get_doc("Journal Entry", doc[0]).cancel()
 
 
-def update_accounting_ledgers_after_reference_removal(ref_type: str = None, ref_no: str = None):
+def update_accounting_ledgers_after_reference_removal(
+	ref_type: str = None, ref_no: str = None, payment_name: str = None
+):
 	# General Ledger
 	gle = qb.DocType("GL Entry")
-	qb.update(gle).set(gle.against_voucher_type, None).set(gle.against_voucher, None).set(
-		gle.modified, now()
-	).set(gle.modified_by, frappe.session.user).where(
-		(gle.against_voucher_type == ref_type) & (gle.against_voucher == ref_no)
-	).run()
+	gle_update_query = (
+		qb.update(gle)
+		.set(gle.against_voucher_type, None)
+		.set(gle.against_voucher, None)
+		.set(gle.modified, now())
+		.set(gle.modified_by, frappe.session.user)
+		.where((gle.against_voucher_type == ref_type) & (gle.against_voucher == ref_no))
+	)
+
+	if payment_name:
+		gle_update_query = gle_update_query.where(gle.voucher_no == payment_name)
+	gle_update_query.run()
 
 	# Payment Ledger
 	ple = qb.DocType("Payment Ledger Entry")
-	qb.update(ple).set(ple.against_voucher_type, ple.voucher_type).set(
-		ple.against_voucher_no, ple.voucher_no
-	).set(ple.modified, now()).set(ple.modified_by, frappe.session.user).where(
-		(ple.against_voucher_type == ref_type) & (ple.against_voucher_no == ref_no) & (ple.delinked == 0)
-	).run()
+	ple_update_query = (
+		qb.update(ple)
+		.set(ple.against_voucher_type, ple.voucher_type)
+		.set(ple.against_voucher_no, ple.voucher_no)
+		.set(ple.modified, now())
+		.set(ple.modified_by, frappe.session.user)
+		.where(
+			(ple.against_voucher_type == ref_type)
+			& (ple.against_voucher_no == ref_no)
+			& (ple.delinked == 0)
+		)
+	)
+
+	if payment_name:
+		ple_update_query = ple_update_query.where(ple.voucher_no == payment_name)
+	ple_update_query.run()
 
 
 def remove_ref_from_advance_section(ref_doc: object = None):
+	# TODO: this might need some testing
 	if ref_doc.doctype in ("Sales Invoice", "Purchase Invoice"):
 		ref_doc.set("advances", [])
 		adv_type = qb.DocType(f"{ref_doc.doctype} Advance")
 		qb.from_(adv_type).delete().where(adv_type.parent == ref_doc.name).run()
 
 
-def unlink_ref_doc_from_payment_entries(ref_doc):
-	remove_ref_doc_link_from_jv(ref_doc.doctype, ref_doc.name)
-	remove_ref_doc_link_from_pe(ref_doc.doctype, ref_doc.name)
-	update_accounting_ledgers_after_reference_removal(ref_doc.doctype, ref_doc.name)
+def unlink_ref_doc_from_payment_entries(ref_doc: object = None, payment_name: str = None):
+	remove_ref_doc_link_from_jv(ref_doc.doctype, ref_doc.name, payment_name)
+	remove_ref_doc_link_from_pe(ref_doc.doctype, ref_doc.name, payment_name)
+	update_accounting_ledgers_after_reference_removal(ref_doc.doctype, ref_doc.name, payment_name)
+	remove_ref_from_advance_section(ref_doc)
 
 
-def remove_ref_doc_link_from_jv(ref_type, ref_no):
+def remove_ref_doc_link_from_jv(
+	ref_type: str = None, ref_no: str = None, payment_name: str = None
+):
 	jea = qb.DocType("Journal Entry Account")
 
 	linked_jv = (
@@ -748,13 +772,23 @@
 		.run(as_list=1)
 	)
 	linked_jv = convert_to_list(linked_jv)
+	# remove reference only from specified payment
+	linked_jv = [x for x in linked_jv if x == payment_name] if payment_name else linked_jv
 
 	if linked_jv:
-		qb.update(jea).set(jea.reference_type, None).set(jea.reference_name, None).set(
-			jea.modified, now()
-		).set(jea.modified_by, frappe.session.user).where(
-			(jea.reference_type == ref_type) & (jea.reference_name == ref_no)
-		).run()
+		update_query = (
+			qb.update(jea)
+			.set(jea.reference_type, None)
+			.set(jea.reference_name, None)
+			.set(jea.modified, now())
+			.set(jea.modified_by, frappe.session.user)
+			.where((jea.reference_type == ref_type) & (jea.reference_name == ref_no))
+		)
+
+		if payment_name:
+			update_query = update_query.where(jea.parent == payment_name)
+
+		update_query.run()
 
 		frappe.msgprint(_("Journal Entries {0} are un-linked").format("\n".join(linked_jv)))
 
@@ -766,7 +800,9 @@
 	return [x[0] for x in result]
 
 
-def remove_ref_doc_link_from_pe(ref_type, ref_no):
+def remove_ref_doc_link_from_pe(
+	ref_type: str = None, ref_no: str = None, payment_name: str = None
+):
 	per = qb.DocType("Payment Entry Reference")
 	pay = qb.DocType("Payment Entry")
 
@@ -779,13 +815,24 @@
 		.run(as_list=1)
 	)
 	linked_pe = convert_to_list(linked_pe)
+	# remove reference only from specified payment
+	linked_pe = [x for x in linked_pe if x == payment_name] if payment_name else linked_pe
 
 	if linked_pe:
-		qb.update(per).set(per.allocated_amount, 0).set(per.modified, now()).set(
-			per.modified_by, frappe.session.user
-		).where(
-			(per.docstatus.lt(2) & (per.reference_doctype == ref_type) & (per.reference_name == ref_no))
-		).run()
+		update_query = (
+			qb.update(per)
+			.set(per.allocated_amount, 0)
+			.set(per.modified, now())
+			.set(per.modified_by, frappe.session.user)
+			.where(
+				(per.docstatus.lt(2) & (per.reference_doctype == ref_type) & (per.reference_name == ref_no))
+			)
+		)
+
+		if payment_name:
+			update_query = update_query.where(per.parent == payment_name)
+
+		update_query.run()
 
 		for pe in linked_pe:
 			try: