diff --git a/adm/forms.py b/adm/forms.py
index 55b6422..aeacad6 100644
--- a/adm/forms.py
+++ b/adm/forms.py
@@ -1,6 +1,8 @@
from django import forms
from django.forms import ModelForm
from users.models import AgencyBills
+from timemanagement.models import Absence, AbsenceReason, FreeDays, Workday, Breaks
+from bootstrap_datepicker_plus import DatePickerInput
class AgencyBillForm(forms.ModelForm):
@@ -11,3 +13,45 @@ class AgencyBillForm(forms.ModelForm):
'agency' : "Agentur",
'start' : "Leistungszeitraum Start",
}
+
+class AdmWorkdayForm(forms.ModelForm):
+
+ class Meta:
+ model = Workday
+ labels = {
+ "start" : "Start",
+ "end" : "Ende",
+ "target" : "Zielarbeitszeit",
+ "freefield" : "Notiz"
+ }
+
+ fields = [
+ "start", "end", "target", "freefield"
+ ]
+ widgets = {
+ 'start': DatePickerInput(options={"format":'DD.MM.YYYY HH:mm', "locale":'de'}),
+ 'end': DatePickerInput(options={"format":'DD.MM.YYYY HH:mm', "locale":'de'}),
+ }
+
+# ADD BREAK FORM
+class AdmBreakAddForm(forms.ModelForm):
+ class Meta:
+ model = Breaks
+ labels = {
+ "start" : "Start",
+ "end" : "Ende"
+ }
+
+ fields = [
+ "start", "end"
+ ]
+ widgets = {
+ 'start': DatePickerInput(options={"format":'DD.MM.YYYY HH:mm', "locale":'de'}),
+ 'end': DatePickerInput(options={"format":'DD.MM.YYYY HH:mm', "locale":'de'}),
+ }
+
+ def __init__(self, *arg, **kwargs):
+ super(AdmBreakAddForm, self).__init__(*arg, **kwargs)
+ self.fields['start'].required = True
+ self.fields['end'].required = True
+
diff --git a/adm/templates/adm/adm_break_add.html b/adm/templates/adm/adm_break_add.html
new file mode 100644
index 0000000..955b310
--- /dev/null
+++ b/adm/templates/adm/adm_break_add.html
@@ -0,0 +1,22 @@
+{% extends "adm/adm_base.html" %}
+{% block content %}
+{% load crispy_forms_tags %}
+{% if request.user.profile.agency.module_timemanagement %}
+
+
+
Zum Arbeitstag am {{workday.start|date:"d.m.Y"}} Pause hinzufügen
+
+
+
+{% endif %}
+{% endblock content %}
\ No newline at end of file
diff --git a/adm/templates/adm/adm_break_delete.html b/adm/templates/adm/adm_break_delete.html
new file mode 100644
index 0000000..4cfa48c
--- /dev/null
+++ b/adm/templates/adm/adm_break_delete.html
@@ -0,0 +1,13 @@
+{% extends "adm/adm_base.html" %}
+{% block content %}
+{% load crispy_forms_tags %}
+
+
Pause des Arbeitstags von {{object.user.get_full_name}} am {{object.workday.start|date:"d.m.Y"}} löschen?
+
+
+{% endblock content %}
diff --git a/adm/templates/adm/adm_user_single.html b/adm/templates/adm/adm_user_single.html
index 12f9ec5..277139c 100644
--- a/adm/templates/adm/adm_user_single.html
+++ b/adm/templates/adm/adm_user_single.html
@@ -144,22 +144,26 @@
Arbeitstage
+
+ Arbeitstag
+
- |
+ ID |
Start |
Ende |
Ziel |
+ |
{% for wd in workdays %}
- | {{forloop.counter}} |
+ {{wd.pk}} |
{{wd.start|date:"d.m.Y H:i"}} |
{{wd.end|date:"d.m.Y H:i"}} |
{{wd.target}} |
+ |
{% endfor %}
@@ -203,7 +207,7 @@
- |
+ ID |
Art |
Start |
Ende |
@@ -220,7 +224,7 @@
{% for ab in absences %}
- | {{forloop.counter}} |
+ {{ab.pk}} |
{{ab.reason.name}} |
{{ab.start|date:"d.m.Y"}} |
{{ab.end|date:"d.m.Y"}} |
diff --git a/adm/templates/adm/adm_workday_add.html b/adm/templates/adm/adm_workday_add.html
new file mode 100644
index 0000000..44b77b9
--- /dev/null
+++ b/adm/templates/adm/adm_workday_add.html
@@ -0,0 +1,23 @@
+{% extends "adm/adm_base.html" %}
+{% block content %}
+{% load crispy_forms_tags %}
+{% if request.user.profile.agency.module_timemanagement %}
+
+
+
Arbeitstag für {{user.get_full_name}} erstellen
+
+
+
+{% endif %}
+{% endblock content %}
\ No newline at end of file
diff --git a/adm/templates/adm/adm_workday_delete.html b/adm/templates/adm/adm_workday_delete.html
new file mode 100644
index 0000000..a7092fa
--- /dev/null
+++ b/adm/templates/adm/adm_workday_delete.html
@@ -0,0 +1,13 @@
+{% extends "adm/adm_base.html" %}
+{% block content %}
+{% load crispy_forms_tags %}
+
+
Arbeitstag von {{object.user.get_full_name}} am {{object.start|date:"d.m.Y"}} löschen?
+
+
+{% endblock content %}
diff --git a/adm/templates/adm/adm_workday_update.html b/adm/templates/adm/adm_workday_update.html
new file mode 100644
index 0000000..b444a52
--- /dev/null
+++ b/adm/templates/adm/adm_workday_update.html
@@ -0,0 +1,67 @@
+{% extends "adm/adm_base.html" %}
+{% block content %}
+{% load crispy_forms_tags %}
+
+
Arbeitstag von {{object.user.get_full_name}} am {{object.start|date:"d.m.Y"}} aktualisieren
+
+
+
+{% endblock content %}
diff --git a/adm/urls.py b/adm/urls.py
index 5fcaf1e..fe07012 100644
--- a/adm/urls.py
+++ b/adm/urls.py
@@ -17,5 +17,11 @@ urlpatterns = [
path('usersingle/
', AdmUserSingle.as_view(), name="adm-user-single"),
path('cron/', statisticCronJob, name="adm-cron"),
path('getorders/', getCSVRDOrders, name="getorders"),
- path('adm/addbill', AdmAddBill.as_view(), name="admbill-add")
+ path('adm/addbill', AdmAddBill.as_view(), name="admbill-add"),
+ path('wd//update', AdmWorkdayUpdate.as_view(), name="adm-workday-update"),
+ path('wd/add/', AdmWorkdayAdd.as_view(), name="adm-workday-add"),
+ path('wd//delete', AdmWorkdayDelete.as_view(), name="adm-workday-delete"),
+ path('wd/break//delete', AdmBreakDelete.as_view(), name="adm-break-delete"),
+ path('wd//break/add', AdmAddBreak.as_view(), name="adm-break-add"),
+
]
diff --git a/adm/views.py b/adm/views.py
index 11ff67d..e3ee5a5 100644
--- a/adm/views.py
+++ b/adm/views.py
@@ -13,9 +13,9 @@ import csv, os
from auditlog.models import LogEntry
import json
from users.models import UserYearAbsenceInfo, UserTime
-from timemanagement.models import Workday, Absence
+from timemanagement.models import Workday, Absence, Breaks
from recoverdir.models import *
-from .forms import AgencyBillForm
+from .forms import AgencyBillForm, AdmWorkdayForm, AdmBreakAddForm
from datetime import date, timedelta, datetime
from organizer.models import QuickLinks, AGContacts, AGPassword
from django.core.mail import EmailMessage
@@ -547,8 +547,6 @@ def getLexOfficeBill(billid):
return returnvalue
-
-
def convert_size(size_bytes):
if size_bytes == 0:
return "0B"
@@ -561,10 +559,98 @@ def convert_size(size_bytes):
-
-
-
-
+''' WORKDAY VIEWS
+
+ Hier sind alle Views für Arbeitstage und Pausen (Create, Update, Delete)
+
+'''
+
+class AdmWorkdayAdd(CreateView):
+ model = Workday
+ template_name = "adm/adm_workday_add.html"
+ form_class = AdmWorkdayForm
+
+ def form_valid(self, form):
+ wd_user = User.objects.get(pk=self.kwargs['uspk'])
+ wd = Workday(user=wd_user, agency=wd_user.profile.agency, start=form.cleaned_data['start'], end=form.cleaned_data['end'], target=form.cleaned_data["target"], freefield=form.cleaned_data["freefield"])
+ wd.save()
+ return HttpResponseRedirect(self.get_success_url())
+
+
+ def get_context_data(self, **kwargs):
+ context = super().get_context_data(**kwargs)
+ context.update({'active_link' : 'adm-users'})
+ context.update({'user' : User.objects.get(pk=self.kwargs['uspk'])})
+ return context
+
+ def get_success_url(self):
+ return reverse('adm-user-single', kwargs={'uspk': self.kwargs['uspk']})
+
+
+
+class AdmWorkdayUpdate(UpdateView):
+ model = Workday
+ form_class = AdmWorkdayForm
+ template_name = "adm/adm_workday_update.html"
+
+ def get_context_data(self, **kwargs):
+ context = super().get_context_data(**kwargs)
+ context.update({'active_link' : 'adm-users'})
+ return context
+
+ def get_success_url(self):
+ return reverse('adm-user-single', kwargs={'uspk': self.get_object().user.pk})
+
+
+class AdmWorkdayDelete(DeleteView):
+ model = Workday
+ template_name = "adm/adm_workday_delete.html"
+
+ def get_success_url(self):
+ return reverse('adm-user-single', kwargs={'uspk': self.get_object().user.pk})
+
+ def get_context_data(self, **kwargs):
+ context = super().get_context_data(**kwargs)
+ context.update({'active_link' : 'adm-users'})
+ return context
+
+
+
+class AdmBreakDelete(DeleteView):
+ model = Breaks
+ template_name = "adm/adm_break_delete.html"
+
+ def get_success_url(self):
+ return reverse('adm-workday-update', kwargs={'pk': self.get_object().workday.pk})
+
+ def get_context_data(self, **kwargs):
+ context = super().get_context_data(**kwargs)
+ context.update({'active_link' : 'adm-users'})
+ return context
+
+
+
+class AdmAddBreak(CreateView):
+ model = Breaks
+ template_name = "adm/adm_break_add.html"
+ form_class = AdmBreakAddForm
+
+ def form_valid(self, form):
+ wd = Workday.objects.get(pk=self.kwargs['pk'])
+ b = Breaks(user=wd.user, agency=wd.user.profile.agency, workday=wd, start=form.cleaned_data["start"], end=form.cleaned_data["end"])
+ b.save()
+ wd.breaks.add(b)
+ wd.save()
+ return HttpResponseRedirect(self.get_success_url())
+
+ def get_context_data(self, **kwargs):
+ context = super().get_context_data(**kwargs)
+ context.update({'active_link' : 'adm-users'})
+ context.update({'workday' : Workday.objects.get(pk=self.kwargs['pk'])})
+ return context
+
+ def get_success_url(self):
+ return reverse('adm-workday-update', kwargs={'pk': self.kwargs['pk']})
diff --git a/digitaleagentur/timemanagement_utils.py b/digitaleagentur/timemanagement_utils.py
new file mode 100644
index 0000000..4b097b5
--- /dev/null
+++ b/digitaleagentur/timemanagement_utils.py
@@ -0,0 +1,41 @@
+from datetime import timedelta
+
+'''
+
+ Hier sind alle Methoden gesammelt, die bei der Zeiterfassung eine Rolle spielen.
+
+
+ daterange()
+ - Gibt jeden Tag zwischen zwei Daten zurück
+
+ @param:
+ date1 - Startdatum
+ date2 - Enddatum
+
+ @return:
+ Array mit den entsprechenden Tagen
+
+ getIsAbsenceStartEndHalf()
+ - Gibt True zurücke, wenn der Anfang oder das Ende einer Abwesenheit nur ein halber Tag ist, ansonsten False
+
+ @param:
+ absence - Die zu prüfende Abwesenheit
+
+ @return:
+ True -> Ist nur ein halber Tag
+ False -> Ist ein ganzer Tag
+
+'''
+
+# Gibt die Woche als Wochentage zurück
+def daterange(date1, date2):
+ for n in range(int ((date2 - date1).days)+1):
+ yield date1 + timedelta(n)
+
+
+# Gibt True zurück, wenn eine Tag einer Abwesenheit nur ein halber Tag ist
+def getIsAbsenceStartEndHalf(absence):
+ if absence.startday_info == "1" or absence.startday_info == "2" or absence.endday_info == "1" or absence.endday_info == "2":
+ return True
+ else:
+ return False
\ No newline at end of file
diff --git a/digitaleagentur/utils.py b/digitaleagentur/utils.py
new file mode 100644
index 0000000..6ce8d3f
--- /dev/null
+++ b/digitaleagentur/utils.py
@@ -0,0 +1,78 @@
+from timemanagement.models import *
+from digitaleagentur.timemanagement_utils import *
+'''
+
+ Hier sind Funktion implementiert, die in verschiedenen Module benötigt werden
+
+
+ getAbsenceForOneDay - Gibt Abwesenheit eines Users für einen Tag zurück
+
+
+'''
+
+# getAbsenceForOneDay
+'''
+
+ Gibt eine Abwesenheit für einen übergebenen Tag zurück oder False, wenn keine Abwesenheit vorliegt.
+
+ @param:
+ - user (der entsprechende Nutzer)
+ - day (Tag, welcher auf Abwesenheiten geprüft werden soll)
+
+'''
+def getAbsenceForOneDay(user, day):
+ absencedays = Absence.objects.filter(agency=user.profile.agency, user=user, confirm_status=0) & (Absence.objects.filter(agency=user.profile.agency, user=user, start=day) | (Absence.objects.filter(agency=user.profile.agency, user=user, start__lt=day) & Absence.objects.filter(agency=user.profile.agency, user=user, end__gt=day)) | Absence.objects.filter(agency=user.profile.agency, user=user, end=day))
+
+ # Gibt es eine Abwesenheit an diesem Tag, welche einen halben Tag ist, dann gibt die Methode True zurück und bricht die Schleife ab!
+ for ab in absencedays:
+ if ab.startday_info == "1" or ab.startday_info == "2" or ab.endday_info == "1" or ab.endday_info == "2":
+ return True
+
+ # Es gibt an diesem Tag keine Abwesenheit mit einem halben Tag
+ return False
+
+'''
+
+
+# checkAbsenceWorkdayCollide()
+
+
+ Prüft, ob eine aktualisierte Abwesenheit Einfluss auf bereits bestehende Arbeitstage hat. Wenn zB nachträglich Arbeitstage eingetragen werden, dann würden diese hier angepasst werden.
+
+ Folgende Fälle werden berücksichtigt:
+ - Halber Tag der Abwesenheit verringert die Zielarbeitszeit dieses Tags auf die Hälfte
+ - Abwesenheit ist der komplette Tag, dann wird dieser Arbeitstag gelöscht, wenn es is_time false ist, sprich die Zeiterfassung soll nicht angefasst werden
+ - Abwesenheit ist kompletter Tag und die Abwesenheit soll Zeiterfassung beeinflussen (z.B. Gleitzeit) dann wird der Arbeitstag nicht verändert. Ist die Gleitzeit ein halber Tag, wird die Zielarbeitszeit halbiert.
+'''
+def checkAbsenceWorkdayCollide(absence):
+ # Alle einzelnen Tage der Abwesenheit werden durchgegangen:
+ for day in daterange(absence.start, absence.end):
+ # Arbeitstage an diesem Tag werden geladen
+ workdays = Workday.objects.filter(user=absence.user, start__day=absence.start.day, start__month=absence.start.month, start__year=absence.start.year)
+
+ # Wenn es Arbeitstage gibt, dann wird geprüft, ob die Abwesenheit diesen verändert hat.
+ for workday in workdays:
+ # Arbeitstag in Tag ohne Zeit umwandeln
+ # Wenn die Abwesenheit die Zeiterfassung NICHT ändert, muss diese ggf. geändert werden. Ansonsten bleibt sie gleich.
+ if absence.reason.is_time == False:
+ # Prüfung, ob der Tag halb ist oder nicht. Wenn ja, dann Zielarbeitszeit des Tages um die Hälfte reduzieren.
+ if (workday.start.day == absence.start.day and workday.start.month == absence.start.month and workday.start.year == absence.start.year) or (workday.end.day == absence.end.day and workday.end.month == absence.end.month and workday.end.year == absence.end.year):
+ if(getIsAbsenceStartEndHalf(absence)):
+ workday.target = workday.target / 2
+ workday.save()
+ # Ganzer Tag vorhanden, Arbeitstag wird gelöscht
+ else:
+ workday.delete()
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dump.rdb b/dump.rdb
index c660362..6c9e157 100644
Binary files a/dump.rdb and b/dump.rdb differ
diff --git a/requirements.txt b/requirements.txt
index 17e7ac2..502092a 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -26,8 +26,8 @@ mysqlclient==2.0.1
Pillow==6.2.1
pycparser==2.20
python-bidi==0.4.2
-python-dateutil==2.6.0
python-magic-bin==0.4.14
+python-dateutil==2.6.0
pytz==2019.3
requests==2.22.0
requests-oauthlib==1.3.0
diff --git a/timemanagement/forms.py b/timemanagement/forms.py
index 03013e9..fc91872 100644
--- a/timemanagement/forms.py
+++ b/timemanagement/forms.py
@@ -45,8 +45,6 @@ class AddAbsence(forms.ModelForm):
self.fields['activeyear'] = forms.CharField(initial="", required=False, widget=forms.HiddenInput())
self.fields['userid'] = forms.CharField(initial="", required=False, widget=forms.HiddenInput())
-
-
class UpdateAbsence(forms.ModelForm):
class Meta:
diff --git a/timemanagement/templates/timemanagement/timemanagement_singleview.html b/timemanagement/templates/timemanagement/timemanagement_singleview.html
index a388f95..fb85cd5 100644
--- a/timemanagement/templates/timemanagement/timemanagement_singleview.html
+++ b/timemanagement/templates/timemanagement/timemanagement_singleview.html
@@ -35,6 +35,8 @@
{% for da in days_this_month %}
+
+
{% getabscenceday request.user request.user da as abday %}
0.0 and user.usertime.usetime_start < today):
+ if(workdays_yesterday == 0 and (absencecheck(user, yesterday) == False or day_half_found) and targettworktime > 0.0 and user.usertime.usetime_start < today):
workdaytemp = Workday(user=user, agency=user.profile.agency, start=datetime(yesterday.year, yesterday.month, yesterday.day, 8, 0), end=datetime(yesterday.year, yesterday.month, yesterday.day, 8, 0), target=targettworktime)
workdaytemp.save()
except:
@@ -1680,7 +1685,6 @@ def cronactionsdaily(request, code):
#["htrampe@gmail.com"],
fail_silently=True,
)
-
return JsonResponse(data)
#import datetime