Zeiterfassung bis auf Bearbeiten durch andere Mitarbeiter fertig für Betatest

This commit is contained in:
Holger Trampe 2020-07-29 13:45:34 +02:00
parent 51d7a746f7
commit 0de2a48c9e
7 changed files with 503 additions and 338 deletions

View File

@ -1,15 +1,16 @@
{% load counter_tag %}
<a href="{% url 'newuserfirst' %}"class="btn btn-primary active" data-toggle="tooltip" data-placement="top" title="Fügen Sie hier einen weiteren Mitarbeiter Ihrer Agentur hinzu."><i class="fas fa-plus"></i>&nbsp;Mitarbeiter</a>
<hr>
<div class="row">
<div class="table-responsive">
<div class="row">
<div class="table-responsive">
<table class="table hover" id="usertableall">
<thead>
<tr>
<th scope="col">Vorname</th>
<th scope="col">Nachname</th>
<th scope="col">Vorname</th>
<th scope="col">Nachname</th>
<th scope="col">E-Mail</th>
<th scope="col">Agenturfunktion</th>
<th scope="col">Gleitzeit</th>
<th scope="col">Letzter Login</th>
<th scope="col">Tätigkeit</th>
<th scope="col">Telefon</th>
@ -21,21 +22,33 @@
{% for item in usersofagency %}
<tr>
<td><a href="{% url 'user_updateprofile' item.pk 0 %}">{{item.first_name }}</a></td>
<td><a href="{% url 'user_updateprofile' item.pk 0 %}">{{ item.last_name }}</a></td>
<td><a href="{% url 'user_updateprofile' item.pk 0 %}">{{ item.last_name }}</a></td>
<td>{{ item.email }}</td>
<td>{% if item.profile.func == None %}-{%else%}{{ item.profile.func }}{%endif%}</td>
<td>
{% if item.usertime.usetime %}
{% loadaccounttime item as actualaccounttime %}
{% if actualaccounttime.1 == 0 %}
<b><span style="color: green">+{{actualaccounttime.0}}</span></b>
{% else %}
<b><span style="color: red">-{{actualaccounttime.0}}</span></b>
{% endif %}
{% else %}
-
{% endif %}
</td>
<td>{% if item.last_login != Nonte %}{{ item.last_login }}{% endif %}</td>
<td>{{ item.profile.compfunc }}</td>
<td>{{ item.profile.phoneland }}</td>
<td>{{ item.profile.phonemobile }}</td>
<td>
{% if item != request.user %}
{% if item != request.user %}
<a href="{% url 'users-delete' item.pk %}" ><button class="btn btn-sm btn-secondary"><i class="fas fa-trash"></i></button></a>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
{% endfor %}
</tbody>
</table>
</div>
</div>
@ -51,7 +64,7 @@
<script>
$(document).ready(function(){
$('#usertableall').DataTable({
"order" : [],
"language": {
@ -71,9 +84,8 @@ $(document).ready(function(){
"className" : "btn-danger"
}
});
});
</script>

View File

@ -17,3 +17,13 @@ six==1.13.0
sqlparse==0.3.0
urllib3==1.25.7
webcolors==1.10
django-summernote==0.8.11.6
django-mathfilters==1.0.0
django-cleanup==5.0.0
django-user-agents==0.4.0
djangorestframework==3.11.0
django-channels==0.7.0
django-channels-presence==1.0.0
django-cryptography==1.0
channels-redis==3.0.1
filetype==1.0.7

View File

@ -20,9 +20,7 @@
<!--
# TODO: Sichtbarkeit durch andere Nutzer absprechen?
# TODO: Bis wann darf rückwirkend eine Abwesenheit bearbeitet werden? Monatlich?
# TODO: CronJob einmal umbauen
# TODO: Halber Tag mit Workday verbinden
Aktuell sieht man den Gleitzeitkontostand in der Mitarbeitertabelle, ein Bearbeiten muss ich nachschieben
-->
<div class="table-responsive ">
@ -63,14 +61,129 @@
{% if abday.start == da%}
{% if abday.startday_info == "1" %}
(nur Vormittags)
{% for workday in workdays %}
{% if workday.start|date:"d-m-y" == da|date:"d-m-y" %}
<br />
{{workday.start|date:"H:i"}} - {{workday.end|date:"H:i"}},
{% getsumworkdayexcludebreak workday as sumworkday %}
{{ sumworkday }},
{% getsumbreak workday as sumbreakofday %}
{{sumbreakofday}} min. ({{workday.breaks.all|length}}),
{% getsumworkday workday as sumwd %}
{{sumwd}},
{% 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 %}
{% if breakmonthline < da %}
<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 %}
{% endif %}
{% endfor %}
{% elif abday.startday_info == "2" %}
(nur Nachmittags)
{% for workday in workdays %}
{% if workday.start|date:"d-m-y" == da|date:"d-m-y" %}
<br />
{{workday.start|date:"H:i"}} - {{workday.end|date:"H:i"}}
{% getsumworkdayexcludebreak workday as sumworkday %}
{{ sumworkday }},
{% getsumbreak workday as sumbreakofday %}
{{sumbreakofday}} min. ({{workday.breaks.all|length}}),
{% getsumworkday workday as sumwd %}
{{sumwd}},
{% 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 %}
{% if breakmonthline < da %}
<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 %}
{% endif %}
{% endfor %}
{% endif %}
{% elif abday.end == da%}
{% if abday.endday_info == "1" %}
(nur Vormittags)
{% for workday in workdays %}
{% if workday.start|date:"d-m-y" == da|date:"d-m-y" %}
<br />
{{workday.start|date:"H:i"}} - {{workday.end|date:"H:i"}}
{% getsumworkdayexcludebreak workday as sumworkday %}
{{ sumworkday }},
{% getsumbreak workday as sumbreakofday %}
{{sumbreakofday}} min. ({{workday.breaks.all|length}}),
{% getsumworkday workday as sumwd %}
{{sumwd}},
{% 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 %}
{% if breakmonthline < da %}
<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 %}
{% endif %}
{% endfor %}
{% elif abday.endday_info == "2" %}
(nur Nachmittags)
{% for workday in workdays %}
{% if workday.start|date:"d-m-y" == da|date:"d-m-y" %}
<br />
{{workday.start|date:"H:i"}} - {{workday.end|date:"H:i"}}
{% getsumworkdayexcludebreak workday as sumworkday %}
{{ sumworkday }},
{% getsumbreak workday as sumbreakofday %}
{{sumbreakofday}} min. ({{workday.breaks.all|length}}),
{% getsumworkday workday as sumwd %}
{{sumwd}},
{% 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 %}
{% if breakmonthline < da %}
<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 %}
{% endif %}
{% endfor %}
{% endif %}
{% endif %}
@ -212,9 +325,10 @@
{% counterWDUp %}
<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>
{% if breakmonthline < da %}
<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 %}
{% endif %}
{% endfor %}
</td>

View File

@ -321,6 +321,7 @@ def TimeManagement(request, activemonth=False, activeyear=False):
# Hier werden nur die Arbeitstage gefiltert, die auch aktuell zur Ansicht stehen sollen
context = {
"next_month" : next_month,
"breakmonthline" : date.today() - timedelta(days=30),
"prev_month" : prev_month,
"next_year" : next_year,
"prev_year" : prev_year,
@ -331,7 +332,7 @@ def TimeManagement(request, activemonth=False, activeyear=False):
"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)

View File

@ -12,43 +12,43 @@
<title>Digitale Agentur</title>
<!-- Custom fonts for this template-->
<link rel="canonical" href="https://www.digitale-agentur.com">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.js" type="text/javascript"></script>
<link href="{%static 'users/vendor/fontawesome-free/css/all.min.css' %}" rel="stylesheet" type="text/css">
<link href="https://fonts.googleapis.com/css?family=Nunito:200,200i,300,300i,400,400i,600,600i,700,700i,800,800i,900,900i" rel="stylesheet">
<!--<link href="{%static 'users/css/bootstrap.min.css' %}" rel="stylesheet">-->
<link href='https://fonts.googleapis.com/css?family=Roboto&display=swap' rel='stylesheet' type='text/css'>
<!-- include summernote css/js -->
<!--<link href="https://cdn.jsdelivr.net/npm/summernote@0.8.15/dist/summernote.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/summernote@0.8.15/dist/summernote.min.js"></script>-->
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.1/jquery-ui.min.js"></script>
<!-- <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>-->
<!-- CROPPER -->
<link href="{% static 'users/css/cropper.min.css' %}" type="text/css" rel="stylesheet">
<!-- DATATABLES -->
<link href="https://cdn.datatables.net/1.10.20/css/jquery.dataTables.min.css" type="text/css" rel="stylesheet">
<link href="https://cdn.datatables.net/1.10.20/css/dataTables.bootstrap4.min.css" type="text/css" rel="stylesheet">
<!-- Custom styles for this template-->
<link href="{% static 'users/css/sb-admin-2.css' %}" type="text/css" rel="stylesheet">
<link href="{% static 'users/css/theme.css' %}" type="text/css" rel="stylesheet">
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/summernote@0.8.16/dist/summernote-bs4.js"></script>
<link href="https://cdn.jsdelivr.net/npm/summernote@0.8.16/dist/summernote.css" rel="stylesheet" type="text/css">
<link href="https://cdn.jsdelivr.net/npm/summernote@0.8.16/dist/summernote.css" rel="stylesheet" type="text/css">
<script type="text/javascript" src="{% static 'summernote/lang/summernote-de-DE.min.js' %}"></script>
@ -59,8 +59,8 @@
<div id="wrapper">
<!-- Sidebar -->
<ul class=" bg-gray-900 sidebar sidebar-dark accordion fixed-top" id="accordionSidebar">
<!-- Sidebar -->
<ul class=" bg-gray-900 sidebar sidebar-dark accordion fixed-top" id="accordionSidebar">
<!-- Sidebar - Brand -->
<a class="sidebar-brand d-flex align-items-center justify-content-center" href="{% url 'users-dashboard' %}">
@ -148,7 +148,7 @@
<i class="fas fa-envelope"></i>
<span>Mitteilungen
{% if gs > 0 %}
<span class="badge badge-primary badge-counter" id="messcounter_badge" style="margin-right: 85px;"><span id="messcounter">{{gs}}</span></span>&nbsp;
<span class="badge badge-primary badge-counter" id="messcounter_badge" style="margin-right: 85px;"><span id="messcounter">{{gs}}</span></span>&nbsp;
{% endif %}
</span>
</a>
@ -162,7 +162,7 @@
{% else%}
<li class="nav-item">
{%endif%}
<a class="nav-link " href="{% url 'chat:chat-management' %}" aria-expanded="true">
<i class="fas fa-comments"></i>
<span>Chat&nbsp;<sup>BETA</sup></span>
@ -205,7 +205,7 @@
<style scoped>
#bottom_info {
position: absolute;
bottom: 0;
bottom: 0;
}
</style>
<div id="bottom_info">
@ -214,7 +214,7 @@
<li class="nav-item active">
{% else%}
<li class="nav-item">
{%endif%}
{%endif%}
<a class="nav-link " href="{% url 'dasettings' %}" aria-expanded="true" style="margin-top: -15px">
<i class="fas fa-cog"></i>
<span>Einstellungen</span>
@ -224,13 +224,13 @@
<li class="nav-item active">
{% else%}
<li class="nav-item">
{%endif%}
{%endif%}
<a class="nav-link " href="{% url 'supportda' %}" aria-expanded="true" style="margin-top: -15px">
<i class="fas fa-question"></i>
<span>Support</span>
</a>
</li>
<div style="" class="sidebar-heading ">
<!--<span style="float: left"><small>poweder by&nbsp;</small><img src="{% static 'users/img/VVE-Logo.png' %}" width="27%" class="mb-2"></span>-->
<img src="{% static 'users/img/VVE-Logo.png' %}" width="27%" class="mb-2">
@ -239,9 +239,9 @@
<a style="color: #999; text-decoration: none;" href="{% url 'impressumda' %}">Impressum</a>
</div>
<div style="margin-top: 10px; margin-bottom: 5px;" class="sidebar-heading">
Version 0.9.1
Version 0.9.2
</div>
</div>
</div>
</ul>
<!-- End of Sidebar -->
@ -267,7 +267,7 @@
<div id="content-wrapper">
<!-- Main Content -->
<!-- Topbar -->
<nav id="topnavbarmain" class="navbar navbar-expand navbar-light bg-white topbar fixed-top mb-4 static-top shadow" style="margin-left: 224px;">
<nav id="topnavbarmain" class="navbar navbar-expand navbar-light bg-white topbar fixed-top mb-4 static-top shadow" style="margin-left: 224px;">
<!-- Sidebar Toggle (Topbar) -->
<button id="sidebarToggleTop" class="btn btn-link d-md-none rounded-circle mr-3" onclick="javascript:toggleSidebar()">
<i class="fa fa-bars"></i>
@ -276,7 +276,7 @@
<!-- Topbar Search -->
<form class="d-none d-sm-inline-block form-inline mr-auto ml-md-3 my-2 my-md-0 mw-100 navbar-search">
<div class="input-group">
<input list="searchres" placeholder="Agenturweite Suche..." id="search_string" onkeyup="javascript:startSearch(this.value)" class="form-control bg-light border-0 small" >
<input list="searchres" placeholder="Agenturweite Suche..." id="search_string" onkeyup="javascript:startSearch(this.value)" class="form-control bg-light border-0 small" >
<!--
<input type="text" onkeyup="javascript:startSearch(this.value)" class="form-control bg-light border-0 small" placeholder="Suche..." aria-label="Suche" aria-describedby="basic-addon2" id="searchfield">
-->
@ -296,7 +296,7 @@
{% getUserIsRep request.user as repstring %}
{% if repstring != False %}
<div class="alert alert-info alert-dismissible" id="repholinfo" style="margin-bottom: -2px; text-align: center; display: none; float: right;"> <i class="fas fa-info-circle"></i>&nbsp;
<div class="alert alert-info alert-dismissible" id="repholinfo" style="margin-bottom: -2px; text-align: center; display: none; float: right;"> <i class="fas fa-info-circle"></i>&nbsp;
{{repstring}}
<button type="button" class="close" data-dismiss="alert" onclick="javascript:closeRepHolInfo()">&times;</button>
</div>
@ -316,7 +316,7 @@
{% endif %}
<!-- Topbar Navbar -->
<ul class="navbar-nav ml-auto ">
<ul class="navbar-nav ml-auto ">
{% if request.user.usertime.usetime and user.usertime.usetime_start != None %}
<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">
@ -325,7 +325,7 @@
<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
Zeiterfassung
</h6>
<div>
{% block timemanagement_content_realtime %}
@ -337,7 +337,7 @@
</li>
{% endif %}
<!-- ALERT_AREA -->
<!-- Nav Item - Alerts -->
<li class="nav-item dropdown no-arrow mx-1">
@ -363,9 +363,9 @@
border-radius: 50%;
-webkit-box-shadow: 0px 0px 4px 5px {{onlinecolor}};
-moz-box-shadow: 0px 0px 4px 5px {{onlinecolor}};
box-shadow: 0px 0px 4px 5px {{onlinecolor}};
box-shadow: 0px 0px 4px 5px {{onlinecolor}};
}
</style>
</style>
<div class="topbar-divider d-none d-sm-block"></div>
<!-- Nav Item - User Information -->
<li class="nav-item dropdown no-arrow">
@ -376,19 +376,19 @@
</a>
<!-- Dropdown - User Information -->
<div class="dropdown-menu dropdown-menu-right shadow animated--grow-in" aria-labelledby="userDropdown">
<a class="dropdown-item" onclick="javascript:changeOnlineStatus(0)" href="#/">
<a class="dropdown-item" onclick="javascript:changeOnlineStatus(0)" href="#/">
<i class="fas fa-circle mr-2" style="color: green"></i>
Online
</a>
<a class="dropdown-item" onclick="javascript:changeOnlineStatus(1)" href="#/">
<a class="dropdown-item" onclick="javascript:changeOnlineStatus(1)" href="#/">
<i class="fas fa-circle mr-2" style="color: red"></i>
Beschäftigt
</a>
<a class="dropdown-item" onclick="javascript:changeOnlineStatus(2)" href="#/">
<a class="dropdown-item" onclick="javascript:changeOnlineStatus(2)" href="#/">
<i class="fas fa-circle mr-2" style="color: yellow"></i>
Abwesend
</a>
<a class="dropdown-item" onclick="javascript:changeOnlineStatus(3)" href="#/">
<a class="dropdown-item" onclick="javascript:changeOnlineStatus(3)" href="#/">
<i class="fas fa-circle mr-2" style="color: grey"></i>
Offline anzeigen
</a>
@ -396,14 +396,14 @@
<div class="dropdown-divider"></div>
<!--<a class="dropdown-item" onclick="userGoToSettings({{user.pk}})" href="{% url 'orga-single' user.pk %}">-->
<a class="dropdown-item" onclick="userGoToSettings()" href="#/">
<a class="dropdown-item" onclick="userGoToSettings()" href="#/">
<i class="fas fa-user fa-sm fa-fw mr-2 text-gray-400"></i>
Einstellungen
</a>
<a class="dropdown-item" onclick="userGoToNotification()" href="#/">
</a>
<a class="dropdown-item" onclick="userGoToNotification()" href="#/">
<i class="fas fa-bell fa-sm fa-fw mr-2 text-gray-400"></i>
Benachrichtigungen
</a>
</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item" href="{% url 'users-logout' %}">
<i class="fas fa-sign-out-alt fa-sm fa-fw mr-2 text-gray-400"></i>
@ -416,12 +416,12 @@
<!-- End of Topbar -->
<!-- Begin Page Content -->
<div class="container-fluid" >
<div class="container-fluid" >
<div id="searchcontent" style="min-height: 100%; margin-top: 85px;">
</div>
<div id="maincontent" style="min-height: 100%; margin-top: 85px;" >
<div id="maincontent" style="min-height: 100%; margin-top: 85px;" >
<!-- MESSAGES -->
{% if messages %}
{% for message in messages %}
@ -434,17 +434,17 @@
{% endfor %}
{% endif %}
{% block content %}
{% endblock %}
{% endblock %}
</div>
<div style="height: 300px">&nbsp;</div>
</div> <!-- End of Main Content CONTAINER FLUID-->
</div> <!-- End of Main Content CONTAINER FLUID-->
<!-- End of Content Wrapper -->
{% if active_link != 'chat' %}
{% if request.user.profile.agency.module_chat %}
<div id="chat_alluserscontent" style="position: fixed; bottom: 75px; right: 36px; z-index: 999;"></div>
<button id="chatButton" class="btn btn-primary" style="position: fixed; right: 36px; bottom: 30px;"><i class="far fa-comments"></i></button>
<!-- CHATAREA -->
<!-- CHATAREA -->
<div id="dynamicchatwindow" class="col-4" style="position: fixed; bottom: 30px; right: 23px; display: none;z-index: 999;">
<div class="card">
@ -454,10 +454,10 @@
</div>
</div>
<script type="text/javascript">
</script>
{% endif %}
<!-- CHATAREA END -->
<!-- CHATAREA END -->
{% endif %}
</div>
</div>
@ -489,14 +489,14 @@
<script type="text/javascript" src="{%static 'users/vendor/jquery-easing/jquery.easing.min.js' %}"></script>
<!-- DATABLES JS -->
<script type="text/javascript" src="https://cdn.datatables.net/1.10.20/js/jquery.dataTables.min.js"></script>
<script type="text/javascript" src="https://cdn.datatables.net/1.10.20/js/dataTables.bootstrap4.min.js"></script>
<script type="text/javascript" src="https://cdn.datatables.net/1.10.20/js/dataTables.bootstrap4.min.js"></script>
<!-- Custom scripts for all pages-->
<script type="text/javascript" src="{%static 'users/js/sb-admin-2.js' %}"></script>
<!-- CUSTOM FONT -->
<!--<link href="{% static 'users/css/custom.css' %}" rel="stylesheet">-->
<!-- TABLE SORT -->
<!--<script src="https://cdn.datatables.net/1.10.20/js/jquery.dataTables.min.js"></script>-->
@ -509,14 +509,14 @@
<link href="{% static 'users/css/custom.css' %}" type="text/css" rel="stylesheet">
</body>
</html>
{% if request.user.profile.showtooltips %}
<script type="text/javascript">
$(document).ready(function(){
$('[data-toggle="tooltip"]').tooltip()
$(document).ready(function(){
$('[data-toggle="tooltip"]').tooltip()
});
</script>
{% endif %}
@ -524,18 +524,18 @@
<script type="text/javascript">
function userGoToSettings(){
localStorage.setItem('activeTabSettings', "profil");
localStorage.setItem('activeTabSettings', "profil");
location.href = "{% url 'dasettings' %}"
}
function userGoToNotification(){
localStorage.setItem('activeTabSettings', "notifications");
localStorage.setItem('activeTabSettings', "notifications");
location.href = "{% url 'dasettings' %}"
}
function clearSF(){
$("#searchcontent").empty();
$("#searchcontent").hide();
$("#searchcontent").hide();
$("#maincontent").show();
$("#search_string").val("");
}
@ -549,26 +549,26 @@ function clearSF(){
data:{
searchstring: searchstring
},
success: function( data )
{
success: function( data )
{
$("#maincontent").hide();
$("#searchcontent").show();
$("#searchcontent").html(data);
$("#searchcontent").html(data);
}
});
} else {
} else {
$("#searchcontent").empty();
$("#searchcontent").hide();
$("#searchcontent").hide();
$("#maincontent").show();
}
}
$(document).ready(function(){
$(document).ready(function(){
if($( window ).width() < 768)
{
$("#accordionSidebar").addClass("toggled");
$("#content-wrapper").css("margin-left" , "0px");
$("#content-wrapper").css("margin-left" , "0px");
$("#topnavbarmain").css("margin-left" , "0px");
@ -577,21 +577,21 @@ $(document).ready(function(){
});
// Toggle the side navigation
function toggleSidebar(){
function toggleSidebar(){
if(sidebar_hidden == false){
$("#accordionSidebar").addClass("toggled");
$("#accordionSidebar").addClass("toggled");
$("#content-wrapper").css("margin-left" , "105px");
$("#topnavbarmain").css("margin-left" , "105px");
$("#topnavbarmain").css("margin-left" , "105px");
sidebar_hidden = true;
}
else{
$("#accordionSidebar").removeClass("toggled");
$("#content-wrapper").css("margin-left" , "0px");
$("#topnavbarmain").css("margin-left" , "0px");
sidebar_hidden = false;
$("#content-wrapper").css("margin-left" , "0px");
$("#topnavbarmain").css("margin-left" , "0px");
sidebar_hidden = false;
}
}
@ -599,16 +599,16 @@ $( window ).resize(function() {
if($( window ).width() < 750)
{
$("#accordionSidebar").addClass("toggled");
$("#content-wrapper").css("margin-left" , "0px");
$("#topnavbarmain").css("margin-left" , "0px");
$("#content-wrapper").css("margin-left" , "0px");
$("#topnavbarmain").css("margin-left" , "0px");
sidebar_hidden = false;
}
else{
$("#accordionSidebar").removeClass("toggled");
$("#accordionSidebar").removeClass("toggled");
$("#content-wrapper").css("margin-left" , "212px");
$("#topnavbarmain").css("margin-left" , "224px");
sidebar_hidden = true;
sidebar_hidden = true;
}
});
/*
@ -624,8 +624,8 @@ function loadUnsendNotifications(){
data : {
action : "checknotifications"
},
success: function( data )
{
success: function( data )
{
$("#notification_items").html("");
notifications = data['unknownnotification'];
var i = 0;
@ -634,7 +634,7 @@ function loadUnsendNotifications(){
$("#notification_items").append('<span><a href="/'+notifications[i]['elelink']+'" id="notifyid_'+notifications[i]['not_id']+'" class="dropdown-item d-flex align-items-center"><div><div class="small text-gray-500">'+notifications[i]['date']+'</div>'+notifications[i]['text']+'</div></a></span>')
i = i + 1;
newunknownotificationscounter = newunknownotificationscounter + 1;
}
}
$("#notificationcounter").html("");
if(i > 0){$("#notificationcounter").html(i);}
@ -650,8 +650,8 @@ function loadUnviewnNotifications(){
data : {
action : "oldnotifications"
},
success: function( data )
{
success: function( data )
{
notifications = data['oldnotifications'];
i = 0;
$("#notification_items").html("");
@ -660,7 +660,7 @@ function loadUnviewnNotifications(){
$("#notification_items").append('<a href="/'+notifications[i]['elelink']+'" id="notifyid_'+notifications[i]['not_id']+'" class="dropdown-item d-flex align-items-center"><div><div class="small text-gray-500">'+notifications[i]['date']+'</div>'+notifications[i]['text']+'</div></a>')
i = i + 1;
}
}
}
}
});
}
@ -673,8 +673,8 @@ function changeNewNotToViewed(){
data : {
action : "newnotificationsviewed"
},
success: function( data )
{
success: function( data )
{
$("#notificationcounter").html("");
}
});
@ -711,12 +711,12 @@ $(document).ready(function(){
}
const mainwebsocket = new WebSocket(ws_string+window.location.host+"/ws/")
mainwebsocket.onmessage = function(e) {
if(e["data"] != "presence_update")
mainwebsocket.onmessage = function(e) {
if(e["data"] != "presence_update")
{
//HANDLER FOR ALL PUSHNOTIFICATIONS
if(e["data"].split("__")[0] == "pushnotification"){
if(e["data"].split("__")[0] == "pushnotification"){
/*
Check for Chat-Message in CHatview or invisible-Browser
@ -727,7 +727,7 @@ $(document).ready(function(){
if(finalsplit[0] != "Chat"){
var notify = new Notification('Digitale Agentur', {
body: e["data"].split("__")[1]
});
});
}
else{
{% if active_link == "chat" %}
@ -751,20 +751,20 @@ $(document).ready(function(){
else{
var notify = new Notification('Digitale Agentur', {
body: e["data"].split("__")[1]
});
}
});
}
}
}
}
}
loadUnsendNotifications();
loadUnviewnNotifications();
}
else{
else{
{% if active_link == "chat" %}
if(preventUpdatePresLive == false){
updatePresenceLive();
updatePresenceLive();
}
{% endif %}
}
@ -778,10 +778,10 @@ $(document).ready(function(){
//HEARTBEAT every minute
setInterval(function()
{
mainwebsocket.send(JSON.stringify("heartbeat"));
mainwebsocket.send(JSON.stringify("heartbeat"));
console.log("heartbeat is alive...");
},60000);
});
@ -793,8 +793,8 @@ function changeOnlineStatus(newstat){
data : {
newstat : newstat
},
success: function( data )
{
success: function( data )
{
if(data["newstat"] == "0"){
$("#userbaseprofilepicture").css({
'-webkit-box-shadow' : '0px 0px 4px 5px green',
@ -814,15 +814,15 @@ function changeOnlineStatus(newstat){
'-webkit-box-shadow' : '0px 0px 4px 5px orange',
'-moz-box-shadow': '0px 0px 4px 5px orange',
'box-shadow': '0px 0px 4px 5px orange'
})
})
}
else if(data["newstat"] == "3"){
$("#userbaseprofilepicture").css({
'-webkit-box-shadow' : '0px 0px 4px 5px grey',
'-moz-box-shadow': '0px 0px 4px 5px grey',
'box-shadow': '0px 0px 4px 5px grey'
})
}
})
}
}
});
}
@ -836,7 +836,7 @@ window.onerror = function (msg, url, line) {
} else {
// check if permission is already granted
if (Notification.permission === 'granted') {
// show notification here
// show notification here
} else {
// request permission from user
Notification.requestPermission().then(function(p) {
@ -851,7 +851,7 @@ window.onerror = function (msg, url, line) {
});
}
}
$("#chatButton").click(function(){
@ -862,8 +862,8 @@ $("#chatButton").click(function(){
data : {
action : "getloggedusers"
},
success: function( data )
{
success: function( data )
{
$("#chat_alluserscontent").fadeToggle();
$("#chat_alluserscontent").html(data);
}
@ -872,7 +872,3 @@ $("#chatButton").click(function(){
</script>

View File

@ -11,16 +11,16 @@
<title>Digitale Agentur</title>
<!-- Custom fonts for this template-->
<link rel="canonical" href="https://www.digitale-agentur.com">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.js"></script>
<link href="{%static 'users/vendor/fontawesome-free/css/all.min.css' %}" rel="stylesheet" type="text/css">
<link href="https://fonts.googleapis.com/css?family=Nunito:200,200i,300,300i,400,400i,600,600i,700,700i,800,800i,900,900i" rel="stylesheet">
<!--<link href="{%static 'users/css/bootstrap.min.css' %}" rel="stylesheet">-->
<link href='https://fonts.googleapis.com/css?family=Roboto&display=swap' rel='stylesheet' type='text/css'>
@ -34,37 +34,37 @@
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.1/jquery-ui.min.js"></script>
<!-- <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>-->
<!-- CROPPER -->
<link href="{% static 'users/css/cropper.min.css' %}" rel="stylesheet">
<!-- DATATABLES -->
<link href="https://cdn.datatables.net/1.10.20/css/jquery.dataTables.min.css" rel="stylesheet">
<link href="https://cdn.datatables.net/1.10.20/css/dataTables.bootstrap4.min.css" rel="stylesheet">
<!-- Custom styles for this template-->
<link href="{% static 'users/css/sb-admin-2.css' %}" rel="stylesheet">
<link href="{% static 'users/css/theme.css' %}" rel="stylesheet">
<link href="{% static 'users/css/custom.css' %}" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/summernote@0.8.16/dist/summernote-bs4.js"></script>
<link href="https://cdn.jsdelivr.net/npm/summernote@0.8.16/dist/summernote.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/summernote@0.8.16/dist/summernote.css" rel="stylesheet">
<script src="{% static 'summernote/lang/summernote-de-DE.min.js' %}"></script>
<style type="text/css">
.background-image {
background-image: url('{% static 'users/img/registerbackground.jpg' %}');
background-size: cover; /* Resize the background image to cover the entire container */
background-size: cover; /* Resize the background image to cover the entire container */
position: absolute;
left: 0;
top: 0;
top: 0;
width: 100%;
height: 100%;
filter: blur(8px);
@ -85,7 +85,7 @@
<ul class=" bg-gray-900 navbar sidebar-dark accordion fixed-top" id="accordionSidebar">
<a class="sidebar-brand d-flex align-items-center justify-content-center" href="{% url 'users-dashboard' %}">
<div class="sidebar-brand-text mx-3" style="text-align: left;">
<img src="{% static 'users/img/logo.png' %}" style="float: left;" width="7%" class="">
<img src="{% static 'users/img/VVE-Logo.png' %}" style="float: right;" width="2.5%" class="">
@ -94,13 +94,13 @@
</ul>
<style type="text/css">
#logincard {
width: 35%;
width: 35%;
min-width: 500px !important;
margin-top: 4%;
}
</style>
{% block content %}
{% endblock %}
{% endblock %}
</body>
</html>

View File

@ -56,10 +56,10 @@ def randomString(stringLength=10):
@login_required
def toUpdate(request):
# NO AGENVYJOBS
# NO AGENVYJOBS
# CREATE DEFAULT
'''
Agenturleiter
Außendienst
Innendienst
@ -97,25 +97,25 @@ def toUpdate(request):
temgroup_mitarbeiter_ag.group.user_set.add(user)
# ADDING ALL RIGHTS TO GROUP "VERWALTUNG"
perms = AgencyGroup._meta.permissions
perms = AgencyGroup._meta.permissions
for p in perms:
tempperm = Permission.objects.get(codename=p[0])
temgroup_verwaltung_ag.group.permissions.add(tempperm)
print("default groups created and users added")
else:
else:
print("default groups existing")
# CHECK FOR ALL POSSIBLE RIGHTS ON ADMINGROUP
m2m_changed.disconnect(adjust_group_notifications_permission, sender=Group.permissions.through)
ag_admingroup = list(AgencyGroup.objects.filter(agency=request.user.profile.agency, is_admin=True))[0]
perms = AgencyGroup._meta.permissions
perms = AgencyGroup._meta.permissions
for p in perms:
tempperm = Permission.objects.get(codename=p[0])
ag_admingroup.group.permissions.add(tempperm)
# INITIAL ROOT DIR
rootdir = DataDir.objects.filter(is_root=True, agency__pk=request.user.profile.agency.pk)
@ -123,7 +123,7 @@ def toUpdate(request):
print("NO MAIN DIR FOUND - CREATE")
rootdir = DataDir(is_root=True, agency=request.user.profile.agency)
rootdir.save()
print("AGENCY ROOT DIR CREATED")
print("AGENCY ROOT DIR CREATED")
else:
print("MAIN ROOT DIR FOUND - FILESMODULE READY")
@ -135,7 +135,7 @@ def toUpdate(request):
os.mkdir(os.path.join(settings.MEDIA_ROOT + "/agencydata/", "agency_" + str(request.user.profile.agency.pk) + "/files"))
os.mkdir(os.path.join(settings.MEDIA_ROOT + "/agencydata/", "agency_" + str(request.user.profile.agency.pk) + "/agencystats"))
os.mkdir(os.path.join(settings.MEDIA_ROOT + "/agencydata/", "agency_" + str(request.user.profile.agency.pk) + "/agencystats/profilepics"))
# DEF STANDARD DIR
defstandard = DataDir.objects.filter(is_defaultstandard=True, agency__pk=request.user.profile.agency.pk)
@ -161,7 +161,7 @@ def toUpdate(request):
# USER TIME MODEL
usersofagency = User.objects.filter(profile__agency=request.user.profile.agency)
for u in usersofagency:
for u in usersofagency:
# CREATE USERTIME-OBJECT
if(len(UserTime.objects.filter(user=u)) == 0):
usertime_new = UserTime(user=u)
@ -182,19 +182,19 @@ Templates: welcomeusers.html und base.html
'''
def registerNewAgency(request):
if request.method == "POST":
newagencyform = NewAgencyForm(request.POST)
if newagencyform.is_valid():
# Check Mail
email = newagencyform.cleaned_data.get('mail')
mailset = User.objects.filter(email=email)
if(len(mailset) == 0):
newuser_name = newagencyform.cleaned_data.get('first_name') + ' ' + newagencyform.cleaned_data.get('last_name')
agency = Agency()
agency = Agency()
agency.name = newagencyform.cleaned_data.get("agencyname")
agency.vve = newagencyform.cleaned_data.get("vve")
agency.save()
@ -212,9 +212,9 @@ def registerNewAgency(request):
user.profile = pr
user.save()
msg_html = render_to_string('users/register_mail.html', {'username': newuser_name})
# E-Mail für Passwort-Setzung!
msg_html = render_to_string('users/register_mail.html', {'username': newuser_name})
# E-Mail für Passwort-Setzung!
form = PasswordResetForm({'email': email})
if form.is_valid():
form.save(request=request,html_email_template_name='users/password_reset_mail.html')
@ -225,10 +225,10 @@ def registerNewAgency(request):
'Agenturanmeldung',
'Hallo ' + newagencyform.cleaned_data.get('first_name') + ' ' + newagencyform.cleaned_data.get('last_name') + '! Bitte setzen sie sich auf https://digitale-agentur.com/password-reset/ ein Passwort. Anschließend können Sie weitere Details Ihrer Agentur eingeben.',
'noreply@digitale-agentur.com',
[email],
[email],
html_message=msg_html,
fail_silently=True
)
)
'''
return render (request, 'users/registercomplete.html')
@ -238,7 +238,7 @@ def registerNewAgency(request):
"form" : NewAgencyForm(request.POST)
}
return render (request, 'users/register.html',context)
else:
messages.success(request, f'Es ist ein Fehler aufgetreten.')
@ -258,39 +258,39 @@ class AgencyCreateView(CreateView):
fields = ['first_name', 'last_name','username', 'email']
success_url = '/register/done'
def form_valid(self, form):
def form_valid(self, form):
# Send message to the site
messages.success(self.request, f'Agentur erstellt! Es wurde eine E-Mail verschickt mit weitere Infos zur Passworterstellung.')
# SAVE OBJECTS TO SIGNALE!
agency = Agency()
agency = Agency()
agency.name = self.request.POST.get("agency_name")
agency.save()
newuser_name = form.cleaned_data.get('first_name') + ' ' + form.cleaned_data.get('last_name')
form.instance.agency = agency
form.instance.parent = None
msg_html = render_to_string('users/register_mail.html', {'username': newuser_name})
# E-Mail für Passwort-Setzung!
msg_html = render_to_string('users/register_mail.html', {'username': newuser_name})
# E-Mail für Passwort-Setzung!
send_mail(
'Agenturanmeldung',
'Hallo ' + form.cleaned_data.get('first_name') + ' ' + form.cleaned_data.get('last_name') + '! Bitte setzen sie sich auf https://digitale-agentur.com/password-reset/ ein Passwort. Anschließend können Sie weitere Details Ihrer Agentur eingeben.',
'noreply@digitale-agentur.com',
[form.cleaned_data.get('email')],
[form.cleaned_data.get('email')],
html_message=msg_html,
fail_silently=True
)
return super().form_valid(form)
)
return super().form_valid(form)
@login_required
def dashboard(request):
# UPDATE FUNCTIONS BY NEW MODEL-CHANGES FOR COPIEN SOME DATA
toUpdate(request)
#storageinfo = sys.getfilesystemencoding()
context = {
context = {
'active_link' : 'dashboard',
#"systemencode" : storageinfo
}
@ -298,8 +298,8 @@ def dashboard(request):
# Loading only user same agency
# Change context and return for template-data
# # Get all Users of the Same Agency as logged user
standards_of_agency = Standards.objects.filter(agency__pk=request.user.profile.agency.pk).filter(public=True).exclude(area=None).exclude(task=None).order_by('-last_modified_on')[:5]
standards_of_agency = Standards.objects.filter(agency__pk=request.user.profile.agency.pk).filter(public=True).exclude(area=None).exclude(task=None).order_by('-last_modified_on')[:5]
filterdate = datetime.now()
news = News.objects.filter(agency__pk=request.user.profile.agency.pk).filter(go_online_on__lt=filterdate).filter(go_offline_on__gt=filterdate).order_by('-go_online_on')[:4]
@ -331,19 +331,19 @@ class UsersCreateUser(LoginRequiredMixin, CreateView):
model = User
fields = ['first_name', 'last_name', 'email']
success_url = '/settings/newuser/s2/'
# Adding active_link
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context.update({'active_link' : 'settings'})
return context
def form_valid(self, form):
def form_valid(self, form):
# Send message to the site
messages.success(self.request, f'Neuer Mitarbeiter angelegt!')
# SAVE OBJECTS TO SIGNALE!
form.instance.agency = self.request.user.profile.agency
form.instance.parent = None
form.instance.parent = None
newuser_name = form.cleaned_data.get('first_name') + " " + form.cleaned_data.get('last_name')
msg_html = render_to_string('users/newusers_email.html', {'username': newuser_name})
'''
@ -355,7 +355,7 @@ class UsersCreateUser(LoginRequiredMixin, CreateView):
[form.cleaned_data.get('email')],
html_message=msg_html,
fail_silently=True,
)
)
'''
return super().form_valid(form)
@ -364,14 +364,14 @@ class UsersCreateUser(LoginRequiredMixin, CreateView):
def profile(request):
if request.method == 'POST':
u_form = UsersChangeProfil(request.POST, instance=request.user)
if u_form.is_valid():
u_form.save()
prename = request.user.first_name
name = request.user.last_name
messages.success(request, f'Daten für {prename} {name} aktualisiert!')
# Daten neu laden und nicht die "Mächten sie die Daten speichern...?"
return redirect('users-dashboard')
# Daten neu laden und nicht die "Mächten sie die Daten speichern...?"
return redirect('users-dashboard')
else:
# Form in Klammern sind die aktuellen Daten :)
@ -387,16 +387,16 @@ def profile(request):
# Hier andere Nutzer ändern, wenn man Usersmanagement darf!
class UserManagementUpdateForm(LoginRequiredMixin, UpdateView):
model = Profile
labels = {
model = Profile
labels = {
"phoneland" : "Telefon",
"phonemobile" : "Mobil",
"compfunc" : "Agenturfunktion",
}
fields = ['phoneland','phonemobile','compfunc']
fields = ['phoneland','phonemobile','compfunc']
# Update der Zugrifssrechte eines Users
class UsersPermUpdateView(LoginRequiredMixin, View):
class UsersPermUpdateView(LoginRequiredMixin, View):
template_name = 'users/users_perm.html'
form_class = UsersPermForm
success_url = '/dashboard/usersman/'
@ -405,11 +405,11 @@ class UsersPermUpdateView(LoginRequiredMixin, View):
# Form wird geladen; Checkboxen werden vorbereitet und hier rausgerendert.
def get(self,request,*args, **kwargs):
# User ist der hier Aufgerufene, bzw. das Profil!
user_tochange = Profile.objects.get(pk=kwargs['pk']).user
user_tochange = Profile.objects.get(pk=kwargs['pk']).user
return render (request, self.template_name, {'form':self.form_class(user_tochange), 'active_link': 'usersmanagement', 'user_tochange': user_tochange})
# Handle POST GTTP requests
def post(self, request, *args, **kwargs):
def post(self, request, *args, **kwargs):
permissions_loaded = dict(request.POST.lists())
user_tochange = Profile.objects.get(pk=kwargs['pk']).user
# ITERATION Über alle Elemente gehen und Rechte entziehen (nicht vorhanden) oder adden (wenn vorhanden)
@ -418,14 +418,14 @@ class UsersPermUpdateView(LoginRequiredMixin, View):
temprof = Profile
for ele in temprof._meta.permissions:
tempperm = Permission.objects.get(codename=ele[0])
if ele[0] in permissions_loaded:
if ele[0] in permissions_loaded:
user_tochange.user_permissions.add(tempperm)
else:
else:
# Eingeloggter User darf sich nicht selbst die Userverwaltungsrechte entziehen
if user_tochange == request.user and ele[0]=='usermanager':
messages.warning(request, f'Benutzerverwaltungsrechte für {user_tochange.first_name} {user_tochange.last_name} kann nicht entfernt werden.')
else:
user_tochange.user_permissions.remove(tempperm)
user_tochange.user_permissions.remove(tempperm)
user_tochange.save()
messages.success(request, f'Berechtigungen für {user_tochange.first_name} {user_tochange.last_name} aktualisiert!')
return HttpResponseRedirect('/dashboard/usersman/')
@ -434,7 +434,7 @@ class UsersPermUpdateView(LoginRequiredMixin, View):
@login_required
def ProfileUpdateView(request, pk):
prof_user = User.objects.get(profile__pk=pk)
if request.method == 'POST':
profileform_form = UsersAddProfileForm(request.POST, request.FILES, instance=prof_user.profile)
#profileform_parents = UsersAddProfileFormParents(request.POST, instance=request.user)
@ -442,25 +442,25 @@ def ProfileUpdateView(request, pk):
name = prof_user.last_name
if profileform_form.is_valid():
profileform_form.save()
messages.success(request, f'Daten für {prename} {name} aktualisiert!')
return redirect('users-management')
profileform_form.save()
messages.success(request, f'Daten für {prename} {name} aktualisiert!')
return redirect('users-management')
else:
# Form in Klammern sind die aktuellen Daten :)
profileform_form = UsersAddProfileForm(instance=prof_user.profile)
# Nur User, die im Organigramm auch sichtbar sein, können ausgewählt werden
possible_users = User.objects.filter(profile__agency__pk=prof_user.profile.agency.pk).filter(profile__visible=True)
possible_users = User.objects.filter(profile__agency__pk=prof_user.profile.agency.pk).filter(profile__visible=True)
context = {
'prof_user' : prof_user,
'profileform_form' : profileform_form,
'profileform_form' : profileform_form,
'active_link' : 'usersmanagement',
'possible_users' : possible_users
}
return render(request, 'users/profile_update.html', context)
'''
@ -471,13 +471,13 @@ def ProfileUpdateView(request, pk):
@login_required
def setuserparent(request):
if request.method == 'GET':
if request.GET['action'] == 'adduserp':
if request.GET['action'] == 'adduserp':
userid = request.GET['objectid']
toadd = request.GET['userid']
toadd_user = User.objects.get(pk=toadd)
workinguser = User.objects.get(pk=userid)
username_clean = toadd_user.first_name + " " + toadd_user.last_name
workinguser.profile.parent = toadd_user
workinguser.profile.parent = toadd_user
workinguser.save()
# Getting Remaining-Users
possible_users = User.objects.filter(profile__agency__pk=request.user.profile.agency.pk)
@ -486,14 +486,14 @@ def setuserparent(request):
final_possible_users = {}
for ele in possible_users_js:
final_possible_users.update({'first_name':ele['first_name'],'last_name':ele['last_name'],'id':ele['id']})
# Counter for remaining users to show/hide "Keine Mitarbeiter"-Div
# Counter for remaining users to show/hide "Keine Mitarbeiter"-Div
return JsonResponse({'userid' : userid, 'username_clean' : username_clean, 'remaining_users':possible_users_js})
else:
return HttpResponse("Request method is not a GET")
@login_required
def changeonlinestat(request):
if request.method == 'GET':
if request.method == 'GET':
request.user.profile.onlinestatus = request.GET["newstat"]
request.user.save()
return JsonResponse({"newstat" : request.GET["newstat"]})
@ -511,9 +511,9 @@ class ProfileDeleteView(LoginRequiredMixin, DeleteView):
model = User
success_url = '/dasettings/main'
template_name = 'users/user_confirm_delete.html'
def delete(self, request, *args, **kwargs):
def delete(self, request, *args, **kwargs):
user = User.objects.get(pk=kwargs['pk'])
logged_user = request.user
areas_fs = Areas.objects.filter(created_area_by=user)
@ -549,8 +549,8 @@ class ProfileDeleteView(LoginRequiredMixin, DeleteView):
return response
@login_required
def agency(request):
context = {
def agency(request):
context = {
'active_link' : 'agencyinfo'
}
return render(request, 'users/agency.html', context)
@ -563,7 +563,7 @@ class AgencyUpdateView(LoginRequiredMixin, UpdateView):
success_url = '/dashboard/agencyinfo'
def get_context_data(self, **kwargs):
context = super(AgencyUpdateView, self).get_context_data(**kwargs)
context = super(AgencyUpdateView, self).get_context_data(**kwargs)
context['active_link'] = 'agencyinfo'
return context
@ -571,19 +571,19 @@ class AgencyUpdateView(LoginRequiredMixin, UpdateView):
# PRIORISIERUNG
'''
Es werden alle Aufgabenbereiche den Bereichen der Agentur zugeordnet und ausgegeben.
Es werden alle Aufgabenbereiche den Bereichen der Agentur zugeordnet und ausgegeben.
'''
@login_required
def UsersPrio(request, pk):
user = User.objects.get(pk=pk)
if(user.profile.agency.pk != request.user.profile.agency.pk):
return HttpResponseRedirect('users-dashboard')
else:
prios = Prio.objects.filter(user__pk=pk)
areas = Areas.objects.filter(agency__pk=request.user.profile.agency.pk)
prios = Prio.objects.filter(user__pk=pk)
areas = Areas.objects.filter(agency__pk=request.user.profile.agency.pk)
user_first_name = user.first_name
user_last_name = user.last_name
user_id = user.pk
@ -598,7 +598,7 @@ def UsersPrio(request, pk):
return render(request, 'users/users_prio.html', context)
'''
Ajax-Call für Prio.Updates seitens des Users
im Profil
@ -645,7 +645,7 @@ def GlobalSearch(request):
res_files = DataFile.objects.filter(agency__pk=ag).filter(name__icontains=searchfor)
html = render_to_string('users/searchres.html', {'links': links, 'res_standard': res_standard, 'res_areas': res_areas, 'res_tasks': res_tasks, 'res_pers': res_pers, 'res_news' : res_news, 'res_pass' : res_pass, 'res_contacts' : res_contacts, 'res_files' : res_files, 'user' : request.user})
return HttpResponse(html)
@ -657,7 +657,7 @@ def searchStandardRouter(request):
if request.method == 'GET':
return redirect('/standards/standard/'+request.GET['s_id']+'/single')
else:
return redirect('dashboard')
return redirect('dashboard')
'''
Hier werden die Zuständigkeiten eines Benutzers über alle Bereiche/Aufgaben hinweg gesetzt.
@ -669,14 +669,14 @@ def UsersAreaTaskUpdate(request, pk):
user = User.objects.get(pk=pk)
if request.user.profile.agency.pk != user.profile.agency.pk:
return redirect('dashboard')
return redirect('dashboard')
else:
finaldata = {}
context = {
'active_link' : 'usersmanagement',
'user_id' : user.pk,
'active_link' : 'usersmanagement',
'user_id' : user.pk,
}
if request.method == 'POST':
form = request.POST
areatask_formdata = list(form)
@ -693,22 +693,22 @@ def UsersAreaTaskUpdate(request, pk):
area_ids.append(int(tempdata[1]))
elif(tempdata[0] == 'task'):
task_ids.append(int(tempdata[1]))
# Alle Areas und Tasks laden
areas = Areas.objects.filter(agency__pk=user.profile.agency.pk)
tasks = Tasks.objects.filter(agency__pk=user.profile.agency.pk)
# Prüfen, ob der User in Area ist oder nicht und ihn ggf. hinzufügen/entfernen
for area in areas:
if area.pk in area_ids:
area.usersfield.add(user)
area.usersfield.add(user)
else:
area.usersfield.remove(user)
area.save()
'''
Prüfen, ob ein User einem Aufgabenbereich hinzugeprdnet ist.
Prüfen, ob ein User einem Aufgabenbereich hinzugeprdnet ist.
Ist er im Bereich, passiert nichts. Ist er nicht in diesem
Bereich, wird er dem usersfield der Aufgabe und der Tabelle
Prio hinzugeüfügt. Damit kann der User seine individuellen
@ -728,7 +728,7 @@ def UsersAreaTaskUpdate(request, pk):
task.save()
username_message = user.first_name + " " + user.last_name
messages.success(request, f'Zuständigkeiten für {username_message} aktualisiert!')
return redirect('users-management')
return redirect('users-management')
else:
form = UserAreaTaskForm(user)
user_first_name = user.first_name
@ -747,7 +747,7 @@ def UsersAreaTaskUpdate(request, pk):
@login_required
def support(request):
context = {
'active_link' : 'support',
'active_link' : 'support',
'form' : SupportForm(request.user)
}
@ -758,35 +758,35 @@ def support(request):
attachments=[] #myfile is the key of a multi value dictionary, values are the uploaded files
for f in request.FILES.getlist('attachment_1'): #myfile is the name of your html file button
filename = f.name
attachment_type = filetype.guess(f)
fileblob = str(base64.b64encode(f.read()))
attachment_type = filetype.guess(f)
fileblob = str(base64.b64encode(f.read()))
fileblob = fileblob[1 : : ]
fileblob = fileblob[1 : : ]
fileblob = fileblob[:-1:]
attachments.append({str(filename) : "data:" + attachment_type.mime + ";base64," + fileblob})
fileblob = "data:" + attachment_type.mime + ";base64," + fileblob
for f in request.FILES.getlist('attachment_2'): #myfile is the name of your html file button
filename = f.name
attachment_type = filetype.guess(f)
fileblob = str(base64.b64encode(f.read()))
attachment_type = filetype.guess(f)
fileblob = str(base64.b64encode(f.read()))
fileblob = fileblob[1 : : ]
fileblob = fileblob[1 : : ]
fileblob = fileblob[:-1:]
attachments.append({str(filename) : "data:" + attachment_type.mime + ";base64," + fileblob})
for f in request.FILES.getlist('attachment_2'): #myfile is the name of your html file button
filename = f.name
attachment_type = filetype.guess(f)
fileblob = str(base64.b64encode(f.read()))
attachment_type = filetype.guess(f)
fileblob = str(base64.b64encode(f.read()))
fileblob = fileblob[1 : : ]
fileblob = fileblob[1 : : ]
fileblob = fileblob[:-1:]
attachments.append({str(filename) : "data:" + attachment_type.mime + ";base64," + fileblob})
'''
#image = request.FILES
supportdata = dict(form)
supportdata = dict(form)
# Data from Form to JSON-Format
name = str(supportdata['name'][0])
name = str(supportdata['name'][0])
mail = str(supportdata['mail'][0])
problemconc = str(supportdata['problemconc'][0])
problem = str(supportdata['problem'][0])
@ -794,31 +794,31 @@ def support(request):
# HEADERS CURL
headers = {'X-API-Key': 'F025A238EB74914E3653BA2989BFF7C4'}
subject = "Digitale Agentur: " + str(problemconc)
# DataJSON
ostdata = {
"topicId" : '12',
"name": name,
"email": mail,
"email": mail,
"subject": 'Digitale Agentur: '+problemconc,
"ip": "1.1.1.1",
"message": "*****************************\nAgentur: "+ request.user.profile.agency.name +" (ID: "+ str(request.user.profile.agency.pk) +")\nBenutzer: "+request.user.first_name+" "+request.user.last_name+" (ID: "+ str(request.user.pk) +")\n*******************************\n\n" + problem
#"attachments" : attachments
}
json_data = json.dumps(ostdata)
}
json_data = json.dumps(ostdata)
r = requests.post("https://support.vh-solutions.de/api/http.php/tickets.json", data=json_data, headers=headers)
# IF request FAILED error-Message
r = requests.post("https://support.vh-solutions.de/api/http.php/tickets.json", data=json_data, headers=headers)
# IF request FAILED error-Message
if(r.status_code != 201):
messages.warning(request, f'Supportanfrage fehlgeschlagen!' + str(r))
messages.warning(request, f'Supportanfrage fehlgeschlagen!' + str(r))
else:
messages.success(request, f'Supportanfrage erfolgreich! Ihre Ticketnummer ist '+ str(r.json()) +'!')
messages.success(request, f'Supportanfrage erfolgreich! Ihre Ticketnummer ist '+ str(r.json()) +'!')
return render(request, 'users/support_done.html', context)
else:
return render(request, 'users/support.html', context)
'''
@ -827,23 +827,23 @@ Schickt eine E-Mail an den User inkl. Passwortlink zum zurücksetzen.
'''
def sendpassmail(request):
if(request.method == 'GET'):
if(request.method == 'GET'):
userid = request.GET['userid']
tempuser = User.objects.get(pk=userid)
# E-Mail für Passwort-Setzung!
tempuser = User.objects.get(pk=userid)
# E-Mail für Passwort-Setzung!
form = PasswordResetForm({'email': tempuser.email})
if form.is_valid():
form.save(request=request,html_email_template_name='users/password_reset_mail.html')
return render (request, 'users/registercomplete.html')
return render (request, 'users/registercomplete.html')
return JsonResponse({'message' : 0})
def datenschutz(request):
def datenschutz(request):
if request.user.is_authenticated:
return render(request, 'users/datenschutz.html')
else:
return render(request, 'users/datenschutz_p.html')
def impressum(request):
def impressum(request):
if request.user.is_authenticated:
return render(request, 'users/impressum.html')
else:
@ -876,37 +876,37 @@ def handler500(request):
# CRONJOBS ALLE 5 MINUTEN
def cronactions(request, code):
data = {}
if(code == settings.CRONAPIKEY):
# NEWS CHECKING
all_unnotifc_news = News.objects.filter(agnotify=False, go_online_on__lt=timezone.now())
if(code == settings.CRONAPIKEY):
# NEWS CHECKING
all_unnotifc_news = News.objects.filter(agnotify=False, go_online_on__lt=timezone.now())
allusers = User.objects.all()
for news in all_unnotifc_news:
targeturl = settings.BASE_URL + "news/news/" + str(news.pk) + "/single"
# NEW NEWS FOUND WHICH HAVE TO GO ONLINE NOW!
news.agnotify = True
news.save()
for user in allusers:
for user in allusers:
# SEMI-SIGNAL FOR NEWS GOING ONLINE
if(user.usernotifications.news_created_mail and news.agency == user.profile.agency):
notificationtext = "Neue Agenturnews: " + news.name
username = user.first_name + " " + user.last_name
msg_html = render_to_string('notificsys/notification_mail.html', {'username': username, 'notificationtext' : notificationtext, 'linktarget' : targeturl})
msg_html = render_to_string('notificsys/notification_mail.html', {'username': username, 'notificationtext' : notificationtext, 'linktarget' : targeturl})
send_mail(
'Agentur-Benachrichtigung',
'Hallo ' + user.first_name + ' ' + user.last_name + '! ' + notificationtext,
'noreply@digitale-agentur.com',
[user.email],
[user.email],
html_message=msg_html,
fail_silently=True
)
if(user.usernotifications.news_created_push and news.agency == user.profile.agency):
newnotification = UserNotification(touser=user, notificationtext="Neue Agenturnews: " + news.name, notificationtype="agencynews", elementid=news.pk)
newnotification.save()
channel_layer = channels.layers.get_channel_layer()
async_to_sync(channel_layer.group_send)("user_" + str(user.pk), {'type' : 'pushhandler', 'pushtext' : "pushnotification__News | Neue Agenturnews: " + news.name})
async_to_sync(channel_layer.group_send)("user_" + str(user.pk), {'type' : 'pushhandler', 'pushtext' : "pushnotification__News | Neue Agenturnews: " + news.name})
# LEXOFFICE TEST
# HEADERS CURL
'''
@ -915,41 +915,41 @@ def cronactions(request, code):
'Content-Type': 'application/json',
'Accept': 'application/json',
}
# DataJSON
lexdata = {
lexdata = {
"voucherDate": "2020-07-09T00:00:00.000+01:00",
"address" : {
"name" : "Agenturname XXX",
"name" : "Agenturname XXX",
"countryCode" : "DE"
},
"totalPrice" : {
"currency" : "EUR",
"currency" : "EUR",
},
"lineItems" : [
{
"type" : "custom",
{
"type" : "custom",
"name" : "Monatsbeitrag",
"quantity" : 1,
"unitName" : "Stück",
"unitPrice" :
"unitPrice" :
{
"currency" : "EUR",
"netAmount" : 10,
"netAmount" : 10,
"taxRatePercentage" : 16
},
},
},
{
"type" : "custom",
{
"type" : "custom",
"name" : "Abwesenheitsmodul",
"quantity" : 1,
"unitName" : "Stück",
"unitPrice" :
"unitPrice" :
{
"currency" : "EUR",
"currency" : "EUR",
"netAmount" : 10,
"taxRatePercentage" : 16
},
},
}
],
"taxConditions": {
@ -959,96 +959,128 @@ def cronactions(request, code):
"shippingDate": "2020-07-09T00:00:00.000+01:00",
"shippingType": "service"
},
}
json_data = json.dumps(lexdata)
r = requests.get("https://api.lexoffice.io/v1/invoices/0f9b6a1d-1912-4a10-9926-7909e5580202", data=json_data, headers=headers)
}
json_data = json.dumps(lexdata)
r = requests.get("https://api.lexoffice.io/v1/invoices/0f9b6a1d-1912-4a10-9926-7909e5580202", data=json_data, headers=headers)
print(r)
print(r.text)
'''
else:
print("API CODE FAILED")
data.update({"status" : "failed"})
data.update({"status" : "failed"})
return JsonResponse(data)
'''
Gibt False zurück, wenn der User an diesem Tag KEINE Abwesenheiten hat, ansonsten True!
'''
def absencecheck(user, daytocheck):
returnstat = False
absencedays = 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 = True
return returnstat
# CRONJOBS UM 00:01!
def cronactionsdaily(request, code):
data = {}
if(code == settings.CRONAPIKEY):
allusers = User.objects.all()
'''
Pro User gibt es das Feld loose_holiday in der UserTime-Info. Ist dieser Tag vorbei, muss die Differenz der days_inuse des VORJHARES in den Rest das AKTUELLEN JAHRES gespeichert werden!
data = {}
if(code == settings.CRONAPIKEY):
allusers = User.objects.all()
'''
Pro User gibt es das Feld loose_holiday in der UserTime-Info. Ist dieser Tag vorbei, muss die Differenz der days_inuse des VORJHARES in den Rest das AKTUELLEN JAHRES gespeichert werden!
'''
today = date.today()
for user in allusers:
# REST URLAUB BERECHNUNG
usertimedata = UserTime.objects.get(user=user)
day_tocheck = usertimedata.loose_holidedate.split(".")[0]
month_tocheck = usertimedata.loose_holidedate.split(".")[1]
month = today.month
if month < 10:
month = "0" + str(month)
day = today.day
if day < 10:
day = "0" + str(day)
'''
today = date.today()
for user in allusers:
# REST URLAUB BERECHNUNG
usertimedata = UserTime.objects.get(user=user)
# Restetag erreicht, Reste ins nächste Jahr übertragen
if(str(day_tocheck) == str(day) and str(month_tocheck) == str(month)):
sourceyear = today.year
this_year = list(UserYearAbsenceInfo.objects.filter(year=sourceyear, user=user))[0]
next_year = list(UserYearAbsenceInfo.objects.filter(year=sourceyear+1, user=user))[0]
next_year.restdays = this_year.days - this_year.days_inuse
next_year.save()
day_tocheck = usertimedata.loose_holidedate.split(".")[0]
month_tocheck = usertimedata.loose_holidedate.split(".")[1]
# ARBEITSTAGE BEENDEN
# Benutzer hat Zeiterfassung aktiv
if(user.usertime.usetime):
workdays = Workday.objects.filter(user=user, end=None)
for wd in workdays:
wd.end = datetime(wd.start.year, wd.start.month, wd.start.day, 23, 59)
wd.save()
month = today.month
yesterday = date.today() - timedelta(days=1)
weekday = yesterday.weekday()
workdays_yesterday = len(Workday.objects.filter(user=user, start__day=yesterday.day, start__month=yesterday.month, start__year=yesterday.year))
# Mitarbeiter hat für den gestrigen Tag keine Zeiten erfasst, daher automatisch auf null wenn KEINE Abwesenheit eingetragen wurde
if month < 10:
month = "0" + str(month)
if(workdays_yesterday == 0 and absencecheck(user, yesterday) == False):
targettworktime = 0.0
if(weekday == 0):
targettworktime = user.usertime.wd_mo
if(weekday == 1):
targettworktime = user.usertime.wd_tu
if(weekday == 2):
targettworktime = user.usertime.wd_we
if(weekday == 3):
targettworktime = user.usertime.wd_th
if(weekday == 4):
targettworktime = user.usertime.wd_fr
if(weekday == 5):
targettworktime = user.usertime.wd_sa
if(weekday == 6):
targettworktime = user.usertime.wd_so
day = today.day
if day < 10:
day = "0" + str(day)
workdaytemp = Workday(user=user, agency=user.profile.agency, start=datetime(yesterday.year, yesterday.month, yesterday.day, 8, 0), end=datetime(yesterday.year, yesterday.month, yesterday.day, 8, 0), target=targettworktime)
workdaytemp.save()
# Restetag erreicht, Reste ins nächste Jahr übertragen
if(str(day_tocheck) == str(day) and str(month_tocheck) == str(month)):
sourceyear = today.year
this_year = list(UserYearAbsenceInfo.objects.filter(year=sourceyear, user=user))[0]
next_year = list(UserYearAbsenceInfo.objects.filter(year=sourceyear+1, user=user))[0]
next_year.restdays = this_year.days - this_year.days_inuse
next_year.save()
# ARBEITSTAGE BEENDEN
# Benutzer hat Zeiterfassung aktiv
if(user.usertime.usetime):
workdays = Workday.objects.filter(user=user, end=None)
for wd in workdays:
wd.end = datetime(wd.start.year, wd.start.month, wd.start.day, 23, 59)
wd.save()
# Erinnerungsmails/Push bei Vertretung verschicken
one_week_later = date.today() + timedelta(days=7)
repre_absence = Absence.objects.filter(representator=user, start=one_week_later, confirm_status=0)
# Erinnerungsmails/Push bei Vertretung verschicken
one_week_later = date.today() + timedelta(days=7)
repre_absence = Absence.objects.filter(representator=user, start=one_week_later, confirm_status=0)
for r in repre_absence:
if(r.representator.usernotifications.absence_user_is_rep_reminder_mail):
sendMailNoti(" in einer Woche startet Ihre Vertretung für " + r.user.first_name + " " + r.user.last_name + "!", user)
if(r.representator.usernotifications.absence_user_is_rep_reminder_push):
newnotification = UserNotification(touser=user, notificationtext="Erinnerung für Abwesenheitsvertretung!", notificationtype="", elementid=r.pk)
newnotification.save()
for r in repre_absence:
if(r.representator.usernotifications.absence_user_is_rep_reminder_mail):
sendMailNoti(" in einer Woche startet Ihre Vertretung für " + r.user.first_name + " " + r.user.last_name + "!", user)
channel_layer = channels.layers.get_channel_layer()
async_to_sync(channel_layer.group_send)("user_" + str(user.pk), {'type' : 'pushhandler', 'pushtext' : "pushnotification__Abwesenheit | In einer Woche startet Ihre Vertretung für " + r.user.first_name + " " + r.user.last_name + "!"})
data.update({"status" : "ok"})
else:
print("API CODE FAILED")
data.update({"status" : "failed"})
return JsonResponse(data)
if(r.representator.usernotifications.absence_user_is_rep_reminder_push):
newnotification = UserNotification(touser=user, notificationtext="Erinnerung für Abwesenheitsvertretung!", notificationtype="", elementid=r.pk)
newnotification.save()
channel_layer = channels.layers.get_channel_layer()
async_to_sync(channel_layer.group_send)("user_" + str(user.pk), {'type' : 'pushhandler', 'pushtext' : "pushnotification__Abwesenheit | In einer Woche startet Ihre Vertretung für " + r.user.first_name + " " + r.user.last_name + "!"})
data.update({"status" : "ok"})
else:
print("API CODE FAILED")
data.update({"status" : "failed"})
return JsonResponse(data)
def sendMailNoti(notificationtext, user_touched, linktarget=""):
username = user_touched.first_name + " " + user_touched.last_name
msg_html = render_to_string('notificsys/notification_mail.html', {'username': username, 'notificationtext' : notificationtext, 'linktarget' : linktarget})
msg_html = render_to_string('notificsys/notification_mail.html', {'username': username, 'notificationtext' : notificationtext, 'linktarget' : linktarget})
send_mail(
'Agentur-Benachrichtigung',
'Hallo ' + user_touched.first_name + ' ' + user_touched.last_name + '! ' + notificationtext,
'noreply@digitale-agentur.com',
[user_touched.email],
[user_touched.email],
html_message=msg_html,
fail_silently=True
)
)