fix: remove all leaves via scheduler
This commit is contained in:
parent
5eac8703da
commit
8fb600162e
@ -35,8 +35,11 @@ frappe.ui.form.on("Leave Allocation", {
|
|||||||
|
|
||||||
expire_allocation: function(frm) {
|
expire_allocation: function(frm) {
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method: 'expire_current_allocation',
|
method: 'erpnext.hr.doctype.leave_ledger_entry.leave_ledger_entry.expire_allocation',
|
||||||
doc: frm.doc,
|
args: {
|
||||||
|
'allocation': frm.doc,
|
||||||
|
'expiry_date': frappe.datetime.get_today()
|
||||||
|
},
|
||||||
freeze: true,
|
freeze: true,
|
||||||
callback: function(r){
|
callback: function(r){
|
||||||
if(!r.exc){
|
if(!r.exc){
|
||||||
|
|||||||
@ -7,7 +7,7 @@ from frappe.utils import flt, date_diff, formatdate, add_days, today
|
|||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from erpnext.hr.utils import set_employee_name, get_leave_period
|
from erpnext.hr.utils import set_employee_name, get_leave_period
|
||||||
from erpnext.hr.doctype.leave_ledger_entry.leave_ledger_entry import create_leave_ledger_entry
|
from erpnext.hr.doctype.leave_ledger_entry.leave_ledger_entry import expire_allocation, create_leave_ledger_entry
|
||||||
|
|
||||||
class OverlapError(frappe.ValidationError): pass
|
class OverlapError(frappe.ValidationError): pass
|
||||||
class BackDatedAllocationError(frappe.ValidationError): pass
|
class BackDatedAllocationError(frappe.ValidationError): pass
|
||||||
@ -42,7 +42,11 @@ class LeaveAllocation(Document):
|
|||||||
|
|
||||||
def on_submit(self):
|
def on_submit(self):
|
||||||
self.create_leave_ledger_entry()
|
self.create_leave_ledger_entry()
|
||||||
self.expire_previous_allocation()
|
|
||||||
|
# expire all unused leaves in the ledger on creation of carry forward allocation
|
||||||
|
allocation = get_previous_allocation(self.from_date, self.leave_type, self.employee)
|
||||||
|
if self.carry_forward and allocation:
|
||||||
|
expire_allocation(allocation)
|
||||||
|
|
||||||
def on_cancel(self):
|
def on_cancel(self):
|
||||||
self.create_leave_ledger_entry(submit=False)
|
self.create_leave_ledger_entry(submit=False)
|
||||||
@ -128,55 +132,17 @@ class LeaveAllocation(Document):
|
|||||||
)
|
)
|
||||||
create_leave_ledger_entry(self, args, submit)
|
create_leave_ledger_entry(self, args, submit)
|
||||||
|
|
||||||
def expire_current_allocation(self):
|
def get_previous_allocation(from_date, leave_type, employee):
|
||||||
''' expires allocation '''
|
''' Returns document properties of previous allocation '''
|
||||||
date = self.to_date
|
|
||||||
leaves = get_unused_leaves(self.employee, self.leave_type, date)
|
|
||||||
ref_name = self.name
|
|
||||||
|
|
||||||
if leaves:
|
|
||||||
expiry_date = today()
|
|
||||||
args = dict(
|
|
||||||
leaves=flt(leaves) * -1,
|
|
||||||
transaction_name=ref_name,
|
|
||||||
from_date=expiry_date,
|
|
||||||
to_date=expiry_date,
|
|
||||||
is_carry_forward=0,
|
|
||||||
is_expired=1
|
|
||||||
)
|
|
||||||
create_leave_ledger_entry(self, args)
|
|
||||||
|
|
||||||
frappe.db.set_value("Leave Allocation", self.name, "expired", 1)
|
|
||||||
|
|
||||||
def expire_previous_allocation(self):
|
|
||||||
date = self.from_date
|
|
||||||
leaves = get_unused_leaves(self.employee, self.leave_type, date)
|
|
||||||
ref_name = self.get_previous_allocation()
|
|
||||||
|
|
||||||
if leaves:
|
|
||||||
expiry_date = add_days(self.from_date, -1)
|
|
||||||
args = dict(
|
|
||||||
leaves=flt(leaves) * -1,
|
|
||||||
transaction_name=ref_name,
|
|
||||||
from_date=expiry_date,
|
|
||||||
to_date=expiry_date,
|
|
||||||
is_carry_forward=0,
|
|
||||||
is_expired=1
|
|
||||||
)
|
|
||||||
create_leave_ledger_entry(self, args)
|
|
||||||
|
|
||||||
frappe.db.set_value("Leave Allocation", ref_name, "expired", 1)
|
|
||||||
|
|
||||||
def get_previous_allocation(self):
|
|
||||||
return frappe.db.get_value("Leave Allocation",
|
return frappe.db.get_value("Leave Allocation",
|
||||||
filters={
|
filters={
|
||||||
'to_date': ("<", self.from_date),
|
'to_date': ("<", from_date),
|
||||||
'leave_type': self.leave_type,
|
'leave_type': leave_type,
|
||||||
'employee': self.employee,
|
'employee': employee,
|
||||||
'docstatus': 1
|
'docstatus': 1
|
||||||
},
|
},
|
||||||
order_by='to_date DESC',
|
order_by='to_date DESC',
|
||||||
fieldname=['name'])
|
fieldname=['name', 'from_date', 'to_date'], 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
|
||||||
@ -203,21 +169,27 @@ 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):
|
||||||
carry_forwarded_leaves = 0
|
''' Returns carry forwarded leaves for the given employee '''
|
||||||
if carry_forward:
|
carry_forwarded_leaves = 0.0
|
||||||
|
previous_allocation = get_previous_allocation(date, leave_type, employee)
|
||||||
|
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, date)
|
carry_forwarded_leaves = get_unused_leaves(employee, leave_type, previous_allocation.from_date, previous_allocation.to_date)
|
||||||
|
|
||||||
return carry_forwarded_leaves
|
return carry_forwarded_leaves
|
||||||
|
|
||||||
def get_unused_leaves(employee, leave_type, date):
|
def get_unused_leaves(employee, leave_type, from_date, to_date):
|
||||||
return frappe.db.get_value("Leave Ledger Entry", filters={
|
''' Returns unused leaves between the given period while skipping leave allocation expiry '''
|
||||||
"to_date": ("<=", date),
|
leaves = frappe.get_all("Leave Ledger Entry", filters={
|
||||||
"employee": employee,
|
'employee': employee,
|
||||||
"docstatus": 1,
|
'leave_type': leave_type,
|
||||||
"leave_type": leave_type,
|
'from_date': ('>=', from_date),
|
||||||
"is_lwp": 0
|
'to_date': ('<=', to_date)
|
||||||
}, fieldname=['SUM(leaves)'])
|
}, or_filters={
|
||||||
|
'is_expired': 0,
|
||||||
|
'is_carry_forward': 1
|
||||||
|
}, fields=['sum(leaves) as leaves'])
|
||||||
|
return flt(leaves[0]['leaves'])
|
||||||
|
|
||||||
def validate_carry_forward(leave_type):
|
def validate_carry_forward(leave_type):
|
||||||
if not frappe.db.get_value("Leave Type", leave_type, "is_carry_forward"):
|
if not frappe.db.get_value("Leave Type", leave_type, "is_carry_forward"):
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import frappe
|
|||||||
import unittest
|
import unittest
|
||||||
from frappe.utils import nowdate, add_months, getdate, add_days
|
from frappe.utils import nowdate, add_months, getdate, add_days
|
||||||
from erpnext.hr.doctype.leave_type.test_leave_type import create_leave_type
|
from erpnext.hr.doctype.leave_type.test_leave_type import create_leave_type
|
||||||
from erpnext.hr.doctype.leave_ledger_entry.leave_ledger_entry import process_expired_allocation
|
from erpnext.hr.doctype.leave_ledger_entry.leave_ledger_entry import process_expired_allocation, expire_allocation
|
||||||
|
|
||||||
class TestLeaveAllocation(unittest.TestCase):
|
class TestLeaveAllocation(unittest.TestCase):
|
||||||
def test_overlapping_allocation(self):
|
def test_overlapping_allocation(self):
|
||||||
@ -108,6 +108,8 @@ class TestLeaveAllocation(unittest.TestCase):
|
|||||||
carry_forward=1)
|
carry_forward=1)
|
||||||
leave_allocation.submit()
|
leave_allocation.submit()
|
||||||
|
|
||||||
|
expire_allocation(leave_allocation)
|
||||||
|
|
||||||
leave_allocation = create_leave_allocation(
|
leave_allocation = create_leave_allocation(
|
||||||
leave_type="_Test_CF_leave_expiry",
|
leave_type="_Test_CF_leave_expiry",
|
||||||
from_date=add_days(nowdate(), -90),
|
from_date=add_days(nowdate(), -90),
|
||||||
|
|||||||
@ -198,7 +198,7 @@ class LeaveApplication(Document):
|
|||||||
if not is_lwp(self.leave_type):
|
if not is_lwp(self.leave_type):
|
||||||
self.leave_balance = get_leave_balance_on(self.employee, self.leave_type, self.from_date, self.to_date,
|
self.leave_balance = get_leave_balance_on(self.employee, self.leave_type, self.from_date, self.to_date,
|
||||||
consider_all_leaves_in_the_allocation_period=True)
|
consider_all_leaves_in_the_allocation_period=True)
|
||||||
if self.status != "Rejected" and self.leave_balance < self.total_leave_days:
|
if self.status != "Rejected" and (self.leave_balance < self.total_leave_days or not self.leave_balance):
|
||||||
if frappe.db.get_value("Leave Type", self.leave_type, "allow_negative"):
|
if frappe.db.get_value("Leave Type", self.leave_type, "allow_negative"):
|
||||||
frappe.msgprint(_("Note: There is not enough leave balance for Leave Type {0}")
|
frappe.msgprint(_("Note: There is not enough leave balance for Leave Type {0}")
|
||||||
.format(self.leave_type))
|
.format(self.leave_type))
|
||||||
|
|||||||
@ -6,29 +6,31 @@ from __future__ import unicode_literals
|
|||||||
import frappe
|
import frappe
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.utils import add_days, today, flt, DATE_FORMAT
|
from frappe.utils import add_days, today, flt, DATE_FORMAT, getdate
|
||||||
|
|
||||||
class LeaveLedgerEntry(Document):
|
class LeaveLedgerEntry(Document):
|
||||||
def validate(self):
|
def validate(self):
|
||||||
if self.from_date > self.to_date:
|
if getdate(self.from_date) > getdate(self.to_date):
|
||||||
frappe.throw(_("To date needs to be before from date"))
|
frappe.throw(_("To date needs to be before from date"))
|
||||||
|
|
||||||
def on_cancel(self):
|
def on_cancel(self):
|
||||||
# allow cancellation of expiry leaves
|
# allow cancellation of expiry leaves
|
||||||
if not self.is_expired:
|
if self.is_expired:
|
||||||
|
frappe.db.set_value("Leave Allocation", self.transaction_name, "expired", 0)
|
||||||
|
else:
|
||||||
frappe.throw(_("Only expired allocation can be cancelled"))
|
frappe.throw(_("Only expired allocation can be cancelled"))
|
||||||
|
|
||||||
def validate_leave_allocation_against_leave_application(ledger):
|
def validate_leave_allocation_against_leave_application(ledger):
|
||||||
''' Checks that leave allocation has no leave application against it '''
|
''' Checks that leave allocation has no leave application against it '''
|
||||||
leave_application_records = frappe.db.sql_list("""
|
leave_application_records = frappe.db.sql_list("""
|
||||||
SELECT transaction_name
|
SELECT transaction_name
|
||||||
FROM `tabLeave Application`
|
FROM `tabLeave Ledger Entry`
|
||||||
WHERE
|
WHERE
|
||||||
employee=%s,
|
employee=%s
|
||||||
leave_type=%s,
|
AND leave_type=%s
|
||||||
transaction_type='Leave Application',
|
AND transaction_type='Leave Application'
|
||||||
from_date>=%s,
|
AND from_date>=%s
|
||||||
to_date<=%s
|
AND to_date<=%s
|
||||||
""", (ledger.employee, ledger.leave_type, ledger.from_date, ledger.to_date))
|
""", (ledger.employee, ledger.leave_type, ledger.from_date, ledger.to_date))
|
||||||
|
|
||||||
if leave_application_records:
|
if leave_application_records:
|
||||||
@ -48,6 +50,7 @@ def create_leave_ledger_entry(ref_doc, args, submit=True):
|
|||||||
is_lwp=0
|
is_lwp=0
|
||||||
)
|
)
|
||||||
ledger.update(args)
|
ledger.update(args)
|
||||||
|
|
||||||
if submit:
|
if submit:
|
||||||
frappe.get_doc(ledger).submit()
|
frappe.get_doc(ledger).submit()
|
||||||
else:
|
else:
|
||||||
@ -92,39 +95,69 @@ def process_expired_allocation():
|
|||||||
if leave_type_records:
|
if leave_type_records:
|
||||||
leave_type = [record[0] for record in leave_type_records]
|
leave_type = [record[0] for record in leave_type_records]
|
||||||
expired_allocation = frappe.get_all("Leave Ledger Entry",
|
expired_allocation = frappe.get_all("Leave Ledger Entry",
|
||||||
|
fields=['leaves', 'to_date', 'employee', 'leave_type', 'is_carry_forward', 'transaction_name as name', 'transaction_type'],
|
||||||
filters={
|
filters={
|
||||||
'to_date': add_days(today(), -1),
|
'to_date': add_days(today(), -1),
|
||||||
'transaction_type': 'Leave Allocation',
|
'transaction_type': 'Leave Allocation',
|
||||||
'is_carry_forward': 1,
|
},
|
||||||
|
or_filters={
|
||||||
|
'is_carry_forward': 0,
|
||||||
'leave_type': ('in', leave_type)
|
'leave_type': ('in', leave_type)
|
||||||
}, fields=['leaves', 'to_date', 'employee', 'leave_type'])
|
})
|
||||||
|
|
||||||
if expired_allocation:
|
if expired_allocation:
|
||||||
create_expiry_ledger_entry(expired_allocation)
|
create_expiry_ledger_entry(expired_allocation)
|
||||||
|
|
||||||
def create_expiry_ledger_entry(expired_allocation):
|
def create_expiry_ledger_entry(expired_allocation):
|
||||||
''' Create expiry ledger entry for carry forwarded leaves '''
|
''' Create ledger entry for expired allocation '''
|
||||||
for allocation in expired_allocation:
|
for allocation in expired_allocation:
|
||||||
|
|
||||||
leaves_taken = get_leaves_taken(allocation)
|
if allocation.is_carry_forward:
|
||||||
leaves = flt(allocation.leaves) + flt(leaves_taken)
|
expire_carried_forward_allocation(allocation)
|
||||||
|
else:
|
||||||
|
expire_allocation(allocation)
|
||||||
|
|
||||||
if leaves > 0:
|
def get_remaining_leaves(allocation):
|
||||||
args = frappe._dict(
|
''' Returns remaining leaves from the given allocation '''
|
||||||
leaves=allocation.leaves * -1,
|
|
||||||
to_date=allocation.to_date,
|
|
||||||
is_carry_forward=1,
|
|
||||||
is_expired=1,
|
|
||||||
from_date=allocation.to_date
|
|
||||||
)
|
|
||||||
create_leave_ledger_entry(allocation, args)
|
|
||||||
|
|
||||||
def get_leaves_taken(allocation):
|
|
||||||
return frappe.db.get_value("Leave Ledger Entry",
|
return frappe.db.get_value("Leave Ledger Entry",
|
||||||
filters={
|
filters={
|
||||||
'employee': allocation.employee,
|
'employee': allocation.employee,
|
||||||
'leave_type': allocation.leave_type,
|
'leave_type': allocation.leave_type,
|
||||||
'from_date': ('>=', allocation.from_date),
|
|
||||||
'to_date': ('<=', allocation.to_date),
|
'to_date': ('<=', allocation.to_date),
|
||||||
'transaction_type': 'Leave application'
|
|
||||||
}, fieldname=['SUM(leaves)'])
|
}, fieldname=['SUM(leaves)'])
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def expire_allocation(allocation, expiry_date=None):
|
||||||
|
''' expires allocation '''
|
||||||
|
leaves = get_remaining_leaves(allocation)
|
||||||
|
expiry_date = expiry_date if expiry_date else allocation.to_date
|
||||||
|
|
||||||
|
if leaves:
|
||||||
|
args = dict(
|
||||||
|
leaves=flt(leaves) * -1,
|
||||||
|
transaction_name=allocation.name,
|
||||||
|
from_date=expiry_date,
|
||||||
|
to_date=expiry_date,
|
||||||
|
is_carry_forward=0,
|
||||||
|
is_expired=1
|
||||||
|
)
|
||||||
|
create_leave_ledger_entry(allocation, args)
|
||||||
|
|
||||||
|
frappe.db.set_value("Leave Allocation", allocation.name, "expired", 1)
|
||||||
|
|
||||||
|
def expire_carried_forward_allocation(allocation):
|
||||||
|
''' Expires remaining leaves in the on carried forward allocation '''
|
||||||
|
from erpnext.hr.doctype.leave_application.leave_application import get_leaves_for_period
|
||||||
|
leaves_taken = get_leaves_for_period(allocation.employee, allocation.leave_type, allocation.from_date, allocation.to_date)
|
||||||
|
leaves = flt(allocation.leaves) + flt(leaves_taken)
|
||||||
|
if leaves > 0:
|
||||||
|
args = frappe._dict(
|
||||||
|
transaction_name=allocation.name,
|
||||||
|
transaction_type="Leave Allocation",
|
||||||
|
leaves=allocation.leaves * -1,
|
||||||
|
is_carry_forward=allocation.is_carry_forward,
|
||||||
|
is_expired=1,
|
||||||
|
from_date=allocation.to_date,
|
||||||
|
to_date=allocation.to_date
|
||||||
|
)
|
||||||
|
create_leave_ledger_entry(allocation, args)
|
||||||
@ -48,13 +48,14 @@ def generate_encashment_leave_ledger_entries():
|
|||||||
|
|
||||||
def generate_expiry_allocation_ledger_entries():
|
def generate_expiry_allocation_ledger_entries():
|
||||||
''' fix ledger entries for missing leave allocation transaction '''
|
''' fix ledger entries for missing leave allocation transaction '''
|
||||||
|
from erpnext.hr.doctype.leave_ledger_entry.leave_ledger_entry import expire_allocation
|
||||||
allocation_list = get_allocation_records()
|
allocation_list = get_allocation_records()
|
||||||
|
|
||||||
for allocation in allocation_list:
|
for allocation in allocation_list:
|
||||||
if not frappe.db.exists("Leave Ledger Entry", {'transaction_type': 'Leave Allocation', 'transaction_name': allocation.name, 'is_expired': 1}):
|
if not frappe.db.exists("Leave Ledger Entry", {'transaction_type': 'Leave Allocation', 'transaction_name': allocation.name, 'is_expired': 1}):
|
||||||
allocation.update(dict(doctype="Leave Allocation"))
|
allocation.update(dict(doctype="Leave Allocation"))
|
||||||
allocation_obj = frappe.get_doc(allocation)
|
allocation_obj = frappe.get_doc(allocation)
|
||||||
allocation_obj.expire_previous_allocation()
|
expire_allocation(allocation_obj)
|
||||||
|
|
||||||
def get_allocation_records():
|
def get_allocation_records():
|
||||||
return frappe.db.sql("""
|
return frappe.db.sql("""
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user