From fd53c64d5d6aa77e6dbd88c8c65732b2effd9486 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Fri, 29 Mar 2019 20:50:06 +0530 Subject: [PATCH 01/98] feat: create carried forward leave expiry option --- erpnext/hr/doctype/leave_type/leave_type.json | 85 +++++++++++++++++-- erpnext/hr/doctype/leave_type/leave_type.py | 9 +- 2 files changed, 86 insertions(+), 8 deletions(-) diff --git a/erpnext/hr/doctype/leave_type/leave_type.json b/erpnext/hr/doctype/leave_type/leave_type.json index 6a7a80a784..06f18c8a5e 100644 --- a/erpnext/hr/doctype/leave_type/leave_type.json +++ b/erpnext/hr/doctype/leave_type/leave_type.json @@ -1,5 +1,6 @@ { "allow_copy": 0, + "allow_events_in_timeline": 0, "allow_guest_to_view": 0, "allow_import": 1, "allow_rename": 1, @@ -14,10 +15,12 @@ "fields": [ { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "leave_type_name", "fieldtype": "Data", "hidden": 0, @@ -42,15 +45,17 @@ "search_index": 0, "set_only_once": 0, "translatable": 0, - "unique": 0 + "unique": 1 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, "depends_on": "", + "fetch_if_empty": 0, "fieldname": "max_leaves_allowed", "fieldtype": "Int", "hidden": 0, @@ -78,10 +83,12 @@ }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "applicable_after", "fieldtype": "Int", "hidden": 0, @@ -109,10 +116,12 @@ }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "max_continuous_days_allowed", "fieldtype": "Int", "hidden": 0, @@ -141,10 +150,12 @@ }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "column_break_3", "fieldtype": "Column Break", "hidden": 0, @@ -171,10 +182,12 @@ }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "is_carry_forward", "fieldtype": "Check", "hidden": 0, @@ -203,10 +216,46 @@ }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, + "depends_on": "eval: doc.is_carry_forward == 1", + "fetch_if_empty": 0, + "fieldname": "carry_forward_leave_expiry", + "fieldtype": "Int", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Carry Forward Leave Expiry", + "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, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, "fieldname": "is_lwp", "fieldtype": "Check", "hidden": 0, @@ -233,10 +282,12 @@ }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "is_optional_leave", "fieldtype": "Check", "hidden": 0, @@ -264,10 +315,12 @@ }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "allow_negative", "fieldtype": "Check", "hidden": 0, @@ -294,10 +347,12 @@ }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "include_holiday", "fieldtype": "Check", "hidden": 0, @@ -324,10 +379,12 @@ }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "is_compensatory", "fieldtype": "Check", "hidden": 0, @@ -355,10 +412,12 @@ }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 1, "columns": 0, + "fetch_if_empty": 0, "fieldname": "encashment", "fieldtype": "Section Break", "hidden": 0, @@ -386,10 +445,12 @@ }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "allow_encashment", "fieldtype": "Check", "hidden": 0, @@ -417,11 +478,13 @@ }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, "depends_on": "allow_encashment", + "fetch_if_empty": 0, "fieldname": "encashment_threshold_days", "fieldtype": "Int", "hidden": 0, @@ -449,11 +512,13 @@ }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, "depends_on": "allow_encashment", + "fetch_if_empty": 0, "fieldname": "earning_component", "fieldtype": "Link", "hidden": 0, @@ -482,10 +547,12 @@ }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 1, "columns": 0, + "fetch_if_empty": 0, "fieldname": "earned_leave", "fieldtype": "Section Break", "hidden": 0, @@ -513,10 +580,12 @@ }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "is_earned_leave", "fieldtype": "Check", "hidden": 0, @@ -544,11 +613,13 @@ }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, "depends_on": "is_earned_leave", + "fetch_if_empty": 0, "fieldname": "earned_leave_frequency", "fieldtype": "Select", "hidden": 0, @@ -577,12 +648,14 @@ }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, "default": "0.5", "depends_on": "is_earned_leave", + "fetch_if_empty": 0, "fieldname": "rounding", "fieldtype": "Select", "hidden": 0, @@ -611,17 +684,15 @@ } ], "has_web_view": 0, - "hide_heading": 0, "hide_toolbar": 0, "icon": "fa fa-flag", "idx": 1, - "image_view": 0, "in_create": 0, "is_submittable": 0, "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2018-06-03 18:32:51.803472", + "modified": "2019-03-29 16:06:03.456035", "modified_by": "Administrator", "module": "HR", "name": "Leave Type", @@ -687,8 +758,8 @@ ], "quick_entry": 0, "read_only": 0, - "read_only_onload": 0, "show_name_in_global_search": 0, "track_changes": 0, - "track_seen": 0 -} + "track_seen": 0, + "track_views": 0 +} \ No newline at end of file diff --git a/erpnext/hr/doctype/leave_type/leave_type.py b/erpnext/hr/doctype/leave_type/leave_type.py index e0127e5e97..d7891a4f62 100644 --- a/erpnext/hr/doctype/leave_type/leave_type.py +++ b/erpnext/hr/doctype/leave_type/leave_type.py @@ -3,8 +3,15 @@ from __future__ import unicode_literals import frappe +from frappe import _ from frappe.model.document import Document class LeaveType(Document): - pass \ No newline at end of file + def validate(self): + if self.is_carry_forward: + self.validate_carry_forward() + + def validate_carry_forward(self): + if (self.carry_forward_leave_expiry <1 or self.carry_forward_leave_expiry > 365): + frappe.throw(_('Invalid entry!! Carried forward days need to expire within a year')) From c182a5687eae94994b0df8f8692d70195a594670 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Fri, 5 Apr 2019 11:17:40 +0530 Subject: [PATCH 02/98] feat: add validation for carry forward leave expiry --- erpnext/hr/doctype/leave_type/leave_type.json | 107 ++++++++++++------ erpnext/hr/doctype/leave_type/leave_type.py | 4 +- 2 files changed, 75 insertions(+), 36 deletions(-) diff --git a/erpnext/hr/doctype/leave_type/leave_type.json b/erpnext/hr/doctype/leave_type/leave_type.json index 06f18c8a5e..ee9b04fc00 100644 --- a/erpnext/hr/doctype/leave_type/leave_type.json +++ b/erpnext/hr/doctype/leave_type/leave_type.json @@ -221,40 +221,7 @@ "bold": 0, "collapsible": 0, "columns": 0, - "depends_on": "eval: doc.is_carry_forward == 1", - "fetch_if_empty": 0, - "fieldname": "carry_forward_leave_expiry", - "fieldtype": "Int", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Carry Forward Leave Expiry", - "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, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, + "depends_on": "", "fetch_if_empty": 0, "fieldname": "is_lwp", "fieldtype": "Check", @@ -410,6 +377,76 @@ "translatable": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 1, + "columns": 0, + "depends_on": "eval: doc.is_carry_forward == 1", + "fetch_if_empty": 0, + "fieldname": "carry_forward_section", + "fieldtype": "Section 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, + "label": "Carry Forward", + "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, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "1", + "depends_on": "", + "description": "calculated in days", + "fetch_if_empty": 0, + "fieldname": "carry_forward__leave_expiry", + "fieldtype": "Int", + "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": "Carry Forward Leave Expiry", + "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, + "translatable": 0, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_in_quick_entry": 0, @@ -692,7 +729,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2019-03-29 16:06:03.456035", + "modified": "2019-04-04 14:27:47.742997", "modified_by": "Administrator", "module": "HR", "name": "Leave Type", diff --git a/erpnext/hr/doctype/leave_type/leave_type.py b/erpnext/hr/doctype/leave_type/leave_type.py index d7891a4f62..9cd574a598 100644 --- a/erpnext/hr/doctype/leave_type/leave_type.py +++ b/erpnext/hr/doctype/leave_type/leave_type.py @@ -2,6 +2,7 @@ # License: GNU General Public License v3. See license.txt from __future__ import unicode_literals +import calendar import frappe from frappe import _ @@ -13,5 +14,6 @@ class LeaveType(Document): self.validate_carry_forward() def validate_carry_forward(self): - if (self.carry_forward_leave_expiry <1 or self.carry_forward_leave_expiry > 365): + max_days = 366 if calendar.isleap() else 365 + if not (1 < self.carry_forward_leave_expiry < max_days): frappe.throw(_('Invalid entry!! Carried forward days need to expire within a year')) From 1208ca6a36069cdd7b5f394839960826e3cdadba Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Fri, 5 Apr 2019 11:18:28 +0530 Subject: [PATCH 03/98] feat: add old leaves to track carry forward leave allocation --- .../leave_allocation/leave_allocation.json | 94 ++++++++++++++++++- 1 file changed, 90 insertions(+), 4 deletions(-) diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation.json b/erpnext/hr/doctype/leave_allocation/leave_allocation.json index 6d61fe3d5c..431adfb16b 100644 --- a/erpnext/hr/doctype/leave_allocation/leave_allocation.json +++ b/erpnext/hr/doctype/leave_allocation/leave_allocation.json @@ -22,6 +22,7 @@ "collapsible": 0, "columns": 0, "default": "", + "fetch_if_empty": 0, "fieldname": "naming_series", "fieldtype": "Select", "hidden": 0, @@ -55,6 +56,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "employee", "fieldtype": "Link", "hidden": 0, @@ -90,6 +92,7 @@ "collapsible": 0, "columns": 0, "fetch_from": "employee.employee_name", + "fetch_if_empty": 0, "fieldname": "employee_name", "fieldtype": "Data", "hidden": 0, @@ -122,6 +125,7 @@ "collapsible": 0, "columns": 0, "fetch_from": "employee.department", + "fetch_if_empty": 0, "fieldname": "department", "fieldtype": "Link", "hidden": 0, @@ -155,6 +159,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "column_break1", "fieldtype": "Column Break", "hidden": 0, @@ -186,6 +191,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "leave_type", "fieldtype": "Link", "hidden": 0, @@ -220,6 +226,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "from_date", "fieldtype": "Date", "hidden": 0, @@ -252,6 +259,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "to_date", "fieldtype": "Date", "hidden": 0, @@ -284,6 +292,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "section_break_6", "fieldtype": "Section Break", "hidden": 0, @@ -316,6 +325,8 @@ "bold": 1, "collapsible": 0, "columns": 0, + "depends_on": "eval: doc.is_carry_forward != 1", + "fetch_if_empty": 0, "fieldname": "new_leaves_allocated", "fieldtype": "Float", "hidden": 0, @@ -347,7 +358,76 @@ "bold": 0, "collapsible": 0, "columns": 0, + "depends_on": "eval: doc.is_carry_forward == 1", + "fetch_if_empty": 0, + "fieldname": "old_leaves_allocated", + "fieldtype": "Int", + "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": "Old Leaves Allocated", + "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, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "is_carry_forward", + "fieldtype": "Check", + "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": "is carry forward", + "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, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "", "description": "", + "fetch_if_empty": 0, "fieldname": "carry_forward", "fieldtype": "Check", "hidden": 0, @@ -380,6 +460,7 @@ "collapsible": 0, "columns": 0, "depends_on": "carry_forward", + "fetch_if_empty": 0, "fieldname": "carry_forwarded_leaves", "fieldtype": "Float", "hidden": 0, @@ -411,6 +492,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "total_leaves_allocated", "fieldtype": "Float", "hidden": 0, @@ -443,6 +525,7 @@ "collapsible": 0, "columns": 0, "depends_on": "eval:doc.total_leaves_encashed>0", + "fetch_if_empty": 0, "fieldname": "total_leaves_encashed", "fieldtype": "Float", "hidden": 0, @@ -475,6 +558,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "column_break_10", "fieldtype": "Column Break", "hidden": 0, @@ -506,6 +590,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "compensatory_request", "fieldtype": "Link", "hidden": 0, @@ -539,6 +624,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "leave_period", "fieldtype": "Link", "hidden": 0, @@ -572,6 +658,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "amended_from", "fieldtype": "Link", "hidden": 0, @@ -606,6 +693,7 @@ "bold": 0, "collapsible": 1, "columns": 0, + "fetch_if_empty": 0, "fieldname": "notes", "fieldtype": "Section Break", "hidden": 0, @@ -638,6 +726,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "description", "fieldtype": "Small Text", "hidden": 0, @@ -667,17 +756,15 @@ } ], "has_web_view": 0, - "hide_heading": 0, "hide_toolbar": 0, "icon": "fa fa-ok", "idx": 1, - "image_view": 0, "in_create": 0, "is_submittable": 1, "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2019-01-30 11:28:09.360525", + "modified": "2019-04-04 15:09:33.421008", "modified_by": "Administrator", "module": "HR", "name": "Leave Allocation", @@ -724,7 +811,6 @@ ], "quick_entry": 0, "read_only": 0, - "read_only_onload": 0, "search_fields": "employee,employee_name,leave_type,total_leaves_allocated", "show_name_in_global_search": 1, "sort_field": "modified", From 7c6b6eae5b55651d6401c57b6f8d2a1a4cd4a1b4 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Fri, 5 Apr 2019 11:58:35 +0530 Subject: [PATCH 04/98] feat: set leave allocation on carry forward check --- .../leave_allocation/leave_allocation.py | 42 ++++++++++++++----- 1 file changed, 31 insertions(+), 11 deletions(-) diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation.py b/erpnext/hr/doctype/leave_allocation/leave_allocation.py index dc270dba41..e755da5f54 100755 --- a/erpnext/hr/doctype/leave_allocation/leave_allocation.py +++ b/erpnext/hr/doctype/leave_allocation/leave_allocation.py @@ -62,11 +62,18 @@ class LeaveAllocation(Document): if flt(self.new_leaves_allocated) % 0.5: frappe.throw(_("Leaves must be allocated in multiples of 0.5"), ValueMultiplierError) - def validate_allocation_overlap(self): + def validate_allocation_overlap(self, carry_forward=0): leave_allocation = frappe.db.sql(""" - select name from `tabLeave Allocation` - where employee=%s and leave_type=%s and docstatus=1 - and to_date >= %s and from_date <= %s""", + SELECT + name + FROM `tabLeave Allocation` + WHERE + employee=%s + AND leave_type=%s + AND docstatus=1 + AND is_carry_forward={0} + AND to_date >= %s + AND from_date <= %s""".format(carry_forward), (self.employee, self.leave_type, self.from_date, self.to_date)) if leave_allocation: @@ -111,6 +118,11 @@ class LeaveAllocation(Document): else: frappe.throw(_("Total allocated leaves {0} cannot be less than already approved leaves {1} for the period").format(self.total_leaves_allocated, leaves_taken), LessAllocationError) + def set_carry_forward_leaves(self): + self.validate_allocation_overlap(carry_forward=1) + self.old_leaves_allocated = get_carry_forwarded_leaves(self.employee, self.leave_type, + self.from_date, self.is_carry_forward) + def get_leave_allocation_for_period(employee, leave_type, from_date, to_date): leave_allocated = 0 leave_allocations = frappe.db.sql(""" @@ -135,17 +147,25 @@ def get_leave_allocation_for_period(employee, leave_type, from_date, to_date): return leave_allocated @frappe.whitelist() -def get_carry_forwarded_leaves(employee, leave_type, date, carry_forward=None): +def get_carry_forwarded_leaves(employee, leave_type, date, is_carry_forward=None): carry_forwarded_leaves = 0 - if carry_forward: + if is_carry_forward: validate_carry_forward(leave_type) previous_allocation = frappe.db.sql(""" - select name, from_date, to_date, total_leaves_allocated - from `tabLeave Allocation` - where employee=%s and leave_type=%s and docstatus=1 and to_date < %s - order by to_date desc limit 1 + SELECT + name, + from_date, + to_date, + total_leaves_allocated + FROM `tabLeave Allocation` + WHERE + employee=%s + AND leave_type=%s + AND docstatus=1 + AND to_date < %s + ORDER BY to_date desc limit 1 """, (employee, leave_type, date), as_dict=1) if previous_allocation: leaves_taken = get_approved_leaves_for_period(employee, leave_type, @@ -157,4 +177,4 @@ def get_carry_forwarded_leaves(employee, leave_type, date, carry_forward=None): def validate_carry_forward(leave_type): if not frappe.db.get_value("Leave Type", leave_type, "is_carry_forward"): - frappe.throw(_("Leave Type {0} cannot be carry-forwarded").format(leave_type)) + frappe.throw(_("Leave Type {0} cannot be carry-forwarded").format(leave_type)) \ No newline at end of file From bd3b3ea12c45c2d5cda37fa984fcbbd607dde56e Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Thu, 11 Apr 2019 00:20:32 +0530 Subject: [PATCH 05/98] test: create leave type --- .../hr/doctype/leave_type/test_leave_type.py | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/erpnext/hr/doctype/leave_type/test_leave_type.py b/erpnext/hr/doctype/leave_type/test_leave_type.py index b844e49e7c..671865b5a0 100644 --- a/erpnext/hr/doctype/leave_type/test_leave_type.py +++ b/erpnext/hr/doctype/leave_type/test_leave_type.py @@ -2,6 +2,24 @@ # License: GNU General Public License v3. See license.txt from __future__ import unicode_literals - import frappe -test_records = frappe.get_test_records('Leave Type') \ No newline at end of file +from frappe import _ + +test_records = frappe.get_test_records('Leave Type') + +def create_leave_type(**args): + if frappe.db.exists("Leave Type", args.leave_type_name): + return frappe.get_doc("Leave Type", args.leave_type_name) + leave_type = frappe.get_doc({ + "doctype": "Leave Type", + "leave_type_name": args.leave_type_name or "_Test Leave Type", + "include_holiday": args.include_holidays or 1, + "allow_encashment": args.allow_encashment or 0, + "is_earned_leave": args.is_earned_leave or 0, + "is_lwp": args.is_lwp or 0, + "is_carry_forward": args.is_carry_forward or 0, + "carry_forward_leave_expiry": args.is_carry_forward or 0, + "encashment_threshold_days": args.encashment_threshold_days or 5, + "earning_component": "Leave Encashment" + }) + return leave_type \ No newline at end of file From c28d2e4b2aeae44daa08ca247f4fa5142fdcf4f8 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Thu, 11 Apr 2019 00:21:11 +0530 Subject: [PATCH 06/98] test: create leave allocation check --- .../leave_allocation/test_leave_allocation.py | 83 +++++++++++++++---- 1 file changed, 65 insertions(+), 18 deletions(-) diff --git a/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py b/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py index 3b22eb2e44..3bc8dc458d 100644 --- a/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py +++ b/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py @@ -1,12 +1,13 @@ from __future__ import unicode_literals import frappe import unittest -from frappe.utils import getdate +from frappe.utils import nowdate, add_months, getdate +from erpnext.hr.doctype.leave_type.test_leave_type import create_leave_type class TestLeaveAllocation(unittest.TestCase): def test_overlapping_allocation(self): frappe.db.sql("delete from `tabLeave Allocation`") - + employee = frappe.get_doc("Employee", frappe.db.sql_list("select name from tabEmployee limit 1")[0]) leaves = [ { @@ -18,7 +19,7 @@ class TestLeaveAllocation(unittest.TestCase): "from_date": getdate("2015-10-01"), "to_date": getdate("2015-10-31"), "new_leaves_allocated": 5, - "docstatus": 1 + "docstatus": 1 }, { "doctype": "Leave Allocation", @@ -28,17 +29,17 @@ class TestLeaveAllocation(unittest.TestCase): "leave_type": "_Test Leave Type", "from_date": getdate("2015-09-01"), "to_date": getdate("2015-11-30"), - "new_leaves_allocated": 5 + "new_leaves_allocated": 5 } ] frappe.get_doc(leaves[0]).save() self.assertRaises(frappe.ValidationError, frappe.get_doc(leaves[1]).save) - - def test_invalid_period(self): + + def test_invalid_period(self): employee = frappe.get_doc("Employee", frappe.db.sql_list("select name from tabEmployee limit 1")[0]) - - d = frappe.get_doc({ + + doc = frappe.get_doc({ "doctype": "Leave Allocation", "__islocal": 1, "employee": employee.name, @@ -46,15 +47,15 @@ class TestLeaveAllocation(unittest.TestCase): "leave_type": "_Test Leave Type", "from_date": getdate("2015-09-30"), "to_date": getdate("2015-09-1"), - "new_leaves_allocated": 5 + "new_leaves_allocated": 5 }) - + #invalid period - self.assertRaises(frappe.ValidationError, d.save) - + self.assertRaises(frappe.ValidationError, doc.save) + def test_allocated_leave_days_over_period(self): employee = frappe.get_doc("Employee", frappe.db.sql_list("select name from tabEmployee limit 1")[0]) - d = frappe.get_doc({ + doc = frappe.get_doc({ "doctype": "Leave Allocation", "__islocal": 1, "employee": employee.name, @@ -62,10 +63,56 @@ class TestLeaveAllocation(unittest.TestCase): "leave_type": "_Test Leave Type", "from_date": getdate("2015-09-1"), "to_date": getdate("2015-09-30"), - "new_leaves_allocated": 35 + "new_leaves_allocated": 35 }) - - #allocated leave more than period - self.assertRaises(frappe.ValidationError, d.save) - + #allocated leave more than period + self.assertRaises(frappe.ValidationError, doc.save) + + def test_carry_forward_allocation(self): + frappe.db.sql("delete from `tabLeave Allocation`") + + employee = frappe.get_doc("Employee", frappe.db.sql_list("select name from tabEmployee limit 1")[0]) + doc = frappe.get_doc({ + "doctype": "Leave Allocation", + "__islocal": 1, + "employee": employee.name, + "employee_name": employee.employee_name, + "leave_type": "_Test Leave Type Carry Forward", + "from_date": nowdate(), + "to_date": add_months(nowdate(),-12), + "new_leaves_allocated": 10 + }) + doc.save() + doc = frappe.get_doc({ + "doctype": "Leave Allocation", + "__islocal": 1, + "employee": employee.name, + "employee_name": employee.employee_name, + "leave_type": "_Test Leave Type Carry Forward", + "from_date": nowdate(), + "to_date": add_months(now_date(),12), + "carry_forward": 1 + }) + doc.save() + self.assertEquals(doc.total_leaves_allocated, 10) + +def create_leave_allocation(**args): + args = frappe._dict(args) + if not frappe.db.exists("Leave Type", "_Test Leave Type"): + leave_type = create_leave_type(args.leave_type) + leave_type.insert() + + employee = frappe.get_doc("Employee", frappe.db.sql_list("select name from tabEmployee limit 1")[0]) + leave_allocation = frappe.get_doc({ + "doctype": "Leave Allocation", + "__islocal": 1, + "employee": employee.name, + "employee_name": employee.employee_name, + "leave_type": args.leave_type or "_Test Leave Type", + "from_date": args.from_date or nowdate(), + "to_date": args.to_date or add_months(nowdate(), 12), + "new_leaves_allocated": args.new_leaves_allocated or 20 + }) + return leave_allocation + test_dependencies = ["Employee", "Leave Type"] \ No newline at end of file From e46d3a87ea7fc9850f4619ef02d6850ba3abe876 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Thu, 11 Apr 2019 00:22:11 +0530 Subject: [PATCH 07/98] feat: set carry forwarded leave allocation --- .../leave_allocation/leave_allocation.json | 107 +--------------- .../leave_allocation/leave_allocation.py | 114 +++++++++++------- 2 files changed, 75 insertions(+), 146 deletions(-) diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation.json b/erpnext/hr/doctype/leave_allocation/leave_allocation.json index 431adfb16b..aa203384e8 100644 --- a/erpnext/hr/doctype/leave_allocation/leave_allocation.json +++ b/erpnext/hr/doctype/leave_allocation/leave_allocation.json @@ -325,7 +325,7 @@ "bold": 1, "collapsible": 0, "columns": 0, - "depends_on": "eval: doc.is_carry_forward != 1", + "depends_on": "eval: doc.carry_forward != 1", "fetch_if_empty": 0, "fieldname": "new_leaves_allocated", "fieldtype": "Float", @@ -351,73 +351,6 @@ "translatable": 0, "unique": 0 }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval: doc.is_carry_forward == 1", - "fetch_if_empty": 0, - "fieldname": "old_leaves_allocated", - "fieldtype": "Int", - "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": "Old Leaves Allocated", - "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, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "is_carry_forward", - "fieldtype": "Check", - "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": "is carry forward", - "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, - "translatable": 0, - "unique": 0 - }, { "allow_bulk_edit": 0, "allow_in_quick_entry": 0, @@ -437,7 +370,7 @@ "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, - "label": "Add unused leaves from previous allocations", + "label": "Allocate unused leaves from previous allocations", "length": 0, "no_copy": 0, "permlevel": 0, @@ -452,39 +385,6 @@ "translatable": 0, "unique": 0 }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "carry_forward", - "fetch_if_empty": 0, - "fieldname": "carry_forwarded_leaves", - "fieldtype": "Float", - "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": "Unused leaves", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "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, @@ -492,6 +392,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "depends_on": "", "fetch_if_empty": 0, "fieldname": "total_leaves_allocated", "fieldtype": "Float", @@ -764,7 +665,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2019-04-04 15:09:33.421008", + "modified": "2019-04-05 15:31:04.627015", "modified_by": "Administrator", "module": "HR", "name": "Leave Allocation", diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation.py b/erpnext/hr/doctype/leave_allocation/leave_allocation.py index e755da5f54..7ce3143901 100755 --- a/erpnext/hr/doctype/leave_allocation/leave_allocation.py +++ b/erpnext/hr/doctype/leave_allocation/leave_allocation.py @@ -18,14 +18,19 @@ class ValueMultiplierError(frappe.ValidationError): pass class LeaveAllocation(Document): def validate(self): self.validate_period() - self.validate_new_leaves_allocated_value() + self.validate_lwp() self.validate_allocation_overlap() self.validate_back_dated_allocation() + if not self.carry_forward: + self.validate_new_leaves_allocated_value() + self.validate_leave_allocation_days() self.set_total_leaves_allocated() self.validate_total_leaves_allocated() - self.validate_lwp() set_employee_name(self) - self.validate_leave_allocation_days() + + def on_update(self): + if self.carry_forward: + self.set_carry_forward_leaves() def validate_leave_allocation_days(self): company = frappe.db.get_value("Employee", self.employee, "company") @@ -44,7 +49,6 @@ class LeaveAllocation(Document): self.validate_new_leaves_allocated_value() self.set_total_leaves_allocated() - frappe.db.set(self,'carry_forwarded_leaves', flt(self.carry_forwarded_leaves)) frappe.db.set(self,'total_leaves_allocated',flt(self.total_leaves_allocated)) self.validate_against_leave_applications() @@ -62,7 +66,7 @@ class LeaveAllocation(Document): if flt(self.new_leaves_allocated) % 0.5: frappe.throw(_("Leaves must be allocated in multiples of 0.5"), ValueMultiplierError) - def validate_allocation_overlap(self, carry_forward=0): + def validate_allocation_overlap(self): leave_allocation = frappe.db.sql(""" SELECT name @@ -71,10 +75,10 @@ class LeaveAllocation(Document): employee=%s AND leave_type=%s AND docstatus=1 - AND is_carry_forward={0} + AND carry_forward={0} AND to_date >= %s - AND from_date <= %s""".format(carry_forward), - (self.employee, self.leave_type, self.from_date, self.to_date)) + AND from_date <= %s""" #nosec + .format(self.carry_forward), (self.employee, self.leave_type, self.from_date, self.to_date)) if leave_allocation: frappe.msgprint(_("{0} already allocated for Employee {1} for period {2} to {3}") @@ -94,12 +98,11 @@ class LeaveAllocation(Document): BackDatedAllocationError) def set_total_leaves_allocated(self): - self.carry_forwarded_leaves = get_carry_forwarded_leaves(self.employee, - 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.new_leaves_allocated) - 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))) def validate_total_leaves_allocated(self): @@ -119,20 +122,44 @@ class LeaveAllocation(Document): frappe.throw(_("Total allocated leaves {0} cannot be less than already approved leaves {1} for the period").format(self.total_leaves_allocated, leaves_taken), LessAllocationError) def set_carry_forward_leaves(self): - self.validate_allocation_overlap(carry_forward=1) - self.old_leaves_allocated = get_carry_forwarded_leaves(self.employee, self.leave_type, - self.from_date, self.is_carry_forward) + + leaves_allocated + # check number of days to expire, ignore expiry for default value + expiry_days = frappe.db.get_value("Leave Type", + filters={"leave_type": leave_type, "is_carry_forward": 1}, + fieldname="carry_forward_leave_expiry") + + max_leaves_allowed = frappe.db.get_value("Leave Type", self.leave_type, "max_leaves_allowed") + leave_period = get_leave_period(self.from_date, self.to_date, company) + if leave_period: + leave_allocated = get_leave_allocation_for_period(self.employee, self.leave_type, leave_period[0].from_date, leave_period[0].to_date) + + carry_forwarded_leaves = get_carry_forwarded_leaves(self.employee, self.leave_type, + self.from_date, expiry_days) + leaves_allocated += carry_forwarded_leaves + + if leaves_allocated > max_leaves_allowed: + self.total_leaves_allocated = max_leaves_allowed - leaves_allocated + else: + self.total_leaves_allocated = carry_forwarded_leaves def get_leave_allocation_for_period(employee, leave_type, from_date, to_date): leave_allocated = 0 leave_allocations = frappe.db.sql(""" - select employee, leave_type, from_date, to_date, total_leaves_allocated - from `tabLeave Allocation` - where employee=%(employee)s and leave_type=%(leave_type)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)) + SELECT + employee, + leave_type, + from_date, + to_date, + total_leaves_allocated + FROM `tabLeave Allocation` + WHERE + employee=%(employee)s + AND leave_type=%(leave_type)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)) """, { "from_date": from_date, "to_date": to_date, @@ -147,31 +174,32 @@ def get_leave_allocation_for_period(employee, leave_type, from_date, to_date): return leave_allocated @frappe.whitelist() -def get_carry_forwarded_leaves(employee, leave_type, date, is_carry_forward=None): +def get_carry_forwarded_leaves(employee, leave_type, date, expiry_days): carry_forwarded_leaves = 0 - if is_carry_forward: - validate_carry_forward(leave_type) + validate_carry_forward(leave_type) + filters = { + "employee": employee, + "leave_type": leave_type, + "docstatus": 1, + "to_date": ("<", date) + } + limit = 1 + if expiry_days: + filters.update(carry_forward=0) + limit = 2 - previous_allocation = frappe.db.sql(""" - SELECT - name, - from_date, - to_date, - total_leaves_allocated - FROM `tabLeave Allocation` - WHERE - employee=%s - AND leave_type=%s - AND docstatus=1 - AND to_date < %s - ORDER BY to_date desc limit 1 - """, (employee, leave_type, date), as_dict=1) - if previous_allocation: - leaves_taken = get_approved_leaves_for_period(employee, leave_type, - previous_allocation[0].from_date, previous_allocation[0].to_date) + previous_allocation = frappe.get_all("Leave Allocation", + filters=filters, + fields=["name","from_date","to_date","total_leaves_allocated"], + order_by="to_date desc", + limit=limit) - carry_forwarded_leaves = flt(previous_allocation[0].total_leaves_allocated) - flt(leaves_taken) + if previous_allocation: + leaves_taken = get_approved_leaves_for_period(employee, leave_type, + previous_allocation[0].from_date, previous_allocation[0].to_date) + + carry_forwarded_leaves = flt(previous_allocation[0].total_leaves_allocated) - flt(leaves_taken) return carry_forwarded_leaves From d6c5b6320ffc0327dabad107688bc71ffccbcbf5 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Tue, 23 Apr 2019 17:47:25 +0530 Subject: [PATCH 08/98] feat: add a field for conditionally displaying carry forwarded leave --- .../leave_allocation/leave_allocation.json | 36 ++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation.json b/erpnext/hr/doctype/leave_allocation/leave_allocation.json index aa203384e8..0903398d1f 100644 --- a/erpnext/hr/doctype/leave_allocation/leave_allocation.json +++ b/erpnext/hr/doctype/leave_allocation/leave_allocation.json @@ -351,6 +351,40 @@ "translatable": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "eval: doc.carry_forward == 1", + "fetch_if_empty": 0, + "fieldname": "carry_forwarded_leaves", + "fieldtype": "Int", + "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": "Unused Leaves", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "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, @@ -665,7 +699,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2019-04-05 15:31:04.627015", + "modified": "2019-04-22 18:33:15.858006", "modified_by": "Administrator", "module": "HR", "name": "Leave Allocation", From 0c0bfb1ef02d5ab68c97f4ec17c05c783abfe47c Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Tue, 23 Apr 2019 18:25:51 +0530 Subject: [PATCH 09/98] feat: calculate carry forward leaves allocation --- .../leave_allocation/leave_allocation.py | 72 +++++++++---------- 1 file changed, 33 insertions(+), 39 deletions(-) diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation.py b/erpnext/hr/doctype/leave_allocation/leave_allocation.py index 7ce3143901..7f8f806f3b 100755 --- a/erpnext/hr/doctype/leave_allocation/leave_allocation.py +++ b/erpnext/hr/doctype/leave_allocation/leave_allocation.py @@ -21,35 +21,25 @@ class LeaveAllocation(Document): self.validate_lwp() self.validate_allocation_overlap() self.validate_back_dated_allocation() - if not self.carry_forward: - self.validate_new_leaves_allocated_value() - self.validate_leave_allocation_days() - self.set_total_leaves_allocated() self.validate_total_leaves_allocated() set_employee_name(self) - - def on_update(self): - if self.carry_forward: - self.set_carry_forward_leaves() + self.validate_leaves_allocated_value() + self.validate_leave_allocation_days() + self.set_total_leaves_allocated() def validate_leave_allocation_days(self): - company = frappe.db.get_value("Employee", self.employee, "company") - leave_period = get_leave_period(self.from_date, self.to_date, company) - max_leaves_allowed = frappe.db.get_value("Leave Type", self.leave_type, "max_leaves_allowed") - if max_leaves_allowed > 0: - leave_allocated = 0 - if leave_period: - leave_allocated = get_leave_allocation_for_period(self.employee, self.leave_type, leave_period[0].from_date, leave_period[0].to_date) - leave_allocated += self.new_leaves_allocated - if leave_allocated > max_leaves_allowed: - frappe.throw(_("Total allocated leaves are more days than maximum allocation of {0} leave type for employee {1} in the period")\ - .format(self.leave_type, self.employee)) + new_leaves = self.new_leaves_allocate if not self.carry_forward else self.carry_forwarded_leaves + max_leaves, leaves_allocated = self.get_max_leaves_with_leaves_allocated_for_leave_type(flt(new_leaves)) + + if leave_allocated > max_leaves: + frappe.throw(_("Total allocated leaves are more days than maximum allocation of {0} leave type for employee {1} in the period")\ + .format(self.leave_type, self.employee)) def on_update_after_submit(self): self.validate_new_leaves_allocated_value() self.set_total_leaves_allocated() - frappe.db.set(self,'total_leaves_allocated',flt(self.total_leaves_allocated)) + frappe.db.set(self,'total_leaves_allocated', flt(self.total_leaves_allocated)) self.validate_against_leave_applications() @@ -61,7 +51,7 @@ class LeaveAllocation(Document): if frappe.db.get_value("Leave Type", self.leave_type, "is_lwp"): frappe.throw(_("Leave Type {0} cannot be allocated since it is leave without pay").format(self.leave_type)) - def validate_new_leaves_allocated_value(self): + def validate_leaves_allocated_value(self): """validate that leave allocation is in multiples of 0.5""" if flt(self.new_leaves_allocated) % 0.5: frappe.throw(_("Leaves must be allocated in multiples of 0.5"), ValueMultiplierError) @@ -98,8 +88,11 @@ class LeaveAllocation(Document): BackDatedAllocationError) def set_total_leaves_allocated(self): - - self.total_leaves_allocated = flt(self.new_leaves_allocated) + if self.carry_forward: + self.set_carry_forwarded_leaves() + self.total_leaves_allocated = flt(self.carry_forwarded_leaves) + else: + self.total_leaves_allocated = flt(self.new_leaves_allocated) 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"): @@ -121,27 +114,28 @@ class LeaveAllocation(Document): else: frappe.throw(_("Total allocated leaves {0} cannot be less than already approved leaves {1} for the period").format(self.total_leaves_allocated, leaves_taken), LessAllocationError) - def set_carry_forward_leaves(self): - - leaves_allocated - # check number of days to expire, ignore expiry for default value + def set_carry_forwarded_leaves(self): + # check number of days to expire, ignore expiry for default value 0 expiry_days = frappe.db.get_value("Leave Type", filters={"leave_type": leave_type, "is_carry_forward": 1}, fieldname="carry_forward_leave_expiry") - max_leaves_allowed = frappe.db.get_value("Leave Type", self.leave_type, "max_leaves_allowed") + self.carry_forwarded_leaves = get_carry_forwarded_leaves(self.employee, self.leave_type, self.from_date, expiry_days) + max_leaves, leaves_allocated = self.get_max_leaves_with_leaves_allocated_for_leave_type(self.carry_forwarded_leaves) + if leaves_allocated > max_leaves: + self.carry_forwarded_leaves = max_leaves - (leaves_allocated - self.carry_forwarded_leaves) + + def get_max_leaves_with_leaves_allocated_for_leave_type(self, new_leaves): + ''' compare new leaves allocated with max leaves ''' + company = frappe.db.get_value("Employee", self.employee, "company") leave_period = get_leave_period(self.from_date, self.to_date, company) - if leave_period: - leave_allocated = get_leave_allocation_for_period(self.employee, self.leave_type, leave_period[0].from_date, leave_period[0].to_date) - - carry_forwarded_leaves = get_carry_forwarded_leaves(self.employee, self.leave_type, - self.from_date, expiry_days) - leaves_allocated += carry_forwarded_leaves - - if leaves_allocated > max_leaves_allowed: - self.total_leaves_allocated = max_leaves_allowed - leaves_allocated - else: - self.total_leaves_allocated = carry_forwarded_leaves + max_leaves_allowed = frappe.db.get_value("Leave Type", self.leave_type, "max_leaves_allowed") + if max_leaves_allowed > 0: + leave_allocated = 0 + if leave_period: + leave_allocated = get_leave_allocation_for_period(self.employee, self.leave_type, leave_period[0].from_date, leave_period[0].to_date) + leave_allocated += new_leaves + return max_leaves_allowed, leaves_allocated def get_leave_allocation_for_period(employee, leave_type, from_date, to_date): leave_allocated = 0 From 4badca54af4b06d127836e60af0dd5f7d9091f1d Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Tue, 23 Apr 2019 18:26:24 +0530 Subject: [PATCH 10/98] feat: validate leave expiry days --- erpnext/hr/doctype/leave_type/leave_type.json | 6 +++--- erpnext/hr/doctype/leave_type/leave_type.py | 5 +++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/erpnext/hr/doctype/leave_type/leave_type.json b/erpnext/hr/doctype/leave_type/leave_type.json index ee9b04fc00..0b8e38ea73 100644 --- a/erpnext/hr/doctype/leave_type/leave_type.json +++ b/erpnext/hr/doctype/leave_type/leave_type.json @@ -422,7 +422,7 @@ "depends_on": "", "description": "calculated in days", "fetch_if_empty": 0, - "fieldname": "carry_forward__leave_expiry", + "fieldname": "carry_forward_leave_expiry", "fieldtype": "Int", "hidden": 0, "ignore_user_permissions": 0, @@ -431,7 +431,7 @@ "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, - "label": "Carry Forward Leave Expiry", + "label": "Carry Forward Leave Expiry", "length": 0, "no_copy": 0, "permlevel": 0, @@ -729,7 +729,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2019-04-04 14:27:47.742997", + "modified": "2019-04-11 15:38:39.334283", "modified_by": "Administrator", "module": "HR", "name": "Leave Type", diff --git a/erpnext/hr/doctype/leave_type/leave_type.py b/erpnext/hr/doctype/leave_type/leave_type.py index 9cd574a598..da21f7827b 100644 --- a/erpnext/hr/doctype/leave_type/leave_type.py +++ b/erpnext/hr/doctype/leave_type/leave_type.py @@ -4,6 +4,7 @@ from __future__ import unicode_literals import calendar import frappe +from datetime import datetime from frappe import _ from frappe.model.document import Document @@ -14,6 +15,6 @@ class LeaveType(Document): self.validate_carry_forward() def validate_carry_forward(self): - max_days = 366 if calendar.isleap() else 365 - if not (1 < self.carry_forward_leave_expiry < max_days): + max_days = 366 if calendar.isleap(datetime.now().year) else 365 + if not (0 <= self.carry_forward_leave_expiry <= max_days): frappe.throw(_('Invalid entry!! Carried forward days need to expire within a year')) From 99c9cfaaedd069c4053a190a37a681ca2f79eea9 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Tue, 23 Apr 2019 18:28:28 +0530 Subject: [PATCH 11/98] feat: generate leave allocation for carry forwarded leaves --- .../hr/doctype/leave_period/leave_period.py | 38 ++++++++++++++----- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/erpnext/hr/doctype/leave_period/leave_period.py b/erpnext/hr/doctype/leave_period/leave_period.py index 15fa8d6f8c..1e2884d202 100644 --- a/erpnext/hr/doctype/leave_period/leave_period.py +++ b/erpnext/hr/doctype/leave_period/leave_period.py @@ -5,9 +5,10 @@ from __future__ import unicode_literals import frappe from frappe import _ -from frappe.utils import getdate, cstr +from frappe.utils import getdate, cstr, add_days from frappe.model.document import Document from erpnext.hr.utils import validate_overlap, get_employee_leave_policy +from erpnext.hr.doctype.leave_allocation.leave_allocation import get_carry_forwarded_leaves from frappe.utils.background_jobs import enqueue from six import iteritems @@ -39,8 +40,8 @@ class LeavePeriod(Document): employee=None, carry_forward_leaves=0): employees = self.get_employees({ "grade": grade, - "department": department, - "designation": designation, + "department": department, + "designation": designation, "name": employee }) @@ -57,7 +58,7 @@ def grant_leave_alloc_for_employees(employees, leave_period, carry_forward_leave leave_allocations = [] existing_allocations_for = get_existing_allocations(employees, leave_period.name) leave_type_details = get_leave_type_details() - count=0 + count = 0 for employee in employees: if employee in existing_allocations_for: continue @@ -77,8 +78,14 @@ def grant_leave_alloc_for_employees(employees, leave_period, carry_forward_leave def get_existing_allocations(employees, leave_period): leave_allocations = frappe.db.sql_list(""" - select distinct employee from `tabLeave Allocation` - where leave_period=%s and employee in (%s) and docstatus=1 + SELECT DISTINCT + employee + FROM `tabLeave Allocation` + WHERE + leave_period=%s + AND employee in (%s) + AND carry_forward=0 + AND docstatus=1 """ % ('%s', ', '.join(['%s']*len(employees))), [leave_period] + employees) if leave_allocations: frappe.msgprint(_("Skipping Leave Allocation for the following employees, as Leave Allocation records already exists against them. {0}") @@ -87,7 +94,8 @@ def get_existing_allocations(employees, leave_period): 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"]) + leave_types = frappe.get_all("Leave Type", + fields=["name", "is_lwp", "is_earned_leave", "is_compensatory", "is_carry_forward", "carry_forward_leave_expiry"]) for d in leave_types: leave_type_details.setdefault(d.name, d) return leave_type_details @@ -106,9 +114,19 @@ def create_leave_allocation(employee, leave_type, new_leaves_allocated, leave_ty allocation.leave_period = leave_period.name if carry_forward_leaves: if leave_type_details.get(leave_type).is_carry_forward: - allocation.carry_forward = carry_forward_leaves - allocation.save(ignore_permissions = True) + create_carry_forward_leaves_allocation(employee, leave_type, leave_type_details, leave_period, carry_forward_leaves) + allocation.save(ignore_permissions=True) allocation.submit() return allocation.name - +def create_carry_forward_leaves_allocation(employee, leave_type, leave_type_details, leave_period, carry_forward_leaves): + allocation = frappe.new_doc("Leave Allocation") + allocation.employee = employee + allocation.leave_type = leave_type + allocation.from_date = leave_period.from_date + allocation.to_date = add_days(leave_period.from_date, leave_type_details.carry_forward_leave_expiry) if not leave_type_details.carry_forward_leave_expiry else leave_period.to_date + allocation.leave_period = leave_period.name + allocation.carry_forward = carry_forward_leaves + allocation.carry_forwarded_leaves = get_carry_forwarded_leaves(employee, leave_type, leave_period.from_date, leave_type_details.carry_forward_leave_expiry) + allocation.save(ignore_permissions=True) + allocation.submit() \ No newline at end of file From 70cf4a67967ceb1525cc1f98340ae81c4180f1d8 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Wed, 24 Apr 2019 13:17:16 +0530 Subject: [PATCH 12/98] feat: validate leave allocation period to be within expiry limits --- .../leave_allocation/leave_allocation.py | 32 ++++++++++++++----- .../hr/doctype/leave_period/leave_period.py | 2 +- 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation.py b/erpnext/hr/doctype/leave_allocation/leave_allocation.py index 7f8f806f3b..0c00afff66 100755 --- a/erpnext/hr/doctype/leave_allocation/leave_allocation.py +++ b/erpnext/hr/doctype/leave_allocation/leave_allocation.py @@ -44,9 +44,17 @@ class LeaveAllocation(Document): self.validate_against_leave_applications() def validate_period(self): - if date_diff(self.to_date, self.from_date) <= 0: + allocation_period = date_diff(self.to_date, self.from_date) + + if allocation_period <= 0: frappe.throw(_("To date cannot be before from date")) + # check if the allocation period is more than the expiry allows for carry forwarded allocation + if self.carry_forward: + expiry_days = get_days_to_expiry_for_leave_type(self.leave_type) + if allocation_period > expiry_days: + frappe.throw(_("Leave allocation period cannot be more than the expiry days allocated")) + def validate_lwp(self): if frappe.db.get_value("Leave Type", self.leave_type, "is_lwp"): frappe.throw(_("Leave Type {0} cannot be allocated since it is leave without pay").format(self.leave_type)) @@ -115,13 +123,10 @@ class LeaveAllocation(Document): frappe.throw(_("Total allocated leaves {0} cannot be less than already approved leaves {1} for the period").format(self.total_leaves_allocated, leaves_taken), LessAllocationError) def set_carry_forwarded_leaves(self): - # check number of days to expire, ignore expiry for default value 0 - expiry_days = frappe.db.get_value("Leave Type", - filters={"leave_type": leave_type, "is_carry_forward": 1}, - fieldname="carry_forward_leave_expiry") + self.carry_forwarded_leaves = get_carry_forwarded_leaves(self.employee, self.leave_type, self.from_date) - self.carry_forwarded_leaves = get_carry_forwarded_leaves(self.employee, self.leave_type, self.from_date, expiry_days) max_leaves, leaves_allocated = self.get_max_leaves_with_leaves_allocated_for_leave_type(self.carry_forwarded_leaves) + if leaves_allocated > max_leaves: self.carry_forwarded_leaves = max_leaves - (leaves_allocated - self.carry_forwarded_leaves) @@ -168,9 +173,10 @@ def get_leave_allocation_for_period(employee, leave_type, from_date, to_date): return leave_allocated @frappe.whitelist() -def get_carry_forwarded_leaves(employee, leave_type, date, expiry_days): +def get_carry_forwarded_leaves(employee, leave_type, date): + ''' Calculates carry forwarded days based on previous unused leave allocations ''' carry_forwarded_leaves = 0 - + expiry_days = get_days_to_expiry_for_leave_type(leave_type) validate_carry_forward(leave_type) filters = { "employee": employee, @@ -179,6 +185,8 @@ def get_carry_forwarded_leaves(employee, leave_type, date, expiry_days): "to_date": ("<", date) } limit = 1 + + # check number of days to expire, ignore expiry for default value 0 if expiry_days: filters.update(carry_forward=0) limit = 2 @@ -197,6 +205,14 @@ def get_carry_forwarded_leaves(employee, leave_type, date, expiry_days): return carry_forwarded_leaves +def get_days_to_expiry_for_leave_type(leave_type): + ''' returns days to expiry for a provided leave type ''' + expiry_days = frappe.db.get_value("Leave Type", + filters={"leave_type": leave_type, "is_carry_forward": 1}, + fieldname="carry_forward_leave_expiry") + + + def validate_carry_forward(leave_type): if not frappe.db.get_value("Leave Type", leave_type, "is_carry_forward"): frappe.throw(_("Leave Type {0} cannot be carry-forwarded").format(leave_type)) \ No newline at end of file diff --git a/erpnext/hr/doctype/leave_period/leave_period.py b/erpnext/hr/doctype/leave_period/leave_period.py index 1e2884d202..0c83a1e548 100644 --- a/erpnext/hr/doctype/leave_period/leave_period.py +++ b/erpnext/hr/doctype/leave_period/leave_period.py @@ -127,6 +127,6 @@ def create_carry_forward_leaves_allocation(employee, leave_type, leave_type_deta allocation.to_date = add_days(leave_period.from_date, leave_type_details.carry_forward_leave_expiry) if not leave_type_details.carry_forward_leave_expiry else leave_period.to_date allocation.leave_period = leave_period.name allocation.carry_forward = carry_forward_leaves - allocation.carry_forwarded_leaves = get_carry_forwarded_leaves(employee, leave_type, leave_period.from_date, leave_type_details.carry_forward_leave_expiry) + allocation.carry_forwarded_leaves = get_carry_forwarded_leaves(employee, leave_type, leave_period.from_date) allocation.save(ignore_permissions=True) allocation.submit() \ No newline at end of file From d01863707c891a83305f05cc6da0457a163a28e0 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Thu, 25 Apr 2019 19:40:37 +0530 Subject: [PATCH 13/98] test: pass leave type as params --- .../leave_allocation/test_leave_allocation.py | 52 ++++++++----------- .../hr/doctype/leave_type/test_leave_type.py | 3 +- 2 files changed, 23 insertions(+), 32 deletions(-) diff --git a/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py b/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py index 3bc8dc458d..325f3a7299 100644 --- a/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py +++ b/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py @@ -1,7 +1,7 @@ from __future__ import unicode_literals import frappe import unittest -from frappe.utils import nowdate, add_months, getdate +from frappe.utils import nowdate, add_months, getdate, add_days from erpnext.hr.doctype.leave_type.test_leave_type import create_leave_type class TestLeaveAllocation(unittest.TestCase): @@ -71,36 +71,27 @@ class TestLeaveAllocation(unittest.TestCase): def test_carry_forward_allocation(self): frappe.db.sql("delete from `tabLeave Allocation`") - employee = frappe.get_doc("Employee", frappe.db.sql_list("select name from tabEmployee limit 1")[0]) - doc = frappe.get_doc({ - "doctype": "Leave Allocation", - "__islocal": 1, - "employee": employee.name, - "employee_name": employee.employee_name, - "leave_type": "_Test Leave Type Carry Forward", - "from_date": nowdate(), - "to_date": add_months(nowdate(),-12), - "new_leaves_allocated": 10 - }) - doc.save() - doc = frappe.get_doc({ - "doctype": "Leave Allocation", - "__islocal": 1, - "employee": employee.name, - "employee_name": employee.employee_name, - "leave_type": "_Test Leave Type Carry Forward", - "from_date": nowdate(), - "to_date": add_months(now_date(),12), - "carry_forward": 1 - }) - doc.save() - self.assertEquals(doc.total_leaves_allocated, 10) + leave_type = create_leave_type( + leave_type_name="_Test Carry Forward", + is_carry_forward=1, + carry_forward_leave_expiry=366) + leave_type.submit() + + leave_allocation = create_leave_allocation( + from_date=add_months(nowdate(), -12), + to_date=add_days(nowdate(), -1), + leave_type=leave_type + ) + leave_allocation.new_leaves_allocated = 10 + leave_allocation.submit() + + carry_forward_alloc = create_leave_allocation(leave_type=leave_type) + carry_forward_alloc.carry_forward = 1 + carry_forward_alloc.save() + self.assertEquals(carry_forward_alloc.total_leaves_allocated, 10) def create_leave_allocation(**args): args = frappe._dict(args) - if not frappe.db.exists("Leave Type", "_Test Leave Type"): - leave_type = create_leave_type(args.leave_type) - leave_type.insert() employee = frappe.get_doc("Employee", frappe.db.sql_list("select name from tabEmployee limit 1")[0]) leave_allocation = frappe.get_doc({ @@ -108,10 +99,9 @@ def create_leave_allocation(**args): "__islocal": 1, "employee": employee.name, "employee_name": employee.employee_name, - "leave_type": args.leave_type or "_Test Leave Type", + "leave_type": args.leave_type.leave_type_name or "_Test Leave Type", "from_date": args.from_date or nowdate(), - "to_date": args.to_date or add_months(nowdate(), 12), - "new_leaves_allocated": args.new_leaves_allocated or 20 + "to_date": args.to_date or add_months(nowdate(), 12) }) return leave_allocation diff --git a/erpnext/hr/doctype/leave_type/test_leave_type.py b/erpnext/hr/doctype/leave_type/test_leave_type.py index 671865b5a0..1006550de4 100644 --- a/erpnext/hr/doctype/leave_type/test_leave_type.py +++ b/erpnext/hr/doctype/leave_type/test_leave_type.py @@ -8,6 +8,7 @@ from frappe import _ test_records = frappe.get_test_records('Leave Type') def create_leave_type(**args): + args = frappe._dict(args) if frappe.db.exists("Leave Type", args.leave_type_name): return frappe.get_doc("Leave Type", args.leave_type_name) leave_type = frappe.get_doc({ @@ -18,7 +19,7 @@ def create_leave_type(**args): "is_earned_leave": args.is_earned_leave or 0, "is_lwp": args.is_lwp or 0, "is_carry_forward": args.is_carry_forward or 0, - "carry_forward_leave_expiry": args.is_carry_forward or 0, + "carry_forward_leave_expiry": args.carry_forward_leave_expiry or 0, "encashment_threshold_days": args.encashment_threshold_days or 5, "earning_component": "Leave Encashment" }) From 0abf5d340cbbfcc9d05cb51243ce9a47dca2d1e0 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Thu, 25 Apr 2019 19:52:45 +0530 Subject: [PATCH 14/98] fix: carry forwarded allocation period validation --- .../leave_allocation/leave_allocation.py | 31 +++++++++---------- .../hr/doctype/leave_period/leave_period.py | 5 ++- erpnext/hr/doctype/leave_type/leave_type.py | 4 +-- 3 files changed, 21 insertions(+), 19 deletions(-) diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation.py b/erpnext/hr/doctype/leave_allocation/leave_allocation.py index 0c00afff66..f7db7dee86 100755 --- a/erpnext/hr/doctype/leave_allocation/leave_allocation.py +++ b/erpnext/hr/doctype/leave_allocation/leave_allocation.py @@ -19,19 +19,19 @@ class LeaveAllocation(Document): def validate(self): self.validate_period() self.validate_lwp() + set_employee_name(self) + self.set_total_leaves_allocated() self.validate_allocation_overlap() self.validate_back_dated_allocation() self.validate_total_leaves_allocated() - set_employee_name(self) self.validate_leaves_allocated_value() self.validate_leave_allocation_days() - self.set_total_leaves_allocated() def validate_leave_allocation_days(self): - new_leaves = self.new_leaves_allocate if not self.carry_forward else self.carry_forwarded_leaves + new_leaves = self.new_leaves_allocated if not self.carry_forward else self.carry_forwarded_leaves max_leaves, leaves_allocated = self.get_max_leaves_with_leaves_allocated_for_leave_type(flt(new_leaves)) - if leave_allocated > max_leaves: + if leaves_allocated > max_leaves: frappe.throw(_("Total allocated leaves are more days than maximum allocation of {0} leave type for employee {1} in the period")\ .format(self.leave_type, self.employee)) @@ -52,8 +52,9 @@ class LeaveAllocation(Document): # check if the allocation period is more than the expiry allows for carry forwarded allocation if self.carry_forward: expiry_days = get_days_to_expiry_for_leave_type(self.leave_type) - if allocation_period > expiry_days: - frappe.throw(_("Leave allocation period cannot be more than the expiry days allocated")) + + if allocation_period > flt(expiry_days) and expiry_days: + frappe.throw(_("Leave allocation period cannot exceed carry forward expiry limit")) def validate_lwp(self): if frappe.db.get_value("Leave Type", self.leave_type, "is_lwp"): @@ -109,7 +110,7 @@ class LeaveAllocation(Document): def validate_total_leaves_allocated(self): # Adding a day to include To Date in the difference date_difference = date_diff(self.to_date, self.from_date) + 1 - if date_difference < self.total_leaves_allocated: + if date_difference < flt(self.total_leaves_allocated): frappe.throw(_("Total allocated leaves are more than days in the period"), OverAllocationError) def validate_against_leave_applications(self): @@ -133,13 +134,13 @@ class LeaveAllocation(Document): def get_max_leaves_with_leaves_allocated_for_leave_type(self, new_leaves): ''' compare new leaves allocated with max leaves ''' company = frappe.db.get_value("Employee", self.employee, "company") + leaves_allocated = 0 leave_period = get_leave_period(self.from_date, self.to_date, company) max_leaves_allowed = frappe.db.get_value("Leave Type", self.leave_type, "max_leaves_allowed") if max_leaves_allowed > 0: - leave_allocated = 0 if leave_period: - leave_allocated = get_leave_allocation_for_period(self.employee, self.leave_type, leave_period[0].from_date, leave_period[0].to_date) - leave_allocated += new_leaves + leaves_allocated = get_leave_allocation_for_period(self.employee, self.leave_type, leave_period[0].from_date, leave_period[0].to_date) + leaves_allocated += new_leaves return max_leaves_allowed, leaves_allocated def get_leave_allocation_for_period(employee, leave_type, from_date, to_date): @@ -184,12 +185,12 @@ def get_carry_forwarded_leaves(employee, leave_type, date): "docstatus": 1, "to_date": ("<", date) } - limit = 1 + limit = 2 # check number of days to expire, ignore expiry for default value 0 if expiry_days: filters.update(carry_forward=0) - limit = 2 + limit = 1 previous_allocation = frappe.get_all("Leave Allocation", filters=filters, @@ -207,12 +208,10 @@ def get_carry_forwarded_leaves(employee, leave_type, date): def get_days_to_expiry_for_leave_type(leave_type): ''' returns days to expiry for a provided leave type ''' - expiry_days = frappe.db.get_value("Leave Type", - filters={"leave_type": leave_type, "is_carry_forward": 1}, + return frappe.db.get_value("Leave Type", + filters={"leave_type_name": leave_type, "is_carry_forward": 1}, fieldname="carry_forward_leave_expiry") - - def validate_carry_forward(leave_type): if not frappe.db.get_value("Leave Type", leave_type, "is_carry_forward"): frappe.throw(_("Leave Type {0} cannot be carry-forwarded").format(leave_type)) \ No newline at end of file diff --git a/erpnext/hr/doctype/leave_period/leave_period.py b/erpnext/hr/doctype/leave_period/leave_period.py index 0c83a1e548..a6198a8918 100644 --- a/erpnext/hr/doctype/leave_period/leave_period.py +++ b/erpnext/hr/doctype/leave_period/leave_period.py @@ -120,6 +120,9 @@ def create_leave_allocation(employee, leave_type, new_leaves_allocated, leave_ty return allocation.name def create_carry_forward_leaves_allocation(employee, leave_type, leave_type_details, leave_period, carry_forward_leaves): + carry_forwarded_leaves = get_carry_forwarded_leaves(employee, leave_type, leave_period.from_date) + if not carry_forwarded_leaves: + return allocation = frappe.new_doc("Leave Allocation") allocation.employee = employee allocation.leave_type = leave_type @@ -127,6 +130,6 @@ def create_carry_forward_leaves_allocation(employee, leave_type, leave_type_deta allocation.to_date = add_days(leave_period.from_date, leave_type_details.carry_forward_leave_expiry) if not leave_type_details.carry_forward_leave_expiry else leave_period.to_date allocation.leave_period = leave_period.name allocation.carry_forward = carry_forward_leaves - allocation.carry_forwarded_leaves = get_carry_forwarded_leaves(employee, leave_type, leave_period.from_date) + allocation.carry_forwarded_leaves = carry_forwarded_leaves allocation.save(ignore_permissions=True) allocation.submit() \ No newline at end of file diff --git a/erpnext/hr/doctype/leave_type/leave_type.py b/erpnext/hr/doctype/leave_type/leave_type.py index da21f7827b..dcae5fe085 100644 --- a/erpnext/hr/doctype/leave_type/leave_type.py +++ b/erpnext/hr/doctype/leave_type/leave_type.py @@ -15,6 +15,6 @@ class LeaveType(Document): self.validate_carry_forward() def validate_carry_forward(self): - max_days = 366 if calendar.isleap(datetime.now().year) else 365 + max_days = 367 if calendar.isleap(datetime.now().year) else 366 if not (0 <= self.carry_forward_leave_expiry <= max_days): - frappe.throw(_('Invalid entry!! Carried forward days need to expire within a year')) + frappe.throw(_('Invalid entry!! Carried forward days need to expire within a year')) \ No newline at end of file From 5e2b067107b5ab18d7cc1e863b5cd8d908ee23f7 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Thu, 25 Apr 2019 19:54:40 +0530 Subject: [PATCH 15/98] feat: display carry forwarded allocation days and total leaves allocated --- .../leave_allocation/leave_allocation.js | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation.js b/erpnext/hr/doctype/leave_allocation/leave_allocation.js index 4b4bfafd16..0014c07a3c 100755 --- a/erpnext/hr/doctype/leave_allocation/leave_allocation.js +++ b/erpnext/hr/doctype/leave_allocation/leave_allocation.js @@ -34,34 +34,31 @@ frappe.ui.form.on("Leave Allocation", { }, carry_forwarded_leaves: function(frm) { - frm.set_value("total_leaves_allocated", - flt(frm.doc.carry_forwarded_leaves) + flt(frm.doc.new_leaves_allocated)); + frm.set_value("total_leaves_allocated", flt(frm.doc.carry_forwarded_leaves)); }, new_leaves_allocated: function(frm) { - frm.set_value("total_leaves_allocated", - flt(frm.doc.carry_forwarded_leaves) + flt(frm.doc.new_leaves_allocated)); + frm.set_value("total_leaves_allocated", flt(frm.doc.new_leaves_allocated)); }, calculate_total_leaves_allocated: function(frm) { - if (cint(frm.doc.carry_forward) == 1 && frm.doc.leave_type && frm.doc.employee) { + if (cint(frm.doc.carry_forward) === 1 && frm.doc.leave_type && frm.doc.employee) { return frappe.call({ method: "erpnext.hr.doctype.leave_allocation.leave_allocation.get_carry_forwarded_leaves", args: { "employee": frm.doc.employee, - "date": frm.doc.from_date, "leave_type": frm.doc.leave_type, - "carry_forward": frm.doc.carry_forward + "date": frm.doc.from_date, }, callback: function(r) { if (!r.exc && r.message) { + frm.set_value("new_leaves_allocated", 0); frm.set_value('carry_forwarded_leaves', r.message); - frm.set_value("total_leaves_allocated", - flt(r.message) + flt(frm.doc.new_leaves_allocated)); + frm.set_value("total_leaves_allocated", flt(r.message)); } } }) - } 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("total_leaves_allocated", flt(frm.doc.new_leaves_allocated)); } From 1de990b2ac71f95468285cdce119f83295906210 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Thu, 9 May 2019 16:51:02 +0530 Subject: [PATCH 16/98] feat: create leave ledger entry --- .../hr/doctype/leave_ledger_entry/__init__.py | 0 .../leave_ledger_entry/leave_ledger_entry.js | 8 ++ .../leave_ledger_entry.json | 99 +++++++++++++++++++ .../leave_ledger_entry/leave_ledger_entry.py | 39 ++++++++ .../test_leave_ledger_entry.py | 10 ++ 5 files changed, 156 insertions(+) create mode 100644 erpnext/hr/doctype/leave_ledger_entry/__init__.py create mode 100644 erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.js create mode 100644 erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.json create mode 100644 erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py create mode 100644 erpnext/hr/doctype/leave_ledger_entry/test_leave_ledger_entry.py diff --git a/erpnext/hr/doctype/leave_ledger_entry/__init__.py b/erpnext/hr/doctype/leave_ledger_entry/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.js b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.js new file mode 100644 index 0000000000..c68d518f67 --- /dev/null +++ b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.js @@ -0,0 +1,8 @@ +// Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Leave Ledger Entry', { + // refresh: function(frm) { + + // } +}); diff --git a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.json b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.json new file mode 100644 index 0000000000..8ef302452d --- /dev/null +++ b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.json @@ -0,0 +1,99 @@ +{ + "creation": "2019-05-09 15:47:39.760406", + "doctype": "DocType", + "engine": "InnoDB", + "field_order": [ + "employee", + "employee_name", + "leave_type", + "transaction_type", + "transaction_name", + "leaves", + "from_date", + "to_date", + "is_carry_forward", + "amended_from" + ], + "fields": [ + { + "fieldname": "employee", + "fieldtype": "Link", + "label": "Employee", + "options": "Employee" + }, + { + "fieldname": "employee_name", + "fieldtype": "Data", + "label": "Employee Name" + }, + { + "fieldname": "leave_type", + "fieldtype": "Link", + "label": "Leave Type", + "options": "Leave Type" + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Leave Ledger Entry", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "transaction_type", + "fieldtype": "Link", + "label": "Transaction Type", + "options": "DocType" + }, + { + "fieldname": "transaction_name", + "fieldtype": "Dynamic Link", + "label": "Transaction Name", + "options": "transaction_type" + }, + { + "fieldname": "leaves", + "fieldtype": "Int", + "label": "Leaves" + }, + { + "fieldname": "from_date", + "fieldtype": "Date", + "label": "From Date" + }, + { + "fieldname": "to_date", + "fieldtype": "Date", + "label": "To Date" + }, + { + "fieldname": "is_carry_forward", + "fieldtype": "Check", + "label": "Is Carry Forward" + } + ], + "is_submittable": 1, + "modified": "2019-05-09 15:54:52.834794", + "modified_by": "Administrator", + "module": "HR", + "name": "Leave Ledger Entry", + "owner": "Administrator", + "permissions": [ + { + "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": "ASC" +} \ No newline at end of file diff --git a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py new file mode 100644 index 0000000000..dd8de56717 --- /dev/null +++ b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2019, 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.utils import add_days + +class LeaveLedgerEntry(Document): + pass + +def create_leave_ledger_entry(ref_doc, submit=True): + ledger = dict( + doctype='Leave Ledger Entry', + employee=ref_doc.employee, + employee_name=ref_doc.employee_name, + leave_type=ref_doc.leave_type, + from_date=ref_doc.from_date, + transaction_document_type=ref_doc.doctype, + transaction_document_name=ref_doc.name + ) + + if ref_doc.carry_forwarded_leaves: + expiry_days = frappe.db.get_value("Leave Type", ref_doc.leave_type, "carry_forward_leave_expiry") + + ledger.update(dict( + leaves=ref_doc.carry_forwarded_leaves * 1 if submit else -1, + to_date=add_days(ref_doc.from_date, expiry_days) if expiry_days else ref_doc.to_date, + is_carry_forward=1 + )) + frappe.get_doc(ledger).insert() + + ledger.update(dict( + leaves=ref_doc.new_leaves_allocated * 1 if submit else -1, + to_date=ref_doc.to_date, + is_carry_forward=0 + )) + frappe.get_doc(ledger).insert() \ No newline at end of file diff --git a/erpnext/hr/doctype/leave_ledger_entry/test_leave_ledger_entry.py b/erpnext/hr/doctype/leave_ledger_entry/test_leave_ledger_entry.py new file mode 100644 index 0000000000..6f7725c254 --- /dev/null +++ b/erpnext/hr/doctype/leave_ledger_entry/test_leave_ledger_entry.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +# import frappe +import unittest + +class TestLeaveLedgerEntry(unittest.TestCase): + pass From 2b421c39b5c0dbf7ea8e5a9a567bd124b527af15 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Thu, 9 May 2019 19:36:01 +0530 Subject: [PATCH 17/98] feat: add transaction details in ledger --- .../leave_allocation/leave_allocation.json | 1491 ++++++++--------- .../leave_ledger_entry.json | 2 +- .../leave_ledger_entry/leave_ledger_entry.py | 11 +- 3 files changed, 741 insertions(+), 763 deletions(-) diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation.json b/erpnext/hr/doctype/leave_allocation/leave_allocation.json index 0903398d1f..12c74de9d0 100644 --- a/erpnext/hr/doctype/leave_allocation/leave_allocation.json +++ b/erpnext/hr/doctype/leave_allocation/leave_allocation.json @@ -1,757 +1,736 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 1, - "allow_rename": 0, - "autoname": "naming_series:", - "beta": 0, - "creation": "2013-02-20 19:10:38", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "Setup", - "editable_grid": 0, - "engine": "InnoDB", - "fields": [ - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", - "fetch_if_empty": 0, - "fieldname": "naming_series", - "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": 0, - "label": "Series", - "length": 0, - "no_copy": 1, - "options": "HR-LAL-.YYYY.-", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 1, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "employee", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 1, - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Employee", - "length": 0, - "no_copy": 0, - "oldfieldname": "employee", - "oldfieldtype": "Link", - "options": "Employee", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 1, - "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, - "fetch_from": "employee.employee_name", - "fetch_if_empty": 0, - "fieldname": "employee_name", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 1, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Employee Name", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 1, - "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, - "fetch_from": "employee.department", - "fetch_if_empty": 0, - "fieldname": "department", - "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": "Department", - "length": 0, - "no_copy": 0, - "options": "Department", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "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, - "fetch_if_empty": 0, - "fieldname": "column_break1", - "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, - "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, - "width": "50%" - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "leave_type", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Leave Type", - "length": 0, - "no_copy": 0, - "oldfieldname": "leave_type", - "oldfieldtype": "Link", - "options": "Leave Type", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 1, - "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, - "fetch_if_empty": 0, - "fieldname": "from_date", - "fieldtype": "Date", - "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": "From Date", - "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": 1, - "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, - "fetch_if_empty": 0, - "fieldname": "to_date", - "fieldtype": "Date", - "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": "To Date", - "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": 1, - "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, - "fetch_if_empty": 0, - "fieldname": "section_break_6", - "fieldtype": "Section 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, - "label": "Allocation", - "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, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 1, - "bold": 1, - "collapsible": 0, - "columns": 0, - "depends_on": "eval: doc.carry_forward != 1", - "fetch_if_empty": 0, - "fieldname": "new_leaves_allocated", - "fieldtype": "Float", - "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": "New Leaves Allocated", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "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, - "depends_on": "eval: doc.carry_forward == 1", - "fetch_if_empty": 0, - "fieldname": "carry_forwarded_leaves", - "fieldtype": "Int", - "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": "Unused Leaves", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "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, - "depends_on": "", - "description": "", - "fetch_if_empty": 0, - "fieldname": "carry_forward", - "fieldtype": "Check", - "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": "Allocate unused leaves from previous allocations", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "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": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fetch_if_empty": 0, - "fieldname": "total_leaves_allocated", - "fieldtype": "Float", - "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": "Total Leaves Allocated", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "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, - "depends_on": "eval:doc.total_leaves_encashed>0", - "fetch_if_empty": 0, - "fieldname": "total_leaves_encashed", - "fieldtype": "Float", - "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": "Total Leaves Encashed", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "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, - "fetch_if_empty": 0, - "fieldname": "column_break_10", - "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, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "compensatory_request", - "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": "Compensatory Leave Request", - "length": 0, - "no_copy": 0, - "options": "Compensatory Leave Request", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "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, - "fetch_if_empty": 0, - "fieldname": "leave_period", - "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": 1, - "label": "Leave Period", - "length": 0, - "no_copy": 0, - "options": "Leave Period", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "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, - "fetch_if_empty": 0, - "fieldname": "amended_from", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Amended From", - "length": 0, - "no_copy": 1, - "oldfieldname": "amended_from", - "oldfieldtype": "Data", - "options": "Leave Allocation", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "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": 1, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "notes", - "fieldtype": "Section 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, - "label": "Notes", - "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, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "description", - "fieldtype": "Small Text", - "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": "Description", - "length": 0, - "no_copy": 0, - "oldfieldname": "reason", - "oldfieldtype": "Small Text", - "permlevel": 0, - "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, - "width": "300px" - } - ], - "has_web_view": 0, - "hide_toolbar": 0, - "icon": "fa fa-ok", - "idx": 1, - "in_create": 0, - "is_submittable": 1, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2019-04-22 18:33:15.858006", - "modified_by": "Administrator", - "module": "HR", - "name": "Leave Allocation", - "owner": "Administrator", - "permissions": [ - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "set_user_permissions": 0, - "share": 1, - "submit": 1, - "write": 1 - }, - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 1, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 1, - "write": 1 - } - ], - "quick_entry": 0, - "read_only": 0, - "search_fields": "employee,employee_name,leave_type,total_leaves_allocated", - "show_name_in_global_search": 1, - "sort_field": "modified", - "sort_order": "DESC", - "timeline_field": "employee", - "track_changes": 0, - "track_seen": 0, - "track_views": 0 -} \ No newline at end of file + "allow_copy": 0, + "allow_events_in_timeline": 0, + "allow_guest_to_view": 0, + "allow_import": 1, + "allow_rename": 0, + "autoname": "naming_series:", + "beta": 0, + "creation": "2013-02-20 19:10:38", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "Setup", + "editable_grid": 0, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "", + "fieldname": "naming_series", + "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": 0, + "label": "Series", + "length": 0, + "no_copy": 1, + "options": "HR-LAL-.YYYY.-", + "permlevel": 0, + "precision": "", + "print_hide": 1, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 1, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "employee", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 1, + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Employee", + "length": 0, + "no_copy": 0, + "oldfieldname": "employee", + "oldfieldtype": "Link", + "options": "Employee", + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 1, + "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, + "fetch_from": "employee.employee_name", + "fieldname": "employee_name", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 1, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Employee Name", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 1, + "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, + "fetch_from": "employee.department", + "fieldname": "department", + "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": "Department", + "length": 0, + "no_copy": 0, + "options": "Department", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "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": "column_break1", + "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, + "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, + "width": "50%" + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "leave_type", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Leave Type", + "length": 0, + "no_copy": 0, + "oldfieldname": "leave_type", + "oldfieldtype": "Link", + "options": "Leave Type", + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 1, + "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": "from_date", + "fieldtype": "Date", + "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": "From Date", + "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": 1, + "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": "to_date", + "fieldtype": "Date", + "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": "To Date", + "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": 1, + "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": "section_break_6", + "fieldtype": "Section 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, + "label": "Allocation", + "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, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 1, + "bold": 1, + "collapsible": 0, + "columns": 0, + "fieldname": "new_leaves_allocated", + "fieldtype": "Float", + "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": "New Leaves Allocated", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "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, + "description": "", + "fieldname": "carry_forward", + "fieldtype": "Check", + "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": "Add unused leaves from previous allocations", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "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, + "depends_on": "carry_forward", + "fieldname": "carry_forwarded_leaves", + "fieldtype": "Float", + "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": "Unused leaves", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "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": 1, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "total_leaves_allocated", + "fieldtype": "Float", + "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": "Total Leaves Allocated", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "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, + "depends_on": "eval:doc.total_leaves_encashed>0", + "fieldname": "total_leaves_encashed", + "fieldtype": "Float", + "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": "Total Leaves Encashed", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "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": "column_break_10", + "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, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "compensatory_request", + "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": "Compensatory Leave Request", + "length": 0, + "no_copy": 0, + "options": "Compensatory Leave Request", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "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": "leave_period", + "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": 1, + "label": "Leave Period", + "length": 0, + "no_copy": 0, + "options": "Leave Period", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "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": "amended_from", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 1, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Amended From", + "length": 0, + "no_copy": 1, + "oldfieldname": "amended_from", + "oldfieldtype": "Data", + "options": "Leave Allocation", + "permlevel": 0, + "print_hide": 1, + "print_hide_if_no_value": 0, + "read_only": 1, + "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": 1, + "columns": 0, + "fieldname": "notes", + "fieldtype": "Section 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, + "label": "Notes", + "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, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "description", + "fieldtype": "Small Text", + "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": "Description", + "length": 0, + "no_copy": 0, + "oldfieldname": "reason", + "oldfieldtype": "Small Text", + "permlevel": 0, + "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, + "width": "300px" + } + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "icon": "fa fa-ok", + "idx": 1, + "image_view": 0, + "in_create": 0, + "is_submittable": 1, + "issingle": 0, + "istable": 0, + "max_attachments": 0, + "modified": "2019-01-30 11:28:09.360525", + "modified_by": "Administrator", + "module": "HR", + "name": "Leave Allocation", + "owner": "Administrator", + "permissions": [ + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 0, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "set_user_permissions": 0, + "share": 1, + "submit": 1, + "write": 1 + }, + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 1, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "set_user_permissions": 0, + "share": 1, + "submit": 1, + "write": 1 + } + ], + "quick_entry": 0, + "read_only": 0, + "read_only_onload": 0, + "search_fields": "employee,employee_name,leave_type,total_leaves_allocated", + "show_name_in_global_search": 1, + "sort_field": "modified", + "sort_order": "DESC", + "timeline_field": "employee", + "track_changes": 0, + "track_seen": 0, + "track_views": 0 + } \ No newline at end of file diff --git a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.json b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.json index 8ef302452d..c7e6b709bd 100644 --- a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.json +++ b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.json @@ -75,7 +75,7 @@ } ], "is_submittable": 1, - "modified": "2019-05-09 15:54:52.834794", + "modified": "2019-05-09 18:36:07.383714", "modified_by": "Administrator", "module": "HR", "name": "Leave Ledger Entry", diff --git a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py index dd8de56717..35dce97936 100644 --- a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py +++ b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py @@ -3,7 +3,7 @@ # For license information, please see license.txt from __future__ import unicode_literals -# import frappe +import frappe from frappe.model.document import Document from frappe.utils import add_days @@ -17,10 +17,9 @@ def create_leave_ledger_entry(ref_doc, submit=True): employee_name=ref_doc.employee_name, leave_type=ref_doc.leave_type, from_date=ref_doc.from_date, - transaction_document_type=ref_doc.doctype, - transaction_document_name=ref_doc.name + transaction_type=ref_doc.doctype, + transaction_name=ref_doc.name ) - if ref_doc.carry_forwarded_leaves: expiry_days = frappe.db.get_value("Leave Type", ref_doc.leave_type, "carry_forward_leave_expiry") @@ -29,11 +28,11 @@ def create_leave_ledger_entry(ref_doc, submit=True): to_date=add_days(ref_doc.from_date, expiry_days) if expiry_days else ref_doc.to_date, is_carry_forward=1 )) - frappe.get_doc(ledger).insert() + frappe.get_doc(ledger).submit() ledger.update(dict( leaves=ref_doc.new_leaves_allocated * 1 if submit else -1, to_date=ref_doc.to_date, is_carry_forward=0 )) - frappe.get_doc(ledger).insert() \ No newline at end of file + frappe.get_doc(ledger).submit() \ No newline at end of file From 679371e397a840267d1e29b7d19bdcccf5598f5a Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Thu, 9 May 2019 19:43:35 +0530 Subject: [PATCH 18/98] feat: get carry forwarded leaves via ledger entries --- .../leave_allocation/leave_allocation.js | 19 +- .../leave_allocation/leave_allocation.json | 965 +++++------------- .../leave_allocation/leave_allocation.py | 159 +-- .../hr/doctype/leave_period/leave_period.py | 21 +- 4 files changed, 293 insertions(+), 871 deletions(-) diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation.js b/erpnext/hr/doctype/leave_allocation/leave_allocation.js index 0014c07a3c..228b5528de 100755 --- a/erpnext/hr/doctype/leave_allocation/leave_allocation.js +++ b/erpnext/hr/doctype/leave_allocation/leave_allocation.js @@ -34,33 +34,36 @@ frappe.ui.form.on("Leave Allocation", { }, carry_forwarded_leaves: function(frm) { - frm.set_value("total_leaves_allocated", flt(frm.doc.carry_forwarded_leaves)); + frm.set_value("total_leaves_allocated", + flt(frm.doc.carry_forwarded_leaves) + flt(frm.doc.new_leaves_allocated)); }, new_leaves_allocated: function(frm) { - frm.set_value("total_leaves_allocated", flt(frm.doc.new_leaves_allocated)); + frm.set_value("total_leaves_allocated", + flt(frm.doc.carry_forwarded_leaves) + flt(frm.doc.new_leaves_allocated)); }, calculate_total_leaves_allocated: function(frm) { - if (cint(frm.doc.carry_forward) === 1 && frm.doc.leave_type && frm.doc.employee) { + if (cint(frm.doc.carry_forward) == 1 && frm.doc.leave_type && frm.doc.employee) { return frappe.call({ method: "erpnext.hr.doctype.leave_allocation.leave_allocation.get_carry_forwarded_leaves", args: { "employee": frm.doc.employee, - "leave_type": frm.doc.leave_type, "date": frm.doc.from_date, + "leave_type": frm.doc.leave_type, + "carry_forward": frm.doc.carry_forward }, callback: function(r) { if (!r.exc && r.message) { - frm.set_value("new_leaves_allocated", 0); frm.set_value('carry_forwarded_leaves', r.message); - frm.set_value("total_leaves_allocated", flt(r.message)); + frm.set_value("total_leaves_allocated", + flt(r.message) + flt(frm.doc.new_leaves_allocated)); } } }) - } 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("total_leaves_allocated", flt(frm.doc.new_leaves_allocated)); } } -}) +}) \ No newline at end of file diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation.json b/erpnext/hr/doctype/leave_allocation/leave_allocation.json index 12c74de9d0..bb851c6850 100644 --- a/erpnext/hr/doctype/leave_allocation/leave_allocation.json +++ b/erpnext/hr/doctype/leave_allocation/leave_allocation.json @@ -1,736 +1,231 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 1, - "allow_rename": 0, - "autoname": "naming_series:", - "beta": 0, - "creation": "2013-02-20 19:10:38", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "Setup", - "editable_grid": 0, - "engine": "InnoDB", - "fields": [ - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", - "fieldname": "naming_series", - "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": 0, - "label": "Series", - "length": 0, - "no_copy": 1, - "options": "HR-LAL-.YYYY.-", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 1, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "employee", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 1, - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Employee", - "length": 0, - "no_copy": 0, - "oldfieldname": "employee", - "oldfieldtype": "Link", - "options": "Employee", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 1, - "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, - "fetch_from": "employee.employee_name", - "fieldname": "employee_name", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 1, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Employee Name", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 1, - "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, - "fetch_from": "employee.department", - "fieldname": "department", - "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": "Department", - "length": 0, - "no_copy": 0, - "options": "Department", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "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": "column_break1", - "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, - "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, - "width": "50%" - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "leave_type", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Leave Type", - "length": 0, - "no_copy": 0, - "oldfieldname": "leave_type", - "oldfieldtype": "Link", - "options": "Leave Type", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 1, - "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": "from_date", - "fieldtype": "Date", - "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": "From Date", - "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": 1, - "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": "to_date", - "fieldtype": "Date", - "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": "To Date", - "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": 1, - "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": "section_break_6", - "fieldtype": "Section 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, - "label": "Allocation", - "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, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 1, - "bold": 1, - "collapsible": 0, - "columns": 0, - "fieldname": "new_leaves_allocated", - "fieldtype": "Float", - "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": "New Leaves Allocated", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "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, - "description": "", - "fieldname": "carry_forward", - "fieldtype": "Check", - "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": "Add unused leaves from previous allocations", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "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, - "depends_on": "carry_forward", - "fieldname": "carry_forwarded_leaves", - "fieldtype": "Float", - "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": "Unused leaves", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "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": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "total_leaves_allocated", - "fieldtype": "Float", - "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": "Total Leaves Allocated", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "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, - "depends_on": "eval:doc.total_leaves_encashed>0", - "fieldname": "total_leaves_encashed", - "fieldtype": "Float", - "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": "Total Leaves Encashed", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "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": "column_break_10", - "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, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "compensatory_request", - "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": "Compensatory Leave Request", - "length": 0, - "no_copy": 0, - "options": "Compensatory Leave Request", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "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": "leave_period", - "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": 1, - "label": "Leave Period", - "length": 0, - "no_copy": 0, - "options": "Leave Period", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "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": "amended_from", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Amended From", - "length": 0, - "no_copy": 1, - "oldfieldname": "amended_from", - "oldfieldtype": "Data", - "options": "Leave Allocation", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "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": 1, - "columns": 0, - "fieldname": "notes", - "fieldtype": "Section 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, - "label": "Notes", - "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, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "description", - "fieldtype": "Small Text", - "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": "Description", - "length": 0, - "no_copy": 0, - "oldfieldname": "reason", - "oldfieldtype": "Small Text", - "permlevel": 0, - "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, - "width": "300px" - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "icon": "fa fa-ok", - "idx": 1, - "image_view": 0, - "in_create": 0, - "is_submittable": 1, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2019-01-30 11:28:09.360525", - "modified_by": "Administrator", - "module": "HR", - "name": "Leave Allocation", - "owner": "Administrator", - "permissions": [ - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "set_user_permissions": 0, - "share": 1, - "submit": 1, - "write": 1 - }, - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 1, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 1, - "write": 1 - } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "search_fields": "employee,employee_name,leave_type,total_leaves_allocated", - "show_name_in_global_search": 1, - "sort_field": "modified", - "sort_order": "DESC", - "timeline_field": "employee", - "track_changes": 0, - "track_seen": 0, - "track_views": 0 - } \ No newline at end of file + "allow_import": 1, + "autoname": "naming_series:", + "creation": "2013-02-20 19:10:38", + "doctype": "DocType", + "document_type": "Setup", + "engine": "InnoDB", + "field_order": [ + "naming_series", + "employee", + "employee_name", + "department", + "column_break1", + "leave_type", + "from_date", + "to_date", + "section_break_6", + "new_leaves_allocated", + "carry_forward", + "carry_forwarded_leaves", + "total_leaves_allocated", + "total_leaves_encashed", + "column_break_10", + "compensatory_request", + "leave_period", + "amended_from", + "notes", + "description" + ], + "fields": [ + { + "fieldname": "naming_series", + "fieldtype": "Select", + "label": "Series", + "no_copy": 1, + "options": "HR-LAL-.YYYY.-", + "print_hide": 1, + "reqd": 1, + "set_only_once": 1 + }, + { + "fieldname": "employee", + "fieldtype": "Link", + "in_global_search": 1, + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Employee", + "oldfieldname": "employee", + "oldfieldtype": "Link", + "options": "Employee", + "reqd": 1, + "search_index": 1 + }, + { + "fetch_from": "employee.employee_name", + "fieldname": "employee_name", + "fieldtype": "Data", + "in_global_search": 1, + "in_list_view": 1, + "label": "Employee Name", + "read_only": 1, + "search_index": 1 + }, + { + "fetch_from": "employee.department", + "fieldname": "department", + "fieldtype": "Link", + "label": "Department", + "options": "Department", + "read_only": 1 + }, + { + "fieldname": "column_break1", + "fieldtype": "Column Break", + "width": "50%" + }, + { + "fieldname": "leave_type", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Leave Type", + "oldfieldname": "leave_type", + "oldfieldtype": "Link", + "options": "Leave Type", + "reqd": 1, + "search_index": 1 + }, + { + "fieldname": "from_date", + "fieldtype": "Date", + "label": "From Date", + "reqd": 1 + }, + { + "fieldname": "to_date", + "fieldtype": "Date", + "label": "To Date", + "reqd": 1 + }, + { + "fieldname": "section_break_6", + "fieldtype": "Section Break", + "label": "Allocation" + }, + { + "allow_on_submit": 1, + "bold": 1, + "fieldname": "new_leaves_allocated", + "fieldtype": "Float", + "label": "New Leaves Allocated" + }, + { + "fieldname": "carry_forward", + "fieldtype": "Check", + "label": "Add unused leaves from previous allocations" + }, + { + "depends_on": "carry_forward", + "fieldname": "carry_forwarded_leaves", + "fieldtype": "Float", + "label": "Unused leaves", + "read_only": 1 + }, + { + "allow_on_submit": 1, + "fieldname": "total_leaves_allocated", + "fieldtype": "Float", + "label": "Total Leaves Allocated", + "read_only": 1, + "reqd": 1 + }, + { + "depends_on": "eval:doc.total_leaves_encashed>0", + "fieldname": "total_leaves_encashed", + "fieldtype": "Float", + "label": "Total Leaves Encashed", + "read_only": 1 + }, + { + "fieldname": "column_break_10", + "fieldtype": "Column Break" + }, + { + "fieldname": "compensatory_request", + "fieldtype": "Link", + "label": "Compensatory Leave Request", + "options": "Compensatory Leave Request", + "read_only": 1 + }, + { + "fieldname": "leave_period", + "fieldtype": "Link", + "in_standard_filter": 1, + "label": "Leave Period", + "options": "Leave Period", + "read_only": 1 + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Amended From", + "no_copy": 1, + "oldfieldname": "amended_from", + "oldfieldtype": "Data", + "options": "Leave Allocation", + "print_hide": 1, + "read_only": 1 + }, + { + "collapsible": 1, + "fieldname": "notes", + "fieldtype": "Section Break", + "label": "Notes" + }, + { + "fieldname": "description", + "fieldtype": "Small Text", + "label": "Description", + "oldfieldname": "reason", + "oldfieldtype": "Small Text", + "width": "300px" + } + ], + "icon": "fa fa-ok", + "idx": 1, + "is_submittable": 1, + "modified": "2019-05-09 19:06:33.659196", + "modified_by": "Administrator", + "module": "HR", + "name": "Leave Allocation", + "owner": "Administrator", + "permissions": [ + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "import": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "submit": 1, + "write": 1 + } + ], + "search_fields": "employee,employee_name,leave_type,total_leaves_allocated", + "show_name_in_global_search": 1, + "sort_field": "modified", + "sort_order": "DESC", + "timeline_field": "employee" +} \ No newline at end of file diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation.py b/erpnext/hr/doctype/leave_allocation/leave_allocation.py index f7db7dee86..aa1cc9ecad 100755 --- a/erpnext/hr/doctype/leave_allocation/leave_allocation.py +++ b/erpnext/hr/doctype/leave_allocation/leave_allocation.py @@ -8,6 +8,7 @@ from frappe import _ from frappe.model.document import Document from erpnext.hr.utils import set_employee_name, get_leave_period from erpnext.hr.doctype.leave_application.leave_application import get_approved_leaves_for_period +from erpnext.hr.doctype.leave_ledger_entry.leave_ledger_entry import create_leave_ledger_entry class OverlapError(frappe.ValidationError): pass class BackDatedAllocationError(frappe.ValidationError): pass @@ -18,66 +19,62 @@ class ValueMultiplierError(frappe.ValidationError): pass class LeaveAllocation(Document): def validate(self): self.validate_period() - self.validate_lwp() - set_employee_name(self) - self.set_total_leaves_allocated() + self.validate_new_leaves_allocated_value() self.validate_allocation_overlap() self.validate_back_dated_allocation() + self.set_total_leaves_allocated() self.validate_total_leaves_allocated() - self.validate_leaves_allocated_value() + self.validate_lwp() + set_employee_name(self) self.validate_leave_allocation_days() def validate_leave_allocation_days(self): - new_leaves = self.new_leaves_allocated if not self.carry_forward else self.carry_forwarded_leaves - max_leaves, leaves_allocated = self.get_max_leaves_with_leaves_allocated_for_leave_type(flt(new_leaves)) + company = frappe.db.get_value("Employee", self.employee, "company") + leave_period = get_leave_period(self.from_date, self.to_date, company) + max_leaves_allowed = frappe.db.get_value("Leave Type", self.leave_type, "max_leaves_allowed") + if max_leaves_allowed > 0: + leave_allocated = 0 + if leave_period: + leave_allocated = get_leave_allocation_for_period(self.employee, self.leave_type, leave_period[0].from_date, leave_period[0].to_date) + leave_allocated += self.new_leaves_allocated + if leave_allocated > max_leaves_allowed: + frappe.throw(_("Total allocated leaves are more days than maximum allocation of {0} leave type for employee {1} in the period")\ + .format(self.leave_type, self.employee)) - if leaves_allocated > max_leaves: - frappe.throw(_("Total allocated leaves are more days than maximum allocation of {0} leave type for employee {1} in the period")\ - .format(self.leave_type, self.employee)) + def on_submit(self): + create_leave_ledger_entry(self) + + def on_cancel(self): + create_leave_ledger_entry(self) def on_update_after_submit(self): self.validate_new_leaves_allocated_value() self.set_total_leaves_allocated() - frappe.db.set(self,'total_leaves_allocated', flt(self.total_leaves_allocated)) + frappe.db.set(self,'carry_forwarded_leaves', flt(self.carry_forwarded_leaves)) + frappe.db.set(self,'total_leaves_allocated',flt(self.total_leaves_allocated)) self.validate_against_leave_applications() def validate_period(self): - allocation_period = date_diff(self.to_date, self.from_date) - - if allocation_period <= 0: + if date_diff(self.to_date, self.from_date) <= 0: frappe.throw(_("To date cannot be before from date")) - # check if the allocation period is more than the expiry allows for carry forwarded allocation - if self.carry_forward: - expiry_days = get_days_to_expiry_for_leave_type(self.leave_type) - - if allocation_period > flt(expiry_days) and expiry_days: - frappe.throw(_("Leave allocation period cannot exceed carry forward expiry limit")) - def validate_lwp(self): if frappe.db.get_value("Leave Type", self.leave_type, "is_lwp"): frappe.throw(_("Leave Type {0} cannot be allocated since it is leave without pay").format(self.leave_type)) - def validate_leaves_allocated_value(self): + def validate_new_leaves_allocated_value(self): """validate that leave allocation is in multiples of 0.5""" if flt(self.new_leaves_allocated) % 0.5: frappe.throw(_("Leaves must be allocated in multiples of 0.5"), ValueMultiplierError) def validate_allocation_overlap(self): leave_allocation = frappe.db.sql(""" - SELECT - name - FROM `tabLeave Allocation` - WHERE - employee=%s - AND leave_type=%s - AND docstatus=1 - AND carry_forward={0} - AND to_date >= %s - AND from_date <= %s""" #nosec - .format(self.carry_forward), (self.employee, self.leave_type, self.from_date, self.to_date)) + select name from `tabLeave Allocation` + where employee=%s and leave_type=%s and docstatus=1 + and to_date >= %s and from_date <= %s""", + (self.employee, self.leave_type, self.from_date, self.to_date)) if leave_allocation: frappe.msgprint(_("{0} already allocated for Employee {1} for period {2} to {3}") @@ -97,20 +94,18 @@ class LeaveAllocation(Document): BackDatedAllocationError) def set_total_leaves_allocated(self): - if self.carry_forward: - self.set_carry_forwarded_leaves() - self.total_leaves_allocated = flt(self.carry_forwarded_leaves) - else: - self.total_leaves_allocated = flt(self.new_leaves_allocated) + self.carry_forwarded_leaves = get_carry_forwarded_leaves(self.employee, + self.leave_type, self.from_date, self.carry_forward) - 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"): + self.total_leaves_allocated = flt(self.carry_forwarded_leaves) + flt(self.new_leaves_allocated) + + 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))) def validate_total_leaves_allocated(self): # Adding a day to include To Date in the difference date_difference = date_diff(self.to_date, self.from_date) + 1 - if date_difference < flt(self.total_leaves_allocated): + if date_difference < self.total_leaves_allocated: frappe.throw(_("Total allocated leaves are more than days in the period"), OverAllocationError) def validate_against_leave_applications(self): @@ -123,43 +118,16 @@ class LeaveAllocation(Document): else: frappe.throw(_("Total allocated leaves {0} cannot be less than already approved leaves {1} for the period").format(self.total_leaves_allocated, leaves_taken), LessAllocationError) - def set_carry_forwarded_leaves(self): - self.carry_forwarded_leaves = get_carry_forwarded_leaves(self.employee, self.leave_type, self.from_date) - - max_leaves, leaves_allocated = self.get_max_leaves_with_leaves_allocated_for_leave_type(self.carry_forwarded_leaves) - - if leaves_allocated > max_leaves: - self.carry_forwarded_leaves = max_leaves - (leaves_allocated - self.carry_forwarded_leaves) - - def get_max_leaves_with_leaves_allocated_for_leave_type(self, new_leaves): - ''' compare new leaves allocated with max leaves ''' - company = frappe.db.get_value("Employee", self.employee, "company") - leaves_allocated = 0 - leave_period = get_leave_period(self.from_date, self.to_date, company) - max_leaves_allowed = frappe.db.get_value("Leave Type", self.leave_type, "max_leaves_allowed") - if max_leaves_allowed > 0: - if leave_period: - leaves_allocated = get_leave_allocation_for_period(self.employee, self.leave_type, leave_period[0].from_date, leave_period[0].to_date) - leaves_allocated += new_leaves - return max_leaves_allowed, leaves_allocated - def get_leave_allocation_for_period(employee, leave_type, from_date, to_date): leave_allocated = 0 leave_allocations = frappe.db.sql(""" - SELECT - employee, - leave_type, - from_date, - to_date, - total_leaves_allocated - FROM `tabLeave Allocation` - WHERE - employee=%(employee)s - AND leave_type=%(leave_type)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)) + select employee, leave_type, from_date, to_date, total_leaves_allocated + from `tabLeave Allocation` + where employee=%(employee)s and leave_type=%(leave_type)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)) """, { "from_date": from_date, "to_date": to_date, @@ -174,43 +142,14 @@ def get_leave_allocation_for_period(employee, leave_type, from_date, to_date): return leave_allocated @frappe.whitelist() -def get_carry_forwarded_leaves(employee, leave_type, date): - ''' Calculates carry forwarded days based on previous unused leave allocations ''' - carry_forwarded_leaves = 0 - expiry_days = get_days_to_expiry_for_leave_type(leave_type) - validate_carry_forward(leave_type) - filters = { - "employee": employee, - "leave_type": leave_type, - "docstatus": 1, - "to_date": ("<", date) - } - limit = 2 +def get_carry_forwarded_leaves(employee, leave_type, date, carry_forward=None): + leave_records = frappe.get_all("Leave Ledger Entry", + filters={'Employee':employee, + 'leave_type':leave_type, + 'to_date':("<=", date)}, + fields=['leaves']) - # check number of days to expire, ignore expiry for default value 0 - if expiry_days: - filters.update(carry_forward=0) - limit = 1 - - previous_allocation = frappe.get_all("Leave Allocation", - filters=filters, - fields=["name","from_date","to_date","total_leaves_allocated"], - order_by="to_date desc", - limit=limit) - - if previous_allocation: - leaves_taken = get_approved_leaves_for_period(employee, leave_type, - previous_allocation[0].from_date, previous_allocation[0].to_date) - - carry_forwarded_leaves = flt(previous_allocation[0].total_leaves_allocated) - flt(leaves_taken) - - return carry_forwarded_leaves - -def get_days_to_expiry_for_leave_type(leave_type): - ''' returns days to expiry for a provided leave type ''' - return frappe.db.get_value("Leave Type", - filters={"leave_type_name": leave_type, "is_carry_forward": 1}, - fieldname="carry_forward_leave_expiry") + return sum(record.get("leaves") for record in leave_records) def validate_carry_forward(leave_type): if not frappe.db.get_value("Leave Type", leave_type, "is_carry_forward"): diff --git a/erpnext/hr/doctype/leave_period/leave_period.py b/erpnext/hr/doctype/leave_period/leave_period.py index a6198a8918..91cc9b8340 100644 --- a/erpnext/hr/doctype/leave_period/leave_period.py +++ b/erpnext/hr/doctype/leave_period/leave_period.py @@ -114,22 +114,7 @@ def create_leave_allocation(employee, leave_type, new_leaves_allocated, leave_ty allocation.leave_period = leave_period.name if carry_forward_leaves: if leave_type_details.get(leave_type).is_carry_forward: - create_carry_forward_leaves_allocation(employee, leave_type, leave_type_details, leave_period, carry_forward_leaves) - allocation.save(ignore_permissions=True) + allocation.carry_forward = carry_forward_leaves + allocation.save(ignore_permissions = True) allocation.submit() - return allocation.name - -def create_carry_forward_leaves_allocation(employee, leave_type, leave_type_details, leave_period, carry_forward_leaves): - carry_forwarded_leaves = get_carry_forwarded_leaves(employee, leave_type, leave_period.from_date) - if not carry_forwarded_leaves: - return - allocation = frappe.new_doc("Leave Allocation") - allocation.employee = employee - allocation.leave_type = leave_type - allocation.from_date = leave_period.from_date - allocation.to_date = add_days(leave_period.from_date, leave_type_details.carry_forward_leave_expiry) if not leave_type_details.carry_forward_leave_expiry else leave_period.to_date - allocation.leave_period = leave_period.name - allocation.carry_forward = carry_forward_leaves - allocation.carry_forwarded_leaves = carry_forwarded_leaves - allocation.save(ignore_permissions=True) - allocation.submit() \ No newline at end of file + return allocation.name \ No newline at end of file From 5ad83c06c225f5a8a8cb47f0f5b5dc412f988fe5 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Fri, 10 May 2019 19:50:04 +0530 Subject: [PATCH 19/98] feat: add ledger entries on leave addition --- .../leave_allocation/leave_allocation.py | 24 ++++++++++++++++--- .../leave_application/leave_application.py | 21 ++++++++++++++++ .../leave_ledger_entry/leave_ledger_entry.py | 19 +++------------ 3 files changed, 45 insertions(+), 19 deletions(-) diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation.py b/erpnext/hr/doctype/leave_allocation/leave_allocation.py index aa1cc9ecad..5f474a4554 100755 --- a/erpnext/hr/doctype/leave_allocation/leave_allocation.py +++ b/erpnext/hr/doctype/leave_allocation/leave_allocation.py @@ -42,10 +42,10 @@ class LeaveAllocation(Document): .format(self.leave_type, self.employee)) def on_submit(self): - create_leave_ledger_entry(self) + self.create_leave_ledger_entry() - def on_cancel(self): - create_leave_ledger_entry(self) + # def before_cancel(self): + # self.create_leave_ledger_entry(submit=False) def on_update_after_submit(self): self.validate_new_leaves_allocated_value() @@ -118,6 +118,24 @@ class LeaveAllocation(Document): else: frappe.throw(_("Total allocated leaves {0} cannot be less than already approved leaves {1} for the period").format(self.total_leaves_allocated, leaves_taken), LessAllocationError) + def create_leave_ledger_entry(self, submit=True): + if self.carry_forwarded_leaves: + expiry_days = frappe.db.get_value("Leave Type", self.leave_type, "carry_forward_leave_expiry") + + args = dict( + leaves=self.carry_forwarded_leaves * 1 if submit else -1, + to_date=add_days(self.from_date, expiry_days) if expiry_days else self.to_date, + is_carry_forward=1 + ) + create_leave_ledger_entry(self, args) + + args = dict( + leaves=self.new_leaves_allocated * 1 if submit else -1, + to_date=self.to_date, + is_carry_forward=0 + ) + create_leave_ledger_entry(self, args) + def get_leave_allocation_for_period(employee, leave_type, from_date, to_date): leave_allocated = 0 leave_allocations = frappe.db.sql(""" diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py index f542fa1e5c..61f617806f 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.py +++ b/erpnext/hr/doctype/leave_application/leave_application.py @@ -10,6 +10,7 @@ from erpnext.hr.utils import set_employee_name, get_leave_period from erpnext.hr.doctype.leave_block_list.leave_block_list import get_applicable_block_dates from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee from erpnext.buying.doctype.supplier_scorecard.supplier_scorecard import daterange +from erpnext.hr.doctype.leave_ledger_entry.leave_ledger_entry import create_leave_ledger_entry class LeaveDayBlockedError(frappe.ValidationError): pass class OverlapError(frappe.ValidationError): pass @@ -50,6 +51,7 @@ class LeaveApplication(Document): # notify leave applier about approval self.notify_employee() + self.create_leave_ledger_entry() self.reload() def on_cancel(self): @@ -57,6 +59,7 @@ class LeaveApplication(Document): # notify leave applier about cancellation self.notify_employee() self.cancel_attendance() + self.create_leave_ledger_entry(submit=False) def validate_applicable_after(self): if self.leave_type: @@ -346,6 +349,14 @@ class LeaveApplication(Document): except frappe.OutgoingEmailError: pass + def create_leave_ledger_entry(self, submit=True): + args = dict( + leaves=self.total_leave_days * -1 if submit else 1, + to_date=self.to_date, + is_carry_forward=0 + ) + create_leave_ledger_entry(self, args) + @frappe.whitelist() def get_number_of_leave_days(employee, leave_type, from_date, to_date, half_day = None, half_day_date = None): number_of_days = 0 @@ -384,6 +395,16 @@ def get_leave_details(employee, date): return ret +@frappe.whitelist() +def get_leave_balance(employee, leave_type, date): + leave_records = frappe.get_all("Leave Ledger Entry", + filters={'Employee':employee, + 'leave_type':leave_type, + 'to_date':("<=", date)}, + fields=['leaves']) + + return sum(record.get("leaves") for record in leave_records) + @frappe.whitelist() def get_leave_balance_on(employee, leave_type, date, allocation_records=None, docname=None, consider_all_leaves_in_the_allocation_period=False, consider_encashed_leaves=True): diff --git a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py index 35dce97936..60d35957b9 100644 --- a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py +++ b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py @@ -10,7 +10,7 @@ from frappe.utils import add_days class LeaveLedgerEntry(Document): pass -def create_leave_ledger_entry(ref_doc, submit=True): +def create_leave_ledger_entry(ref_doc, args): ledger = dict( doctype='Leave Ledger Entry', employee=ref_doc.employee, @@ -20,19 +20,6 @@ def create_leave_ledger_entry(ref_doc, submit=True): transaction_type=ref_doc.doctype, transaction_name=ref_doc.name ) - if ref_doc.carry_forwarded_leaves: - expiry_days = frappe.db.get_value("Leave Type", ref_doc.leave_type, "carry_forward_leave_expiry") - ledger.update(dict( - leaves=ref_doc.carry_forwarded_leaves * 1 if submit else -1, - to_date=add_days(ref_doc.from_date, expiry_days) if expiry_days else ref_doc.to_date, - is_carry_forward=1 - )) - frappe.get_doc(ledger).submit() - - ledger.update(dict( - leaves=ref_doc.new_leaves_allocated * 1 if submit else -1, - to_date=ref_doc.to_date, - is_carry_forward=0 - )) - frappe.get_doc(ledger).submit() \ No newline at end of file + ledger.update(args) + frappe.get_doc(ledger).insert(ignore_permissions=True) \ No newline at end of file From 01490f1560e5fcdae2e6b8923f016b5c1c83ba68 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Sun, 12 May 2019 21:01:25 +0530 Subject: [PATCH 20/98] feat: add cancellation workflow for leave allocation ledger entry --- .../hr/doctype/leave_allocation/leave_allocation.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation.py b/erpnext/hr/doctype/leave_allocation/leave_allocation.py index 5f474a4554..57fe9830f6 100755 --- a/erpnext/hr/doctype/leave_allocation/leave_allocation.py +++ b/erpnext/hr/doctype/leave_allocation/leave_allocation.py @@ -44,8 +44,8 @@ class LeaveAllocation(Document): def on_submit(self): self.create_leave_ledger_entry() - # def before_cancel(self): - # self.create_leave_ledger_entry(submit=False) + def on_cancel(self): + self.create_leave_ledger_entry(submit=False) def on_update_after_submit(self): self.validate_new_leaves_allocated_value() @@ -123,14 +123,14 @@ class LeaveAllocation(Document): expiry_days = frappe.db.get_value("Leave Type", self.leave_type, "carry_forward_leave_expiry") args = dict( - leaves=self.carry_forwarded_leaves * 1 if submit else -1, + leaves=self.carry_forwarded_leaves, to_date=add_days(self.from_date, expiry_days) if expiry_days else self.to_date, is_carry_forward=1 ) - create_leave_ledger_entry(self, args) + create_leave_ledger_entry(self, args, submit) args = dict( - leaves=self.new_leaves_allocated * 1 if submit else -1, + leaves=self.new_leaves_allocated, to_date=self.to_date, is_carry_forward=0 ) From 5448edff2c9ec4be725c96d0d9b82c19dd651261 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Sun, 12 May 2019 21:02:05 +0530 Subject: [PATCH 21/98] feat: delete cancelled allocation from ledger --- .../leave_ledger_entry/leave_ledger_entry.py | 30 ++++++++++++++++--- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py index 60d35957b9..12be4794aa 100644 --- a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py +++ b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py @@ -5,13 +5,14 @@ from __future__ import unicode_literals import frappe from frappe.model.document import Document +from frappe import _ from frappe.utils import add_days class LeaveLedgerEntry(Document): pass -def create_leave_ledger_entry(ref_doc, args): - ledger = dict( +def create_leave_ledger_entry(ref_doc, args, submit): + ledger = frappe._dict( doctype='Leave Ledger Entry', employee=ref_doc.employee, employee_name=ref_doc.employee_name, @@ -20,6 +21,27 @@ def create_leave_ledger_entry(ref_doc, args): transaction_type=ref_doc.doctype, transaction_name=ref_doc.name ) - ledger.update(args) - frappe.get_doc(ledger).insert(ignore_permissions=True) \ No newline at end of file + + if submit: + frappe.get_doc(ledger).insert(ignore_permissions=True) + else: + delete_ledger_entry(ledger) + +def delete_ledger_entry(ledger): + ''' Delete ledger entry on cancel of leave application/allocation ''' + ledger_entry, creation_date = frappe.db.get_value("Leave Ledger Entry", + {'transaction_name': ledger.transaction_name}, + ['name', 'creation'] + ) + leave_application_records = frappe.get_all("Leave Ledger Entry", + filters={ + 'transaction_type': 'Leave Application', + 'creation_date': (">", creation_date) + }, + fields=['transaction_type']) + if not leave_application_records: + frappe.delete_doc("Leave Ledger Entry", ledger_entry) + else: + frappe.throw(_("Leave allocation %s is linked with leave application %s" + % (ledger_entry, ', '.join(leave_application_records)))) \ No newline at end of file From 783bd89413b2fc7fa3572ef123fddafc17f3b531 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Sun, 12 May 2019 21:59:36 +0530 Subject: [PATCH 22/98] feat: handle cancellation workflow for leave application --- erpnext/hr/doctype/leave_application/leave_application.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py index 61f617806f..76a3af7ae9 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.py +++ b/erpnext/hr/doctype/leave_application/leave_application.py @@ -350,12 +350,12 @@ class LeaveApplication(Document): pass def create_leave_ledger_entry(self, submit=True): - args = dict( - leaves=self.total_leave_days * -1 if submit else 1, + args = frappe._dict( + leaves=self.total_leave_days, to_date=self.to_date, is_carry_forward=0 ) - create_leave_ledger_entry(self, args) + create_leave_ledger_entry(self, args, submit) @frappe.whitelist() def get_number_of_leave_days(employee, leave_type, from_date, to_date, half_day = None, half_day_date = None): From cf8f4bda8fc4d9521a47168cad220034a3f42b15 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Sun, 12 May 2019 22:45:14 +0530 Subject: [PATCH 23/98] fix: skip application fetch for non allocation records --- .../leave_ledger_entry/leave_ledger_entry.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py index 12be4794aa..23258f1c77 100644 --- a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py +++ b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py @@ -34,12 +34,15 @@ def delete_ledger_entry(ledger): {'transaction_name': ledger.transaction_name}, ['name', 'creation'] ) - leave_application_records = frappe.get_all("Leave Ledger Entry", - filters={ - 'transaction_type': 'Leave Application', - 'creation_date': (">", creation_date) - }, - fields=['transaction_type']) + + leave_application_records = [] + if ledger.transaction_type == "Leave Allocation": + leave_application_records = frappe.get_all("Leave Ledger Entry", + filters={ + 'transaction_type': 'Leave Application', + 'creation_date': (">", creation_date) + }, + fields=['transaction_type']) if not leave_application_records: frappe.delete_doc("Leave Ledger Entry", ledger_entry) else: From 9bb4b8e8b29eec81af9c6599a6772ec714a0116a Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Sun, 12 May 2019 23:21:04 +0530 Subject: [PATCH 24/98] feat: create expiry ledger entry on allocation period completion --- erpnext/hooks.py | 1 + .../leave_ledger_entry/leave_ledger_entry.py | 29 +++++++++++++++++-- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 53da013f8d..72c538fd3c 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -264,6 +264,7 @@ scheduler_events = { "erpnext.projects.doctype.project.project.update_project_sales_billing", "erpnext.projects.doctype.project.project.send_project_status_email_to_users", "erpnext.quality_management.doctype.quality_review.quality_review.review", + "erpnext.hr.doctype.leave_ledger_entry.leave_ledger_entry.check_expired_allocation" "erpnext.support.doctype.service_level_agreement.service_level_agreement.check_agreement_status" ], "daily_long": [ diff --git a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py index 23258f1c77..585e1079f7 100644 --- a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py +++ b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py @@ -6,12 +6,12 @@ from __future__ import unicode_literals import frappe from frappe.model.document import Document from frappe import _ -from frappe.utils import add_days +from frappe.utils import add_days, today class LeaveLedgerEntry(Document): pass -def create_leave_ledger_entry(ref_doc, args, submit): +def create_leave_ledger_entry(ref_doc, args, submit=True): ledger = frappe._dict( doctype='Leave Ledger Entry', employee=ref_doc.employee, @@ -47,4 +47,27 @@ def delete_ledger_entry(ledger): frappe.delete_doc("Leave Ledger Entry", ledger_entry) else: frappe.throw(_("Leave allocation %s is linked with leave application %s" - % (ledger_entry, ', '.join(leave_application_records)))) \ No newline at end of file + % (ledger_entry, ', '.join(leave_application_records)))) + +def check_expired_allocation(): + ''' Checks for expired allocation by comparing to_date with current_date and + based on that creates an expiry ledger entry ''' + expired_allocation = frappe.db.get_all("Leave Ledger Allocation", + filters={ + 'to_date': today(), + 'transaction_type': 'Leave Allocation' + }, + fields=['name', 'transaction_name']) + + if expired_allocation: + create_expiry_ledger_entry(expired_allocation) + +def create_expiry_ledger_entry(expired_allocation): + for allocation in expired_allocation: + ledger_entry = frappe.get_doc('Leave Ledger Entry', allocation.name) + args = { + 'leaves': -ledger_entry.leaves, + 'to_date': '', + 'is_carry_forward': ledger_entry.is_carry_forward + } + create_leave_ledger_entry(ledger_entry, args) \ No newline at end of file From 170b8dded8cd5c9bb7309193d1260bbcb5435b3d Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Mon, 13 May 2019 19:17:44 +0530 Subject: [PATCH 25/98] fix: expiry logic for carry forwarded allocation --- .../leave_ledger_entry.json | 8 +++- .../leave_ledger_entry/leave_ledger_entry.py | 45 ++++++++++++++----- 2 files changed, 41 insertions(+), 12 deletions(-) diff --git a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.json b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.json index c7e6b709bd..89c703db65 100644 --- a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.json +++ b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.json @@ -12,6 +12,7 @@ "from_date", "to_date", "is_carry_forward", + "is_expired", "amended_from" ], "fields": [ @@ -72,10 +73,15 @@ "fieldname": "is_carry_forward", "fieldtype": "Check", "label": "Is Carry Forward" + }, + { + "fieldname": "is_expired", + "fieldtype": "Check", + "label": "Is Expired" } ], "is_submittable": 1, - "modified": "2019-05-09 18:36:07.383714", + "modified": "2019-05-13 12:56:45.542495", "modified_by": "Administrator", "module": "HR", "name": "Leave Ledger Entry", diff --git a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py index 585e1079f7..526a91e976 100644 --- a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py +++ b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py @@ -9,7 +9,10 @@ from frappe import _ from frappe.utils import add_days, today class LeaveLedgerEntry(Document): - pass + def validate_entries(self): + leave_records = frappe.get_all('Leave Ledger Entry', ['leaves']) + if sum(record.get("leaves") for record in leave_records) <0: + frappe.throw(_("Invalid Ledger Entry")) def create_leave_ledger_entry(ref_doc, args, submit=True): ledger = frappe._dict( @@ -19,7 +22,7 @@ def create_leave_ledger_entry(ref_doc, args, submit=True): leave_type=ref_doc.leave_type, from_date=ref_doc.from_date, transaction_type=ref_doc.doctype, - transaction_name=ref_doc.name + transaction_name=ref_doc.name, ) ledger.update(args) @@ -52,22 +55,42 @@ def delete_ledger_entry(ledger): def check_expired_allocation(): ''' Checks for expired allocation by comparing to_date with current_date and based on that creates an expiry ledger entry ''' - expired_allocation = frappe.db.get_all("Leave Ledger Allocation", + expired_allocation = frappe.get_all("Leave Ledger Allocation", filters={ 'to_date': today(), 'transaction_type': 'Leave Allocation' }, - fields=['name', 'transaction_name']) + fields=['*']) if expired_allocation: create_expiry_ledger_entry(expired_allocation) def create_expiry_ledger_entry(expired_allocation): for allocation in expired_allocation: - ledger_entry = frappe.get_doc('Leave Ledger Entry', allocation.name) - args = { - 'leaves': -ledger_entry.leaves, - 'to_date': '', - 'is_carry_forward': ledger_entry.is_carry_forward - } - create_leave_ledger_entry(ledger_entry, args) \ No newline at end of file + filters = { + 'employee': allocation.employee, + 'leave_type': allocation.leave_type, + 'from_date': ('>=', allocation.from_date), + } + # get only application ledger entries in case of carry forward + if allocation.is_carry_forward: + filters.update(dict(transaction_type='Leave Application')) + + leave_records = frappe.get_all("Leave Ledger Entry", + filters=filters, + fields=['leaves']) + + leaves = sum(record.get("leaves") for record in leave_records) + + if allocation.is_carry_forward: + leaves = allocation.leaves + leaves + + if leaves > 0: + args = frappe._dict( + leaves=allocation.leaves * -1, + to_date='', + is_carry_forward=allocation.is_carry_forward, + is_expired=1, + from_date=allocation.to_date + ) + create_leave_ledger_entry(allocation, args) \ No newline at end of file From 201aeeb20d6286e9ed8eaea25500889626be8767 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Mon, 13 May 2019 19:19:02 +0530 Subject: [PATCH 26/98] test: creation of ledger entries on allocation submit --- .../leave_allocation/test_leave_allocation.py | 30 ++++++++----------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py b/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py index 325f3a7299..a0113e2d9f 100644 --- a/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py +++ b/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py @@ -68,27 +68,22 @@ class TestLeaveAllocation(unittest.TestCase): #allocated leave more than period self.assertRaises(frappe.ValidationError, doc.save) - def test_carry_forward_allocation(self): + def test_creation_of_leave_ledger_entry_on_submit(self): frappe.db.sql("delete from `tabLeave Allocation`") - leave_type = create_leave_type( - leave_type_name="_Test Carry Forward", - is_carry_forward=1, - carry_forward_leave_expiry=366) - leave_type.submit() - - leave_allocation = create_leave_allocation( - from_date=add_months(nowdate(), -12), - to_date=add_days(nowdate(), -1), - leave_type=leave_type - ) - leave_allocation.new_leaves_allocated = 10 + leave_allocation = create_leave_allocation() leave_allocation.submit() - carry_forward_alloc = create_leave_allocation(leave_type=leave_type) - carry_forward_alloc.carry_forward = 1 - carry_forward_alloc.save() - self.assertEquals(carry_forward_alloc.total_leaves_allocated, 10) + leave_ledger_entry = frappe.get_all('Leave Ledger Entry', fields='*', filters=dict(transaction_name=leave_allocation.name)) + + self.assertEquals(len(leave_ledger_entry), 1) + self.assertEquals(leave_ledger_entry[0].employee, leave_allocation.employee) + self.assertEquals(leave_ledger_entry[0].leave_type, leave_allocation.leave_type) + self.assertEquals(leave_ledger_entry[0].leaves, leave_allocation.new_leaves_allocated) + + # check if leave ledger entry is deleted on cancellation + leave_allocation.cancel() + self.assertFalse(frappe.db.exists("Leave Ledger Entry", {'transaction_name':leave_allocation.name})) def create_leave_allocation(**args): args = frappe._dict(args) @@ -101,6 +96,7 @@ def create_leave_allocation(**args): "employee_name": employee.employee_name, "leave_type": args.leave_type.leave_type_name or "_Test Leave Type", "from_date": args.from_date or nowdate(), + "new_leaves_allocated": args.new_leaves_created or 15, "to_date": args.to_date or add_months(nowdate(), 12) }) return leave_allocation From 964deaca96e7a4ea5fc8f2eea03297a70720b5f6 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Mon, 13 May 2019 19:19:39 +0530 Subject: [PATCH 27/98] test: creation of ledger entries on application submit --- .../test_leave_application.py | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/erpnext/hr/doctype/leave_application/test_leave_application.py b/erpnext/hr/doctype/leave_application/test_leave_application.py index d3dcca1da0..2581c2a659 100644 --- a/erpnext/hr/doctype/leave_application/test_leave_application.py +++ b/erpnext/hr/doctype/leave_application/test_leave_application.py @@ -457,6 +457,28 @@ class TestLeaveApplication(unittest.TestCase): leave_application.submit() self.assertEqual(leave_application.docstatus, 1) + def test_creation_of_leave_ledger_entry_on_submit(self): + + leave_application = frappe.get_doc(dict( + doctype = 'Leave Application', + employee = employee.name, + leave_type = leave_type_1.name, + from_date = nowdate(), + to_date = add_days(nowdate(), 4), + company = "_Test Company", + docstatus = 1, + status = "Approved" + )).submit() + leave_ledger_entry = frappe.get_all('Leave Ledger Entry', fields='*', filters=dict(transaction_name=leave_application.name)) + + self.assertEquals(leave_ledger_entry[0].employee, leave_application.employee) + self.assertEquals(leave_ledger_entry[0].leave_type, leave_application.leave_type) + self.assertEquals(leave_ledger_entry[0].leaves, leave_application.new_leaves_allocated) + + # check if leave ledger entry is deleted on cancellation + leave_application.cancel() + self.assertFalse(frappe.db.exists("Leave Ledger Entry", {'transaction_name':leave_application.name})) + def make_allocation_record(employee=None, leave_type=None): frappe.db.sql("delete from `tabLeave Allocation`") @@ -513,4 +535,4 @@ def allocate_leaves(employee, leave_period, leave_type, new_leaves_allocated, el "docstatus": 1 }).insert() - allocate_leave.submit() + allocate_leave.submit() \ No newline at end of file From 6ba9a128e7fd95c2b2ebea29c36738550209c666 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Tue, 14 May 2019 11:23:10 +0530 Subject: [PATCH 28/98] feat: calculate leave balance using ledger entries --- .../leave_application/leave_application.py | 57 ++++++++++++------- 1 file changed, 36 insertions(+), 21 deletions(-) diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py index 76a3af7ae9..70d7aa4b3a 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.py +++ b/erpnext/hr/doctype/leave_application/leave_application.py @@ -197,6 +197,7 @@ class LeaveApplication(Document): if not is_lwp(self.leave_type): self.leave_balance = get_leave_balance_on(self.employee, self.leave_type, self.from_date, docname=self.name, consider_all_leaves_in_the_allocation_period=True) + if self.status != "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}") @@ -350,6 +351,13 @@ class LeaveApplication(Document): pass def create_leave_ledger_entry(self, submit=True): + # check if there is a carry forwarded allocation expiry between application + allocation = frappe.db.get_value('Leave Ledger Entry', + filters={ + 'transaction_type': "Leave Allocation", + 'is_carry_forward': 1, + 'from_date': self.to_date + }) args = frappe._dict( leaves=self.total_leave_days, to_date=self.to_date, @@ -396,31 +404,38 @@ def get_leave_details(employee, date): return ret @frappe.whitelist() -def get_leave_balance(employee, leave_type, date): +def get_leave_balance_on(employee, leave_type, date, allocation_records=None, consider_all_leaves_in_the_allocation_period=False): + + if allocation_records: + # leave_balance based on current allocation records + allocation = allocation_records.get(leave_type, frappe._dict()) + from_date = allocation.from_date + to_date = allocation.to_date + else: + # fetched the expired allocation creation date + from_date, to_date = frappe.db.get_value('Leave Ledger Entry', + filters={ + 'transaction_type': 'Leave Allocation', + 'employee': employee, + 'leave_type': leave_type, + 'is_expired': 0, + 'is_carry_forward': 0, + }, + fieldname=['from_date', 'to_date'], + order_by='to_date DESC') + + if consider_all_leaves_in_the_allocation_period: + date = to_date + leave_records = frappe.get_all("Leave Ledger Entry", - filters={'Employee':employee, - 'leave_type':leave_type, - 'to_date':("<=", date)}, - fields=['leaves']) + filters={'Employee':employee, + 'leave_type':leave_type, + 'to_date':("<=", date), + 'from_date': from_date}, + fields=['leaves']) return sum(record.get("leaves") for record in leave_records) -@frappe.whitelist() -def get_leave_balance_on(employee, leave_type, date, allocation_records=None, docname=None, - consider_all_leaves_in_the_allocation_period=False, consider_encashed_leaves=True): - - if allocation_records == None: - allocation_records = get_leave_allocation_records(date, employee).get(employee, frappe._dict()) - allocation = allocation_records.get(leave_type, frappe._dict()) - if consider_all_leaves_in_the_allocation_period: - date = allocation.to_date - leaves_taken = get_leaves_for_period(employee, leave_type, allocation.from_date, date, status="Approved", docname=docname) - leaves_encashed = 0 - if frappe.db.get_value("Leave Type", leave_type, 'allow_encashment') and consider_encashed_leaves: - leaves_encashed = flt(allocation.total_leaves_encashed) - - return flt(allocation.total_leaves_allocated) - (flt(leaves_taken) + flt(leaves_encashed)) - def get_total_allocated_leaves(employee, leave_type, date): filters= { 'from_date': ['<=', date], From 50037f86097ab5f744772041026c0ca172d10a34 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Wed, 15 May 2019 21:49:27 +0530 Subject: [PATCH 29/98] fix: consider min days remaining as remaining leaves --- .../leave_application/leave_application.py | 67 +++++++++---------- 1 file changed, 31 insertions(+), 36 deletions(-) diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py index 70d7aa4b3a..9e0ed0f1b5 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.py +++ b/erpnext/hr/doctype/leave_application/leave_application.py @@ -351,15 +351,9 @@ class LeaveApplication(Document): pass def create_leave_ledger_entry(self, submit=True): - # check if there is a carry forwarded allocation expiry between application - allocation = frappe.db.get_value('Leave Ledger Entry', - filters={ - 'transaction_type': "Leave Allocation", - 'is_carry_forward': 1, - 'from_date': self.to_date - }) args = frappe._dict( leaves=self.total_leave_days, + from_date=self.from_date, to_date=self.to_date, is_carry_forward=0 ) @@ -403,38 +397,37 @@ def get_leave_details(employee, date): return ret -@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: - # leave_balance based on current allocation records - allocation = allocation_records.get(leave_type, frappe._dict()) - from_date = allocation.from_date - to_date = allocation.to_date - else: - # fetched the expired allocation creation date - from_date, to_date = frappe.db.get_value('Leave Ledger Entry', - filters={ - 'transaction_type': 'Leave Allocation', - 'employee': employee, - 'leave_type': leave_type, - 'is_expired': 0, - 'is_carry_forward': 0, - }, - fieldname=['from_date', 'to_date'], - order_by='to_date DESC') +def get_leave_balance_on(employee, leave_type, date, allocation_records=None, docname=None, + consider_all_leaves_in_the_allocation_period=False, consider_encashed_leaves=True): + if allocation_records == None: + allocation_records = get_leave_allocation_records(date, employee).get(employee, frappe._dict()) + allocation = allocation_records.get(leave_type, frappe._dict()) if consider_all_leaves_in_the_allocation_period: - date = to_date - - leave_records = frappe.get_all("Leave Ledger Entry", - filters={'Employee':employee, + date = allocation.to_date + leaves_taken = frappe.db.get_value("Leave Ledger Entry", filters={ + 'Employee':employee, 'leave_type':leave_type, + 'transaction_type': ('!=', 'Leave Allocation'), 'to_date':("<=", date), 'from_date': from_date}, - fields=['leaves']) + fields=['SUM(leaves)']) - return sum(record.get("leaves") for record in leave_records) + remaining_unused_leaves = flt(allocation.unused_leaves + leaves_taken) + if allocation.carry_forward and remaining_unused_leaves > 0: + expiry_date = frappe.db.get_value("Leave Ledger Entry", filters={ + 'Employee':employee, + 'leave_type':leave_type, + 'transaction_type': 'Leave Allocation', + 'transaction_name': allocation.name, + 'is_carry_forward': 1, + 'is_expiry': 0 + }, fieldname=['to_date']) + remaining_days = date_diff(expiry_date, date) + 1 + + remaining_unused_leaves = min(remaining_days, remaining_unused_leaves) + + return flt(allocation.new_leaves) + remaining_unused_leaves + leaves_taken def get_total_allocated_leaves(employee, leave_type, date): filters= { @@ -495,8 +488,10 @@ def get_leave_allocation_records(date, employee=None): allocated_leaves.setdefault(d.employee, frappe._dict()).setdefault(d.leave_type, frappe._dict({ "from_date": d.from_date, "to_date": d.to_date, - "total_leaves_allocated": d.total_leaves_allocated, - "total_leaves_encashed":d.total_leaves_encashed + "carry_forward": d.carry_forward, + "unused_leaves": d.carry_forwarded_leaves, + "new_leaves": d.new_leaves_allocated, + "name": d.name })) return allocated_leaves @@ -677,4 +672,4 @@ def get_leave_approver(employee, department=None): if department: return frappe.db.get_value('Department Approver', {'parent': department, - 'parentfield': 'leave_approvers', 'idx': 1}, 'approver') + 'parentfield': 'leave_approvers', 'idx': 1}, 'approver') \ No newline at end of file From f8f02c508d5122b3af0fc1e4eb829b3ba2a3e995 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Wed, 15 May 2019 21:50:06 +0530 Subject: [PATCH 30/98] feat: create leave ledger entry on leave encashment creation --- .../hr/doctype/leave_encashment/leave_encashment.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/erpnext/hr/doctype/leave_encashment/leave_encashment.py b/erpnext/hr/doctype/leave_encashment/leave_encashment.py index 9944bc5380..4f4a8760cf 100644 --- a/erpnext/hr/doctype/leave_encashment/leave_encashment.py +++ b/erpnext/hr/doctype/leave_encashment/leave_encashment.py @@ -10,6 +10,7 @@ from frappe.utils import getdate, nowdate, flt from erpnext.hr.utils import set_employee_name from erpnext.hr.doctype.leave_application.leave_application import get_leave_balance_on from erpnext.hr.doctype.salary_structure_assignment.salary_structure_assignment import get_assigned_salary_structure +from erpnext.hr.doctype.leave_ledger_entry.leave_ledger_entry import create_leave_ledger_entry class LeaveEncashment(Document): def validate(self): @@ -40,6 +41,8 @@ class LeaveEncashment(Document): frappe.db.set_value("Leave Allocation", self.leave_allocation, "total_leaves_encashed", frappe.db.get_value('Leave Allocation', self.leave_allocation, 'total_leaves_encashed') + self.encashable_days) + self.create_leave_ledger_entry() + def on_cancel(self): if self.additional_salary: frappe.get_doc("Additional Salary", self.additional_salary).cancel() @@ -48,6 +51,7 @@ class LeaveEncashment(Document): if self.leave_allocation: frappe.db.set_value("Leave Allocation", self.leave_allocation, "total_leaves_encashed", frappe.db.get_value('Leave Allocation', self.leave_allocation, 'total_leaves_encashed') - self.encashable_days) + self.create_leave_ledger_entry(submit=False) def get_leave_details_for_encashment(self): salary_structure = get_assigned_salary_structure(self.employee, self.encashment_date or getdate(nowdate())) @@ -75,3 +79,11 @@ class LeaveEncashment(Document): and employee= '{2}'""".format(self.encashment_date or getdate(nowdate()), self.leave_type, self.employee)) return leave_allocation[0][0] if leave_allocation else None + + def create_leave_ledger_entry(self, submit=True): + args = frappe._dict( + leaves=self.encashable_days * -1, + from_date=self.encashment_date, + is_carry_forward=0 + ) + create_leave_ledger_entry(self, args, submit) \ No newline at end of file From afb0c4aa431f55415397c6c8b6fe2c23eb0d5979 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Wed, 15 May 2019 21:50:34 +0530 Subject: [PATCH 31/98] fix: delete entry on cancellation of transaction --- .../leave_ledger_entry/leave_ledger_entry.py | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py index 526a91e976..1c8fed3d7e 100644 --- a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py +++ b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py @@ -20,34 +20,36 @@ def create_leave_ledger_entry(ref_doc, args, submit=True): employee=ref_doc.employee, employee_name=ref_doc.employee_name, leave_type=ref_doc.leave_type, - from_date=ref_doc.from_date, transaction_type=ref_doc.doctype, transaction_name=ref_doc.name, ) ledger.update(args) - if submit: - frappe.get_doc(ledger).insert(ignore_permissions=True) + frappe.get_doc(ledger).submit() else: delete_ledger_entry(ledger) def delete_ledger_entry(ledger): - ''' Delete ledger entry on cancel of leave application/allocation ''' - ledger_entry, creation_date = frappe.db.get_value("Leave Ledger Entry", - {'transaction_name': ledger.transaction_name}, - ['name', 'creation'] - ) + ''' Delete ledger entry on cancel of leave application/allocation/encashment ''' leave_application_records = [] + # prevent deletion when leave application has been created after allocation if ledger.transaction_type == "Leave Allocation": leave_application_records = frappe.get_all("Leave Ledger Entry", filters={ + 'employee': ledger.employee, + 'leave_type': ledger.leave_type, 'transaction_type': 'Leave Application', - 'creation_date': (">", creation_date) + 'from_date': (">=", ledger.from_date), + 'to_date': ('<=', ledger.to_date) }, - fields=['transaction_type']) + fields=['transaction_name']) + if not leave_application_records: - frappe.delete_doc("Leave Ledger Entry", ledger_entry) + frappe.db.sql("""DELETE + FROM `tabLeave Ledger Entry` + WHERE + `transaction_name`=%s""", (ledger.transaction_name)) else: frappe.throw(_("Leave allocation %s is linked with leave application %s" % (ledger_entry, ', '.join(leave_application_records)))) From 5ba17c87e53bf0b3a99fb25690c19ddebf5415e4 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Wed, 22 May 2019 18:29:22 +0530 Subject: [PATCH 32/98] feat: remove update allocation after submit --- .../leave_allocation/leave_allocation.json | 591 ++++++++++++++++-- .../leave_allocation/leave_allocation.py | 55 +- 2 files changed, 577 insertions(+), 69 deletions(-) diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation.json b/erpnext/hr/doctype/leave_allocation/leave_allocation.json index bb851c6850..568182d3b7 100644 --- a/erpnext/hr/doctype/leave_allocation/leave_allocation.json +++ b/erpnext/hr/doctype/leave_allocation/leave_allocation.json @@ -1,192 +1,683 @@ { + "allow_copy": 0, + "allow_events_in_timeline": 0, + "allow_guest_to_view": 0, "allow_import": 1, + "allow_rename": 0, "autoname": "naming_series:", + "beta": 0, "creation": "2013-02-20 19:10:38", + "custom": 0, + "docstatus": 0, "doctype": "DocType", "document_type": "Setup", + "editable_grid": 0, "engine": "InnoDB", - "field_order": [ - "naming_series", - "employee", - "employee_name", - "department", - "column_break1", - "leave_type", - "from_date", - "to_date", - "section_break_6", - "new_leaves_allocated", - "carry_forward", - "carry_forwarded_leaves", - "total_leaves_allocated", - "total_leaves_encashed", - "column_break_10", - "compensatory_request", - "leave_period", - "amended_from", - "notes", - "description" - ], "fields": [ { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "", "fieldname": "naming_series", "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": 0, "label": "Series", + "length": 0, "no_copy": 1, "options": "HR-LAL-.YYYY.-", + "permlevel": 0, + "precision": "", "print_hide": 1, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, "reqd": 1, - "set_only_once": 1 + "search_index": 0, + "set_only_once": 1, + "translatable": 0, + "unique": 0 }, { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, "fieldname": "employee", "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, "in_global_search": 1, "in_list_view": 1, "in_standard_filter": 1, "label": "Employee", + "length": 0, + "no_copy": 0, "oldfieldname": "employee", "oldfieldtype": "Link", "options": "Employee", + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, "reqd": 1, - "search_index": 1 + "search_index": 1, + "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, "fetch_from": "employee.employee_name", "fieldname": "employee_name", "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, "in_global_search": 1, "in_list_view": 1, + "in_standard_filter": 0, "label": "Employee Name", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 1, - "search_index": 1 + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 1, + "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, "fetch_from": "employee.department", "fieldname": "department", "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": "Department", + "length": 0, + "no_copy": 0, "options": "Department", - "read_only": 1 + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "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": "column_break1", "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, + "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, "width": "50%" }, { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, "fieldname": "leave_type", "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, "in_list_view": 1, "in_standard_filter": 1, "label": "Leave Type", + "length": 0, + "no_copy": 0, "oldfieldname": "leave_type", "oldfieldtype": "Link", "options": "Leave Type", + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, "reqd": 1, - "search_index": 1 + "search_index": 1, + "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": "from_date", "fieldtype": "Date", + "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": "From Date", - "reqd": 1 + "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": 1, + "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": "to_date", "fieldtype": "Date", + "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": "To Date", - "reqd": 1 + "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": 1, + "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": "section_break_6", "fieldtype": "Section Break", - "label": "Allocation" + "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": "Allocation", + "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, + "translatable": 0, + "unique": 0 }, { - "allow_on_submit": 1, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, "bold": 1, + "collapsible": 0, + "columns": 0, "fieldname": "new_leaves_allocated", "fieldtype": "Float", - "label": "New Leaves Allocated" + "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": "New Leaves Allocated", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "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, + "description": "", "fieldname": "carry_forward", "fieldtype": "Check", - "label": "Add unused leaves from previous allocations" + "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": "Add unused leaves from previous allocations", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "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, "depends_on": "carry_forward", "fieldname": "carry_forwarded_leaves", "fieldtype": "Float", + "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": "Unused leaves", - "read_only": 1 + "length": 0, + "no_copy": 0, + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "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": 1, + "bold": 0, + "collapsible": 0, + "columns": 0, "fieldname": "total_leaves_allocated", "fieldtype": "Float", + "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": "Total Leaves Allocated", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, "read_only": 1, - "reqd": 1 + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "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, "depends_on": "eval:doc.total_leaves_encashed>0", "fieldname": "total_leaves_encashed", "fieldtype": "Float", + "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": "Total Leaves Encashed", - "read_only": 1 + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "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": "column_break_10", - "fieldtype": "Column Break" + "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, + "translatable": 0, + "unique": 0 }, { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, "fieldname": "compensatory_request", "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": "Compensatory Leave Request", + "length": 0, + "no_copy": 0, "options": "Compensatory Leave Request", - "read_only": 1 + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "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": "leave_period", "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": 1, "label": "Leave Period", + "length": 0, + "no_copy": 0, "options": "Leave Period", - "read_only": 1 + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "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": "amended_from", "fieldtype": "Link", + "hidden": 0, "ignore_user_permissions": 1, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, "label": "Amended From", + "length": 0, "no_copy": 1, "oldfieldname": "amended_from", "oldfieldtype": "Data", "options": "Leave Allocation", + "permlevel": 0, "print_hide": 1, - "read_only": 1 + "print_hide_if_no_value": 0, + "read_only": 1, + "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": 1, + "columns": 0, "fieldname": "notes", "fieldtype": "Section Break", - "label": "Notes" + "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": "Notes", + "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, + "translatable": 0, + "unique": 0 }, { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, "fieldname": "description", "fieldtype": "Small Text", + "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": "Description", + "length": 0, + "no_copy": 0, "oldfieldname": "reason", "oldfieldtype": "Small Text", + "permlevel": 0, + "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, "width": "300px" } ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, "icon": "fa fa-ok", "idx": 1, + "image_view": 0, + "in_create": 0, "is_submittable": 1, - "modified": "2019-05-09 19:06:33.659196", + "issingle": 0, + "istable": 0, + "max_attachments": 0, + "modified": "2019-05-22 11:28:09.360525", "modified_by": "Administrator", "module": "HR", "name": "Leave Allocation", @@ -198,10 +689,15 @@ "create": 1, "delete": 1, "email": 1, + "export": 0, + "if_owner": 0, + "import": 0, + "permlevel": 0, "print": 1, "read": 1, "report": 1, "role": "HR User", + "set_user_permissions": 0, "share": 1, "submit": 1, "write": 1 @@ -213,19 +709,28 @@ "delete": 1, "email": 1, "export": 1, + "if_owner": 0, "import": 1, + "permlevel": 0, "print": 1, "read": 1, "report": 1, "role": "HR Manager", + "set_user_permissions": 0, "share": 1, "submit": 1, "write": 1 } ], + "quick_entry": 0, + "read_only": 0, + "read_only_onload": 0, "search_fields": "employee,employee_name,leave_type,total_leaves_allocated", "show_name_in_global_search": 1, "sort_field": "modified", "sort_order": "DESC", - "timeline_field": "employee" + "timeline_field": "employee", + "track_changes": 0, + "track_seen": 0, + "track_views": 0 } \ No newline at end of file diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation.py b/erpnext/hr/doctype/leave_allocation/leave_allocation.py index 57fe9830f6..8429ad4308 100755 --- a/erpnext/hr/doctype/leave_allocation/leave_allocation.py +++ b/erpnext/hr/doctype/leave_allocation/leave_allocation.py @@ -47,15 +47,6 @@ class LeaveAllocation(Document): def on_cancel(self): self.create_leave_ledger_entry(submit=False) - def on_update_after_submit(self): - self.validate_new_leaves_allocated_value() - self.set_total_leaves_allocated() - - frappe.db.set(self,'carry_forwarded_leaves', flt(self.carry_forwarded_leaves)) - frappe.db.set(self,'total_leaves_allocated',flt(self.total_leaves_allocated)) - - self.validate_against_leave_applications() - def validate_period(self): if date_diff(self.to_date, self.from_date) <= 0: frappe.throw(_("To date cannot be before from date")) @@ -108,16 +99,6 @@ class LeaveAllocation(Document): if date_difference < self.total_leaves_allocated: frappe.throw(_("Total allocated leaves are more than days in the period"), OverAllocationError) - def validate_against_leave_applications(self): - leaves_taken = get_approved_leaves_for_period(self.employee, self.leave_type, - self.from_date, self.to_date) - - if flt(leaves_taken) > flt(self.total_leaves_allocated): - if frappe.db.get_value("Leave Type", self.leave_type, "allow_negative"): - frappe.msgprint(_("Note: Total allocated leaves {0} shouldn't be less than already approved leaves {1} for the period").format(self.total_leaves_allocated, leaves_taken)) - else: - frappe.throw(_("Total allocated leaves {0} cannot be less than already approved leaves {1} for the period").format(self.total_leaves_allocated, leaves_taken), LessAllocationError) - def create_leave_ledger_entry(self, submit=True): if self.carry_forwarded_leaves: expiry_days = frappe.db.get_value("Leave Type", self.leave_type, "carry_forward_leave_expiry") @@ -134,7 +115,7 @@ class LeaveAllocation(Document): to_date=self.to_date, is_carry_forward=0 ) - create_leave_ledger_entry(self, args) + create_leave_ledger_entry(self, args, submit) def get_leave_allocation_for_period(employee, leave_type, from_date, to_date): leave_allocated = 0 @@ -161,13 +142,35 @@ def get_leave_allocation_for_period(employee, leave_type, from_date, to_date): @frappe.whitelist() def get_carry_forwarded_leaves(employee, leave_type, date, carry_forward=None): - leave_records = frappe.get_all("Leave Ledger Entry", - filters={'Employee':employee, - 'leave_type':leave_type, - 'to_date':("<=", date)}, - fields=['leaves']) + carry_forwarded_leaves = 0 - return sum(record.get("leaves") for record in leave_records) + if carry_forward: + validate_carry_forward(leave_type) + + carry_forwarded_leaves = frappe.db.sql(""" + SELECT + SUM(leaves) + FROM `tabLeave Ledger Entry` + WHERE + employee=%s + AND leave_type=%s + AND docstatus=1 + AND to_date < %s + AND name NOT IN ( + SELECT name + from `tabLeave Ledger Entry` + WHERE + employee=%s + AND leave_type=%s + AND docstatus=1 + AND to_date < %s + is_expired=1 + ORDER BY creation DESC + ) + ORDER BY creation DESC + """, (employee, leave_type, date), as_dict=1) + + return carry_forwarded_leaves def validate_carry_forward(leave_type): if not frappe.db.get_value("Leave Type", leave_type, "is_carry_forward"): From 8ef81870bb1f282caecc51a2de7c2c0ba9bf204d Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Thu, 23 May 2019 20:28:16 +0530 Subject: [PATCH 33/98] test: create leave ledger entry for encashment --- .../leave_encashment/test_leave_encashment.py | 57 +++++++++++++------ 1 file changed, 39 insertions(+), 18 deletions(-) diff --git a/erpnext/hr/doctype/leave_encashment/test_leave_encashment.py b/erpnext/hr/doctype/leave_encashment/test_leave_encashment.py index ef5c2aa6f3..cfed780eb7 100644 --- a/erpnext/hr/doctype/leave_encashment/test_leave_encashment.py +++ b/erpnext/hr/doctype/leave_encashment/test_leave_encashment.py @@ -9,41 +9,39 @@ from frappe.utils import today, add_months from erpnext.hr.doctype.employee.test_employee import make_employee from erpnext.hr.doctype.salary_structure.test_salary_structure import make_salary_structure from erpnext.hr.doctype.leave_period.test_leave_period import create_leave_period +from erpnext.hr.doctype.leave_policy.test_leave_policy import create_leave_policy\ test_dependencies = ["Leave Type"] class TestLeaveEncashment(unittest.TestCase): def setUp(self): frappe.db.sql('''delete from `tabLeave Period`''') - def test_leave_balance_value_and_amount(self): - employee = "test_employee_encashment@salary.com" - leave_type = "_Test Leave Type Encashment" + frappe.db.sql('''delete from `tabLeave Allocation`''') # create the leave policy - leave_policy = frappe.get_doc({ - "doctype": "Leave Policy", - "leave_policy_details": [{ - "leave_type": leave_type, - "annual_allocation": 10 - }] - }).insert() + leave_policy = create_leave_policy( + leave_type="_Test Leave Type Encashment", + annual_allocation=10) leave_policy.submit() # create employee, salary structure and assignment - employee = make_employee(employee) - frappe.db.set_value("Employee", employee, "leave_policy", leave_policy.name) - salary_structure = make_salary_structure("Salary Structure for Encashment", "Monthly", employee, + self.employee = make_employee("test_employee_encashment@example.com") + frappe.db.set_value("Employee", "test_employee_encashment@example.com", "leave_policy", leave_policy.name) + salary_structure = make_salary_structure("Salary Structure for Encashment", "Monthly", self.employee, other_details={"leave_encashment_amount_per_day": 50}) # create the leave period and assign the leaves - leave_period = create_leave_period(add_months(today(), -3), add_months(today(), 3)) - leave_period.grant_leave_allocation(employee=employee) + self.leave_period = create_leave_period(add_months(today(), -3), add_months(today(), 3)) + self.leave_period.grant_leave_allocation(employee=self.employee) + + def test_leave_balance_value_and_amount(self): + frappe.db.sql('''delete from `tabLeave Encashment`''') leave_encashment = frappe.get_doc(dict( doctype = 'Leave Encashment', - employee = employee, - leave_type = leave_type, - leave_period = leave_period.name, + employee = self.employee, + leave_type = "_Test Leave Type Encashment", + leave_period = self.leave_period.name, payroll_date = today() )).insert() @@ -53,3 +51,26 @@ class TestLeaveEncashment(unittest.TestCase): leave_encashment.submit() self.assertTrue(frappe.db.get_value("Leave Encashment", leave_encashment.name, "additional_salary")) + + def test_creation_of_leave_ledger_entry_on_submit(self): + frappe.db.sql('''delete from `tabLeave Encashment`''') + leave_encashment = frappe.get_doc(dict( + doctype = 'Leave Encashment', + employee = self.employee, + leave_type = "_Test Leave Type Encashment", + leave_period = self.leave_period.name, + payroll_date = today() + )).insert() + + leave_encashment.submit() + + leave_ledger_entry = frappe.get_all('Leave Ledger Entry', fields='*', filters=dict(transaction_name=leave_encashment.name)) + + self.assertEquals(len(leave_ledger_entry), 1) + self.assertEquals(leave_ledger_entry[0].employee, leave_encashment.employee) + self.assertEquals(leave_ledger_entry[0].leave_type, leave_encashment.leave_type) + self.assertEquals(leave_ledger_entry[0].leaves, leave_encashment.encashable_days * -1) + + # check if leave ledger entry is deleted on cancellation + leave_encashment.cancel() + self.assertFalse(frappe.db.exists("Leave Ledger Entry", {'transaction_name':leave_encashment.name})) From 45cf02308e8ef0effb3260d340988b95b342f5a2 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Thu, 23 May 2019 20:29:18 +0530 Subject: [PATCH 34/98] fix: prevent manual creation of ledger entries --- .../leave_ledger_entry.json | 49 ++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.json b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.json index 89c703db65..ae36735570 100644 --- a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.json +++ b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.json @@ -19,6 +19,7 @@ { "fieldname": "employee", "fieldtype": "Link", + "in_list_view": 1, "label": "Employee", "options": "Employee" }, @@ -30,6 +31,7 @@ { "fieldname": "leave_type", "fieldtype": "Link", + "in_list_view": 1, "label": "Leave Type", "options": "Leave Type" }, @@ -57,16 +59,19 @@ { "fieldname": "leaves", "fieldtype": "Int", + "in_list_view": 1, "label": "Leaves" }, { "fieldname": "from_date", "fieldtype": "Date", + "in_list_view": 1, "label": "From Date" }, { "fieldname": "to_date", "fieldtype": "Date", + "in_list_view": 1, "label": "To Date" }, { @@ -80,8 +85,9 @@ "label": "Is Expired" } ], + "in_create": 1, "is_submittable": 1, - "modified": "2019-05-13 12:56:45.542495", + "modified": "2019-05-23 19:22:09.028366", "modified_by": "Administrator", "module": "HR", "name": "Leave Ledger Entry", @@ -97,6 +103,47 @@ "report": 1, "role": "System Manager", "share": 1, + "submit": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "All", + "share": 1, + "submit": 1, "write": 1 } ], From f3926d0fcbd57e7e3c59f33b2992a699ce3902a7 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Sun, 26 May 2019 20:17:16 +0530 Subject: [PATCH 35/98] patch: leave ledger entries --- erpnext/patches.txt | 1 + .../v12_0/generate_leave_ledger_entries.py | 95 +++++++++++++++++++ 2 files changed, 96 insertions(+) create mode 100644 erpnext/patches/v12_0/generate_leave_ledger_entries.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index bdc1ed4f10..a7fb7a2bb5 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -602,3 +602,4 @@ erpnext.patches.v11_1.set_salary_details_submittable erpnext.patches.v11_1.rename_depends_on_lwp execute:frappe.delete_doc("Report", "Inactive Items") erpnext.patches.v11_1.delete_scheduling_tool +erpnext.patches.v12_0.generate_leave_ledger_entries \ No newline at end of file diff --git a/erpnext/patches/v12_0/generate_leave_ledger_entries.py b/erpnext/patches/v12_0/generate_leave_ledger_entries.py new file mode 100644 index 0000000000..9223bb2ebc --- /dev/null +++ b/erpnext/patches/v12_0/generate_leave_ledger_entries.py @@ -0,0 +1,95 @@ +# Copyright (c) 2018, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals +import frappe + +def execute(): + """ Generates leave ledger entries for leave allocation/application/encashment + for last allocation """ + if frappe.db.exists("DocType","Leave Ledger Entry"): + return + + allocation_list = get_allocation_records() + generate_allocation_ledger_entries(allocation_list) + generate_application_leave_ledger_entries(allocation_list) + generate_encashment_leave_ledger_entries(allocation_list) + +def generate_allocation_ledger_entries(allocation_list): + ''' fix ledger entries for missing leave allocation transaction ''' + from erpnext.hr.doctype.leave_allocation.leave_allocation import LeaveAllocation + + for allocation in allocation_list: + LeaveAllocation.create_leave_ledger_entry(allocation) + +def generate_application_leave_ledger_entries(allocation_list): + ''' fix ledger entries for missing leave application transaction ''' + from erpnext.hr.doctype.leave_application.leave_application import LeaveApplication + + leave_applications = get_leaves_application_records(allocation_list) + + for record in leave_applications: + LeaveApplication.create_leave_ledger_entry(record) + +def generate_encashment_leave_ledger_entries(allocation_list): + ''' fix ledger entries for missing leave encashment transaction ''' + from erpnext.hr.doctype.leave_encashment.leave_encashment import LeaveEncashment + + leave_encashments = get_leave_encashment_records(allocation_list) + + for record in leave_encashments: + LeaveEncashment.create_leave_ledger_entry(record) + +def get_allocation_records(): + return frappe.db.sql(""" + SELECT + DISTINCT name, + employee, + leave_type, + new_leaves_allocated, + carry_forwarded_leaves, + from_date, + to_date, + carry_forward, + RANK() OVER(PARTITION BY employee, leave_type ORDER BY to_date DESC) as allocation + FROM `tabLeave Allocation` + WHERE + allocation=1 + """, as_dict=1) + +def get_leaves_application_records(allocation_list): + leave_applications = [] + for allocation in allocation_list: + leave_applications.append(frappe.db.sql(""" + SELECT + name, + employee, + leave_type, + total_leave_days, + from_date, + to_date + FROM `tabLeave Application` + WHERE + from_date >= %s + leave_type = %s + employee = %s + """, (allocation.from_date, allocation.leave_type, allocation.employee))) + return leave_applications + +def get_leave_encashment_records(allocation_list): + leave_encashments = [] + for allocation in allocation_list: + leave_encashments.append(frappe.db.sql(""" + SELECT + DISTINCT name, + employee, + leave_type, + encashable_days, + from_date, + to_date + FROM `tabLeave Encashment` + WHERE + leave_type = %s + employee = %s + """, (allocation.leave_type, allocation.employee))) + return leave_encashments \ No newline at end of file From 9e3b6883332ac26d6aca89c147e1cda233709960 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Mon, 27 May 2019 03:10:39 +0530 Subject: [PATCH 36/98] test: create leave policy --- .../doctype/leave_policy/test_leave_policy.py | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/erpnext/hr/doctype/leave_policy/test_leave_policy.py b/erpnext/hr/doctype/leave_policy/test_leave_policy.py index 2c6f1d0a21..fc868ea15a 100644 --- a/erpnext/hr/doctype/leave_policy/test_leave_policy.py +++ b/erpnext/hr/doctype/leave_policy/test_leave_policy.py @@ -12,16 +12,20 @@ class TestLeavePolicy(unittest.TestCase): if random_leave_type: random_leave_type = random_leave_type[0] leave_type = frappe.get_doc("Leave Type", random_leave_type.name) - old_max_leaves_allowed = leave_type.max_leaves_allowed leave_type.max_leaves_allowed = 2 leave_type.save() - leave_policy_details = { - "doctype": "Leave Policy", - "leave_policy_details": [{ - "leave_type": leave_type.name, - "annual_allocation": leave_type.max_leaves_allowed + 1 - }] - } + leave_policy = create_leave_policy(leave_type=leave_type.name, annual_allocation=leave_type.max_leaves_allowed + 1) - self.assertRaises(frappe.ValidationError, frappe.get_doc(leave_policy_details).insert) + self.assertRaises(frappe.ValidationError, leave_policy.insert) + +def create_leave_policy(**args): + ''' Returns an object of leave policy ''' + args = frappe._dict(args) + return frappe.get_doc({ + "doctype": "Leave Policy", + "leave_policy_details": [{ + "leave_type": args.leave_type or "_Test Leave Type", + "annual_allocation": args.annual_allocation or 10 + }] + }) \ No newline at end of file From 7a7f4bd822f0474ba9b97d85786b66d20369644d Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Mon, 27 May 2019 03:12:57 +0530 Subject: [PATCH 37/98] fix: leave balance calculation --- .../leave_application/leave_application.js | 3 +- .../leave_application/leave_application.py | 127 +++++++++++++----- 2 files changed, 92 insertions(+), 38 deletions(-) diff --git a/erpnext/hr/doctype/leave_application/leave_application.js b/erpnext/hr/doctype/leave_application/leave_application.js index 5bce348489..0b0a69f7a1 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.js +++ b/erpnext/hr/doctype/leave_application/leave_application.js @@ -143,7 +143,8 @@ frappe.ui.form.on("Leave Application", { method: "erpnext.hr.doctype.leave_application.leave_application.get_leave_balance_on", args: { employee: frm.doc.employee, - date: frm.doc.from_date, + from_date: frm.doc.from_date, + to_date: frm.doc.to_date, leave_type: frm.doc.leave_type, consider_all_leaves_in_the_allocation_period: true }, diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py index 9e0ed0f1b5..2adf8fba71 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.py +++ b/erpnext/hr/doctype/leave_application/leave_application.py @@ -195,9 +195,8 @@ class LeaveApplication(Document): frappe.throw(_("The day(s) on which you are applying for leave are holidays. You need not apply for leave.")) if not is_lwp(self.leave_type): - self.leave_balance = get_leave_balance_on(self.employee, self.leave_type, self.from_date, docname=self.name, + 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) - if self.status != "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}") @@ -351,13 +350,46 @@ class LeaveApplication(Document): pass def create_leave_ledger_entry(self, submit=True): - args = frappe._dict( - leaves=self.total_leave_days, + expiry_date = get_allocation_expiry(self.employee, self.leave_type, + self.to_date, self.from_date) + + if expiry_date: + self.create_application_entry_for_expiry(expiry_date) + else: + args = dict( + leaves=self.total_leave_days * -1, + from_date=self.from_date, + to_date=self.to_date, + ) + create_leave_ledger_entry(self, args, submit) + + def create_application_entry_for_expiry(self, expiry_date): + ''' splits leave application into two ledger entries to consider expiry ''' + args = dict( from_date=self.from_date, to_date=self.to_date, - is_carry_forward=0 + leaves=date_diff(expiry_date, self.from_date) * -1 ) create_leave_ledger_entry(self, args, submit) + start_date = add_days(expiry_date, 1) + args.update(dict( + from_date=start_date, + to_date=self.to_date, + leaves=date_diff(self.to_date, start_date) * -1 + )) + create_leave_ledger_entry(self, args, submit) + +def get_allocation_expiry(employee, leave_type, to_date, from_date): + return frappe.db.sql(""" + SELECT + to_date + FROM `tabLeave Ledger Entry` + WHERE + employee='%s' + AND leave_type='%s' + AND transaction_type='Leave Allocation' + AND (to_date BETWEEN %s AND %s) + """ %(employee, leave_type, from_date, to_date), as_dict=1) @frappe.whitelist() def get_number_of_leave_days(employee, leave_type, from_date, to_date, half_day = None, half_day_date = None): @@ -380,10 +412,11 @@ def get_leave_details(employee, date): leave_allocation = {} for d in allocation_records: allocation = allocation_records.get(d, frappe._dict()) + remaining_leaves = get_leave_balance_on(employee, d, date) 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, @@ -397,37 +430,45 @@ def get_leave_details(employee, date): return ret -def get_leave_balance_on(employee, leave_type, date, allocation_records=None, docname=None, - consider_all_leaves_in_the_allocation_period=False, consider_encashed_leaves=True): +@frappe.whitelist() +def get_leave_balance_on(employee, leave_type, from_date, to_date=nowdate(), allocation_records=None, docname=None, + consider_all_leaves_in_the_allocation_period=False): if allocation_records == None: - allocation_records = get_leave_allocation_records(date, employee).get(employee, frappe._dict()) + allocation_records = get_leave_allocation_records(from_date, employee).get(employee, frappe._dict()) allocation = allocation_records.get(leave_type, frappe._dict()) - if consider_all_leaves_in_the_allocation_period: - date = allocation.to_date - leaves_taken = frappe.db.get_value("Leave Ledger Entry", filters={ + + end_date = allocation.to_date if consider_all_leaves_in_the_allocation_period else from_date + expiry = get_allocation_expiry(employee, leave_type, to_date, from_date) + + leaves_taken = get_leaves_taken(employee, leave_type, allocation.from_date, end_date) + return get_remaining_leaves(allocation, leaves_taken, from_date, expiry) + +def get_remaining_leaves(allocation, leaves_taken, date, expiry): + ''' Returns leaves remaining after comparing with remaining days for allocation expiry ''' + def _get_remaining_leaves(allocated_leaves, end_date): + remaining_leaves = flt(allocated_leaves) + flt(leaves_taken) + + if remaining_leaves > 0: + remaining_days = date_diff(end_date, date) + 1 + remaining_leaves = min(remaining_days, remaining_leaves) + return remaining_leaves + + if expiry: + remaining_leaves = _get_remaining_leaves(allocation.carry_forwarded_leaves, expiry) + return flt(allocation.new_leaves_allocated) + flt(remaining_leaves) + else: + return _get_remaining_leaves(allocation.total_leaves_allocated, allocation.to_date) + +def get_leaves_taken(employee, leave_type, from_date, to_date): + ''' Returns leaves taken based on leave application/encashment ''' + return frappe.db.get_value("Leave Ledger Entry", filters={ 'Employee':employee, 'leave_type':leave_type, - 'transaction_type': ('!=', 'Leave Allocation'), - 'to_date':("<=", date), - 'from_date': from_date}, - fields=['SUM(leaves)']) - - remaining_unused_leaves = flt(allocation.unused_leaves + leaves_taken) - if allocation.carry_forward and remaining_unused_leaves > 0: - expiry_date = frappe.db.get_value("Leave Ledger Entry", filters={ - 'Employee':employee, - 'leave_type':leave_type, - 'transaction_type': 'Leave Allocation', - 'transaction_name': allocation.name, - 'is_carry_forward': 1, - 'is_expiry': 0 - }, fieldname=['to_date']) - remaining_days = date_diff(expiry_date, date) + 1 - - remaining_unused_leaves = min(remaining_days, remaining_unused_leaves) - - return flt(allocation.new_leaves) + remaining_unused_leaves + leaves_taken + 'leaves': ("<", 0), + 'to_date':("<=", to_date), + 'from_date': (">=", from_date)}, + fieldname=['SUM(leaves)']) def get_total_allocated_leaves(employee, leave_type, date): filters= { @@ -479,19 +520,31 @@ def get_leave_allocation_records(date, employee=None): conditions = (" and employee='%s'" % employee) if employee else "" leave_allocation_records = frappe.db.sql(""" - select employee, leave_type, total_leaves_allocated, total_leaves_encashed, from_date, to_date - from `tabLeave Allocation` - where %s between from_date and to_date and docstatus=1 {0}""".format(conditions), (date), as_dict=1) + SELECT + employee, + leave_type, + total_leaves_allocated, + carry_forwarded_leaves, + new_leaves_allocated, + carry_forward, + from_date, + to_date + FROM + `tabLeave Allocation` + WHERE + %s between from_date and to_date + AND docstatus=1 {0}""".format(conditions), (date), as_dict=1) #nosec allocated_leaves = frappe._dict() for d in leave_allocation_records: allocated_leaves.setdefault(d.employee, frappe._dict()).setdefault(d.leave_type, frappe._dict({ "from_date": d.from_date, "to_date": d.to_date, + "total_leaves_allocated": d.total_leaves_allocated, "carry_forward": d.carry_forward, - "unused_leaves": d.carry_forwarded_leaves, + "carry_forwarded_leaves": d.carry_forwarded_leaves, "new_leaves": d.new_leaves_allocated, - "name": d.name + "leave_type": d.leave_type })) return allocated_leaves From c99f644ffe661af081df406824dda294a7aef4c2 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Mon, 27 May 2019 03:13:47 +0530 Subject: [PATCH 38/98] test: carry forward and expiry allocation --- .../leave_allocation/test_leave_allocation.py | 68 ++++++++++++++++++- 1 file changed, 65 insertions(+), 3 deletions(-) diff --git a/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py b/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py index a0113e2d9f..308f2245b9 100644 --- a/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py +++ b/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py @@ -3,6 +3,7 @@ import frappe import unittest 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_ledger_entry.leave_ledger_entry import process_expired_allocation class TestLeaveAllocation(unittest.TestCase): def test_overlapping_allocation(self): @@ -68,6 +69,65 @@ class TestLeaveAllocation(unittest.TestCase): #allocated leave more than period self.assertRaises(frappe.ValidationError, doc.save) + def test_carry_forward_calculation(self): + frappe.db.sql("delete from `tabLeave Allocation`") + frappe.db.sql("delete from `tabLeave Ledger Entry`") + leave_type = create_leave_type(leave_type_name="_Test_CF_leave", is_carry_forward=1) + leave_type.submit() + + # initial leave allocation + leave_allocation = create_leave_allocation( + leave_type="_Test_CF_leave", + from_date=add_months(nowdate(), -12), + to_date=add_months(nowdate(), -1), + carry_forward=1) + leave_allocation.submit() + + # leave allocation with carry forward from previous allocation + leave_allocation_1 = create_leave_allocation( + leave_type="_Test_CF_leave", + carry_forward=1) + leave_allocation_1.submit() + + self.assertEquals(leave_allocation.total_leaves_allocated, leave_allocation_1.carry_forwarded_leaves) + + def test_carry_forward_leaves_expiry(self): + frappe.db.sql("delete from `tabLeave Allocation`") + frappe.db.sql("delete from `tabLeave Ledger Entry`") + leave_type = create_leave_type( + leave_type_name="_Test_CF_leave_expiry", + is_carry_forward=1, + carry_forward_leave_expiry=90) + leave_type.submit() + + # initial leave allocation + leave_allocation = create_leave_allocation( + leave_type="_Test_CF_leave_expiry", + from_date=add_months(nowdate(), -24), + to_date=add_months(nowdate(), -12), + carry_forward=1) + leave_allocation.submit() + + leave_allocation = create_leave_allocation( + leave_type="_Test_CF_leave_expiry", + from_date=add_days(nowdate(), -90), + to_date=add_days(nowdate(), 100), + carry_forward=1) + leave_allocation.submit() + + # expires all the carry forwarded leaves after 90 days + process_expired_allocation() + + # leave allocation with carry forward of only new leaves allocated + leave_allocation_1 = create_leave_allocation( + leave_type="_Test_CF_leave_expiry", + carry_forward=1, + from_date=add_months(nowdate(), 6), + to_date=add_months(nowdate(), 12)) + leave_allocation_1.submit() + + self.assertEquals(leave_allocation_1.carry_forwarded_leaves, leave_allocation.new_leaves_allocated) + def test_creation_of_leave_ledger_entry_on_submit(self): frappe.db.sql("delete from `tabLeave Allocation`") @@ -92,11 +152,13 @@ def create_leave_allocation(**args): leave_allocation = frappe.get_doc({ "doctype": "Leave Allocation", "__islocal": 1, - "employee": employee.name, - "employee_name": employee.employee_name, - "leave_type": args.leave_type.leave_type_name or "_Test Leave Type", + "employee": args.employee or employee.name, + "employee_name": args.employee_name or employee.employee_name, + "leave_type": args.leave_type or "_Test Leave Type", "from_date": args.from_date or nowdate(), "new_leaves_allocated": args.new_leaves_created or 15, + "carry_forward": args.carry_forward or 0, + "carry_forwarded_leaves": args.carry_forwarded_leaves or 0, "to_date": args.to_date or add_months(nowdate(), 12) }) return leave_allocation From 2417c93d0eaff061cff7416d7f5f27ace72ecfeb Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Mon, 27 May 2019 03:20:10 +0530 Subject: [PATCH 39/98] feat: create expiry and carry forward calculation on leave allocation creation --- .../leave_allocation/leave_allocation.js | 2 +- .../leave_allocation/leave_allocation.py | 63 +++++++++++-------- 2 files changed, 38 insertions(+), 27 deletions(-) diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation.js b/erpnext/hr/doctype/leave_allocation/leave_allocation.js index 228b5528de..1fc1d89635 100755 --- a/erpnext/hr/doctype/leave_allocation/leave_allocation.js +++ b/erpnext/hr/doctype/leave_allocation/leave_allocation.js @@ -66,4 +66,4 @@ frappe.ui.form.on("Leave Allocation", { frm.set_value("total_leaves_allocated", flt(frm.doc.new_leaves_allocated)); } } -}) \ No newline at end of file +}); \ No newline at end of file diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation.py b/erpnext/hr/doctype/leave_allocation/leave_allocation.py index 8429ad4308..6fe8fdff24 100755 --- a/erpnext/hr/doctype/leave_allocation/leave_allocation.py +++ b/erpnext/hr/doctype/leave_allocation/leave_allocation.py @@ -3,7 +3,7 @@ from __future__ import unicode_literals import frappe -from frappe.utils import flt, date_diff, formatdate +from frappe.utils import flt, date_diff, formatdate, add_days from frappe import _ from frappe.model.document import Document from erpnext.hr.utils import set_employee_name, get_leave_period @@ -42,6 +42,7 @@ class LeaveAllocation(Document): .format(self.leave_type, self.employee)) def on_submit(self): + self.expire_previous_allocation() self.create_leave_ledger_entry() def on_cancel(self): @@ -89,10 +90,20 @@ class LeaveAllocation(Document): self.leave_type, self.from_date, self.carry_forward) self.total_leaves_allocated = flt(self.carry_forwarded_leaves) + flt(self.new_leaves_allocated) + self.maintain_carry_forwarded_leaves() 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))) + def maintain_carry_forwarded_leaves(self): + ''' 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") + 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.total_leaves_allocated = flt(max_leaves_allowed) + def validate_total_leaves_allocated(self): # Adding a day to include To Date in the difference date_difference = date_diff(self.to_date, self.from_date) + 1 @@ -102,9 +113,9 @@ class LeaveAllocation(Document): def create_leave_ledger_entry(self, submit=True): if self.carry_forwarded_leaves: expiry_days = frappe.db.get_value("Leave Type", self.leave_type, "carry_forward_leave_expiry") - args = dict( leaves=self.carry_forwarded_leaves, + from_date=self.from_date, to_date=add_days(self.from_date, expiry_days) if expiry_days else self.to_date, is_carry_forward=1 ) @@ -112,11 +123,26 @@ class LeaveAllocation(Document): args = dict( leaves=self.new_leaves_allocated, + from_date=self.from_date, to_date=self.to_date, is_carry_forward=0 ) create_leave_ledger_entry(self, args, submit) + def expire_previous_allocation(self): + ''' expire previous allocation leaves ''' + leaves = get_remaining_leaves(self.employee, self.leave_type, self.from_date) + + if flt(leaves) > 0: + args = dict( + leaves=leaves * -1, + from_date=self.to_date, + to_date=self.to_date, + is_carry_forward=0, + is_expired=1 + ) + create_leave_ledger_entry(self, args) + def get_leave_allocation_for_period(employee, leave_type, from_date, to_date): leave_allocated = 0 leave_allocations = frappe.db.sql(""" @@ -143,35 +169,20 @@ def get_leave_allocation_for_period(employee, leave_type, from_date, to_date): @frappe.whitelist() def get_carry_forwarded_leaves(employee, leave_type, date, carry_forward=None): carry_forwarded_leaves = 0 - if carry_forward: validate_carry_forward(leave_type) - - carry_forwarded_leaves = frappe.db.sql(""" - SELECT - SUM(leaves) - FROM `tabLeave Ledger Entry` - WHERE - employee=%s - AND leave_type=%s - AND docstatus=1 - AND to_date < %s - AND name NOT IN ( - SELECT name - from `tabLeave Ledger Entry` - WHERE - employee=%s - AND leave_type=%s - AND docstatus=1 - AND to_date < %s - is_expired=1 - ORDER BY creation DESC - ) - ORDER BY creation DESC - """, (employee, leave_type, date), as_dict=1) + carry_forwarded_leaves = get_remaining_leaves(employee, leave_type, date) return carry_forwarded_leaves +def get_remaining_leaves(employee, leave_type, date): + return frappe.db.get_value("Leave Ledger Entry", filters={ + "to_date": ("<=", date), + "employee": employee, + "docstatus": 1, + "leave_type": leave_type, + }, fieldname=['SUM(leaves)']) + def validate_carry_forward(leave_type): if not frappe.db.get_value("Leave Type", leave_type, "is_carry_forward"): frappe.throw(_("Leave Type {0} cannot be carry-forwarded").format(leave_type)) \ No newline at end of file From e7d307e6bf98eceb2741bf36d848263611043b71 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Mon, 27 May 2019 03:21:32 +0530 Subject: [PATCH 40/98] fix: only expire carry forwarded allocation via scheduler --- erpnext/hooks.py | 2 +- .../leave_ledger_entry/leave_ledger_entry.py | 115 ++++++++++-------- 2 files changed, 65 insertions(+), 52 deletions(-) diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 72c538fd3c..28dd4e8f9c 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -264,7 +264,7 @@ scheduler_events = { "erpnext.projects.doctype.project.project.update_project_sales_billing", "erpnext.projects.doctype.project.project.send_project_status_email_to_users", "erpnext.quality_management.doctype.quality_review.quality_review.review", - "erpnext.hr.doctype.leave_ledger_entry.leave_ledger_entry.check_expired_allocation" + "erpnext.hr.doctype.leave_ledger_entry.leave_ledger_entry.process_expired_allocation" "erpnext.support.doctype.service_level_agreement.service_level_agreement.check_agreement_status" ], "daily_long": [ diff --git a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py index 1c8fed3d7e..e85d5cef0a 100644 --- a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py +++ b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py @@ -6,14 +6,34 @@ from __future__ import unicode_literals import frappe from frappe.model.document import Document from frappe import _ -from frappe.utils import add_days, today +from frappe.utils import add_days, today, flt class LeaveLedgerEntry(Document): def validate_entries(self): - leave_records = frappe.get_all('Leave Ledger Entry', ['leaves']) - if sum(record.get("leaves") for record in leave_records) <0: + total_leaves = frappe.get_all('Leave Ledger Entry', ['SUM(leaves)']) + if total_leaves < 0: frappe.throw(_("Invalid Ledger Entry")) + def on_cancel(self): + # allow cancellation of expiry leaves + if not self.is_expired: + frappe.throw(_("Only expired allocation can be cancelled")) + +def validate_leave_allocation_against_leave_application(ledger): + ''' Checks that leave allocation has no leave application against it ''' + leave_application_records = frappe.get_all("Leave Ledger Entry", + filters={ + 'employee': ledger.employee, + 'leave_type': ledger.leave_type, + 'transaction_type': 'Leave Application', + 'from_date': (">=", ledger.from_date), + 'to_date': ('<=', ledger.to_date) + }, fields=['transaction_name']) + + if leave_application_records: + frappe.throw(_("Leave allocation %s is linked with leave application %s" + % (ledger.transaction_name, ', '.join(leave_application_records)))) + def create_leave_ledger_entry(ref_doc, args, submit=True): ledger = frappe._dict( doctype='Leave Ledger Entry', @@ -22,6 +42,8 @@ def create_leave_ledger_entry(ref_doc, args, submit=True): leave_type=ref_doc.leave_type, transaction_type=ref_doc.doctype, transaction_name=ref_doc.name, + is_carry_forward=0, + is_expired=0 ) ledger.update(args) if submit: @@ -32,67 +54,58 @@ def create_leave_ledger_entry(ref_doc, args, submit=True): def delete_ledger_entry(ledger): ''' Delete ledger entry on cancel of leave application/allocation/encashment ''' - leave_application_records = [] - # prevent deletion when leave application has been created after allocation if ledger.transaction_type == "Leave Allocation": - leave_application_records = frappe.get_all("Leave Ledger Entry", + validate_leave_allocation_against_leave_application(ledger) + + frappe.db.sql("""DELETE + FROM `tabLeave Ledger Entry` + WHERE + `transaction_name`=%s""", (ledger.transaction_name)) + +def process_expired_allocation(): + ''' Check if a carry forwarded allocation has expired and create a expiry ledger entry ''' + + # fetch leave type records that has carry forwarded leaves expiry + leave_type_records = frappe.db.get_values("Leave Type", filters={ + 'carry_forward_leave_expiry': (">", 0) + }, fieldname=['name']) + + if leave_type_records: + leave_type = [record[0] for record in leave_type_records] + expired_allocation = frappe.get_all("Leave Ledger Entry", filters={ - 'employee': ledger.employee, - 'leave_type': ledger.leave_type, - 'transaction_type': 'Leave Application', - 'from_date': (">=", ledger.from_date), - 'to_date': ('<=', ledger.to_date) - }, - fields=['transaction_name']) - - if not leave_application_records: - frappe.db.sql("""DELETE - FROM `tabLeave Ledger Entry` - WHERE - `transaction_name`=%s""", (ledger.transaction_name)) - else: - frappe.throw(_("Leave allocation %s is linked with leave application %s" - % (ledger_entry, ', '.join(leave_application_records)))) - -def check_expired_allocation(): - ''' Checks for expired allocation by comparing to_date with current_date and - based on that creates an expiry ledger entry ''' - expired_allocation = frappe.get_all("Leave Ledger Allocation", - filters={ - 'to_date': today(), - 'transaction_type': 'Leave Allocation' - }, - fields=['*']) + 'to_date': today(), + 'transaction_type': 'Leave Allocation', + 'is_carry_forward': 1, + 'leave_type': ('in', leave_type) + }, fields=['leaves', 'to_date', 'employee', 'leave_type']) if expired_allocation: create_expiry_ledger_entry(expired_allocation) def create_expiry_ledger_entry(expired_allocation): + ''' Create expiry ledger entry for carry forwarded leaves ''' for allocation in expired_allocation: - filters = { - 'employee': allocation.employee, - 'leave_type': allocation.leave_type, - 'from_date': ('>=', allocation.from_date), - } - # get only application ledger entries in case of carry forward - if allocation.is_carry_forward: - filters.update(dict(transaction_type='Leave Application')) - leave_records = frappe.get_all("Leave Ledger Entry", - filters=filters, - fields=['leaves']) - - leaves = sum(record.get("leaves") for record in leave_records) - - if allocation.is_carry_forward: - leaves = allocation.leaves + leaves + leaves_taken = get_leaves_taken(allocation) + leaves = flt(allocation.leaves) + flt(leaves_taken) if leaves > 0: args = frappe._dict( leaves=allocation.leaves * -1, - to_date='', - is_carry_forward=allocation.is_carry_forward, + to_date=allocation.to_date, + is_carry_forward=1, is_expired=1, from_date=allocation.to_date ) - create_leave_ledger_entry(allocation, args) \ No newline at end of file + create_leave_ledger_entry(allocation, args) + +def get_leaves_taken(allocation): + return frappe.db.get_value("Leave Ledger Entry", + filters={ + 'employee': allocation.employee, + 'leave_type': allocation.leave_type, + 'from_date': ('>=', allocation.from_date), + 'to_date': ('<=', allocation.to_date), + 'transaction_type': 'Leave application' + }, fieldname=['SUM(leaves)']) \ No newline at end of file From d751281fa721c0ac75e452826e61f381c91a6186 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Mon, 27 May 2019 03:23:58 +0530 Subject: [PATCH 41/98] fix: application and leave encashment test cases --- .../test_leave_application.py | 31 +++++++++++++------ .../leave_encashment/leave_encashment.py | 1 + 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/erpnext/hr/doctype/leave_application/test_leave_application.py b/erpnext/hr/doctype/leave_application/test_leave_application.py index 2581c2a659..4dea413dc3 100644 --- a/erpnext/hr/doctype/leave_application/test_leave_application.py +++ b/erpnext/hr/doctype/leave_application/test_leave_application.py @@ -8,6 +8,8 @@ import unittest from erpnext.hr.doctype.leave_application.leave_application import LeaveDayBlockedError, OverlapError, NotAnOptionalHoliday, get_leave_balance_on from frappe.permissions import clear_user_permissions_for_doctype from frappe.utils import add_days, nowdate, now_datetime, getdate +from erpnext.hr.doctype.leave_type.test_leave_type import create_leave_type +from erpnext.hr.doctype.leave_allocation.test_leave_allocation import create_leave_allocation test_dependencies = ["Leave Allocation", "Leave Block List"] @@ -274,7 +276,7 @@ class TestLeaveApplication(unittest.TestCase): )) # can only apply on optional holidays - self.assertTrue(NotAnOptionalHoliday, leave_application.insert) + self.assertRaises(NotAnOptionalHoliday, leave_application.insert) leave_application.from_date = today leave_application.to_date = today @@ -287,6 +289,8 @@ class TestLeaveApplication(unittest.TestCase): def test_leaves_allowed(self): + frappe.db.sql("delete from `tabLeave Allocation`") + frappe.db.sql("delete from `tabLeave Ledger Entry`") employee = get_employee() leave_period = get_leave_period() frappe.delete_doc_if_exists("Leave Type", "Test Leave Type", force=1) @@ -301,7 +305,7 @@ class TestLeaveApplication(unittest.TestCase): allocate_leaves(employee, leave_period, leave_type.name, 5) leave_application = frappe.get_doc(dict( - doctype = 'Leave Application', + doctype = 'Leave Application', employee = employee.name, leave_type = leave_type.name, from_date = date, @@ -310,15 +314,14 @@ class TestLeaveApplication(unittest.TestCase): docstatus = 1, status = "Approved" )) - - self.assertTrue(leave_application.insert()) + leave_application.submit() leave_application = frappe.get_doc(dict( doctype = 'Leave Application', employee = employee.name, leave_type = leave_type.name, from_date = add_days(date, 4), - to_date = add_days(date, 7), + to_date = add_days(date, 8), company = "_Test Company", docstatus = 1, status = "Approved" @@ -458,22 +461,32 @@ class TestLeaveApplication(unittest.TestCase): self.assertEqual(leave_application.docstatus, 1) def test_creation_of_leave_ledger_entry_on_submit(self): + frappe.db.sql("delete from `tabLeave Allocation`") + employee = get_employee() + + leave_type = create_leave_type(leave_type_name = 'Test Leave Type 1') + leave_type.save() + + leave_allocation = create_leave_allocation(employee=employee.name, employee_name=employee.employee_name, + leave_type=leave_type.name) + leave_allocation.submit() leave_application = frappe.get_doc(dict( doctype = 'Leave Application', employee = employee.name, - leave_type = leave_type_1.name, - from_date = nowdate(), + leave_type = leave_type.name, + from_date = add_days(nowdate(), 1), to_date = add_days(nowdate(), 4), company = "_Test Company", docstatus = 1, status = "Approved" - )).submit() + )) + leave_application.submit() leave_ledger_entry = frappe.get_all('Leave Ledger Entry', fields='*', filters=dict(transaction_name=leave_application.name)) self.assertEquals(leave_ledger_entry[0].employee, leave_application.employee) self.assertEquals(leave_ledger_entry[0].leave_type, leave_application.leave_type) - self.assertEquals(leave_ledger_entry[0].leaves, leave_application.new_leaves_allocated) + self.assertEquals(leave_ledger_entry[0].leaves, leave_application.total_leave_days * -1) # check if leave ledger entry is deleted on cancellation leave_application.cancel() diff --git a/erpnext/hr/doctype/leave_encashment/leave_encashment.py b/erpnext/hr/doctype/leave_encashment/leave_encashment.py index 4f4a8760cf..c59ce63c7c 100644 --- a/erpnext/hr/doctype/leave_encashment/leave_encashment.py +++ b/erpnext/hr/doctype/leave_encashment/leave_encashment.py @@ -84,6 +84,7 @@ class LeaveEncashment(Document): args = frappe._dict( leaves=self.encashable_days * -1, from_date=self.encashment_date, + to_date=self.encashable_days, is_carry_forward=0 ) create_leave_ledger_entry(self, args, submit) \ No newline at end of file From 45197965d7ab957ccd7b493478655cd213d72469 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Mon, 27 May 2019 03:26:48 +0530 Subject: [PATCH 42/98] fix: give cancellation permission to hr manager --- erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.json b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.json index ae36735570..ba33d75029 100644 --- a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.json +++ b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.json @@ -87,7 +87,7 @@ ], "in_create": 1, "is_submittable": 1, - "modified": "2019-05-23 19:22:09.028366", + "modified": "2019-05-27 03:25:47.805142", "modified_by": "Administrator", "module": "HR", "name": "Leave Ledger Entry", @@ -107,6 +107,7 @@ "write": 1 }, { + "cancel": 1, "create": 1, "delete": 1, "email": 1, From 24248f687b4067ce56bb13f94274170310683979 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Mon, 27 May 2019 10:58:42 +0530 Subject: [PATCH 43/98] patch: create entries for only missing transactions --- .../v12_0/generate_leave_ledger_entries.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/erpnext/patches/v12_0/generate_leave_ledger_entries.py b/erpnext/patches/v12_0/generate_leave_ledger_entries.py index 9223bb2ebc..9c638abaff 100644 --- a/erpnext/patches/v12_0/generate_leave_ledger_entries.py +++ b/erpnext/patches/v12_0/generate_leave_ledger_entries.py @@ -7,7 +7,7 @@ import frappe def execute(): """ Generates leave ledger entries for leave allocation/application/encashment for last allocation """ - if frappe.db.exists("DocType","Leave Ledger Entry"): + if frappe.db.a_row_exists("Leave Ledger Entry"): return allocation_list = get_allocation_records() @@ -20,7 +20,9 @@ def generate_allocation_ledger_entries(allocation_list): from erpnext.hr.doctype.leave_allocation.leave_allocation import LeaveAllocation for allocation in allocation_list: - LeaveAllocation.create_leave_ledger_entry(allocation) + if not frappe.db.exists("Leave Ledger Entry", {'transaction_type': 'Leave Allocation', + 'transaction_name': allocation.name}): + LeaveAllocation.create_leave_ledger_entry(allocation) def generate_application_leave_ledger_entries(allocation_list): ''' fix ledger entries for missing leave application transaction ''' @@ -29,7 +31,9 @@ def generate_application_leave_ledger_entries(allocation_list): leave_applications = get_leaves_application_records(allocation_list) for record in leave_applications: - LeaveApplication.create_leave_ledger_entry(record) + if not frappe.db.exists("Leave Ledger Entry", {'transaction_type': 'Leave Application', + 'transaction_name': record.name}): + LeaveApplication.create_leave_ledger_entry(record) def generate_encashment_leave_ledger_entries(allocation_list): ''' fix ledger entries for missing leave encashment transaction ''' @@ -38,7 +42,9 @@ def generate_encashment_leave_ledger_entries(allocation_list): leave_encashments = get_leave_encashment_records(allocation_list) for record in leave_encashments: - LeaveEncashment.create_leave_ledger_entry(record) + if not frappe.db.exists("Leave Ledger Entry", {'transaction_type': 'Leave Encashment', + 'transaction_name': record.name}): + LeaveEncashment.create_leave_ledger_entry(record) def get_allocation_records(): return frappe.db.sql(""" @@ -62,7 +68,7 @@ def get_leaves_application_records(allocation_list): for allocation in allocation_list: leave_applications.append(frappe.db.sql(""" SELECT - name, + DISTINCT name, employee, leave_type, total_leave_days, From 71cdcb35930e37befd227fc6e0200041eb26aec1 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Mon, 27 May 2019 11:00:04 +0530 Subject: [PATCH 44/98] style: add a more descriptive method name --- erpnext/hr/doctype/leave_application/leave_application.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py index 2adf8fba71..8e8e47e510 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.py +++ b/erpnext/hr/doctype/leave_application/leave_application.py @@ -354,7 +354,7 @@ class LeaveApplication(Document): self.to_date, self.from_date) if expiry_date: - self.create_application_entry_for_expiry(expiry_date) + self.create_ledger_entry_for_intermediate_expiry(expiry_date, submit) else: args = dict( leaves=self.total_leave_days * -1, @@ -363,7 +363,7 @@ class LeaveApplication(Document): ) create_leave_ledger_entry(self, args, submit) - def create_application_entry_for_expiry(self, expiry_date): + def create_ledger_entry_for_intermediate_expiry(self, expiry_date, submit): ''' splits leave application into two ledger entries to consider expiry ''' args = dict( from_date=self.from_date, From 7fbaef5de34d14d4535813179f888336fd7b7076 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Mon, 27 May 2019 15:39:48 +0530 Subject: [PATCH 45/98] fix: expiry ledger creation --- .../leave_application/leave_application.py | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py index 8e8e47e510..e5ee4e6a26 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.py +++ b/erpnext/hr/doctype/leave_application/leave_application.py @@ -364,32 +364,31 @@ class LeaveApplication(Document): create_leave_ledger_entry(self, args, submit) def create_ledger_entry_for_intermediate_expiry(self, expiry_date, submit): - ''' splits leave application into two ledger entries to consider expiry ''' + ''' splits leave application into two ledger entries to consider expiry of allocation ''' args = dict( from_date=self.from_date, to_date=self.to_date, - leaves=date_diff(expiry_date, self.from_date) * -1 + leaves=(date_diff(expiry_date, self.from_date) + 1) * -1 ) create_leave_ledger_entry(self, args, submit) + start_date = add_days(expiry_date, 1) args.update(dict( from_date=start_date, to_date=self.to_date, - leaves=date_diff(self.to_date, start_date) * -1 + leaves=date_diff(self.to_date, expiry_date) * -1 )) create_leave_ledger_entry(self, args, submit) def get_allocation_expiry(employee, leave_type, to_date, from_date): - return frappe.db.sql(""" - SELECT - to_date - FROM `tabLeave Ledger Entry` - WHERE - employee='%s' - AND leave_type='%s' - AND transaction_type='Leave Allocation' - AND (to_date BETWEEN %s AND %s) - """ %(employee, leave_type, from_date, to_date), as_dict=1) + expiry = frappe.get_all("Leave Ledger Entry", + filters={ + 'employee': employee, + 'leave_type': leave_type, + 'transaction_type': 'Leave Allocation', + 'to_date': ['between', (from_date, to_date)] + },fields=['to_date']) + return expiry[0]['to_date'] if expiry else None @frappe.whitelist() def get_number_of_leave_days(employee, leave_type, from_date, to_date, half_day = None, half_day_date = None): @@ -452,10 +451,12 @@ def get_remaining_leaves(allocation, leaves_taken, date, expiry): if remaining_leaves > 0: remaining_days = date_diff(end_date, date) + 1 remaining_leaves = min(remaining_days, remaining_leaves) + return remaining_leaves if expiry: remaining_leaves = _get_remaining_leaves(allocation.carry_forwarded_leaves, expiry) + return flt(allocation.new_leaves_allocated) + flt(remaining_leaves) else: return _get_remaining_leaves(allocation.total_leaves_allocated, allocation.to_date) @@ -543,7 +544,7 @@ def get_leave_allocation_records(date, employee=None): "total_leaves_allocated": d.total_leaves_allocated, "carry_forward": d.carry_forward, "carry_forwarded_leaves": d.carry_forwarded_leaves, - "new_leaves": d.new_leaves_allocated, + "new_leaves_allocated": d.new_leaves_allocated, "leave_type": d.leave_type })) return allocated_leaves From 6f69bbe1d782bb710ddb80111458984a4ac35e55 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Mon, 27 May 2019 15:40:36 +0530 Subject: [PATCH 46/98] test: leave ledger balance --- .../leave_allocation/test_leave_allocation.py | 1 - .../leave_application/leave_application.py | 4 +- .../test_leave_application.py | 70 ++++++++++++++++++- .../leave_encashment/test_leave_encashment.py | 15 ++-- 4 files changed, 79 insertions(+), 11 deletions(-) diff --git a/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py b/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py index 308f2245b9..dfae329da1 100644 --- a/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py +++ b/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py @@ -158,7 +158,6 @@ def create_leave_allocation(**args): "from_date": args.from_date or nowdate(), "new_leaves_allocated": args.new_leaves_created or 15, "carry_forward": args.carry_forward or 0, - "carry_forwarded_leaves": args.carry_forwarded_leaves or 0, "to_date": args.to_date or add_months(nowdate(), 12) }) return leave_allocation diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py index e5ee4e6a26..f3bb9f7eb9 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.py +++ b/erpnext/hr/doctype/leave_application/leave_application.py @@ -354,7 +354,7 @@ class LeaveApplication(Document): self.to_date, self.from_date) if expiry_date: - self.create_ledger_entry_for_intermediate_expiry(expiry_date, submit) + self.create_ledger_entry_for_intermediate_allocation_expiry(expiry_date, submit) else: args = dict( leaves=self.total_leave_days * -1, @@ -363,7 +363,7 @@ class LeaveApplication(Document): ) create_leave_ledger_entry(self, args, submit) - def create_ledger_entry_for_intermediate_expiry(self, expiry_date, submit): + def create_ledger_entry_for_intermediate_allocation_expiry(self, expiry_date, submit): ''' splits leave application into two ledger entries to consider expiry of allocation ''' args = dict( from_date=self.from_date, diff --git a/erpnext/hr/doctype/leave_application/test_leave_application.py b/erpnext/hr/doctype/leave_application/test_leave_application.py index 4dea413dc3..f5f4fa55f5 100644 --- a/erpnext/hr/doctype/leave_application/test_leave_application.py +++ b/erpnext/hr/doctype/leave_application/test_leave_application.py @@ -7,7 +7,7 @@ import unittest from erpnext.hr.doctype.leave_application.leave_application import LeaveDayBlockedError, OverlapError, NotAnOptionalHoliday, get_leave_balance_on from frappe.permissions import clear_user_permissions_for_doctype -from frappe.utils import add_days, nowdate, now_datetime, getdate +from frappe.utils import add_days, nowdate, now_datetime, getdate, add_months from erpnext.hr.doctype.leave_type.test_leave_type import create_leave_type from erpnext.hr.doctype.leave_allocation.test_leave_allocation import create_leave_allocation @@ -287,7 +287,6 @@ class TestLeaveApplication(unittest.TestCase): # check leave balance is reduced self.assertEqual(get_leave_balance_on(employee.name, leave_type, today), 9) - def test_leaves_allowed(self): frappe.db.sql("delete from `tabLeave Allocation`") frappe.db.sql("delete from `tabLeave Ledger Entry`") @@ -404,6 +403,20 @@ class TestLeaveApplication(unittest.TestCase): self.assertRaises(frappe.ValidationError, leave_application.insert) + def test_leave_balance_near_allocaton_expiry(self): + frappe.db.sql("delete from `tabLeave Allocation`") + frappe.db.sql("delete from `tabLeave Ledger Entry`") + employee = get_employee() + leave_type = create_leave_type( + leave_type_name="_Test_CF_leave_expiry", + is_carry_forward=1, + carry_forward_leave_expiry=90) + leave_type.submit() + + create_carry_forwarded_allocation(employee, leave_type) + + self.assertEqual(get_leave_balance_on(employee.name, leave_type.name, nowdate(), add_days(nowdate(), 8)), 21) + def test_earned_leave(self): leave_period = get_leave_period() employee = get_employee() @@ -492,6 +505,59 @@ class TestLeaveApplication(unittest.TestCase): leave_application.cancel() self.assertFalse(frappe.db.exists("Leave Ledger Entry", {'transaction_name':leave_application.name})) + def test_ledger_entry_creation_on_intermediate_allocation_expiry(self): + frappe.db.sql("delete from `tabLeave Allocation`") + frappe.db.sql("delete from `tabLeave Ledger Entry`") + employee = get_employee() + leave_type = create_leave_type( + leave_type_name="_Test_CF_leave_expiry", + is_carry_forward=1, + carry_forward_leave_expiry=90) + leave_type.submit() + + create_carry_forwarded_allocation(employee, leave_type) + + leave_application = frappe.get_doc(dict( + doctype = 'Leave Application', + employee = employee.name, + leave_type = leave_type.name, + from_date = add_days(nowdate(), -3), + to_date = add_days(nowdate(), 7), + company = "_Test Company", + docstatus = 1, + status = "Approved" + )) + leave_application.submit() + + leave_ledger_entry = frappe.get_all('Leave Ledger Entry', '*', filters=dict(transaction_name=leave_application.name)) + + self.assertEquals(len(leave_ledger_entry), 2) + self.assertEquals(leave_ledger_entry[0].employee, leave_application.employee) + self.assertEquals(leave_ledger_entry[0].leave_type, leave_application.leave_type) + self.assertEquals(leave_ledger_entry[0].leaves, -9) + self.assertEquals(leave_ledger_entry[1].leaves, -2) + +def create_carry_forwarded_allocation(employee, leave_type): + + # initial leave allocation + leave_allocation = create_leave_allocation( + leave_type="_Test_CF_leave_expiry", + employee=employee.name, + employee_name=employee.employee_name, + from_date=add_months(nowdate(), -24), + to_date=add_months(nowdate(), -12), + carry_forward=1) + leave_allocation.submit() + + leave_allocation = create_leave_allocation( + leave_type="_Test_CF_leave_expiry", + employee=employee.name, + employee_name=employee.employee_name, + from_date=add_days(nowdate(), -85), + to_date=add_days(nowdate(), 100), + carry_forward=1) + leave_allocation.submit() + def make_allocation_record(employee=None, leave_type=None): frappe.db.sql("delete from `tabLeave Allocation`") diff --git a/erpnext/hr/doctype/leave_encashment/test_leave_encashment.py b/erpnext/hr/doctype/leave_encashment/test_leave_encashment.py index cfed780eb7..7d4fb285d8 100644 --- a/erpnext/hr/doctype/leave_encashment/test_leave_encashment.py +++ b/erpnext/hr/doctype/leave_encashment/test_leave_encashment.py @@ -17,6 +17,7 @@ class TestLeaveEncashment(unittest.TestCase): def setUp(self): frappe.db.sql('''delete from `tabLeave Period`''') frappe.db.sql('''delete from `tabLeave Allocation`''') + frappe.db.sql('''delete from `tabAdditional Salary`''') # create the leave policy leave_policy = create_leave_policy( @@ -26,7 +27,9 @@ class TestLeaveEncashment(unittest.TestCase): # create employee, salary structure and assignment self.employee = make_employee("test_employee_encashment@example.com") - frappe.db.set_value("Employee", "test_employee_encashment@example.com", "leave_policy", leave_policy.name) + + frappe.db.set_value("Employee", self.employee, "leave_policy", leave_policy.name) + salary_structure = make_salary_structure("Salary Structure for Encashment", "Monthly", self.employee, other_details={"leave_encashment_amount_per_day": 50}) @@ -55,11 +58,11 @@ class TestLeaveEncashment(unittest.TestCase): def test_creation_of_leave_ledger_entry_on_submit(self): frappe.db.sql('''delete from `tabLeave Encashment`''') leave_encashment = frappe.get_doc(dict( - doctype = 'Leave Encashment', - employee = self.employee, - leave_type = "_Test Leave Type Encashment", - leave_period = self.leave_period.name, - payroll_date = today() + doctype='Leave Encashment', + employee=self.employee, + leave_type="_Test Leave Type Encashment", + leave_period=self.leave_period.name, + payroll_date=today() )).insert() leave_encashment.submit() From 61bb236cfaf234fadd69cce768c8e6915de0156e Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Mon, 27 May 2019 19:36:40 +0530 Subject: [PATCH 47/98] test: delete ledger entry after each test to maintain balance --- .../doctype/leave_encashment/test_leave_encashment.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/erpnext/hr/doctype/leave_encashment/test_leave_encashment.py b/erpnext/hr/doctype/leave_encashment/test_leave_encashment.py index 7d4fb285d8..2daeffcffe 100644 --- a/erpnext/hr/doctype/leave_encashment/test_leave_encashment.py +++ b/erpnext/hr/doctype/leave_encashment/test_leave_encashment.py @@ -17,6 +17,7 @@ class TestLeaveEncashment(unittest.TestCase): def setUp(self): frappe.db.sql('''delete from `tabLeave Period`''') frappe.db.sql('''delete from `tabLeave Allocation`''') + frappe.db.sql('''delete from `tabLeave Ledger Entry`''') frappe.db.sql('''delete from `tabAdditional Salary`''') # create the leave policy @@ -41,11 +42,11 @@ class TestLeaveEncashment(unittest.TestCase): def test_leave_balance_value_and_amount(self): frappe.db.sql('''delete from `tabLeave Encashment`''') leave_encashment = frappe.get_doc(dict( - doctype = 'Leave Encashment', - employee = self.employee, - leave_type = "_Test Leave Type Encashment", - leave_period = self.leave_period.name, - payroll_date = today() + doctype='Leave Encashment', + employee=self.employee, + leave_type="_Test Leave Type Encashment", + leave_period=self.leave_period.name, + payroll_date=today() )).insert() self.assertEqual(leave_encashment.leave_balance, 10) From c6d6adcbf3889a642ff1ed7deb955e5fd0bda04f Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Tue, 28 May 2019 09:44:38 +0530 Subject: [PATCH 48/98] fix: ledger entry creation for encashment --- erpnext/hr/doctype/leave_encashment/leave_encashment.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/hr/doctype/leave_encashment/leave_encashment.py b/erpnext/hr/doctype/leave_encashment/leave_encashment.py index c59ce63c7c..70cd5780c8 100644 --- a/erpnext/hr/doctype/leave_encashment/leave_encashment.py +++ b/erpnext/hr/doctype/leave_encashment/leave_encashment.py @@ -84,7 +84,7 @@ class LeaveEncashment(Document): args = frappe._dict( leaves=self.encashable_days * -1, from_date=self.encashment_date, - to_date=self.encashable_days, + to_date=self.encashment_date, is_carry_forward=0 ) create_leave_ledger_entry(self, args, submit) \ No newline at end of file From 9d6151d20047bfa54b6ffa96c14acedc25b74146 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Tue, 28 May 2019 11:45:51 +0530 Subject: [PATCH 49/98] patch: add reload doc for leave ledger entry --- erpnext/patches/v12_0/generate_leave_ledger_entries.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/patches/v12_0/generate_leave_ledger_entries.py b/erpnext/patches/v12_0/generate_leave_ledger_entries.py index 9c638abaff..b8e82ef416 100644 --- a/erpnext/patches/v12_0/generate_leave_ledger_entries.py +++ b/erpnext/patches/v12_0/generate_leave_ledger_entries.py @@ -7,6 +7,7 @@ import frappe def execute(): """ Generates leave ledger entries for leave allocation/application/encashment for last allocation """ + frappe.reload_doc("HR","doctype", "Leave Ledger Entry") if frappe.db.a_row_exists("Leave Ledger Entry"): return From ae4228aed4d5097a35ec65d585238c8320784121 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Tue, 28 May 2019 13:24:15 +0530 Subject: [PATCH 50/98] fix: fetch queries --- .../v12_0/generate_leave_ledger_entries.py | 44 ++++++++++++------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/erpnext/patches/v12_0/generate_leave_ledger_entries.py b/erpnext/patches/v12_0/generate_leave_ledger_entries.py index b8e82ef416..8b1ca8c7ad 100644 --- a/erpnext/patches/v12_0/generate_leave_ledger_entries.py +++ b/erpnext/patches/v12_0/generate_leave_ledger_entries.py @@ -49,17 +49,26 @@ def generate_encashment_leave_ledger_entries(allocation_list): def get_allocation_records(): return frappe.db.sql(""" + WITH allocation_values AS ( + SELECT + DISTINCT name, + employee, + leave_type, + new_leaves_allocated, + carry_forwarded_leaves, + from_date, + to_date, + carry_forward, + RANK() OVER( + PARTITION BY employee, leave_type + ORDER BY to_date DESC + ) as allocation + FROM `tabLeave Allocation` + ) SELECT - DISTINCT name, - employee, - leave_type, - new_leaves_allocated, - carry_forwarded_leaves, - from_date, - to_date, - carry_forward, - RANK() OVER(PARTITION BY employee, leave_type ORDER BY to_date DESC) as allocation - FROM `tabLeave Allocation` + * + FROM + `allocation_values` WHERE allocation=1 """, as_dict=1) @@ -67,7 +76,7 @@ def get_allocation_records(): def get_leaves_application_records(allocation_list): leave_applications = [] for allocation in allocation_list: - leave_applications.append(frappe.db.sql(""" + leave_applications += frappe.db.sql(""" SELECT DISTINCT name, employee, @@ -78,15 +87,15 @@ def get_leaves_application_records(allocation_list): FROM `tabLeave Application` WHERE from_date >= %s - leave_type = %s - employee = %s - """, (allocation.from_date, allocation.leave_type, allocation.employee))) + AND leave_type = %s + AND employee = %s + """, (allocation.from_date, allocation.leave_type, allocation.employee)) return leave_applications def get_leave_encashment_records(allocation_list): leave_encashments = [] for allocation in allocation_list: - leave_encashments.append(frappe.db.sql(""" + leave_encashments += frappe.db.sql(""" SELECT DISTINCT name, employee, @@ -97,6 +106,7 @@ def get_leave_encashment_records(allocation_list): FROM `tabLeave Encashment` WHERE leave_type = %s - employee = %s - """, (allocation.leave_type, allocation.employee))) + AND employee = %s + AND encashment_date >= %s + """, (allocation.leave_type, allocation.employee, allocation.from_date)) return leave_encashments \ No newline at end of file From 8f47bffa0ef85040621b57a2a6defbfb6031e403 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Wed, 29 May 2019 15:54:14 +0530 Subject: [PATCH 51/98] fix: create an instance --- .../v12_0/generate_leave_ledger_entries.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/erpnext/patches/v12_0/generate_leave_ledger_entries.py b/erpnext/patches/v12_0/generate_leave_ledger_entries.py index 8b1ca8c7ad..3cdfa53f88 100644 --- a/erpnext/patches/v12_0/generate_leave_ledger_entries.py +++ b/erpnext/patches/v12_0/generate_leave_ledger_entries.py @@ -21,9 +21,9 @@ def generate_allocation_ledger_entries(allocation_list): from erpnext.hr.doctype.leave_allocation.leave_allocation import LeaveAllocation for allocation in allocation_list: - if not frappe.db.exists("Leave Ledger Entry", {'transaction_type': 'Leave Allocation', - 'transaction_name': allocation.name}): - LeaveAllocation.create_leave_ledger_entry(allocation) + if not frappe.db.exists("Leave Ledger Entry", {'transaction_type': 'Leave Allocation', 'transaction_name': allocation.name}): + leave_allocation = LeaveAllocation(allocation) + leave_allocation.create_leave_ledger_entry() def generate_application_leave_ledger_entries(allocation_list): ''' fix ledger entries for missing leave application transaction ''' @@ -32,9 +32,9 @@ def generate_application_leave_ledger_entries(allocation_list): leave_applications = get_leaves_application_records(allocation_list) for record in leave_applications: - if not frappe.db.exists("Leave Ledger Entry", {'transaction_type': 'Leave Application', - 'transaction_name': record.name}): - LeaveApplication.create_leave_ledger_entry(record) + if not frappe.db.exists("Leave Ledger Entry", {'transaction_type': 'Leave Application', 'transaction_name': record.name}): + leave_application = LeaveApplication(record) + leave_application.create_leave_ledger_entry() def generate_encashment_leave_ledger_entries(allocation_list): ''' fix ledger entries for missing leave encashment transaction ''' @@ -43,9 +43,9 @@ def generate_encashment_leave_ledger_entries(allocation_list): leave_encashments = get_leave_encashment_records(allocation_list) for record in leave_encashments: - if not frappe.db.exists("Leave Ledger Entry", {'transaction_type': 'Leave Encashment', - 'transaction_name': record.name}): - LeaveEncashment.create_leave_ledger_entry(record) + if not frappe.db.exists("Leave Ledger Entry", {'transaction_type': 'Leave Encashment', 'transaction_name': record.name}): + leave_encashment = LeaveEncashment(record) + leave_encashment.create_leave_ledger_entry() def get_allocation_records(): return frappe.db.sql(""" From 7cc6a67c186bd492c3af10e715acd3346f048e0b Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Wed, 29 May 2019 15:59:20 +0530 Subject: [PATCH 52/98] add department filter to employee leave balance --- .../leave_application/leave_application.py | 4 +++- .../leave_ledger_entry.json | 6 +++--- .../employee_leave_balance.js | 7 +++++++ .../employee_leave_balance.py | 21 +++++++++++++------ 4 files changed, 28 insertions(+), 10 deletions(-) diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py index f3bb9f7eb9..9eb0b91c7c 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.py +++ b/erpnext/hr/doctype/leave_application/leave_application.py @@ -411,7 +411,8 @@ def get_leave_details(employee, date): leave_allocation = {} for d in allocation_records: allocation = allocation_records.get(d, frappe._dict()) - remaining_leaves = get_leave_balance_on(employee, d, date) + remaining_leaves = get_leave_balance_on(employee, d, date, to_date = allocation.to_date, + consider_all_leaves_in_the_allocation_period=True) 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") @@ -441,6 +442,7 @@ def get_leave_balance_on(employee, leave_type, from_date, to_date=nowdate(), all expiry = get_allocation_expiry(employee, leave_type, to_date, from_date) leaves_taken = get_leaves_taken(employee, leave_type, allocation.from_date, end_date) + return get_remaining_leaves(allocation, leaves_taken, from_date, expiry) def get_remaining_leaves(allocation, leaves_taken, date, expiry): diff --git a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.json b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.json index ba33d75029..3af013d12b 100644 --- a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.json +++ b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.json @@ -65,21 +65,21 @@ { "fieldname": "from_date", "fieldtype": "Date", - "in_list_view": 1, "label": "From Date" }, { "fieldname": "to_date", "fieldtype": "Date", - "in_list_view": 1, "label": "To Date" }, { + "default": "0", "fieldname": "is_carry_forward", "fieldtype": "Check", "label": "Is Carry Forward" }, { + "default": "0", "fieldname": "is_expired", "fieldtype": "Check", "label": "Is Expired" @@ -87,7 +87,7 @@ ], "in_create": 1, "is_submittable": 1, - "modified": "2019-05-27 03:25:47.805142", + "modified": "2019-05-29 15:58:29.656351", "modified_by": "Administrator", "module": "HR", "name": "Leave Ledger Entry", diff --git a/erpnext/hr/report/employee_leave_balance/employee_leave_balance.js b/erpnext/hr/report/employee_leave_balance/employee_leave_balance.js index 59c25608c2..ecb3e3061f 100644 --- a/erpnext/hr/report/employee_leave_balance/employee_leave_balance.js +++ b/erpnext/hr/report/employee_leave_balance/employee_leave_balance.js @@ -24,6 +24,13 @@ frappe.query_reports["Employee Leave Balance"] = { "options": "Company", "reqd": 1, "default": frappe.defaults.get_user_default("Company") + }, + { + "fieldname":"department", + "label": __("Department"), + "fieldtype": "Link", + "options": "Department", + "reqd": 1, } ] } diff --git a/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py b/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py index 95cb30b791..02be799501 100644 --- a/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py +++ b/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py @@ -4,6 +4,7 @@ from __future__ import unicode_literals import frappe from frappe import _ +from frappe.utils import flt from erpnext.hr.doctype.leave_application.leave_application \ import get_leave_allocation_records, get_leave_balance_on, get_approved_leaves_for_period, get_total_allocated_leaves @@ -30,14 +31,23 @@ def get_columns(leave_types): return columns +def get_conditions(filters): + filters = { + "status": "Active", + "company": filters.company, + } + if filters.get("Department"): + filters.update(filters.get("Department")) + + return filters + def get_data(filters, leave_types): user = frappe.session.user - allocation_records_based_on_to_date = get_leave_allocation_records(filters.to_date) - allocation_records_based_on_from_date = get_leave_allocation_records(filters.from_date) + conditions = get_conditions(filters) active_employees = frappe.get_all("Employee", - filters = { "status": "Active", "company": filters.company}, - fields = ["name", "employee_name", "department", "user_id"]) + filters=conditions, + fields=["name", "employee_name", "department", "user_id"]) data = [] for employee in active_employees: @@ -54,8 +64,7 @@ def get_data(filters, leave_types): opening = get_total_allocated_leaves(employee.name, leave_type, filters.to_date) # closing balance - closing = get_leave_balance_on(employee.name, leave_type, filters.to_date, - allocation_records_based_on_to_date.get(employee.name, frappe._dict())) + closing = flt(opening) - flt(leaves_taken) row += [opening, leaves_taken, closing] From ded33a7e2e18c393783889984aa2cfd5c7e3ee02 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Wed, 29 May 2019 19:12:19 +0530 Subject: [PATCH 53/98] fix: ledger entries creation --- .../doctype/leave_allocation/leave_allocation.py | 4 ++-- .../doctype/leave_application/leave_application.py | 14 ++++++++------ .../patches/v12_0/generate_leave_ledger_entries.py | 7 +++++-- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation.py b/erpnext/hr/doctype/leave_allocation/leave_allocation.py index 6fe8fdff24..2a1301d2fd 100755 --- a/erpnext/hr/doctype/leave_allocation/leave_allocation.py +++ b/erpnext/hr/doctype/leave_allocation/leave_allocation.py @@ -136,8 +136,8 @@ class LeaveAllocation(Document): if flt(leaves) > 0: args = dict( leaves=leaves * -1, - from_date=self.to_date, - to_date=self.to_date, + from_date=self.from_date, + to_date=self.from_date, is_carry_forward=0, is_expired=1 ) diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py index 9eb0b91c7c..b1c666ca83 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.py +++ b/erpnext/hr/doctype/leave_application/leave_application.py @@ -431,22 +431,24 @@ def get_leave_details(employee, date): return ret @frappe.whitelist() -def get_leave_balance_on(employee, leave_type, from_date, to_date=nowdate(), allocation_records=None, docname=None, +def get_leave_balance_on(employee, leave_type, date, to_date=nowdate(), allocation_records=None, docname=None, consider_all_leaves_in_the_allocation_period=False): + ''' Returns leave balance till date and fetches expiry date based on to_date + to calculate minimum remaining leave balance ''' if allocation_records == None: - allocation_records = get_leave_allocation_records(from_date, employee).get(employee, frappe._dict()) + allocation_records = get_leave_allocation_records(date, employee).get(employee, frappe._dict()) allocation = allocation_records.get(leave_type, frappe._dict()) - end_date = allocation.to_date if consider_all_leaves_in_the_allocation_period else from_date - expiry = get_allocation_expiry(employee, leave_type, to_date, from_date) + end_date = allocation.to_date if consider_all_leaves_in_the_allocation_period else date + expiry = get_allocation_expiry(employee, leave_type, to_date, date) leaves_taken = get_leaves_taken(employee, leave_type, allocation.from_date, end_date) - return get_remaining_leaves(allocation, leaves_taken, from_date, expiry) + return get_remaining_leaves(allocation, leaves_taken, date, expiry) def get_remaining_leaves(allocation, leaves_taken, date, expiry): - ''' Returns leaves remaining after comparing with remaining days for allocation expiry ''' + ''' Returns minimum leaves remaining after comparing with remaining days for allocation expiry ''' def _get_remaining_leaves(allocated_leaves, end_date): remaining_leaves = flt(allocated_leaves) + flt(leaves_taken) diff --git a/erpnext/patches/v12_0/generate_leave_ledger_entries.py b/erpnext/patches/v12_0/generate_leave_ledger_entries.py index 3cdfa53f88..31cbcc6181 100644 --- a/erpnext/patches/v12_0/generate_leave_ledger_entries.py +++ b/erpnext/patches/v12_0/generate_leave_ledger_entries.py @@ -22,6 +22,7 @@ def generate_allocation_ledger_entries(allocation_list): for allocation in allocation_list: if not frappe.db.exists("Leave Ledger Entry", {'transaction_type': 'Leave Allocation', 'transaction_name': allocation.name}): + allocation.update(dict(doctype="Leave Allocation")) leave_allocation = LeaveAllocation(allocation) leave_allocation.create_leave_ledger_entry() @@ -33,6 +34,7 @@ def generate_application_leave_ledger_entries(allocation_list): for record in leave_applications: if not frappe.db.exists("Leave Ledger Entry", {'transaction_type': 'Leave Application', 'transaction_name': record.name}): + record.update(dict(doctype="Leave Application")) leave_application = LeaveApplication(record) leave_application.create_leave_ledger_entry() @@ -44,6 +46,7 @@ def generate_encashment_leave_ledger_entries(allocation_list): for record in leave_encashments: if not frappe.db.exists("Leave Ledger Entry", {'transaction_type': 'Leave Encashment', 'transaction_name': record.name}): + record.update(dict(doctype="Leave Encashment")) leave_encashment = LeaveEncashment(record) leave_encashment.create_leave_ledger_entry() @@ -89,7 +92,7 @@ def get_leaves_application_records(allocation_list): from_date >= %s AND leave_type = %s AND employee = %s - """, (allocation.from_date, allocation.leave_type, allocation.employee)) + """, (allocation.from_date, allocation.leave_type, allocation.employee), as_dict=1) return leave_applications def get_leave_encashment_records(allocation_list): @@ -108,5 +111,5 @@ def get_leave_encashment_records(allocation_list): leave_type = %s AND employee = %s AND encashment_date >= %s - """, (allocation.leave_type, allocation.employee, allocation.from_date)) + """, (allocation.leave_type, allocation.employee, allocation.from_date), as_dict=1) return leave_encashments \ No newline at end of file From aafb5cb6f684ca28921d2a6de934a5f6fd4f3162 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Thu, 30 May 2019 13:13:14 +0530 Subject: [PATCH 54/98] fix: ignore expired non-carry forwarded allocation on calculating leaves taken --- .../hr/doctype/leave_application/leave_application.py | 10 +++++++--- .../leave_application/test_leave_application.py | 1 - erpnext/patches/v12_0/generate_leave_ledger_entries.py | 3 +-- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py index b1c666ca83..fd13436d8f 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.py +++ b/erpnext/hr/doctype/leave_application/leave_application.py @@ -431,7 +431,7 @@ def get_leave_details(employee, date): return ret @frappe.whitelist() -def get_leave_balance_on(employee, leave_type, date, to_date=nowdate(), allocation_records=None, docname=None, +def get_leave_balance_on(employee, leave_type, date, to_date=nowdate(), allocation_records=None, consider_all_leaves_in_the_allocation_period=False): ''' Returns leave balance till date and fetches expiry date based on to_date to calculate minimum remaining leave balance ''' @@ -467,13 +467,17 @@ def get_remaining_leaves(allocation, leaves_taken, date, expiry): def get_leaves_taken(employee, leave_type, from_date, to_date): ''' Returns leaves taken based on leave application/encashment ''' - return frappe.db.get_value("Leave Ledger Entry", filters={ + leaves = frappe.db.get_all("Leave Ledger Entry", filters={ 'Employee':employee, 'leave_type':leave_type, 'leaves': ("<", 0), 'to_date':("<=", to_date), 'from_date': (">=", from_date)}, - fieldname=['SUM(leaves)']) + or_filters={ + 'is_expired': 0, + 'is_carry_forward': 1 + }, fields=['SUM(leaves) as leaves']) + return leaves[0]['leaves'] if leaves else None def get_total_allocated_leaves(employee, leave_type, date): filters= { diff --git a/erpnext/hr/doctype/leave_application/test_leave_application.py b/erpnext/hr/doctype/leave_application/test_leave_application.py index f5f4fa55f5..30dcafafc7 100644 --- a/erpnext/hr/doctype/leave_application/test_leave_application.py +++ b/erpnext/hr/doctype/leave_application/test_leave_application.py @@ -538,7 +538,6 @@ class TestLeaveApplication(unittest.TestCase): self.assertEquals(leave_ledger_entry[1].leaves, -2) def create_carry_forwarded_allocation(employee, leave_type): - # initial leave allocation leave_allocation = create_leave_allocation( leave_type="_Test_CF_leave_expiry", diff --git a/erpnext/patches/v12_0/generate_leave_ledger_entries.py b/erpnext/patches/v12_0/generate_leave_ledger_entries.py index 31cbcc6181..dff749fce7 100644 --- a/erpnext/patches/v12_0/generate_leave_ledger_entries.py +++ b/erpnext/patches/v12_0/generate_leave_ledger_entries.py @@ -104,8 +104,7 @@ def get_leave_encashment_records(allocation_list): employee, leave_type, encashable_days, - from_date, - to_date + encashment_date FROM `tabLeave Encashment` WHERE leave_type = %s From 2124d9884ba6bbf22e88111a229e053001832cb3 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Thu, 30 May 2019 13:21:34 +0530 Subject: [PATCH 55/98] fix: pass positional arguments on creation of leave application --- erpnext/hr/doctype/leave_application/leave_application.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/hr/doctype/leave_application/leave_application.js b/erpnext/hr/doctype/leave_application/leave_application.js index 0b0a69f7a1..11146abce2 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.js +++ b/erpnext/hr/doctype/leave_application/leave_application.js @@ -143,7 +143,7 @@ frappe.ui.form.on("Leave Application", { method: "erpnext.hr.doctype.leave_application.leave_application.get_leave_balance_on", args: { employee: frm.doc.employee, - from_date: frm.doc.from_date, + date: frm.doc.from_date, to_date: frm.doc.to_date, leave_type: frm.doc.leave_type, consider_all_leaves_in_the_allocation_period: true From 91e62f575e51fe21058ea296adc32ddbb785faa0 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Thu, 30 May 2019 13:38:35 +0530 Subject: [PATCH 56/98] fix: department filters in employee leave balance --- .../employee_leave_balance/employee_leave_balance.js | 1 - .../employee_leave_balance/employee_leave_balance.py | 11 ++++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/erpnext/hr/report/employee_leave_balance/employee_leave_balance.js b/erpnext/hr/report/employee_leave_balance/employee_leave_balance.js index ecb3e3061f..68302f6ee4 100644 --- a/erpnext/hr/report/employee_leave_balance/employee_leave_balance.js +++ b/erpnext/hr/report/employee_leave_balance/employee_leave_balance.js @@ -30,7 +30,6 @@ frappe.query_reports["Employee Leave Balance"] = { "label": __("Department"), "fieldtype": "Link", "options": "Department", - "reqd": 1, } ] } diff --git a/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py b/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py index 02be799501..e65220ae25 100644 --- a/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py +++ b/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py @@ -32,14 +32,15 @@ def get_columns(leave_types): return columns def get_conditions(filters): - filters = { + conditions = { "status": "Active", "company": filters.company, } - if filters.get("Department"): - filters.update(filters.get("Department")) - - return filters + if filters.get("department"): + conditions.update({ + "department": filters.get("department") + }) + return conditions def get_data(filters, leave_types): user = frappe.session.user From 95e5d812fef9ff2a03a40fa443f0007e95797582 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Thu, 30 May 2019 22:22:15 +0530 Subject: [PATCH 57/98] fix: UX changes --- .../leave_application/leave_application.json | 197 +++---- .../leave_ledger_entry.json | 12 +- .../hr/doctype/leave_period/leave_period.json | 538 +++++++++--------- erpnext/hr/doctype/leave_type/leave_type.json | 5 +- .../v12_0/generate_leave_ledger_entries.py | 1 + 5 files changed, 381 insertions(+), 372 deletions(-) diff --git a/erpnext/hr/doctype/leave_application/leave_application.json b/erpnext/hr/doctype/leave_application/leave_application.json index d7b2c9f875..812494c1c0 100644 --- a/erpnext/hr/doctype/leave_application/leave_application.json +++ b/erpnext/hr/doctype/leave_application/leave_application.json @@ -485,7 +485,7 @@ "read_only": 0, "remember_last_selected_value": 0, "report_hide": 0, - "reqd": 0, + "reqd": 1, "search_index": 0, "set_only_once": 0, "translatable": 0, @@ -507,6 +507,7 @@ "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, + "label": "Approval", "length": 0, "no_copy": 0, "permlevel": 0, @@ -652,39 +653,6 @@ "translatable": 0, "unique": 0 }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "salary_slip", - "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": "Salary Slip", - "length": 0, - "no_copy": 0, - "options": "Salary Slip", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "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, @@ -748,6 +716,38 @@ "translatable": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "company", + "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": "Company", + "length": 0, + "no_copy": 0, + "options": "Company", + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 1, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_in_quick_entry": 0, @@ -780,38 +780,6 @@ "translatable": 0, "unique": 0 }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "color", - "fieldtype": "Color", - "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": "Color", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "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, @@ -843,37 +811,38 @@ "unique": 0 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "company", - "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": "Company", - "length": 0, - "no_copy": 0, - "options": "Company", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 1, - "report_hide": 0, - "reqd": 1, - "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": "salary_slip", + "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": "Salary Slip", + "length": 0, + "no_copy": 0, + "options": "Salary Slip", + "permlevel": 0, + "precision": "", + "print_hide": 1, + "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, @@ -906,6 +875,38 @@ "translatable": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 1, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "color", + "fieldtype": "Color", + "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": "Color", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 1, + "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, @@ -950,7 +951,7 @@ "issingle": 0, "istable": 0, "max_attachments": 3, - "modified": "2019-01-30 11:28:14.745572", + "modified": "2019-05-30 11:28:14.745572", "modified_by": "Administrator", "module": "HR", "name": "Leave Application", diff --git a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.json b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.json index 3af013d12b..20b64f81aa 100644 --- a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.json +++ b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.json @@ -9,6 +9,7 @@ "transaction_type", "transaction_name", "leaves", + "column_break_7", "from_date", "to_date", "is_carry_forward", @@ -20,6 +21,7 @@ "fieldname": "employee", "fieldtype": "Link", "in_list_view": 1, + "in_standard_filter": 1, "label": "Employee", "options": "Employee" }, @@ -32,6 +34,7 @@ "fieldname": "leave_type", "fieldtype": "Link", "in_list_view": 1, + "in_standard_filter": 1, "label": "Leave Type", "options": "Leave Type" }, @@ -83,11 +86,15 @@ "fieldname": "is_expired", "fieldtype": "Check", "label": "Is Expired" + }, + { + "fieldname": "column_break_7", + "fieldtype": "Column Break" } ], "in_create": 1, "is_submittable": 1, - "modified": "2019-05-29 15:58:29.656351", + "modified": "2019-05-30 14:45:16.577534", "modified_by": "Administrator", "module": "HR", "name": "Leave Ledger Entry", @@ -149,5 +156,6 @@ } ], "sort_field": "modified", - "sort_order": "ASC" + "sort_order": "ASC", + "title_field": "employee" } \ No newline at end of file diff --git a/erpnext/hr/doctype/leave_period/leave_period.json b/erpnext/hr/doctype/leave_period/leave_period.json index df28763c79..9e895c34fb 100644 --- a/erpnext/hr/doctype/leave_period/leave_period.json +++ b/erpnext/hr/doctype/leave_period/leave_period.json @@ -1,294 +1,294 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 1, - "allow_rename": 1, - "autoname": "HR-LPR-.YYYY.-.#####", - "beta": 0, - "creation": "2018-04-13 15:20:52.864288", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "allow_copy": 0, + "allow_guest_to_view": 0, + "allow_import": 1, + "allow_rename": 1, + "autoname": "HR-LPR-.YYYY.-.#####", + "beta": 0, + "creation": "2018-04-13 15:20:52.864288", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "from_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "From Date", - "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": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "from_date", + "fieldtype": "Date", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "From Date", + "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": 1, + "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": "to_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "To Date", - "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": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "to_date", + "fieldtype": "Date", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "To Date", + "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": 1, + "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": "column_break_3", - "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, - "translatable": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "is_active", + "fieldtype": "Check", + "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": "Is Active", + "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, + "translatable": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "company", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Company", - "length": 0, - "no_copy": 0, - "options": "Company", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "column_break_3", + "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, + "translatable": 0, "unique": 0 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "is_active", - "fieldtype": "Check", - "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": "Is Active", - "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, - "translatable": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "company", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Company", + "length": 0, + "no_copy": 0, + "options": "Company", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "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": "optional_holiday_list", - "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": "Holiday List for Optional Leave", - "length": 0, - "no_copy": 0, - "options": "Holiday List", - "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, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "optional_holiday_list", + "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": "Holiday List for Optional Leave", + "length": 0, + "no_copy": 0, + "options": "Holiday List", + "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 } - ], - "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-08-21 16:15:43.305502", - "modified_by": "Administrator", - "module": "HR", - "name": "Leave Period", - "name_case": "", - "owner": "Administrator", + ], + "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": "2019-05-30 16:15:43.305502", + "modified_by": "Administrator", + "module": "HR", + "name": "Leave Period", + "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, + "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, + "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, + "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, + ], + "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 } \ No newline at end of file diff --git a/erpnext/hr/doctype/leave_type/leave_type.json b/erpnext/hr/doctype/leave_type/leave_type.json index 0b8e38ea73..7899c2f1e6 100644 --- a/erpnext/hr/doctype/leave_type/leave_type.json +++ b/erpnext/hr/doctype/leave_type/leave_type.json @@ -418,7 +418,6 @@ "bold": 0, "collapsible": 0, "columns": 0, - "default": "1", "depends_on": "", "description": "calculated in days", "fetch_if_empty": 0, @@ -431,7 +430,7 @@ "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, - "label": "Carry Forward Leave Expiry", + "label": "Expire Carried Forward Leaves", "length": 0, "no_copy": 0, "permlevel": 0, @@ -729,7 +728,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2019-04-11 15:38:39.334283", + "modified": "2019-05-30 15:38:39.334283", "modified_by": "Administrator", "module": "HR", "name": "Leave Type", diff --git a/erpnext/patches/v12_0/generate_leave_ledger_entries.py b/erpnext/patches/v12_0/generate_leave_ledger_entries.py index dff749fce7..065a0137a5 100644 --- a/erpnext/patches/v12_0/generate_leave_ledger_entries.py +++ b/erpnext/patches/v12_0/generate_leave_ledger_entries.py @@ -8,6 +8,7 @@ def execute(): """ Generates leave ledger entries for leave allocation/application/encashment for last allocation """ frappe.reload_doc("HR","doctype", "Leave Ledger Entry") + frappe.reload_doc("HR","doctype", "Leave Encashment") if frappe.db.a_row_exists("Leave Ledger Entry"): return From c5385e141b0b9ecf192375dfc52207c880f8200c Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Fri, 31 May 2019 00:53:28 +0530 Subject: [PATCH 58/98] fix: add mandatory reason fields in leave application --- .../test_leave_application.py | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/erpnext/hr/doctype/leave_application/test_leave_application.py b/erpnext/hr/doctype/leave_application/test_leave_application.py index 30dcafafc7..899ce2ae8d 100644 --- a/erpnext/hr/doctype/leave_application/test_leave_application.py +++ b/erpnext/hr/doctype/leave_application/test_leave_application.py @@ -19,6 +19,7 @@ _test_records = [ "doctype": "Leave Application", "employee": "_T-Employee-00001", "from_date": "2013-05-01", + "description": "_Test Reason", "leave_type": "_Test Leave Type", "posting_date": "2013-01-02", "to_date": "2013-05-05" @@ -28,6 +29,7 @@ _test_records = [ "doctype": "Leave Application", "employee": "_T-Employee-00002", "from_date": "2013-05-01", + "description": "_Test Reason", "leave_type": "_Test Leave Type", "posting_date": "2013-01-02", "to_date": "2013-05-05" @@ -37,6 +39,7 @@ _test_records = [ "doctype": "Leave Application", "employee": "_T-Employee-00001", "from_date": "2013-01-15", + "description": "_Test Reason", "leave_type": "_Test Leave Type LWP", "posting_date": "2013-01-02", "to_date": "2013-01-15" @@ -46,8 +49,8 @@ _test_records = [ class TestLeaveApplication(unittest.TestCase): def setUp(self): - for dt in ["Leave Application", "Leave Allocation", "Salary Slip"]: - frappe.db.sql("delete from `tab%s`" % dt) + for dt in ["Leave Application", "Leave Allocation", "Salary Slip", "Leave Ledger Entry"]: + frappe.db.sql("DELETE FROM `tab%s`" % dt) @classmethod def setUpClass(cls): @@ -270,6 +273,7 @@ class TestLeaveApplication(unittest.TestCase): doctype = 'Leave Application', employee = employee.name, company = '_Test Company', + description = "_Test Reason", leave_type = leave_type, from_date = date, to_date = date, @@ -288,8 +292,6 @@ class TestLeaveApplication(unittest.TestCase): self.assertEqual(get_leave_balance_on(employee.name, leave_type, today), 9) def test_leaves_allowed(self): - frappe.db.sql("delete from `tabLeave Allocation`") - frappe.db.sql("delete from `tabLeave Ledger Entry`") employee = get_employee() leave_period = get_leave_period() frappe.delete_doc_if_exists("Leave Type", "Test Leave Type", force=1) @@ -307,6 +309,7 @@ class TestLeaveApplication(unittest.TestCase): doctype = 'Leave Application', employee = employee.name, leave_type = leave_type.name, + description = "_Test Reason", from_date = date, to_date = add_days(date, 2), company = "_Test Company", @@ -319,6 +322,7 @@ class TestLeaveApplication(unittest.TestCase): doctype = 'Leave Application', employee = employee.name, leave_type = leave_type.name, + description = "_Test Reason", from_date = add_days(date, 4), to_date = add_days(date, 8), company = "_Test Company", @@ -344,6 +348,7 @@ class TestLeaveApplication(unittest.TestCase): doctype = 'Leave Application', employee = employee.name, leave_type = leave_type.name, + description = "_Test Reason", from_date = date, to_date = add_days(date, 4), company = "_Test Company", @@ -365,6 +370,7 @@ class TestLeaveApplication(unittest.TestCase): doctype = 'Leave Application', employee = employee.name, leave_type = leave_type_1.name, + description = "_Test Reason", from_date = date, to_date = add_days(date, 4), company = "_Test Company", @@ -394,6 +400,7 @@ class TestLeaveApplication(unittest.TestCase): doctype = 'Leave Application', employee = employee.name, leave_type = leave_type.name, + description = "_Test Reason", from_date = date, to_date = add_days(date, 4), company = "_Test Company", @@ -404,8 +411,6 @@ class TestLeaveApplication(unittest.TestCase): self.assertRaises(frappe.ValidationError, leave_application.insert) def test_leave_balance_near_allocaton_expiry(self): - frappe.db.sql("delete from `tabLeave Allocation`") - frappe.db.sql("delete from `tabLeave Ledger Entry`") employee = get_employee() leave_type = create_leave_type( leave_type_name="_Test_CF_leave_expiry", @@ -460,9 +465,10 @@ class TestLeaveApplication(unittest.TestCase): allocation.insert(ignore_permissions=True) allocation.submit() leave_application = frappe.get_doc(dict( - doctype = 'Leave Application', + doctype = 'Leave Application', employee = employee.name, leave_type = leave_type, + description = "_Test Reason", from_date = '2018-10-02', to_date = '2018-10-02', company = '_Test Company', @@ -474,7 +480,6 @@ class TestLeaveApplication(unittest.TestCase): self.assertEqual(leave_application.docstatus, 1) def test_creation_of_leave_ledger_entry_on_submit(self): - frappe.db.sql("delete from `tabLeave Allocation`") employee = get_employee() leave_type = create_leave_type(leave_type_name = 'Test Leave Type 1') @@ -490,6 +495,7 @@ class TestLeaveApplication(unittest.TestCase): leave_type = leave_type.name, from_date = add_days(nowdate(), 1), to_date = add_days(nowdate(), 4), + description = "_Test Reason", company = "_Test Company", docstatus = 1, status = "Approved" @@ -506,8 +512,6 @@ class TestLeaveApplication(unittest.TestCase): self.assertFalse(frappe.db.exists("Leave Ledger Entry", {'transaction_name':leave_application.name})) def test_ledger_entry_creation_on_intermediate_allocation_expiry(self): - frappe.db.sql("delete from `tabLeave Allocation`") - frappe.db.sql("delete from `tabLeave Ledger Entry`") employee = get_employee() leave_type = create_leave_type( leave_type_name="_Test_CF_leave_expiry", @@ -523,6 +527,7 @@ class TestLeaveApplication(unittest.TestCase): leave_type = leave_type.name, from_date = add_days(nowdate(), -3), to_date = add_days(nowdate(), 7), + description = "_Test Reason", company = "_Test Company", docstatus = 1, status = "Approved" @@ -558,8 +563,6 @@ def create_carry_forwarded_allocation(employee, leave_type): leave_allocation.submit() def make_allocation_record(employee=None, leave_type=None): - frappe.db.sql("delete from `tabLeave Allocation`") - allocation = frappe.get_doc({ "doctype": "Leave Allocation", "employee": employee or "_T-Employee-00001", From afa1dc4ffa1e7decadf5937452143e426cf34fec Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Mon, 3 Jun 2019 20:09:22 +0530 Subject: [PATCH 59/98] feat: expire current allocation --- .../leave_allocation/leave_allocation.js | 23 ++++++++++++ .../leave_allocation/leave_allocation.json | 36 ++++++++++++++++++- .../leave_allocation/leave_allocation.py | 21 +++++++++-- 3 files changed, 76 insertions(+), 4 deletions(-) diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation.js b/erpnext/hr/doctype/leave_allocation/leave_allocation.js index 1fc1d89635..489db67109 100755 --- a/erpnext/hr/doctype/leave_allocation/leave_allocation.js +++ b/erpnext/hr/doctype/leave_allocation/leave_allocation.js @@ -21,6 +21,29 @@ frappe.ui.form.on("Leave Allocation", { }) }, + refresh: function(frm) { + if(frm.doc.docstatus == 1){ + frm.add_custom_button('Expire Allocation', function() { + frm.trigger("expire_allocation"); + }); + } + }, + + expire_allocation: function(frm) { + frappe.call({ + method: 'erpnext.hr.doctype.leave_application.leave_application.expire_previous_allocation', + args: { + ref_doc: frm.doc + }, + freeze: true, + callback: function(r){ + if(!r.exc){ + frappe.msgprint(__("Allocation Expired!")); + } + } + }); + }, + employee: function(frm) { frm.trigger("calculate_total_leaves_allocated"); }, diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation.json b/erpnext/hr/doctype/leave_allocation/leave_allocation.json index 568182d3b7..1d6307cbe1 100644 --- a/erpnext/hr/doctype/leave_allocation/leave_allocation.json +++ b/erpnext/hr/doctype/leave_allocation/leave_allocation.json @@ -565,6 +565,40 @@ "translatable": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_from": "employee.leave_policy", + "fieldname": "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": 1, + "label": "Leave Policy", + "length": 0, + "no_copy": 0, + "options": "Leave Policy", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "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, @@ -677,7 +711,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2019-05-22 11:28:09.360525", + "modified": "2019-05-30 11:28:09.360525", "modified_by": "Administrator", "module": "HR", "name": "Leave Allocation", diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation.py b/erpnext/hr/doctype/leave_allocation/leave_allocation.py index 2a1301d2fd..0b2972e565 100755 --- a/erpnext/hr/doctype/leave_allocation/leave_allocation.py +++ b/erpnext/hr/doctype/leave_allocation/leave_allocation.py @@ -131,7 +131,7 @@ class LeaveAllocation(Document): def expire_previous_allocation(self): ''' expire previous allocation leaves ''' - leaves = get_remaining_leaves(self.employee, self.leave_type, self.from_date) + leaves = get_unused_leaves(self.employee, self.leave_type, self.from_date) if flt(leaves) > 0: args = dict( @@ -171,11 +171,26 @@ def get_carry_forwarded_leaves(employee, leave_type, date, carry_forward=None): carry_forwarded_leaves = 0 if carry_forward: validate_carry_forward(leave_type) - carry_forwarded_leaves = get_remaining_leaves(employee, leave_type, date) + carry_forwarded_leaves = get_unused_leaves(employee, leave_type, date) return carry_forwarded_leaves -def get_remaining_leaves(employee, leave_type, date): +@frappe.whitelist() +def expire_current_allocation(ref_doc): + ''' expire previous allocation leaves ''' + leaves = get_unused_leaves(ref_doc.employee, ref_doc.leave_type, ref_doc.to_date) + + if flt(leaves) > 0: + args = dict( + leaves=leaves * -1, + from_date=ref_doc.from_date, + to_date=ref_doc.from_date, + is_carry_forward=0, + is_expired=1 + ) + create_leave_ledger_entry(ref_doc, args) + +def get_unused_leaves(employee, leave_type, date): return frappe.db.get_value("Leave Ledger Entry", filters={ "to_date": ("<=", date), "employee": employee, From 62011c9dc40f46cde0b3395452168e5077ba44c9 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Wed, 5 Jun 2019 21:16:27 +0530 Subject: [PATCH 60/98] feat: add link to policy --- .../leave_allocation/leave_allocation.js | 13 +++++-- .../leave_allocation/leave_allocation.json | 35 ++++++++++++++++++- .../leave_allocation/leave_allocation.py | 6 ++-- .../leave_policy/leave_policy_dashboard.py | 5 ++- 4 files changed, 53 insertions(+), 6 deletions(-) diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation.js b/erpnext/hr/doctype/leave_allocation/leave_allocation.js index 489db67109..50dbaadec7 100755 --- a/erpnext/hr/doctype/leave_allocation/leave_allocation.js +++ b/erpnext/hr/doctype/leave_allocation/leave_allocation.js @@ -22,10 +22,19 @@ frappe.ui.form.on("Leave Allocation", { }, refresh: function(frm) { - if(frm.doc.docstatus == 1){ - frm.add_custom_button('Expire Allocation', function() { + if(frm.doc.docstatus === 1 && frm.doc.status === "Active") { + // expire current allocation + frm.add_custom_button(__('Expire Allocation'), function() { frm.trigger("expire_allocation"); }); + + // opens leave balance report for employee + frm.add_custom_button(__('Check Leave Balance'), function() { + frappe.route_options = { + employee: frm.doc.employee, + }; + frappe.set_route("query-report", "Employee Leave Balance"); + }); } }, diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation.json b/erpnext/hr/doctype/leave_allocation/leave_allocation.json index 1d6307cbe1..125fb3150b 100644 --- a/erpnext/hr/doctype/leave_allocation/leave_allocation.json +++ b/erpnext/hr/doctype/leave_allocation/leave_allocation.json @@ -599,6 +599,39 @@ "translatable": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "status", + "fieldtype": "Select", + "hidden": 1, + "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": 0, + "options": "Active\nExpired", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "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, @@ -711,7 +744,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2019-05-30 11:28:09.360525", + "modified": "2019-05-31 11:28:09.360525", "modified_by": "Administrator", "module": "HR", "name": "Leave Allocation", diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation.py b/erpnext/hr/doctype/leave_allocation/leave_allocation.py index 0b2972e565..b402e908b0 100755 --- a/erpnext/hr/doctype/leave_allocation/leave_allocation.py +++ b/erpnext/hr/doctype/leave_allocation/leave_allocation.py @@ -183,13 +183,15 @@ def expire_current_allocation(ref_doc): if flt(leaves) > 0: args = dict( leaves=leaves * -1, - from_date=ref_doc.from_date, - to_date=ref_doc.from_date, + from_date=ref_doc.to_date, + to_date=ref_doc.to_date, is_carry_forward=0, is_expired=1 ) create_leave_ledger_entry(ref_doc, args) + frappe.db.set_value("Leave Allocation", ref_doc.name, "status", "Expired") + def get_unused_leaves(employee, leave_type, date): return frappe.db.get_value("Leave Ledger Entry", filters={ "to_date": ("<=", date), diff --git a/erpnext/hr/doctype/leave_policy/leave_policy_dashboard.py b/erpnext/hr/doctype/leave_policy/leave_policy_dashboard.py index f97d2855a4..48a204596c 100644 --- a/erpnext/hr/doctype/leave_policy/leave_policy_dashboard.py +++ b/erpnext/hr/doctype/leave_policy/leave_policy_dashboard.py @@ -12,6 +12,9 @@ def get_data(): }, { 'items': ['Employee Grade'] - } + }, + { + 'items': ['Leave Allocation'] + }, ] } \ No newline at end of file From 351f4d53a05d2513fc7b389590b2f9d6d4906058 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Wed, 5 Jun 2019 21:22:07 +0530 Subject: [PATCH 61/98] feat: add employee filter to employee leave balance --- .../hr/doctype/leave_allocation/leave_allocation.js | 2 +- .../hr/doctype/leave_application/leave_application.js | 10 ++++++++++ .../leave_application/test_leave_application.py | 2 +- .../doctype/leave_ledger_entry/leave_ledger_entry.json | 9 ++++++++- .../employee_leave_balance/employee_leave_balance.js | 6 ++++++ .../employee_leave_balance/employee_leave_balance.py | 7 ++++--- 6 files changed, 30 insertions(+), 6 deletions(-) diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation.js b/erpnext/hr/doctype/leave_allocation/leave_allocation.js index 50dbaadec7..005f45a657 100755 --- a/erpnext/hr/doctype/leave_allocation/leave_allocation.js +++ b/erpnext/hr/doctype/leave_allocation/leave_allocation.js @@ -29,7 +29,7 @@ frappe.ui.form.on("Leave Allocation", { }); // opens leave balance report for employee - frm.add_custom_button(__('Check Leave Balance'), function() { + frm.add_custom_button(__('Leave Balance'), function() { frappe.route_options = { employee: frm.doc.employee, }; diff --git a/erpnext/hr/doctype/leave_application/leave_application.js b/erpnext/hr/doctype/leave_application/leave_application.js index 11146abce2..cb1042c5ef 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.js +++ b/erpnext/hr/doctype/leave_application/leave_application.js @@ -86,6 +86,16 @@ frappe.ui.form.on("Leave Application", { frm.set_value('employee', perm['Employee'].map(perm_doc => perm_doc.doc)[0]); } } + + if (frm.doc.docstatus === 1) { + frm.add_custom_button(__('Leave Balance'), function() { + frappe.route_options = { + employee: frm.doc.employee, + group_by: "" + }; + frappe.set_route("query-report", "Employee Leave Balance"); + }); + } }, employee: function(frm) { diff --git a/erpnext/hr/doctype/leave_application/test_leave_application.py b/erpnext/hr/doctype/leave_application/test_leave_application.py index 899ce2ae8d..54216ee3c7 100644 --- a/erpnext/hr/doctype/leave_application/test_leave_application.py +++ b/erpnext/hr/doctype/leave_application/test_leave_application.py @@ -50,7 +50,7 @@ _test_records = [ class TestLeaveApplication(unittest.TestCase): def setUp(self): for dt in ["Leave Application", "Leave Allocation", "Salary Slip", "Leave Ledger Entry"]: - frappe.db.sql("DELETE FROM `tab%s`" % dt) + frappe.db.sql("DELETE FROM `tab%s`" % dt) #nosec @classmethod def setUpClass(cls): diff --git a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.json b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.json index 20b64f81aa..4234fc4b40 100644 --- a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.json +++ b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.json @@ -14,6 +14,7 @@ "to_date", "is_carry_forward", "is_expired", + "is_lwp", "amended_from" ], "fields": [ @@ -90,11 +91,17 @@ { "fieldname": "column_break_7", "fieldtype": "Column Break" + }, + { + "default": "0", + "fieldname": "is_lwp", + "fieldtype": "Check", + "label": "Is Leave Without Pay" } ], "in_create": 1, "is_submittable": 1, - "modified": "2019-05-30 14:45:16.577534", + "modified": "2019-06-05 12:56:04.980160", "modified_by": "Administrator", "module": "HR", "name": "Leave Ledger Entry", diff --git a/erpnext/hr/report/employee_leave_balance/employee_leave_balance.js b/erpnext/hr/report/employee_leave_balance/employee_leave_balance.js index 68302f6ee4..05728a297b 100644 --- a/erpnext/hr/report/employee_leave_balance/employee_leave_balance.js +++ b/erpnext/hr/report/employee_leave_balance/employee_leave_balance.js @@ -30,6 +30,12 @@ frappe.query_reports["Employee Leave Balance"] = { "label": __("Department"), "fieldtype": "Link", "options": "Department", + }, + { + "fieldname":"employee", + "label": __("Employee"), + "fieldtype": "Link", + "options": "Employee", } ] } diff --git a/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py b/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py index e65220ae25..48e9bf5e5d 100644 --- a/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py +++ b/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py @@ -37,9 +37,10 @@ def get_conditions(filters): "company": filters.company, } if filters.get("department"): - conditions.update({ - "department": filters.get("department") - }) + conditions.update({"department": filters.get("department")}) + if filters.get("employee"): + conditions.update({"employee": filters.get("employee")}) + return conditions def get_data(filters, leave_types): From 050f65beb44bfed1cabef5dbd51fe543002027df Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Thu, 6 Jun 2019 20:32:01 +0530 Subject: [PATCH 62/98] feat: fetch leave allocation from ledger entry --- .../employee_leave_balance.py | 24 +++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py b/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py index 48e9bf5e5d..3c3e6af25c 100644 --- a/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py +++ b/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py @@ -6,7 +6,7 @@ import frappe from frappe import _ from frappe.utils import flt from erpnext.hr.doctype.leave_application.leave_application \ - import get_leave_allocation_records, get_leave_balance_on, get_approved_leaves_for_period, get_total_allocated_leaves + import get_leaves_for_period def execute(filters=None): @@ -59,11 +59,11 @@ def get_data(filters, leave_types): for leave_type in leave_types: # leaves taken - leaves_taken = get_approved_leaves_for_period(employee.name, leave_type, - filters.from_date, filters.to_date) + leaves_taken = get_leaves_for_period(employee.name, leave_type, + filters.from_date, filters.to_date) * -1 # opening balance - opening = get_total_allocated_leaves(employee.name, leave_type, filters.to_date) + opening = get_total_allocated_leaves(employee.name, leave_type, filters.from_date, filters.to_date) # closing balance closing = flt(opening) - flt(leaves_taken) @@ -91,3 +91,19 @@ def get_approvers(department): where parent = %s and parentfield = 'leave_approvers'""", (d), as_dict=True)]) return approvers + +def get_total_allocated_leaves(employee, leave_type, from_date, to_date): + ''' Returns leave allocation between from date and to date ''' + filters= { + 'from_date': ['between', (from_date, to_date)], + 'to_date': ['between', (from_date, to_date)], + 'docstatus': 1, + 'is_expired': 0, + 'leave_type': leave_type, + 'employee': employee, + 'transaction_type': 'Leave Allocation' + } + + leave_allocation_records = frappe.db.get_all('Leave Ledger Entry', filters=filters, fields=['SUM(leaves) as leaves']) + + return flt(leave_allocation_records[0].get('leaves')) if leave_allocation_records else flt(0) \ No newline at end of file From 3863fc5fb24a76b1f5820cb1da4e1257f537f668 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Thu, 6 Jun 2019 20:34:10 +0530 Subject: [PATCH 63/98] feat: create ledger entry for each earned leave --- erpnext/hr/utils.py | 60 +++++++++++++++++++++++++++------------------ 1 file changed, 36 insertions(+), 24 deletions(-) diff --git a/erpnext/hr/utils.py b/erpnext/hr/utils.py index 7357fd0cc1..e8c9dd0c45 100644 --- a/erpnext/hr/utils.py +++ b/erpnext/hr/utils.py @@ -270,31 +270,43 @@ def allocate_earned_leaves(): filters={'is_earned_leave' : 1}) today = getdate() divide_by_frequency = {"Yearly": 1, "Half-Yearly": 6, "Quarterly": 4, "Monthly": 12} - if e_leave_types: - for e_leave_type in e_leave_types: - leave_allocations = frappe.db.sql("""select name, employee, from_date, to_date from `tabLeave Allocation` where '{0}' - between from_date and to_date and docstatus=1 and leave_type='{1}'""" - .format(today, e_leave_type.name), as_dict=1) - for allocation in leave_allocations: - leave_policy = get_employee_leave_policy(allocation.employee) - if not leave_policy: - continue - if not e_leave_type.earned_leave_frequency == "Monthly": - if not check_frequency_hit(allocation.from_date, today, e_leave_type.earned_leave_frequency): - continue - annual_allocation = frappe.db.sql("""select annual_allocation from `tabLeave Policy Detail` - where parent=%s and leave_type=%s""", (leave_policy.name, e_leave_type.name)) - if annual_allocation and annual_allocation[0]: - earned_leaves = flt(annual_allocation[0][0]) / divide_by_frequency[e_leave_type.earned_leave_frequency] - if e_leave_type.rounding == "0.5": - earned_leaves = round(earned_leaves * 2) / 2 - else: - earned_leaves = round(earned_leaves) - allocated_leaves = frappe.db.get_value('Leave Allocation', allocation.name, 'total_leaves_allocated') - new_allocation = flt(allocated_leaves) + flt(earned_leaves) - new_allocation = new_allocation if new_allocation <= e_leave_type.max_leaves_allowed else e_leave_type.max_leaves_allowed - frappe.db.set_value('Leave Allocation', allocation.name, 'total_leaves_allocated', new_allocation) + for e_leave_type in e_leave_types: + leave_allocations = frappe.db.sql("""select name, employee, from_date, to_date from `tabLeave Allocation` where '{0}' + between from_date and to_date and docstatus=1 and leave_type='{1}'""" + .format(today, e_leave_type.name), as_dict=1) + for allocation in leave_allocations: + leave_policy = get_employee_leave_policy(allocation.employee) + if not leave_policy: + continue + if not e_leave_type.earned_leave_frequency == "Monthly": + if not check_frequency_hit(allocation.from_date, today, e_leave_type.earned_leave_frequency): + continue + annual_allocation = frappe.db.sql("""select annual_allocation from `tabLeave Policy Detail` + where parent=%s and leave_type=%s""", (leave_policy.name, e_leave_type.name)) + if annual_allocation and annual_allocation[0]: + earned_leaves = flt(annual_allocation[0][0]) / divide_by_frequency[e_leave_type.earned_leave_frequency] + if e_leave_type.rounding == "0.5": + earned_leaves = round(earned_leaves * 2) / 2 + else: + earned_leaves = round(earned_leaves) + + allocation = frappe.get_doc('Leave Allocation', allocation.name) + new_allocation = flt(allocation.total_leaves_allocated) + flt(earned_leaves) + new_allocation = new_allocation if new_allocation <= e_leave_type.max_leaves_allowed else e_leave_type.max_leaves_allowed + + if new_allocation == allocation.total_leaves_allocated: + continue + allocation.db_set("total_leaves_allocated", new_allocation, update_modified=False) + create_earned_leave_ledger_entry(allocation, earned_leaves, today) + + +def create_earned_leave_ledger_entry(allocation, earned_leaves, date): + ''' Create leave ledger entry based on the earned leave frequency ''' + allocation.new_leaves_allocated = earned_leaves + allocation.from_date = date + allocation.carry_forwarded_leaves = 0 + allocation.create_leave_ledger_entry() def check_frequency_hit(from_date, to_date, frequency): '''Return True if current date matches frequency''' From f13243a92eba52883aefe645d346ef4a67685958 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Thu, 6 Jun 2019 20:35:10 +0530 Subject: [PATCH 64/98] feat: fetch annual allocation based on leave policy --- .../hr/doctype/leave_allocation/leave_allocation.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation.js b/erpnext/hr/doctype/leave_allocation/leave_allocation.js index 005f45a657..7ecd3d1c91 100755 --- a/erpnext/hr/doctype/leave_allocation/leave_allocation.js +++ b/erpnext/hr/doctype/leave_allocation/leave_allocation.js @@ -58,6 +58,7 @@ frappe.ui.form.on("Leave Allocation", { }, leave_type: function(frm) { + frm.trigger("leave_policy") frm.trigger("calculate_total_leaves_allocated"); }, @@ -75,6 +76,17 @@ frappe.ui.form.on("Leave Allocation", { flt(frm.doc.carry_forwarded_leaves) + flt(frm.doc.new_leaves_allocated)); }, + leave_policy: function(frm) { + if(frm.doc.leave_policy && frm.doc.leave_type) { + frappe.db.get_value("Leave Policy Detail", + {'parent': frm.doc.leave_policy, 'leave_type': frm.doc.leave_type}, + 'annual_allocation', (r) => { + if (!r.exc) { + frm.set_value("new_leaves_allocated", flt(r.annual_allocation)); + } + }, "Leave Policy") + } + }, calculate_total_leaves_allocated: function(frm) { if (cint(frm.doc.carry_forward) == 1 && frm.doc.leave_type && frm.doc.employee) { return frappe.call({ From 00c607116bbf8627028290c30e6d1494c4530236 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Thu, 6 Jun 2019 20:37:34 +0530 Subject: [PATCH 65/98] fix: handle negative leaves without allocation --- erpnext/hr/doctype/leave_allocation/leave_allocation.py | 6 +++--- .../doctype/leave_ledger_entry/leave_ledger_entry.json | 4 ++-- .../hr/doctype/leave_ledger_entry/leave_ledger_entry.py | 8 ++------ erpnext/hr/doctype/leave_type/leave_type.py | 9 +-------- 4 files changed, 8 insertions(+), 19 deletions(-) diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation.py b/erpnext/hr/doctype/leave_allocation/leave_allocation.py index b402e908b0..843d3055d5 100755 --- a/erpnext/hr/doctype/leave_allocation/leave_allocation.py +++ b/erpnext/hr/doctype/leave_allocation/leave_allocation.py @@ -7,7 +7,6 @@ from frappe.utils import flt, date_diff, formatdate, add_days from frappe import _ from frappe.model.document import Document from erpnext.hr.utils import set_employee_name, get_leave_period -from erpnext.hr.doctype.leave_application.leave_application import get_approved_leaves_for_period from erpnext.hr.doctype.leave_ledger_entry.leave_ledger_entry import create_leave_ledger_entry class OverlapError(frappe.ValidationError): pass @@ -133,9 +132,9 @@ class LeaveAllocation(Document): ''' expire previous allocation leaves ''' leaves = get_unused_leaves(self.employee, self.leave_type, self.from_date) - if flt(leaves) > 0: + if leaves: args = dict( - leaves=leaves * -1, + leaves=flt(leaves) * -1, from_date=self.from_date, to_date=self.from_date, is_carry_forward=0, @@ -198,6 +197,7 @@ def get_unused_leaves(employee, leave_type, date): "employee": employee, "docstatus": 1, "leave_type": leave_type, + "is_lwp": 0 }, fieldname=['SUM(leaves)']) def validate_carry_forward(leave_type): diff --git a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.json b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.json index 4234fc4b40..0166e43c0f 100644 --- a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.json +++ b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.json @@ -62,7 +62,7 @@ }, { "fieldname": "leaves", - "fieldtype": "Int", + "fieldtype": "Float", "in_list_view": 1, "label": "Leaves" }, @@ -101,7 +101,7 @@ ], "in_create": 1, "is_submittable": 1, - "modified": "2019-06-05 12:56:04.980160", + "modified": "2019-06-06 20:33:37.531161", "modified_by": "Administrator", "module": "HR", "name": "Leave Ledger Entry", diff --git a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py index e85d5cef0a..6142dcf648 100644 --- a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py +++ b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py @@ -9,11 +9,6 @@ from frappe import _ from frappe.utils import add_days, today, flt class LeaveLedgerEntry(Document): - def validate_entries(self): - total_leaves = frappe.get_all('Leave Ledger Entry', ['SUM(leaves)']) - if total_leaves < 0: - frappe.throw(_("Invalid Ledger Entry")) - def on_cancel(self): # allow cancellation of expiry leaves if not self.is_expired: @@ -43,7 +38,8 @@ def create_leave_ledger_entry(ref_doc, args, submit=True): transaction_type=ref_doc.doctype, transaction_name=ref_doc.name, is_carry_forward=0, - is_expired=0 + is_expired=0, + is_lwp=0 ) ledger.update(args) if submit: diff --git a/erpnext/hr/doctype/leave_type/leave_type.py b/erpnext/hr/doctype/leave_type/leave_type.py index dcae5fe085..5b13edb684 100644 --- a/erpnext/hr/doctype/leave_type/leave_type.py +++ b/erpnext/hr/doctype/leave_type/leave_type.py @@ -10,11 +10,4 @@ from frappe import _ from frappe.model.document import Document class LeaveType(Document): - def validate(self): - if self.is_carry_forward: - self.validate_carry_forward() - - def validate_carry_forward(self): - max_days = 367 if calendar.isleap(datetime.now().year) else 366 - if not (0 <= self.carry_forward_leave_expiry <= max_days): - frappe.throw(_('Invalid entry!! Carried forward days need to expire within a year')) \ No newline at end of file + pass \ No newline at end of file From b840ba4407c3b0cf124b8bb9f3b32267f04ed821 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Thu, 6 Jun 2019 20:38:23 +0530 Subject: [PATCH 66/98] feat: show dashboard on submit --- .../leave_application/leave_application.js | 24 ++++++++------ .../leave_application/leave_application.json | 33 ++++++++++++++++++- 2 files changed, 46 insertions(+), 11 deletions(-) diff --git a/erpnext/hr/doctype/leave_application/leave_application.js b/erpnext/hr/doctype/leave_application/leave_application.js index cb1042c5ef..2076ccd22a 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.js +++ b/erpnext/hr/doctype/leave_application/leave_application.js @@ -53,24 +53,27 @@ frappe.ui.form.on("Leave Application", { }, callback: function(r) { if (!r.exc && r.message['leave_allocation']) { - leave_details = r.message['leave_allocation']; + frm.set_value('leave_details', JSON.stringify(r.message['leave_allocation'])); } if (!r.exc && r.message['leave_approver']) { frm.set_value('leave_approver', r.message['leave_approver']); } } }); - - $("div").remove(".form-dashboard-section"); - let section = frm.dashboard.add_section( - frappe.render_template('leave_application_dashboard', { - data: leave_details - }) - ); - frm.dashboard.show(); + frm.trigger("create_dashboard") } }, + create_dashboard: function(frm) { + $("div").remove(".form-dashboard-section"); + let section = frm.dashboard.add_section( + frappe.render_template('leave_application_dashboard', { + data: JSON.parse(frm.doc.leave_details) + }) + ); + frm.dashboard.show(); + }, + refresh: function(frm) { if (frm.is_new()) { frm.trigger("calculate_total_days"); @@ -95,6 +98,7 @@ frappe.ui.form.on("Leave Application", { }; frappe.set_route("query-report", "Employee Leave Balance"); }); + frm.trigger("create_dashboard"); } }, @@ -148,7 +152,7 @@ frappe.ui.form.on("Leave Application", { }, get_leave_balance: function(frm) { - if(frm.doc.docstatus==0 && frm.doc.employee && frm.doc.leave_type && frm.doc.from_date) { + if(frm.doc.docstatus==0 && frm.doc.employee && frm.doc.leave_type && frm.doc.from_date && frm.doc.to_date) { return frappe.call({ method: "erpnext.hr.doctype.leave_application.leave_application.get_leave_balance_on", args: { diff --git a/erpnext/hr/doctype/leave_application/leave_application.json b/erpnext/hr/doctype/leave_application/leave_application.json index 812494c1c0..c59449f850 100644 --- a/erpnext/hr/doctype/leave_application/leave_application.json +++ b/erpnext/hr/doctype/leave_application/leave_application.json @@ -653,6 +653,37 @@ "translatable": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "leave_details", + "fieldtype": "Small Text", + "hidden": 1, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Leave Details", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "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, @@ -951,7 +982,7 @@ "issingle": 0, "istable": 0, "max_attachments": 3, - "modified": "2019-05-30 11:28:14.745572", + "modified": "2019-05-31 11:30:14.745572", "modified_by": "Administrator", "module": "HR", "name": "Leave Application", From c7b9ae9c5e6e0f08004bb7f997eb8909fb9919fb Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Thu, 6 Jun 2019 20:38:59 +0530 Subject: [PATCH 67/98] fix: get leave balance based on the ledger entries --- .../leave_application/leave_application.py | 157 +++++++++--------- 1 file changed, 75 insertions(+), 82 deletions(-) diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py index fd13436d8f..fec7cede2f 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.py +++ b/erpnext/hr/doctype/leave_application/leave_application.py @@ -5,7 +5,7 @@ from __future__ import unicode_literals import frappe from frappe import _ from frappe.utils import cint, cstr, date_diff, flt, formatdate, getdate, get_link_to_form, \ - comma_or, get_fullname, add_days, nowdate + comma_or, get_fullname, add_days, nowdate, get_datetime_str from erpnext.hr.utils import set_employee_name, get_leave_period from erpnext.hr.doctype.leave_block_list.leave_block_list import get_applicable_block_dates from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee @@ -353,6 +353,8 @@ class LeaveApplication(Document): expiry_date = get_allocation_expiry(self.employee, self.leave_type, self.to_date, self.from_date) + lwp = frappe.db.get_value("Leave Type", self.leave_type, "is_lwp") + if expiry_date: self.create_ledger_entry_for_intermediate_allocation_expiry(expiry_date, submit) else: @@ -360,6 +362,7 @@ class LeaveApplication(Document): leaves=self.total_leave_days * -1, from_date=self.from_date, to_date=self.to_date, + is_lwp=lwp ) create_leave_ledger_entry(self, args, submit) @@ -407,21 +410,22 @@ def get_number_of_leave_days(employee, leave_type, from_date, to_date, half_day @frappe.whitelist() def get_leave_details(employee, date): - allocation_records = get_leave_allocation_records(date, employee).get(employee, frappe._dict()) + allocation_records = get_leave_allocation_records(employee, date) leave_allocation = {} for d in allocation_records: allocation = allocation_records.get(d, frappe._dict()) remaining_leaves = get_leave_balance_on(employee, d, date, to_date = allocation.to_date, consider_all_leaves_in_the_allocation_period=True) 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") + leaves_taken = get_leaves_for_period(employee, d, allocation.from_date, date) * -1 + leaves_pending = get_pending_leaves_for_period(employee, d, allocation.from_date, date) leave_allocation[d] = { "total_leaves": allocation.total_leaves_allocated, "leaves_taken": leaves_taken, "pending_leaves": leaves_pending, "remaining_leaves": remaining_leaves} + leave_details = leave_allocation ret = { 'leave_allocation': leave_allocation, @@ -436,17 +440,64 @@ def get_leave_balance_on(employee, leave_type, date, to_date=nowdate(), allocati ''' Returns leave balance till date and fetches expiry date based on to_date to calculate minimum remaining leave balance ''' - if allocation_records == None: - allocation_records = get_leave_allocation_records(date, employee).get(employee, frappe._dict()) + if not allocation_records: + allocation_records = get_leave_allocation_records(employee, date, leave_type) + allocation = allocation_records.get(leave_type, frappe._dict()) end_date = allocation.to_date if consider_all_leaves_in_the_allocation_period else date expiry = get_allocation_expiry(employee, leave_type, to_date, date) - leaves_taken = get_leaves_taken(employee, leave_type, allocation.from_date, end_date) + leaves_taken = get_leaves_for_period(employee, leave_type, allocation.from_date, end_date) return get_remaining_leaves(allocation, leaves_taken, date, expiry) +def get_leave_allocation_records(employee, date, leave_type=None): + ''' returns the total allocated leaves and carry forwarded leaves based on ledger entries ''' + + conditions = ("and leave_type='%s'" % leave_type) if leave_type else "" + allocation_details = frappe.db.sql(""" + SELECT + SUM(CASE WHEN is_carry_forward = 1 THEN leaves ELSE 0 END) as cf_leaves, + SUM(CASE WHEN is_carry_forward = 0 THEN leaves ELSE 0 END) as new_leaves, + MIN(from_date) as from_date, + MAX(to_date) as to_date, + leave_type + FROM `tabLeave Ledger Entry` + WHERE + from_date <= %(date)s + AND to_date >= %(date)s + AND docstatus=1 + AND transaction_type="Leave Allocation" + AND employee=%(employee)s + AND is_expired=0 + AND is_lwp=0 + {0} + GROUP BY employee, leave_type + """.format(conditions), dict(date=date, employee=employee), as_dict=1) + + allocated_leaves = frappe._dict() + for d in allocation_details: + allocated_leaves.setdefault(d.leave_type, frappe._dict({ + "from_date": d.from_date, + "to_date": d.to_date, + "total_leaves_allocated": flt(d.cf_leaves) + flt(d.new_leaves), + "carry_forwarded_leaves": d.cf_leaves, + "new_leaves_allocated": d.new_leaves, + "leave_type": d.leave_type + })) + return allocated_leaves + +def get_pending_leaves_for_period(employee, leave_type, from_date, to_date): + ''' Returns leaves that are pending approval ''' + return frappe.db.get_value("Leave Application", + filters={ + "employee": employee, + "leave_type": leave_type, + "from_date": ("<=", from_date), + "to_date": (">=", to_date), + }, fieldname=['SUM(total_leave_days)']) or flt(0) + def get_remaining_leaves(allocation, leaves_taken, date, expiry): ''' Returns minimum leaves remaining after comparing with remaining days for allocation expiry ''' def _get_remaining_leaves(allocated_leaves, end_date): @@ -465,39 +516,14 @@ def get_remaining_leaves(allocation, leaves_taken, date, expiry): else: return _get_remaining_leaves(allocation.total_leaves_allocated, allocation.to_date) -def get_leaves_taken(employee, leave_type, from_date, to_date): - ''' Returns leaves taken based on leave application/encashment ''' - leaves = frappe.db.get_all("Leave Ledger Entry", filters={ - 'Employee':employee, - 'leave_type':leave_type, - 'leaves': ("<", 0), - 'to_date':("<=", to_date), - 'from_date': (">=", from_date)}, - or_filters={ - 'is_expired': 0, - 'is_carry_forward': 1 - }, fields=['SUM(leaves) as leaves']) - return leaves[0]['leaves'] if leaves else None - -def get_total_allocated_leaves(employee, leave_type, date): - filters= { - 'from_date': ['<=', date], - 'to_date': ['>=', date], - 'docstatus': 1, - 'leave_type': leave_type, - 'employee': employee - } - - leave_allocation_records = frappe.db.get_all('Leave Allocation', filters=filters, fields=['total_leaves_allocated']) - - return flt(leave_allocation_records[0]['total_leaves_allocated']) if leave_allocation_records else flt(0) - -def get_leaves_for_period(employee, leave_type, from_date, to_date, status, docname=None): - leave_applications = frappe.db.sql(""" - select name, employee, leave_type, from_date, to_date, total_leave_days - from `tabLeave Application` +def get_leaves_for_period(employee, leave_type, from_date, to_date): + leave_entries = frappe.db.sql(""" + select employee, leave_type, from_date, to_date, leaves, transaction_type + from `tabLeave Ledger Entry` where employee=%(employee)s and leave_type=%(leave_type)s - and status = %(status)s and docstatus != 2 + and docstatus=1 + and leaves<0 + and (is_expired=0 or is_carry_forward=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)) @@ -505,58 +531,25 @@ def get_leaves_for_period(employee, leave_type, from_date, to_date, status, docn "from_date": from_date, "to_date": to_date, "employee": employee, - "status": status, "leave_type": leave_type }, as_dict=1) leave_days = 0 - for leave_app in leave_applications: - if docname and leave_app.name == docname: - continue - if leave_app.from_date >= getdate(from_date) and leave_app.to_date <= getdate(to_date): - leave_days += leave_app.total_leave_days + + for leave_entry in leave_entries: + if leave_entry.from_date >= getdate(from_date) and \ + leave_entry.to_date <= getdate(to_date) and leave_entry.transaction_type=='Leave Encashment': + leave_days += leave_entry.leaves else: - if leave_app.from_date < getdate(from_date): - leave_app.from_date = from_date - if leave_app.to_date > getdate(to_date): - leave_app.to_date = to_date + if leave_entry.from_date < getdate(from_date): + leave_entry.from_date = from_date + if leave_entry.to_date > getdate(to_date): + leave_entry.to_date = to_date leave_days += get_number_of_leave_days(employee, leave_type, - leave_app.from_date, leave_app.to_date) + leave_entry.from_date, leave_entry.to_date) * -1 return leave_days -def get_leave_allocation_records(date, employee=None): - conditions = (" and employee='%s'" % employee) if employee else "" - - leave_allocation_records = frappe.db.sql(""" - SELECT - employee, - leave_type, - total_leaves_allocated, - carry_forwarded_leaves, - new_leaves_allocated, - carry_forward, - from_date, - to_date - FROM - `tabLeave Allocation` - WHERE - %s between from_date and to_date - AND docstatus=1 {0}""".format(conditions), (date), as_dict=1) #nosec - - allocated_leaves = frappe._dict() - for d in leave_allocation_records: - allocated_leaves.setdefault(d.employee, frappe._dict()).setdefault(d.leave_type, frappe._dict({ - "from_date": d.from_date, - "to_date": d.to_date, - "total_leaves_allocated": d.total_leaves_allocated, - "carry_forward": d.carry_forward, - "carry_forwarded_leaves": d.carry_forwarded_leaves, - "new_leaves_allocated": d.new_leaves_allocated, - "leave_type": d.leave_type - })) - return allocated_leaves - @frappe.whitelist() def get_holidays(employee, from_date, to_date): '''get holidays between two dates for the given employee''' From 368a97436820a35148f8f470b683bed0ed47a381 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Thu, 6 Jun 2019 20:43:32 +0530 Subject: [PATCH 68/98] style: change formatting --- erpnext/hr/doctype/leave_application/leave_application.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py index fec7cede2f..02539a4c3b 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.py +++ b/erpnext/hr/doctype/leave_application/leave_application.py @@ -474,7 +474,7 @@ def get_leave_allocation_records(employee, date, leave_type=None): AND is_lwp=0 {0} GROUP BY employee, leave_type - """.format(conditions), dict(date=date, employee=employee), as_dict=1) + """.format(conditions), dict(date=date, employee=employee), as_dict=1) #nosec allocated_leaves = frappe._dict() for d in allocation_details: From e4d03bf0d0d9d4a3ca8fb548630ebb7ce30018c1 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Fri, 7 Jun 2019 11:30:27 +0530 Subject: [PATCH 69/98] fix: consider expiry in leaves --- .../leave_allocation/leave_allocation.js | 19 ++++++---- .../leave_allocation/leave_allocation.py | 36 +++++++------------ .../leave_application/leave_application.py | 5 ++- 3 files changed, 26 insertions(+), 34 deletions(-) diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation.js b/erpnext/hr/doctype/leave_allocation/leave_allocation.js index 7ecd3d1c91..1b3349a1ce 100755 --- a/erpnext/hr/doctype/leave_allocation/leave_allocation.js +++ b/erpnext/hr/doctype/leave_allocation/leave_allocation.js @@ -23,10 +23,13 @@ frappe.ui.form.on("Leave Allocation", { refresh: function(frm) { if(frm.doc.docstatus === 1 && frm.doc.status === "Active") { - // expire current allocation - frm.add_custom_button(__('Expire Allocation'), function() { - frm.trigger("expire_allocation"); - }); + var valid_expiry = moment(frappe.datetime.get_today()).isBetween(frm.doc.from_date, frm.doc.to_date); + if(valid_expiry) { + // expire current allocation + frm.add_custom_button(__('Expire Allocation'), function() { + frm.trigger("expire_allocation"); + }); + } // opens leave balance report for employee frm.add_custom_button(__('Leave Balance'), function() { @@ -40,15 +43,17 @@ frappe.ui.form.on("Leave Allocation", { expire_allocation: function(frm) { frappe.call({ - method: 'erpnext.hr.doctype.leave_application.leave_application.expire_previous_allocation', + method: 'expire_allocation', + doc: frm.doc, args: { - ref_doc: frm.doc + current: true }, freeze: true, callback: function(r){ if(!r.exc){ frappe.msgprint(__("Allocation Expired!")); } + frm.refresh(); } }); }, @@ -81,7 +86,7 @@ frappe.ui.form.on("Leave Allocation", { frappe.db.get_value("Leave Policy Detail", {'parent': frm.doc.leave_policy, 'leave_type': frm.doc.leave_type}, 'annual_allocation', (r) => { - if (!r.exc) { + if (r && !r.exc) { frm.set_value("new_leaves_allocated", flt(r.annual_allocation)); } }, "Leave Policy") diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation.py b/erpnext/hr/doctype/leave_allocation/leave_allocation.py index 843d3055d5..72ea2733b0 100755 --- a/erpnext/hr/doctype/leave_allocation/leave_allocation.py +++ b/erpnext/hr/doctype/leave_allocation/leave_allocation.py @@ -3,7 +3,7 @@ from __future__ import unicode_literals import frappe -from frappe.utils import flt, date_diff, formatdate, add_days +from frappe.utils import flt, date_diff, formatdate, add_days, today from frappe import _ from frappe.model.document import Document from erpnext.hr.utils import set_employee_name, get_leave_period @@ -41,8 +41,8 @@ class LeaveAllocation(Document): .format(self.leave_type, self.employee)) def on_submit(self): - self.expire_previous_allocation() self.create_leave_ledger_entry() + self.expire_allocation() def on_cancel(self): self.create_leave_ledger_entry(submit=False) @@ -128,20 +128,25 @@ class LeaveAllocation(Document): ) create_leave_ledger_entry(self, args, submit) - def expire_previous_allocation(self): - ''' expire previous allocation leaves ''' - leaves = get_unused_leaves(self.employee, self.leave_type, self.from_date) + def expire_allocation(self, current=False): + ''' expires allocation ''' + date = self.to_date if current else self.from_date + leaves = get_unused_leaves(self.employee, self.leave_type, date) if leaves: + expiry_date = today() if current else add_days(self.from_date, -1) args = dict( leaves=flt(leaves) * -1, - from_date=self.from_date, - to_date=self.from_date, + from_date=expiry_date, + to_date=expiry_date, is_carry_forward=0, is_expired=1 ) create_leave_ledger_entry(self, args) + if current: + frappe.db.set_value("Leave Allocation", self.name, "status", "Expired") + def get_leave_allocation_for_period(employee, leave_type, from_date, to_date): leave_allocated = 0 leave_allocations = frappe.db.sql(""" @@ -174,23 +179,6 @@ def get_carry_forwarded_leaves(employee, leave_type, date, carry_forward=None): return carry_forwarded_leaves -@frappe.whitelist() -def expire_current_allocation(ref_doc): - ''' expire previous allocation leaves ''' - leaves = get_unused_leaves(ref_doc.employee, ref_doc.leave_type, ref_doc.to_date) - - if flt(leaves) > 0: - args = dict( - leaves=leaves * -1, - from_date=ref_doc.to_date, - to_date=ref_doc.to_date, - is_carry_forward=0, - is_expired=1 - ) - create_leave_ledger_entry(ref_doc, args) - - frappe.db.set_value("Leave Allocation", ref_doc.name, "status", "Expired") - def get_unused_leaves(employee, leave_type, date): return frappe.db.get_value("Leave Ledger Entry", filters={ "to_date": ("<=", date), diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py index 02539a4c3b..a9e4f05ca2 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.py +++ b/erpnext/hr/doctype/leave_application/leave_application.py @@ -523,7 +523,6 @@ def get_leaves_for_period(employee, leave_type, from_date, to_date): where employee=%(employee)s and leave_type=%(leave_type)s and docstatus=1 and leaves<0 - and (is_expired=0 or is_carry_forward=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)) @@ -536,8 +535,8 @@ def get_leaves_for_period(employee, leave_type, from_date, to_date): leave_days = 0 for leave_entry in leave_entries: - if leave_entry.from_date >= getdate(from_date) and \ - leave_entry.to_date <= getdate(to_date) and leave_entry.transaction_type=='Leave Encashment': + if leave_entry.from_date >= getdate(from_date) and leave_entry.to_date <= getdate(to_date) \ + and leave_entry.transaction_type in ('Leave Encashment', 'Leave Allocation'): leave_days += leave_entry.leaves else: if leave_entry.from_date < getdate(from_date): From 283d2c5ca24926e5737bc58df12c8164744417d3 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Tue, 11 Jun 2019 16:35:29 +0530 Subject: [PATCH 70/98] fix: only create leave application ledger on intermediate allocation expiry --- .../leave_application/leave_application.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py index a9e4f05ca2..f6550f0dbe 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.py +++ b/erpnext/hr/doctype/leave_application/leave_application.py @@ -370,18 +370,19 @@ class LeaveApplication(Document): ''' splits leave application into two ledger entries to consider expiry of allocation ''' args = dict( from_date=self.from_date, - to_date=self.to_date, + to_date=expiry_date, leaves=(date_diff(expiry_date, self.from_date) + 1) * -1 ) create_leave_ledger_entry(self, args, submit) - start_date = add_days(expiry_date, 1) - args.update(dict( - from_date=start_date, - to_date=self.to_date, - leaves=date_diff(self.to_date, expiry_date) * -1 - )) - create_leave_ledger_entry(self, args, submit) + if expiry_date != self.to_date: + start_date = add_days(expiry_date, 1) + args.update(dict( + from_date=start_date, + to_date=self.to_date, + leaves=date_diff(self.to_date, expiry_date) * -1 + )) + create_leave_ledger_entry(self, args, submit) def get_allocation_expiry(employee, leave_type, to_date, from_date): expiry = frappe.get_all("Leave Ledger Entry", From 4e1b60d401e9ce5f6f59aa8847985b421dc1aa29 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Fri, 14 Jun 2019 11:15:54 +0530 Subject: [PATCH 71/98] style: change formatting --- .../doctype/leave_allocation/leave_allocation.js | 15 +++++++-------- .../leave_application/leave_application.js | 2 +- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation.js b/erpnext/hr/doctype/leave_allocation/leave_allocation.js index 1b3349a1ce..a620d9e081 100755 --- a/erpnext/hr/doctype/leave_allocation/leave_allocation.js +++ b/erpnext/hr/doctype/leave_allocation/leave_allocation.js @@ -63,7 +63,7 @@ frappe.ui.form.on("Leave Allocation", { }, leave_type: function(frm) { - frm.trigger("leave_policy") + frm.trigger("leave_policy"); frm.trigger("calculate_total_leaves_allocated"); }, @@ -83,13 +83,12 @@ frappe.ui.form.on("Leave Allocation", { leave_policy: function(frm) { if(frm.doc.leave_policy && frm.doc.leave_type) { - frappe.db.get_value("Leave Policy Detail", - {'parent': frm.doc.leave_policy, 'leave_type': frm.doc.leave_type}, - 'annual_allocation', (r) => { - if (r && !r.exc) { - frm.set_value("new_leaves_allocated", flt(r.annual_allocation)); - } - }, "Leave Policy") + frappe.db.get_value("Leave Policy Detail",{ + 'parent': frm.doc.leave_policy, + 'leave_type': frm.doc.leave_type + }, 'annual_allocation', (r) => { + if (r && !r.exc) frm.set_value("new_leaves_allocated", flt(r.annual_allocation)); + }, "Leave Policy"); } }, calculate_total_leaves_allocated: function(frm) { diff --git a/erpnext/hr/doctype/leave_application/leave_application.js b/erpnext/hr/doctype/leave_application/leave_application.js index 2076ccd22a..5534cec061 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.js +++ b/erpnext/hr/doctype/leave_application/leave_application.js @@ -60,7 +60,7 @@ frappe.ui.form.on("Leave Application", { } } }); - frm.trigger("create_dashboard") + frm.trigger("create_dashboard"); } }, From 87adaed9336dd2692b185f9ece5df93877990775 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Fri, 14 Jun 2019 15:13:53 +0530 Subject: [PATCH 72/98] fix: generate ledger entries for all leave transactions --- .../v12_0/generate_leave_ledger_entries.py | 106 +++++++----------- 1 file changed, 42 insertions(+), 64 deletions(-) diff --git a/erpnext/patches/v12_0/generate_leave_ledger_entries.py b/erpnext/patches/v12_0/generate_leave_ledger_entries.py index 065a0137a5..07e53c7450 100644 --- a/erpnext/patches/v12_0/generate_leave_ledger_entries.py +++ b/erpnext/patches/v12_0/generate_leave_ledger_entries.py @@ -3,84 +3,70 @@ from __future__ import unicode_literals import frappe +from frappe.utils import getdate def execute(): """ Generates leave ledger entries for leave allocation/application/encashment for last allocation """ - frappe.reload_doc("HR","doctype", "Leave Ledger Entry") - frappe.reload_doc("HR","doctype", "Leave Encashment") + frappe.reload_doc("HR", "doctype", "Leave Ledger Entry") + frappe.reload_doc("HR", "doctype", "Leave Encashment") if frappe.db.a_row_exists("Leave Ledger Entry"): return - allocation_list = get_allocation_records() - generate_allocation_ledger_entries(allocation_list) - generate_application_leave_ledger_entries(allocation_list) - generate_encashment_leave_ledger_entries(allocation_list) + generate_allocation_ledger_entries() + generate_application_leave_ledger_entries() + generate_encashment_leave_ledger_entries() -def generate_allocation_ledger_entries(allocation_list): +def generate_allocation_ledger_entries(): ''' fix ledger entries for missing leave allocation transaction ''' - from erpnext.hr.doctype.leave_allocation.leave_allocation import LeaveAllocation + allocation_list = get_allocation_records() for allocation in allocation_list: if not frappe.db.exists("Leave Ledger Entry", {'transaction_type': 'Leave Allocation', 'transaction_name': allocation.name}): allocation.update(dict(doctype="Leave Allocation")) - leave_allocation = LeaveAllocation(allocation) - leave_allocation.create_leave_ledger_entry() + allocation_obj = frappe.get_doc(allocation) + allocation_obj.create_leave_ledger_entry() + if allocation.to_date <= getdate(): + allocation_obj.expire_allocation() -def generate_application_leave_ledger_entries(allocation_list): + +def generate_application_leave_ledger_entries(): ''' fix ledger entries for missing leave application transaction ''' - from erpnext.hr.doctype.leave_application.leave_application import LeaveApplication + leave_applications = get_leaves_application_records() - leave_applications = get_leaves_application_records(allocation_list) - - for record in leave_applications: + for application in leave_applications: if not frappe.db.exists("Leave Ledger Entry", {'transaction_type': 'Leave Application', 'transaction_name': record.name}): - record.update(dict(doctype="Leave Application")) - leave_application = LeaveApplication(record) - leave_application.create_leave_ledger_entry() + application.update(dict(doctype="Leave Application")) + frappe.get_doc(application).create_leave_ledger_entry() -def generate_encashment_leave_ledger_entries(allocation_list): +def generate_encashment_leave_ledger_entries(): ''' fix ledger entries for missing leave encashment transaction ''' - from erpnext.hr.doctype.leave_encashment.leave_encashment import LeaveEncashment + leave_encashments = get_leave_encashment_records() - leave_encashments = get_leave_encashment_records(allocation_list) - - for record in leave_encashments: + for encashment in leave_encashments: if not frappe.db.exists("Leave Ledger Entry", {'transaction_type': 'Leave Encashment', 'transaction_name': record.name}): - record.update(dict(doctype="Leave Encashment")) - leave_encashment = LeaveEncashment(record) - leave_encashment.create_leave_ledger_entry() + encashment.update(dict(doctype="Leave Encashment")) + frappe.get_doc(encashment).create_leave_ledger_entry() def get_allocation_records(): return frappe.db.sql(""" - WITH allocation_values AS ( - SELECT - DISTINCT name, - employee, - leave_type, - new_leaves_allocated, - carry_forwarded_leaves, - from_date, - to_date, - carry_forward, - RANK() OVER( - PARTITION BY employee, leave_type - ORDER BY to_date DESC - ) as allocation - FROM `tabLeave Allocation` - ) SELECT - * - FROM - `allocation_values` + DISTINCT name, + employee, + leave_type, + new_leaves_allocated, + carry_forwarded_leaves, + from_date, + to_date, + carry_forward + FROM `tabLeave Allocation` WHERE - allocation=1 + docstatus=1 + ORDER BY to_date ASC """, as_dict=1) -def get_leaves_application_records(allocation_list): - leave_applications = [] - for allocation in allocation_list: - leave_applications += frappe.db.sql(""" +def get_leaves_application_records(): + return frappe.db.sql(""" SELECT DISTINCT name, employee, @@ -90,16 +76,11 @@ def get_leaves_application_records(allocation_list): to_date FROM `tabLeave Application` WHERE - from_date >= %s - AND leave_type = %s - AND employee = %s - """, (allocation.from_date, allocation.leave_type, allocation.employee), as_dict=1) - return leave_applications + docstatus=1 + """, as_dict=1) -def get_leave_encashment_records(allocation_list): - leave_encashments = [] - for allocation in allocation_list: - leave_encashments += frappe.db.sql(""" +def get_leave_encashment_records(): + return frappe.db.sql(""" SELECT DISTINCT name, employee, @@ -108,8 +89,5 @@ def get_leave_encashment_records(allocation_list): encashment_date FROM `tabLeave Encashment` WHERE - leave_type = %s - AND employee = %s - AND encashment_date >= %s - """, (allocation.leave_type, allocation.employee, allocation.from_date), as_dict=1) - return leave_encashments \ No newline at end of file + AND docstatus=1 + """, as_dict=1) \ No newline at end of file From 24fbe23c8d4c1a9553f2bc3721f98d25600c5785 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Fri, 14 Jun 2019 15:36:26 +0530 Subject: [PATCH 73/98] fix: add indicators for expire in the leave allocation list --- .../leave_allocation/leave_allocation_list.js | 12 +++++++ .../leave_application/leave_application.js | 21 +++++------- .../leave_application/leave_application.json | 33 +------------------ .../leave_application/leave_application.py | 1 - 4 files changed, 21 insertions(+), 46 deletions(-) create mode 100644 erpnext/hr/doctype/leave_allocation/leave_allocation_list.js diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation_list.js b/erpnext/hr/doctype/leave_allocation/leave_allocation_list.js new file mode 100644 index 0000000000..946b4f8492 --- /dev/null +++ b/erpnext/hr/doctype/leave_allocation/leave_allocation_list.js @@ -0,0 +1,12 @@ +// Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors +// License: GNU General Public License v3. See license.txt + +// render +frappe.listview_settings['Leave Allocation'] = { + get_indicator: function(doc) { + if(doc.status==="Expired") { + return [__("Expired"), "darkgrey", "status, =, Expired"]; + } + }, + right_column: "grand_total" +}; diff --git a/erpnext/hr/doctype/leave_application/leave_application.js b/erpnext/hr/doctype/leave_application/leave_application.js index 5534cec061..b81e615b72 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.js +++ b/erpnext/hr/doctype/leave_application/leave_application.js @@ -53,27 +53,23 @@ frappe.ui.form.on("Leave Application", { }, callback: function(r) { if (!r.exc && r.message['leave_allocation']) { - frm.set_value('leave_details', JSON.stringify(r.message['leave_allocation'])); + leave_details = r.message['leave_allocation']; } if (!r.exc && r.message['leave_approver']) { frm.set_value('leave_approver', r.message['leave_approver']); } } }); - frm.trigger("create_dashboard"); + $("div").remove(".form-dashboard-section"); + frm.dashboard.add_section( + frappe.render_template('leave_application_dashboard', { + data: leave_details + }) + ); + frm.dashboard.show(); } }, - create_dashboard: function(frm) { - $("div").remove(".form-dashboard-section"); - let section = frm.dashboard.add_section( - frappe.render_template('leave_application_dashboard', { - data: JSON.parse(frm.doc.leave_details) - }) - ); - frm.dashboard.show(); - }, - refresh: function(frm) { if (frm.is_new()) { frm.trigger("calculate_total_days"); @@ -98,7 +94,6 @@ frappe.ui.form.on("Leave Application", { }; frappe.set_route("query-report", "Employee Leave Balance"); }); - frm.trigger("create_dashboard"); } }, diff --git a/erpnext/hr/doctype/leave_application/leave_application.json b/erpnext/hr/doctype/leave_application/leave_application.json index c59449f850..60efb33ca4 100644 --- a/erpnext/hr/doctype/leave_application/leave_application.json +++ b/erpnext/hr/doctype/leave_application/leave_application.json @@ -653,37 +653,6 @@ "translatable": 0, "unique": 0 }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "leave_details", - "fieldtype": "Small Text", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Leave Details", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "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, @@ -982,7 +951,7 @@ "issingle": 0, "istable": 0, "max_attachments": 3, - "modified": "2019-05-31 11:30:14.745572", + "modified": "2019-06-01 11:30:14.745572", "modified_by": "Administrator", "module": "HR", "name": "Leave Application", diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py index f6550f0dbe..f87f7d53ef 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.py +++ b/erpnext/hr/doctype/leave_application/leave_application.py @@ -426,7 +426,6 @@ def get_leave_details(employee, date): "leaves_taken": leaves_taken, "pending_leaves": leaves_pending, "remaining_leaves": remaining_leaves} - leave_details = leave_allocation ret = { 'leave_allocation': leave_allocation, From fefdac432ce02d07b24fe3d757ce97db2a8c483a Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Fri, 14 Jun 2019 15:39:50 +0530 Subject: [PATCH 74/98] refactor: add json changes --- .../leave_allocation/leave_allocation.json | 646 +------------ .../leave_application/leave_application.json | 915 ++---------------- 2 files changed, 123 insertions(+), 1438 deletions(-) diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation.json b/erpnext/hr/doctype/leave_allocation/leave_allocation.json index 125fb3150b..41d864d912 100644 --- a/erpnext/hr/doctype/leave_allocation/leave_allocation.json +++ b/erpnext/hr/doctype/leave_allocation/leave_allocation.json @@ -1,750 +1,212 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, "allow_import": 1, - "allow_rename": 0, "autoname": "naming_series:", - "beta": 0, "creation": "2013-02-20 19:10:38", - "custom": 0, - "docstatus": 0, "doctype": "DocType", "document_type": "Setup", - "editable_grid": 0, "engine": "InnoDB", + "field_order": [ + "naming_series", + "employee", + "employee_name", + "department", + "column_break1", + "leave_type", + "from_date", + "to_date", + "section_break_6", + "new_leaves_allocated", + "carry_forward", + "carry_forwarded_leaves", + "total_leaves_allocated", + "total_leaves_encashed", + "column_break_10", + "compensatory_request", + "leave_period", + "leave_policy", + "status", + "amended_from", + "notes", + "description" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", "fieldname": "naming_series", "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": 0, "label": "Series", - "length": 0, "no_copy": 1, "options": "HR-LAL-.YYYY.-", - "permlevel": 0, - "precision": "", "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, "reqd": 1, - "search_index": 0, - "set_only_once": 1, - "translatable": 0, - "unique": 0 + "set_only_once": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "employee", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, "in_global_search": 1, "in_list_view": 1, "in_standard_filter": 1, "label": "Employee", - "length": 0, - "no_copy": 0, "oldfieldname": "employee", "oldfieldtype": "Link", "options": "Employee", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, "reqd": 1, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "search_index": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fetch_from": "employee.employee_name", "fieldname": "employee_name", "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, "in_global_search": 1, "in_list_view": 1, - "in_standard_filter": 0, "label": "Employee Name", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "search_index": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fetch_from": "employee.department", "fieldname": "department", "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": "Department", - "length": 0, - "no_copy": 0, "options": "Department", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "column_break1", "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, - "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, "width": "50%" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "leave_type", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, "in_standard_filter": 1, "label": "Leave Type", - "length": 0, - "no_copy": 0, "oldfieldname": "leave_type", "oldfieldtype": "Link", "options": "Leave Type", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, "reqd": 1, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "search_index": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "from_date", "fieldtype": "Date", - "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": "From Date", - "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": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "reqd": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "to_date", "fieldtype": "Date", - "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": "To Date", - "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": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "reqd": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "section_break_6", "fieldtype": "Section 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, - "label": "Allocation", - "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, - "translatable": 0, - "unique": 0 + "label": "Allocation" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, "bold": 1, - "collapsible": 0, - "columns": 0, "fieldname": "new_leaves_allocated", "fieldtype": "Float", - "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": "New Leaves Allocated", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "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 + "label": "New Leaves Allocated" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "", + "default": "0", "fieldname": "carry_forward", "fieldtype": "Check", - "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": "Add unused leaves from previous allocations", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "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 + "label": "Add unused leaves from previous allocations" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "carry_forward", "fieldname": "carry_forwarded_leaves", "fieldtype": "Float", - "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": "Unused leaves", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "total_leaves_allocated", "fieldtype": "Float", - "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": "Total Leaves Allocated", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "reqd": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "eval:doc.total_leaves_encashed>0", "fieldname": "total_leaves_encashed", "fieldtype": "Float", - "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": "Total Leaves Encashed", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "column_break_10", - "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, - "translatable": 0, - "unique": 0 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "compensatory_request", "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": "Compensatory Leave Request", - "length": 0, - "no_copy": 0, "options": "Compensatory Leave Request", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "leave_period", "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": 1, "label": "Leave Period", - "length": 0, - "no_copy": 0, "options": "Leave Period", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fetch_from": "employee.leave_policy", "fieldname": "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": 1, "label": "Leave Policy", - "length": 0, - "no_copy": 0, "options": "Leave Policy", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "status", "fieldtype": "Select", "hidden": 1, - "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": 0, "options": "Active\nExpired", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "amended_from", "fieldtype": "Link", - "hidden": 0, "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Amended From", - "length": 0, "no_copy": 1, "oldfieldname": "amended_from", "oldfieldtype": "Data", "options": "Leave Allocation", - "permlevel": 0, "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, "collapsible": 1, - "columns": 0, "fieldname": "notes", "fieldtype": "Section 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, - "label": "Notes", - "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, - "translatable": 0, - "unique": 0 + "label": "Notes" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "description", "fieldtype": "Small Text", - "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": "Description", - "length": 0, - "no_copy": 0, "oldfieldname": "reason", "oldfieldtype": "Small Text", - "permlevel": 0, - "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, "width": "300px" } ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, "icon": "fa fa-ok", "idx": 1, - "image_view": 0, - "in_create": 0, "is_submittable": 1, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2019-05-31 11:28:09.360525", + "modified": "2019-06-14 15:39:02.898695", "modified_by": "Administrator", "module": "HR", "name": "Leave Allocation", @@ -756,15 +218,10 @@ "create": 1, "delete": 1, "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, "print": 1, "read": 1, "report": 1, "role": "HR User", - "set_user_permissions": 0, "share": 1, "submit": 1, "write": 1 @@ -776,28 +233,19 @@ "delete": 1, "email": 1, "export": 1, - "if_owner": 0, "import": 1, - "permlevel": 0, "print": 1, "read": 1, "report": 1, "role": "HR Manager", - "set_user_permissions": 0, "share": 1, "submit": 1, "write": 1 } ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, "search_fields": "employee,employee_name,leave_type,total_leaves_allocated", "show_name_in_global_search": 1, "sort_field": "modified", "sort_order": "DESC", - "timeline_field": "employee", - "track_changes": 0, - "track_seen": 0, - "track_views": 0 + "timeline_field": "employee" } \ No newline at end of file diff --git a/erpnext/hr/doctype/leave_application/leave_application.json b/erpnext/hr/doctype/leave_application/leave_application.json index 60efb33ca4..f8344b53e0 100644 --- a/erpnext/hr/doctype/leave_application/leave_application.json +++ b/erpnext/hr/doctype/leave_application/leave_application.json @@ -1,979 +1,274 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, "allow_import": 1, - "allow_rename": 0, "autoname": "naming_series:", - "beta": 0, "creation": "2013-02-20 11:18:11", - "custom": 0, "description": "Apply / Approve Leaves", - "docstatus": 0, "doctype": "DocType", "document_type": "Document", - "editable_grid": 0, + "engine": "InnoDB", + "field_order": [ + "naming_series", + "employee", + "employee_name", + "column_break_4", + "leave_type", + "department", + "leave_balance", + "section_break_5", + "from_date", + "to_date", + "half_day", + "half_day_date", + "total_leave_days", + "column_break1", + "description", + "section_break_7", + "leave_approver", + "leave_approver_name", + "column_break_18", + "status", + "leave_details", + "sb10", + "posting_date", + "company", + "follow_via_email", + "column_break_17", + "salary_slip", + "letter_head", + "color", + "amended_from" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", "fieldname": "naming_series", "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": 0, "label": "Series", - "length": 0, "no_copy": 1, "options": "HR-LAP-.YYYY.-", - "permlevel": 0, - "precision": "", "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, "reqd": 1, - "search_index": 0, - "set_only_once": 1, - "translatable": 0, - "unique": 0 + "set_only_once": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "employee", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, "in_global_search": 1, - "in_list_view": 0, "in_standard_filter": 1, "label": "Employee", - "length": 0, - "no_copy": 0, "options": "Employee", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, "reqd": 1, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "search_index": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "employee_name", "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, "in_global_search": 1, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Employee Name", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "column_break_4", - "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, - "translatable": 0, - "unique": 0 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "leave_type", "fieldtype": "Link", - "hidden": 0, "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, "in_standard_filter": 1, "label": "Leave Type", - "length": 0, - "no_copy": 0, "options": "Leave Type", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, "reqd": 1, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "search_index": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fetch_from": "employee.department", "fieldname": "department", "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": "Department", - "length": 0, - "no_copy": 0, "options": "Department", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "leave_balance", "fieldtype": "Float", - "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": "Leave Balance Before Application", - "length": 0, "no_copy": 1, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "section_break_5", - "fieldtype": "Section 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, - "translatable": 0, - "unique": 0 + "fieldtype": "Section Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "from_date", "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, "in_standard_filter": 1, "label": "From Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, "reqd": 1, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "search_index": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "to_date", "fieldtype": "Date", - "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": "To Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, "reqd": 1, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "search_index": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, + "default": "0", "fieldname": "half_day", "fieldtype": "Check", - "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": "Half Day", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "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 + "label": "Half Day" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "eval:doc.half_day && (doc.from_date != doc.to_date)", "fieldname": "half_day_date", "fieldtype": "Date", - "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": "Half Day Date", - "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, - "translatable": 0, - "unique": 0 + "label": "Half Day Date" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "total_leave_days", "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, "label": "Total Leave Days", - "length": 0, "no_copy": 1, - "permlevel": 0, "precision": "1", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "column_break1", "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, - "print_hide": 0, - "print_hide_if_no_value": 0, "print_width": "50%", - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, "width": "50%" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "description", "fieldtype": "Small Text", - "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": "Reason", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "reqd": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "section_break_7", "fieldtype": "Section 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, - "label": "Approval", - "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, - "translatable": 0, - "unique": 0 + "label": "Approval" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "leave_approver", "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": "Leave Approver", - "length": 0, - "no_copy": 0, - "options": "User", - "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": "User" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "leave_approver_name", "fieldtype": "Data", - "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": "Leave Approver Name", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "column_break_18", - "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, - "translatable": 0, - "unique": 0 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 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\nCancelled", - "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": "Open\nApproved\nRejected\nCancelled" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "sb10", - "fieldtype": "Section 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, - "label": "", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "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 + "fieldtype": "Section Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "default": "Today", "fieldname": "posting_date", "fieldtype": "Date", - "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": "Posting Date", - "length": 0, "no_copy": 1, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "reqd": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "company", "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": "Company", - "length": 0, - "no_copy": 0, "options": "Company", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, "remember_last_selected_value": 1, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "reqd": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, "default": "1", "fieldname": "follow_via_email", "fieldtype": "Check", - "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": "Follow via Email", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 1, - "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 + "print_hide": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "column_break_17", - "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, - "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 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "salary_slip", - "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": "Salary Slip", - "length": 0, - "no_copy": 0, - "options": "Salary Slip", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "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 - }, + "fieldname": "salary_slip", + "fieldtype": "Link", + "label": "Salary Slip", + "options": "Salary Slip", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "letter_head", "fieldtype": "Link", - "hidden": 0, "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Letter Head", - "length": 0, - "no_copy": 0, "options": "Letter Head", - "permlevel": 0, - "print_hide": 1, - "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 + "print_hide": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "color", "fieldtype": "Color", - "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": "Color", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "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 + "print_hide": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "amended_from", "fieldtype": "Link", - "hidden": 0, "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Amended From", - "length": 0, "no_copy": 1, "options": "Leave Application", - "permlevel": 0, "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 + }, + { + "fieldname": "leave_details", + "fieldtype": "Small Text", + "hidden": 1, + "label": "Leave Details" } ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, "icon": "fa fa-calendar", "idx": 1, - "image_view": 0, - "in_create": 0, "is_submittable": 1, - "issingle": 0, - "istable": 0, "max_attachments": 3, - "modified": "2019-06-01 11:30:14.745572", + "modified": "2019-06-14 15:37:45.988552", "modified_by": "Administrator", "module": "HR", "name": "Leave Application", "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, "create": 1, - "delete": 0, "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, "print": 1, "read": 1, "report": 1, "role": "Employee", - "set_user_permissions": 0, "share": 1, - "submit": 0, "write": 1 }, { @@ -983,9 +278,6 @@ "delete": 1, "email": 1, "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, "print": 1, "read": 1, "report": 1, @@ -996,23 +288,9 @@ "write": 1 }, { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 0, - "export": 0, - "if_owner": 0, - "import": 0, "permlevel": 1, - "print": 0, "read": 1, - "report": 0, - "role": "All", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 + "role": "All" }, { "amend": 1, @@ -1020,10 +298,6 @@ "create": 1, "delete": 1, "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, "print": 1, "read": 1, "report": 1, @@ -1036,71 +310,34 @@ { "amend": 1, "cancel": 1, - "create": 0, "delete": 1, "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, "print": 1, "read": 1, "report": 1, "role": "Leave Approver", - "set_user_permissions": 0, "share": 1, "submit": 1, "write": 1 }, { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 0, - "export": 0, - "if_owner": 0, - "import": 0, "permlevel": 1, - "print": 0, "read": 1, "report": 1, "role": "HR User", - "set_user_permissions": 0, - "share": 0, - "submit": 0, "write": 1 }, { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 0, - "export": 0, - "if_owner": 0, - "import": 0, "permlevel": 1, - "print": 0, "read": 1, "report": 1, "role": "Leave Approver", - "set_user_permissions": 0, - "share": 0, - "submit": 0, "write": 1 } ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, "search_fields": "employee,employee_name,leave_type,from_date,to_date,total_leave_days", - "show_name_in_global_search": 0, "sort_field": "modified", "sort_order": "DESC", "timeline_field": "employee", - "title_field": "employee_name", - "track_changes": 0, - "track_seen": 0, - "track_views": 0 + "title_field": "employee_name" } \ No newline at end of file From 1db0fc91a5d6b3d07ddff51877dc80297753327f Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Fri, 14 Jun 2019 15:48:31 +0530 Subject: [PATCH 75/98] fix: check generated ledger entries to avoid overlap --- erpnext/patches/v12_0/generate_leave_ledger_entries.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/patches/v12_0/generate_leave_ledger_entries.py b/erpnext/patches/v12_0/generate_leave_ledger_entries.py index 07e53c7450..5b71c167b6 100644 --- a/erpnext/patches/v12_0/generate_leave_ledger_entries.py +++ b/erpnext/patches/v12_0/generate_leave_ledger_entries.py @@ -35,7 +35,7 @@ def generate_application_leave_ledger_entries(): leave_applications = get_leaves_application_records() for application in leave_applications: - if not frappe.db.exists("Leave Ledger Entry", {'transaction_type': 'Leave Application', 'transaction_name': record.name}): + if not frappe.db.exists("Leave Ledger Entry", {'transaction_type': 'Leave Application', 'transaction_name': application.name}): application.update(dict(doctype="Leave Application")) frappe.get_doc(application).create_leave_ledger_entry() @@ -44,7 +44,7 @@ def generate_encashment_leave_ledger_entries(): leave_encashments = get_leave_encashment_records() for encashment in leave_encashments: - if not frappe.db.exists("Leave Ledger Entry", {'transaction_type': 'Leave Encashment', 'transaction_name': record.name}): + if not frappe.db.exists("Leave Ledger Entry", {'transaction_type': 'Leave Encashment', 'transaction_name': encashment.name}): encashment.update(dict(doctype="Leave Encashment")) frappe.get_doc(encashment).create_leave_ledger_entry() From 12a2b21465303b51110cdae94fe1c8c72d1e041a Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Wed, 19 Jun 2019 13:00:52 +0530 Subject: [PATCH 76/98] fix: track status for pending leaves --- .../hr/doctype/leave_allocation/leave_allocation_list.js | 7 +++---- erpnext/hr/doctype/leave_application/leave_application.py | 1 + erpnext/patches/v12_0/generate_leave_ledger_entries.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation_list.js b/erpnext/hr/doctype/leave_allocation/leave_allocation_list.js index 946b4f8492..3ea0e2403f 100644 --- a/erpnext/hr/doctype/leave_allocation/leave_allocation_list.js +++ b/erpnext/hr/doctype/leave_allocation/leave_allocation_list.js @@ -4,9 +4,8 @@ // render frappe.listview_settings['Leave Allocation'] = { get_indicator: function(doc) { - if(doc.status==="Expired") { - return [__("Expired"), "darkgrey", "status, =, Expired"]; - } + if(doc.status==="Expired") { + return [__("Expired"), "darkgrey", "status, =, Expired"]; + } }, - right_column: "grand_total" }; diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py index f87f7d53ef..d08c9edb1f 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.py +++ b/erpnext/hr/doctype/leave_application/leave_application.py @@ -496,6 +496,7 @@ def get_pending_leaves_for_period(employee, leave_type, from_date, to_date): "leave_type": leave_type, "from_date": ("<=", from_date), "to_date": (">=", to_date), + "status": "Open" }, fieldname=['SUM(total_leave_days)']) or flt(0) def get_remaining_leaves(allocation, leaves_taken, date, expiry): diff --git a/erpnext/patches/v12_0/generate_leave_ledger_entries.py b/erpnext/patches/v12_0/generate_leave_ledger_entries.py index 5b71c167b6..ffb2e3fba5 100644 --- a/erpnext/patches/v12_0/generate_leave_ledger_entries.py +++ b/erpnext/patches/v12_0/generate_leave_ledger_entries.py @@ -89,5 +89,5 @@ def get_leave_encashment_records(): encashment_date FROM `tabLeave Encashment` WHERE - AND docstatus=1 + docstatus=1 """, as_dict=1) \ No newline at end of file From bd999b09089c9f5cea8307cfe18a12d8b0f1952a Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Fri, 21 Jun 2019 00:49:48 +0530 Subject: [PATCH 77/98] fix: minor changes --- .../leave_allocation/leave_allocation.js | 15 +++-------- .../leave_allocation/leave_allocation.py | 15 ++++++++++- .../leave_application/leave_application.js | 3 ++- .../leave_application/leave_application.py | 23 +++++++++-------- .../test_leave_application.py | 2 +- .../leave_ledger_entry.json | 2 +- .../leave_ledger_entry/leave_ledger_entry.py | 25 ++++++++++++++++--- 7 files changed, 55 insertions(+), 30 deletions(-) diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation.js b/erpnext/hr/doctype/leave_allocation/leave_allocation.js index a620d9e081..8f734ac1bc 100755 --- a/erpnext/hr/doctype/leave_allocation/leave_allocation.js +++ b/erpnext/hr/doctype/leave_allocation/leave_allocation.js @@ -94,19 +94,10 @@ frappe.ui.form.on("Leave Allocation", { calculate_total_leaves_allocated: function(frm) { if (cint(frm.doc.carry_forward) == 1 && frm.doc.leave_type && frm.doc.employee) { return frappe.call({ - method: "erpnext.hr.doctype.leave_allocation.leave_allocation.get_carry_forwarded_leaves", - args: { - "employee": frm.doc.employee, - "date": frm.doc.from_date, - "leave_type": frm.doc.leave_type, - "carry_forward": frm.doc.carry_forward - }, + method: "set_total_leaves_allocated", + doc: frm.doc, callback: function(r) { - if (!r.exc && r.message) { - frm.set_value('carry_forwarded_leaves', r.message); - frm.set_value("total_leaves_allocated", - flt(r.message) + flt(frm.doc.new_leaves_allocated)); - } + frm.refresh_fields(); } }) } else if (cint(frm.doc.carry_forward) == 0) { diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation.py b/erpnext/hr/doctype/leave_allocation/leave_allocation.py index 72ea2733b0..df479e72e9 100755 --- a/erpnext/hr/doctype/leave_allocation/leave_allocation.py +++ b/erpnext/hr/doctype/leave_allocation/leave_allocation.py @@ -115,7 +115,7 @@ class LeaveAllocation(Document): args = dict( leaves=self.carry_forwarded_leaves, from_date=self.from_date, - to_date=add_days(self.from_date, expiry_days) 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 ) create_leave_ledger_entry(self, args, submit) @@ -132,11 +132,13 @@ class LeaveAllocation(Document): ''' expires allocation ''' date = self.to_date if current else self.from_date leaves = get_unused_leaves(self.employee, self.leave_type, date) + ref_name = self.name if current else self.get_previous_allocation() if leaves: expiry_date = today() if current else 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, @@ -147,6 +149,17 @@ class LeaveAllocation(Document): if current: frappe.db.set_value("Leave Allocation", self.name, "status", "Expired") + def get_previous_allocation(self): + return frappe.db.get_value("Leave Allocation", + filters={ + 'to_date': ("<", self.from_date), + 'leave_type': self.leave_type, + 'employee': self.employee, + 'docstatus': 1 + }, + order_by='to_date DESC', + fieldname=['name']) + def get_leave_allocation_for_period(employee, leave_type, from_date, to_date): leave_allocated = 0 leave_allocations = frappe.db.sql(""" diff --git a/erpnext/hr/doctype/leave_application/leave_application.js b/erpnext/hr/doctype/leave_application/leave_application.js index b81e615b72..44a60b0259 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.js +++ b/erpnext/hr/doctype/leave_application/leave_application.js @@ -49,7 +49,7 @@ frappe.ui.form.on("Leave Application", { async: false, args: { employee: frm.doc.employee, - date: frm.doc.posting_date + date: frm.doc.from_date? frm.doc.from_date:frm.doc.posting_date }, callback: function(r) { if (!r.exc && r.message['leave_allocation']) { @@ -124,6 +124,7 @@ frappe.ui.form.on("Leave Application", { }, from_date: function(frm) { + frm.trigger("make_dashboard"); frm.trigger("half_day_datepicker"); frm.trigger("calculate_total_days"); }, diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py index d08c9edb1f..7aa3e95862 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.py +++ b/erpnext/hr/doctype/leave_application/leave_application.py @@ -417,9 +417,9 @@ def get_leave_details(employee, date): allocation = allocation_records.get(d, frappe._dict()) remaining_leaves = get_leave_balance_on(employee, d, date, to_date = allocation.to_date, consider_all_leaves_in_the_allocation_period=True) - date = allocation.to_date - leaves_taken = get_leaves_for_period(employee, d, allocation.from_date, date) * -1 - leaves_pending = get_pending_leaves_for_period(employee, d, allocation.from_date, date) + end_date = allocation.to_date + leaves_taken = get_leaves_for_period(employee, d, allocation.from_date, end_date) * -1 + leaves_pending = get_pending_leaves_for_period(employee, d, allocation.from_date, end_date) leave_allocation[d] = { "total_leaves": allocation.total_leaves_allocated, @@ -435,14 +435,17 @@ def get_leave_details(employee, date): return ret @frappe.whitelist() -def get_leave_balance_on(employee, leave_type, date, to_date=nowdate(), allocation_records=None, - consider_all_leaves_in_the_allocation_period=False): - ''' Returns leave balance till date and fetches expiry date based on to_date - to calculate minimum remaining leave balance ''' - - if not allocation_records: - allocation_records = get_leave_allocation_records(employee, date, leave_type) +def get_leave_balance_on(employee, leave_type, date, to_date=nowdate(), consider_all_leaves_in_the_allocation_period=False): + ''' + Returns leave balance on date + :param employee: employee name + :param leave_type: leave type + :param date: date to check balance on + :param to_date: future date to check for allocation expiry + :param consider_all_leaves_in_the_allocation_period: consider all leaves taken till the allocation end date + ''' + allocation_records = get_leave_allocation_records(employee, date, leave_type) allocation = allocation_records.get(leave_type, frappe._dict()) end_date = allocation.to_date if consider_all_leaves_in_the_allocation_period else date diff --git a/erpnext/hr/doctype/leave_application/test_leave_application.py b/erpnext/hr/doctype/leave_application/test_leave_application.py index 54216ee3c7..ca6b99ca12 100644 --- a/erpnext/hr/doctype/leave_application/test_leave_application.py +++ b/erpnext/hr/doctype/leave_application/test_leave_application.py @@ -557,7 +557,7 @@ def create_carry_forwarded_allocation(employee, leave_type): leave_type="_Test_CF_leave_expiry", employee=employee.name, employee_name=employee.employee_name, - from_date=add_days(nowdate(), -85), + from_date=add_days(nowdate(), -84), to_date=add_days(nowdate(), 100), carry_forward=1) leave_allocation.submit() diff --git a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.json b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.json index 0166e43c0f..c11422211c 100644 --- a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.json +++ b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.json @@ -101,7 +101,7 @@ ], "in_create": 1, "is_submittable": 1, - "modified": "2019-06-06 20:33:37.531161", + "modified": "2019-06-21 00:37:07.782810", "modified_by": "Administrator", "module": "HR", "name": "Leave Ledger Entry", diff --git a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py index 6142dcf648..a73f10adb8 100644 --- a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py +++ b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py @@ -6,7 +6,7 @@ from __future__ import unicode_literals import frappe from frappe.model.document import Document from frappe import _ -from frappe.utils import add_days, today, flt +from frappe.utils import add_days, today, flt, DATE_FORMAT class LeaveLedgerEntry(Document): def on_cancel(self): @@ -49,14 +49,31 @@ def create_leave_ledger_entry(ref_doc, args, submit=True): def delete_ledger_entry(ledger): ''' Delete ledger entry on cancel of leave application/allocation/encashment ''' - if ledger.transaction_type == "Leave Allocation": validate_leave_allocation_against_leave_application(ledger) + expired_entry = get_previous_expiry_ledger_entry(ledger) frappe.db.sql("""DELETE FROM `tabLeave Ledger Entry` WHERE - `transaction_name`=%s""", (ledger.transaction_name)) + `transaction_name`=%s + OR `name`=%s""", (ledger.transaction_name, expired_entry)) + +def get_previous_expiry_ledger_entry(ledger): + ''' Returns the expiry ledger entry having same creation date as the ledger entry to be cancelled ''' + creation_date = frappe.db.get_value("Leave Ledger Entry", filters={ + 'transaction_name': ledger.transaction_name, + 'is_expired': 0 + }, fieldname=['creation']).strftime(DATE_FORMAT) + + return frappe.db.get_value("Leave Ledger Entry", filters={ + 'creation': ('like', creation_date+"%"), + 'employee': ledger.employee, + 'leave_type': ledger.leave_type, + 'is_expired': 1, + 'docstatus': 1, + 'is_carry_forward': 0 + }, fieldname=['name']) def process_expired_allocation(): ''' Check if a carry forwarded allocation has expired and create a expiry ledger entry ''' @@ -70,7 +87,7 @@ def process_expired_allocation(): leave_type = [record[0] for record in leave_type_records] expired_allocation = frappe.get_all("Leave Ledger Entry", filters={ - 'to_date': today(), + 'to_date': add_days(today(), -1), 'transaction_type': 'Leave Allocation', 'is_carry_forward': 1, 'leave_type': ('in', leave_type) From 0d4db95d9923e433bf51dfca1d6aeb3263cbe1ee Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Wed, 3 Jul 2019 11:35:51 +0530 Subject: [PATCH 78/98] fix: skip expired leaves on allocation end date --- .../leave_application/leave_application.py | 51 ++++++++++++------- 1 file changed, 33 insertions(+), 18 deletions(-) diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py index 7aa3e95862..0dde8e157e 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.py +++ b/erpnext/hr/doctype/leave_application/leave_application.py @@ -521,8 +521,39 @@ def get_remaining_leaves(allocation, leaves_taken, date, expiry): return _get_remaining_leaves(allocation.total_leaves_allocated, allocation.to_date) def get_leaves_for_period(employee, leave_type, from_date, to_date): - leave_entries = frappe.db.sql(""" - select employee, leave_type, from_date, to_date, leaves, transaction_type + leave_entries = get_leave_entries(employee, leave_type, from_date, to_date) + leave_days = 0 + + for leave_entry in leave_entries: + inclusive_period = leave_entry.from_date >= getdate(from_date) and leave_entry.to_date <= getdate(to_date) + + if inclusive_period and leave_entry.transaction_type == 'Leave Encashment': + leave_days += leave_entry.leaves + + elif inclusive_period and leave_entry.transaction_type == 'Leave Allocation' \ + and not skip_expiry_leaves(leave_entry, to_date): + leave_days += leave_entry.leaves + + else: + if leave_entry.from_date < getdate(from_date): + leave_entry.from_date = from_date + if leave_entry.to_date > getdate(to_date): + leave_entry.to_date = to_date + + leave_days += get_number_of_leave_days(employee, leave_type, + leave_entry.from_date, leave_entry.to_date) * -1 + + return leave_days + +def skip_expiry_leaves(leave_entry, date): + ''' Checks whether the expired leaves coincide with the to_date of leave balance check ''' + end_date = frappe.db.get_value("Leave Allocation", {'name': leave_entry.transaction_name}, ['to_date']) + return True if end_date == date and not leave_entry.is_carry_forward else False + +def get_leave_entries(employee, leave_type, from_date, to_date): + ''' Returns leave entries between from_date and to_date ''' + return frappe.db.sql(""" + select employee, leave_type, from_date, to_date, leaves, transaction_type, is_carry_forward from `tabLeave Ledger Entry` where employee=%(employee)s and leave_type=%(leave_type)s and docstatus=1 @@ -536,22 +567,6 @@ def get_leaves_for_period(employee, leave_type, from_date, to_date): "employee": employee, "leave_type": leave_type }, as_dict=1) - leave_days = 0 - - for leave_entry in leave_entries: - if leave_entry.from_date >= getdate(from_date) and leave_entry.to_date <= getdate(to_date) \ - and leave_entry.transaction_type in ('Leave Encashment', 'Leave Allocation'): - leave_days += leave_entry.leaves - else: - if leave_entry.from_date < getdate(from_date): - leave_entry.from_date = from_date - if leave_entry.to_date > getdate(to_date): - leave_entry.to_date = to_date - - leave_days += get_number_of_leave_days(employee, leave_type, - leave_entry.from_date, leave_entry.to_date) * -1 - - return leave_days @frappe.whitelist() def get_holidays(employee, from_date, to_date): From 80fb0bf520ea43fa1000601ae27a299e05b31db2 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Wed, 3 Jul 2019 13:06:46 +0530 Subject: [PATCH 79/98] fix: add employee leave balance report to the dashboard --- .../doctype/leave_allocation/leave_allocation.js | 8 -------- .../leave_allocation/leave_allocation_dashboard.py | 5 +++++ .../doctype/leave_application/leave_application.js | 10 ---------- .../doctype/leave_application/leave_application.py | 2 +- .../leave_application_dashboard.py | 14 ++++++++++++++ erpnext/patches.txt | 2 +- 6 files changed, 21 insertions(+), 20 deletions(-) create mode 100644 erpnext/hr/doctype/leave_application/leave_application_dashboard.py diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation.js b/erpnext/hr/doctype/leave_allocation/leave_allocation.js index 8f734ac1bc..2a26a85fa9 100755 --- a/erpnext/hr/doctype/leave_allocation/leave_allocation.js +++ b/erpnext/hr/doctype/leave_allocation/leave_allocation.js @@ -30,14 +30,6 @@ frappe.ui.form.on("Leave Allocation", { frm.trigger("expire_allocation"); }); } - - // opens leave balance report for employee - frm.add_custom_button(__('Leave Balance'), function() { - frappe.route_options = { - employee: frm.doc.employee, - }; - frappe.set_route("query-report", "Employee Leave Balance"); - }); } }, diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation_dashboard.py b/erpnext/hr/doctype/leave_allocation/leave_allocation_dashboard.py index 72a1b7c194..7456aebb45 100644 --- a/erpnext/hr/doctype/leave_allocation/leave_allocation_dashboard.py +++ b/erpnext/hr/doctype/leave_allocation/leave_allocation_dashboard.py @@ -12,4 +12,9 @@ def get_data(): 'items': ['Leave Encashment'] } ], + 'reports': [ + { + 'items': ['Employee Leave Balance'] + } + ] } \ No newline at end of file diff --git a/erpnext/hr/doctype/leave_application/leave_application.js b/erpnext/hr/doctype/leave_application/leave_application.js index 44a60b0259..a755b57608 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.js +++ b/erpnext/hr/doctype/leave_application/leave_application.js @@ -85,16 +85,6 @@ frappe.ui.form.on("Leave Application", { frm.set_value('employee', perm['Employee'].map(perm_doc => perm_doc.doc)[0]); } } - - if (frm.doc.docstatus === 1) { - frm.add_custom_button(__('Leave Balance'), function() { - frappe.route_options = { - employee: frm.doc.employee, - group_by: "" - }; - frappe.set_route("query-report", "Employee Leave Balance"); - }); - } }, employee: function(frm) { diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py index 74862fb011..86d9130aab 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.py +++ b/erpnext/hr/doctype/leave_application/leave_application.py @@ -438,7 +438,7 @@ def get_leave_details(employee, date): @frappe.whitelist() def get_leave_balance_on(employee, leave_type, date, to_date=nowdate(), consider_all_leaves_in_the_allocation_period=False): ''' - Returns leave balance on date + Returns leave balance till date :param employee: employee name :param leave_type: leave type :param date: date to check balance on diff --git a/erpnext/hr/doctype/leave_application/leave_application_dashboard.py b/erpnext/hr/doctype/leave_application/leave_application_dashboard.py new file mode 100644 index 0000000000..8075b7b5c5 --- /dev/null +++ b/erpnext/hr/doctype/leave_application/leave_application_dashboard.py @@ -0,0 +1,14 @@ +from __future__ import unicode_literals + +from frappe import _ + + +def get_data(): + return { + 'reports': [ + { + 'label': _('Reports'), + 'items': ['Employee Leave Balance'] + } + ] + } \ No newline at end of file diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 3b4cfd835d..b3aeab68f2 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -606,7 +606,6 @@ execute:frappe.delete_doc_if_exists("Page", "support-analytics") erpnext.patches.v12_0.make_custom_fields_for_bank_remittance #14-06-2019 execute:frappe.delete_doc_if_exists("Page", "support-analytics") erpnext.patches.v12_0.make_item_manufacturer -erpnext.patches.v12_0.generate_leave_ledger_entries erpnext.patches.v11_1.move_customer_lead_to_dynamic_column erpnext.patches.v11_1.set_default_action_for_quality_inspection erpnext.patches.v11_1.delete_bom_browser @@ -617,3 +616,4 @@ erpnext.patches.v11_1.set_missing_opportunity_from erpnext.patches.v12_0.set_quotation_status erpnext.patches.v12_0.set_priority_for_support erpnext.patches.v12_0.delete_priority_property_setter +erpnext.patches.v12_0.generate_leave_ledger_entries # From 29e9f14f95e967141841c3519946d5b3b7dae7c2 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Mon, 22 Jul 2019 13:54:39 +0530 Subject: [PATCH 80/98] fix: minor changes --- .../hr/doctype/leave_application/leave_application.py | 9 +++++---- .../hr/doctype/leave_ledger_entry/leave_ledger_entry.py | 4 ++++ erpnext/hr/doctype/leave_type/leave_type.py | 6 +++++- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py index 86d9130aab..8f02ec0f92 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.py +++ b/erpnext/hr/doctype/leave_application/leave_application.py @@ -357,7 +357,7 @@ class LeaveApplication(Document): lwp = frappe.db.get_value("Leave Type", self.leave_type, "is_lwp") if expiry_date: - self.create_ledger_entry_for_intermediate_allocation_expiry(expiry_date, submit) + self.create_ledger_entry_for_intermediate_allocation_expiry(expiry_date, submit, lwp) else: args = dict( leaves=self.total_leave_days * -1, @@ -367,16 +367,17 @@ class LeaveApplication(Document): ) create_leave_ledger_entry(self, args, submit) - def create_ledger_entry_for_intermediate_allocation_expiry(self, expiry_date, submit): + def create_ledger_entry_for_intermediate_allocation_expiry(self, expiry_date, submit, lwp): ''' splits leave application into two ledger entries to consider expiry of allocation ''' args = dict( from_date=self.from_date, to_date=expiry_date, - leaves=(date_diff(expiry_date, self.from_date) + 1) * -1 + leaves=(date_diff(expiry_date, self.from_date) + 1) * -1, + is_lwp=lwp ) create_leave_ledger_entry(self, args, submit) - if expiry_date != self.to_date: + if getdate(expiry_date) != getdate(self.to_date): start_date = add_days(expiry_date, 1) args.update(dict( from_date=start_date, diff --git a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py index a73f10adb8..99a9d0d933 100644 --- a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py +++ b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py @@ -9,6 +9,10 @@ from frappe import _ from frappe.utils import add_days, today, flt, DATE_FORMAT class LeaveLedgerEntry(Document): + def validate(self): + if self.from_date > self.to_date: + frappe.throw(_("To date needs to be before from date")) + def on_cancel(self): # allow cancellation of expiry leaves if not self.is_expired: diff --git a/erpnext/hr/doctype/leave_type/leave_type.py b/erpnext/hr/doctype/leave_type/leave_type.py index 5b13edb684..cbc6781783 100644 --- a/erpnext/hr/doctype/leave_type/leave_type.py +++ b/erpnext/hr/doctype/leave_type/leave_type.py @@ -10,4 +10,8 @@ from frappe import _ from frappe.model.document import Document class LeaveType(Document): - pass \ No newline at end of file + def validate(self): + if self.is_lwp: + leave_allocation = frappe.get_doc("Leave Allocation", {"leave_type": self.name}, ['name']) + if leave_allocation: + frappe.throw(_("""Leave application is linked with leave allocations {0}. Leave application cannot be set as leave without pay""").format(", ".join(leave_allocation))) #nosec \ No newline at end of file From f6cf58fa8c49443b47912639caaa10eb0b065601 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Mon, 22 Jul 2019 15:32:41 +0530 Subject: [PATCH 81/98] fix: change get all to sql list --- .../leave_ledger_entry/leave_ledger_entry.py | 18 ++++++++++-------- erpnext/hr/doctype/leave_type/leave_type.py | 2 +- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py index 99a9d0d933..0520b867f2 100644 --- a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py +++ b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py @@ -20,14 +20,16 @@ class LeaveLedgerEntry(Document): def validate_leave_allocation_against_leave_application(ledger): ''' Checks that leave allocation has no leave application against it ''' - leave_application_records = frappe.get_all("Leave Ledger Entry", - filters={ - 'employee': ledger.employee, - 'leave_type': ledger.leave_type, - 'transaction_type': 'Leave Application', - 'from_date': (">=", ledger.from_date), - 'to_date': ('<=', ledger.to_date) - }, fields=['transaction_name']) + leave_application_records = frappe.db.sql_list(""" + SELECT transaction_name + FROM `tabLeave Application` + WHERE + employee=%s, + leave_type=%s, + transaction_type='Leave Application', + from_date>=%s, + to_date<=%s + """, (ledger.employee, ledger.leave_type, ledger.from_date, ledger.to_date)) if leave_application_records: frappe.throw(_("Leave allocation %s is linked with leave application %s" diff --git a/erpnext/hr/doctype/leave_type/leave_type.py b/erpnext/hr/doctype/leave_type/leave_type.py index cbc6781783..75336e0c7e 100644 --- a/erpnext/hr/doctype/leave_type/leave_type.py +++ b/erpnext/hr/doctype/leave_type/leave_type.py @@ -12,6 +12,6 @@ from frappe.model.document import Document class LeaveType(Document): def validate(self): if self.is_lwp: - leave_allocation = frappe.get_doc("Leave Allocation", {"leave_type": self.name}, ['name']) + leave_allocation = frappe.db.sql_list("""select name from `tabLeave Allocation` where leave_type=%s""", (self.name)) if leave_allocation: frappe.throw(_("""Leave application is linked with leave allocations {0}. Leave application cannot be set as leave without pay""").format(", ".join(leave_allocation))) #nosec \ No newline at end of file From 08c02287dd3a396d61dd3573f10efbcfe300fca1 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Mon, 22 Jul 2019 17:47:03 +0530 Subject: [PATCH 82/98] fix: expiry allocaton after creating all the transactions --- .../v12_0/generate_leave_ledger_entries.py | 146 +++++++++--------- 1 file changed, 77 insertions(+), 69 deletions(-) diff --git a/erpnext/patches/v12_0/generate_leave_ledger_entries.py b/erpnext/patches/v12_0/generate_leave_ledger_entries.py index ffb2e3fba5..8a92bb3a3d 100644 --- a/erpnext/patches/v12_0/generate_leave_ledger_entries.py +++ b/erpnext/patches/v12_0/generate_leave_ledger_entries.py @@ -6,88 +6,96 @@ import frappe from frappe.utils import getdate def execute(): - """ Generates leave ledger entries for leave allocation/application/encashment - for last allocation """ - frappe.reload_doc("HR", "doctype", "Leave Ledger Entry") - frappe.reload_doc("HR", "doctype", "Leave Encashment") - if frappe.db.a_row_exists("Leave Ledger Entry"): - return + """ Generates leave ledger entries for leave allocation/application/encashment + for last allocation """ + frappe.reload_doc("HR", "doctype", "Leave Ledger Entry") + frappe.reload_doc("HR", "doctype", "Leave Encashment") + if frappe.db.a_row_exists("Leave Ledger Entry"): + return - generate_allocation_ledger_entries() - generate_application_leave_ledger_entries() - generate_encashment_leave_ledger_entries() + generate_allocation_ledger_entries() + generate_application_leave_ledger_entries() + generate_encashment_leave_ledger_entries() + generate_expiry_allocation_ledger_entries() def generate_allocation_ledger_entries(): - ''' fix ledger entries for missing leave allocation transaction ''' - allocation_list = get_allocation_records() - - for allocation in allocation_list: - if not frappe.db.exists("Leave Ledger Entry", {'transaction_type': 'Leave Allocation', 'transaction_name': allocation.name}): - allocation.update(dict(doctype="Leave Allocation")) - allocation_obj = frappe.get_doc(allocation) - allocation_obj.create_leave_ledger_entry() - if allocation.to_date <= getdate(): - allocation_obj.expire_allocation() + ''' fix ledger entries for missing leave allocation transaction ''' + allocation_list = get_allocation_records() + for allocation in allocation_list: + if not frappe.db.exists("Leave Ledger Entry", {'transaction_type': 'Leave Allocation', 'transaction_name': allocation.name}): + allocation.update(dict(doctype="Leave Allocation")) + allocation_obj = frappe.get_doc(allocation) + allocation_obj.create_leave_ledger_entry() def generate_application_leave_ledger_entries(): - ''' fix ledger entries for missing leave application transaction ''' - leave_applications = get_leaves_application_records() + ''' fix ledger entries for missing leave application transaction ''' + leave_applications = get_leaves_application_records() - for application in leave_applications: - if not frappe.db.exists("Leave Ledger Entry", {'transaction_type': 'Leave Application', 'transaction_name': application.name}): - application.update(dict(doctype="Leave Application")) - frappe.get_doc(application).create_leave_ledger_entry() + for application in leave_applications: + if not frappe.db.exists("Leave Ledger Entry", {'transaction_type': 'Leave Application', 'transaction_name': application.name}): + application.update(dict(doctype="Leave Application")) + frappe.get_doc(application).create_leave_ledger_entry() def generate_encashment_leave_ledger_entries(): - ''' fix ledger entries for missing leave encashment transaction ''' - leave_encashments = get_leave_encashment_records() + ''' fix ledger entries for missing leave encashment transaction ''' + leave_encashments = get_leave_encashment_records() - for encashment in leave_encashments: - if not frappe.db.exists("Leave Ledger Entry", {'transaction_type': 'Leave Encashment', 'transaction_name': encashment.name}): - encashment.update(dict(doctype="Leave Encashment")) - frappe.get_doc(encashment).create_leave_ledger_entry() + for encashment in leave_encashments: + if not frappe.db.exists("Leave Ledger Entry", {'transaction_type': 'Leave Encashment', 'transaction_name': encashment.name}): + encashment.update(dict(doctype="Leave Encashment")) + frappe.get_doc(encashment).create_leave_ledger_entry() + +def generate_expiry_allocation_ledger_entries(): + ''' fix ledger entries for missing leave allocation transaction ''' + allocation_list = get_allocation_records() + + for allocation in allocation_list: + 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_obj = frappe.get_doc(allocation) + allocation_obj.expire_allocation() def get_allocation_records(): - return frappe.db.sql(""" - SELECT - DISTINCT name, - employee, - leave_type, - new_leaves_allocated, - carry_forwarded_leaves, - from_date, - to_date, - carry_forward - FROM `tabLeave Allocation` - WHERE - docstatus=1 - ORDER BY to_date ASC - """, as_dict=1) + return frappe.db.sql(""" + SELECT + name, + employee, + leave_type, + new_leaves_allocated, + carry_forwarded_leaves, + from_date, + to_date, + carry_forward + FROM `tabLeave Allocation` + WHERE + docstatus=1 + ORDER BY to_date ASC + """, as_dict=1) def get_leaves_application_records(): - return frappe.db.sql(""" - SELECT - DISTINCT name, - employee, - leave_type, - total_leave_days, - from_date, - to_date - FROM `tabLeave Application` - WHERE - docstatus=1 - """, as_dict=1) + return frappe.db.sql(""" + SELECT + name, + employee, + leave_type, + total_leave_days, + from_date, + to_date + FROM `tabLeave Application` + WHERE + docstatus=1 + """, as_dict=1) def get_leave_encashment_records(): - return frappe.db.sql(""" - SELECT - DISTINCT name, - employee, - leave_type, - encashable_days, - encashment_date - FROM `tabLeave Encashment` - WHERE - docstatus=1 - """, as_dict=1) \ No newline at end of file + return frappe.db.sql(""" + SELECT + name, + employee, + leave_type, + encashable_days, + encashment_date + FROM `tabLeave Encashment` + WHERE + docstatus=1 + """, as_dict=1) \ No newline at end of file From fd1d4c2927d6d397965ad12b9792fb609064e6ea Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Mon, 22 Jul 2019 18:05:27 +0530 Subject: [PATCH 83/98] chore: split expire allocation --- .../leave_allocation/leave_allocation.js | 7 ++-- .../leave_allocation/leave_allocation.json | 22 ++++++------- .../leave_allocation/leave_allocation.py | 32 +++++++++++++++---- .../leave_allocation/leave_allocation_list.js | 2 +- .../v12_0/generate_leave_ledger_entries.py | 2 +- 5 files changed, 40 insertions(+), 25 deletions(-) diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation.js b/erpnext/hr/doctype/leave_allocation/leave_allocation.js index 2a26a85fa9..7c3e1e44fa 100755 --- a/erpnext/hr/doctype/leave_allocation/leave_allocation.js +++ b/erpnext/hr/doctype/leave_allocation/leave_allocation.js @@ -22,7 +22,7 @@ frappe.ui.form.on("Leave Allocation", { }, refresh: function(frm) { - if(frm.doc.docstatus === 1 && frm.doc.status === "Active") { + if(frm.doc.docstatus === 1 && frm.doc.expired) { var valid_expiry = moment(frappe.datetime.get_today()).isBetween(frm.doc.from_date, frm.doc.to_date); if(valid_expiry) { // expire current allocation @@ -35,11 +35,8 @@ frappe.ui.form.on("Leave Allocation", { expire_allocation: function(frm) { frappe.call({ - method: 'expire_allocation', + method: 'expire_current_allocation', doc: frm.doc, - args: { - current: true - }, freeze: true, callback: function(r){ if(!r.exc){ diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation.json b/erpnext/hr/doctype/leave_allocation/leave_allocation.json index 41d864d912..1618c67d3d 100644 --- a/erpnext/hr/doctype/leave_allocation/leave_allocation.json +++ b/erpnext/hr/doctype/leave_allocation/leave_allocation.json @@ -24,7 +24,7 @@ "compensatory_request", "leave_period", "leave_policy", - "status", + "expired", "amended_from", "notes", "description" @@ -167,15 +167,6 @@ "options": "Leave Policy", "read_only": 1 }, - { - "fieldname": "status", - "fieldtype": "Select", - "hidden": 1, - "in_standard_filter": 1, - "label": "Status", - "options": "Active\nExpired", - "read_only": 1 - }, { "fieldname": "amended_from", "fieldtype": "Link", @@ -201,12 +192,21 @@ "oldfieldname": "reason", "oldfieldtype": "Small Text", "width": "300px" + }, + { + "default": "0", + "fieldname": "expired", + "fieldtype": "Check", + "hidden": 1, + "in_standard_filter": 1, + "label": "Expired", + "read_only": 1 } ], "icon": "fa fa-ok", "idx": 1, "is_submittable": 1, - "modified": "2019-06-14 15:39:02.898695", + "modified": "2019-07-22 17:50:39.591195", "modified_by": "Administrator", "module": "HR", "name": "Leave Allocation", diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation.py b/erpnext/hr/doctype/leave_allocation/leave_allocation.py index df479e72e9..494043ff14 100755 --- a/erpnext/hr/doctype/leave_allocation/leave_allocation.py +++ b/erpnext/hr/doctype/leave_allocation/leave_allocation.py @@ -42,7 +42,7 @@ class LeaveAllocation(Document): def on_submit(self): self.create_leave_ledger_entry() - self.expire_allocation() + self.expire_previous_allocation() def on_cancel(self): self.create_leave_ledger_entry(submit=False) @@ -128,14 +128,14 @@ class LeaveAllocation(Document): ) create_leave_ledger_entry(self, args, submit) - def expire_allocation(self, current=False): + def expire_current_allocation(self): ''' expires allocation ''' - date = self.to_date if current else self.from_date + date = self.to_date leaves = get_unused_leaves(self.employee, self.leave_type, date) - ref_name = self.name if current else self.get_previous_allocation() + ref_name = self.name if leaves: - expiry_date = today() if current else add_days(self.from_date, -1) + expiry_date = today() args = dict( leaves=flt(leaves) * -1, transaction_name=ref_name, @@ -146,8 +146,26 @@ class LeaveAllocation(Document): ) create_leave_ledger_entry(self, args) - if current: - frappe.db.set_value("Leave Allocation", self.name, "status", "Expired") + 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", diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation_list.js b/erpnext/hr/doctype/leave_allocation/leave_allocation_list.js index 3ea0e2403f..93f7b8356b 100644 --- a/erpnext/hr/doctype/leave_allocation/leave_allocation_list.js +++ b/erpnext/hr/doctype/leave_allocation/leave_allocation_list.js @@ -5,7 +5,7 @@ frappe.listview_settings['Leave Allocation'] = { get_indicator: function(doc) { if(doc.status==="Expired") { - return [__("Expired"), "darkgrey", "status, =, Expired"]; + return [__("Expired"), "darkgrey", "expired, =, 1"]; } }, }; diff --git a/erpnext/patches/v12_0/generate_leave_ledger_entries.py b/erpnext/patches/v12_0/generate_leave_ledger_entries.py index 8a92bb3a3d..f2a798e1f8 100644 --- a/erpnext/patches/v12_0/generate_leave_ledger_entries.py +++ b/erpnext/patches/v12_0/generate_leave_ledger_entries.py @@ -54,7 +54,7 @@ def generate_expiry_allocation_ledger_entries(): 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_obj = frappe.get_doc(allocation) - allocation_obj.expire_allocation() + allocation_obj.expire_previous_allocation() def get_allocation_records(): return frappe.db.sql(""" From bb1be2e2ee4ba2d2f9a688ef88ff51419177ad4d Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Tue, 23 Jul 2019 13:18:40 +0530 Subject: [PATCH 84/98] chore: change fieldname for carried forward leave expiry --- erpnext/hr/doctype/leave_allocation/leave_allocation.py | 2 +- .../hr/doctype/leave_allocation/test_leave_allocation.py | 2 +- .../hr/doctype/leave_application/test_leave_application.py | 4 ++-- erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py | 2 +- erpnext/hr/doctype/leave_period/leave_period.py | 2 +- erpnext/hr/doctype/leave_type/leave_type.json | 6 +++--- erpnext/hr/doctype/leave_type/test_leave_type.py | 2 +- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation.py b/erpnext/hr/doctype/leave_allocation/leave_allocation.py index 494043ff14..67f30b5816 100755 --- a/erpnext/hr/doctype/leave_allocation/leave_allocation.py +++ b/erpnext/hr/doctype/leave_allocation/leave_allocation.py @@ -111,7 +111,7 @@ class LeaveAllocation(Document): def create_leave_ledger_entry(self, submit=True): if self.carry_forwarded_leaves: - expiry_days = frappe.db.get_value("Leave Type", self.leave_type, "carry_forward_leave_expiry") + expiry_days = frappe.db.get_value("Leave Type", self.leave_type, "expire_carried_forward_leaves") args = dict( leaves=self.carry_forwarded_leaves, from_date=self.from_date, diff --git a/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py b/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py index dfae329da1..dfa64db416 100644 --- a/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py +++ b/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py @@ -97,7 +97,7 @@ class TestLeaveAllocation(unittest.TestCase): leave_type = create_leave_type( leave_type_name="_Test_CF_leave_expiry", is_carry_forward=1, - carry_forward_leave_expiry=90) + expire_carried_forward_leaves=90) leave_type.submit() # initial leave allocation diff --git a/erpnext/hr/doctype/leave_application/test_leave_application.py b/erpnext/hr/doctype/leave_application/test_leave_application.py index ca6b99ca12..709a2f57df 100644 --- a/erpnext/hr/doctype/leave_application/test_leave_application.py +++ b/erpnext/hr/doctype/leave_application/test_leave_application.py @@ -415,7 +415,7 @@ class TestLeaveApplication(unittest.TestCase): leave_type = create_leave_type( leave_type_name="_Test_CF_leave_expiry", is_carry_forward=1, - carry_forward_leave_expiry=90) + expire_carried_forward_leaves=90) leave_type.submit() create_carry_forwarded_allocation(employee, leave_type) @@ -516,7 +516,7 @@ class TestLeaveApplication(unittest.TestCase): leave_type = create_leave_type( leave_type_name="_Test_CF_leave_expiry", is_carry_forward=1, - carry_forward_leave_expiry=90) + expire_carried_forward_leaves=90) leave_type.submit() create_carry_forwarded_allocation(employee, leave_type) diff --git a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py index 0520b867f2..135b750bf9 100644 --- a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py +++ b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py @@ -86,7 +86,7 @@ def process_expired_allocation(): # fetch leave type records that has carry forwarded leaves expiry leave_type_records = frappe.db.get_values("Leave Type", filters={ - 'carry_forward_leave_expiry': (">", 0) + 'expire_carried_forward_leaves': (">", 0) }, fieldname=['name']) if leave_type_records: diff --git a/erpnext/hr/doctype/leave_period/leave_period.py b/erpnext/hr/doctype/leave_period/leave_period.py index 91cc9b8340..f37efeba0b 100644 --- a/erpnext/hr/doctype/leave_period/leave_period.py +++ b/erpnext/hr/doctype/leave_period/leave_period.py @@ -95,7 +95,7 @@ def get_existing_allocations(employees, leave_period): 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", "carry_forward_leave_expiry"]) + fields=["name", "is_lwp", "is_earned_leave", "is_compensatory", "is_carry_forward", "expire_carried_forward_leaves"]) for d in leave_types: leave_type_details.setdefault(d.name, d) return leave_type_details diff --git a/erpnext/hr/doctype/leave_type/leave_type.json b/erpnext/hr/doctype/leave_type/leave_type.json index 7899c2f1e6..7a279063ce 100644 --- a/erpnext/hr/doctype/leave_type/leave_type.json +++ b/erpnext/hr/doctype/leave_type/leave_type.json @@ -419,9 +419,9 @@ "collapsible": 0, "columns": 0, "depends_on": "", - "description": "calculated in days", + "description": "Calculated in days", "fetch_if_empty": 0, - "fieldname": "carry_forward_leave_expiry", + "fieldname": "expire_carried_forward_leaves", "fieldtype": "Int", "hidden": 0, "ignore_user_permissions": 0, @@ -728,7 +728,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2019-05-30 15:38:39.334283", + "modified": "2019-07-22 15:38:39.334283", "modified_by": "Administrator", "module": "HR", "name": "Leave Type", diff --git a/erpnext/hr/doctype/leave_type/test_leave_type.py b/erpnext/hr/doctype/leave_type/test_leave_type.py index 1006550de4..d9852be6c9 100644 --- a/erpnext/hr/doctype/leave_type/test_leave_type.py +++ b/erpnext/hr/doctype/leave_type/test_leave_type.py @@ -19,7 +19,7 @@ def create_leave_type(**args): "is_earned_leave": args.is_earned_leave or 0, "is_lwp": args.is_lwp or 0, "is_carry_forward": args.is_carry_forward or 0, - "carry_forward_leave_expiry": args.carry_forward_leave_expiry or 0, + "expire_carried_forward_leaves": args.expire_carried_forward_leaves or 0, "encashment_threshold_days": args.encashment_threshold_days or 5, "earning_component": "Leave Encashment" }) From 5ca3e83a005827316613c57da92b402fd4ce18c4 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Thu, 25 Jul 2019 14:35:39 +0530 Subject: [PATCH 85/98] feat: add ledger link in hr config --- erpnext/config/hr.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/erpnext/config/hr.py b/erpnext/config/hr.py index 0367755595..1f597f080d 100644 --- a/erpnext/config/hr.py +++ b/erpnext/config/hr.py @@ -134,6 +134,12 @@ def get_data(): "name": "Employee Leave Balance", "doctype": "Leave Application" }, + { + "type": "report", + "is_query_report": True, + "name": "Leave Ledger Entry", + "doctype": "Leave Ledger Entry" + }, ] }, { From 5eac8703da409c7c540552eeda87456ffd02bf4c Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Wed, 31 Jul 2019 19:28:21 +0530 Subject: [PATCH 86/98] fix: create reverse expiry for back dated leaves --- .../leave_encashment/leave_encashment.py | 30 ++++++++++++++----- .../leave_encashment/test_leave_encashment.py | 1 - 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/erpnext/hr/doctype/leave_encashment/leave_encashment.py b/erpnext/hr/doctype/leave_encashment/leave_encashment.py index 70cd5780c8..c47ff85717 100644 --- a/erpnext/hr/doctype/leave_encashment/leave_encashment.py +++ b/erpnext/hr/doctype/leave_encashment/leave_encashment.py @@ -11,6 +11,7 @@ from erpnext.hr.utils import set_employee_name from erpnext.hr.doctype.leave_application.leave_application import get_leave_balance_on from erpnext.hr.doctype.salary_structure_assignment.salary_structure_assignment import get_assigned_salary_structure from erpnext.hr.doctype.leave_ledger_entry.leave_ledger_entry import create_leave_ledger_entry +from erpnext.hr.doctype.leave_allocation.leave_allocation import get_unused_leaves class LeaveEncashment(Document): def validate(self): @@ -26,7 +27,7 @@ class LeaveEncashment(Document): def on_submit(self): if not self.leave_allocation: - self.leave_allocation = self.get_leave_allocation() + self.leave_allocation = self.get_leave_allocation().get('name') additional_salary = frappe.new_doc("Additional Salary") additional_salary.company = frappe.get_value("Employee", self.employee, "company") additional_salary.employee = self.employee @@ -61,8 +62,9 @@ class LeaveEncashment(Document): if not frappe.db.get_value("Leave Type", self.leave_type, 'allow_encashment'): frappe.throw(_("Leave Type {0} is not encashable").format(self.leave_type)) - self.leave_balance = get_leave_balance_on(self.employee, self.leave_type, - self.encashment_date or getdate(nowdate()), consider_all_leaves_in_the_allocation_period=True) + 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) 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 @@ -70,15 +72,15 @@ class LeaveEncashment(Document): per_day_encashment = frappe.db.get_value('Salary Structure', salary_structure , 'leave_encashment_amount_per_day') self.encashment_amount = self.encashable_days * per_day_encashment if per_day_encashment > 0 else 0 - self.leave_allocation = self.get_leave_allocation() + self.leave_allocation = allocation.name return True def get_leave_allocation(self): - leave_allocation = frappe.db.sql("""select name from `tabLeave Allocation` where '{0}' + leave_allocation = frappe.db.sql("""select name, to_date, total_leaves_allocated from `tabLeave Allocation` where '{0}' 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)) + and employee= '{2}'""".format(self.encashment_date or getdate(nowdate()), self.leave_type, self.employee), as_dict=1) - return leave_allocation[0][0] if leave_allocation else None + return leave_allocation[0] if leave_allocation else None def create_leave_ledger_entry(self, submit=True): args = frappe._dict( @@ -87,4 +89,16 @@ class LeaveEncashment(Document): to_date=self.encashment_date, is_carry_forward=0 ) - create_leave_ledger_entry(self, args, submit) \ No newline at end of file + create_leave_ledger_entry(self, args, submit) + + # create reverse entry for expired leaves + to_date = self.get_leave_allocation().get('to_date') + if to_date < getdate(nowdate()): + args = frappe._dict( + leaves=self.encashable_days, + from_date=to_date, + to_date=to_date, + is_carry_forward=0 + ) + create_leave_ledger_entry(self, args, submit) + diff --git a/erpnext/hr/doctype/leave_encashment/test_leave_encashment.py b/erpnext/hr/doctype/leave_encashment/test_leave_encashment.py index 2daeffcffe..e5bd170bc4 100644 --- a/erpnext/hr/doctype/leave_encashment/test_leave_encashment.py +++ b/erpnext/hr/doctype/leave_encashment/test_leave_encashment.py @@ -38,7 +38,6 @@ class TestLeaveEncashment(unittest.TestCase): self.leave_period = create_leave_period(add_months(today(), -3), add_months(today(), 3)) self.leave_period.grant_leave_allocation(employee=self.employee) - def test_leave_balance_value_and_amount(self): frappe.db.sql('''delete from `tabLeave Encashment`''') leave_encashment = frappe.get_doc(dict( From 8fb600162e1f93c0adaffcbff567b4821d853635 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Wed, 31 Jul 2019 19:31:26 +0530 Subject: [PATCH 87/98] fix: remove all leaves via scheduler --- .../leave_allocation/leave_allocation.js | 7 +- .../leave_allocation/leave_allocation.py | 96 +++++++------------ .../leave_allocation/test_leave_allocation.py | 4 +- .../leave_application/leave_application.py | 2 +- .../leave_ledger_entry/leave_ledger_entry.py | 91 ++++++++++++------ .../v12_0/generate_leave_ledger_entries.py | 3 +- 6 files changed, 107 insertions(+), 96 deletions(-) diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation.js b/erpnext/hr/doctype/leave_allocation/leave_allocation.js index 7c3e1e44fa..8c910a200a 100755 --- a/erpnext/hr/doctype/leave_allocation/leave_allocation.js +++ b/erpnext/hr/doctype/leave_allocation/leave_allocation.js @@ -35,8 +35,11 @@ frappe.ui.form.on("Leave Allocation", { expire_allocation: function(frm) { frappe.call({ - method: 'expire_current_allocation', - doc: frm.doc, + method: 'erpnext.hr.doctype.leave_ledger_entry.leave_ledger_entry.expire_allocation', + args: { + 'allocation': frm.doc, + 'expiry_date': frappe.datetime.get_today() + }, freeze: true, callback: function(r){ if(!r.exc){ diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation.py b/erpnext/hr/doctype/leave_allocation/leave_allocation.py index 67f30b5816..2374e469a5 100755 --- a/erpnext/hr/doctype/leave_allocation/leave_allocation.py +++ b/erpnext/hr/doctype/leave_allocation/leave_allocation.py @@ -7,7 +7,7 @@ from frappe.utils import flt, date_diff, formatdate, add_days, today from frappe import _ from frappe.model.document import Document 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 BackDatedAllocationError(frappe.ValidationError): pass @@ -42,7 +42,11 @@ class LeaveAllocation(Document): def on_submit(self): 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): self.create_leave_ledger_entry(submit=False) @@ -128,55 +132,17 @@ class LeaveAllocation(Document): ) create_leave_ledger_entry(self, args, submit) - def expire_current_allocation(self): - ''' expires 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", - filters={ - 'to_date': ("<", self.from_date), - 'leave_type': self.leave_type, - 'employee': self.employee, - 'docstatus': 1 - }, - order_by='to_date DESC', - fieldname=['name']) +def get_previous_allocation(from_date, leave_type, employee): + ''' Returns document properties of previous allocation ''' + return frappe.db.get_value("Leave Allocation", + filters={ + 'to_date': ("<", from_date), + 'leave_type': leave_type, + 'employee': employee, + 'docstatus': 1 + }, + order_by='to_date DESC', + fieldname=['name', 'from_date', 'to_date'], as_dict=1) def get_leave_allocation_for_period(employee, leave_type, from_date, to_date): leave_allocated = 0 @@ -203,21 +169,27 @@ def get_leave_allocation_for_period(employee, leave_type, from_date, to_date): @frappe.whitelist() def get_carry_forwarded_leaves(employee, leave_type, date, carry_forward=None): - carry_forwarded_leaves = 0 - if carry_forward: + ''' Returns carry forwarded leaves for the given employee ''' + 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) - 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 -def get_unused_leaves(employee, leave_type, date): - return frappe.db.get_value("Leave Ledger Entry", filters={ - "to_date": ("<=", date), - "employee": employee, - "docstatus": 1, - "leave_type": leave_type, - "is_lwp": 0 - }, fieldname=['SUM(leaves)']) +def get_unused_leaves(employee, leave_type, from_date, to_date): + ''' Returns unused leaves between the given period while skipping leave allocation expiry ''' + leaves = frappe.get_all("Leave Ledger Entry", filters={ + 'employee': employee, + 'leave_type': leave_type, + 'from_date': ('>=', from_date), + 'to_date': ('<=', to_date) + }, 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): if not frappe.db.get_value("Leave Type", leave_type, "is_carry_forward"): diff --git a/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py b/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py index dfa64db416..4f4e0ab1fc 100644 --- a/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py +++ b/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py @@ -3,7 +3,7 @@ import frappe import unittest 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_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): def test_overlapping_allocation(self): @@ -108,6 +108,8 @@ class TestLeaveAllocation(unittest.TestCase): carry_forward=1) leave_allocation.submit() + expire_allocation(leave_allocation) + leave_allocation = create_leave_allocation( leave_type="_Test_CF_leave_expiry", from_date=add_days(nowdate(), -90), diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py index 8f02ec0f92..36a4145467 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.py +++ b/erpnext/hr/doctype/leave_application/leave_application.py @@ -198,7 +198,7 @@ class LeaveApplication(Document): 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, 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"): frappe.msgprint(_("Note: There is not enough leave balance for Leave Type {0}") .format(self.leave_type)) diff --git a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py index 135b750bf9..d19e15cf1a 100644 --- a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py +++ b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py @@ -6,29 +6,31 @@ from __future__ import unicode_literals import frappe from frappe.model.document import Document 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): 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")) def on_cancel(self): # 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")) def validate_leave_allocation_against_leave_application(ledger): ''' Checks that leave allocation has no leave application against it ''' leave_application_records = frappe.db.sql_list(""" SELECT transaction_name - FROM `tabLeave Application` + FROM `tabLeave Ledger Entry` WHERE - employee=%s, - leave_type=%s, - transaction_type='Leave Application', - from_date>=%s, - to_date<=%s + employee=%s + AND leave_type=%s + AND transaction_type='Leave Application' + AND from_date>=%s + AND to_date<=%s """, (ledger.employee, ledger.leave_type, ledger.from_date, ledger.to_date)) if leave_application_records: @@ -48,6 +50,7 @@ def create_leave_ledger_entry(ref_doc, args, submit=True): is_lwp=0 ) ledger.update(args) + if submit: frappe.get_doc(ledger).submit() else: @@ -91,40 +94,70 @@ def process_expired_allocation(): if 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={ 'to_date': add_days(today(), -1), 'transaction_type': 'Leave Allocation', - 'is_carry_forward': 1, + }, + or_filters={ + 'is_carry_forward': 0, 'leave_type': ('in', leave_type) - }, fields=['leaves', 'to_date', 'employee', 'leave_type']) + }) if expired_allocation: 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: - leaves_taken = get_leaves_taken(allocation) - leaves = flt(allocation.leaves) + flt(leaves_taken) + if allocation.is_carry_forward: + expire_carried_forward_allocation(allocation) + else: + expire_allocation(allocation) - if leaves > 0: - args = frappe._dict( - 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): +def get_remaining_leaves(allocation): + ''' Returns remaining leaves from the given allocation ''' return frappe.db.get_value("Leave Ledger Entry", filters={ 'employee': allocation.employee, 'leave_type': allocation.leave_type, - 'from_date': ('>=', allocation.from_date), 'to_date': ('<=', allocation.to_date), - 'transaction_type': 'Leave application' - }, fieldname=['SUM(leaves)']) \ No newline at end of file + }, 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) \ No newline at end of file diff --git a/erpnext/patches/v12_0/generate_leave_ledger_entries.py b/erpnext/patches/v12_0/generate_leave_ledger_entries.py index f2a798e1f8..7dfdcc162b 100644 --- a/erpnext/patches/v12_0/generate_leave_ledger_entries.py +++ b/erpnext/patches/v12_0/generate_leave_ledger_entries.py @@ -48,13 +48,14 @@ def generate_encashment_leave_ledger_entries(): def generate_expiry_allocation_ledger_entries(): ''' 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() for allocation in allocation_list: 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_obj = frappe.get_doc(allocation) - allocation_obj.expire_previous_allocation() + expire_allocation(allocation_obj) def get_allocation_records(): return frappe.db.sql(""" From f281f00d4393c5edff7b1e05a27c85662351dc70 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Mon, 5 Aug 2019 14:47:02 +0530 Subject: [PATCH 88/98] feat: auto leave encashment --- erpnext/hooks.py | 5 +++-- erpnext/hr/doctype/hr_settings/hr_settings.json | 9 ++++++++- .../doctype/leave_encashment/leave_encashment.py | 16 +++++++++++++++- erpnext/hr/utils.py | 15 ++++++++++++++- 4 files changed, 40 insertions(+), 5 deletions(-) diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 4f89533ef8..3a73e23991 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -271,11 +271,12 @@ scheduler_events = { "erpnext.projects.doctype.project.project.update_project_sales_billing", "erpnext.projects.doctype.project.project.send_project_status_email_to_users", "erpnext.quality_management.doctype.quality_review.quality_review.review", - "erpnext.hr.doctype.leave_ledger_entry.leave_ledger_entry.process_expired_allocation" "erpnext.support.doctype.service_level_agreement.service_level_agreement.check_agreement_status", ], "daily_long": [ - "erpnext.manufacturing.doctype.bom_update_tool.bom_update_tool.update_latest_price_in_all_boms" + "erpnext.manufacturing.doctype.bom_update_tool.bom_update_tool.update_latest_price_in_all_boms", + "erpnext.hr.doctype.leave_ledger_entry.leave_ledger_entry.process_expired_allocation", + "erpnext.hr.utils.generate_leave_encashment" ], "monthly_long": [ "erpnext.accounts.deferred_revenue.convert_deferred_revenue_to_income", diff --git a/erpnext/hr/doctype/hr_settings/hr_settings.json b/erpnext/hr/doctype/hr_settings/hr_settings.json index 8dd0acf455..a41c887852 100644 --- a/erpnext/hr/doctype/hr_settings/hr_settings.json +++ b/erpnext/hr/doctype/hr_settings/hr_settings.json @@ -24,6 +24,7 @@ "column_break_18", "leave_approver_mandatory_in_leave_application", "show_leaves_of_all_department_members_in_calendar", + "auto_leave_encashment", "hiring_settings", "check_vacancies" ], @@ -153,12 +154,18 @@ "fieldname": "check_vacancies", "fieldtype": "Check", "label": "Check Vacancies On Job Offer Creation" + }, + { + "default": "0", + "fieldname": "auto_leave_encashment", + "fieldtype": "Check", + "label": "Auto Leave Encashment" } ], "icon": "fa fa-cog", "idx": 1, "issingle": 1, - "modified": "2019-07-01 18:59:55.256878", + "modified": "2019-08-05 13:07:17.993968", "modified_by": "Administrator", "module": "HR", "name": "HR Settings", diff --git a/erpnext/hr/doctype/leave_encashment/leave_encashment.py b/erpnext/hr/doctype/leave_encashment/leave_encashment.py index c47ff85717..d256c7f369 100644 --- a/erpnext/hr/doctype/leave_encashment/leave_encashment.py +++ b/erpnext/hr/doctype/leave_encashment/leave_encashment.py @@ -64,7 +64,7 @@ class LeaveEncashment(Document): 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 - get_unused_leaves(self.employee, self.leave_type, self.encashment_date) 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 @@ -102,3 +102,17 @@ class LeaveEncashment(Document): ) create_leave_ledger_entry(self, args, submit) + +def create_leave_encashment(leave_allocation): + ''' Creates leave encashment for the given allocations ''' + for allocation in leave_allocation: + if not get_assigned_salary_structure(allocation.employee, allocation.to_date): + continue + leave_encashment = frappe.get_doc(dict( + doctype="Leave Encashment", + leave_period=allocation.leave_period, + employee=allocation.employee, + leave_type=allocation.leave_type, + encashment_date=allocation.to_date + )) + leave_encashment.insert(ignore_permissions=True) \ No newline at end of file diff --git a/erpnext/hr/utils.py b/erpnext/hr/utils.py index 1d9db07157..bf09de86e7 100644 --- a/erpnext/hr/utils.py +++ b/erpnext/hr/utils.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals import frappe, erpnext from frappe import _ -from frappe.utils import formatdate, format_datetime, getdate, get_datetime, nowdate, flt, cstr +from frappe.utils import formatdate, format_datetime, getdate, get_datetime, nowdate, flt, cstr, add_days, today from frappe.model.document import Document from frappe.desk.form import assign_to from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee @@ -263,6 +263,19 @@ def get_leave_period(from_date, to_date, company): if leave_period: return leave_period +def generate_leave_encashment(): + ''' Generates a draft leave encashment on allocation expiry ''' + from erpnext.hr.doctype.leave_encashment.leave_encashment import create_leave_encashment + if frappe.db.get_single_value('HR Settings', 'auto_leave_encashment'): + leave_type = frappe.db.sql_list("SELECT name FROM `tabLeave Type` WHERE `allow_encashment`=1") + + leave_allocation = frappe.get_all("Leave Allocation", filters={ + 'to_date': add_days(today(), -1), + 'leave_type': ('in', leave_type) + }, fields=['employee', 'leave_period', 'leave_type', 'to_date', 'total_leaves_allocated', 'new_leaves_allocated']) + + create_leave_encashment(leave_allocation=leave_allocation) + def allocate_earned_leaves(): '''Allocate earned leaves to Employees''' e_leave_types = frappe.get_all("Leave Type", From 6bed870dfa368d7b96d57a3f9ddcf40ac5790d80 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Mon, 5 Aug 2019 14:47:37 +0530 Subject: [PATCH 89/98] feat: pro-rata leave allocation --- .../leave_application/leave_application.py | 2 +- .../hr/doctype/leave_period/leave_period.js | 2 +- .../hr/doctype/leave_period/leave_period.py | 59 +++++++++++-------- 3 files changed, 36 insertions(+), 27 deletions(-) diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py index 36a4145467..2e43721a58 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.py +++ b/erpnext/hr/doctype/leave_application/leave_application.py @@ -515,7 +515,7 @@ def get_remaining_leaves(allocation, leaves_taken, date, expiry): return remaining_leaves - if expiry: + if expiry and allocation.carry_forwarded_leaves: remaining_leaves = _get_remaining_leaves(allocation.carry_forwarded_leaves, expiry) return flt(allocation.new_leaves_allocated) + flt(remaining_leaves) diff --git a/erpnext/hr/doctype/leave_period/leave_period.js b/erpnext/hr/doctype/leave_period/leave_period.js index b8c5f716f5..bad2b8766c 100644 --- a/erpnext/hr/doctype/leave_period/leave_period.js +++ b/erpnext/hr/doctype/leave_period/leave_period.js @@ -68,7 +68,7 @@ frappe.ui.form.on('Leave Period', { }, { "label": "Add unused leaves from previous allocations", - "fieldname": "carry_forward_leaves", + "fieldname": "carry_forward", "fieldtype": "Check" } ], diff --git a/erpnext/hr/doctype/leave_period/leave_period.py b/erpnext/hr/doctype/leave_period/leave_period.py index f37efeba0b..7b0f882035 100644 --- a/erpnext/hr/doctype/leave_period/leave_period.py +++ b/erpnext/hr/doctype/leave_period/leave_period.py @@ -5,7 +5,7 @@ from __future__ import unicode_literals import frappe from frappe import _ -from frappe.utils import getdate, cstr, add_days +from frappe.utils import getdate, cstr, add_days, date_diff, getdate, ceil from frappe.model.document import Document from erpnext.hr.utils import validate_overlap, get_employee_leave_policy from erpnext.hr.doctype.leave_allocation.leave_allocation import get_carry_forwarded_leaves @@ -22,8 +22,8 @@ class LeavePeriod(Document): condition_str = " and " + " and ".join(conditions) if len(conditions) else "" - employees = frappe.db.sql_list("select name from tabEmployee where status='Active' {condition}" - .format(condition=condition_str), tuple(values)) + employees = frappe._dict(frappe.db.sql("select name, date_of_joining from tabEmployee where status='Active' {condition}" + .format(condition=condition_str), tuple(values))) return employees @@ -37,29 +37,29 @@ class LeavePeriod(Document): def grant_leave_allocation(self, grade=None, department=None, designation=None, - employee=None, carry_forward_leaves=0): - employees = self.get_employees({ + employee=None, carry_forward=0): + employee_records = self.get_employees({ "grade": grade, "department": department, "designation": designation, "name": employee }) - if employees: - if len(employees) > 20: + if employee_records: + if len(employee_records) > 20: frappe.enqueue(grant_leave_alloc_for_employees, timeout=600, - employees=employees, leave_period=self, carry_forward_leaves=carry_forward_leaves) + employee_records=employee_records, leave_period=self, carry_forward=carry_forward) else: - grant_leave_alloc_for_employees(employees, self, carry_forward_leaves) + grant_leave_alloc_for_employees(employee_records, self, carry_forward) else: frappe.msgprint(_("No Employee Found")) -def grant_leave_alloc_for_employees(employees, leave_period, carry_forward_leaves=0): +def grant_leave_alloc_for_employees(employee_records, leave_period, carry_forward=0): leave_allocations = [] - existing_allocations_for = get_existing_allocations(employees, leave_period.name) + existing_allocations_for = get_existing_allocations(list(employee_records.keys()), leave_period.name) leave_type_details = get_leave_type_details() count = 0 - for employee in employees: + for employee in employee_records.keys(): if employee in existing_allocations_for: continue count +=1 @@ -68,10 +68,10 @@ def grant_leave_alloc_for_employees(employees, leave_period, carry_forward_leave 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 = create_leave_allocation(employee, leave_policy_detail.leave_type, - leave_policy_detail.annual_allocation, leave_type_details, leave_period, carry_forward_leaves) + leave_policy_detail.annual_allocation, leave_type_details, leave_period, carry_forward, employee_records.get(employee)) leave_allocations.append(leave_allocation) frappe.db.commit() - frappe.publish_progress(count*100/len(set(employees) - set(existing_allocations_for)), title = _("Allocating leaves...")) + frappe.publish_progress(count*100/len(set(employee_records.keys()) - set(existing_allocations_for)), title = _("Allocating leaves...")) if leave_allocations: frappe.msgprint(_("Leaves has been granted sucessfully")) @@ -100,21 +100,30 @@ def get_leave_type_details(): leave_type_details.setdefault(d.name, d) return leave_type_details -def create_leave_allocation(employee, leave_type, new_leaves_allocated, leave_type_details, leave_period, carry_forward_leaves): - allocation = frappe.new_doc("Leave Allocation") - allocation.employee = employee - allocation.leave_type = leave_type - allocation.from_date = leave_period.from_date - allocation.to_date = leave_period.to_date +def create_leave_allocation(employee, leave_type, new_leaves_allocated, leave_type_details, leave_period, carry_forward, date_of_joining): + ''' Creates leave allocation for the given employee in the provided leave period ''' + if carry_forward and not leave_type_details.get(leave_type).is_carry_forward: + carry_forward = 0 + + # Calculate leaves at pro-rata basis for employees joining after the beginning of the given leave period + if getdate(date_of_joining) > getdate(leave_period.from_date): + remaining_period = ((date_diff(leave_period.to_date, date_of_joining) + 1) / (date_diff(leave_period.to_date, leave_period.from_date) + 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 - allocation.new_leaves_allocated = new_leaves_allocated - allocation.leave_period = leave_period.name - if carry_forward_leaves: - if leave_type_details.get(leave_type).is_carry_forward: - allocation.carry_forward = carry_forward_leaves + allocation = frappe.get_doc(dict( + doctype="Leave Allocation", + employee=employee, + leave_type=leave_type, + from_date=leave_period.from_date, + to_date=leave_period.to_date, + new_leaves_allocated=new_leaves_allocated, + leave_period=leave_period.name, + carry_forward=carry_forward + )) allocation.save(ignore_permissions = True) allocation.submit() return allocation.name \ No newline at end of file From 314647572cc0824dddc34d3057f2e321fa3d9176 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Mon, 5 Aug 2019 15:35:19 +0530 Subject: [PATCH 90/98] fix: check for old unexpired allocation --- .../leave_encashment/leave_encashment.py | 2 +- .../leave_ledger_entry/leave_ledger_entry.py | 28 +++++++++++++------ 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/erpnext/hr/doctype/leave_encashment/leave_encashment.py b/erpnext/hr/doctype/leave_encashment/leave_encashment.py index d256c7f369..30529b6241 100644 --- a/erpnext/hr/doctype/leave_encashment/leave_encashment.py +++ b/erpnext/hr/doctype/leave_encashment/leave_encashment.py @@ -64,7 +64,7 @@ class LeaveEncashment(Document): allocation = self.get_leave_allocation() - self.leave_balance = allocation.total_leaves_allocated - get_unused_leaves(self.employee, self.leave_type, self.encashment_date) + self.leave_balance = allocation.total_leaves_allocated - 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') self.encashable_days = encashable_days if encashable_days > 0 else 0 diff --git a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py index d19e15cf1a..8fca24127b 100644 --- a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py +++ b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py @@ -72,8 +72,11 @@ def get_previous_expiry_ledger_entry(ledger): ''' Returns the expiry ledger entry having same creation date as the ledger entry to be cancelled ''' creation_date = frappe.db.get_value("Leave Ledger Entry", filters={ 'transaction_name': ledger.transaction_name, - 'is_expired': 0 - }, fieldname=['creation']).strftime(DATE_FORMAT) + 'is_expired': 0, + 'transaction_type': 'Leave Allocation' + }, fieldname=['creation']) + + creation_date = creation_date.strftime(DATE_FORMAT) if creation_date else '' return frappe.db.get_value("Leave Ledger Entry", filters={ 'creation': ('like', creation_date+"%"), @@ -94,24 +97,31 @@ def process_expired_allocation(): if leave_type_records: leave_type = [record[0] for record in leave_type_records] - expired_allocation = frappe.get_all("Leave Ledger Entry", + + expired_allocation = frappe.db.sql_list("""SELECT name + FROM `tabLeave Ledger Entry` + WHERE + `transaction_type`='Leave Allocation' + AND `is_expired`=1""") + + expire_allocation = frappe.get_all("Leave Ledger Entry", fields=['leaves', 'to_date', 'employee', 'leave_type', 'is_carry_forward', 'transaction_name as name', 'transaction_type'], filters={ - 'to_date': add_days(today(), -1), + 'to_date': ("<", today()), 'transaction_type': 'Leave Allocation', + 'transaction_name': ('not in', expired_allocation) }, or_filters={ 'is_carry_forward': 0, 'leave_type': ('in', leave_type) }) - if expired_allocation: - create_expiry_ledger_entry(expired_allocation) + if expire_allocation: + create_expiry_ledger_entry(expire_allocation) -def create_expiry_ledger_entry(expired_allocation): +def create_expiry_ledger_entry(expire_allocation): ''' Create ledger entry for expired allocation ''' - for allocation in expired_allocation: - + for allocation in expire_allocation: if allocation.is_carry_forward: expire_carried_forward_allocation(allocation) else: From 5cbe6160cade4236894af6bcd41f1e195145042c Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Thu, 8 Aug 2019 17:06:15 +0530 Subject: [PATCH 91/98] feat: consider carry forwarded leaves on creation of encashment --- .../leave_allocation/leave_allocation.js | 8 ++-- .../leave_allocation/leave_allocation.json | 26 ++++++++----- .../leave_allocation/leave_allocation.py | 39 ++++++++++++------- .../leave_encashment/leave_encashment.py | 7 ++-- .../leave_ledger_entry/leave_ledger_entry.py | 9 +++-- .../hr/doctype/leave_period/leave_period.py | 2 +- erpnext/hr/utils.py | 2 +- .../v12_0/generate_leave_ledger_entries.py | 13 ++++++- 8 files changed, 69 insertions(+), 37 deletions(-) diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation.js b/erpnext/hr/doctype/leave_allocation/leave_allocation.js index 8c910a200a..210a73cfe5 100755 --- a/erpnext/hr/doctype/leave_allocation/leave_allocation.js +++ b/erpnext/hr/doctype/leave_allocation/leave_allocation.js @@ -63,14 +63,14 @@ frappe.ui.form.on("Leave Allocation", { frm.trigger("calculate_total_leaves_allocated"); }, - carry_forwarded_leaves: function(frm) { + unused_leaves: function(frm) { 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) { 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) { @@ -93,7 +93,7 @@ frappe.ui.form.on("Leave Allocation", { } }) } 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)); } } diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation.json b/erpnext/hr/doctype/leave_allocation/leave_allocation.json index 1618c67d3d..007497e34a 100644 --- a/erpnext/hr/doctype/leave_allocation/leave_allocation.json +++ b/erpnext/hr/doctype/leave_allocation/leave_allocation.json @@ -17,13 +17,14 @@ "section_break_6", "new_leaves_allocated", "carry_forward", - "carry_forwarded_leaves", + "unused_leaves", "total_leaves_allocated", "total_leaves_encashed", "column_break_10", "compensatory_request", "leave_period", "leave_policy", + "carry_forwarded_leaves_count", "expired", "amended_from", "notes", @@ -119,7 +120,7 @@ }, { "depends_on": "carry_forward", - "fieldname": "carry_forwarded_leaves", + "fieldname": "unused_leaves", "fieldtype": "Float", "label": "Unused leaves", "read_only": 1 @@ -167,6 +168,15 @@ "options": "Leave Policy", "read_only": 1 }, + { + "default": "0", + "fieldname": "expired", + "fieldtype": "Check", + "hidden": 1, + "in_standard_filter": 1, + "label": "Expired", + "read_only": 1 + }, { "fieldname": "amended_from", "fieldtype": "Link", @@ -194,19 +204,17 @@ "width": "300px" }, { - "default": "0", - "fieldname": "expired", - "fieldtype": "Check", - "hidden": 1, - "in_standard_filter": 1, - "label": "Expired", + "depends_on": "carry_forwarded_leaves_count", + "fieldname": "carry_forwarded_leaves_count", + "fieldtype": "Float", + "label": "Carry Forwarded Leaves", "read_only": 1 } ], "icon": "fa fa-ok", "idx": 1, "is_submittable": 1, - "modified": "2019-07-22 17:50:39.591195", + "modified": "2019-08-08 15:08:42.440909", "modified_by": "Administrator", "module": "HR", "name": "Leave Allocation", diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation.py b/erpnext/hr/doctype/leave_allocation/leave_allocation.py index 2374e469a5..5e81999765 100755 --- a/erpnext/hr/doctype/leave_allocation/leave_allocation.py +++ b/erpnext/hr/doctype/leave_allocation/leave_allocation.py @@ -50,6 +50,8 @@ class LeaveAllocation(Document): def on_cancel(self): 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): if date_diff(self.to_date, self.from_date) <= 0: @@ -89,24 +91,33 @@ class LeaveAllocation(Document): BackDatedAllocationError) 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.total_leaves_allocated = flt(self.carry_forwarded_leaves) + flt(self.new_leaves_allocated) - self.maintain_carry_forwarded_leaves() + self.total_leaves_allocated = flt(self.unused_leaves) + flt(self.new_leaves_allocated) + + 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"): - 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): - ''' reduce the carry forwarded leaves to be within the maximum allowed leaves ''' - if not self.carry_forward: - return + ''' Reduce the carry forwarded leaves to be within the maximum allowed leaves ''' + 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: - 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) + 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): # Adding a day to include To Date in the difference 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) 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") args = dict( - leaves=self.carry_forwarded_leaves, + leaves=self.unused_leaves, from_date=self.from_date, to_date=add_days(self.from_date, expiry_days - 1) if expiry_days else self.to_date, is_carry_forward=1 @@ -142,7 +153,7 @@ def get_previous_allocation(from_date, leave_type, employee): 'docstatus': 1 }, 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): leave_allocated = 0 @@ -170,13 +181,13 @@ def get_leave_allocation_for_period(employee, leave_type, from_date, to_date): @frappe.whitelist() def get_carry_forwarded_leaves(employee, leave_type, date, carry_forward=None): ''' 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) if carry_forward and previous_allocation: 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): ''' Returns unused leaves between the given period while skipping leave allocation expiry ''' diff --git a/erpnext/hr/doctype/leave_encashment/leave_encashment.py b/erpnext/hr/doctype/leave_encashment/leave_encashment.py index 30529b6241..42f0179baf 100644 --- a/erpnext/hr/doctype/leave_encashment/leave_encashment.py +++ b/erpnext/hr/doctype/leave_encashment/leave_encashment.py @@ -64,7 +64,8 @@ class LeaveEncashment(Document): 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') self.encashable_days = encashable_days if encashable_days > 0 else 0 @@ -76,9 +77,9 @@ class LeaveEncashment(Document): return True 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}' - 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 diff --git a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py index 8fca24127b..33e4ea0909 100644 --- a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py +++ b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py @@ -104,7 +104,7 @@ def process_expired_allocation(): `transaction_type`='Leave Allocation' 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'], filters={ 'to_date': ("<", today()), @@ -119,9 +119,9 @@ def process_expired_allocation(): if 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 ''' - for allocation in expire_allocation: + for allocation in allocations: if allocation.is_carry_forward: expire_carried_forward_allocation(allocation) else: @@ -138,7 +138,7 @@ def get_remaining_leaves(allocation): @frappe.whitelist() def expire_allocation(allocation, expiry_date=None): - ''' expires allocation ''' + ''' expires non-carry forwarded allocation ''' leaves = get_remaining_leaves(allocation) expiry_date = expiry_date if expiry_date else allocation.to_date @@ -146,6 +146,7 @@ def expire_allocation(allocation, expiry_date=None): args = dict( leaves=flt(leaves) * -1, transaction_name=allocation.name, + transaction_type='Leave Allocation', from_date=expiry_date, to_date=expiry_date, is_carry_forward=0, diff --git a/erpnext/hr/doctype/leave_period/leave_period.py b/erpnext/hr/doctype/leave_period/leave_period.py index 7b0f882035..a7de7185c9 100644 --- a/erpnext/hr/doctype/leave_period/leave_period.py +++ b/erpnext/hr/doctype/leave_period/leave_period.py @@ -22,7 +22,7 @@ class LeavePeriod(Document): 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))) return employees diff --git a/erpnext/hr/utils.py b/erpnext/hr/utils.py index bf09de86e7..4fe9f6b285 100644 --- a/erpnext/hr/utils.py +++ b/erpnext/hr/utils.py @@ -318,7 +318,7 @@ def create_earned_leave_ledger_entry(allocation, earned_leaves, date): ''' Create leave ledger entry based on the earned leave frequency ''' allocation.new_leaves_allocated = earned_leaves allocation.from_date = date - allocation.carry_forwarded_leaves = 0 + allocation.unused_leaves = 0 allocation.create_leave_ledger_entry() def check_frequency_hit(from_date, to_date, frequency): diff --git a/erpnext/patches/v12_0/generate_leave_ledger_entries.py b/erpnext/patches/v12_0/generate_leave_ledger_entries.py index 7dfdcc162b..c1e1b4f3b2 100644 --- a/erpnext/patches/v12_0/generate_leave_ledger_entries.py +++ b/erpnext/patches/v12_0/generate_leave_ledger_entries.py @@ -13,11 +13,22 @@ def execute(): if frappe.db.a_row_exists("Leave Ledger Entry"): 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_application_leave_ledger_entries() generate_encashment_leave_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(): ''' fix ledger entries for missing leave allocation transaction ''' allocation_list = get_allocation_records() @@ -64,7 +75,7 @@ def get_allocation_records(): employee, leave_type, new_leaves_allocated, - carry_forwarded_leaves, + unused_leaves, from_date, to_date, carry_forward From 9bc4232af619503028400ffdc446428feb81691d Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Thu, 8 Aug 2019 17:11:08 +0530 Subject: [PATCH 92/98] feat: calculate remaining leaves both multiple simultaneous allocation --- .../leave_allocation/test_leave_allocation.js | 2 +- .../leave_allocation/test_leave_allocation.py | 10 ++++------ .../leave_application/leave_application.py | 16 ++++++++++------ .../leave_application/test_leave_application.py | 15 ++++++++++++++- 4 files changed, 29 insertions(+), 14 deletions(-) diff --git a/erpnext/hr/doctype/leave_allocation/test_leave_allocation.js b/erpnext/hr/doctype/leave_allocation/test_leave_allocation.js index b8f4fafa6d..0ef78f2f88 100644 --- a/erpnext/hr/doctype/leave_allocation/test_leave_allocation.js +++ b/erpnext/hr/doctype/leave_allocation/test_leave_allocation.js @@ -34,7 +34,7 @@ QUnit.test("Test: Leave allocation [HR]", function (assert) { () => assert.equal(today_date, cur_frm.doc.from_date, "from date correctly set"), // check for total leaves - () => assert.equal(cur_frm.doc.carry_forwarded_leaves + 2, cur_frm.doc.total_leaves_allocated, + () => assert.equal(cur_frm.doc.unused_leaves + 2, cur_frm.doc.total_leaves_allocated, "total leave calculation is correctly set"), () => done() ]); diff --git a/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py b/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py index 4f4e0ab1fc..8f876ae7ce 100644 --- a/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py +++ b/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py @@ -80,7 +80,7 @@ class TestLeaveAllocation(unittest.TestCase): leave_type="_Test_CF_leave", from_date=add_months(nowdate(), -12), to_date=add_months(nowdate(), -1), - carry_forward=1) + carry_forward=0) leave_allocation.submit() # leave allocation with carry forward from previous allocation @@ -89,7 +89,7 @@ class TestLeaveAllocation(unittest.TestCase): carry_forward=1) leave_allocation_1.submit() - self.assertEquals(leave_allocation.total_leaves_allocated, leave_allocation_1.carry_forwarded_leaves) + self.assertEquals(leave_allocation.total_leaves_allocated, leave_allocation_1.unused_leaves) def test_carry_forward_leaves_expiry(self): frappe.db.sql("delete from `tabLeave Allocation`") @@ -105,11 +105,9 @@ class TestLeaveAllocation(unittest.TestCase): leave_type="_Test_CF_leave_expiry", from_date=add_months(nowdate(), -24), to_date=add_months(nowdate(), -12), - carry_forward=1) + carry_forward=0) leave_allocation.submit() - expire_allocation(leave_allocation) - leave_allocation = create_leave_allocation( leave_type="_Test_CF_leave_expiry", from_date=add_days(nowdate(), -90), @@ -128,7 +126,7 @@ class TestLeaveAllocation(unittest.TestCase): to_date=add_months(nowdate(), 12)) leave_allocation_1.submit() - self.assertEquals(leave_allocation_1.carry_forwarded_leaves, leave_allocation.new_leaves_allocated) + self.assertEquals(leave_allocation_1.unused_leaves, leave_allocation.new_leaves_allocated) def test_creation_of_leave_ledger_entry_on_submit(self): frappe.db.sql("delete from `tabLeave Allocation`") diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py index 2e43721a58..0aa8849e87 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.py +++ b/erpnext/hr/doctype/leave_application/leave_application.py @@ -387,10 +387,12 @@ class LeaveApplication(Document): create_leave_ledger_entry(self, args, submit) def get_allocation_expiry(employee, leave_type, to_date, from_date): + ''' Returns expiry of carry forward allocation in leave ledger entry ''' expiry = frappe.get_all("Leave Ledger Entry", filters={ 'employee': employee, 'leave_type': leave_type, + 'is_carry_forward': 1, 'transaction_type': 'Leave Allocation', 'to_date': ['between', (from_date, to_date)] },fields=['to_date']) @@ -487,7 +489,7 @@ def get_leave_allocation_records(employee, date, leave_type=None): "from_date": d.from_date, "to_date": d.to_date, "total_leaves_allocated": flt(d.cf_leaves) + flt(d.new_leaves), - "carry_forwarded_leaves": d.cf_leaves, + "unused_leaves": d.cf_leaves, "new_leaves_allocated": d.new_leaves, "leave_type": d.leave_type })) @@ -515,12 +517,14 @@ def get_remaining_leaves(allocation, leaves_taken, date, expiry): return remaining_leaves - if expiry and allocation.carry_forwarded_leaves: - remaining_leaves = _get_remaining_leaves(allocation.carry_forwarded_leaves, expiry) + total_leaves = allocation.total_leaves_allocated - return flt(allocation.new_leaves_allocated) + flt(remaining_leaves) - else: - return _get_remaining_leaves(allocation.total_leaves_allocated, allocation.to_date) + if expiry and allocation.unused_leaves: + remaining_leaves = _get_remaining_leaves(allocation.unused_leaves, expiry) + + total_leaves = flt(allocation.new_leaves_allocated) + flt(remaining_leaves) + + return _get_remaining_leaves(total_leaves, allocation.to_date) def get_leaves_for_period(employee, leave_type, from_date, to_date): leave_entries = get_leave_entries(employee, leave_type, from_date, to_date) diff --git a/erpnext/hr/doctype/leave_application/test_leave_application.py b/erpnext/hr/doctype/leave_application/test_leave_application.py index 709a2f57df..0f7bf86b22 100644 --- a/erpnext/hr/doctype/leave_application/test_leave_application.py +++ b/erpnext/hr/doctype/leave_application/test_leave_application.py @@ -542,6 +542,19 @@ class TestLeaveApplication(unittest.TestCase): self.assertEquals(leave_ledger_entry[0].leaves, -9) self.assertEquals(leave_ledger_entry[1].leaves, -2) + def test_leave_application_creation_after_expiry(self): + # test leave balance for carry forwarded allocation + employee = get_employee() + leave_type = create_leave_type( + leave_type_name="_Test_CF_leave_expiry", + is_carry_forward=1, + expire_carried_forward_leaves=90) + leave_type.submit() + + create_carry_forwarded_allocation(employee, leave_type) + + self.assertEquals(get_leave_balance_on(employee.name, leave_type.name, add_days(nowdate(), -85), add_days(nowdate(), -84)), 0) + def create_carry_forwarded_allocation(employee, leave_type): # initial leave allocation leave_allocation = create_leave_allocation( @@ -550,7 +563,7 @@ def create_carry_forwarded_allocation(employee, leave_type): employee_name=employee.employee_name, from_date=add_months(nowdate(), -24), to_date=add_months(nowdate(), -12), - carry_forward=1) + carry_forward=0) leave_allocation.submit() leave_allocation = create_leave_allocation( From bafc89f4399600bea1df4eecd6bc708b7ebf20c2 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Thu, 8 Aug 2019 17:43:23 +0530 Subject: [PATCH 93/98] refactor: convert raw sql to orm --- .../leave_allocation/leave_allocation.py | 2 +- .../leave_allocation/test_leave_allocation.py | 2 +- .../leave_application/leave_application.js | 2 +- .../test_leave_application.py | 6 +-- .../leave_ledger_entry/leave_ledger_entry.py | 2 +- .../hr/doctype/leave_period/leave_period.py | 2 +- erpnext/hr/doctype/leave_type/leave_type.json | 6 +-- .../hr/doctype/leave_type/test_leave_type.py | 2 +- .../v12_0/generate_leave_ledger_entries.py | 49 +++++-------------- 9 files changed, 23 insertions(+), 50 deletions(-) diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation.py b/erpnext/hr/doctype/leave_allocation/leave_allocation.py index 5e81999765..c11c125c6a 100755 --- a/erpnext/hr/doctype/leave_allocation/leave_allocation.py +++ b/erpnext/hr/doctype/leave_allocation/leave_allocation.py @@ -126,7 +126,7 @@ class LeaveAllocation(Document): def create_leave_ledger_entry(self, submit=True): 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_carry_forwarded_leaves_after_days") args = dict( leaves=self.unused_leaves, from_date=self.from_date, diff --git a/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py b/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py index 8f876ae7ce..bdba8c9f8f 100644 --- a/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py +++ b/erpnext/hr/doctype/leave_allocation/test_leave_allocation.py @@ -97,7 +97,7 @@ class TestLeaveAllocation(unittest.TestCase): leave_type = create_leave_type( leave_type_name="_Test_CF_leave_expiry", is_carry_forward=1, - expire_carried_forward_leaves=90) + expire_carry_forwarded_leaves_after_days=90) leave_type.submit() # initial leave allocation diff --git a/erpnext/hr/doctype/leave_application/leave_application.js b/erpnext/hr/doctype/leave_application/leave_application.js index a755b57608..174641048b 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.js +++ b/erpnext/hr/doctype/leave_application/leave_application.js @@ -49,7 +49,7 @@ frappe.ui.form.on("Leave Application", { async: false, args: { employee: frm.doc.employee, - date: frm.doc.from_date? frm.doc.from_date:frm.doc.posting_date + date: frm.doc.from_date || frm.doc.posting_date }, callback: function(r) { if (!r.exc && r.message['leave_allocation']) { diff --git a/erpnext/hr/doctype/leave_application/test_leave_application.py b/erpnext/hr/doctype/leave_application/test_leave_application.py index 0f7bf86b22..ad141a5748 100644 --- a/erpnext/hr/doctype/leave_application/test_leave_application.py +++ b/erpnext/hr/doctype/leave_application/test_leave_application.py @@ -415,7 +415,7 @@ class TestLeaveApplication(unittest.TestCase): leave_type = create_leave_type( leave_type_name="_Test_CF_leave_expiry", is_carry_forward=1, - expire_carried_forward_leaves=90) + expire_carry_forwarded_leaves_after_days=90) leave_type.submit() create_carry_forwarded_allocation(employee, leave_type) @@ -516,7 +516,7 @@ class TestLeaveApplication(unittest.TestCase): leave_type = create_leave_type( leave_type_name="_Test_CF_leave_expiry", is_carry_forward=1, - expire_carried_forward_leaves=90) + expire_carry_forwarded_leaves_after_days=90) leave_type.submit() create_carry_forwarded_allocation(employee, leave_type) @@ -548,7 +548,7 @@ class TestLeaveApplication(unittest.TestCase): leave_type = create_leave_type( leave_type_name="_Test_CF_leave_expiry", is_carry_forward=1, - expire_carried_forward_leaves=90) + expire_carry_forwarded_leaves_after_days=90) leave_type.submit() create_carry_forwarded_allocation(employee, leave_type) diff --git a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py index 33e4ea0909..c82114e6d5 100644 --- a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py +++ b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py @@ -92,7 +92,7 @@ def process_expired_allocation(): # fetch leave type records that has carry forwarded leaves expiry leave_type_records = frappe.db.get_values("Leave Type", filters={ - 'expire_carried_forward_leaves': (">", 0) + 'expire_carry_forwarded_leaves_after_days': (">", 0) }, fieldname=['name']) if leave_type_records: diff --git a/erpnext/hr/doctype/leave_period/leave_period.py b/erpnext/hr/doctype/leave_period/leave_period.py index a7de7185c9..a8566c4ffb 100644 --- a/erpnext/hr/doctype/leave_period/leave_period.py +++ b/erpnext/hr/doctype/leave_period/leave_period.py @@ -95,7 +95,7 @@ def get_existing_allocations(employees, leave_period): 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_carried_forward_leaves"]) + 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 diff --git a/erpnext/hr/doctype/leave_type/leave_type.json b/erpnext/hr/doctype/leave_type/leave_type.json index 7a279063ce..36d5f8b3d1 100644 --- a/erpnext/hr/doctype/leave_type/leave_type.json +++ b/erpnext/hr/doctype/leave_type/leave_type.json @@ -421,7 +421,7 @@ "depends_on": "", "description": "Calculated in days", "fetch_if_empty": 0, - "fieldname": "expire_carried_forward_leaves", + "fieldname": "expire_carry_forwarded_leaves_after_days", "fieldtype": "Int", "hidden": 0, "ignore_user_permissions": 0, @@ -430,7 +430,7 @@ "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, - "label": "Expire Carried Forward Leaves", + "label": "Expire Carry Forwarded Leaves After Days", "length": 0, "no_copy": 0, "permlevel": 0, @@ -728,7 +728,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2019-07-22 15:38:39.334283", + "modified": "2019-08-01 15:38:39.334283", "modified_by": "Administrator", "module": "HR", "name": "Leave Type", diff --git a/erpnext/hr/doctype/leave_type/test_leave_type.py b/erpnext/hr/doctype/leave_type/test_leave_type.py index d9852be6c9..0c4f435860 100644 --- a/erpnext/hr/doctype/leave_type/test_leave_type.py +++ b/erpnext/hr/doctype/leave_type/test_leave_type.py @@ -19,7 +19,7 @@ def create_leave_type(**args): "is_earned_leave": args.is_earned_leave or 0, "is_lwp": args.is_lwp or 0, "is_carry_forward": args.is_carry_forward or 0, - "expire_carried_forward_leaves": args.expire_carried_forward_leaves or 0, + "expire_carry_forwarded_leaves_after_days": args.expire_carry_forwarded_leaves_after_days or 0, "encashment_threshold_days": args.encashment_threshold_days or 5, "earning_component": "Leave Encashment" }) diff --git a/erpnext/patches/v12_0/generate_leave_ledger_entries.py b/erpnext/patches/v12_0/generate_leave_ledger_entries.py index c1e1b4f3b2..38f6883b9a 100644 --- a/erpnext/patches/v12_0/generate_leave_ledger_entries.py +++ b/erpnext/patches/v12_0/generate_leave_ledger_entries.py @@ -69,45 +69,18 @@ def generate_expiry_allocation_ledger_entries(): expire_allocation(allocation_obj) def get_allocation_records(): - return frappe.db.sql(""" - SELECT - name, - employee, - leave_type, - new_leaves_allocated, - unused_leaves, - from_date, - to_date, - carry_forward - FROM `tabLeave Allocation` - WHERE - docstatus=1 - ORDER BY to_date ASC - """, as_dict=1) + return frappe.get_all("Leave Allocation", filters={ + "docstatus": 1 + }, fields=['name', 'employee', 'leave_type', 'new_leaves_allocated', + 'unused_leaves', 'from_date', 'to_date', 'carry_forward' + ], order_by='to_date ASC') def get_leaves_application_records(): - return frappe.db.sql(""" - SELECT - name, - employee, - leave_type, - total_leave_days, - from_date, - to_date - FROM `tabLeave Application` - WHERE - docstatus=1 - """, as_dict=1) + return frappe.get_all("Leave Application", filters={ + "docstatus": 1 + }, fields=['name', 'employee', 'leave_type', 'total_leave_days', 'from_date', 'to_date']) def get_leave_encashment_records(): - return frappe.db.sql(""" - SELECT - name, - employee, - leave_type, - encashable_days, - encashment_date - FROM `tabLeave Encashment` - WHERE - docstatus=1 - """, as_dict=1) \ No newline at end of file + return frappe.get_all("Leave Encashment", filters={ + "docstatus": 1 + }, fields=['name', 'employee', 'leave_type', 'encashable_days', 'encashment_date']) From 3662ed50d2eb624b62c4b27d00e53b9689560223 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Thu, 8 Aug 2019 19:47:17 +0530 Subject: [PATCH 94/98] fix: multiple changes --- erpnext/hr/doctype/leave_type/leave_type.json | 4 ++-- erpnext/hr/doctype/leave_type/leave_type.py | 2 +- .../employee_leave_balance.py | 2 +- erpnext/hr/utils.py | 15 +++++++++------ erpnext/patches.txt | 1 - 5 files changed, 13 insertions(+), 11 deletions(-) diff --git a/erpnext/hr/doctype/leave_type/leave_type.json b/erpnext/hr/doctype/leave_type/leave_type.json index 36d5f8b3d1..2f15e3b3c1 100644 --- a/erpnext/hr/doctype/leave_type/leave_type.json +++ b/erpnext/hr/doctype/leave_type/leave_type.json @@ -430,7 +430,7 @@ "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, - "label": "Expire Carry Forwarded Leaves After Days", + "label": "Expire Carry Forwarded Leaves (Days)", "length": 0, "no_copy": 0, "permlevel": 0, @@ -728,7 +728,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2019-08-01 15:38:39.334283", + "modified": "2019-08-02 15:38:39.334283", "modified_by": "Administrator", "module": "HR", "name": "Leave Type", diff --git a/erpnext/hr/doctype/leave_type/leave_type.py b/erpnext/hr/doctype/leave_type/leave_type.py index 75336e0c7e..3a19fa98e6 100644 --- a/erpnext/hr/doctype/leave_type/leave_type.py +++ b/erpnext/hr/doctype/leave_type/leave_type.py @@ -14,4 +14,4 @@ class LeaveType(Document): if self.is_lwp: leave_allocation = frappe.db.sql_list("""select name from `tabLeave Allocation` where leave_type=%s""", (self.name)) if leave_allocation: - frappe.throw(_("""Leave application is linked with leave allocations {0}. Leave application cannot be set as leave without pay""").format(", ".join(leave_allocation))) #nosec \ No newline at end of file + frappe.throw(_('Leave application is linked with leave allocations {0}. Leave application cannot be set as leave without pay').format(", ".join(leave_allocation))) #nosec \ No newline at end of file diff --git a/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py b/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py index b99708a91f..66e3614982 100644 --- a/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py +++ b/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py @@ -66,7 +66,7 @@ def get_data(filters, leave_types): filters.from_date, filters.to_date) * -1 # opening balance - opening = get_leave_balance_on(employee.name, leave_type, filters.from_date) + opening = get_total_allocated_leaves(employee.name, leave_type, filters.from_date, filters.to_date) # closing balance closing = flt(opening) - flt(leaves_taken) diff --git a/erpnext/hr/utils.py b/erpnext/hr/utils.py index 4fe9f6b285..3accbbb156 100644 --- a/erpnext/hr/utils.py +++ b/erpnext/hr/utils.py @@ -266,8 +266,10 @@ def get_leave_period(from_date, to_date, company): def generate_leave_encashment(): ''' Generates a draft leave encashment on allocation expiry ''' from erpnext.hr.doctype.leave_encashment.leave_encashment import create_leave_encashment + if frappe.db.get_single_value('HR Settings', 'auto_leave_encashment'): - leave_type = frappe.db.sql_list("SELECT name FROM `tabLeave Type` WHERE `allow_encashment`=1") + leave_type = frappe.get_all('Leave Type', filters={'allow_encashment': 1}, fields=['name']) + leave_type=[l['name'] for l in leave_type] leave_allocation = frappe.get_all("Leave Allocation", filters={ 'to_date': add_days(today(), -1), @@ -285,9 +287,8 @@ def allocate_earned_leaves(): divide_by_frequency = {"Yearly": 1, "Half-Yearly": 6, "Quarterly": 4, "Monthly": 12} for e_leave_type in e_leave_types: - leave_allocations = frappe.db.sql("""select name, employee, from_date, to_date from `tabLeave Allocation` where '{0}' - between from_date and to_date and docstatus=1 and leave_type='{1}'""" - .format(today, e_leave_type.name), as_dict=1) + leave_allocations = frappe.db.sql("""select name, employee, from_date, to_date from `tabLeave Allocation` where %s + between from_date and to_date and docstatus=1 and leave_type=%s""", (today, e_leave_type.name), as_dict=1) for allocation in leave_allocations: leave_policy = get_employee_leave_policy(allocation.employee) if not leave_policy: @@ -295,8 +296,10 @@ def allocate_earned_leaves(): if not e_leave_type.earned_leave_frequency == "Monthly": if not check_frequency_hit(allocation.from_date, today, e_leave_type.earned_leave_frequency): continue - annual_allocation = frappe.db.sql("""select annual_allocation from `tabLeave Policy Detail` - where parent=%s and leave_type=%s""", (leave_policy.name, e_leave_type.name)) + annual_allocation = frappe.get_all("Leave Policy Detail", filters={ + 'parent': leave_policy.name, + 'leave_type': e_leave_type.name + }, fields=['annual_allocation']) if annual_allocation and annual_allocation[0]: earned_leaves = flt(annual_allocation[0][0]) / divide_by_frequency[e_leave_type.earned_leave_frequency] if e_leave_type.rounding == "0.5": diff --git a/erpnext/patches.txt b/erpnext/patches.txt index ce2a5192d9..9b8c2f0a6b 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -603,7 +603,6 @@ erpnext.patches.v11_1.set_salary_details_submittable erpnext.patches.v11_1.rename_depends_on_lwp execute:frappe.delete_doc("Report", "Inactive Items") erpnext.patches.v11_1.delete_scheduling_tool -execute:frappe.delete_doc_if_exists("Page", "support-analytics") erpnext.patches.v12_0.rename_tolerance_fields erpnext.patches.v12_0.make_custom_fields_for_bank_remittance #14-06-2019 execute:frappe.delete_doc_if_exists("Page", "support-analytics") From 261d132f3a90ffc0b3391d4baf7f2bc1e50a9c77 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Fri, 9 Aug 2019 13:18:52 +0530 Subject: [PATCH 95/98] fix: calculate earned leaves based on annual allocation --- erpnext/hr/utils.py | 8 ++++---- erpnext/patches/v12_0/generate_leave_ledger_entries.py | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/erpnext/hr/utils.py b/erpnext/hr/utils.py index 3accbbb156..7ef61e920c 100644 --- a/erpnext/hr/utils.py +++ b/erpnext/hr/utils.py @@ -296,12 +296,12 @@ def allocate_earned_leaves(): if not e_leave_type.earned_leave_frequency == "Monthly": if not check_frequency_hit(allocation.from_date, today, e_leave_type.earned_leave_frequency): continue - annual_allocation = frappe.get_all("Leave Policy Detail", filters={ + annual_allocation = frappe.db.get_value("Leave Policy Detail", filters={ 'parent': leave_policy.name, 'leave_type': e_leave_type.name - }, fields=['annual_allocation']) - if annual_allocation and annual_allocation[0]: - earned_leaves = flt(annual_allocation[0][0]) / divide_by_frequency[e_leave_type.earned_leave_frequency] + }, fieldname=['annual_allocation']) + if annual_allocation: + earned_leaves = flt(annual_allocation) / divide_by_frequency[e_leave_type.earned_leave_frequency] if e_leave_type.rounding == "0.5": earned_leaves = round(earned_leaves * 2) / 2 else: diff --git a/erpnext/patches/v12_0/generate_leave_ledger_entries.py b/erpnext/patches/v12_0/generate_leave_ledger_entries.py index 38f6883b9a..44b59bf09b 100644 --- a/erpnext/patches/v12_0/generate_leave_ledger_entries.py +++ b/erpnext/patches/v12_0/generate_leave_ledger_entries.py @@ -83,4 +83,4 @@ def get_leaves_application_records(): def get_leave_encashment_records(): return frappe.get_all("Leave Encashment", filters={ "docstatus": 1 - }, fields=['name', 'employee', 'leave_type', 'encashable_days', 'encashment_date']) + }, fields=['name', 'employee', 'leave_type', 'encashable_days', 'encashment_date']) \ No newline at end of file From 8b9b77959d0b2b03158cfa376d3d4d74ee1b3005 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Tue, 13 Aug 2019 13:20:10 +0530 Subject: [PATCH 96/98] fix: fetch min date when carry forward expiry is greater than leave allocation expiry --- erpnext/hr/doctype/leave_allocation/leave_allocation.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation.py b/erpnext/hr/doctype/leave_allocation/leave_allocation.py index c11c125c6a..296a52c2c7 100755 --- a/erpnext/hr/doctype/leave_allocation/leave_allocation.py +++ b/erpnext/hr/doctype/leave_allocation/leave_allocation.py @@ -3,7 +3,7 @@ from __future__ import unicode_literals import frappe -from frappe.utils import flt, date_diff, formatdate, add_days, today +from frappe.utils import flt, date_diff, formatdate, add_days, today, getdate from frappe import _ from frappe.model.document import Document from erpnext.hr.utils import set_employee_name, get_leave_period @@ -127,10 +127,11 @@ class LeaveAllocation(Document): def create_leave_ledger_entry(self, submit=True): if self.unused_leaves: expiry_days = frappe.db.get_value("Leave Type", self.leave_type, "expire_carry_forwarded_leaves_after_days") + end_date = add_days(self.from_date, expiry_days - 1) if expiry_days else self.to_date args = dict( leaves=self.unused_leaves, from_date=self.from_date, - to_date=add_days(self.from_date, expiry_days - 1) if expiry_days else self.to_date, + to_date= min(getdate(end_date), getdate(self.to_date)), is_carry_forward=1 ) create_leave_ledger_entry(self, args, submit) From 107b2768fd1a585f1ca83d1aad202751c426f3ca Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Wed, 14 Aug 2019 16:09:16 +0530 Subject: [PATCH 97/98] refactor: replace raw sql with orm --- erpnext/hr/doctype/leave_type/leave_type.py | 9 ++++++++- erpnext/hr/utils.py | 1 - 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/erpnext/hr/doctype/leave_type/leave_type.py b/erpnext/hr/doctype/leave_type/leave_type.py index 3a19fa98e6..598bff2f13 100644 --- a/erpnext/hr/doctype/leave_type/leave_type.py +++ b/erpnext/hr/doctype/leave_type/leave_type.py @@ -5,6 +5,7 @@ from __future__ import unicode_literals import calendar import frappe from datetime import datetime +from frappe.utils import today from frappe import _ from frappe.model.document import Document @@ -12,6 +13,12 @@ from frappe.model.document import Document class LeaveType(Document): def validate(self): if self.is_lwp: - leave_allocation = frappe.db.sql_list("""select name from `tabLeave Allocation` where leave_type=%s""", (self.name)) + leave_allocation = frappe.get_all("Leave Allocation", filters={ + 'leave_type': self.name, + 'from_date': ("<=", today()), + 'to_date': (">=", today()) + }, ['name']) + leave_allocation = [l['name'] for l in leave_allocation] + frappe.db("""select name from `tabLeave Allocation` where leave_type=%s""", (self.name)) if leave_allocation: frappe.throw(_('Leave application is linked with leave allocations {0}. Leave application cannot be set as leave without pay').format(", ".join(leave_allocation))) #nosec \ No newline at end of file diff --git a/erpnext/hr/utils.py b/erpnext/hr/utils.py index 2183c5e678..1464a77936 100644 --- a/erpnext/hr/utils.py +++ b/erpnext/hr/utils.py @@ -323,7 +323,6 @@ def allocate_earned_leaves(): allocation.db_set("total_leaves_allocated", new_allocation, update_modified=False) create_earned_leave_ledger_entry(allocation, earned_leaves, today) - def create_earned_leave_ledger_entry(allocation, earned_leaves, date): ''' Create leave ledger entry based on the earned leave frequency ''' allocation.new_leaves_allocated = earned_leaves From ae2a0fb4c7ae3704e8f37fa3820cd9511fb05bf5 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Wed, 14 Aug 2019 16:50:31 +0530 Subject: [PATCH 98/98] chore: remove unused code --- erpnext/hr/doctype/leave_type/leave_type.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/erpnext/hr/doctype/leave_type/leave_type.py b/erpnext/hr/doctype/leave_type/leave_type.py index 598bff2f13..c0d1296841 100644 --- a/erpnext/hr/doctype/leave_type/leave_type.py +++ b/erpnext/hr/doctype/leave_type/leave_type.py @@ -17,8 +17,7 @@ class LeaveType(Document): 'leave_type': self.name, 'from_date': ("<=", today()), 'to_date': (">=", today()) - }, ['name']) + }, fields=['name']) leave_allocation = [l['name'] for l in leave_allocation] - frappe.db("""select name from `tabLeave Allocation` where leave_type=%s""", (self.name)) if leave_allocation: - frappe.throw(_('Leave application is linked with leave allocations {0}. Leave application cannot be set as leave without pay').format(", ".join(leave_allocation))) #nosec \ No newline at end of file + frappe.throw(_('Leave application is linked with leave allocations {0}. Leave application cannot be set as leave without pay').format(", ".join(leave_allocation))) #nosec