fix: Leave Encashment calculations (#31062)

diff --git a/erpnext/hr/doctype/leave_encashment/leave_encashment.py b/erpnext/hr/doctype/leave_encashment/leave_encashment.py
index 0f655e3..7c0f0db 100644
--- a/erpnext/hr/doctype/leave_encashment/leave_encashment.py
+++ b/erpnext/hr/doctype/leave_encashment/leave_encashment.py
@@ -7,7 +7,7 @@
 from frappe.model.document import Document
 from frappe.utils import getdate, nowdate
 
-from erpnext.hr.doctype.leave_allocation.leave_allocation import get_unused_leaves
+from erpnext.hr.doctype.leave_application.leave_application import get_leaves_for_period
 from erpnext.hr.doctype.leave_ledger_entry.leave_ledger_entry import create_leave_ledger_entry
 from erpnext.hr.utils import set_employee_name, validate_active_employee
 from erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment import (
@@ -107,7 +107,10 @@
 		self.leave_balance = (
 			allocation.total_leaves_allocated
 			- allocation.carry_forwarded_leaves_count
-			- get_unused_leaves(self.employee, self.leave_type, allocation.from_date, self.encashment_date)
+			# adding this because the function returns a -ve number
+			+ get_leaves_for_period(
+				self.employee, self.leave_type, allocation.from_date, self.encashment_date
+			)
 		)
 
 		encashable_days = self.leave_balance - frappe.db.get_value(
@@ -126,14 +129,25 @@
 		return True
 
 	def get_leave_allocation(self):
-		leave_allocation = frappe.db.sql(
-			"""select name, to_date, total_leaves_allocated, carry_forwarded_leaves_count from `tabLeave Allocation` where '{0}'
-		between from_date and to_date and docstatus=1 and leave_type='{1}'
-		and employee= '{2}'""".format(
-				self.encashment_date or getdate(nowdate()), self.leave_type, self.employee
-			),
-			as_dict=1,
-		)  # nosec
+		date = self.encashment_date or getdate()
+
+		LeaveAllocation = frappe.qb.DocType("Leave Allocation")
+		leave_allocation = (
+			frappe.qb.from_(LeaveAllocation)
+			.select(
+				LeaveAllocation.name,
+				LeaveAllocation.from_date,
+				LeaveAllocation.to_date,
+				LeaveAllocation.total_leaves_allocated,
+				LeaveAllocation.carry_forwarded_leaves_count,
+			)
+			.where(
+				((LeaveAllocation.from_date <= date) & (date <= LeaveAllocation.to_date))
+				& (LeaveAllocation.docstatus == 1)
+				& (LeaveAllocation.leave_type == self.leave_type)
+				& (LeaveAllocation.employee == self.employee)
+			)
+		).run(as_dict=True)
 
 		return leave_allocation[0] if leave_allocation else None
 
diff --git a/erpnext/hr/doctype/leave_encashment/test_leave_encashment.py b/erpnext/hr/doctype/leave_encashment/test_leave_encashment.py
index 83eb969..d06b6a3 100644
--- a/erpnext/hr/doctype/leave_encashment/test_leave_encashment.py
+++ b/erpnext/hr/doctype/leave_encashment/test_leave_encashment.py
@@ -4,26 +4,42 @@
 import unittest
 
 import frappe
-from frappe.utils import add_months, today
+from frappe.tests.utils import FrappeTestCase
+from frappe.utils import add_days, get_year_ending, get_year_start, getdate
 
 from erpnext.hr.doctype.employee.test_employee import make_employee
+from erpnext.hr.doctype.holiday_list.test_holiday_list import set_holiday_list
 from erpnext.hr.doctype.leave_period.test_leave_period import create_leave_period
 from erpnext.hr.doctype.leave_policy.test_leave_policy import create_leave_policy
 from erpnext.hr.doctype.leave_policy_assignment.leave_policy_assignment import (
 	create_assignment_for_multiple_employees,
 )
+from erpnext.payroll.doctype.salary_slip.test_salary_slip import (
+	make_holiday_list,
+	make_leave_application,
+)
 from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure
 
-test_dependencies = ["Leave Type"]
+test_records = frappe.get_test_records("Leave Type")
 
 
-class TestLeaveEncashment(unittest.TestCase):
+class TestLeaveEncashment(FrappeTestCase):
 	def setUp(self):
-		frappe.db.sql("""delete from `tabLeave Period`""")
-		frappe.db.sql("""delete from `tabLeave Policy Assignment`""")
-		frappe.db.sql("""delete from `tabLeave Allocation`""")
-		frappe.db.sql("""delete from `tabLeave Ledger Entry`""")
-		frappe.db.sql("""delete from `tabAdditional Salary`""")
+		frappe.db.delete("Leave Period")
+		frappe.db.delete("Leave Policy Assignment")
+		frappe.db.delete("Leave Allocation")
+		frappe.db.delete("Leave Ledger Entry")
+		frappe.db.delete("Additional Salary")
+		frappe.db.delete("Leave Encashment")
+
+		if not frappe.db.exists("Leave Type", "_Test Leave Type Encashment"):
+			frappe.get_doc(test_records[2]).insert()
+
+		date = getdate()
+		year_start = getdate(get_year_start(date))
+		year_end = getdate(get_year_ending(date))
+
+		make_holiday_list("_Test Leave Encashment", year_start, year_end)
 
 		# create the leave policy
 		leave_policy = create_leave_policy(
@@ -32,9 +48,9 @@
 		leave_policy.submit()
 
 		# create employee, salary structure and assignment
-		self.employee = make_employee("test_employee_encashment@example.com")
+		self.employee = make_employee("test_employee_encashment@example.com", company="_Test Company")
 
-		self.leave_period = create_leave_period(add_months(today(), -3), add_months(today(), 3))
+		self.leave_period = create_leave_period(year_start, year_end, "_Test Company")
 
 		data = {
 			"assignment_based_on": "Leave Period",
@@ -53,27 +69,15 @@
 			other_details={"leave_encashment_amount_per_day": 50},
 		)
 
-	def tearDown(self):
-		for dt in [
-			"Leave Period",
-			"Leave Allocation",
-			"Leave Ledger Entry",
-			"Additional Salary",
-			"Leave Encashment",
-			"Salary Structure",
-			"Leave Policy",
-		]:
-			frappe.db.sql("delete from `tab%s`" % dt)
-
+	@set_holiday_list("_Test Leave Encashment", "_Test Company")
 	def test_leave_balance_value_and_amount(self):
-		frappe.db.sql("""delete from `tabLeave Encashment`""")
 		leave_encashment = frappe.get_doc(
 			dict(
 				doctype="Leave Encashment",
 				employee=self.employee,
 				leave_type="_Test Leave Type Encashment",
 				leave_period=self.leave_period.name,
-				payroll_date=today(),
+				encashment_date=self.leave_period.to_date,
 				currency="INR",
 			)
 		).insert()
@@ -88,15 +92,46 @@
 		add_sal = frappe.get_all("Additional Salary", filters={"ref_docname": leave_encashment.name})[0]
 		self.assertTrue(add_sal)
 
-	def test_creation_of_leave_ledger_entry_on_submit(self):
-		frappe.db.sql("""delete from `tabLeave Encashment`""")
+	@set_holiday_list("_Test Leave Encashment", "_Test Company")
+	def test_leave_balance_value_with_leaves_and_amount(self):
+		date = self.leave_period.from_date
+		leave_application = make_leave_application(
+			self.employee, date, add_days(date, 3), "_Test Leave Type Encashment"
+		)
+		leave_application.reload()
+
 		leave_encashment = frappe.get_doc(
 			dict(
 				doctype="Leave Encashment",
 				employee=self.employee,
 				leave_type="_Test Leave Type Encashment",
 				leave_period=self.leave_period.name,
-				payroll_date=today(),
+				encashment_date=self.leave_period.to_date,
+				currency="INR",
+			)
+		).insert()
+
+		self.assertEqual(leave_encashment.leave_balance, 10 - leave_application.total_leave_days)
+		# encashable days threshold is 5, total leaves are 6, so encashable days = 6-5 = 1
+		# with charge of 50 per day
+		self.assertEqual(leave_encashment.encashable_days, leave_encashment.leave_balance - 5)
+		self.assertEqual(leave_encashment.encashment_amount, 50)
+
+		leave_encashment.submit()
+
+		# assert links
+		add_sal = frappe.get_all("Additional Salary", filters={"ref_docname": leave_encashment.name})[0]
+		self.assertTrue(add_sal)
+
+	@set_holiday_list("_Test Leave Encashment", "_Test Company")
+	def test_creation_of_leave_ledger_entry_on_submit(self):
+		leave_encashment = frappe.get_doc(
+			dict(
+				doctype="Leave Encashment",
+				employee=self.employee,
+				leave_type="_Test Leave Type Encashment",
+				leave_period=self.leave_period.name,
+				encashment_date=self.leave_period.to_date,
 				currency="INR",
 			)
 		).insert()