Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Calculate earned leaves pro-rata if policy assignment is created after the leave period (backport #2704) #2752

Merged
merged 4 commits into from
Feb 4, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions hrms/hr/doctype/leave_allocation/test_earned_leaves.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,8 +137,14 @@ def test_alloc_based_on_joining_date(self):

# assignment created on the last day of the current month
frappe.flags.current_date = get_last_day(getdate())

leave_policy_assignments = make_policy_assignment(self.employee, assignment_based_on="Joining Date")
"""set end date while making assignment based on Joining date because while start date is fetched from
employee master, make_policy_assignment ends up taking current date as end date if not specified which
causes the date of assignment to be later than the end date of leave period"""
start_date = self.employee.date_of_joining
end_date = get_last_day(add_months(self.employee.date_of_joining, 12))
leave_policy_assignments = make_policy_assignment(
self.employee, assignment_based_on="Joining Date", start_date=start_date, end_date=end_date
)
leaves_allocated = get_allocated_leaves(leave_policy_assignments[0])
effective_from = frappe.db.get_value(
"Leave Policy Assignment", leave_policy_assignments[0], "effective_from"
Expand Down Expand Up @@ -581,6 +587,8 @@ def make_policy_assignment(
"leave_policy": leave_policy.name,
"leave_period": leave_period.name,
"carry_forward": carry_forward,
"effective_from": start_date,
"effective_to": end_date,
}

leave_policy_assignments = create_assignment_for_multiple_employees([employee.name], frappe._dict(data))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ def set_dates(self):
)
elif self.assignment_based_on == "Joining Date":
self.effective_from = frappe.db.get_value("Employee", self.employee, "date_of_joining")
if not self.effective_to:
self.effective_to = get_last_day(add_months(self.effective_from, 12))

def validate_policy_assignment_overlap(self):
leave_policy_assignment = frappe.db.get_value(
Expand Down Expand Up @@ -134,12 +136,13 @@ def get_new_leaves(self, annual_allocation, leave_details, date_of_joining):
from frappe.model.meta import get_field_precision

precision = get_field_precision(frappe.get_meta("Leave Allocation").get_field("new_leaves_allocated"))

current_date = getdate(frappe.flags.current_date) or getdate()
# Earned Leaves and Compensatory Leaves are allocated by scheduler, initially allocate 0
if leave_details.is_compensatory:
new_leaves_allocated = 0
# if earned leave is being allcated after the effective period, then let them be calculated pro-rata

elif leave_details.is_earned_leave:
elif leave_details.is_earned_leave and current_date < getdate(self.effective_to):
new_leaves_allocated = self.get_leaves_for_passed_months(
annual_allocation, leave_details, date_of_joining
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

import frappe
from frappe.tests.utils import FrappeTestCase
from frappe.utils import add_months, get_first_day, get_year_ending, getdate
from frappe.utils import add_days, add_months, get_first_day, get_year_ending, get_year_start, getdate

from hrms.hr.doctype.leave_application.test_leave_application import get_employee, get_leave_period
from hrms.hr.doctype.leave_period.test_leave_period import create_leave_period
Expand Down Expand Up @@ -33,6 +33,9 @@ def setUp(self):
self.original_doj = employee.date_of_joining
self.employee = employee

def tearDown(self):
frappe.db.set_value("Employee", self.employee.name, "date_of_joining", self.original_doj)

def test_grant_leaves(self):
leave_period = get_leave_period()
leave_policy = create_leave_policy(annual_allocation=10)
Expand Down Expand Up @@ -208,5 +211,58 @@ def test_pro_rated_leave_allocation_for_custom_date_range(self):

self.assertGreater(new_leaves_allocated, 0)

def tearDown(self):
frappe.db.set_value("Employee", self.employee.name, "date_of_joining", self.original_doj)
def test_earned_leave_allocation_if_leave_policy_assignment_submitted_after_period(self):
year_start_date = get_year_start(getdate())
year_end_date = get_year_ending(getdate())
leave_period = create_leave_period(year_start_date, year_end_date)

# assignment 10 days after the leave period
frappe.flags.current_date = add_days(year_end_date, 10)
leave_type = create_leave_type(
leave_type_name="_Test Earned Leave", is_earned_leave=True, allocate_on_day="Last Day"
)
annual_earned_leaves = 10
leave_policy = create_leave_policy(leave_type=leave_type, annual_allocation=annual_earned_leaves)
leave_policy.submit()

data = {
"assignment_based_on": "Leave Period",
"leave_policy": leave_policy.name,
"leave_period": leave_period.name,
}
assignment = create_assignment(self.employee.name, frappe._dict(data))
assignment.submit()

earned_leave_allocation = frappe.get_value(
"Leave Allocation", {"leave_policy_assignment": assignment.name}, "new_leaves_allocated"
)
self.assertEqual(earned_leave_allocation, annual_earned_leaves)

def test_earned_leave_allocation_for_leave_period_spanning_two_years(self):
first_year_start_date = get_year_start(getdate())
second_year_end_date = get_year_ending(add_months(first_year_start_date, 12))
leave_period = create_leave_period(first_year_start_date, second_year_end_date)

# assignment during mid second year
frappe.flags.current_date = add_months(second_year_end_date, -6)
leave_type = create_leave_type(
leave_type_name="_Test Earned Leave", is_earned_leave=True, allocate_on_day="Last Day"
)
annual_earned_leaves = 24
leave_policy = create_leave_policy(leave_type=leave_type, annual_allocation=annual_earned_leaves)
leave_policy.submit()

data = {
"assignment_based_on": "Leave Period",
"leave_policy": leave_policy.name,
"leave_period": leave_period.name,
}
assignment = create_assignment(self.employee.name, frappe._dict(data))
assignment.submit()

earned_leave_allocation = frappe.get_value(
"Leave Allocation", {"leave_policy_assignment": assignment.name}, "new_leaves_allocated"
)
# months passed (18) are calculated correctly but total allocation of 36 exceeds 24 hence 24
# this upper cap is intentional, without that 36 leaves would be allocated correctly
self.assertEqual(earned_leave_allocation, 24)
Loading