Merge pull request #13984 from manassolanki/employee-boarding
[feature] added the employee onboarding and separation doctype
diff --git a/erpnext/accounts/doctype/accounting_period/accounting_period.js b/erpnext/accounts/doctype/accounting_period/accounting_period.js
index 1fb57ea..e3d805a 100644
--- a/erpnext/accounts/doctype/accounting_period/accounting_period.js
+++ b/erpnext/accounts/doctype/accounting_period/accounting_period.js
@@ -2,7 +2,23 @@
// For license information, please see license.txt
frappe.ui.form.on('Accounting Period', {
- refresh: function(frm) {
-
+ onload: function(frm) {
+ if(frm.doc.closed_documents.length === 0 || (frm.doc.closed_documents.length === 1 && frm.doc.closed_documents[0].document_type == undefined)) {
+ frappe.call({
+ method: "get_doctypes_for_closing",
+ doc:frm.doc,
+ callback: function(r) {
+ if(r.message) {
+ cur_frm.clear_table("closed_documents");
+ r.message.forEach(function(element) {
+ var c = frm.add_child("closed_documents");
+ c.document_type = element.document_type;
+ c.closed = element.closed;
+ });
+ refresh_field("closed_documents");
+ }
+ }
+ });
+ }
}
});
diff --git a/erpnext/accounts/doctype/accounting_period/accounting_period.py b/erpnext/accounts/doctype/accounting_period/accounting_period.py
index 31f1849..32441db 100644
--- a/erpnext/accounts/doctype/accounting_period/accounting_period.py
+++ b/erpnext/accounts/doctype/accounting_period/accounting_period.py
@@ -7,4 +7,48 @@
from frappe.model.document import Document
class AccountingPeriod(Document):
- pass
+ def validate(self):
+ self.validate_overlap()
+
+ def before_insert(self):
+ self.bootstrap_doctypes_for_closing()
+
+ def autoname(self):
+ company_abbr = frappe.db.get_value("Company", self.company, "abbr")
+ self.name = " - ".join([self.period_name, company_abbr])
+
+ def validate_overlap(self):
+ existing_accounting_period = frappe.db.sql("""select name from `tabAccounting Period`
+ where (
+ (%(start_date)s between start_date and end_date)
+ or (%(end_date)s between start_date and end_date)
+ or (start_date between %(start_date)s and %(end_date)s)
+ or (end_date between %(start_date)s and %(end_date)s)
+ ) and name!=%(name)s and company=%(company)s""",
+ {
+ "start_date": self.start_date,
+ "end_date": self.end_date,
+ "name": self.name,
+ "company": self.company
+ }, as_dict=True)
+
+ if len(existing_accounting_period) > 0:
+ frappe.throw("Accounting Period overlaps with {0}".format(existing_accounting_period[0].get("name")))
+
+ def get_doctypes_for_closing(self):
+ docs_for_closing = []
+ #if not self.closed_documents or len(self.closed_documents) == 0:
+ doctypes = ["Sales Invoice", "Purchase Invoice", "Journal Entry", "Payroll Entry", "Bank Reconciliation", "Asset", "Purchase Order", "Sales Order", "Leave Application", "Leave Allocation", "Stock Entry"]
+ closed_doctypes = [{"document_type": doctype, "closed": 1} for doctype in doctypes]
+ for closed_doctype in closed_doctypes:
+ docs_for_closing.append(closed_doctype)
+
+ return docs_for_closing
+
+ def bootstrap_doctypes_for_closing(self):
+ if len(self.closed_documents) == 0:
+ for doctype_for_closing in self.get_doctypes_for_closing():
+ self.append('closed_documents', {
+ "document_type": doctype_for_closing.document_type,
+ "closed": doctype_for_closing.closed
+ })
diff --git a/erpnext/accounts/doctype/accounting_period/test_accounting_period.py b/erpnext/accounts/doctype/accounting_period/test_accounting_period.py
index 99694d2..cc2e6a9 100644
--- a/erpnext/accounts/doctype/accounting_period/test_accounting_period.py
+++ b/erpnext/accounts/doctype/accounting_period/test_accounting_period.py
@@ -7,4 +7,21 @@
import unittest
class TestAccountingPeriod(unittest.TestCase):
- pass
+ def test_overlap(self):
+ ap1 = create_accounting_period({"start_date":"2018-04-01", "end_date":"2018-06-30", "company":"Wind Power LLC"})
+ ap1.save()
+ ap2 = create_accounting_period({"start_date":"2018-06-30", "end_date":"2018-07-10", "company":"Wind Power LLC"})
+ self.assertRaises(frappe.OverlapError, accounting_period_2.save())
+
+ def tearDown(self):
+ pass
+
+
+def create_accounting_period(**args):
+ accounting_period = frappe.new_doc("Accounting Period")
+ accounting_period.start_date = args.start_date or frappe.utils.datetime.date(2018, 4, 1)
+ accounting_period.end_date = args.end_date or frappe.utils.datetime.date(2018, 6, 30)
+ accounting_period.company = args.company
+ accounting_period.period_name = "_Test_Period_Name_1"
+
+ return accounting_period
diff --git a/erpnext/hr/doctype/leave_application/leave_application.js b/erpnext/hr/doctype/leave_application/leave_application.js
index 242c987..76b5ae5 100755
--- a/erpnext/hr/doctype/leave_application/leave_application.js
+++ b/erpnext/hr/doctype/leave_application/leave_application.js
@@ -18,7 +18,7 @@
doctype: frm.doc.doctype
}
};
- });
+ });
frm.set_query("employee", erpnext.queries.employee);
},
@@ -27,6 +27,33 @@
frm.toggle_reqd("half_day_date", frm.doc.half_day == 1);
},
+ make_dashboard: function(frm) {
+ var leave_details;
+ if (frm.doc.employee) {
+ frappe.call({
+ method: "erpnext.hr.doctype.leave_application.leave_application.get_leave_details",
+ async: false,
+ args: {
+ employee: frm.doc.employee,
+ date: frm.doc.posting_date
+ },
+ callback: function(r) {
+ if (!r.exc && r.message) {
+ leave_details = r.message;
+ }
+ }
+ });
+
+ $("div").remove(".form-dashboard-section");
+ let section = frm.dashboard.add_section(
+ frappe.render_template('leave_application_dashboard', {
+ data: leave_details
+ })
+ );
+ frm.dashboard.show();
+ }
+ },
+
refresh: function(frm) {
if (frm.is_new()) {
frm.trigger("calculate_total_days");
@@ -43,6 +70,7 @@
},
employee: function(frm) {
+ frm.trigger("make_dashboard");
frm.trigger("get_leave_balance");
},
diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py
index 23514e1..304afdd 100755
--- a/erpnext/hr/doctype/leave_application/leave_application.py
+++ b/erpnext/hr/doctype/leave_application/leave_application.py
@@ -19,6 +19,7 @@
from frappe.model.document import Document
class LeaveApplication(Document):
+
def get_feed(self):
return _("{0}: From {0} of type {1}").format(self.employee_name, self.leave_type)
@@ -307,6 +308,24 @@
return number_of_days
@frappe.whitelist()
+def get_leave_details(employee, date):
+ allocation_records = get_leave_allocation_records(date, employee).get(employee, frappe._dict())
+ leave_allocation = {}
+ for d in allocation_records:
+ allocation = allocation_records.get(d, frappe._dict())
+ date = allocation.to_date
+ leaves_taken = get_leaves_for_period(employee, d, allocation.from_date, date, status="Approved")
+ leaves_pending = get_leaves_for_period(employee, d, allocation.from_date, date, status="Open")
+ remaining_leaves = allocation.total_leaves_allocated - leaves_taken - leaves_pending
+ leave_allocation[d] = {
+ "total_leaves": allocation.total_leaves_allocated,
+ "leaves_taken": leaves_taken,
+ "pending_leaves": leaves_pending,
+ "remaining_leaves": remaining_leaves}
+
+ return leave_allocation
+
+@frappe.whitelist()
def get_leave_balance_on(employee, leave_type, date, allocation_records=None,
consider_all_leaves_in_the_allocation_period=False):
if allocation_records == None:
@@ -316,16 +335,16 @@
if consider_all_leaves_in_the_allocation_period:
date = allocation.to_date
- leaves_taken = get_approved_leaves_for_period(employee, leave_type, allocation.from_date, date)
+ leaves_taken = get_leaves_for_period(employee, leave_type, allocation.from_date, date, status=Approved)
return flt(allocation.total_leaves_allocated) - flt(leaves_taken)
-def get_approved_leaves_for_period(employee, leave_type, from_date, to_date):
+def get_leaves_for_period(employee, leave_type, from_date, to_date, status):
leave_applications = frappe.db.sql("""
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 docstatus=1
+ and status = %(status)s 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))
@@ -333,6 +352,7 @@
"from_date": from_date,
"to_date": to_date,
"employee": employee,
+ "status": status,
"leave_type": leave_type
}, as_dict=1)
diff --git a/erpnext/hr/doctype/leave_application/leave_application_dashboard.html b/erpnext/hr/doctype/leave_application/leave_application_dashboard.html
new file mode 100644
index 0000000..95e74a6
--- /dev/null
+++ b/erpnext/hr/doctype/leave_application/leave_application_dashboard.html
@@ -0,0 +1,30 @@
+
+{% if data %}
+<h5 style="margin-top: 20px;"> {{ __("Allocated Leaves") }} </h5>
+<table class="table table-bordered small">
+ <thead>
+ <tr>
+ <th style="width: 20%">{{ __("Leave Type") }}</th>
+ <th style="width: 20%" class="text-right">{{ __("Total Allocated Leaves") }}</th>
+ <th style="width: 20%" class="text-right">{{ __("Used Leaves") }}</th>
+ <th style="width: 20%" class="text-right">{{ __("Pending Leaves") }}</th>
+ <th style="width: 20%" class="text-right">{{ __("Available Leaves") }}</th>
+ </tr>
+ <!-- <p> {{data["Sick Leave"][0]["leaves_taken"]}}</p> -->
+
+ </thead>
+ <tbody>
+ {% for(const [key, value] of Object.entries(data)) { %}
+ <tr>
+ <td> {%= key %} </td>
+ <td> {%= value["total_leaves"] %} </td>
+ <td> {%= value["leaves_taken"] %} </td>
+ <td> {%= value["pending_leaves"] %} </td>
+ <td> {%= value["remaining_leaves"] %} </td>
+ </tr>
+ {% } %}
+ </tbody>
+</table>
+{% } else { %}
+<p style="margin-top: 30px;"> No Leaves have been allocated. </p>
+{% } %}
\ No newline at end of file