From 7d013e51b15ae715fbbaf6bd7e1505d9cdd20540 Mon Sep 17 00:00:00 2001 From: scdanieli <23150094+scdanieli@users.noreply.github.com> Date: Sat, 28 Sep 2024 18:39:06 +0200 Subject: [PATCH] feat: make mandatory breaks flexible (DRC-26) --- .../doctype/mandatory_break/__init__.py | 0 .../mandatory_break/mandatory_break.json | 44 +++++++++++++++++++ .../mandatory_break/mandatory_break.py | 9 ++++ .../doctype/working_time/working_time.py | 42 +++++++++++++----- .../working_time_settings.json | 18 +++++++- arbeitszeiterfassung_s4a/translations/de.csv | 3 ++ 6 files changed, 103 insertions(+), 13 deletions(-) create mode 100644 arbeitszeiterfassung_s4a/arbeitszeiterfassung_s4a/doctype/mandatory_break/__init__.py create mode 100644 arbeitszeiterfassung_s4a/arbeitszeiterfassung_s4a/doctype/mandatory_break/mandatory_break.json create mode 100644 arbeitszeiterfassung_s4a/arbeitszeiterfassung_s4a/doctype/mandatory_break/mandatory_break.py diff --git a/arbeitszeiterfassung_s4a/arbeitszeiterfassung_s4a/doctype/mandatory_break/__init__.py b/arbeitszeiterfassung_s4a/arbeitszeiterfassung_s4a/doctype/mandatory_break/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/arbeitszeiterfassung_s4a/arbeitszeiterfassung_s4a/doctype/mandatory_break/mandatory_break.json b/arbeitszeiterfassung_s4a/arbeitszeiterfassung_s4a/doctype/mandatory_break/mandatory_break.json new file mode 100644 index 0000000..0f696da --- /dev/null +++ b/arbeitszeiterfassung_s4a/arbeitszeiterfassung_s4a/doctype/mandatory_break/mandatory_break.json @@ -0,0 +1,44 @@ +{ + "actions": [], + "allow_rename": 1, + "creation": "2024-09-27 15:10:44.141822", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "working_time", + "additional_break_time" + ], + "fields": [ + { + "fieldname": "working_time", + "fieldtype": "Duration", + "hide_days": 1, + "hide_seconds": 1, + "in_list_view": 1, + "label": "Working Time (Greater Than)", + "reqd": 1 + }, + { + "fieldname": "additional_break_time", + "fieldtype": "Duration", + "hide_days": 1, + "hide_seconds": 1, + "in_list_view": 1, + "label": "Additional Break", + "reqd": 1 + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2024-09-28 18:33:23.991534", + "modified_by": "Administrator", + "module": "Arbeitszeiterfassung S4A", + "name": "Mandatory Break", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/arbeitszeiterfassung_s4a/arbeitszeiterfassung_s4a/doctype/mandatory_break/mandatory_break.py b/arbeitszeiterfassung_s4a/arbeitszeiterfassung_s4a/doctype/mandatory_break/mandatory_break.py new file mode 100644 index 0000000..061d2f5 --- /dev/null +++ b/arbeitszeiterfassung_s4a/arbeitszeiterfassung_s4a/doctype/mandatory_break/mandatory_break.py @@ -0,0 +1,9 @@ +# Copyright (c) 2024, ALYF GmbH and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class MandatoryBreak(Document): + pass diff --git a/arbeitszeiterfassung_s4a/arbeitszeiterfassung_s4a/doctype/working_time/working_time.py b/arbeitszeiterfassung_s4a/arbeitszeiterfassung_s4a/doctype/working_time/working_time.py index 3819d27..0a71e50 100644 --- a/arbeitszeiterfassung_s4a/arbeitszeiterfassung_s4a/doctype/working_time/working_time.py +++ b/arbeitszeiterfassung_s4a/arbeitszeiterfassung_s4a/doctype/working_time/working_time.py @@ -43,7 +43,8 @@ def on_submit(self): def create_attendance(self): if not frappe.db.exists( - "Attendance", {"employee": self.employee, "attendance_date": self.date, "docstatus": ("!=", 2)} + "Attendance", + {"employee": self.employee, "attendance_date": self.date, "docstatus": ("!=", 2)}, ): HALF_DAY = frappe.get_value("Employee", self.employee, "expected_daily_working_hours") / 2 OVERTIME_FACTOR = 1.15 @@ -136,8 +137,11 @@ def calculate_total_times(time_logs, user_indicated_break_time): total_time += duration - mandatory_break_time = calcualte_mandatory_break_time( - total_working_time, total_indicated_break_time + mandatory_break_time = calculate_mandatory_break_time( + total_working_time + if total_indicated_break_time + else total_working_time - user_indicated_break_time, + total_indicated_break_time or user_indicated_break_time, ) actual_break_time = max( mandatory_break_time, total_indicated_break_time or user_indicated_break_time @@ -154,12 +158,28 @@ def calculate_total_times(time_logs, user_indicated_break_time): ) -def calcualte_mandatory_break_time(working_time, break_time): - if not frappe.db.get_single_value("Working Time Settings", "enforce_mandatory_breaks"): - return 0 - elif working_time + break_time > 9.75 * ONE_HOUR or working_time > 9 * ONE_HOUR: - return 45 * 60 - elif working_time + break_time > 6.5 * ONE_HOUR or working_time > 6 * ONE_HOUR: - return 30 * 60 - else: +def calculate_mandatory_break_time(working_time, break_time): + # TODO: Write comprehensive tests to cover all edge cases + settings = frappe.get_single("Working Time Settings") + + if not settings.enforce_mandatory_breaks: return 0 + + break_cases = sorted( + (entry.working_time, entry.additional_break_time) for entry in settings.mandatory_breaks + ) + + total_mandatory_break = 0 + + for threshold, mandatory_break in break_cases: + if ( + working_time + break_time > threshold + mandatory_break + total_mandatory_break + or working_time > threshold + ): + total_mandatory_break += mandatory_break + + if total_mandatory_break > break_time: + working_time = working_time - total_mandatory_break + break_time + break_time = total_mandatory_break + + return total_mandatory_break diff --git a/arbeitszeiterfassung_s4a/arbeitszeiterfassung_s4a/doctype/working_time_settings/working_time_settings.json b/arbeitszeiterfassung_s4a/arbeitszeiterfassung_s4a/doctype/working_time_settings/working_time_settings.json index 490bc28..14dae4e 100644 --- a/arbeitszeiterfassung_s4a/arbeitszeiterfassung_s4a/doctype/working_time_settings/working_time_settings.json +++ b/arbeitszeiterfassung_s4a/arbeitszeiterfassung_s4a/doctype/working_time_settings/working_time_settings.json @@ -8,7 +8,9 @@ "engine": "InnoDB", "field_order": [ "default_activity", - "enforce_mandatory_breaks" + "mandatory_breaks_tab", + "enforce_mandatory_breaks", + "mandatory_breaks" ], "fields": [ { @@ -23,12 +25,24 @@ "fieldname": "enforce_mandatory_breaks", "fieldtype": "Check", "label": "Enforce Mandatory Breaks" + }, + { + "depends_on": "eval:doc.enforce_mandatory_breaks", + "fieldname": "mandatory_breaks", + "fieldtype": "Table", + "label": "Mandatory Breaks", + "options": "Mandatory Break" + }, + { + "fieldname": "mandatory_breaks_tab", + "fieldtype": "Tab Break", + "label": "Mandatory Breaks" } ], "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2024-09-27 12:24:23.603346", + "modified": "2024-09-28 16:20:12.769126", "modified_by": "Administrator", "module": "Arbeitszeiterfassung S4A", "name": "Working Time Settings", diff --git a/arbeitszeiterfassung_s4a/translations/de.csv b/arbeitszeiterfassung_s4a/translations/de.csv index bc33898..0b3f898 100644 --- a/arbeitszeiterfassung_s4a/translations/de.csv +++ b/arbeitszeiterfassung_s4a/translations/de.csv @@ -2,8 +2,11 @@ Working Time Settings,Arbeitszeiteinstellungen, Default Activity,Standardtätigkeit, Enforce Mandatory Breaks,Verpflichtende Pausen erzwingen, Break,Pause, +Additional Break,Zusätzliche Pause, Indicated Break,Angegebene Pause, Mandatory Break,Verpflichtende Pause, +Mandatory Breaks,Verpflichtende Pausen, Total Time,Gesamtzeit, Working Time,Arbeitszeit, +Working Time (Greater Than),Arbeitszeit (Größer als), Project Time,Projektzeit,