Modulcommit Zeiterfassung
|
|
@ -2,6 +2,8 @@ media/agencymain/*
|
||||||
media/agencydata/*
|
media/agencydata/*
|
||||||
!media/default.jpg
|
!media/default.jpg
|
||||||
!media/ag_default.jpg
|
!media/ag_default.jpg
|
||||||
|
!media/agencymain/default.jpg
|
||||||
|
!media/agencymain/ag_default.jpg
|
||||||
!media/agencymain/linkdefault.png
|
!media/agencymain/linkdefault.png
|
||||||
|
|
||||||
digitaleagentur/__pycache__/*
|
digitaleagentur/__pycache__/*
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from users.models import AgencyGroup, Agency, Profile, AgencyJob, AgencyNetwork
|
from users.models import AgencyGroup, Agency, Profile, AgencyJob, AgencyNetwork, UserTime
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
from bootstrap_datepicker_plus import DatePickerInput
|
||||||
|
|
||||||
class AgencyOrganigrammForm(forms.ModelForm):
|
class AgencyOrganigrammForm(forms.ModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
|
|
@ -48,6 +49,25 @@ class UsersNotificationForm(forms.ModelForm):
|
||||||
#fields = ['news_mail', 'news_push', 'user_standard_public_mail', 'user_standard_public_push', 'agency_new_standard_mail', 'agency_new_standard_push', 'add_new_group_mail', 'add_new_group_push', 'add_task_mail', 'add_task_push', 'user_messages_mail', 'user_messages_push']
|
#fields = ['news_mail', 'news_push', 'user_standard_public_mail', 'user_standard_public_push', 'agency_new_standard_mail', 'agency_new_standard_push', 'add_new_group_mail', 'add_new_group_push', 'add_task_mail', 'add_task_push', 'user_messages_mail', 'user_messages_push']
|
||||||
fields = ['news_mail', 'news_push', 'agency_new_standard_mail', 'agency_new_standard_push', 'add_new_group_mail', 'add_new_group_push', 'add_task_mail', 'add_task_push', 'user_messages_mail', 'user_messages_push']
|
fields = ['news_mail', 'news_push', 'agency_new_standard_mail', 'agency_new_standard_push', 'add_new_group_mail', 'add_new_group_push', 'add_task_mail', 'add_task_push', 'user_messages_mail', 'user_messages_push']
|
||||||
|
|
||||||
|
# Usertime Form
|
||||||
|
class UserTimeForm(forms.ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = UserTime
|
||||||
|
labels = {
|
||||||
|
"holiday" : "Urlaubstage",
|
||||||
|
"loose_holidedate" : "Urlaubstage aus Vorjahr verfallen am",
|
||||||
|
"wd_mo" : "Montag",
|
||||||
|
"wd_tu" : "Dienstag",
|
||||||
|
"wd_we" : "Mittwoch",
|
||||||
|
"wd_th" : "Donnerstag",
|
||||||
|
"wd_fr" : "Freitag",
|
||||||
|
}
|
||||||
|
fields = ["holiday", "loose_holidedate", "wd_mo", "wd_tu", "wd_we", "wd_th", "wd_fr"]
|
||||||
|
widgets = {
|
||||||
|
'loose_holidedate': DatePickerInput(options={"format":'DD.MM.YYYY', "locale":'de'})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
# PERMISSION GROUPS FORM
|
# PERMISSION GROUPS FORM
|
||||||
class AgencyGroupPerms(forms.Form):
|
class AgencyGroupPerms(forms.Form):
|
||||||
'''
|
'''
|
||||||
|
|
@ -77,8 +97,9 @@ class AgencyModulsForm(forms.ModelForm):
|
||||||
'module_files' : "Dateien",
|
'module_files' : "Dateien",
|
||||||
'module_organigramm' : "Organigramm",
|
'module_organigramm' : "Organigramm",
|
||||||
'module_messages' : "Mitteilungen",
|
'module_messages' : "Mitteilungen",
|
||||||
|
'module_timemanagement' : "Zeiterfassung",
|
||||||
}
|
}
|
||||||
fields = ['module_news','module_organizer','module_files','module_organigramm', 'module_messages']
|
fields = ['module_news','module_organizer','module_files','module_organigramm', 'module_messages', 'module_timemanagement']
|
||||||
|
|
||||||
# NEW USER FORM
|
# NEW USER FORM
|
||||||
class UserNewUserForm(forms.ModelForm):
|
class UserNewUserForm(forms.ModelForm):
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-4">
|
<div class="col-4">
|
||||||
{% for perm in perms %}
|
{% for perm in perms %}
|
||||||
{% if forloop.counter|divisibleby:7 %}
|
{% if forloop.counter|divisibleby:8 %}
|
||||||
</div>
|
</div>
|
||||||
<div class="col-4">
|
<div class="col-4">
|
||||||
<div class="custom-control custom-checkbox mb-2 {{perm.name}}">
|
<div class="custom-control custom-checkbox mb-2 {{perm.name}}">
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{formfield.label_tag}}</td>
|
<td>{{formfield.label_tag}}</td>
|
||||||
<td>{{formfield}}</td>
|
<td>{{formfield}}</td>
|
||||||
<td><button type="button" class="btn btn-sm btn-primary" onclick="javascript:$('#modulesettings_{{formfield.name}}').modal('toggle');"><i class="fas fa-cog"></i></button></td>
|
<td>{% if formfield.name == 'module_organigramm' %}<button type="button" class="btn btn-sm btn-primary" onclick="javascript:$('#modulesettings_{{formfield.name}}').modal('toggle');"><i class="fas fa-cog"></i></button>{% endif %}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|
@ -26,7 +26,6 @@
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
{% for formfield in modulform %}
|
{% for formfield in modulform %}
|
||||||
<div class="modal fade" id="modulesettings_{{formfield.name}}" tabindex="-1" role="dialog" data-backdrop="static" aria-labelledby="" aria-hidden="true">
|
<div class="modal fade" id="modulesettings_{{formfield.name}}" tabindex="-1" role="dialog" data-backdrop="static" aria-labelledby="" aria-hidden="true">
|
||||||
<div class="modal-dialog " role="document">
|
<div class="modal-dialog " role="document">
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,7 @@
|
||||||
</p>
|
</p>
|
||||||
<h6><b>E-Mail</b></h6>
|
<h6><b>E-Mail</b></h6>
|
||||||
<p>
|
<p>
|
||||||
<span id="user_email">{{ mail }}</span> <button onclick="javascript:ChangeMail()" style="float: right" type="button" class="btn btn-secondary btn-sm" id="changemailbutton"><i class="fas fa-pen"></i></button>
|
<span id="user_email">{{ mail }}</span> <button onclick="javascript:ChangeMail()" style="float: right" type="button" class="btn btn-secondary btn-sm" id="changemailbutton"><i class="fas fa-pen"></i></button>
|
||||||
</p>
|
</p>
|
||||||
<div style="float: left">
|
<div style="float: left">
|
||||||
<button type="button" id="" onclick="javascript:sendPassMail({{vieweduser}})" class="btn-primary btn-sm active" >Passwort wiederherstellen</button>
|
<button type="button" id="" onclick="javascript:sendPassMail({{vieweduser}})" class="btn-primary btn-sm active" >Passwort wiederherstellen</button>
|
||||||
|
|
@ -130,18 +130,18 @@
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-6">
|
<div class="col-6">
|
||||||
|
<input type="hidden" name="form_type" value="profileform">
|
||||||
{% for field in profileform %}
|
{% for field in profileform %}
|
||||||
{% if forloop.counter|divisibleby:6 %}
|
{% if forloop.counter|divisibleby:6 %}
|
||||||
</div>
|
</div>
|
||||||
<div class="col-6">
|
<div class="col-6">
|
||||||
{{field|as_crispy_field}}
|
{{field|as_crispy_field}}
|
||||||
{% else %}
|
{% else %}
|
||||||
{{field|as_crispy_field}}
|
{{field|as_crispy_field}}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
{% if newuser == 1 %}
|
{% if newuser == 1 %}
|
||||||
<button type="submit" class="btn btn-primary" style="float: right">Profilerstellung abschließen</button>
|
<button type="submit" class="btn btn-primary" style="float: right">Profilerstellung abschließen</button>
|
||||||
|
|
@ -149,19 +149,50 @@
|
||||||
<button type="submit" name="submitprof" class="btn btn-primary" style="float: right">Profil Aktualisieren</button>
|
<button type="submit" name="submitprof" class="btn btn-primary" style="float: right">Profil Aktualisieren</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<a class="btn" href="{% url 'dasettings' %} ">Profilbearbeitung abbrechen</a>
|
<a class="btn" href="{% url 'dasettings' %} ">Profilbearbeitung abbrechen</a>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="tab-pane fade" id="contract" role="tabpanel" aria-labelledby="contract-tab">
|
||||||
|
|
||||||
|
|
||||||
|
<div class="col-9 mt-2" style="margin-left: -10px;">
|
||||||
|
<form method="POST" enctype="multipart/form-data" name="usertime_basic">
|
||||||
|
{% csrf_token %}
|
||||||
|
<input type="hidden" name="form_type" value="contract">
|
||||||
|
|
||||||
|
<p>Arbeitszeiten <small><i data-toggle="tooltip" data-placement="top" title="Legen Sie fest, an welchen Tagen dieser Mitarbeiter wie viele Stunden arbeitet." class="far fa-question-circle"></i></small></p>
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table">
|
||||||
|
<tr>
|
||||||
|
<td>{{usertime_form.wd_mo|as_crispy_field}}</td>
|
||||||
|
<td>{{usertime_form.wd_tu|as_crispy_field}}</td>
|
||||||
|
<td>{{usertime_form.wd_we|as_crispy_field}}</td>
|
||||||
|
<td>{{usertime_form.wd_th|as_crispy_field}}</td>
|
||||||
|
<td>{{usertime_form.wd_fr|as_crispy_field}}</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<hr style="margin-top: -20px;">
|
||||||
|
<div class="col-5" >
|
||||||
|
<p>Urlaub <small><i data-toggle="tooltip" data-placement="top" title="Legen Sie fest, wie viel Urlaub dieser Mitarbeiter im Jahr hat." class="far fa-question-circle"></i></small></p>
|
||||||
|
{{usertime_form.media}}
|
||||||
|
{{usertime_form.holiday|as_crispy_field}}
|
||||||
|
{{usertime_form.loose_holidedate|as_crispy_field}}
|
||||||
|
</div>
|
||||||
|
<div class="col-12">
|
||||||
|
<hr>
|
||||||
|
<button type="submit" class="btn btn-primary" style="float: right">Vertragsdaten aktualisieren</button>
|
||||||
|
<a class="btn" href="{% url 'dasettings' %} ">Abbrechen</a>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
<br />
|
||||||
<div class="tab-pane fade" id="contract" role="tabpanel" aria-labelledby="contract-tab">
|
<br />
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,14 @@
|
||||||
from django.shortcuts import render, redirect
|
from django.shortcuts import render, redirect
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
from django.http import HttpResponseRedirect,HttpResponse, JsonResponse
|
from django.http import HttpResponseRedirect,HttpResponse, JsonResponse
|
||||||
from .forms import UsersSelfChangeForm, UsersNotificationForm, AgencyGroupPerms, AgencyModulsForm, UserNewUserForm, UserProfileForm, AgencyNetworkForm, AgencyOrganigrammForm
|
from .forms import UsersSelfChangeForm, UsersNotificationForm, AgencyGroupPerms, AgencyModulsForm, UserNewUserForm, UserProfileForm, AgencyNetworkForm, AgencyOrganigrammForm, UserTimeForm
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.auth import update_session_auth_hash
|
from django.contrib.auth import update_session_auth_hash
|
||||||
from django.contrib.auth.forms import PasswordChangeForm
|
from django.contrib.auth.forms import PasswordChangeForm
|
||||||
from users.usersforms import AgencyUpdateForm
|
from users.usersforms import AgencyUpdateForm
|
||||||
from users.models import AgencyJob, AgencyGroup, AgencyNetwork, Agency, AgencyNetworkPreperation
|
from users.models import AgencyJob, AgencyGroup, AgencyNetwork, Agency, AgencyNetworkPreperation
|
||||||
from django.contrib.auth.models import User, Group, Permission
|
from django.contrib.auth.models import User, Group, Permission
|
||||||
|
from users.models import UserTime
|
||||||
import random
|
import random
|
||||||
import string
|
import string
|
||||||
from django.template.loader import render_to_string
|
from django.template.loader import render_to_string
|
||||||
|
|
@ -501,40 +502,70 @@ def UserProfileUpdate(request, pk, newuser=0):
|
||||||
usertochange.profile.image = request.FILES['image']
|
usertochange.profile.image = request.FILES['image']
|
||||||
formtosave = False
|
formtosave = False
|
||||||
|
|
||||||
formtosave = UserProfileForm(request.POST, instance=usertochange.profile)
|
if(request.POST["form_type"] == "profileform"):
|
||||||
|
|
||||||
if formtosave.is_valid():
|
formtosave = UserProfileForm(request.POST, instance=usertochange.profile)
|
||||||
try:
|
|
||||||
usertochange.profile.parent = User.objects.get(pk=request.POST['usertoparent'], profile__agency=request.user.profile.agency)
|
if formtosave.is_valid():
|
||||||
usertochange.save()
|
try:
|
||||||
except Exception as e:
|
usertochange.profile.parent = User.objects.get(pk=request.POST['usertoparent'], profile__agency=request.user.profile.agency)
|
||||||
usertochange.profile.parent = None
|
usertochange.save()
|
||||||
usertochange.save()
|
except Exception as e:
|
||||||
|
usertochange.profile.parent = None
|
||||||
|
usertochange.save()
|
||||||
|
|
||||||
|
formtosave.save()
|
||||||
|
messages.success(request, f'Profil gespeichert!')
|
||||||
|
return redirect('dasettings')
|
||||||
|
else:
|
||||||
|
messages.success(request, f'Fehlerhafte Eingabe!')
|
||||||
|
context = {
|
||||||
|
'active_link' : 'dasettings',
|
||||||
|
'user_fullname' : user_fullname,
|
||||||
|
'first_name' : usertochange.first_name,
|
||||||
|
'last_name' : usertochange.last_name,
|
||||||
|
'usertime_form' : UserTimeForm(instance=UserTime.objects.get(user=usertochange)),
|
||||||
|
'newuser' : newuser,
|
||||||
|
'vieweduser' : usertochange.pk,
|
||||||
|
'parentuser' : parentuser,
|
||||||
|
'mail' : usertochange.email,
|
||||||
|
'imagelink' : usertochange.profile.get_photo_url,
|
||||||
|
'profileform' : UserProfileForm(instance=usertochange.profile),
|
||||||
|
'usertoparent' : User.objects.filter(profile__agency__pk=usertochange.profile.agency.pk, profile__visible=True)
|
||||||
|
}
|
||||||
|
return render(request, 'dasettings/user_usprof.html', context)
|
||||||
|
elif(request.POST["form_type"] == "contract"):
|
||||||
|
|
||||||
|
formtosave = UserTimeForm(request.POST, instance=UserTime.objects.get(user=usertochange))
|
||||||
|
print(formtosave)
|
||||||
|
if(formtosave.is_valid()):
|
||||||
|
messages.success(request, f'Vertragsdaten gespeichert!')
|
||||||
|
formtosave.save()
|
||||||
|
return redirect('dasettings')
|
||||||
|
else:
|
||||||
|
messages.success(request, f'Fehlerhafte Eingabe!')
|
||||||
|
context = {
|
||||||
|
'active_link' : 'dasettings',
|
||||||
|
'user_fullname' : user_fullname,
|
||||||
|
'first_name' : usertochange.first_name,
|
||||||
|
'last_name' : usertochange.last_name,
|
||||||
|
'usertime_form' : UserTimeForm(instance=UserTime.objects.get(user=usertochange)),
|
||||||
|
'newuser' : newuser,
|
||||||
|
'vieweduser' : usertochange.pk,
|
||||||
|
'parentuser' : parentuser,
|
||||||
|
'mail' : usertochange.email,
|
||||||
|
'imagelink' : usertochange.profile.get_photo_url,
|
||||||
|
'profileform' : UserProfileForm(instance=usertochange.profile),
|
||||||
|
'usertoparent' : User.objects.filter(profile__agency__pk=usertochange.profile.agency.pk, profile__visible=True)
|
||||||
|
}
|
||||||
|
return render(request, 'dasettings/user_usprof.html', context)
|
||||||
|
|
||||||
formtosave.save()
|
|
||||||
messages.success(request, f'Profil gespeichert!')
|
|
||||||
return redirect('dasettings')
|
|
||||||
else:
|
|
||||||
messages.success(request, f'Fehlerhafte Eingabe!')
|
|
||||||
context = {
|
|
||||||
'active_link' : 'dasettings',
|
|
||||||
'user_fullname' : user_fullname,
|
|
||||||
'first_name' : usertochange.first_name,
|
|
||||||
'last_name' : usertochange.last_name,
|
|
||||||
'newuser' : newuser,
|
|
||||||
'vieweduser' : usertochange.pk,
|
|
||||||
'parentuser' : parentuser,
|
|
||||||
'mail' : usertochange.email,
|
|
||||||
'imagelink' : usertochange.profile.get_photo_url,
|
|
||||||
'profileform' : UserProfileForm(instance=usertochange.profile),
|
|
||||||
'usertoparent' : User.objects.filter(profile__agency__pk=usertochange.profile.agency.pk, profile__visible=True)
|
|
||||||
}
|
|
||||||
return render(request, 'dasettings/user_usprof.html', context)
|
|
||||||
else:
|
else:
|
||||||
|
|
||||||
context = {
|
context = {
|
||||||
'active_link' : 'dasettings',
|
'active_link' : 'dasettings',
|
||||||
'user_fullname' : user_fullname,
|
'user_fullname' : user_fullname,
|
||||||
|
'usertime_form' : UserTimeForm(instance=UserTime.objects.get(user=usertochange)),
|
||||||
'first_name' : usertochange.first_name,
|
'first_name' : usertochange.first_name,
|
||||||
'last_name' : usertochange.last_name,
|
'last_name' : usertochange.last_name,
|
||||||
'newuser' : newuser,
|
'newuser' : newuser,
|
||||||
|
|
|
||||||
|
|
@ -72,6 +72,7 @@ INSTALLED_APPS = [
|
||||||
'tasks.apps.TasksConfig',
|
'tasks.apps.TasksConfig',
|
||||||
'organizer.apps.OrganizerConfig',
|
'organizer.apps.OrganizerConfig',
|
||||||
'standards.apps.StandardsConfig',
|
'standards.apps.StandardsConfig',
|
||||||
|
'timemanagement.apps.TimemanagementConfig',
|
||||||
'news.apps.NewsConfig',
|
'news.apps.NewsConfig',
|
||||||
'crispy_forms',
|
'crispy_forms',
|
||||||
'colorful',
|
'colorful',
|
||||||
|
|
@ -83,7 +84,7 @@ INSTALLED_APPS = [
|
||||||
'django.contrib.contenttypes',
|
'django.contrib.contenttypes',
|
||||||
'django.contrib.sessions',
|
'django.contrib.sessions',
|
||||||
'django.contrib.messages',
|
'django.contrib.messages',
|
||||||
'django.contrib.staticfiles',
|
'django.contrib.staticfiles',
|
||||||
'bootstrap_datepicker_plus',
|
'bootstrap_datepicker_plus',
|
||||||
'django_cleanup',
|
'django_cleanup',
|
||||||
'django_user_agents',
|
'django_user_agents',
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,8 @@ urlpatterns = [
|
||||||
path('register/', registerNewAgency, name='register'),
|
path('register/', registerNewAgency, name='register'),
|
||||||
path('register/done', views.registerdone, name='register-done'),
|
path('register/done', views.registerdone, name='register-done'),
|
||||||
path('summernote/', include('django_summernote.urls')),
|
path('summernote/', include('django_summernote.urls')),
|
||||||
path('notifications/', include('notificsys.urls'), name="notifications")
|
path('notifications/', include('notificsys.urls'), name="notifications"),
|
||||||
|
path('tm/', include('timemanagement.urls'), name="timemanagement")
|
||||||
] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
|
] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
|
||||||
if settings.DEBUG:
|
if settings.DEBUG:
|
||||||
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||||
|
|
|
||||||
|
After Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 5.9 KiB |
|
|
@ -100,12 +100,19 @@
|
||||||
</div>
|
</div>
|
||||||
<hr>
|
<hr>
|
||||||
<div id="commentsection">
|
<div id="commentsection">
|
||||||
|
<style type="text/css">
|
||||||
|
.commentimg {
|
||||||
|
border-radius: 50%;
|
||||||
|
width: 8%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
{% for comment in comments %}
|
{% for comment in comments %}
|
||||||
{% getcommentstat_user comment.pk request.user as cstat %}
|
{% getcommentstat_user comment.pk request.user as cstat %}
|
||||||
|
|
||||||
<div id="comment_{{comment.pk}}">
|
<div id="comment_{{comment.pk}}">
|
||||||
<small>Von {{comment.comment_by.first_name}} {{comment.comment_by.last_name}} am {{comment.last_modified_on|date:"d.m.Y H:i"}}</small><br />
|
<small>
|
||||||
|
<img class="commentimg" src="{{ comment.comment_by.profile.get_photo_url }}">
|
||||||
|
{{comment.comment_by.first_name}} {{comment.comment_by.last_name}} am {{comment.last_modified_on|date:"d.m.Y H:i"}}</small><br />
|
||||||
{{comment.content}}<br/>
|
{{comment.content}}<br/>
|
||||||
<div class="mt-2">
|
<div class="mt-2">
|
||||||
<button type="button" id="comment_{{comment.pk}}_rate_down" class="btn btn-sm {% if cstat == 0 %} btn-primary {% elif cstat == 'nostat' %} btn-secondary {% endif %}" {% if comment.comment_by == request.user %} disabled="true" {% endif %} onclick="javascript:rateComment({{comment.pk}}, 0)">
|
<button type="button" id="comment_{{comment.pk}}_rate_down" class="btn btn-sm {% if cstat == 0 %} btn-primary {% elif cstat == 'nostat' %} btn-secondary {% endif %}" {% if comment.comment_by == request.user %} disabled="true" {% endif %} onclick="javascript:rateComment({{comment.pk}}, 0)">
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,12 @@ from django import template
|
||||||
from django.contrib.auth.models import Group, User
|
from django.contrib.auth.models import Group, User
|
||||||
from users.models import AgencyGroup, Agency, AgencyNetwork, AgencyNetworkPreperation
|
from users.models import AgencyGroup, Agency, AgencyNetwork, AgencyNetworkPreperation
|
||||||
from standards.models import Standards, StandardCommentRate, StandardComments
|
from standards.models import Standards, StandardCommentRate, StandardComments
|
||||||
|
from timemanagement.models import Workday
|
||||||
from message.models import Message
|
from message.models import Message
|
||||||
import os
|
import os
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.utils import timezone
|
||||||
|
from datetime import date
|
||||||
|
|
||||||
register = template.Library()
|
register = template.Library()
|
||||||
|
|
||||||
|
|
@ -260,4 +263,97 @@ def isUserInRep(task, area, user_id):
|
||||||
if len(st_auth) > 0:
|
if len(st_auth) > 0:
|
||||||
found = True
|
found = True
|
||||||
|
|
||||||
return found
|
return found
|
||||||
|
|
||||||
|
# REALTIME
|
||||||
|
# Check for active WorkDay
|
||||||
|
@register.simple_tag
|
||||||
|
def getactualworkingday(user):
|
||||||
|
wd = Workday.objects.filter(user=user, agency=user.profile.agency, end=None)
|
||||||
|
returnstat = 0
|
||||||
|
|
||||||
|
if(len(wd) > 0):
|
||||||
|
returnstat = list(wd)[0].start
|
||||||
|
return returnstat
|
||||||
|
|
||||||
|
# Return formatted Time-String
|
||||||
|
@register.simple_tag
|
||||||
|
def getformatedstarttime(user):
|
||||||
|
wd = list(Workday.objects.filter(user=user, agency=user.profile.agency, end=None))[0]
|
||||||
|
return wd.start.strftime("%H:%M:%S")
|
||||||
|
|
||||||
|
@register.simple_tag
|
||||||
|
def getformattetstarttime_last_start(user):
|
||||||
|
today = date.today()
|
||||||
|
wd = Workday.objects.filter(user=user, agency=user.profile.agency, start__day=today.day).order_by("start")
|
||||||
|
|
||||||
|
if(len(wd) == 0):
|
||||||
|
return ("00:00:00")
|
||||||
|
else:
|
||||||
|
return list(wd)[0].start.strftime("%H:%M:%S")
|
||||||
|
|
||||||
|
@register.simple_tag
|
||||||
|
def getformattetstarttime_last_end(user):
|
||||||
|
today = date.today()
|
||||||
|
wd = Workday.objects.filter(user=user, agency=user.profile.agency, end__day=today.day).order_by("end")
|
||||||
|
|
||||||
|
if(len(wd) == 0):
|
||||||
|
return ("00:00:00")
|
||||||
|
else:
|
||||||
|
return list(wd)[0].end.strftime("%H:%M:%S")
|
||||||
|
|
||||||
|
@register.simple_tag
|
||||||
|
def getactualbreak(user):
|
||||||
|
wd = Workday.objects.filter(user=user, agency=user.profile.agency, end=None)
|
||||||
|
returnstat = 0
|
||||||
|
# ACTIVE WORKING DAY
|
||||||
|
if(len(wd) > 0):
|
||||||
|
wd = list(wd)[0]
|
||||||
|
|
||||||
|
# BREAK FOUND
|
||||||
|
if(len(wd.breaks.all()) > 0):
|
||||||
|
# Check if all Breaks ended
|
||||||
|
wdbreak = wd.breaks.filter(end=None)
|
||||||
|
if(len(wdbreak) > 0):
|
||||||
|
returnstat = True
|
||||||
|
|
||||||
|
else:
|
||||||
|
returnstat = False
|
||||||
|
return returnstat
|
||||||
|
|
||||||
|
@register.simple_tag
|
||||||
|
def getactualbreakcounter(user):
|
||||||
|
wd = Workday.objects.filter(user=user, agency=user.profile.agency, end=None)
|
||||||
|
returnstat = False
|
||||||
|
# ACTIVE WORKING DAY
|
||||||
|
if(len(wd) > 0):
|
||||||
|
wd = list(wd)[0]
|
||||||
|
|
||||||
|
if(len(wd.breaks.all()) > 0):
|
||||||
|
wdbreak = wd.breaks.filter(end=None)
|
||||||
|
if(len(wdbreak) > 0):
|
||||||
|
now = timezone.now()
|
||||||
|
breakstart = list(wdbreak)[0].start
|
||||||
|
returnstat = (now - breakstart).seconds * 1000
|
||||||
|
return returnstat
|
||||||
|
|
||||||
|
# GET ALL BREAK AS MILLISECOND-RESULT
|
||||||
|
@register.simple_tag
|
||||||
|
def getdailybreaktime(user):
|
||||||
|
wd = list(Workday.objects.filter(user=user, agency=user.profile.agency, end=None))[0]
|
||||||
|
breaksum = 0
|
||||||
|
for b in wd.breaks.all():
|
||||||
|
if(b.end != None):
|
||||||
|
breaksum += (b.end - b.start).seconds
|
||||||
|
return breaksum*1000
|
||||||
|
|
||||||
|
@register.simple_tag
|
||||||
|
def getdailybreaktimetoday(user):
|
||||||
|
today = date.today()
|
||||||
|
wd = list(Workday.objects.filter(user=user, agency=user.profile.agency, start__day=today.day).order_by("start"))[0]
|
||||||
|
breaksum = 0
|
||||||
|
for b in wd.breaks.all():
|
||||||
|
if(b.end != None):
|
||||||
|
breaksum += (b.end - b.start).seconds
|
||||||
|
return breaksum*1000
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
|
# Register your models here.
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class TimemanagementConfig(AppConfig):
|
||||||
|
name = 'timemanagement'
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
# Generated by Django 3.0.4 on 2020-04-26 15:22
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('users', '0022_auto_20200426_1522'),
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Breaks',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('start', models.DateTimeField(blank=True, default=None, null=True)),
|
||||||
|
('end', models.DateTimeField(blank=True, default=None, null=True)),
|
||||||
|
('agency', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='users.Agency')),
|
||||||
|
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Workday',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('start', models.DateTimeField(blank=True, default=None, null=True)),
|
||||||
|
('end', models.DateTimeField(blank=True, default=None, null=True)),
|
||||||
|
('agency', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='users.Agency')),
|
||||||
|
('breaks', models.ManyToManyField(blank=True, related_name='breaks_at_day', to='timemanagement.Breaks')),
|
||||||
|
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='breaks',
|
||||||
|
name='workday',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workdayele', to='timemanagement.Workday'),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
from django.db import models
|
||||||
|
from django.contrib.auth.models import User
|
||||||
|
from users.models import Agency
|
||||||
|
|
||||||
|
# Create your models here.
|
||||||
|
class Workday(models.Model):
|
||||||
|
user = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||||
|
agency = models.ForeignKey(Agency, on_delete=models.CASCADE)
|
||||||
|
breaks = models.ManyToManyField("Breaks", blank=True, related_name='breaks_at_day')
|
||||||
|
start = models.DateTimeField(default=None, null=True, blank=True)
|
||||||
|
end = models.DateTimeField(default=None, null=True, blank=True)
|
||||||
|
|
||||||
|
class Breaks(models.Model):
|
||||||
|
user = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||||
|
agency = models.ForeignKey(Agency, on_delete=models.CASCADE)
|
||||||
|
workday = models.ForeignKey("Workday", on_delete=models.CASCADE, related_name='workdayele')
|
||||||
|
start = models.DateTimeField(default=None, null=True, blank=True)
|
||||||
|
end = models.DateTimeField(default=None, null=True, blank=True)
|
||||||
|
|
||||||
|
|
@ -0,0 +1,264 @@
|
||||||
|
{% load counter_tag %}
|
||||||
|
<div class="mt-2 ml-2 text-center">
|
||||||
|
|
||||||
|
<div id="activeDay" style="display: none;">
|
||||||
|
<span id="worktime">
|
||||||
|
<h3>Heutiger Arbeitstag</h3>
|
||||||
|
<h2 id="realtimeclock">00:00</h2>
|
||||||
|
<hr>
|
||||||
|
</span>
|
||||||
|
<span id="breaktimeclock" style="display: none">
|
||||||
|
<h3>Aktuelle Pause</h3>
|
||||||
|
<h2 id="realtimeclock_break">00:00</h2>
|
||||||
|
<hr>
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
<button onclick="javascript:endBreak()" type="button" id="end_break" class="btn btn-success" style="display: none;"><i class="fas fa-play"></i></button>
|
||||||
|
<button onclick="javascript:startBreak()" type="button" id="start_break" class="btn btn-primary"><i class="fas fa-pause"></i></button>
|
||||||
|
<button onclick="javascript:endDay()" type="button" id="end_workday" class="btn btn-secondary">Arbeitstag beenden</button>
|
||||||
|
</span>
|
||||||
|
<hr>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
Arbeitsbeginn: <span id="starttime">00:00:00</span><br />
|
||||||
|
Arbeitsende: <span id="endtime">00:00:00</span><br />
|
||||||
|
Pausenzeit: <span id="breaksum">00:00:00</span>
|
||||||
|
</div>
|
||||||
|
<div id="start_workday">
|
||||||
|
<hr>
|
||||||
|
<button onclick="javascript:startDay()" type="button" id="" class="btn btn-success">Arbeitstag starten</button>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
Gleitzeitkonto: +00:13 Stunden
|
||||||
|
</div>
|
||||||
|
<script type="text/javascript">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
window.setInterval(function(){
|
||||||
|
if(starttime_view != false && isbreak == false){
|
||||||
|
realTimeClock();
|
||||||
|
}
|
||||||
|
else if(isbreak == true){
|
||||||
|
realTimeBreakClock();
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
function convertMS(ms) {
|
||||||
|
var d, h, m, s;
|
||||||
|
s = Math.floor(ms / 1000);
|
||||||
|
m = Math.floor(s / 60);
|
||||||
|
s = s % 60;
|
||||||
|
h = Math.floor(m / 60);
|
||||||
|
m = m % 60;
|
||||||
|
d = Math.floor(h / 24);
|
||||||
|
h = h % 24;
|
||||||
|
h += d * 24;
|
||||||
|
|
||||||
|
if(s < 10){
|
||||||
|
s = "0" + s;
|
||||||
|
}
|
||||||
|
if(m < 10){
|
||||||
|
m = "0" + m;
|
||||||
|
}
|
||||||
|
if(h < 10){
|
||||||
|
h = "0" + h;
|
||||||
|
}
|
||||||
|
|
||||||
|
return h + ':' + m + ':' + s;
|
||||||
|
}
|
||||||
|
|
||||||
|
{% getactualworkingday request.user as starttime %}
|
||||||
|
//No day info
|
||||||
|
{% if starttime == 0 %}
|
||||||
|
var starttime_view = false;
|
||||||
|
var startbreaktime_view = false;
|
||||||
|
var isbreak = false;
|
||||||
|
var breaktime = 0;
|
||||||
|
|
||||||
|
{% getformattetstarttime_last_start request.user as formattetstarttime_last_start %}
|
||||||
|
{% getformattetstarttime_last_end request.user as formattetstarttime_last_end %}
|
||||||
|
$("#starttime").html("{{formattetstarttime_last_start}}");
|
||||||
|
$("#endtime").html("{{formattetstarttime_last_end}}");
|
||||||
|
$("#breaksum").html("00:00:00");
|
||||||
|
//day info
|
||||||
|
{% else %}
|
||||||
|
|
||||||
|
{% getdailybreaktime request.user as actbreaktime %}
|
||||||
|
$("#breaksum").html(convertMS({{actbreaktime}}));
|
||||||
|
var breaktime = {{actbreaktime}};
|
||||||
|
|
||||||
|
{% getformatedstarttime request.user as formattetstarttime %}
|
||||||
|
{% getactualbreak request.user as breakcounter %}
|
||||||
|
|
||||||
|
//BREAK CHECKEN!
|
||||||
|
{% if breakcounter %}
|
||||||
|
|
||||||
|
{% getactualbreakcounter request.user as breaktimer %}
|
||||||
|
var isbreak = true;
|
||||||
|
var startbreaktime_view = {{breaktimer}};
|
||||||
|
var starttime_view = Date.parse("{{starttime}}");
|
||||||
|
$("#start_workday").hide();
|
||||||
|
$("#activeDay").show();
|
||||||
|
$("#worktime").hide();
|
||||||
|
$("#breaktimeclock").show();
|
||||||
|
$("#end_break").show();
|
||||||
|
$("#start_break").hide();
|
||||||
|
$("#starttime").html("{{formattetstarttime}}");
|
||||||
|
|
||||||
|
{% else %}
|
||||||
|
var isbreak = false;
|
||||||
|
var startbreaktime_view = 0;
|
||||||
|
var starttime_view = Date.parse("{{starttime}}");
|
||||||
|
|
||||||
|
$("#start_workday").hide();
|
||||||
|
$("#activeDay").show();
|
||||||
|
$("#starttime").html("{{formattetstarttime}}");
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
function realTimeClock(start=false)
|
||||||
|
{
|
||||||
|
if(start != false){
|
||||||
|
startDate = Date.parse(start);
|
||||||
|
starttime_view = startDate;
|
||||||
|
realTimeClock();
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
now = new Date();
|
||||||
|
viewtime = now - starttime_view - breaktime;
|
||||||
|
$("#realtimeclock").html(convertMS(viewtime));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function realTimeBreakClock(){
|
||||||
|
breaktime = startbreaktime_view + 1000;
|
||||||
|
startbreaktime_view = startbreaktime_view + 1000;
|
||||||
|
$("#realtimeclock_break").html(convertMS(startbreaktime_view));
|
||||||
|
}
|
||||||
|
|
||||||
|
function startBreak(){
|
||||||
|
|
||||||
|
isbreak = true;
|
||||||
|
$("#timemanagement_clock").click(function(e){
|
||||||
|
e.stopPropagation();
|
||||||
|
})
|
||||||
|
$("#breaktimeclock").show();
|
||||||
|
$("#realtimeclock").hide();
|
||||||
|
$("#end_break").show();
|
||||||
|
$("#start_break").hide();
|
||||||
|
$("#worktime").hide();
|
||||||
|
$.ajax(
|
||||||
|
{
|
||||||
|
type: "GET",
|
||||||
|
url: "{% url 'tm-ajax' %}",
|
||||||
|
data:{
|
||||||
|
action : "start_break",
|
||||||
|
},
|
||||||
|
success: function( data )
|
||||||
|
{
|
||||||
|
startbreaktime_view = -1000;
|
||||||
|
realTimeBreakClock();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function endBreak(){
|
||||||
|
isbreak = false;
|
||||||
|
$("#timemanagement_clock").click(function(e){
|
||||||
|
e.stopPropagation();
|
||||||
|
})
|
||||||
|
$("#breaktimeclock").hide();
|
||||||
|
$("#realtimeclock").show();
|
||||||
|
$("#end_break").hide();
|
||||||
|
$("#start_break").show();
|
||||||
|
$("#worktime").show();
|
||||||
|
$.ajax(
|
||||||
|
{
|
||||||
|
type: "GET",
|
||||||
|
url: "{% url 'tm-ajax' %}",
|
||||||
|
data:{
|
||||||
|
action : "end_break",
|
||||||
|
},
|
||||||
|
success: function( data )
|
||||||
|
{
|
||||||
|
breaktime = data["actualbreaktime"];
|
||||||
|
$("#breaksum").html(convertMS(data["actualbreaktime"]));
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function startDay()
|
||||||
|
{
|
||||||
|
$("#timemanagement_clock").click(function(e){
|
||||||
|
e.stopPropagation();
|
||||||
|
})
|
||||||
|
|
||||||
|
$("#start_workday").hide();
|
||||||
|
$("#activeDay").show();
|
||||||
|
$("#breaktimeclock").hide();
|
||||||
|
$.ajax(
|
||||||
|
{
|
||||||
|
type: "GET",
|
||||||
|
url: "{% url 'tm-ajax' %}",
|
||||||
|
data:{
|
||||||
|
action : "start_day",
|
||||||
|
},
|
||||||
|
success: function( data )
|
||||||
|
{
|
||||||
|
if(data["success"]){
|
||||||
|
$("#starttime").html(data["wd_starttime"])
|
||||||
|
$("#endtime").html("00:00:00");
|
||||||
|
$("#breaksum").html("00:00:00");
|
||||||
|
}
|
||||||
|
|
||||||
|
realTimeClock(data["wd_starttime_complete"]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function endDay()
|
||||||
|
{
|
||||||
|
$("#timemanagement_clock").click(function(e){
|
||||||
|
e.stopPropagation();
|
||||||
|
})
|
||||||
|
|
||||||
|
$.ajax(
|
||||||
|
{
|
||||||
|
type: "GET",
|
||||||
|
url: "{% url 'tm-ajax' %}",
|
||||||
|
data:{
|
||||||
|
action : "end_day",
|
||||||
|
},
|
||||||
|
success: function( data )
|
||||||
|
{
|
||||||
|
if(data["success"]){
|
||||||
|
$("#breaktimeclock").hide();
|
||||||
|
$("#realtimeclock").show();
|
||||||
|
$("#end_break").hide();
|
||||||
|
$("#start_break").show();
|
||||||
|
$("#worktime").show();
|
||||||
|
|
||||||
|
$("#endtime").html(data["wd_endtime"]);
|
||||||
|
$("#activeDay").hide();
|
||||||
|
$("#start_workday").show();
|
||||||
|
$("#breaksum").html(convertMS(data["actualbreaktime"]));
|
||||||
|
starttime_view = false;
|
||||||
|
isbreak = false;
|
||||||
|
breaktime = 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
realTimeClock(data["wd_starttime_complete"]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
{% extends "users/base.html" %}
|
||||||
|
{% block content %}
|
||||||
|
{% if request.user.profile.agency.module_timemanagement %}
|
||||||
|
<div class="content-section col-12">
|
||||||
|
<h3>Zeiterfassung <small><i data-toggle="tooltip" data-placement="top" title="Bearbeiten Sie hier Ihre Zeiterfassung." class="far fa-question-circle"></i></small></h3>
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
/* DATATABLES */
|
||||||
|
.paginate_button {
|
||||||
|
padding: 0px !important;
|
||||||
|
border: 0px !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<script>
|
||||||
|
$(document).ready(function(){
|
||||||
|
/*
|
||||||
|
$('#activenews').DataTable({
|
||||||
|
"language": {
|
||||||
|
"search" : "Suche",
|
||||||
|
"info": "Zeige _START_ bis _END_ von _TOTAL_ Einträgen",
|
||||||
|
"lengthMenu": "Zeige _MENU_ Einträge",
|
||||||
|
"zeroRecords": "Nichts gefunden",
|
||||||
|
"infoEmpty": "Keine Einträge",
|
||||||
|
"paginate": {
|
||||||
|
"first": "Erste",
|
||||||
|
"last": "Letzte",
|
||||||
|
"next": "Nächste",
|
||||||
|
"previous": "Zurück"
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"buttons" : {
|
||||||
|
"className" : "btn-danger"
|
||||||
|
}*/
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
</script>
|
||||||
|
{% else %}
|
||||||
|
<h3>Das Modul Zeiterfassung wurde in ihrer Agentur deaktiviert.</h3>
|
||||||
|
{% endif %}
|
||||||
|
{% endblock content %}
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
# Create your tests here.
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
from django.urls import path
|
||||||
|
from django.contrib.auth.decorators import login_required, permission_required
|
||||||
|
from .views import TimeManagement, TimeAjax
|
||||||
|
'''
|
||||||
|
Permissions definiert in models.py bei USERS und dann hier vor die View geschrieben!
|
||||||
|
'''
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
path('', TimeManagement, name='tm-management'),
|
||||||
|
path('ajax/', TimeAjax, name='tm-ajax'),
|
||||||
|
#path('newsadd/', permission_required('users.modulenews')(views.NewsAdd), name='news-add'),
|
||||||
|
#path('newsupdate/<int:id>/', permission_required('users.modulenews')(views.NewsUpdate), name='news-update'),
|
||||||
|
#path('news/<int:pk>/delete', permission_required('users.modulenews')(NewsDeleteView.as_view()), name='news-delete'),
|
||||||
|
|
||||||
|
#path('standard/<int:pk>/changestat', views.StandardChangePublic, name="standard-status"),
|
||||||
|
#path('news/<int:pk>/single', views.NewsSingle, name="news-single"),
|
||||||
|
#path('standard/<int:pk>/area', views.StandardArea, name="standard-area"),
|
||||||
|
#path('standard/<int:pk>/task', views.StandardTask, name="standard-task")
|
||||||
|
]
|
||||||
|
|
@ -0,0 +1,78 @@
|
||||||
|
from django.shortcuts import render
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.http import JsonResponse
|
||||||
|
from .models import Workday, Breaks
|
||||||
|
from django.utils import timezone
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def TimeManagement(request):
|
||||||
|
context = {
|
||||||
|
"active_link" : "timemanagement"
|
||||||
|
}
|
||||||
|
return render(request, 'timemanagement/timemanagement_management.html', context)
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def TimeAjax(request):
|
||||||
|
data = {}
|
||||||
|
if request.method == "GET":
|
||||||
|
if request.GET["action"] == "start_day":
|
||||||
|
wd = Workday(user=request.user, agency=request.user.profile.agency, start=timezone.now())
|
||||||
|
wd.save()
|
||||||
|
data = {
|
||||||
|
"success" : True,
|
||||||
|
"wd_starttime" : wd.start.strftime("%H:%M:%S"),
|
||||||
|
"wd_starttime_complete" : wd.start
|
||||||
|
}
|
||||||
|
elif request.GET["action"] == "end_day":
|
||||||
|
wd = list(Workday.objects.filter(user=request.user, agency=request.user.profile.agency, end=None))[0]
|
||||||
|
# END ALL BREAKS
|
||||||
|
for b in wd.breaks.all():
|
||||||
|
if b.end == None:
|
||||||
|
b.end = timezone.now()
|
||||||
|
b.save()
|
||||||
|
wd.end = timezone.now()
|
||||||
|
wd.save()
|
||||||
|
|
||||||
|
breaksum = 0
|
||||||
|
for b in wd.breaks.all():
|
||||||
|
if(b.end != None):
|
||||||
|
breaksum += (b.end - b.start).seconds
|
||||||
|
|
||||||
|
data = {
|
||||||
|
"success" : True,
|
||||||
|
"wd_endtime" : wd.end.strftime("%H:%M:%S"),
|
||||||
|
"actualbreaktime" : breaksum*1000
|
||||||
|
}
|
||||||
|
# START A BREAK
|
||||||
|
elif request.GET["action"] == "start_break":
|
||||||
|
wd = list(Workday.objects.filter(user=request.user, agency=request.user.profile.agency, end=None))[0]
|
||||||
|
newbreak = Breaks(workday=wd, user=request.user, agency=request.user.profile.agency, start=timezone.now())
|
||||||
|
newbreak.save()
|
||||||
|
wd.breaks.add(newbreak)
|
||||||
|
data = {
|
||||||
|
"success" : True,
|
||||||
|
"break_starttime" : newbreak.start,
|
||||||
|
}
|
||||||
|
elif request.GET["action"] == "end_break":
|
||||||
|
wd = list(Workday.objects.filter(user=request.user, agency=request.user.profile.agency, end=None))[0]
|
||||||
|
toendbreak = list(wd.breaks.filter(end=None))[0]
|
||||||
|
toendbreak.end = timezone.now()
|
||||||
|
toendbreak.save()
|
||||||
|
|
||||||
|
wd = list(Workday.objects.filter(user=request.user, agency=request.user.profile.agency, end=None))[0]
|
||||||
|
breaksum = 0
|
||||||
|
for b in wd.breaks.all():
|
||||||
|
if(b.end != None):
|
||||||
|
breaksum += (b.end - b.start).seconds
|
||||||
|
|
||||||
|
data = {
|
||||||
|
"success" : True,
|
||||||
|
"actualbreaktime" : breaksum*1000
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
data = {
|
||||||
|
"success" : False
|
||||||
|
}
|
||||||
|
|
||||||
|
return JsonResponse(data)
|
||||||
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from .models import Profile, Agency, AgencyGroup, AgencyJob, AgencyNetwork, AgencyNetworkPreperation
|
from .models import Profile, Agency, AgencyGroup, AgencyJob, AgencyNetwork, AgencyNetworkPreperation, UserTime
|
||||||
from .priomodel import Prio
|
from .priomodel import Prio
|
||||||
from standards.models import StandardCommentRate, StandardComments
|
from standards.models import StandardCommentRate, StandardComments
|
||||||
from django.contrib.auth.models import Permission
|
from django.contrib.auth.models import Permission
|
||||||
from message.models import Message
|
from message.models import Message
|
||||||
from cloud.models import DataFile
|
from cloud.models import DataFile
|
||||||
from organizer.models import AGContacts
|
from organizer.models import AGContacts
|
||||||
|
from timemanagement.models import Workday, Breaks
|
||||||
|
|
||||||
admin.site.register(StandardComments)
|
admin.site.register(StandardComments)
|
||||||
admin.site.register(StandardCommentRate)
|
admin.site.register(StandardCommentRate)
|
||||||
|
|
@ -21,4 +21,7 @@ admin.site.register(AgencyNetwork)
|
||||||
admin.site.register(AGContacts)
|
admin.site.register(AGContacts)
|
||||||
admin.site.register(AgencyNetworkPreperation)
|
admin.site.register(AgencyNetworkPreperation)
|
||||||
admin.site.register(DataFile)
|
admin.site.register(DataFile)
|
||||||
|
admin.site.register(UserTime)
|
||||||
|
admin.site.register(Workday)
|
||||||
|
admin.site.register(Breaks)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -88,6 +88,8 @@ class Agency(models.Model):
|
||||||
# MONEY
|
# MONEY
|
||||||
balance = models.FloatField(default=0.0, max_length=9, blank=True)
|
balance = models.FloatField(default=0.0, max_length=9, blank=True)
|
||||||
nextdebiting = models.DateTimeField(default=timezone.now, blank=True)
|
nextdebiting = models.DateTimeField(default=timezone.now, blank=True)
|
||||||
|
monthlyprice = models.FloatField(default=25.0, max_length=9, blank=True)
|
||||||
|
|
||||||
|
|
||||||
# MODULEEINSTELLUNGEN FÜR DIE AGENTUR
|
# MODULEEINSTELLUNGEN FÜR DIE AGENTUR
|
||||||
module_news = models.BooleanField(default=True)
|
module_news = models.BooleanField(default=True)
|
||||||
|
|
@ -95,6 +97,10 @@ class Agency(models.Model):
|
||||||
module_files = models.BooleanField(default=True)
|
module_files = models.BooleanField(default=True)
|
||||||
module_organigramm = models.BooleanField(default=True)
|
module_organigramm = models.BooleanField(default=True)
|
||||||
module_messages = models.BooleanField(default=True)
|
module_messages = models.BooleanField(default=True)
|
||||||
|
|
||||||
|
# KOSTENPFLICHTIGE MODULE
|
||||||
|
module_timemanagement = models.BooleanField(default=False)
|
||||||
|
module_timemanagement_price = models.FloatField(default=5.0, max_length=9, blank=True)
|
||||||
|
|
||||||
# Steckbrief dynamisch aus Standard
|
# Steckbrief dynamisch aus Standard
|
||||||
dynamicprofile = models.BooleanField(default=True)
|
dynamicprofile = models.BooleanField(default=True)
|
||||||
|
|
@ -158,7 +164,7 @@ class Profile(models.Model):
|
||||||
func = models.ForeignKey("AgencyJob", blank=True, null=True, default=None, on_delete=models.SET_NULL)
|
func = models.ForeignKey("AgencyJob", blank=True, null=True, default=None, on_delete=models.SET_NULL)
|
||||||
# Wenn dieses Profil gelöscht wird, wird NICHT die Agency geslöscht
|
# Wenn dieses Profil gelöscht wird, wird NICHT die Agency geslöscht
|
||||||
agency = models.ForeignKey(Agency, on_delete=models.PROTECT)
|
agency = models.ForeignKey(Agency, on_delete=models.PROTECT)
|
||||||
image = models.ImageField(default='default.jpg', upload_to=picturepath_user, blank=True)
|
image = models.ImageField(default='agencymain/default.jpg', upload_to=picturepath_user, blank=True)
|
||||||
compfunc = models.CharField(max_length=60, blank=True)
|
compfunc = models.CharField(max_length=60, blank=True)
|
||||||
visible = models.BooleanField(default=True)
|
visible = models.BooleanField(default=True)
|
||||||
persnumber = models.CharField(default="", max_length=50, blank=True)
|
persnumber = models.CharField(default="", max_length=50, blank=True)
|
||||||
|
|
@ -197,16 +203,6 @@ class Profile(models.Model):
|
||||||
user_messages_mail = models.BooleanField(default=True)
|
user_messages_mail = models.BooleanField(default=True)
|
||||||
user_messages_push = models.BooleanField(default=True)
|
user_messages_push = models.BooleanField(default=True)
|
||||||
|
|
||||||
# TIME ELEMENTS
|
|
||||||
wd_mo = models.IntegerField(default=8)
|
|
||||||
wd_tu = models.IntegerField(default=8)
|
|
||||||
wd_we = models.IntegerField(default=8)
|
|
||||||
wd_th = models.IntegerField(default=8)
|
|
||||||
wd_fr = models.IntegerField(default=8)
|
|
||||||
holiday = models.IntegerField(default=24)
|
|
||||||
loose_holidedate = models.DateField(default=datetime.date(datetime.datetime.now().year, 4,30))
|
|
||||||
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f'{self.user.last_name}'
|
return f'{self.user.last_name}'
|
||||||
|
|
||||||
|
|
@ -238,8 +234,19 @@ class Profile(models.Model):
|
||||||
return self.image.url
|
return self.image.url
|
||||||
else:
|
else:
|
||||||
return "/media/default.jpg"
|
return "/media/default.jpg"
|
||||||
|
|
||||||
|
|
||||||
|
class UserTime(models.Model):
|
||||||
|
|
||||||
|
user = models.OneToOneField(User, on_delete=models.CASCADE, blank=True, null=True, default=None)
|
||||||
|
# TIME ELEMENTS
|
||||||
|
wd_mo = models.FloatField(default=8.0)
|
||||||
|
wd_tu = models.FloatField(default=8.0)
|
||||||
|
wd_we = models.FloatField(default=8.0)
|
||||||
|
wd_th = models.FloatField(default=8.0)
|
||||||
|
wd_fr = models.FloatField(default=8.0)
|
||||||
|
holiday = models.FloatField(default=24.0)
|
||||||
|
loose_holidedate = models.DateField(default=datetime.date(datetime.datetime.now().year + 1, 4,30))
|
||||||
|
|
||||||
|
|
||||||
'''
|
'''
|
||||||
|
|
@ -270,7 +277,7 @@ class AgencyGroup(models.Model):
|
||||||
('filesmanager', 'Dateien bearbeiten'),
|
('filesmanager', 'Dateien bearbeiten'),
|
||||||
('filedirmanager', 'Ordner bearbeiten'),
|
('filedirmanager', 'Ordner bearbeiten'),
|
||||||
('filesviewer', 'Dateien lesen'),
|
('filesviewer', 'Dateien lesen'),
|
||||||
|
('absencemanager', 'Abwesenheiten verwalten')
|
||||||
]
|
]
|
||||||
|
|
||||||
# SUBCLASS
|
# SUBCLASS
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,25 @@ import os
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from standards.models import Standards
|
from standards.models import Standards
|
||||||
|
from django.contrib.auth.signals import user_logged_in
|
||||||
|
from timemanagement.models import Workday, Breaks
|
||||||
|
from datetime import date
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
# CHECK SOMETHING WHEN USER LOGGED IN
|
||||||
|
@receiver(signal=user_logged_in, sender=User)
|
||||||
|
def checkForWorkDays(sender, user, request, **kwargs):
|
||||||
|
today = date.today()
|
||||||
|
wd = Workday.objects.filter(user=user, end=None, start__day__lte=today.day)
|
||||||
|
|
||||||
|
for d in wd:
|
||||||
|
d.end = datetime.datetime(d.start.year, d.start.month, d.start.day, 23, 59, 00)
|
||||||
|
d.save()
|
||||||
|
for b in d.breaks.all():
|
||||||
|
if(b.end == None):
|
||||||
|
b.end = datetime.datetime(d.start.year, d.start.month, d.start.day, 23, 59, 00)
|
||||||
|
b.save()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Deletes all Notifications added to to delete news
|
# Deletes all Notifications added to to delete news
|
||||||
|
|
|
||||||
|
After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 5.9 KiB |
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 3.2 KiB |
|
|
@ -150,8 +150,6 @@
|
||||||
{%endif%}
|
{%endif%}
|
||||||
|
|
||||||
{% getmesscounter request.user as gs %}
|
{% getmesscounter request.user as gs %}
|
||||||
|
|
||||||
|
|
||||||
<a class="nav-link " href="{% url 'messages' %}" aria-expanded="true">
|
<a class="nav-link " href="{% url 'messages' %}" aria-expanded="true">
|
||||||
<i class="fas fa-envelope"></i>
|
<i class="fas fa-envelope"></i>
|
||||||
<span>Mitteilungen
|
<span>Mitteilungen
|
||||||
|
|
@ -163,8 +161,18 @@
|
||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
{% if request.user.profile.agency.module_timemanagement %}
|
||||||
|
{% if active_link == 'timemanagement' %}
|
||||||
|
<li class="nav-item active">
|
||||||
|
{% else%}
|
||||||
|
<li class="nav-item">
|
||||||
|
{%endif%}
|
||||||
|
<a class="nav-link " href="{% url 'tm-management' %}" aria-expanded="true">
|
||||||
|
<i class="far fa-clock"></i>
|
||||||
|
<span>Zeiterfassung</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
<!-- Sidebar Toggler (Sidebar) -->
|
<!-- Sidebar Toggler (Sidebar) -->
|
||||||
<!--
|
<!--
|
||||||
|
|
@ -251,10 +259,39 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
<style type="text/css">
|
||||||
|
.dropdown-header {
|
||||||
|
background-color: #5a5c69 !important;
|
||||||
|
border-color: #5a5c69 !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
<!-- Topbar Navbar -->
|
<!-- Topbar Navbar -->
|
||||||
<ul class="navbar-nav ml-auto ">
|
<ul class="navbar-nav ml-auto ">
|
||||||
|
{% if request.user.profile.agency.module_timemanagement %}
|
||||||
|
<li class="nav-item dropdown no-arrow mx-1">
|
||||||
|
<a class="nav-link dropdown-toggle" onclick="" id="timemanagement_realtime" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||||
|
<i class="far fa-clock"></i>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<div class="dropdown-list dropdown-menu dropdown-menu-right shadow animated--grow-in" aria-labelledby="alertsDropdown" id="timemanagement_clock" name="timemanagement_clock" aria-role="static">
|
||||||
|
<h6 class="dropdown-header text-white">
|
||||||
|
Zeiterfassung
|
||||||
|
</h6>
|
||||||
|
<div>
|
||||||
|
{% block timemanagement_content_realtime %}
|
||||||
|
{% include "timemanagement/realtime_dropdown.html" %}
|
||||||
|
{% endblock %}
|
||||||
|
</div>
|
||||||
|
<a class="dropdown-item text-center small text-gray-500" href="{% url 'tm-management' %}">Zur Zeiterfassung</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
<!-- ALERT_AREA -->
|
<!-- ALERT_AREA -->
|
||||||
<!-- Nav Item - Alerts -->
|
<!-- Nav Item - Alerts -->
|
||||||
<li class="nav-item dropdown no-arrow mx-1">
|
<li class="nav-item dropdown no-arrow mx-1">
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ from django.views.generic import CreateView, ListView, UpdateView, DetailView, D
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.auth.models import User, Permission
|
from django.contrib.auth.models import User, Permission
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from .models import Profile, Agency
|
from .models import Profile, Agency, UserTime
|
||||||
from django.core.mail import send_mail
|
from django.core.mail import send_mail
|
||||||
from django.http import HttpResponseRedirect,HttpResponse, JsonResponse
|
from django.http import HttpResponseRedirect,HttpResponse, JsonResponse
|
||||||
from areas.models import Areas
|
from areas.models import Areas
|
||||||
|
|
@ -94,6 +94,13 @@ def toUpdate(request):
|
||||||
else:
|
else:
|
||||||
print("default groups existing")
|
print("default groups existing")
|
||||||
|
|
||||||
|
# CHECK FOR ALL POSSIBLE RIGHTS ON ADMINGROUP
|
||||||
|
ag_admingroup = list(AgencyGroup.objects.filter(agency=request.user.profile.agency, is_admin=True))[0]
|
||||||
|
perms = AgencyGroup._meta.permissions
|
||||||
|
for p in perms:
|
||||||
|
tempperm = Permission.objects.get(codename=p[0])
|
||||||
|
ag_admingroup.group.permissions.add(tempperm)
|
||||||
|
|
||||||
# INITIAL ROOT DIR
|
# INITIAL ROOT DIR
|
||||||
rootdir = DataDir.objects.filter(is_root=True, agency__pk=request.user.profile.agency.pk)
|
rootdir = DataDir.objects.filter(is_root=True, agency__pk=request.user.profile.agency.pk)
|
||||||
|
|
||||||
|
|
@ -133,6 +140,15 @@ def toUpdate(request):
|
||||||
a.group.permissions.add(Permission.objects.get(codename="moduleorganizer"))
|
a.group.permissions.add(Permission.objects.get(codename="moduleorganizer"))
|
||||||
a.group.permissions.add(Permission.objects.get(codename="agencynetwork"))
|
a.group.permissions.add(Permission.objects.get(codename="agencynetwork"))
|
||||||
|
|
||||||
|
# USER TIME MODEL
|
||||||
|
usersofagency = User.objects.filter(profile__agency=request.user.profile.agency)
|
||||||
|
|
||||||
|
for u in usersofagency:
|
||||||
|
if(len(UserTime.objects.filter(user=u)) == 0):
|
||||||
|
usertime_new = UserTime(user=u)
|
||||||
|
usertime_new.save()
|
||||||
|
|
||||||
|
|
||||||
'''
|
'''
|
||||||
DASHBOARD-View
|
DASHBOARD-View
|
||||||
|
|
||||||
|
|
|
||||||