Browse Source
* feat: Leave Policy Assignment * feat: linking with leave allocation and valiations * style: removed old code from leave period * feat: Bulk Leave policy Assignment and grant Leaves * fix: overlap validation * feat: earned leaves based on joining date * feat: automatic grant leave based on leave policy * patch: create leave policy assignment based on employee current leave policy * fix: dependent test cases * test: Leave policy assignment * fix: some enhancement * style: break large function into small function * fix:requested Changes * fix(patch): Handled old Leave allocatioln * fix:codacy * fix: travis and sider,codacy * fix: codacy * fix: codacy * fix: requested changes and sider Co-authored-by: Nabin Hait <nabinhait@gmail.com>develop
Anurag Mishra
4 years ago
committed by
GitHub
21 changed files with 899 additions and 414 deletions
@ -1,167 +1,69 @@ |
|||
{ |
|||
"allow_copy": 0, |
|||
"allow_guest_to_view": 0, |
|||
"allow_import": 1, |
|||
"allow_rename": 1, |
|||
"autoname": "Prompt", |
|||
"beta": 0, |
|||
"creation": "2018-04-13 16:14:24.174138", |
|||
"custom": 0, |
|||
"docstatus": 0, |
|||
"doctype": "DocType", |
|||
"document_type": "", |
|||
"editable_grid": 1, |
|||
"engine": "InnoDB", |
|||
"actions": [], |
|||
"allow_import": 1, |
|||
"allow_rename": 1, |
|||
"autoname": "Prompt", |
|||
"creation": "2018-04-13 16:14:24.174138", |
|||
"doctype": "DocType", |
|||
"editable_grid": 1, |
|||
"engine": "InnoDB", |
|||
"field_order": [ |
|||
"default_salary_structure" |
|||
], |
|||
"fields": [ |
|||
{ |
|||
"allow_bulk_edit": 0, |
|||
"allow_in_quick_entry": 0, |
|||
"allow_on_submit": 0, |
|||
"bold": 0, |
|||
"collapsible": 0, |
|||
"columns": 0, |
|||
"fieldname": "default_leave_policy", |
|||
"fieldtype": "Link", |
|||
"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": "Default Leave Policy", |
|||
"length": 0, |
|||
"no_copy": 0, |
|||
"options": "Leave Policy", |
|||
"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, |
|||
"fieldname": "default_salary_structure", |
|||
"fieldtype": "Link", |
|||
"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": "Default Salary Structure", |
|||
"length": 0, |
|||
"no_copy": 0, |
|||
"options": "Salary Structure", |
|||
"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 |
|||
"options": "Salary Structure" |
|||
} |
|||
], |
|||
"has_web_view": 0, |
|||
"hide_heading": 0, |
|||
"hide_toolbar": 0, |
|||
"idx": 0, |
|||
"image_view": 0, |
|||
"in_create": 0, |
|||
"is_submittable": 0, |
|||
"issingle": 0, |
|||
"istable": 0, |
|||
"max_attachments": 0, |
|||
"modified": "2018-09-18 17:17:45.617624", |
|||
"index_web_pages_for_search": 1, |
|||
"links": [], |
|||
"modified": "2020-08-26 13:12:07.815330", |
|||
"modified_by": "Administrator", |
|||
"module": "HR", |
|||
"name": "Employee Grade", |
|||
"name_case": "", |
|||
"owner": "Administrator", |
|||
"permissions": [ |
|||
{ |
|||
"amend": 0, |
|||
"cancel": 0, |
|||
"create": 1, |
|||
"delete": 1, |
|||
"email": 1, |
|||
"export": 1, |
|||
"if_owner": 0, |
|||
"import": 0, |
|||
"permlevel": 0, |
|||
"print": 1, |
|||
"read": 1, |
|||
"report": 1, |
|||
"role": "System Manager", |
|||
"set_user_permissions": 0, |
|||
"share": 1, |
|||
"submit": 0, |
|||
"write": 1 |
|||
}, |
|||
{ |
|||
"amend": 0, |
|||
"cancel": 0, |
|||
"create": 1, |
|||
"delete": 1, |
|||
"email": 1, |
|||
"export": 1, |
|||
"if_owner": 0, |
|||
"import": 0, |
|||
"permlevel": 0, |
|||
"print": 1, |
|||
"read": 1, |
|||
"report": 1, |
|||
"role": "HR Manager", |
|||
"set_user_permissions": 0, |
|||
"share": 1, |
|||
"submit": 0, |
|||
"write": 1 |
|||
}, |
|||
{ |
|||
"amend": 0, |
|||
"cancel": 0, |
|||
"create": 1, |
|||
"delete": 1, |
|||
"email": 1, |
|||
"export": 1, |
|||
"if_owner": 0, |
|||
"import": 0, |
|||
"permlevel": 0, |
|||
"print": 1, |
|||
"read": 1, |
|||
"report": 1, |
|||
"role": "HR User", |
|||
"set_user_permissions": 0, |
|||
"share": 1, |
|||
"submit": 0, |
|||
"write": 1 |
|||
} |
|||
], |
|||
"quick_entry": 0, |
|||
"read_only": 0, |
|||
"read_only_onload": 0, |
|||
"show_name_in_global_search": 0, |
|||
"sort_field": "modified", |
|||
"sort_order": "DESC", |
|||
"track_changes": 1, |
|||
"track_seen": 0, |
|||
"track_views": 0 |
|||
"track_changes": 1 |
|||
} |
@ -0,0 +1,72 @@ |
|||
// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
|
|||
// For license information, please see license.txt
|
|||
|
|||
frappe.ui.form.on('Leave Policy Assignment', { |
|||
onload: function(frm) { |
|||
frm.ignore_doctypes_on_cancel_all = ["Leave Ledger Entry"]; |
|||
}, |
|||
|
|||
refresh: function(frm) { |
|||
if (frm.doc.docstatus === 1 && frm.doc.leaves_allocated === 0) { |
|||
frm.add_custom_button(__("Grant Leave"), function() { |
|||
|
|||
frappe.call({ |
|||
doc: frm.doc, |
|||
method: "grant_leave_alloc_for_employee", |
|||
callback: function(r) { |
|||
let leave_allocations = r.message; |
|||
let msg = frm.events.get_success_message(leave_allocations); |
|||
frappe.msgprint(msg); |
|||
cur_frm.refresh(); |
|||
} |
|||
}); |
|||
}); |
|||
} |
|||
}, |
|||
|
|||
get_success_message: function(leave_allocations) { |
|||
let msg = __("Leaves has been granted successfully"); |
|||
msg += "<br><table class='table table-bordered'>"; |
|||
msg += "<tr><th>"+__('Leave Type')+"</th><th>"+__("Leave Allocation")+"</th><th>"+__("Leaves Granted")+"</th><tr>"; |
|||
for (let key in leave_allocations) { |
|||
msg += "<tr><th>"+key+"</th><td>"+leave_allocations[key]["name"]+"</td><td>"+leave_allocations[key]["leaves"]+"</td></tr>"; |
|||
} |
|||
msg += "</table>"; |
|||
return msg; |
|||
}, |
|||
|
|||
assignment_based_on: function(frm) { |
|||
if (frm.doc.assignment_based_on) { |
|||
frm.events.set_effective_date(frm); |
|||
} else { |
|||
frm.set_value("effective_from", ''); |
|||
frm.set_value("effective_to", ''); |
|||
} |
|||
}, |
|||
|
|||
leave_period: function(frm) { |
|||
if (frm.doc.leave_period) { |
|||
frm.events.set_effective_date(frm); |
|||
} |
|||
}, |
|||
|
|||
set_effective_date: function(frm) { |
|||
if (frm.doc.assignment_based_on == "Leave Period" && frm.doc.leave_period) { |
|||
frappe.model.with_doc("Leave Period", frm.doc.leave_period, function () { |
|||
let from_date = frappe.model.get_value("Leave Period", frm.doc.leave_period, "from_date"); |
|||
let to_date = frappe.model.get_value("Leave Period", frm.doc.leave_period, "to_date"); |
|||
frm.set_value("effective_from", from_date); |
|||
frm.set_value("effective_to", to_date); |
|||
|
|||
}); |
|||
} else if (frm.doc.assignment_based_on == "Joining Date" && frm.doc.employee) { |
|||
frappe.model.with_doc("Employee", frm.doc.employee, function () { |
|||
let from_date = frappe.model.get_value("Employee", frm.doc.employee, "date_of_joining"); |
|||
frm.set_value("effective_from", from_date); |
|||
frm.set_value("effective_to", frappe.datetime.add_months(frm.doc.effective_from, 12)); |
|||
}); |
|||
} |
|||
frm.refresh(); |
|||
} |
|||
|
|||
}); |
@ -0,0 +1,160 @@ |
|||
{ |
|||
"actions": [], |
|||
"autoname": "HR-LPOL-ASSGN-.#####", |
|||
"creation": "2020-08-19 13:02:43.343666", |
|||
"doctype": "DocType", |
|||
"editable_grid": 1, |
|||
"engine": "InnoDB", |
|||
"field_order": [ |
|||
"employee", |
|||
"employee_name", |
|||
"company", |
|||
"leave_policy", |
|||
"carry_forward", |
|||
"column_break_5", |
|||
"assignment_based_on", |
|||
"leave_period", |
|||
"effective_from", |
|||
"effective_to", |
|||
"leaves_allocated", |
|||
"amended_from" |
|||
], |
|||
"fields": [ |
|||
{ |
|||
"fieldname": "employee", |
|||
"fieldtype": "Link", |
|||
"in_list_view": 1, |
|||
"in_standard_filter": 1, |
|||
"label": "Employee", |
|||
"options": "Employee", |
|||
"reqd": 1 |
|||
}, |
|||
{ |
|||
"fetch_from": "employee.employee_name", |
|||
"fieldname": "employee_name", |
|||
"fieldtype": "Data", |
|||
"label": "Employee name", |
|||
"read_only": 1 |
|||
}, |
|||
{ |
|||
"fieldname": "leave_policy", |
|||
"fieldtype": "Link", |
|||
"in_list_view": 1, |
|||
"in_standard_filter": 1, |
|||
"label": "Leave Policy", |
|||
"options": "Leave Policy", |
|||
"reqd": 1 |
|||
}, |
|||
{ |
|||
"fieldname": "assignment_based_on", |
|||
"fieldtype": "Select", |
|||
"label": "Assignment based on", |
|||
"options": "\nLeave Period\nJoining Date" |
|||
}, |
|||
{ |
|||
"depends_on": "eval:doc.assignment_based_on == \"Leave Period\"", |
|||
"fieldname": "leave_period", |
|||
"fieldtype": "Link", |
|||
"label": "Leave Period", |
|||
"mandatory_depends_on": "eval:doc.assignment_based_on == \"Leave Period\"", |
|||
"options": "Leave Period" |
|||
}, |
|||
{ |
|||
"fieldname": "effective_from", |
|||
"fieldtype": "Date", |
|||
"label": "Effective From", |
|||
"read_only_depends_on": "eval:doc.assignment_based_on", |
|||
"reqd": 1 |
|||
}, |
|||
{ |
|||
"fieldname": "effective_to", |
|||
"fieldtype": "Date", |
|||
"label": "Effective To", |
|||
"read_only_depends_on": "eval:doc.assignment_based_on == \"Leave Period\"", |
|||
"reqd": 1 |
|||
}, |
|||
{ |
|||
"fetch_from": "employee.company", |
|||
"fieldname": "company", |
|||
"fieldtype": "Link", |
|||
"in_standard_filter": 1, |
|||
"label": "Company", |
|||
"options": "Company", |
|||
"read_only": 1 |
|||
}, |
|||
{ |
|||
"fieldname": "column_break_5", |
|||
"fieldtype": "Column Break" |
|||
}, |
|||
{ |
|||
"fieldname": "amended_from", |
|||
"fieldtype": "Link", |
|||
"label": "Amended From", |
|||
"no_copy": 1, |
|||
"options": "Leave Policy Assignment", |
|||
"print_hide": 1, |
|||
"read_only": 1 |
|||
}, |
|||
{ |
|||
"default": "0", |
|||
"fieldname": "carry_forward", |
|||
"fieldtype": "Check", |
|||
"label": "Add unused leaves from previous allocations" |
|||
}, |
|||
{ |
|||
"default": "0", |
|||
"fieldname": "leaves_allocated", |
|||
"fieldtype": "Check", |
|||
"hidden": 1, |
|||
"label": "Leaves Allocated" |
|||
} |
|||
], |
|||
"is_submittable": 1, |
|||
"links": [], |
|||
"modified": "2020-10-15 15:18:15.227848", |
|||
"modified_by": "Administrator", |
|||
"module": "HR", |
|||
"name": "Leave Policy Assignment", |
|||
"owner": "Administrator", |
|||
"permissions": [ |
|||
{ |
|||
"create": 1, |
|||
"delete": 1, |
|||
"email": 1, |
|||
"export": 1, |
|||
"print": 1, |
|||
"read": 1, |
|||
"report": 1, |
|||
"role": "HR Manager", |
|||
"share": 1, |
|||
"write": 1 |
|||
}, |
|||
{ |
|||
"create": 1, |
|||
"delete": 1, |
|||
"email": 1, |
|||
"export": 1, |
|||
"print": 1, |
|||
"read": 1, |
|||
"report": 1, |
|||
"role": "HR User", |
|||
"share": 1, |
|||
"write": 1 |
|||
}, |
|||
{ |
|||
"create": 1, |
|||
"delete": 1, |
|||
"email": 1, |
|||
"export": 1, |
|||
"print": 1, |
|||
"read": 1, |
|||
"report": 1, |
|||
"role": "System Manager", |
|||
"share": 1, |
|||
"write": 1 |
|||
} |
|||
], |
|||
"sort_field": "modified", |
|||
"sort_order": "DESC", |
|||
"track_changes": 1 |
|||
} |
@ -0,0 +1,163 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors |
|||
# For license information, please see license.txt |
|||
|
|||
from __future__ import unicode_literals |
|||
import frappe |
|||
from frappe.model.document import Document |
|||
from frappe import _, bold |
|||
from frappe.utils import getdate, date_diff, comma_and, formatdate |
|||
from math import ceil |
|||
import json |
|||
from six import string_types |
|||
|
|||
class LeavePolicyAssignment(Document): |
|||
|
|||
def validate(self): |
|||
self.validate_policy_assignment_overlap() |
|||
self.set_dates() |
|||
|
|||
def set_dates(self): |
|||
if self.assignment_based_on == "Leave Period": |
|||
self.effective_from, self.effective_to = frappe.db.get_value("Leave Period", self.leave_period, ["from_date", "to_date"]) |
|||
elif self.assignment_based_on == "Joining Date": |
|||
self.effective_from = frappe.db.get_value("Employee", self.employee, "date_of_joining") |
|||
|
|||
def validate_policy_assignment_overlap(self): |
|||
leave_policy_assignments = frappe.get_all("Leave Policy Assignment", filters = { |
|||
"employee": self.employee, |
|||
"name": ("!=", self.name), |
|||
"docstatus": 1, |
|||
"effective_to": (">=", self.effective_from), |
|||
"effective_from": ("<=", self.effective_to) |
|||
}) |
|||
|
|||
if len(leave_policy_assignments): |
|||
frappe.throw(_("Leave Policy: {0} already assigned for Employee {1} for period {2} to {3}") |
|||
.format(bold(self.leave_policy), bold(self.employee), bold(formatdate(self.effective_from)), bold(formatdate(self.effective_to)))) |
|||
|
|||
def grant_leave_alloc_for_employee(self): |
|||
if self.leaves_allocated: |
|||
frappe.throw(_("Leave already have been assigned for this Leave Policy Assignment")) |
|||
else: |
|||
leave_allocations = {} |
|||
leave_type_details = get_leave_type_details() |
|||
|
|||
leave_policy = frappe.get_doc("Leave Policy", self.leave_policy) |
|||
date_of_joining = frappe.db.get_value("Employee", self.employee, "date_of_joining") |
|||
|
|||
for leave_policy_detail in leave_policy.leave_policy_details: |
|||
if not leave_type_details.get(leave_policy_detail.leave_type).is_lwp: |
|||
leave_allocation, new_leaves_allocated = self.create_leave_allocation( |
|||
leave_policy_detail.leave_type, leave_policy_detail.annual_allocation, |
|||
leave_type_details, date_of_joining |
|||
) |
|||
|
|||
leave_allocations[leave_policy_detail.leave_type] = {"name": leave_allocation, "leaves": new_leaves_allocated} |
|||
|
|||
self.db_set("leaves_allocated", 1) |
|||
return leave_allocations |
|||
|
|||
def create_leave_allocation(self, leave_type, new_leaves_allocated, leave_type_details, date_of_joining): |
|||
# Creates leave allocation for the given employee in the provided leave period |
|||
carry_forward = self.carry_forward |
|||
if self.carry_forward and not leave_type_details.get(leave_type).is_carry_forward: |
|||
carry_forward = 0 |
|||
|
|||
new_leaves_allocated = self.get_new_leaves(leave_type, new_leaves_allocated, |
|||
leave_type_details, date_of_joining) |
|||
|
|||
allocation = frappe.get_doc(dict( |
|||
doctype="Leave Allocation", |
|||
employee=self.employee, |
|||
leave_type=leave_type, |
|||
from_date=self.effective_from, |
|||
to_date=self.effective_to, |
|||
new_leaves_allocated=new_leaves_allocated, |
|||
leave_period=self.leave_period or None, |
|||
leave_policy_assignment = self.name, |
|||
leave_policy = self.leave_policy, |
|||
carry_forward=carry_forward |
|||
)) |
|||
allocation.save(ignore_permissions = True) |
|||
allocation.submit() |
|||
return allocation.name, new_leaves_allocated |
|||
|
|||
def get_new_leaves(self, leave_type, new_leaves_allocated, leave_type_details, date_of_joining): |
|||
# Calculate leaves at pro-rata basis for employees joining after the beginning of the given leave period |
|||
if getdate(date_of_joining) > getdate(self.effective_from): |
|||
remaining_period = ((date_diff(self.effective_to, date_of_joining) + 1) / (date_diff(self.effective_to, self.effective_from) + 1)) |
|||
new_leaves_allocated = ceil(new_leaves_allocated * remaining_period) |
|||
|
|||
# Earned Leaves and Compensatory Leaves are allocated by scheduler, initially allocate 0 |
|||
if leave_type_details.get(leave_type).is_earned_leave == 1 or leave_type_details.get(leave_type).is_compensatory == 1: |
|||
new_leaves_allocated = 0 |
|||
|
|||
return new_leaves_allocated |
|||
|
|||
@frappe.whitelist() |
|||
def grant_leave_for_multiple_employees(leave_policy_assignments): |
|||
leave_policy_assignments = json.loads(leave_policy_assignments) |
|||
not_granted = [] |
|||
for assignment in leave_policy_assignments: |
|||
try: |
|||
frappe.get_doc("Leave Policy Assignment", assignment).grant_leave_alloc_for_employee() |
|||
except Exception: |
|||
not_granted.append(assignment) |
|||
|
|||
if len(not_granted): |
|||
msg = _("Leave not Granted for Assignments:")+ bold(comma_and(not_granted)) + _(". Please Check documents") |
|||
else: |
|||
msg = _("Leave granted Successfully") |
|||
frappe.msgprint(msg) |
|||
|
|||
@frappe.whitelist() |
|||
def create_assignment_for_multiple_employees(employees, data): |
|||
|
|||
if isinstance(employees, string_types): |
|||
employees= json.loads(employees) |
|||
|
|||
if isinstance(data, string_types): |
|||
data = frappe._dict(json.loads(data)) |
|||
|
|||
docs_name = [] |
|||
for employee in employees: |
|||
assignment = frappe.new_doc("Leave Policy Assignment") |
|||
assignment.employee = employee |
|||
assignment.assignment_based_on = data.assignment_based_on or None |
|||
assignment.leave_policy = data.leave_policy |
|||
assignment.effective_from = getdate(data.effective_from) or None |
|||
assignment.effective_to = getdate(data.effective_to) or None |
|||
assignment.leave_period = data.leave_period or None |
|||
assignment.carry_forward = data.carry_forward |
|||
|
|||
assignment.save() |
|||
assignment.submit() |
|||
docs_name.append(assignment.name) |
|||
return docs_name |
|||
|
|||
|
|||
def automatically_allocate_leaves_based_on_leave_policy(): |
|||
today = getdate() |
|||
automatically_allocate_leaves_based_on_leave_policy = frappe.db.get_single_value( |
|||
'HR Settings', 'automatically_allocate_leaves_based_on_leave_policy' |
|||
) |
|||
|
|||
pending_assignments = frappe.get_list( |
|||
"Leave Policy Assignment", |
|||
filters = {"docstatus": 1, "leaves_allocated": 0, "effective_from": today} |
|||
) |
|||
|
|||
if len(pending_assignments) and automatically_allocate_leaves_based_on_leave_policy: |
|||
for assignment in pending_assignments: |
|||
frappe.get_doc("Leave Policy Assignment", assignment.name).grant_leave_alloc_for_employee() |
|||
|
|||
|
|||
def get_leave_type_details(): |
|||
leave_type_details = frappe._dict() |
|||
leave_types = frappe.get_all("Leave Type", |
|||
fields=["name", "is_lwp", "is_earned_leave", "is_compensatory", "is_carry_forward", "expire_carry_forwarded_leaves_after_days"]) |
|||
for d in leave_types: |
|||
leave_type_details.setdefault(d.name, d) |
|||
return leave_type_details |
|||
|
@ -0,0 +1,138 @@ |
|||
frappe.listview_settings['Leave Policy Assignment'] = { |
|||
onload: function (list_view) { |
|||
let me = this; |
|||
list_view.page.add_inner_button(__("Bulk Leave Policy Assignment"), function () { |
|||
me.dialog = new frappe.ui.form.MultiSelectDialog({ |
|||
doctype: "Employee", |
|||
target: cur_list, |
|||
setters: { |
|||
company: '', |
|||
department: '', |
|||
}, |
|||
data_fields: [{ |
|||
fieldname: 'leave_policy', |
|||
fieldtype: 'Link', |
|||
options: 'Leave Policy', |
|||
label: __('Leave Policy'), |
|||
reqd: 1 |
|||
}, |
|||
{ |
|||
fieldname: 'assignment_based_on', |
|||
fieldtype: 'Select', |
|||
options: ["", "Leave Period"], |
|||
label: __('Assignment Based On'), |
|||
onchange: () => { |
|||
if (cur_dialog.fields_dict.assignment_based_on.value === "Leave Period") { |
|||
cur_dialog.set_df_property("effective_from", "read_only", 1); |
|||
cur_dialog.set_df_property("leave_period", "reqd", 1); |
|||
cur_dialog.set_df_property("effective_to", "read_only", 1); |
|||
} else { |
|||
cur_dialog.set_df_property("effective_from", "read_only", 0); |
|||
cur_dialog.set_df_property("leave_period", "reqd", 0); |
|||
cur_dialog.set_df_property("effective_to", "read_only", 0); |
|||
cur_dialog.set_value("effective_from", ""); |
|||
cur_dialog.set_value("effective_to", ""); |
|||
} |
|||
} |
|||
}, |
|||
{ |
|||
fieldname: "leave_period", |
|||
fieldtype: 'Link', |
|||
options: "Leave Period", |
|||
label: __('Leave Period'), |
|||
depends_on: doc => { |
|||
return doc.assignment_based_on == 'Leave Period'; |
|||
}, |
|||
onchange: () => { |
|||
if (cur_dialog.fields_dict.leave_period.value) { |
|||
me.set_effective_date(); |
|||
} |
|||
} |
|||
}, |
|||
{ |
|||
fieldtype: "Column Break" |
|||
}, |
|||
{ |
|||
fieldname: 'effective_from', |
|||
fieldtype: 'Date', |
|||
label: __('Effective From'), |
|||
reqd: 1 |
|||
}, |
|||
{ |
|||
fieldname: 'effective_to', |
|||
fieldtype: 'Date', |
|||
label: __('Effective To'), |
|||
reqd: 1 |
|||
}, |
|||
{ |
|||
fieldname: 'carry_forward', |
|||
fieldtype: 'Check', |
|||
label: __('Add unused leaves from previous allocations') |
|||
} |
|||
], |
|||
get_query() { |
|||
return { |
|||
filters: { |
|||
status: ['=', 'Active'] |
|||
} |
|||
}; |
|||
}, |
|||
add_filters_group: 1, |
|||
primary_action_label: "Assign", |
|||
action(employees, data) { |
|||
frappe.call({ |
|||
method: 'erpnext.hr.doctype.leave_policy_assignment.leave_policy_assignment.create_assignment_for_multiple_employees', |
|||
async: false, |
|||
args: { |
|||
employees: employees, |
|||
data: data |
|||
} |
|||
}); |
|||
cur_dialog.hide(); |
|||
} |
|||
}); |
|||
}); |
|||
|
|||
list_view.page.add_inner_button(__("Grant Leaves"), function () { |
|||
me.dialog = new frappe.ui.form.MultiSelectDialog({ |
|||
doctype: "Leave Policy Assignment", |
|||
target: cur_list, |
|||
setters: { |
|||
company: '', |
|||
employee: '', |
|||
}, |
|||
get_query() { |
|||
return { |
|||
filters: { |
|||
docstatus: ['=', 1], |
|||
leaves_allocated: ['=', 0] |
|||
} |
|||
}; |
|||
}, |
|||
add_filters_group: 1, |
|||
primary_action_label: "Grant Leaves", |
|||
action(leave_policy_assignments) { |
|||
frappe.call({ |
|||
method: 'erpnext.hr.doctype.leave_policy_assignment.leave_policy_assignment.grant_leave_for_multiple_employees', |
|||
async: false, |
|||
args: { |
|||
leave_policy_assignments: leave_policy_assignments |
|||
} |
|||
}); |
|||
me.dialog.hide(); |
|||
} |
|||
}); |
|||
}); |
|||
}, |
|||
|
|||
set_effective_date: function () { |
|||
if (cur_dialog.fields_dict.assignment_based_on.value === "Leave Period" && cur_dialog.fields_dict.leave_period.value) { |
|||
frappe.model.with_doc("Leave Period", cur_dialog.fields_dict.leave_period.value, function () { |
|||
let from_date = frappe.model.get_value("Leave Period", cur_dialog.fields_dict.leave_period.value, "from_date"); |
|||
let to_date = frappe.model.get_value("Leave Period", cur_dialog.fields_dict.leave_period.value, "to_date"); |
|||
cur_dialog.set_value("effective_from", from_date); |
|||
cur_dialog.set_value("effective_to", to_date); |
|||
}); |
|||
} |
|||
} |
|||
}; |
@ -0,0 +1,103 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors |
|||
# See license.txt |
|||
from __future__ import unicode_literals |
|||
|
|||
import frappe |
|||
import unittest |
|||
from erpnext.hr.doctype.leave_application.test_leave_application import get_leave_period, get_employee |
|||
from erpnext.hr.doctype.leave_policy_assignment.leave_policy_assignment import create_assignment_for_multiple_employees |
|||
from erpnext.hr.doctype.leave_policy.test_leave_policy import create_leave_policy |
|||
|
|||
class TestLeavePolicyAssignment(unittest.TestCase): |
|||
|
|||
def setUp(self): |
|||
for doctype in ["Leave Application", "Leave Allocation", "Leave Policy Assignment", "Leave Ledger Entry"]: |
|||
frappe.db.sql("delete from `tab{0}`".format(doctype)) #nosec |
|||
|
|||
def test_grant_leaves(self): |
|||
leave_period = get_leave_period() |
|||
employee = get_employee() |
|||
|
|||
# create the leave policy with leave type "_Test Leave Type", allocation = 10 |
|||
leave_policy = create_leave_policy() |
|||
leave_policy.submit() |
|||
|
|||
|
|||
data = { |
|||
"assignment_based_on": "Leave Period", |
|||
"leave_policy": leave_policy.name, |
|||
"leave_period": leave_period.name |
|||
} |
|||
|
|||
leave_policy_assignments = create_assignment_for_multiple_employees([employee.name], frappe._dict(data)) |
|||
|
|||
leave_policy_assignment_doc = frappe.get_doc("Leave Policy Assignment", leave_policy_assignments[0]) |
|||
leave_policy_assignment_doc.grant_leave_alloc_for_employee() |
|||
leave_policy_assignment_doc.reload() |
|||
|
|||
self.assertEqual(leave_policy_assignment_doc.leaves_allocated, 1) |
|||
|
|||
leave_allocation = frappe.get_list("Leave Allocation", filters={ |
|||
"employee": employee.name, |
|||
"leave_policy":leave_policy.name, |
|||
"leave_policy_assignment": leave_policy_assignments[0], |
|||
"docstatus": 1})[0] |
|||
|
|||
leave_alloc_doc = frappe.get_doc("Leave Allocation", leave_allocation) |
|||
|
|||
self.assertEqual(leave_alloc_doc.new_leaves_allocated, 10) |
|||
self.assertEqual(leave_alloc_doc.leave_type, "_Test Leave Type") |
|||
self.assertEqual(leave_alloc_doc.from_date, leave_period.from_date) |
|||
self.assertEqual(leave_alloc_doc.to_date, leave_period.to_date) |
|||
self.assertEqual(leave_alloc_doc.leave_policy, leave_policy.name) |
|||
self.assertEqual(leave_alloc_doc.leave_policy_assignment, leave_policy_assignments[0]) |
|||
|
|||
def test_allow_to_grant_all_leave_after_cancellation_of_every_leave_allocation(self): |
|||
leave_period = get_leave_period() |
|||
employee = get_employee() |
|||
|
|||
# create the leave policy with leave type "_Test Leave Type", allocation = 10 |
|||
leave_policy = create_leave_policy() |
|||
leave_policy.submit() |
|||
|
|||
|
|||
data = { |
|||
"assignment_based_on": "Leave Period", |
|||
"leave_policy": leave_policy.name, |
|||
"leave_period": leave_period.name |
|||
} |
|||
|
|||
leave_policy_assignments = create_assignment_for_multiple_employees([employee.name], frappe._dict(data)) |
|||
|
|||
leave_policy_assignment_doc = frappe.get_doc("Leave Policy Assignment", leave_policy_assignments[0]) |
|||
leave_policy_assignment_doc.grant_leave_alloc_for_employee() |
|||
leave_policy_assignment_doc.reload() |
|||
|
|||
|
|||
# every leave is allocated no more leave can be granted now |
|||
self.assertEqual(leave_policy_assignment_doc.leaves_allocated, 1) |
|||
|
|||
leave_allocation = frappe.get_list("Leave Allocation", filters={ |
|||
"employee": employee.name, |
|||
"leave_policy":leave_policy.name, |
|||
"leave_policy_assignment": leave_policy_assignments[0], |
|||
"docstatus": 1})[0] |
|||
|
|||
leave_alloc_doc = frappe.get_doc("Leave Allocation", leave_allocation) |
|||
|
|||
# User all allowed to grant leave when there is no allocation against assignment |
|||
leave_alloc_doc.cancel() |
|||
leave_alloc_doc.delete() |
|||
|
|||
leave_policy_assignment_doc.reload() |
|||
|
|||
|
|||
# User are now allowed to grant leave |
|||
self.assertEqual(leave_policy_assignment_doc.leaves_allocated, 0) |
|||
|
|||
def tearDown(self): |
|||
for doctype in ["Leave Application", "Leave Allocation", "Leave Policy Assignment", "Leave Ledger Entry"]: |
|||
frappe.db.sql("delete from `tab{0}`".format(doctype)) #nosec |
|||
|
|||
|
@ -0,0 +1,77 @@ |
|||
# Copyright (c) 2019, Frappe and Contributors |
|||
# License: GNU General Public License v3. See license.txt |
|||
|
|||
from __future__ import unicode_literals |
|||
|
|||
import frappe |
|||
|
|||
def execute(): |
|||
if "leave_policy" in frappe.db.get_table_columns("Employee"): |
|||
employees_with_leave_policy = frappe.db.sql("SELECT name, leave_policy FROM `tabEmployee` WHERE leave_policy IS NOT NULL and leave_policy != ''", as_dict = 1) |
|||
|
|||
employee_with_assignment = [] |
|||
leave_policy =[] |
|||
|
|||
#for employee |
|||
|
|||
for employee in employees_with_leave_policy: |
|||
alloc = frappe.db.exists("Leave Allocation", {"employee":employee.name, "leave_policy": employee.leave_policy, "docstatus": 1}) |
|||
if not alloc: |
|||
create_assignment(employee.name, employee.leave_policy) |
|||
|
|||
employee_with_assignment.append(employee.name) |
|||
leave_policy.append(employee.leave_policy) |
|||
|
|||
|
|||
if "default_leave_policy" in frappe.db.get_table_columns("Employee"): |
|||
employee_grade_with_leave_policy = frappe.db.sql("SELECT name, default_leave_policy FROM `tabEmployee Grade` WHERE default_leave_policy IS NOT NULL and default_leave_policy!=''", as_dict = 1) |
|||
|
|||
#for whole employee Grade |
|||
|
|||
for grade in employee_grade_with_leave_policy: |
|||
employees = get_employee_with_grade(grade.name) |
|||
for employee in employees: |
|||
|
|||
if employee not in employee_with_assignment: #Will ensure no duplicate |
|||
alloc = frappe.db.exists("Leave Allocation", {"employee":employee.name, "leave_policy": grade.default_leave_policy, "docstatus": 1}) |
|||
if not alloc: |
|||
create_assignment(employee.name, grade.default_leave_policy) |
|||
leave_policy.append(grade.default_leave_policy) |
|||
|
|||
#for old Leave allocation and leave policy from allocation, which may got updated in employee grade. |
|||
leave_allocations = frappe.db.sql("SELECT leave_policy, leave_period, employee FROM `tabLeave Allocation` WHERE leave_policy IS NOT NULL and leave_policy != '' and docstatus = 1 ", as_dict = 1) |
|||
|
|||
for allocation in leave_allocations: |
|||
if allocation.leave_policy not in leave_policy: |
|||
create_assignment(allocation.employee, allocation.leave_policy, leave_period=allocation.leave_period, |
|||
allocation_exists=True) |
|||
|
|||
def create_assignment(employee, leave_policy, leave_period=None, allocation_exists = False): |
|||
|
|||
filters = {"employee":employee, "leave_policy": leave_policy} |
|||
if leave_period: |
|||
filters["leave_period"] = leave_period |
|||
|
|||
if not frappe.db.exists("Leave Policy Assignment" , filters): |
|||
lpa = frappe.new_doc("Leave Policy Assignment") |
|||
lpa.employee = employee |
|||
lpa.leave_policy = leave_policy |
|||
|
|||
lpa.flags.ignore_mandatory = True |
|||
if allocation_exists: |
|||
lpa.assignment_based_on = 'Leave Period' |
|||
lpa.leave_period = leave_period |
|||
lpa.leaves_allocated = 1 |
|||
|
|||
lpa.save() |
|||
if allocation_exists: |
|||
lpa.submit() |
|||
#Updating old Leave Allocation |
|||
frappe.db.sql("Update `tabLeave Allocation` set leave_policy_assignment = %s", lpa.name) |
|||
|
|||
|
|||
def get_employee_with_grade(grade): |
|||
return frappe.get_list("Employee", filters = {"grade": grade}) |
|||
|
|||
|
|||
|
Loading…
Reference in new issue