feat: set carry forwarded leave allocation
diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation.json b/erpnext/hr/doctype/leave_allocation/leave_allocation.json
index 431adfb..aa20338 100644
--- a/erpnext/hr/doctype/leave_allocation/leave_allocation.json
+++ b/erpnext/hr/doctype/leave_allocation/leave_allocation.json
@@ -325,7 +325,7 @@
    "bold": 1,
    "collapsible": 0,
    "columns": 0,
-   "depends_on": "eval: doc.is_carry_forward != 1",
+   "depends_on": "eval: doc.carry_forward != 1",
    "fetch_if_empty": 0,
    "fieldname": "new_leaves_allocated",
    "fieldtype": "Float",
@@ -358,73 +358,6 @@
    "bold": 0,
    "collapsible": 0,
    "columns": 0,
-   "depends_on": "eval: doc.is_carry_forward == 1",
-   "fetch_if_empty": 0,
-   "fieldname": "old_leaves_allocated",
-   "fieldtype": "Int",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
-   "label": "Old Leaves Allocated",
-   "length": 0,
-   "no_copy": 0,
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
-  },
-  {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "fetch_if_empty": 0,
-   "fieldname": "is_carry_forward",
-   "fieldtype": "Check",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
-   "label": "is carry forward",
-   "length": 0,
-   "no_copy": 0,
-   "permlevel": 0,
-   "precision": "",
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 0,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
-  },
-  {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
    "depends_on": "",
    "description": "",
    "fetch_if_empty": 0,
@@ -437,7 +370,7 @@
    "in_global_search": 0,
    "in_list_view": 0,
    "in_standard_filter": 0,
-   "label": "Add unused leaves from previous allocations",
+   "label": "Allocate unused leaves from previous allocations",
    "length": 0,
    "no_copy": 0,
    "permlevel": 0,
@@ -455,43 +388,11 @@
   {
    "allow_bulk_edit": 0,
    "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
-   "depends_on": "carry_forward",
-   "fetch_if_empty": 0,
-   "fieldname": "carry_forwarded_leaves",
-   "fieldtype": "Float",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
-   "in_list_view": 0,
-   "in_standard_filter": 0,
-   "label": "Unused leaves",
-   "length": 0,
-   "no_copy": 0,
-   "permlevel": 0,
-   "print_hide": 0,
-   "print_hide_if_no_value": 0,
-   "read_only": 1,
-   "remember_last_selected_value": 0,
-   "report_hide": 0,
-   "reqd": 0,
-   "search_index": 0,
-   "set_only_once": 0,
-   "translatable": 0,
-   "unique": 0
-  },
-  {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
    "allow_on_submit": 1,
    "bold": 0,
    "collapsible": 0,
    "columns": 0,
+   "depends_on": "",
    "fetch_if_empty": 0,
    "fieldname": "total_leaves_allocated",
    "fieldtype": "Float",
@@ -764,7 +665,7 @@
  "issingle": 0,
  "istable": 0,
  "max_attachments": 0,
- "modified": "2019-04-04 15:09:33.421008",
+ "modified": "2019-04-05 15:31:04.627015",
  "modified_by": "Administrator",
  "module": "HR",
  "name": "Leave Allocation",
diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation.py b/erpnext/hr/doctype/leave_allocation/leave_allocation.py
index e755da5..7ce3143 100755
--- a/erpnext/hr/doctype/leave_allocation/leave_allocation.py
+++ b/erpnext/hr/doctype/leave_allocation/leave_allocation.py
@@ -18,14 +18,19 @@
 class LeaveAllocation(Document):
 	def validate(self):
 		self.validate_period()
-		self.validate_new_leaves_allocated_value()
+		self.validate_lwp()
 		self.validate_allocation_overlap()
 		self.validate_back_dated_allocation()
+		if not self.carry_forward:
+			self.validate_new_leaves_allocated_value()
+		self.validate_leave_allocation_days()
 		self.set_total_leaves_allocated()
 		self.validate_total_leaves_allocated()
-		self.validate_lwp()
 		set_employee_name(self)
-		self.validate_leave_allocation_days()
+
+	def on_update(self):
+		if self.carry_forward:
+			self.set_carry_forward_leaves()
 
 	def validate_leave_allocation_days(self):
 		company = frappe.db.get_value("Employee", self.employee, "company")
@@ -44,7 +49,6 @@
 		self.validate_new_leaves_allocated_value()
 		self.set_total_leaves_allocated()
 
-		frappe.db.set(self,'carry_forwarded_leaves', flt(self.carry_forwarded_leaves))
 		frappe.db.set(self,'total_leaves_allocated',flt(self.total_leaves_allocated))
 
 		self.validate_against_leave_applications()
@@ -62,7 +66,7 @@
 		if flt(self.new_leaves_allocated) % 0.5:
 			frappe.throw(_("Leaves must be allocated in multiples of 0.5"), ValueMultiplierError)
 
-	def validate_allocation_overlap(self, carry_forward=0):
+	def validate_allocation_overlap(self):
 		leave_allocation = frappe.db.sql("""
 			SELECT
 				name
@@ -71,10 +75,10 @@
 				employee=%s
 				AND leave_type=%s
 				AND docstatus=1
-				AND is_carry_forward={0}
+				AND carry_forward={0}
 				AND to_date >= %s
-				AND from_date <= %s""".format(carry_forward),
-			(self.employee, self.leave_type, self.from_date, self.to_date))
+				AND from_date <= %s""" #nosec
+				.format(self.carry_forward), (self.employee, self.leave_type, self.from_date, self.to_date))
 
 		if leave_allocation:
 			frappe.msgprint(_("{0} already allocated for Employee {1} for period {2} to {3}")
@@ -94,12 +98,11 @@
 					BackDatedAllocationError)
 
 	def set_total_leaves_allocated(self):
-		self.carry_forwarded_leaves = get_carry_forwarded_leaves(self.employee,
-			self.leave_type, self.from_date, self.carry_forward)
 
-		self.total_leaves_allocated = flt(self.carry_forwarded_leaves) + flt(self.new_leaves_allocated)
+		self.total_leaves_allocated = flt(self.new_leaves_allocated)
 
-		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"):
+		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 validate_total_leaves_allocated(self):
@@ -119,20 +122,44 @@
 				frappe.throw(_("Total allocated leaves {0} cannot be less than already approved leaves {1} for the period").format(self.total_leaves_allocated, leaves_taken), LessAllocationError)
 
 	def set_carry_forward_leaves(self):
-		self.validate_allocation_overlap(carry_forward=1)
-		self.old_leaves_allocated = get_carry_forwarded_leaves(self.employee, self.leave_type,
-			self.from_date, self.is_carry_forward)
+
+		leaves_allocated
+		# check number of days to expire, ignore expiry for default value
+		expiry_days = frappe.db.get_value("Leave Type",
+			filters={"leave_type": leave_type, "is_carry_forward": 1},
+			fieldname="carry_forward_leave_expiry")
+
+		max_leaves_allowed = frappe.db.get_value("Leave Type", self.leave_type, "max_leaves_allowed")
+		leave_period = get_leave_period(self.from_date, self.to_date, company)
+		if leave_period:
+			leave_allocated = get_leave_allocation_for_period(self.employee, self.leave_type, leave_period[0].from_date, leave_period[0].to_date)
+
+		carry_forwarded_leaves = get_carry_forwarded_leaves(self.employee, self.leave_type,
+			self.from_date, expiry_days)
+		leaves_allocated += carry_forwarded_leaves
+
+		if leaves_allocated > max_leaves_allowed:
+			self.total_leaves_allocated = max_leaves_allowed - leaves_allocated
+		else:
+			self.total_leaves_allocated = carry_forwarded_leaves
 
 def get_leave_allocation_for_period(employee, leave_type, from_date, to_date):
 	leave_allocated = 0
 	leave_allocations = frappe.db.sql("""
-		select employee, leave_type, from_date, to_date, total_leaves_allocated
-		from `tabLeave Allocation`
-		where employee=%(employee)s and leave_type=%(leave_type)s
-			and docstatus=1
-			and (from_date between %(from_date)s and %(to_date)s
-				or to_date between %(from_date)s and %(to_date)s
-				or (from_date < %(from_date)s and to_date > %(to_date)s))
+		SELECT
+			employee,
+			leave_type,
+			from_date,
+			to_date,
+			total_leaves_allocated
+		FROM `tabLeave Allocation`
+		WHERE
+			employee=%(employee)s
+			AND leave_type=%(leave_type)s
+			AND docstatus=1
+			AND (from_date BETWEEN %(from_date)s AND %(to_date)s
+				OR to_date BETWEEN %(from_date)s AND %(to_date)s
+				OR (from_date < %(from_date)s AND to_date > %(to_date)s))
 	""", {
 		"from_date": from_date,
 		"to_date": to_date,
@@ -147,31 +174,32 @@
 	return leave_allocated
 
 @frappe.whitelist()
-def get_carry_forwarded_leaves(employee, leave_type, date, is_carry_forward=None):
+def get_carry_forwarded_leaves(employee, leave_type, date, expiry_days):
 	carry_forwarded_leaves = 0
 
-	if is_carry_forward:
-		validate_carry_forward(leave_type)
+	validate_carry_forward(leave_type)
+	filters = {
+		"employee": employee,
+		"leave_type": leave_type,
+		"docstatus": 1,
+		"to_date": ("<", date)
+	}
+	limit = 1
+	if expiry_days:
+		filters.update(carry_forward=0)
+		limit = 2
 
-		previous_allocation = frappe.db.sql("""
-			SELECT
-				name,
-				from_date,
-				to_date,
-				total_leaves_allocated
-			FROM `tabLeave Allocation`
-			WHERE
-				employee=%s
-				AND leave_type=%s
-				AND docstatus=1
-				AND to_date < %s
-			ORDER BY to_date desc limit 1
-		""", (employee, leave_type, date), as_dict=1)
-		if previous_allocation:
-			leaves_taken = get_approved_leaves_for_period(employee, leave_type,
-				previous_allocation[0].from_date, previous_allocation[0].to_date)
+	previous_allocation = frappe.get_all("Leave Allocation",
+		filters=filters,
+		fields=["name","from_date","to_date","total_leaves_allocated"],
+		order_by="to_date desc",
+		limit=limit)
 
-			carry_forwarded_leaves = flt(previous_allocation[0].total_leaves_allocated) - flt(leaves_taken)
+	if previous_allocation:
+		leaves_taken = get_approved_leaves_for_period(employee, leave_type,
+			previous_allocation[0].from_date, previous_allocation[0].to_date)
+
+		carry_forwarded_leaves = flt(previous_allocation[0].total_leaves_allocated) - flt(leaves_taken)
 
 	return carry_forwarded_leaves