diff --git a/hrms/hr/doctype/leave_allocation/leave_allocation.js b/hrms/hr/doctype/leave_allocation/leave_allocation.js index ad97491b73..6ca85faa2d 100755 --- a/hrms/hr/doctype/leave_allocation/leave_allocation.js +++ b/hrms/hr/doctype/leave_allocation/leave_allocation.js @@ -43,12 +43,81 @@ frappe.ui.form.on("Leave Allocation", { 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 monthly_earned_leave = await frm.trigger("get_monthly_earned_leave"); + + frm.add_custom_button(__("Allocate Leaves Manually"), function () { + const dialog = new frappe.ui.Dialog({ + title: "Enter details", + 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(); + }); + }, + + get_monthly_earned_leave: async function (frm) { + const { + message: { date_of_joining }, + } = await frappe.db.get_value("Employee", frm.doc.employee, "date_of_joining"); + + const { + message: { annual_allocation }, + } = await frappe.db.get_value( + "Leave Policy Detail", + { + parent: frm.doc.leave_policy, + leave_type: frm.doc.leave_type, + }, + "annual_allocation", + () => {}, + "Leave Policy", + ); + + const { + message: { earned_leave_frequency, rounding }, + } = await frappe.db.get_value("Leave Type", frm.doc.leave_type, [ + "earned_leave_frequency", + "rounding", + ]); + + const { message } = await frappe.call({ + method: "hrms.hr.utils.get_monthly_earned_leave", + args: { + date_of_joining: date_of_joining, + annual_leaves: annual_allocation, + frequency: earned_leave_frequency, + rounding: rounding, + }, + }); + return message; + }, + expire_allocation: function (frm) { frappe.call({ method: "hrms.hr.doctype.leave_ledger_entry.leave_ledger_entry.expire_allocation", @@ -110,6 +179,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({ diff --git a/hrms/hr/doctype/leave_allocation/leave_allocation.py b/hrms/hr/doctype/leave_allocation/leave_allocation.py index 2631f691db..000d3b9ba5 100755 --- a/hrms/hr/doctype/leave_allocation/leave_allocation.py +++ b/hrms/hr/doctype/leave_allocation/leave_allocation.py @@ -12,7 +12,7 @@ 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 class OverlapError(frappe.ValidationError): @@ -312,6 +312,40 @@ 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) + def get_previous_allocation(from_date, leave_type, employee): """Returns document properties of previous allocation""" diff --git a/hrms/hr/utils.py b/hrms/hr/utils.py index aaf1080089..55c3f37676 100644 --- a/hrms/hr/utils.py +++ b/hrms/hr/utils.py @@ -412,6 +412,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,