feat: create expiry and carry forward calculation on leave allocation creation
diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation.js b/erpnext/hr/doctype/leave_allocation/leave_allocation.js
index 228b552..1fc1d89 100755
--- a/erpnext/hr/doctype/leave_allocation/leave_allocation.js
+++ b/erpnext/hr/doctype/leave_allocation/leave_allocation.js
@@ -66,4 +66,4 @@
 			frm.set_value("total_leaves_allocated", flt(frm.doc.new_leaves_allocated));
 		}
 	}
-})
\ No newline at end of file
+});
\ No newline at end of file
diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation.py b/erpnext/hr/doctype/leave_allocation/leave_allocation.py
index 8429ad4..6fe8fdf 100755
--- a/erpnext/hr/doctype/leave_allocation/leave_allocation.py
+++ b/erpnext/hr/doctype/leave_allocation/leave_allocation.py
@@ -3,7 +3,7 @@
 
 from __future__ import unicode_literals
 import frappe
-from frappe.utils import flt, date_diff, formatdate
+from frappe.utils import flt, date_diff, formatdate, add_days
 from frappe import _
 from frappe.model.document import Document
 from erpnext.hr.utils import set_employee_name, get_leave_period
@@ -42,6 +42,7 @@
 				.format(self.leave_type, self.employee))
 
 	def on_submit(self):
+		self.expire_previous_allocation()
 		self.create_leave_ledger_entry()
 
 	def on_cancel(self):
@@ -89,10 +90,20 @@
 			self.leave_type, self.from_date, self.carry_forward)
 
 		self.total_leaves_allocated = flt(self.carry_forwarded_leaves) + flt(self.new_leaves_allocated)
+		self.maintain_carry_forwarded_leaves()
 
 		if not self.total_leaves_allocated and not frappe.db.get_value("Leave Type", self.leave_type, "is_earned_leave") and not frappe.db.get_value("Leave Type", self.leave_type, "is_compensatory"):
 			frappe.throw(_("Total leaves allocated is mandatory for Leave Type {0}".format(self.leave_type)))
 
+	def maintain_carry_forwarded_leaves(self):
+		''' reduce the carry forwarded leaves to be within the maximum allowed leaves '''
+		if not self.carry_forward:
+			return
+		max_leaves_allowed = frappe.db.get_value("Leave Type", self.leave_type, "max_leaves_allowed")
+		if self.new_leaves_allocated <= max_leaves_allowed <= self.total_leaves_allocated:
+			self.carry_forwarded_leaves = max_leaves_allowed - flt(self.new_leaves_allocated)
+			self.total_leaves_allocated = flt(max_leaves_allowed)
+
 	def validate_total_leaves_allocated(self):
 		# Adding a day to include To Date in the difference
 		date_difference = date_diff(self.to_date, self.from_date) + 1
@@ -102,9 +113,9 @@
 	def create_leave_ledger_entry(self, submit=True):
 		if self.carry_forwarded_leaves:
 			expiry_days = frappe.db.get_value("Leave Type", self.leave_type, "carry_forward_leave_expiry")
-
 			args = dict(
 				leaves=self.carry_forwarded_leaves,
+				from_date=self.from_date,
 				to_date=add_days(self.from_date, expiry_days) if expiry_days else self.to_date,
 				is_carry_forward=1
 			)
@@ -112,11 +123,26 @@
 
 		args = dict(
 			leaves=self.new_leaves_allocated,
+			from_date=self.from_date,
 			to_date=self.to_date,
 			is_carry_forward=0
 		)
 		create_leave_ledger_entry(self, args, submit)
 
+	def expire_previous_allocation(self):
+		''' expire previous allocation leaves '''
+		leaves = get_remaining_leaves(self.employee, self.leave_type, self.from_date)
+
+		if flt(leaves) > 0:
+			args = dict(
+				leaves=leaves * -1,
+				from_date=self.to_date,
+				to_date=self.to_date,
+				is_carry_forward=0,
+				is_expired=1
+			)
+			create_leave_ledger_entry(self, args)
+
 def get_leave_allocation_for_period(employee, leave_type, from_date, to_date):
 	leave_allocated = 0
 	leave_allocations = frappe.db.sql("""
@@ -143,35 +169,20 @@
 @frappe.whitelist()
 def get_carry_forwarded_leaves(employee, leave_type, date, carry_forward=None):
 	carry_forwarded_leaves = 0
-
 	if carry_forward:
 		validate_carry_forward(leave_type)
-
-		carry_forwarded_leaves =  frappe.db.sql("""
-			SELECT
-				SUM(leaves)
-			FROM `tabLeave Ledger Entry`
-			WHERE
-				employee=%s
-				AND leave_type=%s
-				AND docstatus=1
-				AND	to_date < %s
-				AND name NOT IN (
-					SELECT name
-					from `tabLeave Ledger Entry`
-					WHERE
-						employee=%s
-						AND leave_type=%s
-						AND docstatus=1
-						AND	to_date < %s
-						is_expired=1
-					ORDER BY creation DESC
-				)
-			ORDER BY creation DESC
-		""", (employee, leave_type, date), as_dict=1)
+		carry_forwarded_leaves = get_remaining_leaves(employee, leave_type, date)
 
 	return carry_forwarded_leaves
 
+def get_remaining_leaves(employee, leave_type, date):
+	return frappe.db.get_value("Leave Ledger Entry", filters={
+			"to_date": ("<=", date),
+			"employee": employee,
+			"docstatus": 1,
+			"leave_type": leave_type,
+			}, fieldname=['SUM(leaves)'])
+
 def validate_carry_forward(leave_type):
 	if not frappe.db.get_value("Leave Type", leave_type, "is_carry_forward"):
 		frappe.throw(_("Leave Type {0} cannot be carry-forwarded").format(leave_type))
\ No newline at end of file