Skip to content

Commit

Permalink
Merge pull request #5 from alyf-de/drc-26
Browse files Browse the repository at this point in the history
feat: add option to enforce mandatory breaks (DRC-26)
  • Loading branch information
PatrickDEissler authored Oct 11, 2024
2 parents 1d5b0d8 + 7d013e5 commit fcf2689
Show file tree
Hide file tree
Showing 8 changed files with 214 additions and 13 deletions.
Empty file.
Original file line number Diff line number Diff line change
@@ -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": []
}
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ frappe.ui.form.on("Working Time", {
});

frappe.ui.form.on("Working Time Log", {
is_break: function (frm) {
frm.refresh_fields();
},
time_logs_add: function (frm, cdt, cdn) {
let current_row = locals[cdt][cdn];
let index = frm.doc.time_logs.findIndex((row) => row.name === current_row.name);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,14 @@
"date",
"time_logs",
"section_break_7",
"indicated_break_time",
"mandatory_break_time",
"break_time",
"column_break_amiy",
"total_time",
"working_time",
"project_time",
"section_break_syac",
"amended_from"
],
"fields": [
Expand Down Expand Up @@ -60,6 +65,7 @@
"read_only": 1
},
{
"bold": 1,
"fieldname": "working_time",
"fieldtype": "Duration",
"hide_days": 1,
Expand All @@ -75,6 +81,7 @@
"options": "Working Time Log"
},
{
"bold": 1,
"fieldname": "break_time",
"fieldtype": "Duration",
"hide_days": 1,
Expand All @@ -89,6 +96,8 @@
{
"fieldname": "project_time",
"fieldtype": "Duration",
"hide_days": 1,
"hide_seconds": 1,
"label": "Project Time",
"read_only": 1
},
Expand All @@ -99,6 +108,38 @@
"label": "Department",
"options": "Department",
"read_only": 1
},
{
"fieldname": "column_break_amiy",
"fieldtype": "Column Break"
},
{
"fieldname": "section_break_syac",
"fieldtype": "Section Break"
},
{
"fieldname": "indicated_break_time",
"fieldtype": "Duration",
"hide_days": 1,
"hide_seconds": 1,
"label": "Indicated Break",
"read_only_depends_on": "eval:doc.time_logs.some(row => row.is_break === 1)"
},
{
"fieldname": "mandatory_break_time",
"fieldtype": "Duration",
"hide_days": 1,
"hide_seconds": 1,
"label": "Mandatory Break",
"read_only": 1
},
{
"fieldname": "total_time",
"fieldtype": "Duration",
"hide_days": 1,
"hide_seconds": 1,
"label": "Total Time",
"read_only": 1
}
],
"is_submittable": 1,
Expand All @@ -112,7 +153,7 @@
"link_fieldname": "working_time"
}
],
"modified": "2023-06-22 12:25:38.722346",
"modified": "2024-09-27 11:50:43.070779",
"modified_by": "Administrator",
"module": "Arbeitszeiterfassung S4A",
"name": "Working Time",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,17 @@ def get_default_activity():

class WorkingTime(Document):
def before_validate(self):
self.break_time = self.working_time = self.project_time = 0
for log in self.time_logs:
log.set_duration()
duration = log.duration or 0
self.break_time += duration if log.is_break else 0
self.working_time += 0 if log.is_break else duration
self.project_time += duration if log.project and not log.is_break else 0
self.set_total_times()

def set_total_times(self):
(
self.total_time,
self.working_time,
self.project_time,
self.indicated_break_time,
self.mandatory_break_time,
self.break_time,
) = calculate_total_times(self.time_logs, self.indicated_break_time or 0)

def validate(self):
for log in self.time_logs:
Expand All @@ -39,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
Expand Down Expand Up @@ -111,3 +116,70 @@ def get_costing_rate(employee):
{"activity_type": get_default_activity(), "employee": employee},
"costing_rate",
)


def calculate_total_times(time_logs, user_indicated_break_time):
total_time = 0
total_working_time = 0
total_project_time = 0
total_indicated_break_time = 0

for log in time_logs:
log.set_duration()
duration = log.duration or 0

if log.is_break:
total_indicated_break_time += duration
else:
total_working_time += duration
if log.project:
total_project_time += duration

total_time += duration

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
)
adjusted_working_time = total_time - actual_break_time

return (
total_time,
adjusted_working_time,
total_project_time,
total_indicated_break_time or user_indicated_break_time,
mandatory_break_time,
actual_break_time,
)


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
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,42 @@
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"default_activity"
"default_activity",
"mandatory_breaks_tab",
"enforce_mandatory_breaks",
"mandatory_breaks"
],
"fields": [
{
"fieldname": "default_activity",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Default Activity",
"options": "Activity Type",
"reqd": 1
"options": "Activity Type"
},
{
"default": "0",
"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": "2023-08-02 17:24:48.248467",
"modified": "2024-09-28 16:20:12.769126",
"modified_by": "Administrator",
"module": "Arbeitszeiterfassung S4A",
"name": "Working Time Settings",
Expand Down
12 changes: 12 additions & 0 deletions arbeitszeiterfassung_s4a/translations/de.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
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,

0 comments on commit fcf2689

Please sign in to comment.