Zusätzliche Übersichtsansicht im Adminbereich
Genehmigungsfunktion für Urlaube mit automatischer Eintragung
This commit is contained in:
parent
57eec6d4f1
commit
a12bd1e15a
106
lib/admin.py
106
lib/admin.py
@ -41,9 +41,12 @@ def page_admin():
|
||||
|
||||
ui.button("Logout", on_click=admin_logout)
|
||||
|
||||
updates_available = ValueBinder()
|
||||
updates_available.value = False
|
||||
|
||||
with ui.tabs() as tabs:
|
||||
|
||||
time_overview = ui.tab('Zeitübersichten')
|
||||
time_overview = ui.tab('Zeitdaten')
|
||||
settings = ui.tab('Einstellungen')
|
||||
users = ui.tab('Benutzer')
|
||||
backups = ui.tab('Backups')
|
||||
@ -59,6 +62,13 @@ def page_admin():
|
||||
with ui.tab_panels(tabs, value=time_overview):
|
||||
|
||||
with ui.tab_panel(time_overview):
|
||||
with ui.tabs() as overview_tabs:
|
||||
user_month_overview = ui.tab('Monatsansicht')
|
||||
user_summary = ui.tab("Zusammenfassung")
|
||||
vacation_applications = ui.tab("Urlaubsanträge")
|
||||
|
||||
with ui.tab_panels(overview_tabs, value = user_month_overview):
|
||||
with ui.tab_panel(user_month_overview).classes('w-full'):
|
||||
ui.markdown("##Übersichten")
|
||||
|
||||
# Tabelle konstruieren
|
||||
@ -83,6 +93,7 @@ def page_admin():
|
||||
def update_user():
|
||||
current_user = user(time_user.value)
|
||||
available_years = current_user.get_years()
|
||||
|
||||
try:
|
||||
select_year.clear()
|
||||
select_year.set_options(available_years)
|
||||
@ -91,6 +102,7 @@ def page_admin():
|
||||
select_year.value = str(datetime.datetime.now().year)
|
||||
except:
|
||||
select_year.value = list(available_years)[0]
|
||||
update_months()
|
||||
try:
|
||||
select_month.value = datetime.datetime.now().month
|
||||
except:
|
||||
@ -601,9 +613,100 @@ Dies kann nicht rückgängig gemacht werden!''')
|
||||
month_header.set_content(f"###Buchungen für **{current_user.fullname}** für **{calendar.month_name[int(select_month.value)]} {select_year.value}**")
|
||||
|
||||
month_header.set_content(f"###Buchungen für **{current_user.fullname}** für **{calendar.month_name[int(select_month.value)]} {select_year.value}**")
|
||||
|
||||
timetable()
|
||||
button_update = ui.button("Aktualisieren", on_click=timetable.refresh)
|
||||
button_update.move(timetable_header)
|
||||
with ui.tab_panel(user_summary):
|
||||
|
||||
global overview_table
|
||||
@ui.refreshable
|
||||
def overview_table():
|
||||
ov_columns = [ {'label': 'Benutzername', 'name': 'username', 'field': 'username'},
|
||||
{'label': 'Name', 'name': 'name', 'field': 'name'},
|
||||
{'label': 'Zeitsaldo', 'name': 'time', 'field': 'time'},
|
||||
{'label': 'Urlaub', 'name': 'vacation', 'field': 'vacation'},
|
||||
{'label': 'Resturlaub', 'name': 'vacation_remaining', 'field': 'vacation_remaining'},
|
||||
{'label': 'Krankheit', 'name': 'illness', 'field': 'illness'}]
|
||||
ov_rows = [ ]
|
||||
for username in userlist:
|
||||
actual_user = user(username)
|
||||
ov_rows.append(
|
||||
{'username': username, 'name': actual_user.fullname, 'time': f'{convert_seconds_to_hours(actual_user.get_last_months_overtime() + actual_user.get_worked_time()[0])} h', 'vacation': f'{actual_user.count_absence_days("U")} Tage',
|
||||
'vacation_remaining': f'{actual_user.get_vacation_claim() - actual_user.count_absence_days("U")} Tage', 'illness': f'{actual_user.count_absence_days("K")} Tage'})
|
||||
ui.table(columns=ov_columns, rows=ov_rows, row_key='username', column_defaults={'align': 'left', 'sortable': True})
|
||||
|
||||
overview_table()
|
||||
ui.button("Aktualisieren", on_click=overview_table.refresh)
|
||||
|
||||
with ui.tab_panel(vacation_applications):
|
||||
date_string = '%d.%m.%Y'
|
||||
|
||||
@ui.refreshable
|
||||
def va_table():
|
||||
va_columns = [ {'label': 'Benutzername', 'name': 'username', 'field': 'username'},
|
||||
{'label': 'Name', 'name': 'name', 'field': 'name'},
|
||||
{'label': 'key', 'name': 'key', 'field': 'key', 'classes': 'hidden', 'headerClasses': 'hidden'},
|
||||
{'label': 'Anfang', 'name': 'start', 'field': 'start'},
|
||||
{'label': 'Ende', 'name': 'end', 'field': 'end'},
|
||||
{'label': 'Resturlaub', 'name': 'vacation_remaining', 'field': 'vacation_remaining'},
|
||||
{'label': 'Resturlaub nach Genehmigung', 'name': 'vacation_remaining_after_submission', 'field': 'vacation_remaining_after_submission'}
|
||||
]
|
||||
va_rows = [ ]
|
||||
|
||||
for username in userlist:
|
||||
actual_user = user(username)
|
||||
open_va = actual_user.get_open_vacation_applications()
|
||||
for i, dates in open_va.items():
|
||||
startdate_dt = datetime.datetime.strptime(dates[0], '%Y-%m-%d')
|
||||
enddate_dt = datetime.datetime.strptime(dates[1], '%Y-%m-%d')
|
||||
vacation_start_to_end = (enddate_dt-startdate_dt).days
|
||||
vacation_duration = 0
|
||||
for day_counter in range(0, vacation_start_to_end):
|
||||
new_date = startdate_dt + datetime.timedelta(days=day_counter)
|
||||
if int(actual_user.get_day_workhours(new_date.year, new_date.month, new_date.day)) > 0:
|
||||
if not datetime.datetime(new_date.year, new_date.month, new_date.day).strftime('%Y-%m-%d') in list(load_adminsettings()["holidays"]):
|
||||
vacation_duration += 1
|
||||
|
||||
va_rows.append({'username': username,
|
||||
'name': actual_user.fullname,
|
||||
'key': f'{username}-{i}',
|
||||
'start': startdate_dt.strftime(date_string),
|
||||
'end': enddate_dt.strftime(date_string),
|
||||
'vacation_remaining': f'{actual_user.get_vacation_claim() - actual_user.count_absence_days("U", startdate_dt.year)} Tage',
|
||||
'vacation_remaining_after_submission': f'{actual_user.get_vacation_claim() - actual_user.count_absence_days("U", startdate_dt.year) - vacation_duration } Tage'})
|
||||
global vacation_table
|
||||
vacation_table = ui.table(columns=va_columns, rows=va_rows, row_key='key', selection='multiple', column_defaults={'align': 'left', 'sortable': True})
|
||||
va_table()
|
||||
|
||||
def approve_vacation():
|
||||
global vacation_table
|
||||
for selection in vacation_table.selected:
|
||||
key = selection["key"]
|
||||
username = key.split("-")[0]
|
||||
index = key.split("-")[1]
|
||||
actual_user = user(username)
|
||||
startdate_dt = datetime.datetime.strptime(selection["start"], date_string)
|
||||
enddate_dt = datetime.datetime.strptime(selection["end"], date_string)
|
||||
delta = (enddate_dt - startdate_dt).days
|
||||
for i in range(0, delta):
|
||||
new_date = startdate_dt + datetime.timedelta(days=i)
|
||||
if int(actual_user.get_day_workhours(new_date.year, new_date.month, new_date.day)) > 0:
|
||||
if not datetime.datetime(new_date.year, new_date.month, new_date.day).strftime('%Y-%m-%d') in list(load_adminsettings()["holidays"]):
|
||||
actual_user.update_absence(new_date.year, new_date.month, new_date.day, "U")
|
||||
ui.notify(f"Urlaub vom {selection['start']} bis {selection['end']} für {actual_user.fullname} eingetragen")
|
||||
try:
|
||||
retract_result = actual_user.revoke_vacation_application(index)
|
||||
except IndexError:
|
||||
ui.notify("Fehler beim Entfernen des Urlaubsantrages nach dem Eintragen.")
|
||||
va_table.refresh()
|
||||
timetable.refresh()
|
||||
|
||||
ui.button("Aktualisieren", on_click=va_table.refresh)
|
||||
ui.separator()
|
||||
with ui.grid(columns=2):
|
||||
ui.button("Genehmigen", on_click=approve_vacation)
|
||||
ui.button("Ablehnen")
|
||||
|
||||
with ui.tab_panel(settings):
|
||||
with ui.grid(columns='auto auto'):
|
||||
@ -1240,6 +1343,7 @@ Dies kann nicht rückgängig gemacht werden!''')
|
||||
ui.button("Speichern", on_click=save_workhours)
|
||||
ui.button("Löschen", on_click=delete_workhour_entry)
|
||||
user_selection_changed()
|
||||
|
||||
with ui.tab_panel(backups):
|
||||
|
||||
try:
|
||||
|
@ -525,7 +525,7 @@ def json_info(api_key: str):
|
||||
data["time"]["overall"] = time_saldo
|
||||
data["vacation"] = { }
|
||||
data["vacation"]["claim"] = current_user.get_vacation_claim(now_dt.year, now_dt.month, now_dt.day)
|
||||
data["vacation"]["used"] = current_user.count_vacation_days(now_dt.year)
|
||||
data["vacation"]["used"] = current_user.count_absence_days("U", now_dt.year)
|
||||
data["vacation"]["remaining"] = data["vacation"]["claim"] - data["vacation"]["used"]
|
||||
return data
|
||||
break
|
||||
|
@ -262,8 +262,9 @@ def homepage():
|
||||
va_table = ui.table(columns=va_columns, rows=va_rows, selection="single", row_key="index").classes('w-full')
|
||||
def retract_va():
|
||||
try:
|
||||
current_user.revoke_vacation_application(va_table.selected[0]["index"])
|
||||
retract_result = current_user.revoke_vacation_application(va_table.selected[0]["index"])
|
||||
open_vacation_applications.refresh()
|
||||
if retract_result == 0:
|
||||
ui.notify("Urlaubsantrag zurückgezogen")
|
||||
except IndexError:
|
||||
ui.notify("Kein Urlaubsanstrag ausgewählt")
|
||||
|
22
lib/users.py
22
lib/users.py
@ -5,6 +5,8 @@ import hashlib
|
||||
import os
|
||||
from calendar import monthrange
|
||||
from stat import S_IREAD, S_IWUSR
|
||||
from nicegui import ui
|
||||
|
||||
import datetime
|
||||
import time
|
||||
import json
|
||||
@ -61,7 +63,6 @@ class user:
|
||||
with open(filename, 'a') as file:
|
||||
# Schreibe den Timestamp in die Datei und füge einen Zeilenumbruch hinzu
|
||||
file.write(f"{timestamp}\n")
|
||||
file.close()
|
||||
except FileNotFoundError:
|
||||
# Fehlende Verzeichnisse anlegen
|
||||
folder_path = os.path.dirname(filename)
|
||||
@ -263,7 +264,7 @@ class user:
|
||||
filename_txt = os.path.join(self.userfolder, f"{year}-{month}.txt")
|
||||
os.chmod(filename_txt, S_IREAD)
|
||||
|
||||
def get_last_months_overtime(self, year, month):
|
||||
def get_last_months_overtime(self, year=datetime.datetime.now().year, month=datetime.datetime.now().month):
|
||||
try:
|
||||
if int(month) == 1:
|
||||
year = str(int(year) - 1)
|
||||
@ -387,7 +388,7 @@ class user:
|
||||
hours_to_work = -1
|
||||
return hours_to_work
|
||||
|
||||
def get_vacation_claim(self, year, month, day):
|
||||
def get_vacation_claim(self, year=datetime.datetime.now().year, month=datetime.datetime.now().month, day=datetime.datetime.now().day):
|
||||
workhour_entries = list(self.workhours)
|
||||
workhour_entries.sort()
|
||||
day_to_check = datetime.datetime(int(year), int(month), int(day))
|
||||
@ -405,18 +406,18 @@ class user:
|
||||
|
||||
return int(claim)
|
||||
|
||||
def count_vacation_days(self, year):
|
||||
vacation_used = 0
|
||||
def count_absence_days(self, absence_code: str, year=datetime.datetime.now().year):
|
||||
absence_days = 0
|
||||
for month in range(0, 13):
|
||||
try:
|
||||
absence_dict = self.get_absence(year, month)
|
||||
for entry, absence_type in absence_dict.items():
|
||||
if absence_type == "U":
|
||||
vacation_used += 1
|
||||
if absence_type == absence_code:
|
||||
absence_days += 1
|
||||
|
||||
except:
|
||||
pass
|
||||
return vacation_used
|
||||
return absence_days
|
||||
|
||||
def delete_photo(self):
|
||||
os.remove(self.photofile)
|
||||
@ -477,6 +478,7 @@ class user:
|
||||
application_file = os.path.join(self.userfolder, va_file)
|
||||
with open(application_file, 'r') as json_file:
|
||||
applications = json.load(json_file)
|
||||
try:
|
||||
del(applications[index])
|
||||
new_applications = { }
|
||||
new_index = 0
|
||||
@ -485,6 +487,10 @@ class user:
|
||||
new_index += 1
|
||||
with open(application_file, 'w') as json_file:
|
||||
json_file.write(json.dumps(new_applications, indent=4))
|
||||
return 0
|
||||
except KeyError:
|
||||
ui.notify("Urlaubsantrag wurde schon bearbeitet")
|
||||
return -1
|
||||
|
||||
# Benutzer auflisten
|
||||
def list_users():
|
||||
|
@ -71,6 +71,7 @@
|
||||
"2030-10-03": "Tag der deutschen Einheit",
|
||||
"2030-10-30": "Reformationstag",
|
||||
"2030-12-25": "1. Weihnachtsfeiertag",
|
||||
"2030-12-26": "2. Weihnachtsfeiertag"
|
||||
"2030-12-26": "2. Weihnachtsfeiertag",
|
||||
"2025-06-11": "Testeintrag"
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user