Zwischencommit Absence

This commit is contained in:
holger.trampe 2020-05-03 21:54:11 +02:00
parent db58c37c82
commit 37070e045d
20 changed files with 1053 additions and 189 deletions

View File

@ -66,15 +66,18 @@ class UserTimeForm(forms.ModelForm):
labels = { labels = {
"holiday" : "Urlaubstage", "holiday" : "Urlaubstage",
"loose_holidedate" : "Urlaubstage aus Vorjahr verfallen am", "loose_holidedate" : "Urlaubstage aus Vorjahr verfallen am",
"startdate" : "Einstellungsdatum",
"holiday_start" : "Urlaubstage bei Einstellung",
"wd_mo" : "Montag", "wd_mo" : "Montag",
"wd_tu" : "Dienstag", "wd_tu" : "Dienstag",
"wd_we" : "Mittwoch", "wd_we" : "Mittwoch",
"wd_th" : "Donnerstag", "wd_th" : "Donnerstag",
"wd_fr" : "Freitag", "wd_fr" : "Freitag",
} }
fields = ["holiday", "loose_holidedate", "wd_mo", "wd_tu", "wd_we", "wd_th", "wd_fr"] fields = ["holiday", "loose_holidedate", "startdate", "holiday_start", "wd_mo", "wd_tu", "wd_we", "wd_th", "wd_fr"]
widgets = { widgets = {
'loose_holidedate': DatePickerInput(options={"format":'DD.MM.YYYY', "locale":'de'}) 'loose_holidedate': DatePickerInput(options={"format":'DD.MM.YYYY', "locale":'de'}),
"startdate" : DatePickerInput(options={"format":'DD.MM.YYYY', "locale":'de'})
} }
@ -130,14 +133,14 @@ class UserProfileForm(forms.ModelForm):
class Meta: class Meta:
model = Profile model = Profile
labels = { labels = {
"persnumber" : "Personalnummer", "persnumber" : "Personalnummer",
"visible" : "Im Organigramm sichtbar", "visible" : "Im Organigramm sichtbar",
"phonemobile" : "Mobilnummer", "phonemobile" : "Mobilnummer",
"phone_public" : "Nur Interne Verwendung der Mobilnummer", "phone_public" : "Nur Interne Verwendung der Mobilnummer",
"phoneland" : "Festnetznummer", "phoneland" : "Festnetznummer",
"image": "Profilbild", "image": "Profilbild",
"func" : "Agenturfunktion", "func" : "Agenturfunktion",
"compfunc" : "Tätigkeit" "compfunc" : "Tätigkeit"
} }
widgets = {"parent" : forms.HiddenInput()} widgets = {"parent" : forms.HiddenInput()}
fields = ["parent", "func", "compfunc", "visible", "phoneland", "phonemobile", "phone_public", "persnumber", "image" ] fields = ["parent", "func", "compfunc", "visible", "phoneland", "phonemobile", "phone_public", "persnumber", "image" ]

View File

@ -99,7 +99,7 @@
<h5 class="mt-3">Agenturinformationen{% if request.user.profile.showtooltips %}&nbsp;<small><i data-toggle="tooltip" data-placement="top" title="Verwalten Sie hier die Informationen Ihrer Agentur, z.B. Adresse, E-Mailadresse und Telefon." class="far fa-question-circle"></i></small>{% endif %} <h5 class="mt-3">Agenturinformationen{% if request.user.profile.showtooltips %}&nbsp;<small><i data-toggle="tooltip" data-placement="top" title="Verwalten Sie hier die Informationen Ihrer Agentur, z.B. Adresse, E-Mailadresse und Telefon." class="far fa-question-circle"></i></small>{% endif %}
</h5> </h5>
<hr> <hr>
{% block agency_content %} {% block agency_content %}
{% include "dasettings/agency_content.html" %} {% include "dasettings/agency_content.html" %}
{% endblock %} {% endblock %}
</div> </div>

View File

@ -176,7 +176,9 @@
<p>Urlaub&nbsp;<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> <p>Urlaub&nbsp;<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.media}}
{{usertime_form.holiday|as_crispy_field}} {{usertime_form.holiday|as_crispy_field}}
{{usertime_form.holiday_start|as_crispy_field}}
{{usertime_form.loose_holidedate|as_crispy_field}} {{usertime_form.loose_holidedate|as_crispy_field}}
{{usertime_form.startdate|as_crispy_field}}
</div> </div>
<div class="col-12"> <div class="col-12">
<hr> <hr>

View File

@ -544,7 +544,6 @@ def UserProfileUpdate(request, pk, newuser=0):
elif(request.POST["form_type"] == "contract"): elif(request.POST["form_type"] == "contract"):
formtosave = UserTimeForm(request.POST, instance=UserTime.objects.get(user=usertochange)) formtosave = UserTimeForm(request.POST, instance=UserTime.objects.get(user=usertochange))
print(formtosave)
if(formtosave.is_valid()): if(formtosave.is_valid()):
messages.success(request, f'Vertragsdaten gespeichert!') messages.success(request, f'Vertragsdaten gespeichert!')
formtosave.save() formtosave.save()
@ -792,7 +791,6 @@ def ManageAgInAgn(request, pk):
for a in network.adminagencys.all(): for a in network.adminagencys.all():
allagofagn.append(a) allagofagn.append(a)
print(allagofagn)
context = { context = {
'active_link' : 'dasettings', 'active_link' : 'dasettings',

View File

@ -29,7 +29,7 @@
{% if perms.users.standardmanager %} {% if perms.users.standardmanager %}
<li class="nav-item" style="float: right !important;"> <li class="nav-item" style="float: right !important;">
<a class="nav-link" id="agencys" data-toggle="tab" href="#t_agencys" role="tab" aria-controls="t_agencys" aria-selected="false"> <a class="nav-link" id="agencys" data-toggle="tab" href="#t_agencys" role="tab" aria-controls="t_agencys" aria-selected="false">
{% if unpubstandards_of_user|length > 0 %} {% if unpubstandards_of_user|length > 0 %}
<span class="badge badge-primary badge-counter" style="float: right; margin-left: 5px; margin-top: 0px">{{unpubstandards_of_user|length}} </span>&nbsp; <span class="badge badge-primary badge-counter" style="float: right; margin-left: 5px; margin-top: 0px">{{unpubstandards_of_user|length}} </span>&nbsp;
{%endif%} {%endif%}
Unveröffentlichte Standards Unveröffentlichte Standards

View File

@ -2,7 +2,7 @@ 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, FreeDays from timemanagement.models import Workday, FreeDays, Absence
from message.models import Message from message.models import Message
import os import os
from django.conf import settings from django.conf import settings
@ -415,3 +415,28 @@ def isfreedayname(user, daytocheck):
returnstat = True returnstat = True
returnstat = fd[0].name returnstat = fd[0].name
return returnstat return returnstat
# RETURN ALL ABSENCE ELEMENTS FOR THAT DAY
@register.simple_tag
def getabscenceday(loggeduser, user, daytocheck):
returnstat = False
if(loggeduser.has_perm("users.absencemanager")):
absencedays = Absence.objects.filter(agency=user.profile.agency, user=user, start=daytocheck) | (Absence.objects.filter(agency=user.profile.agency, user=user, start__lt=daytocheck) & Absence.objects.filter(agency=user.profile.agency, user=user, end__gt=daytocheck)) | Absence.objects.filter(agency=user.profile.agency, user=user, end=daytocheck)
else:
absencedays = (Absence.objects.filter(agency=user.profile.agency, user=loggeduser, confirm_status=1) | Absence.objects.filter(agency=user.profile.agency, user=user, confirm_status=0)) & (Absence.objects.filter(agency=user.profile.agency, user=user, start=daytocheck) | (Absence.objects.filter(agency=user.profile.agency, user=user, start__lt=daytocheck) & Absence.objects.filter(agency=user.profile.agency, user=user, end__gt=daytocheck)) | Absence.objects.filter(agency=user.profile.agency, user=user, end=daytocheck) )
if(len(absencedays) > 0):
returnstat = list(absencedays)[0]
return returnstat
@register.simple_tag
def getsomeyears(start):
years = []
start_int = int(start)
years = [start_int-4, start_int-3,start_int-2,start_int-1,start_int, start_int+1, start_int+2, start_int+3, start_int+4]
return years
@register.simple_tag
def getsomemonths():
return ["Januar", "Februar", "März", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember"]

View File

@ -1,19 +1,65 @@
from bootstrap_datepicker_plus import DatePickerInput from bootstrap_datepicker_plus import DatePickerInput
from django import forms from django import forms
from django.forms import ModelForm from django.forms import ModelForm
from .models import Absence from .models import Absence, AbsenceReason
from users.models import UserFullName
class AddAbsence(forms.ModelForm): class AddAbsence(forms.ModelForm):
class Meta: class Meta:
model = Absence model = Absence
labels = { labels = {
"start" : "Beginn der Abwesenheit", "start" : "Beginn der Abwesenheit",
"end" : "Ende der Abwesenheit", "start_ishalf" : "Halber Tag?",
} "end" : "Ende der Abwesenheit",
fields = ['start', 'end'] "end_ishalf" : "Halber Tag?",
"reason" : "Abwesenheitsgrund",
"representator" : "Vertreter",
"info" : "Begründung"
}
widgets = { widgets = {
'start': DatePickerInput(options={"format":'DD.MM.YYYY', "locale":'de'}), 'start': DatePickerInput(options={"format":'DD.MM.YYYY', "locale":'de'}),
'end': DatePickerInput(options={"format":'DD.MM.YYYY', "locale":'de'}), 'end': DatePickerInput(options={"format":'DD.MM.YYYY', "locale":'de'}),
} }
fields = ['start', 'start_ishalf', 'end','end_ishalf', 'reason', "representator", 'info']
def __init__(self, *arg, **kwargs):
super(AddAbsence, self).__init__(*arg, **kwargs)
self.fields['reason'].queryset = AbsenceReason.objects.filter(agency=kwargs['instance'].profile.agency).order_by('-name')
self.fields['representator'].queryset = UserFullName.objects.filter(profile__agency=kwargs['instance'].profile.agency)
self.fields['info'].widget.attrs['rows'] = 3
self.fields['start'].required = True
self.fields['end'].required = True
self.fields['reason'].required = True
self.fields['nextmonth'] = forms.CharField(initial="", required=False, widget=forms.HiddenInput())
self.fields['prevmonth'] = forms.CharField(initial="", required=False, widget=forms.HiddenInput())
self.fields['nextyear'] = forms.CharField(initial="", required=False, widget=forms.HiddenInput())
self.fields['prevyear'] = forms.CharField(initial="", required=False, widget=forms.HiddenInput())
self.fields['activemonth'] = forms.CharField(initial="", required=False, widget=forms.HiddenInput())
self.fields['activeyear'] = forms.CharField(initial="", required=False, widget=forms.HiddenInput())
self.fields['userid'] = forms.CharField(initial="", required=False, widget=forms.HiddenInput())
'''
def clean(self):
start_date = self.cleaned_data['start']
end_date = self.cleaned_data['end']
if end_date <= start_date:
raise forms.ValidationError("Das Ende der Abwesenheit muss am oder nach dem Start der Abwesenheit liegen.")
return super(AddAbsence, self).clean()
'''
class ConfirmAbsenceForm(forms.ModelForm):
class Meta:
model = Absence
labels = {
"confirm_info" : "Begründung zur Annahme/zur Ablehnung"
}
fields = ['confirm_info']
def __init__(self, *arg, **kwargs):
super(ConfirmAbsenceForm, self).__init__(*arg, **kwargs)
self.fields['confirm_info'].widget.attrs['rows'] = 3

View File

@ -1,6 +1,7 @@
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 Agency from users.models import Agency
from django.core.exceptions import ValidationError
# Create your models here. # Create your models here.
class Workday(models.Model): class Workday(models.Model):
@ -24,17 +25,31 @@ class AbsenceReason(models.Model):
need_rep = models.BooleanField(default=True) need_rep = models.BooleanField(default=True)
is_holiday = models.BooleanField(default=True) is_holiday = models.BooleanField(default=True)
def __str__(self):
return f'{self.name}'
class Absence(models.Model): class Absence(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE) user = models.ForeignKey(User, on_delete=models.CASCADE)
agency = models.ForeignKey(Agency, on_delete=models.CASCADE) agency = models.ForeignKey(Agency, on_delete=models.CASCADE)
start = models.DateTimeField(default=None, null=True, blank=True) start = models.DateTimeField(default=None, null=True, blank=True)
end = models.DateTimeField(default=None, null=True, blank=True) end = models.DateTimeField(default=None, null=True, blank=True)
start_ishalf = models.BooleanField(default=False) start_ishalf = models.BooleanField(default=False, blank=True)
end_ishalf = models.BooleanField(default=False) end_ishalf = models.BooleanField(default=False, blank=True)
reason = models.ForeignKey("AbsenceReason", on_delete=models.SET_NULL, null=True, blank=True) reason = models.ForeignKey("AbsenceReason", on_delete=models.SET_NULL, null=True, blank=True)
info = models.TextField(blank=True, verbose_name='Abwesenheitsbegründung', default="") info = models.TextField(blank=True, verbose_name='Abwesenheitsbegründung', default="")
'''
CONFIRM_STATUS INFOS
0 = NO NEED TO CONFIRM AND CONFORM OK
1 = IS CONFIRMED, AWAITING OK
2 = NOT CONFIRMED
'''
confirm_status = models.IntegerField(default=0) confirm_status = models.IntegerField(default=0)
confirm_info = models.TextField(blank=True, verbose_name='Begründung', default="") confirm_info = models.TextField(blank=True, verbose_name='Begründung', default="")
representator = models.ForeignKey(User, blank=True, default=None, null=True, on_delete=models.CASCADE, related_name="Vertreter")
class FreeDays(models.Model): class FreeDays(models.Model):
agency = models.ForeignKey(Agency, on_delete=models.CASCADE) agency = models.ForeignKey(Agency, on_delete=models.CASCADE)

View File

@ -0,0 +1,23 @@
{% load crispy_forms_tags %}
<h5>Abwesenheit für {{absence.user.first_name}} {{absence.user.last_name}} annehmen oder ablehnen</h5>
<hr>
<h6>Informationen der Abwesenheit</h6>
Von: {{absence.start|date:"d.m Y"}}<br />
Bis: {{absence.end|date:"d.m Y"}}<br />
Grund: {{absence.reason.name}}<br />
Informationen: {{absence.info}}<br />
<hr>
<form method="POST">
<input type="hidden" name="form_type" value="confirmform">
{% csrf_token %}
{{confirmform|crispy}}
</div>
<div class="modal-footer">
<button type="button" class="btn btn-success" onclick="javascript:noconfirmAbsence({{absence.pk}})">Ablehnen</button>
<button type="button" class="btn btn-danger" onclick="javascript:confirmAbsence({{absence.pk}})">Annehmen</button>
</div>
</form>
<script type="text/javascript">
absencestring_confirm = 'Sicher, dass Sie die Abwesenheit für {{absence.user.first_name}} {{absence.user.last_name}} vom {{absence.start|date:"d.m Y"}} bis {{absence.end|date:"d.m Y"}} <b>annehmen</b> wollen?'
absencestring_noconfirm = 'Sicher, dass Sie die Abwesenheit für {{absence.user.first_name}} {{absence.user.last_name}} vom {{absence.start|date:"d.m Y"}} bis {{absence.end|date:"d.m Y"}} <b>ablehnen</b> wollen?'
</script>

View File

@ -1,18 +1,17 @@
{% load counter_tag %} {% load counter_tag %}
<div class="btn-group mr-2 mb-2" role="group" aria-label="calendarbuttons">
<button type="button" class="btn btn-primary mr-1" onclick="javascript:prevMonth()" ><i class="fas fa-arrow-circle-left"></i></button>
<button type="button" class="btn btn-primary mr-1" style="min-width: 150px !important;">{{days_this_month.0|date:"F Y"}}</button>
<button type="button" class="btn btn-primary mr-1" onclick="javascript:nextMonth()"><i class="fas fa-arrow-circle-right"></i></button>
</div>
<table class="table table-striped table-sm table-bordered" id="timetable_team"> <table class="table table-striped table-sm table-bordered" id="timetable_team">
<th> <tr>
<td id="9999999999_tableheadid">
<div class="btn-group ml-3 mb-2 mt-2" role="group" aria-label="calendarbuttons">
<button type="button" class="btn btn-primary mr-1" onclick="javascript:prevMonth()" ><i class="fas fa-arrow-circle-left"></i></button>
<button type="button" class="btn btn-primary mr-1" style="min-width: 150px !important;" onclick="javascript:fastChangeModal()">{{days_this_month.0|date:"F Y"}}</button>
<button type="button" class="btn btn-primary mr-1" onclick="javascript:nextMonth()"><i class="fas fa-arrow-circle-right"></i></button>
</div>
</td>
{% for da in days_this_month %} {% for da in days_this_month %}
<td id="{{forloop.counter0}}_tableheadid"><small>{{da|date:"d D"}}</small></td> <td id="{{forloop.counter0}}_tableheadid"><small>{{da|date:"d D"}}</small></td>
{% endfor %} {% endfor %}
</th> </tr>
{% for us in usersofagency %} {% for us in usersofagency %}
{% setdateforloopcounter forloop.counter %} {% setdateforloopcounter forloop.counter %}
@ -27,12 +26,72 @@
{% isfreeday user da as isfree %} {% isfreeday user da as isfree %}
{% isfreedayname user da as isfreename %} {% isfreedayname user da as isfreename %}
{% if isfree %} {% getabscenceday user us da as abday %}
<td id="{{actfcounter}}_{{forloop.counter}}_{{us.pk}}_free" style="background-color: #e74a3b" data-toggle="tooltip" data-placement="top" title="{{isfreename}}">
{% if isfree %}
<!-- FREEDAYS -->
<td id="{{actfcounter}}_{{forloop.counter}}_{{us.pk}}_free" style="background-color: #e74a3b" data-toggle="tooltip" data-placement="top" title="{{isfreename}}">
{% elif da.weekday == 5 or da.weekday == 6 %} {% elif da.weekday == 5 or da.weekday == 6 %}
<!-- WEEKEND -->
<td id="{{actfcounter}}_{{forloop.counter}}_{{us.pk}}_free" style="background-color: #d3d3d3"> <td id="{{actfcounter}}_{{forloop.counter}}_{{us.pk}}_free" style="background-color: #d3d3d3">
{% elif abday != False %}
<!-- USER IS NOT THERE -->
{% if abday.confirm_status == 0 %}
{% if user|usergperm:"absencemanager" %}
<td id="{{actfcounter}}_{{forloop.counter}}_{{us.pk}}_{{abday.pk}}_absence"
{% if abday.start_ishalf and abday.start.day == da.day %}
class="partialfilling negative"
style="background-size: 50% 100%"
{% elif abday.end_ishalf and abday.end.day == da.day %}
class="partialfilling"
style="background-size: 50% 100%"
{% else %}
style="background-color: #36b9cc"
{% endif %}
data-toggle="tooltip" data-placement="top" title="{{abday.reason.name}} {% if abday.representator != None %} | Vertreten durch {{abday.representator.first_name}} {{abday.representator.last_name}} {% endif %} ">
{% else %}
<td id="{{actfcounter}}_{{forloop.counter}}_{{us.pk}}_{{abday.pk}}_absence"
{% if abday.start_ishalf and abday.start.day == da.day %}
class="partialfilling negative"
style="background-size: 50% 100%"
{% elif abday.end_ishalf and abday.end.day == da.day %}
class="partialfilling"
style="background-size: 50% 100%"
{% else %}
style="background-color: #36b9cc"
{% endif %} data-toggle="tooltip" data-placement="top" title="Abwesend {% if abday.representator != None %} | Vertreten durch {{abday.representator.first_name}} {{abday.representator.last_name}} {% endif %} ">
{% endif %}
{% elif abday.confirm_status == 1 %}
{% if user|usergperm:"absencemanager" %}
<td id="{{actfcounter}}_{{forloop.counter}}_{{us.pk}}_{{abday.pk}}_absencetoc"
{% if abday.start_ishalf and abday.start.day == da.day %}
class="partialfilling_nf negative_nf"
style="background-size: 50% 100%"
{% elif abday.end_ishalf and abday.end.day == da.day %}
class="partialfilling_nf"
style="background-size: 50% 100%"
{% else %}
style="background-color: #858796"
{% endif %}
data-toggle="tooltip" data-placement="top" title="Nicht bestätigt | {{abday.reason.name}} {% if abday.representator != None %} | Vertreten durch {{abday.representator.first_name}} {{abday.representator.last_name}} {% endif %} ">
{% else %}
<td id="{{actfcounter}}_{{forloop.counter}}_{{us.pk}}_{{abday.pk}}_absencetoc"
{% if abday.start_ishalf and abday.start.day == da.day %}
class="partialfilling_nf negative_nf"
style="background-size: 50% 100%"
{% elif abday.end_ishalf and abday.end.day == da.day %}
class="partialfilling_nf"
style="background-size: 50% 100%"
{% else %}
style="background-color: #858796"
{% endif %} data-toggle="tooltip" data-placement="top" title="Nicht bestätigt | Abwesend {% if abday.representator != None %} | Vertreten durch {{abday.representator.first_name}} {{abday.representator.last_name}} {% endif %}">
{% endif %}
{% else %}
<td id="{{actfcounter}}_{{forloop.counter}}_{{us.pk}}_{{da|date:'Y-m-d'}}" {% if us.pk == user.pk or user|usergperm:"absencemanager" %}class="tm-ab-tdhover"{% endif %}>
{% endif %}
{% else %} {% else %}
<!-- NORMAL CHOICE -->
<td id="{{actfcounter}}_{{forloop.counter}}_{{us.pk}}_{{da|date:'Y-m-d'}}" {% if us.pk == user.pk or user|usergperm:"absencemanager" %}class="tm-ab-tdhover"{% endif %}> <td id="{{actfcounter}}_{{forloop.counter}}_{{us.pk}}_{{da|date:'Y-m-d'}}" {% if us.pk == user.pk or user|usergperm:"absencemanager" %}class="tm-ab-tdhover"{% endif %}>
{% endif %} {% endif %}
</td> </td>
@ -43,23 +102,120 @@
<style type="text/css">
.partialfilling {
background-image: linear-gradient(to right, #36b9cc 0%, #36b9cc 17%, #36b9cc 33%, #36b9cc 67%, #36b9cc 83%, #36b9cc 100%); /* your gradient */
background-repeat: no-repeat; /* don't remove */
}
.partialfilling.negative {
background-image: linear-gradient(to left, #36b9cc 0%, #36b9cc 17%, #36b9cc 33%, #36b9cc 67%, #36b9cc 83%, #36b9cc 100%); /* your gradient */
background-position: 100% 100%;
}
.partialfilling_nf {
background-image: linear-gradient(to right, #858796 0%, #858796 17%, #858796 33%, #858796 67%, #858796 83%, #858796 100%); /* your gradient */
background-repeat: no-repeat; /* don't remove */
}
.partialfilling_nf.negative_nf {
background-image: linear-gradient(to left, #858796 0%, #858796 17%, #858796 33%, #858796 67%, #858796 83%, #858796 100%); /* your gradient */
background-position: 100% 100%;
}
</style>
<div class="modal fade" tabindex="-1" id="fastjumpmodal" data-backdrop="static">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Zeitraum auswählen</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<div class="form-group">
<label for="exampleFormControlSelect1">Jahr auswählen</label>
{% getsomeyears activeyear as years %}
<select class="form-control" id="choosenyear">
{% for y in years %}
<option val="{{y}}" {% if y == activeyear %} selected{% endif%}>{{y}}</option>
{% endfor %}
</select>
</div>
{% getsomemonths as months %}
{% for m in months %}
<button class="btn
{% if forloop.counter == activemonth %} btn-secondary {% else %} btn-primary {% endif %}
btn-sm mr-2 mb-2" onclick="javascript:goFastToMonth({{forloop.counter}})">{{m}}</button>
{% endfor %}
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Schließen</button>
</div>
</div>
</div>
</div>
<script type="text/javascript"> <script type="text/javascript">
var choosenyear = "{{activeyear}}";
function goFastToMonth(month){
location.href = "/tm/abs/" + month +"/" + choosenyear
}
document.getElementById("choosenyear").addEventListener("change", function(){
choosenyear = $('#choosenyear :selected').val();
})
$(function () { $(function () {
$('[data-toggle="tooltip"]').tooltip() $('[data-toggle="tooltip"]').tooltip()
}) })
function fastChangeModal(){
$("#fastjumpmodal").modal("toggle");
}
//Set required repr or not
document.getElementById("id_reason").addEventListener("change", function(){
reasonid = $('#id_reason :selected').val();
//Get required-rep
$.ajax(
{
type: "GET",
url: "{% url 'tm-ajax' %}",
data:{
action : "checkrequired",
rid : reasonid
},
success : function(data){
if(data["isreq"]){
$("#id_representator").prop('required',true);
$("label[for*='id_representator']").html("Vertreter*");
}
else{
$("#id_representator").prop('required',false);
$("label[for*='id_representator']").html("Vertreter");
}
}
});
})
$("#id_activemonth").val("{{activemonth}}")
$("#id_activeyear").val("{{activeyear}}")
$("#id_prevmonth").val("{{prevmonth}}")
$("#id_nextmonth").val("{{nextmonth}}")
$("#id_prevyear").val("{{prevyear}}")
$("#id_nextyear").val("{{nextyear}}")
function prevMonth(){ function prevMonth(){
$.ajax({ $.ajax(
{
type: "GET", type: "GET",
url: "{% url 'tm-ajax' %}", url: "{% url 'tm-ajax' %}",
data:{ data:{
action : "testdjango", action : "getrenderedtable",
activeyear : {{prevyear}}, activeyear : {{prevyear}},
activemonth : {{prevmonth}} activemonth : {{prevmonth}}
}, },
@ -69,9 +225,9 @@ function prevMonth(){
}, },
success : function(data){ success : function(data){
$("#overlay").fadeOut(); $("#overlay").fadeOut();
$("#rendered_table").html(data) $("#rendered_table").html(data);
} }
}); });
} }
function nextMonth(){ function nextMonth(){
@ -79,7 +235,7 @@ function nextMonth(){
type: "GET", type: "GET",
url: "{% url 'tm-ajax' %}", url: "{% url 'tm-ajax' %}",
data:{ data:{
action : "testdjango", action : "getrenderedtable",
activeyear : {{nextyear}}, activeyear : {{nextyear}},
activemonth : {{nextmonth}} activemonth : {{nextmonth}}
}, },
@ -106,7 +262,6 @@ else{
} }
function recalculateChoosenDays(){ function recalculateChoosenDays(){
seldates = []; seldates = [];
userid = ""; userid = "";
@ -119,6 +274,7 @@ function recalculateChoosenDays(){
date_end = new Date(seldates[seldates.length-1]) date_end = new Date(seldates[seldates.length-1])
$("#div_id_end").show(); $("#div_id_end").show();
$("#div_id_end_ishalf").show();
$("#startAbsenceProgress").modal("toggle"); $("#startAbsenceProgress").modal("toggle");
$("#id_start").data("DateTimePicker").date(date_start); $("#id_start").data("DateTimePicker").date(date_start);
@ -126,10 +282,11 @@ function recalculateChoosenDays(){
if(seldates.length == 1){ if(seldates.length == 1){
$("#div_id_end").hide(); $("#div_id_end").hide();
$("#div_id_end_ishalf").hide();
} }
} }
$( function() { $( function() {
$( "#timetable_team" ).selectable({ $( "#timetable_team" ).selectable({
filter: 'td', filter: 'td',
@ -140,8 +297,58 @@ $( function() {
}, },
selecting: function(event, ui){ selecting: function(event, ui){
newid = ui["selecting"]["id"]; newid = ui["selecting"]["id"];
if(newid.split("_")[1] == "tableheadid" || newid.split("_")[3] == "firstcolum" || (newid.split("_")[2] != {{user.pk}} && user_has_right != true ) || newid.split("_")[3] == "free"){ if(newid.split("_")[1] == "tableheadid" || newid.split("_")[3] == "firstcolum" || (newid.split("_")[2] != {{user.pk}} && user_has_right != true ) || newid.split("_")[3] == "free" || newid.split("_")[4] == "absence"){
$("#" + newid).removeClass();
if($("#" + newid).hasClass("negative")){
$("#" + newid).removeClass();
$("#" + newid).addClass("partialfilling");
$("#" + newid).addClass("negative");
}
else if($("#" + newid).hasClass("partialfilling"))
{
$("#" + newid).removeClass();
$("#" + newid).addClass("partialfilling");
}
else if($("#" + newid).hasClass("negative_nf")){
$("#" + newid).removeClass();
$("#" + newid).addClass("partialfilling_nf");
$("#" + newid).addClass("negative_nf");
}
else if($("#" + newid).hasClass("partialfilling_nf"))
{
$("#" + newid).removeClass();
$("#" + newid).addClass("partialfilling_nf");
}
else{
$("#" + newid).removeClass();
}
}
else if(newid.split("_")[4] == "absencetoc"){
if($("#" + newid).hasClass("negative")){
$("#" + newid).removeClass();
$("#" + newid).addClass("partialfilling");
$("#" + newid).addClass("negative");
}
else if($("#" + newid).hasClass("partialfilling"))
{
$("#" + newid).removeClass();
$("#" + newid).addClass("partialfilling");
}
else if($("#" + newid).hasClass("negative_nf")){
$("#" + newid).removeClass();
$("#" + newid).addClass("partialfilling_nf");
$("#" + newid).addClass("negative_nf");
}
else if($("#" + newid).hasClass("partialfilling_nf"))
{
$("#" + newid).removeClass();
$("#" + newid).addClass("partialfilling_nf");
}
else{
$("#" + newid).removeClass();
}
idtoopen = newid.split("_")[3];
openModalABChangeTable(idtoopen);
} }
else{ else{
if(selectedElements.length == 0){ if(selectedElements.length == 0){
@ -149,7 +356,20 @@ $( function() {
selectedElements.push(ui["selecting"]["id"]); selectedElements.push(ui["selecting"]["id"]);
} }
else if(newid.split("_")[0] != active_row){ else if(newid.split("_")[0] != active_row){
$("#" + newid).removeClass();
if($("#" + newid).hasClass("negative")){
$("#" + newid).removeClass();
$("#" + newid).addClass("partialfilling");
$("#" + newid).addClass("negative");
}
else if($("#" + newid).hasClass("partialfilling"))
{
$("#" + newid).removeClass();
$("#" + newid).addClass("partialfilling");
}
else{
$("#" + newid).removeClass();
}
} }
else{ else{
selectedElements.push(ui["selecting"]["id"]); selectedElements.push(ui["selecting"]["id"]);
@ -160,48 +380,21 @@ $( function() {
selectedElements.splice(selectedElements.indexOf(ui["unselecting"]["id"]),1); selectedElements.splice(selectedElements.indexOf(ui["unselecting"]["id"]),1);
}, },
stop: function(){ stop: function(){
recalculateChoosenDays(); //Selection ends, check if elements set and open modal
if(selectedElements.length > 0){
$("#timetable_team tbody tr").each(function(i) {
// find the first td in the row
var value = $(this).find("td:first").text();
// display the value in console
checkrow = parseInt(active_row)
if(i == (checkrow)){
$("#username_abscence").html(value);
$("#id_userid").val(selectedElements[0].split("_")[2])
}
});
recalculateChoosenDays();
}
} }
}); });
} ); } );
</script> </script>
<style>
.loader {
position: relative;
border: 7px solid #d3d3d3;
border-radius: 50%;
border-top: 7px solid red;
width: 70px;
height: 70px;
left:50%;
top:50%;
-webkit-animation: spin 1s linear infinite; /* Safari */
animation: spin 1s linear infinite;
}
#overlay{
position: absolute;
top:0px;
left:0px;
width: 100%;
height: 100%;
background: black;
opacity: .4;
}
.container{
position:relative;
height: 300px;
width: 200px;
border:1px solid red;
}
/* Safari */
@-webkit-keyframes spin {
0% { -webkit-transform: rotate(0deg); }
100% { -webkit-transform: rotate(360deg); }
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
</style>

View File

@ -68,7 +68,7 @@
<div class="modal-dialog"> <div class="modal-dialog">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
Arbeitstag löschen <h5 class="modal-title">Arbeitstag löschen</h5>
</div> </div>
<div class="modal-body"> <div class="modal-body">
Möchten Sie wirklich den Arbeitstag am {{workday.start|date:"d.m"}} löschen? Möchten Sie wirklich den Arbeitstag am {{workday.start|date:"d.m"}} löschen?

View File

@ -0,0 +1,93 @@
<div class="row col-12 mt-3" >
<div class="table-responsive">
<table class="table table-hover" id="table_allabsences">
<thead>
<tr>
<th scope="col">Mitarbeiter</th>
<th scope="col">Start</th>
<th scope="col">Ende</th>
<th scope="col">Grund</th>
<th scope="col">Info</th>
<th scope="col">Status</th>
<th scope="col">&nbsp;</th>
</tr>
</thead>
<tbody id="">
{% for abday in allabsences %}
<tr id="tableele_ab_{{abday.pk}}">
<td>{{abday.user.first_name}} {{abday.user.last_name}}</td>
<td>{{abday.start|date:"d.M Y"}}</td>
<td>{{abday.end|date:"d.M Y"}}</td>
<td>{{abday.reason.name}}</td>
<td>{{abday.info}}</td>
<td>{% if abday.confirm_status == 0 %} Genehmigt {% elif abday.confirm_status == 1 %} Beantragt {% else %} Abgelehnt {% endif %}</td>
<td>
<button type="button " class="btn btn-secondary btn-sm" onclick="javascript:$('#confirm-delete_{{abday.pk}}').modal('toggle')"><i class="fas fa-trash"></i></button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
<script type="text/javascript">
$(document).ready(function(){
$('#table_allabsences').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>
{% for ab in allabsences %}
<div class="modal fade" id="confirm-delete_{{ab.pk}}" tabindex="-1" role="dialog" aria-labelledby="" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Abwesenheit löschen</h5>
</div>
<div class="modal-body">
Möchten Sie wirklich die Abwesenheit von {{ab.user.first_name}} {{ab.user.last_name}} vom {{ab.start|date:"d.m.Y"}} bis {{ab.end|date:"d.m.Y"}} löschen?
</div>
<div class="modal-footer">
<button class="btn btn-danger" id="ab_{{ab.pk}}" >Löschen</button>
<button type="button" class="btn btn-success" data-dismiss="modal">Abbrechen</button>
</div>
</div>
</div>
</div>
<script type="text/javascript">
$("#ab_{{ab.pk}}").click(function(){
$.ajax(
{
type: "GET",
url: "{% url 'tm-ajax' %}",
data:{
action : "remove_absence",
ab: {{ab.pk}},
},
success: function( data )
{
location.href = "{% url 'tma-management' activemonth activeyear %}"
}
});
});
</script>
{% endfor %}

View File

@ -3,48 +3,143 @@
{% load crispy_forms_tags %} {% load crispy_forms_tags %}
{% load counter_tag %} {% load counter_tag %}
{% if request.user.profile.agency.module_timemanagement_ze %} {% if request.user.profile.agency.module_timemanagement_ze %}
<!-- Moment.js -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.23.0/moment.min.js" integrity="sha256-VBLiveTKyUZMEzJd6z2mhfxIqz3ZATCuVMawPZGzIfA=" crossorigin="anonymous"></script>
<!-- Tempus Dominus Bootstrap 4 -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/tempusdominus-bootstrap-4/5.1.2/css/tempusdominus-bootstrap-4.min.css" integrity="sha256-XPTBwC3SBoWHSmKasAk01c08M6sIA5gF5+sRxqak2Qs=" crossorigin="anonymous" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/tempusdominus-bootstrap-4/5.1.2/js/tempusdominus-bootstrap-4.min.js" integrity="sha256-z0oKYg6xiLq3yJGsp/LsY9XykbweQlHl42jHv2XTBz4=" crossorigin="anonymous"></script>
<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css"> <link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
<div class="content-section col-12"> <div class="content-section col-12">
<h3>Abwesenheiten{% if request.user.profile.showtooltips %}&nbsp;<small><i data-toggle="tooltip" data-placement="top" title="Bearbeiten Sie hier Ihre Abwesenheiten." class="far fa-question-circle"></i></small>{% endif %}</h3> <h3>Abwesenheiten{% if request.user.profile.showtooltips %}&nbsp;<small><i data-toggle="tooltip" data-placement="top" title="Bearbeiten Sie hier Ihre Abwesenheiten." class="far fa-question-circle"></i></small>{% endif %}</h3>
<hr> <hr>
<div>
<div id="rendered_table"> <ul class="nav nav-tabs" id="absencetabs" role="tablist">
Lade Kalenderdaten... <li class="nav-item">
</div> <a class="nav-link active" id="team-tab" data-toggle="tab" href="#team" role="tab" aria-controls="team" aria-selected="false">Teamübersicht</a>
<div id="overlay" style="display: none;"> </li>
<div class="loader"></div> <li class="nav-item">
<a class="nav-link" id="userown-tab" data-toggle="tab" href="#userown" role="tab" aria-controls="userown" aria-selected="false">Meine Abwesenheiten</a>
</li>
{% if user|usergperm:"absencemanager" %}
<li class="nav-item ">
<a class="nav-link" id="absencemanagercontent-tab" data-toggle="tab" href="#absencemanagercontent" role="tab" aria-controls="absencemanagercontent" aria-selected="false">
{% if needtoconfirm|length > 0 %}
<span class="badge badge-primary badge-counter" style="float: right; margin-left: 5px; margin-top: 0px">{{needtoconfirm|length}} </span>&nbsp;
{%endif%}
Ausstehende Anträge
</a>
</li>
<li class="nav-item ">
<a class="nav-link" id="absencemanagercontent_all-tab" data-toggle="tab" href="#absencemanagercontent_all" role="tab" aria-controls="absencemanagercontent_all" aria-selected="false">
Alle Abwesenheiten
</a>
</li>
{% endif %}
</ul>
<div class="tab-content" id="absencetabsContent">
<div class="tab-pane fade" id="team" role="tabpanel" aria-labelledby="team-tab">
<div>
<div id="rendered_table">
Lade Kalenderdaten...
</div>
</div> </div>
</div> </div>
<div class="tab-pane fade" id="userown" role="tabpanel" aria-labelledby="team-tab">
{% block ab_userown %}
{% include "timemanagement/tm_ab_userown.html" %}
{% endblock %}
</div>
{% if user|usergperm:"absencemanager" %}
<div class="tab-pane fade" id="absencemanagercontent" role="tabpanel" aria-labelledby="team-tab">
{% block ab_toconfirm %}
{% include "timemanagement/tm_ab_toconfirm.html" %}
{% endblock %}
</div>
<div class="tab-pane fade" id="absencemanagercontent_all" role="tabpanel" aria-labelledby="team-tab">
{% block ab_all %}
{% include "timemanagement/tm_ab_all.html" %}
{% endblock %}
</div>
{% endif %}
</div>
</div>
<!-- LOADER OVERLAY -->
<div id="overlay" style="display: none;">
<div class="loader"></div>
</div>
<!-- MODAL -->
<div class="modal fade" tabindex="-1" role="dialog" id="startAbsenceProgress" data-backdrop="static"> <div class="modal fade" tabindex="-1" role="dialog" id="startAbsenceProgress" data-backdrop="static">
<div class="modal-dialog" role="document"> <div class="modal-dialog" role="document">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<h5 class="modal-title">Abwesenheit bentragen</h5> <h5 class="modal-title">Abwesenheit beantragen</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close"> <button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span> <span aria-hidden="true">&times;</span>
</button> </button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<h5>Abwesenheit für <b><span id="username_abscence"></span></b> beantragen</h5>
<hr>
<form method="POST"> <form method="POST">
<input type="hidden" name="form_type" value="absenceform">
{% csrf_token %} {% csrf_token %}
{{abscenceform.media}} {{abscenceform.media}}
{{abscenceform|crispy}} {{abscenceform|crispy}}
</form>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-primary">Abwesenheit beantragen</button> <button type="submit" class="btn btn-primary">Abwesenheit beantragen</button>
<button type="button" class="btn btn-secondary" data-dismiss="modal">Abbrechen</button> <button type="button" class="btn btn-secondary" data-dismiss="modal">Abbrechen</button>
</div> </div>
</form>
</div>
</div>
</div>
<!-- MODAL -->
<div class="modal fade" tabindex="-1" id="updateAbsenceToConfirm" data-backdrop="static">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Abwesenheitsantrag</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<div id="confirmcontent"></div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal" onclick="javascript:closeOnly()">Schließen</button>
</div>
</div> </div>
</div> </div>
</div> </div>
<div class="modal fade" tabindex="-1" id="updateAbsenceToConfirmSecond" data-backdrop="static">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Abwesenheitsantrag</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<div id="confirmcontentsecond"></div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary" onclick="javascript:confirmAbscenceFinal()">Bestätigung</button>
<button type="button" class="btn btn-secondary" data-dismiss="modal">Abbrechen</button>
</div>
</div>
</div>
</div>
<style> <style>
.tm-ab-tdhover:hover{ .tm-ab-tdhover:hover{
background-color: #858796 !important; background-color: #858796 !important;
@ -78,20 +173,142 @@
<style> <style>
/* DATATABLES */ /* DATATABLES */
.paginate_button { .paginate_button {
padding: 0px !important; padding: 0px !important;
border: 0px !important; border: 0px !important;
} }
</style> </style>
<style>
.loader {
position: relative;
border: 7px solid #d3d3d3;
border-radius: 50%;
border-top: 7px solid red;
width: 70px;
height: 70px;
left:50%;
top:35%;
-webkit-animation: spin 1s linear infinite; /* Safari */
animation: spin 1s linear infinite;
}
#overlay{
position: absolute;
top:0px;
left:0px;
width: 100%;
height: 100%;
background: black;
opacity: .4;
}
.container{
position:relative;
height: 300px;
width: 200px;
border:1px solid red;
}
/* Safari */
@-webkit-keyframes spin {
0% { -webkit-transform: rotate(0deg); }
100% { -webkit-transform: rotate(360deg); }
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
</style>
<script> <script>
//Initial Load
function loaddjango(){ var absencetowork = "";
var absencestring_confirm = "";
var absencestring_noconfirm = "";
var newconfstat = "";
var closeonly = false;
function closeOnly(){
closeonly = true;
}
function confirmAbscenceFinal(){
$.ajax({ $.ajax({
type: "GET", type: "GET",
url: "{% url 'tm-ajax' %}", url: "{% url 'tm-ajax' %}",
data:{ data:{
action : "testdjango", action : "confirmornotabscence",
absencetowork : absencetowork,
newconfstat : newconfstat,
info : $("#id_confirm_info").val(),
activemonth : $("#id_activemonth").val(),
activeyear : $("#id_activeyear").val(),
},
success : function(data){
location.href = "/tm/abs/" + data["activemonth"] +"/" + data["activeyear"]
}
});
}
function openModalABChangeTable(idtopen){
closeonly = false;
absencetowork = idtopen;
$.ajax({
type: "GET",
url: "{% url 'tm-ajax' %}",
data:{
action : "getrenderedform",
abscenceid : idtopen
},
beforeSend: function(request) {
request.setRequestHeader("X-Requested-With", "XMLHttpRequest");
},
success : function(data){
$("#confirmcontent").html(data);
$("#updateAbsenceToConfirm").modal("show");
}
});
}
function noconfirmAbsence(id){
newconfstat = 2;
$("#updateAbsenceToConfirm").modal("toggle");
$('#updateAbsenceToConfirm').on('hidden.bs.modal', function () {
if(closeonly == false){
$("#updateAbsenceToConfirmSecond").modal("toggle");
$("#confirmcontentsecond").html(absencestring_noconfirm);
}
});
}
function confirmAbsence(id){
newconfstat = 0;
$("#updateAbsenceToConfirm").modal("toggle");
$('#updateAbsenceToConfirm').on('hidden.bs.modal', function () {
if(closeonly == false){
$("#updateAbsenceToConfirmSecond").modal("toggle");
$("#confirmcontentsecond").html(absencestring_confirm);
}
});
}
//Initial Load
function loaddjango(){
$("#id_activemonth").val("{{activemonth}}")
$("#id_activeyear").val("{{activeyear}}")
$("#id_prevmonth").val("{{prevmonth}}")
$("#id_nextmonth").val("{{nextmonth}}")
$("#id_prevyear").val("{{prevyear}}")
$("#id_nextyear").val("{{nextyear}}")
$.ajax({
type: "GET",
url: "{% url 'tm-ajax' %}",
data:{
action : "getrenderedtable",
activeyear : {{activeyear}}, activeyear : {{activeyear}},
activemonth : {{activemonth}} activemonth : {{activemonth}}
}, },
@ -101,14 +318,46 @@ function loaddjango(){
success : function(data){ success : function(data){
$("#rendered_table").html(data) $("#rendered_table").html(data)
} }
}); });
} }
$(document).ready(function(){ $(document).ready(function(){
loaddjango(); loaddjango();
moment.locale('de');
var activeTab = localStorage.getItem('activeTabAbsence');
if(activeTab){
if($('#' + activeTab).find().prevObject.length != 0){
$('#' + activeTab).tab('show');
$(".nav-link").removeClass("active");
$("#" + activeTab + "-tab").addClass("active");
}
else{
$("#team-tab").addClass("active");
$('#team').tab('show');
}
}
else{
$("#team-tab").addClass("active");
$('#team').tab('show');
}
}); });
$('#absencetabs a').on('click', function (e) {
e.preventDefault();
lastview_name = $(this)[0]['hash'].substring(1);
localStorage.setItem('activeTabAbsence', lastview_name);
});
</script> </script>
{% else %} {% else %}
<h3>Das Modul Abwesenheits- und Zeiterfassung wurde in ihrer Agentur deaktiviert.</h3> <h3>Das Modul Abwesenheits- und Zeiterfassung wurde in ihrer Agentur deaktiviert.</h3>

View File

@ -0,0 +1,57 @@
{% load counter_tag %}
<div class="row col-12 mt-3" >
<div class="table-responsive">
<table class="table table-hover" id="table_toconfirmab">
<thead>
<tr>
<th scope="col">Mitarbeiter</th>
<th scope="col">Start</th>
<th scope="col">Ende</th>
<th scope="col">Grund</th>
<th scope="col">Info</th>
<th scope="col">Status ändern</th>
</tr>
</thead>
<tbody id="">
{% for abday in needtoconfirm %}
<tr>
<td>{{abday.user.first_name}} {{abday.user.last_name}}</td>
<td>{{abday.start|date:"d.M Y"}}</td>
<td>{{abday.end|date:"d.M Y"}}</td>
<td>{{abday.reason.name}}</td>
<td>{{abday.info}}</td>
<td>
<button type="button " class="btn btn-secondary btn-sm" onclick='javascript:openModalABChangeTable({{abday.pk}})'><i class="fas fa-eye"></i></button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
<script type="text/javascript">
$(document).ready(function(){
$('#table_toconfirmab').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>

View File

@ -0,0 +1,50 @@
<div class="row col-12 mt-3" >
<div class="table-responsive">
<table class="table table-hover" id="table_userownab">
<thead>
<tr>
<th scope="col">Start</th>
<th scope="col">Ende</th>
<th scope="col">Grund</th>
<th scope="col">Status</th>
<th scope="col">Begründung</th>
</tr>
</thead>
<tbody id="">
{% for abday in userown %}
<tr>
<td>{{abday.start|date:"d.M Y"}}</td>
<td>{{abday.end|date:"d.M Y"}}</td>
<td>{{abday.reason.name}}</td>
<td>{% if abday.confirm_status == 0 %} Genehmigt {% elif abday.confirm_status == 1 %} Beantragt {% else %} Abgelehnt {% endif %}</td>
<td>{{abday.confirm_info}}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
<script type="text/javascript">
$(document).ready(function(){
$('#table_userownab').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>

View File

@ -1,7 +1,7 @@
from django.shortcuts import render 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 JsonResponse from django.http import JsonResponse
from .models import Workday, Breaks, FreeDays from .models import Workday, Breaks, FreeDays, AbsenceReason, Absence
from django.utils import timezone from django.utils import timezone
import requests, csv, os import requests, csv, os
from django.templatetags.static import static from django.templatetags.static import static
@ -11,7 +11,11 @@ from django.contrib.auth.models import User
from calendar import monthrange from calendar import monthrange
import datetime import datetime
import calendar import calendar
from .forms import AddAbsence from .forms import AddAbsence, ConfirmAbsenceForm
from django.contrib import messages
from users.models import UserFullName
def loadingFreeDays(plz): def loadingFreeDays(plz):
# Getting land # Getting land
@ -46,66 +50,120 @@ def get_datetime_range(year, month):
@login_required @login_required
def AbsenceManagmenet(request, activemonth=False, activeyear=False): def AbsenceManagmenet(request, activemonth=False, activeyear=False):
prevmonth = ""
nextmonth = ""
# NEW ABSENCE
if(request.method == "POST"):
'''
#MONTH Nachdem eine neue Abwesenheit gespeichert wurde, geht es zur normalen Seite zurück, jedoch mit den Daten des
if(not activemonth or activemonth > 12 or activemonth < 1): aktuell angezeigten Monate/Jahr
activemonth = int(activemonth)
#Active month
activemonth=date.today().month
if(activemonth == 1): '''
prevmonth = 12 if(request.POST.get("form_type") == "absenceform"):
formtocheck = AddAbsence(request.POST, instance=request.user)
if(formtocheck.is_valid()):
try:
workinguser = UserFullName.objects.get(pk=formtocheck.cleaned_data["userid"])
# DIFFERENT USER AGENCY
if(workinguser.profile.agency != request.user.profile.agency):
messages.success(request, f'Das dürfen Sie nicht!')
return redirect('tma-management', formtocheck.cleaned_data['activemonth'], formtocheck.cleaned_data['activeyear'])
# ALL OK - START SAVING ABSENCE
else:
confirmstat = 0
if(request.user.has_perm("absencemanager") == False):
confirmstat = 1
messages.success(request, f'Abwesenheit beantragt')
# SEND NOTIFICATION
else:
messages.success(request, f'Abwesenheit eingetragen')
rep = None
if(formtocheck.cleaned_data["representator"] != None):
rep = User.objects.get(pk=formtocheck.cleaned_data["representator"].pk)
newab = Absence(agency=request.user.profile.agency, user=workinguser, start=formtocheck.cleaned_data["start"],end=formtocheck.cleaned_data["end"], representator=rep, confirm_status=confirmstat, info=formtocheck.cleaned_data["info"], reason=formtocheck.cleaned_data["reason"], start_ishalf=formtocheck.cleaned_data["start_ishalf"], end_ishalf=formtocheck.cleaned_data["end_ishalf"]).save()
# USER NOT FOUND
except:
messages.success(request, f'Fehler bei Benutzerzuweisung!')
return redirect('tma-management', formtocheck.cleaned_data['activemonth'], formtocheck.cleaned_data['activeyear'])
return redirect('tma-management', formtocheck.cleaned_data['activemonth'], formtocheck.cleaned_data['activeyear'])
else:
messages.success(request, f'Fehler beim eintragen der neuen Abwesenheit!')
return redirect('tma-management', formtocheck.cleaned_data['activemonth'], formtocheck.cleaned_data['activeyear'])
else: else:
prevmonth = activemonth-1 return redirect('tma-management')
# NORMAL VIEW
if(activemonth == 12):
nextmonth = 1
else:
nextmonth = activemonth + 1
else: else:
prevmonth = ""
nextmonth = ""
#MONTH
if(not activemonth or activemonth > 12 or activemonth < 1):
activemonth = int(activemonth)
#Active month
activemonth=date.today().month
if(activemonth == 1): if(activemonth == 1):
prevmonth = 12 prevmonth = 12
else:
prevmonth = activemonth-1
if(activemonth == 12):
nextmonth = 1
else:
nextmonth = activemonth + 1
else: else:
prevmonth = activemonth-1
if(activemonth == 12): if(activemonth == 1):
nextmonth = 1 prevmonth = 12
else:
prevmonth = activemonth-1
if(activemonth == 12):
nextmonth = 1
else:
nextmonth = activemonth + 1
#YEAR
nextyear = date.today().year
prevyear = date.today().year
if(not activeyear):
activeyear = date.today().year
else: else:
nextmonth = activemonth + 1 if(nextmonth == 1):
nextyear = activeyear + 1
else:
nextyear = activeyear
if(prevmonth == 12):
prevyear = activeyear - 1
else:
prevyear = activeyear
#YEAR context = {
nextyear = date.today().year "active_link" : "abscence",
prevyear = date.today().year "usersofagency" : User.objects.filter(profile__agency=request.user.profile.agency).order_by("-last_name"),
"days_this_month" : get_datetime_range(activeyear,activemonth),
"nextmonth" : nextmonth,
"prevmonth" : prevmonth,
"nextyear" : nextyear,
"prevyear" : prevyear,
"activemonth" : activemonth,
"activeyear" : activeyear,
"abscenceform" : AddAbsence(instance=request.user),
"userown" : Absence.objects.filter(agency=request.user.profile.agency, user=request.user).order_by("start")
}
if(not activeyear): if(request.user.has_perm("users.absencemanager")):
activeyear = date.today().year context.update({
else: "needtoconfirm" : Absence.objects.filter(agency=request.user.profile.agency, confirm_status=1).order_by("-start"),
if(nextmonth == 1): "allabsences" : Absence.objects.filter(agency=request.user.profile.agency).order_by("-start")
nextyear = activeyear + 1 })
else:
nextyear = activeyear
if(prevmonth == 12):
prevyear = activeyear - 1
else:
prevyear = activeyear
context = { return render(request, 'timemanagement/tm_ab_management.html', context)
"active_link" : "abscence",
"usersofagency" : User.objects.filter(profile__agency=request.user.profile.agency).order_by("-last_name"),
"days_this_month" : get_datetime_range(activeyear,activemonth),
"nextmonth" : nextmonth,
"prevmonth" : prevmonth,
"nextyear" : nextyear,
"prevyear" : prevyear,
"activemonth" : activemonth,
"activeyear" : activeyear,
"abscenceform" : AddAbsence()
}
return render(request, 'timemanagement/tm_ab_management.html', context)
@login_required @login_required
@ -187,8 +245,8 @@ def TimeAjax(request):
} }
else: else:
data = { "success" : False} data = { "success" : False}
# REMOVE WORKDAY # Get Rendered Table
elif request.GET["action"] == "testdjango": elif request.GET["action"] == "getrenderedtable":
prevmonth = "" prevmonth = ""
nextmonth = "" nextmonth = ""
activemonth = int(request.GET["activemonth"]) activemonth = int(request.GET["activemonth"])
@ -248,9 +306,57 @@ def TimeAjax(request):
"nextyear" : nextyear, "nextyear" : nextyear,
"prevyear" : prevyear "prevyear" : prevyear
} }
return render(request, "timemanagement/rendered_table.html", context) return render(request, "timemanagement/rendered_table.html", context)
# Get Rendered Table
elif request.GET["action"] == "checkrequired":
reason = AbsenceReason.objects.get(pk=request.GET["rid"])
if(reason.agency == request.user.profile.agency):
data = {
"success" : True,
"isreq" : reason.need_rep
}
else:
data = {
"success" : False
}
# DELETE ABSENCE
elif request.GET["action"] == "remove_absence":
absence = Absence.objects.get(pk=request.GET["ab"])
if(request.user.has_perm("users.absencemanager") and absence.agency == request.user.profile.agency):
absence.delete()
data = {
"success" : True
}
# GET FORM FOR CONFIRM ABSENCE
elif request.GET["action"] == "getrenderedform":
context = {
"confirmform" : ConfirmAbsenceForm(instance=request.user),
"absence" : Absence.objects.get(pk=request.GET["abscenceid"])
}
return render(request, "timemanagement/rendered_confirmform.html", context)
elif request.GET["action"] == "confirmornotabscence":
absence = Absence.objects.get(pk=request.GET["absencetowork"])
new_stat = request.GET["newconfstat"]
info = request.GET["info"]
if(absence.user.profile.agency == request.user.profile.agency and request.user.has_perm("users.absencemanager")):
absence.confirm_status = new_stat
absence.confirm_info = info
absence.save()
messages.success(request, f'Abwesenheit gespeichert!')
else:
messages.success(request, f'Das dürfen Sie nicht!')
data = {
"success" : True,
"activemonth" : request.GET["activemonth"],
"activeyear" : request.GET["activeyear"]
}
else: else:
data = { data = {
"success" : False "success" : False
@ -258,3 +364,4 @@ def TimeAjax(request):
return JsonResponse(data) return JsonResponse(data)

View File

@ -6,7 +6,7 @@ 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, AbsenceReason, FreeDays from timemanagement.models import Workday, Breaks, AbsenceReason, FreeDays, Absence
admin.site.register(StandardComments) admin.site.register(StandardComments)
admin.site.register(StandardCommentRate) admin.site.register(StandardCommentRate)
@ -25,4 +25,5 @@ admin.site.register(UserTime)
admin.site.register(Workday) admin.site.register(Workday)
admin.site.register(Breaks) admin.site.register(Breaks)
admin.site.register(AbsenceReason) admin.site.register(AbsenceReason)
admin.site.register(Absence)
admin.site.register(FreeDays) admin.site.register(FreeDays)

View File

@ -252,7 +252,9 @@ class UserTime(models.Model):
wd_th = models.FloatField(default=8.0) wd_th = models.FloatField(default=8.0)
wd_fr = models.FloatField(default=8.0) wd_fr = models.FloatField(default=8.0)
holiday = models.FloatField(default=24.0) holiday = models.FloatField(default=24.0)
holiday_start = models.FloatField(default=0.0)
loose_holidedate = models.DateField(default=datetime.date(datetime.datetime.now().year + 1, 4,30)) loose_holidedate = models.DateField(default=datetime.date(datetime.datetime.now().year + 1, 4,30))
startdate = models.DateTimeField(default=None, blank=True, null=True)
''' '''

Binary file not shown.

Before

Width:  |  Height:  |  Size: 456 KiB

After

Width:  |  Height:  |  Size: 149 KiB