From ec6b11c8b21b4181cbb1a8b3169b1abd27ed92a5 Mon Sep 17 00:00:00 2001 From: Marc-Constantin Enke Date: Wed, 9 Apr 2025 18:08:15 +0200 Subject: [PATCH 01/17] feat: new bulk leave application doctype (drc-148) --- .../bulk_leave_application/__init__.py | 0 .../bulk_leave_application.js | 8 + .../bulk_leave_application.json | 148 ++++++++++++++++++ .../bulk_leave_application.py | 24 +++ .../test_bulk_leave_application.py | 9 ++ .../leave_application_dates_table/__init__.py | 0 .../leave_application_dates_table.json | 47 ++++++ .../leave_application_dates_table.py | 9 ++ 8 files changed, 245 insertions(+) create mode 100644 time_capture/time_capture/doctype/bulk_leave_application/__init__.py create mode 100644 time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application.js create mode 100644 time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application.json create mode 100644 time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application.py create mode 100644 time_capture/time_capture/doctype/bulk_leave_application/test_bulk_leave_application.py create mode 100644 time_capture/time_capture/doctype/leave_application_dates_table/__init__.py create mode 100644 time_capture/time_capture/doctype/leave_application_dates_table/leave_application_dates_table.json create mode 100644 time_capture/time_capture/doctype/leave_application_dates_table/leave_application_dates_table.py diff --git a/time_capture/time_capture/doctype/bulk_leave_application/__init__.py b/time_capture/time_capture/doctype/bulk_leave_application/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application.js b/time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application.js new file mode 100644 index 0000000..4b8be55 --- /dev/null +++ b/time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application.js @@ -0,0 +1,8 @@ +// Copyright (c) 2025, ALYF GmbH and contributors +// For license information, please see license.txt + +// frappe.ui.form.on("Bulk Leave Application", { +// refresh(frm) { + +// }, +// }); diff --git a/time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application.json b/time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application.json new file mode 100644 index 0000000..fb48b10 --- /dev/null +++ b/time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application.json @@ -0,0 +1,148 @@ +{ + "actions": [], + "allow_rename": 1, + "creation": "2025-04-09 14:17:35.501172", + "doctype": "DocType", + "engine": "InnoDB", + "field_order": [ + "employee", + "employee_name", + "column_break_ihkc", + "leave_type", + "company", + "department", + "dates_section", + "table_leaves", + "approval_section", + "leave_approver", + "leave_approver_name", + "column_break_lfyt", + "posting_date", + "status", + "amended_from" + ], + "fields": [ + { + "fieldname": "employee", + "fieldtype": "Link", + "label": "Employee", + "options": "Employee" + }, + { + "fieldname": "employee_name", + "fieldtype": "Data", + "label": "Employee Name" + }, + { + "fieldname": "column_break_ihkc", + "fieldtype": "Column Break" + }, + { + "fieldname": "leave_type", + "fieldtype": "Link", + "label": "Leave Type", + "options": "Leave Type" + }, + { + "fetch_from": "employee.company", + "fieldname": "company", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Company", + "options": "Company", + "read_only": 1, + "reqd": 1 + }, + { + "fetch_from": "employee.department", + "fieldname": "department", + "fieldtype": "Link", + "label": "Department", + "options": "Department" + }, + { + "fieldname": "approval_section", + "fieldtype": "Section Break", + "label": "Approval" + }, + { + "fieldname": "leave_approver", + "fieldtype": "Link", + "label": "Leave Approver", + "options": "User" + }, + { + "fieldname": "leave_approver_name", + "fieldtype": "Data", + "label": "Leave Approver Name", + "read_only": 1 + }, + { + "fieldname": "column_break_lfyt", + "fieldtype": "Column Break" + }, + { + "default": "Today", + "fieldname": "posting_date", + "fieldtype": "Date", + "label": "Posting Date", + "no_copy": 1, + "reqd": 1 + }, + { + "fieldname": "status", + "fieldtype": "Select", + "in_standard_filter": 1, + "label": "Status", + "no_copy": 1, + "options": "Open\nApproved\nRejected\nCancelled" + }, + { + "fieldname": "dates_section", + "fieldtype": "Section Break", + "label": "Dates" + }, + { + "fieldname": "table_leaves", + "fieldtype": "Table", + "label": "\u00d6eaves", + "options": "Leave Application Dates Table" + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Bulk Leave Application", + "print_hide": 1, + "read_only": 1, + "search_index": 1 + } + ], + "grid_page_length": 50, + "index_web_pages_for_search": 1, + "is_submittable": 1, + "links": [], + "modified": "2025-04-09 16:34:43.747223", + "modified_by": "Administrator", + "module": "Time Capture", + "name": "Bulk Leave Application", + "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": "DESC", + "states": [] +} \ No newline at end of file diff --git a/time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application.py b/time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application.py new file mode 100644 index 0000000..263711f --- /dev/null +++ b/time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application.py @@ -0,0 +1,24 @@ +# Copyright (c) 2025, ALYF GmbH and contributors +# For license information, please see license.txt + +import frappe +from frappe.model.document import Document + + +class BulkLeaveApplication(Document): + def on_submit(self): + self.create_leave_applications() + + + def create_leave_applications(self): + for item in self.table_leaves: + leave_application = frappe.new_doc("Leave Application") + leave_application.employee = self.employee + leave_application.leave_type = self.leave_type + leave_application.leave_approver = self.leave_approver + leave_application.posting_date = self.posting_date + leave_application.status = self.status + leave_application.from_date = item.from_date + leave_application.to_date = item.to_date + leave_application.reason = item.description + leave_application.insert() diff --git a/time_capture/time_capture/doctype/bulk_leave_application/test_bulk_leave_application.py b/time_capture/time_capture/doctype/bulk_leave_application/test_bulk_leave_application.py new file mode 100644 index 0000000..3a702aa --- /dev/null +++ b/time_capture/time_capture/doctype/bulk_leave_application/test_bulk_leave_application.py @@ -0,0 +1,9 @@ +# Copyright (c) 2025, ALYF GmbH and Contributors +# See license.txt + +# import frappe +from frappe.tests.utils import FrappeTestCase + + +class TestBulkLeaveApplication(FrappeTestCase): + pass diff --git a/time_capture/time_capture/doctype/leave_application_dates_table/__init__.py b/time_capture/time_capture/doctype/leave_application_dates_table/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/time_capture/time_capture/doctype/leave_application_dates_table/leave_application_dates_table.json b/time_capture/time_capture/doctype/leave_application_dates_table/leave_application_dates_table.json new file mode 100644 index 0000000..b60eb83 --- /dev/null +++ b/time_capture/time_capture/doctype/leave_application_dates_table/leave_application_dates_table.json @@ -0,0 +1,47 @@ +{ + "actions": [], + "allow_rename": 1, + "creation": "2025-04-09 15:37:31.771057", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "from_date", + "to_date", + "description" + ], + "fields": [ + { + "fieldname": "description", + "fieldtype": "Text Editor", + "in_list_view": 1, + "label": "Reason" + }, + { + "fieldname": "from_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "From Date", + "reqd": 1 + }, + { + "fieldname": "to_date", + "fieldtype": "Date", + "label": "To Date", + "reqd": 1 + } + ], + "grid_page_length": 50, + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2025-04-09 16:14:33.234760", + "modified_by": "Administrator", + "module": "Time Capture", + "name": "Leave Application Dates Table", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/time_capture/time_capture/doctype/leave_application_dates_table/leave_application_dates_table.py b/time_capture/time_capture/doctype/leave_application_dates_table/leave_application_dates_table.py new file mode 100644 index 0000000..b7c35cb --- /dev/null +++ b/time_capture/time_capture/doctype/leave_application_dates_table/leave_application_dates_table.py @@ -0,0 +1,9 @@ +# Copyright (c) 2025, ALYF GmbH and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class LeaveApplicationDatesTable(Document): + pass From cd58815015b4d85cceb8efd81209313b4b53d038 Mon Sep 17 00:00:00 2001 From: Marc-Constantin Enke Date: Wed, 9 Apr 2025 18:32:07 +0200 Subject: [PATCH 02/17] fix: linter --- .../doctype/bulk_leave_application/bulk_leave_application.py | 1 - 1 file changed, 1 deletion(-) diff --git a/time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application.py b/time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application.py index 263711f..0ab5dd6 100644 --- a/time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application.py +++ b/time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application.py @@ -9,7 +9,6 @@ class BulkLeaveApplication(Document): def on_submit(self): self.create_leave_applications() - def create_leave_applications(self): for item in self.table_leaves: leave_application = frappe.new_doc("Leave Application") From 3680fadb742a2aa63a86ba15e8eeab0f8a90b382 Mon Sep 17 00:00:00 2001 From: Marc-Constantin Enke Date: Fri, 11 Apr 2025 16:20:40 +0200 Subject: [PATCH 03/17] feat: add permissons --- .../bulk_leave_application.json | 111 ++++++++++++++++-- .../__init__.py | 0 .../bulk_leave_application_dates_table.json} | 5 +- .../bulk_leave_application_dates_table.py} | 2 +- 4 files changed, 108 insertions(+), 10 deletions(-) rename time_capture/time_capture/doctype/{leave_application_dates_table => bulk_leave_application_dates_table}/__init__.py (100%) rename time_capture/time_capture/doctype/{leave_application_dates_table/leave_application_dates_table.json => bulk_leave_application_dates_table/bulk_leave_application_dates_table.json} (87%) rename time_capture/time_capture/doctype/{leave_application_dates_table/leave_application_dates_table.py => bulk_leave_application_dates_table/bulk_leave_application_dates_table.py} (77%) diff --git a/time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application.json b/time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application.json index fb48b10..6835379 100644 --- a/time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application.json +++ b/time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application.json @@ -26,7 +26,8 @@ "fieldname": "employee", "fieldtype": "Link", "label": "Employee", - "options": "Employee" + "options": "Employee", + "reqd": 1 }, { "fieldname": "employee_name", @@ -41,7 +42,8 @@ "fieldname": "leave_type", "fieldtype": "Link", "label": "Leave Type", - "options": "Leave Type" + "options": "Leave Type", + "reqd": 1 }, { "fetch_from": "employee.company", @@ -69,7 +71,8 @@ "fieldname": "leave_approver", "fieldtype": "Link", "label": "Leave Approver", - "options": "User" + "options": "User", + "reqd": 1 }, { "fieldname": "leave_approver_name", @@ -95,7 +98,8 @@ "in_standard_filter": 1, "label": "Status", "no_copy": 1, - "options": "Open\nApproved\nRejected\nCancelled" + "options": "Open\nApproved\nRejected\nCancelled", + "permlevel": 1 }, { "fieldname": "dates_section", @@ -105,8 +109,8 @@ { "fieldname": "table_leaves", "fieldtype": "Table", - "label": "\u00d6eaves", - "options": "Leave Application Dates Table" + "label": "Leaves", + "options": "Bulk Leave Application Dates Table" }, { "fieldname": "amended_from", @@ -123,7 +127,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2025-04-09 16:34:43.747223", + "modified": "2025-04-11 08:16:12.530794", "modified_by": "Administrator", "module": "Time Capture", "name": "Bulk Leave Application", @@ -140,8 +144,101 @@ "role": "System Manager", "share": 1, "write": 1 + }, + { + "create": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Employee", + "share": 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 + }, + { + "email": 1, + "export": 1, + "permlevel": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "All", + "share": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Leave Approver", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "email": 1, + "export": 1, + "permlevel": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Leave Approver", + "share": 1, + "write": 1 + }, + { + "email": 1, + "export": 1, + "permlevel": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "write": 1 + }, + { + "email": 1, + "export": 1, + "permlevel": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "write": 1 } ], + "row_format": "Dynamic", "sort_field": "modified", "sort_order": "DESC", "states": [] diff --git a/time_capture/time_capture/doctype/leave_application_dates_table/__init__.py b/time_capture/time_capture/doctype/bulk_leave_application_dates_table/__init__.py similarity index 100% rename from time_capture/time_capture/doctype/leave_application_dates_table/__init__.py rename to time_capture/time_capture/doctype/bulk_leave_application_dates_table/__init__.py diff --git a/time_capture/time_capture/doctype/leave_application_dates_table/leave_application_dates_table.json b/time_capture/time_capture/doctype/bulk_leave_application_dates_table/bulk_leave_application_dates_table.json similarity index 87% rename from time_capture/time_capture/doctype/leave_application_dates_table/leave_application_dates_table.json rename to time_capture/time_capture/doctype/bulk_leave_application_dates_table/bulk_leave_application_dates_table.json index b60eb83..2165cb6 100644 --- a/time_capture/time_capture/doctype/leave_application_dates_table/leave_application_dates_table.json +++ b/time_capture/time_capture/doctype/bulk_leave_application_dates_table/bulk_leave_application_dates_table.json @@ -35,12 +35,13 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2025-04-09 16:14:33.234760", + "modified": "2025-04-10 16:51:40.541427", "modified_by": "Administrator", "module": "Time Capture", - "name": "Leave Application Dates Table", + "name": "Bulk Leave Application Dates Table", "owner": "Administrator", "permissions": [], + "row_format": "Dynamic", "sort_field": "modified", "sort_order": "DESC", "states": [] diff --git a/time_capture/time_capture/doctype/leave_application_dates_table/leave_application_dates_table.py b/time_capture/time_capture/doctype/bulk_leave_application_dates_table/bulk_leave_application_dates_table.py similarity index 77% rename from time_capture/time_capture/doctype/leave_application_dates_table/leave_application_dates_table.py rename to time_capture/time_capture/doctype/bulk_leave_application_dates_table/bulk_leave_application_dates_table.py index b7c35cb..2c97050 100644 --- a/time_capture/time_capture/doctype/leave_application_dates_table/leave_application_dates_table.py +++ b/time_capture/time_capture/doctype/bulk_leave_application_dates_table/bulk_leave_application_dates_table.py @@ -5,5 +5,5 @@ from frappe.model.document import Document -class LeaveApplicationDatesTable(Document): +class BulkLeaveApplicationDatesTable(Document): pass From 5f8f58d59e1f46c193ad87e595fa1c2431fd6a30 Mon Sep 17 00:00:00 2001 From: Marc-Constantin Enke Date: Tue, 22 Apr 2025 13:49:29 +0200 Subject: [PATCH 04/17] fix: doctype fields changes --- .../bulk_leave_application.json | 46 ++++++------------- .../__init__.py | 0 .../bulk_leave_application_date.json} | 18 ++++---- .../bulk_leave_application_date.py} | 2 +- 4 files changed, 23 insertions(+), 43 deletions(-) rename time_capture/time_capture/doctype/{bulk_leave_application_dates_table => bulk_leave_application_date}/__init__.py (100%) rename time_capture/time_capture/doctype/{bulk_leave_application_dates_table/bulk_leave_application_dates_table.json => bulk_leave_application_date/bulk_leave_application_date.json} (85%) rename time_capture/time_capture/doctype/{bulk_leave_application_dates_table/bulk_leave_application_dates_table.py => bulk_leave_application_date/bulk_leave_application_date.py} (77%) diff --git a/time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application.json b/time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application.json index 6835379..28d734a 100644 --- a/time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application.json +++ b/time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application.json @@ -9,13 +9,10 @@ "employee_name", "column_break_ihkc", "leave_type", - "company", - "department", "dates_section", "table_leaves", "approval_section", "leave_approver", - "leave_approver_name", "column_break_lfyt", "posting_date", "status", @@ -25,11 +22,13 @@ { "fieldname": "employee", "fieldtype": "Link", + "in_list_view": 1, "label": "Employee", "options": "Employee", "reqd": 1 }, { + "fetch_from": "employee.employee_name", "fieldname": "employee_name", "fieldtype": "Data", "label": "Employee Name" @@ -41,27 +40,11 @@ { "fieldname": "leave_type", "fieldtype": "Link", + "in_list_view": 1, "label": "Leave Type", "options": "Leave Type", "reqd": 1 }, - { - "fetch_from": "employee.company", - "fieldname": "company", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Company", - "options": "Company", - "read_only": 1, - "reqd": 1 - }, - { - "fetch_from": "employee.department", - "fieldname": "department", - "fieldtype": "Link", - "label": "Department", - "options": "Department" - }, { "fieldname": "approval_section", "fieldtype": "Section Break", @@ -70,16 +53,11 @@ { "fieldname": "leave_approver", "fieldtype": "Link", + "in_list_view": 1, "label": "Leave Approver", "options": "User", "reqd": 1 }, - { - "fieldname": "leave_approver_name", - "fieldtype": "Data", - "label": "Leave Approver Name", - "read_only": 1 - }, { "fieldname": "column_break_lfyt", "fieldtype": "Column Break" @@ -88,11 +66,13 @@ "default": "Today", "fieldname": "posting_date", "fieldtype": "Date", + "in_list_view": 1, "label": "Posting Date", "no_copy": 1, "reqd": 1 }, { + "allow_on_submit": 1, "fieldname": "status", "fieldtype": "Select", "in_standard_filter": 1, @@ -106,12 +86,6 @@ "fieldtype": "Section Break", "label": "Dates" }, - { - "fieldname": "table_leaves", - "fieldtype": "Table", - "label": "Leaves", - "options": "Bulk Leave Application Dates Table" - }, { "fieldname": "amended_from", "fieldtype": "Link", @@ -121,13 +95,19 @@ "print_hide": 1, "read_only": 1, "search_index": 1 + }, + { + "fieldname": "table_leaves", + "fieldtype": "Table", + "label": "Leaves", + "options": "Bulk Leave Application Date" } ], "grid_page_length": 50, "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2025-04-11 08:16:12.530794", + "modified": "2025-04-22 13:48:05.632369", "modified_by": "Administrator", "module": "Time Capture", "name": "Bulk Leave Application", diff --git a/time_capture/time_capture/doctype/bulk_leave_application_dates_table/__init__.py b/time_capture/time_capture/doctype/bulk_leave_application_date/__init__.py similarity index 100% rename from time_capture/time_capture/doctype/bulk_leave_application_dates_table/__init__.py rename to time_capture/time_capture/doctype/bulk_leave_application_date/__init__.py diff --git a/time_capture/time_capture/doctype/bulk_leave_application_dates_table/bulk_leave_application_dates_table.json b/time_capture/time_capture/doctype/bulk_leave_application_date/bulk_leave_application_date.json similarity index 85% rename from time_capture/time_capture/doctype/bulk_leave_application_dates_table/bulk_leave_application_dates_table.json rename to time_capture/time_capture/doctype/bulk_leave_application_date/bulk_leave_application_date.json index 2165cb6..5aa1fed 100644 --- a/time_capture/time_capture/doctype/bulk_leave_application_dates_table/bulk_leave_application_dates_table.json +++ b/time_capture/time_capture/doctype/bulk_leave_application_date/bulk_leave_application_date.json @@ -8,15 +8,9 @@ "field_order": [ "from_date", "to_date", - "description" + "reason" ], "fields": [ - { - "fieldname": "description", - "fieldtype": "Text Editor", - "in_list_view": 1, - "label": "Reason" - }, { "fieldname": "from_date", "fieldtype": "Date", @@ -29,16 +23,22 @@ "fieldtype": "Date", "label": "To Date", "reqd": 1 + }, + { + "fieldname": "reason", + "fieldtype": "Text Editor", + "in_list_view": 1, + "label": "Reason" } ], "grid_page_length": 50, "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2025-04-10 16:51:40.541427", + "modified": "2025-04-22 13:30:38.858496", "modified_by": "Administrator", "module": "Time Capture", - "name": "Bulk Leave Application Dates Table", + "name": "Bulk Leave Application Date", "owner": "Administrator", "permissions": [], "row_format": "Dynamic", diff --git a/time_capture/time_capture/doctype/bulk_leave_application_dates_table/bulk_leave_application_dates_table.py b/time_capture/time_capture/doctype/bulk_leave_application_date/bulk_leave_application_date.py similarity index 77% rename from time_capture/time_capture/doctype/bulk_leave_application_dates_table/bulk_leave_application_dates_table.py rename to time_capture/time_capture/doctype/bulk_leave_application_date/bulk_leave_application_date.py index 2c97050..0651c99 100644 --- a/time_capture/time_capture/doctype/bulk_leave_application_dates_table/bulk_leave_application_dates_table.py +++ b/time_capture/time_capture/doctype/bulk_leave_application_date/bulk_leave_application_date.py @@ -5,5 +5,5 @@ from frappe.model.document import Document -class BulkLeaveApplicationDatesTable(Document): +class BulkLeaveApplicationDate(Document): pass From 92cf6359e96a766444841765087f28bb3eae15a9 Mon Sep 17 00:00:00 2001 From: Marc-Constantin Enke Date: Thu, 1 May 2025 09:29:18 +0200 Subject: [PATCH 05/17] feat: create leave application when approved --- .../bulk_leave_application.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application.py b/time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application.py index 0ab5dd6..da826cd 100644 --- a/time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application.py +++ b/time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application.py @@ -3,11 +3,16 @@ import frappe from frappe.model.document import Document +from hrms.hr.utils import share_doc_with_approver, validate_active_employee class BulkLeaveApplication(Document): - def on_submit(self): - self.create_leave_applications() + def on_update_after_submit(self): + if self.status == "Approved": + self.create_leave_applications() + + def on_update(self): + share_doc_with_approver(self, self.leave_approver) def create_leave_applications(self): for item in self.table_leaves: @@ -19,5 +24,7 @@ def create_leave_applications(self): leave_application.status = self.status leave_application.from_date = item.from_date leave_application.to_date = item.to_date - leave_application.reason = item.description + leave_application.reason = item.reason + leave_application.follow_via_email = 0 leave_application.insert() + leave_application.submit() From 1dc2f86d55662240cf7914de8a36dd005d4e94d1 Mon Sep 17 00:00:00 2001 From: Marc-Constantin Enke Date: Tue, 6 May 2025 14:22:44 +0200 Subject: [PATCH 06/17] feat: weekly off dates get added to table --- .../bulk_leave_application.js | 9 ++++ .../bulk_leave_application.json | 47 ++++++++++++++----- .../bulk_leave_application.py | 33 ++++++++++++- .../bulk_leave_application_date.json | 23 ++++----- 4 files changed, 85 insertions(+), 27 deletions(-) diff --git a/time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application.js b/time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application.js index 4b8be55..2faac96 100644 --- a/time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application.js +++ b/time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application.js @@ -6,3 +6,12 @@ // }, // }); + +frappe.ui.form.on("Bulk Leave Application", { + from_date: function (frm) { + if (frm.doc.from_date && !frm.doc.to_date) { + var a_year_from_start = frappe.datetime.add_months(frm.doc.from_date, 12); + frm.set_value("to_date", frappe.datetime.add_days(a_year_from_start, -1)); + } + }, +}); diff --git a/time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application.json b/time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application.json index 28d734a..b649828 100644 --- a/time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application.json +++ b/time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application.json @@ -9,12 +9,16 @@ "employee_name", "column_break_ihkc", "leave_type", + "from_date", + "to_date", + "add_weekly_holidays", + "weekly_off", + "get_weekly_off_dates", "dates_section", "table_leaves", "approval_section", "leave_approver", "column_break_lfyt", - "posting_date", "status", "amended_from" ], @@ -62,15 +66,6 @@ "fieldname": "column_break_lfyt", "fieldtype": "Column Break" }, - { - "default": "Today", - "fieldname": "posting_date", - "fieldtype": "Date", - "in_list_view": 1, - "label": "Posting Date", - "no_copy": 1, - "reqd": 1 - }, { "allow_on_submit": 1, "fieldname": "status", @@ -101,13 +96,43 @@ "fieldtype": "Table", "label": "Leaves", "options": "Bulk Leave Application Date" + }, + { + "depends_on": "eval: doc.from_date && doc.to_date", + "fieldname": "add_weekly_holidays", + "fieldtype": "Section Break", + "label": "Add Weekly Holidays" + }, + { + "fieldname": "weekly_off", + "fieldtype": "Select", + "label": "Weekly Off", + "options": "\nSunday\nMonday\nTuesday\nWednesday\nThursday\nFriday\nSaturday" + }, + { + "fieldname": "get_weekly_off_dates", + "fieldtype": "Button", + "label": "Add to Holidays", + "options": "get_weekly_off_dates" + }, + { + "fieldname": "from_date", + "fieldtype": "Date", + "label": "From Date", + "reqd": 1 + }, + { + "fieldname": "to_date", + "fieldtype": "Date", + "label": "To Date", + "reqd": 1 } ], "grid_page_length": 50, "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2025-04-22 13:48:05.632369", + "modified": "2025-05-06 14:13:25.081817", "modified_by": "Administrator", "module": "Time Capture", "name": "Bulk Leave Application", diff --git a/time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application.py b/time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application.py index da826cd..2fb58ce 100644 --- a/time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application.py +++ b/time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application.py @@ -2,8 +2,10 @@ # For license information, please see license.txt import frappe +from frappe import _ from frappe.model.document import Document -from hrms.hr.utils import share_doc_with_approver, validate_active_employee +from frappe.utils import getdate +from hrms.hr.utils import share_doc_with_approver class BulkLeaveApplication(Document): @@ -14,6 +16,35 @@ def on_update_after_submit(self): def on_update(self): share_doc_with_approver(self, self.leave_approver) + @frappe.whitelist() + def get_weekly_off_dates(self): + if not self.weekly_off: + frappe.throw(_("Please select weekly off day")) + for d in self.get_weekly_off_date_list(self.from_date, self.to_date): + self.append("table_leaves", {"reason": _(self.weekly_off), "date": d}) + + def get_weekly_off_date_list(self, start_date, end_date): + start_date, end_date = getdate(start_date), getdate(end_date) + + import calendar + from datetime import timedelta + + from dateutil import relativedelta + + date_list = [] + existing_date_list = [] + weekday = getattr(calendar, (self.weekly_off).upper()) + reference_date = start_date + relativedelta.relativedelta(weekday=weekday) + + existing_date_list = [getdate(row.date) for row in self.get("table_leaves")] + + while reference_date <= end_date: + if reference_date not in existing_date_list: + date_list.append(reference_date) + reference_date += timedelta(days=7) + + return date_list + def create_leave_applications(self): for item in self.table_leaves: leave_application = frappe.new_doc("Leave Application") diff --git a/time_capture/time_capture/doctype/bulk_leave_application_date/bulk_leave_application_date.json b/time_capture/time_capture/doctype/bulk_leave_application_date/bulk_leave_application_date.json index 5aa1fed..0c45179 100644 --- a/time_capture/time_capture/doctype/bulk_leave_application_date/bulk_leave_application_date.json +++ b/time_capture/time_capture/doctype/bulk_leave_application_date/bulk_leave_application_date.json @@ -6,36 +6,29 @@ "editable_grid": 1, "engine": "InnoDB", "field_order": [ - "from_date", - "to_date", + "date", "reason" ], "fields": [ { - "fieldname": "from_date", - "fieldtype": "Date", + "fieldname": "reason", + "fieldtype": "Text Editor", "in_list_view": 1, - "label": "From Date", - "reqd": 1 + "label": "Reason" }, { - "fieldname": "to_date", + "fieldname": "date", "fieldtype": "Date", - "label": "To Date", - "reqd": 1 - }, - { - "fieldname": "reason", - "fieldtype": "Text Editor", "in_list_view": 1, - "label": "Reason" + "label": "Date", + "reqd": 1 } ], "grid_page_length": 50, "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2025-04-22 13:30:38.858496", + "modified": "2025-05-06 11:28:32.421287", "modified_by": "Administrator", "module": "Time Capture", "name": "Bulk Leave Application Date", From b3b13e3a8e46e561c2a3dae611163437c9e49e4d Mon Sep 17 00:00:00 2001 From: Marc-Constantin Enke Date: Tue, 6 May 2025 14:40:11 +0200 Subject: [PATCH 07/17] feat: add from date and to date in child table --- .../bulk_leave_application.js | 6 ++++++ .../bulk_leave_application.py | 7 ++++--- .../bulk_leave_application_date.json | 15 +++++++++++---- 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application.js b/time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application.js index 2faac96..6d2decd 100644 --- a/time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application.js +++ b/time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application.js @@ -15,3 +15,9 @@ frappe.ui.form.on("Bulk Leave Application", { } }, }); + +frappe.ui.form.on("Bulk Leave Application Date", { + from_date: function (frm, cdt, cdn) { + frappe.model.set_value(cdt, cdn, "to_date", locals[cdt][cdn].from_date); + }, +}); diff --git a/time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application.py b/time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application.py index 2fb58ce..55b92c4 100644 --- a/time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application.py +++ b/time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application.py @@ -5,6 +5,7 @@ from frappe import _ from frappe.model.document import Document from frappe.utils import getdate +from frappe.utils.data import today from hrms.hr.utils import share_doc_with_approver @@ -21,7 +22,7 @@ def get_weekly_off_dates(self): if not self.weekly_off: frappe.throw(_("Please select weekly off day")) for d in self.get_weekly_off_date_list(self.from_date, self.to_date): - self.append("table_leaves", {"reason": _(self.weekly_off), "date": d}) + self.append("table_leaves", {"reason": _(self.weekly_off), "from_date": d, "to_date": d}) def get_weekly_off_date_list(self, start_date, end_date): start_date, end_date = getdate(start_date), getdate(end_date) @@ -36,7 +37,7 @@ def get_weekly_off_date_list(self, start_date, end_date): weekday = getattr(calendar, (self.weekly_off).upper()) reference_date = start_date + relativedelta.relativedelta(weekday=weekday) - existing_date_list = [getdate(row.date) for row in self.get("table_leaves")] + existing_date_list = [getdate(row.from_date) for row in self.get("table_leaves")] while reference_date <= end_date: if reference_date not in existing_date_list: @@ -51,7 +52,7 @@ def create_leave_applications(self): leave_application.employee = self.employee leave_application.leave_type = self.leave_type leave_application.leave_approver = self.leave_approver - leave_application.posting_date = self.posting_date + leave_application.posting_date = today() leave_application.status = self.status leave_application.from_date = item.from_date leave_application.to_date = item.to_date diff --git a/time_capture/time_capture/doctype/bulk_leave_application_date/bulk_leave_application_date.json b/time_capture/time_capture/doctype/bulk_leave_application_date/bulk_leave_application_date.json index 0c45179..bf733ed 100644 --- a/time_capture/time_capture/doctype/bulk_leave_application_date/bulk_leave_application_date.json +++ b/time_capture/time_capture/doctype/bulk_leave_application_date/bulk_leave_application_date.json @@ -6,7 +6,8 @@ "editable_grid": 1, "engine": "InnoDB", "field_order": [ - "date", + "from_date", + "to_date", "reason" ], "fields": [ @@ -17,10 +18,16 @@ "label": "Reason" }, { - "fieldname": "date", + "fieldname": "from_date", "fieldtype": "Date", "in_list_view": 1, - "label": "Date", + "label": "From Date", + "reqd": 1 + }, + { + "fieldname": "to_date", + "fieldtype": "Date", + "label": "To Date", "reqd": 1 } ], @@ -28,7 +35,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2025-05-06 11:28:32.421287", + "modified": "2025-05-06 14:28:29.297564", "modified_by": "Administrator", "module": "Time Capture", "name": "Bulk Leave Application Date", From d8a433b92a8540eb69f9f4b31fe217a16f6409a2 Mon Sep 17 00:00:00 2001 From: Patrick Eissler <77415730+PatrickDEissler@users.noreply.github.com> Date: Mon, 12 May 2025 08:29:52 +0200 Subject: [PATCH 08/17] fix(Bulk Leave Application): adjust permissions --- .../bulk_leave_application/bulk_leave_application.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application.json b/time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application.json index b649828..de54577 100644 --- a/time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application.json +++ b/time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application.json @@ -132,13 +132,15 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2025-05-06 14:13:25.081817", + "modified": "2025-05-12 08:29:14.829897", "modified_by": "Administrator", "module": "Time Capture", "name": "Bulk Leave Application", "owner": "Administrator", "permissions": [ { + "amend": 1, + "cancel": 1, "create": 1, "delete": 1, "email": 1, @@ -148,6 +150,7 @@ "report": 1, "role": "System Manager", "share": 1, + "submit": 1, "write": 1 }, { @@ -159,6 +162,7 @@ "report": 1, "role": "Employee", "share": 1, + "submit": 1, "write": 1 }, { From c8571075873233d21832fe2068828f3d1e24c07d Mon Sep 17 00:00:00 2001 From: Patrick Eissler <77415730+PatrickDEissler@users.noreply.github.com> Date: Mon, 12 May 2025 08:40:14 +0200 Subject: [PATCH 09/17] feat: re-add to_date to grid view --- .../bulk_leave_application_date.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/time_capture/time_capture/doctype/bulk_leave_application_date/bulk_leave_application_date.json b/time_capture/time_capture/doctype/bulk_leave_application_date/bulk_leave_application_date.json index bf733ed..c0ab3e7 100644 --- a/time_capture/time_capture/doctype/bulk_leave_application_date/bulk_leave_application_date.json +++ b/time_capture/time_capture/doctype/bulk_leave_application_date/bulk_leave_application_date.json @@ -27,6 +27,7 @@ { "fieldname": "to_date", "fieldtype": "Date", + "in_list_view": 1, "label": "To Date", "reqd": 1 } @@ -35,7 +36,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2025-05-06 14:28:29.297564", + "modified": "2025-05-12 08:31:36.000904", "modified_by": "Administrator", "module": "Time Capture", "name": "Bulk Leave Application Date", From cd10aaffaf1573ba20028bd264e66808597f5ec8 Mon Sep 17 00:00:00 2001 From: Patrick Eissler <77415730+PatrickDEissler@users.noreply.github.com> Date: Mon, 12 May 2025 08:44:54 +0200 Subject: [PATCH 10/17] feat: set Leave Approver and limit options to valid approvers only --- .../bulk_leave_application.js | 40 +++++++++++++++++-- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application.js b/time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application.js index 6d2decd..472869a 100644 --- a/time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application.js +++ b/time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application.js @@ -8,16 +8,50 @@ // }); frappe.ui.form.on("Bulk Leave Application", { + setup: function (frm) { + frm.set_query("leave_approver", function () { + return { + query: "hrms.hr.doctype.department_approver.department_approver.get_approvers", + filters: { + employee: frm.doc.employee, + doctype: "Leave Application", + }, + }; + }); + frm.set_query("employee", erpnext.queries.employee); + }, + from_date: function (frm) { if (frm.doc.from_date && !frm.doc.to_date) { var a_year_from_start = frappe.datetime.add_months(frm.doc.from_date, 12); frm.set_value("to_date", frappe.datetime.add_days(a_year_from_start, -1)); } }, -}); -frappe.ui.form.on("Bulk Leave Application Date", { - from_date: function (frm, cdt, cdn) { + employee: function (frm) { + frm.trigger("set_leave_approver"); + }, + + set_leave_approver: function (frm) { + if (frm.doc.employee) { + return frappe.call({ + method: "hrms.hr.doctype.leave_application.leave_application.get_leave_approver", + args: { + employee: frm.doc.employee, + }, + callback: function (r) { + if (r && r.message) { + frm.set_value("leave_approver", r.message); + } + }, + }); + } + }, +}); + + frappe.ui.form.on("Bulk Leave Application Date", { + from_date: function (frm, cdt, cdn) { frappe.model.set_value(cdt, cdn, "to_date", locals[cdt][cdn].from_date); }, }); + From 5eab4940a9a944f7be2b914ee6fe90833dd67f59 Mon Sep 17 00:00:00 2001 From: Patrick Eissler <77415730+PatrickDEissler@users.noreply.github.com> Date: Mon, 12 May 2025 08:54:33 +0200 Subject: [PATCH 11/17] feat: adjust list view and general form styling --- .../bulk_leave_application.json | 8 +++++--- .../bulk_leave_application_list.js | 16 ++++++++++++++++ 2 files changed, 21 insertions(+), 3 deletions(-) create mode 100644 time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application_list.js diff --git a/time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application.json b/time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application.json index de54577..d59aedf 100644 --- a/time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application.json +++ b/time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application.json @@ -26,6 +26,7 @@ { "fieldname": "employee", "fieldtype": "Link", + "in_filter": 1, "in_list_view": 1, "label": "Employee", "options": "Employee", @@ -57,7 +58,6 @@ { "fieldname": "leave_approver", "fieldtype": "Link", - "in_list_view": 1, "label": "Leave Approver", "options": "User", "reqd": 1 @@ -70,6 +70,7 @@ "allow_on_submit": 1, "fieldname": "status", "fieldtype": "Select", + "in_filter": 1, "in_standard_filter": 1, "label": "Status", "no_copy": 1, @@ -132,7 +133,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2025-05-12 08:29:14.829897", + "modified": "2025-05-12 08:50:33.467370", "modified_by": "Administrator", "module": "Time Capture", "name": "Bulk Leave Application", @@ -250,5 +251,6 @@ "row_format": "Dynamic", "sort_field": "modified", "sort_order": "DESC", - "states": [] + "states": [], + "title_field": "employee_name" } \ No newline at end of file diff --git a/time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application_list.js b/time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application_list.js new file mode 100644 index 0000000..6344517 --- /dev/null +++ b/time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application_list.js @@ -0,0 +1,16 @@ +frappe.listview_settings["Bulk Leave Application"] = { + has_indicator_for_draft: 1, + get_indicator: function (doc) { + const status_color = { + Approved: "green", + Rejected: "red", + Open: "orange", + Draft: "red", + Cancelled: "red", + Submitted: "blue", + }; + const status = + !doc.docstatus && ["Approved", "Rejected"].includes(doc.status) ? "Draft" : doc.status; + return [__(status), status_color[status], "status,=," + doc.status]; + }, +}; From fcfc0fdce83b432b3a0cd88b517131e30d3abd41 Mon Sep 17 00:00:00 2001 From: Patrick Eissler <77415730+PatrickDEissler@users.noreply.github.com> Date: Mon, 12 May 2025 14:58:53 +0200 Subject: [PATCH 12/17] fix: adjust process (similiar to Leave Application = more consistent status) --- .../bulk_leave_application/bulk_leave_application.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application.py b/time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application.py index 55b92c4..6a16ae5 100644 --- a/time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application.py +++ b/time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application.py @@ -10,12 +10,17 @@ class BulkLeaveApplication(Document): - def on_update_after_submit(self): + def on_update(self): + share_doc_with_approver(self, self.leave_approver) + + def on_submit(self): + if self.status in ["Open", "Cancelled"]: + frappe.throw(_("Only Leave Applications with status 'Approved' and 'Rejected' can be submitted")) if self.status == "Approved": self.create_leave_applications() - def on_update(self): - share_doc_with_approver(self, self.leave_approver) + def before_cancel(self): + self.status = "Cancelled" @frappe.whitelist() def get_weekly_off_dates(self): From 51bbaf8a909761dc87ea4f002032a84871c0a542 Mon Sep 17 00:00:00 2001 From: Patrick Eissler <77415730+PatrickDEissler@users.noreply.github.com> Date: Mon, 12 May 2025 15:09:59 +0200 Subject: [PATCH 13/17] feat: link Leave Application with Bulk Leave Application --- time_capture/custom_fields.py | 10 ++++++++++ time_capture/patches.txt | 2 +- .../bulk_leave_application/bulk_leave_application.py | 1 + 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/time_capture/custom_fields.py b/time_capture/custom_fields.py index fc5c4c5..95d4ccc 100644 --- a/time_capture/custom_fields.py +++ b/time_capture/custom_fields.py @@ -40,6 +40,16 @@ def get_custom_fields(): "reqd": 1, }, ], + "Leave Application": [ + { + "fieldname": "bulk_leave_application", + "fieldtype": "Link", + "insert_after": "letter_head", + "label": _("Bulk Leave Application"), + "options": "Bulk Leave Application", + "read_only": 1, + }, + ], "Task": [ { "fieldname": "custom_hourly_billed", diff --git a/time_capture/patches.txt b/time_capture/patches.txt index 30a5c90..916a412 100644 --- a/time_capture/patches.txt +++ b/time_capture/patches.txt @@ -3,4 +3,4 @@ # Read docs to understand patches: https://frappeframework.com/docs/v14/user/en/database-migrations [post_model_sync] -execute:from time_capture.install import after_install;after_install() # 2025-03-19 #2 \ No newline at end of file +execute:from time_capture.install import after_install;after_install() # 2025-05-12 \ No newline at end of file diff --git a/time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application.py b/time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application.py index 6a16ae5..12c104e 100644 --- a/time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application.py +++ b/time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application.py @@ -63,5 +63,6 @@ def create_leave_applications(self): leave_application.to_date = item.to_date leave_application.reason = item.reason leave_application.follow_via_email = 0 + leave_application.bulk_leave_application = self.name leave_application.insert() leave_application.submit() From 086d2ba2ae3eb9ea6d1f2ccf0be07c1ad271573b Mon Sep 17 00:00:00 2001 From: Patrick Eissler <77415730+PatrickDEissler@users.noreply.github.com> Date: Mon, 12 May 2025 15:58:08 +0200 Subject: [PATCH 14/17] feat: avoid holidays in weekly-offs --- .../doctype/bulk_leave_application/bulk_leave_application.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application.py b/time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application.py index 12c104e..781d2bc 100644 --- a/time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application.py +++ b/time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application.py @@ -49,6 +49,9 @@ def get_weekly_off_date_list(self, start_date, end_date): date_list.append(reference_date) reference_date += timedelta(days=7) + holiday_list = frappe.db.get_value("Employee", self.employee, "holiday_list") + holidays = frappe.get_all("Holiday", filters={"parent": holiday_list, "holiday_date": ["between", [start_date, end_date]]}, pluck="holiday_date") + date_list = [d for d in date_list if d not in holidays] return date_list def create_leave_applications(self): From 9a150339185bc4038618c8cf67438ab5cb76ba53 Mon Sep 17 00:00:00 2001 From: Patrick Eissler <77415730+PatrickDEissler@users.noreply.github.com> Date: Mon, 12 May 2025 16:17:34 +0200 Subject: [PATCH 15/17] refactor: use hr utils function to get holidays --- .../doctype/bulk_leave_application/bulk_leave_application.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application.py b/time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application.py index 781d2bc..4aa8f94 100644 --- a/time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application.py +++ b/time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application.py @@ -6,7 +6,7 @@ from frappe.model.document import Document from frappe.utils import getdate from frappe.utils.data import today -from hrms.hr.utils import share_doc_with_approver +from hrms.hr.utils import share_doc_with_approver, get_holiday_dates_for_employee class BulkLeaveApplication(Document): @@ -49,8 +49,7 @@ def get_weekly_off_date_list(self, start_date, end_date): date_list.append(reference_date) reference_date += timedelta(days=7) - holiday_list = frappe.db.get_value("Employee", self.employee, "holiday_list") - holidays = frappe.get_all("Holiday", filters={"parent": holiday_list, "holiday_date": ["between", [start_date, end_date]]}, pluck="holiday_date") + holidays = get_holiday_dates_for_employee(self.employee, start_date, end_date) date_list = [d for d in date_list if d not in holidays] return date_list From a918ad1725d2453df8fe6817164ccfc8775bf0bc Mon Sep 17 00:00:00 2001 From: Patrick Eissler <77415730+PatrickDEissler@users.noreply.github.com> Date: Mon, 12 May 2025 16:43:18 +0200 Subject: [PATCH 16/17] feat: create Attendances instead of Leave Applications --- .../bulk_leave_application.py | 34 ++++++++++--------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application.py b/time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application.py index 4aa8f94..15c3044 100644 --- a/time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application.py +++ b/time_capture/time_capture/doctype/bulk_leave_application/bulk_leave_application.py @@ -6,6 +6,9 @@ from frappe.model.document import Document from frappe.utils import getdate from frappe.utils.data import today + +from erpnext.buying.doctype.supplier_scorecard.supplier_scorecard import daterange + from hrms.hr.utils import share_doc_with_approver, get_holiday_dates_for_employee @@ -17,7 +20,7 @@ def on_submit(self): if self.status in ["Open", "Cancelled"]: frappe.throw(_("Only Leave Applications with status 'Approved' and 'Rejected' can be submitted")) if self.status == "Approved": - self.create_leave_applications() + self.create_attendances() def before_cancel(self): self.status = "Cancelled" @@ -53,18 +56,17 @@ def get_weekly_off_date_list(self, start_date, end_date): date_list = [d for d in date_list if d not in holidays] return date_list - def create_leave_applications(self): - for item in self.table_leaves: - leave_application = frappe.new_doc("Leave Application") - leave_application.employee = self.employee - leave_application.leave_type = self.leave_type - leave_application.leave_approver = self.leave_approver - leave_application.posting_date = today() - leave_application.status = self.status - leave_application.from_date = item.from_date - leave_application.to_date = item.to_date - leave_application.reason = item.reason - leave_application.follow_via_email = 0 - leave_application.bulk_leave_application = self.name - leave_application.insert() - leave_application.submit() + def create_attendances(self): + holidays = get_holiday_dates_for_employee(self.employee, self.from_date, self.to_date) + for period in self.table_leaves: # TODO: Rename this! + for dt in daterange(getdate(period.from_date), getdate(period.to_date)): + if dt in holidays: + continue + attendance = frappe.new_doc("Attendance") + attendance.employee = self.employee + attendance.employee_name = self.employee_name + attendance.attendance_date = str(dt) + attendance.status = "On Leave" + attendance.leave_type = self.leave_type + attendance.insert() + attendance.submit() From d48c741346bc1bf17b9d0249cb20be962e8288de Mon Sep 17 00:00:00 2001 From: Patrick Eissler <77415730+PatrickDEissler@users.noreply.github.com> Date: Wed, 14 May 2025 14:55:12 +0200 Subject: [PATCH 17/17] fix(Attendance): calculate flexitime on_change to cover all use cases (DRC-148) --- time_capture/hooks.py | 2 +- time_capture/scripts/attendance.py | 32 +++++++++++++++---- .../doctype/time_capture/time_capture.py | 10 ------ 3 files changed, 27 insertions(+), 17 deletions(-) diff --git a/time_capture/hooks.py b/time_capture/hooks.py index e0849be..5a77a58 100644 --- a/time_capture/hooks.py +++ b/time_capture/hooks.py @@ -139,7 +139,7 @@ doc_events = { "Attendance": { - "before_insert": "time_capture.scripts.attendance.before_insert", + "on_change": "time_capture.scripts.attendance.on_change", "on_submit": "time_capture.scripts.attendance.on_submit", "on_cancel": "time_capture.scripts.attendance.on_cancel", }, diff --git a/time_capture/scripts/attendance.py b/time_capture/scripts/attendance.py index 805eb67..e378c60 100644 --- a/time_capture/scripts/attendance.py +++ b/time_capture/scripts/attendance.py @@ -4,8 +4,9 @@ from time_capture.time_capture.doctype.time_capture.time_capture import _create_time_capture -def before_insert(doc, event): - set_flexitime_for_compensatory_leave(doc) +def on_change(doc, event): + if not doc.flags.flexitime_updated: + set_flexitime_and_working_time(doc) def on_submit(doc, event): @@ -18,10 +19,29 @@ def on_cancel(doc, event): _create_time_capture(employee, doc.attendance_date) -def set_flexitime_for_compensatory_leave(doc): - if not doc.leave_type or frappe.db.get_value("Leave Type", doc.leave_type, "is_compensatory") != 1: - return - doc.flexitime = -frappe.db.get_value("Employee", doc.employee, "expected_daily_working_hours") +def set_flexitime_and_working_time(doc): + doc.flags.flexitime_updated = True + expected_working_hours = frappe.db.get_value("Employee", doc.employee, "expected_daily_working_hours") + working_hours = doc.working_hours + if not doc.leave_type: + if expected_working_hours: + HALF_DAY = expected_working_hours / 2 + OVERTIME_FACTOR = 1.15 + MAX_HALF_DAY = HALF_DAY * OVERTIME_FACTOR * 60 * 60 + doc.db_set({"status": "Present" if working_hours > MAX_HALF_DAY else "Half Day"}) + else: + if frappe.db.get_value("Leave Type", doc.leave_type, "is_compensatory") == 1: + working_hours = 0 + else: + expected_working_hours = 0 + working_hours = 0 + doc.db_set( + { + "expected_working_hours": expected_working_hours, + "working_hours": working_hours, + "flexitime": working_hours - expected_working_hours, + } + ) def delete_time_capture(doc): diff --git a/time_capture/time_capture/doctype/time_capture/time_capture.py b/time_capture/time_capture/doctype/time_capture/time_capture.py index 47d59f2..47a5f3b 100644 --- a/time_capture/time_capture/doctype/time_capture/time_capture.py +++ b/time_capture/time_capture/doctype/time_capture/time_capture.py @@ -146,24 +146,14 @@ def create_attendance(self): }, ): working_hours = self.working_time / 60 / 60 - expected_working_hours = frappe.get_value( - "Employee", self.employee, "expected_daily_working_hours" - ) - if expected_working_hours: - HALF_DAY = expected_working_hours / 2 - OVERTIME_FACTOR = 1.15 - MAX_HALF_DAY = HALF_DAY * OVERTIME_FACTOR * 60 * 60 attendance = frappe.get_doc( { "doctype": "Attendance", "employee": self.employee, - "status": "Present" if self.working_time > MAX_HALF_DAY else "Half Day", "attendance_date": self.date, "custom_time_capture": self.name, "working_hours": working_hours, - "expected_working_hours": expected_working_hours, - "flexitime": working_hours - expected_working_hours, } ) attendance.flags.ignore_permissions = True