feat: consider carry forwarded leaves on creation of encashment

This commit is contained in:
Mangesh-Khairnar 2019-08-08 17:06:15 +05:30
parent 314647572c
commit 5cbe6160ca
8 changed files with 69 additions and 37 deletions

View File

@ -63,14 +63,14 @@ frappe.ui.form.on("Leave Allocation", {
frm.trigger("calculate_total_leaves_allocated"); frm.trigger("calculate_total_leaves_allocated");
}, },
carry_forwarded_leaves: function(frm) { unused_leaves: function(frm) {
frm.set_value("total_leaves_allocated", frm.set_value("total_leaves_allocated",
flt(frm.doc.carry_forwarded_leaves) + flt(frm.doc.new_leaves_allocated)); flt(frm.doc.unused_leaves) + flt(frm.doc.new_leaves_allocated));
}, },
new_leaves_allocated: function(frm) { new_leaves_allocated: function(frm) {
frm.set_value("total_leaves_allocated", frm.set_value("total_leaves_allocated",
flt(frm.doc.carry_forwarded_leaves) + flt(frm.doc.new_leaves_allocated)); flt(frm.doc.unused_leaves) + flt(frm.doc.new_leaves_allocated));
}, },
leave_policy: function(frm) { leave_policy: function(frm) {
@ -93,7 +93,7 @@ frappe.ui.form.on("Leave Allocation", {
} }
}) })
} else if (cint(frm.doc.carry_forward) == 0) { } else if (cint(frm.doc.carry_forward) == 0) {
frm.set_value("carry_forwarded_leaves", 0); frm.set_value("unused_leaves", 0);
frm.set_value("total_leaves_allocated", flt(frm.doc.new_leaves_allocated)); frm.set_value("total_leaves_allocated", flt(frm.doc.new_leaves_allocated));
} }
} }

View File

@ -17,13 +17,14 @@
"section_break_6", "section_break_6",
"new_leaves_allocated", "new_leaves_allocated",
"carry_forward", "carry_forward",
"carry_forwarded_leaves", "unused_leaves",
"total_leaves_allocated", "total_leaves_allocated",
"total_leaves_encashed", "total_leaves_encashed",
"column_break_10", "column_break_10",
"compensatory_request", "compensatory_request",
"leave_period", "leave_period",
"leave_policy", "leave_policy",
"carry_forwarded_leaves_count",
"expired", "expired",
"amended_from", "amended_from",
"notes", "notes",
@ -119,7 +120,7 @@
}, },
{ {
"depends_on": "carry_forward", "depends_on": "carry_forward",
"fieldname": "carry_forwarded_leaves", "fieldname": "unused_leaves",
"fieldtype": "Float", "fieldtype": "Float",
"label": "Unused leaves", "label": "Unused leaves",
"read_only": 1 "read_only": 1
@ -167,6 +168,15 @@
"options": "Leave Policy", "options": "Leave Policy",
"read_only": 1 "read_only": 1
}, },
{
"default": "0",
"fieldname": "expired",
"fieldtype": "Check",
"hidden": 1,
"in_standard_filter": 1,
"label": "Expired",
"read_only": 1
},
{ {
"fieldname": "amended_from", "fieldname": "amended_from",
"fieldtype": "Link", "fieldtype": "Link",
@ -194,19 +204,17 @@
"width": "300px" "width": "300px"
}, },
{ {
"default": "0", "depends_on": "carry_forwarded_leaves_count",
"fieldname": "expired", "fieldname": "carry_forwarded_leaves_count",
"fieldtype": "Check", "fieldtype": "Float",
"hidden": 1, "label": "Carry Forwarded Leaves",
"in_standard_filter": 1,
"label": "Expired",
"read_only": 1 "read_only": 1
} }
], ],
"icon": "fa fa-ok", "icon": "fa fa-ok",
"idx": 1, "idx": 1,
"is_submittable": 1, "is_submittable": 1,
"modified": "2019-07-22 17:50:39.591195", "modified": "2019-08-08 15:08:42.440909",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "HR", "module": "HR",
"name": "Leave Allocation", "name": "Leave Allocation",

View File

@ -50,6 +50,8 @@ class LeaveAllocation(Document):
def on_cancel(self): def on_cancel(self):
self.create_leave_ledger_entry(submit=False) self.create_leave_ledger_entry(submit=False)
if self.carry_forward:
self.set_carry_forwarded_leaves_in_previous_allocation(on_cancel=True)
def validate_period(self): def validate_period(self):
if date_diff(self.to_date, self.from_date) <= 0: if date_diff(self.to_date, self.from_date) <= 0:
@ -89,24 +91,33 @@ class LeaveAllocation(Document):
BackDatedAllocationError) BackDatedAllocationError)
def set_total_leaves_allocated(self): def set_total_leaves_allocated(self):
self.carry_forwarded_leaves = get_carry_forwarded_leaves(self.employee, self.unused_leaves = get_carry_forwarded_leaves(self.employee,
self.leave_type, self.from_date, self.carry_forward) self.leave_type, self.from_date, self.carry_forward)
self.total_leaves_allocated = flt(self.carry_forwarded_leaves) + flt(self.new_leaves_allocated) self.total_leaves_allocated = flt(self.unused_leaves) + flt(self.new_leaves_allocated)
self.maintain_carry_forwarded_leaves()
if self.carry_forward:
self.maintain_carry_forwarded_leaves()
self.set_carry_forwarded_leaves_in_previous_allocation()
if not self.total_leaves_allocated and not frappe.db.get_value("Leave Type", self.leave_type, "is_earned_leave") and not frappe.db.get_value("Leave Type", self.leave_type, "is_compensatory"): if not self.total_leaves_allocated and not frappe.db.get_value("Leave Type", self.leave_type, "is_earned_leave") and not frappe.db.get_value("Leave Type", self.leave_type, "is_compensatory"):
frappe.throw(_("Total leaves allocated is mandatory for Leave Type {0}".format(self.leave_type))) frappe.throw(_("Total leaves allocated is mandatory for Leave Type {0}").format(self.leave_type))
def maintain_carry_forwarded_leaves(self): def maintain_carry_forwarded_leaves(self):
''' reduce the carry forwarded leaves to be within the maximum allowed leaves ''' ''' Reduce the carry forwarded leaves to be within the maximum allowed leaves '''
if not self.carry_forward:
return
max_leaves_allowed = frappe.db.get_value("Leave Type", self.leave_type, "max_leaves_allowed") max_leaves_allowed = frappe.db.get_value("Leave Type", self.leave_type, "max_leaves_allowed")
if self.new_leaves_allocated <= max_leaves_allowed <= self.total_leaves_allocated: if self.new_leaves_allocated <= max_leaves_allowed <= self.total_leaves_allocated:
self.carry_forwarded_leaves = max_leaves_allowed - flt(self.new_leaves_allocated) self.unused_leaves = max_leaves_allowed - flt(self.new_leaves_allocated)
self.total_leaves_allocated = flt(max_leaves_allowed) self.total_leaves_allocated = flt(max_leaves_allowed)
def set_carry_forwarded_leaves_in_previous_allocation(self, on_cancel=False):
''' Set carry forwarded leaves in previous allocation '''
previous_allocation = get_previous_allocation(self.from_date, self.leave_type, self.employee)
if on_cancel:
self.unused_leaves = 0.0
frappe.db.set_value("Leave Allocation", previous_allocation.name, 'carry_forwarded_leaves_count', self.unused_leaves)
def validate_total_leaves_allocated(self): def validate_total_leaves_allocated(self):
# Adding a day to include To Date in the difference # Adding a day to include To Date in the difference
date_difference = date_diff(self.to_date, self.from_date) + 1 date_difference = date_diff(self.to_date, self.from_date) + 1
@ -114,10 +125,10 @@ class LeaveAllocation(Document):
frappe.throw(_("Total allocated leaves are more than days in the period"), OverAllocationError) frappe.throw(_("Total allocated leaves are more than days in the period"), OverAllocationError)
def create_leave_ledger_entry(self, submit=True): def create_leave_ledger_entry(self, submit=True):
if self.carry_forwarded_leaves: if self.unused_leaves:
expiry_days = frappe.db.get_value("Leave Type", self.leave_type, "expire_carried_forward_leaves") expiry_days = frappe.db.get_value("Leave Type", self.leave_type, "expire_carried_forward_leaves")
args = dict( args = dict(
leaves=self.carry_forwarded_leaves, leaves=self.unused_leaves,
from_date=self.from_date, from_date=self.from_date,
to_date=add_days(self.from_date, expiry_days - 1) if expiry_days else self.to_date, to_date=add_days(self.from_date, expiry_days - 1) if expiry_days else self.to_date,
is_carry_forward=1 is_carry_forward=1
@ -142,7 +153,7 @@ def get_previous_allocation(from_date, leave_type, employee):
'docstatus': 1 'docstatus': 1
}, },
order_by='to_date DESC', order_by='to_date DESC',
fieldname=['name', 'from_date', 'to_date'], as_dict=1) fieldname=['name', 'from_date', 'to_date', 'employee', 'leave_type'], as_dict=1)
def get_leave_allocation_for_period(employee, leave_type, from_date, to_date): def get_leave_allocation_for_period(employee, leave_type, from_date, to_date):
leave_allocated = 0 leave_allocated = 0
@ -170,13 +181,13 @@ def get_leave_allocation_for_period(employee, leave_type, from_date, to_date):
@frappe.whitelist() @frappe.whitelist()
def get_carry_forwarded_leaves(employee, leave_type, date, carry_forward=None): def get_carry_forwarded_leaves(employee, leave_type, date, carry_forward=None):
''' Returns carry forwarded leaves for the given employee ''' ''' Returns carry forwarded leaves for the given employee '''
carry_forwarded_leaves = 0.0 unused_leaves = 0.0
previous_allocation = get_previous_allocation(date, leave_type, employee) previous_allocation = get_previous_allocation(date, leave_type, employee)
if carry_forward and previous_allocation: if carry_forward and previous_allocation:
validate_carry_forward(leave_type) validate_carry_forward(leave_type)
carry_forwarded_leaves = get_unused_leaves(employee, leave_type, previous_allocation.from_date, previous_allocation.to_date) unused_leaves = get_unused_leaves(employee, leave_type, previous_allocation.from_date, previous_allocation.to_date)
return carry_forwarded_leaves return unused_leaves
def get_unused_leaves(employee, leave_type, from_date, to_date): def get_unused_leaves(employee, leave_type, from_date, to_date):
''' Returns unused leaves between the given period while skipping leave allocation expiry ''' ''' Returns unused leaves between the given period while skipping leave allocation expiry '''

View File

@ -64,7 +64,8 @@ class LeaveEncashment(Document):
allocation = self.get_leave_allocation() allocation = self.get_leave_allocation()
self.leave_balance = allocation.total_leaves_allocated - get_unused_leaves(self.employee, self.leave_type, allocation.from_date, self.encashment_date) self.leave_balance = allocation.total_leaves_allocated - allocation.carry_forwarded_leaves_count\
- get_unused_leaves(self.employee, self.leave_type, allocation.from_date, self.encashment_date)
encashable_days = self.leave_balance - frappe.db.get_value('Leave Type', self.leave_type, 'encashment_threshold_days') encashable_days = self.leave_balance - frappe.db.get_value('Leave Type', self.leave_type, 'encashment_threshold_days')
self.encashable_days = encashable_days if encashable_days > 0 else 0 self.encashable_days = encashable_days if encashable_days > 0 else 0
@ -76,9 +77,9 @@ class LeaveEncashment(Document):
return True return True
def get_leave_allocation(self): def get_leave_allocation(self):
leave_allocation = frappe.db.sql("""select name, to_date, total_leaves_allocated from `tabLeave Allocation` where '{0}' leave_allocation = frappe.db.sql("""select name, to_date, total_leaves_allocated, carry_forwarded_leaves_count from `tabLeave Allocation` where '{0}'
between from_date and to_date and docstatus=1 and leave_type='{1}' between from_date and to_date and docstatus=1 and leave_type='{1}'
and employee= '{2}'""".format(self.encashment_date or getdate(nowdate()), self.leave_type, self.employee), as_dict=1) and employee= '{2}'""".format(self.encashment_date or getdate(nowdate()), self.leave_type, self.employee), as_dict=1) #nosec
return leave_allocation[0] if leave_allocation else None return leave_allocation[0] if leave_allocation else None

View File

@ -104,7 +104,7 @@ def process_expired_allocation():
`transaction_type`='Leave Allocation' `transaction_type`='Leave Allocation'
AND `is_expired`=1""") AND `is_expired`=1""")
expire_allocation = frappe.get_all("Leave Ledger Entry", expire_allocation = frappe.get_all("Leave Ledger Entry",
fields=['leaves', 'to_date', 'employee', 'leave_type', 'is_carry_forward', 'transaction_name as name', 'transaction_type'], fields=['leaves', 'to_date', 'employee', 'leave_type', 'is_carry_forward', 'transaction_name as name', 'transaction_type'],
filters={ filters={
'to_date': ("<", today()), 'to_date': ("<", today()),
@ -119,9 +119,9 @@ def process_expired_allocation():
if expire_allocation: if expire_allocation:
create_expiry_ledger_entry(expire_allocation) create_expiry_ledger_entry(expire_allocation)
def create_expiry_ledger_entry(expire_allocation): def create_expiry_ledger_entry(allocations):
''' Create ledger entry for expired allocation ''' ''' Create ledger entry for expired allocation '''
for allocation in expire_allocation: for allocation in allocations:
if allocation.is_carry_forward: if allocation.is_carry_forward:
expire_carried_forward_allocation(allocation) expire_carried_forward_allocation(allocation)
else: else:
@ -138,7 +138,7 @@ def get_remaining_leaves(allocation):
@frappe.whitelist() @frappe.whitelist()
def expire_allocation(allocation, expiry_date=None): def expire_allocation(allocation, expiry_date=None):
''' expires allocation ''' ''' expires non-carry forwarded allocation '''
leaves = get_remaining_leaves(allocation) leaves = get_remaining_leaves(allocation)
expiry_date = expiry_date if expiry_date else allocation.to_date expiry_date = expiry_date if expiry_date else allocation.to_date
@ -146,6 +146,7 @@ def expire_allocation(allocation, expiry_date=None):
args = dict( args = dict(
leaves=flt(leaves) * -1, leaves=flt(leaves) * -1,
transaction_name=allocation.name, transaction_name=allocation.name,
transaction_type='Leave Allocation',
from_date=expiry_date, from_date=expiry_date,
to_date=expiry_date, to_date=expiry_date,
is_carry_forward=0, is_carry_forward=0,

View File

@ -22,7 +22,7 @@ class LeavePeriod(Document):
condition_str = " and " + " and ".join(conditions) if len(conditions) else "" condition_str = " and " + " and ".join(conditions) if len(conditions) else ""
employees = frappe._dict(frappe.db.sql("select name, date_of_joining from tabEmployee where status='Active' {condition}" employees = frappe._dict(frappe.db.sql("select name, date_of_joining from tabEmployee where status='Active' {condition}" #nosec
.format(condition=condition_str), tuple(values))) .format(condition=condition_str), tuple(values)))
return employees return employees

View File

@ -318,7 +318,7 @@ def create_earned_leave_ledger_entry(allocation, earned_leaves, date):
''' Create leave ledger entry based on the earned leave frequency ''' ''' Create leave ledger entry based on the earned leave frequency '''
allocation.new_leaves_allocated = earned_leaves allocation.new_leaves_allocated = earned_leaves
allocation.from_date = date allocation.from_date = date
allocation.carry_forwarded_leaves = 0 allocation.unused_leaves = 0
allocation.create_leave_ledger_entry() allocation.create_leave_ledger_entry()
def check_frequency_hit(from_date, to_date, frequency): def check_frequency_hit(from_date, to_date, frequency):

View File

@ -13,11 +13,22 @@ def execute():
if frappe.db.a_row_exists("Leave Ledger Entry"): if frappe.db.a_row_exists("Leave Ledger Entry"):
return return
if not frappe.get_meta("Leave Allocation").has_field("unused_leaves"):
frappe.reload_doc("HR", "doctype", "Leave Allocation")
update_leave_allocation_fieldname()
generate_allocation_ledger_entries() generate_allocation_ledger_entries()
generate_application_leave_ledger_entries() generate_application_leave_ledger_entries()
generate_encashment_leave_ledger_entries() generate_encashment_leave_ledger_entries()
generate_expiry_allocation_ledger_entries() generate_expiry_allocation_ledger_entries()
def update_leave_allocation_fieldname():
''' maps data from old field to the new field '''
frappe.db.sql("""
UPDATE `tabLeave Allocation`
SET `unused_leaves` = `carry_forwarded_leaves`
""")
def generate_allocation_ledger_entries(): def generate_allocation_ledger_entries():
''' fix ledger entries for missing leave allocation transaction ''' ''' fix ledger entries for missing leave allocation transaction '''
allocation_list = get_allocation_records() allocation_list = get_allocation_records()
@ -64,7 +75,7 @@ def get_allocation_records():
employee, employee,
leave_type, leave_type,
new_leaves_allocated, new_leaves_allocated,
carry_forwarded_leaves, unused_leaves,
from_date, from_date,
to_date, to_date,
carry_forward carry_forward