Skip to content

Commit

Permalink
Merge pull request #2233 from frappe/mergify/bp/version-15-hotfix/pr-…
Browse files Browse the repository at this point in the history
…1950

feat(Earned Leave): allocate leaves manually (backport #1950)
  • Loading branch information
krantheman authored Sep 26, 2024
2 parents 1495f48 + eafe643 commit fa71cd4
Show file tree
Hide file tree
Showing 4 changed files with 146 additions and 8 deletions.
56 changes: 51 additions & 5 deletions hrms/hr/doctype/leave_allocation/leave_allocation.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,20 +35,65 @@ frappe.ui.form.on("Leave Allocation", {
);
if (valid_expiry) {
// expire current allocation
frm.add_custom_button(__("Expire Allocation"), function () {
frm.trigger("expire_allocation");
});
frm.add_custom_button(
__("Expire Allocation"),
function () {
frappe.confirm("Are you sure you want to expire this allocation?", () => {
frm.trigger("expire_allocation");
});
},
__("Actions"),
);
}
}

if (!frm.doc.__islocal && frm.doc.leave_policy_assignment) {
frappe.db.get_value("Leave Type", frm.doc.leave_type, "is_earned_leave", (r) => {
if (cint(r?.is_earned_leave))
frm.set_df_property("new_leaves_allocated", "read_only", 1);
if (!r?.is_earned_leave) return;
frm.set_df_property("new_leaves_allocated", "read_only", 1);
frm.trigger("add_allocate_leaves_button");
});
}
},

add_allocate_leaves_button: async function (frm) {
const { message: monthly_earned_leave } = await frappe.call({
method: "get_monthly_earned_leave",
doc: frm.doc,
});

frm.add_custom_button(
__("Allocate Leaves"),
function () {
const dialog = new frappe.ui.Dialog({
title: "Manual Leave Allocation",
fields: [
{
label: "New Leaves to be Allocated",
fieldname: "new_leaves",
fieldtype: "Float",
},
],
primary_action_label: "Allocate",
primary_action({ new_leaves }) {
frappe.call({
method: "allocate_leaves_manually",
doc: frm.doc,
args: { new_leaves },
callback: function () {
frm.reload_doc();
},
});
dialog.hide();
},
});
dialog.fields_dict.new_leaves.set_value(monthly_earned_leave);
dialog.show();
},
__("Actions"),
);
},

expire_allocation: function (frm) {
frappe.call({
method: "hrms.hr.doctype.leave_ledger_entry.leave_ledger_entry.expire_allocation",
Expand Down Expand Up @@ -110,6 +155,7 @@ frappe.ui.form.on("Leave Allocation", {
);
}
},

calculate_total_leaves_allocated: function (frm) {
if (cint(frm.doc.carry_forward) == 1 && frm.doc.leave_type && frm.doc.employee) {
return frappe.call({
Expand Down
69 changes: 68 additions & 1 deletion hrms/hr/doctype/leave_allocation/leave_allocation.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
create_leave_ledger_entry,
expire_allocation,
)
from hrms.hr.utils import get_leave_period, set_employee_name
from hrms.hr.utils import create_additional_leave_ledger_entry, get_leave_period, set_employee_name
from hrms.hr.utils import get_monthly_earned_leave as _get_monthly_earned_leave


class OverlapError(frappe.ValidationError):
Expand Down Expand Up @@ -312,6 +313,72 @@ def create_leave_ledger_entry(self, submit=True):
)
create_leave_ledger_entry(self, args, submit)

@frappe.whitelist()
def allocate_leaves_manually(self, new_leaves):
new_allocation = flt(self.total_leaves_allocated) + flt(new_leaves)
new_allocation_without_cf = flt(
flt(self.get_existing_leave_count()) + flt(new_leaves),
self.precision("total_leaves_allocated"),
)

max_leaves_allowed = frappe.db.get_value("Leave Type", self.leave_type, "max_leaves_allowed")
if new_allocation > max_leaves_allowed and max_leaves_allowed > 0:
new_allocation = max_leaves_allowed

annual_allocation = frappe.db.get_value(
"Leave Policy Detail",
{"parent": self.leave_policy, "leave_type": self.leave_type},
"annual_allocation",
)
annual_allocation = flt(annual_allocation, self.precision("total_leaves_allocated"))

if (
new_allocation != self.total_leaves_allocated
# annual allocation as per policy should not be exceeded
and new_allocation_without_cf <= annual_allocation
):
self.db_set("total_leaves_allocated", new_allocation, update_modified=False)

date = frappe.flags.current_date or getdate()
create_additional_leave_ledger_entry(self, new_leaves, date)

text = _("{0} leaves were manually allocated by {1} on {2}").format(
frappe.bold(new_leaves), frappe.session.user, frappe.bold(formatdate(date))
)
self.add_comment(comment_type="Info", text=text)

else:
msg = _("Total leaves allocated cannot exceed annual allocation of {0}.").format(
frappe.bold(_(annual_allocation))
)
msg += "<br><br>"
msg += _("Reference: {0}").format(get_link_to_form("Leave Policy", self.leave_policy))
frappe.throw(msg, title=_("Annual Allocation Exceeded"))

@frappe.whitelist()
def get_monthly_earned_leave(self):
doj = frappe.db.get_value("Employee", self.employee, "date_of_joining")

annual_allocation = frappe.db.get_value(
"Leave Policy Detail",
{
"parent": self.leave_policy,
"leave_type": self.leave_type,
},
"annual_allocation",
)

frequency, rounding = frappe.db.get_value(
"Leave Type",
self.leave_type,
[
"earned_leave_frequency",
"rounding",
],
)

return _get_monthly_earned_leave(doj, annual_allocation, frequency, rounding)


def get_previous_allocation(from_date, leave_type, employee):
"""Returns document properties of previous allocation"""
Expand Down
28 changes: 26 additions & 2 deletions hrms/hr/doctype/leave_allocation/test_earned_leaves.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
from frappe.utils import (
add_days,
add_months,
date_diff,
flt,
get_first_day,
get_last_day,
get_year_ending,
Expand Down Expand Up @@ -477,6 +475,32 @@ def test_get_earned_leave_details_for_dashboard(self):
}
self.assertEqual(leave_allocation, expected)

def test_allocate_leaves_manually(self):
frappe.flags.current_date = get_year_start(getdate())
lpas = make_policy_assignment(
self.employee,
allocate_on_day="First Day",
start_date=frappe.flags.current_date,
)

leave_allocation = frappe.get_last_doc(
"Leave Allocation", filters={"leave_policy_assignment": lpas[0]}
)
leave_allocation.allocate_leaves_manually(1)
leave_allocation.allocate_leaves_manually(1)
leave_allocation.allocate_leaves_manually(1)
leave_allocation.allocate_leaves_manually(1)
leave_allocation.allocate_leaves_manually(1)
self.assertEqual(
get_leave_balance_on(self.employee.name, self.leave_type, frappe.flags.current_date), 6
)

leave_allocation.allocate_leaves_manually(6)
self.assertEqual(
get_leave_balance_on(self.employee.name, self.leave_type, frappe.flags.current_date), 12
)
self.assertRaises(frappe.ValidationError, leave_allocation.allocate_leaves_manually, 1)

def tearDown(self):
frappe.db.set_value("Employee", self.employee.name, "date_of_joining", self.original_doj)
frappe.db.set_value("Leave Type", self.leave_type, "max_leaves_allowed", 0)
Expand Down
1 change: 1 addition & 0 deletions hrms/hr/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,7 @@ def update_previous_leave_allocation(allocation, annual_allocation, e_leave_type
allocation.add_comment(comment_type="Info", text=text)


@frappe.whitelist()
def get_monthly_earned_leave(
date_of_joining,
annual_leaves,
Expand Down

0 comments on commit fa71cd4

Please sign in to comment.