Merge pull request #12587 from shreyashah115/leave-workflow
Refactor Leave Application
This commit is contained in:
commit
8df0556af5
@ -192,7 +192,7 @@ def mark_attendance():
|
||||
"attendance_date": attendance_date
|
||||
})
|
||||
leave = frappe.db.sql("""select name from `tabLeave Application`
|
||||
where employee = %s and %s between from_date and to_date and status = 'Approved'
|
||||
where employee = %s and %s between from_date and to_date and workflow_state = 'Approved'
|
||||
and docstatus = 1""", (employee.name, attendance_date))
|
||||
|
||||
if leave:
|
||||
|
@ -21,7 +21,7 @@ class Attendance(Document):
|
||||
|
||||
def check_leave_record(self):
|
||||
leave_record = frappe.db.sql("""select leave_type, half_day from `tabLeave Application`
|
||||
where employee = %s and %s between from_date and to_date and status = 'Approved'
|
||||
where employee = %s and %s between from_date and to_date and workflow_state = 'Approved'
|
||||
and docstatus = 1""", (self.employee, self.attendance_date), as_dict=True)
|
||||
if leave_record:
|
||||
if leave_record[0].half_day:
|
||||
|
@ -29,7 +29,7 @@ frappe.ui.form.on("Leave Application", {
|
||||
|
||||
refresh: function(frm) {
|
||||
if (frm.is_new()) {
|
||||
frm.set_value("status", "Open");
|
||||
frm.set_value("workflow_state", "Open");
|
||||
frm.trigger("calculate_total_days");
|
||||
}
|
||||
},
|
||||
|
@ -44,66 +44,6 @@
|
||||
"set_only_once": 1,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "Open",
|
||||
"fieldname": "status",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 1,
|
||||
"label": "Status",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"options": "Open\nApproved\nRejected",
|
||||
"permlevel": 1,
|
||||
"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,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "column_break_12",
|
||||
"fieldtype": "Column Break",
|
||||
"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,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"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,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
@ -796,7 +736,7 @@
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 3,
|
||||
"modified": "2017-06-13 14:28:52.426044",
|
||||
"modified": "2018-01-22 12:10:40.757274",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "Leave Application",
|
||||
@ -887,9 +827,9 @@
|
||||
{
|
||||
"amend": 1,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"cancel": 1,
|
||||
"create": 0,
|
||||
"delete": 0,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 0,
|
||||
"if_owner": 0,
|
||||
|
@ -21,9 +21,10 @@ class AttendanceAlreadyMarkedError(frappe.ValidationError): pass
|
||||
from frappe.model.document import Document
|
||||
class LeaveApplication(Document):
|
||||
def get_feed(self):
|
||||
return _("{0}: From {0} of type {1}").format(self.status, self.employee_name, self.leave_type)
|
||||
return _("{0}: From {0} of type {1}").format(self.workflow_state, self.employee_name, self.leave_type)
|
||||
|
||||
def validate(self):
|
||||
if self.get("__islocal"): self.workflow_state = 'Open'
|
||||
if not getattr(self, "__islocal", None) and frappe.db.exists(self.doctype, self.name):
|
||||
self.previous_doc = frappe.get_value(self.doctype, self.name, "leave_approver", as_dict=True)
|
||||
else:
|
||||
@ -43,18 +44,16 @@ class LeaveApplication(Document):
|
||||
|
||||
def on_update(self):
|
||||
if (not self.previous_doc and self.leave_approver) or (self.previous_doc and \
|
||||
self.status == "Open" and self.previous_doc.leave_approver != self.leave_approver):
|
||||
self.workflow_state == "Open" and self.previous_doc.leave_approver != self.leave_approver):
|
||||
# notify leave approver about creation
|
||||
self.notify_leave_approver()
|
||||
|
||||
def on_submit(self):
|
||||
if self.status == "Open":
|
||||
frappe.throw(_("Only Leave Applications with status 'Approved' and 'Rejected' can be submitted"))
|
||||
|
||||
self.validate_back_dated_application()
|
||||
|
||||
# notify leave applier about approval
|
||||
self.notify_employee(self.status)
|
||||
self.notify_employee(self.workflow_state)
|
||||
|
||||
def on_cancel(self):
|
||||
# notify leave applier about cancellation
|
||||
@ -71,10 +70,10 @@ class LeaveApplication(Document):
|
||||
frappe.throw(_("Half Day Date should be between From Date and To Date"))
|
||||
|
||||
if not is_lwp(self.leave_type):
|
||||
self.validate_dates_acorss_allocation()
|
||||
self.validate_dates_across_allocation()
|
||||
self.validate_back_dated_application()
|
||||
|
||||
def validate_dates_acorss_allocation(self):
|
||||
def validate_dates_across_allocation(self):
|
||||
def _get_leave_alloction_record(date):
|
||||
allocation = frappe.db.sql("""select name from `tabLeave Allocation`
|
||||
where employee=%s and leave_type=%s and docstatus=1
|
||||
@ -89,7 +88,7 @@ class LeaveApplication(Document):
|
||||
frappe.throw(_("Application period cannot be outside leave allocation period"))
|
||||
|
||||
elif allocation_based_on_from_date != allocation_based_on_to_date:
|
||||
frappe.throw(_("Application period cannot be across two alocation records"))
|
||||
frappe.throw(_("Application period cannot be across two allocation records"))
|
||||
|
||||
def validate_back_dated_application(self):
|
||||
future_allocation = frappe.db.sql("""select name, from_date from `tabLeave Allocation`
|
||||
@ -129,7 +128,7 @@ class LeaveApplication(Document):
|
||||
block_dates = get_applicable_block_dates(self.from_date, self.to_date,
|
||||
self.employee, self.company)
|
||||
|
||||
if block_dates and self.status == "Approved":
|
||||
if block_dates and self.workflow_state == "Approved":
|
||||
frappe.throw(_("You are not authorized to approve leaves on Block Dates"), LeaveDayBlockedError)
|
||||
|
||||
def validate_balance_leaves(self):
|
||||
@ -144,7 +143,7 @@ class LeaveApplication(Document):
|
||||
self.leave_balance = get_leave_balance_on(self.employee, self.leave_type, self.from_date,
|
||||
consider_all_leaves_in_the_allocation_period=True)
|
||||
|
||||
if self.status != "Rejected" and self.leave_balance < self.total_leave_days:
|
||||
if self.workflow_state != "Rejected" and self.leave_balance < self.total_leave_days:
|
||||
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}")
|
||||
.format(self.leave_type))
|
||||
@ -161,7 +160,7 @@ class LeaveApplication(Document):
|
||||
select
|
||||
name, leave_type, posting_date, from_date, to_date, total_leave_days, half_day_date
|
||||
from `tabLeave Application`
|
||||
where employee = %(employee)s and docstatus < 2 and status in ("Open", "Approved")
|
||||
where employee = %(employee)s and docstatus < 2 and workflow_state in ("Open", "Approved")
|
||||
and to_date >= %(from_date)s and from_date <= %(to_date)s
|
||||
and name != %(name)s""", {
|
||||
"employee": self.employee,
|
||||
@ -182,16 +181,16 @@ class LeaveApplication(Document):
|
||||
self.throw_overlap_error(d)
|
||||
|
||||
def throw_overlap_error(self, d):
|
||||
msg = _("Employee {0} has already applied for {1} between {2} and {3}").format(self.employee,
|
||||
msg = _("Employee {0} has already applied for {1} between {2} and {3} : ").format(self.employee,
|
||||
d['leave_type'], formatdate(d['from_date']), formatdate(d['to_date'])) \
|
||||
+ """ <br><b><a href="#Form/Leave Application/{0}">{0}</a></b>""".format(d["name"])
|
||||
+ """ <b><a href="#Form/Leave Application/{0}">{0}</a></b>""".format(d["name"])
|
||||
frappe.throw(msg, OverlapError)
|
||||
|
||||
def get_total_leaves_on_half_day(self):
|
||||
leave_count_on_half_day_date = frappe.db.sql("""select count(name) from `tabLeave Application`
|
||||
where employee = %(employee)s
|
||||
and docstatus < 2
|
||||
and status in ("Open", "Approved")
|
||||
and workflow_state in ("Open", "Approved")
|
||||
and half_day = 1
|
||||
and half_day_date = %(half_day_date)s
|
||||
and name != %(name)s""", {
|
||||
@ -232,7 +231,7 @@ class LeaveApplication(Document):
|
||||
frappe.throw(_("Attendance for employee {0} is already marked for this day").format(self.employee),
|
||||
AttendanceAlreadyMarkedError)
|
||||
|
||||
def notify_employee(self, status):
|
||||
def notify_employee(self, workflow_state):
|
||||
employee = frappe.get_doc("Employee", self.employee)
|
||||
if not employee.user_id:
|
||||
return
|
||||
@ -247,14 +246,14 @@ class LeaveApplication(Document):
|
||||
message += "Leave Type: {leave_type}".format(leave_type=self.leave_type)+"<br>"
|
||||
message += "From Date: {from_date}".format(from_date=self.from_date)+"<br>"
|
||||
message += "To Date: {to_date}".format(to_date=self.to_date)+"<br>"
|
||||
message += "Status: {status}".format(status=_(status))
|
||||
message += "Status: {workflow_state}".format(workflow_state=_(workflow_state))
|
||||
return message
|
||||
|
||||
self.notify({
|
||||
# for post in messages
|
||||
"message": _get_message(url=True),
|
||||
"message_to": employee.user_id,
|
||||
"subject": (_("Leave Application") + ": %s - %s") % (self.name, _(status))
|
||||
"subject": (_("Leave Application") + ": %s - %s") % (self.name, _(workflow_state))
|
||||
})
|
||||
|
||||
def notify_leave_approver(self):
|
||||
@ -372,7 +371,7 @@ def get_approved_leaves_for_period(employee, leave_type, from_date, to_date):
|
||||
select employee, leave_type, from_date, to_date, total_leave_days
|
||||
from `tabLeave Application`
|
||||
where employee=%(employee)s and leave_type=%(leave_type)s
|
||||
and status="Approved" and docstatus=1
|
||||
and workflow_state="Approved" and docstatus=1
|
||||
and (from_date between %(from_date)s and %(to_date)s
|
||||
or to_date between %(from_date)s and %(to_date)s
|
||||
or (from_date < %(from_date)s and to_date > %(to_date)s))
|
||||
@ -472,11 +471,11 @@ def add_department_leaves(events, start, end, employee, company):
|
||||
|
||||
def add_leaves(events, start, end, match_conditions=None):
|
||||
query = """select name, from_date, to_date, employee_name, half_day,
|
||||
status, employee, docstatus
|
||||
workflow_state, employee, docstatus
|
||||
from `tabLeave Application` where
|
||||
from_date <= %(end)s and to_date >= %(start)s <= to_date
|
||||
and docstatus < 2
|
||||
and status!="Rejected" """
|
||||
and workflow_state!="Rejected" """
|
||||
if match_conditions:
|
||||
query += match_conditions
|
||||
|
||||
@ -486,7 +485,7 @@ def add_leaves(events, start, end, match_conditions=None):
|
||||
"doctype": "Leave Application",
|
||||
"from_date": d.from_date,
|
||||
"to_date": d.to_date,
|
||||
"status": d.status,
|
||||
"workflow_state": d.workflow_state,
|
||||
"title": cstr(d.employee_name) + \
|
||||
(d.half_day and _(" (Half Day)") or ""),
|
||||
"docstatus": d.docstatus
|
||||
|
@ -7,7 +7,7 @@ frappe.views.calendar["Leave Application"] = {
|
||||
"end": "to_date",
|
||||
"id": "name",
|
||||
"title": "title",
|
||||
"status": "status",
|
||||
"workflow_state": "workflow_state",
|
||||
},
|
||||
options: {
|
||||
header: {
|
||||
|
@ -1,8 +1,7 @@
|
||||
frappe.listview_settings['Leave Application'] = {
|
||||
add_fields: ["status", "leave_type", "employee", "employee_name", "total_leave_days", "from_date", "to_date"],
|
||||
filters:[["status","!=", "Rejected"]],
|
||||
add_fields: ["workflow_state", "leave_type", "employee", "employee_name", "total_leave_days", "from_date", "to_date"],
|
||||
get_indicator: function(doc) {
|
||||
return [__(doc.status), frappe.utils.guess_colour(doc.status),
|
||||
"status,=," + doc.status];
|
||||
return [__(doc.workflow_state), frappe.utils.guess_colour(doc.workflow_state),
|
||||
"workflow_state,=," + doc.workflow_state];
|
||||
}
|
||||
};
|
||||
|
@ -20,29 +20,24 @@ QUnit.test("Test: Leave application [HR]", function (assert) {
|
||||
{follow_via_email: 0}
|
||||
]);
|
||||
},
|
||||
|
||||
() => frappe.timeout(1),
|
||||
// check calculated total leave days
|
||||
() => assert.ok(!cur_frm.doc.docstatus,
|
||||
"leave application not submitted with status as open"),
|
||||
() => cur_frm.set_value("status", "Approved"), // approve the application [as administrator]
|
||||
() => frappe.timeout(0.5),
|
||||
// save form
|
||||
() => cur_frm.save(),
|
||||
() => frappe.timeout(1),
|
||||
() => cur_frm.savesubmit(),
|
||||
() => frappe.timeout(1),
|
||||
() => frappe.click_button('Actions'),
|
||||
() => frappe.click_link('Approve'), // approve the application [as administrator]
|
||||
() => frappe.click_button('Yes'),
|
||||
() => frappe.timeout(1),
|
||||
() => assert.ok(cur_frm.doc.docstatus,
|
||||
"leave application submitted after approval"),
|
||||
|
||||
// check auto filled posting date [today]
|
||||
|
||||
() => assert.equal(today_date, cur_frm.doc.posting_date,
|
||||
"posting date correctly set"),
|
||||
() => frappe.set_route("List", "Leave Application", "List"),
|
||||
() => frappe.timeout(1),
|
||||
// check approved application in list
|
||||
() => assert.deepEqual(["Test Employee 1", "Approved"], [cur_list.data[0].employee_name, cur_list.data[0].status],
|
||||
"leave for correct employee is approved"),
|
||||
// // check approved application in list
|
||||
() => assert.deepEqual(["Test Employee 1", "Approved"], [cur_list.data[0].employee_name, cur_list.data[0].workflow_state]),
|
||||
// "leave for correct employee is approved"),
|
||||
() => done()
|
||||
]);
|
||||
});
|
@ -103,7 +103,7 @@ class TestLeaveApplication(unittest.TestCase):
|
||||
|
||||
application = self.get_application(_test_records[0])
|
||||
application.insert()
|
||||
application.status = "Approved"
|
||||
application.workflow_state = "Approved"
|
||||
self.assertRaises(LeaveDayBlockedError, application.submit)
|
||||
|
||||
frappe.set_user("test1@example.com")
|
||||
@ -257,7 +257,7 @@ class TestLeaveApplication(unittest.TestCase):
|
||||
application.insert()
|
||||
|
||||
frappe.set_user("test@example.com")
|
||||
application.status = "Approved"
|
||||
application.workflow_state = "Approved"
|
||||
|
||||
# clear permlevel access cache on change user
|
||||
del application._has_access_to
|
||||
@ -297,7 +297,7 @@ class TestLeaveApplication(unittest.TestCase):
|
||||
|
||||
# submit leave application by Leave Approver
|
||||
frappe.set_user("test1@example.com")
|
||||
application.status = "Approved"
|
||||
application.workflow_state = "Approved"
|
||||
del application._has_access_to
|
||||
application.submit()
|
||||
self.assertEqual(frappe.db.get_value("Leave Application", application.name,
|
||||
@ -339,7 +339,7 @@ class TestLeaveApplication(unittest.TestCase):
|
||||
application.insert()
|
||||
frappe.set_user("test1@example.com")
|
||||
del application._has_access_to
|
||||
application.status = "Approved"
|
||||
application.workflow_state = "Approved"
|
||||
|
||||
from erpnext.hr.doctype.leave_application.leave_application import LeaveApproverIdentityError
|
||||
self.assertRaises(LeaveApproverIdentityError, application.submit)
|
||||
@ -364,7 +364,7 @@ class TestLeaveApplication(unittest.TestCase):
|
||||
|
||||
# change to valid leave approver and try to submit leave application
|
||||
frappe.set_user("test2@example.com")
|
||||
application.status = "Approved"
|
||||
application.workflow_state = "Approved"
|
||||
del application._has_access_to
|
||||
application.submit()
|
||||
self.assertEqual(frappe.db.get_value("Leave Application", application.name,
|
||||
|
@ -300,7 +300,7 @@ class SalarySlip(TransactionBase):
|
||||
where t2.name = t1.leave_type
|
||||
and t2.is_lwp = 1
|
||||
and t1.docstatus = 1
|
||||
and t1.status = 'Approved'
|
||||
and t1.workflow_state = 'Approved'
|
||||
and t1.employee = %(employee)s
|
||||
and CASE WHEN t2.include_holiday != 1 THEN %(dt)s not in ('{0}') and %(dt)s between from_date and to_date
|
||||
WHEN t2.include_holiday THEN %(dt)s between from_date and to_date
|
||||
|
@ -489,3 +489,4 @@ erpnext.patches.v10_0.update_reserved_qty_for_purchase_order
|
||||
erpnext.patches.v10_0.fichier_des_ecritures_comptables_for_france
|
||||
erpnext.patches.v10_0.update_assessment_plan
|
||||
erpnext.patches.v10_0.update_assessment_result
|
||||
erpnext.patches.v10_0.workflow_leave_application
|
51
erpnext/patches/v10_0/workflow_leave_application.py
Normal file
51
erpnext/patches/v10_0/workflow_leave_application.py
Normal file
@ -0,0 +1,51 @@
|
||||
# Copyright (c) 2017, Frappe and Contributors
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
|
||||
def execute():
|
||||
frappe.reload_doc("hr", "doctype", "leave_application")
|
||||
frappe.reload_doc("workflow", "doctype", "workflow")
|
||||
|
||||
if not frappe.db.exists("Workflow State", "Open"):
|
||||
frappe.get_doc({
|
||||
'doctype': 'Workflow State',
|
||||
'workflow_state_name': 'Open',
|
||||
'style': 'Warning'
|
||||
}).insert(ignore_permissions=True)
|
||||
|
||||
frappe.get_doc({
|
||||
'doctype': 'Workflow',
|
||||
'workflow_name': 'Leave Approval',
|
||||
'document_type': 'Leave Application',
|
||||
'is_active': 1,
|
||||
'workflow_state_field': 'workflow_state',
|
||||
'states': [{
|
||||
"state": 'Open',
|
||||
"doc_status": 0,
|
||||
"allow_edit": 'Employee'
|
||||
}, {
|
||||
"state": 'Approved',
|
||||
"doc_status": 1,
|
||||
"allow_edit": 'Leave Approver'
|
||||
}, {
|
||||
"state": 'Rejected',
|
||||
"doc_status": 1,
|
||||
"allow_edit": 'Leave Approver'
|
||||
}],
|
||||
'transitions': [{
|
||||
"state": 'Open',
|
||||
"action": 'Approve',
|
||||
"next_state": 'Approved',
|
||||
"allowed": 'Leave Approver'
|
||||
},
|
||||
{
|
||||
"state": 'Open',
|
||||
"action": 'Reject',
|
||||
"next_state": 'Rejected',
|
||||
"allowed": 'Leave Approver'
|
||||
}]
|
||||
}).insert(ignore_permissions=True)
|
||||
|
||||
frappe.db.sql("""update `tabLeave Application` set workflow_state = status""")
|
Loading…
Reference in New Issue
Block a user