Earned Leave not Allocated(last Month)

Hi Guys I am Sarah,
I am very new to ERPNext,
→ The issue is that, we have created Earned Leave Allocation for Annual Leave, which frequency is
set to monthly and that runs every month and all employees get 2.5 earned leaves per month.

→ But Last month on 30 November, it ran and assigned some employees and left most of them.

→ I cannot find the Pattern and i cannot find out that what has happened, looking into it since 4 days.

→ If anyone can provide assistance, it will be great.

1 Like

Hello @Sarah
Welcome to the community!

Sure! If I can get a look into it, I believe i can resolve it.

Hi Prashant,
Thank you so much for reply.
So, Below is the Native Code by Frappe.
It allocated the 2.5 Earned Leaves to every employee every month.
But at the last of November, it gave the earned leaves to some employees and not to some employees
I don’t know why, it did that.
and I am not able to find the pattern of the employees who got 2.5 earned leaves & those who did not.
I also cannot check one by one the emplooyees as there are 600+ employees.

def allocate_earned_leaves():
	"""Allocate earned leaves to Employees"""
	e_leave_types = get_earned_leaves()
	today = getdate()

	for e_leave_type in e_leave_types:

		leave_allocations = get_leave_allocations(today, e_leave_type.name)

		for allocation in leave_allocations:

			if not allocation.leave_policy_assignment and not allocation.leave_policy:
				continue

			leave_policy = (
				allocation.leave_policy
				if allocation.leave_policy
				else frappe.db.get_value(
					"Leave Policy Assignment", allocation.leave_policy_assignment, ["leave_policy"]
				)
			)

			annual_allocation = frappe.db.get_value(
				"Leave Policy Detail",
				filters={"parent": leave_policy, "leave_type": e_leave_type.name},
				fieldname=["annual_allocation"],
			)

			from_date = allocation.from_date

			if e_leave_type.based_on_date_of_joining:
				from_date = frappe.db.get_value("Employee", allocation.employee, "date_of_joining")

			if check_effective_date(
				from_date, today, e_leave_type.earned_leave_frequency, e_leave_type.based_on_date_of_joining
			):
				update_previous_leave_allocation(allocation, annual_allocation, e_leave_type)


def update_previous_leave_allocation(allocation, annual_allocation, e_leave_type):
	earned_leaves = get_monthly_earned_leave(
		annual_allocation, e_leave_type.earned_leave_frequency, e_leave_type.rounding
	)

	allocation = frappe.get_doc("Leave Allocation", allocation.name)
	new_allocation = flt(allocation.total_leaves_allocated) + flt(earned_leaves)

	if new_allocation > e_leave_type.max_leaves_allowed and e_leave_type.max_leaves_allowed > 0:
		new_allocation = e_leave_type.max_leaves_allowed

	if new_allocation != allocation.total_leaves_allocated:
		today_date = today()

		allocation.db_set("total_leaves_allocated", new_allocation, update_modified=False)
		create_additional_leave_ledger_entry(allocation, earned_leaves, today_date)

		if e_leave_type.based_on_date_of_joining:
			text = _("allocated {0} leave(s) via scheduler on {1} based on the date of joining").format(
				frappe.bold(earned_leaves), frappe.bold(formatdate(today_date))
			)
		else:
			text = _("allocated {0} leave(s) via scheduler on {1}").format(
				frappe.bold(earned_leaves), frappe.bold(formatdate(today_date))
			)

		allocation.add_comment(comment_type="Info", text=text)


def get_monthly_earned_leave(annual_leaves, frequency, rounding):
	earned_leaves = 0.0
	divide_by_frequency = {"Yearly": 1, "Half-Yearly": 6, "Quarterly": 4, "Monthly": 12}
	if annual_leaves:
		earned_leaves = flt(annual_leaves) / divide_by_frequency[frequency]
		if rounding:
			if rounding == "0.25":
				earned_leaves = round(earned_leaves * 4) / 4
			elif rounding == "0.5":
				earned_leaves = round(earned_leaves * 2) / 2
			else:
				earned_leaves = round(earned_leaves)

	return earned_leaves


def is_earned_leave_already_allocated(allocation, annual_allocation):
	from erpnext.hr.doctype.leave_policy_assignment.leave_policy_assignment import (
		get_leave_type_details,
	)

	leave_type_details = get_leave_type_details()
	date_of_joining = frappe.db.get_value("Employee", allocation.employee, "date_of_joining")

	assignment = frappe.get_doc("Leave Policy Assignment", allocation.leave_policy_assignment)
	leaves_for_passed_months = assignment.get_leaves_for_passed_months(
		allocation.leave_type, annual_allocation, leave_type_details, date_of_joining
	)

	# 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


def get_leave_allocations(date, leave_type):
	return frappe.db.sql(
		"""select name, employee, from_date, to_date, leave_policy_assignment, leave_policy
		from `tabLeave Allocation`
		where
			%s between from_date and to_date and docstatus=1
			and leave_type=%s""",
		(date, leave_type),
		as_dict=1,
	)


def get_earned_leaves():
	return frappe.get_all(
		"Leave Type",
		fields=[
			"name",
			"max_leaves_allowed",
			"earned_leave_frequency",
			"rounding",
			"based_on_date_of_joining",
		],
		filters={"is_earned_leave": 1},
	)


def create_additional_leave_ledger_entry(allocation, leaves, date):
	"""Create leave ledger entry for leave types"""
	allocation.new_leaves_allocated = leaves
	allocation.from_date = date
	allocation.unused_leaves = 0
	allocation.create_leave_ledger_entry()


def check_effective_date(from_date, to_date, frequency, based_on_date_of_joining):
	import calendar

	from dateutil import relativedelta

	from_date = get_datetime(from_date)
	to_date = get_datetime(to_date)
	rd = relativedelta.relativedelta(to_date, from_date)
	# last day of month
	last_day = calendar.monthrange(to_date.year, to_date.month)[1]

	if (from_date.day == to_date.day and based_on_date_of_joining) or (
		not based_on_date_of_joining and to_date.day == last_day
	):
		if frequency == "Monthly":
			return True
		elif frequency == "Quarterly" and rd.months % 3:
			return True
		elif frequency == "Half-Yearly" and rd.months % 6:
			return True
		elif frequency == "Yearly" and rd.months % 12:
			return True

	if frappe.flags.in_test:
		return True

	return False
1 Like

I understand, but to diagnose the issue, I may need to see the leave setup and assignments.

This below is the annual earned leave that is assigned (2.5) to the employees, every month