Zeiterfassung für QS bereit

This commit is contained in:
Holger Trampe 2020-07-26 12:30:54 +02:00
parent a8ba4a3ad5
commit 1702272929
9 changed files with 514 additions and 134 deletions

View File

@ -551,10 +551,16 @@ def gettimeoveralldiff(workday, user):
# Ladet das aktuelle Gleitzeitkonto
'''
Es werden nur Tage berücksichtigt, die in der Vergangenheit liegen!
'''
@register.simple_tag
def loadaccounttime(user):
status = 0
workdays = Workday.objects.filter(user=user).exclude(end=None)
today = date.today()
workdays = Workday.objects.filter(user=user, start__lt=today).exclude(end=None)
finalaccounttimesum = datetime.timedelta(minutes=0)

View File

@ -162,6 +162,29 @@ class UpdateWorkdayForm(forms.ModelForm):
self.fields['start'].required = True
self.fields['end'].required = True
# ADD WORKDAY FORM
class AddWorkdayForm(forms.ModelForm):
class Meta:
model = Workday
labels = {
"start" : "Start",
"end" : "Ende",
"target" : "Zielarbeitszeit"
}
fields = [
"start", "end", "target"
]
widgets = {
'start': DatePickerInput(options={"format":'DD.MM.YYYY HH:mm', "locale":'de'}),
'end': DatePickerInput(options={"format":'DD.MM.YYYY HH:mm', "locale":'de'}),
}
def __init__(self, *arg, **kwargs):
super(AddWorkdayForm, self).__init__(*arg, **kwargs)
self.fields['start'].required = True
self.fields['end'].required = True
# ADD BREAK FORM
class AddBreakForm(forms.ModelForm):
class Meta:

View File

@ -0,0 +1,22 @@
{% extends "users/base.html" %}
{% block content %}
{% load crispy_forms_tags %}
{% load counter_tag %}
{% if request.user.profile.agency.module_timemanagement %}
<div class="content-section col-5">
<h3>Arbeitstag hinzufügen</h3>
<hr>
<h5>Start- und Endzeitpunkt</h5>
<div class="col-6" style="margin-left: -10px;">
<form method="POST" class="">
{% csrf_token %}
{{form.media}}
{{form|crispy}}
</div>
<hr>
<a class="btn" href="{% url 'tm-management' %} ">Abbrechen</a>
<button type="submit" class="btn btn-primary" style="float: right" id="addbreakbutton">Arbeitstag hinzufügen</button>
</form>
</div>
{% endif %}
{% endblock content %}

View File

@ -4,7 +4,7 @@
{% if request.user.profile.agency.module_timemanagement %}
<div class="content-section col-12">
<h3>Zeiterfassung&nbsp;<small><i data-toggle="tooltip" data-placement="top" title="Bearbeiten Sie hier Ihre Zeiterfassung." class="far fa-question-circle"></i></small>
<button type="button" class="btn btn-primary btn-sm" style="position: relative; float: right !important;" onclick=""><i class="fas fa-plus"></i>&nbsp;Arbeitstag</button>
<a type="button" class="btn btn-primary btn-sm" style="position: relative; float: right !important;" href="{% url 'tm-add' %}"><i class="fas fa-plus"></i>&nbsp;Arbeitstag</a>
</h3>
<hr>
@ -15,32 +15,99 @@
<button type="button" class="btn btn-primary mr-1" style="min-width: 150px !important;" onclick="">{{active_month}} {{active_year}}</button>
<a type="button" class="btn btn-primary mr-1" href="{% url 'tm-management' next_month next_year %}"><i class="fas fa-arrow-circle-right"></i></a>
</div>
{% for workday in workdays %}
{% if workday.start.weekday == 0 and forloop.counter > 1%}
</div>
<div class="col-12 mb-2" style="float: left;">
<hr>
<div style="float: left;"><h4>{{workday.start|date:"W"}}. Woche</h4></div>
<br /><br />
{% elif forloop.counter == 1 %}
<div class="col-12 mb-2" style="float: left;">
<hr>
<div style="float: left;"><h4>{{workday.start|date:"W"}}. Woche</h4></div>
<br /><br />
{% endif %}
<div class="card col-2 mr-2" style="float: left; margin-top: -12px;">
<div class="card-body">
<h5 class="card-title">{{workday.start|date:"l"}}, {{workday.start|date:"d.m"}}</h5>
Von {{workday.start|date:"H:i"}} bis {{workday.end|date:"H:i"}}<br />
<small>
<hr>
<div class="table-responsive ">
<table class="table table-hover" id="table_timemanagement" >
<thead>
<tr>
<th scope="col"></th>
<th scope="col"></th>
<th scope="col">Start</th>
<th scope="col">Ende</th>
<th scope="col">Arbeitszeit</th>
<th scope="col">Pausen</th>
<th scope="col">Gesamtzeit</th>
<th scope="col">Gleitzeit</th>
<th scope="col">&nbsp;</th>
</tr>
</thead>
<tbody id="table_contacts" >
{% for da in days_this_month %}
{% getabscenceday request.user request.user da as abday %}
<tr id="da_{{da|date:"d-m-y"}}"
{% if da.weekday == 5 or da.weekday == 6 %}
style="background-color: #d3d3d3;"
{% elif abday != False %}
style="background-color: {{abday.reason.color}}; color: #ffffff"
{% endif %}>
<td>
{{da|date:"l"}}
</td>
<td>
{{da|date:"d.m.y"}}
</td>
<td>
{% if abday != False %}
{{abday.reason}}
{% else %}
{% if abday == False %}
{% for workday in workdays %}
{% if workday.start|date:"d-m-y" == da|date:"d-m-y" %}
{{workday.start|date:"H:i"}}
{% endif %}
{% endfor %}
{% endif %}
{% endif %}
</td>
<td>
{% if abday == False %}
{% for workday in workdays %}
{% if workday.end|date:"d-m-y" == da|date:"d-m-y" %}
{{workday.end|date:"H:i"}}
{% endif %}
{% endfor %}
{% endif %}
</td>
<td>
{% if abday == False %}
{% for workday in workdays %}
{% if workday.start|date:"d-m-y" == da|date:"d-m-y" %}
{% getsumworkdayexcludebreak workday as sumworkday %}
Arbeitszeit: {{ sumworkday }}<br />
{{ sumworkday }}
{% endif %}
{% endfor %}
{% endif %}
</td>
<td>
{% if abday == False %}
{% for workday in workdays %}
{% if workday.start|date:"d-m-y" == da|date:"d-m-y" %}
{% getsumbreak workday as sumbreakofday %}
Pausen: {{sumbreakofday}} min. ({{workday.breaks.all|length}})
{% getsumworkday workday as sumwd %}<br />
Gesamtzeit: {{sumwd}}<br />
Gleitzeit:
{{sumbreakofday}} min. ({{workday.breaks.all|length}})
{% endif %}
{% endfor %}
{% endif %}
</td>
<td>
{% if abday == False %}
{% for workday in workdays %}
{% if workday.start|date:"d-m-y" == da|date:"d-m-y" %}
{% getsumworkday workday as sumwd %}
{{sumwd}}
{% endif %}
{% endfor %}
{% endif %}
</td>
<td>
{% if abday == False %}
{% for workday in workdays %}
{% if workday.start|date:"d-m-y" == da|date:"d-m-y" %}
{% gettimeoveralldiff workday user as erg%}
{% if erg.1 == 0 %}
<span style="color: green">+{{erg.0}}</span>
@ -49,14 +116,29 @@
{% else %}
<span style="color: red">-{{erg.0}}</span>
{% endif %}
</small>
<button style="float: right; margin-right: 10px; margin-bottom: -40px;" class="btn btn-secondary btn-sm " onclick="javascript:$('#confirm-delete_{{workday.pk}}').modal('toggle')"><small><i class="fas fa-trash"></i></small></button>
<button style="float: right; margin-right: -22px; margin-bottom: -40px;" class="btn btn-secondary btn-sm ml-2" onclick="window.location.href='{% url 'tm-update' workday.pk %}'"><small><i class="fas fa-pen"></i></small></button>
</div>
</div>
{% endif %}
{% endfor %}
</div> <!-- CLOSE GRID DAY DIV -->
{% endif %}
</td>
<td>
{% if abday == False %}
{% for workday in workdays %}
{% if workday.start|date:"d-m-y" == da|date:"d-m-y" %}
<button class="btn btn-secondary btn-sm ml-2" onclick="window.location.href='{% url 'tm-update' workday.pk %}'"><small><i class="fas fa-pen"></i></small></button>
<button class="btn btn-secondary btn-sm " onclick="javascript:$('#confirm-delete_{{workday.pk}}').modal('toggle')"><small><i class="fas fa-trash"></i></small></button>
{% endif %}
{% endfor %}
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% if user.usertime.usetime_start == None %}
<div class="modal fade" tabindex="-1" role="dialog" data-backdrop="static" id="missingdatainfo">
<div class="modal-dialog" role="document">
@ -99,7 +181,7 @@
</button>
</div>
<div class="modal-body">
Es wurden keine Arbeitstage bis zum Beginn der Zeiterfassung am {{user.usertime.usetime_start|date:"d.m.Y"}} gefunden. Sollen diese Tage mit der Regelarbeitszeit aufgefüllt werden, welche bei den Vertragsdaten hinterlegt wurde?
Es wurden keine Arbeitstage bis zum Beginn der Zeiterfassung am {{user.usertime.usetime_start|date:"d.m.Y"}} gefunden. Sollen diese Tage mit der Regelarbeitszeit aufgefüllt werden, welche bei den Vertragsdaten hinterlegt wurde? Abwesenheiten werden nicht berücksichtigt!
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary" onclick="javascript:loadInitiDays()">Ja</button>
@ -152,7 +234,6 @@
</div>
</div>
<script type="text/javascript">
$("#dodel_{{workday.pk}}").click(function(){
$.ajax(
{
@ -164,8 +245,7 @@
},
success: function( data )
{
$("#wd_{{workday.pk}}").remove();
$("#confirm-delete_{{workday.pk}}").modal("toggle");
location.href = location.href;
}
});
});
@ -197,7 +277,9 @@ $(document).ready(function(){
},
"buttons" : {
"className" : "btn-danger"
}
},
"pageLength": 50,
"order": [[ 1, "asc" ]]
});
});

View File

@ -0,0 +1,208 @@
{% extends "users/base.html" %}
{% block content %}
{% load counter_tag %}
{% if request.user.profile.agency.module_timemanagement %}
<div class="content-section col-12">
<h3>Zeiterfassung&nbsp;<small><i data-toggle="tooltip" data-placement="top" title="Bearbeiten Sie hier Ihre Zeiterfassung." class="far fa-question-circle"></i></small>
<button type="button" class="btn btn-primary btn-sm" style="position: relative; float: right !important;" onclick=""><i class="fas fa-plus"></i>&nbsp;Arbeitstag</button>
</h3>
<hr>
<!-- TODO: Hier noch Jahr speichern und Monat usw. -->
<div class="btn-group" role="group" aria-label="" style="min-width: 100% !important">
<a type="button" class="btn btn-primary mr-1" href="{% url 'tm-management' prev_month prev_year %}" ><i class="fas fa-arrow-circle-left"></i></a>
<button type="button" class="btn btn-primary mr-1" style="min-width: 150px !important;" onclick="">{{active_month}} {{active_year}}</button>
<a type="button" class="btn btn-primary mr-1" href="{% url 'tm-management' next_month next_year %}"><i class="fas fa-arrow-circle-right"></i></a>
</div>
{% for workday in workdays %}
{% if workday.start.weekday == 0 and forloop.counter > 1%}
</div>
<div class="col-12 mb-2" style="float: left;">
<hr>
<div style="float: left;"><h4>{{workday.start|date:"W"}}. Woche</h4></div>
<br /><br />
{% elif forloop.counter == 1 %}
<div class="col-12 mb-2" style="float: left;">
<hr>
<div style="float: left;"><h4>{{workday.start|date:"W"}}. Woche</h4></div>
<br /><br />
{% endif %}
<div class="card col-2 mr-2" style="float: left; margin-top: -12px;">
<div class="card-body">
<h5 class="card-title">{{workday.start|date:"l"}}, {{workday.start|date:"d.m"}}</h5>
Von {{workday.start|date:"H:i"}} bis {{workday.end|date:"H:i"}}<br />
<small>
{% getsumworkdayexcludebreak workday as sumworkday %}
Arbeitszeit: {{ sumworkday }}<br />
{% getsumbreak workday as sumbreakofday %}
Pausen: {{sumbreakofday}} min. ({{workday.breaks.all|length}})
{% getsumworkday workday as sumwd %}<br />
Gesamtzeit: {{sumwd}}<br />
Gleitzeit:
{% gettimeoveralldiff workday user as erg%}
{% if erg.1 == 0 %}
<span style="color: green">+{{erg.0}}</span>
{% elif erg.1 == 1 %}
<span>{{erg.0}}</span>
{% else %}
<span style="color: red">-{{erg.0}}</span>
{% endif %}
</small>
<button style="float: right; margin-right: 10px; margin-bottom: -40px;" class="btn btn-secondary btn-sm " onclick="javascript:$('#confirm-delete_{{workday.pk}}').modal('toggle')"><small><i class="fas fa-trash"></i></small></button>
<button style="float: right; margin-right: -22px; margin-bottom: -40px;" class="btn btn-secondary btn-sm ml-2" onclick="window.location.href='{% url 'tm-update' workday.pk %}'"><small><i class="fas fa-pen"></i></small></button>
</div>
</div>
{% endfor %}
</div> <!-- CLOSE GRID DAY DIV -->
{% if user.usertime.usetime_start == None %}
<div class="modal fade" tabindex="-1" role="dialog" data-backdrop="static" id="missingdatainfo">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Fehlende Informationen</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
Achtung! Es wurde kein Datum gefunden, ab wann die Zeiterfassung beginnen soll. Bitte tragen Sie dies in Ihrem Mitarbeiterkonto unter Einstellungen, Mitarbeiter, Ihre Vertragsdaten nach.
<br />
<small>Dies können nur Mitarbeiter eintragen, die das Recht haben, Mitarbeiter zu verwalten.</small>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary" data-dismiss="modal">Schließen</button>
</div>
</div>
</div>
</div>
<script type="text/javascript">
$(document).ready(function(){
$("#missingdatainfo").modal("toggle");
})
$('#missingdatainfo').on('hidden.bs.modal', function (e) {
location.href = "{% url 'dasettings' %}";
})
</script>
{% elif userhasworkdays == False %}
<div class="modal fade" tabindex="-1" role="dialog" data-backdrop="static" id="initialload">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Arbeitstage auffüllen</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
Es wurden keine Arbeitstage bis zum Beginn der Zeiterfassung am {{user.usertime.usetime_start|date:"d.m.Y"}} gefunden. Sollen diese Tage mit der Regelarbeitszeit aufgefüllt werden, welche bei den Vertragsdaten hinterlegt wurde? Abwesenheiten werden nicht berücksichtigt!
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary" onclick="javascript:loadInitiDays()">Ja</button>
<button type="button" class="btn btn" data-dismiss="modal">Nein</button>
</div>
</div>
</div>
</div>
<script type="text/javascript">
$(document).ready(function(){
$("#initialload").modal("toggle");
});
function loadInitiDays(){
$.ajax(
{
type: "GET",
url: "{% url 'tm-ajax' %}",
data:{
action : "initial_load",
},
success: function( data )
{
location.href = location.href;
}
});
}
</script>
{% endif %}
{% for workday in workdays %}
<div class="modal fade" id="confirm-delete_{{workday.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">Arbeitstag löschen</h5>
</div>
<div class="modal-body">
Möchten Sie wirklich den Arbeitstag am {{workday.start|date:"d.m"}} löschen?
</div>
<div class="modal-footer">
<button class="btn btn-primary" id="dodel_{{workday.pk}}" >Löschen</button>
<button type="button" class="btn" data-dismiss="modal">Abbrechen</button>
</div>
</div>
</div>
</div>
<script type="text/javascript">
$("#dodel_{{workday.pk}}").click(function(){
$.ajax(
{
type: "GET",
url: "{% url 'tm-ajax' %}",
data:{
action : "remove_workday",
workday: {{workday.pk}},
},
success: function( data )
{
$("#wd_{{workday.pk}}").remove();
$("#confirm-delete_{{workday.pk}}").modal("toggle");
}
});
});
</script>
{% endfor %}
<style>
/* DATATABLES */
.paginate_button {
padding: 0px !important;
border: 0px !important;
}
</style>
<script>
$(document).ready(function(){
$('#table_timemanagement').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 Abwesenheits- und Zeiterfassung wurde in ihrer Agentur deaktiviert oder die Zeiterfassung wurde im Modul deaktiviert.</h3>
{% endif %}
{% endblock content %}

View File

@ -16,7 +16,7 @@
</div>
<hr>
<h5>Pausen
<button class="btn btn-primary btn-sm" style="float: right" onclick="window.location.href='{% url 'add-break' workday.pk %}'"><i class="fas fa-plus"></i></button>
<a class="btn btn-primary btn-sm" style="float: right" href="{% url 'add-break' workday.pk %}"><i class="fas fa-plus"></i></a>
</h5>
<div col="col-10">
<table class="table table-hover" >

View File

@ -1,6 +1,6 @@
from django.urls import path
from django.contrib.auth.decorators import login_required, permission_required
from .views import TimeManagement, TimeAjax, AbsenceManagmenet, AbsenceUpdate, TimeUpdate, AddBreak
from .views import TimeManagement, TimeAjax, AbsenceManagmenet, AbsenceUpdate, TimeUpdate, AddBreak, TimeAdd
'''
Permissions definiert in models.py bei USERS und dann hier vor die View geschrieben!
'''
@ -9,6 +9,7 @@ urlpatterns = [
path('', TimeManagement, name='tm-management'),
path('<int:activemonth>/<int:activeyear>', TimeManagement, name='tm-management'),
path('update/<int:pk>', TimeUpdate, name='tm-update'),
path('add/', TimeAdd, name='tm-add'),
path('update/<int:pk>/addbreak/', AddBreak, name='add-break'),
path('abs/', AbsenceManagmenet, name='tma-management'),
path('abs/<int:activemonth>/<int:activeyear>', AbsenceManagmenet, name='tma-management'),

View File

@ -11,7 +11,7 @@ from django.contrib.auth.models import User
from calendar import monthrange
import datetime
import calendar
from .forms import AddAbsence, ConfirmAbsenceForm, UpdateAbsence, UpdateWorkdayForm, AddBreakForm
from .forms import AddAbsence, ConfirmAbsenceForm, UpdateAbsence, UpdateWorkdayForm, AddBreakForm, AddWorkdayForm
from django.contrib import messages
from users.models import UserFullName, UserYearAbsenceInfo
from users.models import UserTime
@ -328,9 +328,11 @@ def TimeManagement(request, activemonth=False, activeyear=False):
"active_year" : active_year,
"active_month" : active_month,
"active_link" : "timemanagement",
"days_this_month" : get_datetime_range(int(active_year), int(activemonth)),
"workdays" : Workday.objects.filter(agency=request.user.profile.agency, user=request.user, start__month=activemonth, start__year=active_year).order_by("start").exclude(end=None),
"userhasworkdays" : user_has_workdays
}
return render(request, 'timemanagement/timemanagement_management.html', context)
@login_required
@ -358,6 +360,42 @@ def TimeUpdate(request, pk):
}
return render(request, 'timemanagement/timemanagement_update.html', context)
@login_required
def TimeAdd(request):
if(request.method == "POST"):
form = AddWorkdayForm(request.POST, instance=request.user)
if form.is_valid():
#start = datetime.datetime(int(workday.start.year), int(workday.start.month), int(workday.start.day), int(((str(form["start"].value()).split(":"))[0])), int(((str(form["start"].value()).split(":"))[1])))
#end = datetime.datetime(int(workday.end.year), int(workday.end.month), int(workday.end.day), int(((str(form["end"].value()).split(":"))[0])), int(((str(form["end"].value()).split(":"))[1])))
workday = Workday(start=form.cleaned_data["start"], end=form.cleaned_data["end"], target=form.cleaned_data["target"], user=request.user, agency=request.user.profile.agency)
if workday.start.day != workday.end.day or workday.start.month != workday.end.month or workday.start.year != workday.end.year or workday.start > workday.end:
messages.success(request, f'Der Arbeitstag darf nur an einem Tag stattfinden und das Ende muss nach dem Anfang liegen.')
context = {
"active_link" : "timemanagement",
"form" : AddWorkdayForm()
}
return render(request, 'timemanagement/timemanagement_add.html', context)
else:
workday.save()
messages.success(request, f'Arbeitstag hinzugefügt')
return redirect('tm-management')
else:
messages.success(request, f'Bitte valide Daten eingeben!')
context = {
"active_link" : "timemanagement",
"form" : AddWorkdayForm()
}
return render(request, 'timemanagement/timemanagement_add.html', context)
else:
context = {
"active_link" : "timemanagement",
"form" : AddWorkdayForm()
}
return render(request, 'timemanagement/timemanagement_add.html', context)
@login_required
def AddBreak(request, pk):
if(request.method == "POST"):