diff --git a/time_capture/custom_fields.py b/time_capture/custom_fields.py index fc5c4c5..3ab59a3 100644 --- a/time_capture/custom_fields.py +++ b/time_capture/custom_fields.py @@ -32,12 +32,12 @@ def get_custom_fields(): ], "Employee": [ { - "fieldname": "expected_daily_working_hours", - "fieldtype": "Float", + "fieldname": "expected_working_hours", + "fieldtype": "Table", "insert_after": "attendance_device_id", - "label": _("Expected Daily Working Hours"), - "translatable": 0, + "label": _("Expected Working Hours"), "reqd": 1, + "options": "Employee Expected Working Hours", }, ], "Task": [ diff --git a/time_capture/hooks.py b/time_capture/hooks.py index e0849be..f425a44 100644 --- a/time_capture/hooks.py +++ b/time_capture/hooks.py @@ -143,6 +143,9 @@ "on_submit": "time_capture.scripts.attendance.on_submit", "on_cancel": "time_capture.scripts.attendance.on_cancel", }, + "Employee": { + "before_validate": "time_capture.scripts.employee.before_validate", + }, } # Scheduled Tasks diff --git a/time_capture/patches.txt b/time_capture/patches.txt index 89e3822..6072d3f 100644 --- a/time_capture/patches.txt +++ b/time_capture/patches.txt @@ -3,5 +3,6 @@ # 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 -time_capture.patches.log_inconsistent_time_capture_logs +execute:from time_capture.install import make_custom_fields;make_custom_fields() # 2025-07-01 #14:26 +execute:from time_capture.install import make_property_setters;make_property_setters() # 2025-07-01 #14:26 +time_capture.patches.move_expected_working_time_to_child_table diff --git a/time_capture/patches/move_expected_working_time_to_child_table.py b/time_capture/patches/move_expected_working_time_to_child_table.py new file mode 100644 index 0000000..d80509b --- /dev/null +++ b/time_capture/patches/move_expected_working_time_to_child_table.py @@ -0,0 +1,30 @@ +import frappe + + +def execute(): + custom_field_filters = { + "dt": "Employee", + "fieldname": "expected_daily_working_hours", + "fieldtype": "Float", + } + if not frappe.db.exists("Custom Field", custom_field_filters): + return + + for employee_id in frappe.get_all( + "Employee", filters={"expected_daily_working_hours": ("is", "set")}, pluck="name" + ): + print(f"Updating {employee_id}") + + employee = frappe.get_doc("Employee", employee_id) + if employee.expected_working_hours or not employee.expected_daily_working_hours: + continue + + employee.append( + "expected_working_hours", + { + "valid_from": employee.date_of_joining, + "expected_daily_working_hours": employee.expected_daily_working_hours, + }, + ) + employee.save() + frappe.db.delete("Custom Field", custom_field_filters) diff --git a/time_capture/scripts/employee.py b/time_capture/scripts/employee.py new file mode 100644 index 0000000..c16803d --- /dev/null +++ b/time_capture/scripts/employee.py @@ -0,0 +1,24 @@ +import frappe +from frappe import _ +from frappe.utils import getdate + + +def before_validate(doc, method): + validate_expected_working_hours(doc) + + +def validate_expected_working_hours(doc): + if not doc.date_of_joining == min(getdate(ewh.valid_from) for ewh in doc.expected_working_hours): + frappe.throw(_("Date of Joining is not the same as the earliest date of Expected Working Hours.")) + + +def get_expected_working_hours(employee_id, date): + """ + Get the expected working hours for an employee on a specific date. + """ + return frappe.db.get_value( + "Employee Expected Working Hours", + filters={"parent": employee_id, "valid_from": ("<=", date)}, + fieldname="expected_daily_working_hours", + order_by="valid_from desc", + ) diff --git a/time_capture/time_capture/doctype/employee_expected_working_hours/__init__.py b/time_capture/time_capture/doctype/employee_expected_working_hours/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/time_capture/time_capture/doctype/employee_expected_working_hours/employee_expected_working_hours.json b/time_capture/time_capture/doctype/employee_expected_working_hours/employee_expected_working_hours.json new file mode 100644 index 0000000..4b67278 --- /dev/null +++ b/time_capture/time_capture/doctype/employee_expected_working_hours/employee_expected_working_hours.json @@ -0,0 +1,45 @@ +{ + "actions": [], + "allow_rename": 1, + "creation": "2025-05-14 14:56:54.878019", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "valid_from", + "expected_daily_working_hours" + ], + "fields": [ + { + "columns": 4, + "fieldname": "valid_from", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Valid From", + "reqd": 1 + }, + { + "columns": 6, + "fieldname": "expected_daily_working_hours", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Expected Daily Working Hours", + "non_negative": 1, + "reqd": 1 + } + ], + "grid_page_length": 50, + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2025-05-14 15:44:30.974078", + "modified_by": "Administrator", + "module": "Time Capture", + "name": "Employee Expected Working Hours", + "owner": "Administrator", + "permissions": [], + "row_format": "Dynamic", + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/time_capture/time_capture/doctype/employee_expected_working_hours/employee_expected_working_hours.py b/time_capture/time_capture/doctype/employee_expected_working_hours/employee_expected_working_hours.py new file mode 100644 index 0000000..5753a41 --- /dev/null +++ b/time_capture/time_capture/doctype/employee_expected_working_hours/employee_expected_working_hours.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 EmployeeExpectedWorkingHours(Document): + pass