fix: handle carry forwarded leaves while checking for duplicate allocation
diff --git a/erpnext/hr/doctype/leave_policy_assignment/test_leave_policy_assignment.py b/erpnext/hr/doctype/leave_policy_assignment/test_leave_policy_assignment.py
index 3455bae..8c76ca1 100644
--- a/erpnext/hr/doctype/leave_policy_assignment/test_leave_policy_assignment.py
+++ b/erpnext/hr/doctype/leave_policy_assignment/test_leave_policy_assignment.py
@@ -188,6 +188,58 @@
 		}, "total_leaves_allocated")
 		self.assertEqual(leaves_allocated, 3)
 
+	def test_earned_leave_allocation_for_passed_months_with_carry_forwarded_leaves(self):
+		from erpnext.hr.doctype.leave_allocation.test_leave_allocation import create_leave_allocation
+
+		employee = get_employee()
+		leave_type = create_earned_leave_type("Test Earned Leave")
+		leave_period = create_leave_period("Test Earned Leave Period",
+			start_date=get_first_day(add_months(getdate(), -2)))
+		leave_policy = frappe.get_doc({
+			"doctype": "Leave Policy",
+			"title": "Test Leave Policy",
+			"leave_policy_details": [{"leave_type": leave_type.name, "annual_allocation": 12}]
+		}).insert()
+
+		# initial leave allocation = 5
+		leave_allocation = create_leave_allocation(
+			employee=employee.name,
+			employee_name=employee.employee_name,
+			leave_type=leave_type.name,
+			from_date=add_months(getdate(), -12),
+			to_date=add_months(getdate(), -3),
+			new_leaves_allocated=5,
+			carry_forward=0)
+		leave_allocation.submit()
+
+		# Case 3: assignment created on the last day of the leave period's latter month with carry forwarding
+		frappe.flags.current_date = get_last_day(add_months(getdate(), -1))
+
+		data = {
+			"assignment_based_on": "Leave Period",
+			"leave_policy": leave_policy.name,
+			"leave_period": leave_period.name,
+			"carry_forward": 1
+		}
+		# carry forwarded leaves = 5, 3 leaves allocated for passed months
+		leave_policy_assignments = create_assignment_for_multiple_employees([employee.name], frappe._dict(data))
+
+		details = frappe.db.get_value("Leave Allocation", {
+			"leave_policy_assignment": leave_policy_assignments[0]
+		}, ["total_leaves_allocated", "new_leaves_allocated", "unused_leaves", "name"], as_dict=True)
+		self.assertEqual(details.new_leaves_allocated, 2)
+		self.assertEqual(details.unused_leaves, 5)
+		self.assertEqual(details.total_leaves_allocated, 7)
+
+		# if the daily job is not completed yet, there is another check present
+		# to ensure leave is not already allocated to avoid duplication
+		from erpnext.hr.utils import is_earned_leave_already_allocated
+		frappe.flags.current_date = get_last_day(getdate())
+
+		allocation = frappe.get_doc('Leave Allocation', details.name)
+		# 1 leave is still pending to be allocated, irrespective of carry forwarded leaves
+		self.assertFalse(is_earned_leave_already_allocated(allocation, leave_policy.leave_policy_details[0].annual_allocation))
+
 	def tearDown(self):
 		frappe.db.rollback()
 
@@ -200,7 +252,8 @@
 		doctype="Leave Type",
 		is_earned_leave=1,
 		earned_leave_frequency="Monthly",
-		rounding=0.5
+		rounding=0.5,
+		is_carry_forward=1
 	)).insert()
 
 
diff --git a/erpnext/hr/utils.py b/erpnext/hr/utils.py
index 2a07e56..7fd3a98 100644
--- a/erpnext/hr/utils.py
+++ b/erpnext/hr/utils.py
@@ -312,7 +312,12 @@
 	leaves_for_passed_months = assignment.get_leaves_for_passed_months(allocation.leave_type,
 		annual_allocation, leave_type_details, date_of_joining)
 
-	if allocation.total_leaves_allocated >= leaves_for_passed_months:
+	# exclude carry-forwarded leaves while checking for leave allocation for passed months
+	num_allocations = allocation.total_leaves_allocated
+	if allocation.unused_leaves:
+		num_allocations -= allocation.unused_leaves
+
+	if num_allocations >= leaves_for_passed_months:
 		return True
 	return False